;* ======================================================================== *; ;* The routines and data in this file (ivoice.asm) are dedicated to the *; ;* public domain via the Creative Commons CC0 v1.0 license by its author, *; ;* Joseph Zbiciak. *; ;* *; ;* https://creativecommons.org/publicdomain/zero/1.0/ *; ;* ======================================================================== *; ;; ======================================================================== ;; ;; INTELLIVOICE DRIVER ROUTINES ;; ;; Written in 2002 by Joe Zbiciak ;; ;; 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 ;; ;; ;; ;; 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 ;; ;; ;; ;; 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 ;; ;; ;; ;; 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 ;; ;; ;; ;; 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_HUSH Flush the speech queue, and hush the Intellivoice. ;; ;; ;; ;; AUTHOR ;; ;; Joseph Zbiciak ;; ;; ;; ;; REVISION HISTORY ;; ;; 02-Feb-2018 Initial revision . . . . . . . . . . . J. Zbiciak ;; ;; ;; ;; INPUTS for IV_HUSH ;; ;; None. ;; ;; ;; ;; OUTPUTS ;; ;; R0 trashed. ;; ;; ;; ;; NOTES ;; ;; Returns via IV_WAIT. ;; ;; ;; ;; ======================================================================== ;; IV_HUSH PROC MVI IV.QH, R0 SWAP R0, 2 BMI IV_WAIT.leave DIS ;; We can't stop a phrase segment that's being FIFOed down. ;; We need to remember if we've committed to pushing ALD. ;; We _can_ stop new phrase segments from going down, and _can_ ;; stop new phrases from being started. ;; Set head pointer to indicate we've inserted one item. MVI IV.QH, R0 ; Re-read, as an interrupt may have occurred ANDI #$F0, R0 INCR R0 MVO R0, IV.QH ;; Reset tail pointer, keeping "need ALD" bit and other flags. MVI IV.QT, R0 ANDI #$F0, R0 MVO R0, IV.QT ;; Reset the phrase pointer, to stop a long phrase. CLRR R0 MVO R0, IV.PPTR ;; Queue a PA1 in the queue. Since we're can't guarantee the user ;; has included resrom.asm, let's just use the raw number (5). MVII #5, R0 MVO R0, IV.Q ;; Re-enable interrupts and wait for Intellivoice to shut up. ;; ;; We can't just jump to IV_WAIT.q_loop, as we need to reload ;; IV.QH into R0, and I'm really committed to only using R0. ; JE IV_WAIT EIS ; fallthrough into IV_WAIT ENDP ;; ======================================================================== ;; ;; NAME ;; ;; IV_WAIT Wait for voice queue to empty. ;; ;; ;; ;; AUTHOR ;; ;; Joseph Zbiciak ;; ;; ;; ;; REVISION HISTORY ;; ;; 15-Sep-2002 Initial revision . . . . . . . . . . . J. Zbiciak ;; ;; 02-Feb-2018 Shave cycles on presence detect . . . J. Zbiciak ;; ;; 02-Aug-2018 Wait for last phrase to complete . . . 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 CMPI #$80, R0 BC @@leave ; Wait for queue to drain. @@q_loop: CMP IV.QT, R0 BNEQ @@q_loop ; Wait for last phrase to complete. CLRR R0 @@p_loop: CMP IV.PPTR,R0 BNEQ @@p_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/ LRQ status. 1 == ready TSTR R0 BPL @@s_loop ; if bit 15 == 0, not ready. @@leave: JR R5 ENDP ;; ======================================================================== ;; ;; End of File: ivoice.asm ;; ;; ======================================================================== ;;