]>
Commit | Line | Data |
---|---|---|
1 | /* entry.S - exception handler for emulating MIPS16 'entry' and 'exit' | |
2 | pseudo-instructions. These instructions are generated by the compiler | |
3 | when the -mentry switch is used. The instructions are not implemented | |
4 | in the MIPS16 CPU; hence the exception handler that emulates them. | |
5 | ||
6 | This module contains the following public functions: | |
7 | ||
8 | * void __install_entry_handler(void); | |
9 | ||
10 | This function installs the entry/exit exception handler. It should | |
11 | be called before executing any MIPS16 functions that were compiled with | |
12 | -mentry, typically before main() is called. | |
13 | ||
14 | * void __remove_entry_handler(void); | |
15 | ||
16 | This function removes the entry/exit exception handler. It should | |
17 | be called when the program is exiting, or when it is known that no | |
18 | more MIPS16 functions compiled with -mentry will be called. | |
19 | */ | |
20 | ||
21 | #ifdef __mips16 | |
22 | /* This file contains 32 bit assembly code. */ | |
23 | .set nomips16 | |
24 | #endif | |
25 | ||
26 | #include "regs.S" | |
27 | ||
28 | #define CAUSE_EXCMASK 0x3c /* mask for ExcCode in Cause Register */ | |
29 | #define EXC_RI 0x28 /* 101000 == 10 << 2 */ | |
30 | ||
31 | /* Set DEBUG to 1 to enable recording of the last 16 interrupt causes. */ | |
32 | ||
33 | #define DEBUG 0 | |
34 | ||
35 | #if DEBUG | |
36 | ||
37 | .sdata | |
38 | int_count: | |
39 | .space 4 /* interrupt count modulo 16 */ | |
40 | int_cause: | |
41 | .space 4*16 /* last 16 interrupt causes */ | |
42 | #endif | |
43 | ||
44 | .text | |
45 | ||
46 | .set noreorder /* Do NOT reorder instructions */ | |
47 | ||
48 | ||
49 | /* __entry_exit_handler - the reserved instruction exception handler | |
50 | that emulates the entry and exit instruction. */ | |
51 | ||
52 | __entry_exit_handler: | |
53 | .set noat /* Do NOT use at register */ | |
54 | #if DEBUG | |
55 | /* Must avoid using 'la' pseudo-op because it uses gp register, which | |
56 | may not have a good value in an exception handler. */ | |
57 | ||
58 | # la k0, int_count /* intcount = (intcount + 1) & 0xf */ | |
59 | lui k0 ,%hi(int_count) | |
60 | addiu k0, k0 ,%lo(int_count) | |
61 | lw k1, (k0) | |
62 | addiu k1, k1, 1 | |
63 | andi k1, k1, 0x0f | |
64 | sw k1, (k0) | |
65 | # la k0, int_cause /* k1 = &int_cause[intcount] */ | |
66 | lui k0, %hi(int_cause) | |
67 | addiu k0, k0, %lo(int_cause) | |
68 | sll k1, k1, 2 | |
69 | add k1, k1, k0 | |
70 | #endif | |
71 | mfc0 k0, C0_CAUSE /* Fetch cause */ | |
72 | #if DEBUG | |
73 | sw k0, -4(k1) /* Save exception cause in buffer */ | |
74 | #endif | |
75 | mfc0 k1, C0_EPC /* Check for Reserved Inst. without */ | |
76 | and k0, CAUSE_EXCMASK /* destroying any register */ | |
77 | subu k0, EXC_RI | |
78 | bne k0, zero, check_others /* Sorry, go do something else */ | |
79 | ||
80 | and k0, k1, 1 /* Check for TR mode (pc.0 = 1) */ | |
81 | beq k0, zero, ri_in_32 /* Sorry, RI in 32-bit mode */ | |
82 | xor k1, 1 | |
83 | ||
84 | /* Since we now are going to emulate or die, we can use all the T-registers */ | |
85 | /* that MIPS16 does not use (at, t0-t8), and we don't have to save them. */ | |
86 | ||
87 | .set at /* Now it's ok to use at again */ | |
88 | ||
89 | #if 0 | |
90 | j leave | |
91 | rfe | |
92 | #endif | |
93 | ||
94 | lhu t0, 0(k1) /* Fetch the offending instruction */ | |
95 | xor t8, k1, 1 /* Prepare t8 for exit */ | |
96 | and t1, t0, 0xf81f /* Check for entry/exit opcode */ | |
97 | bne t1, 0xe809, other_ri | |
98 | ||
99 | deareg: and t1, t0, 0x0700 /* Isolate the three a-bits */ | |
100 | srl t1, 6 /* Adjust them so x4 is applied */ | |
101 | slt t2, t1, 17 /* See if this is the exit instruction */ | |
102 | beqz t2, doexit | |
103 | la t2, savea | |
104 | subu t2, t1 | |
105 | jr t2 /* Jump into the instruction table */ | |
106 | rfe /* We run the rest in user-mode */ | |
107 | ||
108 | /* This is the entry instruction! */ | |
109 | sw a3, 12(sp) /* 4: a0-a3 saved */ | |
110 | sw a2, 8(sp) /* 3: a0-a2 saved */ | |
111 | sw a1, 4(sp) /* 2: a0-a1 saved */ | |
112 | sw a0, 0(sp) /* 1: a0 saved */ | |
113 | savea: /* 0: No arg regs saved */ | |
114 | ||
115 | dera: and t1, t0, 0x0020 /* Isolate the save-ra bit */ | |
116 | move t7, sp /* Temporary SP */ | |
117 | beq t1, zero, desreg | |
118 | subu sp, 32 /* Default SP adjustment */ | |
119 | sw ra, -4(t7) | |
120 | subu t7, 4 | |
121 | ||
122 | desreg: and t1, t0, 0x00c0 /* Isolate the two s-bits */ | |
123 | beq t1, zero, leave | |
124 | subu t1, 0x0040 | |
125 | beq t1, zero, leave /* Only one to save... */ | |
126 | sw s0, -4(t7) /* Do the first one */ | |
127 | sw s1, -8(t7) /* Do the last one */ | |
128 | ||
129 | leave: jr t8 /* Exit to unmodified EPC */ | |
130 | nop /* Urgh - the only nop!! */ | |
131 | ||
132 | doexf0: mtc1 v0,$f0 /* Copy float value */ | |
133 | b doex2 | |
134 | ||
135 | doexf1: mtc1 v1,$f0 /* Copy double value */ | |
136 | mtc1 v0,$f1 | |
137 | b doex2 | |
138 | ||
139 | doexit: slt t2, t1, 21 | |
140 | beq t2, zero, doexf0 | |
141 | slt t2, t1, 25 | |
142 | beq t2, zero, doexf1 | |
143 | ||
144 | doex2: and t1, t0, 0x0020 /* Isolate ra bit */ | |
145 | beq t1, zero, dxsreg /* t1 holds ra-bit */ | |
146 | addu t7, sp, 32 /* Temporary SP */ | |
147 | lw ra, -4(t7) | |
148 | subu t7, 4 | |
149 | ||
150 | dxsreg: and t1, t0, 0x00c0 /* Isolate the two s-bits */ | |
151 | beq t1, zero, leavex | |
152 | subu t1, 0x0040 | |
153 | beq t1, zero, leavex /* Only one to save... */ | |
154 | lw s0, -4(t7) /* Do the first one */ | |
155 | lw s1, -8(t7) /* Do the last one */ | |
156 | ||
157 | leavex: jr ra /* Exit to ra */ | |
158 | addu sp, 32 /* Clean up stack pointer */ | |
159 | ||
160 | /* Come here for exceptions we can't handle. */ | |
161 | ||
162 | ri_in_32: | |
163 | other_ri: | |
164 | check_others: /* call the previous handler */ | |
165 | la k0,__previous | |
166 | jr k0 | |
167 | nop | |
168 | ||
169 | __exception_code: | |
170 | .set noreorder | |
171 | la k0, __entry_exit_handler | |
172 | # lui k0, %hi(exception) | |
173 | # addiu k0, k0, %lo(exception) | |
174 | jr k0 | |
175 | nop | |
176 | .set reorder | |
177 | __exception_code_end: | |
178 | ||
179 | .data | |
180 | __previous: | |
181 | .space (__exception_code_end - __exception_code) | |
182 | .text | |
183 | ||
184 | ||
185 | /* void __install_entry_handler(void) | |
186 | ||
187 | Install our entry/exit reserved instruction exception handler. | |
188 | */ | |
189 | .ent __install_entry_handler | |
190 | .globl __install_entry_handler | |
191 | __install_entry_handler: | |
192 | .set noreorder | |
193 | mfc0 a0,C0_SR | |
194 | nop | |
195 | li a1,SR_BEV | |
196 | and a1,a1,a0 | |
197 | beq a1,$0,baseaddr | |
198 | lui a0,0x8000 /* delay slot */ | |
199 | lui a0,0xbfc0 | |
200 | addiu a0,a0,0x0100 | |
201 | baseaddr: | |
202 | addiu a0,a0,0x080 /* a0 = base vector table address */ | |
203 | li a1,(__exception_code_end - __exception_code) | |
204 | la a2,__exception_code | |
205 | la a3,__previous | |
206 | /* there must be a better way of doing this???? */ | |
207 | copyloop: | |
208 | lw v0,0(a0) | |
209 | sw v0,0(a3) | |
210 | lw v0,0(a2) | |
211 | sw v0,0(a0) | |
212 | addiu a0,a0,4 | |
213 | addiu a2,a2,4 | |
214 | addiu a3,a3,4 | |
215 | subu a1,a1,4 | |
216 | bne a1,$0,copyloop | |
217 | nop | |
218 | j ra | |
219 | nop | |
220 | .set reorder | |
221 | .end __install_entry_handler | |
222 | ||
223 | ||
224 | /* void __remove_entry_handler(void); | |
225 | ||
226 | Remove our entry/exit reserved instruction exception handler. | |
227 | */ | |
228 | ||
229 | .ent __remove_entry_handler | |
230 | .globl __remove_entry_handler | |
231 | __remove_entry_handler: | |
232 | .set noreorder | |
233 | ||
234 | mfc0 a0,C0_SR | |
235 | nop | |
236 | li a1,SR_BEV | |
237 | and a1,a1,a0 | |
238 | beq a1,$0,res_baseaddr | |
239 | lui a0,0x8000 /* delay slot */ | |
240 | lui a0,0xbfc0 | |
241 | addiu a0,a0,0x0200 | |
242 | res_baseaddr: | |
243 | addiu a0,a0,0x0180 /* a0 = base vector table address */ | |
244 | li a1,(__exception_code_end - __exception_code) | |
245 | la a3,__previous | |
246 | ||
247 | /* there must be a better way of doing this???? */ | |
248 | res_copyloop: | |
249 | lw v0,0(a3) | |
250 | sw v0,0(a0) | |
251 | addiu a0,a0,4 | |
252 | addiu a3,a3,4 | |
253 | subu a1,a1,4 | |
254 | bne a1,$0,res_copyloop | |
255 | nop | |
256 | j ra | |
257 | nop | |
258 | .set reorder | |
259 | .end __remove_entry_handler | |
260 | ||
261 | ||
262 | /* software_init_hook - install entry/exit handler and arrange to have it | |
263 | removed at exit. This function is called by crt0.S. */ | |
264 | ||
265 | .text | |
266 | .globl software_init_hook | |
267 | .ent software_init_hook | |
268 | software_init_hook: | |
269 | .set noreorder | |
270 | subu sp, sp, 8 /* allocate stack space */ | |
271 | sw ra, 4(sp) /* save return address */ | |
272 | jal __install_entry_handler /* install entry/exit handler */ | |
273 | nop | |
274 | lui a0, %hi(__remove_entry_handler) /* arrange for exit to */ | |
275 | jal atexit /* de-install handler */ | |
276 | addiu a0, a0, %lo(__remove_entry_handler) /* delay slot */ | |
277 | lw ra, 4(sp) /* get return address */ | |
278 | j ra /* return */ | |
279 | addu sp, sp, 8 /* deallocate stack */ | |
280 | .set reorder | |
281 | .end software_init_hook |