--- title: "Hexas, Basic*Olay" id: 56686 type: "computer_media" slug: "hexas-basicolay" url: "http://localhost/computer_media/hexas-basicolay/" markdown_url: "http://localhost/computer_media/hexas-basicolay.md" published_at: "2024-09-22T23:31:42+00:00" modified_at: "2026-04-03T07:59:00+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/09/BasicOlay.jpg" excerpt: "A two-pass symbolic hex assembler with label resolution, relative jump checking, and a relocatable code generator — all written entirely in BASIC." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "Siriusware" slug: "siriusware" taxonomy: "post_tag" url: "http://localhost/tag/siriusware/" - 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: "Machine Language" slug: "machine-language" taxonomy: "genre" url: "http://localhost/type/machine-language/" media_type: "Cassette" download_url: "https://archive.org/download/timex-sinclair-software-archive/Hexas%20BasicOlay%20%281983%29%28SiriusWare%29%28TS1000%29%28US%29%28Cassette%29.zip" mediadate: "1983" producer_company: - id: 11298 title: "SiriusWare" type: "company" url: "http://localhost/company/siriusware/" images: - url: "http://localhost/wp-content/uploads/2024/09/BasicOlay.jpg" - url: "http://localhost/wp-content/uploads/2024/09/Hexas.jpg" related_products: - id: 11883 title: "BASIC*OLAY" type: "product" url: "http://localhost/product/basicolay/" - id: 12826 title: "HEXAS*" type: "product" url: "http://localhost/product/hexas/" media_type_tags: "Machine Language" --- Hexas is a symbolic hex assembler written in BASIC that reads machine code expressed as hexadecimal bytes embedded within REM statements and assembles it to a target address. It supports forward-reference resolution, relative jump range checking, and two-pass label/symbol table management using parallel DIM arrays for names, addresses, and relocation records. A companion relocator module generates position-independent code by patching absolute addresses at load time, making the output relocatable to any memory location. The Basic*Olay loader uses a machine code routine in REM line 1 to install an overlay system, reporting entry points for COPY and OVERLAY functions along with the next available address. *** ## Program Analysis ### Program Structure The listing comprises two distinct programs. The first is **Hexas**, a symbolic hex assembler; the second is **Basic*Olay**, a loader/installer for an overlay system. In Hexas, lines 1–12 form a REM expander that grows a REM statement to hold machine code output. Lines 6670–6995 are the scanner/parser that walks through REM-embedded source. Lines 7000–7450 handle token reading and symbol definition. Lines 7460–7690 parse label and equate directives. Lines 7700–7915 perform the second-pass back-patch (relocation resolution). Lines 7970–8205 print the symbol table and completion report. Lines 8995–9090 constitute the relocator data setup routine, reached by continuing after a STOP. ### Machine Code in REM Statements Both programs store executable Z80 machine code in REM line 1. In Hexas, line 1 contains the REM expander routine invoked at line 8 via `RAND USR 16514`. In Basic*Olay, line 1 holds the overlay manager, invoked at line 45 via `RAND USR 16686`. The REM bytes are accessed directly by POKEing or PEEKing absolute addresses, a standard technique for embedding machine code without a separate binary file. ### REM Expander (Lines 1–12) Before assembly can begin, Hexas must create a sufficiently large REM statement to hold the assembled output. Lines 4–11 prompt for the required byte count `N`, patch the REM length field at addresses 16444–16445, call the machine code expander, then fill the new REM body with `22` (the byte for the NEWLINE token) as a placeholder. This is a well-known idiom for allocating workspace inside the BASIC program itself. ### Assembler Pass Structure The assembler is effectively a two-pass design implemented in a single scan with deferred back-patching: 1. **Pass 1 (scan):** Starting at BASIC address `A=16508`, the scanner (`GOSUB 6930`) seeks parenthesised hex blocks. Inside each block, hex digit pairs are assembled immediately into memory at address `Z`. Labels (prefixed `+`) are recorded in `N$(NS)`/`N(NS)`. References (prefixed `$` or `£`) are deferred into `R$(NR)`/`R(NR)`. 2. **Pass 2 (back-patch):** At line 7700, every entry in the reference table is matched against the name table. Relative references get range-checked (±128 bytes) and POKEd as signed offsets. Absolute references (`$`/`£`) are stored little-endian by the routine at line 7920. ### Symbol and Reference Tables | Array | Dimensions | Purpose | | --- | --- | --- | | `N$(LL,8)` | 48 × 8 chars | Symbol names (definitions) | | `N(LL)` | 48 elements | Symbol addresses | | `R$(LL+LL,9)` | 96 × 9 chars | Reference names with type prefix | | `R(LL+LL)` | 96 elements | Reference target addresses | The first character of each `R$` entry encodes the reference type: `+` for relative, `$` or `£` for absolute (two-byte little-endian). The name portion starts at character 2, allowing a single string to carry both metadata and the label name. ### Key BASIC Idioms - `LET I=1`, `LET O=0`, `LET T=28`, `LET TT=256`, `LET NL=118` — constants stored in variables to save repeated literal parsing overhead and improve inner-loop speed. - `T=28` is the BASIC token code for `0`; hex digits `0–9` and `A–F` are detected by testing `X >= T` and `X <= 43` (token 43 = `F`), exploiting the ZX81 token table layout where digits are consecutive. - `NL=118` is the NEWLINE token (0x76), used to detect end-of-line while scanning REM body bytes. - Hex nibble conversion uses `16*(X-T)+Y-T`, a compact formula relying on the same token-offset trick. - `IF X<0 THEN LET X=X+TT` at line 7880 converts a negative signed byte offset to its unsigned 8-bit equivalent for POKEing. ### Relocator Setup (Lines 8995–9090) After the main assembly, the user types CONT to fall through to the relocator setup. This routine iterates the reference table a second time, collecting only absolute (`$`-prefixed) reference addresses into a data table appended after the assembled code. It then patches three fields inside the relocator stub: the count of relocatable words, the base address of the assembled block, and the code length — all computed from the assembled address ranges. The result is a self-contained relocatable binary with an embedded relocation map. ### Basic*Olay Loader The second program installs the overlay manager stored in its REM line 1. Lines 35–40 write the user-supplied load address `E` into a two-byte pointer at 16507–16508 (little-endian), then call `RAND USR 16686` to execute the installer. Line 70 computes the next available address as `E+172` (`0xAC` bytes), consistent with the `01 AC 00` operand visible in the REM hex dump which loads BC with the copy length. ### Notable Techniques - Source code is embedded directly in BASIC REM lines as parenthesised hex strings — no separate editor or file format is needed. - The scanner navigates raw BASIC line storage by PEEKing absolute addresses and advancing `A` byte-by-byte, bypassing the BASIC interpreter’s own line-access mechanisms for speed. - Line number recovery for error messages uses `TT*PEEK AA + PEEK (AA+I)`, decoding the two-byte big-endian line number stored at the start of each BASIC line in memory. - Error bytes are marked in-place with `POKE A, Y+128` (setting bit 7), providing a visual inverse-video marker in the REM source for post-mortem debugging. - The `GOTO 7490` inside the label-parsing loop at line 7560 uses a bare `LET W=M` / loop-back pattern rather than a WHILE construct, a common ZX81 BASIC workaround for the absence of structured loops. ### Potential Issues - Line 7560 reads `GOTO 7490` but the loop body starts at 7500; line 7490 only resets `LET W=M`, so the first iteration after a non-`=` character does redundantly re-execute that assignment — functionally harmless but slightly unexpected. - The REM expander fills new bytes with `22` (NEWLINE token), but the assembled output subsequently overwrites these; if `N` is underestimated the assembler will silently overwrite subsequent BASIC lines without warning. - The symbol table is capped at 48 definitions and 96 references (`LL=48`); exceeding these limits will cause a subscript error with no graceful message. ## Source Code ``` 1 REM 2A3C4011220ED52444D21824019C5CD9E9C12A7F409227F402A29409222940C9 2 REM SIRIUSWARE HEXAS* 3 REM COPYRIGHT 1983 D.B.WOOD 4 PRINT "BYTES NEEDED IN REM" 5 INPUT N 6 POKE 16445,INT (N/256) 7 POKE 16444,N-256*PEEK 16445 8 RAND USR 16514 9 FOR I=0 TO N-1 10 POKE 16514+I,22 11 NEXT I 12 REM ==END OF REM EXPANDER== 6670 REM ==RELOCATING ASSEMBLER= 6675 REM PLACE YOUR MACH. LANG. CODE IN FRONT OF THIS 6680 REM (3E 00 A7 2A 7B40 11 8240 ED52 E5 21 0000 6685 REM (5E 23 56 23 E3 E5 EB 4E 23 46 EB 09 EB 72 2B 73 E1 E3 3D 20 EB 6690 REM (E1 11 8240 19 EB 01 0000 EDB0 C9) 6702 LET I=1 6704 LET T=28 6705 LET TT=256 6706 LET NL=118 6708 LET O=0 6710 LET NS=I 6720 LET NR=NS 6730 LET LL=48 6740 DIM N$(LL,8) 6750 DIM R$(LL+LL,9) 6760 DIM N(LL) 6770 DIM R(LL+LL) 6780 PRINT "START ADDR: "; 6790 INPUT Z 6795 PRINT Z 6800 LET ZZ=Z 6810 LET A=16508 6870 GOSUB 6930 6880 IF X$="(" THEN GOSUB 7050 6910 IF X$=")" THEN GOTO 7700 6920 GOTO 6870 6930 LET A=A+5 6940 IF 234=PEEK A THEN GOTO 7000 6950 LET A=A+I 6960 IF NL<>PEEK A THEN GOTO 6950 6970 IF NL<>PEEK (A+I) THEN GOTO 6930 6980 PRINT "NO CODE" 6985 IF Z=ZZ THEN STOP 6990 PRINT " END "")""" 6995 STOP 7000 LET A=A+I 7010 LET X$=CHR$ PEEK A 7020 IF X$=")" THEN RETURN 7030 IF X$<>"(" THEN GOTO 6960 7040 RETURN 7050 LET AA=A-5 7060 GOSUB 7180 7070 IF E=I THEN RETURN 7080 LET A=A+I 7090 LET Y=PEEK A 7100 IF Y43 THEN GOTO 7150 7120 POKE Z,16*(X-T)+Y-T 7130 LET Z=Z+I 7140 GOTO 7060 7150 PRINT "-HEX ERROR" 7160 POKE A,Y+128 7170 PRINT "AT LINE ";TT*PEEK AA+PEEK (AA+I) 7175 STOP 7180 LET E=O 7190 LET A=A+I 7200 LET X=PEEK A 7210 IF X=O THEN GOTO 7190 7220 IF X=NL THEN LET E=I 7230 LET X$=CHR$ X 7240 IF X$=")" THEN LET E=I 7250 IF E=I THEN RETURN 7260 IF X43 THEN GOTO 7290 7280 RETURN 7290 IF X$="+" OR X$="$" OR X$="£" THEN GOTO 7380 7310 GOSUB 7460 7330 LET N$(NS)=S$ 7340 LET N(NS)=Z 7350 IF W>O THEN LET N(NS)=M 7360 LET NS=NS+I 7370 GOTO 7200 7380 GOSUB 7460 7400 LET R$(NR)=S$ 7410 LET R(NR)=Z 7420 IF X$="$" OR X$="£" THEN LET Z=Z+I 7430 LET NR=NR+I 7440 LET Z=Z+I 7450 GOTO 7200 7460 LET S$=X$ 7480 LET M=O 7490 LET W=M 7500 LET A=A+I 7510 LET Q$=CHR$ PEEK A 7520 IF Q$=" " OR Q$>"Z" THEN RETURN 7540 IF Q$="=" THEN GOTO 7570 7550 LET S$=S$+Q$ 7560 GOTO 7490 7570 IF X$="+" OR X$="$" OR X$="£" THEN GOTO 7680 7580 LET A=A+I 7590 LET X=PEEK A 7600 IF X=O AND W<=O THEN GOTO 7580 7610 IF X=NL THEN RETURN 7630 IF X=O THEN RETURN 7640 IF X43 THEN GOTO 7680 7650 LET M=16*M+X-T 7660 LET W=W+I 7670 IF M<65536 THEN GOTO 7580 7680 PRINT "-EQU ERROR" 7690 GOTO 7910 7700 LET W=O 7720 FOR J=I TO NR-I 7730 FOR K=I TO NS-I 7740 IF N$(K)=R$(J,2 TO ) THEN GOTO 7800 7750 NEXT K 7770 PRINT "UNDEFINED: ";R$(J,) 7780 LET W=W+I 7785 GOTO 7895 7800 IF R$(J,I)="$" OR R$(J,I)="£" THEN GOTO 7920 7820 LET X=N(K)-R(J)-I 7830 IF X<128 AND X>-129 THEN GOTO 7880 7850 PRINT "REL JUMP RANGE ERROR" 7860 PRINT J;"TH REFERENCE. JUMP TO ";N$(K) 7862 FOR K=I TO NS-I 7864 IF R(J)"$" THEN GOTO 9040 9015 LET NN=INT (R(J)/TT) 9020 POKE Z,R(J)-NN*TT 9025 POKE Z+I,NN 9030 LET Z=Z+I+I 9040 NEXT J 9050 POKE ZX-47,(Z-ZX)/2 9055 LET NN=INT (ZX/TT) 9060 POKE ZX-35,ZX-NN*TT 9065 POKE ZX-34,NN 9070 LET K=ZX-ZZ-48 9075 LET NN=INT (K/TT) 9080 POKE ZX-5,K-NN*TT 9085 POKE ZX-4,NN 9090 PRINT "TOTAL BYTES INCLUDING RELOCATIONDATA: ";Z-ZZ 1 REM AF18137F5CD23F2A29401150192216407ECDD914CD8A15ED437B402A16403E76BE2320FCCD141223E40CD141F12A3E4038FEBED52444D2A7B40EBEDB02B7E12C9E5A7ED52444DD5EB2BCDAD9E1D1D5EDB0D12A7B407EFE76C8E5D5CDF29CDC5ED1D5C52A1C40E5EB2BCDAD9ED5B1C40E1EDB8C1D1E1EDB018DAE53E76BE202CFF232323233EEABE287E1CDF29EB18E83ED13023EDA120EFEA1F413E76BE20E723D1C93E3A72A7B40118240ED52E5215E415E235623E3E5EB4E2346EB9EB722B73E1E33D20EBE111824019EB1AC0EDB0C9A940AF4024411B1B1B1B1B1B1B1B1B1B1B1B1B1B1B1B1B1B1B1B1B1B1B1B 2 REM COPYRIGHT 1982 D.B.WOOD 10 SAVE "BASIC*OLA%Y" 15 PRINT "ADDR:" 30 INPUT E 35 POKE 16508,INT (E/256) 40 POKE 16507,E-256*PEEK 16508 45 RAND USR 16686 50 PRINT "ENTRY POINTS" 55 PRINT " COPY ";E 60 PRINT "OVERLAY ";E+3 70 PRINT "NEXT AVAIL ADDR ";E+172 ```