--- title: "2068 Programmer’s Toolkit AROS" id: 50585 type: "computer_media" slug: "2068-progammers-toolkit-aros" url: "http://localhost/computer_media/2068-progammers-toolkit-aros/" markdown_url: "http://localhost/computer_media/2068-progammers-toolkit-aros.md" published_at: "2023-06-19T00:13:05+00:00" modified_at: "2026-04-03T07:59:23+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2019/02/20230809-041332.jpg" excerpt: "A powerful eight-function programmer's toolkit combining a Z80 disassembler, tri-base calculator, UDG editor, tape header reader, and memory loader in one cartridge-based package." 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: "Thomas B. Woods (company)" slug: "thomas-b-woods" taxonomy: "post_tag" url: "http://localhost/tag/thomas-b-woods/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" indiv: - name: "Thomas B. Woods" slug: "thomas-b-woods" taxonomy: "indiv" url: "http://localhost/indiv/thomas-b-woods/" genre: - name: "Programming" slug: "programming" taxonomy: "genre" url: "http://localhost/type/programming/" - name: "Renumber" slug: "renumber" taxonomy: "genre" url: "http://localhost/type/renumber/" - name: "Utility" slug: "utility" taxonomy: "genre" url: "http://localhost/type/utility/" media_type: "ROM" programmers: - name: "Thomas B. Woods" slug: "thomas-b-woods" taxonomy: "indiv" url: "http://localhost/indiv/thomas-b-woods/" download_url: "https://archive.org/download/timex-sinclair-software-archive/2068%20Progammers%20Toolkit%20AROS%20%281986%29%28Thomas%20Woods%29%28TS2068%29%28US%29%28ROM%29.zip" producer_company: - id: 11390 title: "Thomas B. Woods" type: "company" url: "http://localhost/company/thomas-b-woods/" images: - url: "http://localhost/wp-content/uploads/2023/06/2068-toolkit.png" related_products: - id: 39160 title: "2068 Programmer’s Cartridge (AROS) Toolkit" type: "product" url: "http://localhost/product/aros-toolkit/" related_content: - id: 39160 title: "2068 Programmer’s Cartridge (AROS) Toolkit" type: "product" url: "http://localhost/product/aros-toolkit/" media_type_tags: "Programming, Renumber, Utility" --- # 2068 Programmer’s Toolkit AROS This program is a multi-function [programmer’s toolkit](http://localhost/product/aros-toolkit/) controlled by a menu-driven BASIC shell, offering eight utilities: a block line renumberer, a hex/decimal memory loader, a Z80 disassembler, a tri-base (decimal/hex/binary) arithmetic calculator, a UDG graphic editor, a tape header reader, a print driver configurator, and a bank-switcher for returning to the home memory bank. The program relies on machine code routines stored in a cartridge EPROM, with three bootstrap routines (called via USR 55216, 55228, and 55239) that copy working code from one RAM chunk to another before each tool runs. Bank switching is managed by writing to hardware port $F4 and by poking system variables at addresses 24601–24602 to detect which bank is active. The Z80 disassembler at lines 300–460 decodes the full Z80 instruction set, including ED, CB, DD, and FD prefix groups, and formats output for both screen and printer simultaneously. The tape header reader (lines 700–720) POKEs a small machine code loader directly into RAM to read tape block headers and then decodes the type, name, length, start address, and variable area from the raw bytes. Can be burned to an EPROM and used with a cartridge board. *** ## Program Analysis ### Program Structure The program is organized as a menu dispatcher with numbered function blocks. Line 10 presents the main menu and uses a calculated `GO TO` to branch: `GO TO 100*q+(100 AND q>3)`. This maps selections 1–3 to lines 100, 200, 300, and selections 4–8 to lines 500, 600, 700, 800, 900 (the extra 100 accounts for the gap after function 3). | Menu Item | Function | Entry Line | | --- | --- | --- | | 1 | Block Line Renumberer | 100 | | 2 | Hex/Dec Loader | 200 | | 3 | Disassembler | 300 | | 4 | Tri-Base Arithmetic | 500 | | 5 | UDG Generator | 600 | | 6 | Header Reader | 700 | | 7 | Configure Print Driver | 800 | | 8 | Return to HOME Bank | 900 | ### Machine Code Integration Nearly every function depends on machine code routines that are not directly embedded in the BASIC but instead reside in a cartridge EPROM (mapped into memory above address 55000). Three bootstrap entry points are used: - `USR 55216` — copies 22 bytes of dispatch/bank-switching code from chunk 6 RAM to chunk 3 RAM at $6200. - `USR 55228` — copies 64 bytes of hex-conversion utilities to $6216, used by the Hex/Dec Loader. - `USR 55239` — copies 89 bytes of binary-conversion utilities to $6216, used by the Tri-Base calculator. After bootstrapping, most machine code calls are made via `RANDOMIZE value : RANDOMIZE USR 25092`. The RANDOMIZE seeds the system SEED variable at $5C76, which the dispatcher at $6200 reads to determine which subroutine to call. This is a compact way to pass a parameter to machine code without requiring a separate `POKE`. ### Bank Switching The program targets a multi-bank cartridge system. Bank state is detected by peeking addresses 24601 and 24602. If both are zero, subroutine 9997 is called to save home bank variables before switching. Port $F4 is manipulated by the machine code dispatcher (passing E=0 for HOME bank, E=$40 for the alternate bank). Line 900 restores the home bank explicitly, and line 9997/`RANDOMIZE USR 25092` saves home state before any bank operation. ### Z80 Disassembler (Lines 300–460) The disassembler at lines 300–460 is the most algorithmically complex section. It handles the full Z80 instruction set by decoding opcode bytes and looking up mnemonic strings from DATA statements at lines 400–460. Prefix bytes ED, CB, DD, and FD are each detected and routed to separate handling routines. - Lines 400–416 cover unprefixed opcodes (0x00–0xFF, excluding the 0x40–0x7F LD block). - Lines 420–432 cover ED-prefixed extended instructions. - Lines 440 and 450 provide operand modifier strings for arithmetic and rotate/shift groups. - Lines 460 cover relative jump instructions (DJNZ, JR variants), computing the absolute target address. - DD and FD prefixes (IX/IY) are handled by replacing “HL” in the decoded mnemonic with “IX+d” or “IY+d” at lines 340–354. - DDCB/FDCB double-prefix instructions are handled at line 353, with a check that the displacement byte position is correct. The placeholder character `#` is used in mnemonic DATA strings to mark byte operand positions, and `##` marks word operands. Subroutine at line 371 scans the assembled mnemonic for these markers, and lines 319–322 replace them with the actual fetched hex bytes. RST 28h (the Spectrum floating-point calculator) and RST 08h (error handler) are given special treatment: RST 28h triggers a loop that disassembles following calculator literals as DEFB entries (lines 330–332), while RST 08h decodes the following error code byte (line 328). ### Hex/Dec Loader (Lines 200–292) This utility allows interactive memory editing in either hexadecimal or decimal mode. The operator specifies an address (decimal with “D” prefix, hex with “H” prefix, or “R” for the current BASIC program start) and enters bytes one at a time. Mode can be toggled mid-session by entering “H” in decimal mode or “D” in hex mode. The hex-to-nibble conversion at lines 223 and 239 uses the `VAL` function on individual characters; this works for digits 0–9 but not for A–F, so those hex nibbles must have been pre-assigned to BASIC variables A–F (set to 10–15) at line 201. ### Tri-Base Arithmetic Calculator (Lines 500–596) This calculator accepts integer input in decimal (D prefix), hex (H prefix), or binary (B prefix) and displays results simultaneously in all three bases. It supports the operators +, -, *, /, AND, OR, XOR, as well as shift left/right (SL/SR), rotate left/right (RL*n*/RR*n*), one’s complement (C), and two’s complement negation (+/-). Storage/recall (STO/RCL) provides a single memory register. Binary display is handled by the machine code routine at $6247 (installed by bootstrap 3), which converts a 16-bit value to a 16-character binary string stored in `m$()`. Overflow is detected by checking whether the result falls outside the range −65536 to 65535 (line 581). ### Tape Header Reader (Lines 700–720) Rather than relying on a pre-loaded routine, this utility POKEs a 44-byte machine code header-reading routine directly into RAM at addresses 25110–25153 using DATA statements at lines 702–706. After reading, the 17-byte header block is decoded from addresses 25154–25170: byte 25154 holds the file type (0=BASIC, 1=numeric array, 2=character array, 3=code); bytes 25155–25164 hold the filename; and bytes 25165–25170 hold length, start/autostart, and parameter fields. Line 709 uses a row of UDG-A characters (defined as a blank underline at line 707) as a visual overlay to display the raw name field with graphics characters replaced. Array variable names for types 1 and 2 are decoded by arithmetic on the stored byte at address 25168, using the known Spectrum array name encoding offset. ### Print Driver Configurator (Lines 800–850) This section configures a hardware print driver by writing to system variables at addresses 23321/23322 (printer driver address) and 23326/23327 (print flags and mode). Seven printer interfaces are supported (Aerco, two Tasman variants, T/S 2040, A&J, Byte-Back Parallel, and Other). For a user-defined driver (option 7), the user supplies the driver’s starting address directly. Automatic line feed and initial print mode (normal, up-arrow, raw ASCII) are stored as packed flags in `POKE 23326` and `POKE 23327`. ### Notable BASIC Idioms - Boolean arithmetic for hex nibble formatting: `CHR$ (i+48+7*(i>9))` converts a nibble 0–15 to ‘0’–’9′ or ‘A’–’F’ without a conditional branch. - String expressions as conditional assignments: `("Hex. " AND b$(1)="H")+("Dec. " AND b$(1)="D")` selects a string based on a flag without IF/THEN branching. - The `RANDOMIZE n : RANDOMIZE USR 25092` idiom to pass a parameter to machine code via SEED ($5C76/$5C77) is used throughout instead of direct POKEing. - `LPRINT` and `PRINT` are called in tandem throughout, producing identical output on both screen and printer simultaneously. - Low/high byte splitting uses the pattern `q-256*INT(q/256)` and `INT(q/256)` rather than BASIC’s lack of bitwise AND for 16-bit values. ### Anomalies and Notes - Line 1000 appears to be a diagnostic routine for dumping header byte contents; it is reachable only by direct `GO TO 1000` and is not listed in the main menu. - Lines 9995–9996 are bare `STOP` statements that serve as safety guards; line 9997 is the home-bank variable save subroutine. - Lines 9998–9999 contain bank-switching and CLEAR setup code that is not called from within the program — these are likely initialization stubs invoked from outside the normal BASIC flow, possibly from the cartridge ROM directly. - In the disassembler, line 307 is referenced by GO TO but appears after lines 308–310 in source order; execution reaches it from line 306 via a fall-through that skips line 307 on the first pass, which is intentional — the label is a shared entry point for hex-parsed addresses. - The VAL-based hex digit conversion in the Hex/Dec Loader (using pre-assigned variables A=10 through F=15) would fail silently if those variables were ever overwritten, since they share the single-letter namespace with the disassembler’s register name variable `a`. ## Source Code ``` 1 CLS :POKE (PEEK 23740+256*PEEK 23741+1),2:RANDOMIZE USR 55216 2 IF (PEEK 24601=175 AND PEEK 24602=211) OR (PEEK 24601+PEEK 24602=0) THEN GO SUB 9997 10 CLS :POKE (PEEK 23740+256*PEEK 23741+1),2:PRINT TAB 6;INVERSE 1;" * TOOLKIT EPROM * "'''INVERSE 0;"Select Function-"''TAB 4;"1. Block Line Renumberer."''TAB 4;"2. Hex/Dec Loader."''TAB 4;"3. Disassembler."''TAB 4;"4. Tri-Base Arithmetic."''TAB 4;"5. UDG Generator."''TAB 4;"6. Header Reader."''TAB 4;"7. Configure Print Driver."''TAB 4;"8. Return to HOME Bank.":INPUT "Function Nr: ";q:GO TO 100*q+(100 AND q>3) 100 REM Block Line Renumberer. 101 IF PEEK 24601+PEEK 24602=0 THEN GO SUB 9997 110 CLS :PRINT TAB 2;INVERSE 1;" * Block Line Renumberer * "'':RANDOMIZE 56891:LET q=USR 25092 120 PRINT "Old starting line number: ";:INPUT q:IF q=0 THEN GO TO 10 130 POKE 25110,q-256*INT (q/256):POKE 25111,INT (q/256):LPRINT ''"Old starting line number: ";q:PRINT q 135 LET q=USR 25120 140 POKE 25112,q-256*INT (q/256):POKE 25113,INT (q/256):LPRINT "Address: ";q,"Length: ";PEEK (q+2)+256*PEEK (q+3)+4 142 LPRINT "Relative address: ";q-PEEK 23635-256*PEEK 23636'' 144 PRINT "Address: ";q,"Length: ";PEEK (q+2)+256*PEEK (q+3)+4 146 PRINT "Relative address: ";q-PEEK 23635-256*PEEK 23636'' 148 PRINT "New starting line number: ";:INPUT q:IF q=0 THEN GO TO 10 150 POKE 25114,q-256*INT (q/256):POKE 25115,INT (q/256):LPRINT "New starting line number: ";q'':PRINT q''"Step: ";:INPUT q:IF q=0 THEN GO TO 10 160 POKE 25118,q-256*INT (q/256):POKE 25119,INT (q/256):LPRINT "Step: ";q'':PRINT q''"Old stopping line number: ";:INPUT q:IF q=0 THEN GO TO 10 170 POKE 25116,q-256*INT (q/256):POKE 25117,INT (q/256):LPRINT "Old stopping line number: ";q:PRINT q:INPUT "Inputs OK (y/n)? ";q$:IF q$<>"Y" AND q$<>"y" THEN CLS :GO TO 120 180 LET q=USR 25151 185 LPRINT '''" Renumbering complete."''''':PRINT AT 15,5;"Renumbering complete.":IF PEEK (PEEK 23631+256*PEEK 23632+16)=5 THEN INPUT "Copy (y/n)? ";q$:IF q$="Y" OR q$="y" THEN COPY 190 GO TO 900 200 IF PEEK 24601+PEEK 24602=0 THEN GO SUB 9997:REM Hex/Dec Loader. 201 CLS ::PRINT TAB 5;INVERSE 1;" * Hex/Dec Loader * "'':POKE 23658,8:DIM h$(4):LET h$="0000":LET A=10:LET B=11:LET C=12:LET D=13:LET E=14:LET F=15:POKE (PEEK 23740+256*PEEK 23741+1),2:RANDOMIZE USR 55228 210 INPUT "Hex or Decimal Codes (H/D)? ";b$:IF b$="A" OR b$="Q" OR b$="STOP" OR b$="STOP " THEN GO TO 290 211 IF b$="" OR (b$(1)<>"H" AND b$(1)<>"D") THEN GO TO 210 212 LET b$=("Hex. " AND b$(1)="H")+("Dec. " AND b$(1)="D") 220 INPUT "Address? ";i$:IF i$="A" OR i$="Q" OR i$="STOP" OR i$="STOP " THEN GO TO 290 221 IF i$="" OR (i$(1)<>"H" AND i$(1)<>"D" AND i$(1)<>"R") THEN GO TO 220 222 IF i$(1)="R" THEN LET i=PEEK 23635+256*PEEK 23636:GO TO 226 223 IF i$(1)="H" THEN LET h$(6-LEN i$ TO )=i$(2 TO ):LET i=4096*VAL h$(1)+256*VAL h$(2)+16*VAL h$(3)+VAL h$(4):LET i$=i$(2 TO ):GO TO 228 224 LET i=VAL i$(2 TO ) 225 IF i>65535 THEN PRINT '"Address exceeds maximum."''':LPRINT ''"Address exceeds maximum."''':GO TO 220 226 RANDOMIZE i:IF i=0 THEN POKE 23670,0:POKE 23671,0 227 LET h$=h$:RANDOMIZE USR 25110 228 LET x=PEEK i:LET h=x:LET x$=CHR$ (INT (h/16)+48+7*(INT (h/16)>9)):LET h=h-16*INT (h/16):LET x$=x$+CHR$ (h+48+7*(h>9)) 229 FOR j=0 TO 5-LEN STR$ i:LPRINT " ";:NEXT j:LPRINT i;" ";h$;" ";(" " AND LEN STR$ x<3);(" " AND LEN STR$ x=1);x;" ";x$;" ";:PRINT TAB 5-LEN STR$ i;i;TAB 6;h$;TAB 15-LEN STR$ x;x;TAB 16;x$;" "; 230 INPUT (b$);"Code? ";x$:IF x$="Q" OR x$="STOP" OR x$="STOP " THEN GO TO 290 231 IF x$="A" THEN PRINT '':LPRINT '':GO TO 220 232 IF x$="" THEN LPRINT :GO TO 260 233 IF b$(1)="H" THEN GO TO 237 234 IF x$="H" THEN LET b$="Hex. ":GO TO 230 235 LET x=VAL x$:IF x>255 THEN GO TO 230 236 LET h=x:LET x$=CHR$ (INT (h/16)+48+7*(INT (h/16)>9)):LET h=h-16*INT (h/16):LET x$=x$+CHR$ (h+48+7*(h>9)):GO TO 240 237 IF x$="D" THEN LET b$="Dec. ":GO TO 230 238 IF LEN x$<>2 THEN GO TO 230 239 LET x=16*VAL x$(1)+VAL x$(2) 240 POKE i,x 250 LPRINT (" " AND LEN STR$ x<3);(" " AND LEN STR$ x=1);x;" ";x$':PRINT (" " AND LEN STR$ x<3);(" " AND LEN STR$ x=1);x;" ";x$' 260 LET i=i+1:GO TO 225 290 LPRINT ''':IF PEEK (PEEK 23631+256*PEEK 23632+16)=5 THEN INPUT "Copy (Y/N)? ";i$:IF i$="Y" THEN COPY 291 INPUT "More (Y/N)? ";i$:IF i$="Y" THEN PRINT ''':GO TO 210 292 LET h$=h$:RANDOMIZE USR 25158:GO TO 10 300 IF PEEK 24601+PEEK 24602=0 THEN GO SUB 9997:REM BASIC Disassembler. 301 CLS :PRINT TAB 3;INVERSE 1;" * BASIC Disassembler * ":POKE 23658,8:DIM d$(8,4):POKE (PEEK 23740+256*PEEK 23741+1),2:RESTORE 301:FOR j=1 TO 8:READ d$(j):NEXT j:DATA "B","C","D","E","H","L","(HL)","A" 302 INPUT "Address? ";k$:IF k$="" THEN GO TO 302 303 IF k$="Q" OR k$="STOP" OR k$="STOP " THEN GO TO 10 304 IF k$(1)<>"D" THEN GO TO 308 305 IF LEN k$>6 OR k$="D" THEN GO TO 302 306 GO TO 385 307 LET a=VAL (k$(2 TO )):GO TO 311 308 IF k$(1)<>"H" OR LEN k$<>5 THEN GO TO 302 309 GO TO 389 310 GO SUB 384 311 LET c=0:CLS 312 LET b$="":LET c$="":GO SUB 363:GO SUB 366:IF c$(LEN c$-1 TO )="ED" THEN GO TO 333 313 IF c$(LEN c$-1 TO )="CB" THEN GO TO 335 314 IF c$(LEN c$-1 TO )="DD" THEN GO TO 340 315 IF c$(LEN c$-1 TO )="FD" THEN GO TO 341 316 IF c$<"39" THEN IF c$(2)="0" OR c$(2)="8" THEN IF c$>"0F" THEN GO TO 394 317 GO SUB 374 318 LET n$="":GO SUB 371:IF LEN n$=0 THEN GO TO 323 319 IF LEN n$=1 THEN GO TO 322 320 GO SUB 366:GO SUB 366:LET b$=c$(LEN c$-1 TO )+c$(LEN c$-3 TO LEN c$-2) 321 LET n=CODE n$(1)-48-(7 AND n$(1)>"@"):LET i$=i$( TO n-1)+b$+(i$(n+1+(LEN n$=2) TO ) AND i$(LEN i$)<>"#"):GO TO 325 322 GO SUB 366:LET b$=c$(LEN c$-1 TO ):GO TO 321 323 IF i$="" THEN LET i$="Not def.":GO TO 325 324 IF i$(1)="*" THEN LET i$="Not def." 325 PRINT TAB 0;a$;TAB 6;c$;TAB 15;i$:LPRINT a$;" ";c$;:FOR j=1 TO 9-LEN c$:LPRINT " ";:NEXT j:LPRINT i$ 326 LET c=c+1:IF c=22 THEN GO TO 355 327 IF i$="RST 28h" THEN GO TO 330 328 IF i$="RST 08h" THEN LET c$="":GO SUB 363:GO SUB 366:LET i=16*VAL c$(1)+CODE c$(2)-47-(7 AND c$(2)>"@"):LET i$=" ERR "+CHR$ (i+48+(7 AND i>9)):GO TO 325 329 GO TO 312 330 LET c$="":GO SUB 363:GO SUB 366:LET i$="DEFB "+c$:PRINT TAB 0;a$;TAB 6;c$;TAB 15;i$:LPRINT a$;" ";c$;:FOR j=1 TO 9-LEN c$:LPRINT " ";:NEXT j:LPRINT i$:IF c$="38" THEN GO TO 326 331 LET c=c+1:IF c=22 THEN GO TO 355 332 GO TO 330 333 GO SUB 366:LET b=b-64:IF b<0 OR (b>59 AND b<96) OR b>123 THEN LET i$="":GO TO 323 334 RESTORE 420+INT (b/10):GO SUB 376:GO TO 318 335 GO SUB 366:GO SUB 336:GO TO 318 336 IF b<64 THEN RESTORE 450:LET v=INT (b/8)-8*INT (b/64):FOR j=0 TO v:READ i$:NEXT j:GO TO 338 337 LET i$=("BIT " AND b<128)+("RES " AND b>127 AND b<192)+("SET " AND b>191)+STR$ INT ((b-64*INT (b/64))/8)+"," 338 IF i$="" THEN LET i$="****":RETURN 339 LET v=b-8*INT (b/8):GO SUB 383:RETURN 340 LET x$="IX":GO TO 342 341 LET x$="IY" 342 GO SUB 366 343 IF c$(LEN c$-1 TO )="CB" THEN GO TO 353 344 LET y=b:GO SUB 374:LET k=1 345 IF k>LEN i$-1 OR y=235 THEN LET i$="":GO TO 323 346 IF i$(k TO k+1)="HL" THEN GO TO 348 347 LET k=k+1:GO TO 345 348 IF y<=43 OR y=57 OR y>=225 THEN GO TO 351 349 GO SUB 366:LET u$="":IF k<>LEN i$-1 THEN LET u$=i$(k+2 TO ) 350 LET i$=i$(1 TO k-1)+x$+"+"+c$(LEN c$-1 TO )+u$:GO TO 318 351 LET u$="":IF k<>LEN i$-1 THEN LET u$=i$(k+2 TO ):IF y=41 THEN LET u$(2 TO )=x$ 352 LET i$=i$(1 TO k-1)+x$+u$:GO TO 318 353 GO SUB 366:GO SUB 366:LET b=PEEK (a-1):IF (b+2)/8<>INT ((b+2)/8) THEN LET i$="":GO TO 323 354 GO SUB 336:LET i$=i$( TO LEN i$-3)+x$+"+"+c$(5 TO 6)+i$(LEN i$):GO TO 324 355 INPUT "More? (Y/N/C/Q) ";q$:IF q$="Q" THEN GO TO 10 356 IF q$="N" THEN GO TO 302 357 IF q$="Y" THEN GO TO 360 358 IF PEEK (PEEK 23631+256*PEEK 23632+16)<>5 OR q$<>"C" THEN GO TO 355 359 COPY :GO TO 355 360 IF i$="RST 28h" THEN GO TO 362 361 IF i$<>"DEFB "+c$ OR c$="38" THEN GO TO 311 362 CLS :LET c=0:GO TO 330 363 LET a$="":LET r=a:LET k=3 364 LET i=INT (r/(16^k)):LET a$=a$+CHR$ (i+48+(7 AND i>9)):LET r=r-(16^k)*i:IF k=0 THEN RETURN 365 LET k=k-1:GO TO 364 366 IF a>65535 THEN GO TO 368 367 LET b=PEEK a:LET i=INT (b/16):LET c$=c$+CHR$ (i+48+(7 AND i>9))+CHR$ (b-16*i+48+(7 AND (b-16*i)>9)):LET a=a+1:RETURN 368 PRINT AT 21,6;"Address exceeds maximum.":LPRINT ''"Address exceeds maximum."'':BEEP 3,10:PAUSE 300:PRINT AT 21,6;" ":IF c=0 THEN GO TO 302 369 IF PEEK (PEEK 23631+256*PEEK 23632+16)=5 THEN INPUT "Copy (Y/N)? ";q$:IF q$="Y" THEN COPY 370 GO TO 302 371 LET n$="":FOR j=1 TO LEN i$:IF i$(j)<>"#" THEN GO TO 373 372 LET n$=n$+CHR$ (j+48+(7 AND j>9)) 373 NEXT j:RETURN 374 IF b>=64 THEN GO TO 377 375 RESTORE 400+INT (b/10) 376 FOR j=0 TO b-10*INT (b/10):READ i$:NEXT j:RETURN 377 IF b<=191 THEN GO TO 379 378 RESTORE 410+INT ((b-192)/10):FOR j=0 TO b-10*INT ((b-192)/10)-192:READ i$:NEXT j:RETURN 379 LET b=b-64:IF b=54 THEN LET i$="HALT":RETURN 380 IF b<64 THEN LET v=INT (b/8):LET i$="LD ":GO SUB 383:LET i$=i$+",":GO TO 382 381 LET b=b-64:RESTORE 440:LET v=INT (b/8):FOR j=0 TO v:READ i$:NEXT j 382 LET v=b-8*v:GO SUB 383:RETURN 383 LET e$=d$(1+v):LET i$=i$+e$(1 TO 1+(3 AND 1+v=7)):RETURN 384 LET a=0:LET a$=k$(2 TO ):FOR j=1 TO 4:LET a=a+(16^(4-j))*(CODE a$(j)-48-(7 AND a$(j)>"@")):NEXT j:RETURN 385 LET k=2 386 IF k$(k)<"0" OR k$(k)>"9" THEN GO TO 302 387 IF k=LEN k$ THEN GO TO 307 388 LET k=k+1:GO TO 386 389 LET k=2 390 IF (k$(k)>="0" AND k$(k)<="9") OR (k$(k)>="A" AND k$(k)<="F") THEN GO TO 392 391 GO TO 302 392 IF k=LEN k$ THEN GO TO 310 393 LET k=k+1:GO TO 390 394 RESTORE 460:FOR j=0 TO INT (b/8)-2:READ i$:NEXT j:GO SUB 366:LET q=a:LET q$=a$:LET a=a+b-(256 AND b>127):GO SUB 363:LET i$=i$+a$:LET a=q:LET a$=q$:GO TO 325 400 DATA "NOP","LD BC,##","LD (BC),A","INC BC","INC B","DEC B","LD B,#","RLCA","EX AF,AF'","ADD HL,BC" 401 DATA "LD A,(BC)","DEC BC","INC C","DEC C","LD C,#","RRCA","","LD DE,##","LD (DE),A","INC DE" 402 DATA "INC D","DEC D","LD D,#","RLA","","ADD HL,DE","LD A,(DE)","DEC DE","INC E","DEC E" 403 DATA "LD E,#","RRA","","LD HL,##","LD (##),HL","INC HL","INC H","DEC H","LD H,#","DAA" 404 DATA "","ADD HL,HL","LD HL,(##)","DEC HL","INC L","DEC L","LD L,#","CPL","","LD SP,##" 405 DATA "LD (##),A","INC SP","INC (HL)","DEC (HL)","LD (HL),#","SCF","","ADD HL,SP","LD A,(##)","DEC SP" 406 DATA "INC A","DEC A","LD A,#","CCF" 410 DATA "RET NZ","POP BC","JP NZ,##","JP ##","CALL NZ,##","PUSH BC","ADD A,#","RST 00h","RET Z","RET" 411 DATA "JP Z,##","","CALL Z,##","CALL ##","ADC A,#","RST 08h","RET NC","POP DE","JP NC,##","OUT (#),A" 412 DATA "CALL NC,##","PUSH DE","SUB #","RST 10h","RET C","EXX","JP C,##","IN A,(#)","CALL C,##","" 413 DATA "SBC A,#","RST 18h","RET PO","POP HL","JP PO,##","EX (SP),HL","CALL PO,##","PUSH HL","AND #","RST 20h" 414 DATA "RET PE","JP (HL)","JP PE,##","EX DE,HL","CALL PE,##","","XOR #","RST 28h","RET P","POP AF" 415 DATA "JP P,##","DI","CALL P,##","PUSH AF","OR #","RST 30h","RET M","LD SP,HL","JP M,##","EI" 416 DATA "CALL M,##","","CP #","RST 38h" 420 DATA "IN B,(C)","OUT (C),B","SBC HL,BC","LD (##),BC","NEG","RETN","IM 0","LD I,A","IN C,(C)","OUT (C),C" 421 DATA "ADC HL,BC","LD BC,(##)","","RETI","","LD R,A","IN D,(C)","OUT (C),D","SBC HL,DE","LD (##),DE" 422 DATA "","","IM 1","LD A,I","IN E,(C)","OUT (C),E","ADC HL,DE","LD DE,(##)","","" 423 DATA "IM 2","LD A,R","IN H,(C)","OUT (C),H","SBC HL,HL","","","","","RRD" 424 DATA "IN L,(C)","OUT (C),L","ADC HL,HL","","","","","RLD","","" 425 DATA "SBC HL,SP","LD (##),SP","","","","","IN A,(C)","OUT (C),A","ADC HL,SP","LD SP,(##)" 429 DATA "","","","","","","LDI","CPI","INI","OUTI" 430 DATA "","","","","LDD","CPD","IND","OUTD" 431 DATA "","","LDIR","CPIR","INIR","OTIR","","","","" 432 DATA "LDDR","CPDR","INDR","OTDR" 440 DATA "ADD A,","ADC A,","SUB ","SBC A,","AND ","XOR ","OR ","CP " 450 DATA "RLC ","RRC ","RL ","RR ","SLA ","SRA ","","SRL " 460 DATA "DJNZ ","JR ","JR NZ,","JR Z,","JR NC,","JR C," 500 IF PEEK 24601+PEEK 24602=0 THEN GO SUB 9997:REM Tri-Base Arithmetic. 501 CLS :PRINT TAB 3;INVERSE 1;" * Tri-Base Arithmetic * ":POKE 23658,8:DIM h$(5):DIM m$(17):LET A=10:LET B=11:LET C=12:LET D=13:LET E=14:LET F=15:POKE (PEEK 23740+256*PEEK 23741+1),2:RANDOMIZE USR 55239 520 LET f$="" 521 LET l=1 522 INPUT "Input: ";i$ 523 IF (i$(1)>"/" AND i$(1)<":") OR (i$(1)="-" AND i$<>"-") OR i$(1)="D" THEN GO TO 541 524 IF i$(1)="H" THEN LET n=0:FOR i=2 TO LEN i$:LET n=16*n+VAL i$(i):NEXT i:GO TO 581 525 IF i$(1)="B" THEN LET n=0:FOR i=2 TO LEN i$:LET n=2*n+VAL i$(i):NEXT i:GO TO 581 526 IF i$="V" OR i$="CLS" THEN CLS 527 IF PEEK (PEEK 23631+256*PEEK 23632+16)=5 THEN IF i$="Z" OR i$="COPY" THEN COPY 528 LET l=0:IF i$="A" OR i$="Q" OR i$="STOP" OR i$="STOP " THEN GO TO 595 529 IF i$="+" OR i$="-" OR i$="*" OR i$="/" OR i$="AND" OR i$="OR" OR i$="XOR" THEN GO TO 544 530 LET l=2 531 IF i$="+/-" THEN GO TO 556 532 IF i$="STO" THEN GO TO 570 533 IF i$="RCL" THEN GO TO 571 534 LET l=3 535 IF i$="=" THEN GO TO 545 536 IF i$="C" THEN GO TO 557 537 IF i$="SL" OR i$="SR" THEN GO TO 558 538 IF LEN i$<3 THEN GO TO 522 539 IF i$(1 TO 2)="RL" OR i$(1 TO 2)="RR" THEN GO TO 560 540 GO TO 521 541 LET n=VAL i$(1+(1 AND i$(1)="D") TO ):GO TO 581 544 LPRINT i$:PRINT i$:GO SUB 546:LET f$=i$:LET l$=m$:GO TO 522 545 GO SUB 546:LET n=x:GO TO 580 546 IF f$="" THEN LET x=n 547 IF f$="+" THEN LET x=x+n 548 IF f$="-" THEN LET x=x-n 549 IF f$="*" THEN LET x=x*n 550 IF f$="/" THEN LET x=INT (x/n) 551 IF f$<>"AND" AND f$<>"OR" AND f$<>"XOR" THEN RETURN 552 LET n=0:FOR i=2 TO 17:IF f$="AND" THEN LET n=2*n+(VAL m$(i) AND VAL l$(i)) 553 IF f$="OR" THEN LET n=2*n+(VAL m$(i) OR VAL l$(i)) 554 IF f$="XOR" THEN LET n=2*n+((VAL m$(i) OR VAL l$(i)) AND NOT (VAL m$(i) AND VAL l$(i))) 555 NEXT i:LET x=n:RETURN 556 LET n=-n:GO TO 580 557 LET i$="1's Compl":LET n=-n-1:GO TO 580 558 LET n=(2*n AND i$(2)="L")+(INT (n/2) AND i$(2)="R"):GO TO 580 560 LET m=VAL i$(3 TO ):IF m>16 OR m<1 THEN GO TO 522 561 LET z=1:FOR i=2 TO 17-m:LET m$(i)="0":NEXT i:LET n=0:FOR i=2 TO 17:LET n=2*n+VAL m$(i):NEXT i:IF i$(2)="R" THEN GO TO 565 562 IF n<2^(m-1) THEN LET z=0 563 LET n=2*n+z:IF n>2^m THEN LET n=n-2^m 564 GO TO 580 565 IF n/2=INT (n/2) THEN LET z=0 566 LET n=INT (n/2)+z*2^(m-1):GO TO 580 570 LPRINT "STO":PRINT "STO":LET s=n:GO TO 521 571 LET n=s 580 LPRINT i$:PRINT i$ 581 IF n<65536 AND n>-65537 THEN GO TO 583 582 LPRINT "OVERFLOW!":LPRINT :PRINT "OVERFLOW!":PRINT :GO TO 520 583 IF n<0 THEN GO TO 585 584 LET z=n:LET h$(1)=" ":GO SUB 586:LET m$(1)=" ":GO TO 588 585 LET z=65536+n:LET h$(1)="F":GO SUB 586:LET m$(1)="C":GO TO 588 586 RANDOMIZE z:IF z=0 THEN POKE 23670,0:POKE 23671,0 587 LET h$=h$:RANDOMIZE USR 25110:RETURN 588 RANDOMIZE z:IF z=0 THEN POKE 23670,0:POKE 23671,0 589 LET m$=m$:RANDOMIZE USR 25159 590 IF l=1 THEN LPRINT :PRINT 591 FOR i=1 TO 6-LEN STR$ n:LPRINT " ";:NEXT i:LPRINT n;" ";h$;" ";m$:PRINT TAB 6-LEN STR$ n;n;TAB 8;h$;TAB 15;m$:IF l=0 THEN GO TO 522 592 IF l=2 THEN GO TO 521 593 GO TO 520 595 LET h$=h$:RANDOMIZE USR 25185:LET m$=m$:RANDOMIZE USR 25185:IF PEEK (PEEK 23631+256*PEEK 23632+16)=5 THEN INPUT "Copy (Y/N)? ";a$:IF a$="Y" OR a$="y" THEN COPY 596 GO TO 10 600 IF PEEK 24601+PEEK 24602=0 THEN GO SUB 9997:REM UDG Generator. 601 CLS :PRINT TAB 11;INVERSE 1;" * UDG Generator * ";INVERSE 0;AT 14,0;" Key No Shift Cap Shft Sym Shft";AT 14,0;OVER 1;"_____ ________ ________ ________";OVER 0''" A-U Define Replace Replace Arrow Move Cur Mv & Ink Mv & Pap 1 ------ All Ink All Pap 2,3,9 ------ Ink Paper 4 ------ Toggle Toggle 0 ------ Exit Exit ":RANDOMIZE 57012:RANDOMIZE USR 25092 602 INPUT "Restore original graphics (y/n)? ";a$:IF a$="Y" OR a$="y" THEN RANDOMIZE 57299:RANDOMIZE USR 25092:GO TO 602 603 IF PEEK (PEEK 23631+256*PEEK 23632+16)=5 THEN INPUT "Copy (y/n)? ";a$:IF a$="Y" OR a$="y" THEN COPY 604 INPUT "Save UDG's (y/n)? ";a$:IF a$<>"Y" AND a$<>"y" THEN GO TO 10 605 INPUT "Name for SAVE? ";a$:IF a$="" OR LEN a$>10 THEN GO TO 605 606 SAVE a$CODE PEEK 23675+256*PEEK 23676,65536-PEEK 23675-256*PEEK 23676:CLS :PRINT AT 10,7;FLASH 1;" REWIND to VERIFY ":BEEP .5,12:VERIFY ""CODE :BEEP .5,12:PRINT AT 10,7;" ";AT 12,8+LEN a$;"is OK." 607 INPUT "More (y/n)? ";a$:IF a$="Y" OR a$="y" THEN GO TO 601 608 GO TO 10 700 IF PEEK 24601+PEEK 24602=0 THEN GO SUB 9997:REM Header reader. 701 CLS :PRINT TAB 6;INVERSE 1;" * Header Reader * ":POKE (PEEK 23740+256*PEEK 23741+1),2:RESTORE 702:FOR a=25110 TO 25153:READ c:POKE a,c:NEXT a 702 DATA 175,55,221,33,66,98,17,17,0,243 703 DATA 245,219,255,203,255,211,255,219,244,50 704 DATA 65,98,62,1,211,244,241,205,252,0 705 DATA 58,65,98,211,244,219,255,203,191,211 706 DATA 255,251,201,0 707 FOR i=0 TO 6:POKE USR "a"+i,0:NEXT i:POKE USR "a"+7,126 708 POKE 25154,4:CLS :PRINT AT 10,8;"Start the tape-":RANDOMIZE USR 25110:CLS :LET type=PEEK 25154:IF type>3 THEN PRINT AT 10,9;FLASH 1;" Not a Header ":GO TO 719 709 LET a$="":FOR i=0 TO 9:LET a$=a$+CHR$ PEEK (25155+i):NEXT i:LET len=PEEK 25165+256*PEEK 25166:LET start=PEEK 25167+256*PEEK 25168:LET pa=PEEK 25169+256*PEEK 25170:LET var=len-pa:LPRINT "Name: ";a$'':PRINT "Name: [UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A]";OVER 1;AT 0,6;a$'':IF type THEN GO TO 712 710 LPRINT "Program type: BASIC."''"Program area: ";pa;" bytes."''"Variables area: ";var;" bytes."'':PRINT "Program type: BASIC."''"Program area: ";pa;" bytes."''"Variables area: ";var;" bytes."'':IF start>9999 THEN LPRINT "No auto-start."'''':PRINT "No auto-start.":GO TO 718 711 LPRINT "Auto-start at Line ";start;"."'''':PRINT "Auto-start at Line ";start;".":GO TO 718 712 LET t$="Code Block.":IF start=16384 AND len=6912 THEN LET t$="Screen." 713 IF type=1 THEN LET t$="Numeric Array.":LET n$=CHR$ (PEEK 25168-64)+" or "+CHR$ (PEEK 25168-32) 714 IF type=2 THEN LET t$="Character Array.":LET n$=CHR$ (PEEK 25168-128)+"$ or "+CHR$ (PEEK 25168-96)+"$" 715 LPRINT "Type: ";t$'':PRINT "Type: ";t$'':IF type=1 OR type=2 THEN LPRINT "Array Variable Name: ";n$'':PRINT "Array Variable Name: ";n$'' 716 IF type=3 THEN LPRINT "Starting Address: ";start'':PRINT "Starting Address: ";start'' 717 LPRINT "Length: ";len;" bytes."'''':PRINT "Length: ";len;" bytes." 718 IF PEEK (PEEK 23631+256*PEEK 23632+16)=5 THEN INPUT "Copy (y/n)? ";r$:IF r$="Y" OR r$="y" THEN COPY 719 INPUT "Read another header (y/n)? ";r$:IF r$="Y" OR r$="y" THEN GO TO 708 720 GO TO 10 800 IF PEEK 24601+PEEK 24602=0 THEN GO SUB 9997:REM Configure Print Driver. 801 CLS :PRINT TAB 1;INVERSE 1;" * Configure Print Driver * "''':PRINT TAB 4;"1. Aerco."''TAB 4;"2. Tasman (B)."''TAB 4;"3. Tasman (C)."''TAB 4;"4. T/S 2040."''TAB 4;"5. A & J."''TAB 4;"6. Byte-Back Parallel."''TAB 4;"7. Other." 810 INPUT "Select a Number: ";q:IF q<1 OR q>7 THEN GO TO 810 820 LET q=q-1:IF q=3 THEN LET q=PEEK 23631+256*PEEK 23632+15:POKE q,0:POKE (q+1),5:GO TO 10 825 RANDOMIZE 56352:RANDOMIZE USR 25092 826 IF q=6 THEN INPUT "Enter the starting address of your driver software:";q:RANDOMIZE q:POKE 23321,PEEK 23670:POKE 23322,PEEK 23671:LET q=6 830 PRINT AT 20,0;"Do you want an automatic LINE FEED with CARRIAGE RETURN? (y/n)":INPUT q$:IF q$="Y" OR q$="y" THEN LET q=q+128 840 LET q$=" ":POKE 23326,q:PRINT AT 20,0;q$'q$;AT 18,0;"Now input initial print mode:"'q$;TAB 4;"1. NORMAL."'TAB 4;"2. UP-ARROW.":INPUT AT 0,4;"3. RAW ASCII.",q:IF q<1 OR q>3 THEN GO TO 840 850 LET q=q-1:POKE 23327,q:GO TO 10 900 REM Return to HOME Bank. 905 RANDOMIZE 56224:RANDOMIZE USR 25092:REM restore home vars 910 POKE 24601,0:POKE 24602,0:POKE (PEEK 23740+256*PEEK 23741+1),0:POKE 23750,0:STOP 1000 CLS :PRINT "Byte Contents";OVER 1;AT 0,0;"____ ________"'':FOR i=1 TO 17:PRINT TAB 3-LEN STR$ i;i;TAB 12-LEN STR$ PEEK (25131+i);PEEK (25153+i):NEXT i:GO TO 718 9995 STOP 9996 STOP 9997 RANDOMIZE 56250:RANDOMIZE USR 25092:RETURN :REM save home vars 9998 CLEAR :POKE 23750,0:OUT 244,240:POKE 23635,10:POKE 23636,128:POKE 23627,PEEK 32776:POKE 23628,PEEK 32777:RANDOMIZE (1+PEEK 32776+256*PEEK 32777):POKE 23641,PEEK 23670:POKE 23642,PEEK 23671:LIST :STOP 9999 CLEAR :POKE 32776,PEEK 23627:POKE 32777,PEEK 23628:POKE PEEK 23627+256*PEEK 23628,128:POKE 1+PEEK 23627+256*PEEK 23628,13:POKE 2+PEEK 23627+256*PEEK 23628,128 ; eToolkit EPROM - Machine Code Routines ; by Thomas B. Woods ; Extracted from eToolkit.ROM, offset $5700-$57CF ; Located in chunk 2 (display file area, $4000-$5FFF) ; ; These routines live in the display file area of the cartridge ROM. ; They are NOT directly executable from here — they are accessed ; when the BASIC program calls USR at various addresses. ; ; Three bootstrap routines at $57B0/$57BC/$57C7 (called as USR 55216, ; USR 55228, USR 55239 from BASIC) copy MC from $D700+ (RAM chunk 6) ; into $6200+ (RAM chunk 3) for the various toolkit functions. ; ; The routines at $5700-$57AC handle bank switching and string ; conversion, and are the templates that get copied to $6200+. org $5700 ;================================================================ ; Dispatcher ($6204 when copied to RAM) ; Called via: RANDOMIZE value: RANDOMIZE USR 25092 ; Reads SEED ($5C76), saves/restores HSR (port $F4), ; switches to HOME bank (E=0) or alternate (E=$40), ; then calls address in HL from dispatcher table. ;================================================================ DISPATCH_HOME: ld e,$00 ;5700 E=0: switch to HOME bank jr DISPATCH ;5702 DISPATCH_ALT: ld e,$40 ;5704 E=$40: switch to alternate bank DISPATCH: ld hl,($5C76) ;5706 HL = SEED (dispatch address) in a,($F4) ;5709 save current HSR push af ld a,e ;570C switch bank via HSR out ($F4),a call $1264 ;570F call address (in HOME ROM context) pop af ;5712 restore original HSR out ($F4),a ret ;================================================================ ; Hex string conversion ($6216 when copied to RAM) ; Converts byte in SEED low ($5C76) to 2-char hex string ; at the BASIC string workspace pointed to by CHANS ($5C4D) ;================================================================ HEX_CONVERT: ld a,($5C77) ;5716 high byte of SEED (iteration count?) ld hl,($5C4D) ;5719 HL = CHANS (string workspace pointer) call $6222 ;571C store high nibble ld a,($5C76) ;571F low byte of SEED (value to convert) push af srl a ;5723 extract high nibble srl a srl a srl a push af call $623B ;572C convert nibble to ASCII and store pop af sla a ;5730 extract low nibble sla a sla a sla a ld b,a pop af sub b ;573A low nibble = original - (high << 4) add a,$30 ;573B convert to ASCII '0'-'9' cp $3A jr c,.store ;573F if > '9', add 7 for 'A'-'F' add a,$07 .store: ld (hl),a ;5743 inc hl ret ;================================================================ ; Cleanup / mode restore ($6246 when copied to RAM) ; Adjusts string length byte in BASIC workspace ;================================================================ CLEANUP_1: ld hl,($5C4D) ;5746 CHANS dec hl dec hl dec hl dec hl dec hl dec hl ;574E back up 6 bytes to length field ld a,(hl) sub $80 ;5750 clear bit 7 (length adjustment) ld (hl),a ret ;================================================================ ; Binary string conversion ($6247 when copied to RAM) ; Converts byte in SEED to 8-char binary string ("01010011") ;================================================================ BIN_CONVERT: ld a,($5C77) ;5754 iteration control ld hl,($5C4D) ;5757 string workspace inc hl call $6223 ;575B setup ld a,($5C76) ;575E byte to convert ld b,8 ;5792 8 bits .bit_loop: rla ;5794 rotate left through carry ld (hl),'1' ;5795 assume bit is 1 jr c,.next ;5797 ld (hl),'0' ;5799 bit was 0 .next: inc hl djnz .bit_loop ;579C ret ;================================================================ ; Cleanup 2 ($6261 when copied to RAM) ; Same as CLEANUP_1 but at different entry point ;================================================================ CLEANUP_2: ld hl,($5C4D) ;579F dec hl dec hl dec hl dec hl dec hl dec hl ld a,(hl) sub $80 ld (hl),a ret nop ;57AD padding rst $38 ;57AE rst $38 ;57AF ;================================================================ ; Bootstrap 1: USR 55216 ($D7B0 in memory) ; Copies 22 bytes from $D700 to $6200 (dispatch routine) ; Called from BASIC line 1 during init ;================================================================ BOOTSTRAP_1: ld hl,$D700 ;57B0 source in RAM chunk 6 ld bc,$0016 ;57B3 22 bytes ld de,$6200 ;57B6 dest in RAM chunk 3 ldir ret ;================================================================ ; Bootstrap 2: USR 55228 ($D7BC in memory) ; Copies 64 bytes from $D716 to $6216 (hex converter + utilities) ; Called from BASIC line 201 (Hex/Dec Loader init) ;================================================================ BOOTSTRAP_2: ld hl,$D716 ;57BC ld de,$6216 ;57BF ld bc,$0040 ;57C2 64 bytes jr BOOTSTRAP_1+9 ;57C5 jump to LDIR; RET ;================================================================ ; Bootstrap 3: USR 55239 ($D7C7 in memory) ; Copies 89 bytes from $D754 to $6216 (binary converter + utilities) ; Called from BASIC line 501 (Tri-Base Arithmetic init) ;================================================================ BOOTSTRAP_3: ld hl,$D754 ;57C7 ld de,$6216 ;57CA ld bc,$0059 ;57CD 89 bytes ; falls through to LDIR; RET at $57B9 ```