401 lines
16 KiB
Markdown
401 lines
16 KiB
Markdown
------------------------ --------------------------------- --------------------
|
||
[Previous](53-01.html) [Table of Contents](index.html) [Next](53-03.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
**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](53-01.html) [Table of Contents](index.html) [Next](53-03.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
* * * * *
|
||
|
||
Graphics Programming Black Book © 2001 Michael Abrash
|