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