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

Материал из Emuverse
Версия от 16:34, 11 февраля 2008; Panther (обсуждение | вклад) (Новая: {{ДИ|Автор=А. Евдокимов, А. Капульцевич, И. Капульцевич, ИД «Питер»}} {{TOCright}} ; ГЛАВА ОДИННАДЦАТАЯ, ; расск...)
(разн.) ← Предыдущая версия | Текущая версия (разн.) | Следующая версия → (разн.)
Данный материал защищён авторскими правами!

Использование материала заявлено как добросовестное, исключительно для образовательных некоммерческих целей.

Автор: А. Евдокимов, А. Капульцевич, И. Капульцевич, ИД «Питер»

ГЛАВА ОДИННАДЦАТАЯ,
рассказывающая о некоторых дополнительных возможностях ассемблера GENS4

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

ФУНКЦИЯ ПОИСКА/ЗАМЕНЫ

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

F[номер начальной строки],[номер конечной строки],
[текст для поиска][,текст для замены]

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

Предположим, вам нужно в тексте программы найти все места, где встречается метка LABEL. Введите в редакторе команду

F1,20000,LABEL

Как только функция найдет последовательность символов, совпадающую с заданной (LABEL), на экране появится строка текста, содержащая эту последовательность, и GENS перейдет в режим редактирования. При этом курсор для удобства автоматически устанавливается в самое начало найденного текста. После этого у вас есть два варианта дальнейших действий: либо закончить поиск, нажав клавишу Enter, либо продолжить его, для чего нет надобности набирать команду заново, а достаточно нажать клавишу F. Естественно, вам ничто не мешает сразу же внести в текст какие-то изменения, но если они должны быть везде однотипными, проще задать в команде F также и последний параметр. Например, чтобы заменить все имена LABEL на METKA введите в редакторе строку

F1,20000,LABEL,METKA

Внешне поведение функции при этом не изменится: при нахождении первого имени строка точно так же будет вызываться на редактирование, а курсор указывать на первый символ слова LABEL. Вы так же можете продолжать вносить изменения вручную и по желанию продолжать поиск без изменений текста или прервать выполнение команды. Но теперь у вас появилась и иная возможность. Если нажать клавишу S, то слово LABEL мгновенно заменится словом METKA, а на экране появится следующая найденная строка.

УПРАВЛЕНИЕ ТРАНСЛЯЦИЕЙ

Основная задача ассемблера — трансляция исходных текстов, поэтому о том, как получается исполняемый файл, не помешает знать несколько больше того, что вам уже известно. При обработке небольших программ обычно бывает вполне достаточно просто ввести команду A, но иногда может потребоваться, например, узнать адреса некоторых меток, вывести листинг ассемблирования на экран или распечатать его на принтере. А как быть, если какая-то часть программы должна размещаться в экранной области памяти или на месте системных переменных? Или размер программы получается настолько большим, что перекрывает не только исходный текст, но и коды самого ассемблера? Оказывается, GENS позволяет справиться и с подобными трудностями! Нужно только дать ему соответствующее задание. Для этого необходимо указать в команде A дополнительные параметры, которые мы раньше пропускали.

Ключи ассемблирования

Первое число, следующее за командой A, называют ключами ассемблирования, потому что в зависимости от него трансляция протекает с теми или иными условиями. Ключи похожи на флаги, так как каждому из них соответствует определенный бит задаваемого значения. При объединении нескольких битов можно получить комбинацию различных условий. Перечислим значения всех используемых ключей при установке соответствующего бита в 1:

  • бит 0 (1) — заставляет ассемблер вывести таблицу адресов меток и значений констант в конце второго прохода трансляции;
  • бит 1 (2) — произвести проверку синтаксиса строк программы, не создавая при этом машинного кода;
  • бит 2 (4) — вывести на втором проходе листинг ассемблирования программы;
  • бит 3 (8) — на время трансляции назначить вывод вместо экрана на принтер;
  • бит 4 (16) — реально размещать машинный код сразу за таблицей меток (адрес, указанный в директиве ORG влияет только на создание ссылок в программе);
  • бит 5 (32) — не делать проверки расположения кода в памяти (обычно же максимальная верхняя граница задается системной переменной UDG и может быть изменена командой редактора U).

Поясним смысл некоторых наиболее важных ключей и покажем, как ими пользоваться на практике. Первый ключ бывает совершенно необходим, по крайней мере, в двух случаях: при отладке достаточно больших программ, чтобы знать, где искать ту или иную процедуру и для определения адресов подпрограмм и переменных при использовании машинного кода совместно с программами на других языках или для связи различных модулей, транслируемых раздельно (что поделаешь, память Speccy весьма ограничена и иногда поневоле приходится изощряться). В таких программах, как БИТВА С НЛО или ГЕНЕРАТОР СПРАЙТОВ мы предложили простой способ выбора необходимой процедуры из пакета, но он не всегда может оказаться пригодным. Более того, в ГЕНЕРАТОРЕ СПРАЙТОВ переменные располагались в буфере принтера, а это в некоторых случаях не допускается. Например, в компьютере ZX Spectrum 128, как мы уже говорили, в этой области содержится важная информация, разрушать которую никак нельзя. Вот здесь-то и поможет ключ 1. Разместите все переменные внутри программы и оттранслируйте ее командой A1. Когда ассемблирование закончится, на экран (или на принтер, если задать режим A9 — 1+8) будет выдана таблица меток, в которой найдите нужное имя, а рядом с ним — адрес. Для обращения к этому адресу из Бейсика вам останется только перевести шестнадцатеричное значение в десятичное.

Ключи 2 и 4 бывают особенно полезны на этапе отладки, а вот без ключа 16 не обойтись при трансляции больших программ или если машинный код должен располагаться в недоступных иными средствами областях памяти. Как уже говорилось, при использовании этого ключа исполняемый код располагается сразу за таблицей символов, создаваемой ассемблером на первом проходе трансляции (которая, в свою очередь, помещается следом за исходным текстом). Это позволяет отвести для исполняемого модуля всю свободную память. Однако после сохранения полученной программы на ленте или диске адрес загрузки ее кодов в заголовке файла окажется равным тому, который вы указали в директиве ORG. Все адреса переходов, естественно, также будут правильными.

Применять последний ключ 32 можно лишь в тех случаях, когда вы абсолютно уверены, что исполняемый код полностью уместится в оперативной памяти, иначе «хвост» программы залезет в начальные адреса ПЗУ, где толку от него не будет никакого. Этот ключ может принести пользу, пожалуй, лишь при создании операционных систем или перемещаемых модулей, но эта тема уже выходит за рамки нашей книги.

Установка размера таблицы меток

Среди сообщений GENS на экране изредка может появиться:

No Symbol table space!

говорящее о том, что ассемблеру не хватило памяти для размещения таблицы меток. Это может показаться странным, особенно если ваша программа имеет небольшие размеры и свободной памяти на самом деле остается еще килобайт 15-20. А все дело в том, что GENS перед началом трансляции исходного текста выделяет под таблицу не все пространство, а определенных размеров буфер, величина которого рассчитывается исходя из размера текста программы, находящейся в памяти. Чем больше строк вы ввели, тем больше окажется и таблица. В большинстве случаев рассчитанного объема оказывается вполне достаточно, но если при небольших размерах текста он будет насыщен метками и прочими именами, то тогда, скорее всего, и появится указанное выше сообщение.

Бороться с подобной ситуацией поможет второй параметр, задаваемый в команде A. Он соответствует размеру создаваемой таблицы в байтах. Например, если вы считаете, что таблицы в две с половиной тысячи байт будет достаточно, введите для начала ассемблирования команду

A,2500

При необходимости, конечно, можете указать также и ключи трансляции.

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

ТРАНСЛЯЦИЯ БОЛЬШИХ ПРОГРАММ

Особенно много сложностей возникает при трансляции больших программ, и в первую очередь это касается тех, кто еще не успел обзавестись дисковой операционной системой TR-DOS и адаптированной к ней версией ассемблера gens4b или GENS4D. Однако это не означает, что если текст программы, таблица меток, исполняемый файл и сам ассемблер не умещаются в памяти, то мечту о создании серьезных игровых программ можно похоронить. Ассемблер GENS4 предоставляет возможность получения достаточно объемистых исполняемых модулей, сопоставимых с размерами управляющей части фирменных программ. Безусловно, это потребует от вас некоторого терпения, но если есть желание, все остальное приложится.

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

Идея заключается в том, чтобы не загружать в память исходный текст программы, а транслировать его непосредственно с внешнего носителя. Это позволит высвободить максимальное пространство, так как именно текстовый файл имеет наибольшие размеры по сравнению с таблицей меток и исполняемым файлом. Если ваш компьютер не снабжен дисководом и вы вынуждены работать с лентой, то прежде всего потребуется создать текстовый файл в специальном формате, пригодном для трансляции таким способом. При этом GENS разбивает исходный текст на небольшие фрагменты, а затем считывает и транслирует файл по частям. Считывание происходит в специально предназначенный для этих целей буфер, размер которого необходимо указать в самом начале работы. Введите в редакторе команду C (Change buffers — изменить буферы). На экране появится запрос:

Include buffer?

на который нужно ввести размер входного буфера для включаемых файлов в байтах. Введите в ответ на него, например, значение 1000. Затем вы увидите еще один запрос — Macro buffer? — который нас пока не интересует, поэтому его можно пропустить, просто нажав Enter. После этого загрузите текст программы в память и сохраните его снова, но уже не обычным образом, а с помощью команды T. Ее формат похож на формат команды P для сохранения текста:

T[нач.строка],[конечн.строка],[имя_файла]

Например, чтобы перевести весь исходный текст во включаемый формат и записать его в файле с именем INCL, нужно ввести команду

T1,20000,INCL

Теперь очистите память командой Z и напишите одну-единственную строку:

10 *F INCL

Обратите внимание, что команда ассемблера *F (не путайте с командами редактора) должна находиться в поле меток, а имя файла записывается следом за ней после единичного пробела. Для начала трансляции введите команду редактора A, например:

A16,5400

Конечно, размер таблицы меток в вашем случае может потребоваться совершенно иной, но важно то, что он обязательно должен быть указан, так как при единственной строке в памяти GENS для этих нужд зарезервирует примерно 100 байт, которых хватит от силы на пару десятков меток. Размер таблицы желательно задать максимально близким к тому, который будет использован, и чтобы узнать его, может понадобиться сначала оттранслировать программу «вхолостую», использовав ключ 2. Для окончательной трансляции желательно применить ключ 16, который отведет максимум памяти для исполняемого модуля.

Таким образом можно обрабатывать исходные тексты программ, превосходящие размеры всей свободной памяти компьютера. Ведь текст может располагаться не в одном, а в нескольких файлах еще до обработки их командой T. Например, программа MOON занимает 3 файла. Создайте для каждого из них три включаемых файла с именами MOON1, MOON2 и MOON3, а затем введите три строки

10 *F MOON1
20 *F MOON2
30 *F MOON3

которые будут транслироваться уже описанным способом.

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

Обладателям дисковой системы TR-DOS повезло несравненно больше. При работе с диском не нужно переводить файлы в специальный формат, достаточно воспользоваться командой *F с указанием имени файла, которое должно начинаться, как и в прочих командах работы с внешней памятью, с указания номера дисковода, например, строка

10 *F 1:BATTY

оттранслирует непосредственно с диска, находящегося на дисководе A, текстовый файл с именем BATTY. Правда, и в этом случае потребуется задать размер таблицы меток достаточных размеров, но и не слишком большой, чтобы осталось больше места для исполняемого файла. С этой же целью также желательно указать ключ 16.

Еще один существенный плюс использования для трансляции дисковода (помимо скорости считывания) заключается в том, что если исполняемый модуль перекрывает всю свободную память, уже полученный машинный код «сбрасывается» на диск порциями по мере заполнения памяти. Однако в этом случае необходимо указать и третий параметр в команде редактора A — имя выходного файла, то есть того файла, в который будет записана оттранслированная программа. К примеру, это может выглядеть так:

A16,8000,2:BATT.EXE

После ассемблирования текста исполняемый модуль будет сохранен на дискете в дисководе B под именем BATT.EXE.

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

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

Для определения макроса в поле меток записывается его имя, а в поле мнемоник — директива ассемблера 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. Так как макросы сразу не транслируются, то это никак не повлияет на размер исполняемого кода, даже если среди включаемых макросов есть такие, которые ни разу не используются в программе. Они, конечно, займут некоторый объем памяти, но так и останутся в буфере невостребованными.

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

ДИРЕКТИВЫ УСЛОВНОЙ ТРАНСЛЯЦИИ

Работая с GENS4, у вас есть возможность получать различные варианты исполняемого кода в зависимости от выполнения тех или иных условий. Достигается это включением в программу команд условной трансляции IF, ELSE и END, которые записываются в поле мнемоник (эти слова не относятся к зарезервированным и поэтому их можно использовать в качестве меток, но не макрокоманд). Общий вид текста программы при этом будет таким:

       .........
       IF    выражение
       команды_1
       [ELSE
       команды_2]
       END
       .........

Команда ELSE и следующий за ней блок инструкций «команды_2» являются необязательной частью условной конструкции, поэтому в данном примере они заключены в квадратные скобки. Если значение выражения после команды IF истинно (то есть не равно нулю), то транслируется блок команд «команды_1» до ELSE или, если его нет, до END. В противном случае (если значение выражения равно нулю) ассемблируются «команды_2» после ELSE, конечно, если эта команда указана. После END трансляция текста протекает как обычно.

Часто эти команды используются для получения различных версий одной и той же программы, одна из которых, например, предназначена для работы на «обычном» Speccy, другая на ZX Spectrum 128 и т. п. Но, на наш взгляд, наиболее полезными они оказываются при написании макроопределений. В этом случае макрос можно составить таким образом, чтобы в зависимости от задаваемых в макрокоманде параметров получался максимально компактный код. Рассмотрим такой пример:

CHAN   MAC
       IF    =0
       LD    A,=0        ;если первый параметр не 0
       ELSE
       XOR   A           ;если параметр равен 0
       END
       CALL  5633
       ENDM

Встретив в тексте макрокоманду CHAN, ассемблер обратится к одноименному макросу и в первую очередь проверит значение первого параметра =0. Если его величина отлична от 0 (условие истинно), то транслируется команда LD A,N, затем ассемблирование продолжается после команды END. В противном же случае, то есть если заданный параметр равен 0 (условие ложно), то обрабатываются команды после ELSE, в данном случае — XOR A и далее текст транслируется, как и в предыдущем варианте. Поэтому после трансляции макрокоманды

       CHAN  2

получится последовательность инструкций

       LD    A,2
       CALL  5633

а если задать

       CHAN  0

то такая макрокоманда оттранслируется иначе:

       XOR   A
       CALL  5633

Приведем другой, более серьезный пример применения команд условной трансляции в макросах:

       ORG   60000
UP     EQU   1
DN     EQU   %10
RT     EQU   %100
LF     EQU   %1000
SCRL   MAC
       PUSH  BC
       LD    HL,=1*256+=0
       LD    (COL),HL
       LD    HL,=3*256+=2
       LD    (LEN),HL
       IF    =4 & UP ;если 5-й параметр = UP
       CALL  SCR_UP
       END
       IF    =4 & DN ;если 5-й параметр = DN
       CALL  SCR_DN
       END
       IF    =4 & RT ;если 5-й параметр = RT
       CALL  SCR_RT
       END
       IF    =4 & LF ;если 5-й параметр = LF
       CALL  SCR_LF
       END
       POP   BC
       ENDM
; ------
       LD    B,16
SCRL1  SCRL  10,4,5,7,UP
       DJNZ  SCRL1
       LD    B,16
SCRL2  SCRL  10,4,5,7,RT
       DJNZ  SCRL2
       LD    B,16
SCRL3  SCRL  10,4,5,7,DN
       DJNZ  SCRL3
       LD    B,16
SCRL4  SCRL  10,4,5,7,LF
       DJNZ  SCRL4
       RET
SCR_UP .........
SCR_DN .........
SCR_RT .........
SCR_LF .........
COL    DEFB  0
ROW    DEFB  0
LEN    DEFB  0
HGT    DEFB  0

В выражениях ассемблера GENS отсутствует знак равенства, но из этого затруднения можно выйти, если употребить поразрядную операцию «И» — AND, обозначаемую символом «амперсанд» (&), а в соответствующем параметре использовать отдельные биты, указывающие на различные действия. В приведенном макросе после определения графических переменных COL, ROW, LEN и HGT в зависимости от последнего параметра вызывается одна из четырех процедур скроллингов (напомним, что сами процедуры были описаны в 6-й главе). Как видите, благодаря командам условной трансляции стало возможно объединить их в одном макросе. В результате и текст программы заметно сократился и стал значительно более удобочитаемым. Правда, при этом несколько возрос размер исполняемого модуля, но этот недостаток также можно устранить, слегка доработав макрос. Например, можно добавить еще один условный блок в самом начале, в котором проверяется значение самого первого параметра и только если он не равен 0, транслируются команды определения переменных, а в противном случае они будут пропускаться.