-
Notifications
You must be signed in to change notification settings - Fork 1
/
nv_string_code.asm
408 lines (353 loc) · 14 KB
/
nv_string_code.asm
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
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
//////////////////////////////////////////////////////////////////////////////
// nv_string_code.asm
// Copyright(c) 2022 Neal Smith.
// License: MIT. See LICENSE file in root directory.
//////////////////////////////////////////////////////////////////////////////
// Contains string related subroutines (and some supporting macros) and data
//
#importonce
#if !NV_C64_UTIL_DATA
.error "Error - nv_debug_code.asm: NV_C64_UTIL_DATA not defined. Import nv_c64_util_data.asm"
#endif
// the #if above doesn't seem to always work so..
// if data hasn't been imported yet, import it into default location
#importif !NV_C64_UTIL_DATA "nv_c64_util_default_data.asm"
#import "nv_string_macs.asm"
#import "nv_math16_macs.asm"
#import "nv_math124_macs.asm"
// pointer to use for all string subroutines
nv_str1_ptr: .word $0000
nv_str2_ptr: .word $0000
nv_str_save_block: .word $0000
//////////////////////////////////////////////////////////////////////////////
// inline macro to fill a string with the decimal representation of the
// specified fp124 value. Carry flag determines if its interpreted as
// signed or unsigned.
// carry flag: set -> signed fp124
// clear -> unsigned fp124
// fp124_addr: is the address of the LSB of the fp124 value
// that will be interpreted as a signed or usigned and
// converted to string.
// str_ptr: is the pointer to the address of the first char of string
// to build up. It must point to area in memory big enough
// to hold all the
// digits on both sides of decimal, plus a sign, plus a decimal
// plus a null. (11 bytes for fp124s)
.macro nv_str_fp124x_to_str_sr(fp124_addr, str_ptr)
{
// start with empty string
lda #$00
nv_store_a_to_mem_ptr(str_ptr, nv_str_save_block)
// will call NvStrCatChar_a subroutine below so must
// set the nv_str1_ptr to the same address as passed pointer
nv_xfer16_mem_mem(str_ptr, nv_str1_ptr)
lda fp124_addr+1
// if carry is clear then its an unsigned fp124
bcc DoneWithSign // unsigned number so skip sign stuff
bpl DoneWithSign // signed but positive number sign is 0
// if get here its a signed number and its negative
IsNegative:
// start the string by concatenating a minus sign
lda #$2D // the - sign
jsr NvStrCatChar_a // call sr to concatenate char in Accum to str
// setup scratch word with the value left of decimal point
lda fp124_addr + 1
and #$7F // its neg signed so mask off the sign bit
DoneWithSign:
// accum now has MSB of the fp124x with sign masked off if needed
sta scratch_word+1
lda fp124_addr
sta scratch_word
nv_lsr16u_mem16u_immed8u(scratch_word, 4)
ldy #0
ThousandsTop:
nv_blt16_immed(scratch_word, 1000, DoneThousands)
// count 1000s in y reg
iny
nv_sbc16_mem_immed(scratch_word, 1000, scratch_word)
jmp ThousandsTop
DoneThousands:
// Y reg has number of thousands in it
lda hex_digit_lookup, y
jsr NvStrCatChar_a // call sr to concatenate char in A to str
ldy #0
HundredsTop:
nv_blt16_immed(scratch_word, 100, DoneHundreds)
// count 100s in y reg
iny
nv_sbc16_mem_immed(scratch_word, 100, scratch_word)
jmp HundredsTop
DoneHundreds:
// y reg has number of hundreds in it
lda hex_digit_lookup, y
jsr NvStrCatChar_a // call sr to concatenate char in A to str
ldy #0
TensTop:
nv_blt16_immed(scratch_word, 10, DoneTens)
// count 10s in y reg
iny
nv_sbc16_mem_immed(scratch_word, 10, scratch_word)
jmp TensTop
DoneTens:
// y reg has number of tens in it
lda hex_digit_lookup, y
jsr NvStrCatChar_a // call sr to concatenate char in A to str
ldy #0
OnesTop:
nv_blt16_immed(scratch_word, 1, DoneOnes)
// count 1s in y reg
iny
nv_sbc16_mem_immed(scratch_word, 1, scratch_word)
jmp OnesTop
DoneOnes:
// y reg has number of ones in it
lda hex_digit_lookup, y
jsr NvStrCatChar_a // call sr to concatenate char in A to str
// add decimal point to string
lda #$2E // decimal point char to accum
jsr NvStrCatChar_a // call sr to concatenate char in A to str
// zero out the scratch word. we will use this as a bcd
// value that is the number on the right of decimal point
nv_store16_immed(scratch_word, 0)
// setup Y register with the low 4 bits of the fp124
lda fp124_addr
and #$0F
tay
TopRight:
cpy #0
beq DoneRight
// add the smallest decimal value to our scratch bcd 16 bit value
nv_bcd_adc16_mem_immed(scratch_word, $0625, scratch_word)
dey
jmp TopRight
DoneRight:
// now scratch_word has the BCD for the right of decimal
// concatenate each digit
// first digit right of decimal
lda scratch_word+1
lsr
lsr
lsr
lsr
tay
// y reg has first digit to right of decimal its in range 0-9
lda hex_digit_lookup, y
jsr NvStrCatChar_a // call sr to concatenate char in A to str
// second digit to right of decimal
lda scratch_word+1
and #$0F
tay
// y reg has second digit to right of decimal its in range 0-9
lda hex_digit_lookup, y
jsr NvStrCatChar_a // call sr to concatenate char in A to str
// third digit to right of decimal
lda scratch_word
lsr
lsr
lsr
lsr
tay
// y reg has third digit to right of decimal its in range 0-9
lda hex_digit_lookup, y
jsr NvStrCatChar_a // call sr to concatenate char in A to str
// Forth digit to right of decimal
lda scratch_word
and #$0F
tay
// y reg has second digit to right of decimal its in range 0-9
lda hex_digit_lookup, y
jsr NvStrCatChar_a // call sr to concatenate char in A to str
Done:
rts
}
//
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// Subroutines to call below here
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// Subroutine to call to concatenate the char in accumulator to the
// null terminated string at the address pointed to by the specified
// pointer.
// Before calling:
// Accum: load with the character to concat to string
// nv_str1_ptr: setup to point to the string. If the string is
// at a label str_addr then the setup code could be:
// lda #<str_addr
// sta nv_str1_ptr
// lda #>str_addr
// sta nv_str1_ptr+1
// After calling:
// The char will be concatenated to the string pointed to by
// nv_str1_ptr.
NvStrCatChar_a:
nv_str_cat_char_a_sr(nv_str1_ptr, nv_str_save_block)
//
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// Subroutine to call to fill a string with the decimal string
// representation of a fp124 value. The fp124 value can be signed or
// unsigned the carry flag will determine how its intepreted
// Before calling:
// carry flag: set -> signed fp124
// clear -> unsigned fp124
// nv_fp124_for_to_str: Set to the fp124 value to convert
// nv_str1_ptr: setup to point to the string. If the string is
// at a label str_addr then the setup code could be:
// lda #<str_addr
// sta nv_str1_ptr
// lda #>str_addr
// sta nv_str1_ptr+1
// After calling:
// The null terminated string will be in the string at the address
// pointed to by nv_str1_ptr. the string will be a decimal number
// and always look have 4 digits on both sides of the decimal point
// like this: 0001.5000 or if negative, then like this -0001.5000
NvStrFP124xToStr:
nv_str_fp124x_to_str_sr(nv_fp124_for_to_str, nv_str1_ptr)
// rts is within macro above
// same as NvStrFP124xToStr except the result is trimmed of
// leading and trailing zeros. Also if the result is < 1 the leading
// zero is left. If everything to right of decimal point is a zero
// then the decimal point is also removed.
// 0001.5000 becomes 1.5
// 0001.0000 becomes 1
// 0000.5000 becomes 0.5
// sign will be included if negative only
// 8001.5000 becomes -1.5
NvStrFP124xToStrWithTrim:
// get string in 0000.0000 format
jsr NvStrFP124xToStr
lda #'0' // char to trim
ldx #$00 // max chars to trim (unlimited)
jsr NvStrTrimEnd // trim the trailing zeros
lda #'.' // char to trim
ldx #$00 // max chars to trim (unlimited)
jsr NvStrTrimEnd // trim the trailing dec point if exists
// get the first char of the string
nv_load_a_from_mem_ptr(nv_str1_ptr, save_block_fp124_to_str)
cmp #'-' // check if its minus sign
bne NoMinus // if not minus then trim leading zeros
IsMinus:
// the first char is a minus so add one to the pointer
// so that we skip the minus when trimming
nv_adc16x_mem_immed(nv_str1_ptr, 1, nv_str1_ptr)
lda #'0' // char to trim
ldx #$03 // max chars to trim is 3
jsr NvStrTrimStart // trim the zeros to right of minus
// subtract one to restore nv_str1_ptr
nv_adc16x_mem_immed(nv_str1_ptr, $FFFF, nv_str1_ptr)
rts
NoMinus:
lda #'0' // char to trim
ldx #$03 // max chars to trim (unlimited)
jsr NvStrTrimStart // trim the leading zeros
rts
// the fp124x input parameter for NvStrFP124xToStr and NvStrFP124xToStrTrim
nv_fp124_for_to_str: .word $0000
save_block_fp124_to_str: .word $0000
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Subroutine that Trims all matching chars from the end of a string
// Before calling:
// nv_str1_ptr: setup to point to the string that will be trimmed.
// If the string to trim is at a label str_addr then
// the setup code could be:
// lda #<str_addr
// sta nv_str1_ptr
// lda #>str_addr
// sta nv_str1_ptr+1
// Accum: should contain the char that will be trimmed from the end
// of the string.
// X Reg: should be the set to max number of chars to trim. set to
// zero to have no max other than the 254 limit.
//
NvStrTrimEnd:
{
nv_str_trim_end_char_a_sr(nv_str1_ptr, trim_end_save_block)
// rts is in macro above
trim_end_save_block:
.word $0000
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Subroutine that Trims all matching chars from the start of a string
// removing any chars that match a specified char
// Note this the string must being null terminated.
// Note that the string can be 254 chars max (or 255 bytes including null.)
// Before calling:
// nv_str1_ptr: setup to point to the string that will be trimmed.
// If the string to trim is at a label str_addr then
// the setup code could be:
// lda #<str_addr
// sta nv_str1_ptr
// lda #>str_addr
// sta nv_str1_ptr+1
// Accum: should contain the char that will be trimmed from the end
// of the string.
// Accum: changes
// X Reg: changes
// Y Reg: changes
NvStrTrimStart:
{
nv_str_trim_start_char_a_sr(nv_str1_ptr, trim_start_save_block)
// rts is in macro above
trim_start_save_block:
.word $0000
}
////////////////////////////////////////////////////////////////////////
// Subroutine to call to compare two strings.
// Before calling:
// nv_str1_ptr: setup to point to the str1. If the string is
// at a label str_addr then the setup code could be:
// lda #<str_addr
// sta nv_str1_ptr
// lda #>str_addr
// sta nv_str1_ptr+1
// nv_str2_ptr: setup to point to the str2. If the string is
// at a label str2_addr then the setup code could be:
// lda #<str2_addr
// sta nv_str2_ptr
// lda #>str2_addr
// sta nv_str2_ptr+1
// After calling: The flags will be set as if cmp done
// Z flag set if strings are equal
// Carry flag set when str1 is greater than or equal to str2
// Carry flag is clear when str1 is less than str2
NvStrCmp:
{
nv_str_cmp_sr(nv_str1_ptr, nv_str2_ptr, nv_str_save_block)
// rts is in macro above.
nv_str_cmp_save_block: .word $0000
}
//
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// Subroutine to call to copy one string to another.
// The source string must be null terminated and the destination
// string will be null terminated upon return.
// The destination string pointer must be large enough to accomodate
// the source string including the terminating null
// Before calling:
// nv_str1_ptr: setup to point to the source string. If the src str is
// at a label str_addr then the setup code could be:
// lda #<str_addr
// sta nv_str1_ptr
// lda #>str_addr
// sta nv_str1_ptr+1
// nv_str2_ptr: setup to point to the destination string. If the dest str
// is at a label str2_addr then the setup code could be:
// lda #<str2_addr
// sta nv_str2_ptr
// lda #>str2_addr
// sta nv_str2_ptr+1
NvStrCpy:
{
nv_str_cpy_sr(nv_str1_ptr, nv_str2_ptr, nv_str_cpy_save_block)
// note that rts is in above macro
nv_str_cpy_save_block: .word $0000
}