!TITLE The Distributed Array Processor (DAP)
! %externalintegerfn DDAP(%integer ACT, ADR)
!
! ACT is an activity number and ADR is the address of a record DAP of
! format DAPF:
!
!      %recordformat DAPF(%integer CONF, INSTAT, STATUS, ADVIOL,
!           COB, COL, NDPC, IT,
!           IC, DOLOG1, DOLOG2, ILOG1,
!           ILOG2, SECONDS, KBYTES, VADDR,
!           BATCH LIMIT, INTER LIMIT, SPOOLR BATCH LIMIT,
!           %string(6)SOLE USER1, SOLE USER2)
!
!
!ACT = 0
!   Called by DSTOP if DAP STATE is not zero to tidy up
!
!ACT = 1
!   DAP store of DAP_KBYTES is claimed. If the call is successful,
!   DAP_VADDR is set to the virtual address by which the claimed DAP
!   store may be referenced.
!
!
!
! Spec for Allocate call to Supervisor.  Service number 31 (decimal).
!
!DACT = 1    Allocate P_P1 blocks of DAP store.
!            Reply:   P_P1 = 0     OK and
!                                  P_P2 = logical DAP no<<16 ! physical DAP SMAC number
!                                  P_P3 = number of first block allocated
!                                  P_P4 = number of blocks allocated.
!                            1     No DAP in configuration
!                            2     insufficient contiguous DAP blocks
!                                  available
!                            3     DAP closing
!                            4     process already has some DAP.
!
!ACT = 2
!   Any DAP store that has been claimed is released.
!
!DACT = 2    De-allocate DAP (i.e. whole of allocated DAP store).
!            P_P1 = logical DAP no<<16 ! physical DAP SMAC number
!            P_P2 = number of first block allocated
!            P_P3 = number of blocks allocated.
!            Reply: P_P1 = 0 OK, 1 fail
!ACT = 3
!   DAP start.
!
!
!
! Spec for Supervisor Start DAP service (DSNO = 31 decimal DACT = 4)
!
!   P_P1 = DATUM   rel to start    DAP program datum (displacement in
!                  of DAP store    words of the first word of the DAP
!                                  program block relative to the base of
!                                  the DAP store).
!
!   P_P2 = LIMIT   rel to DATUM    DAP program limit (displacement in
!                                  words of the last plane of the program
!                                  block relative to DATUM).
!
!   P_P3 = COB     rel to DATUM    Code base (words from DATUM).
!
!   P_P4 = COL     rel to DATUM    Code limit (word address of first word
!                                  of last plane of code area relative to
!                                  DATUM).
!
!   P_P5 = NDPC    rel to COB      Program counter (word address relative
!                                  to COB)
!   P_P6 = IC                      Instruction counter
!
!
! For further information about these DAP registers see DAP/TN/6
! Section 4.3
!
!
! On return from the DAP START request, the record P is set up as
! follows.
!
! The SACT field tells whether DAP execution was aborted by the DDAP STOP
! procedure. Even if DDAP STOP was called, the execution may have already
! terminated due to some program condition (e.g. stop instruction
! executed).  Or the DAP START request may have been queued by the
! supervisor, i.e. execution has not yet started.
!
! Thus the SACT field will be set as follows.
!
!  SACT  =  4      the DAP was not in fact  started. (A STOP request
!                  was received before the preceeding START request was
!                  actually scheduled). The record P contains the same
!                  information as was given in the START request.
!                  P_P1 = 1 DAP not claimed, 2 DAP closing
!
!  SACT  =  3      the DAP WAS running. The record P contains information
!                  necessary (e.g. NDPC) for a re-start, if required, as
!                  below.
!
!
! P_P1 has the following packed-up data:
!
!       bits  0 -  7       bottom byte of INSTAT
!       bits  8 - 23       bits  8 -23 of ADVIOL
!       bits 24 - 31       bits 24 - 31 of STATUS
!
! P_P2 has NDPC
!
! P_P3 has the following packed-up data:
!
!       bits  0 - 16       bits 15 - 31 of DLOG1
!       bits 17 - 31       bits 17 - 31 of DLOG2
!
! P_P4 has IC
!
! P_P5 has the following packed-up data:
!
!       bits  0 - 11       bits 16 - 23 of DOLOG1
!       bits 12 - 31       the failing instruction
!
! P_P6 has the following packed-up data:
!
!       bits  0 - 11       bits 20 - 31 of DOLOG2
!       bits 12 - 31       the instruction which was executed prior to
!                          the failing instruction.
!
!ACT = 4
!   DAP stop.
!
! This activity is intended to be called on the signal stack following an
! Int:A frm the user. (Otherwise it's not easy to see why it should be
! relevant, as not both the user process and the DAP MCU should be
! running simulataneously).
!
! A DAP STOP message is sent to Supervisor (no reply, at least not to the
! calling service). What happens is:
!
!       If the DAP was running for "this user", then a reply is given to 
!       the START DAP request, and the user finds that the HOST FORCED
!       STOP bit is set in STATUS.
!
!       If the DAP was not running for "this user", but the user has a
!       "claim" request queued, then Supervisor gives a special reply to
!       the claim request (SRCE=X'1F0005', P_P1=-1), and we interpret
!       this and pass the reply back to the user.
!
!       If neither of the above things are the case (and this could arise
!       when the user does an Int:A such that the DAP STOP request goes
!       to Supervisor before the claim request was queued, because the
!       DDAP routine has been interrupted after setting the STATE to
!       SUPVR TOLD but before the claim request was PONned), then
!       Supervisor ignores the DAP STOP message.
!
!ACT = 5
!   DAP related data.
!
! The following fields are set in the 'DAP' record:
!     DAP_CONF to Current ClaimQ limit (DIRCOM_CLAIMQ LIMIT)
!     DAP_SECONDS to H_DAPSECS
!     DAP_BATCH LIMIT to DIRCOM_DAP BATCH LIMIT
!     DAP_INTER LIMIT to DIRCOM_DAP INTER LIMIT
!     DAP_SOLE USER to DIRCOM_DAP SOLE USER
!     DAP_SPOOLR BATCH LIMIT either to DAP_BATCH LIMIT or, if DAP
!        not available for (any) batch job, 0
!
!
!ACT = 6
! Give users currently having Claim requests queued (or currently using
! DAP).  ADR points to a %STRING(6) %ARRAY A(1:20), initially set to all
! zeroes by the caller.  Director stuffs in the usernames (procnos
! obtained from Supervisor) - first username is RUNNING in the DAP and
! subsequent ones have Claim requests queued.
!
!! New entries to DDAP (August 1983)
!
!
! ACT = 7     ADR points to a record format:
!
!             (%string(31) TO USER, FROM USER, TO FILE, FROM FILE,
!              %integer TO FSYS, FROM FSYS, DAP PAGE NO, TO FILE PAGE,
!                       FROM FILE PAGE, NO OF PAGES, IDENT)
!
!             Causes transfers of NO OF PAGES of data from
! 
!                   FROM FILE PAGE          to    DAP PAGE NO (if FROM FILE PAGE
!                                                              non-zero)
! 
!             and/or from
! 
!                   DAP PAGE NO             to    TO FILE PAGE (if TO FILE PAGE
!                                                               non-zero)
! 
!             FROM FILE PAGE and TO FILE PAGE describe pages within the files
!             FROMUSER.FROMFILE on FROMFSYS and TOUSER.TOFILE on TO FSYS
!             respectively. If FROM FILE PAGE is minus one then no transfer takes
!             place FROM the file. If TO FILE PAGE is minus one then no transfer
!             takes place TO the file. BOTH may not be minus one.
! 
!             DAP PAGE NO is the start-page-number of the required DAP page
!             within the DAP block.
! 
!             The IDENT field is set to a number identifying the transfer request.
!             Idents are bit settings, so that several may be ORed together to
!             represent several idents.
! 
! 
! ACT = 8     ADR points to the same record format described above. The IDENTS
!             field should be set with IDENTs supplied from the ACT=7 call, to
!             indicate one or more transfers.
! 
!             The EMAS process suspends until the transfers represented by the
!             given IDENTS are complete.
! 
!
! --------------------------------------------------------------------------------
!
! Programming notes.
!
! These entries may be called only from the ROUTINE which is given in the call of
! DDAP. (This routine is called when a X'F7Fxxxxx' DAP stop instruction
! is executed). On return from this routine, DAP execution resumes at the next
! DAP instruction (except for ACT=9). But in all cases, return MUST BE MADE, so
! that DDAP can "collect all replies to oustanding page in/out requests.
! The integerfunction given as the parameter to DDAP should be specified:
!
!    %integerfn UROUTINE(%integer ILOG1, ILOG2, NDPC)
!
! Any non-zero result given by further calls of DDAP from UROUTINE should be
! given as the result from UROUTINE. This will disable further DAP execution.
! Thus giving a non-zero result is a way (THE way) of aborting the DAP run at
! this point. The parameters handed are (obviously) ILOG1, ILOG2, and DAP PC.
!    
! --------------------------------------------------------------------------------
! Estimated timings
! 
! Transfer speed (after seek and rotational delay) is 5 msecs/page.
! Average latency is 7.5 msecs.
! Seek time is 10 to 60 msecs.
! 
! One-eighth of the DAP is 256 Kbytes or 64 pages. These will in practice appear
! as two contiguous blocks of 32 pages with no seek or latency within the 32-page
! transfers. Transfer time for 32 pages will therefore be
! 
!        35 + 7.5 + 32*5 msecs   =    202.5 msecs
! 
! and for 64 pages on the same disc 505 msecs at best. We must double this for
! the swap, giving 1.01 secs. And we must also allow for disc contention and for
! channel (DFC) contention, resulting from other users' presence. At worst this
! might involve a further factor of four for channel contention and four for disc
! contention. At best, perhaps two and two (say for night or weekend usage).
! 
! We might expect a one-eighth DAP swap, therefore, to take from 5 to 18 seconds.
! In an empty machine, we might get down to two seconds. Remember we have to wake
! up and page in the EMAS process itself, as well as getting the actual transfers
! done.
!
! --------------------------------------------------------------------------------
!
!
!DAP related error numbers:
!     62 ClaimQ limit would be exceeded
!     63 DAP not claimed at START
!     64 DAP Claim de-queued (as a result of a DAP STOP request to
!        Supervisor)
!     67 Not claimed at Release
!     71 No time left
!     72 DAP not started
!     73 DAP not available
!     74 Not enough contiguous DAP blocks
!     75 DAP closing
!     76 User already has DAP
!     90 Max already allocated
!     92 Interactive use not allowed
!
!
      
! Commentary on COM_DAP STATE:
!
!            0          No DAP (allocated as store if DAP exists)
!            1          DAP free and idle (ready to be claimed as DAP)
!            2          Claimed by some user.
!            3          DAP running.
!           16          DAP is available as store, but will become DAP if
!                       someone does an Allocate.
!           17          DAP is free and idle (after user de-allocate) and
!                       is going to revert to being store, after a short
!                       timeout.
!>
INCLUDE  "PD22S_C03FORMATS"
!
      recordformat  c 
DAPF(INTEGER  CONF, INSTAT, STATUS, ADVIOL,
      COB, COL, NDPC, IT,
      IC, DOLOG1, DOLOG2, ILOG1,
      ILOG2, SECONDS, KBYTES, VADDR,
      BATCH LIMIT, INTER LIMIT, SPOOLR BATCH LIMIT,
      STRING (6)ARRAY  DAP USER(0 : 2),
      INTEGERARRAY  DAP INTEGER(1 : 8))
CONSTINTEGER  DAPF LEN = 132
      recordformat  c 
DFINFOF(integer  NKB, RUP, EEP, APF,
      USE, ARCH, FSYS, CONSEG,
      CCT, CODES, byteinteger  SP1, DAYNO, POOL, CODES2, integer  SSBYTE,
      string (6)OFFER)
      recordformat  c 
PROCF(STRING (6)USER, BYTEINTEGER  INCAR,CATEGORY,WSN,RUNQ,ACTIVE,
      INTEGER  ACTWO, LSTAD, LAMTX, STACK, STATUS)
!
!
!
!
CONSTRECORD (UINFF) NAME  UINF = 9<<18
CONSTINTEGER  NO = 0
CONSTINTEGER  YES = 1
CONSTINTEGER  INTER = 0
CONSTINTEGER  BATCH = 2
CONSTINTEGER  DAP BLOCK SIZE = X'20000'  {bytes, i.e. half-seg}
CONSTINTEGER  DAP BLOCKS = 16
CONSTINTEGER  LEAVE = 8
!
!
!
      EXTERNALINTEGERFNSPEC  C 
DCONNECTI(STRING (31) FILE, INTEGER  FSYS, MODE, APF,
      INTEGERNAME  SEG, GAP)
      EXTERNALINTEGERFNSPEC  C 
DCREATEF(STRING (31) FILE, INTEGER  FSYS, NKB, ALLOC,
      LEAVE, INTEGERNAME  DA)
      EXTERNALINTEGERFNSPEC  C 
DDESTROYF(STRING (31) FILE, INTEGER  FSYS, DEALLOC)
      EXTERNALINTEGERFNSPEC  C 
DDISCONNECTI(STRING (31) FILE, INTEGER  FSYS, LO)
      externalintegerfnSPEC  C 
DFINFO(string (31)FILE INDEX, FILE, integer  FSYS, ADR)
      EXTERNALINTEGERFNSPEC  C 
DGETDA(STRING (31) USER, FILE, INTEGER  FSYS, ADR)
      EXTERNALROUTINESPEC  C 
DOUT11I(RECORD (PARMF)NAME  P)
      EXTERNALROUTINESPEC  C 
DOUTI(RECORD (PARMF) NAME  P)
      EXTERNALROUTINESPEC  C 
DPONI(RECORD (PARMF) NAME  P)
      EXTERNALROUTINESPEC  C 
DPOFFI(RECORD (PARMF) NAME  P)
      externalroutinespec  c 
DTOFF(record (PARMF)name  P)
      EXTERNALINTEGERFNSPEC  C 
HINDA(STRING (6)USER, INTEGERNAME  FSYS, INDAD, INTEGER  TYPE)
      EXTERNALINTEGERFNSPEC  C 
IN2(INTEGER  FN)
      EXTERNALSTRINGFNSPEC  C 
ITOS(INTEGER  I)
      EXTERNALINTEGERFNSPEC  C 
OUT(INTEGER  FLAG, STRING (63) TEMPLATE)
      EXTERNALROUTINESPEC  C 
PREC(STRING (255)S, RECORD (PARMF)NAME  P, INTEGER  NL)
      EXTERNALINTEGERFNSPEC  C 
SYSAD(INTEGER  KEY, FSYS)
      EXTERNALINTEGERFNSPEC  C 
VAL(INTEGER  ADR, LEN, RW, PSR)
      EXTERNALROUTINESPEC  C 
WRS(STRING (255) S)
      EXTERNALROUTINESPEC  C 
WRSN(STRING (255) S, INTEGER  N)
      EXTERNALROUTINESPEC  C 
WRSNT(STRING (255) S, INTEGER  N, T)
!
!
!      
EXTRINSICINTEGER  DAP STATE; ! defined for DSTOP
EXTRINSICINTEGER  D CALLERS ACR
EXTRINSICINTEGER  D CALLERS PSR
EXTRINSICINTEGER  DIRMON
EXTRINSICINTEGER  D TRYING
EXTRINSICINTEGER  OUTPAD
EXTRINSICSTRING (6) PROCUSER
!
!
OWNINTEGER  DAP SEG
OWNINTEGER  DAP GAP
OWNINTEGER  KBYTES
OWNINTEGER  DAP BLOCKS ALLOC, DAP BLOCK NO, DAP IDENT, LOGICAL DAP NO
OWNINTEGER  EXTRA BLOCKS  {this can be used to leave unused the low-}
      {numbered blocks, to avoid the areas giving}
      {hardware trouble}
OWNSTRING (3) SUFF
OWNINTEGER  DAP ACR
OWNINTEGER  DAP PSR

! DATUM and LIMIT are word quantities and must be plane-aligned.
! A plane, in the 64 X 64 DAP is 128 words (64 rows, each row being 64 bits).

CONSTINTEGER  PLANE SIZE = 128  {words}
CONSTINTEGER  DAP INS PER SEC = 3300000; ! increased by 10% in Director 19
                                         ! following receipt of figures from
                                         ! Mike Brown.
CONSTLONGINTEGER  COME BACK AFTER = 120 * DAP INSPERSEC; ! max IC value set per DAPSTART

CONSTINTEGER  HOST FORCED STOP  = B'10000000'     {bit 24}  {These come as  }
CONSTINTEGER  DAP STOP          = B'1000000'      {bit 25}  { bits 0-7 of   }
CONSTINTEGER  DAP EXSTP         = B'100000'       {bit 26}  { P_P1 in reply }
CONSTINTEGER  DAP INTR          = B'10000'        {bit 27}  { from DAPSTART }
CONSTINTEGER  DAP HWARE ERR     = B'1000'         {bit 28}
CONSTINTEGER  DAP PROG ERR      = B'100'          {bit 29}
CONSTINTEGER  DAP IT EXPIRED    = B'10'           {bit 30}
CONSTINTEGER  DAP IC EXPIRED    = B'1'            {bit 31}

CONSTINTEGER  SWAP OUT BIT      = B'10000000'; ! {bit 24  (ICL unused) in STATUS
      
CONSTINTEGER  REPORT DAP START REQUEST    = B'1'
CONSTINTEGER  REPORT DAP RESPONSE         = B'10'
CONSTINTEGER  REPORT DAP SEGNUMBER        = B'100'
      
CONSTINTEGER  CLAIMED                     = B'1'
CONSTINTEGER  SUPVR TOLD                  = B'10'

CONSTINTEGER  DAP TO FILE = 2, FILE TO DAP = 1

CONSTINTEGER  SECT SIZE = 32 {i.e. pages per sect}

CONSTINTEGER  MAX IN PROGRESS = 64 {max no of page transfer requests allowed to be in progress at any time}



INTEGERFN  DDAPI(INTEGERFN  UROUTINE(INTEGER  ILOG1, ILOG2, NDPC),
      INTEGER  ACT, ADR)
!
!
!

INTEGER  FLAG, J, K, DA, XEXIT, DAP VREALADDR, DATUM, LIMIT, SEG
INTEGER  COB, COL, NDPC, IC, INSTAT, KK, UROUT FLAG, DAPTIMEOUTFLAG
INTEGER  MINSTRS, DSECS, FSYS, INDAD, THIS
LONGINTEGER  ILEFT, IUSED
STRING (6)USER
RECORD (HF) NAME  H
RECORD (PARMF) P, DAPSTARTP
RECORD (DAPF) NAME  DAP
RECORD (DIRCOMF) NAME  DIRCOM
RECORD (PROCF)ARRAYFORMAT  PROCAF(0:COM_MAXPROCS)
RECORD (PROCF)ARRAYNAME  PROCLIST

INTEGERARRAY  SECTS( - 4:16 {allows 2 Mbytes: 16 sects @ 128K})

CONSTSTRING (4) DAPFILE = "#DAP"
OWNINTEGER  LAST PRINT
CONSTSTRING (4)ARRAY  TAG1(0:2)="B0 ", ",B1 ", ",I "
CONSTSTRING (4)ARRAY  TAG2(1:8)=",CQ",",I",",L0",",H0",",L1",",H1",",SB0",",SB1"
!
CONSTINTEGER  TOPACT = 8
SWITCH  SW(1:TOPACT)
SWITCH  SW5(0 : 2)  {used in activity 5}
!
!
!
ROUTINE  DSEGMENT(INTEGER  SEG)
! REQUESTS THE LOCAL CONTROLLER TO REMOVE ACTIVE MEMORY
! TABLE ENTRIES FOR SEGMENT SEG
RECORD (PARMF)NAME  P
INTEGER  J

      ! Reference a page in each block, just to ensure that zero pages are
      ! provided for each block which has not been referenced. In fact DAPRUN
      ! has definitely referenced each page, because it zeroes the whole program
      ! block, ie. the whole file, first. However, we'll do this to be rigorous
      ! and enforce integrity. The top bit in the SEG parameter is SET if the segment
      ! has two blocks, and is ZERO if the segment has one block.

      J=INTEGER(SEG<<18)
      J=INTEGER(SEG<<18 + X'20000') IF  SEG<0
      SEG=SEG<<1>>1

      P==RECORD(OUTPAD)
      P=0
      P_P1=SEG
      *OUT_3; ! We would set P_P2=1 for "DESTROYING SEGMENT". But we are not destroying it.
      IF  P_DEST=-1 THEN  WRS("DSEGMENT fails in DDAP")
END ; ! DSEGMENT


INTEGERFN  PAGE TRANSFERS(INTEGER  DIRECTION)
! In this routine we move data from the #DAP file into the real DAP and vv,
! allowing a maximum of MAX IN PROGRESS transfers to be outstanding at atime
! (so as not to swamp the transferqueues unnecessarily).


! Result    0    no failures
!           1    a transfer failed


INTEGER  PAGE, DATUM PAGE, LIMIT PAGE, CODE BASE PAGE, CODE LIMIT PAGE
INTEGER  FAIL, TFERS OUTSTANDING, DISC SECT, SECT PAGE, TFERS

RECORD (PARMF) P

RECORD (PARMF) NAME  OUTP

      TFERS = 0 {remove this variable "sometime" - was just for some monitoring}
      TFERS OUTSTANDING = 0
      FAIL = 0
      DISC SECT = 0
      SECT PAGE = 0
      
! DATUM PAGE is the lowest relative pageno of DAP which is to be swapped
      DATUM PAGE = DATUM {words} >> 10
      
! LIMIT PAGE is the highest relative pageno of DAP which is to be swapped
      LIMIT PAGE = DATUM PAGE + KBYTES >> 2 - 1
      
! CODE BASE PAGE is the lowest page containing code WHICH DOES NOT NEED TO
! BE SWAPPED OUT.
      CODE BASE PAGE = DATUM PAGE + (COB + 1023) >> 10
      
! CODE LIMIT PAGE is the highest relative pageno of DAP which does not need to be SWAPPED OUT.
      CODE LIMIT PAGE = DATUM PAGE + ((COL + PLANE SIZE) >> 10) - 1

      ! This bunch of monitoring can be removed, say at next Director relase.
{     WRSNT("CODE BASE PAGE", CODE BASE PAGE, 4+2);WRSNT("' CODE LIMIT PAGE", 
{     CODE LIMIT PAGE, 2)
{     WRSNT("' LIMIT",LIMIT,4+2);WRSNT("' LIMIT PAGE",
{     LIMIT PAGE,4+2);WRSNT("' KBYTES",KBYTES,2)

      PAGE = DATUM PAGE
      
      IF  DIRECTION = FILE TO DAP START 
 ! Make sure that pages of the first section are back to disc (redundant
 ! code on calls of PAGE TRANSFERS after the first following the
 ! ddisconnect, but never mind). Subsequent sections (blocks) are checked
 ! below, when DISC SECT is incremented.
         
         ! WRSNT("OUT_17A ON", SECTS(0), 2)
         OUTP == RECORD(OUTPAD)
         OUTP = 0
         OUTP_DEST = SECTS(0)
         *OUT_17; ! Returns p_dest=-1 if block still active, 0 if not still active
         IF  OUTP_DEST =  - 1 START 
            WRS("DAP PAGE TRANSFERS: block still active!!")
            FAIL = 1
            -> OUT
         FINISH 
      FINISH 
      CYCLE 
         WHILE  FAIL = 0 AND  PAGE <= LIMIT PAGE AND  C 
            TFERS OUTSTANDING < MAX IN PROGRESS CYCLE 
            IF  DIRECTION = FILE TO DAP OR (DIRECTION = DAP TO FILE AND  C 
               NOT (CODE BASE PAGE <= PAGE <= CODE LIMIT PAGE)) START 
               P = 0
               P_DEST = 33 {SNO for PDISC} << 16 ! DIRECTION
               {1=read disc to store 2=write store to disc}
               P_P3 = DAP VREALADDR + PAGE << 12
               P_P2 = SECTS(DISC SECT) + SECT PAGE
               IF  DIRECTION = DAP TO FILE THEN  P_P6 = M'DTOF' ELSE  P_P6 = M'FTOD' {to see in Dirmonning}
               DPONI(P)

               TFERS OUTSTANDING = TFERS OUTSTANDING + 1
               TFERS = TFERS + 1
            FINISH 
            
            PAGE = PAGE + 1
            SECT PAGE = SECT PAGE + 1
            IF  SECT PAGE >= SECT SIZE AND  C 
               DISC SECT < SECTS( - 3) {no of sections} - 1 START 
               DISC SECT = DISC SECT + 1
               SECT PAGE = 0
               
               IF  DIRECTION = FILE TO DAP START 
 ! See comment before outer cycle.
                  
                  ! WRSNT("OUT_17B ON", SECTS(DISC SECT), 2)
                  OUTP == RECORD(OUTPAD)
                  OUTP = 0
                  OUTP_DEST = SECTS(DISC SECT)
                  *OUT_17; ! Returns p_dest=-1 if block still active, 0 if not still active
                  IF  OUTP_DEST =  - 1 START 
                     WRS("DAP PAGE TRANSFERS: block still active!!")
                     FAIL = 1 {fail}
                  FINISH 
               FINISH 
            FINISH 
         REPEAT 
         DPOFFI(P)
         IF  P_P2 # 0 THEN  FAIL = 1
         TFERS OUTSTANDING = TFERS OUTSTANDING - 1
      REPEATUNTIL  TFERS OUTSTANDING = 0

      { WRSNT("Page transfers =", TFERS, 4+2); WRSNT(" FAIL =", FAIL, 2) }
OUT:
!      %IF DIRECTION = FILE TO DAP %C
!      %THEN PRINTSTRING("FILE TO DAP") %C
!      %ELSE PRINTSTRING("DAP TO FILE")
!      WRSN(" flag", FAIL)
!
      RESULT  = FAIL
END ; ! PAGE TRANSFERS
!-------------------------------------------------------------------------------

OWN  INTEGER  TRANSFER REC ADDR = 0;  ! This variable is zero except when the
                                      ! DAP-start (entry 3) has been made.

CONSTINTEGER  MAX DDE SECTS = 15;     ! Max 2 Mbytes/transfer, less one section
                                      ! (We need to record data for one extra
                                      ! section, in case transfer-start disc
                                      ! address is not aligned with start of
                                      ! section).

CONSTINTEGER  TOP SLOT = 1;           ! Allowing two simultaneous transfers

RECORDFORMAT  TFRF(INTEGERARRAY  TO SECTS, FROM SECTS, SECT CHKD(0:MAX DDE SECTS),
   BYTEINTEGERARRAY  STATE(0:(MAX DDE SECTS*SECT SIZE)-1), INTEGER  REQ COUNT, TERM COUNT, DAP PAGENO,
   FROM PAGENO, TO PAGENO, NPAGES, SEG1, SEG2)

RECORDFORMAT  TRF(INTEGER  TFRS WAITING, TFRS SUSPENDED, HI SLOT, SLOTS IN USE, SLOTS WAITING TFR,TFRS IN PROGRESS,
   DAP VREALADDR,
   RECORD (TFRF)ARRAY  TFERS(0:TOP SLOT))

RECORD (TRF) TRANSFER REC
! We wish to be able to swap 2 Meg = 8 segments = 16 blocks or sections
! (But less one section, see comment near MAX DE SECTS above).


CONSTINTEGER  AWAIT DAP STOP=1, FINISH TRANSFERS=2

CONSTINTEGER  TO PAGE OUT THEN PAGE IN = 5
CONSTINTEGER  AWAITING TERM THEN PAGE IN = 4
CONSTINTEGER  TO PAGE OUT ONLY = 3
CONSTINTEGER  TO PAGE IN ONLY = 2
CONSTINTEGER  AWAITING TERM = 1
CONSTINTEGER  DONE = 0

!   AWAITING TERM goes to                     DONE
!   TO PAGE IN ONLY goes to                   AWAITING TERM
!   TO PAGE OUT ONLY goes to                  AWAITING TERM
!   AWAITING TERM THEN PAGE IN goes to        AWAITING TERM
!   TO PAGE OUT THEN PAGE IN goes to          AWAITING PAGE IN

RECORDFORMAT  FORMAT7(STRING (31) TO USER, FROM USER, TO FILE, FROM FILE,
   INTEGER  TO FSYS, FROM FSYS, DAP PAGENO, TO FILE PAGENO, FROM FILE PAGENO,
            NO OF PAGES, IDENT)
CONST  INTEGER  DAPF LEN7 = 156


RECORD (FORMAT7) NAME  REQ8


! The general scheme of things for our block transfer system (BTS) is as follows.  First, nothing is
! different (from previous arrangements, pre-BTS) unless and until a special DAP STOP instruction is executed
! by the DAP MCU.  The special stop is one with op-code F7 and stop parameter Fxxxxx (agreed between Mike
! Brown and Keith Yarwood).  When such a stop instruction is executed (and even if other exception conditions
! are present), the user-supplied procedure UROUTINE is executed.  This procedure is expected to supply
! details of swap-in/out operations on part of the DAP program block by calling DDAP entry 7 (recursive
! call).  This entry records details of required page transfers and returns zero result if OK.  Provided that
! UROUTINE also returns a zero result, DAP execution then continues at the current DAP PC (viz. after the
! special stop instruction).  If UROUTINE gives a non-zero result then the DAP is not re-started and control
! eventually returns again to user, but in fact AFTER completing all successfully- requested transfers.
! Likewise if any other exception condition (e.g. "time exceeded") is present simultaneously with the the
! special DAP stop, then it is treated after the return from UROUTINE.  If this exception would cause return
! to user in the "pre-BTS" situation, then it will still do so, again after all current BTS requests are
! complete.
!
! The other thing that UROUTINE can do, in addition to requesting a series of BTS transfers, is to request
! DAP suspension (i.e. continued suspension - the DAP is already stopped) UNTIL one or more previous BTS
! transfer requests are complete.  This it does by calling DDAP entry 8, setting one or more bits in the
! IDENT field of the supplied record with the identifier bits supplied from previous calls of DDAP entry 7.
! Return is made from DDAP entry 8 only when all the specified transfers are complete.
!
! In DDAP, the guiding principle is that all requested BTS transfers are complete before return is made from
! the start-DAP entry - they must be, otherwise the termination POFF messages cannot be collected.  This is
! the purpose of the ACTION=AWAIT entry to the CHECK BTS TRANSFERS function.


INTEGERFN  INITIALISE REQUEST RECORDS
INTEGER  FLAG, I, TO FILE PAGES, FROM FILE PAGES, NPAGES, ID, RET ID, SLOT,
         DAP PAGENO, TO FILE PAGENO, FROM FILE PAGENO, TO START SECT,
         FROM START SECT, INIT STATE, OPINC, SEG1, SEG2, NSEGS, LASTSEG, KK
! %RECORD(TFRF)%ARRAYFORMAT TFERSF(0:TOP SLOT)
RECORD (TFRF)ARRAYNAME  TFERS
RECORD (TRF)NAME  TRANSFER REC
RECORD (TFRF)NAME  T
RECORD (FORMAT7)NAME  REQ
RECORD (DFINFOF) FINFOREC
INTEGERARRAY  TO SECTS, FROM SECTS(-4 : 255)

! This procedure is called during a RECURSIVE call of DDAP. We must be sure to
! map onto the TRANSFER REQUESTS array in the ORIGINAL INVOCATION of DDAP.

! REMOVE THIS CODE WHEN IN DIRECTOR. IT'S TO AVOID UNASSIGNED CHECK DURING TESTING********
{FOR I=255, -1, 0 %CYCLE; TO SECTS(I)=0; FROM SECTS(I)=0; %REPEAT; FROM START SECT=0; TO START SECT=0

      REQ == RECORD(ADR)
      DAP PAGENO = REQ_DAP PAGENO
      TO FILE PAGENO = REQ_TO FILE PAGENO
      FROM FILE PAGENO = REQ_FROM FILE PAGENO
      NPAGES = REQ_NO OF PAGES
      OPINC = 1 {This is the number of operations per request page. It becomes 2 for a "swap"}

      SEG1 = 0
      SEG2 = 0
      INIT STATE = DONE
      IF  TO FILE PAGENO>=0 START 
         UNLESS  DIRMON = 0 START 
            WRSNT("DAP OUT REQUEST ON FSYS", REQ_TO FSYS, 4)
            WRSNT(" DAP page", DAP PAGENO, 4)
            WRSNT(" PAGES:", NPAGES, 4)
            WRSNT(" to PAGENO", TO FILE PAGENO, 0)
         FINISH 

         FLAG = DGETDA(REQ_TO USER, REQ_TO FILE, REQ_TO FSYS, ADDR(TO SECTS(-4)))
         RESULT  = FLAG UNLESS  FLAG = 0
         TO FILE PAGES = (TO SECTS(-3)-1)*SECT SIZE + TO SECTS(-2)
         TO START SECT = TO FILE PAGENO//SECT SIZE
         INIT STATE = TO PAGE OUT ONLY
      FINISH 

      IF  FROM FILE PAGENO>=0 START 
         UNLESS  DIRMON = 0 START 
            WRSNT("DAP IN REQUEST ON FSYS", REQ_FROM FSYS, 4)
            WRSNT(" DAP page", DAP PAGENO, 4)
            WRSNT(" PAGES:", NPAGES, 4)
            WRSNT(" from PAGENO", FROM FILE PAGENO, 0)
         FINISH 

         FLAG = DGETDA(REQ_FROM USER, REQ_FROM FILE, REQ_FROM FSYS, ADDR(FROM SECTS(-4)))
         UNLESS  FLAG = 0 THEN  RESULT  = FLAG
         FROM FILE PAGES = (FROM SECTS(-3)-1)*SECT SIZE + FROM SECTS(-2)
         FROM START SECT = FROM FILE PAGENO//SECT SIZE

         FLAG = DFINFO(REQ_FROM USER, REQ_FROM FILE, REQ_FROM FSYS, ADDR(FINFOREC))
         UNLESS  FLAG = 0 THEN  RESULT  = FLAG

         IF  FINFOREC_CONSEG # 0 START 
            ! Work out start and end-segment numbers. This is so that we have
            ! the necessary information to ensure that pages are on disc before
            ! we bulk move them.
            FLAG = 1 - (FROM SECTS(-3){no of sects} & 1)
            ! namely, zero if last seg of file has one block, and one if it has two
            NSEGS = (FROM SECTS(-3) + 1) >> 1
            LASTSEG = FINFOREC_CONSEG + NSEGS - 1
            SEG1 = FINFOREC_CONSEG + (FROM FILE PAGENO >> 6)
            IF  SEG1 < LASTSEG THEN  KK = 1 ELSE  KK = FLAG
            SEG1 = SEG1 ! (KK << 31)
            SEG2 = FINFOREC_CONSEG + ((FROM FILE PAGENO + NPAGES - 1) >> 6)
            IF  SEG2 < LASTSEG THEN  KK = 1 ELSE  KK = FLAG
            SEG2 = SEG2 ! (KK << 31)
         FINISH 

         IF  INIT STATE = TO PAGE OUT ONLY START 
            ! A swap
            INIT STATE = TO PAGE OUT THEN PAGE IN
            OPINC = 2
         FINISH  ELSE  INIT STATE = TO PAGE IN ONLY
      FINISH 

      UNLESS  0 < DAP PAGENO + NPAGES <= KBYTES>>2 AND  (TO FILE PAGENO = -1 OR  C 
         (TO FILE PAGENO >= 0 AND  TO FILE PAGENO < TO FILE PAGES C 
         AND  TO FILE PAGENO + NPAGES <= TO FILE PAGES)) AND  C 
         (FROM FILE PAGENO = -1 OR  C 
         (FROM FILE PAGENO >= 0 AND  FROM FILE PAGENO < FROM FILE PAGES C 
         AND  FROM FILE PAGENO + NPAGES <= FROM FILE PAGES)) AND  C 
         NPAGES > 0 AND  TRANSFER REC ADDR > 0 AND  NPAGES <= MAX DDE SECTS C 
         * SECT SIZE THEN  RESULT  = 8

      ID = 1 {slot zero}
      RET ID = 0 {ident to be returned}
      TRANSFER REC == RECORD(TRANSFER REC ADDR)
      TFERS == TRANSFER REC_TFERS {see note at declaration}

      ! Look for a free transfer request slot (indicated by zero count fields)
      SLOT = 0
      UNTIL  SLOT = 0 CYCLE 
         T == TFERS(SLOT)
         IF  TFERS(SLOT)_REQ COUNT = 0 = TFERS(SLOT)_TERM COUNT START 
            ! Slot free
            TFERS(SLOT) = 0
            RET ID = ID

            ! Fill up the request entry
            ! First the (up to) MAX DDE SECTS+1 disc addresses required

            FOR  I = 0, 1, MAX DDE SECTS CYCLE 
               TFERS(SLOT)_TO SECTS(I) = TO SECTS(TO START SECT + I) IF  TO FILE PAGENO >= 0
               TFERS(SLOT)_FROM SECTS(I) = FROM SECTS(FROM START SECT + I) IF  FROM FILE PAGENO >= 0
            REPEAT 

            TFERS(SLOT)_FROM PAGENO = FROM FILE PAGENO & (SECT SIZE-1)
            TFERS(SLOT)_TO PAGENO = TO FILE PAGENO & (SECT SIZE-1)

            ! And set the necessary number of STATE bytes to get the required
            ! transfers effected

            FOR  I = 0, 1, NPAGES-1 CYCLE 
               TFERS(SLOT)_STATE(I) = INIT STATE
               TFERS(SLOT)_REQ COUNT = TFERS(SLOT)_REQ COUNT + OPINC
               TFERS(SLOT)_TERM COUNT = TFERS(SLOT)_REQ COUNT
               TRANSFER REC_TFRS WAITING = TRANSFER REC_TFRS WAITING + 1
               TRANSFER REC_TFRS SUSPENDED = TRANSFER REC_TFRS SUSPENDED + 1 IF  OPINC = 2
               ! In the case of TO PAGE OUT THEN PAGE IN, the second transfer is
               ! suspended until the first terminates. We need to distinguish in
               ! the loop which fires off the transfers.
            REPEAT 
            TFERS(SLOT)_DAP PAGENO = DAP PAGENO
            TFERS(SLOT)_NPAGES = NPAGES
            TFERS(SLOT)_SEG1 = SEG1
            TFERS(SLOT)_SEG2 = SEG2
            TRANSFER REC_SLOTS IN USE = TRANSFER REC_SLOTS IN USE ! RET ID
            TRANSFER REC_SLOTS WAITING TFR = TRANSFER REC_SLOTS WAITING TFR ! RET ID
            IF  SLOT > TRANSFER REC_HI SLOT THEN  TRANSFER REC_HI SLOT = SLOT
            EXIT 
         FINISH 
         SLOT = SLOT + 1
         ID = ID << 1
         IF  SLOT > TOP SLOT THEN  SLOT = 0 AND  ID = 1
      REPEAT 
      IF  RET ID = 0 THEN  RESULT  = 99 {no free slot/max tfrs outstanding}
      REQ_IDENT = RET ID
      RESULT  = 0
END  {INITIALISE REQUEST RECORDS}

INTEGERFN  CHECK BTS TRANSFERS(INTEGER  ACTION, IDENTS)
! Does two things:
!     ACTION = FINISH TRANSFERS accepts POFF messages and returns only when all
!                               BTS transfers represented by bits in IDENTS are
!                               complete. This entry is made (only) when the DAP
!                               is stopped, and the purpose is to ensure that
!                               specified (or all) requested BTS transfers are complete
!                               (i.e. initiated and terminated).
!
!                               Calls with this ACTION may be during recursive
!                               execution of DDAP [as when UROUTINE calls DDAP(8, )]
!                               or during non-recursive calls [as called from DDAP(3, )]
!
!     ACTION = AWAIT DAP STOP   accepts POFF messages and initiates more BTS
!                               transfers if any are waiting to be fired off.
!                               This entry is made (only) when the DAP is
!                               running (namely after the DAP has been started).
!                               The purpose of this entry is to field page-transfer
!                               terminations, and to initiate further page-transfers
!                               if any are outstanding. Return is made from this
!                               call only when the DAP stops. At this point, page
!                               transfers may still be in progress.
!
!                               Calls with this ACTION occur only during non-recursive
!                               execution of DDAP.
!
!
! POFF messages received can be BTS transfer completions OR a DAP STOPS message.
! If the latter, then record DAPSTART is set with the DAP STOPS POFF-data.

! Result from this function determines whether the DAP run continues
! subsequently, or aborts. If result is ZERO, all has gone OK. If result
! is NON-ZERO then an error has occurred, and the DAP run is to be terminated.

INTEGER  I, SLOT, PG, DISC ADDRESS, DIRECTION, TO SINDEX, FROM SINDEX,
         TO PINDEX, FROM PINDEX, STATE, FAIL FLAG, SEG,SEG1, SEG2
RECORD (PARMF) P
RECORD (TFRF) NAME  T
RECORD (PARMF) NAME  OUTP
! %RECORD(TFRF)%ARRAYFORMAT TFERSF(0:TOP SLOT)
RECORD (TFRF)ARRAYNAME  TFERS
RECORD (TRF)NAME  TRANSFER REC

! Constants used in positioning the page & slot in the Page Transfer Request
CONSTINTEGER  SLOTSHIFT = 10, PAGEMASK = X'3FF'

   RESULT  = 8 IF  TRANSFER REC ADDR = 0
   FAIL FLAG = 0

   TRANSFER REC == RECORD(TRANSFER REC ADDR)
   TFERS == TRANSFER REC_TFERS {see note at declaration}
   I = 1
   SLOT = 0

   UNLESS  DIRMON = 0 START 
      PRINTSTRING("CHK BTS, ")
      IF  ACTION = FINISH TRANSFERS C 
      THEN  PRINTSTRING("Finish TFRS ".ITOS(IDENTS)) C 
      ELSE  PRINTSTRING("Await STOP")
      WRSN(" SLOTS IN USE: ", TRANSFER REC_SLOTS IN USE)
   FINISH 

   CYCLE 
      IF  ACTION = FINISH TRANSFERS AND  TRANSFER REC_SLOTS IN USE & IDENTS = 0 THEN  RESULT  = FAIL FLAG

      ! Take, or wait for, a POFF message
      IF  TRANSFER REC_TFRS WAITING > 0 AND  TRANSFER REC_TFRS IN PROGRESS < MAX IN PROGRESS C 
         THEN  DTOFF(P) ELSE  DPOFFI(P)
      IF  P_DEST # 0 START 
         IF  P_SRCE>>16 = X'1F' {DAP driver} START 
            IF  ACTION # AWAIT DAP STOP THEN  WRS("Ouch A")
            DAPSTARTP = P {we are in non-recursive exec of DDAP, so this is the right DAPSTARTP}
            RESULT  = 0
         FINISH  ELSE  IF  P_SRCE>>16 = 33 {PDISC} START 
            IF  P_P2 # 0 THEN  FAIL FLAG=1 {Abort - failed transfer}

            ! Termination - update relevant slot.
            ! P_P1 has SLOT<<8 ! PAGE
            BEGIN 
            INTEGER  SLOT
            RECORD (TFRF) NAME  T
            BYTEINTEGERNAME  STATE
               SLOT = P_P1 >> SLOTSHIFT
               T == TRANSFER REC_TFERS(SLOT)
               STATE == T_STATE(P_P1&PAGEMASK)
               IF  STATE = TO PAGE OUT THEN PAGE IN OR  STATE = TO PAGE OUT ONLY  C 
               OR  STATE = TO PAGE IN ONLY OR  STATE = DONE THEN  WRS("Ouch C") ELSE  START 
                  IF  STATE = AWAITING TERM THEN PAGE IN START 
                     STATE = TO PAGE IN ONLY
                     TRANSFER REC_TFRS SUSPENDED = TRANSFER REC_TFRS SUSPENDED - 1
                     TRANSFER REC_TFRS WAITING = TRANSFER REC_TFRS WAITING + 1
                  FINISH  ELSE  IF  STATE = AWAITING TERM THEN  STATE = DONE
                  T_TERM COUNT = T_TERM COUNT - 1
                  IF  T_TERM COUNT = 0 THEN  TRANSFER REC_SLOTS IN USE = TRANSFER REC_SLOTS IN USE & (¬(1<<SLOT))
                  TRANSFER REC_TFRS IN PROGRESS = TRANSFER REC_TFRS IN PROGRESS - 1
               FINISH 
            END  {begin-block}
         FINISH  ELSE  PREC("Ouch B ", P, 0)
      FINISH 
      IF  TRANSFER REC_TFRS IN PROGRESS < MAX IN PROGRESS AND  TRANSFER REC_SLOTS WAITING TFR & I # 0 START 
         T == TRANSFER REC_TFERS(SLOT)
         TO SINDEX = 0; FROM SINDEX = 0
         TO PINDEX = T_TO PAGENO; FROM PINDEX = T_FROM PAGENO
         PG = 0
         WHILE  PG < T_NPAGES CYCLE 
            STATE = T_STATE(PG)
            DIRECTION = 0 {shows whether or not a transfer is to be performed for this page}
            IF  STATE = TO PAGE OUT THEN PAGE IN START 
               DISC ADDRESS = T_TO SECTS(TO SINDEX) + TO PINDEX
               DIRECTION = DAP TO FILE
               STATE = AWAITING TERM THEN PAGE IN
            FINISH  ELSE  IF  STATE = TO PAGE IN ONLY START 
               DISC ADDRESS = T_FROM SECTS(FROM SINDEX) + FROM PINDEX
               DIRECTION = FILE TO DAP
               STATE = AWAITING TERM
            FINISH  ELSE  IF  STATE = TO PAGE OUT ONLY START 
               DISC ADDRESS = T_TO SECTS(TO SINDEX) + TO PINDEX
               DIRECTION = DAP TO FILE
               STATE = AWAITING TERM
            FINISH 

            IF  DIRECTION # 0 START 
               IF  DIRECTION = FILE TO DAP START 
                  SEG1 = T_SEG1<<1>>1; SEG2 = T_SEG2<<1>>1
if  seg2 > 0 start 
                  FOR  SEG = SEG1, 1, SEG2 CYCLE 
                     IF  SEG = SEG2 THEN  DSEGMENT(T_SEG2) ELSE  DSEGMENT(SEG)
                  REPEAT 
finish 
                  T_SEG2 = 0; ! So that the cycle is executed once per transfer
                              ! per call of this function only.
                  IF  T_SECT CHKD(FROM SINDEX) = 0 START 
                     T_SECT CHKD(FROM SINDEX) = 1
!                    WRSNT("OUT_17C ON SECT ", T_FROM SECTS(FROM SINDEX), 2)
                     OUTP == RECORD(OUTPAD)
                     OUTP = 0
                     OUTP_DEST = T_FROM SECTS(FROM SINDEX)
                     *OUT_17
                     IF  OUTP_DEST = -1 START 
                        WRS("DAP BTS: block still active")
                        RESULT  = 1 {abort}
                     FINISH 
                  FINISH 
               FINISH 
               P_DEST = 33<<16 ! DIRECTION {pdisc}
               P_P1 = SLOT<<SLOTSHIFT ! PG;        ! IDENT, returned in P_P1 of reply
               P_P2 = DISC ADDRESS
               P_P3 = TRANSFER REC_DAP VREALADDR + (T_DAP PAGENO + PG)<<12
               IF  DIRECTION = DAP TO FILE THEN  P_P6 = M'BTOT' ELSE  P_P6 = M'BTIN' {to see in Dirmonning}
               DPONI(P)
               T_REQ COUNT = T_REQ COUNT - 1
               IF  T_REQ COUNT = 0 THEN  TRANSFER REC_SLOTS WAITING TFR = TRANSFER REC_SLOTS WAITING TFR & (¬I)
               T_STATE(PG) = STATE
               TRANSFER REC_TFRS WAITING = TRANSFER REC_TFRS WAITING - 1
               TRANSFER REC_TFRS IN PROGRESS = TRANSFER REC_TFRS IN PROGRESS + 1
               EXIT  IF  TRANSFER REC_TFRS IN PROGRESS >= MAX IN PROGRESS
            FINISH 
            PG = PG + 1
            FROM PINDEX = FROM PINDEX + 1
            IF  FROM PINDEX >= SECT SIZE START 
               FROM SINDEX = FROM SINDEX + 1
               FROM PINDEX = 0
            FINISH 
            TO PINDEX = TO PINDEX + 1
            IF  TO PINDEX >= SECT SIZE START 
               TO SINDEX = TO SINDEX+ 1
               TO PINDEX = 0
            FINISH 
         REPEAT 
      FINISH  {slot in use}
      SLOT = SLOT + 1
      I = I <<1
      IF  SLOT > TRANSFER REC_HI SLOT THEN  SLOT = 0 AND  I = 1
   REPEAT 
END  {CHECK BTS TRANSFERS}
!
!
!
!
!
!
      PRINTSTRING("DAP ") UNLESS  ACT > 4

!      FLAG = IN2(256 + 96)
!      ->OUT %UNLESS FLAG = 0
      
      DIRCOM == RECORD(SYSAD(DIRCOMKEY,  - 1))
      PROCLIST == ARRAY(COM_PROCAAD, PROCAF)
      
      ->SW0 IF  ACT = 0
      
      FLAG = 8
      ->OUT UNLESS  1 <= ACT <= TOPACT
      
      FLAG = 45
      IF  ACT < 7 THEN  J = DAPF LEN ELSE  J = DAPF LEN7
      ->OUT IF  VAL(ADR, J, 1, DAP PSR) = 0
      
!      DAP ACR = D CALLERS ACR; ! gets set to '2' subsequently
      DAP == RECORD(ADR)
      ->SW(ACT)
!
!--------------------------------------------------
!
SW0:      ! tidy up at DSTOP
! invoked by statement in DSTOP:
! flag = DDAP(0, 0) unless DAPSTATE = 0
      WRSNT("Act 0, State =", DAPSTATE, 4)
      UNLESS  DAP STATE & SUPVR TOLD = 0 START 
         P = 0
         P_DEST = (31 << 16) ! (LOGICAL DAP NO ! 5); ! STOP
         DPONI(P)
!
         P = 0
         P_DEST = (31 << 16) ! (LOGICAL DAP NO ! 2); ! DE-ALLOC
         P_P1 = DAP IDENT
         P_P2 = DAP BLOCK NO
         P_P3 = DAP BLOCKS ALLOC
         DOUTI(P)
      FINISH 
!
      DAP STATE = 0
      ->OK
!
!----------------------------------------
!
SW(1): ! CLAIM
      PRINTSTRING("Claim ")
      FLAG = 73; ! DAP not available
      ->OUT IF  COM_CDR(1)_DAP STATE = 0 = COM_CDR(2)_DAP STATE
                 
!
      FLAG = 76
      -> OUT UNLESS  DAP STATE & CLAIMED = 0
!      
      FLAG = 90; ! max (one DAP) already claimed
      ->OUT IF  UINF_REASON = BATCH AND  UINF_DAPINSTRS <= 0
 ! no DAP time requested
!
      IF  UINF_REASON = INTER START 
         FLAG = 62 {Max requests on DAP ClaimQ}
         P = 0
         P_DEST = (31 << 16) ! 10
         DOUT11I(P)
         P_P1 = 0 UNLESS  0 <= P_P1 <= 20
         -> OUT IF  P_P1 >= DIRCOM_DAP INTEGER(1)  {CLAIMQ LIMIT}
         FLAG = 92; ! interactive use (of DAP) not allowed
         ->OUT IF  DTRYING << 17 >= 0
         -> OUT IF  PROCUSER # DIRCOM_DAP USER(2) # ""
         UINF_DAPINSTRS = LENGTHENI(DIRCOM_DAP INTEGER(2)) * DAP INSPERSEC
      FINISH 
!
! Make attempts to destroy the file (in case re-starting from disorder.
!
      SUFF = ITOS(UINF_ISUFF)
      FLAG = DDISCONNECTI(DAPFILE.SUFF,  - 1, 2 {and destroy})
      FLAG = DDESTROYF(DAPFILE.SUFF,  - 1, 1)
!
      DAP_KBYTES = (DAP_KBYTES + 3) & ( ¬ 3); ! make it a whole no of pages for the swap in/out
      FLAG = DCREATEF(DAPFILE.SUFF,  - 1, DAP_KBYTES, 5, LEAVE, DA) {temp}
      ->OUT UNLESS  FLAG = 0
!
      DAPSEG = 0
      DAPGAP = 0
      FLAG = DCONNECTI(DAPFILE.SUFF,  - 1, 3, (DAP ACR << 4) ! DAP ACR, DAPSEG, DAPGAP)
      ->OUT UNLESS  FLAG = 0
!
      
      KBYTES = DAP_KBYTES
      DAP_VADDR = DAPSEG << 18
      DAP STATE = CLAIMED
      ->OK
!
!--------------------------------------------------
!
SW(2): ! RELEASE
      PRINTSTRING("Release ")
      FLAG = 67
      ->OUT IF  DAP STATE & CLAIMED = 0
!
!
      K = DDISCONNECTI(DAPFILE.SUFF,  - 1, 0{temp}); !2 {and destroy})
 ! result not interesting
!
      DAP STATE = 0
      ->OK
!
!--------------------------------------------------
!
SW(3): ! START
      PRINTSTRING("Start ")
      FLAG = 63 {not claimed}
      ->OUT IF  DAP STATE & CLAIMED = 0

      UROUT FLAG = 0
      COB = DAP_COB
      COL = DAP_COL
      NDPC = DAP_NDPC

      LIMIT = (KBYTES << 8 {to words}) - PLANE SIZE

      FLAG = 8
      ->OUT UNLESS  40 * PLANE SIZE {workspace+control area=40 planes} < COB
      ->OUT UNLESS  COB <= COL < LIMIT
      ->OUT UNLESS  0 <= NDPC < COL - COB + PLANE SIZE
      WRSNT(" secs", DAP_SECONDS, 5)
      -> OUT UNLESS  0 < DAP_SECONDS < 72*3600; ! 3 day max!

      FLAG = 71
      ->OUT IF  UINF_DAPINSTRS <= 0; ! nothing left

      ILEFT = LENGTHENI(DAP_SECONDS) * DAP INSPERSEC; ! length of this run
      ->OUT IF  ILEFT > UINF_DAPINSTRS; ! not enough left for the full run
      IUSED = 0
      NEWLINE

      FLAG = DGETDA(PROCUSER, DAPFILE.SUFF,  - 1, ADDR(SECTS( - 4)))
      ->OUT IF  FLAG # 0 {but what  about a nice flag?}

      ! Remove AMT entries for the file, ie. get all pages out of store and back to disc.
      ! We check in fn PAGE TRANSFER that these transfers have completed.

      J = 1 - (SECTS(-3){no of sects} & 1)
      ! J is now zero if the file has an odd number of sections (blocks), and is
      ! 1 if it has an odd number of blocks. This way we get the right parameter
      ! for DSEGMENT for the final segment.
      K = DAPSEG + DAPGAP - 1
      FOR  SEG = DAPSEG, 1, K CYCLE 
         IF  SEG < K THEN  KK = 1 ELSE  KK = J
         DSEGMENT(SEG ! (KK << 31))
      REPEAT 

      FLAG = DGETDA(PROCUSER, DAPFILE.SUFF,  - 1, ADDR(SECTS( - 4)))
      ->OUT IF  FLAG # 0 {but what  about a nice flag?}

      DAPTIMEOUTFLAG = 0

      CYCLE 
         FLAG = 0
         P = 0
         P_DEST = (31 << 16) ! 1; ! Allocate
         P_P1 = ((((LIMIT + PLANE SIZE) << 2 {to bytes}) C 
             + DAP BLOCK SIZE - 1) // DAP BLOCK SIZE) + EXTRA BLOCKS

         DAP STATE = DAP STATE ! SUPVR TOLD

         DOUTI(P)  {This is where we CLAIM a DAP from SUPERVISOR}
         ! WRSNT("Status ", PROCLIST(UINF_PROCNO)_STATUS, X'32')
         FLAG = P_P1
         PREC("Allocate: ", P, 0) UNLESS  FLAG = 0
         IF  FLAG = -1 AND  P_SRCE = X'1F0005' START 
            ! DAP Claim request has been de-queued (abandoned) in response to a
            ! DAP STOP request (DAP Driver DACT=5).
            FLAG = 64 {DAP Claim de-queued}
            -> OUT
         FINISH 

         FLAG = FLAG + 72 AND  ->OUT IF  FLAG # 0

         DAP IDENT = P_P2
         LOGICAL DAP NO = DAP IDENT >> 16 << 8 {see note in spec for supvr allocate call}
         DAP BLOCK NO = P_P3 + EXTRA BLOCKS
         DAP BLOCKS ALLOC = P_P4 - EXTRA BLOCKS
         DAP VREALADDR = ((DAP IDENT << 22) ! C 
            (DAP BLOCK NO * DAP BLOCK SIZE) + X'1000000') ! X'80000000'
            ! This is a virtual address referencing the first page of the first
            ! DAP block of the current allocation.



         DATUM = (DAP BLOCK NO * DAP BLOCK SIZE {bytes}) >> 2
         LIMIT = DATUM + (KBYTES << 8 {to words}) - PLANE SIZE

         XEXIT = PAGE TRANSFERS(FILE TO DAP)

         IF  XEXIT = 0 START 

            TRANSFER REC ADDR = ADDR(TRANSFER REC) {this variable tells whether a recursive call has been made}
            TRANSFER REC = 0
            TRANSFER REC_DAP VREALADDR = DAP VREALADDR {to pass the right address to recursive calls}

            CYCLE 
               IC = COME BACK AFTER
               IC = ILEFT IF  ILEFT < COME BACK AFTER

               DAPSTARTP = 0
               DAPSTARTP_DEST = (31 << 16) ! (LOGICAL DAP NO ! 4); ! Start
               DAPSTARTP_P1 = DATUM
               DAPSTARTP_P2 = LIMIT
               DAPSTARTP_P3 = COB
               DAPSTARTP_P4 = COL
               DAPSTARTP_P5 = NDPC
               DAPSTARTP_P6 = IC

               DPONI(DAPSTARTP)
               XEXIT = CHECK BTS TRANSFERS(AWAIT DAP STOP, 0)
               ! Result is zero if no page transfer failures have occurred, else 1

               J = DAPSTARTP_SRCE & X'FFFF'
               DAPTIMEOUTFLAG = 44 AND  XEXIT = 1 AND  EXIT  IF  J = 11
               IF  J = 3 C 
               THEN  FLAG = 0 C 
               ELSE  START 
                  PREC("Start: ", DAPSTARTP, 0)
                  FLAG = 72
                  XEXIT = 1
                  EXIT 
               FINISH 

               INSTAT = DAPSTARTP_P1 >> 24
               NDPC = DAPSTARTP_P2

               THIS = IC - DAPSTARTP_P4; ! amount used this time
               IUSED = IUSED + THIS
               ILEFT = ILEFT - THIS

               ! Now see if the special DAP STOP instruction has been executed.
               ! This is in connection with the block transfer system (BTS).
               IF  DAPSTARTP_P5 {ILOG1} >> 20 = X'F7F' START 
                  UROUT FLAG = UROUTINE(DAPSTARTP_P5, DAPSTARTP_P6, NDPC)
                  XEXIT = XEXIT ! UROUT FLAG

                  ! Remove the DAP STOP bit, because we don't really want it to stop - quite the reverse
                  INSTAT = INSTAT & (¬DAP STOP)
               FINISH 

               XEXIT = XEXIT ! INSTAT & (DAP PROGERR ! DAP HWARE ERR ! DAP INTR ! DAP STOP ! HOST FORCED STOP)

               XEXIT = XEXIT ! 1 IF  ILEFT <= 0

            REPEAT  UNTIL  XEXIT # 0 OR  DAPSTARTP_P1 {STATUS bits, 24-31} & SWAP OUT BIT # 0

            XEXIT = XEXIT ! CHECK BTS TRANSFERS(FINISH TRANSFERS, -1) {ensure all BTS transfers complete}

            XEXIT = XEXIT ! PAGE TRANSFERS(DAP TO FILE)
         FINISH 

         TRANSFER REC ADDR = 0
         P = 0
         P_DEST = (31 << 16) ! (LOGICAL DAP NO ! 2); ! De-allocate
         P_P1 = DAP IDENT
         P_P2 = DAP BLOCK NO - EXTRA BLOCKS
         P_P3 = DAP BLOCKS ALLOC + EXTRA BLOCKS

         DOUTI(P);              ! De-allocate
         J = P_P1
         UNLESS  J = 0 AND  FLAG = 0 START 
            PREC("De-alloc: ", P, 0)
            FLAG = 67 IF  FLAG = 0; ! may have been set to 72 by the DAP START DOUT above
            ->OUT
         FINISH 

         DAP STATE = DAP STATE & ( ¬ SUPVR TOLD)

      REPEATUNTIL  XEXIT # 0


      UINF_DAPINSTRS = UINF_DAPINSTRS - IUSED

      MINSTRS = (IUSED + 500000) // 1000000
      DSECS = (IUSED + DAP INSPERSEC - 1) // DAP INSPERSEC; ! round up for safety
      WRSNT("DAP stops.", MINSTRS, 5)
      WRSNT(" million instructions. Notional time", DSECS, 5)
      PRINTSTRING(" seconds ")

      FSYS =  - 1
      J = HINDA(PROCUSER, FSYS, INDAD, 0)
      IF  J = 0 START 
         H == RECORD(INDAD)
         H_DAPSECS = H_DAPSECS + DSECS
      FINISH 
!
! Give back neccessary data from Image Store
!
      DAP_CONF = LOGICAL DAP NO >> 8
      DAP_INSTAT = INSTAT
      DAP_ADVIOL = DAPSTARTP_P1 & X'00FFFF00'
      DAP_STATUS = DAPSTARTP_P1 & X'000000FF'
      DAP_NDPC = NDPC
      DAP_IC = IC
      DAP_DOLOG1 = (DAPSTARTP_P3 & X'FFFF8000') >> 15
      DAP_DOLOG2 = DAPSTARTP_P3 & X'00007FFF'
      DAP_ILOG1 = DAPSTARTP_P5
      DAP_ILOG2 = DAPSTARTP_P6
      DAP_SECONDS = DAP_SECONDS - DSECS

      FLAG = UROUT FLAG
      IF  FLAG = 0 THEN  FLAG = DAPTIMEOUTFLAG {Error if non-zero}
      IF  FLAG # 0 THEN  ->OUT
      ->OK
!
!--------------------------------------------------
!
SW(4):
 ! STOP
      PRINTSTRING("Stop ")
      FLAG = 67 {not claimed}
      ->OUT IF  DAP STATE & CLAIMED = 0

      FLAG = 72
      ->OUT IF  DAP STATE & SUPVR TOLD = 0
!
      ! See comments above for DDAP  ACT = 4, for the detailed effects of this
      ! message to Supervisor.
      P = 0
      P_DEST = (31 << 16) ! (LOGICAL DAP NO ! 5); ! Stop
      DPONI(P)
      ->OK
!
!--------------------------------------------------
!
SW(5): ! DAP RELATED DATA
      FSYS =  - 1
      FLAG = HINDA(PROCUSER, FSYS, INDAD, 0)
      ->OUT1 UNLESS  FLAG = 0
!
      J = COM_SECSFRMN - LAST PRINT
      J = J + 24*60*60 IF  J < 0
      IF  J > 600 AND  DTRYING = X'F7F7F7F7' START 
         FLAG = 1; ! non-zero to indicate that print reqd
         LAST PRINT = COM_SECSFRMN
         PRINTSTRING("DAP Data ")
      FINISH 
!
      H == RECORD(INDAD)
      DAP_CONF=DIRCOM_DAP INTEGER(1)  {CLAIMQ LIMIT}
      DAP_SECONDS = H_DAPSECS
      DAP_BATCH LIMIT = DIRCOM_DAP INTEGER(4)  {BATCH LIMIT}
      DAP_INTER LIMIT = DIRCOM_DAP INTEGER(2)  {INTER LIMIT}
      CYCLE  J = 0, 1, 2
         USER = DIRCOM_DAP USER(J)
         DAP_DAP USER(J) = USER
         PRINTSTRING(TAG1(J) . USER) UNLESS  FLAG = 0
      REPEAT 
!
      PRINTSTRING(",CB0 " . DIRCOM_DAP BATCH USER(0)) UNLESS  FLAG = 0
      PRINTSTRING(",CB1 " . DIRCOM_DAP BATCH USER(1)) UNLESS  FLAG = 0
!
      CYCLE  J = 1, 1, 6
         DAP_DAP INTEGER(J) = DIRCOM_DAP INTEGER(J)
      REPEAT 
!
      DAP_DAP INTEGER(7) = 0
      DAP_DAP INTEGER(8) = 0
      J = 0  {count how many DAP's we have}
      J = 1 IF  COM_CDR(1)_DAP STATE > 0
      J = J + 1 IF  COM_CDR(2)_DAP STATE > 0
   PRINTSTRING(", " . ITOS(J) . " daps") UNLESS  FLAG = 0
      -> SW5(J)
SW5(1):
      -> SW52 IF  DIRCOM_DAP BATCH USER(1) = ""
      -> SW5(0)
SW5(2):
      IF  DIRCOM_DAP BATCH USER(1) = "" C 
      THEN  DAP_DAP INTEGER(8) = DIRCOM_DAP INTEGER(6)  {HI B1}
SW52:
      IF  DIRCOM_DAP BATCH USER(0) = "" C 
      THEN  DAP_DAP INTEGER(7) = DIRCOM_DAP INTEGER(4)  {HI B0}
SW5(0):
      DAP_SPOOLR BATCH LIMIT = DAP_DAP INTEGER(7)
      -> OK1 IF  FLAG = 0; ! no more printing reqd
!
      CYCLE  J = 1, 1, 8
         PRINTSTRING(TAG2(J))
         WRITE(DAP_DAP INTEGER(J), 1)
      REPEAT 
      SPACE
!
      ->OK
!
!-----------------------------------------------------------------------
!
SW(6):                        ! Give users running or queued for the DAP
                              ! ADR is address of a %STRING(6)%ARRAY 1:20
                              ! initially all zeroes, into which this code
                              ! places the usernames currently queued for or
                              ! using the DAP. First username is the one which
                              ! HAS the DAP.
      FLAG = 45
      ->OUT IF  VAL(ADR, 20*7, 1, DAP PSR) = 0

      P = 0
      P_DEST = (31<<16) ! 10 {enquire for queued users}
      DOUT11I(P)

      ! P_P1 holds number of users, P_P2 to P_P6 is a byte array of the process
      ! numbers.
      P_P1 = 0 UNLESS  0<=P_P1<=20


      FOR  J = 0, 1, P_P1-1 CYCLE  {zero times round cycle if P_P1 zero}
         K = BYTEINTEGER(ADDR(P_P2)+J) {procno}
         ! Obtain username from proclist
         STRING(ADR) = PROCLIST(K)_USER
         ADR = ADR + 7
      REPEAT 

      -> OK1

SW(7):                                  ! Specify (possibly asynchronous)
                                        ! transfers into and/or out of the DAP
      FLAG = INITIALISE REQUEST RECORDS
      IF  FLAG # 0 THEN  -> OUT
      -> OK1

SW(8):                                  ! Await completion of specified, or all,
                                        ! transfers initiated via entry 7
      REQ8 == RECORD(ADR)
      FLAG = CHECK BTS TRANSFERS(FINISH TRANSFERS, REQ8_IDENT)
      -> OUT IF  FLAG # 0
      -> OK1

OK:
      WRS("OK")
OK1:
      RESULT  = 0
OUT:
      WRSN(" Flag", FLAG)
OUT1:
      RESULT  = FLAG
END ; ! DDAPI
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  DDAP(INTEGERFN  A(INTEGER  A, B, C), INTEGER  B, C)
INTEGER  J
      J = IN2(256+96)
      -> OUT UNLESS  J = 0
!
      DAP ACR = D CALLERS ACR
      DAP PSR = D CALLERS PSR
!
      J = DDAPI(A, B, C)
OUT:
      RESULT  = OUT(J, "----II")
END ; ! DDAP
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  DAZ(INTEGERFN  A(INTEGER  A, B, C), INTEGER  B, C)
INTEGER  J
      J = IN2(256+96)
      -> OUT UNLESS  J = 0
!
      DAP ACR = D CALLERS ACR
      DAP PSR = D CALLERS PSR
!
      J = DDAPI(A, B, C)
OUT:
      RESULT  = OUT(J, "----II")
END ; ! DAZ
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  DAP INTERFACE(INTEGER  ACT)
INTEGER  J
RECORD (DIRCOMF)NAME  DIRCOM
SWITCH  SW(1 : 3)
      DIRCOM == RECORD(SYSAD(DIRCOMKEY, -1))
      -> SW(ACT)
SW(1):   ! Set defaults in DIRCOM
      DIRCOM_DAP USER(0) = ""
      DIRCOM_DAP USER(1) = ""
      DIRCOM_DAP USER(2) = ""
      DIRCOM_DAP BATCH USER(0) = ""
      DIRCOM_DAP BATCH USER(1) = ""
      DIRCOM_DAP INTEGER(1) = 4
      DIRCOM_DAP INTEGER(2) = 600
      DIRCOM_DAP INTEGER(3) = 0
      DIRCOM_DAP INTEGER(4) = 3600
      DIRCOM_DAP INTEGER(5) = 0
      DIRCOM_DAP INTEGER(6) = 3600
      RETURN 
SW(2):     ! Called at start of a batch job
      WRSNT(",DAPsecs=", UINF_DAPSECS, 5)
      J = UINF_DAP NO
      WRSNT(",DAPno=", J, 4)
      UINF_DAP NO = 0 UNLESS  0 <= J <= 1
      WRS(",Slot=" . DIRCOM_DAP BATCH USER(UINF_DAP NO))
      DIRCOM_DAP BATCH USER(UINF_DAP NO) = PROCUSER
      UINF_DAPINSTRS = LENGTHENI(UINF_DAPSECS) * DAPINSPERSEC
      RETURN 
SW(3):     ! Called at DSTOP
      DIRCOM_DAP BATCH USER(UINF_DAP NO) = ""
END ; ! DAP INTERFACE
!
!
!
ENDOFFILE