Макросы

Тема в разделе "WASM.ARTICLES", создана пользователем Mikl___, 25 янв 2017.

  1. Edmond

    Edmond узник замка IF THEN ELSE

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    203
    Адрес:
    WASM.RU
    Конечно же, использование макро было бы бесполезным, если бы макро не имел формальных параметров. При вызове макро, препроцессор заменяет все имена формальных параметров их непосредственными значениями в теле макроопределения. Список формальных параметров разделяется запятой, и может иметь вид:
    MyMacro macro param0, param1:REQ, param2 := <0>,param3:VARARG
    Здесь: Param0 – пример определения параметра.
    Param1:REQ – ключевое слово REQ указывает на то, что этот параметр обязательный. То есть, если он не будет указан, вы получите ошибку этапа компиляции.
    Param2:=<0> – пример параметра, который имеет значение по умолчанию. То есть если этот параметр не будет указан при вызове макро, он будет равен этому значению.
    Заметьте, что при вызове макро параметр может быть не определён:

    MyMacro param1,,param3
    Значение второго параметра неопределенно.
    Param3:vararg – становится именем параметра, который воспринимает всё остальное как строку. При этом запятые между параметрами так же попадают в строку, а значит число параметров макроса в принципе неограниченно.
    Ограничениям являются особенности архитектуры компилятора. Так, например, компилятор имеет ограничение на длину логической строки, которая равна 512 байтам.
    Конечно же, после параметра с директивой vararg не возможно объявить другие параметры.
    Обратите внимание, что если при определении формального параметра в макро нет директивы – он считается необязательным параметром. Более подробно о вызове макро и значении параметров я расскажу далее.
    Пример:
    Так что же происходит с формальными параметрами?
    Посмотрите, как работает препроцессор ML:

    MyMacro macro param1,param2
    mov eax, param1
    mov ebx, param2
    endm
    MyMacro var, 123
    1. Препроцессор берёт текст внутри макро, и заменяет в нём все слова param1, param2, на их значения:
      mov eax, var
      mov ebx, 123
    2. Полученный текст вставляет на место вызова макро, и передаёт компилятору.
    Вот интересно, а что будет если:
    MyMacro macro param1,param2
    MyMacro2 macro param1
    mov eax, param1
    mov ebx, param2
    endm

    endm
    MyMacro var, 123
    Можно различать два вида макро – макропроцедуры и макрофункции.
    В официальном руководстве MASM различается четыре основных вида макро.
    1. Text macros – текстовый макрос
    2. Macro procedures – макро-процедура
    3. Repeat blocks – блок повторения
    4. Macro functions – макро-функция
    Однако автор считает, что разделение макро на два вида – лучше систематизирует материал, и отражает суть темы.
    Макрофункции в отличие от макропроцедур могут возвращать результат, и получают список формальных параметров в скобках, подобно функциям в С. Например:
    mov eax,@GetModuleHandle()
    Заметьте, что к макрофункции невозможно обратится как к макро, вы всегда должны заключать формальные параметры макрофункции между «()», иначе MASM не будет распознавать её как макрофункцию:
    mov eax,@GetModuleHandle
    error A2148: invalid symbol type in expression : @GetModuleHandle
    Препроцессор MASM анализирует текст макроопределения на наличие директивы exitm, и помечает макрос как макрофункцию.
    Ключевое слово exitm <retval>, аналогично оператору return в C++, выполнение макро заканчивается, и возвращается необязательный параметр retval. Этот параметр – строка, которую должен вернуть макрос.
    Таким образом, окончательно будем считать, что макро, не имеющие в себе вызова директивы exitm – это макропроцедуры, а макро, которые имеют exitm – это макрофункции.
    Код (ASM):
    1. ;#######################################################
    2.     @GetModuleHandle   macro
    3.     Invoke GetModuleHandle,0
    4.          exitm
    5.          endm
    6.        .code
    7.     ; Это макрофункция так нельзя
    8.     @GetModuleHandle ;;– ошибка
    9.     ; Так можно
    10.     @GetModuleHandle()
    11.     ;########################################################
    12.     @GetModuleHandle   macro
    13.     Invoke GetModuleHandle,0
    14.          endm
    15.        .code
    16.     ; Это макрос. Так правильно
    17.     @GetModuleHandle
    18.     ; Так можно, но всё равно это вызывает ошибку ?
    19.     ; warning A4006: too many arguments in macro call
    20.     @GetModuleHandle()
    21.     ; Это макро, а не макрофункция так нельзя!!!
    22.       mov eax,@GetModuleHandle
    23.     ; И так нельзя
    24.       mov eax,@GetModuleHandle()
    Что касается директивы endm, которая заканчивает каждое макроопределение, в руководстве написано, что при помощи неё так же можно указать возвращаемый параметр:
    endm <retvalue>
    Однако на практике это не так. ? Очень странно, хотя об этом чётко написано в руководстве.
    Заметьте, что макропроцедура может быть вызвана только в начале строки:
    @GetModuleHandle
    ;; Но не так:
    mov eax,@MyMacro
    Макрофункция может быть вызвана в любых выражениях:
    ;; Так:
    mov eax,@GetModuleHandle()
    ;; И так:
    @FunMacro()
    ;; И так:
    @GetModuleHandle() EQU eax

    III.1. Функционирование макросов

    Чтобы строить макросы, важно понимать, как они работают, и как их обрабатывает MASM. Давайте рассмотрим типичный макро, и этапы его обработки.
    MyMacro macro param1,param2,param3:VARARG
    echo param1
    echo param2
    echo param3
    endm
    MyMacro Параметр 1, Параметр 2, Параметр 3, Параметр 4
    ;; Вывод -=-=-=-=-=-=-=-=
    Параметр 1
    Параметр 2
    Параметр 3,Параметр 4
    1. Компилятор встречает лексему MyMacro
    2. Он проверяет, содержится ли эта лексема в словаре ключевых слов
    3. Если нет, то он проверяет, содержится ли эта лексема в списке макросов
    4. Если да, он передаёт текст, содержащийся в макро препроцессору. Препроцессор заменяет все вхождения формальных параметров в этом тексте на их значения. В данном случае мы имеем:
      echo Параметр 1
      echo Параметр 2
      echo Параметр 3,Параметр 4
    5. Препроцессор возвращает компилятору обработанный текст, который после компилируется.
    Обратите внимание на пункт 4 и 5. Они ключевые. Очень часто при работе с макроопределениями появляются ошибки из-за неверного понимания порядка генерирования макро текста. Например:
    PROGRAM_IMAGE_BASE EQU 400000h
    FunMacro macro
    exitm <Параметр 3,параметр 4>
    endm
    MyMacro macro param1,param2,param3:VARARG
    echo param1
    echo param2
    echo param3
    endm
    MyMacro PROGRAM_IMAGE_BASE, FunMacro(),Параметр 5
    А теперь самостоятельно опишите порядок действий компилятора при вызове этого макро. Запишите его себе куда-нибудь, так чтобы сравнить, и смотрите на вывод:
    PROGRAM_IMAGE_BASE
    Параметр 3, Параметр 4
    Параметр 5
    Прежде чем объяснять действительный порядок, я оговорюсь, что директива echo никогда не обрабатывает определённые константы, такие как PROGRAM_IMAGE_BASE.
    Это утверждение справедливо даже тогда, когда перед директивой echo стоит оператор %, который может раскрывать только текстовые макроопределения. То есть выражение:
    echo FunMacro()
    Даст результат:
    FunMacro()
    Теперь, когда мы немного порассуждали можно привести тот текст, который генерируется из макро:
    echo PROGRAM_IMAGE_BASE
    echo Параметр 3, Параметр 4
    echo Параметр 5
    Это означает следующее:
    1. При вызове макро, значение формальных параметров воспринимается как текст, и передаётся в макро как строка.
    2. Исключение составляют лишь макрофункции, результат выполнения которых вычисляется и присваивается значению параметра.
    Специальный оператор % заставляет ассемблер вычислять текстовую строку, следующую за ним, и только потом подставлять в правое выражение. Например, если мы перепишем макровызов так:
    MyMacro %PROGRAM_IMAGE_BASE, FunMacro,Параметр 5
    То получим вывод:
    4194304 ;; Значение PROGRAM_IMAGE_BASE
    Параметр 3, Параметр 4
    Параметр 5
    Давайте рассмотрим ещё один пример, который хорошо показывает, как работает макро. Например, вы определили макропроцедуру (именно его, а не макрофункцию). То когда вы пишите такое:
    @Macro что-то, что придёт вам в голову [символ возврата каретки]
    Что делает препроцессор ML:
    1. Считывает всю строку до символа возврата каретки;
    2. Смотрит, как вы определили параметры в макро;
    3. Сканирует строку на наличие символа «,» или «<», «>»;
     
    Последнее редактирование модератором: 31 янв 2017
    rococo795 нравится это.
  2. Edmond

    Edmond узник замка IF THEN ELSE

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    203
    Адрес:
    WASM.RU
    Вам может показаться странным, но препроцессору всё равно, какие символы идут во время вызова макро. То есть вы можете вызвать макро так:
    @MyMacro Привет, это кириллица в файле,\
    и ML не будет на неё ругаться
    или
    @MyMacro !@#$%^&*(){}[]
    Посмотрите как СИльно (от буквы ) будет выглядеть макро в MASM:
    MyMacro{Это что С++?}
    MyMacro[Нет, это MASM]
    4. Назначает формальным параметрам (любого типа, кроме VARARG) макро участки строк, которые были определены разделителями запятыми (предварительно очистив от хвостовых и начальных пробелов, если только строка не была определена в угловых кавычках <>);
    5. Если макро содержит формальный параметр типа VARARG, то ML сперва инициализирует значениями (согласно пункту 4) обычные формальные параметры, и только потом назначает параметру типа VARARG (который может быть только один в конце списка параметров) всю строку до конца.
    -= Обратите внимание =-
    Если вы пишите макровызов как
    @Macro Param1 , Param2
    То значение параметров будут:
    param1 = «Param1»
    param2 = «Param2»
    Если вы хотите передать сами значения строк, то должны заключит их в угловые кавычки:
    @Macro < Param1 >,< Param2 >
    6. Препроцессор разрешает все вызовы макрофункций, если они есть в лексемах параметра, и присваивает их результат соответствующему параметру. Если лексему в строке параметра предваряет символ %, то он вычисляет её значение до того, как передаст строку внутрь макро.
    -= Обратите внимание =-
    Благодаря именно такому порядку:
    1. Разделение строки на макропараметры
    2. Поиск и Вызов макрофункций в значениях макропараметров
    3. Присвоение результатов соответствующему макропараметру
    в следующем случае:
    MyMacro macro param1,param2,param3
    echo param1
    endm
    --------------------------------------
    FunMacro macro param:VARARG
    exitm param
    endm

    MyMacro FunMacro(param1, param2, param3)

    OUT:
    param1, param2, param3
    строка, возращаемая макрофункцией присваивается параметру param1, а не param2, param3
    Теперь вы в состоянии объяснить следующую ситуацию:
    MyMacro macro

    endm

    MyMacro()
    Предупреждение при компиляции:
    : warning A4006: too many arguments in macro call
    Как нужно было бы изменить этот макро (именно макро, а не макрофункцию), чтобы предупреждение не выдавалось? А почему оно происходит?

    Если вы с лёгкостью ответили на этот вопрос, значит, материал усвоен, иначе советую ещё раз прочитать его, и ответить на следующий вопрос.

    Как должен понять компилятор следующий код:
    MyMacro macro param1
    param1
    endm

    MyMacro = 2
    Естественно отвечать на этот вопрос вы должны без помощи компилятора (то есть проверить компиляцией). Если вы не можете ответить на этот вопрос, или неуверенны в верности ответа, я поменяю задание:
    MyMacro macro param1
    echo param1
    endm

    MyMacro = 2
    Запустите его в ML. Если и теперь вы сомневаетесь – перечитайте этот пункт снова и снова, продолжая экспериментировать.
     
    Последнее редактирование модератором: 31 янв 2017
    rococo795 нравится это.
  3. Edmond

    Edmond узник замка IF THEN ELSE

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    203
    Адрес:
    WASM.RU

    Руководство по проектированию макросов в MASM32

    # Пролог


    Ну, вот и всё... Отпуск. Тишина. Блаженство. 00F ловит рыбу (наверно), а может и кое-что и на огороде ловит :). Охота... 00A занят турниром по "Го", а кто-то плескается на сказочных озёрах...
    Только что прошёл матч. Возбуждённые подростки изнутри раскачивают троллейбус, приводя в ужас прохожих и водителя. Они счастливы. Пусть всего лишь эти несколько минут, пока он не перевернётся, но они счастливы. Как мало нужно для нашего человека.
    А у меня отключили свет, и я сижу, медитируя перед тающей свечкой. И ничего не работает, окромя чего-то там, тихо жужжащего в голове.
    Сегодня пришло письмо: «Реальность», в котором один из хакеров, ужаснувшись IA-64, долго жаловался на такое вот положение. Интересно, а что было, когда в 01 году от начала эпохи UNIX, вдруг просто так появился 4004? Помню эту замечательную историю.
    Нет, мир изменился. Я чувствую это в коде, я чувствую это в API. (где-то это уже было? :)
    Вот в тот самый момент, когда в проводах погасла сила ЭДС, когда напряжение равно нулю, а рядом с тобой извечный огонь, сопровождавший человека с самого его рождения, может показаться, что время замерло, а прошлое вдруг смешалось с будущим. Именно в этот момент можно заглянуть в любой мир, даже самый несуществующий.
    Интересно, а что будет через сто, тысячу, две тысячи, да ещё больше лет. А вдруг человечество выживет, станет подобно богам, творящим миры. А что будет тогда? Coca-Cola? Нет. А программисты будут? А учёные? Какие желания будут у человека? Будут ли у него проблемы? А останется ли любовь, или хотя бы её подобие? А интересно, какой будет тогда ассемблер? А будет ли он… А будет ли WASM.RU? Или хотя бы память о нём? :) Ух!!!
    А свеча заканчивается, фитиль грустно падает, и постепенно из сумерек комнаты, возникает новый неизвестный мир… В котором ВСЁ это БУДЕТ.
    Edmond/HI-TECH

    В поисках утраченного

    I. Голос Хаоса

    I.1. Сайт DeVaida

    Сайт DeVaidа процветал. Более двадцати миллиардов жителей населяли эти сегменты, всё текло, и изменялась прямо под ним, а он подолгу смотрел в окно с высоты небесного аэрограда. Как приятно было осознавать себя одним из тех, живущих так высоко, чтобы позволить себе наплевать на директивы нейвира (Абстрактная вселенная). Сейчас он находился в роскошном вирте (подобие того, что мы бы назвали ЭВМ) с несколькими миллионами генерационных процессоров, и тихо наблюдал за взлом Cassape.
    Как и все креативные администраторы сайтов, DeVaida был не только специалистом в N-сетях, а и одним из ведущих архитекторов по UltraC. Этот умирающий язык абстракций всё ещё пользовался популярностью в узких кругах хакеров и крекеров, которые охотились за лакомыми кусочками информации, раскиданной в секциях нейвира. DeVaida был не просто «ещё одним из», а скорее именно тем, кого боялись и уважали. Его любимая фраза давно стала крылатой на форумах: «Знание это то, что вне директив». Правда, время I-атак уже прошло, он и теперь был хорошим специалистом по реверсингу Абстрактов. Но за последние несколько лет, утрачивал удовольствие даже от любимого дела, смутные мысли посещали его всё чаще и чаще. Он оглядывался в сторону LSAVE и думал о вечности. LSAVE – хранилище жизни на UltraC, обозначающее скопление используемых данных, в сети нейвира это был один единственный сайт, где хранились миллиарды разумов, некогда живых тел.
    MainFra как-то сказал: «В будущем человечеству не понадобятся библиотеки, и книги, потому что знания будет хранить разум». Если бы MainFra великий архитектор Formal знал, насколько зловеще воплотится его мысль – он бы не только промолчал, но и никогда не подумал её.
    Теперь генеративный B сервер под названием «LSAVE» хранил больше знаний, чем вообще их можно было бы представить. Эта «живомёртвая» библиотека разума приводила в трепет и вселяла страх в посетителей. Дом воспоминаний LSAVE, история мира LSAVE, БД программистов LSAVE, БД архитекторов LSAVE, БД психобольных, Зал умных мыслей. Зал смешных мыслей. LSAVE – был самым богатым сервером нейвира, продавая то, что когда-то тысячи лет назад человечество считало недосягаемым и сверхбесценным.
    На какой-то момент в комнате потемнело, и Cassape сдался, информация свободно устремилась по каналам из LSAVE. DeVaida повернулся, и молча наблюдал за проносившимися воспоминаниями Cassape. Когда-то, этот человек был одним из великим философов портала NICAF. DeVaida просматривал его детство, пытаясь уловить какую-то неуловимую нить, превратившую ребёнка в предсказателя. Cassape обрёл «покой мудрости» лишь двести лет назад , когда DeVaida уже учился на курсах генериолога абстрактных систем. Замечательное было время. Пару разработчиков из NSPACE разработали систему преобразования абстракций нового поколения с названием АО, и он помнил, как лихо разбил их в первом раунде дискуссии. Разработку замяли, и «первая в нейвире» генерационная система абстракции с половинным парадоксом сохранения архитектуры исчезла, так и не успев, окончательно появится. Этот миг торжества дал молодому DeVaida путь в Архон – объединение, занимавшееся креативом систем высшей сложности. «Будь себе Богом» – гласила великая фраза Cassape на шлюзе в портал Arhon.
    Там он и познакомился с тремя замечательными H-архитекторами. Один из них был специалистом в абстракте Граней, с ником Avenue. Другой замечательно соображал в F-сети, или как это называлось иначе: «старый мир», его манило на всё, что было связано с материей F-уровня, и не удивительно, что его третий за жизнь ник был: HideWate. Ну и наконец к всеобщему коктейлю добавлялся IA, бредивший всем, что выходило за рамки понимания обычного жителя нейвира.
    Их работа была более чем секретна, DeVaida корпел над генератором нового поколения (вспоминая с усладой про опыт NSPACE). Эта новая система генерации не смогла творить «из ничего», как хотели этого разработчики из NSPACE, зато при минимуме информации они создании две крупные RE-галактики на краю Синтальёна. Каждый раз, подходив к сегменту начальства DeVaida встречал глазами мутную табличку: «Будь себе Богом», и может быть из-за этого, а может и по иной причине, H-архитектор генерационных систем нейвира задумал невозможное.
    437 кб. и 7736Exб с начала эпохи UNIX на столе делегата нейвира лежал новый Аван-проект «Перекрёстной абстракции с измерением GH». Что думало себе начальство, когда позволило этот проект амбициозному DeVaida, а что думали его друзья по-оружию, уже неизвестно. Но рассказывают, что именно в работе над этим проектом, H-архитектор DeVaida посетил святая святых LSAVE. По коридорам Arhon говорили, что для этого он побывал в абстракте V – то есть в абстракции разума, или как это называли более научно – пространстве граней.
    Теперь и сам DeVaida не помнил, как закончилось дело с его детищем, но это уже и не волновало. Теперь он заработал достаточно, достаточно времени, чтобы создать свой сайт.
    Он часто посещал любимый форум своего дома по UltraC. На главной площади можно было бы встретить несколько знакомых лиц, и старых друзей.
    DeVaida почувствовал присутствие.
    – Я вижу, взлом LSAVE тебе просто доставляет удовольствие. Я ж не думаю, что мысли этого маразматика, – гость указал на Cassape, – имеют хоть какую-то ценность. Или ты до сих пор сожалеешь, что не встретился с ним?
    DeVaida мысленно развернул комнату лицом к себе:
    – Но что-то он рано достиг «тихой мудрости». Просто интересно узнать.
    – Узнать когда ты умрёшь? Ой, по тебе ещё не видно, успокойся.
    DeVaida улыбнулся:
    – А хорошо ж было нашим предшественникам. Их срок ведь был отмерен чётко.
    – По-моему это самый настоящий бред, сверх всего, нужно позвать IA, потому что я думаю, после его бреда, своего ты не захочешь.
    HideWate громогласно засмеялся, и уселся недалеко от хозяина сайта. HideWate всегда походил на высокомерного начальника, и всё его поведение хорошо диктовало тон такой игре: «Я знаю истину, и не мешайте мне со своими глупостями». Возможно поэтому он почти никогда не приходил один просто так. Какой в этом смысл? Никакого. Вот именно.
    – Так что за дело ко мне?
    HideWate не долго ожидал этот вопрос, и выдержав небольшую паузу, сказал:
    – У меня есть один ископаемый, требуется его достать.
    – Кто? А что снова в LSAVE?
    – Да, а тебя это смущает?
    – Ну..
    DeVaida готов был поверить, что над ним шутят, если бы не серьёзно надменный вид гостя.
    – Ну достать его не так просто, потому что он в F-пространстве. А потом есть одно но – этот ископаемый очень опасен, и нам нужны услуги крекера, который хоть чуть занимался «взломом мозгов».
    – Я им не занимался.
    – Да… А что ж ты делал в LSAVE? Или уже забыл?
    – Ну у меня не очень хорошо получалось.
    – А потому что не у кого лучше не получилось бы. Да не ломай свой нрав, а? Слушай этот тип куда более интересней твоего современника философа. Если я скажу, что его ник Нейромансер?
    Если бы HideWate не знал на чём можно подловить любопытство DeVaida, то вряд ли спешил навестить старого знакомого. Вот оно – его секретное оружие.
    Neiromanser был последним из программистов и первым из H-архитекроров, который, как рассказывалось в мануалах, открыл программирование на VASM – языке ассемблера V-абстракций. В то время как раз только появился первый генератор UltraС, и программирование, вместе со старыми науками окончательно умирало, отдавая мир в новые руки.
     
    Последнее редактирование модератором: 31 янв 2017
    rococo795 нравится это.
  4. Edmond

    Edmond узник замка IF THEN ELSE

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    203
    Адрес:
    WASM.RU
    Известность Neiromanser принесли его опыты над абстракцией граней, или разумом. Он добился поразительных результатов, в мутациях и декомпозиции сознания человека, осветив свой путь скандалами и великой наукой. Эти тайные исследования над святая святых так и не стали известны человечеству, а 833 kб от начала UNIX эпохи последний программист переселил проекцию своего разума в первый тогда в мире генерационный компьютер-планету, на чём его история обрывается.
    «Ну да, конечно» – подумал DeVaida, – «такой неандерталец будет хорошим экспонатом в LSAVE, интересно насколько сохранилась его абстракция разума…»
    (продолжение ожидается)​
    Edmond/ HI-TECH

    Руководство по проектированию макросов в MASM32

    (часть 1.2)


    Осознай Единое в Различном – и будь Богом.
    Автор

    III.2. Определение макро переменных и строк

    Я бы назвал следующее:
    Param = 0
    Constant EQU 123
    WASM EQU <One Wonderful Wonderful ASM>
    WASM_RU TEXTEQU <wasm.ru>
    макропеременными (с тем фактом, что переменная может иметь константный тип).
    В терминологии MASM:
    WASM EQU <One Wonderful Wonderful ASM>
    WASM_RU TEXTEQU <wasm.ru>
    ;; Такие определения называются текстовыми макро.
    ;; В этой статье вы встретите два варианта определений
    Потому что под термином «переменная» понимается:
    var dd 123
    Переменные являются частью программы, а макропеременные живут только на этапе компиляции. По сути, они есть более простым видом макроопределений, и поэтому их стоит понимать как специальные макро, которые так же раскрываются препроцессором.
    Макропеременная может иметь только три типа – целочисленная макропеременная INEGER4 (dword), целочисленная макроконстанта или текстовой макро (строковая макропеременная).
    Автор считает значительным упущением отсутствия возможности определять тип макропеременной. Это очень сильно ограничивает возможности макропрепроцессора. Но что поделать.
    При чём, в зависимости от вида определения макропеременной ML считает, что:
    Param = 0 ;; Param – это целочисленная макропеременная
    Constant EQU 123 ;; Макроконстанта
    ;; Текстовой макро (Макропеременная строкового типа)
    ;; (Это не так в руководстве MASM)
    Var EQU qwer
    ;; Текстовой макро (Макропеременная строкового типа)
    WASM EQU <One Wonderful Wonderful ASM>
    ;; Текстовой макро (Макропеременная строкового типа)
    WASM_RU TEXTEQU <wasm.ru>
    Как вы уже догадались, каждое макроопределение обладает своими свойствами и возможностями.
    1. Целочисленная макропеременная. Имеет тип INT (dword). Может участвовать во всех арифметических выражениях MASM. Как переменная она может изменять своё значение.
    2. Макроконстанта может иметь целочисленное значение. Её значение не может быть повторно изменено.
    3. Текстовой макро может быть любой строкой не более 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.
     
    Последнее редактирование модератором: 31 янв 2017
  5. Edmond

    Edmond узник замка IF THEN ELSE

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    203
    Адрес:
    WASM.RU
    Теперь вы должны понимать, что это не совсем так. Является ли это ошибкой разработчиков 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> ;; Строковая макропеременная
    string TEXTEQU string ;; Строковая макропеременная
    string EQU string ;; Должна была быть константой
    Последний случай записывается в таблицу, как строковая макропеременная по той простой причине, что string не может быть записано в поле value, а поле type не имеет специального значения, чтобы указать, что value – это константный указатель на строку (помните C++?).
    В конце концов, совершенно не важно угадал ли автор причину, или нет. Важно другое – что ошибка достаточно явная. А, кроме того, так и не была исправлена до сих пор (версия 7.0). Зато теперь вы сможет с пониманием отнестись к таким неожиданным эффектам.
    Видимо разработчики не задумываются о том, что кто-то будет использовать определения MASM, иначе, нежели это написано в руководстве. И кому-то взбредёт в голову проверить, а можно ли переопределить EQU.
    А подумайте, к каким бы серьёзным неуловимым ошибкам произвела бы эта халатность, если бы на MASM писали сложные приложения. Но как видно их никто не пишет.
    Свои особенности имеют так же целочисленные выражения с оператором «=». В таких выражениях перед их выполнением осуществляется полная замена всех макроконстант, макропеременных на их значения, и вызов всех макрофункций.
    Как вы думаете, что будет в следующем примере:
    literal EQU Something
    literal = 1234
    Варианты ответа:
    1. Произойдёт ошибка переопределения константы.
    2. literal = 1234.
    Второй вариант ответа мы должны откинуть сразу, потому что в этом пункте чётко определили, что данное переопределение невозможно. Первый вариант ответа больше похож на правду.… Однако не соответствует истине. Что же произошло? А произошло следующее:
    1. Препроцессор нашёл лексемы «literal» и «1234».
    2. Обнаружил, что «literal» является текстовым макро, и именно поэтому выполнил замену лексемы «literal» на её строковое значение.
    3. Проанализировал строку: «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,<str>
    m$__charcode = \
    @InStr (1,<@ABCDEFGHIJKLMNOPQRSTUVWXYZ>,<char>)
    Если в строке попадается символ кавычки, а макропеременная 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 <char>
    m$__charcode = \
    @InStr (1,<@ABCDEFGHIJKLMNOPQRSTUVWXYZ>,%m$__char)
    Единственно, отчего не может помочь данный код – это от вылавливания в строке символов «> или <». Для этого можно использовать специальную проверку в условных блоках на наличие символа «>», но при этом придётся отказаться от микроблока FORC.
     
    Последнее редактирование модератором: 1 фев 2017
  6. Edmond

    Edmond узник замка IF THEN ELSE

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    203
    Адрес:
    WASM.RU

    III.3. Обработка выражения в MASM

    MASM обрабатывает выражения в правой и левой части в зависимости от контекста. Там, где вам необходима предварительная обработка выражений, используется оператор «%». Он заставляет препроцессор ML сначала вычислить выражение после оператора % (то есть выражение в правой части относительно %), и только потом продолжить анализ всей строки. Например, если вы хотите, чтобы при вызове макро:
    num TEXTEQU <123>
    FunMacro num
    макропараметр был бы равен не строке «num», а значению текстового макро num, вы должны поставить оператор % перед num. Например:
    FunMacro %num
    ;;или
    FunMacro %(1+2*num)
    Но и с оператором % не всё гладко. Оказывается препроцессор ML, различает два (фактически три) вида выражений, в которых используется оператор %. Первый вид выражений – Арифметические:
    Все выражения, содержащие операторы +,-,*,\ а так же сдвиговые и битовые операции
    Строковые выражения:
    Все выражения результат вычисления которых – строка.
    Примеры:
    ;Арифметические выражения
    %(num shl 3)
    %num = 2134 shl 3 + 2*6
    ;Всё равно арифметическое выражение
    %(num shl 3 @CatStr(num))
    ;Строковое выражение
    %(@CatStr(num shl 3))
    ;Строковое выражение
    %PROGRAM IMAGE
    Так вот что интересно.
    В арифметических выражениях происходит полная замена правой части: вызовы макрофункций, значение макроконстант, макропеременных любых типов, как строковых, так и целочисленных. Так же в левой части выражения: замена строковых макропеременных, и вызов макрофункций.
    То есть:
    Левая часть = Правая часть
    (Вызвать все макрофункции, и заменить все строковые макропеременные) = (Вызвать все макрофункции, и заменить все строковые и целочисленные макропеременные и константы)
    В строковых выражениях происходит замена только строковых макропеременных (текстовых макро) (замете, что в ML нет строковых макроконстант). Это значит что в случае:
    %echo PROGRAM_IMAGE_BASE
    Появится: «PROGRAM_IMAGE_BASE», а не его числовое значение.
    Однако есть и третий частный случай, когда оператор % относится только к одному литералу:
    %literal
    В этом случае происходит полный комплекс подстановок:
    1. Вызываются макрофункции.
    2. Заменяются все макропеременные или макроконстанты.
    Например:
    FunMacro %literal
    Значение literal будет подставлено в вызов макро, в независимости от того, какой тип имеет literal.
    -= Внимание =-
    Выдержка из руководства MASM:
    -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    temp TEXTEQU %(SIZEOF array / LENGTHOF array)
    % ECHO Bytes per element: temp
    Note that you cannot get the same results simply by putting the % at the beginning of the first echo line, because % expands only text macros, not numeric equates or constant expressions.
    Следует так же отметить, что в выражениях с exitm оператор % работает точно так же, как с выражениями в TEXTEQU.

    III.4. Целочисленные выражения MASM

    Целочисленные, побитовые операции так же необходимы разработчику макроопределений. Они дают возможность скрыть обработку битовых полей, или вычисление сложных выражений. Например, как это сделано в макрофункции $$$MAKELANGID.
    $$$MAKELANGID macro p:REQ,s:REQ
    m$__langid = (s SHL 10) or p
    EXITM <m$__langid>
    endm
    Вы всегда должны помнить, что препроцессор MASM не различается знаковые и беззнаковые числа (подобно тому, как это делает x86), и значение числа не может выходить за диапазон dword. Препроцессор MASM не выдаёт предупреждений при переполнении. Следующий пример демонстрирует такое поведение:
    myint = 0ffffffffh
    myint = myint + 1 ;; myint = 0
    %echo @CatStr(%myint)
    =================================
    OUT:
    0
    ;; Ещё один пример с умножением:
    myint = 0ffffffffh
    ;; 0ffffffffh * 2 = (dword)1FFFFFFFEh = 4294967294
    myint = myint * 2
    %echo @CatStr(%myint)
    =================================
    OUT:
    4294967294
    В следующей статье мы поговорим про то, как работать с 64-разрядными макропеременными, используя данный факт.
    Ниже приводится список операций, которые могут участвовать в целочисленных выражениях MASM.
    ОператорПримерОписание
    ANDres = op1 AND op2 Операция логического «И» над каждым битом операндов op1 и op2.
    OR res = op1 OR op2 Операция логического «ИЛИ» над каждым битом операндов op1, op2
    NOT res = NOT op1 Операция логического «НЕ» над каждым битом операнда op1
    XOR res = op1 XOR op2 Операция XOR между операндами op1, op2
    SHL res = op1 SHL count Выполняет побитовый сдвиг влево (наподобие команды x86 shl) операнда op1, на количество бит, указанное в операнде count.
    SHR res = op1 SHL count Выполняет побитовый сдвиг вправо операнда op1, на число бит, указанное в операнде count.
    +,-,*,/ Основные математические операции
    MOD res = op1 MOD op2 Возвращает остаток от деления операнда op1 на операнд op2
    [] res = op1[op2] Операция: «Смещение». Выполняет сложение операндов op1 и op2

    III.5. Вычисление рекурсивных выражений

    Теперь, когда мы рассмотрели правила анализа и вычисления выражений в MASM, остаётся раскрыть важный вопрос: «Как происходит анализ выражений, если они состоят из других выражений?».
    Обычно это называется короче: вложенные выражения.
    Вложенное выражение – это такое выражение, элементы которого сами являются выражениями, которые так же могут иметь вложенность.
    Замороченное определение, похожее на «Иди туда, не знаю куда, возьми то, не знаю что» – пример старинной народной русской рекурсии, которая так часто встречается в нашей жизни. :)
    Например, вызов макрофункции при вызове макро – это вложенное выражение:
    MyMacro FunMacro(Мой парамерт)
    ;;Или это:
    %echo FunMacro(Мой параметр)
    ;;Или это:
    MyMacro FunMacro(Fun2(Привет))
    Вложенность характеризуется параметром количества уровней вложенности. В недавнем примере уровень вложенности был равен двум. При чём вызов Fun2() можно называть выражением низшего уровня вложенности, а вызов макро MyMacro – выражением верхнего уровня.
    После анализа выражений, и получения их многоуровневой структуры вложенности, препроцессор начинает вычислять результат выражения самого низшего уровня. Потом подставляет его результат в выражение следующего уровня, и так далее.
    Например, для случая:
    Fun2 macro param
    exitm <MyCount = param>
    endm
    FunMacro(Fun2(%(12+34)))
    Порядок вычислений такой:
    1. %(12+34) = 46
    2. Fun2(46)
    3. FunMacro(MyCount = 46)
    4. Результат выполнения FunMacro(MyCount = 46)
    А иначе препроцессор не смог бы. Если бы он начал вычисления выражений с верхнего уровня, то это то же самое, как если бы он попытался выполнить народную русскую рекурсию:
    «Пойди туда, не знаю куда…, вычисли то, не знаю что»
    или
    FunMacro(???)
    То есть: Вложенные выражения вычисляются последовательно от низшего уровня к верхнему, и результаты вычисления каждого уровня становятся материалом для выражений следующего уровня.
    Это правило называется рекурсивным вычислением выражений. Оно используется везде, кроме мест вычисления значений макропараметров при вызове макро (как макросов, так и макрофункций). В этом случае действует правило: результат вложенного выражения присваивается макропараметру и не анализируется повторно. Это значит, что в данном примере:
    myvar EQU <123>
    MyMacro macro param1,param2,param3
    echo param1
    endm
    FunMacro macro param:VARARG
    exitm <param>
    endm
    MyMacro FunMacro(var,@CatStr(<%>,myvar),var4)
    вывод будет таким:
    var,myvar,var4
    То есть препроцессор не будет снова вычислять выражение для второго макропараметра функции FunMacro(). Если бы он сделал это, то тогда вывод был бы таким, как в этом случае:
    %echo FunMacro(var,@CatStr(<%>,myvar),var4)
    Вывод:
    var,123,var4
    Теперь, когда вы знаете все тонкости вычисления выражений в MASM, настало время рассмотреть Встроенные макрофункции и директивы, которые участвуют в этих выражениях.
     
    Последнее редактирование модератором: 31 янв 2017
    rococo795 нравится это.
  7. Edmond

    Edmond узник замка IF THEN ELSE

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    203
    Адрес:
    WASM.RU

    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(<my>,var)
    Вывод: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
    Информация о сегментах
    @соdе, строковая макропеременнаяВозвращает имя сегмента кода.
    @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 <param2>
    ;; Если макропараметр не определён,
    ;; генерируем ошибку
    .ERR <Не определён параметр param2>
    IFNB строка
    ELSEIFNB строка
    если строка не пуста.
    IFDIF str1,str2
    ELSEIFDIF str1,str2
    если строки различны.
    IFDIF <String>,<string>
    echo Этот код выполнится
    echo потому что строки различны
    ENDIF
    IFDIFI str1,str2
    ELSEIFDIFI str1,str2
    если строки различны (без учёта различий в регистре букв).
    IFDIF <String1>,<string2>
    echo Этот код не выполнится
    echo потому что строки Одинаковы
    ENDIF
    IFIDN str1,str2
    ELSEIFIDN str1,str2
    если строки одинаковы.
    IFDIF <String1>,<string2>
    echo Этот код не выполнится
    echo потому что строки Различны
    ENDIF
    IFIDN str1,str2
    ELSEIFIDN str1,str2
    если строки одинаковы (без учёта различий в регистре букв).
    IFDIF <String1>,<string2>
    echo Этот код выполнится
    echo потому что строки Одинаковы
    ENDIF
    На протяжении всей статьи я часто пользовался следующей директивой, которая позволяет выводить текст на консоль во время компиляции. Эта директива echo. Как мы узнаем позже, она оказалось просто незаменимой при проектировании макро.
    Вы уже, наверное, убедились насколько полезна эта директива, позволяющая заглянуть, а что именно происходит в недрах макроса, или посмотреть значения макропеременных.
    Кроме этого, есть ещё одна группа директив, без которой мы не сможем обойтись. Не сможем потому, что макрофункции, или макросы, которые мы собираемся создавать должны быть слегка умными, иначе говоря, иметь «защиту от дурака».
    Если кто-то неправильно использует макрос, то код, полученный таким образом может быть неправильным с точки зрения программиста, но не вызовет подозрений у компилятора. Поэтому макро не просто должен завершится, а и каким-то образом остановить компиляцию программы с выдачей сообщения об ошибке.
    Именно для этого и существует простой набор директив условной генерации ошибки. Действуют они подобно условным блокам и директиве echo. Пример безусловной генерации ошибки:
    .ERR <Ошибочка вышла, гражданин начальник>
     
  8. Edmond

    Edmond узник замка IF THEN ELSE

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    203
    Адрес:
    WASM.RU
    Условная генерация ошибки, имеют ту же форму, что и IFDIRECTIVE в таблице выше, однако последним дополнительным параметром является строка сообщения. Например:
    .ERRE выражение,<ошибка, если выражение равно нулю>
    .ERRNZ выражение,<ошибка, если выражение не равно нулю>
    .ERRDEF id,<ошибка, если id определен>
    .ERRB строка,<ошибка, если строка пуста>
    .ERRNB строка,<ошибка, если строка не пуста>
    .ERRDIF str1,str2,<ошибка, если строки различны>
    .ERRDIFI str1,str2,<ошибка, если строки различны (без учёта регистра)>
    .ERRIDN str1,str2,<ошибка, если строки одинаковы>
    .ERRIDNI str1,str2,<ошибка, если строки одинаковы (без учёта регистра)>

    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:
    1. Поместить параметры в стек в обратном порядке их определению.
    2. Вызвать функцию 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, <It’s, wonderful, wonderful, asm>
    echo parameter
    ENDM
    ВЫВОД:
    -=-=-=-=-=-=
    It's
    wonderful
    wonderful
    asm
    А вот пример макрофункции, который подсчитывает число аргументов VARARG:
    @ArgCount MACRO parmlist:VARARG
    count = 0
    FOR param, <parmlist>
    count = count + 1
    ENDM
    EXITM count
    ENDM
    Вот в принципе, уже на основе этих знаний можно было бы организовать макрос stdcall:
    stdcall macro funname,params:VARARG
    count = 0
    FOR param, <parmlist>
    count = count + 1 ;; Считаем число параметров
    push param ;; Помещаем их в стек
    ENDM
    ;;Вызываем функцию
    call ??? ;;А вот как это сделать?
    endm
    Ещё несколько минут необходимо для того, чтобы понять, что этот макро работает неправильно. Хотя бы потому, что параметры помещаются в стек не так. Нужно было бы помещать их от последнего к первому, а не от первого к последнему. А, кроме того, ведь символ макроподстановки нельзя употреблять к макропеременной count, потому что это не макропараметр, это макропеременная.
    К сожалению, в MASM нет обратной конструкции FOR. Поэтому самый простой выход, который напрашивается сам собой – это изменить порядок параметров в списке, а потом только генерировать команды push.
    Вторую проблему можно легко решить, воспользовавшись макрофункцией конкатенации строк:
    call @CatStr(_,funname,@,%(count*4))
    С параметрами в стек будет посложнее. В принципе я бы решил эту задачу, если бы MASM поддерживал бы такой тип макропеременных как массив. Но хотя MASM и не поддерживает этот тип, его можно эмулировать.
    count = 0
    FOR param, <paramlist>
    count = count + 1 ;; Считаем число параметров
    @CatStr(var,%count) TEXTEQU <param>
    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 <char>
    count = count + 1
    ENDIF
    ENDM
    %echo @CatStr(%count)
     
  9. Edmond

    Edmond узник замка IF THEN ELSE

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    203
    Адрес:
    WASM.RU

    Упражнение:


    The Svin'у, как и любому программисту, который часто имеет дело с битами, было бы удобно записывать значения бит по группам, через пробел.
    ;;Вот так неудобно и ненаглядно
    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. Читать поток символов, пока не встретится символ возврата каретки без предыдущего символа «/». Игнорировать часть строки после «;»
      1. Определить наличие лексем первого уровня в строке:
        1. Выделить все строковые литералы в кавычках, если только это не выражение с TEXTEQU и символ комментария «;»
        2. Строковые литералы: <текст>
        3. Численные литералы: 1234, 1234h, 01011b
        4. Правильные литералы: строка из символов «A-Z,a-z,_0-9», но не начинающаяся на цифру
        5. Литералы разделители: «,.»
        6. Управляющие Литералы: «+-*» Правильные литералы: строка из символов «A-Z,a-z,_0-9», но не начинающаяся на цифру
      2. Проверить правильные литералы на совпадение в списке ключевых слов, и определить схему выражения. В зависимости от схемы выражения, выполнить или пропустить:
        1. Проверить правильные литералы на совпадение в списке макро (в зависимости от способа вызова в списке макрофункций, или макросов)
        2. Проверить на наличие имени правильного литерала в таблице макропеременных.
        3. Осуществить вызов и замену макро и макропеременных, в соответствии с выражением строки.
        4. Вычислить все выражения допустимые в ML (+-*).
      3. Осуществить разбор схемы.
        1. Если это определение процедуры, записать в таблице процедур имя и прототип новой процедуры
        2. Если это макроопределение: анализировать его тело. Если найден возвращаемый параметр, записать макроопределение в таблицу макрофункций, иначе в таблицу макросов.
        3. Если это определение EQU вычислить правую часть.
          1. Если эта макропеременная уже есть в таблице макропеременных, и её тип – числовой, выдать ошибку. Если эта макропеременная имеет строковый тип, изменить строку, на которую указывает свойство value этой макропеременной.
          2. Если правая часть числовой литерал – записать EQU определение в таблицу, и пометить его тип как числовой константы. Записать в свойство макропеременной value значение указателя на строку. Записать свойство value равным числу.
          3. иначе EQU – переменная, имеющая указатель на строку. Записать в значения свойства value указатель на строку.
        4. Если это выражение с «=» или подобное, выполнить замену всех литералов на макроконстанты, переменные, вызов всех макрофункций, и только потом выполнять выражение.
    4. Перейти к анализу следующей строки.

    # Эпилог

    Когда большая стрелка часов перекатила через 12-ти часовой рубеж, появился свет...