;;; LIBBASIC64.OPH

;;; This is a collection of routines inside the BASIC ROM that can
;;; be repurposed to do floating-point math inside your machine
;;; language programs. It is currently VERY EXPERIMENTAL. The documentation
;;; available for this is spotty at best and disassembly confirms that
;;; a lot of hidden invariants may lurk.

;;; There's a general TODO here to generate a relatively safe and
;;; easy to use set of macros that will do more or less what you want.

	;; BASIC functions
	.alias	abs_fac1	$bc58
	.alias	atn_fac1	$e30e
	.alias	cos_fac1	$e264
	.alias	exp_fac1	$bfed
	.alias	int_fac1	$bccc
	.alias	log_fac1	$b9ea
	.alias	rnd_fac1	$e097
	.alias	sgn_fac1	$bc39
	.alias	sin_fac1	$e26b
	.alias	tan_fac1	$e2b4

	;; Getting data in and out of the FACs
	.alias	ld_fac1_a	$bc3c
	.alias	ld_fac1_s16	$b391
	.alias	ld_fac1_mem	$bba2
	.alias	ld_fac1_fac2	$bbfc
	.alias	fac1_to_string	$bddd
	.alias	fac1_to_s32	$bc9b
	.alias	fac1_to_57	$bbca
	.alias	fac1_to_5c	$bbc7

	.alias	ld_fac2_mem	$ba8c
	.alias	ld_fac2_fac1	$bc0c

	;; Unlike sgn_fac1, this returns the -1/0/1 in
	;; the accumulator
	.alias	fac1_sign	$bc2b

	;; FP operators. These are all FAC2 OP FAC1
	;; with the result in FAC1.
	;; PRECONDITIONS: All of these operations but AND and
	;; OR require you to have the contents of $61 in the
	;; accumulator. calling one of the ld_fac* routines
	;; will do that for you automatically. f_add_op also
	;; requires that $6F be set properly; only ld_fac2_mem
	;; does this.
	.alias	f_add_op	$b86a
	.alias	f_subtract_op	$b853
	.alias	f_multiply_op	$ba2b
	.alias	f_divide_op	$bb12
	.alias	f_pow_op	$bf7b
	.alias	f_and_op	$afe9
	.alias	f_or_op		$afe6

	;; Memory-based FP operations. All are MEM OP FAC1.
	;; These are usually safer than the *_op routines.
	.alias f_add_mem	$b867
	.alias f_subtract_mem	$b850
	.alias f_multiply_mem	$ba28
	.alias f_divide_mem	$bb0f

	;; Useful FP constants that live in the ROM.
	;; It's plausible that ld_fac1_a or ld_fac1_s16
	;; would be more convenient than ld_fac1_mem
	;; with f_1 or f_10, but when doing memory-based
	;; generic stuff, these will still be useful
	.alias	f_0_5		$bf11		;  0.5
	.alias	f_1		$b9bc		;  1.0
	.alias	f_pi		$aea8		;  3.1415926
	.alias	f_10		$baf9		; 10.0

	;; Macros to make our lives easier
.macro	f_move
	ldx	#$00
_fmvlp:	lda	_2,x
	sta	_1,x
	inx
	cpx	#$05
	bne	_fmvlp
.macend

.macro	print_str
	lda	#<_1
	ldy	#>_1
	jsr	strout
.macend

.macro	ld_fac1
	lda	#<_1
	ldy	#>_1
	jsr	ld_fac1_mem
.macend

.macro	ld_fac2
	lda	#<_1
	ldy	#>_1
	jsr	ld_fac2_mem
.macend

.macro	st_fac1
	lda	#<_1
	ldy	#>_1
	jsr	fac1_to_mem
.macend

.macro	fp_load
	`ld_fac1 _1
.macend

.macro	fp_store
	`st_fac1 _1
.macend

.macro	fp_print
	`ld_fac1 _1
	jsr	fac1out
.macend

.macro	fp_read
	lda	#<_1
	ldy	#>_1
	jsr	ld_fac1_string
.macend

.macro	fp_add
	lda	#<_1
	ldy	#>_1
	jsr	f_add_mem
.macend

.macro	fp_subtract
	jsr	ld_fac2_fac1
	`ld_fac1 _1
	jsr	f_subtract_op
.macend

.macro	fp_multiply
	lda	#<_1
	ldy	#>_1
	jsr	f_multiply_mem
.macend

.macro	fp_divide
	jsr	ld_fac2_fac1
	`ld_fac1 _1
	jsr	f_divide_op
.macend

.macro	fp_pow
	jsr	ld_fac2_fac1
	`ld_fac1 _1
	jsr	f_pow_op
.macend

.macro	fp_and
	`ld_fac2 _1
	jsr	f_and_op
.macend

.macro	fp_or
	`ld_fac2 _1
	jsr	f_or_op
.macend

ld_fac1_ti:
	jsr	$ffde		; RDTIM
	sty	$63
	stx	$64
	sta	$65
	;; Once the requirements on .Y and $68 are better
	;; understood, this might be exportable as
	;; ld_fac1_s32, but there are still some dragons
	;; lurking
	ldy	#$00		; Clear out intermediary values
	sta	$62
	sta	$68
	jmp	$bcd5

fac1_to_mem:
	sta	$fd
	sty	$fe
	jsr	fac1_to_5c
	ldy	#$00
*	lda	$5c,y
	sta	($fd),y
	iny
	cpy	#$05
	bne	-
	rts

ld_fac1_string:
	ldx	$7a
	sta	$7a
	txa
	pha
	lda	$7b
	pha
	sty	$7b
	jsr	$79
	jsr	$bcf3
	pla
	sta	$7b
	pla
	sta	$7a
	rts

fac1out:
	ldy	#$00		; Clean out overflow
	sty	$68
	sty	$70
	jsr	fac1_to_string
	ldy	#$01
	;; Skip the first character if it's not "-"
	lda	$100
	sec
	sbc	#$2d
	beq	strout
	lda	#$01
	;; Fall through to strout

;;; The BASIC ROM already has a STROUT routine - $ab1e - but
;;; it makes use of BASIC's own temporary string handling. We
;;; don't want it to ever touch its notion of temporary strings
;;; here, so we provide our own short routine to do this.
strout:	sta	$fd
	sty	$fe
	ldy	#$00
*	lda	($fd),y
	beq	+
	jsr	$ffd2		; CHROUT
	iny
	bne	-
*	rts

randomize:
	jsr	ld_fac1_ti
	lda	#$ff
	sta	$66		; Force sign negative
	jmp	rnd_fac1	; RND(-TI)

rnd:	lda	#$01
	jsr	ld_fac1_a
	jmp	rnd_fac1
