From 67761f7d5710fa93e2cd60726c1b8a02776077b5 Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Wed, 13 Jan 2016 06:28:21 -0500 Subject: more floating point test code --- bigfmt.c | 57 ++++++++++++++++++++++++++++++ bignum.h | 64 +++++++++++++++++++++++++++------- bignum.s | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- bigtest.c | 38 +++++++++++++++++++- 4 files changed, 260 insertions(+), 16 deletions(-) create mode 100644 bigfmt.c diff --git a/bigfmt.c b/bigfmt.c new file mode 100644 index 0000000..7f78e00 --- /dev/null +++ b/bigfmt.c @@ -0,0 +1,57 @@ +#include +#include "bignum.h" + +bignum(big1M) = BIG_1M; +bignum(big100M) = BIG_100M; +bignum(big1B) = BIG_1B; + +/* +Requires a bit of explanation. b's value will always be zero or +positive, and can range up to 1.0e+14 (aka 100 trillion). + +magnitude values: +0 - result is used as-is (result range 0 - 999999) + b range 0 - 999999 +1 - result is in 100000's, print result/10 Million (range 0-9999) + b range 1,000,000 - 999,999,999 (999 Million) +2 - result is in 100 millions, print result/10 Billion (range 0-9999) + b range 1,000,000,000 (1 Billion) - 999,999,999,999 (1 trillion - 1) + +The calling code decides whether or not to print a decimal point +and 10ths digit (using integer math only). +*/ + +unsigned long cformat_big(char *magnitude, bignump b) { + bignum(tmp); + unsigned long ret; + if(big_cmp(b, big1M) < 0) { + *magnitude = 0; + big_to_ulong(b, &ret); + } else if(big_cmp(b, big1B) < 0) { + *magnitude = 1; + big_to_ulong(b, &ret); + ret /= 100000L; + } else { + *magnitude = 2; + big_div(tmp, b, big100M); + big_to_ulong(tmp, &ret); + } + return ret; +} + +void cprintfancy_big(bignump b) { + char m; + unsigned long l = cformat_big(&m, b); + if(!m) { + cprintf("%lu", l); + } else { + if(l > 100) { + cprintf("%lu ", l / 10L); + } else { + cprintf("%lu.%lu ", l / 10L, l % 10L); + } + cputc(m == 1 ? 'M' : 'B'); + cputs("illion"); + } + cputs("\r\n"); +} diff --git a/bignum.h b/bignum.h index a6306f4..cb33c62 100644 --- a/bignum.h +++ b/bignum.h @@ -3,20 +3,40 @@ 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!) */ + changing (at least, not in relation to bignums!) + + Why call them "bignums" instead of "Float" or something? because + the whole implementation might get ripped out & replaced with + 64-bit integers, or some other data type. The API shouldn't change + in that case. + + to declare a bignum: + bignum(foo); + + ...foo actually ends up a pointer (a bignump), which can + be passed around to the various big_* functions. + + to use the constants: + bignum(foo) = BIG_0; + which looks a little weird I admit. +*/ #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 } +/* constant initializer for 1.005 goes here */ +#define BIG_1_005 { 0x40, 0x01, 0x00, 0x50, 0x00, 0x00 } /* bignum 100, used for score calculations in final_stats() */ -#define BIG_10 { 0x41, 0x01, 0x00, 0x00, 0x00, 0x00 } +#define BIG_100 { 0x41, 0x01, 0x00, 0x00, 0x00, 0x00 } + +/* one million, one hundred million, and one billion */ +#define BIG_1M { 0x43, 0x01, 0x00, 0x00, 0x00, 0x00 } +#define BIG_100M { 0x44, 0x01, 0x00, 0x00, 0x00, 0x00 } +#define BIG_1B { 0x44, 0x10, 0x00, 0x00, 0x00, 0x00 } // void int_to_big(int i, bignum *b); // void uint_to_big(unsigned int i, bignum *b); @@ -26,24 +46,42 @@ 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); +// extern unsigned long __fastcall__ cformat_big(char *magnitude, 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. + These are __cdecl__ *not* __fastcall__ !! */ -// 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); +extern char __cdecl__ big_add(bignump dest, bignump addend1, bignump addend2); +extern char __cdecl__ big_sub(bignump dest, bignump minuend, bignump subtrahend); +extern char __cdecl__ big_mul(bignump dest, bignump multiplicand, bignump multiplier); +extern char __cdecl__ big_div(bignump dest, bignump dividend, bignump divisor); + +/* returns true if the bank is maxed out. We do this by checking the exponent + byte, so the "max" is tied to the bignum implementation, which is why its + prototype is here rather than bank.h. For Atari floats, it's 1.0e+14, or + 100 trillion. + + If you deposit 1 in the bank at the start of the game and never deposit + more, the interest will max it out in 1915 (661 turns of play). + */ +extern char __fastcall__ bank_maxed_out(bignump b); /* comparison. Perl spaceship operator, <=> returns | if ---------+---------------- 0 | a == b - 1 | a > b - -1 | a < b + positive| a > b + negative| a < b + +BEWARE: unlike perl's <=>, the return value is *not* guaranteed to + be 0, 1, or -1. This is more like C's strcmp() or memcmp(). */ // signed char big_cmp(const bignum *a, const bignum *b) + +extern signed char __fastcall__ big_cmp(bignump a, bignump b); + +extern unsigned long cformat_big(char *magnitude, bignump b); +extern void cprintfancy_big(bignump b); diff --git a/bignum.s b/bignum.s index 56f1299..06e99ab 100644 --- a/bignum.s +++ b/bignum.s @@ -1,14 +1,18 @@ .importzp ptr3, ptr4, sreg - .import popeax, popax - .export _ulong_to_big, _big_to_ulong + .import popeax, popax, pushax, _memcmp + .export _ulong_to_big, _big_to_ulong, _big_add, _big_sub, _big_mul, _big_div + .export _bank_maxed_out, _big_cmp .include "atari.inc" ;IFP = $d9aa fptemp = $a0 ; for now + trampoline = $c0 + + NORMALIZE = $dc00 .rodata BIG_64K: @@ -75,6 +79,7 @@ ptr4_to_fr1: bpl @l rts +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; truncate FR0 to integer (no rounding: 2.8 -> 2) trunc_fr0: lda FR0 @@ -99,8 +104,71 @@ trunc_fr0: @done: rts +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; void __fastcall__ big_binary_op(bignump dest, bignump a, bignump b, unsigned int jsraddr); +_big_binary_op: + + ; JSR address in A/X pair, set up JMP instruction + sta trampoline+1 + stx trampoline+2 + lda #$4c ; JMP opcode + sta trampoline + + ; get 2nd operand (b), load into FR1 + jsr popax + sta FLPTR + stx FLPTR+1 + jsr PLD1P + + ; get 1st operand (a), load into FR0 + jsr popax + sta FLPTR + stx FLPTR+1 + jsr FLD0P + + ; call the FP routine + jsr trampoline + +; jsr NORMALIZE +; .byte $02 + + ; result now in FR0, get destination & copy + jsr popax + sta FLPTR + stx FLPTR+1 + jmp FST0P + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; void __cdecl__ big_add(bignump dest, bignump a, bignump b); +_big_add: + lda #FADD + jmp _big_binary_op + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; void __cdecl__ big_sub(bignump dest, bignump a, bignump b); +_big_sub: + lda #FSUB + jmp _big_binary_op + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; void __cdecl__ big_mul(bignump dest, bignump a, bignump b); +_big_mul: + lda #FMUL + jmp _big_binary_op + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; void __cdecl__ big_div(bignump dest, bignump a, bignump b); +_big_div: + lda #FDIV + jmp _big_binary_op + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; void __fastcall__ big_trunc(bignump b); +; C-callable wrapper for trunc_fr0 sta FLPTR stx FLPTR+1 jsr FLD0P @@ -188,6 +256,7 @@ _big_to_ulong: ldx sreg ; reload original *b in FR0 ldy sreg+1 jsr FLD0R + jsr trunc_fr0 ; grrr. If we don't do this, we get rounding (not desired) jsr FSUB ; FR0 = FR0 - FR1 jsr FPI @@ -203,3 +272,47 @@ _big_to_ulong: tya ldx #0 rts + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; char __fastcall__ bank_maxed_out(bignump b); +_bank_maxed_out: + sta FLPTR + stx FLPTR+1 + jsr FLD0P + jsr NORMALIZE ; just in case + lda FR0 ; get exponent + ldx #0 + eor #$7f ; remove sign bit (should never be negative anyway!) + cmp #$46 + bcc @false + lda #1 + rts +@false: + txa + rts + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; signed char __fastcall__ big_cmp(bignump a, bignump b) +; +; this could be better: it could be a wrapper for _big_binary_op. But +; I'd have to move stuff all around on the stack. +_big_cmp: + sta FLPTR + stx FLPTR+1 + jsr FLD0P + + jsr FMOVE ; move to FR1 (since it's the 2nd arg) + + jsr popax ; get a arg + + sta FLPTR + stx FLPTR+1 + jsr FLD0P + + ; subtract (and throw away the result, only care about sign) + jsr FSUB ; FR0 = FR0 - FR1 + + ldx #0 + lda FR0 ; exponent has sign bit, and happily is 0 if the result was 0! + rts + diff --git a/bigtest.c b/bigtest.c index f6b2551..cd8cd1d 100644 --- a/bigtest.c +++ b/bigtest.c @@ -19,6 +19,42 @@ unsigned long values[] = { }; int main(void) { + char i, j; + unsigned long got; + bignum(a); + bignum(b); + + ulong_to_big(1234L, a); + ulong_to_big(10L, b); + // got = cformat_big(&i, a); + // printf("got %lu, mag %d\n", got, i); + for(i = 0; i < 11; i++) { + cprintfancy_big(a); + big_mul(a, a, b); + } + + /* + ulong_to_big(5L, a); + for(i = 0; i < 10; i++) { + ulong_to_big((unsigned long)i, b); + j = big_cmp(a, b); + printf("5 cmp %d: %d\n", i, j); + } + */ + + /* + unsigned long al = 111, bl = 2, result; + + ulong_to_big(al, a); + ulong_to_big(bl, b); + big_div(a, a, b); + big_to_ulong(a, &result); + printf("%lu\n", result); + */ +hang: goto hang; +} + +int oldmain(void) { char i, j; unsigned long l = 123456789L; // 075bcd15, or 52501 + 256 * 1883 bignum(b); @@ -40,5 +76,5 @@ int main(void) { } printf("%d\n", PEEK(20)+256*PEEK(19)); -hang: goto hang; +// hang: goto hang; } -- cgit v1.2.3