From:	CSBVAX::MRGATE!gkn@M5.Sdsc.Edu@SMTP 29-MAY-1988 05:24
To:	ARISIA::EVERHART
Subj:	RAMDRIVER


Received: from M5.Sdsc.Edu by KL.SRI.COM with TCP; Thu 26 May 88 10:03:25-PDT
Date:    Thu, 26 May 88 16:40:16 GMT
From:     gkn@M5.Sdsc.Edu (Gerard K. Newman)
Message-Id: <880526164016.202000cb@M5.Sdsc.Edu>
Subject: RAMDRIVER
To:       info-vax@kl.sri.com
X-ST-Vmsmail-To: ST%"Info-VAX@KL.SRI.COM"

Well, from what I can tell my RAM disk driver works.  It's a lot faster
than PDDRIVER, and it's faster than some internal DEC RAM disk driver
called FDDRIVER.

Assemble & link it thusly:

	$ Macro RAMDRIVER
	$ Link/System=0/Header RAMDRIVER

No warranty, expressed or implied.  Use at your own risk.  It doesn't
crash systems or corrupt data, or anything like that, but I am in no
position to support it or be responsible for pathological cases or
configurations.

RAMDRIVER doesn't run on VMS V5 systems yet.  When I have V5 here it
will, and I will post an update.

gkn
----------------------------------------
Internet: GKN@SDS.SDSC.EDU
Bitnet:   GKN@SDSC
Span:	  SDSC::GKN (27.1)
MFEnet:   GKN@SDS
USPS:	  Gerard K. Newman
	  San Diego Supercomputer Center
	  P.O. Box 85608
	  San Diego, CA 92138-5608
Phone:	  619.534.5076

........................ Cut between dotted lines and save ......................
$!..............................................................................
$! VAX/VMS archive file created by VMS_SHAR V-5.01 01-Oct-1987
$! which was written by Michael Bednarek (U3369429@ucsvc.dn.mu.oz.au)
$! To unpack, simply save and execute (@) this file.
$!
$! This archive was created by GKN
$! on Thursday 26-MAY-1988 16:30:14.66
$!
$! It contains the following 1 file:
$! RAMDRIVER.MAR
$!==============================================================================
$ Set Symbol/Scope=(NoLocal,NoGlobal)
$ Version=F$GetSYI("VERSION") ! See what VMS version we have here:
$ If Version.ges."V4.4" then goto Version_OK
$ Write SYS$Output "Sorry, you are running VMS ",Version, -
                ", but this procedure requires V4.4 or higher."
$ Exit 44
$Version_OK: CR[0,8]=13
$ Pass_or_Failed="failed!,passed."
$ Goto Start
$Convert_File:
$ Read/Time_Out=0/Error=No_Error1/Prompt="creating ''File_is'" SYS$Command ddd
$No_Error1: Define/User_Mode SYS$Output NL:
$ Edit/TPU/NoSection/NoDisplay/Command=SYS$Input/Output='File_is' -
        VMS_SHAR_DUMMY.DUMMY
f:=Get_Info(Command_Line,"File_Name");b:=Create_Buffer("",f);
o:=Get_Info(Command_Line,"Output_File");Set(Output_File,b,o);
Position(Beginning_of(b));Loop x:=Erase_Character(1);Loop ExitIf x<>"V";
Move_Vertical(1);x:=Erase_Character(1);Append_Line;
Move_Horizontal(-Current_Offset);EndLoop;Move_Vertical(1);
ExitIf Mark(None)=End_of(b) EndLoop;Position(Beginning_of(b));Loop
x:=Search("`",Forward,Exact);ExitIf x=0;Position(x);Erase_Character(1);
If Current_Character='`' then Move_Horizontal(1);else
Copy_Text(ASCII(INT(Erase_Character(3))));EndIf;EndLoop;Exit;
$ Delete VMS_SHAR_DUMMY.DUMMY;*
$ Checksum 'File_is
$ Success=F$Element(Check_Sum_is.eq.CHECKSUM$CHECKSUM,",",Pass_or_Failed)+CR
$ Read/Time_Out=0/Error=No_Error2/Prompt=" CHECKSUM ''Success'" SYS$Command ddd
$No_Error2: Return
$Start:
$ File_is="RAMDRIVER.MAR"
$ Check_Sum_is=309999102
$ Copy SYS$Input VMS_SHAR_DUMMY.DUMMY
X`009.Title`009RAMDriver`009- RAM disk driver for VAX/VMS
X`009.Ident`009/V01.000/
X`009.Enable`009SUP
X`009.Default Displacement,Word
X`009.Subtitle`009Introduction
X
X;+
X;
X; ----- RAMDriver:  RAM disk driver for VAX/VMS
X;
X;
X; Facility:
X;
X;`009VAX/VMS executive, I/O subsystem.
X;
X; Abstract:
X;
X;`009This module implements a RAM disk driver, whereby a collection
X;`009of pages from non-paged pool behave as though they were a disk
X;`009drive.  The  driver is $QIO compatible  with the  DEC supplied
X;`009PDDRIVER but  is much more  efficient since it does all of its
X;`009work at  IPL$_ASTDEL rather  than IPL$_SYNCH  (it uses a MUTEX
X;`009for synchronization).
X;
X; Environment:
X;
X;`009VAX/VMS native mode, VMS V4.4 or later, resident, kernel mode,
X;`009IPL$_ASTDEL and above.
X;
X;
X;
X; Version:`009V01.000
X; Date:`009`00912-Mar-1988
X;
X; Copyright `169 1988  San Diego Supercomputer Center
X;
X; Gerard K. Newman`00912-Mar-1988
X; San Diego Supercomputer Center
X; General Atomics
X; P.O. Box 85608
X; San Diego, CA  92138-5608
X; 619.534.5076
X;
X; Internet:`009GKN@SDS.SDSC.EDU
X; BITNET:`009GKN@SDSC.BITNET
X; SPAN:`009`009SDSC::GKN (27.1)
X; MFENET:`009GKN@SDS.MFENET
X; SDSCNET:`009GKN@SDS.SDSCNET
X;
X;
X; Modifications:
X;
X;
X;-
X
X`009.Page
X`009.Subtitle`009Local definitions
X
V`009.Link`009 "SYS$SYSTEM:SYS.STB"/Selective_Search`009;Grab the system symbol 
Xtable
X`009.Library "SYS$LIBRARY:LIB.MLB"`009`009`009;Get special macros from here
X
X`009.NoCross`009`009`009;Save a tree
X
X`009$ACBDEF`009`009`009`009;AST control block offsets
X`009$CRBDEF`009`009`009`009;Controller request block definitions
X`009$DCDEF`009`009`009`009;Device class & type definitions
X`009$DDBDEF`009`009`009`009;Device data block definitions
X`009$DDTDEF`009`009`009`009;Driver dispatch table offsets
X`009$DEVDEF`009`009`009`009;Device independent status bits
X`009$DPTDEF`009`009`009`009;Driver prologue table offsets
X`009$DYNDEF`009`009`009`009;Nonpaged pool data structure type codes
X`009$IDBDEF`009`009`009`009;Interrupt dispatch block offsets
X`009$IODEF`009`009`009`009;I/O function codes
X`009$IPLDEF`009`009`009`009;Interrupt priority levels
X`009$IRPDEF`009`009`009`009;I/O request packet definitions
X`009$ORBDEF`009`009`009`009;Object rights block definitions
X`009$SSDEF`009`009`009`009;System service codes
X`009$UCBDEF`009`009`009`009;UCB offsets
X`009$VECDEF`009`009`009`009;Interrupt transfer vector offsets
X
X`009.Cross`009`009`009`009;Turn CREF back on
X
X
X; Local definitions
X
XRAMD$K_IPL`009=`009IPL$_SYNCH`009;Synchronization IPL
X
XP1`009`009=`0090`009`009;QIO parameter P1
XP2`009`009=`0094`009`009;QIO parameter P2
X
X
X; UCB offsets which follow the standard disk UCB.
X
X$DEFINI`009UCB`009`009`009`009;Start of the UCB offsets
X
X.`009=`009UCB$K_LCL_DISK_LENGTH`009;Start after the standard disk crap
X
X$DEF`009UCB$L_RAMD_BUFF`009 .Blkl`009`009;Address of the disk buffer
X$DEF`009UCB$L_RAMD_MUTEX .Blkl`009`009;Disk access MUTEX
X$DEF`009UCB$K_RAMD_LENG`009`009`009;Length of a RAM disk UCB
X
X$DEFEND`009UCB`009`009`009`009;End of the UCB offsets
X
X`009.Page
X`009.Subtitle`009Standard device driver tables
X
X
X; Driver prologue table.
X
X`009DPTAB`009-`009`009`009   ;Driver prologue table
X`009`009End`009 = RAMD_END,-`009   ;End of the driver
X`009`009Adapter`009 = NULL,-`009   ;No adapter (no hardware)
X`009`009MaxUnits = 1,-`009`009   ;Only 1 unit
X`009`009Unload`009 = RAMD_UNLOAD,-   ;Unload routine
X`009`009Name`009 = RAMDRIVER,-`009   ;Driver name
X`009`009UCBSize`009 = UCB$K_RAMD_LENG ;UCB size
X
X
X; Initialization table.
X
X`009DPT_Store INIT`009`009`009;Initialization table
X
X; Device data block (DDB).
X
X`009DPT_Store DDB,DDB$L_ACPD,L,<^a/F11/>`009;Default ACP name
X`009DPT_Store DDB,DDB$L_ACPD+3,B,DDB$K_SLOW`009;ACP class
X
X; Unit control block (UCB).
X
X`009DPT_Store UCB,UCB$B_FIPL,B,RAMD$K_IPL`009;Fork IPL
X`009DPT_Store UCB,UCB$B_DIPL,B,RAMD$K_IPL`009;Device IPL
X`009DPT_Store UCB,UCB$L_DEVCHAR,L,<-`009;Device characteristics:
X`009`009`009DEV$M_FOD!-`009`009;   File oriented
X`009`009`009DEV$M_DIR!-`009`009;   Directory structured
X`009`009`009DEV$M_AVL!-`009`009;   Available
X`009`009`009DEV$M_SHR!-`009`009;   Shareable
X`009`009`009DEV$M_IDV!-`009`009;   Capable of input
X`009`009`009DEV$M_ODV!-`009`009;   Capable of output
X`009`009`009DEV$M_RND>`009`009;   Random access
X`009DPT_Store UCB,UCB$L_DEVCHAR2,L,<-`009;More device characteristics:
X`009`009`009DEV$M_NNM>`009`009;   Prefix the name with "node$"
X`009DPT_Store UCB,UCB$W_DEVSTS,W,<-`009`009;Device status:
X`009`009`009UCB$M_NOCNVRT>`009`009;   No LBN conversion
X`009DPT_Store UCB,UCB$B_DEVCLASS,B,DC$_DISK`009;It's a disk
X`009DPT_Store UCB,UCB$B_DEVTYPE,B,DT$_ML11`009;It's a funny looking disk
X`009DPT_Store UCB,UCB$W_DEVBUFSIZ,W,512`009;Buffer size
X`009DPT_Store UCB,UCB$B_TRACKS,B,1`009`009;1 track
X`009DPT_Store UCB,UCB$B_SECTORS,B,1`009`009;1 sector
X
X; Reinitialization table.
X
X`009DPT_Store REINIT`009`009`009;Reinitialization table
X
X; Controller request block (CRB).
X
V`009DPT_Store CRB,CRB$L_INTD+VEC$L_INITIAL,D,RAMD_CTRL_INIT`009 ;Controller ini
Xtialization
V`009DPT_Store CRB,CRB$L_INTD+VEC$L_UNITINIT,D,RAMD_UNIT_INIT ;Unit initializati
Xon
X
X; Device data block (DDB).
X
X`009DPT_Store DDB,DDB$L_DDT,D,RAMD$DDT`009;Driver dispatch table address
X
X`009DPT_Store END`009`009`009`009;End of the initialization tables.
X
X
X; Driver dispatch table.
X
X`009DDTAB`009-`009`009`009 ;Driver dispatch table
X`009`009DevNam`009= RAMD,-`009 ;Device name
X`009`009Start`009= RAMD_STARTIO,- ;Start I/O
V`009`009Unsolic`009= 0,-`009`009 ;No unsolicited interrupt service (no interrup
Xts)
X`009`009FuncTB`009= RAMD_FUNCTAB,- ;Function decision table address
X`009`009Cancel`009= 0,-`009`009 ;No cancel I/O
X`009`009RegDmp`009= 0,-`009`009 ;Register dump (no registers!)
X`009`009DiagBf`009= 0,-`009`009 ;No diagnostic bufer
X`009`009ErlgBf`009= 0`009`009 ;No error logging buffer
X
X
X; Function decision table.
X
XRAMD_FUNCTAB:`009`009`009`009;Function decision table.
X
X; Legal functions.
X
X`009FuncTab`009,<-`009`009`009;Legal functions:
X`009`009UNLOAD,-`009`009;   Unload
X`009`009PACKACK,-`009`009;   Pack acknowlege
X`009`009AVAILABLE,-`009`009;   Drive available
X`009`009SENSECHAR,-`009`009;   Sense characteristics
X`009`009SENSEMODE,-`009`009;   Sense mode
X`009`009FORMAT,-`009`009;   Format (special)
X`009`009READVBLK,-`009`009;   Read virtual
X`009`009READLBLK,-`009`009;   Read logical
X`009`009READPBLK,-`009`009;   Read physical
X`009`009WRITEVBLK,-`009`009;   Write virtual
X`009`009WRITELBLK,-`009`009;   Write logical
X`009`009WRITEPBLK,-`009`009;   Write physical
X`009`009ACCESS,-`009`009;   Access / lookup
X`009`009ACPCONTROL,-`009`009;   XQP control, actually
X`009`009CREATE,-`009`009;   Create / enter
X`009`009DEACCESS,-`009`009;   Deaccess
X`009`009DELETE,-`009`009;   Delete
X`009`009MODIFY,-`009`009;   Modify
X`009`009MOUNT>`009`009`009;   Mount volume
X
X; Buffered functions.
X
X`009FuncTab`009,<-`009`009`009;Buffered functions:
X`009`009UNLOAD,-`009`009;   Unload
X`009`009PACKACK,-`009`009;   Pack acknowlege
X`009`009AVAILABLE,-`009`009;   Drive available
X`009`009SENSECHAR,-`009`009;   Sense characteristics
X`009`009SENSEMODE,-`009`009;   Sense mode
X`009`009FORMAT,-`009`009;   Format (special)
X`009`009ACCESS,-`009`009;   Access / lookup
X`009`009ACPCONTROL,-`009`009;   XQP control, actually
X`009`009CREATE,-`009`009;   Create / enter
X`009`009DEACCESS,-`009`009;   Deaccess
X`009`009DELETE,-`009`009;   Delete
X`009`009MODIFY,-`009`009;   Modify
X`009`009MOUNT>`009`009`009;   Mount volume
X
X; Direct functions and FDT mapping.
X
X`009FuncTab`009RAMD_STASH,<-`009`009;Stash buffer addresss for:
X`009`009READVBLK,-`009`009;   Read virtual
X`009`009READLBLK,-`009`009;   Read logical
X`009`009READPBLK,-`009`009;   Read physical
X`009`009WRITEVBLK,-`009`009;   Write virtual
X`009`009WRITELBLK,-`009`009;   Write logical
X`009`009WRITEPBLK>`009`009;   Write physical
X`009FuncTab`009+ACP$READBLK,<-`009`009;Read functions:
X`009`009READVBLK,-`009`009;   Read virtual
X`009`009READLBLK,-`009`009;   Read logical
X`009`009READPBLK>`009`009;   Read physical
X`009FuncTab`009+ACP$WRITEBLK,<-`009;Write functions:
X`009`009WRITEVBLK,-`009`009;   Write virtual
X`009`009WRITELBLK,-`009`009;   Write logical
X`009`009WRITEPBLK>`009`009;   Write physical
X`009FuncTab`009+ACP$ACCESS,<-`009`009;Access functions:
X`009`009ACCESS,-`009`009;   Access
X`009`009CREATE>`009`009`009;   Create
X`009FuncTab`009+ACP$DEACCESS,<-`009;Deaccess function:
X`009`009DEACCESS>`009`009;   Deaccess
X`009FuncTab`009+ACP$MODIFY,<-`009`009;Modify functions:
X`009`009ACPCONTROL,-`009`009;   ACP control
X`009`009DELETE,-`009`009;   Delete
X`009`009MODIFY>`009`009`009;   Modify
X`009FuncTab`009+ACP$MOUNT,<-`009`009;Mount function:
X`009`009MOUNT>`009`009`009;   Mount volume
X`009FuncTab`009RAMD_PACKACK,<-`009`009;Pack acknowlege function:
X`009`009PACKACK>`009`009;   Pack acknowlege
X`009FuncTab`009RAMD_UNL_AVL,<-`009`009;Unload functions:
X`009`009UNLOAD,-`009`009;   Unload
X`009`009AVAILABLE>`009`009;   Available
X`009FuncTab`009RAMD_FORMAT,<-`009`009;Format function:
X`009`009FORMAT>`009`009`009;   Format (special)
X`009FuncTab`009+EXE$SENSEMODE,<-`009;Sense functions
X`009`009SENSEMODE,-`009`009;   Sense mode
X`009`009SENSECHAR>`009`009;   Sense characteristics
X
X`009.Page
X`009.Subtitle`009RAMD_UNLOAD`009- Driver unload routine
X
X;+
X;
X; ----- RAMD_UNLOAD:  Driver unload routine
X;
X;
X; This routine is called in response to the SYSGEN UNLOAD command.
X;
X; This driver is not unloadable.
X;
X; Environment:
X;
X;`009IPL$_POWER, process context.
X;
X; Inputs:
X;
X;`009None.
X;
X; Outputs:
X;
X;`009None.
X;
X;-
X
XRAMD_UNLOAD:`009`009`009`009;Driver unload routine
X
X`009RSB`009`009`009`009;Driver is not unloadable.
X
X`009.Page
X`009.Subtitle`009RAMD_CTRL_INIT`009- Controller initialization
X
X;+
X;
X; ----- RAMD_CTRL_INIT:  Controller initialization
X;
X;
X; This routine is called to perform controller specific initialization
X; during the SYSGEN LOAD or RELOAD  commands and  during power failure
X; recovery.  Since  there isn't a  controller this  routine doesn't do
X; a whole lot.
X;
X; Environment:
X;
X;`009IPL$_POWER, system context.
X;
X; Inputs:
X;
X;`009R5`009- IDB address
X;
X; Outputs:
X;
X;`009Not much.
X;
X;-
X
XRAMD_CTRL_INIT:`009`009`009`009;Controller initialization
X
X`009CLRL`009IDB$L_CSR(R5)`009`009;No CSR
X`009RSB`009`009`009`009;Done
X
X`009.Page
X`009.Subtitle`009RAMD_UNIT_INIT`009- Unit initialization
X
X;+
X;
X; ----- RAMD_UNIT_INIT:  Unit initialization
X;
X;
X; This routine is called during the SYSGEN LOAD command (but not
X; RELOAD) and during  power failure recovery.  We set the online
X; bit in the UCB, initialize the MUTEX and return.
X;
X; Environment:
X;
X;`009IPL$_POWER, system context.
X;
X; Inputs:
X;
X;`009R5`009- UCB address
X;
X; Outputs:
X;
X;`009Unit set online.
X;
X;-
X
XRAMD_UNIT_INIT:`009`009`009`009     ;Unit initialization
X
X`009BISW`009#UCB$M_ONLINE,UCB$W_STS(R5)  ;We're on the air
X`009MOVL`009#^xFFFF,UCB$L_RAMD_MUTEX(R5) ;Initialize the MUTEX
X`009RSB`009`009`009`009     ;Done.
X
X`009.Page
X`009.Subtitle`009RAMD_PACKACK`009- Handle IO$_PACKACK
X
X;+
X;
X; ----- RAMD_PACKACK:  Handle IO$_PACKACK
X;
X;
X; This routine is called during FDT processing to handle IO$_PACKACK.
X; We set the valid bits in the UCB and return.
X;
X; Environment:
X;
X;`009IPL$_ASTDEL, process context.
X;
X; Inputs:
X;
X;`009R5`009- UCB address
X;`009R3`009- IRP address
X;
X; Outputs:
X;
X;`009Volume valid changed.
X;
X;-
X
XRAMD_PACKACK:`009`009`009`009;Handle IO$_PACKACK
X
X`009BBSS`009#UCB$V_LCL_VALID,UCB$L_STS(R5),10$ ;Already valid?
X`009BISW`009#UCB$M_VALID,UCB$L_STS(R5)`009   ;No, it is now.
X`009INCB`009UCB$B_ONLCNT(R5)`009`009   ;Another system has this one on line
X
X10$:`009MOVL`009#SS$_NORMAL,R0`009`009;Success!
X`009JMP`009G^EXE$FINISHIOC`009`009;Post the I/O request complete
X
X`009.Page
X`009.Subtitle`009RAMD_UNL_AVL`009- Handle IO$_UNLOAD and IO$_AVAILABLE
X
X;+
X;
X; ----- RAMD_UNL_AVL:  Handle IO$_UNLOAD and IO$_AVAILABLE
X;
X;
X; This routine is called as an FDT routine for IO$_UNLOAD and IO$_AVAILABLE.
X; We clear the volume valid bits and post the I/O request complete.
X;
X; Environment:
X;
X;`009IPL$_ASTDEL, process context.
X;
X; Inputs:
X;
X;`009R5`009- UCB address
X;`009R3`009- IRP address
X;
X; Outputs:
X;
X;`009Volume valid cleared.
X;
X;-
X
XRAMD_UNL_AVL:`009`009`009`009;Handle IO$_UNLOAD and IO$_AVAILABLE
X
X`009BBCC`009#UCB$V_LCL_VALID,UCB$L_STS(R5),10$ ;Volume valid?
X`009BICW`009#UCB$M_VALID,UCB$W_STS(R5)`009   ;Volume no longer valid
X`009DECB`009UCB$B_ONLCNT(R5)`009`009   ;Another system has let go of this one
X
X10$:`009MOVL`009#SS$_NORMAL,R0`009`009;Success!
X`009JMP`009G^EXE$FINISHIOC`009`009;Post the I/O request complete.
X
X`009.Page
X`009.Subtitle`009RAMD_FORMAT`009- Handle IO$_FORMAT
X
X;+
X;
X; ----- RAMD_FORMAT:  Handle IO$_FORMAT
X;
X;
X; This routine is called as an FDT routine for IO$_FORMAT, which is
X; used to allocate and deallocate nonpaged pool which comprises the
X; blocks of  the "disk".  P1 is the new size of the disk.  We first
X; deallocate the  existing chunk (all data is lost), and allocate a
X; chunk of the new size.
X;
X; Environment:
X;
X;`009IPL$_ASTDEL, process context.
X;
X; Inputs:
X;
X;`009AP`009- Address of P1
X;`009R5`009- UCB address
X;`009R3`009- IRP address
X;
X; Outputs:
X;
X;`009Disk size changed.
X;
X;-
X
XRAMD_FORMAT:`009`009`009`009;Handle IO$_FORMAT
X
X`009PUSHL`009R3`009`009`009;Keep the IRP safe
X
X; Lock the disk MUTEX for write access.
X
X`009MOVAB`009UCB$L_RAMD_MUTEX(R5),R0`009;Address the MUTEX
X`009JSB`009G^SCH$LOCKW`009`009;Lock the MUTEX for write access.
X
X; Ensure that the disk isn't mounted.
X
X`009MOVL`009#SS$_DEVMOUNT,R0`009`009 ;Presume failure
X`009BBS`009#DEV$V_MNT,UCB$L_DEVCHAR(R5),40$ ;Branch if it's mounted.
X
X; Next deallocate the existing buffer.
X
X`009MOVL`009UCB$L_RAMD_BUFF(R5),R0`009   ;Grab the existing buffer
X`009BEQL`00920$`009`009`009   ;None there, that was easy.
X`009MULL3`009#512,UCB$L_MAXBLOCK(R5),R1 ;Grab the size to deallocate
X`009BEQL`00910$`009`009`009   ;Eh?
X`009JSB`009G^EXE$DEANONPGDSIZ`009   ;Deallocate the block.
X
X10$:`009CLRL`009UCB$L_RAMD_BUFF(R5)`009;No more buffer.
X
X; Allocate a new buffer.
X
X20$:`009MOVL`009#SS$_NORMAL,R0`009`009  ;Presume no new disk to allocate
X`009MOVL`009P1(AP),UCB$L_MAXBLOCK(R5) ;New size to allocate
X`009BEQL`00940$`009`009`009  ;If EQL that was easy
X`009MULL3`009#512,P1(AP),R1`009`009  ;Here's the size we want
X`009JSB`009G^EXE$ALONPAGVAR`009  ;Allocate some pool
X`009BLBS`009R0,30$`009`009`009  ;Win!
X`009MOVZWL`009#SS$_INSFMEM,R0`009`009  ;Lose.
X
X30$:`009MOVL`009R2,UCB$L_RAMD_BUFF(R5)`009;Stash the block address
X
X40$:`009MOVW`009UCB$L_MAXBLOCK(R5),UCB$W_CYLINDERS(R5)`009;Cylinders = blocks
X`009PUSHL`009R0`009`009`009`009`009;Stash the final status.
X`009MOVAB`009UCB$L_RAMD_MUTEX(R5),R0`009`009`009;Address our MUTEX
X`009JSB`009G^SCH$UNLOCK`009`009`009`009;Unlock same.
X`009POPR`009#^m<R0,R3>`009`009`009`009;Restore the status & IRP
X`009JMP`009G^EXE$FINISHIOC`009`009`009`009;Done
X
X`009.Page
X`009.Subtitle`009RAMD_STASH`009- Stash the virtual address of the I/O buffer
X
X;+
X;
X; ----- RAMD_STASH:  Stash the virtual address of the I/O buffer
X;
X;
X; This routine is called as an FDT routine to stash the virtual
X; address of the user's I/O buffer in the IRP so that RAMD_KAST
X; knows  where to move data to or from.  All of the rest of the
X; FDT processing will be done in ACP$READBLK or ACP$WRITEBLK.
X;
X; Environment:
X;
X;`009IPL$_ASTDEL, process context.
X;
X; Inputs:
X;
X;`009AP`009- Address of the QIO parameter list
X;`009R3`009- IRP address
X;
X; Outputs:
X;
X;`009P1(AP) stashed in IRP$L_VAL1.
X;
X;-
X
XRAMD_STASH:`009`009`009`009;Stash the virtual address of the I/O buffer
X
X`009MOVL`009P1(AP),IRP$L_VAL1(R3)`009;Stash the buffer's virtual address
X`009RSB`009`009`009`009;Back to the FDT dispatcher
X
X`009.Page
X`009.Subtitle`009RAMD_STARTIO`009- Start I/O routine
X
X;+
X;
X; ----- RAMD_STARTIO:  Start I/O routine
X;
X;
X; This  routine is called  as a fork process to start an I/O request.
X; The only requests which get here are reads and writes (both logical
X; and physical).
X;
X; Recall  that we're  at IPL$_SYNCH and  do not have process context.
X; The  only way we  can move  data to  or from user space is to use a
X; system  virtual  page to  double  map the  user's  buffer  and call
X; IOC$MOVxUSER,  which is very slow and is doubly bad because it must
X; execute at  IPL$_SYNCH.  This  is what  DEC's PDDRIVER does (and is
X; why that driver may not be a win on a busy system).
X;
X; A better solution is  to execute with process context, where we can
X; run at IPL$_ASTDEL and use a MOVC3 instruction to transfer the data
X; to user space.  Normally one would do this sort of thing in the FDT
X; routines and  simply bypass the driver start I/O routine.  However,
X; due to  the complex nature  of the FDT processing required for file
X; oriented devices it is desirable to let DEC's routines do it. Those
X; routines  (in SYSACPFDT) exit  by queueing the  IRP to the driver's
X; start I/O routine, so we're almost (but not quite) stuck.
X;
X; So, what we must do is to get back our lost process context.  We do
X; this by  queueing a kernel mode AST back to the requesting process.
X; The AST  routine (below) will transfer  the data  and then post the
X; I/O request complete.
X;
X; Since we us a MUTEX for synchronization, we can allow multiple read
X; requests to execute in parallel (since we can lock a MUTEX for read
X; or write access).  In order for this to happen the device must look
X; like it is not busy to IOC$INITIATE when it's called to start up an
X; I/O request.  We do this by clearing the busy bit in the UCB before
X; we queue the AST.
X;
X; Environment:
X;
X;`009IPL$_SYNCH, fork context.
X;
X; Inputs:
X;
X;`009R5`009- UCB address
X;`009R3`009- IRP address
X;
X; Outputs:
X;
X;`009IRP used as an ACB and a kernel mode AST is queued to the
X;`009requesting process to complete the I/O request.
X;
X;-
X
XRAMD_STARTIO:`009`009`009`009`009;Start I/O routine
X
X`009BICW`009#UCB$M_BSY,UCB$W_STS(R5)`009;Device must not look busy
X`009MOVL`009IRP$L_WIND(R3),IRP$L_VAL2(R3)`009;Keep the WCB address safe
V`009MOVL`009R3,R5`009`009`009`009;Copy the IRP address and make believe it's an
X ACB
X`009BISB`009#ACB$M_KAST,ACB$B_RMOD(R5)`009;Say we're a kernel AST
X`009MOVAB`009RAMD_KAST,ACB$L_KAST(R5)`009;Execute this routine
V`009CLRL`009R2`009`009`009`009;No priority increment (could use PRI$_IOCOM, I g
Xuess)
X`009JMP`009G^SCH$QAST`009`009`009;Queue the AST and return
X
X; Ok - we're back at IPL$_ASTDEL with (the correct one, even!) process context.
X; R0-R5 are available for use, and R5 is the address of the IRP (er, ACB).  The
X; user's  buffer address is in  IRP$L_VAL1 (thanks  to RAMD_STASH), and the PCB
X; address is in R4 (thanks to the AST dispatcher).
X
XRAMD_KAST:`009`009`009`009;Ref. label
X
V`009MOVL`009R5,R3`009`009`009;Put the IRP where most everything expects to find
X it
X`009MOVL`009IRP$L_UCB(R3),R5`009;Address our UCB
X
X; Decide if it's physical or logical I/O.
X
X`009MULL3`009#512,IRP$L_MEDIA(R3),R1`009`009;Presume logical I/O
X`009BBC`009#IRP$V_PHYSIO,IRP$W_STS(R3),10$`009;Branch if it's physical I/O
X
V; Physical I/O ... Byte offset = Sector size * ((Sectors per track) * Track + S
Xector)
X
X`009MOVZBL`009UCB$B_SECTORS(R5),R1`009;Grab the sectors/track
X`009MULW`009IRP$L_MEDIA+2(R3),R1`009;Multiply by track number
X`009ADDW`009IRP$L_MEDIA(R3),R1`009;Add in the sector
X`009DECL`009R1`009`009`009;Make it zero based
X`009MULL`009#512,R1`009`009`009;Multiply by the sector size
X
X; Ok - now we have the byte offset into the disk buffer in R1.
X
V10$:`009ADDL`009UCB$L_RAMD_BUFF(R5),R1`009;Point to the right spot in the disk 
XbUffer
X`009MOVAB`009UCB$L_RAMD_MUTEX(R5),R0`009;Address our MUTEX
X`009PUSHR`009#^m<R3,R4,R5>`009`009;Save volitile registers
X`009
X; Now lock the MUTEX based on the function type.
X
X`009BBC`009#IRP$V_FUNC,IRP$W_STS(R3),20$`009    ;Read or write?
X
X; Read ... disk to user.
X
X`009JSB`009G^SCH$LOCKR`009`009`009    ;Lock the MUTEX for read access
X`009MOVC3`009IRP$L_BCNT(R3),(R1),@IRP$L_VAL1(R3) ;"Read" the data
X`009BRB`00930$`009`009`009`009    ;Join common code
X
X; Write ... user to disk.
X
X20$:`009JSB`009G^SCH$LOCKW`009`009`009    ;Lock the MUTEX for write access
X`009MOVC3`009IRP$L_BCNT(R3),@IRP$L_VAL1(R3),(R1) ;"Write" the data
X
X; Here to post the I/O request complete.
X
X30$:`009MOVQ`0094(SP),R4`009`009`009;Restore the PCB and UCB addresses
X`009MOVAB`009UCB$L_RAMD_MUTEX(R5),R0`009`009;Address our MUTEX
X`009JSB`009G^SCH$UNLOCK`009`009`009;Unlock it.
X`009POPR`009#^m<R3,R4,R5>`009`009`009;Restore the IRP, PCB and UCB addresses
X`009MOVL`009IRP$L_VAL2(R3),IRP$L_WIND(R3)`009;Restore the window pointer
X`009MOVZWL`009IRP$L_BCNT(R3),R0`009`009;Grab the byte count
V`009ADDL`009R0,IRP$L_VAL1(R3)`009`009;Update the buffer pointer in case this is
X segmented
X`009ASHL`009#16,R0,R0`009`009`009;Shift the byte count up to the high word
X`009MOVW`009#SS$_NORMAL,R0`009`009`009;Success
X`009CLRL`009R1`009`009`009`009;No further status
X`009SETIPL`009UCB$B_FIPL(R5)`009`009`009;Up to fork IPL to modify
X`009MOVL`009R3,UCB$L_IRP(R5)`009`009; the IRP field in the UCB
X`009REQCOM`009`009`009`009`009;Post the request complete and look for more.
X
X`009.Page
X`009.Subtitle`009RAMD_END`009- End of the driver (for SYSGEN)
X
XRAMD_END:`009`009`009`009;Mark the end of the driver for SYSGEN
X
X
X
X
X`009.End
$ GoSub Convert_File
$ Exit
