Файл: О.А.Калашников. Ассемблер Это Просто. Учимся программировать.pdf
Добавлен: 16.02.2019
Просмотров: 29204
Скачиваний: 1689
Глава 3. Сегментация памяти в реальном режиме
35
функцию
09h
прерывания
21h
(вывод строки на экран в текущую позицию кур-
сора);
функцию
10h
прерывания
16h
(ожидание нажатия клавиши).
В принципе, довольно много, чтобы начать серьезное и интенсивное обучение
языку ассемблера. Если вы усвоили как минимум 75% материала из данной части,
то, можно сказать, мы добились того, чего хотели. Если же вы чувствуете, что мно-
гое непонятно, то попробуйте прочитать все еще раз с самого начала, воспользуй-
тесь отладчиком. Мы впоследствии будем еще не раз возвращаться к материалу,
изложенному в части I. Если вы что-то не поняли сейчас, то, надеемся, поймете со
временем. Самое интересное и увлекательное — впереди!
В случае если вопросов по пройденному материалу у вас не возникло, то можете
смело приступать к изучению части II "Усложняем задачи".
Увлекательного вам чтения!
Ч А С Т Ь I I
Усложняем задачи
Глава 4
Создание циклов
4.1. Еще немного о сегментации памяти
Рассмотрим часть примера из листинга 3.1. Кое-что в этом примере мы не раз-
бирали:
(01) ...
(02) mov ah,9
(03) mov dx,offset My_string
(04) int 21h
(05) ...
(06) My_string db 'Ура!$'
(07) ...
В строке (03) загружаем в регистр
dx
адрес строки в памяти. Обратите внимание
на запись:
mov dx,offset My_string
. Вспоминаем, что оператор
mov
загружает
в регистр число. Например:
mov cx,125
В строке (03) мы видим пока еще неизвестный нам оператор
offset
. Что же он
делает? И почему нельзя записать таким образом:
mov dx,My_string
?
4.1.2. Введение в адресацию
В переводе с английского "offset" — это смещение. Когда ассемблер
(MASM/TASM) обрабатывает нашу программу и доходит до строки (03), он заме-
няет
offset My_string
на адрес (смещение) этой строки в памяти. Если мы запи-
шем
mov dx,My_string
(хотя корректно будет
mov dx,word ptr My_string
, но об
этом позже), то в
dx
загрузится не адрес (смещение), а первые два символа нашей
строки (в данном случае "Ур"). Два символа, потому что
dx
— 16-разрядный ре-
гистр, в который можно загрузить два байта. А один символ, как вы уже знаете,
всегда занимает один байт. Можно записать и так:
mov dl,My_string
(здесь кор-
ректно будет
mov dl,byte ptr My_string
). В этом случае, в
dl
будет находиться
символ "У", т. к.
dl
— 8-разрядный регистр и может хранить только один байт.
Несколько слов про записи вида
mov dl,byte ptr My_string
и
mov dx,word ptr
My_string
.
Byte
— это байт,
word
— слово (два байта). Посмотрите внимательно
Часть II. Усложняем задачи
40
на приведенные ранее строки. Вы заметите, что когда используется 8-разрядный
регистр (
dl
), мы пишем
byte
. А когда 16-разрядный (
dx
) —
word
. Это указывает
ассемблер-программе, что именно мы хотим загрузить — байт или слово.
Вспомним, что в реальном режиме для формирования адреса используются сег-
мент и смещение. Данный пример — не исключение. Для формирования адреса
строки
"Ура!$"
используется пара регистров
ds
(сегмент) и
dx
(смещение).
Почему же тогда мы ничего не загружаем в
ds
? Дело в том, что при загрузке
COM-программы в память (а мы пока создаем только такие), все сегментные реги-
стры принимают значение, равное значению сегмента, в который загрузилась наша
программа (в том числе и
ds
). Поэтому нет необходимости загружать в
ds
сегмент
строки (он уже загружен). Программа типа COM всегда занимает один сегмент,
в котором должен уместиться код, данные и пр. А размер COM-файлов ограничен
64 Кбайт (65 536 байтами). Программы, написанные на "чистом" ассемблере, очень
компактны, и 64 Кбайт для них — довольно большой объем.
Приведем несколько примеров. В свое время нами разрабатывалась антиви-
русная файловая оболочка типа Norton Commander на ассемблере (некоторое ее
подобие будем изучать в части III). В итоге размер приложения составил всего
36 Кбайт. Несмотря на такой небольшой размер, оболочка выполняла многие
функции Norton Commander (хотя некоторых функций Norton Commander в ней
не было).
В качестве другого примера можно привести Volcov Commander первых версий.
Практически копия Norton Commander, но занимает всего 64 000 байт (в отличие от
Norton). Если Volcov Commander писали и не на "чистом" ассемблере, то хотя бы
большую часть кода так точно. Да и работает Volcov гораздо быстрее, чем Norton.
Но продолжим изучение. В качестве эксперимента попробуйте перед вызовом
прерывания
21h
загрузить в
ds
любое другое число. Например, так:
...
mov dx,offset My_string
mov ax,10h
mov ds,ax
mov ah,9
int 21h
...
My_string db 'Hello!$'
...
Вы увидите, что программа выведет на экран не строку "Hello!", а какой-то "му-
сор", хотя в
dx
мы правильно загружаем адрес (смещение) нашей строки, но сег-
мент-то в
ds
другой (рис. 4.1).
Давайте вспомним, что функция
09h
прерывания
21h
выводит строку, адрес ко-
торой задается в регистрах
ds:dx
. На рис. 4.1 отображено состояние программы,
при котором мы умышленно загружаем в
ds
число 10. Обратите внимание, что
в окне
Memory1
отображены данные, расположенные в памяти по адресу
0A09:010Ch
,
Глава 4. Создание циклов
41
а именно строка
Hello!
, которую должна вывести программа. Это и есть полный
адрес выводимой строки, включающий в себя сегмент и смещение. Однако про-
грамма перед вызовом прерывания
21h
загружает в
ds
число 10. Следовательно,
операционная система начнет выводить символы, находящиеся по адресу
ds:dx
—
0010:010Ch
, что является логической ошибкой в программе.
Рис. 4.1. Некорректный вывод строки
Если все же возникает необходимость в выводе строки, расположенной не в том
сегменте, в который загружена сама программа, а в другом (такое требуется часто),
то после выполнения всех необходимых операций следует восстановить сегмент-
ный регистр данных
ds
:
...
mov ax,cs
mov ds,ax
...
В табл. 4.1 приведено полное описание функции вывода строки на экран.
Таблица 4.1. Функция
09h
прерывания
21h
— вывод строки символов
на экран в текущую позицию курсора
Вход
Выход
AH = 09h
DS:DX
= адрес ASCII-строки символов, заканчивающийся $
Ничего
С этим также разобрались, хотя возвращаться к сегментным регистрам мы бу-
дем еще не один раз.