Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'libgloss/mips/entry.S')
-rw-r--r--libgloss/mips/entry.S281
1 files changed, 281 insertions, 0 deletions
diff --git a/libgloss/mips/entry.S b/libgloss/mips/entry.S
new file mode 100644
index 000000000..3630c552f
--- /dev/null
+++ b/libgloss/mips/entry.S
@@ -0,0 +1,281 @@
+/* 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