| /* entry.S - exception handler for emulating MIPS16 'entry' and 'exit' |
| pseudo-instructions. These instructions are generated by the compiler |
| when the -mentry switch is used. The instructions are not implemented |
| in the MIPS16 CPU; hence the exception handler that emulates them. |
| |
| This module contains the following public functions: |
| |
| * void __install_entry_handler(void); |
| |
| This function installs the entry/exit exception handler. It should |
| be called before executing any MIPS16 functions that were compiled with |
| -mentry, typically before main() is called. |
| |
| * void __remove_entry_handler(void); |
| |
| This function removes the entry/exit exception handler. It should |
| be called when the program is exiting, or when it is known that no |
| more MIPS16 functions compiled with -mentry will be called. |
| */ |
| |
| #ifdef __mips16 |
| /* This file contains 32 bit assembly code. */ |
| .set nomips16 |
| #endif |
| |
| #include "regs.S" |
| |
| #define CAUSE_EXCMASK 0x3c /* mask for ExcCode in Cause Register */ |
| #define EXC_RI 0x28 /* 101000 == 10 << 2 */ |
| |
| /* Set DEBUG to 1 to enable recording of the last 16 interrupt causes. */ |
| |
| #define DEBUG 0 |
| |
| #if DEBUG |
| |
| .sdata |
| int_count: |
| .space 4 /* interrupt count modulo 16 */ |
| int_cause: |
| .space 4*16 /* last 16 interrupt causes */ |
| #endif |
| |
| .text |
| |
| .set noreorder /* Do NOT reorder instructions */ |
| |
| |
| /* __entry_exit_handler - the reserved instruction exception handler |
| that emulates the entry and exit instruction. */ |
| |
| __entry_exit_handler: |
| .set noat /* Do NOT use at register */ |
| #if DEBUG |
| /* Must avoid using 'la' pseudo-op because it uses gp register, which |
| may not have a good value in an exception handler. */ |
| |
| # la k0, int_count /* intcount = (intcount + 1) & 0xf */ |
| lui k0 ,%hi(int_count) |
| addiu k0, k0 ,%lo(int_count) |
| lw k1, (k0) |
| addiu k1, k1, 1 |
| andi k1, k1, 0x0f |
| sw k1, (k0) |
| # la k0, int_cause /* k1 = &int_cause[intcount] */ |
| lui k0, %hi(int_cause) |
| addiu k0, k0, %lo(int_cause) |
| sll k1, k1, 2 |
| add k1, k1, k0 |
| #endif |
| mfc0 k0, C0_CAUSE /* Fetch cause */ |
| #if DEBUG |
| sw k0, -4(k1) /* Save exception cause in buffer */ |
| #endif |
| mfc0 k1, C0_EPC /* Check for Reserved Inst. without */ |
| and k0, CAUSE_EXCMASK /* destroying any register */ |
| subu k0, EXC_RI |
| bne k0, zero, check_others /* Sorry, go do something else */ |
| |
| and k0, k1, 1 /* Check for TR mode (pc.0 = 1) */ |
| beq k0, zero, ri_in_32 /* Sorry, RI in 32-bit mode */ |
| xor k1, 1 |
| |
| /* Since we now are going to emulate or die, we can use all the T-registers */ |
| /* that MIPS16 does not use (at, t0-t8), and we don't have to save them. */ |
| |
| .set at /* Now it's ok to use at again */ |
| |
| #if 0 |
| j leave |
| rfe |
| #endif |
| |
| lhu t0, 0(k1) /* Fetch the offending instruction */ |
| xor t8, k1, 1 /* Prepare t8 for exit */ |
| and t1, t0, 0xf81f /* Check for entry/exit opcode */ |
| bne t1, 0xe809, other_ri |
| |
| deareg: and t1, t0, 0x0700 /* Isolate the three a-bits */ |
| srl t1, 6 /* Adjust them so x4 is applied */ |
| slt t2, t1, 17 /* See if this is the exit instruction */ |
| beqz t2, doexit |
| la t2, savea |
| subu t2, t1 |
| jr t2 /* Jump into the instruction table */ |
| rfe /* We run the rest in user-mode */ |
| |
| /* This is the entry instruction! */ |
| sw a3, 12(sp) /* 4: a0-a3 saved */ |
| sw a2, 8(sp) /* 3: a0-a2 saved */ |
| sw a1, 4(sp) /* 2: a0-a1 saved */ |
| sw a0, 0(sp) /* 1: a0 saved */ |
| savea: /* 0: No arg regs saved */ |
| |
| dera: and t1, t0, 0x0020 /* Isolate the save-ra bit */ |
| move t7, sp /* Temporary SP */ |
| beq t1, zero, desreg |
| subu sp, 32 /* Default SP adjustment */ |
| sw ra, -4(t7) |
| subu t7, 4 |
| |
| desreg: and t1, t0, 0x00c0 /* Isolate the two s-bits */ |
| beq t1, zero, leave |
| subu t1, 0x0040 |
| beq t1, zero, leave /* Only one to save... */ |
| sw s0, -4(t7) /* Do the first one */ |
| sw s1, -8(t7) /* Do the last one */ |
| |
| leave: jr t8 /* Exit to unmodified EPC */ |
| nop /* Urgh - the only nop!! */ |
| |
| doexf0: mtc1 v0,$f0 /* Copy float value */ |
| b doex2 |
| |
| doexf1: mtc1 v1,$f0 /* Copy double value */ |
| mtc1 v0,$f1 |
| b doex2 |
| |
| doexit: slt t2, t1, 21 |
| beq t2, zero, doexf0 |
| slt t2, t1, 25 |
| beq t2, zero, doexf1 |
| |
| doex2: and t1, t0, 0x0020 /* Isolate ra bit */ |
| beq t1, zero, dxsreg /* t1 holds ra-bit */ |
| addu t7, sp, 32 /* Temporary SP */ |
| lw ra, -4(t7) |
| subu t7, 4 |
| |
| dxsreg: and t1, t0, 0x00c0 /* Isolate the two s-bits */ |
| beq t1, zero, leavex |
| subu t1, 0x0040 |
| beq t1, zero, leavex /* Only one to save... */ |
| lw s0, -4(t7) /* Do the first one */ |
| lw s1, -8(t7) /* Do the last one */ |
| |
| leavex: jr ra /* Exit to ra */ |
| addu sp, 32 /* Clean up stack pointer */ |
| |
| /* Come here for exceptions we can't handle. */ |
| |
| ri_in_32: |
| other_ri: |
| check_others: /* call the previous handler */ |
| la k0,__previous |
| jr k0 |
| nop |
| |
| __exception_code: |
| .set noreorder |
| la k0, __entry_exit_handler |
| # lui k0, %hi(exception) |
| # addiu k0, k0, %lo(exception) |
| jr k0 |
| nop |
| .set reorder |
| __exception_code_end: |
| |
| .data |
| __previous: |
| .space (__exception_code_end - __exception_code) |
| .text |
| |
| |
| /* void __install_entry_handler(void) |
| |
| Install our entry/exit reserved instruction exception handler. |
| */ |
| .ent __install_entry_handler |
| .globl __install_entry_handler |
| __install_entry_handler: |
| .set noreorder |
| mfc0 a0,C0_SR |
| nop |
| li a1,SR_BEV |
| and a1,a1,a0 |
| beq a1,$0,baseaddr |
| lui a0,0x8000 /* delay slot */ |
| lui a0,0xbfc0 |
| addiu a0,a0,0x0100 |
| baseaddr: |
| addiu a0,a0,0x080 /* a0 = base vector table address */ |
| li a1,(__exception_code_end - __exception_code) |
| la a2,__exception_code |
| la a3,__previous |
| /* there must be a better way of doing this???? */ |
| copyloop: |
| lw v0,0(a0) |
| sw v0,0(a3) |
| lw v0,0(a2) |
| sw v0,0(a0) |
| addiu a0,a0,4 |
| addiu a2,a2,4 |
| addiu a3,a3,4 |
| subu a1,a1,4 |
| bne a1,$0,copyloop |
| nop |
| j ra |
| nop |
| .set reorder |
| .end __install_entry_handler |
| |
| |
| /* void __remove_entry_handler(void); |
| |
| Remove our entry/exit reserved instruction exception handler. |
| */ |
| |
| .ent __remove_entry_handler |
| .globl __remove_entry_handler |
| __remove_entry_handler: |
| .set noreorder |
| |
| mfc0 a0,C0_SR |
| nop |
| li a1,SR_BEV |
| and a1,a1,a0 |
| beq a1,$0,res_baseaddr |
| lui a0,0x8000 /* delay slot */ |
| lui a0,0xbfc0 |
| addiu a0,a0,0x0200 |
| res_baseaddr: |
| addiu a0,a0,0x0180 /* a0 = base vector table address */ |
| li a1,(__exception_code_end - __exception_code) |
| la a3,__previous |
| |
| /* there must be a better way of doing this???? */ |
| res_copyloop: |
| lw v0,0(a3) |
| sw v0,0(a0) |
| addiu a0,a0,4 |
| addiu a3,a3,4 |
| subu a1,a1,4 |
| bne a1,$0,res_copyloop |
| nop |
| j ra |
| nop |
| .set reorder |
| .end __remove_entry_handler |
| |
| |
| /* software_init_hook - install entry/exit handler and arrange to have it |
| removed at exit. This function is called by crt0.S. */ |
| |
| .text |
| .globl software_init_hook |
| .ent software_init_hook |
| software_init_hook: |
| .set noreorder |
| subu sp, sp, 8 /* allocate stack space */ |
| sw ra, 4(sp) /* save return address */ |
| jal __install_entry_handler /* install entry/exit handler */ |
| nop |
| lui a0, %hi(__remove_entry_handler) /* arrange for exit to */ |
| jal atexit /* de-install handler */ |
| addiu a0, a0, %lo(__remove_entry_handler) /* delay slot */ |
| lw ra, 4(sp) /* get return address */ |
| j ra /* return */ |
| addu sp, sp, 8 /* deallocate stack */ |
| .set reorder |
| .end software_init_hook |