Итак, продолжим изучение языка. Опубликую текст программы ещё раз:
Связка mov ah,9 и int 21h и есть по сути одна команда, если сравнивать с языками высокого уровня, конечно. Однако на языке ассемблера первая указанная команда называется функцией, а последняя – прерыванием. Прерывание выполняет команду с заданной функцией. Есть прерывания и без функций, например, то же int 20h. Возможно, пока это несколько сложно для восприятия, но такую форму составления программы необходимо запомнить. В этом же примере мы столкнулись и с другими ключевыми понятиями ассемблера, такими, как регистры и шестнадцатеричная система счисления. Начнём по порядку.
Так получилось, что в современных компьютерах минимальной единицей памяти является 8-битный байт, значения которого удобно записывать двумя шестнадцатеричными цифрами. Для обозначения шестнадцатеричного числа мы будем использовать букву "h", которую будем ставить позади такой цифры. Это обозначение общепринятое, хотя некоторые платформы, например мой любимый ZX Spectrum, в своих ассемблерах использовали запись вида #05B3. Ноль впереди обозначается "ведущим", так как число #05B3 = #5B3, и служит для удобочитаемости и называется "выравниванием" (обычно выравнивают до одного или двух байт: #05B3). Нам необходимо научиться переводить числа из одной системы счисления в другую. Для этой цели прекрасно служит стандартный калькулятор "Windows" в инженерном режиме. Однако, если под рукой калькулятора нет, преобразование чисел, представленных в двоичной и шестнадцатеричной системах счисления, в десятичную выполнить довольно легко. Для этого необходимо записать число в развёрнутой форме и вычислить его значение.
Перевод числа из двоичной системы в десятичную. Возьмём любое двоичное число, например 10,112. Запишем его в развёрнутой форме и произведём вычисления:
10,112 = 1 × 21 + 0 × 20 + 1 × 2-1 + 1 × 2-2 = 1 × 2 + 0 × 1 + 1 × 1/2 + 1 × 1/4 = 2,7510.
Перевод чисел из шестнадцатеричной системы в десятичную. Возьмём любое шестнадцатеричное число, например 19F16. Запишем его в развёрнутой форме (при этом необходимо помнить, что шестнадцатеричная цифра F соответствует десятичному числу 15) и произведём вычисления:
19F16 = 1 × 162 + 9 × 161 + F × 160 = 1 × 256 + 9 × 16 + 15 × 1 = 41510.
Алгоритм перевода целых десятичных чисел в двоичную систему счисления. Пусть Ацд – целое десятичное число. Запишем его в виде суммы степеней основания 2 с двоичными коэффициентами. В его записи в развёрнутой форме будут отсутствовать отрицательные степени основания (числа 2):
Ацд = аn-1 × 2n-1 + аn-2 × 2n-2 + … + а1 × 21 + а0 × 20.
На первом шаге разделим число Ацд на основание двоичной системы, то есть на 2. Частное от деления будет равно
аn-1 × 2n-2 + аn-2 × 2n-3 + … + а1 ,
а остаток – равен a0.
На втором шаге целое частное опять разделим на 2, остаток от деления будет теперь равен a1.
Если продолжать этот процесс деления, то после n-го шага получим последовательность остатков:
а0 , а1 , … , аn-1.
Легко заметить, что их последовательность совпадает с обратной последовательностью цифр целого двоичного числа, записанного в свёрнутой форме:
A2 = an-1 … a1 a0
Таким образом, достаточно записать остатки в обратной последовательности, чтобы получить искомое двоичное число.
Алгоритм перевода целого десятичного числа в двоичное будет следующим:
1. Последовательно выполнять деление исходного целого десятичного числа и получаемых целых частных на основание системы (на 2) до тех пор, пока не получится частное, меньшее делителя, то есть меньшее 2.
2. Записать полученные остатки в обратной последовательности.
Перевод чисел из десятичной системы в двоичную, восьмеричную и шестнадцатеричную более сложен и может осуществляться различными способами. Рассмотрим один из алгоритмов перевода на примере перевода чисел из десятичной системы в двоичную. При этом необходимо учитывать, что алгоритмы перевода целых чисел и правильных дробей будут различаться.
Существуют регистры общего назначения, сегментные регистры, счётчик команд и регистры флагов. Здесь мы встречаемся впервые с регистрами общего назначения ax и dx. Причём каждый из них состоит из двух частей – старшей (ah) и младшей (al) (для ax):
Каждое имя регистра несёт какой-либо смысл.
В нашей программе мы использовали старшую часть регистра ax (аккумулятор) и регистр dx (разместили данные). Каждый регистр состоит из двух байт – старшего (идёт первым) и младшего. Например, число 3DEFh можно занести в регистр ax двумя путями. Первый – прямым:
и отдельно к старшему и младшему байту:
Надеюсь, с этим всё ясно. Скомпилируем нашу программу, создав с помощью Far новый файл test.asm (Shift+F4) и поместив в каталог с ним программы MASM.EXE, ML.EXE, LINK.EXE (либо прописав соответствующие системные пути для них. Для LINK.EXE у меня это сделать не получилось, он остаётся в папке с программой). Не забудьте выбрать кодировку файла 866 (клавишей F8), иначе увидите на экране кракозябры.
Выполняем: ML test.asm /AT
В папке с программой должно появиться ещё два файла – test.obj и test.com. Последний нам и нужен. Запускаем его и видим на экране фразу "Hello, world!".