📄 txtutil.asm
字号:
;pass ptr to buffer to ListLine
push [lnIncl] ;pass current line
call OtxOfLn ;ax = text offset to start of line
push ax
call OtxNoInclPrev ;ax = otx of $INCLUDE line if this
; line is from an include file
push ax ;pass text offset
lea ax,bdBuf
push ax ;can't use bdpSrc, because editor
; may have dirty copy of a line in it.
mov [psdLsIncl],si ;causes ListLine to load sdFilenameInc
call ListLine ; with arg to $INCLUDE
sub ax,ax
mov [psdLsIncl],ax
cmp [fDoIt],ax
mov ax,[si.SD_cb] ;ax = non-zero if $INCLUDE was found
je TviExit ;return if fDoIt was FALSE
;should never be called with fDoIt true if line contains no $INCLUDE
DbAssertRel ax,ne,0,CP,<TxtViewIncl: err2>
push si ;pass &sdFilenameInc
PUSHI ax,LF_ViewIncl ;pass file type
inc [fLoadInclude] ; inform LoadFile of $INCLUDE
call LoadFile ;ax = error code
dec [fLoadInclude] ; reset flag
TviExit:
cEnd
;**************************************************************
; SetCompSwitches
; Purpose:
; Called by user interface when it is about to invoke
; Compiler to produce an EXE file. This function
; scans the current text table's pcode, and sets the
; following masks in the global static variable
; 'compSwitches' as follows:
; COMP_SW_E is set for each module which has ON ERROR stmt
; COMP_SW_X is set for each module which has RESUME [NEXT] stmt
; COMP_SW_V is set if any module has ON <event> stmt
; COMP_SW_W is set if any module has ON <event> stmt
;
; Exit:
; ax is always non-zero (so it can be called by ForEach...)
;
;**************************************************************
cProc SetCompSwitches,<PUBLIC,FAR>
cBegin
call B$GetCompSwitches ; AX nonzero if QLB used /V or /W
or ax,ax
jz ScLoop ;brif no /V or /V in QLB
or [compSwitches],COMP_SW_V + COMP_SW_W ;set /V/W switches
sub ax,ax ;start at text offset 0
ScLoop:
push ax ;pass otxCur
PUSHI ax,<CODEOFFSET tOpCompSw>
call TxtFindNextOp ;ax = otx to next opcode of interest
;dl = txtFindIndex
cmp dl,CSW_opEot
je ScExit ;brif reached opEot
mov bl,COMP_SW_E
.errnz CSW_opStOnError
or dl,dl
je ScOrMask
mov bl,COMP_SW_V + COMP_SW_W
cmp dl,CSW_EventMax
jbe ScOrMask ;brif ON <event> or <event> ON
; If we fall through to here we have some sort of RESUME statement.
; All variants except one require /X. The exception is opStResume
; with an operand other than UNDEFINED. In this case /E is sufficient,
; /X is overkill. Because generated code for /E is significantly
; smaller than /X we check for that special case here.
mov bl,COMP_SW_X ; assume not special case
cmp dl,CSW_opStResume ; found an opStResume?
jne ScOrMask ; no, can't be special case
GetSegTxtTblCur ; es = seg adr of current txt tbl
xchg ax,bx ; es:bx = oTx of opStResume
cmp word ptr es:[bx+2], UNDEFINED ; "RESUME 0" statement?
xchg ax,bx ; bl = /X again
je ScOrMask ; brif it's "RESUME 0", need /X
mov bl,COMP_SW_E ; got it! only need /E
ScOrMask:
or [compSwitches],bl
jmp SHORT ScLoop
;we know ax is non-zero because there is always an opEndProg before an opEot
; in every text table
ScExit:
cEnd
;**************************************************************
; ONamOVarRudeOrParse
; Purpose:
; Get the oNam for a variable
; Entry:
; parm1 = oNam if txdCur.scanState == SS_RUDE
; = oVar if txdCur.scanState == SS_PARSE
; Exit:
; ax = variable's oNam
;
;**************************************************************
cProc ONamOVarRudeOrParse,<FAR,PUBLIC>
ParmW oNamoVar
cBegin ONamOVarRudeOrParse
mov ax,[oNamoVar]
DbChk ConStatStructs ;ensure static structures
cmp [txdCur.TXD_scanState],SS_RUDE
jae NoVarConv ; brif table is in rude-edit state
DbChk oVar,ax
xchg ax,bx ;bx is now the oVar
add bx,[mrsCur.MRS_bdVar.BD_pb] ;bx points to entry for variable
mov ax,VAR_oNam[bx] ;fetch oNam from variable table
NoVarConv: ;ax = variable's oNam
DbChk oNam,ax ;pass oNam from op[A]Idxxx opcode
cEnd ONamOVarRudeOrParse
;--------------------------------------------------------------
; Functions which let identifiers with periods (like A.B)
; be either a scalar/array (for compatibility) or a record
; element (to support records with C-like syntax).
; In some future version of BASIC, it is conceivable that
; A.B will always mean 'element of record'.
;--------------------------------------------------------------
;**************************************************************
; ONamOfAs
; Purpose:
; Given a pointer to an opAsType(oNamTyp) op[A]VtRef(oNamId),
; which is produced for syntax like 'id as user-type' in a DIM
; type statement, return the oNam for id.
; Entry:
; ax = text offset into current text table to opAsType pcode
; current text table's scan state is either SS_RUDE or SS_PARSE
; Exit:
; carry is set if opAsType is from expression in TYPE/END TYPE block
; else ax = oNam of id
;
;**************************************************************
ONamOfAs PROC NEAR
DbAssertRelB [txdCur.TXD_scanState],ne,SS_EXECUTE,CP,<ONamOfAs: bad scanState>
xchg bx,ax ;bx = text offset
GetSegTxtTblCur ;[42]es = seg addr of current txt tbl
cmp WORD PTR es:[bx-4],opElemRef
stc ;assume we are in a TYPE block
;note JE ONLY checks psw.z.
je OaExit ;brif in TYPE/END TYPE block
mov ax,es:[bx+6] ;ax = opcode following opAsType
mov dx,es:[bx+8] ;dx = oNam/oVar operand if scalar
.erre opIdLd LT opVtRf
.erre opAIdLd GT opVtRf
.erre opAVtRf GT opVtRf
cmp ax,opVtRf
jle OaGotONam ;brif got opVtRf or opIdLd
mov dx,es:[bx+10d] ;dx = oNam/oVar operand for array
OaGotONam:
push dx ;pass parm = oNam/oVar
call ONamOVarRudeOrParse ;ax = variable's oNam
clc
OaExit:
ret
ONamOfAs ENDP
;**************************************************************
;ChkAsInTbl
;Purpose:
; Checks to see if there are any refs to oNam AS in this
; text table, for a particular oNam. Any pcode within
; a range which is being deleted is ignored.
; If oNam AS is found within a TYPE block, it is ignored,
; because type elements have their own name space.
;Entry:
; oNamAs - oNam in question
; oRsAs - identifies text table where text is being deleted.
; otxAsDelStart - offset to 1st byte being deleted
; otxAsDelEnd - offset to last byte being deleted
;Exit:
; If oNam AS is found, returns 0
;
;**************************************************************
ChkAsInTbl PROC NEAR
push si ;preserve caller's si
sub si,si ;initial text offset = 0
CiLoop:
push si
PUSHI ax,<CODEOFFSET tOpAsType>
call TxtFindNextOp ;ax points to next opcode of interest
cmp dl,ASTYPE_opEot
je CiDone ;brif done with text table
mov si,ax ;si points to current opcode
mov cx,[oRsAs]
cmp cx,[grs.GRS_oRsCur]
jne CiCountIt ;brif it's not being deleted
cmp ax,[otxAsDelStart]
jb CiCountIt ;brif it's not being deleted
cmp ax,[otxAsDelEnd]
jb CiLoop ;brif it's being deleted
CiCountIt:
cmp dl,ASTYPE_opStDefFn
jb CiNotDeclare ;brif its not SUB/FUNC/DEFFN/DECLARE op
;Walk through parm list seeing if there are any oNam AS ... in parm list
push si ;save ptr to opStDeclare/Sub/Func
jne CiNotDefFn
inc si ;skip link field
inc si
CiNotDefFn:
add si,DCL_cParms+2 ;si points to parm count field
GETSEG es,[txdcur.TXD_BDLTEXT_SEG]
lods WORD PTR es:[si] ;ax = parm count (UNDEFINED same as 0)
xchg cx,ax ;cx = parm count
CiParmLoop:
dec cx
js CiParmDone ;brif done with parm list
lods WORD PTR es:[si] ;ax = parm's oNam/oVar
xchg bx,ax ;bx = parm's oNam/oVar
lods WORD PTR es:[si] ;ax = parm's atr flags
xchg dx,ax ;dx = parm's atr flags
lods WORD PTR es:[si] ;ax = parm's oTyp (oNam of type if rude)
test dx,PATR_asClause
je CiParmLoop ;brif parm has no AS clause
cmp ax,ET_MAX
jbe CiParmLoop ;brif ANY,INTEGER,...,STRING
xchg ax,bx ;ax = parm's oNam/oVar
cmp [txtFindIndex],ASTYPE_opStDeclare
je CiDeclare ;brif we're looking at DECLARE parm
; list - no oVars, just oNams
push ax ;pass parm1 = oNam/oVar
call ONamOVarRudeOrParse ;ax = variable's oNam
CiDeclare:
sub ax,[oNamAs] ;see if it is id of interest
jne CiParmLoop ;brif not
pop si ;restore ptr to opStDeclare/Sub/Func
jmp SHORT CiDone ;return 0 - terminates ForEach...
CiParmDone:
pop si ;restore ptr to opStDeclare/Sub/Func
jmp SHORT CiLoop
CiNotDeclare:
call ONamOfAs ;ax = oNam of id
jc CiLoop ;brif AS clause was within TYPE/END TYPE
sub ax,[oNamAs] ;see if it is id of interest
jne CiLoop ;brif not
;return 0 - terminates ForEach...
CiDone:
pop si ;restore caller's si
ret
ChkAsInTbl ENDP
;**************************************************************
; ChkLastAs
; Purpose:
; Called when opAsType op[A]Idxxx(oNam) pcode sequence is deleted.
; If there are no other refs to oNam AS in pcode, oNam's NM_fAs
; name table bit is reset.
; This causes lexer to know all future references to oNam.xxx are not
; user-type record element references.
; Entry:
; ax = oNam
; Exit:
; carry set if no more <oNam> AS constructs exist in pcode
;
;**************************************************************
PUBLIC ChkLastAs
ChkLastAs PROC NEAR
mov [oRsAs],UNDEFINED
ChkLastAs ENDP
ChkLastAs1 PROC NEAR
mov [oNamAs],ax ;pass oNam to ChkAsInTbl
;scan all text tables in module to see if any more AS x
mov bx,CPOFFSET ChkAsInTbl
call ForEachTxtTblInMrs
or ax,ax ;test return value
je NotLastAs ;brif found an AS x
; exit with carry clear
mov bx,[oNamAs] ;pass oNam in bx
mov al,NM_fAS ;pass mask for bit to be reset
call ResetONamMask
stc ;set return code
NotLastAs:
ret
ChkLastAs1 ENDP
;**************************************************************
; ChkLastAsText
; Purpose:
; Same as ChkLastAs except for input parm.
; Pcode is assumed to be in SS_RUDE state.
; Entry:
; ax = text offset to opAsType opcode being deleted from
; current text table
; bx = text offset to 1st byte being deleted
; cx = text offset beyond last byte being deleted
;
; Pcode is:
; For id AS type: opAsType(oNamType) opVtRf(oNamId)
; For id(10,20) AS type: 10,20,opAsType(oNamType) opAVtRf(2,oNamId)
; For typeElem AS type: opElemRef(oNamElem) opAsType(oNamType)
; si is pointing to opAsType in all cases
;
;**************************************************************
cProc ChkLastAsText,<PUBLIC,NEAR>
cBegin
;if PreScanAsChg is active, 'AS <userType>' is not really
;being deleted, just re-parsed, so don't reset name table bit.
cmp [fPreScanAsChg],0
jne ClatExit
;save ptr to opAsType being deleted, so ChkAsInTbl doesn't
;count one being deleted when looking for other occurances
;of x AS <usertype>
mov [otxAsDelStart],bx
mov [otxAsDelEnd],cx
mov dx,[grs.GRS_oRsCur]
mov [oRsAs],dx
call ONamOfAs ;ax = oNam of x for x as <type>
jc ClatExit ;brif not x as <type> pcode
call ChkLastAs1
jnc ClatExit ;brif not last x as <type> in pcode
or [mrsCur.MRS_flags],FM_asChg
;remember to call PreScanAsChg
; before trying to scan/run program
ClatExit:
cEnd
;**************************************************************
; PreScanAsChg
; Purpose:
; Called just before we scan a text table in preparation
; for execution. Only called when text table's FM_asChg
; bit is set, so we know that either:
; an 'x AS y' clause has been inserted in this table, or
; an 'x AS y' clause has been deleted from this table and
; the module contained no more 'x AS <user type>' clauses
; It walks through every pcode in current text table.
; For every record element x.a, if x's NM_fAs bit is not set,
; the line is re-parsed.
; If the line contains an 'opNoTyp' opcode, meaning the line
; contains an identifier other than a record element with
; a period in it's name, the line is re-parsed.
;
;**************************************************************
cProc PreScanAsChg,<PUBLIC,NEAR>,<si,di>
cBegin
mov [fPreScanAsChg],1
SetStartOtx si ;si = offset to start of text table
mov di,si
PsLoop:
push si
PUSHI ax,<CODEOFFSET tOpAs>
call TxtFindNextOp ;ax = offset to next opcode of interest
;dl = [txtFindIndex]
xchg si,ax ;si = offset to opcode
cmp dl,AS_opNoType
ja PsDone ;brif got opEot
.errnz AS_opEot - AS_opNoType - 1
je PsReParse ;brif opcode is opNoType
;else opcode must be opOff...
xchg ax,di ;ax = ptr beyond last opOffxxx opcode
mov di,si ;di = ptr to current opOffxxx opcode
add di,4 ;di = ptr beyond current opOffxxx opcode
DbAssertRel ax,be,si,CP,<PreScanAsChg: err2>
cmp si,ax
je PsLoop ;this is just an offset modifier, like
; c in a.b.c
GETSEG es,[txdCur.TXD_bdlText_seg]
push es:[si-2] ;push oNam/oVar from op[A]Idxxx opcode
call ONamOVarRudeOrParse ;ax = variable's oNam
push ax ;pass oNam from op[A]Idxxx opcode
call FlagOfONam ;al = oNam's flags
test al,NM_fAS
jne PsLoop ;brif valid record element
;Else, what used to look like a record element should now be a simple id
PsReParse:
push si ;pass opcode offset to LnOfOtx
call LnOfOtx ;ax = current line #
inc ax ;ax = line after current line
push ax ;pass to OtxOfLn below
mov ax,si ;ax = offset to opcode of interest
call TxtReEnter ;re list & parse line @ ax
call OtxOfLn ;ax = offset to next opBol/opEot
; (Must be done after TxtReEnter)
xchg si,ax ;si = offset to next opBol/opEot
jmp SHORT PsLoop
PsDone:
mov [fPreScanAsChg],0
cEnd
sEnd CP
;**************************************************************
; Support for GetSegAddr MACRO
;
;**************************************************************
;seg_cp = segment address for the CP segment
;It can be referenced from any module as follows:
; EXTRN Seg_CP:abs
; mov ax,SEG Seg_CP
; These statements are generated by the macro call:
; GetSegAddr CP
Seg_CP = SEG ModuleRudeEdit
PUBLIC Seg_CP
assumes DS,DATA
sEnd CP
end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -