Google
 

Trailing-Edge - PDP-10 Archives - 7.01A_PCL20_KMCSRC_19830513 - kmcser.mac
There are no other files named kmcser.mac in the archive.
TITLE KMCSER - Service for KMC controlled DZ11 communication lines - V003
SUBTTL	S. A. Davidson	01-Nov-80/TL

XP VKMCSR,003

	COMMENT	@

Modifications to upgrade this code to TOPS-10 V 7.01A are
	COPYRIGHT (C) 1983 DIGITAL EQUIPMENT CORPORATION, MAYNARD MASS.

	THIS SOFTWARE IS FURNISHED UNDER LICENSE, AND MAY BE USED AND COPIED
ONLY ACCORDING TO THE TERMS OF SUCH LICENSE.

Also note:
	This code seems to have been taken from Digital's D8KINT for 7.01.
As such, title to the original code seems unclear.  TL/DEC 10-may-83

		@
;***********************************************************************
;
;	PROGRAM KMCSER
;
;	PURPOSE:
;		THIS ROUTINE IS THE TOPS-10 DRIVER FOR THE
;		KMC11B DEVICE WITH 8 LINES AVAILABLE.
;
;	INPUTS:
;		MONITOR CALL PARAMETERS FROM UUOCON
;		DATA IN KMC CSR'S
;
;	OUTPUTS:
;		DATA TO KMC CSR'S
;		STATUS RETURN TO MONITOR CALLS
;
;	INTERFACES:
;		AS A TOPS-10 DRIVER, THIS ROUTINE IS CALLED
;		BY AND RETURNS TO UUOCON.
;		IT ALSO RESPONDS TO INTERRUPTS PROVIDED BY
;		THE KMC11B.
;
;	CPPS REFERENCE PARAGRAPH NUMBER:  3.2.2
;
;	HISTORY:
;		RELEASE NUMBER:	1(1)
;		RELEASE DATE:	MARCH 13, 1981
;		PROGRAMMER:	STU DAVIDSON
;		ORGANIZATION:	FREY FEDERAL SYSTEMS
;
;	PROBLEMS:
;
;
;
;
;*************************************************************************
	SEARCH	F,S
	SALL

	$RELOC
	$LOW			;define local data area first


DEFINE	RETSKP,< JRST CPOPJ1## >	;SKIP RETURN MACRO
SUBTTL	DMC SPECIFIC DDB DEFINITIONS


;prototype definition for DMC20 interrupt service entry and DDB - DMCDDB

	.TYDMC==61			;DEVICE TYPE CODE FOR DMC20
	DMCTYP==61B27
 	PDVTIM=3400			;DEVICE HUNG TIME CONSTANT
	BFRSZE=^D<256/4>+1			;DEFAULT BUFFER SIZE
	kmcqln=10.		;length of message queue to KMC

DEFINE	CHNMAC	(Z)	<
	CHN'Z'CB##>			;END OF CHNMAC MACRO


	DMCMOD==1_I+1_IB+1_D		;image, image binary, dump


; DEVICE DEPENDANT BITS IN LH(DEVIOS)

PCIACT==(1B1)		;INPUT ACTIVE
PCOACT==(1B2)		;OUTPUT ACTIVE
DVOACT==(1B3)		;DEVOP PENDING

	ENTRY	KMCINT,KMCDDB,KMCDDS
;DMC DDB BLOCK

KMCDDB::
dmcDDB::PHASE	0			;THE FIRST DDB LOCATION
	SIXBIT /dmc/			;(0) DEVNAM
	XWD PDVTIM,BFRSZE		;(1) DEVCHR
	0				;(2) DEVIOS
	XWD 0,DMCDSP			;(3) DEVSER
	XWD DVIN+DVOUT+DVLNG,DMCMOD	;(4) DEVMOD
	0				;(5) DEVLOG
	0				;(6) DEVBUF
	XWD R,0				;(7) DEVIAD
	XWD R,0				;(10) DEVOAD
	0				;(11) DEVSTS
	XWD DEPLEN!DMCTYP,DEPEVM	;(12) DEVSTA
	0				;(13) DEVXTR
	0				;(14) DEVEVM
	0				;(15) DEVPSI
	0				;(16) DEVESE
	0				;(17) DEVHCW
	0				;(20) DEVJOB
DMCPTR:!0				;INPUT DATA POINTER WORD
dmckmc:!KMC0CB				;link to owning KMC
DMCDV2:! 0				;place to store sel2 for DEVOP.
DMCDV4:! 0				;place to store sel4, sel6

KMCDDS::!				;length of DMC DDB

	DEPHASE


SUBTTL	KMC -- KMC block.  One for each KMC that drives DZ's


KMCINT::
KMCAIN:	0			;TRANSMIT INTERRUPT VECTOR ENTRY
	EXCH	T1,KMCAIN	;GET INTERRUPT PC
	MOVEM	T1,KMCCHL##	;PUT INTO -10 STYLE INTERRUPT HANDLER
	EXCH	T1,KMCAIN	;AND RESTORE T1
	JSR	KMCSAV##	;SAVE AC'S
	JSP	W,KMCIVA	;handle ready for input


KMCBIN:	0			;RECEIVER INTERRUPT VECTOR ENTRY
	EXCH	T1,KMCBIN	;GET INTERRUPT PC
	MOVEM	T1,KMCCHL	;PUT INTO -10 STYLE INTERRUPT HANDLER
	EXCH	T1,KMCBIN	;RESTORE T1
	JSR	KMCSAV##	;SAVE AC'S
	JSP	W,KMCIVB	;Vector B interrupt


;TRADITIONALY INDEXED BY "W"

	%%%OFF==0		;INITIALIZE FOR "X" MACRO

	KMCQLN==^D32		;32 QUEUE ENTRIES IN TRANSACTION Q (64 WORDS)
	MXNDUP==^D2		;MAXIMUM NUMBER OF DUP-11'S ON THE KMC-11

KMC0CB::PHASE 0
KMCTBL:!EXP	DMCDDB		;DDB table
	block	7		; ...
KMCCSR:!EXP KMC1BA	;XWD UBA #,ADDRESS OF KMC-11 CSR (KMC1BA)
KMCVEC:!EXP KMC1IV	;ADDRESS OF KMC-11 VECTOR (540 = KMC1IV)
KMCCDB:!CHNMAC \M.KMCC	;address of channel block
KMC1DZ:!EXP 0		;-11 ADDRESS OF FIRST DZ11
KMCCPC:!EXP 0		;PC OF THE LAST CALLER TO KMCERR.
KMCZER:!		;START ZERO-ING HERE ON A RESTART
KMCSTS:!EXP 0		;STATUS.
    KMCSRU==1B0		;SAYS KMC-11 IS BELIEVED TO BE RUNNING
KMCACT:!EXP 0		;COUNT OF VECTOR "A" INTERRUPTS
KMCBCT:!EXP 0		;COUNT OF VECTOR "B" INTERRUPTS
KMCIQT:!EXP 0		;INPUT QUEUE "TAKER"
KMCIQP:!EXP 0		;INPUT QUEUE "PUTTER"
KMCINQ:!BLOCK 2*KMCQLN	;KMC-11 INPUT QUEUE.  TWO WORD ENTRYS CONTAIN
			;  XWD 0,SEL2
			;  XWD SEL4,SEL6
KMCLEN:!		;LENGTH OF A KMC BLOCK
	DEPHASE
	SUBTTL	DEFINITIONS -- KMC11

SEL0==0
BSEL1==1
	KMCRUN==100000		;RUN FLOP
	KMCMCL==040000		;MASTER CLEAR
	KMCCWR==020000		;CRAM WRITE
	KMCSLU==010000		;STEP LINE UNIT
	KMCLUL==004000		;LINE UNIT LOOP
	KMCRMO==002000		;ROM OUTPUT
	KMCRMI==001000		;ROM INPUT
	KMCSUP==000400		;STEP u-PROCESSOR
	KMCRQI==000200		;REQUEST INPUT
	KMCIEO==000020		;INTERRUPT ENABLE OUTPUT
	KMCIEI==000001		;INTERRUPT ENABLE INPUT
SEL2==2
BSEL3==3			;CONTAINS LINE NUMBER
	KM3CMS==170000		;KMC command status
	KM3IOD==004000		;I/O direction (1 for out, 0 for in)
	KM3FCN==003400		;function code
	  F.DZS==0*400		;DZ status
	  F.LNS==1*400		;Line status
	  F.KIL==F.LNS!KM3IOD	;kill I/O (on close)
	  F.LPR==2*400		;Line parameters
	  F.DRAM==3*400		;read/write data ram
	  F.DATA==4*400		;data i/o request
	KMCRDO==000200		;READY FOR OUTPUT
	KMCRDI==000020		;READY FOR INPUT
	KM2LIN==000007		;Line number
SEL4==4
BSEL5==5
		;16 bit address for F.DATA  (output)
		;real byte count for F.DATA (input)
		;DZ11 address for F.DZS
SEL6==6
BSEL7==7
	KM6XBA==000014	;extended addressing bits F.DATA (output)
	;file status bits for F.DATA (input):
	 ddead==200	;device dead
	 HDTERR==100	;hard data error
	 TIMOUT==040	;line time out
	 EOF==020	;eof (ETX in block)
	 IODIR==010	;I/O direction
	 LDEAD==004	;line dead
	 STXETX==002	;STX/ETX sequencing error
	 STXINB==001	;STX in this block

;KMC11B IS 4K WORDS CRAM, 4K BYTES DRAM
CRAMSZ==10000			;SIZE OF KMC11 CRAM
DRAMSZ==10000			;SIZE OF KMC11 DRAM
;
;KMC11A IS 1K WORDS CRAM, 1KBYTES DRAM
;CRAMSZ==2000			;SIZE OF KMC11A CRAM
;DRAMSZ==2000			;SIZE OF KMC11A DRAM
SUBTTL	KMC SERVICE DISPATCH TABLE

	$HIGH

;KMC-11 DRIVER SERVICE DISPATCH TABLE

	JRST	KMCONL		;(-5)SEE IF THE KMC IS ON-LINE NOW
	JRST	KMCDVP		;(-4)KMC-11 DEVOP. UUO DISPATCH
	JRST	KMCOPN		;(-3)OPEN CALL, GET BUFFER SIZE FROM DDB
	JRST	KMCINI		;(-2)INITIALIZE KMC NETWORK
	JRST	KMCHNG		;(-1)HUNG DEVICE ERROR
DMCDSP::JRST	KMCREL		;(0)RELEASE
	JRST	KMCCLS		;(1)CLOSE
	JRST	KMCOUT		;(2)OUTPUT
	JRST	KMCINP		;(3)INPUT
	POPJ	P,		;(4)ENTER
	POPJ	P,		;(5)LOOKUP
	JRST	KMCDOU		;(6)DUMP OUTPUT
	JRST	KMCDIN		;(7)DUMP INPUT
	POPJ	P,		;(10)USETO
	POPJ	P,		;(11)USETI
	POPJ	P,		;(12)GETF UUO
	POPJ	P,		;(13)RENAME UUO
	PJRST	KMCCLS		;(14)CLOSE INPUT
	POPJ	P,		;(15)UTPCLR
	POPJ	P,		;(16)MTAPE UUO


; Fix buffer size and EVM required on OPEN

KMCOPN:	LDB	T3,PIOMOD##	;see what mode we're to use
	CAIE	T3,D		;is it DUMP?
	 JRST	BUFOPN		;no, must be a buffered mode
	MOVEI	T1,CRAMSZ/4+1	;CRAM SIZE RETURNED AS BUFFER SIZE
	POPJ	P,
BUFOPN:	MOVEI	T1,^D256/4+1	;DATA BUFFER SIZE
	CAIE	T3,I		;IMAGE IS REAL DATA TRANSFER
	 MOVEI	T1,DRAMSZ/4+1	;IB MODE READS DATA RAM
	POPJ	P,
SUBTTL	Once only code

;KMCINI is called at ONCE time.  It is responcible for initializing
;data structures.  In particular, DDB's built at ONCE time must be
;adjusted to reflect unit numbers.
; At entry, F contains a DDB address.

KMCINI:	MOVE	T2,DEVNAM(F)	;Unit number on this KMC (in sixbit)
	ANDI	T2,7		;get binary line number
	DPB	T2,PUNIT##	;Store as line number
	move	w,dmckmc(f)	;get pointer to KMC block
	ADDI	T2,KMCTBL(W)	;compute addr of back pointer
	MOVEM	F,(T2)		;Create link from KMC to DMC
	JRST	CPOPJ1		;Skip return, get called for each DDB
SUBTTL	CHECKS FOR KMC ON-LINE & HUNG

KMCONL:	AOS	(P)			;ADJUST FOR SKIP RETURN
	POPJ	P,			;RETURN

KMCHNG:	PUSHJ	P,SAVE1			;we need P1
	PUSHJ	P,LOADW			;and our registers loaded
	MOVEI	T1,F.KIL		;tell KMC to stop
	SETZ	T2,			; SEL4,sel6
	PUSHJ	P,KMCQUE		;queue message to KMC
	TRZ	S,IOACT			;no longer I/O active
	TRO	S,IODERR!IODEND		;set error & EOF
	JRST	STOIOS##
SUBTTL	DEVOP UUO INTERFACE - DISPATCH

;HERE ON A DISPATCH FROM UUOCON
;	F=DDB
;	T1=FUNCTION
;
;THE FOLLOWING ARE ERROR CODES WHICH CAN RESULT FOR THE KMC DEVOP UUO'S
;
;
;ERROR CODE	TYPE UUO		DESCRIPTION
;
;	1	GENERAL		FUNCTION CODE = 0. SHOULD NEVER OCCUR AT
;				DRIVER LEVEL SINCE UUOCON CHECKS FOR ZERO
;	2	GENERAL		FUNCTION CODE NOT VALID FOR KMC-11 DRIVER
;	3	GENERAL		CHECK OF THE KMC UBA ADDRESS FAILED
;	4	GENERAL		TEST OF JOB ASSIGNED FAILED
;
;ERRORS

	KE%ILF==ECOD1##		;ILLEGAL FUNCTION CODE
	KE%ILK==ECOD2##		;ILLEGAL KMC NUMBER
	KE%ALS==ECOD3##		;ARG LIST TOO SHORT
	KE%IWR==ECOD4##		;ILLEGAL WHEN KMC-11 RUNNING
	KE%ICA==ECOD5##		;ILLEGAL CRAM ADDRESS (READ OR WRITE)
	KE%ILL==ECOD6##		;ILLEGAL LINE NUMBER
	KE%KNR==ECOD7##		;KMC-11 NOT RUNNING (KDL TYPE OP'S)
	KE%LNS==ECOD10##	;KDL LINE NOT STARTED
	KE%LAS==ECOD11##	;KDL LINE ALREADY STARTED
	KE%UNP==ECOD12##	;USER NOT PRIVLEDGED
KMCDVP:	PUSHJ	P,SAVE4##	;SAVE P1 THRU P4
	MOVE	P2,T1		;make a more permanent copy
	MOVSI	T2,-KMCDVL	;GET TABLE LENGTH SETUP AOBJN PTR
KMCDV1:	HLRZ	T3,KMCDVT(T2)	;GET FUNCTION
	HRRZ	P3,KMCDVT(T2)	;GET THE DISPATCH ADDRESS
	CAMN	P2,T3		;DO CODE MATCH?
	JRST	KMCDV2		;YES, SO DISPATCH
	AOBJN	T2,KMCDV1	;NO, GO LOOP
	PJRST	ECOD2##		;NO MATCH, GIVE ERROR RTN

KMCDV2:	PUSHJ	P,TSTJBN	;SEE IF USER OWNS DDB
	PUSHJ	P,CKPVBT	;TEST FOR [1,2] OR JACCT
	PUSHJ	P,SETBAS	;LOAD KMC BASE ADDRESS INTO P1
	TRNN	P2,3000		;read/write type funct?
	 JRST	(P3)		;no,  just dispatch
	HLRE	T3,P4		;yes, get actual aarg block len
	CAIGE	T3,4		;need at least 4 words
	 JRST	KE%ALS		;  too short
	JRST	(P3)		;long enuf .... dispatch

KMCDVT:	XWD 0,ECOD1##		;NO FUNCTION CODE SPECIFIED
	XWD 1,KMCST		;Start the KMC
	XWD 2,KMCHA		;Halt the KMC
	XWD 1004,KMCPRR		;protocol read registers
	XWD 1005,KMCBRR		;Blind read registers
	XWD 2004,KMCPWR		;protocol write registers
	XWD 2005,KMCBWR		;Blind write registers
	KMCDVL==.-KMCDVT	;DISPATCH TABLE LENGTH

; KMCBRR - Blind read of KMC registers (debug only?)

KMCBRR:	RDIO	T2,SEL0(P1)		;read all registers
	RDIO	T1,SEL2(P1)		; ...
	HRL	T1,T2			;merge sel0, sel2
	PUSHJ	P,PUTWD1##		;give to user
	RDIO	T2,SEL4(P1)		; ...
	RDIO	T1,SEL6(P1)		; ...
	HRL	T1,T2			;merge sel4, sel6
	PUSHJ	P,PUTWD1##		;give to user
	RETSKP

; KMCBWR - Blind write of KMC registers (Debug ONLY!!!)

KMCBWR:	PUSHJ	P,FTCHWD		;get user word
	WRIO	T1,SEL2(P1)		;write sel2
	HLRZ	T1,T1			;get sel0
	WRIO	T1,SEL0(P1)		;and sel0
	PUSHJ	P,FTCHWD		;get next word
	HLRZ	T2,T1			;get sel4 data
	WRIO	T2,SEL4(P1)		;write SEL4
	WRIO	T1,SEL6(P1)		;write SEL6
	RETSKP

; KMCPWR, KMCPRR - Read/Write commands to the KMC.
;These are nearly the same, because the KMC indicates completion status
;for "set parameters" type commands.

KMCPRR:
KMCPWR:	PUSHJ	P,FTCHWD		;get next user word (0,,sel2)
	MOVE	P3,T1			;save
	TRNE	P2,2000			;is this a write DEVOP.?
	 TROA	P3,KM3IOD		;set direction to out
	 TRZ	P3,KM3IOD		;set for read
	PUSHJ	P,FTCHWD		;get [sel4,,sel6]
	MOVE	P4,T1			;and save that!
	TLO	S,DVOACT		;mark DEVOP response pending
	PUSHJ	P,SETACT##		;set IO active
	DMOVE	T1,P3			;get data to write
	PUSHJ	P,KMCQUE		;queue write registers request
	PUSHJ	P,WAIT1##		;wait for IO active to vanish!
	TLZ	T1,DVOACT		;no longer waiting for DEVOP.
	MOVEM	S,DEVIOS(F)		;store status
	DMOVE	T1,DMCDV2(F)		;get registers read
	EXCTXU <DMOVEM T1,-1(M)>	;return data to users
	RETSKP
; #1 -- START THE KMC-11.  THIS FIRST STARTS THE KMC-11, AND THEN
;	SETS UP THE INTERRUPT VECTORS. ONCE THE VECTORS ARE SET UP,
;	IT SETS THE TWO KMC-11 INTERRUPT ENABLES, AND DECLARES THE KMC RUNNING.
KMCST:	MOVEI	T1,KMCRUN	;GET THE RUN BIT
	TIOE	T1,@KMCCSR(W)	;MAKE SURE THE KMC-11 ISN'T RUNNING ALREADY
	PJRST	KE%IWR		;IF IT IS, GIVE "ILLEGAL WHEN RUNNING" ERROR
	WRIO	T1,@KMCCSR(W)	;SET RUN NOW, WILL SET IEI & IEO SOON
				;  (MUST GIVE KMC TIME TO INIT)

	MOVE	T1,KMCVEC(W)	;GET THE ADDRESS OF THE VECTOR
	LSH	T1,-2		;CONVERT BYTE OFFSET INTO WORD OFFSET
	HRLI	T2,(JSR)	;GET BODY OFF A "JSR" INSTRUCTION
	HRRI	T2,KMCIVA(W)	;GET ADDRESS OF VECTOR "A" INTERRUPT SERVICE
	MOVEM	T2,VECTB3##(T1)	;STORE ADDRESS OF ROUTINE TO FORCE JSR TO
	HRRI	T2,KMCIVB(W)	;GET ADDRESS OF "B" SERVICE ROUTINE
	MOVEM	T2,VECTB3##+1(T1);SET UP VECTOR "B" INTERRUPT SERVICE

	MOVEI	T1,KMCRUN!KMCIEI!KMCIEO	;START THE KMC-11 AND ENABLE INTS
	WRIO	T1,@KMCCSR(W)	;START THE KMC-11

	MOVSI	T1,(KMCSRU)	;GET AND SET THE "RUNNING"
	IORB	T1,KMCSTS(W)	;  BIT SO IT AT LEAST LOOKS LIKE WE'RE UP.

	RETSKP			;ALL DONE. GIVE THE USER A GOOD RETURN




; #2 -- HALT THE KMC-11. THIS DOES NOT DECLARE DUP-11 LINES DOWN SINCE
;	SETTING THE RUN BIT WOULD, IN THEORY, ALLOW THE KMC-11 TO CONTINUE.
KMCHA:	MOVEI	T1,0		;GET THE KMC-11 RUN BIT
	WRIO	T1,@KMCCSR(W)	;CLEAR THE RUN/MAINT BITS
	MOVSI	T1,(KMCSRU)	;GET THE "MICROCODE RUNNING" BIT
	ANDCAM	T1,KMCSTS(W)	;  AND CLEAR THAT
	RETSKP			;GIVE GOOD RETURN

; Read KMC-11 cram.  KMC-11 must be halted.
KMCDIN:	PUSHJ	P,SAVE4		;save the P's
	PUSHJ	P,LOADW		;setup W, check that KMC exists
	MOVEI	T1,KMCRUN	;GET THE RUN BIT
	TIOE	T1,SEL0(P1)	;SEE IF THE KMC-11 IS RUNNING
	 PJRST	IMPERR		;IF IT IS RUNNING, GIVE AN ERROR
	MOVEM	S,DEVIOS(F)	;S is destroyed by COMCHK
	PUSHJ	P,COMCHK##	;get first IOWD, and buffer mapped
	JUMPE	T1,CPOPJ##	;no IOWD??
	JUMPN	S,ADRERR##	;address check??
	HLRE	P3,T1		;isolate word count
	ASH	P3,1		; *2 (cram words per 10 word)
	HRLI	T1,(POINT 18,,35) ;ILDB pointer to next word
	SETZ	P2,		;cram address
RDLOOP:	MOVEI	T2,KMCRMO	;GET THE "READ CRAM" BIT
	WRIO	T2,SEL0(P1)	;ENABLE THE KMC-11 FOR MAINT CRAM rd/wt
	WRIO	P2,SEL4(P1)	;STORE THE ADDRESS IN MAINT CRAM ADDR REG
	RDIO	T2,SEL6(P1)	;read the cram memory buffer register
	XCT	PX.SRC!PX.BYT,[IDPB	T2,T1]	;GET THE VALUE TO WRITE
	ADDI	P2,1		;step CRAM addr
	AOJL	P3,RDLOOP	;and loop.
	POPJ	P,


; Write KMC-11 cram.  Just like read (almost).
KMCDOU:	PUSHJ	P,SAVE4##	;save registers
	PUSHJ	P,LOADW		;SET W,P1, CHECK FOR DEVICE EXISTS
	PUSHJ	P,KMCHA		;halt the KMC (in case it's running)
	MOVEI	T1,KMCRUN	;GET THE RUN BIT
	TIOE	T1,SEL0(P1)	;SEE IF THE KMC-11 IS RUNNING
	 PJRST	IMPERR		;IF IT IS RUNNING, GIVE AN ERROR
	MOVEM	S,DEVIOS(F)	;S is destroyed by COMCHK
	PUSHJ	P,COMCHK##	;get first IOWD, and buffer mapped
	JUMPE	T1,CPOPJ##	;no IOWD??
	JUMPN	S,ADRERR##	;address check??
	HLRE	T3,T1		;isolate word count
	ASH	T3,1		; *2 (cram words per 10 word)
	HRLI	T1,(POINT 18,,35) ;ILDB pointer to next word
	MOVE	P3,T1		;save byte pointer for verify
	MOVE	P4,T3		; and word count
	SETZ	P2,		;cram address
LDLOOP:	MOVEI	T2,KMCRMO	;GET THE "READ CRAM" BIT
	WRIO	T2,SEL0(P1)	;ENABLE THE KMC-11 FOR MAINT CRAM rd/wt
	WRIO	P2,SEL4(P1)	;STORE THE ADDRESS IN MAINT CRAM ADDR REG
	XCT	PX.BYT!PX.SRC,[ILDB	T2,T1]	;GET THE VALUE TO WRITE
	WRIO	T2,SEL6(P1)	;PUT IT IN THE CRAM MEMORY BUFFER REGISTER
	ADDI	P2,1		;increment cram addr
	MOVEI	T2,KMCCWR	;GET THE CRAM WRITE BIT
	BSIO	T2,SEL0(P1)	;CLOCK THE DATA INTO THE CRAM
	JFCL			;delay 1
	BCIO	T2,SEL0(P1)	;clear write cram toggle
	AOJL	T3,LDLOOP	;and loop.
;	now verify the load
	SETZ	P2,		;cram address
VRLOOP:	MOVEI	T2,KMCRMO	;GET THE "READ CRAM" BIT
	WRIO	T2,SEL0(P1)	;ENABLE THE KMC-11 FOR MAINT CRAM rd/wt
	WRIO	P2,SEL4(P1)	;STORE THE ADDRESS IN MAINT CRAM ADDR REG
	XCT	PX.BYT!PX.SRC,[ILDB	T2,P3]	;get the value to be verified
	RDIO	T3,SEL6(P1)	;read the cram memory buffer register
	CAIE	T3,(T2)		;does the word match?
	 JRST	DEVERR		;device error
	ADDI	P2,1		;increment cram addr
	AOJL	p4,VRLOOP	;and loop.
	SETO	T2,		;get a non-zero
	WRIOB	T2,SEL2(P1)	; and set as a flag that KMC not init'd
	MOVEI	T1,KMCMCL	;get the KMC master clear
	WRIO	T1,SEL0(P1)	;set run now, will set IEI & IEO soon
	MOVEI	T1,KMCRUN	;get the KMC run bit
	WRIO	T1,SEL0(P1)	;set run now, will set IEI & IEO soon
				;  (must give kmc time to init)

	MOVE	T1,KMCVEC(W)	;get the address of the vector
	LSH	T1,-2		;convert byte offset into word offset
	HRLI	T2,(JSR)	;get body off a "JSR" instruction
	HRRI	T2,KMCAIN-KMC0CB(W)	;vector "A" interrupt service
	MOVEM	T2,VECTB3##(T1)	;store JSR to sevice routine
	HRRI	T2,KMCBIN-KMC0CB(W)	;GET ADDRESS OF "B" SERVICE ROUTINE
	MOVEM	T2,VECTB3##+1(T1);SET UP VECTOR "B" INTERRUPT SERVICE

	MOVEI	T1,KMCRUN!KMCIEI!KMCIEO	;START THE KMC-11 AND ENABLE INTS
	RDIOB	T2,SEL2(P1)	;KMC should have clear'd this by now
	JUMPN	T2,DEVERR	; declare device error if not!
	WRIO	T1,SEL0(P1)	;START THE KMC-11
	POPJ	P,		;done with loop ... exit

IMPERR:	TRO	S,IOIMPM	;set improper mode
	PJRST	STOIOS##	;store IOS and return to user


DEVERR:	MOVEI S,IODERR		;device error
	IORB S,DEVIOS(F)	;set error flag
	POPJ P, 		;and quit
;HERE TO GET A WORD FROM USERS ARGUMENT BLOCK

FTCHWD:	PUSHJ	P,GETWR1##		;GET USER ARGUMENT WORD
	SKIPA
	POPJ	P,			;RETURN
	POP	P,(P)			;FETCH FAILED
	PJRST	ECOD10##		;EXIT

;HERE TO TEST DDB OWNERSHIP

TSTJBN:	LDB	T2,PJOBN##		;GET NODE ASSIGNED JOB NO.
	CAMN	J,T2			;COMPARE WITH REQUESTING JOB
	POPJ	P,			;RETURN
	POP	P,(P)			;ADJUST STACK
	PJRST	ECOD4##			;JOB DOESN'T OWN DDB - ERROR

;HERE TO SETUP AND TEST KMC BASE ADDRESS

SETBAS:	MOVE	W,DMCKMC(F)		;GET KMC CONTROL BLOCK 
	MOVE	P1,KMCCSR(W)		;LOAD P1 WITH DEVICE ADDR
	MOVE	T1,P1			;T1 MUST HAVE ADDRESS
	PUSHJ	P,UBGOOD##		;GO CHECK UBA THERE
	SKIPA				;BAD UBA,ADDRESS OR BUS
	POPJ	P,			;YES, RETURN
	POP	P,(P)			;ADJUST STACK
	PJRST	ECOD3##			;EXIT

;HERE TO CHECK FOR PRIVILEDGE USER

CKPVBT:	popj	p,		;%%%%for now, always OK.
	PUSHJ	P,PRVJ##		;GOTO CHECK ROUTINE
	 POPJ	P,			;OK, GOOD RETURN
	POP	P,(P)			;NO - FORGET IT,BAD GUY!!!
	PJRST	ECOD5##			;EXIT

; Here to set W and P1 for I/O uuo calls

LOADW:	MOVE	W,DMCKMC(F)		;GET KMC CONTROL BLOCK 
	MOVE	P1,KMCCSR(W)		;LOAD P1 WITH DEVICE ADDR
	MOVE	T1,P1			;T1 MUST HAVE ADDRESS
	PUSHJ	P,UBGOOD##		;GO CHECK UBA THERE
	SKIPA				;BAD UBA,ADDRESS OR BUS
	POPJ	P,			;YES, RETURN
	POP	P,(P)			;ADJUST STACK
	TRO	S,IODERR		;set device error
	PJRST	STOIOS##		;store IOS and return
SUBTTL	OUT/OUTPUT KMC-11 UUO SERVICE

KMCOUT:	PUSHJ	P,SAVE4##		;SAVE P1 - P4
	PUSHJ	P,LOADW			;LOAD W, P1
	TLO	S,IO			;FLAG OUTPUT
	PUSHJ	P,KMSETO		;SETUP USER'S OUT BUFFER
	PJRST	SETACT##		;set I/O active and return



SUBTTL	IN/INPUT KMC-11 UUO SERVICE

KMCINP:	PUSHJ	P,SAVE4##		;SAVE P1 - P4
	PUSHJ	P,LOADW			;LOAD W, P1
	TLZ	S,IO			;FLAG OUTPUT
	PUSHJ	P,KMSETI		;setup user's input buffer
	PJRST	SETACT##		;set I/O active and return
SUBTTL	SETUP NEXT BUFFER ROUTINES

KMSETI:	HRRZ	T2,DEVIAD(F)		;GET INPUT BUFFER ADDRESS
	CAIA
KMSETO:	HRRZ	T2,DEVOAD(F)		;GET OUTPUT BUFFER ADDRESS
	SETZ	P1,			;CLEAR P1
	LDB	T1,PIOMOD##		;get address-1 and correct
	ADD	T2,[IOWD ^D256/4,2	;    buffer size for MAPIO
		    0			;    and form IOWD
		    0			;
		    IOWD DRAMSZ/4,2]-I(T1)	;
	MOVE	P3,KMCCDB(W)		;get Channel Data Block
	LDB	T1,PUNIT		;get line #
	LSH	T1,1			;2 pages per DZ line
	ADD	T1,[KMCIMR##]		;compute correct first page
	MOVEM	T1,CHNIMR##(P3)		;store for MAPIO
	PUSHJ	P,MAPIO##		;go setup UBA registers
	STOPCD	CPOPJ,JOB,KGL		;+++MAPIO GOT LOST
	LDB	T1,PIOMOD##		;get IO mode
	CAIE	T1,I			;is it image?
	 SKIPA	T1,[F.DRAM]		;IB mode - read DRAM
	 MOVEI	T1,F.DATA		;normal data i/o
	TLNE	S,IO			;in or out?
	 TRO	T1,KM3IOD		;set direction for output
	HRLZ	T2,CHNIEA##(P3)		;11 style virtual buffer addr
	PUSHJ	P,KMCQUE		;queue request to KMC
	POPJ	P,			;RETURN
SUBTTL	CLOSE & RELEASE

KMCCLS:
KMCREL:	PUSHJ	P,SAVE1##		;we use P1 for KMC addr
	PUSHJ	P,LOADW			;load W, P1
	TLZ	S,DVOACT!IOEND		;no DEVOP pending!
	MOVEI	T1,F.KIL		;tell KMC to cancel
	SETZ	T2,			; sel4 = sel6 = 0
	PUSHJ	P,KMCQUE		;que rqst to KMC
	PJRST	CLRACT##		;return any EVM
	SUBTTL	INTERRUPTS -- INTERRUPT LEVEL INTERFACE TO THE KMC-11

                           Comment @

Each KMC-11 has two interrupt vector addresses.

    "A"	This interrupt is taken when RDYI (KMCRDI) comes up.  In this
	state the KMC-11 is ready for an input transaction.  All
	input transactions for the KMC-11 are queued in the KMC block.
	This is necessary because the following situation would otherwise
	cause a deadlock.
	   1)	The KMC-11 sets RDYO and gives a BUFFER-OUT transaction.
	   2)	At interrupt level, we want to do a BUFFER-IN.
	   3)	If, in the meantime, the KMC-11 has set RDYO again,
		we will not be able to get RDYI until we process another
		output transaction.
	The solution to this is to queue all input transactions.  This does
	mean that we have to take an interrupt on each transaction, but it
	does circumvent the above problem.

    "B"	This interrupt is taken when RDYO (KMCRDO) comes up.  In this
	state the KMC-11 wants to perform an output transaction.
	It is these output transactions that drive almost all of the
	interrupt level KMC processing.

The vector instructions are set up to be JSR's to the
locations "KMCIVA", and "KMCIVB" in the KMC block for the
KMC-11.  These locations contain the 5 or 6 instructions
necessary to save the AC's, load "W" with the address of the
particular KMC block, and dispatch to either of the two
interrupt routines "KMCIVA" or "KMCIVB".

                         End comment @
	SUBTTL	KMCIVA -- KMC-11 INTERRUPT VECTOR "A" PROCESSING.

;KMCIVA -- ROUTINE TO HANDLE KMC-11 INTERRUPT VECTOR "A" (INPUT)
;CALL	MOVE	W,[EXP KMC-BLOCK-ADDRESS]
;	PUSHJ	P,KMCIVA	;CALLED FROM KMCIVA IN THE KMC BLOCK
;RETURN	POPJ	P,		;TO DISMISS THE INTERRUPT.
;
;CLOBBERS MOST AC'S (WE SHOULD HAVE OUR OWN AC BLOCK ANYWAY)
;
;
KMCIVA::ADDI	W,KMC0CB-KMCBIN		;FIX KMC POINTER
	AOS	KMCACT(W)	;COUNT THE INTERRUPT

	MOVE	U,KMCCSR(W)	;GET THE UNIBUS ADDRESS OF THE KMC-11
	MOVEI	T1,KMCRDI	;GET THE "RDYI" FLAG
	TION	T1,SEL2(U)	;MAKE SURE "RDYI" IS UP
	PJSP	T1,KMCERR	;  IF IT'S NOT, THEN ITS AN ILLEGAL INTERRUPT
	MOVE	T3,KMCIQT(W)	;GET NUMBER OF NEXT QUEUED TRANSACTION
	CAMN	T3,KMCIQP(W)	;MAKE SURE THAT IT'S DIFFERENT NOT THE "PUTTER"
	PJSP	T1,KMCERR	;  IF IT IS, THEN WE'RE GETTINT UNSOLICITED
				;  INTERRUPTS.  DECLARE KMC ILL.
	LSH	T3,1		;MAKE IT AN OFFSET INTO THE QUEUE
	ADDI	T3,KMCINQ(W)	;RELOCATE BY THE ADDRESS OF THE QUEUE
	MOVE	T1,0(T3)	;GET SEL2 DATA
	MOVE	T2,1(T3)	;GET SEL4, SEL6 DATA
	AOS	T3,KMCIQT(W)	;ADVANCE QUEUE TAKER
	CAIL	T3,KMCQLN	;IF ITS TIME TO WRAP AROUND, THEN
	SETZB	T3,KMCIQT(W)	;  WRAP AROUND TO THE FIRST ENTRY
	MOVEI	T4,KMCRQI	;GET THE REQUEST INPUT INTERRUPT BIT
	CAMN	T3,KMCIQP(W)	;IF QUEUE EMPTY (PUTTER = TAKER) THEN
	BCIO	T4,SEL0(U)	;  CLEAR RQI TO STOP THE INTERRUPTS

	WRIO	T2,SEL6(U)	;STORE TOP WORD
	MOVSS	T2,T2		;GET SEL4 DATA
	WRIO	T2,SEL4(U)	;  AND STORE THAT
	TRZ	T1,KMCRDI	;MAKE SURE RDYI CLEAR (SO KMC CAN RUN)
	WRIO	T1,SEL2(U)	;GIVE REST OF DATA TO KMC
	POPJ	P,		;ALL DONE

kmcerr:	jfcl			;put bpt here, if you want
	setz	t1,		;stop the "bad" kmc
	wrio	t1,sel0(u)	; ...
	popj	p,
;KMCIVB -- Routine to handle KMC-11 interrupt vector "B" (output)
;Call:	MOVE	W,[EXP KMC-BLOCK-ADDRESS]
;	PUSHJ	P,KMCIVB	;called from KMCIVB in the KMC block
;Return:POPJ	P,		;to dismiss the interrupt
;
;clobbers most AC's (we should have our own AC block anyways)
;
;
KMCIVB::AOS	KMCBCT(W)	;Count the interrupt
	MOVE	U,KMCCSR(W)	;get the Unibus Address of the KMC
	RDIO	P1,SEL2(U)	;read status bits
	TRNN	P1,KMCRDO	;better want an output transaction
	PJSP	T1,KMCERR	;illegal interrupt. crash KMC
	RDIO	P2,SEL4(U)	;read SEL4 for later analysis
	RDIO	P3,SEL6(U)	;read error, status or what ever
	PUSHJ	P,SAVE3##	;make sure csrs are saved
	MOVEI	T1,KMCRDO	;get the RDYO bit
	BCIO	T1,SEL2(U)	;clear it to let the KMC continue

	LDB	F,[POINT 3,P1,35] ;get 3 bit line #.
	ADD	F,W		;get correct DDB
	SKIPN	F,KMCTBL(F)	;load DDB address
	 POPJ	P,		;no DDB? - dismiss interrupt
	HRL	P3,P2		;merge SEL4, SEL6
	MOVEM	P1,DMCDV2(F)	;store registers in DDB
	MOVEM	P3,DMCDV4(F)	; ...
	PUSHJ	P,IOSET##	;set up S, R, J from DDB
	LDB	J,PJOBN##	;set J to owning job #
	PUSHJ	P,SVEUF##	;GET THE PAGING "RIGHT" (??)
	LDB	T1,[POINT 3,P1,35-8]	;get function just completed
	CAIE	T1,F.DATA/400	;buffer done?
	 CAIN	T1,F.DRAM/400	; (data or DRAM)
	  JRST	BUFDON		;yes, go handle buffer done
	TLNN	S,DVOACT	;doing DEVOP.?
	 POPJ	P,		;no, just return
	PUSHJ	P,SETIOD##	;yes, well it's done!
	PJRST	CLRACT##	;clear I/O active and exit
SUBTTL	BUFDON - buffered IO done

BUFDON:	ANDI	P3,377-IODIR	;mask to relevent file status bits
	LSH	P3,^D9		;shift to corresponding position for IOS
	TRZ	S,7700		;clear device dependent bits in IOS
	TRO	S,(P3)		;merge new file status bits
	MOVEM	S,DEVIOS(F)	;save updated device status
	PUSHJ	P,SETIOD##	;a buffer is done...job may want to run
	MOVE	P1,KMCCSR(W)	;load P1 with csr addr
	TLNN	S,IO		;which direction?
	 JRST	INPDON		;go handle input
;	here on output done
	PUSHJ	P,ADVBFE##	;advance output buffers
	 JRST	BUFSTP		;no more
	TRNE	S,760000	;TEST FOR KMC ERRORS OR EOF
	 JRST	BUFSTP		;  END TRANSMISSION IF FOUND
	PJRST	KMSETO		;setup next output buffer



INPDON:	HRRZ	T1,DEVIAD(F)	;get current buffer header
	EXCTXU	<HRRZM	P2,1(T1)>;store byte count
	PUSHJ	P,ADVBFF##	;advance buffers
	 JRST	BUFSTP		;buffered IO stop
	TRNE	S,760000	;TEST FOR KMC ERRORS OR EOF
	 JRST	BUFSTP		;   END TRANSMISSION IF FOUND
	PJRST	KMSETI		;set up next buffer

BUFSTP:	TRNE	S,760000	;test for kmc errors or eof
	 TLO	S,IOEND		;set for MONITOR
	PJRST	CLRACT##
	SUBTTL	KMCINP -- ROUTINE TO QUEUE TRANSACTIONS FOR THE KMC-11

;Called with:
;	W 	    := KMC pointer
;	T1	    := XWD 0,SEL2
;	T2	    := XWD SEL4,SEL6
;Returns
;	CPOPJ	KMC-11 not running
;	CPOPJ1	Transaction described by T1 & T2 has been queued.
;		RQI has been set to request an interrupt on vector "A".
;		KMCIVA will then process the top transaction on the queue.
;


KMCQUE:	
	LDB	T3,PUNIT##	;get line number
	TRZ	T1,KM2LIN!KMCRDO!KMCRDI	;clear forbidden bits
	TRO	T1,(T3)		;set line number
	MOVE	T3,KMCIQP(W)	;GET INDEX OF NEXT ENTRY IN THE QUEUE
	LSH	T3,1		;MAKE IT AN OFFSET (ENTRYS ARE 2 WDS)
	ADDI	T3,KMCINQ(W)	;RELOCATE TO THE ADDRESS OF THE QUEUE
	MOVEM	T1,0(T3)	;STORE SEL2
	MOVEM	T2,1(T3)	;STORE XWD SEL4,SEL6
	AOS	T3,KMCIQP(W)	;ADVANCE THE "PUTTER"'S INDEX
	CAIL	T3,KMCQLN	;IF WE NEED TO WRAP AROUND, THEN
	SETZB	T3,KMCIQP(W)	;  THEN WRAP TO THE FIRST ENTRY
	CAMN	T3,KMCIQT(W)	;IS THE QUEUE FULL (PUTTER = TAKER)
	PJSP	T1,KMCERR	;  IF SO, KMC MUST BE DEAD.  CRASH IT.

	MOVEI	T3,KMCRQI	;GET RQI AND SET IT IN BSEL0
	BSIO	T3,@KMCCSR(W)	;  THIS WILL CAUSE A VECTOR "A" INTERRUPT
	POPJ	P,		;RETURN
	XLIST	; **** LITERALS ****
	LIT
	LIST

KMCEND:	END