;       SCCSID = @(#)debcmd.asm 6.2 91/06/01
;      SCCSID = @(#)debcmd.asm  6.2 91/06/01
PAGE    ,132
NAME    DEBCMD
TITLE   DEBCMD.ASM - Debugger Commands
.286p

;***    DEBCMD.ASM
;
;       Routines to perform debugger commands.
;
;

.xlist
        include debseg.inc
        include debmac.inc
        include debequ.inc
        include debinit.inc
        include debdesc.inc
        include debsym.inc
.list

DataSegmentStart

Extern DefType,         WORD
Extern DebData,         WORD
Extern DumpAddr,        WORD
Extern DefMode,         BYTE
Extern EnterAddr,       WORD
Extern DisAddr,         WORD
Extern pTXT,            WORD
Extern pTXTstart,       WORD
Extern CmdInputBuffer,  BYTE
Extern RegSA,           WORD
Extern StackTop,        WORD
Extern fRegDisp,        BYTE
Extern Tcount,          BYTE
Extern MemCache,        BYTE
Extern DebStackFence,   WORD
Extern CommandScriptAddrS,      BYTE
Extern CommandScriptSize,       WORD

IFDEF   KDP
Extern fKDPPacketStop,  BYTE
Extern fKDPPacketMode,  BYTE
Extern fKDPRaw,         BYTE
ENDIF

DEB_OPT_CLEARBIT        equ     0       ; clear bit/print if clear
DEB_OPT_SETBIT          equ     1       ; set bit /print if set
DEB_OPT_TOGGLEBIT_CLEAR equ     2       ; toggle bit/print if clear
DEB_OPT_TOGGLEBIT_SET   equ     3       ; toggle bit/print if set
DEB_OPT_WORD            equ     4       ; word value

OptionTableS    struc
        OptionName      dw      ?
        OptionAddr      dw      ?
        OptionBitMask   dw      ?
        OptionType      db      ?
OptionTableS    ends

DebOptHelp      label   byte
        db      "386env - 32 bit environment (toggles)", CR, LF
        db      "dislwr - lower case display (toggles)", CR, LF
        db      "regterse - terse register set (toggles)", CR, LF
        db      0

Static  Env386Str,      STRING, "386env"
Static  DisLwrStr,      STRING, "dislwr"
Static  RegTerseStr,    STRING, "regterse"

OptionTable     label   word
        OptionTableS    <dataOFFSET Env386Str, dataOFFSET DebData.InitFlag, INIT_ENV386, DEB_OPT_TOGGLEBIT_SET>
        OptionTableS    <dataOFFSET DisLwrStr, dataOFFSET DebData.InitFlag, INIT_DISLWR, DEB_OPT_TOGGLEBIT_SET>
        OptionTableS    <dataOFFSET RegTerseStr, dataOFFSET DebData.InitFlag, INIT_NOREGTERSE, DEB_OPT_TOGGLEBIT_CLEAR>
        OptionTableS    <0>

Static bytebuf,         BUFLEN, 0       ; Table used by LIST

;
; "?" (help) command message
;
; This is a table of null-terminated strings terminated by
; a string of length zero.

HelpMsg label   byte
        db      "BC [<bp list> | *] - clear breakpoint(s)", 0
        db      "BD [<bp list> | *] - disable breakpoint(s)", 0
        db      "BE [<bp list> | *] - enable breakpoint(s)", 0
        db      "BL - list breakpoint(s)", 0
        db      "BP[<bp>] [<addr>] [<passcnt>] [""<bp cmds>""] - set a breakpoint", 0
        db      "BR[<bp>] E|W|R|I|1|2|4 [<addr>] [<passcnt>] [""<bp cmds>""] - set a debug register", 0 ; 148688
; Start of change DGE
        db      "BS - show time stamp entries",0
        db      "BT[<bp>] [<addr>] - set a time stamping break point",0
; End of change DGE
        db      "C <range> <addr> - compare bytes", 0
        db      "D [<range>] - dump memory", 0
        db      "DA [<range>] - dump asciiz string", 0
        db      "DB [<range>] - dump memory in bytes", 0
        db      "DW [<range>] - dump memory in words", 0
        db      "DD [<range>] - dump memory in dwords", 0
        db      "DG[A] [<range>] - dump GDT entries", 0
        db      "DI[A] [<range>] - dump IDT entries", 0
        db      "DL[A|P|S|H] [<range>] - dump LDT entries", 0
        db      "DP[A|D] [<range>] - dump page directory/table entries", 0
        db      "DT [<addr>] - dump TSS", 0
        db      "DX - dump loadall buffer", 0
        db      "E <addr> [<list>] - enter memory", 0
        db      "F <range> <list> - fill  memory", 0
        db      "G[S][T] [=<addr> [<addr> ...]] - go", 0
        db      "H <word> <word> - hexadd", 0
        db      "I <word> - input from port", 0
        db      "J <expr> [<cmds>] - execute <cmds> if <expr> is true (non-zero)", 0
        db      "K[S|B] [<SS:BPaddr>] [<CS:IPaddr>] - stack trace", 0
        db      "LG [<mapname>] - list groups in active maps", 0
        db      "LM [<mapname>] - list linked and active maps", 0
        db      "LN [<addr>] - list near symbols", 0
        db      "LA [<mapname>] - list absolute symbols in active maps", 0
        db      "LS [<addr>] - list symbols in group", 0
        db      "M <range> <addr> - move", 0
        db      "O <word> <byte> - output to port", 0
        db      "P[N|T] [=<addr>] [<word>] - program step", 0
        db      "R[T]|[<reg>] [[=] <word>] - register", 0
        db      "S <range> <list> - search", 0
        db      "T[A|C|N|S|T|X] [=<addr>] [<word>] [<addr>] - trace", 0
        db      "U [<range>] - unassemble", 0
        db      "VL[N|P|V|R|F] - list trap vectors intercepted", 0
        db      "VS[N|P|V|R|F] <byte> ... - intercept trap vector, ring 3 only", 0
        db      "VT[N|P|V|R|F] <byte> ... - intercept trap vector, all rings", 0
        db      "VC[N|P|V|R|F] <byte> ... - stop intercepting trap vector", 0
        db      "WA <mapname> | * - add a map to the active list", 0
        db      "WR <mapname> | * - remove a map from the active list", 0
        db      "Y[?] - display/modify debugger options", 0
        db      "Z - execute the default command", 0
        db      "ZL - list the default command", 0
        db      "ZS - set the default command", 0
        db      ". - external commands, execute "".?"" for help", 0
        db      "? - print help menu", 0
        db      "? <expr> | ""string"" - display expression or string", 0
        db      "<range> = [<addr>] [<word>] | <addr> [L <word>]", 0
        db      "<addr> = [& | #][<word>:]<word> | %<dword> | %%<dword>", 0
        db      "<list> = <byte> <byte> ... | ""string""", 0
        db      "<binary ops> = : | * / MOD + . - < > >= <= AND XOR OR && ||", 0
        db      "<unary ops> = &seg #sel %lin %%phy @ ! NOT SEG OFF ", 0
        db      "BY WO DW POI PORT WPORT", 0
        db      0

;***    MoreMsg
;
;       MoreMsg is the help screen prompt.  BlankMsg is used to
;       erase MoreMsg and depends upon being just after MoreMsg in the
;       data segment.

MoreMsg db      "--More--", 0
BlankMsg db     0Dh, (BlankMsg - MoreMsg - 1) dup(' '), 0Dh, 0

;***    ComandTable
;
;       Command letter table and routine address. perr prints error for
;       no such command.
;

        EVEN

CommandTable    label   byte
    CmdTable_s <"B", codeOFFSET BreakPoint>     ; in debbcmd.asm
    CmdTable_s <"C", codeOFFSET compare>        ; in debcmd.asm
    CmdTable_s <"D", codeOFFSET Dump>           ; in debdcmd.asm
    CmdTable_s <"E", codeOFFSET EnterBytes>     ; in debcmd.asm
    CmdTable_s <"F", codeOFFSET fill>           ; in debcmd.asm
    CmdTable_s <"G", codeOFFSET Go>             ; in debtrap.asm
    CmdTable_s <"H", codeOFFSET hexadd>         ; in debcmd.asm
    CmdTable_s <"I", codeOFFSET input>          ; in debcmd.asm
    CmdTable_s <"J", codeOFFSET ExecuteIfTrue>  ; in debbcmd.asm
    CmdTable_s <"K", codeOFFSET DumpStack>      ; in debcmd.asm
    CmdTable_s <"L", codeOFFSET Lists>          ; in debsym.asm
    CmdTable_s <"M", codeOFFSET move>           ; in debcmd.asm
    CmdTable_s <"O", codeOFFSET output>         ; in debcmd.asm
    CmdTable_s <"P", codeOFFSET Ptrace>         ; in debtrap.asm
    CmdTable_s <"R", codeOFFSET reg>            ; in debrcmd.asm
    CmdTable_s <"S", codeOFFSET search>         ; in debcmd.asm
    CmdTable_s <"T", codeOFFSET Trace>          ; in debtrap.asm
    CmdTable_s <"U", codeOFFSET unassem>        ; in debucmd.asm
    CmdTable_s <"W", codeOFFSET WithMap>        ; in debsym.asm
    CmdTable_s <"X", codeOFFSET PrintRebootMsg> ; in debcmd.asm
    CmdTable_s <"V", codeOFFSET Vector>         ; in debbcmd.asm
    CmdTable_s <"Y", codeOFFSET DebOptions>     ; in debcmd.asm
    CmdTable_s <"Z", codeOFFSET ChangeDefCmd>   ; in debbcmd.asm
    CmdTable_s <"?", codeOFFSET PrintHelpOrExp> ; in debcmd.asm
    CmdTable_s <".", codeOFFSET DoExtCmd>       ; in debcmd.asm
    CmdTable_s <0,   codeOFFSET perr>           ; end mark, error address

DataSegmentEnd

CodeSegmentStart

Extern BreakPoint,      NEAR
Extern Go,              NEAR
Extern Ptrace,          NEAR
Extern Trace,           NEAR
Extern unassem,         NEAR
Extern reg,             NEAR
Extern Dump,            NEAR
Extern Vector,          NEAR
Extern ChangeDefCmd,    NEAR
Extern ExecuteIfTrue,   NEAR
Extern Lists,           NEAR
Extern WithMap,         NEAR
Extern UpdateWinOS2Maps,NEAR
Extern GetDSaddr,       NEAR

IFDEF   KDP
Extern GetMem,          NEAR
ENDIF

;***    ErrBreak - print error message and break
;
;       ENTRY:  (AX) = error code
;               (BX) = pointer to AddrS
;
;       NOTE:   Can't not reset sp to StackTop because
;               the AddrS BX points maybe trashed (because
;               it is may be a local variable on the stack).
;
        ASSUME  DS:NOTHING, ES:NOTHING

Entry   ErrBreak, NEAR, PUBLIC
        Pcall   CommandReset            ; reset everything
   assumeDATA   <ds,es>
        Pcall   PrintMemErr
        jmp     CmdBreak

;***    RCmdBreak - do "R" command and then stop
;
;       Used to break debugger when default command
;       doesn't stop.
;
        ASSUME  DS:NOTHING, ES:NOTHING

Entry   RCmdBreak, NEAR, PUBLIC
        mov     sp,[StackTop]
        Pcall   CommandReset            ; reset everything
   assumeDATA   <ds,es>
        mov     Tcount,0
        mov     fRegDisp,1
        mov     di,dataOFFSET CmdInputBuffer
        mov     pTXT,di
        mov     pTXTstart,di
        mov     [CommandScriptSize],0

        IFDEF   KDP
        cmp     fKDPPacketStop,FALSE    ; if (fKDPPacketStop) {
        je      rcb1
        mov     fKDPPacketStop,FALSE    ;  fKDPPacketStop = FALSE;
        mov     al,KDP_ASYNC_TRAP       ;  event = Async. Stop
        jmp     KDPEvent                ;  goto event handler
rcb1:                                   ; } else
        ENDIF

        mov     ax,"R"
        stosw
        jmp     command                 ; if ctrlc stop

;***    perr
;
;       Command error. [pTXT] has been incremented beyond the
;       command letter so it must decremented for the
;       error pointer to work.
;
        ASSUME  DS:NOTHING, ES:NOTHING

Entry   perr,   NEAR, PUBLIC
        mov     sp,[StackTop]
        Pcall   CommandReset            ; reset everything
   assumeDATA   <ds,es>
        dec     pTXT

;***    perror
;
;       Syntax error. [pTXT] points to character in the input buffer
;       which caused error. By subtracting from start of buffer,
;       we will know how far to tab over to appear directly below
;       it on the terminal. Then print "^ Error".
;
        ASSUME  DS:NOTHING, ES:NOTHING

Entry   perror, NEAR, PUBLIC
        mov     sp,[StackTop]
        Pcall   CommandReset            ; reset everything
   assumeDATA   <ds,es>
        mov     dx,pTXTstart            ; print line
        Pcall   rprbuf                  ;
        Pcall   crlf

;       tab over to bad character and print "^ Error" pointing at it.
;       (DX) = address of start of command line buffer

per2:   mov     cx,pTXT
        sub     cx,dx                   ; how many char so far
        jz      per3
        Pcall   tab                     ; directly below bad char
per3:   putstr  "^ Error"

;***    CmdBreak
;
;       Break from command flow, remove any commands in
;       line buffer and start command loop.
;
        ASSUME  DS:NOTHING, ES:NOTHING

Entry   CmdBreak, NEAR, PUBLIC
        mov     sp,[StackTop]
        Pcall   CommandReset            ; reset everything
   assumeDATA   <ds,es>
        mov     di,dataOFFSET CmdInputBuffer
        mov     pTXT,di
        mov     pTXTstart,di
        mov     [CommandScriptSize],0

        IFDEF   KDP
        cmp     fKDPPacketStop,FALSE    ; if (fKDPPacketStop) {
        je      cbr1
        mov     fKDPPacketStop,FALSE    ;  fKDPPacketStop = FALSE;
        mov     al,KDP_ASYNC_TRAP       ;  event = Async. Stop
        jmp     KDPEvent                ;  goto event handler
cbr1:                                   ; } else
        ENDIF

        Pcall   crlf
        xor     al,al
        stosb

;***    command
;
;       Command interpreter entry point.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
        ASSUME  DS:NOTHING, ES:NOTHING

DefProc command, NEAR, PUBLIC
        mov     sp,[StackTop]
        Pcall   CommandReset            ; reset everything
   assumeDATA   <ds,es>
        Pcall   DoCommandSub            ; do the command line
        cmp     [CommandScriptSize],0
        jne     short cmd30
        Pcall   WhatIsClientMode
        mov     al,PROMPT               ; (AL) = real-mode prompt
        jz      short cmd20
        mov     al,PPROMPT              ; (AL) = prot-mode prompt
        jc      short cmd10
        mov     al,VPROMPT              ; (AL) = v86-mode prompt
cmd10:  test    [RegSA].CR0sav.HighWord,CR0HI_PG
        jz      short cmd20
        Pcall   putchar
cmd20:  Pcall   putchar
        mov     fRegDisp,1              ; Print the full "R" command now
        Pcall   inbuf                   ; Get command line
        jmp     short command           ; Get next command

cmd30:  Pcall   GetCommandScriptLine
        jmp     short command           ; Get next command
EndProc NoGen

;***    DoCommand
;
;       Save old text pointer, do a command and return (if no errors).
;
;       ENTRY:  SI = pointer to command line string (null terminated).
;
;       EXIT:   NONE
;
        assumeDATA <ds, es, ss>

DefProc DoCommand, NEAR, PUBLIC
        SaveReg <pTXT,pTXTstart>        ; save old command line pointers
        mov     pTXTstart,si            ; put pointer to len byte in pTXTstart
        mov     pTXT,si                 ; put pointer to buffer in pTXT
        Pcall   DoCommandSub
        RestReg <pTXTstart,pTXT>        ; restore old text pointers
EndProc

;***    DoCommandSub
;
;       Do a command and return (if no errors).
;
;       ENTRY:  pTXT, pTXTstart set up.
;
;       EXIT:   NONE
;
        assumeDATA <ds, es, ss>

DefProc DoCommandSub, NEAR, LOCAL
        push    bp
        Pcall   UpdateWinOS2Maps
        mov     si,pTXT
        Pcall   StrUpr                  ; upper case command string

;       From now and throughout command line processing, [pTXT] points
;       to next character in command line to be processed.

dcd1:   Pcall   CommandReset            ; reset everything
   assumeDATA   <ds,es>
        cmp     DebStackFence,STACK_FENCE_VALUE
        je      dcd2
        mov     DebStackFence,STACK_FENCE_VALUE
        putstr  <"Warning! Debugger stack overflow", CR, LF>

dcd2:   Pcall   GetChrI
        jnz     dcd3                    ; jump if not end of command

        or      al,al
        je      dcd4                    ; jump if no more commands on this line

;       Must be a ";", so skip it (GetChrI won't skip a ";").

        inc     pTXT
        jmp     short dcd2

;       AL = command letter

dcd3:   mov     si,dataOFFSET CommandTable
        Pcall   ExecSubr
        Pcall   GetEOL
        jmp     short dcd1

dcd4:   pop     bp
EndProc

;***    GetCommandScriptLine
;
;       Gets one command line from the command line script block.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetCommandScriptLine, NEAR, LOCAL
        mov     bx,dataOFFSET CommandScriptAddrS
        mov     di,dataOFFSET CmdInputBuffer
        mov     cx,(CMDLINELEN - 1)     ; save one byte for null
gcs10:  cmp     [CommandScriptSize],0
        je      short gcsx
        Pcall   GetByte                 ; (AL) = byte from command line
        jc      short gcse
        Pcall   AddAddr, <1, 0>
        dec     [CommandScriptSize]
        cmp     al,CR
        jz      short gcs10             ; skip CR's
        or      al,al
        jz      short gcsx              ; jump if end of buffer
        cmp     al,LF
        jz      short gcsx              ; jump if end of line
        stosb                           ; save in CmdInputBuffer
        loop    gcs10                   ; loop if more characters

gcsx:   xor     al,al
        stosb
        mov     si,dataOFFSET CmdInputBuffer
        Pcall   StrUpr
        mov     pTXT,dataOFFSET CmdInputBuffer
        mov     pTXTstart,dataOFFSET CmdInputBuffer
EndProc
gcse:   jmp     ErrBreak

;***    ExecSubr - execute subroutine
;
;       Executes a subroutine it from the table when the
;       character matches the in table entry. If character is
;       invalid, it will jump to the address after end of table
;       marker (0).  Table must be terminated with a zero the
;       character field. Table format:
;
;               db      character
;               dw      routine address
;               ...
;               db      0               ; end of table
;               dw      error routine address
;
;       ENTRY:  AL = character to find in table.
;
;               DS:SI = pointer to table of characters followed by a
;               routine address to be executed if the characters.
;
;       EXIT:   NONE
;
;       USES:   AX, SI, Flags
;

DefProc ExecSubr, NEAR, PUBLIC
        mov     ah,al
es1:    lodsb
        .errnz  (offset cmd_char)
        or      al,al
        jz      es2
        cmp     al,ah
        je      es2
        add     si,size CmdTable_s - 1
        jmp     es1

es2:    lodsw
        .errnz  (offset cmd_proc - 1)
        call    ax
EndProc

IFDEF   KDP
;***    KDPEvent
;
;       Packet mode entry point
;
;       ENTRY:  al = Event type (Trap Number 0-e or KDP_NOEVENT)
;
;       EXIT:   NONE
;
        ASSUME  DS:NOTHING, ES:NOTHING

DefProc KDPEvent, NEAR, PUBLIC
        mov     sp,[StackTop]
        Pcall   CommandReset            ; reset everything
    assumeDATA  <ds, es>
        mov     fKDPPacketMode,TRUE     ; set packet mode
        mov     fKDPRaw,FALSE           ; clear raw command
        and     ax,00ffh                ; event type is in al
        cmp     al,KDP_NOEVENT
        je      kev1
        Ccall   KDPEventNotify, <ax>
kev1:   Ccall   KDPMain                 ; returns on ASCII input
        mov     fKDPPacketMode,FALSE
        jmp     CmdBreak
EndProc NoGen

;***    KDPRaw
;
;       Handles entry and re-entry for executing raw commands.
;       If we're already in raw mode, return.  Otherwise, jump
;       to the main command loop.
;
        assumeDATA <ds, es, ss>

DefProc KDPRaw, NEAR, PUBLIC, C
        cmp     fKDPRaw,FALSE           ; if (fKDPRaw == FALSE)
        jne     krwx                    ; {
        mov     fKDPRaw,TRUE            ;  fKDPRaw = TRUE;
        mov     pTXT,dataOFFSET CmdInputBuffer      ; JWE
        mov     pTXTstart,dataOFFSET CmdInputBuffer ; JWE
        jmp     command                 ;  goto command loop
                                        ; }
krwx:
EndProc

;***    KDPReadMem
;
        assumeDATA <ds, es, ss>

DefProc KDPReadMem, NEAR, PUBLIC, C

ArgVar  Addr,   WORD
ArgVar  Dest,   WORD
ArgVar  pCount, WORD

BeginProc
        push    bx
        push    cx
        push    di
        mov     bx,pCount
        mov     cx,[bx]
        mov     bx,Addr
        mov     di,Dest
        Pcall   GetMem
        jc      krm1                    ; if !(error)
        xor     ax,ax                   ;  return 0
krm1:
        mov     bx,pCount
        mov     [bx],cx                 ; store number of bytes read
        pop     di
        pop     cx
        pop     bx
krmx:
EndProc

;***    KDPWriteMem
;
        assumeDATA <ds, es, ss>

DefProc KDPWriteMem, NEAR, PUBLIC, C

ArgVar  Src,    WORD
ArgVar  Addr,   WORD
ArgVar  Count,  WORD

BeginProc
        push    bx
        push    cx
        push    si
        mov     bx,Addr
        mov     cx,Count
        mov     si,Src
        Pcall   PutBuff
        pop     si
        pop     cx
        pop     bx
        jc      kwmx
        xor     ax,ax
kwmx:
EndProc

;***    KDPGetAccAtr
;
;       Entry:  Addr = pointer to address structure
;
;       Exit:   If selector address
;                   AH = selector access byte
;                        (D_SEG should be set)
;                   AL = selector attribute byte
;                        (D_COPER32 may or may not be set)
;
;               Else if segment address
;                   AH = 0
;                   AL = 0
;
;               Else // default 386 environment
;                   AH = 0
;                   AL = D_COPER32
;
        assumeDATA <ds, es, ss>

DefProc KDPGetAccAtr, NEAR, PUBLIC, C

ArgVar  Addr,   WORD

BeginProc
        SaveReg <bx,si,di>
        mov     bx,[Addr]
        Pcall   GetAddrInfo             ; (BX) = attribute/access byte
        mov     al,bh
        mov     ah,bl                   ; (AX) = access/attribute bytes
        jnc     kgax

        mov     bx,[Addr]
        Pcall   GetAddrType             ; (AL) = address type
        cmp     al,EXPR_TYPE_SEG
        mov     ax,0
        jz      short kgax              ; assume D bit clear for seg address

        mov     ax,D_COPER32            ; assume D bit set for 386 env
kgax:   RestReg <di,si,bx>
EndProc
ENDIF

;***    CommandReset - does every to reset for a new command
;
;       ENTRY   NONE
;
;       EXIT    DS, ES reinitialized
;               direction flag clear
;               forced lower case off
;
;       USES    DS, ES, Flags
;
        ASSUME  DS:NOTHING, ES:NOTHING

DefProc CommandReset, NEAR, LOCAL
        cld                             ; clear direction flags
        DataContext <ds,es>             ; reset segment register
        xor     bp,bp                   ; clear backlink
        Pcall   LowerCaseOff            ; case sensitive
EndProc

;***    PrintHelpOrExp
;
;       Prints the help message if no arguments.
;
        assumeDATA <ds, es, ss>

DefProc PrintHelpOrExp, NEAR, LOCAL

LocVar  TmpExpr, <size ExprS>
LocVar  TmpExpr2, <size ExprS>

BeginProc
        Pcall   ScanP
        jnz     short phe1
        mov     di,dataOFFSET HelpMsg           ; (DI) = string offset
        mov     cx,23                           ; (CX) = Lines per screen
hlp0:   push    cx
        mov     dx,di                           ; (DX) = string offset
        xor     al,al
        mov     cx,0FFFFh
        repne scasb                             ; Find next string
        cmp     cx,0FFFEh                       ; Previous string length 0?
        pop     cx
        je      hlp2                            ;  yes, done
        dec     cx                              ; Room for line on screen?
        jns     short hlp1                      ;  yes, skip ahead
        push    dx
        mov     dx,dataOFFSET MoreMsg
        Pcall   rprbuf                          ; Prompt user
        IFDEF   KDP
        cmp     fKDPRaw,0               ; if (raw command)
        je      hlpje1
        SaveReg <bx,cx,dx>
        Ccall   KDPCharInput            ;  KDPRawInput();
        RestReg <dx,cx,bx>
        jmp     hlpje2
hlpje1:                                   ; else
        ENDIF
        Pcall   waitchr                         ; (AL) = response
hlpje2:
        mov     dx,dataOFFSET BlankMsg
        Pcall   rprbuf                          ; Erase prompt
        pop     dx
        cmp     al,'q'                          ; Quit help?
        je      short hlp2                      ;  yes, done
        mov     cx,22                           ; Assume full screen wanted
        cmp     al,0Dh                          ; One line wanted (<enter>)?
        jne     short hlp1                      ;  no, loop
        xor     cx,cx                           ; Do one line only
hlp1:   Pcall   rprbuf                          ; Print the line
        Pcall   crlf
        jmp     short hlp0                      ; Look for more
hlp2:   jmp     phe11

phe1:   Pcall   CheckString             ; check if output string
        jne     short phe2
        mov     di,dataOFFSET bytebuf
        Pcall   ParseString
        mov     dx,dataOFFSET bytebuf
        Pcall   rprbuf
        Pcall   crlf
        jmp     phe10

phe2:   lea     si,TmpExpr              ; (SI) = pointer to ExprS struc
        Pcall   EvalExpr

        cmp     TmpExpr.ExprType,EXPR_TYPE_DATA
        je      short phe3
        jmp     phe7

phe3:   xor     bh,bh
        mov     bl,TmpExpr.ExprSize     ; (BX) = expresion size
        shl     bx,1                    ; (BX) = number of hex digits

        mov     dx,TmpExpr.ExprData.LowWord
        mov     si,TmpExpr.ExprData.HighWord    ; (SI:DX) = data word

        Pcall   OutRadix, <dx, si, 16, bx, "0">
        mov     al,"H"
        Pcall   putchar

        Pcall   blank
        Pcall   OutRadix, <dx, si, 10, 0, 0>
        mov     al,"T"
        Pcall   putchar
        Pcall   blank
        Pcall   OutRadix, <dx, si, 8, 0, 0>
        mov     al,"Q"
        Pcall   putchar
        Pcall   blank
        shl     bx,2                    ; convert to number of binary digits
        Pcall   OutRadix, <dx, si, 2, bx, "0">
        mov     al,"Y"
        Pcall   putchar
        Pcall   blank
        mov     al,"'"
        Pcall   putchar
        mov     al,dl                   ; get low byte of data
        cmp     al," "
        jb      short phe4
        cmp     al,7Fh
        jb      short phe5
phe4:   mov     al,"."

phe5:   Pcall   putchar
        mov     al,"'"
        Pcall   putchar

        lea     si,TmpExpr
        Pcall   GetExprS
        Pcall   IsTrue
        jnc     short phe6              ; jump if false
        putstr  <" TRUE">
        jmp     short phe10

phe6:   putstr  <" FALSE">
        jmp     short phe10

phe7:   lea     si,TmpExpr              ; (AX) = pointer to ExprS/AddrS struc
        cmp     [si].AddrTask,0
        jne     short phe8
        Pcall   GetCurrTask             ; (AX) = current task number
        mov     [si].AddrTask,ax
phe8:   Pcall   OutAddrS, <si>

        cmp     TmpExpr.AddrType,EXPR_TYPE_LIN
        je      phe9                    ; don't print if already linear

        lea     bx,TmpExpr              ; (BX) = pointer to AddrS struc
        mov     cx,1
        Pcall   AddrToLin
        jc      phe9

        Pcall   blank
        mov     TmpExpr2.AddrOff.LowWord,dx
        mov     TmpExpr2.AddrOff.HighWord,ax
        mov     TmpExpr2.AddrType,EXPR_TYPE_LIN
        lea     si,TmpExpr2             ; (SI) = pointer to ExprS/AddrS struc
        Pcall   OutAddrS, <si>

phe9:   test    DebData.InitFlag,INIT_ENV386
        jz      phe10                   ; don't print in a 286 environment

        cmp     TmpExpr.AddrType,EXPR_TYPE_PHY
        je      phe10                   ; don't print if already physical

        lea     bx,TmpExpr              ; (BX) = pointer to AddrS struc
        mov     cx,1
        Pcall   AddrToPhy
        jc      phe10

        Pcall   blank
        mov     TmpExpr2.AddrOff.LowWord,dx
        mov     TmpExpr2.AddrOff.HighWord,ax
        mov     TmpExpr2.AddrType,EXPR_TYPE_PHY
        lea     si,TmpExpr2             ; (SI) = pointer to ExprS/AddrS struc
        Pcall   OutAddrS, <si>
phe10:  Pcall   crlf
phe11:
EndProc

;***    hexadd
;
;       Hex addition and subtraction
;

DefProc hexadd, NEAR, LOCAL
        Pcall   EvalWord
        mov     di,dx
        Pcall   EvalWord
        Pcall   GetEOL
        mov     si,dx
        add     dx,di
        mov     al,'+'
        Pcall   putchar
        Pcall       out16
        Pcall   blank
        Pcall   blank
        mov     dx,di
        sub     dx,si
        mov     al,'-'
        Pcall   putchar
        Pcall   out16
        Pcall   blank
        Pcall   blank
        mov     ax,di
        mul     si
        xchg    ax,dx
        push    ax
        mov     al,'*'
        Pcall   putchar
        Pcall   out16
        Pcall   blank
        pop     dx
        Pcall   out16
        Pcall   blank
        Pcall   blank
        mov     ax,0FFFFh
        mov     dx,ax
        or      si,si
        jz      short hexa1
        mov     ax,di
        xor     dx,dx
        div     si
        xchg    ax,dx
hexa1:  push    ax
        mov     al,'/'
        Pcall   putchar
        Pcall   out16
        Pcall   blank
        pop     dx
        call    out16
        Pcall   crlf
EndProc

;***    move
;
;       Block move one area of memory to another. Overlapping moves
;       are performed correctly, i.e., so that a source byte is not
;       overwritten until after it has been moved.
;

DefProc move, NEAR, LOCAL

LocVar  SrcAddr,   <size AddrS>         ; source address
LocVar  DesAddr,   <size AddrS>         ; destination address
LocVar  Len,       WORD                 ; number of bytes to move
LocVar  Dir,       BYTE                 ; direction flag

BeginProc
        lea     bx,SrcAddr
        mov     si,bx
        Pcall   GetDSaddr
        Pcall   EvalRange, <bx, bx, 128>
        Pcall   CheckAddr, <bx>
        jcxz    mv1                     ; don't do anything if 0 bytes to move

        mov     Len,cx                  ; Save length
        Pcall   ScanP
        jnz     short mv2
        jmp     perror                  ; error if end of line

mv1:    jmp     mv10

mv2:    lea     bx,DesAddr
        mov     di,bx
        Pcall   GetDSaddr
        Pcall   EvalAddr, <bx, bx>
        Pcall   CheckAddr, <bx>
        Pcall   GetEOL                  ; Check for errors

;       Compare addresses as physical addresses to determine direction of move

        Pcall   CompareAddr, <si, di>
        jc      short mv8               ; Exit if error in addresses

        mov     Dir,0                   ; clear direction flag, 0 = forward
        cmp     ax,1                    ; If (AX) == 1, SI > DI, move forward
        je      short mv5               ; jump if move backwards, SI <= DI
        jc      short mv5               ; If (AX) was zero, go with (Dir) = 0

;       Move backward if src < dest. Figure end of source and destination
;       areas and flip direction flag.

        mov     cx,Len
        cmp     cx,MEMCACHESIZE
        jbe     short mv4

;       (SI) = SrcAddr
;       (DI) = DesAddr
;       (CX) = Len

        sub     cx,MEMCACHESIZE
        mov     bx,si
        Pcall   AddAddr, <cx, 0>
        mov     bx,di
        Pcall   AddAddr, <cx, 0>
mv4:    mov     Dir,-1

;       (Dir) = direction flag, 0 = forward, !0 = backword
;       (Len) = number of bytes
;       (SrcAddr) = source AddrS
;       (DesAddr) = destination AddrS

mv5:    mov     dx,Len
        lea     bx,SrcAddr              ; (BX) = AddrS struc pointer
        Pcall   GetCache                ; (DI) = buffer cache, (CX) = num bytes
        jc      short mv8               ; (DX) = num bytes left
        mov     Len,dx

        lea     bx,DesAddr              ; (BX) = AddrS struc pointer
        mov     si,di                   ; (SI) = cache memory
        Pcall   PutBuff
        jc      short mv8

        or      dx,dx
        jz      short mv10              ; jump if no more bytes

        cmp     Dir,0
        je      short mv9               ; jump if move forward

        cmp     cx,Len
        jbe     short mv7
        mov     cx,Len
mv7:    neg     cx

        Pcall   AddAddr, <cx, -1>
        lea     bx,SrcAddr
        Pcall   AddAddr, <cx, -1>
        jmp     short mv5

mv8:    jmp     ErrBreak

mv9:    Pcall   AddAddr, <cx, 0>
        lea     bx,SrcAddr
        Pcall   AddAddr, <cx, 0>
        jmp     short mv5
mv10:
EndProc

;***    fill
;
;       Fill an area of memory with a list values. If the list
;       is bigger than the area, don't use the whole list. If the
;       list is smaller, repeat it as many times as necessary.
;

DefProc fill, NEAR, LOCAL

LocVar  Addr,    <size AddrS>           ; fill address
LocVar  ListLen, WORD                   ; number of bytes in list
LocVar  Len,     WORD                   ; number of bytes to fill

BeginProc
        lea     bx,Addr
        Pcall   GetDSaddr
        Pcall   EvalRange, <bx, bx, 128>
        Pcall   CheckAddr, <bx>
        jcxz    short fll4              ; don't do anything if 0 bytes to fill

        mov     Len,cx                  ; Save length
        Pcall   list                    ; Get list of values to fill with
        mov     ListLen,bx
        mov     cx,bx                   ; (CX) = list length

fll1:   cmp     cx,Len
        jbe     short fll2              ; jump if (CX) <= Len
        mov     cx,Len
fll2:   sub     Len,cx
        jcxz    short fll4

        lea     bx,Addr
        mov     si,dataOFFSET bytebuf   ; List is in byte buffer
        Pcall   PutBuff
        jc      short fll3
        Pcall   AddAddr, <cx, 0>
        jmp     short fll1

fll3:   jmp     ErrBreak
fll4:

EndProc

;***    search
;
;       Search a specified area of memory for given list of bytes.
;       Print address of first byte of each match.
;

DefProc search, NEAR, LOCAL

LocVar  Addr,    <size AddrS>
LocVar  ListLen, WORD

BeginProc
        lea     bx,Addr
        Pcall   GetDSaddr
        Pcall   EvalRange, <bx, bx, 128>
        Pcall   CheckAddr, <bx>
        jcxz    short scn7

        push    cx                      ; save search region length
        Pcall   list                    ; (BX) = list item length
        pop     dx                      ; (DX) = search region length

        dec     bx                      ; num of bytes in list - 1
        mov     ListLen,bx
        lea     bx,Addr

;       (BX) = pointer to AddrS struc
;       (DX) = current total number of bytes left

scn1:   cmp     dx,ListLen
        jbe        scn7                    ; if bytes left < list len, no match

        Pcall   GetCache                ; (DI) = buffer cache, (CX) = num bytes
        jc      short scn4              ; (DX) = num bytes left
        mov     si,cx

;       (BX) = pointer to AddrS struc
;       (CX) = number of bytes left in cache
;       (DX) = total bytes left
;       (SI) = number of bytes in cache
;       (DI) = pointer to cache

scn2:   jcxz    short scn6
        mov     al,byte ptr bytebuf
        repne   scasb
        jne     short scn6

        cmp     ListLen,0               ; if listLen == 0, one byte match
        je      scn3

        cmp     cx,ListLen
        jb      scn5                    ; jump if num of cach byte < list len

        SaveReg <cx, si, di>
        mov     si,dataOFFSET bytebuf+1
        mov     cx,ListLen
        repe    cmpsb
        RestReg <di, si, cx>
        jne     short scn2

scn3:   mov     ax,si
        sub     ax,cx
        dec     ax                      ; (AX) = number of bytes searched
        Pcall   OutAddrOff, <bx, ax, 0>
        Pcall   crlf
        jmp      short scn2

scn4:   jmp     ErrBreak

scn5:   inc     cx                        ; (CX) = number of bytes really left
        add     dx,cx                   ; add the bytes left back into len
        sub     si,cx                   ; (SI) = number of bytes searched

scn6:   Pcall   AddAddr, <si, 0>
        or      dx,dx
        jnz     short scn1              ; jump if no more bytes
scn7:
EndProc


;***    EnterBytes
;
;       Enter values into memory at a specified address. If the
;       line contains nothing but the address we go into "enter
;       mode", where the address and its current value are printed
;       and the user may change it if desired. To change, type in
;       new value in hex. Backspace works to correct errors. If
;       an illegal hex digit or too many digits are typed, the
;       bell is sounded but it is otherwise ignored. To go to the
;       next byte (with or without change), hit space bar. To
;       back up to a previous address, type "-". On
;       every 8-byte boundary a new line is started and the address
;       is printed. To terminate command, type carriage return.
;         Alternatively, the list of bytes to be entered may be
;       included on the original command line immediately following
;       the address. This is in regular LIST format so any number
;       of hex values or strings in quotes may be entered.
;

DefProc EnterBytes, NEAR, LOCAL
        mov     bx,dataOFFSET EnterAddr
        Pcall   EvalAddr, <bx, bx>
        Pcall   CheckAddr, <bx>
        Pcall   ScanP                   ; Any more parameters?
        jz      short etm1              ; If not end-of-line get list
        jmp     etm14

etm1:   Pcall   OutAddrS, <bx>
        Pcall   blank                   ; Leave a space
        Pcall   blank

etm2:   Pcall   GetByte                 ; Get current value
        jc      short scn4              ; jump to error
        Pcall   hex                     ; And display it

etm3:   mov     al,"."
        Pcall   putchar                 ; Prompt for new value
        mov     cx,2                    ; Max of 2 digits in new value
        mov     dx,0                    ; Intial new value

etm4:   Pcall   GetEchoChr              ; Get digit from user
        mov     ah,al                   ; Save
        Pcall   HexChk                  ; Hex digit?
        xchg    ah,al                   ; Need original for echo
        jc      short etm6              ; If not, try special command
        mov     dh,dl                   ; Rotate new value
        mov     dl,ah                   ; And include new digit
        loop    etm4                    ; At most 2 digits

;       We have two digits, so all we will accept now is a command.

etm5:   Pcall   GetEchoChr              ; Get command character

etm6:   cmp     al,BS                   ; Backspace
        je      short etm8
        cmp     al,DEL                  ; Rub out
        je      short etm7
        cmp     al,"-"                  ; Back to previous address
        je      short etm11
        cmp     al,CR                   ; All done with command?
        je      short etm12
        cmp     al," "                  ; Go to next address
        je      short etm9
        mov     al,BS
        Pcall   putchar                 ; Back over illegal character
        Pcall   backup
        jcxz    short etm5
        jmp     short etm4

etm7:   mov     al,BS
        Pcall   putchar

etm8:   cmp     cl,2                    ; CX=2 means nothing typed yet
        jz      short etm3              ; Put back the dot we backed over
        inc     cl                      ; Accept one more character
        mov     dl,dh                   ; Rotate out last digit
        mov     dh,ch                   ; Zero this digit
        Pcall   backup                  ; Physical backspace
        jmp     short etm4              ; Get more digits
etm9:
        call    store                   ; Enter new value
        inc     cx                      ; Leave a space plus two for
        inc     cx                      ;  each digit not entered
        Pcall   tab
        mov     ax,EnterAddr.AddrOff.LowWord ; Next memory address
        and     al,7                    ; Check for 8-byte boundary
        jz      short etm10
        jmp     etm2                    ; Take 8 per line

etm10:  Pcall   crlf                    ; Terminate line
        jmp     etm1                    ; Print address on new line

etm11:  Pcall   store                   ; Enter the new value

;       EnterAddr has been bumped to next byte. Drop it 2 to go to
;       previous address.

        Pcall   AddAddr, <-2, -1>
        jmp     short etm10             ; Terminate line after backing

etm12:  Pcall   store                   ; Enter the new value
        Pcall   crlf                    ; CR/LF and terminate
        jmp     short etm15

etm13:  jmp     ErrBreak

etm14:  Pcall   list                    ; Get the bytes to enter
        mov     cx,bx                   ; Count of bytes
        mov     si,dataOFFSET bytebuf   ; List of bytes is in byte Buffer
        mov     bx,dataOFFSET EnterAddr
        Pcall   PutBuff                 ; Enter list of bytes
        jc      etm13
etm15:

EndProc

;***    store
;
;       If new value has been entered, convert it to binary and
;       put into memory. Always bump pointer to next location.
;

DefProc store, NEAR, LOCAL
        mov     bx,dataOFFSET EnterAddr
        cmp     cl,2                    ; CX=2 means nothing typed yet
        jz      short sto1              ; So no new value to store

;       Rotate DH left 4 bits to combine with DL and make a byte value

        shl     dh,4
        or      dl,dh                   ; Hex is now converted to binary
        mov     ax,dx
        Pcall   PutByte
sto1:   Pcall   AddAddr, <1, 0>

EndProc

;***    list
;
;       Get a byte list for _ENTER, FILL or SEARCH. Accepts any number
;       of 2-digit hex values or character strings in either single
;       (') or double (") quotes.
;

DefProc list, NEAR, LOCAL
        mov     bx,dataOFFSET bytebuf   ; Put byte list in the byte buffer
lst1:   Pcall   listitem                ; Process a parameter
        jnc     short lst1              ; If OK, try for more
        sub     bx,dataOFFSET bytebuf   ; BX now has no. of bytes in list
        jnz     short lst2              ; List must not be empty
        jmp     perror

lst2:   Pcall   GetEOL

EndProc

;***    listitem
;
;       Process one parameter when a list of bytes is
;       required. Carry set if parameter bad. Called by LIST.
;
;       ENTRY:  BX = pointer to list byte buffer.
;
;       EXIT:   Carry set if error.
;
;       USES:   AX, BX, SI, FLAGS
;

DefProc listitem, NEAR, LOCAL
        Pcall   ScanP                   ; Scan to parameter
        push    ax
        Pcall   HexChk                  ; Is it in hex?
        pop     ax
        jc      short lsi1              ; If not, could be a string

        Pcall   EvalByte                ; Get the byte value
        mov     [bx],dl                 ; Add to list
        inc     bx
        jmp     short lsi2

lsi1:   Pcall   CheckString             ; String?
        jne     short lsi3              ; Not string, not hex - bad
        mov     di,bx                   ; (DI) = buffer address
        Pcall   ParseString
        mov     bx,di                   ; (BX) = end of string
        jc      short lsi3

lsi2:   clc                             ; Parameter was OK
        jmp     short lsi4
lsi3:   stc

lsi4:

EndProc

;***    input
;
;       Input from the specified port and display result
;

DefProc input, NEAR, LOCAL
        Pcall   EvalWord                ; Get port number in DX
        Pcall   GetEOL
        in      al,dx                   ; Variable port input
        Pcall   hex                     ; And display
        Pcall   crlf
EndProc

;***    output
;
;       Output a value to specified port.
;

DefProc output, NEAR, LOCAL
        Pcall   EvalWord                ; Get port number
        push    dx                      ; Save while we get data
        Pcall   EvalByte                ; Get data to output
        Pcall   GetEOL
        xchg    ax,dx                   ; Output data in AL
        pop     dx                      ; Port in DX
        out     dx,al                   ; Variable port output
EndProc

;***    compare
;
;       Compare memory command.
;

DefProc compare, NEAR, LOCAL

LocVar  SrcAddr,  <size AddrS>
LocVar  DesAddr,  <size AddrS>
LocVar  Len,      WORD

BeginProc
        lea     bx,SrcAddr
        Pcall   GetDSaddr
        Pcall   EvalRange, <bx, bx, 128>
        Pcall   CheckAddr, <bx>
        jcxz    cmp1                    ; don't do anything if 0 bytes to comp

        mov     Len,cx                  ; Save length
        Pcall   ScanP
        jnz     short cmp3
        jmp     perror                  ; error if end of line

cmp1:   jmp     cmpx

cmp2:   jmp     ErrBreak

cmp3:   lea     bx,DesAddr
        Pcall   GetDSaddr
        Pcall   EvalAddr, <bx, bx>
        Pcall   CheckAddr, <bx>
        Pcall   GetEOL

cmp4:   mov     cx,Len
        jcxz    short cmp1

        cmp     cx,(MEMCACHESIZE/2)
        jbe     short cmp5
        mov     cx,(MEMCACHESIZE/2)
cmp5:   sub     Len,cx

        mov     di,dataOFFSET MemCache
        lea     bx,SrcAddr
        Pcall   GetBuff
        jc      cmp2

        mov     di,dataOFFSET MemCache + (MEMCACHESIZE/2)
        lea     bx,DesAddr
        Pcall   GetBuff
        jc      cmp2
        mov     si,dataOFFSET MemCache
        push    cx

cmp6:   jcxz    short cmp7
        repe    cmpsb
        je      short cmp7

        mov     ax,si
        sub     ax,dataOFFSET MemCache + 1

        lea     bx,SrcAddr
        Pcall   OutAddrOff, <bx, ax, 0>

        push    ax
        Pcall   blank
        mov     al,[si-1]
        Pcall   hex
        Pcall   blank
        Pcall   blank
        mov     al,[di-1]
        Pcall   hex
        Pcall   blank
        pop     ax

        lea     bx,DesAddr
        Pcall   OutAddrOff, <bx, ax, 0>
        Pcall   crlf
        jmp     short cmp6

cmp7:   pop     cx
        lea     bx,SrcAddr
        Pcall   AddAddr, <cx, 0>
        lea     bx,DesAddr
        Pcall   AddAddr, <cx, 0>
        jmp     short cmp4
cmpx:
EndProc

;***    DoExtCmd
;
;       This command allows environment specific commands to
;       be added to the debugger.  Uses the debugger call out
;       function to call the command.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   ALL
;

DefProc DoExtCmd, NEAR, LOCAL
        Pcall   DebCallOut, <DEB_CALLOUT_EXTCMD>
EndProc

;***    PrintRebootMsg
;
;       This command allows environment specific commands to
;       be added to the debugger.  Uses the debugger call out
;       function to call the command.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   ALL
;

DefProc PrintRebootMsg, NEAR, LOCAL
        putstr  <"use .reboot", CR, LF>
EndProc

;***    DebOptions
;
;       Changes debugger options
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   AX, BX
;

DefProc DebOptions, NEAR, LOCAL
        Pcall   PeekChr                 ; (AL) = peeked character
        jz      short dop2              ; jump if EOL, to print settings

        cmp     al,"?"
        jne     short dop1

;       Print option help list

        Pcall   SkipB
        mov     dx,dataOFFSET DebOptHelp
        Pcall   rprbuf                  ; print help message
        jmp     dopx

dop1:   Pcall   ScanP
        jnz     short dop3              ; jump if chars on line
dop2:   jmp     dop12

;       Change options settings

dop3:   Pcall   GetInStr                ; (BX) = option ptr, (CX) = option len
        mov     si,dataOFFSET OptionTable
        mov     dx,size OptionTableS
        Pcall   SearchString            ; (SI) = OptionTable entry
        jc      short dop9              ; jump if error

        mov     di,[si].OptionAddr    ; (DI) = address of option variable
        mov     ax,[si].OptionBitMask   ; (AX) = option variable bit mask
        xor     bh,bh
        mov     bl,[si].OptionType      ; (BX) = option type
        shl     bx,1
        jmp     word ptr cs:dop4[bx]

dop4    dw      codeOFFSET dop5
        .errnz  DEB_OPT_CLEARBIT-0
        dw      codeOFFSET dop6
        .errnz  DEB_OPT_SETBIT-1
        dw      codeOFFSET dop7
        .errnz  DEB_OPT_TOGGLEBIT_CLEAR-2
        dw      codeOFFSET dop7
        .errnz  DEB_OPT_TOGGLEBIT_SET-3
        dw      codeOFFSET dop8
        .errnz  DEB_OPT_WORD-4

dop5:   not     ax
        and     [di],ax                 ; clear bit
        jmp     short dop11

dop6:   or      [di],ax                 ; set bit
        jmp     short dop11

dop7:   xor     [di],ax                 ; toggle bit
        jmp     short dop11

dop8:   Pcall   GetChrI                 ; (AL) = next character
        jz      short dop9
        cmp     al,"="
        je      short dop10
dop9:   jmp     perror

dop10:  Pcall   EvalWord                ; (DX) = evaluated word expression
        mov     [di],dx

dop11:  Pcall   ScanP
        jnz     short dop3              ; jump if more characters on line
        jmp     dopx

;       Print current option settings

dop12:  mov     si,dataOFFSET OptionTable

;       Loop on each entry in OptionTable

dop13:  mov     di,[si].OptionAddr      ; (DI) = address of option variable
        mov     ax,[si].OptionBitMask   ; (AX) = option variable bit mask
        mov     dx,[si].OptionName      ; (DX) = pointer to option name
        xor     bh,bh
        mov     bl,[si].OptionType      ; (BX) = option type
        shl     bx,1
        jmp     word ptr cs:dop14[bx]

dop14   dw      codeOFFSET dop15
        .errnz        DEB_OPT_CLEARBIT-0
        dw      codeOFFSET dop16
        .errnz  DEB_OPT_SETBIT-1
        dw      codeOFFSET dop15
        .errnz  DEB_OPT_TOGGLEBIT_CLEAR-2
        dw      codeOFFSET dop16
        .errnz  DEB_OPT_TOGGLEBIT_SET-3
        dw      codeOFFSET dop18
        .errnz  DEB_OPT_WORD-4

dop15:  test    [di],ax
        jz      short dop17             ; jump if bit is clear, print name
        jmp     short dop19

dop16:  test    [di],ax
        jz      short dop19             ; jump if bit is clear, skip entry

dop17:  Pcall   rprbuf                  ; print option name
        Pcall   blank
        jmp     short dop19

dop18:  Pcall   rprbuf                  ; print option name
        mov     al,'='
        Pcall   putchar
        mov     dx,[di]
        xor     ax,ax                   ; (AX:DX) = option value to print
        Pcall   OutHex
        Pcall   blank

dop19:  add     si,size OptionTableS    ; next entry
        cmp     [si].OptionName,0
        jne     short dop13             ; jump if more entries
        Pcall   crlf
dopx:
EndProc

;***    DumpStack - stack trace command (K)
;***    DumpStack2 - stack trace command (K)
;
;       K[S|B] [<SS:BPaddr>] [<CS:IPaddr>]
;         S = 16 bit stack frame (Small)
;         B = 32 bit stack frame (Big)
;
;       ENTRY:  (DI) = size flag (DumpStack2 only)
;
;       EXIT:   NONE
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;

DefProc DumpStack, NEAR, PUBLIC
        xor     di,di                   ; (DI) = 16 bit stack frame (0)
        Pcall   PeekChr
        jz      short dst20
        cmp     al,'S'
        je      short dst10
        cmp     al,'B'
        jne     short dst20
        inc     di                      ; (DI) = 32 bit stack frame (1)
dst10:  Pcall   SkipB
        jmp     short DumpStack2

dst20:  dec     di                      ; (DI) = default size indication (-1)

Entry   DumpStack2, NEAR, PUBLIC

LocVar  SSBPaddr, <size AddrS>
LocVar  CSIPaddr, <size AddrS>

BeginProc
        cmp     di,1
        jne     short dst30
        lea     si,CSIPaddr
        Pcall   FillLinAddr, <si, RegSA.EIPsav.LowWord, RegSA.EIPsav.HighWord>
        jmp     short dst40

dst30:  lea     bx,CSIPaddr
        Pcall   GetCSIPaddr
        mov     si,bx                   ; (SI) = CS:IP AddrS
dst40:  lea     bx,SSBPaddr             ; (BX) = SS:BP AddrS
        Pcall   FillDefAddr, <bx, RegSA.SSsav, RegSA.EBPsav.LowWord, RegSA.EBPsav.HighWord>
        Pcall   EvalAddr, <bx, bx>
        Pcall   CheckAddr, <bx>
        Pcall   EvalAddr, <si, si>
        Pcall   CheckAddr, <si>
        cmp     di,-1
        jne     short dst50
        push    bx
        Ccall   getdbit, <si>
        pop     bx
        mov     di,ax                   ; (DI) = 0 if d bit clear, !0 if set
dst50:  Pcall   PrintStack
EndProc

;***    PrintStack
;
;       Print out a stack dump threading thru with BP.
;
;       ENTRY:  (BX) = AddrS containing start of SS:BP chain.
;               (SI) = AddrS containing the CS:IP of current proc.
;               (DI) = 16 bit (0) or 32 bit (!0) stack frame flag
;
;       EXIT:   NONE
;
;       USES:   BX->AddrS, SI->AddrS, AX, CX, DX, Flags
;

DefProc PrintStack, NEAR, PUBLIC

LocVar  ParmOffs, WORD

BeginProc
pst10:  and     [bx].AddrOff.LowWord,0FFFEh ; windows might have set this bit
        cmp     [bx].AddrOff.LowWord,0
        je      short pst20
        or      di,di                   ; 16 bit address?
        jz      short pst30             ;  jump if 16 bit address

        Pcall   GetDwordOff, <4, 0>     ; (AX:DX) = 32 bit near address
        jc      short pst20
        mov     [si].AddrOff.LowWord,dx
        mov     [si].AddrOff.HighWord,ax
        Pcall   OutAddrS, <si>
        Pcall   blank

;       Spit out 4 dwords of parameters

        mov     ParmOffs,8
        Pcall   GetDwordOff, <ParmOffs, 0>
        jc      short pst15
        Pcall   Out32
        Pcall   blank
        add     ParmOffs,4
        Pcall   GetDwordOff, <ParmOffs, 0>
        jc      short pst15
        Pcall   Out32
        Pcall   blank
        add     ParmOffs,4
        Pcall   GetDwordOff, <ParmOffs, 0>
        jc      short pst15
        Pcall   Out32
        Pcall   blank
        add     ParmOffs,4
        Pcall   GetDwordOff, <ParmOffs, 0>
        jc      short pst15
        Pcall   Out32
        Pcall   blank
pst15:  jmp     pst50

pst20:  jmp     pstx

pst30:  Pcall   GetWordOff, <2, 0>      ; (DX) = 16 bit near address
        jc      short pst20
        mov     [si].AddrOff.LowWord,dx
        mov     ParmOffs,6              ; assume far address

;       Check if the address is a far address

        Pcall   WhatIsClientMode
        jnc     pst40                   ; jump if real or V86 mode
        Pcall   GetWordOff, <4, 0>      ; (DX) = possible CS selector
        xchg    dx,[si].AddrSeg         ; exchange possible CS selector
        xchg    bx,si                   ; (BX) = CS:IP AddrS (SI) = SS:BP AddrS
        push    dx                      ; save original CS
        mov     cx,1
        Pcall   AddrToPhy               ; verify CS
        pop     dx                      ; (DX) = original CS
        xchg    bx,si                   ; (SI) = CS:IP AddrS (BX) = SS:BP AddrS
        jnc     pst40
        xchg    dx,[si].AddrSeg         ; if error, restore original CS
        mov     ParmOffs,4              ; whoops, near address

pst40:  Pcall   OutAddrS, <si>
        Pcall   blank

;       Spit out 4 words of parameters

        Pcall   GetWordOff, <ParmOffs, 0>
        jc      short pst50
        Pcall   out16
        Pcall   blank
        add     ParmOffs,2
        Pcall   GetWordOff, <ParmOffs, 0>
        jc      short pst50
        Pcall   out16
        Pcall   blank
        add     ParmOffs,2
        Pcall   GetWordOff, <ParmOffs, 0>
        jc      short pst50
        Pcall   out16
        Pcall   blank
        add     ParmOffs,2
        Pcall   GetWordOff, <ParmOffs, 0>
        jc      short pst50
        Pcall   out16
        Pcall   blank

pst50:  push    bx
        xor     cx,cx                   ; (CX) = PrintNearSym flags
        Pcall   PrintNearSym
        Pcall   crlf
        pop     bx
        or      di,di                   ; 16 bit address?
        jz      short pst60             ;  jump if 16 bit address
        Pcall   GetDword                ; (AX:DX) = next EBP
        jc      short pstx
        mov     [bx].AddrOff.LowWord,dx
        mov     [bx].AddrOff.HighWord,ax
        jmp     pst10

pst60:  Pcall   GetWord                 ; (DX) = next BP
        jc      short pstx
        mov     [bx].AddrOff.LowWord,dx
        jmp     pst10
pstx:
EndProc

;***    GetEOL
;
;       Make sure there is nothing more on the line except for
;       blanks and carriage return. If there is, it is an
;       unrecognized parameter and an error.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;

DefProc GetEOL, NEAR, PUBLIC
        push    ax
        Pcall   GetChr                  ; Skip blanks
        pop     ax
        jz      short geo1
        jmp     perror
geo1:
EndProc

;***    ScanP
;
;       Scan for parameters of a command skipping comma's.
;
;       ENTRY:  NONE
;
;       EXIT:   (AL) = character
;               (ZR) = 1 if end of command line.
;
;       USES:   AL, FLAGS
;

DefProc ScanP, NEAR, PUBLIC
        Pcall   GetChr                  ; Get first non-blank
        je      short scp1
        cmp     al,','                  ; check if comma
        jne     short scp1
        Pcall   SkipB
        Pcall   GetChr                  ; Get first non-blank
scp1:
EndProc

;***    SkipB
;
;       Skip Character
;
;       ENTRY:  NONE
;
;       EXIT:   AL = next character
;               ZR = 1 if end of command line.
;
;       USES:   AL, FLAGS
;

DefProc SkipB, NEAR, PUBLIC
        inc     pTXT
        Pcall   PeekChr
EndProc

;***    GetChr
;
;       Peek at current character skipping over white space only.
;
;       ENTRY:  NONE
;
;       EXIT:   AL = character
;               ZR = 1 if end of command line.
;
;       USES:   AL, FLAGS
;

DefProc GetChr, NEAR, PUBLIC
        Pcall   GetChrI                 ; Get first non-blank
        je      short gtc1
        dec     pTXT                    ; back up one character
gtc1:
EndProc

;***    GetChrI
;
;       Get next non-blank character, advance pTXT.
;
;       ENTRY:       NONE
;
;       EXIT:   AL = character
;               ZR = 1 if end of command line.
;
;       USES:   AL, FLAGS
;

DefProc GetChrI, NEAR, PUBLIC
        SaveReg <si>
        mov     si,pTXT
        cld
gti1:   lodsb
        cmp     al,' '                  ; check for white space
        je      short gti1
        cmp     al,TABCHR
        je      short gti1
        mov     pTXT,si
        dec     si
        Pcall   EOLchk
        jne     short gti2
        mov     pTXT,si                 ; Back to first non-blank
gti2:   RestReg <si>                    ; can't affect the ZF flag
EndProc

;***    IsSeparator
;
;       ENTRY:  NONE
;
;       EXIT:   (ZR) = if command separator
;               (NZ) = if not command separator
;               (AL) = character
;
;       USES:   AL, Flags
;

DefProc IsSeparator, NEAR, PUBLIC
        Pcall   PeekChr                 ; (AL) = command letter
        jz      isp1
        cmp     al,' '
        je      isp1
        cmp     al,TABCHR
        je      isp1
        cmp     al,','
isp1:
EndProc

;***    PeekChr
;
;       Peek at first character without skip blanks or commas.
;
;       ENTRY:  NONE
;
;       EXIT:   AL = next character
;               ZR = 1 if end of command line.
;
;       USES:   AL, FLAGS
;

DefProc PeekChr, NEAR, PUBLIC
        SaveReg <si>
        mov     si,pTXT
        lodsb
        Pcall   EOLchk
        RestReg <si>
EndProc

;***    EOLchk
;
;       Check if AL is ";" or NULL which indicates in of command.
;
;       ENTRY:  AL = character to check
;
;       EXIT:   ZR = 1 if end of command line.
;
;       USES:   FLAGS
;

DefProc EOLchk, NEAR, PUBLIC
        cmp     al,";"                  ; end of command if semicolon
        je      short ecr1
        or      al,al                   ; end of line if NULL
ecr1:
EndProc

;***    SetDefaults
;
;       Sets the default starting addresses, etc for the commands.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   AX, BX
;

DefProc SetDefaults, NEAR, PUBLIC
        Pcall   CopyAddr, <dataOFFSET RegSA.CSIPsav, dataOFFSET DisAddr>

        mov     bx,dataOFFSET EnterAddr
        Pcall   GetDSaddr

        mov     bx,dataOFFSET DumpAddr
        Pcall   GetDSaddr
        mov     DefMode,"B"
EndProc

CodeSegmentEnd

        end
