--- title: "Parallel 1000" id: 53140 type: "computer_media" slug: "parallel-1000" url: "http://localhost/computer_media/parallel-1000/" markdown_url: "http://localhost/computer_media/parallel-1000.md" published_at: "2024-02-21T00:52:27+00:00" modified_at: "2026-04-04T20:17:50+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/02/Byte-Back-Par-1000.jpg" excerpt: "A parallel printer driver installer that relocates Z80 machine code to four possible memory locations and reports USR entry points for LCOPY, LLIST, CLRBUF, and LPRINT." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Byte-Back Co" slug: "byte-back-co" taxonomy: "post_tag" url: "http://localhost/tag/byte-back-co/" - 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: "Printer" slug: "printer" taxonomy: "genre" url: "http://localhost/type/printer/" media_type: "Cassette" download_url: "https://archive.org/download/timex-sinclair-software-archive/Parallel%201000%20%281985%29%28Byte-Back%29%28US%29%28TS1000%29%28Cassette%29.zip" mediadate: "1985" producer_company: - id: 10711 title: "Byte-Back Co" type: "company" url: "http://localhost/company/byte-back-co/" images: - url: "http://localhost/wp-content/uploads/2024/02/Byte-Back-Par-1000.jpg" related_products: - id: 13501 title: "Parallel 1000" type: "product" url: "http://localhost/product/parallel-1000/" media_type_tags: "Printer" --- # Parallel 1000 Parallel 1000 is a ZX81/TS1000 printer driver installation utility that configures and relocates a Z80 machine code parallel printer driver to one of four memory locations: above RAMTOP, below system variables (address 8192), at the line 1 REM statement, or at a user-specified address. The program validates the relocation address against key system boundaries, checking that it falls between 8192 and 32767 and avoids the system variable area (15784–17325). It calculates high and low bytes of the target address using integer division and POKEs them into memory at addresses 17127 and 17128, then activates the driver via USR calls to fixed offsets (17161 or 17157). Entry points for LCOPY, LLIST, CLRBUF, and LPRINT functions are reported as USR expressions relative to the BASE address, with offsets of 167, 325, 560, and 171 respectively. The REM statements at lines 1 and 3 contain the actual Z80 machine code payload embedded in the BASIC program. *** ## Program Analysis ### Program Structure The program is organized into several functional blocks: 1. **Lines 1 and 3 (REM blocks)** — contain the embedded Z80 machine code for the parallel printer driver, stored as raw bytes within REM statement payloads. 2. **Lines 1000–1110** — user interface for linefeed control code configuration and relocation mode selection. 3. **Lines 1210–1280** — custom address entry and validation loop. 4. **Lines 1300–1450** — handlers for the three preset relocation modes (below system variables, at REM, above RAMTOP). 5. **Lines 1560–1600** — address patching: writes LRAM and HRAM into the machine code at addresses 17127 and 17128. 6. **Lines 1700–1799** — output section reporting BASE and entry point addresses, then activating the driver via USR. 7. **Lines 1800–1820** — subroutine decomposing RAM into high/low byte variables. 8. **Lines 9000–9020** — diagnostic loop printing memory contents from 16626 to 16652. ### Machine Code Embedding The Z80 printer driver is embedded directly in the two REM statements at lines 1 and 3. This is a standard technique for storing machine code in BASIC programs: the bytes following the REM token are never interpreted by the BASIC interpreter and can hold arbitrary binary data. The driver is activated by calling `USR 17161` or `USR 17157` at lines 1785 and 1791, which are fixed absolute addresses pointing into or near the REM data area. ### Relocation Modes The program supports four relocation destinations, selected by single-character input: | Key | Destination | BASE value | | --- | --- | --- | | R | Above RAMTOP | PEEK(16388)+256*PEEK(16389)+1 | | V | Below system variables | 8192 | | T | At line 1 REM | 16521 (hardcoded as BASE) | | E | User-specified address | User input, validated | RAMTOP is read from system variables at addresses 16388–16389 using the standard high/low byte formula. For the “above RAMTOP” mode, the program enforces an upper limit of 32138 (code cannot run above 32767, and a margin is added), stopping with an error message if RAMTOP is too high. ### Address Validation For the user-specified (“E”) mode, the input address is checked against multiple boundaries. Addresses below 8192 or above 32767 are rejected outright. Addresses in the range 15784–17324 are also rejected, as this encompasses the system variable area. The validation loop at lines 1210–1280 re-prompts on any invalid entry, printing the offending value with an “IS AN INVALID RELOCATION ADDRESS” message before looping back to line 1225. ### Address Patching Mechanism The subroutine at lines 1800–1820 decomposes the `RAM` variable into `HRAM` (high byte, via `INT(RAM/256)`) and `LRAM` (low byte, via `RAM - HRAM*256`). These are then POKEd into specific locations within the machine code (addresses 17127 and 17128), effectively self-modifying the driver’s internal jump or load address so it operates correctly at the chosen relocation target. This is a classic position-dependent patching approach rather than fully position-independent code. ### Entry Point Reporting After relocation, the program prints the USR call addresses for four driver entry points, all expressed as `BASE + offset`: - `LCOPY` at BASE+167 - `LLIST` at BASE+325 - `CLRBUF` at BASE+560 - `LPRINT` at BASE+171 These offsets are fixed constants baked into the listing, implying the machine code layout is fixed-size and the entry points are at known positions within it. ### Linefeed Control Lines 1000–1007 ask whether the printer needs a separate linefeed control code. If the user enters “N”, execution continues normally. If “Y”, the program skips the POKE at line 1007. The POKE at line 1007 writes 201 (the Z80 RET opcode) to address 16524, which is within the system variable / ROM call area, presumably disabling or short-circuiting a linefeed-generating routine within the driver. ### Activation and Cleanup At lines 1785 and 1791, the driver is activated by `USR 17161` and `USR 17157` respectively. When the “T” (REM) mode is chosen, the `NEW` command at line 1796 is skipped (branching to line 1799 `STOP`), since the driver lives in the REM area and a NEW would destroy it. For all other modes, `NEW` is called to clear BASIC memory, leaving only the relocated machine code resident. ### Diagnostic Routine Lines 9000–9020 provide a simple memory inspection loop, PRINTing the address and byte value for each location from 16626 to 16652. This range covers part of the system variables / driver patching area and appears to be a debugging aid left in the final listing. ### Notable Idioms and Anomalies - The use of `PAUSE 180` at line 1775 gives the user approximately six seconds to read the entry point information before the screen is potentially modified. - Line 1790 is a bare `REM` with no content, serving as a label target for the GOTO at line 1782 — a common ZX81 BASIC technique to annotate branch targets. - The `B$="Z"` assignment at line 1050 is set but never referenced again in the visible BASIC code, suggesting it may be a remnant of an earlier version or used within the machine code context. - The variable `L` assigned from `USR` calls at lines 1785 and 1791 captures the return value of the machine code routine but is never used, which is normal when USR is called purely for its side effects. ## Source Code ``` 1 REM LN PEEK PILN [E]RNDY~~LN STR$ PITAN FASTVAL NEWZ#▞ 5[M]RND,,LN [E]RND#PEEK 3 AT LPRINT TAN <=3 ACS #C RAND TAN 4EEEEEEEEEE678UZCDYWXFHEJVGIKLMNOPQRSTINKEY$ PI########################5B )▛INKEY$ :▞,#Y X4 CLEAR<$4 PRINT TAN RNDEXP )LN PAUSE RNDLN DIM PI#LN [+]RND7# RETURN#4 RUN LN BINKEY$ 7# ▌COS / GOSUB LN [E]RNDLN [,,]RNDTAN ▞-/ OR LN PAUSE RNDE£RND7 FAST FOR E:RND GOSUB # FOR LPRINT #LN [+]RND71C▛# RETURN#4 POKE / PRINT LN BINKEY$ TAN :£TAN 5#RND# RETURN#COS LN TO INKEY$ #7#Y LN PEEK INKEY$ 7.#[M]C-# RETURN#C* RETURN#C" RETURNRNDK#LN [+]RND/ STOP/USR ; LN BINKEY$ 7/INT 77777...../LN PRINT LN **INKEY$ 4~~Y LN [+]RNDY█LN PEEK INKEY$ LET FASTSTR$ LN #,,~~#LN [+]RND▀# NEW█C PRINT Y LN [+]RNDSGN LPRINT /[C] RETURN#SABS RETURN[Z]KEXP /[L]LN PAUSE RND/[L]▘ THENINKEY$ ▝TAN ▘ THENINKEY$ ~~[R]TAN #7#7 FASTLN PIPI:▌5 IF INKEY$ #LN [+]RNDF$4 SAVE LPRINT TAN 08863 5SPI6OPI▘▖ 5 PLOT INKEY$ ,,Q06MPIVAL EOPI#7#76OPIVAL LPRINT 6QPIEKPILN #PILN [2]PIAT $4**TAN RUN INKEY$ RNDPI~~ (B CONT ▀# ~~ ▘ FOR 6KPILN RETURNINKEY$ EKPI FOR LN [-]PITAN #[W]**""#[X]TAN # # VAL EQPI6#PIY~~ PRINT LET XC1 PRINT EMPIOE#PI6#PI FOR EQPI;6#PI FASTAT LN [A]PIC LPRINT S TO LET FOR E#PI FOR ZLN [H]PI AT TAN 5 PLOT INKEY$ #LEN 0#TAN C▝K▀LN #PITAN EKPILN #PITAN #J##J#<;6KPITAN E£RND7▞▀:4Q 7$4 IF 7▌4 POKE E£RND7LN INPUT PITAN Y$PEEK 3 FAST56( STOP STOPF#[O]C▝/ RUN LPRINT TAN ▘#E£RND7TAN 6:RNDY/MURNDTAN 3 REM ## LN KINKEY$ TAN ▘[,,]RNDE SCROLLPI[R] GOSUB PI FASTAT -X<>5Z#<># <>7<># <>7STR$ #7# FOR ,, FOR #F#SGN +4 CONT GOSUB # SCROLLPI5[,,]RND▘#▝ GOSUB [K]TAN RND[1]RND[5]INKEY$ :INKEY$ +INKEY$ 2INKEY$ CINKEY$ FINKEY$ PINKEY$ #INKEY$ #INKEY$ )INKEY$ #INKEY$ #INKEY$ █INKEY$ [,,]INKEY$ [0]INKEY$ [7]INKEY$ [C]PI[Q]INKEY$ [N]INKEY$ [Y]INKEY$ INT INKEY$ USR INKEY$ OR INKEY$ FASTINKEY$ REM INKEY$ INPUT INKEY$ COPYPI▝PI▒PI:PI>PI;PI2PI5PI8PIBPI#PI#PI#PI#PI#PI#PI#PI#PI#PI#PI#PI▙PI["]PI[(]PI[*]PI[7]PI[B]PI[E]#▞PISQR RND[~~]RND[$]RND[>]RND PLOT 1000 CLS 1001 PRINT "DOES YOUR PRINTER REQUIRE A" 1002 PRINT "SEPARATE LINEFEED CONTROL CODE" 1003 PRINT "FOR PROPER OPERATION(Y,N)?" 1004 INPUT A$ 1005 IF A$="Y" THEN GOTO 1008 1006 IF A$<>"N" THEN GOTO 1000 1007 POKE 16524,201 1008 CLS 1009 PRINT "DO YOU WANT THE PRINTER" 1010 PRINT "DRIVER TO EXECUTE:" 1015 PRINT 1020 PRINT "ABOVE RAMTOP(R) OR" 1030 PRINT "BELOW SYSTEM VARIABLES(V) OR" 1040 PRINT "AT 1 REM(T) OR" 1045 PRINT "ALMOST ANYWHERE ELSE(E)?" 1050 LET B$="Z" 1060 INPUT A$ 1070 IF A$="R" THEN GOTO 1400 1080 IF A$="V" THEN GOTO 1300 1090 IF A$="T" THEN GOTO 1350 1095 IF A$="E" THEN GOTO 1220 1100 PRINT "PLEASE ENTER R, V, T, OR E." 1110 GOTO 1060 1210 PRINT 1212 PRINT RAM;" IS AN INVALID" 1215 PRINT "RELOCATION ADDRESS." 1220 PRINT 1225 PRINT "ENTER RELOCATION ADDRESS?" 1230 INPUT RAM 1240 IF RAM<8192 THEN GOTO 1210 1250 IF RAM>32767 THEN GOTO 1210 1260 IF RAM<15784 THEN GOTO 1570 1270 IF RAM<17325 THEN GOTO 1210 1280 GOTO 1570 1300 LET RAM=8192 1310 GOTO 1570 1350 LET BASE=16521 1360 GOTO 1700 1400 LET RAM=PEEK (16388)+256*PEEK 16389 1410 IF RAM<32139 THEN GOTO 1560 1415 PRINT 1420 PRINT "Z80 MACHINE CODE CANNOT EXECUTE" 1430 PRINT "ABOVE 32767. RAMTOP MUST BE" 1440 PRINT "BELOW 32139." 1450 STOP 1560 LET RAM=RAM+1 1570 GOSUB 1800 1580 LET BASE=RAM 1590 POKE 17127,LRAM 1600 POKE 17128,HRAM 1700 CLS 1705 PRINT "THE PRINTER DRIVER WILL BE" 1710 PRINT "LOCATED AT ";BASE 1715 PRINT 1720 PRINT "THE ENTRY ADDRESSES ARE:" 1725 PRINT 1730 PRINT "LCOPY = USR(";BASE+167;")" 1740 PRINT "LLIST = USR(";BASE+325;")" 1750 PRINT "CLRBUF= USR(";BASE+560;")" 1760 PRINT "LPRINT= USR(";BASE+171;")" 1765 PRINT 1770 PRINT "IF YOUR PRINTER IS CONNECTED" 1771 PRINT "A COPY OF THIS WILL BE PRINTED." 1775 PAUSE 180 1776 PRINT 1777 PRINT "PLEASE FOLLOW THE REMAINDER" 1778 PRINT "OF THE INSTRUCTIONS TO USE" 1779 PRINT 1780 PRINT "THE BYTE-BACK PRINTER DRIVER" 1782 IF A$="T" THEN GOTO 1790 1785 LET L=USR 17161 1790 REM 1791 LET L=USR 17157 1795 IF A$="T" THEN GOTO 1799 1796 NEW 1799 STOP 1800 LET HRAM=INT (RAM/256) 1810 LET LRAM=RAM-HRAM*256 1820 RETURN 9000 FOR I=16626 TO 16652 9010 PRINT I,PEEK I 9020 NEXT I ```