1 # This is a shell archive. Remove anything before this line,
2 # then unpack it by saving it in a file and typing "sh file".
4 # Wrapped by Glenn Engel <glenne@labgre> on Mon Jun 12 15:19:20 1989
6 # This archive contains:
11 PATH=/bin:/usr/bin:$PATH; export PATH
14 cat >remcom.c <<'@EOF'
16 /****************************************************************************
18 THIS SOFTWARE IS NOT COPYRIGHTED
20 HP offers the following for use in the public domain. HP makes no
21 warranty with regard to the software or it's performance and the
22 user accepts the software "AS IS" with all faults.
24 HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
25 TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
26 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
28 ****************************************************************************/
30 /****************************************************************************
33 * $Module name: remcom.c $
36 * $Contributor: Lake Stevens Instrument Division$
38 * $Description: low level support for gdb debugger. $
40 * $Considerations: only works on target hardware $
42 * $Written by: Glenn Engel $
43 * $ModuleState: Experimental $
47 * To enable debugger support, two things need to happen. One, a
48 * call to set_debug_traps() is necessary in order to allow any breakpoints
49 * or error conditions to be properly intercepted and reported to gdb.
50 * Two, a breakpoint needs to be generated to begin communication. This
51 * is most easily accomplished by a call to breakpoint(). Breakpoint()
52 * simulates a breakpoint by executing a trap #1.
54 * Some explanation is probably necessary to explain how exceptions are
55 * handled. When an exception is encountered the 68000 pushes the current
56 * program counter and status register onto the supervisor stack and then
57 * transfers execution to a location specified in it's vector table.
58 * The handlers for the exception vectors are hardwired to jmp to an address
59 * given by the relation: (exception - 256) * 6. These are decending
60 * addresses starting from -6, -12, -18, ... By allowing 6 bytes for
61 * each entry, a jsr, jmp, bsr, ... can be used to enter the exception
62 * handler. Using a jsr to handle an exception has an added benefit of
63 * allowing a single handler to service several exceptions and use the
64 * return address as the key differentiation. The vector number can be
65 * computed from the return address by [ exception = (addr + 1530) / 6 ].
66 * The sole purpose of the routine _catchException is to compute the
67 * exception number and push it on the stack in place of the return address.
68 * The external function exceptionHandler() is
69 * used to attach a specific handler to a specific 68k exception.
70 * For 68020 machines, the ability to have a return address around just
71 * so the vector can be determined is not necessary because the '020 pushes an
72 * extra word onto the stack containing the vector offset
74 * Because gdb will sometimes write to the stack area to execute function
75 * calls, this program cannot rely on using the supervisor stack so it
76 * uses it's own stack area reserved in the int array remcomStack.
80 * The following gdb commands are supported:
82 * command function Return value
84 * g return the value of the CPU registers hex data or ENN
85 * G set the value of the CPU registers OK or ENN
87 * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
88 * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
90 * c Resume at current address SNN ( signal NN)
91 * cAA..AA Continue at address AA..AA SNN
93 * s Step one instruction SNN
94 * sAA..AA Step one instruction from AA..AA SNN
98 * ? What was the last sigval ? SNN (signal NN)
100 * All commands and responses are sent with a packet which includes a
101 * checksum. A packet consists of
103 * $<packet info>#<checksum>.
106 * <packet info> :: <characters representing the command or response>
107 * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
109 * When a packet is received, it is first acknowledged with either '+' or '-'.
110 * '+' indicates a successful transfer. '-' indicates a failed transfer.
115 * $m0,10#2a +$00010203040506070809101112131415#42
117 ****************************************************************************/
123 /************************************************************************
125 * external low-level support routines
127 typedef void (*ExceptionHook)(int); /* pointer to function with int parm */
128 typedef void (*Function)(); /* pointer to a function */
130 extern putDebugChar(); /* write a single character */
131 extern getDebugChar(); /* read and return a single char */
133 extern Function exceptionHandler(); /* assign an exception handler */
134 extern ExceptionHook exceptionHook; /* hook variable for errors/exceptions */
137 /************************************************************************/
138 /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
139 /* at least NUMREGBYTES*2 are needed for register packets */
142 static char initialized; /* boolean flag. != 0 means we've been initialized */
144 int remote_debug = 0;
145 /* debug > 0 prints ill-formed commands in valid packets & checksum errors */
147 char hexchars[]="0123456789abcdef";
149 /* there are 180 bytes of registers on a 68020 w/68881 */
150 /* many of the fpa registers are 12 byte (96 bit) registers */
151 #define NUMREGBYTES 180
152 enum regnames {D0,D1,D2,D3,D4,D5,D6,D7,
153 A0,A1,A2,A3,A4,A5,A6,A7,
155 FP0,FP1,FP2,FP3,FP4,FP5,FP6,FP7,
156 FPCONTROL,FPSTATUS,FPIADDR
159 typedef struct FrameStruct
161 struct FrameStruct *previous;
162 int exceptionPC; /* pc value when this frame created */
163 int exceptionVector; /* cpu vector causing exception */
164 short frameSize; /* size of cpu frame in words */
165 short sr; /* for 68000, this not always sr */
169 int morejunk[0]; /* exception frame, fp save... */
172 #define FRAMESIZE 500
173 static Frame *lastFrame;
174 static int frameStack[FRAMESIZE];
177 * these should not be static cuz they can be used outside this module
179 int registers[NUMREGBYTES/4];
182 static int remcomStack[400];
183 static int* stackPtr = &remcomStack[399];
186 * In many cases, the system will want to continue exception processing
187 * when a continue command is given.
188 * oldExceptionHook is a function to invoke in this case.
191 static ExceptionHook oldExceptionHook;
193 /* the size of the exception stack on the 68020 varies with the type of
194 * exception. The following table is the number of WORDS used
195 * for each exception format.
197 static short exceptionSize[] = { 4,4,6,4,4,4,4,4,29,10,16,46,4,4,4,4 };
199 /************* jump buffer used for setjmp/longjmp **************************/
202 /*************************** ASSEMBLY CODE MACROS *************************/
205 #ifdef __HAVE_68881__
206 /* do an fsave, then remember the address to begin a restore from */
207 #define SAVE_FP_REGS() asm(" fsave a0@-"); \
208 asm(" fmovemx fp0-fp7,_registers+72"); \
209 asm(" fmoveml fpcr/fpsr/fpi,_registers+168");
210 #define RESTORE_FP_REGS() asm(" fmoveml _registers+168,fpcr/fpsr/fpi"); \
211 asm(" fmovemx _registers+72,fp0-fp7"); \
212 asm(" frestore a0@+");
214 #define SAVE_FP_REGS()
215 #define RESTORE_FP_REGS()
216 #endif /* __HAVE_68881__ */
220 .globl _return_to_super
222 movel _registers+60,sp /* get new stack pointer */
223 movel _lastFrame,a0 /* get last frame info */
226 .globl _return_to_user
228 movel _registers+60,a0 /* get usp */
229 movel a0,usp /* set usp */
230 movel _superStack,sp /* get original stack pointer */
233 movel _lastFrame,a0 /* get last frame info */
234 movel a0@+,_lastFrame /* link in previous frame */
235 addql #8,a0 /* skip over pc, vector#*/
236 movew a0@+,d0 /* get # of words in cpu frame */
237 addw d0,a0 /* point to end of data */
238 addw d0,a0 /* point to end of data */
241 # copy the stack frame
248 asm(" moveml _registers,d0-d7/a0-a6");
249 asm(" rte"); /* pop and go! */
251 #define DISABLE_INTERRUPTS() asm(" oriw #0x0700,sr");
252 #define BREAKPOINT() asm(" trap #1");
254 /* this function is called immediately when a level 7 interrupt occurs */
255 /* if the previous interrupt level was 7 then we're already servicing */
256 /* this interrupt and an rte is in order to return to the debugger. */
257 /* For the 68000, the offset for sr is 6 due to the jsr return address */
260 .globl __debug_level7
264 asm(" movew sp@(2),d0");
266 asm(" movew sp@(6),d0");
268 asm(" andiw #0x700,d0
276 asm(" lea sp@(4),sp"); /* pull off 68000 return address */
280 extern void _catchException();
283 /* This function is called when a 68020 exception occurs. It saves
284 * all the cpu and fpcp regs in the _registers array, creates a frame on a
285 * linked list of frames which has the cpu and fpcp stack frames needed
286 * to properly restore the context of these processors, and invokes
287 * an exception handler (remcom_handler).
289 * stack on entry: stack on exit:
290 * N bytes of junk exception # MSWord
291 * Exception Format Word exception # MSWord
292 * Program counter LSWord
293 * Program counter MSWord
300 .globl __catchException
302 DISABLE_INTERRUPTS();
304 moveml d0-d7/a0-a6,_registers /* save registers */
305 movel _lastFrame,a0 /* last frame pointer */
309 lea _registers,a5 /* get address of registers */
310 movew sp@,d1 /* get status register */
311 movew d1,a5@(66) /* save sr */
312 movel sp@(2),a4 /* save pc in a4 for later use */
313 movel a4,a5@(68) /* save pc in _regisers[] */
316 # figure out how many bytes in the stack frame
317 movew sp@(6),d0 /* get '020 exception format */
318 movew d0,d2 /* make a copy of format word */
319 andiw #0xf000,d0 /* mask off format type */
320 rolw #5,d0 /* rotate into the low byte *2 */
321 lea _exceptionSize,a1
322 addw d0,a1 /* index into the table */
323 movew a1@,d0 /* get number of words in frame */
324 movew d0,d3 /* save it */
325 subw d0,a0 /* adjust save pointer */
326 subw d0,a0 /* adjust save pointer(bytes) */
327 movel a0,a1 /* copy save pointer */
328 subql #1,d0 /* predecrement loop counter */
335 # now that the stack has been clenaed,
336 # save the a7 in use at time of exception
337 movel sp,_superStack /* save supervisor sp */
338 andiw #0x2000,d1 /* were we in supervisor mode ? */
340 movel a7,a5@(60) /* save a7 */
344 movel a1,a5@(60) /* save user stack pointer */
352 # compute exception number
353 andl #0xfff,d2 /* mask off vector offset */
354 lsrw #2,d2 /* divide by 4 to get vect num */
355 movel d2,a0@- /* save it */
357 # save pc causing exception
360 # save old frame link and set the new value
361 movel _lastFrame,a1 /* last frame pointer */
362 movel a1,a0@- /* save pointer to prev frame */
365 movel d2,sp@- /* push exception num */
366 movel _exceptionHook,a0 /* get address of handler */
367 jbsr a0@ /* and call it */
368 jmp __returnFromException /* now, return */
371 /* This function is called when an exception occurs. It translates the
372 * return address found on the stack into an exception vector # which
373 * is then handled by either handle_exception or a system handler.
374 * _catchException provides a front end for both.
376 * stack on entry: stack on exit:
377 * Program counter MSWord exception # MSWord
378 * Program counter LSWord exception # MSWord
380 * Return Address MSWord
381 * Return Address LSWord
385 .globl __catchException
387 DISABLE_INTERRUPTS();
389 moveml d0-d7/a0-a6,_registers /* save registers */
390 movel _lastFrame,a0 /* last frame pointer */
394 lea _registers,a5 /* get address of registers */
395 movel sp@+,d2 /* pop return address */
396 addl #1530,d2 /* convert return addr to */
397 divs #6,d2 /* exception number */
400 moveql #3,d3 /* assume a three word frame */
402 cmpiw #3,d2 /* bus error or address error ? */
403 bgt normal /* if >3 then normal error */
404 movel sp@+,a0@- /* copy error info to frame buff*/
405 movel sp@+,a0@- /* these are never used */
406 moveql #7,d3 /* this is a 7 word frame */
409 movew sp@+,d1 /* pop status register */
410 movel sp@+,a4 /* pop program counter */
411 movew d1,a5@(66) /* save sr */
412 movel a4,a5@(68) /* save pc in _regisers[] */
413 movel a4,a0@- /* copy pc to frame buffer */
414 movew d1,a0@- /* copy sr to frame buffer */
416 movel sp,_superStack /* save supervisor sp */
418 andiw #0x2000,d1 /* were we in supervisor mode ? */
420 movel a7,a5@(60) /* save a7 */
423 movel usp,a1 /* save user stack pointer */
424 movel a1,a5@(60) /* save user stack pointer */
427 movew d3,a0@- /* push frame size in words */
428 movel d2,a0@- /* push vector number */
429 movel a4,a0@- /* push exception pc */
432 # save old frame link and set the new value
433 movel _lastFrame,a1 /* last frame pointer */
434 movel a1,a0@- /* save pointer to prev frame */
437 movel d2,sp@- /* push exception num */
438 movel _exceptionHook,a0 /* get address of handler */
439 jbsr a0@ /* and call it */
440 jmp __returnFromException /* now, return */
446 * remcomHandler is a front end for handle_exception. It moves the
447 * stack pointer into an area reserved for debugger use in case the
448 * breakpoint happened in supervisor mode.
450 asm("_remcomHandler:");
451 asm(" addl #4,sp"); /* pop off return address */
452 asm(" movel sp@+,d0"); /* get the exception number */
453 asm(" movel _stackPtr,sp"); /* move to remcom stack area */
454 asm(" movel d0,sp@-"); /* push exception onto stack */
455 asm(" jbsr _handle_exception"); /* this never returns */
456 asm(" rts"); /* return */
458 void _returnFromException( Frame *frame )
460 /* if no existing frame, dummy one up */
463 frame = lastFrame -1;
464 frame->frameSize = 4;
466 frame->fsaveHeader = 0;
467 frame->previous = lastFrame;
471 /* a 68000 cannot use the internal info pushed onto a bus error
472 * or address error frame when doing an RTE so don't put this info
473 * onto the stack or the stack will creep every time this happens.
478 /* throw away any frames in the list after this frame */
481 frame->sr = registers[(int) PS];
482 frame->pc = registers[(int) PC];
484 if (registers[(int) PS] & 0x2000)
486 /* return to supervisor mode... */
490 { /* return to user mode */
498 if ((ch >= 'a') && (ch <= 'f')) return (ch-'a'+10);
499 if ((ch >= '0') && (ch <= '9')) return (ch-'0');
504 /* scan for the sequence $<data>#<checksum> */
505 void getpacket(buffer)
508 unsigned char checksum;
509 unsigned char xmitcsum;
515 /* wait around for the start character, ignore all other characters */
516 while ((ch = getDebugChar()) != '$');
520 /* now, read until a # or end of buffer is found */
521 while (count < BUFMAX) {
523 if (ch == '#') break;
524 checksum = checksum + ch;
531 xmitcsum = hex(getDebugChar()) << 4;
532 xmitcsum += hex(getDebugChar());
533 if ((remote_debug ) && (checksum != xmitcsum)) {
534 fprintf(stderr,"bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n",
535 checksum,xmitcsum,buffer);
538 if (checksum != xmitcsum) putDebugChar('-'); /* failed checksum */
540 putDebugChar('+'); /* successful transfer */
541 /* if a sequence char is present, reply the sequence ID */
542 if (buffer[2] == ':') {
543 putDebugChar( buffer[0] );
544 putDebugChar( buffer[1] );
545 /* remove sequence chars from buffer */
546 count = strlen(buffer);
547 for (i=3; i <= count; i++) buffer[i-3] = buffer[i];
551 } while (checksum != xmitcsum);
555 /* send the packet in buffer. The host get's one chance to read it.
556 This routine does not wait for a positive acknowledge. */
559 void putpacket(buffer)
562 unsigned char checksum;
566 /* $<packet info>#<checksum>. */
572 while (ch=buffer[count]) {
573 if (! putDebugChar(ch)) return;
579 putDebugChar(hexchars[checksum >> 4]);
580 putDebugChar(hexchars[checksum % 16]);
582 } while (1 == 0); /* (getDebugChar() != '+'); */
586 static char inbuffer[BUFMAX];
587 static char outbuffer[BUFMAX];
591 void debug_error(format, parm)
595 if (remote_debug) fprintf(stderr,format,parm);
598 /* convert the memory pointed to by mem into hex, placing result in buf */
599 /* return a pointer to the last char put in buf (null) */
600 char* mem2hex(mem, buf, count)
607 for (i=0;i<count;i++) {
609 *buf++ = hexchars[ch >> 4];
610 *buf++ = hexchars[ch % 16];
616 /* convert the hex array pointed to by buf into binary to be placed in mem */
617 /* return a pointer to the character AFTER the last byte written */
618 char* hex2mem(buf, mem, count)
625 for (i=0;i<count;i++) {
626 ch = hex(*buf++) << 4;
627 ch = ch + hex(*buf++);
633 /* a bus error has occurred, perform a longjmp
634 to return execution and allow handling of the error */
636 void handle_buserror()
641 /* this function takes the 68000 exception number and attempts to
642 translate this number into a unix compatible signal value */
643 int computeSignal( exceptionVector )
647 switch (exceptionVector) {
648 case 2 : sigval = 10; break; /* bus error */
649 case 3 : sigval = 10; break; /* address error */
650 case 4 : sigval = 4; break; /* illegal instruction */
651 case 5 : sigval = 8; break; /* zero divide */
652 case 6 : sigval = 16; break; /* chk instruction */
653 case 7 : sigval = 16; break; /* trapv instruction */
654 case 8 : sigval = 11; break; /* privilege violation */
655 case 9 : sigval = 5; break; /* trace trap */
656 case 10: sigval = 4; break; /* line 1010 emulator */
657 case 11: sigval = 4; break; /* line 1111 emulator */
658 case 31: sigval = 2; break; /* interrupt */
659 case 33: sigval = 5; break; /* breakpoint */
660 case 40: sigval = 8; break; /* floating point err */
661 case 48: sigval = 8; break; /* floating point err */
662 case 49: sigval = 8; break; /* floating point err */
663 case 50: sigval = 8; break; /* zero divide */
664 case 51: sigval = 8; break; /* underflow */
665 case 52: sigval = 8; break; /* operand error */
666 case 53: sigval = 8; break; /* overflow */
667 case 54: sigval = 8; break; /* NAN */
669 sigval = 7; /* "software generated"*/
675 * This function does all command procesing for interfacing to gdb.
677 void handle_exception(int exceptionVector)
685 if (remote_debug) printf("vector=%d, sr=0x%x, pc=0x%x\n",
690 /* reply to host that an exception has occurred */
691 sigval = computeSignal( exceptionVector );
692 sprintf(outbuffer,"S%02x",sigval);
693 putpacket(outbuffer);
699 switch (inbuffer[0]) {
700 case '?' : sprintf(outbuffer,"S%02x",sigval);
702 case 'd' : remote_debug = !(remote_debug); /* toggle debug flag */
704 case 'g' : /* return the value of the CPU registers */
705 mem2hex((char*) registers, outbuffer, NUMREGBYTES);
707 case 'G' : /* set the value of the CPU registers - return OK */
708 hex2mem(&inbuffer[1], (char*) registers, NUMREGBYTES);
709 strcpy(outbuffer,"OK");
712 /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
714 if (setjmp(env) == 0) {
715 exceptionHandler(2,handle_buserror);
717 if (2 == sscanf(&inbuffer[1],"%x,%x",&addr,&length)) {
718 mem2hex((char*) addr, outbuffer, length);
721 strcpy(outbuffer,"E01");
722 debug_error("malformed read memory command: %s",inbuffer);
726 exceptionHandler(2,_catchException);
727 strcpy(outbuffer,"E03");
728 debug_error("bus error");
731 /* restore handler for bus error */
732 exceptionHandler(2,_catchException);
735 /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
737 if (setjmp(env) == 0) {
738 exceptionHandler(2,handle_buserror);
740 if (2 == sscanf(&inbuffer[1],"%x,%x:",&addr,&length)) {
741 ptr = strchr(inbuffer,':');
742 ptr += 1; /* point 1 past the colon */
743 hex2mem(ptr, (char*) addr, length);
744 strcpy(outbuffer,"OK");
747 strcpy(outbuffer,"E02");
748 debug_error("malformed write memory command: %s",inbuffer);
752 exceptionHandler(2,_catchException);
753 strcpy(outbuffer,"E03");
754 debug_error("bus error");
758 /* cAA..AA Continue at address AA..AA(optional) */
759 /* sAA..AA Step one instruction from AA..AA(optional) */
762 /* try to read optional parameter, addr unchanged if no parm */
763 if (1 == sscanf(&inbuffer[1],"%x",®isters[ PC ]));
764 newPC = registers[ PC];
766 /* clear the trace bit */
767 registers[ PS ] &= 0x7fff;
769 /* set the trace bit if we're stepping */
770 if (inbuffer[0] == 's') registers[ PS ] |= 0x8000;
773 * look for newPC in the linked list of exception frames.
774 * if it is found, use the old frame it. otherwise,
775 * fake up a dummy frame in returnFromException().
777 if (remote_debug) printf("new pc = 0x%x\n",newPC);
782 printf("frame at 0x%x has pc=0x%x, except#=%d\n",
783 frame,frame->exceptionPC,
784 frame->exceptionVector);
785 if (frame->exceptionPC == newPC) break; /* bingo! a match */
787 * for a breakpoint instruction, the saved pc may
788 * be off by two due to re-executing the instruction
789 * replaced by the trap instruction. Check for this.
791 if ((frame->exceptionVector == 33) &&
792 (frame->exceptionPC == (newPC+2))) break;
793 frame = frame->previous;
797 * If we found a match for the PC AND we are not returning
798 * as a result of a breakpoint (33),
799 * trace exception (9), nmi (31), jmp to
800 * the old exception handler as if this code never ran.
804 if ((frame->exceptionVector != 9) &&
805 (frame->exceptionVector != 31) &&
806 (frame->exceptionVector != 33))
809 * invoke the previous handler.
811 if (oldExceptionHook)
812 (*oldExceptionHook) (frame->exceptionVector);
813 newPC = registers[ PC ]; /* pc may have changed */
814 if (newPC != frame->exceptionPC)
817 printf("frame at 0x%x has pc=0x%x, except#=%d\n",
818 frame,frame->exceptionPC,
819 frame->exceptionVector);
820 /* dispose of this frame, we're skipping it (longjump?)*/
821 lastFrame = frame->previous;
827 _returnFromException( frame );
831 /* kill the program */
832 case 'k' : /* do nothing */
836 /* reply to the request */
837 putpacket(outbuffer);
842 /* this function is used to set up exception handlers for tracing and
844 void set_debug_traps()
846 extern void _debug_level7();
847 extern void remcomHandler();
850 for (exception = 2; exception <= 23; exception++)
851 exceptionHandler(exception,_catchException);
853 /* level 7 interrupt */
854 exceptionHandler(31,_debug_level7);
856 /* breakpoint exception (trap #1) */
857 exceptionHandler(33,_catchException);
859 /* floating point error (trap #8) */
860 exceptionHandler(40,_catchException);
862 /* 48 to 54 are floating point coprocessor errors */
863 for (exception = 48; exception <= 54; exception++)
864 exceptionHandler(exception,_catchException);
866 if (oldExceptionHook != remcomHandler)
868 oldExceptionHook = exceptionHook;
869 exceptionHook = remcomHandler;
874 lastFrame = (Frame *) &frameStack[FRAMESIZE-1];
875 lastFrame->previous = (Frame *) 0;
878 /* This function will generate a breakpoint exception. It is used at the
879 beginning of a program to sync up with a debugger and can be used
880 otherwise as a quick means to stop program execution and "break" into
885 if (initialized) BREAKPOINT();