/* LoadMeterIO.c
   Stewart, January 12, 1984  2:29 PM, created
 */

#include	"LoadMeter.h"

#define Disarm 0x00c0
#define SetLow 0x00e0
#define SetHigh 0x00e8
#define SetPointer 0x0000
#define Load 0x0040
#define Arm 0x0020
#define Save 0x00a0
#define SetPointerHold 0x0010

/* external variables */
extern int IPIO[2];
extern int ITimer[2];

/* local variables */

int rtc;
int oldClock;
int saved[3];

/* IO routines */

InitLMIO()
  {
  PortStr(IPIO);
  PortStr(ITimer);
  saved[0] = saved[1] = saved[2] = 0;
  };

SetLeds(table, mode)
  int table[], mode;
  {
  Bit(table[0], false);
  Bit(table[1], false);
  Bit(table[2], false);
  Bit(table[3], false);
  Bit(table[mode], true);
  };

Bit(id, on)
  int id, on;
  {
  int port, bp;
  bp = id >> 8;
  switch (bp) {
    case 0: port = lmpioa; break;
    case 1: port = lmpiob; break;
    case 2: port = lmpioc; break;
    default: return;
    };
  id &= 0x00ff;
  if (on) saved[bp] |= id;
  else saved[bp] &= (id ↑ 0x00ff);
  OutByte(port, saved[bp]);
  };

/* switch is a switch id from LoadMeter.h */
int GetSwitch(swi)
  int swi;
  {
  swi = (InByte(lmpiob) >> swi) & 3;
  return((swi == 1) ? true : false);
  };

int GetClock()
  {
  return(GetCounter(5));
  };

int GetPPS()
  {
  return(GetCounter(3));
  };

int GetBPS()
  {
  return(GetCounter(4));
  };

int GetCounter(counter)
  int counter;
  {
  /* save the count value in the hold register
     set the data pointer to the particular hold register
     read the low and high bytes of the hold register
   */
  OutByte(lmTimCtl, Save + (1 << (counter - 1)));
  OutByte(lmTimCtl, SetPointerHold + counter);
  counter = InByte(lmTimData) & 0x00ff;
  counter |= InByte(lmTimData) << 8;
  return(counter);
  };

/*  meter is either:
     meterBPS:  The bits/second meter
     meterPPS:  The packets/second meter

    actually, meter is the timer channel, IN[1..5] !

    value is IN [0..1000] which is 0 to 100% in .1 % steps
 */
SetMeter(meter, value)
int meter, value;
  {
  int meterBit;
  if (value > 1000) return;
  if (value < 0) return;
  if (meter > 5) return;
  if (meter < 1) return;
  meterBit = (1 << (meter - 1));
  if (value == 0) {
    /* disarm timer and set the output low */
    OutByte(lmTimCtl, Disarm + meterBit);
    OutByte(lmTimCtl, SetLow + meter);
    return;
    };
  if (value == 1000) {
    /* disarm timer and set the output high */
    OutByte(lmTimCtl, Disarm + meterBit);
    OutByte(lmTimCtl, SetHigh + meter);
    return;
    };
  /* the input to the timer channel is 5 MHz, the output
     will be 5 KHz.  The time low and the time high should
     add up to 1000.  value is the time high.
   */
  OutByte(lmTimCtl, Disarm + meterBit);  /* disarm */
  OutByte(lmTimCtl, SetLow + meter);  /* set output low */
  OutByte(lmTimCtl, SetPointer + meter);  /* set data pointer to counter group for meter */
  OutByte(lmTimData, 0x0062);  /* mode J, toggle out */
  OutByte(lmTimData, 0x001b);  /* clock F1 */
  OutByte(lmTimData, (1000 - value) & 0x00ff);  /* low lsb */
  OutByte(lmTimData, (1000 - value) >> 8);  /* low msb */
  OutByte(lmTimData, value & 0x00ff);  /* high lsb */
  OutByte(lmTimData, value >> 8);  /* high msb */
  OutByte(lmTimCtl, Load + meterBit);  /* load */
  OutByte(lmTimCtl, Arm + meterBit);  /* arm */
  };

PollClock()
  {
  int clock;
  clock = GetClock();
  rtc += UpdateModFK(oldClock, clock);
  oldClock = clock;
  };

/* Utilities */

/* set the timer to expire at time now plus interval */
STimer(interval, timer)
  int interval, *timer;
  {
  PollClock();
  *timer = rtc;
  *timer += interval;
  };

/* returns boolean if timer has expired */
int Expired(timer)
  int *timer;
  {
  PollClock();
  if ((rtc - *timer) < 0) return(false);
  return(true);
  };

/* add a certain amount to the timer */
AddTimer(increment, timer)
  int increment, *timer;
  {
  *timer += increment;
  };

int UMin(a, b)
  int a, b;
  {
  return((Ugt(a, b)) ? b : a);
  };

int UpdateModFK(old, new)
  int old, new;
  {
  int inc;
  if (new > old) inc = old + (0x4000 - new);
  else inc = old - new;
  return(inc);
  };

int ModN(n, lim)
  int n, lim;
  {
  if ((n < 0) || (n >= lim)) return(0);
  n += 1;
  if (n == lim) return(0);
  return(n);
  };

CycleFive(modep, boolp)
  int *modep, *boolp;
  {
  if (*boolp) {
    *boolp = false;
    *modep = 0;
    };
  else {
    if (*modep == 3) *boolp = true;
    else *modep = ModN(*modep, 4);
    };
  };

int Clip(low, val, high)
int low, val, high;
  {
  if (Ugt(low, val)) return(low);
  if (Ugt(val, high)) return(high);
  return(val);
  };