Info Guide #06
03 декабря 2004

For Coderz - Макросы ч.2 - облегчаем себе жизнь при программировании.

<b>For Coderz</b> - Макросы ч.2 - облегчаем себе жизнь при программировании.
    О макросе бедном замолвите слово-2
                   или
           Макрос возвращается.
 

   В этой статье мы продолжим знакомство с
представителями  замечательного  семейства 
макросов :)  Вы узнаете, как  с их помощью 
ещё больше облегчить себе  жизнь при прог- 
раммировании.Как всегда,примеры разработа- 
ны применительно к ALASM. Для ALASM версии 
4.46 и выше параметры макросов обозначают- 
ся префиксом '' вместо двоеточия. 

   Вам,наверное уже известны способы полу-
чать адреса,выравненные по какой-то грани-
 це. Вот один из вариантов:
        ORG '($-1)+1<8
   Это  выравнивание  по границе 256 байт.
 Можно определить более общий случай:
         MACRO ALIGN
         ORG +$-1&(-())
         ENDM
   [Желательно вместо ORG использовать DS,
чтобы не допустить заполнения памяти неиз- 
вестно чем,каковое неизвестно что пакуется 
обычно хуже,чем нули. Сделайте макрос с DS 
 - это вам домашнее задание.] 
    - это  параметр  макроса.  В  данном
случае параметр имеет значение границы вы-
равнивания. [В скобки  он заключен потому,
что  параметром может оказаться выражение. 
При вызове макросов параметры передаются в 
том виде,как они стоят в строке вызова.Ес- 
ли  макрос  вызывает  макрос, то параметры 
внешнего  макроса раскрываются.] Очевидно, 
что если в программе много подобных вырав-
ниваний, то возникают довольно большие по-
тери памяти. Но у нас есть макросы,значит,
мы  можем попытаться проконтролировать эти
потери  и  затем попытаться их минимизиро-
 вать. Модифицируем наш макрос:
         MACRO ALIGN
        LOCAL
 OLDADR=$ 
         ORG +$-1&(-())
         IFN $-OLDADR
         DISPLAY "Memory leak before ",/H,$
         DISPLAY "Leak size=",$-OLDADR
         ENDIF
         ENDL
        ENDM
   Теперь при компиляции мы будем получать
сообщения типа:
  Memory leak before #a700
  Leak size=253
   Если  мы  соптимизируем  код  до адреса
#a603 на 3 байта, то получим выигрыш в 256 
байт!
   К сожалению, данный  макрос не работает
при компиляции в верхней памяти - без ука-
зания страницы ORG по умолчанию компилиру-
ет в нулевую. [Текст  писался давно, и для
ALASM 4.46 это замечание уже не справедли- 
во.] 

   Иногда бывает,что на одном диске храни-
тся несколько проектов. И часть файлов ис-
ходников  принадлежит нескольким проектам,
причём зависимости могут быть самыми запу-
танными.Иногда возникают случаи,когда один
и  тот  же  файл  бывает включён несколько
раз. Чтобы избежать этого и раз и навсегда
обезопасить  себя  от  дальнейшей головной
боли, оформляем файлы следующим образом:
IFN ?_UNIQUE_NAME_  ;в самом начале файла 
 _UNIQUE_NAME_=0 
   ...
ENDIF               ;в самом конце файла 
   Как  вы  уже  догадались, _UNIQUE_NAME_
должно быть  для каждого файла уникальным.
Работает всё это очень просто.Если включа-
емый  файл  не обнаруживает в пространстве
меток свой идентификатор, он его объявляет
и со спокойной совестью компилируется.Ина-
че он просто пропускается.

   Обычно программа существует в двух вер-
сиях - отладочной  и  итоговой. Отладочная
версия,как вы уже поняли,существует только
у программиста,который её и делает. Итого-
вая версия - это то,что попадает к пользо-
вателю. Между  двумя  этими версиями может
быть  довольно большая разница. Начиная от
выхода (в отладочной версии  это возврат в
ассемблер,а в итоговой или выход в дос или
игнорирование) и заканчивая  заменой целых
кусков  кода. Как  это реализовать удобно,
чтобы  не  забыть  в предрелизной суматохе
заменить  нужные  строчки. Очень просто. В
критических  местах  программы расставляем
 строки:
       IFN ?_DEBUG_ ;0=метка уже = чему-то
                    ;1=не присвоена и used
                     ;-1=вообще нет метки
        ...       ;release version
        ELSE
        ...       ;debug version
       ENDIF
   А в самом начале программы пишем строч-
ку:
_DEBUG_ 
   При релизе ее просто нужно будет заком-
ментировать. "Реакция" исходника на версию
может быть самой разной.Это может быть на-
поминание о проверке версии программы, вы-
вод диагностических сообщений и т.д.

   ALASM с версии 4.46 поддерживает  неза-
крытие скобок условной компиляции в макро-
сах, поэтому вышеприведенные примеры можно
 слегка модифицировать, создав макросы:
        MACRO IFDEF
        IF ?
       ENDM
 

        MACRO IFNDEF
        IFN ?
       ENDM
   Как их применять, думаю, вы уже догада-
лись :)

          Работа с множествами.

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

   Сначала файл примера:
 

         INCLUDE "MAKESET",199
        ORG 24576
 ;пустое множество 
        MAKESET SET1,"A","B",""
 ;множество из двух букв 
         MAKESET SET2,"B","Z","CD"
        ORG 24576 ;обязательно!!!

   И текст самого макроса:
 

        MACRO MAKESET
;MAKESET NAMESET,MIN,MAX,.... 
      DS 2-1+8&#FFF8>3 
 

        IF ?BITOPER+1
 BITOPER RRA 
         RR L
         RRA
         RR L
         RRA
         RR L
         RRA
        RRA
 BITOPA  OR #46 
        LD (BITOP+1),A
 BITOP   DB #CB,0        ;$+1 
         RET
        ENDIF

_MIN=1 
_MAX=2 

 ADD   LD L,A 
         LD A,#C6
        JR OP

 DEL   LD L,A 
         LD A,86
        JR OP

 ISIN  LD L,A 
        LD A,#46
 OP    LD (BITOPA+1),A 
         LD A,L
         IFN 1
         SUB 1
         RET C
         ENDIF
         CP 2-1
         JR Z,$+4
         CCF
         RET C
                         ;HERE
         LD H,'
         IFN &31
         ADD A,&31<3
         ENDIF
         LD L,A
         IFN &7
         LD A,&7
         ELSE
         XOR A
         ENDIF
        JP BITOPER
 

        LOCAL
 _OLDADR=$ 
         DUP 3
         REPEAT
         UNTIL "NC"-44
        EDUP
 

         IFN "NNC"-34
         REPEAT
        ORG "C"-1&#FFF8>3+
 

         IFN "C"-1<1&1
         DISPLAY "SYMBOL C IS OUT OF RANGE
         ENDIF
         IFN 2-"C"<1&1
         DISPLAY "SYMBOL C IS OUT OF RANGE
        ENDIF
 

         DB {$}|(1<("C"-1&7))
         UNTIL "NC"-34
         ENDIF
         ORG _OLDADR
         ENDL
        ENDM

   Как можно заметить, также создается не-
обходимый  код для обеспечения модификации
множества и определения наличия элементов.
Если множество имеет имя SET1, то операции
будут  иметь соответственно имена ADDSET1,
DELSET1, ISINSET1. Если  вам  нужна только 
процедура проверки, остальные можно не ге-
нерировать.

    Большая программистская хитрость :)

   Вы уже привыкли, что макросы обычно вы-
полняют пассивные функции, управляя компи-
ляцией  и предупреждая. А вот как их можно
заставить  выполнять работу. Ниже приведён
пример  библиотеки  макросов,  которая, не
делая ни одного байта объектного кода, со-
здаёт  файл дизассемблера заданной области
памяти. Специфика макросов объясняет такой
огромный и запутанный "код" (а также нако-
пление  знаний  в  процессе создания этого
исходника  и лень исправлять старые реали-
зации :) ).
   Из-за "подстановочного" алгоритма обра-
ботки  макросов  в ALASM 4.46f  библиотека
очень медленно работает в этой версии ала-
сма [только в ней!], поэтому  лучше её за-
пускать в более ранних [или более поздних]
версиях ассемблера.
   Вот пример файла,демонстрирующего рабо-
 ту библиотеки:
         INCLUDE "mDASM",#C4
        ORG 24576
;_DEC        ;включение десятичной системы 
 ;_VERBOSE    ;отображение текущего адреса 
        DISASM "TESTA",#C0,0,149
   В данном случае в странице #C0 создаёт-
ся  файл с именем TESTA, содержащий дизас-
семблер  кода  ПЗУ с адреса 0 и длиной 149
байт.Файл нужно просто откомпилировать.За-
пускать ничего не надо.
   А вот что получилось в итоге:

;This file was created by mDASM. 
;(C) Vitamin/CAIG/2001 all rights reserved 

 L00000  DI 
         XOR A
         LD DE,#FFFF
        JP #11CB

 L00008  LD HL,(#5C5D) 
         LD (#5C5F),HL
        JR $+#45

L00016  JP #15F2 

 L00019  RST #38 
         RST #38
         RST #38
         RST #38
         RST #38
         LD HL,(#5C5D)
         LD A,(HL)
         CALL #007D
         RET NC
         CALL #0074
        JR $-#07

 L00037  RST #38 
         RST #38
         RST #38
        JP #335B

 L00043  RST #38 
         RST #38
         RST #38
         RST #38
         RST #38
         PUSH BC
         LD HL,(#5C61)
         PUSH HL
        JP #169E

 L00056  PUSH AF 
         PUSH HL
         LD HL,(#5C78)
         INC HL
         LD (#5C78),HL
         LD A,H
         OR L
         JR NZ,$+#05
         INC (IY+#40)
         PUSH BC
         PUSH DE
         CALL #386E
         POP DE
         POP BC
         POP HL
         POP AF
         EI
        RET

 L00083  POP HL 
         LD L,(HL)
         LD (IY),L
         LD SP,(#5C3D)
        JP #16C5

 L00095  RST #38 
         RST #38
         RST #38
         RST #38
         RST #38
         RST #38
         RST #38
         PUSH AF
         PUSH HL
         LD HL,(#5CB0)
         LD A,H
         OR L
         JR NZ,$+#03
        JP (HL)

 L00112  POP HL 
         POP AF
        RETN

 L00116  LD HL,(#5C5D) 
         INC HL
         LD (#5C5D),HL
         LD A,(HL)
        RET

 L00125  CP #21 
         RET NC
         CP #0D
         RET Z
         CP #10
         RET C
         CP #18
         CCF
         RET C
         INC HL
         CP #16
         JR C,$+#03
         INC HL
         SCF
         LD (#5C5D),HL
        RET

   Как видно, код получается не релоцируе-
мый.Реализация замены всех адресов метками
потребовала бы ещё одного прохода по коду,
что увеличило бы и так большое время обра-
ботки. Например,при обработке вышеупомяну-
того  куска кода длиной 149 байт было нас-
читано  более 50 тыс. строк. Счётчик строк
может несколько  раз переполниться, прежде
чем вы получите результат.
   И, наконец,текст самой библиотеки. [Ос-
новных фрагментов.Полностью ищите в прило- 
жении.] 
 

         MACRO digit
         IF -10<1&1
         DB +55
         ELSE
         DB +"0
         ENDIF
        ENDM
 

         MACRO itoa ;$=ADDR
         IF ?_DEC+1
        DB "#
digit <4&15 
digit <8&15 
digit >4&15 
 digit &15 
        ELSE
LVAL= 
digit LVAL/10000 
 LVAL=LVAL-(LVAL/10000*10000) 
        ...
digit LVAL/10 
LVAL=LVAL-(LVAL/10*10) 
 digit LVAL 
         ENDIF
        ENDM
 

        MACRO btoa
 ;то же, но для байта 
        ENDM
 

         MACRO offset
         IF <1&1
        DB "+
 btoa  
         ELSE
        DB "-
 btoa - 
         ENDIF
        ENDM
 

        MACRO offs
LVAL=&128*511| 
 offset LVAL 
        ENDM
 

         MACRO hl
         IFN _IX
         DD E4  ;IX
         ELSE
         IFN _IY
         DD E5  ;IY
         ELSE
         DD E2
         ENDIF
         ENDIF
        ENDM
 

        MACRO hl_l
 ;аналогично для LX/LY/L 
        ENDM
 

        MACRO hl_h
 ;аналогично для HX/HY/H 
        ENDM
 

         MACRO _hl_
         LOCAL
        IFN _IX
_FROM=_FROM+1 
_LOFF=.{_FROM} 
 _SIZE=_SIZE-1 
         IFN _LOFF
        DD D1          ;(IX
 offs _LOFF 
         DB ")
         ELSE
         DD A3          ;(IX)
         ENDIF
         ELSE
        IFN _IY
 ;аналогично для (IY+d) 
         ELSE
         DD A1          ;(HL)
         ENDIF
         ENDIF
         ENDL
        ENDM
 

         MACRO lrpNN
         DD D4
        IF -#E2
 hl 
         ELSE
         DB 
         ENDIF
        DB ",
itoa {_FROM+1} 
 _OPSIZE=_OPSIZE+2 
        ENDM
 

         MACRO selreg
         IFN -4<1&1
         DB +#EF      ;B,C,D,E
         ENDIF
        IF -4
 hl_h                   ;H 
         ENDIF
         ...
        ENDM
 

        MACRO selop
[ IF..ELSE:IF..ELSE:IF..ENDIF: ENDIF:ENDIF 
компилируется не быстрее, чем несколько IF 
..ENDIF, а на стеке занимает места больше. 
 По 4 байта на IF. ] 
         IF -7
         DD D8          ;CP
         ENDIF
         ...
         IF 
         DD AEF52C      ;ADD
         ENDIF
        ENDM
 

         MACRO operCB
         IF 
         DD B8          ;RLC
         ENDIF
         ...
        ENDM
 

         MACRO operED   ;lda,rp,rh
         IF           ;IN r,(C)
         IF 2-#F5
         DD CE          ;INF
         ELSE
         DB #DC,2,",",#D0
         ENDIF
         ENDIF
         ...
         IF -15
         ...
         ENDIF
        ENDM
 

         MACRO flag
         IF 
         DD E7          ;NZ
         ENDIF
         ...
         IF -7
         DD F7          ;M
         ENDIF
        ENDM
 ;----------------------------------------- 
         MACRO DISASM
        ...
_FROM=2 
 _SIZE=3 
        ORG #C000,1
 _MNAME  DB S, 
         IFN $-_MNAME-8
         DS 8-$+_MNAME,32
         ENDIF
         DB "H
        DS 24
 _HSIZE  DW 0,#C040 
         DB 0,0
         DB 1
         DD F376C7DDFDEDB0D9 ;ключ ALASM
        DS 16
_TXTSTART 
DB 33,";This file was created by mDASM." 
DB 43,";(C) Vitamin/CAIG/2001" 
DB " all rights reserved" 
 _SETLAB=1         ;начнём исходник с метки 
        REPEAT               ;главный цикл
 _STRLEN=$ 
        DB 0
 ;SET LABEL 
         IFN _SETLAB
         ORG $-1
        DB 1
 _STRLEN=$ 
        DB 0,"L
LVAL=_FROM 
digit LVAL/10000 
 LVAL=LVAL-(LVAL/10000*10000) 
        ...
digit LVAL 
 _SETLAB=0 
        ENDIF

_DA=.{_FROM} 
_OPSIZE=1 
_IX=0 
 _IY=0 
        REPEAT
 _SETXY=0 
         IFN ?_VERBOSE+1
         DISPLAY _FROM
         ENDIF
        IF _DA-#DD
_IX=1                  ;IX PREFIX 
_IY=0 
_FROM=_FROM+1 
_SIZE=_SIZE-1 
_DA=.{_FROM} 
 _SETXY=1 
         ENDIF
        IF _DA-#FD
 _IY=1   ...            ;IY PREFIX 
         ENDIF
        UNTIL _SETXY
 

         IF _DA
         DD A5          ;NOP
         ENDIF
        IF _DA-1
 lrpNN #E0              ;LD BC,nn 
         ENDIF
         ...
         IF _DA-6
        DD D4EF2C      ;LD B,n
btoa .{_FROM+1} 
 _OPSIZE=_OPSIZE+1 
         ENDIF
        ...
 _DA=_DA-16 
         IF _DA
        DD 8B24        ;DJNZ
offs .{_FROM+1}+2 
 _OPSIZE=_OPSIZE+1 
         ENDIF
        ...
 _DA=_DA-16 
         ...
         IF _DA-2
        DD D428        ;LD (nn),HL
 itoa {_FROM+1} 
        DB "),
hl 
 _OPSIZE=_OPSIZE+2 
         ENDIF
         ...
         IF _DA-9
        DD AE          ;ADD HL,HL
 hl 
        DB ",
 hl 
         ENDIF
        ...
 _DA=_DA-16 
        ...
 _DA=_DA-16 
         IF _DA&192
         IF _DA-#36
         DD 86          ;HALT
         ELSE
        DD D4
 

        IFN _IX
        IF _DA&56>3-6
selreg _DA&56>3 
 _IX=0 
         ELSE
        IF _DA&7-6
 _IX=0 
        ENDIF
selreg _DA&56>3 
 _IX=1 
         ENDIF
        ELSE
       IFN _IY
 ;аналогично, но с IY вместо IX 
       ELSE
 selreg _DA&56>3 
        ENDIF
        ENDIF
        DB ",
 selreg _DA&7 
         ENDIF          ;36
        ENDIF          ;192

 _DA=_DA-64 
        IF _DA&192
selop _DA&56>3 
 selreg _DA&7 
        ENDIF

 _DA=_DA-64 
         IF _DA&192
         IF _DA&7
        DD B4          ;RET
 flag _DA>3&7 
         ENDIF
         ...
        IF _DA&7-3
 _PAR=_DA>3&7 
         ...
         IF _PAR-1      ;CB
         LOCAL          ;###
         ...          ;хитрая обработка #CB
         ENDL           ;***
         ENDIF          ;CB
         ...
         ENDIF
         ...
        IF _DA&7-5
 _PAR=_DA>3&7 
         ...
         IF _PAR-5      ;ED
         LOCAL          ;###
         ...          ;хитрая обработка #ED
        ENDL           ;###
 _OPSIZE=_OPSIZE+1 
         ENDIF          ;ED
         ...
         ENDIF
         ...
        ENDIF
;STRING FINISH 
_STRSIZ=$-_STRLEN 
 _OLDADR=$ 
         ORG _STRLEN
         DB _STRSIZ
        ORG _OLDADR
_SIZE=_SIZE-_OPSIZE 
 _FROM=_FROM+_OPSIZE 
         UNTIL ~_SIZE<1~&1 ;конец гл. цикла
        NOP       ;признак конца исходника
 _TEXTSIZ=$-_TXTSTART 
         ORG _HSIZE,1
         DW _TEXTSIZ
         ORG 82  ;чтобы не запустили по RUN
        ENDM         ;конец макроса DISASM

Vitamin/CAIG/2001 



Другие статьи номера:

Inferno - Вступление от редактора.

Интервью - интервью с AIG - кодером из группы MKHG.

Sofтинка - ACE 0.888: отличия от 0.666

Sofтинка - макро-ассемблер отладчик ALASM 4.47: отличия от 4.44

For Coderz - Арифметическое кодирование.

Inferno - Авторы журнала.

Sofтинка - BGE 4 графический редактор для ZX.

События - The Compo 2: результаты голосования.

For Coderz - Декомпиляция программ - оживление старых прог.

Inferno - Ошибки в предыдущих номерах.

For Coderz - Маленькие программерские хитрости.

DIY - Схема моего электрофумигатора.

Gameland - о пройденных играх: Imperia 2, Hexagonal Filler, From Beyond.

Железо - устройство расширенной клавиатуры (58 клавиш).

Gamedev - Игровой цикл - цикл, внутри которого вызываются все подпрограммы игры.

Gameland - прохождение Lords of Time от Level 9.

For Coderz - Макросы ч.2 - облегчаем себе жизнь при программировании.

Inferno - Письма в редакцию.

Gameland - прохождение уровней игры Чёрный Ворон.

For Coderz - Описание модульной структуры программ.

Inferno - Об оболочке.

Sofтинка - преимущества упаковочного алгоритма Optimal LZH.

События - серпуховский фестиваль ParaDiGMus party 2003. Как это было.

События - серпуховский фестиваль ParaDiGMus party 2003. Afterparty.

Gameland - прохождение игры The Price of Magik от Level 9.

Железо - Описание блока памяти от принтера Robotron CM 6329.01 M. Часть 1.

Железо - Описание блока памяти от принтера Robotron CM 6329.01 M. Часть 2.

Реклама - реклама и объявления.

DIY - советы по ремонту часов, Dream Cast и джойстика.

Интервью - Интервью с Shaitan/Stars of Keladan: Interred Inferno.

Gameland - прохождение игры Snowball от Level 9.

Железо - Видеомагнитофон GoldStar RN800AW Art vision. История ремонта.

Железо - Видеомагнитофон GoldStar RN800AW Art vision. Советы по разборке и ремонту.

Интервью - интервью с музыкантом Visual^Extreme (Сергей Агапов).

Gamedev - о сборке игры Wolfenstein 2004. Часть 1.

Gamedev - о сборке игры Wolfenstein 2004. Часть 2.

For Coderz - Как получить на звуковом устройстве больше бит.


Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Мнение - письмо к Nemo.
Анекдоты - Идет Вовочка по улице, смотрит в мерседесе сидит браток...
Кто там кодит? - Быстрая графика: несколько рецептов от Zetter'а (печать спрайтов, обновление экрана).
Ария - Сборник лучших песен Самой крутой рок-группы 'АРИЯ' (часть вторая).
IS-DOS - news: новые программы IS-DOS.

В этот день...   8 мая