chiark / gitweb /
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