#
# $NetBSD: ilsp.s,v 1.3 2022/05/28 21:14:56 andvar Exp $
#
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
#
#
# (INCLUDING WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS,
#
#
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
#
bra.l _060LSP__idivs64_
short 0x0000
bra.l _060LSP__idivu64_
short 0x0000
bra.l _060LSP__imuls64_
short 0x0000
bra.l _060LSP__imulu64_
short 0x0000
bra.l _060LSP__cmp2_Ab_
short 0x0000
bra.l _060LSP__cmp2_Aw_
short 0x0000
bra.l _060LSP__cmp2_Al_
short 0x0000
bra.l _060LSP__cmp2_Db_
short 0x0000
bra.l _060LSP__cmp2_Dw_
short 0x0000
bra.l _060LSP__cmp2_Dl_
short 0x0000
align 0x200
#########################################################################
# #
# 64-bit divide instruction. #
# #
# #
# 0x4(sp) = divisor #
# 0x8(sp) = hi(dividend) #
# 0xc(sp) = lo(dividend) #
# 0x10(sp) = pointer to location to place quotient/remainder #
# #
# 0x10(sp) = points to location of remainder/quotient. #
# #
# #
#########################################################################
set POSNEG, -1
set NDIVISOR, -2
set NDIVIDEND, -3
set DDSECOND, -4
set DDNORMAL, -8
set DDQUOTIENT, -12
set DIV64_CC, -16
##########
##########
global _060LSP__idivs64_
_060LSP__idivs64_:
link.w %a6,&-16
movm.l &0x3f00,-(%sp) # save d2-d7
mov.w %cc,DIV64_CC(%a6)
st POSNEG(%a6) # signed operation
bra.b ldiv64_cont
##########
##########
global _060LSP__idivu64_
_060LSP__idivu64_:
link.w %a6,&-16
movm.l &0x3f00,-(%sp) # save d2-d7
mov.w %cc,DIV64_CC(%a6)
sf POSNEG(%a6) # unsigned operation
ldiv64_cont:
mov.l 0x8(%a6),%d7 # fetch divisor
beq.w ldiv64eq0 # divisor is = 0!!!
mov.l 0xc(%a6), %d5 # get dividend hi
mov.l 0x10(%a6), %d6 # get dividend lo
tst.b POSNEG(%a6) # signed or unsigned?
beq.b ldspecialcases # use positive divide
tst.l %d7 # chk sign of divisor
slt NDIVISOR(%a6) # save sign of divisor
bpl.b ldsgndividend
neg.l %d7 # complement negative divisor
ldsgndividend:
tst.l %d5 # chk sign of hi(dividend)
slt NDIVIDEND(%a6) # save sign of dividend
bpl.b ldspecialcases
mov.w &0x0, %cc # clear 'X' cc bit
negx.l %d6 # complement signed dividend
negx.l %d5
# - is (dividend == 0) ?
# - is (hi(dividend) == 0 && (divisor <= lo(dividend))) ? (32-bit div)
ldspecialcases:
tst.l %d5 # is (hi(dividend) == 0)
bne.b ldnormaldivide # no, so try it the long way
tst.l %d6 # is (lo(dividend) == 0), too
beq.w lddone # yes, so (dividend == 0)
cmp.l %d7,%d6 # is (divisor <= lo(dividend))
bls.b ld32bitdivide # yes, so use 32 bit divide
exg %d5,%d6 # q = 0, r = dividend
bra.w ldivfinish # can't divide, we're done.
ld32bitdivide:
tdivu.l %d7, %d5:%d6 # it's only a 32/32 bit div!
bra.b ldivfinish
ldnormaldivide:
# - is hi(dividend) >= divisor ? if yes, then overflow
cmp.l %d7,%d5
bls.b lddovf # answer won't fit in 32 bits
bsr.l ldclassical # do int divide
ldivfinish:
tst.b POSNEG(%a6) # do divs, divu separately
beq.b lddone # divu has no processing!!!
tst.b NDIVIDEND(%a6) # remainder has same sign
beq.b ldcc # as dividend.
neg.l %d5 # sgn(rem) = sgn(dividend)
ldcc:
mov.b NDIVISOR(%a6), %d0
eor.b %d0, NDIVIDEND(%a6) # chk if quotient is negative
beq.b ldqpos # branch to quot positive
# 0x80000000 is the largest number representable as a 32-bit negative
cmpi.l %d6, &0x80000000 # will (-quot) fit in 32 bits?
bhi.b lddovf
neg.l %d6 # make (-quot) 2's comp
bra.b lddone
ldqpos:
btst &0x1f, %d6 # will (+quot) fit in 32 bits?
bne.b lddovf
lddone:
# if the register numbers are the same, only the quotient gets saved.
andi.w &0x10,DIV64_CC(%a6)
mov.w DIV64_CC(%a6),%cc
tst.l %d6 # may set 'N' ccode bit
ldexit:
movm.l &0x0060,([0x14,%a6]) # save result
movm.l (%sp)+,&0x00fc # restore d2-d7
unlk %a6
rts
lddovf:
mov.l 0xc(%a6), %d5 # get dividend hi
mov.l 0x10(%a6), %d6 # get dividend lo
andi.w &0x1c,DIV64_CC(%a6)
ori.w &0x02,DIV64_CC(%a6) # set 'V' ccode bit
mov.w DIV64_CC(%a6),%cc
bra.b ldexit
ldiv64eq0:
mov.l 0xc(%a6),([0x14,%a6])
mov.l 0x10(%a6),([0x14,%a6],0x4)
mov.w DIV64_CC(%a6),%cc
movm.l (%sp)+,&0x00fc # restore d2-d7
unlk %a6
divu.w &0x0,%d0 # force a divbyzero exception
rts
###########################################################################
#########################################################################
# #
#########################################################################
ldclassical:
# if the divisor msw is 0, use simpler algorithm then the full blown
cmpi.l %d7, &0xffff
bhi.b lddknuth # go use D. Knuth algorithm
clr.l %d1
swap %d5 # same as r*b if previous step rqd
swap %d6 # get u3 to lsw position
mov.w %d6, %d5 # rb + u3
divu.w %d7, %d5
mov.w %d5, %d1 # first quotient word
swap %d6 # get u4
mov.w %d6, %d5 # rb + u4
divu.w %d7, %d5
swap %d1
mov.w %d5, %d1 # 2nd quotient 'digit'
clr.w %d5
swap %d5 # now remainder
mov.l %d1, %d6 # and quotient
rts
lddknuth:
# 'normalized' so that the process of estimating the quotient digit
clr.l DDNORMAL(%a6) # count of shifts for normalization
clr.b DDSECOND(%a6) # clear flag for quotient digits
clr.l %d1 # %d1 will hold trial quotient
lddnchk:
btst &31, %d7 # must we normalize? first word of
bne.b lddnormalized # divisor (V1) must be >= 65536/2
addq.l &0x1, DDNORMAL(%a6) # count normalization shifts
lsl.l &0x1, %d7 # shift the divisor
lsl.l &0x1, %d6 # shift u4,u3 with overflow to u2
roxl.l &0x1, %d5 # shift u1,u2
bra.w lddnchk
lddnormalized:
mov.l %d7, %d3 # divisor
mov.l %d5, %d2 # dividend mslw
swap %d2
swap %d3
cmp.w %d2, %d3 # V1 = U1 ?
bne.b lddqcalc1
mov.w &0xffff, %d1 # use max trial quotient word
bra.b lddadj0
lddqcalc1:
mov.l %d5, %d1
divu.w %d3, %d1 # use quotient of mslw/msw
andi.l &0x0000ffff, %d1 # zero any remainder
lddadj0:
mov.l %d6, -(%sp)
clr.w %d6 # word u3 left
swap %d6 # in lsw position
lddadj1: mov.l %d7, %d3
mov.l %d1, %d2
mulu.w %d7, %d2 # V2q
swap %d3
mulu.w %d1, %d3 # V1q
mov.l %d5, %d4 # U1U2
sub.l %d3, %d4 # U1U2 - V1q
swap %d4
mov.w %d4,%d0
mov.w %d6,%d4 # insert lower word (U3)
tst.w %d0 # is upper word set?
bne.w lddadjd1
cmp.l %d2, %d4
bls.b lddadjd1 # is V2q > (U1U2-V1q) + U3 ?
subq.l &0x1, %d1 # yes, decrement and recheck
bra.b lddadj1
lddadjd1:
mov.l %d5, -(%sp) # save %d5 (%d6 already saved)
mov.l %d1, %d6
swap %d6 # shift answer to ms 3 words
mov.l %d7, %d5
bsr.l ldmm2
mov.l %d5, %d2 # now %d2,%d3 are trial*divisor
mov.l %d6, %d3
mov.l (%sp)+, %d5 # restore dividend
mov.l (%sp)+, %d6
sub.l %d3, %d6
subx.l %d2, %d5 # subtract double precision
bcc ldd2nd # no carry, do next quotient digit
subq.l &0x1, %d1 # q is one too large
# - according to Knuth, this is done only 2 out of 65536 times for random
clr.l %d2
mov.l %d7, %d3
swap %d3
clr.w %d3 # %d3 now ls word of divisor
add.l %d3, %d6 # aligned with 3rd word of dividend
addx.l %d2, %d5
mov.l %d7, %d3
clr.w %d3 # %d3 now ms word of divisor
swap %d3 # aligned with 2nd word of dividend
add.l %d3, %d5
ldd2nd:
tst.b DDSECOND(%a6) # both q words done?
bne.b lddremain
# (subtracted) dividend
mov.w %d1, DDQUOTIENT(%a6)
clr.l %d1
swap %d5
swap %d6
mov.w %d6, %d5
clr.w %d6
st DDSECOND(%a6) # second digit
bra.w lddnormalized
lddremain:
mov.w %d1, DDQUOTIENT+2(%a6)
mov.w %d5, %d6
swap %d6
swap %d5
mov.l DDNORMAL(%a6), %d7 # get norm shift count
beq.b lddrn
subq.l &0x1, %d7 # set for loop count
lddnlp:
lsr.l &0x1, %d5 # shift into %d6
roxr.l &0x1, %d6
dbf %d7, lddnlp
lddrn:
mov.l %d6, %d5 # remainder
mov.l DDQUOTIENT(%a6), %d6 # quotient
rts
ldmm2:
mov.l %d6, %d2
mov.l %d6, %d3
mov.l %d5, %d4
swap %d3
swap %d4
mulu.w %d5, %d6 # %d6 <- lsw*lsw
mulu.w %d3, %d5 # %d5 <- msw-dest*lsw-source
mulu.w %d4, %d2 # %d2 <- msw-source*lsw-dest
mulu.w %d4, %d3 # %d3 <- msw*msw
clr.l %d4
swap %d6
add.w %d5, %d6 # add msw of l*l to lsw of m*l product
addx.w %d4, %d3 # add any carry to m*m product
add.w %d2, %d6 # add in lsw of other m*l product
addx.w %d4, %d3 # add any carry to m*m product
swap %d6 # %d6 is low 32 bits of final product
clr.w %d5
clr.w %d2 # lsw of two mixed products used,
swap %d5 # now use msws of longwords
swap %d2
add.l %d2, %d5
add.l %d3, %d5 # %d5 now ms 32 bits of final product
rts
#########################################################################
# #
# 64-bit multiply instruction. #
# #
# #
# 0x4(sp) = multiplier #
# 0x8(sp) = multiplicand #
# 0xc(sp) = pointer to location to place 64-bit result #
# #
# 0xc(sp) = points to location of 64-bit result #
# #
# "rts". #
# #
#########################################################################
set MUL64_CC, -4
global _060LSP__imulu64_
_060LSP__imulu64_:
link.w %a6,&-4
movm.l &0x3800,-(%sp) # save d2-d4
mov.w %cc,MUL64_CC(%a6) # save incoming ccodes
mov.l 0x8(%a6),%d0 # store multiplier in d0
beq.w mulu64_zero # handle zero separately
mov.l 0xc(%a6),%d1 # get multiplicand in d1
beq.w mulu64_zero # handle zero separately
#########################################################################
# 63 32 0 #
# ---------------------------- #
# | hi(mplier) * hi(mplicand)| #
# ---------------------------- #
# ----------------------------- #
# | hi(mplier) * lo(mplicand) | #
# ----------------------------- #
# ----------------------------- #
# | lo(mplier) * hi(mplicand) | #
# ----------------------------- #
# | ----------------------------- #
# --|-- | lo(mplier) * lo(mplicand) | #
# | ----------------------------- #
# ======================================================== #
# -------------------------------------------------------- #
# | hi(result) | lo(result) | #
# -------------------------------------------------------- #
#########################################################################
mulu64_alg:
mov.l %d0,%d2 # mr in d2
mov.l %d0,%d3 # mr in d3
mov.l %d1,%d4 # md in d4
swap %d3 # hi(mr) in lo d3
swap %d4 # hi(md) in lo d4
mulu.w %d1,%d0 # [1] lo(mr) * lo(md)
mulu.w %d3,%d1 # [2] hi(mr) * lo(md)
mulu.w %d4,%d2 # [3] lo(mr) * hi(md)
mulu.w %d4,%d3 # [4] hi(mr) * hi(md)
clr.l %d4 # load d4 w/ zero value
swap %d0 # hi([1]) <==> lo([1])
add.w %d1,%d0 # hi([1]) + lo([2])
addx.l %d4,%d3 # [4] + carry
add.w %d2,%d0 # hi([1]) + lo([3])
addx.l %d4,%d3 # [4] + carry
swap %d0 # lo([1]) <==> hi([1])
clr.w %d1 # clear lo([2])
clr.w %d2 # clear hi([3])
swap %d1 # hi([2]) in lo d1
swap %d2 # hi([3]) in lo d2
add.l %d2,%d1 # [4] + hi([2])
add.l %d3,%d1 # [4] + hi([3])
# 'N' CAN be set if the operation is unsigned if bit 63 is set.
mov.w MUL64_CC(%a6),%d4
andi.b &0x10,%d4 # keep old 'X' bit
tst.l %d1 # may set 'N' bit
bpl.b mulu64_ddone
ori.b &0x8,%d4 # set 'N' bit
mulu64_ddone:
mov.w %d4,%cc
mulu64_end:
exg %d1,%d0
movm.l &0x0003,([0x10,%a6]) # save result
movm.l (%sp)+,&0x001c # restore d2-d4
unlk %a6
rts
mulu64_zero:
clr.l %d0
clr.l %d1
mov.w MUL64_CC(%a6),%d4
andi.b &0x10,%d4
ori.b &0x4,%d4
mov.w %d4,%cc # set 'Z' ccode bit
bra.b mulu64_end
##########
##########
global _060LSP__imuls64_
_060LSP__imuls64_:
link.w %a6,&-4
movm.l &0x3c00,-(%sp) # save d2-d5
mov.w %cc,MUL64_CC(%a6) # save incoming ccodes
mov.l 0x8(%a6),%d0 # store multiplier in d0
beq.b mulu64_zero # handle zero separately
mov.l 0xc(%a6),%d1 # get multiplicand in d1
beq.b mulu64_zero # handle zero separately
clr.b %d5 # clear sign tag
tst.l %d0 # is multiplier negative?
bge.b muls64_chk_md_sgn # no
neg.l %d0 # make multiplier positive
ori.b &0x1,%d5 # save multiplier sgn
muls64_chk_md_sgn:
tst.l %d1 # is multiplicand negative?
bge.b muls64_alg # no
neg.l %d1 # make multiplicand positive
eori.b &0x1,%d5 # calculate correct sign
#########################################################################
# 63 32 0 #
# ---------------------------- #
# | hi(mplier) * hi(mplicand)| #
# ---------------------------- #
# ----------------------------- #
# | hi(mplier) * lo(mplicand) | #
# ----------------------------- #
# ----------------------------- #
# | lo(mplier) * hi(mplicand) | #
# ----------------------------- #
# | ----------------------------- #
# --|-- | lo(mplier) * lo(mplicand) | #
# | ----------------------------- #
# ======================================================== #
# -------------------------------------------------------- #
# | hi(result) | lo(result) | #
# -------------------------------------------------------- #
#########################################################################
muls64_alg:
mov.l %d0,%d2 # mr in d2
mov.l %d0,%d3 # mr in d3
mov.l %d1,%d4 # md in d4
swap %d3 # hi(mr) in lo d3
swap %d4 # hi(md) in lo d4
mulu.w %d1,%d0 # [1] lo(mr) * lo(md)
mulu.w %d3,%d1 # [2] hi(mr) * lo(md)
mulu.w %d4,%d2 # [3] lo(mr) * hi(md)
mulu.w %d4,%d3 # [4] hi(mr) * hi(md)
clr.l %d4 # load d4 w/ zero value
swap %d0 # hi([1]) <==> lo([1])
add.w %d1,%d0 # hi([1]) + lo([2])
addx.l %d4,%d3 # [4] + carry
add.w %d2,%d0 # hi([1]) + lo([3])
addx.l %d4,%d3 # [4] + carry
swap %d0 # lo([1]) <==> hi([1])
clr.w %d1 # clear lo([2])
clr.w %d2 # clear hi([3])
swap %d1 # hi([2]) in lo d1
swap %d2 # hi([3]) in lo d2
add.l %d2,%d1 # [4] + hi([2])
add.l %d3,%d1 # [4] + hi([3])
tst.b %d5 # should result be signed?
beq.b muls64_done # no
# -negate all bits and add 1
muls64_neg:
not.l %d0 # negate lo(result) bits
not.l %d1 # negate hi(result) bits
addq.l &1,%d0 # add 1 to lo(result)
addx.l %d4,%d1 # add carry to hi(result)
muls64_done:
mov.w MUL64_CC(%a6),%d4
andi.b &0x10,%d4 # keep old 'X' bit
tst.l %d1 # may set 'N' bit
bpl.b muls64_ddone
ori.b &0x8,%d4 # set 'N' bit
muls64_ddone:
mov.w %d4,%cc
muls64_end:
exg %d1,%d0
movm.l &0x0003,([0x10,%a6]) # save result at (a0)
movm.l (%sp)+,&0x003c # restore d2-d5
unlk %a6
rts
muls64_zero:
clr.l %d0
clr.l %d1
mov.w MUL64_CC(%a6),%d4
andi.b &0x10,%d4
ori.b &0x4,%d4
mov.w %d4,%cc # set 'Z' ccode bit
bra.b muls64_end
#########################################################################
# #
# #
# #
# 0x4(sp) = Rn #
# 0x8(sp) = pointer to boundary pair #
# #
# #
# #
#########################################################################
set CMP2_CC, -4
global _060LSP__cmp2_Ab_
_060LSP__cmp2_Ab_:
link.w %a6,&-4
movm.l &0x3800,-(%sp) # save d2-d4
mov.w %cc,CMP2_CC(%a6)
mov.l 0x8(%a6), %d2 # get regval
mov.b ([0xc,%a6],0x0),%d0
mov.b ([0xc,%a6],0x1),%d1
extb.l %d0 # sign extend lo bnd
extb.l %d1 # sign extend hi bnd
bra.w l_cmp2_cmp # go do the compare emulation
global _060LSP__cmp2_Aw_
_060LSP__cmp2_Aw_:
link.w %a6,&-4
movm.l &0x3800,-(%sp) # save d2-d4
mov.w %cc,CMP2_CC(%a6)
mov.l 0x8(%a6), %d2 # get regval
mov.w ([0xc,%a6],0x0),%d0
mov.w ([0xc,%a6],0x2),%d1
ext.l %d0 # sign extend lo bnd
ext.l %d1 # sign extend hi bnd
bra.w l_cmp2_cmp # go do the compare emulation
global _060LSP__cmp2_Al_
_060LSP__cmp2_Al_:
link.w %a6,&-4
movm.l &0x3800,-(%sp) # save d2-d4
mov.w %cc,CMP2_CC(%a6)
mov.l 0x8(%a6), %d2 # get regval
mov.l ([0xc,%a6],0x0),%d0
mov.l ([0xc,%a6],0x4),%d1
bra.w l_cmp2_cmp # go do the compare emulation
global _060LSP__cmp2_Db_
_060LSP__cmp2_Db_:
link.w %a6,&-4
movm.l &0x3800,-(%sp) # save d2-d4
mov.w %cc,CMP2_CC(%a6)
mov.l 0x8(%a6), %d2 # get regval
mov.b ([0xc,%a6],0x0),%d0
mov.b ([0xc,%a6],0x1),%d1
extb.l %d0 # sign extend lo bnd
extb.l %d1 # sign extend hi bnd
extb.l %d2 # sign extend data byte
bra.w l_cmp2_cmp # go do the compare emulation
global _060LSP__cmp2_Dw_
_060LSP__cmp2_Dw_:
link.w %a6,&-4
movm.l &0x3800,-(%sp) # save d2-d4
mov.w %cc,CMP2_CC(%a6)
mov.l 0x8(%a6), %d2 # get regval
mov.w ([0xc,%a6],0x0),%d0
mov.w ([0xc,%a6],0x2),%d1
ext.l %d0 # sign extend lo bnd
ext.l %d1 # sign extend hi bnd
ext.l %d2 # sign extend data word
bra.w l_cmp2_cmp # go emulate compare
global _060LSP__cmp2_Dl_
_060LSP__cmp2_Dl_:
link.w %a6,&-4
movm.l &0x3800,-(%sp) # save d2-d4
mov.w %cc,CMP2_CC(%a6)
mov.l 0x8(%a6), %d2 # get regval
mov.l ([0xc,%a6],0x0),%d0
mov.l ([0xc,%a6],0x4),%d1
#
# (1) save 'Z' bit from (Rn - lo)
# (2) save 'Z' and 'N' bits from ((hi - lo) - (Rn - hi))
# (3) keep 'X', 'N', and 'V' from before instruction
# (4) combine ccodes
#
l_cmp2_cmp:
sub.l %d0, %d2 # (Rn - lo)
mov.w %cc, %d3 # fetch resulting ccodes
andi.b &0x4, %d3 # keep 'Z' bit
sub.l %d0, %d1 # (hi - lo)
cmp.l %d1,%d2 # ((hi - lo) - (Rn - hi))
mov.w %cc, %d4 # fetch resulting ccodes
or.b %d4, %d3 # combine w/ earlier ccodes
andi.b &0x5, %d3 # keep 'Z' and 'N'
mov.w CMP2_CC(%a6), %d4 # fetch old ccodes
andi.b &0x1a, %d4 # keep 'X','N','V' bits
or.b %d3, %d4 # insert new ccodes
mov.w %d4,%cc # save new ccodes
movm.l (%sp)+,&0x001c # restore d2-d4
unlk %a6
rts