REM. BBCSDL utility to cross-reference functions and variables v1.8 REM. (C) Richard Russell, http://www.rtrussell.co.uk/, 06-Sep-2023 HIMEM = PAGE + 40000000 REM Custom constants: XREF_LEFT = 1 XREF_DEF = 2 XREF_DEFLINE = 4 XREF_ASMREM = 8 XREF_ENDFN = 16 XREF_IFON = 32 XREF_LOCAL = 64 XREF_PRIVATE = 128 MAXFNPROC = 1000 : REM Maximum number of FNs/PROCs MAXENTITY = 1000 : REM Maximum number of entities per FN/PROC MAXOPEN = 100 : REM Maximum number of 'open' FNs/PROCs MAXLOCAL = 100 : REM Maximum number of LOCAL/PRIVATEs per 'open' FN/PROC MAXDEPTH = 3 : REM Maximum nesting depth of recursive FNs/PROCs DEBUG = FALSE GUIscale = 2.0 I% = INSTR(@cmd$, "-scale") : IF I% GUIscale = VALMID$(@cmd$, I% + 6) Darkmode% = INSTR(@cmd$, "-dark") <> 0 BBCFile$ = @tmp$ + "program.tmp.bbc" *ESC OFF *SYS 4 VDU 23,22,400*GUIscale;250*GUIscale;8,16,16,128+8 - (128 AND Darkmode%) IF INKEY(-256) = &57 THEN SYS "SetWindowText", @hwnd%, "Cross Reference utility" SYS "ShowWindow", @hwnd%, 5 TypeFace$ = "Courier New" Points% = 11 * GUIscale / 2 Accs%% = !332 ELSE SYS "SDL_SetWindowTitle", @hwnd%, "Cross Reference utility", @memhdc% SYS "SDL_ShowWindow", @hwnd%, @memhdc% TypeFace$ = """" + @lib$ + "DejaVuSans""" Points% = 11 * GUIscale / 2 IF @platform% AND &40 Accs%% = ]332 ELSE Accs%% = !332 ENDIF CLS OFF OSCLI "FONT " + TypeFace$ + "," + STR$(Points%) IF Darkmode% THEN COLOR 0,255,255,255 : COLOR 15,0,0,0 : COLOR 1,255,0,0 COLOR 7, &40,&40,&40 : REM Primary GUI background colour COLOR 8, &80,&80,&80 : REM Primary GUI foreground colour GCOL 0 : GCOL 143 : COLOR 0 : COLOR 143 ELSE COLOR 7, &F0,&F0,&F0 : REM Primary GUI background colour COLOR 8, &80,&80,&80 : REM Primary GUI foreground colour ENDIF INSTALL @lib$+"dlglib" INSTALL @lib$+"msgbox" INSTALL @lib$+"treeview" INSTALL @lib$+"stringlib" ON ERROR ON ERROR OFF : R% = FN_messagebox("Cross Reference utility", REPORT$, 0) : QUIT REM Allocate arrays, structures and buffers: DIM Open%(MAXOPEN) DIM FnProc{(MAXFNPROC) name$, index%, row%}, nLocal%(MAXFNPROC) DIM Local{(MAXFNPROC,MAXLOCAL) name$, type&, inst&, row%}, nEntity%(MAXFNPROC) DIM Entity{(MAXFNPROC,MAXENTITY) name$, type&, inst&, row%} FOR F% = 1 TO MAXFNPROC : FnProc{(F%)}.index% = F% : NEXT FnProc{(0)}.name$ = "Main program" REM Assembler operands and modifiers to be ignored: AsmOp$ = ".al.cl.dl.bl.ah.ch.dh.bh.st0.st1.st2.st3.st4.st5.st6.st7" + \ \ ".eax.ecx.edx.ebx.esp.ebp.esi.edi.ax.cx.dx.bx.sp.bp.si.di" + \ \ ".es.cs.ss.ds.fs.gs.byte.word.dword.qword.short.near.far" + \ \ ".tbyte.mm0.mm1.mm2.mm3.mm4.mm5.mm6.mm7.stosb.stosw.stosd" + \ \ ".movsb.movsw.movsd.scasb.scasw.scasd.cmpsb.cmpsw.cmpsd" REM. Scan program: nFnProc% = 0 nOpen% = 0 LineNo% = 0 Asm% = 0 IfLevel% = 0 State% = 0 CaseLevel% = 0 File% = OPENIN(BBCFile$) REPEAT REM Read line, concatenating 'continuation' lines: text$ = "" contin% = FALSE Lindex% = LineNo% REPEAT REM Read line from editor: PRINT TAB(0,0) "Examining line "; LineNo% D% = BGET#File% + BGET#File% + BGET#File% : REM Line length and number INPUT #File%, A$ LineNo% += 1 REM Check for continuation line: IF contin% THEN IF ASCA$=&5C A$ = MID$(A$,2) ELSE ERROR 100, "Missing \ in row " + STR$(LineNo%-1) ELSE IF ASCA$=&5C ERROR 100, "Unexpected \ at start of row " + STR$(LineNo%-1) ENDIF REM Discard DATA statements and 'star' commands: IF text$="" IF ASCA$=&DC A$ = "" IF text$="" IF ASCA$=&2A A$ = "" REM Remove quoted strings (can contain &F4): REPEAT Q% = INSTR(A$, """") IF Q% THEN P% = INSTR(A$, """", Q%+1) IF P% A$ = LEFT$(A$, Q%-1) + MID$(A$, P%+1) : REM Remove quoted string ENDIF UNTIL Q% = 0 OR P% = 0 REM Remove REMarks (can contain \), but leave REM in case THEN REM: R% = INSTR(A$, CHR$&F4) IF R% A$ = LEFT$(A$, R%) IF Q% IF Q% < R% OR R% = 0 ERROR 100, "Mismatched quotes "" "" in row " + STR$(LineNo%-1) REM Replace encoded line numbers (can contain \), replace with constant: REPEAT L% = INSTR(A$, CHR$&8D) IF L% A$ = LEFT$(A$, L%-1) + " 1 " + MID$(A$, L%+4) UNTIL L% = 0 REM Check for continuation character: contin% = INSTR(A$, "\") IF contin% THEN text$ += LEFT$(A$, contin%-1) + " " ELSE text$ += A$ ENDIF UNTIL contin% = FALSE REM Remove trailing spaces: WHILE RIGHT$(text$) = " " : text$ = LEFT$(text$) : ENDWHILE REM Discard lines beginning with ; (and cancel all definitions); IF ASCtext$=&3B IF Asm%=0 THEN IfLevel% = 0 CaseLevel% = 0 State% = 0 nOpen% = 0 text$ = "" ENDIF REM Check for mismatched parentheses or braces: P% = 0 : B% = 0 IF text$ <> "" THEN FOR p%% = PTR(text$) TO PTR(text$) + LEN(text$) - 1 CASE ?p%% OF WHEN &28: P% += 1 WHEN &29: P% -= 1 : IF P% < 0 EXIT FOR WHEN &7B: B% += 1 WHEN &7D: B% -= 1 : IF B% < 0 EXIT FOR WHEN &8A,&A7,&B0,&C0,&C1,&C2,&C4: P% += 1 ENDCASE NEXT ENDIF IF P% ERROR 100, "Mismatched parentheses ( ) in row " + STR$(LineNo%-1) IF B% ERROR 100, "Mismatched braces ( ) in row " + STR$(LineNo%-1) REM Process FN/PROC definitions (and remove other DEFs): Flags% = XREF_LEFT IF ASCtext$=&DD THEN Asm% = 0 p%% = PTR(text$) REPEAT p%% += 1 UNTIL ?p%%<>&20 IF ?p%%=&A4 OR ?p%%=&F2 Flags% OR= XREF_DEF OR XREF_DEFLINE ELSE text$ = "" ENDIF REM Process WHEN: IF ASCtext$=&C9 PROCwhen REM Process OTHERWISE: IF ASCtext$=&CC PROCotherwise REM Now scan the line: text$ += CHR$0 p%% = PTR(text$) WHILE ?p%% C% = ?p%% p%% += 1 CASE C% OF WHEN &20: REM Spaces don't change 'Left' state WHEN &26: p%% -= 1 : PROChex(p%%) WHEN &2A: IF Flags% AND XREF_LEFT p%% -= 1 : PROCstar(p%%) WHEN &2E: IF Asm% Flags% AND= NOT XREF_LEFT : PROCname("", p%%) : Flags% OR= XREF_LEFT WHEN &3B: IF Asm% Flags% OR= XREF_ASMREM WHEN &3D,&85,&06: IF Flags% AND XREF_LEFT Flags% OR= XREF_ENDFN WHEN &5B: Asm% += 1 : IF Asm% = 1 Flags% OR= XREF_LEFT : PROCss("L%") : PROCss("O%") : PROCss("P%") WHEN &5D: IF Flags% AND XREF_LEFT Asm% = FALSE WHEN &A4: IF ?p%%<>&28 PROCname("FN", p%%) IF Flags% AND XREF_DEF Flags% = (Flags% OR XREF_LEFT) AND NOT XREF_DEF WHEN &BA,&D6: PROCss("A%") : PROCss("B%") : PROCss("C%") : PROCss("D%") WHEN &CB: PROCendcase WHEN &CD: PROCendif WHEN &E0: IF Flags% AND XREF_LEFT PROCendfnproc WHEN &E1: PROCendfnproc WHEN &E3: PROCfor(p%%) WHEN &E7: PROCif WHEN &EA: IF Flags% AND XREF_LEFT Flags% OR= XREF_LOCAL WHEN &EE: PROCon(p%%) WHEN &F2: IF ?p%%<>&28 PROCname("PROC", p%%) IF Flags% AND XREF_DEF Flags% = (Flags% OR XREF_LEFT) AND NOT XREF_DEF WHEN &0E: Flags% OR= XREF_PRIVATE WHEN &3A,&8B,&8C,&CC,&F5: Flags% OR= XREF_LEFT : REM Set to Left Flags% AND= NOT(XREF_LOCAL OR XREF_PRIVATE OR XREF_ASMREM) IF Flags% AND XREF_ENDFN Flags% AND= NOT XREF_ENDFN : PROCendfnproc OTHERWISE: IF C%<=&39 IF C%>=&30 p%% -= 1 : PROCdecimal(p%%) IF C%>=&40 IF C%<=&7A IF C%<=&5A OR C%>=&5F p%% -= 1 : PROCname("", p%%) Flags% AND= NOT XREF_LEFT : REM Set to Right ENDCASE IF C%=&8B PROCelse ENDWHILE IF Flags% AND XREF_ENDFN PROCendfnproc : REM order is important IF Flags% AND XREF_IFON PROCendif : REM (e.g. FNsa in SHEET) text$ = LEFT$(text$) : REM Remove terminating NUL REM Process trailing OF, THEN and 'THEN;': IF RIGHT$(text$,1)=CHR$&CA CaseLevel% += 1 : State% = (State% << 3) OR 2 IF RIGHT$(text$,1)=CHR$&8C IfLevel% += 1 : State% = (State% << 3) OR 3 IF RIGHT$(text$,2)=CHR$&8C+";" IF (State% AND 3) = 2 State% += 1 UNTIL EOF #File% CLOSE #File% IF Asm% ERROR 100, "Mismatched assembler []" IF IfLevel% ERROR 100, "Mismatched IF...ENDIF" IF CaseLevel% ERROR 100, "Mismatched CASE...ENDCASE" REM Sort the data arrays: PROCstructarraysort1D$(1, nFnProc%, FnProc{()}, ^FnProc{(0)}.name$ - FnProc{(0)}) FOR F% = 0 TO nFnProc% PROCstructarraysort2D$(1, nEntity%(F%), Entity{()}, ^Entity{(0,0)}.name$ - Entity{(0,0)}, F%) NEXT F% IF DEBUG REPEAT : WAIT 1 : UNTIL FALSE REM Analyze data and create Tree View: hTree%% = FN_TVcreatetree(TypeFace$, Points%) hmain%% = FNinsertitem("Main program", 0, 0) hfunc%% = FNinsertitem("Functions", 0, 0) hproc%% = FNinsertitem("Procedures", 0, 0) PROClistentities(hmain%%, 0) IF nFnProc% THEN FOR func% = 1 TO nFnProc% func$ = FnProc{(func%)}.name$ index% = FnProc{(func%)}.index% row% = FnProc{(func%)}.row% IF LEFT$(func$,2) = "FN" THEN hnode%% = FNinsertitem(func$, hfunc%%, row%) PROClistentities(hnode%%, index%) ELSE hnode%% = FNinsertitem(func$, hproc%%, row%) PROClistentities(hnode%%, index%) ENDIF NEXT func% ENDIF hwarn%% = FNinsertitem("Warnings", 0, 0) REM Check for possibly unused variables: W%% = 0 H%% = 0 IF nEntity%(0) THEN FOR E% = 1 TO nEntity%(0) IF Entity{(0,E%)}.inst& <= 1 THEN name$ = Entity{(0,E%)}.name$ IF LEFT$(name$,2)<>"FN" IF LEFT$(name$,4)<>"PROC" IF ASCname$<>&40 THEN IF INSTR(name$,".")=0 THEN IF W%% = 0 W%% = FNinsertitem("Possibly unused variables", hwarn%%, 0) IF H%% = 0 H%% = FNinsertitem("Main program", W%%, 0) dummy%% = FNinsertitem(name$, H%%, Entity{(0,E%)}.row%) ENDIF ENDIF ENDIF NEXT ENDIF IF nFnProc% THEN FOR F% = 1 TO nFnProc% H%% = 0 I% = FnProc{(F%)}.index% IF nLocal%(I%) THEN FOR L% = 1 TO nLocal%(I%) IF Local{(I%,L%)}.inst& = 0 THEN name$ = Local{(I%,L%)}.name$ IF ASCname$<>&40 THEN IF W%% = 0 W%% = FNinsertitem("Possibly unused variables", hwarn%%, 0) IF H%% = 0 H%% = FNinsertitem(FnProc{(F%)}.name$, W%%, FnProc{(F%)}.row%) dummy%% = FNinsertitem(name$, H%%, Local{(I%,L%)}.row%) ENDIF ENDIF NEXT ENDIF IF nEntity%(I%) THEN FOR E% = 1 TO nEntity%(I%) IF Entity{(I%,E%)}.inst& <= 1 THEN name$ = Entity{(I%,E%)}.name$ type& = Entity{(I%,E%)}.type& IF type& = 0 THEN type& = XREF_LOCAL FOR J% = 0 TO nFnProc% IF J%<>I% IF nEntity%(J%) THEN FOR K% = 1 TO nEntity%(J%) IF name$ = Entity{(J%,K%)}.name$ type& = 0 : EXIT FOR J% NEXT ENDIF NEXT J% ENDIF IF type& AND (XREF_LOCAL OR XREF_PRIVATE) THEN IF LEFT$(name$,2)<>"FN" IF LEFT$(name$,4)<>"PROC" IF ASCname$<>&40 THEN IF INSTR(name$,".")=0 THEN IF W%% = 0 W%% = FNinsertitem("Possibly unused variables", hwarn%%, 0) IF H%% = 0 H%% = FNinsertitem(FnProc{(F%)}.name$, W%%, FnProc{(F%)}.row%) dummy%% = FNinsertitem(name$, H%%, Entity{(I%,E%)}.row%) ENDIF ENDIF ENDIF ENDIF NEXT ENDIF NEXT F% ENDIF REM Check for possibly unused FNs / PROCs: W%% = 0 IF nFnProc% THEN FOR F% = 1 TO nFnProc% func$ = FnProc{(F%)}.name$ FOR I% = 0 TO nFnProc% FOR E% = 1 TO nEntity%(I%) IF func$ = Entity{(I%,E%)}.name$ EXIT FOR I% NEXT NEXT I% IF I% > nFnProc% THEN IF W%% = 0 W%% = FNinsertitem("Possibly unused functions or procedures", hwarn%%, 0) dummy%% = FNinsertitem(func$, W%%, FnProc{(F%)}.row%) ENDIF NEXT F% ENDIF REM Check for two FNs / PROCs with the same name: W%% = 0 IF nFnProc% > 1 THEN FOR F% = 1 TO nFnProc%-1 func$ = FnProc{(F%)}.name$ IF func$ = FnProc{(F%+1)}.name$ THEN IF W%% = 0 W%% = FNinsertitem("Functions or procedures with the same name", hwarn%%, 0) dummy%% = FNinsertitem(func$, W%%, FnProc{(F%)}.row%) dummy%% = FNinsertitem(func$, W%%, FnProc{(F%+1)}.row%) ENDIF NEXT F% ENDIF REM Check for shared variables which are not globals: W%% = 0 IF nFnProc% THEN FOR F% = 1 TO nFnProc% H%% = 0 I% = FnProc{(F%)}.index% IF nEntity%(I%) THEN FOR E% = 1 TO nEntity%(I%) IF Entity{(I%,E%)}.type& = 0 THEN name$ = Entity{(I%,E%)}.name$ root$ = FNroot(name$) IF LEFT$(name$,2)<>"FN" IF LEFT$(name$,4)<>"PROC" IF ASCname$<>&40 THEN FOR G% = 1 TO nEntity%(0) IF name$ = Entity{(0,G%)}.name$ EXIT FOR IF root$<>"" IF root$ = Entity{(0,G%)}.name$ EXIT FOR NEXT IF G% > nEntity%(0) THEN IF W%% = 0 W%% = FNinsertitem("Shared variables which are not globals", hwarn%%, 0) IF H%% = 0 H%% = FNinsertitem(FnProc{(F%)}.name$, W%%, FnProc{(F%)}.row%) dummy%% = FNinsertitem(name$, H%%, Entity{(I%,E%)}.row%) ENDIF ENDIF ENDIF NEXT ENDIF NEXT F% ENDIF REM Check for shared static variables: W%% = 0 IF nFnProc% THEN FOR F% = 1 TO nFnProc% H%% = 0 I% = FnProc{(F%)}.index% IF nEntity%(I%) THEN FOR E% = 1 TO nEntity%(I%) IF Entity{(I%,E%)}.type& = 0 THEN name$ = Entity{(I%,E%)}.name$ IF LEN(name$)=2 IF RIGHT$(name$)="%" IF name$>="A%" IF name$<="Z%" THEN IF W%% = 0 W%% = FNinsertitem("Shared static variables", hwarn%%, 0) IF H%% = 0 H%% = FNinsertitem(FnProc{(F%)}.name$, W%%, FnProc{(F%)}.row%) dummy%% = FNinsertitem(name$, H%%, Entity{(I%,E%)}.row%) ENDIF ENDIF NEXT ENDIF NEXT F% ENDIF REM Check for variables made 'local' and 'private' (etc.): W%% = 0 IF nFnProc% THEN FOR F% = 1 TO nFnProc% H%% = 0 I% = FnProc{(F%)}.index% IF nLocal%(I%) THEN FOR L% = 1 TO nLocal%(I%) type& = Local{(I%,L%)}.type& IF type& AND (type&-1) THEN name$ = Local{(I%,L%)}.name$ IF W%% = 0 W%% = FNinsertitem("Variables made both LOCAL and PRIVATE (etc.)", hwarn%%, 0) IF H%% = 0 H%% = FNinsertitem(FnProc{(F%)}.name$, W%%, FnProc{(F%)}.row%) dummy%% = FNinsertitem(name$, H%%, Local{(I%,L%)}.row%) ENDIF NEXT ENDIF NEXT F% ENDIF REM Check for variables which violate naming guidelines: W%% = 0 IF nFnProc% THEN FOR F% = 1 TO nFnProc% H%% = 0 I% = FnProc{(F%)}.index% IF nEntity%(I%) THEN FOR E% = 1 TO nEntity%(I%) name$ = Entity{(I%,E%)}.name$ root$ = name$ dot% = INSTR(root$, ".") IF dot% root$ = LEFT$(root$, dot%-1) IF Entity{(I%,E%)}.type&<>0 EOR root$=FN_lower(root$) THEN IF ASCname$<>&40 IF (LENname$<>2 OR RIGHT$(name$)<>"%" AND RIGHT$(name$)<>"$") THEN IF W%% = 0 W%% = FNinsertitem("Variables which violate naming guidelines", hwarn%%, 0) IF H%% = 0 H%% = FNinsertitem(FnProc{(F%)}.name$, W%%, FnProc{(F%)}.row%) dummy%% = FNinsertitem(name$, H%%, Entity{(I%,E%)}.row%) ENDIF ENDIF NEXT ENDIF NEXT F% ENDIF REM Check for variables incompatible with Lowercase Keywords: W%% = 0 *lowercase on FOR F% = 0 TO nFnProc% H%% = 0 I% = FnProc{(F%)}.index% IF nEntity%(I%) THEN FOR E% = 1 TO nEntity%(I%) name$ = Entity{(I%,E%)}.name$ IF LEFT$(name$,2)<>"FN" IF LEFT$(name$,4)<>"PROC" THEN temp$ = name$ REPEAT D% = INSTR(temp$, ".") IF D% MID$(temp$,D%,1) = CHR$&1E UNTIL D% = 0 IF EVAL("1:"+temp$) IF temp$ <> $(Accs%%+2) THEN IF W%% = 0 W%% = FNinsertitem("Variables incompatible with Lowercase Keywords", hwarn%%, 0) IF H%% = 0 H%% = FNinsertitem(FnProc{(F%)}.name$, W%%, FnProc{(F%)}.row%) dummy%% = FNinsertitem(name$, H%%, Entity{(I%,E%)}.row%) ENDIF ENDIF NEXT ENDIF NEXT F% REPEAT R% = FN_TVviewtree(hTree%%) UNTIL R% = 1 OR R% = 2 IF R% = 1 THEN RUN QUIT REM Parse and discard a 'star' (Operating System) command: DEF PROCstar(RETURN p%%) REPEAT p%% += 1 UNTIL ?p%%=0 ENDPROC REM Parse and discard a decimal number (can contain E or e): DEF PROCdecimal(RETURN p%%) REPEAT p%% += 1 UNTIL NOT(?p%%=&2E OR ?p%%=&45 OR ?p%%=&65 OR ?p%%>=&30 AND ?p%%<=&39) ENDPROC REM Parse and discard a hexadecimal number (can contain ABCDEFabcdef): DEF PROChex(RETURN p%%) REPEAT p%% += 1 UNTIL ?p%%<&30 OR ?p%%>&66 OR ?p%%>&39 AND ?p%%<&41 OR ?p%%>&46 AND ?p%%<&61 ENDPROC REM Parse and process a function, procedure or variable name: DEF PROCname(S$, RETURN p%%) LOCAL first%, I%, J%, S%, T%, V$ REPEAT REPEAT V$ += CHR$?p%% p%% += 1 UNTIL ?p%%<&30 OR ?p%%>&7A OR ?p%%>&39 AND ?p%%<&40 OR ?p%%>&5A AND ?p%%<&5F UNTIL ?p%%<>&2E IF ?p%%=&23 OR ?p%%=&24 OR ?p%%=&25 OR ?p%%=&26 V$ += CHR$?p%% : p%% += 1 : IF ?p%%=&25 V$ += CHR$?p%% : p%% += 1 IF ?p%%=&7B p%% += 1 : S% = TRUE : V$ += "{" IF ?p%%=&28 V$ += "()" IF Flags% AND XREF_DEF IF S$ = "FN" OR S$ = "PROC" THEN nFnProc% += 1 IF nFnProc% > MAXFNPROC ERROR 100, "Too many FNs/PROCs" FnProc{(nFnProc%)}.name$ = S$ + V$ FnProc{(nFnProc%)}.row% = Lindex% nEntity%(nFnProc%) = 0 nOpen% += 1 IF nOpen% > MAXOPEN ERROR 100, "Too many open FNs/PROCs" Open%(nOpen%) = nFnProc% : nLocal%(nFnProc%) = 0 IF DEBUG COLOR 1 : PRINT "New FN/PROC: " S$ V$ ENDIF IF ?p%%=&28 PROCbracket(p%%) IF S% THEN WHILE ?p%%<>&7D AND ?p%%<>0 IF ?p%%=&3D THEN REPEAT p%% += 1 : UNTIL ?p%%<>&20 PROCname("", p%%) ENDIF IF ?p%%>=&30 AND ?p%%<=&39 OR ?p%%>=&40 AND ?p%%<=&5A OR ?p%%>=&5F AND ?p%%<=&7A THEN IF RIGHT$(V$,2) = "()" THEN PROCname(S$ + V$ + "}.", p%%) ELSE PROCname(S$ + LEFT$(V$) + ".", p%%) ENDIF ELSE p%% += 1 ENDIF ENDWHILE V$ += "}" IF ?p%% p%% += 1 : IF ?p%%=&2E p%% += 1 : PROCname(S$ + V$ + ".", p%%) ENDIF IF Flags% AND XREF_ASMREM THEN IF DEBUG COLOR 1 : PRINT "ASM comment discarded: " S$ V$ ENDPROC ENDIF IF Asm% THEN IF Flags% AND XREF_LEFT THEN IF DEBUG COLOR 1 : PRINT "ASM opcode discarded: " S$ V$ ENDPROC ENDIF IF INSTR(AsmOp$, "." + FN_lower(V$) + ".") THEN IF DEBUG COLOR 1 : PRINT "ASM operand discarded: " V$ ENDPROC ENDIF ENDIF IF Flags% AND XREF_DEF IF (S$ + V$) = FnProc{(Open%(nOpen%))}.name$ THEN ENDPROC IF Flags% AND XREF_DEFLINE THEN first% = nOpen% ELSE IF nOpen% first% = 1 IF Flags% AND (XREF_DEF OR XREF_LOCAL OR XREF_PRIVATE) IF nOpen% THEN FOR I% = first% TO nOpen% T% = Open%(I%) FOR J% = 0 TO nLocal%(T%) IF Local{(T%,J%)}.name$ = S$ + V$ THEN EXIT FOR NEXT IF J% > nLocal%(T%) THEN nLocal%(T%) = J% IF J% > MAXLOCAL ERROR 100, "Too many LOCALs / PRIVATEs" Local{(T%,J%)}.name$ = S$ + V$ ENDIF Local{(T%,J%)}.row% = Lindex% Local{(T%,J%)}.type& OR= Flags% AND (XREF_DEF OR XREF_LOCAL OR XREF_PRIVATE) NEXT I% IF DEBUG IF Flags% AND XREF_DEF COLOR 2 : PRINT "Formal: " S$ V$ IF DEBUG IF Flags% AND XREF_LOCAL COLOR 2 : PRINT "Local: " S$ V$ IF DEBUG IF Flags% AND XREF_PRIVATE COLOR 2 : PRINT "Private: " S$ V$ ELSE FOR I% = first% TO nOpen% T% = Open%(I%) PROCentity(T%, S$ + V$) NEXT ENDIF ENDPROC REM Process bracketed expression: DEF PROCbracket(RETURN p%%) REPEAT p%% += 1 IF ?p%%=&26 p%% += 1 : PROChex(p%%) IF ?p%%=&A4 p%% += 1 : IF ?p%%<>&28 PROCname("FN", p%%) IF ?p%%=&F2 p%% += 1 : IF ?p%%<>&28 PROCname("PROC", p%%) IF ?p%%=&28 PROCbracket(p%%) IF ?p%%<=&39 IF ?p%%>=&30 PROCdecimal(p%%) IF ?p%%>=&40 IF ?p%%<=&7A IF ?p%%<=&5A OR ?p%%>=&5F PROCname("", p%%) UNTIL ?p%%=&29 OR ?p%%=0 IF ?p%%=&29 p%% += 1 ENDPROC REM Process IF: DEF PROCif IF (Flags% AND XREF_IFON) = 0 THEN IfLevel% += 1 State% = State% << 3 Flags% OR= XREF_IFON ENDIF State% OR= 3 ENDPROC REM Process ON: DEF PROCon(RETURN p%%) WHILE ?p%%=&20 p%% += 1 : ENDWHILE IF ?p%%=&85 OR ?p%%=&91 OR ?p%%=&D9 OR ?p%%=&EC OR ?p%%=&04 OR ?p%%=&09 THEN REPEAT p%% += 1 : UNTIL ?p%%<>&20 IF ?p%% = &EA p%% += 1 Flags% OR= XREF_LEFT IF (Flags% AND XREF_IFON) = 0 THEN IfLevel% += 1 State% = State% << 3 Flags% OR= XREF_IFON ENDIF State% OR= 4 ELSE IF ?p%%<>0 IF ?p%%<>&3A IF ?p%%<>&8B IF ?p%%<>&F4 PROCif ENDIF ENDPROC REM Process ELSE: DEF PROCelse IF DEBUG COLOR 5 : PRINT "ELSE State% = &"; ~State% ", IfLevel% = "; IfLevel% ", CaseLevel% = "; CaseLevel% IF IfLevel%=0 ERROR 100, "Unexpected ELSE at row " + STR$(Lindex%) IF (State% AND 7) = 2 State% -= 1 ELSE State% OR= 4 ENDPROC REM Process ENDIF (or end of single-line IF): DEF PROCendif LOCAL temp& IF DEBUG COLOR 5 : PRINT "ENDIF State% = &"; ~State% ", IfLevel% = "; IfLevel% ", CaseLevel% = "; CaseLevel% IF IfLevel%=0 ERROR 100, "Unexpected ENDIF at row " + STR$(Lindex%) IfLevel% -= 1 temp& = State% AND 7 State% = State% >>> 3 IF temp&=0 PROCendfnproc : REM all branches exited ENDPROC REM Process WHEN: DEF PROCwhen IF CaseLevel%=0 ERROR 100, "Unexpected WHEN at row " + STR$(Lindex%) IF (State% AND 7) = 2 State% += 1 ELSE State% OR= 4 ENDPROC REM Process OTHERWISE: DEF PROCotherwise IF DEBUG COLOR 6 : PRINT "OTHERWISE State% = &"; ~State% ", IfLevel% = "; IfLevel% ", CaseLevel% = "; CaseLevel% IF CaseLevel%=0 ERROR 100, "Unexpected OTHERWISE at row " + STR$(Lindex%) IF (State% AND 7) = 2 State% -= 1 ELSE State% OR= 4 ENDPROC REM Process ENDCASE: DEF PROCendcase LOCAL temp& IF DEBUG COLOR 6 : PRINT "ENDCASE State% = &"; ~State% ", IfLevel% = "; IfLevel% ", CaseLevel% = "; CaseLevel% IF CaseLevel%=0 ERROR 100, "Unexpected ENDCASE at row " + STR$(Lindex%) CaseLevel% -= 1 temp& = State% AND 7 State% = State% >>> 3 IF temp&=0 PROCendfnproc : REM all cases exited ENDPROC REM Process ENDPROC, = or ENDIF/ENDCASE (when all cases exited): DEF PROCendfnproc IF DEBUG COLOR 4 : PRINT "ENDFNPROC State% = &"; ~State% ", IfLevel% = "; IfLevel% ", CaseLevel% = "; CaseLevel% IF IfLevel%=0 IF CaseLevel%=0 THEN IF Flags% AND XREF_DEFLINE THEN nOpen% -= 1 IF DEBUG COLOR 1 : PRINT "Current FN or PROC cancelled" ELSE nOpen% = 0 IF DEBUG COLOR 1 : PRINT "All FNs and PROCs cancelled" ENDIF Asm% = 0 ELSE IF (State% AND 5) = 1 THEN State% -= 1 ENDIF ENDPROC REM Process a FOR variable (register it twice!): DEF PROCfor(p%%) : REM n.b. not RETURN WHILE ?p%%=&20 p%% += 1 : ENDWHILE IF ?p%%>=&40 AND ?p%%<=&5A OR ?p%%>=&5F AND ?p%%<=&7A PROCname("", p%%) ENDPROC REM Process 'special static' variables (used by assembler code): DEF PROCss(name$) LOCAL F%, I%, J%, T% IF Flags% AND XREF_DEFLINE THEN F% = nOpen% ELSE F% = 1 IF nOpen% THEN FOR I% = F% TO nOpen% T% = Open%(I%) IF nLocal%(T%) THEN FOR J% = 1 TO nLocal%(T%) IF Local{(T%,J%)}.name$ = name$ THEN EXIT FOR NEXT IF J% <= nLocal%(T%) PROCentity(T%, name$) ENDIF NEXT I% ENDIF ENDPROC REM Add an entity (if new): DEF PROCentity(T%, name$) LOCAL I%, type&, root$ root$ = FNroot(name$) IF DEBUG COLOR 0 : PRINT "Entity: " name$ : IF root$<>"" PRINT "Root: " root$ FOR I% = 1 TO nLocal%(T%) IF name$ = Local{(T%,I%)}.name$ type& = Local{(T%,I%)}.type& : EXIT FOR IF root$<>"" IF root$ = Local{(T%,I%)}.name$ type& = Local{(T%,I%)}.type& : EXIT FOR NEXT IF type& IF Local{(T%,I%)}.inst& < 255 Local{(T%,I%)}.inst& += 1 IF type& = 0 IF nEntity%(0) THEN FOR I% = 1 TO nEntity%(0) IF name$ = Entity{(0,I%)}.name$ OR root$ = Entity{(0,I%)}.name$ THEN IF Entity{(0,I%)}.inst& < 255 Entity{(0,I%)}.inst& += 1 ENDIF NEXT ENDIF FOR I% = 0 TO nEntity%(T%) IF name$ = Entity{(T%,I%)}.name$ IF type& = Entity{(T%,I%)}.type& EXIT FOR NEXT IF I% > nEntity%(T%) THEN IF I% > MAXENTITY ERROR 100, "Too many variables" nEntity%(T%) = I% Entity{(T%,I%)}.name$ = name$ Entity{(T%,I%)}.type& = type& Entity{(T%,I%)}.row% = Lindex% ENDIF IF Entity{(T%,I%)}.inst& < 255 Entity{(T%,I%)}.inst& += 1 FOR I% = 1 TO nEntity%(T%) IF root$ = Entity{(T%,I%)}.name$ IF type& = Entity{(T%,I%)}.type& THEN IF Entity{(T%,I%)}.inst& < 255 Entity{(T%,I%)}.inst& += 1 ENDIF NEXT ENDPROC REM Get the root name of an array or structure: DEF FNroot(name$) LOCAL I%, J%, K%, root$ I% = INSTR(name$,".") : IF I%=0 I% = LEN(name$) J% = INSTR(name$,"(") : IF J%=0 J% = LEN(name$) K% = INSTR(name$,"{(") : IF K%=0 K% = LEN(name$) IF I% 0 CASE TRUE OF WHEN LEFT$(name$,2)="FN": f%%(2) OR= FNinsertfunc(name$, h%%(2), R%, 0) WHEN LEFT$(name$,4)="PROC": f%%(3) OR= FNinsertfunc(name$, h%%(3), R%, 0) WHEN LEFT$(name$,1)="@": f%%(T%+5) OR= FNinsertitem(name$, h%%(T%+5), R%) WHEN RIGHT$(name$,1)="&": f%%(T%+11) OR= FNinsertitem(name$, h%%(T%+11), R%) WHEN RIGHT$(name$,1)="$": f%%(T%+15) OR= FNinsertitem(name$, h%%(T%+15), R%) WHEN LENname$=2 AND name$>="A%" AND name$<="Z%" AND RIGHT$(name$)="%": f%%(T%+7) OR= FNinsertitem(name$, h%%(T%+7), R%) WHEN RIGHT$(name$,1)="%": f%%(T%+9) OR= FNinsertitem(name$, h%%(T%+9), R%) WHEN INSTR(name$,"()")<>0: f%%(T%+17) OR= FNinsertitem(name$, h%%(T%+17), R%) WHEN INSTR(name$,"{")<>0: f%%(T%+19) OR= FNinsertitem(name$, h%%(T%+19), R%) OTHERWISE: f%%(T%+13) OR= FNinsertitem(name$, h%%(T%+13), R%) ENDCASE NEXT ENDIF FOR I% = 4 TO 18 STEP 2 : f%%(0) OR= f%%(I%) : NEXT FOR I% = 5 TO 19 STEP 2 : f%%(1) OR= f%%(I%) : NEXT FOR I% = 0 TO 19 IF f%%(I%) = 0 THEN PROCmodifyitem("DELETED", h%%(I%)) ENDIF NEXT ENDPROC REM Recursively list called functions/procedures: DEF FNinsertfunc(func$, parent%%, param%, depth%) LOCAL hnode%%, F%, I%, T%, name$ hnode%% = FNinsertitem(func$, parent%%, param%) IF func$ = "..." THEN = hnode%% FOR F% = 1 TO nFnProc% IF func$ = FnProc{(F%)}.name$ THEN T% = TRUE FOR I% = 1 TO nEntity%(FnProc{(F%)}.index%) name$ = Entity{(FnProc{(F%)}.index%,I%)}.name$ IF LEFT$(name$,2)="FN" OR LEFT$(name$,4)="PROC" THEN IF depth% > MAXDEPTH name$ = "..." IF FNinsertfunc(name$, hnode%%, param%, depth%+1) IF depth% > MAXDEPTH EXIT FOR ENDIF NEXT ENDIF NEXT F% IF NOT T% THEN PROCmodifyitem(func$ + " [external]", hnode%%) = hnode%% REM Insert a Tree View item: DEF FNinsertitem(text$, parent%%, param%) LOCAL hnode%% hnode%% = FN_TVinsertitem(hTree%%, parent%%, text$, param%) IF hnode%% = 0 ERROR 100, "TVM_INSERTITEM failed" = hnode%% REM Modify a Tree View item: DEF PROCmodifyitem(text$, hnode%%) PROC_TVmodifyitem(hnode%%, text$, 0) ENDPROC REM Sort a 1D array of structures DEF PROCstructarraysort1D$(L%, H%, sa{()}, K%) LOCAL F%,G%,I%,a%%,b%%,a$,b$ IF L% >= H% ENDPROC a%% = ^a$ : b%% = ^b$ G% = 1 : REPEAT G% *= 2 : UNTIL G% >= (H% - L%) REPEAT G% DIV= 2 REPEAT F% = TRUE FOR I% = L% TO H%-G% !a%% = !(sa{(I%)}+K%) : a%%!4 = !(sa{(I%)}+K%+4) !b%% = !(sa{(I%+G%)}+K%) : b%%!4 = !(sa{(I%+G%)}+K%+4) IF a$ > b$ SWAP sa{(I%)},sa{(I%+G%)} : F% = FALSE NEXT UNTIL F% UNTIL G% = 1 !a%% = 0 : !b%% = 0 : a%%!4 = 0 : b%%!4 = 0 ENDPROC REM Sort a 2D array of structures DEF PROCstructarraysort2D$(L%, H%, sa{()}, K%, J%) LOCAL F%,G%,I%,a%%,b%%,a$,b$ IF L% >= H% ENDPROC a%% = ^a$ : b%% = ^b$ G% = 1 : REPEAT G% *= 2 : UNTIL G% >= (H% - L%) REPEAT G% DIV= 2 REPEAT F% = TRUE FOR I% = L% TO H%-G% !a%% = !(sa{(J%,I%)}+K%) : a%%!4 = !(sa{(J%,I%)}+K%+4) !b%% = !(sa{(J%,I%+G%)}+K%) : b%%!4 = !(sa{(J%,I%+G%)}+K%+4) IF a$ > b$ SWAP sa{(J%,I%)},sa{(J%,I%+G%)} : F% = FALSE NEXT UNTIL F% UNTIL G% = 1 !a%% = 0 : !b%% = 0 : a%%!4 = 0 : b%%!4 = 0 ENDPROC DEF PROCdebug1 LOCAL debug1local DEF PROCdebug2 LOCAL debug2local DEF FNsingleline : LOCAL singlelocal : IF W% THEN = X% ELSE = Y% ON ERROR LOCAL V% = TRUE : ENDPROC PRIVATE Z% LOCAL Z% CASE Z% OF WHEN 1: ENDPROC WHEN 2: ENDPROC OTHERWISE: ENDPROC ENDCASE :\ \\ This is comment line one \\ This is comment line two \\ This is comment line three \: DEF PROCdebug1 [OPT 1 mov eax,1 ; Assembler test ] IF TIME THEN ENDPROC ELSE ENDPROC MainProgramVar = PI DEF PROCdebug3 LOCAL struct{} DIM struct{1,2} PRINT struct.1 ENDPROC