home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Graphics Programming Black Book (Special Edition)
/
BlackBook.bin
/
disk1
/
xsharp22
/
fixed.asm
< prev
next >
Wrap
Assembly Source File
|
1997-06-18
|
35KB
|
862 lines
; Fixed point routines.
; Tested with TASM 4.0.
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)
adc edx,0 ;whole part of result is in DX
endif ;MUL_ROUNDING_ON
shr eax,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 short 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 short 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
mul di
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 ;accumulate result in CX:BX
mov dx,cx
mov ax,bx
pop cx
and cx,cx ;is the result negative?
jz short 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
align ALIGNMENT
public _FixedDiv
_FixedDiv proc near
push bp
mov bp,sp
if USE386
if DIV_ROUNDING_ON
sub cx,cx ;assume positive result
mov eax,[bp+Dividend]
and eax,eax ;positive dividend?
jns short 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 short 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 short 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
sub cx,cx ;assume both operands positive
mov ax,word ptr [bp+Dividend+2]
and ax,ax ;first operand negative?
jns short CheckSecondOperandD ;no
neg ax ;yes, so negate first operand
neg word ptr [bp+Dividend]
sbb ax,0
inc cx ;mark that first operand is negative
CheckSecondOperandD:
mov bx,word ptr [bp+Divisor+2]
and bx,bx ;second operand negative?
jns short SaveSignStatusD ;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 short 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 *);
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
_CosSin proc near
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 short CheckInRange
MakePos: ;less than 0, so make it positive
add bx,360*10
js short 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 short MakeInRange
cmp bx,180*10 ;figure out which quadrant
ja short BottomHalf ;quadrant 2 or 3
cmp bx,90*10 ;quadrant 0 or 1
ja short Quadrant1
;quadrant 0
shl bx,2
mov eax,CosTable[bx] ;look up sine
neg bx ;sin(Angle) = cos(90-Angle)
mov edx,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)
mov edx,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 short Quadrant2
;quadrant 3
shl bx,2
mov eax,CosTable[bx] ;look up cosine
neg bx ;sin(Angle) = cos(90-Angle)
mov edx,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
mov eax,CosTable[bx] ;look up cosine
neg eax ;negative in this quadrant
neg bx ;sin(Angle) = cos(90-Angle)
mov edx,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 short CheckInRange
MakePos: ;less than 0, so make it positive
add bx,360*10
js short 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 short MakeInRange
cmp bx,180*10 ;figure out which quadrant
ja short BottomHalf ;quadrant 2 or 3
cmp bx,90*10 ;quadrant 0 or 1
ja short Quadrant1
;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
ja short Quadrant2
;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 short 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 short 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 short FixedMulDone ;no, we're all set
neg dx ;yes, so negate DX:AX
neg ax
sbb dx,0
FixedMulDone:
ENDM
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
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
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 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
mov eax,[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
_XformVec endp
;=====================================================================
; 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
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)
mov eax,[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
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 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 eax,[si+roff+8] ;column 2 entry on this row
imul dword 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
mov eax,[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
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 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 eax,[si+roff+8] ;column 2 entry on this row
imul dword 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
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
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