/* #define DUALCPU to create output suitable for dual CPU debugging */

/************************************************************************
 *                                                                      *
 * Cinematronics Binary Code Translator                                 *
 *                                                                      *
 * This program is used to generate a disassembled listing of a program	*
 * written for the Cinematronics CPU cards in a way such that if        *
 * recompiled with a C compiler it will generate a working program.	*
 *                                                                      *
 * Author:	Zonn Moore                                                  *
 * Hacked by:	Graham Toal                                           *
 * Version:	0.1.9                                                       *
 * Date:	21Jan00                                                     *
 *                                                                      *
 * Revision History:                                                    *
 * -----------------                                                    *
 *                                                                      *
 * Version 0.1, 01/03/00                                                *
 *    Initial GT release.                                               *
 *                                                                      *
 * Conversion from disassember to translator, 01/02/00			*
 *    Using routines from MAME CCPU emulator                            *
 *                                                                      *
 * Version 1.0, 01/31/97                                                *
 *    Initial release.                                                  *
 *                                                                      *
 * Version 1.1, 07/31/97                                                *
 *    Removed '[j]' from JMP instructructions.                          *
 *                                                                      *
 * Released to public domain by the author, distribute freely.		*
 ************************************************************************/

#include	<stdio.h>

#include	<stdlib.h>

#include	<string.h>

#include	<ctype.h>

#include    <assert.h>


#ifndef __MSDOS__

#include	<unistd.h>

#include	<fcntl.h>

#include	<sys/types.h>

#endif


#ifndef TRUE

#define TRUE (0==0)

#define FALSE (0!=0)

#endif



#define PASS_1  1

#define PASS_2  2

#define PASS_3  3


/* We do several passes over the program, to mark the rom addresses
   with special info, such as 'this is a jump target, so forget
   all cached register values', or 'this instruction requires
   the previous instruction to remember intermediate accumulator
   results' which would otherwise be removed by optimisation.
   Unfortunately I've realised there are cases where each pass
   affects code generation and that it may not even ever converge :-(
 */

int pass = PASS_1;

/*

   The TAG variables are used to set bits in an array whose size
   is the same number of words as the roms are bytes.  They are
   set on PASS1 which does not generate any code, and are used
   to modify code generation in PASS2

 */

/* This opcode requires that cmp_old is valid */
#define TAG_REQUIRES_CMP_OLD 1


/* This opcode requires that cmp_new is valid */
#define TAG_REQUIRES_CMP_NEW  2


/* This opcode must make a note of the next state at runtime */
#define TAG_NOTESTATE 4


/* This opcode is the target of a jump from state_X 
   If it is an ACC instruction and gets jumpers from different
   states, we must generate different code depending on the state.
   All jump destinations should set all registers to DEADBEEF for
   now which will turn off unsafe optimisations */

#define TAG_JUMPTARGET_A 8

#define TAG_JUMPTARGET_AA 16

#define TAG_JUMPTARGET_B 32

#define TAG_JUMPTARGET_BB 64


#define TAG_OPCODE 128


#define TAG_JUMPINST 256


#define TAG_JUMPTARGET (TAG_JUMPTARGET_A|TAG_JUMPTARGET_AA|TAG_JUMPTARGET_B|TAG_JUMPTARGET_BB)


#include "ccpu.h"


#define CARRYBIT (1<<12)

#define A0BIT 1


CINESTATE state;

/*

  The real PC is only 12 bits, but it has an immutable top 4 bits
  representing the current rom page.  In this code I am making
  the PC 16 bits rather than 12 and storing the current rom number
  in there with it.  Note that the top bits are NOT the same as
  the P register below, because P is only transferred into the PC
  when the longjump instructions are executed.

  Any instructions that save the PC must mask it with 0xFFF as they
  do so.  I'm not sure which instruction that is but I believe
  there is one, for building subroutines out of.

  Loads of the PC that are *not* longjumps should preserve the top
  4 bits of the extended PC register, so as not to accidentally
  drop out of the current rom page.

 */

unsigned long register_PC; /* address of instruction currently examined */

/*

  When we set P we set it to what the CCPU really thinks it is.  However
  I'm considering also setting P_rompage to P<<12 and P_rampage to P<<4
  for use in instructions which use P, to save shifting them on read;
  i.e. we generally read these registers more often than we set them.

 */

unsigned long register_P; /* cached value of Page register from previous LDP # */

unsigned long register_J; /* cached value of jump register from previous LDJ # */
unsigned long register_I; /* cached value of Index register from previous LDI # */
unsigned long register_A; /* cached value of A register from previous LDAA # */
unsigned long register_B; /* cached value of A register from previous LDAB # */
unsigned long this_page;  /* top 4 bits of PC.  May not keep this variable */

/* known variables that are always constant for tailgunner */
unsigned long ccpu_monitor = CCPU_MONITOR_BILEV; /* arcade monitor type */
unsigned long ccpu_msize = CCPU_MEMSIZE_8K;      /* ram for tailgunner board */
unsigned long ccpu_jmi_dip = 0; /* EI or MI - Tailgunner is EI */


/* Remember if you change these, to change tailgunr.c to match! */
/* Really should be a run-time parameter */

#ifdef __MSDOS__

int comments = FALSE; /* Currently don't generate quite so many comments */
int tracing = FALSE; /* run-time opcode/register trace */
int before = FALSE; /* run-time opcode/register trace format tweak */
int debug = FALSE;   /* run-time machine debug */
int jumptableflag = TRUE;
#ifdef SUPEROPTIMISE

int disp_opcodes = FALSE; /* Currently don't generate quite so many comments */
int slave_pc = FALSE; /* Helps slightly when diagnosing callback routines */
#else

int disp_opcodes = TRUE; /* Currently don't generate quite so many comments */
int slave_pc = FALSE; /* Helps slightly when diagnosing callback routines */
#endif

#else

int comments = TRUE; /* Currently don't generate quite so many comments */
int tracing = TRUE; /* run-time opcode/register trace */
int before = TRUE; /* run-time opcode/register trace format tweak */
int debug = FALSE;   /* run-time machine debug */
int disp_opcodes = TRUE; /* Currently don't generate quite so many comments */
int jumptableflag = FALSE;
int slave_pc = TRUE; /* Helps slightly when diagnosing callback routines */
#endif


#ifdef SUPEROPTIMISE

int loggingCycles = FALSE; 
#else

/* We currently need this one in order to do benchmark timing calculations */
int loggingCycles = FALSE; /* Don't keep a runtime count of cycles burned */
#endif

#include "opcodes.h"


#ifndef __MSDOS__

/* Some DOS emulation procedures since this code didn't come from Unix */
void strlwr(char *s)
{
  int c;
  if (s == NULL) return;
  for (;;) {
    c = *s++;
    if (c == '\0') break;
    if (isalpha(c) && isupper(c)) {
      c = tolower(c);
    }
  }
}

off_t filelength(int fd)
{
  off_t curpos = lseek(fd, 0, SEEK_CUR);
  off_t l = lseek(fd, 0, SEEK_END);
  lseek(fd, curpos, SEEK_SET);
  return(l);
}
#endif


unsigned long context_register_A[0x2000];   /* A-Register (accumulator) */
unsigned long context_register_B[0x2000];   /* B-Register (accumulator) */
unsigned long context_register_I[0x2000];   /* I-Register (last access RAM location) */
unsigned long context_register_J[0x2000];   /* J-Register (target address for JMP opcodes) */
unsigned long context_register_P[0x2000];   /* Page-Register (4 bits, shifts to high short nibble for code, hight byte nibble for ram) */


/* This procedure MUST be kept in synch with the one in optimise.c */
void load_invariants()
{
    FILE *context_file = fopen("tailgunr.opt", "r");
    int i, rc;
    /* Initialise everything to 'unknown' */
    for (i = 0; i < 0x2000; i++) {
      context_register_A[i] = 0xFEEDBEEFUL;
      context_register_B[i] = 0xFEEDBEEFUL;
      context_register_I[i] = 0xFEEDBEEFUL;
      context_register_J[i] = 0xFEEDBEEFUL;
      context_register_P[i] = 0xFEEDBEEFUL;
    }
    if (context_file != NULL) {
      int tot = 0;
      /* All executions are merged over the long term */
      for (;;) {
        /* Read in results of previous executions */
        rc = fscanf(context_file, "L%x ", &i);
        if (rc != 1) break;
        tot += 1;
        fscanf(context_file, "A %x ", &context_register_A[i]);
        fscanf(context_file, "B %x ", &context_register_B[i]);
        fscanf(context_file, "I %x ", &context_register_I[i]);
        fscanf(context_file, "J %x ", &context_register_J[i]);
        fscanf(context_file, "P %x\n", &context_register_P[i]);
      }
      fclose(context_file);
      fprintf(stderr, "OPTIMISE: pulled in %d previous records\n", tot);
    }
 }

/* Formatting help */

char cur_tabs[256];

void push_indent(char *s)
{
  strcat(cur_tabs, s);
}

void pop_indent(char *s)
{
  cur_tabs[strlen(cur_tabs)-strlen(s)] = '\0';
}

/* Define some common data types */

typedef unsigned char	uchar;

/* This stuff is exbedded from main() */
int		inFile, err, ii;
static char	fileName[131];
int		nameLen, romSize, startAdr, endAdr, romidx, objidx;
int		oldStart, oldEnd;
long		tempSize;
unsigned int	objSize, brk_f;
char	       *gamename;

/* Testing: was 12 */
#define	TAB_OPCODE	14		/* Column to place opcode */

#define	TAB_PARAM	(TAB_OPCODE+5)	/* Column to place parameters */


/* Default filname extensions */

#define	EXT1		".t7"

#define	EXT2		".p7"

#define	EXT3		".u7"

#define	EXT4		".r7"


#include "disasm.h"


static uchar rom[0x2000] =
#include "tailgunner-data.c"


FILE    *codefile; /* Dump code on final pass to codefile */
unsigned long	*romFlags;/* Special hints for optimiser */
char	 OpcBfr[81];  /* Temp buffer used to build opcodes */
char	 DspBfr[81];  /* Line display buffer */
uchar	 Extd_f;      /* Extended diss flag, if set display extended insts. */

#define internal_test(o) do_internal_test(o, __LINE__, __FILE__)

void do_internal_test(int opcode, const int line, const char *file)
{
  /* This is called at the top of every opcode procedure.  Good opportunity
     to do things that always need done... */
  if (pass == PASS_1) {
    romFlags[register_PC] |= TAG_OPCODE;
  }
  if (opcode != rom[register_PC]) {
    fprintf(stderr, "Consistency error at line %0d in %s\n", line, file);
    exit(1);
  }
  if (loggingCycles) {
    fprintf(codefile, "%sccpu_ICount += %0d;\n", cur_tabs, ccpu_cycles[opcode]);
  }
  /* If turning off register optimisations, set the regs to DEADBEEF here ... */
}

int require_note_state(int register_PC)
{
  return(romFlags[register_PC] & TAG_NOTESTATE);
}


/* Short macros */

/* May soon take these and put the expansions inline */

void explore(int startAdr, int endAdr);

char *Jump(void) {
#ifdef DUALCPU

#define DOIT "return"

#else

#ifdef SUPEROPTIMISE

#define DOIT "goto *lab[register_PC]"

#else

#define DOIT (jumptableflag == 1 ? "goto *lab[register_PC]" : "break")

#endif

#endif

  int savedpc;
  static char buffer[256];
  if (register_J != 0xDEADBEEFUL) {
    savedpc = (register_J | (register_PC & 0xF000));
    explore(savedpc, endAdr);
    if (loggingCycles) {
#ifdef SUPEROPTIMISE

      sprintf(buffer, "{ccpu_ICount -= 2; goto L%04x;}", savedpc);
#else

      sprintf(buffer, "{register_PC = 0x%04x; ccpu_ICount -= 2; %s;}", savedpc, DOIT);
#endif

      /* correct instruction execution count for jump taken */
    } else {
#ifdef SUPEROPTIMISE

      sprintf(buffer, "goto L%04x", savedpc);
#else

      sprintf(buffer, "{register_PC = 0x%04x; %s;}", savedpc, DOIT);
#endif

    }
    if (savedpc == 0xDEADBEEF) {
      fprintf(stderr, "*** BIG PROBLEM!  PC=deadbeef\n");
    } else {
      romFlags[register_PC = savedpc] |= TAG_JUMPTARGET; /* For now.  Needs be more selectiove */
    }
  } else {
    int offset;
    if (register_PC != 0xDEADBEEFUL) {
      if ((register_PC & (~0xfff)) == 0) {
        offset = sprintf(buffer, "{register_PC = register_J;");
      } else {
        offset = sprintf(buffer, "{register_PC = 0x%04x | register_J;", register_PC & 0xf000);
      }
      if (loggingCycles) offset = sprintf(buffer+offset, " ccpu_ICount -= 2;");
      sprintf(buffer+offset, " %s;}", DOIT);
    } else {
      offset = sprintf(buffer, "{register_PC = (register_PC & 0xf000) | register_J;");
      if (loggingCycles) offset = sprintf(buffer+offset, " ccpu_ICount -= 2;");
      sprintf(buffer+offset, " %s;}", DOIT);
    }
    /* correct instruction execution count for jump taken */
    fprintf(stderr, "Unknown jump at 0x%04x\n", register_PC); /* register_PC can be deadbeef!  Shouldn't be, at translate time... */
  }
  return(buffer);
}

char *SAR(char *var, char *arg) {
  static char buffer[128];
  sprintf(buffer, "((signed short int)(((signed short int)%s) >> (signed short int)%s)) /* SAR */", var, arg);
  /* ANSI C paranoia here */
  return(buffer);
}

#include "disopc.h"


#include "macros.h"


int started = 0;

void explore(int startAdr, int endAdr)
{
unsigned long saved_PC = register_PC,
              saved_J = register_J,
              saved_I = register_I,
              saved_A = register_A,
              saved_B = register_B;

/* What about register_P ???? */

if (startAdr == 0x1000) fprintf(stderr, "EXPLORING 1000\n");

  if ((pass == PASS_2) && (started == 1)) return;

  if ((pass == PASS_2) && (startAdr == 0) && (endAdr == 0x1fff) && (started == 0)) {
    started = 1;
  }
  while (startAdr <= endAdr)
  {
      int caselabel = startAdr;
if (startAdr == 0x1000) fprintf(stderr, "DECODING 1000\n");

      /* Kill recursion on pass1 - we've already encoded from here */
      if ((pass == PASS_1) && ((romFlags[startAdr] & TAG_OPCODE) != 0)) {
        break;
      }
      /* Skip code generation for any instructions which can't be reached */
      if ((pass == PASS_2) && ((romFlags[startAdr] & TAG_OPCODE) == 0)) {
        for (;;) {
          if (startAdr == 0x1000) break;

          if (romFlags[startAdr] & TAG_OPCODE) break;

          startAdr += 1;
	}
        caselabel = startAdr;
      }
      if (startAdr > endAdr) break;
      if (startAdr > 0x1fc7 /* Tailgunner hack */) break;
      /* Do I need to DEADBEEF everything here??? */

      memset(DspBfr, ' ', 80);				/* set to blanks */
      DspBfr[80] = '\0';

      /* dissOpcode sets register_PC for no good reason.  Would be 
         better done here, no? */
      dissOpcode(DspBfr, startAdr, &objSize, &brk_f);	/* setup buffer */

      for (ii = 79; DspBfr[ii] == ' '; ii--)
        ;

      DspBfr[ii+1] = '\0';											/* terminate line */

      if (((startAdr & 0xFFF) == 0x000) && (startAdr != 0)) {
        /* start of a new rom bank */
        fprintf(codefile, "/*********************************************************/\n");
      }

      if ((romFlags[startAdr] & TAG_JUMPTARGET) != 0) {
        register_P = 0xDEADBEEFUL;  /* We don't yet have code to merge contexts at jump destinations */
        register_I = 0xDEADBEEFUL;
        register_A = 0xDEADBEEFUL;
        register_B = 0xDEADBEEFUL;
        /*register_J = 0xDEADBEEFUL;*/ /* Actually this one *IS* known, but paranoia for now... */
#ifdef DUALCPU

      }
#endif


	  fprintf(codefile, "\n");
#ifdef DUALCPU

        fprintf(codefile, "      return;\n\n");
#endif

        if (jumptableflag) {
	    fprintf(codefile, "L%04x:\n", caselabel);
	  } else {
          fprintf(codefile, "case 0x%04x:\n", caselabel);
	  }
        if (comments) fprintf(codefile, " /* romflags %04x */", romFlags[startAdr]);
        fprintf(codefile, "\n");
#ifdef DETECT_INVARIANTS

        fprintf(codefile, "      note_invariants(0x%04x);\n", caselabel);
#endif

#ifdef USE_INVARIANTS

        if (context_register_P[caselabel] != 0xFEEDBEEFUL) {
          fprintf(codefile, "      /* Invariants: register_P = 0x%01x", context_register_P[caselabel]);
          if (context_register_I[caselabel] != 0xDEADBEEF) fprintf(codefile, " register_I = 0x%02x", context_register_I[caselabel]);
          if (context_register_A[caselabel] != 0xDEADBEEF) fprintf(codefile, " register_A = 0x%02x", context_register_A[caselabel]);
          fprintf(codefile, " */;\n");
          register_P = context_register_P[caselabel]; /* Tell optimiser! */
          register_I = context_register_I[caselabel];
          register_A = context_register_A[caselabel];
        }
#endif

#ifndef DUALCPU

      }
#endif

      if (comments) {
        fprintf(codefile, "%s/* %s */\n", cur_tabs, DspBfr);
      }

      if (((startAdr & 0xFFF) == 0x000) && (startAdr != 0)) {
        /* start of a new rom bank */
        fprintf(codefile, "%sstate = state_A; /* Even if it's not! :-) */\n", cur_tabs);
      }

      if (tracing) {
        if (before) {
          fprintf(codefile, "#ifdef BEFORE\n");
          fprintf(codefile, "%straceregs(register_A, register_B,
            register_P, register_I, register_J, flag_C, acc_a0, cmp_new,  cmp_old, ccpu_ICount);\n", cur_tabs);
          fprintf(codefile, "#endif\n");
	}
        fprintf(codefile, "#ifdef TRACING\n");
        fprintf(codefile, "%straceinst(\"%s\");\n", cur_tabs, DspBfr);
        fprintf(codefile, "#endif\n");
      }


      /* This may not be the right test here but it'll do for now */
      if (require_note_state(register_PC)) {

        /* When we're not sure of the state at compile time, 
           generate code to do the right thing at run time ... */

        fprintf(codefile, "%sif (state == state_A) {\n", cur_tabs);
          push_indent("  ");
          cineops[state_A][opCode](opCode);
          pop_indent("  ");
        fprintf(codefile, "%s} else if (state == state_AA) {\n", cur_tabs);
         push_indent("  ");
          cineops[state_AA][opCode](opCode);
          pop_indent("  ");
        fprintf(codefile, "%s} else if (state == state_B) {\n", cur_tabs);
          push_indent("  ");
          cineops[state_B][opCode](opCode);
          pop_indent("  ");
        fprintf(codefile, "%s} else if (state == state_BB) {\n", cur_tabs);
          push_indent("  ");
          cineops[state_BB][opCode](opCode);
          pop_indent("  ");
        fprintf(codefile, "%s} else {\n", cur_tabs);
        fprintf(codefile, "%s  /* error */\n", cur_tabs);
        fprintf(codefile, "%s}\n", cur_tabs);

      } else {

        /* we need these damned brackets because of declarations
           inside the ops.  Would prefer to fix it there. */

/*        fprintf(codefile, "%s{\n", cur_tabs);*/
        cineops[state][opCode](opCode);
/*        fprintf(codefile, "%s}\n", cur_tabs);*/

      }

      /* return on all jumps, because the rest of the code would have
         been tagged by the recursive call to 'explore' in the jump
         procedures */
      if ((pass == PASS_1) && ((romFlags[startAdr] & TAG_JUMPINST) != 0)) break; /* end recursion */

      if (tracing) {

        /* trace should show regs in entry, instr, regs on exit -
           except when the opcode itself prints something to stderr */

        fprintf(codefile, "#ifdef TRACING\n");
        fprintf(codefile, "%straceregs(register_A, register_B,
          register_P, register_I, register_J, flag_C, acc_a0, cmp_new, cmp_old, ccpu_ICount);\n", cur_tabs);
        fprintf(codefile, "%stracenl();\n", cur_tabs);
        fprintf(codefile, "#endif\n");
      }

      if ((pass == PASS_1) && (((startAdr + objSize) & 0xFFF) < (startAdr & 0xFFF)) ) {
        /* don't follow code over a page boundary on exploratory pass */
        break;
      }

      startAdr += objSize;

/*
      if (brk_f)
        putchar('\n');  /-* print break *-/
 */
  }
  register_PC = saved_PC;
  register_J = saved_J;
  register_I = saved_I;
  register_A = saved_A;
  register_B= saved_B;
}

int main(int argc, char **argv)
{

  /* Should all command line options for debug, tracing etc */

  gamename = "tailgunr";
  startAdr = 0x0000; endAdr = 0x1FFF;
  Extd_f = 0;					/* reset flags */
  strcpy(cur_tabs, "      ");

  load_invariants();

  romFlags = (unsigned long *)calloc(0x2000, 4);	/* Flags */

  if (romFlags == 0) {
    fputs("Not enough memory for rom flags!\n", stderr);
    exit(0);
  }

  /* get file name and read in ROMs */

  nameLen = strlen(gamename);		/* get length of filename */

  memcpy(fileName, gamename, nameLen);	/* get games name */

  /* open EXT1 (lower, even, ROM) */

  romSize = 0x2000;

  /* Also set these inside the loop on any label, or inside the
     opcode procedures whenever a register is altered other
     than by a constant */

  romFlags[0] = TAG_JUMPTARGET_A;

  oldStart = startAdr; oldEnd = endAdr;

  for (pass = PASS_1; pass <= PASS_2; pass++) {

    fprintf(stderr, "Compilation pass %d; start = %04x  end = %04x\n", pass, startAdr, endAdr);

    state = state_A;
    /* Actually on entry they're all 0 in the emulator */
    register_P = 0xDEADBEEFUL;
    register_I = 0xDEADBEEFUL;
    register_A = 0xDEADBEEFUL;
    register_B = 0xDEADBEEFUL;
    this_page = 0x0000;

    startAdr = oldStart; endAdr = oldEnd;

    if (pass == PASS_1) {
      codefile = fopen("devnull", "w"); /* unix-dependant :-( */
    }

    explore(startAdr, endAdr);
    explore(0x1000, endAdr);

    /* BUG! */
    /* Well, apparently there _are_ some calculated jumps in this program,
       and one of them (at 1480:) jumps to 1600:.  We need to not only
       explore the code, but ensure that a label is dumped at that
       address.  we should probably do the same for *all* data segments
       which follow unconditional jumps.  But that is for another
       night... I need to go sleep now... */


    {
      FILE *jumps = fopen("tailgunr.jmp", "r");
      char line[128], *s;
      long d;
      int i=0;

      /* Now that we know all the jump dests, this needs to be cleaned up */

      romFlags[0x1600] |= TAG_JUMPTARGET; explore(0x1600, endAdr);
      romFlags[0x1671] |= TAG_JUMPTARGET; explore(0x1671, endAdr);
      romFlags[0x168f] |= TAG_JUMPTARGET; explore(0x168f, endAdr);
      romFlags[0x180f] |= TAG_JUMPTARGET; explore(0x180f, endAdr);
      romFlags[0x182d] |= TAG_JUMPTARGET; explore(0x182d, endAdr);
      romFlags[0x184b] |= TAG_JUMPTARGET; explore(0x184b, endAdr);
      romFlags[0x1878] |= TAG_JUMPTARGET; explore(0x1878, endAdr);
      romFlags[0x18a0] |= TAG_JUMPTARGET; explore(0x18a0, endAdr);
      romFlags[0x198f] |= TAG_JUMPTARGET; explore(0x198f, endAdr);
      romFlags[0x19d7] |= TAG_JUMPTARGET; explore(0x19d7, endAdr);
      romFlags[0x19fb] |= TAG_JUMPTARGET; explore(0x19fb, endAdr);
      romFlags[0x1a0b] |= TAG_JUMPTARGET; explore(0x1a0b, endAdr);

      if (jumps == NULL) {
        fprintf(stderr, "We need a tailgunr.jmp file containing integers, one per line\n");
        exit(1);
      }

      for (;;) {
        s = fgets(line, 127, jumps);
        if (s == NULL) break;
        i += 1;
        s = strchr(line, '\n'); if (s != NULL) *s = '\n';
        s = strchr(line, '\r'); if (s != NULL) *s = '\n';
        d = atol(line);
        fprintf(stderr, "Added from file: %d ('%s', 0x%04x)\n", (int)d, line, (int)d);
        romFlags[(int)d] |= TAG_JUMPTARGET; explore((int)d, endAdr);
      }
      if (i == 0) {
        fprintf(stderr, "tailgunr.jmp appears to be empty\n");
        exit(1);
      }
    }

    if (pass == PASS_1) {
      FILE *dispatchfile;
      fclose(codefile);
#ifdef DUALCPU

      codefile = fopen("tailgunr-exec.c", "w"); /* unix-dependant, and tailgunr-dependent :-( */
      fprintf(codefile, "void gtExec(void) {\n#include \"dispatch-exec.h\"\n\n  goto *lab[register_PC];\n");
      dispatchfile = fopen("dispatch-exec.h", "w"); /* unix-dependant, and tailgunr-dependent :-( */
#else

      codefile = fopen("tailgunr-ops.c", "w"); /* unix-dependant, and tailgunr-dependent :-( */
      if (jumptableflag) fprintf(codefile, "goto *lab[register_PC];\n");
      dispatchfile = fopen("dispatch-ops.h", "w"); /* unix-dependant, and tailgunr-dependent :-( */
#endif

      romFlags[0x1000] |= TAG_JUMPTARGET_A;
      {
        int labad;
	  if (jumptableflag) {
          fprintf(dispatchfile, "static void *lab[0x2000] = {\n");
          for (labad = 0; labad < 0x2000; labad++){
#ifdef DUALCPU

            if ((romFlags[labad] & TAG_OPCODE) != 0) {
#else

            if ((romFlags[labad] & TAG_JUMPTARGET) != 0) {
#endif

              fprintf(dispatchfile, "&&L%04x, ", labad);
  	      } else {
              fprintf(dispatchfile, "&&Lelse, ", labad);
            }
            if ((labad&7) == 7) fprintf(dispatchfile, "\n");
          }
          fprintf(dispatchfile, "};\n");
        }
      }
      fclose(dispatchfile);
    }
  }
#ifdef DUALCPU

  fprintf(codefile, "      return;\nLelse: UNFINISHED(\"ELSE\");\n} /* End of gtExec() */\n");
#endif

  fclose(codefile);

  fprintf(stderr, "ROMFLAGS[1000] = %04x\n", romFlags[0x1000]);

  exit(0);
  return(0);
}