Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Последние темы форума

Показать новые сообщения »

Почтовая рассылка

Подписчиков: 11639
Последний выпуск: 19.06.2015

СПРАВОЧНИК по системе программирования ТУРБО АССЕМБЛЕР 2.0


Оглавление

                             Подпрограммы
-----------------------------------------------------------------

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

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

     Подпрограммы позволяют вам строить программы  по  модульному
принципу.  При этом подпрограммы позволяют "скрывать" специфичес-
кие детали (то есть убирать их на нижний уровень) и сосредоточить
внимание  на  общем  алгоритме программы.  Подпрограммы позволяют
также сделать программы намного более компактными,  поскольку от-
дельную  подпрограмму можно вызывать во многих местах программы и
даже выполнять различные функции,  передавая ей различные  значе-
ния.  В  больших программах (независимо от того,  написаны они на
Ассемблере,  Паскале или Си) подпрограммы  являются  существенным
средством  для создания упорядоченного и легко обслуживаемого ис-
ходного кода.



                        Выполнение подпрограмм
-----------------------------------------------------------------

     Основные моменты выполнения подпрограммы  иллюстрируются  на
Рис.  5.12. В вызывающей подпрограмму программе выполняется инст-
рукция CALL, которая заносит адрес следующей инструкции в стек  и
загружает в регистр IP адрес соответствующей подпрограммы, осуще-
ствляя таким образом переход на подпрограмму.  После  этого  под-
программа  выполняется, как любой другой код. В подпрограммах мо-
гут (часто это так и бывает) содержаться инструкции вызовов  дру-
гих подпрограмм. Фактически, должным образом построенные подпрог-
раммы могут даже вызывать сами себя (это называется рекурсией).

        .             .                      .            .
        .             .                      .            .
        .             .                      .            .
        |-------------|                      |------------|
   1000 | mov al,1    | (в IP загружается -->| shl al,1   | 1110
        |-------------| 1110 и 1007 зано- |  |------------|
   1002 | mov bl,3    | сится в стек)     |  | add al,bl  | 1112
        |-------------|                   |  |------------|
   1004 | call DoCalc |--------------------  | and al,7   | 1114
        |-------------|                      |------------|
   1007 | mov ah,2    |<-------------------  | add al,'0' | 1116
        |-------------|  Значение вершины |  |------------|
   1009 | int 21h     | стека 1007 извле- ---| ret        | 1118
        |-------------| кается и заносится в |------------|
        .             . IP                   .            .
        .             .                      .            .
        .             .                      .            .

     Рис. 5.12 Выполнение подпрограммы.

     Когда подпрограмма заканчивает работу, она вызывает  инстру-
кцию  RET,  которая извлекает из стека адрес, занесенный туда со-
ответствующей инструкцией CALL, и заносит его в IP. Это  приводит
к тому, что вызывающая программа возобновит выполнение с инструк-
ции, следующей за инструкции CALL.

     Например, следующая программы выводит на экран три строки:

      Привет
      Пример строки
      Еще одна строка

     Для вывода строк вызывается подпрограмма PrintString:

         DOSSEG
         .MODEL   SMALL
         .STACK   200h
         .DATA
Message1          DB   'Привет',0dh,0ah,0
Message2          DB   'Пример строки',0dh,0ah,0
Message3          DB   'Еще одна строка',0dh,0ah,0
         .CODE
ProgramStart      PROC  NEAR
         mov   ax,@Data
         mov   ds,ax
         mov   bx,OFFSET Message1
         call  PrintString     ; вывести строку "Привет"
         mov   bx,OFFSET Message2
         call  PrintString     ; вывести строку "Пример строки"
         mov   bx,OFFSET Message3
         call  PrintString     ; вывести строку "Еще одна
                               ; строка"
         mov   ax,4ch          ; функция DOS завершения
                               ; программы
         int   21h             ; завершить программу
ProgramStart   ENDP
;
; Подпрограмма вывода на экран строки, завершающейся
; нулевым символом
;
; Входные данные:
;    DS:BX - указатель на выводимую строку.
;
; Нарушаемые регистры: AX, BX
;
PrintString      PROC   NEAR
PrintStringLoop:
         mov   al[bx]          ; получить следующий символ
                               ; строки
         and   dl,dl           ; значение символа равно 0?
         jz    EndPrintString  ; если это так, то вывод
                               ; строки завершен
         inc   bx              ; ссылка на следующий
                               ; символ
         mov   ah,2            ; функция DOS вывода символа
         int   21h             ; вызвать DOS для вывода
                               ; символа
         jmp   PrintStringLoop ; вывести следующий символ,
                               ; если он имеется
EndPrintString:
         ret                   ; возврат в вызывающую
                               ; программу
PrintString    ENDP
         END   ProgramStart

     Здесь стоит отметить два  момента.  Во-первых,  подпрограмма
PrintString  не  настроена  жестко на печать определенной строки.
Она может печатать любую строку, на которую с помощью  BX  укажет
вызывающая  программа.  Во-вторых, для выделения подпрограммы ис-
пользованы две новых директивы - PROC и ENDP.

     Директива PROC используется для того, чтобы отметить  начало
процедуры.  Метка,  указанная в директиве PROC (в данном случае -
PrintString), представляет собой имя процедуры, как если  бы  ис-
пользовалось:

PrintString   LABEL   PROC

     Однако, директива PROC делает большее. Она определяет, какую
инструкцию  RET (возврат управления) - ближнюю или дальнюю - сле-
дует использовать в данной процедуре.

     Давайте рассмотрим последний оператор  несколько  подробнее.
Вспомним,  что  когда  выполняется переход на метку ближнего типа
(NEAR), в IP загружается новое значение, а при переходе на  даль-
нюю метку (FAR) новые значения загружаются и в регистр  IP,  и  в
CS.  Если инструкция CALL ссылается на дальнюю метку, загружаются
и CS, и IP (как и при переходе).

     Вот почему при дальнем вызове в стек заносятся и регистр CS,
и IP.  Иначе откуда инструкция RET получит достаточную информацию
для возврата в вызывающую программу? Ведь если дальний вызов заг-
рузит CS и IP,  а занесет в стек только IP, то при возврате можно
будет только загрузить IP из вершины стека.  Тогда  в  результате
выполнения  инструкции RET пара регистров CS:IP содержала бы зна-
чение CS вызываемой программы, а IP - вызывающей, что очевидно не
имеет смысла.

     Что же произойдет, когда в стек будут занесены оба  регистра
- CS и IP? Как Турбо Ассемблер узнает о типе возврата, генерируе-
мом в соответствующей подпрограмме? Один из путей состоит в явном
задании  типа каждой инструкции возврата - RETN (возврат ближнего
типа) или RETF (возврат дальнего типа), однако лучший способ зак-
лючается в использовании директив PROC и ENDP.

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

          .
          .
          .
TestSub        PROC   NEAR
          .
          .
          .
TestSub        ENDP
          .
          .
          .

отмечают начало и конец подпрограммы TestSub.

     Директивы ENDP и PROC не генерируют выполняемого кода,  ведь
это  директивы,  а  не  инструкции. Все их действие заключаются в
управлении типом инструкции RET данной подпрограммы.

     Если операндом директивы PROC является  NEAR  (ближний),  то
все  инструкции RET между  директивой PROC  и соответствующей ди-
рективой ENDP ассемблируются, как  возвраты  управления  ближнего
типа. Если же, с другой стороны, операндом директивы PROC являет-
ся FAR (дальний), то все инструкции RET в данной процедуре ассем-
блируются, как возвраты управления дальнего типа.

     Поэтому, чтобы, например, изменить тип всех инструкций RET в
TestSub, измените директиву PROC следующим образом:

TestSub      PROC   FAR

     В общем случае лучше там,  где  это  возможно,  использовать
подпрограммы ближнего типа, так как дальние вызовы занимают боль-
ше памяти и выполняются медленнее, а возвраты дальнего типа также
выполняются медленнее, чем ближнего. Однако подпрограммы дальнего
типа становятся необходимыми, когда объем  кода  вашей  программы
превышает 64К.

     Если вы используете упрощенные директивы определения сегмен-
тов,  то  лучше использовать директиву PROC без операндов, напри-
мер:

TestSub     PROC

     Когда Турбо Ассемблер встречает такую директиву, он  автома-
тически  настраивает  ее тип в соответствии с выбранной с помощью
директивы .MODEL моделью памяти (по умолчанию  это  малая  модель
памяти). Программы со сверхмалой, малой и компактной моделями па-
мяти могут иметь вызовы ближнего типа, а вызовы в  программах  со
средней, большой и сверхбольшой моделью памяти имеют дальний тип.
Например, в программе:

          .
          .
          .
          .MODEL   SMALL  ; малая модель памяти
          .
          .
          .
TestSub   PROC
          .
          .
          .

подпрограмма TestSub вызывается с помощью ближнего  вызова,  а  в
программе:

          .
          .
          .
          .MODEL   LARGE ; большая модель памяти
          .
          .
          .
TestSub   PROC
          .
          .
          .

с помощью дальнего.




Оглавление

Оставить комментарий

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог