CodeNet / Языки программирования / Ассемблер / СПРАВОЧНИК по системе программирования ТУРБО АССЕМБЛЕР 2.0
СПРАВОЧНИК по системе программирования ТУРБО АССЕМБЛЕР 2.0
Режимы адресации к памяти
-----------------------------------------------------------------
Как при использовании операнда в памяти задать ту ячейку па-
мяти, с которой вы хотите работать? Очевидный ответ состоит в
том, чтобы присвоить нужной переменной в памяти имя (как мы это
делали в последнем разделе). С помощью, например, следующих опе-
раторов вы можете вычесть переменную памяти Debts (долги) из пе-
ременной памяти Assets (имущество):
.
.
.
Assets DW ?
Debts DW ?
.
.
.
mov ax,[Debts]
sub [Assets],ax
.
.
.
Однако адресация к памяти имеет и более глубокий смысл, ко-
торый не бросается в глаза. Предположим, у вас имеется символьная
строка с именем CharString, содержащая буквы ABCDEFGHIGKLM, кото-
рые начинаются в сегменте данных со смещения 100, как показано на
Рис. 4.1. Каким образом можно считать девятый символ (I), который
расположен по адресу 108? В языке Си вы можете просто использо-
вать оператор:
C = CharString[8];
(в Си элементы нумеруются с 0), а в Паскале:
C := CharString[9];
Как же это можно сделать в Ассемблере? Прямая ссылка на
строку CharString здесь, конечно, не подходит, так как первым
символом является символ A.
. .
. .
| |
|--------------|
TASM2 #1-5/Док = 137 =
99 | ? |
|--------------|
CharString --------------> 100 | 'A' |
|--------------|
101 | 'B' |
|--------------|
102 | 'C' |
|--------------|
103 | 'D' |
|--------------|
104 | 'E' |
|--------------|
105 | 'F' |
|--------------|
106 | 'G' |
|--------------|
107 | 'H' |
|--------------|
108 | 'I' |
|--------------|
109 | 'J' |
|--------------|
110 | 'K' |
|--------------|
111 | 'L' |
|--------------|
112 | 'M' |
|--------------|
113 | 0 |
|--------------|
114 | ? |
|--------------|
. .
. .
Рис. 5.1 Ячейки памяти со строкой символов CharString.
В действительности язык Ассемблера обеспечивает несколько
различных способов адресации к строкам символов, массивам и буфе-
рам данных. Наиболее простой способ состоит в том, чтобы считать
девятый по счету символ строки CharString:
.
.
.
.DATA
CharString DB 'ABCDEFGHIJKLM'
.
.
.
.CODE
.
.
.
mov ax,@Data
mov ds,ax
mov al,[CharString+8]
.
.
.
В данном случае это тоже самое, что:
mov al,[100+8]
так как CharString начинается со смещения 100. Все, что заключено
в квадратные скобки, интерпретируется Турбо Ассемблером, как ад-
рес, поэтому смещение CharString и 8 складывается и используется
в качестве адреса памяти. Инструкция принимает вид:
mov al,[108]
как показано на Рис. 5.2.
. .
. .
| |
|--------------|
99 | ? |
|--------------|
CharString --------------> 100 | 'A' |
|--------------|
101 | 'B' |
|--------------|
102 | 'C' |
|--------------|
103 | 'D' |
|--------------|
104 | 'E' |
|--------------|
105 | 'F' |
|--------------|
106 | 'G' |
|--------------|
107 | 'H' |-------
|--------------| |
CharString+8 -----------> 108 | 'I' | |
|--------------| V
109 | 'J' | --------
|--------------| | |
110 | 'K' | --------
|--------------| AL
111 | 'L' |
|--------------|
112 | 'M' |
|--------------|
113 | 0 |
|--------------|
114 | ? |
|--------------|
. .
. .
Рис. 5.1 Адресация строки символов строки CharString.
Такой тип адресации, когда ячейка памяти задается ее именем,
плюс некоторая константа, называется непосредственной (прямой)
адресацией. Хотя непосредственная адресация - это хороший метод,
она не отличается достаточной гибкостью, поскольку обращение вы-
полняется каждый раз по одному и тому же адресу памяти. Поэтому
давайте рассмотрим другой, более гибкий путь адресации памяти.
Рассмотрим следующий фрагмент программы, где в регистр AL
также загружается девятый символ CharString:
.
.
.
mov bx,OFFSET CharString+8
mov al,[bx]
.
.
.
В данном примере для ссылки на девятый символ используется
регистр BX. Первая инструкция загружает в регистр BX смещение
CharString (вспомните о том, что операция OFFSET возвращает сме-
щение метки в памяти), плюс 8. (Вычисление OFFSET и сложение для
этого выражения выполняется Турбо Ассемблером во время ассембли-
рования.) Вторая инструкция определяет, что AL нужно сложить с
содержимым по смещению в памяти, на которое указывает регистр BX
(см. Рис. 5.3).
mov al,[108]
как показано на Рис. 5.2.
. .
. .
| |
|--------------|
99 | ? |
|--------------|
CharString --------------> 100 | 'A' |
|--------------|
101 | 'B' |
|--------------|
102 | 'C' |
|--------------|
103 | 'D' |
|--------------|
104 | 'E' |
|--------------|
105 | 'F' |
|--------------|
106 | 'G' |
|--------------|
107 | 'H' |-------
----------- |--------------| |
BX | 108 | ------------> 108 | 'I' | |
----------- |--------------| V
109 | 'J' | --------
|--------------| | |
110 | 'K' | --------
|--------------| AL
111 | 'L' |
|--------------|
112 | 'M' |
|--------------|
113 | 0 |
|--------------|
114 | ? |
|--------------|
. .
. .
Рис. 5.3 Использование регистра BX для адресации к строке
CharString.
Квадратные скобки показывают, что в качестве операнда-источ-
ника должна быть использоваться ячейка, на которую указывает ре-
гистр BX а не сам регистр BX. Не забывайте указывать квадратные
скобки при использовании BX в качестве указателя памяти. Напри-
мер:
mov ax,[bx] ; загрузить AX из ячейки памяти,
; на которую указывает BX
и
mov ax,bx ; загрузить в AX содержимое
; регистра BX
это две совершенно различные инструкции.
Может возникнуть вопрос, зачем сначала загружать в регистр
BX смещение ячейки памяти и затем использовать BX, как указатель,
если тоже самое можно сделать с помощью одной инструкции с непос-
редственным операндом? Особое свойство регистров, используемых в
качестве указателей, состоит в том, что в отличие от инструкций,
использующих непосредственные операнды, инструкции, использующие
в качестве указателей регистры, могут ссылаться в разное время (в
процессе выполнения программы) на разные ячейки памяти.
Предположим, вы хотите определить последний символ завершаю-
щейся нулем строки CharString. Чтобы это сделать, вы должны, на-
чиная с первого символа строки CharString, найти завершающий
строку нулевой байт, затем вернуться назад на один символ и счи-
тать этот последний символ. Это невозможно сделать с помощью не-
посредственной адресации, так как строка может иметь произвольную
длину. Использование регистра BX значительно облегчает задачу:
.
.
.
mov bx,OFFSET CharString ; указывает на строку
FindLastCharLoop:
mov al,[bx] ; получить следующий
; символ строки
cmp al,0 ; это нулевой байт?
je FoundEndOfString ; да, вернуться на
; один символ
inc bx
jmp FilnLastCharLoop ; проверить следующий
; символ
FoundEndOfString:
dec bx ; снова указывает на
; последний символ
mov al,[bx] ; получить последний
. ; символ строки
.
.
Если вы собираетесь выполнять в памяти поиск символов или
слов, работать с массивами, или копировать блоки данных, вы пой-
мете, что использование регистров-указателей дает неоценимую по-
мощь.
BX - это не единственный регистр, который можно использовать
для ссылка на память. Допускается также использовать вместе с не-
обязательным значением-константой или меткой регистры BP, SI и
DI. Общий вид операндов в памяти выглядит следующим образом:
[базовый регистр + индексный регистр + смещение]
где базовый регистр - это BX или BP, индексный регистр - SI или
DI, а смещение - любая 16-битовая константа, включая метки и вы-
ражения. Каждый раз, когда выполняется инструкция, использующая
операнд в памяти, процессором 8086 эти три компоненты складывают-
ся. Каждая из трех частей операнда в памяти является необязатель-
ной, хотя (это очевидно) вы должны использовать один из трех эле-
ментов, иначе вы не получите адреса в памяти. Вот как элементы
операнда в памяти выглядят в другом формате:
BX SI
или + или + Смещение
BP DI
(база) (индекс)
Существует 16 способов задания адреса в памяти:
[смещение] [bp+смещение]
[bx] [bx+смещение]
[si] [si+смещение]
[di] [di+смещение]
[bx+si] [bx+si+смещение]
[bx+di] [bx+di+смещение]
[bp+si] [bp+si+смещение]
[bp+di] [bp+di+смещение]
где смещение - это то, что можно свести к 16-битовому постоянному
значению.
Может показаться, что 16 режимов адресации - это очень мно-
го, но если вы еще раз посмотрите на этот список, вы увидите, что
все режимы адресации получаются всего из нескольких элементов,
комбинируемых различными путями. Вот еще несколько способов, с
помощью которых можно, используя различные режимы адресации, заг-
рузить девятый символ строки CharString в регистр AL:
.
.
.
.DATA
CharString DB 'ABCDEFGHIJKLM',0
.
.
.
.CODE
mov ax,@Data
mov ds,ax
.
.
.
mov si,OFFSET CharString+8
mov al,[si]
.
.
.
mov bx,8
mov al,[Charstring+bx]
.
.
.
mov ..,OFFSET CharString
mov al,[bx+8]
.
.
.
mov si,8
mov al,[CharString+si]
.
.
.
mov bx,OFFSET CharString
mov di,8
mov al,[bx+di]
.
.
.
mov si,OFFSET CharString
mov bx,8
mov al,[si+bx]
.
.
.
mov bx,OFFSET CharString
mov si,7
mov al,[bx+si+1]
.
.
.
mov bx,3
mov si,5
mov al,[bx+CharString+si]
.
.
.
Все эти инструкции ссылаются на одну и ту же ячейку памяти -
[CharString]+8.
В данном примере можно найти несколько интересных моментов.
Во-первых, вы должны понимать, что знак плюс (+), используемый
внутри квадратных скобок, имеет специальное значение. Во время
ассемблирования Турбо Ассемблер складывает все постоянные значе-
ния (константы) внутри квадратных скобок, поэтому инструкция:
mov [10+bx+si+100],cl
принимает вид:
mov [bx+si+111],cl
После этого при реальном выполнении инструкции (во время
прогона программы) операнды в памяти складываются вместе процес-
сором 8086. Если регистр BX содержит значение 25, а SI содержит
52, то при выполнении инструкции MOV CL записывается по адресу
памяти 25 + 52 + 111 = 188. Ключевой момент состоит в том, что
базовый регистр, индексный регистр и смещение складываются вместе
процессором 8086 при выполнении инструкции. Таким образом, Турбо
Ассемблер складывает константы во время ассемблирования, а про-
цессор 8086 складывает содержимое базового регистра, индексного
регистра и смещения во время действительного выполнения инструк-
ции.
Как вы можете заметить, ни в одном из примеров мы не исполь-
зовали регистр BP. Это связано с тем, что поведение регистра BP
несколько отличается от регистра BX. Вспомните, что в то время
как регистр BX используется, как смещение внутри сегмента данных,
регистр BP используется, как смещение в сегменте стека. Это озна-
чает, что регистр BP не может обычно использоваться для адресации
к строке CharString, которая находится в сегменте данных.
Пояснение использования регистра BP для адресации к сегменту
стека приводится в Главе 4. В данный момент достаточно знать, что
регистр BP можно использовать так же, как мы использовали в при-
мерах регистр BX, только адресуемые данные должны в этом случае
находиться в стеке.
(На самом деле регистр BP можно использовать и для адресации
к сегменту данных, а BX, SI и DI - для адресации к сегменту сте-
ка, дополнительному сегменту или сегменту кода. Для этого исполь-
зуются префиксы переопределения сегментов (segment override pre-
fixes). О некоторых из них мы расскажем в Главе 10. Однако в
большинстве случаев они вам не понадобятся, поэтому пока мы прос-
то забудем об их существовании.)
Наконец, квадратные скобки, в которые заключаются непос-
редственные адреса, являются необязательными. То есть инструкции:
mov al,[MemVar]
и
mov al,MemVar
выполняют одни и те же действия. Тем не менее мы настоятельно ре-
комендуем вам заключать все ссылки на память в квадратные скобки.
Это поможет избежать путаницы и сделает вашу программу более яс-
ной и понятной. Несомненно, вы столкнетесь с программами, в кото-
рых квадратные скобки отсутствуют, так как некоторые все же счи-
тают, что в таком виде программа воспринимается лучше. Это, в об-
щем, дело вкуса, но если вы выберете стиль адресации по одной
ячейке памяти и будете содержательно его использовать, вам будет
легче писать программы.
Вы можете использовать также такую форму адресации к памяти:
mov al,CharString[bx]
или даже
mov al,CharString[bx][si]+1
Все эти формы представляют собой то же самое, что размещение
отдельных элементов адресации к памяти в одной паре квадратных
скобок и разделение их знаком плюс. Таким образом, последний опе-
ратор эквивалентен оператору:
mov al,[charString+bx+si+1]
Здесь снова нужно выбрать ту форму записи, которая вам боль-
ше нравится, и придерживаться ее.
Квадратные скобки, в которые заключаются регистры, указываю-
щие на ячейки памяти, являются обязательными. Без этих скобок,
BX, например, интерпретируется, как операнд, а не как ссылка на
операнд.
Оставить комментарий
Комментарии
1.
+2 / -0


25 ноября 2009, 23:04:12
как получилось 111?!
