#ifndef _MDEP_C
#define _MDEP_C 1
/* 
 *    Example program for the Allegro library, by Shawn Hargreaves.
 *
 *    This program demonstrates how to use the 3d matrix functions.
 *    It isn't a very elegant or efficient piece of code, but it does 
 *    show the stuff in action. I'll leave it to you to design a proper 
 *    model structure and rendering pipeline: after all, the best way to 
 *    do that sort of stuff varies hugely from one game to another.
 */

#include <stdarg.h>
void ERROR(char *fmt, ...)
{
  va_list ap;
  if (!debugf) {
    debugf = fopen("retro.crash", "w");
  }
  va_start(ap, fmt);
  (void) vfprintf(debugf, fmt, ap);
  va_end(ap);
  if (debugf) fclose(debugf);
  exit(1);
}

void reset_coin_counter(int RCstate)
{
  coinflag = IO_COIN;
  ioSwitches |= IO_COIN;
}

int get_coin_state(void)
{
  if (((ioSwitches >> 7) & 1) != ((coinflag >> 7) & 1)) {
    ERROR("ioSwitches and coinflag not in synch");
  }
  return((coinflag >> 7) & 1);
}

int get_quarters_per_game(void)
{
/*   Bit 0 of switches = 1:  1 Quarter per game
     Bit 0 of switches = 0:  2 Quarters per game  ... these don't match the documentation!
 */
  return(quarterflag & 1);
}

void set_watchdog(void)
{
}

int get_shield_bit0(void)
{
  return((SW_SHIELDS>>5)&1);
}

int get_shield_bit1(void)
{
  return((SW_SHIELDS>>4)&1);
}

int get_shield_bit2(void)
{
  return((SW_SHIELDS>>1)&1);
}

int get_switch_bit(int n)
{

/* BUG!!! switches is not the same variable as ioSwitches !!! */

  return((ioSwitches >> n) & 1);
}

/* Mouse values currently sampled once per frame and set in WAI code.
   This is not ideal. */

const int xmousethresh = 2;
const int ymousethresh = 2;
static int mickeyx = 0;
static int mickeyy = 0;
static int mousecode = 0;

int get_io_bit(int n)
{
  return(((startflag  | shieldsflag  | fireflag | mousecode /* | inputs */ ) >> n) & 1);
}

int get_io_moveright(void)
{
  return(((startflag  | shieldsflag  | fireflag | mousecode /* | inputs */ ) >> 1) & 1);
}
int get_io_moveleft(void)
{
  return(((startflag  | shieldsflag  | fireflag | mousecode /* | inputs */ ) >> 2) & 1);
}
int get_io_moveup(void)
{
  return(((startflag  | shieldsflag  | fireflag | mousecode /* | inputs */ ) >> 3) & 1);
}
int get_io_movedown(void)
{
  return(((startflag  | shieldsflag  | fireflag | mousecode /* | inputs */ ) >> 4) & 1);
}
int get_io_fire(void)
{
  return(((startflag  | shieldsflag  | fireflag | mousecode /* | inputs */ ) >> 5) & 1);
}
int get_io_shields(void)
{
  return(((startflag  | shieldsflag  | fireflag | mousecode /* | inputs */ ) >> 6) & 1);
}
int get_io_startbutton(void)
{
  return(((startflag  | shieldsflag  | fireflag | mousecode /* | inputs */ ) >> 7) & 1);
}

void put_io_bit(int pos, int how)
{
  /*
	Tail Gunner sound info - doesn't appear to be invoked?

	Output 0 = A         ABC is an address to load Data into
	Output 1 = B          A is least significant
	Output 2 = C
	Output 3 = Data
	Output 4 = /Strobe

	ABC / Trigger
	0 / Explosion
	1 / Rumble
	2 / Laser
	3 / Shield
	4 / Shield Bounce
	5 / HyperSpace
	6 / Start Button LED
   */

/*
      The order of calling these procedures to send a sound appears to be:
pos
3
0 1 2
4 4

I suspect the strobe (4) is executed twice to start the sound then stop the sound?

 */

/* BUG: There is inputs and ioInputs !!! */

  if (how == 1) {
    /* Set a bit */
    ioInputs = ioInputs | (1<<pos);
  } else {
    /* Clear a bit */
    ioInputs = ioInputs & (~(1<<pos));
  }
}

static int sound_addr = 0;
static int sound_addr_A = 0;
static int sound_addr_B = 0;
static int sound_addr_C = 0;
static int sound_data = 0;
void set_sound_data(int bit)
{
  sound_data = bit;
}

void set_sound_addr_A(int bit)
{
  sound_addr_A = bit;
}

void set_sound_addr_B(int bit)
{
  sound_addr_B = bit;
}

void set_sound_addr_C(int bit)
{
  sound_addr_C = bit;
}

/* These don't yet do anything obvious or useful.  Debugging needed... */
void strobe_sound_on(void)
{
  int sound_addr_tmp = (sound_addr_C<<2) | (sound_addr_B<<1) | sound_addr_A;
  sound_addr = sound_addr_tmp;
}

void strobe_sound_off(void)
{
  int sound_addr_tmp = (sound_addr_C<<2) | (sound_addr_B<<1) | sound_addr_A;
  sound_addr = sound_addr_tmp;
}

#ifdef ALLEGRO
#include "allegro.h"

static BITMAP *buffer;
#endif

void init_graph(void)
{
#ifdef ALLEGRO 
  PALLETE pal;
  int c = 256;
  int w = 640;
  int h = 480;
  int bpp = 8;
  int last_retrace_count;
  int i;

  allegro_init();
  install_keyboard();
  install_mouse();
  install_timer();

  for (i=0; i<16; i++)
    pal[i].r = pal[i].g = pal[i].b = 0;

  /* greyscale */
  pal[16].r = pal[16].g = pal[16].b = 63;
  pal[17].r = pal[17].g = pal[17].b = 48;
  pal[18].r = pal[18].g = pal[18].b = 32;
  pal[19].r = pal[19].g = pal[19].b = 16;
  pal[20].r = pal[20].g = pal[20].b = 8;

  for (i=0; i<16; i++)   /* red range */
  {
    pal[i+32].r = 31+(i*2);
    pal[i+32].g = 15;
    pal[i+32].b = 7;
  }
  for (i=64; i<68; i++)
  {
    pal[i].r = 63;      /* a nice fire orange */
    pal[i].g = 17+((i-64)*3);
    pal[i].b = 0;
  }

  set_color_depth(bpp);

  if (set_gfx_mode(c, w, h, 0, 0) != 0) {
     allegro_exit();
     printf("Error setting graphics mode\n%s\n\n", allegro_error);
     return;
  }

  if (gfx_driver->id == GFX_MODEX) {
     allegro_exit();
     printf("Can't draw 3d polygons in mode-X, sorry!\n\n");
     return;
  }

  /* Tailgunner: */
  for (i=1; i<8; i++)   /* normal */
  {
    pal[i].r = pal[i].g = pal[i].b = 48;
  }
  for (i=8; i<16; i++)  /* bright */
  {
    pal[i].r = pal[i].g = pal[i].b = 63;
  }

  set_palette (pal);

  /* double buffer the animation */
  buffer = create_bitmap(SCREEN_W, SCREEN_H);

  /* set up the viewport for the perspective projection */
  set_projection_viewport(0, 0, SCREEN_W, SCREEN_H);

  last_retrace_count = retrace_count;
  clear(buffer);
#endif
}

void end_graph(void)
{
#ifdef ALLEGRO
   destroy_bitmap(buffer);
#endif
}


static FILE *dumpfile = NULL;
void PUTBYTE(int c)
{
  fputc(c, dumpfile);
}
void PUTWORD(int c)
{
  fputc(c>>8, dumpfile);
  fputc(c, dumpfile);
}
int xGETBYTE(void)
{
  return(fgetc(dumpfile));
}
#define GETBYTE(varname) varname = xGETBYTE()
int xGETWORD(void)
{
  return((fgetc(dumpfile)<<8) | fgetc(dumpfile));
}
#define GETWORD(varname) varname = xGETWORD()

#define GETSTATE GETBYTE
#define PUTSTATE PUTBYTE

void save_config(void)
{
  int i;
/*#ifdef DUALCPU*/
  dumpfile = fopen("tailgunr.dmp", "wb");
  if (dumpfile == NULL) return;
  PUTWORD(register_PC); /* C-CPU registers; program counter */
  PUTWORD(register_A);  /* ; A-Register (accumulator) */
  PUTWORD(register_B);  /* ; B-Register (accumulator) */
  PUTBYTE(register_I);  /* ; I-Register (last access RAM location) */
  PUTWORD(register_J);  /* ; J-Register (target address for JMP opcodes) */
  PUTBYTE(register_P);  /* ; Page-Register (4 bits) */
  PUTWORD(FromX);       /* ; X-Register (start of a vector) */
  PUTWORD(FromY);       /* ; Y-Register (start of a vector) */
  PUTWORD(register_T);  /* ; T-Register (vector draw length timer) */
  PUTWORD(flag_C);      /* C-CPU flags; carry. Is word sized, instead
                         * of RCCINEBYTE, so we can do direct assignment
                         * and then change to BYTE during inspection.
                         */

  PUTWORD(cmp_old);     /* last accumulator value */
  PUTWORD(cmp_new);     /* new accumulator value */
  PUTBYTE(acc_a0);      /* bit0 of A-reg at last accumulator access */

  PUTSTATE(state); /* C-CPU RCstate machine current RCstate */
  for (i = 0; i < 256; i++) {
    PUTWORD(ram[i]);     /* C-CPU RCram (for all pages) */
  }
  PUTBYTE(vgShiftLength); /* number of shifts loaded into length reg */
  PUTWORD(ioSwitches);
  PUTWORD(ioInputs);
  PUTWORD(coinflag);
  PUTWORD(startflag);
  PUTWORD(shieldsflag);
  PUTWORD(fireflag);
  fclose(dumpfile);
/*#endif*/
}

int load_config(void)
{
  int i;
/*#ifdef DUALCPU*/
  dumpfile = fopen("tailgunr.dmp", "rb");
  if (dumpfile == NULL) return(FALSE);
  GETWORD(register_PC); /* C-CPU registers; program counter */
  GETWORD(register_A);  /* ; A-Register (accumulator) */
  GETWORD(register_B);  /* ; B-Register (accumulator) */
  GETBYTE(register_I);  /* ; I-Register (last access RAM location) */
  GETWORD(register_J);  /* ; J-Register (target address for JMP opcodes) */
  GETBYTE(register_P);  /* ; Page-Register (4 bits) */
  GETWORD(FromX);       /* ; X-Register (start of a vector) */
  GETWORD(FromY);       /* ; Y-Register (start of a vector) */
  GETWORD(register_T);  /* ; T-Register (vector draw length timer) */
  GETWORD(flag_C);      /* C-CPU flags; carry. Is word sized, instead
                         * of RCCINEBYTE, so we can do direct assignment
                         * and then change to BYTE during inspection.
                         */

  GETWORD(cmp_old);     /* last accumulator value */
  GETWORD(cmp_new);     /* new accumulator value */
  GETBYTE(acc_a0);      /* bit0 of A-reg at last accumulator access */

  GETSTATE(state); /* C-CPU RCstate machine current RCstate */
  for (i = 0; i < 256; i++) {
    GETWORD(ram[i]);     /* C-CPU RCram (for all pages) */
  }
  GETBYTE(vgShiftLength); /* number of shifts loaded into length reg */
  GETWORD(ioSwitches);
  GETWORD(ioInputs);
  GETWORD(coinflag);
  GETWORD(startflag);
  GETWORD(shieldsflag);
  GETWORD(fireflag);
  GETWORD(quarterflag);
  fclose(dumpfile);
/*#endif*/
#ifdef DUALCPU
  RCregister_PC = register_PC; /* C-CPU registers; program counter */
  RCregister_A = register_A;  /* ; A-Register (accumulator) */
  RCregister_B = register_B;  /* ; B-Register (accumulator) */
  RCregister_I = register_I;  /* ; I-Register (last access RAM location) */
  RCregister_J = register_J;  /* ; J-Register (target address for JMP opcodes) */
  RCregister_P = register_P;  /* ; Page-Register (4 bits) */
  RCFromX = FromX;       /* ; X-Register (start of a vector) */
  RCFromY = FromY;       /* ; Y-Register (start of a vector) */
  RCregister_T = register_T;  /* ; T-Register (vector draw length timer) */
  RCflag_C = flag_C;      /* C-CPU flags; carry. Is word sized, instead
                         * of RCCINEBYTE, so we can do direct assignment
                         * and then change to BYTE during inspection.
                         */

  RCcmp_old = cmp_old;     /* last accumulator value */
  RCcmp_new = cmp_new;     /* new accumulator value */
  RCacc_a0 = acc_a0;      /* bit0 of A-reg at last accumulator access */

  RCstate = state; /* C-CPU RCstate machine current RCstate */
  for (i = 0; i < 256; i++) {
    RCram[i] = ram[i];     /* C-CPU RCram (for all pages) */
  }
  /* We should clone this one too! RCvgShiftlength = vgShiftLength; */  /* number of shifts loaded into length reg */
#endif
  return(TRUE);
}

void cineReset(void) {
#ifdef DUALCPU
  /* zero registers */
  RCregister_PC = 0;
  RCregister_A = 0;
  RCregister_B = 0;
  RCregister_I = 0;
  RCregister_J = 0;
  RCregister_P = 0;
  RCFromX = 0;
  RCFromY = 0;
  RCregister_T = 0;

  /* zero flags */
  RCflag_C = 0;

  /* reset RCstate */
  RCstate = 0;

  /* reset RCram*/
  memset(RCram, 0, sizeof(RCram));

  /* reset internal RCstate */
  RCcmp_old = 0;
  RCcmp_new = 0;
  SETA0 ( 0 );
#endif
}


UINT32 cineGetElapsedTicks ( UINT32 dwClearIt ) {
  UINT32 dwTemp;
#ifdef DUALCPU
  dwTemp = dwElapsedTicks;
  if ( dwClearIt ) {
         dwElapsedTicks = 0;
  }
#endif
  return ( dwTemp );
}

#endif /* _MDEP_C */

