20000317 sourceware import
This commit is contained in:
		
							
								
								
									
										281
									
								
								libgloss/mips/entry.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								libgloss/mips/entry.S
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
		Reference in New Issue
	
	Block a user