В гл.3 обсуждалось, как реализован стек в микропроцессоре 8088.  Микропроцессор 8088 адресует стек с помощью регистровой пары SS:SP.  Помещение объектов в стек приводит к тому, что он растет в сторону  меньших адресов памяти.  Стек, кроме всего прочего, служит и для  запоминания адресов возврата из подпрограмм.  В этом разделе  рассматриваются некоторые команды, которые непосредственно работают  со стеком.

Фиг.4.7 иллюстрирует ассемблированные стековые команды.  Мнемоника команд очевидна; за кодами операций PUSH и POP следует  имя регистра для указания операнда. Единственным исключением  является помещение и извлечение из стека регистра флагов, которые  используют мнемонику PUSHF и POPF соответственно. Содержимое любой  ячейки памяти, которую программа может адресовать, используя  возможные способы адресации, также может быть помещено или  извлечено из стека.

При любых действиях со стеком в микропроцессоре 8088 базовой  единицей информации является 16=битовое слово. Длина любого  объекта, помещаемого в стек либо извлекаемого из стека, составляет  одно или несколько слов. Байтовых команд, связанных с засылкой  данных или извлечением их из стека, не существует. Если, например,  программе необходимо сохранить содержимое регистра AL а стеке, она  должна поместить содержимое регистра AX, так как не существует  способа сохранения только содержимого регистра AL.

Основное назначение стека - временное хранение информации. Как  мы уже видели, стек используется для сохранения адреса возврата;  программа также может сохранять данные. Если программа хочет  использовать регистр, пусть даже сохранить текущие данные, она  может послать значение этого регистра в стек. Эти данные  сохраняются в стеке и позже могут быть восстановлены. Например,

          Microsoft (R) Macro Assembler Version 5.00              1/1/80 04:00:43

          Фиг. 4.7 Операции со стеком                      Page  1-1

 

                                         PAGE   ,132

                                         TITLE  Фиг. 4.7 Операции со стеком

           0000                   CODE   SEGMENT

                                         ASSUME CS:CODE,DS:CODE

           0000                   EXWORD LABEL   WORD

 

           0000  50                          PUSH    AX        ; Поместить регистр в стек

           0001  56                          PUSH    SI

           0002  0E                          PUSH    CS        ; Можно поместить в стек сегментный регистр

           0003  FF 36 0000 R                PUSH    EXWORD         ; Можно также поместить в стек ячейку памяти

 

           0007  8F 06 0000 R                POP     EXWORD         ; Можно извлечь то, что в помещено в стек

           000B  07                          POP     ES        ; Можно извлечь в другое место

           000C  5F                          POP     DI

           000D  5B                          POP     BX

 

           000E  9C                          PUSHF             ; Другая мнемоника для флагов

           000F  9D                          POPF

 

                                   ;----- Пример, демонстрирующий передачу параметров

 

           0010  50                          PUSH    AX

           0011  53                          PUSH    BX

           0012  51                          PUSH    CX

           0013  52                          PUSH    DX

           0014  E8 0017 R                    CALL    SUBROUTINE      ; Передача управления

                                   ;           ...               ; Продолжение программы

 

           0017                   SUBROUTINE      PROC    NEAR

 

           0017  8B EC                  MOV     BP, SP         ; Занесение в BP адреса стека

           0019  8B 46 02                     MOV     AX, [BP+2]      ; Выборка последнего параметра (DX)

           001C  8B 5E 04                     MOV     BX, [BP+4]      ; Выборка третьего параметра (CX)

           001F  8B 4E 06                     MOV     CX, [BP+6]      ; Выборка второго параметра (BX)

           0022  8B 56 08                     MOV     DX, [BP+8]      ; Выборка первого параметра (AX)

                                   ;           ...

           0025  C2 0008                      RET     8              ; Возврат с уничтожением поля параметров

           0028                   SUBROUTINE      ENDP

           0028                   CODE   ENDS

                                         END

  Фиг. 4.7 Операции со стеком

программе нужно ввести код из порта ввода=вывода 3DAH, а в регистре  DX находятся важные данные. Следующая последовательность команд

  PUSH  DX

  MOV DX,   3DAH

  IN AL, DX

  POP DX

сохраняет регистр DX в стеке на то время, пока он нужен в  программе для выполнения команды IN.

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

Помните о том, что стек - это структура типа LIFO. Если в вашей  программе выполняется последовательность команд

  PUSH  BX

  PUSH CX

  POP BX

  POP CX

то результирующим эффектом будет обмен значений в регистрах BX  и CX. Только тот факт, что в команде PUSH был указан регистр BX, не  означает, что команда POP, указывающая на тот же регистр,  восстанавливает первоначальное содержимое регистра BX. Еще одним  важным моментом является то, что команды PUSH и POP должны быть  сбалансированы, т.е. каждой команде PUSH должна соответствовать  команда POP. Точно так же, как и в случае скобок в арифметическом  выражении, если посылки и извлечения из стека не сбалансированы,  результаты будут неверны. Более того, несбалансированные команды  PUSH/POP обычно приводят к возврату из подпрограмм по адресу  значения данных, а не значения указателя команд из=за того, что  микропроцессор 8088 записывает в стек адрес возврата. Обычно это  вынуждает микропроцессор выполнять программу, которую программист  никогда не писал. Поэтому баланс стековых команд обязателен. Будьте  особенно внимательны в тех случаях, когда в программе есть условный  переход вокруг стековых операций; можно легко выпустить из виду  один из вариантов выполнения, что оставит стек несбалансированным.

Наряду с сохранением данных, программа может использовать стек  в качестве буфера при некоторых пересылках; в частности, не  существует команды пересылки, которая бы переносила данные из  одного сегментного регистра в другой. В обычном случае загрузка  одного сегментного регистра из другого требует сначала загрузки его  значения а промежуточный регистр. Это достигается следующей  последовательностью из двух команд:

      MOV   AX,CS  ;переслать значение регистра

                    ;CS в регистр AX

      MOV   DS,AX  ;загрузить это значение в

                    ; регистр DS

Каждая из этих команд имеет длину несколько байт, и эта  последовательность разрушает содержимое регистра AX. Альтернативным  подходом может быть

  PUSH  CS    ; регистр CS поместить в стек

  POP DS    ; поместить это значение в регистр DS

Результирующий эффект этой последовательности команд тот же,  регистр DS загружается из регистра CS. Здесь длина программы -  всего два байта, и к тому же не требуется промежуточный регистр.  Однако эти две команды занимают больше времени, так как нужны  дополнительные циклы чтения и записи в стек. Это - метод потери в  скорости выполнения ради уменьшения размера объектного кода.