Previous Table of Contents Next


LISTING 53.1 FIXED.ASM

; 386-specific fixed point routines.
; Tested with TASM
ROUNDING-ON     equ     1     ;1 for rounding, 0 for no rounding
                              ;no rounding is faster, rounding is
                              ; more accurate
ALIGNMENT        equ    2
      .model     small
      .386
      .code
;=====================================================================
; Multiplies two fixed-point values together.
; C near-callable as:
;     Fixedpoint FixedMul(Fixedpoint M1, Fixedpoint M2);
;     Fixedpoint FixedDiv(Fixedpoint Dividend, Fixedpoint Divisor);
FMparms struc
            dw   2 dup(?)      ;return address & pushed BP
M1          dd   ?
M2          dd   ?
FMparms     ends
            align   ALIGNMENT
            public  -FixedMul
-FixedMul       proc    near
        push    bp
        mov     bp,sp
        mov     eax,[bp+M1]
        imul    dword ptr [bp+M2] ;multiply
if ROUNDING-ON
        add     eax,8000h       ;round by adding 2^(-17)
        adc     edx,0           ;whole part of result is in DX
endif ;ROUNDING-ON
        shr     eax,16          ;put the fractional part in AX
        pop     bp
        ret
-FixedMul       endp
;=====================================================================
; Divides one fixed-point value by another.
; C near-callable as:
;     Fixedpoint FixedDiv(Fixedpoint Dividend, Fixedpoint Divisor);
FDparms struc
           dw    2 dup(?)       ;return address & pushed BP
Dividend   dd    ?
Divisor    dd    ?
FDparms    ends
      align ALIGNMENT
public        -FixedDiv
-FixedDiv       proc    near
        push    bp
        mov     bp,sp

if ROUNDING-ON
        sub     cx,cx           ;assume positive result
        mov     eax,[bp+Dividend]
        and     eax,eax         ;positive dividend?
        jns     FDP1            ;yes
        inc     cx              ;mark it’s a negative dividend
        neg     eax             ;make the dividend positive
FDP1:   sub     edx,edx         ;make it a 64-bit dividend, then shift
                                ; left 16 bits so that result will be
                                ; in EAX
        rol     eax,16          ;put fractional part of dividend in
                                ; high word of EAX
        mov     dx,ax           ;put whole part of dividend in DX
        sub     ax,ax           ;clear low word of EAX
        mov     ebx,dword ptr [bp+Divisor]
        and     ebx,ebx         ;positive divisor?
        jns     FDP2            ;yes
        dec     cx              ;mark it’s a negative divisor
        neg     ebx             ;make divisor positive
FDP2:   div     ebx             ;divide
        shr     ebx,1           ;divisor/2, minus 1 if the divisor is
        adc     ebx,0           ; even
        dec     ebx
        cmp     ebx,edx         ;set Carry if remainder is at least
        adc     eax,0           ; half as large as the divisor, then
                                ; use that to round up if necessary
        and     cx,cx           ;should the result be made negative?
        jz      FDP3            ;no
        neg     eax             ;yes, negate it
FDP3:
else ;  !ROUNDING-ON
        mov     edx,[bp+Dividend]
        sub     eax,eax
        shrd    eax,edx,16       ;position so that result ends up
        sar     edx,16           ; in EAX
        idivdword ptr [bp+Divisor]
endif ;ROUNDING-ON
        shld    edx,eax,16       ;whole part of result in DX;
                                 ; fractional part is already in AX
        pop      bp
        ret
-FixedDiv       endp
;=====================================================================
; Returns the sine and cosine of an angle.
; C near-callable as:
;void CosSin(TAngle Angle, Fixedpoint *Cos, Fixedpoint *);

align ALIGNMENT
CosTable label dword
include costable.inc

SCparms struc
            dw    2 dup(?)    ;return address & pushed BP
Angle       dw    ?           ;angle to calculate sine & cosine for
Cos         dw    ?           ;pointer to cos destination
Sin         dw    ?           ;pointer to sin destination
SCparms ends

   align ALIGNMENT
   public -CosSin
-CosSinprocnear
     push  bp                  ;preserve stack frame
     mov   bp,sp               ;set up local stack frame

     mov   bx,[bp].Angle
     and   bx,bx               ;make sure angle’s between 0 and 2*pi
jns  CheckInRange
MakePos:;less than 0, so make it positive
     add    bx,360*10
     js     MakePos
     jmp    short CheckInRange

align ALIGNMENT
MakeInRange:                   ;make sure angle is no more than 2*pi
     sub   bx,360*10
CheckInRange:
cmp   bx,360*10
jg    MakeInRange

      cmp  bx,180*10        ;figure out which quadrant
      ja   BottomHalf       ;quadrant 2 or 3
      cmp  bx,90*10         ;quadrant 0 or 1
      ja   Quadrant1
                            ;quadrant 0
      shl   bx,2
      move  ax,CosTable[bx] ;look up sine
      neg   bx              ;sin(Angle) = cos(90-Angle)
      move  dx,CosTable[bx+90*10*4] ;look up cosine
      jmp   short CSDone

      align  ALIGNMENT
Quadrant1:
      neg    bx
      add    bx,180*10       ;convert to angle between 0 and 90
      shl    bx,2
      mov    eax,CosTable[bx] ;look up cosine
      neg    eax             ;negative in this quadrant
      neg    bx              ;sin(Angle) = cos(90-Angle)
      move   dx,CosTable[bx+90*10*4] ;look up cosine
      jmp    short CSDone

      align  ALIGNMENT
BottomHalf:                ;quadrant 2 or 3
      neg     bx
      add     bx,360*10    ;convert to angle between 0 and 180
      cmp     bx,90*10     ;quadrant 2 or 3
      ja      Quadrant2
                           ;quadrant 3
      shl      bx, 2
      mov   eax,CosTable[bx]     ;look up cosine
      neg   bx              ;sin(Angle) = cos(90-Angle)
      movedx,CosTable[90*10*4+bx] ;look up sine
      nege     dx          ;negative in this quadrant
      jmp     short CSDone

      align  ALIGNMENT
Quadrant2:
     neg     bx
     add     bx,180*10      ;convert to angle between 0 and 90
     shl     bx,2
     mov     eax,CosTable[bx] ;look up cosine
     neg     eax          ;negative in this quadrant
     neg     bx           ;sin(Angle) = cos(90-Angle)
     move    dx,CosTable[90*10*4+bx] ;look up sine
     nege    dx             ;negative in this quadrant
CSDone:
     mov      bx,[bp].Cos
     mov      [bx],eax
     mov      bx,[bp].Sin
     mov      [bx],edx

     pop      bp;restore stack frame
ret
-CosSinendp
;=====================================================================
; Matrix multiplies Xform by SourceVec, and stores the result in
; DestVec. Multiplies a 4x4 matrix times a 4x1 matrix; the result
; is a 4x1 matrix. Cheats by assuming the W coord is 1 and the
; bottom row of the matrix is 0 0 0 1, and doesn’t bother to set
; the W coordinate of the destination.
; C near-callable as:
;     void XformVec(Xform WorkingXform, Fixedpoint *SourceVec,
;          Fixedpoint *DestVec);
;
; This assembly code is equivalent to this C code:
;   int i;
;
;   for (i=0; i<3; i++)
;      DestVec[i] = FixedMul(WorkingXform[i][0], SourceVec[0]) +
;     FixedMul(WorkingXform[i][1], SourceVec[1]) +
;     FixedMul(WorkingXform[i][2], SourceVec[2]) +
;     WorkingXform[i][3];   /* no need to multiply by W = 1 */

XVparms struc
               dw    2 dup(?)   ;return address & pushed BP
WorkingXform   dw    ?          ;pointer to transform matrix
SourceVec      dw    ?          ;pointer to source vector
DestVec        dw    ?          ;pointer to destination vector
XVparms       ends

    align  ALIGNMENT
    public -XformVec
-XformVec   proc   near
    push    bp                  ;preserve stack frame
    mov     bp,sp               ;set up local stack frame
    push    si                  ;preserve register variables
    push    di

    mov     si,[bp].WorkingXform  ;SI points to xform matrix
    mov     bx,[bp].SourceVec    ;BX points to source vector
    mov     di,[bp].DestVec    ;DI points to dest vector

soff=0
doff=0
    REPT 3                     ;do once each for dest X, Y, and Z
    mov    eax,[si+soff]       ;column 0 entry on this row
    imul   dword ptr [bx]      ;xform entry times source X entry
if ROUNDING-ON
    add    eax,8000h           ;round by adding 2^(-17)
    adc    edx,0               ;whole part of result is in DX
endif ;ROUNDING-ON
    shrd   eax,edx,16;shift the result back to 16.16 form
    mov    ecx,eax             ;set running total

    mov    eax,[si+soff+4]     ;column 1 entry on this row
    imul   dword ptr [bx+4]    ;xform entry times source Y entry
if ROUNDING-ON
    add    eax,8000h           ;round by adding 2^(-17)
    adc    edx,0               ;whole part of result is in DX
endif ;ROUNDING-ON
    shrd   eax,edx,16          ;shift the result back to 16.16 form
    add    ecx,eax             ;running total for this row

    mov    eax,[si+soff+8]     ;column 2 entry on this row
    imul   dword ptr [bx+8]    ;xform entry times source Z entry
if ROUNDING-ON
    add    eax,8000h           ;round by adding 2^(-17)
    adc    edx,0               ;whole part of result is in DX
endif ;ROUNDING-ON
    shrd   eax,edx,16          ;shift the result back to 16.16 form
    add    ecx,eax             ;running total for this row

    add    ecx,[si+soff+12]    ;add in translation
    mov[di+doff],ecx          ;save the result in the dest vector
soff=soff+16
doff=doff+4
ENDM

popdi;restore register variables
popsi
popbp;restore stack frame
ret
-XformVecendp
;=====================================================================
; Matrix multiplies SourceXform1 by SourceXform2 and stores the
; result in DestXform. Multiplies a 4x4 matrix times a 4x4 matrix;
; the result is a 4x4 matrix. Cheats by assuming the bottom row of
; each matrix is 0 0 0 1, and doesn’t bother to set the bottom row
; of the destination.
; C near-callable as:
;       void ConcatXforms(Xform SourceXform1, Xform SourceXform2,
;               Xform DestXform)
;
; This assembly code is equivalent to this C code:
;   int i, j;
;
;   for (i=0; i<3; i++) {
;      for (j=0; j<3; j++)
;         DestXform[i][j] =
;               FixedMul(SourceXform1[i][0], SourceXform2[0][j]) +
;               FixedMul(SourceXform1[i][1], SourceXform2[1][j]) +
;               FixedMul(SourceXform1[i][2], SourceXform2[2][j]);
;      DestXform[i][3] =
;            FixedMul(SourceXform1[i][0], SourceXform2[0][3]) +
;            FixedMul(SourceXform1[i][1], SourceXform2[1][3]) +
;            FixedMul(SourceXform1[i][2], SourceXform2[2][3]) +
;            SourceXform1[i][3];
;   }

CXparms struc
             dw     2 dup(?)         ;return address & pushed BP
SourceXform1 dw     ?                ;pointer to first source xform matrix
SourceXform2  dw    ?                ;pointer to second source xform matrix
DestXform     dw    ?                ;pointer to destination xform matrix
CXparms       ends

       align  ALIGNMENT
       public -ConcatXforms
-ConcatXforms    proc  near
     push   bp                     ;preserve stack frame
     mov    bp,sp                  ;set up local stack frame
     push   si                     ;preserve register variables
pushdi

     mov    bx,[bp].SourceXform2    ;BX points to xform2 matrix
     mov    si,[bp].SourceXform1    ;SI points to xform1 matrix
     mov    di,[bp].DestXform       ;DI points to dest xform matrix

roff=0                          ;row offset
      REPT 3                    ;once for each row
coff=0                          ;column offset
      REPT 3                    ;once for each of the first 3 columns,
                                ; assuming 0 as the bottom entry (no
; translation)
      mov     eax,[si+roff]     ;column 0 entry on this row
      imul    dword ptr [bx+coff];times row 0 entry in column
if ROUNDING-ON
      add     eax,8000h        ;round by adding 2^(-17)
      adcedx,0;whole part of result is in DX
endif ;ROUNDING-ON
       shrd    eax,edx,16       ;shift the result back to 16.16 form
       mov     ecx,eax          ;set running total

       mov     eax,[si+roff+4]  ;column 1 entry on this row
       imul    dword ptr [bx+coff+16];times row 1 entry in col
if ROUNDING-ON
       add    eax,8000h         ;round by adding 2^(-17)
       adc    edx,0             ;whole part of result is in DX
endif ;ROUNDING-ON
       shrd   eax,edx,16        ;shift the result back to 16.16 form
       add    ecx,eax           ;running total

       mov    eax,[si+roff+8]   ;column 2 entry on this row
       imul   dword ptr [bx+coff+32];times row 2 entry in col
if ROUNDING-ON
       add    eax,8000h         ;round by adding 2^(-17)
       adc    edx,0             ;whole part of result is in DX
endif ;ROUNDING-ON
       shrd   eax,edx,16        ;shift the result back to 16.16 form
       add    ecx,eax           ;running total

       mov[di+coff+roff],ecx    ;save the result in dest matrix
coff=coff+4                     ;point to next col in xform2 & dest
ENDM
                               ;now do the fourth column, assuming
                               ; 1 as the bottom entry, causing
                               ; translation to be performed
       mov     eax,[si+roff]   ;column 0 entry on this row
       imul    dword ptr [bx+coff]   ;times row 0 entry in column
if ROUNDING-ON
       add     eax,8000h       ;round by adding 2^(-17)
       adc     edx,0           ;whole part of result is in DX
endif ;ROUNDING-ON
       shrd    eax,edx,16      ;shift the result back to 16.16 form
       mov     ecx,eax         ;set running total

       mov     eax,[si+roff+4] ;column 1 entry on this row
       imul    dword ptr [bx+coff+16];times row 1 entry in col
if ROUNDING-ON
        add    eax,8000h       ;round by adding 2^(-17)
        adc    edx,0           ;whole part of result is in DX
endif ;ROUNDING-ON
        shrd   eax,edx,16      ;shift the result back to 16.16 form
       add     ecx,eax         ;running total

       mov     eax,[si+roff+8]  ;column 2 entry on this row
       imul    dword ptr [bx+coff+32];times row 2 entry in col
if ROUNDING-ON
       add    eax,8000h         ;round by adding 2^(-17)
       adc    edx,0             ;whole part of result is in DX
endif ;ROUNDING-ON
       shrd   eax,edx,16        ;shift the result back to 16.16 form
addecx,eax;running total

addecx,[si+roff+12];add in translation

mov[di+coff+roff],ecx;save the result in dest matrix
coff=coff+4                ;point to next col in xform2 & dest

roff=roff+16                ;point to next col in xform2 & dest
ENDM

popdi;restore register variables
popsi
popbp;restore stack frame
ret
-ConcatXformsendp
end


Previous Table of Contents Next

Graphics Programming Black Book © 2001 Michael Abrash