- •Введение
- •1. Особенности персонального компьютера
- •1.1. Оперативная память
- •1.2. Регистры
- •1.3. Представление данных
- •1.4. Представление команд
- •2. Язык ассемблера. Начальные сведения
- •2.1. Лексемы
- •2.2. Предложения
- •2.3. Директивы определения данных
- •2.4. Директивы эквивалентности и присваивания
- •2.5. Выражения
- •3. Пересылки. Арифметические команды
- •3.1. Обозначения операторов команд
- •3.2. Команды пересылки
- •3.3. Команды сложения и вычитания
- •3.4. Команды умножения и деления
- •3.5. Изменение размера числа
- •3.6. Примеры
- •3.7. Лабораторная работа № 1
- •4. Переходы. Циклы
- •4.1. Безусловный переход
- •4.2. Команды сравнения и условного перехода
- •4.3. Команды управления циклом
- •4.4. Вспомогательные операции ввода-вывода
- •4.5. Массивы
- •4.6. Лабораторная работа № 2
- •5. Программные сегменты
- •5.1. Сегментирование адресов в пк
- •5.2. Программные сегменты
- •5.3. Начальная загрузка сегментных регистров
- •5.4. Структура программы
- •6. Стек
- •6.1. Стек и сегмент стека
- •6.2. Стековые команды
- •6.3. Приемы работы со стеком
- •7. Процедуры
- •7.1. Дальние переходы
- •7.2. Подпрограммы-процедуры
- •7.3. Передача параметров через регистры
- •7.4. Передача параметров через стек
- •7.5. Локальные данные процедур
- •7.6. Лабораторная работа № 3
- •8. Ввод и вывод данных
- •8.1. Реализация основных операций ввода-вывода
- •8.2. Операции ввода-вывода
- •8.3. Пример структуры программы
- •Заключение
- •Библиографический список
- •Оглавление
- •Учебное издание
- •394026 Воронеж, Московский просп., 14
7.5. Локальные данные процедур
Во многих процедурах не возникает проблемы с хранением локальных данных (величин, нужных только на время выполнения процедуры) – для них достаточно и регистров. Однако если в процедуре много локальных данных, тогда возникает вопрос: где отводить место для них? Конечно, можно выделить место в сегменте данных, но это плохо, т. к. большую часть времени это место памяти будет пропадать зря. Лучше выделять это место в стеке: при входе в процедуру в вершине стека «захватывается» нужное число байтов для локальных данных, а перед выходом это место освобождается. В таком случае место в памяти занимается только на время выполнения процедуры.
Такой «захват» места в стеке можно делать как в случае передачи параметров процедуре через регистры, так и в случае передачи параметров через стек. Для этого надо запомнить в стеке текущее значение регистра ВР и затем установить его на вершину стека (при передаче параметров через стек это есть ничто иное, как «входные» действия), после чего надо уменьшить значение указателя стека SP на число «захватываемых» байтов. Например, если некоторой процедуре Р требуется 3 байта (скажем, 2 байта под локальную переменную А и 1 байт под локальную переменную В), тогда указанные действия реализуются следующими командами (на рис. 39 изображено состояние стека после этих команд):
P PROC
PUSH BP
MOV BP,SP
SUB SP,3
...
Рис. 39. Хранение локальных данных в стеке
После этого доступ к локальным данным осуществляется с помощью выражений вида [ВР-k]; например, [ВР-2] - это адрес локальной переменной А, а [ВР-3] - адрес локальной переменной В.
При завершении работы процедуры надо выполнить такие действия:
...
MOV SP, BP ;отказ от места для локальных данных
POP BP ;восстановление старого значения ВР
RET ;(или RET n) - возврат из процедуры
Р ENDS
В качестве конкретного примера опишем близкую процедуру DIF, которая подсчитывает, сколько различных символов входит в заданную строку (символьный массив), при условии, что начальный адрес строки передается через регистр ВХ, а длина строки – через СХ, а результат возвращается через АХ.
Воспользуемся следующим алгоритмом. Заводим в стеке локальный массив из 256 байтов – по одному на каждый возможный символ, причем символу с кодом к поставим в соответствие байт стека с адресом [BP-256+k], т. е. нумеруем элементы этого стекового массива «сверху вниз», и обнуляем этот массив. Затем просматриваем заданную строку и для каждого входящего в нее символа записываем 1 в соответствующий элемент локального массива; тем самым в этом массиве будут отмечены все символы, которые хотя бы раз входили в строку. В конце подсчитываем число единиц в локальном массиве.
DIF PROC
; «входные» действия
PUSH BP
MOV BP, SP
SUB SP, 256
;занять место в стеке под локальный массив
PUSH ВХ
;спасти регистры, используемые в процедуре
PUSH СХ
PUSH SI
;обнуление локального массива
MOV АХ, СХ ;сохранить длину строки
MOV СХ, 256 ;длина локального массива
MOV SI, 0 ;индекс в этом массиве (от 0 до 255)
DIF1: MOV BYTE PTR [BP-256+SI], 0
INC SI
LOOP DIF1
MOV СХ, АХ ;восстановить в СХ длину строки
;просмотр строки и запись 1 в локальный массив
MOV АН, 0 ;для расширения AL —> АХ
DIF2: MOV AL,[ВХ] ;код очередного символа строки
MOV SI,AX ;перепись его в модификатор SI
MOV BYTE PTR [BP-256+SI], l
;запись 1 в локальный массив
INC BX ; адрес следующего символа
LOOP DIF2
;подсчет числа 1 в локальном массиве
MOV АХ, 0 ; счетчик единиц
MOV СХ, 256 ; длина локального массива
MOV SI, 0 ; индекс в этом массиве
DIF3: CMP BYTE PTR [BP-256+SI], 1
JNE DIF4
INC AX
DIF4: INC SI
LOOP DIF3
; «выходные» действия
POP SI ; восстановить регистры
POP СХ
POP BX
MOV SP, BP ;освободить стек от лок. массива
POP ВР ;восстановить старое значение ВР
RET
DIF ENDP