суббота, 13 июня 2009 г.

Прямая адресация

Данный вид адресации иногда, так же, называют - прямая адресация памяти.

Адресуется память; адрес ячейки памяти (слова или байта) указывается в команде (обычно в символической форме) и поступает в код команды:

;Сегмент данных
meml dw 0 ;Слово памяти содержит 0
mem2 db 230 ;Байт памяти содержит 230
;Сегмент команд
inc meml ;Содержимое слова meml увеличивается на 1
mov DX,meml ; Содержимое слова с именем menu загружается в DX
mov AL,mem2 ; Содержимое байта с именем mem2 загружается в АL

Сравнивая этот пример с предыдущим, мы видим, что указание в команде имени ячейки памяти обозначает, что операндом является содержимое этой ячейки; указание имени ячейки с описателем offset - что операндом является адрес ячейки.
Прямая адресация памяти на первой взгляд кажется простой и наглядной. Если мы хотим обратиться, например, к ячейке mem1, мы просто указываем ее имя в программе. В действительности, однако, дело обстоит сложнее. Вспомним, что адрес любой ячейки состоит из двух компонентов: сегментного адреса и смещения. Обозначения mem1 и mem2 в предыдущем примере, очевидно, являются смещениями. Сегментные же адреса хранятся в сегментных регистрах. Однако сегментных регистров четыре: DS, ES, CS и SS. Каким образом процессор узнает, из какого регистра взять сегментный адрес, и как сообщить ему об этом в программе?
Процессор различает группу кодов, носящих название префиксов. Имеется несколько групп префиксов: повторения, размера адреса, размера операнда, замены сегмента. Здесь нас будут интересовать префиксы замены сегмента.
Команды процессора, обращающиеся к памяти, могут в качестве первого байта своего кода содержать префикс замены сегмента, с помощью которого процессор определяет, из какого сегментного регистра взять сегментный адрес. Для сегментного регистра ES код префикса составляет 26h, для SS - 36h, для CS - 2Eh. Если префикс отсутствует, сегментный адрес берется из регистра DS (хотя для него тоже предусмотрен свой префикс).
Если в начале программы с помощью директивы assume указано соответствие сегменту данных сегментного регистра DS

assume DS:data

то команды обращения к памяти транслируются без какого-либо префикса, а процессор при выполнении этих команд берет сегментный адрес из регистра DS.
Если в директиве assume указано соответствие сегмента данных регистру ES

assume ES:data

(в этом случае сегмент данных должен располагаться перед сегментом команд), то команды обращения к содержимому полей этого сегмента транслируются с добавлением префикса замены для сегмента ES. При этом предложения программы выглядят обычным образом; в них по-прежнему просто указываются имена полей данных, к которым производится обращение.
Небольшой примерчик...

И его листинг...

Однако в ряде случаев префикс замены сегмента должен указываться в программе в явной форме. Такая ситуация возникает, например, если данные расположены в сегменте команд, что типично для резидентных обработчиков прерываний. Для обращения к таким данным можно, конечно, использовать регистр DS, если предварительно настроить его на сегмент команд, но проще выполнить адресацию через регистр CS, который и так уже настроен должным образом. Если в сегменте команд содержится поле данных с именем mem, то команда чтения из этого поля будет выглядеть следующим образом:

mov BX,CS:mem

В этом случае транслятор включит в код команды префикс замены для сегмента CS (2Eh).

До сих пор мы обсуждали адресацию ячеек, содержащихся в сегментах данных программы. Однако часто бывает нужно обратиться к памяти вне пределов программы: к векторам прерываний, системным таблицам, видеобуферу и т.д. Разумеется, такое обращение возможно только если мы знаем абсолютный адрес интересующей нас ячейки. В этом случае необходимо сначала настроить один из сегментных регистров на начато интересующей нас области, после чего можно адресоваться к ячейкам по их смещениям.
Пусть требуется вывести в левый верхний угол экрана несколько символов, например, два восклицательных знака. Эту операцию можно реализовать с помощью следующих команд:

mov AX,0B800h ;Сегментный адрес видеобуфера
mov ES,AX ;Отправим его в ES
mov byte ptr ES:0,'!' ;Отправим символ на 1-е знакоместо экрана
mov byte ptr ES:2,'!' ;Отправим символ на 2-е знакоместо экрана

Настроив регистр ES на сегментный адрес видеобуфера B800h, мы пересылаем код знака "!" сначала по относительному адресу 0 (в самое начало видеобуфера, в байт со смещением 0), а затем на следующее знакоместо, имеющее смещение 2 (в нечетных байтах видеобуфера хранятся атрибуты символов, т.е. цвет символов и фона под ними). В обеих командах необходимо с помощью обозначения ES: указать сегментный регистр, который используется для адресации памяти. Встретившись с этим обозначением, транслятор включит в код команды префикс замены сегмента, в данном случае код 26h.
Примерчик для закрепления материала...

И его листинг...

В приведенном примере мы снова столкнулись с использованием атрибутивного оператора byte ptr, который позволяет в явной форме задать размер операнда. Однако если раньше этот оператор использовался, чтобы извлечь байт из данного, объявленного, как слово, то здесь его назначение иное. Транслятор, обрабатывая команду

mov byte ptr ES:0,'!'

не имеет возможности определить размер операнда-приемника. Разумеется, видеобуфер, как и любая память, состоит из байтов, однако надо ли рассматривать эту память, как последовательность байтов или слов? Команда без явного задания размера операнда

mov ES:0,'!'

вызовет ошибку трансляции, так как ассемблер не сможет определить, надо ли транслировать это предложение, как команду пересылки в видеобуфер байта 21h, или как команду пересылки слова 0021h.
Между прочим, на первый взгляд может показаться, что в обсуждаемой команде достаточно ясно указан размер правого операнда, так как символ (в данном случае "!") всегда занимает один байт. Однако транслятор, встретив обозначение "!", сразу же преобразует его в код ASCII этого символа, т.е. в число 21h, и уже не знает, откуда это число произошло и какой размер оно имеет.
Стоит еще отметить, что указание в команде описателя word ptr

mov word ptr ES:0,'!'

не вызовет ошибки трансляции, но приведет к неприятным результатам. В этом случае в видеобуфер будет записано слово 002lh, которое заполнит байт 0 видеобуфера кодом 21h, а байт 1 кодом 00h. Однако атрибут 00h обозначает черный цвет на черном фоне, и символ на экране виден не будет (хотя и будет записан в видеобуфер).
При желании можно избавиться от необходимости вводить описатель размера операнда. Для этого надо пересылать не непосредственное данное, а содержимое регистра:

mov AL,'!'
mov ES:0,AL

Здесь операндом-источником служит регистр AL, размер которого (1 байт) известен, и размер операнда-приемника определять не надо. Разумеется, команда

mov ES:0,AX

заполнит в видеобуфере не байт, а слово.
Для адресации к видеобуферу в вышеприведенном примере использовался сегментный регистр дополнительных данных ES. Это вполне естественно, так как обычно регистр DS служит для обращения к полям данных программы, а регистр ES как раз и предназначен для адресации всего остального. Однако при необходимости можно было воспользоваться для записи в видеобуфер регистром DS:

mov AX,0B800h ;Сегментный адрес
mov DS,AX ; видеобуфера в DS
mov byte ptr DS:0, '!' ;Символ в видеобуфер

Любопытно, что хотя обозначение DS: здесь необходимо, транслятор не включит в код команды префикс замены сегмента, так как команда без префикса выполняет адресацию по умолчанию через DS.
Если, однако, по умолчанию выполняется адресация через DS, то нельзя ли опустить в последней команде обозначение сегментного регистра? Нельзя, так как обозначение DS: число указывает, что число является не непосредственным операндом, а адресом операнда. Команда (неправильная)

mov 6,10

должна была бы переслать число 10 в число 6, что, разумеется, лишено смысла и выполнено быть не может. Команда же

mov DS:6,10

пересылает число 10 по относительному адресу 6, что имеет смысл. Таким образом, обозначение сегментного регистра с двоеточием перед операндом говорит о том, что операнд является адресом. В дальнейшем мы еще столкнемся с этим важным правилом.

Комментариев нет: