Initial revision
[ssr] / StraySrc / Libraries / BAS / src / s / string
1 ;
2 ; string.s
3 ;
4 ; String handling routines (control terminated)
5 ;
6 ; © 1994-1998 Straylight
7 ;
8
9 ;----- Licensing note -------------------------------------------------------
10 ;
11 ; This file is part of Straylight's BASIC Assembler Supplement.
12 ;
13 ; BAS 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 ; BAS 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 BAS. 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 sh.workspace
35
36 ;----- Main code ------------------------------------------------------------
37 ;
38 ; No string routine corrupts the scratchpad.
39
40 AREA |BAS$$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_subst ---
154 ;
155 ; On entry: R0 == Pointer to skeleton
156 ; R1 == Pointer to output buffer
157 ; R2-R11 == Pointer to filler strings (optional)
158 ;
159 ; On exit: R0 == Pointer to start of buffer
160 ; R1 == Pointer to terminating null
161 ;
162 ; Use: Performs string substitution, filling in a skeleton string
163 ; containing placeholders with `filler' strings. The
164 ; placeholders are actually rather powerful. The syntax of
165 ; these is as follows:
166 ;
167 ; `%' [<type>] <digit>
168 ;
169 ; (spaces are for clarity -- in fact you must not include
170 ; spaces in the format string.)
171 ;
172 ; <digit> is any charater between `0' and `9'. It refers to
173 ; registers R2-R11 (so `0' means R2, `5' is R7 etc.) How the
174 ; value is interpreted is determined by <type>.
175 ;
176 ; <type> is one of:
177 ;
178 ; s String. This is the default. The register is
179 ; considered to be a pointer to an ASCII string
180 ; (control terminated).
181 ;
182 ; i Integer. The (signed) decimal representation is
183 ; inserted. Leading zeros are suppressed.
184 ;
185 ; x Hex fullword. The hexadecimal representation of the
186 ; register is inserted. Leading zeros are included.
187 ;
188 ; b Hex byte. The hexadecimal representation of the
189 ; least significant byte is inserted. Leading zeros
190 ; are included.
191 ;
192 ; c Character. The ASCII character corresponding to the
193 ; least significant byte is inserted.
194
195 EXPORT str_subst
196 str_subst ROUT
197
198 STMFD R13!,{R1-R11,R14}
199
200 ; --- Move arguments into more amenable registers ---
201
202 MOV R11,R0 ;Pointer to skeleton string
203
204 ; --- Main `get a character' loop ---
205
206 00str_subst LDRB R14,[R11],#1 ;Get an input character
207 CMP R14,#'%' ;Is it a `%' sign?
208 BEQ %01str_subst ;Yes -- deal with it
209 02str_subst CMP R14,#&20 ;Is it the end of input?
210 MOVLT R14,#0 ;Yes -- null terminate it
211 STRB R14,[R1],#1 ;Not special, so store it
212 BGE %00str_subst ;No -- get another one
213 SUB R1,R1,#1 ;Point to null terminator
214 LDMFD R13!,{R0,R2-R11,PC}^ ;And return to caller
215
216 ; --- Found a `%' sign, so find out what to substitute ---
217
218 01str_subst LDRB R14,[R11],#1 ;Get the next character
219
220 ; --- Now find out what we're substituting ---
221
222 ORR R9,R14,#&20 ;Convert it to lowercase
223 CMP R9,#'s' ;Is it a string?
224 CMPNE R9,#'i' ;Or an integer?
225 CMPNE R9,#'x' ;Or a fullword hex number?
226 CMPNE R9,#'b' ;Or a single byte in hex?
227 CMPNE R9,#'c' ;Or an ASCII character?
228 LDREQB R14,[R11],#1 ;And get another character
229
230 ; --- Now find which filler it is ---
231
232 CMP R14,#'0' ;Is it a digit?
233 BLT %02str_subst ;No -- just ignore the `%'
234 CMP R14,#'9' ;Make sure it's small enough
235 BGT %02str_subst ;No -- just ignore the `%'
236 SUB R14,R14,#'0'-1 ;Convert to binary (1..10)
237 LDR R0,[R13,R14,LSL #2] ;Load appropriate register
238
239 ; --- Now find out how to substitute this argument ---
240
241 MOV R2,#256 ;Buffer size -- saves space
242
243 CMP R9,#'s' ;Is it meant to be a string?
244 BEQ %03str_subst ;Yes -- a quick copy loop
245 CMP R9,#'i' ;A decimal integer?
246 BEQ %04str_subst ;Yes -- go ahead to convert
247 CMP R9,#'x' ;A hex fullword?
248 BEQ %05str_subst ;Yes -- convert that
249 CMP R9,#'b' ;A hex byte?
250 BEQ %06str_subst ;Yes -- convert that
251 CMP R9,#'c' ;A character?
252 BEQ %07str_subst ;Yes -- convert that
253
254 ; --- String substitution copy-loop ---
255
256 03str_subst LDRB R14,[R0],#1 ;Get an input byte
257 CMP R14,#&20 ;Is it the end of the string?
258 BLT %00str_subst ;Yes -- read main string
259 STRB R14,[R1],#1 ;No -- store it in output
260 B %03str_subst ;... and get another one
261
262 ; --- Decimal integer conversion ---
263
264 04str_subst SWI OS_ConvertInteger4 ;Convert and update nicely
265 B %00str_subst ;And rejoin the main loop
266
267 ; --- Hexadecimal fullword conversion ---
268
269 05str_subst SWI OS_ConvertHex8 ;Convert and update nicely
270 B %00str_subst ;And rejoin the main loop
271
272 ; --- Hexadecimal byte conversion ---
273
274 06str_subst SWI OS_ConvertHex2 ;Convert and update nicely
275 B %00str_subst ;And rejoin the main loop
276
277 ; --- ASCII character conversion ---
278
279 07str_subst STRB R0,[R1],#1 ;Store the byte in
280 B %00str_subst ;And rejoin the main loop
281
282 LTORG
283
284 ; --- str_error ---
285 ;
286 ; On entry: R0 == Pointer to skeleton
287 ; R2-R11 == Pointers to fillin strings
288 ;
289 ; On exit: R0 == Pointer to error in buffer
290 ; R1 == Pointer to terminator
291 ;
292 ; Use: Fills in an error skeleton (containing a 4 byte error number
293 ; and a control terminated skeleton string as for str_subst)
294 ; and returns the address of the filled in error block. The
295 ; error block is stored in a buffer obtained from str_buffer.
296 ;
297 ; Filler strings may be held in the scratchpad.
298
299 EXPORT str_error
300 str_error ROUT
301
302 STMFD R13!,{R14} ;Store the link register
303 BL str_buffer ;Find a spare buffer
304 LDR R14,[R0],#4 ;Get the error number
305 STR R14,[R1],#4 ;Output it too
306 BL str_subst ;Do the string substitution
307 SUB R0,R0,#4 ;Point to the error start
308 LDMFD R13!,{PC}^ ;Return to caller
309
310 LTORG
311
312 ; ---- str_buffer ---
313 ;
314 ; On entry: --
315 ;
316 ; On exit: R1 == pointer to the next free buffer
317 ;
318 ; Use: Returns a pointer to a 256-byte buffer. There are at present
319 ; 2 buffers, which are returned alternately.
320
321 EXPORT str_buffer
322 str_buffer ROUT
323
324 STMFD R13!,{R14} ;Save a work register
325 LDR R14,str__buffNum ;Get the current buffer
326 ADD R14,R14,#1 ;Bump the buffer count
327 CMP R14,#3 ;Have we gone too far?
328 MOVCS R14,#0 ;Yes -- go back then
329 STR R14,str__buffNum ;Store the new one back
330 ADRL R1,str__buffer ;Point to the actual buffer
331 ADD R1,R1,R14,LSL #8 ;Point to correct buffer
332 LDMFD R13!,{PC}^ ;Return to caller
333
334 LTORG
335
336 ;----- That's all folks -----------------------------------------------------
337
338 END