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?!