О макросе бедном замолвите слово... (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&#8000 ;если переход вперед 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),но тогда при ассемблировании на эк- ран будут выводиться ошибки,что некрасиво. К тому же, лучше точная уверенность в ре- зультате!