This commit was generated by cvs2svn to track changes on a CVS vendor
[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(ch)
273      unsigned char ch;
274 {
275   if (ch >= 'a' && ch <= 'f')
276     return ch-'a'+10;
277   if (ch >= '0' && ch <= '9')
278     return ch-'0';
279   if (ch >= 'A' && ch <= 'F')
280     return ch-'A'+10;
281   return -1;
282 }
283
284 static char remcomInBuffer[BUFMAX];
285 static char remcomOutBuffer[BUFMAX];
286
287 /* scan for the sequence $<data>#<checksum>     */
288
289 unsigned char *
290 getpacket ()
291 {
292   unsigned char *buffer = &remcomInBuffer[0];
293   unsigned char checksum;
294   unsigned char xmitcsum;
295   int count;
296   char ch;
297
298   while (1)
299     {
300       /* wait around for the start character, ignore all other characters */
301       while ((ch = getDebugChar ()) != '$')
302         ;
303
304 retry:
305       checksum = 0;
306       xmitcsum = -1;
307       count = 0;
308
309       /* now, read until a # or end of buffer is found */
310       while (count < BUFMAX)
311         {
312           ch = getDebugChar ();
313           if (ch == '$')
314             goto retry;
315           if (ch == '#')
316             break;
317           checksum = checksum + ch;
318           buffer[count] = ch;
319           count = count + 1;
320         }
321       buffer[count] = 0;
322
323       if (ch == '#')
324         {
325           ch = getDebugChar ();
326           xmitcsum = hex (ch) << 4;
327           ch = getDebugChar ();
328           xmitcsum += hex (ch);
329
330           if (checksum != xmitcsum)
331             {
332               putDebugChar ('-');       /* failed checksum */
333             }
334           else
335             {
336               putDebugChar ('+');       /* successful transfer */
337
338               /* if a sequence char is present, reply the sequence ID */
339               if (buffer[2] == ':')
340                 {
341                   putDebugChar (buffer[0]);
342                   putDebugChar (buffer[1]);
343
344                   return &buffer[3];
345                 }
346
347               return &buffer[0];
348             }
349         }
350     }
351 }
352
353 /* send the packet in buffer.  */
354
355 static void
356 putpacket(buffer)
357      unsigned char *buffer;
358 {
359   unsigned char checksum;
360   int count;
361   unsigned char ch;
362
363   /*  $<packet info>#<checksum>. */
364   do
365     {
366       putDebugChar('$');
367       checksum = 0;
368       count = 0;
369
370       while (ch = buffer[count])
371         {
372           putDebugChar(ch);
373           checksum += ch;
374           count += 1;
375         }
376
377       putDebugChar('#');
378       putDebugChar(hexchars[checksum >> 4]);
379       putDebugChar(hexchars[checksum & 0xf]);
380
381     }
382   while (getDebugChar() != '+');
383 }
384
385 /* Indicate to caller of mem2hex or hex2mem that there has been an
386    error.  */
387 static volatile int mem_err = 0;
388
389 /* Convert the memory pointed to by mem into hex, placing result in buf.
390  * Return a pointer to the last char put in buf (null), in case of mem fault,
391  * return 0.
392  * If MAY_FAULT is non-zero, then we will handle memory faults by returning
393  * a 0, else treat a fault like any other fault in the stub.
394  */
395
396 static unsigned char *
397 mem2hex(mem, buf, count, may_fault)
398      unsigned char *mem;
399      unsigned char *buf;
400      int count;
401      int may_fault;
402 {
403   unsigned char ch;
404
405   set_mem_fault_trap(may_fault);
406
407   while (count-- > 0)
408     {
409       ch = *mem++;
410       if (mem_err)
411         return 0;
412       *buf++ = hexchars[ch >> 4];
413       *buf++ = hexchars[ch & 0xf];
414     }
415
416   *buf = 0;
417
418   set_mem_fault_trap(0);
419
420   return buf;
421 }
422
423 /* convert the hex array pointed to by buf into binary to be placed in mem
424  * return a pointer to the character AFTER the last byte written */
425
426 static char *
427 hex2mem(buf, mem, count, may_fault)
428      unsigned char *buf;
429      unsigned char *mem;
430      int count;
431      int may_fault;
432 {
433   int i;
434   unsigned char ch;
435
436   set_mem_fault_trap(may_fault);
437
438   for (i=0; i<count; i++)
439     {
440       ch = hex(*buf++) << 4;
441       ch |= hex(*buf++);
442       *mem++ = ch;
443       if (mem_err)
444         return 0;
445     }
446
447   set_mem_fault_trap(0);
448
449   return mem;
450 }
451
452 /* This table contains the mapping between SPARC hardware trap types, and
453    signals, which are primarily what GDB understands.  It also indicates
454    which hardware traps we need to commandeer when initializing the stub. */
455
456 static struct hard_trap_info
457 {
458   unsigned char tt;             /* Trap type code for SPARClite */
459   unsigned char signo;          /* Signal that we map this trap into */
460 } hard_trap_info[] = {
461   {1, SIGSEGV},                 /* instruction access error */
462   {2, SIGILL},                  /* privileged instruction */
463   {3, SIGILL},                  /* illegal instruction */
464   {4, SIGEMT},                  /* fp disabled */
465   {36, SIGEMT},                 /* cp disabled */
466   {7, SIGBUS},                  /* mem address not aligned */
467   {9, SIGSEGV},                 /* data access exception */
468   {10, SIGEMT},                 /* tag overflow */
469   {128+1, SIGTRAP},             /* ta 1 - normal breakpoint instruction */
470   {0, 0}                        /* Must be last */
471 };
472
473 /* Set up exception handlers for tracing and breakpoints */
474
475 void
476 set_debug_traps()
477 {
478   struct hard_trap_info *ht;
479
480   for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
481     exceptionHandler(ht->tt, trap_low);
482
483   initialized = 1;
484 }
485
486 asm ("
487 ! Trap handler for memory errors.  This just sets mem_err to be non-zero.  It
488 ! assumes that %l1 is non-zero.  This should be safe, as it is doubtful that
489 ! 0 would ever contain code that could mem fault.  This routine will skip
490 ! past the faulting instruction after setting mem_err.
491
492         .text
493         .align 4
494
495 _fltr_set_mem_err:
496         sethi %hi(_mem_err), %l0
497         st %l1, [%l0 + %lo(_mem_err)]
498         jmpl %l2, %g0
499         rett %l2+4
500 ");
501
502 static void
503 set_mem_fault_trap(enable)
504      int enable;
505 {
506   extern void fltr_set_mem_err();
507   mem_err = 0;
508
509   if (enable)
510     exceptionHandler(9, fltr_set_mem_err);
511   else
512     exceptionHandler(9, trap_low);
513 }
514
515 /* Convert the SPARC hardware trap type code to a unix signal number. */
516
517 static int
518 computeSignal(tt)
519      int tt;
520 {
521   struct hard_trap_info *ht;
522
523   for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
524     if (ht->tt == tt)
525       return ht->signo;
526
527   return SIGHUP;                /* default for things we don't know about */
528 }
529
530 /*
531  * While we find nice hex chars, build an int.
532  * Return number of chars processed.
533  */
534
535 static int
536 hexToInt(char **ptr, int *intValue)
537 {
538   int numChars = 0;
539   int hexValue;
540
541   *intValue = 0;
542
543   while (**ptr)
544     {
545       hexValue = hex(**ptr);
546       if (hexValue < 0)
547         break;
548
549       *intValue = (*intValue << 4) | hexValue;
550       numChars ++;
551
552       (*ptr)++;
553     }
554
555   return (numChars);
556 }
557
558 /*
559  * This function does all command procesing for interfacing to gdb.  It
560  * returns 1 if you should skip the instruction at the trap address, 0
561  * otherwise.
562  */
563
564 extern void breakinst();
565
566 static void
567 handle_exception (registers)
568      unsigned long *registers;
569 {
570   int tt;                       /* Trap type */
571   int sigval;
572   int addr;
573   int length;
574   char *ptr;
575   unsigned long *sp;
576
577 /* First, we must force all of the windows to be spilled out */
578
579   asm(" save %sp, -64, %sp
580         save %sp, -64, %sp
581         save %sp, -64, %sp
582         save %sp, -64, %sp
583         save %sp, -64, %sp
584         save %sp, -64, %sp
585         save %sp, -64, %sp
586         save %sp, -64, %sp
587         restore
588         restore
589         restore
590         restore
591         restore
592         restore
593         restore
594         restore
595 ");
596
597   if (registers[PC] == (unsigned long)breakinst)
598     {
599       registers[PC] = registers[NPC];
600       registers[NPC] += 4;
601     }
602
603   sp = (unsigned long *)registers[SP];
604
605   tt = (registers[TBR] >> 4) & 0xff;
606
607   /* reply to host that an exception has occurred */
608   sigval = computeSignal(tt);
609   ptr = remcomOutBuffer;
610
611   *ptr++ = 'T';
612   *ptr++ = hexchars[sigval >> 4];
613   *ptr++ = hexchars[sigval & 0xf];
614
615   *ptr++ = hexchars[PC >> 4];
616   *ptr++ = hexchars[PC & 0xf];
617   *ptr++ = ':';
618   ptr = mem2hex((char *)&registers[PC], ptr, 4, 0);
619   *ptr++ = ';';
620
621   *ptr++ = hexchars[FP >> 4];
622   *ptr++ = hexchars[FP & 0xf];
623   *ptr++ = ':';
624   ptr = mem2hex(sp + 8 + 6, ptr, 4, 0); /* FP */
625   *ptr++ = ';';
626
627   *ptr++ = hexchars[SP >> 4];
628   *ptr++ = hexchars[SP & 0xf];
629   *ptr++ = ':';
630   ptr = mem2hex((char *)&sp, ptr, 4, 0);
631   *ptr++ = ';';
632
633   *ptr++ = hexchars[NPC >> 4];
634   *ptr++ = hexchars[NPC & 0xf];
635   *ptr++ = ':';
636   ptr = mem2hex((char *)&registers[NPC], ptr, 4, 0);
637   *ptr++ = ';';
638
639   *ptr++ = hexchars[O7 >> 4];
640   *ptr++ = hexchars[O7 & 0xf];
641   *ptr++ = ':';
642   ptr = mem2hex((char *)&registers[O7], ptr, 4, 0);
643   *ptr++ = ';';
644
645   *ptr++ = 0;
646
647   putpacket(remcomOutBuffer);
648
649   while (1)
650     {
651       remcomOutBuffer[0] = 0;
652
653       ptr = getpacket();
654       switch (*ptr++)
655         {
656         case '?':
657           remcomOutBuffer[0] = 'S';
658           remcomOutBuffer[1] = hexchars[sigval >> 4];
659           remcomOutBuffer[2] = hexchars[sigval & 0xf];
660           remcomOutBuffer[3] = 0;
661           break;
662
663         case 'd':               /* toggle debug flag */
664           break;
665
666         case 'g':               /* return the value of the CPU registers */
667           {
668             ptr = remcomOutBuffer;
669             ptr = mem2hex((char *)registers, ptr, 16 * 4, 0); /* G & O regs */
670             ptr = mem2hex(sp + 0, ptr, 16 * 4, 0); /* L & I regs */
671             memset(ptr, '0', 32 * 8); /* Floating point */
672             mem2hex((char *)&registers[Y],
673                     ptr + 32 * 4 * 2,
674                     8 * 4,
675                     0);         /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
676           }
677           break;
678
679         case 'G':          /* set the value of the CPU registers - return OK */
680           {
681             unsigned long *newsp, psr;
682
683             psr = registers[PSR];
684
685             hex2mem(ptr, (char *)registers, 16 * 4, 0); /* G & O regs */
686             hex2mem(ptr + 16 * 4 * 2, sp + 0, 16 * 4, 0); /* L & I regs */
687             hex2mem(ptr + 64 * 4 * 2, (char *)&registers[Y],
688                     8 * 4, 0);  /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
689
690             /* See if the stack pointer has moved.  If so, then copy the saved
691                locals and ins to the new location.  This keeps the window
692                overflow and underflow routines happy.  */
693
694             newsp = (unsigned long *)registers[SP];
695             if (sp != newsp)
696               sp = memcpy(newsp, sp, 16 * 4);
697
698             /* Don't allow CWP to be modified. */
699
700             if (psr != registers[PSR])
701               registers[PSR] = (psr & 0x1f) | (registers[PSR] & ~0x1f);
702
703             strcpy(remcomOutBuffer,"OK");
704           }
705           break;
706
707         case 'm':         /* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
708           /* Try to read %x,%x.  */
709
710           if (hexToInt(&ptr, &addr)
711               && *ptr++ == ','
712               && hexToInt(&ptr, &length))
713             {
714               if (mem2hex((char *)addr, remcomOutBuffer, length, 1))
715                 break;
716
717               strcpy (remcomOutBuffer, "E03");
718             }
719           else
720             strcpy(remcomOutBuffer,"E01");
721           break;
722
723         case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
724           /* Try to read '%x,%x:'.  */
725
726           if (hexToInt(&ptr, &addr)
727               && *ptr++ == ','
728               && hexToInt(&ptr, &length)
729               && *ptr++ == ':')
730             {
731               if (hex2mem(ptr, (char *)addr, length, 1))
732                 strcpy(remcomOutBuffer, "OK");
733               else
734                 strcpy(remcomOutBuffer, "E03");
735             }
736           else
737             strcpy(remcomOutBuffer, "E02");
738           break;
739
740         case 'c':    /* cAA..AA    Continue at address AA..AA(optional) */
741           /* try to read optional parameter, pc unchanged if no parm */
742
743           if (hexToInt(&ptr, &addr))
744             {
745               registers[PC] = addr;
746               registers[NPC] = addr + 4;
747             }
748
749 /* Need to flush the instruction cache here, as we may have deposited a
750    breakpoint, and the icache probably has no way of knowing that a data ref to
751    some location may have changed something that is in the instruction cache.
752  */
753
754           flush_i_cache();
755           return;
756
757           /* kill the program */
758         case 'k' :              /* do nothing */
759           break;
760 #if 0
761         case 't':               /* Test feature */
762           asm (" std %f30,[%sp]");
763           break;
764 #endif
765         case 'r':               /* Reset */
766           asm ("call 0
767                 nop ");
768           break;
769         }                       /* switch */
770
771       /* reply to the request */
772       putpacket(remcomOutBuffer);
773     }
774 }
775
776 /* This function will generate a breakpoint exception.  It is used at the
777    beginning of a program to sync up with a debugger and can be used
778    otherwise as a quick means to stop program execution and "break" into
779    the debugger. */
780
781 void
782 breakpoint()
783 {
784   if (!initialized)
785     return;
786
787   asm(" .globl _breakinst
788
789         _breakinst: ta 1
790       ");
791 }