From:	ADVAX::"mcnc!pucc.PRINCETON.EDU!moeller%gwdgv1.dnet.gwdg.de" "GWDGV1::MOELLER" 22-JAN-1991 01:09:49.95
To:	Arisia::Everhart, jim@mango.rsmas.miami.edu, R602MS5U@VB.CC.CMU.EDU, PALTER@UBVMS.BITNET, johnsond@calstate.bitnet, FOX@MSUNSCL.BITNET, awpsys@ultb.isc.rit.edu, ACCESS@KRDC.INT.Alcan.CA, moeller@dgogwdg5
CC:	
Subj:	VMS 5.4 ZTDRIVER part 4 of 4

Received: by ADVAX.DECnet (utk-mail11 v1.5) ; Tue, 22 Jan 91 01:07:43 EST
Received:  from mcnc by ge-dab.GE.COM (5.61/GE-DAB 1.15) with UUCP
	id AA03847 for ; Tue, 22 Jan 91 00:31:09 -0500
Received: from pucc.PRINCETON.EDU by mcnc.mcnc.org (5.59/MCNC/6-11-90)
	id AA02579; Mon, 21 Jan 91 23:33:12 -0500
	for Arisia.dnet.ge.com!Everhart
Message-Id: <9101220433.AA02579@mcnc.mcnc.org>
Received: from DGOGWDG1.BITNET by pucc.PRINCETON.EDU (IBM VM SMTP R1.2.2MX) with BSMTP id 1307; Mon, 21 Jan 91 23:33:16 EST
Received: from dnet.gwdg.de by DGOGWDG1.BITNET (Mailer R2.07) with BSMTP id
 7553; Mon, 21 Jan 91 22:20:21 MEZ
Date:    Mon, 21 Jan 1991 22:19:31 +0100
From: "GWDGV1::MOELLER" <mcnc!pucc.PRINCETON.EDU!moeller%gwdgv1.dnet.gwdg.de>
To: Arisia::Everhart, jim@mango.rsmas.miami.edu, R602MS5U@VB.CC.CMU.EDU,
        PALTER@UBVMS.BITNET, johnsond@calstate.bitnet, FOX@MSUNSCL.BITNET,
        awpsys@ultb.isc.rit.edu, ACCESS@KRDC.INT.Alcan.CA, moeller@dgogwdg5
Subject: VMS 5.4 ZTDRIVER part 4 of 4
 
+-+-+-+ Beginning of part 4 +-+-+-+
X`009movq`009irp$l_media(r3),-`009`009; ditto for parameter(s)
X`009`009ucb_a_ztmsg+zt_l_media(r5)
X`009movw`009ucb$w_sts(r5),-`009`009`009; ditto for ucb$w_sts
X`009`009ucb_a_ztmsg+zt_w_ucbsts(r5)
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`009movl`009#ss$_devoffline,-
X`009`009ucb_a_ztmsg+zt_w_iosts(r5)`009; default 1st iosb longword
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`009extzv`009#irp$v_fcode,#irp$s_fcode,-`009;get major function
X`009`009irp$w_func(r3),r2
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`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`009bicw`009#ucb$m_valid,ucb$w_sts(r5)`009;set invalid now
X`009bicw`009#ucb$m_valid,-
X`009`009ucb_a_ztmsg+zt_w_ucbsts(r5)`009;also update in message (!)
X`009brw`00920$
X;
X;
X; check if i/o is permitted
X;
X20$:
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`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`009bitw`009#ucb$m_cancel,ucb$w_sts(r5)`009;; I/O to be aborted?
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<r3,r4,r5>`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<r3,r4,r5>`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`009movzwl`009#ss$_cancel,-
X`009`009ucb_a_ztmsg+zt_w_iosts(r5)`009; set status
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`009bisw`009#<ucb$m_int>,ucb$w_sts(r5)`009;; re-set interrupt expected
X`009bicw`009#<ucb$m_timout!-`009`009;; clear timout,
X`009`009ucb$m_tim!-`009`009`009;;`009timed,
X`009`009ucb$m_power>,ucb$w_sts(r5)`009;;`009and powerfail
X`009rsb`009`009`009`009`009;; return!
X;
X;
X; server gone
X;
Xio_err_noserver:
X`009bicw`009#<ucb$m_online!ucb$m_valid>,-`009;clear valid & online
X`009`009ucb$w_sts(r5)
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`009bicw`009#<ucb$m_tim!ucb$m_int!-
X`009`009ucb$m_timout!ucb$m_power!-
X`009`009ucb$m_cancel>,-
X`009`009ucb$w_sts(r5)`009`009`009; cleanup some UCB flags
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`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`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`009movl`009ucb_a_ztmsg+zt_w_iosts(r5),r0`009`009; 1st iosb longword
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`009bbc`009#irp$v_virtual,irp$w_sts(r3),-`009;br if not virtual
X`009`009done
X;
X`009movq`009r0,-(sp)`009`009;save r0,r1
X`009movl`009irp$l_wind(r3),r4`009;...
X`009clrw`009wcb$w_nmap(r4)`009`009;...
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`009bbc`009#irp$v_virtual,-
X`009`009irp$w_sts(r0),70$`009;skip IRP if not virtual
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;`009`009
X;
Xpseudo_interrupt:
X;
X`009bbc`009#ucb$v_int,ucb$w_sts(r5),90$
X;
X`009bicw`009#<ucb$m_int!ucb$m_tim>,-
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`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
 
$ FILE_IS = "ZTDRIVER.OPT"
$ CHECKSUM_IS = 1701995486
$ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY
Xztdriver
X
Xbase=0
$ GOSUB UNPACK_FILE
 
$ FILE_IS = "ZTNS.H"
$ CHECKSUM_IS = 67703244
$ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY
X/* definitions for ZT<->ZTnetserver protocol */
X
Xtypedef struct IOSB `123
X`009unsigned short status;
X`009unsigned short count;
X`009unsigned long devdep;
X`125 IOSB;
X
X
X/***** ZTNS function codes *****/
X
X#define ZTNS_F_IOREQ`0091`009/* ZT -> NS: iofunc + count`009`009*/
X#define ZTNS_F_IOREPLY`0092`009/* NS -> ZT: iosb,`009`009`009*/
X`009`009`009`009/*   ZT -> NS: iosb.status only`009`009*/
X`009`009`009`009/*`009`009(after CMP & PUT)`009*/
X#define ZTNS_F_DATA`0093`009/* 'DMA' data (both directions): count`009*/
X#define ZTNS_F_R_DATA`0094`009/* NS -> ZT: accept <count> 'DMA' data`009*/
X#define ZTNS_F_W_DATA`0095`009/* NS -> ZT: send <count> 'DMA' data`009*/
X#define ZTNS_F_C_DATA`0096`009/* NS -> ZT: rcv.&check <count> 'DMA' data */
X
X
X/***** ZTNS network messages *****/
X
X#define ZTNS_DATALEN 1400`009/* max. #bytes transferred in 1 message */
X`009`009`009`009/* ? ought to be a single Ethernet packet ? */
X
Xtypedef struct ZTNS_MSG `123
X`009int seq;`009`009/* sequence number, starts at 1 */
X`009unsigned short func;`009/* ZTNS_F_xxxx */
X`009unsigned short iofunc;`009/* used by:`009IOREQ, IO$_xxx + modifiers */
X`009int count;`009`009/* used by:`009IOREQ (signed!),`032
X`009`009`009`009`009`009xxxDATA (total cnt),
X`009`009`009`009`009`009DATA (#bytes in 'data') */
X`009IOSB iosb;`009`009/* used by:`009IOREPLY */
X`009unsigned char data[ZTNS_DATALEN];`009/* used with DATA only */
X`125 ZTNS_MSG;
X
X#define ZTNS_LEN1 (sizeof(ZTNS_MSG) - ZTNS_DATALEN)`009/* short packet */
X
X/* note: length of a DATA packet must be ZTNS_LEN1 + (count) */
$ GOSUB UNPACK_FILE
 
$ FILE_IS = "ZTNS2.C"
$ CHECKSUM_IS = 1248242859
$ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY
X/* serve real tape, for ZT2/ZTserver */
X/* w.j.m. jun 1989 */
X
X/* implicit inputs:
X`009logical name ZT_TAPE points to a tape unit mounted foreign
X`009logical name ZT_NETOBJECT has equivalence `096node::"0=name/'
X*/
X
X#define T_LOGNAME "ZT_TAPE"
X#define N_LOGNAME "ZT_NETOBJECT"
X
X
X#include "ztns.h"`009/* includes IOSB, VMS_ITEM */
X
X#include ssdef
X#include lnmdef
X#include "iodef.h"`009/* my own! */
X#include "mtdef.h"
X
X#include stdio
X#include stddef
X#include string
X#include descrip
Xtypedef struct dsc$descriptor DESCR;
X
X#define CHECK(x) do `123unsigned s=x; if(!(s&1)) lib$stop(s);`125 while(0)
X#define FEHLER(m) do `123$DESCRIPTOR(d,m); Fehler(&d);`125 while(0)
X#define CHECKX(x) do `123unsigned s=x; if(!(s&1)) sys$exit(s);`125 while(0)
X#define MIN(a,b) ((a) < (b)) ? (a) : (b)
X
Xextern unsigned lib$get_foreign(),lib$stop();
Xextern unsigned sys$exit(),sys$assign(),sys$wake(),sys$hiber(),sys$trnlnm();
Xextern unsigned sys$qiow(int,int,int,
X`009`009`009IOSB*,void*,int,
X`009`009`009void*,int,int,int,int,int);
Xextern unsigned sys$qio(int,int,int,
X`009`009`009IOSB*,void*,int,
X`009`009`009void*,int,int,int,int,int);
X
Xtypedef struct VMS_ITEM `123
X`009unsigned short size;
X`009unsigned short code;
X`009void *bufp;
X`009int *lenp;
X`125 VMS_ITEM;
X
X
X/***** tape data *****/
Xstatic IOSB t_iosb;
Xstatic short t_chan;
Xstatic unsigned char t_buffer[0xFFFF];
X
Vstatic unsigned short ti_iofunc = 0;`009/* request function, !=0 while busy *
X/
Xstatic int ti_count;`009`009`009/* request count */
X
Xstatic unsigned char *ti_datap = NULL;`009/* input 'DMA' pointer,
X`009`009`009`009`009`009==NULL unless data expected */
Xstatic int ti_datacnt = 0;`009`009/* input 'DMA' counter */
X
Xstatic int/*logical*/ ti_replyexp = 0;`009/* IOREPLY expected */
Xstatic unsigned short ti_replysts;`009/* status from same */
X
X/***** network data *****/
Xstatic short n_chan;
X
X`009/* ZT -> NS */
Xstatic IOSB ni_iosb;
Xstatic ZTNS_MSG ni_buf;
Xstatic int ni_seq = 1;
X
X`009/* NS -> ZT (our output) */
Xstatic IOSB no_iosb;
Xstatic ZTNS_MSG no_buf;
Xstatic int no_seq = 1;
X
X
X/*****/
X
Xstatic void net_ast();`009`009/*forward*/
X
Xstatic void net_read()
X`123
X`009CHECKX(sys$qio(1,n_chan,IO$_READVBLK,
X`009`009&ni_iosb,net_ast,0,
X`009`009&ni_buf,sizeof(ni_buf),0,0,0,0));
X`125
X
X
Xstatic void net_send(fct)
Xint fct;`009/* ZTNS_F_xxxx */
X`123
X`009no_buf.seq = no_seq++;
X`009no_buf.func = fct;
X
X`009CHECKX(sys$qiow(0,n_chan,IO$_WRITEVBLK,
X`009`009&no_iosb,0,0,
X`009`009&no_buf,
X`009`009((fct == ZTNS_F_DATA) ? (ZTNS_LEN1 + no_buf.count) : ZTNS_LEN1),
X`009`0090,0,0,0));
X`009CHECKX(no_iosb.status);
X`125
X
X
Xstatic void net_start()
X`123
X`009char ncb[512];
X`009DESCR ncb_dsc = `1230-0,0,0,ncb`125;
X
X
X/* translate n_logname */
X
X`009`123
X`009`009$DESCRIPTOR(lnmtabdsc,"LNM$FILE_DEV");
X`009`009$DESCRIPTOR(lognamedsc,N_LOGNAME);
X`009`009VMS_ITEM lnmlist[] =
X`009`009`009`123`123sizeof(ncb),LNM$_STRING,ncb,&ncb_dsc.dsc$w_length`125,
X`009`009`009 `1230,0,NULL,NULL`125`125;
X
X`009`009CHECK(sys$trnlnm(0,&lnmtabdsc,&lognamedsc,0,lnmlist));
X`009`125
X
X/* assign channel to NET */
X
X`009`123
X`009`009$DESCRIPTOR(netdsc,"_NET:");
X
X`009`009CHECK(sys$assign(&netdsc,&n_chan,0,0));
X`009`125
X
X/* open connection */
X
X`009if(ncb_dsc.dsc$w_length+2+1+16+1 > sizeof(ncb)) `123
X`009`009FEHLER("ncb too small");
X`009`125
X
X`009/* clear conn.id & ncb info */
X`009memset(ncb+ncb_dsc.dsc$w_length,0,2+1+16);
X`009ncb_dsc.dsc$w_length += 2+1+16;
X
X`009/* add trailing quotemark */
X`009ncb[ncb_dsc.dsc$w_length] = '\"';
X`009ncb_dsc.dsc$w_length += 1;
X
X`009CHECK(sys$qiow(0,n_chan,IO$_ACCESS,
X`009`009`009&no_iosb,0,0,
X`009`009`0090,&ncb_dsc,0,0,0,0));
X`009CHECK(no_iosb.status);
X
X
X/* start async. read from net */
X
X`009net_read();
X`125
X
X
Xstatic void net_ast()`009/* here when we receive net msg */
X`123
X/* check I/O */
X`009CHECKX(ni_iosb.status);
X
X/* check seq# */
X`009if(ni_buf.seq != ni_seq++) FEHLER("prot.error: iseq");
X
X/* check minimum length */
X`009if(ni_iosb.count < ZTNS_LEN1) FEHLER("prot.error: msg too short");
X
X/* process low level function */
X`009switch(ni_buf.func) `123
X`009  default:
X`009`009FEHLER("prot.error: bad func1");
X`009`009return;
X
X`009  case ZTNS_F_IOREPLY:
X`009`009if(ti_replyexp == 0) `123
X`009`009`009FEHLER("prot.error: unexpected IOREPLY");
X`009`009`125
X`009`009ti_replyexp = 0;
X`009`009ti_replysts = ni_buf.iosb.status;
X`009`009CHECK(sys$wake(0,0));
X`009`009break;
X
X`009  case ZTNS_F_IOREQ:
X`009`009if(ti_iofunc != 0) `123`009/* still busy ... */
X`009`009`009FEHLER("prot.error: unexpected IOREQ");
X`009`009`125
X`009`009ti_iofunc = ni_buf.iofunc;
X`009`009ti_count = ni_buf.count;
X`009`009CHECK(sys$wake(0,0));
X`009`009break;
X
X`009  case ZTNS_F_DATA:
X`009`009if(ti_datap == NULL) `123
X`009`009`009FEHLER("prot.error: unexpected DATA");
X`009`009`125
X`009`009if(ni_iosb.count != ZTNS_LEN1 + ni_buf.count) `123
X`009`009`009FEHLER("prot.error: bad DATA msg length");
X`009`009`125
X`009`009if(ni_buf.count > ti_datacnt) `123
X`009`009`009FEHLER("prot.error: too much data");
X`009`009`125
X
X`009`009memcpy(ti_datap,ni_buf.data,ni_buf.count);
X`009`009ti_datacnt -= ni_buf.count;
X`009`009ti_datap += ni_buf.count;
X
X`009`009if(ti_datacnt == 0) `123`009/* data transfer complete */
X`009`009`009ti_datap = NULL;
X`009`009`009CHECK(sys$wake(0,0));
X`009`009`125
X`009`009break;
X`009`125
X
X/* wait for next msg */
X`009net_read();
X`125
X
X
X/*****/
X
Xstatic void tap_invfun()`009/* unsupported function or modifier */
X`123
X`009t_iosb.status = SS$_ILLIOFUNC;
X`009t_iosb.count = 0;
X`009/* leave devdepend! */
X`125
X
X
Xstatic void tap_0par()`009`009/* REWIND, REWINDOFF, WRITEOF */
X`123
X`009CHECK(sys$qiow(0,t_chan,ti_iofunc,
X`009`009`009&t_iosb,0,0,
X`009`009`0090,0,0,0,0,0));
X`125
X
X
Xstatic void tap_skiprec()`009/* skip records, forward/backward */
X`123
X`009CHECK(sys$qiow(0,t_chan,IO$_SKIPRECORD,
X`009`009`009&t_iosb,0,0,
X`009`009`009ti_count,0,0,0,0,0));
X
X/* must undo "end-of-volume" recognition */
X`009if(t_iosb.status == SS$_ENDOFVOLUME) `123
X`009`009unsigned char dummybuf[80];
X
X`009`009if(t_iosb.count != 0) FEHLER("skipcount !=0 on e.o.v.");
X
X`009`009CHECK(sys$qiow(0,t_chan,IO$_READVBLK,
X`009`009`009`009&t_iosb,0,0,
X`009`009`009`009dummybuf,sizeof(dummybuf),0,0,0,0));
X`009`009if(t_iosb.status != SS$_ENDOFFILE) `123
X`009`009`009/* tape position lost since EOF must be next */
X`009`009`009t_iosb.status = SS$_TAPEPOSLOST;
X`009`009`125
X`009`009t_iosb.count = 1;
X`009`125
X`125
X
X
Xstatic void tap_read_wchk(wchk)`009/* wchk==0: read, possibly w/datacheck */
Xint wchk;`009`009`009/* wchk==1: simulated writecheck */
X`123
X`009CHECK(sys$qiow(0,t_chan,wchk ? IO$_READLBLK : ti_iofunc,
X`009`009`009&t_iosb,0,0,
X`009`009`009t_buffer,ti_count,0,0,0,0));
X`009if(t_iosb.count > 0) `123
X`009`009int bcnt,c;
X`009`009unsigned char *bpt;
X
X`009`009no_buf.count = bcnt = MIN(t_iosb.count,ti_count);
X`009`009net_send(wchk ? ZTNS_F_C_DATA : ZTNS_F_R_DATA);
X`009`009bpt = t_buffer;
X`009`009do `123
X`009`009`009no_buf.count = c = MIN(bcnt,ZTNS_DATALEN);
X`009`009`009bcnt -= c;
X`009`009`009memcpy(no_buf.data,bpt,c);
X`009`009`009bpt += c;
X`009`009`009if(bcnt == 0) `123`009/* last message */
X`009`009`009`009ti_replyexp = 1;
X`009`009`009`125
X`009`009`009net_send(ZTNS_F_DATA);
X`009`009`125 while(bcnt > 0);
X`009`009CHECK(sys$hiber());`009/* wait for reply */
X
X`009`009if(wchk) `123`009/* CMP may have error = SS$_DATACHECK */
X`009`009`009if((ti_replysts & 1) == 0) `123
X`009`009`009`009t_iosb.status = ti_replysts;
X`009`009`009`125
X`009`009`125 else `123`009/* GET must be o.k. */
X`009`009`009CHECKX(ti_replysts);
X`009`009`125
X`009`125
X`125
X
X
Xstatic void tap_write()`009`009/* write, possibly w/datacheck */
X`123
X`009if(ti_count > 0) `123
X`009`009no_buf.count = ti_datacnt = ti_count;
X`009`009ti_datap = t_buffer;
X`009`009net_send(ZTNS_F_W_DATA);
X
X`009`009CHECK(sys$hiber());`009/* wait for data */
X`009`125
X
X`009CHECK(sys$qiow(0,t_chan,ti_iofunc,
X`009`009`009&t_iosb,0,0,
X`009`009`009t_buffer,ti_count,0,0,0,0));
X`125
X
X
Xstatic unsigned ztns()`009`009/* the action routine */
X`123
X`009int/*logical*/ ok = 1;
X`009unsigned fcode,fmodif;
X
X
X`009while(ok) `123
X
X/* sleep waiting for function */
X`009`009CHECK(sys$hiber());
X
X/* check & dispatch function */
X`009`009fcode = ti_iofunc & IO$M_FCODE;
X`009`009fmodif = ti_iofunc & IO$M_FMODIFIERS;
X`009`009switch(fcode) `123
X
X`009`009  case IO$_WRITEOF:`009/* no modifiers */
X`009`009`009if(fmodif != 0) `123
X`009`009`009`009tap_invfun();
X`009`009`009`125 else `123
X`009`009`009`009tap_0par();
X`009`009`009`125
X`009`009`009break;
X
X`009`009  case IO$_REWIND:`009/* mod: NOWAIT */
X`009`009  case IO$_REWINDOFF:`009/* mod: NOWAIT */
X`009`009`009if((fmodif & `126IO$M_NOWAIT) != 0) `123
X`009`009`009`009tap_invfun();
X`009`009`009`125 else `123
X`009`009`009`009tap_0par();
X`009`009`009`125
X`009`009`009break;
X
X`009`009  case IO$_SKIPRECORD:`009/* no modifiers */
X`009`009`009if(fmodif != 0) `123
X`009`009`009`009tap_invfun();
X`009`009`009`125 else `123
X`009`009`009`009tap_skiprec();
X`009`009`009`125
X`009`009`009break;
X
X`009`009  case IO$_READLBLK:`009/* mod: datacheck */
X`009`009`009`009`009/* note: datacheck is not end-to-end */
X`009`009`009if((fmodif & `126IO$M_DATACHECK) != 0) `123
X`009`009`009`009tap_invfun();
X`009`009`009`125 else `123
X`009`009`009`009tap_read_wchk(0);
X`009`009`009`125
X`009`009`009break;
X
X`009`009  case IO$_WRITELBLK:`009/* mod: datacheck */
X`009`009`009`009`009/* note: datacheck is not end-to-end */
X`009`009`009if((fmodif & `126IO$M_DATACHECK) != 0) `123
X`009`009`009`009tap_invfun();
X`009`009`009`125 else `123
X`009`009`009`009tap_write();
X`009`009`009`125
X`009`009`009break;
X
X`009`009  case IO$_WRITECHECK:`009/* no modifiers */
X`009`009`009`009`009/* note: this PHYSICAL I/O,`032
X`009`009`009`009`009simulated as end-to-end check */
X`009`009`009if(fmodif != 0) `123
X`009`009`009`009tap_invfun();
X`009`009`009`125 else `123
X`009`009`009`009tap_read_wchk(1);
X`009`009`009`125
X`009`009`009break;
X
X`009`009  default:
X`009`009`009tap_invfun();
X`009`009`125
X
X/* fudge */
X`009`009if(t_iosb.status == SS$_VOLINV) `123`009
X`009`009`009/* 'real' VALID bit apparently clear,
X`009`009`009/* since we can't change that,
X`009`009`009/* say MEDOFL (manual intervention required) */
X`009`009`009t_iosb.status = SS$_MEDOFL;
X`009`009`125
X
X/* say we're done */
X`009`009ti_iofunc = 0;
X
X/* return result from t_iosb */
X`009`009no_buf.iosb = t_iosb;
X`009`009net_send(ZTNS_F_IOREPLY);
X`009`125
X
X`009return 1;`009/* not reached */
X`125
X
X`009`009
X/*****/
X
Xstatic unsigned main()
X`123
X`009static $DESCRIPTOR(tape_dsc,T_LOGNAME);
X
X
X/* assign channel to tape */
X`009CHECK(sys$assign(&tape_dsc,&t_chan,0,0));
X
X/* rewind tape. this MUST succeed, otherwise something is wrong */
X/* in particular, this checks that the tape is mounted foreign */
X`009CHECK(sys$qiow(0,t_chan,IO$_REWIND,
X`009`009`009&t_iosb,0,0,
X`009`009`0090,0,0,0,0,0));
X`009CHECK(t_iosb.status);
X
X/* start the network connection & remote ZTserver */
X`009net_start();
X
X/* do our work ... */
X
X`009return ztns();
X`125
$ GOSUB UNPACK_FILE
 
$ FILE_IS = "ZTSERVER.MAR"
$ CHECKSUM_IS = 558612985
$ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY
X`009.title`009ZTSERVER subroutines
X;
X;`009w.j.m. jun 1989 (0.2)
X;`009documented 3-jul-1989 wjm
X;`009mod 18-aug-1989 wjm (0.9): support VMS V5
X;
X`009.ident`009/0.9/`009;this version needs ZTDRIVER 0.9
X;
X;*****
X; entries:
X;`009status=ZT_INIT()`009- initialize,`032
X;`009`009`009set up communication with ZTDRIVER.
X;`009`009`009NOTE: an exit handler is set up,
X;`009`009`009`009which MUST be executed eventually!
X;
X;`009status=ZT_WAIT()`009- wait for message (I/O request) from ZTDRIVER.
X;`009`009`009  On return, the 'message buffer' has been filled in.
X;
X;`009status=ZT_REQCOM()`009- send (part of) 'message buffer'
X;`009`009`009to ZTDRIVER and complete I/O.
X;
X;`009status=ZT_TOUSER()`009- simulates DMA transfer
X;`009`009`009from 'data buffer' to ZTDRIVER's user buffer.
X;`009`009`009Number of bytes tranferred:
X;`009`009`009`009 MIN(UCB$W_BCNT,ZT_BUFDSC.dsc$w_length)
X;`009`009`009Can be called at most once per I/O request!
X;
X;`009status=ZT_FRUSER()`009- simulates DMA transfer
X;`009`009`009from ZTDRIVER's user buffer to 'data buffer'.
X;`009`009`009Number of bytes transferred:
X;`009`009`009`009UCB$W_BCNT
X;`009`009`009On return, ZT_BUFDSC.dsc$w_length is set to this number.
X;`009`009`009Can be called at most once per I/O request!
X;`009`009`009`009`009
X;`009ZT_MSGDSC (globaldef):`032
X;`009`009`009Descriptor of 'message buffer' (see ZTDEF.MAR)
X;`009`009`009`009used for communication with ZTDRIVER.
X;`009`009`009Both buffer address and length are FIXED.
X;
X;`009ZT_BUFDSC (globaldef):
X;`009`009`009Descriptor of 'data buffer' (see above).
X;`009`009`009Buffer address is FIXED, actual size is `094xFFFF .
X;`009`009`009ZT_BUFDSC.dsc$w_length is set by ZT_FRUSER(),
X;`009`009`009`009and by caller of ZT_TOUSER().
X;
X;
X;*****
X;
X`009.link`009`009"SYS$SYSTEM:SYS.STB"/selective_search
X`009.library`009"SYS$LIBRARY:LIB"
X`009.library`009"ZT"
X;
X`009$dvidef
X`009$iodef
X`009$prdef
X`009$prvdef
X`009$ucbdef
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`009ztdef`009`009; ZT definitions - need $ucbdef
X;
X;
X;*****`009macros
X;
X.macro`009chkr0`009?lab
X`009blbs`009r0,lab
X`009pushl`009r0
X`009calls`009#1,g`094lib$stop
Xlab:
X.endm`009chkr0
X;
X;
X;*****`009PSECTs
X;
X`009.psect`009_data,quad
X`009.psect`009_code,nowrt
X`009.psect`009_nonpagedcode,page,nowrt
Xnonpagedcode:`009`009`009;start of psect
X`009.psect`009_nonpageddata,page
Xnonpageddata:`009`009`009;start of psect
X;
X`009.page
X;*****`009nonpaged data
X;
X`009.psect`009_nonpageddata
X;
Xzt_ucb:`009.long`0090
Xmbx_ucb:.long`0090
X;
X`009.align`009quad
Xzt_namedsc:
X`009.ascid`009"_ZTA0:"
X;
Xmbx_name_size=32
Xmbx_name:
X`009.blkb`009mbx_name_size
X`009.align`009quad
Xmbx_namedsc:
X`009.long`0090-0
X`009.long`009mbx_name
X;
X`009.align`009quad
Xzt_msgbuf:
X`009.byte`0090[zt_msglen]
X;
X;
X;*****`009get ucb from device name
X;`009`009called in kernel mode, IPL=0
X;`009`009p1 = addr of descriptor`009`009! must be nonpaged
X;`009`009p2 = addr of ucb-address`009! must be nonpaged
X;
X`009.psect`009_nonpagedcode
Xdev_to_ucb:`009.word`009`094m<r2,r3,r4,r5>
X;
X`009movl`009g`094CTL$GL_PCB,r4
X;
X`009jsb`009g`094SCH$IOLOCKR`009`009;;***** start IPL2 *********************
X`009movaq`009@1*4(ap),r1`009`009;;
X`009jsb`009g`094IOC$SEARCHDEV`009`009;;search for device
X`009blbc`009r0,90$`009`009`009;;
X`009movl`009r1,@2*4(ap)`009`009;;r1=UCB
X90$:`009`009`009`009`009;;
X`009pushl`009r0`009`009`009;;
X`009jsb`009g`094SCH$IOUNLOCK`009`009;; Unlock the I/O database
X`009setipl`009#0`009`009`009;;***** end IPL2 ***********************
X`009popl`009r0
X;
X`009ret
X;
X;
X;*****`009fake ZT interrupt
X;`009`009called in kernel mode, IPL=0
X;
X`009.psect`009_nonpagedcode
Xzt_int:`009.word`009`094m<r2,r3,r4,r5>
X;
X`009movl`009zt_ucb,r5
X`009beql`00990$
X;
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`009jsb`009@ucb_l_inter(r5)`009`009;;
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;
X90$:
X`009movl`009#1,r0
X`009ret
X;
X;
X;*****`009kernel mode setup
X;`009`009called via $CMKRNL
X;
X`009.psect`009_nonpagedcode
Xk_setup:`009.word`009`094m<r2,r3,r4,r5>
X;
X`009movab`009g`094lib$sig_to_ret,(fp)
X;
X`009pushal`009mbx_ucb
X`009pushaq`009mbx_namedsc
X`009calls`009#2,dev_to_ucb
X`009blbc`009r0,90$
X;
X`009pushal`009zt_ucb
X`009pushaq`009zt_namedsc
X`009calls`009#2,dev_to_ucb
X`009blbc`009r0,90$
X;
X`009movl`009zt_ucb,r5
X`009movl`009mbx_ucb,ucb_l_ztmbx(r5)
X;
X`009bicw`009#ucb$m_valid,ucb$w_sts(r5)
X`009bisw`009#ucb$m_online,ucb$w_sts(r5)
X;
X`009calls`009#0,zt_int`009`009; interrupt unconditionally,
X`009`009`009`009`009; to clear potential hang
X;
X`009movl`009#1,r0
X90$:
X`009ret
X;
X;
X;*****`009kernel mode shutdown
X;`009`009called via $CMKRNL
X;
X`009.psect`009_nonpagedcode
Xk_shut:`009.word`009`094m<r2,r3,r4,r5>
X;
X`009movab`009g`094lib$sig_to_ret,(fp)
X;
X`009movl`009zt_ucb,r5
X`009beql`00990$
X;
X`009clrl`009ucb_l_ztmbx(r5)
X`009bicw`009#<ucb$m_online!ucb$m_valid>,ucb$w_sts(r5)
X;
X; ggf. terminate i/o
X;
X`009bicw`009#<ucb$m_online!ucb$m_valid>,-
X`009`009zt_msgbuf+zt_w_ucbsts`009`009;just in case ...
X;
X`009calls`009#0,zt_int`009`009`009;interrupt unconditionally
X;
X90$:
X`009movl`009#1,r0
X`009ret
X;
X;
X;*****`009kernel mode data transfer server -> driver's user
X;`009`009called via $CMKRNL
X;`009`009implicit inputs:`032
X;`009`009`009- 'zt_ucb'
X;`009`009`009- data in 'buffer'
X;`009`009`009- byte count in 'bufbct'
X;`009`009implicit outputs:
X;`009`009`009- ucb$w_bcnt cleared
X;
X`009.psect`009_nonpagedcode
Xk_touser:`009.word`009`094m<r2,r3,r4,r5>
X;
X`009movab`009g`094lib$sig_to_ret,(fp)
X;
X`009clrl`009r0
X`009movab`009buffer,r1`009`009; 'from' address
X`009movzwl`009bufbct,r2`009`009; byte count
X`009movl`009zt_ucb,r5
X`009beql`00990$
X`009cmpw`009r2,ucb$w_bcnt(r5)`009;sanity check
X`009bgtru`00990$`009`009`009;br if it failed
X;
X.if eq smp_code
X`009dsbint`009ucb$b_fipl(r5)`009`009`009;;
X.iff`009`009`009`009`009`009;;
X`009forklock`009-`009`009`009;;
X`009`009lock=ucb$b_flck(r5),-`009`009;;
X`009`009savipl=-(sp),-`009`009`009;;
X`009`009preserve=NO,-`009`009`009;;
X`009`009fipl=YES`009`009`009;;`032
X.endc`009`009`009`009`009`009;;
X`009`009`009`009`009`009;;
X`009tstl`009r2`009`009`009`009;;
X`009bleq`00928$`009`009`009`009;; br if nothing to be moved!
X`009jsb`009ioc$movtouser`009`009`009;;
X28$:`009`009`009`009`009`009;;
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`009forkunlock`009-`009`009`009;;
X`009`009lock=ucb$b_flck(r5),-`009`009;;
X`009`009newipl=(sp)+,-`009`009`009;;
X`009`009preserve=NO`009`009`009;;
X.endc
X;
X`009clrw`009ucb$w_bcnt(r5)`009`009;make sure we don't copy twice
X;
X`009movl`009#1,r0
X90$:
X`009ret
X;
X;
X;*****`009kernel mode data transfer, driver's user -> server
X;`009`009called via $CMKRNL
X;`009`009implicit inputs:`032
X;`009`009`009- 'zt_ucb'
X;`009`009`009- byte count in 'zt_msgbuf+zt_w_bnct'
X;`009`009implicit outputs:
X;`009`009`009- data in 'buffer'
X;`009`009`009- byte count in 'bufbct'
X;`009`009`009- ucb$w_bcnt cleared
X;
X`009.psect`009_nonpagedcode
Xk_fruser:`009.word`009`094m<r2,r3,r4,r5>
X;
X`009movab`009g`094lib$sig_to_ret,(fp)
X;
X`009clrl`009r0
X`009clrl`009bufbct
X`009movl`009zt_ucb,r5
X`009beql`00990$
X;
X`009movab`009buffer,r1`009`009;'to' address
X`009movzwl`009zt_msgbuf+zt_w_bcnt,r2`009;byte count
X`009cmpw`009r2,ucb$w_bcnt(r5)`009;sanity check
X`009bgtru`00990$`009`009`009;br if it failed
X`009movl`009r2,bufbct
X;
X.if eq smp_code
X`009dsbint`009ucb$b_fipl(r5)`009`009`009;;
X.iff`009`009`009`009`009`009;;
X`009forklock`009-`009`009`009;;
X`009`009lock=ucb$b_flck(r5),-`009`009;;
X`009`009savipl=-(sp),-`009`009`009;;
X`009`009preserve=NO,-`009`009`009;;
X`009`009fipl=YES`009`009`009;;`032
X.endc`009`009`009`009`009`009;;
X`009`009`009`009`009`009;;
X`009tstl`009r2`009`009`009`009;;
X`009bleq`00928$`009`009`009`009;; br if nothing to be moved!
X`009jsb`009ioc$movfruser`009`009`009;;
X28$:`009`009`009`009`009`009;;
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`009forkunlock`009-`009`009`009;;
X`009`009lock=ucb$b_flck(r5),-`009`009;;
X`009`009newipl=(sp)+,-`009`009`009;;
X`009`009preserve=NO`009`009`009;;
X.endc
X;
X`009clrw`009ucb$w_bcnt(r5)`009`009;make sure we don't copy twice
X;
X`009movl`009#1,r0
X90$:
X`009ret
X;
X;
X;*****`009kernel mode copy of 'result' to driver & activate driver
X;`009`009called via $CMKRNL
X;`009`009implicit inputs:`032
X;`009`009`009- 'zt_ucb'
X;`009`009`009- 'result'
X;
X`009.psect`009_nonpagedcode
Xk_reqcom:`009.word`009`094m<r2,r3,r4,r5>
X;
X`009movab`009g`094lib$sig_to_ret,(fp)
X;
X`009clrl`009r0
X`009movl`009zt_ucb,r5
X`009beql`00990$
X;
X`009movc3`009#zt_msglen,zt_msgbuf,ucb_a_ztmsg(r5)`009;copy back
X;
X`009calls`009#0,zt_int`009`009`009`009;'interrupt' driver
X;
X`009movl`009#1,r0
X90$:
X`009ret
X;
X;
X;*****`009get mailbox & its name
X;
X`009.psect`009_data
X;
Xmbx_dvi_itm:
X`009.word`009mbx_name_size
X`009.word`009dvi$_fulldevnam
X`009.long`009mbx_name
X`009.long`009mbx_namedsc
X`009.long`0090
X;
X`009.align `009quad
Xmbxiosb:
X`009.blkw`0094
X;
Xmbxchan:
X`009.blkw
X;
X;
X`009.psect`009_code
Xget_mbx:`009.word`009`094m<>
X;
X`009$crembx_s`009prmflg=#0,-
X`009`009`009chan=mbxchan,-
X`009`009`009maxmsg=#zt_msglen,-
X`009`009`009-;`009???`009`009bufquo=#zt_msglen,-
X`009`009`009promsk=#0
X`009chkr0
X;
X`009$getdvi_s`009chan=mbxchan,-
X`009`009`009itmlst=mbx_dvi_itm
X`009chkr0
X;
X`009ret
X;
X;
X;*****`009start mbx read
X;
X`009.psect`009_code
Xread_mbx:`009.word`009`094m<>
X;
X`009$qio_s`009efn=#1,-
X`009`009chan=mbxchan,-
X`009`009func=#io$_readvblk,-
X`009`009iosb=mbxiosb,-
X`009`009p1=zt_msgbuf,-
X`009`009p2=#zt_msglen
X`009chkr0
X;
X`009ret
X;
X;
X;*****`009wait for mbx message
X;
X`009.psect`009_code
Xwait_mbx:`009.word`009`094m<>
X;
X`009$synch_s`009efn=#1,-
X`009`009`009iosb=mbxiosb
X`009chkr0
X;
X`009movzwl`009mbxiosb,r0
X`009chkr0
X;
X`009cmpw`009mbxiosb+2,#zt_msglen
X`009beql`00950$
X`009clrl`009r0
X`009chkr0
X50$:
X`009ret
X;
X;
X;*****`009exit handler
X;
X;
X`009.psect`009_data
Xcurprivs:
X`009.quad`0090
X;
X;
X`009.psect`009_code
Xexith:`009.word`009`094m<>
X;
X`009$setprv_s`009enbflg=#1,-`009`009;establish initial privileges
X`009`009`009prvadr=curprivs`009`009;(sometimes removed by rundown)
X`009chkr0
X;
X`009$cmkrnl_s`009routin=k_shut,-
X`009`009`009arglst=(ap)
X`009chkr0
X;
X`009ret
X;
X;
X;*****`009setup exit handler
X;
X`009.psect`009_data
X;
X`009.align`009long
Xexstat:
X`009.blkl
Xexblk:
X`009.long`0090-0,exith,1,exstat
X;
X;
X`009.psect`009_code
Xsetup_exith:`009.word`009`094m<>
X;
X`009$dclexh_s`009desblk=exblk
X`009chkr0
X;
X`009ret
X;
X`009.page
X;*****`009externalized routines & data
X;
X`009.psect`009_nonpageddata
X`009.align`009quad
XZT_BUFDSC::
Xbufbct:
X`009.long`0090-0,buffer
X;
X;
X`009.psect`009_data
X`009.align`009quad
XZT_MSGDSC::
X`009.long`009zt_msglen,zt_msgbuf
X;
X;
X;*****`009initialize
X;
X`009.psect`009_data
X;
X`009.align`009quad
Xlkwdata_inadr:
X`009.long`009nonpageddata,nonpageddata_end
Xlkwcode_inadr:
X`009.long`009nonpagedcode,nonpagedcode_end
X;
X;
X`009.psect`009_code
X.entry`009ZT_INIT,`094m<>
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`009$lkwset_s`009inadr=lkwcode_inadr`009; lock "nonpaged" code
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`009$cmkrnl_s`009routin=k_setup,-`009; start zt:
X`009`009`009arglst=(ap)
X`009ret
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`009$cmkrnl_s`009routin=k_touser,-
X`009`009`009arglst=(ap)
X`009ret
X;
X;
X;*****`009move data from user to 'buffer'
X;
X`009.psect`009_code
X.entry`009ZT_FRUSER,`094m<>
X;
X`009$cmkrnl_s`009routin=k_fruser,-
X`009`009`009arglst=(ap)
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`009$cmkrnl_s`009routin=k_reqcom,-
X`009`009`009arglst=(ap)
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`009.psect`009_nonpagedcode
Xnonpagedcode_end:
X`009.blkb
X;
X`009.end
$ GOSUB UNPACK_FILE
$ EXIT
-+-+-+-+-+ End of part 4 +-+-+-+-+-
