program chkif(ifil,output); { CHKIF v0.1 by J.Loke 87Sep06 Checks an ASM or PRN program for correct conditional assembly and macro balance. Vers Date Name Notes 0.10 87Sep06 J.Loke Original version in TURBO Pascal Known bug: Will not process statements with non-comment ';' Known bug: Will not process COMMENT properly } type str=string[255]; var cmd: string[127] absolute $80; ifil: text; p,v: boolean; i,m,n: byte; l: integer; lif,lelse,lmacro,nmacro: array [1..16] of integer; a,b,c: str; function kwd(wrd: str): boolean; { KWD uses global variable b: str KWD returns true if WRD is a keyword in string B WRD must be delimited by chars belonging to the set PUNC, else KWD returns false } const punc: set of char = [#0..'?','['..'`','{'..'~']; var i: byte; f: boolean; begin i:=pos(wrd,b); if (i=0) then { WRD not in string B } f:=false else begin f:=true; { Test if a PUNCtuation char precedes WRD } if (i>1) then f:=(b[i-1] in punc); { Test if a PUNCtuation char follows WRD } if (f) then if (i -Q=quiet') else begin { Open file for sequential read } assign(ifil,cmd); reset(ifil); { Set P flag if PRN output truncation needed } p:=(pos('.PRN',cmd)>0); { Initialize line counter, IF/ELSE and MACRO level pointers } l:=0; n:=0; m:=0; { Process each line of the file } while not(eof(ifil)) do begin readln(ifil,c); l:=l+1; { If .PRN file, strip address and HEX display } if (p) then begin i:=16; if (copy(c,1,1)=#12) then i:=i+1; delete(c,1,i); end; {if (p)} { Process each statement of the file } while (length(c)>0) do begin { Strip statement from multi-statement line } i:=pos('!',c+'!')-1; a:=copy(c,1,i); delete(c,1,i+1); { Ignore assembly directives or comment lines } if (copy(a,1,1)='$') or (copy(a,1,1)='*') then i:=0 else begin { Strip leading spaces and tabs } while (copy(a,1,1)=' ') or (copy(a,1,1)=#9) do delete(a,1,1); i:=pos(';',a+';')-1 end; if (i>0) then begin { Process non-empty statements, convert to upper case } b:=copy(a,1,i); for i:=1 to length(b) do b[i]:=upcase(b[i]); { ENDIF keyword } if kwd('ENDIF') then begin if v then writeln(l:5+n+m,' ',a); if (n=0) then writeln(l:5+n+m,'*ENDIF: No matching IF') else n:=n-1; end {if kwd('ENDIF') then } { IF keyword } else if kwd('IF') then begin if v then writeln(l:6+n+m,' ',a); if (n=8) then writeln(l:6+n+m,'*IF: Too many IF''s'); n:=n+1; lif[n]:=l; lelse[n]:=0; end {else if kwd('IF')} { ELSE keyword } else if kwd('ELSE') then begin if v then writeln(l:5+n+m,' ',a); if (n=0) then writeln(l:5+n+m,'*ELSE: Missing IF') else begin if (lelse[n]<>0) then writeln(l:5+n+m,'*ELSE: Missing ENDIF'); lelse[n]:=l; end end {else if kwd('ELSE')} { MACRO keywords } else if kwd('MACRO')or kwd('IRP')or kwd('IRPC')or kwd('REPT') then begin if v then writeln(l:6+n+m,' ',a); if (m=8) then writeln(l:6+n+m,'*MACRO: Too many MACRO''s'); m:=m+1; lmacro[m]:=l; nmacro[m]:=n; end {else if kwd('MACRO')or kwd('IRP')or kwd('IRPC')or kwd('REPT')} { EXITM keyword } else if kwd('EXITM') then begin if v then writeln(l:5+n+m,' ',a); if (m=0) then writeln(l:5+n+m,'*EXITM: Missing MACRO') end {else if kwd('EXITM')} { ENDM keyword } else if kwd('ENDM') then begin if v then writeln(l:5+n+m,' ',a); if (m=0) then writeln(l:5+n+m,'*ENDM: Missing MACRO') else m:=m-1 end {else if kwd('ENDM')} { Process next statement on this line } end {if (i>0)} end {length(c)=0} end; {while not(eof(ifil))} writeln; { IF/ELSE/ENDIF balance error } if (n>0) then for i:=n downto 1 do begin write('CHKIF: Missing ENDIF. Unmatched IF at line',lif[i]:6); if (lelse[i]<>0) then write(' Unmatched ELSE at line',lelse[i]:6); writeln end; {for i:=n downto 1} { MACRO/ENDM balance error } if (m>0) then for i:=m downto 1 do writeln('CHKIF: Missing ENDM. Unmatched MACRO at line',lmacro[i]:6); end; {if (length(cmd)=0)} { End of Program } writeln('CHKIF: Done'); end.