Доступ к элементам массива

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

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

Для того чтобы локализовать определенный элемент массива, к его имени нужно добавить индекс. Так как мы моделируем массив, то должны позаботиться и о моделировании индекса. В языке ассемблера индексы массивов — это обычные адреса, но с ними работают особым образом. Другими словами, когда при программировании на ассемблере мы говорим об индексе, то скорее подразумеваем под этим не номер элемента в массиве, а некоторый адрес.

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

mas dw 0,1,2,3,4,5

Пусть эта последовательность чисел трактуется как одномерный массив. Размерность каждого элемента определяется директивой dw, то есть она равна 2 байта. Чтобы получить доступ к третьему элементу, нужно к адресу массива прибавить 6. Нумерация элементов массива в ассемблере начинается с нуля. То есть в нашем случае речь, фактически, идет о 4 -м элементе массива — 3, но об этом знает только программист; микропроцессору в данном случае все равно — ему нужен только адрес.

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

база + (индекс*размер элемента)

Архитектура микропроцессора предоставляет достаточно удобные программно-аппаратные средства для работы с массивами. К ним относятся базовые и индексные регистры, позволяющие реализовать несколько режимов адресации данных. Используя данные режимы адресации, можно организовать эффективную работу с массивами в памяти. Вспомним эти режимы:

· индексная адресация со смещением — режим адресации, при котором эффективный адрес формируется из двух компонентов:

o постоянного (базового) — указанием прямого адреса массива в виде имени идентификатора, обозначающего начало массива;

o переменного (индексного) — указанием имени индексного регистра. К примеру:

mas dw 0,1,2,3,4,5... mov si,4;поместить 3-й элемент массива mas в регистр ax: mov ax,mas[si]

· базовая индексная адресация со смещением — режим адресации, при котором эффективный адрес формируется максимум из трех компонентов:

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

o переменного (базового) — указанием имени базового регистра;

o переменного (индексного) — указанием имени индексного регистра.

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

Напомним, что в качестве базового регистра может использоваться любой из восьми регистров общего назначения. В качестве индексного регистра также можно использовать любой регистр общего назначения, за исключением esp/sp.

Микропроцессор позволяет масштабировать индекс. Это означает, что если указать после имени индексного регистра знак умножения “*” с последующей цифрой 2, 4 или 8, то содержимое индексного регистра будет умножаться на 2, 4 или 8, то есть масштабироваться.

Применение масштабирования облегчает работу с массивами, которые имеют размер элементов, равный 2, 4 или 8 байт, так как микропроцессор сам производит коррекцию индекса для получения адреса очередного элемента массива. Нам нужно лишь загрузить в индексный регистр значение требуемого индекса (считая от 0). Кстати сказать, возможность масштабирования появилась в микропроцессорах Intel, начиная с модели i486. По этой причине в рассматриваемом здесь примере программы стоит директива .486. Ее назначение, как и ранее использовавшейся директивы .386, в том, чтобы указать ассемблеру при формировании машинных команд на необходимость учета и использования дополнительных возможностей системы команд новых моделей микропроцессоров.

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

Пример: Просмотр массива слов с использованием масштабированияMASMMODEL smallSTACK 256.data;начало сегмента данных;тексты сообщений:mes1 db 'не равен 0!$',0ah,0dhmes2 db 'равен 0!$',0ah,0dhmes3 db 0ah,0dh,'Элемент $'mas dw 2,7,0,0,1,9,3,6,0,8;исходный массив.code.486;это обязательноmain: mov ax,@data mov ds,ax;связка ds с сегментом данных xor ax,ax;обнуление axprepare: mov cx,10;значение счетчика цикла в cx mov esi,0;индекс в esicompare: mov dx,mas[esi*2];первый элемент массива в dx cmp dx,0;сравнение dx c 0 je equal;переход, если равноnot_equal:;не равно mov ah,09h;вывод сообщения на экран lea dx,mes3 int 21h mov ah,02h;вывод номера элемента массива на экран mov dx,si add dl,30h int 21h mov ah,09h lea dx,mes1 int 21h inc esi;на следующий элемент dec cx;условие для выхода из цикла jcxz exit;cx=0? Если да — на выход jmp compare;нет — повторить циклequal:;равно 0 mov ah,09h;вывод сообщения mes3 на экран lea dx,mes3 int 21h mov ah,02h mov dx,si add dl,30h int 21h mov ah,09h;вывод сообщения mes2 на экран lea dx,mes2 int 21h inc esi;на следующий элемент dec cx;все элементы обработаны? jcxz exit jmp compareexit: mov ax,4c00h;стандартный выход int 21hend main;конец программы

Еще несколько слов о соглашениях:

· Если для описания адреса используется только один регистр, то речь идет о базовой адресации и этот регистр рассматривается как базовый:

;переслать байт из области данных, адрескоторой находится в регистре ebx:mov al,[ebx]

· Если для задания адреса в команде используется прямая адресация (в виде идентификатора) в сочетании с одним регистром, то речь идет об индексной адресации. Регистр считается индексным, и поэтому можно использовать масштабирование для получения адреса нужного элемента массива:

add eax,mas[ebx*4];сложить содержимое eax с двойным словом в памяти;по адресу mas + (ebx)*4

· Если для описания адреса используются два регистра, то речь идет о базово-индексной адресации. Левый регистр рассматривается как базовый, а правый — как индексный. В общем случае это не принципиально, но если мы используем масштабирование с одним из регистров, то он всегда является индексным. Но лучше придерживаться определенных соглашений. Помните, что применение регистров ebp/bp и esp/sp по умолчанию подразумевает, что сегментная составляющая адреса находится в регистре ss.

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

К примеру:

mov ax,mas[ebx][ecx*2];адрес операнда равен [mas+(ebx)+(ecx)*2]...sub dx,[ebx+8][ecx*4];адрес операнда равен [(ebx)+8+(ecx)*4]

Но имейте в виду, что масштабирование эффективно лишь тогда, когда размерность элементов массива равна 2, 4 или 8 байт. Если же размерность элементов другая, то организовывать обращение к элементам массива нужно обычным способом, как описано ранее.

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

Пример: Обработка массива элементов с нечетной длинойMASMMODEL small;модель памятиSTACK 256;размер стека.data;начало сегмента данныхN=5;количество элементов массиваmas db 5 dup (3 dup (0)).code;сегмент кодаmain:;точка входа в программу mov ax,@data mov ds,ax xor ax,ax;обнуление ax mov si,0;0 в si mov cx,N;N в cxgo: mov dl,mas[si];первый байт поля в dl inc dl;увеличение dl на 1 (по условию) mov mas[si],dl;заслать обратно в массив add si,3;сдвиг на следующий элемент массива loop go;повтор цикла mov si,0;подготовка к выводу на экран mov cx,Nshow:;вывод на экран содержимого;первых байт полей mov dl,mas[si] add dl,30h mov ah,02h int 21h loop showexit: mov ax,4c00h;стандартный выход int 21hend main;конец программы

Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: