# VIRTIO - VIRTUAL IO FUNCTION FOR SMMACP # # PURPOSE: TO IMPLEMENT THE STEPS NECESSARY TO PERFORM VIRTUAL IO REQUESTS # QUEUED TO THE ACP FROM THE MPP DEVICE DRIVER # # CALLING SEQUENCE: # # INTEGER*4 = VIRTIO(VCN,VDIM,NBUFF,NMOVE,STBA,STBL,OUT,RECSIZ, # BASADR,BASCNT,BASWGT,BASLEN,BASCUR,IOPCKT) # # INPUTS: VCN IS THE VIRTUAL CHANNEL NUMBER # VDIM IS THE MAXIMUM NUMBER OF VARIABLE DIMENSIONS REQUIRED BY # ANY MOVE ON THE CHANNEL # NBUFF IS THE NUMBER OF BUFFERS RESERVED FOR THE CHANNEL # NMOVE IS THE NUMBER OF MOVES SUPPORTED BY THE CHANNEL # STBA IS THE BASE ADDRESS OF THE ASSIGNED STAGER BUFFER FOR THE # CHANNEL # STBL IS THE LENGTH OF EACH STAGER BUFFER IN STAGER WORDS # OUT IS THE ARRAY OF HARDWARE CONTROL PARAMETERS COMPUTED FOR # THE CHANNEL # RECSIZ IS A VECTOR OF RECORD SIZES FOR EACH MOVE # BASADR IS A VECTOR OF BUFFER RELATIVE BASE ADDRESSES FOR THE FIRST # RECORD IN EACH MOVE # BASCNT IS A VECTOR CONTAINING THE NUMBER OF VARIABLE DIMENSIONS # USED BY EACH MOVE TO MODIFY THE RECORD STARTING ADDRESS # BASWGT IS AN ARRAY OF WEIGHTS FOR EACH VARIABLE DIMENSION OF EACH # MOVE AND IS USED TO MODIFY THE STAGER ADDRESS USED FOR EACH # RECORD MOVED ON A CHANNEL # BASLEN IS AN ARRAY OF VARIABLE DIMENSION LENGTHS FOR EACH MOVE # BASCUR IS AN ARRAY OF CURRENT INDICES FOR THE VARIABLE DIMENSIONS # OF EACH MOVE. BASCUR(I,MV) IS BOUNDED BY 0 <= BASCUR(I,MV) <= # BASLEN(I,MV) - 1 # IOPCKT IS THE IO PACKET CURRENTLY BEING PROCESSED # # OUTPUTS: INTEGER*4 RESULT IS THE FINAL IO STATUS TO BE RETURNED TO THE USER # PROGRAM THAT ISSUED THE VIRTUAL TRANSFER REQUEST. BASCUR IS # UPDATED TO REFLECT THE TRANSFER OF THE RECORD OF DATA. # # NOTE: THE ARGUMENTS TO THIS SUBROUTINE, WHILE NAMED SIMILARLY TO VARIABLES # IN THE FORTRAN COMMON SMMDAT, ARE NOT IN COMMON. RATHER THEY ARE # PASSED VIA A CALLING SEQUENCE CONTAINED IN THE CHANNEL WINDOW BLOCK. # THE ADDRESSES IN THE CALLING SEQUENCE POINT TO CHANNEL DATA # STRUCTURES LOCATED IN POOL. THESE STRUCTURES AND THE CALLING SEQUENCE # WERE DEVELOPED DURING THE CREATION OR OPENING OF THE VIRTUAL CHANNEL. # # PROGRAMMER: CARL T. MICKELSON # GOODYEAR AEROSPACE CORP # 1210 MASSILLON ROAD # AKRON, OHIO 44315 # # PROJECT: MASSIVELY PARALLEL PROCESSOR (MPP) # # DATE: APRIL 1983 # ## INCLUDE "SMMDEF.RAT" INCLUDE "MPPQIODEF.RAT" DEFINE (ISCLR,0) #-EVENT FLAG ALREADY CLEAR DEFINE (ISSUC,1) #-QIO SUCCESS RESPONSE CODE DEFINE (ISSET,2) #-EVENT FLAG ALREADY SET DEFINE (IEABO,-15) #-QIO ABORT ERROR CODE DEFINE (IETMO,-74) #-QIO TIMEOUT ERROR CODE DEFINE (TMOTMG,120) #-TIMEOUT COUNT IS 120 DEFINE (TMOUNT,1) #-CLOCK TICKS DEFINE (MXFUNC,4) #-NUMBER OF VIRTUAL IO FUNCTION TYPES SUPPORTED INTEGER*4 FUNCTION VIRTIO(VCN,VDIM,NBUFF,NMOVE,STBA,STBL,OUT,RECSIZ, BASADR,BASCNT,BASWGT,BASLEN,BASCUR,IOPCKT) INTEGER VCN #-VIRTUAL CHANNEL NUMBER INTEGER VDIM #-MAXIMUM VARIBLE DIMENSION COUNT INTEGER NBUFF #-NUMBER OF BUFFERS INTEGER NMOVE #-NUMBER OF MOVES INTEGER*4 STBA #-STAGER BUFFER BASE ADDRESS INTEGER*4 STBL #-STAGER BUFFER LENGTH BYTE OUT(0:CTLNM1,NMOVE) #-HARDWARE CONTROL PARAMETERS INTEGER*4 RECSIZ(NMOVE) #-RECORD SIZES FOR CHANNEL MOVES INTEGER*4 BASADR(NMOVE) #-BUFFER RELATIVE STARTING ADDRESSES INTEGER BASCNT(NMOVE) #-VARIABLE DIMENSION COUNT FOR EACH MOVE INTEGER*4 BASWGT(VDIM,NMOVE) #-ADDRESS ADJUSTMENT WEIGHTS FOR EACH MOVE INTEGER BASLEN(VDIM,NMOVE) #-ADDRESS ADJUSTMENT LENGTHS FOR EACH MOVE INTEGER BASCUR(VDIM,NMOVE) #-ADDRESS ADJUSTMENT INDICES FOR EACH MOVE INTEGER IOPCKT(0:1) #-IO PACKET IN PROCESS INTEGER DRVQIO #-ISSUE QIO FUNCTION SUBROUTINE INTEGER IOSTRT #-IOCU PROGRAM START FUNCTION COMMON /LSTWIN/NBNKS,LASTWB,LASTMV INTEGER NBNKS INTEGER LASTWB(2),LASTMV(2) INTEGER PARS(6),CURWIN,IMV,IBN,IFUNC,ISUBF,IWIN INTEGER IQWVB,IQRVB,FNCIDX,FLGSNS,SIGN INTEGER XFRTYP(MXFUNC),LOGFNC(MXFUNC),FLGLEN(MXFUNC),ITLFLG(5,MXFUNC) LOGICAL FIRST INTEGER ISTAT(2),IXFRLN(2),STADDR(2),ISTADR(2),CTLWRD INTEGER*4 DSTAT,DXFRLN,STGADR BYTE CSTAT(4),STADRC(4),CSTADR(4),CTLBYT(2) EQUIVALENCE (DSTAT,ISTAT(1),CSTAT(1)) EQUIVALENCE (DXFRLN,IXFRLN(1)) EQUIVALENCE (STGADR,STADDR(1),STADRC(1)) EQUIVALENCE (ISTADR(1),CSTADR(1)) EQUIVALENCE (CTLWRD,CTLBYT(1)) DATA PARS/6*0/,FIRST/.TRUE./,SIGN/"100000"/ DATA XFRTYP/WVBHST,WVBARU,RVBHST,RVBARU/ DATA LOGFNC/WLBSTG,0,RLBSTG,0/ DATA FLGLEN/10,10,10,10/ DATA ITLFLG/"160400","120400","146400","106400","136400", "130400","160400","120400","146400","106400", "160404","120404","136404","146404","106404", "130404","160404","120404","146404","106404"/ # START PROC DEFINITIONS PROC RSTSTG $( # RESET STAGING MEMORY CONTROL CALL DRVQIO(CTLRST,AC0LUN,PARS) # ISSUE RESET QIO TO STAGER $) # END RSTSTG PROC #-END PROC DEFINITIONS IF (FIRST) $( # DO SOME FIRST TIME INITIALIZATION IQWVB = IAND(XFRTYP(1),"177400") # SET IQWVB FUNCTION CODE IQRVB = IAND(XFRTYP(3),"177400") # SET IQRVB FUNCTION CODE FIRST = .FALSE. # CLEAR FIRST TIME FLAG $) # END INITIALIZATION BLOCK DSTAT = 0 # INITIALIZE IO STATUS CSTAT(1) = IEABO # ASSUME FUNCTION WILL FAIL CALL ASTSON(0) # ENABLE AST LINKAGE FOR IO FUNCTION IMV = IAND("17",ISHFT(IOPCKT(13),-12)) # GET MOVE NUMBER FROM PARAMETERS IF ((IMV == 0) .OR. (IMV > NMOVE)) $( # IF MOVE NUMBER IS ILLEGAL CSTAT(2) = -27 # SET IO FUNCTION ERROR CODE $) # END ILLEGAL MOVE BLOCK ELSE $( # ELSE, MOVE NUMBER IS LEGAL IFUNC = IAND(IOPCKT(5),"177400") # GET IO FUNCTION CODE FROM IO PACKET ISUBF = IAND(IOPCKT(5),"377") # GET IO SUB-FUNCTION CODE FROM IO PACKET IF ((IFUNC == IQWVB) .AND. (IMV .NE. 1)) $( # IF STAGER INPUT NOT MOVE ONE CSTAT(2) = -28 # SET IO FUNCTION ERROR CODE $) # END ILLEGAL INPUT MOVE BLOCK ELSE $( # ELSE, INPUT MOVE IS NUMBER ONE IF ((IFUNC == IQRVB) .AND. (IMV == 1)) $( # IF STAGER OUTPUT IS MOVE ONE CSTAT(2) = -29 # SET IO FUNCTION ERROR CODE $) # END ILLEGAL OUTPUT MOVE BLOCK ELSE $( # ELSE, OUTPUT MOVE IS NOT NUMBER ONE IBN = IAND("17",ISHFT(IOPCKT(13),-8)) # GET BUFFER NUMBER FROM PARAMETERS IF ((IBN == 0) .OR. (IBN > NBUFF)) $( # IF BUFFER NUMBER IS ILLEGAL CSTAT(2) = -30 # SET IO FUNCTION ERROR CODE $) # END ILLEGAL BUFFER BLOCK ELSE $( # ELSE, BUFFER NUMBER IS LEGAL DO FNCIDX = 1,MXFUNC $( # FIND FUNCTION INDEX FOR CURRENT REQUEST IF (IOPCKT(5) == XFRTYP(FNCIDX)) BREAK $) IF (FNCIDX .GT. MXFUNC) $( # IF FUNCTION MATCH NOT FOUND CSTAT(2) = -36 # SET IO FUNCTION ERROR CODE $) # END FUNCTION NOT FOUND BLOCK ELSE $( # ELSE, START SETUP FOR IO IXFRLN(1) = IOPCKT(12) # GET TRANSFER LENGTH FROM PARAMETERS IXFRLN(2) = 0 IF (LOGFNC(FNCIDX) == 0) $( # IF AN ARU/STAGER TRANSFER DXFRLN = JISHFT(DXFRLN,7) # CONVERT ARU COLUMNS TO BITS $) # END ARU/STAGER BLOCK ELSE $( # ELSE, ITS A HOST/STAGER TRANSFER DXFRLN = JISHFT(DXFRLN,3) # CONVERT BYTES TO BITS $) # END HOST/STAGER TRANSFER BLOCK IF (DXFRLN .NE. RECSIZ(IMV)) $( # IF TRANSFER LENGTH IS WRONG FOR MOVE CSTAT(2) = -31 # SET IO FUNCTION ERROR CODE $) # END BAD RECSIZ BLOCK ELSE $( # ELSE, TRANSFER LENGTH IS OK CALL GETADR(CURWIN,VCN) # GET ADDRESS OF VIRTUAL CHANNEL NUMBER CURWIN = CURWIN - 2 # POINT ADDRESS TO START OF WINDOW BLOCK IWIN = 2 # SET LAST WINDOW INDEX FOR OUTPUT MOVE IF (IMV == 1) IWIN = 1 # IF INPUT MOVE, SET NEW LAST WINDOW INDEX IF ((CURWIN .NE. LASTWB(IWIN)) .OR. (IMV .NE. LASTMV(IWIN))) $( # IF NOT SAME WINDOW AND MOVE AS LAST TRANSFER CALL GETADR(PARS(1),OUT(0,IMV)) # SET OUT ARRAY ADDRESS PARS(2) = CTLENG - 2 # PROGRAM FULL CONTROLLER PARS(3) = IAND(ITLFLG(1,FNCIDX),"377") # SELECT INPUT/OUTPUT CONTROL PARS(4) = 0 # START AT INTERNAL ADDRESS ZERO PARS(5) = 0 # NO CONTROL FLAG FUNCTION BUFFER PARS(6) = 0 ISTAT(1) = DRVQIO(WLBSCB,AC1LUN,PARS,ISTAT(2)) # ISSUE QIO FUNCTION IF (CSTAT(1) == ISSUC) $( # IF SUCCESSFUL LASTWB(IWIN) = CURWIN # SAVE CURRENT WINDOW BLOCK ADDRESS LASTMV(IWIN) = IMV # AND MOVE NUMBER $) # END SUCCESSFUL BLOCK ELSE $( # ELSE, TERMINATE QIO FUNCTION LASTWB(IWIN) = 0 # INVALIDATE SAVED WINDOW POINTER LASTMV(IWIN) = 0 # AND MOVE NUMBER CSTAT(2) = -32 # SET IO FUNCTION ERROR CODE $) # END TERMINATE QIO BLOCK $) # END NOT SAME WINDOW BLOCK IF (CSTAT(2) == 0) $( # IF OK TO COMPUTE STAGER ADDRESS STGADR = BASADR(IMV) + ((32/NBNKS) * (STBA + (IBN - 1) * STBL)) # COMPUTE STAGER HARDWARE ADDRESS IF (VDIM .NE. 0) $( # IF THERE ARE SOME VARIABLE DIMENSIONS IF (BASCNT(IMV) .NE. 0) $( # FOR THIS MOVE DO I = 1,BASCNT(IMV) $( # THEN UPDATE STAGER ADDRESS STGADR = STGADR + (BASCUR(I,IMV) * BASWGT(I,IMV)) $) # END UPDATE ADDRESS BLOCK $) # END THIS MOVE BLOCK $) # END ANY VARIABLE DIMENSIONS BLOCK #-RE-ORGANIZE STGADR PRIOR TO TRANSFER TO HARDWARE SO THAT BITS #-ARE PROPERLY LOADED BY DRIVER INTO CONTROL UNIT A REGISTER CSTADR(1) = STADRC(4) CSTADR(2) = STADRC(3) CSTADR(3) = STADRC(2) CSTADR(4) = STADRC(1) CALL GETADR(PARS(1),CSTADR(1)) # SET OUT ARRAY ADDRESS PARS(2) = 4 # PROGRAM ONLY FOUR BYTES IN CONTROLLER PARS(3) = IAND(ITLFLG(1,FNCIDX),"377") # SELECT INPUT/OUTPUT CONTROL PARS(4) = 406 # START AT LAST STAGE OF A REGISTER PARS(5) = 0 # NO CONTROL FLAG FUNCTION BUFFER PARS(6) = 0 ISTAT(1) = DRVQIO(WLBSCB,AC1LUN,PARS,ISTAT(2)) # ISSUE QIO FUNCTION IF (CSTAT(1) .NE. ISSUC) $( # IF NOT SUCCESSFUL, TERMINATE QIO CSTAT(2) = -33 # SET IO FUNCTION ERROR CODE $) # END TERMINATE QIO BLOCK ELSE $( # ELSE, STAGER ADDRESS SETUP DONE CTLWRD = "76000" # SET UP INTERRUPT MASK BYTE CALL GETADR(PARS(1),CTLWRD) # SET DATA BUFFER ADDRESS PARS(2) = 2 # SET TRANSFER LENGTH PARS(3) = IAND(ITLFLG(1,FNCIDX),"377") # SELECT INPUT/OUTPUT CONTROL PARS(4) = 470 # SET INTERNAL DEVICE ADDRESS ISTAT(1) = DRVQIO(WLBSCB,AC1LUN,PARS,ISTAT(2)) # ISSUE QIO FUNCTION IF (CSTAT(1) .NE. ISSUC) $( # IF NOT SUCCESSFUL, TERMINATE QIO CSTAT(2) = -46 # SET IO FUNCTION ERROR CODE $) # END TERMINATE QIO BLOCK ELSE $( # ELSE, INTERRUPT MASK SETUP DONE CALL CLREF(STEEFG) # CLEAR STAGER RELATED EVENT FLAGS CALL CLREF(STGEFG) CTLBYT(1) = OUT(443,IMV) # GET LAST BYTES OF CONTROL DATA CTLBYT(2) = OUT(444,IMV) IF (LOGFNC(FNCIDX) == 0) $( # IF AN ARU TRANSFER PARS(1) = 0 # DO NOT PROGRAM INTERFACE CONTROL GROUP PARS(2) = 0 # JUST DO FLAGS TO START OPERATION $) # END ARU TRANSFER BLOCK PARS(4) = IOR(SIGN,443) # SET INTERNAL DEVICE ADDRESS CALL GETADR(PARS(5),ITLFLG(1,FNCIDX)) # SET FLAG BUFFER ADDRESS PARS(6) = FLGLEN(FNCIDX) # SET FLAG BUFFER LENGTH ISTAT(1) = DRVQIO(WLBSCB,AC1LUN,PARS,ISTAT(2)) # ISSUE QIO FUNCTION IF (CSTAT(1) .NE. ISSUC) $( # IF QIO FAILED CSTAT(2) = -37 # SET IO FUNCTION ERROR CODE PARS(1) = SGFLAG # RESET STAGING MEMORY CONTROL PERFORM RSTSTG $) # END QIO FAILED BLOCK ELSE $( # ELSE, STAGER INTERFACE SETUP DONE IF (LOGFNC(FNCIDX) .NE. 0) $( # IF HOST/STAGER IO REQUESTED #-NOTE: THE USER PROGRAM STAGER DATA BUFFER ADDRESS HAS #-ALREADY BEEN RELOCATED AND MAPPED INTO EXECUTIVE ADDRESS #-SPACE BY THE DRIVER BEFORE BEING SENT TO THE ACP. #-THEREFORE, IT IS NECESSARY TO SEND THE EXECUTIVE #-DATA BUFFER ADDRESS WORD PAIR AND LENGTH TO THE DRIVER NOW #-TO DO THE DMA TRANSFER TO THE STAGER. THE DRIVER #-BYPASSES PARAMETER CHECKING FOR SF.STG DMA DATA REQUESTS #-IF THEY ARE ISSUED BY ACP CLASS PROGRAMS. PARS(1) = IOPCKT(10) # SET UP BUFFER RELOCATION BIAS PARS(2) = IOPCKT(11) # SET UP BUFFER KAPR6 VIRTUAL ADDRESS PARS(3) = IOPCKT(12) # SET UP TRANSFER LENGTH PARS(4) = 0 PARS(5) = 0 PARS(6) = 0 ISTAT(1) = DRVQIO(LOGFNC(FNCIDX),AC1LUN,PARS,ISTAT(2)) # ISSUE QIO FUNCTION IF (CSTAT(1) .NE. ISSUC) $( # IF QIO FAILED CSTAT(2) = -38 # SET IO FUNCTION ERROR CODE $) # END QIO FAILED BLOCK $) # END HOST/STAGER IO BLOCK ELSE $( # ELSE, DO A STAGER/ARU MEMORY IO TRANSFER CALL CLREF(IOEEFG) # CLEAR IOCU RELATED EVENT FLAGS CALL CLREF(IOPEFG) CALL SETEF(STGEFG) #-SET STAGER EVENT FLAG SINCE STAGER #-STATUS INTS DO NOT OCCUR FOR IOCU DRIVEN FUNCTIONS ISTAT(1) = IOSTRT(IOPCKT(11)) # START IOCU AT ADDRESS IN IO REQUEST IF (CSTAT(1) == ISSUC) $( # IF IOCU PROGRAM STARTED OK CALL MARK(TMOEFG,TMOTMG,TMOUNT) # SETUP IOCU PROGRAM TIMEOUT PERIOD CALL STLOR(IOEEFG,IOPEFG,TMOEFG) # WAIT FOR IOCU EVENT FLAG CALL CLREF(TMOEFG,FLGSNS) # READ TIMEOUT EVENT FLAG IF (FLGSNS == ISSET) $( # IF TIMEOUT EVENT FLAG IS SET CSTAT(1) = IETMO # SET FUNCTION TIMEOUT CODE CSTAT(2) = -45 # SET IO FUNCTION ERROR CODE ISTAT(2) = 0 # INDICATE NO DATA TRANSFERRED $) # END TIMEOUT BLOCK ELSE $( # ELSE, TIMEOUT DID NOT OCCUR CALL CANMT(TMOEFG) # CANCEL TIMEOUT MARKTIME CALL CLREF(IOEEFG,FLGSNS) # READ IOCU ERROR EVENT FLAG IF (FLGSNS == ISSET) $( # IF IOCU ERROR EVENT FLAG IS ON CSTAT(1) = IEABO # SET FUNCTION ABORTED CODE CSTAT(2) = -39 # SET IO FUNCTION ERROR CODE ISTAT(2) = 0 # INDICATE NO DATA TRANSFERRED $) # END IOCU PROGRAM ERROR $) # END NO TIMEOUT BLOCK $) # END IOCU STARTED OK BLOCK $) # END STAGER/ARU TRANSFER BLOCK IF (CSTAT(2) == 0) $( # IF NO ERRORS YET CALL STLOR(STEEFG,STGEFG) # WAIT FOR A STAGER EVENT FLAG CALL CLREF(STEEFG,FLGSNS) # READ STAGER ERROR EVENT FLAG IF (FLGSNS == ISSET) $( # IF ERROR FLAG IS SET CSTAT(1) = IEABO # SET FUNCTION ABORTED CODE CSTAT(2) = -40 # SET IO FUNCTION ERROR CODE ISTAT(2) = 0 # INDICATE NO DATA TRANSFERRED $) # END ERROR FLAG SET BLOCK $) # END NO ERRORS YET BLOCK IF (CSTAT(2) .NE. 0) $( # IF AN ERROR HAS OCCURRED IF (LOGFNC(FNCIDX) == 0) # IF IO REQUEST USES IOCU AND STAGER PARS(1) = SGFLAG + IOFLAG # RESET STAGER AND IOCU ELSE # ELSE, REQUEST USES ONLY STAGER PARS(1) = SGFLAG # RESET STAGER ONLY PERFORM RSTSTG $) # END ERROR HAS OCCURRED BLOCK ELSE $( # IO DONE, START CLEANUP CSTAT(1) = ISSUC # SET FUNCTION SUCCESS CODE ISTAT(2) = IOPCKT(12) # SET TRANSFER LENGTH IF (VDIM .NE. 0) $( # IF THERE ARE SOME VARIABLE DIMENSIONS IF (BASCNT(IMV) .NE. 0) $( # FOR THIS MOVE DO I = 1,BASCNT(IMV) $( # THEN UPDATE RECORD NUMBER COUNTERS BASCUR(I,IMV) = BASCUR(I,IMV) + 1 # INCREMENT COUNTER IF (BASCUR(I,IMV) .LT. BASLEN(I,IMV)) BREAK # QUIT LOOP IF NOT OVERFLOWED BASCUR(I,IMV) = 0 # ELSE, RESET COUNTER AND INCREMENT NEXT STAGE $) # END UPDATE RECORD NUMBER BLOCK $) # END THIS MOVE BLOCK $) # END ANY VARIABLE DIMENSIONS BLOCK IF (VDIM == 0) # IF NO VARIABLE DIMENSIONS CSTAT(2) = ISSUC # INDICATE ARRAY TRANSFER COMPLETED ELSE $( # ELSE, CHECK FOR THIS MOVE ALONE IF (BASCNT(IMV) == 0) # IF NONE FOR THIS MOVE CSTAT(2) = ISSUC # INDICATE ARRAY TRANSFER COMPLETED ELSE $( # ELSE, CHECK IF ARRAY TRANSFER COMPLETED IF ((I .GE. BASCNT(IMV)) .AND. (BASCUR(BASCNT(IMV),IMV) == 0)) CSTAT(2) = ISSUC # INDICATE ARRAY TRANSFER COMPLETED $) # END CHECK IF ARRAY DONE BLOCK $) # END CHECK FOR MOVE ALONE BLOCK $) # END CLEANUP BLOCK $) # END STAGER I/F DONE BLOCK $) # END INTERRUPT MASK SETUP DONE BLOCK $) # END STAGER ADDRESS DONE BLOCK $) # END OK TO COMPUTE ADDRESS BLOCK $) # END TRANSFER LENGTH OK BLOCK $) # END START SETUP BLOCK $) # END BUFFER NUMBER OK BLOCK $) # END OUTPUT MOVE NUMBER OK BLOCK $) # END INPUT MOVE NUMBER OK BLOCK $) # END MOVE NUMBER OK BLOCK CALL ASTSOF(0) # DISABLE ASTS AFTER IO FUNCTION VIRTIO = DSTAT # SET FUNCTION VALUE TO IO STATUS RETURN END