Синус угла

      Последний пример использования сопроцессора 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

    иллюстрирует результат выполнения этой программы.

Hosted by uCoz