From:	MERC::"uunet!TGV.COM!mahan" 12-NOV-1992 02:27:14.34
To:	galaxy::gleeve
CC:	
Subj:	VDDRIVER (Alpha Port of VDDRIVER) 3/3

-+-+-+-+-+-+-+-+ START OF PART 3 -+-+-+-+-+-+-+-+
X; I/O completion processing, we'll replace the original function
X; from here back in the IRP. This will be saved/restored along with
X; ucb$ppid (irp$l_pid field) and so synchronization will be detected
X; with ucb$ppid usage.
X; define extra fork blks to try to avoid double forking possibilities
X$def`09ucb$l_vd_host_descr`09.blkl`092`09; char string descr
X$def`09ucb$vdcontfil`09.blkb`0980
X;
X$DEF`09UCB$K_VD_LEN`09.BLKW`091`09;LENGTH OF UCB
X;UCB$K_VD_LEN=.`09`09`09`09;LENGTH OF UCB
X
X`09$DEFEND`09UCB`09`09`09;END OF UCB DEFINITONS
X`20
X`09.SBTTL`09STANDARD TABLES
X
X`09DRIVER_DATA`09`09`09; make sure we are in driver data psect (PLM)
X
X;`20
X; DRIVER PROLOGUE TABLE
X;`20
X; `09THE DPT DESCRIBES DRIVER PARAMETERS AND I/O DATABASE FIELDS
X; `09THAT ARE TO BE INITIALIZED DURING DRIVER LOADING AND RELOADING
X;`20
XVD$DPT::
X`09DPTAB`09-`09`09`09;DPT CREATION MACRO
X`09`09END=VD_END,-`09`09;END OF DRIVER LABEL
X`09`09ADAPTER=NULL,-`09`09;ADAPTER TYPE = NONE (VIRTUAL)
X`09`09FLAGS=<DPT$M_SMPMOD!DPT$M_NOUNLOAD>, -`09;SET TO USE SMP
X`09`09DEFUNITS=32,-`09`09;UNITS 0 THRU 1 thru 31
X`09`09UCBSIZE=UCB$K_VD_LEN,-`09;LENGTH OF UCB
X`09`09MAXUNITS=VD_UNITS,-`09;FOR SANITY...CAN CHANGE
X`09`09NAME=VDDRIVER,-`09`09;DRIVER NAME
X;`09`09PSECT=$$$105_PROLOGUE,-
X`09`09SMP=YES,-
X`09`09STEP=1
X;`09.PSECT`09$$$105_PROLOGUE
X
X`09;alpha drivers need these 2 lines
X`09; alpha preliminary vers. however.
X
X`09DPT_STORE INIT`09`09`09;START CONTROL BLOCK INIT VALUES
X`09DPT_STORE DDB,DDB$L_ACPD,L,<`5EA\F11\>  ;DEFAULT ACP NAME
X`09DPT_STORE DDB,DDB$L_ACPD+3,B,DDB$K_PACK`09;ACP CLASS
X`09.IF`09NDF,VMS$V5
X`09DPT_STORE UCB,UCB$B_FIPL,B,8`09;FORK IPL (VMS V4.X)
X`09.IFF`09;DEFINE FOR VMS V5.X & LATER
X`09DPT_STORE UCB,UCB$B_FLCK,B,SPL$C_IOLOCK8  ;FORK IPL (VMS V5.X + LATER)
X`09.ENDC
X; NOTE THESE CHARACTERISTICS HAVE TO LOOK LIKE THE "REAL" DISK.
X`09DPT_STORE UCB,UCB$L_DEVCHAR,L,-`09;DEVICE CHARACTERISTICS
X`09`09<DEV$M_FOD-`09`09; FILES ORIENTED
X`09`09!DEV$M_DIR-`09`09; DIRECTORY STRUCTURED
X`09`09!DEV$M_AVL-`09`09; AVAILABLE
X`09`09!DEV$M_SHR-`09`09; SHAREABLE
X`09`09!DEV$M_IDV-`09`09; INPUT DEVICE
X`09`09!DEV$M_ODV-`09`09; OUTPUT DEVICE
X`09`09!DEV$M_RND>`09`09; RANDOM ACCESS
X`09DPT_STORE UCB,UCB$L_DEVCHAR2,L,- ;DEVICE CHARACTERISTICS
X`09`09<DEV$M_NNM>`09`09; Prefix name with "node$" (like rp06)
X`09DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_DISK  ;DEVICE CLASS
X`09DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,512  ;DEFAULT BUFFER SIZE
X; FOLLOWING DEFINES OUR DEVICE "PHYSICAL LAYOUT". It's faked here and
X; this structure (64 sectors/trk, 1 trk/cyl, nn cylinders) forces
X; VD: units to be in multiples of 64 blocks. It can be modified as
X; appropriate. However, recall that one has 1 byte for sectors/trk
X; and 16 bits for cylinder number and 1 byte for tracks/cylinder.
X; The current structure allows vd: units as large as 65535*64 blocks
X; (about 4 million blocks, or 2 gigabytes), which is probably big enough
X; for most purposes. The actual size is set up in ASNVD which finds the
X; number of cylinders to "fit" in the container file. For emulating other
X; ODS-2 volumes, the appropriate physical structure should be emulated also.
X; NO logic in this driver depends on this stuff. It just has to be there
X; to keep INIT and friends happy.
X`09DPT_STORE UCB,UCB$B_TRACKS,B,1`09; 1 TRK/CYL
X`09DPT_STORE UCB,UCB$B_SECTORS,B,64  ;NUMBER OF SECTORS PER TRACK
X`09DPT_STORE UCB,UCB$W_CYLINDERS,W,16  ;NUMBER OF CYLINDERS
X; FAKE GEOMETRY TO MAKE TRANSLATION EASIER. HAVE PRIV'D IMAGE LATER
X; RESET THE UCB$W_CYLINDERS TO WHATEVER'S DESIRED. JUST MAKE SURE IT'S
X; A MULTIPLE OF 64 BLOCKS IN SIZE, WHICH OUGHT TO BE GOOD ENOUGH.
X`09DPT_STORE UCB,UCB$B_DIPL,B,21`09;DEVICE IPL
X`09DPT_STORE UCB,UCB$L_ERTMAX,L,10`09;MAX ERROR RETRY COUNT
X`09DPT_STORE UCB,UCB$L_DEVSTS,L,-`09;INHIBIT LOG TO PHYS CONVERSION IN FDT
X`09`09<UCB$M_NOCNVRT>`09`09;...
X;
X; don't mess with LBN; leave alone so it's easier to hack on...
X;
X`09DPT_STORE REINIT`09`09;START CONTROL BLOCK RE-INIT VALUES
X;`09DPT_STORE CRB,CRB$L_INTD+VEC$L_ISR,D,VD_INT  ;INTERRUPT SERVICE ROUTINE
V ADDRESS
X;`09DPT_STORE CRB,CRB$L_INTD+VEC$L_INITIAL,-  ;CONTROLLER INIT ADDRESS
X;`09`09      D,VD_ctrl_INIT`09`09  ;...
X;`09DPT_STORE CRB,CRB$L_INTD+VEC$L_UNITINIT,- ;UNIT INIT ADDRESS
X;`09`09      D,VD_unit_INIT`09`09  ;...
X`09DPT_STORE DDB,DDB$L_DDT,D,VD$DDT`09  ;DDT ADDRESS
X
X`09DPT_STORE END`09`09`09;END OF INITIALIZATION TABLE
X
X;`20
X; DRIVER DISPATCH TABLE
X;`20
X; `09THE DDT LISTS ENTRY POINTS FOR DRIVER SUBROUTINES WHICH ARE
X; `09CALLED BY THE OPERATING SYSTEM.
X;`20
X;VD$DDT:
X`09DDTAB`09-`09`09`09;DDT CREATION MACRO
X`09`09DEVNAM=VD,-`09`09;NAME OF DEVICE
X`09`09START=VD_STARTIO,-`09;START I/O ROUTINE
X`09`09FUNCTB=VD_FUNCTABLE,-`09;FUNCTION DECISION TABLE
X;`09`09CANCEL=0,-`09`09;CANCEL=NO-OP FOR FILES DEVICE
X;`09`09REGDMP=0,-`09;REGISTER DUMP ROUTINE
X;`09`09DIAGBF=0,-  ;BYTES IN DIAG BUFFER
X`09`09ERLGBF=0`09;BYTES IN
X`09`09`09`09;ERRLOG BUFFER
X;`20
X; FUNCTION DECISION TABLE
X;`20
X; `09THE FDT LISTS VALID FUNCTION CODES, SPECIFIES WHICH
X; `09CODES ARE BUFFERED, AND DESIGNATES SUBROUTINES TO
X; `09PERFORM PREPROCESSING FOR PARTICULAR FUNCTIONS.
X;`20
X
XVD_FUNCTABLE:
X`09FUNCTAB`09,-`09`09`09;LIST LEGAL FUNCTIONS
X`09`09<NOP,-`09`09`09; NO-OP
X`09`09FORMAT,-`09`09; We use format to point to file
X`09`09UNLOAD,-`09`09; UNLOAD
X`09`09PACKACK,-`09`09; PACK ACKNOWLEDGE
X`09`09AVAILABLE,-`09`09; AVAILABLE
X`09`09SENSECHAR,-`09`09; SENSE CHARACTERISTICS
X`09`09SETCHAR,-`09`09; SET CHARACTERISTICS
X`09`09SENSEMODE,-`09`09; SENSE MODE
X`09`09SETMODE,-`09`09; SET MODE
X`09`09READLBLK,-`09`09; READ LOGICAL BLOCK
X`09`09WRITELBLK,-`09`09; WRITE LOGICAL BLOCK
X;`09`09READPBLK,-`09`09; READ PHYSICAL BLOCK`20
X;`09`09WRITEPBLK,-`09`09; WRITE PHYSICAL BLOCK
X`09`09READVBLK,-`09`09; READ VIRTUAL BLOCK
X`09`09WRITEVBLK,-`09`09; WRITE VIRTUAL BLOCK
X`09`09ACCESS,-`09`09; ACCESS FILE / FIND DIRECTORY ENTRY
X`09`09ACPCONTROL,-`09`09; ACP CONTROL FUNCTION
X`09`09CREATE,-`09`09; CREATE FILE AND/OR DIRECTORY ENTRY
X`09`09DEACCESS,-`09`09; DEACCESS FILE
X`09`09DELETE,-`09`09; DELETE FILE AND/OR DIRECTORY ENTRY
X`09`09MODIFY,-`09`09; MODIFY FILE ATTRIBUTES
X`09`09MOUNT>`09`09`09; MOUNT VOLUME
X; no-op phys I/O for a test here...
X`09FUNCTAB`09,-`09`09`09;BUFFERED FUNCTIONS
X`09`09<NOP,-
X`09`09FORMAT,-`09`09; FORMAT
X`09`09UNLOAD,-`09`09; UNLOAD
X`09`09PACKACK,-`09`09; PACK ACKNOWLEDGE
X`09`09AVAILABLE,-`09`09; AVAILABLE
X`09`09SENSECHAR,-`09`09; SENSE CHARACTERISTICS
X`09`09SETCHAR,-`09`09; SET CHARACTERISTICS
X`09`09SENSEMODE,-`09`09; SENSE MODE
X`09`09SETMODE,-`09`09; SET MODE
X`09`09ACCESS,-`09`09; ACCESS FILE / FIND DIRECTORY ENTRY
X`09`09ACPCONTROL,-`09`09; ACP CONTROL FUNCTION
X`09`09CREATE,-`09`09; CREATE FILE AND/OR DIRECTORY ENTRY
X`09`09DEACCESS,-`09`09; DEACCESS FILE
X`09`09DELETE,-`09`09; DELETE FILE AND/OR DIRECTORY ENTRY
X`09`09MODIFY,-`09`09; MODIFY FILE ATTRIBUTES
X`09`09MOUNT>`09`09`09; MOUNT VOLUME
X`09FUNCTAB`09VD_ALIGN,-`09`09;TEST ALIGNMENT FUNCTIONS
X`09`09<READLBLK,-`09`09; READ LOGICAL BLOCK
X`09`09READVBLK,-`09`09; READ VIRTUAL BLOCK
X`09`09READPBLK,-
X;`09`09WRITEPBLK,-
X`09`09WRITELBLK,-`09`09; WRITE LOGICAL BLOCK
X`09`09WRITEVBLK-`09`09; WRITE VIRTUAL BLOCK
X`09`09>
X`09functab vd_format,-`09`09;point to host disk
X`09`09<format>
X; NOTE SEPARATE CALL FOR PHYSICAL I/O SO WE CAN JUST CONVERT TO LOGICAL AND
X; DO OUR THING... CONVERT TO A LOGICAL QIO THERE FOR "REAL" DRIVER ALSO
X; SO IT CAN DO CONVERSION TO ITS IDEA OF PHYSICAL IF IT WISHES...
X;
X; LEAVE NORMAL ACP CALLS IN SO FILE STRUCTURED STUFF ON OUR VD: UNIT
X; WILL WORK OK.
X;
X`09FUNCTAB`09+ACP$READBLK,-`09`09;READ FUNCTIONS
X`09`09<READLBLK,-`09`09; READ LOGICAL BLOCK
X`09`09READPBLK,-
X`09`09READVBLK-`09`09; READ VIRTUAL BLOCK
X`09`09>
X`09FUNCTAB`09+ACP$WRITEBLK,-`09`09;WRITE FUNCTIONS
X`09`09<WRITELBLK,-`09`09; WRITE LOGICAL BLOCK
X`09`09WRITEPBLK,-
X`09`09WRITEVBLK-`09`09; WRITE VIRTUAL BLOCK
X`09`09>
X`09FUNCTAB`09+ACP$ACCESS,-`09`09;ACCESS FUNCTIONS
X`09`09<ACCESS,-`09`09; ACCEESS FILE / FIND DIRECTORY ENTRY
X`09`09CREATE-`09`09`09; CREATE FILE AND/OR DIRECTORY ENTRY
X`09`09>
X`09FUNCTAB`09+ACP$DEACCESS,-`09`09;DEACCESS FUNCTION
X`09`09<DEACCESS-`09`09; DEACCESS FILE
X`09`09>
X`09FUNCTAB`09+ACP$MODIFY,-`09`09;MODIFY FUNCTIONS
X`09`09<ACPCONTROL,-`09`09; ACP CONTROL FUNCTION
X`09`09DELETE,-`09`09; DELETE FILE AND/OR DIRECTORY ENTRY
X`09`09MODIFY-`09`09`09; MODIFY FILE ATTRIBUTES
X`09`09>
X`09FUNCTAB`09+ACP$MOUNT,-`09`09;MOUNT FUNCTION
X`09`09<MOUNT>`09`09`09; MOUNT VOLUME
X`09FUNCTAB`09+EXE$ZEROPARM,-`09`09;ZERO PARAMETER FUNCTIONS
X`09`09<UNLOAD,-`09`09; UNLOAD
X`09`09PACKACK,-`09`09; PACK ACKNOWLEDGE
X`09`09AVAILABLE>`09`09; AVAILABLE
X`09FUNCTAB`09+EXE$ONEPARM,-`09`09;ONE PARAMETER FUNCTION
X`09`09<FORMAT-`09`09; FORMAT
X`09`09>
X`09FUNCTAB`09+EXE$SENSEMODE,-`09;SENSE FUNCTIONS
X`09`09<SENSECHAR,-`09`09; SENSE CHARACTERISTICS
X`09`09SENSEMODE-`09`09; SENSE MODE
X`09`09>
X`09FUNCTAB`09+EXE$SETCHAR,-`09`09;SET FUNCTIONS
X`09`09<SETCHAR,-`09`09; SET CHARACTERISTICS
X`09`09SETMODE-`09`09; SET MODE
X`09`09>
X;
X; BLOCK OF UCB ADDRESSES
X;
X`09
XVD_UCBTBL::
X`09.BLKL`09VD_UNITS
X`09.LONG 0,0`09;SAFETY
X`09.long`090,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0`09;more safety
X
X`09.PAGE
X`09.SBTTL FDT Routines
X`09DRIVER_CODE
X;++
X;
X; vd_format - point to proper location on the host disk
X;
X; With no function modifiers, this routine takes as arguments the name
X; of the host disk (the real disk where the virtual disk will exist),
X; the size of the virtual disk, and the LBN where the virtual disk
X; will start. After these are set up, the device is put online and is
X; software enabled.
X;
X; This routine does virtually no checking, so the parameters must be
X; correct.
X;
X; Inputs:
X;`09p1 - pointer to buffer. The buffer has the following format:
X;`09     longword 0 - starting LBN, where the virtual disk starts
X;`09     `09`09  on the real disk.
X;`09     longword 1 - virtual disk length, the number of blocks in
X;`09`09`09  the virtual disk.
X;`09     longword 2 through the end of the buffer, the name of the
X;`09`09`09  virtual disk. This buffer must be blank
X;`09`09`09  padded if padding is necessary
X;
X;`09p2 - size of the above buffer
X;--
Xvd_format: .jsb_entry
X`09bicl3`09#io$m_fcode,irp$l_func(r3),r0`09;mask off function code
X`09bneq`0920$`09`09`09;branch if modifiers, special
X;thus, normal io$_format will do nothing.
X`09rsb`09`09`09`09;regular processing
X10$:
X`09movzwl`09#SS$_BADPARAM,r0`09;illegal parameter
X`09clrl`09r1
X`09jmp`09g`5Eexe$abortio
X20$:
X`09movl`09irp$l_qio_p1(r3),r0`09;buffer address
X`09movl`09irp$l_qio_p2(r3),r1`09;length of buffer
X`09jsb`09g`5Eexe$writechk`09`09;read access? doesn't return on error
X`09clrl`09irp$l_bcnt`09`09;paranoia, don't need to do this...
X`09movl`09irp$l_qio_p1(r3),r0`09;get buffer address
X`09movl`09(r0)+,-`09`09`09;move starting lbn
X`09`09ucb$hlbn(r5)
X`09blss`0910$
X`09movl`09(r0)+,-`09`09`09;size of virtual disk
X`09`09ucb$hfsz(r5)
X`09bleq`0910$
X`09movl`09(r0),-`09`09`09;name of "real" disk
X`09`09ucb$l_vd_host_descr+4(r5)
X`09subl3`09#8,irp$l_qio_p2(r3),-`09;set length of name in descriptor
X`09`09ucb$l_vd_host_descr(r5)
X`09bleq`0910$`09`09`09;bad length
X`09moval`09ucb$l_vd_host_descr(r5),r1`09;descriptor for...
X`09jsb`09g`5Eioc$searchdev`09`09;search for host device
X`09blbs`09r0,30$`09`09`09;branch on success
X`09movzwl`09#ss$_nosuchdev+2,r0`09;make an error, usually a warning
X`09clrl`09r1
X`09jmp`09g`5Eexe$abortio`09`09;exit with error
X30$:
X`09addl3`09ucb$hfsz(r5),- `09;end of virtual device
X`09`09ucb$hlbn(r5),r0
X`09cmpl`09ucb$l_maxblock(r1),r0`09`09; < end of real disk?
X`09blss`0910$
X`09movl`09r1,ucb$hucb(r5)`09;stash the ucb
X`09clrl`09ucb$ppid(r5)`09`09; mark driver free of old pids
X`09bisl`09#ucb$m_valid,ucb$l_sts(r5)`09;set volume valid
X`09bisl`09#ucb$m_online,ucb$l_sts(r5)`09;set unit online
X`09movl`09ucb$l_irp(r5),r3`09`09;restore r3, neatness counts
X`09movzwl`09#ss$_normal,r0`09`09`09;success
X`09jmp`09g`5Eexe$finishioc`09`09`09;wrap things up.
X
X`09.SBTTL`09CONTROLLER INITIALIZATION ROUTINE
X; ++
X;`20
X; VD_ctrl_INIT - CONTROLLER INITIALIZATION ROUTINE
X;`20
X; FUNCTIONAL DESCRIPTION:
X; noop
X; INPUTS:
X; R4 - CSR ADDRESS
X; R5 - IDB ADDRESS
X; R6 - DDB ADDRESS
X; R8 - CRB ADDRESS
X;`20
X; `09THE OPERATING SYSTEM CALLS THIS ROUTINE:
X; `09`09- AT SYSTEM STARTUP
X; `09`09- DURING DRIVER LOADING
X; `09`09- DURING RECOVERY FROM POWER FAILURE
X; `09THE DRIVER CALLS THIS ROUTINE TO INIT AFTER AN NXM ERROR.
X;--
XVD_ctrl_INIT: .jsb_entry`09;vd CONTROLLER INITIALIZATION
X`09CLRL`09CRB$L_AUXSTRUC(R8)`09; SAY NO AUX MEM
X`09RSB`09`09`09`09;RETURN
X`09.PAGE
X`09.SBTTL`09INTERNAL CONTROLLER RE-INITIALIZATION
X;
X; INPUTS:
X;`09R4 => controller CSR (dummy)
X;`09R5 => UCB
X;
Xctrl_REINIT: .jsb_entry
X`09RSB`09`09; RETURN TO CALLER
X`09.PAGE
X`09.SBTTL`09UNIT INITIALIZATION ROUTINE
X;++
X;`20
X; VD_unit_INIT - UNIT INITIALIZATION ROUTINE
X;`20
X; FUNCTIONAL DESCRIPTION:
X;`20
X; `09THIS ROUTINE SETS THE VD: ONLINE.
X;`20
X; `09THE OPERATING SYSTEM CALLS THIS ROUTINE:
X; `09`09- AT SYSTEM STARTUP
X; `09`09- DURING DRIVER LOADING
X; `09`09- DURING RECOVERY FROM POWER FAILURE
X;`20
X; INPUTS:
X;`20
X; `09R4`09- CSR ADDRESS (CONTROLLER STATUS REGISTER)
X; `09R5`09- UCB ADDRESS (UNIT CONTROL BLOCK)
X;`09R8`09- CRB ADDRESS
X;`20
X; OUTPUTS:
X;`20
X; `09THE UNIT IS SET ONLINE.
X; `09ALL GENERAL REGISTERS (R0-R15) ARE PRESERVED.
X;`20
X;--
X
XVD_unit_INIT: .jsb_entry`09`09;vd UNIT INITIALIZATION
X; Don't set unit online here. Priv'd task that assigns VD unit
X; to a file does this to ensure only assigned VDn: get used.
X;`09BISl`09#UCB$M_ONLINE,UCB$l_STS(R5)  ;SET UCB STATUS ONLINE
X;limit size of VD: data buffers
Xvd_bufsiz=8192
X`09movl`09#vd_bufsiz,ucb$l_maxbcnt(r5)`09;limit transfers to 8k
X`09MOVB`09#DC$_DISK,UCB$B_DEVCLASS(R5) ;SET DISK DEVICE CLASS
X; NOTE: we may want to set this as something other than an RX class
X; disk if MSCP is to use it. MSCP explicitly will NOT serve an
X; RX type device. For now leave it in, but others can alter.
X; (There's no GOOD reason to disable MSCP, but care!!!)
X`09movl`09#`5EXb12c4001,ucb$l_media_id(r5)`09; set media id as VD
X; (note the id might be wrong but is attempt to get it.) (used only for
X; MSCP serving.)
X`09MOVB`09#DT$_FD1,UCB$B_DEVTYPE(R5)  ;Make it foreign disk type 1
X; (dt$_rp06 works but may confuse analyze/disk)
X;;; NOTE: changed from fd1 type so MSCP will know it's a local disk and
X;;; attempt no weird jiggery-pokery with the VD: device.
X; MSCP may still refuse to do a foreign drive too; jiggery-pokery later
X; to test if there's occasion to do so.
X`09RSB`09`09`09`09;RETURN`20
X`09.PAGE
X`09.SBTTL`09FDT ROUTINES`20
X;++
X;`20
X; VD_ALIGN - FDT ROUTINE TO TEST XFER BYTE COUNT
X;`20
X; FUNCTIONAL DESCRIPTION:
X;`20
X; `09THIS ROUTINE IS CALLED FROM THE FUNCTION DECISION TABLE DISPATCHER
X; `09TO CHECK THE BYTE COUNT PARAMETER SPECIFIED BY THE USER PROCESS
X; `09FOR AN EVEN NUMBER OF BYTES (WORD BOUNDARY).
X;`20
X; INPUTS:
X;`20
X; `09R3`09- IRP ADDRESS (I/O REQUEST PACKET)
X; `09R4`09- PCB ADDRESS (PROCESS CONTROL BLOCK)
X; `09R5`09- UCB ADDRESS (UNIT CONTROL BLOCK)
X; `09R6`09- CCB ADDRESS (CHANNEL CONTROL BLOCK)
X; `09R7`09- BIT NUMBER OF THE I/O FUNCTION CODE
X; `09R8`09- ADDRESS OF FDT TABLE ENTRY FOR THIS ROUTINE
X; `094(AP)`09- ADDRESS OF FIRST FUNCTION DEPENDENT QIO PARAMETER
X;`20
X; OUTPUTS:
X;`20
X; `09IF THE QIO BYTE COUNT PARAMETER IS ODD, THE I/O OPERATION IS
X; `09TERMINATED WITH AN ERROR. IF IT IS EVEN, CONTROL IS RETURNED
X; `09TO THE FDT DISPATCHER.
X;`20
X;--
Xnolchk=0
XVD_ALIGN: .jsb_entry`09`09`09`09;CHECK BYTE COUNT AT P1(AP)
X`09BLBS`09irp$l_qio_p2(R3),10$`09`09;IF LBS - ODD BYTE COUNT
X`09RSB`09`09`09`09;EVEN - RETURN TO CALLER
X10$:`09MOVZWL`09#SS$_IVBUFLEN,R0`09;SET BUFFER ALIGNMENT STATUS
X`09JMP`09G`5EEXE$ABORTIO`09`09;ABORT I/O
X`09.PAGE
X`09.SBTTL`09START I/O ROUTINE
X;++
X;`20
X; VD_STARTIO - START I/O ROUTINE
X;`20
X; FUNCTIONAL DESCRIPTION:
X;`20
X; `09THIS FORK PROCESS IS ENTERED FROM THE EXECUTIVE AFTER AN I/O REQUEST
X; `09PACKET HAS BEEN DEQUEUED.
X;`20
X; INPUTS:
X;`20
X; `09R3`09`09- IRP ADDRESS (I/O REQUEST PACKET)
X; `09R5`09`09- UCB ADDRESS (UNIT CONTROL BLOCK)
X; `09IRP$L_MEDIA`09- PARAMETER LONGWORD (LOGICAL BLOCK NUMBER)
X;`20
X; OUTPUTS:
X;`20
X; `09R0`09- FIRST I/O STATUS LONGWORD: STATUS CODE & BYTES XFERED
X; `09R1`09- SECOND I/O STATUS LONGWORD: 0 FOR DISKS
X;`20
X; `09THE I/O FUNCTION IS EXECUTED.
X;`20
X; `09ALL REGISTERS EXCEPT R0-R4 ARE PRESERVED.
X;`20
X;--
X`09.if df,x$$$dt
Xrwflg:`09.long`090
X`09.endc
XREQUEUE:
X`09.if`09df,d$$bug
X`09movl`09r3,dbgdta+68`09;store offending IRP here
X`09.endc
X`09.if`09df,x$$$dt
X`09jsb`09g`5Eini$brk
X`09.endc
X`09JMP`09G`5EEXE$INSIOQ`09`09; REQUEUE packet to ourselves
X; return to our caller direct from insioq.
X; (note this also sets busy, so it will NOT loop forever.)
XVD_STARTIO: .jsb_entry`09`09`09`09;START I/O OPERATION
X;`20
X; `09PREPROCESS UCB FIELDS
X;`20
X;`09ASSUME`09RY_EXTENDED_STATUS_LENGTH  EQ  8
X;`09CLRQ`09UCB$Q_VD_EXTENDED_STATUS(R5)`09; Zero READ ERROR REGISTER area.
X;`20
X; `09BRANCH TO FUNCTION EXECUTION
X`09bbs`09#ucb$v_online,-`09; if online set software valid
X`09`09ucb$l_sts(r5),210$
X216$:`09movzwl`09#ss$_volinv,r0`09; else set volume invalid
X`09brw`09resetxfr`09; reset byte count & exit
X210$:
X`09tstl`09ucb$hucb(r5)`09; do we have any host device?
X`09beql`09216$`09`09; if eql no, flag invalid volume.
X`09`09`09; THIS IS SAFETY FROM CONFIGURING FROM OUTSIDE
X; BEFORE GOING ON, WE WANT TO ENSURE THE UCB IS FREE.
X`09TSTL`09UCB$PPID(R5)`09`09; MAKE SURE we haven't got
X`09`09`09`09`09; a packet in process
X`09BNEQ`09REQUEUE`09`09`09; IF a packet's in process, requeue
X`09`09`09`09`09; back to this driver; do NOT process
X`09`09`09`09`09; immediately!
X; Note...never seems to get to requeue (xdelta would catch it!)
X; (that's a good sign; should never get there.)
X`09bisl`09#ucb$m_online,ucb$l_sts(r5)`09; set online
X`09bisl`09#ucb$m_valid,ucb$l_sts(r5)`09;set valid
X; set ourselves as owners of channel for VD:
X;`09movl`09ucb$l_crb(r5),r0
X;`09movl`09crb$l_intd+vec$l_idb(r0),r0`09;get idb address
X214$:
X;
X10$:;`09BBS`09#IRP$V_PHYSIO,-`09`09;IF SET - PHYSICAL I/O FUNCTION
X;`09`09IRP$l_STS(R3),20$`09;...
X`09BBS`09#UCB$V_VALID,-`09`09;IF SET - VOLUME SOFTWARE VALID
X`09`09UCB$l_STS(R5),20$`09;...
X`09MOVZWL`09#SS$_VOLINV,R0`09`09;SET VOLUME INVALID STATUS
X`09BRW`09RESETXFR`09`09;RESET BYTE COUNT AND EXIT
X20$:
X; IF WE GET A SEGMENT TRANSFER HERE (LOGICAL I/O)
X; IT MUST BE UPDATED FOR HOST AND SHIPPED OUT.
X; OUR UCB HAS BLOCK NUMBER INFO...
X; FIND OUT IF THIS IS LOGICAL OR PHYSICAL I/O FIRST. THEN IF IT IS BUGGER
X; THE I/O PACKET USING UCB INFO AND SEND TO THE REAL DRIVER...
X; ALSO ENSURE WE ARE UNBUSIED...
X;
X`09EXTZV`09#IRP$V_FCODE,#IRP$S_FCODE,IRP$L_FUNC(R3),R1`09; GET FCN CODE
X`09case`09r1,<-`09`09`09; Dispatch to function handling routine
X`09`09unload,-`09`09; Unload
X`09`09nop,-`09`09`09; Seek
X`09`09NOP,-`09`09`09; Recalibrate(unsupported)
X`09`09nop,-`09`09`09; Drive clear
X`09`09NOP,-`09`09`09; Release port(unsupported)
X`09`09NOP,-`09`09`09; Offset heads(unsupported)
X`09`09NOP,-`09`09`09; Return to center
X`09`09nop,-`09`09`09; Pack acknowledge
X`09`09NOP,-`09`09`09; Search(unsupported)
X`09`09NOP,-`09`09`09; Write check(unsupported)
X`09`09WRITEDATA,-`09`09; Write data
X`09`09READDATA,-`09`09; Read data
X`09`09NOP,-`09`09`09; Write header(unsupported)
X`09`09NOP,-`09`09`09; Read header(unsupported)
X`09`09NOP,-`09`09`09; Place holder
X`09`09NOP,-`09`09`09; Place holder
X`09`09available,-`09`09; Available (17)
X`09`09NOP,NOP,NOP,-`09`09; 18-20
X`09`09NOP,NOP,NOP,NOP,nop,nop,nop,NOP,NOP,nop,- ;21-30
X`09`09NOP,NOP,NOP,NOP,nop,NOP,nop,nop,nop,NOP,- ;31-40
X`09`09NOP,NOP,NOP,NOP,NOP,NOP,NOP,NOP,NOP,nop,- ;41-50
X`09`09NOP,NOP,NOP,NOP,nop,NOP,NOP,NOP,NOP,NOP,- ;51-60
X`09`09nop,-`09`09`09`09`09  ;61
X`09`09>,LIMIT=#1
X
Xnop:`09`09`09`09;unimplemented function
X`09brw`09fexl
X
Xreaddata:
X`09.if df,x$$$dt
X`09clrl`09rwflg
X`09brb`09rwcmn
X`09.endc
Xwritedata:
X`09.if df,x$$$dt
X`09movl #1,rwflg
Xrwcmn:
X`09.endc
X; debug using sda to peek
X; NOW VALIDATED I/O FCN... MODIFY AND SEND OFF
X`09.if`09df,d$$bug
X`09MOVL`09#1,DBGDTA`09;TELL THAT WE GOT HERE
X`09MOVL`09R5,DBGDTA+4`09;SAVE OUR UCB ADDR
X`09MOVL`09R3,DBGDTA+8`09; AND I/O PKT
X`09MOVL`09IRP$L_MEDIA(R3),DBGDTA+12`09;STORE BLK NUMBER GIVEN
X`09MOVL`09IRP$L_OBCNT(R3),DBGDTA+16`09;ALSO ORIG BYTE CNT
X`09MOVL`09R1,DBGDTA+36`09;SAVE FCN OFFSET
X`09.endc
X`09CMPL`09IRP$L_MEDIA(R3),UCB$HFSZ(R5)`09;BE SURE LBN OK
X`09blequ`0965$
X`09brw`09Fatalerr`09;dismiss I/o if not ok block number
X65$:
X; HAVE TO BE CAREFUL WHAT WE SHIP TO REAL DRIVER
X; Now that we know IRP$L_MEDIA is ok in IRP, save it for restore at
X; I/O completion by VDDRIVER
X
X`09movl`09irp$l_media(r3),ucb$lmedia(r5)
X`09; Prepare to enter another context.
X; SEND PKT OFF TO REAL DRIVER...
X`09.if`09df,x$$$dt
X`09cmpl`09irp$l_bcnt(r3),irp$l_obcnt(r3)
X`09beql`09117$`09`09;branch if no split I/O going on
X`09movl`09irp$l_segvbn(r3),r2`09;get segvbn
X`09movl`09irp$l_media(r3),r1`09;and media fields
X`09jsb`09g`5Eini$brk
X117$:
X`09.endc
X`09ADDL2`09UCB$HLBN(R5),IRP$L_MEDIA(R3)`09;ADJUST LBN IN IO PKT
X`09.if`09df,d$$bug
X`09MOVL`09IRP$L_MEDIA(R3),DBGDTA+20
X`09MOVL`09UCB$HLBN(R5),DBGDTA+24
X`09MOVL`09UCB$HFSZ(R5),DBGDTA+28
X`09MOVL`09IRP$L_SEGVBN(R3),DBGDTA+32
X`09.endc
X;
X; NOW we have to fix up the media address for the host...
X; ... otherwise we confuse the heck out of things by making
X; ... the host (who is expecting a track/sect/cyl number) get really
X; ... goofy numbers. Cheat by using exec routine after a bit more messup.
X;
X`09MOVL`09UCB$HUCB(R5),IRP$L_UCB(R3)`09;FIX UP PTR IN I/O PKT
X; GRAB HOST PID
X`09TSTL`09UCB$PPID(R5)`09`09; GUARD AGAINST DOUBLE BASH
X`09BNEQ`0912$
X`09MOVL`09IRP$L_PID(R3),UCB$PPID(R5)`09; SAVE PROCESS ID IN VD: UCB
X`09movl`09irp$l_sts(r3),ucb$stats(r5)`09;save original fcn code
X`09movl`09irp$l_obcnt(r3),ucb$obct(r5)`09;store obcnt field
X; belt 'n' suspenders next...
X`09movl`09irp$l_bcnt(r3),irp$l_obcnt(r3)`09;and reset to actual
X`09`09`09`09`09; requested so driver NEVER sees
X`09`09`09`09`09; need to do postprocessing requeues
X`09`09`09`09`09; in host context. (we do that in OUR
X`09`09`09`09`09; context.)
X`09movl`09irp$l_wind(r3),ucb$owind(r5)`09;store window ptr
X`09movl`09irp$l_segvbn(r3),ucb$osegv(r5)`09;store segment vbn also
X`09brb`091200$`09;mousetrap loc for attempted dbl bash
X12$:
X`09.if`09df,x$$$dt
X`09jsb`09g`5Eini$brk`09;break here ONLY if ppid was nonzero
X`09.endc
X1200$:
X`09MOVZWL`09UCB$W_UNIT(R5),-(SP)`09; BUILD ADDRESS OF UCB STORE
X`09ASHL`09#2,(SP),(SP)`09`09; WITH OFFSET * 4
X`09.IF`09DF,D$$BUG
X`09MOVAB`09VD_UCBTBL,DBGDTA+60
X`09MOVAB`09VD_FXS0,DBGDTA+64
X`09.ENDC
X`09MOVAB`09VD_UCBTBL,-(SP)`09`09; GET TBL BASE IN STACK
X`09ADDL2`09(SP)+,(SP)`09`09; NOW ADD BASE + OFFSET
X`09MOVL`09R5,@(SP)+`09`09; AND STORE UCB ADDRESS IN VD_UCBTBL
X; (THIS ALLOWS US TO GET IT BACK...)
X`09MOVZWL`09UCB$W_UNIT(R5),-(SP)`09; BUILD ADDRESS OF ENTRY NOW
X`09MULL2`09#VD_FXPL,(SP)`09`09; MULTIPLY OFFSET BY SIZE OF ENTRY
X`09MOVAB`09VD_FXS0,IRP$L_PID(R3)`09;AND BASH PID IN IRP SO WE
X`09`09`09`09; GET BACK CONTROL AT VD_FIXSPLIT (VIA JSB)
X`09`09`09`09; WHEN HOST'S I/O IS DONE.
X`09ADDL2`09(SP)+,IRP$L_PID(R3)`09;SET TO ENTER IN CORRECT
X`09`09`09`09`09; UNIT'S ENTRY
X`09.IF`09DF,D$$BUG
X`09MOVL`09UCB$PPID(R5),DBGDTA+40`09;SAVE OLD PID
X`09MOVL`09IRP$L_PID(R3),DBGDTA+44`09;AND NEW "PID"
X`09MOVL`09IRP$l_STS(R3),DBGDTA+48 ;STATUS BYTE
X`09MOVL`09#IRP$M_PHYSIO,DBGDTA+52`09;AND PHYS I/O BIT
X`09.ENDC
X`09.if`09df,vms$v5`09`09;save our UCB for synch
X; normally just call exe$insioqc to avoid SMP problems in V5.x and up
X`09.if`09df,relacq
X`09movzbl`09ucb$b_flck(r5),-(sp)`09;store fork lock info on stack
X`09.endc
X`09.endc
X`09MOVL`09UCB$HUCB(R5),R5`09`09;NOW POINT AT HOST UCB OURSELVES
X;
X;;;
X`09MOVL`09IRP$L_MEDIA(R3),R0`09;GET LBN TO CONVERT
X; Note that the host driver normally will get physical I/O addresses
X; in this entry. Logical I/O is converted to physical in FDT
X; routines for most drivers; the few exceptions inhibit conversion
X; via IOC$CVTLOGPHY anyway. Therefore we must ALWAYS convert to
X; physical.
X`09JSB`09G`5EIOC$CVTLOGPHY`09`09; LET THE EXEC DO IT
X; Logical I/O... relocate it here
X; Already adjusted the logical blk # earlier
X`09.if`09df,d$$bug
X`09MOVL`09IRP$L_MEDIA(R3),DBGDTA+56`09;SAVE FOR DEBUG EXAM
X`09.endc
X; next op may mess up some regs. Also we cannot access the packet once
X; we give it to the host driver thus:
X`09.if`09ndf,vms$v5`09;this code is fine in V4
X`09JMP`09G`5EEXE$INSIOQ`09`09; INSERT PACKET INTO HOST'S QUEUE
X`09.iff
X; for smp may need to release a spinlock on vd: ucb at this point before
X; the jmp.
X;  Since this vd: unit is busy at this point, we won't get back here
X; until the I/O completion anyway, so we should be able to leave fork state
X; and let exe$insioq re-enter it for our host driver.
X; Main difficulty is releasing the correct fork...
X`09.if`09df,relacq
X`09.if`09ndf,v5$picky
X`09movl`09(sp)+,r0`09`09;restore our fork lock info
X`09forkunlock R0`09`09`09;and release VD:'s spinlocks
X; Notice that exe$insioq acquires and releases forklock. Since we were
X; called from this routine ultimately also, we should ensure that
X; when we return that we hold the proper VD: forklock to be released.
X; This will be conditioned by v5$picky defined.
X`09JMP`09G`5EEXE$INSIOQ`09`09; INSERT PACKET INTO HOST'S QUEUE
X`09.iff`09;v5$picky
X`09movl`09(sp),r0`09`09`09;get our lock ID
X`09forkunlock r0`09`09`09;release vd: spinlocks and so on
X`09jsb`09g`5Eexe$insioq`09`09;stash the IRP on host driver's queue
X;now get our forklock back
X`09movl`09(sp)+,r0`09`09;get r0 back as vd: lock id
X`09forklock r0`09`09`09;and re-acquire lock so
X;`09`09`09`09`09;exe$insioq can dismiss it...
X`09rsb`09`09`09`09;then return
X`09.endc`09;v5$picky
X`09.iff`09;relacq
X`09JMP`09G`5EEXE$INSIOQC`09`09;insert packet but keep forklock
X`09.endc`09;relacq
X`09.endc`09;vms$v5
X; WE Now have queued the work to the real driver. Since the
X; I/O may have splits, just await done return and let the
X; vd_fixsplit processing get done our cleanup. Because we need
X; to await this, just return with VD: unit STILL BUSY to ensure
X; that we don't get thru here until we're GOOD AND READY!
X`09.if`09ndf,vms$v5
X402$:
X`09BRW`09FEXL`09`09`09;Else, branch to execute function.
X;
X`09.endc
X;`09UNLOAD and AVAILABLE Functions
X;`09Clear UCB$V_VALID in UCB$W_STS
X;
XUNLOAD:
XAVAILABLE: ;.jsb_entry
X;`09BICl`09#UCB$M_VALID, -`09`09;Clear sofware volume valid bit.
X;`09`09UCB$l_STS(R5)
X;`09BRB`09NORMAL`09`09`09;Then complete the operation.
X;`20
X; `09OPERATON COMPLETION
X;`20
XFEXL:`09; dummy entry ... should never get here
XNORMAL:`09;.jsb_entry`09`09`09`09;SUCCESSFUL OPERATION COMPLETE
X`09MOVZWL`09#SS$_NORMAL,R0`09`09;ASSUME NORMAL COMPLETION STATUS
X`09BRB`09FUNCXT`09`09`09;FUNCTION EXIT
X
XFATALERR: ;.jsb_entry`09`09;UNRECOVERABLE ERROR
X`09MOVZWL`09#SS$_DRVERR,R0`09`09;ASSUME DRIVE ERROR STATUS
X
XRESETXFR:;.jsb_entry`09; dummy entry ... should never really get here
X`09MOVL`09UCB$L_IRP(R5),R3`09;GET I/O PKT
X;`09MNEGW`09IRP$L_BCNT(R3),UCB$L_BCR(R5) ; RESET BYTECOUNT
X;`09BRW`09FUNCXT
XFUNCXT: ;.jsb_entry`09`09`09`09`09;FUNCTION EXIT
X`09CLRL`09R1`09`09`09;CLEAR 2ND LONGWORD OF IOSB
X`09REQCOM`09`09`09`09; COMPLETE REQUEST
X`09.PAGE
X;`20
XPWRFAIL: .jsb_entry`09`09`09;POWER FAILURE
X`09BICl`09#UCB$M_POWER,UCB$l_STS(R5)  ;CLEAR POWER FAILURE BIT
X`09MOVL`09UCB$L_IRP(R5),R3`09;GET ADDRESS OF I/O PACKET
X`09MOVQ`09IRP$L_SVAPTE(R3),-`09;RESTORE TRANSFER PARAMETERS
X`09`09UCB$L_SVAPTE(R5)`09;...
X`09BRW`09VD_STARTIO`09`09;START REQUEST OVER
X`09.if`09df,d$$bug
XDBGDTA:`09.BLKL`096`09;AREA TO HOLD DISPLAYS
X`09.BLKL`0920
X`09.BLKL`0920
X`09.BLKL`0940
X`09.endc
X;VD_INT::
XVD_UNSOLNT:: .jsb_entry
X`09POPR`09#`5EM<R0,R1,R2,R3,R4,R5>
X`09REI`09;DUMMY RETURN FROM ANY INTERRUPT
X`09;;
X; FIX SPLITS...
X; RETURN IRP TO OUR UCB ADDRESS
X; THEN REQCOM
X;
X; TRICK IS TO GET OUR UCB ADDRESS BACK WHEN WE REGAIN CONTROL. DO SO VIA
X; JIGGERY-POKERY WITH THE ADDRESS WE CALL. STORE UCB ADDRESSES IN A TABLE
X; INTERNALLY AND USE THE CALL ADDRESS TO GET WHERE WE ARE BACK AGAIN.
X;
X; Note: On entry, r5 points at the IRP we're to handle. We bash this
X; information and regenerate it, since irp$l_ucb has already been
X; bashed and can't locate our UCB anyway. Therefore we let the return
X; address give us the VD: UCB address implicitly via local save and
X; restore; each VD: unit returns to a different entry point which preloads
X; r5 with a different offset. Once the ucb is located, ucb$l_irp gets
X; back the IRP address. This is possibly extra work; one can imagine
X; that some IRP fields like SEGVBN could be used to hold the vd: ucb
X; address temporarily, since they are saved/restored internally. This
X; would allow some address arithmetic to be dispensed with. The current
X; method is merely intended to work and NOT force us to let the host driver
X; (and maybe other host software) look at bogus IRP fields, and also
X; let us remain blissfully ignorant of how VMS handles IRPEs for purposes
X; of this driver. (ecch...having to figure out a way to keep i/o post
X; processing out of OUR IRPEs (for sure) while using them for temporary
X; storage... what a thought!)
X;
X; NOTE FOLLOWING CODE ASSUMES VD_UNITS IS 2 OR MORE.
XV_UNIT=0
XV_UNM=1
XVD_FXS0:: .jsb_entry
X`09MOVL`09I`5E#V_UNIT,R4
X`09BSBW`09VD_FIXSPLIT`09;GO HANDLE
X`09RSB
XVD_FXPL==.-VD_FXS0`09;LENGTH IN BYTES OF THIS LITTLE CODE SEGMENT
XV_UNIT=V_UNIT+4`09`09;PASS TO NEXT UNIT
X`09.MACRO`09XVEC LBLC
XVD_FXS'LBLC: .jsb_entry
X`09MOVL`09I`5E#V_UNIT,R4
X`09BSBW`09VD_FIXSPLIT
X`09RSB
X`09.ENDM
X`09.REPEAT`09<VD_UNITS+4>`09; some extra for safety
X`09XVEC`09\V_UNM
XV_UNIT=V_UNIT+4`09`09;PASS TO NEXT UNIT
XV_UNM=V_UNM+1
X`09.ENDR
XVD_FIXSPLIT: .jsb_entry
X; GET OLD PID..
X; IN OUR UCB$PPID LONGWORD...
X;`09.IF`09NDF,VMS$V5
X;; assume ipl$_synch = 8
X;`09DSBINT`09ipl=#8`09`09; GO TO FORK IPL
X;`09.ENDC
X; NOTE!!! PROBABLY NEEDS MODS FOR VMS V5!!!
X;some cleanup for host needed here. Note that r5 enters as IRP address.
X; Use initial R5 to help reset host's system...
X`09movl`09irp$l_ucb(r5),r3`09;get host's UCB addr
X`09movl`09r5,r2`09`09;store entry IRP address for check later
X`09PUSHL`09R4`09;NEED TO WORK IN R5
X`09MOVAB`09VD_UCBTBL,R5
X`09ADDL2`09(SP)+,R5`09;R5 NOW POINTS AT UCB ADDRESS
X`09MOVL`09(R5),R5`09`09;NOW HAVE OUR UCB ADDRESS IN R5
X`09decl`09ucb$l_qlen(r3)`09;cleanup host's q len as ioc$iopost would have
X`09bgeq`096$
X`09clrl`09ucb$l_qlen(r3)`09;force queue length zero
X6$:
X`09cmpl`09r2,ucb$l_irp(r5)`09;got the correct IRP???
X`09beql`097$`09`09;if eql yes
X`09.iif`09df,x$$$dt,jsb g`5Eini$brk ;notify...
X; MUST avoid screwup where we don't have the correct IRP since there's
X; no connection directly between IRP and UCB. VD: unit being busy should
X; avoid this error, BUT we have no way to be certain of this w/o exhaustive
X; system code checks.
X`09rsb`09`09`09;else wrong IRP, don't do more damage.
X7$:
X; notice stack is now clean too.
X`09movl`09r5,r4
X`09FORK`09`09`09;go fork on our UCB now (vd: ucb)
X`09movl`09r4,r5
X;`09.IF`09DF,VMS$V5
X;`09FORKLOCK SAVIPL=-(SP)
X;`09.ENDC
X`09.if`09df,d$$bug
X`09movl`09r5,dbgdta+72`09;save ucb addr as flag we got here
X`09movl`09ucb$l_irp(r5),dbgdta+76`09;save irp addr also
X`09.endc
X`09MOVL`09UCB$L_IRP(R5),R3`09; POINT R3 AT IRP AGAIN
X`09.if`09df,d$$bug
X`09movl`09r5,dbgdta+80`09;save ucb addr as flag we got here
X`09movl`09ucb$l_irp(r5),dbgdta+84`09;save irp addr also
X`09movl`09ucb$ppid(r5),dbgdta+88`09;save old pid field also
X`09.endc
X`09TSTL`09UCB$PPID(R5)`09; ENSURE PID IS NONZERO AS SAVED
X`09BEQL`0915$`09`09; SKIP BASH IF NOT
X`09MOVL`09UCB$PPID(R5),IRP$L_PID(R3)`09;RESTORE THE OLD PID
X; since we may now have later parts of virtual, paging, or swapping I/O
X; to do, restore saved byte counts and function codes.
X`09movl`09ucb$obct(r5),irp$l_obcnt(r3)`09;restore orig byte cnt
X;`09movl`09ucb$owind(r5),irp$l_wind(r3)`09;restore window pointer
X`09movl`09ucb$osegv(r5),irp$l_segvbn(r3)`09;restore segment vbn also
X`09brb`091501$
X15$:
X`09.if`09df,x$$$dt
X;mousetrap if we EVER see 0 saved PID!!
X`09jsb`09g`5Eini$brk
X`09.endc
X`09clrl`09irp$l_pid(r3)`09;make sure we DON'T get back here anyway!
X; this is actually an error condition and should NEVER occur...
X`09movl`09ucb$obct(r5),irp$l_obcnt(r3)`09;restore orig byte cnt
X1501$:
X`09.if`09df,x$$$dt
X`09cmpl`09irp$l_bcnt(r3),irp$l_obcnt(r3)
X`09beql`0917$`09`09;branch if no split I/O going on
X`09movl`09irp$l_segvbn(r3),r2`09;get segvbn
X`09movl`09irp$l_media(r3),r1`09;and media fields
X`09jsb`09g`5Eini$brk
X17$:
X`09.endc
X`09CLRL`09UCB$PPID(R5)`09; ZERO SAVED PID FIELD FOR CLEANLINESS
X`09MOVL`09R5,IRP$L_UCB(R3)`09;RESTORE VD: AS UCB IN IRP TOO
X; GRAB R0 AND R1 AS REQCOM IN HOST DRIVER LEFT THEM...
X`09MOVL`09IRP$L_MEDIA(R3),R0`09;GET BACK R0
X`09MOVL`09IRP$L_MEDIA+4(R3),R1`09;AND R1
X; R0, R1 ARE AS HOST DRIVER LEFT THEM. R5 POINTS TO CORRECT UCB.
X;  ===> GO FOR IT !!!
X;
X; Now restore the original IRP$L_MEDIA field of the IRP in case error
X; paths in IOC$REQCOM ever need it. Some very low XQP cache situations
X; may occasionally need this, though in reasonable sysgen configs it
X; should never be needed. This is the one area that got bashed during
X; the earlier I/O completion processing in the host driver.
X;
X`09MOVL`09UCB$LMEDIA(R5),IRP$L_MEDIA(R3)
X;
X; notice that for virtual I/O, the IRP's IRP$L_SEGVBN longword still
X; has the starting VIRTUAL block number of the I/O request in the context
X; of the virtual disk. This must be present as any second and later parts
X; of the I/O request modify that field to compute where to go for the
X; next I/O. Due to getting back here, the host driver need never know
X; about this; it is basically doing ONLY physical and logical I/O where
X; this sort of completion jiggery-pokery does not occur.
X;`09`09- GCE
X; Now go REALLY complete the I/O (possibly causing more I/O and certainly
X; ensuring the VD: I/O queue is emptied and VD: unbusied after all is done.)
X`09JSB`09@#IOC$REQCOM`09; GO COMPLETE THE I/O REQUEST IN VD: CONTEXT
X; (OR DO I/O SPLIT NEXT PART IN VD: CONTEXT!)
X; ALSO, RETURN **HERE**, SO WE CAN WRAP UP ALL ELSE.
X;`09.IF`09NDF,VMS$V5
X;`09ENBINT`09`09; RESTORE IPL TO IPL$_IOPOST
X;`09.IFF
X;`09FORKUNLOCK`09NEWIPL=(SP)+
X;`09.ENDC
X; rsb exits the fork level.
X; IPL4 level exited at fork above, with stack intact at that point.
X; iopost saves/restores regs, so r5 bash is ok.
X`09RSB`09; GET BACK TO HOST SOMETIME
X;
X;ADDRESS OF LAST LOCATION IN DRIVER
X;
XVD_END:
X`09.END
$ CALL UNPACK VDDRIVERALPHA.MAR;13 1009677393
$ v=f$verify(v)
$ EXIT
