uimain.asm
资源名称:DOS系统的源代码.rar [点击查看]
上传用户:xiaoan1112
上传日期:2013-04-11
资源大小:19621k
文件大小:40k
源码类别:
操作系统开发
开发平台:
Visual C++
- 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]
- or al,[grs.GRS_fDirect]
- je DontReEnter
- or [bosFlags],FBOSDEBUG ;tell executor to call UserInterface
- ; at the next beginning of stmt
- DontReEnter:
- ;ContReinitStat needs to be called every time we exit the
- ;user interface if any editing/loading took place. It ensures
- ;there is no unused space within BDs and no free space between
- ;near-heap entries. If there was any, a string allocation could fail
- ;even though there is free space, because arrays of string descriptors
- ;can only be moved at statement level, because the runtime
- ;could have several pointers to them, i.e. x$=a$(1)+a$(2)
- call ContReinitStat
- ;Return to the executor to execute pcode indicated by:
- ;grs.fDirect, grs.oRs, grs.otx.
- ;If the Quit menu item is selected, or the SYSTEM command is entered
- ;UserInterface returns to let the executor execute ExStSystem.
- ;Returning from this function causes the pcode-executor to begin.
- ;
- cEnd ;UserInterface
- ;**************************************************************************
- ; EnterUserInterface()
- ; Purpose:
- ; Enable all user interface interrupt handlers.
- ;
- ;**************************************************************************
- cProc EnterUserInterface,<PUBLIC,NEAR>
- cBegin
- cmp [fUiActive],0
- jne EntDmExit ;brif already in user interface
- mov [fUiActive],sp
- KbLoadOk:
- cCall DrawDebugScr
- cCall B$UnhookKbd ;tell runtime to disable its handler
- cCall HookInt24 ;enable interrupt-24 interrupt handler
- cCall CwHook
- call FlushMsgs ;flush any type-ahead keystrokes or
- ; mouse clicks
- EntDmExit:
- ;Set the BIOS INSERT flag to match what we will be using in
- ;this invocation of the UI. This will keep us in ssync with
- ;what speach software thinks our insert state is.
- xor bx, bx
- mov es, bx
- cli ; don't allow interrupts (might
- ; change BIOS flags on us)
- mov al, es:[417h] ; get BIOS shift state flags
- and al, 7fh ; assume insert mode off
- cmp fInsertMode, bl ; is CW in an insert mode
- je @F ; brif not, assumption good
- or al, 80h ; set BIOS insert mode on
- @@:
- mov es:[417h], al ; update BIOS flags
- sti ; reenable interrupts
- cEnd ;EnterUserInterface
- ;**************************************************************************
- ; ExitUserInterface()
- ; Purpose:
- ; Unhooks all user interface interrupt handlers.
- ;
- ;**************************************************************************
- cProc ExitUserInterface,<PUBLIC,NEAR>
- cBegin
- call FlushMsgs ;flush any type-ahead keystrokes or
- ; mouse clicks
- DbAssertRel [fUiActive],ne,0,UI,<ExitUserInterface: not in user interface>
- mov [fUiActive],0
- cCall CwUnHook
- cCall UnHookInt24
- cCall B$hookKbd ;tell runtime to enable its handler
- cEnd ;ExitUserInterface
- ;**************************************************************************
- ; DebugExecCmd
- ; Purpose:
- ; Invoked when we enter NextStmt with DEBUG_EXEC_CMD bit set in
- ; debugFlags
- ; Exit:
- ; Returns actionFlag bit which will cause the work to get done.
- ;
- ;**************************************************************************
- DebugExecCmd PROC NEAR
- mov [grs.GRS_otxCONT],UNDEFINED
- ;RUN <filename> needs otxCont UNDEFINED
- ; so scanner can grow variable tables.
- mov al,FDM_ExecCmd
- ret
- DebugExecCmd ENDP
- ;**************************************************************************
- ; NextStmt
- ; Purpose:
- ; Called by UserInterface() to determine the next opcode to be executed
- ; It is only called when UserInterface has something significant to do.
- ;
- ; Entry:
- ; same as for UserInterface()
- ;
- ;**************************************************************************
- .errnz DEBUG_ERROR - 01h
- .errnz DEBUG_EXEC_CMD - 02h
- .errnz DEBUG_STOP - 04h
- .errnz DEBUG_END - 08h
- .errnz DEBUG_WATCHPOINT - 10h
- .errnz DEBUG_TRACE - 20h
- DebugDispTbl LABEL WORD
- DW UIOFFSET DebugError
- DW UIOFFSET DebugExecCmd
- DW UIOFFSET DebugStop
- DW UIOFFSET DebugEnd
- DW UIOFFSET DebugStop
- DW UIOFFSET DebugTrace
- DebugDispTblEnd LABEL WORD
- DbPub NextStmt
- cProc NextStmt,<NEAR>
- localB actionFlags
- localB debugFlagsTmp
- cBegin
- DbAssertRelB [cGrab],e,0,UI,<NextStmt: cGrab non-zero>
- ;remember if the debug screen was visible when we entered NextStmt()
- mov al,[debugFlags]
- mov [debugFlagsTmp],al
- test al,DEBUG_ERROR
- jne GotRtErr
- ;Set b$fCompErr 0 so any errors generated from within the user
- ; interface don't look like they were generated in QuickLibrary.
- mov [b$fCompErr],0
- GotRtErr:
- ;reset any variables that assume we're in same context we were
- ;in last time they were set.
- call UiFlushCache
- sub ax,ax
- mov [actionFlags],al
- mov [debugFlags],al
- mov [fDoCmd],ax
- ; Dispatch to function based on debugFlagsTmp. Function
- ; returns ushort mask with 0 or more of FDM_xxx bits set
- ; indicating what action is to be performed.
- ; Function also sets various bits in debugFlagsTmp to cause
- ; UserInterface to be entered after the next statement is executed.
- ;
- ; NOTE: The order these flags are tested is IMPORTANT
- ;
- mov al,[debugFlagsTmp]
- and al,DEBUG_EXEC_CMD+DEBUG_ERROR+DEBUG_STOP+DEBUG_END+DEBUG_WATCHPOINT+DEBUG_TRACE
- je DispRet ;brif nothing interesting
- mov bx,UIOFFSET DebugDispTbl-2
- DispLoop:
- inc bx ;advance to next exception handler
- inc bx
- DbAssertRel bx,b,<UIOFFSET DebugDispTblEnd>,UI,<uimain.asm: Bad debugFlags>
- shr al,1
- jnc DispLoop
- call cs:[bx] ;dispatch to routine based on debugFlags
- ;al = bit mask of actions to carry out
- DispRet:
- mov [actionFlags],al
- test al,FDM_ShowStmt
- je TestGetCmd ;brif cursor not to be positioned
- call EnsShowDebugScr ;make debug screen visible
- call DoDrawDebugScr ;actually draw debug screen, can't
- ; wait for next call in GetCmd() in the
- ; case where user just executed a TRON
- ; stmt, we don't call GetCmd at all
- call ContContext ;activate "program counter's" context
- je TestGetCmd ;brif can't continue & no main program
- ;otx may point to beginning of next statement, get it back to current
- ;map UNDEFINED,0,1,2,3,... to 0,0,0,1,2,3,...
- mov ax,[grs.GRS_otxCONT]
- inc ax ;test for UNDEFINED
- je DoShowStmt ;if can't CONT, cur stmt is 0 of main
- dec ax ;restore ax = otxCONT
- je DoShowStmt ;map 0 to 0
- dec ax ;map 1,2,... to 0,1,...
- DoShowStmt:
- push [grs.GRS_oRsCur]
- push ax ;pass otxCur
- PUSHI ax,UNDEFINED ;determine column from otx
- call ShowStmt
- TestGetCmd:
- ;See if executor told user interface to not allow continuing.
- ;This is done when it wants to show where execution stopped
- ;(i.e. for error reporting) so it leaves grs.otxCur not UNDEFINED
- ;on entry to UserInterface. It is important that we not call CantCont
- ;before calling ShowStmt, or else ShowStmt has nothing to show.
- test [debugFlagsTmp],DEBUG_CANT_CONT
- je CantContNotSet
- call CantCont
- mov [actionFlags],FDM_GetCmd
- CantContNotSet:
- test [actionFlags],FDM_GetCmd
- je TestDoCmd
- ;Init debug screen if necessary, make it visible. This is necessary
- ;because DEBUG_END causes FDM_GetCmd to be set, but not FDM_ShowStmt.
- ;Continue calling GetCmd() until we get a command which we can execute
- ;without errors. GetCmd() interacts with user until user enters a
- ;direct-mode command (which may take the form of a menu selection, or
- ;accelerator key like SingleStep).
- ;This can cause context switches to different module and
- ;procedure text tables. It also invokes the text
- ;manager (which invokes the parser) to alter program text.
- ;
- GetCmdLoop:
- ;Turn off CMD_SW_RUN switch in case user specified /RUN <filename>
- ;in qb command line, and then got a runtime error. This will
- ;cause the SYSTEM statement to return to user interface rather than
- ;terminating qb.
- and [cmdSwitches],NOT CMD_SW_RUN
- call EnsShowDebugScr
- call EnterUserInterface ;enable userInterface interrupt handlers
- mov [otxContProg],UNDEFINED ;since this call to GetCmd can cause
- ; editing, and otxContProg isn't updated
- ; for pcode movement, we can't depend
- ; on it after this call.
- call GetCmd
- call WatchInfoUI ;update cWatch if necessary
- WatchWnOk:
- call ExitUserInterface ;restore runtime's interrupt handlers
- ;Make sure TxtDirect and SystemScan are unable to tie up so much
- ;variable table space that we are unable to execute very simple
- ;direct mode statements. This block of memory will be freed
- ;by UiReleaseSpace below. Be very careful when changing control
- ;flow below this point that all UiGrabSpace calls are balanced by
- ;UiReleaseSpace calls.
- TestDoCmd:
- call UiGrabSpace ; UiGrabSpace can take a lot of time...
- ; slows down tracing, and WATCH (when
- ; debug screen is visible)
- cmp [fDoCmd],0 ; fDoCmd can only be set as a result
- ; of calling GetCmd(). It is not
- ; set as a if user wants to TRACE
- je NoDirect ; brif all scanned and ready to execute
- ; (speed optimization for WATCH/TRACE)
- mov [fDoCmd],0
- call ContContext ;activate "program counter's" context
- ; for TxtDirect
- call TxtDirect ;Now parse & scan direct mode statement
- mov ax,[grs.GRS_otxCONT]
- mov [otxContProg],ax
- jmp SHORT CheckErr
- ;no direct mode stmt (we're tracing), just scan all text tables
- ;and set current context to grs.otxCONT
- NoDirect:
- call SystemScan
- test [actionFlags],FDM_ExecCmd
- je NoExecCmd
- ;some executor setup direct mode buffer, i.e. ExRunFile
- ;loaded program and put ExRunMain in grs.bdlDirect
- cmp [txtErr.TXER_errCode],0
- jne CheckErr ;dont set otxCONT if scan error
- mov ax,[grs.GRS_oMrsMain]
- mov [grs.GRS_oRsCONT],ax
- push ax
- call UiRsActivate ;activate oRs to continue
- sub ax,ax
- mov [grs.GRS_otxCur],ax
- mov [grs.GRS_otxCONT],ax ;ExStChain causes ExStCont to be
- ; invoked, which needs otxCONT=0
- dec ax
- mov [grs.GRS_fDirect],al ;execute pcode out of direct mode buf
- ;If we were tracing when we entered with DEBUG_EXEC_CMD,
- ; continue tracing. The only reason we entered DirectMode
- ; was to SystemScan and report errors.
- mov al,[debugFlagsTmp]
- and al,DEBUG_TRACE
- or [debugFlags],al
- jmp SHORT CheckErr ;report any parser/scanner errors
- NoExecCmd:
- call NeedContContext ;activate "program counter's" context
- ; if no main module, uierr is set
- je SHORT GetNextCmd ;brif CONT is not possible
- ;now see if TxtDirect/SystemScan encountered any errors
- CheckErr:
- mov ax,[txtErr.TXER_errCode]
- or ax,ax
- jne CmdErr ;brif parser/scanner errors
- ;We could get here without having called UiGrabSpace (if we were
- ;tracing, and all text tables were already scanned to SS_EXECUTE)
- ;If we didn't call UiGrabSpace, we are guarenteed that cGrab=0
- ;by assertion at entry of NextStmt().
- cmp [cGrab],0
- je NoneGrabbed1
- call UiReleaseSpace
- NoneGrabbed1:
- cEnd ;NextStmt
- CmdErr:
- inc ax ;test for UNDEFINED
- je GetNextCmd ;brif user wants to back out of cmd
- ; because it would prevent CONT
- dec ax ;restore ax=errCode
- mov [uierr],ax ;remember to report error in GetCmd's
- GetNextCmd: ; structure txtErr.xxx
- call UiReleaseSpace ;at this point, we know we called
- ; UiGrabSpace. Release it
- jmp GetCmdLoop
- ;*********************************************************************
- ; AskCantCont()
- ;
- ; Purpose:
- ; AskCantCont() is called by TextMgr when it is about to make an
- ; edit which would prevent continuing program execution.
- ; This function should not be called during exection. It is not
- ; valid (or useful) while the executor is in operation.
- ; If already impossible to continue (i.e. grs.otxCONT ==
- ; UNDEFINED) AskCantCont returns TRUE. Otherwise, the user is warned
- ; with a dialog box that this edit will prevent continuing.
- ; If the user says OK, grs.otxCONT is set to UNDEFINED
- ; and the context manager's CantCont() is called (which
- ; sets grs.otxCONT to UNDEFINED among other things.
- ; AskCantCont() then returns TRUE.
- ; If the user says CANCEL, the Debug screen is refreshed (discarding
- ; the current edit) and AskCantCont() returns FALSE.
- ;
- ; Exit:
- ; Returns FALSE if user wants to abort current edit, with
- ; condition codes set based on value in ax.
- ;
- ;*********************************************************************
- cProc AskCantCont,<PUBLIC,FAR>
- cBegin
- call fCanContUI
- je AcCantCont ;brif already can't continue
- ;display "This will prevent CONT, proceed anyway?"
- PUSHI ax,MB_OKCANCEL
- PUSHI ax,MSG_CantCont
- call MsgBoxStd
- cmp al,IDOK
- mov ax,0 ;prepare to return FALSE
- jne AcExit ; brif user wants to backout of edit
- call CantCont ;disable CONT
- AcCantCont:
- mov ax,sp ;return TRUE
- AcExit:
- or ax,ax ;set condition codes for caller
- cEnd ;AskCantCont
- ;*********************************************************************
- ; AskMakeRem()
- ; Purpose:
- ; The user tried to insert a blank line before a SUB/FUNCTION line.
- ; This would cause us grief when we tried to ASCII save/load the
- ; file, because blank lines delimit the block of comments that are
- ; to remain with the SUB/FUNCTION. The case where this is important
- ; is when the user has a module with no module level code, but still
- ; wants to have a block of comments at module-level.
- ; If we allowed blank lines in the block of comments that we move
- ; into the SUB during ASCII load, this block of module comments
- ; would be moved as well.
- ;
- ; Exit:
- ; Returns FALSE if user wants to abort current edit, with
- ; condition codes set based on value in ax.
- ;
- ;*********************************************************************
- cProc AskMakeRem,<PUBLIC,FAR>
- cBegin
- ;"Blank lines not allowed before SUB/FUNCTION line. Is remark ok?"
- PUSHI ax,MB_OKCANCEL
- PUSHI ax,MSG_MakeRem
- call MsgBoxStd
- sub ax,IDCANCEL ;ax=0 if user said CANCEL
- cEnd
- ;*********************************************************************
- ; NotSaved(), NotSavedInc(), NotSavedIncSav()
- ;
- ; Purpose:
- ; Called by Context Mgr's NewStmt and SYSTEM executor when it is about
- ; to discard module(s).
- ; If any module has been modified since last saved, a dialog box asks
- ; the user if the module(s) are to be saved.
- ; If user selects Yes,
- ; CmdFileSaveAll is invoked
- ; If any errors occur during CmdFileSaveAll, an error code is returned.
- ; else the function returns 0.
- ; If user selects Cancel,
- ; it returns MSG_GoDirect, which is not trappable,
- ; and eventually gets us back into user interface, which does not
- ; report this special MSG_GoDirect runtime error.
- ; If user selects No,
- ; the function returns -2.
- ; Else if no modules need to be saved,
- ; it just returns -1.
- ;
- ; NotSavedInc just does this for INCLUDE mrs's with the prompt:
- ; "Modified $INCLUDE files must be saved before running. Save them now?"
- ;
- ; NotSavedIncSav just does this for INCLUDE mrs's with the prompt:
- ; "Save modified $INCLUDE files first?"
- ;
- ; Entry:
- ; none
- ;
- ; Exit:
- ; returns 0 if all modified files were saved without error,
- ; returns -1 if no files were modified (and thus not saved)
- ; returns -2 if user responded NO to the prompt
- ; returns MSG_GoDirect if user responded CANCEL to the prompt
- ; returns error code in ax if an I/O error occurred while trying to save.
- ;
- ;*********************************************************************
- PUBLIC NotSaved, NotSavedInc, NotSavedIncSav
- NotSavedIncSav PROC FAR
- mov al,2
- SKIP2_PSW ;skip following mov al,1 instruction
- NotSavedIncSav ENDP
- NotSavedInc PROC FAR
- mov al,1
- SKIP2_PSW ;skip following mov al,0 instruction
- NotSavedInc ENDP
- NotSaved PROC FAR
- mov al,0
- ;Common entry for NotSaved, NotSavedInc
- push si ;save caller's si,di
- push di
- cbw ;ax = fInclude
- xchg di,ax ;di = fInclude
- mov [uierr],0 ;need to init uierr, because it
- ; could be non-zero if user selected
- ; File/Exit with dirty line in editor.
- call EnStaticStructs ;activate static prsCur, mrsCur, txdCur
- ;cx = 0 if no action taken
- push ax ;remember if we need to call DisStatic..
- push WORD PTR([grs.GRS_fDirect])
- push [grs.GRS_oRsCur] ;save caller's oRs (for UiRsActivate)
- mov si,UNDEFINED
- .erre IDCANCEL - UNDEFINED ; (it will be tested below)
- .erre IDNO - UNDEFINED ; (it will be tested below)
- .erre IDYES - UNDEFINED ; (it will be tested below)
- ;pass UNDEFINED to UiRsActivate so no mrs is active
- cCall UiRsActivate,<si>
- NsLoop:
- call NextMrsFile_All ; activate next mrs that has a FILE
- ; NextMrsFile doesn't pick up an
- ; empty <Untitled> module.
- inc ax ;test for UNDEFINED
- je NsDone ;brif done with all mrs's
- test [mrsCur.MRS_flags2],FM2_Modified
- je NsLoop ;brif module is unchanged
- or di,di
- je SaveIt ;brif saving ALL files, not just INCLUDE
- test [mrsCur.MRS_flags2],FM2_Include
- je NsLoop ;brif this isn't an INCLUDE file
- SaveIt: ;Got at least 1 module which needs to be saved - ask user
- PUSHI ax,MB_YESNOCANCEL
- mov ax,MSG_NotSavedAll
- .errnz MSG_NotSavedInc - MSG_NotSavedAll - 1
- .errnz MSG_NotSavedIncSav - MSG_NotSavedAll - 2
- add ax,di ;bump to MSG_NotSavedInc if fInclude
- push ax
- call MsgBoxStd ;al = reply
- cmp al,IDYES
- xchg si,ax ;save user's response in si
- jne NsNoSave ;brif NO or CANCEL reply
- xor ax,ax ; no title for subsequent dialog
- xchg ax,szDialogTitle ; boxes (save all)
- push ax ; save current dialog box title title
- cCall CmdFileSaveAll,<di> ;save all modified modules
- pop szDialogTitle ; restore current dialog box title
- or ax,ax
- jne NsNoSave ;brif user didn't select CANCEL in
- ; Save dialog, and no I/O errors
- mov si,IDCANCEL
- NsNoSave:
- ;At this point, si = UNDEFINED if no files needed to be saved,
- ; IDNO if user chose to not save unsaved files,
- ; IDCANCEL if user wants to back out of current operation,
- ; IDYES if all files were saved,
- ; (uierr = non-zero if any errors occurred while saving)
- ;
- NsDone:
- call UiRsActivate ;activate stacked register set.
- pop ax ;ax = saved value of grs.fDirect
- mov [grs.GRS_fDirect],al ;restore it
- pop cx ;cx=0 if static structs were already
- jcxz NsStatic ; enabled when NotSaved was called
- call DisStaticStructs ;ensure static structs deactivated
- NsStatic:
- ;If CANCEL button was pressed, return untrappable runtime error
- ;which will get us back into user interface if we were in the
- ;middle of CHAIN/RUN <file>
- mov ax,[uierr]
- or ax,ax
- jne NsExit ;brif I/O error during save
- mov al,MSG_GoDirect
- cmp si,IDCANCEL
- je NsExit ;brif user responded CANCEL
- sub ax,ax ;ax = 0
- cmp si,IDYES
- je NsExit ;return 0 if any/all files saved
- dec ax ;ax = -1
- inc si ;test for UNDEFINED
- je NsExit ;return -1 if no files need to be saved
- dec ax ;if user said NO, return -2
- NsExit:
- pop di ;restore caller's si,di
- pop si
- or ax,ax ;set condition codes for caller
- ret
- NotSaved ENDP
- ;**************************************************************************
- ; UiGrabSpace
- ; Purpose:
- ; Don't let user enter such a long program that he can't even execute
- ; a SYSTEM, CLEAR, or SETMEM statement.
- ; VERY IMPORTANT: Every call to UiGrabSpace MUST be balanced by
- ; a subsequent call to UiReleaseSpace. Make sure the code in
- ; between can't take pathological branches (RtError) or non-pathological
- ; branches around the UiReleaseSpace call.
- ;
- ; The goal of user-interface tight-memory-management is to never
- ; let the user get so low on memory that they get locked up
- ; (i.e. so tight they can't even delete some text or unload a module
- ; or execute a direct-mode clear/setmem statement). If they
- ; get locked up to the point where they can't save their file,
- ; it is as bad as crashing.
- ; Part of the strategy involves reserving memory whenever the
- ; user could potentially be making long-term memory commitments
- ; (i.e. loading a new module or inserting a new line of text)
- ; Another part of it is having UiGrabSpace release any
- ; "discretionary memory" it can when we run out of memory.
- ;
- ;**************************************************************************
- cProc UiGrabSpace,<PUBLIC,NEAR>
- cBegin
- inc [cGrab]
- call GrabSpace
- or ax,ax
- jne GotSpace ;brif got space
- ;We're very low on memory
- ;Release any discretionary info we can to give us the space we need.
- call AlphaORsFree ;release sorted alphabetised list
- ; of modules/procedures - we build
- ; it whenever we need it.
- extrn FreeCmdHistory:near
- call FreeCmdHistory ;release command window's history
- call GrabSpace ;should never fail.
- GotSpace:
- cEnd
- ;**************************************************************************
- ; UiReleaseSpace
- ; Purpose:
- ; We're not in an area where we could make long-term memory commitments
- ; that would prevent us from doing a SYSTEM, CLEAR, or SETMEM statement.
- ; Unlike ReleaseSpace(), this function doesn't release the space
- ; unless all nested callers of UiGrabSpace have released their hold.
- ;
- ;**************************************************************************
- cProc UiReleaseSpace,<PUBLIC,NEAR>
- cBegin
- DbAssertRelB [cGrab],ne,0,UI,<UiReleaseSpace: cGrab=0>
- dec [cGrab]
- jne NotDone ;brif not everyone that called
- call ReleaseSpace ; UiGrabSpace has called UiReleaseSpace
- NotDone:
- cEnd
- ;**************************************************************************
- ; UiAlphaORsBuild
- ; Purpose:
- ; Since AlphaORsBuild allocates a heap entry, we need to make sure
- ; it doesn't encroach upon the minimum heap the user interface needs.
- ; Exit:
- ; ax = 0 if out-of-memory
- ;
- ;**************************************************************************
- cProc UiAlphaORsBuild,<PUBLIC,NEAR>
- cBegin
- call AlphaBuildORs
- cEnd
- ;**************************************************************************
- ;**************************************************************************
- ;EnMenuCall
- ;Purpose:
- ; Makes a far call to EnableMenuItem.
- ;Entry:
- ; AL = Menu Item Name
- ; DI = Enable / Disable item
- ;Exit:
- ; None
- ;Usage:
- ; Saves AX,BX,CX,DX.
- ;**************************************************************************
- cProc EnMenuCall,<NEAR>,<AX,BX,CX,DX>
- cBegin
- xor ah,ah ; AX = MenuItemName
- cCall EnableMenuItem,<ax,di>
- cEnd
- ;*************************************************************************
- ;ChMenuCall
- ;Purpose:
- ; Makes a far call to CheckMenuItem
- ;Entry:
- ; AL = Menu Item Name
- ; DI = Enable / Disable item
- ;Exit:
- ; None
- ;Usage:
- ; Per Convention
- ;*************************************************************************
- cProc ChMenuCall,<NEAR>
- cBegin
- xor ah,ah ; AX = MenuItemName
- cCall CheckMenuItem,<ax,di>
- cEnd
- ;**************************************************************************
- ; MenuEnable - rewritten in assembler from c code in uictl.c
- ; Purpose:
- ; Enables Display in Main Window.
- ; Entry:
- ; None
- ; Exit:
- ; None
- ; Uses:
- ; PerConvention
- ;
- ;**************************************************************************
- cProc MenuEnable,<PUBLIC,FAR>,<DI,SI>
- cBegin
- cCall UiRsActivateWnd ; Tell context manager to activate
- ; active window's register set
- ; The Edit Field Structure is updated by every cursor movement
- ; indicating system's current state, in terms of options available.
- ; For example at the begining of qb sesbx on with blank screen, i.e.
- ; no files loaded, you would not have any 'Edit' options available.
- ; the moment you type anything 'Undo' becomes available. Further
- ; until you select and 'cut', 'paste' would not be availble.
- mov bx,pwndAct ; bx = pointer to active window's reg set
- mov bx,[bx.pefExtra] ; bx = * to edit field structure
- push bx ; Save for later use
- mov bl,[bx.EF_fSelection]
- xor bh,bh ; BX = fSelection from edit field structure
- xor cx,cx ; if pwndAct != wndCmd then ListWnd=False
- cmp pwndAct,DATAOFFSET wndCmd; else ListWnd=True
- je fIsListWndSet ; CX = ListWnd Status
- dec cx
- fIsListWndSet:
- xor dx,dx ; So no changes like cut, paste
- cmp pwndAct,DATAOFFSET wndHelp ; are allowed. fChangeable flag
- je fChangeableSet ; specifies if user is in Help.
- dec dx
- fChangeableSet: ; DX = fChangeable
- mov si,dx ;SI = fChangeable
- mov di,bx ; DI = fSelection
- mov al,midEditCopy ; Set up Edit/Copy
- cCall EnMenuCall
- and di,si ; DI = fSelection AND fChangeable
- mov al,midEditCut ; Setup Edit/Cut
- call EnMenuCall
- mov al,midEditClear ; Set up Edit/Clear
- cCall EnMenuCall
- mov al,fPasteOk
- cbw
- xchg di,ax ; DI = Paste Command availability status
- and di,si ; DI = fPasteOK AND fChangeable
- mov al,midEditPaste ; Set up Edit/Paste
- cCall EnMenuCall
- mov di,cx ; DI = ListWindow Status
- mov al,midSearchFind ; Set up Search/Find
- cCall EnMenuCall
- mov al,midSearchNext ; Set up Search/Next
- cCall EnMenuCall
- and di,si ; DI = ListWnd AND fChangeable
- mov al,midSearchChange ; Set up Search/Change
- cCall EnMenuCall
- ; bx was save way above.
- pop bx ; Restore bx = * Edit Field Structure
- mov al,fSyntaxCheck
- cbw ; AX = Syntax Check options Selected?
- xchg di,ax ; DI = Syntax Check options
- mov al,midOptnsSyntax ; in Options of menus
- cCall ChMenuCall
- xor cx,cx
- xor ah,ah
- mov al,[mrsCur].MRS_flags2 ;get mrs file flags
- and al,FM2_Include+FM2_NoPcode ;is this a pcoded window?
- jnz SetFalse ; brif not
- dec cx
- SetFalse:
- mov dx,si ; DX = fChangable
- and dx,cx ; fIsCodeWnd = Pcode & Changeable
- ; DX = fIsCodeWnd, CX = fPcode
- mov di,dx ; DI = IsCodeWnd
- mov al,midEditNewSub ; Setup Edit/New Sub
- cCall EnMenuCall
- mov al,midEditNewFunc ; Setup Edit/New Function
- cCall EnMenuCall
- mov al,midDebugToggleBp ; Set up Debug/Toggle BreakPoint
- cCall EnMenuCall
- mov al,midGoUntilCursor
- cCall EnMenuCall
- mov di,fIsProgram ; DI = TRUE if prog, FALSE if doc
- mov al,midViewSubs ; Setup View/Subs
- cCall EnMenuCall
- mov al,midRunStart ; Setup Run/Start
- cCall EnMenuCall
- mov al,midRunRestart ; Setup Run/Retart
- cCall EnMenuCall
- mov al,midRunContinue ; Setup Run/Continue
- cCall EnMenuCall
- mov al,midStep ; Setup Debug/Step
- cCall EnMenuCall
- mov al,midPStep ; Setup Debug/PStep
- cCall EnMenuCall
- mov al,midDebugClearAllBp ; Setup Debug/Clear all Bp
- cCall EnMenuCall
- mov al,midDebugTraceOn ; Setup Debug/Trace On
- cCall EnMenuCall
- mov di,dx ; di = fIsCodeWnd
- or di,di ; is this a code window?
- je NotCodeWnd ; brif not
- cCall fCanContUI ; returns AX = Execution State
- xchg ax,di ; False if Not in Code Window
- NotCodeWnd:
- mov al,midDebugSetNextStmt; Set up Set Next Stmt command
- cCall EnMenuCall ; in debug options of menus
- mov al,fTraceOn
- cbw
- xchg di,ax ; di = Trace On Flag
- mov al,midDebugTraceOn ; Setup Debug/Trace On
- cCall ChMenuCall
- cEnd
- sEnd UI
- end