Compiles a BASIC program into Z80 machine code on the TS2068. The user types the program to be compiled in lines below 5000, tests it in BASIC with RUN, then enters GO TO 5000. The compiler, itself written in BASIC, scans the program in two passes and reports the code size. It then gives the SAVE "..."CODE addresses for the result and the RANDOMIZE USR address that runs it. It handles integer BASIC only: strings, streams, decimal values, numbers beyond ±65535 and multi-letter variable names are rejected with an error message, and arrays are one-dimensional with single-letter names.
The cassette label reads “ZIP COMPILER – TIMEX SINCLAIR 2068 – LOAD “” – Dist By KNIGHTED COMPUTERS – (C) SIMON GOODWIN 1985″. The loader’s title screen describes it as “An optimizing BASIC compiler for the Timex Sinclair 2068 computer” by Simon N Goodwin and Jon A Smith, Version 1.5, and the compiler’s REM header gives a last revision date of 29/01/85. The tape holds four blocks: the BASIC loader; lib, a 1,530-byte run-time library loaded at 53247; the compiler, saved as ZIP; and demo game, a BASIC game called “Star Base!”, (c)1984 Simon N Goodwin, written in the compiler’s BASIC subset.
Program Analysis
Program Structure
The loader runs CLEAR 53246 when RAMTOP is too high, draws a title graphic with PLOT and DRAW, then loads the library CODE and the compiler. The compiler program is laid out in labeled sections:
- 5000–5440: initialization: token-type and operator-precedence tables read from
DATA, named constants for character and keyword codes (LET then=VAL "203",LET goto=VAL "236"and so on), and variables holding the line numbers of its own subroutines (pass1,pass2,maths,Z80and others). - 6000–6155: lexical analysis, reading the source program directly from memory through the
PROGsystem variable and reporting what is not allowed. - 6500–6590: pass 1, which lists each line as it scans, counts lines and sets aside space for each
DIM. - 6700–6925: memory tables and a cross-reference of line addresses kept at the top of memory, with a binary search to find a line and a list of forward references to fill in afterwards.
- 7000–7970: pass 2, a statement parser and an expression handler that uses an operator stack with precedences to turn expressions into stack-based code.
- 8000 onward: code generation from numbered templates, and an optimization module at 8280 that rewrites common pairs, for example a
PRINT CHR$of a constant into a directLD A,nandRST 16.
Demo Game
Star Base! puts a gun at the center of the screen that turns clockwise with 7 and anticlockwise with 6 and fires with 0, against enemies that approach from eight directions, with 500 rounds of ammunition and a score line. Because it is written for the compiler, it uses only integers: the keyboard is read with IN 61438 and decoded arithmetically, random numbers come from its own routine at 4530 that also mixes in PEEK 23692, and sound comes from a few bytes of machine code POKEd into the printer buffer at 23500.
Notable Techniques
- Readable names throughout the compiler, including names with spaces such as
find lineandcapital A, which the TS2068 allows in numeric variables. GO SUBthrough named variables (GO SUB pass1,GO SUB deek) instead of line numbers.- 16-bit memory access with small
deekanddokesubroutines. - Constants written as
VAL "n"and zero asNOT PIto save memory in the larger sections. - The compiled program carries a BREAK trap, and the output starts at the library address so library and program save together as one CODE block.
Source Code
20 PRINT AT 17,0;"Type RUN to test this code in BASIC, and then GO TO 5000 to compile it into machine code. Enter RANDOMIZE USR prog to execute the compiled code."
30 FOR i=16384 TO 20479
40 POKE i,BIN 10011011
50 NEXT i
60 STOP
5000 REM TMS 2068 BASIC compiler
5005 REM Last revision: 29/01/85
5010 REM S N Goodwin & J A Smith
5015 REM
5020 REM **** INITIALISATION
5022 GO TO VAL "5035"
5025 IF PEEK VAL "23731">VAL "207" THEN CLEAR VAL "53246"
5030 PRINT "Load library.": LOAD ""CODE VAL "53247"
5035 DIM t(VAL "91"): REM Token types
5040 DIM a(VAL "27"): REM Array bases
5045 DIM p(VAL "27"): DIM o(VAL "27"): REM Function precedences & opcodes
5047 LET t=VAL "1": LET i=t: LET j=i: LET s=i: LET p=i: LET pc=i: LET z=i: LET s2=i: LET c=i: LET bugs=i: REM Search fast
5050 LET maxz=VAL "22": DIM l(maxz): DIM s(maxz): REM Maths stacks
5060 RESTORE : LET y=VAL "1": LET n=NOT PI: FOR i=VAL "1" TO VAL "91": READ t(i): NEXT i: DATA n,n,n,n,n,n,y,y,y,n,n,n,n,n,n,n,n,n,n,n,n,y,n,y,y,y,y,y,n,y,y,y,y,y,y,y,y,n,y,y,y,n,n,n,n,n,n,n,n,n,n,n,y,y,y,y,y,y,y,n,n,y,n,n,n,n,y,n,y,y,y,y,y,y,n,n,y,y,y,y,y,y,n,n,y,y,y,y,y,y,n
5070 FOR i=VAL "1" TO VAL "27": READ p(i): READ o(i): NEXT i: DATA VAL "14",-VAL "1",NOT PI,-VAL "1",VAL "8",VAL "15",VAL "6",VAL "16",VAL "1",-VAL "1",VAL "6",VAL "17",VAL "9",VAL "56",VAL "8",VAL "18",VAL "12",VAL "3",VAL "5",VAL "42",VAL "5",VAL "51",VAL "5",VAL "44",VAL "11",VAL "39",VAL "11",VAL "40",VAL "11",VAL "36",VAL "11",VAL "38",VAL "11",VAL "37",-VAL "1",-VAL "1",VAL "11",VAL "47",VAL "4",VAL "41",-VAL "1",-VAL "1",VAL "2",VAL "54",VAL "3",VAL "55",VAL "5",VAL "43",VAL "5",VAL "45",VAL "5",VAL "46",VAL "11",VAL "50"
5190 REM
5200 REM **** MANIFEST CONSTANTS
5205 LET true=VAL "1": LET false=NOT PI: LET eof=VAL "5000"
5210 LET appos=VAL "39": LET plus=VAL "43": LET number=VAL "14": LET enter=VAL "13": LET string=VAL "50": LET overflow=VAL "15": LET quote=VAL "34": LET comma=VAL "44": LET space=VAL "32": LET dollar=VAL "36": LET hash=VAL "35": LET op=VAL "42": LET variable=VAL "1"
5220 LET keyword=VAL "164": LET digit 0=VAL "48": LET digit 9=VAL "57": LET capital A=VAL "65": LET capital Z=VAL "90": LET colon=VAL "58": LET semi=VAL "59"
5225 LET attr=VAL "171": LET at=VAL "172": LET tab=VAL "173": LET chr=VAL "194": LET then=VAL "203": LET to=VAL "204": LET step=VAL "205": LET ink=VAL "217": LET paper=VAL "218": LET flash=VAL "219": LET bright=VAL "220": LET inverse=VAL "221": LET over=VAL "222"
5230 LET border=VAL "231": LET dim=VAL "233": LET rem=VAL "234": LET for=VAL "235": LET goto=VAL "236": LET gosub=VAL "237": LET input=VAL "238": LET let=VAL "241": LET pause=VAL "242": LET next=VAL "243": LET poke=VAL "244": LET print=VAL "245": LET random=VAL "249": LET if=VAL "250": LET cls=VAL "251": LET clear=VAL "253": LET return=VAL "254"
5235 LET out=VAL "223": LET stop=VAL "226": LET int=VAL "186": LET bin=VAL "196": LET lbracket=VAL "40": LET rbracket=VAL "41": LET small a=VAL "97": LET small z=VAL "122"
5250 LET source=VAL "23635": LET scroll=VAL "23692": REM System variables
5255 LET index=VAL "9": LET uminus=VAL "7": LET opbrt=VAL "1": LET clbrt=VAL "2"
5390 REM
5400 REM **** BASIC LABELS
5405 LET gets=VAL "6012": LET fetch=VAL "6015": LET init=VAL "6010": LET dump=VAL "8035"
5410 LET deek=VAL "6205": LET doke=VAL "6300": LET find line=VAL "6745": LET too big=VAL "6715": LET request=VAL "6900": LET skip st=VAL "7970": LET p2error=VAL "7950"
5415 LET pass1=VAL "6500": LET pass2=VAL "7000": LET patch=VAL "6795": LET tables=VAL "6700": LET addln=VAL "6725": LET next1=VAL "6555": LET Z80=VAL "8000": LET itm=VAL "7330"
5420 LET p1error=VAL "6125": LET parsestr=VAL "7910": LET nextln=VAL "7030": LET nextst=VAL "7050": LET pre colon=VAL "7660": LET atcolon=VAL "7670": LET store=VAL "7690"
5440 LET exppush=VAL "7752": LET exppop=VAL "7770": LET maths=VAL "7785": LET expnexts=VAL "7900": LET expnext=VAL "7800": LET expop=VAL "7822": LET expdoop=VAL "7857"
5790 REM
5800 REM **** MAIN PROGRAM
5805 OVER 0: LET bottom=VAL "53247": LET vars=bottom+VAL "1537": LET arrays=vars+VAL "26"*VAL "8": LET prog=arrays: LET pc=prog
5810 LET top=VAL "65535": LET linetab=top: LET tp=linetab
5815 LET last=NOT PI: LET F1=VAL "54": LET errors=NOT PI: LET lines=NOT PI: FOR i=VAL "1" TO VAL "26": LET a(i)=NOT PI: NEXT i
5820 GO SUB pass1: PRINT PAPER VAL "5"; INK NOT PI;'lines;" LINES scanned: ";errors;" ERRORS.": IF errors THEN STOP
5830 GO SUB tables: GO TO pass2
5840 IF errors THEN PRINT AT NOT PI,NOT PI; PAPER VAL "1";"ZIP FOUND ";errors;" ERRORS.": STOP
5845 GO SUB patch: REM Correct forward jumps
5850 PRINT AT NOT PI,NOT PI; PAPER VAL "1";"ZIP HAS FINISHED (";pc-prog-VAL "34"
5860 POKE (prog+8),0: PRINT AT VAL "19",NOT PI; INVERSE VAL "1";"Save: SAVE ""???""CODE ";bottom;",";pc-bottom+VAL "2",,,"Run: RANDOMIZE USR prog [";prog;"]": STOP
5990 REM
6000 REM **** LEXICAL ANALYSIS
6005 REM Init & Fetch (source)
6010 LET t=source: GO SUB deek: LET p=t: LET s=enter: GO TO 6100
6012 IF bugs THEN RETURN
6015 LET last=s: LET s=PEEK p: LET p=p+1
6017 IF s>keyword THEN IF s<>bin THEN IF t(s-keyword) THEN RETURN : REM Keyword OK
6020 IF s=enter THEN GO TO 6100
6021 IF s<=space THEN GO TO 6015
6022 IF s>=small a THEN IF s<=small z THEN LET s=s-32: REM lower case
6023 IF s=quote THEN LET num=p: RETURN
6024 REM Analyse variable name
6025 IF last<capital A OR last>capital Z OR s<digit 0 OR s>capital Z THEN GO TO 6030
6026 IF s>digit 9 THEN IF s<capital A THEN GO TO 6030
6027 PRINT CHR$ s;: LET s=PEEK p: IF s>=small a AND s<=small z THEN LET s=s-32
6028 IF (s>=digit 0 AND s<=digit 9) OR s=space OR (s>=capital A AND s<=capital Z) THEN LET p=p+1: GO TO 6027
6029 LET s=variable: GO TO p1error
6030 IF s>=digit 0 THEN IF s=bin OR s<=digit 9 THEN GO TO 6110
6045 IF s=hash OR s=dollar THEN GO TO p1error
6050 IF s>keyword THEN GO TO p1error
6090 RETURN
6095 REM New line found
6100 LET num=256*PEEK p+PEEK (p+1): LET lnum=num: LET t=p+2: GO SUB deek: LET p=p+4: RETURN
6105 REM Number found
6110 IF PEEK p<>number THEN LET p=p+1: GO TO 6110
6112 IF PEEK (p+1)<>0 THEN LET s=overflow: LET p=p+6: GO TO p1error
6115 LET p=p+3: LET t=p: GO SUB deek: LET num=t: IF PEEK (p-1) THEN LET num=-num
6120 LET p=p+3: LET s=number: RETURN
6122 REM PASS 1 Error (p1error)
6125 PAPER 6: INK 0: PRINT
6130 IF s=dollar OR s=l bracket THEN PRINT "Strings";
6135 IF s=variable THEN PRINT "Variable name";
6140 IF s=hash THEN PRINT "Streams";
6145 IF s>keyword THEN PRINT CHR$ s;
6150 IF s=overflow THEN PRINT "Decimal values & integers beyond+/-65535";
6155 PRINT " not allowed.": LET errors=errors+1: PAPER 0: INK 7: RETURN
6190 REM
6195 REM **** DEEK and DOKE
6200 REM Deek - t=contents of address t
6205 LET t=PEEK t+256*PEEK (t+1): RETURN
6295 REM Doke - put t at address i
6300 POKE i,t-INT (t/256)*256: POKE i+1,INT (t/256): RETURN
6490 REM
6495 REM **** PASS1 Allocate RAM
6500 PAPER 0: INK 7: BORDER 0: CLS : PRINT PAPER 5; INK 0;"TMS 2068 BASIC COMPILER PASS 1.": GO SUB init
6505 IF s=enter THEN IF num>=eof THEN RETURN
6507 IF s=enter THEN LET lines=lines+1: PRINT : PRINT num;" ";: POKE scroll,255: GO TO next1
6510 IF s=number THEN PRINT num;: GO TO next1
6512 IF s=rem THEN GO TO 6545
6513 IF s=dim THEN GO SUB 6565
6514 IF s=step THEN LET f1=false
6515 IF s<>quote THEN PRINT CHR$ s;: GO TO next1
6520 PRINT """";
6530 PRINT CHR$ PEEK p;
6535 IF PEEK p<>quote THEN LET p=p+1: GO TO 6530
6540 LET p=p+1: GO SUB fetch: IF s=l bracket THEN GO SUB p1error
6542 GO TO 6505
6545 PRINT CHR$ s;: LET s=PEEK p: LET p=p+1: IF s<>enter THEN GO TO 6545
6550 LET p=p-1: GO TO next1
6555 GO SUB fetch: GO TO 6505
6560 REM Evaluate DIM
6565 PRINT " DIM ";: GO SUB fetch: IF s<capital A OR s>capital Z THEN GO TO 6590
6570 LET v=s-64: PRINT CHR$ (s);: GO SUB fetch: IF s<>lbracket OR a(v) THEN GO TO 6590
6575 PRINT "(";: GO SUB fetch: IF s<>number THEN GO TO 6590
6580 PRINT num;: GO SUB fetch: IF s<>rbracket OR num<1 THEN GO TO 6590
6585 LET a(v)=num: RETURN
6590 PRINT PAPER 6; INK 0;'"Faulty DIM statement.": LET errors=errors+1: RETURN
6690 REM
6695 REM **** BUILD RAM TABLES
6700 FOR i=1 TO 26: LET t=prog: LET prog=prog+a(i)*2: LET a(i)=t: NEXT i: LET a(27)=prog: LET pc=prog
6705 LET linetab=top-4*lines-4: LET tp=linetab: FOR i=tp TO top-1: POKE i,127: NEXT i
6710 IF tp>pc THEN RETURN
6715 PRINT AT 21,0; PAPER 6; INK 0;"Insufficient memory.": STOP
6718 REM
6720 REM **** CROSS REFERENCE ROUTINES
6725 REM Add an entry to line address table (addln)
6730 LET t=lnum: LET i=xref: GO SUB doke: LET i=xref-2: LET t=pc+1: GO SUB doke: LET xref=xref-4: RETURN
6740 REM Find line n (return T pointing to its address)
6745 LET L=1: LET u=(top-linetab)/4
6750 IF L>u THEN LET t=u*4+linetab-2: RETURN
6755 LET t1=INT ((u+l)/2): LET t=t1*4+linetab-2: GO SUB deek
6760 IF n=t THEN LET t=t1*4+linetab-2: RETURN
6765 IF n>t THEN LET u=t1-1: GO TO 6750
6770 LET L=t1+1: GO TO 6750
6790 REM
6795 REM Correct line references (patch)
6800 FOR q=linetab-2 TO tp STEP -2: LET t=q: GO SUB deek: LET i=t: GO SUB deek: LET n=t: GO SUB find line: LET t=t-2: GO SUB deek: GO SUB doke: NEXT q: RETURN
6895 REM Put address of line t at pc-1 (request)
6910 IF lnum<t THEN GO TO 6920
6915 LET n=t: GO SUB find line: LET t=t-2: GO SUB deek: LET i=pc-1: GO SUB doke: RETURN
6920 IF tp-2<=pc THEN GO TO too big
6925 LET tp=tp-2: LET i=pc-1: GO SUB doke: LET i=tp: LET t=pc-1: GO SUB doke: RETURN
6990 REM
6995 REM **** SYNTAX PARSER (pass2)
7000 GO SUB init: LET bugs=false: LET peep=0: LET oldc=0: LET oldt=0: LET xref=top-2: LET z=0
7010 LET pc=pc-1: LET c=71: GO SUB Z80: REM Trap BREAK
7015 LET t=bottom+140: GO SUB deek: LET t1=t: LET i=t1+1: LET t=vars: GO SUB doke: LET i=t1+9: LET t=prog-vars-1: GO SUB doke: REM Adjust library CLEAR code to initialise arrays (if any)
7020 LET c=49: GO SUB Z80: LET c=10: GO SUB Z80: REM CLEAR & CLS
7025 CLS : PRINT PAPER 1;"COMPILING LINE (0 bytes)"
7030 REM nextln
7040 IF lnum>=eof THEN LET lnum=9999: GO SUB addln: LET c=53: GO SUB Z80: POKE pc,255: GO TO 5840: REM End with 'OK' message
7045 PRINT AT 0,15; FLASH 1;lnum: GO SUB addln
7050 REM nextst
7080 GO SUB gets
7100 IF s<>rem THEN GO TO 7120
7105 LET s=PEEK p: LET p=p+1: IF s<>enter THEN GO TO 7105
7110 LET p=p-1: GO TO pre colon
7120 IF s<>goto AND s<>gosub THEN GO TO 7150
7125 LET c=4+(s=gosub): GO SUB gets: IF s=number THEN LET t=num: GO SUB Z80: GO TO pre colon
7130 LET bugs=number: GO TO p2error
7150 IF s=let THEN GO SUB gets: GO SUB store: GO SUB gets: GO SUB maths: LET c=assmod: LET t=assvar: GO SUB Z80: GO TO atcolon
7175 IF s=return THEN LET c=6: GO SUB Z80: GO TO pre colon
7185 IF s=if THEN GO SUB gets: GO SUB maths: LET c=48: LET t=lnum+1: GO SUB Z80: GO TO nextst
7205 IF s=stop THEN LET c=53: GO SUB Z80: GO TO pre colon
7210 IF s<>for THEN GO TO 7245
7215 GO SUB gets: LET assvar=s: GO SUB gets: GO SUB gets: GO SUB maths: GO SUB gets: GO SUB maths: IF s=step THEN GO SUB gets: GO SUB maths: GO TO 7230
7225 IF f1=false THEN LET c=2: LET t=1: GO SUB Z80: REM Generate STEP 1
7230 LET c=7+f1: LET t=assvar-65: GO SUB Z80: GO TO atcolon
7245 IF s=next THEN GO SUB gets: LET c=8+f1: LET t=s-65: GO SUB Z80: GO TO pre colon
7250 IF s<>246 THEN IF s<>252 THEN GO TO 7260
7252 LET assmod=59+(s=252): LET c=52: GO SUB Z80: REM Open #2
7254 GO SUB gets: IF s>=ink AND s<=over THEN LET sep=s: GO SUB gets: GO SUB maths: LET c=sep-189: GO SUB Z80: GO TO 7254
7258 GO SUB maths: GO SUB gets: GO SUB maths: LET c=assmod: GO SUB Z80: GO TO atcolon
7260 IF s<>border AND s<>pause AND (s<ink OR s>over) THEN GO TO 7295
7265 LET assmod=s-197: IF s=border THEN LET assmod=19
7266 IF s=pause THEN LET assmod=58
7270 GO SUB gets: GO SUB maths: LET c=assmod: GO SUB Z80: GO TO atcolon
7295 IF s=poke OR s=out THEN LET assmod=12+2*(s=out): GO SUB gets: GO SUB maths: GO SUB gets: GO SUB maths: LET c=assmod: GO SUB Z80: GO TO atcolon
7315 IF s=clear OR s=cls THEN LET c=10+39*(s=clear): GO SUB Z80: GO TO pre colon
7320 IF s<>print THEN GO TO 7500
7323 LET sep=false: LET c=52: GO SUB Z80
7325 GO SUB gets
7327 REM Check print item (itm)
7330 IF s=enter OR s=colon THEN GO TO 7480
7334 LET sep=true
7335 IF s=semi THEN GO TO 7325
7340 IF s=appos THEN LET c=2: LET t=13: GO SUB Z80: LET c=47: GO SUB Z80: GO TO 7325
7345 IF s=comma THEN LET c=2: LET t=6: GO SUB Z80: LET c=47: GO SUB Z80: GO TO 7325
7346 LET sep=false
7355 IF s=chr THEN GO SUB maths: GO TO itm
7365 IF s<>quote THEN GO TO 7385
7370 LET sptr=num
7375 GO SUB parsestr: IF num THEN LET t=sptr: LET c=26: GO SUB Z80
7376 IF more THEN LET t=quote: LET c=2: GO SUB Z80: LET c=47: GO SUB Z80: LET sptr=nptr+1: GO TO 7375
7377 LET p=nptr: GO SUB gets: GO TO itm
7385 IF s=at THEN GO SUB gets: GO SUB maths: GO SUB gets: GO SUB maths: LET c=34: GO SUB Z80: GO TO itm
7405 IF s=tab THEN GO SUB gets: GO SUB maths: LET c=35: GO SUB Z80: LET c=2: LET t=0: GO SUB Z80: LET c=47: GO SUB Z80: GO TO itm: REM Oddly, TAB requires a 2 byte parameter!
7425 IF s>=ink AND s<=over THEN LET assmod=s: GO SUB gets: GO SUB maths: LET c=assmod-189: GO SUB Z80: GO TO itm
7440 GO SUB maths: LET c=27: GO SUB Z80: GO TO itm
7480 IF NOT sep THEN LET c=2: LET t=13: GO SUB Z80: LET c=47: GO SUB Z80
7485 GO TO atcolon
7500 IF s<>input THEN GO TO 7535
7505 GO SUB gets
7507 IF s=semi OR s=comma THEN GO TO 7505
7515 IF s=colon OR s=enter THEN GO TO atcolon
7520 IF s>=capital A AND s<=capital Z THEN GO SUB store: LET c=9: GO SUB Z80: LET c=assmod: LET t=assvar: GO SUB Z80: GO TO 7507
7525 LET bugs=input: GO TO p2error
7535 IF s=random THEN GO SUB gets: IF s<>enter AND s<>colon THEN GO SUB maths: LET c=57: GO SUB Z80: GO TO atcolon
7635 GO SUB skip st: GO TO atcolon
7660 REM pre colon
7665 GO SUB gets
7670 REM atcolon
7675 IF s=colon THEN GO TO nextst
7680 IF s=enter THEN GO TO nextln
7685 GO TO 7130: REM Computed GO TO / GO SUB error??
7687 REM store (var. name in s)
7690 LET assvar=s-65: GO SUB gets: LET assmod=11: IF s=lbracket THEN GO SUB gets: LET assmod=13: GO SUB maths: GO SUB gets: IF a(assvar+1)=a(assvar+2) THEN LET bugs=index: GO TO p2error
7695 RETURN
7698 REM
7700 REM **** EXPRESSION HANDLER
7750 REM exppush
7752 IF z=maxz THEN GO TO p2error
7755 LET z=z+1: LET S(z)=tsav: LET L(z)=s2: LET oplast=true
7760 RETURN
7765 REM exppop
7770 LET z=z-1: RETURN
7780 REM Scan expression (maths)
7785 LET commas=0: LET z=0
7790 LET s2=opbrt: LET tsav=0: GO SUB exppush: REM mark start
7795 REM expnext
7800 LET s2=s: IF s2<>number THEN GO TO 7805
7802 IF L(z)=uminus THEN LET num=-num: GO SUB exppop: GO TO 7802: REM Perform negation at once
7803 LET c=2: LET oplast=false: LET t=num: GO SUB Z80: GO TO expnexts: REM Put num on Z80 stack
7805 IF s2>capital Z OR s2<capital A THEN GO TO expop
7810 LET oplast=false: LET tsav=s2-65: GO SUB gets: LET s2=s: IF s2=lbracket THEN LET s2=index: GO SUB exppush: LET s2=opbrt: GO SUB exppush: GO TO expnexts: REM Subscript is a new expression
7815 LET c=1: LET t=tsav: GO SUB Z80: GO TO expnext
7820 REM expop
7822 IF s2=plus AND oplast THEN GO TO expnexts: REM Unary plus
7823 IF s2=int THEN GO TO expnexts
7825 IF s2=appos OR (s2=comma AND commas=0) OR s2=step OR s2=to OR s=then OR s2=colon OR s2=semi OR s2=enter THEN LET s2=rbracket: REM End expression with ')'
7828 REM Identify operators: ()*+,-./<=> etc.
7830 IF s2=45 AND oplast THEN LET s2=uminus: GO TO expdoop
7835 IF s2<=47 AND s2>=40 THEN LET s2=s2-39: GO TO expdoop
7840 IF s2<=62 AND s2>=60 THEN LET s2=s2-50: GO TO expdoop
7845 IF s2<=201 AND s2>=188 THEN LET s2=s2-175: GO TO expdoop
7847 IF s2=attr THEN LET s2=27: LET commas=commas+1: GO TO expdoop
7850 GO TO p2error: REM Unrecognised operator in s2
7855 REM expdoop
7857 IF z=1 AND s2=clbrt THEN RETURN : REM finished
7860 LET prio=P(L(z)): IF L(z)=opbrt THEN LET prio=0
7865 IF oplast OR prio<P(s2) OR prio=P(s2) AND oplast THEN GO SUB exppush: GO TO expnexts: REM Don't operate yet
7875 IF L(z)=opbrt AND s2=clbrt THEN GO SUB exppop: GO TO expnexts: REM End of sub-expression
7880 IF L(z)=opbrt THEN GO TO p2error
7885 LET c=O(L(z)): LET t=S(z): GO SUB exppop: IF c<>-1 THEN GO SUB Z80: GO TO expdoop
7887 LET commas=commas-1: GO TO expdoop
7890 REM expnexts
7900 GO SUB gets: GO TO expnext
7910 REM parsestr
7915 LET i=sptr
7920 IF PEEK (i)<>quote THEN LET i=i+1: GO TO 7920
7925 LET nptr=i+1: LET more=PEEK (nptr)=quote: LET num=sptr-i: RETURN
7950 REM PASS 2 Error (p2error)
7955 LET errors=errors+1: LET t=xref+4: GO SUB deek: PRINT AT errors+1,0;t;" ";: REM Echo the most recent line reference
7956 IF bugs=index THEN PRINT "ARRAY ";CHR$ (assvar+65);"() WAS NOT DIMENSIONED": GO TO 7963
7957 IF bugs=input THEN PRINT "WRONG INPUT FORMAT": GO TO 7963
7958 PRINT "CALCULATION ";: IF z=maxz THEN PRINT "TOO COMPLEX": LET z=1: GO TO 7963
7960 IF bugs=number THEN PRINT "NOT ALLOWED": GO TO 7963
7961 PRINT "NOT UNDERSTOOD"
7963 LET bugs=false
7964 IF s<>enter THEN GO SUB fetch: GO TO 7964
7965 IF errors<15 THEN GO TO nextln
7967 STOP
7970 REM skip st
7975 IF s<>colon AND s<>enter THEN GO SUB gets: GO TO 7975
7980 RETURN
7990 REM
7995 REM **** Z80 CODE GENERATION
8000 IF errors THEN RETURN
8005 GO TO 8300: REM Attempt to optimise the code
8010 IF pc>prog+34 THEN PRINT AT 0,21; PAPER 1;pc-prog-34
8015 LET oldc=c: LET oldt=t
8020 IF c<=5 OR c=7 OR c=8 OR c=11 OR c=13 OR c=26 OR c=37 OR c=48 OR c=61 OR c=62 OR c=68 THEN GO TO 8100
8025 REM Store code routine 'c'
8030 IF c>=28 AND c<=33 THEN LET c=35: GO SUB dump: POKE pc-3,oldc-12: RETURN : REM Handle INK, PAPER etc alike
8032 REM dump (template No. c)
8035 LET j=bottom+c*2: LET t=j: GO SUB deek: LET i=t: LET t=j+2: GO SUB deek: LET j=t-i
8045 IF pc+j>=tp THEN GO TO too big
8050 LET peep=peep AND PEEK (i)=225: IF peep THEN LET pc=pc-2
8055 FOR t=1+peep TO j: POKE t+pc,PEEK (i+t-1): NEXT t: LET pc=pc+j
8060 LET peep=PEEK pc=229: RETURN
8100 GO SUB dump: LET t=oldt
8105 IF c=1 THEN LET i=pc-2: LET t=vars+8*t: GO SUB doke: RETURN
8110 IF c=2 THEN LET i=pc-2: GO SUB doke: RETURN
8115 IF c=3 THEN LET i=pc-9: LET t=a(t+1): GO SUB doke: RETURN
8120 IF c=4 OR c=5 OR c=48 THEN GO SUB request: RETURN
8125 IF c<>7 THEN GO TO 8142
8130 LET t1=t*8+vars: LET i=pc-15: LET t=t1+2: GO SUB doke: LET i=pc-11: LET t=t1+4: GO SUB doke
8135 LET i=pc-7: LET t=t1: GO SUB doke: LET i=pc-4: LET t=pc+1: GO SUB doke
8140 LET i=pc-1: LET t=t1+6: GO SUB doke: RETURN
8142 IF c<>61 THEN GO TO 8150
8144 LET t1=t*8+vars: LET i=pc-11: LET t=t1+4: GO SUB doke
8146 LET i=pc-7: LET t=t1: GO SUB doke: LET i=pc-4: LET t=pc+1: GO SUB doke
8148 LET i=pc-1: LET t=t1+6: GO SUB doke: RETURN
8150 IF c<>8 THEN GO TO 8170
8155 LET t1=vars+t*8: LET i=pc-28: LET t=t1: GO SUB doke: LET i=pc-24: LET t=t1+2: GO SUB doke
8160 LET i=pc-20: LET t=t1: GO SUB doke: LET i=pc-16: LET t=t1+4: GO SUB doke: LET i=pc-2: LET t=t1+6: GO SUB doke: RETURN
8170 IF c<>62 THEN GO TO 8180
8172 LET t1=vars+t*8: LET i=pc-18: LET t=t1: GO SUB doke: LET i=pc-14: LET t=t1: GO SUB doke
8174 LET i=pc-10: LET t=t1+4: GO SUB doke: LET i=pc-2: LET t=t1+6: GO SUB doke: RETURN
8180 IF c=11 THEN LET i=pc-1: LET t=vars+8*t: GO SUB doke: RETURN
8185 IF c=13 THEN LET i=pc-7: LET t=a(t+1): GO SUB doke: RETURN
8190 IF c<>26 THEN GO TO 8205
8192 LET i=0
8194 LET i=i+1: IF PEEK (t+i)<>quote THEN GO TO 8194
8196 IF i+pc>=tp THEN GO TO too big
8200 LET pc=pc+1: FOR j=0 TO i: POKE pc+j,PEEK (t+j): NEXT j: LET pc=pc+i: RETURN
8205 IF c=37 THEN LET i=pc-4: LET t=pc: GO SUB doke: RETURN
8210 IF c=68 THEN LET t=bottom+c*2-2: GO SUB deek: LET i=t+1: LET t=top-8: GO SUB doke: RETURN
8280 REM ZIP OPTIMISATION MODULE
8285 REM Alter common code pairs
8290 REM
8300 IF oldc=VAL "2" THEN IF c=VAL "47" THEN LET pc=pc-VAL "1": POKE pc-VAL "2",VAL "62": POKE pc,VAL "215": RETURN : REM PRINT CHR$ num
8305 IF oldc<>VAL "2" OR c<VAL "15" OR c>VAL "18" THEN GO TO VAL "8010": REM Const. op?
8310 IF oldt<VAL "7" THEN IF oldt>NOT PI THEN IF c=VAL "16" OR c=VAL "17" THEN FOR i=pc-VAL "4" TO pc-VAL "5"+oldt: POKE i,VAL "35"+VAL "8"*(c=VAL "17"): NEXT i: LET pc=pc-VAL "4"+oldt: GO TO VAL "8400": REM ADD/SUB number 1 to 6
8315 IF oldt=VAL "256" THEN IF c=VAL "18" THEN RESTORE VAL "8315": FOR i=pc-VAL "4" TO pc+VAL "7": READ j: POKE i,j: NEXT i: LET pc=pc+VAL "8": GO TO VAL "8400": DATA VAL "203",VAL "124",VAL "40",VAL "1",VAL "43",VAL "108",VAL "38",NOT PI,VAL "40",VAL "2",VAL "37",VAL "35"
8320 IF c=VAL "18" THEN IF oldt>VAL "1" THEN IF oldt<VAL "129" THEN LET i=LN oldt/LN VAL "2": IF i=INT i THEN LET pc=pc-VAL "4": FOR j=SGN PI TO i: POKE pc,VAL "203": POKE pc+SGN PI,VAL "44": POKE pc+VAL "2",VAL "203": POKE pc+INT PI,VAL "29": LET pc=pc+VAL "4": NEXT j: GO TO VAL "8400"
8325 IF c<>VAL "15" THEN GO TO VAL "8010"
8330 IF oldt=VAL "256" THEN LET pc=pc-VAL "1": POKE pc-VAL "3",VAL "101": POKE pc-VAL "2",VAL "46": POKE pc-VAL "1",NOT PI: GO TO VAL "8400"
8335 IF oldt<VAL "1" OR oldt>VAL "100" THEN GO TO VAL "8010"
8340 LET pc=pc-VAL "5": LET j=NOT PI
8345 IF oldt=VAL "1" THEN FOR i=VAL "1" TO j: LET pc=pc+VAL "2": POKE pc-VAL "1",VAL "209": POKE pc,VAL "25": NEXT i: LET pc=pc+VAL "1": GO TO VAL "8400"
8350 LET oldt=oldt/VAL "2": IF oldt<>INT oldt THEN LET oldt=INT oldt: LET j=j+VAL "1": LET pc=pc+VAL "1": POKE pc,VAL "229"
8355 LET pc=pc+VAL "1": POKE pc,VAL "41": GO TO VAL "8345"
8400 POKE pc,VAL "229": LET oldc=NOT PI: RETURNNote: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
