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
92extern void putDebugChar();	/* write a single character      */
93extern 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
100static int initialized = 0;	/* !0 means we've been initialized */
101
102static void set_mem_fault_trap();
103
104static const char hexchars[]="0123456789abcdef";
105
106#define NUMREGS 72
107
108/* Number of bytes of registers.  */
109#define NUMREGBYTES (NUMREGS * 4)
110enum 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
124extern void trap_low();
125
126asm("
127	.reserve trapstack, 1000 * 4, \"bss\", 8
128
129	.data
130	.align	4
131
132in_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
178good_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
195window_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
204recursive_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
271static int
272hex (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
283static char remcomInBuffer[BUFMAX];
284static char remcomOutBuffer[BUFMAX];
285
286/* scan for the sequence $<data>#<checksum>     */
287
288unsigned char *
289getpacket (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
303retry:
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
354static void
355putpacket (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.  */
385static 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
394static unsigned char *
395mem2hex (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
420static char *
421hex2mem (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
446static 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
465void
466set_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
476asm ("
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
492static void
493set_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
506static int
507computeSignal (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
523static int
524hexToInt(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
552extern void breakinst();
553
554static void
555handle_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
768void
769breakpoint (void)
770{
771  if (!initialized)
772    return;
773
774  asm("	.globl _breakinst
775
776	_breakinst: ta 1
777      ");
778}
779