aboutsummaryrefslogtreecommitdiff
path: root/bignum.s
diff options
context:
space:
mode:
Diffstat (limited to 'bignum.s')
-rw-r--r--bignum.s205
1 files changed, 205 insertions, 0 deletions
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