На главную
Подписка
Новости










Главная / MS-DOS / MS-DOS. РУКОВОДСТВО РАЗРАБОТЧИКА / Глава 1 / Метки типа LOCAL Сделать домашней страницей Добавить в избранное Написать писмо

НАЗАД СОДЕРЖАНИЕ ВПЕРЁД

Метки типа LOCAL

До сих пор макросы, использованные нами, предназначались лишь для генерации простых ассемблерных команд. Теперь предположим, что мы хотим создать макро, которое выбирает меньшее из двух чисел и помещает результат в какую-то ячейку. Такое макро может выглядеть, например, так:


            min  MACRO    result,first,second
                 mov      &result,&first
                 cmp      &first,&second
                 jl       order_ok
                 mov      &result,&second
            order_ok:
                 ENDM

Когда мы вызываем макро min, оно вырабатывает правильный код, однако имеется одна проблема: хотя макро вычисляется превосходно, оно может быть использовано лишь единожды. Так как метка order_ok может быть определена в программе только один раз, при использовании данного макро в двух местах программы MASM распознает множественное определение символа.


Чтобы в добавление к другим параметрам разрешить указание параметра метки, мы можем выполнить небольшое изменение макро:


            min   MACRO     result,first,second,order-ok
                  mov       &result,&first
                  cmp       &first,&second
                  jl        &order_ok
                  mov       &result,&second
            оrder_ok&:
                  ENDM

При вызове нового макро min, показанного в следующем номере, мы можем указать имя, которое будет использоваться для метки перехода. Теперь макро min может быть использовано всякий раз, когда это необходимо, так как каждый раз метке перехода будет присваиваться новое имя. Однако действительное имя не имеет для нас никакого значения, ибо метка является собственностью функции min.


                  min       ax,bx,cx,jmp1        <- вызов макро
            1     mov       ax,bx
            1     cmp       bx,cx
            1     jl        jmp1
            1     mov       ax,cx
            1  jmp1:

Такой способ создания нового имени при каждом вызове макро min является лучшим. Именно для этой цели MASM имеет директиву LOCAL (локальный). Когда MASM встречает LOCAL, для стоящего рядом имени создается уникальная метка. Другой способ заключается в помещении параметра LOCAL в список параметров MACRO, но при этом MASM производит присваивание действительного аргумента. Предупреждение: операторы LOCAL всегда должны помещаться сразу же после строки именования MACRO! После включения директивы LOCAL новое макро min выглядит так:


            min    MACRO     result,first,second
                   LOCAL     order_ok
                   mov       &result,&first
                   cmp       &first,&second
                   jl        order_ok
                   mov       &result,&second
            order_ok:
                   ENDM

Теперь, когда мы снова вызовем макро min, образуется листинговый файл, как показано в следующем примере. Значение order_ok будет заменено на ??0000. Каждый раз при вызове макро order_ok заменяется на новое значение, вырабатываемое MASM.


                    min       ax,bx,cx   ;первый вызов
            1       mov       ax,bx
            1       cmp       bx,cx
            1       jl        ??0000
            1       mov       ax,cx
            1  ??0000:
                    min       ax,bx,cx   ;второй вызов
            1       mov       ax,bx
            1       cmp       bx,cx
            1       jl        ??0001
            1       mov       ax,cx
            1  ??0001:

Конечно, остается вероятность возникновения конфликта меток, если Вы решите использовать метки, начинающиеся с ??. Если Вы устраните использование меток, начинающихся с ??, то Вы сможете вызывать макро min столько раз, сколько захотите.


Использование меток LOCAL не ограничивается только переходами по адресам. Метки LOCAL могут также использоваться с данными, как показано в следующем макросе. В этом случае макрос используется для вставки текстовых строк в сегмент данных и последующего создания ссылки на них в сегменте кода.


Сравнивая исходный текст с макрорасширением в Листинге 1-1, можно увидеть, насколько удобно использовать макрос. Листинг 1-1 также содержит некоторые полезные макросы, облегчающие решение задачи по написанию программ .ЕХЕ. Однажды определив эти макросы, Вы можете не беспокоиться за правильность синтаксиса программ .ЕХЕ!


                        Листинг 1-1. Программа Hello World
             -------------------------------------------------------------
            ;********************************************************
            ;        С Е К Ц И Я   М А К Р О О П И С А Н И Я
            ;********************************************************
            ;
            @DosCall  MACRO
                      int      21h  ;вызвать функцию MS-DOS
                      ENDM
            ;
            @InitStk  MACRO         ;определить размер стека
            stk_seg   SEGMENT  stack
                      DB       32 dup ('stack   ')
            stk_seg   ENDS
                      ENDM
            ;
            @InitPrg  MACRO    segment  ; инициализировать сегмент
                      ASSUME ds:segment ; данных
            start:                      ; основная точка входа
                      mov      ax,segment
                      mov      ds,ax    ;установить сегмент данных
                      mov      es,ax    ;установить внешний сегмент
                      ENDM
            ;
            @Finis    MACRO
                      mov      ax,4C00h ;завершить процесс
                      @DosCall
                      ENDM
            ;
            @DisStr   MACRO    string   ;отобразить строку памяти
                      mov      dx,offset string
                      mov      ah,09h
                      @DosCall
                      ENDM
            ;
            @TypeStr  MACRO     string  ;определить и отобразить строку
                      LOCAL     saddr   ;установить локальную метку
            cod_seg   ENDS              ;завершить сегмент кода
            dat_seg   SEGMENT           ;перейти к сегменту данных
            saddr     DB    string,'$'  ;определить строку в сегм.данных
            dat_seg   ENDS              ;завершить сегмент данных
            cod_seg   SEGMENT           ;вернуться к сегменту кода
                      @DisStr   saddr   ;отобразить строку
                      ENDM
            ;
            ;
            ;********************************************************
            ;         П Р О Г Р А М М Н А Я    С Е К Ц И Я
            ;********************************************************
            ;
                      @IniStk                ;установить стек
            cod_seg SEGMENT                  ;определить сегмент кода
            main    PROC        FAR          ;главная (одна) процедура
                    ASSUME      cs:cod_seg   ;назначить сегм.кода рег.CS
                    @InitPrg dat_seg         ;инициализ-ать сегмент кода
                    @TypeStr 'Hello world!'  ;выдать приветствие
                    @Finis                   ;закончить программу
            main    ENDP                     ;завершить процедуру
            cod_seg ENDS                     ;завершить сегмент кода
                    END         start        ;завершить программу и ...
                                             ;определить адрес пуска
            -------------------------------------------------------

Программу можно вводить точно так, как она приведена, а затем ее ассемблировать и выполнять. Слова "Hello world!" подлежат отображению на экране. Сам по себе результат не очень выразителен, однако, если используемый макрос сохранен в файле include (включить), написание .EXE-программ значительно облегчается. Давайте посмотрим на распечатку расширения программы, приведенного в Листинге 1-2.


                Листинг 1-2. Макрорасширение программы Hello World
             -------------------------------------------------------------
            ;********************************************************
            ;           П Р О Г Р А М М Н А Я   С Е К Ц И Я
            ;********************************************************
            ;
            ;       @InitStk                 ;установить стек
            1       stk_seg   SEGMENT stack
            1                 DB      32 dup ('stack   ')
            1       stk_seg   ENDS
                    cod_seg   SEGMENT        ;определить сегмент  кода
                       main   PROC   FAR     ;главная (одна) процедура
                    ASSUME    cs:cod_seg     ;назначить сегм.кода рег.CS
                    @InitPrg  dat_seg        ;инициализ-ать сегмент данных
            1       start:                   ;главная точка входа
            1                 mov  ax,dat_seg
            1                 mov  ds,ax     ;установить сегмент данных
            1                 mov  es,ax     ;установить внешний сегмент
                    @TypeStr 'Hello World!'  ;выдать приветствие
            1       cod_seg   ENDS           ;приостановить сегмент кода
            1       dat_seg   SEGMENT        ;перейти к сегменту данных
            1       ??0000    DB   'Hello world!,'$' ;определить строку
            1       dat_seg   ENDS        ;приостановить сегмент данных
            1       cod_seg   SEGMENT     ;вернуться к сегменту кода
            2                 mov   dx,offset ??0000
            2                 mov   ah,09h
            3                 int   21h      ;вызвать функцию MS-DOS
                    @Finis                   ;завершить программу
            1                 mov   ax,4C00h ;завершить процесс
            2                 int  21h       ;вызвать функцию MS-DOS
                    main      ENDP           ;закончить процедуру
                    cod_seg   ENDS           ;закончить сегмент кода
                    END       start          ;закончить программу ...

Прежде всего необходимо заметить, что используемый локальный адрес (saddr) в @TypeStr отлично работает как метка оператора данных. При связывании меток с данными не используйте двоеточие (:). Далее посмотрим, как макрорасширение использует зарезервированное слово SEGMENT (сегмент) в макро @InitPrg. Нет проблем! Вспомните, что имена формальных аргументов в списке аргументов перекрывают все другие описания MASM.


Обратите внимание, что некоторые строки не включены в листинговый файл. Например, оператор ASSUME ds:data_seg из @InitPrg опущен. Оператор был отассемблирован, но MASM подавил вывод его расширения.


Все это произошло по причине специфики обработки макросов. По умолчанию, исходные строки, не вырабатывающие исполнительного кода, в листинге подавляются. Оператор ASSUME является директивой MASM, которая не вырабатывает собственного кода; таким образом, он в листинге отсутствует. С другой стороны, директивы завершения сегмента ENDS приводятся в листинге, хотя программный код не вырабатывают. Есть в MASM тайны, над которыми всем нам стоит поразмышлять.


Представленную программу не следует рассматривать, как эталон хорошего программирования. Хотя идея использования макросов в вводной и заключительной части .EXE-программ и замечательна, включение имен "важных" символов в сами макросы применяется редко. Если имя сегмента данных отличается от dat _seg, в программе может возникнуть нежелательная конфликтная ситуация. Например, когда макро @TypeStr должно передать имя dat_seg в качестве аргумента или макро @InitPrg полагает, что сегмент данных называется dat_seg.


НАЗАД СОДЕРЖАНИЕ ВПЕРЁД

Hosted by uCoz