{ File: [Iris]<WMicro>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