Справочник по языку Ассемблера IBM PC

     

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


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

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

Если последовательность однотипных элементов в памяти трактуется как двухмерный массив, расположенный по строкам, то адрес элемента (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) и его обнуления .data mas db 23,4,5,67,5,6,7,99,67,8,9,23,87,9,0,8 i=2 j=3 .code ... mov si,4*1*i mov di,j mov al,mas[si][di] ;в al элемент mas(2,3) ...

    В качестве законченного примера рассмотрим программу поиска элемента в двухмерном массиве чисел (листинг 5). Элементы массива заданы статически.
    Листинг 5. Поиск элемента в двухмерном массиве ;prg_11_4.asm MASM MODEL small STACK 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,'$' .code main: 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,0 iternal: ;внутренний цикл по строкам inc si ;передвижение на следующий элемент в строке ;сравниваем содержимое текущего элемента в ax с искомым элементом: cmp ax,elem ;если текущий совпал с искомым, то переход на here для обработки, ;иначе цикл продолжения поиска je here ;иначе — цикл по строке cx=2 раз loop iternal here: 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 21h exit: ;выход mov ax,4c00h ;стандартное завершение программы int 21h end main ;конец программы

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

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




    Содержание раздела