282 lines
7.0 KiB
ArmAsm
282 lines
7.0 KiB
ArmAsm
/* 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
|