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

Last change by
Chuck Fay 15-Nov-82 16:04:28: Always post status, whether it's different or not;
	moved test of PortBusyFlag to top so that it's tested whether or not there
	is a command to be sent.
Jim Frandeen September 9, 1982  1:19 PM: Zero status after each command.
Jim Frandeen August 5, 1982  12:20 PM: New IO Page format.
Jim Frandeen May 3, 1982  7:41 AM: reorganize for new tasking
Written by Roy Ogus}

;  DEFINITION FILES:

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


;  IMPORTS/EXPORTS:

	IMP	DoNakedNotify	; From CPSubs
	IMP	LSEPData	; From Common
	IMP	LSEPStatus	; From Common
	IMP	PortBusyFlag	; From CPSubs
	IMP	ReadCPBuffer	; From CPSubs
	IMP	WriteCPBuffer	; From CPSubs
	IMP	Wait		; From Common
	IMP	ZeroCommand	; From Common


LSEPConsumerTask:
{We can't send a command or read status unless the CP port is free.}
	LDA	PortBusyFlag
	ORA	A
	JNZ	LSEPProducerTaskYield	;  nz => CP port busy, skip to next task

	LDA	LSEPData	;  Check if data #0
	ora	a		;  Set Flags
	JZ	LSEPConsumerTaskYield

	DB	opJMP		
LSEPConsumerTaskResumeAddress:
	DW	InitializeLSEPConsumerTask
;	JMP	LSEPConsumerLoop



;  Internal Definitions.

LSEPCSB:
{The LSEP CSB is read in the first time LSEPData is non zero. }

LSEPCtlMask:
{This mask is used to issue a NakedNotify when a control character has been sent to the LSEP. This mask is only read in once, the first time a control character is sent to the LSEP.}
	DS	2		;1401F
	
LSEPStatMask:
{This mask is used to issue a NakedNotify when a status byte has been sent to the CP. This mask is only read in once, the first time a control character is sent to the LSEP.}
	DS	2		;14020
	
PTDelay	equ	13H	;  Wait value of ~ 100 usec for UART settle
;  Printer UART Baud rate generator programming
SC0	equ	00H	;  Counter 0
RL3	equ	30H	;  Read/Load least significant, then most significant data
Mode3	equ	6	;  Mode 3 - square generator
BRGMode	equ	SC0+RL3+Mode3	;  Mode byte for 8253 BRG Clk
Freq:
	db	3		;  Frequency: 0=110Hz, 1=300Hz, 2=1200Hz, 3=9600Hz
				;  Frequency: 4=19.2KHz
Div110:
	dw	1047		;  Divisor for 110Hz (16X)
Div300:
	dw	384		;  Divisor for 300Hz (16X)
Div1200:
	dw	96		;  Divisor for 1200Hz (16X)
Div9600:
	dw	12		;  Divisor for 9600Hz (16X)
Div19200:
	dw	6		;  Divisor for 19200Hz (16X)
Div28800:
	dw	4		;  Divisor for 28800Hz (16X)

;  8251A UART data.

PTErrorMask	equ	38H	;  Framing, overrun, parity errors
BreakDetMask	equ	40H	;  Break Detect bit
PTCmdER	equ	10H	;  Command bit to clear errors
Reset0		equ	80H	;  First character of reset sequence (see notes)
Reset1		equ	00H	;  Second character of reset sequence
Reset2		equ	40H	;  Third character of reset sequence
;InvalidChar		equ	4EH	;  Invalid character as Raven data
InvalidChar		equ	7EH	;  Invalid character for bad RxStatus
PTMode:
	EQU	5AH	;  1 stop, odd parity, 7 bits, 16X
PTCmdTx:
	db	23H	;  Command, enable Tx only
PTCmdTxRx:
	EQU	27H	;  Command, enable Tx, Rx

;  Port Control Block values to write zero into data word of CSB.
LSEPZeroDataPCB:
	dw	LSEPDataLoc	;  CP buffer pointer (low): LSEP Ctl Data
	dw	CPIOPageHi	;  CP buffer pointer (high)
	dw	2		;  CP buffer count (bytes)
	dw	ZeroCommand	;  Pointer to IOP LSEP Ctl CSB status

;  Port Control Block values to read LSEP CSB.
LSEPReadCSBPCB:
	dw	LSEPCSBLoc	;  CP buffer pointer (low): LSEP Ctl Data
	dw	CPIOPageHi	;  CP buffer pointer (high)
	dw	LSEPCSBSize		;  CP buffer count (bytes)
	dw	LSEPCSB		;  Pointer to IOP LSEP Ctl CSB status

;  Control Block values to transfer status to IOPage.
LSEPStatusPCB:
	dw	LSEPStatusLoc	;  CP buffer pointer (low): LSEP Ctl CSB status
	dw	CPIOPageHi	;  CP buffer pointer (high)
	dw	2		;  CP buffer count (bytes)
	dw	LSEPStatus	;  Pointer to IOP LSEP Status CSB status
;
InitializeLSEPConsumerTask:
{  Initialize the LSEPBaud rate generator and the LSEP UART. This sequence will guarantee correct initialization after both a hardware (expecting Mode), or a software (expecting Command) reset. The enable Rx,Tx command is sent to the UART. Set the 8253 printer UART clock according to value in Freq. Freq: 0=110Hz, 1=300Hz, 2=1200Hz, 3=9600Hz, 4=19.2 kHz, 5=28.8 kHz. }

	lda	Freq
	rlc			;  Multiply by 2
	mov	E,a
	mvi	D,0		;  2*index in B,C
	di			;  No interrupts
	mvi	a,BRGMode	;  Get 8253 Mode word
	out	LTimerMode	;  Output to Timer
	lxi	h,Div110
	dad	D		;  Point to divisor
	mov	a,m		;  Least significant byte
	out	LTimerCount0	;  Output to Timer
	inx	h
	mov	a,m		;  Most significant byte
	out	LTimerCount0	;  Output to Timer
	ei			;  Enable interrupts

	LXI	H,8000H+LPrinterCommand	;memory mapped IO
	MVI	M,Reset0	;  
	MVI	M,Reset1	;  
	MVI	M,Reset2	;  
	nop			;  Needed for B-step chips
	MVI	M,PTMode	;  Mode value (assumed asynchronous)
	MVI	M,PTCmdTxRx	;  Command to UART for Send/Receive

	LXI	H,LSEPConsumerLoop
	SHLD	LSEPConsumerTaskResumeAddress
	LXI	H,BeginLSEPProducerTask
	SHLD	LSEPProducerTaskResumeAddress
	
{Read in the LSEP CSB. We read this only once at initialization time. It contains the Naked Notify masks.}
	LXI	H,LSEPReadCSBPCB
	CALL	ReadCPBuffer
;
;  This is where the Task will start after the LSEP has been initialized.
LSEPConsumerLoop:
;  Send the byte to the Raven.
;  The data to be sent is in LSEPData.
;  Wait for transmitter to be ready.  Yield if not.
	in	IntReq		;  Check for Tx Ready flag.
	ani	LSEPTxReqMask
	JNZ	LSEPConsumerTaskYield			;  z =>  Tx is ready

	lda	LSEPData		;  Get next data item
	out	LPrinterData	;   and output it

;  Transfer empty status to CP.
;  Clear Full flag in Status
	lxi	h,LSEPZeroDataPCB	;  Point to the CPport control block
	call	WriteCPBuffer	;  Write CP main memory
	
	LHLD	LSEPCtlMask
	CALL	DoNakedNotify

LSEPConsumerTaskYield:
{Pass control to the LSEP Producer Task}
;

	DB	opJMP	
LSEPProducerTaskResumeAddress:
	DW	LSEPProducerTaskYield
;	JMP	BeginLSEPProducerTask

BeginLSEPProducerTask:
;  Read the data from the UART.
;  Check if a character is ready. If we get here, we know the port is free.
	in	IntReq		;  Check for Tx Ready flag.
	ani	LSEPRxReqMask
	JNZ	LSEPProducerTaskYield	; Z => character ready

	in	LPrinterData	;  Read in character
	sta	LStatus		;  Save in local
	mvi	E,3		;  Counter for bad status tries
RxCharLoop:
	in	LPrinterStatus	;  Read in status
	sta	UARTStatus	;  Store away
	ani	PTErrorMask
	jz	GoodRxData
;  Not good status, try again.
	dcr	E
	jnz	RxCharLoop


;  Bad status received from the UART is in A.
;  Reset the errors in the UART and pass InvalidChar as the status.
BadPTRxStatus:
	MVI	A,InvalidChar
	STA	LStatus	
	MVI	A,PTCmdTxRx+PTCmdER	;  Command to UART for Send/Receive
				;  Plus Set ClearErrors
	out	LPrinterCommand
	lxi	h,PTDelay  
	call	Wait		;  Wait for UART to settle
	MVI	A,PTCmdTxRx	;  Send command again
	out	LPrinterCommand
	lxi	h,PTDelay
	call	Wait		;  Wait for UART to settle

;  Check if BreakDetect was on.
	DB	opMVIA		;A ← UARTStatus
UARTStatus:
	DB	0
	ani	BreakDetMask
	jnz	LSEPProducerTaskYield	; nz => a break, ignore character
;  If no break detect, continue.

;  The character was received with no Uart errors.  
GoodRxData:
	DB	opMVIA		;A ← new status
LStatus:
	DB	0

	MOV	E,A		;E ← new status
	lda	LSEPStatus	;A ← previous status from IO Page
	ora	a		;Test for zero
	MOV	A,E		;A ← new status
	JZ	SendLSEPStatus	;Send new status

;If the previous status has not been processed, set overflow.
	MVI	A,7FH
	
SendLSEPStatus:
	STA	LSEPStatus	
	lxi	h,LSEPStatusPCB	;  Point to CPport Control Block
	call	WriteCPBuffer	;  Write CP main memory
	LHLD	LSEPStatMask
	CALL	DoNakedNotify


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


	END	LSEPTask