ITEEDU

11.4 协处理器的编程举例

本节提供几个利用协处理器进行编程的例子,从这些例子可看出使用协处理器指令编程的方法和技巧。

例11.8 已知L=4.0,F按每次递增10.0的幅度从10.0增加到1000.0,试按公式Y=2πFL,计算出100个Y值,并把它们存入数组RES中。

解:

.MODEL        SMALL
.386
.387
.DATA
     L DD 4.0
     F DD 1000.0 ;为了便于循环控制,F“从大到小”来计算
     TEN DD 10.0
     Y DD 100 DUP(?)
.CODE
 .STARTUP 
              FLDPI                 ;装入π
              FADD      ST, ST(0)   ;自身相加,得到2π
              FMUL      L           ;计算出不变的结果2πL
              MOV       ECX, 100
              MOV       EDI, OFFSET Y-4
      again:
              FLD       F
              FMUL      ST, ST(1)   ;得到一个计算结果2πLF
              FSTP      dword ptr [EDI+4*ECX]   ;保存当前得到结果(从后向前)
              FLD       F
              FSUB      TEN
              FSTP      F           ;F的值变小10.0
              LOOP      again
              FCOMP                 ;先前计算出结果2πL出栈,使之为空
.EXIT         0
              END
例11.9 把内存单元DATA中存放的单精度浮点数以小数的形式显示在屏幕上。

解:

 .MODEL SMALL 
.386
.387
.DATA
     DATA DD -1234.75
     STATUS DW ?
     WHOLE DD ?
     FRACT DD ?
.CODE
DISPS PROC NEAR ;子程序:显示AL中的字符 
              MOV       AH, 6
              MOV       DL, AL
              INT       21H
              RET
       DISPS  ENDP
 .STARTUP 
              FSTCW     STATUS      ;以小数形式显示浮点数DATA
              OR        STATUS,0C00H            ;设定截取舍入方式
              FLDCW     STATUS
              FLD       DATA
              FTST
              FSTSW     AX
              AND       AX, 4500H
              CMP       AX, 0100H
              JNZ       Positive
              MOV       AL, ‘-‘
              CALL      DISPS
              FABS
   Positive:
              FLD       ST
              FRNDINT
              FIST      WHOLE       ;保存整数部分
              FSUBR
              FABS
              FSTP      FRACT       ;保存小数部分
              MOV       EAX, WHOLE
              MOV       EBX, 10
              MOV       CX, 0
              PUSH      BX
     Again1:
              MOV       EDX, 0
              DIV       EBX
              ADD       DL, 30H
              PUSH      DX
              INC       CX
              CMP       EAX, 0
              JNZ       Again1
      Disp1:
              POP       AX
              CALL      DISPS       ;显示整数部分
              LOOP      Disp1
              MOV       AL, ‘.’
              CALL      DISPS       ;显示小数点
              MOV       EAX, FRACT
              FSTCW     STATUS
              XOR       STATUS, 0C00H
              FLDCW     STATUS
              FLD       FRACT
              FXTRACT
              FSTP      FRACT
              FABS
              FISTP     WHOLE
              MOV       ECX, WHOLE
              MOV       EAX, FRACT
              SHL       EAX, 9
              RCR       EAX, CL
     Again2:
              MUL       EBX
              PUSH      EAX
              XCHG      EAX, EDX
              ADD       AL, 30H
              CALL      DISPS
              POP       EAX
              CMP       EAX, 0
              JNZ       Again2
.EXIT 0 
              END
例11.10 从键盘上读入一个带小数的数字字符串,然后把它转换成单精度浮点数,并存入内存单元DATA中。

解:

.MODEL SMALL 
.386
.387
.DATA
DATA DD 0
TEN DD 10.0
TEMP DW ?
SIGN DB ?
.CODE
       GETCH  MACRO                 ;宏GETCH的功能是从键盘读入一个字符
              MOV       AH, 1
              INT       21H
              ENDM
.STARTUP 
              FLDZ                  ;ST=0
              GETCH
.IF           AL == ‘+’                   ;判定是否输入正号‘+’
              MOV       SIGN, 0
              GETCH
 
.ENDIF
.IF           AL == ‘-‘
              MOV       SIGN, 1     ;判定是否输入负号‘-’
              GETCH
 
.ENDIF
.REPEAT
              FMUL      TEN
              MOV       AH,0
              SUB       AL, 30H
              MOV       TEMP, AX
              FIADD     TEMP        ;部分数×10.0+(字符-‘0’)
              GETCH
 
.UNTIL AL<’0’ || AL>’9’ ;完成整数部分的处理 
              CMP       AL, ‘.’
              JNE       exit
              FLD1                  ;开始处理小数部分
.WHILE1 
              FDIV      TEN
              GETCH
 
.BREAK        .IF AL<’0’ || AL>’9’
 
              MOV       AH, 0
              SUB       AL, 30H
              MOV       TEMP, AX
              FILD      TEMP
              FMUL      ST, ST(1)
              FADD      ST(2), ST
              FCOMP
 
.ENDW
              FCOMP
       exit:
.IF           SIGN == 1                   ;如果是负数,那么需要改变符号
              FCHS
 
.ENDIF
              FSTP      DATA        ;保存最终转换的结果
.EXIT 0 
              END
例11.11 编写一个子程序,求解一元二次方程的根ax2 + bx + c = 0的根。

解:

;调用参数:方程的系数a、b和c,R1和R2是指向存放根的指针; 
 
;返回参数:返回值存于AX中,其含义:0—有二个根,1—有一个根R1,2—无实根。
 
   Quadratic  PROC      USES DS DI SI, AA:DWORD, BB:DWORD, CC:DWORD, R1:PDWORD, R2:PDWORD
 
              LES       DI, R1      ;(ES:DI) → 1st Root-R1
              LDS       SI, R2      ;(DS:SI) → 2nd Root-R2
              SUB       BX, BX      ;用BX来标识方程有根的情况
              FLD1                  ;栈顶寄存器置1
              FADD      ST, ST      ;栈顶寄存器自加,变成2
              FLD       ST          ;把数值2复制一份到新的栈顶
              FMUL      AA          ;ST = 2A
              FTST                  ;测试ST是否为0,即,考虑A=0的情况
              FSTSW     AX          ;把当前状态寄存器存入AX中
              FWAIT                 ;等待协处理器完成上面操作
              SAHF                  ;AH装入到标志寄存器中
              JNZ       Notzero     ;考虑A = 0的情况
              FLD       CC          ;常数项C进栈
              FCHS                  ;改变栈顶的符号,得到-C
              FLD       BB          ;参数B进栈
              FTST                  ;测试当前栈顶是否为0
              FSTSW     AX
              SAHF
              JZ        EXIT2       ;考虑B = 0的情况
              FDIV                  ;计算出-C/B
              FSTP      dword ptr ES:[DI]       ;得到一个根,并弹出栈顶
              FSTP      ST          ;弹出多余的堆栈数据
              JMP       EXIT1       ;返回,并标识有1个根
    Notzero:
              FMUL      ST(1), ST   ;ST(1) = 4A
              FXCH                  ;ST和ST(1)交换
              FMUL      CC          ;ST = 4AC
              FTST
              FSTSW     AX
              SAHF
              JP        EXIT2       ;如果状态位C2=1,则4AC是无穷大
              FLD       BB          ;装入参数B
              FMUL      ST, ST      ;ST = B2
              FSUBR                 ;ST = B2 - 4AC
              FTST
              FSTSW     AX
              SAHF
              JC        EXIT2       ;如果C0=1,则B2 < 4AC
              JNZ       Tworoot     ;如果C3=1,则B2 = 4AC
              INC       BX          ;标志有1个根
    Tworoot:
              FSQRT                 ;求出B2 - 4AC的平方根
              FLD       BB
              FCHS                  ;得到-B
              FXCH                  ;ST 和ST(1)交换
              FLD       ST          ;栈顶再复制一份
              FADD      ST, ST(2)   ;ST = -B + SQRT(B2 - 4AC)
              FXCH                  ;ST 和ST(1)交换
              FSUBP     ST(2), ST   ;ST = -B - SQRT(B2 - 4AC)
              FDIV      ST, ST(2)   ;得到一个根ST = ST/(2A)
              FSTP      dword ptr ES:[DI]       ;存储第一个根
              FDIVR                 ;得到另一个根ST = ST/(2A)
              FSTP      dword ptr DS:[SI]       ;存储第二个根
              JMP       EXIT
      EXIT2:
              INC       BX          ;无根时的返回出口
              FSTP      ST          ;清除多余的堆栈数据
      EXIT1:
              INC       BX          ;有一个根时的返回出口
              FSTP      ST          ;清除多余的堆栈数据
       EXIT:
              MOV       AX, BX
              RET
   Quadratic  ENDP
              END