/* sh-stub.c -- debugging stub for the Hitachi-SH-4 on Dreamcast. */ /* This is originally based on an m68k software stub written by Glenn Engel at HP, but has changed quite a bit. Modifications for the SH by Ben Lee and Steve Chamberlain Modifications by Dan Potter to do all kinds of changes to make this thing work on the Dreamcast. There has been a rather large amount of mods by me (Dan) to make it work with the SH-4 and cooperate with Marcus' serial slave. I realized (a bit late ^_^;;) that the sh-linux guys had already done most of this work, but I like mine better anyhow. A few of the routines (flush_i_cache) have been built/copied from their gdb-sh-stub. Finally, I've rescued this file from the hideous GNU coding standards. It's pretty much Ansified K&R now. */ /**************************************************************************** THIS SOFTWARE IS NOT COPYRIGHTED HP offers the following for use in the public domain. HP makes no warranty with regard to the software or it's performance and the user accepts the software "AS IS" with all faults. HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ****************************************************************************/ #include #include #include /* Hitachi SH architecture instruction encoding masks */ #define COND_BR_MASK 0xff00 #define UCOND_DBR_MASK 0xe000 #define UCOND_RBR_MASK 0xf0df #define TRAPA_MASK 0xff00 #define UCOND_FAR_MASK 0xf0ff #define COND_DISP 0x00ff #define UCOND_DISP 0x0fff #define UCOND_REG 0x0f00 /* Hitachi SH instruction opcodes */ #define BF_INSTR 0x8b00 #define BT_INSTR 0x8900 #define BFS_INSTR 0x8f00 /* pg 223; COND_BR_MASK */ #define BTS_INSTR 0x8d00 /* pg 234; COND_BR_MASK */ #define BRA_INSTR 0xa000 #define BRAF_INSTR 0x0023 /* pg 227; UCOND_FAR_MASK */ #define BSR_INSTR 0xb000 #define BSRF_INSTR 0x0003 /* pg 230; UCOND_FAR_MASK */ #define JMP_INSTR 0x402b /* pg 309; UCOND_FAR_MASK */ #define JSR_INSTR 0x400b #define RTS_INSTR 0x000b #define RTE_INSTR 0x002b #define TRAPA_INSTR 0xc300 #define SSTEP_INSTR 0xc3ff /* Hitachi SH processor register masks */ #define T_BIT_MASK 0x0001 /* * BUFMAX defines the maximum number of characters in inbound/outbound * buffers. At least NUMREGBYTES*2 are needed for register packets. */ #define BUFMAX 1024 /* * Number of bytes for registers */ #define NUMREGBYTES 112 /* 92 */ /* typedef */ typedef void (*Function) (); typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned long ulong; /* External Interface */ void set_debug_traps(); void breakpoint(); /* * Forward declarations */ static int hex(char); static char * mem2hex (char *, char *, int); static char * hex2mem (char *, char *, int); static int hexToInt (char **, int *); static uchar * getpacket(); static void putpacket(uchar *); static void handle_buserror (); static int compute_signal (int exceptionVector); static void handle_exception (int exceptionVector); void flush_icache_range(unsigned long start, unsigned long end); void put_debug_char(int); int get_debug_char(); void debug_printf(char *fmt, ...); /* Forward declarations for exception handlers */ void exception_type000(); // Type 1: reset void exception_type100(); // Type 2: completeion void exception_type400(); void exception_type600(); /* The stub stack -- this needs to be seperate so that we don't run over the program that's been interrupted. The init stack has been removed from the eval board versions because we don't ever handle reset and init. --Dan */ //#define stub_stack_size 8*1024 //int stub_stack[stub_stack_size] __attribute__ ((section ("stack"))) = {0}; // Just put it at the top of memory for now #define stub_stack_size (16*1024) int* stub_stack = (int*)(0x8c800000 - stub_stack_size); #define CPU_BUS_ERROR_VEC 9 #define DMA_BUS_ERROR_VEC 10 #define NMI_VEC 11 #define INVALID_INSN_VEC 4 #define INVALID_SLOT_VEC 6 #define TRAP_VEC 32 #define IO_VEC 33 #define USER_VEC 255 char in_nmi; /* Set when handling an NMI, so we don't reenter */ int dofault; /* Non zero, bus errors will raise exception */ int *stub_sp; /* Current stub stack pointer */ /* debug > 0 prints ill-formed commands in valid packets & checksum errors */ int remote_debug; /* jump buffer used for setjmp/longjmp */ jmp_buf remcomEnv; enum regnames { R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, PC, PR, GBR, VBR, MACH, MACL, SR, TICKS, STALLS, CYCLES, INSTS, PLR }; typedef struct { short *memAddr; short oldInstr; } stepData; int registers[NUMREGBYTES / 4]; stepData instrBuffer; char stepped; static const char hexchars[] = "0123456789abcdef"; static uchar remcomInBuffer[BUFMAX]; static uchar remcomOutBuffer[BUFMAX]; char highhex(int x) { return hexchars[(x >> 4) & 0xf]; } char lowhex(int x) { return hexchars[x & 0xf]; } /* Assembly macros */ #define BREAKPOINT() asm("trapa #0x20"); /*********************************************************** * Routines to handle hex data ***********************************************************/ static int hex (char ch) { if ((ch >= 'a') && (ch <= 'f')) return (ch - 'a' + 10); if ((ch >= '0') && (ch <= '9')) return (ch - '0'); if ((ch >= 'A') && (ch <= 'F')) return (ch - 'A' + 10); return (-1); } /* convert the memory, pointed to by mem into hex, placing result in buf */ /* return a pointer to the last char put in buf (the terminating null) */ static char *mem2hex (char *mem, char *buf, int count) { int i, ch; for (i=0; i= 0) { *intValue = (*intValue << 4) | hexValue; numChars++; } else break; (*ptr)++; } return numChars; } /************************************************************** * Routines to get and put packets **************************************************************/ /* scan for the sequence $# */ uchar *getpacket() { uchar *buffer = &remcomInBuffer[0]; uchar checksum; uchar xmitcsum; int count; char ch; while (1) { /* wait around for the start character, ignore all other characters */ while ((ch = get_debug_char()) != '$') ; retry: checksum = 0; xmitcsum = -1; count = 0; /* now, read until a # or end of buffer is found */ while (count < BUFMAX) { ch = get_debug_char(); if (ch == '$') goto retry; if (ch == '#') break; checksum = checksum + ch; buffer[count] = ch; count = count + 1; } buffer[count] = 0; if (ch == '#') { ch = get_debug_char(); xmitcsum = hex(ch) << 4; ch = get_debug_char(); xmitcsum += hex(ch); if (checksum != xmitcsum) { put_debug_char ('-'); /* failed checksum */ } else { put_debug_char ('+'); /* successful transfer */ /* if a sequence char is present, reply the sequence ID */ if (buffer[2] == ':') { put_debug_char(buffer[0]); put_debug_char(buffer[1]); debug_printf("R: %s", buffer+3); return &buffer[3]; } debug_printf("R: %s", buffer+0); return &buffer[0]; } } } } /* send the packet in buffer. */ static void putpacket(uchar *buffer) { int checksum, count; /* $#. */ do { uchar *src = buffer; put_debug_char('$'); checksum = 0; while (*src) { #if 0 int runlen; /* Do run length encoding */ for (runlen = 0; runlen < 100; runlen ++) { if (src[0] != src[runlen]) { if (runlen > 3) { int encode; /* Got a useful amount */ put_debug_char(*src); checksum += *src; put_debug_char('*'); checksum += '*'; checksum += (encode = runlen + ' ' - 4); put_debug_char(encode); src += runlen; } else { #endif put_debug_char (*src); checksum += *src; src++; #if 0 } break; } } #endif } put_debug_char('#'); put_debug_char(highhex(checksum)); put_debug_char(lowhex(checksum)); dc_serial_printf(""); /* For some reason this must be here, or it occasionally locks up... werd */ debug_printf("S: %s", buffer); } while (get_debug_char() != '+'); debug_printf("ACK RECVD"); } /* a bus error has occurred, perform a longjmp to return execution and allow handling of the error */ void handle_buserror() { longjmp (remcomEnv, 1); } /* * this function takes the SH-1 exception number and attempts to * translate this number into a unix compatible signal value */ static int compute_signal(int exceptionVector) { int sigval; switch (exceptionVector) { case INVALID_INSN_VEC: sigval = 4; break; case INVALID_SLOT_VEC: sigval = 4; break; case CPU_BUS_ERROR_VEC: sigval = 10; break; case DMA_BUS_ERROR_VEC: sigval = 10; break; case NMI_VEC: sigval = 2; break; case TRAP_VEC: case USER_VEC: sigval = 5; break; default: sigval = 7; /* "software generated" */ break; } return sigval; } /* Perform the single-step hooking, whatever that involves. Generally we just replace the instruction after PC with a trap that calls us... in more exotic cases (branches, etc), we have to do a bit of interpreting on the instruction and see where it goes. */ void doSStep() { short *instrMem; int displacement; int reg; unsigned short opcode; instrMem = (short *)registers[PC]; opcode = *instrMem; stepped = 1; /* Look for VBR and SR setting instructions and skip over them; we'll * also skip trapa instructions, 'cause those screw up debugging. */ if ((instrBuffer.oldInstr & 0xf0ff) == 0x402e || (instrBuffer.oldInstr & 0xf0ff) == 0x4027 || (instrBuffer.oldInstr & 0xf0ff) == 0x400e || (instrBuffer.oldInstr & 0xf0ff) == 0x4007 || (instrBuffer.oldInstr & 0xff00) == TRAPA_INSTR) registers[PC]+=2; debug_printf("Instruction to be stepped is %04x", opcode); if ((opcode & COND_BR_MASK) == BT_INSTR || (opcode & COND_BR_MASK) == BTS_INSTR) { debug_printf("Handling it as a BT instruction"); if (registers[SR] & T_BIT_MASK) { /* displacement = (opcode & COND_DISP) << 1; if (displacement & 0x80) displacement |= 0xffffff00; // Remember PC points to second instr. // after PC of branch ... so add 4 instrMem = (short *) (registers[PC] + displacement + 4); */ displacement = (opcode & COND_DISP); if (displacement & 0x80) { displacement |= 0xFFFFFF00; } else { displacement &= 0x000000FF; } displacement <<= 1; instrMem = (short*)(registers[PC] + displacement + 4); } else instrMem += 1; } else if ((opcode & COND_BR_MASK) == BF_INSTR || (opcode & COND_BR_MASK) == BFS_INSTR) { debug_printf("Handling it as a BF instruction"); if (registers[SR] & T_BIT_MASK) instrMem += 1; else { /* displacement = (opcode & COND_DISP) << 1; if (displacement & 0x80) displacement |= 0xffffff00; // Remember PC points to second instr. // after PC of branch ... so add 4 instrMem = (short *) (registers[PC] + displacement + 4); */ displacement = (opcode & COND_DISP); if (displacement & 0x80) { displacement |= 0xFFFFFF00; } else { displacement &= 0x000000FF; } displacement <<= 1; instrMem = (short*)(registers[PC] + displacement + 4); } } else if ((opcode & UCOND_DBR_MASK) == BRA_INSTR) { debug_printf("Handling it as a BRA instruction"); /* displacement = (opcode & UCOND_DISP) << 1; if (displacement & 0x0800) displacement |= 0xfffff000; // Remember PC points to second instr. // after PC of branch ... so add 4 instrMem = (short *) (registers[PC] + displacement + 4); */ displacement = (opcode & UCOND_DISP); if (displacement & 0x800) { displacement |= 0xFFFFF000; } else { displacement &= 0x00000FFF; } displacement <<= 1; instrMem = (short*)(registers[PC] + displacement + 4); } else if ((opcode & UCOND_RBR_MASK) == JSR_INSTR || (opcode & UCOND_FAR_MASK) == BRAF_INSTR || (opcode & UCOND_FAR_MASK) == BSRF_INSTR || (opcode & UCOND_FAR_MASK) == JMP_INSTR) { debug_printf("Handling it as a JSR instruction"); reg = (char) ((opcode & UCOND_REG) >> 8); instrMem = (short *) registers[reg]; } else if (opcode == RTS_INSTR) { debug_printf("Handling it as a RTS instruction"); instrMem = (short *) registers[PR]; } else if (opcode == RTE_INSTR) { debug_printf("Handling it as a RTE instruction"); instrMem = (short *) registers[15]; /* This is wrong */ } else if ((opcode & TRAPA_MASK) == TRAPA_INSTR) { debug_printf("Handling it as a TRAPA instruction"); instrMem = (short *) ((opcode & ~TRAPA_MASK) << 2); /* So is this =) */ } else { debug_printf("Handling it as a misc instruction"); instrMem += 1; } instrBuffer.memAddr = instrMem; instrBuffer.oldInstr = *instrMem; *instrMem = SSTEP_INSTR; // flush_icache_range((unsigned long)instrMem, (unsigned long)(instrMem+1)); } /* Undo the effect of a previous doSStep. If we single stepped, restore the old instruction. */ void undoSStep() { if (stepped) { short *instrMem; instrMem = instrBuffer.memAddr; #if 0 if ((instrBuffer.oldInstr & 0xf0ff) == 0x402e || (instrBuffer.oldInstr & 0xf0ff) == 0x4027) /* Kill VBR instructions */ *instrMem = 0x0009; if ((instrBuffer.oldInstr & 0xf0ff) == 0x400e || (instrBuffer.oldInstr & 0xf0ff) == 0x4007) /* Kill SR instructions */ *instrMem = 0x0009; else #endif *instrMem = instrBuffer.oldInstr; // flush_icache_range((unsigned long)instrMem, (unsigned long)(instrMem+1)); } stepped = 0; } /* This function does all exception handling. It only does two things - it figures out why it was called and tells gdb, and then it reacts to gdb's requests. When in the monitor mode we talk a human on the serial line rather than gdb. */ void gdb_handle_exception(int exceptionVector) { int sigval, stepping; int addr, length; char *ptr; debug_printf("Inside handle_exception"); /* This little hack makes the CPU single-step through the entire program, displaying the current PC as a banner on the screen */ /*if (exceptionVector == 0xff || exceptionVector == 0x20) { registers[PC] -= 2; dc_draw_stringf_4(20, 20, 0xf800, "%08x", registers[PC]); undoSStep(); doSStep(); return; } */ /* reply to host that an exception has occurred */ sigval = compute_signal(exceptionVector); remcomOutBuffer[0] = 'S'; remcomOutBuffer[1] = highhex(sigval); remcomOutBuffer[2] = lowhex (sigval); remcomOutBuffer[3] = 0; putpacket(remcomOutBuffer); /* * exception 255 indicates a software trap * inserted in place of code ... so back up * PC by one instruction, since this instruction * will later be replaced by its original one! */ if (exceptionVector == 0xff || exceptionVector == 0x20) registers[PC] -= 2; /* * Do the thangs needed to undo * any stepping we may have done! */ undoSStep(); stepping = 0; while (1) { remcomOutBuffer[0] = 0; ptr = getpacket(); /* wait for a GDB packet */ switch (*ptr++) { case '?': /* Repeat exception code */ remcomOutBuffer[0] = 'S'; remcomOutBuffer[1] = highhex (sigval); remcomOutBuffer[2] = lowhex (sigval); remcomOutBuffer[3] = 0; break; case 'd': /* Toggle remote debugging flag */ remote_debug = !remote_debug; break; case 'g': /* return the value of the CPU registers */ debug_printf("Dumping registers"); mem2hex ((char *) registers, remcomOutBuffer, NUMREGBYTES); debug_printf("Register dump complete"); break; case 'G': /* set the value of the CPU registers - return OK */ hex2mem (ptr, (char *) registers, NUMREGBYTES); strcpy(remcomOutBuffer, "OK"); break; /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ case 'm': if (setjmp (remcomEnv) == 0) { dofault = 0; /* TRY, TO READ %x,%x. IF SUCCEED, SET PTR = 0 */ if (hexToInt(&ptr, &addr) && *(ptr++) == ',') if (hexToInt (&ptr, &length)) { ptr = 0; /* Special cases for DreamICE */ if (length == 2) { unsigned short value; value = *((unsigned short*)addr); mem2hex((char*)&value, remcomOutBuffer, 2); } else if (length == 4) { unsigned long value; value = *((unsigned long*)addr); mem2hex((char*)&value, remcomOutBuffer, 4); } else { mem2hex ((char *) addr, remcomOutBuffer, length); } } if (ptr) strcpy (remcomOutBuffer, "E01"); } else { strcpy (remcomOutBuffer, "E03"); } /* restore handler for bus error */ dofault = 1; break; /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ case 'M': if (setjmp (remcomEnv) == 0) { dofault = 0; /* TRY, TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */ if (hexToInt (&ptr, &addr) && *(ptr++) == ',' && hexToInt(&ptr, &length) && *(ptr++) == ':') { /* Special cases for DreamICE */ if (length == 2) { unsigned short value; hex2mem(ptr, (char*)&value, 2); *((unsigned short*)addr) = value; } else if (length == 4) { unsigned long value; hex2mem(ptr, (char*)&value, 4); *((unsigned long*)addr) = value; } else { hex2mem (ptr, (char *) addr, length); } ptr = 0; strcpy (remcomOutBuffer, "OK"); } if (ptr) strcpy (remcomOutBuffer, "E02"); } else { strcpy (remcomOutBuffer, "E03"); } /* restore handler for bus error */ dofault = 1; break; /* cAA..AA Continue at address AA..AA(optional) */ /* sAA..AA Step one instruction from AA..AA(optional) */ case 's': debug_printf("stepping enabled"); stepping = 1; case 'c': /* Try to read optional parameter, pc unchanged if no parm */ if (hexToInt (&ptr, &addr)) { debug_printf("Setting PC to %08x", addr); registers[PC] = addr; } if (stepping) { debug_printf("Going to doSStep"); doSStep(); } return; /* kill the program */ case 'k': break; /* not supported */ } /* reply to the request */ putpacket (remcomOutBuffer); } } #define GDBCOOKIE 0x5ac static int ingdbmode; /* We've had an exception - choose to go into the monitor or the gdb stub */ void handle_exception(int exceptionVector) { #ifdef MONITOR if (ingdbmode != GDBCOOKIE) monitor_handle_exception (exceptionVector); else #endif gdb_handle_exception (exceptionVector); } void gdb_mode() { ingdbmode = GDBCOOKIE; breakpoint(); } /* This function will generate a breakpoint exception. It is used at the beginning of a program to sync up with a debugger and can be used otherwise as a quick means to stop program execution and "break" into the debugger. */ void breakpoint() { BREAKPOINT(); } /******************************************************************* * SH-4 specific routines start here *******************************************************************/ /* A routine to flush the caches */ #define L1_CACHE_BYTES 32 #define CACHE_IC_ADDRESS_ARRAY 0xf0000000 #define CACHE_IC_ENTRY_MASK 0x1fe0 struct __large_struct { unsigned long buf[100]; }; #define __m(x) (*(struct __large_struct *)(x)) void flush_icache_range(unsigned long start, unsigned long end) { unsigned long addr, data, v; start &= ~(L1_CACHE_BYTES-1); for (v=start; v> 8) & 0xFF, /* TA, LSB+1 */ (ta >> 16) & 0xFF, /* TA, MSB-1 */ (ta >> 24) & 0xFF /* TA, MSB */ }; /* Four instructions plus one data long */ memcpy(dest, code, sizeof(code)); #else /* Test code: writes code to change the border color */ unsigned char code[] = { 0x03, 0xd0, // mov.l pc+0x10,r0 0xff, 0xe1, // mov #-1,r1 0x12, 0x20, // mov.l r1,@r0 0xfe, 0xaf, // bra pc+0 0x09, 0x00, // nop (observe alignment) 0x09, 0x00, // nop (observe alignment) 0x09, 0x00, // nop (observe alignment) 0x09, 0x00, // nop (observe alignment) 0x40, 0x80, // border reg address 0x5f, 0xa0 }; // Four instructions plus one data long memcpy(dest, code, sizeof(code)); #endif } /* Stub initialization part 1: this is called from set_debug_traps() before the VBR is set. This lets us put together an exception handler space before the processor is switched over. */ void stub_init_1() { /* We're running in P1 (cache), do this in P2 (no cache) */ ulong vttmp = (ulong)vectable; /* Make sure it's 4-byte aligned */ vttmp = (vttmp & ~3) + 4 + 0x20000000; vtaddr = (ushort*)vttmp; dc_serial_printf("vectable is at %08x/%08x\r\n", vectable, vtaddr); /* init_serial(); */ /* It's already been done */ /* Basic flags */ in_nmi = 0; dofault = 1; stepped = 0; /* De-cache the vectable */ memset(vtaddr, 0, sizeof(vectable)); /* Build exception handlers for the for states */ build_exception_handler(vtaddr + 0x000/2, exception_type000); build_exception_handler(vtaddr + 0x100/2, exception_type100); build_exception_handler(vtaddr + 0x400/2, exception_type000); build_exception_handler(vtaddr + 0x600/2, exception_type000); /* Initialize stub's SP */ stub_sp = stub_stack + stub_stack_size; } /* Stub initialization part 2: called from set_debug_traps() after the VBR is set. Basically just turns on traps. */ void stub_init_2() { /* Clear BL to allow traps and interrupts */ asm(" mov.l r1,@-r15 stc sr,r0 mov.l Land,r1 and r1,r0 ldc r0,sr mov.l @r15+,r1 "); } /* Asm constant from above */ asm(" .align 4 Land: .long 0xefffffff Lor: .long 0x000000f0 "); extern ulong vbraddr, vbractual; /* Do all stub initialization */ void set_debug_traps() { /* Pre-init */ stub_init_1(); /* Setup the VBR */ dc_serial_printf("Setting VBR to %08x\r\n", vbraddr); asm(" mov.l _vbraddr,r0 ! Install our VBR ldc r0,vbr nop nop nop stc vbr,r0 mov.l r1,@-r15 mov.l _actualaddr,r1 mov.l r0,@r1 mov.l @r15+,r1 "); dc_serial_printf("VBR was set to %08x\r\n", vbractual); /* Post-init */ stub_init_2(); } /* Asm constant for set_debug_traps() */ asm(" .align 4 _vbraddr: .long _vectable + 0x20000000 _actualaddr: .long _vbractual _vbractual: .long 0 "); static void sr() { asm(" ! This is a little confusing since we have to do some juggling to save ! all the register values. save_registers: mov.l r1, @-r15 ! Save R1 on stack mov.l r0, @-r15 ! Save R0 on stack mov.l @(L_reg, pc), r0 ! Grab the location of register store mov.l @r15+, r1 ! pop R0 from stack into R1 mov.l r1, @r0 ! save R0 to reg. store (via r1) mov.l @r15+, r1 ! pop R1 from stack into R1 mov.l r1, @(0x04, r0) ! save R1 mov.l r2, @(0x08, r0) ! save R2 mov.l r3, @(0x0c, r0) ! save R3 mov.l r4, @(0x10, r0) ! save R4 mov.l r5, @(0x14, r0) ! save R5 mov.l r6, @(0x18, r0) ! save R6 mov.l r7, @(0x1c, r0) ! save R7 mov.l r8, @(0x20, r0) ! save R8 mov.l r9, @(0x24, r0) ! save R9 mov.l r10, @(0x28, r0) ! save R10 mov.l r11, @(0x2c, r0) ! save R11 mov.l r12, @(0x30, r0) ! save R12 mov.l r13, @(0x34, r0) ! save R13 mov.l r14, @(0x38, r0) ! save R14 mov.l r15, @(0x3c, r0) ! save R15 add #0x5c, r0 ! readjust register pointer stc.l ssr,@-r0 ! save SSR sts.l macl, @-r0 ! save MACL sts.l mach, @-r0 ! save MACH stc.l vbr, @-r0 ! save VBR stc.l gbr, @-r0 ! save GBR sts.l pr, @-r0 ! save SPR stc.l spc,@-r0 ! save PC mov.l @(L_traploc, pc), r4 ! Get location of TRA mov.l @r4,r4 ! Set as first parameter shlr2 r4 mov.l @(L_stubstack, pc), r2 mov.l @(L_hdl_except, pc), r3 mov.l @r2, r15 jsr @r3 nop mov.l @(L_stubstack, pc), r0 mov.l @(L_reg, pc), r1 bra restore_registers mov.l r15, @r0 ! save __stub_stack .align 2 L_reg: .long _registers L_stubstack: .long _stub_sp L_hdl_except: .long _handle_exception L_traploc: .long 0xFF000020 "); } static void rr() { asm(" .align 2 .global _resume _resume: mov r4,r1 restore_registers: mov.l @(0x08,r1), r2 ! restore R2 mov.l @(0x0c,r1), r3 ! restore R3 mov.l @(0x10,r1), r4 ! restore R4 mov.l @(0x14,r1), r5 ! restore R5 mov.l @(0x18,r1), r6 ! restore R6 mov.l @(0x1c,r1), r7 ! restore R7 mov.l @(0x20,r1), r8 ! restore R8 mov.l @(0x24,r1), r9 ! restore R9 mov.l @(0x28,r1), r10 ! restore R10 mov.l @(0x2c,r1), r11 ! restore R11 mov.l @(0x30,r1), r12 ! restore R12 mov.l @(0x34,r1), r13 ! restore R13 mov.l @(0x38,r1), r14 ! restore R14 mov.l @(0x3c,r1), r15 ! restore programs stack add #0x40,r1 ! jump up to status words ldc.l @r1+,spc ! restore SPC lds.l @r1+,pr ! restore PR ldc.l @r1+,gbr ! restore GBR ldc.l @r1+,vbr ! restore VBR lds.l @r1+,mach ! restore MACH lds.l @r1+,macl ! restore MACL ldc.l @r1+,ssr ! restore SSR add #-0x5c,r1 ! jump back to registers mov.l @(0,r1),r0 ! restore R0 mov.l @(4,r1),r1 ! restore R1 rte ! return nop "); } /* We shouldn't ever see this one -- do something cheesy */ void exception_type000_wrapper() { asm(" .global _exception_type000 _exception_type000: rte nop "); } /* This is our main culprit */ void exception_type100_wrapper() { asm(" .global _exception_type100 _exception_type100: bra save_registers nop "); } /* Old exception code kept around for reference */ #if 0 static __inline__ void code_for_catch_exception(int n) { asm(" .globl _catch_exception_%O0" : : "i" (n) ); asm(" _catch_exception_%O0:" :: "i" (n) ); if (n == NMI_VEC) { /* Special case for NMI - make sure that they don't nest */ asm(" mov.l r0, @-r15 ! push R0"); asm(" mov.l L_in_nmi, r0"); asm(" tas.b @r0 ! Fend off against addtnl NMIs"); asm(" bt noNMI"); asm(" mov.l @r15+, r0"); asm(" mov.l @r15+, r1"); asm(" add #4, r15"); asm(" rte"); asm(" nop"); asm(".align 2"); asm("L_in_nmi: .long _in_nmi"); asm("noNMI:"); } else { if (n == CPU_BUS_ERROR_VEC) { /* Exception 9 (bus errors) are disasbleable - so that you can probe memory and get zero instead of a fault. Because the vector table may be in ROM we don't revector the interrupt like all the other stubs, we check in here */ asm("mov.l L_dofault,r1"); asm("mov.l @r1,r1"); asm("tst r1,r1"); asm("bf faultaway"); asm("bsr _handle_buserror"); asm(".align 2"); asm("L_dofault: .long _dofault"); asm("faultaway:"); } asm(" mov #15<<4, r1 "); asm(" ldc r1, sr ! disable interrupts "); asm(" mov.l r0, @-r15 ! push R0 "); } /* Prepare for saving context, we've already pushed r0 and r1, stick exception number into the frame */ asm(" mov r15, r0 "); asm(" add #8, r0 "); asm(" mov %0,r1" :: "i" (n) ); asm(" extu.b r1,r1 "); asm(" bra saveRegisters ! save register values "); asm(" mov.l r1, @r0 ! save exception # "); } static void exceptions() { code_for_catch_exception (CPU_BUS_ERROR_VEC); code_for_catch_exception (DMA_BUS_ERROR_VEC); code_for_catch_exception (INVALID_INSN_VEC); code_for_catch_exception (INVALID_SLOT_VEC); code_for_catch_exception (NMI_VEC); code_for_catch_exception (TRAP_VEC); code_for_catch_exception (USER_VEC); code_for_catch_exception (IO_VEC); } #endif /**************************************************************************** * Support for Serial I/O using on chip uart ****************************************************************************/ /* Hooks into Marcus' serial code (part of libdream right now) */ int get_debug_char() { return dc_serial_getchar(); } void put_debug_char(int c) { dc_serial_printf("%c", c); } void debug_printf(char *fmt,...) { /* static int curx = 32, cury = 0; static ushort color = 0xf800; va_list args; char pbuf[2048]; int y; va_start(args,fmt); vsprintf(pbuf, fmt, args); va_end(args); dc_draw_string_2(curx, cury, color, " "); dc_draw_string_2(curx, cury+17, color, " "); dc_draw_string_2(curx, cury, color, pbuf); cury += 17; if (cury >= 400) { cury = 0; if (color == 0xf800) color = 31; else color = 0xf800; } */ }