Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Textnew2

.pdf
Скачиваний:
13
Добавлен:
06.02.2018
Размер:
1.48 Mб
Скачать

регистре edx увеличивается на единицу, показывая теперь на следующий байт этой области. Эти же действия выполняются в цикле многократно. После доступа к некоторому байты внутри области tabla (пересылке его в регистр AL для обработки и выполнения такой обработки), команда инкремента увеличивает значение адреса в регистре 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 cntdd 0

61

SEGMENT .data numtxt resb 10

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

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

mov ecx, numtxt mov edx,[cnt]

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

4.2.Использование индексной адресации данных

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

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

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

На ассемблере NASM операнды, задаваемые индексным способом адресации, записываются в форме [имя_массива + индексный регистр] или [смещение + индексный регистр]. Первый вариант широко используется для именованных областей, второй наблюдается при просмотре исполняемых файлов с помощью дизассембле-

62

ров и отладчиков, когда нет достаточной информации об исходных программах на языке ассемблера. В частности, индексному способу адресации отвечают обозначения операндов [tabla+ESI], [buffer+EDX] и т.п.

На ассемблерах MASM и TASM индексный способ адресации операнда позволяется и в форме имя_массива [индексный регистр], что практически делает его внешне совпадающим с традиционной формой записи индексированных переменных в алголоподобных языках.

Запишем для сравнения простейшую программу суммирования элементов массива, где каждый элемент занимает один байт (это предельно упрошенный пример, т.к. отдельные байт позволяют записывать числа в чрезвычайно ограниченном диапазоне от - 128 до +127). Вот эта программа в двух вариантах - на языке ассемблера (в первом столбце) и на языке Паскаль (во втором столбце):

mov eax,0

sum:=0;

 

repeat

mov esi, 0

i:=0;

met: add eax, [tabla + esi]

sum:=sum+tabla[i];

inc esi

i:=i+1;

cmp esi, 10

until i >= 10;

jl met

 

Как видим, сходство поразительное и отличие, в основном, заключаются в средствах организации цикла, а не в форме записи доступа к элементу массива.

При практическом использовании индексного способа адресации следует иметь в виду, что недопустимо применение в качестве индексного 8-битного регистра. Регистры же, которые можно применять в косвенно-регистровой адресации, также хорошо подходят и для индексной адресации. (Напомним, что это регистры EAX, EBX, ECX, EDX, ESI, EDI.) В 16-битной архитектуре для индексной адресации были назначены только два регистра SI и DI, отсюда возникло и их наименование (Source Index и Destination Index). В той же 16-битной архитектуре допускалось использование в качестве индексных и регистров BX, BP. Практически же в этой исходной примитивной архитектуре все регистры были специализированы в большей или меньшей степени.

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

В качестве примера практического использования индексной адресации, рассмотрим задачу поиска максимального в массиве чисел основного для 32-бит-

63

ной архитектуры размера числовых элементов, составляющего 4 байта (размер типа int для этой архитектуры). Пример такой программы приведен на рис. 4.2.1.

GLOBAL _start SEGMENT .text

_start:

mov esi, 0 ; esi=valindex = 0

mov eax, [narray+0] ; или просто [narray]; eax - текущее значение max met1: cmp eax, [narray+esi] ; сравнение max и narray[index]

jg next

mov eax, [narray+esi] ; поместить narray[index] в edx

next: add esi, 4 ; valindex - приходится увеличивать на 4 байта ; - размер места числа в памяти

cmp esi, 10*4 ; проверены ли все 10 элементов массива? jl met1

; полученный max в регистре eax

 

mov esi,10 ; base of position digit system

 

mov ecx, 0 ; reset digit counter

pov:

mov edx, 0 ; null into left part of devident

 

div esi

; divide for next digit = rest

 

add dl, '0'

 

 

push edx

 

 

inc ecx

; step into counter

 

cmp eax, 0

 

jne pov

 

 

mov [cnt], ecx

 

mov ebx, formax

izv:

pop edx

 

 

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

 

inc ebx

 

 

loop izv

; izv,ecx

;--- write(1, txtmes, lenmes+[cnt]) == <4>(ebx, ecx, edx)

 

mov eax,4 ; N function=write

 

mov ebx,1

; N handle=1 (stdout)

 

mov edx,[cnt] ; number of byte

 

add edx, lenmes

mov ecx, txtmes ; address of txtmes (и далее formax) int 80h

mov eax,1

int 80h ; function=exit for Linux SEGMENT .data

64

narraydd 3,17,-5,27,0,-11,17,33,-1,9

txtmes db 'Максимальное число в массиве есть ' lenmes equ $-txtmes

formax times 10 db ' ' cnt dd 0

Рис. 4.2.1.Поиск максимального в массиве чисел

В этой программе значение индекса (смещения индексируемого элемента массива) помещается в регистр ESI. Регистр EAX выделен для размещения в нем будущего значения максимального элемента. Значение индекса, с учетом размеров элементов массива в 4 байта, увеличивается на 4 командой ADD ESI, 4. При проверке выхода значения индекса за пределы массива - в качестве значения для сравнения - используется не число элементов массива, а его произведение на размер элементов. Это произведение явно записывается в операнде с непосредственной адресацией команды сравнения cmp esi, 10*4. Тем самым, в частности, демонстрируется еще одна замечательная возможность современных ассемблеров - использование выражений для вычисления значений операнда, которые можно записывать на месте операнда. Такая возможность использует обычные обозначения арифметических операций, но, по существу требует, чтобы все компоненты такого выражения были определены еще на этапе компиляции программ. Выражаясь несколько менее обобщенно, компоненты таких выражений должны быть заданы явными константами, в частности, определенными через директивы EQU.

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

4.3. Базовая и индексно базовая адресации

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

Для пояснения существа использования базовой адресации будем исходить из следующего описания на языке Си:

struct doca { char sex; int age;

65

char name[10]; };

struct doca persons[20];

которое в упрощенном варианте подготавливает массив структур данных для учетной информации о некоторых людях (пол, возраст и имя).

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

Если отдельно описать строение данной структуры на ассемблере, мы получим следующее

; struct doca

sex

DB 0

age

DD 0

name

TIMES 10 DB 0

Простейшее решение заключается в использовании численных смещений от начала структуры. Непосредственно видно (в том числе из листинга соответствующей программы), что поле sex находится с нулевым смещением от начала экземпляра структуры, поле age - на расстоянии одного байта от начала экземпляра структуры, а поле name, соответственно, на расстоянии пяти байтов от начала структуры (считая, что данные типа int требуют для записи в 32-битной архитектуре четырех байтов).

Технической и, в некоторой степени, языковой проблемой для ассемблера является то, что эти поля нужно обозначать и использовать для любого из экземпляров в массиве структур. Базовый способ адресации и позволяет тут же решить эту задачу. Предварительно в некоторый регистр, называемый далее базовым в операнде с рассматриваемой адресацией, заносится значение адреса экземпляра структуры. Тогда обозначение [базовый_регистр+0] задает в операнде команды поле sex, обозначение [базовый_регистр+1] задает поле age, а обозначение [базовый_регистр+5] задает поле name. (С помощью последнего обозначения можно добраться до одного, двух или четырех начальных байтов поля name, используя, соответственно, модификаторы byte, word и dword.) Меняя значение в регистре базовый_регистр можно с помощью одной и той же команды добираться до одноименного поля в различных экземплярах структуры.

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

66

Например, для вычисления суммы значений полей age в предложенном выше примере (для вычисления среднего возраста или чего-то подобного) можно использовать следующий фрагмент программы на ассемблере

mov ebx, persons ; адрес массива структур заноситься в ebx mov ecx, 20

mov eax, 0 ; регистр EAX предназначен для вычисляемой суммы povt: add eax, [ebx+1] ; прибавление значения поля age

add ebx, 15; увеличение регистра ebx на размер структуры doca loop povt

Здесь в начале значение регистра ebx устанавливается, чтобы указывать на начало области массива структур, а затем внутри цикла каждый раз увеличивается на длину экземпляра структуры, выраженную в байтах, тем самым указывая далее на начало следующего элемента структуры в массиве. Операнд [ebx+1] в команде сложения всегда поэтому задает поле age описанной выше структуры данных.

Для описания всего массива данных может быть использована "очень слепая" форма записи в виде

persons TIMES 20*15 db 0

или же более развернутая для программиста форма, применяющая директиву повторения REP

persons

%rep 20 DB 0 DD 0

TIMES 10 DB 0 %endrep

Эта директива относится к средствам предпроцессора (предварительной до собственно компилятора обработки исходного текста). Она с помощью начальной части конструкции %rep номер задает число повторений идущего далее - до строки с ключевым словом %endrep - текста. В данном случае 20 раз повторяется описание полей для структуры.

Более мощным средством является предпроцессорные директивы описания структуры данных. Они задаются ключевыми словами STRUC и ENDSTRUC, указывающими начало и конец описания структуры, причем имя структуры задается как единственный параметр в директиве STRUC. Само описание при этом должно задаваться исключительно резервированием памяти. В нашем примере мы получаем

 

struc doca

sex

resb 1

age

resd 1

name

resb 10

 

endstruc

Такое введение в использование имен полей sex, age, name равносильно явному заданию для каждого из них числового значения, равного смещению соответствую-

67

щего поля относительно начала структуры. В данном случае неявно для поля sex задается значение 0, для поля age - значения 1, для поля name - значения 5. После этого можно в операндах, указывающих доступ к этим полям записывать, соответственно, [ebx+sex], [ebx+age], [ebx+name],

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

 

struc doca

.sex

resb 1

.age

resd 1

.name resb 10 endstruc

то имена .sex, .age, .name будут относиться только к структуре doca. Определенные таким образом имена полей должны использоваться вместе с именем структуры в виде имя_структуры.имя_поля. Все это сделано, чтобы можно было применять одноименные поля в различных структурах данных, которые различаются именно по квалификатору имени структуры, стоящему перед именем поля.

В нашем последнем примере обозначения поля sex в операнде, который в качестве базового используется регистр ebx, должно записываться в виде [ebx+doca.sex], использование поля age - в виде [ebx+doca.age], а поля name - в виде [ebx+doca.name]. Еще раз подчеркнем, что вместо таких достаточно наглядных, но более длинных обозначений программист может использовать просто числовое значение смещения.

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

[базовый_регистр+индексный_регистр+смещение] Компонент смещение в этой форме, равно как и в более простой форме базового

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

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

mov ebx, persons ; адрес массива структур заноситься в ebx

68

mov ecx, 20

povt: mov esi, 0 ; нулевой индекс в esi для доступа к начальному байту области

iname: . . . использование байтов области name

; . . . с помощью операнда в виде [ebx+esi+5]

inc esi ; индекс в esi переустановить на следующий элемент в массиве

name

cmp esi, 10 jl iname

add ebx, 15 ; увеличение регистра ebx на размер структуры doca loop povt

Здесь, как уже указывалось выше, обозначение доступа к байту в массиве name может быть записано в виде [ebx+esi+doca.name], но только, если в программе директивой предпроцессора определено строение структуры doca.

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

4.4. Адресация с масштабированием

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

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

Вэтом способе кроме записи в операнде обозначение индексного регистра записывается вместе с ним и значение одной из стандартных длин элементов массива. В

69

качестве таких стандартных длин допустимы только 2, 4 или 8. Операнд, записанный в этом способе адресации имеет вид

[имя_области+масштаб*индексный_регистр] или [базовый_регистр+смещение+масштаб*индексный_регистр],

где параметр масштаб должен задаваться только числом 2, 4 или 8.

Далее на рис. 4.4.1 приведен пример использования способа адресации с масштабированием. Этот пример выполняет поиск максимального в массиве чисел.

GLOBAL _start SEGMENT .text

_start:

mov esi, 0 ; esi=index = 0

mov eax, [narray+4*esi] ; или просто [narray]; eax - текущее значение max met1: cmp eax, [narray+4*esi] ; сравнение max и narray[index]

jg next

mov eax, [narray+4*esi] ; поместить narray[index] в eax next: inc esi

cmp esi, 10 ; проверены ли все 10 элементов массива? jl met1

; полученный max в eax

 

mov esi,10 ; base of position digit system

 

mov ecx, 0 ; reset digit counter

pov:

mov edx, 0 ; null into left part of devident

 

div esi

; divide for next digit = rest

 

add dl, '0'

 

 

push edx

 

 

inc ecx

; step into counter

 

cmp eax, 0

 

jne pov

 

 

mov [cnt], ecx

 

mov ebx, formax

izv:

pop edx

 

 

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

 

inc ebx

 

 

loop izv

; izv,ecx

;--- write(1, txtmes, lenmes+[cnt]) == <4>(ebx, ecx, edx) mov eax,4 ; N function=write

mov ebx,1 ; N handle=1 (stdout) mov edx,[cnt] ; number of byte add edx, lenmes

70

Соседние файлы в предмете Операционные системы