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

/* Moper side device driver for the drv11-J. This driver is
   very similar to the device driver drv11.c (which is written for the
   DRV11), with only changes made as required by the DRV11-J 
*/

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

/* DRV11-J Command and bit definitions. Hijacked from the file 
   "../h/drjDevice.h" */

/* Interrupt Mode Commands. These commands are described in the 
    DRV11-J manual. They are entered by the driver into the icmd registers */

#define ICMD_RESET		0x00	/* reset everything */
#define ICMD_CLEAR_IRMR_ALL	0x10	/* clear IMR and IRR */
#define ICMD_CLEAR_IRMR		0x18	/* clear IMR and IRR bit */
#define ICMD_CLEAR_MR_ALL	0x20	/* clear IMR */
#define ICMD_CLEAR_MR		0x28	/* clear IMR bit  */
#define ICMD_SET_MR_ALL		0x30	/* set IMR */
#define ICMD_SET_MR		0x38	/* set IMR bit */
#define ICMD_CLEAR_IR_ALL	0x40	/* clear IRR */
#define ICMD_CLEAR_IR		0x48	/* clear IRR bit  */
#define ICMD_SET_IR_ALL		0x50	/* set IRR */
#define ICMD_SET_IR		0x58	/* set IRR bit */
#define ICMD_CLEAR_IS_HIGH	0x60	/* clear highest priority ISR bit */
#define ICMD_CLEAR_IS_ALL	0x70	/* clear ISR */
#define ICMD_CLEAR_IS		0x78	/* clear ISR bit */
#define ICMD_MODE		0x80	/* load mode bits 0:4 */
#define ICMD_SELR		0xa0	/* load mode bits 5 & 6 */
#define ICMD_SELR_ARM		0xa1	/* load mode bits 5 & 6 and set 7 */
#define ICMD_SELR_DISARM	0xa2	/* load mode bits 5 & 6 and clear 7 */
#define ICMD_SELW_IM		0xb0	/* select IMR for writing */
#define ICMD_SELW_AC		0xc0	/* select ACR for writing */
#define ICMD_VECTOR		0xe0	/* select vector for writing */

/* mode register bits defs for writing */

#define DRJMODE_ROTATING	0x01	/* rotating interrupt priority */
#define DRJMODE_COMMON		0x02	/* use only one interrupt vector */
#define DRJMODE_POLLED		0x04	/* disable interrupts */
#define DRJMODE_HIGHEDGE	0x10	/* interrupt on rising edge */

/* the same mode bits for reading from status register */
	
#define DRJSTAT_GINT		0x80	/* some interrupt is present */
#define DRJSTAT_EI		0x40	/* interrupts are enabled */
#define DRJSTAT_ROTATING	0x20	/* rotating interrupt priority */
#define DRJSTAT_POLLED		0x10	/* interrupts disabled */
#define DRJSTAT_ARMED		0x08	/* interrupts are armed */

/* csr control bits */

#define DRJCSR_RDY		0x80	/* usr device is ready */
#define DRJCSR_IE		0x02	/* enable interrupts (csra only) */
#define DRJCSR_DIR		0x01	/* direction (output) */

/* application protocol bits */

#define HANDSHAKE	0x8000
#define LISTEN		0x0800
#define READ_READY	0x0400
#define WRITE_READY	0x0200
#define SIGNAL_ACK	0x0100

#define INTERRUPT0	0x0001
#define INTERRUPT1	0x0002

#define SIGOUT		HANDSHAKE
#define SIGIN		HANDSHAKE

/* we have to use the preprocessor to combine bits because the assember
   is to dumb to do this */

#define CLEARMASK0	(ICMD_CLEAR_MR)
#define CLEARMASK1	(ICMD_CLEAR_MR | 1)

	OSIGOUT =	 0xfffd
	OSIGIN =	 0x7fff
	

.globl hostListen
.globl hostAttach
.globl hostDetach
.globl hostWrite
.globl hostRead
.globl hostSignal

.globl hostError
.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 ... */


	PRCSR = 0177564
	PRBUF = 0177566

#define PUTN(x)		8: tstb *$PRCSR; bpl 8b; movb $48.+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:

	/ the DRV11-J is set up as follows:
	/
	/	csra	is used for enabling interrupts.
	/       bufa	is used for input signals
	/	bufb	is used for output signals
	/	bufc	is used for input data
	/	bufd	is used for output data

	/	icmd0	is used to command interrupt group 0
	/	ibuf0	
	/	icmd1	is used to command interrupt group 1
	/	ibuf1

	mov	2(sp), r0		/ record the device addresses

	mov	r0, hostAddr
	mov	r0, dr_icmd0
	inc	r0
	mov	r0, dr_csra
	inc	r0
	mov	r0, dr_isig
	add	$2, r0
	mov	r0, dr_ibuf0
	inc	r0
	mov	r0, dr_csrb
	inc	r0
	mov	r0, dr_osig
	add	$2, r0
	mov	r0, dr_icmd1
	inc	r0
	mov	r0, dr_csrc
	inc	r0
	mov	r0, dr_ibuf
	add	$2, r0
	mov	r0, dr_ibuf1
	inc	r0
	mov	r0, dr_csrd
	inc	r0
	mov	r0, dr_obuf

	clrb	*dr_csra		/ 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	$onDRintr, (r0)		/ basic interrupt handler 
	mov	$1, 2(r0)		/ argument is 1 for 2nd interrupt
	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

	/ Now configure the device to be able to recieve two interrupts
	/ from lines 0 and 1 on the insig buffer (bufa). Mask all
	/ interrupts on group 1. Enable device interrupts, and set
	/ up the buffers so that bufa and bufc are input buffers, and
	/ bufb and bufd are output buffers.

#define VECTOR0		(ICMD_VECTOR | 0x08)
#define VECTOR1		(ICMD_VECTOR | 0x08 | 1)


	movb	$ICMD_RESET, *dr_icmd0	/ reset vector group 0
	movb	$ICMD_RESET, *dr_icmd1	/ reset vector group 1

	/ set up the two interrupt vectors on the DRV11-J

	mov	4(sp), r0		/ get vector number
	ash	$-2, r0			/ divide by 4
	movb	$VECTOR0, *dr_icmd0
	movb	r0, *dr_ibuf0
	inc	r0
	movb	$VECTOR1, *dr_icmd0
	movb	r0, *dr_ibuf0

	clrb	*dr_csra
	bisb	$DRJCSR_DIR, *dr_csrb
	clrb	*dr_csrc
	bisb	$DRJCSR_DIR, *dr_csrd

	/ set the ACR register to clear for all bits:

	movb	$ICMD_SELW_AC, *dr_icmd0
	movb	$0xff, *dr_ibuf0

	/ set the interrupts to trigger on a high edge

#define HIGHEDGE	(ICMD_MODE | DRJMODE_HIGHEDGE)
	movb	$HIGHEDGE, *dr_icmd0

	/ arn the interrupts

	movb	$ICMD_SELR_ARM, *dr_icmd0

	rts 	pc

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

onDRintr:
	tst	2(sp)
	bne	1f
	tst	intraFxn
	beq	3f
	jsr	pc, _pushr
	mov	intraFxn, r0
	clr	intraFxn
	mov	intraArg, -(sp)
	br	2f
1:
	tst	intrbFxn
	beq	3f
	jsr	pc, _pushr
	mov	intrbFxn, r0
	clr	intrbFxn
	mov	intrbArg, -(sp)
2:
	jsr	pc, (r0)
	tst	(sp)+
	jsr	pc, _popr
3:
	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_isig, -(sp)		/ push isig buf
/	mov	*dr_osig, -(sp)		/ push osig buf (probably not needed)
	tst	*dr_ibuf		/ check if we have a signal
	beq	3f			/ illegal signal, ignore
	bis	$SIGNAL_ACK, *dr_osig	/ acknowledge signal
	mov	$20000., r0
2:
	tst	*dr_isig		/ wait for input signal to clear
	beq	1f
	sob	r0, 2b
	br	3f
1:
	bic	$SIGNAL_ACK,*dr_osig	/ clear acknowledge

	cmp	$1, (sp) 		/ signal #1?
	bne	1f
	tst	sig1Fxn
	beq	3f			/ goto return
	jsr	pc, _pushr
	mov	sig1Fxn, r0
	clr	sig1Fxn
	mov	sig1Arg, -(sp)
	br	2f
1:
	cmp	$2, (sp)		/ signal #2 ?
	bne	3f
	tst	sig2Fxn
	beq	3f			/ goto return
	jsr	pc, _pushr
	mov	sig2Fxn, r0
	clr	sig2Fxn
	mov	sig2Arg, -(sp)
2:
	jsr	pc, (r0)
	tst	(sp)+
	jsr	pc, _popr
3:	
/	mov	(sp)+, *dr_osig
	tst	(sp)+
	mov	(sp)+, r0
	rts	pc

sig1Fxn:
	.word	0
sig1Arg:
	.word	0
sig2Fxn:
	.word	0
sig2Arg:
	.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
	bisb	$DRJCSR_IE, *dr_csra
	movb	$CLEARMASK0, *dr_icmd0

	mov	$1, r0
	rts	pc
1:
	cmp	2(sp), $2		/ signal 2 ?
	bne	1f
	mov	6(sp), sig2Arg
	mov	4(sp), sig2Fxn
	bisb	$DRJCSR_IE, *dr_csra
	movb	$CLEARMASK0, *dr_icmd0

	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:
	bisb	*DRJCSR_IE, *dr_csra
	movb	$CLEARMASK1, *dr_icmd0
	mov	$10., r0			/  to catch strays
1:
	sob	r0, 1b
	mov	2(sp), intrbFxn
	mov	4(sp), intrbArg
	bis	$LISTEN, *dr_osig	/ 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:
	bit	$LISTEN, *dr_isig
	beq	1f
	mov	$HST_SYNC_E, hostError
	jbr	7f
1:
	bis	$SIGOUT, *dr_osig	//
/	clr	*dr_csr
	bic	$LISTEN, *dr_osig
1:
	bit	*LISTEN, *dr_isig
	bne	1b
	bic	$SIGOUT, *dr_osig	//
	bisb	$DRJCSR_IE, *dr_csra
	movb	$CLEARMASK1, *dr_icmd0
	mov	$1, r0
	rts	pc
7:
	bicb	$DRJCSR_IE, *dr_csra
	movb	$ICMD_SET_MR_ALL, *dr_icmd0
	clr	*dr_osig
	mov	$-1, r0
	rts	pc

#if 0			/* old routine for catching signals from host */
.globl hostCatch
hostCatch:
	mov	2(sp), intraFxn
	mov	4(sp), intraArg
	bis	$DR_IEA, *dr_csr	
	rts	pc
#endif

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

hostDetach:
	bicb	$DRJCSR_IE, *dr_csra
	movb	$ICMD_SET_MR_ALL, *dr_icmd0
	clr	*dr_osig
	clr	sig1Fxn
	clr	sig2Fxn
	clr	intraFxn
	clr	intrbFxn
	rts 	pc

/ send a signal to the host CPU 

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

/* macros for the read and write routines */

#if 0
#define SIGNAL_HOST	xor r0,*dr_osig; xor r0,r3
#define WAIT_FOR_HOST	9: cmp r3,(r5); beq 9b
#else
#define SIGNAL_HOST	mov $SIGOUT,r0; xor r0,*dr_osig; mov $SIGIN,r0; xor r0,r3
#define WAIT_FOR_HOST	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_isig, r5			/ use r5 to address dr11 isig

/	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

	bis	$WRITE_READY, *dr_osig		/ tell host we are ready
1:						
	bit	$READ_READY, *dr_isig		/ 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

	tst	r1
	beq	2f
1:	
	mov	(r2)+, (r4)			/ output all the words
	SIGNAL_HOST
	WAIT_FOR_HOST
	sob	r1, 1b
2:	
	mov	(sp), (r4)			/ send checksum
	SIGNAL_HOST
	WAIT_FOR_HOST

	clr	(r4)				/ clear output buffer
	SIGNAL_HOST

	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_isig, r5			/ use r5 to address dr11 isig
#if 0
	mov	(r5), r3			/ Load old csr value
#else
	mov	(r5), r3			/ Load old csr value
	bic	$OSIGIN, r3
#endif
	
	bit	$READ_READY, *dr_osig		/ send READY signal

	WAIT_FOR_HOST

	bic	$READ_READY, *dr_osig		/ 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
	mov	(r4), (r2)+
	sob	r1, 1b
2:	
	SIGNAL_HOST				/ get checksum
	WAIT_FOR_HOST
	mov	(r4), (sp)

	SIGNAL_HOST				
	WAIT_FOR_HOST				/ host output buffer is clear
	bic	$SIGOUT, *dr_osig		/ clear the signal bit

	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:
	bicb	$DRJCSR_IE, *dr_csra
	movb	$ICMD_SET_MR_ALL, *dr_icmd0
	clr	*dr_osig
	mov	$-1, r0
	add	$4, sp
	POPR
	rts	pc

.data

hostError:	.word  0
hostSize:	.word  6
hostAddr:	.word  0

dr_csra:	.word  -1
dr_isig:	.word  -1
dr_csrb:	.word  -1
dr_osig:	.word  -1
dr_csrc:	.word  -1
dr_ibuf:	.word  -1
dr_csrd:	.word  -1
dr_obuf:	.word  -1

dr_icmd0:	.word  -1
dr_ibuf0:	.word  -1
dr_icmd1:	.word  -1
dr_ibuf1:	.word  -1
