Initial revision
[ssr] / StraySrc / Libraries / Sapphire / s / string
1 ;
2 ; string.s
3 ;
4 ; String handling routines (control terminated) (MDW)
5 ;
6 ; © 1994-1998 Straylight
7 ;
8
9 ;----- Licensing note -------------------------------------------------------
10 ;
11 ; This file is part of Straylight's Sapphire library.
12 ;
13 ; Sapphire is free software; you can redistribute it and/or modify
14 ; it under the terms of the GNU General Public License as published by
15 ; the Free Software Foundation; either version 2, or (at your option)
16 ; any later version.
17 ;
18 ; Sapphire is distributed in the hope that it will be useful,
19 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ; GNU General Public License for more details.
22 ;
23 ; You should have received a copy of the GNU General Public License
24 ; along with Sapphire. If not, write to the Free Software Foundation,
25 ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26
27 ;----- Standard header ------------------------------------------------------
28
29 GET libs:swis
30 GET libs:header
31
32 ;----- External dependencies ------------------------------------------------
33
34 GET sapphire:sapphire
35
36 ;----- Main code ------------------------------------------------------------
37 ;
38 ; No string routine corrupts the scratchpad.
39
40 AREA |Sapphire$$Code|,CODE,READONLY
41
42 ; --- str_cpy ---
43 ;
44 ; On entry: R0 == destination string
45 ; R1 == source string
46 ;
47 ; On exit: R0 == pointer to terminator of destination
48 ;
49 ; Use: Copies a string from one block to another. It leaves the
50 ; destination pointer at the end of the string so that any
51 ; subsequent copies concatenate other bits on the same string.
52 ; Single characters can of course be appended with
53 ;
54 ; MOV Rx,#&cc
55 ; STRB Rx,[R0],#1
56
57 EXPORT str_cpy
58 str_cpy ROUT
59
60 STMFD R13!,{R1,R14} ;Keep return address safe
61 00str_cpy LDRB R14,[R1],#1 ;Get a byte from source
62 CMP R14,#' ' ;Is it a control character
63 MOVLT R14,#0 ;Yes -- translate to a 0
64 STRB R14,[R0],#1 ;Store in destination
65 BGE %00str_cpy ;No -- copy another byte
66 SUB R0,R0,#1 ;Point back at terminator
67 LDMFD R13!,{R1,PC}^ ;Return to caller
68
69 LTORG
70
71 ; --- str_len ---
72 ;
73 ; On entry: R0 == pointer to string
74 ;
75 ; On exit: R0 == length of the string
76 ;
77 ; Use: Calculates the length of a string.
78
79 EXPORT str_len
80 str_len ROUT
81
82 STMFD R13!,{R1,R14} ;Save some registers
83 MOV R14,R0 ;Point to the string
84 MOV R0,#0 ;Current length is 0
85 00str_len LDRB R1,[R14],#1 ;Get a byte from the string
86 CMP R1,#' ' ;Is it the end yet?
87 LDMLTFD R13!,{R1,PC}^ ;Yes -- return
88 ADD R0,R0,#1 ;Bump the length counter
89 B %00str_len ;And go back for more
90
91 LTORG
92
93 ; --- str_cmp ---
94 ;
95 ; On entry: R0 == pointer to string A
96 ; R1 == pointer to string B
97 ;
98 ; On exit: Flags as appropriate
99 ;
100 ; Use: Case-sensitively compares two strings. You can use the
101 ; normal ARM condition codes after the compare, so you can
102 ; treat this fairly much like a normal CMP.
103
104 EXPORT str_cmp
105 str_cmp ROUT
106
107 STMFD R13!,{R0,R1,R3,R4,R14}
108 00str_cmp LDRB R3,[R0],#1 ;Get a character from A
109 LDRB R4,[R1],#1 ;And one from B
110 CMP R3,#&20 ;Is that the end of A?
111 MOVLT R3,#0 ;Yes -- pretend it's null
112 CMP R4,#&20 ;Is that the end of B?
113 MOVLT R4,#0 ;Yes -- pretend it's null
114 CMP R3,R4 ;How do they match up?
115 LDMNEFD R13!,{R0,R1,R3,R4,PC} ;If NE, return condition
116 CMP R3,#0 ;Is this the end?
117 BNE %00str_cmp ;No -- loop again
118 LDMFD R13!,{R0,R1,R3,R4,PC} ;Return to caller
119
120 ; --- str_icmp ---
121 ;
122 ; On entry: R0 == pointer to string A
123 ; R1 == pointer to string B
124 ;
125 ; On exit: Flags as appropriate
126 ;
127 ; Use: As for str_cmp above, but case-insensitive.
128
129 EXPORT str_icmp
130 str_icmp ROUT
131
132 STMFD R13!,{R0,R1,R3,R4,R14}
133 00str_icmp LDRB R3,[R0],#1 ;Get a character from A
134 LDRB R4,[R1],#1 ;And one from B
135 SUB R14,R3,#'a' ;Subtract the bottom limit
136 CMP R14,#26 ;Is it a lower case letter?
137 BICLO R3,R3,#&20 ;Yes -- convert to upper
138 SUB R14,R4,#'a' ;Subtract the bottom limit
139 CMP R14,#26 ;Is it a lower case letter?
140 BICLO R4,R4,#&20 ;Yes -- convert to upper
141 CMP R3,#&20 ;Is that the end of A?
142 MOVLT R3,#0 ;Yes -- pretend it's null
143 CMP R4,#&20 ;Is that the end of B?
144 MOVLT R4,#0 ;Yes -- pretend it's null
145 CMP R3,R4 ;How do they match up?
146 LDMNEFD R13!,{R0,R1,R3,R4,PC} ;If NE, return condition
147 CMP R3,#0 ;Is this the end?
148 BNE %00str_icmp ;No -- loop again
149 LDMFD R13!,{R0,R1,R3,R4,PC} ;Return to caller
150
151 LTORG
152
153 ; --- str_index ---
154 ;
155 ; On entry: R0 == pointer to name table
156 ; R1 == index into name table
157 ;
158 ; On exit: CS if index good, and
159 ; R0 == address of R0th string in table
160 ; else CC and
161 ; R0 corrupted
162 ;
163 ; Use: Finds an indexed string in a table. The table consists of
164 ; ctrl-terminated strings, with no separation. The table is
165 ; terminated by a zero-length entry.
166
167 EXPORT str_index
168 str_index ROUT
169
170 ORRS R14,R14,#C_flag ;Set C initially
171 STMFD R13!,{R1,R14} ;Save a register or two
172 05 SUBS R1,R1,#1 ;Decrement the counter
173 LDMCCFD R13!,{R1,PC}^ ;And return to caller
174
175 LDRB R14,[R0],#1 ;Load byte from string
176 CMP R14,#&20 ;Is this the end?
177 BCC %10str_index ;Yes -- ooops
178 00 LDRB R14,[R0],#1 ;No -- load another
179 CMP R14,#&20 ;Is this the end?
180 BCS %b00 ;No -- loop then
181 B %b05 ;Go back to main loop
182
183 10str_index LDMFD R13!,{R1,R14} ;And return to caller
184 BICS PC,R14,#C_flag ;With a bad result
185
186 LTORG
187
188 ; --- str_match ---
189 ;
190 ; On entry: R0 == pointer to name table
191 ; R1 == string to match in table
192 ;
193 ; On exit: CS if match found, and
194 ; R0 == index of string matched
195 ; else CC and
196 ; R0 corrupted
197 ;
198 ; Use: Looks up a string in a table. The table consists of
199 ; ctrl-terminated strings, with no separation. The table is
200 ; terminated by a zero-length entry.
201
202 EXPORT str_match
203 str_match ROUT
204
205 STMFD R13!,{R1-R5,R14} ;Save some registers
206 MOV R2,#0 ;Index of the current item
207 LDRB R14,[R1,#0] ;Load the first byte
208 CMP R14,#0 ;Is it a null string?
209 BEQ %90str_match ;Yes -- no match then
210
211 ; --- The main loop ---
212
213 00str_match MOV R3,R1 ;Point to argument start
214 LDRB R4,[R0],#1 ;Load a byte from the table
215 LDRB R5,[R3],#1 ;Load a byte from the arg
216 CMP R4,#&20 ;Is this an empty string?
217 BCC %90str_match ;Yes -- no match then
218
219 ; --- Try to match a word ---
220
221 10str_match CMP R5,#&20 ;End of argument string?
222 BCC %80str_match ;Yes -- that's a match then
223 SUB R14,R4,#'a' ;Subtract the bottom limit
224 CMP R14,#26 ;Is it a lower case letter?
225 BICLO R4,R4,#&20 ;Yes -- convert to upper
226 SUB R14,R5,#'a' ;Subtract the bottom limit
227 CMP R14,#26 ;Is it a lower case letter?
228 BICLO R5,R5,#&20 ;Yes -- convert to upper
229 CMP R4,R5 ;Do characters match up?
230 LDREQB R4,[R0],#1 ;Load a byte from the table
231 LDREQB R5,[R3],#1 ;Load a byte from the arg
232 BEQ %10str_match ;Yes -- go round for more
233
234 ; --- Failed -- find end of table entry ---
235
236 20str_match CMP R4,#&20 ;End of entry string?
237 LDRCSB R4,[R0],#1 ;No -- load byte from table
238 BCS %20str_match ;And go round again
239 ADD R2,R2,#1 ;Increment item index
240 B %00str_match ;Loop round for next entry
241
242 ; --- Found a match ---
243
244 80str_match MOV R0,R2 ;Get the item index
245 LDMFD R13!,{R1-R5,R14} ;Unstack the registers
246 ORRS PC,R14,#C_flag ;And return with C set
247
248 ; --- No match found ---
249
250 90str_match LDMFD R13!,{R1-R5,R14} ;Unstack the registers
251 BICS PC,R14,#C_flag ;And return with C clear
252
253 LTORG
254
255 ; --- str_subst ---
256 ;
257 ; On entry: R0 == Pointer to skeleton
258 ; R1 == Pointer to output buffer
259 ; R2-R11 == Pointer to filler strings (optional)
260 ;
261 ; On exit: R0 == Pointer to start of buffer
262 ; R1 == Pointer to terminating null
263 ;
264 ; Use: Performs string substitution, filling in a skeleton string
265 ; containing placeholders with `filler' strings. The
266 ; placeholders are actually rather powerful. The syntax of
267 ; these is as follows:
268 ;
269 ; `%' [<type>] <digit>
270 ;
271 ; (spaces are for clarity -- in fact you must not include
272 ; spaces in the format string.)
273 ;
274 ; <digit> is any charater between `0' and `9'. It refers to
275 ; registers R2-R11 (so `0' means R2, `5' is R7 etc.) How the
276 ; value is interpreted is determined by <type>.
277 ;
278 ; <type> is one of:
279 ;
280 ; s String. This is the default. The register is
281 ; considered to be a pointer to an ASCII string
282 ; (control terminated).
283 ;
284 ; i Integer. The (signed) decimal representation is
285 ; inserted. Leading zeros are suppressed.
286 ;
287 ; x Hex fullword. The hexadecimal representation of the
288 ; register is inserted. Leading zeros are included.
289 ;
290 ; b Hex byte. The hexadecimal representation of the
291 ; least significant byte is inserted. Leading zeros
292 ; are included.
293 ;
294 ; c Character. The ASCII character corresponding to the
295 ; least significant byte is inserted.
296
297 EXPORT str_subst
298 str_subst ROUT
299
300 STMFD R13!,{R1-R11,R14}
301
302 ; --- Move arguments into more amenable registers ---
303
304 MOV R10,R0 ;Pointer to skeleton string
305
306 ; --- Main `get a character' loop ---
307
308 00str_subst LDRB R14,[R10],#1 ;Get an input character
309 CMP R14,#'%' ;Is it a `%' sign?
310 BEQ %01str_subst ;Yes -- deal with it
311 02str_subst CMP R14,#&20 ;Is it the end of input?
312 MOVLT R14,#0 ;Yes -- null terminate it
313 STRB R14,[R1],#1 ;Not special, so store it
314 BGE %00str_subst ;No -- get another one
315 SUB R1,R1,#1 ;Point to null terminator
316 LDMFD R13!,{R0,R2-R11,PC}^ ;And return to caller
317
318 ; --- Found a `%' sign, so find out what to substitute ---
319
320 01str_subst LDRB R14,[R10],#1 ;Get the next character
321
322 ; --- Now find out what we're substituting ---
323
324 ORR R9,R14,#&20 ;Convert it to lowercase
325 CMP R9,#'s' ;Is it a string?
326 CMPNE R9,#'i' ;Or an integer?
327 CMPNE R9,#'x' ;Or a fullword hex number?
328 CMPNE R9,#'b' ;Or a single byte in hex?
329 CMPNE R9,#'c' ;Or an ASCII character?
330 LDREQB R14,[R10],#1 ;And get another character
331
332 ; --- Now find which filler it is ---
333
334 CMP R14,#'0' ;Is it a digit?
335 BLT %02str_subst ;No -- just ignore the `%'
336 CMP R14,#'9' ;Make sure it's small enough
337 BGT %02str_subst ;No -- just ignore the `%'
338 SUB R14,R14,#'0'-1 ;Convert to binary (1..10)
339 LDR R0,[R13,R14,LSL #2] ;Load appropriate register
340
341 ; --- Now find out how to substitute this argument ---
342
343 MOV R2,#256 ;Buffer size -- saves space
344
345 CMP R9,#'s' ;Is it meant to be a string?
346 BEQ %03str_subst ;Yes -- a quick copy loop
347 CMP R9,#'i' ;A decimal integer?
348 BEQ %04str_subst ;Yes -- go ahead to convert
349 CMP R9,#'x' ;A hex fullword?
350 BEQ %05str_subst ;Yes -- convert that
351 CMP R9,#'b' ;A hex byte?
352 BEQ %06str_subst ;Yes -- convert that
353 CMP R9,#'c' ;A character?
354 BEQ %07str_subst ;Yes -- convert that
355
356 ; --- String substitution copy-loop ---
357
358 03str_subst LDRB R14,[R0],#1 ;Get an input byte
359 CMP R14,#&20 ;Is it the end of the string?
360 BLT %00str_subst ;Yes -- read main string
361 STRB R14,[R1],#1 ;No -- store it in output
362 B %03str_subst ;... and get another one
363
364 ; --- Decimal integer conversion ---
365
366 04str_subst SWI OS_ConvertInteger4 ;Convert and update nicely
367 B %00str_subst ;And rejoin the main loop
368
369 ; --- Hexadecimal fullword conversion ---
370
371 05str_subst SWI OS_ConvertHex8 ;Convert and update nicely
372 B %00str_subst ;And rejoin the main loop
373
374 ; --- Hexadecimal byte conversion ---
375
376 06str_subst SWI OS_ConvertHex2 ;Convert and update nicely
377 B %00str_subst ;And rejoin the main loop
378
379 ; --- ASCII character conversion ---
380
381 07str_subst STRB R0,[R1],#1 ;Store the byte in
382 B %00str_subst ;And rejoin the main loop
383
384 LTORG
385
386 ; --- str_error ---
387 ;
388 ; On entry: R0 == Pointer to skeleton
389 ; R2-R11 == Pointers to fillin strings
390 ;
391 ; On exit: R0 == Pointer to error in buffer
392 ; R1 == Pointer to terminator
393 ;
394 ; Use: Fills in an error skeleton (containing a 4 byte error number
395 ; and a control terminated skeleton string as for str_subst)
396 ; and returns the address of the filled in error block. The
397 ; error block is stored in a buffer obtained from str_buffer.
398 ;
399 ; Filler strings may be held in the scratchpad.
400
401 EXPORT str_error
402 str_error ROUT
403
404 STMFD R13!,{R14} ;Store the link register
405 BL str_buffer ;Find a spare buffer
406 LDR R14,[R0],#4 ;Get the error number
407 STR R14,[R1],#4 ;Output it too
408 BL str_subst ;Do the string substitution
409 SUB R0,R0,#4 ;Point to the error start
410 LDMFD R13!,{PC}^ ;Return to caller
411
412 LTORG
413
414 str__wSpace DCD 0 ;Pointer to error buffer
415
416 ; ---- str_buffer ---
417 ;
418 ; On entry: --
419 ;
420 ; On exit: R1 == pointer to the next free buffer
421 ;
422 ; Use: Returns a pointer to a 256-byte buffer. There are at present
423 ; 2 buffers, which are returned alternately.
424
425 EXPORT str_buffer
426 str_buffer ROUT
427
428 STMFD R13!,{R14} ;Save a work register
429
430 ; --- Work out which buffer to use ---
431 ;
432 ; This uses some vaguely clever tricks, so watch out [mdw]
433 ; In fact, the C compiler used exactly the same tricks when
434 ; tried `return (buffer+256*(count^=1))'.
435
436 WSPACE str__wSpace,R1 ;Find workspace address
437 LDR R14,[R1,#0] ;Get the current buffer
438 EOR R14,R14,#1 ;Toggle the buffer number
439 STR R14,[R1],#4 ;Store the new one back
440 ADD R1,R1,R14,LSL #8 ;Point to correct buffer
441 LDMFD R13!,{PC}^ ;Return to caller
442
443 LTORG
444
445 ;----- Workspace ------------------------------------------------------------
446
447 str__buffers EQU 2 ;Use two buffers for now
448
449 ^ 0 ;Don't tie it to R12
450 str__wStart # 0
451
452 str__buffNum # 4
453 str__buffer # 256*str__buffers ;The number of buffers I want
454
455 str__wSize EQU {VAR}-str__wStart
456
457 AREA |Sapphire$$LibData|,CODE,READONLY
458
459 DCD str__wSize ;For the error buffer
460 DCD str__wSpace ;Pointer to the pointer
461 DCD 0 ;Don't use the scratchpad
462 DCD 0 ;No initialisation reqd.
463
464 ;----- That's all folks -----------------------------------------------------
465
466 END