aboutsummaryrefslogtreecommitdiff
path: root/bignum.s
blob: 56f129921164947687dc586e1c141328b44a9d63 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
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