; Prog8 definitions for floating point handling on the CommanderX16 %option enable_floats, no_symbol_prefixing, ignore_unused %import shared_floats_functions floats { ; ---- this block contains C-64 compatible floating point related functions ---- ; ---- ROM float functions (same as on C128 except base page) ---- ; note: the fac1 and fac2 are working registers and take 6 bytes each, ; floats in memory (and rom) are stored in 5-byte MFLPT packed format. ; note: fac1/2 might get clobbered even if not mentioned in the function's name. ; note: for subtraction and division, the left operand is in fac2, the right operand in fac1. extsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 'facmo' and 'faclo', MSB FIRST. DON'T USE THIS, USE WRAPPER 'AYINT2' INSTEAD. (might throw ILLEGAL QUANTITY) ; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1 ; there is also floats.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1 ; (tip: use GIVAYFAY to use A/Y input; lo/hi switched to normal order) extsub $fe03 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y) extsub $fe06 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY extsub $fe09 = VAL_1(uword string @XY, ubyte length @A) clobbers(A,X,Y) -> float @FAC1 ; convert ASCII string in XY and length in A, to floating point in FAC1. WARNING: only implemented in ROM 47+. Safer to use floats.parse() instead. ; GETADR: fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15) ; (tip: use GETADRAY to get A/Y output; lo/hi switched to normal little endian order) extsub $fe0c = GETADR() clobbers(X) -> ubyte @ Y, ubyte @ A extsub $fe0f = FLOATC() clobbers(A,X,Y) ; convert address to floating point extsub $fe12 = FSUB(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt from A/Y - fac1 extsub $fe15 = FSUBT() clobbers(A,X,Y) ; fac1 = fac2-fac1 mind the order of the operands extsub $fe18 = FADD(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 += mflpt value from A/Y extsub $fe1b = FADDT() clobbers(A,X,Y) ; fac1 += fac2 extsub $fe1e = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y extsub $fe21 = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2 extsub $fe24 = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 extsub $fe27 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 mind the order of the operands extsub $fe2a = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log) extsub $fe2d = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to integer round instead of trunc extsub $fe30 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1) extsub $fe33 = NEGOP() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1) extsub $fe36 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** float in A/Y extsub $fe39 = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1 extsub $fe3c = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1) extsub $fe3f = COS() clobbers(A,X,Y) ; fac1 = COS(fac1) extsub $fe42 = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1) extsub $fe45 = TAN() clobbers(A,X,Y) ; fac1 = TAN(fac1) extsub $fe48 = ATN() clobbers(A,X,Y) ; fac1 = ATN(fac1) extsub $fe4b = ROUND() clobbers(A,X,Y) ; round least significant bit of fac1 extsub $fe4e = ABS() clobbers(A,X,Y) ; fac1 = ABS(fac1) extsub $fe51 = SIGN() clobbers(X,Y) -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive extsub $fe54 = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than extsub $fe57 = RND_0() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator NOTE: incompatible with C64's RND routine extsub $fe57 = RND() clobbers(A,X,Y) ; alias for RND_0 extsub $fe5a = CONUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac2 extsub $fe5d = ROMUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in current bank in A/Y into fac2 extsub $fe60 = MOVFRM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1 (use MOVFM instead) extsub $fe63 = MOVFM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1 extsub $fe66 = MOVMF(uword mflpt @ XY) clobbers(A,X,Y) ; store fac1 to memory X/Y as 5-byte mflpt extsub $fe69 = MOVFA() clobbers(A,X) ; copy fac2 to fac1 extsub $fe6c = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded the least significant bit) ; X16 additions extsub $fe6f = FADDH() clobbers(A,X,Y) ; fac1 += 0.5, for integer rounding- call this before INT extsub $fe72 = ZEROFC() clobbers(A,X,Y) ; fac1 = 0 extsub $fe75 = NORMAL() clobbers(A,X,Y) ; normalize fac1 extsub $fe78 = NEGFAC() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1) (doesn't work, juse use NEGOP() instead!) extsub $fe7b = MUL10() clobbers(A,X,Y) ; fac1 *= 10 extsub $fe7e = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive! Have to restore sign manually! extsub $fe81 = MOVEF() clobbers(A,X) ; copy fac1 to fac2 extsub $fe84 = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1) extsub $fe87 = FLOAT() clobbers(A,X,Y) ; FAC = (s8).A extsub $fe8a = FLOATS() clobbers(A,X,Y) ; FAC = (s16)facho+1:facho extsub $fe8d = QINT() clobbers(A,X,Y) ; facho:facho+1:facho+2:facho+3 = u32(FAC) extsub $fe90 = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A asmsub AYINT2() clobbers(X) -> word @AY { ; fac1-> signed word in AY. Safe wrapper around the AYINT kernal routine (not reading internal memory locations) ; (might throw ILLEGAL QUANTITY) %asm {{ jsr AYINT ldx #floats_temp_var jsr MOVMF lda floats_temp_var+4 ldy floats_temp_var+3 rts }} } asmsub FREADSA (byte value @A) clobbers(A,X,Y) { ; ---- 8 bit signed A -> float in fac1 %asm {{ tay bpl + lda #$ff jmp GIVAYF + lda #0 jmp GIVAYF }} } asmsub FREADU24AXY(ubyte lo @ A, ubyte mid @ X, ubyte hi @ Y) clobbers(A, X, Y) -> float @FAC1 { ; ---- fac1 = unsigned int24 (A/X/Y contain lo/mid/hi bytes) %asm{{ FAC = $C3 sty FAC+1 stx FAC+2 sta FAC+3 cpy #$00 bne + cpx #$00 bne + cmp #$00 beq ++ + ldx #$98 bit FAC+1 bmi + - dex asl FAC+3 rol FAC+2 rol FAC+1 bpl - + stx FAC stz FAC+4 stz FAC+5 rts }} } asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) { ; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1 ; See "basic.sym" kernal symbol file for the facmo memory location. ; TODO find a way to not depend on that internal fac memory location. MOVFM doesn't work. %asm {{ sty $c4 ; facmo sta $c5 ; facmo+1 ldx #$90 sec jmp FLOATC }} } asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) { ; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1 %asm {{ sta P8ZP_SCRATCH_B1 tya ldy P8ZP_SCRATCH_B1 jmp GIVAYF ; this uses the inverse order, Y/A }} } asmsub GETADRAY () clobbers(X) -> uword @ AY { ; ---- fac1 to unsigned word in A/Y %asm {{ jsr GETADR ; this uses the inverse order, Y/A sta P8ZP_SCRATCH_B1 tya ldy P8ZP_SCRATCH_B1 rts }} } asmsub FREADUY (ubyte value @Y) { ; -- 8 bit unsigned Y -> float in fac1 %asm {{ lda #0 jmp GIVAYF }} } asmsub parse(str value @AY) -> float @FAC1 { ; -- Parse a string value of a number to float in FAC1. ; Requires kernal R47 or newer! (depends on val_1 working) %asm {{ ldx VAL_1 cpx #$4c ; is there an implementation in VAL_1? (test for JMP) bne _borked ; no, print error message pha ; yes, count the length and call rom VAL_1. phy jsr prog8_lib.strlen tya ply plx jmp VAL_1 _borked ldy #0 - lda _msg,y beq + jsr cbm.CHROUT iny bne - + jmp sys.exit _msg .text 13,"?rom 47+ required for val1",13,0 ; !notreached! }} } sub rnd() -> float { %asm {{ lda #1 jmp RND_0 }} } asmsub normalize(float value @FAC1) -> float @ FAC1 { %asm {{ jmp floats.NORMAL }} } ; get the jiffy clock as a float asmsub time() -> float @ FAC1 { %asm {{ jsr cbm.RDTIM jmp floats.FREADU24AXY }} } %asminclude "library:c64/floats.asm" %asminclude "library:c64/floats_funcs.asm" }