;  Long Integer Package  -   Assembly Code Portion
;
;  Rob Shostak        7/82
;
;  This is the assembly language portion of a BDS-C library package
;  to enable the manipulation of long integers (which we also call
;  "quads", since they occupy 4 bytes) in the same spirit as Bob
;  Mathias' floating point package.  See long.c  and long.doc
;

INCLUDE "bds.lib"

FUNCTION long

; temporary storage is allocated in the
; "args" area of the run-time environment

u       equ  args       ;temporary quad storage (4 bytes)
uh      equ  u          ;high word of u
ul      equ  u+2        ;low word of u
mq      equ  u+4        ;temporary quad storage used by
                       ;multiplication and division routines
temp    equ  mq+4       ;temporary storage byte used by div'n routine


; long is main routine which dispatches to the various functions
; of the package according to the value of its first argument

long:   push b          ;save for benefit of caller
       call ma2toh     ;get 1st arg (function code) into HL and A
       mov  d,h
       mov  e,l
       dad  h
       dad  d          ;HL now has triple the function code
       lxi  d,jtab     ;base of jump table
       dad  d
       pchl            ;dispatch to appropriate function

jtab:   jmp  lmake      ;jump table for quad functions
       jmp  lcomp
       jmp  ladd
       jmp  lsub
       jmp  lmul
       jmp  ldiv
       jmp  lmod


; lmake converts integer (arg3) to a long (arg2)

lmake:  call ma4toh     ;get arg3 into HL
       mov  a,h        ;look at sign first
       ora  a
       push psw        ;save it
       cm   cmh        ;take abs value
       xchg            ;into (DE)
       lxi  b,0        ;zero out high word
       pop  psw
       cm   qneg       ;complement if necessary
       jmp  putarg     ;copy result into arg2 and return

;all other routines copy their arguments into the quad register (BCDE)
;and the temporary quad storage location u  (note that temporary storage
;must be used to keep the routines from clobbering the user's arguments)


;lcomp compares arg2 with arg3, returns -1, 0, 1 for <, =, >, resp

lcomp:  call ma3toh     ;get pointer to arg2
       call qld
       lxi  h,u
       call qst        ;arg2 now in u
       call ma4toh     ;get pointer to arg3
       call qld        ;arg3 now in (BCDE)
       lxi  h,-1       ;presume <
       call qsub
       call qtst
       pop  b          ;restore bc for caller
       rm
       inx  h
       rz
       inx  h
       ret

; long addition

ladd:   call getargs    ;get args into (BCDE) and u
       call qadd       ;do the addition
       jmp  putarg     ;copy result into arg2 and return

lsub:   call getargs
       call qsub
       jmp  putarg

lmul:   call getargs
       call qmul
       jmp  putarg

ldiv:   call getargs
       call qdiv
       jmp  putarg

lmod:   call getargs
       call qmod
       jmp  putarg

;getargs gets arg3 into u, arg4 into (BCDE)

getargs:
       call ma5toh             ;get ptr to arg3 (note use ma5 cause of
                               ;return addr on stack)
       call qld                ;arg3 now in (BCDE)
       lxi  h,u
       call qst                ;now in u
       call ma6toh             ;get ptr to arg4
       jmp  qld                ;arg4 now in (BCDE)


; putarg copies (BCDE) into result arg (arg2) and cleans up

putarg: call ma3toh             ;get pointer to arg2
       call qst                ;copy (BCDE) into it
       pop  b                  ;restore BC for caller
       ret



; quad subtraction  u - (BCDE) -> (BCDE)

qsub:   call qneg       ;complement (BCDE) and fall thru to add

; quad addition     u + (BCDE) -> (BCDE)

qadd:   push h
       lxi  h,u+3      ;tenSHUN
       mov  a,m        ;hup
       add  e          ;two
       mov  e,a        ;three
       dcx  h          ;four
       mov  a,m        ;hup
       adc  d          ;two
       mov  d,a        ;three
       dcx  h          ;four
       mov  a,m        ;hup
       adc  c          ;two
       mov  c,a        ;three
       dcx  h          ;four
       mov  a,m        ;hup
       adc  b          ;two
       mov  b,a        ;three
       pop  h          ;four
       ret             ;at ease


; two's complement (BCDE)

qneg:   push h
       xra  a
       mov  l,a
       sbb  e
       mov  e,a
       mov  a,l
       sbb  d
       mov  d,a
       mov  a,l
       sbb  c
       mov  c,a
       mov  a,l
       sbb  b
       mov  b,a
       pop  h
       ret


qneghl: push b
       push d
       call qld
       call qneg
       call qst
       pop  d
       pop  b
       ret

; signed quad multiplication
; u * (BCDE) --> (BCDE)

qmul:   call csign                      ;take abs values and compute signs
       push psw                        ;save result sign
       call uqmul                      ;compute product
qmul1:  pop  psw
       jm   qneg                       ;complement product if needed
       ret

; csign takes abs vals of u, (BCDE), and computes product of their signs

csign:  mov  a,b                        ;look at (BCDE) first
       ora  a
       push psw                        ;save flags
       cm   qneg                       ;complement if needed
       lxi  h,u                        ;now look at u
       mov  a,m
       ora  a
       jp   csign1
       call qneghl
       pop  psw
       xri  80h                        ;flip sign
       ret
csign1: pop psw
       ret

; unsigned quad multiplication
; u * (BCDE) --> (BCDE)     (expects ptr. to u in (HL)

uqmul:  lxi  h,u
       push h                          ;put pointer to u on stack
       lxi  h,mq
       call qst                        ;(BCDE) -> mq
       lxi  b,0                        ;init product to 0
       lxi  d,0
uqmul1: call qtsthl                     ;test if mq is 0
       jz   uqmul2                     ;if so, done
       xra  a                          ;clear carry
       call qrarhl                     ;shift mq over
       cc   qadd                       ;add u to (BCDE) if lsb=1
       xthl                            ;get pointer to u
       xra  a                          ;clear carry
       call qralhl                     ;double u
       xthl                            ;get back pointer to mq
       jmp  uqmul1
uqmul2: pop  h                          ;restore stack
       ret

; signed division  u / (BCDE) --> (BCDE)

qdiv:   call qtst                       ;first test for zero divisor
       rz
       call csign                      ;take care of signs
       push psw                        ;save quotient sign
       call uqdiv
       call qld                        ;get quotient in (BCDE)
       jmp  qmul1                      ;adjust sign of result

;  signed remainder  u mod (BCDE) --> (BCDE)

qmod:   call qtst                       ;test for zero modulus
       rz
       lda  u                          ;sign of u is that of result
       ora  a
       push psw                        ;save flags
       call csign                      ;get abs val of args
       call uqdiv                      ;remainder in (BCDE)
       jmp  qmul1


;  unsigned division  u / (BCDE) --> mq, remainder in (BCDE)



uqdiv:  lxi  h,mq                       ;mq will contain quotient
       call qclrhl                     ;clear it
       push h                          ;save it on the stack

       mvi  l,1                        ;now normalize divisor
uqdiv1: mov  a,b                        ;look at most signif non-sign bit
       ani  40h
       jnz   uqdiv2
       call qral                       ;if not 1, shift left
       inr  l
       jmp  uqdiv1
uqdiv2: mov  a,l
       sta  temp                       ;save normalization count
       lxi  h,u
       call qxchg                      ;want divid in (BCDE), divisor in u
       xthl                            ;pointer to mq in (HL), u on stack

;main loop

uqdiv3: call trial                      ;trial subtraction of divisor
       call qralhl                     ;shift in the carry
       lda  temp                       ;get the count
       dcr  a
       jz   uqdiv4                     ;done
       sta  temp                       ;save count again
       xthl                            ;divisor in (HL)
       xra  a
       call qrarhl                     ;shift it right one
       xthl                            ;quotient in (HL)
       jmp  uqdiv3

uqdiv4: inx  sp
       inx  sp                         ;clean off top of stack
       ret


trial:  call qsub                       ;subtract divid from divisor
       call qneg                       ;actually want divisor from divid
       stc                             ;assume was positive
       rp
       call qadd                       ;else must restore dividend
       xra  a                          ;clear carry
       ret


;
; routines to manipulate quads
;
; qld loads the quad pointed to by (HL) into (BCDE)

qld:    push h
       mov  b,m
       inx  h
       mov  c,m
       inx  h
       mov  d,m
       inx  h
       mov  e,m
       pop  h
       ret

; qst is inverse of qld

qst:    push h
       mov  m,b
       inx  h
       mov  m,c
       inx  h
       mov  m,d
       inx  h
       mov  m,e
       pop  h
       ret



; rotate  (BCDE) right thru carry

qrar:   mov a,b
       rar
       mov b,a
       mov a,c
       rar
       mov c,a
       mov a,d
       rar
       mov d,a
       mov a,e
       rar
       mov e,a
       ret

; same for quad pointed to by (HL)

qrarhl: push h
       mov  a,m
       rar
       mov  m,a
       inx  h
       mov  a,m
       rar
       mov  m,a
       inx  h
       mov  a,m
       rar
       mov  m,a
       inx  h
       mov  a,m
       rar
       mov  m,a
       pop  h
       ret


; rotate (BCDE) left thru carry

qral:   mov a,e
       ral
       mov e,a
       mov a,d
       ral
       mov d,a
       mov a,c
       ral
       mov c,a
       mov a,b
       ral
       mov b,a
       ret

; qralhl does it for quad pointed to by (HL)

qralhl: inx  h
       inx  h
       inx  h                          ;get to rightmost byte
       mov  a,m
       ral
       mov  m,a
       dcx  h
       mov  a,m
       ral
       mov  m,a
       dcx  h
       mov  a,m
       ral
       mov  m,a
       dcx  h
       mov  a,m
       ral
       mov  m,a
       ret


;qclrhl clears quad pointed to by (HL)

qclrhl: push h
       xra  a
       mov  m,a
       inx  h
       mov  m,a
       inx  h
       mov  m,a
       inx  h
       mov  m,a
       pop  h
       ret


; qtst tests sign of (BCDE), setting the usual flags

qtst:   mov  a,b                        ;look at most signif byte
       ora  a
       rnz
       ora  c                          ;test for zero
       ora  d
       ora  e
qtst1:  rp
       mvi  a,1
       ora  a
       ret

qtsthl: mov  a,m
       ora  a
       rnz
       push h
       inx  h
       ora  m
       inx  h
       ora  m
       inx  h
       ora  m
       pop  h
       jmp  qtst1

; swap (BCDE) with thing pointed to by HL

qxchg:  push h
       mov  a,m
       mov  m,b
       mov  b,a
       inx  h
       mov  a,m
       mov  m,c
       mov  c,a
       inx  h
       mov  a,m
       mov  m,d
       mov  d,a
       inx  h
       mov  a,m
       mov  m,e
       mov  e,a
       pop  h
       ret


ENDFUNCTION