Команды PUSH и POP дают доступ только к вершине стека, но иногда необходим доступ к другим, более низким элементам стека. Для этого необходимо использовать косвенную адресацию по регистру-модификатору BP.
Сначала следует записать в BP адрес какого-либо элемента стека, например, вершины:
MOV BP, SP
Теперь команда
MOV AX, [BP]
поместит в AX содержимое последней ячейки стека. Команда
MOV AX, [BP+2]
Поместит в AX содержимое предпоследней ячейки (ее адрес равен адресу вершины стека плюс 2), а команда
MOV AX,[BP+4]
– содержимое третьей с конца ячейки и т.д.
Регистр BP выбран для адресации доступа специально. Регистр SP нельзя использовать, таким образом, т.к. SP не относится к регистрам-модификаторам (т.е. выражение [SP+4] будет ошибкой). Также, если в качестве регистра модификатора используется BP, по умолчанию в качестве сегментного регистра при определении абсолютного физического адреса будет использоваться регистр SS (т.е. в команде его можно не указывать). Если использовать другой регистр модификатор, то регистр SS следует указывать явно, например:
MOV AX, SS:[BX+4]
Таким образом, вариант с регистром BP более удобен.
2.3. Задание на лабораторную работу
1) Написать программу, содержащую изложенные выше приемы работы со стеком (кроме проверок заполнения стека). Выполнить программу в отладчике и проследить за изменениями значений SP и содержимого стека, отображаемого отладчиком.
2) Добавить в созданную программу фрагмент, в цикле записывающий в стек произвольное значение до тех пор, пока стек не окажется полон. Потом в цикле должно извлекаться по значению из стека, пока он не вернется в исходное состояние (что в нем было изначально, то и должно остаться).
3) Написать программу, использующую стек для организации алгоритма проверки корректности расстановки скобок в арифметическом выражении (с точки зрения парности открывающих и закрывающих скобок). В простейшем варианте можно считать, что допускаются только круглые скобки. По желанию можно усложнить задачу, рассматривая также квадратные и фигурные скобки. Программа должна выдавать на экран соответствующее сообщение.
Задача должна быть реализована примерно по следующему алгоритму:
– задание строки — арифметического выражения с помощью директивы DB.
– просмотр строки символ за символом и занесение в стек каждой встреченной открывающей скобки.
– если найдена закрывающая скобка, должна быть осуществлена проверка, лежит ли на вершине стека соответствующая открывающая (и было ли внесено в стек, что-либо с момента начала проверки). Если нет – обнаружена некорректность. Если да, то эту открывающую скобку следует удалить из стека.
– после просмотра всей строки обязательно следует проверить не осталась ли в стеке открывающая скобка. Если осталась – обнаружена некорректность.
Кроме того важно, чтобы в момент возврата из программы в ОС (команда RET) стек был точно в том же состоянии, в каком он оказался после выполнения обязательных команд при входе в программу из системы (в стек было отправлено содержимое регистра DS и значение 0).
2.4. Отчет по лабораторной работе
Отчет по лабораторной работе должен содержать:
– текст полученного задания;
– тексты программ с комментариями (в комментариях отобразить изменения содержимого регистров и флагов по ходу выполнения программы).
Кроме отчета и демонстрации работающей программы студент отвечает на вопросы по теоретической части, относящейся к данной лабораторной работе.
3 .ЛАБОРАТОРНАЯ РАБОТА № 5
ПРОЦЕДУРЫ
3.1. Общие методические указания по выполнению лабораторной работы
Цели работы:
– Знакомство с методами организации процедур в программах на ассемблере.
– Изучение особенностей передачи параметров из основной программы в процедуры.
Среда выполнения:
Интерпретатор команд DOS, ассемблер MASM, интерактивный отладчик AFD.
3.2. Теоретические сведения
3.2.1. Дальние переходы
В предыдущих лабораторных работах рассматривались программы с одним сегментом команд. Однако в общем случае в программе может быть столь много команд, что они не вместятся в один сегмент памяти (их суммарный размер может превзойти 64Кб). В таком случае (либо по какой-то иной причине) в программе описывается несколько сегментов команд, например:
Cl SEGMENT
ASSUME CS: Cl, …
START: MOV AX, 0
…
JMP FAR PTR L ;goto L
…
Cl ENDS
C2 SEGMENT
ASSUME CS: C2
L: INC BX
…
C2 ENDS
В начале каждого сегмента команд должна быть указана директива ASSUME, в которой (помимо прочего) сегментному регистру CS ставится в соответствие данный сегмент (именно по этой информации ассемблер узнает, что текущий сегмент является сегментом команд). В противном случае, встретив первую же метку, ассемблер зафиксирует ошибку.
В ПК адрес команды, которая должна выполняться следующей, задается парой регистров CS и IP: регистр CS указывает на начало сегмента памяти, в котором находится эта команда, а в регистре IP находится смещение этой команды, отсчитанное от начала данного сегмента.
Если меняется только IP, то это означает переход внутри сегмента. Такие переходы называются близкими или внутрисегментными. Но если в программе имеется несколько сегментов команд, тогда возникает потребность в переходах из одного такого сегмента в другой (например, из сегмента О на метку L сегмента С2). Такие переходы называются дальними или межсегментными. При этих переходах меняется значение и регистра CS, и регистра IP: CS устанавливается на начало сегмента с меткой (CS:=C2), а в IP записывается смещение метки внутри ее сегмента (IP:=offset L).
Существуют два варианта дальнего перехода: прямой и косвенный (но и в том и в другом случае переход всегда является безусловным).
Команда прямого перехода записывается следующим образом:
JMP FAR PTR
Слово FAR (дальний) указывает ассемблеру, что метка дальняя, что она находится в другом сегменте команд. По этой команде регистр CS устанавливается на начало того сегмента, в котором эта метка находится, а в регистр IP записывается смещение этой метки внутри данного сегмента:
CS:=seg ; IP:=offset
Так, в примере, приведенном выше, указан дальний переход на метку L.
Команда дальнего косвенного перехода записывается следующим образом:
JMP
В этой команде указывается адрес двойного слова, в котором должен находиться абсолютный адрес перехода в виде адресной пары seg:ofs, записанной в перевернутом виде: смещение ofs должно быть записано в два младших байта двойного слова, а номер сегмента seg – в два старших байта. Команда делает переход по этому абсолютному адресу, т. е. записывает в регистр CS величину seg, а в регистр IP — величину ofs:
CS:= [двойное слово+2]; IP:= [двойное слово]
Пример:
X DD L ;X:offset L, X+2:seg L
…
JMP X ;goto L(CS:=seg L,IP:=offset L)
Имя X, указанное в команде, должно быть описано заранее, чтобы ассемблер, встретив команду перехода, интерпретировал X как двойное слово. В противном случае, следует явно указать, что имя обозначает переменную размером в двойное слово. Для этого используется оператор PTR:
JMP DWORD PTR X
3.2.2. Подпрограммы (процедуры)
Довольно часто в программах, особенно больших, приходится несколько раз решать одну и ту же подзадачу и потому приходится многократно выписывать одинаковую группу команд, решающих эту подзадачу. Чтобы избежать повторного выписывания такой группы команд, ее обычно выписывают один раз и оформляют соответствующим образом, а затем в нужных местах программы просто передают управление на эти команды, которые, проработав, возвращают управление обратно. Такая группа команд, которая решает некоторую подзадачу и которая организована таким образом, что ее можно использовать любое число раз и из любых мест, называется подпрограммой. По отношению к подпрограмме остальную часть программы принято называть основной программой. Аналог подпрограмм на языках высокого уровня называется процедурой.
Размещение подпрограммы
Обычно подпрограммы (п/п) размещают либо в конце сегмента команд за командой FINISH (см. рисунок а), либо в самом начале этого сегмента — перед той командой, с которой должно начинаться выполнение программы (см. рисунок б). В больших программах подпрограммы нередко размещают в отдельном сегменте команд (см. рисунок в).
Варианты размещения подпрограммы в тексте программы
Оформление подпрограммы
Описание подпрограммы в виде процедуры выглядит так:
PROC
ENDP
Перед телом процедуры (ее командами) ставится директива PROC (procedure), а за ним — директива ENDP (end of procedure). В обеих этих директивах указывается одно и то же имя — имя, присвоенное процедуре.
У директивы PROC есть параметр — это либо NEAR (близкий), либо FAR (дальний). Параметр может и отсутствовать, тогда считается, что он равен NEAR (в связи с этим параметр NEAR обычно не указывается). При параметре NEAR или при отсутствии параметра процедура называется близкой, при параметре FAR — дальней. К близкой процедуре можно обращаться только из того сегмента команд, где она описана, и нельзя обращаться из других сегментов, а к дальней процедуре можно обращаться из любых сегментов команд (в том числе и из того, где она описана). В этом и только в этом различие между близкими и дальними процедурами.
Статьи к прочтению:
Jessie J — Price Tag ft. B.o.B
Похожие статьи:
-
Массивы и кортежи • Простые массивы • Многомерные массивы • Зубчатые массивы • Класс Array • Массивы в качестве параметров • Перечисления • Кортежи •…
-
Использование стека для передачи параметров
Стек часто используется при обращении к процедурам для передачи параметров в процедуры. При передаче управления процедуре процессор автоматически…