7.8. Примеры

Этот раздел главы с помощью примеров исполльзования наиболее распространенных операций научит вас применять сопроцессор 8087, предполагая, что вы этого совсем не умеете. Все примеры просты, т.е. в них не делается попыток обработки всех возможных ошибок или реакции на какие-либо специальные числа, с которыми может работать микросхема 8087. Сопроцессор 8087, конечно же, имеет такие возможности, но эти действия относятся к более сложным. Когда вы освоите основы техники, вы сможете добавить к этим примерам некоторые фрагменты, которые превратят их в настоящие подпрограммы общего назначения.

7.8.1. Степени десяти

Первый пример - исходный текст программы на Фиг. 7.23. Эта программа распечатывает короткое действительное представление степеней 10 от 10^3 до 10^39. Как мы уже видели в разделе, посвященном представлению данных, Макроассемблер фирмы 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 находится значение 10^3. Мы будем использовать число в егистре ST1 для увеличения числа в регистре ST0 после каждой итерации программы. Целая переменная POWER содержит текущую степень 10, находящуюся в регистре ST0.

После метки POWER_LOOP элемент ST0 умножается на элемент ST1, (в котором содержится число 1000), чтобы увеличить содержимое регистра ST0 в 10^3 раз. Команда FST записывает результат в память. Оставшаяся часть программы после метки POWER_LOOP печатает результаты вычислений. В подпрограмме TRANSLATE шестнадцатеричный байт преобразуется в двухбайтовую строку в коде ASCII так, что программа может его распечатать. Текущее значение POWER (степень десяти), а также шестнадцатеричная строка, записанная процессором 8087, преобразуются в код ASCII. Затем функция DOS печатает строку на дисплее. Цикл POWER_LOOP продолжается до тех пор, пока е напечатанное значение не станет больше 10^38. Это значение выбрано потому, что 10^38 - это максимальное число, которое может быть представлено в коротком действительном формате. Если бы использовался длинный действительный формат чисел, это значение было бы равно 10^308. Заключительная часть Фиг. 7.23 показывает, как выглядит результат работы этой программы на дисплее.


Microsoft (R) Macro Assembler Version 5.00                  1/1/80 04:03:56
Фиг. 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
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 (а) Степени 10


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) Вывод процедуры степеней 10

Вам нужно посмотреть часть программы TRANSLATE, несмотря на то, что она не использует ни одной команды сопроцессора 8087. Эта часть - пример подготовки чисел к печати. В частности, команда XLAT преобразует для печати шестнадцатеричную тетраду (значение от 0 до 0FH) в правильный символ кода ASCII (от 0 до F). Просто прибавлять значение тетрады к значению 0 нельзя, так как в коде ASCII символы от A до F не следуют непосредственно за символами от 0 до 9; преобразование прекрасно выполняет команда перекодировки. Мы используем аналогичный метод, когда преобразуем число с плавающей точкой в пригодную для печати десятичную форму.