.TITLE DKDRIVER - VAX/VMS RK05 DISK DRIVER .IDENT 'V01-005' ; ; FACILITY: ; VAX/VMS RK05 DISK DRIVER ; ; AUTHOR: ; MICHAEL N. LEVINE AUGUST 1983 ; BASED ON VAX/VMS RL01 AND RK06/7 DRIVERS, ; TEMPLATE DRIVER, AND A. HERMES ; RK05 DRIVER ; ; MODIFIED BY: ; ; ; MICHAEL N. LEVINE 1-NOV-83 ; FIX FIRST MOUNT ERROR MESSAGE AS PER INFO FROM ; DECUS. ; ; MICHAEL N. LEVINE 17-FEB-84 ; REARRANGED FUNCTION DISPATCH TABLE TO ATTEMPT TO PUT THE ; MOST COMMON FUNCTIONS AT THE BEGINNING. EXPAND COMMENTS. ; RETRY ERROR HANDLEING RELEASED CONTROLLER THEN USED IT- ; CORRECTED LOGIC. ALSO SOME NORMAL EXIT CODE RELEASED THE ; CONTROLER AND THEN CLEARED THE CSR.DRIVE VALID BIT ; IN UCB STATUS LONG WORD WAS SET USEING THE $V FORM ; NOT THE $M FORM--CORRECTED ; MICHAEL N. LEVINE 22-FEB-84 ; ADDED DATA CHECK ON READ/WRITE CAPABILITY ; MICHAEL N. LEVINE 19-MARCH-84 ; FIXED ERROR IN DATA CHECK HANDLEING-DID INCORRECT ; CLEAR OF SOFTWARE INDICATORS ; MICHAEL N. LEVINE 26-NOVEMBER-84 ; MADE MODS FOR V4.0 VMS AS PER UPDATE SEMINAR NOTES ; MICHAEL N. LEVINE 9-JANUARY-85 ; ERROR IN TAKEING ERROR EXIT. PUT INTO R0/R1 FOR IOSB ; TO SOON AND WERE DESTROYED BEFORE ENTERING REQCOM ; ; .PAGE .SUBTITLE EXTERNAL AND LOCAL DEFINITIONS ; ; External symbols ; $ADPDEF ; Define Adapter Control Block $CANDEF ; Cancel reason codes $CRBDEF ; Channel request block $DCDEF ; Device classes and types $DDBDEF ; Device data block $DEVDEF ; Device characteristics $DYNDEF $EMBDEF ; Define Error message buffer $IDBDEF ; Interrupt data block $IODEF ; I/O function codes $IPLDEF ; Hardware IPL definitions $IRPDEF ; I/O request packet $PRDEF $SSDEF ; System status codes $UCBDEF ; Unit control block $VECDEF ; Interrupt vector block .PAGE .SUBTITLE UCB ; ; Definitions that follow the standard UCB fields ; $DEFINI UCB ; Start of UCB definitions .=UCB$K_LCL_DISK_LENGTH+2 ; Position at end of UCB $DEF UCB$L_DK_LOC .BLKL 1 ; DISK ADDR LOC $DEF UCB$W_DK_DS .BLKW 1 ; DRIVE STATUS REGESTER $DEF UCB$W_DK_ER .BLKW 1 ; ERROR REGESTER $DEF UCB$W_DK_CS .BLKW 1 ; Device's CSR register $DEF UCB$W_DK_WC .BLKW 1 ; Device's word count register $DEF UCB$W_DK_BA .BLKW 1 ; Device's buffer address $DEF UCB$W_DK_DA .BLKW 1 ; DEVICE DISK ADDRESS $DEF UCB$W_DK_DPN .BLKW 1 ; DATA PATH NUMBER $DEF UCB$L_DK_DPR .BLKL 1 ; DATA PATH REGESTER $DEF UCB$L_DK_FMPR .BLKL 1 ; FINAL MAP REGESTER $DEF UCB$L_DK_PMPR .BLKL 1 ; PREVIOUS MAP REGESTER $DEF UCB$W_DK_DB .BLKW 3 ; DATA BUFFER REGESTER $DEF UCB$B_DK_IND .BLKB 1 ; SOFTWARE INDICATORS UCB$M_DK_IND_PURGE=1 ; BIT 0 PURGE ERROR UCB$M_DK_IND_DATACK=2 ; BIT 1 DATA CHECK ALLOWED $DEF UCB$W_DK_FUNC .BLKW 1 ; CURRENT FUNCTION BEING EXECUTED UCB$K_DK_UCBLEN=. $DEFEND UCB ; End of UCB definitions .PAGE .SUBTITLE DEVICE REGESTER OFFSETS AND BIT DEFINITIONS ; ; Device register offsets from CSR address ; $DEFINI DK,DOT=-4 ; Start of status definitions $DEF DK_DS .BLKW 1 ; DRIVE STATUS _VIELD DK_DS,0,<- - ; SECTOR COUNTER - ; SC=SA - ; WRITE PROTECT - ; READ/WRITE/SEEK READY - ; DRIVE READY - ; SECTOR COUNTER OK - ; SEEK INCOMPLETE - ; DRIVE UNSAFE - ; DRIVE IS RK05 - ; DRIVE POWER LOW - ; DRIVE ID > $DEF DK_ER .BLKW 1 ;ERROR REGESTER _VIELD DK_ER,0,<- - ; WRITE CHECK - ; CHECK SUMM ERROR <,3>,- ; NOT USED - ; NEX SECTOR - ; NEX CYLINDER - ; NEX DRIVE - ; TIMEING ERROR - ; DATA LATE - ; NEX MEMORY - ; PROGRAMMING - ; SEEK ERROR - ; WRITE LOCK OUT - ; OVERRUN - ; DRIVE ERROR > .PAGE $DEF DK_CS .BLKW 1 ; Control/status ; ; Bit positions for device control/status register ; _VIELD DK_CS,0,<- ; Control/status register ,- ; Start device ,- ; FUNCTION BITS 1,2,3 ,- ; Extended address bits ,- ; Enable interrupts ,- ; Device ready for command ,- ; STOP ON SOFT ERROR <,1>,- ; Bit nine ,- ; FORMAT ,- ; INHIBIT INCREMENT <,1>,- ; Bit TWELVE ,- ; SEARCH COMPLETE ,- ; HARD ERROR flag ,- ; Error or external interrupt > $DEF DK_WC .BLKW 1 ; Word count $DEF DK_BA .BLKW 1 ; Buffer address $DEF DK_DA .BLKW 1 ; DISK ADDRESS _VIELD DK_DA,0,<- - ; SECTOR - ; SURFACE - ; TRACK/CYLINDER - ; UNIT NUMBER > $DEF DK_DB .BLKW 1 ; Data buffer $DEFEND DK ; End of device register ; definitions. .PAGE .SUBTITLE DRIVER PROLOG TABLE ; ; Driver prologue table ; DPTAB - ; DPT-creation macro END=DK_END,- ; End of driver label ADAPTER=UBA,- ; Adapter type FLAGS=DPT$M_SVP,- ; PERMINENT SYS PAGE REQUIRED UCBSIZE=,- ; Length of UCB NAME=DKDRIVER,- ; Driver name DEFUNITS=8,- ; DEFAULT IS 8 UNITS MAXUNITS=8,- ; MAX OF 8 UNITS DELIVER=DK_AUTO_UNIT ; AUTOGEN-DETERMINE UNIT'S DPT_STORE INIT ; Start of load ; initialization table DPT_STORE DDB,DDB$L_ACPD,L,<^A/F11/> ; DEFAULT ACP DPT_STORE DDB,DDB$B_ACPCLASS,B,DDB$K_CART;DEVICE IS A CARTRIDGE DPT_STORE UCB,UCB$B_FIPL,B,8 ; Device fork IPL DPT_STORE UCB,UCB$B_DIPL,B,21 ; Device interrupt IPL DPT_STORE UCB,UCB$L_DEVCHAR,L,<- ; Device characteristics DEV$M_DIR!- ; DIRECTORY DEVICE DEV$M_FOD!- ; FILE ORIENTED DEVICE DEV$M_SHR!- ; SHAREABLE DEV$M_AVL!- ; AVAILABLE DEV$M_ELG!- ; ERROR LOGGING DEV$M_IDV!- ; input device DEV$M_ODV!- ; output device DEV$M_RND> ; RANDOM ACCESS DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_DISK ; DISK device class DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,512 ; Default buffer size DPT_STORE UCB,UCB$B_DEVTYPE,B,100 ; NEW DEVICE TYPE GIVE IT #100 DPT_STORE UCB,UCB$B_ERTMAX,B,8 ; ERROR RETRY MAX ALLOWED DPT_STORE UCB,UCB$B_ERTCNT,B,8 ; CURR. XFR RETRY COUNT DPT_STORE UCB,UCB$L_MAXBLOCK,L,<200*2*12>;DEVICE SIZE IN BLOCKS DPT_STORE UCB,UCB$B_SECTORS,B,12 ; NUMBER OF SECTORS/TRACK DPT_STORE UCB,UCB$B_TRACKS,B,2 ; TRACKS(SURFACES)/CYLINDER DPT_STORE UCB,UCB$W_CYLINDERS,W,200 ; CYLINDERS/DISK .PAGE DPT_STORE REINIT ; Start of reload ; initialization table DPT_STORE DDB,DDB$L_DDT,D,DK$DDT ; Address of DDT DPT_STORE CRB,CRB$L_INTD+4,D,- ; Address of interrupt DK_INTERRUPT ; service routine DPT_STORE CRB,- ; Address of controller CRB$L_INTD+VEC$L_INITIAL,- ; initialization routine D,DK_CONTROL_INIT DPT_STORE CRB,- ; Address of device CRB$L_INTD+VEC$L_UNITINIT,- ; unit initialization D,DK_UNIT_INIT ; routine DPT_STORE END ; End of initialization ; tables .PAGE .SUBTITLE DRIVER DISPATCH TABLE ; ; Driver dispatch table ; DDTAB - ; DDT-creation macro DEVNAM=DK,- ; Name of device START=DK_START,- ; Start I/O routine FUNCTB=DK_FUNCTABLE,- ; FDT address REGDMP=DK_REG_DUMP,- ; Register dump routine ERLGBF=<<<16>*4>+<1*4>+>,-;ERROR LOG BUFFER DIAGBF=<<<16>*4>+<<3+5+1>*4>> ; DIAG. BUFFER .PAGE .SUBTITLE FUNCTION DECISION TABLE ; ; Function decision table ; DK_FUNCTABLE: ; FDT for driver FUNCTAB ,- ; Valid I/O functions .PAGE FUNCTAB ,- ; BUFFERED I/O FUNCTIONS FUNCTAB DK_ALIGN_COUNT,- ; COMMANDS REQUIREING I/O FUNCTAB +ACP$READBLK,- ; FDT read routine for ; read FUNCTAB +ACP$WRITEBLK,- ; FDT write routine for ;WRITE FUNCTAB +ACP$ACCESS, ; OPEN/CREATE FILE FUNCTAB +ACP$DEACCESS, ; CLOSE FILE FUNCTAB +ACP$MODIFY,;FILE PROCESSING FUNCTAB +ACP$MOUNT, ; MOUNT DISK FUNCTAB +EXE$ZEROPARM,;NO PARAMETERS FUNCTAB +EXE$ONEPARM, ; ONE PARAMETER FUNCTAB +EXE$SETMODE,- ; FDT set mode routine ; set mode. FUNCTAB +EXE$SENSEMODE,- FUNCTAB +EXE$LCLDSKVALID, .PAGE .SBTTL DK_FDT_ROUTINE, FDT routines ;++ ; DK_ALIGN_COUNT ; Check to see that xfer is an even number of bytes ; Functional description: ; ; T.B.S. ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; AP - address of the 1st function dependent QIO parameter ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- DK_ALIGN_COUNT: ; FDT routine BBS #0,4(AP),10$ ; IS BYTE COUNT EVEN RSB ; YES-Return SIGNALLING OK 10$: MOVL #SS$_IVBUFLEN,R0 ; NO-SIGNAL ERROR CLRL R1 JMP G^EXE$ABORTIO ; AND ABORT THIS I/O .PAGE .SBTTL DK_START, Start I/O routine ;++ ; DK_START - Start a transmit, receive, or set mode operation ; ; Functional description: ; ; T.B.S. ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; R0 - 1st longword of I/O status: contains status code and ; number of bytes transferred ; R1 - 2nd longword of I/O status: device-dependent ; ; The routine must preserve all registers except R0-R2 and R4. ; ;-- DK_START: ; Process an I/O packet MOVW IRP$W_FUNC(R3),UCB$W_FUNC(R5) ; SAVE FUNCTION AND MODIFIERS MOVW IRP$W_FUNC(R3),UCB$W_DK_FUNC(R5); WORKING COPY DATA_CHECK_LOOP_BACK: MOVB UCB$B_ERTMAX(R5),UCB$B_ERTCNT(R5);INIT ERROR RETRY RETRY_LOOP: ; ALL RETRYS LOOP BACK HERE MOVL IRP$L_MEDIA(R3),R0 ; SAVE PARAMETER LONG WORD CLRB UCB$B_DK_IND(R5) ; CLEAR SOFTWARE INDICATORS ; ; MOVE SOFTWARE DEPENDENT PARAMETERS TO UCB ; 10$: EXTZV #IRP$V_FCODE,#IRP$S_FCODE,UCB$W_DK_FUNC(R5),R1 ; GET FUNCTION MOVL R0,UCB$L_DK_LOC(R5) ; SAVE CYLINDER MOVB R1,UCB$B_FEX(R5) ; SAVE FUNCTION ; CLEAR FLAGS-ECC CORRECTION AND DIAG BUFFER BICW #UCB$M_ECC!UCB$M_DIAGBUF,UCB$W_DEVSTS(R5) ; CHECK TO SEE IF DIAG BUFFER SUPPLIED-IF NOT DO DISPATCH BBC #IRP$V_DIAGBUF,IRP$W_STS(R3),FDISPATCH ; SET DIAG BUFF FLAG BISW #UCB$M_DIAGBUF,UCB$W_DEVSTS(R5) .PAGE .SUBTITLE QIO FUNCTION DISPATCH ; ; CENTRAL DISPATCH FUNCTION ; FDISPATCH: MOVL UCB$L_IRP(R5),R3 ; RETRIEVE IOPACKET ADDRESS BBS #IRP$V_PHYSIO,IRP$W_STS(R3),10$ ; PHYSICAL I/O ALWAYS OK BBS #UCB$V_VALID,UCB$W_STS(R5),10$ ; VOL. VALID ? MOVZWL #SS$_VOLINV,R0 ; VOLUMN INVALID CLRL R1 ; SET ERROR VALUE REQCOM ; AND EXIT FINISHING I/O ; ; UNIT IS VALID OR PHYSICAL I/O ; 10$: CLRB UCB$W_OFFSET+1(R5) ; CLEAR OFFSET FLAG(NOT USED) ; RK05 DOES NOT RETRY I/O ; OFFSET FROM CYLINDER CENTER ; LINE MOVZBL UCB$B_FEX(R5),R2 ; GET FUNCTION ; ; MACRO TO CHECK FOR VALID FUNCTION AND DISPATCH TO PROPER ROUTINE ; .MACRO CHK FUNC,?AAA CMPB #IO$_'FUNC',R2 ; IS IT THIS FUNCTION BNEQ AAA ; NO-JUMP TO NEXT CHECK BRW DK_'FUNC' ; GOTO PROPER ROUTINE AAA: .ENDM CHK CHK READPBLK ; IO$_READPBLK CHK WRITEPBLK ; IO$_WRITEPBLK CHK AVAILABLE ; IO$_AVAILABLE CHK PACKACK ; IO$_PACKACK CHK UNLOAD ; IO$_UNLOAD CHK DRVCLR ; IO$_DRVCLR CHK WRITEHEAD ; IO$_WRITEHEAD CHK RECAL ; IO$_REACAL CHK SEEK ; IO$_SEEK CHK WRITECHECK ; IO$_WRITECHECK CHK READHEAD ; IO$_READHEAD CHK NOP ; IO$_NOP ; ; ERROR-IMPROPER FUNCTION ; MOVL #SS$_ILLIOFUNC,R0 ;ILLEGAL I/O FUNCTION CLRL R1 REQCOM ;DONE-TERMINATE I/O .PAGE .SUBTITLE FUNCTION SPECIFIC NON TRANSFER START I/O ROUTINES DK_AVAILABLE: ; DRIVE NOT VALID(NO DISK) DK_UNLOAD: ; UNLOAD DRIVE BICW #UCB$M_VALID,UCB$W_STS(R5) ; CLEAR PACK VALID BIT BRW ALT_NORMAL ; TAKE ALT EXIT ROUTINE DK_PACKACK: ; DRIVE VALID(HAS DISK MOUNTED) REQPCHAN ; GET THE CONTROLLER CHANNEL MOVW #1,DK_CS(R4) ; CLEAR THE CONTROLLER TIMEWAIT #330,#DK_CS_M_RDY,DK_CS(R4),W ; WAIT FOR READY OR3.3MS BISW #UCB$M_VALID,UCB$W_STS(R5) ; SET THE DRIVE VALID BIT BRW NORMAL ; TAKE EXIT ROUTE DK_NOP: ; DO-NOTHING BRW ALT_NORMAL ; TAKE ALTERNATE EXIT DK_DRVCLR: ; CLEAR THE DRIVE REQPCHAN ; GET OK TO USE CONTROLLER CLRL R1 ; CLEAR SCRATCH REG FOR DISK ; ADDRESS INSV UCB$W_UNIT(R5),#DK_DA_V_DR_SEL,#DK_DA_S_DR_SEL,R1 ; GET UNIT TO CLEAR DSBINT ; DISABLE ALL INTERUPTS BBC #UCB$V_POWER,UCB$W_STS(R5),1$ ; CHECK FOR POWER FAIL BRW POWER_FAIL ; TAKE POWER FAIL ROUTINE 1$: MOVW R1,DK_DA(R4) ; LOAD DRIVE TO CLEAR MOVW #,DK_CS(R4); CLEAR THE DRIVE 3$: WFIKPCH 2$,#10 ; WAIT FOR INTERUPT OR TIMEOUT IOFORK ; DROP TO FORK LEVEL BITW #,DK_CS(R4) ; CHECK FOR ERROR BEQL 5$ ; NO ERROR BRW RETRY ; GO TO RETRY ROUTINE 2$: BRW TIME_OUT ; GOTO TIMEOUT ROUTINE 5$: BRW NORMAL ; TAKE NORMAL EXIT .PAGE DK_RECAL: ; RECALIBRATE THE DRIVE CLRL UCB$L_DK_LOC(R5) ; SET SEEK CYLINDER ZERO ; FALL THROUGH INTO SEEK ; ROUTINE DK_SEEK: ; SEEK SPECIFIED CYLINDER REQPCHAN ; REQUEST USE OF CONTROLLER CLRL R1 ; CLEAR SCRATCH REGISTER ; BUILD THE DISK ADDRESS VALUE TO BE PUT INTO THE DISK ; ADDRESS REGISTER INSV UCB$L_DK_LOC(R5),#DK_DA_V_CYL_ADD,#DK_DA_S_CYL_ADD,R1 ; EXTRACT THE CYLINDER NUMBER ; TO SEEK INSV UCB$W_UNIT(R5),#DK_DA_V_DR_SEL,#DK_DA_S_DR_SEL,R1 ; EXTRACT THE UNIT NUMBER DSBINT ; DISABLE ALL INTERUPTS BBC #UCB$V_POWER,UCB$W_STS(R5),1$ ; HAS A POWER FAIL HAPPENED BRW POWER_FAIL ; GO TO POWER FAIL RECOVERY 1$: MOVW R1,DK_DA(R4) ; LOAD DISK ADDRESS REGISTER MOVW #,DK_CS(R4); LOAD CSR WITH SEEK CMD 3$: WFIKPCH 2$,#10 ; WAIT FOR INTERUPT OR TIMEOUT ; RK05 WILL GIVE AN IMEDIATE ; INTERUPT HERE ON SEEK COMMAND IOFORK ; INTERUPT OCCOURED ; DROP TO FORK LEVEL DSBINT ; DISABLE INTERUPTS BBC #UCB$V_POWER,UCB$W_STS(R5),4$ ; CHECK FOR POWER FAIL ENBINT ; REENABLE INTERUPTS BRW POWER_FAIL ; GOTO POWER FAIL RECOVERY 4$: WFIKPCH 2$,#10 ; WAIT FOR SEEK COMPLETE ; OR TIMEOUT IOFORK ; DROP TO FORK LEVEL BITW #,UCB$W_DK_CS(R5); CHECK FOR ERROR BEQL 5$ ; NONE-CONTINUE BRW RETRY ; GOTO RETRY ROUTINE 5$: BRW NORMAL ; TAKE NORMAL EXIT 2$: BRW TIME_OUT ; TAKE TIMEOUT EXIT .PAGE .SUBTITLE START I/O TRANSFER ROUTINES DK_WRITECHECK: ; COMMAND IS A WRITE CHECK EXTZV #IRP$V_FCODE,#IRP$S_FCODE,UCB$W_FUNC(R5),R1 ; CHECK TO SEE WHAT FUNCTION ; WAS BEFORE CMPB #IO$_WRITECHECK,R1 ; WAS IT WRITE CHECK ? BEQL 1$ ; YES CMPB #IO$_WRITEPBLK,R1 ; WAS IT WRITE WITH DATA CHECK BEQL 1$ ; YES MOVL #,UCB$W_DK_CS(R5) ; WAS READ WITH DATA CHECK ; NOW DO READCHECK BRB TRANSFR ; GOTO COMMON XFER ROUTINE 1$: MOVL #,UCB$W_DK_CS(R5) ; LOAD COMMAND TO BE EXECUTED ; INTO UCB COPY OF CSR BRB TRANSFR ; GO TO COMMON XFER ROUTINE DK_WRITEHEAD: ; WRITE HEADER COMMAND (FORMAT) MOVL #,UCB$W_DK_CS(R5) ; LOAD COMMAND TO BE EXECUTED ; INTO UCB COPY OF CSR BRB TRANSFR ; GO TO COMMON XFER ROUTINE DK_READHEAD: ; READ DISK FORMAT INFORMATION MOVL #,UCB$W_DK_CS(R5) ; LOAD COMMAND TO BE EXECUTED ; INTO UCB COPY OF CSR BRB TRANSFR ; GO TO COMMON XFER ROUTINE DK_WRITEPBLK: ; WRITE TO DISK BISB #UCB$M_DK_IND_DATACK,UCB$B_DK_IND(R5) ; SAY DATA CHECK ALLOWED MOVL #,UCB$W_DK_CS(R5) ; LOAD COMMAND TO BE EXECUTED ; INTO UCB COPY OF CSR BRB TRANSFR ; GO TO COMMON XFER ROUTINE DK_READPBLK: ; READ FROM DISK BISB #UCB$M_DK_IND_DATACK,UCB$B_DK_IND(R5) ; SAY DATA CHECK ALLOWED MOVL #,UCB$W_DK_CS(R5) ; LOAD COMMAND TO BE EXECUTED ; INTO UCB COPY OF CSR BRB TRANSFR ; GO TO COMMON XFER ROUTINE .PAGE .SUBTITLE COMMON DISK TRANSFER ROUTINE-SET UP IO OPERATION & WAIT TRANSFR: REQPCHAN ; GET USE OF CONTROLLER REQDPR ; REQUEST UNIBUSS ADAPTER ; DATA PATH REQMPR ; REQUEST A SET OF UNIBUSS ; MAP REGISTERS LOADUBA ; LOAD UNIBUSS ADAPTER ; MAP REGISTERS MOVZWL UCB$W_BCNT(R5),R0 ; GET BYTE COUNT DIVL2 #2,R0 ; CONVERT TO WORD COUNT MNEGW R0,UCB$W_DK_WC(R5) ; NEGATE AND STORE IN UCB ; BUILD THE DISK ADDRESS CLRL R1 ; CLEAR SCRATCH REGISTER INSV UCB$L_DK_LOC(R5),#DK_DA_V_SC,#DK_DA_S_SC,R1; LOAD SECTOR TSTB UCB$L_DK_LOC+1(R5) ; WHAT SURFACE BEQL 20$ ; SURFACE 0 BISW #DK_DA_M_SUR,R1 ; SURFACE 1 20$: INSV UCB$L_DK_LOC+2(R5),#DK_DA_V_CYL_ADD,#DK_DA_S_CYL_ADD,R1 ; INSERT CYLINDER ADDRESS INSV UCB$W_UNIT(R5),#DK_DA_V_DR_SEL,#DK_DA_S_DR_SEL,R1 ; INSERT UNIT NUMBER MOVW R1,UCB$W_DK_DA(R5) ; LOAD INTO UCB ; BUILD MEMORY ADDRESS MOVZWL UCB$W_BOFF(R5),R0 ; GET BYTE OFFSET IN PAGE MOVL UCB$L_CRB(R5),R1 ; GET ADDR OF CRB INSV CRB$L_INTD+VEC$W_MAPREG(R1),#9,#7,R0 ; INSERT HIGH 7 BITS OF ADDR MOVW R0,UCB$W_DK_BA(R5) ; SAVE ADDRESS EXTZV #7,#2,CRB$L_INTD+VEC$W_MAPREG(R1),R0 ; GET MEMORY EXTENTION BITS ASHL #4,R0,R0 ; SHIFT TO FIT INTO CSR BISW R0,UCB$W_DK_CS(R5) ; SET MEM EXT INTO CSR DSBINT ; DISABLE INTERUPTS BBC #UCB$V_POWER,UCB$W_STS(R5),10$ ; CHECK FOR POWER FAIL ENBINT ; REENABLE INTERUPTS (FOUND IT) SETIPL UCB$B_FIPL(R5) ; SET PRIORITY TO FORK LEVEL RELDPR ; RELEASE DATA PATH RELMPR ; RELEASE MAP REGISTERS BRW POWER_FAIL ; GOTO POWER FAIL ROUTINE 10$: MOVW UCB$W_DK_BA(R5),DK_BA(R4) ; LOAD CONTROLLER BUSS ADDR MOVW UCB$W_DK_WC(R5),DK_WC(R4) ; LOAD CONTROLLER WORD COUNT MOVW UCB$W_DK_DA(R5),DK_DA(R4) ; LOAD CONTROLLER DISK ADDRESS MOVW UCB$W_DK_CS(R5),DK_CS(R4) ; LOAD CONTROLLER CSR WFIKPCH 60$,#10 ; WAIT FOR INTERUPT OR TIMEOUT .PAGE .SUBTITLE IO COMPLETE-POST XFER PROCESSING IOFORK ; DROP TO FORK LEVEL PURDPR ; PURGE DATA PATH BLBS R0,30$ ; CHECK FOR PURGE ERROR BISB #UCB$M_DK_IND_PURGE,UCB$B_DK_IND(R5) ; SET PURGE ERROR FLAG BRB 40$ ; GO TO ERROR CODE 30$: BITW #,UCB$W_DK_CS(R5) ; CHECK FOR TRANSFER ERROR BNEQ 40$ ; ERROR FOUND-ERROR CODE BBC #UCB$M_DIAGBUF,UCB$W_STS(R5),50$; WAS DIAG. BUFFER SUPPLIED ; YES-NO ERROR-BUT TAKE ; ERROR CODE ROUTE ANYWAY 40$: MOVL UCB$L_CRB(R5),R0 ; GET CRB ADDRESS EXTZV #VEC$V_DATAPATH,#VEC$S_DATAPATH,CRB$L_INTD+VEC$B_DATAPATH(R0),- UCB$W_DK_DPN(R5) ; EXTRACT AND SAVE DATA PATH # MOVL R1,UCB$L_DK_DPR(R5) ; SAVE DATA PATH REGISTER VALUE EXTZV #9,#7,UCB$W_DK_BA(R5),R0 ; GET LOW BITS OF FINAL MAP ; REGISTER BUFFER INSV UCB$W_DK_CS+1(R5),#7,#2,R0 ; INSERT HIGH BITS OF FINAL MAP ; REGISTER BUFFER NUMBER CMPW #495,R0 ; IS THIS A LEGAL NUMBER ? BGEQ 41$ ; YES MOVZWL #495,R0 ; NO-TRUNCATE TO HIGHEST LEGAL# 41$: MOVL (R2)[R0],UCB$L_DK_FMPR(R5) ; SAVE FINAL MAP REGISTER ; CONTENTS CLRL UCB$L_DK_PMPR(R5) ; CLEAR PREV. MAP REG VALUE DECL R0 ; CALC PREV MAP REG NO. CMPV #VEC$V_MAPREG,#VEC$S_MAPREG,CRB$L_INTD+VEC$W_MAPREG(R3),R0 ; ANY PREVIOUS MAP REGISTERS? BGTR 50$ ; NO MOVL (R2)[R0],UCB$L_DK_PMPR(R5) ; SAVE PREVIOUS MAP REGISTER 50$: MULW3 #2,UCB$W_DK_WC(R5),UCB$W_BCR(R5); CVT WORD TO BYTE COUNT BITW #,UCB$W_DK_CS(R5) ; WAS IT DEVICE ERROR BNEQ 70$ ; YES BITB #UCB$M_DK_IND_PURGE,UCB$B_DK_IND(R5) ; PURGE ERROR ? BNEQ 70$ ; YES CMPB #IO$_READHEAD,UCB$B_CEX(R5) ; WAS IT READ HEADER BNEQ XFR_NORMAL ; NO PUSHL UCB$L_SVAPTE(R5) ; SAVE ADDR OP PTE PUSHL UCB$L_SVAPTE+4(R5) MOVAB UCB$W_DK_DB(R5),R1 ; SAVE ADDR OF INTERNAL BUFF MOVL #6,R2 ; SET NUMBER OF BYTES TO MOVE CMPW R2,UCB$W_BCNT(R5) ; ROOM FOR FULL HEADER BLSSU 51$ ; YES MOVZWL UCB$W_BCNT(R5),R2 ; SET LENGTH OF PARTIAL HEADER 51$: SUBW3 UCB$W_BCNT(R5),R2,UCB$W_BCR(R5) ; CALC XFR BYTE COUNT JSB G^IOC$MOVTOUSER ; MOVE HEADER TO USER BUFFER POPL UCB$L_SVAPTE+4(R5) ; RESTORE ADDR OF PTE POPL UCB$L_SVAPTE(R5) BRB XFR_NORMAL ; TAKE NORMAL EXIT 60$: SETIPL UCB$B_FIPL(R5) ; SET PROPER IPL RELDPR ; RELEASE DATA PATH RELMPR ; RELEASE MAP REG'S BRW TIME_OUT_2 ; TAKE TIME OUT #2 PATH 70$: SETIPL UCB$B_FIPL(R5) ; SET IPL RELDPR ; RELEASE DATA PATHS RELMPR ; RELEASE MAP REGS BRW RETRY ; SEE IF CAN RETRY .PAGE .SUBTITLE OPERATION COMPLETED NORMAL EXIT ROUTINES ; ENTRY POINT FOR TRANSFER OPERATIONS HAVEING CONTROLLER XFR_NORMAL: SETIPL UCB$B_FIPL(R5) ; SET PROPER IPL RELDPR ; RELEASE DATA PATH RELMPR ; RELEASE MAP REG'S CLRW DK_CS(R4) ; CLEAR THE CONTROLLER RELCHAN ; RELEASE CONTROLLER BBC #IO$V_DATACHECK,UCB$W_DK_FUNC(R5),1$ ; WAS A DATA CHECK REQUESTED BITB #,UCB$B_DK_IND(R5) ; SEE IF DATA CHECK ALLOWED BEQL 1$ ; NO MOVL UCB$L_IRP(R5),R3 ; RETRIEVE IOPACKET ADDRESS MOVW #IO$_WRITECHECK,UCB$W_DK_FUNC(R5) ; SET NEW FUNC AS DATA CHECK BRW DATA_CHECK_LOOP_BACK ; GO DO DATA CHECK 1$: MOVW UCB$W_BCNT(R5),R0 ; LOAD BYTE COUNT IN R0 ASHL #16,R0,R0 ; SHIFT TO HIGH WORD MOVW #SS$_NORMAL,R0 ; LOAD NORMAL COMPLETION FLAG CLRL R1 ; CLEAR R1 FOR IOSTAT REQCOM ; I/O DONE ; ENTRY POINT FOR NON-TRANSFER OPERATIONS HAVEING CONTROLLER NORMAL: CLRW DK_CS(R4) ; CLEAR CSR RELCHAN ; RELEASE CONTROLLER ; ENTRY POINT FOR NON TRANSFER OPERATIONS NOT HAVEING CONTROLLER ALT_NORMAL: MOVZWL #SS$_NORMAL,R0 ; SET NORMAL OK EXIT CLRL R1 ; R1 FOR IOSTAT MOVL UCB$L_CRB(R5),R4 ; GET ADDR OF CRB MOVL (R4),R4 ; GET ADDR OF IDB MOVL IDB$L_CSR(R4),R4 ; GET ADDR OF CSR REQCOM ; OPERATION COMPLETE .PAGE .SUBTITLE ERROR RETRY HANDLEING ; ; ERROR-SEE IF RETRYABLE ; RETRY: SETIPL UCB$B_FIPL(R5) ; SET IPL LEVEL TO FORK PUSHR #^M ; SAVE CRITICAL REGISTERS JSB G^ERL$DEVICERR ; LOG THE ERROR POPR #^M ; RESTORE REGISTERS BBS #IO$V_INHRETRY,UCB$W_FUNC(R5),FATALERR ; IF RETRY INHIBITED-ERROR ; IS FATAL DECB UCB$B_ERTCNT(R5) ; DECREMENT ERROR RETRY COUNT BLEQ FATALERR ; IF USED UP-FATAL ERROR BITW #,UCB$W_DK_ER(R5) ; CHECK ERROR REG FOR FATAL ; ERRORS (NOT WORTH TRYING ; TO DO THEM AGAIN) ; NON EXISTENT DRIVE ; NON EXISTENT CYLINDER ; NON EXISTENT SECTOR BNEQ FATALERR ; FATAL ERROR BITW #,UCB$W_DK_DS(R5) ; FURTHER HARDWARE CHECK FOR ; FATAL TYPE ERRORS ; DRIVE UNSAFE ; DRIVE POWER LOW BNEQ FATALERR ; FATAL ERROR MOVL UCB$L_IRP(R5),R3 ; GET ADDR OF I/O REQ. PACKET MOVW #1,DK_CS(R4) ; RESET CONTROLLER TIMEWAIT #330,#DK_CS_M_RDY,DK_CS(R4),W; WAIT FOR READY OR3.3MS CLRW DK_ER(R4) ; CLEAR CSR RELCHAN ; RELEASE CONTROLLER BRW RETRY_LOOP ; RETRY OPERATION .PAGE .SUBTITLE FATAL ERROR EXIT ; ERROR IS FATAL FATALERR: ; MACRO TO LOAD PROPER ERROR TOKEN INTO R0 ; BASED ON THE ERROR TYPE .MACRO CKER BIT,WORD,ERROR,?AAAA BITW #BIT,UCB$W_'WORD'(R5) BEQL AAAA MOVZWL #ERROR,R0 BRW FOUND_ERROR AAAA: .ENDM BITB #UCB$M_DK_IND_PURGE,UCB$B_DK_IND(R5) ; IS IT PURGE ERROR BEQL 1$ ; NO MOVZWL #SS$_CTRLERR,R0 ; SAY ITS A CONTROLLER ERROR BRW FOUND_ERROR ; TAKE EXIT 1$: CKER DK_DS_M_DRU,DK_DS,SS$_UNSAFE ; DRIVE UNSAFE CKER DK_DS_M_DPL,DK_DS,SS$_UNSAFE ; DRIVE POWER LOW=>DRIVE UNSAFE CKER DK_DS_M_SIN,DK_DS,SS$_DRVERR ; SEEK INCOMPLETE=>DRIVE ERROR CKER DK_ER_M_OVR,DK_ER,SS$_DATAOVERUN; DATA OVERFLOW CKER DK_ER_M_WLO,DK_ER,SS$_WRITLCK ; WRITE LOCK CKER DK_ER_M_SKE,DK_ER,SS$_DRVERR ; SEEK ERROR => DRIVE ERROR CKER DK_ER_M_PGE,DK_ER,SS$_DEVCMDERR ; PROGR ERROR=>DEV PRG ERROR CKER DK_ER_M_NXM,DK_ER,SS$_IVADDR ; NON EX. MEM=>INVALID ADDR CKER DK_ER_M_DLT,DK_ER,SS$_DRVERR ; DATA LATE=>DRIVE ERROR CKER DK_ER_M_TE,DK_ER,SS$_DRVERR ; TMEING ERROR=> DRIVE ERROR CKER DK_ER_M_NXD,DK_ER,SS$_NONEXDRV ; NON EXISTENT DRIVE CKER ,DK_ER,SS$_IVADDR ; NON EXISTENT CYLENDER, ; NON EXISTENT SECTOR=> ; INVALID ADDRESS CKER DK_ER_M_CSE,DK_ER,SS$_PARITY ; CHECKSUM ERROR=>PARITY CKER DK_ER_M_WCE,DK_ER,SS$_DATACHECK ; WRITE CHECK ERROR=>DATA CHECK MOVZWL #SS$_CTRLERR,R0 ; UNKNOWN=>CONTROLER ERROR FOUND_ERROR: ; ERROR TOKEN LOADED IN R0-FINSH FATAL ERROR CLEANUP CLRL R1 ; R1 FOR IOSB PUSHR #^M ; SAVE R0R1 MOVW #1,DK_CS(R4) ; CLEAR THE CONTROLLER TIMEWAIT #330,#DK_CS_M_RDY,DK_CS(R4),W; WAIT FOR READY OR3.3MS CLRW DK_ER(R4) ; CLEAR THE CSR RELCHAN ; RELEASE CONTROLLER POPR #^M ; RESTORE R0/R1 (IOSB) REQCOM ; I/O FATAL ERROR EXIT .PAGE .SUBTITLE POWER FAIL HANDLEING ROUTINES POWER_FAIL: MOVL UCB$L_IRP(R5),R3 ; LOAD THE I/O REQUEST PACKET ; ADDRESS MOVQ IRP$L_SVAPTE(R3),UCB$L_SVAPTE(R5);RESTORE TRANSFER PARAMETERS BICW #UCB$M_POWER,UCB$W_STS(R5) ; CLEAR POWER FAIL BIT MOVW #1,DK_CS(R4) ; RESET THE CONTROLLER TIMEWAIT #330,#DK_CS_M_RDY,DK_CS(R4),W; WAIT FOR READY OR3.3MS CLRW DK_ER(R4) ; CLEAR THE CSR RELCHAN ; RELEASE THE CONTROLLER DECB UCB$B_ERTCNT(R5) ; POWER FAIL IS AN ERROR BLEQ 1$ ; DEC ERROR COUNT-BR IF LAST BRW RETRY_LOOP ; TRY TRANSFER AGAIN 1$: MOVZWL #SS$_POWERFAIL,R0 ; LOAD POWER FAIL TOKEN CLRL R1 ; CLEAR R1 FOR IOSB REQCOM ; TAKE POWER FAIL FATAL EXIT .PAGE .SUBTITLE TIME OUT HANDLEING ROUTINES ; ENTRY POINT FOR INTERUPT LEVEL IPL TIMEPOUT TIME_OUT: SETIPL UCB$B_FIPL(R5) ; DROP IPL TO FORK LEVEL ; ENTRY POINT FOR FORK LEVEL TIMEOUTS TIME_OUT_2: MOVL UCB$L_IRP(R5),R3 ; GET ADR OF I/O REQUEST PACKET PUSHR #^M ; SAVE KEY POINTERS JSB G^ERL$DEVICTMO ; LOG THE TIMEOUT ERROR POPR #^M ; RESTORE POINTERS MOVW #1,DK_CS(R4) ; REINIT THE CONTROLLER TIMEWAIT #330,#DK_CS_M_RDY,DK_CS(R4),W; WAIT FOR READY OR3.3MS CLRW DK_ER(R4) ; CLEAR THE CSR RELCHAN ; RELEASE THE CONTROLLER DECB UCB$B_ERTCNT(R5) ; DEC THE ERROR COUNTER BLEQ 1$ ; LAST TRY FAILED BRW RETRY_LOOP ; GO RETRY 1$: MOVZWL #SS$_TIMEOUT,R0 ; SET TIMEOUT TOKEN CLRL R1 ; SET R1 FOR IOSB REQCOM ; TIMEOUT FATAL ERROR .PAGE .SBTTL DK_INTERRUPT, Interrupt service routine ;++ ; DK_INTERRUPT, Analyzes interrupts, processes solicited interrupts ; ; Functional description: ; ; The code assumes ; ; that the driver's start I/O routine acquired the ; controller's channel with a REQPCHANL macro call, and ; then invoked the WFIKPCH macro to keep the channel ; while waiting for an interrupt. ; ; Inputs: ; ; 0(SP) - pointer to the address of the IDB (interrupt data ; block) ; 4(SP) - saved R0 ; 8(SP) - saved R1 ; 12(SP) - saved R2 ; 16(SP) - saved R3 ; 20(SP) - saved R4 ; 24(SP) - saved R5 ; 28(SP) - saved PSL (program status longword) ; 32(SP) - saved PC ; ; The IDB contains the CSR address and the UCB address. ; ; Outputs: ; ; The routine must preserve all registers except R0-R5. ; ;-- .PAGE DK_INTERRUPT: ; Service device interrupt MOVL @(SP)+,R4 ; Get address of IDB and remove ; pointer from stack. MOVL IDB$L_OWNER(R4),R5 ; Get address of device owner's ; UCB. MOVL IDB$L_CSR(R4),R4 ; Get address of device's CSR. BBCC #UCB$V_INT,- ; If device does not expect UCB$W_STS(R5),- ; interrupt, dismiss it. UNSOL_INTERRUPT ; ; This is a solicited interrupt. Save ; the contents of the device registers in the UCB. ; MOVW DK_CS(R4),- ; Otherwise, save all device UCB$W_DK_CS(R5) ; registers. First the CSR. MOVW DK_WC(R4),- ; Save the word count register. UCB$W_DK_WC(R5) MOVW DK_BA(R4),- ; Save the buffer address UCB$W_DK_BA(R5) ; register. MOVW DK_DS(R4),- ; Save the data buffer register. UCB$W_DK_DS(R5) MOVW DK_ER(R4),- ; SAVE THE ERROR REGISTER UCB$W_DK_ER(R5) MOVW DK_DA(R4),- ; SAVE THE DISK ADDR REGISTER UCB$W_DK_DA(R5) ; ; Restore control to the main driver. ; RESTORE_DRIVER: ; Jump to main driver code. MOVQ UCB$L_FR3(R5),R3 ; Restore driver's R3 (use a ; MOVQ to restore R3-R4). JSB @UCB$L_FPC(R5) ; Call driver at interrupt ; wait address. ; ; Dismiss the interrupt. ; UNSOL_INTERRUPT: ; Dismiss unsolicited interrupt. POPR #^M ; Restore R0-R5 REI ; Return from interrupt. .PAGE .SBTTL DK_REG_DUMP, Device register dump routine ;++ ; DK_REG_DUMP, Dumps the contents of device registers to a buffer ; ; Functional description: ; ; Writes the number of device registers, and their current ; contents into a diagnostic or error buffer. ; ; Inputs: ; ; R0 - address of the output buffer ; R4 - address of the CSR (controller status register) ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; The routine must preserve all registers except R1-R3. ; ; The output buffer contains the current contents of the device ; registers. R0 contains the address of the next empty longword in ; the output buffer. ; ;-- DK_REG_DUMP: ; Dump device registers MOVZBL #14,(R0)+ ; Store device register count. MOVZWL UCB$W_DK_DS(R5),(R0)+ ; COPY TO OUTPUT BUFFER DRIVE STATUS MOVZWL UCB$W_DK_ER(R5),(R0)+ ; COPY TO OUTPUT BUFFER ERROR REG MOVZWL UCB$W_DK_CS(R5),(R0)+ ; COPY TO OUTPUT BUFFER CSR MOVZWL UCB$W_DK_WC(R5),(R0)+ ; COPY TO OUTPUT BUFFER WORD COUNT MOVZWL UCB$W_DK_BA(R5),(R0)+ ; COPY TO OUTPUT BUFFER BUFFER ADDR MOVZWL UCB$W_DK_DA(R5),(R0)+ ; COPY TO OUTPUT BUFFER DISK ADDR MOVL UCB$L_DK_DPR(R5),(R0)+ ; COPY TO OUTPUT BUFFER DATA PATH REG MOVL UCB$L_DK_FMPR(R5),(R0)+ ; COPY TO OUTPUT BUFFER FINAL PATH REG MOVL UCB$L_DK_PMPR(R5),(R0)+ ; COPY TO OUTPUT BUFFER PREV MAP REG MOVZWL UCB$W_DK_DB(R5),(R0)+ ; COPY TO OUTPUT BUFFER DATA BUF REG MOVZWL UCB$W_DK_DB+2(R5),(R0)+ MOVZWL UCB$W_DK_DB+4(R5),(R0)+ MOVZBL UCB$B_DK_IND(R5),(R0)+ ; COPY TO OUTPUT BUFFER SOFTWARE IND. MOVZWL UCB$W_DK_FUNC(R5),(R0)+ ; COPY TO OUTPUT BUFFER CURRENT FUNC. RSB ; Return .PAGE .SBTTL DK_CONTROL_INIT, Controller initialization routine ;++ ; DK_CONTROL_INIT, Readies controller for I/O operations ; ; Functional description: ; ; The operating system calls this routine in 3 places: ; ; at system startup ; during driver loading and reloading ; during recovery from a power failure ; ; Inputs: ; ; R4 - address of the CSR (controller status register) ; R5 - address of the IDB (interrupt data block) ; R6 - address of the DDB (device data block) ; R8 - address of the CRB (channel request block) ; ; Outputs: ; ; The routine must preserve all registers except R0-R3. ; ;-- DK_CONTROL_INIT: ; Initialize controller MOVW #1,DK_CS(R4) ; INIT THE CONTROLLER TIMEWAIT #330,#DK_CS_M_RDY,DK_CS(R4),W; WAIT FOR READY OR3.3MS MOVL #SS$_NORMAL,R0 ; LOAD OK FLAG RSB ; Return .PAGE .SBTTL DK_UNIT_INIT, Unit initialization routine ;++ ; DK_UNIT_INIT, Readies unit for I/O operations ; ; Functional description: ; ; The operating system calls this routine after calling the ; controller initialization routine: ; ; at system startup ; during driver loading ; during recovery from a power failure ; ; Inputs: ; ; R4 - address of the CSR (controller status register) ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; The routine must preserve all registers except R0-R3. ; ;-- DK_UNIT_INIT: ; Initialize unit BISW #UCB$M_ONLINE, - UCB$W_STS(R5) ; Set unit online CVTWL UCB$W_UNIT,R0 ; GET UNIT NUMBER CLRL R1 ; CLEAR SCRATCH REG TO BUILD DISK ADDR INSV R0,#13,#3,R1 ; BUILD UNIT NUMBER MOVW R1,DK_DA(R4) ; LOAD DISK ADDR MOVW #<^O14+1>,DK_CS(R4) ; INIT SELECTED DRIVE TIMEWAIT #330,#DK_CS_M_RDY,DK_CS(R4),W; WAIT FOR READY OR3.3MS MOVL #SS$_NORMAL,R0 ; LOAD OK FLAG RSB ; Return .page .SUBTITLE DK_AUTO_UNIT, WHAT DRIVES ARE AVAILABLE AT AUTOGEN ; ; DK_AUTO_UNIT ; ; Functional description ; ; Check the specified unit number to see if it is available for use. ; ; Inputs: ; ; R0-R2 Scratch ; R3 Address of IDB (if 0 then no IDB) ; R4 CSR address ; R5 Unit number to test for ; R6 Base address of UNIBUSS adapter I/O space ; R7 Address of configuration control block ; R8 Address of UNIBUSS adapter control block ; DK_AUTO_UNIT: DSBINT ; DISABLE INTERUPTS MOVL #SS$_NORMAL,R0 ; ASSUME OK CLRL R1 ; SET UP FOR BUILD ; DISK ADDR COMMAND INSV R5,#13,#3,R1 ; SET UP R1 UNIT NO CLRW DK_CS(R4) ; CLEAR CSR MOVW R1,DK_DA(R4) ; SELECT DRIVE MOVW #<^O14+1>,DK_CS(R4) ; RESET DRIVE TIMEWAIT #100,#DK_CS_M_RDY,DK_CS(R4),W; WAIT FOR READY OR 1MS BITW #DK_ER_M_NXD,DK_ER(R4) ; DOES DISK EXIST BEQL 1$ ; YES-SKIP ERROR HANDLEING MOVL #SS$_NONEXDRV,R0 ; NO SUCH DISK MOVW #1,DK_CS(R4) ; CLEAR THE ERROR TIMEWAIT #350,#DK_CS_M_RDY,DK_CS(R4),W; WAIT FOR READY OR 3.5 MS 1$: ENBINT ; REENABLE INTERUPTS RSB ; RETURN .PAGE .SBTTL DK_END, End of driver ;++ ; Label that marks the end of the driver ;-- DK_END: ; Last location in driver .END