- •Аппаратно-ориентированное программирование
- •Ббк 32.973.73
- •Удк 681.3 ббк 32.973.73ф 73
- •1. Основы программирования на ассемблере
- •1.1. Принципы построения ассемблерных программ
- •1.2. Понятие архитектуры компьютера
- •1.3. Регистры программиста в ia32
- •1.4. Описание сегментной структуры программы
- •2. Простейшие средства ассемблера
- •2.1. Средства описания данных
- •2.2. Обращения к функциям ос посредством прерываний
- •2.3. Средства преобразования в исполняемый файл
- •2.4. Управление строками при выводе и ввод данных
- •2.5. Простейшие способы адресации
- •3. Архитектурные элементы для построения программ
- •3.1. Организация условных переходов
- •3.2. Средства организации циклов
- •3.3. Особенности команд умножения и деления
- •3.4. Организация процедур
- •3.5. Неарифметические операции над кодами
- •4. Использование неэлементарных способов адресации
- •4.1. Косвенно-регистровая адресация
- •4.2. Использование индексной адресации данных
- •4.3. Базовая и индексно базовая адресации
- •4.4. Адресация с масштабированием
- •5. Взаимосвязи программных единиц
- •5.1. Многомодульная разработка программ
- •5.2. Использование библиотек объектных модулей
- •5.3. Организация стекового кадра подпрограммы
- •5.4. Программный доступ к системным функциям Win32
- •5.5. Особенности использования объектных файлов формата coff
- •5.6. Стандартный доступ к системным функциям Unix
- •6. Вспомогательные средства базовой архитектуры
- •6.1. Использование строковых команд пересылки
- •6.2. Применение строковых команд сравнения
- •7. Использование ассемблерных отладчиков
- •7.1. Особенности отладчика gdb для программ в Linux
- •7.2. Отладчики текстового режима для Windows
- •Библиографический список
- •Оглавление
4.2. Использование индексной адресации данных
В простейших архитектурах компьютеров удавалось ограничиться косвенно-регистровой адресацией для доступа к различным областям памяти с помощью одной и той же команды. Но для программиста удобней строить программы, используя явный индекс - номер элемента массива. Такой прием восходит к математическим средствам записи элементов массива, и он был в значительной степени реализован с помощью индексной адресации данных.
Существо индексной адресации данных - использование в записи операнда регистра, содержащего индекс - порядковый номер байта, относительно начала массива. С учетом машинных особенностей численного смещения (а не абстрактного нумерующего индекса) значения индексов в этом способе адресации изменяются, начиная с нуля (а не от единицы, как в традиционном математическом индексировании). Такой подход к индексированию должен быть известен и привычен читателю после овладения языком Си (или даже языком Бейсик).
На ассемблере NASM операнды, задаваемые индексным способом адресации, записываются в форме [имя_массива + индексный регистр] или [смещение + индексный регистр]. Первый вариант широко используется для именованных областей, второй наблюдается при просмотре исполняемых файлов с помощью дизассемблеров и отладчиков, когда нет достаточной информации об исходных программах на языке ассемблера. В частности, индексному способу адресации отвечают обозначения операндов [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-битной архитектуры размера числовых элементов, составляющего 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
narray dd 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.
Кроме данных выше основных форм записи операндов в индексном способе адресации возможно также применение записи [имя_области + смещение + индексный_регистр], два первых члена которой на этапе компиляции превращаются в одно числовое смещение от начала сегмента данных, сводясь тем самым ко второй из записанных в начале разделов основных форм. (Внутреннее представление этого способа адресации в машинных кодах и есть машинная форма записи операнда [смещение_от_начала_сегмента + индексный_регистр].)