{  File:  [Idun]<WDLion>BookKeepingTask.asm

Last change by Jim Frandeen August 9, 1982  3:04 PM: new IO Page format. Add support for keyset. Add support for ReadBlock and WriteBlock processor commands.
Last change by Jim Frandeen July 20, 1982  11:08 AM: reread the whole Mouse CSB to fix the cursor jumping problem.
Last change by Jim Frandeen May 25, 1982  8:23 AM
Written by Jim Frandeen April 1, 1982  9:21 AM
}


	get "SysDefs"		;System defs
	get "CommonDefs"		;Common defs

	IMP	BlockBuffer	;From Buffer
	IMP	CheckCPDmaComplete	;From CPSubs
	IMP	ClearMiscControl1Bit	;From Common
	IMP	Copy		;From Common
	IMP	DisableRST		;From Common
	IMP	DoMiscClock	;From Common
	IMP	DoReadIOPCmd	;From Buffer
	IMP	DoReadUmbilicalBlockCmd	;From Buffer
	IMP	DoWriteIOPCmd	;From Buffer
	IMP	DoWriteUmbilicalBlockCmd	;From Buffer
	IMP	ErrorReport		;From Common
	IMP	ExitInterrupt	;From RS232CSubs
	IMP	KeyMap		;From FloppyTask
	IMP	MiscControl1Val		;From Common
	IMP	PutMPanel		;From Common
	IMP	ReadCPBuffer		;From CPSubs
	IMP	SetMiscControl1Bit	;From Common
	IMP	StartCPReadDma		;From CPSubs
	IMP	StartCPWriteDma		;From CPSubs
	IMP	Wait		;From Common
	IMP	WriteCPBuffer	;From CPSubs
	IMP	ZeroCommand	;From Common

	EXP	CommandData
	EXP	FloppyIocbAddr
	EXP	FloppyIocbAddrHi
	EXP	GetTOD
	EXP	LSEPData
	EXP	LSEPStatus
	EXP	MPNumber
	EXP	PortBusyFlag
	EXP	ProcData0
	EXP	ProcData1
	EXP	ProcData2
	EXP	MainLoop
	EXP	ReadKeyboard
	EXP	ResetProcessorCommand
	EXP	RS232CGetFlag
	EXP	RS232CMiscCommand
	EXP	RS232CMiscFlag
	EXP	RS232CPutFlag
	EXP	TransferProcessorCSB
	EXP	TTYCommand
	EXP	TTYOutData
	EXP	TTYOutStatus
;
CommandData:
{The following command data from the IOPage is read from CP memory each time around the main loop. This command data indicates what tasks are to be performed. Each task has a one-word command. In general, the the command word is set to a non zero value by the CP when there is something for the IOP task to do. In addition, the CP may specify parameters in the CSB associated with the command. It is important that the CP set up the parameters before setting the command word. When the IOP task has completed, it sets the command word to zero.} 

LSEPData:
{When LSEPData is non zero, it means we have a control character to send to the LSEP. The LSEPTask sets it back to zero when the operation is complete. The CP waits for this word to be non-zero before it stores a new control character here.}
	DS	1		;14010 low
	DS	1		;14010 High order byte is not used
	
LSEPStatus:
{When the LSEP Task has a new status byte to send to the CP, it stores it here. When LSEPStatus is zero, it means the last status sent to the CP has been processed. When the LSEP Task has a new status byte to send to the CP, it checks to be sure LSEPStatus is zero. If not, it sets the status to overrun.}
	DS	1		;14011 low
	DS	1		;14011 High byte is not used
	
MPNumber:
{This is the maintenance panel number to be displayed. When the CP wants to set the maintenance panel, it stores the number here. Each time through its main loop, the MPTask checks to see if MPNumber is different from the number currently in the maintenance panel. If so, it sets the maintenance panel to the new number.}
	DS	2		;14012
	
FloppyIocbAddr:
{If the virtual address is non zero, activate the Floppy Task. When the Floppy Task has completed the operation, it sets this word back to zero.}
	DS	1		;14013 low
FloppyIocbAddrHi:
	DS	1		;14013 High
	
TTYOutData:
{When the command is Put, the data byte to be sent is in the low byte of the TTY command word. When the command is SetParameter, the low byte specifies what prameters to set.}

TTYOutStatus:
{When the Out command has completed, the status is returned in the low order byte of the word, replacing the Data byte.}
	DS	1		;14014 low byte
	
TTYCommand:
{The high bit of this word is set nonzero when there is something for the TTY Task to do. The low 7 bits of the command byte specify the command.}
	DS	1		;14014 high byte

ProcCommand:
{The Processor Command is set to a non zero value when there is a command to be processed. The command is set back to zero by the IOP when the command has been processed.} 
	DS	1		;14015 low byte
	DS	1		;14015 High order byte is not used
	
{The Mouse Change Flag is set by the CP when it is necessary to set new mouse coordinates. The flag word is set back to zero by the IOP when the new coordinates have been set.}
	DS	1		;14016 low byte is not used
MouseChangeFlag:
	DS	1		;14016 High order byte
	
ToneCommand:
{ToneCommandFlag is set by the CP when a Tone Command is to be executed. The command is set back to zero by the IOP when the command has been processed.}
	DS	1		;14017 low byte
ToneCommandFlag:
	DS	1		;14017 High order
	
RS232CMiscCommand:
{The RS232CMisc Flag high order bit is set when there is a Misc command to be executed. The command is specified in the low order byte of the word. The word is set back to zero by the IOP when the command has been executed. The low 7 bits of MiscFlag are used to set the Baud rate.}
	DS	1		;14018 low byte
RS232CMiscFlag:
	DS	1		;14018 High has flag in high bit
	
{This flag is set by the CP when a Put IOCB is ready. It is reset by the CP when the Put command has been completed.}
	DS	1		;14019 low byte is not used
RS232CPutFlag:
	DS	1		;14019 High order byte
	
{This flag is set by the CP when an IOCB is ready for a Get. It is reset by the IOP when the Get has completed.}
	DS	1		;1401A low byte is not used
RS232CGetFlag:
	DS	1		;1401A High order byte
	
;End of Command data that is read in each time around the loop
	
;
ProcessorCSB:
ProcData2:
	DS	2		;14026
ProcData1:
	DS	2		;14027
ProcData0:
	DS	1		;14028 low
ProcData0Hi:
	DS	1		;14028 high

MouseChgCSB:	
MouseChgX:
	DS	2		;14029
MouseChgY:
	DS	2		;1402A

ToneCSB:
ToneFreq:			;1402B
	DS	2		;Frequency constant (1843.2/f, f in kHz)

{  Time-of Day storage @ 14037.
  Note that the High word is stored twice to allow the CP to determine whether the full state has been written.}
TODValid:			;14037
	DS	1		;TOD data valid (0 = invalid, -1 = valid)
TODValidHi
	DS	1		;14037 high byte
TODLo:
	DS	2		;14038 TOD data low word
TODHi:
	DS	1		;14039 TOD data high word
TODMost:
	DS	1		;TOD data most significant byte
TODLoX:
	DS	2		;1403A TOD data high word (copy of Lo above)

;  Mouse and Keyboard state.

MouseXPosition:	;CP Mouse address = 1403B
	DW	0		;MouseX coordinate (16 bits)
MouseYPosition:
	DW	0		;1403C MouseY coordinate (16 bits)
MouseSwitches:
	DW	0FFFFH		;1403D Mouse switches
				;   (MSw: bit 5: Sw1, bit 6: Sw3, bit 7: Sw2)
				;   (MSw+1: 0)
;
KeysetPresentFlag:
{This flag is set by a ProcessorCommand. If the Keyset is present, the MouseTask will read the keyset.}
	DB	0

IOPagePCB:	;Control Block to transfer command data from IOPage		
	DW	CommandLoc	;CP buffer pointer (low): Start of commands
	DW	CPIOPageHi	;CP buffer pointer (high)
	DW	CommandDataSize	;CP buffer count (bytes)
	DW	CommandData	;Pointer to start of IOP command area


TODPCB:	;Control Block for TOD transfer to IOPage
	DW	TODLoc		;CP buffer pointer (low): TOD state
	DW	CPIOPageHi	;CP buffer pointer (high)
	DW	TODSize		;CP buffer count (words)
	DW	TODValid	;Pointer to IOP buffer

MouseChangeFlagPCB:	;Control Block to transfer MouseChangeFlag to IOPage
	DW	MouseChangeFlagLoc	;CP pointer (low): MouseChg CSB.Flag
	DW	CPIOPageHi	;CP buffer pointer (high)
	DW	2		;CP buffer count (bytes): Flag word
	DW	ZeroCommand	;Pointer to IOP buffer

MouseChgPCB:	;Control Block to transfer Mouse Change PCB from IOPage into MouseXPosition and MouseYPosition
	DW	MouseChgCSBLoc	;CP pointer (low): MouseChg CSB.MouseChgX
	DW	CPIOPageHi	;CP buffer pointer (high)
	DW	MouseChgCSBSize		;CP buffer count (4 bytes)
	DW	MouseXPosition		;Pointer to IOP buffer

MousePCB:	;Control Block for transfer of mouse data to IOPage.
	DW	MXLoc		;CP buffer pointer (low): Mouse state
	DW	CPIOPageHi	;CP buffer pointer (high)
	DW	MStateSize	;CP buffer count (3 words)
	DW	MouseXPosition		;Pointer to IOP buffer

KBTaskPCB:	;Control Block to transfer keyboard map to IOPage.
	DW	KBLoc		;CP buffer pointer (low): Keyboard map
	DW	CPIOPageHi	;CP buffer pointer (high)
	DW	KeyMapSize	;CP buffer count 
	DW	KeyMap		;Pointer to IOP buffer

ZeroProcessorPCB:	;Port Control Block values to zero Processor Command.
	DW	ProcCommandLoc	;CP buffer pointer (low): Processor CSB
	DW	CPIOPageHi	;CP buffer pointer (high)
	DW	2		;CP buffer count (bytes)
	DW	ZeroCommand	;Pointer to IOP buffer


ProcPCB:	;Port Control Block values for Processor CSB transfer.
	DW	ProcessorCSBLoc	;CP buffer pointer (low): Processor CSB
	DW	CPIOPageHi	;CP buffer pointer (high)
	DW	ProcessorCSBSize	;CP buffer count (words)
	DW	ProcData2	;Pointer to IOP buffer

ZeroToneCommand:
	DW	ToneCommandLoc	;CP buffer pointer (low): ToneCSB.ToneCommand
	DW	CPIOPageHi	;CP buffer pointer (high)
	DW	2	;CP buffer count (bytes)
	DW	ZeroCommand		;Pointer to IOP buffer

TonePCB:
	;PCB to read Tone CSB from IOPage
	DW	ToneCSBLoc	;CP buffer pointer (low): ToneCSB.ToneCommand
	DW	CPIOPageHi	;CP buffer pointer (high)
	DW	ToneCSBSize	;CP buffer count (bytes)
	DW	ToneCSB		;Pointer to IOP buffer

RST0Loc	equ	0			;Start of EProm boot code
;
; Keyboard tables:
;  The index table provides a table lookup between the keyboard event byte,
;     and the KeyMap byte index.
KBIndex:
;   00H:
	db	00H,00H,00H,00H	;No key
	db	00H,00H,00H,00H	;No key
	db	00H,00H,00H,00H	;No key
	db	00H,00H,00H,00H	;No key

;   10H:
	db	0BH,0AH,0AH,0AH	;D1, T10, T9, T8.
	db	0BH,6H,0BH,5H	;T7, R6, T6, L12.
	db	6H,8H,0BH,0BH	;L9, L6, T5, T4.
	db	0BH,0BH,0BH,00H	;T3, T2, T1, .

;   20H:
	db	8H,00H,4H,8H	;R4, , R1, R2.
	db	9H,0AH,00H,9H	;R5, R3, , L10.
	db	9H,9H,9H,00H	;L7, L4, L1, .
	db	00H,9H,00H,00H	;, A9, , .

;   30H:
	db	8H,1H,2H,0H	;R7, R10, R11, R8.
	db	9H,6H,6H,2H	;R9, R12, A7, L11.
	db	0H,8H,8H,4H	;L8, L5, L2, L3.
	db	8H,0AH,00H,00H	;A8, A11, , .

;   40H:
	db	00H,0AH,6H,0H	;, A12, A6, /.
	db	4H,2H,7H,7H	;., , m, n.
	db	5H,1H,5H,2H	;b, v, c, x.
	db	4H,00H,0AH,00H	;z, , 47, .

;   50H:
	db	4H,4H,2H,4H	;A4, 46, 43, :.
	db	2H,0H,5H,7H	;l, k, j, h.
	db	7H,5H,1H,3H	;g, f, d, s.
	db	3H,00H,6H,4H	;a, , A3, A5.

;   60H:
	db	0AH,2H,6H,0H	;A10, 45 , 42, p.
	db	2H,3H,1H,7H	;o, i, u, y.
	db	7H,7H,1H,3H	;t, r, e, w.
	db	3H,5H,00H,8H	;q, A1, , D2.

;   70H:
	db	0H,6H,0H,0H	;A2, =, -, 0
	db	3H,7H,1H,1H	;9, 8, 7, 6.
	db	1H,1H,3H,3H	;5, 4, 3, 2.
	db	5H,5H,00H,00H	;1, 48, , .

;  The mask table gives the bit mask within the KeyMap byte.
KBMask:
;   00H:
	db	00H,00H,00H,00H	;No key
	db	00H,00H,00H,00H	;No key
	db	00H,00H,00H,00H	;No key
	db	00H,00H,00H,00H	;No key

;   10H:
	db	80H,40H,4H,80H	;D1, T10, T9, T8.
	db	1H,1H,2H,8H	;T7, R6, T6, L12.
	db	2H,40H,4H,8H	;L9, L6, T5, T4.
	db	10H,40H,20H,00H	;T3, T2, T1, .

;   20H:
	db	2H,00H,1H,8H	;R4, , R1, R2.
	db	80H,20H,00H,20H	;R5, R3, , L10.
	db	10H,8H,4H,00H	;L7, L4, L1.
	db	00H,2H,00H,00H	;, A9, , .

;   30H:
	db	4H,1H,1H,4H	;R7, R10, R11, R8.
	db	40H,4H,40H,2H	;R9, R12, A7, L11.
	db	2H,20H,10H,2H	;L8, L5, L2, L3.
	db	80H,2H,00H,00H	;A8, A11, , .

;   40H:
	db	00H,1H,8H,8H	;, A12, A6, /.
	db	20H,10H,1H,2H	;., , m, n.
	db	1H,1H,4H,80H	;b, v, c, x.
	db	80H,00H,10H,00H	;z, , 47, .

;   50H:
	db	8H,4H,8H,10H	;A4, 46, 43, :.
	db	20H,40H,2H,8H	;l, k, j, h.
	db	20H,10H,4H,8H	;g, f, d, s.
	db	4H,00H,80H,40H	;a, , A3, A5.

;   60H:
	db	8H,4H,20H,10H	;A10, 45 , 42, p.
	db	40H,1H,2H,10H	;o, i, u, y.
	db	40H,80H,10H,20H	;t, r, e, w.
	db	10H,20H,00H,1H	;q, A1, , D2.

;   70H:
	db	1H,10H,20H,80H	;A2, =, -, 0
	db	2H,4H,8H,20H	;9, 8, 7, 6.
	db	80H,40H,80H,40H	;5, 4, 3, 2.
	db	80H,40H,00H,00H	;1, 48, , .


; Internal definitions:

SetTimeWait	equ	30000	;Wait value of ~ 200 msec for MP
KBDataWait	equ	6H	;Wait value of ~ 50 usec for KB Data
;  KB Bell programming
SC1	equ	40H	;Counter 1
RL3	equ	30H	;Read/Load least significant, then most significant data
Mode3	equ	6	;Mode 3 - square generator
KBBellMode	equ	SC1+RL3+Mode3	;Mode byte for 8253 KBBell Clk
;
;  Subroutine: ReadMouse 
;  Read the Mouse hardware and check for CS Parity error.
;   Updates MouseXPosition and MouseYPosition
;   Stores switches in MouseSwitches.
;  On exit:
;   Condition code = 0:  No change in mouse position or switches.
;   Condition code = #0:  Change in mouse position or switches.

ReadMouse:
	LDA	MouseSwitches
	MOV	D,A		;Save old MouseSwitches in D
	LHLD	8000H+MouseX	;Memory-mapped I/O: L ← MouseX, H ← MouseYPosition
	OUT	MouseClr	;Clear Mouse hardware counters
	IN	MiscInput1	;Read switches
	CMA			;Mouse sense complemented
	ANI	MouseSwMask
	MOV	E,A		;Save Mouse Switches in E
	ANI	CSParityMask	;Check for CS Parity error
	JNZ	CSParityError

ReadKeyset:
	LDA	KeysetPresentFlag
	ORA	A
	JZ	UpdateSwitches

{Read the Keyset. The keyset has the following format:
	bit 0:	Keyset 0 (leftmost switch)
	bit 1:	Keyset 1
	bit 2:	Keyset 2
	bit 3:	Keyset 3
	bit 4:	Keyset 4
}
	IN	Keyset

{The value of a keyset bit is: 0 = not depressed (up), 1 = depressed (down). Reverse the bits.}

UpdateSwitches:
	CMA
	ANI	0F8H
	ORA	E		;OR in MouseSwitches
	STA	MouseSwitches	;Store new mouse switches
	XRA	D		;Compare to old mouse switches
	MOV	D,A		;D=0 if no change in switches

{HL = change in mouse position. See if there was a change.}
	MOV	A,L		;A ← mouse change X
	ORA	H		;OR in mouse change Y
	JNZ	MouseChanged

{Continue if the mouse position did not change. Return with condition code to indicate whether mouse buttons changed.}
	ORA	D
	RET

MouseChanged:
{The mouse position has changed. HL = change in mouse position. Update the mouse coordinates.}
	MOV	E,L		;DE will contain X change
	MOV	A,L		;A ← mouse change X
	ORA	A		;Check direction of change
	JP	XChangePositive
	MVI	D,0FFH		;Set sign of X change negative
SaveXChange:
	PUSH	D		;Save X change in Stack
	MOV	E,H		;DE will contain Y change
	MOV	A,H		;A ← mouse change Y
	ORA	A		;Check direction of change
	JP	YChangePositive
	MVI	D,0FFH		;Set sign of Y change negative
UpdateCoordinates:
	LHLD	MouseYPosition
	DAD	D		;HL ← MouseYPosition + change Y
	SHLD	MouseYPosition
	LHLD	MouseXPosition
	POP	D		;DE ← X change
	DAD	D		;HL ← MouseXPosition + change X
	SHLD	MouseXPosition
	MVI	A,1		;Return condition code
	ORA	A
	RET

XChangePositive:
	MVI	D,0		;Set sign of X change positive
	JMP	SaveXChange
YChangePositive:
	MVI	D,0		;Set sign of Y change positive
	JMP	UpdateCoordinates

CSParityError:
	IN	MiscInput1	;Read in CSParity error bit again
	ANI	CSParityMask
	JNZ	ReadKeyset	;z =>  CS parity error (active low)
;  Control Store parity error detected.  Stop the world.
	LXI	H,ErrorCSParity
	JMP	ErrorReport

	
{  **** LimitSample is deactivated. The Tajo window package wants to do the limiting itself.
;MXMax:
;	DW	1024-16	;Maximum MouseX coordinate (1008 = 3F0H)
;MYMax:
;	DW	808-16	;Maximum MouseY coordinate (792 = 318H)
;  SubrOUTine: LimitSample[].
;  Limit the mouse sample for maximum and minimum values.
;  On entry:  B,C points to the coordinate location (high byte).
;	       H,L points to the maximum coordinate location (high byte).
;  Algorithm used:
;    Minimum:     IF Hi[Coord] < 0 =>  Set coordinate to zero, RETURN;
;    Maximum:
;        IF Hi[Coord] < Hi[Max]  THEN  [No change, RETURN];
;        IF Hi[Coord] > Hi[Max]  THEN  [Set to maximum, RETURN];
;        IF Hi[Coord] = Hi[Max]  THEN
;            BEGIN
;              IF Lo[Coord] < Lo[Max]  THEN  No change, RETURN;
;              IF Lo[Coord] = Lo[Max]  THEN  No change, RETURN;
;              IF Lo[Coord] > Lo[Max]  THEN  Set to maximum, RETURN;
;            END;

LimitSample:
; Check minimum.
	LDAx	b		;Compare Hi[coord] to 0
	CPI	0		;Check sign bit
	jm	SetToMIN	;m => coordinate negative
; Check maximum.  A has Hi[coord].
	cmp	m		;compare to Hi[Max]
	rc			;c => Hi[Coord] < Hi[Max]:  [No change, RETURN]
;  Now Hi[Coord] >= Hi[Max].
	JNZ	SetToMax2	;nz => Hi[Coord] > Hi[Max]:  [Set to maximum, RETURN]
;  Now Hi[Coord] = Hi[Max].  Check low bytes.
	DCX	b		;Point to low bytes
	DCX	h
	LDAx	b		;Get Lo[coord]
	cmp	m		;compare to Lo[Max]
	rc			;c => Lo[Coord] < Lo[Max]:  [No change, RETURN]
;  Now Lo[Coord] >= Lo[Max].
	rz			;z => Hi[Coord] = Hi[Max]:  [No change, RETURN]
;  Now Lo[Coord] > Lo[Max].  Set to maximum.
SetToMax1:
	MOV	a,m		;Get Lo[Max]
	stax	b		;Store in coordinate
	INX	h		;Point to high bytes
	INX	b
SetLast:
	MOV	a,m		;Get byte
	stax	b		;Store in coordinate
	RET
; Pointers are set to high bytes.
SetToMax2:
	MOV	a,m		;Get Hi[Max]
	stax	b		;Store in coordinate
	DCX	h		;Point to high bytes
	DCX	b
	JMP	SetLast	;Do low byte
; Set coordinates to zero.  Pointers are set to high bytes.
SetToMin:
	xra	a		;Clear A
	stax	b		;Store in high coordinate
	DCX	b
	stax	b		;Store in low coordinate
	RET
}
;
GetTOD:
{Read the TOD into the TOD state in IOP memory. If the PowerFailed bit is on, clear IOPValid, otherwise set IOPValid ← -1.}
;  First Read the time.
	LXI	H,TODMost	;Point to most significant byte of TOD buffer
	CALL	ReadTOD	;Read time into TODLo..TODHi
	LHLD	TODLo		;Copy Low word
	SHLD	TODLoX
;  Read the power failed bit.
	IN	MiscInput1		;Read power fail bit from MiscInput1
	ANI	PowerFailedMask	;Mask OUT other bits
	JZ	ValidTime		;z => no power failure
;  There was a power failure.  Clear the valid flag.
	LXI	H,CheckPortBusy
	LXI	D,0
SetTODValid:
	SHLD	TODValidSwitch
	XCHG
	SHLD	TODValid		;Store in TOD state
	RET
;  The time is valid. Set TODValid. Set to check TOD each time through the loop.
ValidTime:
	LXI	H,CheckTOD
	LXI	D,-1
	JMP	SetTODValid

ReadTOD:

{Read the TOD hardware clock. Read the 32 bit time into TODLo (LS word) - TODHi (MS word). Can take up to a second to do this.
On entry:   H,L - pointer to data buffer (most sig. byte) for time to be read into.}

	OUT	TODClr		;Clear TOD interrupt.
;  Wait for a TOD interrupt.
WaitTODIntr:
	IN	MiscInput1	;***Rev G and higher IOP
	ANI	TODIntMask	;TOD interrupt?
	JZ	WaitTODIntr
	OUT	TODClr		;Clear TOD interrupt.
;  Interrupt has just come.  We can now read the clock between 1 Hz pulses.
	MVI	A,ReadTimeMode	;Set ReadTime bit in MiscControl1
	CALL	SetMiscControl1Bit
	MVI	E,4		;4 bytes of data

ReadTODLoop:
{Read a byte of the TOD clock. Assumes the TOD clock is in Read Mode. Bits from clock come in true, and most significant bit first.
Register usage:  D - time data
    E - data byte counter
    HL - pointer to data buffer}

	MVI	D,0		;Clear D
	MVI	A,8		;8 bits in byte
ReadTODByteLoop:
	STA	BitCounter
	MOV	A,D		;Shift left partial byte
	RLC
	MOV	D,A		;Store back in D
	IN	MiscInput1	;Read bit
	ANI	TODDataMask
	RLC			;Align to bit 7 position
	RLC
	ORA	D		;OR into data byte
	MOV	D,A		;Partial back to D
; Shift TOD clock shift register
	PUSH	D
	MVI	D,TODRead	;Mask for TODRead clock
	CALL	DoMiscClock
	POP	D
	DB	opMVIA		;A ← BitCounter
BitCounter:
	DB	0
	DCR	A		;Done all 8 bits?
	JNZ	ReadTODByteLoop
;  TOD data byte in D.  Store in memory.
	MOV	M,D		;Store in Time buffer
	DCX	H		;Move pointer
	DCR	E		;last byte?
	JNZ	ReadTODLoop
;  Clear ReadTime Mode.
	MVI	A,nReadTimeMode	;Clear ReadTime bit in MiscControl1
	JMP	ClearMiscControl1Bit


SetTOD:

{Set the time of day clock. This subrOUTine sets the TOD clock to value in SetTime[0..3]. SubrOUTine first clears the TOD clock and then sets each byte of the clock. The clock is 32 bits wide, and each byte can be set independently.
On entry:  HL points to a buffer where the time to be set is stored.
   Chunk A:  Bits 24-31 (8 bits) (HL↑)
   Chunk B:  Bits 16-23 (8 bits) (HL↑+1)
   Chunk C:  Bits 8-15 (8 bits) (HL↑+2)
   Chunk D:  Bits 0-7 (8 bits) (HL↑+3)}

	MVI	A,SetTimeMode	;First clear the TOD clock.
	CALL	SetMiscControl1Bit	;TOD clock to Set Time Mode
	PUSH	H		;Save H,L over CALL to Wait
	LXI	H,SetTimeWait	;Wait .4 msec
	CALL	Wait
	POP	H		;Restore H,L
	MVI	A,SetTimeMode+ClearTimeMode
	CALL	SetMiscControl1Bit	;TOD clock to Set+Clear Mode
	MVI	A,nClearTimeMode	;Clear time
	CALL	ClearMiscControl1Bit	;TOD clock to Set Mode

{Set each byte of the counter (HL points to buffer)}
	MVI	D,TODSetA	;Initialize clock to byte A
SetTODLoop:
	MOV	E,M		;Get data value for counter
	INR	E		;Correct so that 0 can be set
	CALL	SetTODByte	;Set counter byte
	INX	H		;Point to next Time byte
	MOV	A,D
	RAR			;Form next clock mask
	MOV	D,A		;Return to D
	CPI	0		;Check if the last byte was done (mask shifted off)
	JNZ	SetTODLoop	;If not, loop for next byte
;  Clock is now set.  Release SetTime mode.
	MVI	A,nSetTimeMode
	JMP	ClearMiscControl1Bit	;TOD clock to not Set Mode
;  Jump to ClearMiscControl1Bit and return.


SetTODByte:
{Set one counter in the TOD clock. Assumes that the TOD clock is in SetMode. Assumes that the particular counter has been cleared. The subroutine counts up the counter to the desired value.
  On entry:
	D has mask of particular SetClk to use
	E has value for counter +1 (so zero can be set).}

	DCR	E		;Decrement counter
	RZ
	CALL	DoMiscClock	;D has mask for TOD Set clock
	JMP	SetTODByte
;
DoProcCmd:
{This subroutine is called when a command is detected in the CSB. The comands are:
	1 = Set Time-of-Day clock (time in ProcData2, ProcData1)
	2 = Read Time-of-Day clock (hw) (time returned in TODValid, TODLo, TODHi)
	3 = Read Host address (host address returned in ProcData2, ProcData1, ProcData0)
	4 = Boot
	5 = Read IOP Byte
	6 = Write IOP Byte
	7 = Read Umbilical Block
	8 = Write Umbilical Block
	9 = Turn On Keyset
	10 = Turn Off Keyset
On entry:  The Processor CSB has the command (and data if  neccessary). The CP port is already acquired.}

	LXI	H,ProcPCB	;Pointer to Processor CPport control block
	CALL	ReadCPBuffer	;Read CP main memory
	LDA	ProcCommand	;Check command
	CPI	1
	JZ	DoSetTODCmd
	CPI	2
	JZ	DoReadTODCmd
	CPI	3
	JZ	DoReadHAddrCmd
	CPI	4
{Command 4:  Do Boot. This command causes a jump to the start of the EProm boot code.  The  default boot will be executed. The entry point in the EProm is the same as if the boot button had been pressed, except that the hardware reset was not done.}
	JZ	RST0Loc

	CPI	5
	JZ	DoReadIOPCmd
	CPI	6
	JZ	DoWriteIOPCmd
	CPI	7
	JZ	DoReadUmbilicalBlockCmd
	CPI	8
	JZ	DoWriteUmbilicalBlockCmd
	CPI	9
	JZ	UpdateKeysetPresentFlag
	CPI	10
	JZ	TurnOffKeyset

	LXI	H,ErrorInvProcCmd
	JMP	ErrorReport		;Report the error

TurnOffKeyset:
	XRA	A
UpdateKeysetPresentFlag:
	STA	KeysetPresentFlag
	JMP	ResetProcessorCommand

DoSetTODCmd:
{Command 1:  Set the Time-of Day hardware.}
	LXI	H,ProcData2		;Point to the time buffer
	CALL	SetTOD		;Set the time
	CALL	GetTOD			;Re-initialize the TOD state in memory
	LDA	TODValidHi
	ORA	A
	JNZ	ResetProcessorCommand		;z => time still invalid

{There was an error in setting the TOD hardware.  The PF bit was still on.}
	LXI	H,ErrorSetTOD
	JMP	ErrorReport		;Report the error


DoReadTODCmd:
{Command 2:  Read the Time-of Day hardware.}
	CALL	GetTOD		;Read the TOD into TOD state
	LXI	H,TODValid		; Copy from TODValid
	LXI	D,ProcData2		;   to ProcData2
	MVI	A,6			; Copy 6 bytes
	CALL	Copy
	JMP	TransferProcessorCSB

DoReadHAddrCmd:
{Command 3:  Read the Host Address into ProcData.
Read the host address from the host address prom. The prom has 12 nibbles containing the 48-bit host address, plus an 8-bit checksum. (Checksum is currently unimplemented.) Current realization:  Only the high word is in the prom with the Processor ID. The middle and low words, which are constant, are generated in software.
Register usage:
	HL - buffer pointer
	DE - Host address prom pointer}

	LXI	H,ProcData0Hi		;Point to last byte in ProcData in CSB
	LXI	D,8000H+HostAddr	;Point to the HAddr prom
	MVI	A,6			;Count of 6 bytes
ReadHAddrLoop:
	STA	PromByteCount
	LDAX	D		;Get low nibble of byte
	ANI	0FH		;Mask OUT high part
	STA	LowNibble
	INX	D		;Point to next nibble
	LDAX	D		;Get high nibble of byte
	ANI	0FH		;Mask OUT high part
	INX	D		;Point to next nibble
	RLC			;Move to high part of byte
	RLC
	RLC
	RLC
	DB	opORI		;Form byte in A
LowNibble:
	DB	0
	MOV	M,A		;Store in buffer
	DCX	H		;Point to next byte up
	DB	opMVIA		;A ← PromByteCount
PromByteCount:
	DB	0
	DCR	A
	JNZ	ReadHAddrLoop	;nz => still more to do

TransferProcessorCSB:
	LXI	H,ProcPCB
	CALL	WriteCPBuffer

ResetProcessorCommand:
	LXI	H,ZeroProcessorPCB
	JMP	WriteCPBuffer	;Write CP main memory and RET
;
ReadKeyboard:
{  Read one byte from the keyboard. Assumes that the data is ready in the keyboard (i.e. KBReq is active). Note:  MiscControl1Val is used to get other bits' state, but is not modified.}
	MVI	A,pReadKBData	;Set ReadKB bit high
	CALL	SetMiscControl1Bit
	LXI	H,KBDataWait	;Set up delay
	CALL	Wait		;Wait ~50 usec
	MVI	A,npReadKBData	;Keyboard data latch clock bit low (>RevG)
	CALL	ClearMiscControl1Bit	;Keyboard data is latched	
	IN	KBData		;Read in data from latch
	CMA			;Comes in complemented
	STA	KeyEvent	;Store event

UpdateKeyMap:
{Insert the keyboard event into the KeyMap bitmap.
On entry A (and KeyEvent) has the 8-bit keyboard event.
Bit 0 =0 means downstroke, bit 0 =1 means upstroke.}

	ANI	7FH		;Form 7-bit index to KB tables
	MOV	E,A		;DE has index value
	MVI	D,0
	LXI	H,KBIndex	;Point to start of Index table
	DAD	D		;Add index into table
	MOV	A,M		;C ← index into KeyMap
	PUSH	PSW		;Save IndexIntoKeyMap
	LXI	H,KBMask	;Point to start of Mask table
	DAD	D		;Add index into table
	MOV	A,M		;E ← bitmask in KeyMap
	MOV	E,A
	CMA			;D ← complement of bitmask in KeyMap
	MOV	D,A
	LXI	H,KeyMap	;Point to start of KeyMap
	POP	PSW		;A ← IndexIntoKeyMap
	ADD	L		;Add index into KeyMap
	MOV	L,A
	MOV	A,H
	ACI	0
	MOV	H,A
	DB	opMVIA;		;A ← KeyEvent
KeyEvent:
	DB	0
	ORA	A		;Set flags
	MOV	A,M		;Get old map byte
	JM	KeyUp		;S=1 (m) => Upstroke
; It was a down stroke.
KeyDown:
	ANA	D		;AND in new down stroke
	JMP	SendNewMap
KeyUp:
	ORA	E		;OR in new up stroke
SendNewMap:
	MOV	M,A		;Update map
	LXI	H,KBTaskPCB
	JMP	WriteCPBuffer	;Write new map and RET

DoToneCmd:
{  Come here when a command is detected in the Tone CSB. The comands are:
	0 = Start Tone (frequency divisor in ToneFreq)
	1 = Stop Tone
On entry:  The Tone CSB has the command (and data if neccessary).}

	LDA	ToneCommand	;Check command
	CPI	1
	JZ	DoStopToneCmd	;z =>  Stop Tone command
	LXI	H,TonePCB
	CALL	ReadCPBuffer	;Read ToneCSB

{  Command 0:  Start the Tone Generator. Set up the i8253  for the Tone frequency. Frequency divisor is in ToneFreq.}
DoStartToneCmd:
	MVI	A,KBBellMode	;Get 8253 Mode word
	DI			;No interrupts
	OUT	TimerMode	;Output to Timer
	LHLD	ToneFreq	;Get divisor
	MOV	A,L		;Least significant byte
	OUT	TimerCount1	;Output to Timer
	MOV	A,H		;Most significant byte
	OUT	TimerCount1	;Output to Timer
	EI			;Enable interrupts
	MVI	A,KBTone	;Set KBTone bit in MiscControl1
	CALL	SetMiscControl1Bit	;Enable Tone

{  Command completed.  Clear ToneCommand and transfer to main memory.}
TransferToneCSB:
	LXI	H,ZeroToneCommand	;Pointer to Port control block
	JMP	WriteCPBuffer	;Write main memory


DoStopToneCmd:
{Command 1:  Stop the Tone Generator.}
	MVI	A,nKBTone	;Clear bit in MiscControl1
	CALL	ClearMiscControl1Bit		;Disable Tone
	JMP	TransferToneCSB	;Clear ToneCommand

;
MainLoop:
{  If time is valid, check for time to update TOD. This switch is set by GetTOD. If the TOD is not valid because of a power failure, we will jump to CheckPortBusy.}
	DB	opJMP		;JMP CheckTOD
TODValidSwitch:
	DW	CheckTOD
;	JMP	CheckPortBusy

CheckTOD:
{  Check for a TOD interrupt. If there was an interrupt, increment the local clock. Clears TOD interrupt before returning.}
	IN	MiscInput1	;***Rev G and higher IOP
	ANI	TODIntMask	;TOD interrupt?
	STA	TODChangeFlag
	JZ	CheckPortBusy	;z => no interrupt, return

{  There was an interrupt.  Clear the interrupt.  Increment the local clock.}
	OUT	TODClr		;Clear TOD interrupt.
	LXI	D,1
	LHLD	TODLo
	DAD	D		;Increment low order by 1
	SHLD	TODLo
	SHLD	TODLox
	JNC	CheckPortBusy
	LHLD	TODHi		;If carry,
	DAD	D		;Increment high order by 1
	SHLD	TODHi

CheckPortBusy:
	DB	opMVIA		;A ← PortBusyFlag
PortBusyFlag:
	DB	0
	ORA	A
	JNZ	BookKeepingTaskYield	;Yield if Port is busy

	DB	opORI		;A ← TODChangeFlag
TODChangeFlag:
	DB	0
	JZ	ReadCSB		;z => not time yet for TOD transfer

	XRA	A
	STA	TODChangeFlag
	LXI	H,TODPCB	;Pointer to TOD CPport control block
	CALL	WriteCPBuffer	;Transfer to CP main memory

ReadCSB:
	LXI	H,IOPagePCB	;Point to CPport Control Block
	CALL	ReadCPBuffer	;Read CP main memory

;
{Check to see if the CP says the mouse coordinates have changed. If so, update the mouse X and Y coordinates, and clear MouseChgCSB.Full in main memory.}
	LDA	MouseChangeFlag
	ORA	A		;Set Flags
	JZ	CheckMouseHardware	;Jump if CP says no Mouse change
	CALL	ReadMouse	;Clear mouse hardware

{Reread the mouse change CSB to assure that the data is valid. Read MouseChgX into MouseXPosition and MouseChgY into MouseYPosition}
	LXI	H,MouseChgPCB	;Pointer to MP CPport control block
	CALL	ReadCPBuffer
	LXI	H,MouseChangeFlagPCB
	CALL	WriteCPBuffer	;Write CSB to CP memory
	JMP	UpdateMousePosition

CheckMouseHardware:
{Check mouse hardware to see if the mouse moved.}

	CALL	ReadMouse	;Coord. to MXTemp, MYTemp, switches in MouseSwitches
	JZ	CheckKeyboard

UpdateMousePosition:
{Transfer the mouse state to the CP.
  Mouse state to be transferred:
      MouseXPosition, MouseYPosition, MouseSwitches (3 words)}

	LXI	H,MousePCB	;Pointer to Mouse CPport control block
	CALL	WriteCPBuffer	;Transfer to CP main memory

CheckKeyboard:
	IN	IntReq		;Read interupt port
	ANI	KBReqMask	;KB request?
	CZ	ReadKeyboard	;z => request

CheckProcCSB:
	LDA	ProcCommand	;Check if bit 0 #0 (full)
	ORA	A		;Set Flags
	CNZ	DoProcCmd	;Do the Processor CSB command

CheckToneCSB:
	LDA	ToneCommandFlag	;Check if bit 0 #0 (full)
	ORA	A		;Set Flags
	CNZ	DoToneCmd	;Do the Tone CSB command


BookKeepingTaskYield:
{Pass control to the next task specified in Domino.cfg}
	DS	0
	END	BookKeepingTask