{ File: [Iris]DLion>VoiceTask.asm Modification History: Dennis DEG : 1-Sep-84 17:18:53 Added copyright notice. Dennis DEG : 7-Jun-83 9:40:54 Dennis DEG : 14-Apr-83 8:52:33 variable baud rates for slow link. Dennis DEG : 28-Jan-83 11:58:18 Jim JXF : October 21, 1982 10:55 AM Created by Jim JXF : 21-Sep-82 13:20:50 } { Copyright (C) 1982, 1983 by Xerox Corporation. All rights reserved.} Get "SysDefs" Get "CommonDefs" IMP ClearDmaChannel ; From DmaSubs IMP DoNakedNotify ; From CPSubs IMP DmaActive ; From DmaSubs IMP MainLoop ; From BookKeepingTask IMP PortBusyFlag ; From CPSubs IMP ReadCPBuffer ; From CPsubs IMP RS232CInterruptSwitch ; From Common IMP RS232CMiscTask IMP StartCPReadDMA ; From CPsubs IMP CheckCPDmaComplete ; From CPsubs IMP VoiceCommand ; From BookKeepingTask IMP VoiceSwitch ; From RS232CMiscTask IMP WriteCPBuffer ; From CPsubs IMP SpeechOutBuffer0 ; From Buffer IMP SpeechOutBuffer1 ; From Buffer IMP SpeechInBuffer0 ; From Buffer IMP SpeechInBuffer1 ; From Buffer IMP EnableRST ; From Common IMP DisableRST ; From Common EXP VoiceCommandTask ; VOICE BOX DEFINITIONS (to go in SysDefs) VoiceStatusRegister EQU 0C0H VoiceControlRegister EQU 0C0H VoiceResetRegister EQU 0C2H VoiceRxReady EQU 80H VoiceTxReady EQU 40H VoiceUartControl EQU 0CFH VoiceUartData EQU 0CEH VoiceUartStatus EQU 0CFH VoiceInternalReset EQU 40H VoiceTxEnable EQU 1 VoiceRxEnable EQU 4 EnableFrameSync EQU 20H EnableSpeechIn EQU 80H EnableSpeechOut EQU 40H EnableSpeechLoopBack EQU 10H SpeechBaudRate300 EQU 0 SpeechBaudRate600 EQU 4 SpeechBaudRate1200 EQU 8 SpeechBaudRate2400 EQU 0CH ; VOICE BOX DEFINITIONS (to go in CommonDefs) VoiceCSBLoc EQU 0FF66H VoiceCommandBufferLoc EQU 0FF6FH VoiceCommandLoc EQU 0FF18H ;same as RS232CMiscFlagLoc VoiceBoxMessageLoc EQU 0FF65H VoiceGetCommandLoc EQU 0FF1AH ;same as RS232CGetFlagLoc VoicePutCommandLoc EQU 0FF19H ;same as RS232CPutFlagLoc ; ; VOICE BOX DEFINITIONS (for internal use) ClearSpeechInterruptFF EQU 0C3H KnownMode EQU 80H NumberOfCommands EQU 11 WriteDma EQU 40H ReadDma EQU 80H EnSODma EQU 8H EnSIDma EQU 4H TerminalCountStop EQU 40H SpeechChannels EQU EnSODma+EnSIDma SOChannelMask EQU EnSODma+TerminalCountStop SIChannelMask EQU EnSIDma+TerminalCountStop SpeechDmaIntr EQU 2 TwoTimesNumberOfCommands EQU NumberOfCommands+NumberOfCommands VoiceBoxMode EQU 4EH ;Async, 1 stop bit, no parity, 8 bit length, 16x Rst65Mask EQU 2 VoiceCommandTask: {This task executes commands specified by VoiceCommand: StartVoiceBox, StopVoiceBox, VoiceBoxCommand.} {Whenever we start a DMA operation we modify this entry point to check for a DMA complete. Otherwise we just continue with the StartVoiceCommandTask} DB opJMP VoiceCommandContinueAddress: DW StartVoiceCommandTask ;either ; JMP WaitForCommandString ;or ; JMP StartVoiceCommandTask StartVoiceCommandTask: LDA PortBusyFlag ORA A JNZ VoiceCommandTaskYield DB opJMP VoiceCommandTaskResumeAddress: DW BeginVoiceCommandTask ; JMP SendVoiceBoxCommandCharacters VoiceCSB: NakedMask: DW 0 GetByteCount: DW 0 PutByteCount: DW 0 GetBufferPtrLo: DB 0 GetBufferPtrMid: DB 0 GetBufferPtrHi: DB 0 DB 0 ;not used GetBufferByteCount: DW 0 PutBufferPtrLo: DB 0 PutBufferPtrMid: DB 0 PutBufferPtrHi: DB 0 DB 0 ;not used PutBufferByteCount: DW 0 VoiceCSBSize: EQU 18 VoiceCommandBuffer: DS 10 DB 0 ;high order of CommandByteCount CommandByteCount: DB 0 VoiceCommandSize: EQU 12 ResetCommand: DB 0 VoiceStatus: DB 0 ;0 = success { 4 = overrun 8 = parity error 10 = overflow error 20 = frame error 40 = break 80 = good completion } CharacterReceivedFlag: DB 0 ; = 80H if CP has not processed character VoiceBoxCharacter: DB 0 ;character received from VoiceBox GetStatus: DW 0 GetStatusHighByte EQU GetStatus+1 PutStatus: DW 0 PutStatusHighByte EQU PutStatus+1 ; PCB to read Voice CSB PCBReadVoiceCSB: DW VoiceCSBLoc ;CP Buffer Pointer(Low) DW CPIOPageHi ;CP Buffer Pointer(Hi) DW VoiceCSBSize ;CP Buffer Count (Bytes) DW VoiceCSB ;Pointer to IOP Buffer ; PCB to read VoiceCommandBuffer and the CommandByteCount PCBReadVoiceBoxCommand: DW VoiceCommandBufferLoc ;CP Buffer Pointer(Low) DW CPIOPageHi ;CP Buffer Pointer(Hi) DW VoiceCommandSize ;CP Buffer Count (Bytes) DW VoiceCommandBuffer ;Pointer to IOP Buffer ; PCB to Reset Voice Command and return Status in low byte PCBResetVoiceCommand: DW VoiceCommandLoc ;CP Buffer Pointer(Low) DW CPIOPageHi ;CP Buffer Pointer(Hi) DW 2 ;CP Buffer Count (Bytes) DW ResetCommand ;Pointer to IOP Buffer ; PCB to read and write VoiceBoxMessage PCBVoiceBoxMessage: DW VoiceBoxMessageLoc ;CP Buffer Pointer(Low) DW CPIOPageHi ;CP Buffer Pointer(Hi) DW 2 ;CP Buffer Count (Bytes) DW CharacterReceivedFlag ;Pointer to IOP Buffer ; PCB to read/write getCommand.status PCBGetStatus: DW VoiceGetCommandLoc ;CP Buffer Pointer(Low) DW CPIOPageHi ;CP Buffer Pointer(Hi) DW 2 ;CP Buffer Count (Bytes) DW GetStatus ;Pointer to IOP Buffer ; PCB to read/write putCommand.status PCBPutStatus: DW VoicePutCommandLoc ;CP Buffer Pointer(Low) DW CPIOPageHi ;CP Buffer Pointer(Hi) DW 2 ;CP Buffer Count (Bytes) DW PutStatus ;Pointer to IOP Buffer ; PCB to get Digital Speech input data PCBSpeechIn: DW 0 ;CP Buffer Pointer(Low) DW 0 ;CP Buffer Pointer(Hi) DW 0 ;CP Buffer Count (Bytes) DW 0 ;Pointer to IOP Buffer ; PCB to put Digital Speech output data PCBSpeechOut: DW 0 ;CP Buffer Pointer(Low) DW 0 ;CP Buffer Pointer(Hi) DW 0 ;CP Buffer Count (Bytes) DW 0 ;Pointer to IOP Buffer SavedVoiceUartStatus: DB 0 SOBuffer: DW SpeechOutBuffer0 SOBufferNext: DW SpeechOutBuffer1 SpeechBaudRate: DB SpeechBaudRate1200 ; SODMAStart: LHLD PCBSpeechOut+4 DCX H MOV A,L OUT DmaCh3Count MOV A,H ORI ReadDma OUT DmaCh3Count LHLD PCBSpeechOut+6 MOV A,L OUT DmaCh3Addr MOV A,H OUT DmaCh3Addr LDA DmaActive MOV H,A XRA A OUT DmaMode IN DmaStatus CMA ANA H ORI SOChannelMask OUT DmaMode STA DmaActive LDA VoiceControllerMode ORI EnableSpeechOut STA VoiceControllerMode OUT VoiceControlRegister RET SIDMAStart: LHLD PCBSpeechIn+4 DCX H MOV A,L OUT DmaCh2Count MOV A,H ORI ReadDma OUT DmaCh2Count LHLD PCBSpeechIn+6 MOV A,L OUT DmaCh2Addr MOV A,H OUT DmaCh2Addr LDA DmaActive MOV H,A XRA A OUT DmaMode IN DmaStatus CMA ANA H ORI SIChannelMask OUT DmaMode STA DmaActive LDA VoiceControllerMode ORI EnableSpeechIn STA VoiceControllerMode OUT VoiceControlRegister RET BeginVoiceCommandTask: {When we come here, we know the CP port is free.} {We will now compute the address of the routine to execute and then branch to that routine} LDA VoiceCommand ADD A ;Multiply command by 2 JZ VoiceCommandTaskYield ;Added to make idle loop faster CPI TwoTimesNumberOfCommands ;{Something fishy about EQU Statements} JP IllegalVoiceCommand LXI H,CommandJumpTable ;Get base address of table of pointers to command routines. ADD L ;Add in the offset MOV L,A ;Update the low part of the register JNC JumpToCommand ;If no carry we can branch to routine now. INR H ;Correct the high byte first. JumpToCommand: MOV A,M INX H MOV H,M MOV L,A PCHL ;Do that command. CommandJumpTable: DW VoiceCommandTaskYield ;No command to be processed. DW StartVoiceBox ;Command = 1 DW StopVoiceBox DW VoiceOutTask DW AbortVoiceIn DW AbortVoiceOut DW AbortVoiceCommand DW Set300Baud DW Set600Baud DW Set1200Baud DW Set2400Baud EndCommandJumpTable: {Illegal voice command.} IllegalVoiceCommand: BREAK JMP ResetVoiceCommand ;For debugging only VoiceControllerMode: DB 0 VoiceInterrupt: PUSH PSW PUSH D {This next section of code is a streamlined section of ReadDmaCompletion from DmaSubs. Since we are in a section of code with the interrupts disabled we do not want to waste precious time. Also, if the floppy is running then ReadDmaCompletion would re-enable interrupts before we were ready to have them re-enabled.} LDA DmaActive ;D ¬ DmaActive MOV D,A IN DmaStatus ;Get the completed channels MOV E,A ;Save a copy CMA ;Form complement mask of completed channels ANA D ;Clear channels in DmaActive STA DmaActive ;Store back in DmaActive MVI A,DmaCh3Mask ;Check for Speech Out Completion ANA E DB opORI ;Mask in old flag state SpeechOutFlag: DB 0 STA SpeechOutFlag ;Update flag state MVI A,DmaCh2Mask ;Check for Speech In Completion ANA E DB opORI ;Mask in old flag state SpeechInFlag: DB 0 STA SpeechInFlag ;Update flag state OUT ClearSpeechInterruptFF PUSH H CheckSpeechIn: LDA SpeechInFlag MOV E,A ;Save SpeechInFlag XRA A STA SpeechInFlag ;Reset SpeechInFlag ORA E ;A ¬ SpeechInFlag, Set Flags JZ CheckSpeechOut BREAK CheckSpeechOut: LDA SpeechOutFlag MOV E,A ;Save SpeechOutFlag XRA A STA SpeechOutFlag ;Reset SpeechOutFlag ORA E ;A ¬ SpeechInFlag, Set Flags JZ ExitVoiceInterrupt BREAK DB opMVIA SOIBufferMask: DB 1 MOV E,A MOV A,B XRA E MOV B,A MVI A,3 SUB E STA SOIBufferMask ExitVoiceInterrupt: POP H POP D POP PSW EI RET AbortVoiceIn: {Abort voice in over the DMA link. This is used by InitializeCleanup in the Head, e.g., when the Debugger is called. Nothing to do here until the code for the DMA link is written.} JMP StopVoiceBox AbortVoiceOut: {Abort voice out over the DMA link. This is used by InitializeCleanup in the Head, e.g., when the Debugger is called. Nothing to do here until the code for the DMA link is written.} JMP StopVoiceBox Set300Baud: MVI A,SpeechBaudRate300 JMP SetBaudRate Set600Baud: MVI A,SpeechBaudRate600 JMP SetBaudRate Set1200Baud: MVI A,SpeechBaudRate1200 JMP SetBaudRate Set2400Baud: MVI A,SpeechBaudRate2400 SetBaudRate: STA SpeechBaudRate AbortVoiceCommand: {We should only get this command if we are waiting for the VoiceBox to be ready to send a character. We might get this command here due to a race condition.} JMP ResetVoiceCommand StartVoiceBox: MVI A,SpeechChannels CALL ClearDmaChannel LXI H,VoiceInterrupt ;Get address for Interrupt Service Subroutine (RST 6.5) SHLD RS232CInterruptSwitch ;Store address to vector interrupt LXI H,PCBReadVoiceCSB CALL ReadCPBuffer ;Read Voice CSB MOV L,C ;Save the contents of the BC register pair MOV H,B SHLD SavedBC OUT VoiceResetRegister ;Make sure the Voice hardware is initialized correctly. {Set speech baud rate.} LDA SpeechBaudRate ORI EnableFrameSync+SpeechDmaIntr STA VoiceControllerMode OUT VoiceControlRegister {Enable interrupts on 6.5 line} MVI A,Rst65Mask CALL EnableRST {Reset VoiceBox UART} MVI A,KnownMode ;Force UART into a known state OUT VoiceUartControl XRA A ;Clear the Accumulator OUT VoiceUartControl ;Clear the UART registers MVI A,VoiceInternalReset OUT VoiceUartControl ;Reset voice box {Set up Mode instruction: Async mode, 1 stop bit, no parity, 8 bit length, 16x} MVI A,VoiceBoxMode OUT VoiceUartControl {Set up command instruction: Enable Transmit, Enable Receive} MVI A,VoiceTxEnable+VoiceRxEnable OUT VoiceUartControl LXI B,0 ;Since voice and RS232C cannot be used simultaneously we can take advantage of the BC register pair. JMP ResetVoiceCommand ;VoiceBox should now be started. StopVoiceBox: {Disable interrupts on 6.5 line} MVI A,Rst65Mask CALL DisableRST DB opLXIB SavedBC: DW 0 ;we have saved the BC register pair here when we started the Voice Box MVI A,SpeechChannels CALL ClearDmaChannel XRA A ;Disable Frame Sync and shut off digital speech OUT VoiceControlRegister LXI H,RS232CMiscTask SHLD VoiceSwitch ;Enable RS232 JMP ResetVoiceCommand VoiceOutTask: {Send characters to the VoiceBox over the slow RS232 link. First read in the VoiceCommandBuffer and the CommandByteCount.} LXI H,PCBReadVoiceBoxCommand {Note that we must use The DMA channel to read in the command buffer. This is because the command buffer is in the form of a text string. Each word must be transferred from the CP as follows: high byte of CP word to low byte of IOP word, low byte of CP word to high byte of IOP word. Note that this also reverses the bytes of the count. We only look at the low order byte, which is stored in our high order byte.} CALL StartCPReadDma {Wait for the Transfer to complete.} LXI H,VoiceCommandBuffer ;Set up pointer to the command characters SHLD NextCommandCharacterPtr LXI H,WaitForCommandString ;Fix entry to VoiceTask to check SHLD VoiceCommandContinueAddress ;for DMA complete. WaitForCommandString: CALL CheckCPDmaComplete ;If DMA is complete PortBusyFlag is reset JZ VoiceCommandTaskYield LXI H,StartVoiceCommandTask ;DMA completed so fix up entry SHLD VoiceCommandContinueAddress ;point to VoiceTask LXI H,SendVoiceBoxCommandCharacters SHLD VoiceCommandTaskResumeAddress SendVoiceBoxCommandCharacters: IN VoiceStatusRegister ANI VoiceTxReady JZ SendNextVoiceBoxCharacter {The VoiceBox is not yet ready for a character. Check VoiceBoxCommand to see if the Head wants to abort the command.} LDA VoiceCommand CPI 6 JNZ VoiceCommandTaskYield JMP CommandCharactersSent SendNextVoiceBoxCharacter: {The VoiceBox is ready for the next character.} DB opLXIH ;HL ¬ NextCommandCharacterPtr NextCommandCharacterPtr: DW 0 MOV A,M ;A ¬ NextCommandCharacter OUT VoiceUartData ;Send next command character INX H ;Point to next character SHLD NextCommandCharacterPtr LDA CommandByteCount DCR A STA CommandByteCount JNZ VoiceCommandTaskYield ;Jump if not last command character CommandCharactersSent: {Continue if the last command character has been sent to the VoiceBox.} LXI H,BeginVoiceCommandTask SHLD VoiceCommandTaskResumeAddress ResetVoiceCommand: LXI H,PCBResetVoiceCommand CALL WriteCPBuffer VoiceCommandTaskYield: DS 0 ; VoiceInTask: {The voice input task reads characters from the VoiceBox over the slow RS232C link and sends them to the CP. This task is activated by StartVoiceBox.} LDA PortBusyFlag ORA A JNZ VoiceInTaskYield ;Jump if port is busy IN VoiceStatusRegister ANI VoiceRxReady ;See if input character is waiting JNZ VoiceInTaskYield ;Yield if no character waiting {Continue if a character from the voice box is ready. Read in the VoiceBoxMessage from the CP to see if the CP has processed the last character.} LXI H,PCBVoiceBoxMessage CALL ReadCPBuffer LDA CharacterReceivedFlag ;Check CP Ready ORA A ;See if CP has processed last char MVI A,80H ;Set character received, assume no overrun JZ StoreVoiceCompletion {If the CP has not processed the last character, we have an overrun condition.} MVI A,4 ;Set overrun StoreVoiceCompletion: STA CharacterReceivedFlag IN VoiceUartData ;Read character from UART SendVoiceBoxChar: STA VoiceBoxCharacter IN VoiceUartStatus STA SavedVoiceUartStatus {Check for break detected, Frame Error, Overflow Error, Parity Error} ANI 78H JZ NoVoiceTransmissionError {Continue if transmission error. Return error code to the Head.} STA CharacterReceivedFlag NoVoiceTransmissionError: LXI H,PCBVoiceBoxMessage CALL WriteCPBuffer LHLD NakedMask CALL DoNakedNotify VoiceInTaskYield: SpeechInTask: LDA PortBusyFlag ORA A JNZ SpeechInTaskYield ;Jump if port is busy LXI H,PCBGetStatus CALL ReadCPBuffer LDA GetStatusHighByte ORA A JZ SpeechInTaskYield DB opMVIA SIBufferMask: DB 1 ANA C JNZ SpeechInTaskYield BREAK LDA SIBufferMask MOV D,A DI ORA C MOV C,A EI MVI A,3 SUB D STA SIBufferMask SpeechInTaskYield: SpeechOutTask: LDA PortBusyFlag ORA A JNZ SpeechOutTaskYield ;Jump if port is busy LXI H,PCBPutStatus CALL ReadCPBuffer LDA PutStatusHighByte ORA A JZ SpeechOutTaskYield DB opMVIA ;Load current Speech Out buffer mask SOBufferMask: DB 1 ANA B ;Check for buffer available JNZ SpeechOutTaskYield LHLD PutBufferPtrLo SHLD PCBSpeechOut LHLD PutBufferPtrHi SHLD PCBSpeechOut+2 LHLD PutBufferByteCount SHLD PCBSpeechOut+4 LHLD SOBuffer SHLD PCBSpeechOut+6 MOV D,H MOV E,L LHLD SOBufferNext SHLD SOBuffer XCHG SHLD SOBufferNext LXI H,PCBSpeechOut CALL ReadCPBuffer CALL SODMAStart LDA SOBufferMask MOV D,A DI ORA B MOV B,A EI MVI A,3 SUB D STA SOBufferMask XRA A STA PutStatusHighByte LXI H,PCBPutStatus CALL WriteCPBuffer LHLD PutBufferByteCount SHLD PutByteCount LHLD NakedMask CALL DoNakedNotify SpeechOutTaskYield: {Continue to the next task. This is the last task in Domino.cfg, so we jump back to the top of the round-robin task management.} JMP MainLoop END