/*  SVGATextMode -- An SVGA textmode manipulation/enhancement tool
 *
 *  Copyright (C) 1995  Koen Gadeyne
 *
 *  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 of the License, 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.
 */


/***
 *** get/set VGAreg, a simple VGA register hacking program
 *** (c) 1995 Koen Gadeyne (kmg@barco.be)
 ***
 *** WARNING: since different SVGA cards use different extra address ranges in any 
 *** of the register sets, no checking is done to make sure you don't attempt to change
 *** a non-existing register!
 ***
 *** This is just a hacking tool! Use at your own risk. It was NOT intended to be 
 *** idiot proof! If you don't understand all this, then don't bother trying to use it.
 ***
 ***/

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "misc.h"
#include "vga_prg.h"
#include "configfile.h"
#include "messages.h"

char *CommandName;
char ConfigFile[1024]=CONFIGFILE;
int debug_messages=FALSE;
const char *str_chipsets[NUM_CHIPSETS] = CHIPSET_STRINGS;
const char *str_regsets[NUM_REGSETS] = REGSET_STRINGS;
const int str_regset_type[NUM_REGSETS] = REGSET_TYPE;

#define NUM_BITS   8
char* int_to_bin(int num, int bits)
{
  static char binstr[sizeof(long int)+1];
  int i;
  
  for (i=0; i<bits; i++) binstr[i] = 0x30; /* char '0' */
  binstr[bits] = 0x00;
  
  for (i=0; i<bits; i++) binstr[bits-1-i] += ((num >> i) & 0x00000001);
  PDEBUG(("binstr = '%s'", binstr));
  return(binstr);
}

void usage(int setreg)
{
     PMESSAGE(("version %s. (c) 1995 Koen Gadeyne.\n Usage: %s [options] VGA_register_set [register_index] %s\n\n\
     Options: -h  print usage information\n\
              -n  Don't program VGA hardware\n\
              -d  print debugging information\n\
              -u  unlock chipset-specific registers (needs %s file)\n\
              -p  produce 'pipeable' decimal output (i.e. just the result, no text)\n\
              -x  produce 'pipeable' hex output (i.e. just the result, no text)\n\
              -t <ConfigFile>  Use alternative config file instead of the default (%s)\n\
                               (only useful for '-u' option)\n\n\
     VGA_register_set: any of %s\n\
     register_index: an index in the specified VGA_register_set,\n\
                     either in decimal (e.g. '24') or hex ('0x18') or octal ('030') notation.\n\
                     Only needed when it is an indexed register (all except 'MISC').\n\
     %s", VERSION, 
     CommandName, (setreg) ? "data" : "", ConfigFile, CONFIGFILE, showopts(str_regsets, NUM_REGSETS),
     (setreg) ? "data: the data to program into the specified register (dec|hex|oct).\n" : ""));
}


/***********************************************************************************************************/
 
int main (int argc, char* argv[])
{
  int chipset=-1;
  FILE* param_file;
  char tempstr[1024]="";
  int program_hardware=TRUE;
  int unlock_chipset=FALSE;
  int pipehex=FALSE;
  int pipe=FALSE;
  int regset = -1;
  char c;
  int tmpbyte=0;
  int regnum=0;
  int setreg = FALSE; /* if TRUE: "getVGAreg" function, if FALSE, "setVGAreg" function */
  char* commandfilename;
  int data=0;
  
 /*
  * See what action is required: read or write VGA register
  */
    
  CommandName = argv[0];
  commandfilename = strrchr(CommandName, '/');
  if (commandfilename) commandfilename++;
  else commandfilename = CommandName;
  setreg = (!strncasecmp(commandfilename,"set",3));
  
 
 /*
  * command-line argument parsing
  */

  while ((c = getopt (argc, argv, "ndhupxt:")) != EOF)
    switch (c)
    {
      case 'n': program_hardware=FALSE;
                break;
      case 'd': debug_messages=TRUE;
                break;
      case 'h': usage(setreg);
                exit(0);
                break;
      case 'u': unlock_chipset = TRUE;
                break;
      case 'p': pipe = TRUE;
                break;
      case 'x': pipehex = TRUE;
                break;
      case 't': strcpy(ConfigFile, optarg);
                break;
      case '?': usage(setreg);
                PERROR(("Bad option '-%c'\n",(char)optopt));
                exit(-1);
                break;
      default: PERROR(("getopt returned unknown token '%c'.",c));
    }
    
  PVERSION

  PDEBUG(("'%cetVGAreg' function selected through command name '%s'", (setreg) ? 's' : 'g', commandfilename));

  /* get register set from commandline */
  if (argc<optind+1) PERROR(("Missing register set on commandline"));
  regset = findoption(argv[optind], str_regsets, NUM_REGSETS, "register set");

  /* get register index, if an indexed register set was specified */
  if (str_regset_type[regset]==RT_INDEX)
  {
    optind++;
    if (argc<optind+1) PERROR(("Missing register index for '%s' register set", str_regsets[regset]));
    regnum = getbyte(argv[optind], "register number", 0, 255);
    PDEBUG(("register index = %d (0x%x).", regnum, regnum));
  }

  /* get register data, if 'set'VGAreg command */
  if (setreg)
  {
    optind++;
    if (argc<optind+1) PERROR(("Missing register data"));
    data = getbyte(argv[optind], "register data", 0, regset==REGSET_DAC ? (256 << 16) -1 : 255);
    PDEBUG(("register data to write = %d (0x%x).", data, data));
  }
  optind++;  
  if (argc>optind) PWARNING(("Extra parameters (starting with '%s') ignored", argv[optind])); 
  

 /*
  * open parameter file if needed for unlocking chipset, use only chipset definition (until now)
  */
  chipset = CS_VGA; /* if not defined: chipset = standard VGA */
  if (unlock_chipset)
  {
    param_file = open_param_file(ConfigFile);

    sscanf(findlabel(param_file, "ChipSet", LABEL_REQUIRED+LABEL_FROMSTART), "%*s %s", tempstr);
    chipset = findoption(tempstr, str_chipsets, NUM_CHIPSETS, "chip set");
  }  

/*
 * start doing something useful
 */
 
 if (program_hardware)
  {
     get_VGA_io_perm(chipset);
     if (unlock_chipset) unlock(chipset); 
     if (str_regset_type[regset]==RT_INDEX)
     {
       if (setreg) outb_VGA_indexed(regset, regnum, data);
       tmpbyte = inb_VGA_indexed(regset, regnum);
       if (pipe) printf("%d\n",tmpbyte);
       else if (pipehex) printf("0x%02x\n",tmpbyte);
       else printf("VGA '%s' register, index %d (=0x%x) contains %d (=0x%02x =b%s)\n",
                    str_regsets[regset], regnum, regnum, tmpbyte, tmpbyte,
                    regset==REGSET_DAC ? int_to_bin(tmpbyte,24): int_to_bin(tmpbyte,8));
     }
     else
     {
       if (setreg) outb_VGA_mem(regset, data);
       tmpbyte = inb_VGA_mem(regset);
       if (pipe) printf("%d\n",tmpbyte);
       else if (pipehex) printf("0x%02x\n",tmpbyte);
       else printf("VGA '%s' register contains %d (=0x%02x =b%s)\n",
                    str_regsets[regset], tmpbyte, tmpbyte,
                    regset==REGSET_DAC ? int_to_bin(tmpbyte,24): int_to_bin(tmpbyte,8));
     }
  }
  return(0);
}
