home *** CD-ROM | disk | FTP | other *** search
/ Graphics Programming Black Book (Special Edition) / BlackBook.bin / disk1 / xsharp22 / fixed.asm < prev    next >
Assembly Source File  |  1997-06-18  |  35KB  |  862 lines

  1. ; Fixed point routines.
  2. ; Tested with TASM 4.0.
  3.  
  4. USE386          equ     1       ;1 for 386-specific opcodes, 0 for
  5.                                 ; 8088 opcodes
  6. MUL_ROUNDING_ON equ     1       ;1 for rounding on multiplies,
  7.                                 ; 0 for no rounding. Not rounding is faster,
  8.                                 ; rounding is more accurate and generally a
  9.                                 ; good idea
  10. DIV_ROUNDING_ON equ     0       ;1 for rounding on divides,
  11.                                 ; 0 for no rounding. Not rounding is faster,
  12.                                 ; rounding is more accurate, but because
  13.                                 ; division is only performed to project to
  14.                                 ; the screen, rounding quotients generally
  15.                                 ; isn't necessary
  16. ALIGNMENT       equ     2
  17.  
  18.         .model small
  19.         .386
  20.         .code
  21.  
  22. ;=====================================================================
  23. ; Multiplies two fixed-point values together.
  24. ; C near-callable as:
  25. ;       Fixedpoint FixedMul(Fixedpoint M1, Fixedpoint M2);
  26. FMparms struc
  27.         dw      2 dup(?)        ;return address & pushed BP
  28. M1      dd      ?
  29. M2      dd      ?
  30. FMparms ends
  31.         align   ALIGNMENT
  32.         public  _FixedMul
  33. _FixedMul       proc    near
  34.         push    bp
  35.         mov     bp,sp
  36.  
  37. if USE386
  38.  
  39.         mov     eax,[bp+M1]
  40.         imul    dword ptr [bp+M2] ;multiply
  41. if MUL_ROUNDING_ON
  42.         add     eax,8000h       ;round by adding 2^(-17)
  43.         adc     edx,0           ;whole part of result is in DX
  44. endif ;MUL_ROUNDING_ON
  45.         shr     eax,16          ;put the fractional part in AX
  46.  
  47. else    ;!USE386
  48.  
  49.                                 ;do four partial products and
  50.                                 ; add them together, accumulating
  51.                                 ; the result in CX:BX
  52.         push    si              ;preserve C register variables
  53.         push    di
  54.                                 ;figure out signs, so we can use
  55.                                 ; unsigned multiplies
  56.         sub     cx,cx           ;assume both operands positive
  57.         mov     ax,word ptr [bp+M1+2]
  58.         mov     si,word ptr [bp+M1]
  59.         and     ax,ax           ;first operand negative?
  60.         jns     short CheckSecondOperand ;no
  61.         neg     ax              ;yes, so negate first operand
  62.         neg     si
  63.         sbb     ax,0
  64.         inc     cx              ;mark that first operand is negative
  65. CheckSecondOperand:
  66.         mov     bx,word ptr [bp+M2+2]
  67.         mov     di,word ptr [bp+M2]
  68.         and     bx,bx           ;second operand negative?
  69.         jns     short SaveSignStatus ;no
  70.         neg     bx              ;yes, so negate second operand
  71.         neg     di
  72.         sbb     bx,0
  73.         xor     cx,1            ;mark that second operand is negative
  74. SaveSignStatus:
  75.         push    cx              ;remember sign of result; 1 if result
  76.                                 ; negative, 0 if result nonnegative
  77.         push    ax              ;remember high word of M1
  78.         mul     bx              ;high word M1 times high word M2
  79.         mov     cx,ax           ;accumulate result in CX:BX (BX not used
  80.                                 ; until next operation, however)
  81.                                 ;assume no overflow into DX
  82.         mov     ax,si           ;low word M1 times high word M2
  83.         mul     bx
  84.         mov     bx,ax
  85.         add     cx,dx           ;accumulate result in CX:BX
  86.         pop     ax              ;retrieve high word of M1
  87.         mul     di              ;high word M1 times low word M2
  88.         add     bx,ax
  89.         adc     cx,dx           ;accumulate result in CX:BX
  90.         mov     ax,si           ;low word M1 times low word M2
  91.         mul     di
  92. if MUL_ROUNDING_ON
  93.         add     ax,8000h        ;round by adding 2^(-17)
  94.         adc     bx,dx
  95. else ;!MUL_ROUNDING_ON
  96.         add     bx,dx           ;don't round
  97. endif ;MUL_ROUNDING_ON
  98.         adc     cx,0            ;accumulate result in CX:BX
  99.         mov     dx,cx
  100.         mov     ax,bx
  101.         pop     cx
  102.         and     cx,cx           ;is the result negative?
  103.         jz      short FixedMulDone ;no, we're all set
  104.         neg     dx              ;yes, so negate DX:AX
  105.         neg     ax
  106.         sbb     dx,0
  107. FixedMulDone:
  108.  
  109.         pop     di              ;restore C register variables
  110.         pop     si
  111.  
  112. endif   ;USE386
  113.  
  114.         pop     bp
  115.         ret
  116. _FixedMul       endp
  117.  
  118. ;=====================================================================
  119. ; Divides one fixed-point value by another.
  120. ; C near-callable as:
  121. ;       Fixedpoint FixedDiv(Fixedpoint Dividend, Fixedpoint Divisor);
  122. FDparms struc
  123.         dw      2 dup(?)        ;return address & pushed BP
  124. Dividend dd     ?
  125. Divisor  dd     ?
  126. FDparms ends
  127.         align   ALIGNMENT
  128.         public  _FixedDiv
  129. _FixedDiv       proc    near
  130.         push    bp
  131.         mov     bp,sp
  132.  
  133. if USE386
  134.  
  135. if DIV_ROUNDING_ON
  136.         sub     cx,cx           ;assume positive result
  137.         mov     eax,[bp+Dividend]
  138.         and     eax,eax         ;positive dividend?
  139.         jns     short FDP1      ;yes
  140.         inc     cx              ;mark it's a negative dividend
  141.         neg     eax             ;make the dividend positive
  142. FDP1:   sub     edx,edx         ;make it a 64-bit dividend, then shift
  143.                                 ; left 16 bits so that result will be
  144.                                 ; in EAX
  145.         rol     eax,16          ;put fractional part of dividend in
  146.                                 ; high word of EAX
  147.         mov     dx,ax           ;put whole part of dividend in DX
  148.         sub     ax,ax           ;clear low word of EAX
  149.         mov     ebx,dword ptr [bp+Divisor]
  150.         and     ebx,ebx         ;positive divisor?
  151.         jns     short FDP2      ;yes
  152.         dec     cx              ;mark it's a negative divisor
  153.         neg     ebx             ;make divisor positive
  154. FDP2:   div     ebx             ;divide
  155.         shr     ebx,1           ;divisor/2, minus 1 if the divisor is
  156.         adc     ebx,0           ; even
  157.         dec     ebx
  158.         cmp     ebx,edx         ;set Carry if the remainder is at least
  159.         adc     eax,0           ; half as large as the divisor, then
  160.                                 ; use that to round up if necessary
  161.         and     cx,cx           ;should the result be made negative?
  162.         jz      short FDP3      ;no
  163.         neg     eax             ;yes, negate it
  164. FDP3:
  165. else ;!DIV_ROUNDING_ON
  166.         mov     edx,[bp+Dividend]
  167.         sub     eax,eax
  168.         shrd    eax,edx,16      ;position so that result ends up
  169.         sar     edx,16          ; in EAX
  170.         idiv    dword ptr [bp+Divisor]
  171. endif ;DIV_ROUNDING_ON
  172.         shld    edx,eax,16      ;whole part of result in DX;
  173.                                 ; fractional part is already in AX
  174.  
  175. else ;!USE386
  176.  
  177. ;NOTE!!! Non-386 division uses a 32-bit dividend but only the upper 16 bits
  178. ; of the divisor; in other words, only the integer part of the divisor is
  179. ; used. This is done so that the division can be accomplished with two fast
  180. ; hardware divides instead of a slow software implementation, and is (in my
  181. ; opinion) acceptable because division is only used to project points to the
  182. ; screen (normally, the divisor is a Z coordinate), so there's no cumulative
  183. ; error, although there will be some error in pixel placement (the magnitude
  184. ; of the error is less the farther away from the Z=0 plane objects are). This
  185. ; is *not* a general-purpose divide, though; if the divisor is less than 1,
  186. ; for instance, a divide-by-zero error will result! For this reason, non-386
  187. ; projection can't be performed for points closer to the viewpoint than Z=1.
  188.  
  189.                                 ;figure out signs, so we can use
  190.                                 ; unsigned divisions
  191.         sub     cx,cx           ;assume both operands positive
  192.         mov     ax,word ptr [bp+Dividend+2]
  193.         and     ax,ax           ;first operand negative?
  194.         jns     short CheckSecondOperandD ;no
  195.         neg     ax              ;yes, so negate first operand
  196.         neg     word ptr [bp+Dividend]
  197.         sbb     ax,0
  198.         inc     cx              ;mark that first operand is negative
  199. CheckSecondOperandD:
  200.         mov     bx,word ptr [bp+Divisor+2]
  201.         and     bx,bx           ;second operand negative?
  202.         jns     short SaveSignStatusD ;no
  203.         neg     bx              ;yes, so negate second operand
  204.         neg     word ptr [bp+Divisor]
  205.         sbb     bx,0
  206.         xor     cx,1            ;mark that second operand is negative
  207. SaveSignStatusD:
  208.         push    cx              ;remember sign of result; 1 if result
  209.                                 ; negative, 0 if result nonnegative
  210.         sub     dx,dx           ;put Dividend+2 (integer part) in DX:AX
  211.         div     bx              ;first half of 32/16 division, integer part
  212.                                 ; divided by integer part
  213.         mov     cx,ax           ;set aside integer part of result
  214.         mov     ax,word ptr [bp+Dividend] ;concatenate the fractional part of
  215.                                 ; the dividend to the remainder (fractional
  216.                                 ; part) of the result from dividing the
  217.                                 ; integer part of the dividend
  218.         div     bx              ;second half of 32/16 division
  219.         
  220. if DIV_ROUNDING_ON EQ 0
  221.         shr     bx,1            ;divisor/2, minus 1 if the divisor is
  222.         adc     bx,0            ; even
  223.         dec     bx
  224.         cmp     bx,dx           ;set Carry if the remainder is at least
  225.         adc     ax,0            ; half as large as the divisor, then
  226.         adc     cx,0            ; use that to round up if necessary
  227. endif ;DIV_ROUNDING_ON
  228.  
  229.         mov     dx,cx           ;absolute value of result in DX:AX
  230.         pop     cx
  231.         and     cx,cx           ;is the result negative?
  232.         jz      short FixedDivDone ;no, we're all set
  233.         neg     dx              ;yes, so negate DX:AX
  234.         neg     ax
  235.         sbb     dx,0
  236. FixedDivDone:
  237.  
  238. endif ;USE386
  239.  
  240.         pop     bp
  241.         ret
  242. _FixedDiv       endp
  243.  
  244. ;=====================================================================
  245. ; Returns the sine and cosine of an angle.
  246. ; C near-callable as:
  247. ;       void CosSin(TAngle Angle, Fixedpoint *Cos, Fixedpoint *);
  248.  
  249.         align   ALIGNMENT
  250. CosTable label dword
  251.         include costable.inc
  252.  
  253. SCparms struc
  254.         dw      2 dup(?)        ;return address & pushed BP
  255. Angle   dw      ?               ;angle to calculate sine & cosine for
  256. Cos     dw      ?               ;pointer to cos destination
  257. Sin     dw      ?               ;pointer to sin destination
  258. SCparms ends
  259.  
  260.         align   ALIGNMENT
  261.         public _CosSin
  262. _CosSin proc    near
  263.         push    bp              ;preserve stack frame
  264.         mov     bp,sp           ;set up local stack frame
  265.  
  266. if USE386
  267.  
  268.         mov     bx,[bp].Angle
  269.         and     bx,bx           ;make sure angle's between 0 and 2*pi
  270.         jns     short CheckInRange
  271. MakePos:                        ;less than 0, so make it positive
  272.         add     bx,360*10
  273.         js      short MakePos
  274.         jmp     short CheckInRange
  275.  
  276.         align   ALIGNMENT
  277. MakeInRange:                    ;make sure angle is no more than 2*pi
  278.         sub     bx,360*10
  279. CheckInRange:
  280.         cmp     bx,360*10
  281.         jg      short MakeInRange
  282.  
  283.         cmp     bx,180*10       ;figure out which quadrant
  284.         ja      short BottomHalf ;quadrant 2 or 3
  285.         cmp     bx,90*10        ;quadrant 0 or 1
  286.         ja      short Quadrant1
  287.                                 ;quadrant 0
  288.         shl     bx,2
  289.         mov     eax,CosTable[bx] ;look up sine
  290.         neg     bx              ;sin(Angle) = cos(90-Angle)
  291.         mov     edx,CosTable[bx+90*10*4] ;look up cosine
  292.         jmp     short CSDone
  293.  
  294.         align   ALIGNMENT
  295. Quadrant1:
  296.         neg     bx
  297.         add     bx,180*10       ;convert to angle between 0 and 90
  298.         shl     bx,2
  299.         mov     eax,CosTable[bx] ;look up cosine
  300.         neg     eax             ;negative in this quadrant
  301.         neg     bx              ;sin(Angle) = cos(90-Angle)
  302.         mov     edx,CosTable[bx+90*10*4] ;look up cosine
  303.         jmp     short CSDone
  304.  
  305.         align   ALIGNMENT
  306. BottomHalf:                     ;quadrant 2 or 3
  307.         neg     bx
  308.         add     bx,360*10       ;convert to angle between 0 and 180
  309.         cmp     bx,90*10        ;quadrant 2 or 3
  310.         ja      short Quadrant2
  311.                                 ;quadrant 3
  312.         shl     bx,2
  313.         mov     eax,CosTable[bx] ;look up cosine
  314.         neg     bx              ;sin(Angle) = cos(90-Angle)
  315.         mov     edx,CosTable[90*10*4+bx] ;look up sine
  316.         neg     edx             ;negative in this quadrant
  317.         jmp     short CSDone
  318.  
  319.         align   ALIGNMENT
  320. Quadrant2:
  321.         neg     bx
  322.         add     bx,180*10       ;convert to angle between 0 and 90
  323.         shl     bx,2
  324.         mov     eax,CosTable[bx] ;look up cosine
  325.         neg     eax             ;negative in this quadrant
  326.         neg     bx              ;sin(Angle) = cos(90-Angle)
  327.         mov     edx,CosTable[90*10*4+bx] ;look up sine
  328.         neg     edx             ;negative in this quadrant
  329. CSDone:
  330.         mov     bx,[bp].Cos
  331.         mov     [bx],eax
  332.         mov     bx,[bp].Sin
  333.         mov     [bx],edx
  334.  
  335. else ;!USE386
  336.  
  337.         mov     bx,[bp].Angle
  338.         and     bx,bx           ;make sure angle's between 0 and 2*pi
  339.         jns     short CheckInRange
  340. MakePos:                        ;less than 0, so make it positive
  341.         add     bx,360*10
  342.         js      short MakePos
  343.         jmp     short CheckInRange
  344.  
  345.         align   ALIGNMENT
  346. MakeInRange:                    ;make sure angle is no more than 2*pi
  347.         sub     bx,360*10
  348. CheckInRange:
  349.         cmp     bx,360*10
  350.         jg      short MakeInRange
  351.  
  352.         cmp     bx,180*10       ;figure out which quadrant
  353.         ja      short BottomHalf ;quadrant 2 or 3
  354.         cmp     bx,90*10        ;quadrant 0 or 1
  355.         ja      short Quadrant1
  356.                                 ;quadrant 0
  357.         shl     bx,2
  358.         mov     ax,word ptr CosTable[bx] ;look up sine
  359.         mov     dx,word ptr CosTable[bx+2]
  360.         neg     bx              ;sin(Angle) = cos(90-Angle)
  361.         mov     cx,word ptr CosTable[bx+90*10*4+2] ;look up cosine
  362.         mov     bx,word ptr CosTable[bx+90*10*4]
  363.         jmp     CSDone
  364.  
  365.         align   ALIGNMENT
  366. Quadrant1:
  367.         neg     bx
  368.         add     bx,180*10       ;convert to angle between 0 and 90
  369.         shl     bx,2
  370.         mov     ax,word ptr CosTable[bx] ;look up cosine
  371.         mov     dx,word ptr CosTable[bx+2]
  372.         neg     dx              ;negative in this quadrant
  373.         neg     ax
  374.         sbb     dx,0
  375.         neg     bx              ;sin(Angle) = cos(90-Angle)
  376.         mov     cx,word ptr CosTable[bx+90*10*4+2] ;look up cosine
  377.         mov     bx,word ptr CosTable[bx+90*10*4]
  378.         jmp     short CSDone
  379.  
  380.         align   ALIGNMENT
  381. BottomHalf:                     ;quadrant 2 or 3
  382.         neg     bx
  383.         add     bx,360*10       ;convert to angle between 0 and 180
  384.         cmp     bx,90*10        ;quadrant 2 or 3
  385.         ja      short Quadrant2
  386.                                 ;quadrant 3
  387.         shl     bx,2
  388.         mov     ax,word ptr CosTable[bx] ;look up cosine
  389.         mov     dx,word ptr CosTable[bx+2]
  390.         neg     bx              ;sin(Angle) = cos(90-Angle)
  391.         mov     cx,word ptr CosTable[90*10*4+bx+2] ;look up sine
  392.         mov     bx,word ptr CosTable[90*10*4+bx]
  393.         neg     cx              ;negative in this quadrant
  394.         neg     bx
  395.         sbb     cx,0
  396.         jmp     short CSDone
  397.  
  398.         align   ALIGNMENT
  399. Quadrant2:
  400.         neg     bx
  401.         add     bx,180*10       ;convert to angle between 0 and 90
  402.         shl     bx,2
  403.         mov     ax,word ptr CosTable[bx] ;look up cosine
  404.         mov     dx,word ptr CosTable[bx+2]
  405.         neg     dx              ;negative in this quadrant
  406.         neg     ax
  407.         sbb     dx,0
  408.         neg     bx              ;sin(Angle) = cos(90-Angle)
  409.         mov     cx,word ptr CosTable[90*10*4+bx+2] ;look up sine
  410.         mov     bx,word ptr CosTable[90*10*4+bx]
  411.         neg     cx              ;negative in this quadrant
  412.         neg     bx
  413.         sbb     cx,0
  414. CSDone:
  415.         push    bx
  416.         mov     bx,[bp].Cos
  417.         mov     [bx],ax
  418.         mov     [bx+2],dx
  419.         mov     bx,[bp].Sin
  420.         pop     ax
  421.         mov     [bx],ax
  422.         mov     [bx+2],cx
  423.  
  424. endif ;USE386
  425.  
  426.         pop     bp              ;restore stack frame
  427.         ret
  428. _CosSin endp
  429.  
  430. ;=====================================================================
  431. ; Matrix multiplies Xform by SourceVec, and stores the result in
  432. ; DestVec. Multiplies a 4x4 matrix times a 4x1 matrix; the result
  433. ; is a 4x1 matrix. Cheats by assuming the W coord is 1 and the
  434. ; bottom row of the matrix is 0 0 0 1, and doesn't bother to set
  435. ; the W coordinate of the destination.
  436. ; C near-callable as:
  437. ;       void XformVec(Xform WorkingXform, Fixedpoint *SourceVec,
  438. ;               Fixedpoint *DestVec);
  439. ;
  440. ; This assembly code is equivalent to this C code:
  441. ;   int i;
  442. ;
  443. ;   for (i=0; i<3; i++)
  444. ;      DestVec[i] = FixedMul(WorkingXform[i][0], SourceVec[0]) +
  445. ;            FixedMul(WorkingXform[i][1], SourceVec[1]) +
  446. ;            FixedMul(WorkingXform[i][2], SourceVec[2]) +
  447. ;            WorkingXform[i][3];   /* no need to multiply by W = 1 */
  448.  
  449. XVparms struc
  450.         dw      2 dup(?)        ;return address & pushed BP
  451. WorkingXform dw ?               ;pointer to transform matrix
  452. SourceVec dw    ?               ;pointer to source vector
  453. DestVec dw      ?               ;pointer to destination vector
  454. XVparms ends
  455.  
  456. ; Macro for non-386 multiply. AX, BX, CX, DX destroyed.
  457. FIXED_MUL       MACRO   M1,M2
  458.         local   CheckSecondOperand,SaveSignStatus,FixedMulDone
  459.  
  460.                                 ;do four partial products and
  461.                                 ; add them together, accumulating
  462.                                 ; the result in CX:BX
  463.                                 ;figure out signs, so we can use
  464.                                 ; unsigned multiplies
  465.         sub     cx,cx           ;assume both operands positive
  466.         mov     bx,word ptr [&M1&+2]
  467.         and     bx,bx           ;first operand negative?
  468.         jns     short CheckSecondOperand ;no
  469.         neg     bx              ;yes, so negate first operand
  470.         neg     word ptr [&M1&]
  471.         sbb     bx,0
  472.         mov     word ptr [&M1&+2],bx
  473.         inc     cx              ;mark that first operand is negative
  474. CheckSecondOperand:
  475.         mov     bx,word ptr [&M2&+2]
  476.         and     bx,bx           ;second operand negative?
  477.         jns     short SaveSignStatus  ;no
  478.         neg     bx              ;yes, so negate second operand
  479.         neg     word ptr [&M2&]
  480.         sbb     bx,0
  481.         mov     word ptr [&M2&+2],bx
  482.         xor     cx,1            ;mark that second operand is negative
  483. SaveSignStatus:
  484.         push    cx              ;remember sign of result; 1 if result
  485.                                 ; negative, 0 if result nonnegative
  486.         mov     ax,word ptr [&M1&+2] ;high word times high word
  487.         mul     word ptr [&M2&+2]
  488.         mov     cx,ax           ;
  489.                                 ;assume no overflow into DX
  490.         mov     ax,word ptr [&M1&+2] ;high word times low word
  491.         mul     word ptr [&M2&]
  492.         mov     bx,ax
  493.         add     cx,dx
  494.         mov     ax,word ptr [&M1&] ;low word times high word
  495.         mul     word ptr [&M2&+2]
  496.         add     bx,ax
  497.         adc     cx,dx
  498.         mov     ax,word ptr [&M1&] ;low word times low word
  499.         mul     word ptr [&M2&]
  500. if MUL_ROUNDING_ON
  501.         add     ax,8000h        ;round by adding 2^(-17)
  502.         adc     bx,dx
  503. else ;!MUL_ROUNDING_ON
  504.         add     bx,dx           ;don't round
  505. endif ;MUL_ROUNDING_ON
  506.         adc     cx,0
  507.         mov     dx,cx
  508.         mov     ax,bx
  509.         pop     cx
  510.         and     cx,cx           ;is the result negative?
  511.         jz      short FixedMulDone ;no, we're all set
  512.         neg     dx              ;yes, so negate DX:AX
  513.         neg     ax
  514.         sbb     dx,0
  515. FixedMulDone:
  516.         ENDM
  517.  
  518.         align   ALIGNMENT
  519.         public _XformVec
  520. _XformVec       proc    near
  521.         push    bp              ;preserve stack frame
  522.         mov     bp,sp           ;set up local stack frame
  523.         push    si              ;preserve register variables
  524.         push    di
  525.  
  526. if USE386
  527.  
  528.         mov     si,[bp].WorkingXform ;SI points to xform matrix
  529.         mov     bx,[bp].SourceVec    ;BX points to source vector
  530.         mov     di,[bp].DestVec      ;DI points to dest vector
  531.  
  532. soff=0
  533. doff=0
  534.         REPT 3                  ;do once each for dest X, Y, and Z
  535.         mov     eax,[si+soff]   ;column 0 entry on this row
  536.         imul    dword ptr [bx]  ;xform entry times source X entry
  537. if MUL_ROUNDING_ON
  538.         add     eax,8000h       ;round by adding 2^(-17)
  539.         adc     edx,0           ;whole part of result is in DX
  540. endif ;MUL_ROUNDING_ON
  541.         shrd    eax,edx,16      ;shift the result back to 16.16 form
  542.         mov     ecx,eax         ;set running total
  543.  
  544.         mov     eax,[si+soff+4] ;column 1 entry on this row
  545.         imul    dword ptr [bx+4] ;xform entry times source Y entry
  546. if MUL_ROUNDING_ON
  547.         add     eax,8000h       ;round by adding 2^(-17)
  548.         adc     edx,0           ;whole part of result is in DX
  549. endif ;MUL_ROUNDING_ON
  550.         shrd    eax,edx,16      ;shift the result back to 16.16 form
  551.         add     ecx,eax         ;running total for this row
  552.  
  553.         mov     eax,[si+soff+8] ;column 2 entry on this row
  554.         imul    dword ptr [bx+8] ;xform entry times source Z entry
  555. if MUL_ROUNDING_ON
  556.         add     eax,8000h       ;round by adding 2^(-17)
  557.         adc     edx,0           ;whole part of result is in DX
  558. endif ;MUL_ROUNDING_ON
  559.         shrd    eax,edx,16      ;shift the result back to 16.16 form
  560.         add     ecx,eax         ;running total for this row
  561.  
  562.         add     ecx,[si+soff+12] ;add in translation
  563.         mov     [di+doff],ecx   ;save the result in the dest vector
  564. soff=soff+16
  565. doff=doff+4
  566.         ENDM
  567.  
  568. else ;!USE386
  569.  
  570.         mov     si,[bp].WorkingXform ;SI points to xform matrix
  571.         mov     di,[bp].SourceVec    ;DI points to source vector
  572.         mov     bx,[bp].DestVec      ;BX points to dest vector
  573.         push    bp                   ;preserve stack frame pointer
  574.  
  575. soff=0
  576. doff=0
  577.         REPT 3                  ;do once each for dest X, Y, and Z
  578.         push    bx              ;remember dest vector pointer
  579.         push    word ptr [si+soff+2] 
  580.         push    word ptr [si+soff]
  581.         push    word ptr [di+2]
  582.         push    word ptr [di]
  583.         call    _FixedMul       ;xform entry times source X entry
  584.         add     sp,8            ;clear parameters from stack
  585.         mov     cx,ax           ;set running total
  586.         mov     bp,dx
  587.  
  588.         push    cx              ;preserve low word of running total
  589.         push    word ptr [si+soff+4+2] 
  590.         push    word ptr [si+soff+4]
  591.         push    word ptr [di+4+2]
  592.         push    word ptr [di+4]
  593.         call    _FixedMul       ;xform entry times source Y entry
  594.         add     sp,8            ;clear parameters from stack
  595.         pop     cx              ;restore low word of running total
  596.         add     cx,ax           ;running total for this row
  597.         adc     bp,dx
  598.  
  599.         push    cx              ;preserve low word of running total
  600.         push    word ptr [si+soff+8+2] 
  601.         push    word ptr [si+soff+8]
  602.         push    word ptr [di+8+2]
  603.         push    word ptr [di+8]
  604.         call    _FixedMul       ;xform entry times source Z entry
  605.         add     sp,8            ;clear parameters from stack
  606.         pop     cx              ;restore low word of running total
  607.         add     cx,ax           ;running total for this row
  608.         adc     bp,dx
  609.  
  610.         add     cx,[si+soff+12] ;add in translation
  611.         adc     bp,[si+soff+12+2]
  612.         pop     bx              ;restore dest vector pointer
  613.         mov     [bx+doff],cx    ;save the result in the dest vector
  614.         mov     [bx+doff+2],bp
  615. soff=soff+16
  616. doff=doff+4
  617.         ENDM
  618.  
  619.         pop     bp              ;restore stack frame pointer
  620.  
  621. endif ;USE386
  622.  
  623.         pop     di              ;restore register variables
  624.         pop     si
  625.         pop     bp              ;restore stack frame
  626.         ret
  627. _XformVec       endp
  628.  
  629. ;=====================================================================
  630. ; Matrix multiplies SourceXform1 by SourceXform2 and stores the
  631. ; result in DestXform. Multiplies a 4x4 matrix times a 4x4 matrix;
  632. ; the result is a 4x4 matrix. Cheats by assuming the bottom row of
  633. ; each matrix is 0 0 0 1, and doesn't bother to set the bottom row
  634. ; of the destination.
  635. ; C near-callable as:
  636. ;       void ConcatXforms(Xform SourceXform1, Xform SourceXform2,
  637. ;               Xform DestXform)
  638. ;
  639. ; This assembly code is equivalent to this C code:
  640. ;   int i, j;
  641. ;
  642. ;   for (i=0; i<3; i++) {
  643. ;      for (j=0; j<3; j++)
  644. ;         DestXform[i][j] =
  645. ;               FixedMul(SourceXform1[i][0], SourceXform2[0][j]) +
  646. ;               FixedMul(SourceXform1[i][1], SourceXform2[1][j]) +
  647. ;               FixedMul(SourceXform1[i][2], SourceXform2[2][j]);
  648. ;      DestXform[i][3] =
  649. ;            FixedMul(SourceXform1[i][0], SourceXform2[0][3]) +
  650. ;            FixedMul(SourceXform1[i][1], SourceXform2[1][3]) +
  651. ;            FixedMul(SourceXform1[i][2], SourceXform2[2][3]) +
  652. ;            SourceXform1[i][3];
  653. ;   }
  654.  
  655. CXparms struc
  656.         dw      2 dup(?)        ;return address & pushed BP
  657. SourceXform1 dw ?               ;pointer to first source xform matrix
  658. SourceXform2 dw ?               ;pointer to second source xform matrix
  659. DestXform    dw ?               ;pointer to destination xform matrix
  660. CXparms ends
  661.  
  662.         align   ALIGNMENT
  663.         public _ConcatXforms
  664. _ConcatXforms   proc    near
  665.         push    bp              ;preserve stack frame
  666.         mov     bp,sp           ;set up local stack frame
  667.         push    si              ;preserve register variables
  668.         push    di
  669.  
  670. if USE386
  671.  
  672.         mov     bx,[bp].SourceXform2 ;BX points to xform2 matrix
  673.         mov     si,[bp].SourceXform1 ;SI points to xform1 matrix
  674.         mov     di,[bp].DestXform    ;DI points to dest xform matrix
  675.  
  676. roff=0                          ;row offset
  677.         REPT 3                  ;once for each row
  678. coff=0                          ;column offset
  679.         REPT 3                  ;once for each of the first 3 columns,
  680.                                 ; assuming 0 as the bottom entry (no
  681.                                 ; translation)
  682.         mov     eax,[si+roff]   ;column 0 entry on this row
  683.         imul    dword ptr [bx+coff] ;times row 0 entry in column
  684. if MUL_ROUNDING_ON
  685.         add     eax,8000h       ;round by adding 2^(-17)
  686.         adc     edx,0           ;whole part of result is in DX
  687. endif ;MUL_ROUNDING_ON
  688.         shrd    eax,edx,16      ;shift the result back to 16.16 form
  689.         mov     ecx,eax         ;set running total
  690.  
  691.         mov     eax,[si+roff+4] ;column 1 entry on this row
  692.         imul    dword ptr [bx+coff+16] ;times row 1 entry in col
  693. if MUL_ROUNDING_ON
  694.         add     eax,8000h       ;round by adding 2^(-17)
  695.         adc     edx,0           ;whole part of result is in DX
  696. endif ;MUL_ROUNDING_ON
  697.         shrd    eax,edx,16      ;shift the result back to 16.16 form
  698.         add     ecx,eax         ;running total
  699.  
  700.         mov     eax,[si+roff+8] ;column 2 entry on this row
  701.         imul    dword ptr [bx+coff+32] ;times row 2 entry in col
  702. if MUL_ROUNDING_ON
  703.         add     eax,8000h       ;round by adding 2^(-17)
  704.         adc     edx,0           ;whole part of result is in DX
  705. endif ;MUL_ROUNDING_ON
  706.         shrd    eax,edx,16      ;shift the result back to 16.16 form
  707.         add     ecx,eax         ;running total
  708.  
  709.         mov     [di+coff+roff],ecx ;save the result in dest matrix
  710. coff=coff+4                     ;point to next col in xform2 & dest
  711.         ENDM
  712.                                 ;now do the fourth column, assuming
  713.                                 ; 1 as the bottom entry, causing
  714.                                 ; translation to be performed
  715.         mov     eax,[si+roff]   ;column 0 entry on this row
  716.         imul    dword ptr [bx+coff] ;times row 0 entry in column
  717. if MUL_ROUNDING_ON
  718.         add     eax,8000h       ;round by adding 2^(-17)
  719.         adc     edx,0           ;whole part of result is in DX
  720. endif ;MUL_ROUNDING_ON
  721.         shrd    eax,edx,16      ;shift the result back to 16.16 form
  722.         mov     ecx,eax         ;set running total
  723.  
  724.         mov     eax,[si+roff+4] ;column 1 entry on this row
  725.         imul    dword ptr [bx+coff+16] ;times row 1 entry in col
  726. if MUL_ROUNDING_ON
  727.         add     eax,8000h       ;round by adding 2^(-17)
  728.         adc     edx,0           ;whole part of result is in DX
  729. endif ;MUL_ROUNDING_ON
  730.         shrd    eax,edx,16      ;shift the result back to 16.16 form
  731.         add     ecx,eax         ;running total
  732.  
  733.         mov     eax,[si+roff+8] ;column 2 entry on this row
  734.         imul    dword ptr [bx+coff+32] ;times row 2 entry in col
  735. if MUL_ROUNDING_ON
  736.         add     eax,8000h       ;round by adding 2^(-17)
  737.         adc     edx,0           ;whole part of result is in DX
  738. endif ;MUL_ROUNDING_ON
  739.         shrd    eax,edx,16      ;shift the result back to 16.16 form
  740.         add     ecx,eax         ;running total
  741.  
  742.         add     ecx,[si+roff+12] ;add in translation
  743.  
  744.         mov     [di+coff+roff],ecx ;save the result in dest matrix
  745. coff=coff+4                     ;point to next col in xform2 & dest
  746.  
  747. roff=roff+16                    ;point to next col in xform2 & dest
  748.         ENDM
  749.  
  750. else ;!USE386
  751.  
  752.         mov     di,[bp].SourceXform2 ;DI points to xform2 matrix
  753.         mov     si,[bp].SourceXform1 ;SI points to xform1 matrix
  754.         mov     bx,[bp].DestXform    ;BX points to dest xform matrix
  755.         push    bp                   ;preserve stack frame pointer
  756.  
  757. roff=0                          ;row offset
  758.         REPT 3                  ;once for each row
  759. coff=0                          ;column offset
  760.         REPT 3                  ;once for each of the first 3 columns,
  761.                                 ; assuming 0 as the bottom entry (no
  762.                                 ; translation)
  763.         push    bx              ;remember dest vector pointer
  764.         push    word ptr [si+roff+2] 
  765.         push    word ptr [si+roff]
  766.         push    word ptr [di+coff+2]
  767.         push    word ptr [di+coff]
  768.         call    _FixedMul       ;column 0 entry on this row times row 0
  769.                                 ; entry in column
  770.         add     sp,8            ;clear parameters from stack
  771.         mov     cx,ax           ;set running total
  772.         mov     bp,dx
  773.  
  774.         push    cx              ;preserve low word of running total
  775.         push    word ptr [si+roff+4+2] 
  776.         push    word ptr [si+roff+4]
  777.         push    word ptr [di+coff+16+2]
  778.         push    word ptr [di+coff+16]
  779.         call    _FixedMul       ;column 1 entry on this row times row 1
  780.                                 ; entry in column
  781.         add     sp,8            ;clear parameters from stack
  782.         pop     cx              ;restore low word of running total
  783.         add     cx,ax           ;running total for this row
  784.         adc     bp,dx
  785.  
  786.         push    cx              ;preserve low word of running total
  787.         push    word ptr [si+roff+8+2] 
  788.         push    word ptr [si+roff+8]
  789.         push    word ptr [di+coff+32+2]
  790.         push    word ptr [di+coff+32]
  791.         call    _FixedMul       ;column 1 entry on this row times row 1
  792.                                 ; entry in column
  793.         add     sp,8            ;clear parameters from stack
  794.         pop     cx              ;restore low word of running total
  795.         add     cx,ax           ;running total for this row
  796.         adc     bp,dx
  797.  
  798.         pop     bx              ;restore DestXForm pointer
  799.         mov     [bx+coff+roff],cx ;save the result in dest matrix
  800.         mov     [bx+coff+roff+2],bp
  801. coff=coff+4                     ;point to next col in xform2 & dest
  802.         ENDM
  803.                                 ;now do the fourth column, assuming
  804.                                 ; 1 as the bottom entry, causing
  805.                                 ; translation to be performed
  806.         push    bx              ;remember dest vector pointer
  807.         push    word ptr [si+roff+2] 
  808.         push    word ptr [si+roff]
  809.         push    word ptr [di+coff+2]
  810.         push    word ptr [di+coff]
  811.         call    _FixedMul       ;column 0 entry on this row times row 0
  812.                                 ; entry in column
  813.         add     sp,8            ;clear parameters from stack
  814.         mov     cx,ax           ;set running total
  815.         mov     bp,dx
  816.  
  817.         push    cx              ;preserve low word of running total
  818.         push    word ptr [si+roff+4+2] 
  819.         push    word ptr [si+roff+4]
  820.         push    word ptr [di+coff+16+2]
  821.         push    word ptr [di+coff+16]
  822.         call    _FixedMul       ;column 1 entry on this row times row 1
  823.                                 ; entry in column
  824.         add     sp,8            ;clear parameters from stack
  825.         pop     cx              ;restore low word of running total
  826.         add     cx,ax           ;running total for this row
  827.         adc     bp,dx
  828.  
  829.         push    cx              ;preserve low word of running total
  830.         push    word ptr [si+roff+8+2] 
  831.         push    word ptr [si+roff+8]
  832.         push    word ptr [di+coff+32+2]
  833.         push    word ptr [di+coff+32]
  834.         call    _FixedMul       ;column 1 entry on this row times row 1
  835.                                 ; entry in column
  836.         add     sp,8            ;clear parameters from stack
  837.         pop     cx              ;restore low word of running total
  838.         add     cx,ax           ;running total for this row
  839.         adc     bp,dx
  840.  
  841.         add     cx,[si+roff+12] ;add in translation
  842.         add     bp,[si+roff+12+2]
  843.  
  844.         pop     bx              ;restore DestXForm pointer
  845.         mov     [bx+coff+roff],cx ;save the result in dest matrix
  846.         mov     [bx+coff+roff+2],bp
  847. coff=coff+4                     ;point to next col in xform2 & dest
  848.  
  849. roff=roff+16                    ;point to next col in xform2 & dest
  850.         ENDM
  851.  
  852.         pop     bp              ;restore stack frame pointer
  853.  
  854. endif ;USE386
  855.  
  856.         pop     di              ;restore register variables
  857.         pop     si
  858.         pop     bp              ;restore stack frame
  859.         ret
  860. _ConcatXforms   endp
  861.         end
  862.