4 ; SWI and routine veneers
6 ; © 1996-1998 Straylight
9 ;----- Licensing note -------------------------------------------------------
11 ; This file is part of Straylight's core libraries (corelib)
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)
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.
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.
27 ;----- Options provided -----------------------------------------------------
29 ; OPT_CALL Build code for calling local assembler code
30 ; OPT_SAPPHIRE Do Sapphire R11/sl veneering
45 ;----- Main code ------------------------------------------------------------
48 [ :LNOT::DEF:swix__dfn
50 ; --- _swi, _swix, _call, _callx ---
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
61 ; On exit: R0 == return value, or error indicator
63 ; Use: Calls a SWI or assembler routine.
68 ; --- How this works ---
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.
75 ; The data stacked is as follows:
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
83 ; Because I can't use dynamic code at all, I'm having to
84 ; use some really nasty speed hacks here.
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
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
103 ; --- Code entries ---
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
114 MOV R11,R10 ;Get scratchpad for Sapphire
116 B |_swi_main| ;Skip onwards to main code
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
123 MOV R11,R10 ;Get scratchpad for Sapphire
125 B |_swi_main| ;Skip onwards to main code
129 ; --- First job is to set up the call address ---
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)
138 ; --- Set up the input registers ---
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.
144 MOV R10,R1 ;Fetch the feature flags
145 ADD R12,R13,#68 ;Point to arguments
169 ; --- Now sort out block arguments ---
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
177 ; --- X-type return ---
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
185 ; --- Non-X-type return ---
187 ; Pick out the correct register with a branch table. Also
188 ; invert the table to pick out common case of return R0.
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
229 ; --- Now handle output parameters ---
231 ; Same style as the input parameters, with early exits
232 ; placed conveniently.
234 |_swi_output| MOV R11,PC ;Get the CPU flags
236 TST R10,#&FF000000 ;Are there any output args?
238 BEQ %f10 ;No -- skip onwards then
270 ; --- Handle returning flags ---
276 LDMFD R13!,{R0,R4-R12,R14}
280 ; --- Handle block arguments ---
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.
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
298 ; --- Main dispatch table ---
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.
306 LDMFD R13!,{R10-R12,R14,PC}^
308 LDMFD R13!,{R10-R12,R14,PC}^
310 LDMFD R13!,{R10-R12,R14,PC}^
312 LDMFD R13!,{R10-R12,R14,PC}^
314 LDMFD R13!,{R10-R12,R14,PC}^
316 LDMFD R13!,{R10-R12,R14,PC}^
318 LDMFD R13!,{R10-R12,R14,PC}^
320 LDMFD R13!,{R10-R12,R14,PC}^
322 LDMFD R13!,{R10-R12,R14,PC}^
324 LDMFD R13!,{R10-R12,R14,PC}^
326 ; --- For safety, handle daft values of the parameter ---
328 LDMFD R13!,{R10-R12,R14,PC}^
330 LDMFD R13!,{R10-R12,R14,PC}^
332 LDMFD R13!,{R10-R12,R14,PC}^
334 LDMFD R13!,{R10-R12,R14,PC}^
336 LDMFD R13!,{R10-R12,R14,PC}^
338 LDMFD R13!,{R10-R12,R14,PC}^
345 ;----- That's all, folks ----------------------------------------------------