/*
 * Copyright: GNU Public License 2 applies
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2, or (at your option)
 *   any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * wrshasta (C) Heiko Eissfeldt heiko@colossus.escape.de
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <asm/io.h>

#undef SYNTAXCHECK

#define cli
#define sti
#define highvideo()
#define normvideo()

#define BOOL int
#define true 1
#define false 0

BOOL ram;
unsigned char addr_indexreg, datreg, ramaddressreg, datatransferport;

void errorexit (char *x, unsigned short n)
{
  fprintf(stderr, x);
  exit(n);
}

char *Hexbyte(unsigned char zahl)
{
  const char Hexdigits[16] = "0123456789ABCDEF";
  static char hexbyte[3];

  hexbyte[0] = Hexdigits[zahl >> 4];
  hexbyte[1] = Hexdigits[zahl & 0x0F];
  hexbyte[2] = 0;
  return hexbyte;
}


void upstr (char *x)
{
  while (*x) *x++ = toupper(*x);
}

char *noblank(char *x)
{
  char *y;
  while ((*x == ' ') || (*x == 9)) x++; /* skip white space */

  y = x + strlen(x)-1;
  while ((*y == ' ') || (*y == 9)) *y-- = 0; /* skip white space */
  return x;
}

unsigned char getreg (int addr_index)
{
#ifdef SYNTAXCHECK
  return 0;
#else
  unsigned char retval;

  cli;
  if (addr_index != -1 ) { /*&&&*/
    outb(addr_index, addr_indexreg);
  }
  retval = inb(datreg);
  sti;
  return retval;
#endif
}

unsigned char getregd (int ramaddress, int value)
{
#ifdef SYNTAXCHECK
  return 0;
#else
  unsigned char retval;

  cli;
  if (ramaddress != -1) {
    outb( ramaddressreg,addr_indexreg);
    outb( ramaddress,datreg);
  }
  outb(addr_indexreg,addr_indexreg);
  outb( 0x80 | (value & 0x0f),datreg);
  outb( datatransferport,addr_indexreg);
  retval = inb(datreg);
  sti;
  return retval;
#endif
}

void setregd (int ramaddress, int value, unsigned char data)
{
#ifndef SYNTAXCHECK
  cli;
  if (ram) {
    outb( ramaddressreg,addr_indexreg);
    outb( ramaddress,datreg);
  }
  outb( datatransferport,addr_indexreg);
  outb( data,datreg);
  outb( addr_indexreg,addr_indexreg);
  outb( 0xC0 | (value & 0x0f),datreg);
  sti;
#else
  printf("setregd: I shall write %x to %x:%x\n",data,ramaddress,value);
#endif
}

void setreg (int addr_index, unsigned char dat)
{
#ifndef SYNTAXCHECK
  cli;
  if (addr_index != -1) { /*&&&*/
    outb( addr_index,addr_indexreg);
  }
  outb( dat,datreg);
  sti;
#else
  printf("setreg: I shall write %x to %x\n",dat,addr_index);
#endif
}

long gethex(char **x)
{
  long b;
  char buffer[9];
  char *ind=buffer, *y;

  while ((**x == ' ') || (**x == 9)) (*x)++; /* skip white space */

  if (**x == '0' && toupper(*(x[1])) == 'X') *x += 2;

  while ((**x > 0) && (**x == '-' || **x == 'h' || **x == 'H' || isxdigit(**x))) {
    *ind++  =  *(*x)++;
  }
  *ind = '\0';

  ind = buffer;
  ind = noblank (ind);

  y = ind + strlen(ind)-1;
  if ((*y == 'h') || (*y == 'H')) *y-- = 0;

  if (! *ind) {
    b = -1;
  } else {
    if (*ind == '-') {
      memmove(3+ind,ind,strlen(ind)+1);
      strncpy(ind,"-0x",3);
    } else {
      memmove(2+ind,ind,strlen(ind)+1);
      strncpy(ind,"0x",2);
    }
    
    if (sscanf(ind, "%li", &b) != 1) {
      fprintf (stderr, ": error in hex conversion");
      exit(-1);
    }
  }
  return b;
}

void free_ports(void)
{
#ifndef SYNTAXCHECK
  if (ioperm(0x24, 5, 0) != 0) {
    perror("");
    exit(-3);
  }
#endif
}


int main(int argc, char *argv[])
{ /* main */
  int i;
  char *wert;
  long ramaddress, index_;


  if (argc < 3) {
    fprintf(stderr,
      "This program allows writing registers of the shasta chipset HTK340.\n");
    fprintf(stderr,
      "These are the chips HT321 and the HT342.\n");
    fprintf(stderr,
      "Registers are accessed by (at least) one index port.\n");
    fprintf(stderr,"Invocation: wrshasta index datum [index2 datum2 ...]\n");
    fprintf(stderr," all indices and values are expected in hexadecimal notation\n");
    fprintf(stderr," indices may be in one of two forms:\n");
    fprintf(stderr,"  1. a simple index in the range 1h - 2Ah (hex)\n");
    fprintf(stderr,"  2. or a double index [ramlocation]:dindex\n");
    fprintf(stderr,"   where ramlocation is in the range 08h - 1Fh and\n");
    fprintf(stderr,"   dindex is from the ranges 0h - 03h or 08h - 0Fh.\n");
    fprintf(stderr,"See also file shasta.cfg and program rdshasta.\n");
    exit(-1);
  }

  addr_indexreg = 0x28;
  datreg = 0x24;
  ramaddressreg = 0x29;
  datatransferport = 0x2a;


#ifndef SYNTAXCHECK
  if (ioperm(0x24, 5, 1) != 0) {
    perror("");
    exit(-2);
  }
#endif

  atexit(free_ports);

  putchar('\n');
#ifndef SYNTAXCHECK
  if (((getreg(0) & 0xf0) != 0) | ((getreg(0x20) & 0xf0) != 0x20)) {
    printf("No shasta chipset found. Terminating.\n");
    exit(-3);
  }
#endif
  i = argc - 1;
  while (i > 1) {
    wert = argv[i - 1];
    if (strchr(argv[i - 1], ':') != NULL) {
      ramaddress = gethex(&wert);
      wert = noblank(wert);
      if (*wert != '\0' && wert[0] == ':') {
	wert++;
	wert = noblank(wert);
	index_ = gethex(&wert);
	/*           regdata := getregd(ramaddress,index); */
	wert = argv[i];
	setregd(ramaddress, index_, (int)gethex(&wert));
      }
    } else {
      index_ = gethex(&wert);
      wert = argv[i];
      setreg((int)index_, (int)gethex(&wert));
    }
    i -= 2;
  }
  exit(0);
}


