/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by John E. Lloyd.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

/* This driver is exactly the same as drv11.c, expect that when
   sending a signal to the host system, it does not toggle the
   protocol bit, but instead clears and sets it. This assumes that
   the bit is latched (and locally cleared) at the other end. This
   can be done using the hardware on the XYCOM XVME-240 board.

   The RCI driver to use with this driver is xy2dDriver.c */

#include "../h/moper_com.h"

	PRCSR = 0177564
	PRBUF = 0177566

/	dr_csr = 	0177000
/	DR_OBUF = 	0177002
/	DR_IBUF = 	0177004

	DR_CSR0 =    	 0x0001
	DR_CSR1 =    	 0x0002
	DR_REQA =     	 0x0080
	DR_REQB = 	 0x8000
	DR_IEA =    	 0x0040
	DR_IEB =    	 0x0020

	LISTEN =	 0x1234
	READ_READY =	 0x1111
	WRITE_READY =	 0x2222
	SIGNAL_ACK =	 0x3333
	SIGOUT =	DR_CSR1
	SIGIN =		DR_REQB

	/ AND this I need cause the idiots who wrote this assembler did
	/ not provide constant expressions, though the documentation
	/ says they DO ....

	OSIGOUT =	 0xfffd
	OSIGIN =	 0x7fff
	movpsw =	 0106427	

#define SPL7()		 movpsw; 0340	/* mask interrupts */

.globl _hostDevice
.globl _hostListen
.globl _hostAttach
.globl _hostDetach
.globl _hostWrite
.globl _hostRead
.globl _hostSignal
.globl _hostOnSig

.globl _hostError
.globl _hostLocate
.globl _hostSize
.globl _hostAddr

#define PUSHR		mov r1,-(sp); mov r2,-(sp); mov r3,-(sp); \
			mov r4,-(sp); mov r5,-(sp);

#define POPR		mov (sp)+,r5; mov (sp)+,r4; mov (sp)+,r3; \
			mov (sp)+,r2; mov (sp)+,r1;

/* for debugging ... put a character on the console line ... */

#define PUTN(x)		8: tstb *$PRCSR; bpl 8b; movb x, *$PRBUF;

/* sets up host device at a given address */

/ hostDevice (addr, vector): called at program startup time to define the 
/ location of the device (csr address and vector number). The routine 
/ records the address and attaches the necessary interrupt service routines
/ to the vector.

_hostDevice:

	mov	2(sp), r0		/ record the device addresses
	mov	r0, dr_csr
	mov	r0, _hostAddr
	add	$2, r0
	mov	r0, dr_obuf
	add	$2, r0
	mov	r0, dr_ibuf
	clr	*dr_csr			/ disable device interrupts

	mov	$vect_desc, r0		/ use r0 to build vect desc

	mov	$onIntrA, (r0)		/ basic interrupt handler 
	mov	$0, 2(r0)		/ argument is 0 for 1st interrupt
	mov	$0, 4(r0)		/ do not save registers
	mov	r0, -(sp)		/ pointer to vector descriptor
	mov	6(sp), -(sp)		/ vector number
	jsr	pc, _vectorAlloc	/ set up vector
	add	$4, sp

	mov	$vect_desc, r0		/ use r0 to build vect desc

	mov	$onIntrB, (r0)		/ basic interrupt handler 
	mov	$0, 2(r0)		/ argument (not used)
	mov	r0, -(sp)		/ pointer to vector descriptor
	mov	6(sp), -(sp)		/ vector number
	add	$4, (sp)		/ add offset to second vector 
	jsr	pc, _vectorAlloc	/ set up vector
	add	$4, sp

	rts 	pc

.data
vect_desc:
	.space	2			/ function pointer
	.space	2			/ argument
	.space	2			/ save regs option
intrbCnt:
	.word 	0
intrbBusy:
	.word	0
intrbFxn:
	.word	0
intrbArg:
	.word	0
.text

onIntrB:
/
/	This interrupt handler basically does the following drill:
/
/	 cnt++;
/	 if (busy) return;
/	 busy = 1;
/	 while (cnt && fxn)
/	  { cnt = 0;
/	    h2 = fxn;
/	    fxn = 0;
/	    (*h2)(arg);
/	    spl7();
/	  }
/	 busy = 0;
/	 cnt = 0;
/
	inc	intrbCnt
	tst	intrbBusy
	bne	9f
	inc	intrbBusy
1:
	tst	intrbCnt
	beq	2f
	tst	intrbFxn
	beq	2f
	jsr	pc, _pushr
	clr	intrbCnt
	mov	intrbFxn, r0
	clr	intrbFxn
	mov	intrbArg, -(sp)
	jsr	pc, (r0)
	tst	(sp)+
	jsr	pc, _popr
	SPL7()	
	br	1b
2:
	clr	intrbBusy
	clr	intrbCnt
9:
	rts	pc

/ Service routine for the signals. Signals are sent in through device
/ interrupt A. This routine catches device interrupt A and then reads
/ the signal number being sent from the host and calls any function
/ which has been scheduled to be run on that signal.

onIntrA:
	mov	r0, -(sp)		/ save r0 for counting
	mov	*dr_ibuf, -(sp)		/ push input buffer
	mov	*dr_obuf, -(sp)		/ push output buffer
	tst	2(sp)			/ check if we have a signal
	beq	9f			/ illegal signal, ignore
	mov	$SIGNAL_ACK, *dr_obuf	/ acknowledge signal
	mov	$20000., r0
2:
	tst	*dr_ibuf		/ wait for input buffer to clear
	beq	1f
	sob	r0, 2b
	br	9f
1:
	clr	*dr_obuf		/ clear acknowledge
/ 
/ Deliver signals. The code we use to do this is similar to the code
/ for onIntrB, except that we have 2 sigals to look out for.
/
	cmp	$1, 2(sp) 		/ signal #1?
	bne	5f

	inc	sig1Cnt
	tst	sig1Busy
	bne	9f
	inc	sig1Busy
1:
	tst	sig1Cnt
	beq	2f
	tst	sig1Fxn
	beq	2f
	jsr	pc, _pushr
	clr	sig1Cnt
	mov	sig1Fxn, r0
	clr	sig1Fxn
	mov	sig1Arg, -(sp)
	jsr	pc, (r0)
	tst	(sp)+
	jsr	pc, _popr
	SPL7()
	br	1b
2:
	clr	sig1Busy
	clr	sig1Cnt
	br	9f
5:
	cmp	$2, 2(sp)		/ signal #2 ?
	bne	9f

	inc	sig2Cnt
	tst	sig2Busy
	bne	9f
	inc	sig2Busy
1:
	tst	sig2Cnt
	beq	2f
	tst	sig2Fxn
	beq	2f
	jsr	pc, _pushr
	clr	sig2Cnt
	mov	sig2Fxn, r0
	clr	sig2Fxn
	mov	sig2Arg, -(sp)
	jsr	pc, (r0)
	tst	(sp)+
	jsr	pc, _popr
	SPL7()
	br	1b
2:
	clr	sig2Busy
	clr	sig2Cnt
9:	
	mov	(sp)+, *dr_obuf
	tst	(sp)+
	mov	(sp)+, r0
	rts	pc

sig1Fxn:
	.word	0
sig1Arg:
	.word	0
sig1Cnt:
	.word	0
sig1Busy:	
	.word	0
sig2Fxn:
	.word	0
sig2Arg:
	.word	0
sig2Cnt:
	.word	0
sig2Busy:	
	.word	0

/ hostOnSig (signum, fxn, arg): tells the system to call the specified
/ function with the indicated argument when signal number 'signum'
/ is received from the host. The function is called at maximum priority
/ (spl7()) with all registers saved.

_hostOnSig:				/ (signal number, function, arg)
	cmp	2(sp), $1		/ signal 1 ?
	bne	1f
	mov	6(sp), sig1Arg
	mov	4(sp), sig1Fxn
	bis	$DR_IEA, *dr_csr
	mov	$1, r0
	rts	pc
1:
	cmp	2(sp), $2		/ signal 2 ?
	bne	1f
	mov	6(sp), sig2Arg
	mov	4(sp), sig2Fxn
	bis	$DR_IEA, *dr_csr
	mov	$1, r0
	rts	pc
1:
	mov	$-1, r0
	rts	pc

/ hostListen (fxn, arg): sets up a handler that is called when the
/ system receives an attach request from the host CPU. 

_hostListen:
	clr	sig1Fxn			/ Reset the device control
	clr	sig1Cnt			/ registers
	clr	sig1Busy
	clr	sig2Fxn
	clr	sig2Cnt
	clr	sig2Busy
	clr	intrbFxn
	clr	intrbCnt
	clr	intrbBusy

	mov	$DR_IEB, *dr_csr	/ enable interrupts, first so as
	mov	$10., r0		/  to catch strays
1:
	sob	r0, 1b
	mov	2(sp), intrbFxn
	mov	4(sp), intrbArg
	mov	$LISTEN, *dr_obuf	/ let host know we are listening
	rts	pc

/ Routine that should be called from the attach handler to complete the
/ attachment to the host CPU. Routine returns 1 if all went well and
/ -1 otherwise.

_hostAttach:
	cmp	$LISTEN, *dr_ibuf
	beq	1f
	mov	$HST_SYNC_E, _hostError
	jbr	7f
1:
	clr	*dr_obuf
	mov	$100.,_hostLocate
1:
	tst	*dr_ibuf
	bne	1b
	clr	_hostLocate
	bic	$SIGOUT, *dr_csr	//
	bis	$SIGOUT, *dr_csr	//
	bic     $DR_IEB, *dr_csr
	bis	$DR_IEA, *dr_csr	// so we can catch signals
	mov	$1, r0
	rts	pc
7:
	clr	*dr_csr
	clr	*dr_obuf
	mov	$-1, r0
	rts	pc

/ hostDetach: clear all device registers and effectively disconnect device
/ from host.

_hostDetach:
	clr	*dr_csr			
	clr	*dr_obuf
	clr	sig1Fxn
	clr	sig1Cnt
	clr	sig1Busy
	clr	sig2Fxn
	clr	sig2Cnt
	clr	sig2Busy
	clr	intrbFxn
	clr	intrbCnt
	clr	intrbBusy
	rts 	pc

/ send a signal to the host CPU 

_hostSignal:
	cmp	$WRITE_READY, *dr_obuf		/ make sure we not doing write
	bne	1f
	mov	$13.,-(sp)
	jsr	pc, _zexit
	halt
1:
	mov	$WRITE_READY, *dr_obuf
	bic	$DR_CSR0, *dr_csr		/ send interrupt to host
	mov	$200.,r0			/ XXX 
1:	sob	r0,1b				/ XXX
	bis	$DR_CSR0, *dr_csr		/
	rts	pc

/* macros for the read and write routines */

#define SIGNAL_HOST        bic $SIGOUT,(r5); bis $SIGOUT,(r5); \
			   mov $SIGIN,r0; xor r0,r3
#define WAIT_FOR_HOST(loc) mov $loc,_hostLocate; 9: mov (r5),r0; \
			   bic $OSIGIN,r0; cmp r3,r0; beq 9b

/ writes a buffer of words to the host

_hostWrite:
	PUSHR			/ 020(sp) is the buffer, 022(sp) is the size
	sub	$4, sp		/ (sp) is checksum,  2(sp) is spare

	mov	dr_obuf, r4			/ use r4 to address dr11 buf
	mov	dr_csr, r5			/ use r5 to address dr11 csr

/	Compute checksum and store it away

	clr 	r0				
	mov	020(sp), r2			/ r2 points to software buffer
	mov	022(sp), r1			/ r1 points to word count
	beq	2f
1:	
	add	(r2)+, r0
	sob	r1, 1b
2:	
	mov	r0, (sp)

/	Reset pointers to software buffer and word count

	mov	020(sp), r2			/ r2 points to software buffer
	mov	022(sp), r1			/ r1 points to word count

	mov	$WRITE_READY, *dr_obuf		/ tell host we are ready
	mov	$200.,_hostLocate
1:						
	cmp	$READ_READY, *dr_ibuf		/ wait for host to be ready
	bne	1b
	
	mov	r1, (r4)			/ put word count in buffer

	mov	(r5), r3			/ Load old csr value
	bic	$OSIGIN, r3
	mov	$SIGIN, r0
	xor	r0, r3				/ pretoggle bits

	SIGNAL_HOST
	WAIT_FOR_HOST(201.)

	tst	r1
	beq	2f
1:	
	mov	(r2)+, (r4)			/ output all the words
	SIGNAL_HOST
	WAIT_FOR_HOST(202.)

	sob	r1, 1b
2:	
	mov	(sp), (r4)			/ send checksum
	SIGNAL_HOST
	WAIT_FOR_HOST(203.)

	clr	(r4)				/ clear output buffer
	SIGNAL_HOST

	clr	_hostLocate

	mov	022(sp), r0			/ return word count
	add	$4, sp
	POPR
	rts	pc
	
/ reads a buffer of words from the host

_hostRead:


	PUSHR			/ 020(sp) is the buffer, 022(sp) is the size
	sub	$4, sp		/ (sp) is checksum,  2(sp) is word count

	mov	dr_ibuf, r4			/ use r4 to address dr11 buf
	mov	dr_csr, r5			/ use r5 to address dr11 csr

	mov	(r5), r3			/ Load old csr value
	bic	$OSIGIN, r3
	
	mov	$READ_READY, *dr_obuf		/ send READY signal

	WAIT_FOR_HOST(300.)

	clr	*dr_obuf			/ clear output buffer
	mov	(r4), r1			/ load word count and
	mov	r1, 2(sp)
	cmp	022(sp), r1			/ make sure wc is OK
	bge	1f
	mov	$HST_COUNT_E, _hostError		
	jbr	7f
1:	
	mov	020(sp), r2			/ buffer address in r2
/*	mov	$(SIGOUT|SIGIN), r0		/ r0 contains the signal bit*/

	tst	r1
	beq	2f
1:	
	SIGNAL_HOST
	WAIT_FOR_HOST(301.)
	mov	(r4), (r2)+
	sob	r1, 1b
2:	
	SIGNAL_HOST				/ get checksum
	WAIT_FOR_HOST(302.)
	mov	(r4), (sp)

	SIGNAL_HOST				
	WAIT_FOR_HOST(303.)			/ host output buffer is clear

	bit	$SIGIN, *dr_csr			/ if host signal bit is high,
	beq	1f				/ do one more exchange 
	SIGNAL_HOST				/ to clear it.
	WAIT_FOR_HOST(304.)
1:	
	bic	$SIGOUT, *dr_csr		/ clear the signal bit
	clr	_hostLocate

	clr	r0
	mov	020(sp), r2
	mov	2(sp), r1
	beq	2f
1:	
	add	(r2)+, r0
	sob	r1, 1b
2:	
	cmp	r0, (sp)
	beq	1f
	mov	$HST_CHKSUM_E, _hostError
	jbr	7f
1:	
	mov	2(sp), r0
	add	$4, sp
	POPR
	rts	pc
7:
	clr	*dr_csr
	clr	*dr_obuf
	mov	$-1, r0
	add	$4, sp
	POPR
	rts	pc

.data

_hostError:	.word  0
_hostLocate:	.word  0
_hostSize:	.word  6
_hostAddr:	.word  0

dr_csr:		.word  -1
dr_ibuf:	.word  -1
dr_obuf:	.word  -1
