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