Google
 

Trailing-Edge - PDP-10 Archives - cust_sup_cusp_bb-x130c-sb - 10,7/unsmon/rx2ser.mac
There are 7 other files named rx2ser.mac in the archive. Click here to see a list.
	Title	RX2SER -- RX20 Floppy Diskette Service for KS10s V007
	Subttl	Timothe Litt 17-FEB-87

	Search	F,S

	$RELOC
	$HIGH

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED
;  OR COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1982,1983,1986,1988.
;ALL RIGHTS RESERVED.

.CPYRT<1982,1988>


	VRX2SR==:005		;For GLOB

	ENTRY	RX2SER
RX2SER::

	;Device dependent bits in S

	S.SID1==Z(1B6)		;Set if side 1 selected, clear if side 0
	S.OPRA==Z(1B7)		;Set if OPR action requested at interrupt level

	S.2SID==1B29		;Media is 2 sided
	S.2DEN==1B28		;Double density mode
	S.PHYS==1B27		;Use physical I/O (Inhibit interleaving)
	S.DELD==1B26		;Write deleted data/deleted data detected
	S.WPS==1B24		;Use WPS-8 interleave mode

	;The RX02 has several funny attributes:
	; - It supports 2 densitys, each with a different sector size
	; - The sector size is non-standard
	; - It is addressed in 2 ways -- Interleaved and Physical
	; - The hardware numbers sectors starting with 1, not 0
	; - Data is buffered in a silo (FIFO), then NPRed to memory
	;
	;In physical I/O mode, the RX02 has 77 tracks/side, 26 sectors/track.
	;In interleaved-11 mode, the RX02 has 76 tracks/side, 26 sectors/track.
	;In interleaved-8 mode, the RX02 has 74 tracks/side, 26 sectors/track.
	;
	;In single density mode, a sector is 128 (PDP-11) bytes
	;In double density mode, a sector is 256 (PDP-11) bytes
	;
	;Interleaved mode is a software invention, not a hardware function.
	;Because the RX02 is so slow, each track is divided into even and
	;odd sectors.  LBNs are numbered so that for each track, the odd
	;sectors are used first, then the even sectors on consecutive
	;revolutions.  The skipped sectors allow time for the CPU to empty
	;the silo, and request the next transfer to be the next higher LBN
	;without wasting a whole revolution.  In addition, LBNs are skewed
	;from 1 track to the next.  This skew is to allow for the time needed
	;to seek 1 track, without losing a whole revolution.  The track-track
	;skew is 6 sectors(11 mode), 0 sectors (8 mode).  
	;Track 0 is reserved to ANSI standardization.
	;
	;These parameters should not be modified to ensure 11 compatibility.
	SUBTTL	Table of contents

;		TABLE OF CONTENTS FOR RX2SER -- RX20 Service for KS10s
;
;
;			   SECTION			      PAGE
;    1. Table of contents.....................................   2
;    2. RX211 Hardware bit definitions........................   3
;    3. RX20 Device dispatch table............................   4
;    4. DDB Field definitions.................................   5
;    5. UUOCON interface
;         5.1   Device initialization.........................   6
;         5.2   Obtain buffer size............................   7
;         5.3   Buffered input................................   8
;         5.4   Buffered output...............................   9
;         5.5   USETI/USETO & RELEAS UUOs.....................  10
;         5.6   DEVOP. UUO....................................  11
;    6. Interrupt service.....................................  14
;    7. Interrupt service
;         7.1   Read complete.................................  15
;         7.2   Silo empty....................................  16
;         7.3   Silo full.....................................  17
;         7.4   Write complete................................  18
;         7.5   Read extended status complete.................  19
;         7.6   Set density complete..........................  20
;         7.7   Error processing..............................  21
;    8. Queued I/O processing
;         8.1   Enqueue DDB for Kontroller....................  23
;         8.2   Advance queue and start I/O...................  24
;    9. Utility routines
;         9.1   Fill and empty buffer.........................  25
;         9.2   Wait for TR...................................  26
;         9.3   Setup for INPUT and OUTPUT....................  27
;         9.4   Fixup IOWD for IO mode........................  28
;         9.5   UBA mapping...................................  29
;         9.6   Set up W and U................................  30
;         9.7   Sector addressing.............................  31
	SUBTTL	RX211 Hardware bit definitions

	;CSR bits
	RX2CS==0		;Offset of CSR
	ERR==100000		;Error
	INIT==40000		;Initialize ctl
	EXAD==30000		;Extended bus address
	 EADLSH==-4		;;Amount to shift address to get EXAD bits
	RX02==04000		;RX02 (rather than RX01) controller
	SIDE2==1000		;Use second side of floppy
	DEN==000400		;Set for double density
	TR==0000200		;Transfer -- RXDB needs data
	INTENB==100		;Interrupt enable
	DONE==00040		;Done
	UNIT==00020		;Use unit 1, not 0
	 FCNFIL==1		;;Fill buffer (all FCNs include GO)
	 FCNMT==03		;;MT buffer
	 FCNWR==05		;;Write sector
	 FCNRD==07		;;Read sector
	 FCNSD==11		;;Set media density (format)
	 FCNES==13		;;Read Error Status
	 FCNWD==15		;;Write sector with deleted data mark
	 FCNXS==17		;;Read extended status

	;RX2ES bits
	RX2ES==2		;Offset of Error and Status A
	NXM==04000		;NXM during NPR
	WCOV==2000		;Word count overflow
	USEL==400		;Unit 1 was selected
	RDY==0200		;Drive ready
	DEL==0100		;Deleted data was read
	UDEN==040		;Unit double density selected
	DENERR==020		;Density error
	ACLOW==10		;Power failing
	INTDON==4		;Init done
	%2SIDE==2		;2 sided media
	CRCERR==1		;CRC error

	RX2SA==2		;Sector Address register offset
	 ;;Values 1-32(8)
	RX2TA==2		;Track Address register offset
	 ;;Values 0-114(8)
	RX2WC==2		;Word Count register offset
	 ;;Values 0-100(8) Single density
	 ;;Values 0-200(8) Double density
	RX2BA==2		;Bus Address register offset
	SUBTTL	RX20 Device dispatch table

	JRST	RX2ONL		;(-5) See if on-line
	JRST	RX2DVP		;(-4) DEVOP. UUO
	JRST	RX2SIZ		;(-3) Get buffer size
	JRST	RX2INI		;(-2) Once only
	JRST	RX2HNG		;(-1) Hung timer expired
RX2DSP::JRST	RX2REL		;(0) RELEASE - output
	JRST	RX2CLS		;(1) CLOSE
	JRST	RX2OUT		;(2) OUTPUT
	JRST	RX2INP		;(3) INPUT
	JRST	RX2ENT		;(4) ENTER
	JRST	RX2LKP		;(5) LOOKUP
	JRST	RX2DMO		;(6) Dump mode OUTPUT
	JRST	RX2DMI		;(7) Dump mode INPUT
	JRST	RX2USO		;(10) USETO
	JRST	RX2USI		;(11) USETI
	JRST	RX2GTF		;(12) UGETF
	JRST	RX2REN		;(13) RENAME
	JRST	RX2ICL		;(14) CLOSE input
	JRST	RX2CLR		;(15) UTPCLR
	JRST	RX2MTP		;(16) MTAPE

	;Temp defns for null routines
	RX2ONL==CPOPJ##
	RX2HNG==CPOPJ##
	RX2CLS==CPOPJ##
	RX2ENT==CPOPJ1##
	RX2LKP==CPOPJ1##
	RX2DMO==CPOPJ##
	RX2DMI==CPOPJ##
	RX2GTF==CPOPJ##
	RX2REN==CPOPJ##
	RX2ICL==CPOPJ##
	RX2CLR==CPOPJ##
	RX2MTP==CPOPJ##
	SUBTTL	DDB Field definitions

	;Define symbolic offsets for DEVRXQ words
	QLINK==DEVRXQ##+0	;LH - Next DDB in KON queue
				;RH - Unibus address of start of buffer
	QCSR==DEVRXQ##+1	;LH - CSR for transfer; RH - Data buffer # 1
	QFCN==DEVRXQ##+2	;LH - Data buffer # 2; RH - function & retry
	QMAP==DEVRXQ##+3	;LH - UBA Mapping register # 1; RH - #2

	;Byte pointers into the DDB

DEYFCN:	POINT	4,QFCN(F),21	;Function (state) of RX
	 F..IDL==0		;;Idle
	 F..RD== 1		;;Read
	 F..WR== 2		;;Write & write deleted
	 F..FIL==3		;;Fill buffer
	 F..MT== 4		;;Empty buffer
	 F..SMD==5		;;Set media density
	 F..RES==6		;;Read error status
	 F..RXS==7		;;Read extended status
DEYTRY:	POINT	4,QFCN(F),25	;Retry counter
DEYCSC:	POINT	2,QFCN(F),27	;Number of parameters to stuff in data CSR
DEYSCT:	POINT	2,QFCN(F),29	;Number of sectors left in transfer - 1
DEYUUO:	POINT	3,QFCN(F),32	;Type of UUO in progress
	 U..IDL==0		;;Idle (none)
	 U..BR== 1		;;Buffered input
	 U..BW== 2		;;Buffered output
	 U..SD== 3		;;Set density
	 U..DR== 4		;;Dump input
	 U..DW== 5		;;Dump output
DEYUNT:	POINT	1,DEVRXS##(W),18 ;Active unit number
	SUBTTL	UUOCON interface -- Device initialization

RX2INI:	LDB	T2,PUNIT##	;Get unit number
	JUMPN	T2,CPOPJ1##	;If not Kontroller, forget it
	HLRZ	T1,DEVRXC##(F)	;Get UNIBUS adapter number
	MOVEI	T2,2		;Number of mapping registers needed
	PUSHJ	P,AUTAMR##	;Allocate mapping registers
	  JRST	CPOPJ1##	;Sorry
	MOVEM	T1,DEVRXM##(F)	;Save initial mapping registers
	MOVEM	T3,DEVRXE##(F)	;Save initial eleven address
	MOVE	T1,DEVRXC##(F)	;Get EXP address of control
	PUSHJ	P,UBGOOD##	;Alive?
	  JRST	CPOPJ1##	;No, forget it
	MOVE	T1,DEVRXC##(F)	;Get CSR address
	RDIO	T1,RX2CS(T1)	;Read CSR
	TRNN	T1,RX02		;Is it an RX02?
	 JRST	CPOPJ1##	;No, don't set up a vector
				;If device is used, CPU will halt with Illegal
				;interrupt instruction.
	HLRZ	T1,DEVRXC##(F)	;Get UNIBUS adapter number
	HRRZ	T2,DEVRXV##(F)	;Get vector address
	PUSHJ	P,AUTVIA##	;Compute interrupt instruction address
	MOVE	T2,DEVRXJ##(F)	;Get interrupt instruction
	MOVEM	T2,(T1)		;Store in vector table
	JRST	CPOPJ1##	;Return to SYSINI
	SUBTTL	UUOCON interface -- Obtain buffer size

	;Since UUOCON only calls us on OPEN, and remembers the result,
	;a simple user error probably will be to expect WPS mode buffer
	;size, but do OPEN with IO.WPS clear.

RX2SIZ:	MOVEI	T1,<^D128/4>	;Start with size of single density sector
	TRNE	M,S.2DEN	;If double density mode
	 ASH	T1,1		; sector is twice as big
	TRNE	M,S.WPS		;If in WPS mode
	 JRST	[IMULI	T1,3	; we use three sectors of whatever size
		 AOJA	T1,CPOPJ##] ;Account for buffer size word
	TRNN	M,S.PHYS	;If logical IO (and not WPS)
	 MOVEI	T1,200		; Must use full size buffer
	AOJA	T1,CPOPJ##	;Done
	SUBTTL	UUOCON interface -- Buffered input

RX2INP:	TLZ	S,IO		;Of course, I think UUOCON should do this...
	PUSHJ	P,STUWFZ	;Set up U & W and clear retry counter
	MOVSI	T1,DVOFLN	;Off-line bit
	TLZN	S,S.OPRA	;Opr action requested at interrupt level?
	 TDNE	T1,DEVCHR(F)	; or off-line?
	JRST	TELUSR		;Yes, go tell user
RDNXBF:	MOVEI	T1,@DEVIAD(F)	;Get address of buffer
	PUSHJ	P,IOWFIX	;Turn buffer address into real IOWD
	PUSHJ	P,UBAMAP	;Compute UBA mapping
	MOVEI	T1,U..BR	;Doing a buffered read
	DPB	T1,DEYUUO	;Store for error recovery
RDNXTB:	HRRZ	T1,DEVRXI##(F)	;Get next logical block for input
	PUSHJ	P,TRSET		;Set up for transfer
RDNXTS:	HRLM	T1,DEVRXI##(F)	;Save sector number for later
	MOVEI	T2,F..RD	;Get read data function
	MOVEI	T4,INTENB!FCNRD	;Set up read sector function
WRSET:	DPB	T2,DEYFCN	;Store as current
	PUSHJ	P,TRKSEC	;Get track and sector
	 JRST	[TRO	S,IOBKTL;Set block too large error
		 JRST	ERRXI1]	;and exit
	HRLM	T1,QFCN(F)	;Save track
	HRL	T2,T4		;Get function bits
	LDB	T1,PUNIT##	;Get unit number
	SKIPE	T1		;If not zero
	 TLO	T2,UNIT		; set unit 1
	TRNE	S,S.2DEN	;Double density?
	 TLO	T2,DEN		;Yes
	TLZE	S,S.SID1	;Use head 1 ?
	 TLO	T2,SIDE2	;Yes, tell RX02
	MOVEM	T2,QCSR(F)	;Set up RX2CS and RX2TA
	MOVEI	T1,2		;Parameters to load
	DPB	T1,DEYCSC	;Save for ENQDDB
	TRO	S,IOACT		;Set IOACT (so we won't move)
	MOVEM	S,DEVIOS(F)	; ...
	PJRST	ENQDDB		;Queue DDB and return

TELUSR:	MOVEM	S,DEVIOS(F)	;Update DDB
	PUSHJ	P,HNGSTP##	;Bitch, bitch, bitch
	TLNN	S,IO		;Doing input?
	 JRST	RX2INP		;Yes, try again
	JRST	RX2OUT		;No, try output
	SUBTTL	UUOCON interface -- Buffered output

RX2OUT:	TLO	S,IO		;Do UUOCON's work
	PUSHJ	P,STUWFZ	;Set up U & W and clear retry counter
	MOVSI	T1,DVOFLN	;Off-line bit
	TLZN	S,S.OPRA	;Opr action requested at interrupt level?
	 TDNE	T1,DEVCHR(F)	; or off-line?
	JRST	TELUSR		;Yes, go tell user
WRNXBF:	MOVEI	T1,@DEVOAD(F)	;Get address of buffer
	PUSHJ	P,IOWFIX	;Turn buffer address into real IOWD
	PUSHJ	P,UBAMAP	;Compute UBA mapping
	MOVEI	T1,U..BW	;Doing a buffered write
	DPB	T1,DEYUUO	;Store for error recovery
	TRO	S,IOACT		;Set IOACT here
	MOVEM	S,DEVIOS(F)	;So mapping sticks
WRNXTB:	HRRZ	T1,DEVRXO##(F)	;Get LBN for transfer
	PUSHJ	P,TRSET		;Set up for transfer
WRNXTS:	HRLM	T1,DEVRXO##(F)	;Save sector number
	HRRZ	T1,QLINK(F)	;Get 11 address
	LSH	T1,EADLSH	;Shift for high bits
	ANDI	T1,EXAD		;Only those
	IORI	T1,INTENB!FCNFIL ;Add in the fill buffer
	MOVEI	T2,F..FIL	;Get fill function
	PJRST	SETFMT		;Go fill the silo
	SUBTTL	UUOCON interface -- USETI/USETO & RELEAS UUOs

RX2USI:	MOVEI	U,DEVRXI##(F)	;Point to input block number
	TDZA	S,[XWD IOEND,IODEND] ;Zero the EOF bits
RX2USO:	 MOVEI	U,DEVRXO##(F)	;Point to output block number
	;PUSHJ	P,WAIT1##	;Wait for I/O to stop(UUOCON does this)
	HRRM	M,0(U)		;Store arg as next block to read/write
	PJRST	STOIOS##	;Store S in case USETI

RX2REL:	;PUSHJ	P,WAIT1##	;Wait for I/O to stop(UUOCON does this)
	TLZ	S,S.SID1!S.OPRA	;Clear our working bits
	SETZM	DEVRXI##(F)	;Reset to block 0
	SETZM	DEVRXO##(F)	;...
	SETZM	DEVFIL(F)	;Clear any LOOKUP/ENTER info
	HRRZS	DEVEXT(F)
	SETZM	DEVPPN(F)
	HLLZS	QFCN(F)		;Clear unit status
	PJRST	STOIOS##	;Store S and return
	SUBTTL	UUOCON interface -- DEVOP. UUO

	;Here with F:= DDB, T1:= Function code

RX2DVP:	MOVSI	T2,-RX2DVL	;Set up AOBJN pointer
RX2DV1:	HLRZ	T3,RX2DVT(T2)	;Get function code
	HRRZ	T4,RX2DVT(T2)	;and dispatch address
	CAMN	T1,T3		;Do they match?
	 PJRST	(T4)		;Yes, do the function
	AOBJN	T2,RX2DV1	;No, check rest of table
	PJRST	ECOD2##		;Illegal function for RX20

	;DEVOP. dispatch table -- XWD Fcn,,Routine

RX2DVT:	XWD	1,DVPRXS	;Read extended status
	XWD	11,DVPFMT	;Format a disk (using load LP20 RAM fcn!)
	RX2DVL==.-RX2DVT	;Size of table
	;DEVOP. Read extended status function
	;MOVE	ac,[Len,,addr]
	;DEVOP.	ac,
	; error return
	;Normal return
	;
	;Addr:	EXP	.DFRXS (= 1)
	;	SIXBIT	/Device/ (or channel # or UDX)
	;	BLOCK	2		;8 bytes of status returned here
	;
	;The usual DEVOP. errors apply

DVPRXS:	PUSHJ	P,STUWFZ	;Set up U & W and clear retry counter
	TRNE	S,IOACT		;Is IO ACTive?
	 JRST	ECOD1##		;Yes, silly user
	TLZ	S,IO		;This is "input"
	TRZ	S,IODERR!IODTER	;And errors haven't happened yet
	HRRZ	T1,M		;Get address -1 of buffer
	HRLI	T1,-^D<8/4>	;For 8 bytes of status
	PUSHJ	P,UBAMAP	;Set up mapping for IO
	HRLZ	T2,QLINK(F)	;Get 11 address of transfer
	LSH	T2,EADLSH	;Position for CSR
	TLZ	T2,^-EXAD	;Clear all but high 2 bits
	TLO	T2,INTENB!FCNXS	;Set the function
	HRR	T2,QLINK(F)	;First param is low 16 bits
	MOVEI	T1,U..SD	;Pretend UUO is set density
	MOVEI	T3,F..RXS	;Interrupt function is read status
	JRST	DVDOI1		;Go do the IO
	;DEVOP. Format function
	;MOVE	ac,[Len,,addr]
	;DEVOP.	ac,
	; error return
	;Normal return
	;
	;Addr:	EXP	.DFFMT (= 11)
	;	SIXBIT	/Device/ (or channel # or UDX)
	;	SIXBIT /SINGLE/ or SIXBIT /DOUBLE/
	;
	;The usual DEVOP. errors apply

DVPFMT:	PUSHJ	P,STUWFZ	;Set up U & W and clear retry counter
	MOVSI	T1,DVOFLN	;Off-line bit
	TLNN	S,S.OPRA	;Opr action requested at interrupt level?
	 TDNE	T1,DEVCHR(F)	; or off-line?
	JRST	ECOD7##		;Yes, return off-line error
	PUSHJ	P,GETWR1##	;Get next user word (type of format)
	 PJRST	RTM1##		;Address check!
	CAME	T1,[SIXBIT .SINGLE.] ;Want single density?
	 CAMN	T1,[SIXBIT .DOUBLE.] ;Or double?
	CAIA			;Good, continue
	 PJRST	ECOD3##		;Value out of range error
	TRNE	S,IOACT		;Is I/O active?
	 JRST	ECOD1##		;Yes, return no privs.  (Very dumb user!)
	TLO	S,IO		;This is output
	TRZ	S,IODERR!IODTER	;No errors so far
	CAME	T1,[SIXBIT .SINGLE.] ;Single density format?
	 TROA	S,S.2DEN	;No, set double
	TRZ	S,S.2DEN	;Yes, set single
	MOVSI	T2,INTENB!FCNSD	;Get function bits
	LDB	T1,PUNIT##	;Get unit number
	SKIPE	T1		;If not zero
	 TLO	T2,UNIT		; set unit 1
	TRNE	S,S.2DEN	;Double density?
	 TLO	T2,DEN		;Yes
	TRO	T2,"I"		;Approve the "I"nitialization
	SETZM	QMAP(F)		;No UBA mapping
	MOVEI	T1,U..SD	;Get "Set density" state
	MOVEI	T3,F..SMD	;Set Media Density function
DVDOI1:	MOVEM	T2,QCSR(F)	;Set up RX2CS and RX2DB
	DPB	T1,DEYUUO	;Store as current UUO
	DPB	T3,DEYFCN	;Save as unit status
	MOVEI	T1,1		;Parameter to load
	DPB	T1,DEYCSC	;Save for ENQDDB
DVDOIO:	TRO	S,IOACT		;Set IOACT so we know when its done
	MOVEM	S,DEVIOS(F)	; ...
	PUSHJ	P,ENQDDB	;Queue DDB
	;Note that in 7.01 field image, the DEVOP. code doesn't ensure
	;that this job has the device INITed.  If you don't, you will hang
	;in IOW at the call to WAIT1 because SETIOD doesn't have PJOBN set up
	PUSHJ	P,WAIT1##	;Wait for the I/O to complete
	HRRZ	T1,S		;Copy the final status (Yes, WAIT1 updates it)
	TRNE	T1,IODERR!IODTER ;Errors detected?
	 PJRST	ECOD6##		;Yes, "Not initialized" error
	PJRST	STOTC1##	;No, return S in AC
	SUBTTL	Interrupt service

RX2INT::MOVE	W,F		;Save KON pointer
	HLRZ	F,DEVRXS##(W)	;Get DDB pointer
	SKIPN	F		;Paranoia
RX2STP:	STOPCD	CPOPJ##,STOP,RX2,;++RX2SER fouled up
	LDB	T3,DEYFCN	;Get function in progress
	JUMPE	T3,RX2STP	;If none, error
	LDB	T1,DEYUNT	;Get Kontroller's unit number
	LDB	T2,PUNIT##	;And that of the DDB
	CAME	T1,T2		;They better match
	 JSP	T1,RX2STP	;++ They don't, don't make things worse
	MOVE	S,DEVIOS(F)	;Set up S
	MOVE	U,DEVRXC##(W)	;Set up CSR
	RDIO	T1,RX2CS(U)	;Read CSR
	RDIO	T2,RX2ES(U)	;and Error register
	HRL	T1,T2		;Combine
	MOVEM	T1,DEVSTS(F)	;Save for analysis
	PJRST	@[JRST	RD.INT	;Dispatch on state
		  JRST	WR.INT
		  JRST	FL.INT
		  JRST	MT.INT
		  JRST	SM.INT
		  JRST	RS.INT
		  JRST	RX.INT]-1(T3)	;As we know it

	;**********Temp until use found for fcns
	RS.INT==RX2STP		;Read status
	SUBTTL	Interrupt service -- Read complete

	;Here on a Read Complete interrupt

RD.INT:	TRNE	T1,ERR		;Error detected?
	 JRST	ERROR		;Yes, see if we should retry
	TLNE	T1,DEL		;Deleted data present?
	 TROA	S,S.DELD	;Yes, remember it
	TRZ	S,S.DELD	;No, remember that
	MOVEM	S,DEVIOS(F)	;Tell user
	HRRZ	T1,QLINK(F)	;Get 11 address
	LSH	T1,EADLSH	;Shift for high bits
	ANDI	T1,EXAD		;Only those
	IORI	T1,INTENB!FCNMT	;Add in Empty buffer
	MOVEI	T2,F..MT	;Get empty function
	PJRST	SETFMT		;Setup for empty buffer
	SUBTTL	Interrupt service -- Silo empty

MT.INT:	TRNE	T1,ERR		;Error detected?
	 JRST	ERROR		;Yes, see if we can recover
	LDB	T2,DEYSCT	;Get sectors to go
	SOJL	T2,RDDONE	;1 fewer
	DPB	T2,DEYSCT	;Update for next time
	HRRZ	T1,QCSR(F)	;Get word count from this transfer
	ASH	T1,1		;Make into a byte count
	ADDM	T1,QLINK(F)	;Bump address for next transfer
	HLRZ	T1,DEVRXI##(F)	;Get last sector transferred
	AOJA	T1,RDNXTS	;Go read the next one

	;Here when read is complete for all sectors of transfer

RDDONE:	PUSHJ	P,FREKON	;Free the kontroller in case someone else waiting
	AOS	DEVRXI##(F)	;Bump USETI counter to next block
	PUSHJ	P,SVEUF##	;Make job addressable
	MOVEI	T2,^D128/4	;Number of 10 words/single density sector
	TRNE	S,S.2DEN	;Double density?
	 ASH	T2,1		;Yes, double the 10 words
	TRNE	S,S.PHYS	;If physical IO
	 JRST	RDDON1		;Use size of a sector
	TRNN	S,S.WPS		;If not WPS mode
	 SKIPA	T2,[EXP	200]	; use standard logical block size
	IMULI	T2,3		;WPS mode, use 3 sectors
RDDON1:	MOVEI	T1,@DEVIAD(F)	;Get address of buffer control block
	EXCTUU	<HRRM	T2,1(T1)> ;Save actual word count in buffer
	PUSHJ	P,ADVBFF##	;Advance buffer ring
	 JRST	ERRXI1		;No more buffers, shut down
	PUSHJ	P,SETIOD##	;Signal buffer is available
	JRST	RDNXBF		;And go read next buffer
	SUBTTL	Interrupt service -- Silo full

FL.INT:	TRNE	T1,ERR		;Error detected?
	 JRST	ERROR		;Yes, see if we can recover
	HLRZ	T1,DEVRXO##(F)	;Get sector for transfer
	MOVEI	T4,INTENB!FCNWR	;Write sector function
	TRNE	S,S.DELD	;Wants deleted data mark?
	 MOVEI	T4,INTENB!FCNWD	;Yes, give correct command
	MOVEI	T2,F..WR	;Get write data function
	JRST	WRSET		;Go start the write
	SUBTTL	Interrupt service -- Write complete

WR.INT:	TRNE	T1,ERR		;Error detected?
	 JRST	ERROR		;Yes, see if we can recover
	LDB	T2,DEYSCT	;Get sectors to go
	SOJL	T2,WRDONE	;1 fewer
	DPB	T2,DEYSCT	;Update for next time
	MOVEI	T1,^D128	;Get single density byte count
	TRNE	S,S.2DEN	;Double density?
	 ASH	T1,1		;Yes, double it
	ADDM	T1,QLINK(F)	;Bump address for next transfer
	HLRZ	T1,DEVRXO##(F)	;Get last sector transfered
	AOJA	T1,WRNXTS	;Go do the next one

	;Here when write is complete for all sectors of transfer

WRDONE:	PUSHJ	P,FREKON	;Free kontroller in case of others
	AOS	DEVRXO##(F)	;Bump USETO counter to next LBN
	PUSHJ	P,SVEUF##	;Make the job addressable
	PUSHJ	P,ADVBFE##	;Advance the buffer ring
	 JRST	ERRXI1		;No more data, shut down
	PUSHJ	P,SETIOD##	;Signal a buffer can be refilled
	JRST	WRNXBF		;And go write the next one
	SUBTTL	Interrupt service -- Read extended status complete

	;Here when extended status interrupt happens

RX.INT:	;PJRST	SM.INT		;Treat exactly as set density
	SUBTTL	Interrupt service -- Set density complete

	;Here when Format interrupt happens

SM.INT:	TRNN	T1,ERR		;Any errors?
	 PJRST	ERRXI1		;No, restart UUO level code
	PUSHJ	P,ERRCHK	;Yes, see if retrys left
	 JRST	ERROR2		;No, record the error
	PJRST	STRTIO		;OK to retry, just restart I/O
	SUBTTL	Interrupt service -- Error processing

	;Here when the RX has detected an error
	;Note that we may be either at interrupt or UUO level

ERROR:	MOVE	T1,DEVSTS(F)	;Get error status back
	PUSHJ	P,ERRCHK	;See if retrys left
	 JRST	ERROR1		;None left, declare hard error
	LDB	T4,DEYUUO	;Get function in progress
	TLNN	T1,CRCERR	;CRC error?
	 TLNN	T1,WCOV!DENERR	; No, not WC overflow or density error?
	JRST	RETRY		;Yes, just retry function
	TLNE	T1,UDEN		;Is diskette double density?
	 TROA	S,S.2DEN	;Yes, retry that way (and tell user)
	TRZ	S,S.2DEN	;No, retry in single density
	TLNE	T1,%2SIDE	;Two sided diskette?
	 TROA	S,S.2SID	;Yes, tell user
	TRZ	S,S.2SID	;No, tell user
	PUSHJ	P,SVEUF##	;Must make job addressable
				;since buffer size changed
	TLNE	S,IO		;If output
	 JRST	WRNXBF		;Restart
	JRST	RDNXBF		;Recompute transfer size & do it all over again
ERROR1:	TLNE	T1,RDY		;Drive ready?
	 TLNE	T1,ACLOW	;Yes, is subsystem ready?
	JRST	SETOPR		;No, user ? OPR action requested
ERROR2:	TLNE	T1,CRCERR	;CRC error?
	 TROA	S,IODTER	;Yes, set data error
ERRXIT:	TRO	S,IODERR	;No, set device error
ERRXI1:	PUSHJ	P,SETIOD##	;Restart user
	PUSHJ	P,CLRACT##	;and clear IOACTive
	PJRST	FREKON		;Check Kontroller queue and dismiss

SETOPR:	MOVSI	T1,DVOFLN	;Set offline status
	IORM	T1,DEVCHR(F)	;for UUOCON
	TRO	S,IODERR	;Set device error
	TLO	S,S.OPRA	;and request OPR action
	PUSHJ	P,DEVERR##	; ... later
	PJRST	FREKON		;Release the Kontroller and dismiss
	;Routine to increment the retry counter
	;skips if any left, otherwise returns normally

ERRCHK:	MOVEI	T2,DEPDER	;Get retry disabled bit
	TDNE	T2,DEVSTA(F)	;Did user specify no retrys?
	 POPJ	P,		;Yes, don't
	LDB	T2,DEYTRY	;Get retry counter
	SKIPN	T2		;First retry?
	 AOS	DEVHCW(F)	;Yes, count a soft error
	AOS	T2		;bump by one
	CAILE	T2,^D10		;Done enough?
	 SETZ	T2,		;yes, stop
	DPB	T2,DEYTRY	;Store new count
	JUMPN	T2,CPOPJ1##	;If still trying, skip
	SOS	DEVHCW(F)	;Hard error, count down soft errors
	MOVSI	T2,1		;Now, ...
	ADDM	T2,DEVHCW(F)	;count up the hard ones
	POPJ	P,		;Return hard error

	;Routine to initiate a retry for error recovery

RETRY:	LDB	T1,DEYUUO	;Get the UUO in progress
	JRST	@[JRST	RX2STP	;;Nothing
		  JRST	RT.BR	;;Buffered read
		  JRST	RT.BW	;;Buffered write
		  JRST	RX2STP	;;Set density
		  JRST	RT.DR	;;Dump read
		  JRST	RT.DW](T1) ;Dump write

RT.BR:	HLRZ	T1,DEVRXI##(F)	;Get sector to read
	JRST	RDNXTS		;Re-read it

RT.BW:	HLRZ	T1,DEVRXO##(F)	;Get sector to write (Must refill silo since
	JRST	WRNXTS		;power fail in RX could invalidate it)

RT.DR:		;Should delete these 3 lines if not supporting dump io
RT.DW:
	STOPCD	CPOPJ,DEBUG,RXX	;Unimplimented error recovery
	SUBTTL	Queued I/O processing -- Enqueue DDB for Kontroller


;Routine to enqueue the current DDB for the Kontroller
;Assumes that we are ready to start IO

ENQDDB:	PIOFF			;Prevent races
	PUSHJ	P,ENQDDI	;Now act as though at interrupt level
	 JRST	ENQDD2		;Can't start IO now, some one else is there
	PION			;Restore PI
	PJRST	STRTIO		;and start IO

ENQDD2:	PION			;Restore PI
	POPJ	P,		;and return to caller

ENQDDI:	HLRZ	T1,DEVRXS##(W)	;Get current queue header
	CAIN	T1,0(F)		;Us?
	 PJRST	CPOPJ1##	;Yes, don't foul the queue
	HRRZS	QLINK(F)	;Clear previous link
	JUMPE	T1,[HRLM  F,DEVRXS##(W)	;We're first
		    PJRST CPOPJ1##]  ;So start IO now
ENQDD1:	HLRZ	T2,QLINK(T1)	;Point to next DDB in queue
	JUMPE	T2,[HRLM  F,QLINK(T1) ;Reached end, link this in
		    POPJ  P,]	;I/O is now deferred
	MOVE	T1,T2		;Point to next DDB
	JRST	ENQDD1		;Loop for end
	SUBTTL	Queued I/O processing -- Advance queue and start I/O

	;Routine to free the Kontroller at end of unit I/O

FREKON:	PUSH	P,F		;Save F for caller
	PUSH	P,S
	PUSHJ	P,QUECHK	;Free up the controller if we had it
	POP	P,S
	PJRST	FPOPJ##		; (any pending I/O for other units started)

QUECHK:	HLRZ	T1,DEVRXS##(W)	;Get queue header
	CAIE	T1,0(F)		;Are we it?
	 POPJ	P,		;No, don't touch it
	HLRZ	F,QLINK(F)	;Point to next DDB in queue
	HRLM	F,DEVRXS##(W)	;It's now the first one
	JUMPN	F,STRTI0	;If someone there, start I/O
	WRIO	F,@DEVRXC##(W)	;Otherwise, disable interrupts
	POPJ	P,		;and return

	;Entry to start IO for current DDB
	;requires current DDB to be first on Q

STRTI0:	MOVE	S,DEVIOS(F)	;Get correct S
STRTIO:	TRO	S,IOACT		;Set IOACT
	PUSHJ	P,STOIOS##	; and start hung timer (SETACT clears IOW)
	LDB	T1,PUNIT##	;Get unit number of this DDB
	DPB	T1,DEYUNT	;Save as number of active unit
	MOVE	T1,QMAP(F)	;Get UBA mapping register contents for this XFR
	MOVE	T2,DEVRXM##(W)	;Get IO address of UBA map
	WRIO	T1,1(T2)	;Setup map for second page
	MOVSS	T1		;Get contents for first page
	WRIO	T1,0(T2)	;Set it up
	HLRZ	T1,QCSR(F)	;Get CSR contents for command
	WRIO	T1,RX2CS(U)	;Give the command
	MOVE	T4,[POINT 18,QCSR(F),17] ;Set up byte pointer to contents
	LDB	T3,DEYCSC	;Get CSR count
SETPAR:	JUMPE	T3,CPOPJ##	;Return if done with parameters
	PUSHJ	P,TRWAIT	;Wait for signal to TRansfer
	 JRST	[PUSHJ	P,ERRCHK ;Any retrys left?
		  JRST	ERRXIT	;No, give up
		 JRST	STRTIO]	;Yes, try again
	ILDB	T1,T4		;Get parameter
	WRIO	T1,RX2BA(U)	;Write it to RX02
	SOJA	T3,SETPAR	;Loop till done
	SUBTTL	Utility routines -- Fill and empty buffer

	;Routine to do setup for Fill and Empty buffer functions
	;Fills in CSR with DENSITY, UNIT bits
	;Sets up command queue in DDB
	;Starts IO
	;expects EXAD bits and INTENB!xxFCN in T1
	;expects interrupt dispatch code in T2

SETFMT:	TRNE	S,S.2DEN	;Double density?
	 TRO	T1,DEN		;Yes, tell RX02
	DPB	T2,DEYFCN	;Set interrupt dispatch function
	LDB	T2,PUNIT##	;Get unit number
	SKIPE	T2		;If not unit zero
	 TRO	T1,UNIT		; set unit 1
	HRLZM	T1,QCSR(F)	;Save command in queue
	MOVEI	T1,^D128/2	;Word count for single density
	TRNE	S,S.2DEN	;Double density?
	 ASH	T1,1		;Yes, double it
	HRRM	T1,QCSR(F)	;Save WC in queue
	HRRZ	T1,QLINK(F)	;Get low order 11 address
	HRLM	T1,QFCN(F)	;Store it in queue too
	MOVEI	T1,2		;Get number of parameters
	DPB	T1,DEYCSC	;Set that in queue
	JRST	ENQDDB		;Enqueue (for fill) and start the IO
	SUBTTL	Utility routines -- Wait for TR

;Routine to wait for TR or error
;Call with W:=KON DDB; U:=CSR address
;CPOPJ if error, CPOPJ1 if TR

TRWAIT:	MOVEI	T2,2000		;Set timeout, just in case
TRWAT1:	RDIO	T1,RX2CS(U)	;Read CSR
	TRNE	T1,TR		;TR set yet?
	 JRST	CPOPJ1##	;Yes, return success
	TRNN	T1,ERR!DONE	;Error?
	 SOJG	T2,TRWAT1	;No, keep looking
	RDIO	T2,RX2ES(U)	;Get error status
	HRL	T1,T2		;Combine
	MOVEM	T1,DEVSTS(F)	;Save for analysis
	POPJ	P,		;Error or timeout
	SUBTTL	Utility routines -- Setup for INPUT and OUTPUT

TRSET:	MOVEI	T2,3		;Assume 4 sectors/block
	TRNE	S,S.2DEN	;Unless double density
	 LSH	T2,-1		;In which case, 2 sectors/block
	TRNE	S,S.WPS		;WPS uses three sectors/block
	 MOVEI	T2,2		;Thus
	TRNE	S,S.PHYS	;But physical I/O
	 MOVEI	T2,0		;Provides only 1 sector/block
	DPB	T2,DEYSCT	;Save number of sectors - 1 in transfer
	PJRST	BLKCVT		;Convert LBN to sector
	SUBTTL	Utility routines -- Fixup IOWD for IO mode

	;Routine to turn a buffer address into an IOWD, allowing for IO mode

IOWFIX:	HRLI	T1,-<^D128/4>	;Set word count for a single density sector
	TRNE	S,S.2DEN	;Unless double density
	 HRLI	T1,-<^D256/4>	; in which case use that (10) word count
	TRNE	S,S.PHYS	;If physical IO, we got it right
	 AOJA	T1,CPOPJ##	;So make real IOWD and return
	HRLI	T1,-200		;Logical IO, assume normal size
	TRNN	S,S.WPS		;WPS mode?
	 AOJA	T1,CPOPJ##	;No, we got it right
	HRLI	T1,-<^D128/4>*3	;Yes, assume single density 3 sectors
	TRNE	S,S.2DEN	;Right?
	 HRLI	T1,-<^D256/4>*3	;No, double density WPS (!)
	AOJA	T1,CPOPJ##	;Done
	SUBTTL	Utility routines -- UBA mapping

	;Routine to compute UBA mapping for an IOWD

UBAMAP:	HLRE	T2,T1		;Get word count
	MOVNS	T2		;Make positive
	HRRZI	T1,1(T1)	;Point to first address of buffer
	PUSHJ	P,UADRCK##	;See if it's legal for IO (May call PFH)
	EXCTUX	<MAP T3,@T1>	;Convert to a physical address
	 TLNN	T3,(1B1)	;Error if hard page fail
	TLNN	T3,(1B3!1B4)	;OK if modified or writable
	 JRST	ADRERR##	;Not accessible or not writable or hard PF
	TLZ	T3,777776	;Clear junk bits
	ADDI	T1,-1(T2)	;Point T1 to last word in buffer
	ROT	T3,-^D9		;Get page number of first page
	TRO	T3,UNBD18!UNBVBT;Set 16 bit mode, valid
	HRLZM	T3,QMAP(F)	;This will be the mapping
	ROT	T3,^D9		;Get word number back
	LSH	T3,2		;Make into byte address
	ANDI	T3,777B33	;Byte offset into page only
	ADD	T3,DEVRXE##(W)	;Compute start of transfer
	HRRM	T3,QLINK(F)	;Save for STRTIO
	PUSHJ	P,UADRCK##	;Be sure end of buffer is in low seg
	EXCTUX	<MAP T1,@T1>	;Find address of end of buffer
	 TLNN	T1,(1B1)	;Error if hard page fail
	TLNN	T1,(1B3!1B4)	;OK if modified or writable
	 JRST	ADRERR##	;Not accessible or not writable or hard PF
	TLZ	T1,777776	;Clear junk
	LSH	T1,-^D9		;Get page number
	TRO	T1,UNBD18!UNBVBT;Set 16 bit mode, valid
	HRRM	T1,QMAP(F)	;This is the other mapping
	POPJ	P,		;Done
	SUBTTL	Utility routines -- Set up W and U

	;Coroutine to setup W and U from F

STUWFZ:	SETZ	T1,		;Get a zero
	DPB	T1,DEYTRY	;Clear the retry counter
SETUWF:	EXCH	U,(P)		;Save U, get PC
	PUSH	P,W		;Save W
	MOVEM	U,1(P)		;Save PC of caller
	MOVEI	W,RXADDB##	;Point to first RX02 DDB
SETUW1:	MOVE	T1,DEVNAM(F)	;Get name of current DDB
	XOR	T1,DEVNAM(W)	;Compare
	TLNE	T1,777700	;At least RX?
	 JSP	T1,RX2STP	;No, die
	TLNE	T1,77		;Is controller right?
	 JRST	[HLRZ	W,DEVSER(W) ;No, go to next
		 JRST	SETUW1]	;and loop till find it
	MOVE	U,DEVRXC##(W)	;Point to CSR
	PUSHJ	P,@1(P)		;Call caller (again)
	 JRST	.+2		;Pass non-skip along
	AOS	-2(P)		;Skip
	POP	P,W		;Restore W
	PJRST	UPOPJ##		;and U
	SUBTTL	Utility routines -- Sector addressing

;Subroutine to convert LBNs to logical sector format
;
;Call:	T1/	LBN from user
;	PUSHJ	P,BLKCVT	(To convert LBN)
;	Only return		(T1/logical sector to start I/O with)

BLKCVT:	TRNE	S,S.PHYS	;Physical I/O?
	 POPJ	P,		;Yes, already a sector number
	TRNE	S,S.WPS		;WPS mode?
	 JRST	[IMULI	T1,3	;Yes, any sector = 1/3 block
		 POPJ	P,]	;Done
	ASH	T1,1		;Logical I/O - Double density sector = 1/2 block
	TRNN	S,S.2DEN	;Double density?
	 ASH	T1,1		;No, single density sector is 1/4 block
	POPJ	P,		;Return first logical sector of block
;Subroutine to convert logical sectors to track/sector format
;
;Call:	T1/	logical sector number (Sector # block)
;	PUSHJ	P,TRKSEC	(To convert sector)
;	 Illegal sector number
;	Sector number ok
;
;On success, S has the head select bit set, T1/ Track
;T2/ Sector
;
;Uses T1-3

TRKSEC:	IDIVI	T1,^D26		;T1/ Track, T2/ Sector
	TRNE	S,S.WPS		;If WPS mode
	 JRST	TRKSCW		;This is all different
	CAIGE	T1,^D76		;On side 2?
	 JRST	TRKSC2		;No, must be ok
	TRNN	S,S.PHYS	;Physical I/O?
	 JRST	[SUBI	T1,^D76	;No, logical uses 76 tracks/side
		 JRST	TRKSC1]	;See if legal
	CAIN	T1,^D76		;Exactly 76?
	 JRST	TRKSC2		;Yes, physical uses 77 tracks/side
	SUBI	T1,^D77		;No, convert to side 2 address
TRKSC1:	TRNN	S,S.2SID	;2 Sided media?
	 POPJ	P,		;No, illegal sector number
	TLOA	S,S.SID1	;Yes, use head 1
TRKSC2:	 TLZ	S,S.SID1	;Here when address is on side 0
	TRNE	S,S.PHYS	;Physical?
	 JRST	TRKSC3		;Yes, skip interleave
	ASH	T2,1		;Sector _ Sector * 2
	CAIL	T2,^D<13*2>	;In second 1/2 track?
	 AOJ	T2,		;Yes, use odd sector
	MOVE	T3,T1		;Copy track number
	IMULI	T3,6		;Compute track-track skew
	ADD	T2,T3		;Offset
	IDIVI	T2,^D26		;Modulo 26
	MOVE	T2,T3		;Skewed and interleaved sector in T2
	AOJ	T1,		;Logical tracks start with 1 (but not phys...)
TRKSC3:	AOJ	T2,		;Sectors are always based on 1
	CAIG	T1,^D77		;Track too big?
	 CAILE	T2,^D26		;Or sector too big?
	POPJ	P,		;Yes, error
	JRST	CPOPJ1##	;No, all set
	;Here for WPS mode conversion

TRKSCW:	TRNE	S,S.PHYS	;Physical and WPS
	 POPJ	P,		;Is illegal
	CAIG	T1,^D74-1	;On side 2?
	 JRST	TRKSW1		;No
	SUBI	T1,^D74		;Yes, adjust for side 1
	CAIG	T1,^D74-1	;Still too big?
	 TRNN	S,S.2SID	;No, 2 sided media?
	POPJ	P,		;Illegal track number
	TLOA	S,S.SID1	;Legal, select head 1
TRKSW1:	 TLZ	S,S.SID1	;Select head 0
	IMULI	T2,3		;Interleave by 3 sectors
	IDIVI	T2,^D26		;MOD 26
	AOS	T2,T3		;Sectors start at one
	AOJA	T1,CPOPJ1##	;Fix track and skip

	END