Итак, продолжим изучение языка. Опубликую текст программы ещё раз:
.286 CSEG segment assume cs:CSEG, ds:CSEG, es:CSEG, ss:CSEG org 100h begin: ;Всё написанное выше пока опускаем.
mov ah,9 ;Загружаем в регистр ah число 9 (указываем функцию). mov dx,offset helloworld ;Указываем, что за фразу мы будем выводить. int 21h ;Выводим фразу.
int
20h ;Выходим в DOS.
helloworld db 'Hello, world!$' ;Определяем переменную helloworld, доступную побайтно, с фразой ;"Hello, world!". В одинарных кавычках, после знака "!" ставим ;знак "$".
;Завершение программы. CSEG ends end begin
Связка mov ah,9 и int 21h и есть по сути одна команда, если сравнивать с языками высокого уровня, конечно. Однако
на языке ассемблера первая указанная команда называется функцией, а
последняя — прерыванием. Прерывание выполняет команду с заданной
функцией. Есть прерывания и без функций, например, то же int 20h.
Возможно, пока это несколько сложно для восприятия, но такую форму
составления программы необходимо запомнить.
В этом же примере мы
столкнулись и с другими ключевыми понятиями ассемблера, такими, как
регистры и шестнадцатеричная система счисления. Начнём по порядку.
Так
получилось, что в современных компьютерах минимальной единицей памяти
является 8-битный байт, значения которого удобно записывать двумя
шестнадцатеричными цифрами. Для обозначения шестнадцатеричного числа мы
будем использовать букву "h", которую будем ставить позади такой цифры.
Это обозначение общепринятое, хотя некоторые платформы, например мой
любимый ZX Spectrum, в своих ассемблерах использовали запись вида #05B3.
Ноль впереди обозначается "ведущим", так как число #05B3 = #5B3, и
служит для удобочитаемости и называется "выравниванием" (обычно
выравнивают до одного или двух байт: #05B3).
Нам необходимо
научиться переводить числа из одной системы счисления в другую. Для этой
цели прекрасно служит стандартный калькулятор "Windows" в инженерном
режиме. Однако, если под рукой калькулятора нет, преобразование чисел,
представленных в двоичной и шестнадцатеричной системах счисления, в
десятичную выполнить довольно легко. Для этого необходимо записать число
в развёрнутой форме и вычислить его значение. Ниже приведены алгоритмы преобразования чисел из одной системы в другую (или можно сразу перейти к п. 2. "Регистры").
Перевод числа из двоичной системы в десятичную. Возьмём с вами любое двоичное число, например 10,112. Затем запишем его в развёрнутой форме и произведём вычисления:
Перевод чисел из шестнадцатеричной системы в десятичную. Возьмём теперь любое шестнадцатеричное число, например 19F16.
Затем запишем его в развёрнутой форме (при этом не забываем, что
шестнадцатеричная цифра F соответствует десятичному числу 15) и
произведём вычисления:
Перевод чисел из двоичной системы счисления в шестнадцатеричную.
Для записи шестнадцатеричных чисел используются шестнадцать цифр, то
есть в каждом разряде числа возможны 16 вариантов записи. Решаем
показательное уравнение:
16 = 2i . Так как 16 = 24, то i = 4 бита.
Каждый разряд шестнадцатеричного числа всегда содержит 4 бита информации.
Для перевода целого двоичного числа в шестнадцатеричное нужно разбить его на группы по четыре цифры, начиная справа, и,
если в последней левой группе окажется меньше четырёх цифр, дополнить её
слева нулями. Для перевода дробного двоичного числа в
шестнадцатеричное необходимо его разбить на тетрады слева направо и,
если в последней правой группе окажется меньше четырёх цифр, дополнить её справа нулями.
Затем преобразуем каждую группу в шестнадцатеричную цифру, воспользовавшись составленной таблицей соответствия двоичных
тетрад и шестнадцатеричных цифр.
Переведём какое-нибудь целое двоичное число, например, А2 = 1010012 в шестнадцатеричное:
Двоичные тетрады
0010
1001
Шестнадцатеричные цифры
2
9
В результате получаем: А16 = 2916.
Теперь переведём дробное двоичное число А2 =0,1101012 в шестнадцатеричную систему счисления:
Двоичные тетрады
1101
0100
Шестнадцатеричные цифры
D
4
В результате получаем: А16 = 0,D416.
Для
того, чтобы преобразовать любое двоичное число в восьмеричную или
шестнадцатеричную системы счисления, необходимо произвести
преобразования отдельно для его целой и
дробной частей.
Перевод чисел из шестнадцатеричной систем счисления в двоичную.
Для перевода чисел из шестнадцатеричной систем счисления
в двоичную необходимо цифры нашего числа преобразовать в группы двоичных цифр.
Для
преобразования шестнадцатеричного числа в двоичную необходимо каждую цифру числа
преобразовать в группу из четырёх цифр
(тетраду).
Преобразуем дробное восьмеричное число А8 = 0,478 в двоичную систему счисления:
Восьмеричные цифры
4
7
Двоичные триады
100
111
Получаем: А2 = 0,1001112 .
Преобразуем целое шестнадцатеричное число А16 = АВ16 в двоичную систему счисления:
Шестнадцатеричные цифры
А
В
Двоичные тетрады
1010
1011
Получаем: А2 = 101010112
Алгоритм перевода целых десятичных чисел в двоичную систему счисления. Пусть Ацд
- целое десятичное число. Запишем его в виде суммы степеней основания 2
с двоичными коэффициентами. В его записи в развёрнутой форме будут
отсутствовать отрицательные степени основания (числа 2):
На первом шаге разделим число Ацд на основание двоичной системы, то есть на 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. Записать полученные остатки в обратной последовательности.
Перевод
чисел из десятичной системы в двоичную и
шестнадцатеричную более сложен и может осуществляться различными
способами. Рассмотрим один из алгоритмов перевода на примере перевода
чисел из десятичной системы в двоичную. При этом необходимо учитывать,
что алгоритмы перевода целых чисел и правильных дробей будут
различаться.
2. Регистры.
Существуют
регистры общего назначения, сегментные регистры, счётчик команд и
регистры флагов. Здесь мы встречаемся впервые с регистрами общего
назначения ax и dx. Причём каждый из них состоит из двух частей -
старшей (ah) и младшей (al) (для ax), о чём мы уже писали:
H high старший L low младший Каждое имя регистра несёт какой-либо смысл.
A accumulator аккумулятор B base база C counter счётчик D data данные BP base pointer указатель базы SI source index индекс источника DI destination index индекс приёмника SP stack pointer указатель стека CS code segment сегмент команд
DS data segment сегмент данных SS stack segment сегмент стека ES extra segment дополнительный сегмент IP instruction pointer счётчик команд В нашей программе мы использовали старшую часть регистра ax (аккумулятор) и регистр dx (разместили данные).
Каждый
регистр состоит из двух байт — старшего (идёт первым) и младшего.
Например, число 3DEFh можно занести в регистр ax двумя путями. Первый -
прямым:
mov ax,3DEFh и отдельно к старшему и младшему байту:
mov ah,3Dh mov al,EFh Надеюсь, с этим всё ясно.
Скомпилируем
нашу программу, создав с помощью 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!".