Sitenotice: 11/29/2018: The wiki is back. It turns out, some anti-virus product on my web server had an issue with the latest version of PHP. My server techs have resolved this issue, and things should be working again. During the investigation, I did restore to a backup from September. There is a chance that any changes done since then were lost, but I do not recall any edits. --OS-9 Al
8/30/2016: Massive re-work is being done on the InfoBox Templates. Read that page to keep up with the plan for that, and adding better keyword tags (categories) to all the pages. --OS-9 Al (talk) 15:28, 31 August 2016 (CDT)
Undercolor/840102/A RealTime Talking Clock
Home Articles Companies Publications Hardware People Software Timeline ... Emulators Internet Resources
(Don't see something listed? Click "edit" and add it! Together we can build this database. When making a new info page, refer to this InfoBox Template for guidelines.)
UnderColor, Volume 1, Number 2, December 25, 1984
- Title: A RealTime Talking Clock
- Author: Dennis Kitsz
- Synopsis: Part II of an ongoing series.
- Page Scans: Link
The idea of computer-generated speech always reminds my of my elementary school days, when Bell Laboratories amazed the world with its first talking computer. It sang Daisy in a hollow, inflectionless voice. l heard that same voice about ten years later in 2001: A Space Odyssey. Director Stanley Kubrick paid homage to that first digital talker as the errant supercomputer HAL is dismantled, board by electronic board. It first begins to lose its intelligence and other human traits; its speech becomes simpler, then mindless. Finally HAL begins singing Daisy—in that hollow, inflectionless voice. Bell Labs’ Daisy is nearly a quarter-century past. Last month I presented the concepts of digitally recording your own speech, and this month I’ve got a simple circuit that will provide you with high-quality speech with a built-in clock vocabulary. With just a little more work, a speech device with full, software-controlled inflection can be built.
At the heart of this speechmaking is the General Instrument
SPO256 vocal tract synthesizer circuit. Vocal tract synthesizers work by emulating the vocal tract— lungs, vocal cords, throat, nose, mouth, tongue and lips. Every word makes use of a significant portion of the vocal tract, which can be thought of as a tone generator, a noise generator, and a filter.
Your Personal Synthesizer
To see how your built-in generators and filters work, try this. Hum a note in the middle of your voice range—the pitch generator at work. Now sing "eeee" on that note. Change that sung "eeee" slowly to an "oooo" sound on the same note. Now stop the note and produce the unvoiced "th" sound, like you find at the end of "myth."
Hook them together slowly: "eeee" changing the "oooo" ending with "th." That’s the first step. You've sounded a note, and filtered it with your mouth as "eeee" changed to "oooo." The "th" sound is your noise generator. The next step is to change the pitch. Again, sing "eeee" changing to "oooo," but make the pitch drop as you speak, that is, slide from a higher to a lower singing note while singing "eeee- oooo." End it in "th" again; EEEE·OOooo-th.
The final step is to shorten the "eeee" sound, and put the whole thing together quickly-pitch, filtering and noise. EE·OOoooo·th. Eooth. Youth. There's the word: youth. It consists of a sliding tone, a changing filtered sound, and a bit of noise.
Even the most complex sounds can be broken down into basic chunks of sound. There are the filtered pitches such as ee, oo, oh, ah, ih, eh; noises like th, f, s, sh; and filtered voice-pitch combinations that produce b, p, m, n, l, r, ch and so forth. Pitch changes provide inflection to the words. All these pieces are called allophones, and can be emulated with electronic pitch generators, noise generators, and filters. Such electronic allophones are the basis of many speech synthesis circuits, such as the General Instrument SP0256 device that interfaces so easily with the Color Computer. lnside the SP0256 are the sound generators and filters, plus a small microprocessor which, given sets of instructions, can produce complex, intelligible sound.
lnterfaclng The Synthesizer
The SPO256-017 and its companion SPR016-117 serial speech ROM form a talking clock set; these are the most accessible speech synthesis parts, available for $10 from Radio Shack (catalog number 276-1783). The interface schematic is shown in Figure 1. It uses the CoCoPort
input/output board (Custom Color, April-May, 1983), making it compatible with disk drives and cartridges. The entire Color Computer talking clock can be constructed for under $25.
Construction is simple, using wire-wrapping or point-to-point soldering. Use sockets for all integrated circuits, and be cautious when handling the synthesizer chip and speech ROM. These are static-sensitive parts, and should be left in their packages until you are ready to put them in their sockets. Sufficient power can be
obtained from the computer itself (5 volts is found on pin 9 of the Color Computer's edge connector).
The final step is getting the clock talking. Listing 1 contains the complete interrupt—driven clock display software
presented last time, but integrated into this listing is a driver for the speech board. Have a look at the listing, beginning at the speech driver (Line 940), and the pin diagram (Figure 2).
The overall point of this series is timing, so consider that there's one important requirement for speaking, since in computer terms speaking is a very slow process: your program has to know when the last spoken word is complete. The SP0256 accepts a signal on ALD (Address Load, pin 20) to tell it to start talking, and provides the LRQ (Load Request, pin 9) to inform the host computer when its in-
put buffer is full and cannot accept any
The program in Listing 1 accommodates these special signals, and thus addresses the demands of two real-time problems: maintaining the correct real time, and keeping track of when the speech synthesizer is ready to speak. As
you can imagine, the interrupt process — with its regular, predictable, short bursts of programming — is the ideal way of making both timekeeping and speechmaking invisible to any other programs which may be running.
Turn first to the flowchart in Figure 3, which outlines the sequence needed to announce the time. The actual interrupt-driven timekeeping was described in the
first part of this project.
Talking Is Tricky
Speaking the time is second nature to
us, but it turns out to be a tricky programming task. Consider that from the number 0-20 there are unique written names for each number (zero, one, two... eighteen, nineteen, twenty), but after that only every tenth digit (thirty, forty, fifty, etc.) has a unique name. Hours only need the numbers through twelve, but minutes need the numbers through 59.
Furthermore, leading zeroes (such as 01:15) have to be quashed (you'd say one-fifteen, not oh-one-fifteen), although internal zeroes (11:03) have to be said aloud (eleven-oh-three, not eleven-three). For a 12-hour clock a.m. and p.m. need to be indicated, and exact hours (04:00, for example) have to spoken correctly (four a.m. rather than four-oh-oh a.m.). Counting anomalies have to be handled correctly; for example, the hour after 11:15 p.m., though a larger numerical value, is actually 12:15 a.m.
The original time display from last month was military time; I’ve kept that display. But I’ve decided that the spoken time is to be in ordinary a.m.-p.m. format, so 12 hours must be subtracted from any displayed hours over 12 to obtain the spoken hours: 13:17 becomes 1:17 p.m.
In other words, all options must be checked and any of several different paths might be followed for the correct expression of different times through the day. It's not difficult to organize — children do it easily — but it is tedious, boring programming.
For a run-through, look back at Listing 1. The talking clock subroutine is dormant until triggered, which happens when a full minute rolls over during the software clock interrupt service routine; this roll-over trigger is found at Line 580. (If you like, you change which time roll-over triggers the talking clock. Move the position of LBSR CLOXER from Line 580 to Line 535 for 10 second announcements; to Line 605 for 10 minute speaking; or to Line 635 for one hour timing.) At the roll-over point, the subroutine Cloxer is entered (Line 1080), which saves all registers, sets the clock-talking flag CLKON, and checks to be sure the synthesizer is ready to speak. At this time, the phrase "It is..." is triggered and the announcement begins.
The Cloxer subroutine serves a double purpose, so after the "It is..." announcement, verbal descriptions get a little hazy. If the clock is talking (that is, sound is being output and the synthesizer’s buffer is full), this subroutine returns to the main interrupt service subroutine (the time keeper), finishes its work, and resumes
normal computing in Basic. If the clock is not talking or there's still room in the buffer, the program loads the value stored at WHICH and makes an indirect jump to the program steps that properly complete the subroutine. The first time through, at the one minute roll-over, the value at Which is zero, and the phrase "It is..." is spoken.
I talked about indirect jumps in my article on the Game of Life (April, 1984), so if you're not familiar with them, review that discussion. In general, the Which value provides an offset to any one of four program segments: routine It is, which speaks the words "It is..."; routine Hours, which announces the current hour; routine Minutes, which announces the current minute; and routine Ampm, which speaks the phrase "a.m." or "p.m." Separate routines are needed because the speech synthesizer’s buffer isn't large enough to handle an entire time phrase such as "It is eleven forty-five p.m." at
once. Naturally, the program can't wait for the speech to be completed, or one of the values of a fast, interrupt-driven subroutine would be sacrificed. A companion need for the individual subroutine segments is that, after having triggered the speech synthesizer to speak each phrase and return to the main program, the routine has to be able to continue
where it left off. Otherwise, it might find itself saying "It is... it is... it is... it is... it is..."
Once the indirect jump JMP <A,Y> is made to one of the four possible program branches from Line 1210, the program is off and running according to the flowchart in Figure 3.
Recall that I mentioned the main subroutine is first triggered by the one-minute roll-over when it enters the service routine Cloxer, and begins the phrase "It is...." Back at Line 950 are three indicators: the Ampm flag for a.m. and p.m., the value Which to identify that the speaking routine is in progress, and the flag CLKON to define whether or not the clock is presently announcing.
Once the announcing starts, however, the interrupt service routine is subsequently handles by Line 990 — before
Cloxer — where the routine CLKTST is found. CLKTST evaluates announcement-in-progress flag CLKON every tenth of a second (branching from Line 470). If the synthesizer is not in the midst of announcing, CLKTST merely returns to the main interrupt service routine. If the clock is supposed to be talking — which could only have been triggered by the execution of Cloxer at the one-minute rollover — the CLKTST routine executes Cloxer.
But this time the circumstances are different. Cloxer would already have done some work (the Itis routine) and would be
ready to go on to the other speaking roles (hours, minutes, am/pm). By the way, the complete set of words and phrases
available to the SP0256-017/SPR16-117 speech synthesis pair is shown in Table 1. The multiple use of Cloxer is tricky and doesn't lent itself to quick descriptive summaries. Fortunately, it’s not necessary to understand my logic in retail unless you want to modify or expand my program. In summary, the interrupt service routine executes CLKTST to check (every 1/10 second) for clock synthesizer announcements in progress. Usually nothing happens, but at the roll-over of one minute, the interrupt service routine executes Cloxer to say "It is..." and to spark the clock announcement process into action. The CLKON flag is set. After the announcement is begun and when the synthesizer buffer becomes available, CLKTST continues to execute Cloxer and its four indirectly accessed subroutines until all the synthetic speaking is complete. Finally, the CLKON flag is turned off until the next one minute roll-over triggers the process anew.
Using The Program
Enter the source code in Listing 1 using an editor/assembler. Save the source to tape (W TALKCLOK), assemble the program to tape (A CLOXER), and turn the computer off. Remove the Edtasm cartridge, insert the talking clock board, turn the machine on, protect memory (CLEAR 200,&H3E00), load the assembled program from tape (CLOADM "CLOXER), and execute it (EXEC&H3E00). The program will patch into the interrupt routine, the clock will be displayed on the screen, and the clock will begin announcing each minute.
You'll want to set the clock, which will initialize to 00:00:0.0. Load and execute the short Basic time—setting program in
Listing 2. You can now use the talking clock with your Basic programs and it will provide a display and audible output for
the correct time of day until you turn the machine off, or use program cartridges or assembly language programs which
occupy the same memory area as the clock software. 32K and 64K computer users can relocate the clock in other areas of memory as needed.
Note that if you press the Reset button the peripheral interface adaptor on the clock board will be reset as well. Use EXEC&H3E00 to restore the operation of the
The SND Input
As shown in the schematic, the output of the speech synthesizer feeds the input of an external amplifier and speaker. It can, however, by made to sound through
your television speaker. Connect the output of the speech synthesizer to pin 35 of the Color Computer's edge connector. Then enter the following line:
X = &HFF00:POKEX+1,PEEK(X+1)AND247:POKE X+3,PEEK(X+3)OR 8:POKE X+35) OR 8
The sound will now feed through to the television speaker. This sound input is useful whenever you want to mix sound from an external device with the sound from the computer. For example, other kinds of speech and sound boards, analog input devices, and so forth, can be fed through the SND input. The Basic Sound and Input/Output commands and the Reset button reset the original sound conditions, though, so you'll have to make this subroutine a part of any programming that is to mix external input through SND.
Next: More time! A battery backed-up real-time clock using a brand new ten-year timer from National Semiconductor, plus an interface for the SP0256·AL2 allophone·based speech synthesizer to give a full range of vocabulary and inflection. I'll wrap this series up with a talking clock with battery back·up. (end)
Editor's Note: Dennis has been very busy
these last few weeks on an exciting new project he'll call the Data Gatherer. It's a data
acquisition and control system with, among
other things, 12-bit A/D conversion; 12-bit D/A
conversion; a real-time clock calendar with
battery back-up; a 10-bit parallel port; and
with operating system in ROM. We’re going
to take a break from the Real-Time Talking
clock next month to present Part I of the Data
Gatherer. As soon as Dennis gets the excitement out of his system, we'll go back to
the Talking Clock!
Program Listing 1. Complete assembly language listing to maintain a software real-time clock with synthesized speech time. 3E00 00100 ORG $3E00 00110 * FF52 00120 VOICE EQU $FF52 00130 * 3E00 1A 50 00140 INTOFF ORCC #$50 * TURN INTERRUPTS OFF 3E02 BE 3E33 00150 LDX #START * POINT X TO SERVICE ROUTINE 3E05 BF 0100 00160 STX $010D * STORE ROUTINE TO IRQ VECTOR 3E08 86 37 00170 LDA #$37 * VALUE 00110111 FOR MASKING 3E0A B7 FF03 00180 STA $FE03 * TURN ON VERTICAL SYNC 3E00 8E FF50 00190 LDX #VOICE-2 * POINT TO PORT OUPUTS 3E10 4F 00200 CLRA * PREPARE TO OPEN PORT 3E11 A7 01 00210 STA 1,X * OPEN PORT A 3E13 86 39 00220 LDA #$39 * PREPARE DIRECTION 3E15 A7 84 00230 STA ,X * SET SPEECH I/O BITS 3E17 86 04 00240 LDA #$04 * PREPARE TO CLOSE 3E19 A7 01 00250 STA 1,X * CLOSE PORT A 3E1B 4F 00260 CLRA * PREPARE TO OPEN B 3E1C A7 03 00270 STA 3,X * OPEN PORT B 3E1E 4A 00280 DECA * PREPARE ALL OUTPUTS 3E1F A7 02 00290 STA 2,X * SET ALL BITS OUTPUT 3E21 86 O4 00300 LDA #$04 * PREPARE TO CLOSE B 3E23 A7 03 00310 STA 3,X * CLOSE PORT B 3E25 86 20 00320 LDA #$20 * PREPARE TO RESET 3E27 A7 84 00330 STA ,X * RESET SPEECH DEVICE 3E29 86 38 00340 LDA #$38 * CLEAR SPEECH BUFFER 3E2B A7 B4 00350 STA ,X * SEND CLEARING PULSE 3E2D 4C 00360 INCA * READY FOR RECEIVING 3E2E A7 84 00370 STA ,X * SET RECEIVING PULSE 3E3D 1C EF 00380 ANDCC #$EF * INTERRUPTS BACK ON 3E32 39 00390 RTS * AND BACK TO BASIC "OK" 00400 * 3E33 8E 3EA9 00410 START LDX #IMAGE+10 * POINT X TO 1/10 SEC. 3E36 C6 30 00420 LDB #$30 * B BECOMES ASCII OFFSET 3E38 6C 84 00430 INC ,X * INCREMENT 1/10 SECONDS 3E3A A6 84 00440 LDA ,X * GET 1/10 SECONDS VALUE 3E3C 81 36 00450 CMPA #$36 * IS 6/10 SECONDS COUNTED? 3E3E 20 3E 00460 BLT OUT * IF NOT 6/10 SECONDS, OUT 3E40 80 6B 00470 BSR CLKTST * SEE IF CLOCK TALKING 3E42 80 40 00480 BSR DEC1 * ELSE BAC UP 1 MEM. LOCATION 3E44 81 3A 00490 CMPA #$3A * IS IT 1 SECOND YET? 3E46 20 36 00500 BLT OUT * IF NOT 1 SECOND, OUT 3E4B 80 4E 00510 BSR DEC2 * ELSE BACK UP 2 MEM. LOCNS. 3E4A 81 3A 00520 CMPA #$3A * IS IT 10 SECONDS YET? 3E4C 2D 30 00530 BLT OUT * IF NOT 10 SECONDS, OUT 3E4E B0 41 00540 BSR DEC1 * BACK UP 1 MEM. LOCATION 3E50 81 36 00550 CMPA #$36 * IS IT 60 SECONDS YET? 3E52 20 2A 00560 BLT OUT * IF NOT 60 SECONDS, OUT 3E54 80 42 00570 BSR DEC2 * ELSE BACK UP 2 MEM. LOCNS. 3E56 17 0060 00580 LBSR CLOXER 3E59 81 3A 00590 CMPA #$3A * IS IT 10 MINUTES YET? 3E5B 20 21 00600 BLT OUT * IF NOT 10 MINUTES, OUT 3E5D 8D 32 00610 BSR DEC1 * ELSE BACK UP 1 MEM. LOCATION 3E5F 81 36 00620 CMPA #$36 * IS IT 60 MINUTES YET? 3E61 20 1B 00630 BLT OUT * IF NOT 60 MINUTES, OUT 3E63 80 33 00640 BSR DEC2 * ELSE BACK UP 2 MEM. LOCNS. 3E65 81 35 00641 CMPA #$35 * IS IT 5 HOURS? 3E67 26 0D 00642 BNE NOT24 * IF NOT, TEST FOR 10 3E69 A6 1F 00643 LDA -1,X * GET NEXT LOCATION 3E6B 81 32 00644 CMPA #$32 * IS IT 25 HOURS? 3E6D 26 0F 00645 BNE OUT * IF NOT, THEN OUT 3E6F E7 1F 00646 STB -1,X * MAKE U5 HOURS 3E71 5C 00647 INCB * GET VALUE "1" 3E72 E7 84 00648 STB ,X * MAKE 01 HOURS 3E74 20 08 00649 BRA OUT AND BE DONE WITH IT 3E76 B1 3A 00650 NOT24 CMPA #$3A * IS IT 10 HOURS YET? 3E78 20 04 00660 BLT OUT * IF NOT 10 HOURS, OUT 3E7A E7 84 00661 STB ,X * PLACE #$30 (ASCII ZERO) 3E7C 6C 82 00662 INC ,-X * BACK UP ONE MEM. LOCATION 00710 * 3E7E 108E 0416 00720 OUT LDY #$0416 * POINT TO RIGHT SCREEN 3E82 8E 3E9F 00730 LDX #IMAGE * POINT X TO CLOCK IMAGE 3E85 C6 0A 00740 LDB #$0A * COUNT 10 SCREEN POSITIONS 3E87 A6 80 00750 LOOP LDA ,X+ * GET CHARACTER FROM CLOCK 3E89 A7 A0 00760 STA ,Y+ * AND PLACE IT ON THE SCREEN 3E8B 5A 00770 DECB * DONE WITH IMAGE YET? 3E8C 26 F9 00780 BNE LOOP * IF NOT, THEN GET NEXT CHAR. 00790 * 3E8E 7E 894C 00810 JMP $894C * AND T0 8ASIC TO DO RTI 00820 * 3591 E7 84 00830 DEC1 STB ,X * PLACE $30 (ASCII ZERO) 3E93 6C 82 00840 INC ,-X * BACK UP ONE MEM. LOCATION 3E95 A6 84 00850 LDA ,X * GET VALUE FROM IMAGE 3597 39 00860 RTS * 8ACK T0 MAIN PROGRAM 00870 * 3E98 E7 84 00880 DEC2 STB ,X * PLACE $30 (ASCII ZERO) 3E9A 6C 83 00890 INC ,--X * BACK UP TWO MEM. LOCATIONS 3E9C A6 84 00900 LDA ,X * GET VALUE FROM IMAGE 3E9E 39 00910 RTS * BACK TO MAIN PROGRAM 00920 * 3E9F 31 00930 IMAGE FCC /11:59:59.00/ 31 3A 35 39 3A 35 39 2E 30 30 00940 * 3EAA 00 00950 AMFLAG FCB $00 * 00 = AM, 01 = PM 3EAB 00 00960 WHICH FCB $00 * SAYING WHICH? 3EAC 00 00970 CLKON FCB $00 * 00 = MUTE, 01 = TALKING 00980 * 3EAD 34 02 00990 CLKTST PSHS A * SAVE A REGISTER 3EAF 86 3EAC 01000 LDA CLKON * GET TALKING STATUS 35B2 27 02 01001 BEQ NOTYET * IF ZERO, NOT TALKING 3EB2 80 03 01000 DOTALK BSR CLOXER * READY T0 SPEAK TIME 35B6 35 02 01050 NOTYET PULS A * WHEN DONE RESTORE A 3EB8 39 01060 RTS * AND BACK TO INT. CLOCK 01070 * 3E89 36 36 01080 CLOXER PSHS A,B,X,Y * SAVE ALL IN SIGHT 3EB8 86 01 01090 LDA #1 * SET CLOCK IN PROGRESS 3EB0 87 3EAC 01100 STA CLKON * AND PUT INT0 FLAG 3EC0 86 FF50 01110 LDA VOICE-2 * POINT TO CONTROL PORT 3EC3 81 58 01120 CMPA #$F8 * AND SEE IF READY 3EC5 27 03 01130 BEQ OKAY * IF $FB, IT'S READY 3EC7 35 36 01140 PULS A,B,X,Y * ELSE RESTORE EVERYTHING 3EC9 39 01150 RTS * AND BACK TO INT. CLOCK 01160 * 3ECA 108E 3ED7 01170 OKAY LDY #SPEAK * POINT TO ROUTINES 3ECE 8E 3595 01180 LDX #IMAGE * POINT TO CLOCK DIGITS 3E01 36 3EA8 01190 LDA WHICH * FIND WHICH ROUTINE 3E04 A8 01200 ASLA * 2-BYTE ADDRESS OFFSET 3E05 65 B6 01210 JMP [A,Y] * AND GO TO THE ROUTINE 01220 * 3ED7 3EDF 01230 SPEAK FDB ITIS * ROUTINE EOR "IT IS" 3E09 3EEA 01240 FDB HOURS * ROUTINE SAYS HOUR 3E08 3532 01250 FDB MINUTE * ROUTINE SAYS MINUTE 3EDD 3F66 01260 FDB AMPM * ROUTINE SAYS "AM" OR "PM" 01270 * 3EDF 86 18 01280 ITIS LDA #24 * CET VOICE VALUE "IT IS" 3EE1 17 0098 01290 LBSR TALKER * AND SPEAK IT 3EE4 7C 3EA8 01300 INC WHICH * POINT TO NEXT ROUTINE 3EE7 35 36 01310 PULS A,B,X,Y * RESTORE ALL REGISTERS 3EE9 39 01320 RTS * BACK TO INT. CLOCK 01330 * 3EEA A6 00 01340 HOURS LDA 0,X * GET FIRST HOUR DIGIT 3EEC 80 30 01350 SUBA #$30 * STRIP ASCII OFESET 3EEE 27 2C 01360 BEQ HOUR0 * GO IF 00 - 09 HOURS 3EF0 4A 01370 DECA * DECREMENT TO TEST 3EF1 27 00 01380 BEQ HOUR1 * GO IF 10 — 19 HOURS 3E?3 A6 01 01390 LDA 1,X * ELSE IS 2; GET NEXT 3EF5 80 28 01400 SUBA #40 * NUMBER JUGGLING ***** 3EF7 17 0082 01410 LBSR TALKER * AND SPEAK THE VALUE 3EFA 81 0C 01420 CMPA #12 * SEE IF 12 O'CLOCK 3EFC 27 24 01430 BEQ SETAM * IF IT IS, THEN 12 AM 3EFE 20 27 01440 BRA SETPM * ELSE THEN IS PM 3F00 A6 01 01450 HOUR1 LDA 1,X * HOUR IS 1-9; GET NEXT 3F02 80 30 01460 SUBA #$30 * STRIP ASCII OFFSET 3F04 81 02 01470 CMPA #2 * CHECK IF 12 O`CLOCK 3F06 22 0E 01480 BHI NIGHT * IF HIGHER, THEN PM 3F08 27 06 01490 BEQ TWELVE * ELSE IS EXACTLY 12 3F0A 8B 0A 01500 ADDA #10 * ELSE JUGGLE FOR VOICE 3F0C 8D 6E 01510 BSR TALKER * AND SPEAK THE VALUE 3F0E 20 12 01520 BRA SETAM * AND SET THE MORNING 3F10 86 0C 01530 TWELVE LDA #12 * IF 12 THEN GET IT 3F12 8D 68 01540 BSR TALKER * AND SPEAK THE VALUE 3F14 20 11 01550 BRA SETPM * AND SET IT TO BE PM 3F16 80 02 01560 NIGHT SUBA #2 * ELSE IS NIGHT; JUGGLE 3F18 8D 62 01570 BSR TALKER * AND SPEAK THE VALUE 3F1A 20 0B 01580 BRA SETPM * AN0 SET IT AS PM 3F1C A6 01 01590 HOURO LDA 1,X * IF 0-9 HOURS, GET NEXT 3F1E 80 30 01600 SUBA #$30 * STRIP ASCII OFFSET 3F20 8D 5A 01610 BSR TALKER * ANU SPEAK THE VALUE O 3F22 7F 3EAA 01620 SETAM CLR AMFLAG * ROUTINE SETS AM FLAG 3F25 20 05 01630 BRA GOHOUR * AND GOES ON OUT 3F27 86 01 01640 SETPM LDA #1 * ROUTINE SETS PM FLAG 3F29 B7 3EAB 01650 STA AMFLAG * AND PUTS IN PLACE 3F2C 7C 3EAB 01660 GOHOUR INC WHICH * POINT TO NEXT ROUTINE 3F2F 35 36 01670 PULS A,B,X,Y * RESTORE ALL REGISTERS 3F31 39 01680 RTS * BACK T0 INT. CLOCK 01690 * 3F32 A6 03 01700 MINUTE LDA 3,X * GET FIRST MINUTE DIGIT 3F34 80 30 01710 SUBA #$30 * STRIP ASCII OFFSET 3F36 27 19 01720 BEQ MIN1X0 * IF :00 TO :09, GO 3F38 4A 01730 DECA * DECREMENT FOR TEST 3F39 27 0E 01740 BEQ MIN1X1 * IF :10 TO :19, GO 3F3B 8B 13 01750 ADDA #19 * ELSE GET VOICE OFFSET 3F3D 8D 3D 01760 BSR TALKER * SPEAK :20 :30 :40 :50 3F3F A6 04 01770 LDA 4,X * GET LAST MINUTE 3F41 80 30 01780 SUBA #$30 * STRIP ASCII OFFSET 3F43 27 1B 01790 BEQ GOMIN * IF :X0, THEN GO 3F45 8D 35 01800 BSR TALKER * ELSE SPEAK THE MINUTE 3F47 20 17 01810 BRA GOMIN * AND GO ON OUT 3F49 A6 04 01820 MIN1X1 LDA 4,X * IF :10 TO :19, GET NEXT 3F4B 80 26 01830 SUBA #38 * JUGGLE FOR VOICE 3F4D 8D 2D 01840 BSR TALKER * AND SPEAK THE VALUE 3F4F 20 0F 01850 BRA GOMIN * FINALLY GOING OUT 3F51 A6 04 01860 MIN1X0 LDA 4,X * IF :00 TO :09, GET NEXT 3F53 80 30 01870 SUBA #$30 * STRIP ASCII OFFSET 3F55 27 09 01880 BEQ GOMIN * IF :00, GO OUT SILENTLY 3F57 34 02 01890 PSHS A * STASH A VALUE 3F59 4F 01900 CLRA * GET READY A ZERO 3F5A 8D 20 01910 BSR TALKER * AND MAKE IT SAY "OH" 3F5C 35 02 01920 PULS A * RESTORE LAST MINUTE 3F5E 8D 1C 01930 BSR TALKER * AND SPEAK THE MINUTE 3F60 7C 3EAB 01940 GOMIN INC WHICH * POINT TO NEXT ROUTINE 3F63 35 36 01950 PULS A,B,X,Y * RESTORE ALL REGISTERS 3F65 39 01960 RTS * AND BACK TO INT. CLOCK 01970 * 01970 * 3F66 B6 3EAA 01980 AMPM LDA AMFLAG * GET AM—PM FLAG VALUE 3F69 27 04 01990 BEQ MORN * IF 0, THEN IT'S AM 3F6B 86 1A 02000 LDA #26 * IF 1, THEN GET PM VALUE 3F6D 20 02 02010 BRA GOTIME * AND GO OUT OF ROUTINE 3F6F 86 19 02020 MORN LDA #25 * IF 0, THEN GET AM VALUE 3F71 8D 09 02030 GOTIME BSR TALKER * SPEAK "AM" OR "PM" 3F73 7F 3EAB 02040 CLR WHICH * CLEAR ROUTINE POINTER 3F76 7F 3EAC 02050 CLR CLKON * CLEAR CLOCK ON POINTER 3F79 35 36 02060 PULS A,B,X,Y * RESTORE ALL REGISTERS 3F7B 39 02070 RTS * BACK TO INT. CLOCK 02080 * 3F7C B7 FE52 02090 TALKER STA VOICE * STORE VALUE TO SPEAK 3F7F 34 02 02100 PSHS A * SAVE THE VALUE 3F81 86 38 02110 LDA #$38 * GET VALUE FOR LOW PULSE 3F83 B7 FF50 02120 STA VOICE-2 * PULSE VOICE TO ACCEPT 3F86 4C 02130 INCA * GET VALUE FOR HI PULSE 3F87 B7 FF50 02140 STA VOICE-2 * PULSE VOICE TO READY 3F8A 35 02 02150 PULS A * GET VOICE VALUE BACK 3F8C 39 02160 RTS * BACK TO TALK ROUTINE 02170 * 3EOO 02180 END INTOFF 00000 TOTAL ERRORS AMFLAG 3EAA GOHOUR 3F2C ITIS 3EDF NOTYET 3EB6 TWELVE 3F10 AMPM 3F66 GOMIN 3F60 LOOP 3E87 OKAY 3ECA VOICE FF52 CLKON 3EAC GOTIME 3F71 MIN1X0 3F51 OUT 3E7E WHICH 3EAB CLKTST 3EAD HOUR0 3F1C MIN1X1 3F49 SETAM 3F22 CLOXER 3EB9 HOUR1 3F00 MINUTE 3F32 SETPM 3F27 DEC1 3E91 HOURS 3EEA MORN 3F6F SPEAK 3ED7 DEC2 3E98 IMAGE 3E9F NIGHT 3F16 START 3E33 DOTALK 3EB4 INTOFF 3E00 NOT24 3E76 TALKER 3F7C Program Listing 2. Basic program to set the time for the program in Listing 1. Once the time is set, this program may be deleted. 1 REM * TIME SETTING PROGRAM 2 REM * FOR INTERRUPT-DRIVEN 3 REM * VOICE CLOCK (ONLY). 4 CLS S PRINT:PRINT 6 PRINT"ENTER THE TIME IN THE FORMAT:" 7 PRINT"00:00:00.0" 8 PRINT 9 PRINT"NOTE: USE 24-HOUR TIME." 10 PRINT:PRINT 11 LINEINPUTA$ 12 IFLEN(A$)<>10THENRUN 13 FORX=1TO10:A$(X)=MID$(A$,X,1):NEXT 14 IFA$(3)<>":"ORA$(6)<>":"ORA$(9)<>" ."THENRUN 15 Q$=A$(1):GOSUB30 16 Q$=A$(2):GOSUB30 17 Q$=A$(4):GOSUB30 18 Q$=A$(5):GOSUB30 19 Q$=A$(7):GOSUB30 20 Q$=A$(8):GOSUB30 21 Q$=A$(10):GOSUB30 22 FORX=1TO10 23 POKE16030+X,ASC(MID$(A$,X,1)) 24 NEXT 25 PRINT:PRINT"TIME SET" 26 FORX=lTO1000:NEXT 27 CLS 28 END 29 STOP 30 IFQ$<"0"ORQ$>"9"THENRUNELSERETURN Program Listing 3. A Basic clock program to demonstrate the use of the voice synthesizer. 10 INPUT"HOUR";H 20 INPUT"MINUTE";H 30 A=&HFF50:B=A+1:C=B+1:D=C+1 60 POKEB,0:POKEA,&H39:POKEB,4 50 POKED,0:POKEC,&HFF:POKED,4 60 T=&H38:U=&H39 70 POKEA,&H2O 80 TIMER=0 90 IFTIMER>55THEN100ELSE90 100 S=S+1:IFS=60THENS=O:M=M+1:GOSUB120:IFM=6OTHENM=0:H=H+1:IFH=1 3THENH=1 110 GOTO80 120 POKEC,24 130 GOSUB200 160 POKEC,H:GOSUB200 150 IFM<10THENPOKEC,0:COSUB200 160 IFM<21THENPOKEC,M:GOSUB200:GOTO180 170 IFM>20THENPOKEC,l8+INT(H/10):GOSUB200:IFM-(INT(M/10))*10=0THEN180ELSEPOKEC,M-(INT(M/10))*10:GOSUB200 180 POKEC,26:GOSUB200 190 RETURN 200 POKEA,T:POKEA,U 210 IFPEEK(A)=253THEN210ELSERETURN