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 38int_count: 39 .space 4 /* interrupt count modulo 16 */ 40int_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 99deareg: 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 */ 113savea: /* 0: No arg regs saved */ 114 115dera: 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 122desreg: 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 129leave: jr t8 /* Exit to unmodified EPC */ 130 nop /* Urgh - the only nop!! */ 131 132doexf0: mtc1 v0,$f0 /* Copy float value */ 133 b doex2 134 135doexf1: mtc1 v1,$f0 /* Copy double value */ 136 mtc1 v0,$f1 137 b doex2 138 139doexit: slt t2, t1, 21 140 beq t2, zero, doexf0 141 slt t2, t1, 25 142 beq t2, zero, doexf1 143 144doex2: 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 150dxsreg: 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 157leavex: jr ra /* Exit to ra */ 158 addu sp, 32 /* Clean up stack pointer */ 159 160/* Come here for exceptions we can't handle. */ 161 162ri_in_32: 163other_ri: 164check_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 201baseaddr: 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???? */ 207copyloop: 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 242res_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???? */ 248res_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 268software_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 282