Как написать игру для ZX Spectrum на ассемблере

         

МАКРООПРЕДЕЛЕНИЯ



МАКРООПРЕДЕЛЕНИЯ

Как вы могли заметить, во многих программах повторяются совершенно однотипные фрагменты текста, отличающиеся только значениями отдельных регистров, а то и вовсе совпадающие. Оказывается, в таких случаях не обязательно каждый раз переписывать одну и ту же последовательность команд. Вы можете обозначить данную последовательность специальными директивами, о которых мы скажем ниже, и присвоить ей какое-нибудь имя. Ав дальнейшем, в тех местах текста, где она должна появиться, достаточно записывать только ее имя. Такие фрагменты текста называются макроопределениями или макросами, а имя макроса - макрокомандой.

Для определения макроса в поле меток записывается его имя, а в поле мнемоник - директива ассемблера MAC. Затем пишется тело макроопределения, состоящее из любых команд, и завершается запись директивой ENDM. В качестве примера можно предложить такой макрос:

PRAT MAC LD A,22 RST 16 LD A,B RST 16 LD A,C RST 16 ENDM

Всякий раз, когда в программу потребуется включить записанную между директивами MAC и ENDM последовательность инструкций, достаточно в поле мнемоник записать макрокоманду PRAT. Это позволит сократить исходный текст программы и сделать его несколько более наглядным, приблизив запись к языкам высокого уровня. Действительно, ведь макрокоманды очень похожи на операторы Бейсика. Например, вместо того чтобы писать

CALL 3435 LD A,2 CALL 5633

можно оформить этот фрагмент в виде макроса и присвоить ему имя CLS. Тогда для очистки экрана вы сможете на время забыть адреса соответствующих процедур ПЗУ и записывать в поле мнемоник этот старый знакомый оператор Бейсика.

При задании макросов имеется ряд ограничений, которые необходимо всегда учитывать. Во-первых, имена макроопределений в отличие от имен меток могут состоять не более чем из пяти символов. Причем лишние символы в этом случае не игнорируются, а приводят к появлению ошибки (вообще же лучше обходиться четырьмя символами, тогда вы избавите себя от лишних вопросов). Во-вторых, внутри макроопределения не должно быть строк с метками, так как многократное использование одного и того же макроса приведет к дублированию имен и в результате также появится сообщение об ошибке трансляции. Не допускаются и вложения макросов, то есть внутри макроопределения не могут встречаться ссылки на другие макросы.


Мы уже говорили, что в макросах можно определять не только строго совпадающие фрагменты исходного текста, но и слегка отличающиеся друг от друга. Это становится реальным благодаря возможности использования так называемых формальных параметров. Для каждого макроса допускается задавать до 16 таких параметров. Например, при рисовании точек на экране нужно указывать две координаты. Можно написать макрос, в котором регистры B и C будут загружаться требуемыми значениями и который вызывается командой

PLOT X,Y

где X и Y - любые допустимые в одноименном операторе Бейсика значения координат. Формальные параметры в макроопределении задаются знаком равенства (=) и символом, код которого соответствует порядковому номеру фактического параметра в макрокоманде. Для первого параметра этот символ может иметь коды 0, 16, 32, 48 и так далее, второй параметр будут описывать любые символы с кодами 1, 17, 33, 49... Чтобы не запутаться, рекомендуем использовать цифровые символы от 0 до 9 для определения первых десяти параметров, а остальные 6 можно задавать, например, буквами K, L, M, N, O и P. Тогда макрос PLOT будет записан следующим образом:

PLOT MAC LD C,=0 LD B,=1 CALL 8933 ENDM

После трансляции вышеприведенной макрокоманды PLOT с параметрами 100 для координаты X и 80 для Y получится следующая последовательность команд микропроцессора:

LD C,100 LD B,80 CALL 8933

то есть формальные параметры =0 и =1 заменятся фактическими 100 и 80 соответственно.

При написании макрокоманд нужно помнить, что если имя макроса состоит из пяти символов (напомним еще раз, что это максимальная длина имен макросов), то фактические параметры обязательно нужно заключать в круглые скобки, например:

PRINT (TEXT)

Прежде чем привести пример использования макросов в реальной программе, добавим, что в качестве параметров могут выступать только непосредственные числовые значения. Использование символьных строк (за исключением имен меток и констант) не разрешается.



Во время трансляции текст макроопределения не переводится сразу в машинные коды, а помещается в специальный буфер, из которого затем извлекается по мере необходимости. Поэтому перед вводом команды A необходимо указать размер этого буфера с помощью команды C. Помните, при вводе этой команды сначала запрашивается размер входного буфера Include buffer?, а затем появляется еще один запрос - Macro buffer? На него нужно ввести количество байт, достаточное для размещения текста всех макроопределений, заданных в программе. Если задать слишком маленькое число, то во время первого прохода ассемблирования появится сообщение No Macro Space. В этом случае нужно повторить ввод с большим числом. В приведенном ниже примере для размещения макросов достаточно 300 байт.

ORG 60000 ENT $ ; Печать ASCIIZ-строки в позиции экрана, задаваемой первыми двумя параметрами PRN MAC LD B,=0 LD C,=1 LD HL,=2 CALL PRNZ ENDM ; Позиционирование печати PRAT MAC LD A,22 RST 16 LD A,B RST 16 LD A,C RST 16 ENDM ; Установка цветов INK и PAPER, а также цвета бордюра COLOR MAC LD A,=1*8+=0 LD (23693),A LD A,=1 CALL 8859 ENDM ; Очистка экрана и назначение вывода на основной экран CLS MAC CALL 3435 LD A,2 CALL 5633 ENDM ; Установка PLOT-позиции без рисования точки PSET MAC LD L,=0 LD H,=1 LD (23677),HL ENDM ; Черчение линии из текущей PLOT-позиции DRAW MAC EXX PUSH HL LD DE,=0 LD C,=1 LD B,=2 CALL 9402 POP HL EXX ENDM ; Направления рисования линий UP_RT EQU #0101 ;вверх и вправо DN_RT EQU #FF01 ;вниз и вправо DN_LF EQU #FFFF ;вниз и влево UP_LF EQU #01FF ;вверх и влево ; ------ BEGIN COLOR (5,0) CLS PRN 5,8,TEXT1 PRN 7,7,TEXT2 PSET 48,144 DRAW UP_RT,131,0 DRAW DN_RT,0,39 DRAW UP_LF,131,0 DRAW UP_RT,0,39 PSET 50,142 DRAW UP_RT,127,0 DRAW DN_RT,0,35 DRAW UP_LF,127,0 DRAW UP_RT,0,35 RET ; Подпрограмма печати ASCIIZ-строки, вызываемая макросом PRN PRNZ PUSH HL PRAT PRNZ1 LD A,(HL) INC HL AND A JR Z,PRNZ2 RST 16 JR PRNZ1 PRNZ2 POP HL RET ; ------ TEXT1 DEFB 16,2,19,1 DEFM "*** DEMO ***" DEFB 0 TEXT2 DEFB 16,6,19,1 DEFM "### MACROS ###" DEFB 16,5,0

Имея возможность работать с дисководом, очень удобно собрать все макросы в одном или нескольких файлах (например, по родству выполняемых функций) и затем при необходимости включать их в исходный текст с помощью команды ассемблера *F. Так как макросы сразу не транслируются, то это никак не повлияет на размер исполняемого кода, даже если среди включаемых макросов есть такие, которые ни разу не используются в программе. Они, конечно, займут некоторый объем памяти, но так и останутся в буфере невостребованными.

В заключение хочется предостеречь вас от чрезмерного увлечения макроопределениями. Во всем нужно знать меру. Учтите, что макросы могут запросто свести все преимущества ассемблера на нет, снизив эффективность программы, в лучшем случае, до уровня компиляторов.


Содержание раздела