понедельник, 15 июня 2009 г.

Косвенная адресация со смещением

Теперь скомбинируем два предыдущих метода адресации. Адресуется память (байт или слово). До процессора 80386 относительный адрес операнда определялся, как сумма содержимого регистра BX, BP, SI или DI и указанной в команде константы, иногда называемой смещением. Смещение может быть числом или адресом. При использовании регистров BX, SI и DI подразумевается сегмент, адресуемый через DS, а при использовании ВР подразумевается сегмент стека и, соответственно, регистр SS.
Следующая команда:

mov ax,[bx+2]

помещает в регистр AX слово, находящееся в сегменте, указанном в DS, со смещением на 2 большим, чем число, находящееся в BX.
Такая форма адресации используется в тех случаях, когда в регистре находится адрес начала структуры данных, а доступ надо осуществить к какому-нибудь элементу этой структуры. Другое важное применение косвенной адресации со сдвигом — доступ из подпрограммы к параметрам, переданным в стеке, используя регистр BP (EBP) в качестве базы и номер параметра в качестве смещения.
Другие допустимые формы записи этого способа адресации:

mov ax,[bp]+2
mov ax,2[bp]
mov ax,2+[bp]

Начиная с 80386 и старше, процессоры Intel позволяют дополнительно использовать EAX, EBX, ECX, EDX, EBP, ESP, ESI и EDI, так же как и для обычной косвенной адресации. С помощью этого метода можно организовывать доступ к одномерным массивам байт: смещение соответствует адресу начала массива, а число в регистре — индексу элемента массива, который надо считать. Очевидно, что, если массив состоит не из байт, а из слов, придется умножать базовый регистр на два, а если из двойных слов — на четыре. Для этого предусмотрен специальный метод адресации – косвенная адресация с масштабированием.

Рассмотрим применение косвенной адресации со смещением на примере прямого вывода в видеобуфер.

Небольшое замечание: в четные адреса видеобуфера заносятся символы, в нечетные (после символа) заносится атрибут символа (цвет, фонт и т.п.).

Рассмотрим пример использования косвенной адресации со смещением при обращении к стеку:

И, как водится, листинг:

Обращаем внимание на данные и на то, что команда push offset bt1, транслировалась в 5 машинных команд.

Теперь медитация в отладчике... Скрин ниже сделан после выполнения первых двух команд инициализации регистра DS.

Обращаем пристальное внимание на стек. Хотя мы туда еще ни чего не вносили, там уже полно хлама - это поработал отладчик. Но как видим SP указывает, как ему и положено, на первое слово за "дном" стека.
Так же видим, что одна команда push offset bt1 транслировалась в 5 машинных команд, соответственно и две другие команды, заносящие смещение наших символов в стек, так же транслировались каждая в 5 машинных инструкций. Обращаем внимание на смещение в этик командах, указывающее на наши символы в сегменте данных.
Едем дальше... Следующий скрин сделан после исполнения команды push offset bt1, вернее соответствующих ей 5 машинных команд:

Обращаем внимание на регистр SP, а так же на то, что смещение к символу "О", равное 0000h было занесено в стек.
Теперь выполняем следующую команду push offset bt2...

Опять следим за SP. Видим что смещение 0001h было занесено в стек. Выполняем следующую команду push offset bt3...

Теперь и смещение 0002h помещено в стек. Теперь, на команде call жмем F7, чтобы войти в подпрограмму.

Теперь мы в подпрограмме. Обращаем внимание, что команда call поместила в стек адрес возврата (0026h) из подпрограммы, то есть смещение к следующей команде после себя. И так, сейчас у нас SP указывает на смещение возврата из подпрограммы и в стеке, так же, находятся смещения ко всем символам которые нам надо вывести на экран.
Следующие три команды производят очистку экрана. Команда mov bp,sp поместит содержимое SP. Если бы подпрограмма просто сняла со стека находящиеся там параметры, она первым делом изъяла бы из стека адрес возврата, и лишила бы себя возможности вернуться в основную программу. Поэтому в данном случае вместо команд pop удобнее воспользоваться командами mov для получения нужных значений из стека. Следующие две команды настраивают сегментный регистр ES на работу с видеобуфером. Затем в регистр DI помещается смещение к первому знакоместу экрана. А вот следующую команду mov si,6[bp] рассмотрим более подробно.
Следующий скриншот сделан после выполнения трех команд:

mov si,6[bp]
mov cx,[si]
mov byte ptr es:[di],cl

Смотрим и просветляемся...

Команда mov si,6[bp], поместила в регистр SI смещение (0000h) в сегменте данных к коду символа "O" - 4Fh. Вспоминаем что BP работает в парочке с регистром SS, а SI с регистром DS. На скриншоте приведен простой расчет F8h+06h=FEh. Вспоминаем где мы видели этот адресок 00FEh... Следующая команда mov cx,[si] помещает содержимое ячейки памяти по адресу DS:SI в регистр CX, считывая целое слово. И код нашего символа оказывается в CL. Далее команда mov byte ptr es:[di],cl помещает этот код (4Fh) в видеобуфер, что можно видеть в дампе сегмента адресуемого через регистр ES. Итак на экран выведен символ "O". Далее, подобным образом, выводятся остальные символы. Стоит обратить внимание что значение регистра SP не меняется. Команда ret извлекает из стека по адресу SS:SP адрес возврата 0026h и помещает его в регистр IP. Затем выполняются две команды завершения программы.