aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Watson <yalhcru@gmail.com>2016-01-12 07:42:30 -0500
committerB. Watson <yalhcru@gmail.com>2016-01-12 07:42:30 -0500
commit854aaed7643cc2224987738f04384f884d1084d5 (patch)
treef9b4a019a5c859719678bb523ecc1786df665229
parentf90842d4decc6f9453e53785174c73674dc86a51 (diff)
downloadtaipan-854aaed7643cc2224987738f04384f884d1084d5.tar.gz
some FP wrappers and test code, not very good yet
-rw-r--r--bank.c41
-rw-r--r--bignum.h49
-rw-r--r--bignum.s205
-rw-r--r--bigtest.c44
4 files changed, 339 insertions, 0 deletions
diff --git a/bank.c b/bank.c
new file mode 100644
index 0000000..02b4522
--- /dev/null
+++ b/bank.c
@@ -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;
+}