2068 Programmer’s Toolkit AROS

This program is a multi-function programmer’s 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 ItemFunctionEntry Line
1Block Line Renumberer100
2Hex/Dec Loader200
3Disassembler300
4Tri-Base Arithmetic500
5UDG Generator600
6Header Reader700
7Configure Print Driver800
8Return to HOME Bank900

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 (RLn/RRn), 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.

Content

Appears On

Related Products

24K program has many built-in features (including tri-base arithmetic) and printer drivers. Available on tape ($9.95) or EPROM ($39.95)

Related Articles

Related Content

Image Gallery

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

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

Scroll to Top