- •Глава 14 Элементы программирования на языке ассемблера После изучения данной главы вы должны знать:
- •Основные компоненты языка ассемблер Алфавит языка
- •Константы (числа и строки) Числа — только целые
- •Строки (литералы)
- •Команды (операторы)
- •Директивы (псевдооператоры)
- •Модификаторы
- •Адресация регистров и ячеек памяти в ассемблере
- •Непосредственная адресация
- •Прямая адресация регистров мпп
- •Адресация ячеек оп
- •Основные команды языка ассемблер
- •Команды пересылки данных
- •Арифметические команды
- •Команды сложения, вычитания и сравнения
- •Команды приращения
- •Команды умножения
- •Команды деления
- •Логические команды
- •Команды безусловной передачи управления
- •Команды перехода к подпрограмме и выхода из подпрограммы
- •Команда перехода к подпрограмме: call opr
- •Команда выхода из подпрограммы
- •Команды условной передачи управления
- •Команды условной передачи управления для беззнаковых данных
- •Команды условной передачи управления для знаковых данных
- •Команды условной передачи управления для прочих проверок
- •Команды управления циклами
- •Команды прерывания
- •Основные директивы ассемблера
- •Директивы определения идентификаторов
- •Директивы определения данных
- •Директивы определения сегментов и процедур
- •Видеооперации с прерыванием 21h dos
- •Программирование работы с клавиатурой
- •Программирование работы с принтерами
- •Печатающие устройства параллельного типа
- •Печатающие устройства последовательного типа
- •Программирование работы с файлами
- •Прерывания, используемые при работе с файлами на дисках
- •Некоторые аспекты создания исполняемых программ
- •Процедуры формирования программы
- •Структура программы на языке ассемблера для получения исполняемого файла формата exe
- •Основные сведения о листинге и его структуре
- •Особенности структуры машинных команд
- •Последовательность работы пк при выполнении программы
- •Краткие сведения об отладчике программ debug
- •Основные команды отладчика debug
- •Вопросы для самопроверки
Директивы определения данных
Используются для идентификации переменных и полей памяти. Формат директивы
[имя] D* выражение [,выражение] [,...].
Ключевые слова D* могут быть следующими:
DB — определить байт (1 байт);
DW — определить слово (2 байта);
DD — определить двойное слово (4 байта);
DQ — определить 8 байтов;
DT — определить 10 байтов.
Рассматриваемые директивы объявляют переменную (имя) или присваивают полям (ячейкам) памяти начальные значения; резервируют в памяти (с более поздним присвоением значения) один или несколько байтов — DB, слов — DW, двойных слов — DD и т. д.
Выражение показывает, какое количество элементов памяти необходимо выделить и какие данные там должны содержаться. Выражение может быть:
константой: const DB 56; const DW 1936; const DD 3FFH. Обязательно следует учитывать диапазон и вместимость байта, слова и т. д.; так, для DB константа не может быть больше 255, для DW — 65 535, для DD — 65 5352 – 1 = 4 294 967 295;
вектором или таблицей: table1 DB 30, 4, –15, 0, 0, 0, 56; table2 DW 1936, 3004, 56, 15. В одном псевдооператоре допускается поместить строку до 132 позиций, причем вместо повторения одного и того же значения несколько раз (0 в table1) можно использовать псевдооператор DUP (duplicate — дублировать): table1 DB 30, 4, –15, 3 dup(0), 56);
строкой символов: str1 DB 'Вы ввели слишком большое число'; str2 DB 'Bad command'; в псевдооператоре DB строка может содержать 255 символов, во всех остальных (DW, DD, DQ, DT) — только 2 символа.
пустым полем: pole1 DB ?; pole2 DW 12 dup(?), при этом в элементы резервируемой памяти при загрузке программы ничего не записывается (заносится не 0, как, например, в директиве pole3 DW 5 dup(0), а просто резервируются ячейки памяти);
символическим именем переменной: var1 DW disp; var2 DD vector (одна переменная определяется адресом другой, в директивах указывать offset не надо, поскольку имя переменной воспринимается как ее адрес). Такой вариант подходит, например, для хранения адресов ячеек памяти, меток, на которые допустимо ссылаться в программе (var1 DW disp), причем, если переменная находится в том же сегменте, что и ссылающаяся команда, то достаточно в качестве адреса указать только смещение (2 байта), то есть обойтись DW; если же переменная находится в другом сегменте, то необходимо указать и сегмент, и смещение (всего 4 байта), то есть следует использовать уже DD (var2 DD vector);
простым выражением: fn1 DB 80*3; fn2 DW (disp) + 256, вычисляемым, разумеется, только при трансляции программы.
Директивы определения сегментов и процедур
Сегмент определяется псевдооператорами:
имя_сег segment
...
имя_сег ends
В программе можно использовать 4 сегмента (по числу сегментных регистров) и для каждого указать соответствующий регистр сегмента псевдооператором ASSUME (assume — присвоить), например:
codeseg sedment
assume CS:codeseg, DS:dataseg, SS:stackseg
.
.
codeseg ends
В директиве ASSUME регистр_сег:имя_сег [,..], в частности, ASSUME cs:codeseg, указывается, что для сегмента имя_сег (codeseg) выбран регистр регистр_сег (CS).
После директивы ASSUME следует явным образом загрузить адрес начала сегмента данных в регистр DS:
mov AX, dataseg
mov DS, AX
Подобная же инициализация сегментных регистров CS и SS выполняется автоматически (по умолчанию).
Процедура определяется псевдооператорами:
имя_процедуры proc [far] ...
...
ret
имя_процедуры endp
При определении процедуры после ключевого слова proc должен быть указан атрибут дистанции near или far; если этого атрибута нет, то по умолчанию подразумевается near. Обычно процедура должна заканчиваться командой ret (return). Если процедура объявлена как near, то обращение к ней (call) должно производиться из того же сегмента; если proc far, то из любого сегмента (в этом случае командой ret из стека при возврате будет извлечено два слова: для IP и для CS).
Директивы управления трансляцией
Их несколько, наиболее часто используется END. Директива END отмечает конец программы и указывает ассемблеру, где завершить трансляцию. Формат: END [имя_программы].
Краткие сведения о программировании процедур работы с устройствами ввода-вывода
Процедуры ввода-вывода в ПК выполняются, как правило, по прерываниям. Состав и использование основных видов прерываний и служебных функций DOS прерывания 21H рассмотрены в работах [10, 35]. Ниже мы кратко остановимся на вопросах программирования ввода-вывода лишь прерываний для отображения информации на дисплее, ввода с клавиатуры, вывода на принтер и работы с файлами.
Программирование работы с дисплеем
Задание режимов работы и обмен данными с дисплеем можно выполнять при прерываниях BIOS типа 10H, а вывод данных на дисплей и при прерываниях DOS типа 21H. Для уяснения отличий прерываний BIOS от прерываний DOS рассмотрим в качестве примера несколько функций видеопрерываний BIOS.
Видеооперации с прерыванием 10H BIOS
Это прерывание обеспечивает выполнение 16 различных процедур работы с дисплеем (идентифицируются содержимым регистра AH). Приведем самые важные из них.
Перемещение курсора в заданную позицию: AH = 2.
Координаты курсора (строка, столбец) предварительно засылаются, соответственно, в регистры DH, DL. B регистре BH указывается номер страницы буфера: по умолчанию и в графическом режиме BH = 0.
Очистка экрана дисплея: AH = 6 и AL = 0, или AH = 7 и AL = 0.
Чтение символа, находящегося в текущей позиции курсора и его атрибута (только для текстовых режимов): AH = 8. В регистре ВН указывается номер страницы буфера. Считанный символ возвращается в AL, а его атрибуты — в AH (атрибуты символа — это его характеристики: цвет, цвет фона, яркость, инвертирование, мигание и т. д.).
Установка видеорежима изображения (текстовый, графический, цветность, формат, разрешающая способность и т. п.): AH = 0. Видеорежим определяется содержимым регистра AL.
Запись новых атрибутов символа и вывод символа в текущую позицию курсора: AH = 9. Прочие регистры должны содержать:
AL — записываемый символ;
BL — его атрибуты или цвет (в графическом режиме);
BH — номер видеостраницы;
CX — счетчик записываемых символов (число повторений символа).
Вывод символа в текущую позицию курсора: AH = 9 и BL = 0. Содержимое AL, BH и CX аналогично п. 5.
Чтение текущего видеостатуса: AH = 0Fh. Возвращает в: AL — текущий режим; AH — число столбцов на экране; BH — активную страницу буфера.
Вывод строки символов: AH = 13h и AL = 0. Прочие регистры должны содержать:
ES:BP — указатель строки (ее начальный адрес в памяти);
CX — длину строки;
DX — строку дисплея;
BH — номер страницы буфера.