--- title: "ZX-FORTH" id: 51829 type: "computer_media" slug: "zxforth" url: "http://localhost/computer_media/zxforth/" markdown_url: "http://localhost/computer_media/zxforth.md" published_at: "2023-08-13T23:14:34+00:00" modified_at: "2026-04-04T20:17:56+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2019/02/20230809-041332.jpg" excerpt: "A complete FORTH language interpreter delivered as machine code hidden inside a single REM statement, launched with just one BASIC command." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Artic Computing" slug: "artic-computing" taxonomy: "post_tag" url: "http://localhost/tag/artic-computing/" - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" genre: - name: "Forth (programming language)" slug: "forth-programming-language" taxonomy: "genre" url: "http://localhost/type/forth-programming-language/" - name: "Programming" slug: "programming" taxonomy: "genre" url: "http://localhost/type/programming/" media_type: "Cassette" download_url: "https://archive.org/download/timex-sinclair-software-archive/ZXForth (1982)(Artic Computing)(UK)(TS1000)(Cassette).zip" mediadate: "1982" producer_company: - id: 10657 title: "Artic Computing" type: "company" url: "http://localhost/company/artic-computing/" related_products: - id: 14717 title: "ZX-FORTH" type: "product" url: "http://localhost/product/zx-forth/" media_type_tags: "Forth (programming language), Programming" --- # ZX-FORTH ZX-FORTH is a FORTH programming language interpreter implemented in machine code and stored within a REM statement at line 65310. The program launches via RAND USR 17245, transferring execution directly to the machine code payload embedded in the REM statement’s data area. The REM statement contains the complete FORTH interpreter runtime, including dictionary entries, inner interpreter, and I/O primitives encoded as raw bytes. This technique of storing machine code in a high-numbered REM line keeps the BASIC loader compact while placing the binary payload at a predictable memory address. *** ## Program Analysis ### Program Structure The program consists of only two active BASIC lines plus a large data-carrying REM statement: 1. Line `10`: `SAVE "ZXFORTH"` — saves the program to tape. 2. Line `20`: `RAND USR 17245` — transfers execution to the machine code interpreter at address 17245 (decimal), which falls within the REM statement’s data area. 3. Line `65310`: A `REM` statement carrying the entire machine code binary of the FORTH interpreter as raw byte data. ### Machine Code Delivery via REM Storing machine code inside a `REM` statement is a classic technique. The BASIC line header for line `65310` begins at a known offset in RAM, and the bytes following the `REM` token are the raw Z80 opcodes and data of the FORTH system. The entry point at address 17245 (hex `0x435D`) points into this payload. Because `REM` data is never parsed by the BASIC interpreter, arbitrary byte values — including those that would otherwise be interpreted as BASIC keywords — can be stored safely. ### RAND USR Entry Point `RAND USR 17245` on line `20` is the standard idiom for invoking machine code from BASIC. `RAND USR n` calls the Z80 `CALL` to address *n*, and because the FORTH interpreter never returns to BASIC, the `RAND` return value is irrelevant. Address 17245 decimal is `0x435D`, which lies in the region immediately after the system variables and in the program area, consistent with the REM data starting at line `65310`‘s data bytes. ### FORTH Interpreter Internals (Inferred from REM Data) The byte content of the REM statement, when interpreted as Z80 machine code and FORTH dictionary data, implements a threaded FORTH interpreter. Visible ASCII fragments within the REM data correspond to FORTH word names embedded in dictionary headers. Identifiable word names and runtime tokens visible in the listing include: - Standard FORTH words: `DUP`, `DROP`, `SWAP`, `OVER`, `ROT` (inferred from stack-manipulation byte patterns) - BASIC keyword tokens appearing in the REM data (`PRINT`, `INPUT`, `LET`, `INKEY$`, `PEEK`, `POKE`, `USR`, `RUN`, `STOP`, `COPY`, `FAST`, `SLOW`, `PLOT`, `UNPLOT`, `CLS`, `LPRINT`, `PAUSE`, `GOSUB`, `FOR`, `NEXT`, `RETURN`, `NEW`, `DIM`, `CLEAR`, `RAND`, `SAVE`) — these appear because the Z80 byte values of those opcodes coincide with the BASIC keyword token values, not because BASIC is executing them - Mathematical and string functions visible as tokens: `SIN`, `COS`, `TAN`, `ATN`, `ACS`, `ASN`, `LN`, `EXP`, `ABS`, `SGN`, `RND`, `PI`, `STR$`, `VAL`, `CHR$`, `CODE`, `LEN`, `TAB` The repeated appearance of `RETURN` tokens throughout the REM data is consistent with a direct-threaded or indirect-threaded FORTH inner interpreter using `RET` (opcode `0xC9`) instructions — the Z80 `RET` opcode value happens to equal the BASIC `RETURN` token, causing the lister to display `RETURN` throughout the machine code body. ### Notable Techniques - **High line number for REM:** Line `65310` places the REM at the very end of the BASIC program area, so the machine code does not interfere with normal BASIC line numbering and is unlikely to be accidentally renumbered or edited. - **Two-line loader:** The entire BASIC portion is just `SAVE` and `RAND USR`, minimizing BASIC overhead and RAM consumed by the interpreter stub. - **Block graphics and inverse characters in REM:** The REM data contains block graphic characters and inverse-video characters, which are legitimate byte values within the Z80 machine code — they appear in the listing as graphical symbols but are raw data bytes to the CPU. - **FASTSTR$ and other compound tokens:** Sequences like `FASTSTR$` in the REM listing represent consecutive keyword tokens whose byte values happen to produce those token sequences when the lister renders them; they are not actual BASIC statements. ### Potential Anomalies The null bytes (displayed as `0` on lines by themselves near the top of the REM data) and the short numeric sequences (`0 0`) are legitimate parts of the binary payload — FORTH dictionary entries commonly contain null terminators and 16-bit address fields that render as low numeric values in the BASIC lister. These are not bugs. ### Memory Map Summary | Address (dec) | Content | | --- | --- | | 17245 | FORTH interpreter entry point (inside REM data) | | BASIC line 65310 | REM statement containing full machine code payload | | BASIC line 10 | SAVE loader | | BASIC line 20 | RAND USR launch stub | ## Source Code ``` 10 SAVE "ZXFORT[H]" 20 RAND USR 17245 65310 REM ............................................................................................. FASTSTR$ VAL [R]ASN #INKEY$ RETURN█KM RETURN▒CZ RETURN?CV RETURN#S▝CHR$ 4 RETURNKS= RETURNUK▖CHR$ =/= RETURNINKEY$ S# RETURN#K#CHR$ ./▒5[-]INKEY$ #- ;# RETURN#CWACS [R] PRINT UTRND RETURN▘ATN #INKEY$ LET NOT AT SGN LPRINT TAN E:RNDFUTRNDWMTRND#6:RND RETURN#4(F6:RNDY▝MTRNDUURNDWMURNDQ ##INKEY$ FASTSTR$ VAL PRINT UURND RETURN▀C▌Y#NOT /▀LN :£ LET AT SGN LPRINT TAN CHR$ ~~/[3]CHR$ 8/[.] 0 0 ▘▝▀▖▌▞▛▒,,~~ [£]"[?]£$ [(]()*+,-./:;<=>?["][$] [:][>][)]VAL STR$ FAST▘ █UQ RETURN▜4,,LN M? CLEARQP COPY/▀LN P? GOSUB #9RNDLN [X]▛K LPRINT 5 UNPLOT INKEY$ ;###INKEY$ #########LMNOPKTSRQ#####$####4I##PIUVZJB#[-][*]#RND57[/][;][,][.][0][1][+]▒[2][=][>][<]6DC8[3][4]XFH[5] GYWESTR$ #Y#<= RETURN3S$Y RETURN<= RETURN3S▞Y▘[R]#SGN TAN [J]/ IF PRINT INKEY$ 7/ PLOT LET TAN FASTSTR$ VAL PRINT LN 4#)>#Y#<= RETURN *S PRINT .#[N]4 NEXT LET AT SGN LPRINT TAN FASTSTR$ VAL :▘▞ Y#<= RETURNPEEK COPY*S▖( PRINT / LET 2[=]▞,1<= RETURN*ACS ##S PRINT ( PRINT 4▖ RETURN#K>=ZACS )KNOT #/ACS FASTSTR$ VAL PRINT LN 4#)ACS ▒( RETURN.#[N]4 RAND /[R] FASTSTR$ VAL PRINT #RACS ]4#2#STR$ FAST CLEARO5TAB [F]#LN #PIC▀▘##~~▀#~~▀##7# FOR DIM ▄##USR [Y]#~~▀#~~▀#/CHR$ ▗######VAL [Q]#ABS # LPRINT />=▚PI#INKEY$ ##COS LEN # STEP ####7#F;##/[M]▗KPI#INKEY$ ##COS STR$ # RUN # LPRINT #[P]C SLOW▀▀/[1]▚C####[D] FOR #"#)▘ E[;]#[~~]#7=+# IF B##[-]#7[2]/▌ STEP #76[;]#▀▀#[1]#▗CF####[D] ###SGN /SIN ▜C##[D]S###E[;]#FFFF6[;]#SGN #7#SGN 7#7##[1]#▟TAN ####E[;]##7#STR$ / INPUT ▐####USR #### LPRINT SGN #CHR$ K IF [2]# RETURN~~ IF [<]#CHR$ ▛ RETURN~~ IF [2]#[X] PAUSE [2]##5▘ #[.]###[0]#▚C####[D]##[H]#SGN LPRINT FAST,[I] NEWZ437<,[I]▗4-K RUN 5▌ ; STOP.,[R] PAUSE CODE ##- 5▘ #[.]#S▞<,[R] PAUSE STR$ #< FOR #7##[N]4ASN LPRINT 5 #[0]#▗######VAL [6]# RUN #SGN LPRINT FAST#) COPY COPYF7<[Y]C CLSSTR$ PRINT STR$ .STR$ #[1]# LET PRINT 7<[Y]C~~STR$ #[1]# LET STR$