📄 uimain.asm
字号:
TITLE uimain.asm - BASIC's top level interface to user interface.
;***
;uimain.asm
;
; Copyright <C> 1985-1988, Microsoft Corporation
;
;Purpose:
; Main user interface, and support routines.
;
;
;*******************************************************************************
include version.inc
UIMAIN_ASM = ON
;Next, include COW's 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 parser
includeOnce qbimsgs
includeOnce rtinterp
includeOnce txtmgr
includeOnce ui
includeOnce uiint
includeOnce uimenu
assumes ds,DATA
assumes ss,DATA
assumes es,NOTHING
; external procedures used by uictl
externFP EnableMenuItem
externFP CheckMenuItem
externNP fCanContUI
externNP GetEditLine
externFP SetBlinkBit
IFNDEF ROMBASIC
externFP FCheckTandy1000
ENDIF
;Runtime supplied functions used by this module:
EXTRN B$UnhookKbd:FAR
EXTRN B$hookKbd:FAR
sBegin DATA
externW fIsProgram
externB fPasteOK
externB fSyntaxCheck
externW cWatch
externB fInsertMode ;EditMgr insert state.
EXTRN b$fCompErr:word ;non-zero if error was in compiled code
EXTRN fRefreshWatch:byte ;non-zero if WATCH window needs to
; be refreshed
externW szDialogTitle ; * current dialog box title, or
; 0 if no title.
;fUiActive is TRUE when user-interface keyboard and mouse interrupt
;handlers are active.
PUBLIC fUiActive
fUiActive dw 0
fUIInit db 0 ; non-zero if UI Initialized.
;When we can CONTinue, and we execute a direct mode stmt that causes a ret
; adr to the direct mode buffer to be pushed on the stack (call/gosub/func),
; and then enter direct mode to save the otx of the called routine, we save the
; old grs.otxCont in otxContProg, so that after the direct mode call finishes,
; we can still CONTinue the stopped program.
;This variable gets set to UNDEFINED if we do any EDITs (because otxContProg
; isn't updated as pcode is moved by the text mgr and static-scanner).
;It is also set to UNDEFINED if we execute another direct mode stmt
; while there is a return address to the direct mode buffer on the stack,
; because we choose not to remember an arbitrary number of these, and
; it would be difficult for the user to keep track of them anyway.
;TxtDirect guarentees that if user enters a direct mode statement
; while there is a return address to the direct-mode-buffer on the
; stack (FG_RetDir), that CantCont will occur.
;
otxContProg DW UNDEFINED
cGrab db 0 ;number of active callers of UiGrabSpace
sEnd DATA
sBegin UI
assumes cs,UI
;**************************************************************************
; UiInit
; Purpose:
; In QB, the RUNTIME gets control first (since in stand-alone EXEs
; there is no user-interface). This is called after the runtime
; has initialized. It initializes the user interface.
; Entry:
; cmdSwitches has flags set to indicate command-line switch settings
; Exit:
; the file QB.INI is read
; If command line didn't contain /RUN <filename>, the debug screen
; is shown.
; AX nonzero if Out of Memory
;
;**************************************************************************
cProc UiInit,<PUBLIC,FAR>
cBegin
; Set colors at beginning so first screen writes are the same color.
call ReadQbIni
call B$UnhookKbd
IFNDEF ROMBASIC
call FCheckTandy1000 ;check for/install TANDY 1000 KBD
ENDIF
call CwInit
call B$HookKbd
test [cmdSwitches],CMD_SW_RUN ;/RUN filename given on command line?
jnz NoDebugScr ; brif so, no debug screen
call EnsShowDebugScr
NoDebugScr:
call GrabSpace ;make sure we can successfully
or ax,ax ; GrabSpace (ax = 0 if failed)
not ax ;0 -> -1 if failed, preserves flags
jz UiInitExit ;brif failure, exit with ax<>0
call ReleaseSpace ;release what we grabbed
xor ax,ax ;exit with ax = 0 for success
UiInitExit:
inc fUIInit ;indicate UiInit complete
cEnd
;**************************************************************************
; UiTerm
; Purpose:
; Called just before we are about to leave the QB for good.
; Calls COW and KKIF to tell it to terminate.
;
; Also called before a SHELL is performed. The runtime calls
; this routine prior to SHELL, or at termination. After a SHELL,
; The runtime calls UiReInit so COW can re-initialize.
; Entry:
; Exit:
;
;**************************************************************************
cProc UiTerm,<PUBLIC,FAR>
cBegin
cmp fUIInit,0 ; were we ever initialized?
jz UTExit ; brif not, exit now
cCall WriteQbIni ; write the qb.ini file
cCall CwTerm
mov al, 1
cCall SetBlinkBit,<ax>
UTExit:
cEnd
;**************************************************************************
; UiReInit
; Purpose:
; In QB, the RUNTIME needs to reinitialize the user interface
; at certain points (primarily after SHELL). This entry point
; allows the user interface to reestablish any state (such as
; hooking interrupts) that is necessary.
; Entry:
; none.
; Exit:
; none.
;
;**************************************************************************
cProc UiReInit,<PUBLIC,FAR>
cBegin
cEnd
;**************************************************************************
; UiPause
; Purpose:
; Added with [24].
; Called before a SHELL is performed. The runtime calls
; this routine prior to SHELL, or at termination. After a SHELL,
; The runtime calls UiReInit so COW can re-initialize.
; Entry:
; Exit:
;
;**************************************************************************
cProc UiPause,<PUBLIC,FAR>
cBegin
cEnd
;**************************************************************************
; UserInterface()
; Purpose:
; UserInterface() is the primary interface the user-interface
; provides to the rest of BASIC.
; It is called by the beginning-of-statement/line
; executor when it sees bosMask & BOS_DEBUG non-zero,
; by opEot and opStEnd (end-of-program), opBreakPoint, and opStStop.
;
; For example, if tracing was active for the program:
; CLS : PRINT
; STOP
; UserInterface would be invoked between the opcodes seperated by '^':
; opBol ^ opStCls opBos ^ opBreakPoint ^ opStPrint opBol ^ opStStop ^ opEot ^
;
; Entry:
; grs.oRsCur, otxCur identify the next statement to be executed.
; If grs.otxCur == UNDEFINED, it means we've just executed an ExEot,
; and thus cannot continue. Otherwise, it points just beyond the
; ExBos opcode which caused UserInterface to be called.
; If the parser's input buffer (ps.bdpSrc) contains any text, that
; text is executed as if the user entered it as a direct mode stmt.
; This allows executors like RUN <filename> to load the file, then
; re-invoke UserInterface after stuffing the command "RUN" in the
; parser's input buffer. This allows there to be only 1 control path
; to scan all loaded text tables.
;
; The actions performed by UserInterface() are determined by which
; bits are set in debugFlags as follows:
;
; DEBUG_EXEC_CMD - set by some executor like RUN <file>/CHAIN <file>,
; which has loaded pcode, and now wants the pcode to be scanned
; and a direct mode statement executed (like RUN or CONT). The
; executor for the direct mode stmt has been loaded into grs.bdlDirect.
;
; DEBUG_ERROR - set when a runtime error occurs and
; is not trapped. The runtime error code restores SI to
; the beginning of statement, sets this flag, then re-
; executes the statement, which invokes the debugger.
; The error code is passed in the same static variable
; examined by the ERR intrinsic function. Causes
; DebugError() to be invoked.
;
; DEBUG_STOP - set when a STOP statement is executed,
; when Ctrl-BREAK is pressed and not trapped, or
; when a breakpoint is executed.
; Causes DebugStop() to be invoked.
;
; DEBUG_WATCHPOINT - set when a Stop-Watch-Expression evaluates to TRUE
; Causes DebugStop() to be invoked.
;
; DEBUG_END - set by the executors for opEot and
; opStEnd to indicate end-of-program. Causes DebugEnd()
; to be invoked.
;
; DEBUG_TRACE - set while tracing statement execution
; either because of TRON, single-step, or procedure-
; step. Causes DebugTrace() to be invoked.
;
; DEBUG_WATCH - set when any Watch Expressions are
; active in the program. Causes DebugWatch() to be invoked.
;
; DEBUG_CANT_CONT - Causes UserInterface() to set grs.otxCONT to
; UNDEFINED the next time it is called.
; This is used by executors, which cannot call CantCont
; because UserInterface sets grs.otxCONT every time
; we enter UserInterface, thus undoing their change.
; Since it sets otxCONT rather than calling CantCont(),
; stack tracing and variable printing are still possible
; from direct mode, just not continuing.
;
; Exit:
; grs.fDirect, grs.oRsCur, grs.otxCur indicate where pcode
; execution is to commence
;
;**************************************************************************
cProc UserInterface,<PUBLIC,FAR>
cBegin
DbAssertRelB [cGrab],e,0,UI,<UserInterface: cGrab non-zero>
test [debugFlags],DEBUG_WATCH
je NoWatch ;brif no WATCH expressions visible
call DebugWatch ;get next watch expression, or
; normal program pcode
jne J1_TestRestore ;brif the next pcode to be executed
; is WATCH pcode, DebugWatch has
; set grs up so it's ready to go
cmp [fDebugScr],FALSE
jne NoWatch ;brif debug screen is visible
; need to print values in watch window
test [debugFlags],NOT DEBUG_WATCH
jne NoWatch ;brif WATCH isn't only thing to do
;speeds up WATCHPOINTs significantly
J1_TestRestore:
jmp SHORT TestRestore
;If we didn't care about Watch functions that write to the screen
; we could branch to ExecStmt and be faster, but since most watchpoints
; are handled by the executor calling DebugWatch directly, the only
; watch points we are slowing down are when the debug screen is active.
NoWatch:
cmp [grs.GRS_fDirect],FALSE
je NotInDirect ;brif not executing direct mode stmts
;Following test prevents tracing statements in direct mode.
test [debugFlags],NOT (DEBUG_TRACE OR DEBUG_WATCH)
je GotNextStmt ;brif nothing other than TRACE/WATCH
;We've returned to direct mode, potentially after having debugged
;a GOSUB/SUB/FUNCTION/DEF FN. If we don't set otxCur to
;otxContProg, we'd show the RETURN or END DEF/SUB/FUNCTION
;stmt as the next stmt to be executed, instead of the statement
;that was active before the direct mode statement executed.
mov ax,[otxContProg]
mov [grs.GRS_otxCur],ax ;will be stored in grs.otxCONT
NotInDirect:
;we were not executing in direct mode, setup for CONT executor.
;If grs.otxCur == UNDEFINED (as set by opEot), we cannot continue
cmp [debugFlags],0
je GotNextStmt ;brif nothing to do. This is true
;for the 1st non-direct-mode-buffer
;pcode we execute. Once we branch
;out of the direct mode buffer,
;there's no need to call UserInterface
;between statements.
mov ax,[grs.GRS_oRsCur]
mov [grs.GRS_oRsCONT],ax
test [txdCur.TXD_flags],FTX_mrs
je NotInDefFn ;brif definately not within a DEF FN
mov ax,[grs.GRS_oMrsCur]
NotInDefFn:
mov [grs.GRS_oRsContTxtTbl],ax
mov ax,[grs.GRS_otxCur]
mov [grs.GRS_otxCONT],ax
GetNextStmt:
;Call NextStmt() to determine what stmt should be executed next.
;NextStmt() may interact with user, letting user edit program etc.
call NextStmt
;If user added or removed a WATCH expression, we need to refresh
;the values in the Watch window.
cmp [fRefreshWatch],FALSE
je GotNextStmt
mov [fRefreshWatch],FALSE
call DebugWatch
je GetNextStmt ;brif can't execute WATCH pcode
; for any reason
GotNextStmt:
cmp [cWatch],0
je TestRestore ;brif no WATCH expressions are active
or [debugFlags],DEBUG_WATCH; tell Executor to call UserInterface
; after executing next statement
;The reason we enter direct mode for each statement, regardless of
;the state of debugFlags, is so we can toggle to the output screen
;for direct mode lines like:
; FOR I=1 TO 10: PRINT A(I): NEXT I
;but leave the debug screen active for direct mode lines like:
; FOR I=1 TO 10: A(I) = 1: NEXT I
;
;Make the output screen visible IF:
; we're tracing or watching variables AND next stmt does screen I/O OR
; we're NOT tracing or watching variables AND next stmt is in program
; (as opposed to direct mode statement buffer)
;
TestRestore:
cmp [fDebugScr],FALSE
je ExecStmt ;brif output screen already visible
;If this test is removed, callers
;who set DebugFlags.DEBUG_EXEC_CMD
;need to put an ExEot in bdlDirect
;for FNextStmtDoesIO
test [debugFlags],DEBUG_WATCH OR DEBUG_TRACE
jne TestForIO ;brif we're watching or tracing
cmp [grs.GRS_fDirect],FALSE
je ShowUserScr ;brif not executing direct mode stmts
TestForIO:
call FNextStmtDoesIO
or ax,ax
je ExecStmt ;brif next stmt causes no screen I/O
ShowUserScr:
call EnsShowOutSaveRs ;show output screen, don't alter
; grs.oRsCur
ExecStmt:
;***** Begin revision [17]
;Clear FBOSSTOP in bosFlags now, because we have just had the
;chance to responde to it so if it is still set it is because
;we stopped for some other reason. There for, the FBOSSTOP
;has already been serviced.
and [bosFlags], NOT FBOSSTOP
;***** End revision [17]
;If we are debugging or we are executing a direct mode statement,
;make sure we re-enter user interface for each opBos.
mov al,[debugFlags]
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -