О макросе бедном замолвите слово...
(c) Vitamin/CAIG:
Наверное, многие кодеры (по крайней ме-
ре, начинающие) сетовали на некоторые не-
достатки ассемблера, которые, впрочем, от-
сутствовали у языков более высокого уров-
ня. Это я речь веду о конструкциях. Но
ведь можно если и не превратить ассемблер
в паскаль или что-нибудь еще, то сделать
программирование более удобным. Для осуще-
ствления подобной затеи нам понадобится
ассемблер, поддерживающий макросы. Приве-
дённые ниже примеры написаны под Alasm
v4.4x. При особом желании их можно переде-
лать под любой другой макроассемблер. Так
вот, начнем с одного, то есть двух простых
макросов, которые могут сделать работу с
переменными более удобной, нежели раньше.
MACRO GET
IFN ?:1+1 ;проверяем на наличие
LD :0,(:1) ;переменной
ELSE
DISPLAY "Variable :1 is not found"
ENDIF
ENDM
И второй макрос:
MACRO PUT
IFN ?:0+1
LD (:0),:1
ELSE
DISPLAY "Variable :0 is not found"
ENDIF
ENDM
А пользоваться ими надо так:
xpos equ 32768 ;объявляем переменную
...
get b,xpos ;считали
...
put xpos,a ;записали
Регистры могут быть и парные.
В качестве переменных не могут использо-
ваться абсолютные адреса.
Еще несколько макросов на данную тему.
Пусть у нас есть какая-то сложная структу-
ра данных и нам надо поудобнее обратиться
к ее элементам. Пусть адрес структуры хра-
нится в IX.
MACRO MOV
IFN ?:1+1
LD :0,(IX+:1)
ELSE
DISPLAY "Field :1 is not defined"
ENDIF
ENDM
Пример использования:
BOX DB 0 ;x
DB 1 ;y
DB 2 ;width
DB 3 ;heigth
BOX_X EQU 0
BOX_Y EQU 1
BOX_W EQU 2
BOX_H EQU 3
LD IX,BOX
...
MOV A,BOX_X
...
И, чтобы закончить тему переменных,
расскажу о доступе к битовым полям.
MACRO MASK
IFN (?:0+1)&(?:1+1)
BIT :0,(IX+:1)
ELSE
DISPLAY "Field :0 or field :1 not defined"
ENDIF
ENDM
Для примера дополним предыдущий пример
строками:
BOX ...
DB 0 ;flag
...
BOX_FLAG EQU 4
BOX_FULL EQU 0
BOX_TURN EQU 1
BOX_CLOS EQU 2
...
MASK BOX_FULL,BOX_FLAG ;проверяем
JZ ... ;если что, переходим
Аналогично, можно добавить макросы для
установки и снятия битовых переменных.
Идем дальше. Каждому знакома ошибка вы-
хода за поле при компиляции команд относи-
тельного перехода. Тогда мы берем и испра-
вляем все jr на jp. А потом, когда все уже
соптимизировано, настроено и есть возмож-
ность некоторые jp заменить опять на jr,
но пойди найди их среди кучи себе подоб-
ных - зае..., пардон, забодаешься :) А
следующий макрос старается делать оптима-
льный код, автоматически ставя jr, когда
это возможно, а в остальных случаях - jp.
Но есть одна нехорошая особенность: он
хорошо работает только с конкретно задан-
ными адресами, либо с уже определенными
метками, имхо особенность аласма с его од-
нопроходностью. Если что, компилятор выда-
ст ошибку и вы поменяете jmp на jp. Риска
немного, зато во многих случаях он дейст-
вительно сэкономит вам пямять.
MACRO JMP
LOCAL ;локальные метки
DIFF=:0-$-2 ;разность хода
IF DIFFὀ ;если переход вперед
IF DIFF&#FF80 ;если укладываемся в
DB #18,DIFF ;короткий переход
ELSE
JP :0 ;если нет
ENDIF
ELSE
DIFF=-DIFF ;если переход назад
IF DIFF&#FF80
DB #18,-DIFF
ELSE
JP :0
ENDIF
ENDIF
ENDL
ENDM
Можно модернизировать данный макрос и
создать несколько новых с именами jz, jnz,
jc, jnc, которые будут оптимизировать ус-
ловные переходы, и макрос loop, который
будет выбирать между djnz и dec b:jp nz.
А этот макрос позволяет скопировать об-
ласть памяти.
MACRO COPY
LOCAL
OLDADR=$
DEST=:0
ORG :1
DUP :2
DB {DEST}
DEST=DEST+1
EDUP
ORG $
ENDL
ENDM
А пользоваться им надо так:
COPY from,to,length. Если длина пересылае-
мого блока кратна 2, то можно исправить DB
на DW и DEST=DEST+2. Только учтите, что
данный макрос очень медленно работает -
даже медленней,чем бейсик,да и строк доба-
вляет при компиляции 2*length. Он нужен
для копирования данных, которые должна вы-
полнять внешняя по отношению к данной про-
цедура.
Неплохая коллекция макросов поставляет-
ся в комплекте с аласмом, это и счетчик
билдов, и генератор случайной фигни :)
Как-то мне Steve отдал свои диски на
спектрум, причем некоторые ему отдал один
чувак по имени Unwinder, который был в на-
шей группе еще до того, как я в ней появи-
лся. Так вот, на одном диске я нашел игру-
шку "Питон", написанную целиком на макро-
сах и макросредствах ассемблера. Запуска-
лась она при компиляции и была адаптирова-
на под Tasm. Попытки перенести ее на Alasm
не увенчались успехом, к тому же я не ра-
зобраля в принципе ее работы. Но постара-
юсь ее найти и предоставить на всеобщее
обозрение.
На этом разрешите откланяться. Если что
еще придумаю - расскажу обязательно!
------------------------------------------
Alone Coder:
Со своей стороны могу прокомментиро-
вать, что питона под ALASM написать труд-
нее,поскольку придётся пропатчить резидент
ALASM'а, чтобы он включил прерывания при
ассемблировании. К счастью, он вроде бы не
использует нестандартных стеков...
...А J7n/4D, программируя под TASM,изобрёл
совершенно безумный макрос. Интересно, что
на ALASM он тоже работает!
;MACROS LIBRARY Revision 2.0
;J7n'n'MiKE/4D 98-2001
MACRO COP
IFN :1
:0
COP :0,:1-1
ENDIF
ENDM
Произвольная команда копируется произ-
вольное число раз! И это в одну строчку!
А вот оттуда же, новая вариация на тему
выравнивания кода по границе 256 байт:
MACRO OFFSET
IFN .$
DS #100-.$
ENDIF
ENDM
Вот мой макрос опроса портов клавиату-
ры. Вместо опроса порта #FEFE просто пишем
"i 2", можно с параметром, тогда можно вы-
делить нужную клавишу:
MACRO i
LD A,-:0
IN A,(-2)
IFN ?i:1-2
AND :1
ENDIF
ENDM
Как вы можете заметить, мои макросы од-
нобуквенные. Это для экономии места в ис-
ходнике. Для ещё большей экономии вызовы
макросов следует писать от поля метки, то
есть вот так:
i 2,1
Хотел я сделать такой макрос, но не по-
лучилось - русские буквы не понимаются:
;MACRO ы
;EX DE,HL
;ENDM
Поэтому придётся писать по-старинке:
DB "ы
А вот мой вариант JP/JR:
;Jp/r (UNDEFINED=JR)
MACRO j
IFN ?:0
JR :0
ELSE
IF :0-$+126/256
JR :0
ELSE
JP :0
ENDIF
ENDIF
ENDM
Правда, я им не пользуюсь.
Проверка HL на ноль с возможным перехо-
дом по нулю. Этот макрос не только убавля-
ет исходник,но и позволяет сменить условие
нахождения начала текста в ACEdit. (Макрос
используется во всех проверялках начала
текста в редакторе.)
MACRO b
LD A,H
OR L
IFN ?b:0-2
JR Z,:0
ENDIF
ENDM
Что делает следующий макрос, надеюсь,
объяслять не надо?
MACRO [
LD HL,(:0)
CALL :1
LD (:0),HL
ENDM
Этот макрос я использовал для хранения
таблицы скан-кодов клавиатуры в формате,
описанном в AlCoNews#6:
MACRO d
DB :0
DW :1
ENDM
Кстати, его можно зациклить! Только ещё
вопрос, будет ли это выгодно по размеру :)
Это просто копирование откуда-то куда-
то (позволяет экономить некоторое количес-
тво места в исходнике):
MACRO l
LD HL,(:0)
LD (:1),HL
ENDM
Сравнивалка 16-разрядных чисел.Если да-
ны параметры, то ещё и переход по условию!
MACRO s
OR A
SBC HL,BC
ADD HL,BC
IFN ?s:0-2
JR :0,:1
ENDIF
ENDM
Вот это интересный макрос.Такие вот ма-
кросы, совершенно неожиданные, появляются
при оптимизации программы. Эту последова-
тельность команд я нашёл весьма популяр-
ной, когда смотрел распечатку ACE. Вывод
таких макросов - первый шаг к поиску мест
для сокращения программы по длине.
Итак: цикл,в котором что-то вызывается,
и что-то (регистр-другой) инкрементирует-
ся!
MACRO c
LOCAL
cc
CALL :0
IFN ?c:1-2
INC :1
ENDIF
IFN ?c:2-2
INC :2
ENDIF
DJNZ cc
ENDL
ENDM
На самом деле можно было бы не прове-
рять наличие параметров (всё-таки ненаг-
лядная и недокументированная возможность
ALASM),но тогда при ассемблировании на эк-
ран будут выводиться ошибки,что некрасиво.
К тому же, лучше точная уверенность в ре-
зультате!