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; +} |