HS68 Driver

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

This program is a speech synthesis editor that manages a digitized word library stored in high memory, for use with Tad Painter’s Speech Synthesizer program, roughly from address 55000 to 65535.

It provides routines to record words (via machine code at addresses such as 65025, 65042, 65063, 65077, and 65080), play them back, adjust acquisition speed, and save or load the library to and from tape using SAVE/LOAD CODE.

A graphical memory browser (lines 2–65) uses PLOT and DRAW with PEEK to render a waveform of raw byte values at any 256-byte page of memory, with a cursor controlled by the “8” and “5” keys.

The program encodes word entries as 4-byte records (start address, length) in a descending linked list in high memory, with a block-copy routine at USR 65195 used to pack data toward a moving destination pointer tracked in locations 65207–65208.

Included with the HS68 digitizing hardware.


Program Analysis

Program Structure

The program is organized into loosely coupled functional blocks rather than a single linear flow. Execution begins with GO SUB 9000 (initialization) then falls through to GO TO 9500 (the main menu). From the menu the user types a numeric address to jump to any module. The blocks are:

  • Lines 2–65: Interactive memory waveform browser
  • Lines 14–20: Low-level utility entry points (machine code launchers, POKE configuration, manual byte entry)
  • Lines 50–75: Waveform browser keypress handler and word-marking logic; also the block-copy subroutine (line 70/71–75)
  • Lines 2000–2060: Sentence-to-speech routine — tokenizes a string on spaces and speaks each word number
  • Lines 3000–3070: Library display routine — walks the linked list of word records
  • Lines 3999–4010: Core speak routine — POKEs the word number and calls USR 65077
  • Lines 6000–7800: Save, verify, and load routines for the word library CODE block
  • Line 8000: SOUND fanfare subroutine
  • Lines 8500, 9000–9003: Destination-pointer reset and initialization
  • Lines 9500–9540: Main menu display
  • Lines 9998–9999: Data erase and program save utilities

Machine Code Interface

All speech and data operations are delegated to machine code routines residing in high RAM. The BASIC communicates with them exclusively through POKEs into fixed parameter addresses before calling RANDOMIZE USR. The known entry points and their apparent roles are:

AddressRoleParameters (POKEd addresses)
65025Playback65096, 65117 (play speed)
65042Record acquisition speed POKE targetset via line 18
65077Speak word65023–65024 (word number), 65207–65208 (dest ptr)
65080Unknown utility (line 15)
65195Block memory copy (descending)65196–65197 (count), 65199–65200 (source), 65202–65203 (dest)

All 16-bit values are split into high and low bytes with the standard BASIC idiom: INT(n/256) for the high byte and n - 256*INT(n/256) for the low byte, then POKEd to consecutive addresses in little-endian order.

Waveform Memory Browser

Lines 2–65 implement a rudimentary oscilloscope-style display. For a given 256-byte memory page starting at address q, the loop at line 3 steps through bytes three at a time (STEP 3). Each byte is read with PEEK and its value, divided by 1.5 to fit the screen height, is used as the length of a vertical DRAW 0, ... line rooted at the corresponding x position with PLOT INVERSE 1. This gives a rough amplitude graph of raw byte data — useful for locating speech waveform boundaries. The variable over tracks a cursor position (initialized to 127, the center of the 256-byte window), and pressing “8” or implicitly “5” (line 60, the fall-through case) moves it right or left. The current address and its PEEK value are shown at the top of the screen (lines 65).

Word Library Data Structure

The word library is stored as a descending linked list of 4-byte records in high memory, with the list head near address 65022 and growing downward toward 55000 (the workspace boundary noted in line 14). Each record contains:

  1. 2 bytes: word number (big-endian at WA, WA-1)
  2. 2 bytes: payload length NL (big-endian at WA-2, WA-3)

The library walker at line 3050 advances with LET WA = WA - 4 - NL, skipping over the word data to reach the next record header. The word-marking logic in lines 57–58 writes a new record at stw (start-of-word address) during interactive browsing of the waveform.

Sentence-to-Speech Routine

Lines 2000–2060 implement a simple tokenizer. A trailing space is appended to S$, and the loop scans for space characters. When one is found, VAL S$(B TO X-1) extracts the numeric token and passes it as NO to the speak subroutine at line 4002. This allows a user to enter a sequence of word-index numbers separated by spaces and have them spoken in order — a sentence-by-number approach rather than text-to-speech.

SOUND Fanfare

Line 8000 uses SOUND with multiple channel/register arguments to produce a short musical cue, followed by PAUSE 60 and a second SOUND call to stop the tone. This subroutine is called after save and verify operations as an audible completion signal.

Notable Idioms and Techniques

  • GO TO v / GO TO g after an INPUT prompt is used throughout as a manual dispatcher — the user types a line number to navigate between modules, replacing a conventional menu loop.
  • POKE 23609,15 in line 9003 sets the ZX Spectrum system variable REPDEL to slow down key repeat, relevant for the cursor-movement loop in the waveform browser.
  • Line 58 contains dead code: after GO TO 3, the fragment INPUT "go where? ";g: GO TO g is unreachable but was presumably left from an earlier version.
  • The variable CK (initialized in line 9003 and set in lines 57–58) acts as a one-bit state flag to ensure that a word-end marker (“e”) is only accepted after a word-start marker (“s”) has been placed.
  • Line 9998 erases the data file by POKEing zeros from 65022 down to 55000 in a FOR/NEXT loop — a slow but straightforward wipe.
  • The SAVE "BLANKLIB" CODE 65022,513 in line 9999 saves 513 bytes of the (presumably zeroed) library area as a blank template file.

Bugs and Anomalies

  • Line 6 is GO TO 50, but line 50 checks INKEY$="" in a tight loop. After PAUSE 160, any key still held from the previous action could immediately exit the wait — potentially skipping a screen.
  • Line 4 computes DRAW 0, PEEK(q-j)/1.5 with no bounds check. For large PEEK values (e.g., 255), the draw length of 170 pixels could run off the top of the display.
  • The variable dv is computed in line 4006 but never used anywhere visible in the listing — it may feed into machine code through a POKE elsewhere that was omitted, or it may be vestigial.
  • Line 57 marks the start of a word with LET stw = over + q. Since over ranges 0–255 and q is a page-aligned address, this correctly computes the absolute address, but the variable name stw is also used in the block-copy subroutine (line 70) as the source start address — sharing the name across two different semantic roles is a potential source of confusion.

Content

Appears On

Related Products

Hardware for 2068 owners to create their own vocabularies. Plugs into ear socket. Spoken words are digitized and stored in...
Machine language program, multiple vocabularies, flexible memory requirements, very intelligible. Nine programs include 273 high quality digitized words in variable...

Related Articles

Related Content

Image Gallery

HS68 Driver

Source Code

    1 GO SUB 9000: GO TO 9500
    2 INPUT "enter addrss to be displayed";n
    3 LET over=127: FOR q=n TO 0 STEP -256: CLS : PRINT AT 0,0;q: FOR j=0 TO 255 STEP 3
    4 PLOT INVERSE 1;255-j,4: DRAW 0,PEEK (q-j)/1.5
    5 NEXT j: PAUSE 160
    6 GO TO 50
   14 REM **** work space is from 31488 to 36000 ****
   15 RANDOMIZE USR 65080: INPUT "go where? ";v: GO TO v
   16 RANDOMIZE USR 65025: BEEP .1,50
   17 POKE 65087,140: POKE 65086,160: POKE 65126,122: POKE 65125,255: INPUT "go where? ";g: GO TO g
   18 INPUT "speed of aquisition for record? ";sq: POKE 65042,sq: POKE 65063,sq: INPUT "speed of aquisition for play? ";sq: POKE 65096,sq: POKE 65117,sq: INPUT "go where? ";v: GO TO v
   20 INPUT "enter start addrss for code ";sta: FOR x=sta TO 65535: INPUT "byte? ";byte: PRINT x,byte: POKE x,byte: NEXT x: STOP 
   50 IF INKEY$="" THEN GO TO 50
   52 PLOT INVERSE 1;over,3
   53 LET i$=INKEY$
   55 IF i$="8" THEN LET over=over+1: GO TO 65
   56 IF i$="m" THEN GO TO 9500
   57 IF i$="s" THEN LET CK=1: LET stw=over+q: INPUT "enter # of this word ";now: POKE stw,INT (now/256): POKE stw-1,now-256*INT (now/256)
   58 IF i$="e" AND CK=1 THEN LET CK=0: LET enw=over+q: LET nob=stw-enw: POKE stw-2,INT (nob/256): POKE stw-3,nob-4-256*INT (nob/256): GO SUB 70: LET no=now: GO SUB 4002: LET n=enw-256: GO TO 3: INPUT "go where? ";g: GO TO g
   59 IF i$="n" THEN LET over=127: LET n=n-256: GO TO 3
   60 LET over=over-1
   65 PLOT over,3: PRINT AT 0,0;q-255+over: PRINT AT 1,0;PEEK (q-255+over);"   ": GO TO 50
   69 INPUT "enter strt addrs for data TO be trnsfrd (decsending)";stw: INPUT "enter # of bytes";nob: INPUT "destination adrss?";dw: GO TO 71
   70 REM nob is # of bytes,stw is start addrs,dw is destin addrs
   71 LET dq=dw: POKE 65197,INT (nob/256): POKE 65196,nob-256*INT (nob/256)
   72 POKE 65200,INT (stw/256): POKE 65199,stw-256*INT (stw/256)
   73 POKE 65203,INT (dw/256): POKE 65202,dw-256*INT (dw/256)
   74 LET dw=dw-nob: RANDOMIZE USR 65195
   75 POKE 65208,INT (DW/256): POKE 65207,DW-INT (DW/256)*256: PRINT "dw= ";dw: RETURN 
 2000 INPUT "ENTER SENTENCE TO BE SPOKEN ";S$
 2010 LET S$=S$+" ": LET B=1: FOR X=1 TO LEN S$
 2020 IF S$(X TO X)=" " THEN LET NO=VAL S$(B TO X-1): GO SUB 4002: LET B=X
 2050 NEXT X
 2060 RETURN 
 3000 REM **ROUTINE TO DISPLAY LIBRARY**
 3005 LET WA=65022
 3010 LET NO=PEEK WA*256+PEEK (WA-1)
 3020 LET NL=PEEK (WA-2)*256+PEEK (WA-3)
 3030 PRINT NO;" ";
 3040 GO SUB 4002
 3050 LET WA=WA-4-NL
 3060 IF WA>DW THEN GO TO 3010
 3065 PRINT 
 3070 INPUT "go where? ";g: GO TO g
 3999 INPUT "enter # TO be spoken";no
 4000 REM ** ROUTINE TO SPEAK-LET NO=DESIRED WORD **
 4002 POKE 65024,INT (no/256): POKE 65023,no-(INT (no/256)*256)
 4003 LET dw=PEEK 65208*256+PEEK 65207: POKE dw,INT (no/256): POKE dw-1,no-(INT (no/256)*256): POKE dw-2,0: POKE dw-3,1
 4005 RANDOMIZE USR 65077
 4006 LET dv=PEEK 65125+256*PEEK 65126
 4010 RETURN 
 6000 INPUT "ENTER NAME OF LIB TO BE SAVED";N$: SAVE N$CODE dw,65535-dw
 6010 GO SUB 8000
 6500 INPUT "DO YOU WANT TO VERIFY? ";V$: IF V$(1,1)="Y" THEN PRINT AT 11,8; FLASH 1;"READY TO VERIFY ": GO TO 7000
 6750 INPUT "GO WHERE? ";G: GO TO G
 7000 VERIFY "": VERIFY ""CODE 
 7500 GO SUB 8000
 7750 CLS : INPUT "GO WHERE? ";G: GO TO G
 7800 INPUT "ENTER NAME OF LIB TO BE LOADED OR ENTER";N$: LOAD N$CODE 
 8000 SOUND 0,41;1,0;8,13;7,62: PAUSE 60: SOUND 8,0;7,63: RETURN 
 8500 LET dw=dq: POKE 65208,INT (dw/256): POKE 65207,dw-INT (dw/256)*256: INPUT "go where? ";g: GO TO g
 9000 LET dw=PEEK 65208*256+PEEK 65207
 9001 LET r=16: LET p=15: LET a=18: LET s=6000: LET M=9500: LET D=2: LET WS=36100: LET C=8500
 9002 LET H=3000: LET L=7800
 9003 LET ck=0: POKE 23609,15
 9010 RETURN 
 9500 CLS : PRINT ''TAB 7;"M-MENU:"''
 9502 PRINT TAB 8;"R-RECORD"
 9504 PRINT TAB 8;"P-PLAYBACK"
 9506 PRINT TAB 8;"A-ADJUST RATE"
 9508 PRINT TAB 8;"S-SAVE LIBRARY"
 9509 PRINT TAB 8;"L-LOAD LIBRARY"
 9510 PRINT TAB 8;"H-HEAR LIBRARY"
 9511 PRINT TAB 8;"C-CORRECT LAST MISTAKE"
 9513 PRINT TAB 8;"______________"''
 9514 PRINT TAB 8;"D-DISPLAY WORD:"''
 9515 PRINT TAB 9;"M-CALL MENU"
 9516 PRINT TAB 9;"N-NEXT SCREEN"
 9518 PRINT TAB 9;"S-MARK START OF WORD"
 9520 PRINT TAB 9;"E-MARK END OF WORD"
 9521 PRINT TAB 9;"8-SCAN RIGHT/5-LEFT"
 9522 PRINT TAB 9;"wS=START OF WORK SPACE"
 9540 INPUT "GO WHERE? ";G: GO TO G
 9600 STOP 
 9998 INPUT "DO YOU WANT TO ERASE DATA FILE?";Y$: IF Y$="Y" THEN FOR x=65022 TO 55000 STEP -1: POKE x,0: NEXT x: STOP 
 9999 SAVE "SPEAK EDIT": GO SUB 8000: SAVE "BLANKLIB"CODE 65022,513: GO SUB 8000: STOP 

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

People

No people associated with this content.

Scroll to Top