5 Interesting Routines

Date: 1986
Type: Program
Platform(s): TS 2068

This program is a menu-driven collection of six utility routines for the TS2068, selectable from a central dispatcher at line 2500. Two “reader” routines (lines 10 and 100) walk the BASIC program area by peeking system variables at addresses 23635/23636 (PROG) and 23627/23628 (VARS), displaying each byte’s address, raw value, and printable character; Reader 2 additionally decodes line headers, line lengths, and embedded five-byte floating-point number tokens using a DATA-driven label lookup. Lines 500 and 1000 implement two custom INPUT routines—one character-at-a-time with cursor positioning and one that fills a dimensioned string array—both using the PAUSE 0/INKEY$ keypress idiom with backspace support. Line 1500 loads and executes 25 bytes of Z80 machine code (checksum-verified at 2651) into RAM at address 40000 to print all 28 BASIC error messages, and line 2000 performs a 16K ROM integrity check using a byte sum (expected 1752253), a zero-byte count (expected 609), and a floating-point arithmetic error accumulation test.


Program Analysis

Program Structure

The program is organized as six self-contained routines sharing a single file, dispatched by a menu at line 2500. Line 5 jumps directly to the menu on first run. Each routine occupies a distinct line-number band, separated by REM banners, and each returns to the menu via GO TO 2500 or RUN/PAUSE 200: RUN.

LinesRoutineMenu Option
10–70Reader 1 — raw byte dump of BASIC program area1
100–250Reader 2 — structured BASIC line decoder2
500–630Input 1 — single-string character editor3
1000–1110Input 2 — array string editor4
1500–1630Error Messages — machine code loader5
2000–2310ROM Check — 16K ROM integrity tests6
2500–2590Menu dispatcher

Note a minor bug in the menu: option 6 is listed in the PRINT at line 2520, but the INPUT validation at line 2530 only accepts values 1–5 (a>5 rejects 6), making ROM Check unreachable through the menu. Line 2590 (RUN 2000) is therefore dead code under normal menu navigation.

Reader 1 (Lines 10–70)

This routine uses system variables PROG (address 23635/23636) and VARS (23627/23628) to establish the boundaries of the BASIC program area. It iterates byte-by-byte from PROG to VARS, printing each address and raw byte value. Characters with codes 16–23 (Spectrum/TS2068 control codes for INK, PAPER, FLASH, etc.) are labeled (unprintable); all others are rendered with CHR$. After reaching VARS, it pauses 200 frames and restarts.

Reader 2 (Lines 100–250)

Reader 2 provides a more structured dump, decoding the BASIC line format: two-byte line number (big-endian), two-byte line length (little-endian), then token bytes. The subroutine at line 240 prints the current address and its byte value in columnar form. Special handling is provided for token 14 (the embedded five-byte floating-point literal): it triggers a RESTORE and reads five labels from the DATA statement at line 250 ("FIVE","BYTE","EQUIV.","OF","NUMBER"), printing one label per byte. End-of-program is detected when p=v (VARS boundary).

Input 1 (Lines 500–630)

This routine implements a character-by-character string input with on-screen cursor, using PAUSE 0/INKEY$ at line 560. A flashing ? serves as the cursor. Backspace (CHR$ 12) deletes the last character by slicing the string with t$(TO LEN t$-1) and overwriting the screen position with a space. The screen-wrapping logic at line 540 is notable: it tests y>31 OR y=-1 and adjusts row (x) and column (y) accordingly, supporting multi-line input.

Input 2 (Lines 1000–1110)

This routine fills a two-dimensional string array t$(n,l) (10 rows × 15 columns) one character at a time. Each row is pre-filled with hyphens (CHR$ 45) as a visual field indicator. Backspace reduces k and truncates the row string. Unlike Input 1, wrapping between rows is handled by the FOR j loop structure rather than explicit coordinate arithmetic.

Machine Code Error Message Printer (Lines 1500–1630)

Line 1510 sets the load address to l=40000. A FOR loop POKEs 25 bytes of Z80 machine code into RAM, accumulating a checksum. Line 1560 verifies the checksum equals 2651 before executing with RANDOMIZE USR l. The DATA bytes decode as:

  1. 06 1C — LD B,28 (loop counter for 28 error messages)
  2. C5 — PUSH BC
  3. CD A9 08 — CALL 0x08A9 (ROM routine)
  4. C1 — POP BC
  5. 78 — LD A,B
  6. 11 65 0F — LD DE,0x0F65
  7. C5 — PUSH BC
  8. CD 3F 07 — CALL 0x073F (print routine)
  9. 06 32 — LD B,50
  10. 76 — HALT (delay loop)
  11. 10 FD — DJNZ (back to HALT)
  12. C1 — POP BC
  13. 10 EB — DJNZ (outer loop)
  14. CF — RST 8 (error restart)
  15. 1A — error code 26

Lines 1580–1610 contain developer notes and commented-out hex strings for an alternative machine code version, and line 1620 contains a MOVE command and OUT 244 calls indicating TS2068-specific cartridge/bank switching that appears to be unfinished development code.

ROM Check (Lines 2000–2310)

Three independent checks are performed against the 16KB ROM (addresses 0–16383):

  • Byte sum check: accumulates PEEK i for all 16384 bytes; expected value 1752253.
  • Zero-byte count: counts bytes equal to zero; expected value 609.
  • Floating-point arithmetic error test: loops from 1000 to 1100 computing i*0.5 - i/2, i*0.1 - i/10, and i - SQR(i*i), which should each yield zero for a correct ROM but accumulate rounding errors. The combined average is compared against a hard-coded expected error of 1.3391177E-7.

The arithmetic error test is particularly clever: it exploits the fact that floating-point rounding behavior is deterministic for a correctly burned ROM, making accumulated error a fingerprint of ROM fidelity rather than just content. The comment at line 2300 notes that the additional 8K ROM for cartridge and bank-switching functionality is not tested.

Notable BASIC Idioms

  • PAUSE 0: LET q$=INKEY$ — efficient single-keypress wait at lines 560, 1040.
  • 256*PEEK addr + PEEK (addr+1) vs. PEEK addr + 256*PEEK (addr+1) — both big-endian and little-endian 16-bit reads are used appropriately for line numbers and line lengths respectively.
  • RESTORE without a line number at line 180 resets the DATA pointer to the first DATA statement, which here is line 250 — a side effect that works correctly only because no other DATA is read before this point in routine 2’s execution.
  • Checksum verification before RANDOMIZE USR is a sound defensive practice for machine code loaders.

Content

Appears On

Library tape of the Indiana Sinclair Timex User’s Group.

Related Products

Related Articles

Related Content

Image Gallery

5 Interesting Routines

Source Code

    5 GO TO 2500
   10 LET p=PEEK 23635+256*PEEK 23636: REM  reader 1 
   20 LET v=PEEK 23627+256*PEEK 23628
   30 LET c=PEEK p
   40 PRINT p;TAB 8;c;: IF c<16 OR c>23 THEN PRINT ,CHR$ c: GO TO 60
   50 PRINT ,"(unprintable)"
   60 LET p=p+1: IF p<v THEN GO TO 30
   70 PAUSE 200: RUN 
   99 REM -----------------------
  100 LET p=PEEK 23635+256*PEEK 23636: REM reader 2 
  110 LET v=PEEK 23627+256*PEEK 23628
  120 LET n=256*PEEK p+PEEK (p+1)
  130 GO SUB 240: PRINT n;TAB 21;"( LINE ": LET p=p+1: GO SUB 240: PRINT TAB 21;"( NUMBER": LET p=p+1
  140 LET le=PEEK p+256*PEEK (p+1)
  150 GO SUB 240: PRINT le;TAB 21;"( LINE ": LET p=p+1: GO SUB 240: PRINT TAB 21;"( LENGTH": LET p=p+1
  160 LET le=p+le
  170 LET c=PEEK p
  180 GO SUB 240: IF c=14 THEN RESTORE : PRINT TAB 21;"NUMBER:": FOR j=1 TO 5: LET p=p+1: GO SUB 240: READ n$: PRINT TAB 21;n$: NEXT j: GO TO 210
  190 IF c<16 OR c>23 THEN PRINT TAB 16;CHR$ c: GO TO 210
  200 PRINT ,"(unprintable)"
  210 LET p=p+1: IF p=v THEN PAUSE 200: GO TO 2500
  220 IF p=le THEN GO TO 120
  230 GO TO 170
  240 PRINT p;TAB 8;PEEK p,: RETURN 
  250 DATA "FIVE","BYTE","EQUIV.","OF","NUMBER"
  499 REM -----------------------
  500 LET x=10: LET y=0: LET t$="Name": REM INPUT 1 
  510 PRINT AT x,y;t$
  520 LET y=y+2+LEN t$
  530 LET t$=""
  540 IF y>31 OR y=-1 THEN LET y=31*(y=-1): LET x=x-1+2*(y=0)
  550 PRINT AT x,y; FLASH 1;"?"
  560 PAUSE 0: LET q$=INKEY$
  570 IF q$=CHR$ 13 AND t$<>"" THEN GO TO 630
  580 IF q$=CHR$ 12 AND t$<>"" THEN PRINT AT x,y;CHR$ 32: LET y=y-1: LET t$=t$( TO LEN t$-1): GO TO 540
  590 IF q$<CHR$ 32 THEN GO TO 560
  600 LET t$=t$+q$
  610 PRINT AT x,y;q$: LET y=y+1
  620 GO TO 540
  630 PRINT AT x,y;CHR$ 32: RETURN 
  999 REM -----------------------
 1000 LET x=5: LET n=10: LET l=15: DIM t$(n,l): REM INPUT 2 
 1010 FOR j=1 TO n: FOR k=1 TO l
 1020 PRINT AT x+j-1,k;CHR$ 45
 1030 NEXT k: LET k=1
 1040 PAUSE 0: LET q$=INKEY$
 1050 IF q$=CHR$ 13 AND k<>1 THEN GO TO 1100
 1060 IF q$=CHR$ 12 AND k<>1 THEN LET k=k-1: PRINT AT x+j-1,k;CHR$ 45: LET t$(j)=t$(j, TO k): GO TO 1040
 1070 IF q$<CHR$ 32 THEN GO TO 1040
 1080 LET t$(j,k)=q$: PRINT AT x+j-1,k;q$: LET k=k+1
 1090 IF k<=l THEN GO TO 1040
 1100 NEXT j
 1110 RETURN 
 1499 REM -----------------------
 1500 REM errors prints 28 error messages. From CTM 5/86 by Ed Shaughnessy
 1510 RESTORE 1550: LET l=40000: LET cs=0
 1520 FOR i=l TO l+24
 1530 READ x: POKE i,x: LET cs=cs+x
 1540 NEXT i
 1550 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
 1560 IF cs<>2651 THEN PRINT "error in line 50": STOP 
 1570 RANDOMIZE USR l
 1580 REM mc version of above
 1590 REM 4/29/86 incomplete
 1600 REM mc code to start at 9c40 hex
 1610 DATA "061c","c5","cda908","c1","78","11670f","c5","cd3f07","0632","76","10fd","c1","10eb","cf","1a"
 1620 OUT 244,1: MOVE "errors.bas",10: OUT 244,0
 1630 PAUSE 200: RUN 
 1999 REM -----------------------
 2000 REM TS2068 16K ROM CHECK
 2010 CLS : FLASH 0: BORDER 1: PAPER 6: INK 0: BRIGHT 0: INVERSE 0: CLS 
 2020 BEEP 1,0
 2030 PRINT '"      TS2068 16K ROM CHECK"
 2040 PRINT '"    RUN TIME IS FOUR MINUTES"
 2050 PRINT '"   TO BEGIN PRESS 1 AND ENTER"
 2060 INPUT F
 2070 IF F<>1 THEN GO TO 2050
 2080 PRINT AT 10,12;"Working"
 2090 LET b=0: LET a=b
 2100 FOR i=a TO 16383
 2110 LET a=a+PEEK i
 2120 IF PEEK i=0 THEN LET b=b+1
 2130 NEXT i
 2140 CLS : PRINT "     TS2068 16K ROM CHECK"''
 2150 IF a=1752253 THEN PRINT TAB 5;"FIRST ROM CHECK IS OK"
 2160 PRINT 
 2170 IF b=609 THEN PRINT TAB 5;"SECOND ROM CHECK IS OK"
 2180 PRINT 
 2190 REM rom accuracy check
 2200 LET c=0: LET d=c: LET e=c
 2210 FOR i=1000 TO 1100
 2220 LET c=c+i*.5-i/2
 2230 LET d=d+i*.1-i/10
 2240 LET e=e+i-SQR (i*i)
 2250 NEXT i
 2260 LET AEA=1.3391177E-7
 2270 PRINT "   ARITHMETIC ERRORS AVERAGE =            ";AEA
 2280 PRINT 
 2290 IF (C+D+E)/300<=AEA THEN PRINT "  ARITHMETIC ERRORS AVERAGE IS             CORRECT"
 2300 PRINT ''"NOTE......THE ADDITIONAL 8K ROM  DEDICATED TO COMMAND CARTRIDGE  CAPABILITIES AND BANK SWITCHING IS NOT INCLUDED OR TESTED BY"'"THIS PROGRAM"
 2310 BEEP 1,0: PAUSE 200
 2499 REM -----------------------
 2500 BORDER 6: PAPER 7: INK 9: CLEAR 
 2510 PRINT '"      5 Interesting Routines"
 2520 PRINT '''"      1  Reader 1"''"      2  Reader 2"''"      3  Input 1"''"      4  Input 2"''"      5  Error Messages"''"      6  ROM Checks"
 2530 INPUT "Select 1, 2, 3, 4, 5, 6, ";a: IF (a<1) OR (a>5) THEN GO TO 2530
 2540 IF a=1 THEN RUN 10
 2550 IF a=2 THEN RUN 100
 2560 IF a=3 THEN RUN 500
 2570 IF a=4 THEN RUN 1000
 2580 IF a=5 THEN RUN 1500
 2590 RUN 2000
 9998 SAVE "5 routines" LINE 1

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

People

No people associated with this content.

Scroll to Top