Жанр: Учеба
Ассемблер и программирование для IBM PC
...ту
ADC DX,00
CMP DX,SHIFT ;Результат слишком велик
JB F50 ; для команды DIV?
F40:
SUB AX,AX
JMP F70
F50:
CMP ADJUST,00 ;Сдвиг нее требуется?
JZ F80
DIV SHIFT ;Сдвинуть оплату
F70: SUB DX,DX ;Стереть остаток
F80: RET
F10MULT ENDP
; Преобразование в ASCII формат:
; -----------------------------
G10WAGE PROC
LEA SI,ASCWAGE+7 ;Установить дес. точку
MOV BYTE PTR[SI],'.'
ADD SI,NODEC ;Установить правую позицию
G30:
CMP BYTE PTR[SI],'.'
JNE G35 ;Обойти, если дес.поз.
DEC SI
G35:
CMP DX,00 ;Если dx:ax " 10,
JNZ G40
CMP AX,0010 ; то операция завершена
JB G50
G40:
DIV TENWD ;Остаток - ASCII-цифра
OR DL,30H
MOV [SI],DL ;Записать ASCII символ
DEC SI
SUB DX,DX ;Стереть остаток
JMP G30
G50:
OR AL,30H ;Записать последний ASCII
MOV [SI],AL ; символ
RET
G10WAGE ENDP
; Вывод величины оплаты:
; ---------------------
K10DISP PROC
MOV COL,50 ;Установить столбец
CALL Q20CURS
MOV CX,09
LEA SI,ASCWAGE
K20: ;Стереть лидирующие нули
CMP BYTE PTR[SI],30H
JNE K30 ; пробелами
MOV BYTE PTR[SI],20H
INC SI
LOOP K20
K30:
LEA DX,MESSG3 ;Вывод на экран
MOV AH,09
INT 21H
CMP ROW,20 ;Последняя строка экрана?
JAE K80
INC ROW ; нет - увеличить строку
JMP K90
K80:
MOV AX,0601H ; да --
CALL Q10SCR ; прокрутить и
MOV COL,00 ; установить курсор
CALL Q20CURS
K90: RET
K10DISP ENDP
; Преобразование ASCII-чисел
; в двоичное представление:
; --------------------------
M10ASBI PROC
MOV MULT10,0001
MOV BINVAL,00
MOV DECIND,00
SUB BX,BX
M20:
MOV AL,[SI] ;ASCII-символ
CMP AL,'.' ;Обойти, если дес.точка
JNE M40
MOV DECIND,01
JMP M90
M40:
AND AX,000FH
MUL MULT10 ;Умножить на фактор
ADD BINVAL,AX ;Сложить с дв.значением
MOV AX,MULT10 ;Вычислить следующий
MUL TENVD ; фактор x 10
MOV MULT10,AX
CMP DECIND,00 ;Десятичная точка?
JNZ M90
INC BX ; да - обойти точку
M90:
DEC SI
LOOP M20
;Конец цикла
CMP DECIND,00 ;Была дес.точка?
JZ M100 ; да --
ADD NODEC,BX ; сложить с итогом
M100: RET
M10ASBI ENDP
; Прокрутка экрана:
; ----------------
Q10SCR PROC NEAR ;AX установлен при вызове
MOV BH,30 ;Цвет (07 для ч/б)
SUB CX,CX
MOV DX,184FH
INT 10H
RET
Q10SCR ENDP
; Установка курсора:
; -----------------
Q20CURS PROC NEAR
MOV AH,02
SUB BH,BH
MOV DH,ROW
MOV DL,COL
INT 10H
RET
Q20CURS ENDP
CODESG ENDS
END BEGIN
__________________________________________________________________________
Рис.13.6. Расчет заработной платы.
О г р а н и ч е н и я. Первое ограничение в программе, приведенной на
рис.13.6, состоит в том, что допускает не более шести десятичных знаков
после запятой. Другое ограничение - размер самой зарплаты и тот факт, что
сдвиг включает деление на число, кратное 10, a преобразование в
ASCII-формат включает деление на 10. Если значение времени или расценки
содержит больше шести десятичных знаков или зарплата превышает величину
около 655350, то программа выдает нулевой результат. На практике программа
может предусмотреть в данном случае вывод предупреждающего сообщения или
иметь подпрограммы для исключения таких ограничений.
К о н т р о л ь о ш и б о к. Программа, разработанная для
пользователей, не являющихся программистами, должна не только выдавать
предупреждающие сообщения, но также проверять корректность вводимых
значений. Правильными символами при вводе числовых значений являются цифры
от 0 до 9 и символ десятичной запятой. Для любых других символов программа
должна выдать предупреждающее сообщение и вновь повторить запрос на ввод.
Полезной командой для проверки корректности вводимых символов является
XLAT (см. гл.14).
Тщательно проверяйте программы для любых возможных состояний: нулевое
значение, максимально большие и малые значения, отрицательные значения.
Отрицательные величины
------------------------
Некоторые применения программ допускают наличие отрицательных
величин. Знак минус может устанавливаться после числа, например, 12,34-,
или перед числом -12,34. Программа может проверять наличие минуса при
преобразовании в двоичный формат. Можно оставить двоичное число
положительным, но установить соответствующий индикатор исходной
отрицательной величины. После завершения арифметических операций знак
минус при необходимости может быть вставлен в ASCII поле.
Если необходимо, чтобы двоичное число было также отрицательным, то
можно преобразовать, как обычно, ASCII-формат в двоичный, а для изменения
знака двоичного числа воспользоваться командами, описанными в гл.12
"Преобразование знака". Будьте внимательны при использовании команд IMUL и
IDIV для обработки знаковых данных. Для округления отрицательных чисел
следует не прибавлять, а вычитать фактор 5.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ
________________________________________________________________
- ASCII-формат требует один байт на каждый символ. Если поле содержит
только цифры от 0 до 9, то замена старших троек в каждом байте на нули
создает распакованный десятичный формат. Сжатие числа до двух цифр в байте
создает упакованный десятичный формат.
- После ASCII-сложения необходимо выполнить коррекцию с помощью
команды AAA; после ASCII-вычитания - коррекция с помощью команды AAS.
- Прежде чем выполнить ASCII-умножение, необходимо преобразовать
множимое и множитель в "распакованный десятичный" формат, обнулив в каждом
байте левые тройки. После умножения необходимо выполнить коррекцию
результата с помощью команды AAM.
- Прежде чем выполнить ASCII-деление, необходимо: 1) преобразовать
делимое и делитель в "распакованный десятичный" формат, обнулив в каждом
байте левые тройки и 2) выполнить коррекцию делимого с помощью команды
AAD.
- Для большинства арифметических операций используйте преобразование
чисел из ASCII-формата в двоичной формат. В процессе такого преобразования
проверяйте на корректность ASCII-символы: они должны быть от шест.30 до
шест.39, могут содержать десятичную запятую (точку) и, возможно, знак
минус.
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ
________________________________________________________________
13.1. Предположим, что регистр AX содержит 9 в ASCII коде, а регистр
BX -7 также в ASCII коде. Объясните и дайте точный результат для следующих
несвязанных операций:
а) ADD AX,33H б) ADD AX,BX
AAA AAA
в) SUB AX,BX г) SUB AX,0DH
AAS AAS
13.2. Поле UNPAK содержит шест. 01040705 в распаковочном десятичном
формате. Напишите цикл, который преобразует это содержимое в ASCII-формат,
т.е. 31343735.
13.3. Поле ASCA содержит значение 313733 в ASCII-формате, а другое
поле ASCB содержит 35. Напишите команды для умножения этих чисел в
ASCII-формате и записи произведения в поле ASCPRO.
13.4. Используя данные из вопроса 13.3, разделите ASCA на ASCB и
запишите частное в поле ASCQUO.
13.5. Выполните следующие вычисления вручную: а) преобразовать ASCII
46328 в двоичный формат и показать результат в шест.виде; б) преобразовать
полученное шест. значение обратно в ASCII-формат.
13.6. Напишите и выполните программу, которая определяет размер
памяти компьютера (INT 12H - см. гл.2), преобразует полученное значение в
ASCII-формат и выводит результат на экран в следующем виде:
Размер памяти nnn байтов.
ГЛАВА 14 Обработка таблиц
__________________________________________________________________________
Ц е л ь: Раскрыть требования для определения таблиц, организации
поиска в таблицах и сортировки элементов таблицы.
ВВЕДЕНИЕ
________________________________________________________________
Многие программные применения используют табличную организацию таких
данных, как имена, описания, размеры, цены. Определение и использование
таблиц включает одну новую команду ассемблера - XLAT. Таким образом,
использование таблиц - это лишь дело техники и применения знаний,
полученных из предыдущих глав.
Данная глава начинается определением некоторых общепринятых таблиц.
Организация поиска в таблице зависит от способа ее определения. Существует
много различных вариантов определения таблиц и алгоритмов поиска.
ОПРЕДЕЛЕНИЕ ТАБЛИЦ
________________________________________________________________
Для облегчения табличного поиска большинство таблиц определяются
систематично, т.е. элементы таблицы имеют одинаковый формат (символьный
или числовой), одинаковую длину и восходящую или нисходящую
последовательность элементов.
Таблица, которой уже приходилось пользоваться в данной книге - это
стек, представляющий собой таблицу из 64-х неинициализированных слов:
STACK DW 64 DUP(?)
Следующие две таблицы инициализированы символьными и числовыми
значениями:
MONTAB DB 'JAN','FEB','MAR', ... ,'DEC'
COSTAB DB 205,208,209,212,215,224,...
Таблица MONTAB определяет алфавитные аббревиатуры месяцев, а COSTAB -
определяет таблицу номеров служащих. Таблица может также содержать
смешанные данные (регулярно чередующиеся числовые и символьные поля). В
следующей ассортиментной таблице каждый числовой элемент (инвентарный
номер) имеет две цифры (один байт), а каждый символьный элемент
(наименование) имеет девять байтов. Точки, показанные в наименовании
"Paper" дополняют длину этого поля до 9 байт. Точки показывают, что
недостающее пространство должно присутствовать. Вводить точки
необязательно.
STOKTBL DB 12,'Computers',14,'Paper....',17,'Diskettes'
Для ясности можно закодировать элементы таблицы вертикально:
STOKTBL DB 12, 'Computers'
DB 14, 'Paper....'
DB 17, 'Diskettes'
Рассмотрим теперь различные способы использования таблиц в
программах.
ПРЯМОЙ ТАБЛИЧНЫЙ ДОСТУП
________________________________________________________________
Предположим, что пользователь ввел номер месяца - 03 и программа
должна преобразовать этот номер в алфавитное значение March. Программа для
выполнения такого преобразования включает определение таблицы алфавитных
названий месяцев, имеющих одинаковую длину. Так как самое длинное название
- September, то таблица имеет следующий вид:
MONTBL DB 'January..'
DB 'February.'
DB 'March....'
Каждый элемент таблицы имеет длину 9 байт. Адрес элемента 'January' -
MONTBL+0, 'February' - MONTBL+9, 'March' - MONTBL+18. Для локализации
месяца 03, программа должна выполнить следующее:
1. Преобразовать введенный номер месяца из ASCII 33 в двоичное
03.
2. Вычесть единицу из номера месяца: 03 - 1 = 02
3. Умножить результат на длину элемента (9): 02 х 9 = 18
4. Прибавить произведение (18) к адресу MONTBL; в результате
получится адрес требуемого названия месяца: MONTBL+18.
__________________________________________________________________________
page 60,132
TITLE DIRECT (COM) Прямой табличный доступ
CODESG SEGMENT PARA 'Code'
ASSUME CS:CODESG,DS:CODESG,ES:CODESG
ORG 100H
BEGIN: JMP SHORT MAIN
; ------------------------------------------------
THREE DB 3
MONIN DB '11'
ALFMON DB '???','$'
MONTAB DB 'JAN','FEB','MAR','APR','MAY','JUN'
DB 'JUL','AUG','SEP','OKT','NOV','DEC'
; ------------------------------------------------
MAIN PROC NEAR ;Основная процедура
CALL C10CONV ;Получить двоичное значение
CALL D10LOC ;Выделить месяц из таблицы
CALL F10DISP ;Выдать месяц на экран
RET
MAIN ENDP
; Перевод ASCII в двоичное представление:
; --------------------------------------
C10CONV PROC
MOV AH,MONIN ;Загрузить номер месяца
MOV AL,MONIN+1
XOR AX,3030H ;Удалить ASCII тройки
CMP AH,00 ;Месяц 01-09?
JZ C20 ; да - обойти
SUB AH,AH ; нет - очистить AH,
ADD AL,10 ; и перевести в двоичное
C20 RET
C10CONV ENDP
; Выделение месяца из таблицы:
; ---------------------------
D10LOC PROC
LEA SI,MONTAB
DEC AL ;Коррекция для таблицы
MUL THREE ;Умножить AL на 3
ADD SI,AX
MOV CX,03 ;Трехсимвольная пересылка
CLD
LEA DI,ALFMON
REP MOVSB ;Переслать 3 символа
RET
D10LOC ENDP
; Вывод на экран симв.месяца:
; --------------------------
F10DISP PROC
LEA DX,ALFMON
MOV AH,09
INT 21H
RET
F10DISP ENDP
CODESG ENDS
END BEGIN
__________________________________________________________________________
Рис.14.1. Прямая табличная адресация.
На рис.14.1 приведен пример прямого доступа к таблице названий
месяцев. Для краткости в программе используются вместо девятисимвольных
названий - трехсимвольные. Введенный номер месяца определен в поле MONIN.
Предположим, что некоторая подпрограмма формирует запрос на ввод номера
месяца в ASCII-формате в поле MONIN.
Описанная техника работы с таблицей называется прямым табличным
доступом. Поскольку данный алгоритм непосредственно вычисляет адрес
необходимого элемента в таблице, то в программе не требуется выполнять
операции поиска.
Хотя прямая табличная адресация очень эффективна, она возможна только
при последовательной организации. То есть можно использовать такие
таблицы, если элементы располагаются в регулярной последовательности: 1,
2, 3,... или 106, 107, 108,... или даже 5, 10, 15. Однако, не всегда
таблицы построены таким образом. В следующем разделе рассматриваются
таблицы, имеющие нерегулярную организацию.
ТАБЛИЧНЫЙ ПОИСК
________________________________________________________________
Некоторые таблицы состоят из чисел, не имеющих видимой
закономерности. Характерный пример - таблица инвентарных номеров с
последовательными номерами, например, 134, 138, 141, 239 и 245. Другой тип
таблиц состоит из распределенных по ранжиру величин, таких как подоходный
налог. В следующих разделах рассмотрим эти типы таблиц и организацию
табличного поиска.
Таблицы с уникальными элементами
----------------------------------
Инвентарные номера большинства фирм часто не имеют последовательного
порядка. Номера, обычно, группируются по категориям, первые цифры
указывают на мебель или приборы, или номер отдела. Кроме того время от
времени номера удаляются, а новые добавляются. В таблице необходимо
связать инвентарные номера и их конкретные наименования (и, если
требуется, включить стоимость). Инвентарные номера и наименования могут
быть определены в различных таблицах, например:
STOKNOS DB '101','107','109',...
STOKDCR DB 'Excavators','Processors','Assemblers',...
или в одной таблице, например:
STOKTAB DB '101','Excavators'
DB '107','Processors'
DB '109','Assemblers'
Программа на рис.14.2 определяет инвентарную таблицу и выполняет
табличный поиск. Таблица содержит шесть пар номеров и наименований. Цикл
поиска начинается со сравнения введенного инвентарного номера в поле
STOKNIN с первым номером в таблице. Если номера различные, то адрес в
таблице увеличивается для сравнения со следующим инвентарным номером. Если
номера равны, то программа (A30) выделяет наименование из таблицы и
записывает его в поле DESCRN.
Поиск выполняет максимум шесть сравнений и если требуемый номер в
таблице отсутствует, то происходит переход на программу обработки ошибки,
которая выводит на экран соответствующее сообщение.
Обратите внимание, что в начале программы имеется команда, которая
пересылает содержимое поля STOKNIN в регистр AX. Хотя STOKNIN определенно
как 3233, команда MOV загрузит в регистр AX это значение в обратной
последовательности байтов 3332. Так как элементы таблицы имеют прямую
последовательность байтов, то после команды MOV имеется команда XCHG,
которая меняет местами байты в регистре AX, возвращая им прямую
последовательность, т.е. 3233. Команда CMP, предполагая обратную
последовательность, сравнивает сначала правые байты, а затем - левые.
Следовательно, проверка на равенство будет корректной, но проверки на
больше или меньше дадут неправильные результаты. Для сравнения на больше
или меньше следует опустить команду XCHG, переслать элемент таблицы
командой MOV, скажем, в регистр BX и затем сравнить содержимое регистров
AX и BX следующим образом:
MOV AX,STOKNIN
LEA SI,STOKTAB
C20:
MOV BX,[SI]
CMP AX,BX
JA или JB ...
В программе такого типа другая таблица может определять стоимость
единицы товара. Программа может локализовать элемент таблицы, вычислить
продажную стоимость (количество товара умножить на стоимость единицы
товара) и выдать на экран наименование и продажную стоимость товара.
В примере на рис.14.2 таблица содержит двухбайтовые номера и
десятибайтовые наименования. Детальное программирование будет отличаться
для различного числа и длины элементов. Например, для сравнения
трехбайтовых полей можно использовать команду REPE CMPSB, хотя эта команда
также включает использование регистра CX.
__________________________________________________________________________
page 60,132
TITLE TABSRCH (COM) Табличный поиск
CODESG SEGMENT PARA 'Code'
ASSUME CS:CODESG,DS:CODESG,ES:CODESG
ORG 100H
BEGIN: JMP SHORT MAIN
; -----------------------------------------------
STOKNIN DW '23'
STOKTAB DB '05','Excavators'
DB '08','Lifters '
DB '09','Presses '
DB '12','Valves '
DB '23','Processors'
DB '27','Pumps '
DESCRN 10 DUP(?)
; -----------------------------------------------
MAIN PROC NEAR
MOV AX,STOKNIN ;Загрузить номер элемента
XCHG AL,AH
MOV CX,06 ;Число элементов в таблице
LEA SI,STOKTAB ;Начальный адрес таблицы
A20:
CMP AX,[SI] ;Сравнить элементы
JE A30 ;Если равны - выйти,
ADD SI,12 ; нет - следующий элемент
LOOP A20
CALL R10ERR ;Элемент в таблице не найден
RET
A30:
MOV CX,05 ;Длина описания элемента
LEA DI,DESCRN ;Адрес описания элемента
INC SI
INC SI ;Выделить описание
REP MOVSW ; из таблицы
RET
MAIN ENDP
;
R10ERR PROC
; "Вывод сообщения об ошибке"
RET
R10ERR ENDP
CODESG ENDS
END BEGIN
__________________________________________________________________________
Рис.14.2. Табличный поиск
Таблицы с ранжированием
-------------------------
Подоходный налог дает характерный пример таблицы с ранжированными
значениями. Представим себе таблицу, содержащую размеры доходов облагаемых
налогами, процент налога и поправочный коэффициент:
Размер дохода Процент налога Поправочный к-нт
0-1000.00 10 0,00
1000,01-2500,00 15 050,00
2500,01-4250,00 18 125,00
4250,01-6000,00 20 260,00
6000,01 и более 23 390,00
В налоговой таблице процент увеличивается в соответствии с увеличением
налогооблагаемого дохода. Элементы таблицы доходов содержат максимальные
величины для каждого шага:
TAXTBL DD 100000,250000,425000,600000,999999
для организации поиска в такой таблице, программа сравнивает доxод
налогоплатильщика с табличным значением дохода:
- если меньше или равно, то использовать соответствующий процент
и поправку;
- если больше, то перейти к следующему элементу таблицы.
Величина налога рассчитывается по формуле:
Доход х Процент налога : 100 - поправочный к-нт
Табличный поиск с использованием сравнения строк
--------------------------------------------------
Если элемент таблицы превышает длину в два байта, то для операции
сравнения можно использовать команду REPE CMPS. Предположим, что таблица
инвентарных номеров (рис.14.2) переделана для трехбайтовых номеров. Если
STOKNIN является первым полем в области данных, а STOKTAB - вторым, то они
могут выглядеть следующим образом:
Данные: |123|035Excavators|038Lifters |049Presses | ...
| | | | | | |
Адрес: 00 03 06 16 19 29 32
Программа на рис.14.3 определяет таблицу STOKTAB, включая последний
элемент '999' для индикации конца таблицы при поиске. Программа поиска
сравнивает содержимое каждого элемента таблицы с содержимым поля STOKNIN:
Элемент таблицы STOKNIN Результат сравнения
035 123 Меньше: проверить след.эл-т
038 123 Меньше: проверить след.эл-т
049 123 Меньше: проверить след.эл-т
102 123 Меньше: проверить след.эл-т
123 123 Равно: элемент найден
Заметим, что команда CMPSB на рис.14.3 сравнивает байт за байтом,
пока байты не будут равны и автоматически увеличивает регистры SI и DI.
Регистр CX инициализируется значением 03, а начальные относительные
адреса в регистрах SI и DI устанавливаются равными 03 и 00 соответственно.
Сравнение с первым элементом таблицы (035:123) завершается на первом
байте, после этого регистр SI содержит 04, DI: 01, CX: 02. Для следующего
сравнения регистр SI должен иметь значение 16, а DI: 00. Корректировка
регистра DI сводится к простой перезагрузке адреса STOKNIN. Увеличение
адреса следующего элемента таблицы, который должен быть в регистре SI,
зависит от того, на каком байте (первом, втором или третьем) закончилось
предыдущее сравнение. Регистр CX содержит число байт, не участвующих в
сравнении, в данном случае - 02. Прибавив к содержимому регистра SI
значение в регистре CX и длину наименования, получим относительный адрес
следующего элемента:
Адрес в SI после CMPSB 04
Прибавить CX 02
Прибавить длину наименования 10
--
Относительный адрес след.элемента 16
Так как регистр CX всегда содержит число байт, не участвующих в
сравнении (если такие есть), то расчет справедлив для всех случаев:
прекращение сравнения после 1, 2 или 3 байта. Если сравниваются одинаковые
элементы, то регистр CX получит значение 00, а адрес в регистре SI укажет
на требуемое наименование.
__________________________________________________________________________
page 60,132
TITLE TABSRCH (COM) Табличный поиск, использующий CMPSB
CODESG SEGMENT PARA 'Code'
ASSUME CS:CODESG,DS:CODESG,ES:CODESG
ORG 100H
BEGIN: JMP SHORT MAIN
; ----------------------------------------------------
STOKNIN DW '123'
STOKTAB DB '035','Excavators' ;Начало таблицы
DB '038','Lifters '
DB '049','Presses '
DB '102','Valves '
DB '123','Processors'
DB '127','Pumps '
DB '999', 10 DUP(' ') ;Конец таблицы
DESCRN 10 DUP(?)
; ----------------------------------------------------
MAIN PROC NEAR
CLD
LEA SI,STOKTAB ;Начальный адрес таблицы
A20:
MOV CX,03 ;Сравнивать по 3 байта
LEA DI,STOKNIN ;Адрес искомого элемента
REPE CMPSB ;Сравнение
JE A30 ;Если равно - выйти,
JA A40 ;если больше - нет в таблице
ADD SI,CX ;Прибавить CX к адресу
JMP A20 ;Следующий элемент таблицы
A30:
MOV CX,05 ;Пересылать 5 слов
LEA DI,DESCRN ;Адрес описания
REP MOVSV ;Переслать из таблицы
RET
A40:
CALL R10ERR ;элемент в таблице не найден
RET
MAIN ENDP
R10ERR PROC
; "Вывод на экран сообщения об ошибке"
RET
R10ERR ENDP
CODESG ENDS
END BEGIN
__________________________________________________________________________
Рис.14.3. Табличный поиск с использованием команды CMPSB
Таблицы с элементами переменной длины
---------------------------------------
Существуют таблицы, в которых элементы имеют переменную длину. Каждый
элемент такой таблицы может завершаться специальным символом
ограничителем, например, шест.00; конец таблицы можно обозначить
ограничителем шест.FF. В этом случае необходимо гарантировать, чтобы
внутри элеме
...Поділитися у соцмережах