Рассмотрим следующий фрагмент программы, где в регистр 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, например, интерпретируется, как операнд, а не как ссылка на операнд.