Ivoice.asm
From Intellivision Wiki
This file provides a set of functions to implement an Intellivoice driver. This includes initialization, updating the hardware periodically from an interrupt handler, and providing APIs to queue up voice samples to speak.
Contents |
Functions Provided
Entry point | Function provided | Notes |
---|---|---|
IV_INIT | Detect whether Intellivoice is present, and if it is, initialize it. | Returns R0=0 if Intellivoice found or R0=-1 if not. |
IV_ISR | Manage the Intellivoice | Monitors the speech buffer and command register on the Intellivoice, feeding it voice data as dictated by the speech request queue. |
IV_PLAY | Queues a phrase contained in RESROM or the IV_PHRASE_TBL to be played. | If queue is full, phrase is dropped. The Zero Flag indicates whether the phrase got queued. |
IV_PLAYW | If queue is full, the call blocks until there is room to insert the phrase. | |
IV_WAIT | Wait until phrase queue is empty | Only waits until queue is empty. To ensure Intellivoice is actually silent, 'speak' a PAUSE allophone before calling IV_WAIT. |
See source code below for calling convention.
Examples
(todo... please contribute!)
Notes
The speech driver is build around a queue of phrases to speak. This allows game events to call IV_PLAY
to trigger a phrase to be spoken, knowing it will eventually be said, without actually blocking the game. Some routines, such as saynum16.asm, queue multiple "phrases" to speak out an entire number.
This driver a bit of RAM to store its queue of pending voice requests as well as a table in ROM to describe the phrases to speak. The following table illustrates the RAM requirements.
Symbol Name | Required Width (8-bit or 16-bit) | Number of locations | Description |
---|---|---|---|
IV.QH | 8-bit | 1 | Voice queue head |
IV.QT | 8-bit | 1 | Voice queue tail |
IV.Q | 8-bit | 8 | The actual voice queue (8 entries) |
IV.FLEN | 8-bit | 1 | Length of FIFO data |
IV.FPTR | 16-bit | 1 | Current data-to-FIFO pointer |
IV.PPTR | 16-bit | 1 | Current phrase pointer in IV_PHRASE_TBL |
In addition to the RAM requirements, the driver requires a phrase table—IV_PHRASE_TABLE
—that describes different phrases to speak. The voice queue holds phrase numbers and the voice driver speaks each phrase in turn. Phrase numbers 1-42 corresponding to built-in phrases in the Intellivoice. (See resrom.asm.) The phrase table at IV_PHRASE_TABLE
is a simple table of pointers to phrases to speak. The first entry in that table corresponds to phrase #43.
The pointers in this table point to lists that contain a mix of pointers to data to send to the speech FIFO and direct references to RESROM samples. Thus, a phrase in the IV_PHRASE_TABLE
could invoke a number of RESROM fragments as well as trigger a number of voice samples to be queued in the speech FIFO. The structure is very powerful.
The following code example shows the phrase table from Tag Along Todd 2v. It incorporates RESROM samples and samples from al2.asm—the allophone library extracted from the SP-0256-AL2.
;; ======================================================================== ;; ;; IV_PHRASE_TBL -- These are phrases that will be spoken. ;; ;; ======================================================================== ;; IV_PHRASE_TBL PROC DECLE PHRASE.title DECLE PHRASE.letsplay DECLE PHRASE.gameover DECLE PHRASE.finalscor ENDP PHRASE PROC @@title DECLE _JH, _OW, RESROM.pa2 DECLE _ZZ, RESROM.pa1, _BB1, _EY, _CH, _EH, _KK1, RESROM.pa2 DECLE _PP, _RR1, _IY, _ZZ, _ZH, _EH, _NN1, _TT1, _SS, _SS DECLE RESROM.pa2 DECLE _TT2, _AX, _GG3, RESROM.pa1 DECLE _AX, _LL, _AO, _NG1, _GG2, RESROM.pa2 DECLE _TT2, _AO, _AO, RESROM.pa1, _DD1, RESROM.pa2 DECLE _TT2, _UW2, RESROM.pa2 DECLE 0 @@letsplay DECLE _LL, _EH, _EH, RESROM.pa1, _TT2, _SS, RESROM.pa2 DECLE _PP, _LL, _EH, _EY, RESROM.pa2 DECLE 0 @@gameover DECLE RESROM.pa5 DECLE _GG3, _EY, _MM, RESROM.pa2 DECLE _OW, _VV, _ER1, RESROM.pa5 DECLE 0 @@finalscor DECLE _FF, _AY, _NN2, _AX, _LL, RESROM.pa2 DECLE _SS, _SS, RESROM.pa1, _KK3, _OR, RESROM.pa3 DECLE 0 ENDP
This defines 4 phrases, with phrase numbers 43 through 46, that will be spoken during the game. As you can see, IV_PHRASE_TABLE points to the start of each of the four phrases. Each of the four phrases contains a list of references to samples contained either in the game ROM or the Intellivoice's RESROM.
Source Code
;* ======================================================================== *; ;* These routines are placed into the public domain by their author. All *; ;* copyright rights are hereby relinquished on the routines and data in *; ;* this file. -- Joseph Zbiciak, 2008 *; ;* ======================================================================== *; ;; ======================================================================== ;; ;; INTELLIVOICE DRIVER ROUTINES ;; ;; Written in 2002 by Joe Zbiciak <intvnut AT gmail.com> ;; ;; http://spatula-city.org/~im14u2c/intv/ ;; ;; ======================================================================== ;; ;; ======================================================================== ;; ;; GLOBAL VARIABLES USED BY THESE ROUTINES ;; ;; ;; ;; Note that some of these routines may use one or more global variables. ;; ;; If you use these routines, you will need to allocate the appropriate ;; ;; space in either 16-bit or 8-bit memory as appropriate. Each global ;; ;; variable is listed with the routines which use it and the required ;; ;; memory width. ;; ;; ;; ;; Example declarations for these routines are shown below, commented out. ;; ;; You should uncomment these and add them to your program to make use of ;; ;; the routine that needs them. Make sure to assign these variables to ;; ;; locations that aren't used for anything else. ;; ;; ======================================================================== ;; ; Used by Req'd Width Description ;----------------------------------------------------- ;IV.QH EQU $110 ; IV_xxx 8-bit Voice queue head ;IV.QT EQU $111 ; IV_xxx 8-bit Voice queue tail ;IV.Q EQU $112 ; IV_xxx 8-bit Voice queue (8 bytes) ;IV.FLEN EQU $11A ; IV_xxx 8-bit Length of FIFO data ;IV.FPTR EQU $320 ; IV_xxx 16-bit Current FIFO ptr. ;IV.PPTR EQU $321 ; IV_xxx 16-bit Current Phrase ptr. ;; ======================================================================== ;; ;; MEMORY USAGE ;; ;; ;; ;; These routines implement a queue of "pending phrases" that will be ;; ;; played by the Intellivoice. The user calls IV_PLAY to enqueue a ;; ;; phrase number. Phrase numbers indicate either a RESROM sample or ;; ;; a compiled in phrase to be spoken. ;; ;; ;; ;; The user must compose an "IV_PHRASE_TBL", which is composed of ;; ;; pointers to phrases to be spoken. Phrases are strings of pointers ;; ;; and RESROM triggers, terminated by a NUL. ;; ;; ;; ;; Phrase numbers 1 through 42 are RESROM samples. Phrase numbers ;; ;; 43 through 255 index into the IV_PHRASE_TBL. ;; ;; ;; ;; SPECIAL NOTES ;; ;; ;; ;; Bit 7 of IV.QH and IV.QT is used to denote whether the Intellivoice ;; ;; is present. If Intellivoice is present, this bit is clear. ;; ;; ;; ;; Bit 6 of IV.QT is used to denote that we still need to do an ALD $00 ;; ;; for FIFO'd voice data. ;; ;; ======================================================================== ;; ;; ======================================================================== ;; ;; NAME ;; ;; IV_INIT Initialize the Intellivoice ;; ;; ;; ;; AUTHOR ;; ;; Joseph Zbiciak <intvnut AT gmail.com> ;; ;; ;; ;; REVISION HISTORY ;; ;; 15-Sep-2002 Initial revision . . . . . . . . . . . J. Zbiciak ;; ;; ;; ;; INPUTS for IV_INIT ;; ;; R5 Return address ;; ;; ;; ;; OUTPUTS ;; ;; R0 0 if Intellivoice found, -1 if not. ;; ;; ;; ;; DESCRIPTION ;; ;; Resets Intellivoice, determines if it is actually there, and ;; ;; then initializes the IV structure. ;; ;; ------------------------------------------------------------------------ ;; ;; Copyright (c) 2002, Joseph Zbiciak ;; ;; ======================================================================== ;; IV_INIT PROC MVII #$0400, R0 ; MVO R0, $0081 ; Reset the Intellivoice MVI $0081, R0 ; \ RLC R0, 2 ; |-- See if we detect Intellivoice BOV @@no_ivoice ; / once we've reset it. CLRR R0 ; MVO R0, IV.FPTR ; No data for FIFO MVO R0, IV.PPTR ; No phrase being spoken MVO R0, IV.QH ; Clear our queue MVO R0, IV.QT ; Clear our queue JR R5 ; Done! @@no_ivoice: CLRR R0 MVO R0, IV.FPTR ; No data for FIFO MVO R0, IV.PPTR ; No phrase being spoken DECR R0 MVO R0, IV.QH ; Set queue to -1 ("No Intellivoice") MVO R0, IV.QT ; Set queue to -1 ("No Intellivoice") JR R5 ; Done! ENDP ;; ======================================================================== ;; ;; NAME ;; ;; IV_ISR Interrupt service routine to feed Intellivoice ;; ;; ;; ;; AUTHOR ;; ;; Joseph Zbiciak <intvnut AT gmail.com> ;; ;; ;; ;; REVISION HISTORY ;; ;; 15-Sep-2002 Initial revision . . . . . . . . . . . J. Zbiciak ;; ;; ;; ;; INPUTS for IV_ISR ;; ;; R5 Return address ;; ;; ;; ;; OUTPUTS ;; ;; R0, R1, R4 trashed. ;; ;; ;; ;; NOTES ;; ;; Call this from your main interrupt service routine. ;; ;; ------------------------------------------------------------------------ ;; ;; Copyright (c) 2002, Joseph Zbiciak ;; ;; ======================================================================== ;; IV_ISR PROC ;; ------------------------------------------------------------ ;; ;; Check for Intellivoice. Leave if none present. ;; ;; ------------------------------------------------------------ ;; MVI IV.QT, R1 ; Get queue tail SWAP R1, 2 BPL @@ok ; Bit 7 set? If yes: No Intellivoice @@ald_busy: @@leave JR R5 ; Exit if no Intellivoice. ;; ------------------------------------------------------------ ;; ;; Check to see if we pump samples into the FIFO. ;; ------------------------------------------------------------ ;; @@ok: MVI IV.FPTR, R4 ; Get FIFO data pointer TSTR R4 ; is it zero? BEQ @@no_fifodata ; Yes: No data for FIFO. @@fifo_fill: MVI $0081, R0 ; Read speech FIFO ready bit SLLC R0, 1 ; BC @@fifo_busy MVI@ R4, R0 ; Get next word MVO R0, $0081 ; write it to the FIFO MVI IV.FLEN, R0 ;\ DECR R0 ; |-- Decrement our FIFO'd data length MVO R0, IV.FLEN ;/ BEQ @@last_fifo ; If zero, we're done w/ FIFO MVO R4, IV.FPTR ; Otherwise, save new pointer B @@fifo_fill ; ...and keep trying to load FIFO @@last_fifo MVO R0, IV.FPTR ; done with FIFO loading. ; fall into ALD processing. ;; ------------------------------------------------------------ ;; ;; Try to do an Address Load. We do this in two settings: ;; ;; -- We have no FIFO data to load. ;; ;; -- We've loaded as much FIFO data as we can, but we ;; ;; might have an address load command to send for it. ;; ;; ------------------------------------------------------------ ;; @@fifo_busy: @@no_fifodata: MVI $0080, R0 ; Read LRQ bit from ALD register SLLC R0, 1 BNC @@ald_busy ; LRQ is low, meaning we can't ALD. ; So, leave. ;; ------------------------------------------------------------ ;; ;; We can do an address load (ALD) on the SP0256. Give FIFO ;; ;; driven ALDs priority, since we already started the FIFO ;; ;; load. The "need ALD" bit is stored in bit 6 of IV.QT. ;; ;; ------------------------------------------------------------ ;; ANDI #$40, R1 ; Is "Need FIFO ALD" bit set? BEQ @@no_fifo_ald XOR IV.QT, R1 ;\__ Clear the "Need FIFO ALD" bit. MVO R1, IV.QT ;/ CLRR R1 MVO R1, $80 ; Load a 0 into ALD (trigger FIFO rd.) JR R5 ; done! ;; ------------------------------------------------------------ ;; ;; We don't need to ALD on behalf of the FIFO. So, we grab ;; ;; the next thing off our phrase list. ;; ;; ------------------------------------------------------------ ;; @@no_fifo_ald: MVI IV.PPTR, R4 ; Get phrase pointer. TSTR R4 ; Is it zero? BEQ @@next_phrase ; Yes: Get next phrase from queue. MVI@ R4, R0 TSTR R0 ; Is it end of phrase? BNEQ @@process_phrase ; !=0: Go do it. MVO R0, IV.PPTR ; @@next_phrase: MVI IV.QT, R1 ; reload queue tail (was trashed above) MOVR R1, R0 ; copy QT to R0 so we can increment it ANDI #$7, R1 ; Mask away flags in queue head CMP IV.QH, R1 ; Is it same as queue tail? BEQ @@leave ; Yes: No more speech for now. INCR R0 ANDI #$F7, R0 ; mask away the possible 'carry' MVO R0, IV.QT ; save updated queue tail ADDI #IV.Q, R1 ; Index into queue MVI@ R1, R4 ; get next value from queue CMPI #43, R4 ; Is it a RESROM or Phrase? BNC @@play_resrom_r4 @@new_phrase: ADDI #IV_PHRASE_TBL - 43, R4 ; Index into phrase table MVI@ R4, R4 ; Read from phrase table MVO R4, IV.PPTR JR R5 ; we'll get to this phrase next time. @@play_resrom_r4: MVO R4, $0080 ; Just ALD it JR R5 ; and leave. ;; ------------------------------------------------------------ ;; ;; We're in the middle of a phrase, so continue interpreting. ;; ;; ------------------------------------------------------------ ;; @@process_phrase: MVO R4, IV.PPTR ; save new phrase pointer CMPI #43, R0 ; Is it a RESROM cue? BC @@play_fifo ; Just ALD it and leave. @@play_resrom_r0 MVO R0, $0080 ; Just ALD it JR R5 ; and leave. @@play_fifo: MVI IV.FPTR,R1 ; Make sure not to stomp existing FIFO TSTR R1 ; data. BEQ @@new_fifo_ok DECR R4 ; Oops, FIFO data still playing, MVO R4, IV.PPTR ; so rewind. JR R5 ; and leave. @@new_fifo_ok: MOVR R0, R4 ; MVI@ R4, R0 ; Get chunk length MVO R0, IV.FLEN ; Init FIFO chunk length MVO R4, IV.FPTR ; Init FIFO pointer MVI IV.QT, R0 ;\ XORI #$40, R0 ; |- Set "Need ALD" bit in QT MVO R0, IV.QT ;/ IF 1 ; debug code ;\ ANDI #$40, R0 ; | Debug code: We should only BNEQ @@qtok ; |-- be here if "Need FIFO ALD" HLT ;BUG!! ; | was already clear. @@qtok ;/ ENDI JR R5 ; leave. ENDP ;; ======================================================================== ;; ;; NAME ;; ;; IV_PLAY Play a voice sample sequence. ;; ;; ;; ;; AUTHOR ;; ;; Joseph Zbiciak <intvnut AT gmail.com> ;; ;; ;; ;; REVISION HISTORY ;; ;; 15-Sep-2002 Initial revision . . . . . . . . . . . J. Zbiciak ;; ;; ;; ;; INPUTS for IV_PLAY ;; ;; R5 Invocation record, followed by return address. ;; ;; 1 DECLE Phrase number to play. ;; ;; ;; ;; INPUTS for IV_PLAY.1 ;; ;; R0 Address of phrase to play. ;; ;; R5 Return address ;; ;; ;; ;; OUTPUTS ;; ;; R0, R1 trashed ;; ;; Z==0 if item not successfully queued. ;; ;; Z==1 if successfully queued. ;; ;; ;; ;; NOTES ;; ;; This code will drop phrases if the queue is full. ;; ;; Phrase numbers 1..42 are RESROM samples. 43..255 will index ;; ;; into the user-supplied IV_PHRASE_TBL. 43 will refer to the ;; ;; first entry, 44 to the second, and so on. Phrase 0 is undefined. ;; ;; ;; ;; ------------------------------------------------------------------------ ;; ;; Copyright (c) 2002, Joseph Zbiciak ;; ;; ======================================================================== ;; IV_PLAY PROC MVI@ R5, R0 @@1: ; alternate entry point MVI IV.QT, R1 ; Get queue tail SWAP R1, 2 ;\___ Leave if "no Intellivoice" BMI @@leave ;/ bit it set. @@ok: DECR R1 ;\ ANDI #$7, R1 ; |-- See if we still have room CMP IV.QH, R1 ;/ BEQ @@leave ; Leave if we're full @@2: MVI IV.QH, R1 ; Get our queue head pointer PSHR R1 ;\ INCR R1 ; | ANDI #$F7, R1 ; |-- Increment it, removing MVO R1, IV.QH ; | carry but preserving flags. PULR R1 ;/ ADDI #IV.Q, R1 ;\__ Store phrase to queue MVO@ R0, R1 ;/ @@leave: JR R5 ; Leave. ENDP ;; ======================================================================== ;; ;; NAME ;; ;; IV_PLAYW Play a voice sample sequence. Wait for queue room. ;; ;; ;; ;; AUTHOR ;; ;; Joseph Zbiciak <intvnut AT gmail.com> ;; ;; ;; ;; REVISION HISTORY ;; ;; 15-Sep-2002 Initial revision . . . . . . . . . . . J. Zbiciak ;; ;; ;; ;; INPUTS for IV_PLAY ;; ;; R5 Invocation record, followed by return address. ;; ;; 1 DECLE Phrase number to play. ;; ;; ;; ;; INPUTS for IV_PLAY.1 ;; ;; R0 Address of phrase to play. ;; ;; R5 Return address ;; ;; ;; ;; OUTPUTS ;; ;; R0, R1 trashed ;; ;; ;; ;; NOTES ;; ;; This code will wait for a queue slot to open if queue is full. ;; ;; Phrase numbers 1..42 are RESROM samples. 43..255 will index ;; ;; into the user-supplied IV_PHRASE_TBL. 43 will refer to the ;; ;; first entry, 44 to the second, and so on. Phrase 0 is undefined. ;; ;; ;; ;; ------------------------------------------------------------------------ ;; ;; Copyright (c) 2002, Joseph Zbiciak ;; ;; ======================================================================== ;; IV_PLAYW PROC MVI@ R5, R0 @@1: ; alternate entry point MVI IV.QT, R1 ; Get queue tail SWAP R1, 2 ;\___ Leave if "no Intellivoice" BMI IV_PLAY.leave ;/ bit it set. @@ok: DECR R1 ;\ ANDI #$7, R1 ; |-- See if we still have room CMP IV.QH, R1 ;/ BEQ @@1 ; wait for room B IV_PLAY.2 ENDP ;; ======================================================================== ;; ;; NAME ;; ;; IV_WAIT Wait for voice queue to empty. ;; ;; ;; ;; AUTHOR ;; ;; Joseph Zbiciak <intvnut AT gmail.com> ;; ;; ;; ;; REVISION HISTORY ;; ;; 15-Sep-2002 Initial revision . . . . . . . . . . . J. Zbiciak ;; ;; ;; ;; INPUTS for IV_WAIT ;; ;; R5 Return address ;; ;; ;; ;; OUTPUTS ;; ;; R0 trashed. ;; ;; ;; ;; NOTES ;; ;; This waits until the Intellivoice is nearly completely quiescent. ;; ;; Some voice data may still be spoken from the last triggered ;; ;; phrase. To truly wait for *that* to be spoken, speak a 'pause' ;; ;; (eg. RESROM.pa1) and then call IV_WAIT. ;; ;; ------------------------------------------------------------------------ ;; ;; Copyright (c) 2002, Joseph Zbiciak ;; ;; ======================================================================== ;; IV_WAIT PROC MVI IV.QH, R0 SWAP R0 ;\___ test bit 7, leave if set. SWAP R0 ;/ (SWAP2 corrupts upper byte.) BMI @@leave ; Wait for queue to drain. @@q_loop: CMP IV.QT, R0 BNEQ @@q_loop ; Wait for FIFO and LRQ to say ready. @@s_loop: MVI $81, R0 ; Read FIFO status. 0 == ready. COMR R0 AND $80, R0 ; Merge w/ ALD status. 1 == ready TSTR R0 BPL @@s_loop ; if bit 15 == 0, not ready. @@leave: JR R5 ENDP ;; ======================================================================== ;; ;; End of File: ivoice.asm ;; ;; ======================================================================== ;;