;==============================================================
; SERIAL: Simple Serial Device Driver for PTDOS
;
; BY MARTIN EBERHARD
;
; Since there is no EOF character in a raw serial stream, this
; driver ends the "file" when the user types the ESCAPE key.
;
; This driver does no flow control, so it is up to the user
; to ensure no characters are lost.
;
; The LOCAL key on the Sol-20 keyboard disconnects the serial
; port from the serial connector on the back of the Sol-20 and
; instead shunts the serial port transmit to the serial port
; receive - local mode. This program can detect this state,
; and will request that the user turn LOCAL mode off.
;==============================================================
VERSION	equ	0100h	;BCD encoding
;
;REVISION HISTORY
;
; 1.00  Updated 25 July 2023  M. Eberhard
;   (No change in resulting code) Generate version number in
;   text from the above version number. Cause asembler to test
;   for code overflow.
; 1.00  27 Feb 2015  M.Eberhard
;   Support receiving too. Support user-abort (EOF) with
;   ESCAPE key. Test for LOCAL key.
; 0.01  03 Oct 2011  M. Eberhard
;   Created
;
;To Do (maybe)
; * Hardware handshaking? (will require more buffering)
; * During INIT, time transmitting a null, to determine and
;   report the baud rate.
;==============================================================
;Notes
;
; Written to be assembled with Digital Research's ASM
;
; This resides in USARE, "user area" in Solos/Cuter's RAM.
; Solos/Cuter's stack grows downward toward USARE. However, the
; only portions of Solos/Cuter used by this program are the
; serial port routines, which use only 4 stack bytes, including
; the call.
;==============================================================

;----------------
;ASCII Characters
;----------------
CR	equ	0Dh		;carriage return
LF	equ	0Ah		;Linefeed
QUOTE	equ	27H		;single quote
MODE	equ	80h		;Sol-20 Mode Select key
ESC	equ	1Bh		;Escape

;---------------
;Program equates
;---------------
BLKSZ	equ	1		;Max block size for transfers
ENDCHR	equ	ESC		;console EOF key

;------------------------------------
;CUTER/SOLOS Equates and Entry Points
;------------------------------------

SOLOS	equ	0C000h		;Solos/Cuter base address
SOLID	equ	SOLOS+00h	;00 for solos, 7Fh for Cuter
INIT	equ	SOLOS+01h	;Cold-start SOLOS
RETRN	equ	SOLOS+04h	;Return control to SOLOS
FOPEN	equ	SOLOS+07h	;Open cassette tape file
FCLOS	equ	SOLOS+0Ah	;Close cassette tape file
RDBYT	equ	SOLOS+0Dh	;Read one cassette tape byte
WRBYT	equ	SOLOS+10h	;Write one cassette tape byte
RDBLK	equ	SOLOS+13h	;Read one cassette tape block
WRBLK	equ	SOLOS+16h	;Write one cassette tape block
SOUT	equ	SOLOS+19h	;Write b to current pseudoport
AOUT	equ	SOLOS+1Ch	;Write b to pseudoport a
SINP	equ	SOLOS+1Fh	;Read current pseudoport into a
AINP	equ	SOLOS+22h	;Read from pseudoport a into a

USARE	equ	SOLOS+0AB4h	;User area in SOLOS RAM
VDMEM	equ	0CC00H		;VDM Screen Memory
SOLSTK	equ	4		;Number of bytes for SOLOS's stack
USRMAX	equ	VDMEM-SOLSTK-1	;Max memory address for us

;Pseudoports

KBDPP	equ	0		;Keyboard
VDMPP	equ	0		;VDM
SERPP	equ	1		;Serial port
PARPP	equ	2		;Parallel port
USRPP	equ	3		;User-written port

;------------------
;Sol-20 Serial Port
;------------------
SERST	EQU	0F8H		;SERIAL STATUS PORT
SDATA	EQU	0F9H		;SERIAL DATA

;Serial port bit assignment masks

SCD	EQU	1		;SERIAL CARRIER DETECT
SDSR	EQU	2		;SERIAL DATA SET READY
SPE	EQU	4		;SERIAL PARITY ERROR
SFE	EQU	8		;SERIAL FRAMING ERROR
SOE	EQU	16		;SERIAL OVERRUN ERROR
SCTS	EQU	32		;SERIAL CLEAR TO SEND
SDR	EQU	64		;SERIAL DATA READY
STBE	EQU	128		;SERIAL TRANSMITTER BUFFER EMPTY

;-----------------------------
;PTDOS definitions from PTDEFS
;-----------------------------
; 1) MAIN ENTRY POINTS  (EP AREA)

SYS	equ	0BCBCh
RB	equ	SYS-3	;READ SINGLE BYTE
WB	equ	SYS-6	;WRITE SINGLE BYTE
UTIL	equ	SYS-9	;UTILITY HANDLER
RESET	equ	SYS-12	;RESET TO SYSTEM
ERRL0	equ	SYS-15	;ERROR LEVEL 0 RETURN FROM DRIVER
ERRL1	equ	SYS-18	;ERROR LEVEL 1 RETURN FROM DRIVER
ERRL2	equ	SYS-21	;ERROR LEVEL 2 RETURN FROM DRIVER
SYSGLO	equ	SYS-23	;POINTER TO SYSTEM GLOBAL AREA
CONIN	equ	SYS-26	;CONSOLE INPUT ONE CHARACTER
CONOUT	equ	SYS-29	;CONSOLE OUTPUT ONE CHARACTER
CONTST	equ	SYS-32	;CONSOLE INPUT READY TEST
PSCAN	equ	SYS-35	;PARAMETER SCANNER ENTRY
INTDN	equ	SYS-38	;INTERRUPT DONE ENTRY POINT
INTDK	equ	SYS-41	;INTERRUPT PROCESSING ENTRY

;---------------------------------------------
;Locate program in Solos's (limited) user area
;to keep out of everyone's way
;---------------------------------------------
	org	USARE

;-------------------------------------
;PTDOS Device Driver Header (page 9-2)
;-------------------------------------
DTRB:	dw	DOREAD		;Read Block
DTRNB:	dw	DOREAD		;Read Next Block
DTRLB:	dw	0000h		;Read Previous Block (not supported)
DTWBR:	dw	DOWRIT		;Write Block then Read Next Block
DTWB:	dw	DOWRIT		;Write Block
DTREW:	dw	DORWND		;Rewind
DTEOF:	dw	DOEOF		;Send EOF
DTCLO:	dw	DOCLOS		;Close Driver
DTSEK:	dw	0000h		;Seek (not supported)
DTCTL:	dw	0000h		;Control/Status Request (not supported)
DTBLK:	dw	BLKSZ		;Max block size
DTITO:	db	1		;(yes) Immediate transfer option
DTINI:	dw	DOINI		;Initialize

;***Driver Routine********************************
; INITIALIZATION
;On Entry:
;    No parameters
;On Exit:
;    RETURN1 = Normal return
;*************************************************
DOINI:	call	ILPRNT
	db	'Serial Driver '
	db	((VERSION AND 0F00h)/256)+'0','.'
	db 	((VERSION AND 0F0h)/16)+'0',(VERSION AND 0Fh) +'0'+80h

	mvi	a,SERPP		;serial pseudoport
	jmp	AINP		;Flush serial port UART

;***Driver Routine********************************
; EOF
;On Entry:
;    hl = Source buffer address
;    de = Buffer size
;On Exit:
;    RETURN1 = Normal return
;*************************************************
DOEOF:	call	DOWRIT		;Flush PTDOS buffer
				;Return will skip 3 bytes
		
;Skip over DORWND and fall into DOCLOS

;***Driver Routine*********************************
; REWIND
;
;PTDOS documentation says sequential device drivers
;should return with hl=0.
;
;On Entry:
;    No parameters
;On Exit:
;    RETURN1=Normal return
;    hl=0
;**************************************************
DORWND:	lxi	h,0

;Fall into DOCLOS

;***Driver Routine********************************
; CLOSE
;On Entry:
;    No parameters
;On Exit:
;    RETURN1 = Normal return
;*************************************************
DOCLOS:	ret

;***Driver Routine*********************************
; WRITE BLOCK
;
; Note:  Entry point is DOWRIT below
;
;On Entry:
;    hl = Source buffer address
;    de = number of bytes to transfer (d=0)
;On Exit:
;    RETURN1 = EOF encountered (User abort)
;    RETURN2 = Normal return
;        hl = 0 = Number of bytes read
;
; Get bytes from PTDOS output buffer, and send them
; to the Sol-20 serial port (via SOLOS call)
;**************************************************
WRILP:	mov	b,m		;Get a source byte
	inx	h

	mvi	a,SERPP		;serial pseudoport
	call	AOUT		;send byte to the serial port

;==>Entry point<==
DOWRIT:	call	CHKUSR		;did the user abort?

	dcr	e		;any bytes in buffer?
	jp	WRILP		;y: keep going

;RETURN2 from DOWRIT

	jmp	RETRN2		;d=0

;***Driver Routine********************************
; READ BLOCK
;
;On Entry:
;    hl = destination buffer address
;    de = number of bytes to transfer (d=0)
;On Exit:
;    RETURN1 = EOF encountered (Won't happen here)
;    RETURN2 = Normal return
;        hl = Number of bytes read
;
; Get bytes from the Sol-20 serial port (via SOLOS
; call), and send them to the PTDOS input buffer.
; EOF when the user types ENDCHR
;*************************************************
DOREAD:	lda	INITL		;first time here?
	ora	a
	jnz	DR1		;n: no more initialization

	inr	a		;remember we've been here
	sta	INITL

;Test for LOCAL key-on by sending a null and seeing if
;we then receive a null within 1 chr time. b=0 here.
;Clear RAM variables while we are at it.

;This 100 mS delay should catch a LOCAL key that is on
;at 110 baud or above.

LRETRY:	mvi	a,SERPP		;d=SERPP
	lxi	b,51		;null, count for loops
	call	AOUT		;send b=0, null to serial

;Each pass through is 4018 cycles, or 1968 uS

STALUP:	dcr	c		;(5)timeout?
	jz	DR0		;(5)y: no LOCAL key

STALP1:	dcr	b		;(5)
	jnz	STALP1		;(10) 3840 cycles total

	mvi	a,SERPP		;(7)serial pseudoport
	call	AINP		;(146)Anything from the UART?
	jz	STALUP		;(10)n: keep waiting
	
	ora	a		;receive a null?
	jnz	DR0		;n: some other chr

	call	ILPRNT
 db CR,'Turn off LOCAL key! Ready','?'+80h

WLOCAL:	call	CHKUSR
	jz	WLOCAL		;wait for chr from user

	jmp	LRETRY		;look at LOCAL again

DR0:	call	ILPRNT
 db CR,'Receiving. Type ESCAPE when don','e'+80h

DR1:	lda	RXCLOS		;already seen the EOF?
	ora	a
	rnz			;y: RETURN1

DR2:	call	CHKUSR		;User input?
	mvi	a,SERPP		;serial pseudoport
	call	AINP		;look for a serial port chr
	jz	DR2		;keep waiting

	mov	m,a		;enqueue serial chr
	inr	d		;count up byte count

 if BLKSZ-1
	inx	h		;bump pointer
	dcr	e		;count down queue bytes
	jnz	DR2
 endif

;RETURN2 from DOREAD or DOWRI
;On Entry:
;  d=total bytes transfered to PTDOS

RETRN2:	pop	h		;fix stack for RETURN2
	inx	h
	inx	h
	inx	h
	push	h

	mov	l,d		;bytes transfered
	mvi	h,0		;hl=bytes transfered

	ret

;***Subroutine****************************************
;Check for user abort
;On Entry:
;       d=bytes transfered in case of RETURN1 exit
;If the user typed ENDCHR:
;  RXCLOS = 1
;  exit via RETURN1
;If no chr typed
;  Z flag set
;If chr typed
;  Z cleared, chr in a
;*****************************************************
CHKUSR:	call	CONTST		;anything from user?
	rz			;n: nothing to do

	call	CONIN		;y: get user chr
	cpi	ENDCHR		;did the user end the input?
	rnz

	sta	RXCLOS		;remember EOF
	pop	h		;junk return address
	
	ret			;y: RETURN1 (EOF) exit

;***Subroutine****************************************
;Print inline string
;
;The call to ILPRNT is followed by an ASCII string.
;The last string byte has its msb set.
;trashes psw, b
;*****************************************************
ILPRNT:	xthl			;Save hl, get msg addr

IPLOOP:	mov	a,m
	ani	7Fh		;strip end marker
	call	CONOUT		;print byte (trashes b)
	mov	a,m		;end?
	inx	h		;Next byte
	ora	a		;msb set?
	jp	IPLOOP		;Do all bytes of msg

	xthl			;Restore hl,
				;..get return address
	ret

;*********************
;Initialized Variables
;*********************
INITL:	db	0		;initialized flag
RXCLOS:	db	0		;Haven't received EOF yet

;===================================================
;Test for code overrun
;Note that ASM's math is limited to 16 bits.
;Note also that (despite ASM's documentation), the
;'if' pseudo-op only looks at the lsb of the result.
;===================================================
CODEND	equ	$
 if (USRMAX-CODEND)/8000h
 ERROR: Available memory exceeded
 endif ;(CODEND-USRMAX)/8000h

	END

