Файл: О.А.Калашников. Ассемблер Это Просто. Учимся программировать.pdf
Добавлен: 16.02.2019
Просмотров: 29208
Скачиваний: 1689
Часть II. Усложняем задачи
52
(12) call Out_chars
(13) inc al
(14) loop Next_screen
(15) mov ah,10h
(16) int 16h
(17) int 20h
(18) Out_chars proc
(19) mov dx,cx
(20) mov cx,2000
(21) Next_face:
(22) mov es:[di],ax
(23) add di,2
(24) loop Next_face
(25) mov cx,dx
(26) ret
(27) Out_chars endp
(28) CSEG ends
(29) end Start
Строки (01)—(08), (15)—(17) и (28), (29) опускаем. Вопросов по ним быть не
должно.
В строке (09) заносим в
cx
число 254, сообщающее, сколько раз будет выпол-
няться основной цикл. Строки (10) и (14) — "голова" и "хвост" нашего основного
цикла соответственно. Значение
di
будет меняться во вложенной процедуре, по-
этому нам необходимо будет его постоянно аннулировать (строка (11)). В строке
(12) вызываем процедуру, выводящую на экран символ, код которого находится
в
al
(при первом проходе цикла это будет символ "рожица" с кодом 01). Все! Те-
перь на экран будет выведен символ с кодом 01. При этом
di
будет равно 2001, по-
этому нам и нужно его постоянно обнулять.
Далее увеличим на единицу код символа, который находится в
al
. Во второй раз
al
будет содержать 02 — тоже "рожица", но немного другого вида (строка (13)).
Затем уменьшим счетчик на 1 и перейдем к заполнению экрана кодом 02 (строка
(14)). И так далее. Всего 254 раза.
Теперь рассмотрим работу самой процедуры. В строке (19) сохраняем содержи-
мое регистра
cx
(просто перенесем его в
dx
), т. к. он будет изменен во вложенном
цикле ниже. Строки (21) и (24) — "голова" и "хвост" вложенного цикла, который
будет выполняться 2000 раз (именно столько символов вмещается на экране в тек-
Глава 5. Подпрограммы
53
стовом режиме 80 на 25). Именно это число заносим в строке (20) в регистр
cx
. Те-
перь осталось только восстановить
cx
(строка (25)) и выйти из подпрограммы (26).
Итак, у нас здесь два цикла: основной и вложенный. Основной выполняется
254 раза, а вложенный — 2000 раз. Итого: 2000 254 = 508 000. Теперь представь-
те, сколько работы выполняет процессор, и обратите внимание, как быстро работа-
ет программа. На современных компьютерах вы не успеете заметить молниенос-
ный вывод всех символов. Очень быстро работает, хотя это далеко и не
оптимальный алгоритм.
Для исследования данного примера настоятельно рекомендуем вам воспользо-
ваться помощью отладчика. Лучший вариант в данном случае — AFD.
5.4. Несколько слов об отладчике AFD
AFD, безусловно, не совсем актуален для современных программ по следующим
причинам:
не поддерживает 32-разрядные регистры;
распознает только инструкции 8086—80186 процессоров, а также сопроцессора
8087;
не поддерживает форматы PE и NE (Windows).
Зато у него есть преимущества:
удобен и прост в использовании;
показывает в одном окне регистры, код, память и др.;
идеально подходит начинающим программистам для исследования простых
программ.
Для изучения работы программ в DOS его больше, чем достаточно. Скачать от-
ладчик AFD можно на сайте http://www.Kalashnikoff.ru, приблизительный раз-
мер — 64 Кбайта.
Скачивайте, пробуйте, изучайте, экспериментируйте!
Глава 6
Работа со стеком
6.1. Стек
Стек (стэк, stack) — это специально отведенная область памяти для хранения
промежуточных (временных) данных.
Теперь попробуем разобраться, что именно это такое. Будем считать, что сегмент
"растет" сверху вниз:
0000
0001
0002
...
FFFE
FFFF
То есть мы как бы погружаемся под землю. Таким образом — сверху вниз —
выполняется программа, если в ней, безусловно, не встречаются инструкции типа
jmp
,
call
и т. п. Стек же наоборот пополняется снизу вверх. Вершина стека —
0FFFFh
, а низ (дно) —
0000h
. Когда мы вызываем какую-нибудь подпрограмму ко-
мандой
call
, процессор заносит в стек адрес следующей за командой
call
инст-
рукции. Как только подпрограмма отработала, из стека извлекается сохраненный
адрес возврата, после чего происходит переход по этому адресу.
Следить за стеком позволяет пара регистров
ss:sp
. Многие уже, наверное, заме-
тили, что при запуске какой-нибудь COM-программы регистр
sp
равен
0FFFEh
,
а сегментный регистр
ss
, как уже неоднократно упоминалось, равен нашему един-
ственному сегменту
CSEG
(как, собственно, и
cs
,
ds
,
es
).
Теперь вам необходимо вооружиться отладчиком. Давайте рассмотрим выше-
сказанное на примере. Напечатайте программу из листинга 6.1 в редакторе.
Листинг 6.1. Исследование работы стека
CSEG segment
assume cs:CSEG, ds:CSEG, es:CSEG, ss:CSEG
org 100h
begin:
call Our_proc
Глава 6. Работа со стеком
55
int 20h
Our_proc proc
ret
Our_proc endp
CSEG ends
end Begin
Запускаем отладчик и сразу смотрим на пару регистров
ss:sp
.
sp=0FFFEh
, т. е.
указываем на вершину стека (рис. 6.1).
Рис. 6.1. Инициализация стека при старте программы
Теперь заходим в процедуру. Для CodeView следует нажать клавишу <F8>, для
AFD — <F1>. Регистр
sp
изменился! Он уменьшился на 2. Компьютер поместил
в стек адрес возврата из процедуры на инструкцию
int 20h
. Проще говоря, коман-
да
call
передала управление на метку
Our_proc
, поместив при этом в стек адрес
возврата из этой подпрограммы. В нашем случае это число 103 (рис. 6.2).
Нажимаем еще раз <F8>/<F1>.
sp
опять изменился. Но теперь он увеличился на 2,
стал равным
0FFFEh
. То есть команда
ret
"вытащила" из стека предварительно со-
храненный адрес возврата
0103h
и продолжила работу. А по этому адресу как раз
и находится следующая за
call
инструкция
int 20h
. В данном случае принято го-
ворить, что стек выровнен. При старте программы он был равен
0FFFEh
и остался
равен этому числу перед выходом —
0FFFEh
(рис. 6.3).
Мы рассмотрели один из вариантов использования стека непосредственно про-
цессором. Но на этом мы не остановимся и рассмотрим, какие возможности дает
стек программисту и как его можно использовать в своей программе.
Часть II. Усложняем задачи
56
Рис. 6.2. Вызов процедуры с занесением в стек адреса возврата из нее
Рис. 6.3. Процедура отработала. Стек выровнен
Прежде всего, разберем два оператора, которые позволяют работать со стеком
(табл. 6.1 и 6.2).
Таблица 6.1. Оператор
push
Команда
Перевод
Назначение
Процессор
push приемник
Push
— втолкнуть
Поместить в стек число
8086
Таблица 6.2. Оператор
pop
Команда
Перевод
Назначение
Процессор
pop приемник
Pop
— вытолкнуть
Достать из стека число
8086