ZIP BASIC Compiler

Date: 1985
Type: Cassette
Platform(s): TS 2068
Tags: Compiler

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, Z80 and others).
  • 6000–6155: lexical analysis, reading the source program directly from memory through the PROG system 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 direct LD A,n and RST 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 line and capital A, which the TS2068 allows in numeric variables.
  • GO SUB through named variables (GO SUB pass1, GO SUB deek) instead of line numbers.
  • 16-bit memory access with small deek and doke subroutines.
  • Constants written as VAL "n" and zero as NOT PI to 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.

Related Products

An optimizing BASIC compiler for the TS 2068 computer.

Related Articles

The resident language in the T/S 2068 is Sinclair BASIC (an altered version of Microsoft BASIC). BASIC is a relatively...

Image Gallery

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: RETURN

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.