Низкоуровневое программирование для Дzenствующих
Вид материала | Документы |
- Низкоуровневое программирование, 108.99kb.
- Курс является базовым как для изучения других математических дисциплин, так и для более, 36.89kb.
- 1 Обобщенное программирование. Обобщенное программирование это еще одна парадигма программирования,, 55.18kb.
- Введение в линейное программирование линейное программирование (ЛП), 139.72kb.
- Учебно-методический комплекс для студентов заочного обучения специальности Прикладная, 63.23kb.
- Аттестационное тестирование в сфере профессионального образования, 72.49kb.
- Лекции по дисциплине «Социальное моделирование и программирование», 44.69kb.
- Программа дисциплины Линейное программирование Семестр, 17.93kb.
- Программа дисциплины "Программирование" для направления, 488.76kb.
- Рабочая программа по дисциплине Программирование на языке высокого уровня для специальности, 182.97kb.
III.5. Вычисление рекурсивных выражений
Теперь, когда мы рассмотрели правила анализа и вычисления выражений в MASM, остаётся раскрыть важный вопрос: «Как происходит анализ выражений, если они состоят из других выражений?».
Обычно это называется короче: вложенные выражения.
Вложенное выражение – это такое выражение, элементы которого сами являются выражениями, которые так же могут иметь вложенность. |
Замороченное определение, похожее на «Иди туда, не знаю куда, возьми то, не знаю что» – пример старинной народной русской рекурсии, которая так часто встречается в нашей жизни. :)) |
Например, вызов макрофункции при вызове макро – это вложенное выражение:
MyMacro FunMacro(Мой парамерт)
;;Или это:
%echo FunMacro(Мой параметр)
;;Или это:
MyMacro FunMacro(Fun2(Привет))
Вложенность характеризуется параметром количества уровней вложенности. В недавнем примере уровень вложенности был равен двум. При чём вызов Fun2() можно называть выражением низшего уровня вложенности, а вызов макро MyMacro – выражением верхнего уровня.
После анализа выражений, и получения их многоуровневой структуры вложенности, препроцессор начинает вычислять результат выражения самого низшего уровня. Потом подставляет его результат в выражение следующего уровня, и так далее.
Например, для случая:
Fun2 macro param
exitm
endm
FunMacro(Fun2(%(12+34)))
Порядок вычислений такой:
- %(12+34) = 46
- Fun2(46)
- FunMacro(MyCount = 46)
- Результат выполнения FunMacro(MyCount = 46)
А иначе препроцессор не смог бы. Если бы он начал вычисления выражений с верхнего уровня, то это то же самое, как если бы он попытался выполнить народную русскую рекурсию: «Пойди туда, не знаю куда…, вычисли то, не знаю что» или FunMacro(???) |
То есть: Вложенные выражения вычисляются последовательно от низшего уровня к верхнему, и результаты вычисления каждого уровня становятся материалом для выражений следующего уровня.
Это правило называется рекурсивным вычислением выражений. Оно используется везде, кроме мест вычисления значений макропараметров при вызове макро (как макросов, так и макрофункций). В этом случае действует правило: результат вложенного выражения присваивается макропараметру и не анализируется повторно. Это значит, что в данном примере:
myvar EQU <123>
MyMacro macro param1,param2,param3
echo param1
endm
FunMacro macro param:VARARG
exitm
endm
MyMacro FunMacro(var,@CatStr(<%>,myvar),var4)
вывод будет таким:
var,myvar,var4
То есть препроцессор не будет снова вычислять выражение для второго макропараметра функции FunMacro(). Если бы он сделал это, то тогда вывод был бы таким, как в этом случае:
%echo FunMacro(var,@CatStr(<%>,myvar),var4)
Вывод:
var,123,var4
Теперь, когда вы знаете все тонкости вычисления выражений в MASM, настало время рассмотреть Встроенные макрофункции и директивы, которые участвуют в этих выражениях.
III.6. Встроенные макрофункции и директивы
Несмотря на то, что этот пункт не касается самих макросов в MASM, он необходим, для того, чтобы строить макросы, и манипулировать выражениями, возникающими внутри макросов.
MASM обладает несколькими встроенными макрофункциями, макропеременными и макроконстантами, которые работают так, как если бы они были макро, определённые вами. Вот список этих предопределений:
Имя макроопределения, его тип | Описание |
Определения Даты и Времени | |
@Date, текстовое макроопределение (не макрофункция) | Возвращает строку вида MM/ДД/ГГ Где: MM – месяц, две цифры ДД – день, две цифры ГГ – год, две цифры |
@Time, текстовое макроопределение (не макрофункция) | Возвращает текущее время в 24-х часовом формате вида ЧЧ:ММ:СС ЧЧ – часы, два числа ММ – минуты, два числа СС – секунды, два числа |
Информация об окружении | |
@Cpu, числовая макроконстанта | Битовая маска, определяющая режим работы процессора. Никакой информации о полях этой маски нет. |
@Environ(env), макрофункция | Возвращает строковое значение переменной среды окружения. Например: %echo @Environ(TEMP) Вывод: F:\Temp\asm |
@Interface, целочисленная макроконстанта | Информация о языковых параметрах вызова. |
@Version, строковая макроконстанта | Возвращает версию ML. Например: %echo Version = @Version Вывод: Version = 614Или 615 в MASM 6.15 |
Информация о файле | |
@FileCur, строковая макропеременная | Возвращает имя файла и путь к нему (если есть), так как был подан этот файл в командной строке компилятору ML. Пример: %echo FileCur = @FileCur Вывод: FileCur = .\start.asm |
@FileName, строковая макропеременная | Возвращает имя файла, без его расширения. То есть для модуля start.asm: %echo FileName = @FileName Вывод:FileName = START |
@Line, целочисленная макроконстанта | Возвращает номер текущей строки в файле. Пример: %echo Line = @CatStr(%@Line) Вывод:Line = 31 |
Строковые макрофункции | |
@CatStr( string1 [[, string2...]] ), макрофункция | Возвращает строку, созданную объединением строк параметров функции. Пример: %echo @CatStr( Вывод:Myvar |
@InStr( [[position]], string1, string2 ), макрофункция | Возвращает позицию вхождения строки string2 в строку string1. Если параметр position определён, тогда поиск начинается именно с этой позиции. Отсчёт позиции начинается с единицы. В случае, если вхождение не найдено макрофункция возвращает значение 0. Параметр position должен быть целым числом больше нуля, но не равным нулю. Пример: %echo @InStr(1,asdfg,s) Вывод:02 |
@SizeStr( string ) макрофункция | Возвращает число, характеризующее длину строки, или, что тоже самое количество символов в строке. Функция возвращает число, однако, поскольку это макрофункция то тип возвращаемого значения – строка. |
@SubStr( string, position [[, length]] ) макрофункция | Возвращает подстроку строки string, начиная с позиции, указанной в параметре position (отсчёт начинается с 1). Если необязательный параметр length задан, он ограничивает размер возвращаемой строки. Параметр length не может быть меньше нуля, и не может быть строкой. Пример: %echo @SubStr(1234567890,2) %echo @SubStr(1234567890,1,5) Вывод: 234567890 12345 |
Информация о сегментах | |
@code, строковая макропеременная | Возвращает имя сегмента кода. |
@data, строковая макропеременная | Возвращает модель памяти. Пример: %echo data = @data Вывод:data = FLAT |
@fardata?, строковая макропеременная | Равен имени сегмента FARDATA? |
@WordSize, численная константа | Содержит размер слова в байтах. Для 16-bits – 2. Для 32-bits – 4. |
@CodeSize, численная константа | Содержит идентификатор типа памяти. 0 – TINY, SMALL, COMPACT, FLAT. 1 – MEDIUM, LARGE, HUGE |
@Model, численная константа | 1 – TINY 2 – SMALL 3 – COMPACT 4 – MEDIUM 5 – LARGE 6 – HUGE 7 – FLAT |
@CurSeg, строковая макропеременная | Хранит имя текущего сегмента. |
@fardata, @stack, строковая макропеременная | Содержат соответствующие имена сегментов |
Кроме знания макрофункций, нам так же понадобятся знания о блоках ветвлений или просто IF блоках. Эти блоки позволяют исполнять тот или иной участок исходного кода в зависимости от того, выполняется какое-либо условие или нет. Часто это называют «Условным ассемблированием (компиляцией)», однако для MASM это нечто большее, нежели простое управление компилятором, так как, вы уже поняли, мы имеем дело, как с кодом машины, так и с макрокодом, который вычисляется и живёт только во время компиляции.
Условный блок в MASM имеет следующий общий вид:
[IFDIRECTIVE] условие
...
[ELSEDIRECTIVE] условие
...
ELSE
...
ENDIF
Если выражение «Условие» равно истине, то выполняется блок кода, идущий после условной директивы, иначе управление передаётся на следующий оператор за блоком. [IFDIRECTIVE]/[ELSEDIRECTIVE] – могут быть той или иной директивой условия. Стандартные директивы IF/ELSEIF/ELSE требуют, чтобы выражение, стоящее при них, было целочисленным. Если вам необходимо проверять другие условия, то для этого в MASM предусмотрены специальные директивы.
Список [IFDIRECTIVE]/[ELSEDIRECTIVE]:
Блок | Условие выполнения блока |
IF выражение ELSEIF выражение ELSE | если выражение равно истине |
IF1 ELSEIF1 | если ассемблер выполняет первый проход |
IF2 ELSEIF2 | если ассемблер выполняет второй проход (устарело) |
IFE выражение ELSEIFE выражение | если выражение равно нулю |
IFDEF выражение ELSEIFDEF выражение | если идентификатор, который является результатом выражения, определен. Идентификатором может быть макро, макропеременная, переменная, макроконстанта, любой другой идентификатор. При помощи этой директивы, можно проверить была ли определена та или иная переменная, макро, константа. IFDEF PROGRAM_IMAGE_BASE ;; Выполняем действия если PROGRAM_IMAGE_BASE ;; определена ELSE … |
IFNDEF выражение ELSEIFNDEF выражение | если идентификатор не определён. |
IFB строка ELSEIFB строка | если строка пустая. Строка считается пустой, если её длинна равна нулю, либо она содержит одни пробелы. С помощью этой директивы можно определяет присутствие/отсутствие необязательных макропараметров. MyMacro macro param1,param2 IFB ;; Если макропараметр не определён, ;; генерируем ошибку .ERR <Не определён параметр param2> |
IFNB строка ELSEIFNB строка | если строка не пуста. |
IFDIF str1,str2 ELSEIFDIF str1,str2 | если строки различны. IFDIF echo Этот код выполнится echo потому что строки различны ENDIF |
IFDIFI str1,str2 ELSEIFDIFI str1,str2 | если строки различны (без учёта различий в регистре букв). IFDIF echo Этот код не выполнится echo потому что строки Одинаковы ENDIF |
IFIDN str1,str2 ELSEIFIDN str1,str2 | если строки одинаковы. IFDIF echo Этот код не выполнится echo потому что строки Различны ENDIF |
IFIDN str1,str2 ELSEIFIDN str1,str2 | если строки одинаковы (без учёта различий в регистре букв). IFDIF echo Этот код выполнится echo потому что строки Одинаковы ENDIF |
На протяжении всей статьи я часто пользовался следующей директивой, которая позволяет выводить текст на консоль во время компиляции. Эта директива echo. Как мы узнаем позже, она оказалось просто незаменимой при проектировании макро.
Вы уже, наверное, убедились насколько полезна эта директива, позволяющая заглянуть, а что именно происходит в недрах макроса, или посмотреть значения макропеременных.
Кроме этого, есть ещё одна группа директив, без которой мы не сможем обойтись. Не сможем потому, что макрофункции, или макросы, которые мы собираемся создавать должны быть слегка умными, иначе говоря, иметь «защиту от дурака».
Если кто-то неправильно использует макрос, то код, полученный таким образом может быть неправильным с точки зрения программиста, но не вызовет подозрений у компилятора. Поэтому макро не просто должен завершится, а и каким-то образом остановить компиляцию программы с выдачей сообщения об ошибке.
Именно для этого и существует простой набор директив условной генерации ошибки. Действуют они подобно условным блокам и директиве echo. Пример безусловной генерации ошибки:
.ERR <Ошибочка вышла, гражданин начальник>
Условная генерация ошибки, имеют ту же форму, что и IFDIRECTIVE в таблице выше, однако последним дополнительным параметром является строка сообщения. Например:
.ERRE выражение,<ошибка, если выражение равно нулю>
.ERRNZ выражение,<ошибка, если выражение не равно нулю>
.ERRDEF id,<ошибка, если id определен>
.ERRB строка,<ошибка, если строка пуста>
.ERRNB строка,<ошибка, если строка не пуста>
.ERRDIF str1,str2,<ошибка, если строки различны>
.ERRDIFI str1,str2,<ошибка, если строки различны
(без учёта регистра)>
.ERRIDN str1,str2,<ошибка, если строки одинаковы>
.ERRIDNI str1,str2,<ошибка, если строки одинаковы
(без учёта регистра)>
123>