Низкоуровневое программирование для Дzenствующих
Вид материала | Документы |
- Низкоуровневое программирование, 108.99kb.
- Курс является базовым как для изучения других математических дисциплин, так и для более, 36.89kb.
- 1 Обобщенное программирование. Обобщенное программирование это еще одна парадигма программирования,, 55.18kb.
- Введение в линейное программирование линейное программирование (ЛП), 139.72kb.
- Учебно-методический комплекс для студентов заочного обучения специальности Прикладная, 63.23kb.
- Аттестационное тестирование в сфере профессионального образования, 72.49kb.
- Лекции по дисциплине «Социальное моделирование и программирование», 44.69kb.
- Программа дисциплины Линейное программирование Семестр, 17.93kb.
- Программа дисциплины "Программирование" для направления, 488.76kb.
- Рабочая программа по дисциплине Программирование на языке высокого уровня для специальности, 182.97kb.
III.7. Символ макроподстановки
Ещё раз вернёмся к формальным параметрам макро. Как было сказано, при раскрытии макроопределения препроцессор заменяет в теле макро формальные названия на их величины. В MASM32 предусмотрено ещё одно средство подстановки макропараметров – внутри строкового литерала.
Предположим нам нужно, чтобы макро генерировал строку: «label_xx». Где xx – это формальный параметр макро. Это можно сделать двумя способами:
@CatStr(label_,xx) ;;Вызов макрофункции конкантенации строк
или
label_&xx& ;;Использование символа макроподстановки
То есть если во время генерации макро, препроцессор встречает в его теле символ «&», он анализирует строку после него. Если эта строка однозначно определяет один из макропараметров, препроцессор заменяет выражение &макропараметр& на значение макропараметра.
Следует отметить, что если макропараметр начинает или заканчивает литерал, то можно использовать только один символ «&»:
label_&xx
;;или ещё пример
label_&xx&&xx2 ;; Замена для двух макропараметров xx и xx2
III.8. Макроблоки
И, наконец, у читателя должен остаться единственный вопрос: «А как обрабатывать переменные типа VARARG»? Например, рассмотрим возможный макро для вызова функций – STDCALL:
stdcall macro funname,params:VARARG
endm
Этот макро должен генерировать код вызова функции согласно конвенции STDCALL:
- Поместить параметры в стек в обратном порядке их определению.
- Вызвать функцию funname, предварительно видоизменив её имя по правилам STDCALL.
Получить видоизмененное имя функции по значению параметра funname можно было бы при помощи символа макроподстановки.
call _&funname@(количество параметров * 4)
Но непонятно, как распознать параметры функции, которые представляют собой строку, где значения разделены символом «,». Более того, не понятно, как вообще можно получить эти параметры, и посчитать их число, ведь макропараметр params – это одна строка. То есть при вызове макро:
stdcall win32fun,1,2,3
Мы должны как-то определить количество параметров, а потом их значения.
Именно для решения этой задачи в MASM предусмотрены несколько специальных макроопределений, которые можно назвать макроблоками.
Первый из них FOR позволяет получить значения элементов, разделённых в строке символом «,».
FOR parameter[:REQ | :=default], string
statements
ENDM
Вспоминая С конструкцию FOR, вы сразу поймёте что это цикл, где значение parameter последовательно принимает значения элементов списка string.
Вот вам wonderful пример:
FOR parameter,
echo parameter
ENDM
ВЫВОД:
-=-=-=-=-=-=
It's
wonderful
wonderful
asm
-=-=-=--=-=-
А вот пример макрофункции, который подсчитывает число аргументов VARARG:
@ArgCount MACRO parmlist:VARARG
count = 0
FOR param,
count = count + 1
ENDM
EXITM count
ENDM
Вот в принципе, уже на основе этих знаний можно было бы организовать макрос stdcall:
stdcall macro funname,params:VARARG
count = 0
FOR param,
count = count + 1 ;; Считаем число параметров
push param ;; Помещаем их в стек
ENDM
;;Вызываем функцию
call ??? ;;А вот как это сделать?
endm
Ещё несколько минут необходимо для того, чтобы понять, что этот макро работает неправильно. Хотя бы потому, что параметры помещаются в стек не так. Нужно было бы помещать их от последнего к первому, а не от первого к последнему. А, кроме того, ведь символ макроподстановки нельзя употреблять к макропеременной count, потому что это не макропараметр, это макропеременная.
К сожалению, в MASM нет обратной конструкции FOR. Поэтому самый простой выход, который напрашивается сам собой – это изменить порядок параметров в списке, а потом только генерировать команды push.
Вторую проблему можно легко решить, воспользовавшись макрофункцией конкатенации строк:
call @CatStr(_,funname,@,%(count*4))
С параметрами в стек будет посложнее. В принципе я бы решил эту задачу, если бы MASM поддерживал бы такой тип макропеременных как массив. Но хотя MASM и не поддерживает этот тип, его можно эмулировать.
count = 0
FOR param,
count = count + 1 ;; Считаем число параметров
@CatStr(var,%count) TEXTEQU
ENDM
Как вы можете догадаться, в этом примере создаются макропеременные varXX, которым присваиваются значения параметров. Теперь с той же лёгкостью можно работать с этими переменными. Можно снова использовать цикл FOR, однако в данном случае, было бы грамотней воспользоваться значением count, и выполнить цикл столько раз, сколько записано в нашем счётчике параметров. Для этого мы воспользуемся ещё одним макроблоком rept, о котором скажем позже:
nparams = count
REPT nparams ;; Начало блока
push @CatStr(var,%count)
count = count - 1
ENDM
Блок REPT выполняется столько раз, сколько указано в nparams. Я ввёл эту дополнительную макропеременную, для того, чтобы значение, указанное в REPT осталось неизменным. Однако этого не нужно. Можно было бы написать и так:
REPT count ;; Начало блока
push @CatStr(var,%count)
count = count - 1
ENDM
Значение макропеременной count инициализирует цикл только один раз вначале, после чего, она может, как угодно менять значение.
И ещё один макроблок, без которого нам невозможно будет реализовать макрос для определения строк уникода, или макрос, который позволяет писать строки OEM в редакторе использующий кодировку win cp-1251 (например, при создании консольных приложений).
Этот макроблок FORC:
FORC char, string
;;блок
ENDM
Блок FORC выполняется столько раз, сколько символов в строке string, при этом макропараметр char равен текущему символу из строки.
Например, посчитать количество символов в строке можно было бы так:
count = 0
FORC char, <Сколько тут символов?>
count = count + 1
ENDM
%echo @CatStr(%count)
А вот так, можно было бы посчитать количество пробельных символов.
count = 0
FORC char, <Сколько тут символов?>
IFB
count = count + 1
ENDIF
ENDM
%echo @CatStr(%count)
Упражнение:
TheSvin'у, как и любому программисту, который часто имеет дело с битами, было бы удобно записывать значения бит по группам, через пробел.
;;Вот так неудобно и ненаглядно
mov eax,011110111011b
;;Вот так удобно и наглядно, но компилятор выдаст ошибку
;;Вариант1
mov eax, 0111 101 1101 1b
;;А вот так вообще замечательно, только ML неправильно поймёт
;;Вариант2
mov eax, [0111] [101] [1101] [1]b
Хорошо бы было написать некую макрофункцию, которая смогла бы позволить записывать эти выражения:
mov eax,nf(0111 101 1101 1b)
Напишите такую макрофункцию, которая позволила бы это делать. Напишите её для первого и второго вариантов исполнения.
III.9. Отладка макроопределений и заключение
А напоследок… остаётся маленькая деталь.
И эта деталь не самая приятная. Отладка макроопределений и их испытания невозможны под отладчиком. А, кроме того, если при генерации макро возникает ошибка, то ML выдаёт её в жутком виде:
.\start.asm(84) : error A2008: syntax error : in directive
MacroLoop(3): iteration 8: Macro Called From
.\start.asm(84): Main Line Code
То есть он выдаёт относительную строку в макро MacroLoop(3), где эта ошибка появилась. А если ещё макровызовы будут вложенными, то вам лучше не видеть этой замечательной картины.
Единственной возможностью качественно и относительно легко отлаживать макро – это употребление директивы echo.
На протяжении статьи вы не раз наблюдали примеры её использования. Но я снова повторюсь:
;; Для макропараметров
echo macroparam
;; Для макропеременных типа строка или текстовых макро
%echo macrovar_string
;; Для целочисленных макропеременных, или макроконстант
%echo @CatStr(%macro_num)
Заметьте, чтобы вывести значение целочисленной макропеременной необходимо воспользоваться макрофункцией @CatStr(), и перед аргументом указать оператор %. Почему именно так обсуждалась в пункте III.2. Определение макро переменных и строк.
Теперь вы знакомы с теорией использования макроопределений в MASM32, и сможете смело приступать к разработке макро. Именно этим мы и займёмся в следующей практической части нашего руководства, а так же заполним некоторые пробелы, на которые не обратили внимания здесь.
III.10. Абстрактный алгоритм анализа строки MASM (Дополнение)
1. Определены таблицы элементов:
Таблица переменных | Хранит сведения о всех переменных модуля |
Таблица меток | Хранит список меток в коде. |
Таблица процедур | Хранит таблицу и прототип процедур |
Список ключевых слов KEYLIST | Хранит список ключевых слов, на которые реагирует ML |
Таблица макрофункций | Хранит тело всех макро, их имена и тип: макрофункция, или макро. Список макропараметров |
Таблица макросов | -=- |
Таблица макропеременных, или переменных времени компиляции | Хранит тип макропеременной и её значение. |
Всё остальное, что не включено | |
2. Начальное состояние анализа строки.
3. Читать поток символов, пока не встретится символ возврата каретки без предыдущего символа «/». Игнорировать часть строки после «;»
a. Определить наличие лексем первого уровня в строке:
i. Выделить все строковые литералы в кавычках, если только это не выражение с TEXTEQU и символ комментария «;»
ii. Строковые литералы: <текст>
iii. Численные литералы: 1234, 1234h, 01011b
iv. Правильные литералы: строка из символов «A-Z,a-z,_0-9», но не начинающаяся на цифру
v. Литералы разделители: «,.»
vi. Управляющие Литералы: «+-*» Правильные литералы: строка из символов «A-Z,a-z,_0-9», но не начинающаяся на цифру
b. Проверить правильные литералы на совпадение в списке ключевых слов, и определить схему выражения. В зависимости от схемы выражения, выполнить или пропустить:
i. Проверить правильные литералы на совпадение в списке макро (в зависимости от способа вызова в списке макрофункций, или макросов)
ii. Проверить на наличие имени правильного литерала в таблице макропеременных.
iii. Осуществить вызов и замену макро и макропеременных, в соответствии с выражением строки.
iv. Вычислить все выражения допустимые в ML (+-*).
c. Осуществить разбор схемы.
i. Если это определение процедуры, записать в таблице процедур имя и прототип новой процедуры
ii. Если это макроопределение: анализировать его тело. Если найден возвращаемый параметр, записать макроопределение в таблицу макрофункций, иначе в таблицу макросов.
iii. Если это определение EQU вычислить правую часть.
1. Если эта макропеременная уже есть в таблице макропеременных, и её тип – числовой, выдать ошибку. Если эта макропеременная имеет строковый тип, изменить строку, на которую указывает свойство value этой макропеременной.
2. Если правая часть числовой литерал – записать EQU определение в таблицу, и пометить его тип как числовой константы. Записать в свойство макропеременной value значение указателя на строку. Записать свойство value равным числу.
3. иначе EQU – переменная, имеющая указатель на строку. Записать в значения свойства value указатель на строку.
iv. Если это выражение с «=» или подобное, выполнить замену всех литералов на макроконстанты, переменные, вызов всех макрофункций, и только потом выполнять выражение.
4. Перейти к анализу следующей строки.
# Эпилог
Когда большая стрелка часов перекотила через 12-ти часовой рубеж, появился свет... |