#ifdef ALLEGRO
#define GRAPHICS 1
#define JUMPTABLE 1 /* We know we're using GCC on Win98!... */
#define CYCLES
#else
/* We're on Unix - compile for trace generation, for debugging */
/* If using the 'findbug' procedure, we need all 3 of these on: */
#define BEFORE
#define TRACING
#define CYCLES

#define SCREEN_W 1024
#define SCREEN_H 800
#endif


/* Emulator harness */

/*
   This is a cross between the sort of translating code generator that
   programs like Insignia's SoftPC on the Mac use to translate 68000
   instructions to native mode, and a manual reverse-engineering with
   a disassembler.  Basically we do a trivial disassembly followed
   by small modifications to the disassembled code which at no point
   ever allow the functionality of the code to deviate from that of
   the original assembly language.
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "ccpu.h"
#include "mdep.h"

#ifndef FALSE
#define FALSE (0!=0)
#define TRUE (0==0)
#endif

#ifdef ALLEGRO
#include "mdep.c"
#endif

int tracing = TRUE;
int debug = FALSE;
int onscreen_debug = TRUE;

#define CARRYBIT (1<<12)
#define A0BIT 1

/* Brought out here temporarily, for the convenience of debugging */
           CINEWORD  ram[256];         /* C-CPU ram (for all pages) */


/* Contorted sign-extend macro only evaluates its parameter once, and
   executes in two shifts.  This is far more efficient that any other
   sign extend procedure I can think of, and relatively safe */

#define SEX(twelvebit) ((((int)twelvebit) << (int)((sizeof(int)*8)-12)) \
                          >> (int)((sizeof(int)*8)-12))

void traceinst(char *s)
{
  int l = 24 - strlen(s);
  char ss[256];
  if (!tracing) return;
  strcpy(ss, s);
  if (strlen(ss) > 13) ss[12] = ':';
  fprintf(stderr, " | %s ", ss);
  for (; l > 0; --l) {
    fprintf(stderr, " ");
  }
  fprintf(stderr, " : ");
}

void traceregs(
  const int register_A, const int register_B,
  const int register_P, const int register_I, const int register_J,
  const int flag_C, const int acc_a0,
  const int cmp_new, const int cmp_old,
  const int ccpu_ICount
)
{
  if (!tracing) return;
  fprintf(stderr, "A=%03x B=%03x ", register_A, register_B);
  fprintf(stderr, "I=%02x J=%03x P=%01x ", register_I, register_J, register_P);
  fprintf(stderr, "a=%01x C=%01x ", acc_a0&1, (flag_C & CARRYBIT ? 1 : 0));
  fprintf(stderr, "N=%03x O=%03x c=%08d", cmp_new, cmp_old, ccpu_ICount);
}

static char extraline[256] = { '\0' };
void tracenl(void)
{
  if (!tracing) return;
  fprintf(stderr, "\n");
  if (*extraline != '\0') fprintf(stderr, "%s", extraline);
  *extraline = '\0';
}

/* INP $8 ? */
#define IO_START   0x80

/* INP $7 ? */
#define IO_SHIELDS 0x40

#define IO_FIRE    0x20
#define IO_DOWN    0x10
#define IO_UP      0x08
#define IO_LEFT    0x04
#define IO_RIGHT   0x02

#define IO_COIN   0x80

#define IO_KNOWNBITS (IO_START | IO_SHIELDS | IO_FIRE | IO_DOWN | IO_LEFT | IO_RIGHT)

/* initial value of shields (on a DIP) */
#define SW_SHIELDS80 ((1<<1) | (1<<4) | (1<<5))
#define SW_SHIELDS40 ((0<<1) | (1<<4) | (1<<5))
#define SW_SHIELDS15 ((0<<1) | (0<<4) | (0<<5))
#define SW_SHIELDS    SW_SHIELDS80

static int startflag = IO_START;
static int coinflag  = 0;
static int shieldsflag = IO_SHIELDS;   /* Whether shields are up (mouse or key) */
static int fireflag = IO_FIRE;

void reset_coin_counter(int state)
{
  /* state = 0 or 1 */
  coinflag = IO_COIN;
}
int get_coin_state(void)
{
  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(1);
}

void set_watchdog(void)
{
}

const int switches = SW_SHIELDS;  /* These don't change during a game */
static int inputs = /* These two seem wrong but are in the comparison trace 0x10 | 0x08 | */ /*1*/0;

int get_shield_bit0(void)
{
  return(SW_SHIELDS&1);
}

int get_shield_bit1(void)
{
  return((SW_SHIELDS>>1)&1);
}

int get_shield_bit2(void)
{
  return((SW_SHIELDS>>2)&1);
}

int get_switch_bit(int n)
{
  /* Only ever asks for 0, 1, 4, 5 */
  /* The function of 0 is currently unknown; the other 3 should
     are the three shield values - see below and #define's above.
   */

/*
   Shield points go down 1 per second.  Values here are inverted from #defines above
 ;
 ;   -S---SS  1---11 = 15 Shield Points
 ;            1---01 = 20 Shield Points
 ;            1---10 = 30 Shield Points
 ;            1---00 = 40 Shield Points
 ;            0---11 = 50 Shield Points
 ;            0---01 = 60 Shield Points
 ;            0---10 = 70 Shield Points
 ;            0---00 = 80 Shield Points

NOTE: The table above is stored in Rom[091C] in BCD
The code which initialises the data is at 0026:

0008: D0               STA     $0
0009: C0               LDI     $0
000A: 57               USB
000B: E6               STA     [i]
000C: 21               ADD     #$1
000D: 5D               JNC
000E: D0               STA     $0
000F: 00               CLR
0010: 21               ADD     #$1
0011: 95               OUT     $5
0012: 44 09            LDJ     #$904
0014: 58               JMP



0904: 57               USB
0905: 11               INP     $1    <--- a shield bit
0906: 57               USB
0907: EC               LSL
0908: 88               LDP     #$8
0909: 57               USB
090A: D2               STA     $2    ram[0x82] is the shield value
090B: 57               USB
090C: 14               INP     $4    <--- a shield bit
090D: 57               USB
090E: E7               ADD     [i]
090F: 57               USB
0910: EC               LSL
0911: 57               USB
0912: E6               STA     [i]
0913: 57               USB
0914: 15               INP     $5    <--- a shield bit
0915: 57               USB
0916: E7               ADD     [i]
0917: 57               USB
0918: E6               STA     [i]
0919: 46 20            LDJ     #$026
091B: 58               JMP

jumps back here:
0026: 09               LDA     #$900
0027: 20 1C            ADD     #$1C
0029: E7               ADD     [i]      <--- I must point to a byte containing 0..7
002A: E2               XLT
002B: 5F               NOP
002C: E6               STA     [i]

*/
  return((switches >> 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);
}

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?

 */
  if (how == 1) {
    /* Set a bit */
    inputs = inputs | (1<<pos);
  } else {
    /* Clear a bit */
    inputs = inputs & (~(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;
}

static int Frames = 0, LastFrames = 0;
static int FPS;
static int pleasedisplay = FALSE;
static clock_t now = 0, then = 0;
static clock_t nowtick = 0, thentick = 0;

void CinemaClearScreen(void)
{
  static int parity = 0, slowdown = 0;
  int mx, my;
  parity ^= 1;
  if (parity) {

    Frames++;
#ifdef ALLEGRO
    /* Calculate FPS */
    nowtick = clock(); now = nowtick/CLOCKS_PER_SEC;
    if (now != then) {
      then = now;
      FPS = Frames - LastFrames;      
      LastFrames = Frames;
    }  
    mousecode = 0;
    get_mouse_mickeys(&mickeyx, &mickeyy);
    mx = mickeyx; my = mickeyy;
    mousecode |= IO_LEFT; mousecode |= IO_RIGHT;

    if (mx > xmousethresh) mousecode &= ~IO_RIGHT;
    if ((-mx) > xmousethresh) mousecode &= ~IO_LEFT;

    if (my > ymousethresh) mousecode |= IO_UP; else mousecode &= ~IO_UP;
    if (-my > ymousethresh) mousecode |= IO_DOWN; else mousecode &= ~IO_DOWN;

    if (mouse_b & 1) fireflag &= ~IO_FIRE; else fireflag |= IO_FIRE;
    if (mouse_b & 2) shieldsflag &= ~IO_SHIELDS; else shieldsflag |= IO_SHIELDS;

    if (onscreen_debug) {char msg[256];
      sprintf(msg, "M %02x  S %02x  Mx %04d  My %04d  Snd %0d/%0d  FPS %d",
        mousecode, ram[0x82], mx, my, sound_addr, sound_data, FPS);
      /* the mouse button state is stored in the variable mouse_b */
      textout(buffer, font, msg, 16, 440, 15);
    }

#ifdef NEVER
    if (pleasedisplay) {
      /*vsync();*/ /* remove this and the vsync below to see just how fast the game can run!  (WOW!) */
      blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H); 
      pleasedisplay = FALSE;
    }
#endif
    vsync(); /* remove this and the vsync below to see just how fast the game can run!  (WOW!) */
    blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H); 

    if (keypressed()) {
      int k = readkey() & 0xff;
      if (k == '1') startflag = 0;
      if (k == '3') coinflag = 0;
      if (k == ' ') shieldsflag = IO_SHIELDS;
	if (k == 27) exit(0);
    } else {
      startflag = IO_START; /* Bodge.  1/FPS key debouncing! */
    }
    clear(buffer);
#ifdef NEVER
    if (nowtick != thentick) {
      clear(buffer);
      pleasedisplay = TRUE;
      thentick = nowtick;
    }
#endif
#else
    fprintf(stdout, "CLEAR: %d\n", Frames);
#endif
  } else {
    /* Doing every second vsync() here makes the speed match that
       of Retrocade (and I presume the original?) almost exactly. */
#ifdef ALLEGRO
    if ((slowdown ^= 1) == 0) vsync();
#endif
  }
}
static int MinX=1000, MinY=1000, MaxX=0, MaxY=0;
void xCinemaVectorData(int FromX, int FromY, int ToX, int ToY, int vgColour, int register_PC)
{
  /*Remove stars: if ((FromX == ToX) && (FromY == ToY)) return;*/

  FromX = (FromX * 2) / 3; ToX = (ToX * 2) / 3;
  FromY = (FromY * 2) / 3; ToY = (ToY * 2) / 3;
  FromX -= 32; ToX -= 32;
  FromY = SCREEN_H-FromY; ToY=SCREEN_H-ToY;
#ifndef GRAPHICS
    fprintf(stdout, "LINE: From (%0d,%0d) To (%0d,%0d) Col %0d [0x%04x]\n",
      FromX, FromY, ToX, ToY, vgColour, register_PC);
#endif
  if (FromX < MinX) MinX = FromX;
  if (ToX < MinX) MinX = ToX;
  if (FromY < MinY) MinY = FromY;
  if (ToY < MinY) MinY = ToY;
  if (FromX > MaxX) MaxX = FromX;
  if (ToX > MaxX) MaxX = ToX;
  if (FromY > MaxY) MaxY = FromY;
  if (ToY > MaxY) MaxY = ToY;
#ifdef ALLEGRO
  line(buffer,FromX,FromY,ToX,ToY,vgColour);
#endif
}
#define CinemaVectorData(FromX, FromY, ToX, ToY, vgColour) xCinemaVectorData(FromX, FromY, ToX, ToY, vgColour, register_PC)

void xUNFINISHED(char *s, int register_PC)
{
  fprintf(stderr, "%04x DANGER: Unimplemented instruction - %s\n",
    register_PC, s);
  exit(1);
}
#define UNFINISHED(s) xUNFINISHED(s, register_PC)

int main(int argc, char **argv) {
  /* We do read the eprom contents into memory, in case the code is
     pulling data out of the rom, but we do not execute from this copy
     (unless we later discover that there is some self-modifying code
     going on, but that's unlikely in this architecture since it's
     stored in an eprom :-) )
   */


  clock_t before, after;

#include "dispatch.h"

  const CINEBYTE rom[0x2000] =
#include "tailgunner-data.c"
                               /* Preload whole eprom here */

/* WE DO NOT YET ACTUALLY LOAD THE EPROM!!! */

  /* Map the most comonly used registers to real registers.  Don't map
     things like the PC to a register because in this scheme it's
     seldom written to - we save making changes until it is stored in
     ram (ie setting up a subroutine) or when we make a jump to a
     new basic block.  The choice of which CCPU register is slaved
     to a real register should be made after extensive profiling.
     However a good compiler would probably get it right for you if
     you omit the 'register' qualifier anyway... so use with caution.
     Note we have the emulator to use to gather profiling data accurately.
   */

  /* C-CPU context information begins --  */
           CINEWORD  register_PC = 0;  /* C-CPU registers; program counter */
  register CINEWORD  register_A = 0;   /* A-Register (accumulator) */
  register CINEWORD  register_B = 0;   /* B-Register (accumulator) */
           CINEWORD /*CINEBYTE*/  register_I = 0;   /* I-Register (last access RAM location) */
           CINEWORD  register_J = 0;   /* J-Register (target address for JMP opcodes) */
           CINEWORD /*CINEBYTE*/  register_P = 0;   /* Page-Register (4 bits, shifts to high short nibble for code, hight byte nibble for ram) */
           CINEWORD  FromX = 0;        /* X-Register (start of a vector) */
           CINEWORD  FromY = 0;        /* Y-Register (start of a vector) */
           CINEWORD  register_T = 0;   /* T-Register (vector draw length timer) */
           CINEWORD  flag_C = 0;       /* C-CPU flags; carry. Is word sized, instead
                                        * of CINEBYTE, so we can do direct assignment
                                        * and then change to BYTE during inspection.
                                        */

           CINEWORD  cmp_old = 0;      /* last accumulator value */
           CINEWORD  cmp_new = 0;      /* new accumulator value */
           CINEWORD/*CINEBYTE*/  acc_a0 = 0;       /* bit0 of A-reg at last accumulator access */

           CINESTATE state = state_A;  /* C-CPU state machine current state */

           int       ccpu_jmi_dip = 0; /* as set by cineSetJMI  Use EI, not MI */
           int       ccpu_msize = 0;   /* as set by cineSetMSize */
           int       ccpu_monitor = 0; /* as set by cineSetMonitor */
           int       ccpu_ICount = 0;  /* */

           CINEBYTE  vgShiftLength = 0;   /* number of shifts loaded into length reg */
           CINEWORD  vgColour = 0;

           int bNewFrame = 0;
           int bailOut = 0;
           int bFlipX = 0;
           int bFlipY = 0;
           int bSwapXY = 0;
           int sdwGameXSize = 0;
           int sdwGameYSize = 0;
           int sdwXOffset = 0;
           int sdwYOffset = 0;

  /* -- Context information ends. */

  before = clock();
#ifdef GRAPHICS
  init_graph();
#endif

#ifdef JUMPTABLE
  for (;;) {
#endif
  for (;;) {

#ifdef TRACING
    traceregs(register_A, register_B,
              register_P, register_I, register_J,
              flag_C, acc_a0, cmp_new, cmp_old, ccpu_ICount);

    tracenl();

                /* We came here after a jump, so didn't print the regs
                   after the instruction.  (Side effect of the hacky
                   code for printing a trace - trying to keep it *out*
                   of macros.h) */
#endif

#ifdef GRAPHICS
    if (FALSE /* Invent a suitable termination condition here */) {
      end_graph();
#else
    if (/*ccpu_ICount > 184000000*/Frames >= (10*48)) {
#endif
      after = clock();
      fprintf(stderr, "Executed %d cycles in %d ticks (%d ticks per sec, %d Frames)\n",
              ccpu_ICount, after-before, CLOCKS_PER_SEC, Frames);
      fprintf(stderr, "MinX %d MinY %d MaxX %d MaxY %d\n",
               MinX, MinY, MaxX, MaxY);
      exit(0);
    }

#ifdef JUMPTABLE
    {
#include "tailgunr-ops.c"
    Lelse:
#else
    switch(register_PC) { /* Need opcodes to be addressable for jumps */
#include "tailgunr-ops.c"
      ;
      default:
#endif
        /* Jumping to any illegal address or end of eprom will come here */
        fprintf(stderr, "Error: control was passed to 0x%04x (%d)\n",
          register_PC, register_PC);
        {FILE *errfile = fopen("error.log", "w");
        if (errfile != NULL) {
          fprintf(errfile, "Error: control was passed to 0x%04x (%d)\n",
            register_PC, register_PC);
          fclose(errfile);
        }
        }
        exit(1); /* Illegal instruction */
    }
  }
#ifdef JUMPTABLE
  }
#endif

  exit(0);
  return(0);
}

