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