/***************************************************************************
 *                                                                         *
 *   GALDEBUG.C                                                            *
 *                                                                         *
 *   Copyright (c) 1995-1998 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   Collection of debugging facilities for Worldgroup baseline            *
 *                                                                         *
 *   This module should always be compiled in DEBUG mode.                  *
 *                                                                         *
 *                                  - R. Stein & D. Pitchford 1/28/95      *
 *                    NT Version    - Richard Skurnick        4/12/96      *
 *                                                                         *
 ***************************************************************************/

#if defined(GCWINNT)
#include <windows.h>
#endif
#include "gcomm.h"
#include "majorbbs.h"
#include "remote.h"
#include "phasedbg.h"
#include "galdebug.h"

#define FILREV "$Revision: 19 $"

#ifndef DEBUG
#error GALDEBUG.C should always be compiled in DEBUG mode
#endif // DEBUG define

#ifdef GCDOS
#if (__TURBOC__ >= 0x0400)
#define STKPT1 22
#define STKPT2 36
#else
#define STKPT1 19
#define STKPT2 33
#endif //__TURBOC__
#endif // GCDOS

VOID EXPORT init__galdebug(VOID);
VOID EXPORT initwc__galdebug(VOID);
VOID begdbg(CHAR *name,LONG num);
VOID enddbg(CHAR *name,LONG num);
VOID rtidbg(VOID);
VOID dbgfin(VOID);
VOID rtidbg(VOID);
VOID ckmodl(VOID);
VOID vdagpr(CHAR *rfname);
VOID heapchk(VOID);
INT selchk(UINT sel);
GBOOL dmyinp(VOID);
VOID locinf(INT *loc,INT x,INT y);
VOID phsinf(INT x,INT y);
VOID sflinf(INT x,INT y);
VOID imrinf(INT x,INT y);
VOID splacem(INT x,INT y,INT *words,INT gnum,INT num);
CHAR *hexa(INT u);
CHAR *hexc(CHAR c);

#ifdef GCWINNT

VOID
SendPhases(VOID);                  /* send phase info to separate process  */

VOID
mcuHook(VOID);                     /* cleanup hook to disable reboot       */

CHAR *                             /*   returns pointer to end of buffer   */
csprintf(                          /* sprintf with updated output pointer  */
CHAR *buf,                         /*   buffer to print into               */
const CHAR *fmt,                   /*   format string                      */
...);                              /*   variable arguments                 */

VOID splacen(CHAR *buf,INT x,CHAR *stg,INT limit);
VOID splace(CHAR *buf,INT x,CHAR *stg);

#else

VOID splacen(INT x,INT y,CHAR *stg,INT limit);
VOID splace(INT x,INT y,CHAR *stg);

#endif // GCWINNT

static HMCVFILE dbgmb;             /*---- options in GALDEBUG.MSG ----     */
static GBOOL dbgxphs;              /* phase information display?           */

#ifdef GCDOS

static GBOOL dbgxadr;              /* display execution address?           */
static INT dbgxslw;                /* slowness factor                      */
static GBOOL dbgximr;              /* IMR contents display?                */
static GBOOL dbgmod;               /* check module array?                  */
static GBOOL dbgheap;              /* check for heap corruption?           */
static GBOOL dbgvda;               /* record VDA in GALGPVDA.NNN?          */

#else

#define DM_REBOOT   (WM_USER+1)    /* debug msg: enable/disable reboot     */
static GBOOL dbgSepProc;           /* display phases in separate window?   */
HANDLE hDisplayWindow;             /* hWnd to phase display window         */
VOID (*oldmcurou)(VOID);           /* old module00 MCU handler             */

#endif // GCDOS

VOID (*oldbph)(CHAR *name,LONG num);    /* record old BEG_PHASE vector     */
VOID (*oldeph)(CHAR *name,LONG num);    /* record old END_PHASE vector     */
static CHAR hexs[]="0123456789ABCDEF";  /* for reentrant hex formatting    */
INT drib=0;                        /* to implement display slowness        */

#define RPTFILE "GALHEAP.OUT"      /* file for HEAP ERROR reports          */
#define BINFILE "GALHEAP.*"        /* file for binary copy of bad heap seg */
#define GPVDAFILE "GALGPVDA.*"     /* file for VDA contents                */
CHAR binfile[20];                  /* actual (unique) name of binary file  */
FILE *rfp,*bfp;                    /* file handles for above               */
INT crash=0;                       /* detect double heap crash             */

VOID (*oldfin)(VOID);              /* old value of module[0]->finrou       */
VOID (*oldgpr)(CHAR *);            /* old value of (*hdlgpr)()             */

#ifdef GCWINNT
VOID (*oldprt)(INT x,INT y);       /* old value of (*prtPhase)()           */
#endif // GCWINNT

extern UINT __base;                /* first selector of heap               */
extern UINT __top;                 /* last selector of heap (inclusive)    */
extern UINT __hincr;               /* increment between heap selectors     */

struct freehdr {                   /* free block INTrusion INTo data area  */
     UINT prev;                    /* previous free block \ doubly linked  */
     UINT next;                    /* next free block     / free list      */
};

union vblpart {                    /* variable portion of heap block       */
     struct freehdr f;             /* free-list info when block is free    */
     CHAR data[1];                 /* usable data area of allocated block  */
};

struct heapblock {                 /* block of memory in the heap          */
     UINT size;                    /* even=free size, odd=allocated size+1 */
     UINT prev_real;               /* singly linked list of all blocks     */
     union vblpart x;              /* variable part of heap block          */
};

#define MAXBLKS 8192               /* max possible heap blocks per 64K seg */

struct module dmymodule={          /* dummy module (NEVER REGISTERED)      */
     "",                           /*    name used to refer to this module */
     NULL,                         /*    user logon supplemental routine   */
     dmyinp,                       /*    input routine if selected         */
     dfsthn,                       /*    status-input routine if selected  */
     NULL,                         /*    "injoth" routine for this module  */
     NULL,                         /*    user logoff supplemental routine  */
     NULL,                         /*    hangup (lost carrier) routine     */
     NULL,                         /*    midnight cleanup routine          */
     NULL,                         /*    delete-account routine            */
     NULL                          /*    finish-up (sys shutdown) routine  */
};

VOID EXPORT
initwc__galdebug(VOID)
{
     init__galdebug();
}

VOID EXPORT
init__galdebug(VOID)               /* debug initialization                 */
{
#if defined(GCWINNT)
     PROCESS_INFORMATION pi;
     STARTUPINFO si;
#endif

     dbgmb=opnmsg("galdebug.mcv");
     astmode=tokopt(DBGASTH,"IGNORE","REPORT","CRASH",NULL);
     if (astmode == ASTRPT) {
          astrptfil=stgopt(DBGASTF1);
     }
#ifndef GCWINNT
     dbgxadr=ynopt(DBGXADR);
     dbgxslw=numopt(DBGXSLW,1,100);
     dbgximr=ynopt(DBGXIMR);
     dbgmod=ynopt(DBGMOD);
     dbgvda=ynopt(DBGVDA);
#endif // GCWINNT
     dbgxphs=ynopt(DBGXPHS);
#ifdef GCWINNT
     if (dbgxphs) {
          oldprt=prtPhase;
          prtPhase=phsinf;
     }
#endif // GCWINNT
     oldbph=beg_phase;
     beg_phase=begdbg;
     oldeph=end_phase;
     end_phase=enddbg;
     oldfin=module[0]->finrou;
     module[0]->finrou=dbgfin;
#ifdef GCWINNT
     dbgSepProc=ynopt(DBGXPHP);
     if (dbgSepProc) {
          memset(&pi,0,sizeof(pi));
          memset(&si,0,sizeof(si));
          if ((hDisplayWindow=FindWindow("GALDBGD","Phase Transitions")) == 0
           && !CreateProcess(NULL,"galdbgd.exe",NULL,NULL,FALSE
                            ,DETACHED_PROCESS,NULL,NULL,&si,&pi)) {
               catastro("Unable to find or execute GALDBGD.EXE");
          }
          if (hDisplayWindow != 0) {
               SendMessage(hDisplayWindow,DM_REBOOT,TRUE,0);
          }

          /* hook module00 cleanup handler to turn off reboot */
          oldmcurou=module[0]->mcurou;
          module[0]->mcurou=mcuHook;
     }
#else
     if (dbgvda) {
          oldgpr=hdlgpr;
          hdlgpr=vdagpr;
     }
     dbgheap=!ynopt(DBGMEM) && ynopt(DBGHEAP);
     if (dbgheap) {
          if ((rfp=fopen(RPTFILE,FOPAA)) == NULL) {
               catastro("CANNOT APPEND TO "RPTFILE);
          }
          if ((bfp=fopen(uniqfn(strcpy(binfile,BINFILE)),FOPWB)) == NULL) {
               catastro("CANNOT CREATE %s",binfile);
          }
     }
     if (dbgxadr) {
          rtihdlr(rtidbg);
     }
#endif // GCWINNT
}

VOID
begdbg(                            /* debugging work at beginning of phase */
CHAR *name,
LONG num)
{
     (*oldbph)(name,num);
#if defined(GCWINNT)
     if (dbgSepProc) {
          SendPhases();
     }
#else
     if (dbgxadr) {
          if (dbgxphs) {
               phsinf(30,0);
          }
          if (dbgximr) {
               imrinf(78,0);
          }
     }
     if (dbgmod) {
          ckmodl();
     }
     if (dbgheap) {
          heapchk();
     }
#endif // GCWINNT
}

VOID
enddbg(                            /* debugging work at end of phase       */
CHAR *name,
LONG num)
{
     (*oldeph)(name,num);
#if defined(GCWINNT)
     if (dbgSepProc) {
          SendPhases();
     }
#else
     if (dbgxadr) {
          if (dbgxphs) {
               phsinf(30,0);
          }
          if (dbgximr) {
               imrinf(78,0);
          }
     }
     if (dbgmod) {
          ckmodl();
     }
     if (dbgheap) {
          heapchk();
     }
#endif // GCWINNT
}

#if defined(GCWINNT)

VOID
mcuHook(VOID)                      /* cleanup hook to disable reboot       */
{
     if (hDisplayWindow != 0) {
          SendMessage(hDisplayWindow,DM_REBOOT,FALSE,0);
     }
     (*oldmcurou)();
}

VOID
SendPhases(VOID)                   /* send phase info to separate process  */
{
     struct phasedbg *pdp;         /* current phase info                   */
     CHAR *pOut;                   /* output position in buffer            */
     COPYDATASTRUCT cd;            /* for passing info to other process    */
     INT i;                        /* phase index                          */
     CHAR buf[NUMPHS*100];         /* buffer for phase info string         */
                                   /* (phase desc shouldn't exceed 80 cols)*/

     /* check if output window is available */
     if (hDisplayWindow == 0) {
          hDisplayWindow=FindWindow("GALDBGD","Phase Transitions");
          if (hDisplayWindow == 0) {
               return;
          }
          SendMessage(hDisplayWindow,DM_REBOOT,TRUE,0);   /* enable reboot */
     }

     /* put all phase info into string buffer */
     *buf='\0';
     pOut=buf;
     for (i=NUMPHS-1 ; i >= 0 ; --i) {
          if ((pdp=pstphs(i)) == NULL) {
               continue;
          }
          pOut=csprintf(pOut,pdp->flags&PDBBEG ? "BEG %-24.24s" : "END %-24.24s"
                       ,pdp->name);
          pOut=csprintf(pOut,"0x%08lX",pdp->num);
          if (0 <= pdp->usrnum && pdp->usrnum < nterms) {
               pOut=csprintf(pOut," CH%02X%2d",channel[pdp->usrnum],pdp->usrcls);
               if ((pdp->usrcls > SUPIPG || pdp->usrcls == BBSPRV)
                && 0 <= pdp->usrsta && pdp->usrsta < nmods) {
                    pOut=csprintf(pOut," %-24.24s",module[pdp->usrsta]->descrp);
               }
               else {
                    pOut=csprintf(pOut," state=%6d            ",pdp->usrsta);
               }
               pOut=csprintf(pOut,"%6d",pdp->usrsbs);
          }
          else {
               pOut=csprintf(pOut," usrnum=%d",pdp->usrnum);
          }
          pOut=csprintf(pOut,"\r\n");
     }
     ASSERT(strlen(buf) < sizeof(buf));

     /* send phase info buffer to separate process */
     memset(&cd,0,sizeof(cd));
     cd.cbData=strlen(buf)+1;
     cd.lpData=buf;
     SendMessage(hDisplayWindow,WM_COPYDATA,0,(LPARAM)&cd);
}

CHAR *                             /*   returns pointer to end of buffer   */
csprintf(                          /* sprintf with updated output pointer  */
CHAR *buf,                         /*   buffer to print into               */
const CHAR *fmt,                   /*   format string                      */
...)                               /*   variable arguments                 */
{
     va_list ap;

     va_start(ap,fmt);
     vsprintf(buf,fmt,ap);
     va_end(ap);
     return(buf+strlen(buf));
}

#endif /* GCWINNT */

VOID
dbgfin(VOID)                       /* Intercepted module 0's finrou()      */
{
#if defined(GCWINNT)
     if (dbgSepProc && hDisplayWindow != 0) {
          SendMessage(hDisplayWindow,WM_CLOSE,0,0L);
     }
#else
     if (dbgheap) {
          dbgheap=0;               /* (can't allow any more heap cking!)   */
          if (rfp != NULL) {
               fclose(rfp);
               rfp=NULL;
          }
          if (bfp != NULL) {
               fclose(bfp);
               if (!crash) {
                    unlink(binfile);
               }
               bfp=NULL;
          }
     }
#endif // GCWINNT
     (*oldfin)();
}

#ifndef GCWINNT
VOID
rtidbg(VOID)                       /* display info at 18 Hz                */
{
     INT u=0;                      /* WARNING!  STACK POSITION CRITICAL    */

     locinf(&u,0,0);
     sflinf(0,1);
     if (dbgxphs) {
          phsinf(30,0);
     }
     if (dbgximr) {
          imrinf(78,0);
     }
}

VOID
ckmodl(VOID)                       /* check module structures              */
{
     INT st;
     struct module *mp;

     if (errcod >= 20) {
          return;
     }
     if (!goodptr(module)) {
          catastro("MODULE POINTER CORRUPTED: %04X:%04X",FP_SEG(module),
                                                         FP_OFF(module));
     }
     for (st=0 ; st < nmods ; st++) {
          mp=module[st];
          if (mp == NULL) {
               module[st]=&dmymodule;
               catastro("MODULE POINTER FOR STATE %d CORRUPTED: %04X:%04X %04X:%04X",
                        st,FP_SEG(module),FP_OFF(module),
                           FP_SEG(mp),FP_OFF(mp));
          }
     }
}

VOID
vdagpr(                            /* GP addendum, records VDA contents    */
CHAR *rfname)
{
     FILE *rfp,*bfp;
     CHAR bfname[20];
     UINT nactual;
     CHAR *vdanm1;

     if (vdarea != NULL) {
          uniqfn(strcpy(bfname,GPVDAFILE));
          if (goodstg(rfname) && (rfp=fopen(rfname,FOPAA)) != NULL) {
               vdanm1=vdaoff(nterms-1);
               fprintf(rfp,"GP Addendum:  VDA Report\n"
                           "vdaoff(0)=%04X:%04X  "
                           "vdaptr=%04X:%04X  "
                           "vdaoff(%d)=%04X:%04X\n"
                           "vdasiz=%d (decimal) %X (hex)\n",
                           FP_SEG(vdarea),FP_OFF(vdarea),
                           FP_SEG(vdaptr),FP_OFF(vdaptr),
                           nterms-1,FP_SEG(vdanm1),FP_OFF(vdanm1),
                           vdasiz,vdasiz);
               nactual=goodblk(vdaptr,vdasiz);
               fprintf(rfp,"%u-byte VDA contents stored in binary in \"%s\""
                           "\n\n",nactual,bfname);
               fclose(rfp);
               if (nactual > 0 && (bfp=fopen(bfname,FOPWB)) != NULL) {
                    fwrite(vdaptr,1,nactual,bfp);
                    fclose(bfp);
               }
          }
     }
     (*oldgpr)(rfname);
}

VOID
heapchk(VOID)                      /* check all heap selectors             */
{
     UINT sel,err;
     extern VOID sdllinfo(FILE *dst);
     struct phasedbg *pdb;

     if (crash) {
          return;
     }
     for (sel=__base ; sel <= __top ; sel+=__hincr) {
          if ((err=selchk(sel)) != 0) {
               crash=1;
               fprintf(rfp,"HEAP ERROR %d DETECTED %s %s\n",
                       err,ncdate(today()),nctime(now()));
               fprintf(rfp,"The 64K-segment %04X:XXXX was recorded in %s\n",
                           sel,binfile);
               fflush(rfp);
               rptphs(rfp);
               fflush(rfp);
               fprintf(rfp,"\nDLLs:");
               sdllinfo(rfp);
               fprintf(rfp,"\n\n\n");
               fclose(rfp);
               rfp=NULL;
               fwrite(MK_FP(sel,0),256,256,bfp);
               fclose(bfp);
               bfp=NULL;
               if (errcod < 20) {
                    if ((pdb=pstphs(0)) == NULL) {
                         catastro("HEAP ERROR %d (ref "RPTFILE" and %s)",
                                  err,binfile);
                    }
                    else {
                         catastro("HEAP ERROR %d, %s %s\n(ref "RPTFILE" and %s)",
                                  err,pdb->flags&PDBBEG ? "BEG" : "END",
                                  pdb->name,binfile);
                    }
               }
          }
     }
}

INT
selchk(                            /* check the 64K of this heap selector  */
UINT sel)
{
     /*   UINT LONG alcd=0L;        */
     /*   UINT LONG freed=0L;       */
     /*   UINT LONG infctr=0L;      */
     /*   UINT LONG fresiz;         */
     /*   UINT INT mainfree;        */
     /*   UINT INT curfree;         */
     /*   UINT INT off;             */
     /*   UINT INT elemp=0,elemn=0; */
     /*   struct heapblock *blk;        */
     /*                                 */
     /*   blk=MK_FP(sel,0);             */
     /*   do {                          */ /* first, we count up all memory*/
     /*        off=blk->prev_real;      */
     /*        blk=MK_FP(sel,off);      */
     /*        if (blk->size&1) {       */
     /*             alcd+=blk->size-1;  */      /* allocated block, counted*/
     /*        }                        */
     /*        else {                   */
     /*             freed+=blk->size;   */      /* free block, counted.    */
     /*             mainfree=off;       */
     /*        }                        */
     /*        if (infctr++ > MAXBLKS) {*/      /* infinite loop checker   */
     /*             return(1);          */
     /*        }                        */
     /*   } while (off != 0);           */
     /*   if (freed+alcd != 65536L) {   */   /* freed+alcd must == 64K     */
     /*        return(2);               */
     /*   }                             */
     /*   if (freed == 0L) {            */   /* return if no free space    */
     /*        return(0);               */
     /*   }                             */

     asm  push ds
     asm  mov  ds,sel
     asm  xor  si,si               /* DS:SI is blk                         */
     asm  xor  bx,bx               /* BX is alcd                           */
     asm  xor  dx,dx               /* DX is freed                          */
     asm  mov  cx,MAXBLKS          /* CX does infctr function              */
mloop:
     asm  mov  si,[si.(struct heapblock)prev_real]
     asm  mov  ax,[si.(struct heapblock)size]
     asm  test ax,1
     asm  jz   mfree               /* odd => block is allocated            */
     asm  dec  ax                  /* (strip LSBit)                        */
     asm  add  bx,ax               /* count allocated bytes                */
     asm  jc   mbustma             /* >= 65536 allocated bytes?            */
     asm  dec  cx
     asm  jz   mstuck              /* countdown                            */
     asm  test si,si
     asm  jnz  mloop
     asm  jmp  mboth

     mstuck:                       /* must be in an infinite loop          */
     asm  pop  ds
     return(1);

     mbustma:                      /* MAYBE busted: alcd >= 65536          */
     asm  test si,si               /* the only way this could be valid:    */
     asm  jnz  mbust               /*    must be at last block (offset 0)  */
     asm  test bx,bx
     asm  jnz  mbust               /*    alcd must be 65536                */
     asm  test dx,dx
     asm  jnz  mbust               /*    freed must be 0                   */
     asm  pop  ds
     return(0);                    /*    ok, no more work to do            */

     mbustmf:                      /* MAYBE busted: freed >= 65536         */
     asm  test si,si               /* the only way this could be valid:    */
     asm  jnz  mbust               /*    must be at last block (offset 0)  */
     asm  test bx,bx
     asm  jnz  mbust               /*    alcd must be 0                    */
     asm  test dx,dx
     asm  jnz  mbust               /*    freed must be exactly 65536       */
     asm  jmp  mdone               /*    ok, go check the free list        */

     mbust:                        /* BUST: freed & alloc'd regions don't  */
     asm  pop  ds                  /* add up to exactly 65536              */
     return(2);

     mfree:                        /* even => block is free                */
     asm  add  dx,ax               /* count free bytes                     */
     asm  jc   mbustmf             /* >= 65536 free bytes?                */
     asm  mov  di,si               /* DI is mainfree                       */
     asm  dec  cx
     asm  jz   mstuck              /* countdown                            */
     asm  test si,si
     asm  jnz  mloop

     mboth:
     asm  add  bx,dx               /* freed+alcd == 65536?                 */
     asm  jnc  mbust               /* not if no carry                      */
     asm  jnz  mbust               /* not if no overflow/wraparound to 0   */
     mdone:

     /*   infctr=fresiz=0L;             */
     /*   blk=MK_FP(sel,curfree=mainfree);*/
     /*   do {                          *//* now,  check the linked lists  */
     /*        curfree=blk->x.f.prev;   *//* of free memory. backwards 1st */
     /*        blk=MK_FP(sel,curfree);  */
     /*        elemp++;                 */     /* count it                 */
     /*        fresiz+=blk->size;       */
     /*        if (fresiz&1) {          */     /* if this was really a     */
     /*             return(3);          */     /* non-free block, bomb!    */
     /*        }                        */
     /*        if (infctr++ > MAXBLKS) {*/
     /*             return(4);          */
     /*        }                        */
     /*   } while (curfree != mainfree);*/
     /*   if (fresiz != freed) {        */     /* match totals from before */
     /*        return(5);               */
     /*   }                             */

     asm  mov  cx,MAXBLKS
     asm  mov  si,di               /* DS:SI still represents blk           */
     asm  xor  ax,ax               /* AX will count up elemp and down elemn*/
     asm  xor  bx,bx               /* BX is now fresiz                     */
ploop:
     asm  mov  si,[si.(struct heapblock)x.f.prev]
     asm  inc  ax                  /* count free blocks in prev chain      */
     asm  add  bx,[si.(struct heapblock)size]   /* add up total free again */
     asm  test bx,1                /* whoops, was it odd??                 */
     asm  jnz  palcd               /* all free blocks must have even sizes */
     asm  dec  cx
     asm  jz   pstuck              /* countdown                            */
     asm  cmp  si,di
     asm  jne  ploop               /* done when you get back to mainfree   */
     asm  jmp  pdone

     palcd:                        /* found "allocated" block in free chain*/
     asm  pop  ds
     return(3);

     pstuck:                       /* free-prev must be an infinite chain  */
     asm  pop  ds
     return(4);

     pgoof:                        /* free size totals don't match         */
     asm  pop  ds
     return(5);

     pdone:
     asm  cmp  bx,dx               /* fresiz == freed?                     */
     asm  jne  pgoof

     /*   infctr=fresiz=0L;             */
     /*   blk=MK_FP(sel,curfree=mainfree);*/
     /*   do {                          */ /* now we do other linked list  */
     /*        curfree=blk->x.f.next;   */ /* in the other direction       */
     /*        blk=MK_FP(sel,curfree);  */
     /*        elemn++;                 */
     /*        fresiz+=blk->size;       */
     /*        if (fresiz&1) {          */
     /*             return(6);          */
     /*        }                        */
     /*        if (infctr++ > MAXBLKS) {*/
     /*             return(7);          */
     /*        }                        */
     /*   } while (curfree != mainfree);*/
     /*   if (fresiz != freed) {        */
     /*        return(8);               */
     /*   }                             */
     /*   if (elemp != elemn) {         */
     /*        return(9);               */
     /*   }                             */

     asm  mov  cx,MAXBLKS
     asm  mov  si,di
     asm  xor  bx,bx               /* BX is fresiz again                   */
nloop:
     asm  mov  si,[si.(struct heapblock)x.f.next]
     asm  dec  ax                  /* uncount free blocks in next chain    */
     asm  add  bx,[si.(struct heapblock)size]   /* add up total free again */
     asm  test bx,1                /* whoops, was it odd??                 */
     asm  jnz  nalcd               /* all free blocks must have even sizes */
     asm  dec  cx
     asm  jz   nstuck              /* countdown                            */
     asm  cmp  si,di
     asm  jne  nloop               /* done when you get back to mainfree   */
     asm  jmp  ndone

     nalcd:                        /* found "allocated" block in free chain*/
     asm  pop  ds
     return(6);

     nstuck:                       /* free-next must be an infinite chain  */
     asm  pop  ds
     return(7);

     ngoof:                        /* free size totals don't match         */
     asm  pop  ds
     return(8);

     nmess:                        /* free block counts don't match        */
     asm  pop  ds
     return(9);

     ndone:
     asm  cmp  bx,dx               /* fresiz == freed?                     */
     asm  jne  ngoof
     asm  test ax,ax               /* elemp == elemn?                      */
     asm  jnz  nmess

     asm  pop  ds
     return(0);
}                                  /* all lived happily ever after         */
#endif // GCWINNT

GBOOL
dmyinp(VOID)                       /* dummy input handler                  */
{
     return(FALSE);
}

CHAR *
hexa(                              /* format a 4-digit hex string          */
INT u)                             /* word-size number                     */
{
     static CHAR buff[]="0000";

     buff[3]=hexs[u&0xF];
     buff[2]=hexs[(u>>4)&0xF];
     buff[1]=hexs[(u>>8)&0xF];
     buff[0]=hexs[(u>>12)&0xF];
     return(buff);
}

CHAR *
hexc(                              /* format a 2-digit hex string          */
CHAR c)                            /* byte-size number                     */
{
     static CHAR buff[]="00";

     buff[1]=hexs[c&0xF];
     buff[0]=hexs[(c>>4)&0xF];
     return(buff);
}


/*---- interrupt level code starts here ----*/

#ifndef GCWINNT
VOID
locinf(                            /* 22-CHARacter wide location info      */
INT *loc,                          /* CRITICAL STACK POSITION              */
INT x,                             /* \ screen location (of spinner)       */
INT y)                             /* /                                    */
{
     static CHAR *items[]={"\\","ณ","/","ฤ"};
     static INT fly=0;
     static INT psel,poff;

     if (++drib < dbgxslw) {
          return;
     }
     drib=0;
     splace(x+0,y,items[fly=(fly+1)&3]);
     splace(x+2,y,hexa(psel=loc[STKPT1+1]));
     splace(x+6,y,":");
     splace(x+7,y,hexa(poff=loc[STKPT1]));
     splace(x+11,y,"ออ");
     if (psel == 0x001F && (poff == 0x0D33 || poff == 0x0D2B)) {
          splace(x+13,y,hexa(loc[STKPT2+1]));
          splace(x+17,y,":");
          splace(x+18,y,hexa(loc[STKPT2]));
     }
     else {
          splace(x+13,y,"อออออออออ");
     }
}

VOID
sflinf(                            /* 15-CHARacter wide source file & line */
INT x,                             /* \ screen location (left edge)        */
INT y)                             /* /                                    */
{
     if (sltrak != 0) {
          splacen(x,y,sftrak,10);
          splace(x+11,y,hexa(sltrak));
     }
}

VOID
imrinf(                            /* 2-CHARacter wide 8259 IMR contents   */
INT x,                             /* \ screen location (left edge)        */
INT y)                             /* /                                    */
{
     splace(x,y,hexc(inp(0x21)));
}

VOID
splacem(                           /* display a series of hex words        */
INT x,                             /* \ screen location (left edge)        */
INT y,                             /* /                                    */
INT *words,                        /* poINTer to words                     */
INT gnum,                          /* number readable without GP           */
INT num)                           /* total number to display (.... extra) */
{
     INT i;

     for (i=0 ; i < gnum ; i++) {
          splace(x+i*5,y," ");
          splace(1+x+i*5,y,hexa(words[i]));
     }
     for ( ; i < num ; i++) {
          splace(x+i*5,y," ....");
     }
     splace(x+num*5,y," ");
}
#endif // GCWINNT


#ifdef GCWINNT
VOID                                /* non-interrupt level for NT          */
phsinf(                            /* 47-CHARacter wide phase info         */
INT x,                             /* \ screen location (left edge)        */
INT y)                             /* /                                    */
{
     struct phasedbg *pdp;
     CHAR buf[160];

     if (oldprt != NULL) {         // there really shouldn't be anything else!
          (*oldprt)(x,y);
     }
     if ((pdp=pstphs(0)) != NULL) {
          scn2mem(buf,x*2+y*160,94);
          splace(buf,0,pdp->flags&PDBBEG ? "BEG " : "END ");
          splacen(buf,4,pdp->name,10);
          splace(buf,14," ");
          splace(buf,15,hexa((INT)(pdp->num>>16)));
          splace(buf,19,hexa((INT)pdp->num));
          if (0 <= pdp->usrnum && pdp->usrnum < nterms) {
               splace(buf,23," CH");
               splace(buf,26,hexc(channel[pdp->usrnum]));
               splace(buf,28," ");
               splace(buf,29,hexc(pdp->usrcls));
               if ((pdp->usrcls > SUPIPG || pdp->usrcls == BBSPRV)
                && 0 <= pdp->usrsta && pdp->usrsta < nmods) {
                    splace(buf,31," ");
                    splacen(buf,32,module[pdp->usrsta]->descrp,10);
               }
               else {
                    splace(buf,31," state=");
                    splace(buf,38,hexa(pdp->usrsta));
               }
               splace(buf,42," ");
               splace(buf,43,hexa(pdp->usrsbs));
          }
          else {
               splace(buf,23," usrnum=");
               splace(buf,31,hexa(pdp->usrnum));
               splace(buf,35,"            ");
          }
          mem2scn(buf,x*2+y*160,94);
     }
}

VOID
splacen(                           /* display a left-just space-filled stg */
CHAR *buf,                         /* buffer to print to                   */
INT x,                             /* \ screen location (left edge)        */
CHAR *stg,                         /* string                               */
INT limit)                         /* max number of CHARacters             */
{
     x*=2;
     while (limit-- > 0) {
          buf[x++]=(*stg != '\0' ? *stg++ : ' ');
          x++;
     }
}

VOID
splace(                            /* display a string                     */
CHAR *buf,                         /* buffer to print to                   */
INT x,                             /* \ screen location (left edge)        */
CHAR *stg)                         /* string                               */
{
     x*=2;
     while (*stg != '\0') {
          buf[x++]=*stg++;
          x++;
     }
}

#else

VOID
phsinf(                            /* 47-CHARacter wide phase info         */
INT x,                             /* \ screen location (left edge)        */
INT y)                             /* /                                    */
{
     struct phasedbg *pdp;

     if ((pdp=pstphs(0)) != NULL) {
          splace(x,y,pdp->flags&PDBBEG ? "BEG " : "END ");
          splacen(x+4,y,pdp->name,10);
          splace(x+14,y," ");
          splace(x+15,y,hexa((INT)(pdp->num>>16)));
          splace(x+19,y,hexa((INT)pdp->num));
          if (0 <= pdp->usrnum && pdp->usrnum < nterms) {
               splace(x+23,y," CH");
               splace(x+26,y,hexc(channel[pdp->usrnum]));
               splace(x+28,y," ");
               splace(x+29,y,hexc(pdp->usrcls));
               if ((pdp->usrcls > SUPIPG || pdp->usrcls == BBSPRV)
                && 0 <= pdp->usrsta && pdp->usrsta < nmods) {
                    splace(x+31,y," ");
                    splacen(x+32,y,module[pdp->usrsta]->descrp,10);
               }
               else {
                    splace(x+31,y," state=");
                    splace(x+38,y,hexa(pdp->usrsta));
               }
               splace(x+42,y," ");
               splace(x+43,y,hexa(pdp->usrsbs));
          }
          else {
               splace(x+23,y," usrnum=");
               splace(x+31,y,hexa(pdp->usrnum));
               splace(x+35,y,"            ");
          }
     }
}

VOID
splacen(                           /* display a left-just space-filled stg */
INT x,                             /* \ screen location (left edge)        */
INT y,                             /* /                                    */
CHAR *stg,                         /* string                               */
INT limit)                         /* max number of characters             */
{
     while (limit-- > 0) {
          scnputc(x++*2+y*160,(*stg != '\0' ? *stg++ : ' '));
     }
}

VOID
splace(                            /* display a string                     */
INT x,                             /* \ screen location (left edge)        */
INT y,                             /* /                                    */
CHAR *stg)                         /* string                               */
{
     while (*stg != '\0') {
          scnputc(x++*2+y*160,*stg++);
     }
}

#endif // GCWINNT
