	include ":stone/dazsys/macros.i"
	include ":stone/dazsys/xrefs.i"

;
; Parrallel port driver (c) Spak, SST Scientific Endeavours
; currently implemented for the parallel port
; contact: c9107253@xenon.newcastle.edu.au (1994)
;
; Cable is the same as that for parnet
;
; I didn't use the parnet.device because I couldn't find it at the time
;
; this is a pretty pathetic method of pumping data thru the parallel port
; since it is so proc'r intensive & I think that the parallel port limits the
; speed at which data is pumped anyway
;
; assume that all lines are pulled high (if 2 outputs drive the same line
; then the resulting line state will be an active-low 'OR')
;
;
; Note that there is a bit of a patch in the acquiring of the port for sending
; (SetupSend) and the interrupt "on" stuff was moved into the send.c module


**************************** PARNET CABLE COMPATIBLE *****************************


;   CABLE:  Connect D7-D0,SEL,POUT, and BUSY across,
;	    Connect ACK to SEL locally:
;	    Connect GND lines indicated
;
;   (2-9)   D7-D0   ------------    D7-D0
;   (12)    POUT    ------------    POUT
;   (11)    BUSY    ------------    BUSY       PARALLEL PORT
;   (13)    SEL     --+------+--    SEL
;   (10)    ACK     -/        \-    ACK
;   (18-22) GND     ------------    GND
;
;   WARNING: you cannot connect RI on the serial port to your
;	     modem because it interferes with the parallel
;	     port's SEL line in this configuration.



		xdef		_SendData					;d0/a0/a1
		xdef		_ReceiveData				;d0/a0/a1
		xdef		_BackSendData				;d0/a0/a1
		xdef		_BackReceiveData			;d0/a0/a1
		xdef		_RelinquishPort				;none
		xdef		_SetupSend					;d0/a1 : d0
		xdef		_SetupReceive				;a1 : d0

		xdef		_CIAIntRoutine				;to be called in the interrupt

		xdef		_ChkSum						;a0/d1 : d0
		OPT		L+


; DATA LINE LOCATIONS (8)
inputaddress	= $bfe101	;parallel port
outputaddress	= $bfe101	;same port
datadirection	= $bfe301	;direction control (0=input, 1=output) as per CIA

; HANDSHAKING CONTROL LINES
syncaddress		= $bfd000	;control bits as per CIA (set or read these)
syncdirection	= $bfd200	;direction of sync lines as per CIA (ie LOW bits for input, HIGH  bits for output)
validbit		= 1			;Data on bus is valid (POUT)
ackbit			= 0			;Read data from bus (BUSY)
selbit			= 2			;used to trigger interrupts

; defines of errors
PAR_OK			= 0
PAR_ERROR_TIMEOUT = 1
PAR_ERROR_BUSY = 2			;actually means "timeout on busy"



; CIA interrupt number (using the ciab.resource)
CIAINTB = 4
CIAINTF = (1<<CIAINTB)
AbleICR = -$12
SetICR = -$18


**************************************************************************
ICR_OFF MACRO	;A6 must already have the CIA base
**************************************************************************
; THE PROPPER WAY, WHERE a6 = cia.resource_base
	move.l		#CIAINTF,d0
	jsr			AbleICR(a6)						;don't interrupt us
;	move.b		#CIAINTF,$bfed01				; THE NAUGHTY WAY
	ENDM

**************************************************************************
ICR_ON MACRO	;A6 must already have the CIA base
**************************************************************************
; THE PROPPER METHOD
	move.l		#(1<<7)+CIAINTF,d0
	jsr			AbleICR(a6)						;interrupts back on 
;	move.b		#(1<<7)+CIAINTF,$bfed01			;naughty method
	ENDM


**************************************************************************
	; MACROS
**************************************************************************

; Wait on a bit from a byte to become 0 or 1, with time out
; if "abort" becomes 1 then the routine branches to abort_label
;          \1     \2       \3        \4          \5
; WAITBIT state,bitnumber,address ,abort_address, abort_label
WAITBIT	MACRO
  __wbklfj_loop_\@:
	btst.b		\2,\3
	IFC		'\1','0'
	beq.s	  __djdjjd_ok\@
	ELSEIF
	bne.s	  __djdjjd_ok\@
	ENDC
	tst.b	\4
	bne		\5
	bra.s	__wbklfj_loop_\@
  __djdjjd_ok\@:
	ENDM

WAITVALIDHIGH	MACRO
	WAITBIT	1,#validbit,\1,\2,\3
	ENDM
WAITVALIDLOW	MACRO
	WAITBIT	0,#validbit,\1,\2,\3
	ENDM
WAITACKHIGH		MACRO
	WAITBIT	1,#ackbit,\1,\2,\3
	ENDM
WAITACKLOW		MACRO
	WAITBIT	0,#ackbit,\1,\2,\3
	ENDM
SETVALIDHIGH	MACRO
	bset.b	#validbit,\1
	ENDM
SETVALIDLOW		MACRO
	bclr.b	#validbit,\1
	ENDM
SETACKHIGH		MACRO
	bset.b	#ackbit,\1
	ENDM
SETACKLOW		MACRO
	bclr.b	#ackbit,\1
	ENDM
SETSELHIGH		MACRO
	bset.b	#selbit,\1
	ENDM
SETSELLOW		MACRO
	bclr.b	#selbit,\1
	ENDM
WAITSELHIGH	MACRO
	WAITBIT	1,#selbit,\1,\2,\3
	ENDM
WAITSELLOW	MACRO
	WAITBIT	0,#selbit,\1,\2,\3
	ENDM


; HANDSHAKING DONE IN SOFTWARE HERE! (SLOW!!!)

**************************************************************************
; Input: a0 memory buffer to send/receive
;		 a1 abort flag (when = 0, abort!)
;		 a4 input/output port address
;		 a2 control address (with directions already set)
;		 d0 number of words (ie bytes/2) to get
;
;		 d2 read data only - temp (should be cleared to begin with
;		 d1 read data only - updated checksum (should be cleared to begin with)
;		 d3 valid bit number (input-read, output-write)
;		 d4 ack bit number (output-read, input-write)

; Valid/Acknowledge bits should be high if sending/receiving for the first time
; Note: these start on waiting/setting acknowledge LOW and end on waiting/setting
; a HIGH, which means the ports can be swapped between input and output with
; no effect (since hi-z drifts high)
**************************************************************************
		; READ DATA LOOP
**************************************************************************
rd_mainloop
		WAITBIT		0,d3,(a2),(a1),_rd_done		;WAIT VALID LOW
		move.b		(a4),d2						;read it
		bclr.b		d4,(a2)						;SET ACK LOW
		add.w		d2,d1						;chk sum
		move.b		d2,(a0)+					;put it in memory
		WAITBIT		1,d3,(a2),(a1),_rd_done		;WAIT VALID HIGH
		move.b		(a4),d2						;read it
		bset.b		d4,(a2)						;SET ACK HIGH
		add.w		d2,d1						;chk sum
		move.b		d2,(a0)+					;put it in memory
_ReadData
		dbf.w		d0,rd_mainloop
		addq.w		#1,d0						;fix -1 to 0
	_rd_done:
		rts


**************************************************************************
		; WRITE DATA LOOP
**************************************************************************
wd_mainloop:
		move.b		(a0)+,(a4)					;write it
		bclr.b		d3,(a2)						;SET VALID LOW
		WAITBIT		0,d4,(a2),(a1),_wd_done		;WAIT ACK LOW
		move.b		(a0)+,(a4)					;write it
		bset.b		d3,(a2)						;SET VALID HIGH
		WAITBIT		1,d4,(a2),(a1),_wd_done		;WAIT ACK LOW
_WriteData
		dbf.w		d0,wd_mainloop
		addq.w		#1,d0						;fix -1 to 0
	_wd_done:
		rts


; have to do the handshake in pairs... so we read one byte and
; handshake for the second
**************************************************************************
		_Read1Byte:
**************************************************************************
		WAITBIT		0,d3,(a2),(a1),_r1b_done	;WAIT VALID LOW
		move.b		(a4),d2						;read it
		bclr.b		d4,(a2)						;SET ACK LOW
		add.w		d2,d1						;chk sum
		move.b		d2,(a0)+					;put it in memory
		WAITBIT		1,d3,(a2),(a1),_r1b_done	;WAIT VALID HIGH
		bset.b		d4,(a2)						;SET ACK HIGH
		subq.w		#1,d0						;read 1 (which counts as 2)
  _r1b_done:
		rts

; We could get away with just writing out an extra byte but with this
; we will never go outside our given buffer
**************************************************************************
		_Write1Byte:
**************************************************************************
		move.b		(a0)+,(a4)					;write it
		bclr.b		d3,(a2)						;SET VALID LOW
		WAITBIT		0,d4,(a2),(a1),_w1b_done	;WAIT ACK LOW
		bset.b		d3,(a2)						;SET VALID HIGH
		WAITBIT		1,d4,(a2),(a1),_w1b_done	;WAIT ACK LOW
		subq.w		#1,d0
	_w1b_done:
		rts



**************************************************************************
		_ReceiveData:
**************************************************************************
;input: d0.w = bytes to get (max 32000)
;		a0 = dest address
;		a1 = abort flag address (aborts if flag goes to 0)
;		a2 = check sum address (word)
; a0 is updated
; d0 is returned with the number of WORDS NOT SENT
; SetupReceive should be called prior to this routine
**************************************************************************
		movem.l		d1-d7/a1-a6,-(sp)
		move.l		a2,a6							;a6 <== check sum address
		moveq		#0,d1							;check sum
		lea			syncaddress,a2					;control reg
		lea			inputaddress,a4					;DATA reg
		moveq		#0,d2							;temporary (0)
		move.b		d2,datadirection				;ALL 8 INPUT
		moveq		#validbit,d3
		moveq		#ackbit,d4

		lsr.w		#1,d0							;BYTES -> WORDS
		if			cs,do,<addq.w #1,d0>,<bsr _Read1Byte>
		bsr			_ReadData
		move.w		d1,(a6)							;checksum return
		movem.l		(sp)+,d1-d7/a1-a6
		rts

**************************************************************************
		_BackSendData:
**************************************************************************
; this is the same as the send data except the valid/ack lines are
; reversed in roles (use this for instantly swapping between receiving
; and sending in the same packet)
; also the data has to be EVEN in length
;
;Send Data (Call SetupSend first)
;input: d0.w = number of bytes (MUST BE EVEN, NO CHECKING)
; 		a0 = location to send
;		a1 = abort flag address (aborts if flag goes to 0)
; a0 is updated, d0 is returned with the number of words NOT sent
; SETUP RECEIVE SHOULD BE CALLED PRIOR TO THIS
**************************************************************************
		movem.l		d1-d7/a1-a6,-(sp)
		lea			syncaddress,a2					;parallel control
		lea			outputaddress,a4				;parallel port out
		move.b		#$ff,datadirection				;all 8 lines output
		moveq		#validbit,d4					;SWAPPED!
		moveq		#ackbit,d3
		lsr.w		#1,d0							;BYTES -> WORDS
		bsr			_WriteData
		movem.l		(sp)+,d1-d7/a1-a6
		rts



**************************************************************************
		_SendData:
**************************************************************************
;Send Data (Call SetupSend first)
;input: d0.w = number of bytes
; 		a0 = location to send
;		a1 = abort flag address (aborts if flag goes to 0)
; a0 is updated, d0 is returned with the number of words NOT sent
; SetupSend should be called prior to calling this
**************************************************************************
		movem.l		d1-d7/a1-a6,-(sp)
		lea			syncaddress,a2					;parallel control
		lea			outputaddress,a4				;parallel port out
		move.b		#$ff,datadirection				;all 8 lines output
		moveq		#validbit,d3
		moveq		#ackbit,d4

		lsr.w		#1,d0							;BYTES -> WORDS
		if			cs,do,<addq.w #1,d0>,<bsr _Write1Byte>
		bsr			_WriteData
		movem.l	(sp)+,d1-d7/a1-a6
		rts


**************************************************************************
		_BackReceiveData:
**************************************************************************
;input: d0.w = bytes to get (max 32000) (MUST BE EVEN FOR THIS!)
;		a0 = dest address
;		a1 = abort flag address (aborts if flag goes to 0)
;		a2 = check sum address (word)
; a0 is updated
; d0 is returned with the number of WORDS NOT SENT
; SetupSend should be called prior to this routine
**************************************************************************
		movem.l		d1-d7/a1-a6,-(sp)
		move.l		a2,a6						;a6 <== check sum address
		moveq		#0,d1						;check sum
		lea			syncaddress,a2				;control reg
		lea			inputaddress,a4				;DATA reg
		moveq		#0,d2						;temporary
		move.b		d2,datadirection			;We want to read 8 lines
		moveq		#validbit,d4				;SWAPPED
		moveq		#ackbit,d3
		lsr.w		#1,d0						;BYTES -> WORDS
		bsr			_ReadData
		move.w		d1,(a6)						;checksum
		movem.l		(sp)+,d1-d7/a1-a6
		rts


**************************************************************************
		_RelinquishPort:
**************************************************************************

; simply makes all lines "input", relinquishing any control over the bus
**************************************************************************
		movem.l		d1-d7/a1-a6,-(sp)
		lea			syncaddress,a2					;parallel control
		lea			syncdirection,a3				;parallel control
		lea			outputaddress,a4				;parallel port out

** SET ALL TO INPUT (HI-Z)
		move.b			#0,datadirection			;all 8 lines input
		SETVALIDLOW		(a3)						;hi-z input
		SETACKLOW		(a3)						;hi-z input
		SETSELLOW		(a3)						;hi-z input

** SET ALL LINES HIGH (ie INACTIVE) JUST IN CASE..
		SETSELHIGH		(a2)
		SETACKHIGH		(a2)
		SETVALIDHIGH	(a2)
		move.b			#$ff,(a4)

;		ICR_ON										;(a6) should be already on!

		movem.l			(sp)+,d1-d7/a1-a6
		rts



**************************************************************************
		_SetupSend:
**************************************************************************
;input: d0 = machine address to contact
;		a1 = address of abort flag
;		a6 = cia.resource base (so we can play with the interrupts)

; call this when you want to start communications with another machine
; return (d0) 0 if we got the line ok, 1 if acquisition failed,
; 2 if machine not responding
**************************************************************************
		movem.l		d1-d7/a1-a6,-(sp)
		move.l		a1,a5							;abort (affected by ICR control)
		move.b		d0,d7							;machine ID
		lea			syncaddress,a2					;parallel sync in/out
		lea			syncdirection,a3				;parallel control
		lea			outputaddress,a4				;parallel port out

** TRY AND ACQUIRE THE LINE BY CHECKING FOR SEL HIGH
		SETVALIDLOW		(a3)						;everything is an input
		SETACKLOW		(a3)
		SETSELLOW		(a3)

; note that when nobody drives the line SEL *SHOULD* be high!
		moveq			#PAR_ERROR_BUSY,d0			;
		WAITSELHIGH		(a2),(a5),_sus_done			;acquire! (or bomb out)

** CIA can't interrupt us (else we trigger ourselves)
		ICR_OFF										;(a6)

** SEND OUT A NEGATIVE TRANSITION ON SEL (WHICH IS CONNECTED TO AN ACK)
		SETSELHIGH		(a2)						;set / (order is important!)
		SETVALIDLOW		(a2)						;set valid \ (for handshake)

		SETSELHIGH		(a3)						;SELECT IS AN OUTPUT
		SETVALIDHIGH	(a3)						;"VALID" is an output
		move.b		#$ff,datadirection				;all 8 lines output
		move.b		d7,(a4)							;put out dest machine

** CAUSE AN INTERRUPT ON THE DEST MACHINE
		SETSELLOW	(a2)							;SELECT negative edge


** WAIT FOR A LITTLE HANDSHAKE FROM ANOTHER MACHINE
		moveq			#PAR_ERROR_TIMEOUT,d0
		WAITACKLOW		(a2),(a5),_sus_intson		;wait ack \
		SETVALIDHIGH	(a2)						;set valid /
		WAITACKHIGH		(a2),(a5),_sus_intson		;wait ack /

** WE DON'T WANT INTERRUPT CODE STARTING...
** this refuses to work properly here... moving it to send.c before relinquishport
;		move.l		#CIAINTF,d0						;Clear that interrupt
;		jsr			SetICR(a6)						;put this out of the way
;		ICR_ON										;(a6) but turnem on

		moveq		#PAR_OK,d0						;everything - OK!
	_sus_intson:

	_sus_done:
		movem.l	(sp)+,d1-d7/a1-a6
		rts


**************************************************************************
		_SetupReceive:
**************************************************************************
;input: a1 is the abort flag address as usual
;return d0 = 0 if got the handshake, d0 = 1 if we timed out
**************************************************************************
		movem.l		d1-d7/a1-a6,-(sp)
		lea			syncaddress,a2					;parallel control
		lea			syncdirection,a3				;parallel control
		lea			outputaddress,a4				;parallel port out
		moveq	#PAR_ERROR_TIMEOUT,d0
		SETVALIDLOW		(a3)						;"VALID" is an INPUT
		SETACKHIGH		(a3)						;"ACK" is an OUTPUT
		WAITVALIDLOW	(a2),(a1),_sur_done			;WAIT valid \
		SETACKLOW		(a2)						;set ack \
		WAITVALIDHIGH	(a2),(a1),_sur_done			;WAIT valid /
		SETACKHIGH		(a2)						;set ack /
		moveq	#PAR_OK,d0
	_sur_done:
		movem.l	(sp)+,d1-d7/a1-a6
		rts



**************************************************************************
		_CIAIntRoutine:
**************************************************************************
; Please see the c-code part for the interrupt setup
; data_segment (is_Data) a1
; according to the ROM-kernal manual, d0/d1/d0/a1/a5 need not be saved
; 
; Assume that the input port is already set up to take input data
; No handshaking is done here since that needs a timer to be set up
; after which setupreceive should be called for the first handshake
;
; count on the ciaa.resource to handle ALL of the interrupt hardware
; including the reseting of the CIA interrupt bits
;
; is_Data should point to
				rsset 0
cir_sigmask		rs.l	1			;ORDER IS IMPORTANT!
cir_sigtask		rs.l	1
cir_machine		rs.b	1			;if (inputaddress) is this then signal
cir_abort		rs.b	1			;abort flag (set to 1)
cir_pad			rs.b	2
cir_ciares		rs.l	1			;cia resource

**************************************************************************
		movem.l	a6,-(sp)
		move.l	a1,a5								;use a5 as the base

;		move.w	#$f00,$dff180
		move.b	#-1,cir_abort(a5)					;<-- this stuffs up

** CHECK (inputaddress) AGAINST OUR MACHINE
		move.b	inputaddress,d0
		if.b	d0,ne,cir_machine(a5),cir_thatsall	;is it for our machine?
		move.b	#-2,cir_abort(a5)					;ABORT, in case we are
													;still doing something else

** SIGNAL OUR TASK
		movem.l cir_sigmask(a5),d0/a1				;get mask & task!
		exec	Signal								;a1/d0 : ??

	cir_thatsall:
		moveq	#0,d0
		movem.l	(sp)+,a6
		rts


**************************************************************************
	_ChkSum:
**************************************************************************
; "FAST" check summer with unrolled loop
;input	a0 buffer (updated)
;		d1.w length (trashed)
;output d0 checksum
**************************************************************************
	movem.l	d2/d3,-(sp)

	move.w	d1,d2							;length
	lsr.w	#4,d1							;counter
	and.w	#15,d2							;leftover

	moveq	#0,d0							;sum
	moveq	#0,d3							;temp

*** DO the bulk (should really do an offset into this..)
	bra.s	_csy_skipa
_csy_loopa:
	REPT	16								;unroll this
	move.b	(a0)+,d3
	add.w	d3,d0
	ENDR
_csy_skipa:
	dbf.w	d1,_csy_loopa

*** Do the remaining few
	bra.s	_csy_skipb
_csy_loopb:
	move.b	(a0)+,d3
	add.w	d3,d0
_csy_skipb:
	dbf.w	d2,_csy_loopb

	movem.l	(sp)+,d2/d3
	rts
