ERRORS

Developer(s): Ed Shaughnessy
Date: 1986
Type: Program
Platform(s): TS 2068
Tags: Demo

ERRORS prints all 28 ZX81/TS1000 BASIC error messages by invoking machine code POKEd into RAM starting at address 40000. The 25-byte machine code routine is loaded from DATA statements in line 60 and verified with a checksum (2651) before execution. The routine uses ROM calls — specifically the address 0x08A9 (RST or CALL sequences visible in the DATA) — to walk through and display each error message stored in the system ROM. A commented-out alternative DATA line at line 120 shows the same routine expressed as hex string literals, left as an incomplete development artifact.


Program Structure

The program is organized into four logical phases:

  1. Setup (line 20): Resets the DATA pointer to line 60, sets the load address l=40000, and initializes a checksum accumulator cs=0.
  2. Machine code loading (lines 30–50): A FOR loop reads 25 bytes from the DATA statement and POKEs them into memory starting at address 40000, accumulating a byte sum in cs.
  3. Checksum verification (line 70): Compares the accumulated sum against the expected value 2651. A mismatch halts with an error message, guarding against corrupt or mistyped DATA.
  4. Execution (line 80): RANDOMIZE USR l transfers control to the machine code at address 40000.

Lines 90–120 are development remnants: REMs noting the routine was written on 4/29/86 and marked “incomplete,” followed by line 120 which holds the same machine code expressed as hex string literals in a DATA statement that is never READ. Line 130 is a STOP that prevents line 120’s DATA from being accidentally reached. Line 140 saves the program with LINE 20 to auto-run from line 20.

Machine Code Routine

The 25 bytes POKEd at address 40000 (0x9C40) are a Z80 routine. Disassembling the DATA values from line 60:

OffsetHexZ80 MnemonicNotes
006 1CLD B, 28Loop counter: 28 error messages
2C5PUSH BCPreserve counter
3CD A9 08CALL 0x08A9ROM routine to print error message
6C1POP BCRestore counter
778LD A, BCopy counter to A
811 65 0FLD DE, 0x0F65Load address (ROM error table or helper)
11C5PUSH BCPreserve counter again
12CD 3F 07CALL 0x073FROM routine (likely newline/print)
1506 32LD B, 50Inner delay/pause count
1776HALTWait for interrupt (timing delay)
1810 FDDJNZ -3Repeat HALT 50 times (~1 second pause)
20C1POP BCRestore outer counter
2110 EBDJNZ -21Outer loop: next error message
23CFRST 0x08Return to BASIC (error/restart)
241ALD A, (DE)(Likely part of RST 0x08 parameter)

The outer loop runs 28 times (LD B, 28 = 0x1C), calling ROM address 0x08A9 each iteration to display an error message, then pausing for approximately 50 interrupt cycles (~1 second at 50 Hz) using a HALT/DJNZ inner loop before advancing to the next message. The RST 0x08 at the end provides a clean return to BASIC.

Checksum Verification

The sum of all 25 DATA bytes is checked against the constant 2651 at line 70. This is a simple but effective integrity check: if any byte in line 60 is mistyped during entry, the program stops rather than executing corrupt machine code that could crash or corrupt the system. The checksum is an arithmetic sum (not CRC), which is typical for this era of hobbyist machine code loading routines.

Notable Techniques

  • RESTORE 60 at the start of line 20 ensures the DATA pointer is always reset to line 60, making the program safely re-runnable without manually resetting the pointer.
  • The use of RANDOMIZE USR l rather than RAND USR l is the standard idiom for calling machine code; the return value from USR is discarded by RANDOMIZE.
  • The HALT instruction inside the timing loop leverages the interrupt mechanism as a delay primitive — each HALT suspends the CPU until the next interrupt (every ~20 ms at 50 Hz), giving a reliable frame-rate-based delay without needing a busy-wait counter.
  • Line 120’s DATA of hex strings was an in-development alternative representation of the same machine code, preserved as a reference but intentionally unreachable due to the STOP on line 130.
  • VAL is not used for the GO TO/GO SUB targets here; the author uses a simple variable l for the USR address, which is clear and idiomatic.

Bugs and Anomalies

The offset-8 operand in the DATA is 17, 101, 15 (decimal), which decodes as LD DE, 0x0F65. However, the hex comment in line 120 shows "11670f", which would be LD DE, 0x0F67. This two-byte discrepancy between the actual DATA (line 60) and the hex comment (line 120) suggests the hex string version in line 120 was not kept in sync with the final binary — consistent with its “incomplete” status noted in the REMs.

The final two bytes CF 1A (RST 0x08 followed by 1A) follow the convention where RST 8 takes an inline operand byte indicating the error code or action; 0x1A (26 decimal) would correspond to a specific system action.

Image Gallery

Source Code

  10 REM errors prints 28 error messages. From CTM 5/86 by Ed Shaughnessy
  20 RESTORE 60:LET l=40000:LET cs=0
  30 FOR i=l TO l+24
  40 READ x:POKE i,x:LET cs=cs+x
  50 NEXT i
  60 DATA 6,28,197,205,169,8,193,120,17,101,15,197,205,63,7,6,50,118,16,253,193,16,235,207,26
  70 IF cs <>2651 THEN PRINT "error in line 60":STOP 
  80 RANDOMIZE USR l
  90 REM mc version of above
 100 REM 4/29/86 incomplete
 110 REM mc code to start at 9c40 hex
 120 DATA "061c","c5","cda908","c1","78","11670f","c5","cd3f07","0632","76","10fd","c1","10eb","cf","1a"
 130 STOP 
 140 SAVE "ERRORS" LINE 20

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