Ассемблер Это просто! Учимся программировать

       

Еще немного о сегментации памяти.


Давайте возьмем часть примера, который мы уже рассматривали, но кое-что в нем упустили.

(1) ... (2) mov ah,9 (3) mov dx,offset My_string (4) int 21h (5) .... (6) My_string db 'Ура!$' (7) ...

Опустим некоторые операторы: строки (1), (5) и (7). Их вы уже знаете.

В строке (3) загружаем в регистр DX АДРЕС строки в памяти. Обратите внимание на запись: mov dx,offset My_string. Вы уже знаете, что оператор mov загружает в регистр число. Например:

mov cx,125

В строке (3) мы видим пока еще неизвестный нам оператор offset. Что же он делает? И почему нельзя записать вот так:

mov dx,My_string?

Offset по-английски - это смещение. Когда, при ассемблирвании, Ассемблер дойдет до этой строки, он заменит

offset My_string на АДРЕС (смещение) этой строки в памяти. Если мы запишем

mov dx,My_string (хотя, правильнее будет

mov dx,word ptr My_string,

но об этом позже), то в DX загрузится не адрес (смещение), а первые два символа нашей строки (в данном случае "Ур"). Почему два? Вы не знаете? Потому, что DX - шестнадцатиразрядный регистр, в который можно загрузить два байта. А один символ, как вы уже знаете, всегда один байт.

Можно записать и так:

mov dl,My_string

(здесь правильнее будет

mov dl,byte ptr My_string). В этом случае что будет находится в DL? Символ "У"! Потому, что DL восьмиразрядный регистр и может хранить только один байт.

Несколько слов про записи вида

mov dl,byte ptr My_string и



mov dx,word ptr My_string.

Byte (думаю, все знают) - это байт. Word - слово (два байта). Посмотрите внимательно на приведенные выше строки. Вы заметите, что когда используется восьмиразрядный регистр (DL), мы пишем

byte. А когда шестнадцатиразрядный (DX) - word. Это указывает Ассемблеру, что мы хотим загрузить именно байт либо слово.

Вспомним, что в DOS для формирования адреса используется сегмент и смещение. Данный пример - не исключение. Для формирования адреса строки "Ура!$" используется пара регистров DS (сегмент) и DX (смещение). Почему же мы ничего не загружаем в DS? Дело в том, что при загрузке *.com-программы в память (а мы пока создаем только такие), все сегментные регистры принимают значение равное тому сегменту, в который загрузилась наша программа (в т.ч. и DS). Поэтому нет необходимости загружать в DS сегмент строки (он уже загружен). Программа типа *.com всегда занимает один сегмент, поэтому размер программ такого типа ограничен 64 килобайтами. Помните, почему?


Программы, написанные на "чистом" Ассемблере, очень компактны. И 64 килобайта для них довольно большой объем.

Помнится, писал когда-то я антивирусную оболочку типа "а-ля Нортон Коммандер" на Ассемблере. Так она заняла у меня примерно 40 килобайт, хотя и не выполняла всех функций NC, но делала кое-что другое. Еще пример: Volcov Commander поздних версий. Практически копия NC, но занимает всего 64000 байт (в отличие от Нортона). Я подозреваю, что писали ее если не на "чистом" Ассемблере, то хотя бы большую часть кода так точно. Да и работает Volkov гораздо быстрее Нортона.

Вернемся. Если есть желание поэксперементировать, то попробуйте перед вызовом 21h-ого прерывания загрузить в DS какое-нибудь число. Например, так:

... mov dx,offset My_string mov ax,10h mov ds,ax mov ah,9 int 21h ...

Вы увидите, что программа выведет не нашу строку, а какой-то "мусор" на экран, хотя в DX мы и загружаем адрес нашей строки, но сегмент другой. Только не забудьте восстановить DS после выполнения данной функции:

mov ax,cs mov ds,ax

Итак, полное описание:

Функция 09h прерывания 21h - вывод строки символов на экран в текущую позицию курсора:

Вход: AH = 09h

DS:DX = адрес ASCII-строки символов, заканчивающийся '$'
Выход: ничего
Ну, разобрались с этим окончательно...

Новые операторы.




Содержание раздела