diff options
| -rw-r--r-- | bank.c | 41 | ||||
| -rw-r--r-- | bignum.h | 49 | ||||
| -rw-r--r-- | bignum.s | 205 | ||||
| -rw-r--r-- | bigtest.c | 44 | 
4 files changed, 339 insertions, 0 deletions
| @@ -0,0 +1,41 @@ +#include <stdint.h> +#include "bignum.h" + +char would_overflow(unsigned long value, unsigned long amount) { +	return ((UINT32_MAX - amount) <= value); +} + +char bank_withdraw(long amount) { +	bignum(bigamt); + +	if(amount < 0) { +		/* can't withdraw all, if too much in bank */ +		if(big_cmp(&bank, B_MAXLONG) == 1) +			return 0; +		 +		big_copy(&bigamt, &bank); +		big_to_ulong(&bigamt, &amount); +	} + +	if(would_overflow(cash, amount)) return 0; + +	cash += amount; +	ulong_to_big(&amount, &bigamt); +	big_sub(&bank, &bank, &bigamt); + +	return 1; +} + +void bank_deposit(long amount) { +	bignum bigamt; + +	if(amount < 0) amount = cash; + +	cash -= amount; +	ulong_to_big(&amount, &bigamt); +	big_add(&bank, &bank, &bigamt); +} + +void bank_interest(void) { +	big_mul(&bank, &bank, &interest_rate); +} diff --git a/bignum.h b/bignum.h new file mode 100644 index 0000000..a6306f4 --- /dev/null +++ b/bignum.h @@ -0,0 +1,49 @@ +/* big number functions needed by taipan.c. + +	The implementation will actually use the Atari ROM floating point +	routines. To port Taipan to a new cc65 platform, the functions listed +	here will have to be rewritten, but taipan.c itself shouldn't need +	changing (at least, not in relation to bignums!) */ + +#define bignum(x) char x[6] +#define bignump char * + + +/* zero */ +#define BIG_0 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + +/* constant initializer for 1.05 goes here */ +#define BIG_1_05 { 0x40, 0x01, 0x05, 0x00, 0x00, 0x00 } + +/* bignum 100, used for score calculations in final_stats() */ +#define BIG_10 { 0x41, 0x01, 0x00, 0x00, 0x00, 0x00 } + +// void int_to_big(int i, bignum *b); +// void uint_to_big(unsigned int i, bignum *b); + +extern void __fastcall__ ulong_to_big(const unsigned long l, bignump b); + +/* returns 0 for success, nonzero for fail (overflow or negative) */ +extern char __fastcall__ big_to_ulong(bignump b, unsigned long *l); + +/* this should work like cprintfancy(), but doesn't need centering. */ +// void cprint_big(const bignum *b); + +/* basic math functions. conceptually they return a boolean for +	success, but only division has error checking. +	all can be read as: dest = arg2 OP arg3; +	modulus isn't implemented as taipan doesn't use it for the bank. + */ +// char big_add(bignum *dest, bignum *addend1, bignum *addend2); +// char big_sub(bignum *dest, bignum *minuend, bignum *subtrahend); +// char big_mul(bignum *dest, bignum *multiplicand, bignum *multiplier); +// char big_div(bignum *dest, bignum *dividend, bignum *divisor); + +/* comparison. Perl spaceship operator, <=> +	returns  | if +	---------+---------------- +	    0    | a == b +	    1    | a > b +	   -1    | a < b + */ +// signed char big_cmp(const bignum *a, const bignum *b) diff --git a/bignum.s b/bignum.s new file mode 100644 index 0000000..56f1299 --- /dev/null +++ b/bignum.s @@ -0,0 +1,205 @@ + + + .importzp ptr3, ptr4, sreg + .import popeax, popax + .export _ulong_to_big, _big_to_ulong + + .include "atari.inc" + +;IFP = $d9aa + + fptemp = $a0 ; for now + + .rodata +BIG_64K: + .byte $42, $06, $55, $36, $00, $00 + +;BIG_ULONG_MAX: + ;.byte $44, $42, $94, $96, $72, $95 + + .code + +; TODO: replace these *_to_* with OS calls + +fr0_to_fptemp: + ldx #5 +@l: + lda FR0,x + sta fptemp,x + dex + bpl @l + rts + +fr0_to_fr1: + ldx #5 +@l: + lda FR0,x + sta FR1,x + dex + bpl @l + rts + +fptemp_to_fr0: + ldx #5 +@l: + lda fptemp,x + sta FR0,x + dex + bpl @l + rts + +fptemp_to_fr1: + ldx #5 +@l: + lda fptemp,x + sta FR1,x + dex + bpl @l + rts + +fr0_to_ptr3: + ldy #5 +@l: + lda FR0,y + sta (ptr3),y + dey + bpl @l + rts + +ptr4_to_fr1: + ldy #5 +@l: + lda (ptr4),y + sta FR1,y + dey + bpl @l + rts + +; truncate FR0 to integer (no rounding: 2.8 -> 2) +trunc_fr0: + lda FR0 + and #$7f ; strip sign bit (we only care about exponent magnitude) + sec + sbc #$3f ; A now holds # of base-100 digits in integer part + bcs @ok  ; # of int digits > 0? + jmp ZFR0 ; no, zero out FR0 and exit + +@ok: + cmp #5     ; are there <= 5 int digits? + bcs @done  ; no, the number's already an integer. + + tax        ; zero out digits: X is first one after decimal point + lda #0 +@zloop: + sta FR0+1,x + inx + cpx #5 + bne @zloop + +@done: + rts + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; void __fastcall__ big_trunc(bignump b); + sta FLPTR + stx FLPTR+1 + jsr FLD0P + jsr trunc_fr0 + jsr FST0P + rts + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; void __fastcall__ ulong_to_big(const unsigned long l, bignum *b); +_ulong_to_big: + sta ptr3 + stx ptr3+1 ; save b (destination) + + jsr popeax ; get low 16 bits of l in A/X (hi 16 bits in sreg) + sta FR0 + stx FR0+1 + jsr IFP    ; convert A/X to fp + + jsr fr0_to_fptemp ; stash it + + lda sreg   ; now get high 16 bits of l in A/X + sta FR0 + ldx sreg+1 + stx FR0+1 + jsr IFP    ; convert to fp + + lda #<BIG_64K  ; high value needs to be multiplied by 65536 + sta ptr4 + lda #>BIG_64K + sta ptr4+1 + jsr ptr4_to_fr1 + + jsr FMUL       ; multiply... + jsr fptemp_to_fr1 ; grab low value + jsr FADD          ; add to total + jmp fr0_to_ptr3   ; store it in b and we're done. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; char __fastcall__ big_to_ulong(bignump b, unsigned long *l); +; +; This works, but it's not small, fast, or elegant... +_big_to_ulong: + sta ptr3 + stx ptr3+1 ; save *l (dest) + + jsr popax ; get b + sta FLPTR + sta sreg + stx FLPTR+1 + stx sreg+1 + jsr FLD0P ; there's a typo in atari.inc, should be FLD1P + + ldx #<BIG_64K ; FR1 = 65536 + ldy #>BIG_64K + jsr FLD1R + + jsr FDIV      ; FR0 = FR0 / FR1 + jsr trunc_fr0 ; FR0 = INT(FR0) + jsr fr0_to_fptemp ; stash for later... + jsr FPI       ; get integer form + bcc @ok       ; OS supposed to return with C set if range error + + ; failed, return 0 to caller + lda #0 + tax + rts + +@ok: + ldy #2        ; save top 16 bits of result where they belong + lda FR0 + sta (ptr3),y + iny + lda FR0+1 + sta (ptr3),y + + jsr fptemp_to_fr0 ; this is int((*b)/65536) in FR0 now + + ldx #<BIG_64K ; FR1 = 65536 + ldy #>BIG_64K + jsr FLD1R + + jsr FMUL     ; FR0 now int((*b)/65536)*65536 + jsr FMOVE    ; FR1 = FR0 + + ldx sreg     ; reload original *b in FR0 + ldy sreg+1 + jsr FLD0R + + jsr FSUB     ; FR0 = FR0 - FR1 + jsr FPI + + ldy #0       ; store low 16 bits where they belong + lda FR0 + sta (ptr3),y + iny + lda FR0+1 + sta (ptr3),y + + ; success. return 1 to caller. + tya + ldx #0 + rts diff --git a/bigtest.c b/bigtest.c new file mode 100644 index 0000000..f6b2551 --- /dev/null +++ b/bigtest.c @@ -0,0 +1,44 @@ +#include <stdio.h> +#include <peekpoke.h> + +#include "bignum.h" + +unsigned long values[] = { +	123456789L, +	12345678L, +	1234567L, +	123456L, +	65536L, +	65535L, +	4294966190L, /* works, (2**32-1)-1105 */ +	4294967295L, +	665, +	78, +	1, +	0 +}; + +int main(void) { +	char i, j; +	unsigned long l = 123456789L; // 075bcd15, or 52501 + 256 * 1883 +	bignum(b); + +	/* +	ulong_to_big(l, b); +	for(i=0; i<6; i++) +		printf("%02x ", b[i]); +		*/ + +	POKEW(19,0); +	for(i=0; i < (sizeof(values)) / (sizeof(long)); i++) { +		l = values[i]; +		printf("%lu: ", l); +		ulong_to_big(l, b); +		l = 666L; +		j = big_to_ulong(b, &l); +		printf("%d %lu\n", j, l); +	} + +	printf("%d\n", PEEK(20)+256*PEEK(19)); +hang: goto hang; +} | 
