; Serial.a
; created by John Schultz, 17-Sep-89
; last modified 17-April-90

	section	Serial,CODE

	xref	_raw

	xdef	_baudper
	xdef	_openserial
	xdef	_closeserial
	xdef	_sendser
	xdef	_readser
	xdef	_checkser
	xdef	_rawtoascii
	xdef	_sendsernobuff


_LVOSetIntVector equ	$FFFFFF5E

ESCAPE		equ	69		; input-handler rawkey code

CLOCK		equ	3579545
_baudper	dc.w 	(CLOCK/19200)-1	; defaualt

CUSTOM		equ	$dff000
SERDATR		equ	$000018
SERDAT		equ	$000030
SERPER		equ	$000032

INTENA		equ	$00009a
INTENAR		equ	$00001c
INTREQ		equ	$00009c

TBEQSIZE	equ	128		; buffer size in bytes,
RBFQSIZE	equ	128		; increase or decrease as needed

LED		equ	$bfe001		; toggle power led, audio filter

ledroll		dc.b	%00010001	; flash every 4 bytes
ledpad		dc.b	0

oldintena	dc.w	0

tbequeue	ds.b	TBEQSIZE	; circular queue
tbeqend
tbehead		dc.l	tbequeue
tbetail   	dc.l	tbequeue

oldtbeinterrupt	dc.l	0
oldrbfinterrupt	dc.l	0

tbeinterrupt
	dc.l	0	; ln_succ
	dc.l	0	; ln_prec
	dc.b	2	; NT_INTERRUPT
	dc.b	127	; ln_pri
	dc.l	0	; ln_name
	dc.l	tbehead	; is_data
	dc.l	tbehandler ; is_code

rbfinterrupt
	dc.l	0	; ln_succ
	dc.l	0	; ln_prec
	dc.b	2	; NT_INTERRUPT
	dc.b	127	; ln_pri
	dc.l	0	; ln_name
	dc.l	rbftail	; is_data
	dc.l	rbfhandler ; is_code


tbehandler	
	move.w	#1,INTREQ(a0)		; clear TBE interrupt
	move.l	(a1),a1			; a1 = tbehead, (a1) = pointer
	cmp.l	tbetail,a1		; now a1 = pointer
	beq.b	99$
	move.w	SERDATR(a0),d0		; check to make sure emtpy
	btst	#13,d0
	beq.b	99$
	move.w	#$100,d0
	move.b	(a1)+,d0		; get byte from queue
	move.w	d0,SERDAT(a0)		; send byte
	cmpa.l	#tbeqend,a1
	bne.b	10$
	lea	tbequeue(pc),a1
10$	move.l	a1,tbehead
99$
	rts

rbfqueue	ds.b	RBFQSIZE
rbfqend	
rbfhead		dc.l	rbfqueue
rbftail		dc.l	rbfqueue

rbfhandler
	move.w	SERDATR(a0),d0		; a0 = custom
	move.w	#$800,INTREQ(a0)	; clear RBF interrupt
	move.l	(a1),a0			; a1 = rbftail, get pointer
	move.b	d0,(a0)+		; write byte to buffer
	cmpa.l	#rbfqend,a0
	bne.b	10$
	lea	rbfqueue(pc),a0
10$	move.l	a0,(a1)			; a1 = rbftail

;99$
;	move.b	ledroll(pc),d0		; remove from 99$-100$ for more
;	rol.b	#1,d0			; speed
;	move.b	d0,ledroll
;	btst	#1,d0
;	beq.b	100$
;	bchg.b	#1,LED			; toggle power led / audio filter
;100$
	rts

_openserial
	move.l	a6,-(sp)
	lea	CUSTOM,a0
	move.w	#$4000,INTENA(a0)	; disable
	move.l	$4,a6			; execbase
	lea	rbfinterrupt(pc),a1
	moveq.l	#11,d0
	jsr	_LVOSetIntVector(a6)
	move.l	d0,oldrbfinterrupt
	lea	tbeinterrupt(pc),a1
	moveq.l	#0,d0
	jsr	_LVOSetIntVector(a6)
	move.l	d0,oldtbeinterrupt

	lea	CUSTOM,a0
	move.w	_baudper,SERPER(a0)	; set period

	move.w	#$3fff,$bfd0fe		; init handshake lines
	move.w	INTENAR(a0),oldintena
	move.w	#$8801,INTENA(a0)	; turn on RBF and TBE INTENA
	move.w	#$0801,INTREQ(a0)	; clear RBF and TBE INTREQ
	move.w	#$c000,INTENA(a0)	; enable
	move.l	(sp)+,a6
	rts


_closeserial
	move.l	a6,-(sp)
	lea	CUSTOM,a0
	move.w	#$4000,INTENA(a0)	; disable
	move.l	$4,a6			; execbase
	move.l	oldrbfinterrupt,a1
	moveq.l	#11,d0
	jsr	_LVOSetIntVector(a6)
	move.l	oldtbeinterrupt,a1
	moveq.l	#0,d0
	jsr	_LVOSetIntVector(a6)

	lea	CUSTOM,a0
	move.w	#$0801,INTENA(a0)	; turn off RBF and TBE INTENA
	move.w	oldintena,d0
	and.w	#$0801,d0		; mask for bit 11 and bit 1.
	bset	#15,d0			; set/clr = 1 (set)
	move.w	d0,INTENA(a0) 		; reset old RBF and TBE INTENA

	bclr.b	#1,LED

	move.w	#$0801,INTREQ(a0)	; clear RBF and TBE INTREQ
	move.w	#$c000,INTENA(a0)	; enable
	move.l	(sp)+,a6
	rts

_sendser
	lea	CUSTOM,a0
	move.w	#$4000,INTENA(a0)

	move.l	tbetail,a1
	cmpa.l	tbehead,a1
	beq.b	5$
	move.w	#$8001,INTREQ(a0)	; generate a tbe interrupt
5$
	move.b	d0,(a1)+		; queue up byte
	cmpa.l	#tbeqend,a1
	bne.b	10$
	lea	tbequeue(pc),a1
10$	move.l	a1,tbetail
	
	move.w	#$c000,INTENA(a0)
	rts


; send byte in d0 to serial port, non-buffered.
_sendsernobuff
	lea	CUSTOM,a0
	move.w	#$4000,INTENA(a0)
1$	move.w	SERDATR,d1
	btst	#13,d1			; check TBE
	beq.b	1$			; buffer busy
	
	move.w	#$100,d1
	move.b	d0,d1
	move.w	d1,SERDAT(a0)
2$	move.w	SERDATR(a0),d1
	btst	#13,d1			; check TBE
	beq.b	2$			; buffer busy
	move.w	#$001,INTREQ(a0)	; clear TBE interrupt

	move.w	#$c000,INTENA(a0)
	rts


; uses a0
_readser
	cmp.w	#ESCAPE,_raw	; quits on ESC from input handler
	beq.b	20$
	move.l	rbfhead,a0
	cmpa.l	rbftail,a0
	beq.b	_readser
	move.b	(a0)+,d0
	cmpa.l	#rbfqend,a0
	bne.b	10$
	lea	rbfqueue(pc),a0
10$	move.l	a0,rbfhead
20$
	rts

_checkser
	move.l	rbfhead,a0
	cmpa.l	rbftail,a0
	bne.b	dataisin
	moveq.l	#0,d0
	rts
dataisin
	moveq.l	#1,d0
	rts


; rawkey in d0
; ascii returned in d0
_rawtoascii
	cmp.b	#79,d0
	bls.b	getascii	
	moveq.l	#0,d0
	rts
getascii
	lea	asciitable,a0
	move.b	0(a0,d0),d0
	rts


	section	asciitable,data

; This is a quick trick, don't do this for anything other than
; home use, boys and girls.  Use rawkeyconvert, etc.

asciitable
	dc.b	'`'	; backquote `
	dc.b	'1'
	dc.b	'2'
	dc.b	'3'
	dc.b	'4'
	dc.b	'5'
	dc.b	'6'
	dc.b	'7'
	dc.b	'8'
	dc.b	'9'
	dc.b	'0'
	dc.b	'-'
	dc.b	'='
	dc.b	$0D	; \
	dc.b	0
	dc.b	'0'	; keypad 0
	dc.b	'q'
	dc.b	'w'
	dc.b	'e'
	dc.b	'r'
	dc.b	't'
	dc.b	'y'
	dc.b	'u'
	dc.b	'i'
	dc.b	'o'
	dc.b	'p'
	dc.b	'['
	dc.b	']'
	dc.b	0
	dc.b	'1'
	dc.b	'2'
	dc.b	'3'
	dc.b	'a'
	dc.b	's'
	dc.b	'd'
	dc.b	'f'
	dc.b	'g'
	dc.b	'h'
	dc.b	'j'
	dc.b	'k'
	dc.b	'l'
	dc.b	';'
	dc.b	$27	; ' single quote
	dc.b	0
	dc.b	0
	dc.b	'4'
	dc.b	'5'
	dc.b	'6'
	dc.b	0
	dc.b	'z'
	dc.b	'x'
	dc.b	'c'
	dc.b	'v'
	dc.b	'b'
	dc.b	'n'
	dc.b	'm'
	dc.b	','
	dc.b	'.'
	dc.b	'/'
	dc.b	0
	dc.b	'.'	; keypad
	dc.b	'7'
	dc.b	'8'
	dc.b	'9'
	dc.b	' '
	dc.b	$8	; backspace
	dc.b	$9	; tab
	dc.b	$D	; enter  cr
	dc.b	$D	; return cr
	dc.b	$1B	; ESC
	dc.b	$7f	; delete
	dc.b	''
	dc.b	0
	dc.b	0
	dc.b	'-'	; keypad
	dc.b	0
	dc.b	0	
	dc.b	$b	; up arrow
	dc.b	$a	; down arrow
	dc.b	$20	; rt arrow
	dc.b	$8	; left arrow
	
	END
