**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