1867
RET_MATH_LIB: call COPY_TO.TMP_DAC
1871
MATH_DECADD: ld ix, addSingle
1874
MATH_DECSUB: ld ix, subSingle
1877
MATH_DECMUL: ld ix, mulSingle
1880
MATH_DECDIV: ld ix, divSingle
1884
MATH_SNGEXP: ld ix, powSingle
1895
MATH_SQR: ld ix, sqrtSingle
1908
MATH_RND: ld ix, randSingle
1911
MATH_FRCINT: ld ix, single2Int
1924
MATH_FRCDBL: ; same as MATH_FRCSGL
1925
MATH_FRCSGL: jp int2Single
1927
MATH_ICOMP: ld a, h ; cp hl, de (or use bios DCOMPR)
1929
jr nz, MATH_ICOMP.NE
1932
jr nz, MATH_ICOMP.NE
1934
MATH_ICOMP.NE: jr c, MATH_DCOMP.LT
1937
MATH_XDCOMP: ; same as MATH_DCOMP
1938
MATH_DCOMP: ld ix, cmpSingle
1942
MATH_DCOMP.GT: ld a, 1 ; DAC > ARG
1944
MATH_DCOMP.EQ: ld a, 0 ; DAC = ARG
1946
MATH_DCOMP.LT: ld a, 0xFF ; DAC < ARG
1950
MATH_FIN: ; HL has the source string
1951
ld a, (BASIC_VALTYP)
1952
cp 2 ; test if integer
1954
ld hl, (BASIC_DAC+2)
1959
MATH_FIN.1: ld BC, BASIC_DAC
1963
MATH_FOUT: ld a, (BASIC_VALTYP)
1964
cp 2 ; test if integer
1966
ld hl, (BASIC_DAC+2)
1971
MATH_FOUT.1: ld hl, BASIC_DAC
1980
;---------------------------------------------------------------------------------------------------------
1982
; Copyright 2018 Zeda A.K. Thomas
1983
;---------------------------------------------------------------------------------------------------------
1985
; https://github.com/Zeda/z80float
1986
; https://www.omnimaga.org/asm-language/(z80)-floating-point-routines/
1987
; https://en.wikipedia.org/wiki/Single-precision_floating-point_format
1988
;---------------------------------------------------------------------------------------------------------
1990
; HL points to the first operand
1991
; DE points to the second operand (if needed)
1992
; IX points to the third operand (if needed, rare)
1993
; BC points to where the result should be output
1994
; Floats are stored by a little-endian 24-bit mantissa. However, the highest bit
1995
; is taken as implicitly 1, so we replace it as a sign bit. Next comes an 8-bit
1996
; exponent biased by +128.
1997
;---------------------------------------------------------------------------------------------------------
1998
; Adapted to MSXBas2Asm by Amaury Carvalho, 2019
1999
;---------------------------------------------------------------------------------------------------------
2001
;---------------------------------------------------------------------------------------------------------
2003
;---------------------------------------------------------------------------------------------------------
2005
BASIC_HOLD8: equ 0xF806 ; 48 Work area for decimal multiplications.
2006
BASIC_HOLD2: equ 0xF836 ; 8 Work area in the execution of numerical operators.
2007
BASIC_HOLD: equ 0xF83E ; 8 Work area in the execution of numerical operators.
2008
scrap: equ BASIC_HOLD8
2009
seed0: equ BASIC_RNDX
2010
seed1: equ seed0 + 4
2011
var48: equ scrap + 4
2014
addend2: equ scrap+7 ;4 bytes
2015
var_x: equ BASIC_HOLD ;4 bytes
2016
var_c: equ var_x + 4 ;4 bytes
2017
pow10exp_single: equ scrap+9
2018
strout_single: equ pow10exp_single+2
2020
;---------------------------------------------------------------------------------------------------------
2022
;---------------------------------------------------------------------------------------------------------
2024
;;Still need to tend to special cases
2092
pop hl ;bigger float
2224
;;Need to adjust sign flag
2247
;;How many push/pops are needed?
2255
;;How many push/pops are needed?
2261
;;How many push/pops are needed?
2262
;;Return bigger number
2269
;---------------------------------------------------------------------------------------------------------
2271
;---------------------------------------------------------------------------------------------------------
2294
jp addInject ;jumps in to the addSingle routine
2296
;---------------------------------------------------------------------------------------------------------
2298
;---------------------------------------------------------------------------------------------------------
2301
;Inputs: HL points to float1, DE points to float2, BC points to where the result is copied
2302
;Outputs: float1*float2 is stored to (BC)
2303
;573+mul24+{0,35}+{0,30}
2306
;avg: 2055.13839751681cc
2332
;;return float in CHLB
2342
jr z,mulSingle_case0
2353
jr z,mulSingle_case1
2356
rra ; |Lots of help from Runer112 and
2357
adc a,a ; |calc84maniac for optimizing
2358
jp po,bad ; |this exponent check.
2367
call mul24 ;BDE*CHL->HLBCDE, returns sign info
2423
;special*x = special
2444
;basically, if b|c has bit 5 set, return NaN
2477
;;avg :1464.9033203125cc (1464+925/1024)
2480
;avg: 1449.63839751681cc
2521
;---------------------------------------------------------------------------------------------------------
2523
;---------------------------------------------------------------------------------------------------------
2526
;;HL points to numerator
2527
;;DE points to denominator
2528
;;BC points to where the quotient gets written
2530
divSingle_no_pushpop:
2536
xor (hl) ; |Get sign of output
2543
ex de,hl ; |Get exponent
2650
call divsub1 ;34 or 66
2668
;34cc or 66cc or 93cc
2683
;---------------------------------------------------------------------------------------------------------
2685
;---------------------------------------------------------------------------------------------------------
2691
;;BC points to output
2708
; 2^x = 1.000000001752 + x * (0.693146989552 + x * (0.2402298085906 + x * (5.54833215071e-2 + x * (9.67907584392e-3 + x * (1.243632065103e-3 + x * 2.171671843714e-4)))))
2709
;Please note that usually I like to reduce to [-.5,.5] as the extra overhead is usually worth it.
2710
;In this case, our polynomial is the same degree, with error different by less than 1 bit, so it's just a waste to range-reduce in this way.
2713
;x-=int(x) ;leaves x in [0,1)
2715
;;if x==inf -> out==inf
2716
;;if x==-inf -> out==0
2717
;;if x==NAN -> out==NAN
2724
push af ;keep track of sign
2734
jr c,_pow_1 ;int(x)=0
2747
jr nz,exp_normalized
2758
jr exp_normalized ;.db $11 ;start of `ld de,**`
2765
jr comp_exp ;.db $06 ;start of 'ld b,*` just to eat the next byte
2774
jp z,exp_underflow+1
2775
;perform 1-(var48+10)--> var48+10
2783
;our 'x' is at var48+10
2784
;our `temp` is at var48+6 so as not to cause issues with mulSingle)
2785
;uses 14 bytes of RAM
2827
;-inf -> +0 because lim approaches 0 from the right
2849
;-inf -> +0 because lim approaches 0 from the right
2851
sbc a,a ;FF if should be 0,
2864
;---------------------------------------------------------------------------------------------------------
2866
;---------------------------------------------------------------------------------------------------------
2868
;Uses 3 bytes at scrap
2870
;552+{0,19}+8{0,3+{0,3}}+pushpop+sqrtHLIX
2889
jp z,sqrtSingle_special
2892
push af ;new exponent
2902
;AHL is the new remainder
2903
;Need to divide by 2, then divide by the 16-bit (var_x+4)
2907
;We are just going to approximate it
2989
;Output: DE is the sqrt, AHL is the remainder
2990
;speed: 754+{0,1}+6{0,6}+{0,3+{0,18}}+{0,38}+sqrtHL
3014
jr _15a ;.db $FE ;start of `cp *`
3028
jr _16a ;.db $FE ;start of `cp *`
3042
jr _17a ;.db $FE ;start of `cp *`
3056
jr _18a ;.db $FE ;start of `cp *`
3060
;Now we have four more iterations
3061
;The first two are no problem
3073
jr _19a ;.db $FE ;start of `cp *`
3087
jr _20a ;.db $FE ;start of `cp *`
3092
;On the next iteration, HL might temporarily overflow by 1 bit
3094
rl d ;sla e \ rl d \ inc e
3098
adc hl,hl ;This might overflow!
3099
jr c,sqrt32_iter15_br0
3112
;On the next iteration, HL is allowed to overflow, DE could overflow with our current routine, but it needs to be shifted right at the end, anyways
3115
ld b,a ;either 0x00 or 0x80
3136
;returns A as the sqrt, HL as the remainder, D = 0
3150
jr _23a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
3161
jr _24a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
3172
dec d ;this resets the low bit of D, so `srl d` resets carry.
3173
jr _25a ;.db $06 ;start of ld b,* which is 7cc to skip the next byte.
3195
jr _27a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
3208
jr _28a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
3228
;---------------------------------------------------------------------------------------------------------
3230
;---------------------------------------------------------------------------------------------------------
3234
;---------------------------------------------------------------------------------------------------------
3236
;---------------------------------------------------------------------------------------------------------
3240
;---------------------------------------------------------------------------------------------------------
3242
;---------------------------------------------------------------------------------------------------------
3247
;;BC points to the output
3252
;;DE points to lg(y), HL points to x, BC points to output
3259
;---------------------------------------------------------------------------------------------------------
3261
;---------------------------------------------------------------------------------------------------------
3265
;---------------------------------------------------------------------------------------------------------
3267
;---------------------------------------------------------------------------------------------------------
3271
;---------------------------------------------------------------------------------------------------------
3273
;---------------------------------------------------------------------------------------------------------
3277
;---------------------------------------------------------------------------------------------------------
3279
;---------------------------------------------------------------------------------------------------------
3283
;---------------------------------------------------------------------------------------------------------
3285
;---------------------------------------------------------------------------------------------------------
3288
;Input: DE points to float1, HL points to float2
3290
; float1 >= float2 : nc
3291
; float1 < float2 : c,nz
3292
; float1 == float2 : z
3293
; There is a margin of error allowed in the lower 2 bits of the mantissa.
3295
;Currently fails when both numbers have magnitude less than about 2^-106
3325
ld a,(scrap+3) ;new power
3326
pop bc ;B is old power
3336
or 1 ;not equal, so reset z flag
3337
rla ;if negative, float1<float2, setting c flag as wanted, else nc.
3343
;---------------------------------------------------------------------------------------------------------
3345
;---------------------------------------------------------------------------------------------------------
3348
;Stores a pseudo-random number on [0,1)
3349
;it won't produce values on (0,2^-23)
3358
;DEHL is the mantissa, B is the exponent
3374
;If we needed to shift more than 8 bits, we'll load in more random data
3379
jp nc,rand_no_more_rand_data
3387
rand_no_more_rand_data:
3406
;;Tested and passes all CAcert tests
3407
;;Uses a very simple 32-bit LCG and 32-bit LFSR
3408
;;it has a period of 18,446,744,069,414,584,320
3409
;;roughly 18.4 quintillion.
3410
;;LFSR taps: 0,2,6,7 = 11000101
3412
;;Thanks to Runer112 for his help on optimizing the LCG and suggesting to try the much simpler LCG. On their own, the two are terrible, but together they are great.
3413
;Uses 64 bits of state
3447
;---------------------------------------------------------------------------------------------------------
3449
; HL = Single address
3450
; BC = String address
3451
; http://0x80.pl/notesen/2015-12-29-float-to-string.html
3452
; http://0x80.pl/articles/convert-float-to-integer.html
3453
;---------------------------------------------------------------------------------------------------------
3467
; Move the float to scrap
3471
; Make the float negative, write a '-' if already negative
3485
; Check if the exponent field is 0 (a special value)
3492
; We should write '0' next. When rounding 9.999999... for example, not padding with a 0 will return '.' instead of '1.'
3500
; Now we need to perform signed (A-128)*77 (approximation of exponent*log10(2))
3508
ld (pow10exp_single),a ;The base-10 exponent
3512
ld de,pow10LUT ;get the table of 10^-(2^k)
3515
call singletostr_mul
3516
call singletostr_mul
3517
call singletostr_mul
3518
call singletostr_mul
3519
call singletostr_mul
3520
call singletostr_mul
3521
;now the number is pretty close to a nice value
3523
; If it is less than 1, multiply by 10
3528
;ld hl,scrap ;Since singletostr_mul returns BC = scrap, can do this cheaper
3534
ld hl,pow10exp_single
3540
; Convert to a fixed-point number !
3554
;We need to get 7 digits
3556
pop hl ;Points to the string
3558
;The first digit can be as large as 20, so it'll actually be two digits
3562
;Increment the exponent :)
3563
ld de,(pow10exp_single-1)
3565
ld (pow10exp_single-1),de
3574
; Get the remaining digits.
3581
call singletostrmul10
3586
;Save the pointer to the end of the string
3593
jr c,rounding_done_single
3594
jr _40a ;.db $DA ;start of `jp c,*` in order to skip the next instruction
3603
rounding_done_single:
3606
;Strip the leading zero if it exists (rounding may have bumped this to `1`)
3618
;Now lets move HL-DE bytes at DE+1 to DE
3630
;If z flag is reset, this means that the exponent should be bumped up 1
3631
ld a,(pow10exp_single)
3634
ld (pow10exp_single),a
3637
;if -4<=A<=6, then need to insert the decimal place somewhere.
3642
;for this, we need to insert the decimal after the first digit
3643
;Then, we need to append the exponent string
3645
ld de,strout_single-1
3647
cp $1A ;negative sign
3655
;remove any stray zeroes at the end before appending the exponent
3659
; Write the exponent
3662
ld a,(pow10exp_single)
3665
ld (hl),$1A ;negative sign
3682
ld hl,strout_single-1
3685
ld a,(pow10exp_single)
3689
;need to put zeroes before everything
3692
cp $1A ;negative sign
3718
ld de,strout_single-1
3722
cp $1A ;negative sign
3733
ld hl,strout_single-1
3750
;multiply the 0.24 fixed point number at scrap by 10
3751
;overflow in A register
3786
;Check that the last digit isn't a decimal!
3836
;---------------------------------------------------------------------------------------------------------
3838
; https://www.ticalc.org/pub/86/asm/source/routines/atof.asm
3839
;---------------------------------------------------------------------------------------------------------
3846
;;otherwise, char_TI_CHR
3851
ptr_sto: equ scrap+9
3853
;;#Routines/Single Precision
3855
;; HL points to the string
3856
;; BC points to where the float is output
3858
;; scrap+9 is the pointer to the end of the string
3860
;; 11 bytes at scrap ?
3863
;Check if there is a negative sign.
3872
;Skip all leading zeroes
3875
jr z,$-4 ;jumps back to the `inc hl`
3878
;Check if the next char is char_DEC
3880
or a ;to reset the carry flag
3882
jr _54a ;.db $FE ;start of cp *
3889
jr z,$-5 ;jumps back to the `dec b`
3892
;Now we read in the next 8 digits
3898
;Now `scrap` holds the 4-digit base-100 number.
3900
;if carry flag is set, just need to get rid of remaining digits
3901
;Otherwise, need to get rid of remaining digits, while incrementing the exponent
3912
jp z,strToSingle_inf
3915
;Now check for engineering `E` to modify the exponent
3919
;Gotta multiply the number at (scrap) by 2^24
3922
call scrap_times_256
3925
call scrap_times_256
3928
call scrap_times_256
3931
call scrap_times_256
3934
;Now scrap+3 is a 4-byte mantissa that needs to be normalized
3942
jp z,strToSingle_zero-1
3946
jp m,strToSingle_normed
3947
;Will need to iterate at most three times
3960
;Move the number to scrap
3969
;now (scrap) is our number, need to multiply by power of 10!
3970
;Power of 10 is stored in B, need to put in A first
3978
jp nc,strToSingle_inf+1
3981
jp nc,strToSingle_zero
4005
cp char_NEG ;negative exponent?
4057
call scrap_times_sub
4070
jr nz,strToSingle_inf
4086
;---------------------------------------------------------------------------------------------------------
4088
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
4089
;---------------------------------------------------------------------------------------------------------
4092
ld hl, (BASIC_DAC+2) ; convert integer DAC parameter to single float
4093
ld e, 0 ; digits count
4097
push af ; save sign (0=positive, 0x80=negative)
4098
jr z, int2Single.div10
4105
call div_hl_c ; get next digit
4106
push af ; save digit
4110
jr nz, int2Single.div10
4112
jr nz, int2Single.div10
4114
int2Single.normalize:
4115
ld b, e ; digits count
4123
int2Single.normalize.1:
4124
pop af ; restore next digit
4128
djnz int2Single.normalize.1
4131
and 0x7f ; turn off upper bit
4134
pop af ; restore sign
4136
ld e, a ; into upper mantissa
4138
exx ; restore exponent count
4143
or 0x80 ; exponent bias
4148
ld (ix), l ; mantissa
4149
ld (ix+1), h ; mantissa
4150
ld (ix+2), e ; sign + mantissa
4151
ld (ix+3), d ; expoent
4158
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
4188
djnz div_dehl_c.loop
4192
;---------------------------------------------------------------------------------------------------------
4194
; http://0x80.pl/articles/convert-float-to-integer.html
4195
;---------------------------------------------------------------------------------------------------------
4198
; HL points to the single-precision float
4200
; HL is the 16-bit signed integer part of the float
4218
jr c,no_shift_single_to_int16
4220
jr nc,no_shift_single_to_int16
4242
jr _67a ;.db $11 ;start of ld de,*
4254
no_shift_single_to_int16:
4275
;---------------------------------------------------------------------------------------------------------
4276
; Auxiliary routines
4277
;---------------------------------------------------------------------------------------------------------
4284
const_pi: db $DB,$0F,$49,$81
4285
const_e: db $54,$f8,$2d,$81
4286
const_lg_e: db $3b,$AA,$38,$80
4287
const_ln_2: db $18,$72,$31,$7f
4288
const_log2: db $9b,$20,$1a,$7e
4289
const_lg10: db $78,$9a,$54,$81
4290
const_0: db $00,$00,$00,$00
4291
const_1: db $00,$00,$00,$80
4292
const_inf: db $00,$00,$40,$00
4293
const_NegInf: db $00,$00,$C0,$00
4294
const_NaN: db $00,$00,$20,$00
4295
const_log10_e: db $D9,$5B,$5E,$7E
4296
const_2pi: db $DB,$0F,$49,$82
4297
const_2pi_inv: db $83,$F9,$22,$7D
4298
const_p25: db $00,$00,$00,$7E
4299
const_p5: db $00,$00,$00,$7F
4302
sin_a1: db $A4,$AA,$2A,$7D ;a1= 2^-3 * 11184804/2^23
4303
sin_a2: db $AC,$83,$08,$79 ;a2= 2^-7 * 8946604/2^23
4304
sin_a3: db $11,$97,$4C,$73 ;a3=2^-13 * 13408017/2^23
4305
cos_a1: db $DA,$FF,$7F,$7E ;a1=2^-2 * 16777178/2^23
4306
cos_a2: db $5C,$9F,$2A,$7B ;a2=2^-5 * 11181916/2^23
4307
cos_a3: db $52,$26,$32,$76 ;a3=2^-10* 11675218/2^23
4308
exp_a1: db $15,$72,$31,$7F ;.693146989552
4309
exp_a2: db $CE,$FE,$75,$7D ;.2402298085906
4310
exp_a3: db $7B,$42,$63,$7B ;.0554833215071
4311
exp_a4: db $FD,$94,$1E,$79 ;.00967907584392
4312
exp_a5: db $5E,$01,$23,$76 ;.001243632065103
4313
exp_a6: db $5F,$B7,$63,$73 ;.0002171671843714
4314
const_1p40625: db $00,$00,$34,$80 ;1.40625
4322
;A is the constant ID#
4323
;returns nc if failed, c otherwise
4324
;HL points to the constant
4325
cp (end_const-start_const)>>2
4332
;#if ((end_const-4)>>8)!=(start_const>>8)
4343
db $CD,$CC,$4C,$7C ;.1
4344
db $0A,$D7,$23,$79 ;.01
4345
db $17,$B7,$51,$72 ;.0001
4346
db $77,$CC,$2B,$65 ;10^-8
4347
db $95,$95,$66,$4A ;10^-16
4348
db $1F,$B1,$4F,$15 ;10^-32
4351
db $00,$00,$20,$83 ;10
4352
db $00,$00,$48,$86 ;100
4353
db $00,$40,$1C,$8D ;10000
4354
db $20,$BC,$3E,$9A ;10^8
4355
db $CA,$1B,$0E,$B5 ;10^16
4356
db $AE,$C5,$1D,$EA ;10^32
4363
;C>=128 135+6{0,33+{0,1}}+{0,20+{0,8}}
4364
;C>=64 115+5{0,33+{0,1}}+{0,20+{0,8}}
4365
;C>=32 95+4{0,33+{0,1}}+{0,20+{0,8}}
4366
;C>=16 75+3{0,33+{0,1}}+{0,20+{0,8}}
4367
;C>=8 55+2{0,33+{0,1}}+{0,20+{0,8}}
4368
;C>=4 35+{0,33+{0,1}}+{0,20+{0,8}}
4369
;C>=2 15+{0,20+{0,8}}
4372
;avg: 349.21279907227cc
4463
;26 bytes, adds 118cc to the traditional routine
4496
;c flag means don't increment the exponent
4499
jr c,ascii_to_uint8_noexp
4501
jr z,ascii_to_uint8_noexp-2
4505
jr nc,ascii_to_uint8_noexp_end
4517
jr z,ascii_to_uint8_noexp_2nd
4521
jr nc,ascii_to_uint8_noexp_end
4532
ascii_to_uint8_noexp:
4535
jr nc,ascii_to_uint8_noexp_end
4542
ascii_to_uint8_noexp_2nd:
4547
jr nc,ascii_to_uint8_noexp_end
4550
jr ascii_2 ;.db $FE ;start of `cp **`, saves 1cc
4551
ascii_to_uint8_noexp_end:
4559
; --------------------------------------------------------------
4560
; Converts a signed integer value to a zero-terminated ASCII
4561
; string representative of that value (using radix 10).
4563
; Brandon Wilson WikiTI
4564
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Other:DispA#Decimal_Signed_Version
4565
; --------------------------------------------------------------
4567
; HL Value to convert (two's complement integer).
4568
; DE Base address of string destination. (pointer).
4569
; --------------------------------------------------------------
4572
; --------------------------------------------------------------
4573
; REGISTERS/MEMORY DESTROYED
4575
; --------------------------------------------------------------
4581
; Detect sign of HL.
4585
; HL is negative. Output '-' to string and negate HL.
4590
; Negate HL (using two's complement)
4594
ld a, 0 ; Note that XOR A or SUB A would disturb CF
4598
; Convert HL to digit characters
4600
ld b, 0 ; B will count character length of number
4603
call div_hl_c; HL = HL / A, A = remainder
4610
; Retrieve digits from stack
4618
; Terminate string with NULL
4627
; return remainder in a
4628
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division
4645
;===============================================================
4646
; Convert a string of base-10 digits to a 16-bit value.
4647
; http://z80-heaven.wikidot.com/math#toc32
4649
; DE points to the base 10 number string in RAM.
4651
; HL is the 16-bit value of the number
4652
; DE points to the byte after the number
4657
; A (actually, add 30h and you get the ending token)
4660
; n is the number of digits
4662
; at most 595 cycles for any 16-bit decimal value
4663
;===============================================================
4666
ld hl,0 ; 10 : 210000
4683
jr nc,ConvLoop ;12|23: 30EE
4685
jr ConvLoop ; --- : 18EB
1924
4690
;--------------------------------------------------------
1926
4692
ds romSize-(romPad-pgmArea),0