помощью цифр.
Здесь, в отличие от языков программирования высокого уровня, видна
особенность программирования на низком уровне — многие вещи
приходится реализовывать самостоятельно. Итак, напишем подпрограмму
(назовем ее outint), которая позволит выводить на экран целое число,
например, находящееся в регистре AX, используемом для передачи
аргумента.
Будем реализовывать подпрограмму сразу как универсальную, что
является признаком хорошего стиля в программировании. Наша
подпрограмма сможет выводить число как в десятичном, так и в двоичном,
и в другом виде — с использованием позиционной системы счисления с
произвольным основанием. Вспомним правило записи чисел в
позиционной системе:
N
= ∑ q
i
g
i
, i = 0…M,
где M — количество разрядов числа; q
i
—
основание системы счисления в
степени i, g
i
—
значение цифры в позиции i, а i — номер позиции
(разряда), начиная с нуля и справа налево. Будем делить наше число на
основание системы счисления и накапливать остатки от деления, которые
и будут значениями цифр. Далее приводится возможный вариант
программы. В секции описания данных появится
schisl dw 10 ; основание системы счисления
Chislo db ' ' ; строка где будет записываться цифрами число
Здесь мы резервируем место (пустую строку из нескольких пробелов) для
формирования записи числа, начиная с адреса Chislo в памяти, а кроме
этого заносим по умолчанию в качестве основания системы счисления в
ячейку schisl 10.
Сама подпрограмма может выглядеть следующим образом:
outint: ; вывод содержимого ax в окне сообщений
138
mov edi,Chislo ; заносим в регистр edi адрес строки для
записи числа цифрами
add edi,5 ; записывать цифры будем начиная с правой,
поэтому переходим к концу строки
l: xor dx,dx ; обнуление dx
div [schisl] ; после выполнения div в dl автоматически
заносится остаток от деления
add d
l,'0' ; для получения кода цифры прибавляем к числу код
нуля
mov [edi],dl ; пересылаем в память, где хранится запись
числа цифрами, очередную
dec edi ;передвигаемся к следующему байту — идем от конца
or ax,ax ; проверяем ax, где остается частное, на равенство
нулю, если нет -
jnz l ; продолжаем делить
; вызов WinAPI MessageBox для вывода строки
invoke MessageBox,HWND_DESKTOP,Chislo,"
Сумма
нечетных",MB_OK
ret
Итак, сначала мы заносим в регистр EDI (расширенная версия DI,
используется из-за 32-разрядной архитектуры) адрес начала будущей
записи числа цифрами в памяти с помощью команды mov edi,Chislo.
Однако вспомним, что при формировании позиционной записи мы пишем
остатки справа налево — следовательно, нужно перейти к концу строки.
Поскольку мы отвели пять позиций под запись числа, добавляем 5 к адресу
командой add edi,5 (адресная арифметика в ассемблере). Следующая
строка — начало цикла, поэтому она помечена l. В ней первая команда
иллюстрирует программистский трюк — для обнуления регистра DX
используется xor значения самого с собой — команда выполняется
быстрее, нежели mov dx,0. Далее с помощью div [schisl]
производится деление солержимого AX на основание системы счисления,
извлекаемое из соответствующей ячейки памяти с помощью косвенной
адресации. Особенностью команды div является то, что в данном случае
остаток от деления автоматически попадает в младшие биты регистра DX,
называемые DL. А нам именно этот остаток и нужен! Пересылаем его по
адресу, на который указывает EDI (косвенная адресация), однако
предварительно превращаем остаток, который сам по себе является числом
от 0 до 9 при основании системы счисления 10, в соответствующий код
символа. Сделать это несложно , поскольку в используемой кодировке
коды цифр идут подряд. Достаточно с помощью команды add dl,'0'
прибавить код нуля, чтобы получить необходимый символ — цифру. С
помощью команды dec edi переходим к предыдущей позиции в памяти.
139
Как только число будет исчерпано, в качестве частного от деления,
попадающего по умолчанию в AX, мы получим ноль, что должно стать
сигналом о завершении цикла. Проверку значения AX на ноль можно
осуществить с помощью часто используемой в программах на ассемблере
логической операции ИЛИ аргумента самого с собой or ax,ax (заметим,
что в ряде ассемблеров существует специальная команда тестирования
содержимого ячейки и установки флагов, например TST). Продолжаем
цикл, если это необходимо, с помощью команды условного перехода jnz
l
.
После завершения цикла запись числа будет сформирована в виде строки
по адресу Chislo. C помощью системного вызова invoke
MessageBox,HWND_DESKTOP,Chislo,"
Сумма нечетных",MB_OK
на экране демонстрируется окно сообщений с соответствующим
содержимым.
Последняя строчка подпрограммы — команда ret возврата в
вызывающую программу.
Контрольные вопросы
1.
Какой прием используется в рассмотренной программе для
преобразования выводимого на экран числа в строку?
2.
Почему команда shr применима для проверки нечетности числа?
3.
Где в приведенной программе используется косвенная адресация?
Поясните механизм косвенной адресации.
4.
Можно ли использовать команду xor для обнуления ячейки памяти?
Почему?
5.
Зачем в программе используется команда ИЛИ регистра самого с
собой?
Макросы в ассемблере
Поскольку, как было продемонстрировано, при программировании на
языке ассемблера многие вещи приходится реализовывать вручную,
весьма активно применяется «расширение» языка на основе аппарата
макросов. Говоря коротко, макросы позволяют заменять одни фрагменты
текста в программе другими (называются макрорасширениями) — этот
процесс называется макроподстановкой (использование макросов
аналогично применению директивы #define в языке программирования
Си). При этом возможно использование макросов с параметрами, как
показано далее:
<
имя> MACRO <формальные параметры>
140
<
тело>
ENDM
При выполнении макроподстановки в тексте программы < имя> будет
заменено на < тело> с подстановкой в нем указываемых фактических
параметров вместо формальных. Параметры макроса записываются через
запятую или пробел. Размещать макроопределения в тексте
программыследует до первой макрокоманды, использующей макрос.
Макросредства при программировании на языках ассемблера настолько
популярны, что сам язык иногда называют макроассемблером.
Достарыңызбен бөлісу: |