📄 uidebug.asm
字号:
TITLE uidebug.asm - User interface to debugger.
;***
;uidebug.asm
;
; Copyright <C> 1985-1988, Microsoft Corporation
;
;Purpose:
; Debugging support.
;
;
;*******************************************************************************
.xlist
include version.inc
UIDEBUG_ASM = ON
;Include twin interface headers
include cw/version.inc
include cw/windows.inc
include cw/edityp.inc
;Next, include QBI's headers
includeOnce architec
includeOnce context
includeOnce executor
includeOnce heap
includeOnce lister
includeOnce opmin
includeOnce opstmt
includeOnce parser
includeOnce qbimsgs
includeOnce scanner
includeOnce txtmgr
includeOnce ui
includeOnce uiint
includeOnce util
includeOnce uimenu
.list
;---------------------------------------------------------------------------
;
; The following demonstrates how the user interface code interacts with
; the executor code to handle watchpoints and watch expressions.
; We start the discussion in UserInterface with the following program loaded:
; A=1
; A=2
; The pcode for this program is:
; opBol
; opLit1
; opIdSt(A)
; opBol
; opLit2
; opIdSt(A)
; opEndProg
;
; - User presses single step key, causing UserInterface to exit
; - opBol executes, sees DEBUG_TRACE bit set in debugFlags, enters UserInterface
; - User selects Watchpoint... menu, CmdWatchAdd(WT_Watch) prompts the user
; for an expression, parses it to pcode, and inserts it at the end of the
; text table.
; - User selects Watch... menu, CmdWatchAdd(WT_WatchPoint) prompts the user
; for an expression, parses it to pcode, and inserts it at the end of the
; text table.
; - We now have with a WATCHPOINT of "A=1" and a WATCH EXPRESSION of "A" and
; the pcode looks like:
; opBol
; opLit1
; opIdSt(A)
; opBol
; opLit2
; opIdSt(A)
; opEndProg
; opIdLd(A)
; opLit1
; opEQ
; opWatchStop
; opIdLd(A)
; opWatchExp
; opEot
;
; - User presses single step key. When UserInterface exits, it sees active
; Watch expressions, and sets DEBUG_WATCH in debugFlags.
; - opLit1 and opIdSt(A) execute
; - opBol executes, sees bits set in debugFlags, enters UserInterface
; - UserInterface immediately dispatches to DebugWatch. DebugWatch sees
; iWatch (static counter variable) is UNDEFINED (meaning we weren't executing
; Watch pcode), saves current pc in otxWatchSave, sets the
; current pc to the 1st watch expression's pcode, sets iWatch to 0,
; returns to UserInterface with flags telling it to dispatch.
; - opIdLd(A), opLit1, and opEQ execute.
; - opWatchStop executes, sees a TRUE (non-zero) argument on the stack, sets
; DEBUG_BREAK in debugFlags. opWatchStop then falls into opBol which sees
; debugFlags non-zero and enters UserInterface.
; - UserInterface immediately dispatches to DebugWatch. DebugWatch sees
; iWatch is 0, bumps it to 1, sets the current pc to the 2nd watch
; expression's pcode, returns to UserInterface with flags telling it to dispatch.
; - opIdLd(A) executes.
; - opWatch executes, saves value at [pWatchVal], opWatch then falls into
; opBol which sees debugFlags non-zero and enters UserInterface.
; - UserInterface immediately dispatches to DebugWatch. DebugWatch sees
; iWatch is 1, restores the current pc to otxWatchSave,
; returns to UserInterface.
; - UserInterface then sees DEBUG_BREAK is set, and would have stopped program
; execution even if we weren't tracing. UserInterface then interacts with
; the user until the user gives another command.
; - User presses single step key. When UserInterface exits, it sees active
; Watch expressions, and sets DEBUG_WATCH in debugFlags.
; - opLit2 and opIdSt(A) execute
; - opEndProg executes, causing us to once again enter UserInterface
;
;---------------------------------------------------------------------------
assumes DS,DATA
assumes ES,NOTHING
assumes SS,DATA
EXTRN B$BEEP:FAR
EXTRN B$IFOUT:FAR
EXTRN B$Pause:FAR
sBegin DATA
EXTRN b$ErrNum:word ;error code for last runtime error
EXTRN b$CurFrame:word ;current frame ptr (for PTRACE)
EXTRN b$MainFrame:word ;base frame ptr
EXTRN fRecord:byte ;set TRUE is /RCD switch seen
EXTRN fPlayBack:word ;TRUE if we're playing back.
; an event playback file.
EXTRN axMac:byte
externB HelpFlags ; uiint.inc/h
;Points to space allocated by CallsMenuInit, released by CallsMenuTerm
;
pbCallsRelease DW 0
;iWatch is used by several functions involved in drawing the content
; of the watch window. It indicates which line in the window is currently
; being drawn (0 to n)
;
DbPub iWatch
iWatch DW UNDEFINED
; iInstWatch is used to keep track of where an Instant Watch entry is
; in the tWatch table. The value of iInstWatch is only significant if
; the value of cInstWatch is non-zero.
DbPub iInstWatch
iInstWatch DW 0
;fRefreshWatch is set TRUE whenever a WATCH entry is added or removed.
;It causes us to refresh the WATCH window.
;
PUBLIC fRefreshWatch
fRefreshWatch DB 0
;pWatchVal points to valtyp/value structure to be filled in by Watch executors
PUBLIC pWatchVal
pWatchVal DW 0
;1 entry per watch expression - number of bytes in expression's title
tCbWatchTitle DB (WATCH_MAX+1) DUP (?) ; "+1" is for Instant Watch
otxWatchSave DW 0 ;copy of grs.otxCur while WATCH pcode executes
errnumSave DW 0 ; copy of b$errnum while WATCH pcode executes
bpWatchSave DW 0 ;copy of bp for start of current WATCH expression
fDirectSave DB 0 ;copy of grs.fDirect while WATCH pcode executes
;Trace modes
;
TRACE_OFF EQU 0
TRACE_HISTORY EQU 1 ;Debug/History menu item is ON
TRACE_ANIMATE EQU 2 ;Debug/Trace menu item is ON
TRACE_STEP EQU 3 ;F8 was pressed last time in UserInterface
TRACE_PSTEP EQU 4 ;F10 was pressed last time in UserInterface
TRACE_WATCHSTEP EQU 5 ;Used by watch to force watch code to execute.
traceMode DB TRACE_OFF
pStepFrame DW 0
nonStickyBpRs DW 0
nonStickyBpLn DW UNDEFINED
CHIST EQU 20 ;we will remember last 20 statements executed
CBHIST EQU 4 ;number of bytes in 1 history entry
bdHist DB size BD DUP (0)
oHistNext DW 0
oHistShow DW 0
histFlags DB 0
FH_Full equ 01H
FH_NotEmpty equ 02H
szRun DB "RUN",0
sEnd DATA
sBegin CODE
;Table of opcodes(1) which descanner inserts where it finds return
; addresses on the stack:
;
tOpNoList LABEL WORD
opTabStart NOLIST
opTabEntry NOLIST,opNoList1
opTabEntry NOLIST,opEot
sEnd CODE
sBegin UI
assumes cs,UI
;**************************************************************************
; DoRunOrCont(ax:fFromStart)
; Purpose:
; Setup so program will continue execution.
; If can't continue then start at begining.
;
; Entry:
; ax = fFromStart - if TRUE start program from the beginning.
; Exit:
; fGotCmd is set TRUE (which terminates GetCmd)
;
;**************************************************************************
DoCont PROC NEAR
sub ax,ax
DoCont ENDP
;fall into DoRunOrCont
cProc DoRunOrCont,<NEAR>
cBegin
or ax,ax
jne FromStart
call fCanContUI
jne NotFromStart ;brif can continue
; FNextStmtDoesIO uses grs.otxCONT to determine if the next statement to
; execute will do output. ContContext is called just before FNextStmtDoesIO.
; if grs.otxCONT is UNDEFINED, ContContext will
; set grs.otxCONT to the begining of the main module.
;
FromStart:
call CantCont ;sets [grs.GRS_otxCONT] to UNDEFINED
PUSHI ax,<dataOFFSET szRUN>
call DoCmd
NotFromStart:
mov [fGotCmd],sp
cEnd
;**************************************************************************
; SetTronTroff
; Purpose:
; Set [traceMode] and set DEBUG_TRACE bit in [debugFlags] if not TRACE_OFF
; Called by TRON and TROFF opcode executors after they set [fTraceOn]
; Entry:
; [fTraceOn] = non-zero if TRON is active
; [fHistOn] = non-zero if HISTORY is active
;
;**************************************************************************
PUBLIC SetTronTroff
SetTronTroff PROC FAR
.errnz TRACE_ANIMATE - 2
mov ax,TRACE_ANIMATE
cmp [fTraceOn],ah
jne NoTrace ;brif TRON (Trace ON) is not active
dec ax ;al = TRACE_HISTORY
.errnz TRACE_HISTORY - 1
cmp [fHistOn],ah
jne NoTrace ;brif History ON is not active
dec ax ;al = TRACE_OFF
.errnz TRACE_OFF
NoTrace:
call SetTraceMode
ret ;can't fall into SetTraceMode, far->near
SetTronTroff ENDP
;**************************************************************************
; SetTraceMode
; Purpose:
; Set [traceMode] and set DEBUG_TRACE bit in [debugFlags] if not TRACE_OFF
; Entry:
; al = new trace mode (TRACE_STEP etc.)
;
;**************************************************************************
SetTraceMode PROC NEAR
mov [traceMode],al
or al,al
je GotTroff
.errnz TRACE_OFF
or [debugFlags],DEBUG_TRACE
GotTroff:
ret
SetTraceMode ENDP
;**************************************************************************
; CmdGo(fFromStart)
; Purpose:
; Continue or start program execution taking into account trace state.
; This procedure is called in response to the menu items `Run/Run' and
; `Run/Continue'
;
; Entry:
; fTraceOn - non-zero if TRON is active
; fFromStart - TRUE if we want to RUN else we want to CONT.
;
;**************************************************************************
cProc CmdGo,<PUBLIC,NEAR>
parmW fFromStart
cBegin
call SetTronTroff ;setup based on [fTraceOn]
mov ax,[fFromStart]
call DoRunOrCont ;execute a RUN or CONT command
cEnd
;**************************************************************************
; CmdSetNextStmt()
; Purpose:
; Reset instruction pointer to statement at cursor.
;
;**************************************************************************
cProc CmdSetNextStmt,<PUBLIC,NEAR>,<si,di>
cBegin
call TxtDescan ;descan to SS_PARSE
call GetEditLine ;ax = current edit line (from EditMgr)
push ax
call OtxOfLn ;ax = text offset to start of line
xchg ax, bx ; bx = otx of pcode to skip.
call TxtSkipOpFar ;ax = otx beyond the opBol[xx] opcode
;We always enter direct mode after
;execution of the opBol
xchg si,ax ;si = otx
mov di,[grs.GRS_oRsCur] ;di = oRs of current text table
test [txdCur.TXD_flags],FTX_mrs
je NotInDefFn ;brif we're in a sub/function window
push si ;pass otx
call OPrsOfOtx ;ax = otx of DEF FN
inc ax ;test for UNDEFINED
je NotInDefFn ;brif we're in main-level code
dec ax ;ax = oPrs for DEF FN
or ah,80h ;ax = oRs of DEF FN
xchg di,ax ;di = oRs of DEF FN
NotInDefFn:
call NeedContContext ;grs.oRsCur=grs.oRsCONT or grs.oMrsMain
;if no main module, uierr is set
je NxtStmtExit ;brif no main module
cmp di,[grs.GRS_oRsCur]
je BranchOk ;brif branching within text table
BadBranch:
PUSHI ax,MSG_BadNextStmt ;"Cannot cross module/procedure boundary"
call SetUiErr
jmp SHORT NxtStmtExit
BranchOk:
mov [grs.GRS_otxCONT],si
call DrawDebugScr ;so new current stmt will be hilighted
NxtStmtExit:
cEnd
;**************************************************************************
; CmdRestart()
; Purpose:
; Restart program. Like a Step but always starts at begining of program.
;
;**************************************************************************
cProc CmdRestart,<PUBLIC,NEAR>
cBegin
mov al,TRACE_STEP
call SetTraceMode
mov ax,sp ;ax = TRUE (non-zero)
call DoRunOrCont
cEnd
;**************************************************************************
; PStepReset
; Purpose:
; Called whenever executor resets stack pointer. This guarentees
; that DebugTrace will stop the next time it is called (even for F10).
;
;**************************************************************************
cProc PStepReset,<PUBLIC,FAR>
cBegin
mov [pStepFrame],0
cEnd
;**************************************************************************
; CmdStep(fPStep)
; Purpose:
; Setup to execute the next program statement.
; This routine is called in response to the Step and PStep
; accelerator keys.
;
; Entry:
; fPStep - if TRUE count procedure calls as one statement (F10).
;
; Exit:
; PStepFrame, debugFlags, traceMode, fGotCmd are all updated
;
;**************************************************************************
cProc CmdStep,<PUBLIC,NEAR>
parmW fPStep
cBegin
call ContContext ;activate program counter's context
call SkipStop ;skip past opBreakPoint or opStStop
mov ax,[grs.GRS_otxCONT]
inc ax ;test for UNDEFINED
je UseBp ;if Can't continue, stop at next entry
mov ax,[b$CurFrame] ;get current frame
mov cx,[pGosubLast] ;get gosub stack value
jcxz UseBp ;zero, use current frame as frame
cmp cx,ax
ja UseBp ; brif no gosubs for this frame
xchg ax,cx ;else use top of stack
UseBp:
mov [pStepFrame],ax ;and move it in
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -