Ivoice.asm
From IntelliWiki
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 ;;
;; ======================================================================== ;;

