Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:49

0001 #define  GDB_STUB_ENABLE_THREAD_SUPPORT 1
0002 /****************************************************************************
0003 
0004         THIS SOFTWARE IS NOT COPYRIGHTED
0005 
0006    HP offers the following for use in the public domain.  HP makes no
0007    warranty with regard to the software or it's performance and the
0008    user accepts the software "AS IS" with all faults.
0009 
0010    HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
0011    TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
0012    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
0013 
0014 ****************************************************************************/
0015 
0016 /****************************************************************************
0017  *  Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
0018  *
0019  *  Module name: remcom.c $
0020  *  Revision: 1.34 $
0021  *  Date: 91/03/09 12:29:49 $
0022  *  Contributor:     Lake Stevens Instrument Division$
0023  *
0024  *  Description:     low level support for gdb debugger. $
0025  *
0026  *  Considerations:  only works on target hardware $
0027  *
0028  *  Written by:      Glenn Engel $
0029  *  ModuleState:     Experimental $
0030  *
0031  *  NOTES:           See Below $
0032  *
0033  *  To enable debugger support, two things need to happen.  One, a
0034  *  call to set_debug_traps() is necessary in order to allow any breakpoints
0035  *  or error conditions to be properly intercepted and reported to gdb.
0036  *  Two, a breakpoint needs to be generated to begin communication.  This
0037  *  is most easily accomplished by a call to breakpoint().  Breakpoint()
0038  *  simulates a breakpoint by executing a trap #1.  The breakpoint instruction
0039  *  is hardwired to trap #1 because not to do so is a compatibility problem--
0040  *  there either should be a standard breakpoint instruction, or the protocol
0041  *  should be extended to provide some means to communicate which breakpoint
0042  *  instruction is in use (or have the stub insert the breakpoint).
0043  *
0044  *  Some explanation is probably necessary to explain how exceptions are
0045  *  handled.  When an exception is encountered the 68000 pushes the current
0046  *  program counter and status register onto the supervisor stack and then
0047  *  transfers execution to a location specified in it's vector table.
0048  *  The handlers for the exception vectors are hardwired to jmp to an address
0049  *  given by the relation:  (exception - 256) * 6.  These are decending
0050  *  addresses starting from -6, -12, -18, ...  By allowing 6 bytes for
0051  *  each entry, a jsr, jmp, bsr, ... can be used to enter the exception
0052  *  handler.  Using a jsr to handle an exception has an added benefit of
0053  *  allowing a single handler to service several exceptions and use the
0054  *  return address as the key differentiation.  The vector number can be
0055  *  computed from the return address by [ exception = (addr + 1530) / 6 ].
0056  *  The sole purpose of the routine _catchException is to compute the
0057  *  exception number and push it on the stack in place of the return address.
0058  *  The external function exceptionHandler() is
0059  *  used to attach a specific handler to a specific m68k exception.
0060  *  For 68020 machines, the ability to have a return address around just
0061  *  so the vector can be determined is not necessary because the '020 pushes an
0062  *  extra word onto the stack containing the vector offset
0063  *
0064  *  Because gdb will sometimes write to the stack area to execute function
0065  *  calls, this program cannot rely on using the supervisor stack so it
0066  *  uses it's own stack area reserved in the int array remcomStack.
0067  *
0068  *************
0069  *
0070  *    The following gdb commands are supported:
0071  *
0072  * command          function                               Return value
0073  *
0074  *    g             return the value of the CPU registers  hex data or ENN
0075  *    G             set the value of the CPU registers     OK or ENN
0076  *
0077  *    mAA..AA,LLLL  Read LLLL bytes at address AA..AA      hex data or ENN
0078  *    MAA..AA,LLLL: Write LLLL bytes at address AA.AA      OK or ENN
0079  *
0080  *    c             Resume at current address              SNN   ( signal NN)
0081  *    cAA..AA       Continue at address AA..AA             SNN
0082  *
0083  *    s             Step one instruction                   SNN
0084  *    sAA..AA       Step one instruction from AA..AA       SNN
0085  *
0086  *    k             kill
0087  *
0088  *    ?             What was the last sigval ?             SNN   (signal NN)
0089  *
0090  * All commands and responses are sent with a packet which includes a
0091  * checksum.  A packet consists of
0092  *
0093  * $<packet info>#<checksum>.
0094  *
0095  * where
0096  * <packet info> :: <characters representing the command or response>
0097  * <checksum>    :: < two hex digits computed as modulo 256 sum of <packetinfo>>
0098  *
0099  * When a packet is received, it is first acknowledged with either '+' or '-'.
0100  * '+' indicates a successful transfer.  '-' indicates a failed transfer.
0101  *
0102  * Example:
0103  *
0104  * Host:                  Reply:
0105  * $m0,10#2a               +$00010203040506070809101112131415#42
0106  *
0107  ***************************************************************************
0108  *    added 05/27/02 by IMD, Thomas Doerfler:
0109  *    XAA..AA,LLLL: Write LLLL binary bytes at address AA.AA      OK or ENN
0110  ****************************************************************************/
0111 
0112 #include <stdio.h>
0113 #include <string.h>
0114 #include <setjmp.h>
0115 /* #include <asm.h>*/
0116 #include <rtems.h>
0117 #include <rtems/score/cpu.h>
0118 #include "gdb_if.h"
0119 
0120 #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
0121 static char do_threads = 1;      /* != 0 means we are supporting threads */
0122 int current_thread_registers[NUMREGBYTES/4];
0123 #endif
0124 
0125 /************************************************************************
0126  *
0127  * external low-level support routines
0128  */
0129 typedef void (*ExceptionHook)(int);   /* pointer to function with int parm */
0130 typedef void (*Function)();           /* pointer to a function */
0131 
0132 extern void putDebugChar(); /* write a single character      */
0133 extern int getDebugChar();  /* read and return a single char */
0134 
0135 /************************/
0136 /* FORWARD DECLARATIONS */
0137 /************************/
0138 static void
0139 initializeRemcomErrorFrame ();
0140 
0141 /************************************************************************/
0142 /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
0143 /* at least NUMREGBYTES*2 are needed for register packets */
0144 #define BUFMAX 400
0145 
0146 static bool initialized = false ;  /* boolean flag. != 0 means we've been initialized */
0147 
0148 int     remote_debug;
0149 /*  debug >  0 prints ill-formed commands in valid packets & checksum errors */
0150 
0151 const char gdb_hexchars[]="0123456789abcdef";
0152 #define highhex(x) gdb_hexchars [(x >> 4) & 0xf]
0153 #define lowhex(x) gdb_hexchars [x & 0xf]
0154 
0155 
0156 /* We keep a whole frame cache here.  "Why?", I hear you cry, "doesn't
0157    GDB handle that sort of thing?"  Well, yes, I believe the only
0158    reason for this cache is to save and restore floating point state
0159    (fsave/frestore).  A cleaner way to do this would be to make the
0160  fsave data part of the registers which GDB deals with like any
0161    other registers.  This should not be a performance problem if the
0162    ability to read individual registers is added to the protocol.  */
0163 
0164 typedef struct FrameStruct
0165 {
0166     struct FrameStruct  *previous;
0167     int       exceptionPC;      /* pc value when this frame created */
0168     int       exceptionVector;  /* cpu vector causing exception     */
0169     short     frameSize;        /* size of cpu frame in words       */
0170     short     sr;               /* for 68000, this not always sr    */
0171     int       pc;
0172     short     format;
0173     int       fsaveHeader;
0174     int       morejunk[0];        /* exception frame, fp save... */
0175 } Frame;
0176 
0177 #define FRAMESIZE 500
0178 int   gdbFrameStack[FRAMESIZE];
0179 static Frame *lastFrame;
0180 
0181 /*
0182  * these should not be static cuz they can be used outside this module
0183  */
0184 int registers[NUMREGBYTES/4];
0185 int superStack;
0186 
0187 #define STACKSIZE 10000
0188 int remcomStack[STACKSIZE/sizeof(int)];
0189 static int* stackPtr = &remcomStack[STACKSIZE/sizeof(int) - 1];
0190 
0191 /*
0192  * In many cases, the system will want to continue exception processing
0193  * when a continue command is given.
0194  * oldExceptionHook is a function to invoke in this case.
0195  */
0196 
0197 static ExceptionHook oldExceptionHook;
0198 /* the size of the exception stack on the 68020/30/40/CPU32 varies with
0199  * the type of exception.  The following table is the number of WORDS used
0200  * for each exception format.
0201  * This table should be common for all 68k with VBR
0202  * although each member only implements some of the formats
0203  */
0204 const short exceptionSize[] = { 4,4,6,6,4,4,4,30,29,10,16,46,12,4,4,4 };
0205 
0206 /************* jump buffer used for setjmp/longjmp **************************/
0207 jmp_buf remcomEnv;
0208 
0209 /***************************  ASSEMBLY CODE MACROS *************************/
0210 /*                                     */
0211 
0212 #ifdef __HAVE_68881__
0213 /* do an fsave, then remember the address to begin a restore from */
0214 #define SAVE_FP_REGS()    __asm__ (" fsave   -(%a0)");      \
0215               __asm__ (" fmovem.x %fp0-%fp7,registers+72");        \
0216               __asm__ (" fmovem.l %fpcr/%fpsr/%fpi,registers+168");
0217 #define RESTORE_FP_REGS()                              \
0218 __asm__ ("                                                \n\
0219     fmovem.l  registers+168,%fpcr/%fpsr/%fpi            \n\
0220     fmovem.x  registers+72,%fp0-%fp7                   \n\
0221     cmp.l     #-1,(%a0)     |  skip frestore flag set ? \n\
0222     beq      skip_frestore                           \n\
0223     frestore (%a0)+                                    \n\
0224 skip_frestore:                                       \n\
0225 ");
0226 
0227 #else
0228 #define SAVE_FP_REGS()
0229 #define RESTORE_FP_REGS()
0230 #endif /* __HAVE_68881__ */
0231 
0232 void return_to_super();
0233 void return_to_user();
0234 extern void _catchException ();
0235 
0236 void m68k_exceptionHandler
0237 (
0238  int vecnum,                            /* vector index to hook at         */
0239  void *vector                           /* address of handler function     */
0240 )
0241 {
0242   void **vec_base = NULL;
0243 #if M68K_HAS_VBR
0244   /*
0245    * get vector base register
0246    */
0247   __asm__ (" movec.l %%vbr,%0":"=a" (vec_base));
0248 #endif
0249   vec_base[vecnum] = vector;
0250 }
0251 
0252 ExceptionHook exceptionHook;  /* hook variable for errors/exceptions */
0253 
0254 void m68k_stub_dummy_asm_wrapper()
0255      /*
0256       * this dummy wrapper function ensures,
0257       * that the C compiler manages sections properly
0258       */
0259 {
0260 __asm__ ("\n\
0261 .globl return_to_super                          \n\
0262 return_to_super:                            \n\
0263         move.l   registers+60,%sp /* get new stack pointer */           \n\
0264         move.l   lastFrame,%a0   /* get last frame info  */             \n\
0265         bra     return_to_any                                           \n\
0266                                     \n\
0267 .globl _return_to_user                          \n\
0268 return_to_user:                             \n\
0269         move.l   registers+60,%a0 /* get usp */                         \n\
0270         move.l   %a0,%usp           /* set usp */           \n\
0271         move.l   superStack,%sp  /* get original stack pointer */       \n\
0272                                     \n\
0273 return_to_any:                              \n\
0274         move.l   lastFrame,%a0   /* get last frame info  */             \n\
0275         move.l   (%a0)+,lastFrame /* link in previous frame     */      \n\
0276         addq.l   #8,%a0           /* skip over pc, vector#*/            \n\
0277         move.w   (%a0)+,%d0         /* get # of words in cpu frame */   \n\
0278         add.w    %d0,%a0           /* point to end of data        */    \n\
0279         add.w    %d0,%a0           /* point to end of data        */    \n\
0280         move.l   %a0,%a1                                                \n\
0281 #                                                                       \n\
0282 # copy the stack frame                                                  \n\
0283         subq.l   #1,%d0                                                 \n\
0284 copyUserLoop:                                                           \n\
0285         move.w   -(%a1),-(%sp)                      \n\
0286         dbf     %d0,copyUserLoop                                        \n\
0287 ");
0288         RESTORE_FP_REGS()
0289    __asm__ ("   movem.l  registers,%d0-%d7/%a0-%a6");
0290    __asm__ ("   rte");  /* pop and go! */
0291 
0292 #define DISABLE_INTERRUPTS()   __asm__ ("         oriw   #0x0700,%sr");
0293 #define BREAKPOINT() __asm__ ("   trap #2");
0294 
0295 /* this function is called immediately when a level 7 interrupt occurs */
0296 /* if the previous interrupt level was 7 then we're already servicing  */
0297 /* this interrupt and an rte is in order to return to the debugger.    */
0298 /* For the 68000, the offset for sr is 6 due to the jsr return address */
0299 __asm__ ("                      \n\
0300 .text                       \n\
0301 .globl _debug_level7                \n\
0302 _debug_level7:                  \n\
0303     move.w   %d0,-(%sp)");
0304 #if M68K_HAS_VBR
0305 __asm__ ("  move.w   2(%sp),%d0");
0306 #else
0307 __asm__ ("  move.w   6(%sp),%d0");
0308 #endif
0309 __asm__ ("  andi.w   #0x700,%d0         \n\
0310     cmpi.w   #0x700,%d0         \n\
0311     beq     already7            \n\
0312         move.w   (%sp)+,%d0         \n\
0313         bra     _catchException         \n\
0314 already7:                   \n\
0315     move.w   (%sp)+,%d0");
0316 #if !M68K_HAS_VBR
0317 __asm__ ("  lea     4(%sp),%sp");     /* pull off 68000 return address */
0318 #endif
0319 __asm__ ("  rte");
0320 
0321 #if M68K_HAS_VBR
0322 /* This function is called when a 68020 exception occurs.  It saves
0323  * all the cpu and fpcp regs in the _registers array, creates a frame on a
0324  * linked list of frames which has the cpu and fpcp stack frames needed
0325  * to properly restore the context of these processors, and invokes
0326  * an exception handler (remcom_handler).
0327  *
0328  * stack on entry:                       stack on exit:
0329  *   N bytes of junk                     exception # MSWord
0330  *   Exception Format Word               exception # MSWord
0331  *   Program counter LSWord
0332  *   Program counter MSWord
0333  *   Status Register
0334  *
0335  *
0336  */
0337 __asm__ ("                      \n\
0338 .text                       \n\
0339 .globl _catchException              \n\
0340 _catchException:");
0341 DISABLE_INTERRUPTS();
0342 __asm__ ("                                  \n\
0343         movem.l  %d0-%d7/%a0-%a6,registers /* save registers        */  \n\
0344     move.l  lastFrame,%a0   /* last frame pointer */        \n\
0345 ");
0346 SAVE_FP_REGS();
0347 __asm__ ("\n\
0348     lea     registers,%a5   /* get address of registers     */\n\
0349         move.w  (%sp),%d1        /* get status register          */\n\
0350         move.w   %d1,66(%a5)      /* save sr            */  \n\
0351     move.l   2(%sp),%a4      /* save pc in a4 for later use  */\n\
0352     move.w   6(%sp),%d0 /* get '020 exception format    */\n\
0353         move.w   %d0,%d2        /* make a copy of format word   */\n\
0354 #\n\
0355 # compute exception number\n\
0356     and.l    #0xfff,%d2     /* mask off vector offset   */\n\
0357     lsr.w    #2,%d2     /* divide by 4 to get vect num  */\n\
0358 /* #if 1 */\n\
0359         cmp.l    #33,%d2\n\
0360         bne      nopc_adjust\n\
0361         subq.l   #2,%a4\n\
0362 nopc_adjust:\n\
0363 /* #endif */\n\
0364         move.l   %a4,68(%a5)     /* save pc in _regisers[]          */\n\
0365 \n\
0366 #\n\
0367 # figure out how many bytes in the stack frame\n\
0368         andi.w   #0xf000,%d0    /* mask off format type         */\n\
0369         rol.w    #5,%d0         /* rotate into the low byte *2  */\n\
0370         lea     exceptionSize,%a1   \n\
0371         add.w    %d0,%a1        /* index into the table         */\n\
0372     move.w   (%a1),%d0      /* get number of words in frame */\n\
0373         move.w   %d0,%d3        /* save it                      */\n\
0374         sub.w    %d0,%a0    /* adjust save pointer          */\n\
0375         sub.w    %d0,%a0    /* adjust save pointer(bytes)   */\n\
0376     move.l   %a0,%a1        /* copy save pointer            */\n\
0377     subq.l   #1,%d0         /* predecrement loop counter    */\n\
0378 #\n\
0379 # copy the frame\n\
0380 saveFrameLoop:\n\
0381     move.w  (%sp)+,(%a1)+\n\
0382     dbf     %d0,saveFrameLoop\n\
0383 #\n\
0384 # now that the stack has been clenaed,\n\
0385 # save the a7 in use at time of exception\n\
0386         move.l   %sp,superStack  /* save supervisor sp           */\n\
0387         andi.w   #0x2000,%d1      /* were we in supervisor mode ? */\n\
0388         beq      userMode       \n\
0389         move.l   %a7,60(%a5)      /* save a7                  */\n\
0390         bra      a7saveDone\n\
0391 userMode:  \n\
0392     move.l   %usp,%a1       \n\
0393         move.l   %a1,60(%a5)      /* save user stack pointer    */\n\
0394 a7saveDone:\n\
0395 \n\
0396 #\n\
0397 # save size of frame\n\
0398         move.w   %d3,-(%a0)\n\
0399 \n\
0400         move.l   %d2,-(%a0)     /* save vector number           */\n\
0401 #\n\
0402 # save pc causing exception\n\
0403         move.l   %a4,-(%a0)\n\
0404 #\n\
0405 # save old frame link and set the new value\n\
0406     move.l  lastFrame,%a1   /* last frame pointer */\n\
0407     move.l   %a1,-(%a0)     /* save pointer to prev frame   */\n\
0408         move.l   %a0,lastFrame\n\
0409 \n\
0410         move.l   %d2,-(%sp)         /* push exception num           */\n\
0411     move.l   exceptionHook,%a0  /* get address of handler */\n\
0412         jbsr    (%a0)             /* and call it */\n\
0413         clr.l   (%sp)            /* replace exception num parm with frame ptr */\n\
0414         jbsr     _returnFromException   /* jbsr, but never returns */\n\
0415 ");
0416 #else /* mc68000 */
0417 /* This function is called when an exception occurs.  It translates the
0418  * return address found on the stack into an exception vector # which
0419  * is then handled by either handle_exception or a system handler.
0420  * _catchException provides a front end for both.
0421  *
0422  * stack on entry:                       stack on exit:
0423  *   Program counter MSWord              exception # MSWord
0424  *   Program counter LSWord              exception # MSWord
0425  *   Status Register
0426  *   Return Address  MSWord
0427  *   Return Address  LSWord
0428  */
0429 __asm__ ("\n\
0430 .text\n\
0431 .globl _catchException\n\
0432 _catchException:");
0433 DISABLE_INTERRUPTS();
0434 __asm__ ("\
0435         moveml %d0-%d7/%a0-%a6,registers  /* save registers               */ \n\
0436     movel   lastFrame,%a0   /* last frame pointer */ \n\
0437 ");
0438 SAVE_FP_REGS();
0439 __asm__ (" \n\
0440         lea     registers,%a5   /* get address of registers     */ \n\
0441         movel   (%sp)+,%d2         /* pop return address           */ \n\
0442     addl    #1530,%d2        /* convert return addr to  */ \n\
0443     divs    #6,%d2      /*  exception number        */ \n\
0444     extl    %d2    \n\
0445  \n\
0446         moveql  #3,%d3           /* assume a three word frame     */ \n\
0447  \n\
0448         cmpiw   #3,%d2           /* bus error or address error ? */ \n\
0449         bgt     normal          /* if >3 then normal error      */ \n\
0450         movel   (%sp)+,-(%a0)       /* copy error info to frame buff*/ \n\
0451         movel   (%sp)+,-(%a0)       /* these are never used         */ \n\
0452         moveql  #7,%d3           /* this is a 7 word frame       */ \n\
0453       \n\
0454 normal:    \n\
0455     movew   (%sp)+,%d1         /* pop status register          */ \n\
0456         movel   (%sp)+,%a4         /* pop program counter          */ \n\
0457         movew   %d1,66(%a5)      /* save sr         */   \n\
0458         movel   %a4,68(%a5)      /* save pc in _regisers[]          */ \n\
0459         movel   %a4,-(%a0)         /* copy pc to frame buffer      */ \n\
0460     movew   %d1,-(%a0)         /* copy sr to frame buffer      */ \n\
0461  \n\
0462         movel   %sp,superStack  /* save supervisor sp          */ \n\
0463  \n\
0464         andiw   #0x2000,%d1      /* were we in supervisor mode ? */ \n\
0465         beq     userMode        \n\
0466         movel   %a7,60(%a5)      /* save a7                  */ \n\
0467         bra     saveDone              \n\
0468 userMode: \n\
0469         movel   %usp,%a1        /* save user stack pointer  */ \n\
0470         movel   %a1,60(%a5)      /* save user stack pointer */ \n\
0471 saveDone: \n\
0472  \n\
0473         movew   %d3,-(%a0)         /* push frame size in words     */ \n\
0474         movel   %d2,-(%a0)         /* push vector number           */ \n\
0475         movel   %a4,-(%a0)         /* push exception pc            */ \n\
0476  \n\
0477 # \n\
0478 # save old frame link and set the new value \n\
0479     movel   _lastFrame,%a1  /* last frame pointer */ \n\
0480     movel   %a1,-(%a0)      /* save pointer to prev frame   */ \n\
0481         movel   %a0,lastFrame \n\
0482  \n\
0483         movel   %d2,-(%sp)      /* push exception num           */ \n\
0484     movel   exceptionHook,%a0  /* get address of handler */ \n\
0485         jbsr    (%a0)             /* and call it */ \n\
0486         clrl    (%sp)             /* replace exception num parm with frame ptr */ \n\
0487         jbsr     _returnFromException   /* jbsr, but never returns */ \n\
0488 ");
0489 #endif
0490 
0491 /*
0492  * remcomHandler is a front end for handle_exception.  It moves the
0493  * stack pointer into an area reserved for debugger use in case the
0494  * breakpoint happened in supervisor mode.
0495  */
0496 __asm__ ("remcomHandler:");
0497 __asm__ ("           add.l    #4,%sp");        /* pop off return address     */
0498 __asm__ ("           move.l   (%sp)+,%d0");      /* get the exception number   */
0499 __asm__ ("      move.l   stackPtr,%sp"); /* move to remcom stack area  */
0500 __asm__ ("      move.l   %d0,-(%sp)");  /* push exception onto stack  */
0501 __asm__ ("      jbsr    handle_exception");    /* this never returns */
0502 __asm__ ("           rts");                  /* return */
0503 } /* end of stub_dummy_asm_wrapper function */
0504 
0505 void _returnFromException( Frame *frame )
0506 {
0507     /* if no passed in frame, use the last one */
0508     if (! frame)
0509     {
0510         frame = lastFrame;
0511     frame->frameSize = 4;
0512         frame->format = 0;
0513         frame->fsaveHeader = -1; /* restore regs, but we dont have fsave info*/
0514     }
0515 
0516 #if !M68K_HAS_VBR
0517     /* a 68000 cannot use the internal info pushed onto a bus error
0518      * or address error frame when doing an RTE so don't put this info
0519      * onto the stack or the stack will creep every time this happens.
0520      */
0521     frame->frameSize=3;
0522 #endif
0523 
0524     /* throw away any frames in the list after this frame */
0525     lastFrame = frame;
0526 
0527     frame->sr = registers[(int) PS];
0528     frame->pc = registers[(int) PC];
0529 
0530     if (registers[(int) PS] & 0x2000)
0531     {
0532         /* return to supervisor mode... */
0533         return_to_super();
0534     }
0535     else
0536     { /* return to user mode */
0537         return_to_user();
0538     }
0539 }
0540 
0541 int hex(ch)
0542 char ch;
0543 {
0544   if ((ch >= 'a') && (ch <= 'f')) return (ch-'a'+10);
0545   if ((ch >= '0') && (ch <= '9')) return (ch-'0');
0546   if ((ch >= 'A') && (ch <= 'F')) return (ch-'A'+10);
0547   return (-1);
0548 }
0549 
0550 /* scan for the sequence $<data>#<checksum>     */
0551 void getpacket(buffer)
0552 char * buffer;
0553 {
0554   unsigned char checksum;
0555   unsigned char xmitcsum;
0556   int  i;
0557   int  count;
0558   char ch;
0559 
0560   do {
0561     /* wait around for the start character, ignore all other characters */
0562     while ((ch = getDebugChar()) != '$');
0563     checksum = 0;
0564     xmitcsum = -1;
0565 
0566     count = 0;
0567 
0568     /* now, read until a # or end of buffer is found */
0569     while (count < BUFMAX) {
0570       ch = getDebugChar();
0571       if (ch == '#') break;
0572       checksum = checksum + ch;
0573       buffer[count] = ch;
0574       count = count + 1;
0575       }
0576     buffer[count] = 0;
0577 
0578     if (ch == '#') {
0579       xmitcsum = hex(getDebugChar()) << 4;
0580       xmitcsum += hex(getDebugChar());
0581       if ((remote_debug ) && (checksum != xmitcsum)) {
0582         fprintf (stderr,"bad checksum.  My count = 0x%x, sent=0x%x. buf=%s\n",
0583                              checksum,xmitcsum,buffer);
0584       }
0585 
0586       if (checksum != xmitcsum) putDebugChar('-');  /* failed checksum */
0587       else {
0588      putDebugChar('+');  /* successful transfer */
0589      /* if a sequence char is present, reply the sequence ID */
0590      if (buffer[2] == ':') {
0591         putDebugChar( buffer[0] );
0592         putDebugChar( buffer[1] );
0593         /* remove sequence chars from buffer */
0594         count = strlen(buffer);
0595         for (i=3; i <= count; i++) buffer[i-3] = buffer[i];
0596      }
0597       }
0598     }
0599   } while (checksum != xmitcsum);
0600 
0601 }
0602 
0603 /* send the packet in buffer.  The host get's one chance to read it.
0604    This routine does not wait for a positive acknowledge.  */
0605 
0606 /*
0607  * Send the packet in buffer and wait for a positive acknowledgement.
0608  */
0609 static void
0610 putpacket (char *buffer)
0611 {
0612   int checksum;
0613 
0614   /* $<packet info>#<checksum> */
0615   do
0616     {
0617       char *src = buffer;
0618       putDebugChar ('$');
0619       checksum = 0;
0620 
0621       while (*src != '\0')
0622         {
0623           int runlen = 0;
0624 
0625           /* Do run length encoding */
0626           while ((src[runlen] == src[0]) && (runlen < 99))
0627             runlen++;
0628           if (runlen > 3)
0629             {
0630               int encode;
0631               /* Got a useful amount */
0632               putDebugChar (*src);
0633               checksum += *src;
0634               putDebugChar ('*');
0635               checksum += '*';
0636               checksum += (encode = (runlen - 4) + ' ');
0637               putDebugChar (encode);
0638               src += runlen;
0639             }
0640           else
0641             {
0642               putDebugChar (*src);
0643               checksum += *src;
0644               src++;
0645              }
0646         }
0647 
0648       putDebugChar ('#');
0649       putDebugChar (highhex (checksum));
0650       putDebugChar (lowhex (checksum));
0651     }
0652   while  (getDebugChar () != '+');
0653 }
0654 
0655 char  remcomInBuffer[BUFMAX];
0656 char  remcomOutBuffer[BUFMAX];
0657 static short error;
0658 
0659 void debug_error(
0660   char * format,
0661   char * parm
0662 )
0663 {
0664   if (remote_debug) fprintf (stderr,format,parm);
0665 }
0666 
0667 /* convert the memory pointed to by mem into hex, placing result in buf */
0668 /* return a pointer to the last char put in buf (null) */
0669 char* mem2hex(mem, buf, count)
0670 char* mem;
0671 char* buf;
0672 int   count;
0673 {
0674       int i;
0675       unsigned char ch;
0676       for (i=0;i<count;i++) {
0677           ch = *mem++;
0678           *buf++ = gdb_hexchars[ch >> 4];
0679           *buf++ = gdb_hexchars[ch % 16];
0680       }
0681       *buf = 0;
0682       return(buf);
0683 }
0684 
0685 /* convert the hex array pointed to by buf into binary to be placed in mem */
0686 /* return a pointer to the character AFTER the last byte written */
0687 char* hex2mem(buf, mem, count)
0688 char* buf;
0689 char* mem;
0690 int   count;
0691 {
0692       int i;
0693       unsigned char ch;
0694       for (i=0;i<count;i++) {
0695           ch = hex(*buf++) << 4;
0696           ch = ch + hex(*buf++);
0697           *mem++ = ch;
0698       }
0699       return(mem);
0700 }
0701 
0702 /* Convert the binary stream in BUF to memory.
0703 
0704    Gdb will escape $, #, and the escape char (0x7d).
0705    COUNT is the total number of bytes to write into
0706    memory. */
0707 static unsigned char *
0708 bin2mem (
0709   unsigned char *buf,
0710   unsigned char *mem,
0711   int   count
0712 )
0713 {
0714   int i;
0715 
0716   for (i = 0; i < count; i++) {
0717       /* Check for any escaped characters. Be paranoid and
0718          only unescape chars that should be escaped. */
0719       if (*buf == 0x7d) {
0720           switch (*(buf+1)) {
0721             case 0x3:  /* # */
0722             case 0x4:  /* $ */
0723             case 0x5d: /* escape char */
0724               buf++;
0725               *buf |= 0x20;
0726               break;
0727             default:
0728               /* nothing */
0729               break;
0730             }
0731         }
0732 
0733       *mem++ = *buf++;
0734     }
0735 
0736   return mem;
0737 }
0738 
0739 /* a bus error has occurred, perform a longjmp
0740    to return execution and allow handling of the error */
0741 
0742 void handle_buserror()
0743 {
0744   longjmp(remcomEnv,1);
0745 }
0746 
0747 /* this function takes the 68000 exception number and attempts to
0748    translate this number into a unix compatible signal value */
0749 int computeSignal( exceptionVector )
0750 int exceptionVector;
0751 {
0752   int sigval;
0753   switch (exceptionVector) {
0754     case 2 : sigval = 10; break; /* bus error           */
0755     case 3 : sigval = 10; break; /* address error       */
0756     case 4 : sigval = 4;  break; /* illegal instruction */
0757     case 5 : sigval = 8;  break; /* zero divide         */
0758     case 6 : sigval = 8; break; /* chk instruction     */
0759     case 7 : sigval = 8; break; /* trapv instruction   */
0760     case 8 : sigval = 11; break; /* privilege violation */
0761     case 9 : sigval = 5;  break; /* trace trap          */
0762     case 10: sigval = 4;  break; /* line 1010 emulator  */
0763     case 11: sigval = 4;  break; /* line 1111 emulator  */
0764 
0765       /* Coprocessor protocol violation.  Using a standard MMU or FPU
0766      this cannot be triggered by software.  Call it a SIGBUS.  */
0767     case 13: sigval = 10;  break;
0768 
0769     case 31: sigval = 2;  break; /* interrupt           */
0770     case 33: sigval = 5;  break; /* GDB breakpoint      */
0771     case 34: sigval = 5;  break; /* coded breakpoint    */
0772 
0773       /* This is a trap #8 instruction.  Apparently it is someone's software
0774      convention for some sort of SIGFPE condition.  Whose?  How many
0775      people are being screwed by having this code the way it is?
0776      Is there a clean solution?  */
0777     case 40: sigval = 8;  break; /* floating point err  */
0778 
0779     case 48: sigval = 8;  break; /* floating point err  */
0780     case 49: sigval = 8;  break; /* floating point err  */
0781     case 50: sigval = 8;  break; /* zero divide         */
0782     case 51: sigval = 8;  break; /* underflow           */
0783     case 52: sigval = 8;  break; /* operand error       */
0784     case 53: sigval = 8;  break; /* overflow            */
0785     case 54: sigval = 8;  break; /* NAN                 */
0786     default:
0787       sigval = 7;         /* "software generated"*/
0788   }
0789   return (sigval);
0790 }
0791 
0792 /**********************************************/
0793 /* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */
0794 /* RETURN NUMBER OF CHARS PROCESSED           */
0795 /**********************************************/
0796 int hexToInt(char **ptr, int *intValue)
0797 {
0798     int numChars = 0;
0799     int hexValue;
0800 
0801     *intValue = 0;
0802 
0803     while (**ptr)
0804     {
0805         hexValue = hex(**ptr);
0806         if (hexValue >=0)
0807         {
0808             *intValue = (*intValue <<4) | hexValue;
0809             numChars ++;
0810         }
0811         else
0812             break;
0813 
0814         (*ptr)++;
0815     }
0816 
0817     return (numChars);
0818 }
0819 
0820 /*
0821  *  This support function prepares and sends the message containing the
0822  *  basic information about this exception.
0823  */
0824 
0825 void gdb_stub_report_exception_info(
0826   int vector,
0827   int *regs,
0828   int thread
0829 )
0830 {
0831    char *optr;
0832    int sigval;
0833 
0834    optr = remcomOutBuffer;
0835    *optr++ = 'T';
0836    sigval = computeSignal (vector);
0837    *optr++ = highhex (sigval);
0838    *optr++ = lowhex (sigval);
0839 
0840    *optr++ = highhex(A7);
0841    *optr++ = lowhex(A7);
0842    *optr++ = ':';
0843    optr    = mem2hstr(optr,
0844               (unsigned char *)&(regs[A7]),
0845               sizeof(regs[A7]));
0846    *optr++ = ';';
0847 
0848    *optr++ = highhex(PC);
0849    *optr++ = lowhex(PC);
0850    *optr++ = ':';
0851    optr    = mem2hstr(optr,
0852               (unsigned char *)&(regs[PC]),
0853               sizeof(regs[PC]) );
0854    *optr++ = ';';
0855 
0856 #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
0857    if (do_threads)
0858    {
0859       *optr++ = 't';
0860       *optr++ = 'h';
0861       *optr++ = 'r';
0862       *optr++ = 'e';
0863       *optr++ = 'a';
0864       *optr++ = 'd';
0865       *optr++ = ':';
0866       optr   = thread2vhstr(optr, thread);
0867       *optr++ = ';';
0868    }
0869 #endif
0870    *optr++ = '\0';
0871 }
0872 
0873 /*
0874  * This function does all command procesing for interfacing to gdb.
0875  */
0876 void handle_exception(int exceptionVector)
0877 {
0878   int    host_has_detached = 0;
0879   int    addr, length;
0880   char * ptr;
0881   int    newPC;
0882   Frame  *frame;
0883   int          current_thread;  /* current generic thread */
0884   int          thread;          /* stopped thread: context exception happened in */
0885    void         *regptr;
0886    int binary;
0887 
0888   if (remote_debug) printf("vector=%d, sr=0x%x, pc=0x%x\n",
0889                 exceptionVector,
0890                 registers[ PS ],
0891                 registers[ PC ]);
0892 
0893    thread = 0;
0894 #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
0895    if (do_threads) {
0896       thread = rtems_gdb_stub_get_current_thread();
0897    }
0898 #endif
0899    current_thread = thread;
0900 
0901 #if 0
0902   /* reply to host that an exception has occurred */
0903   sigval = computeSignal( exceptionVector );
0904   remcomOutBuffer[0] = 'S';
0905   remcomOutBuffer[1] =  gdb_hexchars[sigval >> 4];
0906   remcomOutBuffer[2] =  gdb_hexchars[sigval % 16];
0907   remcomOutBuffer[3] = 0;
0908 #else
0909    /* reply to host that an exception has occurred with some basic info */
0910    gdb_stub_report_exception_info(exceptionVector, registers, thread);
0911 #endif
0912 
0913   putpacket(remcomOutBuffer);
0914 
0915   while (!(host_has_detached)) {
0916     binary = 0;
0917     error = 0;
0918     remcomOutBuffer[0] = 0;
0919     getpacket(remcomInBuffer);
0920     switch (remcomInBuffer[0]) {
0921 #if 0
0922       case '?' :   remcomOutBuffer[0] = 'S';
0923                    remcomOutBuffer[1] =  gdb_hexchars[sigval >> 4];
0924                    remcomOutBuffer[2] =  gdb_hexchars[sigval % 16];
0925                    remcomOutBuffer[3] = 0;
0926                  break;
0927 #else
0928       case '?' : gdb_stub_report_exception_info(exceptionVector,
0929                         registers,
0930                         thread);
0931              break;
0932 #endif
0933       case 'd' : remote_debug = !(remote_debug);  /* toggle debug flag */
0934                  break;
0935       case 'D' : /* host has detached */
0936              strcpy(remcomOutBuffer,"OK");
0937              host_has_detached = 1;
0938                  break;
0939          case 'g':               /* return the values of the CPU registers */
0940             regptr = registers;
0941 #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
0942             if (do_threads && current_thread != thread )
0943                regptr = &current_thread_registers;
0944 #endif
0945             mem2hex (regptr, remcomOutBuffer, sizeof registers);
0946             break;
0947 
0948          case 'G':       /* set the values of the CPU registers - return OK */
0949        regptr = registers;
0950 #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
0951        if (do_threads && current_thread != thread )
0952          regptr = &current_thread_registers;
0953 #endif
0954        if (hex2mem (&remcomInBuffer[1],
0955             regptr,
0956             sizeof registers)) {
0957          strcpy (remcomOutBuffer, "OK");
0958        }
0959        else {
0960          strcpy (remcomOutBuffer, "E00"); /* E00 = bad "set register" command */
0961        }
0962        break;
0963 
0964       /* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
0965       case 'm' :
0966             if (setjmp(remcomEnv) == 0)
0967                 {
0968                     m68k_exceptionHandler(2,handle_buserror);
0969 
0970             /* TRY TO READ %x,%x.  IF SUCCEED, SET PTR = 0 */
0971                     ptr = &remcomInBuffer[1];
0972                     if (hexToInt(&ptr,&addr))
0973                         if (*(ptr++) == ',')
0974                             if (hexToInt(&ptr,&length))
0975                             {
0976                                 ptr = 0;
0977                                 mem2hex((char*) addr, remcomOutBuffer, length);
0978                             }
0979 
0980                     if (ptr)
0981                     {
0982               strcpy(remcomOutBuffer,"E01");
0983               debug_error("malformed read memory command: %s",remcomInBuffer);
0984                   }
0985                 }
0986         else {
0987           m68k_exceptionHandler(2,_catchException);
0988           strcpy(remcomOutBuffer,"E03");
0989           debug_error("bus error", 0);
0990           }
0991 
0992         /* restore handler for bus error */
0993         m68k_exceptionHandler(2,_catchException);
0994         break;
0995 
0996       /* XAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
0997       /* Transfer is in binary, '$', '#' and 0x7d is escaped with 0x7d */
0998       case 'X' :
0999                 binary = 1;
1000       /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
1001       case 'M' :
1002             if (setjmp(remcomEnv) == 0) {
1003             m68k_exceptionHandler(2,handle_buserror);
1004 
1005             /* TRY TO READ '%x,%x:'.  IF SUCCEED, SET PTR = 0 */
1006                     ptr = &remcomInBuffer[1];
1007                     if (hexToInt(&ptr,&addr))
1008                         if (*(ptr++) == ',')
1009                             if (hexToInt(&ptr,&length))
1010                                 if (*(ptr++) == ':')
1011                                 {
1012                   if (binary) {
1013                     bin2mem(ptr,(char *)addr,length);
1014                   }
1015                   else {
1016                                     hex2mem(ptr, (char*) addr, length);
1017                   }
1018                   ptr = 0;
1019                   strcpy(remcomOutBuffer,"OK");
1020                                 }
1021                     if (ptr)
1022                     {
1023               strcpy(remcomOutBuffer,"E02");
1024               debug_error("malformed write memory command: %s",remcomInBuffer);
1025               }
1026                 }
1027         else {
1028           m68k_exceptionHandler(2,_catchException);
1029           strcpy(remcomOutBuffer,"E03");
1030           debug_error("bus error", 0);
1031           }
1032 
1033                 /* restore handler for bus error */
1034                 m68k_exceptionHandler(2,_catchException);
1035                 break;
1036 
1037      /* cAA..AA    Continue at address AA..AA(optional) */
1038      /* sAA..AA   Step one instruction from AA..AA(optional) */
1039      case 'c' :
1040      case 's' :
1041           /* try to read optional parameter, pc unchanged if no parm */
1042          ptr = &remcomInBuffer[1];
1043          if (hexToInt(&ptr,&addr))
1044              registers[ PC ] = addr;
1045 
1046           newPC = registers[ PC];
1047 
1048           /* clear the trace bit */
1049           registers[ PS ] &= 0x7fff;
1050 
1051           /* set the trace bit if we're stepping */
1052           if (remcomInBuffer[0] == 's') registers[ PS ] |= 0x8000;
1053 
1054           /*
1055            * look for newPC in the linked list of exception frames.
1056            * if it is found, use the old frame it.  otherwise,
1057            * fake up a dummy frame in returnFromException().
1058            */
1059           if (remote_debug) printf("new pc = 0x%x\n",newPC);
1060           frame = lastFrame;
1061           while (frame)
1062           {
1063               if (remote_debug)
1064                   printf("frame at 0x%x has pc=0x%x, except#=%d\n",
1065                          (uint32_t) frame,
1066              (uint32_t) frame->exceptionPC,
1067                          (uint32_t) frame->exceptionVector);
1068               if (frame->exceptionPC == newPC) break;  /* bingo! a match */
1069               /*
1070                * for a breakpoint instruction, the saved pc may
1071                * be off by two due to re-executing the instruction
1072                * replaced by the trap instruction.  Check for this.
1073                */
1074               if ((frame->exceptionVector == 33) &&
1075                   (frame->exceptionPC == (newPC+2))) break;
1076               if (frame == frame->previous)
1077           {
1078               frame = 0; /* no match found */
1079               break;
1080           }
1081           frame = frame->previous;
1082           }
1083 
1084           /*
1085            * If we found a match for the PC AND we are not returning
1086            * as a result of a breakpoint (33),
1087            * trace exception (9), nmi (31), jmp to
1088            * the old exception handler as if this code never ran.
1089            */
1090           if (frame)
1091           {
1092               if ((frame->exceptionVector != 9)  &&
1093                   (frame->exceptionVector != 31) &&
1094                   (frame->exceptionVector != 33))
1095               {
1096                   /*
1097                    * invoke the previous handler.
1098                    */
1099                   if (oldExceptionHook)
1100                       (*oldExceptionHook) (frame->exceptionVector);
1101                   newPC = registers[ PC ];    /* pc may have changed  */
1102                   if (newPC != frame->exceptionPC)
1103                   {
1104                       if (remote_debug)
1105                           printf("frame at 0x%x has pc=0x%x, except#=%d\n",
1106                                  (uint32_t) frame,
1107                  frame->exceptionPC,
1108                                  frame->exceptionVector);
1109                       /* re-use the last frame, we're skipping it (longjump?)*/
1110               frame = (Frame *) 0;
1111                   _returnFromException( frame );  /* this is a jump */
1112                   }
1113               }
1114           }
1115 
1116           /* if we couldn't find a frame, create one */
1117           if (frame == 0)
1118       {
1119               frame = lastFrame -1 ;
1120 
1121           /* by using a bunch of print commands with breakpoints,
1122                  it's possible for the frame stack to creep down.  If it creeps
1123          too far, give up and reset it to the top.  Normal use should
1124                  not see this happen.
1125               */
1126           if ((unsigned int) (frame-2) < (unsigned int) &gdbFrameStack)
1127               {
1128                  initializeRemcomErrorFrame();
1129                  frame = lastFrame;
1130           }
1131               frame->previous = lastFrame;
1132               lastFrame = frame;
1133               frame = 0;  /* null so _return... will properly initialize it */
1134       }
1135 
1136       _returnFromException( frame ); /* this is a jump */
1137 
1138           break;
1139 
1140          case 'q':   /* queries */
1141 #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
1142             rtems_gdb_process_query( remcomInBuffer,
1143                      remcomOutBuffer,
1144                      do_threads,
1145                      thread );
1146 #endif
1147             break;
1148 
1149 #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
1150          case 'T':
1151          {
1152             int testThread;
1153 
1154             if( vhstr2thread(&remcomInBuffer[1], &testThread) == NULL )
1155             {
1156                strcpy(remcomOutBuffer, "E01");
1157                break;
1158             }
1159 
1160             if( rtems_gdb_index_to_stub_id(testThread) == NULL )
1161             {
1162                strcpy(remcomOutBuffer, "E02");
1163             }
1164             else
1165             {
1166                strcpy(remcomOutBuffer, "OK");
1167             }
1168          }
1169          break;
1170 #endif
1171 
1172          case 'H':  /* set new thread */
1173 #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
1174             if (remcomInBuffer[1] != 'g') {
1175                break;
1176             }
1177 
1178             if (!do_threads) {
1179                break;
1180             }
1181 
1182             {
1183                int tmp, ret;
1184 
1185                /* Set new generic thread */
1186                if (vhstr2thread(&remcomInBuffer[2], &tmp) == NULL) {
1187                   strcpy(remcomOutBuffer, "E01");
1188                   break;
1189                }
1190 
1191                /* 0 means `thread' */
1192                if (tmp == 0) {
1193                   tmp = thread;
1194                }
1195 
1196                if (tmp == current_thread) {
1197                   /* No changes */
1198                   strcpy(remcomOutBuffer, "OK");
1199                   break;
1200                }
1201 
1202                /* Save current thread registers if necessary */
1203                if (current_thread != thread) {
1204                   ret = rtems_gdb_stub_set_thread_regs(
1205                      current_thread, (unsigned int *) &current_thread_registers);
1206                }
1207 
1208                /* Read new registers if necessary */
1209                if (tmp != thread) {
1210                   ret = rtems_gdb_stub_get_thread_regs(
1211                      tmp, (unsigned int *) &current_thread_registers);
1212 
1213                   if (!ret) {
1214                      /* Thread does not exist */
1215                      strcpy(remcomOutBuffer, "E02");
1216                      break;
1217                   }
1218                }
1219 
1220                current_thread = tmp;
1221                strcpy(remcomOutBuffer, "OK");
1222             }
1223 #endif
1224             break;
1225 
1226       /* kill the program */
1227       case 'k' :  /* do nothing */
1228                 break;
1229       } /* switch */
1230 
1231     /* reply to the request */
1232     putpacket(remcomOutBuffer);
1233     }
1234 }
1235 
1236 void
1237 initializeRemcomErrorFrame()
1238 {
1239     lastFrame = ((Frame *) &gdbFrameStack[FRAMESIZE-1]) - 1;
1240     lastFrame->previous = lastFrame;
1241 }
1242 
1243 /* this function is used to set up exception handlers for tracing and
1244    breakpoints */
1245 void set_debug_traps()
1246 {
1247   extern void _debug_level7();
1248   extern void remcomHandler();
1249   int exception;
1250 
1251   initializeRemcomErrorFrame();
1252   stackPtr  = &remcomStack[STACKSIZE/sizeof(int) - 1];
1253 
1254   for (exception = 2; exception <= 23; exception++)
1255       m68k_exceptionHandler(exception,_catchException);
1256 
1257   /* level 7 interrupt              */
1258   m68k_exceptionHandler(31,_debug_level7);
1259 
1260   /* GDB breakpoint exception (trap #1) */
1261   m68k_exceptionHandler(33,_catchException);
1262 
1263   /* coded breakpoint exception (trap #2) */
1264   m68k_exceptionHandler(34,_catchException);
1265 
1266   /* This is a trap #8 instruction.  Apparently it is someone's software
1267      convention for some sort of SIGFPE condition.  Whose?  How many
1268      people are being screwed by having this code the way it is?
1269      Is there a clean solution?  */
1270   m68k_exceptionHandler(40,_catchException);
1271 
1272   /* 48 to 54 are floating point coprocessor errors */
1273   for (exception = 48; exception <= 54; exception++)
1274       m68k_exceptionHandler(exception,_catchException);
1275 
1276   if (oldExceptionHook != remcomHandler)
1277   {
1278       oldExceptionHook = exceptionHook;
1279       exceptionHook    = remcomHandler;
1280   }
1281 
1282   initialized = true;
1283 
1284 }
1285 
1286 /* This function will generate a breakpoint exception.  It is used at the
1287    beginning of a program to sync up with a debugger and can be used
1288    otherwise as a quick means to stop program execution and "break" into
1289    the debugger. */
1290 
1291 void breakpoint()
1292 {
1293   if (initialized) BREAKPOINT();
1294 }