Низкоуровневое программирование для Дzenствующих
Вид материала | Документы |
- Низкоуровневое программирование, 108.99kb.
- Курс является базовым как для изучения других математических дисциплин, так и для более, 36.89kb.
- 1 Обобщенное программирование. Обобщенное программирование это еще одна парадигма программирования,, 55.18kb.
- Введение в линейное программирование линейное программирование (ЛП), 139.72kb.
- Учебно-методический комплекс для студентов заочного обучения специальности Прикладная, 63.23kb.
- Аттестационное тестирование в сфере профессионального образования, 72.49kb.
- Лекции по дисциплине «Социальное моделирование и программирование», 44.69kb.
- Программа дисциплины Линейное программирование Семестр, 17.93kb.
- Программа дисциплины "Программирование" для направления, 488.76kb.
- Рабочая программа по дисциплине Программирование на языке высокого уровня для специальности, 182.97kb.
Руководство по проектированию макросов в MASM32
(часть 1.2)
Пойми в Хаосе Разное, и стань человеком.
Осознай Единое в Различном – и будь Богом.
Автор
I. От автора
I.1. Для тех, кто впервые
I.2. Примечания (обо всём понемногу)
I.3. Особенности терминологии
I.4. Благодарности
II. Лень – двигатель Макро
III. Макромир MASM
III.1. Функционирование макросов
III.2. Определение макро переменных и строк
III.3. Обработка выражения в MASM
III.4. Целочисленные выражения MASM
III.5. Вычисление рекурсивных выражений
III.6. Встроенные макрофункции и директивы
III.7. Символ макроподстановки
III.8. Макроблоки
III.9. Отладка макроопределений и заключение
III.10. Абстрактный алгоритм анализа строки MASM (Дополнение)
III.2. Определение макро переменных и строк
Я бы назвал следующее:
Param = 0
Constant EQU 123
WASM EQU
WASM_RU TEXTEQU
макропеременными (с тем фактом, что переменная может иметь константный тип).
В терминологии MASM:
WASM EQU
WASM_RU TEXTEQU
;; Такие определения называются текстовыми макро.
;; В этой статье вы встретите два варианта определений
Потому что под термином «переменная» понимается:
var dd 123
Переменные являются частью программы, а макропеременные живут только на этапе компиляции. По сути, они есть более простым видом макроопределений, и поэтому их стоит понимать как специальные макро, которые так же раскрываются препроцессором.
Макропеременная может иметь только три типа – целочисленная макропеременная INEGER4 (dword), целочисленная макроконстанта или текстовой макро (строковая макропеременная).
Автор считает значительным упущением отсутствия возможности определять тип макропеременной. Это очень сильно ограничивает возможности макропрепроцессора. Но что поделать. |
При чём, в зависимости от вида определения макропеременной ML считает, что:
Param = 0 ;; Param – это целочисленная макропеременная
Constant EQU 123 ;; Макроконстанта
;; Текстовой макро (Макропеременная строкового типа)
;; (Это не так в руководстве MASM)
Var EQU qwer
;; Текстовой макро (Макропеременная строкового типа)
WASM EQU
;; Текстовой макро (Макропеременная строкового типа)
WASM_RU TEXTEQU
Как вы уже догадались, каждое макроопределение обладает своими свойствами и возможностями.
- Целочисленная макропеременная. Имеет тип INT (dword). Может участвовать во всех арифметических выражениях MASM. Как переменная она может изменять своё значение.
- Макроконстанта может иметь целочисленное значение. Её значение не может быть повторно изменено.
- Текстовой макро может быть любой строкой не более 255 символов. Поскольку он имеет статус переменной, его значение может быть изменено.
А теперь подробнее. Если с целочисленными макропеременными в достаточной степени ясно. То с определениями EQU полный бардак.
Как и в случае с вызовами макро, автор попытается построить алгоритм анализа EQU выражений:
1. Анализируем правую часть. В анализе правой части препроцессор выделяет лексемы, которые классифицирует как числа, строки. Так, например, в выражении:
qqqq EQU 1234567890 string1 23456789012390 macrofun()
«1234567890» – это лексема число, а «string1» – это строка, «macrofun()» – это всё равно строка (а не макрофункция!!!).
-= Внимание =- |
Именно по этому такое определение будет давать ошибку: qqqq EQU 156n7 : error A2048: nondigit in number |
2. Если правая часть является верным определением числа в MASM, то есть 123 или 123h или 0101b – выполнить шаг три, иначе шаг четыре.
-= Внимание =- |
Обратите внимание, что числа с плавающей запятой в этом случае считаются строкой. Такое поведение связано с внутренней организацией препроцессора ML, который просто «не понимает» чисел с плавающей запятой, и не умеет с ними работать. То есть тип макропеременной Float: Float EQU 1.2345 будет не числовой, а строковой |
3. Если полученное число имеет значение, не превышающее диапазон значений для dword – это целочисленная макроконстанта.
-= Интересно =- |
Если правая часть для EQU является верным числом более 25 символов, выдаётся ошибка: : error A2071: initializer magnitude too large for specified size При чём такая ошибка появляется даже в том случае, если выражение содержит другие символы через пробел: qqqq EQU 1234567890123456789012390 dfdg Это объясняется действиями в пункте 1, когда ML анализирует лексемы. Кроме того, если числовая лексема не соответствует правилам определения чисел в ML, то есть в середине числа появляется символ A-Z, либо другие символы, не входящие в разряд разделителей – то такая лексема порождает ошибку, даже если она содержит число большее dword диапазона. |
4. Иначе – это строковая макропеременная.
Теперь попробуйте самостоятельно определить тип макроопределения:
qqqq EQU 0x123234
qqqq EQU 123234h
qqqq EQU 012323
qqqq EQU 0.123234
qqqq EQU 123234 342
qqqq EQU 4294967296
В данном примере только второй и третий вариант – макроконстанта, остальные – текстовые макро. Последний вариант таким не является, так как превышает диапазон значений для dword.
Замете, что поскольку препроцессор в правой части выделяет корректные выражения, правая часть не может состоять из недопустимых символов. Но при этом она может состоять из директивы определения литерала: «<>» – угловых кавычек.
Директива <текст> – определяет литерал, таким образом, указывая препроцессору ML, что он должен воспринимать нечто как строку символов. При этом сами «<>» – в строку не попадают. Директива <> – является единственной директивой для препроцессора ML, которая определяет литералы.
Именно по этой причине, все виды кавычек – двойные, одинарные, – вот такие одинарные, воспринимаются как простые символы, и как следствие проходят к значениям параметров макро. То есть, например: MyMacro “Привет, это строка в двойных кавычках” MyMacro ‘Привет, это строка в одинарных кавычках' MyMacro Привет, это строка в специальных кавычках MyMacro "Привет, это строка"'И это' И замете, что во всех случаях кавычки так же попадают в значения формального параметра макро. Вы можете использовать этот факт, например, для того, чтобы менять поведение макро, в зависимости от типа кавычек обрамляющих строку. |
Кроме директивы, определяющей литерал, препроцессор ML имеет свой ESC-символ (символ отмены). В отличие от С этот символ – «!». Он отменяет действие других символов (<, >, ", ', %, ; , а так же символ запятой), которые могут иметь функциональность в том, или ином выражении. Если вы хотите получить «!», вы должны использовать последовательность «!!».
К сожалению, не обходится без проблем и с символом отмены «!». Восстановить точный алгоритм работы мне не удалось. Единственное, что возможно – это привести несколько примеров с непонятными эффектами при его использовании:
literal EQU ;; Пустая строка
;; Ошибка –
;;: error A2045: missing angle bracket or brace in literal
literal EQU
;; Один символ «!»
literal EQU
;; Не имеют эффекта
literal EQU <Привет!" fgd!">
literal EQU <Привет" fgd">
;; Один символ «>»
literal EQU > ;; literal = «>»
literal EQU <Текст!!!>> ;; literal = «Текст>»
;; Хотя при вызове макро, «!» ведёт себя нормально
;; а так же он ведёт себя нормально в директиве TEXTEQU
Char <Текст!>>
Вывод – не пользуйтесь директивой EQU для определения литералов, для этого есть другая директива – TEXTEQU.
Для директивы TEXTEQU алгоритм несколько отличен от алгоритма EQU, так как в TEXTEQU обрабатывается правое выражение на наличие символа %. То есть вы можете определить этот код:
literal TEXTEQU %FunMacro()
Или
literal TEXTEQU %(10-5)*30 ;; literal = “150”
На самом деле как вы видите, внутренняя работа TEXTEQU значительно отличается от EQU <>. Видимо по этому разработчики ML решили её ввести.
В руководстве MASM32 написано: -------------------------------------------------------------------------------------------- The TEXTEQU directive acts like the EQU directive with text equates but performs macro substitution at assembly and does not require angle brackets. The TEXTEQU directive will also resolve the value of an expression preceded by a percent sign (%). The EQU directive does not perform macro substitution or expression evaluation for strings. -------------------------------------------------------------------------------------------- Теперь вы должны понимать, что это не совсем так. Является ли это ошибкой разработчиков ML? Видимо да. В частности EQU не должна была переводить в статус переменных литералов определения типа: NOLITERAL EQU db И конструкция ниже должна была бы вызывать ошибку: literal EQU db literal EQU dw Но ошибка не появляется, более того значение literal меняется на dw |
В заключении к этому пункту, вы должны осознать, что тип определений невозможно изменить. То есть переменная не может стать целочисленной константой:
literal EQU string
literal EQU 123 ;; Это текстовой макро
Второе переопределение символа literal, не изменит его тип на тип целочисленной константы.
Думаю, у Вас возник вопрос: – Что такое? Недокументированные возможности MASM? У меня есть веские основания считать это ничем иным, как ошибкой разработчиков. Давайте предположим, что все макропеременные хранятся компилятором в памяти в виде массива структур. Не вдаваясь в подробности, пусть эта структура будет такая: macrodefine struct type dd ? ;; тип макроконстанты value dd ? ends Как видно из структуры, значение макроконстанты может быть только dword'ом. Если это строка, то в поле value может быть записан указатель на строку (например, ASCIIZ). Поле type может принимать только два значения, которое описывает тип value: либо value – содержит числовое значение макропеременной (константы). Если определяется числовая константа то, вызывается одна функция (назовём её setmacrodefine_val()), которая добавляет в таблицу макроконстанту. Это конечно предположение. И в действительности всё может быть ещё проще или ещё сложнее. Однако вероятность того, что свойства макропеременных хранятся именно подобным образом близка к единице. Теперь если вы немного подумаете, то поймёте: string EQU string TEXTEQU string ;; Строковая макропеременная string EQU string ;; Должна была быть константой Последний случай записывается в таблицу, как строковая макропеременная по той простой причине, что string не может быть записано в поле value, а поле type не имеет специального значения, чтобы указать, что value – это константный указатель на строку (помните C++?). В конце концов, совершенно не важно угадал ли автор причину, или нет. Важно другое – что ошибка достаточно явная. А, кроме того, так и не была исправлена до сих пор (версия 7.0). Зато теперь вы сможет с пониманием отнестись к таким неожиданным эффектам. Видимо разработчики не задумываются о том, что кто-то будет использовать определения MASM, иначе, нежели это написано в руководстве. И кому-то взбредёт в голову проверить, а можно ли переопределить EQU. А подумайте, к каким бы серьёзным неуловимым ошибкам произвела бы эта халатность, если бы на MASM писали сложные приложения. Но как видно их никто не пишет. |
Свои особенности имеют так же целочисленные выражения с оператором «=». В таких выражениях перед их выполнением осуществляется полная замена всех макроконстант, макропеременных на их значения, и вызов всех макрофункций.
Как вы думаете, что будет в следующем примере:
literal EQU Something
literal = 1234
Варианты ответа:
- Произойдёт ошибка переопределения константы.
- literal = 1234.
Второй вариант ответа мы должны откинуть сразу, потому что в этом пункте чётко определили, что данное переопределение невозможно. Первый вариант ответа больше похож на правду.… Однако не соответствует истине. Что же произошло? А произошло следующее:
- Препроцессор нашёл лексемы «literal» и «1234».
- Обнаружил, что «literal» является текстовым макро, и именно поэтому выполнил замену лексемы «literal» на её строковое значение.
- Проанализировал строку: «Something = 1234».
Этот факт может быть легко доказан, следующим тестом:
literal EQU Something
literal = 1234
%echo @CatStr(%Something)
============================
Вывод:
1234
Если вас сбил с толку этот пример, не отчаивайтесь. Всё дело в том, что препроцессор ML в разных выражениях по-разному заменяет макропеременные. Вот об этом мы и поговорим в следующем пункте.
А пока подумайте, что должно случится в этом примере:
num EQU number
num EQU 123
num = 1234
На этом можно было бы закончить данный пункт, если бы не одна особенность использования строк в вызове макро. А точнее приоритет анализа кавычек и директивы определения литерала <>. Не смотря на описанный выше алгоритм поведения макро, оказывается, что препроцессор при вызове макро выполняет определение литерала в кавычках, но что самое интересное, как было отмечено, выше сами кавычки попадают в строку. Если вам нужно передать макро одиночную кавычку вы должны воспользоваться символом отмены «!». Однако самое неприятное таится в том, что символы «<>» и кавычки конкурируют между собой в определениях строк. Например, попробуйте сказать, что должно было бы получиться в этом случае:
%echo @CatStr(<Раз">,<"Два>)
OUT:
Раз">,<"Два
А можно было бы подумать, что ML должен принять операторы <> и запятую. Данное место – источник многих сложно обнаруживаемых ошибок. Например:
FORC char,
m$__charcode = \
@InStr (1,<@ABCDEFGHIJKLMNOPQRSTUVWXYZ>,
…
Если в строке попадается символ кавычки, а макропеременная char заменяется на значение кавычки, имеем:
m$__charcode = @InStr (1,<@ABCDEFGHIJKLMNOPQRSTUVWXYZ>,<”>)
В этом случае мы получаем ошибку:
missing single or double quotation mark in string
Так и должно быть, потому что кавычки имеют высший приоритет анализа, чем оператор <>. Более того, угловые кавычки <> имеют самый низкий приоритет по отношению ко всем спец. символам, что согласуется с MASM Reference. Посмотрите на Дополнение к статье: пункт 3.a.i, который подозрительно выделен «жирным». В частности, следующее выражение, которое работает без проблем:
TEXT TEXTEQU <"> ;; Это работает?
TEXT TEXTEQU <;> ;; И это???
Появляется закономерный вопрос: для чего символ отмены «!»?
Данный пример демонстрирует скрытые глубины анализатора ML. А точнее его архитектурное несовершенство. Так как выражения с TEXTEQU как видно обрабатываются отдельной функцией, которая проверяет в первую очередь наличие угловых скобок «<>». Все другие выражения ML обрабатываются другой стандартной функцией, которая была написана задолго до появления TEXTEQU.
Замечательная наука всем программистам, которая демонстрирует, во что выливается халатность архитектора при дальнейших попытках расширения продукта. |
Зато благодаря TEXTEQU пример с поиском символа в строке имеет решение:
m$__char TEXTEQU
m$__charcode = \
@InStr (1,<@ABCDEFGHIJKLMNOPQRSTUVWXYZ>,%m$__char)
Единственно, отчего не может помочь данный код – это от вылавливания в строке символов «> или <». Для этого можно использовать специальную проверку в условных блоках на наличие символа «>», но при этом придётся отказаться от микроблока FORC.