/*
 * source file - "hame.c" - for public release
 * Written by Ben Williams, AA7AS - January thru April 1990
 * This source code is released into the public domain.
 * Release 1 of example code: April 19th, 1990
 * 
 * This file contains C code that is for handling the HAM-E mode screens.
 * 
 * The HAM-E is a device that is scheduled to become available in June 1990
 * from:
 *                 Black Belt Systems
 *                 398 Johnson Road
 *                 Glasgow, MT, USA
 *                 59230
 * 
 *             US Sales: (800) TK-AMIGA
 *  International Sales: (406) 367-5513
 *    Technical Support: (406) 367-5509
 *        G3 Office FAX: (406) 367-AFAX
 * 
 * This source code should be freely used to develop your own routines
 * in any language that work in conjunction with the HAM-E device.
 * That is the entire intention of publishing this source.
 *
 * Please note that the Save IFF C source was originally written by
 * Matt Dillon; only small changes were done to this at Black Belt to
 * make it work in this context. Essentially, we're saving 640x200 or
 * 640x400 standard IFF images. Because Matt's source uses functions
 * from his personal library of functions, to compile this you need to
 * link using "my.lib" which has been included in this archive. Only
 * for the IFF code do you need to link with this, so if you're using
 * other source and not the IFF source, you do NOT need to link in my.lib
 * at all.
 *
 * This source was written to compile and run under Lattice C v5.04; It
 * was not written to conform to MANX C at all. We do not use MANX C
 * at Black Belt and so cannot answer questions as to compatability
 * issues. In general, we're compiling this way:
 *
 *             lc -v -d -b0 -ohame.o - hame
 *
 * and linking this way:
 *
 *      blink WITH hame_list LIBRARY lib:lcm.lib+lib:lcnb_S.lib+
 *      lib:amiga.lib+lib:my.lib TO 'OutName' MAP nil: VERBOSE NODEBUG
 *
 * Where the file hame_list contains the following:
 *
 *       lib:c.o
 *       hame.o
 *       iff.o
 *
 * If you have specific questions about the source code, call Black
 * Belt Systems technical support line and we will assist you as best
 * we can.
 *
 * Samples of program use as written:
 *
 *  'hame -o -c'                Generates color diagram
 *  'hame -o -s'                Generates multiple mode sample image
 *  'hame -o -c -i ram:test'    Generates color diagram, saves iff to ram:test
 *  'hame ?'                    Lists command switches
 *  'hame -o -l -x 320 -y 400 -n df0:newtek' Shows 320x400 B&W NewTek IP file
 *  'hame -o -x 320 -y 200 -r (address) -g (address) -b (address)'
 *                              Generates picture from buffers located
 *                              at these addresses - expects 6 bits/byte
 *                              right (lsb) justified. Uses 'REG' mode.
 *  'hame -o -h -x 320 -y 200 -r (address) -g (address) -b (address)'
 *                              Generates picture from buffers located
 *                              at these addresses - expects 6 bits/byte
 *                              right (lsb) justified. Uses 'HAM-E' mode.
 *  'hame -o -c -d 500'         Generates color diagram, waits 10 seconds
 *
 * You will note that all of the above examples use the '-o' switch. This
 * causes the software to write to the 640 resolution screen on ODD pixel
 * boundries. This is currently the way that the HAM-E expects to find it's
 * data. We may change this - make sure that your rendering software can
 * cope with it. If the data is not located on the correct boundry, then
 * the image will NOT render in the new modes. Contact Black Belt Systems
 * technical support in June for the final decision on odd/even pixel
 * rendering.
 *
 */

/*
 *
 * Includes:
 *
 */
 
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <stdio.h>
#include <fcntl.h>

#include <exec/types.h> /* all this exec stuff is so I can find MemList */
#include <exec/ports.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <exec/tasks.h>
#include <exec/lists.h>
#include <exec/nodes.h>
#include <exec/libraries.h>
#include <exec/devices.h>

#include <intuition/preferences.h>
#include <rexx/storage.h>
#include <intuition/intuition.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>
#include <workbench/icon.h>

/*
 *
 * Defines:
 *
 */

/* program error codes: */
/*  0 - program is happy       */
#define HAPPY        0
/*  1 - program happy, but suicidal - user probably asked for help */
#define DIE_QUIETLY  1

/*  2-20 program warnings      */

/* 21-39 program failure       */
#define XDIM_NOGOOD  21
#define YDIM_NOGOOD  22
#define RBUF_NOGOOD  23
#define GBUF_NOGOOD  24
#define BBUF_NOGOOD  25
#define DLY_NOGOOD   26
#define UNKNOWN_OPT  27
#define SHIFT_NOGOOD 28
#define XOFF_NOGOOD  29
#define YOFF_NOGOOD  30
#define IP_NOGOOD    31
#define IF_NOGOOD    32
#define NO_PARAMS    33

/* 40... system problem aborts */
#define NO_INTUILIB 40
#define NO_GFXLIB   41
#define NO_EXECLIB  42
#define NO_ICONLIB  43
#define NO_SCREEN   44

/*
 *
 * Macros:
 *
 */

/*
 *
 * External Casts:
 *
 */

struct Library      *OpenLibrary();
struct IntuiMessage *GetMsg();
struct Window       *OpenWindow();
struct Screen       *OpenScreen();

/*
 * External definitions:
 */
extern struct WBStartup *WBenchMsg;

/*
 * Local structure defs
 */

struct Problems
  {
    unsigned char ProbID;
    char         *ProbText;
  };

/*
 * Globals:
 */
struct Problems our_problem[] =
  {
    NO_EXECLIB, "exec.library not found",
    NO_SCREEN,  "Unable to open screen",
    NO_GFXLIB,  "graphics.library not found",
    NO_ICONLIB, "icon.library not found",
    NO_INTUILIB,"intuition.library not found",
    UNKNOWN_OPT,"Unknown Option",
    DLY_NOGOOD, "delay time invalid",
    XDIM_NOGOOD,"x dimension no good",
    YDIM_NOGOOD,"y dimension no good",
    RBUF_NOGOOD,"red buffer address no good",
    GBUF_NOGOOD,"green buffer address no good",
    BBUF_NOGOOD,"blue buffer address no good",
    XOFF_NOGOOD,"X offset no good",
    YOFF_NOGOOD,"Y offset no good",
    IF_NOGOOD,  "IFF Filename no good",
    NO_PARAMS,  "no parameters - use 'hame ?' for info",
    0,0         /* end of table of error #/strings */
  };

unsigned char from_cli;
struct Library *IntuitionBase;
struct Library *GfxBase;
struct Library *IconBase;
struct Library *ExecBase;
struct Window  *win; /* power windows sample window */
int we_ownit;

unsigned short rdm;
int delay_time,hidden,sample,ham_reg,lacer,luma_mode,shifter,chroma,get_ip;
short write_odd,even_steven,iff_flag;
int xdim,ydim,x_offset,y_offset;
unsigned char *red,*green,*blue;
struct Screen *scr;
char ip_file[200];
char iff_file[200];

static struct NewScreen NewScreenStructure = {
  0,0,
  640,200,
  4,
  0,1,
  HIRES,
  CUSTOMSCREEN,
  NULL,
  "HAM-E 256 mode",
  NULL,
  NULL
};

unsigned char *fp0,*fp1,*fp2,*fp3; /* bitplane pointers - fast ones. */

/* pattern of bits within a byte to be used as ~masks and OR sources. */
unsigned char bitpat[] =
  {
    128,64,32,16,8,4,2,1,
  };

/* register mode cookie */
unsigned char ham_cookie[] =
  {
    0xA2,0xF5,0x84,0xDC,
    0x6D,0xB0,0x7F,0x18
  };

/* ham mode cookie */
unsigned char reg_cookie[] =
  {
    0xA2,0xF5,0x84,0xDC,
    0x6D,0xB0,0x7F,0x14
  };

/* format is: string matching option, string describing option */
char *opts[] =
  {
    "?",    "' ...for info",
    "h",    "' ...for info",
    "help", "' ...for info",
    "-x",   " [dimension]' ...set X dimension",
    "-y",   " [dimension]' ...set Y dimension",
    "-r",   " [red buff pointer]' ...hexidecimal address of data buffer",
    "-g",   " [green buff pointer]' ...hexidecimal address of data buffer",
    "-b",   " [blue buff pointer]' ...hexidecimal address of data buffer",
    "-o",   "' ...write starting at ODD pixels instead of even",
    "-d",   " [jiffies]' ... delay time for test screen to persist",
    "-v",   "' ...hide view while creating",
    "-s",   "' ...generate sample screen",
    "-h",   "' ...create HAM'ed image",
    "-p",   "' ...create Palette REG image",
    "-l",   "' ...luma mode",
    "-m",   " [shift]' ...multiply grey level (shift left N bits)",
    "-xo",  " [offset]' ...enter x offset into luma buffer",
    "-yo",  " [offset]' ...enter Y offset into luma buffer",
    "-c",   " ...chromasticity diagram",
    "-n",   " [filename]' ...read NewTek ip file as B&W",
    "-e",   "' ... use EVEN palette in -s sample screen",
    "-i",   " [filename]' ... save IFF file from generated image",
    0,0
  };

int do_help();
int do_xdim();
int do_ydim();
int do_rbuf();
int do_gbuf();
int do_bbuf();
int do_wodd();
int do_dt();
int do_hide();
int do_sample();
int do_ham();
int do_reg();
int do_luma();
int do_shift();
int do_xo();
int do_yo();
int do_chroma();
int do_ip();
int do_even();
int do_iff();

/* refer to the "opts" table for corresponding CLI/shell options */
int (*opt_name[])() =
  {
    do_help,
    do_help,
    do_help,
    do_xdim,
    do_ydim,
    do_rbuf,
    do_gbuf,
    do_bbuf,
    do_wodd,
    do_dt,
    do_hide,
    do_sample,
    do_ham,
    do_reg,
    do_luma,
    do_shift,
    do_xo,
    do_yo,
    do_chroma,
    do_ip,
    do_even,
    do_iff,
    0
  };

/*
 * Code (finally!) :^)
 */

/*
 * The "do_xxxxx()" proceedures are called when a command line
 * parameter is encountered. They parse the line, setup variables
 * accordingly, and return to the CLI handler. None of them are
 * directly involved with handling the HAM-E device; only in
 * program initialization.
 */

void safe_line(ptr)
  char *ptr;
  {
    if (from_cli)
      {
        printf("%s\n",ptr);
      }
  }

rdmgen() /* easy, fast pseudo-rdm number generator (16k sequence) */
  {
    register unsigned short foo;
    foo = rdm;
    foo <<= 2;
    rdm += foo;
    return(rdm >> 8);
  }

/* parameter handling... */
int do_even(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
    even_steven=1;
    return(0);
  }

int do_chroma(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
    chroma=1;
    return(0);
  }

int do_wodd(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
    write_odd=1;
    return(0);
  }

int do_luma(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
    luma_mode=1;
    return(0);
  }

int do_sample(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
    sample=1;
    return(0);
  }

int do_ham(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
    ham_reg=1;
    return(0);
  }

int do_reg(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
    ham_reg=0;
    return(0);
  }

/* parameter sucking... */
int do_hide(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
    hidden=1;
    return(0);
  }

int do_iff(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
    if (++(*index_ptr) > limit) /* index to our keyword after switch */
      {
        return(IF_NOGOOD);
      }
    strcpy(&iff_file[0],argv[*index_ptr++]); /* get incoming parameter */
    iff_flag=1;

    return(0); /* we're all happy here... :^) */
  }

int do_ip(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
    if (++(*index_ptr) > limit) /* index to our keyword after switch */
      {
        return(IP_NOGOOD);
      }
    strcpy(&ip_file[0],argv[*index_ptr++]); /* get incoming parameter */
    get_ip=1;

    return(0); /* we're all happy here... :^) */
  }

int do_xo(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
  char argbuf[25];
    if (++(*index_ptr) > limit) /* index to our keyword after switch */
      {
        return(XOFF_NOGOOD);
      }
    strcpy(argbuf,argv[*index_ptr++]); /* get incoming parameter */
    sscanf(argbuf,"%d",&x_offset);
    
    return(0); /* we're all happy here... :^) */
  }

int do_yo(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
  char argbuf[25];
    if (++(*index_ptr) > limit) /* index to our keyword after switch */
      {
        return(YOFF_NOGOOD);
      }
    strcpy(argbuf,argv[*index_ptr++]); /* get incoming parameter */
    sscanf(argbuf,"%d",&y_offset);
    
    return(0); /* we're all happy here... :^) */
  }

int do_dt(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
  char argbuf[25];
    if (++(*index_ptr) > limit) /* index to our keyword after switch */
      {
        return(DLY_NOGOOD);
      }
    strcpy(argbuf,argv[*index_ptr++]); /* get incoming parameter */
    sscanf(argbuf,"%d",&delay_time);
    
    return(0); /* we're all happy here... :^) */
  }

int do_shift(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
  char argbuf[25];
    if (++(*index_ptr) > limit) /* index to our keyword after switch */
      {
        return(SHIFT_NOGOOD);
      }
    strcpy(argbuf,argv[*index_ptr++]); /* get incoming parameter */
    sscanf(argbuf,"%d",&shifter);
    
    return(0); /* we're all happy here... :^) */
  }

int do_xdim(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
  char argbuf[25];
    if (++(*index_ptr) > limit) /* index to our keyword after switch */
      {
        return(XDIM_NOGOOD);
      }
    strcpy(argbuf,argv[*index_ptr++]); /* get incoming parameter */
    sscanf(argbuf,"%d",&xdim);
    
    return(0); /* we're all happy here... :^) */
  }

int do_ydim(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
  char argbuf[25];
    if (++(*index_ptr) > limit) /* index to our keyword after switch */
      {
        return(YDIM_NOGOOD);
      }
    strcpy(argbuf,argv[*index_ptr++]); /* get incoming parameter */
    sscanf(argbuf,"%d",&ydim);
    
    return(0); /* we're all happy here... :^) */
  }

int do_rbuf(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
  char argbuf[25];
    if (++(*index_ptr) > limit) /* index to our keyword after switch */
      {
        return(RBUF_NOGOOD);
      }
    strcpy(argbuf,argv[*index_ptr++]); /* get incoming parameter */
    sscanf(argbuf,"%x",((int*)(&red)));
    
    return(0); /* we're all happy here... :^) */
  }

int do_gbuf(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
  char argbuf[25];
    if (++(*index_ptr) > limit) /* index to our keyword after switch */
      {
        return(GBUF_NOGOOD);
      }
    strcpy(argbuf,argv[*index_ptr++]); /* get incoming parameter */
    sscanf(argbuf,"%x",((int*)(&green)));
    
    return(0); /* we're all happy here... :^) */
  }

int do_bbuf(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this option's position */
  int limit;        /* indicates total number of opts on cmd line */
  {
  char argbuf[25];
    if (++(*index_ptr) > limit) /* index to our keyword after switch */
      {
        return(BBUF_NOGOOD);
      }
    strcpy(argbuf,argv[*index_ptr++]); /* get incoming parameter */
    sscanf(argbuf,"%x",((int*)(&blue)));
    
    return(0); /* we're all happy here... :^) */
  }


int do_help(argv,index_ptr,limit)
  char *argv[];     /* cmd line arguments */
  int *index_ptr;   /* points to this options position */
  int limit;        /* indicates total number of opts on cmd line */
  {
  int index;
  char buffer[100];
    sprintf(&buffer[0],"%s. Usage is:",argv[0]);
    safe_line(&buffer[0]);
    for (index=0; opts[index]; index+=2)
      {
        sprintf(&buffer[0],"'[33m1> [31m%s %s%s",argv[0],opts[index],opts[index+1]);
        safe_line(&buffer[0]);
      }
    safe_line("");
    safe_line("(c) Black Belt Systems, 1990 - ALL RIGHTS RESERVED");
    safe_line("Written by Ben Williams, ars:AA7AS");
    return(DIE_QUIETLY);
  }

/*
 * do_cli_args()
 *
 * This proceedure parses the command line arguments and calls other
 * proceedures based upon what is encountered in the command line.
 * This is considered an initialization step (many of them). Once all
 * CLI parameters have been handled, error status (or success) is returned
 * to the main() proceedure, which then passes it on to the runner()
 * proceedure. If an error is encountered, runner() returns immediately
 * and cleanup() is called, which will cleanup (obviously) and also
 * report the error.
 */

int do_cli_args(argc,argv)
  int argc;
  char *argv[];
  {
  int arg_index,arg_limit,opt_index;
  int parm;
  unsigned char found;
    from_cli=1; /* global info for later printfs and the like */
    parm=0; /* no errors - yet. */
    arg_limit = argc-1; /* argv[0] is the programs NAME */
    arg_index = 1;
    while(arg_index <= arg_limit )
      {
        found=0;
        opt_index=0;
        while((!found) && (opts[opt_index]))
          {
            if (strnicmp(argv[arg_index],opts[opt_index])==0)
              {
                found=1;
                /* call FN via table, pass position in parameters */
                if (parm=(*opt_name[opt_index >> 1])(argv,&arg_index,arg_limit))
                  {
                    return(parm);
                  }
              }
            opt_index += 2;
          }
        if (!found)
          {
            return(UNKNOWN_OPT);
          }
        arg_index++;
      }
    return(0);
  }

int init(argc,argv)
  int argc;
  char *argv[];
  {
  int parm;
    iff_flag=0;
    iff_file[0]=0;
    even_steven=0;
    ip_file[0]=0;
    get_ip=0;
    chroma=0;
    x_offset=0;
    y_offset=0;
    shifter=0;
    luma_mode=0;
    lacer=0;
    delay_time=0;
    write_odd=0;
    rdm=1; /* seed! */
    hidden=0;
    red = NULL;
    green = NULL;
    blue = NULL;
    if (!(ExecBase = OpenLibrary("exec.library",0)))
      {
        return(NO_EXECLIB);
      }
    if (!(IntuitionBase = OpenLibrary("intuition.library",0)))
      {
        return(NO_INTUILIB);
      }
    if (!(GfxBase = OpenLibrary("graphics.library",0)))
      {
        return(NO_GFXLIB);
      }
    if (!(IconBase = OpenLibrary(ICONNAME,0)))
      {
        return(NO_ICONLIB);
      }
    /* now we parse the arguments, if any, from the parent environment: */
    parm=NO_PARAMS;
    if (argc)
      {
        parm=do_cli_args(argc,argv);
      }
    return(parm);
  }

/*
 * cleanup()
 *
 * This routine close libraries and so on; if there was an error
 * encountered in the program, it is looked up and printed out here.
 */

void cleanup(parm)
  int parm;
  {
  char ebuff[80];
  int i;
    if (parm == DIE_QUIETLY) parm=HAPPY; /* no errors generated */
    if (GfxBase)        CloseLibrary(GfxBase);
    if (IconBase)       CloseLibrary(IconBase);
    if (IntuitionBase)  CloseLibrary(IntuitionBase);
    if (ExecBase)       CloseLibrary(ExecBase);
    if (parm) /* get system problem from table up front */
      {
        i=0; /* look up error # linearly */
        while ((parm != our_problem[i].ProbID) && (our_problem[i].ProbID))
          {
            i++;
          }
        if (!our_problem[i].ProbID) /* then error # isn't in the table */
          {
            sprintf(&ebuff[0],"Unknown Internal Error #%d",parm);
            safe_line(&ebuff[0]);
          }
        else /* we found the error, signal it. */
          {
            safe_line(our_problem[i].ProbText);
          }
      }
    exit(parm);
  }

/*
 * write_byte() - lowest level routine
 *
 * This routine actually writes bytes to the target screen; all other
 * routines vector thru it. Why? Because we need to control how the
 * bytes are written to the screen; at the current time,
 * a byte must be written to an odd pixel and then an even one,
 * which is a little unusual. We're looking at the hardware to see
 * if we can change this. If not, you'll need a global flag that
 * the user can set (here, it's called "write_odd") for their particular
 * machine... hopefully, this will not be a variable, it could be a pain
 * in the ribs. But for the moment, the variable is needed in case there
 * is variation.
 *
 * This routine (obviously) isn't particularly optomized, it's just very
 * straightforward code. Since it is the bottom level routine, you'll
 * almost certainly want to replace it with in line code or at the
 * very least fast external ASM code. We leave all that fun up to
 * you for now, although we'll try to provide that stuff too, if we
 * get time.
 */

void write_byte(x,y,n)
  short x;
  int y;
  unsigned char n;
  {
  int ypos,byte_offset;
  short bit_offset;
    ypos = y * 80; /* index to correct scan line */
    bit_offset = ((x << 1) & 7) | write_odd; /* find base bit position */
    byte_offset = (x >> 2);   /* find base byte offset */
    if (n & 128) *(fp3 + ypos + byte_offset) |= bitpat[bit_offset];
            else *(fp3 + ypos + byte_offset) &= ~bitpat[bit_offset];
    if (n &  64) *(fp2 + ypos + byte_offset) |= bitpat[bit_offset];
            else *(fp2 + ypos + byte_offset) &= ~bitpat[bit_offset];
    if (n &  32) *(fp1 + ypos + byte_offset) |= bitpat[bit_offset];
            else *(fp1 + ypos + byte_offset) &= ~bitpat[bit_offset];
    if (n &  16) *(fp0 + ypos + byte_offset) |= bitpat[bit_offset];
            else *(fp0 + ypos + byte_offset) &= ~bitpat[bit_offset];
    bit_offset++; /* to next nybble */
    if (bit_offset == 8) /* carry into next byte? */
      {
        bit_offset=0;
        byte_offset++;
      }
    if (n & 8) *(fp3 + ypos + byte_offset) |= bitpat[bit_offset];
          else *(fp3 + ypos + byte_offset) &= ~bitpat[bit_offset];
    if (n & 4) *(fp2 + ypos + byte_offset) |= bitpat[bit_offset];
          else *(fp2 + ypos + byte_offset) &= ~bitpat[bit_offset];
    if (n & 2) *(fp1 + ypos + byte_offset) |= bitpat[bit_offset];
          else *(fp1 + ypos + byte_offset) &= ~bitpat[bit_offset];
    if (n & 1) *(fp0 + ypos + byte_offset) |= bitpat[bit_offset];
          else *(fp0 + ypos + byte_offset) &= ~bitpat[bit_offset];
  }

/*
 * SetRGB8();
 *
 * This routine is similar to SetRGB4() in the Amiga's "normal" gfx library.
 * You pass it the register number, the RGB value, and a parameter that tells
 * it which line the palette starts on and it will set the register for you.
 * For most screens, the starting line will be ZERO, as that leaves the most
 * room for futzing about with images; however, in the test software, we
 * sometimes reload the palette on the same screen. This means that the
 * second palette will begin on a line other than zero.
 *
 * If the global variable "lacer" is set, this routine will write the color
 * register data in two places - one for each 200 line section of the
 * interlace image. If you need different color registers for each field,
 * then you'll need to modify this routine - that would be an unusual
 * situation, even though it's perfectly feasible.
 */

void SetRGB8(reg,rr,gg,bb,base)
  short reg;
  unsigned char rr,gg,bb;
  short base;
  {
  short p_row,p_index;
    p_row = (reg >> 6) + base;           /* palette row 0-3.  */
    p_index = ((reg & 0x3f) * 3) + 8;    /* reg 0-63 in p_row */
    if (lacer) /* then we need both fields! */
      {
        write_byte(p_index,   p_row*2, rr);         /* put RED value     */
        write_byte(p_index+1, p_row*2, gg);         /* put GREEN value   */
        write_byte(p_index+2, p_row*2, bb);         /* put BLUE value    */
        write_byte(p_index,   (p_row*2)+1, rr);     /* put RED value     */
        write_byte(p_index+1, (p_row*2)+1, gg);     /* put GREEN value   */
        write_byte(p_index+2, (p_row*2)+1, bb);     /* put BLUE value    */
      }
    else /* just write to one field */
      {
        write_byte(p_index,   p_row, rr);     /* put RED value     */
        write_byte(p_index+1, p_row, gg);     /* put GREEN value   */
        write_byte(p_index+2, p_row, bb);     /* put BLUE value    */
      }
  }

/*
 * gen_even_palette()
 *
 * This routine creates a special palette; it's extremely useful for
 * drawing images of unknown color composition quickly. See the
 * WriteRGB() routine, just following.
 *
 * This palette essentially gives you 6 levels of red, 7 levels of
 * green, and 6 levels of blue. If you were to do a binary distribution,
 * you'd get 8 of red, 8 of green, and 4 of blue, which leaves images
 * with any blue detail at all looking like trash. This distribution
 * and the accompaning write routine evens the score between the colors.
 */

void gen_even_palette(base)
  int base;
  {
  int rr,gg,bb,i;
    i=0;
    for (rr=0; rr<252; rr+=42)
      {
        for (gg=0; gg<252; gg+=36)
          {
            for (bb=0; bb<252; bb+=42)
              {
                SetRGB8(i++,rr,gg,bb,base);
              }
          }
      }
  }

/*
 * write_RGB()
 *
 * This routine is used after calling "gen_even_palette()", above.
 * gen_even_palette() sets up 252 color registers such that they are
 * pretty evenly distributed accross the color spectrum. Given a 24
 * bit input, this routine maps the image colors to the palette
 * colors evenly. Very useful as a general "show" routine. Most images
 * look very nice indeed... 256 colors, no fringing.
 */

void write_RGB(x,y,rr,gg,bb)
  int x,y,rr,gg,bb;
  {
    write_byte(x,y,((rr/43)*42) + ((gg/37)*6) + (bb/43) );
  }

/*
 * write_cookie()
 *
 * This function writes the cookie on a particular line. The variable
 * "brand" is a pointer to an arrary of data that contains the particular
 * cookie for the mode you want. Call as:
 *      write_cookie(ham_cookie,line);
 *                  -or-
 *      write_cookie(reg_cookie,line);
 * If you have a four line palette, you need to call this function for
 * each successive line the palette exists upon.
 *
 * See the next set of proceedures: WriteRegCookie() and WriteHamCookie()
 * for a higher level approach to this.
 *
 * If the global variable "lacer" is set, this function will write the cookie
 * data on TWO lines... sending four lines of cookie to the function with the
 * lines 0,1,2,3 will write cookies on line pairs 0,1 2,3 4,5 and 6,7.
 */

void write_cookie(brand,line)
  unsigned char *brand;
  int line;
  {
  int i;
    if (lacer) /* we need double the cookie data! */
      {
        for (i=0; i<8; i++)
          {
            write_byte(i,line*2,brand[i]);
            write_byte(i,(line*2)+1,brand[i]);
          }
      }
    else
      {
        for (i=0; i<8; i++)
          {
            write_byte(i,line,brand[i]);
          }
      }
  }

/*
 * WriteHamCookie(), WriteRegCookie()
 *
 * More specific versions of cookie handling...
 */

void WriteHamCookie(lines,base)
  int lines,base;
  {
  int i;
    for (i=base; i<(base+lines); i++)
      {
        write_cookie(ham_cookie,i);
      }
  }

void WriteRegCookie(lines,base)
  int lines,base;
  {
  int i;
    for (i=base; i<(base+lines); i++)
      {
        write_cookie(reg_cookie,i);
      }
  }

/*
 * Sets palette for Amiga side of hardware: The palette here is designed
 * to achieve two independant goals. First, and most importantly, it creates
 * a situation where the IRGB lines at the Amiga's data port will exactly
 * mirror the data in the bitplanes of the screen as each pixel is emitted.
 * Secondly, this palette makes the images visible, if not sensible, on a
 * non HAM-e equipped Amiga... Hopefully this distinctive color palette will
 * quickly cue the user that they are missing something good. :^)
 *
 * IRGB to 12 bit correspondence:
 * 
 * bit 8 - b3 of red
 * bit 4 - b3 of green
 * bit 2 - b3 of blue
 * bit 1 - b0 of blue
 */

void make_hame_palette()
  {
  register struct ViewPort *vp;
  register int rr,gg,bb,i;
  int col;
    col=0;
    vp = &scr->ViewPort;
    for (i=0; i<16; i++)
      {
        rr=0; gg=0; bb=0;
        if (i & 8) rr  = 8; /* this builds the IRGB bit outputs. */
        if (i & 4) gg  = 8;
        if (i & 2) bb  = 8;
        if (i & 1) bb |= 1;
        rr += (col & 7); /* build strange colors in there, too... */
        col += 2;
        gg += (col & 7); /* ... so "normal view" is interesting */
        col += 2;
        bb += (col & 6);
        col += 2;
        /* Set_v_RGB4() is in iff.c - almost the same as SetRGB4() */
        Set_v_RGB4(vp,i,rr,gg,bb); /* actually set the Amiga colors */
      }
  }

/*
 * grey_palette();
 *
 * Writes 256 register palette using the ri, gi, and bi variables as
 * addends for each register. calling with 1,1,1 creates a palette that
 * goes from 0:0:0, 1:1:1, 2:2:2.... 255:255:255  Calling with with
 * the values 1,0,1 would create a palette that goes from 0:0:0, 1:0:1,
 * 2:0:2... 255:0:255  The function is intended to provide an easy way
 * to generate linear 256 level palettes of the seven major colors:
 * R, G, B, RG, RB, GB, RGB.
 */

void grey_palette(base,ri,gi,bi)
  int base;
  {
  int i,rv,gv,bv;
    rv=0; gv=0; bv=0;
    for (i=0; i<256; i++)
      {
        SetRGB8(i,rv,gv,bv,base);
        rv += ri;
        gv += gi;
        bv += bi;
      }
  }

/*
 * make_luma_palette()
 *
 * This routine creates 256 grey levels using the grey_palette routine,
 * intended for use in register mode.
 */

void make_luma_palette()
  {
    grey_palette(0,1,1,1);
  }

/*
 * inc_palette();
 *
 * Writes 256 register palette, intended for use in register mode...
 * Creates 256 colors arranged such that there are 8 levels of red,
 * 8 levels of green, and 4 levels of blue - the typical 8 bit RGB
 * distribution. A much better choice is "gen_even_palette()", but
 * this is the "typical" industry way of distributing colors.
 */

void inc_palette(base)
  int base;
  {
  int i;
  unsigned char pred,pgreen,pblue;
    for (i=0; i<256; i++)
      {
        pred   = (i & 0xE0) + ((i & 0xE0)>>3);
        pgreen = ((i & 0x1C) << 3) + (((i & 0x1C) << 3) >> 3);
        pblue  = ((i & 0x03) << 6) + ((i & 0x03) << 4) + ((i >> 1)&1);
        
        SetRGB8(i,pred,pgreen,pblue,base);
      }
  }

/*
 * This function simply writes a block of a particular value. Intended
 * for generating into a REG mode screen. Used in Bugger();
 */

void write_block(bx,by,value,base)
  short bx,by;
  unsigned char value;
  short base;
  {
  short xbase,ybase,xi,yi;
    xbase = bx * 10; /* determine starting x,y */
    ybase = (by * 8)+base;
    for (yi=ybase; yi<(ybase+8); yi++)
      {
        for (xi=xbase; xi<(xbase+10); xi++)
          {
            write_byte(xi,yi,value);
          }
      }
  }

/*
 * Bugger()
 *
 * This function writes a number of different patterns into the screen
 * that are useful when working with the board (diagnostics). Various
 * techniques, mostly quick and dirty, are used to create these patterns.
 * Bugger is not "nice" code - sorry.
 */

void bugger(srow)
  int srow;
  {
  int i,vx;
  int vy;
    if (hidden) ScreenToBack(scr);
    WriteHamCookie(4,0);
    /* write palette at ham cookie starting row */
    if (even_steven) gen_even_palette(0); /* 6r 7g 6b */
    else             inc_palette(0);      /* 8r 8g 4b */
    WriteRegCookie(4,65);
    grey_palette(65,1,1,1); /* 256 grey levels */
    WriteRegCookie(4,73);
    grey_palette(73,1,0,0); /* 256 RED levels */
    WriteRegCookie(4,81);
    grey_palette(81,0,1,0); /* 256 GREEN levels */
    WriteRegCookie(4,89);
    grey_palette(89,0,0,1); /* 256 BLUE levels */
    WriteRegCookie(4,100);
    if (even_steven) gen_even_palette(100); /* 6r 7g 6b */
    else             inc_palette(100);      /* 8r 8g 4b */
    for (vx=0; vx<256; vx++)
      {
        write_byte(vx,69,vx); /* writes for linear grey scale */
        write_byte(vx,70,vx);
        write_byte(vx,71,vx);
        write_byte(vx,72,vx);
        
        write_byte(vx,77,vx); /* red */
        write_byte(vx,78,vx);
        write_byte(vx,79,vx);
        write_byte(vx,80,vx);
        
        write_byte(vx,85,vx); /* green */
        write_byte(vx,86,vx);
        write_byte(vx,87,vx);
        write_byte(vx,88,vx);
        
        write_byte(vx,93,vx); /* blue */
        write_byte(vx,94,vx);
        write_byte(vx,95,vx);
        write_byte(vx,96,vx);
      }
    i=1;
    vx=1; vy=0;
    while(i < 60) /* show registers in HAM-E mode */
      {
        write_block(vx,vy,i++,24); /* place reg block */
        vx++;
        if (vx == 32)
          {
            vx=0;
            vy++;
          }
      }
    i=0;
    if (even_steven) /* using 6r 7g 6b color distribution */
      {
        for (vy=0; vy<9; vy++) /* generates 9 rows of reg loads */
          {
            for (vx=0; vx<30; vx++)
              {
                write_block(vx,vy,i,srow);
                i++;
                if (i == 252)
                  {
                    vx=30; vy=9;
                  }
              }
          }
        i=2;
        /* write RGB color distribution bar */
        write_block(i++,10,0,srow); /* black (0) */
        write_block(i++,10,1,srow); /* blue (5) */
        write_block(i++,10,2,srow);
        write_block(i++,10,3,srow);
        write_block(i++,10,4,srow);
        write_block(i++,10,5,srow);
        i++;
        write_block(i++,10,0,srow); /* black (0) */
        write_block(i++,10,6,srow);
        write_block(i++,10,12,srow); /* green (6) */
        write_block(i++,10,18,srow);
        write_block(i++,10,24,srow);
        write_block(i++,10,30,srow);
        write_block(i++,10,36,srow);
        i++;
        write_block(i++,10,0,srow); /* black (0) */
        write_block(i++,10,42,srow); /* red (5) */
        write_block(i++,10,84,srow);
        write_block(i++,10,126,srow);
        write_block(i++,10,168,srow);
        write_block(i++,10,210,srow);
      }
    else /* using 8r 8g 4b color distribution */
      {
        for (vy=0; vy<8; vy++) /* generates 9 rows of reg loads */
          {
            for (vx=0; vx<32; vx++)
              {
                write_block(vx,vy,i,srow);
                i++;
              }
          }
        /* write RGB color distribution bar */
        i=3;
        write_block(i++,9,0,srow); /* black (0) */
        write_block(i++,9,1,srow); /* blue (3) */
        write_block(i++,9,2,srow);
        write_block(i++,9,3,srow);
        i++;
        write_block(i++,9,0,srow); /* black (0) */
        write_block(i++,9,4,srow); /* green (7) */
        write_block(i++,9,8,srow);
        write_block(i++,9,12,srow);
        write_block(i++,9,16,srow);
        write_block(i++,9,20,srow);
        write_block(i++,9,24,srow);
        write_block(i++,9,28,srow);
        i++;
        write_block(i++,9,0,srow); /* black (0) */
        write_block(i++,9,32,srow); /* red (7) */
        write_block(i++,9,64,srow);
        write_block(i++,9,96,srow);
        write_block(i++,9,128,srow);
        write_block(i++,9,160,srow);
        write_block(i++,9,192,srow);
        write_block(i++,9,224,srow);
      }
    
    /* write buncha colors in HAM mode */
    
    write_byte(0,50,0x40); /* hamming test - load color reg 0:0 first... */
    write_byte(0,51,0x40);
    write_byte(0,52,0x40);
    
    write_byte(1,50,0x80); /* hamming test - load color reg 0:0 first... */
    write_byte(1,51,0x80);
    write_byte(1,52,0x80);

    write_byte(2,50,0xC0); /* hamming test - load color reg 0:0 first... */
    write_byte(2,51,0xC0);
    write_byte(2,52,0xC0);
    /* ramp up colors using "pure" RGB ham loads */
    for (vx=0; vx<256; vx++) /* then build 0-63 each of three ways */
      {
        write_byte(vx+3,50,(vx >> 2)+0x80); /* write LOTS of HAM data! */
        write_byte(vx+3,51,(vx >> 2)+0xC0);
        write_byte(vx+3,52,(vx >> 2)+0x40);
      }
    
    write_byte(0,55,0x40); /* hamming test - load color reg 0:0 first... */
    write_byte(0,56,0x40);
    write_byte(0,57,0x40);
    
    write_byte(1,55,0x80); /* hamming test - load color reg 0:0 first... */
    write_byte(1,56,0x80);
    write_byte(1,57,0x80);

    write_byte(2,55,0xC0); /* hamming test - load color reg 0:0 first... */
    write_byte(2,56,0xC0);
    write_byte(2,57,0xC0);
    
    for (vx=0; vx<256; vx++) /* then build 63-0 each of three ways */
      {
        write_byte(vx+3,55,((255-vx) >> 2)+0x80); /* write LOTS of HAM data! */
        write_byte(vx+3,56,((255-vx) >> 2)+0xC0);
        write_byte(vx+3,57,((255-vx) >> 2)+0x40);
      }
    /* write hammed grey scale... */
    write_byte(0,60,0x80); /* hamming test - load color reg 0:0 first... */
    write_byte(1,60,0xC0); /* hamming test - load color reg 0:0 first... */
    write_byte(2,60,0x40); /* hamming test - load color reg 0:0 first... */
    i=3;
    for (vx=0; vx<64; vx++)
      {
         write_byte(i++,60,vx+0x80);
         write_byte(i++,60,vx+0xC0);
         write_byte(i++,60,vx+0x40);
      }
    write_byte(0,63,0x80); /* hamming test - load color reg 0:0 first... */
    write_byte(1,63,0xC0); /* hamming test - load color reg 0:0 first... */
    write_byte(2,63,0x40); /* hamming test - load color reg 0:0 first... */
    i=3;
    for (vx=63; vx>=0; vx--)
      {
         write_byte(i++,63,vx+0x80);
         write_byte(i++,63,vx+0xC0);
         write_byte(i++,63,vx+0x40);
      }
    if (hidden) ScreenToFront(scr);
  }

/*
 * show_luma_pic()
 *
 * Use the GREEN buffer pointer to generate a B&W rendering. Color registers
 * are set to 256 grey levels in the "runner()" routine.
 */

void show_luma_pic()
  {
  int h,v,hl,vl,lbase,start;
    WriteRegCookie(4,0);
    make_luma_palette();
    start=4;
    if (lacer) start=8;
    hl = xdim; vl = ydim; /* requested dimensions */
    if (lacer) /* is this an interlace screen? */
      {
        if (vl > 400) vl = 400;
      }
    else
      {
        if (vl > 200) vl = 200;
      }
    if (hl > 320) hl = 320;
    for(v=start; v<vl; v++)
      {
        lbase = (v+y_offset)*xdim;
        write_byte(0,v,0x01); /* this prevents a full like of color zero. */
        for (h=1; h<hl; h++)
          {
            write_byte(h,v,*(green+lbase+h+x_offset) << shifter);
          }
      }
  }


/*
 * show_ip_file()
 *
 * This routine will show a NewTek format "ip" picture; it's a _very_
 * simple routine, but serves to show a nice easy application.
 */

void show_ip_file()
  {
  int h,v,hl,vl,start;
  char mybyte;
  FILE *fp;
    WriteRegCookie(4,0);
    make_luma_palette();
    start=4;
    if (!xdim) hl=320;
    if (!ydim) vl=200;
    if (!(fp=fopen(&ip_file[0],"r")))
      {
        return;
      }
    for(v=start; v<vl; v++)
      {
        write_byte(0,v,0x01); /* Dummy pixel to handle turn off problems */
        if (fread(&mybyte,1,1,fp) != 1)
          {
            fclose(fp);
            return;
          }
        for (h=1; h<hl; h++)
          {
            if (fread(&mybyte,1,1,fp) != 1)
              {
                fclose(fp);
                return;
              }
            write_byte(h,v,mybyte);
          }
      }
    fclose(fp);
    get_ip=0;
  }

/*
 * showregpic6()
 *
 * This function will attempt to display buffers that were (presumably)
 * passed in at the commands startup. See the -r, -g and -b options, as
 * well as the -x and -y options.
 *
 * 18 bit
 */

void showregpic6()
  {
  int h,v,hl,vl,lbase,start;
    start=4;
    WriteRegCookie(4,0);
    gen_even_palette(0);
    if (lacer) start=8;
    hl = xdim; vl = ydim; /* requested dimensions */
    if (lacer)
      {
        if (vl > 400) vl = 400;
      }
    else
      {
        if (vl > 200) vl = 200;
      }
    if (hl > 320) hl = 320;
    for(v=start; v<vl; v++)
      {
        lbase = v*xdim;
        write_byte(0,v,0x01);
        for (h=1; h<hl; h++)
          {
            write_byte(h,v,((red[lbase+h]/43)*42) +
                         ((green[lbase+h]/37)*6)  +
                           (blue[lbase+h]/43) );
          }
      }
  }

/*
 * showregpic8()
 *
 * This function will attempt to display buffers that were (presumably)
 * passed in at the commands startup. See the -r, -g and -b options, as
 * well as the -x and -y options.
 *
 * 24 bit
 */

void showregpic8()
  {
  int h,v,hl,vl,lbase,start;
    WriteRegCookie(4,0);
    gen_even_palette(0);
    start=4;
    if (lacer) start=8;
    hl = xdim; vl = ydim; /* requested dimensions */
    if (lacer)
      {
        if (vl > 400) vl = 400;
      }
    else
      {
        if (vl > 200) vl = 200;
      }
    if (hl > 320) hl = 320;
    for(v=start; v<vl; v++)
      {
        lbase = v*xdim;
        write_byte(0,v,0x01);
        for (h=1; h<hl; h++)
          {
            write_byte(h,v,((red[lbase+h]/43)*42) +
                         ((green[lbase+h]/37)*6)  +
                           (blue[lbase+h]/43) );
          }
      }
  }

/*
 * showhampic()
 *
 * This function is a "pure" ham display routine. It does NOT use the
 * registers - only hamming techniques. It's also (if only because it's
 * in 'c') not an optomized display routine by any means. That's up to you.
 *
 *        write_byte(h,v,0x80+currr);
 *        write_byte(h,v,0xC0+currg);
 *        write_byte(h,v,0x40+currb);
 *
 * This routine expects you to have provided the RGB buffer pointers
 * (the -r -g and -b cmd line switches) and looks at those points
 * using dimensions gleaned from the -x and -y switches to generate
 * a complete HAM (no register calls at all) image. This means that
 * fringing is at a maximum; there are so many ways to allocate
 * registers for a HAM display, we just wanted to make clear how
 * HAM displays are handled in general, here. Another piece of
 * source code will detail register-fixup HAM mode handling all
 * by itself - it's not the only way, but it will be one way.
 *
 * 18 bit buffer data
 */

void showhampic6()
  {
  int h,v,hl,vl,lbase,start;
  short last_color,spinner;
  unsigned char lastr,lastg,lastb;
  unsigned char currr,currg,currb;
  unsigned char dr,dg,db;
    WriteHamCookie(4,0);
    inc_palette(0);             /* write palette at ham cookie starting row */
    last_color=0;
    start=4;
    if (lacer) start = 8;
    hl = xdim; vl = ydim; /* requested dimensions */
    if (lacer)
      {
        if (vl > 400) vl = 400;
      }
    else
      {
        if (vl > 200) vl = 200;
      }
    if (hl > 320) hl = 320;
    spinner=0;
    for(v=start; v<vl; v++)
      {
        lbase = v*xdim;
        write_byte(0,v,0x80); /* begin as BLACK */
        write_byte(0,v,0xC0);
        write_byte(0,v,0x40);
        lastr=0; lastg=0; lastb=0;
        for (h=3; h<hl; h++)
          {
            currr = red[lbase+h] & 0x3f;
            currg = green[lbase+h] & 0x3f;
            currb = blue[lbase+h] & 0x3f;
            if (currr > lastr) dr = currr-lastr; else dr = lastr - currr;
            if (currg > lastg) dg = currg-lastg; else dg = lastg - currg;
            if (currb > lastb) db = currb-lastb; else db = lastb - currb;
            if ((dr > dg) && (dr > db))
              {
                write_byte(h,v,0x80+currr);
                lastr = currr;
              }
            else if ((dg > dr) && (dg > db))
              {
                write_byte(h,v,0xC0+currg);
                lastg = currg;
              }
            else if ((db > dr) && (db > dg))
              {
                write_byte(h,v,0x40+currb);
                lastb = currb;
              }
            else if ((dr > dg) || (dr > db))
              {
                write_byte(h,v,0x80+currr);
                lastr = currr;
              }
            else if ((dg > dr) || (dg > db))
              {
                write_byte(h,v,0xC0+currg);
                lastg = currg;
              }
            else if ((db > dg) || (db > dr))
              {
                write_byte(h,v,0x40+currb);
                lastb = currb;
              }
            else
              {
                 switch(spinner)
                   {
                     case 0:
                       {
                         spinner=1;
                         write_byte(h,v,0x80+currr);
                         lastr = currr;
                         break;
                       }
                     case 1:
                       {
                         write_byte(h,v,0xC0+currg);
                         lastg = currg;
                         spinner=2;
                         break;
                       }
                     case 2:
                       {
                         write_byte(h,v,0x40+currb);
                         lastg = currb;
                         spinner=0;
                         break;
                       }
                   }
              }
          }
      }
  }

/*
 * showhampic8()
 *
 * This routine expects you to have provided the RGB buffer pointers
 * (the -r -g and -b cmd line switches) and looks at those points
 * using dimensions gleaned from the -x and -y switches to generate
 * complete HAM (no register calls at all) image. This means that
 * fringing is at a maximum; there are so many ways to allocate
 * registers for a HAM display, we just wanted to make clear how
 * HAM displays are handled in general, here. Another piece of
 * source code will detail register-fixup HAM mode handling all
 * by itself - it's not the only way, but it will be one way.
 *
 * 24 bit buffer data
 */

void showhampic8()
  {
  int h,v,hl,vl,lbase,start;
  short last_color,spinner;
  unsigned char lastr,lastg,lastb;
  unsigned char currr,currg,currb;
  unsigned char dr,dg,db;
    WriteHamCookie(4,0);
    inc_palette(0);             /* write palette at ham cookie starting row */
    last_color=0;
    start=4;
    if (lacer) start = 8;
    hl = xdim; vl = ydim; /* requested dimensions */
    if (lacer)
      {
        if (vl > 400) vl = 400;
      }
    else
      {
        if (vl > 200) vl = 200;
      }
    if (hl > 320) hl = 320;
    spinner=0;
    for(v=start; v<vl; v++)
      {
        lbase = v*xdim;
        write_byte(0,v,0x80); /* begin as BLACK */
        write_byte(0,v,0xC0);
        write_byte(0,v,0x40);
        lastr=0; lastg=0; lastb=0;
        for (h=3; h<hl; h++)
          {
            currr = (red[lbase+h]>>2) & 0x3f;
            currg = (green[lbase+h]>>2) & 0x3f;
            currb = (blue[lbase+h]>>2) & 0x3f;
            if (currr > lastr) dr = currr-lastr; else dr = lastr - currr;
            if (currg > lastg) dg = currg-lastg; else dg = lastg - currg;
            if (currb > lastb) db = currb-lastb; else db = lastb - currb;
            if ((dr > dg) && (dr > db))
              {
                write_byte(h,v,0x80+currr);
                lastr = currr;
              }
            else if ((dg > dr) && (dg > db))
              {
                write_byte(h,v,0xC0+currg);
                lastg = currg;
              }
            else if ((db > dr) && (db > dg))
              {
                write_byte(h,v,0x40+currb);
                lastb = currb;
              }
            else if ((dr > dg) || (dr > db))
              {
                write_byte(h,v,0x80+currr);
                lastr = currr;
              }
            else if ((dg > dr) || (dg > db))
              {
                write_byte(h,v,0xC0+currg);
                lastg = currg;
              }
            else if ((db > dg) || (db > dr))
              {
                write_byte(h,v,0x40+currb);
                lastb = currb;
              }
            else
              {
                 switch(spinner)
                   {
                     case 0:
                       {
                         spinner=1;
                         write_byte(h,v,0x80+currr);
                         lastr = currr;
                         break;
                       }
                     case 1:
                       {
                         write_byte(h,v,0xC0+currg);
                         lastg = currg;
                         spinner=2;
                         break;
                       }
                     case 2:
                       {
                         write_byte(h,v,0x40+currb);
                         lastg = currb;
                         spinner=0;
                         break;
                       }
                   }
              }
          }
      }
  }

/*
 * make_cie()
 *
 * maps 3-dimensional [(R:G:B)] color map to two dimensional
 * screen (memory) space. Big hack.. but it sure is pretty!
 * If the rgb buffer pointers are initialized, it'll write the
 * image to 6 bits precision to those buffers... this essentially
 * passes the color map "out" to the calling program IF the -r -g and -b
 * switches are specifed. If not, it just gets drawn to the screen.
 */

void make_cie()
  {
  int r_dim, g_dim, b_dim;
  int px,py,ymul,diag,base;
  int rd,bd,rv,bv;
  float gv,gd;
  int diag_len;
    WriteRegCookie(4,0);
    gen_even_palette(0);
    b_dim=0;
    for (r_dim=0; r_dim<64; r_dim++)
      {
        for (g_dim=0; g_dim<64; g_dim++)
          {
            px = 132 + r_dim;
            py = 132 - g_dim;
            write_RGB(px,py,r_dim << 2,g_dim << 2, b_dim <<2);
            ymul = (py*xdim)+px;
            if (red)   *(red  +ymul) = r_dim;
            if (green) *(green+ymul) = g_dim;
            if (blue)  *(blue +ymul) = b_dim;
          }
      }
    g_dim=0;
    for (r_dim=0; r_dim<64; r_dim++)
      {
        for (b_dim=0; b_dim<64; b_dim++)
          {
            px = 132 + r_dim;
            py = 132 + b_dim;
            write_RGB(px,py,r_dim << 2,g_dim << 2, b_dim <<2);
            ymul = (py*xdim)+px;
            if (red)   *(red  +ymul) = r_dim;
            if (green) *(green+ymul) = g_dim;
            if (blue)  *(blue +ymul) = b_dim;
          }
      }
    r_dim=0;
    for (g_dim=0; g_dim<64; g_dim++)
      {
        for (b_dim=0; b_dim<64; b_dim++)
          {
            px = 132 - g_dim;
            py = 132 + b_dim;
            write_RGB(px,py,r_dim << 2,g_dim << 2, b_dim <<2);
            ymul = (py*xdim)+px;
            if (red)   *(red  +ymul) = r_dim;
            if (green) *(green+ymul) = g_dim;
            if (blue)  *(blue +ymul) = b_dim;
          }
      }
    /* second quadrant: */
    b_dim=0;
    for (r_dim=0; r_dim<64; r_dim++)
      {
        for (g_dim=0; g_dim<64; g_dim++)
          {
            px = 68 + g_dim;
            py = 68 - r_dim;
            write_RGB(px,py,(63-r_dim) << 2,(63-g_dim) << 2, (63-b_dim) <<2);
            ymul = (py*xdim)+px;
            if (red)   *(red  +ymul) = (63-r_dim);
            if (green) *(green+ymul) = (63-g_dim);
            if (blue)  *(blue +ymul) = (63-b_dim);
          }
      }
    g_dim=0;
    for (r_dim=0; r_dim<64; r_dim++)
      {
        for (b_dim=0; b_dim<64; b_dim++)
          {
            px = 68 - b_dim;
            py = 68 - r_dim;
            write_RGB(px,py,(63-r_dim) << 2,(63-g_dim) << 2, (63-b_dim) <<2);
            ymul = (py*xdim)+px;
            if (red)   *(red  +ymul) = (63-r_dim);
            if (green) *(green+ymul) = (63-g_dim);
            if (blue)  *(blue +ymul) = (63-b_dim);
          }
      }
    r_dim=0;
    for (g_dim=0; g_dim<64; g_dim++)
      {
        for (b_dim=0; b_dim<64; b_dim++)
          {
            px = 68 - b_dim;
            py = 68 + g_dim;
            write_RGB(px,py,(63-r_dim) << 2,(63-g_dim) << 2, (63-b_dim) <<2);
            ymul = (py*xdim)+px;
            if (red)   *(red  +ymul) = (63-r_dim);
            if (green) *(green+ymul) = (63-g_dim);
            if (blue)  *(blue +ymul) = (63-b_dim);
          }
      }
    /* diagonal space! */
    for (base=0; base<64; base++) /* x diagonals */
      {
        diag_len = 64-base; /* <-- note! not base of ZERO */
        rd = (63/diag_len);
        gd = ((63-((float)base)) - ((float)base)) / ((float)diag_len);
        bd = (63/diag_len);
        rv = 63; gv=63-base; bv=63;
        px = base+68;
        py = 68;
        for (diag=base; diag<64; diag++)
          {
            write_RGB(px,py,rv << 2,((int)gv) << 2,bv <<2);
            ymul = (py*xdim)+px;
            if (red)   *(red  +ymul) = rv;
            if (green) *(green+ymul) = gv;
            if (blue)  *(blue +ymul) = bv;
            rv -= rd;
            gv -= gd;
            bv -= bd;
            px++; py++;
          }
      }
    for (base=0; base<64; base++) /* x diagonals */
      {
        diag_len = 64-base; /* <-- note! not base of ZERO */
        rd = (63/diag_len);
        gd = ((63-((float)base)) - ((float)base)) / ((float)diag_len);
        bd = (63/diag_len);
        rv = 63; gv=63-base; bv=63;
        px = 68;
        py = base+68;
        for (diag=base; diag<64; diag++)
          {
            write_RGB(px,py,rv << 2,((int)gv) << 2,bv <<2);
            ymul = (py*xdim)+px;
            if (red)   *(red  +ymul) = rv;
            if (green) *(green+ymul) = gv;
            if (blue)  *(blue +ymul) = bv;
            rv -= rd;
            gv -= gd;
            bv -= bd;
            px++; py++;
          }
      }
    for (base=0; base<64; base++)
      {
        write_RGB(base+68,base+68,(63-base) << 2,(63-base) << 2,(63-base) << 2);
        ymul = ((base+68)*xdim)+base+68;
        if (red)   *(red  +ymul) = 63-base;
        if (green) *(green+ymul) = 63-base;
        if (blue)  *(blue +ymul) = 63-base;
      }
  }

/*
 * This handles the various options and runs the program.
 */

runner(parm)
  int parm;
  {
  int i,j;
  unsigned char k;
    if (parm) return(parm);     /* watch for setup/parm errors */
    if (ydim > 240)
      {
        NewScreenStructure.ViewModes |= LACE;
        NewScreenStructure.Height = 400;
        lacer=1;
      }
    if (!(scr=OpenScreen(&NewScreenStructure))) return(NO_SCREEN);
    fp0 = (unsigned char*)scr->BitMap.Planes[0]; /* get quick bitplane ptrs */
    fp1 = (unsigned char*)scr->BitMap.Planes[1];
    fp2 = (unsigned char*)scr->BitMap.Planes[2];
    fp3 = (unsigned char*)scr->BitMap.Planes[3];
    j=16000;
    k=0x55;
    if (write_odd) k=0xaa;
    if (lacer) j=32000;
    for (i=0; i<j; i++) /* fills a bitplane so no C0 detection */
      {
        *(fp0+i) = k;
      }
    make_hame_palette();        /* this sets up our AMIGA palette */

    /*
     * now we're set up to run in HAM-E _or_ REG mode. what to
     * do (for this program) depends on the incoming cmd line
     * options; these set various flags - we test them now, and
     * call routines as appropriate for the flags.
     */

    if (sample) /* this is all a huge hack, here. :^) */
      {
        bugger(104);                /* generates data to view of various types */
      }
    else if (ham_reg) /* want a ham drawing */
      {
        showhampic6();
      }
    else if (get_ip) /* want to read in an IP file */
      {
        show_ip_file();
      }
    else if (luma_mode) /* want a registered, 256 grey level drawing */
      {
        show_luma_pic();
      }
    else if (chroma)
      {
        make_cie();
      }
    else /* want a register drawing */
      {
        showregpic6();
      }
    if (iff_flag) /* user want iff file saved from this??? */
      {
        WBenchToFront(); /* get WB for faster saves in chip-only amiga */
        writeiff(&iff_file[0]);
        ScreenToFront(scr);
      }
    /*
     * after display, we may want to delay - if you don't want this,
     * then place a "get_ip=1;" in your new routine.
     */
    if (!get_ip)
      {
        if (delay_time) /* set by "-d" cmd line switch */
          {
            if (delay_time < 50) delay_time=50;
            Delay(delay_time);
          }
        else
          {
            Delay(500);                 /* wait 10 seconds to see "stuff" */
          }
      }
    CloseScreen(scr);  /* and bail out. */
    return(0);
  }

/*
 * main()
 *
 * runs the whole show, more or less.
 */

void main(argc,argv)
  int argc;
  char *argv[];
  {
    cleanup(runner(init(argc,argv)));
  }


