aboutsummaryrefslogtreecommitdiff
path: root/libgloss/mips/entry.S
blob: 3630c552f89ad363b51153005909ccdd87f75618 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
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