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 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 (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 25092idiom to pass a parameter to machine code via SEED ($5C76/$5C77) is used throughout instead of direct POKEing. LPRINTandPRINTare 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)andINT(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 1000and is not listed in the main menu. - Lines 9995–9996 are bare
STOPstatements 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
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.
