
static char rcsid[] = "$Id: lcd.c,v 1.14 2001/10/12 13:51:15 root Exp root $";

/*
 * $Log: lcd.c,v $
 * Revision 1.14  2001/10/12 13:51:15  root
 * #
 *
 * Revision 1.13  2001/01/30 06:16:24  root
 * #
 *
 * Revision 1.12  2000/12/01 04:42:12  root
 * #
 *
 * Revision 1.11  2000/12/01 04:13:46  root
 * #
 *
 * Revision 1.10  2000/11/23 23:45:23  root
 * #
 *
 * Revision 1.9  2000/10/07 18:30:06  root
 * #
 *
 * Revision 1.8  2000/10/06 19:52:19  root
 * #
 *
 * Revision 1.7  2000/09/24 01:48:13  root
 * #
 *
 * Revision 1.6  2000/09/24 01:46:05  root
 * #
 *
 * Revision 1.5  2000/09/24 01:12:42  root
 * #
 *
 * Revision 1.4  1999/10/26 17:46:46  root
 * #
 *
 * Revision 1.3  1999/10/26 17:46:41  root
 * #
 *
 * Revision 1.2  1999/09/21 23:01:38  root
 * #
 *
 * Revision 1.1  1999/09/21 22:56:11  root
 * Initial revision
 *
 */

#include <stdio.h>
#include <malloc.h>
#include <sys/io.h>
#include <strings.h>
#include <stdarg.h>

#include "lcd.h"

#define DATA(l) ((l)->base)
#define STAT(l) (((l)->base)+1)
#define CTRL(l) (((l)->base)+2)

void
jmp ()
{
}

void
OUT (unsigned char d, int p)
{
  outb (d, p);
  jmp ();
}

unsigned char
IN (p)
{
  unsigned char r;
  r = inb (p);
  jmp ();
  return (r);
}

#if 1
static unsigned char binret[80];

unsigned char *
bin (int v)
{
  unsigned char *ptr = binret;
  int a = 0x80;

  v ^= 0x8f;

  while (a)
    {
      if (a & v)
        *(ptr++) = '1';
      else
        *(ptr++) = '0';
      a >>= 1;
    }
  *(ptr++) = 0;

  return (binret);
}

int
lcd_mapbut (unsigned char a)
{
  if (a == LCD_BUTS_BUT1)
    return (1);
  if (a == LCD_BUTS_BUT2)
    return (2);
  if (a == LCD_BUTS_BUT3)
    return (3);
  return (0);
}


int
lcd_readbuts_worker (Lcd l)
{
  int i;
  unsigned char b1, b2;
  OUT (LCD_EN | LCD_BUTS, CTRL (l));
  usleep (200000);
  b1 = (IN (STAT (l)) & LCD_BUTS_MASK) ^ 0x80;
  OUT (LCD_EN, CTRL (l));
  usleep (200000);
  b2 = (IN (STAT (l)) & LCD_BUTS_MASK) ^ 0x80;

  if (b2)
    {
      if (b1 == b2)
        {
          return (lcd_mapbut (b1));
        }
      else if (b1 == 0)
        {
          i = lcd_mapbut (b2);
          if (i)
            i += 3;
          return (i);
        }
      else
        {
          return (0);
        }
    }

  return (0);
}

#define DEB 10
int
lcd_readbuts (Lcd l)
{
  int rs[DEB];
  long int i;
  int df = 0;

  bzero (rs, sizeof (rs));
  do
    {
      df = 0;

      for (i = 1; i < DEB; ++i)
        {
          if (rs[i - 1] != rs[i])
            df++;
          rs[i - 1] = rs[i];
        }
      rs[DEB - 1] = lcd_readbuts_worker (l);
      if (rs[DEB - 1] != rs[0])
        df++;
    }
  while (df);


  return (rs[DEB - 1]);
}

int
lcd_wait_for_but (Lcd l)
{
  int r;
  while (lcd_readbuts (l));
  while (!(r = lcd_readbuts (l)));
  return (r);

}
#endif

#define MAGIC_WRAP 80


static void lcd_cds (Lcd l, int scroll, int dir);
void lcd_pos (Lcd l, int x, int y);

static void
do_dd_write (Lcd l)
{
  int i;

  if (!l->ddmem)
    return;

  l->spos += MAGIC_WRAP + l->sinc;
  l->mpos += MAGIC_WRAP + l->minc;
  l->spos %= MAGIC_WRAP;
  l->mpos %= MAGIC_WRAP;

  i = l->spos % l->bpl;

  if (i < l->w)
    return;
  if (l->ctg)
    {
      if (i < (l->bpl - 1))
        {
          l->spos += MAGIC_WRAP + l->bpl - l->w;
          l->mpos += MAGIC_WRAP + l->bpl - l->w;
        }
      else
        {
          l->spos -= MAGIC_WRAP + l->bpl - l->w;
          l->mpos -= MAGIC_WRAP + l->bpl - l->w;
        }
      l->spos %= MAGIC_WRAP;
      l->mpos %= MAGIC_WRAP;

      lcd_pos (l, l->mpos, 0);
      return;
    }

  if (i < 20)
    {
      lcd_cds (l, 1, 0);
    }
  else
    {
      lcd_cds (l, 1, 1);
    }

}


void
lcd_wr_blind (Lcd l, unsigned char d, int a)
{
  unsigned char r = LCD_RW;
  OUT (d, DATA (l));
  if (a)
    r |= LCD_RS;
  OUT (r | LCD_EN, CTRL (l));
  OUT (r, CTRL (l));
  OUT (r | LCD_EN, CTRL (l));

  if (a == 1)
    do_dd_write (l);
  usleep (10000);
}

void
delay (void)
{
  int i;
  for (i = 0; i < 100; ++i)
    i = i;
}

void
wait_busy (Lcd l)
{
  unsigned char d = 0;

  if (!l->epp)
    {
      usleep (40);
      return;
    }

  OUT (LCD_READEN | LCD_EN, CTRL (l)); /*Turn drivers off, read input */
  do
    {
      if (d)
        delay ();
      OUT (LCD_READEN, CTRL (l));
      d = IN (DATA (l));
      OUT (LCD_READEN | LCD_EN, CTRL (l));
    }
  while (d & LCD_STAT_BUSY);
  OUT (LCD_EN, CTRL (l));       /*Turn drivers back on */
}

void
lcd_wr (Lcd l, unsigned char d, int a)
{
  unsigned char r = LCD_RW;
  if (a)
    r |= LCD_RS;
  OUT (d, DATA (l));
  OUT (r | LCD_EN, CTRL (l));
  OUT (r, CTRL (l));
  OUT (r | LCD_EN, CTRL (l));

  if (a == 1)
    do_dd_write (l);
  wait_busy (l);
}

void
lcd_wr_buf (Lcd l, unsigned char *d, int len)
{
  while (len--)
    {
      OUT (*(d++), DATA (l));
      OUT (LCD_RW | LCD_RS | LCD_EN, CTRL (l));
      OUT (LCD_RW | LCD_RS, CTRL (l));
      OUT (LCD_RW | LCD_RS | LCD_EN, CTRL (l));
      wait_busy (l);
      do_dd_write (l);
    }

}

void
lcd_wr_str (Lcd l, unsigned char *d)
{
  while (*d)
    {
      OUT (*(d++), DATA (l));
      OUT (LCD_RW | LCD_RS | LCD_EN, CTRL (l));
      OUT (LCD_RW | LCD_RS, CTRL (l));
      OUT (LCD_RW | LCD_RS | LCD_EN, CTRL (l));
      wait_busy (l);
      do_dd_write (l);
    }
}

unsigned char
init_byte (Lcd l)
{
  unsigned char init = LCD_FNSET | LCD_FNSET_8BIT;

  if (l->font == LCD_FONT_10)
    init |= LCD_FNSET_10SCANS;
  if (l->h > 1)
    init |= LCD_FNSET_2LINES;

  return (init);
}

void
lcd_pos (Lcd l, int x, int y)
{
  int p = x + (y * l->bpl);
  unsigned char c = p & LCD_DDPOS_MASK;

  l->mpos = p;
  l->ddmem = 1;

  lcd_wr (l, c | LCD_DDPOS, 0);
}

void
lcd_cls (Lcd l)
{
  lcd_wr (l, LCD_CLS, 0);
  lcd_wr (l, LCD_HOME, 0);
  lcd_pos (l, 0, 0);
  l->mpos = 0;
  l->spos = 0;
}

void
lcd_emode_ctl (Lcd l, int dir, int follow)
{
  unsigned char r = LCD_EMOD;

  if (dir > 0)
    {
      r |= LCD_EMOD_DIR;
      l->minc = 1;
    }
  else
    {
      l->minc = -1;
    }

  if (follow)
    {
      if (l->ctg)
        fprintf (stderr,
                 "lcd_curs_ctl called w/ follow=1 for not ctg display\n");
      r |= LCD_EMOD_SCROLL;
      l->sinc = 0;
    }
  else
    {
      l->sinc = l->minc;
    }

}

static void
lcd_cds (Lcd l, int scroll, int dir)
{
  unsigned char r = LCD_CSFT;
  if (scroll)
    r |= LCD_CSFT_SCROLL;
  if (dir > 0)
    {
      r |= LCD_CSFT_DIR;
      l->spos += MAGIC_WRAP - 1;
      l->spos %= MAGIC_WRAP;
    }
  else
    {
      l->spos++;
      l->spos %= MAGIC_WRAP;
    }

  lcd_wr (l, r, 0);
}

void
lcd_dpy_ctrl (Lcd l, int onoff, int curs, int blink)
{
  unsigned char r = LCD_DOOC;

  if (onoff)
    r |= LCD_DOOC_DISPEN;
  if (curs)
    r |= LCD_DOOC_CURSEN;
  if (blink)
    r |= LCD_DOOC_BLINK;

  lcd_wr (l, r, 0);

}

void
lcd_reset (Lcd l)
{

  unsigned char init = init_byte (l);

  lcd_wr_blind (l, init, 0);
  lcd_wr_blind (l, init, 0);
  lcd_wr_blind (l, init, 0);
  lcd_wr_blind (l, init, 0);

  lcd_dpy_ctrl (l, 0, 0, 0);
  lcd_cls (l);
  lcd_dpy_ctrl (l, 1, 1, 1);
  lcd_emode_ctl (l, 1, 0);
}


#if 0
void
lcd_sc_write (unsigned char *d, int len)
{
  unsigned char m = LCD_EMOD | LCD_EMOD_DIR;
  static int do_cls = 0;

  if (do_cls)
    {
      lcd_cls (l);
      do_cls = 0;
    }

  if (pos == 31)
    m |= LCD_EMOD_SCROLL;

  lcd_wr (m, 0);

  while (len--)
    {

      switch (*d)
        {
        case '\n':
          do_cls++;
          break;
        default:
          if (do_cls)
            {
              lcd_cls ();
              do_cls = 0;
            }
          OUT (*d, DATA);
          OUT (LCD_RW | LCD_RS | LCD_EN, CTRL);
          OUT (LCD_RW | LCD_RS, CTRL);
          OUT (LCD_RW | LCD_RS | LCD_EN, CTRL);
          wait_busy ();

          if (pos < 31)
            {
              pos++;
              if (pos == 31)
                m |= LCD_EMOD_SCROLL;
              lcd_wr (m, 0);
            }

          break;
        }
      d++;
    }

}
#endif


#if 0
void
lcd_mkblock (int n, int c)
{
  int map[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 11, 0, 1 };
  int m = sizeof (map) / sizeof (int);

  int i;

  c <<= 3;
  c &= LCD_CGPOS_MASK;
  c |= LCD_CGPOS;
  lcd_wr (c, 0);

  for (i = 0; i < m; ++i)
    {
      if (map[i] < n)
        lcd_wr (0xff, 1);
      else
        lcd_wr (0x0, 1);
    }

}




#define GPTS 24

void
lcd_graph (int *dat)
{
  unsigned char c = 0, d, v;
  int map[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 11, 0, 1 };
  int m = sizeof (map) / sizeof (int);
  int i, j;


  for (c = 0; c < 0x40; c += 0x10)
    {
      lcd_wr (l, c | LCD_CGPOS, 0);

      for (i = 0; i < m; ++i)
        {
          v = 0x10;
          d = 0x0;

          for (j = 0; j < 5; ++j)
            {
              if (dat[j] > map[i])
                d |= v;
              v >>= 1;
            }

          lcd_wr (l, d, 1);

        }
      dat += 6;

    }
}

#endif

int
probe_port (Lcd l)
{
  int ports[] = { 0x378, 0x3bc, 0x278, 0 };
  int *p = ports;

  while (*p)
    {
/* Step one check the port works */

      OUT (LCD_EN, (*p) + 2);   /*Enable output drivers */
      OUT (0x55, *p);
      if (IN (*p) == 0x55)
        {
          OUT (0xaa, *p);
          if (IN (*p) == 0xaa)
            {
              l->base = *p;
              return (1);
            }
        }
      p++;
    }


  return (0);
}


void
check_bidir (Lcd l)
{

/* Write 55 */
  OUT (0x55, DATA (l));
/* Turn off output drivers */
  OUT (LCD_READEN | LCD_EN, CTRL (l)); /*Disable output drivers */
/* Write aa to driver reg */
  OUT (0xaa, DATA (l));
/* Read in status */
  if (IN (DATA (l)) == 0xaa)
    {
      l->epp = 0;
    }
  else
    {
      l->epp = 1;
    }
  OUT (LCD_EN, CTRL (l));       /*Enable output drivers */

}



Lcd
lcd_open (struct Lcd_struct ls)
{
  Lcd ret;

  iopl (3);

  ret = (Lcd) malloc (sizeof (ls));

  *ret = ls;

  if (!probe_port (ret))
    {
      free (ret);
      return (NULL);
    }

  check_bidir (ret);

  printf ("Using Port at 0x%x, type %s\n", ret->base,
          ret->epp ? "Bidirectional" : "Unidirectional");
  lcd_reset (ret);



  return (ret);

}


void
lcd_close (Lcd l)
{
  free (l);
}

int
lcd_printf (Lcd l, char *format, ...)
{
  char buf[1024], i, *ptr = buf, j;
  va_list ap;

  va_start (ap, format);
  j = i = vsnprintf (buf, sizeof (buf), format, ap);
  va_end (ap);

  ptr = buf;

  while (j--)
    {
      if (*ptr == '\n')
        *ptr = 0x20;
      ptr++;
    }



  lcd_cls (l);
  lcd_wr_buf (l, buf, i);


  return (i);

}
