-- NatInstrGPIBDriver.mesa
--    Copyright c 1985 by Xerox Corporation.  All rights reserved.
-- Created for PostCedar5.2 by Neil Gunther, May 17, 1985 11:22:31 am PDT
-- Compiled for Cedar 6.0 , September 6, 1985
-- Last edited by Neil Gunther, September 26, 1985 10:59:15 am PDT
-- Tim Diebert,      7-Oct-85  8:28:10
-- 
-- 
-- This module implements the  NATIONAL INSTRUMENTS GPIB-796P interface functions for a 
-- Dicentra.  It comprises 2 parts. The upper level PROCs conform to the procedural
-- interface specified in  GPIB.mesa; the lower level PROCs implement the board-specific
-- calls.  All GPIB drivers should conform to this organization.
-- 
-- The following is paraphrased from the original National Instruments documentation.
-- These procedures support synchronous, non-interrupt, non-DMA I/O, for a single interface
-- board   which is always the system controller and the controller in charge.  All 
-- procedures return the subset of the standard GPIB status bit vector consisting of
-- just the SRQI and BusEND status bits.
-- The result of the last call is also available in the global  variable,
-- statusWord.  If it was an I/O operation the actual count transferred
-- is available in the global variable,  byteCount.  Before any other call is made 
-- the GPIBInitialize function must be called to initialize the GPIB interface.  Prior
-- to calling GPIBRead or GPIBWrite the appropriate devices, including  the interface
-- board, must be addressed by calling GPIBCommand with the proper addressing commands.
-- 
DIRECTORY
   Ascii,
   DicentraInputOutput,
   GPIB,
   Inline,
   Process,
   String;

NatInstrGPIBDriver: MONITOR 

   IMPORTS  DicentraInputOutput, Inline, Process, String 
   EXPORTS GPIB = BEGIN
   
   OPEN GPIB, Inline; 
   
   -- Globals for Level 1
      maxSendBuf: NAT = GPIB.maxWriteBuffer;
      sendBuffer: LONG STRING ← [maxSendBuf];
      controller: GPIB.DeviceAddr ← 0; 
   
   -- Globals for Level 2 (GPIB-796P interface) 
      statusWord: WORD ← 0000H; 
      BusEND: WORD = LOOPHOLE[BITSHIFT[1, 13]];
      SRQI: WORD = LOOPHOLE[BITSHIFT[1, 12]]; 
      Base: LONG POINTER = LOOPHOLE[0400000H];   
      GPIBBaseAddr: LONG POINTER = Base + 8000H / 2; -- current setting for Multibus 
      mask: Mask;
      maxReadBuf: CARDINAL = GPIB.maxReadBuffer;
      buf: LONG STRING ← [maxReadBuf]; 
      byteCount: INTEGER ← 0;
   
   
-- *** Level 1: Implementation of GPIB.mesa ***
-- 
   InitializeController: PUBLIC ENTRY PROC RETURNS [open: BOOL] = {
      [] ← Init[];
      open ← TRUE;
      };
      
   FinalizeController: PUBLIC ENTRY PROC = {
      SetIdle[];
      };
      
-- Universal commands (all devices whether addressed or not)

   Command: PUBLIC ENTRY PROC [sendMsg: LONG STRING] = {
      [] ← Cmd[sendMsg];
      };
   
   DevicesClear: PUBLIC ENTRY PROC = {
      ClearBuffer[];
      String.AppendChar[sendBuffer, GPIB.devicesClear];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      [] ← Cmd[sendBuffer];
      };
      
   GoToLocal: PUBLIC ENTRY PROC = {
      ClearBuffer[];
      String.AppendChar[sendBuffer, GPIB.goToLocal];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      [] ← Cmd[sendBuffer];
      };
   
   GroupExecuteTrigger: PUBLIC ENTRY PROC = {
      ClearBuffer[];
      String.AppendChar[sendBuffer, GPIB.groupExecuteTrigger];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      [] ← Cmd[sendBuffer];
      };
   
   InterfaceClear: PUBLIC ENTRY PROC = {
      [] ← Clear[];
      };
   
   LocalLockout: PUBLIC ENTRY PROC = {
      ClearBuffer[];
      String.AppendChar[sendBuffer, GPIB.localLockout];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      [] ← Cmd[sendBuffer];
      };
   
   RemoteEnable: PUBLIC ENTRY PROC = {
      [] ← SetRemote[TRUE];
      };
   
-- Selected Device Commands (only those devices addressed)
-- 
   SelectedDeviceClear: PUBLIC ENTRY PROC [device: GPIB.DeviceAddr]= {
      ClearBuffer[]; 
      String.AppendChar[sendBuffer, GPIB.UnListen];
      String.AppendChar[sendBuffer, GetLAD[device]];               
      String.AppendChar[sendBuffer, GPIB.selectedDeviceClear];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      [] ← Cmd[sendBuffer]; 
      };
   
   SelectedGoToLocal: PUBLIC ENTRY PROC [device: GPIB.DeviceAddr] = {
      ClearBuffer[]; 
      String.AppendChar[sendBuffer, GPIB.UnListen];
      String.AppendChar[sendBuffer, GetLAD[device]];               
      String.AppendChar[sendBuffer, GPIB.goToLocal];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      [] ← Cmd[sendBuffer]; 
      };
      
   SelectedGroupEnableTrigger: PUBLIC ENTRY PROC [device: GPIB.DeviceAddr] = {
   };
   PollDevice: PUBLIC ENTRY PROC [device: GPIB.DeviceAddr, labels: GPIB.SRQLabels] = {};
   ReadOnInterrupt: PUBLIC ENTRY PROC [device: GPIB.DeviceAddr, recvMsg:
     LONG STRING, labels: GPIB.SRQLabels] = {};
   SelectedExecuteTrigger: PUBLIC ENTRY PROC [device: GPIB.DeviceAddr] = {
      ClearBuffer[];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      String.AppendChar[sendBuffer, GetLAD[device]];
      String.AppendChar[sendBuffer, GPIB.groupExecuteTrigger];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      [] ← Cmd[sendBuffer];
      };
   
   SelectedRemoteEnable: PUBLIC ENTRY PROC [device: GPIB.DeviceAddr]  = {
      ClearBuffer[]; 
      String.AppendChar[sendBuffer, GPIB.UnListen];
      String.AppendChar[sendBuffer, GetLAD[device]];               
      [] ← SetRemote[TRUE];
      String.AppendChar[sendBuffer, GetTAD[device]];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      [] ← Cmd[sendBuffer];
      };
-- 
-- Explicit Polling Routines
-- 
   ParallelPollConfigure: PUBLIC ENTRY PROC [device: GPIB.DeviceAddr] = {
      ClearBuffer[];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      String.AppendChar[sendBuffer, GPIB.parallelPollConfigure];
      String.AppendChar[sendBuffer, GPIB.parallelPollEnable];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      [] ← Cmd[sendBuffer];
      };
   
   ParallelPollUnconfigure: PUBLIC ENTRY PROC = {
      ClearBuffer[];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      String.AppendChar[sendBuffer, GPIB.parallelPollConfigure];
      String.AppendChar[sendBuffer, GPIB.parallelPollDisable];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      [] ← Cmd[sendBuffer];
      };
   
   SelectedParallelPollConfigure: PUBLIC ENTRY PROC [device: GPIB.DeviceAddr] = {
      ClearBuffer[];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      String.AppendChar[sendBuffer, GetLAD[device]];
      String.AppendChar[sendBuffer, GPIB.parallelPollConfigure];
      String.AppendChar[sendBuffer, GPIB.parallelPollEnable];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      [] ← Cmd[sendBuffer];
      };
      
   SelectedParallelPollUnconfigure: PUBLIC ENTRY PROC [device: GPIB.DeviceAddr] = {
      ClearBuffer[];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      String.AppendChar[sendBuffer, GetLAD[device]];
      String.AppendChar[sendBuffer, GPIB.parallelPollConfigure];
      String.AppendChar[sendBuffer, GPIB.parallelPollDisable];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      [] ← Cmd[sendBuffer];
      };
   
-- Read Specific Devices
-- 
   SelectedReadSerialPoll: PUBLIC ENTRY PROC [device: GPIB.DeviceAddr] RETURNS [statusByte: CHAR] = {
      ClearBuffer[];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      String.AppendChar[sendBuffer, GPIB.serialPollEnable];
      String.AppendChar[sendBuffer, GetTAD[device]];
      String.AppendChar[sendBuffer, GetLAD[controller]];
      [] ← Cmd[sendBuffer];
      ClearBuffer[];
      [statusByte, ] ← DataByteRead[];
      String.AppendChar[sendBuffer, GPIB.serialPollDisable];
      String.AppendChar[sendBuffer, GPIB.UnTalk];
      [] ← Cmd[sendBuffer];
      };
      
   ReadStatusByte: PUBLIC ENTRY PROC [device: GPIB.DeviceAddr] RETURNS [char: CHAR]  = {
      ClearBuffer[];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      String.AppendChar[sendBuffer, GetTAD[device]];
      String.AppendChar[sendBuffer, GetLAD[controller]];
      [] ← Cmd[sendBuffer];
      ClearBuffer[];
      [char, ] ← DataByteRead[];
      String.AppendChar[sendBuffer, GPIB.UnTalk];
      [] ← Cmd[sendBuffer];
      };   
      
   ReadDevice: PUBLIC ENTRY PROC [device: DeviceAddr, recvMsg: LONG STRING] = {
      ClearBuffer[];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      String.AppendChar[sendBuffer, GetTAD[device]];
      String.AppendChar[sendBuffer, GetLAD[controller]];
      [] ← Cmd[sendBuffer];
      ClearBuffer[];
      [recvMsg, ] ← DataRead[];
      String.AppendChar[sendBuffer, GPIB.UnTalk];
      [] ← Cmd[sendBuffer];
      };
      
      
-- Write Specific Devices
-- 
   WriteDevice: PUBLIC ENTRY PROC [device: GPIB.DeviceAddr, sendMsg: LONG STRING] = {
      ClearBuffer[];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      String.AppendChar[sendBuffer, GetLAD[device]];
      String.AppendChar[sendBuffer, GetTAD[controller]];
      [] ← Cmd[sendBuffer];
      ClearBuffer[];
      [] ← DataWrite[sendMsg, TRUE, TRUE];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      [] ← Cmd[sendBuffer];
      };
      
   WriteDeviceInitial: PUBLIC ENTRY PROC [device: DeviceAddr, sendMsg: LONG STRING] = {
      ClearBuffer[];
      String.AppendChar[sendBuffer, GPIB.UnListen];
      String.AppendChar[sendBuffer, GetLAD[device]];
      String.AppendChar[sendBuffer, GetTAD[controller]];
      [] ← Cmd[sendBuffer];
      ClearBuffer[];
      [] ← DataWrite[sendMsg, TRUE, FALSE];
      };
   WriteDeviceContinued: PUBLIC ENTRY PROC [device: DeviceAddr, sendMsg: LONG STRING,
      last: BOOL] = {
      ClearBuffer[];
      [] ← DataWrite[sendMsg, FALSE, last];
      };
   
   WriteDeviceBlock: PUBLIC ENTRY PROC [device: DeviceAddr, lp: LONG POINTER,
     quadWordCnt: CARDINAL, last: BOOL] = {
     dataWord: MACHINE DEPENDENT RECORD [a,b: CHAR] ← LOOPHOLE [lp↑];
      IF quadWordCnt < 1 THEN quadWordCnt ← 1;
      FOR i: CARDINAL IN [0 .. quadWordCnt-1) DO
        DicentraInputOutput.OutputByteEven[LOOPHOLE[dataWord.a, CARDINAL], GPIBBaseAddr];
        WHILE BITAND[DicentraInputOutput.InputByteOdd[GPIBBaseAddr], mask.D0] = 0 DO ENDLOOP;
        DicentraInputOutput.OutputByteEven[LOOPHOLE[dataWord.b, CARDINAL], GPIBBaseAddr];
        WHILE BITAND[DicentraInputOutput.InputByteOdd[GPIBBaseAddr], mask.D0] = 0 DO ENDLOOP;
        lp ← lp + 1;
        dataWord ← LOOPHOLE [lp↑];
        DicentraInputOutput.OutputByteEven[LOOPHOLE[dataWord.a, CARDINAL], GPIBBaseAddr];
        WHILE BITAND[DicentraInputOutput.InputByteOdd[GPIBBaseAddr], mask.D0] = 0 DO ENDLOOP;
        DicentraInputOutput.OutputByteEven[LOOPHOLE[dataWord.b, CARDINAL], GPIBBaseAddr];
        WHILE BITAND[DicentraInputOutput.InputByteOdd[GPIBBaseAddr], mask.D0] = 0 DO ENDLOOP;
        lp ← lp + 1;
        dataWord ← LOOPHOLE [lp↑];
        DicentraInputOutput.OutputByteEven[LOOPHOLE[dataWord.a, CARDINAL], GPIBBaseAddr];
        WHILE BITAND[DicentraInputOutput.InputByteOdd[GPIBBaseAddr], mask.D0] = 0 DO ENDLOOP;
        DicentraInputOutput.OutputByteEven[LOOPHOLE[dataWord.b, CARDINAL], GPIBBaseAddr];
        WHILE BITAND[DicentraInputOutput.InputByteOdd[GPIBBaseAddr], mask.D0] = 0 DO ENDLOOP;
        lp ← lp + 1;
        dataWord ← LOOPHOLE [lp↑];
        DicentraInputOutput.OutputByteEven[LOOPHOLE[dataWord.a, CARDINAL], GPIBBaseAddr];
        WHILE BITAND[DicentraInputOutput.InputByteOdd[GPIBBaseAddr], mask.D0] = 0 DO ENDLOOP;
        DicentraInputOutput.OutputByteEven[LOOPHOLE[dataWord.b, CARDINAL], GPIBBaseAddr];
        WHILE BITAND[DicentraInputOutput.InputByteOdd[GPIBBaseAddr], mask.D0] = 0 DO ENDLOOP;
	lp ← lp + 1;
	dataWord ← LOOPHOLE [lp↑];
        ENDLOOP;
      DicentraInputOutput.OutputByteEven[LOOPHOLE[dataWord.a, CARDINAL], GPIBBaseAddr];
      WHILE BITAND[DicentraInputOutput.InputByteOdd[GPIBBaseAddr], mask.D0] = 0 DO ENDLOOP;
      DicentraInputOutput.OutputByteEven[LOOPHOLE[dataWord.b, CARDINAL], GPIBBaseAddr];
      WHILE BITAND[DicentraInputOutput.InputByteOdd[GPIBBaseAddr], mask.D0] = 0 DO ENDLOOP;
      lp ← lp + 1;
      dataWord ← LOOPHOLE [lp↑];
      DicentraInputOutput.OutputByteEven[LOOPHOLE[dataWord.a, CARDINAL], GPIBBaseAddr];
      WHILE BITAND[DicentraInputOutput.InputByteOdd[GPIBBaseAddr], mask.D0] = 0 DO ENDLOOP;
      DicentraInputOutput.OutputByteEven[LOOPHOLE[dataWord.b, CARDINAL], GPIBBaseAddr];
      WHILE BITAND[DicentraInputOutput.InputByteOdd[GPIBBaseAddr], mask.D0] = 0 DO ENDLOOP;
      lp ← lp + 1;
      dataWord ← LOOPHOLE [lp↑];
      DicentraInputOutput.OutputByteEven[LOOPHOLE[dataWord.a, CARDINAL], GPIBBaseAddr];
      WHILE BITAND[DicentraInputOutput.InputByteOdd[GPIBBaseAddr], mask.D0] = 0 DO ENDLOOP;
      DicentraInputOutput.OutputByteEven[LOOPHOLE[dataWord.b, CARDINAL], GPIBBaseAddr];
      WHILE BITAND[DicentraInputOutput.InputByteOdd[GPIBBaseAddr], mask.D0] = 0 DO ENDLOOP;
      lp ← lp + 1;
      dataWord ← LOOPHOLE [lp↑];
      DicentraInputOutput.OutputByteEven[LOOPHOLE[dataWord.a, CARDINAL], GPIBBaseAddr];
      WHILE BITAND[DicentraInputOutput.InputByteOdd[GPIBBaseAddr], mask.D0] = 0 DO ENDLOOP;
      IF last THEN WriteReg[auxMode, sendEOI]; -- EOI with last byte
      DicentraInputOutput.OutputByteEven[LOOPHOLE[dataWord.b, CARDINAL], GPIBBaseAddr];
      WHILE BITAND[DicentraInputOutput.InputByteOdd[GPIBBaseAddr], mask.D0] = 0 DO ENDLOOP;
      };
      
-- Private Procedures
-- 
   GetLAD: PRIVATE INTERNAL PROC [device: GPIB.DeviceAddr] RETURNS [CHAR] = {
      IF device > 30 THEN ERROR;  
      RETURN [LOOPHOLE[device+32]]; -- ASCII Listen address 
      };
   
   GetTAD: PRIVATE INTERNAL PROC [device: GPIB.DeviceAddr] RETURNS [CHAR] = {
      IF device > 30 THEN ERROR;
      RETURN [LOOPHOLE[device+64]]; -- ASCII Talk address
      };
      
   ClearBuffer: PRIVATE INTERNAL PROC [] =  {sendBuffer.length ← 0};
   
   
-- *** Level 2: Board-specific Procedures ***
-- 
   --          NEC 7210   M a s k s   a n d    R e g i s t e r s
   
   -- Auxiliary Controller Commands          Nat. Instr. mnemonic
   powerOn:             CHAR = 000C;         -- AUX-PON 
   chipReset:           CHAR = 002C;         -- AUXCR
   sendEOI:             CHAR = 006C;         -- AUXSEOI
   takeAsynchControl:   CHAR = 021C;         -- AUXTCA
   goToStandby:         CHAR = 020C;         -- AUXGTS
   executeParaPoll:     CHAR = 035C;         -- AUX-EPP
   setIFC:              CHAR = 036C;         -- AUXSIFC
   clearIFC:            CHAR = 026C;         -- AUXCIFC
   setREN:              CHAR = 037C;         -- AUX-SREN
   clearREN:            CHAR = 027C;         -- AUXCREN
      
   
   -- Control Masks for Hidden Registers         Nat. Instr. mnemonic
   ICR:             CARDINAL = 000040B;        -- Internal Counter Register   
   PPR:             CARDINAL = 000140B;        --   Parallel Poll Register
   AUXRA:           CARDINAL = 000200B;        -- Aux Register A   
   AUXRB:           CARDINAL = 000240B;        -- Aux Register B   
   AUXRE:           CARDINAL = 000300B;        -- Aux Register E
   
   -- Controller Register Definitions              Nat. Instr. mnemonic
   WRegister: TYPE =  {  -- WriteOnly
      cmdDataOut,                            -- cdor  
      intrptMask1,                            -- imr1
      intrptMask2,                           -- imr2
      serialPollMode,                        -- spmr
      addressMode,                        -- admr
      auxMode,                              -- auxmr
      address,                              -- adr
      endOfStr                              -- eosr
   };
   
   RRegister: TYPE =  {  -- ReadOnly
      dataIn,                                 -- dir  
      intrptStatus1,                            -- isr1
      intrptStatus2,                           -- isr2
      serialPollStatus,                        -- spsr
      addressStatus,                        -- adsr
      cmdPassThru,                        -- cptr
      address0,                              -- adr0
      address1                              -- adr1
   };
   
   Mask: TYPE = RECORD [ 
      DI:          CARDINAL ←  BITSHIFT[1, 0], -- Data In     
      D0:          CARDINAL ←  BITSHIFT[1, 1], -- Data Out
      BusEND:      CARDINAL ←  BITSHIFT[1, 4], -- End
      CO:          CARDINAL ←  BITSHIFT[1, 3], -- Command Output
      SRQI:        CARDINAL ←  BITSHIFT[1, 6], -- Service Request Input     
      DMAI:        CARDINAL ←  BITSHIFT[1, 4], -- DMA Input Enable
      DMAO:        CARDINAL ←  BITSHIFT[1, 5], -- DMA Output Enable
      ADMO:        CARDINAL ←  BITSHIFT[1, 0], -- Addr Mode bit 0
      TRMO:        CARDINAL ←  BITSHIFT[1, 4], -- Transmit/Receive Mode bit 0
      TRMI:        CARDINAL ←  BITSHIFT[1, 5], -- Transmit/Receive Mode bit 1     
      DL:          CARDINAL ←  BITSHIFT[1, 5], -- Disable Listener
      DT:          CARDINAL ←  BITSHIFT[1, 6], -- Disable Talker
      ARS:         CARDINAL ←  BITSHIFT[1, 7], -- Addr Reg Select
      PPU:         CARDINAL ←  BITSHIFT[1, 4]  -- Para Poll Unconfigure
   ];   
   
   -- G P I B  - 7 9 6 P    F u n c t i o n s
   
Init: INTERNAL PROC RETURNS [WORD] = {
   WriteReg[auxMode, chipReset];         -- NEC 7210 
   [] ← ReadReg[cmdPassThru];          -- clear registers by reading & trashing result 
   [] ← ReadReg[intrptStatus1];
   [] ← ReadReg[intrptStatus2];
   WriteReg[intrptMask1, 0C];            -- disable all interrupts 
   WriteReg[intrptMask2, 0C];
   WriteReg[serialPollMode, 0C];
   WriteReg[address, 0C];             -- set 796P TAD = 100B+100B  (controller)
   -- disable secondary addressing 
   WriteRegL[address, BITOR[mask.ARS, BITOR[mask.DT, mask.DL]]];  
   WriteRegL[addressMode, BITOR[mask.TRMI, BITOR[mask.TRMO, mask.ADMO]]];
   WriteReg[endOfStr, 0C];
   WriteReg[auxMode, clearIFC];          -- and by default enable system controller 
   WriteRegL[auxMode, BITOR[ICR, 5]];             -- set internal counter register N = 5 
   WriteRegL[auxMode, BITOR[PPR, mask.PPU]];    -- parallel poll unconfigure 
   WriteRegL[auxMode, BITOR[AUXRA, 0]];
   WriteRegL[auxMode, BITOR[AUXRB, 0]];
   WriteRegL[auxMode, BITOR[AUXRE, 0]];
   WriteReg[auxMode, powerOn];   -- put chip online  
   byteCount ← 0;
   statusWord ← IF BITAND[ReadRegL[intrptStatus2], mask.SRQI]  = 1 THEN SRQI  ELSE 0;
   RETURN[statusWord];
   };

SetIdle: INTERNAL PROC = {
   -- return controller to idle state or somesuch.
   WriteReg[auxMode, chipReset];          
   [] ← ReadReg[cmdPassThru];          -- clear registers by reading & trashing result 
   [] ← ReadReg[intrptStatus1];
   [] ← ReadReg[intrptStatus2];
   WriteReg[intrptMask1, 0C];            -- disable all interrupts 
   WriteReg[intrptMask2, 0C];
   WriteReg[serialPollMode, 0C];
   WriteReg[address, 0C];             -- set 796P TAD = 100B+100B  (controller)
   -- disable secondary addressing 
   WriteRegL[address, BITOR[mask.ARS, BITOR[mask.DT, mask.DL]]];  
   WriteRegL[addressMode, BITOR[mask.TRMI, BITOR[mask.TRMO, mask.ADMO]]];
   WriteReg[endOfStr, 0C];
   WriteReg[auxMode, clearIFC];          -- and by default enable system controller 
   WriteRegL[auxMode, BITOR[ICR, 5]];             -- set internal counter register N = 5 
   WriteRegL[auxMode, BITOR[PPR, mask.PPU]];    -- parallel poll unconfigure 
   WriteRegL[auxMode, BITOR[AUXRA, 0]];
   WriteRegL[auxMode, BITOR[AUXRB, 0]];
   WriteRegL[auxMode, BITOR[AUXRE, 0]];
   -- chip is now offline  
   byteCount ← 0;
   };

Cmd: INTERNAL PROC [buffer: LONG STRING] RETURNS [WORD] = {
   s, rLength: INTEGER ← 0; 
   ClearBuf[];        
   String.AppendString[buf, buffer];
   WriteReg[auxMode, takeAsynchControl]; -- if on standby, go to cntl active state 
   byteCount ← 0;
   rLength ← buffer.length;
   WHILE (byteCount < rLength) DO
      s ← BITAND[s, BITNOT[mask.CO]];       -- clear saved copy of Command Output 
      WriteReg[cmdDataOut, buf[byteCount]]; -- output command
      byteCount ← byteCount+1;                                
      WHILE BITAND[(s ← BITOR[s, ReadRegL[intrptStatus2]]), mask.CO] = 0 DO 
         -- wait for Command Output flag BITOR-ed to preserve SRQI
         ENDLOOP;
      ENDLOOP;
   statusWord ← IF BITAND[s, mask.SRQI] # 0 THEN SRQI  ELSE 0; 
   RETURN[statusWord];
   };

DataRead: INTERNAL PROC RETURNS [LONG STRING, WORD] = {
-- Read unspecified number of bytes of data from the GPIB-796P interface into buffer.  In addition to I/O complete, the read operation terminates  on detection of EOI.  Prior to beginning the read the interface is placed in the controller standby state.  No handshake holdoffs are used so care must be exercised when taking control (i.e., asserting ATN) following DataRead.  Prior to calling  DataRead, the intended devices as well as  the interface board itself must be addressed by calling Cmd.
   i, s: CARDINAL ← 0;
   ClearBuf[];
   WriteReg[auxMode, goToStandby];    -- if controller active state, go to standby 
   byteCount ← 0;
   DO  -- read unspecified number of bytes
      WHILE BITAND[(s ← ReadRegL[intrptStatus1]), mask.DI] = 0 DO
         -- wait for Data In flag
         ENDLOOP;
      buf[byteCount] ← ReadReg[dataIn];     -- store byte 
      byteCount ← byteCount+1;
      IF buf[byteCount] = '\n OR buf[byteCount] = '\l THEN EXIT;  -- IEEE 728 string terminator (\n\l)
      IF BITAND[s, mask.BusEND] # 0 THEN EXIT; -- stop reading if EOI detected
   ENDLOOP;
   buf.length ← byteCount;      
   statusWord ← IF BITAND[s, mask.BusEND] # 0 THEN BusEND ELSE 0;       
   statusWord ← BITOR[
      statusWord, IF BITAND[ReadRegL[intrptStatus2], mask.SRQI] # 0 THEN SRQI ELSE 0
     ];
   RETURN[buf, statusWord];
   };
   
DataByteRead: INTERNAL PROC RETURNS [CHAR, WORD] = {
-- Read one byte of data from the GPIB-796P interface into buffer.  In addition to I/O complete, the read operation terminates  on detection of EOI.  Prior to beginning the read the interface is placed in the controller standby state.  No handshake holdoffs are used so care must be exercised when taking control (i.e., asserting ATN) following DataRead.  Prior to calling  DataRead, the intended devices as well as  the interface board itself must be addressed by calling Cmd.
   i, s: CARDINAL ← 0;
   ClearBuf[];
   WriteReg[auxMode, goToStandby];    -- if controller active state, go to standby 
   byteCount ← 0;
   WHILE BITAND[(s ← ReadRegL[intrptStatus1]), mask.DI] = 0 DO
      -- wait for Data In flag
      ENDLOOP;
   buf[byteCount] ← ReadReg[dataIn];     -- store byte  
   buf.length ← byteCount ← byteCount+1;      
   statusWord ← IF BITAND[s, mask.BusEND] # 0 THEN BusEND ELSE 0;       
   statusWord ← BITOR[
      statusWord, IF BITAND[ReadRegL[intrptStatus2], mask.SRQI] # 0 THEN SRQI ELSE 0
     ];
   RETURN[buf[0], statusWord];
   };
   
DataWrite: INTERNAL PROC [buffer: LONG STRING, init, last: BOOL] RETURNS [WORD] = {
-- Write data bytes from buffer to the GPIB-796P.  The write operation terminates only
--   on I/O complete.  By default, EOI is always sent along with the last byte.  Prior
--   to beginning the write the interface is placed in the controller standby state.  Prior
--   to calling  DataWrite, the  intended devices, as well as the interface board itself, 
--   must be addressed by calling Cmd.
   rLength: NAT ← buffer.length;
   ClearBuf[];
   String.AppendString[buf, buffer];
   IF init THEN WriteReg[auxMode, goToStandby];  -- if controller active state, not 
      -- go to standby (data mode)
   byteCount ←  0;
   WHILE (byteCount < rLength) DO
      IF (byteCount = rLength-1) AND last THEN WriteReg[auxMode, sendEOI]; -- EOI with last byte
      WriteReg[cmdDataOut, buf[byteCount]];   -- output byte  
      byteCount ← byteCount+1;
      WHILE BITAND[ReadRegL[intrptStatus1], mask.D0] = 0 DO
         -- wait for DataOut flag
      ENDLOOP;
   ENDLOOP;     
   statusWord ← (IF BITAND[ReadRegL[intrptStatus2], mask.SRQI] # 0 THEN SRQI ELSE 0);
   RETURN[statusWord];
   };

Clear: INTERNAL PROC RETURNS [WORD] = {
-- Send IFC for at least 100 microseconds(4 ticks).  Clear must be  called prior to the first call to Cmd in order to  initialize the bus and enable the interface to leave the controller idle state.
   WriteReg[auxMode, setIFC];          
   Process.Pause[ticks: 10];            -- 40 ticks = 1mS
   WriteReg[auxMode, clearIFC];       
   statusWord ← (IF BITAND[ReadRegL[intrptStatus2], mask.SRQI] # 0 THEN SRQI ELSE 0);
   RETURN[statusWord];
   };

SetRemote: INTERNAL PROC [set: BOOL] RETURNS [WORD] = {
   WriteReg[auxMode,  (IF set THEN setREN ELSE clearREN)];            
   statusWord ← (IF BITAND[ReadRegL[intrptStatus2], mask.SRQI] # 0 THEN SRQI ELSE 0);
   RETURN[statusWord];
   };

SetStandby: INTERNAL PROC RETURNS [WORD] = {
-- Go to the controller standby state (Data mode) from the controller active state, i.e., deassert ATN.
   WriteReg[auxMode,  goToStandby];     
   statusWord ← (IF BITAND[ReadRegL[intrptStatus2], mask.SRQI] # 0 THEN SRQI ELSE 0);
   RETURN[statusWord];
   };

SetCACS: INTERNAL PROC RETURNS [WORD] = {
-- Return to the controller active state (Command mode) from the controller standby state, i.e., assert ATN.  Note that in order to enter the controller active state from the controller idle state, Clear must be called.
   WriteReg[auxMode, takeAsynchControl];     
   statusWord ← (IF BITAND[ReadRegL[intrptStatus2], mask.SRQI] # 0 THEN SRQI ELSE 0);
   RETURN[statusWord];
   };

Wait: INTERNAL PROC [for: WORD] = {
-- Check or wait for a GPIB event to occur.  The mask argument  is a bit vector corresponding to the status bit vector.  It  has a bit set for each condition which can terminate the wait.  If the mask is 0 then no  condition is waited on and the current status is simply  returned.  Note that since the hardware SRQI bit is volatile  it will only be reported once for each occurrence of SRQ.  If  another function has returned the SRQI status bit, then a call to Wait with a mask containing SRQI will never return.
   GetStatus: INTERNAL PROC RETURNS [WORD] = {
      -- Update GPIB status information.
      RETURN [
         statusWord ← (IF BITAND[ReadRegL[intrptStatus2], mask.SRQI] # 0 THEN SRQI ELSE 0)
         ];
      };
      
   WHILE (BITAND[GetStatus[], for] = 0) DO 
       IF for = 0 THEN EXIT; 
   ENDLOOP;
   };

GetParaPoll: INTERNAL PROC RETURNS [LONG STRING, WORD] = { 
-- Conduct a parallel poll and return the byte in buf.  Prior to conducting the poll the interface is      placed in the controller active state.
   ClearBuf[];
   WriteReg[auxMode, takeAsynchControl];    --  if standby, go to controller active state
   WriteReg[auxMode, executeParaPoll];    
   buf[0] ← ReadReg[cmdPassThru];           -- store the response byte 
   buf.length ← 1;
   statusWord ← (IF BITAND[ReadRegL[intrptStatus2], mask.SRQI] # 0 THEN SRQI ELSE 0);
   RETURN[buf, statusWord];
   };
   
   -- P r i v a t e      R e g i s t e r      O p e r a t i o n s
   
   ReadReg: PRIVATE INTERNAL PROC [register: RRegister] RETURNS [char: CHAR] = {
      word: CARDINAL;
      SELECT register FROM
      dataIn           =>  word ← DicentraInputOutput.InputByteEven[GPIBBaseAddr]; 
      intrptStatus1    =>  word ← DicentraInputOutput.InputByteOdd[GPIBBaseAddr];
      intrptStatus2    =>  word ← DicentraInputOutput.InputByteEven[GPIBBaseAddr+1];
      serialPollStatus =>  word ← DicentraInputOutput.InputByteOdd[GPIBBaseAddr+1];
      addressStatus    =>  word ← DicentraInputOutput.InputByteEven[GPIBBaseAddr+2];  
      cmdPassThru      =>  word ← DicentraInputOutput.InputByteOdd[GPIBBaseAddr+2];   
      address0         =>  word ← DicentraInputOutput.InputByteEven[GPIBBaseAddr+3];
      ENDCASE          =>  word ← DicentraInputOutput.InputByteOdd[GPIBBaseAddr+3]; 
      char ← LOOPHOLE[LowByte[word]];
      };
   
   ReadRegL: PRIVATE INTERNAL PROC [register: RRegister] RETURNS [word: CARDINAL] = {
      SELECT register FROM
      dataIn           =>  word ← DicentraInputOutput.InputByteEven[GPIBBaseAddr]; 
      intrptStatus1    =>  word ← DicentraInputOutput.InputByteOdd[GPIBBaseAddr];
      intrptStatus2    =>  word ← DicentraInputOutput.InputByteEven[GPIBBaseAddr+1];
      serialPollStatus =>  word ← DicentraInputOutput.InputByteOdd[GPIBBaseAddr+1];
      addressStatus    =>  word ← DicentraInputOutput.InputByteEven[GPIBBaseAddr+2];  
      cmdPassThru      =>  word ← DicentraInputOutput.InputByteOdd[GPIBBaseAddr+2];   
      address0         =>  word ← DicentraInputOutput.InputByteEven[GPIBBaseAddr+3];
      ENDCASE          =>  word ← DicentraInputOutput.InputByteOdd[GPIBBaseAddr+3]; 
      };
   
   WriteReg: PRIVATE INTERNAL PROC [register: WRegister, dataByte: CHAR ]  =  {
      dataWord: MACHINE DEPENDENT RECORD [a,b: CHAR];
      dataWord.b ← dataByte; dataWord.a ← '\000;
      SELECT register FROM
      cmdDataOut     =>  DicentraInputOutput.OutputByteEven[dataWord, GPIBBaseAddr]; 
      intrptMask1    =>  DicentraInputOutput.OutputByteOdd[dataWord, GPIBBaseAddr];
      intrptMask2    =>  DicentraInputOutput.OutputByteEven[dataWord, GPIBBaseAddr+1];
      serialPollMode =>  DicentraInputOutput.OutputByteOdd[dataWord, GPIBBaseAddr+1];
      addressMode    =>  DicentraInputOutput.OutputByteEven[dataWord, GPIBBaseAddr+2];  
      auxMode        =>  DicentraInputOutput.OutputByteOdd[dataWord, GPIBBaseAddr+2];   
      address        =>  DicentraInputOutput.OutputByteEven[dataWord, GPIBBaseAddr+3];
      ENDCASE        =>  DicentraInputOutput.OutputByteOdd[dataWord, GPIBBaseAddr+3]; 
      };
   
   WriteRegL: PRIVATE INTERNAL PROC [register: WRegister,  dataWord: CARDINAL ]  =  {
      SELECT register FROM
      cmdDataOut     =>  DicentraInputOutput.OutputByteEven[dataWord, GPIBBaseAddr]; 
      intrptMask1    =>  DicentraInputOutput.OutputByteOdd[dataWord, GPIBBaseAddr];
      intrptMask2    =>  DicentraInputOutput.OutputByteEven[dataWord, GPIBBaseAddr+1];
      serialPollMode =>  DicentraInputOutput.OutputByteOdd[dataWord, GPIBBaseAddr+1];
      addressMode    =>  DicentraInputOutput.OutputByteEven[dataWord, GPIBBaseAddr+2];  
      auxMode        =>  DicentraInputOutput.OutputByteOdd[dataWord, GPIBBaseAddr+2];   
      address        =>  DicentraInputOutput.OutputByteEven[dataWord, GPIBBaseAddr+3];
      ENDCASE        =>  DicentraInputOutput.OutputByteOdd[dataWord, GPIBBaseAddr+3]; 
      };
   
   ClearBuf: PRIVATE PROC = {buf.length ← 0};
   
END... of NatInstrGPIBDriver.mesa