📄 erhandlr.asm
字号:
TITLE ERHANDLR - Error trapping for 8086 Basic Compiler
page ,132
;***
; ERHANDLR - Error trapping for 8086 Basic Compiler
;
; Copyright <C> 1987, Microsoft Corporation
;
;Purpose:
; This module contains the run-time support for on-error handling.
;
;******************************************************************************
INCLUDE switch.inc ;Rutime switch file
INCLUDE rmacros.inc ;General runtime macros
useSeg _BSS
useSeg _DATA
useSeg ER_TEXT
useSeg NH_TEXT
INCLUDE seg.inc ;Segment definitions
INCLUDE addr.inc ;Frame and module specific data definitions
INCLUDE baslibma.inc ; need the SKIP macro
INCLUDE idmac.inc
INCLUDE messages.inc ;error message label definitions
INCLUDE stack.inc
sBegin _BSS
externW b$errnum ; Error number encountered
externB b$inonerr ; flag indicating we're in an on error
externW b$curlevel ; current program level
externW b$curframe ; current BASIC frame parameter
externD b$errmod ; error module name address
externW b$cCSubs
externW b$cNonQBIFrames
sEnd _BSS
sBegin NH_TEXT
externNP B$STDALCALLTMP
sEnd NH_TEXT
externFP B$CONERR
externFP B$ClearRange
sBegin ER_TEXT
externNP B$GETMODCODE ;Get module specific code data.
externNP B$IONERR ; interpreter-specific on error handler
externNP B$IErrSaveState
externNP B$IErrRestState
externNP B$ERR_RE
assumes CS,ER_TEXT
;******************************************************************************
;
; Runtime Error Handling
;
; ON ERROR processing
;
; When an error occurs, the common runtime error handler will first call
; B$ONERR to allow us to attempt to process any ON ERROR statement that
; may apply. To get the runtime context, we rely on the
; fact that the passed frame pointer points to stack entries as follows:
;
; +--> +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
; User | |
; Program \ Defined by Compiler/Interpreter & Runtime \
; Frame | | <-+
; +--> +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
; | | |
; \ User program local storage \ |
; \ Gosubs on stack on common runtime entry \ |
; | | |
; +--> +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
; | | | |
; | \ Parameters to Common Runtime Entry \ |
; Common | | |
; Runtime +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
; Entry | CS | |
; Frame + Far return address into BASIC program code + |
; | | IP | |
; | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
; | | BP value on entry to Common Runtime | --+ <- pframe
; +--> +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
;
; From this frame we get the the user program frame (defined elsewhere), from
; which we can find the data structure containing the ON ERROR address for the
; user program if it exists. If this does exist we can perform the ON ERROR.
;
; To process the ON ERROR, we first use the frame to determine the line number
; at which the error occured. The stack is then reset to an appropriate level,
; and we perform the GOSUB.
;
; See erproc.asm for more information on error handling.
;
;******************************************************************************
SUBTTL B$ONERR -- Find ON ERROR handler if possible
PAGE
;
;***
;B$ONERR - Find most recent BASIC error handler if any
; Call via [b$pONERR] for /O granularity.
;
;Purpose:
; This routine will search through BASIC stack frames starting with
; the one in which the error occurred until it finds one with an
; error handler or until there are no more BASIC stack frames left
; to check. It will also stop if it encounters a BASIC event handler
; at the previous level. If it finds an error handler, it will adjust
; the environment to look as if the error occurred at the current
; statement of that module. Otherwise, nothing is changed.
;
;Entry:
; [b$curframe] valid.
; [BX] = frame parameter preceding [b$curframe] in BP chain.
; IF EI_QB THEN [b$cCSubs], [b$cNonQBIFrames] valid.
; [b$errnum] <> 98xxh.
;
;Exit:
; No error handler found.
;
;Uses:
; Per convention.
;
;Preserves:
; None.
;
;Exceptions:
; None.
;
;******************************************************************************
cProc B$ONERR,<PUBLIC,NEAR>
cBegin
DbAssertRelB <BYTE PTR [b$errnum+1]>,NE,<98h>,ER_TEXT,<Bad errnum in B$ONERR>
DbAssertRel <[BX]>,E,<[b$curframe]>,ER_TEXT,<Bad frame parameter in B$ONERR>
PUSH [b$curframe]
PUSH [b$curlevel]
PUSH [b$cCSubs] ; save original [b$cCSubs]
PUSH [b$cNonQBIFrames] ; save original [b$cNonQBIFrames]
cCall B$IErrSaveState ; allow interpreter to save state
;
; This is the main loop that walks the BASIC frame chain and calls either
; B$IONERR or B$NCONERR as appropriate.
;
ONERR_10:
CMP [b$curframe],0
DJMP JZ ONERR_EXIT ; reached the end of BASIC chain
;
; Skip over non-BASIC frames in the BP-chain to find the frame immediately
; preceding the BASIC frame in the BP chain.
;
ONERR_20:
MOV DX,BX ;DX = old value of BX
MOV BX,[BX] ;use bp chain
DbAssertTsT BX,Z,1,ER_TEXT,<Odd BP value found in B$ONERR>
DbAssertRel BX,NZ,0,ER_TEXT,<Bad BP chain in B$ONERR>
CMP BX,[b$curframe]
JNE ONERR_20
; [DX] = frame preceding [BX]
PUSH DX ; parameter to B$xONERR
CMP [b$cCSubs],0 ; check for interpreting module
JNZ ONERR_30
cCall B$IONERR ;returns AX = 0 => quit
JMP SHORT ONERR_40
ONERR_30:
cCall B$NCONERR ; returns AX = 0 => quit
ONERR_40:
OR AX,AX ;AX <> 0 => no error handler
JZ ONERR_EXIT ;quit - restore org. env. & return
DbAssertRelB <[b$inonerr]>,Z,0,ER_TEXT,<b$inonerr was TRUE in B$ONERR loop>
DEC [b$curlevel]
JS ONERR_EXIT ; kind of redundent check
; addjust [b$cCSubs] to reflect current frame
;
CMP [b$cCSubs],0 ; are we already interpreting?
JZ ONERR_60 ; brif interpreting
DEC [b$cCSubs] ; adjust [b$cCSubs]
JNZ ONERR_60 ; need to adjust b$cNonQBIFrames?
DEC [b$cNonQBIFrames] ; yes -- decrement
ONERR_60:
;
; Walk the BASIC frame chain to check for an error handler in the next
; previous BASIC module.
;
MOV BX,[b$curframe]
MOV AX,[BX].FR_BFRAME ;[AX] = NEXT BASIC FRAME
MOV [b$curframe],AX ;update b$curframe
DJMP JMP SHORT ONERR_10
; error handler not found
ONERR_EXIT: ; restore original error state
cCall B$IErrRestState ; allow interpreter to restore state
POP [b$cNonQBIFrames] ; restore original [b$cNonQBIFrames]
POP [b$cCSubs]
POP [b$curlevel]
POP [b$curframe]
cEnd
SUBTTL B$CONERR - Compiler ON ERROR Processing - Enhanced version
PAGE
;***
; B$CONERR - Handle ON ERROR Processing -new version
; B$NCONERR - New error handling COMPILER ON ERROR processing
; void B$CONERR(U2 pframe)
;
;Purpose:
; If enabled, performs ON ERROR transfer of control within the user program.
; This is the NEW version of B$CONERR. Changes and enhancements should only be
; made to this version. The old version in erbcproc.asm should not be changed.
; It is necessary for backward compatibility with QB4 QLB's.
;
; We check the module data area for the address of an ON ERROR routine. If
; one is available, we:
; - Save the error address (offset & segment)
; - Get and save the statement number
; - If the error was "Out Of Memory" reset the gosub count to zero
; - Deallocate local storage and temp. string descriptors as necessary
; - Reset the stack (SP & BP) to execution levels:
; BP = [b$curframe].
; SP = [b$curframe] - <local storage> - (<GOSUB level> X 2).
; - Perform the GO TO
;
;Entry:
; pframe = Pointer to a frame one level below b$curframe
; [b$curframe] = basic-level frame.
; [b$errnum] = error number
;
;Exit:
; [b$errmod] = far address of module name
; IF AX = 0 THEN
; no error trapping is possible
; ELSE
; no error handler found
; ENDIF
; IF an error handler is found THEN
; [b$erradr] = far address of error in the compiled code
; [b$errlin] = line number of error
; ENDIF
;
; Doesn't return if an active BASIC error handler is available.
;
;******************************************************************************
cProc B$NCONERR,<PUBLIC,NEAR> ; new name for B$CONERR
parmW pframe ; frame pointer one level below error time
cBegin
XOR AX,AX ; Get first byte
cCall B$GETMODCODE ; AL = first byte, [BX] = code segment
MOV AX,CX ; CX = module header offset (0 for DOS3)
ADD AX,OF_MOD
MOV WORD PTR [b$errmod],AX ; Far pointer to module name
MOV WORD PTR [b$errmod + 2],BX
CMP [b$inonerr],FALSE ; Are we in on-error handler?
DJMP JNZ BCONERR_90 ; Jump if we are, error-within-error occurred
MOV AX,b$errnum ; get error number
OR AH,AH ; See if valid trappable error
JNZ BCONERR_90 ; Jump if [b$errnum] > 255, internal error
XCHG DX,BX ; DX = user code segment
PUSH CX ; save module header offset
POP BX ; restore module header offset
PUSH DS ; save DGROUP
MOV DS,DX ; [DS] = user code seg
MOV BX,DS:[BX].OF_DAT ; [BX] = mod specif data offset
POP DS ; [DS] = DGROUP restored
MOV CX,[BX].OFD_ONERROR ; [CX] = on error address
MOV BX,pframe ; [BX] = pointer to rt entry frame
DbAssertRel DX,E,<[BX].FR_RETSEG>,ER_TEXT,<Bad user code seg in B$NCONERR>
PUSH BX ; push frame parameter for old B$CONERR
JCXZ BCONERR_80 ; Jump if no address specified - quit
;
; The error can be trapped. Set the "in on error handler" flag, and call the
; appropriate on error handler.
;
MOV AX,[BX] ; AX = [b$curframe]
MOV BX,AX ; BX = AX = [b$curframe]
SUB AX,[BX].FR_CLOCALS ; [AX] = [b$curframe] - locals/temps
SUB AX,[BX].FR_GOSUB ; [AX] = [b$curframe] - locals - gosubs/2
SUB AX,[BX].FR_GOSUB ; [AX] = [b$curframe] - locals - gosubs
SUB AX,FR_SIZE+2 ; can combine 3 instructions for QB case
PUSH BP ; old_BP = FROM parameter to B$ClearRange
PUSH AX ; new_SP-2 = TO parameter to B$ClearRange
CALL B$ClearRange ; deallocate local variables on STACK
MOV AX,[b$curlevel] ; deallocate temp strings > [b$curlevel]
CALL B$STDALCALLTMP ; dealloc temp strings no longer needed
BCONERR_80: ; no error handler - check for event handler
cCALL B$CONERR ; frame parameter pushed way above
MOV [b$inonerr],FALSE ; necessary after call to old B$CONERR
; check if the curent frame is for an event handler
MOV BX,[b$curframe]
CALL B$CEvtHndlr ; returns AX = 0 if an event handler frame
SKIP 2 ; JMP SHORT BCONERR_100
BCONERR_90:
XOR AX,AX ; return AX = 0 means process fatal error
BCONERR_100:
cEnd
SUBTTL RESUME helpers
PAGE
;***
; B$RESUMED - Set up for RESUME of some sort
; Interpreter Entry
; Moved here from erproc.asm
;
;Purpose:
; If RESUME [linenumber|linelabel], reset program level to 0 and deallocate
; all temporary strings.
; Ensure that error is in progress, and then clean flags
;
;Entry:
; [AX] = RESUME type, 0 = RESUME [linenumber|linelabel]
; non-0 = other RESUME
; (parameter on stack for OE_WIN)
;
;Exit:
; if ready to resume. Flags cleared
;
;Exceptions:
; Aborts if no error in progress
;
;******************************************************************************
cProc B$RESUMED,<PUBLIC,FAR,FORCEFRAME> ; moved here from erproc.asm
cBegin
OR AX,AX ;resume to level 0?
JNZ NotLev0 ;go if not
MOV b$curlevel,AX ;set current level to main level (0)
CALL B$STDALCALLTMP ;dealloc all temp strings
NotLev0:
cCall B$RES_SETUP ; Perform Function
cEnd
;***
; B$RES_SETUP - Set up for RESUME of some sort
; Moved here from erproc.asm
;
;Purpose:
; Ensure that error is in progress, and then clean flags
;
;Entry:
; none
;
;Exit:
; if ready to resume. Flags cleared
;
;Exceptions:
; Aborts if no error in progress
;
;******************************************************************************
cProc B$RES_SETUP,<NEAR,PUBLIC> ; moved here from erproc.asm
cBegin
XOR CX,CX
MOV [b$errnum],CX ; if error below, this gets reset anyway
XCHG CL,[b$inonerr]
JCXZ RES_SETUP_OOPS ; brif wern't in error handler
cEnd
RES_SETUP_OOPS:
POP AX ; Discard local return
JMP B$ERR_RE
;***
;B$CEvtHndlr - Checks if a frame is an event handler
;
;Purpose:
; Determine whether the frame is for an active compiled event handler.
; Moved this code back with [6].
;
; NOTE: this code is duplicated in ERROR.ASM.
;
;Entry:
; BX = pointer to the COMPILED BASIC frame to check (ie. [b$curframe]).
;
;Exit:
; AX = 0 IFF an event handler is detected.
;
;Uses:
; AX, BX.
;
;Exceptions:
; None.
;
;******************************************************************************
cProc B$CEvtHndlr,<NEAR>
cBegin
;
; This is the compiler-specific logic to check for a BASIC event handler:
;
MOV AX,[BX].FR_GOSUB ; AX = count of GOSUBS
DbAssertRel AX,GE,0,ER_TEXT,<More than 32767 GOSUBs in B$CEvtHndlr> ;
DEC AX ; if AX = 0, return AX <> 0
JS No_handler ; no gosubs => no event handler
SUB BX,[BX].FR_CLOCALS ; BX = BX - number of local bytes
MOV AX,[BX-FR_SIZE-2] ; AX = 0 IFF event handler
No_Handler:
cEnd ; return
sEnd ER_TEXT
END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -