;       SCCSID = @(#)dostask.asm        6.3 91/09/03
;      SCCSID = @(#)dostask.asm 6.3 91/09/03
PAGE    ,132
NAME    DOSTASK
TITLE   DOSTASK.ASM - External debugger task routines

;***    DOSTASK.ASM
;
;
;       The external debugger routines that implemenent
;       the ".p", ".r" and ".k" commands and the debugger
;       call outs that handle per task addressing.
;
;----------------------------------------------------------------------------
;****************************************************************************
;* Change log flagged with a @#nnnx                                    ;
;*   where the nnn is any unique number                                ;
;*   where the x is n for new code or a for added code                 ;
;*                  d for deleted code(commented out old code)         ;
;*                  c for changed code                                 ;
;*                                                                     ;
;*  @#001 WTE 05/31/91 Changes made for PTR PB723546,                  ;
;*                     SI reg not set to AddrS                         ;
;*                                                                     ;
;*  @#002 MTH 08/14/91 Changes made for PTR PB725079                   ;
;*  - added fixes so 'BT' command would work in 2.0                    ;@#002n
;*                                                                     ;
;*  @#003 AJR 05/18/94 Changes made for D81012                         ;
;*  - changes in the process address region caused the debugger to     ;
;*    leave int 3 op codes in memory.  Modified region checks.         ;
;*                                                                     ;
;*  72417 MLM        11/18/95                                          ;
;*          Moved find.inc after tkexport.inc as part of reducing      ;
;*          FSCRIT serialization.                                      ;
;* 126413 RJM 15 May 1996 ChildWait not decoded correctly              ;
;*---------------------------------------------------------------------
;*
;****************************************************************************

INCL_MI =       1
INCL_ERROR_H =  1
INCL_ERROR2_H = 1

.xlist
        include dosseg.inc
        include basemaca.inc
        include osmaca.inc
        include mi.inc
        include ostypes.inc
        include osconst.inc
        include dossymc.inc
        include vmowner.inc
        include sched.inc
        include sem.inc
        include screen.inc
        include proc.inc
        include sigdef.inc
        include except.inc
        include infoseg.inc
        include curdir.inc
        include dirent.inc
        include pgexport.inc
        include vmexport.inc
        include selxport.inc
        include tkexport.inc
        include find.inc                        ;72417 moved after tkexport.inc
        include tkptda.inc
        include sysinvar.inc
        include kmode.inc
        include mvdmexp.inc
        include pglocal.inc
        include debseg.inc
        include debmac.inc
        include debequ.inc
        include debinit.inc
        include debsym.inc
        include debprt.inc
        include debdesc.inc
        include bseerr.inc
        include ldrvars.inc
        include npx.inc        ; JWE

        CpuMode 386
.list
        ReferFlat       _mte_h,DWORD
        ReferFlat       _DosModMte,BYTE
        ReferFlat       _R0CS,WORD
        ReferFlat       _R2CS,WORD
        ReferFlat       _R3CS,WORD
        ReferFlat       _R0DS,WORD
        ReferFlat       _R2DS,WORD
        ReferFlat       _R3DS,WORD
        ReferFlat       _papTCBSlots,DWORD
        ReferFlat       _uskdbSlot,WORD
        ReferFlat       _ulkdbAddr,DWORD
        ReferFlat       _ulkdbAddrLDT,DWORD
        ReferFlat       _StartDFIRST,BYTE
        ReferFlat       _TKSSBase,DWORD
        ReferFlat       _pTCBCur,DWORD
        ReferFlat       _pPTDACur,DWORD
        ReferFlat       _cInDos,BYTE
        ReferFlat       InterruptLevel,DWORD
        ReferFlat       _pShSemTbl,DWORD
        ReferFlat       _ulShTblSize,DWORD
        ReferFlat       _TKSSBase,DWORD

        ReferGlobal     MaxThreads,WORD
        ReferGlobal     papTCBPtrs,WORD
        ReferGlobal     InDos,BYTE
        ReferGlobal     TaskNumber,WORD
        ReferGlobal     FirstPacket,BYTE
        ReferGlobal     LastPacket,BYTE
        ReferGlobal     SysSemDataTable,BYTE
        ReferGlobal     NPX_Owner,WORD        ; JWE

        ReferGDT        GDT_PTDA,BYTE
        ReferGDT        GDT_LDT,BYTE
        ReferGDT        GDT_Buffers,BYTE
        ReferGDT        GDT_SFT,BYTE

        EXTRN           Pid:FAR ; RJM 126413

        EXTRN32         _tkKDBDaemon
        EXTRN32         _TKWakeup
        EXTRN32         _pgIsInCore
        EXTRN32         pgSwitchRet
        EXTRN32         _TKMakePresentKDB

        EXTRNF32        KMExitKmodeEvents

GSEL                    equ     offset DOSGDTDATA:
LINES_PER_PAGE          equ     23
PS_NAME_LENGTH          equ     9               ; NO ROOM FOR EXTENSION!

; GetProcStat Flags

GET_PROC_NORM           equ   00001b
GET_PROC_BLK            equ   00010b
GET_PROC_CHILD          equ   00100b
GET_PROC_USER           equ   01000b
GET_PROC_QUEUE          equ   10000b

; ProcStat      struc flags

psflags record  PF_3XBOX:1,     \
                PF_THREAD1:1,   \
                PF_CURRENT:1,   \
                PF_HASCHILD:1,  \
                PF_CHILD:1,     \
                PF_DEBCUR:1,    \
                PF_BLKINFO:1,   \
                PF_USER:1,      \
                PF_TSDSWAPPED:1,\
                PF_NOARGS:1,    \
                PF_QUEUEINFO:1

; .PQ command   struc flags
;               Bit clear if field points to self.  (Print blanks if set)

pqflags record  PRIQ_NEXTQ:1,     \
                PRIQ_PREVQ:1,     \
                PRIQ_HIGHER:1,    \
                PRIQ_LOWER:1,     \
                PRIQ_NEXT:1,      \
                PRIQ_PREV:1,

ProcStat        struc
        PS_BlockID              dd      0
        PS_SemInfo              dd      0
        PS_SemDebugAddr         dd      0
        PS_UserESP              dd      0       ; user's ESP
        PS_UserEIP              dd      0       ; user's EIP
        PS_pFrameBase           dd      0       ; pointer to frame base
        PS_pcriFrameType        dd      0       ; pointer to frame cri
        PS_iSlot                dw      0       ; Slot index
        PS_PTDAhandle           dw      0
        PS_Pid                  dw      0       ; pid
        PS_Ppid                 dw      0       ; parent pid
        PS_Csid                 dw      0       ; command subtree pid
        PS_MTEhandle            dw      0
        PS_Ordinal              dw      0       ; 1 based thread number
        PS_TCBcount             dw      0       ; Number of active threads
        PS_NextSlot             dw      0
        PS_MSW                  dw      0       ; MSW from PTDA
        PS_DispBP               dw      0       ; saved kernel BP
        PS_DispSP               dw      0       ; saved kernel SP (TSD-relative)
        PS_UserSS               dw      0       ; user's SS
        PS_UserCS               dw      0       ; user's CS
        PS_TCBCurrTCB           dw      0       ; CurrTCB when TCB is active
        PS_TCBCurrTSD           dw      0       ; CurrTSD when TSD is active
        PS_ParmBytes            dw      0       ; # parm bytes on user stack
        PS_priority             dw      0       ; Thread's priority
        PS_Flags                dw      0
        PS_AddrPTDA             db      size AddrS dup(0)
        PS_AddrTCB              db      size AddrS dup(0)
        PS_AddrTSD              db      size AddrS dup(0)
        PS_Name                 db      PS_NAME_LENGTH dup(0)
        PS_ScreenGroup          db      0       ; screen group number
        PS_state                db      0       ; Thread's state
        PS_Qstate               db      0       ; Thread's Queue state
        PS_QFlags               dw      0       ; Q flags - See PQ_ defines
        PS_AddrPriNextQ         db      size AddrS dup(0)
        PS_AddrPriPrevQ         db      size AddrS dup(0)
        PS_AddrPriHigher        db      size AddrS dup(0)
        PS_AddrPriLower         db      size AddrS dup(0)
        PS_AddrPriNext          db      size AddrS dup(0)
        PS_AddrPriPrev          db      size AddrS dup(0)
ProcStat        ends

DataSegmentStart

Extern  DebData, WORD
Extern  DefType, WORD
Extern  DisAddr, WORD
Extern  EnterAddr, WORD
Extern  DumpAddr, WORD
Extern  RegSA, WORD
Extern  pRegSA, WORD
Extern  DosLinAddr, BYTE
Extern  KDPpMTE,DWORD
Extern  KDPSlot,DWORD ; JWE
Extern  fKDPPacketMode, BYTE ; JWE
Extern  fKDPTaskNotifyEnable, BYTE ; JWE
Extern  TrapNumber,WORD
Extern  ErrorCode,WORD

Static  RegSave,        STRUC   RegSA_frame,    <>
        public          CurrTask
Static  CurrTask,       WORD,   0               ; current task number
Static  CurrOldCS,      WORD,   0               ; old CS value
Static  CurrOldEIP,     DWORD,  0               ; old EIP value
Static  CurrOldSS,      WORD,   0               ; old SS value
Static  CurrOldESP,     DWORD,  0               ; old ESP value
Static  CurrOldEBP,     DWORD,  0               ; old EBP value
Static  SPchanged,      BYTE,   0               ; if !0, SP changed

Static  addrsSwapIn,    STRUC,  AddrS,          <>
Static  addrsTemp,      STRUC,  AddrS,          <>
Static  laddrSwapIn,    DWORD,  0
Static  laddrLDTSwapIn, DWORD,  0
Static  slotSwapIn,     DWORD,  0
Static  errSwapIn,      DWORD,  0
Static  TrapNumberSav,  WORD,   0
Static  ErrorCodeSav,   WORD,   0
Static  CurrTaskSav,    WORD,   0               ; current task number

;*      Process status print routines

Static ProcStatHeader1, STRING, <" Slot  Pid  Ppid Csid Ord  Sta Pri  pTSD     pPTDA    pTCB     Disp SG Name", CR, LF>
Static ProcStatHeader2, STRING, <" Slot  Sta BlockID  Name     Type        Addr    Symbol", CR, LF>
Static ProcStatHeader3, STRING, <" Slot  Pid  Ord  pPTDA    Name     pstkframe  CS:EIP        SS:ESP     cbargs", CR, LF>
Static ProcStatHeader4, STRING, <" Slot  QSt Pri  pTCB     PriNextQ PriPrevQ PriHigh  PriLow   PriNext  PriPrev  ", CR, LF>

StateMsgs       label   word

        .errnz  ($ - StateMsgs) - (STATE_VOID*2)
        dw      dataOFFSET VOIDmsg              ; 0
        .errnz  ($ - StateMsgs) - (STATE_READY*2)
        dw      dataOFFSET READYmsg             ; 1
        .errnz  ($ - StateMsgs) - (STATE_BLOCKED*2)
        dw      dataOFFSET BLOCKmsg             ; 2
        .errnz  ($ - StateMsgs) - (STATE_SUSPENDED*2)
        dw      dataOFFSET SUSPENDEDmsg         ; 3
        .errnz  ($ - StateMsgs) - (STATE_CRITSEC*2)
        dw      dataOFFSET CRITSECmsg           ; 4
        .errnz  ($ - StateMsgs) - (STATE_RUNNING*2)
        dw      dataOFFSET RUNNINGmsg           ; 5
        .errnz  ($ - StateMsgs) - (STATE_READYBOOST*2)
        dw      dataOFFSET RDYBOOSTmsg          ; 6
        .errnz  ($ - StateMsgs) - (STATE_TSD*2)
        dw      dataOFFSET TSDmsg               ; 7
        .errnz  ($ - StateMsgs) - (STATE_DELAYED*2)
        dw      dataOFFSET DELAYEDmsg           ; 8
        .errnz  ($ - StateMsgs) - (STATE_FROZEN*2)
        dw      dataOFFSET FROZENmsg            ; 9
        .errnz  ($ - StateMsgs) - (STATE_GETSTACK*2)
        dw      dataOFFSET GETSTACKmsg          ; 10
        .errnz  ($ - StateMsgs) - (STATE_BADSTACK*2)
        dw      dataOFFSET BADSTACKmsg          ; 11
        .errnz  ($ - StateMsgs) - (STATE_MAX*2) ; verify all states covered
        dw      dataOFFSET INVAL12msg           ; 12
        dw      dataOFFSET INVAL13msg           ; 13
        dw      dataOFFSET INVAL14msg           ; 14
        dw      dataOFFSET INVAL15msg           ; 15
        .errnz  ($ - StateMsgs) - (16*2)        ; verify all masks covered

Static  VOIDmsg,        STRING, "---"
Static  READYmsg,       STRING, "rdy"
Static  BLOCKmsg,       STRING, "blk"
Static  SUSPENDEDmsg,   STRING, "sus"
Static  CRITSECmsg,     STRING, "crt"
Static  RUNNINGmsg,     STRING, "run"
Static  RDYBOOSTmsg,    STRING, "bst"
Static  TSDmsg,         STRING, "tsd"
Static  DELAYEDmsg,     STRING, "dly"
Static  FROZENmsg,      STRING, "frz"
Static  GETSTACKmsg,    STRING, "gsk"
Static  BADSTACKmsg,    STRING, "bad"
Static  INVAL12msg,     STRING, "12?"
Static  INVAL13msg,     STRING, "13?"
Static  INVAL14msg,     STRING, "14?"
Static  INVAL15msg,     STRING, "15?"

Global VDMmsg,          STRING, "*vdm"
Static AGERthreadmsg,   STRING, "*ager"
Static TSDthreadmsg,    STRING, "*tsd"
Static CTXHthreadmsg,   STRING, "*ctxh"
Static KDBthreadmsg,    STRING, "*kdb"
Static LAZYWthreadmsg,  STRING, "*lazyw"           ;@#002n
Static ASYNRthreadmsg,  STRING, "*asyncr"
Global SYIthreadmsg,    STRING, "*sysinit"

ThreadMsgs      label   word
        dw      dataOFFSET VDMmsg               ; any VDM pid
        dw      dataOFFSET AGERthreadmsg        ; pid 1, thread 1
        dw      dataOFFSET TSDthreadmsg         ; pid 1, thread 2
        dw      dataOFFSET CTXHthreadmsg        ; pid 1, thread 3
        dw      dataOFFSET KDBthreadmsg         ; pid 1, thread 4
        dw      dataOFFSET LAZYWthreadmsg       ; pid 1, thread 5 @#002n
        dw      dataOFFSET ASYNRthreadmsg       ; pid 1, thread 6 @#002n
        dw      dataOFFSET SYIthreadmsg         ; pid 1, thread 7
MAXTHREADMSGS = (($ - ThreadMsgs)/2)

PrintProcStat   label   byte
        JumpIfBitSet    PS_Flags, PF_CHILD, pps00
        JumpIfBitSet    PS_Flags, PF_CURRENT, pps05
        OutBlank
        JumpOp          pps10

pps00   label           byte
        OutStr          "     "
        JumpOp          pps25

pps05   label           byte
        OutStr          "*"

pps10   label           byte
        OutWordHexPtr   PS_iSlot
        JumpIfBitSet    PS_Flags, PF_DEBCUR, pps15
        OutBlank
        JumpOp          pps20

pps15   label           byte
        OutStr          "#"

pps20   label           byte
        OutBlank
        JumpIfBitSet    PS_Flags, PF_BLKINFO, pps40
        JumpIfBitSet    PS_Flags, PF_QUEUEINFO, pps35

pps25   label           byte
        OutWordHexPtr   PS_Pid
        OutBlank
        JumpIfBitSet    PS_Flags, PF_USER, pps30

        OutWordHexPtr   PS_Ppid
        OutBlank

        OutWordHexPtr   PS_Csid
        OutBlank

pps30   label           byte
        OutWordHexPtr   PS_Ordinal
        OutBlanks        1
        JumpIfBitSet    PS_Flags, PF_USER, pps60
        JumpIfBitClr    PS_Flags, PF_CHILD, pps40
        OutStr          "child"
        OutBlanks       13
        JumpOp          pps50

pps35   label           byte
        OutMsgBitFlc    PS_Qstate, 0, 4, StateMsgs
        JumpOp          pps45

pps40   label           byte
        OutMsgBitFlc    PS_state, 0, 4, StateMsgs

pps45   label           byte
        OutBlank
        JumpIfBitSet    PS_Flags, PF_BLKINFO, pps55

        OutWordHexPtr   PS_priority
        OutBlank
        JumpIfBitSet    PS_Flags, PF_QUEUEINFO, pps65

pps50   label           byte
        OutDwordHexPtr  PS_AddrTSD.AddrOff
        OutBlank
        JumpOp          pps60

pps55   label           byte
        OutDwordHexPtr  PS_BlockID
        OutBlank
        JumpIfBitSet    PS_Flags, PF_BLKINFO, pps80

pps60   label           byte
        OutDwordHexPtr  PS_AddrPTDA.AddrOff
        OutBlank
        JumpIfBitSet    PS_Flags, PF_USER, pps80

pps65   label           byte
        OutDwordHexPtr  PS_AddrTCB.AddrOff
        OutBlank

        JumpIfBitSet    PS_Flags, PF_QUEUEINFO, pps85
        JumpIfBitSet    PS_Flags, PF_TSDSWAPPED, pps70
        OutWordHexPtr   PS_DispSP
        JumpOp          pps75

pps70   label byte
        OutBlanks       4

pps75   label byte
        OutBlank

        OutByteHexPtr   PS_ScreenGroup
        OutBlank

pps80   label           byte
        OutStrPtr       PS_Name, PS_NAME_LENGTH
        JumpIfBitClr    PS_Flags, PF_USER, pps90

        JumpIfBitF      PS_pFrameBase, 0, 32, 0, pps90
        OutBlank
        OutDwordHexPtr  PS_pFrameBase

        JumpIfBitSet    PS_Flags, PF_TSDSWAPPED, pps90

        OutBlank
        OutWordHexPtr   PS_UserCS
        OutStr          ":"
        OutDwordHexPtr  PS_UserEIP

        OutBlank
        OutWordHexPtr   PS_UserSS
        OutStr          ":"
        OutDwordHexPtr  PS_UserESP

        JumpIfBitSet    PS_Flags, PF_NOARGS, pps90
        OutBlank
        OutWordHexPtr   PS_ParmBytes
        JumpOp          pps90

pps85   label           byte
        JumpIfBitClr    PS_QFlags, PRIQ_NEXTQ, pps85a
        OutBlanks       9
        JumpOp          pps85b

pps85a  label           byte
        OutDwordHexPtr  PS_AddrPriNextQ.AddrOff
        OutBlank

pps85b  label           byte
        JumpIfBitClr    PS_QFlags, PRIQ_PREVQ, pps85c
        OutBlanks       9
        JumpOp          pps85d

pps85c  label           byte
        OutDwordHexPtr  PS_AddrPriPrevQ.AddrOff
        OutBlank

pps85d  label           byte
        JumpIfBitClr    PS_QFlags, PRIQ_HIGHER, pps85e
        OutBlanks       9
        JumpOp          pps85f

pps85e  label           byte
        OutDwordHexPtr  PS_AddrPriHigher.AddrOff
        OutBlank

pps85f  label           byte
        JumpIfBitClr    PS_QFlags, PRIQ_LOWER, pps85g
        OutBlanks       9
        JumpOp          pps85h

pps85g  label           byte
        OutDwordHexPtr  PS_AddrPriLower.AddrOff
        OutBlank

pps85h  label           byte
        JumpIfBitClr    PS_QFlags, PRIQ_NEXT, pps85i
        OutBlanks       9
        JumpOp          pps85j

pps85i  label           byte
        OutDwordHexPtr  PS_AddrPriNext.AddrOff
        OutBlank

pps85j  label           byte
        JumpIfBitClr    PS_QFlags, PRIQ_PREV, pps85k
        OutBlanks       9
        JumpOp          pps90

pps85k  label           byte
        OutDwordHexPtr  PS_AddrPriPrev.AddrOff
        OutBlank

pps90   label           byte
        EndOp


DataSegmentEnd

CodeSegmentStart

Extern  perr, NEAR
Extern  ErrBreak, NEAR
Extern  command, NEAR
Extern  ExitDebug, NEAR ; JWE
Extern  KDPEvent, NEAR  ; JWE

;JWE start
;***    KDPTaskStart and KDPTaskEnd field notifications from the tasking
;       component when thread 1 in a task starts or ends.  Each routine
;       enters the debugger and if packet mode is active forwards an event
;       to KDPEvent
;
DefProc KDPTaskNew, FAR, PUBLIC
        Pcall   EnterDebugRETF
        cmp     fKDPPacketMode,0
        je      short ktnx
        cmp     fKDPTaskNotifyEnable,0
        je      short ktnx
        mov     eax,RegSA.EBXsav ; Client EBX has thread handle
        mov     KDPSlot,eax
        mov     eax,KDP_TASK_CREATE_EVENT
        jmp     KDPEvent
ktnx:   jmp     ExitDebug
EndProc NoGen

DefProc KDPTaskEnd, FAR, PUBLIC
        Pcall   EnterDebugRETF
        cmp     fKDPPacketMode,0
        je      short ktex
        cmp     fKDPTaskNotifyEnable,0
        je      short ktex
        mov     eax,KDP_TASK_END_EVENT
        jmp     KDPEvent
ktex:   jmp     ExitDebug
EndProc NoGen
;JWE end

;***    MakePresentCmd
;
;       This function implements the .I command to swap in a page.
;
;       ENTRY   NONE
;
;       EXIT    NONE
;
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, Flags

        assumeDATA   <ds, es, ss>

MPR_TSD         equ     0001h
MPR_BPS         equ     0002h
MPR_DAEMON      equ     0004h

DefProc MakePresentCmd, NEAR, PUBLIC
        Pcall   PeekChr                 ; anything on command line?
        jz      perr                    ;  no, jump to error
        xor     si,si                   ; (SI) = start with no flags
mpr10:  mov     dx,MPR_BPS
        cmp     al,'B'                  ; set BPs when calling kernel code
        je      short mpr15
        mov     dx,MPR_TSD
        cmp     al,'T'                  ; swap in a TSD
        je      short mpr15
        mov     dx,MPR_DAEMON
        cmp     al,'D'                  ; use kdb daemon thread
        jne     short mpr20
mpr15:  or      si,dx
        Pcall   SkipB
        jmp     short mpr10

;       (SI) = command line options

mpr20:  Pcall   ScanP                   ; anything left on command line?
        jz      perr                    ;  no, jump to error
        mov     [laddrLDTSwapIn],-1     ; indicate no LDT entry address
        test    si,MPR_TSD              ; swapping in a TSD?
        jnz     short mpr25             ;  yes, get slot number

        mov     bx,dataOFFSET addrsSwapIn
        Pcall   GetDSaddr
        Pcall   EvalAddr, <bx, bx>      ; evaluate address
        Pcall   GetAddrTask
        movzx   eax,ax                  ; (EAX) = slot number
        mov     [slotSwapIn],eax

        Pcall   GetAddrType             ; (AL) = address type
        cmp     al,EXPR_TYPE_SEL        ; is it a sel:off address type?
        jne     short mpr22             ;  no, convert to linear
        test    [bx].AddrSeg,TABLE_MASK ; is it a LDT selector?
        jz      short mpr22             ;  no, convert to linear

        ; BUGBUG - the code below will not work for segments that are in the
        ;          packed region.  However, it is not trivial to
        ;          fix this, since we would have to look into the
        ;          (swappable) LDT to find the real base address.
        ;          The work around is to use pass a flat address to .i

        movzx   eax,[bx].AddrSeg
        shl     eax,SEL_LADDR_SHIFT
        and     eax,SEL_LADDR_MASK      ; (EAX) = selector's linear address
        add     eax,[bx].AddrOff        ; (EAX) = linear address
        mov     [laddrSwapIn],eax       ; set linear address of page

        mov     bx,dataOFFSET addrsTemp
        Pcall   FillSelAddr, <bx, [RegSA.LDTsav], [addrsSwapIn.AddrSeg], 0>
        mov     ax,[slotSwapIn].LowWord
        mov     [bx].AddrTask,ax
        Pcall   AddrToLin               ; (AX:DX) = ldt entry address
        jc      ErrBreak                ; jump if bad address
        mov     [laddrLDTSwapIn].LowWord,dx
        mov     [laddrLDTSwapIn].HighWord,ax
        jmp     short mpr30

mpr22:  Pcall   AddrToLin               ; (AX:DX) = linear address
        jc      ErrBreak                ; jump if bad address
        mov     [laddrSwapIn].LowWord,dx
        mov     [laddrSwapIn].HighWord,ax
        jmp     short mpr30

mpr25:  Pcall   EvalWord                ; (DX) = slot number
        movzx   edx,dx                  ; (EDX) = slot number
        mov     [slotSwapIn],edx        ; set slot number of TSD to swapin
        mov     [laddrSwapIn],-1        ; indicates TSD swapin

mpr30:  test    si,MPR_DAEMON
        jz      short mpr35
        Pcall   MakePresentDaemon
        Jump    command

mpr35:  FlatContext ds
        cmp     [InterruptLevel],-1     ; are we at task time?
        je      short mpr40             ;  yes, check swap flag
        DataContext ds                  ;  no, print error and break
        putstr  <"Not allowed at interrupt time">
        Jump    CmdBreak

        ASSUME  DS:FLAT
mpr40:  mov     edi,[_pTCBCur]          ; (EDI) = current ptcb
        cmp     [edi].TCBfSwapping,0    ; are we swapping?
        je      short mpr45             ;  yes, finish command
        DataContext ds                  ;  no, print error and break
        putstr  <"Not allowed while swapping operation pending">
        Jump    CmdBreak

        ASSUME  DS:FLAT
mpr45:  test    [edi].TCBWakeFlags,TK_WF_SLEEPING
        jz      short mpr50
        DataContext ds                  ;  no, print error and break
        putstr  <"Not allowed while thread is blocked">
        Jump    CmdBreak

        ASSUME  DS:FLAT
mpr50:  cmp     [_cInDos],0             ; is InDos set?
        jne     short mpr60             ;  yes, continue
        DataContext ds
        test    [RegSA.EFsav],F_VM      ; is V86 mode
        jnz     short mpr60             ;  yes, continue
        test    [RegSA.CSsav],RPL_MASK  ; is CS ring 0?
        jnz     short mpr60             ;  no, continue
        putstr  <"Not allowed at ring 0/InDos == 0">
        Jump    CmdBreak

;       (SI) = command line flags

mpr60:  DataContext ds
        test    si,MPR_BPS              ; set the breakpoints on exit?
        jz      short mpr65             ;  no, just exit the debugger
        Pcall   PutBPoints, <TRUE>      ;  yes, set all breakpoints

;       Save some global variables that will be trashed when we
;       exit and reenter the debugger.

mpr65:  mov     ax,[TrapNumber]
        mov     [TrapNumberSav],ax
        mov     ax,[ErrorCode]
        mov     [ErrorCodeSav],ax
        mov     ax,[CurrTask]
        mov     [CurrTaskSav],ax

;       Exit debugger mode.  After this call we are on the user's
;       stack and the user's registers have been restored.

        Pcall   ExitDebugIRETD
        PUSHD   0                       ; fake error code
        PUSHD   EXCSSI                  ; fake trap number
        movzx   esp,sp
        test    [esp].tf_eflags,F_VM
        jz      mpr70                   ; jump if protected mode
        KMEnterKmodeVDM v86
        Pcall   MakePresentKDB
        jmp     mpr80

mpr70:  test    [esp].tf_cs,RPL_MASK
        jnz     mpr75                   ; jump if not ring 0
        KMEnterKernelTrap
        Pcall   MakePresentKDB
        KMExitKernelTrap noreturn
        jmp     mpr90

mpr75:  cmp     ss:[TKOPTDA.ptda_ProcType],PT_VDM
        jne     mpr85                   ; jump if not protected mode VDM
        KMEnterKmodeVDM vpm
        Pcall   MakePresentKDB
mpr80:  KMExitKmodeVDM v86orvpm,noreturn
        jmp     mpr90

mpr85:  KMEnterKmodeTrap
        Pcall   MakePresentKDB
        KMExitKmodeTrap noreturn
mpr90:  Pcall   EnterDebugIRETD         ; reenter the debugger
    assumeDATA  <ds, es, ss>
        mov     ax,[TrapNumberSav]      ; restore the global variables
        mov     [TrapNumber],ax
        mov     ax,[ErrorCodeSav]
        mov     [ErrorCode],ax
        mov     ax,[CurrTaskSav]
        mov     [CurrTask],ax
        mov     ecx,[errSwapIn]
        or      ecx,ecx
        jz      command                 ; if no errors, jump to command prompt

        cmp     cx,ERROR_BUSY
        je      short mpr100
        mov     ax,DEBERR_INVALID_TASK_NUMBER
        cmp     cx,ERROR_INVALID_THREADID
        je      short mpr95
        mov     ax,DEBERR_INVALID_ADDRESS
        cmp     cx,ERROR_INVALID_ADDRESS
        je      short mpr95
        printf  <"OS/2 error code: %dt", CR, LF>, <cx>
        mov     ax,DEBERR_INVALID_ADDRESS
mpr95:  mov     bx,dataOFFSET addrsSwapIn
        Jump    ErrBreak

mpr100: putstr  <"Not allowed when page semaphore is owned">
        Jump    CmdBreak
EndProc NoGen

;***    MakePresentDaemon
;
;       This function implements the .ID command to swap in a page or TSD.
;
;       ENTRY   (DX) = slot number
;               (ECX) = linear address
;
;       EXIT    NONE
;
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, Flags

        assumeDATA   <ds, es, ss>

DefProc MakePresentDaemon, NEAR, LOCAL
        mov     dx,[slotSwapIn].LowWord ; (DX) = slot number
        mov     ecx,[laddrSwapIn]       ; (ECX) = linear address
        mov     ebx,[laddrLDTSwapIn]    ; (EBX) = LDT entry's linear address
        FlatContext <ds,es>,ax
        cmp     [_uskdbSlot],0          ; already busy?
        stc                             ;  (assume yes)
        jnz     short mpd10             ;  yes, error
        mov     [_uskdbSlot],dx         ; save slot for daemon
        mov     [_ulkdbAddr],ecx        ; save address for daemon
        mov     [_ulkdbAddrLDT],ebx     ; save LDT entry address for daemon

;       Wake up the daemon:
;       _TKWakeup(sleepid, wakeuptype=0 (single), pcount=NULL)

        push    [_TKSSBase]
        mov     eax,offset FLAT:_StartDFIRST
        mov     [_TKSSBase],eax
        xor     eax,eax                 ; (EAX) = 0
        movzx   esp,sp
        CCALL32 _TKWakeup <<offset FLAT:_tkKDBDaemon>,eax,eax>
        pop     [_TKSSBase]
        clc
mpd10:  DataContext <ds, es>
        jc      short mpd40             ; skip ahead if error
        mov     ax,[slotSwapIn].LowWord ; (AX) = slot number
        mov     ecx,[laddrSwapIn]       ; (ECX) = linear address
        cmp     ecx,-1
        je      short mpd20
        printf  <"task|addr %04x|%08lx">, <ax, ecx>
        mov     ecx,[laddrLDTSwapIn]    ; (ECX) = LDT entry linear address
        cmp     ecx,-1
        je      short mpd30
        printf  <", LDT entry address %08lx">, <ecx>
        jmp     short mpd30

mpd20:  printf  <"TSD for slot %x">, <ax>
mpd30:  putstr  <" queued, g to continue", CR, LF>
        jmp     short mpdx

mpd40:  printf  <"Daemon busy", CR, LF>
mpdx:
EndProc

;***    MakePresentKDB
;
;       This function calls the kernel routine to make a page present.
;
;       ENTRY   NONE
;
;       EXIT    NONE
;
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, Flags

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

DefProc MakePresentKDB, NEAR, LOCAL
        CCALL32 _TKMakePresentKDB,<[slotSwapIn],[laddrSwapIn],[laddrLDTSwapIn]>
        Pcall   GetDebuggerDS
     assumeDATA ds
        mov     [errSwapIn],eax
        FlatContext ds
EndProc

;***    DumpUserStack
;
;       This is the ".K[S|B] [slot | * | #]" command.  It determines
;       which slot to operate on, calls GetProc*() routines to get info
;       for that slot, and then passes the user's SS:BP and CS:IP values
;       for that slot to PrintStack().
;
;       ENTRY   NONE
;
;       EXIT    NONE
;
;       USES    AX, BX, CX, DX, SI, DI, Flags
;
        assumeDATA   <ds, es, ss>

DefProc DumpUserStack, NEAR, PUBLIC

LocVar  InUserSpace,BYTE
LocVar  Slot,WORD
LocVar  PS_Buffer,<size ProcStat>
LocVar  SSBPaddr,<size AddrS>
LocVar  CSIPaddr,<size AddrS>
LocVar  SizeFlag,WORD

BeginProc
        mov     [SizeFlag],0            ; 16 bit stack frame (0)
        Pcall   PeekChr
        jz      short dus20
        cmp     al,'S'
        je      short dus10
        cmp     al,'B'
        jne     short dus20
        inc     [SizeFlag]              ; 32 bit stack frame (1)
dus10:  Pcall   SkipB
        jmp     short dus30

dus20:  dec     [SizeFlag]              ; default size indication (-1)
dus30:  mov     dx,[CurrTask]           ; use CurrTask by default
        Pcall   ScanP                   ; get cmdlin parameters
        jz      short dus60
        cmp     al,'#'                  ; use debugger's current slot?
        je      short dus50             ; yes; that's the default
        cmp     al,'*'                  ; use currently scheduled slot?
        je      short dus40             ; yes; go get TaskNumber
        Pcall   EvalWord                ; (DX) = slot number
        jmp     short dus60

;       We come here to operate on the currently scheduled slot

dus40:  push    ds
        DosContext ds
        mov     dx,[TaskNumber]         ; (DX) = slot number
        pop     ds
    assumeDATA  ds
        Pcall   SkipB
        jmp     short dus60

;       We come here to use the default (debugger's, see .S) slot

dus50:  Pcall   SkipB

;       Now the slot number is in DX.  If it's the currently scheduled
;       slot, and we aren't InDos, then .K is just like K, so call it

dus60:  push    ds
        DosContext ds
        mov     [InUserSpace],0         ; assume not
        cmp     dx,[TaskNumber]         ; current scheduled slot?
        jne     short dus70             ; no, continue as normal
        cmp     [InDos],0               ; in dos?
        jne     short dus70             ; yes, continue as normal
        mov     [InUserSpace],1         ; no, make a note of it
dus70:  pop     ds
    assumeDATA  ds
        cmp     [InUserSpace],1         ; can we call K?
        jne     short dus75             ; nope, go get info
        mov     di,[SizeFlag]
        Pcall   DumpStack2              ; yes, call "K" command
        jmp     short dusx

;       Get user info for the selected slot

dus75:  mov     [Slot],dx               ; save slot number
        lea     bx,PS_Buffer
        Pcall   GetProcStat, <GET_PROC_USER, Slot, bx>

        lea     si,PS_Buffer
        mov     cx,KM_REG_EFLAG
        Pcall   GetRegister             ; (AX:DX) = user's eflags

        mov     di,EXPR_TYPE_SEL        ; (DI) = address type
        test    ax,(F_VM SHR 16)
        jz      short dus80
        mov     di,EXPR_TYPE_SEG        ; (DI) = address type

dus80:  mov     cx,KM_REG_EBP
        Pcall   GetRegister             ; (AX:DX) = user's EBP

        lea     bx,SSBPaddr             ; (BX) = AddrS pointer
        Pcall   FillAddr, <bx, [PS_Buffer.PS_UserSS], dx, ax, [Slot], di>

        lea     bx,CSIPaddr             ; (BX) = AddrS pointer
        Pcall   FillAddr, <bx, [PS_Buffer.PS_UserCS], \
                           [PS_Buffer.PS_UserEIP.LowWord], \
                            [PS_Buffer.PS_UserEIP.HighWord], [Slot], di>

        lea     si,CSIPaddr             ; (SI) = CS:EIP AddrS
        mov     di,[SizeFlag]
        cmp     di,-1
        jne     short dus90
        Ccall   getdbit, <si>
        mov     di,ax                   ; (DI) = 0 if d bit clear, !0 if set
dus90:  lea     bx,SSBPaddr             ; (BX) = SS:EBP AddrS
        Pcall   PrintStack
dusx:
EndProc


;***    ProcessStatus
;
;       This routine prints the current state, etc of all the
;       processes and threads in the system.  With the 'B' option it prints
;       block info, and with the 'U' option it prints user space info.
;
;       ENTER - none
;
;       EXIT -  none
;
;       USES -  AX, BX, CX, DX, SI, DI, Flags

        assumeDATA   <ds, es, ss>

DefProc ProcessStatus, NEAR, PUBLIC

LocVar  Lines,WORD              ; # lines printed
LocVar  Slot,WORD               ; which (or first) slot we're working on
LocVar  EndSlot,WORD            ; last slot we're working on
LocVar  Flag,WORD               ; indicates which type of info to get
LocVar  HeadAddr,WORD           ; address of header string
LocVar  ThreadCount,WORD        ; counts threads for a process
LocVar  PS_Buffer,<size ProcStat>       ; holds slot info

BeginProc

;       Set up defaults

        mov     [Flag],GET_PROC_NORM
        mov     [Slot],1                ; start at slot 1
        mov     [Lines],1               ; force the header to be printed
        mov     [HeadAddr],dataOFFSET ProcStatHeader1

        push    ds
        DosContext ds
        mov     ax,[MaxThreads]
        mov     [EndSlot],ax
        pop     ds
    assumeDATA  ds

;       Check cmdlin for parameters

        Pcall   PeekChr                 ; (AL) = option character
        jz      short pcs10             ; if none, use defaults
        cmp     al,'B'                  ; are they asking for block info?
        jne     short pcs03             ; if not, continue
        mov     [Flag],GET_PROC_BLK     ; if so, change some defaults
        mov     [HeadAddr],dataOFFSET ProcStatHeader2
        Pcall   SkipB
        jmp     short pcs05

pcs03:  cmp     al,'U'                  ; are they asking for user info?
        jne     short pcs04             ; if not, continue
        mov     [Flag],GET_PROC_USER    ; if so, change some defaults
        mov     [HeadAddr],dataOFFSET ProcStatHeader3
        Pcall   SkipB
        jmp     short pcs05

pcs04:  cmp     al,'Q'                  ; are they asking for Queue info?
        jne     short pcs05             ; if not, continue
        mov     [Flag],GET_PROC_QUEUE   ; if so, change some defaults
        mov     [HeadAddr],dataOFFSET ProcStatHeader4
        Pcall   SkipB

pcs05:  Pcall   ScanP                   ; is there a slot # specified?
        jz      short pcs10             ; no, proceed

;       Determine slot number

        Pcall   PeekChr
        cmp     al,'#'                  ; current debugger's slot?
        je      short pcs06             ; yes, use CurrTask
        cmp     al,'*'                  ; current scheduled slot?
        je      short pcs07             ; yes, use TaskNumber
        Pcall   EvalWord                ; (DX) = slot number
        jmp     short pcs09

pcs06:  mov     dx,[CurrTask]           ; use debugger's default slot
        Pcall   SkipB                   ; (DX) = slot number
        jmp     short pcs09

pcs07:  push    ds                      ; use currently scheduled slot
        DosContext ds
        mov     dx,[TaskNumber]         ; (DX) = slot number
        pop     ds
    assumeDATA  ds
        Pcall   SkipB

;       User did specify a slot; print info for it alone

pcs09:  mov     [Slot],dx
        mov     [EndSlot],dx
        mov     [ThreadCount],1         ; don't loop
        mov     ax,dx                   ; (AX) = slot number
        lea     bx,PS_Buffer
        jmp     short pcs25

;       Start of per-process loop

pcs10:  lea     bx,PS_Buffer
        Pcall   GetProcStat, <Flag, Slot, bx>
        jc      pcs60                   ; invalid process or thread slot
        test    [PS_Buffer.PS_Flags],mask PF_THREAD1
        jz      pcs60
        mov     ax,[PS_Buffer.PS_TCBcount]
        mov     [ThreadCount],ax
        or      ax,ax                   ; Any threads?
        jnz     short pcs30             ;  yes, go do them
        printf  <"Slot with no threads: %x", 0Dh, 0Ah>,<Slot>
        jmp     short pcs60             ; Get next slot

;       Start of per-thread loop

pcs20:  lea     bx,PS_Buffer
        mov     ax,[bx].PS_NextSlot     ; (AX) = next slot in process
pcs25:  push    ax
        Pcall   GetProcStat, <Flag, ax, bx>
        pop     ax
        jc      short pcs70

pcs30:  lea     di,PS_Buffer
        test    Flag,GET_PROC_BLK       ; Doing blocked threads?
        jz      short pcs40             ;  no, go print status
        cmp     [di].PS_state,STATE_BLOCKED ; Is thread blocked?
        jne     short pcs50             ;  no, try next thread

pcs40:  dec     [Lines]
        jnz     short pcs45
        mov     dx,[HeadAddr]
        Pcall   rprbuf
        mov     [Lines],LINES_PER_PAGE

pcs45:  mov     si,dataOFFSET PrintProcStat
        Pcall   OpInterpret
        lea     bx,PS_Buffer
        Pcall   PrintBlockInfo, <Flag, 0, bx>
        Pcall   crlf
pcs50:  dec     [ThreadCount]           ; Are all the threads in proc done?
        jnz     short pcs20             ;  no, loop back

;       Print any child process after all the threads are printed.

pcs55:  test    [Flag],GET_PROC_BLK+GET_PROC_USER+GET_PROC_QUEUE
        jnz     short pcs60

        lea     bx,PS_Buffer
        test    [bx].PS_Flags,mask PF_HASCHILD
        jz      short pcs60             ; if no child, don't call
        Pcall   GetProcStat, <GET_PROC_CHILD, Slot, bx>
        jnc     short pcs30             ; jump back to print child

pcs60:  inc     [Slot]
        mov     ax,[Slot]
        cmp     ax,[EndSlot]
        jae     short pcsx
        jmp     pcs10                   ; jump back to per-proc loop start

pcs70:  mov     dx,ax
        jmp     InvalidTask

pcsx:

EndProc

;***    GetProcStat
;
;       Fills in the ProcStat structure for the indicated process
;       slot number.
;
;       Pcall   GetProcStat, <Flag, Slot, Addr>
;
;       ENTRY:  (Flag) = flags indicating the level of info returned
;                       GET_PROC_NORM - get normal proc status
;                       GET_PROC_BLK - get block info
;                       GET_PROC_CHILD - get child for the slot
;                       GET_PROC_USER - get user info
;               (Slot) = process slot number
;               (Addr) = ProcStat structure address to put info
;
;       EXIT:   If carry set, invalid slot number
;               If carry clear, got task info
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetProcStat, NEAR, LOCAL

ArgVar  Flag,WORD
ArgVar  Slot,WORD
ArgVar  Addr,WORD

BeginProc
        Pcall   GetProcGlobal, <Flag, Slot, Addr>
        jc      short gps0              ; jump if invalid slot number
        Pcall   GetProcPTDA, <Flag, Slot, Addr>
        jc      short gps0
        Pcall   GetProcTCB, <Flag, Slot, Addr>
        Pcall   GetProcMTE, <Flag, Slot, Addr>
        clc
gps0:

EndProc

;***    GetProcGlobal
;
;       Fills in the ProcStat structure for the indicated process
;       slot number with the global info.
;
;       Pcall   GetProcGlobal, <Slot, Addr>
;
;       ENTRY:  (Flag) = flags indicating the level of info returned
;                       GET_PROC_NORM - get normal proc status
;                       GET_PROC_BLK - get block info
;                       GET_PROC_CHILD - get child for the slot
;                       GET_PROC_USER - get user info
;                       GET_PROC_QUEUE - get scheduler queue info
;               (Slot) = process slot number
;               (Addr) = ProcStat structure address to put info
;
;       EXIT:   If carry set, invalid slot number
;               If carry clear, got task info
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetProcGlobal, NEAR, LOCAL

ArgVar  Flag,WORD
ArgVar  Slot,WORD
ArgVar  Addr,WORD
LocVar  pTCB,DWORD

BeginProc
        mov     bx,Addr                 ; (BX) = pointer to PS buffer
        mov     [bx].PS_Flags,0         ; initialize flags

        mov     di,Slot                 ; (DI) = Slot number
        mov     [bx].PS_iSlot,di

        push    es
        DosContext es                   ; (ES) = DOSGROUP
        cmp     di,[MaxThreads]
        jb      short gpg2              ; jump if task number not too high
        pop     es
        printf  <"Slot %x exceeds MaxThreads", 0Dh, 0Ah>,<di>
        stc
        jmp     gpgx                    ; exit with bad slot indication

gpg2:   mov     di,Slot
        add     di,di                   ; WORD
        add     di,di                   ; DWORD
        add     di,[papTCBPtrs]         ; (ES:DI) = ptr to papTCBPtrs[iSlot]
        mov     ax,es:[di].HighWord
        mov     [pTCB].HighWord,ax
        mov     ax,es:[di].LowWord
        mov     [pTCB].LowWord,ax       ; save TCB offset for later use.
        or      ax,[pTCB].HighWord      ; Is this a valid slot?
        pop     es
    assumeDATA  es
        stc                             ;  (assume no)
        jz      gpgx                    ;  no, exit
        push    es
        FlatContext es
        mov     eax,[pTCB]              ; (EAX) = TCB address
        mov     eax,es:[eax].TCBpPTDA   ; (EAX) = PTDA address
        mov     dx,es:[eax].ptda_handle ; (DI) = PTDA handle
        mov     [bx].PS_PTDAhandle,dx   ; save handle
        mov     eax,[pTCB]              ; (EAX) = TCB address

        test    Flag,GET_PROC_NORM+GET_PROC_BLK+GET_PROC_USER+GET_PROC_QUEUE
        jz      short gpg6

;       (BX) = ProcStat structure pointer
;       (pTCB) = TCB pointer

        mov     eax,es:[eax].TCBSleepID ; (EAX) = sleep ID
        mov     [bx].PS_BlockID,eax
        DosContext es

;       See if slot is the current thread

        mov     ax,Slot
        cmp     [TaskNumber],ax
        jne     short gpg5
        or      [bx].PS_Flags,mask PF_CURRENT

gpg5:   cmp     CurrTask,ax
        jne     short gpg6
        or      [bx].PS_Flags,mask PF_DEBCUR

gpg6:   pop     es
    assumeDATA  es
        test    Flag,GET_PROC_BLK
        jz      short gpg7
        or      [bx].PS_Flags,mask PF_BLKINFO

gpg7:   test    Flag,GET_PROC_USER
        jz      short gpg7a
        or      [bx].PS_Flags,mask PF_USER

gpg7a:  test    Flag,GET_PROC_QUEUE
        jz      short gpg8
        or      [bx].PS_Flags,mask PF_QUEUEINFO

gpg8:   lea     si,[bx].PS_AddrTCB
        Pcall   FillLinAddr, <si, [pTCB].LowWord, [pTCB].HighWord>
        add     bx,offset PS_AddrTSD    ; (BX) = PS_AddrTSD, (SI) = PS_AddrTCB
        xchg    bx,si                   ; (SI) = PS_AddrTSD, (BX) = PS_AddrTCB

;       Get TSD Address

        Pcall   GetDwordOff, <word ptr TCBpTSD, 0>
        jc      short gpgx
        Pcall   FillLinAddr, <si, dx, ax>
        clc
gpgx:
EndProc

;***    GetProcPTDA
;
;       Fills in the ProcStat structure for the indicated process
;       slot number with the info from the PTDA.
;
;       Pcall   GetProcPTDA, <Flag, Slot, Addr>
;
;       ENTRY:  (Flag) = flags indicating the level of info returned
;                       GET_PROC_NORM - get normal proc status
;                       GET_PROC_BLK - get block info
;                       GET_PROC_CHILD - get child for the slot
;                       GET_PROC_USER - get user info
;                       GET_PROC_QUEUE - get scheduler queue info
;               (Slot) = process slot number
;               (Addr) = ProcStat structure address to put info
;
;       EXIT:   if carry clear, no errors
;               if carry set, (AX) = error code
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;
        assumeDATA <es, ds, ss>

DefProc GetProcPTDA, NEAR, LOCAL

ArgVar  Flag,WORD
ArgVar  Slot,WORD
ArgVar  Addr,WORD

BeginProc
        test    Flag,GET_PROC_NORM+GET_PROC_BLK+GET_PROC_CHILD+GET_PROC_USER+GET_PROC_QUEUE
        jz      gpp7

        mov     si,Addr                 ; (SS:BX) = ProcStat struc pointer
        mov     dx,[si].PS_PTDAhandle
        lea     bx,[si].PS_AddrPTDA
        Pcall   GetPseudoObjectAddr
        jc      short gpp1

        test    Flag,GET_PROC_CHILD
        jz      short gpp3
        Pcall   GetWordOff, <ptda_child, 0>
        jc      short gpp1

        or      dx,dx
        jnz     short gpp2
        stc
gpp1:   jmp     gppx

gpp2:   or      [si].PS_Flags,mask PF_CHILD
        mov     [si].PS_PTDAhandle,dx
        Pcall   GetPseudoObjectAddr
        jc      short gpp1

        Pcall   GetDwordOff, <word ptr ptda_pTCBHead, 0>
        jc      short gpp1

        or      dx,dx
        jnz     short gpp2a

        or      ax,ax
        stc
        jz      short gpp1

gpp2a:  lea     cx,[si].PS_AddrTCB
        Pcall   FillLinAddr, <cx, dx, ax>

gpp3:   Pcall   GetWordOff, <word ptr ptda_Pid, 0>
        jc      short gpp1
        mov     [si].PS_Pid,dx

        Pcall   GetWordOff, <word ptr ptda_Ppid, 0>
        jc      gppx
        mov     [si].PS_Ppid,dx

        Pcall   GetWordOff, <word ptr Csid, 0>
        jc      gppx
        mov     [si].PS_Csid,dx

        Pcall   GetByteOff, <word ptr Cons_Loc.Scrn_Num, 0>
        jc      short gppx
        mov     [si].PS_ScreenGroup,al

        Pcall   GetWordOff, <word ptr TCBcount, 0>
        jc      short gppx
        mov     [si].PS_TCBcount,dx

        Pcall   GetWordOff, <word ptr MSW, 0>
        jc      short gppx
        mov     [si].PS_MSW,ax

        Pcall   GetWordOff, <word ptr ptda_child, 0>
        jc      short gppx
        or      dx,dx
        jz      short gpp4
        or      [si].PS_Flags,mask PF_HASCHILD

gpp4:   Pcall   GetDwordOff, <word ptr ptda_pTCBHead, 0>
        jc      short gppx
        cmp     [si].PS_AddrTCB.AddrOff.HighWord,ax
        jne     short gpp5              ; not first thread on list
        cmp     [si].PS_AddrTCB.AddrOff.LowWord,dx
        jne     short gpp5              ; not first thread on list
        or      [si].PS_Flags,mask PF_THREAD1   ; first thread on list

gpp5:   Pcall   GetWordOff, <word ptr ptda_ProcType, 0>
        jc      short gppx
        cmp     dx,PT_VDM
        jne     short gpp6              ; jump if not 3xBox
        mov     [si].PS_MTEhandle,0
        or      [si].PS_Flags,mask PF_3XBOX
        jmp     short gpp7

gpp6:   Pcall   GetWordOff, <word ptr ptda_module, 0>
        jc      short gppx
        mov     [si].PS_MTEhandle,dx
gpp7:   clc
gppx:
EndProc

;***    GetProcTCB
;
;       Fills in the ProcStat structure for the indicated process
;       slot number with the info from the TCB.
;
;       Pcall   GetProcTCB, <Flag, Slot, Addr>
;
;       ENTRY:  (Flag) = flags indicating the level of info returned
;                       GET_PROC_NORM - get normal proc status
;                       GET_PROC_BLK - get block info
;                       GET_PROC_CHILD - get child for the slot
;                       GET_USER_INFO - get user info
;                       GET_PROC_QUEUE - get scheduler queue info
;               (Slot) = process slot number
;               (Addr) = ProcStat structure address to put info
;
;       EXIT:   NONE
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetProcTCB, NEAR, LOCAL

ArgVar  Flag,    WORD
ArgVar  Slot,    WORD
ArgVar  Addr,    WORD
LocVar  TCBAddrNext, <size AddrS>

BeginProc
        mov     si,[Addr]               ; (SI) = ProcStat struc pointer

;       (SI) = ProcStat structure pointer

        lea     bx,[si].PS_AddrTCB
        Pcall   GetWordOff, <TCBOrdinal, 0>
        jc      gpte
        mov     [si].PS_Ordinal,dx

        Pcall   GetDwordOff, <word ptr TCBpTCBNext, 0>
        jc      gpte
        or      ax,ax
        jnz     short gpt10
        or      dx,dx
        jz      short gpt20

gpt10:  lea     bx,TCBAddrNext
        Pcall   FillLinAddr, <bx, dx, ax>
        Pcall   GetWordOff, <TCBNumber, 0>      ; Get next slot
        jc      gpte
gpt20:  mov     [si].PS_NextSlot,dx
        test    [Flag],GET_PROC_NORM+GET_PROC_BLK+GET_PROC_CHILD+GET_PROC_USER+GET_PROC_QUEUE
        jz      gptx

        lea     bx,[si].PS_AddrTCB
        Pcall   GetDwordOff, <word ptr TCB_SemInfo, 0>
        jc      gpte
        mov     [si].PS_SemInfo.HighWord,ax
        mov     [si].PS_SemInfo.LowWord,dx

        Pcall   GetDwordOff, <word ptr TCB_SemDebugAddr, 0>
        jc      gpte
        mov     [si].PS_SemDebugAddr.HighWord,ax
        mov     [si].PS_SemDebugAddr.LowWord,dx

        Pcall   GetWordOff, <word ptr TCBCurrTCB, 0>
        jc      gpte
        mov     [si].PS_TCBCurrTCB,dx

        Pcall   GetWordOff, <word ptr TCBCurrTSD, 0>
        jc      gpte
        mov     [si].PS_TCBCurrTSD,dx

        Pcall   GetByteOff, <word ptr TCBState, 0>
        jc      gpte
        mov     [si].PS_state,al

        Pcall   GetWordOff, <word ptr TCBPriority, 0>
        jc      gpte
        mov     [si].PS_priority,dx

        test    [Flag],GET_PROC_QUEUE
        jz      gpt25

        mov     [si].PS_QFlags,0

        Pcall   GetByteOff, <word ptr TCBQState, 0>
        jc      gpte
        mov     [si].PS_Qstate,al

        Pcall   GetDwordOff, <word ptr TCBpTCBPriNextQ, 0>
        jc      gpte
        lea     cx,[si].PS_AddrPriNextQ
        Pcall   FillLinAddr, <cx, dx, ax>
        mov     eax,[si].PS_AddrPriNextQ.AddrOff
        cmp     eax,[si].PS_AddrTCB.AddrOff
        jne     short gpt22a

        or      [si].PS_QFlags,mask PRIQ_NEXTQ

gpt22a: Pcall   GetDwordOff, <word ptr TCBpTCBPriPrevQ, 0>
        jc      gpte
        lea     cx,[si].PS_AddrPriPrevQ
        Pcall   FillLinAddr, <cx, dx, ax>
        mov     eax,[si].PS_AddrPriPrevQ.AddrOff
        cmp     eax,[si].PS_AddrTCB.AddrOff
        jne     short gpt22b

        or      [si].PS_QFlags,mask PRIQ_PREVQ

gpt22b: Pcall   GetDwordOff, <word ptr TCBpTCBPriHigher, 0>
        jc      gpte
        lea     cx,[si].PS_AddrPriHigher
        Pcall   FillLinAddr, <cx, dx, ax>
        mov     eax,[si].PS_AddrPriHigher.AddrOff
        cmp     eax,[si].PS_AddrTCB.AddrOff
        jne     short gpt22c

        or      [si].PS_QFlags,mask PRIQ_HIGHER

gpt22c: Pcall   GetDwordOff, <word ptr TCBpTCBPriLower, 0>
        jc      gpte
        lea     cx,[si].PS_AddrPriLower
        Pcall   FillLinAddr, <cx, dx, ax>
        mov     eax,[si].PS_AddrPriLower.AddrOff
        cmp     eax,[si].PS_AddrTCB.AddrOff
        jne     short gpt22d

        or      [si].PS_QFlags,mask PRIQ_LOWER

gpt22d: Pcall   GetDwordOff, <word ptr TCBpTCBPriNext, 0>
        jc      gpte
        lea     cx,[si].PS_AddrPriNext
        Pcall   FillLinAddr, <cx, dx, ax>
        mov     eax,[si].PS_AddrPriNext.AddrOff
        cmp     eax,[si].PS_AddrTCB.AddrOff
        jne     short gpt22e

        or      [si].PS_QFlags,mask PRIQ_NEXT

gpt22e: Pcall   GetDwordOff, <word ptr TCBpTCBPriPrev, 0>
        jc      gpte
        lea     cx,[si].PS_AddrPriPrev
        Pcall   FillLinAddr, <cx, dx, ax>
        mov     eax,[si].PS_AddrPriPrev.AddrOff
        cmp     eax,[si].PS_AddrTCB.AddrOff
        jne     short gpt25

        or      [si].PS_QFlags,mask PRIQ_PREV

gpt25:  or      [si].PS_Flags,mask PF_TSDSWAPPED ; Assume TSD swapped out
        mov     [si].PS_DispSP,0
        mov     [si].PS_DispBP,0

        lea     bx,[si].PS_AddrTSD
        Pcall   GetWordOff, <word ptr TSDKernelESP, 0>
        jc      short gpt30

        sub     dx,[si].PS_TCBCurrTSD   ; Make PS_DispSP relative to TSD base
        mov     [si].PS_DispSP,dx
        Pcall   GetWordOff, <word ptr TSDKernelEBP, 0>                  ;159839
        jc      short gpt30
        mov     [si].PS_DispBP,dx
        and     [si].PS_Flags,not (mask PF_TSDSWAPPED) ; TSD is present

gpt30:  test    [Flag],GET_PROC_USER
        jz      gptx

;       If not InDos and current slot, get CS:IP and SS:SP from RegSA.
;       set args, argptr & frame to 0

        push    ds
        DosContext ds
        mov     ax,[Slot]
        cmp     ax,[TaskNumber]         ; is this the current slot?
        jne     short gpt40             ; nope, continue as usual
        cmp     [InDos],0               ; yup; are we in dos?
        jne     short gpt40             ; nope, continue as usual
        pop     ds                      ; yup; get info from RegSA
    assumeDATA  ds

        mov     [si].PS_ParmBytes,0     ; these aren't applicable if
        mov     [si].PS_pFrameBase,0
        mov     [si].PS_pcriFrameType,0
        mov     edx,[RegSA.ESPsav]      ; get reggies from RegSA
        mov     [si].PS_UserESP,edx
        mov     dx,[RegSA.SSsav]
        mov     [si].PS_UserSS,dx
        mov     edx,[RegSA.CSIPsav.AddrOff]
        mov     [si].PS_UserEIP,edx
        mov     dx,[RegSA.CSIPsav.AddrSeg]
        mov     [si].PS_UserCS,dx
        jmp     short gptx

;       We must get reggies and info from TCB.

gpt40:  pop     ds
    assumeDATA  ds
        lea     bx,[si].PS_AddrTCB
        Pcall   GetDwordOff, <word ptr TCB_pFrameBase, 0>
        jc      short gpte
        mov     [si].PS_pFrameBase.LowWord,dx
        mov     [si].PS_pFrameBase.HighWord,ax

        Pcall   GetDwordOff, <word ptr TCB_pcriFrameType, 0>
        jc      short gpte
        mov     [si].PS_pcriFrameType.LowWord,dx
        mov     [si].PS_pcriFrameType.HighWord,ax

        mov     cx,KM_REG_ESP
        Pcall   GetRegister             ; (AX:DX) = user's ESP
        mov     [si].PS_UserESP.LowWord,dx
        mov     [si].PS_UserESP.HighWord,ax

        mov     cx,KM_REG_SS
        Pcall   GetRegister             ; (DX) = user's SS
        mov     [si].PS_UserSS,dx

        mov     cx,KM_REG_CBARGS
        Pcall   GetRegister             ; (DX) = number of args
        mov     [si].PS_ParmBytes,dx
        jnc     short gpt80
        or      [si].PS_Flags,mask PF_NOARGS

gpt80:  mov     cx,KM_REG_EIP
        Pcall   GetRegister             ; (AX:DX) = user's EIP
        mov     [si].PS_UserEIP.LowWord,dx
        mov     [si].PS_UserEIP.HighWord,ax

        mov     cx,KM_REG_CS
        Pcall   GetRegister             ; (DX) = user's CS
        mov     [si].PS_UserCS,dx
gptx:
EndProc
gpte:   Jump    ErrBreak

;***    GetProcMTE
;
;       Fills in the ProcStat structure for the indicated process
;       slot number with the info from the MTE for the task.
;
;       Pcall   GetProcMTE, <Flag, Slot, Addr>
;
;       ENTRY:  (Flag) = flags indicating the level of info returned
;                       GET_PROC_NORM - get normal proc status
;                       GET_PROC_BLK - get block info
;                       GET_PROC_CHILD - get child for the slot
;                       GET_PROC_USER - get user info
;               (Slot) = process slot number
;               (Addr) = ProcStat structure address to put info
;
;       EXIT:   NONE
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetProcMTE,NEAR,LOCAL

ArgVar  Flag,    WORD
ArgVar  Slot,    WORD
ArgVar  Addr,    WORD

BeginProc
        mov     si,Addr                 ; (SI) = ProcStat struc pointer
        lea     di,[si].PS_Name         ; (DI) = place to put name string
        mov     cx,PS_NAME_LENGTH-1
        mov     al,' '
        rep stosb                       ; fill in buffer with blanks and null
        xor     al,al
        stosb

        test    Flag,GET_PROC_NORM+GET_PROC_BLK+GET_PROC_USER
        jz      short gpmx

        mov     dx,[si].PS_MTEhandle    ; (DX) = MTE handle
        or      dx,dx
        jnz     short gpm40

        xor     di,di
        test    [si].PS_Flags,mask PF_3XBOX
        jnz     short gpm20

        cmp     [si].PS_Pid,1           ; translate thread ordinals for pid 1
        jnz     short gpmx
        mov     di,[si].PS_Ordinal
        cmp     di,MAXTHREADMSGS
        jae     short gpmx
gpm20:  shl     di,1
        mov     di,[ThreadMsgs+di]
        lea     si,[si].PS_Name         ; (si) = place to put name string
        xchg    si,di
        mov     cx,PS_NAME_LENGTH-1
gpm30:  lodsb
        or      al,al
        jz      short gpmx
        stosb
        loop    gpm30
        jmp     short gpmx

gpm40:  mov     al,' '                  ; (AL) = fill character
        mov     bx,7                    ; (BX) = flag: last component, no ext
        mov     cx,PS_NAME_LENGTH       ; (CX) = buffer length
        lea     di,[si].PS_Name         ; (DI) = place to put name string
        Pcall   GetMTEName
        jc      short gpme
gpmx:
EndProc
gpme:   Jump    ErrBreak

;***    PrintBlockInfo
;
;       Print info about why the process is blocking.
;
;       ENTRY:  (Flag) = get proc flags
;               (Slot) = task slot number
;               (Addr) = address of ProcStat struc.
;
;       EXIT:   NONE
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;
        assumeDATA <ds, es, ss>

DefProc PrintBlockInfo, NEAR, LOCAL

ArgVar  Flag,      WORD
ArgVar  Slot,      WORD
ArgVar  Addr,      WORD
LocVar  TempAddrS, <size AddrS>

BeginProc
        test    Flag,GET_PROC_BLK
        jz      gbix

        Pcall   blank
        mov     bx,Addr
        cmp     [bx].PS_state,STATE_BLOCKED
        jne     gbix
        mov     ax,[bx].PS_BlockID.HighWord
        mov     dx,[bx].PS_BlockID.LowWord      ; (AX:DX) = blockID

        cmp     ax,RAMSEM_BLOCK_ID
        jne     short gbi10
        putstr  "RamSem    "
        mov     dx,[bx].PS_SemInfo.HighWord
        Pcall   out16
        mov     al,":"
        Pcall   putchar
        mov     dx,[bx].PS_SemInfo.LowWord
        Pcall   out16
        Pcall   blank
        mov     ax,[bx].PS_SemInfo.HighWord
        mov     dx,[bx].PS_SemInfo.LowWord
        jmp     gbi80

gbi10:  cmp     ax,MUX_BLOCK_ID
        jne     short gbi20
        putstr  "MuxWait   "
        jmp     gbix

gbi20:  cmp     ax,DOSGROUP
        jne     short gbi45
        cmp     dx,offset DOSGROUP:FirstPacket
        jb      short gbi30
        cmp     dx,offset DOSGROUP:LastPacket
        ja      short gbi30
        putstr  "ReqPkt    "
        jmp     gbix

gbi30:  cmp     dx,offset DOSGROUP:SysSemDataTable
        jb      short gbi40
        cmp     dx,offset DOSGROUP:SysSemDataTable + (SYSSEM_MAXIMUM * size SysSemTblStruc)
        ja      short gbi40
        putstr  "SysSem    "
        jmp     gbix

gbi40:  putstr  "DosSem    "
        mov     dx,[bx].PS_BlockID.HighWord
        Pcall   out16
        mov     al,":"
        Pcall   putchar
        mov     dx,[bx].PS_BlockID.LowWord
        Pcall   out16
        Pcall   blank
        mov     ax,[bx].PS_BlockID.HighWord
        mov     dx,[bx].PS_BlockID.LowWord
        jmp     gbi80

gbi45:  mov     cx,[bx].PS_SemInfo.HighWord
        cmp     cx,08001H                       ; 32-bit Shared handle???
        je      short gbi46
        cmp     cx,0001H                        ; 32-bit Private handle???
        jne     gbi50                           ; no, try next item...
        jmp     short gbi47
        .386p
gbi46:  mov     ecx,[bx].PS_SemInfo             ; Look in 32-bit semaphore
        FlatContext <es, ds>
        and     ecx,0000FFFFH                   ; Get the index
        cmp     ecx,_ulShTblSize                        ; Is index w/in range
        jge     short gbi49                     ; nope, don't check table
        shl     ecx,2                           ; * 4 = DWORD offset
        add     ecx,_pShSemTbl                  ; + base = address
        shl     eax,16
        mov     ax,dx                           ; eax = Block ID
        cmp     eax,DWORD PTR [ecx]
        jne     short gbi49
        jmp     short gbi48

gbi47:  mov     ecx,[bx].PS_SemInfo             ; ecx = Semaphore handle
        SaveReg <edx>
        mov     edx,[bx].PS_AddrPTDA.AddrOff    ; edx = PTDA
        FlatContext <es, ds>
        and     ecx,0000FFFFH                   ; Strip the signature
        cmp     ecx,[edx].ulPrTblSize           ; index w/in range?
        jge     short gbi49
        shl     ecx,2                           ; * 4 = DWORD offset
        add     ecx,[edx].pPrSemTbl             ; + base = address
        RestoreReg <edx>
        shl     eax,16
        mov     ax,dx                           ; eax = Block ID
        cmp     eax,DWORD PTR [ecx]
        jne     short gbi49

gbi48:  DataContext <es, ds>                    ; Valid 32-bit Sem
        CpuMode reset
        putstr  "Sem32     "
        mov     dx,[bx].PS_SemInfo.HighWord
        Pcall   out16
        Pcall   blank
        mov     dx,[bx].PS_SemInfo.LowWord
        Pcall   out16
        Pcall   blank
        jmp     short gbi80

gbi49:  DataContext <es, ds>                    ; restore state
        .386p
        shr     eax,16
        CpuMode reset

gbi50:  cmp     ax,GSEL GDT_Buffers
        jne     short gbi60
        putstr  "Buffer    "
        jmp     short gbix

gbi60:  cmp     ax,GSEL GDT_SFT                 ; check if blocked on SFT
        jne     short gbi70
        putstr  "SFT       "
        jmp     short gbix

;RJM 126413
;       cmp     ax,word ptr ptda_Pid            ; check if CWAITing on child
gbi70:  cmp     ax,OFFSET TASKAREA:Pid          ; 126413 check if CWAITing on child
        jne     short gbi75
        cmp     dx,[bx].PS_Pid
        jne     short gbi75
        putstr  "ChildWait "
        jmp     short gbix

gbi75:  putstr  "                    "
gbi80:                                                                 ; @#001c
        lea     si,TempAddrS                    ; (SI) = ptr to AddrS  ; @#001n
        cmp     [bx].PS_SemDebugAddr,-1                                ; @#001c
        je      short gbi85
        mov     ax,[bx].PS_SemDebugAddr.HighWord
        mov     dx,[bx].PS_SemDebugAddr.LowWord ; (AX:DX) = linear address
        jmp     short gbi90

gbi85:  push    ax
        push    dx
;       lea     si,TempAddrS                    ; (SI) = ptr to AddrS    @#001d
        Pcall   FillDefAddr, <si, ax, dx, 0>
        xor     cx,cx
        Pcall   PrintNearSym                    ; Print symbol (if any)
        pop     dx
        pop     ax
        jnc     short gbix                      ; Done if symbol printed

gbi90:  Pcall   FillLinAddr, <si, dx, ax>
        xor     cx,cx                           ; No '[...] around symbol
        Pcall   PrintNearSym                    ; Print symbol (if any)
gbix:
EndProc

;***    RegisterSlot
;
;       This routine implements the .R command, which just displays the
;       user's registers for a specified slot.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;
        assumeDATA <ds, es, ss>

DefProc RegisterSlot,NEAR,PUBLIC

LocVar  InUserSpace, BYTE
LocVar  Slot,        WORD
LocVar  PS_Buffer,   <size ProcStat>

BeginProc
        mov     dx,[CurrTask]           ; (DX) = slot number (the default)

;       Check cmdlin for which slot we should operate on

        Pcall   ScanP
        jz      short rs2               ; none specified, use default
        cmp     al,'#'                  ; asking for debugger's current?
        je      short rs1               ; yup, use CurrTask (the default)
        cmp     al,'*'                  ; asking for currently scheduled slot?
        je      short rs0               ; yup, use TaskNumber
        Pcall   EvalWord                ; (DX) = slot number
        or      dx,dx
        jnz     short rs2
        jmp     rs8

rs0:    push    ds                      ; use currently scheduled slot
        DosContext ds
        mov     dx,[TaskNumber]         ; (DX) = slot number
        pop     ds
    assumeDATA  ds
        Pcall   SkipB
        jmp     short rs2

rs1:    Pcall   SkipB

;       If this is the currently scheduled slot, and we're not InDos,
;       then all the reggies are already in RegSA.

rs2:    push    ds
        DosContext ds
        mov     [InUserSpace],0         ; assume we're not
        cmp     dx,[TaskNumber]         ; currently scheduled task?
        jne     short rs3               ; nope, continue as normal
        cmp     [InDos],0               ; are we in dos?
        jne     short rs3               ; yup, continue as normal
        mov     [InUserSpace],1         ; nope. Use RegSA

rs3:    pop     ds
    assumeDATA  ds
        cmp     [InUserSpace],1         ; can we use RegSA?
        jne     short rs4               ; nope, continue as normal
        mov     [pRegSA],dataOFFSET RegSA
        Pcall   DispReg
        jmp     rsx

;       We must get user's reggies.  Ask GetProcStat to get info.

rs4:    mov     [Slot],dx               ; save slot number
        lea     bx,PS_Buffer
        Pcall   GetProcStat, <GET_PROC_USER, dx, bx>
        jc      rs7

        mov     si,dataOFFSET RegSA
        mov     di,dataOFFSET RegSave
        mov     cx,size RegSA_frame
        rep movsb                       ; initialize RegSave

;       Get general registers

        lea     si,PS_Buffer            ; (SI) -> PS_Buffer
        mov     cx,KM_REG_EAX
        Pcall   GetRegister             ; (AX:DX) = client's EAX register
        mov     [RegSave.EAXsav.LowWord],dx
        mov     [RegSave.EAXsav.HighWord],ax

        mov     cx,KM_REG_EBX
        Pcall   GetRegister             ; (AX:DX) = client's EBX register
        mov     [RegSave.EBXsav.LowWord],dx
        mov     [RegSave.EBXsav.HighWord],ax

        mov     cx,KM_REG_ECX
        Pcall   GetRegister             ; (AX:DX) = client's ECX register
        mov     [RegSave.ECXsav.LowWord],dx
        mov     [RegSave.ECXsav.HighWord],ax

        mov     cx,KM_REG_EDX
        Pcall   GetRegister             ; (AX:DX) = client's EDX register
        mov     [RegSave.EDXsav.LowWord],dx
        mov     [RegSave.EDXsav.HighWord],ax

        mov     cx,KM_REG_EBP
        Pcall   GetRegister             ; (AX:DX) = client's EBP register
        mov     [RegSave.EBPsav.LowWord],dx
        mov     [RegSave.EBPsav.HighWord],ax

        mov     cx,KM_REG_ESI
        Pcall   GetRegister             ; (AX:DX) = client's ESI register
        mov     [RegSave.ESIsav.LowWord],dx
        mov     [RegSave.ESIsav.HighWord],ax

        mov     cx,KM_REG_EDI
        Pcall   GetRegister             ; (AX:DX) = client's EDI register
        mov     [RegSave.EDIsav.LowWord],dx
        mov     [RegSave.EDIsav.HighWord],ax

        mov     cx,KM_REG_DS
        Pcall   GetRegister             ; (DX) = client's DS register
        mov     [RegSave.DSsav],dx

        mov     cx,KM_REG_ES
        Pcall   GetRegister             ; (DX) = client's ES register
        mov     [RegSave.ESsav],dx

        mov     cx,KM_REG_FS
        Pcall   GetRegister             ; (DX) = client's FS register
        mov     [RegSave.FSsav],dx

        mov     cx,KM_REG_GS
        Pcall   GetRegister             ; (DX) = client's GS register
        mov     [RegSave.GSsav],dx

        mov     cx,KM_REG_EFLAG
        Pcall   GetRegister             ; (AX:DX) = client's eflags
        mov     [RegSave.EFsav.LowWord],dx
        mov     [RegSave.EFsav.HighWord],ax

;       CS:EIP, SS:EIP, MSW are all in PS_Buffer

        mov     ax,[si].PS_UserSS
        mov     [RegSave.SSsav],ax
        mov     eax,[si].PS_UserESP
        mov     [RegSave.ESPsav],eax
        mov     ax,[si].PS_MSW
        mov     [RegSave.MSWsav],ax

;       GDT and IDT are stored in DebData

        mov     ax,[DebData.GDTaddr.DTRBaseLow]
        mov     [RegSave.GDTsav.DTRBaseLow],ax
        mov     ax,[DebData.GDTaddr.DTRLimit]
        mov     [RegSave.GDTsav.DTRLimit],ax
        mov     ax,[DebData.GDTaddr.DTRBaseHighExt]
        mov     [RegSave.GDTsav.DTRBaseHighExt],ax

        lea     di,DebData.ProtIDTaddr          ; (DI) -> prot idt address
        mov     cx,EXPR_TYPE_SEL                ; (CX) = sel type
        test    [RegSave.EFsav],F_VM
        jz      short rs6
        lea     di,DebData.RealIDTaddr          ; (DI) -> real idt address
        mov     cx,EXPR_TYPE_SEG                ; (CX) = seg type

rs6:    mov     ax,[di].DTRBaseLow
        mov     [RegSave.IDTsav.DTRBaseLow],ax
        mov     ax,[di].DTRLimit
        mov     [RegSave.IDTsav.DTRLimit],ax
        mov     ax,[di].DTRBaseHighExt
        mov     [RegSave.IDTsav.DTRBaseHighExt],ax

;       Set up RegSave AddrS structure, (SI) = PS_Buffer, (CX) = addr type

        mov     bx,dataOFFSET RegSave.CSIPsav   ; (BX) = RegSave.CSIPsav ptr
        Pcall   FillAddr, <bx, [si].PS_UserCS, [si].PS_UserEIP.LowWord, \
                           [si].PS_UserEIP.HighWord, Slot, cx>

;       Set up default disassembly, enter and dump addresses.

        Pcall   CopyAddr, <bx, dataOFFSET DisAddr>
        Pcall   FillAddr, <dataOFFSET EnterAddr, RegSave.DSsav, 0, 0, Slot, cx>
        Pcall   FillAddr, <dataOFFSET DumpAddr, RegSave.DSsav, 0, 0, Slot, cx>

        mov     [pRegSA],dataOFFSET RegSave
        Pcall   DispReg
        jmp     short rsx

rs7:    mov     dx,[Slot]
rs8:    putstr  <"Invalid task number: ">
        Pcall   out16
        Jump    CmdBreak
rsx:
EndProc

;***    ChangeCurrTask
;
;       Change what the debugger thinks the current task context
;       is (.s[s] [<slot>] | *).
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   Flags
;
        assumeDATA <ds, es, ss>

DefProc ChangeCurrTask,NEAR,PUBLIC

LocVar  PS_Buffer,<size ProcStat>
LocVar  Sflag, BYTE

BeginProc
        mov     Sflag,0
        Pcall   PeekChr
        cmp     al,"S"
        jne     short cct1
        Pcall   SkipB
        mov     Sflag,1

cct1:   Pcall   ScanP
        jnz     short cct2
        putstr  <"Current task number: ">
        mov     dx,CurrTask
        Pcall   out16
        Pcall   crlf
        jmp     cctx

cct2:   cmp     al,"*"                  ; if "*", change to TaskNumber
        jne     short cct3
        Pcall   SkipB
        push    ds
        DosContext ds
        mov     dx,TaskNumber           ; (DX) = new slot number
        pop     ds
    assumeDATA  ds
        jmp     cct6

cct3:   Pcall   EvalWord                ; (DX) = slot number
        or      dx,dx
        jz      short InvalidTask

        push    dx
        lea     bx,PS_Buffer            ; use this routine to verify slot
        Pcall   GetProcStat, <GET_PROC_NORM, dx, bx>
        pop     dx
        jc      short InvalidTask       ; jump if invalid slot number

        push    ds
        DosContext ds
        cmp     dx,TaskNumber
        pop     ds
    assumeDATA  ds
        jne     short cct4              ; not switching back to the current
        Pcall   DebExitRoutine          ; restores SS and SP
        jmp     short cct6

InvalidTask:
        putstr  <"Invalid task number: ">
        Pcall   out16
        Jump    CmdBreak

cct4:   cmp     Sflag,0
        je      short cct6              ; if "S" option, change SS and SP

        mov     al,1
        xchg    SPchanged,al
        or      al,al
        jnz     short cct5

        mov     ax,RegSA.CSsav          ; save cs:eip
        mov     CurrOldCS,ax
        mov     eax,RegSA.EIPsav
        mov     CurrOldEIP,eax

        mov     ax,RegSA.SSsav          ; save ss:esp,ebp
        mov     CurrOldSS,ax
        mov     eax,RegSA.ESPsav
        mov     CurrOldESP,eax
        mov     eax,RegSA.EBPsav
        mov     CurrOldEBP,eax

cct5:   mov     RegSA.CSsav,seg FLAT:DOSHIGH32CODE      ; set cs:eip
        mov     RegSA.EIPsav,offset FLAT:pgSwitchRet

        mov     RegSA.SSsav,GSEL GDT_PTDA               ; set ss:esp,ebp
        mov     ax,PS_Buffer.PS_DispSP
        add     ax,PS_Buffer.PS_TCBCurrTSD              ; make sp ss-relative
        movzx   eax,ax
        mov     RegSA.ESPsav,eax
        movzx   eax,PS_Buffer.PS_DispBP
        mov     RegSA.EBPsav,eax

;       (DX) = is the verified slot number

cct6:   mov     CurrTask,dx

;       Change the default slot numbers

        mov     DisAddr.AddrTask,dx
        mov     EnterAddr.AddrTask,dx
        mov     DumpAddr.AddrTask,dx
cctx:
EndProc

;***    DebEntryRoutine
;
;       Called when the debugger is entered thru a breakpoint,
;       trap or single step, etc.  Called after the user registers
;       have been saved, but before the Get/Put user memory
;       routines have been initialized.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   Flags
;
        assumeDATA <ds, es, ss>

DefProc DebEntryRoutine,NEAR,PUBLIC
        push    ax
        push    ds
        DosContext ds
        mov     ax,TaskNumber           ; (AX) = current task number
        pop     ds
    assumeDATA  ds
        mov     CurrTask,ax
        pop     ax
EndProc

;***    DebExitRoutine
;
;       Called when the debugger exits back to user code.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   Flags
;
;       NOTE:   This routine is also called by ChangeCurrTask to
;               restore the SS and SP registers.  If this routine
;               starts to do more, then the register save code
;               should be broken out into another routine.
;
        assumeDATA <ds, es, ss>

DefProc DebExitRoutine,NEAR,PUBLIC
        push    ax
        xor     al,al
        xchg    SPchanged,al
        or      al,al
        jz      short der1

        mov     ax,CurrOldCS                    ; restore cs:eip
        mov     RegSA.CSsav,ax
        mov     eax,CurrOldEIP
        mov     RegSA.EIPsav,eax

        mov     ax,CurrOldSS                    ; restore ss:esp,ebp
        mov     RegSA.SSsav,ax
        mov     eax,CurrOldESP
        mov     RegSA.ESPsav,eax
        mov     eax,CurrOldEBP
        mov     RegSA.EBPsav,eax
der1:   pop     ax
EndProc

;***    DebGetCurrTask
;
;       Return the current task number to the debugger.
;
;       ENTRY:  NONE
;
;       EXIT:   (AX) = current task number
;
;       USES:   Flags
;
        assumeDATA <ds, es, ss>

DefProc DebGetCurrTask,NEAR,PUBLIC
        mov     ax,CurrTask
EndProc


;***    DebGetCurrHobPTDA
;
;       Return the current hobPTDA to the caller.
;       ENTRY:  NONE
;       EXIT:   if carry clear, no errors
;                       (DX) = current slot's hobPTDA
;               if carry set,
;                       (AX) = error code, 0 = descriptor not copied
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc DebGetCurrHobPTDA,NEAR,PUBLIC
        assumeDATA <ds, es, ss>
LocVar  PS_Buffer,<size ProcStat>
BeginProc
        lea     bx,PS_Buffer
        Pcall   GetProcStat, <GET_PROC_NORM, [CurrTask], bx>
        mov     dx,[PS_Buffer].PS_PTDAhandle
EndProc


;***    DebGetDescInfo
;
;       Get the descriptor for the address.  This allows for
;       global descriptors to be translated that are different
;       between tasks, like GDT_LDT, etc.
;
;       ENTRY:  (AX) = task number
;               (BX) = selector
;
;       EXIT:   if carry clear, no errors
;                       (AX:DX) = base linear address
;                       (SI:DI) = limit
;                       (BL) = access byte
;                       (BH) = attribute byte
;               if carry set,
;                       (AX) = error code, 0 = descriptor not copied
;
;       USES:   AX, BX, CX, DX, SI, DI, FLAGS
;
        assumeDATA <ds, es, ss>

DefProc DebGetDescInfo,NEAR,PUBLIC

LocVar  PTDAaddr, <size AddrS>
LocVar  TmpAddr, <size AddrS>
LocVar  AccInfo, BYTE

BeginProc
        or      ax,ax
        jnz     short dgd1
        mov     ax,CurrTask             ; if zero, then get current task number
dgd1:   DosContext <ds>,cx
        cmp     TaskNumber,ax
        DataContext <ds>,cx
        je      short dgd2              ; let debugger do current task

        test    bx,TABLE_MASK
        jnz     short dgd2              ; let debugger do LDT descriptors

        and     bl,SEL_LOW_MASK
        cmp     bx,GSEL GDT_PTDA
        je      short dgd3
        mov     AccInfo,D_LDT0
        cmp     bx,GSEL GDT_LDT
        je      short dgd4

dgd2:   xor     ax,ax                   ; descriptor not copied
        stc
        jmp     short dgdx

;       (AX) = Task number

dgd3:   mov     si,ax
        lea     bx,PTDAaddr             ; (BX) = pointer to PTDA addr
        Pcall   GetPTDAaddr             ; just to verify slot number
        jc      short dgd6

        push    ds
        FlatContext ds
        movzx   eax,si
        mov     esi,[_papTCBSlots]
        mov     esi,[esi+eax*4]                 ; (ESI) = pTCB
        mov     ax,[esi].TCBBiasTSD.HighWord
        mov     dx,[esi].TCBBiasTSD.LowWord     ; (AX:DX) = base address
        mov     edi,[esi].TCBCurrTSD
        add     edi,TSDSTART - 1        ; (EDI) = TSD limit
        pop     ds
    assumeDATA  ds
                                        ; GDT_PTDA is an expand down segment
        mov     bl,D_DATA0+D_EXPDN      ; (BL) = access byte
        jmp     short dgd5

;       (AX) = Task number

dgd4:   lea     bx,PTDAaddr             ; (BX) = pointer to PTDA addr
        Pcall   GetPTDAaddr
        jc      short dgd6

        Pcall   GetWordOff, <word ptr ptda_ldthandle, 0>
        jc      short dgd6              ; if carry clear, (DX) = ldt handle

        lea     bx,TmpAddr              ; (BX) = pointer to temporary AddrS
        Pcall   GetObjectAddr
        jc      short dgd6

        mov     ax,[bx].AddrOff.HighWord
        mov     dx,[bx].AddrOff.LowWord ; (AX:DX) = PTDA address (LIN)
        mov     bl,AccInfo              ; (BL) = access byte
        mov     di,-1                   ; (DI) = limit (64k - 1)

;       (AX:DX) = base linear address
;       (BL) = access byte
;       (DI) = 16 bit limit

dgd5:   xor     bh,bh                   ; (BH) = attribute byte
        xor     si,si                   ; (SI) = high word of limit (clears C)
        jmp     short dgdx

dgd6:   mov     ax,DEBERR_INVALID_TASK_NUMBER
        stc
dgdx:
EndProc

;***    DebGetPDE - Returns the PDE for the linear address
;
;       ENTRY:  (EAX) = linear address
;               (BX)  = task number
;
;       EXIT:   If carry clear,
;                       (ECX) = PDE
;               If carry set,
;                       (ECX) = PDE (0)
;                       (AX) = error code
;                       if (AX) == 0,
;                           get PTE from current context
;
;       USES:   EAX, EBX, ECX, EDX, ESI, EDI, Flags
;
        assumeDATA <ds, es, ss>

DefProc DebGetPDE, NEAR, PUBLIC

LocVar  dgp_laddr, DWORD
LocVar  dgp_addrPTDA, <size AddrS>
LocVar  dgp_addrsTemp, <size AddrS>

BeginProc
        or      bx,bx
        jnz     short dgp10
        mov     bx,[CurrTask]           ; if zero, then get current task number
dgp10:  DosContext <ds>,cx
        cmp     [TaskNumber],bx         ; is this the current slot?
        DataContext <ds>,cx
        je      dgp30                   ; let debugger do current task

        cmp     eax,MINSYSLADDR         ; if system arena, let debuger get PDE
        jae     dgp30                   ; jump if system arena          D81012

        cmp     eax,MAXGLOBALSHRLADDR   ; if invalid region             D81012
        jae     dpg20                   ; jump to error                 D81012

        cmp     eax,MAXCR_LADDR         ; if shared process region      D81012
        jae     dgp30                   ; let debugger get PDE          D81012

        mov     [dgp_laddr],eax         ; save linear address
        mov     ax,bx                   ; (AX) = task number
        lea     bx,[dgp_addrPTDA]       ; (BX) = pointer to PTDA addr
        Pcall   GetPTDAaddr
        jc      short dgpx

        mov     esi,[dgp_laddr]
        and     esi,mask LinPageDir
        shr     esi,LinPageDir          ; (ESI) = page directory index
        cmp     esi,MAXCRPDE            ; is PDE index > than table size
        jae     short dpg20             ;  yes, jump to error

;       (BX) = pointer to dpg_addrPTDA
;       (ESI) = PDE index

        lea     eax,[esi*(type pp_cpresent)].ptda_pgpc.pp_cpresent
        Pcall   GetWordOff, <eax>       ; (DX) = pte count
        jc      short dgpe
        or      dx,dx
        jz      short dpg20

        mov     eax,ptda_pgdata.pd_ppde
        Pcall   GetDwordOff, <eax>      ; (AX:DX) = flat pointer to pdes
        jc      short dgpe
        lea     bx,[dgp_addrsTemp]
        Pcall   FillLinAddr, <bx, dx, ax>
        lea     eax,[esi*4]
        Pcall   GetDwordOff, <eax>
        shl     eax,16
        mov     ax,dx                   ; (EAX) = pde
        mov     ecx,eax                 ; (ECX) = pde
        clc
        jmp     short dgpx

dgpe:   Jump    ErrBreak

dpg20:  xor     ecx,ecx                 ; PDE = 0
        mov     ax,DEBERR_INVALID_ADDRESS
        stc
        jmp     short dgpx

dgp30:  xor     ax,ax
        stc
dgpx:
EndProc

;***    DebGetFrame - return the frame number from a PDE or PTE
;
;       ENTRY   (EAX) = associated linear address
;               (ECX) = PDE or PTE (present bit is clear)
;               (BX)  = task number
;       EXIT    If carry clear,
;                   (EDX) = PTE frame
;               If carry set,
;                   (AX) = debugger error code
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, Flags

        assumeDATA <ds, es, ss>

DefProc DebGetFrame, NEAR, PUBLIC

LocVar  dgf_pte, DWORD
LocVar  dgf_flags, DWORD
LocVar  dgf_frame, DWORD
LocVar  dgf_lin, DWORD

BeginProc
        jecxz   short dgf20             ; jump if pte is invalid
        mov     [dgf_lin],eax
        mov     [dgf_pte],ecx
        Pcall   IsDebProtMode
        jnc     short dgf10             ; jump if debugger is in real mode

        movzx   esp,sp
        push    ds
        push    es
        FlatContext <ds,es>,eax

;       translate not-present pte to frame:
;       frame = pgIsInCore(ppte), -1 if error

        CCALL32 _pgIsInCore <dgf_pte>
        pop     es
        pop     ds
    assumeDATA <ds, es>
        mov     edx,eax
        add     eax,1                   ; set carry if error (-1) returned
        jnc     dgfx
dgf10:  mov     ax,DEBERR_PAGE_NOT_PRESENT
        stc
        jmp     short dgfx

dgf20:  mov     ax,DEBERR_INVALID_ADDRESS
        stc
dgfx:
EndProc

;***    GetPTDAaddr
;
;       Get the address of a task's PTDA.
;
;       ENTRY:  (AX) = task number
;               (BX) = AddrS struc to put address
;       EXIT:   if carry clear,
;                       no error
;               if carry set,
;                       (AX) = error code
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetPTDAaddr,NEAR
        SaveReg <dx, si>
        push    ds
        DosContext ds
        cmp     ax,[MaxThreads]
        jae     short gpa1              ; jump if task number too high
        mov     si,ax
        shl     si,2                    ; (DWORD)
        add     si,papTCBPtrs
        mov     dx,[si]                 ; (DX) = low word of linear address
        mov     ax,[si+2]               ; (AX) = high word of linear address
        or      ax,dx                   ; Is address zero?
        mov     ax,[si+2]               ; (AX) = high word of linear address
        pop     ds
    assumeDATA  ds
        jz      short gpa2              ;  yes, bad slot

;       (BX) = pointer to AddrS
;       (AX) = high word of address
;       (DX) = low word of address

        Pcall   FillLinAddr <bx,dx,ax>
        Pcall   GetDwordOff <word ptr TCBpPTDA, 0> ; (AX:DX) = PTDA address
        Pcall   FillLinAddr <bx,dx,ax>
        or      ax,dx                   ; PTDA address valid?
        jz      short gpa2              ;  no, return error (OR clears carry)
        jmp     short gpa3              ; Success

gpa1:   pop     ds
    assumeDATA  ds
gpa2:   mov     ax,DEBERR_INVALID_TASK_NUMBER
        stc                             ; map every error into this one
gpa3:   RestReg <si, dx>
EndProc

;***    GetRegister
;
;       Get the user's register.
;
;       ENTRY:  (CX) = register number
;               (SI) = PS structure pointer
;
;       EXIT:   (AX:DX) = register value
;               if carry clear,
;                       no error
;               if carry set,
;                       register number invalid
;
;       USES:   AX, BX, CX, DX, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetRegister,NEAR,LOCAL

LocVar  grg_addrsTemp, <size AddrS>
LocVar  grg_flags, WORD
LocVar  grg_offset, WORD

BeginProc
        lea     bx,[grg_addrsTemp]
        Pcall   FillLinAddr, <bx, [si].PS_pcriFrameType>
        Pcall   GetDwordOff, <cx, 0>    ; (AX) = offset, (DX) = flags
        jc      short grg35
        mov     [grg_flags],dx
        mov     [grg_offset],ax
        test    dx,KM_RIP_INVALID
        jnz     short grg35
        test    dx,KM_RIP_TSD_RELATIVE
        jnz     short grg10
        Pcall   FillLinAddr, <bx, [si].PS_pFrameBase>
        jmp     short grg20

grg10:  lea     ax,[si].PS_AddrTSD
        Pcall   CopyAddr, <ax, bx>
grg20:  Pcall   AddAddr, <[grg_offset], 0>
        test    [grg_flags],KM_RIP_WORD
        jz      short grg40
        Pcall   GetWord                 ; (DX) = register
        jc      short grg30
        xor     ax,ax                   ; CARRY CLEAR
        jmp     short grgx

grg30:  or      [si].PS_Flags,mask PF_TSDSWAPPED ; TSD must be swapped out
grg35:  xor     ax,ax
        xor     dx,dx
        stc
        jmp     short grgx

grg40:  Pcall   GetDword                ; (AX:DX) = register
        jc      short grg30
grgx:
EndProc

;***    InvalidHandle - print invalid handle message
;
;       ENTRY:  (DX) = bad handle
;
;       EXIT:   Does not return
;
;       USES:   NONE
;
        assumeDATA <ds, es, ss>

DefProc InvalidHandle,NEAR,PUBLIC
        putstr  "Bad handle: "
        Pcall   out16
        Jump    CmdBreak
EndProc NoGen

IFDEF   KDP
; JWE begin
;***    PutRegister
;
;       Put the user's register.
;
;       ENTRY:  (AX:DX) = register value
;               (CX) = register number
;               (SI) = PS structure pointer
;
;       EXIT:   if carry clear,
;                       no error
;               if carry set,
;                       register number invalid or memory not resident
;
;       USES:   AX, BX, CX, DX, Flags
;
        assumeDATA <ds, es, ss>

DefProc PutRegister,NEAR,LOCAL

LocVar  prg_addrsTemp, <size AddrS>
LocVar  prg_flags, WORD
LocVar  prg_offset, WORD
LocVar  prg_hiword, WORD
LocVar  prg_loword, WORD

BeginProc
        mov     [prg_hiword],ax
        mov     [prg_loword],dx
        lea     bx,[prg_addrsTemp]
        Pcall   FillLinAddr, <bx, [si].PS_pcriFrameType>
        Pcall   GetDwordOff, <cx, 0>    ; (AX) = offset, (DX) = flags
        jc      short prg35
        mov     [prg_flags],dx
        mov     [prg_offset],ax
        test    dx,KM_RIP_INVALID
        jnz     short prg35
        test    dx,KM_RIP_TSD_RELATIVE
        jnz     short prg10
        Pcall   FillLinAddr, <bx, [si].PS_pFrameBase>
        jmp     short prg20

prg10:  lea     ax,[si].PS_AddrTSD
        Pcall   CopyAddr, <ax, bx>
prg20:  Pcall   AddAddr, <[prg_offset], 0>
        test    [prg_flags],KM_RIP_WORD
        jz      short prg40
        mov     dx,[prg_loword]         ; (DX) = register
        Pcall   PutWord
        jc      short prg30
        xor     ax,ax                   ; CARRY CLEAR
        jmp     short prgx

prg30:  or      [si].PS_Flags,mask PF_TSDSWAPPED ; TSD must be swapped out
prg35:  xor     ax,ax
        xor     dx,dx
        stc
        jmp     short prgx

prg40:
        mov     ax,[prg_hiword]         ; (AX) = register high word
        mov     dx,[prg_loword]         ; (DX) = register low word
        Pcall   PutDword
        jc      short prg30
prgx:
EndProc

;***    KDPMemUtil - Verify memory in flat context is mapped in and get bytes
;
;       ENTRY:  kmc_offset = offset of desired item
;               kmc_psize  = -> size of desired item (bytes); permitted values
;                  are 1, 2, 4, and 4 < size <= MAXCACHESIZE.
;               kmc_pdata  = -> *kmc_psize bytes
;
;       EXIT:   (AX) = 0, normal return
;                  If on entry *kmc_psize is 1, 2, or 4, *kmc_pdata = 1, 2, or 4
;                     bytes from offset kmc_offset
;                  If on entry *kmc_psize is > 4, *kmc_psize is the number of
;                     bytes actually read and *kmc_pdata = *kmc_psize bytes from
;                     offset kmc_offset
;
;               (AX) != 0, memory is not resident (only returned if on entry
;                     *kmc_psize is 1, 2, or 4).
;
;       USES:   EAX
;
        assumeDATA <ds, es, ss>

DefProc KDPMemUtil, NEAR, PUBLIC, C
ArgVar  kmc_offset,     DWORD           ; offset of desired item
ArgVar  kmc_psize,      WORD            ; -> size of desired item (in/out)
ArgVar  kmc_pdata,      WORD            ; -> place to put desired item
LocVar  kmc_address,    STRUC, AddrS    ; input to GetMem routines

BeginProc
        SaveReg <ebx,ecx,edx,edi,esi>   ; Get* seems to clobber some of these?

        push gs
        FlatContext <gs>,bx

        ; Initialize address structure and put its offset in BX
        mov     [kmc_address].AddrType,EXPR_TYPE_SEL
        mov     [kmc_address].AddrTask,0
        mov     [kmc_address].AddrSeg,gs
        mov     [kmc_address].AddrSize,0  ; Size unimportant?
        mov     ebx,[kmc_offset]
        mov     [kmc_address].AddrOff,ebx
        pop     gs
    assume GS:NOTHING
        mov     bx,[kmc_psize]
        mov     dx,ds:[bx]
        lea     bx,[kmc_address]

        Pcall   GetCache     ; Get data in cache referenced by DI

        ; At this point, the carry flag is set if we had an error.  Otherwise
        ; the desired value is in the cache referenced by DI and the number
        ; of bytes returned is in CX.
        mov     ax,-1           ; Assume error
        jc      short kmcx      ; Error if carry is set
        mov     bx,[kmc_psize]  ; Get requested size
        mov     dx,ds:[bx]
        cmp     dx,4            ; If requested size is > 4
        jg      short kmc10
        cmp     dx,3            ;    or requested size is 3
        je      short kmc10     ;    don't have to have gotten all bytes
        cmp     dx,cx           ; If requested size is 1, 2, or 4 and we
        jne     short kmcx      ;    didn't get everything, error
kmc10:
        mov     ds:[bx],cx     ; Save number of bytes read
        xor     ax,ax          ; Return good RC
        mov     si,di          ; SI -> cache containing data
        mov     di,[kmc_pdata] ; DI -> place to put data
        rep movsb              ; Move bytes
kmcx:
        RestoreReg <esi,edi,edx,ecx,ebx>
EndProc
;JWE end

;***    KDPNextModule - Finds next module mte handle.
;
;       USHORT KDPNextModule(VMHOB *phmte, ULONG *ppmte);
;
;       ENTRY:  None
;
;       EXIT:   (AX) = 0, No error
;               *phmte = next mte handle
;               *ppmte = flat mte pointer
;
;               (AX) = ERROR_MOD_NOT_FOUND, No more modules
;
;       USES:   AX
;
        assumeDATA <ds, es, ss>

DefProc KDPNextModule, NEAR, PUBLIC, C
ArgVar  phmte,          WORD            ; mte handle buffer
ArgVar  ppmte,          WORD            ; flat mte pointer buffer

BeginProc
        SaveReg <ecx,dx,bx>
        mov     ecx,KDPpMTE             ; (ECX) = last pMTE returned
        push    ds                      ; (N.b., MTEs are not swappable)
        FlatContext <ds>,ax
        jecxz   short knm10             ; if (last pMTE != NULL)

        mov     ecx,[ecx].mte_link      ;    (ECX) = last pMTE->mte_link
        jmp     short knm20             ; else

knm10:  mov     ecx,_mte_h              ;    (ECX) = mte list head

knm20:  mov     ax,ERROR_MOD_NOT_FOUND  ; initialize return value
        jecxz   short knm30             ; if (pMTE != NULL)

        mov     dx,[ecx].mte_handle     ;    (DX) = mte handle
        .errnz  NO_ERROR
        xor     ax,ax                   ;    (AX) = NO_ERROR
knm30:  pop     ds
        assumeDATA <ds>
        mov     bx,[ppmte]
        mov     [bx],ecx                ; *ppmte = pMTE
        mov     KDPpMTE,ecx             ; last pMTE returned = pMTE
        mov     bx,[phmte]
        mov     [bx],dx                 ; *hmte = mte handle
        RestoreReg <bx,dx,ecx>
EndProc

;***    KDPhmte2pmte - Translate mte handle to flat mte pointer
;
;       USHORT KDPhmte2pmte(VMHOB hmte, ULONG *ppmte);
;
;       ENTRY:  (hmte) = mte handle (0 implies DosModMte)
;               (ppmte) = pointer to space for flat pMTE
;
;       EXIT:   (AX) = 0, No error
;               *ppmte = flat mte pointer
;
;               (AX) = ERROR_MOD_NOT_FOUND, invalid handle
;
;       USES:   AX
;
        assumeDATA <ds, es, ss>

DefProc KDPhmte2pmte, NEAR, PUBLIC, C

ArgVar  hmte,           WORD            ; mte handle
ArgVar  ppmte,          WORD            ; mte pointer buffer

BeginProc
        SaveReg <bx,ecx>
        .errnz  NO_ERROR
        xor     ax,ax                   ; initialize return value
        mov     ecx,offset FLAT:_DosModMte
                                        ; (ECX) = DosModMTE
        mov     bx,[hmte]               ; (BX) = mte handle
        or      bx,bx                   ; if (mte handle != 0) {
        jz      short khp3

        push    ds
        FlatContext <ds>,cx             ; (MTEs are not swappable)
        mov     ecx,ds:_mte_h           ;  (ECX) = mte list head
khp1:   cmp     ds:[ecx].mte_handle,bx  ;  while (ECX)->mte_handle != mte_handle
        je      short khp2              ;   {

        mov     ecx,ds:[ecx].mte_link   ;   (ECX) = (ECX)->mte_link
        or      ecx,ecx                 ;   if (ECX) = NULL
        jnz     short khp1

        mov     ax,ERROR_MOD_NOT_FOUND  ;      (AX) = ERROR_MOD_NOT_FOUND
                                        ;      break
                                        ;   }
khp2:   pop     ds
        assumeDATA <ds>
                                        ; }
khp3:   mov     bx,[ppmte]
        mov     [bx],ecx                ; *ppmte = (ECX)
khpx:   RestoreReg <ecx,bx>
EndProc

;JWE begin
;***    KDPpmte2name - Translate mte flat pointer to module name
;
;       ENTRY:  hmte = mte handle
;               pbuf = buffer for name
;
;       EXIT:   (AX) = name length (including terminating zero); zero if
;               SMTE or name is not resident.
;
;       USES:   AX
;
        assumeDATA <ds, es, ss>

DefProc KDPpmte2name, NEAR, PUBLIC, C

ArgVar  pmte,           DWORD           ; module mte
ArgVar  pbuf,           WORD            ; module name buffer
LocVar  pointer         DWORD           ; -> swappable things
LocVar  kpn_len         WORD            ; Number of bytes in swappable things

BeginProc
        SaveReg <bx,cx,edi>
        mov     edi,[pmte]              ; if (pmte != NULL) {
        or      edi,edi
        je      khnx

        push    ds
        FlatContext <ds>,ax
        mov     edi,ds:[edi].mte_swapmte ; (EDI) = pointer to swappable mte
        pop     ds                       ; (N.b., MTEs are not swappable)
    assumeDATA  ds
        lea     edi,[edi].smte_path      ; (EDI) = address of -> full pathname
        lea     bx,[pointer]             ;
        mov     [kpn_len],4              ; Make sure constant is proper size
        lea     cx,[kpn_len]

        Ccall   KDPMemUtil,<edi,cx,bx>   ; Get -> full pathname if resident
        xor     di,di                    ; Assume failure
        or      ax,ax
        jnz     short khnx

        mov     edi,[pointer]            ; (ESI) = pointer to full pathname
        mov     bx,[pbuf]                ;
        mov     [kpn_len],CVK_MEMCACHE_SIZE  ; n.b., must
                                         ;    be <= MEMCACHESIZE from debequ.inc

        Ccall   KDPMemUtil,<edi,cx,bx>   ; Get pathname if resident
        xor     di,di                    ; Assume failure
        or      ax,ax
        jnz     short khnx

        mov     di,[pbuf]               ; (DI) = name buffer
        mov     cx,[kpn_len]            ; (CX) = number of bytes in buffer
        xor     al,al                   ; Find trailing null
        repne scasb
        jcxz    short khn10
        sub     di,[pbuf]               ; Null found - compute length (DI
        jmp     short khnx              ;    incremented on each scasb)
khn10:  xor     di,di                   ; Null not found - return zero length

khnx:   mov     ax,di                   ; return(Name length)
        RestoreReg <edi,cx,bx>
EndProc

;***    KDPObjInfo - convert mte, obj/seg index to selector, base, flags
;
;       ENTRY:  koi_hmte = module mte handle
;               koi_iote = obj/seg table index
;               koi_psel = -> WORD
;               koi_pbase = -> DWORD
;               koi_pflags = -> DWORD
;
;       EXIT:   (AX) = 0, normal return
;               *koi_psel   = selector for object/segment
;               *koi_pbase  = base address for object (0 for segments)
;               *koi_pflags = object/segment flags
;
;               (AX) != 0, SMTE or object/segment table not resident
;
;       USES:   AX
;
        assumeDATA <ds, es, ss>

DefProc KDPObjInfo, NEAR, PUBLIC, C

ArgVar  koi_hmte,       WORD            ; module mte
ArgVar  koi_iote,       WORD            ; object table index
ArgVar  koi_psel,       WORD            ; pointer to selector
ArgVar  koi_pbase,      WORD            ; pointer to dword base
ArgVar  koi_pflags,     WORD            ; pointer to object flags
LocVar  koi_pmte,       DWORD           ; pointer to module mte
LocVar  koi_mflg,       DWORD           ; savearea for module flags
LocVar  koi_flsav,      BYTE            ; savearea for flags
LocVar  koi_len,        WORD            ; Arg to KDPMemUtil
LocVar  koi_pobj,       DWORD           ; pointer to object/segment table entry

BeginProc
        SaveReg <ebx,ecx,edx,edi,esi>
        lea     bx,[koi_pmte]
        Ccall   KDPhmte2pmte,<[koi_hmte],bx>
        or      ax,ax
        jnz     koix

        mov     edi,[koi_pmte]           ; (EDI) = pMTE

        push    ds
        FlatContext <ds>,ax
        mov     esi,ds:[edi].mte_swapmte ; (ESI) = pointer to swappable mte
        mov     ebx,ds:[edi].mte_flags1  ; (EBX) = module flags
        mov     [koi_mflg],ebx
        mov     ax,ds:[edi].mte_flags2   ; what format module?
        and     ax,MTEFORMATMASK
        cmp     ax,MTEFORMATNE
        lahf
        mov     [koi_flsav],ah
        pop     ds
    assumeDATA  ds

        lea     esi,[esi].smte_objtab    ; (ESI) = address of -> object table
        lea     bx,[koi_pobj]
        mov     [koi_len],4
        lea     dx,[koi_len]

        Ccall   KDPMemUtil,<esi,dx,bx>  ; Get -> object/segment table if ptr
        or      ax,ax                   ;    is resident
        jnz     koix

        mov     esi,[koi_pobj]          ; (ESI) = -> object/segment table

        movzx   ecx,[koi_iote]          ; fetch object table index
        dec     ecx
        mov     ah,[koi_flsav]          ; what format module?
        sahf
        je      koi30                   ; jump if segment table

        imul    ecx,size ldrote_s       ; use object table format
        add     ecx,esi                 ; (ECX) = address of object table entry
        lea     esi,[ecx].ote_flags     ; (ESI) = address of OTE flags

        Ccall   KDPMemUtil,<esi,dx,[koi_pflags]>  ; Get OTE flags if resident
        or      ax,ax
        jnz     koix

        lea     esi,[ecx].ote_base      ; (ESI) = address of OTE base

        Ccall   KDPMemUtil,<esi,dx,[koi_pbase]>  ; Get OTE base if resident
        or      ax,ax
        jnz     koix

        lea     esi,[ecx].ote_selector  ; (CX) = address of OTE selector
        mov     [koi_len],2

        Ccall   KDPMemUtil,<esi,dx,[koi_psel]>  ; Get OTE selector if resident
        or      ax,ax
        jnz     koix

        mov     bx,[koi_pflags]
        mov     esi,ds:[bx]             ; (ESI) = object flags
        mov     ebx,[koi_mflg]          ; (EBX) = module flags

        test    ebx,DOSMOD              ; If module is DOSCALLS
        jz      short koi05

        test    esi,OBJBIGDEF           ; If object is not 0:32
        jz      koi40                   ; Base is 0
        jmp     koi50                   ; Otherwise base is from OTE

koi05:
        test    esi,OBJBIGDEF           ; If module is not DOSCALLS
        jz      short koi20             ; And object is 0:32

        test    esi,OBJEXEC             ; If object is code
        jz      short koi10

    ; For reasons I don't understand, we have to fool the linker (assembler?)
    ; into thinking DS is pointing to the flat data segment in order to get
    ; the proper offsets loaded in the lea ecx instructions below...
    assume DS:FLAT
        lea     ecx,ds:_R0CS            ; If module is VDD, use ring 0 CS
        test    ebx,VDDMOD
        jnz     short koi15
        lea     ecx,ds:_R3CS            ; If module not VDD, object not IOPL,
        test    esi,OBJIOPL             ;    use ring 3 CS
        jz      short koi15
        lea     ecx,ds:_R2CS            ; If object IOPL, use ring 2 CS
        jmp     short koi15

koi10:                                  ; 0:32 data object
        lea     ecx,ds:_R0DS            ; If module is VDD, use ring 0 DS
        test    ebx,VDDMOD
        jnz     short koi15
        lea     ecx,ds:_R3DS            ; If module not VDD, object not IOPL,
        test    esi,OBJIOPL             ;    use ring 3 DS
        jz      short koi15
        lea     ecx,ds:_R2DS            ; If object IOPL, use ring 2 DS
        jmp     short koi15
    assume DS:DATA ; JWE see above
koi15:
        Ccall   KDPMemUtil,<ecx,dx,[koi_psel]>  ; Get ring n CS/DS if resident
        or      ax,ax
        jnz     koix
        jmp     koi50


koi20:                                  ; 16:16 object
        mov     bx,[koi_pbase]          ; Convert base address to selector
        mov     edx,ds:[bx]
        and     edx,SEL_LADDR_MASK
        shr     edx,SEL_LADDR_SHIFT
        or      dx,SEL_LDTBIT           ; All DLL objects are LDT
        or      dx,SEL_RPL2             ; If object has IOPL, use ring 2
        test    esi,OBJIOPL             ;    selector
        jnz     short koi25
        or      dx,SEL_RPL3             ; Otherwise use ring 3 selector
koi25:
        mov     bx,[koi_psel]
        mov     ds:[bx],dx
        jmp     short koi40

koi30:
        imul    ecx,size ldrste_s       ; use segment table format
        add     ecx,esi                 ; (ECX) = address of segment table entry
        lea     esi,[ecx].ste_flags     ; (ESI) = address of STE flags
        mov     [koi_len],2

        Ccall   KDPMemUtil,<esi,dx,[koi_pflags]>  ; Get STE flags if resident
        or      ax,ax
        jnz     short koix

        mov     bx,[koi_pflags]
        mov     word ptr ds:[bx+2],0    ; Pad flags with zeros

        lea     esi,[ecx].ste_selector  ; (ESI) = address of STE selector

        Ccall   KDPMemUtil,<esi,dx,[koi_psel]>    ; Get STE selector if resident
        or      ax,ax
        jnz     short koix

koi40:
        mov     bx,[koi_pbase]
        mov     dword ptr ds:[bx],0     ; Base = 0

koi50:
koix:
        RestoreReg <esi,edi,edx,ecx,ebx>
EndProc

;***    KDPObjCount - return number of objects/segments in module (not including
;                     resource objects)
;
;       ENTRY:  koc_hmte = module mte handle
;               koc_pcount = -> DWORD
;
;       EXIT:   (AX) = 0, normal return
;               *koc_pcount = number of objects/segments in module
;
;               (AX) != 0, SMTE not resident
;
;       USES:   EAX
;
        assumeDATA <ds, es, ss>

DefProc KDPObjCount, NEAR, PUBLIC, C

ArgVar  koc_hmte,       WORD            ; module mte
ArgVar  koc_pcount,     WORD            ; -> dword object count
LocVar  koc_pmte,       DWORD           ; pointer to module mte
LocVar  koc_count       DWORD           ; object count
LocVar  koc_sel         WORD            ; selector from KDPObjInfo
LocVar  koc_base        DWORD           ; base from KDPObjInfo
LocVar  koc_flags       DWORD           ; flags from KDPObjInfo
LocVar  koc_len         WORD            ; Arg for KDPMemUtil
LocVar  koc_modtype     BYTE            ; savearea for flags

BeginProc
        SaveReg <ebx,ecx,edx,edi,esi>
        lea     bx,[koc_pmte]
        Ccall   KDPhmte2pmte,<[koc_hmte],bx>
        or      ax,ax
        jnz     kocx

        mov     edi,[koc_pmte]           ; (EDI) = pMTE
        push    ds
        FlatContext <ds>,ax
        mov     esi,ds:[edi].mte_swapmte ; (ESI) = pointer to swappable mte
        mov     ax,ds:[edi].mte_flags2   ; what format module?
        and     ax,MTEFORMATMASK
        cmp     ax,MTEFORMATNE
        lahf
        mov     [koc_modtype],ah
        pop     ds
    assumeDATA  ds

        lea     esi,[esi].smte_objcnt   ; (ESI) = address of object count
        lea     bx,[koc_count]
        mov     [koc_len],4
        lea     dx,[koc_len]

        Ccall   KDPMemUtil,<esi,dx,bx>  ; Get object count if resident
        or      ax,ax
        jnz     short kocx

        mov     edx,[koc_count]         ; (EDX) = number of objects in module
        test    edx,edx                 ; if no objects, return count = 0
        jz      short koc30

        mov     ah,[koc_modtype]
        sahf
        je      short koc30             ; For 16-bit modules, objcnt is correct

        lea     bx,[koc_sel]            ; Set up args for KDPObjInfo
        lea     si,[koc_base]
        lea     di,[koc_flags]
        mov     ecx,edx
koc10:
        Ccall   KDPObjInfo,<[koc_hmte],cx,bx,si,di>
        or      ax,ax                ; If error, return bad rc
        jnz     short kocx
        test    [koc_flags],OBJRSRC  ; Is entry a resource object?
        jz      short koc20
        dec     edx                  ; Yes - decrement count of objects
koc20:  dec     cx                   ; If we've reached the start of the table
        jnz     short koc10          ;    we're done
koc30:  mov     bx,[koc_pcount]
        mov     ds:[bx],edx          ; Store count
        xor     ax,ax                ; Return 0
kocx:   RestoreReg <esi,edi,edx,ecx,ebx>
EndProc

;***    KDPObjInfo2 - convert mte, obj/seg index to selector/base, flags, size
;
;       ENTRY:  ko2_hmte = module mte handle
;               ko2_iote = obj/seg table index
;               ko2_ptype = -> WORD
;               ko2_pbase = -> DWORD
;               ko2_pflags = -> DWORD
;               ko2_psize  = -> DWORD
;
;       EXIT:   (AX) = 0, normal return
;               *ko2_ptype  = type information
;                  Bit 0 (low-order bit) 0=>32-bit object, 1=>16-bit segment
;                  Bit 1 0=>code 1=>data
;                  Bits 2-15 unused
;               *ko2_pbase  = base address (32-bit object)/selector (16-bit
;                   segment
;               *ko2_pflags = object/segment flags
;               *ko2_psize  = object size (32-bit object)/garbage (16-bit
;                   segment
;
;               (AX) != 0, SMTE or object/segment table not resident
;
;       USES:   EAX
;
        assumeDATA <ds, es, ss>

DefProc KDPObjInfo2, NEAR, PUBLIC, C

ArgVar  ko2_hmte,       WORD            ; module mte
ArgVar  ko2_iote,       WORD            ; object table index
ArgVar  ko2_ptype,      WORD            ; pointer to word type
ArgVar  ko2_pbase,      WORD            ; pointer to dword base/selector
ArgVar  ko2_pflags,     WORD            ; pointer to dword object flags
ArgVar  ko2_psize,      WORD            ; pointer to dword size
LocVar  ko2_pmte,       DWORD           ; pointer to module mte
LocVar  ko2_pobj,       DWORD           ; pointer to object/segment table
LocVar  ko2_len,        WORD            ; Arg to KDPMemUtil
LocVar  ko2_flsav,      BYTE            ; savearea for flags

BeginProc
        SaveReg <ebx,ecx,edx,edi,esi>
        lea     bx,[ko2_pmte]
        Ccall   KDPhmte2pmte,<[ko2_hmte],bx>
        or      ax,ax
        jnz     ko2x

        mov     edi,[ko2_pmte]          ; (EDI) = pMTE

        push    ds
        FlatContext <ds>,ax
        mov     esi,ds:[edi].mte_swapmte ; (ESI) = pointer to swappable mte
        mov     ax,ds:[edi].mte_flags2   ; what format module?
        and     ax,MTEFORMATMASK
        cmp     ax,MTEFORMATNE
        lahf
        mov     [ko2_flsav],ah
        pop     ds
    assumeDATA  ds

        lea     esi,[esi].smte_objtab    ; (ESI) = address of -> object table
        lea     bx,[ko2_pobj]
        mov     [ko2_len],4
        lea     dx,[ko2_len]

        Ccall   KDPMemUtil,<esi,dx,bx>  ; Get -> object/segment table if
        or      ax,ax                   ;    resident
        jnz     ko2x

        mov     esi,[ko2_pobj]          ; (ESI) = -> object/segment table

        movzx   ecx,[ko2_iote]          ; fetch object table index
        dec     ecx
        mov     ah,[ko2_flsav]          ; what format module?
        sahf
        je      ko210                   ; jump if segment table

        imul    ecx,size ldrote_s       ; use object table format
        add     ecx,esi                 ; (ECX) = address of object table entry
        lea     esi,[ecx].ote_flags     ; (ESI) = address of OTE flags

        Ccall   KDPMemUtil,<esi,dx,[ko2_pflags]> ; Get OTE flags if resident
        or      ax,ax
        jnz     ko2x

        lea     esi,[ecx].ote_base      ; (ESI) = address of OTE base

        Ccall   KDPMemUtil,<esi,dx,[ko2_pbase]>  ; Get OTE base if resident
        or      ax,ax
        jnz     ko2x

        lea     esi,[ecx].ote_size      ; (ESI) = address of OTE size

        Ccall   KDPMemUtil,<esi,dx,[ko2_psize]>  ; Get OTE size if resident
        or      ax,ax
        jnz     ko2x

        mov     bx,[ko2_pflags]         ; (BX) -> object flags
        mov     esi,ds:[bx]             ; (ESI) = object flags
        xor     cx,cx                   ; (CX)  = object type (32-bit)

        test    esi,OBJEXEC             ; Executable?
        jnz     short ko220
        inc     cx                      ; No - note object is data
        inc     cx
        jmp     short ko220

ko210:  imul    ecx,size ldrste_s       ; use segment table format
        add     ecx,esi                 ; (ECX) = address of segment table entry

        lea     esi,[ecx].ste_flags     ; (ESI) = address of STE flags
        mov     [ko2_len],2

        Ccall   KDPMemUtil,<esi,dx,[ko2_pflags]> ; Get STE flags if resident
        or      ax,ax
        jnz     ko2x

        mov     bx,[ko2_pflags]
        mov     word ptr ds:[bx + 2],0  ; Clear high order word of flags

        lea     esi,[ecx].ste_selector  ; (ESI) = address of STE selector

        Ccall   KDPMemUtil,<esi,dx,[ko2_pbase]> ; Get STE selector if resident
        or      ax,ax
        jnz     ko2x

        mov     bx,[ko2_pbase]
        mov     word ptr ds:[bx + 2],0  ; Clear high order word of selector

        mov     bx,[ko2_psize]
        mov     dword ptr ds:[bx],0     ; Size = 0

        mov     bx,[ko2_pflags]         ; (BX) -> object flags
        mov     esi,ds:[bx]             ; (ESI) = object flags
        xor     cx,cx                   ; (CX)  = object type (16-bit)
        inc     cx

        test    esi,STE_DATA            ; Data?
        jz      short ko220
        inc     cx                      ; Yes - note object is data
        inc     cx

ko220:  mov     bx,[ko2_ptype]
        mov     es:[bx],cx              ; Store type information

        xor     ax,ax                   ; Return 0
ko2x:
        RestoreReg <esi,edi,edx,ecx,ebx>
EndProc

;***    KDPQueryThread - get PID, thread ordinal, MTE handle, and next thread
;                        slot.
;
;       ENTRY:  kqt_slot = -> slot about which information is desired (slot = 0
;                          => first occupied slot, slot = -1 => currently
;                          scheduled slot [TaskNumber])
;               kqt_thisslot = -> WORD
;               kqt_PID  = -> WORD
;               kqt_Ord  = -> WORD
;               kqt_MTE  = -> WORD
;
;       EXIT:   (AX) = 0, No error
;               *kqt_slot = index of next valid thread slot (0 if none)
;               *kqt_thisslot = index of desired thread's thread slot
;               *kqt_PID  = PID of process containing desired thread
;               *kqt_Ord  = ordinal of desired thread
;               *kqt_MTE  = MTE handle of module running in PID process
;
;               (AX) != 0, invalid slot
;
;       USES:   AX
;
        assumeDATA <ds, es, ss>

DefProc KDPQueryThread, NEAR, PUBLIC, C

ArgVar  kqt_slot,       WORD   ; -> slot (in/out)
ArgVar  kqt_thisslot,   WORD   ; -> current slot (out)
ArgVar  kqt_PID ,       WORD   ; -> PID (out)
ArgVar  kqt_Ord ,       WORD   ; -> ordinal (out)
ArgVar  kqt_MTE ,       WORD   ; -> MTE (out)
LocVar  kqt_pTCB,       DWORD  ; -> TCB

BeginProc
        SaveReg <bx,ecx,dx,si,di>
        mov     ax,1               ; Assume error
        push    es
        DosContext es
        mov     cx,[MaxThreads]    ; cx = MaxThreads
        mov     si,[kqt_slot]      ; si = kqt_slot
        mov     di,[si]            ; di = *kqt_slot
        cmp     di,-1              ; if *kqt_slot = -1
        jne     kqt05
        mov     di,[TaskNumber]    ; Use currently scheduled thread
kqt05:  cmp     cx,di              ; if MaxThreads <= *kqt_slot
        jbe     kqtx               ;    *kqt_slot is out of range
        or      di,di              ; if *kqt_slot = 0
        jnz     short kqt10
        inc     di                 ; Start at slot 1
        dec     cx                 ; Stop at slot MaxThreads - 1
        jmp     short kqt20        ; if *ktq_slot != 0
kqt10:  mov     cx,di              ; Stop at *ktq_slot (only check one)
kqt20:  shl     cx,2               ; Convert stop slot to offset
        shl     di,2               ; Convert start slot to offset
        mov     bx,[papTCBPtrs]        ; bx = papTCBPtrs array
kqt30:  mov     dx,es:[bx+di].HighWord ; do
        mov     [kqt_pTCB].HighWord,dx ; {
        mov     dx,es:[bx+di].LowWord  ;    kqt_pTCB = papTCBPtrs[slot];
        mov     [pTCB].LowWord,dx      ;    slot += 1
        or      dx,[kqt_pTCB].HighWord ; } while(kqt_pTCB == NULL &&
        jnz     short kqt40            ;         slot < MaxThreads)
        add     di,4
        cmp     di,cx
        jle     kqt30
        jmp     kqtx                   ; Reach here if no nonNULL TCB ptrs
kqt40:  mov     cx,[MaxThreads]        ; cx = MaxThreads - 1
        dec     cx
        shl     cx,2                   ; Convert stop slot to offset
        jmp     short kqt50
kqt45:  mov     dx,es:[bx+di].HighWord ; Find next nonNULL TCB ptr, if any
        or      dx,es:[bx+di].LowWord
        jnz     short kqt60
kqt50:  add     di,4                   ; Move to next TCB slot
        cmp     di,cx                  ; Stop if we're past the end
        jbe     kqt45
        xor     di,di                  ; Reach here if no subsequent nonNULL ptr
kqt60:  shr     di,2                   ; di = next slot
        mov     [si],di                ; *kqt_slot = di
        FlatContext es
        mov     bx,[kqt_PID]            ; bx = kqt_PID
        mov     si,[kqt_Ord]            ; si = kqt_Ord
        mov     di,[kqt_MTE]            ; di = kqt_MTE
        mov     ecx,[kqt_pTCB]          ; ecx = kqt_pTCB
        mov     dx,es:[ecx].TCBOrdinal  ; dx = ordinal
        mov     [si],dx                 ; *kqt_Ord = ordinal
        mov     dx,es:[ecx].TCBNumber   ; dx = thread slot number
        mov     si,[kqt_thisslot]       ; si = kqt_thisslot
        mov     [si],dx                 ; *kqt_thisslot = thread slot number
        mov     ecx,es:[ecx].TCBpPTDA   ; ecx = -> PTDA for thread
        mov     dx,es:[ecx].ptda_module ; dx = MTE handle for thread
        mov     [di],dx                 ; *kqt_MTE = MTE handle
        mov     dx,es:[ecx].ptda_Pid    ; dx = Process PID
        mov     [bx],dx                 ; *kqt_PID = PID
        xor     ax,ax                   ; Return good rc
kqtx:   pop     es
    assumeDATA  es
        RestoreReg <di,si,dx,ecx,bx>
EndProc

;***    KDPGetRegs - get registers
;
;       ENTRY:  kgr_slot = slot whose registers are desired
;               kgr_pregs = -> RegSA_struc to hold registers
;
;       EXIT:   (AX) = 0, No error
;               *kgr_pregs = registers for thread
;
;               (AX) != 0, invalid slot or something swapped out
;
;       USES:   AX
;
        assumeDATA <ds, es, ss>

DefProc KDPGetRegs, NEAR, PUBLIC, C

ArgVar  kgr_slot,    WORD            ; slot
ArgVar  kgr_pregs,   WORD            ; -> register savearea (out)
LocVar  kgr_psbuff,  STRUC, ProcStat ; Process status

BeginProc
        SaveReg <bx,cx,dx,di,si>
; Code stolen from RegisterSlot

        lea     bx,kgr_psbuff
        Pcall   GetProcStat, <GET_PROC_USER, kgr_slot, bx>
        jc      kgrerr

        mov     si,dataOFFSET RegSA
        mov     di,[kgr_pregs]
        mov     cx,size RegSA_frame
        rep movsb                       ; initialize RegSave (gets CR0, etc.)

; Get general registers

        mov     di,[kgr_pregs]          ; (DI) -> register savearea
        lea     si,[kgr_psbuff]         ; (SI) -> PS_Buffer
        mov     cx,KM_REG_EAX
        Pcall   GetRegister             ; (AX:DX) = client's EAX register
        jc      kgrerr                  ; N.b., GetRegister uses ax,bx,cx,dx
        mov     [di].EAXsav.LowWord,dx
        mov     [di].EAXsav.HighWord,ax

        mov     cx,KM_REG_EBX
        Pcall   GetRegister             ; (AX:DX) = client's EBX register
        jc      kgrerr
        mov     [di].EBXsav.LowWord,dx
        mov     [di].EBXsav.HighWord,ax

        mov     cx,KM_REG_ECX
        Pcall   GetRegister             ; (AX:DX) = client's ECX register
        jc      kgrerr
        mov     [di].ECXsav.LowWord,dx
        mov     [di].ECXsav.HighWord,ax

        mov     cx,KM_REG_EDX
        Pcall   GetRegister             ; (AX:DX) = client's EDX register
        jc      kgrerr
        mov     [di].EDXsav.LowWord,dx
        mov     [di].EDXsav.HighWord,ax

        mov     cx,KM_REG_EBP
        Pcall   GetRegister             ; (AX:DX) = client's EBP register
        jc      kgrerr
        mov     [di].EBPsav.LowWord,dx
        mov     [di].EBPsav.HighWord,ax

        mov     cx,KM_REG_ESI
        Pcall   GetRegister             ; (AX:DX) = client's ESI register
        jc      kgrerr
        mov     [di].ESIsav.LowWord,dx
        mov     [di].ESIsav.HighWord,ax

        mov     cx,KM_REG_EDI
        Pcall   GetRegister             ; (AX:DX) = client's EDI register
        jc      kgrerr
        mov     [di].EDIsav.LowWord,dx
        mov     [di].EDIsav.HighWord,ax

        mov     cx,KM_REG_DS
        Pcall   GetRegister             ; (DX) = client's DS register
        jc      kgrerr
        mov     [di].DSsav,dx

        mov     cx,KM_REG_ES
        Pcall   GetRegister             ; (DX) = client's ES register
        jc      kgrerr
        mov     [di].ESsav,dx

        mov     cx,KM_REG_FS
        Pcall   GetRegister             ; (DX) = client's FS register
        jc      kgrerr
        mov     [di].FSsav,dx

        mov     cx,KM_REG_GS
        Pcall   GetRegister             ; (DX) = client's GS register
        jc      kgrerr
        mov     [di].GSsav,dx

        mov     cx,KM_REG_EFLAG
        Pcall   GetRegister             ; (AX:DX) = client's eflags
        jc      kgrerr
        mov     [di].EFsav.LowWord,dx
        mov     [di].EFsav.HighWord,ax

;       CS:EIP, SS:EIP, MSW are all in buffer returned by GetProcStat

        mov     ax,[si].PS_UserSS
        mov     [di].SSsav,ax
        mov     eax,[si].PS_UserESP
        mov     [di].ESPsav,eax

;       GDT and IDT are stored in DebData

        mov     ax,[DebData.GDTaddr.DTRBaseLow]
        mov     [di].GDTsav.DTRBaseLow,ax
        mov     ax,[DebData.GDTaddr.DTRLimit]
        mov     [di].GDTsav.DTRLimit,ax
        mov     ax,[DebData.GDTaddr.DTRBaseHighExt]
        mov     [di].GDTsav.DTRBaseHighExt,ax

        lea     bx,DebData.ProtIDTaddr          ; (BX) -> prot idt address
        mov     cx,EXPR_TYPE_SEL                ; (CX) = sel type
        test    [di].EFsav,F_VM
        jz      short kgr10
        lea     bx,DebData.RealIDTaddr          ; (BX) -> real idt address
        mov     cx,EXPR_TYPE_SEG                ; (CX) = seg type
kgr10:
        mov     ax,[bx].DTRBaseLow
        mov     [di].IDTsav.DTRBaseLow,ax
        mov     ax,[bx].DTRLimit
        mov     [di].IDTsav.DTRLimit,ax
        mov     ax,[bx].DTRBaseHighExt
        mov     [di].IDTsav.DTRBaseHighExt,ax

;       Set up RegSave AddrS structure, (SI) = PS_Buffer, (CX) = addr type

        lea     bx,[di].CSIPsav                 ; (BX) = &kgr_pregs->CSIPsav
        Pcall   FillAddr, <bx, [si].PS_UserCS, [si].PS_UserEIP.LowWord, \
                           [si].PS_UserEIP.HighWord, kgr_slot, cx>
        xor     ax,ax  ; Return good RC
        jmp     short kgrx
kgrerr:
        mov     ax,1
kgrx:
        RestoreReg <si,di,dx,cx,bx>
EndProc

;***    KDPPutRegs - put registers
;
;       ENTRY:  kpr_slot = slot whose registers are to be changed
;               kpr_pregs = -> RegSA_struc that holds registers
;
;       EXIT:   (AX) = 0, No error
;
;               (AX) != 0, invalid slot or something swapped out
;
;       USES:   AX
;
        assumeDATA <ds, es, ss>

DefProc KDPPutRegs, NEAR, PUBLIC, C

ArgVar  kpr_slot,    WORD            ; slot
ArgVar  kpr_pregs,   WORD            ; -> register savearea
LocVar  kpr_psbuff,  STRUC, ProcStat ; Process status

BeginProc
        SaveReg <bx,cx,dx,di,si>
; Code stolen from RegisterSlot

        lea     bx,kpr_psbuff
        Pcall   GetProcStat, <GET_PROC_USER, kpr_slot, bx>
        jc      kprerr

; Put general registers

        mov     di,[kgr_pregs]          ; (DI) -> register savearea
        lea     si,[kgr_psbuff]         ; (SI) -> PS_Buffer
        mov     cx,KM_REG_EAX
        mov     ax,[di].EAXsav.HighWord ; (AX:DX) = client's EAX register
        mov     dx,[di].EAXsav.LowWord
        Pcall   PutRegister
        jc      kprerr                  ; N.b., PutRegister uses ax,bx,cx,dx

        mov     cx,KM_REG_EBX
        mov     ax,[di].EBXsav.HighWord ; (AX:DX) = client's EBX register
        mov     dx,[di].EBXsav.LowWord
        Pcall   PutRegister
        jc      kprerr

        mov     cx,KM_REG_ECX
        mov     ax,[di].ECXsav.HighWord ; (AX:DX) = client's ECX register
        mov     dx,[di].ECXsav.LowWord
        Pcall   PutRegister
        jc      kprerr

        mov     cx,KM_REG_EDX
        mov     ax,[di].EDXsav.HighWord ; (AX:DX) = client's EDX register
        mov     dx,[di].EDXsav.LowWord
        Pcall   PutRegister
        jc      kprerr

        mov     cx,KM_REG_EBP
        mov     ax,[di].EBPsav.HighWord ; (AX:DX) = client's EBP register
        mov     dx,[di].EBPsav.LowWord
        Pcall   PutRegister
        jc      kprerr

        mov     cx,KM_REG_ESI
        mov     ax,[di].ESIsav.HighWord ; (AX:DX) = client's ESI register
        mov     dx,[di].ESIsav.LowWord
        Pcall   PutRegister
        jc      kprerr

        mov     cx,KM_REG_EDI
        mov     ax,[di].EDIsav.HighWord ; (AX:DX) = client's EDI register
        mov     dx,[di].EDIsav.LowWord
        Pcall   PutRegister
        jc      kprerr

        mov     cx,KM_REG_ESP
        mov     ax,[di].ESPsav.HighWord ; (AX:DX) = client's ESP register
        mov     dx,[di].ESPsav.LowWord
        Pcall   PutRegister
        jc      kprerr

        mov     cx,KM_REG_EIP
        mov     ax,[di].CSIPsav.AddrOff.HighWord ; (AX:DX) = client's EIP
        mov     dx,[di].CSIPsav.AddrOff.LowWord
        Pcall   PutRegister
        jc      kprerr

        mov     cx,KM_REG_CS
        mov     dx,[di].CSIPsav.AddrSeg ; (DX) = client's CS register
        Pcall   PutRegister
        jc      kprerr

        mov     cx,KM_REG_DS
        mov     dx,[di].DSsav           ; (DX) = client's DS register
        Pcall   PutRegister
        jc      kprerr

        mov     cx,KM_REG_ES
        mov     dx,[di].ESsav           ; (DX) = client's ES register
        Pcall   PutRegister
        jc      kprerr

        mov     cx,KM_REG_FS
        mov     dx,[di].FSsav           ; (DX) = client's FS register
        Pcall   PutRegister
        jc      kprerr

        mov     cx,KM_REG_GS
        mov     dx,[di].GSsav           ; (DX) = client's GS register
        Pcall   PutRegister
        jc      kprerr

        mov     cx,KM_REG_SS
        mov     dx,[di].SSsav           ; (DX) = client's SS register
        Pcall   PutRegister
        jc      kprerr

        mov     cx,KM_REG_EFLAG
        mov     ax,[di].EFsav.HighWord  ; (AX:DX) = client's eflags
        mov     dx,[di].EFsav.LowWord
        Pcall   PutRegister
        jc      kprerr

        xor     ax,ax  ; Return good RC
        jmp     short kprx
kprerr:
        mov     ax,1
kprx:
        RestoreReg <si,di,dx,cx,bx>
EndProc

;***    KDPGetNPX - get NPX state
;
;       ENTRY:  kgn_slot = slot whose NPX state is desired
;               kgn_pbuff = -> buffer to hold NPX state
;
;       EXIT:   (AX) = 0, No error
;               *kgn_pbuff = NPX state for thread
;
;               (AX) = 1, NPX is being emulated
;               (AX) = 2, invalid slot
;               (AX) = 3, thread has no NPX state (hasn't executed a FP inst)
;               (AX) = 4, NPX state is swapped out
;
;       USES:   AX
;
        assumeDATA <ds, es, ss>

DefProc KDPGetNPX, NEAR, PUBLIC, C

ArgVar  kgn_slot,    WORD            ; slot
ArgVar  kgn_pbuff,   WORD            ; -> NPX state savearea (out)
LocVar  kgn_size,    WORD            ; Number of bytes in NPX state savearea

BeginProc
        SaveReg <bx,ecx,si,di>
        push    es
        DosContext <es>,ax
        mov     ax,1               ; Assume error
        smsw    bx                 ; If we're emulating an NPX, return
        test    bx,CR0_EM          ;    an error
        jnz     short kgnx
        mov     di,[kgn_slot]      ; di = kgn_slot
        cmp     di,es:[NPX_Owner]  ; Is this the slot that owns the NPX?
        jne     short kgn05
        mov     di,[kgn_pbuff]     ; Yes
        clts                       ; Clear TS bit so fnsave/fnrstor work
        db      66h                ; Need 32-bit version of fnsave/fnrstor
        fnsave  [di]               ; Get state from NPX
        db      66h                ; Need 32-bit version of fnsave/fnrstor
        fnrstor [di]               ; (fnsave reinitializes NPX)
        lmsw    bx                 ; Restore TS bit
        xor     ax,ax              ; Return 0 RC
        jmp     short kgnx
kgn05:
        mov     ax,2               ; Assume error
        cmp     di,[MaxThreads]    ; if kgn_slot > MaxThreads
        ja      short kgnx         ;    kgn_slot is out of range
        shl     di,2               ; Convert kgn_slot to offset
        mov     bx,[papTCBPtrs]        ; bx = papTCBPtrs array
        mov     cx,es:[bx+di].HighWord ; ecx = papTCBPtrs[slot];
        shl     ecx,16
        mov     cx,es:[bx+di].LowWord
        jecxz   short kgnx             ; If TCB ptr is NULL, slot invalid
        FlatContext <es>,ax
        mov     ax,3               ; Assume error
        mov     ecx,es:[ecx].TCB_NPX_Buffer ; ecx = -> NPX buffer for thread
        jecxz   short kgnx         ; If -> NPX buffer is NULL, no NPX state
        mov     di,[kgn_pbuff]          ; di = kgn_pbuff
        mov     [kgn_size],NPX_CONTEXT_387
        lea     bx,[kgn_size]
        Ccall   KDPMemUtil,<ecx,bx,di>
        or      ax,ax
        mov     ax,4               ; Assume error
        jnz     short kgnx
        cmp     [kgn_size],NPX_CONTEXT_387
        jne     short kgnx
        xor     ax,ax                   ; Return good rc
kgnx:   pop     es
    assumeDATA  es
        RestoreReg <di,si,ecx,bx>
EndProc

;***    KDPPutNPX - put NPX state
;
;       ENTRY:  kpn_slot = slot whose NPX state is to be changed
;               kpn_pregs = -> buffer that holds NPX state
;
;       EXIT:   (AX) = 0, No error
;
;               (AX) != 0, invalid slot or something swapped out
;
;       USES:   AX
;
        assumeDATA <ds, es, ss>

DefProc KDPPutNPX, NEAR, PUBLIC, C

ArgVar  kpn_slot,    WORD            ; slot
ArgVar  kpn_pbuff,   WORD            ; -> NPX state savearea
LocVar  kpn_address, STRUC, AddrS    ; input to PutBuff routine

BeginProc
        SaveReg <bx,ecx,di,si>
        push    es
        DosContext <es>,ax
        mov     ax,1               ; Assume error
        smsw    bx                 ; If we're emulating an NPX, return
        test    bx,CR0_EM          ;    an error
        jnz     short kpnx
        mov     di,[kpn_slot]      ; di = kpn_slot
        cmp     di,es:[NPX_Owner]  ; Is this the slot that owns the NPX?
        jne     short kpn05
        mov     di,[kpn_pbuff]     ; Yes
        clts                       ; Clear TS bit so fnrstor works
        db      66h                ; Need 32-bit version of fnrstor
        fnrstor [di]               ; Restore NPX state
        lmsw    bx                 ; Restore TS bit
        xor     ax,ax              ; Return 0 RC
        jmp     short kpnx
kpn05:
        cmp     di,[MaxThreads]    ; if kgn_slot > MaxThreads
        ja      short kpnx         ;    kgn_slot is out of range
        shl     di,2               ; Convert kgn_slot to offset
        mov     bx,[papTCBPtrs]        ; bx = papTCBPtrs array
        mov     cx,es:[bx+di].HighWord ; ecx = papTCBPtrs[slot];
        shl     ecx,16
        mov     cx,es:[bx+di].LowWord
        jecxz   short kpnx             ; If TCB ptr is NULL, slot invalid
        FlatContext es
        mov     ecx,es:[ecx].TCB_NPX_Buffer ; ecx = -> NPX buffer for thread
        jecxz   short kpnx         ; If -> NPX buffer is NULL, no NPX state

        ; Initialize address structure and put its offset in BX
        mov     [kpn_address].AddrType,EXPR_TYPE_SEL
        mov     [kpn_address].AddrTask,0
        mov     [kpn_address].AddrSeg,es
        mov     [kpn_address].AddrSize,0  ; Size unimportant?
        mov     [kpn_address].AddrOff,ecx
        lea     bx,[kpn_address]
        mov     cx,NPX_CONTEXT_387
        mov     si,[kpn_pbuff]

        Pcall   PutBuff
        mov     ax,1
        jc      short kpnx
        xor     ax,ax                   ; Return good rc
kpnx:   pop     es
    assumeDATA  es
        RestoreReg <si,di,ecx,bx>
EndProc

ENDIF

CodeSegmentEnd

        end
