{  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