-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathutil.inc
377 lines (317 loc) · 10.3 KB
/
util.inc
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
;*******************************************************************************
;Copyright 2022-2024, Stefan Jakobsson
;
;Redistribution and use in source and binary forms, with or without modification,
;are permitted provided that the following conditions are met:
;
;1. Redistributions of source code must retain the above copyright notice, this
; list of conditions and the following disclaimer.
;
;2. Redistributions in binary form must reproduce the above copyright notice,
; this list of conditions and the following disclaimer in the documentation
; and/or other materials provided with the distribution.
;
;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS”
;AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
;IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
;DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
;FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
;DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
;SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
;CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
;OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
;OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;*******************************************************************************
;******************************************************************************
;Function name.......: util_bin_to_bcd
;Purpose.............: Converts a binary value to a BCD value
;Input...............: 24 bit binary value, X=low, Y=mid, A=high
;Returns.............: Pointer to 32 bit BCD value, X=addressL, Y=addressH
;Error returns.......: None
;Credits.............: This routine is based on Andrew Jacobs code published
; here: http://www.6502.org/source/integers/hex2dec-more.htm
; The function basically switches to decimal mode and then
; adds the bits together
.proc util_bin_to_bcd
;Store 24 bit input
stx input
sty input+1
sta input+2
;Clear 32 bit output
stz output
stz output+1
stz output+2
stz output+3
;Number of input bits
ldx #24
;Set decimal mode
sed
loop:
;Rotate input, leftmost bit -> C
asl input
rol input+1
rol input+2
;32 bit addition. Value of C from previous operation is the actual input. Therefore C is not cleared.
lda output
adc output
sta output
lda output+1
adc output+1
sta output+1
lda output+2
adc output+2
sta output+2
lda output+3
adc output+3
sta output+3
;Decrease bit counter, continue if >0
dex
bne loop
;Go back to binary mode
cld
;Set pointer to output, and we're done
ldx #<output
ldy #>output
rts
input = tempvars ;3 bytes
output = tempvars+3 ;4 bytes
.endproc
;******************************************************************************
;Function name.......: util_bcd_to_bin
;Purpose.............: Converts a BCD value to a binary value
;Input...............: Pointer to 32 bit BCD value, X=AddressL, Y=AddressH
;Returns.............: 24 bit binary value, X=low, Y=mid, A=high
;Error returns.......: None
;Credits.............: This routine is based on an article by Marvin L. De Jong
; published here:
; https://www.atarimagazines.com/compute/issue17/221_1_A_GENERAL_PURPOSE_BCD-TO-BINARY_ROUTINE.php
; De Jong was in his turn inspired by the book
; Microcomputer-Based Design, by John B. Peatman,
; McGraw Hill, New York, 1977, p 400.
;
; The technique is based on continuous dividing the input by two and
; storing the remainders as answer. The trick is to adjust the
; answer by subtracting 3 from each nibble if the highest
; bit is set. Why does this work? I guess you have to read
; those publications.
.proc util_bcd_to_bin
;Copy input to local variable
stx TMP1_ADR
sty TMP1_ADR+1
ldy #0
: lda (TMP1_ADR),y
sta bcd_val,y
iny
cpy #5 ;The input is 32 bits = 4 bytes. Thus we've copied all when Y=5
bne :-
;Prepare loop
ldy #32 ;Output bit count, internally 32 bits, but the function only returns the lowest 24 bits. The highest 8 bits are discarded.
ldx #3 ;Byte index (corresponds to 32 bit input, bytes 0 to 3)
clc ;Clear carry and push processor status including carry flag to stack
php
loop:
;Get processor status from stack. We're really only interested in carry flag
plp
;Divide by 2, remainder in carry flag
ror bcd_val,x
;Save processor status including carry flag on stack again
php
;Adjust high nibble: If bit 7=1 subtract $30; This is the essential "trick" in De Jong's and Peatman's
;above mentioned publications.
bit bcd_val,x
bpl :+ ;bpl branches if bit 7 is clear (a positive number)
sec
lda bcd_val,x
sbc #$30
sta bcd_val,x
;Adjust low nibble: If bit 3=1 subtract $3; The same "trick" but for the low nibble
: lda bcd_val,x
and #8 ;The bpl op code doesn't work here, checking bit 3 by anding it
beq next_byte
sec
lda bcd_val,x
sbc #3
sta bcd_val,x
next_byte:
dex
cpx #$ff
bne loop
;Reset X for next loop
ldx #3
;Get C from stack, and rotate to binary output
plp
ror bin_val+3
ror bin_val+2
ror bin_val+1
ror bin_val
;Prepare stack for next loop, carry not set when we start the loop
clc
php
dey ;If Y=0 we're done with all 32 bits
bne loop
;Clean stack
plp
;Fetch result and exit
ldx bin_val
ldy bin_val+1
lda bin_val+2
rts
bcd_val = tempvars ;4 bytes
bin_val = tempvars+4 ;4 bytes
.endproc
;******************************************************************************
;Function name.......: util_bcd_to_str
;Purpose.............: Converts a BCD value to a null terminated string
;Input...............: Pointer to 32 bit BCD value, X=AddressL, Y=AddressH
;Returns.............: Pointer to string, X=AddressL, Y=AddressH
;Error returns.......: None
.proc util_bcd_to_str
;Copy input to local variable
stx TMP1_ADR
sty TMP1_ADR+1
ldy #0
: lda (TMP1_ADR),y
sta input,y
iny
cpy #5
bne :-
ldx #4 ;Byte index, start with rightmost byte (MSB)
ldy #0 ;Char index, start from left
build_str:
;Get value from high nibble
lda input-1,x ;"input-1" is needed because X starts from 4 (i.e. last index+1); this makes the exit test simpler - maybe I'm optimizing too much?
lsr
lsr
lsr
lsr
clc
adc #48
sta output,y
iny
;Get value from low nibble
lda input-1,x
and #15
clc
adc #48
sta output,y
iny
;Decrease input byte index, if = 0 we're done
dex
bne build_str
add_null:
lda #0
sta output,y
strip_leading_zeros:
;Look for first non "0" value, and set the start of the output string to that position
ldx #0
: lda output,x
cmp #48
bne exit
inx
cpx #7 ;We must break this search at position 7. Otherwise input value 0 would return an empty string.
bcc :-
exit:
txa ;Start of string from search for non 0 value above; added to the output pointer
clc
adc #<output
tax
lda #0
adc #>output
tay
rts
input = tempvars ;4 bytes
output = tempvars+4 ;9 bytes (8 figures + null)
.endproc
;******************************************************************************
;Function name.......: util_str_to_bcd
;Purpose.............: Converts string to bcd value
;Input...............: Pointer to null terminated string, X=AddressL, Y=AddressH
; Input beyond 8 digits will be ignored
;Returns.............: Pointer to 32 bit BCD value, X=AddressL, Y=AddressH
;Error returns.......: C=1 if invalid input, else C=0
.proc util_str_to_bcd
;Setup zero page vectors
stx TMP1_ADR
sty TMP1_ADR+1
;Clear output
stz bcd_val
stz bcd_val+1
stz bcd_val+2
stz bcd_val+3
;Going arabic. We need to parse from right to left, therefore go to end of string
ldy #0
: lda (TMP1_ADR),y
beq prepare
iny
bra :-
prepare:
cpy #0
beq invalid ;String len = 0 is invalid input
dey ;Decrease Y to step back to the last digit. Y was at the terminating null char before this.
ldx #0
loop:
;Get input char and subtract 48
lda (TMP1_ADR),y
sec
sbc #48
bcc invalid ;Char was less than 48, exit with error
cmp #10
bcs invalid ;Char was greater than 57, exit with error
sta bcd_val,x
;Move index to next char, if Y wraps around to $ff we're done
dey
cpy #$ff
beq end
;Get input char and subtract 48 as above
lda (TMP1_ADR),y
sec
sbc #48
bcc invalid
cmp #10
bcs invalid
;Multiply by 16, to store every other input char in the outout high nibble
asl
asl
asl
asl
ora bcd_val,x
sta bcd_val,x
;Increase output index and decrease input index. Exit if input index=ff
inx
dey
cpy #$ff
bne loop
end:
;Default exit
clc
ldx #<bcd_val
ldy #>bcd_val
rts
invalid:
;Exit if input value invalid (not only digits in string or zero len string)
sec
ldx #<bcd_val
ldy #>bcd_val
rts
len = tempvars ;1 byte
bcd_val = tempvars+1 ;4 bytes
.endproc
;******************************************************************************
;Function name.......: util_strlen
;Purpose.............: Returns length of null terminated string; this function
; assumes that the input string length is less than
; 256 chars
;Input...............: Pointer to null terminated string, X=AddressL, Y=AddressH
;Returns.............: Y=String len
;Error returns.......: Nothing
.proc util_strlen
stx TMP1_ADR
sty TMP1_ADR+1
ldy #0
: lda (TMP1_ADR),y
beq done
iny
bra :-
done:
rts
.endproc