Последний пример использования сопроцессора 8087 - вычисление  синуса угла.  У сопроцессора 8087 нет команды вычисления функции  SIN; самое большее, что он может - это выполнить команду FPTAN,  нахождение частичного тангенса.  Чтобы выполнить операцию SIN,  воспользуемся этой командой, а также командой FPREM (частичный  остаток).

Программа, вычисляющая SIN, показана на Фиг. 7.27.  Эта  программа вычисляет и печатает синусы углов от 1/2 до 6 с шагом 1/2  радиана. Выдача программы аналогична выдаче следующей программы на  языке Бейсик:

  10 FOR X = .5 TO 6.0 STEP .5

  20 PRINT SIN(X)

  30 NEXT X

Для печати результатов используется подпрограмма на Фиг. 7.25.

            Microsoft (R) Macro Assembler Version 5.00              1/1/80 04:05:01

            Фиг. 7.27 Вычисление синуса угла                  Page    1-1

 

                                           PAGE   ,132

                                           TITLE  Фиг. 7.27 Вычисление синуса угла

 

            0000                     STACK  SEGMENT STACK

            0000  0040[                    DW     64 DUP (?)

                    ????

                              ]

 

            0080                     STACK  ENDS

            0000                     CODE   SEGMENT

                                           ASSUME CS:CODE,DS:CODE,ES:CODE

                                           EXTRN  FLOAT_ASCII:NEAR

            0000  0001               NUM_ANGLE       DW      1

            0002  0002               DEN_ANGLE       DW      2

            0004  ????               STATUS DW      ?

            0006  0004               FOUR   DW      4

            = 0040                        C3      EQU     40H

            = 0004                        C2      EQU     04H

            = 0002                        C1      EQU     02H

            = 0001                         C0     EQU     01H

            0008  93 A3 AE AB 20 E1 AB    ERROR_MSG       DB      'Угол слишком большой', 10, 13, '$'

                  A8 E8 AA AE AC 20 A1

                  AE AB EC E8 AE A9 0A

                  0D 24

            001F                     SIN    PROC    FAR

            001F  1E                       PUSH   DS

            0020  2B C0                    SUB    AX, AX

            0022  50                       PUSH   AX

            0023  8C C8                    MOV    AX, CS

            0025  8E D8                    MOV    DS, AX

            0027  8E C0                    MOV    ES, AX

            0029                     DO_AGAIN:

            0029  9B DB E3                       FINIT                   ;-----ST(0)-----;-----ST(1)------

            002C  9B DF 06 0000 R                FILD   NUM_ANGLE       ;          ;

            0031  9B DE 36 0002 R                FIDIV  DEN_ANGLE       ; X = Угол      ;

 

             Фиг. 7.27 (a) Процедура SIN (начало)

            0036  9B D9 EB                       FLDPI                   ; PI        ; X

            0039  9B DE 36 0006 R                FIDIV  FOUR            ; PI/4            ; X

            003E  9B D9 C9                       FXCH              ; X         ; PI/4

            0041  9B D9 F8                       FPREM                   ; R         ; PI/4

            0044  9B DD 3E 0004 R                FSTSW  STATUS

            0049  9B                       FWAIT

            004A  8A 26 0005 R                   MOV    AH, BYTE PTR STATUS+1

            004E  F6 C4 04                       TEST   AH, C2

            0051  75 55                    JNZ    BIG_ANGLE

            0053  F6 C4 02                       TEST   AH, C1          ; Определяется, необходимо ли вычитание PI/4

            0056  74 05                    JZ     DO_R            ; Если 0, то не необходимо вычитание PI/4

            0058  9B DE E1                       FSUBRP ST(1), ST(0)    ; A = PI/4-R    ; ?

            005B  EB 06                    JMP    SHORT DO_FPTAN

            005D                     DO_R:

            005D  9B D9 C9                       FXCH              ; PI/4            ; R

            0060  9B D8 D9                       FCOMP                   ; R         ; ?

            0063                     DO_FPTAN:

            0063  9B D9 F2                       FPTAN                   ; OPP             ; ADJ  Где OPP/ADJ=Tan(A)

 

                                     ;----- Опеределение того, что нужно - синус или косинус

 

            0066  F6 C4 42                       TEST   AH, C3 or C1

            0069  7A 03                    JPE    DO_SINE

            006B  9B D9 C9                       FXCH              ; ADJ             ; OPP

            006E                     DO_SINE:                ; D        ; N

 

                                     ;----- Вычисление N/SQR(N**2 + D**2)

            006E  9B D8 8E 0000 U                FMUL   ST(0)           ; D**2            ; N

            0073  9B D9 C9                       FXCH   ST(1)           ; N         ; D**2

            0076  9B D9 C0                       FLD    ST(0)           ; N         ; N         ; D**2

            0079  9B D8 8E 0000 U                FMUL   ST(0)           ; N**2            ; N             ; D**2

            007E  9B DC 06 0000 U                FADD   ST(2)           ; N**2 + D**2   ; N            ; D**2

            0083  9B D9 FA                       FSQRT                   ; SQR(N2 + D2)  ; N             ; D**2

            0086  9B DE F1                       FDIVRP ST(1)           ; SIN(X)        ; D**2

            0089  9B D9 C9                       FXCH   ST(1)           ; D**2            ; SIN(X)

            008C  9B D8 D9                       FCOMP                   ; SIN(X)        ; ?

            008F  F6 C4 01                       TEST   AH, C0

            0092  74 03                    JZ     SIGN_OK

            0094  9B D9 E0                       FCHS

            0097                     SIGN_OK:

            0097  E8 0000 E                CALL   FLOAT_ASCII

            009A  FF 06 0000 R                   INC    NUM_ANGLE

            009E  83 3E 0000 R 0D                CMP    NUM_ANGLE, 13

            00A3  77 02                    JA     RETURN_INST

            00A5  EB 82                    JMP    DO_AGAIN

            00A7                     RETURN_INST:

            00A7  CB                       RET

            00A8                     BIG_ANGLE:

            00A8  8D 16 0008 R                   LEA    DX, ERROR_MSG

            00AC  B4 09                    MOV    AH, 9H

            00AE  CD 21                    INT    21H

            00B0  CB                       RET

            00B1                     SIN    ENDP

            00B1                     CODE   ENDS

                                           END    SIN

             Фиг. 7.27 (a) Процедура SIN (продолжение)

            A>SIN

             4.79425539E-001

             8.41470985E-001

             9.97494987E-001

             5.98472144E-001

             1.41120008E-001

            -3.50783228E-001

            -7.56802495E-001

            -9.77530118E-001

            -9.58924275E-001

            -7.05540326E-001

            -2.79415498E-001

             2.15119988E-001

Фиг. 7.27 (b) Вывод процедуры SIN

Фиг. 7.27 Вычисление синуса угла

В первой части программы происходит ее инициализация для работы  в качестве файла типа .EXE.  Затем сопроцессор 8087 загружает два  целых числа и делит их, формируя исходный угол.  Это - пример  использования двух целых чисел для порождения числа с плавающей  точкой (в данном случае 1/2), что нельзя сделать непосредственно с  помощью ассемблера.

Как вы помните из тригонометрии, синус - периодическая функция.  То есть функция дает один и тот же результат в случае исходных  чисел, различающихся ровно на 2*PI.  Поэтому первой задачей  подпрограммы SIN является замена исходного угла соответствующим  значением, лежащим в диапазоне

  0 <= X < 2*PI

В команде FPTAN требуется, чтобы угол находился в диапазоне

  0 <= X < PI/4

Это означает, что даже если угол и меньше 2*PI, мы должны  уменьшить его еще, чтобы он удовлетворял ограничениям команды  FPTAN. К счастью, если исходный угол уменьшен до значения,  меньшего PI/4, все еще можно определить верное значение  тригонометрических функций.  Чтобы это сделать, надо знать, в каком  месте исходного диапазона от 0 до 2*PI находился исходный угол.

Нужное уменьшение угла выполняет команда FPREM.  Она не только  вычисляет остаток, но и три младших бита частного, определяемого в  течение процесса поиска остатка.  Эти три бита команда записывает в  слово состояния.  Следовательно, хотя мы и уменьшили угол до  значения одной восьмой исходного диапазона, все же можно определить  октант, в который попадет угол.  Зная его, можно найти формулу  вычисления синуса с помощью тригонометрических преобразований.  Таблица на Фиг. 7.28 показывает связь между исходным октантом и  методом вычисления синуса угла.  В таблице предполагается, что  число R - это остаток от уменьшения исходного угла до значения  меньше PI/4.  Номер октанта появляется в разрядах C3 = C1 = C0  после выполнения команды FPREM.

С помощью этой таблицы мы можем определить формулу вычислений,  применяемую в каждом случае выполнения программы.  После загрузки  значения угла в радианах программа загружает число и делит его на  4, чтобы использовать в команде FPREM.  В этот момент  "захватывается" слово состояния.  Если процесс поиска остатка не  завершился на этом единственном шаге, это означает, что исходный  угол был больше 2**64.  Следовательно, его значение настолько больше  максимально возможного при вычислениях тригонометрических функций,  что мы отбрасываем это число, как слишком большое.  Этого не  происходит со значениями, выбранными в примере, но здесь для  иллюстрации введена такая проверка.

            Октанты

       C0  C3    C1    Диапазон       SIN(X) = :

       -----------------------------------------------------

        0  0    0    0     PI/4  SIN(R)

        0  0    1    PI/4  PI/2  COS(PI/4-R)

        0  1    0    PI/2  3*PI/4      COS(R)

        0  1    1    3*PI/4      PI    SIN(PI/4-R)

        1  0    0    PI    5*PI/4      - SIN(R)

        1  0    1    5*PI/4      3*PI/2      - COS(PI4-R)

        1  1    0    3*PI/2      7*PI/4      - COS(R)

        1  1    1    7*PI/4      2*PI  - SIN(PI/4-R)

 

                  (R - остаток, 0<R<PI/4)

       -----------------------------------------------------

Фиг. 7.28 SIN(X) в восьми секторах

Программа проверяет разряд C1 в регистре состояния, чтобы  определить, должна ли она использовать остаток R, или его надо  вычесть из PI/4.  Так как PI/4 еще находится в одном из регистров,  это сделать просто.  Если вычитание не требуется, команда FCOMP  удаляет из стека ненужное значение PI/4.

Затем команда FPTAN вычисляет частичный тангенс.  Результат  работы команды показан, как OPP/ADJ (сокращения от английских слов  Opposite (противоположный) и Adjacent (соседний)), что равно  тангенсу угла R или PI/4-R, в зависимости от того, что было  выбрано. С помощью этих двух чисел теперь можно определить синус  или косинус угла.  Например, синус, заданный парой чисел OPP/ADJ,  можно вычислить по формуле

  SIN(X) = OPP/SQR(OPP**2+ADJ**2), где TAN(X) = OPP/ADJ

Чтобы вычислить косинус, нужно числитель заменить на ADJ.  Мы  решаем, нужен ли синус или косинус, анализируя запомненные  описатели октанта, т.е.  проверяя значения разрядов C3 и C1.  Команда TEST выделяет эти значения, а команда JPE делает переход,  если они оба нулевые или оба единичные.  В этом случае мы вычисляем  синус; если же они различны, мы вычисляем косинус, что достигается  заменой местами значений OPP и ADJ в стеке регистров.

Далее следующие команды сопроцессора 8087 вычисляют значение  синуса (или косинуса) по значению частичного тангенса.  Единственный шаг, который еще надо выполнить - это определение  окончательного знака результата.  В случае синуса результат  отрицателен, если угол находится в октантах от четвертого до  седьмого. Проверка разряда C0 определяет верный знак результата.  Затем программа FLOAT_ASCII, показанная на Фиг. 7.25, печатает число в плавающем формате.  Управление возвращается назад, к началу  цикла, если еще не пройдены все октанты.  Нижняя часть Фиг. 7.27  иллюстрирует результат выполнения этой программы.