/*************************************************************************
**                                                                      **
**                          DEBUG_c                                     **
**                          ~~~~~~~                                     **
**                                                                      **
**    (c) Copyright 1988,1991 David J. Walker                           **
**                                                                      **
**    This code may be freely distributed and used as long as this      **
**    copyright notice remains intact, and no commercial gain is        **
**    made from the distribution.                                       **
**                                                                      **
**    This file is a Source Code Debugger for use with C on             **
**    the Sinclair QL. It is draws heavily upon the ideas in the        **
**    book  "DEBUGGING C" by Robert Ward and published by QUE.          **
**                                                                      **
**    It is up to the user to supply the snapshot routines.  If         **
**    they are not provided, then the default routines will be          **
**    included (Source in files "Win1_C_DEBUG_SNAPn_c").                **
**                                                                      **
**  Entry points available to these routines are:                       **
**      dbg_init        - The initialisation routine                    **
**      dbg             - the main debug call                           **
**      dbg_print       - 'printf' style interface to log file          **
**      dbg_vfprint     - 'vfprintf' style interface to log file        **
**      dbg_write       - 'write' style interface to log file           **
**                                                                      **
**                                          LAST AMENDED: 15/12/92      **
*************************************************************************/

#define version   "1.13"

/*==========================================================================
                        DEVELOPMENT HISTORY
                        ===================

           1.03 DJW Trace level added to trace output
           1.04 DJW Use of Trace Level redefined to be bit-dependent field
           1.05 DJW Display of Trace level changed to Hex
           1.06 DJW NEWLOG command changed to LOG
                    Trace level now displayed when tracing to screen
           1.07 DJW Altered to be PDQC compatible
           1.08 DJW Log buffers flushed on every call in case system 
                    crashes
           1.09 DJW 'dbg_write' entry point added
01/04/91   1.10 DJW C68 compatibility added
                    MALLOC library options added
22/11/91   1.11 DJW Alterations made to remove need for assembler
                    support by using vfprintf routines instead.  Also
                    changes made to support operation under MINIX.
02/12/92   1.12 DJW Alterations made as a result of using LINT, and to
                    support operation under Unix SVR4
30/01/94   1.13 DJW Define _PROTOTYPE macro locally within file
===========================================================================*/

#include <debug.h>
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef MALLOC_WARN
#include <malloc.h>             /* Allow for MALLOC debugging library */
#endif /* MALLOC_WARN */
#ifdef QDOS
#include <qdos.h>
#endif /* QDOS */
#ifndef QDOS
#include <signal.h>
#endif /* ! QDOS */
#include <stdio.h>

#ifndef PUBLIC
#define PUBLIC  extern
#define PRIVATE static
#endif


/*
        Global Declarations used by dbug
        Change these to alter system limits mentioned in documentation
*/
#define MAX_LINE    128     /* maximum input line length */
#define STOP_LIST   3       /* Number of stop/trace points */
                            /* supported by this version   */
#define WATCH_LIST  3       /* Number of watch points */
                            /* supported by this version */
#define MAX_NAME_LEN 40     /* plenty for most people */


#ifdef MALLOC_WARN
extern  int     mwarn_level, mfatal_level;
extern  int     mchecking, merrfd;
static  char    dbg_merrfile[40] = "<stderr>";
#endif /* MALLOC_WARN */

#define OFF         0
#define STP         1
#define TRC         2
#define ALL         3

#define NO          0       /* general purpose logic values */
#define YES         1
#ifdef OK
#undef OK
#endif
#define OK          1
#define NOT_OK		0
#define ON          1

#define SILENT      0
#define VERBOSE     1
#define ERR         -1
#define HELP        0       /* logical values for commands */
#define STOP        1
#define WATCH       2
#define WMODE       3
#define SMODE       4
#define LEVEL       5
#define TSKIP       6
#define SSKIP       7
#define SNAP        8
#define DUMP        9
#define STACK       10
#define LOCAL       11
#define CONT        12
#define CRC         13
#define GRAN        14
#define SHOW        15
#define TRACE       16
#define QUIT        17
#define CLEAR       18
#define CMODE       19
#define POKE        20
#define RETRACE     21
#define AMODE       22
#define ADDRESS     23
#define TMODE       24
#define LOG         25
#define NOENTRY     26
#define ERRNO       27
#define MWARN       28
#define MFATAL      29
#define MCHECK      30
#define MFILE       31

static  int     Initialised = NO;

static  FILE *  LogFile;
static  FILE *  CmdInFile;           /* Debug command channels   */
static  FILE *  CmdOutFile;           /* Debug command channels   */
#ifdef QDOS
static  chanid_t LogChan;
static  chanid_t CmdChan;           /* Debug channels QDOS id's */
#endif

static  char *  CodeStart;         /* Job code start addresses */
static  char *  DataStart;
static  char *  CodeTop;           /* Job end addresses */
static  char *  DataTop;

static  va_list DbgParams;
static  char *  DbgFormat;
static  int     DbgErrno;
static  int     DbgOserr;
static  int     StopSkip = 0;       /* skip count for stop points */
static  int     TraceSkip = 0;      /* skip count for trace points */
typedef char    lst[STOP_LIST][MAX_NAME_LEN];
static  lst     StopList;           /* stop point names */
static  lst     TraceList;          /* trace point names */
        int     dbg_level = 0;      /* level of trace details selected */
static  int     Granularity = 1;    /* current trace granularity */
static  int     GranCount = 0;      /* how many calls (for granularity) */
static  int     MonitorMode = OFF;  /* mode of memory monitor check */
static  int     StackMode = OFF;    /* mode of stack check options */
static  int     CrcMode = OFF;      /* mode of crc check */
static  int     AddressMode = 1;
static  int     TraceMode;
static  char *  CrcStart;
static  int     CrcLength;
static  int     CrcValue;
static  int *   StackTop;               /* top of stack */
static  int *   WatchLocs[WATCH_LIST];  /* addresses of watch locations */
static  int     WatchOld[WATCH_LIST];   /* storage for prior watch values */
static  int *   DbgFrame;               /* frame pointer for call */

#ifdef QDOS
static  struct  QLRECT DbgWindow = {448,220,32,20};
#endif

#ifdef _MINIX
static  int	_oserr = 0;
#endif

/*
 *  Prototypes for routines local to this file
 */
#ifdef __STDC__
#define _PROTOTYPE(params) params
#else
#define _PROTOTYPE(params) ()
#endif

PRIVATE void    dbg_input       _PROTOTYPE((char *, int));
PRIVATE void    Addr_Details    _PROTOTYPE((void));
PRIVATE char *  Addr_Display    _PROTOTYPE((char *, long));
PRIVATE void    Addr_Line       _PROTOTYPE((char *, char *, char *));
PRIVATE int     Addr_Parse      _PROTOTYPE((char *, int *, char *, char *));
PRIVATE int     Advance_Frame   _PROTOTYPE((int **));
PRIVATE int     Char_To_Mode    _PROTOTYPE((int *, int ));
PRIVATE int     Cmd_Parse       _PROTOTYPE((int *, int *, char *, char *));
PRIVATE int     Cmd_Type        _PROTOTYPE((char *));
PRIVATE void    Commands        _PROTOTYPE((char *, int));
PRIVATE int     CRC_16          _PROTOTYPE((void));
PRIVATE void    CRC_Details     _PROTOTYPE((void));
PRIVATE int     CRC_Update      _PROTOTYPE((int, int));
PRIVATE void    Fmt_Hex         _PROTOTYPE((unsigned char *, int));
PRIVATE void    Help            _PROTOTYPE((void));
PRIVATE int     In_List         _PROTOTYPE((lst, char *));
PRIVATE void    List_List       _PROTOTYPE((char *, int, lst ));
PRIVATE void    List_Stop       _PROTOTYPE((void));
PRIVATE void    List_Trace      _PROTOTYPE((void));
PRIVATE void    List_Watch      _PROTOTYPE((void));
PRIVATE void    Local_Dump      _PROTOTYPE((int));
PRIVATE void    Mode_Checks     _PROTOTYPE((int *, int));
PRIVATE void    Mode_Details    _PROTOTYPE((void));
PRIVATE char *  Mode_String     _PROTOTYPE((int));
PRIVATE void    Numeric_Args    _PROTOTYPE((char *, int *));
PRIVATE int     Numeric_Parse   _PROTOTYPE((char *, int *));
#ifdef QDOS
PRIVATE void    Open_File       _PROTOTYPE((char *, FILE **, char *, chanid_t *));
#else
PRIVATE void    Open_File       _PROTOTYPE((char *, FILE **, char *));
#endif
PRIVATE void    Out_Of_Range    _PROTOTYPE((void));
PRIVATE int     Pattern_Match   _PROTOTYPE((char *, char *));
PRIVATE void    Print_None      _PROTOTYPE((int));
PRIVATE int     Stack_Dump      _PROTOTYPE((int));
PRIVATE void    Status_Display  _PROTOTYPE((char *,int));
PRIVATE void    Stopped_At      _PROTOTYPE((char *, int));
PRIVATE void    Syntax_Error    _PROTOTYPE((void));
PRIVATE void    Trace_Controls  _PROTOTYPE((void));
PRIVATE int     Watch           _PROTOTYPE((void));
PRIVATE void    Watch_Report    _PROTOTYPE((int, int, int *));
#ifndef QDOS
PRIVATE void    Set_Signals     _PROTOTYPE((void));
PRIVATE void    Catch_Signal    _PROTOTYPE((void));
#endif /* ! QDOS */

#undef _PROTOTYPE

/*================================================================== DBG */
PUBLIC
void    dbg (char *stop, int level, char *formatstr, ...)
/*      ~~~
 *  This function is called by the program under test.  It can
 *  be called with a variable number of parameters like 'printf'.
 *  Extra parameters are assumed to follow 'formatstr' in the
 *  parameter list.
 *
 *  Beacause of the variable list of parameters, the 'dbg'
 *  function should be prototyped as:
 *
 *      extern  void  dbg (int, int, char *, ..);
 *
 *  in all the source files that will reference it.
 *======================================================================*/
{
    int     mark;               /* NOTE.  must be first local */
    int     interp;

    DbgFormat = formatstr;
    va_start(DbgParams,formatstr);
    if (! Initialised) dbg_init();
    DbgErrno = errno;
    DbgOserr = _oserr;
    DbgFrame = &mark + 1;
    interp = OFF;
    Mode_Checks (&interp, ALL);
    /*
    -----------------------------
    Process stop point if set
    -----------------------------
    */
    if (In_List(StopList, stop))  {
        Mode_Checks (&interp,STP);
        if (StopSkip)
            StopSkip -= 1;
        else
            interp = ON;
    }
    /*
    --------------------------
    Process Trace point if set
    --------------------------
    */
    if ((((level & dbg_level) == level) &&  (In_List (TraceList, stop)))
    || interp == ON )  {
        /* Keep track of enabled calls for granularity */
        GranCount = (GranCount++) % Granularity;
        /* Update trace skip count */
        if ((GranCount || TraceSkip) && interp != ON) {
            if (TraceSkip)
                TraceSkip--;
        } else {
			Mode_Checks (&interp,TRACE);
            if (formatstr != NULL) {
                if (stop != NULL) {
                    if (TraceMode || interp == ON) {
                        dbg_print ("%4.4x %s: ",level,stop);
                        dbg_vfprint (DbgFormat, DbgParams);
                        dbg_print ("\n");
                    } else {
                        (void) fprintf (LogFile,"%4.4x %s: ",(unsigned)level, stop);
                        (void) vfprintf (LogFile,DbgFormat,DbgParams);
                        (void) fprintf (LogFile,"\n");
                    }
                }
            }
        }
    }
    /*
    -----------------------------
    If Stop required, then do one
    -----------------------------
    */
#ifdef QDOS
    if ((interp == ON)                        /* Stop condition met   */
    ||  (keyrow(7)==7)) {                     /* ... or User Break-in */
#else
    if (interp == ON) {                       /* Stop condition met   */
#endif
        Stopped_At(stop,level);
        Commands (stop,level);
    }
    errno = DbgErrno;
    _oserr = DbgOserr;
    va_end(DbgParams);
}

/*======================================================== DBG_INIT */
PUBLIC
void    dbg_init()
/*      ~~~~~~~~
 *  This routine must be called once at the beginning of the run
 *  to initialise the debugging system.  It always causes a "STOP"
 *==================================================================*/
{
    int     i;                 /* NOTE. Must be first item in locals */
    FILE    *newfile;
#ifdef QDOS
    long    ownerjob;
    long    nextjob;
    long    priority;
    chanid_t newchan;
#endif

    DbgFrame = &i + 1;
    DbgErrno = errno;
    DbgOserr = _oserr;

    /*
    ----------------------------------
    Set up catching of SIGINT to allow
    user to break in unconditionally.
    ----------------------------------
    */
#ifndef QDOS
    Set_Signals();
#endif
    /*
    ---------------------------------------
    Empty out stop list
    Default is enable trace in all functions
    ----------------------------------------
    */
    for (i = 0 ; i < STOP_LIST ; i++ ) {
        StopList[i][0] = '\0';
        TraceList[i][0] = '\0';
    }
    (void) strcpy (TraceList[0],"*");
    /*
    ---------------------------------
    Give defaults to other modalities
    ---------------------------------
    */
    for (i=0 ; i<WATCH_LIST ; i++)
        WatchLocs[i] = 0;

    StackTop = (int *)((&i) + 1);        /* Framepointer */
    StackTop = (int *) *StackTop;     /* Follow down a level */

#ifdef QDOS
    ownerjob = nextjob = -1;
    (void) mt_jinf (&ownerjob,&nextjob,&priority,&CodeStart);
    CodeTop = (char *)((int)CodeStart-(int)'\x68');
    CodeTop = (char *)(*(int *)CodeTop + (int)CodeStart);
    (void) mt_jinf (&ownerjob,&nextjob,&priority,&DataStart);
    DataTop = (char *)((int)DataStart-(int)'\x68');
    DataTop = (char *)(*(int *)DataTop + (int)DataStart);
#endif
    /*
    ---------------------------------
    Open up streams for debug I/O
    By default use the screen
    ---------------------------------
    */
#ifdef QDOS
    LogFile = CmdInFile = CmdOutFile = newfile = fopen ("CON","r+");
    LogChan = CmdChan = newchan = fgetchid(CmdOutFile);
    (void) sd_wdef (CmdChan,-1,0,0,&DbgWindow);
    (void) sd_clear  (CmdChan,-1);
    setnbf (CmdOutFile);
#else
    CmdInFile = newfile = stdin;
    LogFile = CmdOutFile = stdout;
#endif
    dbg_print ("C Source Code Debugger v%s   (c)1988,1991  D.J.Walker\n",version);
    dbg_print ("=========================\n\n");
#ifdef QDOS
    Open_File ("Command",&newfile,"'<SCREEN>'", &newchan);
    Open_File ("Log",&LogFile,"Command channel",&LogChan);
#else
    Open_File ("Command",&newfile,"'<stdin>'");
    Open_File ("Log",&LogFile,"<stdout>");
#endif
    if (LogFile == CmdOutFile)  {
        LogFile = newfile;
#ifdef QDOS
        LogChan = newchan;
    } else {
        setnbf (LogFile);
#endif
    }
    if (CmdInFile != newfile) {
        CmdInFile = newfile;
#ifdef QDOS
        (void) sd_clear (CmdChan,-1);
        CmdChan = newchan;
        setnbf (CmdInFile);
        (void) sd_clear (CmdChan,-1);
#endif
    }
    TraceMode = (CmdOutFile == LogFile);
    Commands("DEBUG INITIALISATION",0);
    Initialised = YES;

    /*
    Now initialise MALLOC library variables
    */
#ifdef MALLOC_WARN
    if (! mwarn_level)
        mwarn_level = 128;
    if (! mfatal_level)
        mfatal_level = 128;
    if (! merrfd) {
        merrfd = fileno (LogFile);
        (void) strcpy (dbg_merrfile,"<DEBUG>");
    }
#endif
    errno = DbgErrno;
    _oserr = DbgOserr;
    return;
}

/*======================================================== OPEN_FILE */
PRIVATE
#ifdef QDOS
void    Open_File (type, actualfile, assumedname, qdoschan)
#else
void    Open_File (type, actualfile, assumedname)
#endif
/*
 *
 *-------------------------------------------------------------------*/
char    *type;
FILE    **actualfile;
char    *assumedname;
#ifdef QDOS
chanid_t  *qdoschan;
#endif
{
    char    name[40];           /* Name for debug channel */
    FILE    *reply;

    dbg_print (" %s channel (ENTER for %s)\n",type,assumedname);
    dbg_input (name,sizeof(name));
    if (strlen(name)) {
        (void) remove (name);               /* First remove any existing file */
        if ((reply = fopen (name,"w+"))==NULL)
            dbg_print (" ERROR - failed to open file %s\n '%s' will be used instead\n",name,assumedname);
        else {
            dbg_print (" OK\n");
            *actualfile = reply;
#ifdef QDOS
            setnbf (*actualfile);
#endif
        }
    }
#ifdef QDOS
    *qdoschan = fgetchid (*actualfile);
#endif
    return;
}

/*========================================================== WATCH */
PRIVATE
int     Watch()
/*		~~~~~
 *	This function watches up to three memory locations,
 *	reporting any changes.  Prints a message of the form
 *		WATCH: label address oldval newval
 *	whenever a change is detected.
 *-----------------------------------------------------------------*/
{
    int     rval,i;

    rval = OK;
    for (i=0 ; i<WATCH_LIST ; i++) {
        if (WatchLocs[i]) {
            if (WatchOld[i] != *WatchLocs[i]) {
                rval = NOT_OK;
                Watch_Report((i+1),WatchOld[i], WatchLocs[i]);
                    WatchOld[i] = *WatchLocs[i];
            }
		}
	}
    return (rval);
}

/*======================================================= WTCH_RPT */
PRIVATE
void    Watch_Report (number,old, ptr)
/*		~~~~~~~~~~~~
 *	Report when a watch point changes
 *-----------------------------------------------------------------*/
int     number,old, *ptr;
{
    char    ptradr[10];

    dbg_print ("WATCH:\n");
    dbg_print (" %d. %s was $%08.8x, now=$%08.8x\n",
                 number, Addr_Display(ptradr,(long)ptr), old, *ptr);
}

/*======================================================= DBG_CMDS */
PRIVATE
void    Commands (where,level)
/*		~~~~~~~~
 *	This is the interactive command interpreter
 *=================================================================*/
char    *where;
int     level;
{
    int     arg1, arg2;
    char    arg1s[40], arg2s[40];

#ifdef QDOS
    (void) sd_setin (CmdChan,-1,4);
#endif
    for ( ; ; ) {
        /*
        ------------------------------------------------------
        'Cmd_Parse' returns the type of the command and tries
        to interpret the following arguments as integer or
        string.  'arg1', 'arg2' and 'arg2s' are filled
        accordingly. 'Arg2s' is guaranteed to be a string less
        than 80 after 'Cmd_Parse'.
        ------------------------------------------------------
        */
        switch (Cmd_Parse (&arg1, &arg2, arg1s, arg2s)) {
		case HELP:
				Help();
				break;

		case SHOW:
				Status_Display (where,level);
				break;

		case STOP:
				if ((arg1 < 1)
				||  (arg1 > STOP_LIST))
					Out_Of_Range();
				else {
					(void) strcpy (StopList[arg1 - 1],arg2s);
					List_Stop();
				}
				break;

		case TRACE:
				if ((arg1 < 1)
				||  (arg1 > STOP_LIST))
					Out_Of_Range();
				else {
					(void) strcpy (TraceList[arg1 - 1], arg2s);
					List_Trace();
					Trace_Controls();
				}
				break;

		case WATCH:
				if ((arg1 < 1)
				||  (arg1 > WATCH_LIST))
					Out_Of_Range();
				else {
					WatchLocs[--arg1] = (int *) arg2;
					WatchOld[arg1] = *WatchLocs[arg1];
					List_Watch();
				}
				break;

		case WMODE:
				if (Char_To_Mode(&MonitorMode,*arg1s)!=ERR)
					List_Watch();
				break;

		case SMODE:
				if (Char_To_Mode (&StackMode,*arg1s)!=ERR)
					Mode_Details();
				break;

		case CMODE:
				if (Char_To_Mode (&CrcMode,*arg1s)!=ERR)  {
					CRC_Details();
					Mode_Details();
				}
				break;

		case TMODE:
				TraceMode ^= 1;
				Trace_Controls();
				break;

		case AMODE:
				AddressMode ^= 1;
				Mode_Details();
				break;

		case ADDRESS:
				Addr_Details();
				break;

		case LEVEL:
				if (arg1 < 0)
					Syntax_Error();
				else  {
					dbg_level = arg1;
					Trace_Controls();
				}
				break;

		case TSKIP:
				if (arg1 < 0)
					Syntax_Error();
				else  {
					TraceSkip = arg1;
					List_Trace();
					Trace_Controls();
				}
				break;

		case SSKIP:
				if (arg1 < 0)
					Syntax_Error();
				else  {
					StopSkip = arg1;
					List_Stop();
					}
				break;

		case SNAP:
				if (arg1 < 0)
					Syntax_Error();
				else
					switch (arg1) {
					case 1: dbg_snap1();    /* User supplied function */
							break;
					case 2: dbg_snap2();    /* User supplied function */
							break;
					case 3: dbg_snap3();    /* User supplied function */
							break;
					default: Out_Of_Range();
					}
				break;
		case DUMP:
				if (arg2 < 1)
					Syntax_Error();
				else
					Fmt_Hex ((unsigned char *) arg1, arg2);
				break;

		case STACK:
				(void) Stack_Dump (VERBOSE);
				break;

		case LOCAL:
				if (arg1 < 1)
					arg1 = 1;    /* always do one */
				Local_Dump (arg1);
				break;

		case CRC:
				if ( arg1 < 0)  {
					arg1 = (int)CrcStart;
					arg2 = CrcLength;
				}
				if (arg2 < 0)   {
					Syntax_Error();
					break;
				}
				CrcStart = (char *)arg1;
				CrcLength = arg2;
				CrcValue = CRC_16();
				CRC_Details();
				Mode_Details();
				break;

		case GRAN:
				if (arg1 < 0)
					Syntax_Error();
				else  {
					if (arg1 < 1)
						Out_Of_Range();
					else
						Granularity = arg1;
					Trace_Controls();
				}
				break;

		case CONT:
				return;

		case QUIT:
				dbg_print ("\n *** PROGRAM ABANDONED ***\n\n");
				exit(-1);
				/* NOTREACHED */

		case CLEAR:
#ifdef QDOS
				(void) sd_clear (CmdChan,(chanid_t)-1);
#endif
				break;

		case POKE:
				if (arg1 < 0)
					Out_Of_Range();
				else
					*((char *)arg1) = arg2;
				break;

		case RETRACE:
				dbg_print ("%s: ",where);
				dbg_vfprint (DbgFormat,DbgParams);
				dbg_print ("\n");
				break;

		case LOG:
				if (LogFile != CmdOutFile)    {
					(void) fclose (LogFile);
					LogFile = CmdOutFile;
				}
#ifdef QDOS
                Open_File ("Log",&LogFile,"Command channel", &LogChan);
#else
                Open_File ("Log",&LogFile,"Command channel");
#endif
                TraceMode = (CmdOutFile == LogFile);
                break;
        case NOENTRY:
                dbg_print ("Use HELP if you want a command list\n");
                break;
        case ERRNO:
                dbg_print ("errno=%d, _oserr=%d\n",DbgErrno, DbgOserr);
                break;
#ifdef MALLOC_WARN
        case MWARN:
                if (arg1 >= 0)
                    (void)malloc_opt(MALLOC_WARN,arg1);
                dbg_print ("MALLOC_WARN = %d\n", mwarn_level);
                    break;
        case MFATAL:
                if (arg1 >= 0)
                    (void) malloc_opt(MALLOC_FATAL,arg1);
                dbg_print ("MALLOC_FATAL = %d\n", mfatal_level);
                break;
        case MCHECK:
                if (arg1 >= 0)
                    (void) malloc_opt(MALLOC_CKCHAIN,arg1);
                dbg_print ("MALLOC_CKCHAIN = %d\n",mchecking);
                break;
        case MFILE:
                if (*arg1s) {
                    (void) strcpy (dbg_merrfile, arg1s);
                    if (strcmp ("<DEBUG>", arg1s)) {
                        if (merrfd == fileno (LogFile))
                            merrfd = 2;
                        (void) malloc_opt(MALLOC_ERRFILE,arg1s);
                    } else {
                        if ((merrfd)
                        &&  (merrfd != 2)
                        &&  (merrfd != fileno(LogFile)))
                            (void) close (merrfd);
                        merrfd = fileno (LogFile);
                    }
                }
                dbg_print ("MALLOC_ERRFILE = %s\n",dbg_merrfile);
                break;
#endif /* MALLOC_WARN */
		default:
				Syntax_Error();
		}
	}
}

/*===================================================== CHARTOMODE */
PRIVATE
int     Char_To_Mode (reply,ch)
/*		~~~~~~~~~~
 *	Transates a character into a logical mode selector.
 *=================================================================*/
int   *reply;
char    ch;
{
    int     value;

    switch (toupper(ch)) {
	case 'O':   value = OFF;
				break;
	case 'S':   value = STP;
				break;
	case 'T':   value = TRC;
				break;
	case 'A':   value = ALL;
				break;
	default :   dbg_print (" INVALID MODE\n");
				value = ERR;
				break;
	}
    if (value != ERR)
        *reply = value;
    return (value);
}

/*================================================= SYNTAX_ERROR */
PRIVATE
void    Syntax_Error()
/*		~~~~~~~~~~~~
 *	Generate a syntax error message on the debugging channel
 *================================================================*/
{
    dbg_print ( " SYNTAX ERROR\n");
}

/*=================================================== OUT_OF_RANGE */
PRIVATE
void    Out_Of_Range()
/*		~~~~~~~~~~~~
 *	Generate a Out-of-Range error message on the debugging channel
 *=================================================================*/
{
    dbg_print (" OUT OF RANGE\n");
}

/*===================================================== LIST_WATCH */
PRIVATE
void    List_Watch()
/*		~~~~~~~~~~
 *	List the current watch variables
 *================================================================*/
{
    int     i,count;
    char    pntadr[10];

    dbg_print ("WATCH POINTS:   (check mode %s)\n",Mode_String(MonitorMode));
    for (i=0 , count=0 ; i<WATCH_LIST ; i++) {
        if (WatchLocs[i])    {
            dbg_print("%d.  %s, Value: $%08.8lx\n",
                        (i+1),
						Addr_Display(pntadr,(long)WatchLocs[i]),
						(long)WatchOld[i]);
            count++;
		}
	}
    Print_None (count);
}

/*====================================================== LIST_STOP */
PRIVATE
void    List_Stop()
/*		~~~~~~~~~
 *	Print the labels that will cause a stop
 *================================================================*/
{
    List_List("STOP",StopSkip,StopList);
}

/*===================================================== LIST_TRACE */
PRIVATE
void    List_Trace()
/*		~~~~~~~~~~
 *	Print the labels that will cause a trace
 *================================================================*/
{
    List_List ("TRACE",TraceSkip,TraceList);
}


/*====================================================== LIST_LIST */
PRIVATE
void    List_List (type,skipcount,list)
/*		~~~~~~~~~
 *	Print the labels that will cause a stop or list
 *================================================================*/
char    *type;
int     skipcount;
lst     list;
{
    int     i,count;

    dbg_print ("%s POINTS:  (skip count=%d)\n",type,skipcount);
    for (i=0 , count=0 ; i < STOP_LIST ; i++)   {
        if (list[i][0])       {
            dbg_print ("     %d %s\n", (i+1) , list[i]);
            count++;
		}
	}
    Print_None(count);
}


/*===================================================== IN_LIST */
PRIVATE
int     In_List(list,str)
/*		~~~~~~~
 *	Compare an input string against all entries in the stop
 *	list.  Return true if match found, false otherwise.
 *================================================================*/
lst     list;
char    *str;
{
    int     i;

    for (i=0 ; i < STOP_LIST ; i++) {
        if ((list[i][0] == '*')
        ||  (Pattern_Match(str,list[i])))
            return (YES);
	}
    return (NO);
}


/*================================================== PATTERN_MATCH */
PRIVATE
int     Pattern_Match (str1, str2)
/*		~~~~~~~~~~~~
 *	Compare 'str1' to string in 'str2'  performing limited
 *	pattern matching.  Specifically, '?' in 'str2' matches
 *	any character (or end of string) in 'str1'.  Returns
 *	zero on no match, non-zero otherwise.
 *================================================================*/
char    *str1, *str2;
{
    while (*str1 && *str2 && ((*str1 == *str2) || (*str2 == '?' ))) {
        str1++;
        str2++;
	}
    if (*str2 == '*')
        return (YES);
    if (!(*str1))  {
        while (*str2 == '?')  {
            str2++;
		}
	}
    if (*str1 || *str2)
        return (NO);
    return (YES);
}

/*======================================================= CMDPARSE */
PRIVATE
int     Cmd_Parse (arg1, arg2, arg1s, arg2s)
/*		~~~~~~~~
 *	Get a command from the keyboard, parse it into its
 *	components. Return the logical token indentifying the
 *	command portion.  Puts the arguments into 'arg1', 'arg2'
 *	and 'arg2s'.
 *================================================================*/
int     *arg1, *arg2;
char    *arg1s, *arg2s;
{
    char    cmdbuf[MAX_LINE];
    int     i;

    *cmdbuf = '\0';
    dbg_input (cmdbuf,sizeof(cmdbuf));
    for (i=0 ; cmdbuf[i] ; i++)
        cmdbuf[i] = toupper(cmdbuf[i]);
    if (CmdOutFile != LogFile)
        (void) fprintf (LogFile,"%s\n",cmdbuf);
    *arg1s = *arg2s = '\0';
    (void) sscanf(cmdbuf,"%*s%s%s", arg1s, arg2s);
    Numeric_Args (arg1s,arg1);
    Numeric_Args (arg2s,arg2);
    return (Cmd_Type(cmdbuf));
}

/*================================================== NUMERIC_ARGS */
PRIVATE
void    Numeric_Args (string,numeric)
/*		~~~~~~~~~~~~
 *	This routine is used to convert arguments to
 *	numeric values if at all possible
 *===============================================================*/
char    *string;
int     *numeric;
{

    *numeric = -1;
    if (!Addr_Parse(string,numeric,"C+",CodeStart))
        if (!Addr_Parse(string,numeric,"D+",DataStart))
            (void) Numeric_Parse (string,numeric);
    return;
}

/*================================================== ADDRESS_PARSE */
PRIVATE
int     Addr_Parse(string,value,type,adjust)
/*		~~~~~~~~~~
 *	This routine allows for values to be input relative
 *	to the base address of either the data or the code
 *	areas.
 *================================================================*/
char    *string;
int     *value;
char    *type;
char    *adjust;
{
    if (!strncmp(string,type,2)) {
        if (Numeric_Parse ((char *)((int)string+2),value)) {
            *value += (long)adjust;
            return (1);
		}
	}
    return (0);
}

/*================================================== NUMERIC_PARSE */
PRIVATE
int     Numeric_Parse(string,value)
/*		~~~~~~~~~~~~~
 *	This routine allows numbers to be preceded by the
 *	$ symbol to indicate Hex, or the ` symbol to indicate
 *	decimal.  If neither is present, then Hex is assumed.
 *================================================================*/
char    *string;
int     *value;
{
    int     reply;

    if (!strncmp(string,"$",1))
        reply = sscanf (string+1,"%x",(unsigned *)value);
    else
       if (!strncmp(string,"`",1))
            reply = sscanf (string+1,"%d",value);
       else
            reply = sscanf (string,"%x",(unsigned *)value);
    return (reply);
}

/* ======================================================== CMD_TYPE */
PRIVATE
int     Cmd_Type (str)
/*		~~~~~~~~
 *	Look up the command word and translate into a logical
 *	token value.
 *=================================================================*/
char    *str;
{
static  struct  CMDENTRY  {
            char    *cmdtext;
            int     cmdtoken;
            }
            cmdtable[]=  {
                {"",NOENTRY},                {"ADDRESS",ADDRESS},
                {"AMODE",AMODE},             {"CLS",CLEAR},
                {"CONTINUE",CONT},           {"CRC",CRC},
                {"CMODE",CMODE},             {"DUMP",DUMP},
                {"GO",CONT},                 {"GRANULARITY",GRAN},
                {"HELP",HELP},               {"LOCAL",LOCAL},
                {"LEVEL",LEVEL},             {"LOG",LOG},
                {"POKE",POKE},               {"QUIT",QUIT},
                {"SHOW",SHOW},               {"STOP",STOP},
                {"SSKIP",SSKIP},             {"RETRACE",RETRACE},
                {"SNAP",SNAP},               {"STACK",STACK},
                {"SMODE",SMODE},             {"TRACE",TRACE},
                {"TMODE",TMODE},             {"TSKIP",TSKIP},
                {"WATCH",WATCH},             {"WMODE",WMODE},
                {"ERRNO",ERRNO},  
                {"MWARN",MWARN},             {"MFATAL",MFATAL},
                {"MCHECK",MCHECK},           {"MFILE",MFILE},
                {NULL,ERR}
                };

    int     i,n;
    char    *p;

    if ( (p=strchr(str,' ')) == NULL)
        n = strlen(str);
    else
        n = (int)(p - str);

    for (i=0 ; cmdtable[i].cmdtext ; i++) {
        if (!strncmp(str,cmdtable[i].cmdtext,n))
            break;
    }

    return (cmdtable[i].cmdtoken);
}


/*===================================================== STATUS_DISPLAY */
PRIVATE  
void    Status_Display (label,level)
/*		~~~~~~~~~~~~~~
 *	Print the current debugger control status
 *---------------------------------------------------------------------*/
char    *label;
int     level;
{
    Stopped_At (label,level);
    List_Stop();
    List_Trace();
    Trace_Controls();
    List_Watch();
    Mode_Details();
    if (CrcMode != OFF)
        CRC_Details();
    Addr_Details();
    dbg_print ("\n");
}


/*====================================================== ADDR_DETAILS */
PRIVATE  
void    Addr_Details()
/*		~~~~~~~~~~~~
 *	Display the addresses of the code and data areas
 *	for this job.  The format of the display will be
 *	determined by the 'AddressMode' setting.
 *--------------------------------------------------------------------*/
{
    Addr_Line ("CODE",CodeStart,CodeTop);
    Addr_Line ("DATA",DataStart,DataTop);
    return;
}


/*============================================================ ADDR_LINE */
PRIVATE  
void    Addr_Line(type,base,top)
/*		~~~~~~~~~
 *-----------------------------------------------------------------------*/
char    *type;
char    *base, *top;
{
    dbg_print ("%s AREA:  base $%06x,  ",type,base);
    if (AddressMode)
        dbg_print ("size $%05x\n",top-base);
    else
        dbg_print ("top $%06x\n",top);
    return;
}


/*============================================================== ADDRESS */
PRIVATE  
char   *Addr_Display (to,value)
/*		~~~~~~~~~~~~~~~
 *	Take a value and convert it into a suitable format 
 *	for display of an address.
 *-----------------------------------------------------------------------*/
char   *to;
long    value;
{
    if (AddressMode) {
        if ((value >= (int)DataStart) && (value < (int)DataTop))
            (void) sprintf (to,"D+%05lx",(unsigned long)value-(int)DataStart);
        else
            if ((value >= (int)CodeStart) && (value < (int)CodeTop))
                (void) sprintf (to,"C+%05lx",(unsigned long)value-(int)CodeStart);
            else
                (void) sprintf (to,"$%06lx",(unsigned long)value);
    }
    else
        (void) sprintf (to,"$%06lx",(unsigned long)value);

    return (to);
}


/*======================================================== TRACE_CONTROLS */
PRIVATE  
void    Trace_Controls()
/*		~~~~~~~~~~~~~~
 *------------------------------------------------------------------------*/
{
    dbg_print ("TRACE CONTROLS: granularity $%02x, level $%02x, screen %s\n",
                        Granularity, dbg_level,
                        TraceMode ? "ON" : "OFF");
}


/*========================================================= MODE_DETAILS */
PRIVATE  
void    Mode_Details()
/*		~~~~~~~~~~~~
 *-----------------------------------------------------------------------*/
{
    dbg_print ("MODES: stack %s, crc %s, address %s\n",
                        Mode_String (StackMode),
                        Mode_String (CrcMode),
                        AddressMode ? "ON" : "OFF");
}

/*=========================================================== MODE_STRING */
PRIVATE  
char    *Mode_String (md)
/*		~~~~~~~~~~~~
 *-----------------------------------------------------------------------*/
int md;
{
    char    *reply;

    switch (md) {
        case OFF:   reply = "OFF";      break;
        case STP:   reply = "STOP";     break;
        case TRC:   reply = "TRACE";    break;
        case ALL:   reply = "ALL";      break;
        default:    reply = "???";
	}
    return (reply);
}


/*================================================================= HELP */
PRIVATE  
void    Help()
/*		~~~~
 *	Generate a Command summary.
 *-----------------------------------------------------------------------*/
{
    static   char    *prompts[] = {
            "\nDBG COMMANDS:",
            "  LOG filename         WATCH n address",
            "  CONTINUE (or GO)     WMODE mode",
            "  QUIT                 STACK",
            "  SHOW                 SMODE mode",
            "  STOP  n pattern      LOCAL frames",
            "  SSKIP n              DUMP  address length",
            "  TRACE n pattern      CRC   address length",
            "  TSKIP n              CMODE mode",
            "  TMODE mode           POKE  address,value",
            "  RETRACE              ADDRESS",
            "  LEVEL tracelevel     AMODE",
            "  GRANULARITY n        CLS",
            "  ERRNO                HELP",
            "  MWARN [level]        MCHECK",
            "  MFATAL [level]       MFILE [filename]",
            NULL
            };

    int     x;

    for (x = 0 ; prompts[x] ; x++)
        dbg_print ("%s\n",prompts[x]);
    dbg_print ("\n");
}


/*=============================================================== CRC_16 */
PRIVATE  
int     CRC_16 ()
/*		~~~~~~
 *	Compute a cyclic redundancy check word for the memory
 *	block beginning at 'start' and continuing for 'len'
 *	bytes.  Uses CCITT crc 16 algorithm.
 *----------------------------------------------------------------------*/
{
    char    *start;
    int     crc,len;

    crc   = 0;
    start = CrcStart;
    len   = CrcLength;
    while  (len--)
        crc = CRC_Update(crc, *start++);
    return (CRC_Update (CRC_Update(crc,'\0'),'\0'));
}


/*========================================================= CRC_Update */
PRIVATE  
int     CRC_Update (crc, c)
/*		~~~~~~~~~~
 *	The shift register simulation
 *---------------------------------------------------------------------*/
int     crc;
char    c;
{
    long    x;
    int     i;

    x = ((long) crc << 8) + c;
    for (i = 0 ; i < 8 ; i++) {
        x = x << 1;
        if ( x & 0x01000000)
            x = x ^ 0x01102100;
	}
    return ((x & 0x00ffff00) >> 8);
}


/*========================================================== CRC_DETAILS */
PRIVATE  
void    CRC_Details()
/*		~~~~~~~~~~~
 *	Display the current CRC details
 *-----------------------------------------------------------------------*/
{
    char    crcadr[10];

    dbg_print ("CRC:  %s $%04x Value=$%04x\n",
                        Addr_Display(crcadr,(long)CrcStart),
                        CrcLength,
                        CrcValue);
    return;
}


/*=========================================================== MODE_CHECKS */
PRIVATE  
void    Mode_Checks(reply,mode)
/*		~~~~~~~~~~~
 *	Perform checks that are dependent on the mode if it
 *	is appropriate.  If one of the checks fails, then
 *	change the reply field, but otherwise leave it alone.
 ------------------------------------------------------------------------*/
int     *reply;
int     mode;
{

    if (StackMode == mode)
        if (Stack_Dump(SILENT) != OK)
           *reply = ON;
    if (MonitorMode == mode)
        if (Watch()!=OK)
           *reply = ON;
    if (CrcMode == mode)
        if (CRC_16() != CrcValue)
           *reply = ON;
    return;
}


/*========================================================= STACK_DUMP */
PRIVATE  
int     Stack_Dump (mode)
/*		~~~~~~~~~~
 *	Check the stack.
 *
 *	If the parameter is set appropriately, it also
 *	produce an analysed print of the frame pointers and
 *	return addresses.
 *---------------------------------------------------------------------*/
int     mode;
{
    int     rval, i;
    int     *framepointer, *retadr, *framestart;
    char    where[10], from[10], start[10];

    if (mode == VERBOSE)      {
        dbg_print ("         Frame    Frame   Resume\n");
        dbg_print (" Level   Start   Pointer  address\n");
	}

    for ( i=1, framepointer=DbgFrame ; framepointer < StackTop ; i++) {
        retadr = framepointer+1;
        framestart = framepointer+2;
        if ((rval=Advance_Frame (&framepointer)) != OK)
             break;

        if (mode == VERBOSE) {
            dbg_print ("   %2d   %s  %s  %s\n", i,
                     Addr_Display(start,(long)framestart),
                     Addr_Display(where, (long)framepointer),
                     Addr_Display(from,(long)*retadr));
		}
	}
    return (rval);
}

/*========================================================== ADVANCE_FRAME */
PRIVATE  
int     Advance_Frame (framepointer)
/*		~~~~~~~~~~~~~
 *	Advance the framepointer along the stack.
 *
 * 	Before going a level deeper, that level's framepointer
 *	is checked for sanity.  That is, it is checked to ensure
 *	that it points at a space within the current stack.
 *
 *	The return address is also checked to ensure that it is
 *	within the code space for the program.
 *------------------------------------------------------------------------*/
int     **framepointer;
{
    int     *temp, *retadr;
    char    from[10], to[10];

    /*
    -------------------------
    Advance the frame pointer
    -------------------------
    */
    temp = *framepointer;     /* avoid ** operations  */
    temp = (int *) *temp;     /* advance one link     */
    retadr =(int *) *temp+1;  /* get return address   */
    /*
    ----------------------------------
    Check Frame Pointer is larger than
    last one, and still in stack
    ----------------------------------
    */
    if ((temp > StackTop)
    ||  (temp < *framepointer))   {
        dbg_print ("STACK ERROR: Bad frame pointer at %s = %s\n",
                    Addr_Display(from,(long)*framepointer),
					Addr_Display(to,(long)*temp));
        return (NOT_OK);
        }
    /*
    ----------------------------------
    Check Return address is within
    the Code space of the program
    ----------------------------------
    */
    if ((*retadr < (int)CodeStart)
    ||  (*retadr > (int)CodeTop))  {
        dbg_print ("STACK ERROR: Bad return address at %s = %s\n",
                    Addr_Display(from,(long)retadr),
					Addr_Display(to,(long)*retadr));
        return (NOT_OK);
        }

    *framepointer = temp;
    return (OK);
    }

/*=============================================================== FMT_HEX */
PRIVATE  
void    Fmt_Hex(start,len)
/*		~~~~~~~
 *------------------------------------------------------------------------*/
unsigned char *start;
int           len;
{
    int     i,k;
    char    *temp;
    char    at[10], buf[20];

    for (temp = buf, k = 0, i = 0 ; i < len ; i++, k++, start++, temp++) {
        if (k == 16) {
            k = 0;
            *temp = '\0';
            dbg_print (" %s\n",buf);
		}
        if (k == 0)  {
            dbg_print ("%s",Addr_Display(at, (long)start));
            temp = buf; /* reset ascii output pointer */
		}
        dbg_print ("%c%02x",((k % 4) && k) ? ' ' : '-', *start);
        *temp = (isascii(*start)&&(!iscntrl(*start))) ? *start : '.';
	}
    *temp = '\0';
    if (i > 16)
       for ( ; k < 16 ; k++)
           dbg_print ("   ");
    dbg_print (" %s\n",buf);
}

/*========================================================== LOCAL_DUMP */
PRIVATE  
void    Local_Dump(levels)
/*		~~~~~~~~~~
 *	Dump 'level' frames of local variables and parameters
 *-----------------------------------------------------------------------*/
int     levels;
{
    int     *framepointer;  /* points to current frame pointer    */
    int     *endpointer;    /* next frame pointer.  Space between */
                            /* (-8) is locals and parameters      */
    int     l,localsize;

    for ( l=0, endpointer=framepointer = DbgFrame ;
             (framepointer <= StackTop) && (l++ < levels) ;  ) {
        if (framepointer == StackTop) {
			dbg_print ("     Top of stack\n");
            break;
		}
        if (Advance_Frame(&endpointer) != OK)  break;
        dbg_print ("LOCALS for level %d\n",l);
        localsize = ((int)endpointer - (int)framepointer - 8);
        if (localsize) {
            Fmt_Hex((unsigned char *) ((int)framepointer+8),localsize);
		} else {
            Print_None(0);
		}
        framepointer = endpointer;
	}
    return;
}


/*======================================================== PRINT_NONE */
PRIVATE  
void    Print_None (value)
/*		~~~~~~~~~~
 *--------------------------------------------------------------------*/
int value;
{
    if (!value) {
         dbg_print ("     None\n");
	}
}


/*========================================================= DBG_INPUT */
PRIVATE  
void    dbg_input (buffer,bufsize)
/*      ~~~~~~~~~
 *  Read input from command channel
 *--------------------------------------------------------------------*/
char    *buffer;
int     bufsize;
    {
#ifdef QDOS
    short   linelen = 0;
    char    *bufadd = buffer;
#endif

    dbg_print ("DBG>> ");
#ifdef QDOS
    (void) sd_setin (CmdChan,-1,7);
    buffer[0] = '\0';   
    (void) sd_cure (CmdChan,-1);
    (void) io_edlin (CmdChan,-1,&bufadd,(short)bufsize,0,&linelen);
    buffer[linelen-1] = '\0';       /* remove terminating character */
    (void) sd_curs (CmdChan,-1);
    (void) sd_setin (CmdChan,-1, 4);
    (void) fprintf (CmdOutFile,"\n");
#else
    buffer[bufsize - 1] = '\0';
    (void) fgets(buffer,bufsize,CmdInFile);
    if ((strlen(buffer)) && (buffer[strlen(buffer)-1] == '\n')) {
        buffer[strlen(buffer)-1] = '\0';
    }
#endif
    }


/*============================================================= STOPPED_AT */
PRIVATE
void    Stopped_At (where,level)
/*      ~~~~~~~~~~
 *  Give message relating to stop details
---------------------------------------------------------------------------*/ 
char   *where;
int    level;
{
    dbg_print ("\nSTOPPED AT: %4.4x '%s'\n", level, where);
}


/*============================================================= DBG_VFPRINT */
PUBLIC
void    dbg_vfprint (formatstring, paramptr)
/*      ~~~~~~~~~~~
 *  This routine is called for printing with DBG.  It allows output 
 *  to be sent to all DBG output streams.
 *
 *  This routine is used when you would otherwise use vfprintf.
 *------------------------------------------------------------------------*/
char   *formatstring;
va_list paramptr;
{
    int saverrno, savoserr;

    saverrno = errno;
    savoserr = _oserr;
#ifdef QDOS
    (void) sd_setsz (CmdChan,-1,0,0);
#endif
    (void) vfprintf (LogFile,formatstring, paramptr);
    (void) fflush (LogFile);
    if (CmdOutFile != LogFile)  {
        (void) vfprintf (CmdOutFile,formatstring,paramptr);
        (void) fflush (CmdOutFile);
    }
    errno = saverrno;
    _oserr = savoserr;
}


#ifdef __STDC__
/*============================================================ DBG_PRINT */
PUBLIC
void    dbg_print (char *formatstring, ...)
/*      ~~~~~~~~~
 *  This routine is called for printing with DBG.  It allows output 
 *  to be sent to all DBG output streams.
 *
 *  This routine is set up to take a variable number of parameters
 *  along the 'printf' style.
 *------------------------------------------------------------------------*/
#else
void    dbg_print (formatstring)
char * formatstring;
#endif
{
    va_list   params;

    va_start (params,formatstring);
    dbg_vfprint (formatstring, params);
    va_end(params);
}



/*=========================================================== DBG_WRITE */
PUBLIC
void    dbg_write (buffer, length)
/*      ~~~~~~~~~
 *  This routine is called for printing with DBG.  It allows output 
 *  to be sent to the DBG log stream.  It is not sent to the command
 *  stream as it is not thought that this type of output will be wanted
 *  unless the command stream is also the log stream.
 *
 *  It allows a specified amount of data to be written rather like the
 *  'fwrite' library call.
 *----------------------------------------------------------------------*/
char   *buffer;      
int    length;
{
    int     saverrno, savoserr;

    saverrno = errno;
    savoserr = _oserr;
#ifdef QDOS
    (void) sd_setsz (CmdChan,-1,0,0);
#endif
    (void) fwrite (buffer, (size_t)length,(size_t)1,LogFile);
    (void) fflush (LogFile);
    errno = saverrno;
    _oserr = savoserr;
}

#ifndef QDOS
/*======================================================== SET_SIGNALS */
PRIVATE
void	Set_Signals ()
/*      ~~~~~~~~~~~
 *  This routine sets the signals that DEBUG can catch.
 *---------------------------------------------------------------------*/
{
    (void) signal (SIGINT, Catch_Signal);
    return;
}


/*====================================================== CATCH_SIGNAL */
PRIVATE
void	Catch_Signal ()
/*      ~~~~~~~~~~~~
 *  This routine is entered if one of the signals that has been defined
 *  as being caught happens.
 *--------------------------------------------------------------------*/
{
    static char stopmsg[] = "**** SIGNAL ****";
    if (! Initialised) dbg_init();
    DbgErrno = errno;
    DbgOserr = _oserr;

    Stopped_At(stopmsg,0);
    Commands (stopmsg,0);
    Set_Signals();
    errno = DbgErrno;
    _oserr = DbgOserr;
    return;
}
#endif

