В одной из прошлых статей мы поговорили о языках программирования высокого уровня. Сегодня мы опустимся гораздо ниже — в самые недра программных систем и попробуем разобраться в том, на каком «языке» говорит с пользователями и программистами сама ЭВМ.
Двоичный код
Мы часто вкладываем в это понятие самые различные смыслы. «Двоичный код», «машинный код», «программный код». Между тем, значение этих терминов различно. А вот использование в машинной математике двоичного кода очевидно и вполне логично.
Цифровая электроника работает с двумя типами электрических сигналов, или, если точнее, с двумя «состояниями» — высоким уровнем сигнала в его источнике либо передатчике и низким его уровнем. Высокий уровень сигнала — это «ноль», низкий — «единица». Это наиболее надежная схема передачи сигналов, которая дает стабильный результат. Какие бы помехи не возникали на пути сигнала, мы всегда поймем, где «единица», а где — «ноль».
На такую схему великолепно ложится двоичная система счисления. Справедливости ради стоит отметить, что впервые такая система была описана в древнекитайской «книге Перемен». В дальнейшем двоичная система счисления только обретала теоретическую основу. Математик Лейбниц, например, доказал, что основой системы счисления может быть любое число, кроме нуля. Были, а, возможно и будут и другие теоретические работы в этом направлении. Сейчас используются, как минимум, три системы счисления — двоичная, восьмеричная, шестнадцатиричная. Но нас интересует именно двоичная.
Как работает «двоичный код»
С помощью этого кода можно легко перевести десятичное число в понятное компьютеру двоичное. Возьмем число (например, 11001). Пронумеруем его с конца, причем, нумерацию начнем с нуля. Порядковый номер каждой цифры станет степенью, в которую мы возведем основание системы счисления (2), затем перемножим результат с цифрой. Нужно провести эту операцию с каждой цифрой двоичного числа, затем суммировать результат:
1*(2^4)+1*(2^3)+10*(2^2)+0*(2^1)+1*(2^0)
16+8+0+0+2 = 25
Теперь попробуем перевести число 25 обратно, в двоичный код. Используем для этого простейшую арифметическую операцию — получение остатка от деления.
25/2 = 1
12/2 = 0
6/2 = 0
3/2 = 1
1/2 = 1
В результатах, снизу вверх, читаем наше число.
Таким образом считать довольно легко, причем, даже на «пальцах». У нас ведь на руке пять пальцев? Если каждому присвоить единицу или ноль (ноль – палец согнут, единица – выпрямлен) и прикинуть значение основания системы, возведенной в нужную степень, то можно считать от нуля и до 1023. Правда, в случае с числом 25 жест руки будет немного неприличным.
Для любителей посчитать есть сегодня любые инструменты — начиная от калькулятора и заканчивая таблицами разрядности.
С дробными числами расчет ведется по иному. Всем цифрам до «запятой», степень присваивается в обратном порядке, от нуля, как и обычно, а вот числа после «запятой» сопровождаются отрицательной степенью, причем, она уменьшается от запятой. Вот как это выглядит:
Число:
1101,101
1*(2^3)+1*(2^2)+0*(2^1)+1*(2^0)+1*(2^-1)+0*(2^-2)+1*(2^-3)
8+4+0+1 + 0,5+0+0,125 = 13,625
В остальном, операция выполняется также.
Как считает компьютер?
Компьютер не может, допустим, приписать «минус» к двоичному числу и считать его отрицательным. Этот минус нужно где-то поместить. Простейший способ — снабдить число дополнительным знаком (в восьмиразрядной ячейке, в этом случае мы сможем поместить семиразрядное двоичное число, где дополнительный разряд будет означать знак — единица отрицательный, ноль — положительный). Это называется «прямым кодом».
0111010
0111010
Но отрицательные числа лучше представлять по-другому — для того, чтобы компьютер мог с ними работать. «Единица» в крайнем разряде по-прежнему будет выполнять роль «маркера» отрицательного числа. Но остальные разряды требуется инвертировать (то есть, разряды с нулями заменить единицей и наоборот). На месте остается только маркер.
1000101
Теперь к числу прибавляем единицу:
1 1000110
Для чего же вся эта малопонятная морока? – спросит читатель. Для того, чтобы ЭВМ смогла выполнять операции с числами.
0 0111010+0 0111010 = 1110100 (58+58 = 116)
1 1000110+1 1000110 = 1 0001011 = 1 1110100 (-116)
Вычитание работает по тому же принципу, умножение — путем последовательного сложения (я очень сильно упрощаю но принцип именно таков). Что касается операции деления, то она реализуется путем неоднократного сложения делимого с дополнительным кодом делителя до получения необходимого результата.
Подробно эти действия описывать не буду. Расскажу лишь про арифметический сдвиг. Это ещё одна «хитрость», которая позволяет упростить вычисления. Если сдвинуть двоичное число вправо или влево, то оно меняется, согласно определенным правилам.
1111101 = 125
Сдвиг вправо (слева добавляем единицу, последний разряд убираем):
1111110 = 126
Ещё один сдвиг вправо:
1111111 = 127
С дополнительным кодом — ещё интереснее. Сдвиг числа влево дает умножение на 2, вправо – деление на 2. Это очень удобно для умножения и деления целых чисел на числа, равные степени 2 (2, 4, 8, 16, 32, 64). Существует множество вариантов сдвига — арифметический (мы его выполнили), циклический, логический и т.д. Каждый выполняет свою задачу.
Добавлю, что всё, о чем я говорил — это «азы» компьютерных вычислений. Когда-то они выполнялись компьютером именно так. Сегодня они ведутся уже на ином уровне. Персональный компьютер выполняет расчеты при помощи встроенного в ядро основного ЦПУ математического сопроцессора, где каждая операция выполняется одной командой, что значительно ускоряет работу. Математический сопроцессор оптимизирован именно для вычислений, эти функции заложены в него на аппаратном уровне. Но описание работы сопроцессора — это тема для отдельной статьи или даже целой книги.
Кодируем… двоичный код
Сейчас мы подошли к самому главному моменту: мы уже знаем, что компьютер выполняет операции с числами в двоичном коде. Причем, это единственный пока вариант для ЭВМ и не такой уж сложный, если вдуматься, для нашего с вами понимания. Остается выяснить, как же объяснить компьютеру, что он должен делать с числом (числами), чтобы получить результат.
Для этого, очевидно, нужны команды, которые будут точно поняты и интерпретированы центральным процессором. А поскольку ничего, кроме двоичных чисел ЭВМ не понимает, то команды должны состоять из определенного набора цифр — машинной инструкции. Она представляет собой запись, состоящую из нескольких элементов — начального и конечного маркеров и самого её «тела».
use16
org 100h
mov dx,hello
mov ah,9
int 21h
mov ax,4C00h
int 21h
hello db 'Hello, world!$'
use16
org 100h
mov ah,02h
mov dl,'A'
mov cx,26
tt:
int 21h
inc dl
loop tt
mov ah,09h
mov dx,press
int 21h
mov ah,08h
int 21h
mov ax,4C00h
int 21h
press:
db 13,10,'Press any key...$'
Эдуард ТРОШИН
Компьютерная газета
А что вы думаете? Напишите в комментариях!
В комментариях запрещено использовать ненормативную лексику, оскорблять других пользователей сайта, запрещены активные ссылки на сторонние сайты и реклама в комментариях. Уважаемые читатели! Просим вас, оставляя комментарии, уважать друг друга и не злоупотреблять свободой слова. Пользователи, которые нарушают эти правила грубо или систематически, будут заблокированы.
Полная версия правил