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