/*
   In the following C language code, 1- wire I/O is accomplished using the
   serial port of an IBM PC or compatible. The serial port must be capable
   of a 115,200 bps data rate. Setup must be called before any of the
   touch functions to verify the existence of the specified com port and
   initialize it.
   --------------------------------------------------------------------
   The setup function makes sure that the com port number passed to it
   is from 1 to 4 and has a valid address associated with it.
 */

#include <ctype.h>
#include <stdio.h>
#include <dos.h>


typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;

static uchar com_port; /* 0x2F8 = COM2 */

static char *TouchMemStatus[5] = {
   " 0 no presence detect",
   " 1 presence pulse no alarm",
   " 2 alarm followed by presence",
   " 3 short circuit to ground",
   " 4 no com port found" };


#define FL 0
#define TR 1

uchar
Setup (uchar CmPt)
{
  uint far *ptr = (uint far *) 0x00400000;
  uint SPA;
  com_port = CmPt;
/* check to see if it is a valid com port number and address */
  SPA = *(ptr + CmPt - 1);        /* get the address */
  if (CmPt < 1 || CmPt > 4 || !SPA)
    return FL;
/* serial port initialization */
  outportb (SPA + 3, 0x83);        /* set DLAB */
  outportb (SPA, 0x01);                /* bit rate is 115200 */
  outportb (SPA + 1, 0x00);
  outportb (SPA + 3, 0x03);        /* 8 dta, 1 stp, no par */
  outportb (SPA + 1, 0x00);        /* no interrupts */
  outportb (SPA + 4, 0x03);        /* RTS and DTR on */
  printf("COM port #%d at 0x%0x\n", com_port, SPA);
  return TR;
}
/*------------------------------------------------------------------------
* Do a reset on the 1 wire port and return
* 0 no presence detect
* 1 presence pulse no alarm
* 2 alarm followed by presence
* 3 short circuit to ground
* 4 no com port found
*
* The global variable 'com_port' must be set to the com port that the
* DS9097 COM Port Adapter is attached to before calling this routine.
*
*/
uchar
TouchReset (void)
{
  uint SPA, F, X, Y, tmp, trst = 0;
  uint far *ptr = (uint far *) 0x00400000;
  ulong far *sysclk = (ulong far *) 0x0040006c;
  ulong M;
/* get the serial port address */
  SPA = *(ptr + com_port - 1);
/* return if there is no address */
  if (!SPA)
    return 4;
/* serial port initialization */
  outportb (SPA + 3, 0x83);        /* set DLAB */
  outportb (SPA, 0x01);                /* bit rate is 115200 */
  outportb (SPA + 1, 0x00);
  outportb (SPA + 3, 0x03);        /* 8 dta, 1 stp, no par */
  outportb (SPA + 1, 0x00);        /* no interrupts */
  outportb (SPA + 4, 0x03);        /* RTS and DTR on */
/* Initialize the time limit */
  M = *sysclk + 1;
/* loop to clear the buffers */
  do
    {
      tmp = inportb (SPA + 5) & 0x60;
    }
  while (tmp != 0x60);
/* flush input */
  while (inportb (SPA + 5) & 0x1)
    X = inportb (SPA);
  outportb (SPA + 3, 0x83);        /* set DLAB */
  outportb (SPA + 1, 0x00);        /* baud rate is 10473 */
  outportb (SPA, 0x0B);
  outportb (SPA + 3, 0x03);        /* 8 dta, 1 stp, no par */
  outportb (SPA, 0xF0);                /* send the reset pulse */
/* wait until character back or timeout */
  do
    {
      Y = inportb (SPA + 5);
      F = Y & 0x1;
    }
  while (!F && (*sysclk <= M));
  if (F)
    X = inportb (SPA);
  else
    return 3;
  if (X != 0xF0)                /* if more bits back than sent then there */
    {                                /* is a device if framing error or break */
      trst = TR;
      if ((Y & 0x18) != 0)
        {
          trst = 2;
/* loop to clear the buffers */
          do
            {
              tmp = inportb (SPA + 5) & 0x60;
            }
          while (tmp != 0x60);
/* wait until character back or timeout */
          do
            {
              Y = inportb (SPA + 5);
              F = Y & 0x1;
            }
          while (!F && (*sysclk <= M));
          if (F)
            X = inportb (SPA);
          else
            return 3;
        }
    }
  outportb (SPA + 3, 0x83);        /* set DLAB */
  outportb (SPA, 0x01);                /* bit rate is 115200 */
  outportb (SPA + 3, 0x03);        /* 8 dta, 1 stp, no par */
  return trst;
}
/*------------------------------------------------------------------------
* This is the 1- Wire routine 'TouchByte', sometimes called 'DataByte'.
* It transmits 8 bits onto the 1- Wire data line and receives 8 bits
* concurrently. The global variable 'com_port' must be set to the
* com port that the serial brick is attached to before calling this
* routine. This com port must also be set to 115200 baud, 8 dta, 1 stp,
* and no parity. This routine returns the uchar 8 bit value received.
* If it times out waiting for a character then 0xFF is returned.
*/
uchar
TouchByte (uchar outch)
{
  uchar inch = 0, sendbit, Mask = 1;
  uint SPA;
  uint far *ptr = (uint far *) 0x00400000;
  ulong far *sysclk = (ulong far *) 0x0040006c;
  ulong M;
/* get the serial port address */
  SPA = *(ptr + com_port - 1);
/* Initialize the time limit */
  M = *sysclk + 2;
/* wait to TBE and TSRE */
  do
    {
    }
  while ((inportb (SPA + 5) & 0x60) != 0x60);
/* flush input */
  while ((inportb (SPA + 5) & 0x1))
    inportb (SPA);
/* get first bit ready to go out */
  sendbit = (outch & 0x1) ? 0xFF : 0x00;
/* loop to send and receive 8 bits */
  do
    {
      outportb (SPA, sendbit);        /* send out the bit */
/* get next bit ready to go out */
      Mask <<= 1;
      sendbit = (outch & Mask) ? 0xFF : 0x00;
/* shift input char over ready for next bit */
      inch >>= 1;
/* loop to look for the incoming bit */
      for (;;)
        {
/* return if out of time */
          if (*sysclk > M)
            return 0xFF;
          if (inportb (SPA + 5) & 0x01)
            {
              inch |= ((inportb (SPA) & 0x01) ? 0x80 : 0x00);
              break;
            }
        }
    }
  while (Mask);
  return inch;                        /* return the input char */
}


static uchar rom[8];
void
ReadRom(void)
{
  int i;
  uchar code;

  TouchReset();
  TouchByte(0x33);
  for (i=0; i < 8; i++)
    rom[i] = TouchByte(0xFF);
}

#define DEVICE_ID (uchar)rom[0]
#define SERIAL (unsigned long )(*((unsigned long *)(rom+1)))
#define CRC (uchar)rom[7]


uchar
ReadMem(uint address)
{
   TouchReset();
   TouchByte(0xCC);
   TouchByte(0xF0);
   TouchByte(address & 0x00FF);
   TouchByte((address >> 8) & 0x00FF);
   return TouchByte(0xFF);
}


void DumpMemory(void)
{
  uint address;

  TouchReset();
  TouchByte(0xCC);
  TouchByte(0xF0);
  TouchByte(0x00);
  TouchByte(0x00);

  for (address = 0; address < 128; address++)
    {
       uchar asc[17];
       asc[16] = '\0';

       if(address % 16 == 0)
         printf("\n%04X ",address);

       printf("%02X ", (asc[address % 16] = TouchByte(0xFF)));
       if(address % 16 == 15)
         {
            int i;
            for (i = 0; i < 16; i++)
              if (!isalnum(asc[i]))
                 asc[i] = '.';

            printf("%16s",asc);
         }

    }
  printf("\n");
  TouchReset();
}

#define TB(x) printf("TouchByte(0x%02X)= 0x%02X\n", (x), TouchByte(x))

uchar WriteMem(uint address, uchar data)
{
  uchar TA1, TA2, ES;

  TA1 = (uchar)address;
  TA2 = (uchar)(address >> 8);

  /* Write to scratchpad */
  TouchReset();
  TouchByte(0xCC);
  TouchByte(0x0F);
  TouchByte(TA1);
  TouchByte(TA2);
  TouchByte(data);

  /* Read from scratchpad */
  TouchReset();
  TouchByte(0xCC);
  TouchByte(0xAA);
  if ( TouchByte(0xFF) != TA1 )
     printf("Error: Invalid LOW address return\n");
  if ( TouchByte(0xFF) != TA2 )
     printf("Error: Invalid HIGH address return\n");
  if ( (ES = TouchByte(0xFF)) & 0x40)
     printf("Error: Overflow\n");
  if ( TouchByte(0xFF) != data)
     printf("Invalid data returned\n");


  /* COPY scratchpad */
  TouchReset();
  TouchByte(0xCC);
  TouchByte(0x55);
  TouchByte(TA1);
  TouchByte(TA2);
  TouchByte(ES);

  return 0;
}


int
main()
{
  uchar status;
  uint address;
  uchar data;
  uint single;
  uint portnum = 2;
  char filename[150];
  FILE *fp;


  Setup(portnum); /* COM port number */

  status = TouchReset();

  printf("TouchReset: %s\n", TouchMemStatus[status]);

  ReadRom();

  printf("Device ID: %d, Serial number: %08lX\, CRC: 0x%0X\n",
        DEVICE_ID, SERIAL, CRC);

  for (;;)
   {
     fflush(stdin);
     printf("1:Dump  2:Single 3:Polute 4:Load 5:Save mem. 6:Port num 7:Exit >>>");
     switch(getchar())
      {
        case '1':
            ReadRom();

            printf("Device ID: %d, Serial number: %08lX\, CRC: 0x%0X\n",
                        DEVICE_ID, SERIAL, CRC);
            DumpMemory();
            break;

        case '2':
            printf("Address (hex):"); scanf("%x", &single);
            printf("Data    (hex):"); scanf("%x", &data);
            printf("Writting : (%04x) <-  %02x\n", single, data);

            WriteMem(single, data);
            break;

        case '3':
            printf("Data for polution (hex):"); scanf("%X", &data);
            for(address = 0 ; address < 128; address++)
                WriteMem(address, data);
            break;

        case '4':
            printf("Filename to read:"); scanf("%s", filename);
            fp = fopen(filename, "r");
            if (fp == NULL)
              {
                 perror(filename);
                 break;
              }
            for (address = 0; address < 128; address++)
              {
                 fscanf(fp, "%x", &data);
                 printf("%02x%c", data,
                            address % 16 == 15 ? '\n' : ' ');

                 WriteMem(address, data);
              }
            fclose(fp);
            break;

        case '5':
            printf("Filename to write:"); scanf("%s", filename);
            fp = fopen(filename, "w");
            if (fp == NULL)
              {
                 perror(filename);
                 break;
              }
            for (address = 0; address < 128; address++)
              {
                 data = ReadMem(address);
                 fprintf(fp, "%02x%c", data,
                            address % 16 == 15 ? '\n' : ' ');
              }
            fclose(fp);
            break;
        case '6':
                printf("COM port number (1,2):");
                scanf("%d", &portnum);
                Setup(portnum); /* COM port number */
                  status = TouchReset();
                  printf("TouchReset: %s\n", TouchMemStatus[status]);
                break;
        case '7':
            return 0;
        default:
            printf("Input error\n");
      }
   }

}