ASSEMBLY LANGUAGE TALKER
Talking Typewriter for ST
This month we show you how to build, install, and program an ST hardware speech synthesizer using a standard Radio Shack chip. Before you can use the ST speech software described in this article, you need to build the speech synthesizer circuit as explained in the Talking Typewriter article, elsewhere in this issue. ST programmers, however, will find it useful to examine the ST Machine language Talker just as a demonstration of how to drive external speech hardware.
TYPING IT IN
Assembly Language Talker is written in 68000 Assembly Language as implemented by AS68.PRG, the assembler included with the Atari Developers Kit. But nearly any 68000 assembler which conforms to standard Motorola mnemonics will also work-examples include the MetaComco Macro Assembler and A-Seka.
Like our WEFAX program published in September 1986, Assembly Language Talker doesn't realize GEM exists. You'll also notice that this program is somewhat longer than the 8-bit BASIC version. That's because when using assembly language we need to specify low-level control structures such as how to display a character on the screen or get a character from the keyboard. BASIC takes care of these things more or less automatically.
Use your favorite text editor or word processor to carefully type in Listing 1, TALKTYPE.S, and save a copy to disk.
(Antic Disk subscribers: Skip down to the Program Operation section of this article. TALKTYPE.S is on Side B of the monthly disk. Follow the ST Help file instructions to transfer the programs from the 8-bit disk into the ST.)
After you type in Listing 1 and save a copy, you must assemble it into an executable program. While other assemblers may operate differently, the method I use with AS68.PRG is as follows. First, you will need a disk with the following ten programs on it.
1. A568 .PRG 2. AS68SYMB.DAT 3. BATCH .TTP 4. LINK68 .PRG 5. OSBIND .O 6. RELMOD .O 7. RM .PRG 8. WAIT .PRG 9. ASM .BAT 10. TALKTYPE.SThe top eight programs are in the Atari ST Developers Kit, and you just typed in TALKTYPE.S. But what about number nine, ASM.BAT?
You'll need to create your own ASM.BAT batch file, but it's very short. The file contains the following lines of code:
as68 -l -u %1.s
link68 [u,s] %1.68k=%1,osbind
relmod %1.68k %1.tos
Note that the -l in the first line is a lowercase letter l-all the other similar looking characters are the number one.
You may type in this file with any text editor. When finished, save the above file to disk as ASM.BAT. This is the file which will instruct the BATCH program how to assemble and link the TALKTYPE.S program.
With all the needed files on one disk, double-click on BATCH.TTP. In the resulting dialog box type ASM TALKTYPE without the .s extender. AS68 will assemble your source code into an object (.o) file. LINK68 will the resulting .o file with OSBIND.O to create a finished TOS program. When finished, you'll find TALKTYPE.TOS on your desktop. This is the Machine Language Talker program.
Essentially, the program teaches the ST to wait for a keystroke. When a key is pressed, its value is checked. If the key is a letter or a number, we use the value of the keystroke to access the phoneme codes that will speak the letter or number. We then send these values one-by-one out of the printer port, which is connected to the speech hardware.
The first six lines are comments which introduce the program. Below that are constant declarations, which substitute names for numbers throughout the rest of the source code.
The program begins at the label start. The first block of code here was taken from APSTART.O, so you wouldn't need to link it later. This code is needed because whenever an ST program starts running, it "owns" all of the ST's memory and needs to release what it doesn't need. This code measures the size of its program and data sections, adds a small amount of room for its own stack space, and then returns whatever memory is left back to GEM.
Much of the program's work is done at _main. We first point to and then display the opening message, then drop down to mainloop, where we keep an eye out for keystrokes. When one is received, we perform the subroutine decodekey and then check for more keystrokes.
Below, in decodekey, we decide which key was pressed. Paramount in importance to us is the [ESCAPE] key, which will cause a branch to terminate when it is detected. Otherwise, we determine if the key was an uppercase or lowercase letter, or a number key. If it was, we use the value of the keypress as an index into a table of addresses which points to the individual phoneme strings for each letter or number.
For example, assume a user pressed the uppercase letter B. Once we decide which key was pressed, we determine its position in the phoneme table. Since letter A is the first letter in the phoneme table, we may subtract its ASCII value (65) from the ASCII value of any other letter to determine that letter's relative position.
In this case, we're subtracting 65 (the ASCII value of A) from 66 (the ASCII value of B). The resulting value is one, which tells us that B occupies position number one in the table (A occupies position number zero).
The phoneme table is composed of 26 four-byte addresses, so we multiply our result by four (with a double left-shift). We add the resulting index to the base address of the phoneme address table to find the address of the phoneme string to speak.
This work is done in the talk_out routine. As we enter the routine, we are holding the address of the desired phoneme table in register a1, and the number of the letter or number to speak in register d0. We multiply the character number by four (with a double left-shift) and then use it as an index into the phoneme address table to find the proper address. We place this address into register d0 and reset the phoneme count by resetting register d0 to zero. Then the program falls down into talker.
Register a0 now points to the proper phoneme string to speak. Register d0 contains the index into that string, currently zero. We pick up the next available phoneme value from the phoneme table. If the number is a zero, then we have reached the end of this phoneme string and we may return.
Otherwise, we have picked up a valid phoneme value. So we branch to talk_more, which pushes valuable registers on the stack and pokes the phoneme value out the printer port. The speech board is cleverly designed to respond as a printer would, so the ST just thinks a printer is connected. Then we increment d0 to point to the next available phoneme value, and loop back to get the next character.
Next we find a table of phoneme values which comprise the default phrase spoken whenever [RETURN] is pressed. This table has each word spoken on a different line. Below them we find the table of phoneme string addresses for letters-in alphabetical order-and then the table of phoneme strings for the letters themselves. Below them we find a similar set of tables for the numbers.
Skipping over the string of bytes which make up the title message we come upon terminate, which contains code to properly exit an application and return to the GEM desktop. Next, message will accept a null-terminated-string address in register a0 and output it to the console. This routine, in turn, calls charout, which is the routine that makes the GEMDOS call to output a single character.
As the program starts closing down, we see scankey, which checks the console to see if any keys are available. If so, the character is retrieved, or else a zero is returned. Below that we find the bss (Block Storage Segment) where room for un-initialized data is kept. Here we just save room for my_stack and then END the program.