4 ; String handling routines (control terminated)
6 ; © 1994-1998 Straylight
9 ;----- Licensing note -------------------------------------------------------
11 ; This file is part of Straylight's BASIC Assembler Supplement.
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)
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.
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.
27 ;----- Standard header ------------------------------------------------------
32 ;----- External dependencies ------------------------------------------------
36 ;----- Main code ------------------------------------------------------------
38 ; No string routine corrupts the scratchpad.
40 AREA |BAS$$Code|,CODE,READONLY
44 ; On entry: R0 == destination string
47 ; On exit: R0 == pointer to terminator of destination
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
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
73 ; On entry: R0 == pointer to string
75 ; On exit: R0 == length of the string
77 ; Use: Calculates the length of a string.
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
95 ; On entry: R0 == pointer to string A
96 ; R1 == pointer to string B
98 ; On exit: Flags as appropriate
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.
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
122 ; On entry: R0 == pointer to string A
123 ; R1 == pointer to string B
125 ; On exit: Flags as appropriate
127 ; Use: As for str_cmp above, but case-insensitive.
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
155 ; On entry: R0 == Pointer to skeleton
156 ; R1 == Pointer to output buffer
157 ; R2-R11 == Pointer to filler strings (optional)
159 ; On exit: R0 == Pointer to start of buffer
160 ; R1 == Pointer to terminating null
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:
167 ; `%' [<type>] <digit>
169 ; (spaces are for clarity -- in fact you must not include
170 ; spaces in the format string.)
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>.
178 ; s String. This is the default. The register is
179 ; considered to be a pointer to an ASCII string
180 ; (control terminated).
182 ; i Integer. The (signed) decimal representation is
183 ; inserted. Leading zeros are suppressed.
185 ; x Hex fullword. The hexadecimal representation of the
186 ; register is inserted. Leading zeros are included.
188 ; b Hex byte. The hexadecimal representation of the
189 ; least significant byte is inserted. Leading zeros
192 ; c Character. The ASCII character corresponding to the
193 ; least significant byte is inserted.
198 STMFD R13!,{R1-R11,R14}
200 ; --- Move arguments into more amenable registers ---
202 MOV R11,R0 ;Pointer to skeleton string
204 ; --- Main `get a character' loop ---
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
216 ; --- Found a `%' sign, so find out what to substitute ---
218 01str_subst LDRB R14,[R11],#1 ;Get the next character
220 ; --- Now find out what we're substituting ---
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
230 ; --- Now find which filler it is ---
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
239 ; --- Now find out how to substitute this argument ---
241 MOV R2,#256 ;Buffer size -- saves space
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
254 ; --- String substitution copy-loop ---
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
262 ; --- Decimal integer conversion ---
264 04str_subst SWI OS_ConvertInteger4 ;Convert and update nicely
265 B %00str_subst ;And rejoin the main loop
267 ; --- Hexadecimal fullword conversion ---
269 05str_subst SWI OS_ConvertHex8 ;Convert and update nicely
270 B %00str_subst ;And rejoin the main loop
272 ; --- Hexadecimal byte conversion ---
274 06str_subst SWI OS_ConvertHex2 ;Convert and update nicely
275 B %00str_subst ;And rejoin the main loop
277 ; --- ASCII character conversion ---
279 07str_subst STRB R0,[R1],#1 ;Store the byte in
280 B %00str_subst ;And rejoin the main loop
286 ; On entry: R0 == Pointer to skeleton
287 ; R2-R11 == Pointers to fillin strings
289 ; On exit: R0 == Pointer to error in buffer
290 ; R1 == Pointer to terminator
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.
297 ; Filler strings may be held in the scratchpad.
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
312 ; ---- str_buffer ---
316 ; On exit: R1 == pointer to the next free buffer
318 ; Use: Returns a pointer to a 256-byte buffer. There are at present
319 ; 2 buffers, which are returned alternately.
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
336 ;----- That's all folks -----------------------------------------------------