Initial revision
[ssr] / StraySrc / Libraries / Quartz / s / string
1 ;
2 ; string.s
3 ;
4 ; String handling routines (control terminated) (TMA)
5 ;
6 ; © 1994-1998 Straylight
7 ;
8
9 ;----- Licensing note -------------------------------------------------------
10 ;
11 ; This file is part of Straylight's Quartz library.
12 ;
13 ; Quartz 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 ; Quartz 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 Quartz. 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 ; None.
35
36 ;----- Main code ------------------------------------------------------------
37 ;
38 ; No string routine corrupts the scratchpad.
39
40 AREA |Quartz$$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_qcpy ---
72 ;
73 ; On entry: R0 == destination pointer
74 ; R1 == source pointer
75 ;
76 ; On exit: R0 == pointer to terminator of destination
77 ;
78 ; Use: This copies the source string to the destination. It is
79 ; much faster than str_cpy if boths the string are word
80 ; aligned. Note that it only works with NULL terminated
81 ; strings.
82
83 EXPORT str_qcpy
84 str_qcpy ROUT
85
86 STMFD R13!,{R1-R5,R14} ;Stack some registers
87
88 ; --- Are both strings word aligned? ---
89
90 ORR R2,R0,R1 ;OR them together
91 ANDS R2,R2,#3 ;Is bit 0 or 1 set?
92 BNE %10str_qcpy ;Yes, do non-word aligned cpy
93
94 ; --- Set up bit structures ---
95
96 MOV R3,#&01 ;Set bit 0
97 ADD R3,R3,#&100 ;Set bits 0 and 8
98 ADD R3,R3,R3,ROR #16 ;Set LSB of each byte in word
99
100 MOV R4,#&80 ;Set bit 7
101 ADD R4,R4,#&8000 ;Set bit 15
102 ADD R4,R4,R4,ROR #16 ;Set MSB of each byte in word
103
104 ; --- Branch to word loading routine ---
105
106 B %02str_qcpy ;Branch ahead
107
108 ; --- Copy the loaded word ---
109
110 01str_qcpy STR R2,[R0],#4 ;Store word
111
112 ; --- Load a word, and check if any byte in it is 0 ---
113 ;
114 ; The algorithm is actually quite simple. By looking at
115 ; each byte in the word being loaded individually,
116 ; if we subtract 1 from it, the MSB of that byte is set
117 ; if the byte was 0 to start with (ie. it becomes -1).
118 ; However, the MSB may also be set if it was set to
119 ; start with. If this is the case then we clear it
120 ; after the subtract operation.
121 ;
122 ; This is why R3 is set up as it is. R4 can then be used
123 ; to see if any of MSB is set -- if so, then at least
124 ; one of the bytes was initially 0 :-)
125
126 02str_qcpy LDR R2,[R1],#4 ;Load a word
127 SUB R5,R2,R3 ;Subtract 1 from each byte
128 BIC R5,R5,R2 ;Clear MSB(s) if set in R2
129 ANDS R5,R5,R4 ;Are any MSBs set
130 BEQ %02str_qcpy ;No, try another 4 bytes
131
132 ; --- Now do the remaining bytes ---
133
134 03str_qcpy STRB R2,[R0],#1 ;Store a byte
135 ANDS R2,R2,#&FF ;Clear the first byte
136 SUBEQ R0,R0,#1 ;Point to NULL byte if finshd
137 LDMEQIA R13!,{R1-R5,PC}^ ;And return to caller
138 MOV R2,R2,ASR #8 ;Prepare next byte
139 B %03str_qcpy ;Copy what's left
140
141 ; --- Do a non-word aligned copy ---
142
143 10str_qcpy LDRB R2,[R1],#1 ;Get a byte
144 STRB R2,[R0],#1 ;Store it in destination
145 CMP R2,#0 ;Is it a terminator
146 BNE %10str_qcpy ;No -- keep copying
147 SUB R0,R0,#1 ;Point to NULL
148 LDMFD R13!,{R1-R5,PC}^
149
150 LTORG
151
152 ; --- str_len ---
153 ;
154 ; On entry: R0 == pointer to string
155 ;
156 ; On exit: R0 == length of the string
157 ;
158 ; Use: Calculates the length of a string.
159
160 EXPORT str_len
161 str_len ROUT
162
163 STMFD R13!,{R1,R14} ;Save some registers
164 MOV R14,R0 ;Point to the string
165 MOV R0,#0 ;Current length is 0
166 00str_len LDRB R1,[R14],#1 ;Get a byte from the string
167 CMP R1,#' ' ;Is it the end yet?
168 LDMLTFD R13!,{R1,PC}^ ;Yes -- return
169 ADD R0,R0,#1 ;Bump the length counter
170 B %00str_len ;And go back for more
171
172 LTORG
173
174 ; --- str_cmp ---
175 ;
176 ; On entry: R0 == pointer to string A
177 ; R1 == pointer to string B
178 ;
179 ; On exit: Flags as appropriate
180 ;
181 ; Use: Case-sensitively compares two strings. You can use the
182 ; normal ARM condition codes after the compare, so you can
183 ; treat this as a normal CMP type thing (except the arguments
184 ; must be in R0 and R1, and it mangles R14).
185
186 EXPORT str_cmp
187 str_cmp ROUT
188
189 STMFD R13!,{R0,R1,R3-R5,R14}
190 00str_cmp LDRB R3,[R0],#1 ;Get a character from A
191 LDRB R4,[R1],#1 ;And one from B
192 CMP R3,#&20 ;Is that the end of A?
193 MOVLT R3,#0 ;Yes -- pretend it's null
194 CMP R4,#&20 ;Is that the end of B?
195 MOVLT R4,#0 ;Yes -- pretend it's null
196 CMP R3,R4 ;How do they match up?
197 LDMNEFD R13!,{R0,R1,R3-R5,PC} ;If NE, return condition
198 CMP R3,#0 ;Is this the end?
199 BNE %00str_cmp ;No -- loop again
200 LDMFD R13!,{R0,R1,R3-R5,PC} ;Return to caller
201
202 ; --- str_icmp ---
203 ;
204 ; On entry: R0 == pointer to string A
205 ; R1 == pointer to string B
206 ;
207 ; On exit: Flags as appropriate
208 ;
209 ; Use: As for str_cmp above, but case-insensitive.
210
211 EXPORT str_icmp
212 str_icmp ROUT
213
214 STMFD R13!,{R0,R1,R3-R5,R14}
215 ADR R5,str__caseTable ;Point to upper-case table
216 00str_icmp LDRB R3,[R0],#1 ;Get a character from A
217 LDRB R4,[R1],#1 ;And one from B
218 LDRB R3,[R5,R3] ;If so, convert using table
219 LDRB R4,[R5,R4] ;(both characters)
220 CMP R3,R4 ;How do they match up?
221 LDMNEFD R13!,{R0,R1,R3-R5,PC} ;If NE, return condition
222 CMP R3,#0 ;Is this the end?
223 BNE %00str_icmp ;No -- loop again
224 LDMFD R13!,{R0,R1,R3-R5,PC} ;Return to caller
225
226 str__caseTable DCB &00,&00,&00,&00,&00,&00,&00,&00
227 DCB &00,&00,&00,&00,&00,&00,&00,&00
228 DCB &00,&00,&00,&00,&00,&00,&00,&00
229 DCB &00,&00,&00,&00,&00,&00,&00,&00
230 DCB &20,&21,&22,&23,&24,&25,&26,&27
231 DCB &28,&29,&2A,&2B,&2C,&2D,&2E,&2F
232 DCB &30,&31,&32,&33,&34,&35,&36,&37
233 DCB &38,&39,&3A,&3B,&3C,&3D,&3E,&3F
234 DCB &40,&41,&42,&43,&44,&45,&46,&47
235 DCB &48,&49,&4A,&4B,&4C,&4D,&4E,&4F
236 DCB &50,&51,&52,&53,&54,&55,&56,&57
237 DCB &58,&59,&5A,&5B,&5C,&5D,&5E,&5F
238 DCB &60,&41,&42,&43,&44,&45,&46,&47
239 DCB &48,&49,&4A,&4B,&4C,&4D,&4E,&4F
240 DCB &50,&51,&52,&53,&54,&55,&56,&57
241 DCB &58,&59,&5A,&7B,&7C,&7D,&7E,&7F
242 DCB &80,&81,&82,&83,&84,&85,&86,&87
243 DCB &88,&89,&8A,&8B,&8C,&8D,&8E,&8F
244 DCB &90,&91,&92,&93,&94,&95,&96,&97
245 DCB &98,&99,&9A,&9B,&9C,&9D,&9E,&9F
246 DCB &A0,&A1,&A2,&A3,&A4,&A5,&A6,&A7
247 DCB &A8,&A9,&AA,&AB,&AC,&AD,&AE,&AF
248 DCB &B0,&B1,&B2,&B3,&B4,&B5,&B6,&B7
249 DCB &B8,&B9,&BA,&BB,&BC,&BD,&BE,&BF
250 DCB &C0,&C1,&C2,&C3,&C4,&C5,&C6,&C7
251 DCB &C8,&C9,&CA,&CB,&CC,&CD,&CE,&CF
252 DCB &D0,&D1,&D2,&D3,&D4,&D5,&D6,&D7
253 DCB &D8,&D9,&DA,&DB,&DC,&DD,&DE,&DF
254 DCB &E0,&E1,&E2,&E3,&E4,&E5,&E6,&E7
255 DCB &E8,&E9,&EA,&EB,&EC,&ED,&EE,&EF
256 DCB &F0,&F1,&F2,&F3,&F4,&F5,&F6,&F7
257 DCB &F8,&F9,&FA,&FB,&FC,&FD,&FE,&FF
258
259 LTORG
260
261 ; --- str_subst ---
262 ;
263 ; On entry: R0 == Pointer to skeleton
264 ; R1 == Pointer to output buffer
265 ; R2-R11 == Pointer to filler strings (optional)
266 ;
267 ; On exit: R0 == Pointer to start of buffer
268 ; R1 == Pointer to terminating null
269 ;
270 ; Use: Performs string substitution, filling in a skeleton string
271 ; containing placeholders with `filler' strings. The
272 ; placeholders are actually rather powerful. The syntax of
273 ; these is as follows:
274 ;
275 ; `%' [<type>] <digit>
276 ;
277 ; (spaces are for clarity -- in fact you must not include
278 ; spaces in the format string.)
279 ;
280 ; <digit> is any charater between `0' and `9'. It refers to
281 ; registers R2-R11 (so `0' means R2, `5' is R7 etc.) How the
282 ; value is interpreted is determined by <type>.
283 ;
284 ; <type> is one of:
285 ;
286 ; s String. This is the default. The register is
287 ; considered to be a pointer to an ASCII string
288 ; (control terminated).
289 ;
290 ; i Integer. The (signed) decimal representation is
291 ; inserted. Leading zeros are suppressed.
292 ;
293 ; x Hex fullword. The hexadecimal representation of the
294 ; register is inserted. Leading zeros are included.
295 ;
296 ; b Hex byte. The hexadecimal representation of the
297 ; least significant byte is inserted. Leading zeros
298 ; are included.
299 ;
300 ; c Character. The ASCII character corresponding to the
301 ; least significant byte is inserted.
302
303 EXPORT str_subst
304 str_subst ROUT
305
306 STMFD R13!,{R1-R11,R14}
307
308 ; --- Move arguments into more amenable registers ---
309
310 MOV R11,R0 ;Pointer to skeleton string
311
312 ; --- Main `get a character' loop ---
313
314 00str_subst LDRB R14,[R11],#1 ;Get an input character
315 CMP R14,#'%' ;Is it a `%' sign?
316 BEQ %01str_subst ;Yes -- deal with it
317 02str_subst CMP R14,#&20 ;Is it the end of input?
318 MOVLT R14,#0 ;Yes -- null terminate it
319 STRB R14,[R1],#1 ;Not special, so store it
320 BGE %00str_subst ;No -- get another one
321 SUB R1,R1,#1 ;Point to null terminator
322 LDMFD R13!,{R0,R2-R11,PC}^ ;And return to caller
323
324 ; --- Found a `%' sign, so find out what to substitute ---
325
326 01str_subst LDRB R14,[R11],#1 ;Get the next character
327
328 ; --- Now find out what we're substituting ---
329
330 ORR R9,R14,#&20 ;Convert it to lowercase
331 CMP R9,#'s' ;Is it a string?
332 CMPNE R9,#'i' ;Or an integer?
333 CMPNE R9,#'x' ;Or a fullword hex number?
334 CMPNE R9,#'b' ;Or a single byte in hex?
335 CMPNE R9,#'c' ;Or an ASCII character?
336 LDREQB R14,[R11],#1 ;And get another character
337
338 ; --- Now find which filler it is ---
339
340 CMP R14,#'0' ;Is it a digit?
341 BLT %02str_subst ;No -- just ignore the `%'
342 CMP R14,#'9' ;Make sure it's small enough
343 BGT %02str_subst ;No -- just ignore the `%'
344 SUB R14,R14,#'0'-1 ;Convert to binary (1..10)
345 LDR R0,[R13,R14,LSL #2] ;Load appropriate register
346
347 ; --- Now find out how to substitute this argument ---
348
349 MOV R2,#256 ;Buffer size -- saves space
350
351 CMP R9,#'s' ;Is it meant to be a string?
352 BEQ %03str_subst ;Yes -- a quick copy loop
353 CMP R9,#'i' ;A decimal integer?
354 BEQ %04str_subst ;Yes -- go ahead to convert
355 CMP R9,#'x' ;A hex fullword?
356 BEQ %05str_subst ;Yes -- convert that
357 CMP R9,#'b' ;A hex byte?
358 BEQ %06str_subst ;Yes -- convert that
359 CMP R9,#'c' ;A character?
360 BEQ %07str_subst ;Yes -- convert that
361
362 ; --- String substitution copy-loop ---
363
364 03str_subst LDRB R14,[R0],#1 ;Get an input byte
365 CMP R14,#&20 ;Is it the end of the string?
366 BLT %00str_subst ;Yes -- read main string
367 STRB R14,[R1],#1 ;No -- store it in output
368 B %03str_subst ;... and get another one
369
370 ; --- Decimal integer conversion ---
371
372 04str_subst SWI OS_ConvertInteger4 ;Convert and update nicely
373 B %00str_subst ;And rejoin the main loop
374
375 ; --- Hexadecimal fullword conversion ---
376
377 05str_subst SWI OS_ConvertHex8 ;Convert and update nicely
378 B %00str_subst ;And rejoin the main loop
379
380 ; --- Hexadecimal byte conversion ---
381
382 06str_subst SWI OS_ConvertHex2 ;Convert and update nicely
383 B %00str_subst ;And rejoin the main loop
384
385 ; --- ASCII character conversion ---
386
387 07str_subst STRB R0,[R1],#1 ;Store the byte in
388 B %00str_subst ;And rejoin the main loop
389
390 LTORG
391
392 ; --- str_error ---
393 ;
394 ; On entry: R0 == Pointer to skeleton
395 ; R2-R11 == Pointers to fillin strings
396 ;
397 ; On exit: R0 == Pointer to error in buffer
398 ; R1 == Pointer to terminator
399 ;
400 ; Use: Fills in an error skeleton (like a substitution skeleton in
401 ; str_subst above with an error number on the front) and
402 ; returns a pointer to it. The buffer used is found by calling
403 ; str_buffer (see below), so you can safely use the result of
404 ; one call as a filler string for the next.
405 ;
406 ; Filler strings may be held in the scratchpad.
407
408 EXPORT str_error
409 str_error ROUT
410
411 STMFD R13!,{R14} ;Store the link register
412 BL str_buffer ;Find a spare buffer
413 LDR R14,[R0],#4 ;Get the error number
414 STR R14,[R1],#4 ;Output it too
415 BL str_subst ;Do the string substitution
416 SUB R0,R0,#4 ;Point to the error start
417 LDMFD R13!,{PC}^ ;Return to caller
418
419 LTORG
420
421 str__wSpace DCD 0 ;Pointer to error buffer
422
423 ; ---- str_buffer ---
424 ;
425 ; On entry: --
426 ;
427 ; On exit: R1 == pointer to the next free buffer
428 ;
429 ; Use: Returns a pointer to a 256-byte buffer. There are at present
430 ; 2 buffers, which are returned alternately.
431
432 EXPORT str_buffer
433 str_buffer ROUT
434
435 STMFD R13!,{R14} ;Save a work register
436
437 ; --- Work out which buffer to use ---
438 ;
439 ; This uses some vaguely clever tricks, so watch out [mdw]
440 ; In fact, the C compiler used exactly the same tricks when
441 ; tried `return (buffer+256*(count^=1))'.
442
443 LDR R14,str__wSpace ;Find the workspace
444 LDR R1,[R14,#0] ;Get the current buffer
445 EOR R1,R1,#1 ;Toggle the buffer number
446 STR R1,[R14],#4 ;Store the new one back
447 ADD R1,R14,R1,LSL #8 ;Point to correct buffer
448 LDMFD R13!,{PC}^ ;Return to caller
449
450 LTORG
451
452 ;----- Workspace ------------------------------------------------------------
453
454 str__buffers EQU 2 ;Use two buffers for now
455
456 ^ 0 ;Don't tie it to R12
457 str__wStart # 0
458
459 str__buffNum # 4
460 str__buffer # 256*str__buffers ;The number of buffers I want
461
462 str__wSize EQU {VAR}-str__wStart
463
464 AREA |Quartz$$Table|,CODE,READONLY
465
466 DCD str__wSize ;For the error buffer
467 DCD str__wSpace ;Pointer to the pointer
468 DCD 0 ;No initialisation
469 DCD 0 ;No finalisation
470
471 ;----- That's all folks -----------------------------------------------------
472
473 END