Использование ДУА в макросах
Пример 1. Опишем в виде макроса операцию сдвига значения переменной x на n разрядов вправо. n – явно заданное положительное число. Макрорасширение должно содержать минимально возможное число команд. Это можно сделать так:
shift macro x, n Обращения: shift A, 5 ; x = A, n = 5
ife n – 1 ; n – 1 = 0 ? После макрогенерации:
shr x, 1 mov CL, 5
else ; n > 1 shr A, CL
mov CL, n
shr x, CL
endif
endm
Пример 2.Константное выражение в if и ife может быть любым, но так как оно вычисляется на этапе макрогенерации, в нем не должно быть ссылок на величины, которые станут известными только при выполнении программы. Например, в константных выражениях не должно быть ссылок на регистры и ячейки памяти, не должно быть ссылок вперед. Константное выражение должно быть вычислено макрогенератором при первом проходе. Константное выражение часто бывает логическим, в нем могут использоваться операторы отношения: EQ, NE, LT, LE, GT, GE и логические операторы NOT, OR, XOR. Запишем в виде макроса SET_0 x операцию x = 0, если x – переменная размером в байт, слово или двойное слово.
SET_0 macro x
if type x EQ dword
mov dword ptr x, 0
elseif type x EQ word
mov word ptr x, 0
else mov byte ptr x, 0
endif
endm
Пример 3.Напишем еще одно макроопределение для сдвига вправо на n разрядов значения байтовой переменной B. Учтем, что при n = 0 сдвига нет и макрорасширение не должно появиться в тексте программы, а при n > 7 результат сдвига – это 0, поэтому сдвиг можно заменить записью нуля в B. Set_0 macro B, n
if (n GT 0) AND (n LT 8) ;; 0 < n <8
mov CL, n
shr b, CL
else
if n GE 8 ;; n >= 8
mov B, 0
endif
endif
endm
Пример 4 использование ifidn и ifdif. Поиск max или min из двух знаковых величин, хранящихся в байтовых регистрах, т.е. вычислить R1 = T (R1, R2),где T – это max или min, причем, должно генерироваться непустое макрорасширение только если R1 и R2 – это разные регистры. Если обращение к макросу будет Max_Min R1, R2, T, то необходимо проверять несовпадение первых двух параметров. И чтобы один макрос вычислял и max и min, нужно проверять и значение третьего параметра. Макрос может быть таким:
Max_Min macro R1, R2, T
Local L
ifdif <R1>, <R2> ;; R1 и R2 – разные регистры
Cmp R1, R2
ifidn <T>, <max> ;; T = max ?
Jge L
Else
Jle L
Endif
Mov R1, R2
L: endif
Endm
Макрокоманда Max_Min AL, BH, MIN приведет к следующим действиям макрогенератора: (пусть метка L заменилась меткой вида ??0110)
----------------------------
ifdif <AL>, <BH> cmp AL, BH в исходном тексте
cmp AL, BH ifidn <MIN>, <MAX> программы
ifidn <MIN>, <MAX> jge ??0110 окажется
jge ??0110 else cmp AL, BH
else jle ??0110 jle ??0110
jle ??0110 endif mov AL, BH
endif mov AL, BH ??0110:
mov AL, BH ??0110: ----------------------
??0110: ----------------------------
endif
26) Многомодульные программы в Ассемблере, директивы для организации межмодульных связей
Предположим, что есть модуль, содержащий процедуры ввода и вывода символов и строк, который подключается к основной программе на этапе редактирования:
Masm p.asm, p.obj, p.list
link p.obj + ioproc.obj, p.exe
P.exe
Есть файл io.asm, содержащий описания макросов обращения к этим процедурам. Этот файл подключается на этапе ассемблирования с помощью директивы include io.asm.
Для иллюстрации организации многомодульной программы решим задачу: ввести текст не более, чем из 100 символов, заканчивающийся точкой и вывести его в обратном порядке, заменив прописные буквы на строчные.
Пусть программа состоит из двух модулей - головного и вспомогательного. Во вспомогательном описывается переменная EOT, значением которой является символ конца ввода текста, и процедура LOWLAT, заменяющую прописную на строчную.
Головной модуль должен вводить текст, записывать его в массив в обратном порядке, обращаясь к процедуре LOWLAT для замены больших букв на малые, а затем выводить этот массив на экран.
; вспомогательный модуль
Public EOT, LOWLAT
D1 segment
EOT DB ‘.’ ; символ конца ввода
D1 ends
C1 segment
Assume CS: C1
LOWLAT proc far
; процедура перевода больших букв в малые,
; на входе (AL) – любой символ, на выходе (AL) – малая буква
cmp AL, ‘A’ ; AL < ‘A’ или AL > ‘Z’, то nolat
Jb nolat
cmp AL, ‘Z’
Ja nolat
add AL, - ‘A’ + ‘a’
Nolat: ret
LOWLAT endp
C1 ends
End
; головной модуль
include io.asm
extrn EOT: byte, LOWLAT: far
s segment stack
DB 256 dup (?)
s ends
d segment
txt DB 100 dup (?), ‘$’
d ends
c segment
assume SS: s, DS: d, CS: c
start: mov AX, d
mov DS, AX ; DS = d для доступа к TXT
mov AX, seg EOT
mov ES, AX ; ES = D1 для доступа к EOT
mov SI, 100
OutCH ‘>’ ; приглашение к вводу символа
inp: InCH AL ; ввод символа
cmp AL, ES:EOT ; если достигнут конец ввода
je PR ; то PR
call LOWLAT ; замена символа
dec SI
mov TXT[SI], AL
jmp inp
PR: lea DX, TXT[SI] ; вывод
OutSTR ; на экран TXT
Finish
c ends
end start
В основной программе OutCH <параметр> и InCH <параметр> - макрокоманды ввода и вывода на экран параметра OutSTR - макрокоманда вывода строки
Finish macro ; макрос окончания счета
mov AH, 4Ch
int 21h
endm
2) Передача параметров в подпрограммы по ссылке и по значению, организация рекурсивных подпрограмм
При передаче параметров по значению процедуре передается значение фактического параметра, оно копируется в ПП, и ПП использует копию, поэтому изменение, модификация параметра оказывается невозможным. Этот механизм используется для передачи параметров небольшого размера.
Например, нужно вычислить c = max (a, b) + max (7, a-1). Здесь все числа знаковые, размером в слово. Используем передачу параметров через регистры. Процедура получает параметры через регистры AX и BX, результат возвращает в регистре AX.
Процедура: AX = max (AX, BX)
max proc
cmp AX, BX
jge met1
mov AX, BX
met1: ret
max endp
Фрагмент вызывающей программы:
----------------------------------
; c = max (a,b) + max (7, a-1)
mov AX, a
mov BX, b
call max ; AX = max (a,b)
mov c, AX ; c = max (a,b)
mov AX, 7
mov BX, a
Dec BX
call max ; AX = max (7, a-1)
add c, AX