Жанр: Учеба
Ассемблер и программирование для IBM PC
... 5 Конец бумаги
4 Выбран
3 Ошибка ввода/вывода
0 Таймаут
Если принтер включен, то операция возвращает шест.90 или двоичное
10010000 - принтер "не занят" и "выбран" - это нормальное состояние
готовности. В случае неготовности принтера устанавливаются бит 5 (конец
бумаги или бит 3 (ошибка вывода). Если принтер выключен, то операция
возвращает шест.B0 или двоичное 10110000, указывая на "конец бумаги".
Выполняя программу при выключенном принтере, BIOS не выдает сообщение
автоматически, поэтому предполагается, что программа должна сама проверить
и отреагировать на состояние принтера. Если программа не делает этого, то
единственной индикацией будет мигающий курсор на экране дисплея. Если в
этот момент включить принтер, то некоторые выходные данные могут быть
потеряны. Следовательно, прежде чем использовать функции BIOS для печати,
следует проверить состояние порта принтера и, если будет обнаружена
ошибка, то выдать соответствующее сообщение. (Функции DOS выполняют эту
проверку автоматически, хотя их сообщение "Out of paper" относится к
различным состояниям). После включения принтера, вывод сообщений об ошибке
прекращается и принтер начинает нормально работать без потери данных.
В процессе работы принтер может выйти за страницу или быть нечаянно
выключен. Поэтому в программах печати следует предусмотреть проверку
состояния принтера перед каждой попыткой печати.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ
________________________________________________________________
- Прежде чем выводить данные на печатающее устройство, включите
принтер и вставьте в него бумагу.
- Для завершении печати используйте символы "перевод строки" и
"прогон страницы" для очистки буфера принтера.
- Функции DOS для печати предусматривают вывод сообщений при
возникновении ошибки принтера. Функции BIOS возвращают только код
состояния. При использовании BIOS INT 17H проверяйте состояние принтера
перед печатью.
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ
________________________________________________________________
19.1. Напишите программу в расширенной версии DOS для а) прогона
страницы; б) печати вашего имени; в) перевода строки и печати вашего
адреса; г) перевода строки и печати названия вашего города/штата
(республики); д) прогона страницы.
19.2. Переделайте программу из предыдущего вопроса для базовой версии
DOS.
19.3. Закодируйте строку, в которой имеется следующая информация:
возврат каретки, прогон страницы, включение узких букв, заголовок (любое
имя) и выключение узких букв.
19.4. Измените программу из вопроса 19.1 для использования BIOS INT
17H. Обеспечьте проверку состояния принтера.
19.5. Измените программу из вопроса 19.1 так, чтобы пункты б), в), г)
выполнялись по 5 раз.
19.6. Измените программу на рис.19.1 для выполнения в базовой версии
DOS.
19.7. Измените программу на рис.19.2 так, чтобы распечатываемые
строки также выводились на экран.
ГЛАВА 20 Макросредства
__________________________________________________________________________
Ц е л ь: Объяснить определение и использование ассемблерных
макрокоманд.
ВВЕДЕНИЕ
________________________________________________________________
Для каждой закодированной команды ассемблер генерирует одну команду
на машинном языке. Но для каждого закодированного оператора компиляторного
языка Pascal или C генерируется один или более (чаще много) команд
машинного языка. В этом отношении можно считать, что компиляторный язык
состоит из макрооператоров.
Ассемблер MASM также имеет макросредства, но макросы здесь
определяются программистом. Для этого задается имя макроса, директива
MACRO, различные ассемблерные команды, которые должен генерировать данный
макрос и для завершения макроопределения - директива MEND. Затем в любом
месте программы, где необходимо выполнение определенных в макрокоманде
команд, достаточно закодировать имя макроса. В результате ассемблер
сгенерирует необходимые команды.
Использование макрокоманд позволяет:
- упростить и сократить исходный текст программы;
- сделать программу более понятной;
- уменьшить число возможных ошибок кодирования.
Примерами макрокоманд могут быть операции ввода-вывода, связанные с
инициализацией регистров и выполнения прерываний преобразования ASCII и
двоичного форматов данных, арифметические операции над длинными полями,
обработка строковых данных, деление с помощью вычитания.
В данной главе рассмотрены особенности макросредств, включая те,
которые не достаточно ясно даны в руководстве по ассемблеру. Тем не менее
пояснения для некоторых малоиспользуемых операций следует искать в
руководстве по ассемблеру.
ПРОСТОЕ МАКРООПРЕДЕЛЕНИЕ
________________________________________________________________
Макроопределение должно находиться до определения сегмента.
Рассмотрим пример простого макроопределения по имени INIT1, которое
инициализирует сегментные регистры для EXE-программы:
INIT1 MACRO ;Начало
ASSUME CS:CSEG,DS:DSEG,SS:STACK;ES:DSEG ;________________
PUSH DS ; |
SUB AX,AX ; |
PUSH AX ;Тело |
MOV AX,DSEG ;макроопределения|
MOV DS,AX ; |
MOV ES,AX ;________________|
ENDM ;Конец
Директива MACRO указывает ассемблеру, что следующие команды до
директивы ENDM являются частью макроопределения. Имя макрокоманды - INIT1,
хотя здесь возможны другие правильные уникальные ассемблерные имена.
Директива ENDM завершает макроопределение. Семь команд между директивами
MACRO и ENDM составляют тело макроопределения.
Имена, на которые имеются ссылки в макроопределении, CSEG, DSEG и
STACK должны быть определены где-нибудь в другом месте программы.
Макрокоманда INIT1 может использоваться в кодовом сегменте там, где
необходимо инициализировать регистры. Когда ассемблер анализирует команду
INIT1, он сначала просматривает таблицу мнемокодов и, не обнаружив там
соответствующего элемента, проверяет макрокоманды. Так как программа
содержит определение макрокоманды INIT1 ассемблер подставляет тело
макроопределения, генерируя необходимые команды - макрорасширение.
Программа использует рассматриваемую макрокоманду только один раз, хотя
имеются другие макрокоманды, предназначенные на любое число применений и
для таких макрокоманд ассемблер генерирует одинаковые макрорасширения.
На рис.20.1 показана ассемблированная программа. В листинге
макрорасширения каждая команда, помеченная слева знаком плюс (+), является
результатом генерации макрокоманды. Кроме того, в макрорасширении
отсутствует директива ASSUME, так как она не генерирует объектный код.
В последующем разделе "Включение из библиотеки макроопределений
показана возможность каталогизации макрокоманд в библиотеке и
автоматическое включение их в любые программы.
__________________________________________________________________________
TITLE MACRO1 (EXE) Макрос для инициализации
; --------------------------------------------
INIT1 MACRO
ASSUME CS:CSEG,DS:DSEG,SS:STACK,ES:DSEG
PUSH DS
SUB AX,AX
PUSH AX
MOV AX,DSEG
MOV DS,AX
MOV ES,AX
ENDM ;Конец макрокоманды
; --------------------------------------------
0000 STACK SEGMENT PARA STACK 'Stack'
0000 20 [ ???? ] DW 32 DUP(?)
0040 STACK ENDS
; --------------------------------------------
0000 DSEG SEGMENT PARA 'Data'
0000 54 65 73 74 20 6F MESSGE DB 'Test of macro-instruction', 13
66 20 6D 61 63 72
6F 2D 69 6E 73 74
72 65 73 74 69 6F
6E 0D
001A DSEG ENDS
; --------------------------------------------
0000 CSEG SEGMENT PARA 'Code'
0000 BEGIN PROC FAR
INIT1 ;Макрокоманда
0000 1E + PUSH DS
0001 2B C0 + SUB AX,AX
0003 50 + PUSH AX
0004 B8 ---- R + MOV AX,DSEG
0007 8E D8 + MOV DS,AX
0009 8E C0 + MOV ES,AX
000B B4 40 MOV AH,40H ;Вывод на экран
000D BB 0001 MOV BX,01 ;Номер
0010 B9 001A MOV CX,26 ;Длина
0013 8D 16 0000 R LEA DX,MESSGE ;Сообщение
0017 CD 21 INT 21H
0019 CB RET
001A BEGIN ENDP
001A CSEG ENDS
END BEGIN
Macros:
N a m e Length
INIT1. . . . . . . . . . . . . . . . 0004
Segments and Groups:
N a m e Size Align Combine Class
CSEG . . . . . . . . . . . . . . . . 001A PARA NONE 'CODE'
DSEG . . . . . . . . . . . . . . . . 001A PARA NONE 'DATA'
STACK. . . . . . . . . . . . . . . . 0040 PARA STACK 'STACK'
Symbols:
N a m e Type Value Attr
BEGIN. . . . . . . . . . . . . . . . F PROC 0000 CSEG Length=001A
MESSAGE. . . . . . . . . . . . . . . L BYTE 0000 DSEG
__________________________________________________________________________
Рис.20.1. Пример ассемблирования макрокоманды.
ИСПОЛЬЗОВАНИЕ ПАРАМЕТРОВ В МАКРОКОМАНДАХ
________________________________________________________________
В предыдущем макроопределении требовались фиксированные имена
сегментов: CSEG, DSEG и STACK. Для того, чтобы макрокоманда была более
гибкой и могла принимать любые имена сегментов, определим эти имена, как
формальные параметры:
INIT2 MACRO CSNAME,DSNAME,SSNAME ;Формальные параметры
ASSUME CS:CSNAME,DS:DSNAME,SC:SSNAME,ES:DSNAME
PUSH DS
SUB AX,AX
PUSH AX
MOV AX,DSNAME
MOV DS,AX
MOV ES,AX
ENDM ;Конец макроопределения
Формальные параметры в макроопределении указывают ассемблеру на
соответствие их имен любым аналогичным именам в теле макроопределения. Все
три формальных параметра CSNAME, DSNAME и SSNAME встречаются в директиве
ASSUME, а параметр DSNAME еще и в последующей команде MOV. Формальные
параметры могут иметь любые правильные ассемблерные имена, не обязательно
совпадающими именами в сегменте данных.
Теперь при использовании макрокоманды INIT2 необходимо указать в
качестве параметров действительные имена трех сегментов в соответствующей
последовательности. Например, следующая макрокоманда содержит три
параметра, которые соответствуют формальным параметрам в исходном
макроопределении:
Макроопределение: INIT2 MACRO CSNAME,DSNAME,SSNAME (форм. параметры)
| | |
Макрокоманда: INIT2 CSEG,DSEG,STACK (параметры)
Так как ассемблер уже определил соответствие между формальными параметрами
и операторами в макроопределении, то теперь ему остается подставить
параметры макрокоманды в макрорасширении:
- Параметр 1: CSEG ставится в соответствие с CSNAME в
макроопределении. Ассемблер подставляет CSEG вместо CSNAME в
директиве ASSUME.
- Параметр 2: DSEG ставится в соответствие с DSNAME в
макроопределении. Ассемблер подставляет DSEG вместо двух DSNAME: в
директиве ASSUME и в команде MOV.
- Параметр 3: STACK ставится в соответствие с SSNAME в
макроопределении. Ассемблер подставляет STACK вместо SSNAME в
директиве ASSUME.
Макроопределение с формальными параметрами и соответствующее
макрорасширение приведены на рис.20.2.
Формальный параметр может иметь любое правильное ассемблерное имя
(включая имя регистра, например, CX), которое в процессе ассемблирования
будет заменено на параметр макрокоманды. Отсюда следует, что ассемблер не
распознает регистровые имена и имена, определенные в области данных, как
таковые. В одной макрокоманде может быть определено любое число формальных
параметров, разделенных запятыми, вплоть до 120 колонки в строке.
__________________________________________________________________________
TITLE MACRO2 (EXE) Использование параметров
; ----------------------------------------------
INIT2 MACRO CSNAME,DSNAME,SSNAME
ASSUME CS:CSNAME,DS:DSNAME
ASSUME SS:SSNAME,ES:DSNAME
PUSH DS
SUB AX,AX
PUSH AX
MOV AX,DSNAME
MOV DS,AX
MOV ES,AX
ENDM ;Конец макрокоманды
; ----------------------------------------------
0000 STACK SEGMENT PARA STACK 'Stack'
0000 20 [ ???? ] DW 32 DUP(?)
0040 STACK ENDS
; ----------------------------------------------
0000 DSEG SEGMENT PARA 'Data'
0000 54 65 73 74 20 6F MESSAGE DB 'Test of macro', '$'
66 20 6D 61 63 72
6F 24
000E DSEG ENDS
; ----------------------------------------------
0000 CSEG SEGMENT PARA 'Code'
0000 BEGIN PROC FAR
INIT2 CSEG,DSEG,STACK
0000 1E + PUSH DS
0001 2B C0 + SUB AX,AX
0003 50 + PUSH AX
0004 B8 ---- R + MOV AX,DSEG
0007 8E D8 + MOV DS,AX
0009 8E C0 + MOV ES,AX
000B B4 09 MOV AH,09 ;Вывод на экран
000D 8D 16 0000 R LEA DX,MESSGE ;Сообщение
0011 CD 21 INT 21H
0013 CB RET
0014 BEGIN ENDP
0014 CSEG ENDS
END BEGIN
__________________________________________________________________________
Рис.20.2. Использование параметров в макрокомандах.
КОММЕНТАРИИ
________________________________________________________________
Для пояснений назначения макроопределения в нем могут находиться
комментарии. Директива COMMENT или символ точка с запятой указывают на
строку комментария, как это показано в следующем макроопределении PROMPT:
PROMPT MACRO MESSGE
;Эта макрокоманда выводит сообщения на экран
MOV AH,09H
LEA DX,MESSGE
INT 21H
ENDM
Так как по умолчанию в листинг попадают только команды генерирующие
объектный код, то ассемблер не будет автоматически выдавать и комментарии,
имеющиеся в макроопределении. Если необходимо, чтобы в расширении
появлялись комментарии, следует использовать перед макрокомандой директиву
.LALL ("list all" - выводить все), которая кодируется вместе с лидирующей
точкой:
.LALL
PROMPT MESSAG1
Макроопределение может содержать несколько комментариев, причем
некоторые из них могут выдаваться в листинге, а другие - нет. В первом
случае необходимо использовать директиву .LALL. Во втором - кодировать
перед комментарием два символа точка с запятой (;;) - признак подавления
вывода комментария в листинг. По умолчанию в ассемблере действует
директива .XALL, которая выводит в листинг только команды, генерирующие
объектный код. И, наконец, можно запретить появление в листинге
ассемблерного кода в макрорасширениях, особенно при использовании
макрокоманды в одной программе несколько раз. Для этого служит директива
.SALL ("suppress all" - подавить весь вывод), которая уменьшает размер
выводимого листинга, но не оказывает никакого влияния на размер объектного
модуля.
Директивы управления листингом .LALL, .XALL, .SALL сохраняют свое
действие по всему тексту программы, пока другая директива листинга не
изменит его. Эти директивы можно размещать в программе так, чтобы в одних
макрокомандах распечатывались комментарии, в других - макрорасширения, а в
третьих подавлялся вывод в листинг.
Программа на рис.20.3 демонстрирует описанное выше свойство директив
листинга. В программе определено два макроопределения INIT2 и PROMPT,
рассмотренные ранее. Кодовый сегмент содержит директиву .SALL для
подавления распечатки INIT2 и первого расширения PROMPT. Для второго
расширения PROMPT директива .LALL указывает ассемблеру на вывод в листинг
комментария и макрорасширения. Заметим, однако, что комментарий,
отмеченный двумя символами точка с запятой (;;) в макроопределении PROMPT,
не распечатывается в макрорасширениях независимо от действия директив
управления листингом.
__________________________________________________________________________
TITLE MACRO3 (EXE) Директивы .LALL и .SALL
; -----------------------------------------------
INIT2 MACRO CSNAME,DSNAME,SSNAME
ASSUME CS:CSNAME,DS:DSNAME
ASSUME SS:SSNAME,ES:DSNAME
PUSH DS
SUB AX,AX
PUSH AX
MOV AX,DSNAME
MOV DS,AX
MOV ES,AX
ENDM
; -----------------------------------------------
PROMPT MACRO MESSAGE
; Макрокоманда выводит на экран любые сообщения
;; Генерирует команды вызова DOS
MOV AH,09 ;Вывод на экран
LEA DX,MESSAGE
INT 21H
ENDM
; -----------------------------------------------
0000 STACK SEGMENT PARA STACK 'Stack'
0000 20 [ ???? ] DW 32 DUP (?)
0040 STACK ENDS
; -----------------------------------------------
0000 DATA SEGMENT PARA 'Data'
0000 43 75 73 74 6F 6D MESSG1 DB 'Customer name?', '$'
65 72 20 6E 61 6D
65 3F 24
000F 43 75 73 74 6F 6D MESSG2 DB 'Customer address?', '$'
65 72 20 61 64 64
72 65 73 73 3F 24
0021 DATA ENDS
; -----------------------------------------------
0000 CSEG SEGMENT PARA 'Code'
0000 BEGIN PROC FAR
.SALL
INIT2 CSEG,DATA,STACK
PROMPT MESSG1
.LALL
PROMPT MESSG2
+ ; Макрокоманда выводит на экран любые сообщения
0013 B4 09 + MOV AH,09 ;Вывод на экран
0015 8D 16 000F R + LEA DX,MESSG2
0019 CD 21 + INT 21H
001B CB RET
001C BEGIN ENDP
001C CSEG ENDS
END BEGIN
__________________________________________________________________________
Рис.20.3. Распечатка и подавление макрорасширений в листинге.
ИСПОЛЬЗОВАНИЕ МАКРОКОМАНД В МАКРООПРЕДЕЛЕНИЯХ
________________________________________________________________
Макроопределение может содержать ссылку на другое макроопределение.
Рассмотрим простое макроопределение DOS21, которое заносит в регистр AH
номер функции DOS и выполняет INT 21H:
DOS21 MACRO DOSFUNC
MOV AH,DOSFUNC
INT 21H
ENDM
Для использования данной макрокоманды при вводе с клавиатуры необходимо
закодировать:
LEA DX,NAMEPAR
DOS21 0AH
Предположим, что имеется другое макроопределение, использующее функцию 02
в регистре AH для вывода символа:
DISP MACRO CHAR
MOV AH,02
MOV DL,CHAR
INT 21H
ENDM
Для вывода на экран, например, звездочки достаточно закодировать
макрокоманду DISP '*'. Можно изменить макроопределение DISP,
воспользовавшись макрокомандой DOC21:
DISP MACRO CHAR
MOV DL,CHAR
DOS21 02
ENDM
Теперь, если закодировать макрокоманду DISP в виде DISP '*', то ассемблер
сгенерирует следующие команды:
MOV DL,'*'
MOV AH,02
INT 21H
ДИРЕКТИВА LOCAL
________________________________________________________________
В некоторых макрокомандах требуется определять элементы данных или
метки команд. При использовании такой макрокоманды в программе более
одного раза происходит также неоднократное определение одинаковых полей
данных или меток. В результате ассемблер выдаст сообщения об ошибке из-за
дублирования имен. Для обеспечения уникальности генерируемых в каждом
макрорасширении имен используется директива LOCAL, которая кодируется
непосредственно после директивы MACRO, даже перед комментариями. Общий
формат имеет следующий вид:
LOCAL dummy-1,dummy-2,... ;Формальные параметры
Рис.20.4 иллюстрирует использование директивы LOCAL. В приведенной на
этом рисунке программе выполняется деление вычитанием; делитель вычитается
из делимого и частное увеличивается на 1 до тех пор, пока делимое больше
делителя. Для данного алгоритма необходимы две метки: COMP - адрес цикла,
OUT - адрес выхода из цикла по завершению. Обе метки COMP и OUT определены
как LOCAL и могут иметь любые правильные ассемблерные имена.
В макрорасширении для COMP генерируется метка ??0000, а для OUT -
??0001. Если макрокоманда DIVIDE будет использована в этой программе еще
один раз, то в следующем макрорасширении будут сгенерированы метки ??0002
и ??0003 соответственно. Таким образом, с помощью директивы LOCAL
обеспечивается уникальность меток в макрорасширениях в одной программе.
__________________________________________________________________________
TITLE MACRO4 (COM) Использование директивы LOCAL
; -------------------------------------------------
DIVIDE MACRO DIVIDEND,DIVISOR,QUOTIENT
LOCAL COMP
LOCAL OUT
; AX=делимое, BX=делитель, CX=частное
MOV AX,DIVIDEND ;Загрузить делимое
MOV BX,DIVISOR ;Загрузить делитель
SUB CX,CX ;Регистр для частного
COMP:
CMP AX,BX ;Делимое " делителя?
JB OUT ; да - выйти
SUB AX,BX ;Делимое - делитель
INC CX ;Частное + 1
JMP COMP
OUT:
MOV QUOTIENT,CX ;Записать результат
ENDM
; ------------------------------------------------
0000 CSEG SEGMENT PARA 'Code'
ASSUME CS:CSEG,DS:CSEG,SS:CSEG,ES:CSEG
0100 ORG 100H
0100 EB 06 BEGIN: JMP SHORT MAIN
; ------------------------------------------------
0102 0096 DIVDND DW 150 ;Делимое
0104 001B DIVSOR DW 27 ;Делитель
0106 ???? QUOTNT DW ? ;Частное
; ------------------------------------------------
0108 MAIN PROC NEAR
.LALL
DIVIDE DIVDND,DIVSOR,QUOTNT
+ ; AX=делимое, BX=делитель, CX=частное
0108 A1 0102 R + MOV AX,DIVDND ;Загрузить делимое
010B 8B 1E 0104 R + MOV BX,DIVSOR ;Загрузить делитель
010F 2B C9 + SUB CX,CX ;Регистр для частного
0111 + ??0000:
0111 3B C3 + CMP AX,BX ;Делимое " делителя?
0113 72 05 + JB ??0001 ; да - выйти
0115 2B C3 + SUB AX,BX ;Делимое - делитель
0117 41 + INC CX ;Частное + 1
0118 EB F7 + JMP ??0000
011A + ??0001:
011A 89 0E 0106 R + MOV QUOTNT,CX ;Записать результат
011E C3 RET
011F
...Поділитися у соцмережах