/***************************************************************************
 *                                                                         *
 *   PROTSTUF.C                                                            *
 *                                                                         *
 *   Copyright (c) 1992-1997 GALACTICOMM, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   Protected mode initialization and handling routines                   *
 *    (formerly PLSTUFF.C)                                                 *
 *                                                                         *
 *                                    - Robert A. Rose 02/18/92            *
 *     (generalized to non-BBS app's) - Robert N. Stein 01/06/92           *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "protstuf.h"

#define FILREV "$Revision: 18 $"

GBOOL gphdlr=FALSE;                /* FALSE=report&abort, else silent/cont */
GBOOL gpboot=FALSE;                /* Cold Boot after GP?                  */
UINT gpcntr=0;                     /* counter of GP's (reset if recovery)  */
UINT gpslmt=2000;                  /* how many times to continue thru GP   */

#ifndef GCWINNT

#pragma inline

VOID interrupt FAR dzhandler(EXCEP_FRAME e);
VOID interrupt FAR sshandler(EXCEP_FRAME e);
VOID interrupt FAR sfhandler(EXCEP_FRAME e);
VOID interrupt FAR gphandler(EXCEP_FRAME e);
VOID interrupt FAR pfhandler(EXCEP_FRAME e);
VOID exchandler(EXCEP_FRAME e);
VOID dftdblflt(VOID);

CHAR *dstname="GALEXCEP.OUT";      /* destination for GP dumps             */
FILE *gpdst;                       /* File handle for GP dumps             */

#define MAXSTP 20                  /* max number of steps in stack walk    */

INT codes[MAXSTP];                 /* code segments of stack walk entries  */
INT offs[MAXSTP];                  /* code offsets of stack walk entries   */
INT nsteps=0;                      /* number of stack walk steps in above  */
VOID *lastexcro=NULL;              /* address of last exception            */
GBOOL prtdnam=FALSE;               /* ok to print DLL name on screen       */
INT imr;                           /* save 8259's IMR, handler restores    */

#ifdef GCDOSP
static GBOOL ingph=FALSE;          /* are we in the middle of a GP event?  */
static GBOOL fullgp=FALSE;         /* show ALL selectors, etc?             */
static CHAR *gptitle=NULL;         /* remember the "title" fm setgphand()  */
#ifdef SSCRASH
INT excnums[]={0,1,12,13,14};      /* exception numbers monitored          */
#else
INT excnums[]={0,-1,12,13,14};     /* exception numbers monitored          */
#endif // SSCRASH
#define EXCDZ 0                    /* exc  0 index, divide-by-zero         */
#define EXCSS 1                    /* exc  1 index, single-step (et al)    */
#define EXCSF 2                    /* exc 12 index, stack fault            */
#define EXCGP 3                    /* exc 13 index, general protection     */
#define EXCPF 4                    /* exc 14 index, page fault             */
#define EXCNUM 5                   /* number of exceptions monitored       */
INT excidx;                        /* index of exception currently handling*/
PEHANDLER oldexc[EXCNUM];          /* old exception handler vector storage */
PEHANDLER newexc[EXCNUM]={         /* new exception handler vectors        */
     dzhandler,
     sshandler,
     sfhandler,
     gphandler,
     pfhandler,
};
CHAR *excnams[]={
     "DIVIDE-BY-ZERO",
     "DEBUG (SINGLE-STEP)",
     "STACK FAULT",
     "GP",
     "PAGE FAULT"
};
                                   /* long exception names (for GALEXCEP.OUT)*/
CHAR *excabbr[]={"DZ","SS","SF","GP","PF"};
                                   /* short exception names (for CRT)      */
UINT oldimsk;                      /* old 8259 interrupt mask              */

struct dllmod {                    /* note: structure also in MAJORBBS.H   */
     CHAR filnam[9];               /*   DLL filename (less extension)      */
     INT handle;                   /*   DLL handle (when loaded)           */
} *dlls=NULL;

INT maxdlls=20;               /* maximum # of dlls without reallocing      */
INT numdlls=0;                /* # of dlls in "dlls" array                 */
INT curdlls=0;                /* # of dlls currently loaded                */

VOID interrupt FAR gphandler(EXCEP_FRAME e);
VOID gpreport(EXCEP_FRAME e);
static VOID gprebt(VOID);
static VOID dftgpr(CHAR *rfname);
CHAR *seldetail(USHORT sel);
VOID plcatr(CHAR *filnam);
VOID (*hdlgpr)(CHAR *)=dftgpr;/* GP report interceptible vector            */
VOID (*oldcat)(CHAR *filnam); /* saved (*catrpt)() vector                  */
VOID (*gpdblflt)(VOID)=       /* GP double fault exit vector               */
      dftdblflt;              /* (default handler, exit(99))               */

VOID fndnear(FILE *dst,INT code,INT off);
VOID fndproc(INT code,INT off,LONG *rou1,CHAR *rnam1,LONG *rou2,CHAR *rnam2);
VOID shobits(FILE *dst,CHAR *ptr,INT len);
VOID shostack(FILE *dst,CHAR *ptr,INT rows,INT ibpval);
VOID sdllinfo(FILE *dst);

VOID
loaddll(                           /* load a .DLL file                     */
CHAR *modnam)
{
     INT rv;
     CHAR *ptr;
     HMODULE handle;
     struct dllmod *tmp;
     CHAR realnam[40+1];

     if ((rv=DosGetModHandle(modnam,&handle)) != 0) {
          if ((rv=DosLoadModule(realnam,40,modnam,&handle)) != 0) {
               if (rv == 2) {
#                   ifdef GCDOS
                         if (sameas(realnam,".\\MAJORBBS.DLL")) {
                              catastro("%s is for an older version of "
                                        SVR_NAME ".\nYou need to update "
                                        "it.",modnam);
                              return;
                         }
#                   endif
                    catastro("File missing: \"%s\" (while loading %s)",
                        realnam,modnam);
               }
               else {
                    plcatast(rv,spr("LOADDLL: Unable to load module %s!",
                                     modnam));
               }
          }
     }
     if (dlls == NULL) {
          dlls=(struct dllmod *)alcmem(sizeof(struct dllmod)*maxdlls);
     }
     if (numdlls+1 == maxdlls) {
          tmp=dlls;
          dlls=(struct dllmod *)alcmem(sizeof(struct dllmod)*(maxdlls+20));
          movmem(tmp,dlls,sizeof(struct dllmod)*maxdlls);
          free(tmp);
          maxdlls+=20;
     }
     if ((ptr=strchr(modnam,'.')) != NULL) {
          *ptr='\0';
     }
     stzcpy(dlls[numdlls].filnam,modnam,9);
     dlls[numdlls++].handle=handle;
     curdlls++;
}

VOID
initdlls(VOID)                     /* initialize all loaded DLLs           */
{
     CHAR buf[GCMAXPTH];
     HMODULE handle;
     INT i;
     GBOOL fnd;
     VOID (*iniadr)();

     for (i=0 ; i < numdlls ; i++) {
          handle=dlls[i].handle;
          stlcpy(buf,"_init__",sizeof(buf));
          stlcat(buf,dlls[i].filnam,sizeof(buf)); /* C style name */
          strlwr(buf);
          if (DosGetProcAddr(handle,buf,(PPFN)&iniadr) != 0) {
               stlcpy(buf,"@init__",sizeof(buf));
               stlcat(buf,dlls[i].filnam,sizeof(buf)); /* C++ style mangled */
               stlcat(buf,"$qv",sizeof(buf));
               strlwr(buf);
               if (DosGetProcAddr(handle,buf,(PPFN)&iniadr) != 0) {
                    fnd=FALSE;
               }
               else {
                    fnd=TRUE;
               }
          }
          else {
               fnd=TRUE;
          }
          if (fnd) {
               if (prtdnam) {      /* this is only good for WGSERVER.EXE   */
                    sstatr(0x1D);
                    prat(42,20,"%8.8s",dlls[i].filnam);
               }
               (*iniadr)();
          }
     }
     if (dlls != NULL) {
          free(dlls);
          dlls=NULL;
     }
     maxdlls=20;
     numdlls=0;
}

VOID
setgphand(CHAR *title)             /* set the GP handler routine           */
{
     INT i;

     gptitle=title;
     for (i=0 ; i < EXCNUM ; i++) {
          if (newexc[i] >= 0) {
               DosSetExceptionHandler(excnums[i],newexc[i],&oldexc[i]);
          }
     }
}

VOID interrupt FAR
dzhandler(EXCEP_FRAME e)           /* Divide-by-zero handler               */
{
     excidx=EXCDZ;
     gpslmt=0;
     exchandler(e);
}

VOID interrupt FAR
sshandler(EXCEP_FRAME e)           /* Single Step handler                  */
{
     excidx=EXCSS;
     gpslmt=0;
     exchandler(e);
}

VOID interrupt FAR
sfhandler(EXCEP_FRAME e)           /* Stack Fault handler                  */
{
     excidx=EXCSF;
     gpslmt=0;
     exchandler(e);
}

VOID interrupt FAR
gphandler(EXCEP_FRAME e)           /* General Protection Fault handler     */
{
     excidx=EXCGP;
     exchandler(e);
}

VOID interrupt FAR
pfhandler(EXCEP_FRAME e)           /* Page Fault handler                   */
{
     excidx=EXCPF;
     gpslmt=0;
     exchandler(e);
}

VOID
exchandler(EXCEP_FRAME e)          /* generic exception handler            */
{                                  /* (excidx identifies exception)        */
     INT action=0;
     INT state=0;
     INT skip=0;
     INT op,ops[5];
     INT td,od,bc;
     CHAR *cp;

     lastexcro=MK_FP(e.ret_cs,e.ret_ip);
     if (gphdlr && !ingph && gpcntr < gpslmt) {
          ingph=TRUE;
          cp=MK_FP(e.ret_cs,e.ret_ip);
          bc=0;
          while (!action) {
               op=*cp;
               ops[bc]=op;
               td=op>>4;
               od=op&0x0F;
               switch (state) {
               case 0:
                    if (td == 0x04 || td == 0x05 || td == 0x07 || td == 0x0e) {
                         action=-1;          /* calls, pushes, jmps GP immed    */
                    }
                    else if (od >= 0x08 && (td == 0x0c || td == 0x0d)) {
                         action=-1;          /* ESC && rets and ints            */
                    }
                    else if (td == 0x0b || td == 0x09) {
                         action=-1;          /* XCHG regs, mov immediates       */
                    }
                    else if ((od == 0x06 || od == 0x07 || od == 0x0e) && (td <= 0x01)) {
                         action=-1;
                    }
                    else if (op == 0xc2 || op == 0xc3 || op == 0x68 || op == 0x6a ||
                             op == 0x1f || op == 0x8f || op == 0x60 || op == 0x61 ||
                             op == 0xf4 || op == 0xf5 ||
                             (op >= 0xf8 && op <= 0xfd)) {
                         action=-1;
                    }
                    else if (op == 0xa8 || op == 0xa9 || op == 0x2f || op == 0x3f ||
                             op == 0x27 || op == 0x37 || op == 0x82 || op == 0xd4 ||
                             op == 0xd5) {
                         action=-1;
                    }
                    else if ((od == 0x04 || od == 0x05 || od == 0x0c || od == 0x0d)
                             && (td >= 0 && td <= 3) || op == 0xd6 || op == 0xf1 ||
                             op == 0x63 || op == 0x62) {
                         action=-1;
                    }
                    else if (op == 0x26 || op == 0x36 || op == 0x2e || op == 0x3e ||
                             op == 0xf0 || op == 0xf2 || op == 0xf3 || op == 0x64 ||
                             op == 0x65 || op == 0x66 || op == 0x67) {
                         ;                   /* prefixes                   */
                    }
                    else if (op == 0x0f) {   /* 286/386 OS operations      */
                         action=-1;
                    }
                    else if (op == 0x8d) {   /* LEA shouldn't generate GP  */
                         action=-1;
                    }
                    else if (op == 0xd7) {   /* xlat                       */
                         action=1;
                    }
                    else if (op == 0x6e || op == 0x6f || op == 0x6c || op == 0x6d ||
                             (op >= 0xaa && op <= 0xaf) || (op >= 0xa4 && op <= 0xa7)) {
                         action=1;
                    }
                    else if ((od >= 0 && od <= 0x03) && (td >= 0 && td <= 0x03)) {
                         state=1;            /* /r instructions            */
                    }
                    else if (op == 0xc4 || op == 0xc5) {
                         state=1;
                    }
                    else if ((od >= 0x08 && od <= 0x0b) && (td >= 0 && td <= 0x03)) {
                         state=1;            /* more /r instructions       */
                    }
                    else if ((op >= 0x84 && op <= 0x8c) || op == 0x8e) {
                         state=1;
                    }
                    else if (op == 0xf6 || op == 0xf7 || op == 0xfe || op == 0xff) {
                         state=1;            /* /5 instructions            */
                    }
                    else if (op == 0xA0 || op == 0xA2) {
                         state=3;
                    }
                    else if (op == 0xa1 || op == 0xa3) {
                         state=2;
                    }
                    else if (op == 0x83 || op == 0x80) {
                         skip+=1;
                         state=1;
                    }
                    else if (op == 0x69 || op == 0xc7) {
                         skip+=2;
                         state=1;
                    }
                    else if (op == 0x6b || op == 0xc6) {
                         skip+=1;
                         state=1;
                    }
                    break;
               case 1:                            /* /r instructions         */
                    if ((op&0xc0) == 0x40) {   /* disp 8 instructions        */
                         state=3;
                    }
                    else if ((op&0xc0) == 0x80) {   /* disp 16 instructions  */
                         state=2;
                    }
                    else if ((op&0xc0) == 0xc0) {   /* register instructions */
                         action=1;
                    }
                    else {
                         if ((op&0x07) == 0x06) {   /* disp 16 instruction   */
                              state=2;
                         }
                         else {
                              action=1;
                         }
                    }
                    break;
               case 2:                            /* take two bytes and go      */
                    state=3;
                    break;
               case 3:                            /* take one byte and go       */
                    action=1;
                    break;
               }
               if (action >= 0) {
                    cp++;
               }
          }
          if (action == -1) {
               gphdlr=FALSE;
          }
          cp+=skip;
          e.ret_ip=FP_OFF(cp);
          ingph=FALSE;
     }
     if (!gphdlr || ingph || gpcntr >= gpslmt) {
          gpreport(e);
     }
     appgprecd();
     gpcntr++;
}

VOID
gpreport(EXCEP_FRAME e)
{
     DESC desc;
     LONG segbal;
     extern VOID gsbldn(VOID);

     setwin(NULL,0,0,79,24,1);
     if (ingph) {
          setatr(0xF0);
          locate(53,23);
          printf("Double fault @%04X:%04X!",e.ret_cs,e.ret_ip);
          DosSetExceptionHandler(excnums[excidx],oldexc[excidx],NULL);
          (*gpdblflt)();
     }
     setatr(0x70);
     locate(40-58/2,22);
     printf(" %s @ %04X:%04X! Recording information in %s... ",
            excabbr[excidx],e.ret_cs,e.ret_ip,dstname);
     setatr(0xF0);
     locate(40-58/2,23);
     printf("                   One moment please...                   ");
#ifdef GPSCREEN
     setatr(0x70);
     locate(40-63/2,20);
     printf("  AX   BX   CX   DX   SI   DI   "
           "  DS   ES   SS : SP   BP  flag ");
     locate(40-63/2,21);
     printf(" %04x %04x %04x %04x %04x %04x  "
            " %04x %04x %04x:%04x %04x %04x ",
            e.ax,e.bx,e.cx,e.dx,e.si,e.di,
            e.ds,e.es,_SS,e.ret_rsv4,e.bp,e.ret_flags);
     locate(40-50/2,24);
     printf(" Caller:  %04x %04x %04x %04x %04x %04x %04x %04x ",
            ((INT *)MK_FP(_SS,e.bp))[1],
            ((INT *)MK_FP(_SS,e.bp))[2],
            ((INT *)MK_FP(_SS,e.bp))[3],
            ((INT *)MK_FP(_SS,e.bp))[4],
            ((INT *)MK_FP(_SS,e.bp))[5],
            ((INT *)MK_FP(_SS,e.bp))[6],
            ((INT *)MK_FP(_SS,e.bp))[7],
            ((INT *)MK_FP(_SS,e.bp))[8]);
#endif // GPSCREEN
     ingph=TRUE;
     asm  mov  ax,imr
     asm  out  0x21,al
     asm  sti             /* restore interrupts, in case GP while disabled */
     if ((gpdst=fopen(dstname,FOPAA)) != NULL) {
          fprintf(gpdst,"- - - - - - - - - - - - - - - - - "
                        "- - - - - - - - - - - - - - - - - -\n\n"
                        "%s%s @ %04X:%04X EC %04X (recorded %s %s)",
                        gptitle,
                        excnams[excidx],
                        e.ret_cs,
                        e.ret_ip,
                        e.error_code,
                        ncdate(today()),
                        nctime(now()));
          fflush(gpdst);
          if (gpcntr > 0) {
               fprintf(gpdst," GPCNT=%d",gpcntr);
               fflush(gpdst);
          }
          fndnear(gpdst,e.ret_cs,e.ret_ip);
          fprintf(gpdst,"\nAX=%04X BX=%04X CX=%04X DX=%04X "
                        "SI=%04X DI=%04X BP=%04X ES=%04X\n",
                        e.ax,e.bx,e.cx,e.dx,e.si,e.di,e.bp,e.es);
          fflush(gpdst);
          fprintf(gpdst,"DS=%04X  (%s)\n",e.ds,seldetail(e.ds));
          fflush(gpdst);
          fprintf(gpdst,"ES=%04X  (%s)\n",e.es,seldetail(e.es));
          fflush(gpdst);
          fprintf(gpdst,"SS=%04X  (%s)\n",_SS,seldetail(_SS));
          fflush(gpdst);
          fprintf(gpdst,"Current CS:IP==>");
          fflush(gpdst);
          if (DosGetSegDesc(e.ret_cs,&desc) == 0) {
               segbal=(LONG)(desc.size-e.ret_ip);
               segbal=segbal > 16L ? 16L : (segbal < 0L ? 0L : segbal);
               if (segbal > 0L) {
                    shobits(gpdst,MK_FP(e.ret_cs,e.ret_ip),(INT)segbal);
               }
               else {
                    fprintf(gpdst,"Bad IP, no bytes displayed");
                    fflush(gpdst);
               }
          }
          else {
               fprintf(gpdst,"Bad CS, no bytes displayed");
               fflush(gpdst);
          }
          fprintf(gpdst,"\n");
          fflush(gpdst);
          fclose(gpdst);
     }
     appgprept();
     if ((gpdst=fopen(dstname,FOPAA)) != NULL) {
          sdllinfo(gpdst);
          fclose(gpdst);
     }
     plstak(dstname,MK_FP(_SS,e.ret_rsv4),e.bp);
     if ((gpdst=fopen(dstname,FOPAA)) != NULL) {
          fprintf(gpdst,"\n");
          fclose(gpdst);
     }
     (*hdlgpr)(dstname);
     setwin(NULL,0,0,79,24,1);
     setatr(0x70);
     locate(40-58/2,23);
     if (!gpboot) {
          printf("                 Please reboot now!                 ");
          while (1) {
          }
     }
     else {
          printf("                   Now Rebooting!                   ");
          gprebt();
     }
}

VOID
dftdblflt(VOID)                    /* (*gpdblflt)() default vector         */
{
     exit(99);
}

CHAR *
seldetail(                         /* format details on a selector         */
USHORT sel)
{
     DESC desc;
     INT rc;
     CHAR *cp;

     if ((rc=DosGetSegDesc(sel,&desc)) == 0) {
          cp=spr("base %08lX, size %04lX, attrib=%04X",
                 desc.base,desc.size,desc.attrib);
     }
     else if (rc == 490) {
          cp=spr("invalid selector");
     }
     else {
          cp=spr("Phar Lap error %d",rc);
     }
     return(cp);
}

VOID
plcatr(                            /* Phar Lap CATASTRO.TXT additions      */
CHAR *filnam)
{
     FILE *fp;
     extern VOID *catbpsav;

     (*oldcat)(filnam);
     if ((fp=fopen(filnam,FOPAA)) != NULL) {
          sdllinfo(fp);
          fclose(fp);
     }
     plstak(filnam,catbpsav,(UINT)catbpsav);
}

VOID
plstak(                            /* Phar Lap stack report                */
CHAR *filnam,                      /*   file name to append report to      */
VOID *sssp,                        /*   SS:SP where to start               */
USHORT bp)                         /*   initial BP in chain                */
{
     UINT i;
     PFN nrou1,nrou2;
     CHAR near1[80],near2[80];

     if ((gpdst=fopen(filnam,FOPAA)) != NULL) {
          shostack(gpdst,sssp,0,bp);
          if (nsteps > 0) {
               fprintf(gpdst,"\nRoutines:\n");
          }
          fclose(gpdst);
     }
     for (i=0 ; i < nsteps ; i++) {
          if ((gpdst=fopen(filnam,FOPAA)) != NULL) {
               fndproc(codes[i],offs[i],(LONG *)&nrou1,near1,
                                        (LONG *)&nrou2,near2);
               fprintf(gpdst,"%04X:%04X %-15s<   "
                             "%04X:%04X   <   "
                             "%04X:%04X %s\n",
                       FP_SEG(nrou2),FP_OFF(nrou2),near2,
                       codes[i],offs[i],
                       FP_SEG(nrou1),FP_OFF(nrou1),near1);
               fclose(gpdst);
          }
     }
}

VOID
dftgpr(                            /* default (*hdlgpr)() GP report handlr */
CHAR *rfname)                      /* report file name (eg "GP.OUT")       */
{
     rfname=rfname;
}

VOID
sdllinfo(                          /* dump selector data to file when GP   */
FILE *dst)
{
     UINT i;
     CHAR buff[50];
     DESC ssinfo;
     extern USHORT __base,__top,__hincr;
     struct ffblk finfo;

     fprintf(dst,"Modules:");
     for (i=7 ; i < 0xffff ; i+=8) {
          if (DosGetModName(i,50,buff) == 0) {
               if (fndfile(&finfo,buff,0)) {
                    fprintf(dst,"\n%04x=%-45s %s %s %10s",
                                   i,buff,
                                   ncdate(finfo.ff_fdate),
                                   nctime(finfo.ff_ftime),
                                   commas(ul2as(finfo.ff_fsize)));
               }
               else {
                    fprintf(dst,"\n%04x=%s",i,buff);
               }
               fflush(dst);
          }
          else if (fullgp) {
               if (DosGetSegDesc(i,&ssinfo) == 0) {
                    fprintf(dst,"\n%04X=%-32s"
                            "base %08lX,  size %04lX,  attr %04X",
                            i,buff,ssinfo.base,ssinfo.size,ssinfo.attrib);
                    fflush(dst);
               }
          }
          if (i == __base) {
               fprintf(dst,"\n%04X=  <heap base>",i);
          }
          if (i == __top) {
               fprintf(dst,"\n%04X=  <heap top>   (%uK total)",i,
                           (__top-__base+__hincr)*(64/__hincr));
          }
     }
}

VOID
shostack(dst,ptr,rows,ibpval)      /* dump stack to file when GP occurs    */
FILE *dst;
CHAR *ptr;
INT rows;
INT ibpval;
{
     INT i,j;
     INT *ptr2;
     INT bpval;
     DESC ssinfo;
     INT done=0;
     CHAR litbuf[16+1];

     ptr2=(INT *)ptr;
     bpval=ibpval;
     nsteps=0;
     if (DosGetSegDesc((SEL)FP_SEG(ptr2),(PDESC)&ssinfo) != 0) {
          return;
     }
     fprintf(gpdst,"\nStack:");
     fflush(gpdst);
     for (i=0 ; (rows == 0) ? !done : (i < rows) ; i++) {
          fprintf(dst,"\n%04X:%04X ",FP_SEG(ptr2),FP_OFF(ptr2));
          fflush(dst);
          for (j=0 ; j < 16 ; j++) {
               if (FP_OFF(ptr) >= ssinfo.size) {
                    litbuf[j]='-';
               }
               else if (*ptr < ' ') {
                    litbuf[j]='.';
               }
               else {
                    litbuf[j]=*ptr;
               }
               ptr++;
               if ((j&1) == 1) {
               }
               else if ((FP_OFF(ptr2)+1) >= ssinfo.size) {
                    fprintf(dst," ---- ");
                    fflush(dst);
                    ptr2++;
                    done=1;
               }
               else if (FP_OFF(ptr2) == bpval) {
                    bpval=*(ptr2);
                    fprintf(dst,">%04X<",*ptr2++);
                    fflush(dst);
                    if (nsteps < MAXSTP) {
                         offs[nsteps]=*(ptr2);
                         codes[nsteps++]=*(ptr2+1);
                    }
               }
               else {
                    fprintf(dst," %04X ",*ptr2++);
                    fflush(dst);
               }
          }
          litbuf[16]='\0';
          fprintf(dst,"  %s",litbuf);
     }
}

VOID
shobits(dst,ptr,len)               /* utility routine for code dump        */
FILE *dst;
CHAR *ptr;
INT len;
{
     INT i;

     for (i=0 ; i < len ; i++) {
          fprintf(dst,"%02X ",*(ptr++));
          fflush(dst);
     }
}

VOID
fndproc(INT code,INT off,LONG *rou1,CHAR *rnam1,LONG *rou2,CHAR *rnam2)
{                                  /* find procedure bounds for stack walk */
     HMODULE i,modhan;
     USHORT ord;
     CHAR buff[80];
     VOID FAR (*fn)(),FAR (*gp)();
     CHAR near1[80],near2[80];
     PFN nrou1,nrou2;
     LONG maxdif1,maxdif2;

     gp=MK_FP(code,off);
     modhan=0;
     maxdif1=maxdif2=0x7FFFFFFFL;
     strcpy(near1,"Unknown");
     strcpy(near2,"Unknown");
     nrou1=nrou2=NULL;
     for (i=code ; i > 0 && i < (HMODULE)0xFFF8U ; i-=8) {
          if (DosGetModName(i,80,(PCHAR)buff) == 0) {
               modhan=i;
               break;
          }
     }
     if (modhan != 0) {             /* we know what guy its in now....      */
          ord=0;
          while (DosEnumProc(modhan,buff,&ord) == 0) {
               fn=0L;
               DosGetProcAddr(modhan,buff,(PPFN)&fn);
               if ((LONG)fn >= (LONG)gp) {
                    if (((LONG)fn-(LONG)gp) <= maxdif1) {
                         maxdif1=(LONG)fn-(LONG)gp;
                         nrou1=(PFN)fn;
                         strncpy(near1,buff,79);
                    }
               }
               else {
                    if (((LONG)gp-(LONG)fn) < maxdif2) {
                         maxdif2=(LONG)gp-(LONG)fn;
                         nrou2=(PFN)fn;
                         strncpy(near2,buff,79);
                    }
               }
          }
     }
     strcpy(rnam1,near1);
     strcpy(rnam2,near2);
     *(rou1)=(LONG)nrou1;
     *(rou2)=(LONG)nrou2;
}

VOID
fndnear(FILE *dst,INT code,INT off)     /* find nearest exported routine   */
{
     CHAR near1[80],near2[80];
     PFN nrou1,nrou2;

     fndproc(code,off,(LONG *)&nrou1,near1,(LONG *)&nrou2,near2);
     fprintf(dst,"\nGP'ed between %04X:%04X %s and %04X:%04X %s",
          FP_SEG(nrou2),FP_OFF(nrou2),near2,
          FP_SEG(nrou1),FP_OFF(nrou1),near1);
     fflush(dst);
}

static VOID
gprebt(VOID)                       /* reboot machine after GP              */
{
     INT rbdlay;
     LONG l;
     INT sel,*ptr;
     FILE *fp;

     if ((fp=fopen("GALEXCEP.FLG",FOPWA)) != NULL) {
          fprintf(fp,"EXCEPTION Occurred!");
          fclose(fp);
     }
     for (rbdlay=50 ; rbdlay > 0 ; rbdlay--) {
          for (l=0L ; l < 200000L ; l++) {
          }
     }
     DosMapRealSeg(0x0040,(ULONG)0x0074,(SEL *)&sel);
     ptr=MAKEP(sel,0x0072);
     *ptr=0;                       /* this is to set up for a cold boot    */
     DosRealFarJump((REALPTR)0xFFFF0000LU,NULL,0,0);
}

INT                                /* returns base selector for tiled reg  */
pltile(size,bsel,stride,tsize)     /* tile equiv                           */
ULONG size;
INT bsel;
USHORT stride,tsize;
{
     ULONG linadd,curadr;
     INT no,seginc,rsel,i;
     USHORT frac,rv;

     if (bsel != 0) {
          DosFreeSeg(bsel);        /* free already allocated chunk if any  */
     }
     if ((rv=DosAllocLinMem(size,&linadd)) != 0) {
          plcatast(rv,"PLTILE: Error Allocating Linear Memory!");
     }
     no=(USHORT)(size/tsize);
     frac=(USHORT)(size%tsize);
asm  xor  ax,ax
asm  mov  cx,no
asm  inc  cx        /* allow for fractional remainder too */
asm  int  31h
asm  mov  rsel,ax
asm  jc   nook
asm  jmp  ok
nook: ;
     plcatast(rsel,"PLTILE: Out of contiguous selectors!");
ok: ;
asm  mov  ax,3
asm  int  31h
asm  mov  seginc,ax
asm  jc   nook2
asm  jmp  ok2
nook2: ;
     plcatast(seginc,"PLTILE: Can't determine huge increment!");
ok2: ;
     curadr=linadd;
     for (i=0 ; i < no ; i++) {
          if ((rv=DosMapLinMemToSelector(rsel+(i*seginc),curadr,tsize)) != 0) {
               plcatast(rv,"PLTILE: Error mapping full selector!");
          }
          curadr+=stride;
     }
     if (frac != 0
      && (rv=DosMapLinMemToSelector(rsel+(i*seginc),curadr,frac)) != 0) {
          plcatast(rv,"PLTILE: Error mapping fractional selector!");
     }
     return(rsel);
}

VOID
plcatast(INT plerr,CHAR *msg)      /* catastro                             */
{
     static INT errvals[]={1,2,3,4,5,6,8,9,13,30,31,87,111,126,
                           127,180,182,190,203,213,489,490,0};
     static CHAR *msgs[]={"Invalid Function number",
                          "File not found",
                          "Path not found",
                          "Out of file handles",
                          "Access denied",
                          "Invalid handle",
                          "Insufficient memory",
                          "Invalid memory block",
                          "Invalid data",
                          "Read fault",
                          "General failure",
                          "Invalid parameter",
                          "Buffer too small",
                          "Invalid module handle",
                          "Procedure not found",
                          "Invalid segment number",
                          "Invalid function ordinal",
                          "Invalid module type",
                          "Environment variable not found",
                          "Bad Dynamic Link",
                          "Not enough selectors available",
                          "Not a selector"} ;
     INT i;

     for (i=0 ; errvals[i] != 0 ; i++) {
          if (errvals[i] == plerr) {
               catastro("%s (%s)",msg,msgs[i]);
               return;
          }
     }
     catastro("%s (Phar Lap code %04X)",msg,plerr);
}

VOID
DosBlockIntr(VOID)            /* Block all interrupts at 8259              */
{
#ifdef __HUGE__
asm  push ds
asm  mov  ax,PROTSTUF_DATA              /*  Manually load DS!                    */
asm  mov  ds,ax                    /*  ...Borland 4 won't without a C ref   */
#endif // __HUGE__
asm  cli
asm  in   al,0x21
asm  mov  oldimsk,ax
asm  mov  al,0xFF
asm  out  0x21,al
#ifdef __HUGE__
asm      pop  ds                      /*  restore DS to entry value                     */
#endif // __HUGE__
}

VOID
DosUnblockIntr(VOID)          /* Unblock all interrupts at 8259            */
{
#ifdef __HUGE__
asm  push ds
asm  mov  ax,PROTSTUF_DATA         /*  Manually load DS!                    */
asm  mov  ds,ax                    /*  ...Borland 4 won't without a C ref   */
#endif // __HUGE__
asm  mov  ax,oldimsk
asm  out  0x21,al
asm  sti
#ifdef __HUGE__
asm      pop  ds                      /*  restore DS to entry value                     */
#endif // __HUGE__
}
#endif // GCDOSP
#endif // GCWINNT

VOID
protinit(                          /* initialize for protected mode        */
CHAR *pgmname)                     /* program name (end with a space) or ""*/
{
#ifdef GCDOS
#ifdef GCDOSP
     extern int colseg,monseg;
     extern unsigned _maxfarrealloc;

     setgphand(pgmname);
     _maxfarrealloc=0;             /* max # of reallocs for huge allocs    */
     DosMapRealSeg(0xb800,(ULONG)160*25,(SEL *)&colseg);
     DosMapRealSeg(0xb000,(ULONG)160*25,(SEL *)&monseg);
     oldcat=catrpt;
     catrpt=plcatr;
#else
     (VOID)pgmname;
#endif // GCDOSP
     asm  in   al,0x21
     asm  xor  ah,ah
     asm  mov  imr,ax
#else
     (VOID)pgmname;
#endif // GCDOS
}

