;<MON>DRMDDC.MAC	7/2/75		EDIT BY HEATHMAN
;ADDED RECOVERABLE ERROR LOGGING CODE
;<MON>DRMDDC.MAC;27    20-JUN-75 11:18:23    EDIT BY CROSSLAND
;ADDED DRUM MIGRATION CODE FROM IMSS
;<MON>DRMDDC.MAC		14-FEB-75		EDIT BY HEATHMAN
;CHANGES TO INITIALIZE BAND LOCK OUT CODE

;12 JUL 1974 / RAINER SCHULZ

;DRUM DRIVER - DEC RES-10 CONTROLLER,DDC DISK
SEARCH PROLOG
TITLE DRM

	INTERN DRMIO,DRMINI,DRMRST,DRMSV,.DRMOP
	INTERN CLKDRS,DRMBBT,DRMBST

	EXTERN FORKX,HLOCKR,STOPLK,DRPI,SWPPI,NOPILK,HLOCKP
	EXTERN SWPDON,DRUMP,DEVMPE,PRELRQ,DNOGO
	EXTERN DSKIO,PWRDWN,BITS,BUGHLT,BUGCHK
	EXTERN MENTR,CAPENB,WHEELX,MRETN,FPTA,BUGMSG
	EXTERN MLKPG,MULKPG,DISGET,SCHEDP,EDISMS,PSKED
	EXTERN DSKOP2,DSKOP3
	EXTERN DDERLL,DDERST,DDERWD,JB0FLG,DRMEUT,DDERTP,INSKED,DDERBP
	EXTERN DSKNOV
;CST3 LH CONTROL BITS

	INTERN DWRBIT,SWPERR
DWRBIT==400000
SWPERR==200000


	RESCD


;THIS ALLOCATOR HANDLES A DEVICE OF SOME NUMBER (DRMSEC) OF SECTORS,
;AND SOME NUMBER (DRMBND) OF BANDS. IT HAS A RESIDENT BIT TABLE,
;AND IS USED TO ALLOCATE SWAPPING STORAGE, EVEN IF THE PHYSICAL
;SWAPPING DEVICE IS THE SAME AS THE PHYSICAL FILE DEVICE

;THE FOLLOWING ROUTINES ARE IN THIS PACKAGE & CALLED FROM OUTSIDE.

INTERN DRMASD		;ASSIGN SWAPPING DISK ADDRESS
INTERN ASDRMS		;ASSIGN SPECIFIC DRUM ADDRESSES
INTERN DRMASF		;ASSIGN A DRUM PAGE, EMPTIEST SECTOR.
INTERN ASDRM		;ASSIGN DRUM PAGE NEAR A SPECIFIC SECTOR
INTERN DEDRM		;DEASSIGN A DRUM PAGE
INTERN DRMSEC,NDRMEW
INTERN STBTWD,CHKDRR
INTERN MAXDRM

DRUM2=1			;1 SAYS WE HAVE TWO DRUMS
FDRUM==20000		;END OF FIRST DRUM-MUST BE MAXDRM IF ONE DRUM ONLY
DRMSEC==^D13 		;NO. OF DRUM SECTORS
DRMBND==NDST/DRMSEC	;NO OF BANDS
DRMBSZ==<DRMBND+^D35>/^D36	;SIZE OF BIT TABLE CHUNK FOR 1 SECTOR
LS DRMBLK,1		;LOCKOUT WORD FOR DRUM BIT TABLE
LS DRBTWD,NDST/^D36+1	;BIT TABLE FOR RECENTLY REF. DRUM PAGES
GS DRMBIT,DRMSEC*DRMBSZ	;DRUM BIT TABLE

LS DRMLSA,1		;LAST SECTOR ASSIGNED
			;NOW USED TO HOLD OFFSET INTO BITTABLE OF FIRST FREE SECTOR

Z==0
A==1
B==2
C==3
D==4
E==5
F==6
G==7
AA==10
BB==11
CC==12
DD==13
EE==14
FF==15
GG==16

;DRUM ERROR WORDS

NDRMEW==^D18		;NO OF DRUM ERROR WORDS

; DRMCFE THROUGH DRMFRE MUST BE KEPT TOGETHER
LS DRMCFE,1		;COUNT OF ERRORS
LS DRMLER,4		;LAST DRUM ERROR WORDS

LS DRCKER,1		;DRUM CHECKSUM ERROR
LS DCTRER,1		;COMM. SEQ. ERROR
LS DCPAER,1		;PARITY ERROR(MEM)
LS DNXMER,1		;NXM
LS QPAER,1		;QUEUE PARITY ERROR
LS DIPAER,1		;INTERNAL PARITY ERROR(SILO)
LS DOVRER,1		;OVERRUN
LS DTIMER,1		;DRUM TIMEOUT(HUNG DEVICE)
LS QBFER,1		;QUEUE BUFFER FULL
LS EXDER,1		;EXCEEDED DISK CAPACITY
LS DRMRDS,1		;DRUM READS
LS DRMWRS,1		;DRUM WRITES


;FOLLOWING FOUR ENTRIES MAKE UP SWPSTS TABLE
LS DRMFRE,1		;FREE DRUM PAGES
GS DSKFRE,1		;FREE DISK PAGES
LS DRSWCT,1		;REAL DRUM SWAPS
LS DKSWCT,1		;DISK SWAPS INSTEAD OF DRUM

;DRUM CONO BITS

DRMRES==3B19		;RESET
DRMCLT==1B18		;CLEAR INT
DRMCLE==1B19		;CLEAR ERRORS
DRMEXF==1B20		;EXECUTE FR
DRMEXQ==1B21		;EXECUTE QUEUE

;DRUM CONI BITS
DRMQPA==1B0		;QUEUE PARITY ERROR
DRMBUS==1B18		;DRUM BUSY
DRMDON==1B19		;DRUM DONE FLAG
DRMCSE==1B26		;COMM SEQ. ERROR
DRMQF==1B27		;QUEUE FULL ERROR
DRMRDY==1B28		;DRUM READY
DRMSPA==1B29		;DRUM SILO PARITY ERROR
DRMNXM==1B30		;NXM
DRMEXC==1B31		;EXCEEDED CAPACITY
DRMHUD==1B32		;DRUM HUNG
DRMMPE==1B33		;MPE
DRMOVR==1B34		;DATA OVERRUN
DRMCKE==1B35		;CHECKSUM ERROR


;SOME DRUM ALLOCATION AND DEFINITIONS

LS DRMSVR,1		;INTERRUPT RETURN LOCATION
DR0==440		;DRUM CONTR. ADDRESS 0

FDRF==2
IFE DRUM2,<FDRF==1>
LS DRLOOP,1		;FOR LOOPING ON DRUMS
LS DRLSEC,1		;LAST DRUM SECTOR IN QUEUE
LS DRMFLG,1		;DRUM INTERRUPT REQ.
LS DRMSEQ,1		;DRUM SEQUENCE NUMBER
LS CMDST,1		;FIRST NON SERVICED COMMAND IN QUEUE
LS DRMINT,2		;SECTORS TO BE SERVICED

NPWDS==10
LS QPWA,NPWDS		;PROGRAM QUEUE
LS QPWB,NPWDS		;PWB
LS DRMSTS,NPWDS		;DRUM COMMAND STATUS

;BYTE POINTERS

BPWAC:	POINT 3,QPWA(1),2	;COMMAND
BSEQN:	POINT 5,QPWA(1),11	;SEQ. NUMBER
BPIA:	POINT 3,QPWA(1),14	;PIA
BFUNC:	POINT 2,QPWA(1),16	;FUNCTION
BDISC:	POINT 2,QPWA(1),18	;LOGICAL DISC
BTRACK:	POINT 10,QPWA(1),28	;TRACK ADDRESS
BSECT:	POINT 7,QPWA(1),35	;SECTOR ADDRESS

;BYTE POINTER FOR STATUS FROM DRUM
DRSEC:	POINT 7,0,17	;SECTOR COUNTER
DRDPA:	POINT 5,0,10	;DONE SEQ. NO. IN STATUS

;BYTE POINTER FOR DRUM COMMAND STATUS (DRMSTS)
DRERC:	POINT 6,DRMSTS(1),35	;ERROR RETRY COUNT
DRUTS:	POINT 6,DRMSTS(1),26	;UTILITY I/O COMMAND PAIR INDEX

NDRMCM=20		;NUMBER OF POSSILBY QUEUED UTILITY OPS
LS DRMCCT,1		;FREE UTILITY COMMAND PAIR COUNT
LS DRMFCL,1		;START OF FREE UTILITY COMMAND PAIR LIST
LS DRMCL,NDRMCM*2	;UTILITY COMMAND PAIR BUFFERS

LS CMDCNT,1		;COMMANDS IN PROGRESS
NRETRY==50		;50 TRIES ON RECOVERY

LS DRMRI,DRMSEC*FDRF	;READ QUEUE INPUT POINTER
LS DRMRO,DRMSEC*FDRF	;READ QUEUE OUTPUT POINTER
LS DRMWI,DRMSEC*FDRF	;WRITE QUEUE INPUT POINTER
LS DRMWO,DRMSEC*FDRF	;WRITE QUEUE OUTPUT POINTER
LS DRMPI,DRMSEC*FDRF	;PRELOAD READ QUEUE
LS DRMPO,DRMSEC*FDRF
                                                                       LS DRMUI,DRMSEC*FDRF	;UTILITY QUEUE INPUT POINTER
LS DRMUO,DRMSEC*FDRF	;UTILITY QUEUE OUTPUT POINTER


;DRUM STARTUP FROM CLOCK INTERRUPT LEVEL

LS CLKSVA,1	;TEMP. SAVE AC3
LS CLKSVB,1	;TEMP SAVE AC2
LS CLKSVC,1	;TEMP. SAVE AC1

CLKDRS:	SKIPN DRMINT
IFN DRUM2,<SKIPE DRMINT+1
		JRST .+2 >
	JRST 0(1)
	SKIPN CMDCNT		;DRUM BUSY
	CONSZ PI,1B<20+DRMCHN>	;ON DRUM INTERRUPT LEVEL
	JRST 0(1)
	MOVEM 1,CLKSVC	;SAVE AC1
	MOVEM 3,CLKSVA
	MOVEM 2,CLKSVB
	SETZM DRLOOP
	SKIPN DRMINT		;FIRST DRUM READY
	JRST CLKDR3		;NO
	SETZM DRLSEC
	JSP 3,DRMNXT		;GET NEXT SECTOR
	MOVEI 2,1
	LSH 2,0(1)
	TDNE 2,DRMINT		;ANY REQ.
	JRST [SETZM DRLSEC
		JRST CLKDR2 ]
CLKDR3:	SKIPN DRMINT+1		;SEC. DRUM REQ.
	JRST CLKDR1		;NO
	MOVSI 1,400000
	MOVEM 1,DRLSEC
	JSP 3,DRMNXT
	SUBI 1,DRMSEC
	MOVEI 2,1
	LSH 2,0(1)
	TDNN 2,DRMINT+1		;SEC. DRUM READY
	JRST CLKDR1		;NO
	MOVSI 2,400000
	MOVEM 2,DRLSEC
CLKDR2:	AOS DRMFLG		;FLAG INTERR. REQ.
	ISB DRMCHN
CLKDR1:	MOVE 3,CLKSVA
	MOVE 2,CLKSVB
	MOVE 1,CLKSVC
	JRST 0(1)		;RETURN

;DRUM OPERATE JSYS - USER UTILITY DRUM HANDLE

.DRMOP:	JSYS MENTR
	CALL DSKOP2		;DO SAME PAGE SETUP AS IN DSK.MAC
	UMOVE 1,1		;GET DRUM ADDRESS
	CALL UDRMIO		;OPERATE THE DRUM
	JRST DSKOP3		;AND RETURN LIKE DSKOP

;DRUM UTILITY I/O DRIVER
;AC1 - TENEX INTERNAL DRUM ADDRESS
;AC2 - 1B14 = WRITE
;      1B1  = NO RETRY ON ERRORS
;      RH = WORD COUNT
;AC3 - REAL CORE ADDRESS
; IF NOT IN SCHED, RETURNS +1 WITH CONI ERROR BITS.
; IF IN SCHED, RETURNS +1 IF COULD NOT GET COMMAND WORD PAIR
; RETURNS +2 IF COMMAND QUEUED UP WITH COMMAND WORD PAIR POINTER IN 1

UDRMIO:	SOSGE DRMCCT		;COMMAND BUFFER FULL

	 JRST  [AOS DRMCCT	;CORRECT COUNT
		SKIPE INSKED	;IN SCHED?
		 RET		;YES, TAKE ERROR EXIT
		PUSH P,1	;YES, WAIT
		MOVEI 1,UDRM2	;DISMISS UNTIL ROOM IN COMMAND LIST
		JSYS EDISMS
		POP P,1
		JRST UDRMIO]

	NOSKD1
	HLOCKI DRPI		;LOCK QUEUE
	MOVE 4,@DRMFCL		;GET A COMMAND PAIR
	EXCH 4,DRMFCL
	HULOCK DRPI		;UNLOCK QUEUE
	OKPI
	HLL 3,2			;GET BITS WITH CORE ADDRESS
	TLZ 3,577777		;MASK OUT IRRELEVENT BITS
	TLNE 2,(1B14)		
	 TLO 3,(1B16)		;WRITE
	TLNN 2,(1B14)		
	 TLO 3,(2B16)		;NO, MAKE READ FUNCTION
	MOVEM 3,1(4)
	
	MOVEI 2,(2)		;GET JUST SECTOR COUNT
	LSH 2,-7		;DIVIDE BY 200 WORDS/SECTOR
	 JUMPE 2,UDRM3		;SAY 0 SECTORS ILLEGAL
	MOVNS 2
	DPB 2,[POINT 9,1(4),13]	;SET UP SECTOR COUNT

	TRO 1,(1B0)		;1B0 INDICATES COMMAND NOT YET EXECUTED
	HRLZM 1,0(4)		;SET IN DRUM ADDRESS
	TRZ 1,(1B0)

	MOVEI 1,(1)		;MASK OFF IRELEVANT BITS
	IDIVI 1,100		;GET SECTOR IN 2, TRACK IN 1
	LSH 1,-7		;GET DRIVE

	CAIL 1,FDRF		;LEGAL DRIVE?
	 JRST UDRM3		;NO
	CAIL 2,DRMSEC		;LEGAL SECTOR?
	 JRST UDRM3		;NO

	MOVEI 3,DRMSEC		;GET DRUM*SECTORS
	IMUL 3,1
	ADD 3,2			;GET RIGHT DRUM/SECTOR QUEUE
	HLOCKI DRPI
	HRRM 4,@DRMUI(3)	;QUEUE UTILITY REQUEST
	HRRZM 4,DRMUI(3)
	HULOCK DRPI		;UNLOCK QUEUE
	OKPI

	MOVEI 3,1
	LSH 3,(2)
	IORM 3,DRMINT(1)	;SET SECTOR INT REQ FLAG

	OKSKD1
	SKIPE INSKED		;IN SCHED?
	 JRST UDRM5		;YES
	MOVEI 1,DISGET
	HRLI 1,0(4)		;TEST SECOND WORD OF COMMAND PAIR FOR .GE. 0
	JSYS SCHEDP

	NOSKED
	MOVE 1,1(4)		;GET ERROR BITS
UDRM4:	
	HLOCKI DRPI		;LOCK QUEUE
	EXCH 4,DRMFCL		;RETURN PAIR TO FREE LIST
	MOVEM 4,@DRMFCL
	AOS DRMCCT		;ADD FREE COMMAND PAIR BACK INTO COUNT
	HULOCK DRPI		;UNLOCK QUEUE
	OKPI
	OKSKED

	RET

UDRM2:	SKIPG DRMCCT		;ANY ROOM NOW?
	 JRST 0(4)		;NO
	JRST 1(4)		;YES

UDRM3:	SETO 1,			;ILLEGAL ADDRESS ERROR
	SKIPN INSKED		;FROM SCHED?
	JRST UDRM4		;NO
	MOVEM 1,1(4)		;YES, FAKE BAD OP
	SETZM 0(4)

UDRM5:	MOVE 1,4
	AOS (P)
	RET

UDRMSW:	SKIPGE 0(1)		;TEST FO COMMAND COMPLETION
	 JRST 0(4)		;NOT YET
	MOVE 2,1(1)		;GET CONI BITS
	HLOCKI DRPI		;LOCK QUEUE
	EXCH 1,DRMFCL		;PUT COMMAND PAIR BACK ON QUEUE
	MOVEM 1,@DRMFCL
	AOS DRMCCT		;ADD TO QUEUE COUNT
	HULOCK DRPI		;UNLOCK QUEUE
	OKPI
	MOVE 1,2		;RETURN ERROR BITS
	JRST 1(4)		;SAY COMMAND FINISHED


;DRUM DRIVER
;AC1/18-35 CORE PAGE NUMBER (CST INDEX) OF PAGE
;	WRITE OPERATION IF 1, READ IF 0	
;IF ADDRESS IS > = MAXDRM THEN GO TO DSKIO

DRMIO:	SKIPG	DRUMP	; DRUM AVAILABLE
	JRST	DSKIO	; NO
	HRRZ	2, CST1(1)
	CAML	2, MAXDRM
DRMOP3:	JRST	[ AOS DKSWCT	;NOT REAL DRUM SWAP-TO DISK
		  JRST DSKIO  ]
	AOS	DRSWCT		;COUNT REAL DRUM SWAPS
	PUSH	P, 1
	CAIL	2,FDRUM
	JRST	[ ANDI 2,77
		 ADDI 2,DRMSEC	;SECOND DRUM
		 JRST .+2 ]
	ANDI	2, 77	; GET SECTOR NUMBER
	ADDI	1, CST3
	HLLZM	1, 0(1)	; STORE WRITE BIT, 0 MARKS LAST WORD
	HLOCKI	DRPI		;LOCK QUEUE
	JUMPL	1, [HRRM 1, @DRMWI(2)
		    HRRZM 1, DRMWI(2)	
		   AOS DRMWRS	;NO OF DRUM WRITES
		    JRST DRMOP2]
	SKIPE	PRELRQ		;PRELOADING
	JRST	DRMOP4		;YES
	HRRM	1, @DRMRI(2)	;UPDATE READ QUEUES
	HRRZM	1, DRMRI(2)
DRMOP5:	AOS 	DRMRDS		;NO OF DRUM READS
DRMOP2:	HULOCK  DRPI		;UNLOCK QUEUE
	OKPI
	PUSH P,3
	SETZ 3,0
	CAIL 2,DRMSEC
	JRST [ MOVEI 3,1
		SUBI 2,DRMSEC
		JRST .+1 ]
	MOVEI 	1,1
	LSH	1,0(2)
	IORM	1,DRMINT(3)	;SET SECTOR INT. REQ. FLAG
	POP P,3
	POP	P, 1
	RET

DRMOP4:	HRRM	1,@DRMPI(2)
	HRRZM	1,DRMPI(2)
	JRST	DRMOP5


; INTERRUPT ROUTINE

DRMSV:	XWD	DRMSVR, .+1	; CALLED BY JSYS IN PISRV
	DATAI DR0,0
	SKIPN	DRMFLG		;CLOCK LEVEL INT REQ
	SKIPGE  0		;DRUM INT. ON
	JRST	.+2
	JRST	@DRMSVR	;NOT DRUM
IFN KIFLG,<CLSB DRMCHN>
	SETZM 	DRMFLG
	SKIPE 	PWRDWN		;POWER DOWN
	JRST	[ CONO DR0,DRMRES	;RESET DRUM
		JRST DRMINX ]	;EXIT FROM DRUM
	CONO DR0,DRMCLT		;CLEAR DRUM INT.
	CONI DR0,0		;READ STATUS INTO 0
	SKIPN CMDCNT		;DRUM ACTIVE
	JRST DRMSV1		;NO TRY TO START
	TDNE 0,[DRMQPA+DRMCSE+DRMQF+177] ;DRUM ERRORS
	JRST DRMERR
	MOVE 1,CMDST		;FIRST CLEANUP COMMAND
DRMSV3:	SKIPN CMDCNT
	BUG(HLT,<DRUM SEQ. NUMBER ERROR>)
	SKIPN QPWA(1)		;ANYTHING THERE
	BUG(HLT,<DRUM SHOULD HAVE COMM. HERE>)
	JSP 4,DRMSV8		;GO CHECK FOR UTILITY OR SWAPPER
	MOVE 2,CMDST
	AOS 1,CMDST
	CAIL 1,NPWDS		;WRAP AROUND
	SETZB 1,CMDST
	LDB 3,DRDPA		;GET DRUM SEQ. NUMBER
	CAIE 3,0(2)
	JRST DRMSV3		;TRY FOR MORE CLEANUP
DRMSV1:	MOVE 2,CMDCNT		;START LOOKING FOR MORE COMM.
	CAIL 2,2		;DONT PUT OUT MORE THAN 3
	JRST DRMINX		;ENOUGH ALREADY
	SETZM DRLOOP

DRMSV5:	JSP 3,DRMNXT		;GET NEXT SECTOR
	HLOCK DRPI		;LOCK DRUM QUEUES
	HLOCKI SWPPI
	SKIPE 2,DRMUO(1)	;UTILITY QUEUE
	JRST DRMUQ
	SKIPE 2,DRMRO(1)		;READ QEUE
	JRST DRMIQ
	SKIPE 2,DRMPO(1)		;PRELOAD QUEUE
	JRST DRMPQ
	SKIPE 2,DRMWO(1)	;WRITE QUEUE
	JRST DRMSV2
	HULOCK SWPPI
	OKPI
	SETZ 3,0
	CAIL 1,DRMSEC
	JRST [ SUBI 1,DRMSEC
		MOVEI 3,1
		JRST .+1 ]
	MOVEI 2,1
	LSH 2,0(1)
	ANDCAM 2,DRMINT(3)	;REMOVE INT. REQ. FLAG
	HULOCK DRPI
	SKIPE DRLOOP
        JRST DRMINX	;EXIT
	AOS DRLOOP
	JRST DRMSV5
DRMSV2:	HRRZ 2,0(2)
	JUMPN 2,.+3
	MOVEI 3,DRMWO(1)
	MOVEM 3,DRMWI(1)
	EXCH 2,DRMWO(1)
DRM4SV:	HULOCK SWPPI
	HULOCK DRPI		;UNLOCK QUEUES
	OKPI
	AOS 1,DRMSEQ		;GET NEXT SEQ NUMBER
	CAIL 1,NPWDS
	SETZB 1,DRMSEQ		;WRAP AROUND
	SKIPE QPWA(1)		;THIS ENTRY SHOULD BE AVAIL.
	BUG(HLT,<DRUM QUEUE FOUL UP>)
	SKIPN CMDCNT
	MOVEM 1,CMDST		;INIT. FIRST DRUM RETURN



	SUBI 2,CST3		;GET PAGE NO
	SKIPL 2			;IF ILLEGAL PAGE #,
	CAIL 2,1000		;MUST BE
	 JRST DRMSV6		;UTILITY OP
	MOVEI 3,0(2)
	LSH 3,11		;MAKE ACTUAL ADDRESS
	TLO 3,617700		;SET SECTOR COUNT. ,PWB
	MOVEM 3,QPWB(1)
	MOVSI 4,DWRBIT
	MOVEI 3,2
	TDNE 4,CST3(2)		;TRYING TO WRITE
	MOVEI 3,1		;YES
	HRRZ 4,CST1(2)		;GET DRUM ADDRESS

DRMSV7:	DPB 3,BFUNC		;SET FUNCTION
	DPB 1,BSEQN		;SEQ. NO.
	MOVEI 3,DRMCHN
	DPB 3,BPIA		;PIA
	MOVEI 3,5
	DPB 3,BPWAC		;PWA ADDR.
	MOVE 3,4		;GET DRUM ADDRESS
	CAIL 3,FDRUM		;FIRST DRUM
	JRST [ANDI 3,77
		TLO 3,(1B0)
		JRST .+2 ]
	ANDI 3,77
	MOVEM 3,DRLSEC		;LAST DRUM SECTOR ADDRESSED
	LSH 3,2
	DPB 3,BSECT		;SET SECTOR
           	MOVE 3,4		;GET DRUM ADDRESS
	CAIL 3,FDRUM
	JRST [SUBI 3,FDRUM
		JRST .+1 ]
	LSH 3,-6
	DPB 3,BTRACK		;TRACK
	SKIPGE DRLSEC
	JRST [MOVEI 3,400000
		IORM 3,QPWA(1)
		JRST .+1 ]

	DATAO DR0,QPWA(1)
	DATAO DR0,QPWB(1)
	SKIPN CMDCNT
	CONO DR0,DRMEXQ		;START DRUM
	AOS CMDCNT		;ONE MORE COMMAND
	JRST DRMSV1		;TRY FOR MORE

DRMINX:	
	UNBRK DRM		;RETURN FROM INTERRUPT

;UTILITY OPERATION

DRMSV6:	ADDI 2,CST3		;GET COMMAND PAIR INDEX NUMBER 
	SUBI 2,DRMCL-2		;(MACRO DOESN'T LIKE CST3-DRMCL+2)
	LSH 2,-1
	DPB 2,DRUTS		;SET INDEX TO SAY THIS IS UTILITY OP
	LSH 2,1
	ADDI 2,DRMCL-2		;TURN BACK INTO ADDRESS
	MOVE 3,1(2)		;GET PWB
	TLZ 3,760017		;MASK OFF IRRELEVANT BITS
	TLO 3,(6B2)		;SET IN PWB CODE
	MOVEM 3,QPWB(1)		;SAVE PWB
	LDB 3,[POINT 2,1(2),16]	;GET FUNCTION
	HLRZ 4,0(2)		;GET ADDRESS
	TRZ 4,(1B0)		;TURN OFF "NOT YET EXECUTED" BIT
	JRST DRMSV7

DRMSV8: LDB 2,DRUTS		;GET UTILITY INDEX NO.
	SETZM QPWA(1)		;RESET STUFF
	SETZM DRMSTS(1)
	SOS CMDCNT
	JUMPE 2,[LDB 1,[POINT 9,QPWB(1),26] ;NOT UTILITY OP, SWAPPER
		 JRST SWPDON]
	LSH 2,1			;CONVERT TO COMMAND PAIR POINTER
	MOVSI 3,(1B0)		
	ANDCAM 3,DRMCL-2(2)	;TURN OFF "NOT YET EXECUTED" BIT 
	SETZM DRMCL-2+1(2)	;AND SAY "NO ERRORS"
	JRST (4)


DRMIQ:	HRRZ	2, 0(2)		; REMOVE ENTRY FROM READ QUEUE
	JUMPN	2, .+3
	MOVEI	3, DRMRO(1)
	MOVEM	3, DRMRI(1)
	EXCH	2, DRMRO(1)
	JRST	DRM4SV

DRMPQ:	HRRZ	2,0(2)
	JUMPN	2,.+3
	MOVEI	3,DRMPO(1)
	MOVEM	3,DRMPI(1)
	EXCH	2,DRMPO(1)
	JRST	DRM4SV

DRMUQ:	HRRZ 2,0(2)
	JUMPN 2,.+3
	MOVEI 3,DRMUO(1)
	MOVEM 3,DRMUI(1)
	EXCH 2,DRMUO(1)
	JRST DRM4SV

;FIGURE NEXT BEST SECTOR

DRMNXT:	
	SKIPE DRLOOP	;SECOND TIME
	JRST DRMNX3	;YES
	SKIPE CMDCNT		;DRUM ALREADY BUSY
	JRST DRMNX1		;YES
DRMNX3:	SETZ 1,0
	SKIPGE DRLSEC
	MOVEI 1,1
	CONO DR0,0(1)
	DATAI DR0,1		;READ SECTOR
	SKIPE DRLOOP
	 ADDI 1,4
	ADDI 1,2
	ANDI 1,177
	LSH 1,-2		;MAKE PAGE
DRMNX2:	ADDI 1,1
	CAILE	1,DRMSEC-1
	 SUBI 1,DRMSEC
	SKIPGE DRLSEC
	ADDI 1,DRMSEC
IFN DRUM2,<HRLZI 2,400000
		  XORM 2,DRLSEC >
	JRST	0(3)

DRMNX1:	HRRZ 1,DRLSEC		;PICK UP LAST SECTOR
	JRST DRMNX2

; ERROR CONDITIONS

DRMERR:	AOS	DRMCFE		; COUNT ERRORS
	MOVE 2,CMDST
DRMER3:	LDB 1,DRDPA
	CAIN 2,0(1)		;TRY CLEANUPOF COMM. BEFORE ERR.
	JRST DRMER1		;NO MORE
	SKIPN CMDCNT
	BUG(HLT,<DRUM SEQ. ERROR>)
	SKIPN QPWA(2)	;ANYTHING THERE
	BUG(HLT,<DRUM QUEUE SHOULD HAVE COMM. HERE>)
	MOVE 1,2		;MOVE SEQ. NO. INTO 1 FOR DRMSV8
	JSP 4,DRMSV8		;GO CHECK FOR UTILITY OR SWAPPER
                                                   DRMER2:	AOS 2,CMDST
	CAIL 2,NPWDS
	SETZB 2,CMDST
	JRST DRMER3		;TRY AGAIN

DRMER1:	MOVEM 0,DRMLER+3
	MOVE 2,DRMSTS(1)
	MOVEM 2,DRMLER+2
	MOVE 2,QPWA(1)
	MOVEM 2,DRMLER
	MOVE 2,QPWB(1)
	MOVEM 2,DRMLER+1	;SAVE MEM ADDR.
	TRNE 0,DRMCSE		;COMM SEQ ERROR
	JRST [  AOS DCTRER
		JSR BUGCHK
		JRST .+1]
	TDNE 0,[DRMQPA]		;QUEUE PARITY
	AOS QPAER
	TRNE 0,DRMQF
	AOS QBFER		;QUEUE FULL
	TRNE 0,DRMSPA
	AOS DIPAER		;SILO PARITY ERROR
	TRNE 0,DRMNXM
	AOS DNXMER		;NXM
	TRNE 0,DRMEXC
	AOS EXDER		;EXCEEDED CAPACITY
	TRNE 0,DRMHUD
	AOS DTIMER		;HUNG DEVICE
	TRNE 0,DRMMPE
	AOS DCPAER		;MEM PARITY ERROR
	TRNE 0,DRMOVR
	AOS DOVRER		;DATA LATE
	TRNE 0,DRMCKE
	AOS DRCKER		;CHECKSUM ERROR
	LDB 4,DRUTS		;GET UTILITY INDEX
	JUMPN 4,[LSH 4,1	;YES, UTILITY OP,
		MOVSI 2,(1B1)	;IGNORE ERROR RETRYS?
		TDNE 2,DRMCL-2+1(4)
		 JRST DRMEP1	;YES, TREAT AS HARD ERROR
		JRST .+1]	;NO, DO ERROR RETRY
	LDB 4,DRERC		;GET RETRY COUNT
	AOJ 4,
	DPB 4,DRERC		;UPDATE RETRY COUNT
	CAIN 4,1		;FIRST ERROR?
	 JSP 3,DRMEL1		;YES, TRY LOGGING IT
	CAIL 4,NRETRY
	JRST DRMEP		;SOLID ERROR
	MOVE 2,QPWA(1)
	TLZ 2,(1B0)
	DATAO DR0,2		;PUT IN FRA
	MOVE 2,QPWB(1)
	TLZ 2,(1B0)
	DATAO DR0,2		;PUT IN FRB
	CONO DR0,DRMCLT		;CLEAR INTERRUPT AGAIN
	CONO DR0,DRMCLE		;CLEAR ERRORS
	CONO DR0,DRMEXF		;EXECUTE OUT OF FR
	JRST DRMINX		;EXIT

DRMEP:	TRNE 0,DRMMPE		;PERMANENT ERROR
	JRST [ MOVE 2,[SIXBIT /DRM/]
		MOVEM 2,DEVMPE
		ISB APRCHN
		JRST .+1]
	LDB 4,DRUTS		;UTILITY OP?
	JUMPN 4,DRMEP3		;YES
	SETZM QPWA(1)
	SETZM DRMSTS(1)
	SOS CMDCNT
	LDB 1,[POINT 9,QPWB(1),26]
	MOVSI 3,SWPERR
	IORM 3,CST3(1)
	JSP 4,SWPDON

DRMEP2:	CONO DR0,DRMCLT		;CLEAR INT.
	CONO DR0,DRMCLE		;CLEAR ERRORS
	AOS 2,CMDST
	CAIL 2,NPWDS
	SETZM CMDST
	SKIPE CMDCNT		;ANY MORE IN QUEUE
	CONO DR0,DRMEXQ		;YES,START QUEUE AGAIN
	JRST DRMSV1		;CONTINUE

; UTILITY OP HARD ERROR

DRMEP3:	LSH 4,1			;MAKE INTO COMMAND PAIR POINTER
DRMEP1:	MOVEM 0,DRMCL-2+1(4)	;SAVE CONI
	MOVSI 3,(1B0)		;TURN OFF "NOT YET EXECUTED" BIT
	ANDCAM 3,DRMCL-2(4)
	SETZM QPWA(1)
	SETZM DRMSTS(1)
	SOS CMDCNT
	JRST DRMEP2
	SUBTTL	DRUM ERROR LOGGER

; ON FIRST ERROR, LOG STATUS WORDS, AND IF READ, COPY DATA

DRMEL1:	AOSE DDERLL		;GET ERROR LOGGING CODE
	 JRST (3)		;BUSY

	LDB 2,DRUTS		;UTILITY OP?
	SKIPE 2
	SKIPA 2,[1B8+@DRMUTD]	;YES
	MOVE 2,[0B8+@DRMSWD]	;NO
	MOVEM 2,DDERTP		;IDENTIFY OURSELVES

	MOVE 2,[DRMCFE,,DDERWD] ;SAVE STATUS WORDS
	BLT 2,DDERWD+4

	LDB 2,BFUNC		;GET FUNCTION	
	CAIN 2,2		;READ
	TRNN 0,1B29+1B33+1B34+1B35 ; AND EITHER SILO P.E.,MEM P.E., DATA LATE
				;OR DATA CHECK?

	 JRST  [SETOM DDERST	;NO, LET JOB 0 HANDLE IT
		SETOM DDERBP	;SAY WE HAVE NO DATA PAGES WITH THIS ERROR
		AOS JB0FLG	;AND GET JOB 0 IN TO LOOK AT IT
		JRST 0(3)]	;RETURN TO NORMAL DRMSV

	HRRZ 2,QPWB(1)		;SAVE ORIGINAL READ CORE PAGE ADDRESS
	MOVEM 2,DDERST		;AND TURN SCHED LEVEL ROUTINES ON WITH IT
	SETZM QPWA(1)
	SETZM DRMSTS(1)
	SOS CMDCNT
	JRST DRMEP2		;AND CONTINUE DRUM

; THE FOLLOWING ROUTINES ARE DEVICE DEPENDENT ROUTINES CALLED FROM SCHED
; LEVEL THROUGH THE OPERATION TYPE (SWAPPER, UTILITY OP) DEPENDENT
; DISPATCH TABLES

; SWAPPER ROUTINES DISPATCH TABLE

DRMSWD:	JRST DRMEL2		;REQUE COMMAND
	JRST UDRMSW		;TEST FOR SCHED LEVEL UDRMIO COMPLETION
	JRST DRMEL3		;HANDLE PERMANENT ERROR
	JRST DRMEL4		;HANDLE SUCCESSFULL COMPLETION

; UTILITY OP ROUTINES DISPATCH TABLE

DRMUTD:	JRST DRMEL8		;REQUE COMMAND
	JRST UDRMSW		;TEST FOR SCHED LEVEL UDRMIO COMPLETION
	JRST DRMEL6		;HANDLE PERMANENT ERROR
	JRST DRMEL5		;HANDLE SUCCESSFULL COMPLETION

; REQUE SWAPPER DRUM READ AFTER BAD DATA HAS BEEN COPIED

DRMEL2:	MOVE 1,DDERST		;GET CORE ADDRESS
	LSH 1,-^D9		;TURN INTO PAGE NUMBER
	HRRZ 1,CST1(1)		;GET DRUM ADDRESS
DRMEL7:	MOVEI 2,1000		;READ A PAGE
	MOVE 3,DDERST		;INTO CORE ADDRESS
	JRST UDRMIO		;GO DO IT

; REQUE UTILITY DRUM READ AFTER BAD DATA HAS BEEN COPIED

DRMEL8:	LDB 1,[POINT 6,DRMEUT,26] ;GET COMMAND PAIR INDEX
	LSH 1,1			;MAKE INTO COMMAND PAIR ADDRESS
	HLRZ 1,DRMCL-2(1)	;GET ORIGINAL DRUM ADDRESS
	TLZ 1,(1B0)		;TURN OFF NOT YET EXECUTED BIT
	JRST DRMEL7

; HANDLE SWAPPER DRUM READ PERMANENT ERROR

DRMEL3:	MOVSI 3,SWPERR
	JRST .+2

; HANDLE SWAPPER DRUM SUCCESSFULL READ

DRMEL4:	SETZ 3,			;SAY NO ERRORS
	MOVE 1,DDERST		;GET CORE ADDRESS
	LSH 1,-^D9		;TURN INTO PAGE NUMBER
	IORM 3,CST3(1)		;INDICATE ANY POSSIBLE ERRORS
	JRST SWPDON		;GO TELL SWAPPER

; HANDLE UTILITY DRUM SUCCESSFULL READ 

DRMEL5:	SETZ 1,			;SAY NO ERRORS

; HANDLE UTILITY DRUM READ PERMANENT ERROR

DRMEL6:	LDB 3,[POINT 6,DRMEUT,26] ;GET COMMAND PAIR INDEX
	LSH 3,1			;TURN INTO ADDRESS
	MOVEM 1,DRMCL-2+1(3)	;STORE POSSIBLE ERRORS
	MOVSI 1,(1B0)		;TURN OFF EXECUTION AWAITING BIT
	ANDCAM 1,DRMCL-2(3)
	JRST (4)		;AND RETURN

; DRUM INITIALIZATION

DRMIOI:	SETOM	DST		; MAKE ALL DST ENTRIES EMPTY
	MOVE	1, [XWD DST, DST+1]
	BLT	1, DST+NDST+1
DRMRST:	SKIPG	DRUMP		; DRUM ON-LINE
	RET			; NO
	MOVSI	1, -DRMSEC
DRMII2:	SETZM	DRMRO(1)	; SET UP QUEUES
	SETZM	DRMWO(1)
	SETZM	DRMPO(1)
	SETZM	DRMUO(1)
	MOVEI	2, DRMRO(1)	
	MOVEM	2, DRMRI(1)
	MOVEI	2, DRMWO(1)
	MOVEM	2, DRMWI(1)
	MOVEI	2, DRMPO(1)
	MOVEM	2, DRMPI(1)
	MOVEI	2, DRMUO(1)
	MOVEM	2, DRMUI(1)
IFN DRUM2,<
	SETZM DRMRO+DRMSEC(1)
	SETZM DRMWO+DRMSEC(1)
	SETZM DRMPO+DRMSEC(1)
	SETZM DRMUO+DRMSEC(1)
	MOVEI 2,DRMRO+DRMSEC(1)
	MOVEM 2,DRMRI+DRMSEC(1)
	MOVEI 2,DRMWO+DRMSEC(1)
	MOVEM 2,DRMWI+DRMSEC(1)
	MOVEI 2,DRMPO+DRMSEC(1)
	MOVEM 2,DRMPI+DRMSEC(1)  
	MOVEI 2,DRMUO+DRMSEC(1)
	MOVEM 2,DRMUI+DRMSEC(1)
>
	AOBJN	1, DRMII2
	MOVSI	1,-NPWDS
	SETZM	QPWA(1)
	SETZM	QPWB(1)
	SETZM	DRMSTS(1)
	AOBJN	1,.-3
	SETOM	DRMSEQ
	SETZM	DRMFLG
	SETZM	CMDCNT
	SETZM DRLOOP
	SETZM DRMINT
	SETZM DRMINT+1
	MOVEI 1,DRMCL+2*NDRMCM-2
	SETZM (1)

	MOVEM 1,-2(1)
	SUBI 1,2
	CAILE 1,DRMCL
	JRST .-3

	MOVEM 1,DRMFCL
	MOVEI 1,NDRMCM
	MOVEM 1,DRMCCT
	CONO	DR0, DRMRES	; RESET DRUM
	MOVEI	1, ^D11000
	SOJG	1, .		; WAIT AROUND 3OMS
	RET


;ASSIGN PAGE, EMPTIEST SECTOR

;"DRMASF" RETURNS CALLER+1 IF THE DRUM IS FULL OR UNAVAILABLE, ELSE
;CALLER+2 WITH THE DRUM ADDRESS ASSIGNED IN A.  THE SECTOR CHOSEN FOR
;THE ASSIGNMENT HAS THE MOST FREE PAGES.  "ASDRM" TRIES TO ASSIGN
;A PAGE AT SECTOR (A)+1 OR AS SOON AFTER AS POSSIBLE.  IT RETURNS CALLER+1
;IF NO FREE PAGES ARE LEFT OR CALLER+2 WITH THE DRUM ADDRESS IN A.

	INTERN DRMASN

ASDRM:
DRMASN:
DRMASF:	SKIPG DRMFRE		;ROOM AVAILABLE?
	JRST RETN		;NO, RETURN BAD
	MOVE 1,DRMLSA
DRMAS1:	CAIL 1,NDST/^D36+1
	RET			;DRUM FULL
	SKIPE 3,DRMBIT(1)
	JFFO 3,SECFNR		;FOUND SOMETHING
	AOJA 1,DRMAS1		;TRY AGAIN


;ASSIGN PAGE ON SWAPPING DISK

DRMASD:	SKIPG DRMFRE
	JRST RETN		;NO FREE PAGES
	MOVEI 1,NDST/^D36
DRMAD1:	SKIPE 3,DRMBIT(1)
	JFFO 3,SECFND		;FOUND ONE
	SOJGE 1,DRMAD1		;TRY AGAIN
	RET			;NONE FOUND

SECFNR:	MOVEM 1,DRMLSA
SECFND:	MOVE 3,BITS(4)
	ANDCAM 3,DRMBIT(1)	;REMOVE PAGE
	IMULI 1,^D36
	ADDI 1,0(4)
	IDIVI 1,DRMSEC
	LSH 1,6
	CAML 1,MAXDRM
	SOS DSKFRE
	IORI 1,0(2)
	TLO 1,1B34		;SET DRUM BIT
DRMSA1:	SOS DRMFRE		;DECR. FREE PAGE COUNT
DRMSA2:	AOS 0(P)
RETN:	RET


;CONVERT DRUM ADDRESS INTO INDEX INTO DST

	INTERN GDSTX

GDSTX:	TLNN 2,14		;INSIST ON REGULAR DRUM ADDRESS
	TLNN 2,2
	BUG(HLT,<GDSTX - BAD ADDRESS>)
	HRRZS 3,2		;SPLIT BAND AND SECTOR
	ANDI 3,77		;SECTOR
	LSH 2,-6		;BAND
	IMULI 2,DRMSEC
	ADDI 2,0(3)
	CAIL 2,NDST
	BUG(HLT,<GDSTX - BAD ADDRESS>)
	RET

	INTERN DASDRM
DASDRM:	CALL DEDRM
	JFCL
	BUG(HLT,<ATTEMPT TO DEASSIGN BAD OR UNASSIGNED DRUM ADDRESS>)
	SETZM DNOGO		;RESET FLAG THAT PAGE IS AVAILABLE
	RET

;SET USED DRUM PAGE BITS AND CHECK BITS

LS DRNSET,1		;FLAG TO SAY PAGES ARE READ

STBTWD:	TLOA 2,(1B0)
CHKDRR:	TLZ  2,(1B0)		;SET FLAG 
	LDB 3,[POINT 12,2,29]
	LDB 4,[POINT 6,2,35]
	IMULI 3,DRMSEC
	ADDI 3,0(4)
	IDIVI 3,^D36
	MOVE 4,BITS(4)		;GET PROPER BIT
	JUMPL 2, [ TLZ 2,(1B0)	;SET BIT IN TABLE
		   IORM 4,DRBTWD(3)
		   RET ]
	TDNE 4,DRBTWD(3)	;TEST BIT
	AOS 0(P)
	RET

;ROUTINES TO ATTEMPT ASSIGNMENT OF A SPECIFIC DRUM ADDRESS

ASDRMS:	JSP 4,DRMBS
	TDNN D,DRMBIT(B)	;SKIP IF PAGE IS AVAILABLE
	RET
	ANDCAM D,DRMBIT(B)
	CAML 1,MAXDRM		;WAS IT A DSK PAGE OR A DRM PAGE
	SOS DSKFRE		;DISK PAGE
	JRST DRMSA1

;ROUTINE TO RELEASE A DRUM PAGE

DEDRM:	JSP 4,DRMBS
	TDNE D,DRMBIT(B)	;CHECK IF NOT YET ASSIGNED
	RET			;SKIP RETURN 
	IORM D,DRMBIT(B)
	CAMGE B,DRMLSA		;IS THIS IN FRONT OF FIRST FREE SECTOR
	MOVEM B,DRMLSA		;YES UPDATE POINTER
	CAML 1,MAXDRM
	AOS DSKFRE
	AOS DRMFRE
	AOS (P)
	RET			;MAKE DOUBLE SKIP RETURN

;CHECKS FOR LEGAL DRUM ADDRESS & GETS WORD & BIT INDICES TO BIT TABLE
;RETURNS BIT CHUNK WORD INDEX IN A, BIT TABLE WORD INDEX IN B,
;SECTOR NO. IN C, AND BIT POSITION MASK IN D

DRMBS:	ANDI A,-1
	MOVE C,A
	ANDI C,77
	CAIGE A,DRMBND*100	;LEGAL BAND?
	CAIL C,DRMSEC		;LEGAL SECTOR?
	RET 
	AOS (P)
	LDB 2,[POINT 12,1,29]
	LDB 3,[POINT 6,1,35]
	IMULI 2,DRMSEC
	ADDI 2,0(3)
	IDIVI 2,^D36
	PUSH P,4
	MOVE 4,BITS(3)
	LDB 3,[POINT 6,1,35]
	POPJ P,0

;INITIALIZE DRUM BIT TABLES

DRMINI:	SETZM DRMBIT		;EACH "ONE" REPRESENTS A FREE PAGE
	MOVE A,[XWD DRMBIT,DRMBIT+1]
	BLT A,DRMBIT+<NDST/^D36>
	MOVSI F,-NDST
	SKIPG DRUMP		;SWAPPING TO DRUM
	ADD F,[XWD DSKNOV,DSKNOV] ;NO LEAVE SECTION FOR WHICH THEIR IS NO
;				;BACKUP FOR ON DISK ASSIGNED
DRMIN3:	HRRZI 1,0(F)
	IDIVI 1,DRMSEC
	CAIL 1,DRMBND
	JRST .+5
	LSH 1,6
	IORI 1,0(2)
	CALL DASDRM
	AOBJN F,DRMIN3		;RELEASE ALL DRUM PAGES

	MOVEI A,DRMBND*DRMSEC	;NO. OF PAGES ON DRUM
	SKIPG DRUMP
	SUBI A,DSKNOV		;SUBTRACT NON OVERLAP PAGES IF NO DRUM
	MOVEM A,DRMFRE
	MOVE 2,MAXDRM
	LSH 2,-6
	IMULI 2,DRMSEC
	SUB A,2			;CALCULATE NUMBER OF PAGES OF SWAPPING DISK
	MOVEM 1,DSKFRE
	MOVE 1,[XWD DRBTWD,DRBTWD+1]
	SETZM DRBTWD
	BLT 1,DRBTWD+<NDST/^D36>


	MOVEI F,0
DRMIN1:	MOVE A,DRMBBT(F)
	JUMPE A,DRMIN2		;DONE ON 0
DRMIN5:	PUSH P,A
	MOVEI 2,DRMSEC
	LSH A,6
DRMIN6:	PUSH P,2
	PUSH P,A
	CALL ASDRMS
	JFCL
	JFCL
	POP P,A
	ADDI A,1
	POP P,2
	SOJG 2,DRMIN6
	POP P,A
	AOBJN A,DRMIN5
	AOJA F, DRMIN1

DRMIN2:	MOVEI F,0
DRMIN4:	MOVE A,DRMBST(F)
	JUMPE A,DRMIOI		;DONE ON 0
DRMIN7:	PUSH P,A
	CALL ASDRMS
	 JRST	[POP P,A
		ADDI A,100-DRMSEC
		JRST DRMIN7]
	JFCL
	POP P,A
	AOBJN A,DRMIN7
	AOJA F,DRMIN4

DRMBBT:	0		;BAD BAND TABLE ENTRIES ARE -N,,BBBBBB
	BLOCK 10	;WHERE N IS NUMBER OF BANDS THAT ARE BAD
			;AND BBBBBB IS A BAND ADDRESS.
			;FOR EXAMPLE 200,,0 WOULD VARY FIRST DRUM OFFLINE

DRMBST:	0		;BAD SECTOR ENTRIES ARE -N,,BBBBSS
	BLOCK 6		;WHERE N IS NUMBER OF SECTORS THAT ARE BAD
			;AND BBBBSS IS DRUM ADDRESS BBBB BEING THE BAND
			;AND SS BEING THE SECTOR.
			;FOR EXAMPLE 200*^D13,,0 WOULD VARY FIRST DRUM OFFLINE
MAXDRM:	40000			;MAXIMUM DRUM ADDRESS

	END

