литература / Пухальский Проектирование микропроцессорных систем 2001
.pdf4.3. Система команд МП 8086/8088 |
393 |
и извлечений вершина стека TOS оказывается в первоначальном положении. Следует соблю дать правило: нельзя извлекать из стека какие-либо данные, не включив их сначала в стек. И всегда необходимо помнить о “тайном” смысле терминов “включение” (в стек) и “извлече ние” (из стека), подразумевающих автоматическое изменение содержимого указателя стека SP (включение и извлечение — это не то же самое, что запись и чтение данных из памяти).
Физический адрес стека определяется содержимым пар регистров SS:SP или SS:BP, при чем SP служит неявным указателем стека для всех операций включения и извлечения, a SS — сегментным регистром стека. Регистр ВР используется, главным образом, для произвольных обращений к стеку. Содержимое SS является самым младшим адресом (границей) области сте ка и называется базой стека. Первоначальное значение указателя стека SP, устанавливаемое при инициализации, является наибольшим смещением, которого может достигать стек. Это значение определяет максимальное число слов, которое может быть включено в стек (мини мальное значение SP = 0). Местоположение стека задается операционной системой или пользо вательской программой.
Временное сохранение операндов в стеке эффективнее использования для этих целей па мяти в области сегмента данных: команды PUSH и POP короче других команд и не нужно зада вать адрес памяти в явном виде (инкремент и декремент SP производится автоматически).
Команда PUSH выполняется за 11 тактов (reg), 10 тактов (seg) и 16 + еа тактов (М), ко манда POP — за 8 тактов (reg и seg SS, DS, ES) и 17 + еа тактов (М), команда PUSHF — за 10 тактов, команда POPF — за 8 тактов.
Команды CALL target и RET. Основным назначением стека является автоматическое запоминание адресов возврата из процедур (подпрограмм), вызываемых командами CALL tar get и INT type. Адрес возврата — это находящийся в указателе инструкции IP или в регистрах CS.IP адрес команды, следующей за командой вызова процедуры. Процедуры всегда должны заканчиваться командой возврата RET, автоматически извлекающей из стека адрес возврата и загружающей его в указатель инструкции IP или в регистры CS.IP.
Команда CALL предназначена для внутрисегментного и межсегментного вызова процедур proc (procedure — процедура, подпрограмма), которые могут находиться как внутри текущего сегмента кода (пеаг-ргос — внутрисегментный вызов), так и вне него (far-proc — межсегмент ный вызов):
SP <— SP - |
2, М(SP) |
<— IP, IP |
target (near); |
|
SP <— SP - |
2, M(SP) |
CS, SP <— SP - 2, M(SP) 4- IP, IP <h- target1; CS |
target2 (far), |
где target — одно или два слова адреса передачи управления. Режимы адресации (способы за дания адреса target) команд CALL такие же, что и для команд JMP (см. § 4.2, рис. 4.23) — нет только короткого (short) перехода (вызова). Вызов может быть прямым или косвенным, внут рисегментным или межсегментным. Команд условных вызовов процедур нет.
Соответственно вызывающим процедуры командам CALL имеются команды RET, выпол няющие внутрисегментные и межсегментные возвраты. Команды RET выполняют операции со стеком в обратном направлении:
IP <r~ М(SP), SP <— SP + 2 (near-return)',
IP <- M(SP), SP <— SP + 2, CS <- M(SP), SP <— SP + 2 (far-return).
В команде RET может быть задан непосредственный операнд im16, если через стек в вы званную процедуру передавались необходимые ей данные (параметры). В этом случае после извлечения из стека адреса возврата выполняется операция SP + iml6, что соответствует удале
394 Глава 4. Микропроцессоры 8086/8088 и сопроцессор 8087
нию из стека отработавших параметров (модифицируется вершина стека для обхода этих пара метров). Значение im\6 должно быть равно числу слов данных, включенных в стек командами PUSH, перед вызовом процедуры, которой эти данные необходимы. Вызванная процедура по лучает доступ к параметрам, адресуя область памяти стека содержимым пары регистров SS:BP, не извлекая их из стека командами POP.
В виде процедур обычно оформляются большие часто повторяющиеся программные фрагменты — в памяти нужно хранить только одну копию процедуры, а вызываться она может много раз из разных мест программы. Процедуры обеспечивают основное средство разделения кода программы на модули, которые можно легко по отдельности разрабатывать, тестировать и документировать. Процедуры можно хранить в библиотеках и использовать многими програм мами.
Команда CALL выполняется за 19 тактов (внутрисегментный прямой вызов), 16 тактов (внутрисегментный косвенный вызов через регистр), 21 + еа тактов (внутрисегментный кос венный вызов через память), 28 тактов (межсегментный прямой вызов) и 37 + еа тактов (межсегментный косвенный вызов).
Команда RET выполняется за 8 тактов (внутрисегментный возврат), 12 тактов (внутрисег ментный возврат с непосредственным операндом), 18 тактов (межсегментный возврат) и 17 тактов (межсегментный возврат с непосредственным операндом).
Пример 19. Передача управления при выполнении одноуровневых процедур:
CALL |
proc_ 1 |
; — |
MOV |
DL, 75 |
; <- |
proc_ 1 PUSH |
AX |
Начало процедуры proc_ 1 |
PUSH |
BX |
Сохранение содержимого регистров АХ, ВХ и СХ |
PUSH |
CX |
|
POP |
CX |
Восстановление содержимого регистров АХ, ВХ и СХ |
POP |
BX |
|
POP |
AX |
Конец процедуры proc_ 1 |
RET |
|
Содержимое тех регистров, которые использует процедура, будет модифицировано, а зна чит необходимо запомнить содержимое этих регистров до его изменения, а перед самым выхо дом из процедуры восстановить. Для этого используются команды PUSH и POP. Содержимое стека извлекается из него и загружается в регистры МП в порядке, обратном тому, в каком они включались в стек при выполнении программы.
Если процедура обращалась к стеку (выполняла команды PUSH и POP), то указатель стека SP должен быть возвращен на прежнее значение и действия со стеком не должны были разру шить адрес возврата. При работе с процедурами программист должен внимательно следить за правильным использованием стека, в противном случае возможны возвраты из процедур по бессмысленным адресам.
Процедуры могут иметь любое число уровней вложенности, от числа которых зависит, в частности, минимальный размер стека. Размер стека устанавливается программистом, исходя из потребностей разрабатываемой программы.
Пример 20. Передача управления при выполнении двухуровневых процедур:
|
|
|
4.3. Система команд МП 8086/8088 |
395 |
|
|
CALL ргос_ 1 |
— © |
|
|
|
|
SUB |
DX, DI |
«- |
|
|
proc_1 |
PUSH |
AX |
<r- |
Начало процедуры proc_1 первого уровня |
|
|
CALL proc_2 |
— © |
|
|
|
|
CMP |
SI, BX |
<- |
|
|
|
RET |
|
|
© Конец процедуры первого уровня |
|
pro cj. |
PUSH AX |
t— |
Начало процедуры proc_2 второго уровня |
|
|
|
RET |
|
|
Конец процедуры второго уровня |
|
Вэтом примере пояснена последовательность передач управления при выполнении двух уровневой процедуры (цифрами ©, ©, ® и © помечен порядок передачи управления).
Взадаче 36 приведен пример оформления на языке ассемблера процедуры CALL c_intr near. Конечно, вводить в программу такую процедуру (содержит всего две команды и исполь зуется только один раз) бессмысленно — число команд не сократилось, а увеличилось. Задача 36 предназначена в основном для демонстрации манипуляций с векторами прерываний команд
INT type.
Задача 36. Написать программу вывода на экран дисплея собственного сообщения при возникновении ошибки деления с последующим восстановлением системного вектора преры вания INT 0. Решение:
s_seg |
|
segment page STACK ; Сегмент стека |
|
s_seg |
|
dw |
8 dup (0E291A, 0ААА5/г, 5320/г, 2150h) ; Стек SP! — ASCII символы |
|
ends |
; Конец сегмента стека |
|
d_seg |
|
segment |
; Сегмент данных |
Message |
db |
10, 10, ‘ Чтоб в арифметике упрочиться,’, 13, 10 |
|
|
|
db |
‘ О-о-о, Юный Неофит!’, 13, 10 |
|
|
db |
‘ Пиши не так, как хочется,’, 13, 10 |
|
|
db |
‘ А как Zero велит!’, 10, 10, ‘(нажмите любую клавишу)’, *$’ |
; Управляющие ASCH-KOjxbi: |
|||
; |
13d = OD/г — возврат каретки (сдвиг курсора в левую позицию экрана) |
;10d = ОА/г — перевод строки (сдвиг курсора вниз на одну позицию)
;$ — задание конца строки, выводимой на дисплей
d_seg |
ends |
|
|
; Конец сегмента данных |
c_seg |
segment |
|
|
; Сегмент кода |
|
assume CS: c_seg, DS: d_seg, SS: s_seg, ES: d_seg |
|||
main |
proc |
far |
|
; Начало процедуры main |
|
push |
ds |
; Запись в стек начального адреса PSP (Program Segment Prefics) |
|
|
sub |
ax, ax |
; |
и 0000h (для возврата в DOS) |
|
push |
ax |
|
|
|
mov |
ax, djseg |
|
|
|
mov |
ds, ax |
; Инициализация сегментного регистра DS |
|
|
mov |
es, ax |
; ES = DS |
396 |
Глава 4. Микропроцессоры 8086/8088 и сопроцессор 8087 |
||
; Получение системного вектора прерывания CSc:IPc для INT О |
|||
CALL |
c_intr |
; АН <— 35ft — номер функции, AL <— type = 00 |
|
; Исходное значение указателя стека SP = SP0 |
|||
PUSH |
ВХ |
; SP |
SP - 2, M(SP0 -2)< —IPc (сохранение в стеке CSc:IPc) |
PUSH |
ES |
; SP <- SP - 2, M(SP0 - 4) CSc |
|
; Замена системного вектора прерывания пользовательским CSuilPy |
|||
PUSH |
DS |
|
|
LEA |
DX, zero |
|
|
MOV |
AX, c_seg |
|
|
MOV |
DS, AX |
|
25ft — номер функции, AL <— type = 00 |
MOV |
AX, 2500ft |
; AH |
|
INT |
21ft |
; M(0000) <- IPu = offset zero, M(0002) <- CSu = c_seg |
|
POP |
DS |
; DS <- M(SP0 - 6), SP <- SP + 2 |
;INT 21ft, функция DOS 25ft — установка вектора прерывания
;Деление целых чисел с переполнением (“divide by zero”)
MOV |
BL, 0A4/I |
; BH <- A4h = 164rf |
MOV |
AX, 0A400ft |
; AX <— A400ft = 41984c? |
DIV |
BL |
; “divide by zero" => системное прерывание INT 0 |
MOV |
AH, 8 |
|
INT |
21ft |
; Ожидание нажатия любой клавиши |
; INT 21ft, функция DOS 08ft — ввод с клавиатуры символа в ASCII-коде без эха
; Восстановление системного вектора прерывания для INT 0
CLI |
|
IF <— 0 — запрет аппаратных прерываний |
||||
MOV |
SI, DS |
|
|
|
|
|
POP |
AX |
AX |
M(SP0 - 4) |
= CSc, SP |
SP + 2 |
|
MOV |
DS, AX |
DS |
CSc |
|
|
|
POP |
DX |
DX 4 - M(SP0 - 2) |
= IPC, SP |
SP + 2 |
type = 00 |
|
MOV |
AX, 2500ft |
AH <—25ft — номер функции, AL |
||||
INT |
21ft |
M(0000) 4- IPC, M(0002) |
CSc |
. |
; INT 21/г, функция DOS 25ft — установка (восстановление) вектора прерывания
|
MOV |
DS, SI |
|
|
|
STI |
|
; IF |
1 — разрешение аппаратных прерываний |
main |
RET |
|
Конец процедуры main |
|
endp |
|
|||
; Подпрограмма для получения системного вектора прерывания |
||||
c_intr |
proc |
near |
Начало процедуры c_intr |
|
|
MOV |
AX, 3500ft |
АН <— 35ft — номер функции, AL <— type = 00 |
|
|
INT |
21ft |
BX <- M(0000) = IPC, ES M(0002) = CSc |
;ES.BX = CSc:IPc — адрес подпрограммы INT 0
;INT 21ft, функция DOS 35h — получение вектора прерывания
c_intr |
RET |
|
; Конец процедуры c_intr |
|
endp |
|
|||
; Подпрограмма, вызываемая системным прерыванием INT 0 |
||||
zero |
proc |
far |
; Начало процедуры прерывания zero |
|
|
POP |
АХ |
; Изменение адреса возврата на команду, следующую за |
|
|
ADD |
АХ, 2 |
; |
командой DIV, вызывающей проблему “divide by zero” |
|
|
4.3. Система команд МП 8086/8088 |
397 |
PUSH |
АХ |
|
|
LEA |
DX, Message; DX — начальный адрес символьной строки |
|
|
MOV |
АН, 9 |
; Вывод на экран дисплея сообщения Message: |
|
INT |
21h |
; Чтоб в арифметике упрочиться, |
|
;О-о-о, Юный Неофит!
;Пиши не так, как хочется,
;А как Zero велит! (нажмите любую клавишу)
;INT 21h, функция DOS 09h — вывод на дисплей А5С//-строки
zero |
IRET |
|
; Конец процедуры прерывания zero |
endp |
|
||
c_seg |
ends |
|
; Конец сегмента кода |
|
end |
main |
; Конец программы |
Команды INT type и IRET. В системе команд имеется три типа команд программного прерывания'. INT 3, INTO (однобайтовые команды) и INT type (двухбайтовые команды), где type = 0 ... 255. Прерывания подразделяются на внутренние, которые вызываются состоянием МП или одной из перечисленных команд, входящих в программу, и внешние — инициируемые сигналом, подаваемым в МП от других компонент системы (обычно запросы прерываний по ступают от контроллера прерываний 8259.4 и по входу немаскируемого запроса прерывания ЫМГ). Типичное внутреннее прерывание инициируется, например, при делении числа на нуль, атипичные внешние прерывания — сигналами на входах NMI и INTR микропроцессора. Ко манды прерываний по назначению аналогичны командам CALL вызова процедур (подпро грамм). Вызываемая командой прерывания подпрограмма называется процедурой прерывания.
Некоторыми видами прерываний управляют флаги IF и TF. Если условия для прерывания удовлетворяются и необходимые флаги установлены, МП завершает текущую команду, а затем реализует последовательность прерывания:
SP <— SP - |
2, |
M (SP)<-PSW , TF <— 0, IF <— 0, |
SP <- SP - |
2, |
M(SP) <- CS, SP SP - 2, M(SP) IP, |
IP <— M(4 x type), CS <— M(4 x type + 2),
где PSW — регистр признаков, TF — флаг трассировки, IF — флаг разрешения прерываний, 4 х type — адрес первого слова вектора прерывания, 4 х type + 2 — адрес второго слова вектора прерывания. Новое содержимое регистров IP и CS определяет начальный адрес выполняемой процедуры прерывания.
Двойное слово, загружаемое в указатель инструкции IP и сегментный регистр кода CS, называется вектором прерывания. Для хранения двойного слова требуются 4 байта, поэтому вектора прерываний могут занимать первые 1024 байта памяти и их нельзя использовать для других целей. Некоторые из 256 типов прерываний резервируются операционной системой и должны инициализироваться после включения компьютера. Пользователи приспосабливают остальные типы прерываний в соответствии со своими требованиями. В задаче 36 было пока зано, как можно использовать и системные векторы прерываний для вызова своих процедур обслуживания прерываний.
После обслуживания прерывания возврат в прерванную программу осуществляется ко мандой IRET, которая извлекает из стека значения IP, CS и PSW:
IP< -M (SP), SP SP + 2, C S f-M (S P ), SP <— SP + 2, PSW <-M (SP), SP <— SP + 2
в обратном порядке по отношению к их включению в стек.
398 |
Глава 4. Микропроцессоры 8086/8088 и сопроцессор 8087 |
||||||
|
Команда INT type |
|
П ам ять |
|
Адрес |
|
Назначение |
|
INTO |
IP |
для type 0 |
00000/г |
. Зарезервировано для |
||
|
|
CS |
для type |
0 |
|
/ |
ошибки деления |
|
INT 1 |
IP |
для type 1 |
00004/г |
ч Зарезервировано для |
||
|
|
CS |
для type 1 |
|
I |
пошагового режима работы |
|
|
INT 2 |
IP |
для type |
2 |
00008h |
» Зарезервировано для |
|
|
|
CS |
для type |
2 |
|
Г |
немаскируемого прерывания |
|
INT 3 |
IP |
для type 3 |
0000С/г |
* Зарезервировано для однобайтной |
||
|
|
CS для type |
3 |
|
J |
команды прерывания |
|
|
INTO |
IP |
для type |
4 |
00010h |
1 Зарезервировано для команды |
|
|
|
CS для type |
4 |
|
/ |
прерывания по переполнению |
|
|
INT 5 |
IP |
для type |
5 |
00014/г |
|
|
|
|
CS для type |
5 |
|
|
|
|
|
, |
|
. |
|
00018/i. . 003F8h |
||
|
|
|
|
|
|||
|
INT 255 |
IP |
для type |
255 |
ООЗБС/г |
|
|
|
|
CS для type |
255 |
|
|
Конец таблицы |
|
|
|
|
|
|
00400/г |
|
|
Рис. 4.26. Векторная система прерываний
Типы прерываний представлены на рис. 4.26, иллюстрирующем размещение векторов прерываний в памяти. Только первые пять типов определены явно, а остальные отведены для команд программных прерываний (INT type, используемых в программах) или внешних аппа ратных прерываний type, поступающих от контроллера прерываний 8259А. Некоторые типы прерываний в персональных компьютерах IBM PC используются операционной системой
MS-DOS (Microsoft Disk Operating System) и базовой системой ввода-вывода BIOS (Basic Input-Output System), обеспечивающей управление периферийным оборудованием компьютера. Например, команда INT 21h вызывает для выполнения функцию MS-DOS, номер которой дол жен быть предварительно задан в регистре АН (см. § 4.5), а команда INT 10h — функцию
BIOS.
Прерывание типа 0 вызывается автоматически по ошибке деления — компьютер включает текущее содержимое регистров PSW, CS и IP в стек, загружает регистры IP и CS из содержимо го памяти по адресам 00000 и 00002 и продолжает выполнение программы с адреса, опреде ляемого новым содержимым регистров IP и CS. Прерывание из-за ошибки деления возникает независимо от состояния флагов IF и TF, когда в командах DIV или IDIV частное превышает диапазон представления чисел.
Прерывание типа 1 обеспечивает пошаговую работу и только им управляет флаг TF. Если значение флага TF - 1, то по окончании следующей команды возникает прерывание, которое вызывает процедуру обработки прерывания, адрес которой определяется содержимым ячеек памяти 00004 ... 00007.
Прерывание типа 2 вызывается независимо от состояния флага IF сигналом, поступающим на вход NMI (Non-maskable Interrupt) микропроцессора.
Остальные типы прерываний вызываются либо командами прерываний, включенными в программу (этими прерываниями флаг IF не управляет), либо внешними прерываниями, за
4.3. Система команд МП 8086/8088 |
399 |
просы которых поступают на вход 1NTR микропроцессора (разрешением и запретом этих пре рываний управляет флаг IF).
Команда INT 3 часто применяется при отладке, когда пошаговая работа дает больше ин формации, чем требуется. Помещая команды INT 3 в контрольных точках программы (Break point interrupt), программист может использовать процедуру прерывания для вывода на экран дисплея или на принтер результата выполнения программы до контрольной точки.
Команда INTO имеет тип 4 и вызывает прерывание, если только значение флага OF = 1. Ее часто помещают сразу после арифметической команды, способной вызвать ошибку переполне ния, и при наличии переполнения осуществляется специальная обработка этой ошибки. В отли чие от деления на нуль переполнение не вызывает автоматического прерывания — прерывание необходимо явно определять командой INTO.
Команда IRET осуществляет возврат из процедуры прерывания. Она аналогична команде RET, но кроме адреса возврата извлекает из стека исходное содержимое PSW.
Команда INT 3 выполняется за 52 такта, команда INTO — за 53 такта (OF = 1) и за 4 такта (OF = 0), команда INT type # 3 — за 51 такт. Команда IRET выполняется за 24 такта.
Задача 37. Написать программу умножения 16-разрядных двоичных чисел АХ х DX с вы водом на экран дисплея произведения DX:AX в пошаговом режиме. Решение:
s_seg |
segment page STACK ; Сегмент стека |
|
|
dw |
32 dup (?) |
s_seg |
ends |
; Конец сегмента стека |
d_seg |
segment |
; Сегмент данных |
MJDX db |
10, ‘ DX = 0000h, AX = 0000/г’, 13, 10 |
|
tabl |
db |
10, ‘ Нажмите любую клавишу’, 10, 13 |
db |
‘0123456789ABCDEF’ |
; Управляющие ASCII-коды:
; |
13d = 0Dh — |
возврат каретки (сдвиг курсора в левую позицию экрана) |
; |
10d = 0Аh — |
перевод строки (сдвиг курсора вниз на одну позицию) |
;$ — задание конца строки, выводимой на дисплей
d_seg |
ends |
|
; Конец сегмента данных |
|
cjseg |
segment |
|
; Сегмент кода |
|
|
assume |
CS : c_seg, DS: d_seg, SS: s_seg, ES: d_seg |
||
main |
proc |
far |
; Начало процедуры main |
|
|
push |
ds |
; Запись в стек начального адреса PSP (Program Segment Prefics) |
|
|
sub |
ax, ах |
; |
и 0000h (для возврата в DOS) |
|
push |
ax |
|
|
|
mov |
ax, d_seg |
|
|
|
mov |
ds, ax |
; Инициализация сегментного регистра DS |
|
|
mov |
es, ах |
; ES = DS |
|
; Получение системного вектора прерывания CSc:IPc для INT 1 |
||||
|
MOV |
АХ, 3501/г |
; АН <—35/г — номер функции, AL <— type = 01 |
|
|
INT |
21 h |
; ВХ |
М(0000) = IPC, ES <— М(0002) = CSc |
|
|
|
; ES:BX = CSc:IPc — адрес подпрограммы INT 1 |
|
; INT 21 h, функция DOS 35h — получение вектора прерывания |
||||
; Исходное значение указателя стека SP = SP0 |
||||
|
PUSH |
ВХ |
; SP <— SP - 2, M(SP0 - 2) <—IPC (сохранение в стеке CSc:IPc) |
|
|
PUSH |
ES |
; SP <— SP - 2, M(SP0 - 4) CSc |