341 lines
11 KiB
NASM
341 lines
11 KiB
NASM
.MODEL small, c
|
|
INCLUDE demo.inc
|
|
.CODE
|
|
|
|
;* AddLong - Adds two double-word (long) integers.
|
|
;*
|
|
;* Shows: Instructions - add adc
|
|
;*
|
|
;* Params: long1 - First integer
|
|
;* long2 - Second integer
|
|
;*
|
|
;* Return: Sum as long integer
|
|
|
|
AddLong PROC \
|
|
long1:DWORD, long2:DWORD
|
|
|
|
mov ax, WORD PTR long1[0] ; AX = low word, long1
|
|
mov dx, WORD PTR long1[2] ; DX = high word, long1
|
|
add ax, WORD PTR long2[0] ; Add low word, long2
|
|
adc dx, WORD PTR long2[2] ; Add high word, long2
|
|
ret ; Result returned as DX:AX
|
|
|
|
AddLong ENDP
|
|
|
|
|
|
|
|
|
|
;* SubLong - Subtracts a double-word (long) integer from another.
|
|
;*
|
|
;* Shows: Instructions - sub sbb
|
|
;*
|
|
;* Params: long1 - First integer
|
|
;* long2 - Second integer
|
|
;*
|
|
;* Return: Difference as long integer
|
|
|
|
SubLong PROC \
|
|
long1:DWORD, long2:DWORD
|
|
|
|
mov ax, WORD PTR long1[0] ; AX = low word, long1
|
|
mov dx, WORD PTR long1[2] ; DX = high word, long1
|
|
sub ax, WORD PTR long2[0] ; Subtract low word, long2
|
|
sbb dx, WORD PTR long2[2] ; Subtract high word, long2
|
|
ret ; Result returned as DX:AX
|
|
|
|
SubLong ENDP
|
|
|
|
|
|
;* MulLong - Multiplies two unsigned double-word (long) integers. The
|
|
;* procedure allows for a product of twice the length of the multipliers,
|
|
;* thus preventing overflows. The result is copied into a 4-word data area
|
|
;* and a pointer to the data area is returned.
|
|
;*
|
|
;* Shows: Instruction - mul
|
|
;*
|
|
;* Params: long1 - First integer (multiplicand)
|
|
;* long2 - Second integer (multiplier)
|
|
;*
|
|
;* Return: Pointer to quadword result
|
|
|
|
.DATA
|
|
PUBLIC result
|
|
result DQ WORD PTR ? ; Result from MulLong
|
|
|
|
.CODE
|
|
MulLong PROC \
|
|
long1:DWORD, long2:DWORD
|
|
|
|
mov ax, WORD PTR long2[2] ; Multiply long2 high word
|
|
mul WORD PTR long1[2] ; by long1 high word
|
|
mov WORD PTR result[4], ax
|
|
mov WORD PTR result[6], dx
|
|
|
|
mov ax, WORD PTR long2[2] ; Multiply long2 high word
|
|
mul WORD PTR long1[0] ; by long1 low word
|
|
mov WORD PTR result[2], ax
|
|
add WORD PTR result[4], dx
|
|
adc WORD PTR result[6], 0 ; Add any remnant carry
|
|
|
|
mov ax, WORD PTR long2[0] ; Multiply long2 low word
|
|
mul WORD PTR long1[2] ; by long1 high word
|
|
add WORD PTR result[2], ax
|
|
adc WORD PTR result[4], dx
|
|
adc WORD PTR result[6], 0 ; Add any remnant carry
|
|
|
|
mov ax, WORD PTR long2[0] ; Multiply long2 low word
|
|
mul WORD PTR long1[0] ; by long1 low word
|
|
mov WORD PTR result[0], ax
|
|
add WORD PTR result[2], dx
|
|
adc WORD PTR result[4], 0 ; Add any remnant carry
|
|
|
|
mov ax, OFFSET result ; Return pointer
|
|
mov dx, @data ; to result
|
|
ret
|
|
|
|
MulLong ENDP
|
|
|
|
|
|
;* ImulLong - Multiplies two signed double-word integers. Because the imul
|
|
;* instruction (illustrated here) treats each word as a signed number, its
|
|
;* use is impractical when multiplying multi-word values. Thus the technique
|
|
;* used in the MulLong procedure can't be adopted here. Instead, ImulLong
|
|
;* is broken into three sections arranged in ascending order of computational
|
|
;* overhead. The procedure tests the values of the two integers and selects
|
|
;* the section that involves the minimum required effort to multiply them.
|
|
;*
|
|
;* Shows: Instruction - imul
|
|
;*
|
|
;* Params: long1 - First integer (multiplicand)
|
|
;* long2 - Second integer (multiplier)
|
|
;*
|
|
;* Return: Result as long integer
|
|
|
|
ImulLong PROC \
|
|
USES si, \
|
|
long1:DWORD, long2:DWORD
|
|
|
|
; Section 1 tests for integers in the range of 0 to 65,535. If both
|
|
; numbers are within these limits, they're treated as unsigned short
|
|
; integers.
|
|
|
|
sect1: mov ax, WORD PTR long2[0] ; AX = low word of long2
|
|
mov dx, WORD PTR long2[2] ; DX = high word of long2
|
|
mov bx, WORD PTR long1[0] ; BX = low word of long1
|
|
mov cx, WORD PTR long1[2] ; CX = high word of long1
|
|
or dx, dx ; Both high words zero?
|
|
jnz sect2 ; No? Go to section 2
|
|
or cx, cx
|
|
jnz sect2
|
|
mul bx ; Yes? Multiply the low words
|
|
jmp SHORT exit ; and exit section 1
|
|
|
|
; Section 2 tests for integers in the range of -32,768 to 32,767. If
|
|
; both numbers are within these limits, they're treated as signed short
|
|
; integers.
|
|
|
|
sect2: push ax ; Save long2 low word
|
|
push bx ; Save long1 low word
|
|
or dx, dx ; High word of long2 = 0?
|
|
jnz @F ; No? Test for negative
|
|
test ah, 80h ; Low word of long2 in range?
|
|
jz skip1 ; Yes? long2 ok, so test long1
|
|
jmp SHORT sect3 ; No? Go to section 3
|
|
@@: cmp dx, 0FFFFh ; Empty with sign flag set?
|
|
jne sect3 ; No? Go to section 3
|
|
test ah, 80h ; High bit set in low word?
|
|
jz sect3 ; No? Low word is too high
|
|
|
|
skip1: or cx, cx ; High word of long1 = 0?
|
|
jnz @F ; No? Test for negative
|
|
test bh, 80h ; Low word of long1 in range?
|
|
jz skip2 ; Yes? long1 ok, so use sect 2
|
|
jmp SHORT sect3 ; No? Go to section 3
|
|
@@: cmp cx, 0FFFFh ; Empty with sign flag set?
|
|
jne sect3 ; No? Go to section 3
|
|
test bh, 80h ; High bit set in low word?
|
|
jz sect3 ; No? Low word is too high
|
|
|
|
skip2: imul bx ; Multiply low words
|
|
pop bx ; Clean stack
|
|
pop bx
|
|
jmp SHORT exit ; Exit section 2
|
|
|
|
; Section 3 involves the most computational overhead. It treats the two
|
|
; numbers as signed long (double-word) integers.
|
|
|
|
sect3: pop bx ; Recover long1 low word
|
|
pop ax ; Recover long2 low word
|
|
mov si, dx ; SI = long2 high word
|
|
push ax ; Save long2 low word
|
|
mul cx ; long1 high word x long2 low word
|
|
mov cx, ax ; Accumulate products in CX
|
|
mov ax, bx ; AX = low word of long1
|
|
mul si ; Multiply by long2 high word
|
|
add cx, ax ; Add to previous product
|
|
pop ax ; Recover long2 low word
|
|
mul bx ; Multiply by long1 low word
|
|
add dx, cx ; Add to product high word
|
|
|
|
exit: ret ; Return result as DX:AX
|
|
|
|
ImulLong ENDP
|
|
|
|
|
|
;* DivLong - Divides an unsigned long integer by an unsigned short integer.
|
|
;* The procedure does not check for overflow or divide-by-zero.
|
|
;*
|
|
;* Shows: Instruction - div
|
|
;*
|
|
;* Params: long1 - First integer (dividend)
|
|
;* short2 - Second integer (divisor)
|
|
;* remn - Pointer to remainder
|
|
;*
|
|
;* Return: Quotient as short integer
|
|
|
|
DivLong PROC \
|
|
USES di, \
|
|
long1:DWORD, short2:WORD, remn:PTR WORD
|
|
|
|
mov ax, WORD PTR long1[0] ; AX = low word of dividend
|
|
mov dx, WORD PTR long1[2] ; DX = high word of dividend
|
|
div short2 ; Divide by short integer
|
|
LoadPtr es, di, remn ; Point ES:DI to remainder
|
|
mov es:[di], dx ; Copy remainder
|
|
ret ; Return with AX = quotient
|
|
|
|
DivLong ENDP
|
|
|
|
|
|
;* IdivLong - Divides a signed long integer by a signed short integer.
|
|
;* The procedure does not check for overflow or divide-by-zero.
|
|
;*
|
|
;* Shows: Instruction - idiv
|
|
;*
|
|
;* Params: long1 - First integer (dividend)
|
|
;* short2 - Second integer (divisor)
|
|
;* remn - Pointer to remainder
|
|
;*
|
|
;* Return: Quotient as short integer
|
|
|
|
IdivLong PROC \
|
|
USES di, \
|
|
long1:DWORD, short2:WORD, remn:PTR WORD
|
|
|
|
mov ax, WORD PTR long1[0] ; AX = low word of dividend
|
|
mov dx, WORD PTR long1[2] ; DX = high word of dividend
|
|
idiv short2 ; Divide by short integer
|
|
LoadPtr es, di, remn ; ES:DI = remainder
|
|
mov es:[di], dx ; Copy remainder
|
|
ret ; Return with AX = quotient
|
|
|
|
IdivLong ENDP
|
|
|
|
|
|
;* Quadratic - Solves for the roots of a quadratic equation of form
|
|
;* A*x*x + B*x + C = 0
|
|
;* using floating-point instructions. This procedure requires either a math
|
|
;* coprocessor or emulation code. If executing within the QuickAssembler
|
|
;* environment, emulation is automatically provided if a coprocessor is not
|
|
;* installed. If executing from the QCL command line, the /FPi switch must
|
|
;* be specified if a coprocessor is not installed. For example, to create
|
|
;* the MATHDEMO.EXE example program with floating-point emulation, enter the
|
|
;* following line:
|
|
;* QCL /Cx mathdemo.c /FPi math.asm common.asm
|
|
;*
|
|
;* Shows: Instructions - sahf fld1 fld fadd fmul
|
|
;* fxch fsubr fchs fsubp fstp
|
|
;* fst fdivr fwait ftst
|
|
;*
|
|
;* Params: a - Constant for 2nd-order term
|
|
;* b - Constant for 1st-order term
|
|
;* c - Equation constant
|
|
;* r1 - Pointer to 1st root
|
|
;* r2 - Pointer to 2nd root
|
|
;*
|
|
;* Return: Short integer with return code
|
|
;* 0 if both roots found
|
|
;* 1 if single root (placed in r1)
|
|
;* 2 if indeterminate
|
|
|
|
Quadratic PROC \
|
|
USES ds di si, \
|
|
a:DWORD, b:DWORD, c:DWORD, r1:PTR DWORD, r2:PTR DWORD
|
|
|
|
LOCAL status:WORD ; Intermediate status
|
|
|
|
LoadPtr es, di, r1 ; ES:DI points to 1st root
|
|
LoadPtr ds, si, r2 ; DS:SI points to 2nd root
|
|
sub bx, bx ; Clear error code
|
|
fld1 ; Load top of stack with 1
|
|
fadd st, st ; Double it to make 2
|
|
fld st ; Copy to next register
|
|
fmul a ; ST register = 2a
|
|
ftst ; Test current ST value
|
|
fstsw status ; Copy status to local word
|
|
fwait ; Ensure coprocessor is done
|
|
mov ax, status ; Copy status into AX
|
|
sahf ; Load flag register
|
|
jnz @F ; If C3 set, a = 0, in which case
|
|
; solution is x = -c / b
|
|
fld b ; Load b parameter
|
|
ftst ; Test current ST value
|
|
fstsw status ; Copy status to local word
|
|
fwait ; Ensure coprocessor is done
|
|
mov ax, status ; Copy status into AX
|
|
sahf ; Load flag register
|
|
jz exit2 ; If C3 set, b = 0, so don't divide
|
|
fld st ; Copy b to next register
|
|
fld c ; Load C parameter
|
|
fchs ; Reverse sign
|
|
fxch ; Exchange ST and ST(1)
|
|
fdiv ; Divide c by b
|
|
fst DWORD PTR es:[di] ; Copy result
|
|
jmp SHORT exit1 ; Return with code = 1
|
|
|
|
@@: fmul st(1), st ; ST(1) register = 4a
|
|
fxch ; Exchange ST and ST(1)
|
|
fmul c ; ST register = 4ac
|
|
ftst ; Test current ST value
|
|
fstsw status ; Copy status to local word
|
|
fwait ; Ensure coprocessor is done
|
|
mov ax, status ; Copy status into AX
|
|
sahf ; Load flag register
|
|
jp exit2 ; If C2 set, 4*a*c is infinite
|
|
|
|
fld b ; Else load b parameter
|
|
fmul st, st ; Square it; ST register = b*b
|
|
fsubr ; ST register = b*b - 4*a*c
|
|
ftst ; Test current ST value
|
|
fstsw status ; Copy status to local word
|
|
fwait ; Ensure coprocessor is done
|
|
mov ax, status ; Copy status into AX
|
|
sahf ; Load flag register
|
|
jc exit2 ; If C0 set, b*b < 4ac
|
|
jnz @F ; If C3 set, b*b = 4ac, in which
|
|
inc bx ; case only 1 root so set flag
|
|
|
|
@@: fsqrt ; Get square root
|
|
fld b ; Load b parameter
|
|
fchs ; Reverse sign
|
|
fxch ; Exchange ST and ST1
|
|
fld st ; Copy square root to next reg
|
|
fadd st, st(2) ; ST = -b + sqrt(b*b - 4*a*c)
|
|
fxch ; Exchange ST and ST1
|
|
fsubp st(2), st ; ST = -b - sqrt(b*b - 4*a*c)
|
|
|
|
fdiv st, st(2) ; Divide 1st dividend by 2*a
|
|
fstp WORD PTR es:[di] ; Copy result, pop stack
|
|
fdivr ; Divide 2nd dividend by 2*a
|
|
fstp WORD PTR ds:[si] ; Copy result, pop stack
|
|
jmp SHORT exit ; Return with code
|
|
|
|
exit2: inc bx ; Error code = 2 for indeterminancy
|
|
exit1: inc bx ; Error code = 1 for single root
|
|
exit: mov ax, bx
|
|
ret
|
|
|
|
Quadratic ENDP
|
|
|
|
END
|