Previous Table of Contents Next


LISTING 54.1 FIXED.ASM

; Fixed point routines.
; Tested with TASM

USE386             equ       1  ;1 for 386-specific opcodes, 0 for
                                ; 8088 opcodes
MUL-ROUNDING-ON    equ       1  ;1 for rounding on multiplies,
                                ; 0 for no rounding. Not rounding is faster,
                                ; rounding is more accurate and generally a
                                ; good idea
DIV-ROUNDING-ON    equ       0  ;1 for rounding on divides,
                                ; 0 for no rounding. Not rounding is faster,
                                ; rounding is more accurate, but because
                                ; division is only performed to project to
                                ; the screen, rounding quotients generally
                                ; isn't necessary
ALIGNMENT          equ       2

     .model small
     .386
     .code

;=====================================================================
; Multiplies two fixed-point values together.
; C near-callable as:
;     Fixedpoint FixedMul(Fixedpoint M1, Fixedpoint M2);
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

if USE386

     mov     eax,[bp+M1]
     imul    dword ptr [bp+M2]       ;multiply
if MUL-ROUNDING-ON
     add   eax,8000h                 ;round by adding 2^(-17)
adcedx,0                             ;whole part of result is in DX
endif ;MUL-ROUNDING-ON
shreax,16                            ;put the fractional part in AX

else;!USE386

                                     ;do four partial products and
                                     ; add them together, accumulating
                                     ; the result in CX:BX
    push     si                      ;preserve C register variables
    push     di
                                     ;figure out signs, so we can use
                                    ; unsigned multiplies
   sub      cx,cx                   ;assume both operands positive
   mov      ax,word ptr [bp+M1+2]
   mov      si,word ptr [bp+M1]
   and      ax,ax                    ;first operand negative?
   jns      CheckSecondOperand       ;no
   neg      ax                       ;yes, so negate first operand
   neg      si
   sbb      ax,0
   inc      cx                       ;mark that first operand is negative
CheckSecondOperand:
    mov     bx,word ptr [bp+M2+2]
    mov     di,word ptr [bp+M2]
    and     bx,bx                    ;second operand negative?
    jns     SaveSignStatus           ;no
    neg     bx                       ;yes, so negate second operand
    neg     di
    sbb     bx,0
    xor     cx,1                     ;mark that second operand is negative
SaveSignStatus:
     push   cx                       ;remember sign of result; 1 if result
                                     ; negative, 0 if result nonnegative
    push    ax                       ;remember high word of M1
    mul     bx                       ;high word M1 times high word M2
    mov     cx,ax                    ;accumulate result in CX:BX (BX not used
                                     ; until next operation, however)
                                     ;assume no overflow into DX
    mov     ax,si                    ;low word M1 times high word M2
    mul     bx
    mov     bx,ax
    add     cx,dx                    ;accumulate result in CX:BX
    pop     ax                       ;retrieve high word of M1
    mul     di                       ;high word M1 times low word M2
   add      bx,ax
   adc      cx,dx                    ;accumulate result in CX:BX
   mov      ax,si                    ;low word M1 times low word M2
muldi
if MUL-ROUNDING-ON
   add     ax,8000h                  ;round by adding 2^(-17)
adcbx,dx
else ;!MUL-ROUNDING-ON
    add     bx,dx,                   ;don't round
endif ;MUL-ROUNDING-ON
    adc    cx,0                      ;accumulate result in CX:BX
    mov    dx,cx
    mov    ax,bx
    pop    cx
    and    cx,cx                     ;is the result negative?
    jz     FixedMulDone              ;no, we're all set
    neg    dx                        ;yes, so negate DX:AX
    neg    ax
    sbb    dx,0
FixedMulDone:

    pop    di                        ;restore C register variables
    pop    si

endif;USE386

    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
        alignALIGNMENT
        public_FixedDiv
_FixedDivproc    near
        pushbp
        movbp,sp

if USE386

if DIV-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 the 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 ;!DIV-ROUNDING-ON
        mov     edx,[bp+Dividend]
        sub     eax,eax
        shrd    eax,edx,16      ;position so that result ends up
        sar     edx,16          ; in EAX
        idiv    dword ptr [bp+Divisor]
endif ;DIV-ROUNDING-ON
        shld    edx,eax,16      ;whole part of result in DX;
                                ; fractional part is already in AX

else                            ;!USE386

 ;NOTE!!! Non-386 division uses a 32-bit dividend but only the upper 16 bits
 ; of the divisor; in other words, only the integer part of the divisor is
 ; used. This is done so that the division can be accomplished with two fast
 ; hardware divides instead of a slow software implementation, and is (in my
 ; opinion) acceptable because division is only used to project points to the
 ; screen (normally, the divisor is a Z coordinate), so there's no cumulative
 ; error, although there will be some error in pixel placement (the magnitude
 ; of the error is less the farther away from the Z=0 plane objects are). This
 ; is *not* a general-purpose divide, though; if the divisor is less than 1,
 ; for instance, a divide-by-zero error will result! For this reason, non-386
 ; projection can't be performed for points closer to the viewpoint than Z=1.
                                      ;figure out signs, so we can use
                                      ; unsigned divisions
      subcx,    cx                    ;assume both operands positive
      mov   ax,word ptr [bp+Dividend+2]
      and   ax,ax;first operand negative?
      jns   CheckSecondOperandD ;no
      neg   ax                         ;yes, so negate first operand
      neg   word ptr [bp+Dividend]
      sbbax,0
      inc   cx                         ;mark that first operand is negative
      CheckSecondOperandD:
      mov    bx,word ptr [bp+Divisor+2]
      and    bx,bx                      ;second operand negative?
jnsSaveSignStatusD;no
      neg    bx                         ;yes, so negate second operand
      neg    word ptr [bp+Divisor]
      sbb    bx,0
      xor    cx,1                       ;mark that second operand is negative
SaveSignStatusD:
      push   cx                         ;remember sign of result; 1 if result
                                        ; negative, 0 if result nonnegative
      sub    dx,dx                      ;put Dividend+2 (integer part) in DX:AX
      div    bx                         ;first half of 32/16 division, integer part
                                        ; divided by integer part
      mov    cx,ax                      ;set aside integer part of result
      mov    ax,word ptr [bp+Dividend]  ;concatenate the fractional part of
                                        ; the dividend to the remainder (fractional
                                        ; part) of the result from dividing the
                                        ; integer part of the dividend
     div     bx                         ;second half of 32/16 division

if DIV-ROUNDING-ON EQ 0
     shr     bx,1                       ;divisor/2, minus 1 if the divisor is
     adc     bx,0                       ; even
     dec     bx
     cmp     bx,dx                      ;set Carry if the remainder is at least
     adc     ax,0                       ; half as large as the divisor, then
     adc     cx,0                       ; use that to round up if necessary
endif ;DIV-ROUNDING-ON

     mov     dx,cx                      ;absolute value of result in DX:AX
     pop     cx
     and     cx,cx                      ;is the result negative?
     jz       FixedDivDone              ;no, we're all set
     neg      dx                        ;yes, so negate DX:AX
     neg      ax
     sbb      dx,0
FixedDivDone:

endif ;USE386

     pop     bp
     ret
_FixedDiv       endp

;=====================================================================
; Returns the sine and cosine of an angle.
; C near-callable as:
;     void CosSin(TAngle Angle, Fixedpoint *Cos, Fixedpoint *);

      alignALIGNMENT
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

    alignALIGNMENT
    public _CosSin
_CosSin    procnear
     push  bp                          ;preserve stack frame
     mov   bp,sp                       ;set up local stack frame

if USE386

     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
     move  ax,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
     move  ax,CosTable[bx]              ;look up cosine
     neg   bx;sin(Angle) = cos(90-Angle)
     move  dx,CosTable[90*10*4+bx]      ;look up sine
     neg   edx                          ;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
     move  ax,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
     neg   edx                          ;negative in this quadrant
CSDone:
     mov   bx,[bp].Cos
     mov   [bx],eax
     mov   bx,[bp].Sin
     mov   [bx],edx

else ;!USE386

     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
jaQuadrant1
                                         ;quadrant 0
     shl   bx,2
     mov   ax,word ptr CosTable[bx]      ;look up sine
     mov   dx,word ptr CosTable[bx+2]
     neg   bx                            ;sin(Angle) = cos(90-Angle)
     mov   cx,word ptr CosTable[bx+90*10*4+2] ;look up cosine
     mov   bx,word ptr CosTable[bx+90*10*4]
     jmp   CSDone

     align ALIGNMENT
Quadrant1:
     neg   bx
     add   bx,180*10                     ;convert to angle between 0 and 90
     shl   bx,2
     mov   ax,word ptr CosTable[bx]      ;look up cosine
     mov   dx,word ptr CosTable[bx+2]
     neg   dx                            ;negative in this quadrant
     neg   ax
     sbb   dx,0
     neg   bx                            ;sin(Angle) = cos(90-Angle)
     mov   cx,word ptr CosTable[bx+90*10*4+2] ;look up cosine
     mov   bx,word ptr CosTable[bx+90*10*4]
     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
jaQuadrant2
                                         ;quadrant 3
     shl   bx,2
     mov   ax,word ptr CosTable[bx]      ;look up cosine
     mov   dx,word ptr CosTable[bx+2]
     neg   bx                            ;sin(Angle) = cos(90-Angle)
     mov   cx,word ptr CosTable[90*10*4+bx+2] ;look up sine
     mov   bx,word ptr CosTable[90*10*4+bx]
     neg   cx                            ;negative in this quadrant
     neg   bx
     sbb   cx,0
     jmp   short CSDone

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

endif ;USE386

     pop   bp                            ;restore stack frame
     ret
_CosSin    endp

;=====================================================================
; 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

; Macro for non-386 multiply. AX, BX, CX, DX destroyed.
FIXED-MUL MACRO M1,M2
      local CheckSecondOperand,SaveSignStatus,FixedMulDone

                                         ;do four partial products and 
                                         ; add them together, accumulating
                                         ; the result in CX:BX
                                         ;figure out signs, so we can use
                                         ; unsigned multiplies
     sub   cx,cx                         ;assume both operands positive
     mov   bx,word ptr [&M1&+2]
     and   bx,bx                         ;first operand negative?
     jns   CheckSecondOperand            ;no
     neg   bx                            ;yes, so negate first operand
     neg   word ptr [&M1&]
     sbb   bx,0
     mov   word ptr [&M1&+2],bx
     inc   cx                            ;mark that first operand is negative
CheckSecondOperand:
     mov   bx,word ptr [&M2&+2]
     and   bx,bx                         ;second operand negative?
     jns   SaveSignStatus                ;no
     neg   bx                            ;yes, so negate second operand
     neg   word ptr [&M2&]
     sbb   bx,0
     mov   word ptr [&M2&+2],bx
     xor   cx,1                          ;mark that second operand is negative
SaveSignStatus:
     push   cx                           ;remember sign of result; 1 if result
                                         ; negative, 0 if result nonnegative
     mov   ax,word ptr [&M1&+2]    ;high word times high word
     mul   word ptr [&M2&+2]
     mov   cx,ax           ;
                                         ;assume no overflow into DX
     mov   ax,word ptr [&M1&+2]    ;high word times low word
     mul   word ptr [&M2&]
     mov   bx,ax
     add   cx,dx
     mov   ax,word ptr [&M1&]      ;low word times high word
     mul   word ptr [&M2&+2]
     add   bx,ax
     adc   cx,dx
     mov   ax,word ptr [&M1&]      ;low word times low word
     mul   word ptr [&M2&]
if MUL-ROUNDING-ON
     add   ax,8000h                      ;round by adding 2^(-17)
  adc   bx,dx
else ;!MUL-ROUNDING-ON
     add   bx,dx                         ;don't round
endif ;MUL-ROUNDING-ON
     adc   cx,0
     mov   dx,cx
     mov   ax,bx
     pop   cx
     and   cx,cx                         ;is the result negative?
     jz    FixedMulDone                  ;no, we're all set
     neg   dx                            ;yes, so negate DX:AX
     neg   ax
     sbb   dx,0
FixedMulDone:
     ENDM

     align ALIGNMENT
     public _XformVec
_XformVecprocnear
     push   bp                           ;preserve stack frame
     mov    bp,sp                        ;set up local stack frame
     push   si                           ;preserve register variables
     push   di

if USE386

     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 MUL-ROUNDING-ON
     add   eax,8000h                      ;round by adding 2^(-17)
     adc   edx,0                          ;whole part of result is in DX
endif ;MUL-ROUNDING-ON
     shrd  eax,edx,16                     ;shift the result back to 16.16 form
     move  cx,eax                         ;set running total

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

     move  ax,[si+soff+8]                ;column 2 entry on this row
     imul  dword ptr [bx+8]              ;xform entry times source Z entry
if MUL-ROUNDING-ON
     add   eax,8000h                     ;round by adding 2^(-17)
     adc   edx,0                         ;whole part of result is in DX
endif ;MUL-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

else ;!USE386

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

soff=0
doff=0
     REPT 3                              ;do once each for dest X, Y, and Z
     push  bx                           ;remember dest vector pointer
     push  word ptr [si+soff+2]
     push  word ptr [si+soff]
     push  word ptr [di+2]
     push  word ptr [di]
     call  _FixedMul                     ;xform entry times source X entry
     add   sp,8;clear parameters from stack
     mov   cx,ax                         ;set running total
     mov   bp,dx

     push  cx                            ;preserve low word of running total
     push  word ptr [si+soff+4+2]
     push  word ptr [si+soff+4]
     push  word ptr [di+4+2]
     push  word ptr [di+4]
     call  _FixedMul                     ;xform entry times source Y entry
     add   sp,8                          ;clear parameters from stack
     pop   cx                            ;restore low word of running total
     add   cx,ax                         ;running total for this row
     adc   bp,dx

     push  cx                            ;preserve low word of running total
     push  word ptr [si+soff+8+2]
     push  word ptr [si+soff+8]
     push  word ptr [di+8+2]
     push  word ptr [di+8]
     call  _FixedMul                     ;xform entry times source Z entry
     add   sp,8                          ;clear parameters from stack
     pop   cx                            ;restore low word of running total
     add   cx,ax                         ;running total for this row
     adc   bp,dx

     add   cx,[si+soff+12]               ;add in translation
     adc   bp,[si+soff+12+2]
     pop   bx                            ;restore dest vector pointer
     mov   [bx+doff],cx                  ;save the result in the       dest  vector
     mov   [bx+doff+2],bp
soff=soff+16
doff=doff+4
     ENDM

     pop   bp                            ;restore stack frame pointer

endif ;USE386

     pop   di                             ;restore register variables
     pop   si
     pop   bp                             ;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
_ConcatXformsprocnear
          push    bp                   ;preserve stack frame
          mov     bp,sp                ;set up local stack frame
          push    si                   ;preserve register variables
          push    di

if USE386

          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)
          move    ax,[si+roff]         ;column 0 entry on this row
          imul    dword ptr [bx+coff]  ;times row 0 entry in column
if MUL-ROUNDING-ON
          add     eax,8000h            ;round by adding 2^(-17)
          adc     edx,0                ;whole part of result is in DX
endif ;MUL-ROUNDING-ON
          shrd    eax,edx,16           ;shift the result back to 16.16 form
          move    cx,eax               ;set running total

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

          move    ax,[si+roff+8]       ;column 2 entry on this row
          imuld   word ptr [bx+coff+32] ;times row 2 entry in col
if MUL-ROUNDING-ON
          add     eax,8000h             ;round by adding 2^(-17)
          adc     edx,0                 ;whole part of result is in DX
endif ;MUL-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
          move    ax,[si+roff]          ;column 0 entry on this row
          imuld   word ptr [bx+coff]    ;times row 0 entry in column
if MUL-ROUNDING-ON
          add     eax,8000h              ;round by adding 2^(-17)
          adc     edx,0                  ;whole part of result is in DX
endif ;MUL-ROUNDING-ON
          shrd    eax,edx,16             ;shift the result back to 16.16 form
          move    cx,eax                 ;set running total

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

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

          add     ecx,[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

else ;!USE386

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

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)
          push    bx                     ;remember dest vector pointer
          push    word ptr [si+roff+2]
          push    word ptr [si+roff]
          push    word ptr [di+coff+2]
          push    word ptr [di+coff]
          call    _FixedMul               ;column 0 entry on this row times row 0
; entry in column
addsp,8;clear parameters from stack
          mov     cx,ax                   ;set running total
          mov     bp,dx

          push    cx                      ;preserve low word of running total
          push    word ptr [si+roff+4+2]
          push    word ptr [si+roff+4]
          push    word ptr [di+coff+16+2]
          push    word ptr [di+coff+16]
          call    _FixedMul               ;column 1 entry on this row times row 1
; entry in column
          add     sp,8                    ;clear parameters from stack
          pop     cx                      ;restore low word of running total
          add     cx,ax                   ;running total for this row
          adc     bp,dx

          push    cx                      ;preserve low word of running total
          push    word ptr [si+roff+8+2]
          push    word ptr [si+roff+8]
          push    word ptr [di+coff+32+2]
          push    word ptr [di+coff+32]
          call    _FixedMul               ;column 1 entry on this row times row 1
; entry in column
          add     sp,8                    ;clear parameters from stack
          pop     cx                      ;restore low word of running total
          add     cx,ax                   ;running total for this row
          adc     bp,dx

          pop     bx                      ;restore DestXForm pointer
          mov     [bx+coff+roff],cx       ;save the result in dest matrix
          mov     [bx+coff+roff+2],bp
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
          push    bx                      ;remember dest vector pointer
          push    word ptr [si+roff+2]
          push    word ptr [si+roff]
          push    word ptr [di+coff+2]
          push    word ptr [di+coff]
          call    _FixedMul               ;column 0 entry on this row times row 0
; entry in column
          add     sp,8                    ;clear parameters from stack
          mov     cx,ax                   ;set running total
          mov     bp,dx

          push    cx                      ;preserve low word of running total
          push    word ptr [si+roff+4+2]
          push    word ptr [si+roff+4]
          push    word ptr [di+coff+16+2]
          push    word ptr [di+coff+16]
          call    _FixedMul               ;column 1 entry on this row times row 1
; entry in column
          add     sp,8                    ;clear parameters from stack
          pop     cx                      ;restore low word of running total
          add     cx,ax                   ;running total for this row
          adc     bp,dx

          push    cx                      ;preserve low word of running total
          push    word ptr [si+roff+8+2]
          push    word ptr [si+roff+8]
          push    word ptr [di+coff+32+2]
          push    word ptr [di+coff+32]
          call    _FixedMul               ;column 1 entry on this row times row 1
; entry in column
          add     sp,8                    ;clear parameters from stack
          pop     cx                      ;restore low word of running total
          add     cx,ax                   ;running total for this row
          adc     bp,dx

          add     cx,[si+roff+12]         ;add in translation
          add     bp,[si+roff+12+2]

          pop     bx                      ;restore DestXForm pointer
          mov     [bx+coff+roff],cx       ;save the result in dest matrix
          mov     [bx+coff+roff+2],bp
coff=coff+4                               ;point to next col in xform2 & dest

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

          pop    bp                       ;restore stack frame pointer

endif ;USE386

          pop    di                       ;restore register variables
          pop    si
          pop    bp                       ;restore stack frame
ret
_ConcatXforms    endp
end


Previous Table of Contents Next

Graphics Programming Black Book © 2001 Michael Abrash