2 Copyright © 1995-2010, The AROS Development Team. All rights reserved.
5 Desc: m68k-amiga gdb stub
8 /* Adapted from public domain code from:
9 * ftp://ftp.jyu.fi/pub/PalmOS/ryeham/ALPHA/m68k-gdbstub.c
11 * vvvvvvv The below are the original comments from m68k-gdbstub.c vvvvvvv
13 /****************************************************************************
14 * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
16 * Module name: remcom.c $
18 * Date: 91/03/09 12:29:49 $
19 * Contributor: Lake Stevens Instrument Division$
21 * Description: low level support for gdb debugger. $
23 * Considerations: only works on target hardware $
25 * Written by: Glenn Engel $
26 * ModuleState: Experimental $
30 * To enable debugger support, two things need to happen. One, a
31 * call to set_debug_traps() is necessary in order to allow any breakpoints
32 * or error conditions to be properly intercepted and reported to gdb.
33 * Two, a breakpoint needs to be generated to begin communication. This
34 * is most easily accomplished by a call to breakpoint(). Breakpoint()
35 * simulates a breakpoint by executing a trap #1. The breakpoint instruction
36 * is hardwired to trap #1 because not to do so is a compatibility problem--
37 * there either should be a standard breakpoint instruction, or the protocol
38 * should be extended to provide some means to communicate which breakpoint
39 * instruction is in use (or have the stub insert the breakpoint).
41 * Some explanation is probably necessary to explain how exceptions are
42 * handled. When an exception is encountered the 68000 pushes the current
43 * program counter and status register onto the supervisor stack and then
44 * transfers execution to a location specified in it's vector table.
45 * The handlers for the exception vectors are hardwired to jmp to an address
46 * given by the relation: (exception - 256) * 6. These are decending
47 * addresses starting from -6, -12, -18, ... By allowing 6 bytes for
48 * each entry, a jsr, jmp, bsr, ... can be used to enter the exception
49 * handler. Using a jsr to handle an exception has an added benefit of
50 * allowing a single handler to service several exceptions and use the
51 * return address as the key differentiation. The vector number can be
52 * computed from the return address by [ exception = (addr + 1530) / 6 ].
53 * The sole purpose of the routine catchException is to compute the
54 * exception number and push it on the stack in place of the return address.
55 * The external function exceptionHandler() is
56 * used to attach a specific handler to a specific m68k exception.
57 * For 68020 machines, the ability to have a return address around just
58 * so the vector can be determined is not necessary because the '020 pushes an
59 * extra word onto the stack containing the vector offset
61 * Because gdb will sometimes write to the stack area to execute function
62 * calls, this program cannot rely on using the supervisor stack so it
63 * uses it's own stack area reserved in the int array remcomStack.
67 * The following gdb commands are supported:
69 * command function Return value
71 * g return the value of the CPU registers hex data or ENN
72 * G set the value of the CPU registers OK or ENN
74 * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
75 * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
77 * c Resume at current address SNN ( signal NN)
78 * cAA..AA Continue at address AA..AA SNN
80 * s Step one instruction SNN
81 * sAA..AA Step one instruction from AA..AA SNN
85 * ? What was the last sigval ? SNN (signal NN)
87 * All commands and responses are sent with a packet which includes a
88 * checksum. A packet consists of
90 * $<packet info>#<checksum>.
93 * <packet info> :: <characters representing the command or response>
94 * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
96 * When a packet is received, it is first acknowledged with either '+' or '-'.
97 * '+' indicates a successful transfer. '-' indicates a failed transfer.
102 * $m0,10#2a +$00010203040506070809101112131415#42
104 ****************************************************************************/
110 /************************************************************************
112 * external low-level support routines
114 typedef void (*ExceptionHook
)(int); /* pointer to function with int parm */
115 typedef void (*Function
)(); /* pointer to a function */
117 extern int DebugPutChar(register int x
); /* write a single character */
118 extern int DebugGetChar(); /* read and return a single char */
120 void exceptionHandler(int n
, ExceptionHook a
); /* assign an exception handler */
122 /************************/
123 /* FORWARD DECLARATIONS */
124 /************************/
126 /************************************************************************/
127 /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
128 /* at least NUMREGBYTES*2 are needed for register packets */
131 static char initialized
; /* boolean flag. != 0 means we've been initialized */
134 /* debug > 0 prints ill-formed commands in valid packets & checksum errors */
136 static const char hexchars
[]="0123456789abcdef";
138 /* there are 180 bytes of registers on a 68020 w/68881 */
139 /* many of the fpa registers are 12 byte (96 bit) registers */
140 #define NUMREGBYTES 180
141 enum regnames
{D0
,D1
,D2
,D3
,D4
,D5
,D6
,D7
,
142 A0
,A1
,A2
,A3
,A4
,A5
,A6
,A7
,
144 FP0
,FP1
,FP2
,FP3
,FP4
,FP5
,FP6
,FP7
,
145 FPCONTROL
,FPSTATUS
,FPIADDR
149 * these should not be static cuz they can be used outside this module
151 int registers
[NUMREGBYTES
/4];
154 #define STACKSIZE 1024
155 int remcomStack
[STACKSIZE
/sizeof(int)];
158 /* the size of the exception stack on the 68020 varies with the type of
159 * exception. The following table is the number of WORDS used
160 * for each exception format.
162 const short exceptionSize
[] = { 4,4,6,4,4,4,4,4,29,10,16,46,12,4,4,4 };
166 const short exceptionSize
[] = { 4,4,6,4,4,4,4,4,4,4,4,4,16,4,4,4 };
169 /************* jump buffer used for setjmp/longjmp **************************/
172 /*************************** ASSEMBLY CODE MACROS *************************/
175 #define BREAKPOINT() asm(" trap #1");
180 if ((ch
>= 'a') && (ch
<= 'f')) return (ch
-'a'+10);
181 if ((ch
>= '0') && (ch
<= '9')) return (ch
-'0');
182 if ((ch
>= 'A') && (ch
<= 'F')) return (ch
-'A'+10);
187 /* scan for the sequence $<data>#<checksum> */
188 void getpacket(buffer
)
191 unsigned char checksum
;
192 unsigned char xmitcsum
;
198 /* wait around for the start character, ignore all other characters */
199 while ((ch
= (DebugGetChar() & 0x7f)) != '$');
205 /* now, read until a # or end of buffer is found */
206 while (count
< BUFMAX
) {
207 ch
= DebugGetChar() & 0x7f;
208 if (ch
== '#') break;
209 checksum
= checksum
+ ch
;
216 xmitcsum
= hex(DebugGetChar() & 0x7f) << 4;
217 xmitcsum
+= hex(DebugGetChar() & 0x7f);
219 if ((remote_debug
) && (checksum
!= xmitcsum
)) {
220 fprintf (stderr
,"bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n",
221 checksum
,xmitcsum
,buffer
);
225 if (checksum
!= xmitcsum
) DebugPutChar('-'); /* failed checksum */
227 DebugPutChar('+'); /* successful transfer */
228 /* if a sequence char is present, reply the sequence ID */
229 if (buffer
[2] == ':') {
230 DebugPutChar( buffer
[0] );
231 DebugPutChar( buffer
[1] );
232 /* remove sequence chars from buffer */
233 count
= strlen(buffer
);
234 for (i
=3; i
<= count
; i
++) buffer
[i
-3] = buffer
[i
];
238 } while (checksum
!= xmitcsum
);
242 /* send the packet in buffer. The host get's one chance to read it.
243 This routine does not wait for a positive acknowledge. */
246 void putpacket(buffer
)
249 unsigned char checksum
;
253 /* $<packet info>#<checksum>. */
259 while ((ch
=buffer
[count
]) != 0) {
260 if (! DebugPutChar(ch
)) return;
266 DebugPutChar(hexchars
[checksum
>> 4]);
267 DebugPutChar(hexchars
[checksum
% 16]);
269 } while (DebugGetChar() != '+');
273 char remcomInBuffer
[BUFMAX
];
274 char remcomOutBuffer
[BUFMAX
];
278 void debug_error(format
, parm
)
283 if (remote_debug
) fprintf (stderr
,format
,parm
);
287 /* convert the memory pointed to by mem into hex, placing result in buf */
288 /* return a pointer to the last char put in buf (null) */
289 char* mem2hex(mem
, buf
, count
)
296 for (i
=0;i
<count
;i
++) {
298 *buf
++ = hexchars
[ch
>> 4];
299 *buf
++ = hexchars
[ch
% 16];
305 /* convert the hex array pointed to by buf into binary to be placed in mem */
306 /* return a pointer to the character AFTER the last byte written */
307 char* hex2mem(buf
, mem
, count
)
314 for (i
=0;i
<count
;i
++) {
315 ch
= hex(*buf
++) << 4;
316 ch
= ch
+ hex(*buf
++);
322 /* a bus error has occurred, perform a longjmp
323 to return execution and allow handling of the error */
325 void handle_buserror()
327 longjmp(remcomEnv
,1);
330 /* this function takes the 68000 exception number and attempts to
331 translate this number into a unix compatible signal value */
332 int computeSignal( exceptionVector
)
336 switch (exceptionVector
) {
337 case 2 : sigval
= 10; break; /* bus error */
338 case 3 : sigval
= 10; break; /* address error */
339 case 4 : sigval
= 4; break; /* illegal instruction */
340 case 5 : sigval
= 8; break; /* zero divide */
341 case 6 : sigval
= 8; break; /* chk instruction */
342 case 7 : sigval
= 8; break; /* trapv instruction */
343 case 8 : sigval
= 11; break; /* privilege violation */
344 case 9 : sigval
= 5; break; /* trace trap */
345 case 10: sigval
= 4; break; /* line 1010 emulator */
346 case 11: sigval
= 4; break; /* line 1111 emulator */
348 /* Coprocessor protocol violation. Using a standard MMU or FPU
349 this cannot be triggered by software. Call it a SIGBUS. */
350 case 13: sigval
= 10; break;
352 case 31: sigval
= 2; break; /* interrupt */
353 case 32+1: sigval
= 5; break; /* trap #1 breakpoint */
354 case 32+15: sigval
= 5; break; /* trap #15 breakpoint */
356 case 48: sigval
= 8; break; /* floating point err */
357 case 49: sigval
= 8; break; /* floating point err */
358 case 50: sigval
= 8; break; /* zero divide */
359 case 51: sigval
= 8; break; /* underflow */
360 case 52: sigval
= 8; break; /* operand error */
361 case 53: sigval
= 8; break; /* overflow */
362 case 54: sigval
= 8; break; /* NAN */
364 sigval
= 7; /* "software generated"*/
369 /**********************************************/
370 /* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */
371 /* RETURN NUMBER OF CHARS PROCESSED */
372 /**********************************************/
373 int hexToInt(char **ptr
, int *intValue
)
382 hexValue
= hex(**ptr
);
385 *intValue
= (*intValue
<<4) | hexValue
;
398 * This function does all command procesing for interfacing to gdb.
400 void handle_exception(int exceptionVector
)
406 if ((exceptionVector
== (32+1)) ||
407 (exceptionVector
== (32+15)))
408 registers
[ PC
] -= 2;
411 if (remote_debug
) printf("vector=%d, sr=0x%x, pc=0x%x\n",
416 /* reply to host that an exception has occurred */
417 sigval
= computeSignal( exceptionVector
);
418 remcomOutBuffer
[0] = 'S';
419 remcomOutBuffer
[1] = hexchars
[sigval
>> 4];
420 remcomOutBuffer
[2] = hexchars
[sigval
% 16];
421 remcomOutBuffer
[3] = 0;
423 putpacket(remcomOutBuffer
);
427 remcomOutBuffer
[0] = 0;
428 getpacket(remcomInBuffer
);
429 switch (remcomInBuffer
[0]) {
430 case '?' : remcomOutBuffer
[0] = 'S';
431 remcomOutBuffer
[1] = hexchars
[sigval
>> 4];
432 remcomOutBuffer
[2] = hexchars
[sigval
% 16];
433 remcomOutBuffer
[3] = 0;
435 case 'd' : remote_debug
= !(remote_debug
); /* toggle debug flag */
437 case 'g' : /* return the value of the CPU registers */
438 mem2hex((char*) registers
, remcomOutBuffer
, NUMREGBYTES
);
440 case 'G' : /* set the value of the CPU registers - return OK */
441 hex2mem(&remcomInBuffer
[1], (char*) registers
, NUMREGBYTES
);
442 strcpy(remcomOutBuffer
,"OK");
445 /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
447 if (setjmp(remcomEnv
) == 0)
449 exceptionHandler(2,handle_buserror
);
451 /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */
452 ptr
= &remcomInBuffer
[1];
453 if (hexToInt(&ptr
,&addr
))
455 if (hexToInt(&ptr
,&length
))
458 mem2hex((char*) addr
, remcomOutBuffer
, length
);
463 strcpy(remcomOutBuffer
,"E01");
464 debug_error("malformed read memory command: %s",remcomInBuffer
);
468 exceptionHandler(2,handle_exception
);
469 strcpy(remcomOutBuffer
,"E03");
470 debug_error("bus error");
473 /* restore handler for bus error */
474 exceptionHandler(2,handle_exception
);
477 /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
479 if (setjmp(remcomEnv
) == 0) {
480 exceptionHandler(2,handle_buserror
);
482 /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */
483 ptr
= &remcomInBuffer
[1];
484 if (hexToInt(&ptr
,&addr
))
486 if (hexToInt(&ptr
,&length
))
489 hex2mem(ptr
, (char*) addr
, length
);
491 strcpy(remcomOutBuffer
,"OK");
495 strcpy(remcomOutBuffer
,"E02");
496 debug_error("malformed write memory command: %s",remcomInBuffer
);
500 exceptionHandler(2,handle_exception
);
501 strcpy(remcomOutBuffer
,"E03");
502 debug_error("bus error");
505 /* restore handler for bus error */
506 exceptionHandler(2,handle_exception
);
509 /* cAA..AA Continue at address AA..AA(optional) */
510 /* sAA..AA Step one instruction from AA..AA(optional) */
513 /* try to read optional parameter, pc unchanged if no parm */
514 ptr
= &remcomInBuffer
[1];
515 if (hexToInt(&ptr
,&addr
))
516 registers
[ PC
] = addr
;
518 /* clear the trace bit */
519 registers
[ PS
] &= 0x7fff;
521 /* set the trace bit if we're stepping */
522 if (remcomInBuffer
[0] == 's') registers
[ PS
] |= 0x8000;
524 /* Don't send a reply. The trap will signal GDB. */
527 /* kill the program */
528 case 'k' : /* do nothing */
532 /* reply to the request */
533 putpacket(remcomOutBuffer
);
537 /* this function is used to set up exception handlers for tracing and
539 void set_debug_traps()
555 /* This function will generate a breakpoint exception. It is used at the
556 beginning of a program to sync up with a debugger and can be used
557 otherwise as a quick means to stop program execution and "break" into
562 if (initialized
) BREAKPOINT();
570 /**************** end of gdbstub.c, rest is AROS interface ***********/
572 #include <aros/detach.h>
573 #include <proto/dos.h>
574 #include <proto/exec.h>
575 #include <exec/execbase.h>
577 /* write a single character */
578 int DebugPutChar(register int x
)
585 /* read and return a single char */
589 while ((c
= RawMayGetChar()) < 0);
594 static ExceptionHook exceptionTable
[256];
596 void exceptionHandler(int n
, ExceptionHook a
)
598 exceptionTable
[n
] = a
;
601 union M68K_Exception_Frame
{
620 void trapHandler_(union M68K_Exception_Frame
*frame
, ULONG id
)
622 if (SysBase
->AttnFlags
& AFF_68010
) {
623 /* M68010+, any trap */
624 registers
[PS
] = frame
->m68010
.sr
;
625 registers
[PC
] = frame
->m68010
.pc
;
626 } else if (id
== 2 || id
== 3) {
627 /* M68000 Bus/Address trap */
628 registers
[PS
] = frame
->m68000_bus
.sr
;
629 registers
[PC
] = frame
->m68000_bus
.pc
;
631 /* M68000 other traps */
632 registers
[PS
] = frame
->m68000
.sr
;
633 registers
[PC
] = frame
->m68000
.pc
;
636 /* TODO: Save FPU state */
638 if (id
> 256 || exceptionTable
[id
] == NULL
) {
639 handle_exception(id
);
641 (exceptionTable
[id
])(id
);
644 /* Restore registers */
645 if (SysBase
->AttnFlags
& AFF_68010
) {
646 /* M68010+, any trap */
647 frame
->m68010
.sr
= registers
[PS
];
648 frame
->m68010
.pc
= registers
[PC
];
649 } else if (id
== 2 || id
== 3) {
650 /* M68000 Bus/Address trap */
651 frame
->m68000_bus
.sr
= registers
[PS
];
652 frame
->m68000_bus
.pc
= registers
[PC
];
654 /* M68000 other traps */
655 frame
->m68000
.sr
= registers
[PS
];
656 frame
->m68000
.pc
= registers
[PC
];
660 /* Wrapper for the trapHandler_ */
661 extern void trapHandler(void);
664 " .global trapHandler\n"
666 " oriw #0x0700,%sr\n" /* Disable interrupts */
667 " movem.l %d0-%d7/%a0-%a6,registers\n"
668 " move.l %usp,%a0\n" /* registers[A7] = USP */
669 " lea.l registers,%a1\n"
670 " move.l %a0,%a1@(15*4)\n"
671 " lea.l %sp@(+4),%a0\n" /* A0 = exception frame */
672 " move.l %a0,%sp@-\n" /* Stack = Frame *, ID */
673 " jsr trapHandler_\n" /* Call C routine */
674 " addq #8,%sp\n" /* Pop off stack args */
675 " lea.l registers,%a1\n"
676 " move.l %a1@(15*4),%a0\n"
677 " move.l %a0,%usp\n" /* Save new USP */
678 " movem.l registers,%d0-%d7/%a0-%a6\n" /* Restore regs */
682 static APTR oldAlert
;
683 AROS_UFH2(void, myAlert
,
684 AROS_UFHA(ULONG
, alertNum
, D7
),
685 AROS_UFHA(struct ExecBase
*, SysBase
, A6
))
694 static APTR oldAddTask
;
695 AROS_UFH4(APTR
, myAddTask
,
696 AROS_UFHA(struct Task
*, task
, A1
),
697 AROS_UFHA(APTR
, initialPC
, A2
),
698 AROS_UFHA(APTR
, finalPC
, A3
),
699 AROS_UFHA(struct ExecBase
*, SysBase
, A6
))
705 ret
= AROS_UFC4(APTR
, oldAddTask
,
706 AROS_UFCA(struct Task
*, task
, A1
),
707 AROS_UFCA(APTR
, initialPC
, A2
),
708 AROS_UFCA(APTR
, finalPC
, A3
),
709 AROS_UFCA(struct ExecBase
*, SysBase
, A6
));
711 /* Set us up as the trap handler */
712 task
->tc_TrapCode
= trapHandler
;
719 static APTR
UpdateTrapCode(APTR newHandler
)
724 /* Update SysBase default */
725 oldHandler
= SysBase
->TaskTrapCode
;
726 SysBase
->TaskTrapCode
= newHandler
;
729 task
= FindTask(NULL
);
730 task
->tc_TrapCode
= newHandler
;
732 /* And any other tasks in the system.
734 ForeachNode(&SysBase
->TaskReady
, task
) {
735 if (task
->tc_TrapCode
== oldHandler
)
736 task
->tc_TrapCode
= newHandler
;
738 ForeachNode(&SysBase
->TaskWait
, task
) {
739 if (task
->tc_TrapCode
== oldHandler
)
740 task
->tc_TrapCode
= newHandler
;
746 int main(int argc
, char **argv
)
749 APTR oldTaskTrapCode
;
751 if ((DOSBase
= OpenLibrary("dos.library",36))) {
752 const char msg
[] = "GDB trapping enabled on the serial port\n";
753 /* We need to patch AddTask() to set
754 * us up as the default stub for tc_TrapCode.
756 * Although this is not needed (yet) on AROS,
757 * the Shell or Workbench may set tc_TrapCode
758 * before AddTask() on AOS.
760 * So instead of modifying SysBase->TrapCode,
761 * we have to inject this into the Exec Library.
763 /* Set up new handler
766 *(UWORD
*)0 = 0x4e41;
767 oldTaskTrapCode
= UpdateTrapCode(trapHandler
);
768 oldAddTask
= SetFunction((struct Library
*)SysBase
, -47 * LIB_VECTSIZE
, myAddTask
);
769 /* Patch Alert() to generate a breakpoint */
770 oldAlert
= SetFunction((struct Library
*)SysBase
, -18 * LIB_VECTSIZE
, myAlert
);
774 Write(Output(), msg
, sizeof(msg
)-1);
776 Wait(SIGBREAKF_CTRL_C
);
778 /* Restore traps. Not really safe, but better than nothing
781 SetFunction((struct Library
*)SysBase
, -47 * LIB_VECTSIZE
, oldAddTask
);
782 SetFunction((struct Library
*)SysBase
, -18 * LIB_VECTSIZE
, oldAlert
);
783 UpdateTrapCode(oldTaskTrapCode
);
786 CloseLibrary(DOSBase
);