/*-=-=-=-=-=-=-=-=-=-=-=- 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.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

#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
#if 0
	tst	intrbFxn
	beq	9f
	jsr	pc, _pushr
	mov	intrbFxn, r0
	clr	intrbFxn
	mov	intrbArg, -(sp)
	jsr	pc, (r0)
	tst	(sp)+
	jsr	pc, _popr
9:
	rts	pc
#endif

/ 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:
	bis	$SIGOUT, *dr_csr	//
	clr	*dr_obuf
	mov	$100.,_hostLocate
1:
	tst	*dr_ibuf
	bne	1b
	clr	_hostLocate
	bic	$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 */

#if 0
#define SIGNAL_HOST	xor r0,(r5); xor r0,r3
#define WAIT_FOR_HOST	9: cmp r3,(r5); beq 9b
#else
#define SIGNAL_HOST	mov $SIGOUT,r0; xor r0,(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
#endif

/ 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
#if 0
	mov	(r5), r3			/ Load old csr value
	mov	$SIGIN, r0
	xor	r0, r3				/ pretoggle bits
	mov	$(SIGOUT|SIGIN), r0		/ r0 contains the signal bit
#else
	mov	(r5), r3			/ Load old csr value
	bic	$OSIGIN, r3
	mov	$SIGIN, r0
	xor	r0, r3				/ pretoggle bits
#endif

	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
#if 0
	mov	(r5), r3			/ Load old csr value
#else
	mov	(r5), r3			/ Load old csr value
	bic	$OSIGIN, r3
#endif
	
	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
