Описание и инициализация массива в программе 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Описание и инициализация массива в программе



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

  1. Перечислением элементов массива в поле операндов одной из директив описания данных. При перечислении элементы разделяются запятыми. К примеру:
;массив из 5 элементов.Размер каждогоэлемента 4 байта:mas dd 1,2,3,4,5
  1. Используя оператор повторения dup. К примеру:
;массив из 5 нулевых элементов.;Размер каждого элемента 2 байта:mas dw 5 dup (0)


Такой способ определения используется для резервирования памяти с целью размещения и инициализации элементов массива.

  1. Используя директивы label и rept. Пара этих директив может облегчить описание больших массивов в памяти и повысить наглядность такого описания. Директива rept относится к макросредствам языка ассемблера и вызывает повторение указанное число раз строк, заключенных между директивой и строкой endm. К примеру, определим массив байт в области памяти, обозначенной идентификатором mas_b. В данном случае директива label определяет символическое имя mas_b, аналогично тому, как это делают директивы резервирования и инициализации памяти. Достоинство директивы label в том, что она не резервирует память, а лишь определяет характеристики объекта. В данном случае объект — это ячейка памяти. Используя несколько директив label, записанных одна за другой, можно присвоить одной и той же области памяти разные имена и разный тип, что и сделано в следующем фрагменте:
...n=0...mas_b label bytemas_w label wordrept 4 dw 0f1f0hendm

В результате в памяти будет создана последовательность из четырех слов f1f0. Эту последовательность можно трактовать как массив байт или слов в зависимости от того, какое имя области мы будем использовать в программе — mas_b или mas_w.

  1. Использование цикла для инициализации значениями области памяти, которую можно будет впоследствии трактовать как массив.
    Посмотрим на примере листинга 2, каким образом это делается.
Листинг 2 Инициализация массива в цикле;prg_12_1.asmMASMMODEL smallSTACK 256.datames db 0ah,0dh,'Массив- ','$'mas db 10 dup (?);исходный массивi db 0.codemain: mov ax,@data mov ds,ax xor ax,ax;обнуление ax mov cx,10;значение счетчика цикла в cx mov si,0;индекс начального элемента в cxgo:;цикл инициализации mov bh,i;i в bh mov mas[si],bh;запись в массив i inc i;инкремент i inc si;продвижение к следующему элементу массива loop go;повторить цикл;вывод на экран получившегося массива mov cx,10 mov si,0 mov ah,09h lea dx,mes int 21hshow: mov ah,02h;функция вывода значения из al на экран mov dl,mas[si] add dl,30h;преобразование числа в символ int 21h inc si loop showexit: mov ax,4c00h;стандартный выход int 21hend main;конец программы

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

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

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

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

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

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

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

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

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

  • индексная адресация со смещением — режим адресации, при котором эффективный адрес формируется из двух компонентов:
    • постоянного (базового) — указанием прямого адреса массива в виде имени идентификатора, обозначающего начало массива;
    • переменного (индексного) — указанием имени индексного регистра.
      К примеру:
mas dw 0,1,2,3,4,5... mov si,4;поместить 3-й элемент массива mas в регистр ax: mov ax,mas[si]
  • базовая индексная адресация со смещением — режим адресации, при котором эффективный адрес формируется максимум из трех компонентов:
    • постоянного (необязательный компонент), в качестве которой может выступать прямой адрес массива в виде имени идентификатора, обозначающего начало массива, или непосредственное значение;
    • переменного (базового) — указанием имени базового регистра;
    • переменного (индексного) — указанием имени индексного регистра.

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

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

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

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

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

Листинг 3. Просмотр массива слов с использованиеммасштабирования;prg_12_2.asmMASMMODEL 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 байт. Если же размерность элементов другая, то организовывать обращение к элементам массива нужно обычным способом, как описано ранее.

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

Листинг 4. Обработка массива элементов с нечетной длиной;prg_11_3.asmMASMMODEL 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;конец программы

Двухмерные массивы

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

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

Если последовательность однотипных элементов в памяти трактуется как двухмерный массив, расположенный по строкам, то адрес элемента (i, j) вычисляется по формуле

(база + количество_элементов_в_строке * размер_элемента * i+j)

Здесь i = 0...n–1 указывает номер строки, а j = 0...m–1 указывает номер столбца.

Например, пусть имеется массив чисел (размером в 1 байт) mas(i, j) с размерностью 4 на 4
(i= 0...3, j = 0...3):

23 04 05 67 05 06 07 99 67 08 09 23 87 09 00 08

В памяти элементы этого массива будут расположены в следующей последовательности:

23 04 05 67 05 06 07 99 67 08 09 23 87 09 00 08

Если мы хотим трактовать эту последовательность как двухмерный массив, приведенный выше, и извлечь, например, элемент
mas(2, 3) = 23, то проведя нехитрый подсчет, убедимся в правильности наших рассуждений:

Эффективный адрес mas(2, 3) = mas + 4 * 1 * 2 + 3 = mas + 11

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

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

  • сочетание прямого адреса, как базового компонента адреса, и двух индексных регистров для хранения индексов:
mov ax,mas[ebx][esi]
  • сочетание двух индексных регистров, один из которых является и базовым и индексным одновременно, а другой — только индексным:
mov ax,[ebx][esi]

В программе это будет выглядеть примерно так:

;Фрагмент программы выборки элемента;массива mas(2,3) и его обнуления.datamas db23,4,5,67,5,6,7,99,67,8,9,23,87,9,0,8i=2j=3.code... mov si,4*1*i mov di,j mov al,mas[si][di];в al элемент mas(2,3)...

В качестве законченного примера рассмотрим программу поиска элемента в двухмерном массиве чисел (листинг 5). Элементы массива заданы статически.

Листинг 5. Поиск элемента в двухмерном массиве;prg_11_4.asmMASMMODEL smallSTACK 256.data;матрица размером 2x5 — если ее не инициализировать,;то для наглядности она может быть описана так:;array dw 2 DUP (5 DUP (?));но мы ее инициализируем:array dw 1,2,3,4,5,6,7,3,9,0;логически это будет выглядеть так:;array= {1 2}; {3 4}; {5 6}; {7 3}; {9 0} elem dw 3;элемент для поискаfailed db 0ah,0dh,'Нет такого элемента в массиве!','$'success db 0ah,0dh,'Такой элемент в массиве присутствует ','$'foundtime db?;количество найденных элементовfnd db ' раз(а)',0ah,0dh,'$'.codemain: mov ax,@data mov ds,ax xor ax,ax mov si,0;si=столбцы в матрице mov bx,0;bx=строки в матрице mov cx,5;число для внешнего цикла (по строкам)external:;внешний цикл по строкам mov ax,array[bx][si];в ax первый элемент матрицы push cx;сохранение в стеке счётчика внешнего цикла mov cx,2;число для внутреннего цикла (по столбцам) mov si,0iternal:;внутренний цикл по строкам inc si;передвижение на следующий элемент в строке;сравниваем содержимое текущего элемента в ax с искомым элементом: cmp ax,elem;если текущий совпал с искомым, то переход на here для обработки,;иначе цикл продолжения поиска je here;иначе — цикл по строке cx=2 раз loop iternalhere: jcxz move_next;просмотрели строку? inc foundtime;иначе увеличиваем счётчик совпавшихmove_next:;продвижение в матрице pop cx;восстанавливаем CX из стека (5) add bx,1;передвигаемся на следующую строку loop external;цикл (внешний) cmp foundtime,0h;сравнение числа совпавших с 0 ja eql;если больше 0, то переходnot_equal:;нет элементов, совпавших с искомым mov ah,09h;вывод сообщения на экран mov dx,offset failed int 21h jmp exit;на выходeql:;есть элементы, совпавшие с искомым mov ah,09h;вывод сообщений на экран mov dx,offset success int 21h mov ah,02h mov dl,foundtime add dl,30h int 21h mov ah,09h mov dx,offset fnd int 21hexit:;выход mov ax,4c00h;стандартное завершение программы int 21hend main;конец программы

При анализе работы программы не забывайте, что в языке ассемблера принято элементы массива нумеровать с 0. При поиске определенного элемента массив просматривается от начала и до конца.
Приведенная программа сохраняет в поле foundtime количество вхождений искомого элемента в массив. В качестве индексных регистров используются si и bx.



Поделиться:


Последнее изменение этой страницы: 2017-02-05; просмотров: 521; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 52.14.183.150 (0.016 с.)