/*
I2C DLL device driver
*/

#include <windows.h>
#include <conio.h>

int far *PPort;
unsigned short PPA;;
char    Pin13;

#define HIGH			1
#define LOW                     0
#define USECWAIT		9

#define DllExport		_export FAR PASCAL

void WaitCounts(unsigned short);
void CheckPin13();
void PowerOn(void);
void SDA(int);
void SCL(int);
int SenseSDA(void);
int Start(void);
int Stop(void);
unsigned char rotateleft(unsigned char);
unsigned char WriteByte(unsigned char);
unsigned char ReadByte(int);
int DllExport BlockIO(int,unsigned char,unsigned char far *,int,int);
int DllExport LibMain (HINSTANCE,WORD,WORD,LPSTR);

void WaitCounts(unsigned short wait_count)
{
  unsigned short icount,ccount;

  outp(0x43,0);
  icount = inp(0x40) | (inp(0x40) << 8);

  do
  {
    outp(0x43,0);
    ccount = inp(0x40) | (inp(0x40) << 8);

    if (ccount > icount)
      icount = ccount;
  }
  while (((icount - ccount) & 0xFFFF) < wait_count);
}

void CheckPin13(void)
{
  /* Turn on pin 17. */
  outp(PPA+2,inp(PPA+2) & 0xF7);
  /* Check Pin 13 */
  if ((inp(PPA+1) & 0x10) > 0)
  {
    /* Turn off pin 17. */
    outp(PPA+2,inp(PPA+2) | 0x08);
    if ((inp(PPA+1) & 0x10) == 0)
      Pin13 = 1;
    else
      Pin13 = 0;
  }
  else
    Pin13 = 0;
}

void PowerOn(void)
{
  /* Power is on pin 4 of the DB25 connector */
  outp(PPA,inp(PPA) | 0x04);
  /* Turn on pin 17 also. */
  outp(PPA+2,inp(PPA+2) & 0xF7);
  /* Turn on pin 5 also. */
  outp(PPA,inp(PPA) | 0x80);
}

void SDA(int level)
{
  /* Toggles pin 17 on a DB25 high or low,
     pin 36 on centronics end, inverted bit 3
     in register offset 2 from base.
  */
  if (level == HIGH)
  {
    outp(PPA+2,inp(PPA+2) & 0xF7);
  }
  else
  {
    outp(PPA+2,inp(PPA+2) | 0x08);
  }
}

void SCL(int level)
{
  /* Toggles pin 5 on a DB25 high or low,
     pin 5 on centronics end, inverted bit 3
     in register offset 0 from base.
  */
  if (level == HIGH)
  {
    outp(PPA,inp(PPA) | 0x08);
  }
  else
  {
    outp(PPA,inp(PPA) & 0xF7);
  }
}

/* Depending on the value of Pin13, this function 
   returns the value of pin 13, or pin 17 on DB25. */
int SenseSDA(void)
{
  if (Pin13 == 1)
  {
    /* Check pin 13 for level */
    if ((inp(PPA+1) & 0x10) > 0)
      return 1;
    else
      return 0;
  }
  else
  {
    /* Check pin 17 for level */
    if ((inp(PPA+2) & 0x08) > 0)
      return 0;
    else
      return 1;
  }
}

/* Starts I2C serial transfer */
int Start(void)
{
  SDA(HIGH);
  WaitCounts(USECWAIT*3);
  SCL(HIGH);
  WaitCounts(USECWAIT*3);
  SDA(LOW);
  WaitCounts(USECWAIT*3);
  SCL(LOW);
  WaitCounts(USECWAIT*3);
  return(1);
}

/* Stops I2C serial transfer */
int Stop(void)
{
  SDA(HIGH);
  WaitCounts(USECWAIT*3);
  SCL(HIGH);
  WaitCounts(USECWAIT*3);
  SDA(LOW);
  WaitCounts(USECWAIT*3);
  SDA(HIGH);
  WaitCounts(USECWAIT*3);
  return(1);
}

unsigned char rotateleft(unsigned char inchar)
{
  if (inchar & 0x80)
  {
    inchar <<= 1;
    inchar |= 0x01;
  }
  else
  {
    inchar <<= 1;
    inchar &= 0xFE;
  }
  return (inchar);
}

unsigned char WriteByte(unsigned char outchar)
{
  int i;
  int sense;

  /* Write 8 bits */
  for (i = 0;i < 8;i++)
  {
    if (outchar & 0x80)
      SDA(HIGH);
    else
      SDA(LOW);
    WaitCounts(3);
    SCL(HIGH);
    WaitCounts(USECWAIT*3);
    SCL(LOW);
    WaitCounts(USECWAIT*3);
    outchar = rotateleft(outchar);
  }
  /* Get ACK bit */
  SDA(HIGH);
  WaitCounts(3);
  SCL(HIGH);
  WaitCounts(USECWAIT*3);
  sense = SenseSDA();
  SCL(LOW);
  WaitCounts(USECWAIT*3);
  /* If the line was pulled low, the guy on the other end
     was listening.  Return true if we get it. */
  if (sense == 0)
    return(1);
  else
    return(0);
}

unsigned char ReadByte(int islastbyte)
{
  int i;
  unsigned char inchar = 0x00;
  int sense;

  SDA(HIGH);
  WaitCounts(3);
  /* Read 8 bits */
  for (i = 0;i < 8;i++)
  {
    SCL(HIGH);
    WaitCounts(USECWAIT*3);
    inchar = rotateleft(inchar);
    sense = SenseSDA();
    if (sense == 1)
      inchar |= 0x01;
    else
      inchar &= 0xFE;
    SCL(LOW);
    WaitCounts(USECWAIT*3);
  }
  /* Acknowledge sender if this is not the last byte. */
  if (islastbyte)
    SDA(HIGH);
  else
    SDA(LOW);
  WaitCounts(3);
  SCL(HIGH);
  WaitCounts(USECWAIT*3);
  SCL(LOW);
  WaitCounts(USECWAIT*3);
  return(inchar);
}

int DllExport BlockIO(int portnum,unsigned char address, \
				unsigned char far *bytearray,int length,int stop)
{
  int i;

  PPA = PPort[portnum];
  CheckPin13();
  PowerOn();
  WaitCounts(USECWAIT*3);
  Start();
  if (!WriteByte(address))
  {
    Stop();
    return(0);
  }
  for (i = 0;i < length;i++)
  {
    if (address & 0x01)
    {
      if (i == (length - 1))
        bytearray[i] = ReadByte(1);  /* This is the last byte. */
      else
        bytearray[i] = ReadByte(0);  /* This is not the last byte. */
    }
    else
    {
      if (!WriteByte(bytearray[i]))
      {
        Stop();
        return(0);
      }
    }
  }
/* Take the following statement out if we can do stops all the time */
/*  if (stop == 1)*/
  Stop();
  WaitCounts(USECWAIT*3);

  return(1);
}

int DllExport LibMain (HINSTANCE hInstance, WORD wDataSeg, WORD cbHeapSize, LPSTR lpCmdLine)
{
  PPort = (int far *)0x00400008;
  /* Default LPT1 */
  PPA = 0;
  /* Default Pin 13 not used. */
  Pin13 = 0;
  return(1);
}

