Skip to content

An exception-word EPROM generator for the CTS25AL2 text-to-speech IC.

License

Notifications You must be signed in to change notification settings

mecparts/CTS256-exceptions

Repository files navigation

An Exception-Word EPROM Generator for the CTS256AL2

Breadboarded circuit

Background

The CTS256 is a companion chip to its better known sibling, the SP0256 allophone to speech chip. The CTS256 takes ASCII text and converts it to allophones for the SP0256; text to speech in 2 ICs.

This is 1980s tech that we're talking about, so it has its limitations. The CTS256 does a pretty good job at converting words to allophones, but every so often it goes off into the weeds. Simple words like "fine", "purpose" or "minutes" come out as "feen", "purples" or "minuets".

This is where the exception EPROM comes in. If present, it's searched before the CTS256's text to allophone rules are invoked. If the word is found in the EPROM, the associated allophones are used directly.

The problem is in the documentation. If you bought a CTS256 from Radio Shack back in the day, the datasheet that came with it mentioned the existence of the exception EPROM, and invited you to write to them for further (read: any) details. The 1988 Archer Semiconductor Reference Guide was better: it included a copy of a General Instrument application note that detailed, among other things, the use of the exception EPROM.

But the Archer copy of the application note was full of errors (I think it was OCR'd - badly - from the original). And the only copy of the original application note that I could track down on the web was a PDF of a photocopy of a dot matrix printed original. 8s and Bs, and Gs and 6s were very problematic. And since the exception EPROM contained a couple of large chunks of TMS7000 machine code, it really mattered whether that character was an 8 or a B!

I ended up hand disassembling the machine code to try and answer all the "which character is that?" questions. At the end of that there were still a couple that could have gone either way (addresses of function calls in the CTS256s ROM).

Long story short, I burned a 2716 with my best guess at things and, after moving the address select signal from /CS to /OE to change a 350nS 2716 into a 120nS 2716, it actually worked. Mirabile dictu!

The Exception-Word Generator is born

Now that I was fairly sure that I had the kinks out of the original example, it was time to start fixing some of those other words that I'd noticed the CTS256 getting wrong.

I first tried creating an assembler file full of DB statements. I'm sure that would have eventually worked, but it was just too painful.

What I really wanted was a program that would read a text file containing a list of words and their associated allophones, and generate a hex file that I could burn into an EPROM. Working off and on, I had a program that produced a working exception EPROM after just a day. It wasn't pretty, but it worked.

The Exception-Word list file

The format of the input file to the Xception program is as follows:

   BASE n ; where n is a hex digit between 1 and E, representing the
          ; base 4K address of the generated EPROM
   <[WORD]<=[allophone...]
   <[WORD]<=[allophone...]
        .
        .
        .
   <[WORD]<=[allophone...]
   <[SYMBOL or DIGIT]<=[allophone...]
   <[SYMBOL or DIGIT]<=[allophone...]
        .
        .
        .
   <[SYMBOL or DIGIT]<=[allophone...]

The format of the word/allophone definitions is the same as what's shown in the GI application note. The words should be ordered alphabetically (A-Z) but only by the first letter; within each letter group, the ordering of the words isn't important.

Words that are spoken differently depending on their usage, like wind as a verb (wind the clock) or a noun (the wind is blowing) can be differentiated by appending (V) or (N) to the word definition (<[WIND(N)]< for example).

Exception-Word Encoding Scheme

To store a unique word or symbol and its corresponding allophone address string in an efficient and flexible manner, the following encoding format was derived:

   <[encoded word or symbol]< = [encoded allophone address(es)]

   where < equals 13H
         [ equals 40H
         ] equals 80H

The first and last bytes is 13H. This informs the code-to-speech algorithm that the word or symbol is not a prefix or suffix.

If the word or symbol is an individual letter, then the representation of it between the brackes is an FFH; this includes the value of the left and right brackets. If it is a number or punctuation, then it is represented by its value from TABLE-1 plus the value of the left and right brackets.

Otherwise.

  1. The first letter in the word or symbol is always to be ignored; this does not apply to numbers or punctuation.
  2. The next letter in the word is represented by the value of the letter from TABLE-1, plus the value of the left bracket "[" which is 40H.
  3. The following letter(s), if and only if it is not the last letter in the word or symbol, is represented solely by its value from TABLE-1.
  4. The last letter in the word or symbol is represented by the value of the letter from TABLE-1, plus the value of the right bracket "]" which is 80H.

The allophone address string is encoded in a similar manner:

If only one allophone is used for the pronounciation, it is represented by its value from TABLE-2, plus the value of the left "[" and right "]" brackets which are 40H and 80H respectively.

Otherwise:

  1. The first allophone is represented by its value from TABLE-2, plus the value of the left bracket "[" which is 40H.

  2. The following allophone(s), if and only if it is not the last allophone in the string, is represented by its value from TABLE-2.

  3. The last allophone is represented by its value from TABLE-2 plus the value of the right bracket "]" which is 80H.

    Example: To encode "Au" to pronounce as "GOLD"
    <[Au]< = [GG2 OW LL DD1]
    13, F5, 13, 7D, 35, 2D, 95 <--This line is ready to store in
        ^                         EXCEPTION-WORD EPROM under the
        |                         "A" category. (The encoded string
        |                         is shown in Hex notation.)
        |
        +--Remember, throw away the first letter (in this case an
           "A"), then find the value of the next letter in TABLE-1
           and add 40H plus 80H to it so as to represent the left
           "[" and right "]" brackets.
    

For words, the leading "<" (which marks the start of a word) is mandatory. The trailing "<" (which marks the end of the word) is optional, and if it's left off it marks the word as a prefix form. This allows constructs such as:

   <[CAP]AB=[KK1 EY PP] ; CAPABILITY, CAPABLE

Without this, "capable" would be pronounced "cap-able", whereas it ought to be "cape-able".

For symbols, both the leading and trailing "<"s are optional. This allows symbols to occur in the middle of a word (e.g. "up&down" would become "up and down" with the example exception list).

TABLE-1
LETTERENCODED
VALUE
(hex)
NUMBERENCODED
VALUE
(hex)
SYMBOLENCODED
VALUE
(hex)
A21010space00
B22111!01
C23212"02
D24313#03
E25414$04
F26515%05
G27616&06
H28717'07
I29818(08
J2A919)09
K2B*0A
L2C+0B
M2D'0C
N2E-0D
O2F.0E
P30/0F
Q31:1A
R32;1B
S33<1C
T34=1D
U35>1E
V36?1F
W37@20
X38[3B
Y39\3C
Z3A]3D
^3E
_3F
`40
{5B
|5C
}5D
~5E

TABLE-2
ENCODED
VALUE
ALLOPHONESAMPLE
WORD
DURATION(ms)
00PA1PAUSE 10
01PA2PAUSE 30
02PA3PAUSE 50
03PA4PAUSE100
04PA5PAUSE200
05OY bOY290
06AY skY170
07EH End 50
08KK3Coab 80
09PP Pow150
0AJH dodGe400
0BNN1thiN170
0CIH sIt 50
0DTT2To100
0ERR1Rural130
0FAX sUcceed 50
10MM Milk180
11TT1parT 80
12DH1THey140
13IY sEE170
14EY bEIge200
15DD1coulD 50
16UW1tO 60
17AO OUght 70
18AA hOt 60
19YY2Yes130
1AAE hAt 80
1BHH1He 90
1CBB1Business 40
1DTH Thin130
1EUH bOOk 70
1FUW2fOOd170
20AW OUt250
21DD2Do 80
22GG3wiG120
23VV Vest130
24GG1Guest 80
25SH SHip120
26ZH aZUre130
27RR2bRain 80
28FF Food110
29KK2sKy140
2AKK1Can't120
2BZZ Zoo150
2CNG aNchor200
2DLL Lake 80
2EWW Wool140
2FXR repaIR250
30WH WHig150
31YY1Yes 90
32CH CHurch150
33ER1fIR110
34ER2fIR210
35OW bEAU170
36DH2THey180
37SS veSt 60
38NN2No140
39HH2Hoe130
3AOR stORe240
3BAR alARm200
3CYR cleAR250
3DGG2Got 80
3EEL saddLE140
3FBB2Business 60

Using the Exception-Word Generator

The Exception-Word Generator is written in fairly standard C. Any reasonably modern C compiler ought to be able to compile the source.

   xception <input text file >output hex file

The output hex file is sized for a 4K EPROM, as that's what was specified in the original application note. However, careful reading of the note reveals that the exception code can spread over multiple 4K blocks, up to a maximum of 48K using the memory map in the application note.

A demonstration Arduino sketch is included that shows the operation of the EPROM. The computer Joshua's lines from the 1983 film WarGames. Several words from Joshua's lines (e.g. file, minutes, island) are not rendered properly by the CTS256. When the exception-word EPROM is included in the circuit, they are spoken correctly (without mangling the spelling of the text).

References