mov BX,offset pat2;ВХ=смещение pat2
add BX,sex;ВХ=смещение pat2.sex
mov DL,[BX];DL='M'
mov SI,birth;SI=5 (сомнительная команда)
Записи
Записи, как и структуры, представляют собой шаблоны, накладываемые на реальные данные с целью ввдения удобных мнемонических обозначений отдельных элементов данных. В отличие от структур, дающих имена байтам, словам, двойным словам или целым массивам, в записях определяются строки битов внутри байтов, слов или двойных слов.
Известно, что дата создания файла хранится в каталоге диска в виде 16-битового слова, в котором старшие 7 бит обозначают год (от 1980), следующие 4 бит — месяц и последние 5 бит — день (рис. 2.14).
Биты слова 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
Год Месяц День
Рис. 2.14. Формат записи даты в каталоге диска.
Глава 2
Основы программирования
Эти данные удобно специфицировать с помощью записи filedate, оп
ределяемой в программе следующим образом: <Й
fdate record year:7, month:4, day:5 pi
Ключевое слово record говорит о том, что имя fdate относится к записи, а мнемонические обозначения year, month и day являются произвольными именами отдельных битовых полей описываемого слова.
Включение в программу описания шаблона битовых полей позволяет отказаться от утомительного и чреватого ошибками определения «вручную» содержимого полного данного по задаваемым значениям его отдельных составляющих. Для приведенной выше записи объявления конкретных переменных будут выглядеть следующим образом:
fflel fdate <5,6,7> Ше2 fdate <18,12,30> ШеЗ fdate <> |
;7 июня 1985г.
;30 декабря 1998г.
;»Пустая» (пока) переменная
Переменная filel будет определена, как число OAC7h, Ше2 — как число 259Eh, а ШеЗ — как число OOOOU. При необходимости программного заполнения переменной типа fdate можно пользоваться именами ее составляющих, которые трактуются ассемблером, как индексы соответствующих битовых полей, отсчитываемые от младшего конца слова. Для приведенного примера day=0, month=5, a year=9- Однако в системе команд МП 86 практически нет средств работы с битовыми полями. Поэтому программное заполнение придется осуществлять с помощью команд сдвигов и логического сложения:
mov | flle3,30 |
mov | AX,12 |
mov | CL, month |
shl | AX,CL |
or | ШеЗ, AX |
mov | AX,18 |
mov | CL, month |
shl | AX,CL |
or | ШеЗ, AX |
;Помещаем день
;Месяц пока в АХ
;Будем сдвигать на month бит
;Сдвинули месяц в АХ на 5 бит.
;Добавили биты месяца в ШеЗ
;Год пока в АХ
;Будем'сдвигать на year бит
;Сдвинули год в АХ на 9 бит
;Добавили биты года в ШеЗ
В итоге в переменной ШеЗ окажется тот же код 259Е11, что и в переменной file 2.
2.5. Способы адресации
Способом, или режимом адресации называют процедуру нахождения операнда для выполняемой команды. Если команда использует два операнда, то для каждого из них должен быть задан способ адресации, причем режимы адресации первого и второго операнда могут как совпадать,
так и различаться. Операнды команды могут находиться в разных местах: непосредственно в составе кода команды, в каком-либо регистре, в ячейке памяти; в последнем случае существует несколько возможностей указания его адреса. Строго говоря, способы адресации являются элементом архитектуры процессора, отражая заложенные в нем возможности поиска операндов. С другой стороны, различные способы адресации определенным образом обозначаются в языке ассемблера и в этом смысле являются разделом языка.
Следует отметить неоднозначность термина «операнд» применительно к программам, написанным на языке ассемблера. Для машинной команды операндами являются те данные (в сущности, двоичные числа), с которыми она имеет дело. Эти данные могут, как уже отмечалось, находиться в регистрах или в памяти. Если же рассматривать команду языка ассемблера, то для нее операндами (или, лучше сказать, параметрами) являются те обозначения, которые позволяют сначала транслятору, а потом процессору определить местонахождение операндов машинной команды. Так, для команды ассемблера
mov mem, AX
в качестве операндов используется обозначение ячейки памяти mem, a также обозначение регистра АХ. В то же время, для соответствующей машинной команды операндами являются содержимое ячейки памяти и содержимое регистра. Было бы правильнее говорить об операндах машинных команд и о параметрах, или аргументах команд языка ассемблера.
По отношению к командам ассемблера было бы правильнее использовать термин «параметры», оставив за термином «операнд» обозначение тех физических объектов, с которыми имеет дело процессор при выполнении машинной команды, однако обычно эти тонкости не принимают в расчет, и говоря об операндах команд языка, понимают в действительности операнды машинных команд.
В архитектуре современных 32-разрядных процессоров Intel предусмотрены довольно изощренные способы адресации; в МП 86 способов адресации меньше. В настоящем разделе будут описаны режимы адресации, используемые в МП 86.
В книгах, посвященных языку ассемблера, можно встретить разные подходы к описанию способов адресации: не только названия этих режимов, но даже и их количество могут различаться. Разумеется, способов адресации существует в точности столько, сколько их реализовано в процессоре; однако, режимы адресации можно объединять в группы по разным признакам, отчего и создается некоторая путаница, в том числе и в количестве имеющихся режимов. Мы будем придерживаться распространенной, но не единственно возможной терминологии.
Регистровая адресация. Операнд (байт или слово) находится в регистре. Этот способ адресации применим ко всем программно-адресуемым Регистрам процессора.
Глава 2
Основы программирования
inc СН;Плюс 1 к содержимому СН
push DS;;DS сохраняется в стеке №
xchg ВХ.ВР;ВХ и ВР обмениваются содержимым У
mov ES,AX;Содержимое АХ пересылается в ES ||
Непосредственная адресация. Операнд (байт или слово) указывается в команде и после трансляции поступает в код команды; он может иметь любой смысл (число, адрес, код ASCII), а также быть представлен в виде символического обозначения.
mov AH,40h;Число 40h загружается в АН
mov AL,'*";Код ASCII символа "*" загружается в AL
hit 21h;Команда прерывания с аргументом 2Ш
limit = 528;Число 528 получает обозначение limit
mov CX,limit;Число, обозначенное limit, загружается в СХ
Команда mov, использованная в последнем предложении, имеет два операнда; первый операнд определяется с помощью регистровой адресации, второй — с помощью непосредственной.
Важным применением непосредственной адресации является пересылка относительных адресов (смещений). Чтобы указать, что речь идет об относительном адресе данной ячейки, а не об ее содержимом, используется описатель onset (смещение):
;Сегмент данных mes db "Урок Г';Сегмент команд mov DX.oflset mes |
;Строка символов
;Адрес строки засылается в DX
В приведенном примере относительный адрес строки mes, т.е. расстояние в байтах первого байта этой строки от начала сегмента, в котором она находится, заносится в регистр DX.
Прямая адресация памяти. Адресуется память; адрес ячейки памяти (слова или байта) указывается в команде (обычно в символической форме) и поступает в код команды:
;Сегмент данных me ml dw О тет2 db 230;Сегменг команд inc meml mov mov |
;Слово памяти содержит О;Байт памяти содержит 230
;Содержимое слова meml увеличивается на 1 DX,meml;Содержимое слова с именем meml загружается в DX AL,mem2;Содержимое байта с именем mem2 загружается в AL
Сравнивая этот пример с предыдущим, мы видим, что указание в команде имени ячейки памяти обозначает, что операндом является со-
держимое этой ячейки; указание имени ячейки с описателем onset — что операндом является адрес ячейки.
Прямая адресация памяти на первой взгляд кажется простой и наглядной. Если мы хотим обратиться, например, к ячейке meml, мы просто указываем ее имя в программе. В действительности, однако, дело обстоит сложнее. Вспомним, что адрес любой ячейки состоит из двух компонентов: сегментного адреса и смещения. Обозначения meml и mem2 в предыдущем примере, очевидно, являются смещениями. Сегментные же адреса хранятся в сегментных регистрах. Однако сегментных регистров четыре: DS, ES, CS и SS, Каким образом процессор узнает, из какого регистра взять сегментный адрес, и как сообщить ему об этом в программе'
Процессор различает группу кодов, носящих название префиксов. Имеется несколько групп префиксов: повторения, размера адреса, размера операнда, замены сегмента. Здесь нас будут интересовать префиксы замены сегмента.
Команды процессора, обращающиеся к памяти, могут в качестве первого байта своего кода содержать префикс замены сегмента, с помощью которого процессор определяет, из какого сегментного регистра взять сегментный адрес. Для сегментного регистра ES код префикса составляет 26h, для SS — 36U, для CS — 2Eh. Если префикс отсутствует, сегментный адрес берется из регистра DS (хотя для него тоже предусмотрен свой префикс).
Если в начале программы с помощью директивы assume указано соответствие сегменту данных сегментного регистра DS
assume DS:data
то команды обращения к памяти транслируются без какого-либо префикса, а процессор при выполнении этих команд берет сегментный адрес из регистра DS.
Если в директиве assume указано соответствие сегмента данных регистру ES assume ES:data
(в этом случае сегмент данных должен располагаться перед сегментом команд), то команды обращения к полям этого сегмента транслируются с добавлением префикса замены для сегмента ES. При этом предложения программы выглядят обычным образом; в них по-прежнему просто указываются имена полей данных, к которым производится обращение.
Однако в ряде случаев префикс замены сегмента должен указываться в программе в явной форме. Такая ситуация возникает, например, если Данные расположены в сегменте команд, что типично для резидентных обработчиков прерываний. Для обращения к таким данным можно, конечно, использовать регистр DS, если предварительно настроить его на сегмент команд, но проще выполнить адресацию через регистр CS, который и так уже настроен должным образом. Если в сегменте команд содержится поле данных с именем mem, то команда чтения из этого поля будет выглядеть следующим образом:
Глава 2
Основы программирования
mov AX,CS:mem
В этом случае транслятор включит в код команды префикс замены для сегмента CS. Другие примеры команд с заменой сегмента будут приведены ниже.
До сих пор мы обсуждали адресацию ячеек, содержащихся в сегментах данных программы. Однако часто бывает нужно обратиться к памяти вне пределов программы: к векторам прерываний, системным таблицам, видеобуферу и т.д. Разумеется, такое обращение возможно только если мы знаем абсолютный адрес интересующей нас ячейки. В этом случае необходимо сначала настроить один из сегментных регистров на начало интересующей нас области, после чего можно адресоваться к ячейкам по их
смещениям.
Пусть требуется вывести в левый верхний угол экрана несколько символов, например, два восклицательных знака. Эту операцию можно реализовать с помощью следующих команд:
mov AX,OB800h mov ES,AX mov byte ptr ES:0,'!' mov byte ptr ES:2,T |
;Сегментный адрес видеобуфера;Отправим его в ES
;Огправим символ на 1-е знакоместо экрана;Отправим символ на 2-е знакоместо экрана
Настроив регистр ES на сегментный адрес видеобуфера BSOOh, мы пересылаем код знака «!» сначала по относительному адресу 0 (в самое начало видеобуфера, в байт со смещением 0), а затем на следующее знакоместо, имеющее смещение 2 (в нечетных байтах видеобуфера хранятся атрибуты символов, т.е. цвет символов и фона под ними). В обеих командах необходимо с помощью обозначения ES: указать сегментный регистр, который используется для адресации памяти. Встретившись с этим обозначением, транслятор включит в код команды префикс замены сегмента, в данном случае код 26U.
В приведенном примере мы снова столкнулись с использованием атрибутивного оператора byte ptr, который позволяет в явной форме задать размер операнда. Однако если раньше этот оператор использовался, чтобы извлечь байт из данного, объявленного, как слово, то здесь его назначение иное. Транслятор, обрабатывая команду
mov byte ptr ES:0,T
не имеет возможности определить размер операнда-приемника. Разумеется, видеобуфер, как и любая память, состоит из байтов, однако надо ли рассматривать эту память, как последовательность байтов или слов1 Команда без явного задания размера операнда
mov ES:0,T
вызовет ошибку трансляции, так как ассемблер не сможет определить, надо ли транслировать это предложение, как команду пересылки в видеобуфер байта 2Ш, или как команду пересылки слова 00211г.
Между прочим, на первый взгляд может показаться, что в обсуждае-'мой команде достаточно ясно указан размер правого операнда, так как символ (в данном случае "!") всегда занимает один байт. Однако транслятор, встретив обозначение "!", сразу же преобразует его в код ASCII этого символа, т.е. в число 21h, и уже не знает, откуда это число произошло и какой размер оно имеет.
Стоит еще отметить, что указание в команде описателя word ptr
'f
{ mov word ptr ES:0,T
не вызовет ошибки трансляции, но приведет к неприятным результатам. В этом случае в видеобуфер будет записано слово 002 Ш, которое заполнит байт 0 видеобуфера кодом 2Ш, а байт 1 кодом ООН. Однако атрибут OOh обозначает черный цвет на черном фоне, и символ на экране виден не будет (хотя и будет записан в видеобуфер).
При желании можно избавиться от необходимости вводить описатель размера операнда. Для этого надо пересылать не непосредственное данное, а содержимое регистра:
mov AL,'!' mov ES:0,AL
Здесь операндом-источником служит регистр AL, размер которого (1 байт) известен, и размер операнда-приемника определять не надо. Разумеется, команда
mov ES:0,AX
заполнит в видеобуфере не байт, а слово.
Для адресации к видеобуферу в вышеприведенном примере использовался сегментный регистр дополнительных данных ES. Это вполне естественно, так как обычно регистр DS служит для обращения к полям данных программы, а регистр ES как раз и предназначен для адресации всего остального. Однако при необходимости можно было воспользоваться для записи в видеобуфер регистром DS:
mov AX,OB800h;Сегментный адрес
mov DS,AX видеобуфера в DS
mov byte ptr DS:0,T;Символ в видеобуфер
Любопытно, что хотя обознбачение DS: здесь необходимо, транслятор не включит в код команды префикс замены сегмента, так как команда без префикса выполняет адресацию по умолчанию через DS.
Если, однако, по умолчанию бвыполняется адресация через DS, то нельзя ли опустить в последней команде обозначение сегментного регистра1 Нельзя, так как обозначение О5:число указывает, что число является не непосредственным операндом, а адресом операнда. Команда (неправильная)
mov 6,10
Глава 2
должна была бы переслать число 10 в число 6, что, разумеется, лишено смысла и выполнено быть не может. Команда же
mov DS:6,10
пересылает число 10 по относительному адресу 6, что имеет смысл. Таким образом, обозначение сегментного регистра с двоеточием перед операндом говорит о том, что операнд является адресом. В дальнейшем мы еще столкнемся с этим важным правилом.
Мы рассмотрели три важнейших способа адресации: регистровую, непосредственную и прямое обращение к памяти. Все остальные режимы адресации относятся к группе косвенной адресации памяти, когда в определении адреса ячейки памяти участвует один или несколько регистров процессора. Рассмотрим последовательно эти режимы.
Регистровая косвенная (базовая и индексная). Адресуется память (байт или слово). Относительный адрес ячейки памяти находится в регистре, обозначение которого заключается в прямые скобки. В МП 86 косвенная адресация допустима только через регистры ВХ, ВР, SI и DI. При использовании регистров ВХ или ВР адресацию называют базовой, при использовании регистров SI или DI — индексной.
Преобразуем приведенный выше пример, чтобы продемонстрировать использование косвенной адресации через регистр.
t^fr
mov AX,OB800h mov ES,AX mov BX,2000 mov byte ptr ES:[BX],T |
;Сегментный адрес видеобуфера в ES;Смещение к середине экрана;Символ на экран
Настроив ES, мы засылаем в регистр ВХ требуемое смещение (для разнообразия к середине видеобуфера, который имеет объем точно 4000 байт), и в последней команде засылаем код в видеобуфер с помощью косвенной базовой адресации через пару регистров ES:BX с указанием замены сегмента (ES:).
Если косвенная адресация осуществляется через один из регистров ВХ, SI или DI, то подразумевается сегмент, адресуемый через DS, поэтому при адресации через этот регистр обозначение DS: можно опустить:
mov AX,OB800h mov DS,AX mov BX,2000 mov byte ptr [BX],T |
;Сегментный адрес видеобуфера в DS;Смещение к середине экрана; Символ на экран
Кстати, этот фрагмент немного эффективнее предыдущего в смысле расходования памяти. Из-за отсутствия в коде последней команды префикса замены сегмента он занимает на 1 байт меньше места.
Регистры ВХ, SI и DI в данном применении совершенно равнозначны, и с одинаковым.успехом можно воспользоваться любым из них:
ы программирования |
69
mov DI.2000 mov byte ptr [DI],'!' |
;Смещение к середине экрана;Символ на экран
Не так обстоит дело с регистром ВР. Этот регистр специально предназ-1чен для работы со стеком, и при адресации через этот регистр в режимах венной адресации подразумевается сегмент стека; другими словами, в качестве сегментного регистра по умолчанию используется регистр SS.
Обычно косвенная адресация к стеку используется в тех случаях, когда необходимо обратиться к данным, содержащимся в стеке, без изъятия их оттуда (например, если к эти данные приходится считывать неоднократно). Пример такого рода операций будет приведен при обсуждении следующего режима адресации.
Сравнивая приведенные выше фрагменты программ, можно заметить, что использование базовой адресации, на первый взгляд, снижает'эффективность программы, так как требует дополнительной операции — загрузки в базовый регистр требуемого адреса. Действительно, базовая адресация в нашем примере не оправдана — в случае прямого обращения к памяти вместо двух команд
mov mov |
ВХ,2000;Смещение к середине экрана
byte ptr ES:[BX),T;Символ на экран
можно использовать одну
mov byte ptr ES:2000,T;Выведем символ в середину экрана
Однако команда с базовой адресацией занимает меньше места в памяти (так как в нее не входит адрес ячейки) и выполняется быстрее команды с прямой адресацией (из-за того, что команда короче, процессору требуется меньше времени на ее считывание из памяти). Поэтому базовая адресация эффективна в тех случаях, когда по заданному адресу приходится обращаться многократно, особенно, в цикле. Выигрыш оказывается тем больше, чем большее число раз происходит обращение по указанному адресу. С другой стороны, возможности этого режима адресации невелики, и на практике чаще используют более сложные способы, которые будут рассмотрены ниже.
Регистровая косвенная адресация со смещением (базовая и индексная). Адресуется память (байт или слово). Относительный адрес операнда определяется, как сумма содержимого регистра ВХ, ВР, SI или DI и указанной в команде константы, иногда называемой смещением. Смещение может быть числом или адресом. Так же, как и в случае базовой адресации, при использовании регистров ВХ, SI и DI подразумевается сегмент, адресуемый через DS, а при использовании ВР подразумевается сегмент стека и, соответственно, регистр SS.
Рассмотрим применение косвенной адресации со смещением на примере прямого вывода в видеобуфер.
mov AX,OB800h mov ES,AX mov DI, 80*2*24 |
;Сегментный адрес
видеобуфера в ES
;Смещение к нижней строке экрана
Глава 2
Основы программирования
j mov byte ptr ES:[DI],'O*;Символ на экран
mov byte ptr ES:2[DI],'K';3апишем символ в следующую позицию mov byte ptr ES:4[DI],T;3апишем символ в следующую позицию
В этом примере в качестве базового выбран регистр DI; в него заносится базовый относительный адрес памяти, в данном случае смещение в видеобуфере к началу последней строки экрана. Модификация этого адреса с целью получить смещение по строке экрана осуществляется с помощью констант 2 и 4, которые при вычислении процессором исполнительного адреса прибавляются к содержимому базового регистра DI.
Иногда можно встретиться с альтернативными обозначениями того же способа адресации, которые допускает ассемблер. Вместо, например, 4[ВХ] можно с таким же успехом написать [ВХ+4], 4+[ВХ] или [ВХ]+4. Такая неоднозначность языка ничего, кроме путаницы, не приносит, однако ее надо иметь в виду, так как с этими обозначениями можно столкнуться, например, рассматривая текст деассемблированной программы.
Рассмотрим теперь пример использования базовой адресации со смещением при обращении к стеку:
;Основная программа
push DS;В стек загружаются значения
push ES;трех регистров,
push SI передаваемых подпрограмме
call mysub;Вызов подпрограммы mysub,
;использующей эти параметры
;Подпрограмма mysub
mov ВР,5Р;Поместим в ВР текущий адрес вершины стека mov АХ,2[ВР];Читаем в АХ последний параметр (SI) mov ВХ,4[ВР];Читасм в ВХ предыдущий параметр (ES) mov СХ,6[ВР];Читаем в СХ первый параметр (DS)
Здесь продемонстрирован классический прием чтения содержимого стека без извлечения из него этого содержимого. После того, как основная программа сохранила в стеке три параметра, которые потребуются подпрограмме, командой call вызывается подпрограмма mysub. Эта команда сохраняет в стеке адрес возврата (адрес следующего за call предложения основной программы) и осуществляет переход на подпрограмму-Состояние стека при входе в подпрограмму приведено на рис. 2.15.
Если бы подпрограмма просто сняла со стека находящиеся там параметры, она первым делом изъяла бы из стека адрес возврата, и лишила бы себя возможности вернуться в основную программу (подробнее вопросы вызова подпрограммы и возврата из нее будут обсуждаться в последующих разделах). Поэтому в данном случае вместо команд pop удобнее воспользоваться командами mov. Подпрограмма копирует в ВР содержимое SP и использует затем этот адрес в качестве базового, модифицируя его с помощью базовой адресации со смещением.
Состояние SPпосле перехода на подпрограмму |
Параметры, загруженные в стек основной программой |
Исходное состояние SP |
Смещения в стеке относительно текущего состояния SP
0 2 4 б | .Адрес возврата | < ч < |
SI | ||
ES | ||
DS | ||
Рис. 2.15. Состояние стека после загрузки в него трех параметров и перехода на подпрограмму.
Кстати, мы опять сталкиваемся здесь с той весьма обычной ситуацией, когда программист не имеет возможности обращаться по наглядным символическим адресам, которьк в стеке, естественно, нет, а вынужден определять «вручную» смещения к интересующим его элементам стека. При этом необходимо учесть и алгоритм выполнения команды call, которая, сохраняя в стеке адрес возврата в основную программу, смещает указатель стека еще на одно слово.
В нашем фрагментарном примере мы не рассматриваем вопрос возврата в основную программу. Вдумчивый читатель мог также усомниться в правильности или, лучше сказать, в разумности текста подпрограммы. Ведь перенося параметры из стека в регистры общего назначения, подпрограмма затирает их исходное содержимое. Если же они не содержали ничего нужного, то ими можно было воспользоваться для передачи параметров в подпрограмму, а не связываться с мало наглядными операциями со стеком. Действительно, ради краткости мы опустили операции, практически необходимые в любой подпрограмме — сохранение в стеке (опять в стеке!) тех регистров, которые будут использоваться в подпрограмме. Кстати, это относится и к регистру ВР. В реальной подпрограмме эти действия следовало выполнить, что привело бы к изменению смещений при регистре ВХ, которые приняли бы значения (с учетом сохранения 4 регистров) 10, 12 и 14.
Во всех приведенных выше примерах регистр использовался для хранения базового адреса, а смещение, если оно требовалось, указывалось в виде Константы. Возможна и обратная ситуация, когда в качестве смещения выступает адрес массива, а в регистре находится индекс адресуемого элемента в этом массиве. Рассмотрим относительно решгьный пример такого рода
Пусть нам надо заполнить массив из 10000 слов натуральным рядом Чисел. Зарезервируем в сегменте данных место под этот массив, а в сег-
Глава 2
г
Основы программирования
менте команд организуем цикл занесения в последовательные слова массива ряда нарастающих чисел. Нам придется воспользоваться несколькими новым командами (inc, add и loop), которые в дальнейшем будут рассмотрены более подробно.
;Сегмент данных array dw;Сегмент команд mov SI,0 mov AX,0 mov CX, 10000 fill: mov array[SIJ,AX inc AX add SI,2 loop fill |
;Начальное значение индекса элемента в массиве;Первое число-заполнитель;Число шагов в цикле (всегда в СХ)
;3анесение числа в элемент массива '<•
;Инкремент числа-заполнителя $
;Смещение в массиве к следующему слову ^;
;Возврат на метку fill (CX раз) '•'-
Цикл начинается с команды, помеченной меткой fill (правила образования имен меток такие же, как и для имен полей данных). В этой команде содержимое АХ, поначату равное 0, переносится в ячейку памяти, адрес которой вычисляется, как сумма адреса массива array и содержимого индексного регистра SI, в котором в первом шаге цикла тоже 0. В результате в первое слово массива заносится 0. Далее содержимое регистра АХ увеличивается на 1, содержимое регистра SI — на 2 (из-за того, что массив состоит из слов), и командой loop осуществляется переход на метку fill, после чего тело цикла повторяется при новых значениях регистров АХ и SI. Число шагов в цикле, отсчитываемое командой loop, определяется исходным содержимым регистра СХ.
Базо во-индексная адресация. Адресуется память (байт или слово). Относительный адрес операнда определяется, как сумма содержимого следующих пар регистров:
[BX][SI] (подразумевается DS:[BX][SI]) [BX][DI] (подразумевается DS:[BX][DI]) [BP][SI] (подразумевается SS:[BP][SI]) [BP][DI] (подразумевается SS:[BP][DI])
Это чрезвычайно распространенный способ адресации, особенно, при работе с массивами. В нем используются два регистра, при этом одним из них должен быть базовый (ВХ или ВР), а другим — индексный (SI или DI). Как правило, в одном из регистров находится адрес массива, а в другом — индекс в нем, при этом совершенно безразлично, в каком что. Трансформируем предыдущий пример, введя в него более эффективную базо-во-индексную адресацию.
;Сегмент данных array dw 10000
;Сегмент команд mov BX.offset array mov SI,0 |
;Базовый адрес массива в
;базовом регистре
;Начальное значение индекса
;элемента в массиве
mov mov mov inc add loop |
AX,0 CX, 10000 [BX][SI],AX AX SI,2 fill |
;Первое число-заполнитель