aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Watson <yalhcru@gmail.com>2016-01-13 06:28:21 -0500
committerB. Watson <yalhcru@gmail.com>2016-01-13 06:28:21 -0500
commit67761f7d5710fa93e2cd60726c1b8a02776077b5 (patch)
tree9043ccb47b42e360e51fba75edef88ec6966495b
parent854aaed7643cc2224987738f04384f884d1084d5 (diff)
downloadtaipan-67761f7d5710fa93e2cd60726c1b8a02776077b5.tar.gz
more floating point test code
-rw-r--r--bigfmt.c57
-rw-r--r--bignum.h64
-rw-r--r--bignum.s117
-rw-r--r--bigtest.c38
4 files changed, 260 insertions, 16 deletions
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 <conio.h>
+#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
@@ -100,7 +105,70 @@ trunc_fr0:
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
+ ldx #>FADD
+ jmp _big_binary_op
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; void __cdecl__ big_sub(bignump dest, bignump a, bignump b);
+_big_sub:
+ lda #<FSUB
+ ldx #>FSUB
+ jmp _big_binary_op
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; void __cdecl__ big_mul(bignump dest, bignump a, bignump b);
+_big_mul:
+ lda #<FMUL
+ ldx #>FMUL
+ jmp _big_binary_op
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; void __cdecl__ big_div(bignump dest, bignump a, bignump b);
+_big_div:
+ lda #<FDIV
+ ldx #>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
@@ -20,6 +20,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;
}