


MODULE LAB;
{$M AD1_CONVERT }
{$M AD2_CONVERT }
{$M DA_CONVERT  }
{$M TIMER       }
{$M TIME_OUT    }
{$M READ_CLOCK  }
{$M PRINT_TIME  }
{$M CONTROL     }
{$M HYDROGEN    }
{$M OXYGEN      }
{$M AIR         }
{$M STRIP_CHART }       
{$M LOGIN_DISK  }

{$M *}

TYPE         AD1_CHAN = 0..7;
             AD2_CHAN = 0..15;
              DA_CHAN = 0..3;
               NIBBLE = 0..15;
          DEVICE_CODE = RECORD
                          HOUSE : 'A'..'P';
                           UNIT : 0..15;
                        END;
       CONTROL_ACTION = (ON,OFF,ALL_ON,ALL_OFF,BRIGHTEN,DIM,CLR_STOP);

          TIME_OF_DAY = RECORD
                            YEAR : 1982..2000;
                           MONTH : 1..12;
                             DAY : 1..31;
                            HOUR : 0..23;
                          MINUTE : 0..59;
                          SECOND : 0..59;
                         END;

            FILE_NAME = STRING;



EXTERNAL FUNCTION @BDOS(FUNC,PARM : INTEGER) : INTEGER;
 
FUNCTION AD1_CONVERT(CHANNEL : AD1_CHAN; N : NIBBLE) : INTEGER;
   {Access the CDC 12-bit A/D converter board set at +/-5 volts}
   CONST  AD1_PORT = $A0;
              MASK = $20;
   BEGIN
   {GAIN = 2**N.  However we must adjust N so that the proper gain}
   {switching is inputted to the programmable gain amplifier.  See}
   {p. 7 of the 3606 IC manual.   }
   CASE N OF
      0,  1,  2 : N:=N;
          3,  4 : N:=N+2;
      5,  6,  7 : N:=N+3;
      8,  9, 10 : N:=N+4;
      ELSE BEGIN
         WRITELN('Gain of 1024 (2**10) is a maximum bound.');
         N:=14
         END
   END;

   OUT[ AD1_PORT   ]:=N;             {input gain switch}
   OUT[(AD1_PORT+1)]:=CHANNEL;
   OUT[(AD1_PORT+2)]:=0;             {start A/D conversion}

   WAIT(AD1_PORT,MASK,TRUE);         {wait until the 5th bit goes low}

   AD1_CONVERT:=SWAP(INP[(AD1_PORT+3)]) ! INP[(AD1_PORT+2)]
   END;



FUNCTION AD2_CONVERT(CHANNEL : AD2_CHAN) : INTEGER;
   {Access the Techmar 12-bit A/D converter board set at +/-10 volts}
   CONST  AD2_PORT = $B0;
              MASK = $01;
   BEGIN

   OUT[ AD2_PORT   ]:=CHANNEL;
   OUT[(AD2_PORT+1)]:=0;             {start A/D conversion}

   WAIT(AD2_PORT+1,MASK,FALSE);      {wait until the LSB bit goes high}

   AD2_CONVERT:=SWAP(INP[(AD2_PORT+3)]) ! INP[(AD2_PORT+2)]
   END;



PROCEDURE DA_CONVERT(CHANNEL : DA_CHAN; DA_OUTPUT : INTEGER);
   {Access the CDC 12-bit D/A converter board set at +/-10 volts}
   CONST   DA_PORT = $C0;

   VAR   DAC_PORT : BYTE;

   BEGIN
   IF ( CHANNEL>3 ) OR ( CHANNEL<0 ) THEN
      WRITELN('D/A CHANNEL',CHANNEL,' does not exist.');
   DAC_PORT:=DA_PORT + 2*CHANNEL;
   OUT[(DAC_PORT    )]:=HI(DA_OUTPUT);
   OUT[(DAC_PORT + 1)]:=LO(DA_OUTPUT)
   END;



PROCEDURE TIMER(SECONDS : INTEGER);
   {Access the SciTronics RTC-100 Real Time Clock board}
   {and wait a specified number of seconds.            }

   VAR         I : INTEGER;
             NOW : TIME_OF_DAY;
         OLD_SEC : INTEGER;

   BEGIN
   READ_CLOCK(NOW);
   FOR I:=SECONDS DOWNTO 1 DO
      BEGIN
      OLD_SEC:=NOW.SECOND;
      REPEAT
         READ_CLOCK(NOW)
      UNTIL NOW.SECOND<>OLD_SEC
      END
   END;


PROCEDURE TIME_OUT(HOURS,MINUTES,SECONDS : INTEGER);
   {Access the RTC-100 Real Time Clock board and wait a specified}
   {block of time (accurate only to the nearest second).         }

   CONST    RTC0 = $18;
            RTC1 = $19;
            RTC2 = $1A;
            RTC3 = $1B;

   VAR            I : INTEGER;
                NOW : TIME_OF_DAY;
             TARGET : TIME_OF_DAY;
        SUM_SECONDS : INTEGER;
        SUM_MINUTES : INTEGER;
          SUM_HOURS : INTEGER;
       MINUTE_CARRY : INTEGER;
         HOUR_CARRY : INTEGER;
              READY : BOOLEAN;

   BEGIN
   READ_CLOCK(NOW);
   READY:=FALSE;

   SUM_SECONDS  := NOW.SECOND + SECONDS;

   MINUTE_CARRY := SUM_SECONDS DIV 60;
   SUM_MINUTES  := NOW.MINUTE + MINUTES + MINUTE_CARRY;

   HOUR_CARRY   := SUM_MINUTES DIV 60;
   SUM_HOURS    := NOW.HOUR + HOURS + HOUR_CARRY;

   TARGET.SECOND:= SUM_SECONDS MOD 60;
   TARGET.MINUTE:= SUM_MINUTES MOD 60;
   TARGET.HOUR  := SUM_HOURS   MOD 24;

   REPEAT
      READ_CLOCK(NOW);
      IF NOW.HOUR=TARGET.HOUR THEN
         IF NOW.MINUTE=TARGET.MINUTE THEN
            READY:=TARGET.SECOND=NOW.SECOND
   UNTIL READY
   END;



PROCEDURE READ_CLOCK(VAR NOW : TIME_OF_DAY);
   {Read the RTC-100 Real Time Clock}
   VAR  TIME : ARRAY[0..12] OF BYTE;

   PROCEDURE RTC_READ;
      CONST    RTC0 = $18;
               RTC1 = $19;
               RTC2 = $1A;
               RTC3 = $1B;

      VAR    I : INTEGER;

      BEGIN
      OUT[RTC1]:=$F0;
      OUT[RTC0]:=$0F;
      OUT[RTC3]:=$FC;
      OUT[RTC1]:=$F4;

      FOR I:=0 TO 12 DO
         BEGIN
         OUT[RTC0]:=I;
         TIME[I]:=SHR( INP[RTC0], 4)
         END;

      OUT[RTC1]:=$F8;
      OUT[RTC0]:=$0F;
      OUT[RTC3]:=$F8;
      OUT[RTC1]:=$FC;
      OUT[RTC0]:=$0F
      END;

   BEGIN
   RTC_READ;
   WITH NOW DO
      BEGIN
      YEAR:=TIME[12]*10 + TIME[11] + 1900;
      MONTH:=(TIME[10] & 3)*10 + TIME[9];
      DAY:=TIME[8]*10 + TIME[7];
      HOUR:=(TIME[5] & 3)*10 + TIME[4];
      MINUTE:=TIME[3]*10 + TIME[2];
      SECOND:=TIME[1]*10 + TIME[0]
      END
   END;




PROCEDURE PRINT_TIME(F_NAME : FILE_NAME);
   VAR   TIME : TIME_OF_DAY;

   BEGIN
   READ_CLOCK(TIME);
   WITH TIME DO
      IF F_NAME='SCREEN' THEN
         WRITE('Time is:  ',HOUR:2,':',MINUTE:2,':',SECOND:2)
      ELSE WRITE(F_NAME, HOUR:2,':',MINUTE:2,':',SECOND:2)
   END;




PROCEDURE CONTROL(TRIAC : DEVICE_CODE; COMMAND : CONTROL_ACTION);
   {Works in conjunction with the SciTronics Remote Controller  }
   {board.  This procedure accesses and performs the appropriate}
   {command for a BSR control module.  It takes approximately   }
   {3 seconds to execute.                                       }

   CONST     RC_PORT = $40;
                MASK = $80;

   VAR     STR : STRING;
             I : INTEGER;

   BEGIN
   WITH TRIAC DO
      BEGIN
      STR:='JBHPLDFNIAGOKCEM';
      {output the device code --> see p.8 of RC-100 manual}
      OUT[RC_PORT]:=SHL(UNIT,4)!( POS(HOUSE,STR) - 1 );
      CASE COMMAND OF

                ON : OUT[(RC_PORT + 1)]:=$93;

               OFF : OUT[(RC_PORT + 1)]:=$9B;

            ALL_ON : OUT[(RC_PORT + 1)]:=$95;

           ALL_OFF : OUT[(RC_PORT + 1)]:=$9D;

          BRIGHTEN : OUT[(RC_PORT + 1)]:=$C7;

               DIM : OUT[(RC_PORT + 1)]:=$CF;

          CLR_STOP : OUT[(RC_PORT + 1)]:=$A1

          END;

 
      FOR I:=1 TO 100 DO;  {a short delay before reading status}
 
      {wait until controller has stopped transmitting}
      WAIT(RC_PORT,MASK,TRUE)
      END
   END;



FUNCTION KEYPRESSED : BOOLEAN;
   BEGIN
   KEYPRESSED:=( @BDOS(11,0) <> 0 )
   END;



PROCEDURE HYDROGEN(FLOWRATE : INTEGER);
   {This procedure activates the appropriate solenoid valve to   }
   {the gas regulator and inputs a voltage to the flow controller}
   {which will correspond to the given flowrate requested.       }

   CONST    H2_FLOW_CONTROLLER = 3;     {D/A channel 3}
                      MAX_FLOW = 300;   {cm**3/minute}
                      MIN_FLOW =  13;

   VAR     H2_VALVE : DEVICE_CODE;
             FLOW_I : INTEGER;
   BEGIN
   H2_VALVE.HOUSE:='P';      {If using BSR appliance}
   H2_VALVE.UNIT:=2;         {modules, dial in "P3" }

   IF FLOWRATE<=0 THEN
      CONTROL(H2_VALVE,OFF)
   ELSE IF FLOWRATE<=MAX_FLOW THEN
      BEGIN
      CONTROL(H2_VALVE,ON);
      {Generate a positve DAC voltage <= 5.0 volts for proper flowrate}
      FLOW_I:=ROUND(1024.0*FLOWRATE/MAX_FLOW);
      DA_CONVERT(H2_FLOW_CONTROLLER,FLOW_I)
      END
   ELSE WRITELN('Error  *** Hydrogen flowrate exceeds controller max.')

   END;



PROCEDURE OXYGEN(FLOWRATE : INTEGER);
   {This procedure activates the appropriate solenoid valve to   }
   {the gas regulator and inputs a voltage to the flow controller}
   {which will correspond to the given flowrate requested.       }

   CONST    O2_FLOW_CONTROLLER = 2;     {D/A channel 2}
                      MAX_FLOW = 2000;  {cm**3/minute}
                      MIN_FLOW =    0;

   VAR     O2_VALVE : DEVICE_CODE;
             FLOW_I : INTEGER;
   BEGIN
   O2_VALVE.HOUSE:='P';      {If using BSR appliance}
   O2_VALVE.UNIT:=3;         {modules, dial in "P4" }

   IF FLOWRATE<=0 THEN
      CONTROL(O2_VALVE,OFF)
   ELSE IF FLOWRATE<=MAX_FLOW THEN
      BEGIN
      CONTROL(O2_VALVE,ON);
      {Generate a positve DAC voltage <= 5.0 volts for proper flowrate}
      FLOW_I:=ROUND(1024.0*FLOWRATE/MAX_FLOW);
      DA_CONVERT(O2_FLOW_CONTROLLER,FLOW_I)
      END
   ELSE WRITELN('Error  *** Oxygen flowrate exceeds controller max.')

   END;


PROCEDURE AIR(FLOWRATE : INTEGER);
   {This is used when a 21% oxygen/79% nitrogen mixture flows}
   {through the flow controller calibrated for pure oxygen.  }
   BEGIN
   OXYGEN( ROUND(1.00*FLOWRATE) )    {Correction factor given}
   END;                              {in Tylan manual, p. 19 }



PROCEDURE STRIP_CHART(ACTION : CONTROL_ACTION);
   {This procedure turns the Houston Instrument strip chart recorder}
   {on or off.  No pen control is needed since it automatically goes}
   {up when power is curtailed.                                     }

   VAR    CHART : DEVICE_CODE;
   BEGIN
   CHART.HOUSE:='P';         {If using BSR appliance}
   CHART.UNIT:=4;            {modules, dial in "P5" }

   CASE ACTION OF
      ON,OFF : CONTROL(CHART,ACTION);
      ELSE BEGIN
         WRITELN('***> Control action requested is not available');
         WRITELN('      for the strip chart recorder.')
         END
      END
   END;


PROCEDURE LOGIN_DISK(DISK : CHAR);
   {logs in the given floppy disk drive (A,B,C,...)}
   CONST  DISK_DRIVE_LOGIN = 14;
   
   VAR   DUMMY : INTEGER;
   BEGIN
   DUMMY:=@BDOS( DISK_DRIVE_LOGIN, ORD(DISK)-ORD('A') )
   END;
   


MODEND.
