home *** CD-ROM | disk | FTP | other *** search
- title BCDASM -- Copyright 1997, Morten Elling
- subttl Addition and subtraction of packed signed BCDs
-
- include model.inc
- include modelt.inc
- include bcd.ash
-
- ; Note:
- ; To avoid complaints from the assembler, two full-blown
- ; PROC headers are used even though the two procedures here
- ; use identical ARG and USES statements. Keep them in sync.
-
- if 0
- Addition Case
- x + y = x + y (a) @@addn
- x + (-y) = x - y (b) @@srcgt, x >= y
- -x + y = y - x (c) @@dstgt, x < y
- -x + (-y) = -(x + y) -(a) @@addn
-
- Subtraction
- x - y = x - y (b)
- x - (-y) = x + y (a)
- -x - y = -(x + y) -(a)
- -x - (-y) = y - x (c)
- endif
-
- @CODESEG
-
- ;//////////////////////////////////////////////////////////////////////
- ;// Name bcdAdd
- ;// Desc Add two packed signed BCD numbers (dest += src).
- ;//
- ;//
- ;// Entry Passed args
- ;// Exit Packed signed BCD sum returned to destination.
- ;// Accumulator (and sf,zf,cf flags) determined:
- ;// Acc = 0: No carry
- ;// Acc = 1: Carry (overflow)
- ;//
- ;// Note Destination and source may be the same.
-
- bcdAdd proc
- arg dstBCD :dataptr, \ ; Addr of 1st addend (=result)
- srcBCD :dataptr, \ ; Addr of 2nd addend
- BCDsz :@uint ; Byte size of each BCD
- @uses ds,es,rsi,rdi,rbx,rcx
- ;.
- mov ah, 00h ; Set up to keep sign bit
- join_: @cld ; Auto-increment index reg.s
- @LDS rsi, [srcBCD] ; Load
- @LES rdi, [dstBCD] ; pointers
- mov rbx, [BCDsz] ; Get byte size of BCD
- dec rbx ; Handy constant
- mov rcx, rbx ; Loop count (# digits DIV 2)
-
- ; ----- Get number signs
- mov al, [rsi+rbx] ; Get top byte of src
- xor al, ah ; Keep or flip sign bit
- mov ah, al ; Use ah to hold src's sign
- mov al, @ES [rdi+rbx] ; Get top byte of dst
- and rax, 8080h ; Isolate sign bits
- ; ah bit 7 = sign of src
- ; al bit 7 = sign of dst
- cmp al, ah ; If same sign,
- jz sh @@addn ; add numbers
-
- ; ----- Numbers have different signs. Find the numerically
- ; larger BCD, subtract the other from it, and use its
- ; sign for the result.
-
- lea rsi, [rsi+rbx-1] ; Point to byte before sign byte
- lea rdi, [rdi+rbx-1] ; In destination as well
- std ; Auto-decrement index reg.s
- repe cmpsb ; Compare numbers high-to-low
-
- ; cmpsb flags meaning
- ; cf=1 (jc) src < dst, use sign in al
- ; cf=0 (jnc) src >= dst, use sign in ah
- ; zf=1 (je) Treated as cf=0. Add code to handle this
- ; separately if zero results are frequent.
-
- jnc sh @@ahs ; Jump if src is larger (al <> ah)
- mov ah, al ; Set ah = sign of dst (al = ah)
- @@ahs: cld ; df = 0, ah = sign of larger number
- dec rcx ; Adjust for mismatch by cmpsb
- sub rsi, rcx ; Point back to LSB
- sub rdi, rcx ; in destination, too
- mov rcx, rbx ; Loop count
- xor al, ah ; If al = ah (xor clears carry)
- jz sh @@dstgt ; then subtract src from dst
-
- ; ----- Compute src - dst
- @alignn
- @@srcgt:mov al, [rsi] ; Get two digits from src
- inc rsi ; Step src pointer
- sbb al, @ES [rdi] ; Subtract carry & dst digits
- das ; Decimal adjust after subtraction
- stosb ; Store al to destination
- dec rcx ; Loop
- jnz @@srcgt ; until done
- jmp sh @@sign ; cf = 0
-
- ; ----- Compute dst - src
- @alignn
- @@dstgt:mov al, @ES [rdi] ; Get two digits from dst
- sbb al, [rsi] ; Subtract carry and src digits
- das ; Decimal adjust after subtraction
- stosb ; Store al to destination
- inc rsi ; Step src pointer
- dec rcx ; Loop
- jnz @@dstgt ; until done
- jmp sh @@sign ; cf = 0
-
- ; ----- Compute dst + src
- ; cf = 0, df = 0
- @alignn
- @@addn: mov al, [rsi] ; Get two digits of src
- inc rsi ; Step src pointer
- adc al, @ES [rdi] ; Add two digits of dst + cf
- daa ; Adjust to packed BCD format
- stosb ; Store al to destination
- dec rcx ; Loop
- jnz @@addn ; until done
- adc ah, 00h ; Set bit 0 in ah if overflow
- ; jmp sh @@sign ; Carry set by DAA if overflow
-
- ; ----- Test for zero result, determine sign and return value
- ; es:rdi -> dst's sign byte, df = 0
- ; ah bit 7 = sign, bit 0 = overflow
- @@sign: mov rcx, rbx ; Get REP count
- sub rdi, rbx ; Point to LSB
- or ah, 01000000b ; Set Z bit in ah
- and rax, 0ff00h ; Zero al (and high(eax) if 32-bit)
- repz scasb ; See if result is zero
- mov al, 01000001b ; Keep Z and C bits
- jz sh @@sig2 ; if zero
- mov al, 10000001b ; else keep S and C bits
- @@sig2: and ah, al ; Prepare for SAHF
- mov al, ah ; Isolate sign
- and al, 80h ; bit in al
- add rdi, rcx ; Point to dst's sign
- stosb ; Store sign
- mov al, ah ; Copy C bit
- and al, 1 ; to al
- sahf ; sf = bit 7, zf = bit 6, cf = bit 0
- mov ah, 0 ; Return flags and acc = carry
- RET
- bcdAdd endp
-
-
- ;//////////////////////////////////////////////////////////////////////
- ;// Name bcdSub
- ;// Desc Subtract two packed signed BCD numbers (dest -= src).
- ;//
- ;//
- ;// Entry Passed args
- ;// Exit Packed signed BCD difference returned to destination.
- ;// Accumulator (and sf,zf,cf flags) determined:
- ;// Acc = 0: No borrow
- ;// Acc = 1: Borrow (overflow)
- ;//
- ;// Note Destination and source may be the same
- @alignn
- bcdSub proc
- arg dstBCD :dataptr, \ ; Addr of minuend (=result)
- srcBCD :dataptr, \ ; Addr of subtrahend
- BCDsz :@uint ; Byte size of each BCD
- @uses ds,es,rsi,rdi,rbx,rcx
- ;.
- mov ah, 80h ; Set up to flip sign bit
- jmp join_ ; Subtraction is addition reversed
- bcdSub endp
-
- END