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