Small WEB:
Wibe поисковик по WEB1.0http://www.dvara.net/HK/index.asp
Windows 93
db 90h
Макроинструкции могут быть определены для генерации некоторых конкретных последовательностей данных в зависимости от предоставленных параметров. Они могут соответствовать инструкциям выбранного машинного языка, как в следующем примере, но они также могут быть определены для генерирования других видов данных для различных целей.macro int number
if number = 3
db 0CCh
else
db 0CDh, number
end if
end macro
int 20h ; generates two bytes
Сборка, рассматриваемая таким образом, может рассматриваться как некий интерпретируемый язык, и ассемблер, безусловно, обладает многими характеристиками интерпретатора. Однако он также разделяет некоторые аспекты с компилятором. Для инструкции возможно использовать значение, которое определено позже в источнике и может зависеть от инструкций, предшествующих этому определению, как показано в следующем примере.macro jmpi target
if target-($+2) < 80h & target-($+2) >= -80h
db 0EBh
db target-($+1)
else
db 0E9h
dw target-($+2)
end if
end macro
jmpi start
db 'some data'
start:
Определенный выше «jmpi» создает код инструкции перехода, как в архитектуре 8086. Такой код содержит относительное смещение цели перехода, хранящееся либо в однобайтовом, либо в 16-битном слове. Относительное смещение вычисляется как разница между адресом цели и адресом следующей инструкции. Специальный символ «$» предоставляет адрес текущей инструкции, и он используется для вычисления относительного смещения и определения, может ли оно поместиться в один байт.x = (x-1)*(x+2)/2-2*(x+1)
db x
Циркулярная ссылка здесь сведена к единственному определению, которое ссылается на себя для построения значения. Плоский ассемблер g может найти решение в этом случае, хотя во многих других он может потерпеть неудачу. Метод, используемый этим ассемблером, состоит в том, чтобы выполнить несколько проходов по исходному тексту, а затем попытаться предсказать все значения с помощью знаний, собранных таким образом. Этот подход в большинстве случаев достаточно хорош для сборки машинных кодов, но редко бывает достаточно для решения сложных уравнений, и приведенный выше пример является одним из исключений.macro EX? first,second
match (=SP?), first
match =HL?, second
db 0E3h
else match =IX?, second
db 0DDh,0E3h
else match =IY?, second
db 0FDh,0E3h
else
err "incorrect second argument"
end match
else match =AF?, first
match =AF'?, second
db 08h
else
err "incorrect second argument"
end match
else match =DE?, first
match =HL?, second
db 0EBh
else
err "incorrect second argument"
end match
else
err "incorrect first argument"
end match
end macro
EX (SP),HL
EX (SP),IX
EX AF,AF'
EX DE,HL
"?" символ появляется во многих местах, чтобы пометить имена как нечувствительные к регистру, и все эти вхождения могут быть удалены, чтобы еще больше упростить пример.A? equ [:111b:]
B? equ [:000b:]
C? equ [:001b:]
D? equ [:010b:]
E? equ [:011b:]
H? equ [:100b:]
L? equ [:101b:]
macro INC? argument
match [:r:], argument
db 100b + r shl 3
else match (=HL?), argument
db 34h
else match (=IX?+d), argument
db 0DDh,34h,d
else match (=IY?+d), argument
db 0FDh,34h,d
else
err "incorrect argument"
end match
end macro
INC A
INC B
INC (HL)
INC (IX+2)
Этот подход имеет черту, которая не всегда может быть желательной: он позволяет использовать выражение типа «[: 0:]» непосредственно в аргументе. Но можно предотвратить использование синтаксиса таким образом, используя префикс в конструкции «match»:REG.A? equ [:111b:]
REG.B? equ [:000b:]
REG.C? equ [:001b:]
REG.D? equ [:010b:]
REG.E? equ [:011b:]
REG.H? equ [:100b:]
REG.L? equ [:101b:]
macro INC? argument
match [:r:], REG.argument
db 100b + r shl 3
else match (=HL?), argument
db 34h
else match (=IX?+d), argument
db 0DDh,34h,d
else match (=IY?+d), argument
db 0FDh,34h,d
else
err "incorrect argument"
end match
end macro
В случае аргумента, структурированного как «(IX + d)», иногда может потребоваться разрешить другие алгебраически эквивалентные формы выражения, например «(d + IX)» или «(c + IX + d)». Вместо того, чтобы анализировать каждый возможный вариант по отдельности, можно позволить ассемблеру оценивать выражение, одновременно обрабатывая выбранный символ. Когда символ объявляется как «элемент», он не имеет значения, а когда он используется в выражении, он рассматривается алгебраически как переменный член в полиноме.element HL?
element IX?
element IY?
macro INC? argument
match [:r:], argument
db 100b + r shl 3
else match (a), argument
if a eq HL
db 34h
else if a relativeto IX
db 0DDh,34h,a-IX
else if a relativeto IY
db 0FDh,34h,a-IY
else
err "incorrect argument"
end if
else
err "incorrect argument"
end match
end macro
INC (3*8+IX+1)
virtual at IX
x db ?
y db ?
end virtual
INC (y)
Существует небольшая проблема с вышеуказанной макроинструкцией. Параметр может содержать любой текст, и когда такое значение помещается в выражение, оно может вызвать ошибочное поведение. Например, если обработать «INC (1 | 0)», выражение «a eq HL» будет преобразовано в «1 | 0 eq HL», и это логическое выражение будет правильным и истинным, даже если аргумент был искажен. Чтобы этого не происходило, в качестве прокси-сервера может использоваться локальная переменная, содержащая значение аргумента:macro INC? argument
match [:r:], argument
db 100b + r shl 3
else match (a), argument
local value
value = a
if value eq HL
db 34h
else if value relativeto IX
db 0DDh,34h,a-IX
else if value relativeto IY
db 0FDh,34h,a-IY
else
err "incorrect argument"
end if
else
err "incorrect argument"
end match
end macro
У такой прокси-переменной есть дополнительное преимущество благодаря тому, что ее значение вычисляется до того, как макроинструкция начнет генерировать какой-либо вывод. Когда выражение содержит символ, такой как «$», оно может давать разные значения в зависимости от того, где оно вычисляется, и использование прокси-переменной гарантирует, что полученное значение будет тем, которое получено путем оценки аргумента перед генерацией кода инструкции.element register
element A? : register + 111b
element B? : register + 000b
element C? : register + 001b
element D? : register + 010b
element E? : register + 011b
element H? : register + 100b
element L? : register + 101b
element HL?
element IX?
element IY?
macro INC? argument
local value
match (a), argument
value = a
if value eq HL
db 34h
else if value relativeto IX
db 0DDh,34h,a-IX
else if value relativeto IY
db 0FDh,34h,a-IY
else
err "incorrect argument"
end if
else match any more, argument
err "incorrect argument"
else
value = argument
if value eq value element 1 & value metadata 1 relativeto register
db 100b + (value metadata 1 - register) shl 3
else
err "incorrect argument"
end if
end match
end macro
Паттерн «any more» предназначен для перехвата любого аргумента, который содержит сложные выражения, состоящие из более чем одного токена. Это предотвращает использование синтаксиса, такого как «INC A + 0» или «INC A + B-A». Но в случае некоторых наборов инструкций включение такого ограничения может зависеть от личных предпочтений.element CC
NZ? := CC + 000b
Z? := CC + 001b
NC? := CC + 010b
C? := CC + 011b
PO := CC + 100b
PE := CC + 101b
P := CC + 110b
M := CC + 111b
macro CALL? arguments&
local cc,nn
match condition =, target, arguments
cc = condition - CC
nn = target
db 0C4h + cc shl 3
else
nn = arguments
db 0CDh
end match
dw nn
end macro
CALL 0
CALL NC,2135h
Этот подход также позволяет обрабатывать другие, более сложные случаи, например, когда аргументы могут содержать запятые или разделяться различными способами.struc INC? argument
.:
INC argument
end struc
start INC A
INC B
iterate instruction, EX,INC,CALL
struc instruction? argument
.: instruction argument
end struc
end iterate
Это должно быть сделано для каждой инструкции, которая должна разрешить такой синтаксис. Достаточно простого цикла, подобного следующему:struc ? tail&
match :, tail
.:
else match : instruction, tail
.: instruction
else match == value, tail
. = value
else
.: tail
end match
end struc
Каждая встроенная инструкция, которая определяет данные, уже имеет помеченный вариант.struc ? tail&
match :, tail
label . at $ shr 1
else match : instruction, tail
label . at $ shr 1
instruction
else
. tail
end match
end struc
Очевидно, что больше не требуется определять какие-либо конкретные помеченные макроинструкции, когда применяется глобальный эффект такого рода. Вариант должен быть выбран в зависимости от типа синтаксиса, который должен быть разрешен.element CODEBASE
org CODEBASE + 0
macro CALL? argument
local value
value = argument
if value relativeto CODEBASE
db 0CDh
dw value - CODEBASE
else
err "incorrect argument"
end if
end macro
Значение текущего адреса, которое используется для определения меток, может быть изменено с помощью «org». Если метки необходимо отличать от абсолютных значений, символ, определенный с помощью «элемента», может использоваться для формирования адреса:element DATA
DATA_OFFSET = 2000h
element CODE
CODE_OFFSET = 1000h
macro DATA?
_END
virtual at DATA + DATA_OFFSET
end macro
macro CODE?
_END
org CODE + CODE_OFFSET
end macro
macro _END?
if $ relativeto DATA
DATA_OFFSET = $ - DATA
end virtual
else if $ relativeto CODE
CODE_OFFSET = $ - CODE
end if
end macro
postpone
_END
end postpone
CODE
Чтобы определить метки в адресном пространстве, которое не будет отражено в выводе, должен быть объявлен «виртуальный» блок. Следующий пример подготавливает макроинструкции «ДАННЫЕ» и «КОД» для переключения между генерацией программных инструкций и меток данных. Только коды команд будут отправлены на выход:macro CALL? argument
local value
value = argument
if value relativeto CODE
db 0CDh
dw value - CODE
else if value relativeto 0
db 0CDh
dw value
else
err "incorrect argument"
end if
end macro
DATA
variable db ?
CODE
routine:
Блок «отложить» используется здесь, чтобы гарантировать, что «виртуальный» блок всегда закрывается правильно, даже если исходный текст заканчивается определениями данных.virtual at 0
Relocations::
rw RELOCATION_COUNT
end virtual
RELOCATION_INDEX = 0
postpone
RELOCATION_COUNT := RELOCATION_INDEX
end postpone
macro WORD? value
if value relativeto CODE
store $ - CODE : 2 at Relocations : RELOCATION_INDEX shl 1
RELOCATION_INDEX = RELOCATION_INDEX + 1
dw value - CODE
else
dw value
end if
end macro
macro CALL? argument
local value
value = argument
if value relativeto CODE | value relativeto 0
db 0CDh
word value
else
err "incorrect argument"
end if
end macro
В этом контексте «CALL рутина» или «CALL 1000h» будет разрешено, а «CALL variable» не будет.load RELOCATIONS : RELOCATION_COUNT shl 1 from Relocations : 0
dw RELOCATIONS
Таблица перемещений, созданная таким образом, может быть доступна с помощью «load». Следующие две строки могут быть использованы для размещения таблицы целиком где-то в выводе:element MOD.HIGH
element MOD.LOW
HIGH? equ MOD.HIGH +
LOW? equ MOD.LOW +
macro BYTE? value
if value relativeto MOD.HIGH + CODE
; register HIGH relocation
db (value - MOD.HIGH - CODE) shr 8
else if value relativeto MOD.LOW + CODE
; register LOW relocation
db (value - MOD.LOW - CODE) and 0FFh
else if value relativeto MOD.HIGH
db (value - MOD.HIGH) shr 8
else if value relativeto MOD.LOW
db (value - MOD.LOW) and 0FFh
else
db value
end if
end macro
«Load» считывает всю таблицу в одну строку, а затем «dw» записывает ее в вывод (дополняемый несколькими словами, но в этом случае строка никогда не требует такого дополнения).BYTE HIGH address
BYTE LOW address
Команды, которые регистрировали бы перемещение, были опущены для ясности, в этом случае не только смещение в коде, но и некоторая дополнительная информация должна была бы быть зарегистрирована в соответствующих структурах. С такой подготовкой перемещаемые блоки в коде могут быть сгенерированы как:include '8086.inc'
org 100h
jmp CodeSection
DataSection:
virtual
Data::
end virtual
postpone
virtual Data
load Data.OctetString : $ - $$ from $$
end virtual
end postpone
db Data.OctetString
CodeSection:
virtual Data
Hello db "Hello!",24h
end virtual
mov ah,9
mov dx,Hello
int 21h
virtual Data
ExitCode db 37h
end virtual
mov ah,4Ch
mov al,[ExitCode]
int 21h
Такой подход позволяет легко включить синтаксис с модификаторами в любой инструкции, которая внутренне использует «байтовую» макроинструкцию при генерации кода.macro ? line&
match .=CODE?, line
CODE
else match .=DATA?, line
DATA
else
line
end match
end macro
Это приводит к относительно простому синтаксису даже без помощи каких-либо дополнительных макросов.macro concise
macro ? line&
match =end =concise, line
purge ?
else match dest+==src, line
ADD dest,src
else match dest-==src, line
SUB dest,src
else match dest==src, line
LD dest,src
else match dest++, line
INC dest
else match dest--, line
DEC dest
else match any, line
err "syntax error"
end match
end macro
end macro
concise
C=0
B++
A+=2
end concise
Строки, содержащие текст «.CODE» или «.DATA», обрабатываются здесь таким образом, что они вызывают глобальную макроинструкцию с соответствующим именем, тогда как все остальные перехваченные строки выполняются без изменений. Этот метод позволяет отфильтровать любой специальный синтаксис и позволить ассемблеру обрабатывать обычные инструкции как обычно.struc (head) ? tail&
match .=CODE?, head
CODE tail
else
head tail
end match
end struc
Макроинструкция, определенная таким образом, не перехватывает строки, которые содержат директивы, управляющие потоком сборки, такие как «if» или «repeat», и они все еще могут свободно использоваться внутри такого блока. Это изменится, если объявление будет в форме "macro?! Line &". Такой вариант будет перехватывать каждую строку без исключения.struc (head) ? tail&
local invoker
match .=CODE?, head
macro invoker
CODE tail
end macro
else
macro invoker
head tail
end macro
end match
invoker
end struc
Все эти подходы скрывают тонкую ловушку. За меткой, определенной с помощью «:», может следовать другая инструкция в той же строке. Если эта следующая инструкция (которая здесь становится скрытой в параметре tail) является директивой управления, такой как «if», то помещение ее в предложение «else» приведет к нарушению вложенности блоков управления. Решение состоит в том, чтобы каким-то образом вызывать содержимое «tail» вне блока «match». Одним из способов может быть вызов специального макроса:struc (head) ? tail&
match .=CODE?, head
CODE tail
macro ? line&
purge ?
end macro
end match
head tail
end struc
Но более простой вариант - вызвать исходную строку напрямую и, когда необходимо переопределение, заставить ее игнорироваться с помощью другого перехватчика строки (избавиться от него сразу после):macro definitions end?
namespace embedded
struc LABEL? size
match , size
.:
else
label . : size
end match
end struc
macro E#ND? name
end namespace
match any, name
ENTRYPOINT := name
end match
macro ?! line&
end macro
end macro
end macro
definitions end
start LABEL
END start
Можно добавить еще один макрос с целью игнорирования следующей строки, чтобы сделать это решение более компактным.
Параметр, данный макросу «определения», может показаться, что он ничего не делает, так как он заменяет каждый экземпляр «конца» одним и тем же словом - но текст, который приходит из параметра, снабжен дополнительной информацией о контексте, и этот атрибут затем сохраняется, когда текст становится частью нового макроса. Благодаря этому макрос «LABEL» может использоваться в пространстве имен, где инструкция «end» приняла другое значение, но экземпляры «end» в его теле все еще ссылаются на символ во внешнем пространстве имен.define link end
match end, link
namespace embedded
struc LABEL? size
match , size
.:
else
label . : size
end match
end struc
macro END? name
end namespace
match any, name
ENTRYPOINT := name
end match
macro ?! line&
end macro
end macro
end match
start LABEL
END start
Это не будет работать без передачи текста через символьную переменную, поскольку параметры, определенные управляющими директивами, такими как «match», не добавляют контекстную информацию к тексту, если он уже не был там.