Этот раздел главы с помощью примеров исполльзования наиболее
распространенных операций научит вас применять сопроцессор 8087,
предполагая, что вы этого совсем не умеете. Все примеры просты,
т.е. в них не делается попыток обработки всех возможных ошибок или
реакции на какие=либо специальные числа, с которыми может работать
микросхема 8087. Сопроцессор 8087, конечно же, имеет такие
возможности, но эти действия относятся к более сложным. Когда вы
освоите основы техники, вы сможете добавить к этим примерам
некоторые фрагменты, которые превратят их в настоящие подпрограммы
общего назначения.
Первый пример - исходный текст программы на Фиг. 7.23. Эта
программа распечатывает короткое действительное представление
степеней 10 от 103 до 1039. Как мы уже видели в разделе,
посвященном представлению данных, Макроассемблер фирмы IBM не имеет
возможностей непосредственно генерировать действительные числа.
Наличие такой таблицы чисел облегчит вам представление в виде
констант степеней 10. По этой таблице вы сможете определить
шестнадцатеричное представление числа, которое нужно использовать в
программе.
Программа вычисляет только каждую третью степень 10, и
использует короткий действительный формат. Если вы работаете с
много большими числами, или вам нужна большая точность, тогда нужно
выполнить этот пример, используя длинный действительный формат
чисел, а также построить каждую степень 10. Выполните это в
качестве упражнения.
Первоочередная цель этого примера - введение в программирование
и работу на сопроцессоре 8087. Это отдельная самостоятельная
программа, предназначенная для выполнения в виде файла типа .EXE.
Прежде чем разобраться в самой программе, заметим, что в нее входит
сегмент STACK, необходимый в файле типа .EXE. Сначала в сегменте
CODE записаны поля данных, а выполнение программы начинается с
метки CALCULATE_POWER. Заглянув вперед, на оператор END, вы
увидите, что первой выполняемой командой будет команда, помеченная
CALCULATE_POWER, так как она указана в операторе END.
Первая часть программы выполняет инициализацию. Перед
загрузкой указателя на сегмент CODE в регистр DS программа помещает
в стек адрес возврата из файла типа .EXE. Затем с помощью команды
FINIT инициализируется сопроцессор 8087, что аналогично аппаратному
сбросу. Тем самым сопроцессор 8087 оказывается настроенным на
обработку особых ситуаций по умолчанию, что наилучшим образом
подходит для примеров этой книги. Команда FINIT также сбрасывает
регистровый стек сопроцессора 8087, освобождая все его восемь
позиций. Программа должна использовать команду FINIT только в
момент запуска. Команда FINIT никогда не должна быть использована
внутри подпрограммы для сопроцессора 8087.
Следующие команды загружают число 1000 в регистр ST1 и число 1
в регистр ST0. Все следующие команды сопроцессора 8087 используют
эти два регистра стека. В регистре ST0 находится текущая степень
десяти, а в регистре ST1 находится значение 103. Мы будем
использовать число в регистре ST1 для увеличения числа в регистре
ST0 после каждой итерации программы. Целая переменная POWER
содержит текущую степень 10, находящуюся в регистре ST0.
После метки POWER_LOOP элемент ST0 умножается на элемент ST1,
(в котором содержится число 1000), чтобы увеличить содержимое
регистра ST0 в 103 раз. Команда FST записывает результат в память.
Оставшаяся часть программы после метки POWER_LOOP печатает
результаты вычислений. В подпрограмме TRANSLATE шестнадцатеричный
байт преобразуется в двухбайтовую строку в коде ASCII так, что
программа может его распечатать. Текущее значение POWER (степень
десяти), а также шестнадцатеричная строка, записанная процессором
8087, преобразуются в код ASCII. Затем функция DOS печатает строку
на дисплее. Цикл POWER_LOOP продолжается до тех пор, пока
последнее напечатанное значение не станет больше 1038. Это
значение выбрано потому, что 1038 - это максимальное число, которое
может быть представлено в коротком действительном формате. Если бы
использовался длинный действительный формат чисел, это значение
было бы равно 10308. Заключительная часть Фиг. 7.23 показывает,
как выглядит результат работы этой программы на дисплее.
Microsoft (R) Macro Assembler Version 5.00 1/1/80 04:04:33
Фиг. 7.23 Степени 10 Page 1-1
PAGE ,132
TITLE Фиг. 7.23 Степени 10
0000 STACK SEGMENT STACK
0000 0040[ DW 64 DUP (?)
????
]
0080 STACK ENDS
0000 CODE SEGMENT
ASSUME CS:CODE
0000 ???????? POWER_OF_TEN DD ? ; Область данных для 10**x,
; короткое плавающее
0004 0002[ OUTPUT_POWER DB 2 DUP (' ') ; Текстовый буфер для значения
20
]
0006 48 20 20 20 20 DB 'H ' ; степени
000B 0008[ OUTPUT_STRING DB 8 DUP (' ') ; Текстовый буфер для результата -
20 20 20 20 20
20 20 20 20
]
0053 48 0D 0A 24 DB 'H',13,10,'$' ; Конец строки
0057 00 POWER DB 0 ; Текущая степень 10
0058 03E8 THOUSAND DW 1000 ; Константа
005A 03BF CONTROL_87 DW 03BFH
005C CALCULATE_POWER PROC FAR
005C 1E PUSH DS ; Занесение в стек адреса возврата
005D B8 0000 MOV AX, 0
0060 50 PUSH AX
0061 0E PUSH CS
0062 1F POP DS
ASSUME DS:CODE ; Адресация области данных
0063 9B DB E3 FINIT ; Инициализация сопроцессора 8087
Фиг. 7.23 (а) (начало)
0066 9B DF 06 0058 R FILD THOUSAND ; Загрузка 10**3 в стек сопроцессора 8087
006B 9B D9 E8 FLD1 ; Загрузка начального значения в стек 8087
006E POWER_LOOP:
006E 9B DC 8E 0000 FMUL ST(1) ; Умножение ST(0) на ST(1)
0073 9B D9 16 0000 R FST POWER_OF_TEN ; Сохранение в памяти результата
0078 80 06 0057 R 03 ADD POWER, 3 ; Увеличение показателя степени
007D A0 0057 R MOV AL, POWER ; Выборка показателя степени
0080 8D 1E 0004 R LEA BX, OUTPUT_POWER
0084 E8 00AC R CALL TRANSLATE
0087 B9 0004 MOV CX, 4
008A 8D 1E 000B R LEA BX, OUTPUT_STRING
008E 8D 36 0003 R LEA SI, POWER_OF_TEN+3
0092 FD STD ; Установка пересылки с уменьшением адреса
0093 VALUE_OUTPUT:
0093 AC LODSB ; Выборка байта результата
0094 E8 00AC R CALL TRANSLATE ; Занесение символа в выводимую строку
0097 E2 FA LOOP VALUE_OUTPUT ; Цикл по всем байтам результата
0099 8D 16 0004 R LEA DX, OUTPUT_POWER
009D B4 09 MOV AH, 9H
009F CD 21 INT 21H
00A1 80 3E 0057 R 26 CMP POWER, 38
00A6 72 C6 JB POWER_LOOP
00A8 9B DE D9 FCOMPP ; Удаление из стека двух чисел
00AB CB RET
00AC CALCULATE_POWER ENDP
00AC TRANSLATE PROC NEAR
00AC 50 PUSH AX ; Сохранение исходного значения
00AD 51 PUSH CX
00AE B1 04 MOV CL, 4 ; Сдвиг старшей цифры выводимого числа
00B0 D2 E8 SHR AL, CL ; на место младшей цифры
00B2 59 POP CX
00B3 E8 00CB R CALL XLAT_OUTPUT ; Вывод старшей цифры выводимого числа
00B6 58 POP AX ; Восстановление младшей цифры
00B7 E8 00CB R CALL XLAT_OUTPUT ; Вывод младшей цифры выводимого числа
00BA C3 RET
00BB TRANSLATE ENDP
00BB 30 31 32 33 34 35 36 ASCII_TABLE DB '0123456789ABCDEF'
37 38 39 41 42 43 44
45 46
00CB XLAT_OUTPUT PROC NEAR
00CB 24 0F AND AL, 0FH ; Выделение младшей цифры
00CD 53 PUSH BX
00CE 8D 1E 00BB R LEA BX, ASCII_TABLE ; Адрес таблицы трансляции
00D2 D7 XLAT ASCII_TABLE ; Преобразование в символьную форму
00D3 5B POP BX
00D4 88 07 MOV [BX], AL ; Сохранение очередного символа результата
00D6 43 INC BX ; Переключение на следующий символ
00D7 C3 RET
00D8 XLAT_OUTPUT ENDP
00D8 CODE ENDS
END CALCULATE_POWER
Фиг. 7.23 (а) (продолжение)
A>PRINT10
03H 447A0000H
06H 49742400H
09H 4E6E6B28H
0CH 5368D4A5H
0FH 58635FA9H
12H 5D5E0B6BH
15H 6258D727H
18H 6753C21CH
1BH 6C4ECB8FH
1EH 7149F2CAH
21H 76453719H
24H 7B4097CEH
27H 7F800000H
Фиг. 7.23 (b)
Фиг. 7.23 (a) Степени 10; (b) Вывод процедуры степеней 10
Вам нужно посмотреть часть программы TRANSLATE, несмотря на то,
что она не использует ни одной команды сопроцессора 8087. Эта
часть - пример подготовки чисел к печати. В частности, команда
XLAT преобразует для печати шестнадцатеричную тетраду (значение от
0 до 0FH) в правильный символ кода ASCII (от 0 до F). Просто
прибавлять значение тетрады к значению 0 нельзя, так как в коде
ASCII символы от A до F не следуют непосредственно за символами от
0 до 9; преобразование прекрасно выполняет команда перекодировки.
Мы используем аналогичный метод, когда преобразуем число с
плавающей точкой в пригодную для печати десятичную форму.