/*static char *SCCSID = "@(#)debnet.c   6.1 90/11/17";*/
/*static char *SCCSID = "@(#)debnet.c   13.17 90/03/01";*/

/*
 *      DEBNET.C
 *
 *      Communications protocol
 *
 */

#if defined(KDP)

#include "debdef.h"
#include "debkdp.h"
#include "error.h"

/*      CmdInputBuffer
 *
 *      Raw command requests are put in CmdInputBuffer to be executed by
 *      the Kernel Debugger command routines.  Commands end with a 0x00,
 *      without a CR or LF.
 */
extern UCHAR CmdInputBuffer[];

extern struct RegSA_struc RegSA;
extern UCHAR fKDPRaw;
extern UCHAR fKDPReceived;
extern UCHAR fKDPPacketMode;
extern USHORT KDPhMTE;
extern ULONG KDPpMTE;
extern ULONG KDPSlot;
extern UCHAR fKDPTaskNotifyEnable;
extern USHORT KDPPageinCount;
extern PageinStruct KDPPageinTable[];
extern USHORT KDPPageHMTE;
extern ULONG  KDPPageAddr;



/* External worker routines
 *
 *      KDPRaw - C interface to Kernel Debugger command execution routine,
 *              does not return.
 *
 *      The following routines add and remove packet start and end bytes
 *      so that the network layer doesn't need to do it.
 *
 *      KDPDSend - Sends a buffer of bytes to the front end.
 *
 *      KDPDReceive - Receives a packet from the front end, returns count
 *              of bytes received.
 */

extern void KDPRaw(void); /* debcmd.asm */
extern USHORT KDPReadMem(AddrS *,UCHAR *,USHORT *); /* debcmd.asm */
extern USHORT KDPWriteMem(UCHAR *,AddrS *,USHORT); /* debcmd.asm */
extern void KDPTrace(void); /* debtrap.asm */
extern void KDPGo(void); /* debtrap.asm */
extern void KDPDSend(UCHAR *, USHORT); /* debiocs.asm */
extern USHORT KDPDReceive(UCHAR *); /* debiocs.asm */
extern USHORT KDPObjInfo(USHORT,USHORT,USHORT *,ULONG *,ULONG *); /* dostask.asm */
extern USHORT KDPhmte2pmte(USHORT, ULONG *); /* dostask.asm */
extern USHORT KDPpmte2name(ULONG,UCHAR *); /* dostask.asm */
extern USHORT KDPNextModule(USHORT *, ULONG *); /* dostask.asm */
extern void KDPReboot(void); /* dosoem.asm */
extern USHORT KDPGetAccAtr(AddrS *); /* debcmd.asm */

/* JWE start */
typedef struct {
  ULONG base;
  ULONG limit;
  USHORT flags;
} selInfo_t;
extern USHORT KDPQueryThread(ULONG *,USHORT *,USHORT *,USHORT *,USHORT *); /* dostask.asm */
extern USHORT KDPSelToLin(AddrS *,ULONG *); /* debmem.asm */
extern USHORT KDPGetSelInfo(AddrS *,selInfo_t *); /* debmem.asm */
extern USHORT KDPObjCount(USHORT,ULONG *); /* dostask.asm */
extern USHORT KDPObjInfo2(USHORT,USHORT,USHORT *,ULONG *,ULONG *,ULONG *); /* dostask.asm */
extern USHORT KDPGetRegs(USHORT,struct RegSA_struc *); /* dostask.asm */
extern USHORT KDPPutRegs(USHORT,struct RegSA_struc *); /* dostask.asm */
extern USHORT KDPGetNPX(USHORT,UCHAR *); /* dostask.asm */
extern USHORT KDPPutNPX(USHORT,UCHAR *); /* dostask.asm */
extern void ScreenOut(ULONG,ULONG,ULONG,ULONG); /* dostask.asm */
/* JWE end */


/***LD  Current Command
 *
 *      When fKDPReceived == TRUE,
 *              cvkCmd contains the current command
 *              cvkbSequenceNumber = current command sequence number
 *              cvkbIndexNumber = next index number to use
 */

static cvkCmd_t cvkCmd;
static AddrS cvkRangeStart;
static AddrS cvkRangePrev;
static UCHAR cvkbSequenceNumber;
static UCHAR cvkbIndexNumber = 0;


/***LD  Packet Receive Buffer
 *
 *      Packets are received into this buffer which is shared by the data
 *      and network layers.
 */

static UCHAR cvkabRecBuf[ CVK_RECBUF_SIZE ];
static UCHAR *cvkpbRecBuf;



/***LP  cvkDStuffSum - Bitstuff and checksum
 *
 *      cvkDStuffSum bitstuffs the passed source buffer into the passed
 *      destination buffer and adds a two byte checksum to the end of the
 *      data.
 *
 *      ENTRY
 *              pbSrc = pointer to source buffer
 *              pbDest = pointer to destination buffer
 *              cbSrc = length of source buffer
 *
 *      EXIT
 *              Return value = number of new bytes in destination buffer
 *
 *      Pseudocode:
 *
 *              Bitstuff source array and calculate checksum
 *
 *              Bitstuff checksum
 *
 *              return( number of new bytes in dest. buffer )
 */

USHORT cvkDStuffSum( UCHAR  *pbSrc,
                     UCHAR  *pbDestOrig,
                     USHORT  cbSrc )
{
    UCHAR iBit = 1;
    USHORT sChecksum = CVK_CHECKSUM_SEED;
    register UCHAR *pbDest;

    pbDest = pbDestOrig;
    *pbDest = 0x80;

    while (cbSrc)
    {
        *pbDest |= (*pbSrc >> iBit);
        *(++pbDest) = (UCHAR)( (*pbSrc << (7-iBit)) | 0x80 );

        if (++iBit == 8)
        {
            iBit = 1;
            *(++pbDest) = 0x80;
        }

        sChecksum += *(pbSrc++), sChecksum = sRotL( sChecksum, 3 );
        cbSrc--;
    }

    cbSrc = sizeof(sChecksum);
    pbSrc = (UCHAR *) &sChecksum;

    do
    {
        *pbDest |= (*pbSrc >> iBit);
        *(++pbDest) = (UCHAR)( (*(pbSrc++) << (7-iBit)) | 0x80 );

        if (++iBit == 8)
        {
            iBit = 1;
            *(++pbDest) = 0x80;
        }

    } while (--cbSrc);

    if (iBit > 1)
        pbDest++;

    return( pbDest - pbDestOrig );
}




/***LP  cvkDUnStuffSum - Unbitstuff and verify checksum
 *
 *      cvkDUnStuffSum unbitstuffs from the global receive buffer into the
 *      destination buffer and verifies the two byte checksum at the end of
 *      the data.  If an error occurs, the receive buffer pointer is left
 *      at the error.  If no error occurs, the receive buffer pointer is
 *      left after the data that was unstuffed.
 *
 *      ENTRY
 *              pbDest = pointer to destination buffer
 *              cbDest = pointer to length of destination buffer (returned)
 *
 *      EXIT
 *              Return value = 0
 *                      No error
 *              Return value = CVK_ERROR_UNSTUFF
 *                      Error (unbitstuffed byte received)
 *
 *      Pseudocode:
 *
 *      Unbitstuff data, calculate checksum
 *
 *      Unbitstuff checksum, match with calculated checksum
 */

USHORT cvkDUnStuffSum( UCHAR  *pbDest,
                       USHORT  cbDest )
{
    USHORT iBit = 1;
    USHORT sChecksum = CVK_CHECKSUM_SEED;
    USHORT sRecChecksum;

    while (cbDest)
    {
        if (!(*cvkpbRecBuf & 0x80))
            return( CVK_ERROR_UNSTUFF );

        *pbDest = (UCHAR)( *(cvkpbRecBuf++) << iBit );

        if (!(*cvkpbRecBuf & 0x80))
            return( CVK_ERROR_UNSTUFF );

        *pbDest |= ((*cvkpbRecBuf & 0x7f) >> (7 - iBit));

        if (++iBit == 8)
        {
            iBit = 1;
            cvkpbRecBuf++;
        }

        sChecksum += *(pbDest++), sChecksum = sRotL( sChecksum, 3 );
        cbDest--;
    }

    cbDest = sizeof(sRecChecksum);
    pbDest = (UCHAR *) &sRecChecksum;

    do
    {
        if (!(*cvkpbRecBuf & 0x80))
            return( CVK_ERROR_UNSTUFF );

        *pbDest = (UCHAR)( *(cvkpbRecBuf++) << iBit );

        if (!(*cvkpbRecBuf & 0x80))
            return( CVK_ERROR_UNSTUFF );

        *pbDest |= ((*cvkpbRecBuf & 0x7f) >> (7 - iBit));
        pbDest++;

        if (++iBit == 8)
        {
            iBit = 1;
            cvkpbRecBuf++;
        }
    } while (--cbDest);

    if (iBit > 1)
        cvkpbRecBuf++;

    if (sChecksum != sRecChecksum)
        return( CVK_ERROR_CHECKSUM );

    return( CVK_NO_ERROR );
}




/***LP  cvkNSend - Network Layer Send
 *
 *      cvkNSend sends data packets, ACKs, and NACKs to the target kernel.
 *      It remembers the last data packet sent so retransmits do not require
 *      recreating the bitstuffed buffer.  Higher level routines do not
 *      call cvkNSend directly;  instead, they use one of the following
 *      access macros:  cvkNSendPacket(), cvkNSendACK(), or cvkNSendNACK().
 *
 *      ENTRY
 *              dwPacketId = Packet Id
 *              abSendBuffer = buffer to transmit
 *              cbSendBuffer = length of buffer
 *
 *      EXIT
 *              Return value = CVK_NO_ERROR
 *                      No error
 *                      = Dos Error code
 *                      Dos Error
 *
 *      Pseudocode:
 *
 *      UCHAR abHeader[8];
 *      static ULONG dwBufferId = 0xffffffff;  // Invalid ID
 *      static UCHAR abLargeBuffer[ CVK_PACKET_MAXSIZE ];
 *      static USHORT cbLargeBuffer = 0;
 *      UCHAR abSmallBuffer[ CVK_PACKET_MINSIZE ];
 *      UCHAR abPacketBuffer[];
 *
 *      abPacketBuffer = abLargeBuffer;
 *
 *      if (dwPacketId != large buffer Id) {
 *              if (cbSendBuffer == 0)
 *                      abPacketBuffer = abSmallBuffer;
 *
 *              Put dwPacketId in header buffer
 *              Put cbSendBuffer in header buffer
 *
 *              Bitstuff header buffer into abPacketBuffer
 *
 *              if (cbSendBuffer != 0) {
 *                      Bitstuff abSendBuffer into abPacketBuffer
 *                      set large buffer id = dwPacketId
 *              }
 *      }
 *
 *      return( cvkDSend(abPacketBuffer,cbPacketBuffer) );
 */

void cvkNSend( ULONG  lPacketId,
               UCHAR  abSendBuffer[],
               USHORT cbSendBuffer )
{
    cvkHdr_t cvkHdr;
    static ULONG lLargeBufferId = 0xffffffff;
    static UCHAR abLargeBuffer[ CVK_PACKET_MAXSIZE ];
    static USHORT cbLargeBuffer = 0;
    UCHAR abSmallBuffer[ CVK_STUFFEDHEADER_SIZE ];
    UCHAR *abPacketBuffer;
    USHORT cbPacketBuffer;

    abPacketBuffer = abLargeBuffer;
    cbPacketBuffer = cbLargeBuffer;

    if (lPacketId != lLargeBufferId)
    {
        if (cbSendBuffer == 0)
            abPacketBuffer = abSmallBuffer;

        cvkHdr.lId = lPacketId;
        cvkHdr.cbData = cbSendBuffer;

        cbPacketBuffer = cvkDStuffSum( (UCHAR *) &cvkHdr,
                                       abPacketBuffer,
                                       sizeof(cvkHdr_t) );

        if (cbSendBuffer != 0)
        {
            cbPacketBuffer += cvkDStuffSum( abSendBuffer,
                                            abPacketBuffer+cbPacketBuffer,
                                            cbSendBuffer );

            lLargeBufferId = lPacketId;
            cbLargeBuffer = cbPacketBuffer;
        }

    }

    KDPDSend( abPacketBuffer, cbPacketBuffer );
}




/***LP  cvkNReceive - Network layer receive
 *
 *      cvkNReceive picks up packets from the target kernel.  If the sequence
 *      numbers don't match, the packet is ignored.  Otherwise, the following
 *      chart shows the action taken:
 *
 *                              Packet Type
 *
 *                      ACK             NACK             DATA
 *              |=======================================================|
 *  R           |
 *  eACK/NACK   |  return(ACK)          return(NACK)    save
 *  q           |                                       DATA header
 *  u           |                                       return(DATA)
 *  e           |
 *  s           |=======================================================|
 *  t           |
 *        DATA  |  ignore               resend          return(DATA)
 *  T           |                                       original
 *  y           |                                       packet
 *  p           |
 *  e           |=======================================================|
 *
 *
 *      If the request was for DATA, the saved DATA header is checked for a
 *      match first.
 *
 *      ENTRY
 *              bControl = CVK_NREC_ACKNACK or CVK_NREC_DATA
 *              bSequenceNumber = Sequence number of packet sent
 *              abReceiveBuffer = Address to put data
 *              pcbReceiveBuffer = Address to put buffer length
 *
 *      EXIT
 *              Return Value = CVK_NREC_DATA
 *                      Data was received
 *                      = CVK_NREC_ACK
 *                      Packet was acknowledged
 *                      = CVK_NREC_NACK
 *                      Retransmit
 *
 *      Pseudocode:
 *
 *      static USHORT sHeldSequenceNumber = 0xff00;
 *      static USHORT sHeldLength = 0;
 *      static USHORT cbData;
 *      UCHAR bHeaderBuffer[8];
 *      USHORT sRetValue;
 *
 *      if (sHeldSequenceNumber != (USHORT) bSequenceNumber ||
 *               sControl != CVK_NREC_DATA)
 *
 *              for (;;) {
 *                      if (sRetValue = KDPDReceive(cvkabRecBuf))
 *                              return(sRetValue);
 *
 *                      if (sRetValue = cvkDWaitNBytes(CVK_PACKET_MINSIZE)) {
 *                              return(sRetValue);
 *                      }
 *
 *                      Unstuff next 9 bytes into header buffer
 *                      if (unstuffing error) // error leaves buffer unchanged
 *                              continue;
 *
 *                      if (not NULL header) {
 *                              pull out sequence number
 *                              if sequence number does not match
 *                                      continue;
 *                      }
 *
 *                      if (sControl == CVK_NREC_ACKNACK) {
 *                              if (header is DATA) {
 *                                      save header info (sHeldSequenceNumber, sHeldLength);
 *                                      return( CVK_NREC_ACK );
 *                              }
 *                              if (header is NULL) {
 *                                      save header info (sHeldSequenceNumber, sHeldLength);
 *                                      return( CVK_NREC_TIMEOUT );
 *                              }
 *                              return( header == ACK ? CVK_NREC_ACK : CVK_NREC_NACK );
 *                      }
 *                                                                                      // sControl is DATA
 *                      if (header is DATA)
 *                              break;
 *
 *                      if (header is NULL) {
 *                              *pcbReceiveBuffer = 0;
 *                              return( CVK_NREC_NULL );
 *                      }
 *
 *                      if (header is NACK)
 *                              if (sRetValue = cvkNSendPacket(bsequencenumber,NULL,0))
 *                                      return( sRetValue );
 *              } // for
 *
 *      // At this point, we are ready to pick up the incoming data.
 *
 *      if (sRetValue = cvkDWaitNBytes(cbBodyData))
 *              return(sRetValue);
 *
 *      Unbitstuff cbData bytes into abReceiveBuffer, calculate checksum
 *      if (unstuffing error)
 *              return( CVK_ERROR_NOISE );
 *
 *      *pcbReceiveBuffer = cbData;
 *
 *      return( CVK_NREC_DATA );
 */

USHORT cvkNReceive( USHORT  sControl,
                    UCHAR  *pbSequenceNumber,
                    UCHAR  *pbIndex,
                    UCHAR   abReceiveBuffer[],
                    USHORT *pcbReceiveBuffer,
                    UCHAR  *pfLast )
{
    static cvkHdr_t cvkHdrHeld = { 0xffffffff, 0 };
    static UCHAR fHeld = FALSE;
    cvkHdr_t cvkHdr;

    if (sControl != CVK_NREC_DATA || !fHeld)
    {
        for (;;)
        {
            while ( KDPDReceive(cvkabRecBuf) < CVK_STUFFEDHEADER_SIZE );

            cvkpbRecBuf = cvkabRecBuf;

            if ( cvkDUnStuffSum( (UCHAR *)&cvkHdr, sizeof(cvkHdr_t) ) )
                continue;

            if ( CVK_HDR_ACKNACK(cvkHdr) )
            {
                if (sControl != CVK_NREC_ACKNACK)
                    continue;

                *pbSequenceNumber = CVK_ACKNACK_SEQ(cvkHdr);
                *pbIndex = CVK_ACKNACK_IND(cvkHdr);

                if (CVK_HDR_NACK(cvkHdr))
                    return( CVK_NREC_NACK );
                return( CVK_NREC_ACK );
            }

            /* CVK_HDR_DATA(cvkHdr) */

            /* DATA received */
            cvkHdrHeld.lId = cvkHdr.lId;
            cvkHdrHeld.cbData = cvkHdr.cbData;
            if (sControl == CVK_NREC_ACKNACK)
            {
                cvkHdrHeld.lId = cvkHdr.lId;
                fHeld = TRUE;
                return( CVK_NREC_DATA );
            }
            /* get out to pick up data! */
            break;
        }
    }

    *pbSequenceNumber = CVK_DATA_SEQ(cvkHdrHeld);
    *pbIndex = CVK_DATA_IND(cvkHdrHeld);
    *pcbReceiveBuffer = cvkHdrHeld.cbData;
    *pfLast = (UCHAR) CVK_DATA_LAST(cvkHdrHeld);
    fHeld = FALSE;

    if ( cvkDUnStuffSum( abReceiveBuffer, cvkHdrHeld.cbData ) )
        return( CVK_ERROR_UNSTUFF );

    return( CVK_NREC_DATA );
}





/***LP  cvkTSend - Transport layer send
 *
 *      In this implementation, all commands fit in a single packet, so they
 *      do not need to be broken up here.  cvkTSend sends the packet and
 *      checks the target kernel's response, resending if appropriate.
 *
 *      ENTRY
 *              pbCommandBuffer = command
 *              cbCommandBuffer = length of buffer
 *              bSequenceNumber = command sequence number
 *
 *      EXIT
 *              Return Value = CVK_NO_ERROR
 *                      No error
 *                      = CVK_ERROR_TIMEOUT
 *                      No response from target machine
 *                      = CVK_ERROR_NOISE
 *                      Unable to send clear data
 *
 *      Pseudocode:
 *
 */

USHORT cvkTSend( UCHAR  *pbCommandBuffer,
                 USHORT  cbCommandBuffer )
{
    UCHAR bSequenceNumber;
    UCHAR bIndexNumber;

    for (;;)
    {
        cvkNSendPacket( cvkbSequenceNumber,
                        cvkbIndexNumber,
                        FALSE,
                        pbCommandBuffer,
                        cbCommandBuffer );

        switch ( cvkNRecACKNACK( &bSequenceNumber, &bIndexNumber ) )
        {
            case CVK_NREC_ACK:
                if (    bSequenceNumber != cvkbSequenceNumber
                     || bIndexNumber != cvkbIndexNumber )
                            continue;

                    /* fall through */

            case CVK_NREC_DATA:

                    cvkbIndexNumber = 0;
                    return( CVK_NO_ERROR );

            case CVK_NREC_NACK:
                    continue;
        }
    }
}




/***LP  cvkTReceive - Transport layer receive notification
 *
 *      In this implementation, all notifications fit in a single packet, so
 *      packets do not need to be combined here.  cvkTReceive waits for a
 *      notification for the argument sequence number, requesting a
 *      retransmit as needed.
 *
 *      ENTRY
 *              bSequenceNumber = expected notification sequence number
 *              pbReceiveBuffer = receive buffer address
 *              pcbReceiveBuffer = address to store buffer length
 *
 *      EXIT
 *              Return Value = CVK_NO_ERROR
 *                      No error
 *                      pbRecevieBuffer = Notification
 *                      pcbReceiveBuffer = Notification length
 *
 *              Return Value = CVK_ERROR_TIMEOUT
 *                      No response from target machine
 *                      = CVK_ERROR_NOISE
 *                      Unable to receive clear data
 *
 *      Pseudocode:
 *
 */

USHORT cvkTReceive( UCHAR  *pbSequenceNumber,
                    UCHAR  *pbReceiveBuffer,
                    USHORT *pcbReceiveBuffer )
{
    UCHAR fLast;
    UCHAR bIndex;

    while ( cvkNRecPacket( pbSequenceNumber,
                           &bIndex,
                           pbReceiveBuffer,
                           pcbReceiveBuffer,
                           &fLast ) != CVK_NREC_DATA )
    {
        cvkNSendNACK( *pbSequenceNumber, bIndex );
    }

    cvkNSendACK( *pbSequenceNumber, bIndex );

    return(CVK_NO_ERROR);
}




/***EP  KDPRawOutput - Send raw output
 *
 *      Raw output is buffered in a packet until an entire line is ready to
 *      be sent.
 *
 *      ENTRY
 *              bData = character to output
 *
 *      EXIT
 *              None
 *
 *      Pseudocode:
 *
 */

USHORT KDPRawOutput( UCHAR bData )
{
    static cvkCmd_t cvkCmdText = { CVK_CMD_RAW };
    static UCHAR *pbText = (UCHAR *) &cvkCmdText + CVK_CMDSIZE_RAW;
    static UCHAR *pbTextIndex = (UCHAR *) &cvkCmdText + CVK_CMDSIZE_RAW;
    UCHAR bSeq;
    UCHAR bInd;

    if (bData && bData != CR)
        *(pbTextIndex++) = bData;

    if (bData == LF || (!bData && pbTextIndex != pbText))
    {
        *(pbTextIndex++) = '\0';
        cvkCmdText.Value = (ULONG) (pbTextIndex - pbText);

        pbTextIndex = pbText;   /* Reset pbTextIndex BEFORE the Send */

        for (;;)
        {
            cvkNSendPacket( cvkbSequenceNumber,
                            cvkbIndexNumber,
                            TRUE,
                            (UCHAR *) &cvkCmdText,
                            (USHORT) (cvkCmdText.Value+CVK_CMDSIZE_RAW) );

            switch ( cvkNRecACKNACK( &bSeq, &bInd ) )
            {
                case CVK_NREC_DATA:
                   cvkbIndexNumber = 0;
                   return(CVK_NREC_DATA);

                case CVK_NREC_NACK:
                   continue;
            }

            break;
        }

        cvkbIndexNumber++;
        cvkbIndexNumber &= 0x3f;
    }

    return( CVK_NO_ERROR );
}




/***EP  KDPEventNotify - Send notification packet
 *
 *      We come here if kernel execution has stopped for any reason -
 *      module load/unload, single step, breakpoint, async stop, etc.
 *
 *      The fEvent code is translated into a Ptrace compatible value.
 *
 *      ENTRY
 *              fEvent - Event code
 *
 *      EXIT
 *              None
 *
 *      Pseudocode:
 *
 */

/* JWE start */
void KDPEventNotify( USHORT fEvent )
{
    register UCHAR *pbSrc;
    register UCHAR *pbDest;
    USHORT cb;
    USHORT fReg = TRUE;
    ULONG  dummy;
    unsigned i,j;

    if (!fKDPReceived)
    {
       if (fEvent != KDP_T_ASYNC_TRAP)
         ;
         /* Internal Error */
       return;
    }

    dummy = -1; /* Get info about scheduled thread */
    KDPQueryThread(&dummy,
                   &(cvkCmd.SegV),
                   &(cvkCmd.PID),
                   &(cvkCmd.TID),
                   &(cvkCmd.MTE));

    switch (fEvent)
    {
        case KDP_T_PAGEIN:
            cvkCmd.Cmd  = CVK_RET_PAGEIN;
            cvkCmd.MTE  = KDPPageHMTE;
            cvkCmd.OffV = KDPPageAddr;
            cb          = CVK_EVENTSIZE;
            KDPPageHMTE = 0;
            KDPPageAddr = 0;
            fReg        = FALSE;
            break;

        case KDP_T_TASK_CREATE:
            cvkCmd.Cmd = CVK_RET_TNEW;
            cvkCmd.PID = (USHORT)(KDPSlot >> 16);
            cvkCmd.TID = (USHORT)(KDPSlot & 0x0ffff);
            cb = CVK_EVENTSIZE;
            KDPSlot = 0;
            fReg = FALSE;
            break;

        case KDP_T_TASK_END:
            cvkCmd.Cmd = CVK_RET_TEND;
            cb = CVK_EVENTSIZE;
            KDPSlot = 0;
            fReg = FALSE;
            break;

        case KDP_T_UNLINK:
            cvkCmd.Cmd = CVK_RET_KIL;
            cvkCmd.Value = (ULONG) KDPhMTE;
            pbDest = (UCHAR *) &cvkCmd.Reg;
            cb = CVK_EVENTSIZE + KDPpmte2name(KDPpMTE,pbDest);
            KDPpMTE = 0L;
            KDPhMTE = 0;
            fReg = FALSE;
            /*
            * Delete any PAGEIN notification requests for pages in the module.
            */
            for (i = 0; i < KDPPageinCount; i++)
              if (KDPPageinTable[i].hMTE == (USHORT)cvkCmd.Value)
              {
                for (j = i + 1; j < KDPPageinCount; j++)
                  KDPPageinTable[j - 1] = KDPPageinTable[j];

                KDPPageinCount -= 1;
              }

            break;

        case KDP_T_LINK:
            cvkCmd.Cmd = CVK_RET_LIB;
            cvkCmd.Value = (ULONG) KDPhMTE;
            pbDest = (UCHAR *) &cvkCmd.Reg;
            cb = CVK_EVENTSIZE + KDPpmte2name(KDPpMTE,pbDest);
            KDPpMTE = 0L;
            KDPhMTE = 0;
            fReg = FALSE;
            break;

        case KDP_T_SSTEP:
            if (cvkCmd.Cmd == CVK_CMD_RSTEP)
            {
                if (    RegSA.CSIPsav.AddrOff > cvkRangeStart.AddrOff
                     && RegSA.CSIPsav.AddrOff < cvkCmd.OffV
                     && RegSA.CSIPsav.AddrSeg == cvkRangeStart.AddrSeg )
                {
                    cvkRangePrev.AddrOff = RegSA.CSIPsav.AddrOff;
                    KDPTrace(); /* does not return */
                }
                cvkCmd.OffV = cvkRangePrev.AddrOff;
            }
            cvkCmd.Cmd = CVK_RET_TBT;
            break;

        case KDP_T_ASYNC_TRAP:
            cvkCmd.Cmd = CVK_RET_ASYNC;
            break;

        case KDP_T_SUCCESS:
            cvkCmd.Cmd = CVK_RET_SUC;
            break;

        case KDP_T_BREAKPOINT:
            cvkCmd.Cmd = CVK_RET_BPT;
            break;

        case KDP_T_NMI:
            cvkCmd.Cmd = CVK_RET_NMI;
            break;

        default:
            cvkCmd.Cmd = CVK_RET_GPF;
            cvkCmd.Value = (ULONG) fEvent;
    }

    /* copy register info */
    if (fReg)
    {
        pbSrc = (UCHAR *) &RegSA;
        pbDest = (UCHAR *) &cvkCmd.Reg;
        cb = sizeof(cvkCmd.Reg);
        do
        {
            *(pbDest++) = *(pbSrc++);
        } while (--cb);
        cvkCmd.DBit = (USHORT) KDPGetAccAtr( &cvkCmd.Reg.CSIPsav );

        /*
        * If we're in real or V86 mode, we can construct the linear address
        * corresponding to CS:(E)IP directly.  Otherwise we call KDPSelToLin.
        */
        if ((cvkCmd.Reg.EFsav  & 0x20000) != 0 || // V86 mode
            (cvkCmd.Reg.CR0sav & 0x00001) == 0)   // Real mode
          cvkCmd.OffV = (((ULONG)cvkCmd.Reg.CSIPsav.AddrSeg) << 4) +
                         cvkCmd.Reg.CSIPsav.AddrOff;
        else
          KDPSelToLin(&cvkCmd.Reg.CSIPsav,&cvkCmd.OffV);

        cb = CVK_EVENTSIZE + sizeof(cvkCmd.Reg);
    }

    cvkTSend((UCHAR *)&cvkCmd,cb);
    fKDPReceived = FALSE;
}




/***EP  KDPMain - Handle command packets
 *
 *      If the command can be performed without leaving the kernel debugger,
 *      do the command.  Otherwise, let the kernel execute and the response
 *      will be handled by KDPEventNotify.
 *
 *      ENTRY
 *              None
 *
 *      EXIT
 *              None
 *
 *      Pseudocode:
 *
 */

USHORT KDPMain()
{
    USHORT cbCmd;
    USHORT iaKob;
    register UCHAR *pbSrc;
    register UCHAR *pbDest;
    USHORT cb;
    AddrS addr;
    USHORT hMTE;
    ULONG pMTE;
    USHORT rc;
    USHORT scheduledSlot;

    for (;;)
    {
        // BUGBUG - cvkCmd is a structure, note a char ????
        if ( cvkTReceive( &cvkbSequenceNumber, (UCHAR*)&cvkCmd, &cbCmd ) )
            return(TRUE);   /* return value not used */

        fKDPReceived = TRUE;

        if (    cvkCmd.Cmd != CVK_CMD_RAW
             && cvkCmd.Cmd != CVK_CMD_RAWCHAR )
            fKDPRaw = FALSE;

       switch (cvkCmd.Cmd)
       {
            case CVK_CMD_LIBNAME:
                pbDest = (UCHAR *)&cvkCmd + CVK_RETSIZE_LIBNAME;
                cb = CVK_RETSIZE_LIBNAME;
                if (    !KDPhmte2pmte( (USHORT)cvkCmd.Value, &pMTE ) )
                {
                    cvkCmd.Value = (ULONG)KDPpmte2name( pMTE, pbDest );
                    cvkCmd.Cmd = CVK_RET_SUC;
                    cb += (USHORT) cvkCmd.Value;
                }
                else
                {
                    cvkCmd.Cmd = CVK_RET_ERR;
                    cb = CVK_RETSIZE_RC;
                }
                break;

            case CVK_CMD_RSTEP:
                cvkRangeStart.AddrOff = cvkRangePrev.AddrOff
                                      = RegSA.CSIPsav.AddrOff;
                cvkRangeStart.AddrSeg = RegSA.CSIPsav.AddrSeg;

            case CVK_CMD_STEP:
                KDPTrace();
                /* will not return */

            case CVK_CMD_RAW:
                pbSrc = ((UCHAR *) &cvkCmd) + CVK_CMDSIZE_RAW;
                pbDest = CmdInputBuffer;
                do
                {
                    *(pbDest++) = *pbSrc;
                } while ( *(pbSrc++) );
                KDPRaw();   /* may or may not return */
                return(TRUE);   /* return value not used */

            case CVK_CMD_SCANMTE:
            {
                ULONG count;

                cvkCmd.Cmd = CVK_RET_SUC;
                cvkCmd.Value = 0L;
                cb = CVK_RETSIZE_SCANMTE;
                while(KDPNextModule(&hMTE,&pMTE) == NO_ERROR)
                {
                    cvkCmd.Value = (ULONG) hMTE;
                    /*
                    * Get the number of "real" segments in the module.  If
                    * we can't get this information (e.g., the SMTE is paged
                    * out), ignore the module for now...
                    */
                    if (KDPObjCount(hMTE,&count) != 0)
                        continue;

                    if (count != 0) // Ignore "forwarder" modules
                    {
                        pbDest = (UCHAR *) &cvkCmd + CVK_RETSIZE_SCANMTE;
                        cb += KDPpmte2name( pMTE, pbDest );
                        cvkCmd.Cmd = CVK_RET_LIB;
                        break;
                    }
                }
                break;
            }

            case CVK_CMD_RREG:
            {
                ULONG  dummy = -1;
                USHORT dum;

                rc = KDPQueryThread(&dummy,
                                    &scheduledSlot,
                                    &dum,
                                    &dum,
                                    &dum);

                if (rc != NO_ERROR)
                {
                  cvkCmd.Cmd = CVK_RET_ERR;
                  cb = CVK_RETSIZE_RC;
                  break;
                }

                /*
                * If caller is asking about thread that was running when we
                * got control, get information from RegSA structure...
                */
                if (cvkCmd.TID == scheduledSlot ||
                    cvkCmd.TID == 0)
                {
                    /* copy register info */
                    pbSrc  = (UCHAR *) &RegSA;
                    pbDest = (UCHAR *) &cvkCmd.Reg;
                    cb = sizeof(cvkCmd.Reg);
                    do
                    {
                        *(pbDest++) = *(pbSrc++);
                    } while (--cb);
                    cvkCmd.DBit = (USHORT)KDPGetAccAtr( &cvkCmd.Reg.CSIPsav );
                    cvkCmd.Cmd  = CVK_RET_SUC;
                    cb          = CVK_RETSIZE_RREG;
                }
                /*
                * If caller is asking about thread that was not running when we
                * got control, get information from KDP call (global regs,
                * such as CR0, GDT, etc., come from RegSA structure...)
                */
                else if (KDPGetRegs(cvkCmd.TID,&(cvkCmd.Reg)) == 0)
                {
                    cvkCmd.DBit = (USHORT)KDPGetAccAtr( &cvkCmd.Reg.CSIPsav );
                    cvkCmd.Cmd  = CVK_RET_SUC;
                    cb          = CVK_RETSIZE_RREG;
                }
                else
                {
                    cvkCmd.Cmd  = CVK_RET_ERR;
                    cb          = CVK_RETSIZE_RC;
                }

                /*
                * If we successfully obtained the registers, get the linear
                * address corresponding to CS:EIP
                */
                if (cvkCmd.Cmd == CVK_RET_SUC)
                  /*
                  * If we're in real or V86 mode, we can construct the linear
                  * address corresponding to CS:(E)IP directly.  Otherwise we
                  * call KDPSelToLin.
                  */
                  if ((cvkCmd.Reg.EFsav  & 0x20000) != 0 || // V86 mode
                      (cvkCmd.Reg.CR0sav & 0x00001) == 0)   // Real mode
                    *(ULONG *)&(cvkCmd.MemCache) =
                        (((ULONG)cvkCmd.Reg.CSIPsav.AddrSeg) << 4) +
                         cvkCmd.Reg.CSIPsav.AddrOff;
                  else if (KDPSelToLin(&cvkCmd.Reg.CSIPsav,
                                       (ULONG *)&(cvkCmd.MemCache)))
                  {
                      cvkCmd.Cmd  = CVK_RET_ERR;
                      cb          = CVK_RETSIZE_RC;
                  }
                break;
            }

            case CVK_CMD_WREG:
            {
                ULONG  dummy = -1;
                USHORT dum;

                rc = KDPQueryThread(&dummy,
                                    &scheduledSlot,
                                    &dum,
                                    &dum,
                                    &dum);

                if (rc != NO_ERROR)
                {
                  cvkCmd.Cmd = CVK_RET_ERR;
                  cb = CVK_RETSIZE_RC;
                  break;
                }

                /*
                * If caller is asking about thread that was running when we
                * got control, put information into RegSA structure...
                */
                if (cvkCmd.TID == scheduledSlot ||
                    cvkCmd.TID == 0)
                {
                    /* copy register info */
                    pbDest = (UCHAR *) &RegSA;
                    pbSrc  = (UCHAR *) &cvkCmd.Reg;
                    cb = sizeof(cvkCmd.Reg);
                    do
                    {
                        *(pbDest++) = *(pbSrc++);
                    } while (--cb);
                    cvkCmd.Cmd = CVK_RET_SUC;
                    cb         = CVK_RETSIZE_WREG;
                }
                /*
                * If caller is asking about thread that was not running when we
                * got control, put information using KDP call.
                */
                else if (KDPPutRegs(cvkCmd.TID,&(cvkCmd.Reg)) == 0)
                {
                    cvkCmd.Cmd = CVK_RET_SUC;
                    cb         = CVK_RETSIZE_WREG;
                }
                else
                {
                    cvkCmd.Cmd = CVK_RET_ERR;
                    cb         = CVK_RETSIZE_RC;
                }
                break;
            }


            case CVK_CMD_RMEM:
                addr.AddrOff = cvkCmd.OffV;
                addr.AddrSeg = cvkCmd.SegV;
                if (cvkCmd.SegV == 0)
                    addr.AddrType = EXPR_TYPE_LIN;
                else if ((RegSA.CR0sav & 0x1L) && !(RegSA.EFsav & 0x20000L))
                    addr.AddrType = EXPR_TYPE_SEL;
                else
                    addr.AddrType = EXPR_TYPE_SEG;

                addr.AddrSize = 0;
                addr.AddrTask = cvkCmd.TID;

                cvkCmd.Cmd = CVK_NO_ERROR;
                if ( KDPReadMem( &addr,
                                 (UCHAR *) &cvkCmd.Reg,
                                 (USHORT *) &cvkCmd.Value ) )
                {
                    cvkCmd.Cmd = CVK_RET_GPF;
                    cb = CVK_RETSIZE_RC;
                }
                else
                    cb = (USHORT)(CVK_RETSIZE_RMEM + cvkCmd.Value);
                break;

            case CVK_CMD_WMEM:
                addr.AddrOff = cvkCmd.OffV;
                addr.AddrSeg = cvkCmd.SegV;
                if (cvkCmd.SegV == 0)
                    addr.AddrType = EXPR_TYPE_LIN;
                else if ((RegSA.CR0sav & 0x1L) && !(RegSA.EFsav & 0x20000L))
                    addr.AddrType = EXPR_TYPE_SEL;
                else
                    addr.AddrType = EXPR_TYPE_SEG;

                addr.AddrSize = 0;
                addr.AddrTask = cvkCmd.TID;

                cvkCmd.Cmd = CVK_NO_ERROR;
                if ( KDPWriteMem( (UCHAR *) &cvkCmd.Reg,
                                  &addr,
                                  (USHORT) cvkCmd.Value ) )
                {
                    cvkCmd.Cmd = CVK_RET_GPF;
                    cb = CVK_RETSIZE_RC;
                }
                else
                    cb = CVK_RETSIZE_WMEM;
                break;

            case CVK_CMD_NUMTOBASE:
                iaKob = (USHORT) cvkCmd.Value;
                if (KDPObjInfo( cvkCmd.MTE,
                                iaKob,
                                &cvkCmd.SegV,
                                &cvkCmd.OffV,
                                &cvkCmd.Value ) == 0)
                {
                    cb = CVK_CMDSIZE_NUMTOBASE;
                    cvkCmd.Cmd = CVK_RET_SUC;
                }
                else
                {
                    cb = CVK_RETSIZE_RC;
                    cvkCmd.Cmd = CVK_RET_ERR;
                }
                break;

            case CVK_CMD_RAWCHAR:
                return( (USHORT)cvkCmd.Value );

            case CVK_CMD_DBIT:
                addr.AddrSeg = cvkCmd.SegV;
                addr.AddrType = EXPR_TYPE_SEL;
                addr.AddrSize = 0;
                addr.AddrTask = 0;
                cvkCmd.DBit = (USHORT) KDPGetAccAtr(&addr);
                cvkCmd.Cmd = CVK_NO_ERROR;
                cb = CVK_CMDSIZE_DBIT;
                break;

            case CVK_CMD_RUN:
                if (cvkCmd.Value == (long)-CVK_CMD_RUN)
                {
                    fKDPRaw              = FALSE;
                    fKDPReceived         = FALSE;
                    fKDPPacketMode       = FALSE;
                    fKDPTaskNotifyEnable = FALSE;
                    KDPPageinCount       = 0;
                }
                KDPGo();
                /* will not return */

            case CVK_CMD_KILL:
                KDPReboot();
                /* will not return */

            case CVK_CMD_SCANTCB:
            {
                ULONG dummy = -1;
                struct t {
                  USHORT slot;
                  USHORT PID;
                  USHORT TID;
                  USHORT MTE;
                  USHORT flags;
                } *TCBInfo = (struct t *)&(cvkCmd.SegV);

                cvkCmd.Cmd  = CVK_RET_SUC;
                cvkCmd.OffV = 0;
                cb          = CVK_RETSIZE_SCANTCB;

                rc = KDPQueryThread(&dummy,
                                    &scheduledSlot,
                                    &(TCBInfo->PID),
                                    &(TCBInfo->TID),
                                    &(TCBInfo->MTE));
                if (rc != NO_ERROR)
                {
                  cvkCmd.Cmd = CVK_RET_ERR;
                  cb = CVK_RETSIZE_RC;
                  break;
                }

                do
                {
                  rc = KDPQueryThread(&cvkCmd.Value,
                                      &(TCBInfo->slot),
                                      &(TCBInfo->PID),
                                      &(TCBInfo->TID),
                                      &(TCBInfo->MTE));
                  if (rc != NO_ERROR)
                  {
                    cvkCmd.Cmd = CVK_RET_ERR;
                    cb = CVK_RETSIZE_RC;
                    break;
                  }

                  TCBInfo->flags = 0;
                  if (TCBInfo->slot == scheduledSlot)
                    TCBInfo->flags = 1;

                  cvkCmd.OffV += 1;
                  TCBInfo     += 1;
                  cb          += sizeof(struct t);
                } while(cvkCmd.Value != 0 &&
                        cb <= sizeof(cvkCmd) - sizeof(struct t));
                break;
            }

            case CVK_CMD_SEL2LIN:
            {
                AddrS address;
                ULONG  dummy = -1;
                USHORT dum;
                struct RegSA_struc workRegs;

                rc = KDPQueryThread(&dummy,
                                    &scheduledSlot,
                                    &dum,
                                    &dum,
                                    &dum);

                if (rc != NO_ERROR)
                {
                  cvkCmd.Cmd = CVK_RET_ERR;
                  cb = CVK_RETSIZE_RC;
                  break;
                }

                /*
                * If caller is asking about thread that was running when we
                * got control, get information from RegSA structure...
                */
                if (cvkCmd.TID == scheduledSlot ||
                    cvkCmd.TID == 0)
                {
                  /* copy register info */
                  pbSrc  = (UCHAR *) &RegSA;
                  pbDest = (UCHAR *) &workRegs;
                  cb = sizeof(workRegs);
                  do
                  {
                      *(pbDest++) = *(pbSrc++);
                  } while (--cb);
                }
                /*
                * If caller is asking about thread that was not running when we
                * got control, get information from KDP call (global regs,
                * such as CR0, GDT, etc., come from RegSA structure...)
                */
                else if (KDPGetRegs(cvkCmd.TID,&workRegs) != 0)
                {
                  cvkCmd.Cmd  = CVK_RET_ERR;
                  cb          = CVK_RETSIZE_RC;
                  break;
                }

                /*
                * If we're in real or V86 mode, we can construct the linear
                * address corresponding to the segmented address directly.
                * Otherwise we call KDPSelToLin.
                */
                cvkCmd.Cmd  = CVK_RET_SUC;
                cb          = CVK_RETSIZE_SEL2LIN;
                if ((workRegs.EFsav  & 0x20000) != 0 || // V86 mode
                    (workRegs.CR0sav & 0x00001) == 0)   // Real mode
                  cvkCmd.Value = (((ULONG)cvkCmd.SegV) << 4) + cvkCmd.OffV;
                else
                {
                  address.AddrOff  = cvkCmd.OffV;
                  address.AddrSeg  = cvkCmd.SegV;
                  address.AddrType = EXPR_TYPE_SEL;
                  address.AddrSize = 1; // ?
                  address.AddrTask = cvkCmd.TID;

                  rc = KDPSelToLin(&address,&(cvkCmd.Value));
                  if (rc != NO_ERROR)
                  {
                    cvkCmd.Cmd = CVK_RET_ERR;
                    cb = CVK_RETSIZE_RC;
                  }
                }

                break;
            }

            case CVK_CMD_LIN2SEL:
            {
                AddrS address;
                selInfo_t info;
                ULONG  dummy = -1;
                USHORT dum;
                struct RegSA_struc workRegs;

                rc = KDPQueryThread(&dummy,
                                    &scheduledSlot,
                                    &dum,
                                    &dum,
                                    &dum);

                if (rc != NO_ERROR)
                {
                  cvkCmd.Cmd = CVK_RET_ERR;
                  cb = CVK_RETSIZE_RC;
                  break;
                }

                /*
                * If caller is asking about thread that was running when we
                * got control, get information from RegSA structure...
                */
                if (cvkCmd.TID == scheduledSlot ||
                    cvkCmd.TID == 0)
                {
                  /* copy register info */
                  pbSrc  = (UCHAR *) &RegSA;
                  pbDest = (UCHAR *) &workRegs;
                  cb = sizeof(workRegs);
                  do
                  {
                      *(pbDest++) = *(pbSrc++);
                  } while (--cb);
                }
                /*
                * If caller is asking about thread that was not running when we
                * got control, get information from KDP call (global regs,
                * such as CR0, GDT, etc., come from RegSA structure...)
                */
                else if (KDPGetRegs(cvkCmd.TID,&workRegs) != 0)
                {
                  cvkCmd.Cmd  = CVK_RET_ERR;
                  cb          = CVK_RETSIZE_RC;
                  break;
                }


                /*
                * If we're in real or V86 mode, we can construct the segmented
                * address corresponding to the linear address directly.
                * Otherwise we call KDPGetSelInfo for each segment register to
                * find the appropriate selector.
                */
                cvkCmd.Cmd = CVK_RET_SUC;
                cb = CVK_RETSIZE_LIN2SEL;
                if ((workRegs.EFsav  & 0x20000) != 0 || // V86 mode
                    (workRegs.CR0sav & 0x00001) == 0)   // Real mode
                {
                  if (cvkCmd.Value > 0x10ffef) // Linear address out of range
                  {
                    cvkCmd.Cmd = CVK_RET_ERR;
                    cb = CVK_RETSIZE_RC;
                    break;
                  }

                  cvkCmd.OffV = cvkCmd.Value & 0x0f;
                  dummy       = cvkCmd.Value >> 4;
                  while(dummy > 0xffff)
                  {
                    cvkCmd.OffV += 0x10;
                    dummy       -= 1;
                  }
                  cvkCmd.SegV = (USHORT)dummy;
                }
                else
                {
                  address.AddrOff  = 0;
                  address.AddrSeg  = workRegs.CSIPsav.AddrSeg;
                  address.AddrType = EXPR_TYPE_SEL;
                  address.AddrSize = 1; // ?
                  address.AddrTask = 0;

                  rc = KDPGetSelInfo(&address,&info);
                  if (rc != NO_ERROR)
                  {
                    cvkCmd.Cmd = CVK_RET_ERR;
                    cb = CVK_RETSIZE_RC;
                    break;
                  }

                  if (cvkCmd.Value >= info.base &&
                      cvkCmd.Value <= info.base + info.limit)
                  {
                    cvkCmd.OffV = cvkCmd.Value - info.base;
                    cvkCmd.SegV = address.AddrSeg;
                    break;
                  }

                  address.AddrSeg  = workRegs.DSsav;

                  rc = KDPGetSelInfo(&address,&info);
                  if (rc != NO_ERROR)
                  {
                    cvkCmd.Cmd = CVK_RET_ERR;
                    cb = CVK_RETSIZE_RC;
                    break;
                  }

                  if ((info.flags & 0x1c) == 0x14)  // expand down data segment
                  {
                    /*
                    * For expand down segments, the address must be >= base +
                    * limit and <= base + max.  If max is 4G, the second condi-
                    * tion is always true.
                    */
                    if (cvkCmd.Value >= info.base + info.limit &&
                        ((info.flags & 0x0400) != 0            || // Limit 4G
                        cvkCmd.Value <= info.base + 65535))
                    {
                      cvkCmd.OffV = cvkCmd.Value - info.base;
                      cvkCmd.SegV = address.AddrSeg;
                      break;
                    }
                  }
                  else // segment expands up
                    if (cvkCmd.Value >= info.base &&
                        cvkCmd.Value <= info.base + info.limit)
                    {
                      cvkCmd.OffV = cvkCmd.Value - info.base;
                      cvkCmd.SegV = address.AddrSeg;
                      break;
                    }

                  address.AddrSeg  = workRegs.ESsav;

                  rc = KDPGetSelInfo(&address,&info);
                  if (rc != NO_ERROR)
                  {
                    cvkCmd.Cmd = CVK_RET_ERR;
                    cb = CVK_RETSIZE_RC;
                    break;
                  }

                  if ((info.flags & 0x1c) == 0x14)  // expand down data segment
                  {
                    if (cvkCmd.Value >= info.base + info.limit &&
                        ((info.flags & 0x0400) != 0            || // Limit 4G
                        cvkCmd.Value <= info.base + 65535))
                    {
                      cvkCmd.OffV = cvkCmd.Value - info.base;
                      cvkCmd.SegV = address.AddrSeg;
                      break;
                    }
                  }
                  else // segment expands up
                    if (cvkCmd.Value >= info.base &&
                        cvkCmd.Value <= info.base + info.limit)
                    {
                      cvkCmd.OffV = cvkCmd.Value - info.base;
                      cvkCmd.SegV = address.AddrSeg;
                      break;
                    }

                  address.AddrSeg  = workRegs.SSsav;

                  rc = KDPGetSelInfo(&address,&info);
                  if (rc != NO_ERROR)
                  {
                    cvkCmd.Cmd = CVK_RET_ERR;
                    cb = CVK_RETSIZE_RC;
                    break;
                  }

                  if ((info.flags & 0x1c) == 0x14)  // expand down data segment
                  {
                    if (cvkCmd.Value >= info.base + info.limit &&
                        ((info.flags & 0x0400) != 0            || // Limit 4G
                        cvkCmd.Value <= info.base + 65535))
                    {
                      cvkCmd.OffV = cvkCmd.Value - info.base;
                      cvkCmd.SegV = address.AddrSeg;
                      break;
                    }
                  }
                  else // segment expands up
                    if (cvkCmd.Value >= info.base &&
                        cvkCmd.Value <= info.base + info.limit)
                    {
                      cvkCmd.OffV = cvkCmd.Value - info.base;
                      cvkCmd.SegV = address.AddrSeg;
                      break;
                    }

                  cvkCmd.SegV = 0;
                }
                break;
              }

            case CVK_CMD_OBJCOUNT:
            {
                hMTE = (USHORT)cvkCmd.Value;
                if (KDPObjCount(hMTE,&(cvkCmd.Value)) != 0)
                {
                  cvkCmd.Cmd = CVK_RET_ERR;
                  cb         = CVK_RETSIZE_RC;
                }
                else
                {
                  cvkCmd.Cmd = CVK_RET_SUC;
                  cb         = CVK_RETSIZE_OBJCOUNT;
                }
                break;
            }

            case CVK_CMD_SCANOBJ:
            {
                struct o {
                  ULONG base;
                  ULONG size;
                  ULONG flags;
                } *ObjInfo = (struct o *)&(cvkCmd.SegV);
                AddrS address;
                ULONG  count;
                USHORT hMTE = cvkCmd.MTE;
                USHORT type;

                address.AddrOff  = 0;
                address.AddrType = EXPR_TYPE_SEL;
                address.AddrSize = EXPR_SIZE_BYTE;
                address.AddrTask = 0;

                /*
                * Get number of segments in module - if this information is
                * not computable because something is paged out, return an
                * error.
                */
                if (KDPObjCount(hMTE,&count) != 0)
                {
                  cvkCmd.Cmd = CVK_RET_ERR;
                  cb         = CVK_RETSIZE_RC;
                  break;
                }

                cvkCmd.Cmd  = CVK_RET_SUC;
                cvkCmd.OffV = 0;
                cb          = CVK_RETSIZE_SCANOBJ;

                if (cvkCmd.Value == 0)
                  cvkCmd.Value = 1;
                do
                {
                  /*
                  * If we can't get information about a module, return an error.
                  */
                  if (KDPObjInfo2(hMTE,
                                  (USHORT)cvkCmd.Value,
                                  &type,
                                  &(ObjInfo->base),
                                  &(ObjInfo->flags),
                                  &(ObjInfo->size)) != 0)
                  {
                    cvkCmd.Cmd = CVK_RET_ERR;
                    cb         = CVK_RETSIZE_RC;
                    break;
                  }

                  cvkCmd.Value += 1; // Bump obj/seg index

                  /*
                  * If we have a 16-bit segment, we must get the base and
                  * size from its descriptor.
                  */
                  if ((type & 1) != 0)
                  {
                    selInfo_t info;

                    address.AddrSeg = (USHORT)ObjInfo->base;
                    KDPGetSelInfo(&address,&info);
                    ObjInfo->base = info.base;
                    ObjInfo->size = info.limit + 1;
                  }
                  /*
                  * If we have a 32-bit resource object, don't count it.
                  */
                  else if ((ObjInfo->flags & 8) != 0) // OBJRSRC == 8
                    continue;

                  /*
                  * Adjust flags
                  */
                  ObjInfo->flags = type;

                  cvkCmd.OffV  += 1; // Bump # ObjInfo records completed
                  ObjInfo      += 1; // Bump -> next ObjInfo record
                  cb           += sizeof(struct o);
                } while(cvkCmd.Value <= count &&
                        cb <= sizeof(cvkCmd) - sizeof(struct o));

                if (cvkCmd.Value > count)
                  cvkCmd.Value = 0;
                break;
            }

            case CVK_CMD_SELINFO:
            {
                selInfo_t info;
                AddrS address;

                address.AddrOff  = 0;
                address.AddrSeg  = cvkCmd.SegV;
                address.AddrType = EXPR_TYPE_SEL;
                address.AddrSize = EXPR_SIZE_BYTE;
                address.AddrTask = cvkCmd.TID;

                if (KDPGetSelInfo(&address,&info) != 0)
                {
                  cvkCmd.Cmd = CVK_RET_ERR;
                  cb         = CVK_RETSIZE_RC;
                  break;
                }

                cvkCmd.Cmd = CVK_RET_SUC;
                cb         = CVK_RETSIZE_SELINFO;

                cvkCmd.Value = info.limit;
                cvkCmd.OffV  = info.base;
                cvkCmd.DBit  = info.flags;
                break;
            }

            case CVK_CMD_RNPX:
            {
                if ((cvkCmd.Cmd = KDPGetNPX(cvkCmd.TID,(UCHAR *)&cvkCmd.Reg))
                        != 0)
                {
                  cb = CVK_RETSIZE_RC;
                  break;
                }

                cvkCmd.Cmd = CVK_RET_SUC;
                cb         = CVK_RETSIZE_RNPX;
                break;
            }

            case CVK_CMD_WNPX:
            {
                if ((cvkCmd.Cmd = KDPPutNPX(cvkCmd.TID,(UCHAR *)&cvkCmd.Reg))
                        != 0)
                {
                  cb = CVK_RETSIZE_RC;
                  break;
                }

                cvkCmd.Cmd = CVK_RET_SUC;
                cb         = CVK_RETSIZE_WNPX;
                break;
            }

            case CVK_CMD_ENA:
            {
                cvkCmd.Cmd = CVK_RET_SUC;
                cb         = CVK_RETSIZE_ENA;
                if (cvkCmd.Value == 0)
                  fKDPTaskNotifyEnable = TRUE;
                else
                {
                  cvkCmd.Cmd = CVK_RET_ERR;
                  cb         = CVK_RETSIZE_RC;
                }
                break;
            }

            case CVK_CMD_DIS:
            {
                cvkCmd.Cmd = CVK_RET_SUC;
                cb         = CVK_RETSIZE_DIS;
                if (cvkCmd.Value == 0)
                  fKDPTaskNotifyEnable = FALSE;
                else
                {
                  cvkCmd.Cmd = CVK_RET_ERR;
                  cb         = CVK_RETSIZE_RC;
                }
                break;
            }

            case CVK_CMD_PIREG:
            {
              /*
              * If table is empty, add registration at beginning.
              */
              if (KDPPageinCount == 0)
              {
                KDPPageinCount = 1;
                KDPPageinTable[0].hMTE = cvkCmd.MTE;
                KDPPageinTable[0].page = cvkCmd.OffV;
              }
              /*
              * If table is not empty, find where registration goes.
              */
              else
              {
                int high = KDPPageinCount - 1, low = 0, current;
                do
                {
                  current = (high + low) >> 1;
                  if (KDPPageinTable[current].page > cvkCmd.OffV)
                    high = current - 1;
                  else if (KDPPageinTable[current].page < cvkCmd.OffV)
                    low = current + 1;
                  else if (KDPPageinTable[current].hMTE > cvkCmd.MTE)
                    high = current - 1;
                  else if (KDPPageinTable[current].hMTE < cvkCmd.MTE)
                    low = current + 1;
                  else
                    break;
                } while(high >= low);

                /*
                * If page is already registered, do nothing.  Otherwise
                * current is the index of the entry that should contain
                * the information.
                */
                if (high < low)
                  /*
                  * If table is full, return an error.
                  */
                  if (KDPPageinCount == PAGEINMAX)
                  {
                    cvkCmd.Cmd = CVK_RET_ERR;
                    cb         = CVK_RETSIZE_RC;
                  }
                  else
                  {
                    for (high = KDPPageinCount; high > current; high--)
                      KDPPageinTable[high] = KDPPageinTable[high - 1];

                    KDPPageinCount              += 1;
                    KDPPageinTable[current].hMTE = cvkCmd.MTE;
                    KDPPageinTable[current].page = cvkCmd.OffV;
                  }
              }

              cvkCmd.Cmd = CVK_RET_SUC;
              cb         = CVK_RETSIZE_RC;
              break;
            }

            case CVK_CMD_PIDRG:
            {
              /*
              * If table is empty, do nothing.  Otherwise see if requested
              * notification is in table and delete it if so.
              */
              if (KDPPageinCount > 0)
              {
                int high = KDPPageinCount - 1, low = 0, current;
                do
                {
                  current = (high + low) >> 1;
                  if (KDPPageinTable[current].page > cvkCmd.OffV)
                    high = current - 1;
                  else if (KDPPageinTable[current].page < cvkCmd.OffV)
                    low = current + 1;
                  else if (KDPPageinTable[current].hMTE > cvkCmd.MTE)
                    high = current - 1;
                  else if (KDPPageinTable[current].hMTE < cvkCmd.MTE)
                    low = current + 1;
                  else
                    break;
                } while(high >= low);

                /*
                * If page is not there, do nothing.  Otherwise current is the
                * index of the entry to delete.
                */
                if (high >= low)
                {
                  for (high = current; high < (int)KDPPageinCount; high++)
                    KDPPageinTable[high] = KDPPageinTable[high + 1];

                  KDPPageinCount -= 1;
                }
              }

              cvkCmd.Cmd = CVK_RET_SUC;
              cb         = CVK_RETSIZE_RC;
              break;
            }

            default:
                cvkCmd.Cmd = CVK_BAD_COMMAND;
                cb = CVK_RETSIZE_RC;
        }

        // BUGBUG - cvkCmd is a struct, not a char ?
        cvkTSend( (UCHAR *)&cvkCmd, cb );
        fKDPReceived = FALSE;

    }
}
/* JWE end */



/***EP  KDPRawInput - Request a line of raw text
 *
 *      The front end already knows we're looking for input, so
 *      just flush the output buffer and return control to it.
 *
 *      ENTRY
 *              None
 *
 *      EXIT
 *              None
 *
 *      Pseudocode:
 *
 */

void KDPRawInput(void)
{
    KDPRawOutput(NULL);
    KDPEventNotify(KDP_T_SUCCESS);
    KDPMain();
}



/***EP  KDPCharInput - Request a raw character
 *
 *      The front end already knows we're looking for input, so
 *      just flush the output buffer, note that we want a character,
 *      and return control to it.
 *
 *      ENTRY
 *              None
 *
 *      EXIT
 *              None
 *
 *      Pseudocode:
 *
 */

USHORT KDPCharInput(void)
{
    KDPRawOutput(NULL);
    cvkCmd.Cmd = CVK_CMD_RAWCHAR;
    cvkTSend( (UCHAR *)&cvkCmd, CVK_CMDSIZE_RAWCHAR );
    fKDPReceived = FALSE;
    return( KDPMain() );
}
#endif

