1 unsigned long sp_ptr;
2 unsigned long pc_ptr;
3 int cnt;
4 #define UNWIND asm ("movel %/sp, %0" : "=g" (sp_ptr));\
5 printf ("\n\t\t== Starting at 0x%x ==\n", sp_ptr);\
6 for (cnt=4; cnt <=32; cnt+=4) {\
7 printf ("+%d(0x%x): 0x%x\t\t-%d(0x%x): 0x%x\n",\
8 cnt, (sp_ptr + cnt), *(unsigned long *)(sp_ptr + cnt),\
9 cnt, (sp_ptr - cnt), *(unsigned long *)(sp_ptr - cnt)\
10 ); }; fflush (stdout);
11
12 /****************************************************************************
13
14 THIS SOFTWARE IS NOT COPYRIGHTED
15
16 HP offers the following for use in the public domain. HP makes no
17 warranty with regard to the software or it's performance and the
18 user accepts the software "AS IS" with all faults.
19
20 HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
21 TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23
24 ****************************************************************************/
25
26 /****************************************************************************
27 * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
28 *
29 * Module name: remcom.c $
30 * Revision: 1.34 $
31 * Date: 91/03/09 12:29:49 $
32 * Contributor: Lake Stevens Instrument Division$
33 *
34 * Description: low level support for gdb debugger. $
35 *
36 * Considerations: only works on target hardware $
37 *
38 * Written by: Glenn Engel $
39 * ModuleState: Experimental $
40 *
41 * NOTES: See Below $
42 *
43 * To enable debugger support, two things need to happen. One, a
44 * call to set_debug_traps() is necessary in order to allow any breakpoints
45 * or error conditions to be properly intercepted and reported to gdb.
46 * Two, a breakpoint needs to be generated to begin communication. This
47 * is most easily accomplished by a call to breakpoint(). Breakpoint()
48 * simulates a breakpoint by executing a trap #1.
49 *
50 * Some explanation is probably necessary to explain how exceptions are
51 * handled. When an exception is encountered the 68000 pushes the current
52 * program counter and status register onto the supervisor stack and then
53 * transfers execution to a location specified in it's vector table.
54 * The handlers for the exception vectors are hardwired to jmp to an address
55 * given by the relation: (exception - 256) * 6. These are decending
56 * addresses starting from -6, -12, -18, ... By allowing 6 bytes for
57 * each entry, a jsr, jmp, bsr, ... can be used to enter the exception
58 * handler. Using a jsr to handle an exception has an added benefit of
59 * allowing a single handler to service several exceptions and use the
60 * return address as the key differentiation. The vector number can be
61 * computed from the return address by [ exception = (addr + 1530) / 6 ].
62 * The sole purpose of the routine _catchException is to compute the
63 * exception number and push it on the stack in place of the return address.
64 * The external function exceptionHandler() is
65 * used to attach a specific handler to a specific 68k exception.
66 * For 68020 machines, the ability to have a return address around just
67 * so the vector can be determined is not necessary because the '020 pushes an
68 * extra word onto the stack containing the vector offset
69 *
70 * Because gdb will sometimes write to the stack area to execute function
71 * calls, this program cannot rely on using the supervisor stack so it
72 * uses it's own stack area reserved in the int array remcomStack.
73 *
74 *************
75 *
76 * The following gdb commands are supported:
77 *
78 * command function Return value
79 *
80 * g return the value of the CPU registers hex data or ENN
81 * G set the value of the CPU registers OK or ENN
82 *
83 * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
84 * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
85 *
86 * c Resume at current address SNN ( signal NN)
87 * cAA..AA Continue at address AA..AA SNN
88 *
89 * s Step one instruction SNN
90 * sAA..AA Step one instruction from AA..AA SNN
91 *
92 * k kill
93 *
94 * ? What was the last sigval ? SNN (signal NN)
95 *
96 * All commands and responses are sent with a packet which includes a
97 * checksum. A packet consists of
98 *
99 * $<packet info>#<checksum>.
100 *
101 * where
102 * <packet info> :: <characters representing the command or response>
103 * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
104 *
105 * When a packet is received, it is first acknowledged with either '+' or '-'.
106 * '+' indicates a successful transfer. '-' indicates a failed transfer.
107 *
108 * Example:
109 *
110 * Host: Reply:
111 * $m0,10#2a +$00010203040506070809101112131415#42
112 *
113 ****************************************************************************/
114
115 #include <stdio.h>
116 #include <string.h>
117 #include <setjmp.h>
118 #include <_ansi.h>
119
120 /************************************************************************
121 *
122 * external low-level support routines
123 */
124 typedef void (*ExceptionHook)(int); /* pointer to function with int parm */
125 typedef void (*Function)(); /* pointer to a function */
126
127 extern int putDebugChar(); /* write a single character */
128 extern char getDebugChar(); /* read and return a single char */
129
130 ExceptionHook exceptionHook; /* hook variable for errors/exceptions */
131
132 /************************/
133 /* FORWARD DECLARATIONS */
134 /************************/
135 /** static void initializeRemcomErrorFrame PARAMS ((void)); **/
136 static void _DEFUN_VOID (initializeRemcomErrorFrame);
137
138 /************************************************************************/
139 /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
140 /* at least NUMREGBYTES*2 are needed for register packets */
141 #define BUFMAX 400
142
143 static char initialized; /* boolean flag. != 0 means we've been initialized */
144
145 int remote_debug = 0; /*** Robs Thu Sep 24 22:18:51 PDT 1992 ***/
146 /* debug > 0 prints ill-formed commands in valid packets & checksum errors */
147
148 static const char hexchars[]="0123456789abcdef";
149
150 /* there are 180 bytes of registers on a 68020 w/68881 */
151 /* many of the fpa registers are 12 byte (96 bit) registers */
152 #define NUMREGBYTES 180
153 enum regnames {D0,D1,D2,D3,D4,D5,D6,D7,
154 A0,A1,A2,A3,A4,A5,A6,A7,
155 PS,PC,
156 FP0,FP1,FP2,FP3,FP4,FP5,FP6,FP7,
157 FPCONTROL,FPSTATUS,FPIADDR
158 };
159
160 typedef struct FrameStruct
161 {
162 struct FrameStruct *previous;
163 int exceptionPC; /* pc value when this frame created */
164 int exceptionVector; /* cpu vector causing exception */
165 short frameSize; /* size of cpu frame in words */
166 short sr; /* for 68000, this not always sr */
167 int pc;
168 short format;
169 int fsaveHeader;
170 int morejunk[0]; /* exception frame, fp save... */
171 } Frame;
172
173 #define FRAMESIZE 500
174 int gdbFrameStack[FRAMESIZE];
175 Frame *lastFrame;
176
177 /*
178 * these should not be static cuz they can be used outside this module
179 */
180 int registers[NUMREGBYTES/4];
181 int superStack;
182
183 #define STACKSIZE 10000
184 int remcomStack[STACKSIZE/sizeof(int)];
185 int* stackPtr = &remcomStack[STACKSIZE/sizeof(int) - 1];
186
187 /*
188 * In many cases, the system will want to continue exception processing
189 * when a continue command is given.
190 * oldExceptionHook is a function to invoke in this case.
191 */
192
193 static ExceptionHook oldExceptionHook;
194
195 /* the size of the exception stack on the 68020 varies with the type of
196 * exception. The following table is the number of WORDS used
197 * for each exception format.
198 */
199 const short exceptionSize[] = { 4,4,6,4,4,4,4,4,29,10,16,46,12,4,4,4 };
200
201 /************* jump buffer used for setjmp/longjmp **************************/
202 jmp_buf remcomEnv;
203
204 #define BREAKPOINT() asm(" trap #1");
205
206 extern void _DEFUN_VOID (return_to_super);
207 extern void _DEFUN_VOID (return_to_user);
208 extern void _DEFUN_VOID (_catchException);
209
_returnFromException(Frame * frame)210 void _returnFromException( Frame *frame )
211 {
212 /* if no passed in frame, use the last one */
213 if (! frame)
214 {
215 frame = lastFrame;
216 frame->frameSize = 4;
217 frame->format = 0;
218 frame->fsaveHeader = -1; /* restore regs, but we dont have fsave info*/
219 }
220
221 #ifndef mc68020
222 /* a 68000 cannot use the internal info pushed onto a bus error
223 * or address error frame when doing an RTE so don't put this info
224 * onto the stack or the stack will creep every time this happens.
225 */
226 frame->frameSize=3;
227 #endif
228
229 /* throw away any frames in the list after this frame */
230 lastFrame = frame;
231
232 frame->sr = registers[(int) PS];
233 frame->pc = registers[(int) PC];
234
235 if (registers[(int) PS] & 0x2000)
236 {
237 /* return to supervisor mode... */
238 return_to_super();
239 }
240 else
241 { /* return to user mode */
242 return_to_user();
243 }
244 }
245
hex(ch)246 int hex(ch)
247 char ch;
248 {
249 if ((ch >= 'a') && (ch <= 'f')) return (ch-'a'+10);
250 if ((ch >= '0') && (ch <= '9')) return (ch-'0');
251 if ((ch >= 'A') && (ch <= 'F')) return (ch-'A'+10);
252 return (-1);
253 }
254
255
256 /* scan for the sequence $<data>#<checksum> */
getpacket(buffer)257 void getpacket(buffer)
258 char * buffer;
259 {
260 unsigned char checksum;
261 unsigned char xmitcsum;
262 int i;
263 int count;
264 char ch;
265
266 if (remote_debug) {
267 printf("\nGETPACKET: sr=0x%x, pc=0x%x, sp=0x%x\n",
268 registers[ PS ],
269 registers[ PC ],
270 registers[ A7 ]
271 ); fflush (stdout);
272 UNWIND
273 }
274
275 do {
276 /* wait around for the start character, ignore all other characters */
277 while ((ch = getDebugChar()) != '$');
278 checksum = 0;
279 xmitcsum = -1;
280
281 count = 0;
282
283 /* now, read until a # or end of buffer is found */
284 while (count < BUFMAX) {
285 ch = getDebugChar();
286 if (ch == '#') break;
287 checksum = checksum + ch;
288 buffer[count] = ch;
289 count = count + 1;
290 }
291 buffer[count] = 0;
292
293 if (ch == '#') {
294 xmitcsum = hex(getDebugChar()) << 4;
295 xmitcsum += hex(getDebugChar());
296 if ((remote_debug ) && (checksum != xmitcsum)) {
297 fprintf(stderr,"bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n",
298 checksum,xmitcsum,buffer);
299 }
300
301 if (checksum != xmitcsum) putDebugChar('-'); /* failed checksum */
302 else {
303 putDebugChar('+'); /* successful transfer */
304 /* if a sequence char is present, reply the sequence ID */
305 if (buffer[2] == ':') {
306 putDebugChar( buffer[0] );
307 putDebugChar( buffer[1] );
308 /* remove sequence chars from buffer */
309 count = strlen(buffer);
310 for (i=3; i <= count; i++) buffer[i-3] = buffer[i];
311 }
312 }
313 }
314 } while (checksum != xmitcsum);
315
316 }
317
318 /* send the packet in buffer. The host get's one chance to read it.
319 This routine does not wait for a positive acknowledge. */
320
putpacket(buffer)321 void putpacket(buffer)
322 char * buffer;
323 {
324 unsigned char checksum;
325 int count;
326 char ch;
327
328 /* $<packet info>#<checksum>. */
329 /*** do {***/
330 putDebugChar('$');
331 checksum = 0;
332 count = 0;
333
334 while (ch=buffer[count]) {
335 if (! putDebugChar(ch)) return;
336 checksum += ch;
337 count += 1;
338 }
339
340 putDebugChar('#');
341 putDebugChar(hexchars[checksum >> 4]);
342 putDebugChar(hexchars[checksum % 16]);
343
344 if (remote_debug) {
345 printf("\nPUTPACKET: sr=0x%x, pc=0x%x, sp=0x%x\n",
346 registers[ PS ],
347 registers[ PC ],
348 registers[ A7 ]
349 ); fflush (stdout);
350 UNWIND
351 }
352
353 /*** } while (getDebugChar() != '+'); ***/
354 /** } while (1 == 0); (getDebugChar() != '+'); **/
355
356 }
357
358 char remcomInBuffer[BUFMAX];
359 char remcomOutBuffer[BUFMAX];
360 static short error;
361
362
debug_error(format,parm)363 void debug_error(format, parm)
364 char * format;
365 char * parm;
366 {
367 if (remote_debug) fprintf(stderr,format,parm);
368 }
369
370 /* convert the memory pointed to by mem into hex, placing result in buf */
371 /* return a pointer to the last char put in buf (null) */
mem2hex(mem,buf,count)372 char* mem2hex(mem, buf, count)
373 char* mem;
374 char* buf;
375 int count;
376 {
377 int i;
378 unsigned char ch;
379 for (i=0;i<count;i++) {
380 ch = *mem++;
381 *buf++ = hexchars[ch >> 4];
382 *buf++ = hexchars[ch % 16];
383 }
384 *buf = 0;
385 return(buf);
386 }
387
388 /* convert the hex array pointed to by buf into binary to be placed in mem */
389 /* return a pointer to the character AFTER the last byte written */
hex2mem(buf,mem,count)390 char* hex2mem(buf, mem, count)
391 char* buf;
392 char* mem;
393 int count;
394 {
395 int i;
396 unsigned char ch;
397 for (i=0;i<count;i++) {
398 ch = hex(*buf++) << 4;
399 ch = ch + hex(*buf++);
400 *mem++ = ch;
401 }
402 return(mem);
403 }
404
405 /* a bus error has occurred, perform a longjmp
406 to return execution and allow handling of the error */
407
handle_buserror()408 void handle_buserror()
409 {
410 longjmp(remcomEnv,1);
411 }
412
413 /* this function takes the 68000 exception number and attempts to
414 translate this number into a unix compatible signal value */
computeSignal(exceptionVector)415 int computeSignal( exceptionVector )
416 int exceptionVector;
417 {
418 int sigval;
419 switch (exceptionVector) {
420 case 2 : sigval = 10; break; /* bus error */
421 case 3 : sigval = 10; break; /* address error */
422 case 4 : sigval = 4; break; /* illegal instruction */
423 case 5 : sigval = 8; break; /* zero divide */
424 case 6 : sigval = 16; break; /* chk instruction */
425 case 7 : sigval = 16; break; /* trapv instruction */
426 case 8 : sigval = 11; break; /* privilege violation */
427 case 9 : sigval = 5; break; /* trace trap */
428 case 10: sigval = 4; break; /* line 1010 emulator */
429 case 11: sigval = 4; break; /* line 1111 emulator */
430 case 13: sigval = 8; break; /* floating point err */
431 case 31: sigval = 2; break; /* interrupt */
432 case 33: sigval = 5; break; /* breakpoint */
433 case 40: sigval = 8; break; /* floating point err */
434 case 48: sigval = 8; break; /* floating point err */
435 case 49: sigval = 8; break; /* floating point err */
436 case 50: sigval = 8; break; /* zero divide */
437 case 51: sigval = 8; break; /* underflow */
438 case 52: sigval = 8; break; /* operand error */
439 case 53: sigval = 8; break; /* overflow */
440 case 54: sigval = 8; break; /* NAN */
441 default:
442 sigval = 7; /* "software generated"*/
443 }
444 return (sigval);
445 }
446
447 /**********************************************/
448 /* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */
449 /* RETURN NUMBER OF CHARS PROCESSED */
450 /**********************************************/
hexToInt(char ** ptr,int * intValue)451 int hexToInt(char **ptr, int *intValue)
452 {
453 int numChars = 0;
454 int hexValue;
455
456 *intValue = 0;
457
458 while (**ptr)
459 {
460 hexValue = hex(**ptr);
461 if (hexValue >=0)
462 {
463 *intValue = (*intValue <<4) | hexValue;
464 numChars ++;
465 }
466 else
467 break;
468
469 (*ptr)++;
470 }
471
472 return (numChars);
473 }
474
475 /*
476 * This function does all command procesing for interfacing to gdb.
477 */
handle_exception(int exceptionVector)478 void handle_exception(int exceptionVector)
479 {
480 int sigval;
481 int addr, length;
482 char * ptr;
483 int newPC;
484 Frame *frame;
485
486 if (remote_debug) printf("\nHANDLE_EXCEPTION: vector=%d, sr=0x%x, pc=0x%x, sp=0x%x\n",
487 exceptionVector,
488 registers[ PS ],
489 registers[ PC ],
490 registers[ A7 ]
491 ); fflush (stdout);
492
493 /* reply to host that an exception has occurred */
494 sigval = computeSignal( exceptionVector );
495 remcomOutBuffer[0] = 'S';
496 remcomOutBuffer[1] = hexchars[sigval >> 4];
497 remcomOutBuffer[2] = hexchars[sigval % 16];
498 remcomOutBuffer[3] = 0;
499
500 putpacket(remcomOutBuffer);
501
502 while (1==1) {
503 error = 0;
504 remcomOutBuffer[0] = 0;
505 getpacket(remcomInBuffer);
506 switch (remcomInBuffer[0]) {
507 case '?' : remcomOutBuffer[0] = 'S';
508 remcomOutBuffer[1] = hexchars[sigval >> 4];
509 remcomOutBuffer[2] = hexchars[sigval % 16];
510 remcomOutBuffer[3] = 0;
511 break;
512 case 'd' : remote_debug = !(remote_debug); /* toggle debug flag */
513 break;
514 case 'g' : /* return the value of the CPU registers */
515 mem2hex((char*) registers, remcomOutBuffer, NUMREGBYTES);
516 break;
517 case 'G' : /* set the value of the CPU registers - return OK */
518 hex2mem(&remcomInBuffer[1], (char*) registers, NUMREGBYTES);
519 strcpy(remcomOutBuffer,"OK");
520 break;
521
522 /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
523 case 'm' :
524 if (setjmp(remcomEnv) == 0)
525 {
526 exceptionHandler(2,handle_buserror);
527
528 /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */
529 ptr = &remcomInBuffer[1];
530 if (hexToInt(&ptr,&addr))
531 if (*(ptr++) == ',')
532 if (hexToInt(&ptr,&length))
533 {
534 ptr = 0;
535 mem2hex((char*) addr, remcomOutBuffer, length);
536 }
537
538 if (ptr)
539 {
540 strcpy(remcomOutBuffer,"E01");
541 debug_error("malformed read memory command: %s",remcomInBuffer);
542 }
543 }
544 else {
545 exceptionHandler(2,_catchException);
546 strcpy(remcomOutBuffer,"E03");
547 debug_error("bus error");
548 }
549
550 /* restore handler for bus error */
551 exceptionHandler(2,_catchException);
552 break;
553
554 /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
555 case 'M' :
556 if (setjmp(remcomEnv) == 0) {
557 exceptionHandler(2,handle_buserror);
558
559 /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */
560 ptr = &remcomInBuffer[1];
561 if (hexToInt(&ptr,&addr))
562 if (*(ptr++) == ',')
563 if (hexToInt(&ptr,&length))
564 if (*(ptr++) == ':')
565 {
566 hex2mem(ptr, (char*) addr, length);
567 ptr = 0;
568 strcpy(remcomOutBuffer,"OK");
569 }
570 if (ptr)
571 {
572 strcpy(remcomOutBuffer,"E02");
573 debug_error("malformed write memory command: %s",remcomInBuffer);
574 }
575 }
576 else {
577 exceptionHandler(2,_catchException);
578 strcpy(remcomOutBuffer,"E03");
579 debug_error("bus error");
580 }
581
582 /* restore handler for bus error */
583 exceptionHandler(2,_catchException);
584 break;
585
586 /* cAA..AA Continue at address AA..AA(optional) */
587 /* sAA..AA Step one instruction from AA..AA(optional) */
588 case 'c' :
589 case 's' :
590 /* try to read optional parameter, pc unchanged if no parm */
591 ptr = &remcomInBuffer[1];
592 if (hexToInt(&ptr,&addr))
593 registers[ PC ] = addr;
594
595 newPC = registers[ PC];
596
597 /* clear the trace bit */
598 registers[ PS ] &= 0x7fff;
599
600 /* set the trace bit if we're stepping */
601 if (remcomInBuffer[0] == 's') registers[ PS ] |= 0x8000;
602
603 /*
604 * look for newPC in the linked list of exception frames.
605 * if it is found, use the old frame it. otherwise,
606 * fake up a dummy frame in returnFromException().
607 */
608 if (remote_debug) printf("new pc = 0x%x\n",newPC);
609 frame = lastFrame;
610 while (frame)
611 {
612 if (remote_debug)
613 printf("frame at 0x%x has pc=0x%x, except#=%d\n",
614 frame,frame->exceptionPC,
615 frame->exceptionVector);
616 if (frame->exceptionPC == newPC) break; /* bingo! a match */
617 /*
618 * for a breakpoint instruction, the saved pc may
619 * be off by two due to re-executing the instruction
620 * replaced by the trap instruction. Check for this.
621 */
622 if ((frame->exceptionVector == 33) &&
623 (frame->exceptionPC == (newPC+2))) break;
624 if (frame == frame->previous)
625 {
626 frame = 0; /* no match found */
627 break;
628 }
629 frame = frame->previous;
630 }
631
632 /*
633 * If we found a match for the PC AND we are not returning
634 * as a result of a breakpoint (33),
635 * trace exception (9), nmi (31), jmp to
636 * the old exception handler as if this code never ran.
637 */
638 if (frame)
639 {
640 if ((frame->exceptionVector != 9) &&
641 (frame->exceptionVector != 31) &&
642 (frame->exceptionVector != 33))
643 {
644 /*
645 * invoke the previous handler.
646 */
647 if (oldExceptionHook)
648 (*oldExceptionHook) (frame->exceptionVector);
649 newPC = registers[ PC ]; /* pc may have changed */
650 if (newPC != frame->exceptionPC)
651 {
652 if (remote_debug)
653 printf("frame at 0x%x has pc=0x%x, except#=%d\n",
654 frame,frame->exceptionPC,
655 frame->exceptionVector);
656 /* re-use the last frame, we're skipping it (longjump?)*/
657 frame = (Frame *) 0;
658 _returnFromException( frame ); /* this is a jump */
659 }
660 }
661 }
662
663 /* if we couldn't find a frame, create one */
664 if (frame == 0)
665 {
666 frame = lastFrame -1 ;
667
668 /* by using a bunch of print commands with breakpoints,
669 it's possible for the frame stack to creep down. If it creeps
670 too far, give up and reset it to the top. Normal use should
671 not see this happen.
672 */
673 if ((unsigned int) (frame-2) < (unsigned int) &gdbFrameStack)
674 {
675 initializeRemcomErrorFrame();
676 frame = lastFrame;
677 }
678 frame->previous = lastFrame;
679 lastFrame = frame;
680 frame = 0; /* null so _return... will properly initialize it */
681 }
682
683 _returnFromException( frame ); /* this is a jump */
684
685 break;
686
687 /* kill the program */
688 case 'k' : /* do nothing */
689 break;
690 } /* switch */
691
692 /* reply to the request */
693 putpacket(remcomOutBuffer);
694 }
695 }
696
697
initializeRemcomErrorFrame()698 void initializeRemcomErrorFrame()
699 {
700 lastFrame = ((Frame *) &gdbFrameStack[FRAMESIZE-1]) - 1;
701 lastFrame->previous = lastFrame;
702 }
703
704 /* this function is used to set up exception handlers for tracing and
705 breakpoints */
set_debug_traps()706 void set_debug_traps()
707 {
708 extern void _debug_level7();
709 extern void remcomHandler();
710 int exception;
711
712 initializeRemcomErrorFrame();
713 stackPtr = &remcomStack[STACKSIZE/sizeof(int) - 1];
714
715 setup_vectors();
716
717 if (oldExceptionHook != remcomHandler)
718 {
719 oldExceptionHook = exceptionHook;
720 exceptionHook = remcomHandler;
721 }
722
723 initialized = 1;
724
725 }
726 /* This function will generate a breakpoint exception. It is used at the
727 beginning of a program to sync up with a debugger and can be used
728 otherwise as a quick means to stop program execution and "break" into
729 the debugger. */
730
breakpoint()731 void breakpoint()
732 {
733 if (initialized) BREAKPOINT();
734 }
735