/*static char *SCCSID = "@(#)dbger.c    6.14 92/03/10";*/
#define SCCSID  "@(#)dbger.c    6.14 92/03/10"

/***    DBGER.C - Debugger support for DosDebug
 *
 *      THIS IS AN OS/2 VERSION 2.x SPECIFIC FILE
 *
 *       IBM/Microsoft Confidential
 *
 *       Copyright (c) IBM Corporation 1987, 1989
 *       Copyright (c) Microsoft Corporation 1987, 1989
 *
 *       All Rights Reserved
 *
 *      This file implements the debugger context routines required
 *      by the DosDebug API.
 *****************************************************************************
 *  Change                                                               Chg *
 *  Activity      Date    BY   Reason                                    Num *
 *  --------      ----    ---  -------                                   --- *
 *  DCR 1320    04/23/91  ARJ  Pack Segement Support                     001 *
 *  PTR B721562 05/25/91  ARJ  PTDA Address not passed                   002 *
 *  PTR B723886 07/13/91  ARJ  Remove PRINTFs and correct comments       003 *
 *  PTR B727812 10/19/91  ARJ  Exception Handler Not Invoked             004 *
 *  DCR 1372    10/19/91  ARJ  Add IFDEF statements to DBG32APIDebug         *
 *  PTR B729735 11/14/91  ARJ  Correct IFDEF STATEMENTS (No CHG NUM)         *
 *  PTR B732420 02/06/92  ARJ  Remove TKPIDtoPTDA from DBG32APIDEBUG     005 *
 *  PTR B733751 03/06/92  ARJ  Correct address conversions               006 *
 *  Def B055171 10/12/92  ARJ  Delete first dbgAssert from DBGReadWrite  007 *
 *                             Take TIB addr from TCB_s for DBGReadReg   007 *
 *  Def 0061066 01/28/93  ARJ  When SEVER has occur do not change VALUE  008 *
 *  Def 0070301 10/01/93  ARJ  Sever Connection is option on CONNECT CMD 009 *
 *  DEF   80689 03/25/94  AJR  Support registration of GRESEM            010 *
 *  DEF  114816 04/18/95  ARJ  Support for Private DLL's                 011 *
 *****************************************************************************
 */

#define INCL_TYPES                  /* DRV_\H\basedef.h typedefs            */
#define INCL_ERROR_H                /* DRV_\H\bseerr.h  OS/2 error numbers  */
#define INCL_ERROR2_H               /* DRV_\H\bseerr.h  OS/2 error numbers  */

#include <basemac.h>                /* DRV_\H\                              */
#include <basedef.h>                /* DRV_\H\                              */
#define FNO_NUM         FNO_DBGER_C /* DRV_\SRC\DOS\DOSINC\fnotask.h        */
#include <fnotask.h>                /* DRV_\SRC\DOS\DOSINC\                 */
#include <osmac.h>                  /* DRV_\SRC\DOS\DOSINC\                 */
#include <osconst.h>                /* DRV_\SRC\DOS\DOSINC\                 */
#include <ostypes.h>                /* DRV_\SRC\DOS\DOSINC\                 */
#include <infoseg.h>                /* DRV_\H\                              */
#include <vmowner.h>                /* DRV_\SRC\DOS\DOSINC\                 */
#include <dossymc.h>                /* DRV_\SRC\DOS\DOSINC\                 */
#include <sigdef.h>                 /* DRV_\SRC\DOS\DOSINC\                 */
#include <except.h>                 /* DRV_\SRC\DOS\DOSINC\                 */
#include <bsexcpt.h>                /* DRV_\H\                              */
#include <mi.h>                     /* DRV_\H\                              */
#include <pgexport.h>               /* DRV_\SRC\DOS\DOSINC\                 */
#include <vmexport.h>               /* DRV_\SRC\DOS\DOSINC\                 */
#include <selxport.h>               /* DRV_\SRC\DOS\DOSINC\                 */
#include <seldesc.h>                /* DRV_\H\                              */
#include <tkexport.h>               /* DRV_\SRC\DOS\DOSINC\                 */
#include <curdir.h>                 /* DRV_\SRC\DOS\DOSINC\                 */
#include <dirent.h>                 /* DRV_\SRC\DOS\DOSINC\                 */
#include <find.h>                   /* DRV_\SRC\DOS\DOSINC\                 */
#include <screen.h>                 /* DRV_\SRC\DOS\DOSINC\                 */
#include <sem.h>                    /* DRV_\SRC\DOS\DOSINC\                 */
#include <tkptda.h>                 /* DRV_\SRC\DOS\DOSINC\                 */
#include <ldrxport.h>               /* DRV_\SRC\DOS\DOSINC\                 */
#include <bseerr.h>                 /* DRV_\H\                              */
#include <kmode.h>                  /* DRV_\SRC\DOS\DOSINC\                 */
#include <bsetib.h>                 /* DRV_\H\                              */
#include <tklocal.h>                /* DRV_\SRC\DOS\TASK\                   */
#include <proc.h>                   /* DRV_\SRC\DOS\DOSINC\                 */
#include <sched.h>                  /* DRV_\SRC\DOS\DOSINC\                 */
#include <string.h>                 /* DRV_\H\  and  DRV_\H386\             */

#include <dbgapi.h>     /* (debug) API definitions      DRV_\H\              */
#include <dbgex.h>      /* (debug) External definitions DRV_\SRC\DOS\DOSINC\ */
#include <dbgin.h>      /* (debug) Internal definitions DRV_\SRC\DOS\TASK\   */

#ifdef DBGSTRICT
/***LD   DBGBugBug - Global Variable for debugging
 * DosDebug DeBug Flags (bit settings) are defined in DBGAPI.H
 */
    ULONG  DBGBugBug = 0;
#endif

/***LD   vmOffGSR  - Is MODIFIED by Dbgenable, Dbgdisable and         A011 *
  *                  Dbgprocterm                                      A011 *
  * vmOffGSR  =  0 means LOAD DLL's into Global Shared Region         A011 *
  * vmOffGSR !=  0 means DO NOT LOAD DLL's into Global Shared Region  A011 *
 */
    extern SHORT   vmOffGSR;                               /*         A011 */

/***LD  pdcbHead - head of list of DCBs
 *
 *      Each debuggee process is given a Debug Control Block (DCB), and these
 *      DCBs are also linked together, with pdcbHead as the list head.
 */

//  moved to task\sdata.asm for feature 122427 - System Anchor Block
extern DCB_t *pdcbHead;

#ifdef INCDEBUG

/***LD  pSemListHead
 *
 *      All semList structures are linked together, with pSemListHead
 *      as the list head. This chain does not need to be protected
 *      by a semaphore since the entries once registerd cannot be alterd
 *      and no registration is allowed while DosDebug is running.
 */

//  moved to task\sdata.asm for feature 122427 - System Anchor Block
extern semList *pSemListHead;

/***LD  hdbgTextLock - lock handle for DEBUGLOCK_TEXT
 *
 *      The DBG component code is located in three segments - a small resident
 *      segment (in _TEXT), a segment that is always swappable
 *      (DEBUGSWAP_TEXT), and a segment that must be locked in memory when
 *      DosDebug is in use (DEBUGLOCK_TEXT).  hdbgTextLock is the lock handle
 *      used to lock and unlock the latter.
 */

static struct vmlh_s hdbgTextLock;

/***LP  DbgRequestSem - Enter Debugger Command Critical Section
 *
 *      DbgRequestSem is used to request the semaphore which
 *      serializes the debugger threads which are using Debug.
 *
 *      This semaphore must be owned before utilizing the
 *      debuggee thread of execution, such as the Go command.
 *      Not all Debug commands are serialized in this way.
 *
 *      Warning - this function is implemented as a macro.
 *
 *      ENTRY
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *                      Semaphore in Debug Control Block owned
 *
 *              Return Value != 0
 *                      Fatal Error - Propogate Return Value
 *
 */

#define DbgRequestSem(pdcb)     KSEMRequestMutex((HKSEM)pdcb->hKSEMdbger, -1)

/***LP  DbgClearSem - Exit Debugger Command Critical Section
 *
 *      DbgClearSem clears the semaphore obtained via DbgRequestSem,
 *      allowing other serialized debugger threads to run.
 *
 *      Warning - this function is implemented as a macro.
 *
 *      ENTRY
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              No Return Value
 */

#define DbgClearSem(pdcb)       KSEMReleaseMutex((HKSEM)pdcb->hKSEMdbger)

/***LP  DbgNull - Perform the DBG_C_Null command
 *
 *      DbgNull performs the DBG_C_Null command.  This command
 *      merely returns 'Success'.
 *
 *      DbgNull is also used for invalid commands, in which case
 *      it returns an error.
 *
 *      This function is swappable.
 *
 *      API ENTRY
 *              uDB.Cmd = DBG_C_Null
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success or DBG_N_Error
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *      Pseudocode:
 *      if (Command == DBG_C_NULL)
 *        pkDB->Cmd = DBG_N_Success;
 *      else
 *        pkDB->Cmd = DBG_N_Error;
 *        pkDB->Value = ERROR_INVALID_FUNCTION
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

int DbgNull (kDB_t *pkDB, DCB_t *pdcb)
{

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgNull          ");   }
#endif

    //  The Cmd field is preset to DBG_N_Success so no action is required if
    //  lCmd is DBG_C_NULL.
    if (pkDB->lCmd != DBG_C_Null) {
        pkDB->uDBTemp.Cmd = DBG_N_Error;
        pkDB->uDBTemp.Value = ERROR_INVALID_FUNCTION;
    }

    return(NO_ERROR);
}


/***LP  dbgReadWrite - Read/write debuggee memory through an alias
 *
 *      This function is swappable.
 *
 *      ENTRY   laddr = linear address to read from/write to
 *              hptda = debuggee ptda handle
 *              cb = number of bytes to read/write
 *              flvm = VMMapDebugAlias flags
 *                      if read operation, VMDA_RO
 *                      if write operation, 0
 *              pUsrDst = if read operation, destination buffer
 *                        if write operation, NULL
 *              pUsrSrc = if read operation, source buffer
 *                        if write operation, NULL
 *              fltk = TKSuFuBuff flags
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error - debug buffer already filled in.
 *
 *              Return Value != 0
 *                      Error.  Propagate return value back to caller.
 *
 *      Pseudocode:
 *      calcuate alias base and size for debuggee memory region
 *      if debuggee memory region is in System Arena
 *        ERROR_INVALID_ACCESS
 *
 *      create system debug alias to debuggee memory region
 *      copy cbMemRegion bytes from pUsrSrc to alias or alias to pUsrDst
 *      free alias
 *
 *      return possible error
 */

int
dbgReadWrite(ulong_t laddr, VMHOB hptda, int cb, int flvm,
              uchar_t *pUsrDst, uchar_t *pUsrSrc, int fltk)
{
    ulong_t cbalias;                    /* size of alias region */
    ulong_t palias;                     /* start of alias region */
    VMAC vmret;                         /* alias information */
    int rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgReadWrite     ");   }
#endif

    cbalias = VMRegionSize(laddr, cb);
    palias = VMRegionBase(laddr);

    if (VMSysArena(palias + cbalias - 1L) || (palias + cbalias - 1L) < palias)
        rt = ERROR_INVALID_ACCESS;

    else if ((rt = LDRGetSem()) == NO_ERROR) {
        // Create the alias in system space so another debugger thread cannot
        // accidentally write through it and so AliasFree notifications are
        // not returned for internal aliases (see DbgAliasFree).
        rt = VMMapDebugAlias(VMDA_SYSTEM | flvm,
                             palias, cbalias,
                             hptda,
                             SSToDS(&vmret));
        LDRClearSem();

        if (rt == NO_ERROR) {
            palias = vmret.ac_va | (laddr - palias);
            rt = TKSuFuBuff((pUsrSrc == NULL ? pUsrDst : (PCHAR) palias),
                            (pUsrSrc == NULL ? (PCHAR) palias : pUsrSrc),
                            cb, fltk);

            if (VMFreeMem(vmret.ac_va, pPTDACur->ptda_handle,
              NULL) != NO_ERROR)
                dbgAssert(FALSE);
        }
    }
    return(rt);
}


/***LP  DbgRWMem - Read/write debuggee memory through an alias
 *
 *      This is the worker function for DbgReadWord, DbgWriteWord,
 *      DbgReadMem, and DbgWriteMem.
 *
 *      This function is swappable.
 *
 *      ENTRY
 *              puDB = Pointer to debug buffer,
 *                  puDB->Addr = Debuggee address to read from/write to
 *              hptdaDbgee = debuggee ptda handle
 *              cb = number of bytes to read/write
 *              flvm = VMMapDebugAlias flags
 *                      if read operation, VMDA_RO
 *                      if write operation, 0
 *              pUsrDst = if read operation, destination buffer
 *                        if write operation, NULL
 *              pUsrSrc = if read operation, source buffer
 *                        if write operation, NULL
 *              fltk = TKSuFuBuff flags
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error - debug buffer already filled in.
 *
 *              Return Value != 0
 *                      Error.  Propagate return value back to caller.
 *
 *      Pseudocode:
 *
 *      call dbgReadWrite to do the work
 *
 *      return possible error in debug buffer (except for ERROR_INTERRUPT)
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

int DbgRWMem (uDB_t *puDB, ushort_t hptdaDbgee, int cb, int flvm,
              uchar_t *pUsrDst, uchar_t *pUsrSrc, int fltk)
{
    int rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgRWMem         ");   }
#endif

    rt = dbgReadWrite(puDB->Addr,hptdaDbgee,cb,flvm,pUsrDst,pUsrSrc,fltk);

    if (rt != NO_ERROR && rt != ERROR_INTERRUPT) {
        puDB->Cmd = DBG_N_Error;
        puDB->Value = rt;
        rt = NO_ERROR;
    }

    return(rt);
}


/***LP  DbgReadWord - Perform the DBG_C_ReadMem command
 *
 *      The word at the desired address is read, and
 *      stored into Value.
 *
 *      This function is swappable.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Addr = Address to read from
 *              uDB.Cmd = DBG_C_ReadMem_I       (identical commands)
 *              uDB.Cmd = DBG_C_ReadMem_D
 *              uDB.Cmd = DBG_C_ReadMem
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *                      uDB.Value = Word read from the specified address.
 *                      The high word of uDB.Value will be zero.
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Error Accessing Debuggee Memory
 *                      uDB.Value = Error Code
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *              Return Value != 0
 *                      Error occurred
 *
 *      Pseudocode:
 *      Zero high word of Value field
 *      DbgRWMem to read the memory word
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

int DbgReadWord (kDB_t *pkDB, DCB_t *pdcb)
{

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgReadWord      ");   }
#endif

    pkDB->uDBTemp.Value = 0;

    return(DbgRWMem(&pkDB->uDBTemp, pdcb->pptdaDbgee->ptda_handle,
                    sizeof(short), VMDA_RO,
                    (uchar_t *) &pkDB->uDBTemp.Value, NULL,
                    TK_UM_SRCE));
}

/***LP  DbgWriteWord - Perform the DBG_C_WriteMem command
 *
 *      The word in Value is written to the specified address.
 *
 *      In the case of a write to shared read-only memory, the memory
 *      is converted to private, and any set Dynamic RAS tracepoints
 *      are removed from that memory, before the write is performed.
 *
 *      DynamicTrace will continue to function in that area, in the
 *      context of other processes.     The area will continue to be
 *      shared by other processes, if any.
 *
 *      In this way, breakpoints may be set in the debuggee without
 *      affecting the other processes using the modules.
 *
 *      This function is swappable.
 *
 *      Restrictions :
 *
 *      Unable to read from any memory outside user space.
 *
 *      The high word of Value is ignored.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Addr = Address to write to
 *              uDB.Value = Word to write
 *              uDB.Cmd = DBG_C_WriteMem_I      (identical commands)
 *              uDB.Cmd = DBG_C_WriteMem_D
 *              uDB.Cmd = DBG_C_WriteMem
 *              The high word of uDB.Value is ignored.
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Error Accessing Debuggee Memory
 *                      uDB.Value = Error Code
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *              Return Value != 0
 *                      Error occurred
 *
 *      Pseudocode:
 *      DbgRWMem to write the memory word
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

int DbgWriteWord (kDB_t *pkDB, DCB_t *pdcb)
{

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgWriteWord     ");   }
#endif

    return(DbgRWMem(&pkDB->uDBTemp, pdcb->pptdaDbgee->ptda_handle,
                    sizeof(short), 0,
                    NULL, (uchar_t *) &pkDB->uDBTemp.Value,
                    TK_UM_DEST));
}

/***LP  DbgReadBuf - Perform the DBG_C_ReadBuf command
 *
 *      LEN bytes are copied from the debuggee's user memory
 *      space starting at ADDR into the debugger's Buffer.
 *
 *      This command is not serialized with respect to the Go command.
 *
 *      This function is swappable.
 *
 *      Restrictions :
 *
 *      Unable to read from any memory outside user space.
 *
 *      Both specified memory regions must be currently valid.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Cmd = DBG_C_ReadMemBuf
 *              uDB.Addr = Debuggee address to read from
 *              uDB.Buffer = Debugger address to copy to
 *              uDB.Len = Number of bytes to read
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Error Accessing Debuggee Memory
 *                      uDB.Value = Error Code
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *              Return Value != 0
 *                      Error Accessing Debugger Memory
 *
 *      Pseudocode:
 *      DbgRWMem to read the memory
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

int DbgReadBuf (kDB_t *pkDB, DCB_t *pdcb)
{

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgReadBuf       ");   }
#endif

    return(DbgRWMem(&pkDB->uDBTemp, pdcb->pptdaDbgee->ptda_handle,
                    pkDB->uDBTemp.Len, VMDA_RO,
                    (uchar_t *) pkDB->uDBTemp.Buffer, NULL,
                    TK_UM_SRCE|TK_UM_DEST|TK_UM_FATAL_DST));
}

/***LP  DbgWriteBuf - Perform the DBG_C_WriteBuf command
 *
 *      LEN number of bytes is copied from the debugger's Buffer into
 *      the debuggee's memory space starting at ADDR.
 *
 *      This command is not serialized with respect to the Go command.
 *
 *      In the case of a write to shared read-only memory, the memory
 *      is first converted to private, and any set Dynamic RAS logging
 *      points are removed from that memory, before the write is
 *      performed.
 *
 *      Dynamic RAS logging will continue to function in that area, in
 *      the context of other processes.  The area will continue to be
 *      shared by other processes, if any.
 *
 *      In this way, breakpoints may be set in the debuggee without
 *      affecting the other modules.
 *
 *      This function is swappable.
 *
 *      Restrictions :
 *
 *      Unable to write to any memory outside user space.
 *
 *      Both specified memory regions must be currently valid.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Cmd = DBG_C_WriteMemBuf
 *              uDB.Addr = Debuggee address to write to
 *              uDB.Buffer = Debugger address to copy from
 *              uDB.Len = Number of bytes to write
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Error Accessing Debuggee Memory
 *                      uDB.Value = Error Code
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *              Return Value != 0
 *                      Error Accessing Debugger Memory
 *
 *      Pseudocode:
 *      DbgRWMem to write the memory
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

int DbgWriteBuf (kDB_t *pkDB, DCB_t *pdcb)
{

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgWriteBuf      ");   }
#endif

    return(DbgRWMem(&pkDB->uDBTemp, pdcb->pptdaDbgee->ptda_handle,
                    pkDB->uDBTemp.Len, 0,
                    NULL, (uchar_t *) pkDB->uDBTemp.Buffer,
                    TK_UM_SRCE|TK_UM_DEST|TK_UM_FATAL_SRC));
}

/***LP  DbgReadReg - Read Debuggee CPU Registers (DBG_C_ReadReg)
 *
 *      The register set in the uDB is updated, including the
 *      selector information.
 *
 *      This function must be locked when in use because it accesses a
 *      debuggee tcb.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Tid = Thread ID of register set to read.  If Tid is 0,
 *                        use the Tid of the 'active' debuggee thread, if any.
 *              uDB.Cmd = DBG_C_ReadReg
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *                      uDB.Tid = Thread ID corresponding to register set.
 *                      uDB.MTE = Program module's MTE handle
 *                      The register set in the uDB is updated, including
 *                      the selector information.
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Error Reading Registers (Tid invalid)
 *                      uDB.Value = Error Code
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *      Pseudocode:
 *      if (Tid == 0)
 *        Tid = active debuggee thread, if any
 *      MTE = ptda module handle
 *      fill in register set
 *      for each selector
 *        fill in selector information
 */

#pragma code_seg("DEBUGLOCK_TEXT", "CODE")      /* lock DEBUG code */

int DbgReadReg (kDB_t *pkDB, DCB_t *pdcb)
{
    tcb_t *ptcb;                                // debuggee thread's pTCB
    KMREG kmreg;                                // debuggee register set
    int rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgReadReg       ");   }
#endif

    if (pkDB->uDBTemp.Tid == 0)
        //  If the debuggee is stopped, sActiveTid will be current debuggee
        //  thread id.  If the debuggee is running, sActiveTid will be zero
        //  and will cause TKTidTo_pTCB to return an error.

        pkDB->uDBTemp.Tid = pdcb->sActiveTid;

    if ((rt = TKTidTo_pTCB(pdcb->pptdaDbgee, pkDB->uDBTemp.Tid,
                           SSToDS(&ptcb))) != NO_ERROR) {

      /* if an ERROR occured before now do not change Value Field     A008 */
      if (pkDB->uDBTemp.Cmd != DBG_N_Error)               /*          A008 */
        pkDB->uDBTemp.Value = rt;

      pkDB->uDBTemp.Cmd = DBG_N_Error;
      return(NO_ERROR);
    }

    pkDB->uDBTemp.MTE = pdcb->pptdaDbgee->ptda_module;

    KMQueryAllClientRegisters(ptcb, SSToDS(&kmreg));
    pkDB->uDBTemp.EAX = kmreg.kmreg_eax;
    pkDB->uDBTemp.EBX = kmreg.kmreg_ebx;
    pkDB->uDBTemp.ECX = kmreg.kmreg_ecx;
    pkDB->uDBTemp.EDX = kmreg.kmreg_edx;
    pkDB->uDBTemp.ESI = kmreg.kmreg_esi;
    pkDB->uDBTemp.EDI = kmreg.kmreg_edi;
    pkDB->uDBTemp.EBP = kmreg.kmreg_ebp;
    pkDB->uDBTemp.ESP = kmreg.kmreg_esp;
    pkDB->uDBTemp.EIP = kmreg.kmreg_eip;
    pkDB->uDBTemp.EFlags = kmreg.kmreg_eflag;
    pkDB->uDBTemp.CS = (ushort_t) kmreg.kmreg_cs;
    pkDB->uDBTemp.DS = (ushort_t) kmreg.kmreg_ds;
    pkDB->uDBTemp.ES = (ushort_t) kmreg.kmreg_es;
    pkDB->uDBTemp.FS = (ushort_t) kmreg.kmreg_fs;
    pkDB->uDBTemp.GS = (ushort_t) kmreg.kmreg_gs;
    pkDB->uDBTemp.SS = (ushort_t) kmreg.kmreg_ss;

    if (pkDB->uDBTemp.CS != 0)
        SELGetDescInfo((SEL)pkDB->uDBTemp.CS, pdcb->pptdaDbgee->ptda_handle,
                       (struct seldesc_s *) &pkDB->uDBTemp.CSLim);
    else
        pkDB->uDBTemp.CSLim = pkDB->uDBTemp.CSBase = pkDB->uDBTemp.CSAcc =
                              pkDB->uDBTemp.CSAtr = 0;

    if (pkDB->uDBTemp.DS != 0)
        SELGetDescInfo((SEL)pkDB->uDBTemp.DS, pdcb->pptdaDbgee->ptda_handle,
                       (struct seldesc_s *) &pkDB->uDBTemp.DSLim);
    else
        pkDB->uDBTemp.DSLim = pkDB->uDBTemp.DSBase = pkDB->uDBTemp.DSAcc =
                              pkDB->uDBTemp.DSAtr = 0;

    if (pkDB->uDBTemp.ES != 0)
        SELGetDescInfo((SEL)pkDB->uDBTemp.ES, pdcb->pptdaDbgee->ptda_handle,
                       (struct seldesc_s *) &pkDB->uDBTemp.ESLim);
    else
        pkDB->uDBTemp.ESLim = pkDB->uDBTemp.ESBase = pkDB->uDBTemp.ESAcc =
                              pkDB->uDBTemp.ESAtr = 0;

    if (pkDB->uDBTemp.FS != 0)
     {
        SELGetDescInfo((SEL)pkDB->uDBTemp.FS, pdcb->pptdaDbgee->ptda_handle,
                       (struct seldesc_s *) &pkDB->uDBTemp.FSLim);

        /* base address of the TIB for the requested Thread ID         A007*/
        pkDB->uDBTemp.FSBase = (ULONG) ptcb->TCBptib;  /*              A007*/
     }
    else
        pkDB->uDBTemp.FSLim = pkDB->uDBTemp.FSBase = pkDB->uDBTemp.FSAcc =
                              pkDB->uDBTemp.FSAtr = 0;

    if (pkDB->uDBTemp.GS != 0)
        SELGetDescInfo((SEL)pkDB->uDBTemp.GS, pdcb->pptdaDbgee->ptda_handle,
                       (struct seldesc_s *) &pkDB->uDBTemp.GSLim);
    else
        pkDB->uDBTemp.GSLim = pkDB->uDBTemp.GSBase = pkDB->uDBTemp.GSAcc =
                              pkDB->uDBTemp.GSAtr = 0;

    if (pkDB->uDBTemp.SS != 0)
        SELGetDescInfo((SEL)pkDB->uDBTemp.SS, pdcb->pptdaDbgee->ptda_handle,
                       (struct seldesc_s *) &pkDB->uDBTemp.SSLim);
    else
        pkDB->uDBTemp.SSLim = pkDB->uDBTemp.SSBase = pkDB->uDBTemp.SSAcc =
                              pkDB->uDBTemp.SSAtr = 0;

    return(NO_ERROR);
}

/***LP  dbgVerifySel - Verify a new debuggee selector
 *
 *      When a debugger tries to set a debuggee's selectors, each selector
 *      is checked to make sure it access only valid memory.
 *
 *      This function must be locked when in use because it is a worker
 *      function for DbgWriteReg, which must be locked when in use.
 *
 *      ENTRY
 *              selUsr = Selector to verify
 *              HOBDbgeePTDA = Debuggee PTDA handle
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *              Return Value != 0
 *                      Invalid selector
 *
 *      Pseudocode:
 *      if (selUsr == 0)
 *        return(NO_ERROR)
 *      if (SELGetDescInfo(selUsr) != NO_ERROR)
 *        return(ERROR_PROTECTION_VIOLATION)
 *      if (selUsr is not a memory segment || selUsr has DPL 0)
 *        return(ERROR_PROTECTION_VIOLATION)
 *      return(NO_ERROR)
 */

#pragma code_seg("DEBUGLOCK_TEXT", "CODE")      /* lock DEBUG code */

int dbgVerifySel (SEL selUsr, VMHOB hobDbgeePTDA)
{
    struct seldesc_s seldesc;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgVerifySel     ");   }
#endif

    if (selUsr != 0 &&
        (SELGetDescInfo(selUsr, hobDbgeePTDA, SSToDS(&seldesc)) != NO_ERROR ||
         (seldesc.desc_acc & D_SEG == 0) ||
         (seldesc.desc_acc & D_PRIV == D_DPL0)))
        return(ERROR_PROTECTION_VIOLATION);

    return(NO_ERROR);
}

/***LP  DbgWriteReg - Write Debuggee CPU Registers  (DBG_C_WriteReg)
 *
 *      All registers are updated to the stored values.  The access
 *      rights, limits, and eflags are also updated to match the
 *      now-current values.
 *
 *      An error will be returned if the selectors are not accessable
 *      by user space code, or are not valid memory segments.
 *
 *      Restrictions :
 *
 *      Reserved system or processor flags bits are not modified via
 *      this method, but are masked to their correct values.  The
 *      selector access rights and limits can not be modified.  The
 *      flags, access rights, and limits in the uDB are updated to the
 *      actual values.
 *
 *      THIS COMMAND IS SERIALIZED !!
 *
 *      This function must be locked when in use because it accesses a
 *      debuggee tcb.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Tid = Nonzero Thread ID of register set to write
 *              uDB.Cmd = DBG_C_WriteReg
 *
 *              The register set in the uDB should contain the desired values.
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *                      uDB.Tid = Thread ID corresponding to register set.
 *                      Access Rights, Limits, and EFlags updated
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Error Writing Registers (Invalid segment register)
 *                      uDB.Value = Error Code
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *      Pseudocode:
 *      take debuggee semaphore
 *      if (invalid TID)
 *        error = ERROR_INVALID_THREADID
 *      for each selector
 *        dbgVerifySel(selector)
 *      eflags = (original eflags & RESERVED_FLAGS)
 *               | (user eflags & ~RESERVED_FLAGS)
 *      set all other registers
 *      read registers to fill in correct selector information
 *      clear debuggee semaphore
 */

#pragma code_seg("DEBUGLOCK_TEXT", "CODE")      /* lock DEBUG code */

int DbgWriteReg (kDB_t *pkDB, DCB_t *pdcb)
{
    tcb_t *ptcb;                        // debuggee thread's pTCB
    KMREG kmreg;                        // debuggee thread's register set
    int rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgWriteReg      ");   }
#endif

    if ((rt = DbgRequestSem(pdcb)) != NO_ERROR)
        return(rt);

    if ((rt = TKTidTo_pTCB(pdcb->pptdaDbgee, pkDB->uDBTemp.Tid,
                           SSToDS(&ptcb))) != NO_ERROR)
        rt = ERROR_INVALID_THREADID;

    else if (dbgVerifySel(pkDB->uDBTemp.CS, pdcb->pptdaDbgee->ptda_handle) ||
             dbgVerifySel(pkDB->uDBTemp.DS, pdcb->pptdaDbgee->ptda_handle) ||
             dbgVerifySel(pkDB->uDBTemp.ES, pdcb->pptdaDbgee->ptda_handle) ||
             dbgVerifySel(pkDB->uDBTemp.FS, pdcb->pptdaDbgee->ptda_handle) ||
             dbgVerifySel(pkDB->uDBTemp.GS, pdcb->pptdaDbgee->ptda_handle) ||
             dbgVerifySel(pkDB->uDBTemp.SS, pdcb->pptdaDbgee->ptda_handle))
        rt = ERROR_PROTECTION_VIOLATION;

    else if ((rt = KMQueryClientRegister(ptcb, KM_REG_EFLAG,
                                         SSToDS(&kmreg.kmreg_eflag))) !=
             NO_ERROR)
            dbgAssert(FALSE);

    else {
        kmreg.kmreg_eflag = (pkDB->uDBTemp.EFlags & ~DBG_RESERVED_FLAGS) |
                            (kmreg.kmreg_eflag & DBG_RESERVED_FLAGS);
        kmreg.kmreg_eax = pkDB->uDBTemp.EAX;
        kmreg.kmreg_ebx = pkDB->uDBTemp.EBX;
        kmreg.kmreg_ecx = pkDB->uDBTemp.ECX;
        kmreg.kmreg_edx = pkDB->uDBTemp.EDX;
        kmreg.kmreg_esi = pkDB->uDBTemp.ESI;
        kmreg.kmreg_edi = pkDB->uDBTemp.EDI;
        kmreg.kmreg_ebp = pkDB->uDBTemp.EBP;
        kmreg.kmreg_esp = pkDB->uDBTemp.ESP;
        kmreg.kmreg_eip = pkDB->uDBTemp.EIP;
        kmreg.kmreg_cs = pkDB->uDBTemp.CS;
        kmreg.kmreg_ds = pkDB->uDBTemp.DS;
        kmreg.kmreg_es = pkDB->uDBTemp.ES;
        kmreg.kmreg_fs = pkDB->uDBTemp.FS;
        kmreg.kmreg_gs = pkDB->uDBTemp.GS;
        kmreg.kmreg_ss = pkDB->uDBTemp.SS;

        if (KMSetAllClientRegisters(ptcb, SSToDS(&kmreg)))
            dbgAssert(FALSE);
    }

    DbgClearSem(pdcb);

    if (rt != NO_ERROR) {
        pkDB->uDBTemp.Cmd = DBG_N_Error;
        pkDB->uDBTemp.Value = rt;
        rt = NO_ERROR;
    } else
        rt = DbgReadReg(pkDB, pdcb);

    return(rt);
}

/***LP  DbgGo - Perform the DBG_C_Go command
 *
 *      All non-frozen threads of the debuggee are allowed to run user
 *      code at once.  If all of the debuggee threads are frozen, an
 *      error is returned.
 *
 *      The Go command completes when a DosDebug event (such as a
 *      Breakpoint) occurs. This can be any one of the notifications
 *      listed below.
 *
 *      When the next DosDebug event occurs, all threads in the
 *      debuggee process are marked to not run any additional user code
 *      until the next Go command is issued.  This provides a stable
 *      environment for debugging.
 *
 *      When the Go command returns, the register set is automatically
 *      updated to reflect the thread which detected the event.
 *
 *      This function is swappable.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Cmd = DBG_C_Go
 *
 *      API EXIT
 *              Any Debug Notification is possible.
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *                      kDB modified by the debuggee as needed
 *
 *              Return Value != 0
 *                      Fatal Error - Propogate Return Value
 *
 *      Pseudocode:
 *      verify command is legal
 *      take debuggee semaphore
 *      if (number of frozen threads == number of debuggee threads)
 *        error = ERROR_INVALID_THREADID
 *      else
 *        clear single step flag
 *        DbgCall to run debuggee
 *      clear debuggee semaphore
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

int DbgGo (kDB_t *pkDB, DCB_t *pdcb)
{
    int rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgGo            ");   }
#endif

    if ((rt = DbgRequestSem(pdcb)) != NO_ERROR)
        return(rt);

    if (pdcb->sFrozen == pdcb->pptdaDbgee->ptda_cTCB) {
        pkDB->uDBTemp.Cmd = DBG_N_Error;
        pkDB->uDBTemp.Value = ERROR_INVALID_THREADID;
    } else if (pdcb->pptdaDbgee->ptda_flDbg & DBGP_XCPTPENDING) {
        pkDB->uDBTemp.Cmd = DBG_N_Error;
        pkDB->uDBTemp.Value = ERROR_INVALID_FUNCTION;
    } else {
        pdcb->pptdaDbgee->ptda_flDbg &= ~DBGP_SSTEP;
        rt = DbgCall(pkDB, pdcb);
    }

    DbgClearSem(pdcb);

    return(rt);
}

/***LP  DbgTerm - Perform the DBG_C_Term command
 *
 *      The debuggee process is terminated immediately.
 *
 *      No additional DosDebug commands or notifications will be
 *      allowed to this process.  Outstanding memory aliases and
 *      watchpoints will be invalidated automatically.
 *
 *      Debuggee ExitList processing will be attempted, but any
 *      unexpected DosDebug event (such as a Breakpoint) during this
 *      period will cause the process to terminate without completing
 *      ExitList processing.  Data watchpoints will be automatically
 *      cleared before attempting ExitList processing, for this reason.
 *
 *      If the Terminate command is issued during ExitList processing,
 *      ExitList processing will terminate immediately, without
 *      continuing the ExitList routines.
 *
 *      This function is swappable.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Cmd = DBG_C_Term
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *                      kDB modified by the debuggee as needed
 *
 *              Return Value != 0
 *                      Fatal Error - Propogate Return Value
 *
 *      Pseudocode:
 *      take debuggee semaphore
 *      mark debuggee dcb as dead (DBGDCBS_DBGEEDEAD)
 *      mark debuggee as terminated by debugger (DBGP_MANUALTERM)
 *      clear single step flag
 *      TKDieSoonPTDA to mark debuggee to die
 *      run debuggee
 *      clear debuggee semaphore
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

int DbgTerm (kDB_t *pkDB, DCB_t *pdcb)
{
    int rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgTerm          ");   }
#endif

    if ((rt = DbgRequestSem(pdcb)) != NO_ERROR)
        return(rt);

    pdcb->flState |= DBGDCBS_DBGEEDEAD;
    pdcb->pptdaDbgee->ptda_flDbg |= DBGP_MANUALTERM;
    pdcb->pptdaDbgee->ptda_flDbg &= ~DBGP_SSTEP;
    TKDieSoonPTDA(pdcb->pptdaDbgee, 0, TC_SIGNAL, 0);
    TKRun(DBGID_EELOCK(pdcb), NULL);

    DbgClearSem(pdcb);

    return(NO_ERROR);
}

/***LP  DbgSStep - Perform the DBG_C_SStep and DBG_C_RStep commands
 *
 *      Most likely, a SStep or RStep notification will be returned, but
 *      any notification may be returned. See the section on notifications.
 *
 *      When the SStep or RStep command returns, the register set is
 *      automatically updated to reflect the thread which detected the
 *      event.
 *
 *      Callgates that result in a privilege level transition to ring 0
 *      will appear to single-step as a single instruction, with the
 *      single-step occurring just after the function completes.  This
 *      hides ring 0 execution from debuggers.
 *
 *      Attempting to Step any thread which is frozen will result in an error.
 *
 *      Restrictions :
 *
 *      To accomplish callgate single-stepping, the single-step must be
 *      simulated because the flags (specifically, the TF bit) are not
 *      stored in the ring 0 callgate stack frame.  Because of this, a
 *      single-step which results in leaving a ring 0 callgate will
 *      sometimes not execute any user-space code.  The following
 *      single-step should function normally, however.
 *
 *      Single-Stepping some REP instructions may not work perfectly
 *      due to 386 hardware errata.  See errata listings for details.
 *
 *      If Tid==0, all threads will be marked to single-step at once,
 *      and the first thread to be scheduled to run user-space code
 *      will single-step.  No other threads will single-step.
 *
 *      For the Range Step command, TID must not be zero.
 *
 *      This function must be locked when in use because it accesses
 *      debuggee tcbs.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Tid = Thread ID of thread to single-step.
 *              uDB.Cmd = DBG_C_SStep or DBC_C_RStep
 *
 *      API EXIT
 *              Any Debug Notification is possible.
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *                      kDB modified by the debuggee as needed
 *
 *              Return Value != 0
 *                      Fatal Error - Propogate Return Value
 *
 *      Pseudocode:
 *
 *      take debuggee semaphore
 *      if (command == DBC_C_RStep)
 *        if (invalid Tid or thread Tid is frozen or dead)
 *          error = ERROR_INVALID_TID
 *        set TK_FF_TRC on all other debugee threads
 *        set thread Tid to single step out of the kernel
 *        set initial values for last instruction executed, range start, and
 *          range end
 *        set range step and single step flag
 *        DbgCall to execute debuggee
 *      else // command == DBG_C_SStep
 *        if (Tid != 0)
 *          if (invalid Tid or thread Tid is frozen or dead)
 *            error = ERROR_INVALID_TID
 *          set TK_FF_TRC on all other debuggee threads
 *          set thread Tid to single step out of the kernel
 *        set single step flag
 *        DbgCall to execute debuggee
 *      clear debuggee semaphore
 */

#pragma code_seg("DEBUGLOCK_TEXT", "CODE")      /* lock DEBUG code */

int DbgSStep (kDB_t *pkDB, DCB_t *pdcb)
{
    int rt;
    tcb_t *pTidTCB = NULL,
          *pTCB;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgSStep         ");   }
#endif

    if (rt = DbgRequestSem(pdcb))
        return(rt);

    if (pdcb->pptdaDbgee->ptda_flDbg & DBGP_XCPTPENDING) {
        pkDB->uDBTemp.Cmd = DBG_N_Error;
        pkDB->uDBTemp.Value = ERROR_INVALID_FUNCTION;
    } else if (pkDB->uDBTemp.Tid != 0) {
        for (pTCB = pdcb->pptdaDbgee->ptda_pTCBHead; pTCB != NULL;
           pTCB = pTCB->TCBpTCBNext) {

            if ((ulong_t) pTCB->TCBOrdinal == pkDB->uDBTemp.Tid)
                pTidTCB = pTCB;
            else
                pTCB->TCBForcedActions |= TK_FF_TRC;
        }

        if (pTidTCB == NULL || (pTidTCB->TCB_flDbg & (DBGT_FROZEN|DBGT_DEAD))) {
            pkDB->uDBTemp.Cmd = DBG_N_Error;
            pkDB->uDBTemp.Value = ERROR_INVALID_THREADID;
        } else
            pdcb->sDbgerSStepTCB = pTidTCB;

        if (pkDB->lCmd == DBG_C_RangeStep) {

            //  Set up the initial range step values - the start and end of
            //  the range are given by the caller, and the linear address
            //  of the last instruction executed is just the current CS:EIP.

            if ((rt = dbgUserCSEIP(pTidTCB,
              &(pTidTCB->TCBpTSD->TSDlDbgLastAddr),NULL,NULL)) == NO_ERROR) {    // B729833
                pTidTCB->TCBpTSD->TSDlDbgRangeStart = pkDB->uDBTemp.Value;
                pTidTCB->TCBpTSD->TSDlDbgRangeEnd = pkDB->uDBTemp.Addr;
                pdcb->pptdaDbgee->ptda_flDbg |= DBGP_RANGESTEP;
            } else {
                pkDB->uDBTemp.Cmd = DBG_N_Error;
                pkDB->uDBTemp.Value = rt;
            }
        }

    } else if (pkDB->lCmd == DBG_C_RangeStep ||
               pdcb->sFrozen == pdcb->pptdaDbgee->ptda_cTCB) {
        pkDB->uDBTemp.Cmd = DBG_N_Error;
        pkDB->uDBTemp.Value = ERROR_INVALID_THREADID;
    }

    if (pkDB->uDBTemp.Cmd == NO_ERROR) {
        pdcb->pptdaDbgee->ptda_flDbg |= DBGP_SSTEP;
        rt = DbgCall(pkDB, pdcb);
    }

    DbgClearSem(pdcb);

    return(rt);
}

/***LP  DbgStop - Perform the DBG_C_Stop command
 *
 *      The function performed by this command depends on the current
 *      state of the debuggee process.
 *
 *      --  If debuggee is already stopped
 *
 *          If there is a pending notification from the current thread,
 *          it is  returned.
 *
 *          If there is no pending notification from the current
 *          thread, success is returned.
 *
 *      --  If the debuggee is running user code
 *
 *          The debuggee is marked to stop before the next time it is
 *          ready to run user-space (ring  2 or 3) code.  This is known
 *          as an Asynchronous Stop command.
 *
 *          Kernel operations will not be interrupted for this
 *          Asynchronous Stop command.  That is, threads blocked in the
 *          kernel (via a semaphore or internal operation) will not be
 *          interrupted.  However, an infinite loop in user space will
 *          be stopped.
 *
 *      This function must be locked when in use because it modifies the
 *      the debuggee ptda.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Cmd = DBG_C_Stop
 *
 *      API EXIT
 *              Any Debug Notification is possible.
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *                      kDB modified by the debuggee as needed
 *
 *              Return Value != 0
 *                      Fatal Error - Propogate Return Value
 *
 *      Pseudocode:
 *      if debuggee is running
 *        set asynchronous stop flag
 *        set debug forced actions flag
 *      take debuggee semaphore
 *      set panic stop flag
 *      DbgCall to get notification
 *      clear debuggee semaphore
 */

#pragma code_seg("DEBUGLOCK_TEXT", "CODE")      /* swap DEBUG code */

int DbgStop (kDB_t *pkDB, DCB_t *pdcb)
{
    int rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgStop          ");   }
#endif

    if (pdcb->sActiveTid == 0) {                // debuggee is running
        pdcb->pptdaDbgee->ptda_flDbg |= DBGP_ASYNC;
        pdcb->pptdaDbgee->ptda_ForcedActions |= TK_FF_TRC;
    }

    if ((rt = DbgRequestSem(pdcb)) != NO_ERROR)
        return(rt);

    pdcb->pptdaDbgee->ptda_flDbg |= DBGP_PANIC;
    rt = DbgCall(pkDB, pdcb);

    DbgClearSem(pdcb);

    return(rt);
}

/***LP  DbgFreezeThaw - Perform the DBG_C_Freeze and DBG_C_Resume commands
 *
 *      Freeze command :
 *
 *      The desired thread(s) are prevented from running user code on a
 *      Go or SStep command.
 *
 *      By using the Freeze and Resume commands, a given set of threads
 *      can be run at once, while keeping the other threads suspended.
 *
 *      No error is returned if the thread was previously frozen - it
 *      just remains frozen.  Freeze and Resume commands do not nest.
 *
 *      Resume Command :
 *
 *      The resume command complements the freeze command.  A thread
 *      which has been resumed will function as if it was never frozen.
 *
 *      No error will be returned if the thread was not previously
 *      frozen.
 *
 *      The count of frozen debuggee threads is also updated.
 *
 *      This function must be locked when in use because it accesses debuggee
 *      tcbs.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Tid = Thread ID of thread to freeze.
 *              uDB.Cmd = DBG_C_Freeze
 *          OR  uDB.Cmd = DBG_C_Resume
 *
 *      If Tid==0, all debuggee threads will be frozen or thawed.
 *
 *      If the Tid was zero, it will be set to the Tid of the debuggee
 *      thread most recently scheduled to run.
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Error Freezing or Thawing (Tid invalid)
 *                      uDB.Value = Error Code
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *      Pseudocode:
 *      take debuggee semaphore
 *      if (Tid == 0)
 *        if (Command == DBG_C_Freeze)
 *          freeze all threads, update frozen count
 *        else // Command == DBG_C_Thaw
 *          thaw all threads, update frozen count
 *      else // Tid != 0
 *        if (invalid Tid)
 *          error = ERROR_INVALID_TID
 *        else if (Command == DBG_C_Freeze)
 *          freeze thread, update frozen count
 *        else // Command == DBG_C_Thaw
 *          thaw thread, update frozen count
 *      clear debuggee semaphore
 */

#pragma code_seg("DEBUGLOCK_TEXT", "CODE")      /* lock DEBUG code */

int DbgFreezeThaw (kDB_t *pkDB, DCB_t *pdcb)
{
    tcb_t *ptcb;                                // debuggee threads
    int rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgFreezeThaw    ");   }
#endif

    if ((rt = DbgRequestSem(pdcb)) != NO_ERROR)
        return(rt);

    if (pkDB->uDBTemp.Tid == 0) {
        ptcb = pdcb->pptdaDbgee->ptda_pTCBHead;

        while (ptcb != NULL) {
            if (pkDB->lCmd == DBG_C_Freeze) {
                if (!(ptcb->TCB_flDbg & DBGT_FROZEN)) {
                    ptcb->TCB_flDbg |= DBGT_FROZEN;
                    ptcb->TCBForcedActions |= TK_FF_TRC;
                    pdcb->sFrozen++;
                }
            } else if (ptcb->TCB_flDbg & DBGT_FROZEN) {
                ptcb->TCB_flDbg &= ~DBGT_FROZEN;
                pdcb->sFrozen--;
            }

            ptcb = ptcb->TCBpTCBNext;
        }

        dbgAssert(pdcb->sFrozen == (pkDB->lCmd == DBG_C_Freeze
                            ? pdcb->pptdaDbgee->ptda_cTCB : (ushort_t) 0));

        pkDB->uDBTemp.Tid = pdcb->sActiveTid;
    } else {
        if (TKTidTo_pTCB(pdcb->pptdaDbgee, pkDB->uDBTemp.Tid, SSToDS(&ptcb))) {
            pkDB->uDBTemp.Cmd = DBG_N_Error;
            pkDB->uDBTemp.Value = ERROR_INVALID_THREADID;
        } else {
            if (pkDB->lCmd == DBG_C_Freeze) {
                if (!(ptcb->TCB_flDbg & DBGT_FROZEN)) {
                    ptcb->TCB_flDbg |= DBGT_FROZEN;
                    ptcb->TCBForcedActions |= TK_FF_TRC;
                    pdcb->sFrozen++;
                }
            } else if (ptcb->TCB_flDbg & DBGT_FROZEN) {
                ptcb->TCB_flDbg &= ~DBGT_FROZEN;
                pdcb->sFrozen--;
            }
        }
    }

    DbgClearSem(pdcb);

    return(NO_ERROR);
}

/***LP  DbgNumToAddr - Perform the DBG_C_NumToAddr command
 *
 *      The logical object number in Value is converted into an Address
 *      that points to the starting address of the desired logical
 *      object of the specified module in the debuggee's memory space.
 *      This address is then stored in the Addr field, in the form of a
 *      linear address.
 *
 *      The Value and MTE fields will be left unchanged.
 *
 *      The logical object numbers for a module are generated at link
 *      time.  By using this function, a debugger can discern the
 *      relationship between addresses and logical object numbers.
 *      Once the logical object number is known, symbols can be
 *      generated for an address via a map or symbol file, for symbolic
 *      debugging.
 *
 *      This function is swappable.
 *
 *      API ENTRY
 *
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Cmd = DBG_C_NumToAddr
 *              uDB.Value = Logical object number in module
 *              uDB.MTE = MTE handle of module of interest
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *                      uDB.Addr = Starting Address of object
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Error Converting Address
 *                      uDB.Value = Error Code
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *      Pseudocode:
 *      LDRNumToAddr(MTE, Object number)
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

int DbgNumToAddr (kDB_t *pkDB, DCB_t *pdcb)
{
    int rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgNumToAddr     ");   }
#endif

    pkDB->uDBTemp.Addr = (ULONG)pdcb->pptdaDbgee;   /* debuggee PTDA   A002 */

    if ((rt = LDRNumtoAddr((ushort_t) pkDB->uDBTemp.MTE, pkDB->uDBTemp.Value,
                           &(pkDB->uDBTemp.Addr))) != NO_ERROR) {
        pkDB->uDBTemp.Cmd = DBG_N_Error;
        pkDB->uDBTemp.Value = rt;
    }

    return(NO_ERROR);
}

/***LP  DbgCoRegs - Perform the DBG_C_ReadCoRegs and DBG_C_WriteCoRegs commands
 *
 *      ReadCoRegs command :
 *
 *      The debugger's coprocessor register context buffer is filled
 *      in with a copy of the registers read from the appropriate
 *      coprocessor, for the thread specified in the Tid field.
 *
 *      WriteCoRegs command :
 *
 *      The debuggee thread's coprocessor registers are filled with the
 *      values passed via the coprocessor register context buffer, for
 *      the thread specified in the Tid field.
 *
 *      If an error occurs while attempting to access the coprocessor
 *      context during this command, the DBG_N_CoError notification
 *      will be returned.
 *
 *      If Tid==0, the register set comes from the debuggee thread which
 *      was most recently scheduled to run.
 *
 *      The coprocessor type identifier is a number that identifies the
 *      format of the coprocessor register context buffer.  The buffer
 *      length must correspond exactly to the requested buffer format.
 *
 *      The supported coprocessor types, formats and lengths include
 *      the following :
 *
 *      80387 NPX :     Value = DBG_CO_387 = 1
 *                      Len = 108
 *
 *      The coprocessor register context buffer format is same as that
 *      defined by the fsave/frestore instructions as executed by the
 *      appropriate processor.
 *
 *      This function must be locked when in use because it accesses debuggee
 *      tcbs.
 *
 *      Restrictions :
 *
 *      An error will be returned if the debuggee process is emulating
 *      the coprocessor, if the desired debuggee thread has not yet
 *      attempted to use the coprocessor, the wrong coprocessor type is
 *      used, or Index is not zero.
 *
 *      The coprocessor may adjust some control register bits, but
 *      DosDebug will not return an error if a modification is
 *      attempted, nor will it mask the values.  Because of internal
 *      coprocessor management, this adjustment may be delayed until
 *      the thread actually uses the coprocessor again.
 *
 *      API ENTRY
 *              uDB.Cmd = DBG_C_ReadCoRegs
 *              uDB.Pid = Process ID of debuggee processor
 *              uDB.Tid = Thread ID of Coprocessor register set to read.
 *                        If Tid is 0, use the Tid of the 'active' debuggee
 *                        thread, if any.
 *              uDB.Value = Coprocessor Type Identifier
 *              uDB.Buffer = Pointer to Coprocessor Register Context Buffer
 *              uDB.Len =  Size of Coprocessor Register Context Buffer
 *              uDB.Index = Reserved, must be Zero
 *
 *          OR  uDB.Cmd = DBG_C_WriteCoRegs
 *              uDB.Pid = Process ID of debuggee processor
 *              uDB.Tid = Thread ID of Coprocessor register set to write.
 *                        Must be nonzero.
 *              uDB.Value = Coprocessor Type Identifier
 *              uDB.Buffer = Pointer to Coprocessor Register Context Buffer
 *              uDB.Len =  Size of Coprocessor Register Context Buffer
 *              uDB.Index = Reserved, must be Zero
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *
 *              uDB.Cmd = DBG_N_CoError
 *                      uDB.Value = Error Code
 *                      Error Accessing Coprocessor registers
 *                              - Debuggee Emulating
 *                              - Incorrect Parameters
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *              Return Value != 0
 *                      Error
 *
 *      Pseudocode:
 *      verify Value, Len, Index
 *      if (Tid == 0 && Command == DBG_C_ReadCoRegs)
 *        Tid = active debuggee thread Tid
 *      if (invalid Tid)
 *        error
 *      else if (Command == DBG_C_ReadCoRegs)
 *        NPXReadContext
 *        copy context to user buffer
 *      else // Command == DBG_C_WriteCoRegs)
 *        take debuggee semaphore
 *        copy user buffer to context
 *        NPXWriteContext
 *        clear debuggee semaphore
 *
 *      return possible error in debug buffer (except for ERROR_INTERRUPT)
 */

#pragma code_seg("DEBUGLOCK_TEXT", "CODE")      /* lock DEBUG code */

int DbgCoRegs (kDB_t *pkDB, DCB_t *pdcb)
{
    tcb_t *ptcb;
    char achTemp[DBG_LEN_387];
    int rtUser = NO_ERROR;
    int rtNPX = NO_ERROR;
    int rtFatal = NO_ERROR;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgCoRegs        ");   }
#endif

    if (pkDB->uDBTemp.Value != DBG_CO_387 ||
        pkDB->uDBTemp.Len != DBG_LEN_387 ||
        pkDB->uDBTemp.Index != 0)
        rtUser = ERROR_INVALID_DATA;
    else {
        if (pkDB->uDBTemp.Tid == 0 && pkDB->lCmd == DBG_C_ReadCoRegs)
            //  If the debuggee is stopped, sActiveTid will be current
            //  debuggee thread id.  If the debuggee is running,
            //  sActiveTid will be zero and will cause TKTidTo_pTCB to
            //  return an error.

            pkDB->uDBTemp.Tid = pdcb->sActiveTid;

        rtUser = TKTidTo_pTCB(pdcb->pptdaDbgee, pkDB->uDBTemp.Tid,
                                  SSToDS(&ptcb));
    }

    if (rtUser == NO_ERROR) {
        if (ptcb->TCB_flDbg & DBGT_DEAD)
            rtUser = ERROR_INVALID_THREADID;

        else if (pkDB->lCmd == DBG_C_ReadCoRegs) {
            if ((rtNPX = NPXReadContext(ptcb, SSToDS(achTemp),
                                        &pkDB->uDBTemp.Len,
                                        pkDB->uDBTemp.Value)) == NO_ERROR)
                rtFatal = TKSuBuff((char *)pkDB->uDBTemp.Buffer,
                                   SSToDS(achTemp),
                                   pkDB->uDBTemp.Len, TK_UM_FATAL);

        } else if ((rtFatal = DbgRequestSem(pdcb)) == NO_ERROR) {
            if ((rtFatal = TKFuBuff(SSToDS(achTemp),
                                    (char *)pkDB->uDBTemp.Buffer,
                                    pkDB->uDBTemp.Len,
                                    TK_UM_FATAL)) == NO_ERROR)
                rtNPX = NPXWriteContext(ptcb, SSToDS(achTemp),
                                        &pkDB->uDBTemp.Len,
                                        pkDB->uDBTemp.Value);
            DbgClearSem(pdcb);
        }
    }

    if (rtNPX != NO_ERROR) {
        pkDB->uDBTemp.Cmd = DBG_N_CoError;
        pkDB->uDBTemp.Value = rtNPX;
    } else {
        if (rtUser != NO_ERROR) {
            pkDB->uDBTemp.Cmd = DBG_N_Error;
            pkDB->uDBTemp.Value = rtUser;
        } else
            pkDB->uDBTemp.Tid = ptcb->TCBOrdinal;
    }

    return (rtFatal);
}

/***LP  DbgThrdStat - Perform the DBG_C_ThrdStat command
 *
 *      The Value field will be filled in with the Thread ID of the
 *      'next' thread to look at, when traversing threads.
 *
 *      By repeatedly calling the ThrdStat command, replacing the Tid
 *      with the last returned Value, until a thread ID is repeated,
 *      all threads in the process can be traversed.  When used in this
 *      way, the TIDs returned by the ThrdStat command form a loop of
 *      the debuggee's thread IDs.
 *
 *      If Tid==0, the status of the debuggee thread most recently
 *      scheduled to run will be returned.
 *
 *      This function must be locked when in use because it accesses debuggee
 *      tcbs.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.TID = Thread ID of thread of interest.
 *              uDB.Cmd = DBG_C_ThrdStat
 *              uDB.Buffer = Pointer to Thread Status Buffer
 *              uDB.Len = 4 = Length of Thread Status Buffer, in bytes
 *
 *      see dbgapi.h for a description of the thread status buffer.
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *                      uDB.Value = Thread ID of 'next' active thread
 *                      uDB.Tid = Thread ID matching status
 *                      Thread Status Buffer Filled in.
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Error Getting Thread Status (Tid invalid)
 *                      uDB.Value = Error Code
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *      Pseudocode:
 *      if (Tid == 0)
 *        if (there is an active debuggee thread)
 *          Tid = active debuggee thread Tid
 *        else
 *          Tid = first thread's Tid
 *      if (invalid Tid)
 *        error
 *      else
 *        if (thread is frozen)
 *          DbgState = DBG_D_Frozen
 *        else
 *          DbgState = DBG_D_Thawed
 *        switch (thread state)
 *          case STATE_SUSPENDED:
 *            TState = DBG_T_Suspended
 *            break
 *          case STATE_CRITSEC:
 *            TState = DBG_T_CritSec
 *            break
 *          case STATE_BLOCKED:
 *            if (thread is not block on debug semaphore)
 *              TState = DBG_T_Blocked
 *              break
 *          default
 *            TState = DBG_T_Runnable
 *        TPriority = tkGetPriority
 *        Value = next thread's Tid
 *        copy data to user buffer
 */

#pragma code_seg("DEBUGLOCK_TEXT", "CODE")      /* lock DEBUG code */

int DbgThrdStat (kDB_t *pkDB, DCB_t *pdcb)
{
    TStat_t DbgTStatTemp;
    tcb_t *ptcb;
    int rt = NO_ERROR;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgThrdStat      ");   }
#endif

    if (pkDB->uDBTemp.Tid == 0) {
        if (pdcb->sActiveTid != 0)
            pkDB->uDBTemp.Tid = pdcb->sActiveTid;
        else
            pkDB->uDBTemp.Tid = pdcb->pptdaDbgee->ptda_pTCBHead->TCBOrdinal;
    }

    if ((rt = TKTidTo_pTCB(pdcb->pptdaDbgee, pkDB->uDBTemp.Tid,
                           SSToDS(&ptcb))) != NO_ERROR) {
        pkDB->uDBTemp.Cmd = DBG_N_Error;
        pkDB->uDBTemp.Value = rt;
        return(NO_ERROR);
    }

    if (ptcb->TCB_flDbg & DBGT_FROZEN)
        DbgTStatTemp.DbgState = DBG_D_Frozen;
    else
        DbgTStatTemp.DbgState = DBG_D_Thawed;

    switch(ptcb->TCBState) {
        case STATE_SUSPENDED:
        case STATE_FROZEN:
            DbgTStatTemp.TState = DBG_T_Suspended;
            break;
        case STATE_CRITSEC:
            DbgTStatTemp.TState = DBG_T_CritSec;
            break;
        case STATE_BLOCKED:
            if (!(ptcb->TCB_flDbg & DBGT_FREEZING)) {
                DbgTStatTemp.TState = DBG_T_Blocked;
                break;
            }
            // ELSE FALL THROUGH TO DBG_T_Runnable
        // case STATE_VOID
        // case STATE_READY
        // case STATE_RUNNING
        // case STATE_READYBOOST
        // case STATE_TSD
        // case STATE_DELAYED
        // case STATE_GETSTACK
        // case STATE_BADSTACK
        default:
            DbgTStatTemp.TState = DBG_T_Runnable;
    }

    DbgTStatTemp.TPriority = (ushort_t) TKGetPriority(ptcb);

    if (ptcb->TCBpTCBNext)
        ptcb = ptcb->TCBpTCBNext;
    else
        ptcb = pdcb->pptdaDbgee->ptda_pTCBHead;

    pkDB->uDBTemp.Value = (ulong_t) ptcb->TCBOrdinal;

    if ((rt = TKSuBuff((char *)pkDB->uDBTemp.Buffer,
                      (char *)SSToDS(&DbgTStatTemp),
                      sizeof(TStat_t), TK_UM_FATAL)) != NO_ERROR) {
        if (rt != ERROR_INTERRUPT) {
            pkDB->uDBTemp.Cmd = DBG_N_Error;
            pkDB->uDBTemp.Value = rt;
            rt = NO_ERROR;
        }
    }

    return(rt);
}

/***LP  DbgAlias - Perform the DBG_C_***Alias commands
 *
 *      DbgAlias performs the DBG_C_MapROAlias, DBG_C_MapRWAlias, and
 *      DBG_C_UnMapAlias commands.
 *
 *      Map Read-Only or Read-Write Memory Alias Commands :
 *
 *      An alias to the debuggee's memory region of the requested
 *      length is mapped into the debugger's memory space.  This region
 *      is reserved for use as an alias region until it is unmapped.
 *
 *      The access rights for the alias area are determined by the
 *      command number.  The DBG_C_MapROAlias command maps a read-only
 *      alias region, while the DBG_C_MapRWAlias command maps a
 *      read-write alias region.
 *
 *      For read write aliases, if the region is shared and read-only
 *      in the debuggee's context, a private copy of the aliased pages
 *      will be created in the debuggee's context, and DosDynamicTrace
 *      tracepoints will be removed from that region.  This prevents
 *      debugging from affecting other areas of the system, while
 *      allowing access to shared memory areas, and proper disassembly
 *      of regions where DosDynamicTrace is in use.  DosDynamicTrace
 *      will continue to operate in the context of other processes.
 *
 *      Because the read-write aliases may convert objects to private,
 *      using up system resources, it is recommended that read-only
 *      aliases be used when simply perusing memory.  See below
 *      restrictions regarding read-only aliases on the 80386.
 *
 *      Because the entire aliased region may map both valid and
 *      invalid regions of memory, it is recommended that debuggers use
 *      the DosMemoryRegion API call just before accessing the alias
 *      region to determine if the region is valid.  Although possible,
 *      it is not recommended that debuggers access this region while
 *      the debuggee is running, as portions of this region may become
 *      invalid without notifying the debugger.  It is possible that no
 *      valid pages will exist in the alias region.
 *
 *      When the debuggee frees an aliased object, or shrinks the
 *      underlying object such that the alias would span a region
 *      outside the resultant object, an alias-free notification is
 *      returned to the debugger.  This notification will be returned
 *      before the alias is invalidated.  See the notification section
 *      for details.
 *
 *      These commands may be performed while the debuggee is executing
 *      code via a Go command.
 *
 *      UnMap Memory Alias Command :
 *
 *      The UnMapAlias command is used when the debugger has finished
 *      using an alias region.  Both read-only and read-write aliases
 *      may be freed in this way.
 *
 *      Regions returned from other memory management calls may not be
 *      used.
 *
 *      When the debuggee process finally terminates, all aliases to
 *      its memory space will be invalidated.  Likewise, when a
 *      debugger terminates, all aliases from its memory space will
 *      also be invalidated.
 *
 *      Restrictions :
 *
 *      Most memory management calls may not be used on these aliases.
 *      DosQueryMem may be used.
 *
 *      The passed starting addresses must be aligned on a page
 *      boundary, and the length of the aliased region must be a
 *      multiple of the page size.
 *
 *      Aliased regions must be completely contained within a single
 *      debuggee memory object.
 *
 *      Aliases will only be permitted to the the user space memory
 *      region of the debuggee.  No aliases will be provided to system
 *      space.
 *
 *      The alias region will only be provided at the linear level.  No
 *      debugger LDT selector will be available to access the alias
 *      region.
 *
 *      This function is swappable.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Cmd = DBG_C_MapROAlias (Read Only) (NOT ALWAYS SUPPORTED)
 *              uDB.Cmd = DBG_C_MapRWAlias (Read Write)
 *              uDB.Buffer = Reserved, must be Zero.
 *              uDB.Addr = Start of debuggee region to alias (Page-Aligned)
 *              uDB.Len = Requested length of alias region (Page-Multiple)
 *
 *          OR
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Cmd = DBG_C_UnMapAlias
 *              uDB.Buffer = Address of the debugger alias region to unmap.
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *
 *                      Map Read-Only or Read-Write Memory Alias Commands :
 *
 *                              uDB.Buffer = Address of the start of the
 *                                           debugger alias region
 *
 *                      UnMap Memory Alias Command :
 *
 *                              No additional information.
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Aliasing Error
 *                      uDB.Value = Error Code
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *              Return Value != 0
 *                      ERROR_INTERRUPT from LDRGetSem()
 *
 *      Pseudocode:
 *      if (Command == DBG_C_UnMapAlias)
 *        VMFreeMem(alias address, ptda handle)
 *      else
 *        verify debuggee address, alias size
 *        create alias
 *
 *      return possible error in debug buffer (except for ERROR_INTERRUPT)
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

int DbgAlias (kDB_t *pkDB, DCB_t *pdcb)
{
    int rt;
    VMAC vmret;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgAlias         ");   }
#endif

    if (pkDB->lCmd == DBG_C_UnMapAlias)
        rt = VMFreeMem((ulong_t)pkDB->uDBTemp.Buffer, pPTDACur->ptda_handle,
                       NULL);

    else if (pkDB->uDBTemp.Buffer || (pkDB->uDBTemp.Addr & PAGEMASK) ||
        (!pkDB->uDBTemp.Len) || (pkDB->uDBTemp.Len & PAGEMASK))
        rt = ERROR_INVALID_PARAMETER;

    else if (VMSysArena(((ulong_t)pkDB->uDBTemp.Addr) +
              pkDB->uDBTemp.Len - 1L) ||
             (((ulong_t)pkDB->uDBTemp.Addr) + pkDB->uDBTemp.Len - 1L) <
             (ulong_t)pkDB->uDBTemp.Addr)
        rt = ERROR_INVALID_ACCESS;

    else if ((rt = LDRGetSem()) == NO_ERROR) {
        if ((rt = VMMapDebugAlias(pkDB->lCmd == DBG_C_MapRWAlias ?
                                   0 : VMDA_RO,
                                  pkDB->uDBTemp.Addr,
                                  pkDB->uDBTemp.Len,
                                  pdcb->pptdaDbgee->ptda_handle,
                                  SSToDS(&vmret))) == NO_ERROR)
            pkDB->uDBTemp.Buffer = vmret.ac_va;

        LDRClearSem();
    }

    if (rt != ERROR_INTERRUPT && rt != NO_ERROR) {
        pkDB->uDBTemp.Cmd = DBG_N_Error;
        pkDB->uDBTemp.Value = rt;
        rt = NO_ERROR;
    }

    return(rt);
}

/***LP  DbgConnect - Perform the DBG_C_Connect command
 *
 *      This command is used to establish a debugging connection.  It
 *      must be the initial command, as it verifies the buffer format
 *      for the rest of the connection.  No other DosDebug command will
 *      be accepted until the debugging connection has been established.
 *
 *      Because DosDebug usually cannot be ported to new machines
 *      without changing the format of the buffer, this command is
 *      needed to establish that the debugger is capable of handling
 *      the desired format.
 *
 *      If the requested debugging level is not supported, an error
 *      will be returned, and the connection will not be made.  This
 *      gives the debugger a chance to try again, or to automatically
 *      start a different debugger process which is tuned to a
 *      different buffer format.
 *
 *      For this command, only the machine-independent portion of the
 *      buffer will be examined, which includes the Pid, Tid, Cmd, and
 *      Value fields.  This makes it possible to port the DosDebug
 *      buffer from one machine to the next, without faulting the
 *      debugger on the initial DosDebug command.
 *
 *      No DosDebug notifications will be returned by this command
 *      except DBG_N_Success or DBG_N_Error.
 *
 *      Restrictions :
 *
 *      If the connection to the debuggee is not established within a
 *      reasonable ammount of time, it will be assumed that the
 *      connection will never be established, so the debuggee process
 *      will be terminated automatically.
 *
 *      The current format level may or may not be supported on future
 *      versions.  This is due to the machine dependence of the
 *      DosDebug function.
 *
 *      This function is swappable.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Tid = Reserved, must be Zero
 *              uDB.Cmd = DBG_C_Connect
 *              uDB.Value = DBG_L_386 - Debugging Level Number
 *              uDB.Addr  = DBGS_NO_SEVER (Do not Sever Connection)  A009
 *
 *      The only permitted debugging level for this version is DBG_L_386
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Connection Error
 *                      uDB.Value = Error Code
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      See uDB for error codes.
 *
 *      Pseudocode:
 *      take debuggee semaphore
 *      verify Pid, Value, Tid
 *      DCB debugger pid = current pid
 *      mark debuggee linked (DBGP_LINKED)
 *      mark current process as debugger (DBGP_ISADBGER)
 *      DbgCall to get first debuggee notification
 *      clear debuggee semaphore
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

int DbgConnect (kDB_t *pkDB, DCB_t *pdcb)
{
    int rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgConnect       ");   }
#endif

    if ((rt = DbgRequestSem(pdcb)) != NO_ERROR)
        return(rt);

    if ((pdcb->lDbgerPid != 0) || (pkDB->uDBTemp.Value != DBG_L_386) ||
        (pkDB->uDBTemp.Tid != 0)) {
        pkDB->uDBTemp.Cmd = DBG_N_Error;
        pkDB->uDBTemp.Value = ERROR_INVALID_DATA;

    } else {
        /* Set Default to always Sever the DEBUGGER and DEBUGGEE       A009 */
        pdcb->flSever = DBGS_SEVER;                   /* to SEVER      A009 */
        pdcb->flNoSever_Rpt = DBGS_NOSEVER_MSG_OFF;   /* init to zero  A009 */

        /* See if Sever is not wanted the DEBUGGER must handle hangs   A009 */
        if (pkDB->puDB->Addr == (ulong_t)DBGS_NO_SEVER)          /*    A009 */
           pdcb->flSever = DBGS_NO_SEVER;             /* Do Not Sever  A009 */

        pdcb->lDbgerPid = pPTDACur->ptda_Pid;
        pdcb->pptdaDbgee->ptda_flDbg |= DBGP_LINKED;
        pPTDACur->ptda_flDbg |= DBGP_ISADBGER;
        rt = DbgCall(pkDB, pdcb);
    }

    DbgClearSem(pdcb);

    return(rt);
}

/***LP  DbgSetWatch - Perform the DBG_C_SetWatch command
 *
 *      This command sets a code or data watchpoint of the desired
 *      scope and type to cover the specified range of addresses.
 *
 *      The Watchpoint Scope controls the context in which the
 *      watchpoint is actually effective.  DBG_W_Local watchpoints are
 *      effective only in the context of the debuggee process, while
 *      DBG_W_Global watchpoints are effective in the context of any
 *      process.
 *
 *      Both DBG_W_Local and DBG_W_Global watchpoints remain effective
 *      at interrupt time, and while running kernel code.  However, the
 *      DBG_W_Local watchpoints may miss interrupt time accesses,
 *      depending on the process context in which the interrupt
 *      occurred.
 *
 *      Watchpoints are disabled as soon as they are hit, so that they
 *      can only be hit once.
 *
 *      The resources used by a watchpoint will not be freed until the
 *      debugger is finally notified of the hit, or terminates.  It is
 *      recommended that the debugger use the Stop command to free
 *      resources held by any pending watchpoint hits prior to setting
 *      a watchpoint, so that these held resources will not prevent
 *      setting a new watchpoint.
 *
 *      DBG_W_Global watchpoints should be used sparingly, as they
 *      restrict the watchpoint resources available to all processes at
 *      once.  Watchpoint resources are very limited.
 *
 *      The Watchpoint Type and Scope is a combination of a Scope
 *      number and a Type number.  Both the Scope and Type must be
 *      specified.  For example, to set a local watchpoint for
 *      either read or write access, Value should be set to
 *      (DBG_W_Local | DBG_W_ReadWrite).
 *
 *      Watchpoint Scopes :
 *
 *      DBG_W_Global, DBG_W_Local
 *
 *      Watchpoint Types :
 *
 *      DBG_W_Execute, DBG_W_Write, DBG_W_ReadWrite
 *
 *      Restrictions :
 *
 *      The watchpoints are restricted by the hardware. In the case of
 *      the 80386 processor, where debug registers are used, the
 *      available watchpoint lengths are 1, 2, and 4 bytes.     The 2-byte
 *      data watchpoints must be aligned on a WORD boundary, and the
 *      4-byte data watchpoints must be aligned on a DWORD boundary.
 *      DBG_W_Execute watchpoints must be exactly 1 byte in length, and
 *      they must begin on an instruction boundary to be effective.
 *
 *      Global watchpoints are not effective in v86 mode, and can not
 *      detect DMA device accesses.
 *
 *      Global Watchpoints may be set only in the shared memory region
 *      of the linear address space.  Global watchpoints will remain
 *      effective even if the underlying memory has been converted to
 *      private via a DosDebug memory write operation.
 *
 *      This function is swappable.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Cmd = DBG_C_SetWatch
 *              uDB.Addr = Starting Address of Watchpoint
 *              uDB.Len = Length of Watchpoint, in bytes
 *              uDB.Index = Reserved, must be 0.
 *              uDB.Value = Watchpoint Type and Scope.
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *                      uDB.Index = Watchpoint ID Number
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Error Setting Watchpoint
 *                      uDB.Value = Error Code
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *      Pseudocode:
 *      if (Index != 0)
 *        error
 *      else
 *        if (DbgWPAlloc(debuggee ptda, scope) != NO_ERROR)
 *          error
 *        else if (DbgWPSet(watchpoint, address, length, type) != NO_ERROR)
 *          DbgWPFree(watchpoint)
 *
 *      return possible error in debug buffer (except for ERROR_INTERRUPT)
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

int DbgSetWatch (kDB_t *pkDB, DCB_t *pdcb)
{
    WPCB_t *pwpcb;
    int rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgSetWatch      ");   }
#endif

    if (pkDB->uDBTemp.Index != 0)
        rt = ERROR_INVALID_DATA;

    else {
        if ((rt = DbgWPAlloc(SSToDS(&pwpcb), pdcb->pptdaDbgee,
                           pkDB->uDBTemp.Value & DBGWP_MSCOPE)) == NO_ERROR) {

            if ((rt = DbgWPSet(pwpcb, pkDB->uDBTemp.Addr, pkDB->uDBTemp.Len,
                            pkDB->uDBTemp.Value & DBGWP_MTYPE)) != NO_ERROR) {

                if (DbgWPFree(pdcb->pptdaDbgee, pwpcb->wpcb_bWid) != NO_ERROR)
                    dbgAssert(FALSE);
            }
        }
    }

    if (rt == NO_ERROR)
        pkDB->uDBTemp.Index = pwpcb->wpcb_bWid;
    else if (rt != ERROR_INTERRUPT) {
        pkDB->uDBTemp.Cmd = DBG_N_Error;
        pkDB->uDBTemp.Value = rt;
    }

    return(NO_ERROR);
}

/***LP  DbgClearWatch - Perform the DBG_C_ClearWatch command
 *
 *      This command clears a currently set or hit watchpoint.
 *
 *      If the watchpoint is not currently set, an error will be
 *      returned.
 *
 *      If a debugger wishes to move a watchpoint from one location to
 *      another, it should clear the old watchpoint before setting the
 *      new one, to free any resources used by currently set
 *      watchpoints.
 *
 *      The watchpoint will be cleared even if it is currently hit, and
 *      a notification is pending.  To prevent needlessly missing the
 *      watchpoint hit in this way, it is recommended that the Stop
 *      command be used just prior to clearing the watchpoint, to pick
 *      up any pending watchpoint hit notifications.
 *
 *      This function is swappable.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Cmd = DBG_C_ClearWatch
 *              uDB.Index = Watchpoint ID Number
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Error Clearing Watchpoint (Wid invalid or not set)
 *                      uDB.Value = Error Code
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *      Pseudocode:
 *      DbgWPFree(watchpoint)
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

int DbgClearWatch (kDB_t *pkDB, DCB_t *pdcb)
{
   int rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgClearWatch    ");   }
#endif

    rt = DbgWPFree(pdcb->pptdaDbgee, pkDB->uDBTemp.Index);

    if (rt != NO_ERROR) {
        pkDB->uDBTemp.Cmd = DBG_N_Error;
        pkDB->uDBTemp.Value = rt;
    }

    return(NO_ERROR);
}

/***LP  DbgContinue - Perform the DBG_C_CONTINUE command
 *
 *      This command sets the return value for DbgException or DbgTrap.
 *
 *      The default return value for DbgException is NOT_HANDLED;  the
 *      default for DbgTrap (Single steps and breakpoints) is HANDLED.
 *
 *      This function must be locked when in used because it accesses debuggee
 *      tcbs.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Tid = Thread ID of thread to set exception return value
 *              uDB.Cmd = DBG_C_HandleException
 *              uDB.Value = HANDLED or NOT_HANDLED
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Error setting return value (invalid Tid)
 *                      uDB.Value = Error Code
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *      Pseudocode:
 *      if (invalid Tid)
 *        error
 *      else if (Value == HANDLED)
 *        mark debugge thread debug flags as handled
 *      else if (Value == NOT_HANDLED)
 *        mark debugge thread debug flags as not handled
 *      else
 *        error
 */

#pragma code_seg("DEBUGLOCK_TEXT", "CODE")      /* lock DEBUG code */

int DbgContinue (kDB_t *pkDB, DCB_t *pdcb)
{
    tcb_t *ptcb;                                // debuggee thread's pTCB
    int rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgContinue      ");   }
#endif

    if ((rt = TKTidTo_pTCB(pdcb->pptdaDbgee, pkDB->uDBTemp.Tid,
                           SSToDS(&ptcb))) != NO_ERROR) {
        pkDB->uDBTemp.Cmd = DBG_N_Error;
        pkDB->uDBTemp.Value = rt;
    }
    switch ((ULONG)pkDB->uDBTemp.Value) {
       case XCPT_CONTINUE_EXECUTION:
          pdcb->pptdaDbgee->ptda_flDbg &= ~DBGP_XCPTPENDING;
          ptcb->TCB_flDbg |= DBGT_XCPTHANDLED;
          pkDB->uDBTemp.Cmd = DBG_C_Go;
          DbgGo(pkDB, pdcb);
          break;
       case XCPT_CONTINUE_SEARCH:
          pdcb->pptdaDbgee->ptda_flDbg &= ~DBGP_XCPTPENDING;
          ptcb->TCB_flDbg &= ~DBGT_XCPTSTOP;      /* turn off flag   A0004 */
          ptcb->TCB_flDbg &= ~DBGT_XCPTHANDLED;
          pkDB->uDBTemp.Cmd = DBG_C_Go;
          DbgGo(pkDB, pdcb);
          break;
       case XCPT_CONTINUE_STOP:
          pdcb->pptdaDbgee->ptda_flDbg &= ~DBGP_XCPTPENDING;
          ptcb->TCB_flDbg |= DBGT_XCPTSTOP;
          break;
       default:
          pkDB->uDBTemp.Cmd = DBG_N_Error;
          pkDB->uDBTemp.Value = ERROR_INVALID_DATA;
          break;
    }

    return(NO_ERROR);
}

/***LP  DbgAddrToObject - Perform the DBG_C_AddrToObject command
 *
 *      The virtual address in the Addr field is converted into a memory
 *      object base and size.
 *
 *      The Addr field will be left unchanged.
 *
 *      This function is swappable.
 *
 *      API ENTRY
 *
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Cmd = DBG_C_AddrToObj
 *              uDB.Addr = Virtual object number
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *                      uDB.Buffer = Base address of object
 *                      uDB.Len = Size of object (in bytes)
 *                      uDB.Value = object flags
 *                      uDB.MTE = if object flags specify valid MTE, the
 *                                MTE of the owning module
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Error Converting Address
 *                      uDB.Value = Error Code
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

int DbgAddrToObject (kDB_t *pkDB, DCB_t *pdcb)
{
    VMOD od;
    int rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgAddrToObject  ");   }
#endif

    if (VMSysArena(pkDB->uDBTemp.Addr))
        rt = ERROR_INVALID_ACCESS;

    else {
        od.od_pPTDA = pdcb->pptdaDbgee;
        od.od_laddr = pkDB->uDBTemp.Addr;
        if ((rt = VMGetOd(NA, SSToDS(&od))) == NO_ERROR) {
            pkDB->uDBTemp.Buffer = VMLaddrOd(od);
            pkDB->uDBTemp.Len = VMSizeOd(od);
            if (VMMTEOd(od) != 0) {
                pkDB->uDBTemp.Value = DBG_O_OBJMTE;
                pkDB->uDBTemp.MTE = VMMTEOd(od);
            } else
                pkDB->uDBTemp.Value = 0;
        }
    }

    if (rt != NO_ERROR) {
        pkDB->uDBTemp.Cmd = DBG_N_Error;
        pkDB->uDBTemp.Value = rt;
    }

    return(NO_ERROR);
}


/***LP  dbgXchgOpcode - exchage opcode, single-step, and go
 *
 *      This function must be locked when in use because it accesses
 *      debuggee tcbs.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Tid = Thread ID of thread to single-step.
 *              uDB.Cmd = DBG_C_XchgOpcode
 *              uDB.Value = Opcodes to use
 *
 *      API EXIT
 *              Any Debug Notification is possible.
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *                      kDB modified by the debuggee as needed
 *
 *              Return Value != 0
 *                      Fatal Error - Propogate Return Value
 *
 *      Pseudocode:
 *
 */

#pragma code_seg("DEBUGLOCK_TEXT", "CODE")      /* lock DEBUG code */

int
dbgXchgOpcode(kDB_t *pkDB, DCB_t *pdcb)
{
    ulong_t addrop;
    uchar_t opgo;
    tcb_t *ptcb;
    tcb_t *ptcb2;
    int rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgXchgOpcode    ");   }
#endif

    if ((rt = DbgRequestSem(pdcb)) != NO_ERROR)
        return(rt);                     /* Get semaphore */

    if (pdcb->pptdaDbgee->ptda_flDbg & DBGP_XCPTPENDING) {
       pkDB->uDBTemp.Cmd = DBG_N_Error;
       pkDB->uDBTemp.Value = ERROR_INVALID_FUNCTION;
       return(rt);
    }

    for (ptcb = pdcb->pptdaDbgee->ptda_pTCBHead; ptcb != NULL;
      ptcb = ptcb->TCBpTCBNext) {       /* For each debuggee thread */
        if ((ulong_t) ptcb->TCBOrdinal == pkDB->uDBTemp.Tid)
            break;                      /* Break if target thread found */
    }

    if (ptcb != NULL && !(ptcb->TCB_flDbg & (DBGT_FROZEN|DBGT_DEAD))) {
                                        /* If valid thread to single-step */
        if ((rt = dbgUserCSEIP(ptcb,SSToDS(&addrop),NULL,NULL)) == NO_ERROR) {   // B729833
                                        /* If user address obtained okay */
            if ((rt = dbgReadWrite(addrop,ptcb->TCBpPTDA->ptda_handle,
              sizeof(uchar_t),0,NULL,
              (uchar_t *) &(pkDB->uDBTemp.Value),TK_UM_DEST)) == NO_ERROR) {
                                        /* If opcode planted okay */
                opgo = (uchar_t)(pkDB->uDBTemp.Value >> 8);
                                        /* Get "go" opcode */
                if (opgo == (uchar_t) pkDB->uDBTemp.Value) {
                                        /* If no single-step needed */
                    pdcb->pptdaDbgee->ptda_flDbg &= ~DBGP_SSTEP;
                                        /* Turn flag off */
                }
                else {                  /* Else single-step needed */
                    pdcb->sDbgerSStepTCB = ptcb;
                                        /* Set pointer in DCB */
                    ptcb->TCB_flDbg |= DBGT_XCHGOP;
                                        /* Set flag in TCB */
                    pdcb->pptdaDbgee->ptda_flDbg |= DBGP_SSTEP;
                                        /* Cause thread to single-step */
                    pkDB->uDBTemp.Addr = addrop;
                                        /* Remember original address */
                    ptcb2 = pdcb->pptdaDbgee->ptda_pTCBHead;
                                        /* Point to first TCB */
                    while (ptcb2 != NULL) {
                                        /* While TCBs remain */
                        if (ptcb2 != ptcb)
                            ptcb2->TCBForcedActions |= TK_FF_TRC;
                                        /* Set force flag for other threads */
                        ptcb2 = ptcb2->TCBpTCBNext;
                                        /* Try next thread */
                    }
                }
                rt = DbgCall(pkDB, pdcb);
                                        /* Let the debuggee do his stuff */
            }
        }
    }
    else rt = ERROR_INVALID_THREADID;   /* Else invalid thread id */

    DbgClearSem(pdcb);

    if (rt != NO_ERROR && rt != ERROR_INTERRUPT) {
        pkDB->uDBTemp.Cmd = DBG_N_Error;/* Return error in packet */
        pkDB->uDBTemp.Value = rt;
        rt = NO_ERROR;
    }

    return(rt);
}


/***LP  DbgLinearToSel - Perform the DBG_C_LinToSel command,
 *                       this subroutine was added by DCR 1320
 *
 *      This function will convert the address in uDB.Addr (a 32 bit linear
 *      or Flat address) in to a 16 bit Selector and 16 bit Offset, the
 *      selector must exist in the LDT.
 *
 *      This routine only works on LDT selectors.
 *
 *      This function is swappable.
 *
 *      API ENTRY
 *              uDB.Pid = Process ID of debuggee
 *              uDB.Cmd = DBG_C_LinToSel
 *              uDB.Addr = Address to Translate
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *                      uDB.Value = Translated Selector value (32 bit field)
 *                      uDB.Index = Translated Offset   Value (32 bit field)
 *                         NOTE: By setting two different locations in
 *                               memory the caller will be able to set
 *                               the appropriate field with ease.
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Error Converting Address
 *                      uDB.Value = 0
 *                      uDB.Index = 0
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error (The users return code is in uDB.Cmd)
 *                      kDB modified by the debuggee as needed
 *
 *      Pseudocode:
 *
 *      PARM 1 thru PARM 3 below is used to call SELConvertToSelOff
 *      PARM 1, 32 bit Linear/Flat address to translate, this is a ULONG field
 *              pkDB->uDBTemp.Addr
 *      PARM 2, RPL bits, set to -1 therefore access byte is copied
 *              uchar_t rpl_bits
 *      PARM 3, PTDA Handle, this is a USHORT field
 *              pdcb->pptdaDbgee->ptda_handle
 *
 *      Invoke SELConvertToSelOff - To convert the 32 bit Linear address
 *                                  into a 16 bit selector
 *         NOTE SELConvertToSelOff is located in
 *              DRV6\SRC\DOS\MEMORY\selmgra.asm
 *
 *         NOTE we assume this routine will only return 0 or a valid value
 *
 *      Set uDB.Cmd   to DBG_N_SUCCESS to indicate success
 *      Set uDB.Value to the High order 16 bits the SELECTOR
 *      Set uDB.Index to the Low Order 16 bits the OFFSET
 *
 *      IF 0 is returned from SELConvertToSelOff
 *         Set uDB.Cmd  to  DBG_N_ERROR to indicate an error
 *      ENDIF
 *
 *      Return (NO_ERROR) to DBG32ApiDebug because there is no message or
 *      return codes associated with this function. The users return code
 *      is set in uDB.Cmd.
 *
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code      A001*/

int
DbgLinearToSel(kDB_t *pkDB, DCB_t *pdcb)        /*                       A001*/
{
  ulong_t  rpl_bits = -1;           /* SELConvertToSelOff RPL BITs       A001*/

  union                             /* this redefines the storage        A001*/
     {                              /*                                   A001*/
       unsigned long  ul_value;     /* SELConvertToSelOff return value   A001*/
       struct                       /* this is done for bit handling     A001*/
          {                         /* structure                         A001*/
            unsigned  int  LO_16  :16; /* Low  order 16 bits is offset   A001*/
            unsigned  int  HI_16  :16; /* High order 16 bits is selector A001*/
          }   s_value;              /* structure name                    A001*/
     } sel_off;                     /* union name                        A001*/

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgLinearToSel   ");   }
#endif

    if ( ( pkDB->uDBTemp.Addr >= SEL_64K ) &&   /* lower SEL Limit       A006*/
         ( pkDB->uDBTemp.Addr <= SEL_512MEG ) ) /* upper SEL Limit       A006*/
     {  /* input address is above the 64K line                           A006*/
    /* Convert 32 bit Flat or Linear to 16 BIT Selector                  A001*/
    sel_off.ul_value = SELConvertToSelOff( pkDB->uDBTemp.Addr,   /*      A001*/
                                  rpl_bits,                      /*      A001*/
                           (ULONG)pdcb->pptdaDbgee->ptda_handle);/*      A001*/

    if (sel_off.ul_value == 0)                  /* if translation failed A001*/
    {
       pkDB->uDBTemp.Cmd = DBG_N_Error;         /* failed return code    A001*/
       pkDB->uDBTemp.Value = DBG_N_Error;       /* NO LDT Match found    A006*/
    }
    else
    {
    /*************************************************************************/
    /* initilize values to be returned to the caller                     A001*/
    /* ** NOTE **  sel_off should be zero if translation failed          A001*/
    /*************************************************************************/
    pkDB->uDBTemp.Cmd = DBG_N_Success;  /* success in translation        A001*/
    pkDB->uDBTemp.Value =
                      sel_off.s_value.HI_16; /* high 16 bits is SELECTOR A001*/
    pkDB->uDBTemp.Index =
                      sel_off.s_value.LO_16; /* low  16 bits is OFFSET   A001*/
    }

    }   /* end if - input address is above the 64K line                  A006*/
    else
    {
       pkDB->uDBTemp.Cmd = DBG_N_Error;         /* failed return code    A001*/
       pkDB->uDBTemp.Index = DBG_N_Error;       /* Input address bad     A006*/
    } /* else end */


    return(NO_ERROR);                           /* DBG32ApiDebug checks  A001*/
}


/***LP  DbgSelToLinear - Perform the DBG_C_SelToLin command
 *                       this subroutine was added by DCR 1320
 *
 *      This function will convert a 16 bit Selector and 16 bit Offset,
 *      from the LDT into a 32 bit linear or flat address.
 *
 *      This routine only works on LDT selectors.
 *
 *      This function is swappable.
 *
 *      API ENTRY
 *              uDB.Pid   = Process ID of debuggee
 *              uDB.Cmd   = DBG_C_SelToLin
 *              uDB.Value = 16 bit Selector from the address to be Translated
 *              uDB.Index = 16 bit Offset from the address to be Translated
 *
 *      API EXIT
 *              uDB.Cmd = DBG_N_Success
 *                      No Error
 *                      uDB.Addr  = 32 bit Linear or Flat address
 *
 *              uDB.Cmd = DBG_N_Error
 *                      Error Converting Address
 *                      uDB.Addr  = -1 (selector is invalid)
 *                      uDB.Addr  = -2 (offset is greater than 64K)
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error (users return code is in uDB.Cmd)
 *                      kDB modified by the debuggee as needed
 *
 *      Pseudocode:
 *      Set uDB.Cmd   to DBG_N_ERROR to indicate failure
 *      Set uDB.Addr  to zero to indicate nothing has happen
 *      PARM 1 and PARM 2 below is used to call SELConvertToLinear
 *      PARM 1, 16 bit Selector to translate, this is a ULONG field
 *              pkDB->uDBTemp.Value
 *      PARM 2, PTDA Pointer of debuggee, this is a Ulong field
 *              pdcb->pptdaDbgee
 *
 *      IF the offset is valid THEN
 *         Invoke SELConvertToLinear - To convert the 16 bit selector into
 *                                     a 32 bit linear address
 *            NOTE SELConvertTolinear is located in
 *                 DRV6\SRC\DOS\MEMORY\selmgra.asm
 *         IF SELConvertToLinear failed THEN
 *            Set uDB.Addr to -1 (invalid selector)
 *            NOTE we assume this routine will only return -1
 *                 or a valid value
 *         ELSE  (there is a valid selector)
 *            Set uDB.Cmd   to DBG_N_SUCCESS to indicate success
 *            NOTE 1) we assume the selector is left justfied
 *                 2) the offset should be the same in 32 or 16 environment
 *            Set uDB.Addr to uDBTemp.Index plus new_value
 *                this is the new linear address
 *         ENDIF
 *      ELSE (offset is invalid)
 *         Set uDB.Addr to -2 (invalid offset)
 *      ENDIF
 *
 *    Return (NO_ERROR) to DBG32ApiDebug because there is no message or
 *    return codes associated with this function. The users return code
 *    is set in uDB.Cmd.
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code       A001*/

int
DbgSelToLinear(kDB_t *pkDB, DCB_t *pdcb)        /*                       A001*/
{
  struct   seldesc_s    pdesc;                  /* Return desc info      A006*/
  ULONG    new_value = 0L, rc = 0L;             /*                       A006*/
  #define  max_offset   0x0000ffff              /*                       A001*/
  #define  sel_error    -1                      /* selector error -1     A001*/
  #define  off_error    -2                      /* offset error   -2     A001*/
  #define  sel_non_app_err  -3                /* selector not in app -3 A006 */

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgSelToLinear   ");   }
#endif

    /*************************************************************************/
    /* initilize values to be returned to the caller                     A001*/
    /*************************************************************************/
    pkDB->uDBTemp.Cmd   = DBG_N_Error;          /* error in translation  A001*/
    pkDB->uDBTemp.Addr  = DBG_N_Error;          /* error in translation  A001*/

    /*************************************************************************/
    /* Convert 16 bit selector to 32 BIT Flat or Linear Address          A001*/
    /* if the offset is less than or equal 64K                           A001*/
    /*************************************************************************/
    if (pkDB->uDBTemp.Index <= max_offset)        /* if offset is valid  A001*/
     {                 /* start offset is valid, translate selector      A001*/
       rc = SELGetDescInfo(                                   /*         A006*/
                        (SEL)pkDB->uDBTemp.Value,             /*         A006*/
                        pdcb->pptdaDbgee->ptda_handle,        /*         A006*/
                        (SSToDS(&pdesc) ) );                  /*         A006*/
    /*************************************************************************/
    /* Setup the 32 bit addres if the translation was successfully       A001*/
    /* the translated value is right justified                           A001*/
    /*************************************************************************/
       if (rc != NO_ERROR)                                    /*         A006*/
       {  new_value = sel_error; }                            /*  -1     A006*/
       else
       {
          if ( ( (pdesc.desc_attr & (D_SEG + D_GATE)) == D_GATE) ||  /*  A006 */
               ( (pdesc.desc_attr &  D_TSS) == D_TSS)  )             /*  A006 */
          {                   /* Selector is not in Appl CS or DS        A006 */
             new_value = sel_non_app_err;            /*    -3            A006 */
          }                                          /*                  A006 */
          else                                       /*                  A006 */
          {                                          /*                  A006 */
         /*********************************************************************/
         /* linear address is the sum of the SELECTOR and the OFFSET     A001 */
         /*********************************************************************/
             pkDB->uDBTemp.Cmd = DBG_N_Success;  /* Translation is Good   A001*/
             new_value = pdesc.desc_laddr +          /* in Appl CS or DS A006 */
                         pkDB->uDBTemp.Index;        /*                  A006 */
             pkDB->uDBTemp.Addr = new_value;     /* Translated address    A001*/
          }                                          /*                  A006 */
       }  /* endif */
     }                                          /* end offset is valid   A001*/
     else                                       /* offset error          A001*/
     { new_value = off_error;  }                /* offset   invalid  -2  A006*/

  return(NO_ERROR);                             /* DBG32ApiDebug checks  A001*/
}

/***LP  DbgRegisterSemList - Register a semaphore list with DOSDEBUG
 *
 *      This function obtains the semaphore for the sem lists,
 *      allocates a sem list struct of the appropriate size, fetches
 *      the users list and verifies it, and adds it to a link list.
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *
 *      EXIT
 *              NO_ERROR if the list has been registerd
 *
 */

#pragma code_seg()
#pragma code_seg("SWAP32_15","CODE")

#define DB_VMALIASFL (VMAC_ARENASYS | VMAC_LOCANY | VMAC_ALIGNPAGE \
                     | PG_R | PG_W)

int
DbgRegisterSemList(kDB_t *pkDB)
{
    int     rt;
    ulong_t i,j,
            laddr;
    semList *pSemList,
            *temp;
    VMAC    ac;

    /*
     * If no processes are using DosDebug honor the request
     * to register the semaphore list, otherwise return
     * ERROR_REQ_NOT_ACCEP;
     */
    if (pdcbHead == NULL) {

       if ((rt = VMAllocKHB(VM_HKH_PUB_RESRW, pkDB->uDBTemp.Len,
           (VMHOB)DBGDCBOWNER, NA, SSToDS(&pSemList))) == NO_ERROR) {

          /* Fetch the users semList into ours */
          if (TKFuBuff((char *)pSemList, (char *)pkDB->uDBTemp.Buffer,
              pkDB->uDBTemp.Len, NA) == NO_ERROR) {

             /*
              * Verify that the size specifed by the user and the size
              * specified in the semList are equal.
              */
             if (pkDB->uDBTemp.Len == sizeof(semList) + ((pSemList->count - 1)
                 * sizeof(dbSem))) {

                /* Partially link struct into chain for scanning purposes */
                pSemList->next = pSemListHead;

                /*
                 * Verify that the semaphore type is valid, that the
                 * address of the semaphore is valid and we can read from
                 * it, and that the sem addrs are not duplicates.
                 */
                for (i = 0; i < pSemList->count; i++) {

                   if ((pSemList->sem[i].flags & ~DBSEM_TYPEMASK) ||
                       (TKProbeBuff((char *)pSemList->sem[i].addr, 1,
                       TK_UM_READ) != NO_ERROR)) {
                      i = -1; /* Indicate error detected */
                      break;
                   }

                   /*
                    * Set j to i+1 so we don't check the sem were currently
                    * validating
                    */
                   j = i+1;
                   temp = pSemList;

                   /* Scan through all semList's looking for dups */

                   while ((temp) && (i != -1)) {
                      for (; j < temp->count; j++)
                         if (temp->sem[j].addr ==
                             pSemList->sem[i].addr) {
                            i = -1; /* If dup, indicate error detected */
                            break;
                         }
                      j = 0;
                      temp = temp->next;
                   }

                   if (i == -1) break; /* If errors detected, stop loop */
                }

                /*
                 * If no errors detected create system aliases for each sem,
                 * finish linking struct into chain, and return NO_ERROR.
                 */
                if (i != -1) {

                   for (i = 0; i < pSemList->count; i++) {

                      /* Get size of sem */
                      if (pSemList->sem[i].flags & DBSEM_FSRST)
                         j = sizeof(FSRST);
                      else if (pSemList->sem[i].flags & DBSEM_GRESEMT)  // A010
                         j = sizeof(GRESEMT);                           // A010
                      else
                         dbgAssert(FALSE);

                      laddr = pSemList->sem[i].addr;

                      /* Size of alias to create rounded up to a PAGESIZE */
                      j = ((j + (laddr & PAGEMASK)) + PAGEMASK) & ~PAGEMASK;

                      if ((rt = VMMapAlias(DB_VMALIASFL, NULL, NULL, NULL,
                          HOBNULL, (laddr & ~PAGEMASK), HOBNULL, j,
                          SSToDS(&ac))) == NO_ERROR) {
                            pSemList->sem[i].alias = (ulong_t) ac.ac_va +
                               (laddr & PAGEMASK);
                      }
                      else {
                         for (j = 0; j < i; j++)
                            VMFreeMem((pSemList->sem[j].alias & ~PAGEMASK),
                               HOBNULL, NULL);
                         VMFreeKHB(VM_HKH_PUB_RESRW, pSemList);
                         return (rt);
                      }
                   }

                   pSemListHead = pSemList;
                   return (rt);
                }
             }
          }
          rt = ERROR_INVALID_PARAMETER;
          VMFreeKHB(VM_HKH_PUB_RESRW, pSemList);
       }
    }
    else
       rt = ERROR_REQ_NOT_ACCEP;

    return(rt);
}
#pragma code_seg()
/***LP  DbgCall - Let Debuggee Process Execute
 *
 *      DbgCall allows the debuggee thread to attempt to run
 *      user code.  When complete, the debuggee saves the
 *      return information in the kDB.
 *
 *      DbgCall must be serialized via DbgRequestSem and DbgClearSem.
 *
 *      This function must be locked when in use.
 *
 *      ------- WARNING WARNING WARNING WARNING -------
 *
 *      A pointer to the kDB is passed to the debuggee, and the
 *      debuggee thread is allowed to directly read and modify the
 *      debugger thread's kernel stack (in the kDB area) for the
 *      duration of this procedure.
 *
 *      The debuggee kernel code MUST verify that the pointer to
 *      the kDB (in the DCB) is nonzero before attempting to access
 *      the kDB, or it will trash the kernel stack of the debugger
 *      thread.  The passed pointer will be relative to DGroup.
 *
 *      ------- WARNING WARNING WARNING WARNING -------
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *                      kDB modified by the debuggee as needed
 *
 *              Return Value != 0
 *                      Fatal Error - Propogate Return Value
 *
 *      Pseudocode:
 *      clear REPLIED and RECEIVED flags
 *      if (debuggee not dead)
 *        DCB debug buffer pointer = internal debug buffer
 *        TKRun debuggee
 *        while (debuggee not replied and debuggee alive)
 *          if (TKBlock(debugger block id) != NO_ERROR)
 *            break
 *        set RECEIVED flags
 *        clear DCB debug buffer pointer
 *      if (debuggee did not reply)
 *        if (debuggee dead)
 *          error = ERROR_INVALID_PROCID
 *        else // ERROR_INTERRUPT from TKBlock
 *          set debuggee to panic stop
 *      else
 *        clear REPLIED flag
 *        set NOTIFIED flag
 *        Tid = active Tid
 *        if (Tid != 0)
 *          ReadReg
 *
 *      return possible error in debug buffer (except for ERROR_INTERRUPT)
 */

#pragma code_seg("DEBUGLOCK_TEXT", "CODE")      /* lock DEBUG code */

int DbgCall (kDB_t *pkDB, DCB_t *pdcb)
{
    int rt = NO_ERROR;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgCall          ");   }
#endif

    pdcb->flState &= ~(DBGDCBS_REPLIED | DBGDCBS_RECEIVED);

    if (!(pdcb->flState & DBGDCBS_DBGEEDEAD)) {
        pdcb->pkDB = pkDB;
        TKRun(DBGID_EELOCK(pdcb), NULL);

        while (rt == NO_ERROR &&
               (!(pdcb->flState & (DBGDCBS_REPLIED | DBGDCBS_DBGEEDEAD))))
            rt = TKBlock(DBGID_ERLOCK(pdcb),-1,0);

        pdcb->flState &= ~DBGDCBS_RECEIVED;
        pdcb->pkDB = NULL;
    }

    /* if an ERROR has not occured before now continue                A008 */

    if ( (pkDB->uDBTemp.Cmd != DBG_N_Error) &&
       (!(pdcb->flState & DBGDCBS_REPLIED)) ) {
        if (pdcb->flState & DBGDCBS_DBGEEDEAD) {
            pkDB->uDBTemp.Cmd = DBG_N_Error;
            pkDB->uDBTemp.Value = ERROR_INVALID_PROCID;
            rt = NO_ERROR;
        } else {
            // rt must be ERROR_INTERRUPT.  Panic Stop.
            pdcb->pptdaDbgee->ptda_flDbg |= DBGP_PANIC;
            pdcb->pptdaDbgee->ptda_ForcedActions |= TK_FF_TRC;
        }
    } else {
        rt = NO_ERROR;
        pdcb->flState &= ~DBGDCBS_REPLIED;
        pdcb->flState |= DBGDCBS_NOTIFIED;
        pkDB->uDBTemp.Tid = pdcb->sActiveTid;

        if (pkDB->uDBTemp.Tid != 0)
            rt = DbgReadReg(pkDB,pdcb);
    }


    return(rt);
}

#endif /* INCDEBUG */

/***EP  DbgKillDebuggees - Kill All Debuggee processes of current process
 *
 *      DbgKillDebuggees identifies and terminates any process being
 *      debugged by the current process.
 *
 *      This should be called whenever a debugger process terminates.
 *
 *      This function must be locked when in use because it walks the DCB list.
 *
 *      ENTRY
 *              Debugger Process (Current Process) Terminating
 *
 *      EXIT
 *              No Return Value
 *
 *      Pseudocode:
 *      for each DCB
 *        if DCB debugger == current process
 *          mark debuggee dcb as dead (DBGDCBS_DBGEEDEAD)
 *          mark debuggee as debugger terminated (DBGP_ABORTALL)
 *          clear single step flag
 *          if (DCB debuggee not in exit list processing)
 *            TKDieSoonPTDA(DCB debuggee)
 *          TKRun DCB debuggee
 */

#pragma code_seg("DEBUGLOCK_TEXT", "CODE")      /* lock DEBUG code */

void DbgKillDebuggees()
{

#ifdef INCDEBUG

    ulong_t lPidCur;
    DCB_t *pdcb;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgKillDebugges  ");   }
#endif

    if (!(pPTDACur->ptda_flDbg & DBGP_ISADBGER))
        return;

    lPidCur = (ulong_t) pPTDACur->ptda_Pid;

    for (pdcb = pdcbHead; pdcb != NULL; pdcb = pdcb->pdcbNext) {
        if (pdcb->lDbgerPid == lPidCur &&
            !(pdcb->flState & DBGDCBS_DBGEEDEAD)) {

            register PPTDA pptdaDbgee = pdcb->pptdaDbgee;

            // No need to take the debuggee semaphore - only threads from
            // this process could take it and this process is dead.
            pdcb->flState |= DBGDCBS_DBGEEDEAD;
            pptdaDbgee->ptda_flDbg |= DBGP_ABORTALL;
            pptdaDbgee->ptda_flDbg &= ~DBGP_SSTEP;

            if (!(pptdaDbgee->ptda_infoseg.LIS_ProcStatus & PS_XITLST))
                TKDieSoonPTDA(pptdaDbgee, 0, TC_SIGNAL, 0);

            TKRun(DBGID_EELOCK(pptdaDbgee->ptda_pdcb),NULL);
        }
    }

#endif /* INCDEBUG */
}

#ifdef INCDEBUG

#pragma code_seg()
#pragma code_seg("SWAP32_15","CODE")
/***LP  DbgkDBRead - Read the kDB from the given uDB
 *
 *      DbgkDBRead reads the kDB from the debugger application's
 *      uDB.
 *
 *      DbgkDBRead will handle all of the various formats in which
 *      the uDB can appear.     The initial command number is saved
 *      in the lCmd field of the kDB.
 *
 *      If the command is invalid, the pkDB->lCmd will be set up to
 *      force an error.
 *
 *      This function is swappable.
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              puDB = Pointer to a user Debug Buffer
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error.       kDB valid, uDB.Cmd verified
 *
 *              All Other Return Values
 *                      kDB invalid. (Protection Violation)
 *                      Immediately propogate return value
 *                      back to the application.
 *      Pseudocode:
 *      copy header portion of user debug buffer
 *      if (Command < 0 || Command > DBG_C_LastCommand)
 *        Command = error command
 *      else if (Command != DBG_C_Connect)
 *        copy remaining portion of user debug buffer
 */


int DbgkDBRead (uDB_t *puDB, kDB_t *pkDB)
{
    int rt;
    int flags;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgKDBRead       ");   }
#endif

    if ((rt = TKFuBuff((char *)&(pkDB->uDBTemp), (char *)puDB,
                      FIELDOFFSET(uDB_t,Addr), TK_UM_FATAL)) != NO_ERROR)
        return(rt);

    pkDB->puDB = puDB;
    pkDB->lCmd = pkDB->uDBTemp.Cmd;

    // Verify that Cmd is valid
    if (pkDB->lCmd > DBG_C_LastCommand || pkDB->lCmd < 0)
        // make sure lCmd is within the dispatch table
        pkDB->lCmd = DBG_C_LastCommand + 1;
    else
       if (pkDB->lCmd != DBG_C_Connect) {
           flags = (pkDB->lCmd == DBG_C_RegisterSemList ? NA: TK_UM_FATAL);
           rt = TKFuBuff((char *)&(pkDB->uDBTemp.Addr), (char *)&(puDB->Addr),
                sizeof(uDB_t) - FIELDOFFSET(uDB_t, Addr), flags);
       }

    return(rt);
}
#pragma code_seg()
/***LP  DbgkDBWrite - Write the kDB to its corresponding uDB
 *
 *      DbgkDBWrite writes the kDB to the debugger application's
 *      corresponding uDB.
 *
 *      The address and format of the uDB is contained within the kDB.
 *
 *      Extra buffers (NPX, Thread Status, Buffered Memory)
 *      are handled by the individual commands, not here.
 *
 *      This function is swappable.
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *              Return Value != 0
 *                      Error.  Propogate return value back to caller.
 *
 *      Pseudocode:
 *      if (Command == DBG_C_Connect)
 *        copy debug buffer header to user debug buffer
 *      else
 *        copy entire debug buffer to user debug buffer
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

int DbgkDBWrite (kDB_t *pkDB)
{
    int cbBufSize = FIELDOFFSET(uDB_t, Addr);

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgKdbWrite      ");   }
#endif

    if (pkDB->lCmd != DBG_C_Connect)
        cbBufSize = sizeof(uDB_t);

    return(TKSuBuff((char *)pkDB->puDB, (char *)&(pkDB->uDBTemp), cbBufSize,
                   TK_UM_FATAL));
}

/***LP  DbgLinkDCB - Link a new DCB into the DCB list
 *
 *      DbgLinkDCB adds a new DCB to the DCB list, locking down the debug
 *      code segment if there are no other active debuggees.
 *
 *      This function must be resident.
 *
 *      ENTRY
 *              pdcb = Pointer to new DCB
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *              Return Value != 0
 *                      Error.  Propagate return value back to caller.
 *
 *      Pseudocode:
 *      if (no active debuggees)
 *        lock DEBUGLOCK_TEXT in memory
 *      link pdcb into DCB list
 */

#pragma code_seg()

int DbgLinkDCB (DCB_t *pdcb)
{
    int rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgLinkDCB       ");   }
#endif

    if (pdcbHead == NULL) {
        // There are no other active debuggees, so lock the debug code.
        extern char StartDEBUGLOCK_TEXT[];
        extern char EndDEBUGLOCK_TEXT[];

        if ((rt = VMLockMem((ulong_t) StartDEBUGLOCK_TEXT,
                            EndDEBUGLOCK_TEXT - StartDEBUGLOCK_TEXT,
                            VML_LONG,
                            &hdbgTextLock,
                            (struct pagelist_s *) VM_NO_ADDR,
                            (ulong_t *) VM_NO_ADDR)) != NO_ERROR)

            return(rt);
    }

    pdcb->pdcbNext = pdcbHead;
    pdcbHead = pdcb;

    return(NO_ERROR);
}

/***EP  DbgEnable - Enable DosDebug on a Process
 *
 *      DbgEnable allocates and initializes a DCB for a
 *      newly created Debuggee process.  By linking the
 *      DCB to the PTDA, Debug becomes enabled on that
 *      process.
 *
 *      This function is swappable.
 *
 *      ENTRY
 *              pptda = Pointer to a PTDA of new process
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error
 *
 *              Return Value != 0
 *                      Error.  Propogate return value back to caller.
 *
 *      Pseudocode:
 *      allocate and zero a DCB
 *      allocate a kernel mutex debuggee semaphore
 *      DCB debuggee pptda = debuggee pptda
 *      DCB debuggee pid = debuggee pid
 *      DCB reference count = 1
 *      DBGLinkDCB
 *      debuggee forced actions = TK_FF_TFBIT
 *      debuggee debug flags = DBGP_INIT | DBGP_EXELOAD
 *      debuggee DCB = new DCB
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

int DbgEnable (PPTDA pptda)
{
    DCB_t *pdcb;
    ULONG rt;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgEnable        ");   }
#endif

    if ((rt = (ULONG)VMAllocKHB(VM_HKH_PUB_RESRW,sizeof(DCB_t), DBGDCBOWNER,NA,
            SSToDS(&pdcb))) != NO_ERROR)
        return(rt);

    memset((void *)pdcb,0,sizeof(DCB_t));

    if ((rt = KSEMAlloc((PHKSEM) &pdcb->hKSEMdbger, KRNL_MUTEXSEM, 0)) != NO_ERROR) {
        VMFreeKHB(VM_HKH_PUB_RESRW,pdcb);
        return(rt);
    }

    pdcb->pptdaDbgee = pptda;
    pdcb->lDbgeePid  = pptda->ptda_Pid;
    pdcb->sRefer     = 1;

/** vmOffGSR  - incremented here so DLL's will not go to the GSR      A011 *
  * vmOffGSR  =  0 means LOAD DLL's into Global Shared Region         A011 *
  * vmOffGSR !=  0 means DO NOT LOAD DLL's into Global Shared Region  A011 *
 */
    vmOffGSR ++;                                           /*         A011 */

    if ((rt = DbgLinkDCB(pdcb)) != NO_ERROR) {
        KSEMDestroy((HKSEM) pdcb->hKSEMdbger);
        VMFreeKHB(VM_HKH_PUB_RESRW,pdcb);
/** vmOffGSR  - Decremented here so DLL's will go to the GSR          A011 *
  * vmOffGSR  =  0 means LOAD DLL's into Global Shared Region         A011 *
  * vmOffGSR !=  0 means DO NOT LOAD DLL's into Global Shared Region  A011 *
 */
        vmOffGSR --;                                       /*         A011 */
        return(rt);
    }

    pptda->ptda_ForcedActions |= TK_FF_TFBIT;

    // set debug flags so debuggee will give initial success notification and
    // a module load notification for the EXE
    pptda->ptda_flDbg = DBGP_INIT | DBGP_EXELOAD;
    pptda->ptda_pdcb  = pdcb;

    return(NO_ERROR);
}

/***EP  DbgDisable - Disable DosDebug on a Process
 *
 *      DbgDisable Deallocates and unlinks the DCB from the
 *      named process.
 *
 *      DbgDisable should be called to clean up if an error
 *      occured while creating a new process, after DbgEnable
 *      was used on that process.
 *
 *      This function is swappable.
 *
 *      ENTRY
 *              pptda = Pointer to a PTDA of (aborted) new process
 *
 *      EXIT
 *              No Return Value
 *
 *      Pseudocode:
 *      debuggee pptda dcb = NULL
 *      DbgRelease(DCB)
 */

#pragma code_seg("DEBUGSWAP_TEXT", "CODE")      /* swap DEBUG code */

void DbgDisable (PPTDA pptda)
{
    register DCB_t *pdcb = pptda->ptda_pdcb;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgDisable       ");   }
#endif

/** vmOffGSR  - Decremented here so DLL's will go to the GSR          A011 *
  * vmOffGSR  =  0 means LOAD DLL's into Global Shared Region         A011 *
  * vmOffGSR !=  0 means DO NOT LOAD DLL's into Global Shared Region  A011 *
 */
    if ( vmOffGSR >  0 )                                   /*         A011 */
    {  vmOffGSR-- ; }                                      /*         A011 */
    else                                                   /*         A011 */
      if ( vmOffGSR <  0 )                                 /*         A011 */
      {  dbgAssert(vmOffGSR == DBG_N_Error); }             /*         A011 */

    pptda->ptda_pdcb = NULL;
    DbgReleaseDCB(pdcb);
}

/***LP  DbgGetDCB - Locate and Gain debugger access to a DCB
 *
 *      DbgGetDCB is used to locate the debuggee process,
 *      gain addressability to its DCB and PTDA, and verify
 *      that the current process is the correct debugger, and
 *      is connected to the debuggee.
 *
 *      DbgGetDCB locates the DCB by scanning the list of DCBs
 *      in the system.
 *
 *      DbgGetDCB will reject the request if the debuggee process
 *      is not yet linked into the process tree or if the debuggee
 *      process will no longer accept debugging commands.
 *
 *      If an error is returned, DbgkDBWrite should be called to
 *      propagate the correct error information.  ERROR_INVALID_DATA
 *      should NOT be propogated back to the debugger application.
 *
 *      This function must be locked when in use because it accesses the DCB
 *      list.
 *
 *      ENTRY
 *              pkDB = Pointer to kernel Debug Buffer
 *              ppdcb = Pointer to returned Debug Control Block address
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error.       DCB is accessable.
 *
 *              Return Value == ERROR_INVALID_DATA
 *
 *                      Call DbgkDBWrite and propogate its
 *                      return value back to the application.
 *
 *              No other return value is possible.
 *
 *      Pseudocode:
 *      find debuggee DCB
 *      if (no such debuggee ||
 *          debuggee not fully created ||
 *          debuggee dead ||
 *          wrong debugger)
 *        error
 *      else
 *        DCB reference count += 1
 */

#pragma code_seg("DEBUGLOCK_TEXT", "CODE")      /* lock DEBUG code */

int DbgGetDCB (kDB_t *pkDB, DCB_t **ppdcb)
{
    register DCB_t *pdcb = pdcbHead;
    ulong_t lPidDbgee = pkDB->uDBTemp.Pid;

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgGetDCB        ");   }
#endif

    while (pdcb != NULL && pdcb->lDbgeePid != lPidDbgee)
        pdcb = pdcb->pdcbNext;

    if (pdcb == NULL ||
        (pdcb->pptdaDbgee->ptda_ProcStatus & PS_EMBRYO) ||
        (pdcb->flState & DBGDCBS_DBGEEDEAD) ||
        (pdcb->lDbgerPid ? (pdcb->lDbgerPid != pPTDACur->ptda_Pid) :
         (pkDB->lCmd != DBG_C_Connect))) {

      /* if an ERROR has not occured before now continue              A008 */
      if (pkDB->uDBTemp.Cmd != DBG_N_Error) {             /*          A008 */
        pkDB->uDBTemp.Cmd = DBG_N_Error;
        pkDB->uDBTemp.Value = ERROR_INVALID_PROCID;
      } /* if end an ERROR has not occured before now continue        A008 */

        return(ERROR_INVALID_DATA);
    }

    *ppdcb = pdcb;
    ++(pdcb->sRefer);

    return(NO_ERROR);
}

/***LP  DbgReleaseDCB - Release access to a DCB
 *
 *      DbgReleaseDCB is used to release access to a DCB after
 *      a call to DbgGetDCB.
 *
 *      If DbgReleaseDCB is called with a NULL pointer,
 *      DbgReleaseDCB will do nothing.
 *
 *      If DbgReleaseDCB detects that the reference count to the
 *      DCB goes to zero (because the debuggee has died) the
 *      DCB will be freed.
 *
 *      This function must be locked when in use because it modifies the DCB
 *      list.
 *
 *      ENTRY
 *              pdcb = Pointer to Debug Control Block
 *
 *      EXIT
 *              No return value
 *
 *      Pseudocode:
 *      if (DCB exists)
 *        if (--DCB reference count)
 *          remove DCB from DCB list
 *          if no more DCBs
 *            unlock DEBUGLOCK_TEXT
 *          free debuggee semaphore
 *          free DCB
 */

#pragma code_seg("DEBUGLOCK_TEXT", "CODE")      /* lock DEBUG code */

void DbgReleaseDCB (DCB_t *pdcb)
{

#ifdef DBGSTRICT
    if (DBGBugBug & DBGERc)
    {  printf("\nDbger.c ** DbgReleaseDCB    ");   }
#endif

    if (pdcb != NULL)
        if (--(pdcb->sRefer) == 0) {
            register DCB_t *pdcbP = NULL;
            register DCB_t *pdcbN = pdcbHead;

            while ((pdcbN != pdcb) && (pdcbN != NULL)) {
                pdcbP = pdcbN;
                pdcbN = pdcbN->pdcbNext;
            }

            if (pdcbN == NULL)
                dbgAssert(FALSE);

            if (pdcbP == NULL) {
                pdcbHead = pdcbN->pdcbNext;
                if (pdcbHead == NULL)
                    // no more active debuggees, unlock the debug lock text
                    VMUnlock(&hdbgTextLock);

            } else
                pdcbP->pdcbNext = pdcbN->pdcbNext;

            KSEMDestroy((HKSEM) pdcb->hKSEMdbger);
            VMFreeKHB(VM_HKH_PUB_RESRW,pdcb);
        }
}

#endif /* RAS */
#pragma code_seg()
#pragma code_seg("SWAP32_15","CODE")
/***LP  DBG32ApiDebug - Worker Routine for DosDebug
 *
 *      DbgDebug is used to perform any of the Debug commands
 *      to operate on the debuggee process.
 *
 *      DbgDebug is called only by w_Debug.  w_Debug is a short
 *      piece of assembly code which acts as a simple interface
 *      to DbgDebug, allocating and passing a DS-relative
 *      pointer to the kDB to DbgDebug.
 *
 *      This function is swappable.
 *
 *      ENTRY
 *                   puDB = Pointer to a user Debug Buffer
 *                   pkDB = Pointer to kernel Debug Buffer
 *         debugInterface = Debugging Interface. Specifies if
 *                          DBG32ApiDebug was called from
 *                          DOSPTRACE or from DOSDEBUG.
 *                             DBGP_PTRACE - DOSPTRACE
 *                             DBGP_DEBUG  - DOSDEBUG
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error.       See returned values in uDB for more.
 *                      uDB may contain additional error information.
 *
 *              Return Value != 0
 *                      Fatal Error
 *                      Command possibly incomplete
 *                      uDB contents invalid
 *
 *      Pseudocode:
 *      allocate internal debug data structure
 *      if (DbgkDBRead == NO_ERROR) // read user debug buffer
 *        if (DbgGetDCB == NO_ERROR) // get debuggee DCB
 *          if (debugInterface == DOSPTRACE)                                     // B729833
 *             if (debugger has been using DOSDEBUG)                             // B729833
 *               return ERROR_ACCESS_DENIED                                      // B729833
 *          else                                                                 // B729833
 *             if (debugger has been using DOSPTRACE)                            // B729833
 *               return ERROR_ACCESS_DENIED                                      // B729833
 *          debugger using flag |= debugInterface                                // B729833
 *          dispatch to DosDebug worker function
 *          DbgReleaseDCB
 *      if (NO_ERROR || ERROR_INVALID_DATA)
 *        DbgkDBWrite
 *      free internal debug data structure
 */


#ifdef INCDEBUG

// Static array of pointers to command functions - MUST BE IN ORDER!

static (*(apfnDbgCmd[]))(kDB_t *, DCB_t *) = {

//   Index                      Command Procedure Address

/*   DBG_C_Null         */      DbgNull,
/*   DBG_C_ReadMem      */                      // Duplicate
/*   DBG_C_ReadMem_I    */      DbgReadWord,
/*   DBG_C_ReadMem_D    */      DbgReadWord,
/*   DBG_C_ReadReg      */      DbgReadReg,
/*   DBG_C_WriteMem     */                      // Duplicate
/*   DBG_C_WriteMem_I   */      DbgWriteWord,
/*   DBG_C_WriteMem_D   */      DbgWriteWord,
/*   DBG_C_WriteReg     */      DbgWriteReg,
/*   DBG_C_Go           */      DbgGo,
/*   DBG_C_Term         */      DbgTerm,
/*   DBG_C_SStep        */      DbgSStep,
/*   DBG_C_Stop         */      DbgStop,
/*   DBG_C_Freeze       */      DbgFreezeThaw,
/*   DBG_C_Resume       */      DbgFreezeThaw,
/*   DBG_C_NumToAddr    */      DbgNumToAddr,
/*   DBG_C_ReadCoRegs   */      DbgCoRegs,
/*   DBG_C_WriteCoRegs  */      DbgCoRegs,
/*   reserved           */      DbgNull,
/*   DBG_C_ThrdStat     */      DbgThrdStat,
/*   DBG_C_MapROAlias   */      DbgAlias,
/*   DBG_C_MapRWAlias   */      DbgAlias,
/*   DBG_C_UnMapAlias   */      DbgAlias,
/*   DBG_C_Connect      */      DbgConnect,
/*   DBG_C_ReadMemBuf   */      DbgReadBuf,
/*   DBG_C_WriteMemBuf  */      DbgWriteBuf,
/*   DBG_C_SetWatch     */      DbgSetWatch,
/*   DBG_C_ClearWatch   */      DbgClearWatch,
/*   DBG_C_RangeStep    */      DbgSStep,
/*   DBG_C_Continue     */      DbgContinue,
/*   DBG_C_AddrToObject */      DbgAddrToObject,
/*   DBG_C_XchgOpcode   */      dbgXchgOpcode,
/*   DBG_C_LinToSel A001 */     DbgLinearToSel,         /* 32 to 16    A001*/
/*   DBG_C_SelToLin A001 */     DbgSelToLinear,         /* 16 to 32    A001*/
/*   DBG_C_RegisterSemList */   DbgNull,     /* Not used in function table */
/*   Invalid function   */      DbgNull
};

int DBG32ApiDebug (uDB_t *puDB,ulong_t debugInterface)
{


    ULONG rt;
    ULONG save_rt;
    DCB_t *pdcb;
    kDB_t *pkDB;
#ifdef DBGSTRICT
    ULONG P1 = 0L, P2 = 0L;
#endif

    if ((rt = (ULONG)VMAllocKHB(VM_HKH_PUB_RESRW, sizeof(kDB_t), DBGKDBTMPOWNER, NA,
            SSToDS(&pkDB))) != NO_ERROR)
        return(rt);

#ifdef DBGSTRICT
    if (DBGBugBug & DBGinput)
    {
       P1 = (ULONG) puDB;
       P2 = (ULONG) pkDB;

       printf("\n\n DosDebug INPUT puDB is: %4x, pkDB is: %4x \n\n,  \\
               \n    Pid      is: %4x,              \\
               \n    Tid      is: %4x,              \\
               \n    Cmd      is: %4x, %4d,         \\
               \n    Value    is: %4x,              \\
               \n    Addr     is: %4x,              \\
               \n    Buffer   is: %4x,              \\
               \n    Len      is: %4x,              \\
               \n    Index    is: %4x,              \\
               \n    Mte      is: %4x",
                      P1, P2,
                      puDB->Pid,
                      puDB->Tid,
                      puDB->Cmd, puDB->Cmd,
                      puDB->Value,
                      puDB->Addr,
                      puDB->Buffer,
                      puDB->Len,
                      puDB->Index,
                      puDB->MTE);
    }
    else
    if (DBGBugBug & DBGoutput)
    {  printf("\n\nDosDebug Input Cmd is: %4x, %4d\n\n",puDB->Cmd, puDB->Cmd);  }
#endif

    if ((rt = DbgkDBRead(puDB, pkDB)) == NO_ERROR)

        if (pkDB->lCmd == DBG_C_RegisterSemList) {
           rt = DbgRegisterSemList(pkDB);
           VMFreeKHB(VM_HKH_PUB_RESRW,pkDB);
           return(rt);
        }

        if ((rt = DbgGetDCB(pkDB, SSToDS(&pdcb))) == NO_ERROR) {

            if (pdcb->flState & DBGDCBS_SEMLIST)
               TKBlock(DBGID_SRLOCK(pdcb),-1,0);

            switch(debugInterface) {                                          // B729833
               case DBGP_PTRACE:                                              // B729833
                  if (pdcb->pptdaDbgee->ptda_flDbg & DBGP_DEBUG)                         // B729833
                     rt = ERROR_ACCESS_DENIED;                                // B729833
                  break;                                                      // B729833
               case DBGP_DEBUG:                                               // B729833
                  if (pdcb->pptdaDbgee->ptda_flDbg & DBGP_PTRACE)                        // B729833
                     rt = ERROR_ACCESS_DENIED;                                // B729833
                  break;                                                      // B729833
               default:                                                       // B729833
                  dbgAssert(FALSE);                                           // B729833
            }

            if (rt == NO_ERROR) {                                                // B729833
               pdcb->pptdaDbgee->ptda_flDbg |= debugInterface;                              // B729833
               pkDB->uDBTemp.Cmd = DBG_N_Success;        // assume success
               rt = apfnDbgCmd[pkDB->lCmd](pkDB, pdcb);
               DbgReleaseDCB(pdcb);
            }
        }

    if ((rt == NO_ERROR) || (rt == ERROR_INVALID_DATA))
       { save_rt = rt;                                      /*          A005 */
         rt = DbgkDBWrite(pkDB);                            /*          A005 */
          if (save_rt != NO_ERROR)  rt = save_rt; }         /*          A005 */

#ifdef DBGSTRICT
    if (DBGBugBug & DBGoutput)
    {
       printf("\n\n DosDebug OUTPUT puDB is: %4x, pkDB is: %4x \n,   \\
               \n    Return Code is: %4x,   \\
               \n    Pid      is: %4x,      \\
               \n    Tid      is: %4x,      \\
               \n    Cmd      is: %4x, %4d, \\
               \n    Value    is: %4x, %4d, \\
               \n    Addr     is: %4x,      \\
               \n    Buffer   is: %4x,      \\
               \n    Len      is: %4x,      \\
               \n    Index    is: %4x,      \\
               \n    Mte      is: %4x",
                      P1, P2,
                      rt,
                      puDB->Pid,
                      puDB->Tid,
                      puDB->Cmd, puDB->Cmd,
                      puDB->Value, puDB->Value,
                      puDB->Addr,
                      puDB->Buffer,
                      puDB->Len,
                      puDB->Index,
                      puDB->MTE);
    }
#endif

    VMFreeKHB(VM_HKH_PUB_RESRW,pkDB);

    return(rt);

#else /* RAS */

int DBG32ApiDebug ()
{
    return(ERROR_ACCESS_DENIED);

#endif /* RAS */

}
#pragma code_seg()
#pragma code_seg("SWAP32_15","CODE")
/***EP  VR32Debug - Verification Routine for the DosDebug API function
 *
 *      VRDebug allocates the kDB from the kernel heap.
 *
 *      This function is swappable.
 *
 *      ENTRY
 *              puDB = Pointer to a user Debug Buffer
 *
 *      EXIT
 *              Return Value == 0
 *                      No Error.       See returned values in uDB for more
 *
 *              Return Value != 0
 *                      Fatal Error
 *                      Command possibly incomplete
 *                      uDB invalid
 *
 *      Pseudocode:
 *      DBG32ApiDebug(puDB,DBGP_DEBUG)                                           // B729833
 */


int cdecl VR32DEBUG (THUNKSEF sef, uDB_t *puDB)
{

#ifdef INCDEBUG
    return(DBG32ApiDebug(puDB,DBGP_DEBUG));                                      // B729833
#else
    return(ERROR_INVALID_FUNCTION);
#endif  /* DEBUG */
}
#pragma code_seg()
