; SF format is: ; ; [sign] 1.[23bits] E[8bits(n-127)] ; ; SEEEEEEE Emmmmmmm mmmmmmmm mmmmmmmm ; ; [A+0] mmmmmmmm ; [A+1] mmmmmmmm ; [A+2] Emmmmmmm ; [A+3] SEEEEEEE ; ; Special values (xxx != 0): ; ; r11 r10 r9 r8 ; [HL+3] [HL+2] [HL+1] [HL+0] ; s1111111 10000000 00000000 00000000 infinity ; s1111111 1xxxxxxx xxxxxxxx xxxxxxxx NaN ; s0000000 00000000 00000000 00000000 zero ; s0000000 0xxxxxxx xxxxxxxx xxxxxxxx denormals ; ; Note that CMPtype is "signed char" for rl78 ; #include "vregs.h" #define Z PSW.6 ; External Functions: ; ; __int_isnan [HL] -> Z if NaN ; __int_iszero [HL] -> Z if zero START_FUNC __int_isinf ;; [HL] points to value, returns Z if it's #Inf mov a, [hl+2] and a, #0x80 mov x, a mov a, [hl+3] and a, #0x7f cmpw ax, #0x7f80 skz ret ; return NZ if not NaN mov a, [hl+2] and a, #0x7f or a, [hl+1] or a, [hl] ret END_FUNC __int_isinf #define A_SIGN [hl+0] /* byte */ #define A_EXP [hl+2] /* word */ #define A_FRAC_L [hl+4] /* word */ #define A_FRAC_LH [hl+5] /* byte */ #define A_FRAC_H [hl+6] /* word or byte */ #define A_FRAC_HH [hl+7] /* byte */ #define B_SIGN [hl+8] #define B_EXP [hl+10] #define B_FRAC_L [hl+12] #define B_FRAC_LH [hl+13] #define B_FRAC_H [hl+14] #define B_FRAC_HH [hl+15] START_FUNC _int_unpack_sf ;; convert 32-bit SFmode [DE] to 6-byte struct [HL] ("A") mov a, [de+3] sar a, 7 mov A_SIGN, a movw ax, [de+2] and a, #0x7f shrw ax, 7 movw bc, ax ; remember if the exponent is all zeros subw ax, #127 ; exponent is now non-biased movw A_EXP, ax movw ax, [de] movw A_FRAC_L, ax mov a, [de+2] and a, #0x7f cmp0 c ; if the exp is all zeros, it's denormal skz or a, #0x80 mov A_FRAC_H, a mov a, #0 mov A_FRAC_HH, a ;; rounding-bit-shift movw ax, A_FRAC_L shlw ax, 1 movw A_FRAC_L, ax mov a, A_FRAC_H rolc a, 1 mov A_FRAC_H, a mov a, A_FRAC_HH rolc a, 1 mov A_FRAC_HH, a ret END_FUNC _int_unpack_sf ; func(SF a,SF b) ; [SP+4..7] a ; [SP+8..11] b START_FUNC ___subsf3 ;; a - b => a + (-b) ;; Note - we cannot just change the sign of B on the stack and ;; then fall through into __addsf3. The stack'ed value may be ;; used again (it was created by our caller after all). Instead ;; we have to allocate some stack space of our own, copy A and B, ;; change the sign of B, call __addsf3, release the allocated stack ;; and then return. subw sp, #8 movw ax, [sp+4+8] movw [sp], ax movw ax, [sp+4+2+8] movw [sp+2], ax movw ax, [sp+4+4+8] movw [sp+4], ax mov a, [sp+4+6+8] mov [sp+6], a mov a, [sp+4+7+8] xor a, #0x80 mov [sp+7], a call $!___addsf3 addw sp, #8 ret END_FUNC ___subsf3 START_FUNC ___addsf3 ;; if (isnan(a)) return a movw ax, sp addw ax, #4 movw hl, ax call !!__int_isnan bnz $1f ret_a: movw ax, [sp+4] movw r8, ax movw ax, [sp+6] movw r10, ax ret 1: ;; if (isnan (b)) return b; movw ax, sp addw ax, #8 movw hl, ax call !!__int_isnan bnz $2f ret_b: movw ax, [sp+8] movw r8, ax movw ax, [sp+10] movw r10, ax ret 2: ;; if (isinf (a)) movw ax, sp addw ax, #4 movw hl, ax call $!__int_isinf bnz $3f ;; if (isinf (b) && a->sign != b->sign) return NaN movw ax, sp addw ax, #8 movw hl, ax call $!__int_isinf bnz $ret_a mov a, [sp+7] mov h, a mov a, [sp+11] xor a, h bf a.7, $ret_a movw r8, #0x0001 movw r10, #0x7f80 ret 3: ;; if (isinf (b)) return b; movw ax, sp addw ax, #8 movw hl, ax call $!__int_isinf bz $ret_b ;; if (iszero (b)) movw ax, sp addw ax, #8 movw hl, ax call !!__int_iszero bnz $4f ;; if (iszero (a)) movw ax, sp addw ax, #4 movw hl, ax call !!__int_iszero bnz $ret_a movw ax, [sp+4] movw r8, ax mov a, [sp+7] mov h, a movw ax, [sp+10] and a, h movw r10, ax ret 4: ;; if (iszero (a)) return b; movw ax, sp addw ax, #4 movw hl, ax call !!__int_iszero bz $ret_b ; Normalize the two numbers relative to each other. At this point, ; we need the numbers converted to their "unpacked" format. subw sp, #16 ; Save room for two unpacked values. movw ax, sp movw hl, ax addw ax, #16+4 movw de, ax call $!_int_unpack_sf movw ax, sp addw ax, #8 movw hl, ax addw ax, #16+8-8 movw de, ax call $!_int_unpack_sf movw ax, sp movw hl, ax ;; diff = a.exponent - b.exponent movw ax, B_EXP ; sign/exponent word movw bc, ax movw ax, A_EXP ; sign/exponent word subw ax, bc ; a = a.exp - b.exp movw de, ax ; d = sdiff ;; if (diff < 0) diff = -diff bf a.7, $1f xor a, #0xff xor r_0, #0xff ; x incw ax ; a = diff 1: ;; if (diff >= 23) zero the smaller one cmpw ax, #24 bc $.L661 ; if a < 23 goto 661 ;; zero out the smaller one movw ax, de bt a.7, $1f ; if sdiff < 0 (a_exp < b_exp) goto 1f ;; "zero out" b movw ax, A_EXP movw B_EXP, ax movw ax, #0 movw B_FRAC_L, ax movw B_FRAC_H, ax br $5f 1: ;; "zero out" a movw ax, B_EXP movw A_EXP, ax movw ax, #0 movw A_FRAC_L, ax movw A_FRAC_H, ax br $5f .L661: ;; shift the smaller one so they have the same exponents 1: movw ax, de bt a.7, $1f cmpw ax, #0 ; sdiff > 0 bnh $1f ; if (sdiff <= 0) goto 1f decw de incw B_EXP ; because it's [HL+byte] movw ax, B_FRAC_H shrw ax, 1 movw B_FRAC_H, ax mov a, B_FRAC_LH rorc a, 1 mov B_FRAC_LH, a mov a, B_FRAC_L rorc a, 1 mov B_FRAC_L, a br $1b 1: movw ax, de bf a.7, $1f incw de incw A_EXP ; because it's [HL+byte] movw ax, A_FRAC_H shrw ax, 1 movw A_FRAC_H, ax mov a, A_FRAC_LH rorc a, 1 mov A_FRAC_LH, a mov a, A_FRAC_L rorc a, 1 mov A_FRAC_L, a br $1b 1: 5: ;; At this point, A and B have the same exponent. mov a, A_SIGN cmp a, B_SIGN bnz $1f ;; Same sign, just add. movw ax, A_FRAC_L addw ax, B_FRAC_L movw A_FRAC_L, ax mov a, A_FRAC_H addc a, B_FRAC_H mov A_FRAC_H, a mov a, A_FRAC_HH addc a, B_FRAC_HH mov A_FRAC_HH, a br $.L728 1: ;; Signs differ - A has A_SIGN still. bf a.7, $.L696 ;; A is negative, do B-A movw ax, B_FRAC_L subw ax, A_FRAC_L movw A_FRAC_L, ax mov a, B_FRAC_H subc a, A_FRAC_H mov A_FRAC_H, a mov a, B_FRAC_HH subc a, A_FRAC_HH mov A_FRAC_HH, a br $.L698 .L696: ;; B is negative, do A-B movw ax, A_FRAC_L subw ax, B_FRAC_L movw A_FRAC_L, ax mov a, A_FRAC_H subc a, B_FRAC_H mov A_FRAC_H, a mov a, A_FRAC_HH subc a, B_FRAC_HH mov A_FRAC_HH, a .L698: ;; A is still A_FRAC_HH bt a.7, $.L706 ;; subtraction was positive mov a, #0 mov A_SIGN, a br $.L712 .L706: ;; subtraction was negative mov a, #0xff mov A_SIGN, a ;; This negates A_FRAC mov a, A_FRAC_L xor a, #0xff ; XOR doesn't mess with carry add a, #1 ; INC doesn't set the carry mov A_FRAC_L, a mov a, A_FRAC_LH xor a, #0xff addc a, #0 mov A_FRAC_LH, a mov a, A_FRAC_H xor a, #0xff addc a, #0 mov A_FRAC_H, a mov a, A_FRAC_HH xor a, #0xff addc a, #0 mov A_FRAC_HH, a .L712: ;; Renormalize the subtraction mov a, A_FRAC_L or a, A_FRAC_LH or a, A_FRAC_H or a, A_FRAC_HH bz $.L728 ;; Mantissa is not zero, left shift until the MSB is in the ;; right place 1: movw ax, A_FRAC_H cmpw ax, #0x0200 bnc $.L728 decw A_EXP movw ax, A_FRAC_L shlw ax, 1 movw A_FRAC_L, ax movw ax, A_FRAC_H rolwc ax, 1 movw A_FRAC_H, ax br $1b .L728: ;; normalize A and pack it movw ax, A_FRAC_H cmpw ax, #0x01ff bnh $1f ;; overflow in the mantissa; adjust movw ax, A_FRAC_H shrw ax, 1 movw A_FRAC_H, ax mov a, A_FRAC_LH rorc a, 1 mov A_FRAC_LH, a mov a, A_FRAC_L rorc a, 1 mov A_FRAC_L, a incw A_EXP 1: call $!__rl78_int_pack_a_r8 addw sp, #16 ret END_FUNC ___addsf3 START_FUNC __rl78_int_pack_a_r8 ;; pack A to R8 movw ax, A_EXP addw ax, #126 ; not 127, we want the "bt/bf" test to check for denormals bf a.7, $1f ;; make a denormal 2: movw bc, ax movw ax, A_FRAC_H shrw ax, 1 movw A_FRAC_H, ax mov a, A_FRAC_LH rorc a, 1 mov A_FRAC_LH, a mov a, A_FRAC_L rorc a, 1 mov A_FRAC_L, a movw ax, bc incw ax bt a.7, $2b decw ax 1: incw ax ; now it's as if we added 127 movw A_EXP, ax cmpw ax, #0xfe bnh $1f ;; store #Inf instead mov a, A_SIGN or a, #0x7f mov x, #0x80 movw r10, ax movw r8, #0 ret 1: bf a.7, $1f ; note AX has EXP at top of loop ;; underflow, denormal? movw ax, A_FRAC_H shrw ax, 1 movw A_FRAC_H, ax mov a, A_FRAC_LH rorc a, 1 movw A_FRAC_LH, ax mov a, A_FRAC_L rorc a, 1 movw A_FRAC_L, ax incw A_EXP movw ax, A_EXP br $1b 1: ;; undo the rounding-bit-shift mov a, A_FRAC_L bf a.0, $1f ;; round up movw ax, A_FRAC_L addw ax, #1 movw A_FRAC_L, ax bnc $1f incw A_FRAC_H ;; If the rounding set the bit beyond the end of the fraction, increment the exponent. mov a, A_FRAC_HH bf a.1, $1f incw A_EXP 1: movw ax, A_FRAC_H shrw ax, 1 movw A_FRAC_H, ax mov a, A_FRAC_LH rorc a, 1 mov A_FRAC_LH, a mov a, A_FRAC_L rorc a, 1 mov A_FRAC_L, a movw ax, A_FRAC_L movw r8, ax or a, x or a, A_FRAC_H or a, A_FRAC_HH bnz $1f movw ax, #0 movw A_EXP, ax 1: mov a, A_FRAC_H and a, #0x7f mov b, a mov a, A_EXP shl a, 7 or a, b mov r10, a mov a, A_SIGN and a, #0x80 mov b, a mov a, A_EXP shr a, 1 or a, b mov r11, a ret END_FUNC __rl78_int_pack_a_r8 START_FUNC ___mulsf3 ;; if (isnan(a)) return a movw ax, sp addw ax, #4 movw hl, ax call !!__int_isnan bnz $1f mret_a: movw ax, [sp+4] movw r8, ax mov a, [sp+11] and a, #0x80 mov b, a movw ax, [sp+6] xor a, b ; sign is always a ^ b movw r10, ax ret 1: ;; if (isnan (b)) return b; movw ax, sp addw ax, #8 movw hl, ax call !!__int_isnan bnz $1f mret_b: movw ax, [sp+8] movw r8, ax mov a, [sp+7] and a, #0x80 mov b, a movw ax, [sp+10] xor a, b ; sign is always a ^ b movw r10, ax ret 1: ;; if (isinf (a)) return (b==0) ? nan : a movw ax, sp addw ax, #4 movw hl, ax call $!__int_isinf bnz $.L805 movw ax, sp addw ax, #8 movw hl, ax call !!__int_iszero bnz $mret_a movw r8, #0x0001 ; return NaN movw r10, #0x7f80 ret .L805: ;; if (isinf (b)) return (a==0) ? nan : b movw ax, sp addw ax, #8 movw hl, ax call $!__int_isinf bnz $.L814 movw ax, sp addw ax, #4 movw hl, ax call !!__int_iszero bnz $mret_b movw r8, #0x0001 ; return NaN movw r10, #0x7f80 ret .L814: movw ax, sp addw ax, #4 movw hl, ax call !!__int_iszero bz $mret_a movw ax, sp addw ax, #8 movw hl, ax call !!__int_iszero bz $mret_b ;; at this point, we're doing the multiplication. subw sp, #16 ; save room for two unpacked values movw ax, sp movw hl, ax addw ax, #16+4 movw de, ax call $!_int_unpack_sf movw ax, sp addw ax, #8 movw hl, ax addw ax, #16+8-8 movw de, ax call $!_int_unpack_sf movw ax, sp movw hl, ax ;; multiply SI a.FRAC * SI b.FRAC to DI r8 subw sp, #16 movw ax, A_FRAC_L movw [sp+0], ax movw ax, A_FRAC_H movw [sp+2], ax movw ax, B_FRAC_L movw [sp+8], ax movw ax, B_FRAC_H movw [sp+10], ax movw ax, #0 movw [sp+4], ax movw [sp+6], ax movw [sp+12], ax movw [sp+14], ax call !!___muldi3 ; MTMPa * MTMPb -> R8..R15 addw sp, #16 movw ax, sp movw hl, ax ;; add the exponents together movw ax, A_EXP addw ax, B_EXP movw bc, ax ; exponent in BC ;; now, re-normalize the DI value in R8..R15 to have the ;; MSB in the "right" place, adjusting BC as we shift it. ;; The value will normally be in this range: ;; R15 R8 ;; 0001_0000_0000_0000 ;; 0003_ffff_fc00_0001 ;; so to speed it up, we normalize to: ;; 0001_xxxx_xxxx_xxxx ;; then extract the bytes we want (r11-r14) 1: mov a, r15 cmp0 a bnz $2f mov a, r14 and a, #0xfe bz $1f 2: ;; shift right, inc exponent movw ax, r14 shrw ax, 1 movw r14, ax mov a, r13 rorc a, 1 mov r13, a mov a, r12 rorc a, 1 mov r12, a mov a, r11 rorc a, 1 mov r11, a ;; we don't care about r8/r9/r10 if we're shifting this way incw bc br $1b 1: mov a, r15 or a, r14 bnz $1f ;; shift left, dec exponent movw ax, r8 shlw ax, 1 movw r8, ax movw ax, r10 rolwc ax, 1 movw r10, ax movw ax, r12 rolwc ax, 1 movw r12, ax movw ax, r14 rolwc ax, 1 movw r14, ax decw bc br $1b 1: ;; at this point, FRAC is in R11..R14 and EXP is in BC movw ax, bc movw A_EXP, ax mov a, r11 mov A_FRAC_L, a mov a, r12 mov A_FRAC_LH, a mov a, r13 mov A_FRAC_H, a mov a, r14 mov A_FRAC_HH, a mov a, A_SIGN xor a, B_SIGN mov A_SIGN, a call $!__rl78_int_pack_a_r8 addw sp, #16 ret END_FUNC ___mulsf3 START_FUNC ___divsf3 ;; if (isnan(a)) return a movw ax, sp addw ax, #4 movw hl, ax call !!__int_isnan bnz $1f dret_a: movw ax, [sp+4] movw r8, ax mov a, [sp+11] and a, #0x80 mov b, a movw ax, [sp+6] xor a, b ; sign is always a ^ b movw r10, ax ret 1: ;; if (isnan (b)) return b; movw ax, sp addw ax, #8 movw hl, ax call !!__int_isnan bnz $1f dret_b: movw ax, [sp+8] movw r8, ax mov a, [sp+7] and a, #0x80 mov b, a movw ax, [sp+10] xor a, b ; sign is always a ^ b movw r10, ax ret 1: ;; if (isinf (a)) return isinf(b) ? nan : a movw ax, sp addw ax, #4 movw hl, ax call $!__int_isinf bnz $1f movw ax, sp addw ax, #8 movw hl, ax call $!__int_isinf bnz $dret_a dret_nan: movw r8, #0x0001 ; return NaN movw r10, #0x7f80 ret 1: ;; if (iszero (a)) return iszero(b) ? nan : a movw ax, sp addw ax, #4 movw hl, ax call !!__int_iszero bnz $1f movw ax, sp addw ax, #8 movw hl, ax call !!__int_iszero bnz $dret_a br $dret_nan 1: ;; if (isinf (b)) return 0 movw ax, sp addw ax, #8 movw hl, ax call $!__int_isinf bnz $1f mov a, [sp+7] mov b, a mov a, [sp+11] xor a, b and a, #0x80 mov r11, a movw r8, #0 mov r10, #0 ret 1: ;; if (iszero (b)) return Inf movw ax, sp addw ax, #8 movw hl, ax call !!__int_iszero bnz $1f mov a, [sp+7] mov b, a mov a, [sp+11] xor a, b or a, #0x7f mov r11, a movw r8, #0 mov r10, #0x80 ret 1: ;; at this point, we're doing the division. Normalized ;; mantissas look like: ;; 01.xx.xx.xx ;; so we divide: ;; 01.xx.xx.xx.00.00.00.00 ;; by 01.xx.xx.xx ;; to get approx 00.80.00.00.00 to 01.ff.ff.ff.00 subw sp, #16 ; save room for two unpacked values movw ax, sp movw hl, ax addw ax, #16+4 movw de, ax call $!_int_unpack_sf movw ax, sp addw ax, #8 movw hl, ax addw ax, #16+8-8 movw de, ax call $!_int_unpack_sf movw ax, sp movw hl, ax ;; divide DI a.FRAC / SI b.FRAC to DI r8 subw sp, #16 movw ax, A_FRAC_L movw [sp+4], ax movw ax, A_FRAC_H movw [sp+6], ax movw ax, B_FRAC_L movw [sp+8], ax movw ax, B_FRAC_H movw [sp+10], ax movw ax, #0 movw [sp+0], ax movw [sp+2], ax movw [sp+12], ax movw [sp+14], ax call !!___divdi3 ; MTMPa / MTMPb -> R8..R15 addw sp, #16 movw ax, sp movw hl, ax ;; subtract the exponents A - B movw ax, A_EXP subw ax, B_EXP movw bc, ax ; exponent in BC ;; now, re-normalize the DI value in R8..R15 to have the ;; MSB in the "right" place, adjusting BC as we shift it. ;; The value will normally be in this range: ;; R15 R8 ;; 0000_0000_8000_0000 ;; 0000_0001_ffff_ff00 ;; so to speed it up, we normalize to: ;; 0000_0001_xxxx_xxxx ;; then extract the bytes we want (r9-r12) 1: movw ax, r14 cmpw ax, #0 bnz $2f movw ax, r12 cmpw ax, #1 bnh $1f 2: ;; shift right, inc exponent movw ax, r14 shrw ax, 1 movw r14, ax mov a, r13 rorc a, 1 mov r13, a mov a, r12 rorc a, 1 mov r12, a mov a, r11 rorc a, 1 mov r11, a mov a, r10 rorc a, 1 mov r10, a mov a, r9 rorc a, 1 mov r9, a mov a, r8 rorc a, 1 mov r8, a incw bc br $1b 1: ;; the previous loop leaves r15.r13 zero mov a, r12 cmp0 a bnz $1f ;; shift left, dec exponent movw ax, r8 shlw ax, 1 movw r8, ax movw ax, r10 rolwc ax, 1 movw r10, ax movw ax, r12 rolwc ax, 1 movw r12, ax ;; don't need to do r14 decw bc br $1b 1: ;; at this point, FRAC is in R8..R11 and EXP is in BC movw ax, bc movw A_EXP, ax mov a, r9 mov A_FRAC_L, a mov a, r10 mov A_FRAC_LH, a mov a, r11 mov A_FRAC_H, a mov a, r12 mov A_FRAC_HH, a mov a, A_SIGN xor a, B_SIGN mov A_SIGN, a call $!__rl78_int_pack_a_r8 addw sp, #16 ret END_FUNC ___divsf3