Нумерация элементов массива

Пусть имеется массив X из 30 элементов слов.

X DW 30 DUP (?)

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

Адрес (X[i]) =X + (type X)*i

где type X – размер элемента массива в байтах (для слова type X = 2, для двойного слова type X = 4).

Соответственно, если индекс массива начинается с некоторого числа k, то формула приобретет следующий вид:

Адрес (X[i]) =X + (type X)*(i-k)

Модификация адресов

Для реализации обращения к разным элементам массива в одной и той же команде цикла необходимо использовать модификацию адресов.

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

MOV CX, A[BX]

Данная команда будет работать не с указанным в ней адресом A, а с исполнительным адресом, который вычисляется по формуле:

Аисп = (A + [BX]) mod 216

Таким образом, прежде чем выполнить команду, центральный процессор прибавит к адресу A текущее содержимое регистра BX, получит некоторый новый адрес и именно из ячейки с этим адресом возьмет второй операнд. Если в результате сложения получилась слишком большая сумма, то от нее берутся только последние 16 битов, на что указывает операция mod в приведенной формуле.

Замена адреса из команды на исполнительный адрес называется модификацией адреса, а регистр, участвующий в модификации, называют регистром-модификатором или просто модификатором. В качестве модификатора можно использовать только регистры BX, BP, SI или DI.

Записывая в регистр-модификатор новые значения, обеспечивает возможность работы одной и той же команды с разными адресами.

Индексирование

Пример. Пусть имеется массив

X DW 100 DUP (?) ; X[0..99]

Требуется записать в регистр AX сумму элементов массива.

Для нахождения суммы надо сначала в AX записать 0, а затем в цикле выполнить операцию AX:=AX+X[i] при i от 0 до 99. Команда, соответствующая этой операции должна быть следующей:

ADD AX, X + 2 * i

Но данная команда недопустима правилами языка ассемблер: все части команды, в том числе и адрес должны быть фиксированными. Здесь же адрес меняется вместе с изменением индекса i.

Решить проблему поможет разбиение переменного адреса X+2*I на два слагаемых – постоянное слагаемое X, которое не зависит от индекса i, и на переменное слагаемое 2*i, зависящее от индекса. Постоянное слагаемое записывается в саму команду, а переменное слагаемое заносится в регистр-модификатор (например, в SI), название которого записывается в команду.

Так как регистр-модификатор все время один и тот же, то команда будет иметь фиксированный вид, т.е. удовлетворять правилам машинного языка. С другой стороны, меняя содержимое регистра SI в другой команде, можно заставить неменяющуюся команду работать с разными адресами и, тем самым, корректно реализовать алгоритм.

При такой реализации задачи важно правильно менять содержимое регистра SI. Сначала в SI необходимо записать 0, а затем увеличивать его значение с шагом 2; в результате команда будет работать с адресами X, X+2, X+4, …, X+198.

Таким образом, фрагмент программы нахождения суммы элементов массива X может выглядеть следующим образом:

MOV AX, 0 ; начальное значение суммы

MOV СX, 100 ; счетчик цикла

MOV SI, 0 ; начальное смещение по вектору

; (индекс)

L: ADD AX, X [SI] ; AX := AX + X[i]

ADD SI, 2 ; следующий индекс

LOOP L ; цикл 100 раз

1.3. Задание на лабораторную работу

1) Написать и выполнить следующие программы на базе примеров из теоретической части:

– записать в переменную значение большей из двух других переменных;

– записать в переменную (слово без знака) сумму двух переменных-байтов – байтов без знака (учитывая возможность переноса при сложении байтов);

– вычислить (записать в AX) сумму элементов вектора слов (считая, что результат помещается в слове).

2) Выполнить индивидуальное задание согласно варианту из табл. 4.

Таблица 4

Варианты индивидуальных заданий на лабораторную работу

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

1.4. Отчет по лабораторной работе

Отчет по лабораторной работе должен содержать:

– текст полученного задания с указанным номером варианта;

– тексты программ с комментариями (в комментариях отобразить изменения содержимого регистров и флагов по ходу выполнения программы).

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

2. ЛАБОРАТОРНАЯ РАБОТА № 4

СТЕК

2.1. Общие методические указания по выполнению лабораторной работы

Цели работы:

– Знакомство с особенностями организации стека.

– Изучение стековых команд и приемов работы со стеком.

Среда выполнения:

Интерпретатор команд DOS, ассемблер MASM, интерактивный отладчик AFD.

2.2. Теоретические сведения

2.2.1. Стек и сегмент стека

Стек – это хранилище, работа с которым ведется по следующему принципу: элемент, записанный в стек последним, считывается из него первым. Под такое хранилище можно отвести любую область памяти, но к ней предъявляются следующие требования: ее размер не должен превышать 64 Кб и ее начальный размер должен быть кратен 16. Иначе говоря, эта область должна быть сегментом памяти, который обычно называют сегментом стека.

Регистры, используемые для работы со стеком.

SS – сегментный регистр, по которому базируются адреса из стека (в нем находятся старшие 16 разрядов абсолютного адреса начала сегмента стека в ОП).

SP – указывает на вершину стека (последний положенный в стек элемент). В данном регистре хранится не сам адрес вершины, а ее смещение, т.е. адрес вершины, отсчитанный от начала сегмента стека. Поэтому абсолютный адрес вершины стека задается парой SS:SP.

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

Оформление сегмента стека размером k байт осуществляется следующим образом:

S SEGMENT STACK

DB k dup (?)

S ENDS

Параметр STACK директивы SEGMENT автоматически записывает в регистр SS начало сегмента стека, а в SP размер стека в байтах (число k).

Если стек явно не используется, то он все равно должен быть (для работы системы прерываний и ОС). Рекомендуемый размер в этом случае – 128 байт. Если же программа сама использует стек, то под него надо отводить столько места, сколько надо программе, плюс 128 байтов.

2.2.2. Стековые команды

Основными стековыми командами являются команды записи слова в стек и чтении слова из стека.

Запись слова в стек

Запись в стек осуществляется с помощью следующей команды

PUSH op

Допустимые типы операнда:

– регистр общего назначения 16 бит;

– сегментный регистр;

– слово памяти.

Команда PUSH действует следующим образом: сначала значение регистра SP уменьшается на 2 (вычитание происходит по модулю 216), т.е. SP сдвигается вверх и теперь указывает на свободную ячейку стека, а затем в нее записывается операнд.

SP := (SP - 2) mod 216, op -> [SS:SP]

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

MOV AX, 5

PUSH AX

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

Например, необходимо записать в стек символ ‘*’:

MOV AL, ‘*’ ;AL:=’*’ AH:=?

PUSH AX

Для чтения байта из стека надо считать все слово, а затем из него выделить нужный байт.

Чтение слова из стека

Чтение слова из стека осуществляется с помощью следующей команды:

POP op

Допустимые типы операнда:

– регистр общего назначения 16 бит;

– сегментный регистр (кроме CS – изменение этого регистра означает переход к следующей команде);

– слово памяти.

Команда POP считывает слово из вершины стека и присваивает ее указанному операнду. Она действует следующим образом: слово из ячейки, на которую указывает пара SS:SP, пересылается в операнд, а затем SP увеличивается на 2 (сложение происходит по модулю 216), т.е. сдвигается вниз:

[SS:SP] -> op, SP:=(SP+2) mod 216.

2.2.3 Приемы работы со стеком

Наши рекомендации