2004-02-14 Elena Zannoni <ezannoni@redhat.com>
[platform/upstream/binutils.git] / gdb / sparc-stub.c
1 /****************************************************************************
2
3                 THIS SOFTWARE IS NOT COPYRIGHTED
4
5    HP offers the following for use in the public domain.  HP makes no
6    warranty with regard to the software or it's performance and the
7    user accepts the software "AS IS" with all faults.
8
9    HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
10    TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
11    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
12
13 ****************************************************************************/
14
15 /****************************************************************************
16  *  Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
17  *
18  *  Module name: remcom.c $
19  *  Revision: 1.34 $
20  *  Date: 91/03/09 12:29:49 $
21  *  Contributor:     Lake Stevens Instrument Division$
22  *
23  *  Description:     low level support for gdb debugger. $
24  *
25  *  Considerations:  only works on target hardware $
26  *
27  *  Written by:      Glenn Engel $
28  *  ModuleState:     Experimental $
29  *
30  *  NOTES:           See Below $
31  *
32  *  Modified for SPARC by Stu Grossman, Cygnus Support.
33  *
34  *  This code has been extensively tested on the Fujitsu SPARClite demo board.
35  *
36  *  To enable debugger support, two things need to happen.  One, a
37  *  call to set_debug_traps() is necessary in order to allow any breakpoints
38  *  or error conditions to be properly intercepted and reported to gdb.
39  *  Two, a breakpoint needs to be generated to begin communication.  This
40  *  is most easily accomplished by a call to breakpoint().  Breakpoint()
41  *  simulates a breakpoint by executing a trap #1.
42  *
43  *************
44  *
45  *    The following gdb commands are supported:
46  *
47  * command          function                               Return value
48  *
49  *    g             return the value of the CPU registers  hex data or ENN
50  *    G             set the value of the CPU registers     OK or ENN
51  *
52  *    mAA..AA,LLLL  Read LLLL bytes at address AA..AA      hex data or ENN
53  *    MAA..AA,LLLL: Write LLLL bytes at address AA.AA      OK or ENN
54  *
55  *    c             Resume at current address              SNN   ( signal NN)
56  *    cAA..AA       Continue at address AA..AA             SNN
57  *
58  *    s             Step one instruction                   SNN
59  *    sAA..AA       Step one instruction from AA..AA       SNN
60  *
61  *    k             kill
62  *
63  *    ?             What was the last sigval ?             SNN   (signal NN)
64  *
65  * All commands and responses are sent with a packet which includes a
66  * checksum.  A packet consists of
67  *
68  * $<packet info>#<checksum>.
69  *
70  * where
71  * <packet info> :: <characters representing the command or response>
72  * <checksum>    :: < two hex digits computed as modulo 256 sum of <packetinfo>>
73  *
74  * When a packet is received, it is first acknowledged with either '+' or '-'.
75  * '+' indicates a successful transfer.  '-' indicates a failed transfer.
76  *
77  * Example:
78  *
79  * Host:                  Reply:
80  * $m0,10#2a               +$00010203040506070809101112131415#42
81  *
82  ****************************************************************************/
83
84 #include <string.h>
85 #include <signal.h>
86
87 /************************************************************************
88  *
89  * external low-level support routines
90  */
91
92 extern void putDebugChar();     /* write a single character      */
93 extern int getDebugChar();      /* read and return a single char */
94
95 /************************************************************************/
96 /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
97 /* at least NUMREGBYTES*2 are needed for register packets */
98 #define BUFMAX 2048
99
100 static int initialized = 0;     /* !0 means we've been initialized */
101
102 static void set_mem_fault_trap();
103
104 static const char hexchars[]="0123456789abcdef";
105
106 #define NUMREGS 72
107
108 /* Number of bytes of registers.  */
109 #define NUMREGBYTES (NUMREGS * 4)
110 enum regnames {G0, G1, G2, G3, G4, G5, G6, G7,
111                  O0, O1, O2, O3, O4, O5, SP, O7,
112                  L0, L1, L2, L3, L4, L5, L6, L7,
113                  I0, I1, I2, I3, I4, I5, FP, I7,
114
115                  F0, F1, F2, F3, F4, F5, F6, F7,
116                  F8, F9, F10, F11, F12, F13, F14, F15,
117                  F16, F17, F18, F19, F20, F21, F22, F23,
118                  F24, F25, F26, F27, F28, F29, F30, F31,
119                  Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR };
120
121 /***************************  ASSEMBLY CODE MACROS *************************/
122 /*                                                                         */
123
124 extern void trap_low();
125
126 asm("
127         .reserve trapstack, 1000 * 4, \"bss\", 8
128
129         .data
130         .align  4
131
132 in_trap_handler:
133         .word   0
134
135         .text
136         .align 4
137
138 ! This function is called when any SPARC trap (except window overflow or
139 ! underflow) occurs.  It makes sure that the invalid register window is still
140 ! available before jumping into C code.  It will also restore the world if you
141 ! return from handle_exception.
142
143         .globl _trap_low
144 _trap_low:
145         mov     %psr, %l0
146         mov     %wim, %l3
147
148         srl     %l3, %l0, %l4           ! wim >> cwp
149         cmp     %l4, 1
150         bne     window_fine             ! Branch if not in the invalid window
151         nop
152
153 ! Handle window overflow
154
155         mov     %g1, %l4                ! Save g1, we use it to hold the wim
156         srl     %l3, 1, %g1             ! Rotate wim right
157         tst     %g1
158         bg      good_wim                ! Branch if new wim is non-zero
159         nop
160
161 ! At this point, we need to bring a 1 into the high order bit of the wim.
162 ! Since we don't want to make any assumptions about the number of register
163 ! windows, we figure it out dynamically so as to setup the wim correctly.
164
165         not     %g1                     ! Fill g1 with ones
166         mov     %g1, %wim               ! Fill the wim with ones
167         nop
168         nop
169         nop
170         mov     %wim, %g1               ! Read back the wim
171         inc     %g1                     ! Now g1 has 1 just to left of wim
172         srl     %g1, 1, %g1             ! Now put 1 at top of wim
173         mov     %g0, %wim               ! Clear wim so that subsequent save
174         nop                             !  won't trap
175         nop
176         nop
177
178 good_wim:
179         save    %g0, %g0, %g0           ! Slip into next window
180         mov     %g1, %wim               ! Install the new wim
181
182         std     %l0, [%sp + 0 * 4]      ! save L & I registers
183         std     %l2, [%sp + 2 * 4]
184         std     %l4, [%sp + 4 * 4]
185         std     %l6, [%sp + 6 * 4]
186
187         std     %i0, [%sp + 8 * 4]
188         std     %i2, [%sp + 10 * 4]
189         std     %i4, [%sp + 12 * 4]
190         std     %i6, [%sp + 14 * 4]
191
192         restore                         ! Go back to trap window.
193         mov     %l4, %g1                ! Restore %g1
194
195 window_fine:
196         sethi   %hi(in_trap_handler), %l4
197         ld      [%lo(in_trap_handler) + %l4], %l5
198         tst     %l5
199         bg      recursive_trap
200         inc     %l5
201
202         set     trapstack+1000*4, %sp   ! Switch to trap stack
203
204 recursive_trap:
205         st      %l5, [%lo(in_trap_handler) + %l4]
206         sub     %sp,(16+1+6+1+72)*4,%sp ! Make room for input & locals
207                                         ! + hidden arg + arg spill
208                                         ! + doubleword alignment
209                                         ! + registers[72] local var
210
211         std     %g0, [%sp + (24 + 0) * 4] ! registers[Gx]
212         std     %g2, [%sp + (24 + 2) * 4]
213         std     %g4, [%sp + (24 + 4) * 4]
214         std     %g6, [%sp + (24 + 6) * 4]
215
216         std     %i0, [%sp + (24 + 8) * 4] ! registers[Ox]
217         std     %i2, [%sp + (24 + 10) * 4]
218         std     %i4, [%sp + (24 + 12) * 4]
219         std     %i6, [%sp + (24 + 14) * 4]
220                                         ! F0->F31 not implemented
221         mov     %y, %l4
222         mov     %tbr, %l5
223         st      %l4, [%sp + (24 + 64) * 4] ! Y
224         st      %l0, [%sp + (24 + 65) * 4] ! PSR
225         st      %l3, [%sp + (24 + 66) * 4] ! WIM
226         st      %l5, [%sp + (24 + 67) * 4] ! TBR
227         st      %l1, [%sp + (24 + 68) * 4] ! PC
228         st      %l2, [%sp + (24 + 69) * 4] ! NPC
229
230                                         ! CPSR and FPSR not impl
231
232         or      %l0, 0xf20, %l4
233         mov     %l4, %psr               ! Turn on traps, disable interrupts
234
235         call    _handle_exception
236         add     %sp, 24 * 4, %o0        ! Pass address of registers
237
238 ! Reload all of the registers that aren't on the stack
239
240         ld      [%sp + (24 + 1) * 4], %g1 ! registers[Gx]
241         ldd     [%sp + (24 + 2) * 4], %g2
242         ldd     [%sp + (24 + 4) * 4], %g4
243         ldd     [%sp + (24 + 6) * 4], %g6
244
245         ldd     [%sp + (24 + 8) * 4], %i0 ! registers[Ox]
246         ldd     [%sp + (24 + 10) * 4], %i2
247         ldd     [%sp + (24 + 12) * 4], %i4
248         ldd     [%sp + (24 + 14) * 4], %i6
249
250         ldd     [%sp + (24 + 64) * 4], %l0 ! Y & PSR
251         ldd     [%sp + (24 + 68) * 4], %l2 ! PC & NPC
252
253         restore                         ! Ensure that previous window is valid
254         save    %g0, %g0, %g0           !  by causing a window_underflow trap
255
256         mov     %l0, %y
257         mov     %l1, %psr               ! Make sure that traps are disabled
258                                         ! for rett
259
260         sethi   %hi(in_trap_handler), %l4
261         ld      [%lo(in_trap_handler) + %l4], %l5
262         dec     %l5
263         st      %l5, [%lo(in_trap_handler) + %l4]
264
265         jmpl    %l2, %g0                ! Restore old PC
266         rett    %l3                     ! Restore old nPC
267 ");
268
269 /* Convert ch from a hex digit to an int */
270
271 static int
272 hex (unsigned char ch)
273 {
274   if (ch >= 'a' && ch <= 'f')
275     return ch-'a'+10;
276   if (ch >= '0' && ch <= '9')
277     return ch-'0';
278   if (ch >= 'A' && ch <= 'F')
279     return ch-'A'+10;
280   return -1;
281 }
282
283 static char remcomInBuffer[BUFMAX];
284 static char remcomOutBuffer[BUFMAX];
285
286 /* scan for the sequence $<data>#<checksum>     */
287
288 unsigned char *
289 getpacket (void)
290 {
291   unsigned char *buffer = &remcomInBuffer[0];
292   unsigned char checksum;
293   unsigned char xmitcsum;
294   int count;
295   char ch;
296
297   while (1)
298     {
299       /* wait around for the start character, ignore all other characters */
300       while ((ch = getDebugChar ()) != '$')
301         ;
302
303 retry:
304       checksum = 0;
305       xmitcsum = -1;
306       count = 0;
307
308       /* now, read until a # or end of buffer is found */
309       while (count < BUFMAX)
310         {
311           ch = getDebugChar ();
312           if (ch == '$')
313             goto retry;
314           if (ch == '#')
315             break;
316           checksum = checksum + ch;
317           buffer[count] = ch;
318           count = count + 1;
319         }
320       buffer[count] = 0;
321
322       if (ch == '#')
323         {
324           ch = getDebugChar ();
325           xmitcsum = hex (ch) << 4;
326           ch = getDebugChar ();
327           xmitcsum += hex (ch);
328
329           if (checksum != xmitcsum)
330             {
331               putDebugChar ('-');       /* failed checksum */
332             }
333           else
334             {
335               putDebugChar ('+');       /* successful transfer */
336
337               /* if a sequence char is present, reply the sequence ID */
338               if (buffer[2] == ':')
339                 {
340                   putDebugChar (buffer[0]);
341                   putDebugChar (buffer[1]);
342
343                   return &buffer[3];
344                 }
345
346               return &buffer[0];
347             }
348         }
349     }
350 }
351
352 /* send the packet in buffer.  */
353
354 static void
355 putpacket (unsigned char *buffer)
356 {
357   unsigned char checksum;
358   int count;
359   unsigned char ch;
360
361   /*  $<packet info>#<checksum>. */
362   do
363     {
364       putDebugChar('$');
365       checksum = 0;
366       count = 0;
367
368       while (ch = buffer[count])
369         {
370           putDebugChar(ch);
371           checksum += ch;
372           count += 1;
373         }
374
375       putDebugChar('#');
376       putDebugChar(hexchars[checksum >> 4]);
377       putDebugChar(hexchars[checksum & 0xf]);
378
379     }
380   while (getDebugChar() != '+');
381 }
382
383 /* Indicate to caller of mem2hex or hex2mem that there has been an
384    error.  */
385 static volatile int mem_err = 0;
386
387 /* Convert the memory pointed to by mem into hex, placing result in buf.
388  * Return a pointer to the last char put in buf (null), in case of mem fault,
389  * return 0.
390  * If MAY_FAULT is non-zero, then we will handle memory faults by returning
391  * a 0, else treat a fault like any other fault in the stub.
392  */
393
394 static unsigned char *
395 mem2hex (unsigned char *mem, unsigned char *buf, int count, int may_fault)
396 {
397   unsigned char ch;
398
399   set_mem_fault_trap(may_fault);
400
401   while (count-- > 0)
402     {
403       ch = *mem++;
404       if (mem_err)
405         return 0;
406       *buf++ = hexchars[ch >> 4];
407       *buf++ = hexchars[ch & 0xf];
408     }
409
410   *buf = 0;
411
412   set_mem_fault_trap(0);
413
414   return buf;
415 }
416
417 /* convert the hex array pointed to by buf into binary to be placed in mem
418  * return a pointer to the character AFTER the last byte written */
419
420 static char *
421 hex2mem (unsigned char *buf, unsigned char *mem, int count, int may_fault)
422 {
423   int i;
424   unsigned char ch;
425
426   set_mem_fault_trap(may_fault);
427
428   for (i=0; i<count; i++)
429     {
430       ch = hex(*buf++) << 4;
431       ch |= hex(*buf++);
432       *mem++ = ch;
433       if (mem_err)
434         return 0;
435     }
436
437   set_mem_fault_trap(0);
438
439   return mem;
440 }
441
442 /* This table contains the mapping between SPARC hardware trap types, and
443    signals, which are primarily what GDB understands.  It also indicates
444    which hardware traps we need to commandeer when initializing the stub. */
445
446 static struct hard_trap_info
447 {
448   unsigned char tt;             /* Trap type code for SPARClite */
449   unsigned char signo;          /* Signal that we map this trap into */
450 } hard_trap_info[] = {
451   {1, SIGSEGV},                 /* instruction access error */
452   {2, SIGILL},                  /* privileged instruction */
453   {3, SIGILL},                  /* illegal instruction */
454   {4, SIGEMT},                  /* fp disabled */
455   {36, SIGEMT},                 /* cp disabled */
456   {7, SIGBUS},                  /* mem address not aligned */
457   {9, SIGSEGV},                 /* data access exception */
458   {10, SIGEMT},                 /* tag overflow */
459   {128+1, SIGTRAP},             /* ta 1 - normal breakpoint instruction */
460   {0, 0}                        /* Must be last */
461 };
462
463 /* Set up exception handlers for tracing and breakpoints */
464
465 void
466 set_debug_traps (void)
467 {
468   struct hard_trap_info *ht;
469
470   for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
471     exceptionHandler(ht->tt, trap_low);
472
473   initialized = 1;
474 }
475
476 asm ("
477 ! Trap handler for memory errors.  This just sets mem_err to be non-zero.  It
478 ! assumes that %l1 is non-zero.  This should be safe, as it is doubtful that
479 ! 0 would ever contain code that could mem fault.  This routine will skip
480 ! past the faulting instruction after setting mem_err.
481
482         .text
483         .align 4
484
485 _fltr_set_mem_err:
486         sethi %hi(_mem_err), %l0
487         st %l1, [%l0 + %lo(_mem_err)]
488         jmpl %l2, %g0
489         rett %l2+4
490 ");
491
492 static void
493 set_mem_fault_trap (int enable)
494 {
495   extern void fltr_set_mem_err();
496   mem_err = 0;
497
498   if (enable)
499     exceptionHandler(9, fltr_set_mem_err);
500   else
501     exceptionHandler(9, trap_low);
502 }
503
504 /* Convert the SPARC hardware trap type code to a unix signal number. */
505
506 static int
507 computeSignal (int tt)
508 {
509   struct hard_trap_info *ht;
510
511   for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
512     if (ht->tt == tt)
513       return ht->signo;
514
515   return SIGHUP;                /* default for things we don't know about */
516 }
517
518 /*
519  * While we find nice hex chars, build an int.
520  * Return number of chars processed.
521  */
522
523 static int
524 hexToInt(char **ptr, int *intValue)
525 {
526   int numChars = 0;
527   int hexValue;
528
529   *intValue = 0;
530
531   while (**ptr)
532     {
533       hexValue = hex(**ptr);
534       if (hexValue < 0)
535         break;
536
537       *intValue = (*intValue << 4) | hexValue;
538       numChars ++;
539
540       (*ptr)++;
541     }
542
543   return (numChars);
544 }
545
546 /*
547  * This function does all command procesing for interfacing to gdb.  It
548  * returns 1 if you should skip the instruction at the trap address, 0
549  * otherwise.
550  */
551
552 extern void breakinst();
553
554 static void
555 handle_exception (unsigned long *registers)
556 {
557   int tt;                       /* Trap type */
558   int sigval;
559   int addr;
560   int length;
561   char *ptr;
562   unsigned long *sp;
563
564 /* First, we must force all of the windows to be spilled out */
565
566   asm(" save %sp, -64, %sp
567         save %sp, -64, %sp
568         save %sp, -64, %sp
569         save %sp, -64, %sp
570         save %sp, -64, %sp
571         save %sp, -64, %sp
572         save %sp, -64, %sp
573         save %sp, -64, %sp
574         restore
575         restore
576         restore
577         restore
578         restore
579         restore
580         restore
581         restore
582 ");
583
584   if (registers[PC] == (unsigned long)breakinst)
585     {
586       registers[PC] = registers[NPC];
587       registers[NPC] += 4;
588     }
589
590   sp = (unsigned long *)registers[SP];
591
592   tt = (registers[TBR] >> 4) & 0xff;
593
594   /* reply to host that an exception has occurred */
595   sigval = computeSignal(tt);
596   ptr = remcomOutBuffer;
597
598   *ptr++ = 'T';
599   *ptr++ = hexchars[sigval >> 4];
600   *ptr++ = hexchars[sigval & 0xf];
601
602   *ptr++ = hexchars[PC >> 4];
603   *ptr++ = hexchars[PC & 0xf];
604   *ptr++ = ':';
605   ptr = mem2hex((char *)&registers[PC], ptr, 4, 0);
606   *ptr++ = ';';
607
608   *ptr++ = hexchars[FP >> 4];
609   *ptr++ = hexchars[FP & 0xf];
610   *ptr++ = ':';
611   ptr = mem2hex(sp + 8 + 6, ptr, 4, 0); /* FP */
612   *ptr++ = ';';
613
614   *ptr++ = hexchars[SP >> 4];
615   *ptr++ = hexchars[SP & 0xf];
616   *ptr++ = ':';
617   ptr = mem2hex((char *)&sp, ptr, 4, 0);
618   *ptr++ = ';';
619
620   *ptr++ = hexchars[NPC >> 4];
621   *ptr++ = hexchars[NPC & 0xf];
622   *ptr++ = ':';
623   ptr = mem2hex((char *)&registers[NPC], ptr, 4, 0);
624   *ptr++ = ';';
625
626   *ptr++ = hexchars[O7 >> 4];
627   *ptr++ = hexchars[O7 & 0xf];
628   *ptr++ = ':';
629   ptr = mem2hex((char *)&registers[O7], ptr, 4, 0);
630   *ptr++ = ';';
631
632   *ptr++ = 0;
633
634   putpacket(remcomOutBuffer);
635
636   while (1)
637     {
638       remcomOutBuffer[0] = 0;
639
640       ptr = getpacket();
641       switch (*ptr++)
642         {
643         case '?':
644           remcomOutBuffer[0] = 'S';
645           remcomOutBuffer[1] = hexchars[sigval >> 4];
646           remcomOutBuffer[2] = hexchars[sigval & 0xf];
647           remcomOutBuffer[3] = 0;
648           break;
649
650         case 'd':               /* toggle debug flag */
651           break;
652
653         case 'g':               /* return the value of the CPU registers */
654           {
655             ptr = remcomOutBuffer;
656             ptr = mem2hex((char *)registers, ptr, 16 * 4, 0); /* G & O regs */
657             ptr = mem2hex(sp + 0, ptr, 16 * 4, 0); /* L & I regs */
658             memset(ptr, '0', 32 * 8); /* Floating point */
659             mem2hex((char *)&registers[Y],
660                     ptr + 32 * 4 * 2,
661                     8 * 4,
662                     0);         /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
663           }
664           break;
665
666         case 'G':          /* set the value of the CPU registers - return OK */
667           {
668             unsigned long *newsp, psr;
669
670             psr = registers[PSR];
671
672             hex2mem(ptr, (char *)registers, 16 * 4, 0); /* G & O regs */
673             hex2mem(ptr + 16 * 4 * 2, sp + 0, 16 * 4, 0); /* L & I regs */
674             hex2mem(ptr + 64 * 4 * 2, (char *)&registers[Y],
675                     8 * 4, 0);  /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
676
677             /* See if the stack pointer has moved.  If so, then copy the saved
678                locals and ins to the new location.  This keeps the window
679                overflow and underflow routines happy.  */
680
681             newsp = (unsigned long *)registers[SP];
682             if (sp != newsp)
683               sp = memcpy(newsp, sp, 16 * 4);
684
685             /* Don't allow CWP to be modified. */
686
687             if (psr != registers[PSR])
688               registers[PSR] = (psr & 0x1f) | (registers[PSR] & ~0x1f);
689
690             strcpy(remcomOutBuffer,"OK");
691           }
692           break;
693
694         case 'm':         /* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
695           /* Try to read %x,%x.  */
696
697           if (hexToInt(&ptr, &addr)
698               && *ptr++ == ','
699               && hexToInt(&ptr, &length))
700             {
701               if (mem2hex((char *)addr, remcomOutBuffer, length, 1))
702                 break;
703
704               strcpy (remcomOutBuffer, "E03");
705             }
706           else
707             strcpy(remcomOutBuffer,"E01");
708           break;
709
710         case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
711           /* Try to read '%x,%x:'.  */
712
713           if (hexToInt(&ptr, &addr)
714               && *ptr++ == ','
715               && hexToInt(&ptr, &length)
716               && *ptr++ == ':')
717             {
718               if (hex2mem(ptr, (char *)addr, length, 1))
719                 strcpy(remcomOutBuffer, "OK");
720               else
721                 strcpy(remcomOutBuffer, "E03");
722             }
723           else
724             strcpy(remcomOutBuffer, "E02");
725           break;
726
727         case 'c':    /* cAA..AA    Continue at address AA..AA(optional) */
728           /* try to read optional parameter, pc unchanged if no parm */
729
730           if (hexToInt(&ptr, &addr))
731             {
732               registers[PC] = addr;
733               registers[NPC] = addr + 4;
734             }
735
736 /* Need to flush the instruction cache here, as we may have deposited a
737    breakpoint, and the icache probably has no way of knowing that a data ref to
738    some location may have changed something that is in the instruction cache.
739  */
740
741           flush_i_cache();
742           return;
743
744           /* kill the program */
745         case 'k' :              /* do nothing */
746           break;
747 #if 0
748         case 't':               /* Test feature */
749           asm (" std %f30,[%sp]");
750           break;
751 #endif
752         case 'r':               /* Reset */
753           asm ("call 0
754                 nop ");
755           break;
756         }                       /* switch */
757
758       /* reply to the request */
759       putpacket(remcomOutBuffer);
760     }
761 }
762
763 /* This function will generate a breakpoint exception.  It is used at the
764    beginning of a program to sync up with a debugger and can be used
765    otherwise as a quick means to stop program execution and "break" into
766    the debugger. */
767
768 void
769 breakpoint (void)
770 {
771   if (!initialized)
772     return;
773
774   asm(" .globl _breakinst
775
776         _breakinst: ta 1
777       ");
778 }