+-+-+-+ Beginning of part 5 +-+-+-+ X`009movq`009zta0_dsc,r0`009`009`009; load default descriptor X30$: X`009cmpw`009r0,#zt_name_size`009`009; length ok? X`009bleq`00935$`009`009`009`009; br if yes X`009movl`009#ss$_ivdevnam,r0`009`009; no, device name too long ... X`009chkr0`009`009`009`009`009; ... abort! X35$: X`009movw`009r0,zt_namedsc`009`009`009; store length X`009movc3`009r0,(r1),zt_name`009`009`009; copy ZT name`032 X; X`009$setprv_s`009prvprv=curprivs`009`009; save current privileges X`009chkr0 X; X`009$lkwset_s`009inadr=lkwdata_inadr`009; lock "nonpaged" data X`009chkr0 X; X.if ne EVAX X`009$lock_page_init`009error=lock_page_error X.iff X`009$lkwset_s`009inadr=lkwcode_inadr`009; lock "nonpaged" code X.endc X`009chkr0 X; X`009calls`009#0,get_mbx`009`009`009; create mbx etc. X`009chkr0 X; X`009calls`009#0,read_mbx`009`009`009; post read on mbx X`009chkr0 X; X`009calls`009#0,setup_exith`009`009`009; set up exit handler X`009chkr0 X; X.if ne EVAX X`009$cmkrnl_s`009routin=k_setup`009`009; start ZT: X.iff X`009$cmkrnl_s`009routin=k_setup,-`009; start zt: X`009`009`009arglst=(ap) X.endc X`009ret X; X.if ne EVAX Xlock_page_error: X`009pushl`009r0 X`009calls`009#1,g`094lib$stop X`009ret X.endc X; X; X;*****`009wait for MBX X; X`009.psect`009_code X.entry`009ZT_WAIT,`094m<> X; X`009calls`009#0,wait_mbx X; X`009ret X; X; X;*****`009move data from 'buffer' to user X; X`009.psect`009_code X.entry`009ZT_TOUSER,`094m<> X; X.if ne EVAX X`009$cmkrnl_s`009routin=k_touser X.iff X`009$cmkrnl_s`009routin=k_touser,- X`009`009`009arglst=(ap) X.endc X`009ret X; X; X;*****`009move data from user to 'buffer' X; X`009.psect`009_code X.entry`009ZT_FRUSER,`094m<> X; X.if ne EVAX X`009$cmkrnl_s`009routin=k_fruser X.iff X`009$cmkrnl_s`009routin=k_fruser,- X`009`009`009arglst=(ap) X.endc X`009ret X; X; X;*****`009complete I/O X; X`009.psect`009_code X.entry`009ZT_REQCOM,`094m<> X; X`009calls`009#0,read_mbx`009`009`009; post read on mbx X`009chkr0 X; X.if ne EVAX X`009$cmkrnl_s`009routin=k_reqcom X.iff X`009$cmkrnl_s`009routin=k_reqcom,- X`009`009`009arglst=(ap) X.endc X`009ret X; X`009.page X;*****`009the end X; X`009.psect`009_nonpageddata X; Xbuffer:`009`009`009`009;the data buffer X`009.blkb`009`094xFFFF`009`009;i.e. 64k-1 X; Xnonpageddata_end: X`009.blkb X; X.if ne EVAX X.iff X`009.psect`009_nonpagedcode Xnonpagedcode_end: X`009.blkb X.endc X; X`009.end $ GOSUB UNPACK_FILE $ FILE_IS = "ZT_DRIVER.MAR" $ CHECKSUM_IS = 86607548 $ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY X`009.title`009ZT_driver`009... pseudo tape ... X; X;`009w.j.m. jun 1989 (after ZXdriver 0.2 ...) X;`009documentation fixed 3-jul-1989 X;`009mod 18-aug-1989 wjm (0.9): support SMP, i.e. VMS V5 X;`009mod 14-oct-1992 wjm (0.99): change driver name ZTDRIVER => ZT_DRIVER X;`009mod 22-oct-1993 wjm (0.99A): port to AXP VMS 1.5 (needs EVAX defined) X;`009mod 29-jan-1994 wjm (0.99B): small fixes, automatically defined EVAX X;`009mod 18-mar-1994 wjm (0.99FT): port to AXP VMS T2.0 (heuristic) X;`009mod 18-jan-1995 wjm (0.999): support multiple units - UNITINIT code X;`009`009`009`009from Glenn Everhart included but not used X;`009`009`009`009("mod 05-oct-1994 gce make multi-unit etc."); X;`009`009`009`009`009also support "XA" on VAX VMS V6 X;`009mod 03-feb-1995 wjm (1.01): try to (sort of) support IO$_DISPLAY X;`009`009`009`009 X; X`009.ident`009/1.01/ X; X;***** X; X;`009This driver attempts to do the minimum necessary; X;`009all the 'real' work, including data transfer, X;`009is done by a (suitably privileged) server process. X; X;`009Communication: X;`009`009driver->server`009- driver activates server via mailbox message, X;`009`009`009`009`009then waits for pseudo-interrupt. X;`009`009server->driver`009- server (possibly) manipulates UCB, X;`009`009`009`009`009in particular copies back the X;`009`009`009`009`009'message' area (*); X;`009`009`009`009`009then it activates the driver via X;`009`009`009`009`009pseudo-interrupt. X; X;`009(*) Only the following fields are taken from the message area: X;`009`009ucb$l_record X;`009`009ucb$v_valid (in ucb$w_sts) X;`009`009ucb$l_devdepend (from 2nd iosb longword) X;`009 plus I/O completion status & bytecount (1st iosb longword) X; X;`009It is the server's responsibility to set the device 'ONLINE' X;`009and to clean up and set the device 'OFFLINE' before terminating. X; X;***** X; X.iif ndf UNITS, UNITS = 100`009; maximum # of units (gce value) X; X; X.ntype`009__,R31`009`009`009; set EVAX nonzero if R31 is a register X.if eq <__ & `094xF0> - `094x50 XEVAX = 1 X.iff XEVAX = 0 X.endc X; X; X.if ne EVAX X`009.library`009"SYS$LIBRARY:LIB" X`009.library`009"SYS$DISK:[]ZT" X.iff X`009.link`009`009"SYS$SYSTEM:SYS.STB"/SELECTIVE_SEARCH X`009.library`009"SYS$LIBRARY:LIB" X`009.library`009"ZT" X.endc X; X`009$crbdef X`009$dcdef X`009$ddbdef X`009$devdef X`009$dptdef X`009$dyndef X`009$idbdef X`009$iodef X`009$ipldef X`009$irpdef X`009$mtdef X`009$prdef X`009$ssdef X`009$ucbdef X`009$vecdef X`009$wcbdef X; Xsmp_code=0`009`009`009`009;VMS V4 or earlier X.iif df UCB$L_DLCK, smp_code=1`009`009;VMS V5`032 X; X.if ne smp_code X`009$spldef X.endc X; X.if ne EVAX`009`009`009`009;AXP ... X;`009`009`009`009`009;... EVAX=1 -> Step1 X.iif ndf WCB$W_NMAP, EVAX=2`009`009;... EVAX=2 -> Step2 (ndf as of T2.0) X.endc X; X.iif ndf IO$_DISPLAY, IO$_DISPLAY=19`009; so we need not care for VMS version X;`009`009`009`009`009; (defined since at least V5.4) X; X; X`009ztdef`009`009; ZT definitions - need $ucbdef X; X; private UCB fields not in ZTDEF X; X`009$defini`009UCB,dot=ucb_k_ztend X$def`009ucb_q_spare`009.blkq`009`009; reserve for UCB expansion X$def`009ucb_k_size`009`009`009; end of UCB X`009$defend`009UCB X; X`009.page X;*****`009driver prologue table X; X.if ne EVAX X; X`009driver_data X; X.if eq EVAX-2 X`009dptab`009step=2,- X`009`009name=ZT_DRIVER,-`009; Driver name`009`009<<<<< X`009`009adapter=NULL,- X`009`009flags=dpt$m_svp,-`009; for IOC$MOV[FR/TO]USER X`009`009maxunits=UNITS,- X`009`009ucbsize=ucb_k_size,- X`009`009end=end_of_driver X.iff X`009dptab`009step=1,- X`009`009name=ZT_DRIVER,-`009; Driver name`009`009<<<<< X`009`009adapter=NULL,- X`009`009flags=dpt$m_svp,-`009; for IOC$MOV[FR/TO]USER X`009`009maxunits=UNITS,- X`009`009ucbsize=ucb_k_size,- X`009`009end=end_of_driver X.endc X.iff Xdpt_flags = dpt$m_svp`009`009`009; for IOC$MOV[FR/TO]USER X.if df dpt$m_xpamod Xdpt_flags = dpt$m_svp!dpt$m_xpamod`009; no (known) VMS V6 "XA" dependencies X.endc X`009dptab`009name=ZT_DRIVER,-`009; Driver name`009`009<<<<< X`009`009adapter=NULL,- X`009`009flags=dpt_flags,- X`009`009maxunits=UNITS,- X`009`009ucbsize=ucb_k_size,- X`009`009end=end_of_driver X.endc X; X`009dpt_store`009INIT X; X.if eq smp_code X`009dpt_store `009UCB,ucb$b_fipl,B,- X`009`009`009ipl$_synch`009`009; fork IPL X`009dpt_store`009UCB,ucb$b_dipl,B,- X`009`009`009ipl$_synch`009`009; device IPL = same X`009assume`009`009ipl$_synch lt ipl$_mailbox`009;note: writing to MBX X`009`009`009`009`009`009; is done at device IPL X.iff X`009dpt_store `009UCB,ucb$b_flck,B,- X`009`009`009spl$c_iolock8`009`009; fork lock index X`009dpt_store`009UCB,ucb$b_dipl,B,- X`009`009`009ipl$_iolock8`009`009; device IPL = fork IPL X`009assume`009`009ipl$_iolock8 lt ipl$_mailbox`009;note: writing to MBX X`009`009`009`009`009`009; is done at device IPL X.endc X`009dpt_store`009UCB,ucb$b_devclass,B,- X`009`009`009dc$_tape`009`009; device class X`009dpt_store`009UCB,ucb$b_devtype,B,- X`009`009`009dt$_te16`009`009; device type = TE16 X`009dpt_store `009UCB,ucb$l_devchar,L,<-`009; device characteristics X`009`009`009dev$m_avl!-`009`009`009; available X`009`009`009dev$m_idv!-`009`009`009; input device X`009`009`009dev$m_odv!-`009`009`009; output device X`009`009`009dev$m_fod!-`009`009`009; file oriented X`009`009`009dev$m_dir!-`009`009`009; directory structured X`009`009`009dev$m_sdi!-`009`009`009; single directory X`009`009`009dev$m_sqd>`009`009`009; sequential X`009dpt_store`009UCB,ucb$l_devchar2,L,-`009; (cont'd) X`009`009`009dev$m_nnm`009`009`009; "node$" prefix X`009dpt_store`009DDB,ddb$l_acpd,L,-`009; default ACP X`009`009`009<`094a"MTA">`009`009`009; = MTAACP X`009dpt_store`009UCB,ucb$w_devbufsiz,W,-`009; default buffer size X`009`009`0092048`009`009`009`009; = 2048 X`009dpt_store`009UCB,ucb$l_media_id,L,-`009; media i.d. X`009`009`009<`094x6D285010>`009`009`009; media id - TE16 X X`009dpt_store`009REINIT X; X`009dpt_store`009UCB,ucb$l_devdepend,L,<- ; tape characteristics X`009`009`009!-`009; format = normal11 X`009`009`009!-`009; density = 1600 X`009`009`009mt$m_sup_pe!-`009`009`009; support PE only X`009`009`009mt$m_lost>`009`009`009; "position lost" X`009dpt_store`009UCB,ucb$l_record,L,-`009;current tape position X`009`009`0090`009`009`009`009; i.e. BOT X`009dpt_store`009DDB,ddb$l_ddt,D,- X`009`009`009ZT$DDT`009`009`009; address of DDT`009<<<<< X X`009dpt_store`009UCB,ucb_l_inter,D,-`009; works even on VAX VMS V4! X`009`009`009pseudo_interrupt`009; no more controller/unit _init X.if eq EVAX X.if eq 1 ; init not required, since preceding dpt_store works on VAX, too X`009dpt_store`009CRB,crb$l_intd+vec$l_initial,D,- X`009`009`009controller_init`009`009; controller initialization X dpt_store`009CRB,crb$l_intd+vec$l_unitinit,D,-`032 X `009unit_init`009`009; unit initialization X.iff`009; zero out init routines (for RELOADing over previous version) X`009dpt_store`009CRB,crb$l_intd+vec$l_initial,L,0 X dpt_store`009CRB,crb$l_intd+vec$l_unitinit,L,0 X.endc X.endc X; X; X`009dpt_store`009END X; X; X;*****`009driver dispatch table X; X; EVAX: unless we need a ctrlinit/unitinit routine, VAX ddtab is still valid X; X`009ddtab`009devnam=ZT,-`009`009`009; device name X`009`009functb=fdt_table,- X`009`009start=start_io,- X`009`009cancel=+IOC$CANCELIO`009;cancel: just set flag X; X; X;*****`009function dispatch table X; X.if eq EVAX-2 X`009fdt_ini`009fdt=fdt_table X`009fdt_buf`009-`009`009`009`009; buffered functions ... X`009`009-`009`009;... all but data transfer & AVAILABLE (WHY???) X`009`009 X X`009fdt_act`009ACP_STD$READBLK,<-`009`009; read functions => ACP => ... X`009`009readpblk,- X`009`009readlblk,- X`009`009readvblk> X`009fdt_act`009ACP_STD$WRITEBLK,<-`009`009; write functions => ACP => ... X`009`009writecheck,- X`009`009writepblk,- X`009`009writelblk,- X`009`009writevblk> X`009fdt_act`009ACP_STD$ACCESS,<-`009`009; access => ACP only X`009`009access,- X`009`009create> X`009fdt_act`009ACP_STD$DEACCESS,<-`009`009; deaccess => ACP only X`009`009deaccess> X`009fdt_act`009ACP_STD$MODIFY,<-`009`009; control => ACP only X`009`009delete,- X`009`009modify,- X`009`009acpcontrol> X`009fdt_act`009ACP_STD$MOUNT,<-`009`009; mount => ACP only X`009`009mount> X`009fdt_act`009MT_STD$CHECK_ACCESS,-`009`009; ** also does EXE$ZEROPARM ** X`009`009 X`009fdt_act`009EXE_STD$ZEROPARM,<-`009`009; (final) functions with 0 par. X`009`009nop,- X`009`009unload,- X`009`009recal,- X`009`009drvclr,- X`009`009packack,- X`009`009available,- X`009`009sensechar,-`009`009;(!) X`009`009rewindoff,- X`009`009rewind,- X`009`009sensemode>`009`009;(!) X`009fdt_act`009EXE_STD$ONEPARM,<-`009`009; (final) functions with 1 par. X`009`009spacefile,- X`009`009spacerecord,- X`009`009skipfile,- X`009`009skiprecord> X`009fdt_act`009EXE_STD$SETMODE,<-`009`009; (final) set mode X`009`009setchar,- X`009`009setmode> X`009fdt_act`009display_fdt, X.iff X Xfdt_table: X`009functab`009,<-`009`009`009`009; valid functions X`009`009-`009`009`009;physical: X`009`009nop,-`009`009`009`009;nop (status check only) X`009`009unload,-`009`009`009;unload (=rewindoff) X`009`009spacefile,-`009`009`009;(=skipfile) X`009`009recal,-`009`009`009`009;'recalibrate' (=rewind) X`009`009drvclr,-`009`009`009;'drive clear' X`009`009erasetape,-`009`009`009;write extended gap X`009`009packack,-`009`009`009;set valid X`009`009spacerecord,-`009`009`009;(=skiprecord) X`009`009writecheck,-`009`009`009;compare X`009`009writepblk,-`009`009`009;write X`009`009readpblk,-`009`009`009;read X`009`009available,-`009`009`009;unload & clear valid X`009`009display,-`009`009`009;set/clear "display" X`009`009setchar,-`009`009`009; X`009`009sensechar,-`009`009`009; X`009`009writemark,-`009`009`009;(=writeof) X`009`009-`009`009`009;logical: X`009`009writelblk,-`009`009`009;write X`009`009readlblk,-`009`009`009;read X`009`009rewindoff,-`009`009`009;rewind & unload X`009`009setmode,-`009`009`009; X`009`009rewind,-`009`009`009; X`009`009skipfile,-`009`009`009; X`009`009skiprecord,-`009`009`009; X`009`009sensemode,-`009`009`009; X`009`009writeof,-`009`009`009;write tape mark X`009`009-`009`009`009;virtual (ACP only) X`009`009writevblk,-`009`009`009;write X`009`009readvblk,-`009`009`009;read X`009`009access,-`009`009`009; X`009`009create,-`009`009`009; X`009`009deaccess,-`009`009`009; X`009`009delete,-`009`009`009;(???) X`009`009modify,-`009`009`009;(???) X`009`009acpcontrol,-`009`009`009; X`009`009mount>`009`009`009`009; X`009functab`009,<-`009`009`009`009; buffered functions ... X`009`009-`009`009;... all but data transfer & AVAILABLE (WHY???) X`009`009nop,- X`009`009unload,- X`009`009spacefile,- X`009`009recal,- X`009`009drvclr,- X`009`009erasetape,- X`009`009packack,- X`009`009spacerecord,- X`009`009display,- X`009`009setchar,- X`009`009sensechar,- X`009`009writemark,- X`009`009rewindoff,- X`009`009setmode,- X`009`009rewind,- X`009`009skipfile,- X`009`009skiprecord,- X`009`009sensemode,- X`009`009writeof,- X`009`009access,- X`009`009create,- X`009`009deaccess,- X`009`009delete,- X`009`009modify,- X`009`009acpcontrol,- X`009`009mount> X X`009functab`009+ACP$READBLK,<-`009`009`009; read functions => ACP => ... X`009`009readpblk,- X`009`009readlblk,- X`009`009readvblk> X`009functab`009+ACP$WRITEBLK,<-`009`009; write functions => ACP => ... X`009`009writecheck,- X`009`009writepblk,- X`009`009writelblk,- X`009`009writevblk> X`009functab`009+ACP$ACCESS,<-`009`009`009; access => ACP only X`009`009access,- X`009`009create> X`009functab`009+ACP$DEACCESS,<-`009`009; deaccess => ACP only X`009`009deaccess> X`009functab`009+ACP$MODIFY,<-`009`009`009; control => ACP only X`009`009delete,- X`009`009modify,- X`009`009acpcontrol> X`009functab`009+ACP$MOUNT,<-`009`009`009; mount => ACP only X`009`009mount> X`009functab`009+MT$CHECK_ACCESS,<-`009`009; check access for X`009`009-`009`009`009`009; functions not handled by ACP X`009`009erasetape,- X`009`009writemark,- X`009`009writeof> X X`009functab`009+EXE$ZEROPARM,<-`009`009; (final) functions with 0 par. X`009`009nop,- X`009`009unload,- X`009`009recal,- X`009`009drvclr,- X`009`009erasetape,- X`009`009packack,- X`009`009available,- X`009`009sensechar,-`009`009;(!) X`009`009writemark,- X`009`009rewindoff,- X`009`009rewind,- X`009`009sensemode,-`009`009;(!) X`009`009writeof> X`009functab`009+EXE$ONEPARM,<-`009`009`009; (final) functions with 1 par. X`009`009spacefile,- X`009`009spacerecord,- X`009`009skipfile,- X`009`009skiprecord> X`009functab`009+EXE$SETMODE,<-`009`009`009; (final) set mode X`009`009setchar,- X`009`009setmode> X`009functab`009display_fdt, X.endc X; X`009.page X; X.iif ne EVAX,`009driver_code X; X.if eq 1 ; EVAX`009;; no init routine required X; X;*****`009controller initialization X; X;`009called`009(1) after loading the driver (not after RELOAD !?) X;`009`009(2) after a power failure X; X; VAX:`009r4 - `009csr`009`009 AXP:`009r4 - idb X;`009r5 -`009idb`009`009`009others - same X;`009r6 -`009ddb X;`009r8 -`009crb X;`009r0..r2 - free for use X; X; .iif eq UNITS-1, idb$l_ucblst - ucb (since there is only 1 unit) X; X;`009ipl: ipl$_power X;`009`009 X;***** X; Xcontroller_init: X.if ne EVAX*0`009`009`009`009`009`009;; example only X`009.jsb_entry`009input=r5,output=r0`009`009;; (for Step1) X;`009`009`009`009`009`009`009;;`032 X`009movl`009#ss$_normal,r0`009`009; no-op!`009;; X;`009`009`009`009`009`009`009;; X`009rsb`009`009`009`009`009`009;; X.iff X; X.if eq UNITS-1 X`009movl`009idb$l_ucblst(r5),r0`009; r0 -> ucb (NOTE: maxunits=1) X; X;;; setting device on-line is left to server program! X;;;`009bisw`009#ucb$m_online,-`009`009; set device status "on_line" X;;;`009`009ucb$w_sts(r0) X; X`009movl`009r0,idb$l_owner(r5)`009; set permanent controller owner X; X`009movab`009pseudo_interrupt,ucb_l_inter(r0)`009; constant UCB field X.iff X;;;`009Don't have an UCB to play with ... X;;;`009Luckily we don't actually need idb$l_owner, since the pseudo-interrupt X;;;`009is called with r5 already pointing to the UCB. X.endc X`009rsb X.endc X.endc X; X; X.if eq 1`009;; no init required X; X;*****`009unit initialization X;`009(not too useful since it's not called on RELOAD) X; X;`009called`009(1) while CONNECTing a unit X;`009`009(2) after a power failure X; X; VAX:`009r3 -`009csr (primary)`009 AXP:`009r3 - `009not used (Step2) X;`009r4 - `009csr (secondary)`009`009r4 -`009idb X;`009r5 -`009ucb`009`009`009others - same X;`009r0..r3 - free for use X; X;`009ipl: ipl$_power (?) X;`009`009 X;***** X; Xunit_init:`009*** EXAMPLE ONLY *** X.if eq EVAX X`009rsb X.iff X.if eq EVAX-1`009`009`009`009`009`009;*untested*, after gce X`009.jsb_entry`009input=,- X`009`009`009output=,- X`009`009`009preserve=,- X`009`009`009scratch= X`009movzwl`009#ss$_normal,r0 X`009rsb X.iff X`009$driver_unitinit_entry`009`009`009; loads r0 with ss$_normal X ret `009; (!) X.endc X.endc X; X.endc X; X`009.page X;*****`009FDT routine for IO$_DISPLAY X;`009("display text", also used for media loaders ...) X; X; Spec (after VAX V6.1 DUTUSUBS): X; X; P3:`009item code (by value) X; P4:`009mode (by value) X; P1:`0090, or "item list" (by ref) X;`009"item list": X;`009`009.long`009number_of_strings`009;1 or 2 expected, more permitted X;`009`009.long`009string1_dsc_address X;`009`009[.long`009string2_dsc_address] X;`009`009[...] X; P2,P5,P6: ignored X; X; item code`009mode`009meaning X; ---------`009----`009------- X; `0090`009[any]`009no-op`009`009`009(P1 ignored) X; `0091`0090`009clear display`009`009(P1 _not_ ignored) X; `0091`0091`009display volume mounted`009(1 string = label) X; `0091`0092`009display volume request`009(1 or 2 strings) X;`009`009`009`0091st string = label X;`009`009`009`0092nd string = operator command like "MOUNT" X; X; ZT_DRIVER support: X;`009item code <= 255 - communicate via IRP$Q_MEDIA X;`009`009+0`009item code X;`009`009+1`009mode X;`009`009+2..+7`009string#1 (if item code > 0, and present), X;`009`009`009always blank filled (tape labels are 6 bytes max.!) X;`009`009NB. Strings other than the 1st are ignored. X;`009item code > 255, and if(itemcode>0) mode > 255 - rejected here X; X; Registers: X;`009r0..r2, r9..r11 - free X;`009r3 - IRP X;`009r4 - PCB X;`009r5 - UCB X;`009r6 - CCB X;`009...etc... X; X; X.if eq EVAX Xp1=0 Xp3=2*4 Xp4=3*4 X.endc X; Xdisplay_fdt: X.if eq EVAX-1 X`009.jsb_entry`009input=,- X`009`009`009preserve=,- X`009`009`009scratch= X.endc X.iif eq EVAX-2,`009$driver_fdt_entry X X`009movab`009irp$l_media(r3),r2 X`009movl`009#`094x20202020,(r2)`009`009; blank pre-fill X`009movl`009#`094x20202020,4(r2) X X.if eq EVAX X`009movl`009p3(ap),r0`009`009`009; itemcode X.iff X`009movl`009irp$l_qio_p3(r3),r0 X.endc X`009cmpl`009r0,#`094xFF`009`009`009; check X`009bgtru`00980$`009`009`009`009; won't fit X`009movb`009r0,(r2)+`009`009`009; irp$l_media+0 = itemcode X X.if eq EVAX X`009movl`009p4(ap),r1`009`009`009; mode X.iff X`009movl`009irp$l_qio_p4(r3),r1 X.endc X X`009tstb`009r0`009`009`009`009; itemcode 0? X`009bneq`00921$`009`009`009`009; br if no X`009movb`009r1,(r2)+`009`009`009; yes, store mode w/o check X`009brb`00940$`009`009`009`009; all done (p1 ignored) X21$: X`009cmpl`009r1,#`094xFF`009`009`009; check X`009bgtru`00980$`009`009`009`009; won't fit X`009movb`009r1,(r2)+`009`009`009; irp$l_media+1 = mode X X.if eq EVAX X`009movl`009p1(ap),r0 X.iff X`009movl`009irp$l_qio_p1(r3),r0 X.endc X`009beql`00940$`009`009`009`009; done if no itemlist X X`009ifnord`009#4,(r0),85$`009`009`009; probe #items X`009movl`009(r0)+,r1 X`009beql`00940$`009`009`009`009; done if zero #items X X`009ifnord`009#4,(r0),85$`009`009`009; probe item #1 (only) X`009movl`009(r0),r0`009`009`009`009; string#1 descriptor address X X`009ifnord`009#8,(r0),85$`009`009`009; probe descriptor X`009movl`009(r0)+,r1 X`009movl`009(r0),r0 X`009movzwl`009r1,r1`009`009`009`009; extract string length X`009beql`00940$`009`009`009`009; br if zero X X`009ifnord`009r1,(r0),85$`009`009`009; probe all of string#1 X X`009cmpl`009#6,r1`009`009`009`009; truncate length X`009bgeq`00923$ X`009movl`009#6,r1 X23$: X30$:`009`009`009`009`009`009; copy 1..6 bytes X`009movb`009(r0)+,(r2)+`009`009`009; to irp$l_media+2 .. +7 X`009sobgtr`009r1,30$ X X40$:`009`009`009`009`009`009; all done here X.if lt EVAX-2 X`009jmp`009g`094exe$qiodrvpkt X.iff X`009call_qiodrvpkt`009do_ret=YES X.endc X X80$:`009`009`009`009`009`009; badparam X`009movzwl`009#ss$_badparam,r0 X`009brb`00990$ X85$: X`009movzwl`009#ss$_accvio,r0 X`009;brb`00990$ X90$: X.if lt EVAX-2 X`009jmp`009g`094exe$abortio X.iff X`009call_abortio`009do_ret=YES X.endc X; X; X`009.page X;*****`009start I/O routine X; X;`009r3 - IRP X;`009r5 - UCB X;`009r0..r2, r4 - free (only r3..r5 saved across WFI) X; X;`009ipl: ucb$b_fipl, SMP lock: ucb$b_flck X; X;`009irp$w_func`009- io function (logical or physical) X;`009irp$l_media`009- parameter(s) X;`009ucb$w_bcnt, ucb$w_boff, ucb$l_svapte describe buffer X; X; X;***** X; Xstart_io: X.if ne EVAX X`009.jsb_entry`009input=,scratch= X; X`009movl`009irp$l_func(r3),r2`009`009;IO function + modifiers X`009movl`009r2,ucb$l_func(r5)`009`009; save function in UCB X.iff X`009movzwl`009irp$w_func(r3),r2`009`009;IO function + modifiers X`009movw`009r2,ucb$w_func(r5)`009`009; save function in UCB X.endc X; X; set up mailbox request X; X.if ne EVAX X`009movl`009r2,ucb_a_ztmsg+zt_l_func(r5)`009; fill in 'function' X`009movl`009ucb$l_bcnt(r5),-`009`009; ditto for bytecount X`009`009ucb_a_ztmsg+zt_l_bcnt(r5) X.iff X`009movw`009r2,ucb_a_ztmsg+zt_w_func(r5)`009; fill in 'function' X`009movw`009ucb$w_bcnt(r5),-`009`009; ditto for bytecount X`009`009ucb_a_ztmsg+zt_w_bcnt(r5) X.endc X`009movq`009irp$l_media(r3),-`009`009; ditto for parameter(s) X`009`009ucb_a_ztmsg+zt_l_media(r5) X.if ne EVAX X`009movl`009ucb$l_sts(r5),-`009`009`009; ditto for ucb$l_sts X`009`009ucb_a_ztmsg+zt_l_ucbsts(r5) X.iff X`009movw`009ucb$w_sts(r5),-`009`009`009; ditto for ucb$w_sts X`009`009ucb_a_ztmsg+zt_w_ucbsts(r5) X.endc X`009movl`009ucb$l_record(r5),-`009`009; ditto for ucb$l_record X`009`009ucb_a_ztmsg+zt_l_record(r5) X`009movl`009ucb$l_devdepend(r5),-`009`009; ditto for ucb$l_devdepend X`009`009ucb_a_ztmsg+zt_l_devdepend(r5) X`009movl`009ucb$l_devchar(r5),-`009`009; ditto for ucb$l_devchar X`009`009ucb_a_ztmsg+zt_l_devchar(r5) X; X.if ne EVAX X`009movl`009#ss$_devoffline,- X`009`009ucb_a_ztmsg+zt_l_iosts(r5) X`009clrl`009ucb_a_ztmsg+zt_l_iobct(r5)`009; default 1st iosb longword X.iff X`009assume`009zt_w_iobct eq zt_w_iosts+2 X`009movl`009#ss$_devoffline,- X`009`009ucb_a_ztmsg+zt_w_iosts(r5)`009; default 1st iosb longword X.endc X; X; server there? X; X`009tstl`009ucb_l_ztmbx(r5)`009`009`009; server mailbox there? X`009blss`0091$`009`009`009`009; br if yes (S0 space!) X`009brw`009io_err_noserver`009`009`009; server disappeared X; X1$: X; X; check for special action(s) X; X.if ne EVAX X`009extzv`009#irp$v_fcode,#irp$s_fcode,-`009;get major function X`009`009irp$l_func(r3),r2 X.iff X`009extzv`009#irp$v_fcode,#irp$s_fcode,-`009;get major function X`009`009irp$w_func(r3),r2 X.endc X; X`009cmpw`009r2,#io$_setchar X`009beql`009150$ X`009cmpw`009r2,#io$_setmode X`009beql`009160$ X`009cmpw`009r2,#io$_available X`009beql`009175$ X`009brw`00920$ X; X; set char/mode X; X150$:`009`009`009`009`009`009;io$_setchar X`009assume`009ucb$b_devtype eq ucb$b_devclass+1 X`009movw`009irp$l_media(r3),- X`009`009ucb$b_devclass(r5) X160$:`009`009`009`009`009`009;io$_setmode X`009movw`009irp$l_media+2(r3),- X`009`009ucb$w_devbufsiz(r5) X`009brw`00920$ X; X; available X; X175$: X.if ne EVAX X`009bicl`009#ucb$m_valid,ucb$l_sts(r5)`009;set invalid now X`009movl`009ucb$l_sts(r5),- X`009`009ucb_a_ztmsg+zt_l_ucbsts(r5)`009;update message (!) X.iff X`009bicw`009#ucb$m_valid,ucb$w_sts(r5)`009;set invalid now X`009movw`009ucb$w_sts(r5),- X`009`009ucb_a_ztmsg+zt_w_ucbsts(r5)`009;update message (!) X.endc X`009brw`00920$ X; X; X; check if i/o is permitted X; X20$: X.if ne EVAX X`009bbs`009#irp$v_physio,irp$l_sts(r3),30$`009; o.k. if physical X`009bbs`009#ucb$v_valid,ucb$l_sts(r5),30$`009; o.k. if volume valid X`009movl`009#ss$_volinv,- X`009`009ucb_a_ztmsg+zt_l_iosts(r5)`009; else error: volume invalid X.iff X`009bbs`009#irp$v_physio,irp$w_sts(r3),30$`009; o.k. if physical X`009bbs`009#ucb$v_valid,ucb$w_sts(r5),30$`009; o.k. if volume valid X`009movw`009#ss$_volinv,- X`009`009ucb_a_ztmsg+zt_w_iosts(r5)`009; else error: volume invalid X.endc X`009brw`009io_complete X; X; have server do the rest ... X; X30$: X; X; SMP note: postprocessing of the mailbox i/o is done on "this" processor, X;`009`009*** I think ***. X;`009Therefore the mailbox msg will be delivered (by KAST) X;`009only AFTER ipl drops to ASTDEL, i.e. after wfikpch X;`009 X.if eq smp_code X`009dsbint`009ucb$b_dipl(r5)`009`009`009;; X.iff`009`009`009`009`009`009;; X`009devicelock`009-`009`009`009;; X`009`009lockaddr=ucb$l_dlck(r5),-`009;; X`009`009lockipl=ucb$b_dipl(r5),-`009;; X`009`009savipl=-(sp),-`009`009`009;; X`009`009preserve=NO`009`009`009;; X.endc`009`009`009`009`009`009;; X`009`009`009`009`009`009;; X.if ne EVAX`009`009`009`009;; X`009bitl`009#ucb$m_cancel,ucb$l_sts(r5)`009;; I/O to be aborted? X.iff`009`009`009`009`009`009;; X`009bitw`009#ucb$m_cancel,ucb$w_sts(r5)`009;; I/O to be aborted? X.endc`009`009`009`009`009`009;; X`009beql`00921$`009`009`009`009;; X`009brw`009io_cancelled`009`009`009;; br if yes, do it now! X21$:`009`009`009`009`009`009;; X`009pushr`009#`094m`009`009`009;; X`009movl`009#zt_msglen,r3`009`009`009;; X`009movab`009ucb_a_ztmsg(r5),r4`009`009;; X`009movl`009ucb_l_ztmbx(r5),r5`009`009;; X`009jsb`009g`094EXE$WRTMAILBOX`009`009;; X`009popr`009#`094m`009`009`009;; X`009blbs`009r0,23$`009`009`009`009;; br if o.k. X`009`009`009`009`009`009;; X.if eq smp_code`009`009`009`009`009;; X`009enbint`009`009`009`009`009;; X.iff`009`009`009`009`009`009;; X`009deviceunlock`009-`009`009`009;; X`009`009lockaddr=ucb$l_dlck(r5),-`009;; X`009`009newipl=(sp)+,-`009`009`009;; X`009`009preserve=NO`009`009`009;; X.endc X`009brw`009io_err_noserver X23$:`009`009`009`009`009`009;; X`009wfikpch`009io_timeout`009`009`009;; (dummy timeout) X;`009`009`009`009`009;; here with device lock only X`009iofork X`009`009`009`009`009; here with fork lock only X; server replied ... X; X`009brw`009io_complete`009`009`009;o.k., all done X; X; X; i/o cancelled ... X;`009ipl: _dipl with _fipl on stack X; Xio_cancelled:`009`009`009`009`009;; X.if eq smp_code`009`009`009`009`009;; X`009enbint`009`009`009`009`009;; lower IPL (to _fipl) X.iff`009`009`009`009`009`009;; X`009deviceunlock`009-`009`009`009;; X`009`009lockaddr=ucb$l_dlck(r5),-`009;; X`009`009newipl=(sp)+,-`009`009`009;; X`009`009preserve=NO`009`009`009;; X.endc X; X.if ne EVAX X`009movl`009#ss$_cancel,- X`009`009ucb_a_ztmsg+zt_l_iosts(r5)`009; set status X`009clrl`009ucb_a_ztmsg+zt_l_iobct(r5)`009; clear byte count X.iff X`009assume`009zt_w_iobct eq zt_w_iosts+2 X`009movzwl`009#ss$_cancel,- X`009`009ucb_a_ztmsg+zt_w_iosts(r5)`009; set status X.endc X`009brw`009io_complete X; X; X; i/o timeout - we cannot allow for an asynchronous event X;`009ipl: ucb$b_dipl, SMP locks: ucb$l_dlck + ucb$b_flck X; Xio_timeout:`009`009`009`009`009;; X.if ne EVAX X`009bisl`009#,ucb$l_sts(r5)`009;; re-set interrupt expected X`009bicl`009#,ucb$l_sts(r5)`009;;`009and powerfail X.iff X`009bisw`009#,ucb$w_sts(r5)`009;; re-set interrupt expected X`009bicw`009#,ucb$w_sts(r5)`009;;`009and powerfail X.endc X`009rsb`009`009`009`009`009;; return! X; X; X; server gone X; Xio_err_noserver: X.if ne EVAX X`009bicl`009#,-`009;clear valid & online X`009`009ucb$l_sts(r5) X`009movl`009ucb$l_sts(r5),- X`009`009ucb_a_ztmsg+zt_l_ucbsts(r5)`009;update message (!) X.iff X`009bicw`009#,-`009;clear valid & online X`009`009ucb$w_sts(r5) X`009movw`009ucb$w_sts(r5),- X`009`009ucb_a_ztmsg+zt_w_ucbsts(r5)`009;update message (!) X.endc X`009clrl`009ucb_l_ztmbx(r5)`009`009`009;clear our indicator X`009brb`009io_complete`009`009`009;default values still o.k. X; X; X; i/o completed (normally by server) X; Xio_complete: X.if ne EVAX X`009bicl`009#,- X`009`009ucb$l_sts(r5)`009`009`009; cleanup some UCB flags X.iff X`009bicw`009#,- X`009`009ucb$w_sts(r5)`009`009`009; cleanup some UCB flags X.endc X; X`009movl`009ucb_a_ztmsg+zt_l_record(r5),-`009;care for ... X`009`009ucb$l_record(r5)`009`009`009; ucb$l_record ... X.if ne EVAX X`009extzv`009#ucb$v_valid,#1,- X`009`009ucb_a_ztmsg+zt_l_ucbsts(r5),r2`009`009; ucb$v_valid ... X`009insv`009r2,#ucb$v_valid,#1,ucb$l_sts(r5) X.iff X`009extzv`009#ucb$v_valid,#1,- X`009`009ucb_a_ztmsg+zt_w_ucbsts(r5),r2`009`009; ucb$v_valid ... X`009insv`009r2,#ucb$v_valid,#1,ucb$w_sts(r5) X.endc X`009movl`009ucb_a_ztmsg+zt_l_devdepend(r5),r1`009; 2nd iosb longword ... X`009movl`009r1,ucb$l_devdepend(r5)`009`009`009; ucb$l_devdepend ... X.if ne EVAX X`009movzwl`009ucb_a_ztmsg+zt_l_iosts(r5),r0 X`009insv`009ucb_a_ztmsg+zt_l_iobct(r5),- X`009`009#16,#16,r0`009`009`009`009; 1st iosb longword X.iff X`009assume`009zt_w_iobct eq zt_w_iosts+2 X`009movl`009ucb_a_ztmsg+zt_w_iosts(r5),r0`009`009; 1st iosb longword X.endc X; X; X; special ACP-related cleanup on error (... from DEC ...) X; ... destroys r2 and r4 !! X; X`009blbs`009r0,done`009`009`009`009;br if no error X.if ne EVAX X`009bbc`009#irp$v_virtual,irp$l_sts(r3),-`009;br if not virtual X`009`009done X.iff X`009bbc`009#irp$v_virtual,irp$w_sts(r3),-`009;br if not virtual X`009`009done X.endc X; X`009movq`009r0,-(sp)`009`009;save r0,r1 X`009movl`009irp$l_wind(r3),r4`009;... X.if eq EVAX-2 X`009clrl`009wcb$l_nmap(r4)`009`009;... X.iff X`009clrw`009wcb$w_nmap(r4)`009`009;... X.endc X`009movl`009ucb$l_vcb(r5),r4`009;r4=vcb X`009movab`009ucb$l_ioqfl(r5),r2`009;r2=i/o queue listhead (end of list) X`009movl`009r2,r0`009`009`009;r0=IRP pointer X70$: X`009movl`009(r0),r0`009`009`009;get next IRP X`009cmpl`009r0,r2`009`009`009;end of list? X`009beql`00979$`009`009`009;br if yes X.if ne EVAX X`009bbc`009#irp$v_virtual,- X`009`009irp$l_sts(r0),70$`009;skip IRP if not virtual X.iff X`009bbc`009#irp$v_virtual,- X`009`009irp$w_sts(r0),70$`009;skip IRP if not virtual X.endc X`009movl`0094(r0),r0`009`009;back up r0 X`009remque`009@0(r0),r1`009`009;remove IRP from I/O queue X`009insque`009(r1),@4(r4)`009`009;insert it into blocked queue (in VCB) X`009brb`00970$ X79$: X`009movq`009(sp)+,r0`009`009;note: r2 & r4 destroyed X; Xdone: X`009reqcom X; X`009.page X;*****`009ZT pseudo-interrupt X; X;`009r0 ... r4 free X;`009r5 -> UCB X;`009ipl = ucb$b_dipl, SMP lock: ucb$l_dlck X; X; function: X;`009on any expected "interrupt", the driver is called back. X;`009we also clear ucb$m_int and ucb$m_tim X; X; Note: If this was a "real" interrupt, we had to obtain`032 X;`009the UCB address from idb$l_owner, and also we had`032 X;`009to acquire the device lock ourselves (ipl would be ok). X;`009`009`009 X; Xpseudo_interrupt: X.if ne EVAX X`009.jsb_entry`009input=r5,output=r5,scratch= X; X`009bbc`009#ucb$v_int,ucb$l_sts(r5),90$ X; X`009bicl`009#,- X`009`009ucb$l_sts(r5)`009`009`009; no more interrupts desired X; X`009evax_ldq`009r3,ucb$q_fr3(r5) X`009evax_ldq`009r4,ucb$q_fr4(r5) X.iff X`009bbc`009#ucb$v_int,ucb$w_sts(r5),90$ X; X`009bicw`009#,- X`009`009ucb$w_sts(r5)`009`009`009; no more interrupts desired X`009assume`009ucb$l_fr4 eq ucb$l_fr3+4 X`009movq`009ucb$l_fr3(r5),r3 X.endc X`009jsb`009@ucb$l_fpc(r5)`009`009`009; call driver X90$: X`009rsb X; X; X;*****`009end of driver X; Xend_of_driver: X; X`009.end $ GOSUB UNPACK_FILE $ EXIT -+-+-+-+-+ End of part 5 +-+-+-+-+-