Initial revision
[ssr] / StraySrc / Libraries / Core / s / xswi
1 ;
2 ; xswi.s
3 ;
4 ; SWI and routine veneers
5 ;
6 ; © 1996-1998 Straylight
7 ;
8
9 ;----- Licensing note -------------------------------------------------------
10 ;
11 ; This file is part of Straylight's core libraries (corelib)
12 ;
13 ; Corelib 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 ; Corelib 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 Corelib. If not, write to the Free Software Foundation,
25 ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26
27 ;----- Options provided -----------------------------------------------------
28 ;
29 ; OPT_CALL Build code for calling local assembler code
30 ; OPT_SAPPHIRE Do Sapphire R11/sl veneering
31
32 MACRO
33 DCLOPT $var
34 [ :DEF:$var
35 $var SETL {TRUE}
36 |
37 GBLL $var
38 $var SETL {FALSE}
39 ]
40 MEND
41
42 DCLOPT OPT_CALL
43 DCLOPT OPT_SAPPHIRE
44
45 ;----- Main code ------------------------------------------------------------
46
47 GET libs:s.swihack
48 [ :LNOT::DEF:swix__dfn
49
50 ; --- _swi, _swix, _call, _callx ---
51 ;
52 ; On entry: R0 == SWI number (for _swi[x]) or address (for _call[x])
53 ; R1 == feature flags:
54 ; 0--9 == input registers
55 ; 11 == local block flag
56 ; 12--15 == local block register
57 ; 16--19 == return register (_call and _swi only)
58 ; 31--22 == output registers
59 ; R2, R3 and stack contain other arguments
60 ;
61 ; On exit: R0 == return value, or error indicator
62 ;
63 ; Use: Calls a SWI or assembler routine.
64
65 EXPORT |_swi|
66 EXPORT |_swix|
67
68 ; --- How this works ---
69 ;
70 ; The old version of this code used to build some neat code
71 ; on the stack and then execute it. This new spiffy version
72 ; just saves data on the stack, because building code is a
73 ; major no-no on the StrongARM.
74 ;
75 ; The data stacked is as follows:
76 ;
77 ; PC -- address to call
78 ; R14 -- return address
79 ; R12 -- saved R12 value
80 ; R11 -- maybe Sapphire application context
81 ; R10 -- maybe the SWI number
82 ;
83 ; Because I can't use dynamic code at all, I'm having to
84 ; use some really nasty speed hacks here.
85
86 ; --- SWI entries ---
87
88 |_swi| STMFD R13!,{R2,R3} ;Stack all variable args
89 STMFD R13!,{R4-R12,R14} ;Save other registers
90 ADR R12,|_swi_nonx| ;Point to non-X entry point
91 MOV R9,R0 ;Fetch the SWI number
92 LDR R0,|_swihack| ;Point to SWI calling routine
93 B |_swi_main| ;Skip onwards to main code
94
95 |_swix| STMFD R13!,{R2,R3} ;Stack all variable args
96 STMFD R13!,{R4-R12,R14} ;Save other registers
97 BIC R1,R1,#&000F0000 ;Return R0 value no matter
98 ADR R12,|_swi_x| ;Point to X entry point
99 ORR R9,R0,#&20000 ;Set the X bit on the SWI
100 LDR R0,|_swihack| ;Point to SWI calling routine
101 B |_swi_main| ;Skip onwards to main code
102
103 ; --- Code entries ---
104
105 [ OPT_CALL
106
107 EXPORT |_call|
108 EXPORT |_callx|
109
110 |_call| STMFD R13!,{R2,R3} ;Stack all variable args
111 STMFD R13!,{R4-R12,R14} ;Save other registers
112 ADR R12,|_swi_nonx| ;Point to non-X entry point
113 [ OPT_SAPPHIRE
114 MOV R11,R10 ;Get scratchpad for Sapphire
115 ]
116 B |_swi_main| ;Skip onwards to main code
117
118 |_callx| STMFD R13!,{R2,R3} ;Stack all variable args
119 STMFD R13!,{R4-R12,R14} ;Save other registers
120 BIC R1,R1,#&000F0000 ;Return R0 value no matter
121 ADR R12,|_swi_x| ;Point to X entry point
122 [ OPT_SAPPHIRE
123 MOV R11,R10 ;Get scratchpad for Sapphire
124 ]
125 B |_swi_main| ;Skip onwards to main code
126
127 ]
128
129 ; --- First job is to set up the call address ---
130
131 |_swi_main| MOV R14,PC ;Get the current CPU flags
132 AND R14,R14,#&0C000003 ;Leave interrupts and mode
133 ORR R12,R12,R14 ;Set the return address
134 ORR R14,R0,R14 ;And the call address
135 SUB R13,R13,#8 ;Make a hole in the stack
136 STMFD R13!,{R9-R12,R14} ;Save R10-R12 and PC (fake)
137
138 ; --- Set up the input registers ---
139 ;
140 ; Unrolled loop to do two registers at a time. There are
141 ; frequent exits while scanning the early registers to
142 ; speed up common cases, petering out towards the end.
143
144 MOV R10,R1 ;Fetch the feature flags
145 ADD R12,R13,#68 ;Point to arguments
146
147 MOVS R14,R10,LSL #31
148 LDRMI R0,[R12],#4
149 LDRCS R1,[R12],#4
150 TST R10,#&3FC
151 BEQ %f00
152 MOVS R14,R10,LSL #29
153 LDRMI R2,[R12],#4
154 LDRCS R3,[R12],#4
155 TST R10,#&3F0
156 BEQ %f00
157 MOVS R14,R10,LSL #27
158 LDRMI R4,[R12],#4
159 LDRCS R5,[R12],#4
160 TST R10,#&3C0
161 BEQ %f00
162 MOVS R14,R10,LSL #25
163 LDRMI R6,[R12],#4
164 LDRCS R7,[R12],#4
165 MOVS R14,R10,LSL #23
166 LDRMI R8,[R12],#4
167 LDRCS R9,[R12],#4
168 00
169 ; --- Now sort out block arguments ---
170
171 ADD R14,R13,#20 ;Find the hole in the stack
172 STMIA R14,{R10,R12} ;Save important context
173 TST R10,#&800 ;Do we have a block argument?
174 BNE |_swi_block| ;Yes -- sort out out-of-line
175 LDMFD R13!,{R10-R12,R14,PC}^ ;And call the routine
176
177 ; --- X-type return ---
178
179 |_swi_x| LDMFD R13!,{R10,R12} ;Reload important context
180 MOVVC R14,#0 ;If no error, return zero
181 MOVVS R14,R0 ;Otherwise point to the error
182 STR R14,[R13,#-4]! ;Store as return value
183 B |_swi_output| ;And skip on a little
184
185 ; --- Non-X-type return ---
186 ;
187 ; Pick out the correct register with a branch table. Also
188 ; invert the table to pick out common case of return R0.
189
190 |_swi_nonx| LDMFD R13!,{R10,R12} ;Reload important context
191 MOV R14,#&F ;A nice bitmask
192 AND R14,R14,R10,LSR #16 ;So mask the return register
193 RSB R14,R14,#&F ;Invert range hackily
194 ADD PC,PC,R14,LSL #3 ;And dispatch nicely
195 DCB "hack"
196
197 STR PC,[R13,#-4]!
198 B |_swi_output|
199 STR R14,[R13,#-4]!
200 B |_swi_output|
201 STR R13,[R13,#-4]!
202 B |_swi_output|
203 STR R12,[R13,#-4]!
204 B |_swi_output|
205 STR R11,[R13,#-4]!
206 B |_swi_output|
207 STR R10,[R13,#-4]!
208 B |_swi_output|
209 STR R9,[R13,#-4]!
210 B |_swi_output|
211 STR R8,[R13,#-4]!
212 B |_swi_output|
213 STR R7,[R13,#-4]!
214 B |_swi_output|
215 STR R6,[R13,#-4]!
216 B |_swi_output|
217 STR R5,[R13,#-4]!
218 B |_swi_output|
219 STR R4,[R13,#-4]!
220 B |_swi_output|
221 STR R3,[R13,#-4]!
222 B |_swi_output|
223 STR R2,[R13,#-4]!
224 B |_swi_output|
225 STR R1,[R13,#-4]!
226 B |_swi_output|
227 STR R0,[R13,#-4]!
228
229 ; --- Now handle output parameters ---
230 ;
231 ; Same style as the input parameters, with early exits
232 ; placed conveniently.
233
234 |_swi_output| MOV R11,PC ;Get the CPU flags
235
236 TST R10,#&FF000000 ;Are there any output args?
237 TSTEQ R10,#&00E00000
238 BEQ %f10 ;No -- skip onwards then
239
240 MOVS R14,R10,LSL #1
241 LDRCS R14,[R12],#4
242 STRCS R0,[R14,#0]
243 LDRMI R14,[R12],#4
244 STRMI R1,[R14,#0]
245 MOVS R14,R10,LSL #3
246 LDRCS R14,[R12],#4
247 STRCS R2,[R14,#0]
248 LDRMI R14,[R12],#4
249 STRMI R3,[R14,#0]
250 TST R10,#&0FC00000
251 BEQ %f00
252 MOVS R14,R10,LSL #5
253 LDRCS R14,[R12],#4
254 STRCS R4,[R14,#0]
255 LDRMI R14,[R12],#4
256 STRMI R5,[R14,#0]
257 TST R10,#&03C00000
258 BEQ %f00
259 MOVS R14,R10,LSL #7
260 LDRCS R14,[R12],#4
261 STRCS R6,[R14,#0]
262 LDRMI R14,[R12],#4
263 STRMI R7,[R14,#0]
264 MOVS R14,R10,LSL #9
265 LDRCS R14,[R12],#4
266 STRCS R8,[R14,#0]
267 LDRMI R14,[R12],#4
268 STRMI R9,[R14,#0]
269 00
270 ; --- Handle returning flags ---
271
272 TST R10,#&00200000
273 LDRNE R14,[R12],#4
274 STRNE R11,[R14,#0]
275 10
276 LDMFD R13!,{R0,R4-R12,R14}
277 ADD R13,R13,#8
278 MOVS PC,R14
279
280 ; --- Handle block arguments ---
281 ;
282 ; Shift output registers to the right to find the block.
283 ; Then dispatch through a branch table to store in the right
284 ; register. All the registers from R10 upwards are on the
285 ; stack so they can be restored easily.
286
287 |_swi_block| MOV R11,R10,LSR #22 ;Mask off output registers
288 MOV R11,R11,LSL #21 ;Shift down one place
289 MOV R14,R12 ;Preserve R12 here
290 00 MOVS R11,R11,LSL #2 ;Shift into C and N flags
291 ADDCS R14,R14,#4 ;If C set, bump counter
292 ADDMI R14,R14,#4 ;If N set, bump counter
293 BNE %b00 ;And loop back until done
294 AND R11,R10,#&0000F000 ;Fetch the right argument
295 ADD PC,PC,R11,LSR #9 ;Dispatch through branch tbl
296 DCB "hack"
297
298 ; --- Main dispatch table ---
299 ;
300 ; This is now just a branch off the main routine, so I
301 ; can just call the SWI/routine appropriately rather than
302 ; returning to the caller. This gives me an extra register
303 ; to play with above, which helps.
304
305 MOV R0,R14
306 LDMFD R13!,{R10-R12,R14,PC}^
307 MOV R1,R14
308 LDMFD R13!,{R10-R12,R14,PC}^
309 MOV R2,R14
310 LDMFD R13!,{R10-R12,R14,PC}^
311 MOV R3,R14
312 LDMFD R13!,{R10-R12,R14,PC}^
313 MOV R4,R14
314 LDMFD R13!,{R10-R12,R14,PC}^
315 MOV R5,R14
316 LDMFD R13!,{R10-R12,R14,PC}^
317 MOV R6,R14
318 LDMFD R13!,{R10-R12,R14,PC}^
319 MOV R7,R14
320 LDMFD R13!,{R10-R12,R14,PC}^
321 MOV R8,R14
322 LDMFD R13!,{R10-R12,R14,PC}^
323 MOV R9,R14
324 LDMFD R13!,{R10-R12,R14,PC}^
325
326 ; --- For safety, handle daft values of the parameter ---
327
328 LDMFD R13!,{R10-R12,R14,PC}^
329 DCB "daft"
330 LDMFD R13!,{R10-R12,R14,PC}^
331 DCB "daft"
332 LDMFD R13!,{R10-R12,R14,PC}^
333 DCB "daft"
334 LDMFD R13!,{R10-R12,R14,PC}^
335 DCB "daft"
336 LDMFD R13!,{R10-R12,R14,PC}^
337 DCB "daft"
338 LDMFD R13!,{R10-R12,R14,PC}^
339 DCB "daft"
340
341 LTORG
342
343 ]
344
345 ;----- That's all, folks ----------------------------------------------------
346
347 END