Начиная с версии 2.7, компилятор ZXAsm++ вынесен в отдельную DLL, и его исходный код (на языке Pascal, Delphi) опубликован полностью (т.е. он теперь может быть использован в других эмуляторах Spectrum или может быть построен самостоятельный кросс-компилятор на его основе).
Исходный код на языке должен быть написан с соблюдением следующих правил.
Ассемблер понимает следующие директивы. (В обозначениях префиксный символ '#' используется для обозначения операндов, на месте которых могут быть любые допустимые числовые выражения). Так как большинство операторов и директив могут быть продолжены на следующей строке, только в директивах с ограничением на это правило сделаны соответствующие пометки.
ORG [{ RAMn | ROMn },] #target_address [, #dest_address ] Задает целевой адрес ассемблирования (т.е. адрес текущей инструкции во время исполнения) и адрес размещения инструкций при ассемблировании (он может отличаться от адреса инструкции, если команды размещаются первоначально не в тех адресах, по которым будут исполняться, или, например, не в том банке памяти, который сейчас виден по адресу C000). RAMn или ROMn могут использоваться для указания банка ОЗУ ( RAM n (n=0..7) ) или банка ПЗУ (ROM n (n=0..1) ). n может быть выражением. Все имена, используемые в выражениях в данной директиве, должны быть известны уже на первом проходе компиляции, ссылки вперед здесь недопустимы - но только если не выключена опция Strong Address Control в опциях ассемблера). |
|
ENTRYPOINT #expr Задает точку входа в программу (устанавливает PC в указанное значение в случае успешной компиляции). |
|
label[:] EQU #expression Задает константу label. Метка label должна быть уникальной, так же как и обычная метка. |
|
label[:] = #expression Задает переменную label и приравнивает ей значение выражения. Разрешается переопределять такую переменную с помощью оператора = сколько угодно раз в коде (т.е. уникальность метки label в этом случае не требуется, в отличие от директивы EQU). Примечание: после каждого шага компиляции, такие переменные сохраняют значение, полученное при последнем присваивании. Это может использоваться для каких-то вычислений на втором или третьем проходе компилятора, когда код генерируется окончательно. |
|
. = #expression или $ = #expression Специальная форма присваивания, где в левой части используется символ '.' или '$'. В результате, последующий код будет компилироваться с адреса, указанного выражением. Фактически, такой оператор эквивалентен ORG №addr, с той разницей, что данный адрес не попадает в список ORG-ов, доступных для быстрой установки регистра PC из ассемблера. |
|
ENUM label1[=#expr1][,label2[=#expr2]]... Создает набор констант label1, label2, ... Если выражения expr2, expr3, и т.д.
опущены, то следующая константа вычисляется как значение предыдущей + 1. Если
опущено #expr1, то первой константе в списке присваивается значение 0. |
|
label[:] DEFINE any text to the end of line Переопределяет идентификатор. В последующем этот идентификатор замещается указанной строкой везде в тексте. Об использовании макро-параметров, имен, переопределенных директивой DEFINE или переменных препроцессорного цикла FOR смотрите ниже. |
|
DEFB [[(#repeat_count1)]{#expr1|'string1'|?}][,[(#repeat_count2)][{#expr2|'string2'}]]... Определяет список байтов. Для строк в кавычках, для каждого символа создается свой байт. Если выше по тексту задана директива ENCODE, все символы перекодируются перед записью в память в соответствии с этой директивой. По умолчанию, DEFB 'abc',0 будет откомпилировано в 4 байта 61h,62h,63h,0. Если для значения задан повторитель (repeat_count), значение повторяется указанное число раз (если 0 или меньше, оно не генерируется вообще). Если вместо значения используется одиночный символ '?', байт в памяти не инициализируется и в процессе компиляции пропускается. |
|
DEFM[{ #expression | 'string' }] [, [{ #expression | 'string' }] ]... Список байтов - аналогично директиве DEFB, отличие только в том, что
в последнем байте строки бит 7 установлен. Т.е.,
DEFM 'abc' компилируется в 4 байта: 61h,62h,E3h.
|
|
DEFW [[(#repeat_count1)]{#expr1}][,[(#repeat_count2)][{#expr2}]]... Список 16-битных слов. Очень похож на DEFB, но для размещения каждого значения требуется в памяти 2 байта памяти (как обычно, младший значащий байт размещается первым - LSB). |
|
DEFS #repeat_count [, { #expression | 'string' | "string" } ]... Список байтов, как в директиве DEFB, но весь список повторяется указанное первым операндом число раз. Эквивалентно DUP #repeat_count DEFB list EDUP Если имеется только #repeat_count без значений, в памяти образуется заданное повторителем число нулевых байтов. |
|
DEFG ssssssss [ ssssssss ]... Особая директива для задания шрифтов (или двоичных значений байтов). Символ '.' представляет в записи 0, любой другой символ 1. Для разделения байтов используются пробелы. Числовые константы, начинающиеся с десятичной цифры, однако, считываются именно как числа. Если следующая строка стартует с одного из символов '.', 'x' или 'X', то предполагается, что DEFG занимает несколько строк, и в этом случае байты помещаются в память по столбцам, а не один за другим. Например: DEFG ........ .X...... DEFG .XXXXX.. .X...... DEFG ......X. .XXXXX.. DEFG ..XXXXX. .X....X. DEFG .X....X. .X....X. DEFG ..XXXX.X .XXXXX.. DEFG ........ ........ (компилируется в 0,40h,7Ch,40h, и т.д.) или: DEFG ........ .X...... .XXXXX.. .X...... ......X. .XXXXX.. ..XXXXX. .X....X. .X....X. .X....X. ..XXXX.X .XXXXX.. ........ ........ (в этом случае порядок другой: 0,7Ch,2,3Eh,42h,3Dh,0,40h, т.е. байты размещаются по колонкам). |
|
DEFD xxxxxx [[,] xxxxxx ]... Директива для определения упакованных шестнадцатеричных. Например, DEFD 1234567890 эквивалентно DEFB 12h,34h,56h,78h,90h. Если элемент стартует не с шестнадцатеричной цифры, то он считается обычным выражением (и занимает 1 байт, как в директиве DEFB). |
|
ENCODE #from1,#to1[,#from,#to]... Директива для перекодирования символов в строках (например, может использоваться для перекодирования русских букв в заданные значения или похожие по начертанию латинские буквы). Перекодировка указанных символов действует до следующей пере- кодировки этих символов или до конца исходного текста. #fromN и #toN могут быть выражениями, результат которых считается кодом символа в ANSI. |
|
CTEXT text text ... text ENDTEXT Компактное кодирование сплошного текста. В завершающем символе каждой строки устанавливается бит 7 (если строка пустая, то по крайней мере один пробел формируется, с установленным битом 7, т.е. байт с кодом A0h). Финальный маркер ENDTEXT должен начинаться в самой крайней левой позиции строки. |
|
label[:] STRUCT [label1[:]] {DEFB|DEFW|struct_name} [(#repeat_count)[?]] ... [labelN[:]] {ESTRUCT|ENDSTRUCT} Создает константы label.label1, label.label2, ... которые могут позднее использоваться как обычные константы. label1 получает значение 0, и последующие поля - в зависимости от размера всех предыдущих полей структуры. Имена полей так же можно использовать как отдельные имена label1, label2 и т.д. Примечание: для компиляции структуры используется тот же алгоритм, что и обычно для
компиляции операторов, но только при этом ничего не кладется в память, так что для
задания размеров полей могут использоваться в том числе любые команды Z80 или директивы
ассемблера.
|
|
FILE filepath[,start[,length]] Путь задает имя бинарного файла для непосредственной загрузки байтов в память (могут использоваться двойные кавычки: "filepath"). Размер файла или указанный размер порции файла не должен превышать 65536. Вместо пути к файлу так же можно задать директорию, которая в этом случае добавляется в список директорий для поиска включаемых файлов. |
|
INCLUDE filepath Путь задает имя файла с текстом, который "вставляется" в этом месте кода вместо
директивы INCLUDE. Путь может быть заключен в двойные кавычки. Если путь представляет
собой директорию, то эта директория добавляется в список директорий для поиска включаемых
файлов. |
|
label[:] MACRO paramlist ... [label2[:]] {ENDM|ENDMACRO|EMAC} Определяет макро-команду с именем label. Список параметров paramlist может содержать: - во-первых, список фиксированных параметров (т.е. они должны при вызове макрокоманды указываться в том же порядке); - во-вторых, опциональные (необязательные) именованные параметры в форме name=[default]. Для именованных параметров, они могут при вызове макрокоманды передаваться в любом порядке (но в этом случае обязательно в формате name=value). Именованные параметры, для которых в заголовке макрокоманды задано значение по умолчанию, при вызове макрокоманды могут быть опущены. Значением параметра может быть числовая константа, идентификатор, выражение, строка (но если в строке используются двойные кавычки, то значение подставляется без кавычек). Макроопределение может быть задано в любом месте исходного текста (т.е. обращения вперед на макрос, расположенный ниже, допускаются). В теле макрокоманды все сложносоставные конструкции (такие как IF ... EIF)
должны быть завершены, пересечения уровней вложенности не допускаются.
Если макрокоманда используется более чем однократно, все объявленные в
ней метки должны быть локальными.
Об использовании параметров макрокоманд, переопределений DEFINE и переменной препроцессорного цикла FOR смотрите ниже. |
|
ERROR [#severity],'text'[,#passes] или ERROR [#severity],"text"[,#passes] Генерирует ошибку, заданную пользователем. Может использоваться, например, в макроопределениях для проверки допустимости параметров. severity: 0 - warning (предупрежедение: текст попадает в список сообщений, но компиляция продолжается); 1 - обычный error (код более не генерируется, но компиляция может еще некоторое время продолжаться); 2 или выше - fatal error, компиляция останавливается немедленно. Если #passes не задано, сообщение показывается на всех проходах компиляции. Если оно задано, то значение выражения используется как двоичная маска: бит 0 устанавливается, чтобы сообщение показывалось на проходе 1, бит 1 - на проходе 2, и бит 2 - на проходе 3. Т.е. например, если passes = 2, то сообщение будет отображаться только на первом проходе компиляции, а если pass = 6 - то на втором и третьем (при его наличии) проходах. |
|
DISPLAY 'text'[,#expr[,#passes]] или DISPLAY "text"[,#expr[,#passes]] или DISPLAY #expr[,#passes] или DISPLAY "text",#expr[,"text"[,#expr]]...[,#passes] Показывает указанные текстовые сообщения и значения выражений #expr (если заданы) в виде строки. Аналогично директиве ERROR, #passes (если присутствует) задает маску флажков, определяющих, на каких проходах компилятора показывать данное сообщение. |
|
label[:] PROC ... [label2[:]] {ENDP|ENDPROC|EPROC} Задает процедуру. Фактически, всего лишь ограничивает видимость локальных меток, заданных между PROC и ENDPROC. Процедуры могут быть вложены друг в друга, и уровень допустимой вложенности ничем не ограничен. |
|
DUP #expr ... {EDUP|ENDDUP} Дублирует все операторы, расположенные между DUP n и EDUP, число раз, заданное выражением #expr (но если #expr <= 0, то все операторы не компилируются вообще, как если бы их и не было в коде). |
|
FOR var=values ... {EFOR|ENDFOR} Дублирует все операторы между заголовком до финального ENDFOR столько раз, сколько всевозможных значений перечислено в списке для переменной цикла var, на каждом шаге присваивая этой переменной очередное значение из списка, как если бы имя var определялось директивой DEFINE. Список значений может содержать любые выражения, строки, заключенные в одинарные и двойные кавычки (во втором случае значение подставляется без кавычек), а так же среди прочего перечень значений в виде #from TO #to [STEP #step]. В последнем случае сначала переменной присваивается значение выражения #from, и оно на каждом проходе увеличивает свое значение на #step (по умолчанию - на 1), пока не превзойдет (при отрицательном #step - пока станет меньше) значения выражения #to. Об использовании параметров макрокоманд, переопределений DEFINE и переменной препроцессорного цикла FOR смотрите ниже. |
|
IF #expr [THEN] ... [ELSEIF #expr1 [THEN] ...]... [ELSE ...] {EIF|ENDIF} Оператор условной компиляции определяет, какая именно ветка конструкции будет компилироваться в зависимости от значения выражений #expr, #expr1, ... (Но компилироваться всегда будет только одна ветка, а если ни одна из веток IF или ELSEIF не компилировалась, только тогда компилируется - при ее наличии - завершающая ветка ELSE). |
|
IFcond [THEN] ... [ELSE ...] {EIF|ENDIF} Псевдо-оператор для генерации условных переходов в программе. cond - это один из допустимых флажков (Z,NZ,C,NC,PO,PE,P,M). Он генерирует переход в обход ветки THEN (на ELSE или на конец оператора) по условию, противоположному заданному флажком, а при наличии ветки ELSE - по завершении ветки THEN так же генерируется безусловный переход на конец оператора. При возможности, используются короткие переходы (JR). |
|
LONGIFcond [THEN] ... [LONGELSE ...] {EIF|ENDIF} То же самое, что IF, но всегда используются длинные переходы, причем префикс LONG отдельно перед IF задает переход в обход ветки THEN, а префикс LONG перед ELSE задает, что будет создан длинный переход в обход ветки ELSE. Допускается комбинировать LONGIF с ELSE, и IF с LONGELSE. |
|
[label[:]] WHILE ... {EWHILE|EWHILEB|EWHILENZ|EWHILEZ|EWHILENC|EWHILEC|EWHILEPO|EWHILEPE|EWHILEP|EWHILEM} (в предыдущих версиях именовалась LOOP...ELOOP|ELOOPB, при желании их можно использовать: LOOP DEFINE WHILE ELOOP DEFINE EWHILE ELOOPB DEFINE EWHILEB - для обеспечения работоспособности прежнего кода) Создает бесконечный цикл (случай EWHILE), обычный цикл DJNZ (случай ELOOPB) или условный цикл (случай EWHILEcond). Если DJNZ не может быть использован (ELOOPB) по причине слишком большой дистанции, генерируется почти эквивалентная последовательность инструкций: DEC B:JP NZ,loop (4 байта). Везде внутри цикла разрешаются операторы BREAK [label] и CONTINUE [label] для немедленного завершения или для продолжения цикла (для продолжения по CONTINUE в цикле EWHILEB с того места, где уменьшается регистр B, и проверяется на равенство нулю, используйте WHILEB ... EWHILEB). Использование метки в операторах BREAK и CONTINUE позволяет выйти за пределы вложенного цикла, показывая, к какому именно уровню относятся эти операторы. |
|
[label[:]] LONGWHILE ... {EWHILE|EWHILEB|EWHILENZ|EWHILEZ|EWHILENC|EWHILEC|EWHILEPO|EWHILEPE|EWHILEP|EWHILEM} То же, что и WHILE, но для перехода всегда генерируются длинные инструкции JP. |
|
[label[:]] WHILEB ... {EWHILEB} Создает обычный цикл с командой DJNZ. Эквивалентно WHILE ... EWHILEB, но оператор CONTINUE, относящийся к данному циклу, выполняет переход на саму команду DJNZ, а на на WHILE. |
|
BREAK [label] См. директиву WHILE выше. |
|
CONTINUE [label] См. директиву WHILE выше. |
|
GOTO [cond,][label] Эквивалент инструкции JR/JP, но по возможности компилятор использует короткие переходы (JR). Важное ограничение: любое выражение не может использоваться в качестве операнда, а только метка, локальная или глобальная. |
|
PUSH rp1[,rp2]... или PUSH rp1 [rp2]... Генерирует последовательность команд PUSH rp1:PUSH rp2:... для всех перечисленных регистровых пар (допускаются AF,BC,DE,HL,IX,IY как обычно). |
|
POP rp1[[,]rp2]... или POP rp1 [rp2]... Генерирует последовательсть ...:POP rp2:POP rp1 (по умолчанию, регистровые пары выталкиваются из стека в инвертированном порядке. Используйте директивы POPNOINVERT и POPINVERT для управления этим порядком. |
|
POPNOINVERT Не инвертировать порядок регистровых пар для директивы POP. См. директиву POP выше. |
|
POPINVERT Не инвертировать порядок регистровых пар для директивы POP (по умолчанию). См. директиву POP выше. |
|
END Завершает компиляцию. Любой текст ниже этой директивы игнорируется. |
Использование макро-параметров (в теле макро), имен, переопределенных директивой DEFINE и переменной препроцессорного цикла FOR.
Где угодно в своей
области видимости такой идентификатор в тексте замещается своим значением как
строкой. Видимость области зависит от вида переменной (для переменной цикла
FOR - до конца цикла, для параметра макроса - до конца
тела макроса, для имени, определенного директивой DEFINE -
до конца исходного текста (или до следующего переопределения этого имени
другой директивой DEFINE).
Для того, чтобы возможно было использовать определения
непосредственно состыковывая их с другими идентификаторами и числами,
используется запись вида .name или name.
или .name. - При замене значения в этом случае
точка удаляется (для того, чтобы точка осталась в тексте, необходимо писать две
точки подряд).
Имеется возможность работать только с подстрокой значения,
записывая обращение к подстроке параметра в виде: "name"[ nnn ]
или "name"[ nnn,
LLL ] , где nnn и LLL - целые неотрицательные десятичные числа. nnn=1
означает обращение к первому символу, LLL может использоваться для задания длины
подстроки (если >
1). Важно: символ '[' должен быть записан немедленно после закрывающей
кавычки '"', без пробелов между ними.
Значения таких переменных могут использоваться в операциях
сравнения в виде:
"name1"
op "name2" или "name1" op string (где
op может быть одной из операций сравнения
=, <>, <, <=, >, >=). Например, IF "RP" = DE
THEN ...
Список операций Z80 в порядке алфавита.
ADC HL, { BC | DE | HL | SP } ADC IX, { BC | DE | IX | SP } ADC IY, { BC | DE | IY | SP } ADC [ A, ] { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) | #expression } ADD HL, { BC | DE | HL | SP } ADD IX, { BC | DE | IX | SP } ADD IY, { BC | DE | IY | SP } ADD [ A, ] { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) | #expression } AND [ A, ] { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) | #expression } BIT #number, { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) } BIT #number, { (IX±#offset ) | (IY±#offset) }, { B | C | D | E | H | L | A } CALL [ { NZ | Z | NC | C | PO | PE | P | M }, ] #address CCF CP [ A, ] { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) | #expression } CPD CPDR CPI CPIR CPL DAA DI DEC { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | IYH | IYL | ( {IX|IY}±#offset ) } DI DJNZ #address EI EX (SP), { HL | IX | IY } EX AF, AF' EX DE, { HL | IX | IY } EXX HALT IM0 | IM1 | IM2 | IM #n IN [ A, ] { (#port) | (C) } IN { B | C | D | E | H | L | F | A }, (C) INC { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | IYH | IYL | ( {IX|IY}±#offset ) } IND INDR INI INIR JP [ { NZ | Z | NC | C | PO | PE | P | M }, ] #address JR [ { NZ | Z | NC | C }, ] #address LD A, { I | R | B | C | D | E | H | L | M | (HL ) | A | #expression | IXH | IXL | IYH | IYL | (IX±#offset) | (IY±#offset) | (BC) | (DE) | (#address) } LD { B | C | D | E | H | L | M | (HL) }, { B | C | D | E | H | L | M | A | #expression } LD { B | C | D | E | IXH | IXL | A | (IX±#offset) }, { B | C | D | E | IXH | IXL | A | #expression } LD { B | C | D | E | IYH | IYL | A | (IY±#offset) }, { B | C | D | E | IYH | IYL | A | #expression } LD { B | C | D | E | IXH | IXL | A }, { B | C | D | E | IXH | IXL | A | (IX±#offset) } LD { B | C | D | E | IYH | IYL | A }, { B | C | D | E | IYH | IYL | A | (IY±#offset) } LD { I | R }, A LD { BC | DE | HL | SP | IX | IY }, { #expression | ( #address ) } LD ( #address ), { BC | DE | HL | SP | IX | IY } LDD LDDR LDI LDIR NEG NOP OR [ A, ] { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) | #expression } OUT { (#port) | (C) } [, A ] OUT (C), { B | C | D | E | H | L | F | A } OUTD OTDR OUTI OTIR POP { BC | DE | HL | AF | IX | IY } PUSH { BC | DE | HL | AF | IX | IY } RES #number, { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) } RES #number, { (IX±#offset ) | (IY±#offset) }, { B | C | D | E | H | L | A } RET [ { NZ | Z | NC | C | PO | PE | P | M | N | I } ] RETI RETN RL { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) } RLA RLC { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) } RLCA RLD RR { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) } RRA RRC { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) } RRCA RRD RST #expression ; (allowed following values: 0..7 and also 8, 16, 24, 32, 40, 48, 56 - decimal) SBC HL, { BC | DE | HL | SP } SBC [ A, ] { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) | #expression } SCF SET #number, { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) } SET #number, { (IX±#offset ) | (IY±#offset) }, { B | C | D | E | H | L | A } SLA { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) } SLA { (IX±#offset ) | (IY±#offset) }, { B | C | D | E | H | L | A } SLI { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) } SLI { (IX±#offset ) | (IY±#offset) }, { B | C | D | E | H | L | A } SLL { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) } SLL { (IX±#offset ) | (IY±#offset) }, { B | C | D | E | H | L | A } SRA { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) } SRL { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) } SRL { (IX±#offset ) | (IY±#offset) }, { B | C | D | E | H | L | A } SUB [ A, ] { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) | #expression } XOR [ A, ] { B | C | D | E | H | L | M | (HL) | A | IXH | IXL | (IX±#offset ) | (IY±#offset) | #expression }
В версии 2.3 Build 1.5:
Введены дополнительные правила, позволяющие компилировать код, полученный в результате работы дизассемблера:
- любой текст после запятой в скобках при адресайии ячейки в памяти считается
комментарием. Например:
LD HL, (0FFFEh,здесь любой текст).
- Любой текст после запятой после последнего
операнда в инструкции считается комментарием. E.g. LD HL,0FFFEh,any text here.
В версии 2.7:
Добавлена возможность записывать команды в стиле C - - для Z80.
В операторах C--, все имена регистров считаются зарезервированными
переменными:
A, A', F, F', B, B', C, C', D, D', E, E', H, H', L, L', I, R, IXL, IXH, IYL, IYH
AF, AF', BC, BC', DE, DE', HL, HL', IX, IY, SP
Так же, название флажка CF является зарезервированным:
CF
Операторы C-- могут отделаяться друг от друга и от обычных ассемблерных инструкций и директив пробелами и символом ':' как обычно.
При использовании любой регистровой пары или регистра в качестве левого или правого операнда, к нему можно применить или суффиксный, или постфиксный автоинкремент или автодекремент, операнд в памяти, адресуемой регистрами HL, IX или IY так же может быть инкрементирован или декрементирован. Все операнды, кроме SP, при автоинкременте увеличиваются или уменьшаются на 1 (только SP - на 2).
Оператор присваивания в стиле C-- имеет вид: dest=expression. Здесь dest может задавать операнд в регистре или в памяти, а выражение может быть либо обычным выражением (тогда имеется в виду загрузка непосредственной константы) или опять же регистром или операндом в памяти (операнд в памяти не может присутствовать одновременно в левой и правой частях присваивания). Допускается присваивание двухбайтных значений, в тех случаях, когда это требуется, компилятор генерирует две команды LD. Примеры:
A=B | LD A,B |
A=(HL) | LD A,(HL) |
A=(IX+10) | LD A,(IX+10) |
(16384)=SP | LD (16384), SP |
(IX)=HL | LD (IX+0),L:LD (IX+1),H |
BC=HL | LD B,H:LD C,L |
BC'=BC | PUSH BC:EXX:POP BC:EXX |
(IX)=DE | LD (IX),E:LD (IX+1),D |
(HL)=BC | LD (HL),C:INC HL:LD (HL),B:DEC HL |
(HL++)=BC | LD (HL),C:INC HL:LD (HL),B |
SP=DE | EX DE,HL:LD SP,HL:EX DE,HL |
SP=HL' | EXX:LD SP,HL:EXX |
SP=IY | LD SP,IY |
Допускается однооперандный оператор C-- для применения операции ++ или -- , например:.:
A++ | INC A |
HL-- | DEC HL |
(++HL)-- | INC HL:DEC (HL) |
(IX+5)++ | INC (IX+5) |
Другие возможные виды операторов:
= (присваивание)
& ("и") | ("или") ^ ("исключающее или")
+ (сложение) +CF+
(ADC)
- (вычитание) -CF-
(SBC)
r<<CF (сдвиг влево по кругу, RLC)
r<< (RL)
r>>CF (сдвиг вправо по кругу, RRC)
r>> RR
>< (обмен)
? (сравнение)
Примеры использования:
A=A+5 | ADD A,5 |
A+B | ADD A,B |
A-C-CF | SBC A,C |
&E | AND A,E |
|(HL) | OR A,(HL) |
A?(IX+100) | CP A,(IX+100) |
Как можно заметить, A=A op opd2 может быть сокращено до A op opd2 или даже op opd2 (в последнем случае будьте аккуратнее, завершая предыдущую инструкцию: компилятор может решить, что данная команда является продолжением выражения).
A'^E | EX AF,AF':XOR A,E:EX AF,AF' |
Для случая 16-разрядных арифметических операций все то же самое:
HL=HL+DE | ADD HL,DE |
HL+BC | ADD HL, BC |
IX+DE'+CF | EXX:ADC IX, DE:EXX |
IY+HL | EX DE,HL:ADD IY,DE:EX DE,HL |
Несколько примеров операций сдвига:
A<< | RLA |
>>CF | RRCA |
B<<- | SLA B |
E'>>- | EXX:SRA E:EXX |
A'<<CF | EX AF,AF':RLCA:EX AF,AF' |
Несколько примеров операций обмена:
HL><DE | EX DE,HL |
(SP)><DE | EX DE,HL:EX (SP),HL:EX DE,HL |
IX><BC | PUSH IX:PUSH BC:POP IX:POP BC |
DE><HL' | PUSH DE:EXX:PUSH HL:EXX:POP DE:EXX:POP HL:EXX |
AF><AF' | EX AF,AF' |