Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Флоренсов А.Н. УП Системное программное обеспечение.docx
Скачиваний:
45
Добавлен:
28.06.2021
Размер:
148.95 Кб
Скачать

4. Использование неэлементарных способов адресации

4.1. Косвенно-регистровая адресация и ее использование

Наиболее элементарные из способов адресации, рассмотренные в третьей главе, не дают возможности обрабатывать массивы данных, не позволяют одной и той же команде добираться до различных позиций в памяти компьютера.

Для доступа к памяти до сих пор мы использовали только прямую адресацию. Она позволяет указывать в качестве операнда фиксированное место в памяти. Для этого мы задавали в качестве операнда ограниченное квадратными скобками имя области памяти либо в качестве ассемблерных расширений исходной возможности записывали имя области с прибавлением к нему в записи операнда фиксированной числовой величины. На самом деле за этими записями на ассемблере стояли внутренние (в машинных кодах) числовые указания, которые на ассемблере можно записать в виде [числовое смещение].

Таких средств явно недостаточно для полноценного программирования. Поэтому с самого начала компьютерной эры в архитектуре процессоров используется хотя бы еще один способ адресации, называемый косвенно-регистровой адресацией. Существо этого способа в том, что регистр, заданный для доступа к значению операнда, содержит не само значение операнда, а его адрес (фактически смещение относительно начала сегмента, называемое также относительным адресом).

С получением адреса именованной области данных мы уже встречались при изучении средств обращения к системным функциям. Именно запись на NASM команды

MOV регистр, имя_области_данных

приводила при выполнении команды к помещению адреса области данных, заданной именем во втором операнде, в регистр, заданный первым операндом. Тогда не объяснялось, как такое значение адреса используется далее в программе, потому что подобное использование осуществляли команды самой операционной системы.

Косвенно-регистровый способ адресации задается на ассемблере записью операнда в виде [имя_регистра]. В качестве регистра, используемого в такой записи, можно применять любые из перечисленных EAX, EBX, ECX, EDX, ESI, EDI. Для специальных целей в косвенно-регистровом способе адресации можно применять и регистры ESP, EBP, но делать это вне узкой области специализированного применения не рекомендуется. Следует заметить, что в 16-битной архитектуре применение регистров для косвенно-регистровой адресации было существенно ограниченным, нельзя было с этой целью использовать регистры AX, CX и DX. Приходилось практически ограничиваться лишь регистрами BX, SI и DI, что было очень неудобно.

Рассмотрим, как косвенно-регистровый способ адресации применить для доступа к элементам массива. Пусть имеется (для простоты) массив из байтов, описанный с именем tabla, и мы хотим в цикле перебирать все его элементы для какого-то анализа или вычислений. Это можно сделать следующим фрагментом программы:

MOV ecx, число_элементов_в_массиве

MOV edx, tabla

povt: moval, [edx]

анализ или обработка очередного байта, находящегося

сейчас в регистре AL

INC edx

LOOP povt

Первая и последняя команды в этом фрагменте вспомогательные для демонстрации возможностей рассматриваемого способа адресации (цикл может быть организован и как-то иначе). Собственно косвенно-регист-ровый способ адресации использован здесь во втором операнде третьей команды. Именно в команде пересылки местом источника данных задается байт памяти, адрес которого содержит регистр edx. На первом шаге выполнения цикла в этом регистре содержится адрес начала области tabla, занесенный предыдущей командой, о которой речь уже шла раньше. Поэтому на этом шаге выполнения цикла в регистр AL пересылается значение первого байта из области tabla. Перед концом цикла используется команда инкремента регистра edx, благодаря которой перед вторым шагом значение адреса в регистре edx увеличивается на единицу, показывая теперь на следующий байт этой области. Эти же действия выполняются в цикле многократно. После доступа к некоторому байту внутри области tabla команда инкремента увеличивает значение адреса в регистре edx, заставляя его в дальнейшем указывать на следующий очередной байт.

Заметим, что нет крайней необходимости увеличивать адрес в регистре, служащий для косвенно-регистровой адресации, обязательно на единицу. Можно уменьшать это значение в регистре, обеспечивая перемещение доступа к элементам области от более дальних элементов к более близким, в частности обеспечивая просмотр массива от последнего элемента к начальному. Можно перескакивать через элемент или через любое число элементов.

Косвенно-регистровый способ адресации можно было применить в рассмотренном выше примере программы (листинг 3.3.1). В той программе, вынужденно пользуясь минимальными изученными средствами, осуществлялся вывод цифр результата путем отдельного обращения к системной функции вывода для каждой цифры. Для этого целесообразно сначала сформировать текст для вывода, состоящий из последовательности полученных цифр, и только затем обратиться к системной функции вывода, причем единственный раз для всех цифр результата. Но на этом пути следует размещать цифры результата в различных позициях строки для вывода.

Новое решение этой проблемы, основанное на косвенно-регистровой адресации, приведено далее в листинге 4.1.1.

. . . получение цифр результата и запоминание их в стеке как и ранее

mov [cnt], ecx

mov ebx, numtxt

izv: pop edx

mov byte [ebx],dl ; digit into array for text value

inc ebx

loop izv

mov eax,4 ; N function=write

mov ebx,1 ; N handle=1 (stdout)

mov ecx, numtxt ; address of text

mov edx,[cnt] ; number of byte

int 80h

mov eax, 1 ; N function = exit

int 80h ;

SEGMENT .data

cnt dd 0

SEGMENT .bss

numtxt resb 10

Листинг 4.1.1. Фрагмент вывода цифр результата в Linux

Здесь вместо однобайтовой области digit для единственного выводимого символа цифры сразу заведено десять байтов области numtxt, в которых должны разместиться все цифры числа. Адрес этой области заносится в регистр edx второй командой фрагмента. После извлечения очередной цифры из стека выполняется команда пересылки mov byte [ebx],dl. Эта команда переносит содержимое регистра DL (очередную цифру) в байт области numtxt, заданный косвенно-регистровой адресацией через регистр edx. Далее тут же командой инкремента увеличивается значение адреса в регистре edx, заставляя его указывать для следующего шага на дальнейший байт служебной области numtxt. Пока из стека не извлечены все символы цифр, цикл повторяется командой LOOP, число выполнения которой обусловлено начальным значением регистра ecx, где находится число запомненных в стеке цифр. Лишь по завершении этого цикла задается обращение к системной функции вывода, причем адрес области вывода и число символов в ней формируются командами

mov ecx, numtxt

mov edx,[cnt]

которые задают адрес области numtxt и число символов, запомненное ранее в переменной cnt.