CSCHAR	EQU	(Offset $)
   CSEG	0
	ORG	CSCHAR
;****************************************************
;*                                                  *
;*  CHARACTER DEVICE I/O DRIVERS                    *
;*                                                  *
;****************************************************
;
; CONIN - Entry : console number is in CL
;         Exit : Character is returned in AL 
;
CONIN:	MOV	DL,CL		;Console number into DL
	PUSH	DX		;Save console number
	CALL	GETBUF		;Get address of this consoles buffer
	POP	DX		;Get console number back
	CMP	AH,0		;See if device exists
	JE	CONI5		;Exit if no no device
	TEST ES:Byte Ptr B_FLAGS[SI],B_IWAIT ;See if a process was waiting
	JZ	CONI1		;Continue if not
	PUSH	SI		;Save out registers for MP/M call
	PUSH	DI
	PUSH	DX
	PUSH	AX		
	CALL	WAKEUP		;MP/M bug fix abort on sleep DEBUG
	POP	AX
	POP	DX
	POP	DI
	POP	SI
;
CONI1:	MOV	BL,ES:B_ITAIL[SI]	;Get tail 
	CMP	ES:B_IHEAD[SI],BL	;See if any characters to get
	JNE	CONI2		;Yes there are characters
	OR  ES:Byte Ptr B_FLAGS[SI],B_IWAIT ;Set waiting for input flag
	CALL	SLEEP		;Go to sleep until a character in the buffer
CONI2:	CALL	NEXT		;Find next slot to get char from
	XOR	BH,BH		;Clear BH for indexing
	MOV	AL,ES:B_IBUF[SI+BX]  ;Get char from buffer, BX is index in buffer
	MOV	ES:B_ITAIL[SI],BL	;Store new tail
;	AND	AL,07FH		;Lets not strip parity
CONI5:	RET
;
; Console status - Return input status of selected console
;    Entry : CL = Desired console number
;    Exit : AL = 0 if no character ready, AL = 0FF if character is ready
;
CONST:	MOV	DL,CL		;Desired console number in DL
	PUSH	DX
	CALL	GETBUF		;Get offset of desired buffer
	POP	DX
	CMP	AH,0		;See if device existed
	JE	CONS1		;Exit if not
	MOV	BL,ES:B_IHEAD[SI]	;Get head pointer
	XOR	AX,AX		;Clear AX for return
	CMP	ES:B_ITAIL[SI],BL	;See if any character in buffer
	JE	CONS1		;Exit with 0 if no characters
	DEC	AX		;Make AX = 0FFFF
CONS1:	RET
;
; CIS - called from within XIOS to check if a char has                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 	*
;*								*
;****************************************************************

CONOUT: MOV     DH,CL
	PUSH	DX
	CALL	GETBUF		;Get info on device
	POP	DX
	CMP	AH,0		;Check if no device (major = 0)
	JE	CONO13

CONO0:	TEST  ES:Byte Ptr B_FLAGS[SI],B_OWAIT ;Clear flag in case of abort on sleep
	JZ	CONO7
	AND  ES:Byte Ptr B_FLAGS[SI],not B_OWAIT
	PUSH	SI
	PUSH	DI
	PUSH	AX
	PUSH	DX
	CALL	WAKEUP		;MP/M Bug fix on abort on flag wait. DEBUG
	POP	DX
	POP	AX
	POP	DI
	POP	SI

CONO7:	PUSHF			;Save flags so we can shut off interrupts
	CLI	
	MOV	BX,ES:B_OHEAD[SI]	;Head -> (bl), Tail -> (bh)
	CMP	BL,BH		;Check if buffer empty
	JE	SPECOUT		;Buffer is empty so try to output now
CONO8:	CALL	NEXT		;Next pointer for head (bl)
	CMP	BL,BH		;Check if head++ == tail then buffer full
	JNE	CONO1		;Buffer not full
	
	OR  ES:Byte Ptr B_FLAGS[SI],B_OWAIT ;Set wait for buffer empty flag on
	CALL	SLEEP		;Goto sleep till buffer becomes unclogged
	CLI			;Turn interrupts back off

CONO1:	XOR	BH,BH		;Clear BH for indexing
	MOV	ES:B_OBUF[SI+BX],DH  ;Store new character into the buffer
	MOV	ES:B_OHEAD[SI],BL 	;Restore new head pointer
	TEST  ES:Byte Ptr B_FLAGS[SI],(B_EVENT + B_ON) ;See if Tx interrupts are on
	JZ	CONO2		;Go turn on TX interrupts
	POPF			;Restore interrupts
CONO13:	RET

SPECOUT:TEST  ES:Byte Ptr B_FLAGS[SI],B_EVENT ;If process is waiting
	JNZ	CONO8			;Go put char in buffer	

	PUSH	AX
	PUSH	BX
	PUSH	DX
	CALL Word Ptr D_OSTAT[DI]	;Check output status
	OR	AL,AL		;Check if output ready
	POP	DX
	POP	BX
	POP	AX
	JE	CONO8		;Output not ready so put char in buffer

	TEST  ES:Byte Ptr B_FLAGS[SI],B_HARD ;See if hardware handshaking is on
	JZ	CONO9		;Skip hardware check if so

	PUSH	ax
	PUSH	bx
	PUSH	dx
	MOV	CH,0h
	MOV	CL,DH
	CALL Word Ptr D_READY[DI]	;Check output status
	OR	AL,AL		;Check if output ready
	POP	DX
	POP	BX
	POP	AX
	JZ	CONO8		;Hardware not ready so put char in buffer

CONO9:	TEST  ES:Byte Ptr B_FLAGS[SI],B_SOFT	;Is xon/xoff in effect
	JZ	CONO4		;If not output character
	CMP  ES:Byte Ptr B_CNTL[SI],XON	;Is current state XON
	JNE	CONO8		;No, put char in buffer until XON


CONO4:	CALL	NEXT
	XOR	BH,BH
	MOV  ES:B_OBUF[SI+BX],DH  ;Put char in buffer anyway
	MOV	BH,BL
	MOV  ES:B_OHEAD[SI],BX	;Set head and tail equal
	MOV	CL,DH		;Get char in CL 	
	CALL Word Ptr D_OUT[DI] ;Output the character
CONO3:	POPF
	RET	

CONO2:	TEST  ES:Byte Ptr B_FLAGS[SI],B_SOFT  ;See if xon/xoff enabled
	JZ	CONO10			;Continue if no xon/xoff
	CMP  ES:Byte Ptr B_CNTL[SI],XON	;If control char is xon
	JNE	CONO11			;go ahead and turn on tx ints
CONO10:	OR  ES:Byte Ptr B_FLAGS[SI],B_ON   ;Set output interrupt flag on
	CALL Word Ptr D_START[DI]	;Enable output interrupts
CONO11: POPF
	RET

;****************************************************************
;*								*
;* GETBUF - Return address of buffer for selected device.	*
;*								*
;* Enter: 	(dl) = MP/M device number. (High bit set if	*
;*		list device)					*
;*								*
;* Returns:	(si) = Start of buffer				*
;*              (ES) = Segment of buffer                        * 
;*		(di) = Start of DD				*
;*		(al) = port address 				*
;*		(ah) = major device number			*
;*								*
;****************************************************************

GETBUF:	MOV	BX,Offset CONTAB ;Get start of major/minor device table
	TEST	DL,80h		;Check if list device
 	JZ	GET0		;Use console table
	ADD	DL,NUMCON	;Add offset to list table
GET0:
	AND	DX,7Fh		;Strip off high bit, clear DH
	MOV	CL,4		;Put 4 in CL for shifts 
	MOV	DI,DX		
	SHL	DI,1		;Multiply device number by two for 16 bit major/minor
	MOV	AX,[BX+DI]	;Load major/minor for selected device -> (ax)
	PUSH	AX		;Save major/minor for return
	MOV	AL,AH		;Major -> (al)
	XOR	AH,AH		;Clear AH
	CMP	AL,3		;See if device is a slave
	JL	GET2		;Jump on if not
	MOV	AL,3		;All slaves are entry 3
GET2:	SHL	AX,CL		;Shift AX left 4 times (*16) 16 bytes in each DD entry 
	ADD  AX,Offset DD_TBL  	;Offset to start of DD for selected device   
	MOV	DI,AX           ;Start of DD for device -> (ax)              
	POP	AX		;Get major/minor numbers back
	CMP	AH,4		;See if device is a spuz
	JL	GET1		;No, is a motherboard I/O device
	MOV	SI,AX		;Get major device number in SI 
	AND	SI,0FF00H	;Clear lower byte of SI
	SHL	SI,CL		;Put page number in upper nibble
	ADD	SI,8000h	;Must determine memory location of Zslave
	MOV	ES,SI		;All buffer access will be in new page
	MOV	SI,ZBUF		;Put address of buffer offset in slave in SI
	RET

GET1:	MOV	SI,DS
	MOV	ES,SI		;Insure that ES points to local DATA segment 
	MOV  SI,Offset BUF 	;Get start of all 4 buffers in SI
	AND	DX,3		;Remove all but lower 2 bits to get number 0-3
	ADD	SI,DX		;Add number of device to start of buffer space
	RET

;****************************************************************
;*								*
;* NEXT - Increment circular buffer pointer and wrap around	*
;*        to beginning if overflow.				*
;*								*
;* Enter: 	(bl) pointer					*
;* 								*
;* Returns:	(bl) new buffer pointer				*
;*								*
;****************************************************************

NEXT:	INC	BL		;Move pointer up one
	CMP	BL,IOBLEN	;See if wrap around end of buffer
	JNE	INCO1		;Done if no wrap around 
	XOR	BL,BL		;Put pointer back at start
INCO1:	RET

;****************************************************************
;*								*
;* SLEEP - Set an character I/O process to sleep.  This is used	*
;* 	   by a process to wait for an event that an interrupt 	*
;*	   service routine will handle.   Then the process will	*
;*	   be brought alive after the event has been satisfied.	*
;*	   							*
;* Enter:  (dl) console number. High bit set for printer.	*
;*								*
;****************************************************************

SLEEP:	PUSH	AX		;Save all registers during MP/M call
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	SI
	PUSH	DI

	TEST	DL,80h		;Check if its a list device
	JZ	SLEEP1		;Jump if not list device 
	AND	DL,7fh		;Strip off high bit 
	ADD	DL,F_LIST-F_CON	;Add list number to starting list flag-con flag
SLEEP1:	ADD	DL,F_CON	;Add console number to starting flag number
SLEEP2:	MOV	CL,FLGWAIT	;Call MP/M wait function
	CALL	SUPIF

	POP	DI		;Restore registers
	POP	SI
	POP	DX
	POP	CX
	POP	BX
	POP	AX
	RET

;****************************************************************
;*								*
;* WAKEUP - Wakeup a process that has gone to sleep on an	*
;*	    event.						*
;*								*
;* Enter:   (dl) console number of process to wakeup. High bit	*
;*	    set if printer.					*
;*								*
;****************************************************************

WAKEUP:	TEST	DL,80h		;Check if device is a printer or console
	JZ	WAKE1		;Its a console
	AND	DL,7Fh		;Strip off high bit
	ADD	DL,F_LIST-F_CON	;Add in List flag start 
WAKE1:	ADD	DL,F_CON	;Add in Console flag start
WAKE2:	MOV	CL,FLGSET	;Call MP/M flag set function
	JMP	SUPIF


;****************************************************************
;*								*
;* LOOKUP - Look up a major/minor device in the console and	*
;*	    list table then return device number.  		*
;*								*
;* Enter:   (ax) major/minor					*
;*								*
;* Returns: (dl) device number					*
;*								*
;****************************************************************

LOOKUP:	PUSH	ES		;Save segment of buffer
	MOV	CX,CS
	MOV	ES,CX		;Set ES=CS
	MOV	DI,Offset CONTAB	;First search console table
	MOV	CX,NUMCON	;Number of entries in CX
	MOV	DX,CX		;Also put in DX for later
	CLD			;Clear direction flag
	REPNE	SCASW		;If zero flag not set we did not find it
	JNE	LOOK1		; so go check printer table
	SUB	DX,CX		;See how many we did
	DEC	DX
	POP	ES
LOOK0:	RET

LOOK1:	;LSTTAB follows CONTAB so DI is already set 
;	MOV	DI,Offset LSTTAB ;Now search list table
	MOV	CX,NUMLST	;Number of entries in CX
	MOV	DX,CX		;Also put in DX for later
	REPNE	SCASW
	JNE	LOOK2		;Not found in either table
	SUB	DX,CX		;See how many we did
	DEC	DX
	OR      DL,80H     	;Set high bit for printer
	POP	ES
	RET			;Entry found so exit
LOOK2:	POP	ES
	MOV	BX,Offset PNCLOOK 
	JMP	PANIC		;Can't find device

	
    eject

;****************************************************************
;*								*
;* IPOLL - Called from interrupt routines to do internal XIOS	*
;*	   polling.  This will cause the device to be polled 	*
;*	   at every clock tick and when the poll routine says	*
;*	   that the device is ready it will execute the		*
;*	   selected handler to wake up device.			*
;*								*
;* Enter:  (ax) Major/Minor					*
;*	   (cx) Routine to call when device ready		*
;*	   (dx) Address of output ready routine			*
;*								*
;****************************************************************

IPOLL:	PUSH	CX
	MOV	BX,Offset IPTAB	;Start of internal poll table
	MOV	CX,ISLOTS	;Number of slots
IPOLL1:	CMP Word Ptr [BX],0	;Is this slot empty
	JE	IPOLL2		;Yes, go fill it
	ADD	BX,6		;Point to next slot
	LOOP	IPOLL1		;Loop until all slots checked
	POP	CX
	MOV	BX,Offset IPMESS	;No more slots
	JMP	PANIC
	

IPOLL2:	POP	CX		;Recover address of handler
	PUSHF
	CLI
	MOV	0[BX],AX	;Store major/minor
	MOV	2[BX],CX	;Store ready handler
	MOV	4[BX],DX	;Store ready status routine
	INC  Byte Ptr ACTIVE	;Number of active polling processes
	POPF
	RET

CLKPOLL:CMP  Byte Ptr ACTIVE,0	;Check if an devices to poll
	JNE	CLKP1
	RET

CLKP1:	MOV  CL,Byte Ptr ACTIVE
	MOV	BX,Offset IPTAB	;Start of internal poll table
	MOV	AX,islots	;Number of poll slots
CLKP2:	CMP Word Ptr [BX],0	;Check if poll entry for current slot
	JE	CLKP3		;if major/minor = 0 then empty slot
	PUSH	AX
	PUSH	BX
	PUSH	CX
	MOV	AX,0[BX]	;Pick up major/minor
        MOV	CH,0
	CALL Word Ptr 4[BX]	;Call output ready status routine
	CMP	AL,0		;Check if not ready
	POP	CX
	POP	BX
	POP	AX
	JE	CLKP4		;Not ready, skip to next entry
	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSHF
	CLI
	MOV	AX,0[BX]	;Pick up major/minor -> (ax)
	MOV	DX,2[BX]	;Address of handler -> (dx)
	MOV Word Ptr [BX],0	;Free up slot by putting 0 -> major/minor
	DEC  Byte Ptr ACTIVE	;Count down number of devices to poll
	POPF
	CALL	DX
	POP	CX
	POP	BX
	POP	AX
CLKP4:	DEC	CL		;Check if all devices polled
	JNZ	CLKP3		;Still more
	RET

CLKP3:	ADD	BX,6		;Offset to next slot in table
	DEC	AL		;Count down max number of slots left
	JNZ	CLKP2
	RET
	
;****************************************************************
;*								*
;* INTOUT - Output a character to a device.  Called from an	*
;*	    output interrupt routine. This routine will check	*
;*	    if there are any more characters left to print in   *
;*	    the buffer.  If so then it will call the low level	*
;*	    device driver to print the character.  If the 	*
;*	    buffer is empty then it will check if the process	*
;*	    waiting for output to drain and wakeup if so.	*
;* 								*
;* Enter:   (ax) major/minor device number			*
;*								*
;****************************************************************

SSPACE	equ	10		;Space to reserve on the stack

;		
; Offsets off the stack (BP)
;

MM	equ	0		;Major/minor
MPMCON	equ	2		;MP/M console number
SISAVE	equ	4
DISAVE	eq                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                FLAGS[SI],B_EVENT

	MOV	AX,MM[BP]
	CALL Word Ptr D_STOP[DI]	;Stop output till DTR ready

	PUSH	ES
	POP	DS		;Restore DS to be same as CS=ES 
	MOV	AX,MM[BP]	;And poll until DTR is ready
	MOV	DX,D_READY[DI]
	MOV	CX,Offset ESTART
	CALL	IPOLL		;Call Poll routine to check DTR every clock tick
	JMP	INTRET		;And done
	
INIO2:	MOV	DI,DISAVE[BP]
	MOV	CL,CHAR[BP] 	;Pick up character to print
	MOV	AX,MM[BP]
	CALL Word Ptr D_OUT[DI]	;Print the character
INIO3:	MOV	SI,SISAVE[BP]
	MOV	BL,NEWTAIL[BP]
	MOV	ES:B_OTAIL[SI],BL ;Set new tail
	CMP	ES:B_OHEAD[SI],BL ;Checë iæ buffeò empty
	JNE	INTRET		;If not equal all done

OEMPTY:	TEST  ES:Byte Ptr B_FLAGS[SI],B_OWAIT ;If buffer has gone empty and
	JZ	OEM1		; someone is waiting, wake them up
	AND  ES:Byte Ptr B_FLAGS[SI],not B_OWAIT  ; and turn off flag
	MOV	DL,MPMCON[BP]
	CALL	WAKEUP
	MOV	SI,SISAVE[BP]
OEM1:	MOV	DI,DISAVE[BP]
	AND  ES:Byte Ptr B_FLAGS[SI],not B_ON
	MOV	AX,MM[BP]	;Buffer is empty so
	CALL Word Ptr D_STOP[DI]	;Disable further output interrupts

INTRET:	ADD	SP,SSPACE	;Restore stack
	RET
	
;****************************************************************
;*								*
;* INTIN - Input a character from a device and place in input	*
;*	   buffer if room.   if the process is waiting for the	*
;*	   input then wake up the sleeping process so it can	*
;*	   pick up the character and continue executing.	*
;*								*
;* Enter:  (ax) major/minor of the device with a character.	*
;*								*
;****************************************************************

INTIN:	PUSH	AX		;Save major/minor
	CALL	LOOKUP		;Lookup device in console/list tables
	PUSH	DX		;Save console number
	CALL	GETBUF		;Get address of buffer/DD
	MOV	BL,ES:B_IHEAD[SI]	;Pick up head of input pointer
	CALL	NEXT
	POP	DX
	POP	AX		;Save major/minor
	PUSH	AX
	PUSH	SI
	PUSH	BX
	PUSH	DX
	CALL Word Ptr D_INPUT[DI]	;Read character 
	POP	DX
	POP	BX
	POP	SI
	TEST  ES:Byte Ptr B_FLAGS[SI],B_SOFT	;See if xon-xoff handshaking in use
	JZ	INTIN4		;Go on if no XON/XOFF 
	MOV	AH,AL
	AND	AH,07FH
	CMP	AH,XOFF 	;Was char XOFF
	JE	INTIN3		;Go process if so
	CMP	AH,XON		;Else was it XON
	JNE	INTIN4		;Go on if not XON

	MOV	ES:B_CNTL[SI],AH	;Save current state as XON
	MOV	AX,ES:B_OHEAD[SI]	;Get head & tail pointers
	CMP	AL,AH		;See if any chars in output buffer
	POP	AX		;Fix stack now
	JE	INTIN2		;If no chars dont turn ints on
	OR  ES:Byte Ptr B_FLAGS[SI],B_ON	;Set Tx ints as on
	CALL Word Ptr D_START[DI]	;And start up transmit interrupts
	RET			;and all done

INTIN3:
	MOV	ES:B_CNTL[SI],AH	;Save current state as XOFF
	AND ES:Byte Ptr B_FLAGS[SI],not B_ON	;Turn Tx ints off
	POP	AX		;Get major/minor back
	CALL Word Ptr D_STOP[DI]	;Turn off transmit interupts
	RET			;and done
	
INTIN4:
	CMP	BL,ES:B_ITAIL[SI]	;Check if buffer full
	JE	FULL		;If full forget character
	SUB	BH,BH		;Clear BH for indexing
	MOV	ES:B_IHEAD[SI],BL	;Store new head
	MOV	ES:B_IBUF[SI+BX],AL ;Store the character in the buffer
FULL:	POP	AX		;Fix stack
	TEST  ES:Byte Ptr B_FLAGS[SI],B_IWAIT ;Is anyone waiting
	JZ	INTIN2		;No exit now
	AND  ES:Byte Ptr B_FLAGS[SI],not B_IWAIT ;Someone was waiting, turn off flag  
	JMP	WAKEUP		;Go call MP/M
INTIN2:	RET


    eject

;****************************************************************
;*								*
;* PLIST - Output to the selected printer device.  This goes 	*
;*         thru standard console output.			*
;*								*
;* Enter:	(cl) Character to output			*
;*		(dl) Printer number to output			*
;*								*
;****************************************************************

PLIST:	OR	DL,80h		;Set high bit for printer
	JMP	CONOUT


;****************************************************************
;*								*
;* LISTST - List output buffer status.  This checks if the	*
;*	    output buffer is full.  If so then the printer	*
;*	    is not ready to accept the next character else	*
;*	    the printer is ready.				*
;*								*
;* Enter:	(cl) Printer number				*
;*								*
;****************************************************************

LISTST:	MOV	DL,CL		;Printer number -> (dl)
	CALL	GETBUF		;Get info on device
	CMP	AX,0FFFFH	;Check if no device AX=FFFF
	JNE	LISTS2

LISTS1:	MOV	BX,ES:B_OHEAD[SI]	;Head -> (bl), Tail -> (bh)
	CALL	NEXT		;Next pointer for head (bl)
	XOR	AX,AX		;0 = Not ready
	CMP	BL,BH		;Check if head++ == tail then buffer full
	JE	LISTS2		;Buffer full
	DEC	AX		;ffff = list device ready
LISTS2:	RET

;****************************************************************
;*								*
;* MAXCON - Return to MP/M the number of consoles configured in	*
;* 	    the XIOS.  This now returns the number specified	*
;*	    in gensys.						*
;*								*
;* Returns:	(ax) Number of consoles				*
;*								*
;****************************************************************

MAXCON:	MOV	AL,Byte Ptr GCON  ;Pick up number of consoles from SYSDAT
	XOR	AH,AH		;Maximum number is 255
	RET

;****************************************************************
;*								*
;* MAXLIST - Returns number of list devices in the XIOS.  See	*
;*	     MAXCON above for more info.			*
;*								*
;* Returns:	(ax) Number of printers				*
;*								*
;****************************************************************

MAXLIST:MOV  AL,Byte Ptr GLST	;Get number of system consoles from SYSDAT 
	XOR	AH,AH		;Maximum number is 255
	RET

;****************************************************************
;*								*
;* START  - Enable output ready interrupts for selected device.	*
;* ESTART - Same as start, it is used to start up a device that	*
;*	    was waiting for an event.  It will clear the event	*
;*	    flag.						*
;*								*
;* Enter:  (ax) major/minor of device to enable interrupts	*
;*								*
;****************************************************************

START:	PUSH	AX		;Save Major/Minor
	CALL	LOOKUP		;Look up device
	CALl	GETBUF		;Get buffer/DD
	POP	AX		;Restore Major/Minor
	JMP Word Ptr D_START[DI]	;Call device start routine

ESTART:	PUSH	AX
	CALL	LOOKUP
	CALL	GETBUF
	POP	AX
	AND ES:Byte Ptr B_FLAGS[SI],not B_EVENT ;Turn on event flag
	JMP Word Ptr D_START[DI]	;Call device start routine

;****************************************************************
;*								*
;* STOP - Disable output ready interrupts for selected device.	*
;*								*
;* Enter: (ax) major/minor of device to enable interrupts	*
;*								*
;****************************************************************

STOP:	PUSH	AX
	CALL	LOOKUP		;Get address of device's handler
	CALL	GETBUF		;Get device's buffer
	POP	AX
	JMP Word Ptr D_STOP[DI]	;Call device stop routine

;****************************************************************
;*								*
;* BINIT - Initialize buffer pool.  Set up each buffer to be in	*
;*	   initial state of the heads and tails pointer at same	*
;*	   spot and clear out flags.				*
;*         This routine only does the buffers in the local      *
;*         segment 						*
;*								*
;****************************************************************

BINIT:	MOV	BX,Offset BUF		;Start of buffer area
	MOV	CX,NBUF		;Number of buffers to initialize
BINIT1:	MOV  Byte Ptr B_IHEAD[BX],0	;Set heads and tails equal
	MOV  Byte Ptr B_ITAIL[BX],0
	MOV  Byte Ptr B_OHEAD[BX],0
	MOV  Byte Ptr B_OTAIL[BX],0
	MOV  Byte Ptr B_FLAGS[BX],B_NONE  ;Set no handshaking
	MOV  Byte Ptr B_CNTL[BX],XON	  ;Set as XON	
	ADD	BX,BLEN		;Add to next buffer
	LOOP	BINIT1
	RET

     eject

;****************************************************************
;*								*
;* XFUNC0 - Extended XIOS function.  This is for using internal	*
;* 	    sub-routines to execute a function or return info	*
;*	    in a more standard function.			*
;*								*
;* Enter:	(ch) Extended function number			*
;*		(cl),(dx) Arguments to extended function	*
;*								*
;* Returns:	(ax) return argument				*
;*								*
;****************************************************************

XFUNC0:	SUB	BH,BH		;Clear BH
	MOV	BL,CH		;Call number -> (bx)
	SAL	BX,1		;* 2 for 2 byte pointers
	JMP	XFNTAB[BX]
	

;****************************************************************
;*								*
;* STTY - Extended function to set baud rates, number of data	*
;*	  bits, number of stop bits, parity selection and the	*
;*	  type of handshaking on a selected printer or console.	*
;*								*
;* Enter:	(cl) = Console number. High bit for printer.	*
;*		(dl) = Bits 0-3 select baud rate.		*
;*				0 = 9600			*
;*				1 = 110				*
;*				2 = 300				*
;*				3 = 600				*
;*				4 = 1200			*
;*				5 = 1800			*
;*				6 = 2400			*
;*				7 = 4800			*
;*				8 = 9600			*
;*				9 = 19200			*
;*								*
;*		(dh) Handshaking				*
;*			Bits 0 & 1 select handshaking.		*
;*				0 = No handshaking		*
;*				1 = DTR 			*
;* 								*
;****************************************************************

STTY:	PUSH	DX
	PUSH	CX
	MOV	DL,CL		;Move console number -> (dl)
	CALL	GETBUF		;Get info on selected device
	CMP	AH,0		;Check if device exsists
	JNE	STTY1		;Yes
	POP	CX		;No, return error code
	POP	DX
	MOV	AX,0FFFFh	;(ax) = ffff for error (No such device)
	RET

STTY1:	POP	CX
	POP	DX
	AND	DH,03h		;Only 0-3 for handshaking
	PUSH	DX
	ROR	DH,1
	ROR	DH,1		;Move lower to bits to upper two bits
	MOV	BL,ES:B_FLAGS[SI]	;Pick up flags
	AND	BL,B_MODE xor 0FFh
	OR	BL,DH		;Or in new handshaking
	MOV	ES:B_FLAGS[SI],BL	;Resave flags
	MOV	CL,DL		;Baud rate... -> (cl)
	POP	DX
	CALL Word Ptr D_SET[DI]	;Call low level device driver to set baud rate...
	SUB	AX,AX		;(ax) = 0 for no error
XFUNC1:	RET


;****************************************************************
;* 							    	*	
;* Physical device drivers for hardware character devices       *
;*								*
;****************************************************************
;
; MP/M 8-16 driver for the three 2651 UARTs on the Model 10 motherboard
;   These are speced as 2 printers and one modem, but can be used for
;    consoles
;
;------------------------------
; Recieve interrupt service routines
UART0RX:
	PUSH	DX
	MOV	DX,Offset UR0X	;Address of UART0 service
	JMP	SERVICE
;
UR0X:	MOV	AL,UART0D	;Port number for this uart is minor
	MOV	AH,1		;Major number
	JMP	INTIN		;Go to service routine
;
UART1RX:
	PUSH	DX
	MOV	DX,Offset UR1X	;Address of UART0 service
	JMP	SERVICE
;
UR1X:	MOV	AL,UART1D	;Port number for this uart is minor
	MOV	AH,1		;Major number
	JMP	INTIN		;Go to service routine
;
UART2RX:
	PUSH	DX
	MOV	DX,Offset UR2X	;Address of UART0 service
	JMP	SERVICE
;
UR2X:	MOV	AL,UART2D	;Port number for this uart is minor
	MOV	AH,1		;Major number
	JMP	INTIN		;Go to service routine
;
;---------------------------------------
; Transmit service routine, all 3 uarts are tied together
UARTTX:	PUSH	DX
	MOV	DX,Offset URTTX	;Address of common service routine
	JMP	FASTINT		;Go fast interrupt service
;
; Must determine which uarts are requesting tx service and are not masked off
URTTX:	MOV	AH,UARTMSK	;Get our UART mask
	MOV	DX,UART0S	;Status port of last Uart
URTTX0:	RCR	AH,1		;Is uart masked (low masks)
	JNC	URTTX1		;Dont bother with this uart if masked 
	IN	AL,DX		;Get status word
	TEST	AL,UTBE		;See if transmitter empty
	JZ	URTTX1		;If utmbt is low skip this port
	PUSH	AX
	PUSH	DX		;Save 2 registers we care about
	MOV	AH,1		;Major number for these is 1
	DEC	DL		;Point dl at data port
	MOV	AL,DL		;Put data port in al (minor)
	CALL	INTOUT		;Call interrupt service
	POP	DX
	POP	AX
URTTX1:	ADD	DL,4		;4 ports per uart
	CMP	DL,UART2S+4	;Have we done all uarts?
	JB	URTTX0		;Do more if not
	RET
;----------------------------------------------------
;  Uart DTR ready routine
;   AH = 1 , AL = data port of desired uart
;    CH = 0 just return status, else return char
UARTRDY:XOR	DH,DH
	MOV	DL,AL		;Get data port in DL
	INC	DL		;Point to status port
	IN	AL,DX		;Get status word
	AND	AL,UDSR		;Check DTR bit
	JNZ	URDY0		;Exit now if not ready
	RET			;And leave
URDY0:	CMP	CH,0		;Is there a character to output
	JE	URDY1		;If not just exit
	DEC	DL		;Point back to data register
	CALL	UART1		;Output character
URDY1:	MOV	AL,0FFH
	RET
;
;  UART output - AH is 1 for this device and AL is data port
;       CL is character to output
UARTOUT:
	XOR	DH,DH		;Clear DH for output
	MOV     DL,AL		;Port number in DL
UART1:	MOV	AL,CL
	OUT	DX,AL
	RET
;
;  UART input - AH is 1 for this device and AL is data port
UARTIN:	
	XOR	DH,DH		;Clear DH for input
	MOV	DL,AL
	IN	AL,DX		;Get character
	RET
;
;  UART output status - AH is 1 for this device and AL is data port
UARTOSTAT:
	XOR	DH,DH
	MOV	DL,AL		;
	INC	DL		;Point to status register
	IN	AL,DX		;Get status word
	AND	AL,UTBE		;Test status tbmt
	JZ	USTAT0
	MOV	AL,0FFH		;FF in al if uart is ready to transmit
USTAT0:	RET
;
;  UART START/STOP  - Enables/Disables output interrupts
;    AH = 1 , AL = Data port of desired uart
UARTSTART:
	SHR	AL,1
	SHR	AL,1
	AND	AL,03H		;Remove Base port address
	MOV	CL,AL		;Uart number 0-2 in CL
	MOV	AL,1		;Uart 0 is D2 - Uart 1 is D1 - Uart 2 is D0
	SHL	AL,CL		;Put bit in uarts data bit number
	PUSHF	
	CLI	
	OR  Byte Ptr UARTMSK,AL	;Add in this mask bit
	MOV AL,Byte Ptr UARTMSK	;Get new mask byte
	OUT	UMSKPRT,AL	;Send to mask port	
	POPF
	RET
;
UARTSTOP:
	SHR	AL,1
	SHR	AL,1
	AND	AL,03H		;Remove Base port address
	MOV	CL,AL		;Uart number 0-2 in CL
	MOV	AL,1
	SHL	AL,CL		;Put bit in uarts data bit number
	NOT	AL		;Flip bits for and (0 masks)
	PUSHF	
	CLI	
	AND Byte Ptr UARTMSK,AL	;Add in this mask bit
	MOV AL,Byte Ptr UARTMSK	;Get new mask byte
	OUT	UMSKPRT,AL	;Send to mask port	
	POPF
	RET
;
;
; UART set : sets uarts when called through eXtended FUNCtioN 0
UARTSET:
	RET
;--------------------------------------------------------------
; Initialize all 3 uarts and set up the interrupt masks and vectors
;  This routine is called only once from the xios init routine, could
;    be in reuse space.
UARTINIT:
	PUSHF ! CLI		;Save flags so we can turn off interrupts 
;
	MOV	SI,Offset BAUDS	;Get start of usart port table
	IN	AL,SETBYT1	;Get validity string from bram
	MOV	AH,AL
	IN	AL,SETBYT2	;Get valid string
	CMP	AX,BRAMPAT	;See if matches valid pattern
	JNE	UARTDEF		;Use default values instead
	MOV	CX,3*3		;3 uarts to do, 3 bytes each uart 
	MOV	DX,IUART0	;Get start of Bram uart init ports
UBAUD:	IN	AL,DX		;Get an initialization byte
	PUSH	DX		;Save current bram port
	MOV	DL,[SI]		;Get port to initialize
	OUT	DX,AL		;Send initialization byte out to uart
	POP	DX		;Recover Bram port
	INC SI ! INC DX		;Move port table and bram pointers up
	LOOP	UBAUD		;Do all 3 uarts
	IN	AL,PRNTMAP	;Get desired printer map
	MOV  SI,Offset LSTTAB
	MOV	BX,[SI]		;Get default device 0 major/minor
	MOV	CX,2[SI]	;Get default device 1 major/minor
	MOV	DX,4[SI]	;Get default device 2 major/minor	
	SHL	AL,1		;Print map * 2, lsttab entries are 2 bytes
	MOV	SI,AX
	AND	SI,6		;Isolate all but map bits for this device
	MOV Offset LSTTAB[SI],BX  ;Put Uart 0 in its place in the table
	SHR AL,1 ! SHR AL,1	;Get next physical device's mapping
	MOV	SI,AX
	AND	SI,6		;Isolate all but map bits for this device
	MOV Offset LSTTAB[SI],CX  ;Put Uart 1 in its place in the table
	SHR AL,1 ! SHR AL,1	;Get last physical devices mapping 
	MOV	SI,AX
	AND	SI,6		;Isolate all but map bits for this device
	MOV Offset LSTTAB[SI],DX  ;Put Centronix port in its place in the table
	MOV	SI,Offset BAUDS+3*3 ;Get start of uart data port lists
	JMPS	UARTDON
;Value in bram is bad or unitialized, so initialize with defaults
UARTDEF:XOR	DX,DX		;Clear DX
	MOV	CX,3		;Do 3 uarts
UARTD1:	MOV	DL,[SI]		;Get mod1 port
	INC	SI		;Move port table pointer up
	MOV	AL,DEFMOD1	;Get default value for mod1 register
	OUT	DX,AL		;Send it to mod1 port
	MOV	DL,[SI]		;Get mod2 port
	INC	SI		;Move port table pointer up
	MOV	AL,DEFMOD2	;Get mod2 default value
	OUT	DX,AL		;Send to mod2 port
	MOV	DL,[SI]		;Get cmnd port
	INC	SI		;Move port table pointer up
	MOV	AL,DEFCMND	;Get command register default value
	OUT	DX,AL		;Send it to command port
	LOOP	UARTD1		;Do all 3 uarts
;	
UARTDON:MOV	CX,3		;Clear any garbage in all 3 uarts
CLRGRB:	MOV	DL,[SI]		;BX already set from previous loop
	INC	SI		;Point to next Uart port
	IN	AL,DX		;Get a garbage byte
	IN	AL,DX
	LOOP	CLRGRB
;
	PUSH	DS
	XOR	AX,AX		;Interrupt vectors are in segment 0
	MOV	DS,AX
	MOV  Word Ptr (.INTBASE+(0*4)),Offset UART2RX
	MOV  Word Ptr (.INTBASE+(1*4)),Offset UART1RX
	MOV  Word Ptr (.INTBASE+(2*4)),Offset UART0RX
	MOV  Word Ptr (.INTBASE+(3*4)),Offset UARTTX	;All TX are the same
	POP	DS		;Restore DS 
;
	MOV	AL,(not (UMSK0 or UMSK1 or UMSK2)) or SQRWAV ;Mask all uarts tx
	MOV 	UARTMSK,AL	;And save in memory
	OUT	UMSKPRT,AL	;Send to port
	
	IN	AL,PICMP1	;Get current interrupt mask
	AND	AL,not 00001111B ;Enable Rx and Tx interrupts 
	OUT	PICMP1,AL
	POPF
	RET

;****************************************************************
;*      							*
;*             CENTRONIX PRINTER DRIVERS			*
;*								*
;****************************************************************
;Enable the CENTRONIX interrupt in the PIC
CENTSTART:
	PUSHF ! CLI		;Save state and turn off interrupts
	IN	AL,PICMP1	;Get current mask value from PIC
	AND	AL,not 00010000B ;Enable only Centronix interrupt (IR4)
	OUT	PICMP1,AL	;Send new value back to PIC
	POPF			;Restore machine state
	RET
;
;Turn off the Centronix interrupt mask
CENTSTOP:
	PUSHF ! CLI		;Save state and turn off interrupts
	IN	AL,PICMP1	;Get current mask value from PIC
	OR	AL,00010000B 	;disable only Centronix interrupt (IR4)
	OUT	PICMP1,AL	;Send new value back to PIC
	POPF			;Restore machine state
	RET
;
; This is the interrupt handler for the centronix xmit 	
CENTXMT:
	PUSH	DX		;Save users DX on users stack 
	MOV  DX,Offset CENTINTR	;Get address of centronix output routine
	JMP	FASTINT		;Go to fast interrupt hander
;
CENTINTR:
	MOV	AX,200H+CENTDWR	;Data port is 0, major is 2 
	JMP	INTOUT		;Go to general input output routine
;	
; Centronix output routine, CL is character to output,  
;       AL is data port
CENTOUT:   
	MOV	AL,(not CENSTRB) and (not CENAUTO)
	OUT	CENTSWR,AL	;Clear strobe
	MOV	AL,CL		;Get character to xmit
	OUT	CENTDWR,AL	;Send character to data port
	MOV	AL,(CENSTRB and (not CENAUTO))	;No auto LF
	OUT	CENTSWR,AL	;Set strobe
	MOV	CX,0FH		;Waste some time
CENTWAT:PUSH AX ! POP AX	;Stop pre-fetcher
	LOOP	CENTWAT
	MOV	AL,(not CENSTRB) and (CENAUTO)	;No auto LF
	OUT	CENTSWR,AL	;Clear strobe again
	RET			;And done
;
CENTOSTAT: 			; No DTR on centronix
CENTRDY: 
	IN	AL,CENTSWR	;Get status of centronix printer  
	AND	AL,CENBSY or CENACK ;Isolate Busy and Acknowledge bits
	XOR	AL,((not CENBSY) or CENACK) and 3 ;Look for busy low, ack high 
	JNZ	CENTRD1		;If printer is not ready , send back 0
	MOV	AL,0FFH		;Else show ready 
	RET
CENTRD1:XOR	AL,AL		;Show not ready
	RET
;
; No printer initialization needed??? 
CENTINIT: 
	PUSH	DS		;Save DS
	MOV	AX,0
	MOV	DS,AX		;Interrupt vectors are in segment 0
	MOV Word Ptr (.INTBASE+(4*4)),Offset CENTXMT
	POP	DS		;Restore DS
	RET
; No need to set word lengths or handshaking, etc.
CENTSET:   
; No input in CENTRONIX interface
CENTIN:   
	RET	;Do these later
;
;--------------------------------------
DSCHAR	EQU	(Offset $)
   DSEG	0
	ORG	DSCHAR
;****************************************************************
;*								*
;* DD - Device dispatch table.  Each entry is 6 words long 	*
;*      consisting of the address of a handler for each of the	*
;*	following functions:					*
;*								*
;*	1) START  - Enable output interrupts			*
;*	2) STOP	  - Disable output interrupts			*
;*	3) INPUT  - Input a character				*
;* 	4) OUTPUT - Output a character				*
;*	5) SET    - Set special function (Baud rates...)	*
;*	6) INIT	  - Called from XIOS init to set up device and	*
;*		    interrupt vectors.				*
;*	7) OSTAT  - Output ready status				*
;*	8) OREADY - Output ready.  This can be used for		*
;*		    hardware handshaking.			*
;*								*
;****************************************************************
		
;    START    STOP     INPUT    OUTPUT   SET      INIT    OSTAT	   OREADY
		
DD_TBL	EQU	(Offset $)
 DW  NODEV,   NODEV,   NODEV,   NODEV,   NODEV,   NODEV,  NODEV,   NODEV   ; 0
 DW  UARTSTART, UARTSTOP,  UARTIN,    UARTOUT,   UARTSET,   UARTINIT, UARTOSTAT, UARTRDY   ; 1
 DW  CENTSTART, CENTSTOP,  CENTIN,    CENTOUT,   CENTSET,   CENTINIT, CENTOSTAT, CENTRDY   ; 2
 DW  ZURTSTART, ZURTSTOP,  ZURTIN,    ZURTOUT,   ZURTSET,   ZURTINIT, ZURTOSTAT, ZURTRDY   ; 3	
 DW  0,       0,       0,       0,       0,       0,      0,       0

;****************************************************************
;*								*
;* CONTAB - Console select table.  Each entry is a major/minor	*
;*          number for the handler for each logical MP/M 	*
;*	    console. 						*
;*								*
;****************************************************************
noiflist

CONTAB	RS	0
   if not CONZ80
	DW	(1 shl 8) + UART0B	;UART 0, middle connector 
   endif
	DW	400H			;First slave card, left side
	DW	500H			;First slave card, right side
	DW	600H			;Second slave card, left side
	DW	700H			;Second slave card, right side
	DW	(1 shl 8) + UART2B	;Modem port

NUMCON	EQU	((Offset $) - (Offset CO                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                chars 
;
;------------------------------------------
; The following are all buffers which could be in unintialized ram
;NBUF	EQU	(NUMCON + NUMLST) ;Number of buffers (# cons + # printers)
NBUF	EQU	4		;Only 4 sio devices on motherboard
BUF	RS	blen * nbuf	;Length of BUF * (# of consoles + # printers)
;
