48552 (588566), страница 12
Текст из файла (страница 12)
2.1 Методика выполнения.
Алгоритм решения задачи:
Допустим, что байт, значение которого нужно вывести, находится в регистре DH, и имеется таблица символов "0123456789ABCDEF". Байт состоит из двух шестнадцатеричных цифр. С учетом этого задачу можно решить так: нужно вывести на экран два символа из этой таблицы. Сначала - символ с номером, равным старшему полубайту числа, а потом с номером, равным младшему полубайту.
Для решения задачи нужно решить две небольшие проблемы:
Записать в AL символ с нужным номером. Воспользуемся регистровым косвенным режимом адресации со смещением. Для этого значение каждого полубайта следует записывать в BX;
Записать в BX значение полубайта.
Простейший способ решения задачи.
Пример №2.1
. model tiny | ; модель памяти, в которой сегменты кода, данных и стека объединены. |
. code | ; сегмент кода, который содержит данные. |
org 100h | ; начало СОМ-файла |
begin: | ; метка начала кода программы |
mov dh, 10 | ; заносим в регистр dh число 10 |
mov bl, dh | ; заносим в регистр bl число 10 |
xor bh, bh | ; Обнуление вх |
and bl, 0F0h | ; осуществляем логическое (побитовое) умножение dh на 0f0h. |
shr bl, 4 | ; сдвиг в право на 4 бита |
mov al, table [bx] | ; заносим в регистр al значение строки данных |
int 29h | ; вызов прерывания DOS - вызов символа |
mov bl, dh | ; заносим в регистр bl значение регистра dh |
and bl, 0Fh | ; осуществляем логическое (побитовое) умножение dh на 0fh. |
mov al, table [bx] | ; заносим в регистр al значение строки данных |
int 29h | ; вызов прерывания DOS - вызов символа; |
mov al, 13 | ; заносим в регистр al число 13 |
int 29h | ; вызов прерывания DOS - вызов символа; |
mov al, 10 | ; заносим в регистр al число 10 |
int 29h | ; вызов прерывания DOS - вызов символа |
ret | ; функция DOS "завершить программу" |
table db '0123456789ABCDEF' | ; cтрока с содержащая выводимые данные. |
end begin | ; метка окончания кода программы |
3. Вывод значения байта в десятеричной системе счисления
3.1 Методика выполнения
Алгоритм решения задачи:
Будем считать, что байт, значение которого нужно вывести, находится в регистре DH. Однако теперь применим другой способ вывода символа цифры на экран: используем тот факт, что коды символов, обозначающих цифры, отличаются от них на 30h. Но проблема здесь другая: заранее не известно, сколько цифр нужно отобразить, одну или три. Байт может принимать значение от 0 до 255. И есть еще одна проблема. При записи числа с применением позиционной системы записи в некоторой системе счисления поступают следующим образом: вычисляют и записывают остатки от деления числа на основание системы. Так поступают до тех пор, пока частное от деления не станет равным нулю. Затем остатки выписывают в порядке, обратном тому, как они получены.
Пример:
число = 251.
Делим на 10. Частное 25, остаток "1".
Делим на 10. Частное 2, остаток "5".
Делим на 10. Частное 0, остаток "2".
Нужно вывести на экран "2", "5", "1".
Задача решается с использованием стека программы. Остатки будем помещать в стек программы с помощью оператора PUSH. Одновременно будем подсчитывать число остатков, помещенных в стек. Счетчик - CX. Потом его используем для организации цикла, в котором будем извлекать остатки из стека оператором POP. Стек организован таким образом, что оператор POP извлекает последнее слово, которое было помещено туда оператором PUSH. Отметим, что оператор PUSH помещает в стек слово (WORD) или двойное слово (DWORD). Аналогично работает и оператор POP
3.1.1 Команда PUSH (занесение операнда в стек).
Команда push уменьшает на 2 содержимое указателя стека SP и заносит на эту новую вершину двухбайтовый операнд-источник (проталкивает в стек новое данное). Проталкивать в стек можно только целые слова (не байты). Программа должна строго следить за тем, чтобы каждой команде проталкивания в стек push отвечала обратная команда выталкивания из стека pop. Если стек используется для временного хранения некоторых данных, то извлекать эти данные из стека следует в порядке, обратном их сохранению.
В качестве операнда-источника может использоваться любой 16-разрядный регистр (включая сегментный) или ячейка памяти. Команда push не воздействует на флаги процессора.
Пара команд push - pop часто используется для пересылки данного из регистра в регистр (особенно, в сегментный) через стек.
Пример 1
push ES: mem; Сохранение содержимого
push DS; слова памяти mem из
push BP; дополнительного сегмента, а также регистров DS и ВР
pop PP; Восстановление из стека
pop DS; трех операндов
pop ES: mem; в обратном порядке
Пример 2
push DS; Пересылка DS через стек
pop ES; Теперь ES=DS
Простейший способ решения задачи вывода значения байта в десятеричной системе счисления
Пример № 3.1
. model tiny | ; модель памяти, в которой сегменты кода, данных и стека объединены. |
. code | ; сегмент кода, который содержит данные. |
org 100h | ; начало СОМ-файла |
begin: | ; метка начала кода программы |
mov dh, 33 | ; заносим в регистр dh число 33 |
mov al, dh | ; заносим в регистр al число 33 |
xor ah, ah | ; обнуление ah |
mov bl, 10 | ; заносим в регистр bl число 10 |
xor cx, cx | ; обнуление сх (счетчик) |
@1: | ; устанавливаем метку @1 |
div bl | ; делим целое число (регистр ax) без знака, на число (регистр bl) |
push ax | ; пересылаем ax через стек |
inc cx | ; увеличиваем на 1 cx (счетчик) |
xor ah, ah | ; обнуление ah |
or ax, ax | ; логического (побитового) сложения само на себя |
jnz @1 | ; если флаг zf не ноль, то переходим на метку @1 |
@2: | ; вызов прерывания DOS - вызов символа; |
pop ax | ; заносим в регистр al число 10 |
xchg ah, al | ; обмен данными между операндами ah и al |
add al, 30h | ; прибавляем к al число 30h |
int 29h | ; вызов прерывания DOS - вызов символа |
loop @2 | ; реализация цикла - переходим на метку @2 |
mov al, 13 | ; заносим в регистр al число 13 |
int 29h | ; вызов прерывания DOS - вызов символа |
mov al, 10 | ; заносим в регистр al число 10 |
int 29h | ; вызов прерывания DOS - вызов символа |
ret | ; функция DOS "завершить программу" |
end begin | ; метка окончания кода программы |
Задание для выполнения.
3.1 C помощью редактора эмулятора EMU 8086 напишите программы, исходный текст которых приводится в примерах данной лабораторной работы.
3.2 Создайте исполняемые файлы типа *.com.
3.3 Изучите работу полученных программ.
3.4 Напишите программу для вывода на экран содержимого регистра DS (на основе примера №2.1). Сравните результат работы своей программы и того, что показывает отладчик.
3.5 Опишите работу команд DIV, PUSH, POP, SHL, TEST.
3.6 Установите (найдите адреса и запишите), где находятся числа, помещенные в стек.
3.7 Напишите программу для вывода на экран содержимого регистра СS (на основе примера №3.1).
3.8 Предложите другие способы решения поставленных задач.
5. Контрольные вопросы
Преимущества использования команды SHL вместо TEST (пример №1.1)?
Чем отличаются команды
SHL dx,1
и
SHL dx, cl
Как переслать содержимое X в стек и получить обратно?
Опишите методику вывода значения байта в десятеричной системе счисления?
Опишите методику вывода значения байта в шестнадцатеричной системе счисления?
Опишите методику вывода двоичного кода числа, записанного в регистр X
Стек. Принцип работы. Команды работы со стеком.
Укажите отличия в работе тандема команд
push DS
pop ES
от
push DS
pop ES
Лабораторная работа № 5, 6
КОМАНДЫ, ОБСЛУЖИВАЮЩИЕ РАБОТУ С КЛАВИАТУРОЙ
Цель работы: Освоить команды считывания данных и управления клавиатурой. Изучить способы работы с процедурами.
1. Средства BIOS
Так же как и для вывода на экран, BIOS предоставляет больше возможностей по сравнению с DOS для считывания данных и управления клавиатурой. Например, функциями DOS нельзя определить нажатие комбинаций клавиш типа Ctrl-Alt-Enter или нажатие двух клавиш Shift одновременно, DOS не может определить момент отпускания нажатой клавиши, и наконец, в DOS нет аналога функции С ungetch (), помещающей символ в буфер клавиатуры, как если бы его ввел пользователь. Все это можно осуществить, используя различные функции прерывания 16h и операции с байтами состояния клавиатуры.
INT 16h, АН = 0, 10h, 20h - Чтение символа с ожиданием
Ввод: | АН = 00h (83/84-key), 10h (101/102-key), 20h (122-key) |
Вывод: | AL = ASCII-код символа, 0 или префикс скан-кода АН = скан-код нажатой клавиши или расширенный ASCII-код |
Каждой клавише на клавиатуре соответствует так называемый скан-код (см. приложение 1), соответствующий только этой клавише. Этот код посылается клавиатурой при каждом нажатии и отпускании клавиши и обрабатывается BIOS (обработчиком прерывания INT 9). Прерывание 16h дает возможность получить код нажатия, не перехватывая, этот обработчик. Если нажатой клавише соответствует ASCII-символ, то в АН возвращается код этого символа, а в AL - скан-код клавиши. Если нажатой клавише соответствует расширенный ASCII-код, в AL возвращается префикс скан-кода (например, Е0 для серых клавиш) или 0, если префикса нет, а в АН - расширенный ASCII-код. Функция 00Н обрабатывает только комбинации, использующие клавиши 84-клавишной клавиатуры, l0h обрабатывает все 101 - 105-клавишные комбинации, 20h - 122-клавишные. Тип клавиатуры можно определить с помощью функции 09h прерывания 16h, если она поддерживается BIOS (поддерживается ли эта функция, можно узнать с помощью функции C0h прерывания 15h).
INT 16h, АН = 1, 11h, 21h - Проверка символа
Ввод: | АН = 01h (83/84-key), 11h (101/102-key), 21h (122-key) |
Вывод: | ZF = 1, если буфер пуст ZF = 0, если в буфере присутствует символ, в этом случае AL = ASCII-код символа, 0 или префикс скан-кода АН = скан-код нажатой клавиши или расширенный ASCII-код |
Символ остается в буфере клавиатуры, хотя некоторые BIOS удаляют символ из буфера при обработке функции 01h, если он соответствует расширенному ASCII-коду, отсутствующему на 84-клавишных клавиатурах.
INT 16h, АН = 05h - Поместить символ в буфер клавиатуры
Ввод: | АН = 05h СН = скан-код CL = ASCII-код |
Вывод: | AL = 00, если операция выполнена успешно AL = 01h, если буфер клавиатуры переполнен АН модифицируется многими BIOS |
Обычно можно поместить 0 вместо скан-кода в СН, если функция, которая будет выполнять чтение из буфера, будет использовать именно ASCII-код.
Например, следующая программа при запуске из DOS вызывает команду DIR (но при запуске из некоторых оболочек, например FAR, этого не произойдет).
Пример №1.1
. model tiny | ; модель памяти, в которой сегменты кода, данных и стека объединены. |
. code | ; сегмент кода, который содержит данные. |
org 100h | ; начало СОМ-файла |
begin: | ; метка начала кода программы |
mov cl,'d' | ; заносим в регистр cl - ASCII-код буквы "d" |
call ungetch | ; переходим на метку ungetch: (вызов подпрограммы) |
mov cl,'i' | ; заносим в регистр cl - ASCII-код буквы "i" |
call ungetch | ; вызываем подпрограмму |
mov cl,'r' | ; заносим в регистр cl - ASCII-код буквы "r" |
call ungetch | ; вызываем подпрограмму |
mov cl,0Dh | ; перевод строки |
Ungetch proc | ; метка начала подпрограммы |
mov ah,05h | ; AH = номер функции |
mov ch,0 | ; CH = 0 (скан-код неважен) |
int 16h | ; вызов DOS (прерывание) |
ret | ; функция DOS "завершить работу процедуры" |
Ungetch endp | ; окончание подпрограммы |
end begin | ; метка окончания кода программы |
Оформление процедур (подпрограмм).