// If checking the logic of opcode covers, the original emulator on which this
// was based is currently in 6809-fallback.c

// turn this on occasionally and make sure regression tests still working OK
// Caught by cputest:
//#define TESTING_ERROR_DETECTION_1 1
// Caught by sorenroug test (I haven't actually found a test yet that succeeds in cputest and fails in sorenroug test)
// - wait a minute... I know, use the broken TFR B,X code! (TO DO)
//#define TESTING_ERROR_DETECTION_2 1

/*

    6809.c    - cycle accurate simple 6809 cpu emulator
    Copyright (C) 2001  Arto Salmi
                        Joze Fabcic
    modification into a static binary translator
    Copyright (c) 2020  Graham Toal (Last edited $Date: 2021/11/02 03:03:59 $)

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    $Log: 6809.c,v $
    Revision 1.27  2021/11/02 03:03:59  gtoal
    remove EFI
    add a few brackets around expressions to simplify for subset-C parser

    Revision 1.26  2021/11/01 00:46:37  gtoal
    another expr simplified

    Revision 1.25  2021/11/01 00:14:31  gtoal
    making cosmetic changes to generated C code, so that my new mini-C parser can read it.

    Revision 1.24  2021/10/28 07:36:12  gtoal
    added some comments around code that needs to be checked for a problem Malban discovered

    Revision 1.23  2021/10/28 07:26:17  gtoal
    Added a flush to bra() - temporary. There may be other places where one is needed too...

    Revision 1.22  2021/10/28 06:41:04  gtoal
    two bugfixes fed back from Malban's work on starwars

    Revision 1.21  2021/06/11 22:08:14  gtoal
    Adding RCS headers

*/

const char *SBT_Version = "6809.c: $Revision: 1.27 $";

/*

These are the types that the run-time for the generated code uses:

typedef uint32_t UINT32;
typedef int32_t SINT32;
typedef uint16_t UINT16;
typedef int16_t SINT16;
typedef uint8_t UINT8;
typedef int8_t SINT8;

UINT8 memory[0x10000], *memory_DP = &memory[0xD000];

// Modify this header to suit your target...
SINT32 res, A, B, D, C;
UINT16 PC, X, Y, S, U, Z, DP, arg, ea, val;
UINT8 E, F, I, N, H, V, CC, msn, lsn;

- there are several redundant casts in the generated code which
I should remove... by default most things will be unsigned; only
a few casts to SINTx will be needed.
 */

/*
    This module generates the C translations of 6809 opcodes, as detected
    by 6809dasm.c. ( http://gtoal.com/vectrex/6809sbt/6809dasm.c.html )
    We *only* handle a single opcode here, passed to us from the disassembler.
    That keeps this code quite clean and totally separate from the mess that
    6809dasm has become! (that module probably deserves a rewrite)

    The code uses the def/use chain technique described in my Static Binary
    Translation HOWTO ( http://gtoal.com/sbt/ ) to reduce a lot of unnecessary
    flag setting.  However a lot more optimisation could still be done - but may
    not be worth the effort as this now looks acceptable.  (Actually it wouldn't
    be unreasonable to omit those optimisations altogether and allow the compiler
    to handle them behind the scenes, but doing them here looks better as long
    as they are done correctly.)

    The initial implementation used the HOWTO's technique of passing the def/use
    register info to 'dumpf' explicitly.  It should however be possible to infer
    that info just from the code string, with a little care being taken to catch
    dependencies on the left-hand side of assignments such as memory[X] = Y - in
    this case both X and Y are 'use' registers.  (And memory is an 'Always' def,
    if you were wondering...)  We've made small steps in that direction by using
    the R_lookup() procedure quite heavily.

    The sequence of handling statements here is marginally problematic - although
    the def/use chain eliminates redundant loads, it still leaves the problem of
    constant expression folding unhandled.  To do that properly, I think I need
    to parse the generated code strings as proper C expressions!  And then be
    very careful with what is remembered and what is forgotten when a flush() is
    executed.  I'm definitely tempted to attempt that optimisation.  Remembered
    registers may also include symbolic substitution as well as constant folding,
    so that ea = X; Y = ea becomes Y = X...  as long as 'ea' is not used again.
    Whether that needs a second layer of def/use handling or just some care in
    writing the opcode covers, remains to be seen...

    Although we don't handle Phi nodes yet (and probably never will), a lot of
    optimisation can be done locally:
    1) follow through paths from entry to a procedure until return at the same
       level, note which regs and flags are used as inputs (or not used at all)
       and avoid flushing flags which get overwritten in the procedure
    2) half-carry flag only ever used in DAA instruction, so if no DAA used,
       never flush the half-carry.  If it is used, work back to where it was
       set and as long as it is linear, keep those but drop any others.
       Possibly can be done by replacing 'maybe used half carry' bit with
       'definitely used half carry' bit in def/use flags?
    3) technique of working backwards from where flags are used may be
       useful in more places that just H/DAA...
    4) remembering of values when assigned - even symbolic, not necessarily
       for constants - might allow great simplification of comparisons and
       branches, e.g. "if (memory[S--] > 0x45) ... instead of what it does
       generate.  Might only work for ==0 != 0 at first, care needs to be
       taken with signedness for more complex tests.


These are mostly from Minestorm derivatives, reusing
some of the Minestorm internal code.

Warning: undetected bios call to e7b5 from 04a6
Warning: undetected bios call to e7d2 from 0826
Warning: undetected bios call to e8e3 from 10c6
Warning: undetected bios call to e98a from 0585
Warning: undetected bios call to ea3e from 04a1
Warning: undetected bios call to ea6d from 05e5
Warning: undetected bios call to ea9d from 0bcd
Warning: undetected bios call to eaa8 from 0bdc
Warning: undetected bios call to eacf from 12f4
Warning: undetected bios call to eaf0 from 0144

I may have solved the transitive closure problem...
When chaining backwards looking for remaining registers in def/use chain,
whenever you come to a label, that label has to be tagged with all the
places that the code could have come from. (as in orion data flow)
So instead of just going linearly backwards, you also need to go backwards
though all possible 'come from' locations as well.
However if you ever hit a location that you've already visited
during this traverse, stop. (at least for that branch) to avoid
recursion caused by loops.

Conclusion - have entire program in memory, not just
the current buffer up to a flush point.

    GT 2020/03/29
 */

/*
There are certainly a few places in this code where I've taken
some shortcuts, and implemented features with quick hacks of which
I'm not proud - but the majority of those are in places where I
knew what was needed to implement the features properly - they
just needed a much more complex infrastructure to be in place
which wasn't feasible during the early stages of development
while so much was still not working.  For those hacks, I had
plans to reimplement them properly later once a basic level
of functionality was working.

HOWEVER, one problem did surface which was caused by a lack
of foresight at the design stage, and it appears that
the temporary solutions I have tried are just making it
worse.  That is the area of pre-decremented and post-incremented
registers:

                ADDA    ,-X
                ADDA    ,--X
                ADDA    ,-Y
                ADDA    ,--Y
                ADDA    ,-U
                ADDA    ,--U
                ADDA    ,-S
                ADDA    ,--S
                ADDA    [,--X]
                ADDA    [,--Y]
                ADDA    [,--U]
                ADDA    [,--S]
                ADDA    ,X+
                ADDA    ,X++
                ADDA    ,Y+
                ADDA    ,Y++
                ADDA    ,U++
                ADDA    ,S++
                ADDA    [,X++]
                ADDA    [,Y++]
                ADDA    [,U++]
                ADDA    [,S++]

The code to handle these needs to be completely rewritten.
for any operation, the pre-decrement register operations
should be handled first; then the operation performed as
if no pre-decrement is present, then on completion, any
post-increments should be performed.

This will unfortunately generate less concise or readable
code, but that is the price that has to be paid for
correctness.  Tidying up the generated C may be done
as a post-processing pass if required, but if so, that
will necessitate a new infrastructure for handling
multiple statements over several lines, i.e. a proper
C expression parser and an AST.  So be it.

I think I've now completed this, but will leave this comment
here until I've had a little longer to look at the code and
confirm it is OK...

*/

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "6809.h"

extern int rom_start, rom_end; // communicate with 6809dasm.c
int HTOI(char *s) {
  int I = 0;
  while (*s != '\0') {
    int c = *s++;
    if (('0' <= c) && (c <= '9')) {
      I = (I << 4) | (c - '0');
    } else if (('a' <= c) && (c <= 'f')) {
      I = (I << 4) | (c - 'a' + 0xa);
    } else if (('A' <= c) && (c <= 'F')) {
      I = (I << 4) | (c - 'A' + 0xA);
    } else {
      fprintf(stderr, "Bad hex: %s\n", s);
    }
  }
  return I;
}


// register sizes in translated code not the same! The generated code now relies
// primarily on casting (eg (UINT8)Reg) as a substitute for masking (Reg&0xff)
// (although not 100% consistently so revisit that later)

unsigned X, Y, S, U, PC;
unsigned A, B, DP;
unsigned CC, H, N, Z, V, C;
unsigned E, F, I;
unsigned iPC, CodePTR, NextPC;

/*
Notes:
   Whenever you assign to A or B, set D = (A<<8) | B (no longer true)
   Whenever CC is read from, consolidate flags into CC
   Whenever CC is written to, split CC into flags
   Flags are lazily evaluated -
     canonicalisation is only needed when converting in or out of CC;
     tests of flag bits are done similar to "(C != 0)" or "(N < 0)"

   If assignments to variables can be remembered symbolically, then
      A = mem[...]; N = A; if (N < 0) ...
   might end up being translated as
      A = mem[...]; if (A < 0) ...
   or if we're very lucky, 
      if (mem[...] < 0) ...
   which would more closely match the original intent.

   The scheme above, of keeping individual CC flags separate,  is believed
   to be more suitable to a translator which handled redundant loads/
   dead stores than one which uses lookup tables to update all the CC bits
   within a combined CC at once. (cf 6502 and z80 handling in Orion SBT)

   Eventually, constant values in registers will be remembered and folded,
     so optimising writes to PC *should* fall out in the wash and not need
     a special case handler - but initially that's what it will get.
   For now almost all PC manipulations are from values of PC known at
   translate time.  If/when we add a fallback interpreter for handling
   unknown code, some extra effort may need to be put in to the handling
   of the PC...

   Same *ought* to apply to 'clockticks' in an ideal world.  At the moment
   clockticks are pretty much ignored because the code to add clock ticks
   depending on conditional execution is quite broken.

   There are some internal variables which are handled like registers
     for optimisation purposes.  If we run out of bits (32 max for now)
     in the def/usr code, we can overlay any variables that are only used
     within a single opcode cover, i.e. written to, read from, then never
     accessed again.  For example msn and lsn in DAA, and tmp1 and tmp2 in EXG.
     However for simplicity it may just be easier to move to 64 bit 'long long's.

   ALL short opcodes that use DP need DP in the RHS of the def/use params.

 */

char *UC(char *lc) {
  int i=0; // try to copy in place, if that doesn't work create a buffer to copy into.
  while (lc[i] != '\0') {
    if ((isalpha(lc[i])) && (islower(lc[i]))) lc[i] = toupper(lc[i]);
    i++;
  }
  return lc;
}

UINT8 *memory = NULL;
UINT8 *dpmem = NULL;

#ifndef FALSE
#define FALSE (0!=0)
#define TRUE  (0==0)
#endif

#define MAX 40960
long defs[MAX], uses[MAX];
char *codes[MAX];
int nextfree = 0;

int ishex(int c) {
  return ('0' <= c && c <= '9')
      || ('A' <= c && c <= 'F')
      || ('a' <= c && c <= 'f');
}

int R_lookup(char *expr) {
  // TO DO!  '//' and '/*' ... '*/' comments in code strings
  static char varname[128];
  char *s, *v;
  int Regs = 0;
  // single letter: A B D X Y S U H N Z V C
  // two letter: PC DP CC EA 0x?????
  // three letter: val res msn lsn arg
  // multi: clockticks
  // Unless we end up parsing complete expressions, some of those above will never be seen in a call to R_lookup()
  // Need to handle both kinds of comments if they appear in 'expr'
  s = expr;
  for (;;) {
    if (*s == '\0') break;
    if ((s[0] == 'c') && (strncmp(s, "clockticks", strlen("clockticks")) == 0)) {
      Regs |= R_CLOCKTICKS; s += strlen("clockticks"); continue;
    }
    if (s[0] == '0' && s[1] == 'x' && ishex(s[2])) {
      s += 2; while (ishex(s[0])) s+=1; continue;
    } else if (!isalnum(s[1])) {
      if (s[0] == 'A') { Regs |= R_A; s += 1; continue; }
      if (s[0] == 'B') { Regs |= R_B; s += 1; continue; }
      if (s[0] == 'D') { Regs |= R_D; s += 1; continue; }
      if (s[0] == 'X') { Regs |= R_X; s += 1; continue; }
      if (s[0] == 'Y') { Regs |= R_Y; s += 1; continue; }
      if (s[0] == 'S') { Regs |= R_S; s += 1; continue; }
      if (s[0] == 'U') { Regs |= R_U; s += 1; continue; }
      if (s[0] == 'H') { Regs |= R_H; s += 1; continue; }
      if (s[0] == 'N') { Regs |= R_N; s += 1; continue; }
      if (s[0] == 'Z') { Regs |= R_Z; s += 1; continue; }
      if (s[0] == 'V') { Regs |= R_V; s += 1; continue; }
      if (s[0] == 'C') { Regs |= R_C; s += 1; continue; }
      if (s[0] == 'E') { Regs |= R_E; s += 1; continue; }
      if (s[0] == 'F') { Regs |= R_F; s += 1; continue; }
      if (s[0] == 'I') { Regs |= R_I; s += 1; continue; }
    } else if (!isalnum(s[2])) {
      if (s[0] == 'P' && s[1] == 'C') { Regs |= R_PC; s += 2; continue; }
      if (s[0] == 'D' && s[1] == 'P') { Regs |= R_DP; s += 2; continue; }
      if (s[0] == 'C' && s[1] == 'C') { Regs |= R_CC; s += 2; continue; }
      if (s[0] == 'e' && s[1] == 'a') { Regs |= R_EA; s += 2; continue; }
    }
    if (isalpha(s[0])) {
      v = varname;
      while (isalnum(*v++ = s[0])) s += 1;
      *--v = '\0';
      if (strcmp(varname, "val") == 0) {
        Regs |= R_VAL;
      } else if (strcmp(varname, "res") == 0) {
        Regs |= R_RES;
      } else if (strcmp(varname, "msn") == 0) {
        Regs |= R_MSN;
      } else if (strcmp(varname, "lsn") == 0) {
        Regs |= R_LSN;
      } else if (strcmp(varname, "arg") == 0) {
        Regs |= R_ARG;
      } else if (strcmp(varname, "clockticks") == 0) {
        Regs |= R_CLOCKTICKS;
      } else if (strcmp(varname, "memory") == 0) { // Is memory_DP handled properly yet? Should activate DP reg...
        Regs |= R_Always; // should be in the dest not the src regs...
      } else if (strcmp(varname, "UINT8") == 0) { // not relevant
      } else if (strcmp(varname, "SINT8") == 0) {
      } else if (strcmp(varname, "UINT16") == 0) {
      } else if (strcmp(varname, "SINT16") == 0) {
      } else {
        fprintf(stderr, "R_lookup: '%s' not handled in '%s'.\n", varname, expr);
      }
    } else if (*s == '\0') {
      break;
    } else s += 1; // skip anything else.
  }
  return Regs;
}
  
void dumpf(int def, int use, char *s, ...);

static int clockticks = 0, cond_clockticks = 0;
void CK (int ticks)
{
  if (Do_Timing) dumpf(R_CLOCKTICKS, R_CLOCKTICKS, "clockticks = clockticks + %d;", ticks);
}

/* conditional_clockticks would do a dumpf to set clockticks equal
to the sum of clockticks plus the parameter but doesn't update
clockticks itself.  For use in conditional branches.
Ideally delta-clockticks chain starts afresh on each basic block
then gets added to absolute clockticks at end of BB (RTS, JUMPs etc)
"To do..." */

void conditional_clockticks (int ticks)
{
   cond_clockticks += ticks;
}

void flush(long regs)
{
   int i;
   long common_regs;

   for (i = nextfree - 1; i >= 0; --i) {
     if ((common_regs = (regs & defs[i])) != 0) {
        /* if bits in 'defs' then this needs to be dumped. */
        // fprintf(stdout, "// %02d lookfor: %02x Item %d - def %02x\n", nextfree, regs, i, defs[i]);
         defs[i] |= R_Always;   /* flag this line for printing */
         regs &= ~common_regs;  /* don't need to look for these ones any more */
      }
      if (defs[i] & R_Always)
         regs |= uses[i];       /* Any unconditionals + parents */
   }

   fflush(stdout);
   for (i = 0; i < nextfree; i++) {
#ifdef DEBUG
      fprintf (stdout, "/* %03d [def:%04x] [use:%04x] */", i, defs[i], uses[i]);
#endif
      if (Gen_C) {
        if (strncmp(codes[i], "case ", 5) == 0) {
          fprintf (stdout, "  %s%s\n", (defs[i] & R_Always ? "" : " // "), codes[i]);
        } else if ((defs[i] & R_Always) == 0) {
          if (Single_Step) {
            fprintf (stdout, "/**/%s\n", codes[i]); // we can't optimise in a dual cpu situation...
          } else {
            fprintf (stdout, " // %s\n", codes[i]);
          }
        } else if (strncmp(codes[i], "     ", 5) == 0) {
          fprintf (stdout, "  %s\n", codes[i]);
        } else {
          fprintf (stdout, "    %s\n", codes[i]);
        }
      } else {
        fprintf (stdout, "%s%s\n", (defs[i] & R_Always ? "" : " // "), codes[i]);
      }
      /* should free strings here... */
   }
   fflush(stdout);
   nextfree = 0;
}

void unconditional_flush(char *inst)
{
   flush(R_FLUSH);
   fprintf (stdout, "          clockticks += %d; /*poll_nmi(opadd);*/\n", clockticks);
   clockticks = 0; cond_clockticks = 0;
   fprintf (stdout, "          %s\n", inst);
   nextfree = 0;
}

void conditional_flush(char *inst)
{
   flush(R_FLUSH);
   if (cond_clockticks) fprintf (stdout, "              clockticks += %d;\n", clockticks + cond_clockticks);
   // TO DO: fprintf (stdout, "              poll_nmi(opadd); /* if (...) return to dispatch loop - PC is address of NMI proc - real PC is on stack */\n");
   cond_clockticks = 0;
   fprintf (stdout, "          %s\n", inst);
   nextfree = 0;
}

static char *reg = NULL;
static int post_increment = 0;
void set_post(char *r, int incr) {
  reg = r; post_increment = incr;
}
void post(void) { // output the delayed post-increment for indexed() arguments.
                  // pre-decrements have already been handled more sneakily.
  if (reg && post_increment) dumpf(R_Always|R_lookup(reg), R_lookup(reg), "%s = %s + %d;", reg, reg, post_increment);
  reg = NULL; post_increment = 0;
}

signed short SEX8(UINT8 tmp);

unsigned int imm_byte(void)
{
  return memory[NextPC-1];
}

char *imm_byte_s(void)
{
  static char tmp[16];
  sprintf (tmp, "(SINT8)0x%02x", (int8_t)memory[NextPC-1]&0xff);
  return strdup (tmp);
}

int imm_word(void)
{
  return (memory[NextPC-2] << 8) | memory[NextPC-1];
}

char *imm_word_s(void)
{
  static char tmp[7];
  sprintf (tmp, "0x%04x", ((memory[NextPC-2] << 8) | memory[NextPC-1])&0xffff);
  return strdup (tmp);
}

void
push_stack8(char *stack, char *data)
{
  // Malban wants the auto-increment/decrements separated from the memory access -
  //  not sure why, but there's no reason not to, so here it is (and in other similar cases)
  dumpf(R_Always|R_lookup(stack), R_lookup(stack),                "%s = %s - 1;", stack, stack);
  dumpf(R_Always,                 R_lookup(stack)|R_lookup(data), "memory[%s] = %s;", stack, data);
  CK (1);
}

void
pop_stack8(char *stack, char *dest) // stack adjustment done by caller
{
  CK (1);
  // Malban wants the auto-increment/decrements separated from the memory access -
  //  not sure why, but there's no reason not to, so here it is (and in other similar cases)
  dumpf(R_Always|R_lookup(dest),  R_lookup(stack), "%s = memory[%s];", dest, stack);
  dumpf(R_Always|R_lookup(stack), R_lookup(stack), "%s = %s + 1;", stack, stack);
}

char *
RDMEM(char *addr)
{
  static char tmp[256];
  sprintf (tmp, "memory[%s]", addr); // bypassing the use/def chain?????
  return strdup (tmp);
}

char *
RDMEM16(char *addr)
{
  //static char tmp[256];
  CK (1);
  if (strncmp(addr, "DP|", 3) == 0) {
    // this is ugly and we unpick it later which is a dirty hack.
    addr = addr+strlen("DP|");
    if (strncmp(addr, "(UINT16)", strlen("(UINT16)")) == 0) {
      addr[5] = '8';addr[6] = ')';addr[7] = ' ';
    }
    if (strncmp(addr, "0x", 2) == 0) {
      dumpf(R_EA|R_Always, R_DP, "ea = memory[DP|0x%02x]<<8;", HTOI(addr+2));
      dumpf(R_EA|R_Always, R_DP, "ea = ea | memory[DP|0x%02x];", (SEX8(HTOI(addr+2))+1)&0xff);
    } else {
      dumpf(R_EA|R_Always, R_DP|R_lookup(addr), "ea = memory[DP|%s]<<8;", addr);
      dumpf(R_EA|R_Always, R_DP|R_lookup(addr), "ea = ea | memory[DP|(UINT8)((SINT8)%s + 1))];", addr);
    }
  } else {
    if (strncmp(addr, "0x", 2) == 0) {
      dumpf(R_EA|R_Always, 0, "ea = memory[0x%04x]<<8;", HTOI(addr+2));
      dumpf(R_EA|R_Always, 0, "ea = ea | memory[0x%04x];", HTOI(addr+2)+1);
    } else {
      dumpf(R_EA|R_Always, R_lookup(addr), "ea = memory[%s]<<8;", addr);
      dumpf(R_EA|R_Always, R_lookup(addr), "ea = ea | memory[(UINT16)((%s)+1)];", addr);
    }
    // sprintf (tmp, "((memory[%s]<<8)|memory[(UINT16)((%s)+1)])", addr, addr);
  }
  return "ea";
}

void
push_stack16(char *stack, char *data)
{                                                                 // memory is a byte anyway. so casting not needed
  // Malban wants the auto-increment/decrements separated from the memory access -
  //  not sure why, but there's no reason not to, so here it is (and in other similar cases)
  dumpf(R_Always|R_lookup(stack), R_lookup(stack),                "%s = %s - 1;", stack, stack);
  dumpf(R_Always,                 R_lookup(stack)|R_lookup(data), "memory[%s] = %s;", stack, data);
  dumpf(R_Always|R_lookup(stack), R_lookup(stack),                "%s = %s - 1;", stack, stack);
  dumpf(R_Always,                 R_lookup(stack)|R_lookup(data), "memory[%s] = %s >> 8;", stack, data);
  CK (1);
}

void
pop_stack16(char *stack, char *dest) // stack adjustment done by caller
{
  CK (1);
  dumpf(R_Always|R_lookup(dest),   R_lookup(stack),                "%s = memory[%s]<<8;", dest, stack);
  dumpf(R_Always|R_lookup(stack),  R_lookup(stack),                "%s = %s + 1;", stack, stack);
  dumpf(R_Always|R_lookup(dest),   R_lookup(stack)|R_lookup(dest), "%s = %s | memory[%s];", dest, dest, stack);
  dumpf(R_Always|R_lookup(stack),  R_lookup(stack),                "%s = %s + 1;", stack, stack);
}

char *
direct(void)
{
  static char tmp[16];
  sprintf (tmp, "DP|0x%02x", (memory[NextPC-1])&0xff);
  return strdup (tmp);
}

char *get_d (void);

char *f(char *s, ...)
{
   static FILE *nullfile = NULL;
   static char *buff = NULL;
   static int bufflen = 0;
   va_list ap;
   
   if (nullfile == NULL) {
      nullfile = fopen ("/dev/null", "w");
      if (nullfile == NULL) {
         fprintf (stderr, "Major error - cannot open /dev/null\n");
         exit (1);
      }
   }
   if (buff == NULL) {
      buff = malloc (bufflen = 256);
      if (buff == NULL) exit (2);              // if malloc failed so will printf!
   }
   {
      int string_length;

      va_start (ap, s);
      string_length = vfprintf (nullfile, s, ap);
      va_end (ap);

      if (string_length + 1 >= bufflen) {
         free (buff);
         buff = malloc (bufflen = 2 * string_length);
         if (buff == NULL) exit (3);
      }
      va_start (ap, s);
      //vsprintf (buff, s, ap);
      vsnprintf (buff, 0x7FFFFFFF, s, ap);
      va_end (ap);
   }

   return strdup (buff);
}

char *
indexed (void)          /* note take 1 extra cycle */
{
  char *regname[4] = { "X", "Y", "U", "S" };
  unsigned post = memory[CodePTR]; // imm_byte ();
  char *R = regname[(post >> 5) & 0x3];

  if (post & 0x80)
    {
      switch (post & 0x1f)
        {

        case 0x00:          set_post(R, 1); CK (6); return R;
        case 0x01:          set_post(R, 2); CK (7); return R;

        case 0x02:          CK (6); dumpf(R_Always|R_lookup(R), R_lookup(R),     "%s = %s - 1;", R, R); return R;
        case 0x03:          CK (7); dumpf(R_Always|R_lookup(R), R_lookup(R),     "%s = %s - 2;", R, R); return R;

        case 0x04:          CK (4); return R;
        case 0x05:          CK (5); return f("(UINT16)(%s + (SINT8)B)", R); // need to check hardware manual as to whether A or B are signed
        case 0x06:          CK (5); return f("(UINT16)(%s + (SINT8)A)", R);
        case 0x08:          CK (5); return f("(UINT16)(%s + %s)", R, imm_byte_s()); // TO DO: UNSIGNED OR SIGNED???          
        case 0x09:          CK (8); return f("(UINT16)(%s + %s)", R, imm_word_s());
        case 0x0b:          CK (8); return f("(UINT16)(%s + %s)", R, get_d());
        case 0x0c:          CK (5); return f("0x%04x", (NextPC+SEX8(imm_byte()))&0xffff);
        case 0x0d:          CK (9); return f("0x%04x", (NextPC+imm_word())&0xffff);

        case 0x11:          set_post(R, 2); CK (7); CK (2); return RDMEM16(R);
        case 0x13:          CK (7); dumpf(R_Always|R_lookup(R), R_lookup(R),     "%s = %s - 2;", R, R); CK (2); return RDMEM16(R);

        case 0x14:          CK (4); CK (2); return RDMEM16(R);

        case 0x15:          CK (5); CK (2);
                            dumpf(R_EA|R_Always, R_lookup(R)|R_B, "ea = memory[(UINT16)(%s + (SINT8)B)]<<8;", R);
                            dumpf(R_EA|R_Always, R_lookup(R)|R_B, "ea = ea | memory[(UINT16)((%s + (SINT8)B) + 1)];", R); // wrap when B = 255, 0x7f, 0x80???
                            return "ea";
                            
        case 0x16:          CK (5); CK (2);
                            dumpf(R_EA|R_Always, R_lookup(R)|R_A, "ea = memory[(UINT16)(%s + (SINT8)A)]<<8;", R);
                            dumpf(R_EA|R_Always, R_lookup(R)|R_A, "ea = ea | memory[(UINT16)((%s + (SINT8)A) + 1)];", R); // wrap when A = 255, 0x7f, 0x80???
                            return "ea";
                            
        case 0x18:          CK (5); CK (2); // I HOPE THIS DOES NOT WRAP WITHIN 256 BYTE BLOCK!
                            dumpf(R_EA|R_Always, R_lookup(R), "ea = memory[(UINT16)(%s + (SINT8)%s)]<<8;", R, imm_byte_s());
                            dumpf(R_EA|R_Always, R_lookup(R), "ea = ea | memory[(UINT16)(%s + 0x%04x)];", R, (SEX8(imm_byte())+1)&0xffff);
                            assert(strcmp(R, "PC") != 0);
                            return "ea";
                            
        case 0x19:          CK (8); CK (2);
                            dumpf(R_EA|R_Always, R_lookup(R), "ea = memory[(UINT16)(%s + %s)]<<8;", R, imm_word_s());
                            dumpf(R_EA|R_Always, R_lookup(R), "ea = ea | memory[(UINT16)(%s + 0x%04x)];", R, (imm_word()+1)&0xffff);
                            assert(strcmp(R, "PC") != 0);
                            return "ea";
                            
        case 0x1b:          CK (8); CK (2);
                            dumpf(R_EA|R_Always, R_lookup(R)|R_A|R_B, "ea = memory[(UINT16)(%s + %s)]<<8;", R, get_d());
                            dumpf(R_EA|R_Always, R_lookup(R)|R_A|R_B, "ea = ea | memory[(UINT16)((%s + %s) + 1)];", R, get_d());
                            assert(strcmp(R, "PC") != 0);
                            return "ea";

        case 0x1c:          CK (5); CK (2);
                            dumpf(R_EA|R_Always, 0, "ea = memory[0x%04x]<<8;", (NextPC+SEX8(imm_byte()))&0xffff);
                            dumpf(R_EA|R_Always, 0, "ea = ea | memory[0x%04x];", (NextPC+SEX8(imm_byte())+1)&0xffff);
                            return "ea";
                            
        case 0x1d:          CK (9); CK (2);
                            dumpf(R_EA|R_Always, 0, "ea = memory[0x%04x]<<8;", (NextPC+imm_word())&0xffff);
                            dumpf(R_EA|R_Always, 0, "ea = ea | memory[0x%04x];", (NextPC+imm_word()+1)&0xffff);
                            return "ea";

        case 0x1f:          CK (6); CK (2); return RDMEM16(imm_word_s());                                  

        default:            printf ("%X: invalid index post $%02X\n", iPC, post); exit(1);
        }
    } else {
      if (post & 0x10) post |= 0xfff0; else post &= 0x000f;
      CK (5);
      return f("(UINT16)(%s + 0x%04x)", R, post&0xffff);
    }
  assert(__LINE__);
  return ("ea"); /* BUG??? */
}

char *
extended (void)
{
  static char addr[16];
  // Since this is translating known code at a known fixed address, these can be folded at translate time...
  // (if there was any code in ram, this would not catch it...)
  sprintf(addr, "0x%04x", ((memory[NextPC-2]<<8) | memory[NextPC-1])&0xffff);
  return(strdup(addr));  // or put 0xnnnn in the return string...
}

//void Make_D_from_A_and_B(void) {
//  dumpf(R_D, R_AB, "D = (A << 8) | B;");
//}

char *
get_d(void)
{
  return "(((UINT8)A<<8)|(UINT8)B)";
}

//void Get_A_and_B_from_D(void) { // is this still used?  I think I now use A and B everywhere.
//  dumpf(R_A, R_D, "A = (UINT8)(D >> 8);");
//  dumpf(R_B, R_D, "B = (UINT8) D & 0xff;");
//}

void
set_d(char *val)
{
  assert(__LINE__);
  dumpf(R_A, R_lookup(val), "A = (UINT8)(%s >> 8);", val);
  dumpf(R_B, R_lookup(val), "B = (UINT8)%s;", val);
}

/* handle condition code register */

void
CC_Reg_from_Individual_Flags(void)
{
  dumpf(R_Always|R_HNZVC, R_HNZVC, "simplify_flags();"); // canonicalise flags to all 0 or 1
  dumpf(R_Always|R_CC, R_HNZVC, "CC = (((((((((((((E<<1) | F)<<1) | H)<<1) |I)<<1) | N)<<1) | Z)<<1) | V)<<1) | C; // Placeholder for now");
  dumpf(R_Always|R_HNZVC, R_HNZVC, "restore_flags();"); // this is excessive but works and well tested
}

void
Individual_Flags_from_CC_Reg (void)
{
  dumpf(R_Always|R_HNZVC, R_CC, "C = CC; V = C>>1; Z = V>>1; N = Z>>1; I = N>>1; H = I>>1; F = H>>1; E = (F>>1)&1; F = F&1; H = H&1; I = I&1; N = N&1; Z = Z&1; V = V&1; C = C&1;");
  dumpf(R_Always|R_HNZVC, R_HNZVC, "restore_flags();");
}

char *
get_reg (unsigned nro)
{
  switch (nro)
    {
    case 0:      return "((A << 8) | B)";      break;
    case 1:      return "X";                   break;
    case 2:      return "Y";                   break;
    case 3:      return "U";                   break;
    case 4:      return "S";                   break;
    case 5:      return "PC";                  break;
    case 8:      return "A";                   break;
    case 9:      return "B";                   break;
    case 10:     CC_Reg_from_Individual_Flags ();
                 return "CC";                  break;
    case 11:     return "(UINT8)(DP >> 8)";    break;
    default:     printf ("%X: invalid register code $%02X\n", iPC, nro); exit(1);
    }
}

void
set_reg (unsigned nro, char *val)
{
  switch (nro)
    {
    case 0:      dumpf(R_Always|R_A,  R_lookup(val), "A = (UINT8)((%s) >> 8);", val);
                 if (
                      ((strncmp(val, "(SINT8)", strlen("(SINT8)"))) == 0)
                      ||
                      ((strncmp(val, "(UINT8)", strlen("(UINT8)"))) == 0)
                     ) {
                   dumpf(R_Always|R_B,  R_lookup(val), "B = (UINT8)%s;", val+strlen("(SINT8)"));
                 } else {
                   dumpf(R_Always|R_B,  R_lookup(val), "B = (UINT8)%s;", val);
                 }
                 break;
    case 1:      dumpf(R_Always|R_X,  R_lookup(val), "X = %s;", val);              break;
    case 2:      dumpf(R_Always|R_Y,  R_lookup(val), "Y = %s;", val);              break;
    case 3:      dumpf(R_Always|R_U,  R_lookup(val), "U = %s;", val);              break;
    case 4:      dumpf(R_Always|R_S,  R_lookup(val), "S = %s;", val);              break;
    case 5:      dumpf(R_Always|R_PC, R_lookup(val), "PC = %s; // check for missing JUMP", val);             break;
    case 8:      dumpf(R_Always|R_A,  R_lookup(val), "A = %s;", val);              break;
    case 9:      dumpf(R_Always|R_B,  R_lookup(val), "B = %s;", val);              break;
    case 10:     dumpf(R_Always|R_CC, 0,             "CC = %s;", val);
                 Individual_Flags_from_CC_Reg();                          break;
    case 11:     dumpf(R_Always|R_DP, R_lookup(val), "DP = (%s) << 8; checkDP();", val);
                 dumpf(R_Always, R_DP,      "// memory_DP = &memory[DP];");      break;
    default:     printf ("%X: invalid register code $%02X\n", iPC, nro); exit(1);
    }
}

/* 8-Bit Accumulator and Memory Instructions */


void peephole(char *code) {
  // everything goes through here.  Maybe we can fix up some problems...

  // NOTE!!! The peephole procedure is TEMPORARY in this form and basically a
  // dirty hack.  This is one of those 'pay no attention to the man behind the curtain'
  // procedures, for now.

  char *assignment, *lhs, *rhs, *commentp, *comment, *s = strdup(code);
  if (s[strlen(s)-1] == '\n') s[strlen(s)-1] = '\0'; 
  commentp = strstr(s, "//"); // TO DO: change /* to //  If fixed, could rely on " //"
  if (commentp) {
    comment = strdup(commentp);
    if (comment[strlen(comment)-1] == '\n') comment[strlen(comment)-1] = '\0'; 
    *commentp = '\0';
  } else comment = "";

  assignment = strstr(s, " = "); // and not in a string
  if (assignment) {
    *assignment = '\0';
    lhs = s;
    rhs = assignment + 3;
    // we need a minimal lexer here
    while (*lhs == ' ') lhs += 1;
    while (*rhs == ' ') rhs += 1;
    while (lhs[strlen(lhs)-1] == ' ') lhs[strlen(lhs)-1] = '\0';
    while (rhs[strlen(rhs)-1] == ' ') rhs[strlen(rhs)-1] = '\0';
    if (rhs[strlen(rhs)-1] == ';') rhs[strlen(rhs)-1] = '\0';
    if (strstr(lhs, "memory[") && strstr(rhs, "memory[")) {
      assert(__LINE__); // should not have memory on both sides of assignment (even for 'inc mem', although inc may need special handling)
    } else if (strstr(lhs, "memory[")) {
      if (strncmp(lhs, "memory[", strlen("memory[")) == 0) {
        lhs += strlen("memory[");
        if (lhs[strlen(lhs)-1] == ']') {
          lhs[strlen(lhs)-1] = '\0';
        } else {
          assert(__LINE__);
        }
        s = f("wr_mem(%s, %s); %s", lhs, rhs, comment);
      } else {
        assert(__LINE__);
      }
    } else if ((s = strstr(rhs, "memory[")) != NULL) {
      while ((s = strstr(rhs, "memory[")) != NULL) { // handle nested memory[memory[]] expressions
        int depth = 0;
        memmove(s, "rd_mem(", strlen("memory["));
                // "memory["
        while (*s != '\0') {
        if (*s == '[') depth += 1;
          if (*s == ']') {
            if (depth == 0) {
              *s = ')';
              break;
            }
            depth -= 1;
          }
          s += 1;
        }
      }
      //if (s[strlen(s)-1] == '\n') s[strlen(s)-1] = '\0'; 
      s = f("%s = %s; %s", lhs, rhs, comment);
    } else {
      //if (s[strlen(s)-1] == '\n') s[strlen(s)-1] = '\0'; 
      s = f("%s = %s; %s", lhs, rhs, comment);
    }
    //if (*s == '\0') return;
    if ((lhs=strstr(s, "(SINT8)(SINT8)")) != NULL) {
      // remove the unneccessary (SINT8)
      memmove(lhs+strlen("(SINT8)"), lhs+strlen("(SINT8)(SINT8)"), strlen(lhs+strlen("(SINT8)(SINT8)"))+1);
    }
    if ((lhs=strstr(s, "(UINT8)(SINT8)")) != NULL) {
      // remove the unneccessary (SINT8)
      memmove(lhs+strlen("(UINT8)"), lhs+strlen("(UINT8)(SINT8)"), strlen(lhs+strlen("(UINT8)(SINT8)"))+1);
    }
    codes[nextfree] = s;
  } else {
    // procedure call, label, or something
    //if (*code == '\0') return;
    codes[nextfree] = strdup(code);
  }

  nextfree += 1;
}


/* WARNING: There are two different kinds of 64 bit architectures - both support 64 bit
            integers, but one of them also defaults to 64 bit pointers whereas the other
            doesn't.  On one of them (I'm not sure which but probably the one with 64 bit
            pointers) the varargs code below fails - most likely because the final parameter
            before the "..." is a pointer, so all subsequent parameters will also be
            pointer-sized but in fact are not.  I have not yet fixed this.
*/

void dumpf(int def, int use, char *s, ...)                               /* Works like printf */
{
   static FILE *nullfile = NULL;
   static char *buff = NULL;
   static int bufflen = 0;
   va_list ap;
   
   if (nullfile == NULL) {
      nullfile = fopen ("/dev/null", "w");
      if (nullfile == NULL) {
         fprintf (stderr, "Major error - cannot open /dev/null\n");
         exit (1);
      }
   }
   if (buff == NULL) {
      buff = malloc (bufflen = 256);
      if (buff == NULL)
         exit (2);              // if malloc failed so will printf!
   }

   {
   int string_length;

      va_start (ap, s);
      string_length = vfprintf (nullfile, s, ap);
      va_end (ap);

      if (string_length + 1 >= bufflen) {
         free (buff);
         buff = malloc (bufflen = 2 * string_length);
         if (buff == NULL)
            exit (3);
      }
      va_start (ap, s); 
     //vsprintf (buff, s, ap);
      vsnprintf (buff, 0x7FFFFFFF, s, ap);
      va_end (ap);
   }
   defs[nextfree] = def;
   uses[nextfree] = use;

   peephole(buff); // should put indent in front of statement? Nah - just use gnu indent...
}

void
adc(char *arg, char *val)  // arg is a register name, val is a data value or memory location
{
  dumpf(R_ARG,             R_lookup(arg),                         "arg = %s;", arg);
  dumpf(R_VAL,             R_lookup(val),                         "val = %s;", val);
  dumpf(R_RES,             R_ARG|R_VAL|R_C,                       "res = (arg + val) + (C != 0 ? 1:0);");
  dumpf(R_C,               R_RES,                                 "C = (res >> 1) & 0x80;");
  dumpf(R_Z,               R_RES,                                 "Z = (UINT8)res;");
  dumpf(R_N,               R_RES,                                 "N = (UINT8)res;");
  dumpf(R_H,               R_ARG|R_VAL|R_RES|R_C,                 "H = (arg ^ val) ^ ((UINT8)res ^ C);");
  dumpf(R_V,               R_ARG|R_VAL|R_RES|R_C,                 "V = (arg ^ val) ^ ((UINT8)res ^ C);");
  dumpf(R_Always|R_lookup(arg), R_RES,                            "%s = (UINT8)res;", arg);
}

void
add(char *arg, char *opd)
{
  //    DEF                USE                          CODE
  if (strncmp(opd, "(SINT8)", strlen("(SINT8)")) == 0) opd = opd+strlen("(SINT8)"); //GT: bugfix?
  dumpf(R_VAL,             R_lookup(arg),               "val = (UINT8)%s;", arg);
  dumpf(R_ARG,             R_lookup(opd),               "arg = %s;", opd);
  dumpf(R_RES,             R_VAL|R_ARG,                 "res = val + (UINT8)arg;");
#ifndef TESTING_ERROR_DETECTION_1
  // if defined, we don't generate this line... which should cause a wrong result
  dumpf(R_C,               R_RES,                       "C = (res >> 1) & 0x80;");
#endif
  dumpf(R_Z,               R_RES,                       "Z = (UINT8)res;");
  dumpf(R_N,               R_RES,                       "N = (UINT8)res;");
  dumpf(R_H,               R_VAL|R_ARG|R_RES|R_C,       "H = (UINT8)((val ^ arg) ^ (res ^ C));");
  dumpf(R_V,               R_VAL|R_ARG|R_RES|R_C,       "V = (UINT8)((val ^ arg) ^ (res ^ C));");
  dumpf(R_Always|R_lookup(arg), R_RES,                  "%s = (UINT8)res;", arg);
}

void
and(char *arg, char *opd)
{
  //    DEF                USE                          CODE
  dumpf(R_VAL,             R_lookup(arg),               "val = (UINT8)%s;", arg);
  dumpf(R_RES,             R_VAL|R_lookup(opd),         "res = val & %s;", opd);
  dumpf(R_Always|R_lookup(arg),    R_RES,               "%s = res;", arg);
  dumpf(R_Z,               R_RES,                       "Z = res;");
  dumpf(R_N,               R_RES,                       "N = res;");
  dumpf(R_V,               0,                           "V = 0;");
}

void
asl(char *arg)          /* same as lsl */
{
  //    DEF                USE                          CODE
  dumpf(R_VAL,             R_lookup(arg),               "val = (UINT8)%s;", arg);
  dumpf(R_RES,             R_VAL,                       "res = val << 1;");
  dumpf(R_C,               R_RES,                       "C = res & 0x100;");
  dumpf(R_RES,             R_RES,                       "res = (UINT8)res;");
  dumpf(R_Z,               R_RES,                       "Z = res;");
  dumpf(R_N,               R_RES,                       "N = res;");
  dumpf(R_V,               R_VAL | R_RES,               "V = val ^ res;");
  dumpf(R_Always|R_lookup(arg), R_RES,                  "%s = res;", arg);

  CK (2);
}

void
asr(char *arg)
{
  //    DEF                USE                          CODE
  dumpf(R_VAL,             R_lookup(arg),               "val = (UINT8)%s;", arg);
  dumpf(R_C,               R_VAL,                       "C = val & 1;");
  dumpf(R_VAL,             R_VAL,                       "val = (UINT8)((SINT8)val >> 1);");
  dumpf(R_Always|R_lookup(arg),     R_VAL,              "%s = val;", arg);
  dumpf(R_Z,               R_VAL,                       "Z = val;");
  dumpf(R_N,               R_VAL,                       "N = val;");

  CK (2);
}

void
bit(char *arg, char *val)
{
  //    DEF                USE                          CODE
  dumpf(R_RES,             R_lookup(arg)|R_lookup(val), "res = %s & %s;", arg, val);
  dumpf(R_Z,               R_RES,                       "Z = res;");
  dumpf(R_N,               R_RES,                       "N = res;");
  dumpf(R_V,               0,                           "V = 0;");
}

void
clr(char *arg)
{
  //    DEF                USE                          CODE
  if (strstr(arg, "memory") != NULL) { // can be a reg *OR* memory!
    // better to use this?  if (strncmp(arg, "memory", strlen("memory")) == 0) {  // TO DO after checking
    dumpf(R_Always,        R_lookup(arg),               "%s = 0;", arg);
                           // any regs given are in index of memory addr and therefore USEs not DEFs 
  } else {
    dumpf(R_Always|R_lookup(arg),   0,                  "%s = 0;", arg);
  }
  dumpf(R_V,               0,                           "V = 0;");
  dumpf(R_Z,               0,                           "Z = 0;");
  dumpf(R_N,               0,                           "N = 0;");
  dumpf(R_C,               0,                           "C = 0;");

  CK (2);
}

void
cmp(char *arg, char *opd)
{
  //    DEF                USE                          CODE

  if (strncmp(opd, "(SINT8)", strlen("(SINT8)")) == 0) opd = opd+strlen("(SINT8)"); //GT: bugfix?
  dumpf(R_VAL,             R_lookup(arg),               "val = (UINT8)%s;", arg);
  dumpf(R_ARG,             R_lookup(opd),               "arg = %s;", opd);
  dumpf(R_RES,             R_VAL|R_ARG,                 "res = val - (UINT8)arg;");
  dumpf(R_C,               R_RES,                       "C = res & 0x100;");
  dumpf(R_Z,               R_RES,                       "Z = (UINT8)res;");
  dumpf(R_N,               R_RES,                       "N = (UINT8)res;");
  dumpf(R_V,               R_VAL|R_ARG|R_RES,           "V = (UINT8)((val ^ arg) & (val ^ res));");
}

void
com(char *arg)
{
  //    DEF                USE                          CODE
  dumpf(R_VAL,             R_lookup(arg),               "val = (UINT8)%s ^ 0xff;", arg);
  dumpf(R_Always|R_lookup(arg), R_VAL,                  "%s = val;", arg);
  dumpf(R_Z,               R_VAL,                       "Z = val;");
  dumpf(R_N,               R_VAL,                       "N = val;");
  dumpf(R_V,               0,                           "V = 0;");
  dumpf(R_C,               0,                           "C = 1;");

  CK (2);
}

void
daa (void)
{
    //    DEF                USE                          CODE
  dumpf(R_RES,             R_A,                         "res = A;");
  dumpf(R_MSN,             R_RES,                       "msn = res & 0xf0;");
  dumpf(R_LSN,             R_RES,                       "lsn = res & 0x0f;");

  // Yes, these must be '+', not '|'...

  dumpf(R_RES,             R_RES | R_LSN | R_H,         "res = res + (((lsn > 0x09) || (H & 0x10)) ? 0x06 : 0);");
  // msn and lsn may already be UINT8 ?
  dumpf(R_RES,             R_RES | R_MSN | R_LSN | R_C, "res = res + ((((UINT8)msn > 0x80U) && ((UINT8)lsn > 0x09U)) || (((UINT8)msn > 0x90U) || (C != 0)) ? 0x60 : 0);");

  dumpf(R_C,               R_C | R_RES,                 "C = C | (res & 0x100);");
  dumpf(R_RES,             R_RES,                       "res = (UINT8)res;");
  dumpf(R_Always|R_A,      R_RES,                       "A = res;");
  dumpf(R_N,               R_RES,                       "N = res;");
  dumpf(R_Z,               R_RES,                       "Z = res;");
  dumpf(R_V,               0,                           "V = 0;");

  CK (2);
}


void
dec(char *arg)
{
  //    DEF                USE                          CODE
  dumpf(R_VAL,             R_lookup(arg),               "val = (UINT8)%s;", arg);
  dumpf(R_RES,             R_VAL,                       "res = (UINT8)(val - 1);");
  dumpf(R_N,               R_RES,                       "N = res;");
  dumpf(R_Z,               R_RES,                       "Z = res;");
  dumpf(R_V,               R_RES | R_VAL,               "V = val & ~res;");
  dumpf(R_Always|R_lookup(arg), R_RES,                  "%s = res;", arg);
  CK (2);
}


void
eor(char *arg, char *val)
{
  //    DEF                USE                          CODE
  dumpf(R_RES,             R_lookup(arg)|R_lookup(val), "res = %s ^ %s;", arg, val);
  dumpf(R_Always|R_lookup(arg),     R_RES,              "%s = res;", arg);
  dumpf(R_Z,               R_RES,                       "Z = res;");
  dumpf(R_N,               R_RES,                       "N = res;");
  dumpf(R_V,               0,                           "V = 0;");
}

/*
## TFR/EXG with unaligned register sizes

tfr x,b         ; 6809,6309: b low byte of x, a unchanged
tfr x,a         ; 6809, a low byte of x, b unchanged 

tfr a,x         ; low byte of x has value of a, high byte is $FF
tfr b,x         ; low byte of x has value of b, high byte is $FF

exg a,x         ; low byte of x has value of a, high byte is $FF
exg b,x         ; low byte of x has value of b, high byte is $FF

According to Motorola 6809 and Hitachi 6309 Programmer's Reference

Except for the case where the first operand is 8 bit and register CC or DP:
In this case the 16-bit register has the value of the 8-bit register in
high and low byte!
*/

void
exg(void)
{
  //    DEF                USE                          CODE
  unsigned post = imm_byte ();

  // TO DO: undocumented case described above...
  if (((post ^ (post << 4)) & 0x80) == 0) {
    char *tmp = get_reg (post >> 4);
    dumpf(R_VAL|R_Always, R_lookup(tmp), "val = %s;", tmp);
    set_reg (post >> 4, get_reg (post & 15));
    set_reg (post & 15, "val");
  }
  CK (8);
}


void
inc(char *arg)
{
  //    DEF                USE                          CODE
  dumpf(R_VAL,             R_lookup(arg),               "val = (UINT8)%s;", arg);
  dumpf(R_RES,             R_VAL,                       "res = (UINT8)(val + 1);");
  dumpf(R_Z,               R_RES,                       "Z = res;");
  dumpf(R_N,               R_RES,                       "N = res;");
  dumpf(R_V,               R_RES|R_VAL,                 "V = res & ~val;");
  dumpf(R_Always|R_lookup(arg), R_RES,                  "%s = res;", arg);
  CK (2);
}

void
ld(char *arg, char *opd)
{
  //    DEF                USE                          CODE
  dumpf(R_VAL,             R_lookup(opd),               "val = (UINT8)%s;", opd);
  dumpf(R_Always|R_lookup(arg), R_VAL,                  "%s = val;", arg);
  dumpf(R_N,               R_VAL,                       "N = val;");
  dumpf(R_Z,               R_VAL,                       "Z = val;");
  dumpf(R_V,               0,                           "V = 0;");
}

void
lsr(char *arg)
{
  //    DEF                USE                          CODE
  dumpf(R_VAL,             R_lookup(arg),               "val = (UINT8)%s;", arg);
  dumpf(R_C,               R_VAL,                       "C = val & 1;");
  dumpf(R_VAL,             R_VAL,                       "val = val >> 1;");
  dumpf(R_Always|R_lookup(arg),     R_VAL,              "%s = val;", arg);
  dumpf(R_N,               0,                           "N = 0;");
  dumpf(R_Z,               R_VAL,                       "Z = val;");
  CK (2);
}

void
mul(void)
{
  //    DEF                USE                          CODE
  dumpf(R_RES,             R_A|R_B,                     "res = (UINT16)(A * B);");
  dumpf(R_Z,               R_RES,                       "Z = res;");
  dumpf(R_C,               R_RES,                       "C = res & 0x80;");
  dumpf(R_Always|R_A,      R_RES,                       "A = res >> 8;");
  dumpf(R_Always|R_B,      R_RES,                       "B = (UINT8)res;");
  CK (11);
}

void
neg(char *arg)
{
  //    DEF                USE                          CODE
  dumpf(R_VAL,             R_lookup(arg),               "val = (UINT8)%s;", arg);
  dumpf(R_RES,             R_VAL,                       "res = -val;");
  dumpf(R_V,               R_VAL|R_RES,                 "V = val & (UINT8)res;");
  dumpf(R_C,               R_RES,                       "C = res & 0x100;");
  dumpf(R_VAL,             R_RES,                       "val = (UINT8)res;");
  dumpf(R_Always|R_lookup(arg),     R_VAL,              "%s = val;", arg);
  dumpf(R_Z,               R_VAL,                       "Z = val;");
  dumpf(R_N,               R_VAL,                       "N = val;");
  CK (2);
}


void
or(char *arg, char *val)
{
  //    DEF                USE                          CODE
  dumpf(R_RES,             R_lookup(arg)|R_lookup(val), "res = %s | (UINT8)%s;", arg, val); // val may have been cast to SINT8 already...
  dumpf(R_Always|R_lookup(arg),     R_RES,              "%s = res;", arg);
  dumpf(R_Z,               R_RES,                       "Z = res;");
  dumpf(R_N,               R_RES,                       "N = res;");
  dumpf(R_V,               0,                           "V = 0;");
}


void
rol(char *arg)
{
  //    DEF                USE                          CODE
  dumpf(R_VAL,             R_lookup(arg),               "val = (UINT8)%s;", arg);
  dumpf(R_RES,             R_VAL|R_C,                   "res = (val << 1) + (C != 0 ?1:0);"); // TO DO: change '+' for '|'?
  dumpf(R_C,               R_RES,                       "C = res & 0x100;");
  dumpf(R_Z,               R_RES,                       "Z = (UINT8)res;");
  dumpf(R_N,               R_RES,                       "N = (UINT8)res;"); // N, and V are both 8 bit variables so no cast required
  dumpf(R_V,               R_VAL|R_RES,                 "V = (UINT8)res ^ val;");
  dumpf(R_Always|R_lookup(arg), R_RES,                  "%s = (UINT8)res;", arg);
  CK (2);
}


void
ror(char *arg)
{
  //    DEF                USE                          CODE
  dumpf(R_RES,             R_lookup(arg)|R_C,           "res = ((UINT8)%s) | ((C != 0) ? 0x100 : 0);", arg);
  dumpf(R_C,               R_RES,                       "C = res & 1;");
  dumpf(R_RES,             R_RES,                       "res = (UINT8)(res >> 1);");
  dumpf(R_Z,               R_RES,                       "Z = res;");
  dumpf(R_N,               R_RES,                       "N = res;");
  dumpf(R_Always|R_lookup(arg),     R_RES,              "%s = res;", arg);
  CK (2);
}


void
sub(char *arg, char *val)
{
  //    DEF                USE                          CODE
  if (strncmp(val, "(SINT8)", strlen("(SINT8)")) == 0) val = val+strlen("(SINT8)"); //GT: bugfix?
  dumpf(R_VAL,             R_lookup(arg),               "val = (UINT8)%s;", arg);
  dumpf(R_ARG,             R_lookup(val),               "arg = %s;", val);
  dumpf(R_RES,             R_VAL|R_ARG,                 "res = val - (UINT8)arg;");
  dumpf(R_C,               R_RES,                       "C = res & 0x100;");
  dumpf(R_Z,               R_RES,                       "Z = (UINT8)res;");
  dumpf(R_N,               R_RES,                       "N = (UINT8)res;"); // cast is redundant but suppresses warning
  dumpf(R_V,               R_VAL|R_ARG|R_RES,           "V = (UINT8)((val ^ arg) & (val ^ res));");
  dumpf(R_Always|R_lookup(arg), R_RES,                  "%s = (UINT8)(res);", arg);
}


void
sbc(char *arg, char *val)
{
  //    DEF                USE                          CODE

  if (strncmp(val, "(SINT8)", strlen("(SINT8)")) == 0) val = val+strlen("(SINT8)");
  dumpf(R_VAL,             R_lookup(arg),               "val = (UINT8)%s;", arg);
  dumpf(R_ARG,             R_lookup(val),               "arg = %s;", val);
  dumpf(R_RES,             R_VAL|R_ARG|R_C,             "res = (val - (UINT8)arg) - (C != 0 ? 1:0);");
  dumpf(R_C,               R_RES,                       "C = res & 0x100;");
  dumpf(R_Z,               R_RES,                       "Z = (UINT8)res;");
  dumpf(R_N,               R_RES,                       "N = (UINT8)res;");
  dumpf(R_V,               R_VAL|R_ARG|R_RES,           "V = (UINT8)((val ^ arg) & (val ^ res));"); // FIXED!
  dumpf(R_Always|R_lookup(arg), R_RES,                  "%s = (UINT8)res;", arg);
}


void
st(char *arg, char *ea)
{
  // just FYI, none of the validation tests test storing at all well :-(  val = %s+1 went undetected!
  //    DEF                USE                          CODE
  dumpf(R_VAL,             R_lookup(arg),               "val = %s;", arg);
  dumpf(R_Z,               R_VAL,                       "Z = val;");
  dumpf(R_N,               R_VAL,                       "N = val;");
  dumpf(R_V,               0,                           "V = 0;");
  if (strncmp(ea, "0x", 2) == 0) {
    dumpf(R_Always,          R_lookup(ea)|R_VAL,                  "memory[%s] = val;", ea);
  } else {
    dumpf(R_EA,              R_lookup(ea),                "ea = %s;", ea);
    dumpf(R_Always,          R_EA|R_VAL,                  "memory[ea] = val;", ea);
  }
}


void
tst(char *arg)
{
  //    DEF                USE                          CODE
  dumpf(R_VAL,             R_lookup(arg),               "val = (UINT8)%s;", arg);
  dumpf(R_Z,               R_VAL,                       "Z = val;");
  dumpf(R_N,               R_VAL,                       "N = val;");
  dumpf(R_V,               0,                           "V = 0;");
  CK (2);
}


/*
## TFR/EXG with unaligned register sizes

tfr x,b         ; 6809,6309: b low byte of x, a unchanged
tfr x,a         ; 6809, a low byte of x, b unchanged 

tfr a,x         ; low byte of x has value of a, high byte is $FF
tfr b,x         ; low byte of x has value of b, high byte is $FF

exg a,x         ; low byte of x has value of a, high byte is $FF
exg b,x         ; low byte of x has value of b, high byte is $FF

According to Motorola 6809 and Hitachi 6309 Programmer's Reference

Except for the case where the first operand is 8 bit and register CC or DP:
In this case the 16-bit register has the value of the 8-bit register in
high and low byte!
*/

void
tfr (void)
{
  unsigned post = imm_byte ();
  char *val;

  /*if(((post ^ (post << 4)) & 0x80) == 0)*/  // fixing TFR A,X ...
  val = get_reg((post >> 4)&15);

  //dumpf(R_Always, 0,      "/* from R%d to R%d */", (post >> 4)&15, post & 15);

  switch (post & 15)
    {
                 //    DEF   USE            CODE
    case 0:      dumpf(R_VAL,         R_lookup(val), "val = %s;", val);
                 dumpf(R_Always|R_A,  R_VAL,         "A = (UINT8)(val >> 8);");
                 dumpf(R_Always|R_B,  R_VAL,         "B = (UINT8)val;");      break;
    case 1:
      if ((((post >> 4)&15) == 8) || (((post >> 4)&15) == 9)) { // TFR A,X or TFR B,X
        dumpf(R_Always|R_X,  R_lookup(val), "X = %s | 0xFF00; // undocumented", val);
      } else {
        dumpf(R_Always|R_X,  R_lookup(val), "X = %s;", val);
      }
      break;
      
    case 2:      dumpf(R_Always|R_Y,  R_lookup(val), "Y = %s;", val);              break;
    case 3:      dumpf(R_Always|R_U,  R_lookup(val), "U = %s;", val);              break;
    case 4:      dumpf(R_Always|R_S,  R_lookup(val), "S = %s;", val);              break;
    case 5:      dumpf(R_Always|R_PC, R_lookup(val), "PC = %s; // check for missing JUMP", val);             break;
    case 8:      dumpf(R_Always|R_A,  R_lookup(val), "A = (UINT8)%s;", val);              break;
    case 9:      dumpf(R_Always|R_B,  R_lookup(val), "B = (UINT8)%s;", val);              break;
    case 10:     dumpf(R_Always|R_CC, R_lookup(val), "CC = %s;", val);
                 Individual_Flags_from_CC_Reg();                          break;
    case 11:     dumpf(R_Always|R_DP, R_lookup(val), "DP = (%s) << 8; checkDP();", val);
                 dumpf(R_Always, R_DP,      "// memory_DP = &memory[DP];");      break;
    default:     printf ("%X: invalid register code $%02X\n", iPC, post); exit(1);
  }

  CK (6);
}


/* 16-Bit Accumulator Instructions */
void
abx (void)
{
  //    DEF                USE                          CODE
  dumpf(R_Always|R_X,               R_X|R_B,                     "X = X + B;");
  CK (3);
}


void
addd(char *val)
{
  //    DEF                USE                          CODE
  dumpf(R_VAL,             R_lookup(val),               "val = %s;", val);
  dumpf(R_ARG,             R_A|R_B,                     "arg = (A << 8) | B;"); // I think one of the only two places we use ARG at the moment
  dumpf(R_RES,             R_ARG|R_VAL,                 "res = arg + val;");
  dumpf(R_C,               R_RES,                       "C = res & 0x10000;");
  dumpf(R_RES,             R_RES,                       "res = (UINT16)res;");
  dumpf(R_Z,               R_RES,                       "Z = res;");
  dumpf(R_V,               R_ARG|R_RES|R_VAL,           "V = ((arg ^ res) & (val ^ res)) >> 8;");
  dumpf(R_N,               R_RES,                       "N = res >> 8;");
  dumpf(R_Always|R_A,      R_RES,                       "A = (UINT8)(res >> 8);");
  dumpf(R_Always|R_B,      R_RES,                       "B = (UINT8)res;");
}


void
cmp16 (char *arg, char *val) // if val is not already simple it will be bracketed
{
  //    DEF                USE                          CODE
  dumpf(R_VAL,             R_lookup(arg),               "val = %s;", arg);
  dumpf(R_ARG,             R_lookup(val),               "arg = %s;", val);
  dumpf(R_RES,             R_VAL|R_ARG,                 "res = val - arg;"); // thanks for the bugfix, Malban
  dumpf(R_C,               R_RES,                       "C = res & 0x10000;");
  dumpf(R_RES,             R_RES,                       "res = (UINT16)res;");
  dumpf(R_Z,               R_RES,                       "Z = res;");
  dumpf(R_N,               R_RES,                       "N = res >> 8;");
  dumpf(R_V,               R_RES|R_VAL|R_lookup(val),   "V = ((val ^ arg) & (val ^ res)) >> 8;"); // TO DO cast result to UINT8?
}


// Plain LDD should no longer be called.
void
ldd(char *arg)
{
  char *s;
  //    DEF                USE                          CODE
  assert(__LINE__);
  if (arg[0] == '(' && (s=strstr(arg, "<<8)|")) && (strstr(s+1, "<<8)|") == NULL) && arg[strlen(arg)-1] == ')') {
    // Split up D into separate parts for assignment to A and B
    *s++ = '\0';
    if ((R_lookup(arg)&R_A) != 0) {
      dumpf(R_Always|R_VAL,             R_lookup(arg+2),             "val = %s;", arg+2);
      s = strchr(s, '|')+1; s[strlen(s)-1] = '\0';
      dumpf(R_Always|R_B,               R_lookup(s),                 "B = %s;", s);
      dumpf(R_Always|R_A,               R_VAL,                       "A = val;");
    } else {
      dumpf(R_Always|R_A,               R_lookup(arg+2),             "A = %s;", arg+2);
      s = strchr(s, '|')+1; s[strlen(s)-1] = '\0';
      dumpf(R_Always|R_B,               R_lookup(s),                 "B = %s;", s);
    }
  } else {
    dumpf(R_Always|R_A,               R_lookup(arg),               "A = (UINT8)((%s) >> 8);", arg); // is this ever passed a rd_mem? If so, cache in arg
    dumpf(R_Always|R_B,               R_lookup(arg),               "B = (UINT8)%s;", arg);
  }
}

// ldd_imm_word() == ldd(imm_word_s())
void ldd_imm_word(void) {
  dumpf(R_Always|R_A,      0,               "A = 0x%02x;", memory[NextPC-2]);
  dumpf(R_Always|R_B,      0,               "B = 0x%02x;", memory[NextPC-1]);
  dumpf(R_Z,               R_A|R_B,         "Z = A|B;");
  dumpf(R_N,               R_A,             "N = A;");
  dumpf(R_V,               0,               "V = 0;");
}

// ldd_direct() == ldd(RDMEM16(direct()))
void ldd_direct(void) {
#ifdef NEVER
  dumpf(R_Always|R_A,      R_DP,            "A = memory_DP[0x%02x]; // Care needed with I/O space and word fetches", memory[NextPC-1]);
  dumpf(R_Always|R_B,      R_DP,            "B = memory_DP[0x%02x];", memory[NextPC-1]+1);
#else
  dumpf(R_Always|R_A,      R_DP,            "A = memory[DP|0x%02x]; // Care needed with I/O space and word fetches", memory[NextPC-1]);
  dumpf(R_Always|R_B,      R_DP,            "B = memory[DP|0x%02x];", memory[NextPC-1]+1);
#endif
  dumpf(R_Z,               R_A|R_B,         "Z = A|B;");
  dumpf(R_N,               R_A,             "N = A;");
  dumpf(R_V,               0,               "V = 0;");
}

// ldd_indexed_post() == ldd(RDMEM16(indexed())); post()
void ldd_indexed_post(void) {
  char *s = indexed();
  if (strlen(s)>12) {// HACK!
    dumpf(R_EA,              R_lookup(s),                   "ea = %s;", s);
    post();
    //ldd(RDMEM16(indexed()));
    dumpf(R_Always|R_A,      R_EA,                        "A = memory[ea]; // Care needed with I/O space and word fetches");
    dumpf(R_Always|R_B,      R_EA,                        "B = memory[(UINT16)(ea+1)];");
  } else {
    dumpf(R_Always|R_A,      R_lookup(s),                 "A = memory[%s]; // Care needed with I/O space and word fetches. Use 'ea' here?", s);
    dumpf(R_Always|R_B,      R_lookup(s),                 "B = memory[(UINT16)(%s+1)];", s);
    post();
  }
  dumpf(R_Z,               R_A|R_B,                     "Z = A|B;");
  dumpf(R_N,               R_A,                         "N = A;");
  dumpf(R_V,               0,                           "V = 0;");
}

// ldd_extended() == ldd(RDMEM16(extended()))
void ldd_extended(void) {
  dumpf(R_Always|R_A,      0,               "A = memory[0x%04x]; // Care needed with I/O space and word fetches", ((memory[NextPC-2]<<8) | memory[NextPC-1])&0xffff);
  dumpf(R_Always|R_B,      0,               "B = memory[0x%04x];", (((memory[NextPC-2]<<8) | memory[NextPC-1]) + 1)&0xffff);
  dumpf(R_Z,               R_A|R_B,         "Z = A|B;");
  dumpf(R_N,               R_A,             "N = A;");
  dumpf(R_V,               0,               "V = 0;");
}


void
ld16(char *reg, char *arg)
{
  //    DEF                USE                          CODE
  dumpf(R_Always|R_lookup(reg), R_lookup(arg),          "%s = %s;", reg, arg);
  dumpf(R_Z,               R_lookup(reg),               "Z = %s;", reg);
  dumpf(R_N,               R_lookup(reg),               "N = (%s) >> 8;", reg);
  dumpf(R_V,               0,                           "V = 0;");
}


void
sex (void)
{
  //    DEF                USE                          CODE
  dumpf(R_Z,               R_B,                         "Z = B;");
  //dumpf(R_N,               R_B,                         "N = B & 0x80;");
  dumpf(R_N,               R_B,                         "N = B;");
  //dumpf(R_Always|R_A,      R_B,                         "A = ((B & 0x80) != 0) ? 0xff : 0;");
  dumpf(R_Always|R_A,      R_B,                         "A = ((SINT8)B < 0 ? 0xff : 0);");
  CK (2);
}


void
std(char *addr)
{
  //    DEF                USE                          CODE
  dumpf(R_Z,               R_A|R_B,                     "Z = A | B;");
  dumpf(R_N,               R_A,                         "N = A;");
  dumpf(R_V,               0,                           "V = 0;");
  if (strncmp(addr, "0x", 2) == 0) {
    dumpf(R_Always,          R_A|R_lookup(addr),          "memory[0x%04x] = A; // Care needed with I/O space and word fetches", HTOI(addr+2));
    dumpf(R_Always,          R_B|R_lookup(addr),          "memory[0x%04x] = B;", HTOI(addr+2)+1);
  } else {
    dumpf(R_EA,              R_lookup(addr),              "ea = %s;", addr);
    dumpf(R_Always,          R_A|R_EA,                    "memory[ea] = A; // Care needed with I/O space and word fetches");
    dumpf(R_Always,          R_B|R_EA,                    "memory[ea + 1] = B;"); // Is it worth wrapping the +1 here (by cast to UINT16 I think)?
  }
}


void
st16(char *arg, char *addr)
{
  //    DEF                USE                          CODE
  if (strncmp(addr, "(UINT16)", 8) == 0) addr = addr+strlen("(UINT16)");
  dumpf(R_VAL,             R_lookup(arg),               "val = %s;", arg);
  dumpf(R_Z,               R_VAL,                       "Z = val;");
  dumpf(R_N,               R_VAL,                       "N = (UINT8)(val >> 8);");
  dumpf(R_V,               0,                           "V = 0;");
  if (strncmp(addr, "0x", 2) == 0) {
    dumpf(R_Always,          R_VAL,                     "memory[0x%04x] = (UINT8)(val >> 8); // Care needed with I/O space and word fetches", HTOI(addr+2));
    dumpf(R_Always,          R_VAL,                     "memory[0x%04x] = (UINT8)val;", (HTOI(addr+2)+1)&0xffff);
  } else {
    dumpf(R_EA,              R_lookup(addr),            "ea = %s;", addr);
    dumpf(R_Always,          R_VAL|R_EA,                "memory[ea] = (UINT8)(val >> 8); // Care needed with I/O space and word fetches");
    dumpf(R_Always,          R_VAL|R_EA,                "memory[(UINT16)(ea + 1)] = (UINT8)val;");
  }
}


void
subd(char *val)
{
  //    DEF                USE                          CODE
  dumpf(R_VAL,             R_lookup(val),               "val = %s;", val);
  dumpf(R_ARG,             R_A|R_B,                     "arg = (A << 8) | B;"); // only other place we use ARG so far
  dumpf(R_RES,             R_ARG|R_VAL,                 "res = arg - val;");
  dumpf(R_C,               R_RES,                       "C = res & 0x10000;");
  dumpf(R_RES,             R_RES,                       "res = (UINT16)res;");
  dumpf(R_Z,               R_RES,                       "Z = res;");
  dumpf(R_V,               R_ARG|R_RES|R_VAL,           "V = ((arg ^ val) & (arg ^ res)) >> 8;");
  dumpf(R_Always|R_A,      R_RES,                       "A = (UINT8)(res >> 8);");
  dumpf(R_N,               R_RES,                       "N = res >> 8;");
  dumpf(R_Always|R_B,      R_RES,                       "B = (UINT8)res;");
}


/* stack instructions */
void
pshs (void)
{
  //    DEF                USE                          CODE
  unsigned post = imm_byte ();

  CK (5);

  if (post & 0x80) { // Recoded to be more sanitary...
    char tmp[16];
    //dumpf(R_Always|R_PC, 0, "PC = 0x%04x;", NextPC&0xffff); // check this is correct...?
    CK (2);
    //push_stack16 ("S", "PC");
    sprintf(tmp, "0x%02x", NextPC&0xff);
    push_stack8 ("S", tmp);
    sprintf(tmp, "0x%02x", (NextPC>>8)&0xff);
    push_stack8 ("S", tmp);
  }
  if (post & 0x40) { CK (2); push_stack16 ("S", "U"); }
  if (post & 0x20) { CK (2); push_stack16 ("S", "Y"); }
  if (post & 0x10) { CK (2); push_stack16 ("S", "X"); }
  if (post & 0x08) { CK (1); push_stack8 ("S", "(DP >> 8)"); }
  if (post & 0x04) { CK (1); push_stack8 ("S", "B"); }
  if (post & 0x02) { CK (1); push_stack8 ("S", "A"); }
  if (post & 0x01) { CK (1); CC_Reg_from_Individual_Flags(); push_stack8 ("S", "CC"); }
}


void
pshu (void)
{
  //    DEF                USE                          CODE
  unsigned post = imm_byte ();

  CK (5);

  if (post & 0x80) {
    char tmp[16];
    //dumpf(R_Always|R_PC, 0, "PC = 0x%04x;", NextPC&0xffff); // check this is correct...?
    CK (2);
    //push_stack16 ("U", "PC");
    sprintf(tmp, "0x%02x", NextPC&0xff);
    push_stack8 ("U", tmp);
    sprintf(tmp, "0x%02x", (NextPC>>8)&0xff);
    push_stack8 ("U", tmp);
  }
  if (post & 0x40) { CK (2); push_stack16 ("U", "S"); }
  if (post & 0x20) { CK (2); push_stack16 ("U", "Y"); }
  if (post & 0x10) { CK (2); push_stack16 ("U", "X"); }
  if (post & 0x08) { CK (1); push_stack8 ("U", "(DP >> 8)"); } // R_lookup
  if (post & 0x04) { CK (1); push_stack8 ("U", "B"); }
  if (post & 0x02) { CK (1); push_stack8 ("U", "A"); }
  if (post & 0x01) { CK (1); CC_Reg_from_Individual_Flags(); push_stack8 ("U", "CC"); }
}


void
puls (void)
{
  //    DEF                USE                          CODE
  unsigned post = imm_byte ();

  CK (5);

  if (post & 0x01) { CK (1); pop_stack8 ("S", "CC"); Individual_Flags_from_CC_Reg(); }
  if (post & 0x02) { CK (1); pop_stack8 ("S", "A"); }
  if (post & 0x04) { CK (1); pop_stack8 ("S", "B"); }
  if (post & 0x08) {
    CK (1); pop_stack8 ("S", "DP");  dumpf(R_DP, R_DP, "DP = DP << 8; checkDP();");
    dumpf(R_Always, R_DP, "// memory_DP = &memory[DP];");
  }
  if (post & 0x10) { CK (2); pop_stack16 ("S", "X"); }
  if (post & 0x20) { CK (2); pop_stack16 ("S", "Y"); }
  if (post & 0x40) { CK (2); pop_stack16 ("S", "U"); }
  if (post & 0x80) {
    CK (2); pop_stack16 ("S", "PC");
    // TO DO: check if 'optimisation' (& ~R_HNZVC) is valid. It may be like the 'branch()' problem that Malban found.
    dumpf(R_Always,          R_FLUSH & ~R_HNZVC, "JUMP;"); Accessible = FALSE;
  }
}


void
pulu (void)
{
  //    DEF                USE                          CODE
  unsigned post = imm_byte ();

  CK (5);

  if (post & 0x01) { CK (1); pop_stack8 ("U", "CC"); Individual_Flags_from_CC_Reg(); }
  if (post & 0x02) { CK (1); pop_stack8 ("U", "A"); }
  if (post & 0x04) { CK (1); pop_stack8 ("U", "B"); }
  if (post & 0x08) { CK (1); pop_stack8 ("U", "DP"); dumpf(R_DP, R_DP, "DP = DP << 8; checkDP();"); dumpf(R_Always, R_DP, "// memory_DP = &memory[DP];"); }
  if (post & 0x10) { CK (2); pop_stack16 ("U", "X"); }
  if (post & 0x20) { CK (2); pop_stack16 ("U", "Y"); }
  if (post & 0x40) { CK (2); pop_stack16 ("U", "S"); }
  if (post & 0x80) {
    CK (2); pop_stack16 ("U", "PC");
    // TO DO: check if 'optimisation' (& ~R_HNZVC) is valid. It may be like the 'branch()' problem that Malban found.
    dumpf(R_Always,          R_FLUSH & ~R_HNZVC, "JUMP;"); Accessible = FALSE;
  }
}


/* Miscellaneous Instructions */
void
nop (void)
{
  //    DEF                USE                          CODE
  CK (2);
}


void
jsr (char *addr)
{
  //    DEF                USE                          CODE

  static char tmp[16];
  // take care of return address:
  // (It is not possible with the current code to be executing a JSR from RAM.  We would
  //  need to add an interpreter in order to support that. So the assumption that NextPC
  //  is a compile-time constant is currently valid.)
  //sprintf(tmp, "0x%04x", NextPC&0xffff);

  //dumpf(R_Always|R_PC,              0,                           "PC = 0x%04x;", NextPC&0xffff);

  //push_stack16 ("S", tmp); // unclean!  unclean!
  //push_stack16 ("S", "PC");  // cleaner... 
  sprintf(tmp, "0x%02x", NextPC&0xff);
  push_stack8 ("S", tmp);
  sprintf(tmp, "0x%02x", (NextPC>>8)&0xff);
  push_stack8 ("S", tmp); // cleanest...


  if (!VECTREX) {
    if (Gen_Switch || (strncmp(addr, "0x", 2) != 0)) {
      dumpf(R_Always|R_PC,              R_lookup(addr),      "PC = %s;", addr);
      post(); dumpf(R_Always,  R_FLUSH,                    "JUMP;"); Accessible = FALSE;
    } else {
      if ((HTOI(addr+2) >= rom_start) && (HTOI(addr+2) < rom_end)) {
        // REDUNDANT? dumpf(R_Always|R_PC,              R_lookup(addr),      "PC = %s;", addr);
        // BUG! Only works for hex addresses, does not look up label as in disassembler...:
        if (Single_Step) {
          dumpf(R_Always|R_PC,              R_lookup(addr),      "PC = %s;", addr);
          post(); dumpf(R_Always,  R_FLUSH,                    "JUMP;"); Accessible = FALSE;
        } else {
          post();
          // TO DO: check if 'optimisation' (& ~R_HNZVC) is valid. It may be like the 'branch()' problem that Malban found.
          dumpf(R_Always,          R_FLUSH & ~R_HNZVC, "goto L%s;", UC(addr+2)); Accessible = FALSE;
        }
      } else {
        dumpf(R_Always|R_PC,              R_lookup(addr),      "PC = %s;", addr);
        post(); dumpf(R_Always,  R_FLUSH,                    "JUMP;"); Accessible = FALSE;
      }
    }
  } else {    // Not a vectrex program so don't use Vectrex optimisations...
    int rc;
    unsigned int call;

    dumpf(R_Always|R_PC,              R_lookup(addr),              "PC = %s;", addr);
    rc = sscanf(addr, "%x", &call); // could have used the "0x" test?
    if (Single_Step || (rc != 1)) {       // not a constant jsr target.  Eg "JSR [,X++]"  PC has been assigned above.
                         // Unless this is a known address in the translated code, it will require
                         // support from a fallback interpreter.
      call = 0;
      // TO DO: check if 'optimisation' (& ~R_HNZVC) is valid. It may be like the 'branch()' problem that Malban found.
      post(); dumpf(R_Always,          R_FLUSH & ~R_HNZVC, "JUMP;"); Accessible = FALSE;
    } else if ((call&0xffff) >= 0x8000U) {
      if (*Name[call] != '\0') {        // We know this jsr target in the Vectrex BIOS
        // TO DO: If DP is known at this point, could we also check it against the expected value and warn if there is a mismatch?
        post(); dumpf(R_Always|Out[call], R_PC|In[call], "JUMP;"); Accessible = FALSE;
        
        //dumpf(Trash[call], 0, "// This should not be inserted! Someone is using a trashed register?");
        // if anyone tries to access a register that was trashed by this call,
        // the string above will be inserted.

        //if ((Out[call] & R_DP) != 0) {
          // If we ever do a high level emulation and avoid interpreting the vectrex BIOS, then we can use
          // this data to assign to DP where-ever the BIOS would have:
          //dumpf(R_Always, 0, "DP = 0x%02x << 8; memory_DP = &memory[DP];", DP_Out[call]);
          // Note that this has to be inserted after the return, and the return doesn't
          // happen exactly here - it happens after the label which is planted at the start
          // of the following instruction.  So that will have to be dealt with eventually...
        //}

      } else {
        // A *very few* roms use deeply internal calls in the BIOS that have not been documented
        // as externally usable interfaces.  The warning below is more of a call to document the
        // interface than anything critical to this translation.
        fprintf(stderr, "Warning: undetected bios call to %04x from %04x\n", call, iPC);
        // TO DO: check if 'optimisation' (& ~R_HNZVC) is valid. It may be like the 'branch()' problem that Malban found.
        post(); dumpf(R_Always,          R_FLUSH & ~R_HNZVC, "JUMP;"); Accessible = FALSE;
      }
    } else {
      // internal call - we could probably work out the regs that are required, trashed, or ignores using a transitive closure...
      // for now we'll err on the side of safety by requiring all registers except the flags... (fairly sure there's
      // nothing in the BIOS that takes flag bits as input parameters; and I think there's maybe only one that sets
      // a flag as a return value).
      // TO DO: check if 'optimisation' (& ~R_HNZVC) is valid. It may be like the 'branch()' problem that Malban found.
      post(); dumpf(R_Always,          R_FLUSH & ~R_HNZVC, "JUMP;"); Accessible = FALSE; // remove the exceptions!
    }
  }
}


void
jmp (char *addr)
{
  //    DEF                USE                          CODE
  if (Gen_Switch || (strncmp(addr, "0x", 2) != 0)) {
    dumpf(R_Always|R_PC,     R_lookup(addr),            "PC = %s;", addr);
    post(); dumpf(R_Always,  R_FLUSH,                   "JUMP;"); Accessible = FALSE;
  } else {
    // REDUNDANT?  dumpf(R_Always|R_PC,     R_lookup(addr),              "PC = %s;", addr);
    // BUG! Only works for hex addresses, does not look up label as in disassembler...:
    if (HTOI(addr+2) >= rom_start && HTOI(addr+2) < rom_end) {
      if (Single_Step) {
        dumpf(R_Always|R_PC,     R_lookup(addr),            "PC = %s;", addr);
        post(); dumpf(R_Always,  R_FLUSH,                   "JUMP;"); Accessible = FALSE;
      } else {
        post(); dumpf(R_Always,  R_FLUSH,                   "goto L%s;", UC(addr+2)); Accessible = FALSE;
      }
    } else {
      // Jumping out of this block - label cannot exist, so use raw address...
      dumpf(R_Always|R_PC,     R_lookup(addr),            "PC = %s;", addr);
      post(); dumpf(R_Always,  R_FLUSH,                   "JUMP;"); Accessible = FALSE;
    }
  }
}


void
rti (void)
{
  //    DEF                USE                          CODE
  CK (6);
  dumpf(R_RES,    R_E, "res = E;");  // The conditional removal of extra registers from the stack depends on the *original* E,
  pop_stack8 ("S", "CC"); Individual_Flags_from_CC_Reg (); // not the popped value of E...
  // (pop_stack8 had better not use 'res'...)
  dumpf(R_Always, R_RES, "if (res) {");
    CK (9);
    pop_stack8 ("S", "A");
    pop_stack8 ("S", "B");
    pop_stack8 ("S", "DP"); dumpf(R_DP, R_DP, "DP = DP << 8; checkDP();"); dumpf(R_Always, R_DP, "// memory_DP = &memory[DP];");
    pop_stack16 ("S", "X");
    pop_stack16 ("S", "Y");
    pop_stack16 ("S", "U");
  dumpf(R_Always, 0, "}");
  pop_stack16 ("S", "PC"); // needs special handling
  dumpf(R_Always,           R_FLUSH,                    "JUMP;"); Accessible = FALSE;
}


void
rts (void)
{
  //    DEF                USE                          CODE
  CK (5);
  pop_stack16 ("S", "PC");
  dumpf(R_Always,           R_FLUSH,                    "JUMP;"); Accessible = FALSE;
}


void
swi (void)
{
  //    DEF                USE                          CODE
  CK (19);

  static char tmp[16];

  //dumpf(R_Always|R_PC, 0, "PC = 0x%04x", NextPC&0xffff);

  //sprintf(tmp, "0x%04x", NextPC&0xffff);
  //push_stack16 ("S", tmp); // unclean!  unclean!
  //push_stack16 ("S", "PC");// cleaner
  sprintf(tmp, "0x%02x", NextPC&0xff);
  push_stack8 ("S", tmp);
  sprintf(tmp, "0x%02x", (NextPC>>8)&0xff);
  push_stack8 ("S", tmp); // cleanest...

  push_stack16 ("S", "U");
  push_stack16 ("S", "Y");
  push_stack16 ("S", "X");
  push_stack8 ("S", "(DP >> 8)"); // be careful
  push_stack8 ("S", "B");
  push_stack8 ("S", "A");
  CC_Reg_from_Individual_Flags(); push_stack8 ("S", "CC");

  dumpf(R_Always|R_PC, R_FLUSH, "PC = (memory[0xfffa]<<8)|memory[0xfffb];");
  // TO DO: check if 'optimisation' (& ~R_HNZVC) is valid. It may be like the 'branch()' problem that Malban found.
  dumpf(R_Always, R_FLUSH & ~R_HNZVC, "JUMP;"); Accessible = FALSE;
}


void
swi2 (void)
{
  //    DEF                USE                          CODE
  CK (20);

  static char tmp[16];

  //dumpf(R_Always|R_PC, 0, "PC = 0x%04x", NextPC&0xffff);

  //sprintf(tmp, "0x%04x", NextPC&0xffff);
  //push_stack16 ("S", tmp); // unclean!  unclean!
  //push_stack16 ("S", "PC");// cleaner
  sprintf(tmp, "0x%02x", NextPC&0xff);
  push_stack8 ("S", tmp);
  sprintf(tmp, "0x%02x", (NextPC>>8)&0xff);
  push_stack8 ("S", tmp); // cleanest...

  push_stack16 ("S", "U");
  push_stack16 ("S", "Y");
  push_stack16 ("S", "X");
  push_stack8 ("S", "(DP >> 8)"); // be careful
  push_stack8 ("S", "B");
  push_stack8 ("S", "A");
  CC_Reg_from_Individual_Flags(); push_stack8 ("S", "CC");
  dumpf(R_Always|R_PC, R_FLUSH, "PC = (memory[0xfff4]<<8)|memory[0xfff5];");
  // TO DO: check if 'optimisation' (& ~R_HNZVC) is valid. It may be like the 'branch()' problem that Malban found.
  dumpf(R_Always, R_FLUSH & ~R_HNZVC, "JUMP;"); Accessible = FALSE;
}


void
swi3 (void)
{
  //    DEF                USE                          CODE
  CK (20);

  static char tmp[16];

  //dumpf(R_Always|R_PC, 0, "PC = 0x%04x", NextPC&0xffff);

  //sprintf(tmp, "0x%04x", NextPC&0xffff);
  //push_stack16 ("S", tmp); // unclean!  unclean!
  //push_stack16 ("S", "PC");// cleaner
  sprintf(tmp, "0x%02x", NextPC&0xff);
  push_stack8 ("S", tmp);
  sprintf(tmp, "0x%02x", (NextPC>>8)&0xff); // cleanest...
  push_stack8 ("S", tmp); // cleanest...

  push_stack16 ("S", "U");
  push_stack16 ("S", "Y");
  push_stack16 ("S", "X");
  push_stack8 ("S", "(DP >> 8)");
  push_stack8 ("S", "B");
  push_stack8 ("S", "A");
  CC_Reg_from_Individual_Flags();  push_stack8 ("S", "CC");
  dumpf(R_Always|R_PC, R_FLUSH, "PC = (memory[0xfff2]<<8)|memory[0xfff3];");
  // TO DO: check if 'optimisation' (& ~R_HNZVC) is valid. It may be like the 'branch()' problem that Malban found.
  dumpf(R_Always, R_FLUSH & ~R_HNZVC, "JUMP;"); Accessible = FALSE;
}


void
cwai (void)
{
  //    DEF                USE                          CODE
  dumpf(R_Always,          R_FLUSH,                     "// CWAI - not suported yet!");
  //exit(1);
}


void
sync (void)
{
  //    DEF                USE                          CODE
  CK (4);
  dumpf(R_Always,          R_FLUSH,                     "// SYNC - not suported yet!");
  //exit(1);
}


void
orcc (void)
{
  //    DEF                USE                          CODE
  int tmp = imm_byte ();

  if ((tmp&E_FLAG) != 0) dumpf(R_Always|R_E, 0, "E = 1;");
  if ((tmp&F_FLAG) != 0) dumpf(R_Always|R_F, 0, "F = 1;");
  if ((tmp&I_FLAG) != 0) dumpf(R_Always|R_I, 0, "I = 1;");

  // use our conventions for TRUE and FALSE
  if ((tmp&H_FLAG) != 0) dumpf(R_Always|R_H, 0, "H = 0x10;");
  if ((tmp&N_FLAG) != 0) dumpf(R_Always|R_N, 0, "N = 0x80;");
  if ((tmp&Z_FLAG) != 0) dumpf(R_Always|R_Z, 0, "Z = 0;");
  if ((tmp&V_FLAG) != 0) dumpf(R_Always|R_V, 0, "V = 0x80;");
  if ((tmp&C_FLAG) != 0) dumpf(R_Always|R_C, 0, "C = 1;");

  CK (3);
}


void
andcc (void)
{
  //    DEF                USE                          CODE
  int tmp = imm_byte ();

  if ((tmp&E_FLAG) == 0) dumpf(R_Always|R_E, 0, "E = 0;");
  if ((tmp&F_FLAG) == 0) dumpf(R_Always|R_F, 0, "F = 0;");
  if ((tmp&I_FLAG) == 0) dumpf(R_Always|R_I, 0, "I = 0;");

  // use our conventions for TRUE and FALSE
  if ((tmp&H_FLAG) == 0) dumpf(R_Always|R_H, 0, "H = 0;");
  if ((tmp&N_FLAG) == 0) dumpf(R_Always|R_N, 0, "N = 0;");
  if ((tmp&Z_FLAG) == 0) dumpf(R_Always|R_Z, 0, "Z = 1;");
  if ((tmp&V_FLAG) == 0) dumpf(R_Always|R_V, 0, "V = 0;");
  if ((tmp&C_FLAG) == 0) dumpf(R_Always|R_C, 0, "C = 0;");

  CK (3);
}


/* Branch Instructions */

#define cond_HI() "(Z && (!C))"
#define cond_LS() "((!Z) || C)"
#define cond_HS() "(!C)"
#define cond_LO() "(C)"
#define cond_NE() "(Z)"
#define cond_EQ() "(!Z)"
#define cond_VC() "((SINT8)V >= 0)"
#define cond_VS() "((SINT8)V <  0)"
#define cond_PL() "((SINT8)N >= 0)"
#define cond_MI() "((SINT8)N <  0)"
#define cond_GE() "((SINT8)(N^V) >= 0)"
#define cond_LT() "((SINT8)(N^V) <  0)"
#define cond_GT() "(((SINT8)(N^V) >= 0) && Z)"
// #define cond_GT() "(" cond_GE() " && " cond_NE() ")"
#define cond_LE() "(((SINT8)(N^V) <  0) || (!Z))"
// #define cond_LE() "(" cond_LT() " || " cond_EQ() ")"

signed short SEX8(UINT8 tmp) {
  if (tmp & 0x80) return 0xff00 | tmp;
  return tmp & 0x00ff;
}


void
bra (int TWEAKED_FLUSH)
{
  int IGNORE = R_HNZVC & ~TWEAKED_FLUSH;
  //    DEF                USE                          CODE
  if (Single_Step || Gen_Switch) {
    dumpf(R_Always|R_PC,     0,                         "PC = 0x%04x;", (NextPC+SEX8(imm_byte()))&0xffff );
    post(); dumpf(R_Always,  R_FLUSH & ~IGNORE,         "JUMP;"); Accessible = FALSE;
  } else {
    // REDUNDANT? dumpf(R_Always|R_PC,     0,           "PC = 0x%04x;", (NextPC+SEX8(imm_byte()))&0xffff );
    // BUG! Only works for hex addresses, does not look up label as in disassembler...:
    post(); dumpf(R_Always,  R_FLUSH & ~IGNORE,         "goto L%04X;", (NextPC+SEX8(imm_byte()))&0xffff); Accessible = FALSE;
  }
}


void brn(void)
{
}


void
branch (char *cond)
{
  //    DEF                USE                          CODE
  dumpf(R_Always,          R_FLUSH,                     "// temp fix to ensure flags synched");
  dumpf(R_Always,          R_lookup(cond),              "if %s {", cond);
  bra(R_lookup(cond));
  dumpf(R_Always,          0,                           "}"); Accessible = TRUE;

  CK (3);
}


void
long_bra (void)
{
  //    DEF                USE                          CODE
  if (Single_Step || Gen_Switch) {
    dumpf(R_Always|R_PC,     0,                         "PC = 0x%04x;", (NextPC+imm_word())&0xffff);
    post(); dumpf(R_Always,  R_FLUSH,                   "JUMP;"); Accessible = FALSE;
  } else {
    // REDUNDANT? dumpf(R_Always|R_PC,     0,           "PC = 0x%04x;", (NextPC+imm_word())&0xffff);
    // BUG! Only works for hex addresses, does not look up label as in disassembler...:
    post(); dumpf(R_Always,  R_FLUSH,                   "goto L%04X;", (NextPC+imm_word())&0xffff); Accessible = FALSE;
  }
}


void long_brn(void)
{
}


void
long_branch (char *cond)
{
  //    DEF                USE                          CODE
  CK (5);
  dumpf(R_Always,          R_lookup(cond),              "if %s {", cond);
  CK (1);
  long_bra ();
  dumpf(R_Always,          R_lookup(cond),              "}"); Accessible = TRUE;
}


void
long_bsr (void)
{
  //    DEF                USE                          CODE

  static char tmp[16];
  //sprintf(tmp, "0x%04x", NextPC&0xffff);
  //push_stack16 ("S", tmp); // unclean!  unclean!
  //push_stack16 ("S", "PC");// cleaner
  sprintf(tmp, "0x%02x", NextPC&0xff);
  push_stack8 ("S", tmp);
  sprintf(tmp, "0x%02x", (NextPC>>8)&0xff);
  push_stack8 ("S", tmp); // cleanest...

  CK (9);
  if (Single_Step || Gen_Switch) {
    dumpf(R_Always|R_PC,     0,                         "PC = 0x%04x;", (NextPC+imm_word())&0xffff);
    post(); dumpf(R_Always,  R_FLUSH,                   "JUMP;"); Accessible = FALSE;
  } else {
    // REDUNDANT? dumpf(R_Always|R_PC,     0,           "PC = 0x%04x;", (NextPC+imm_word())&0xffff);
    // BUG! Only works for hex addresses, does not look up label as in disassembler...:
    post(); dumpf(R_Always,  R_FLUSH,                   "goto L%04X;", (NextPC+imm_word())&0xffff); Accessible = FALSE;
  }
}


void
bsr (void)
{
  //    DEF                USE                          CODE

  static char tmp[16];
  //sprintf(tmp, "0x%04x", NextPC&0xffff);
  //push_stack16 ("S", tmp); // unclean!  unclean!
  //push_stack16 ("S", "PC");// cleaner
  sprintf(tmp, "0x%02x", NextPC&0xff);
  push_stack8 ("S", tmp);
  sprintf(tmp, "0x%02x", (NextPC>>8)&0xff);
  push_stack8 ("S", tmp); // cleanest...

  CK (7);
  if (Single_Step || Gen_Switch) {
    dumpf(R_Always|R_PC,     0,                         "PC = 0x%04x;", (NextPC+SEX8(imm_byte()))&0xffff);
    post(); dumpf(R_Always,  R_FLUSH,                   "JUMP;"); Accessible = FALSE;
  } else {
    // REDUNDANT? dumpf(R_Always|R_PC,     0,           "PC = 0x%04x;", (NextPC+SEX8(imm_byte()))&0xffff);
    // BUG! Only works for hex addresses, does not look up label as in disassembler...:
    post(); dumpf(R_Always,  R_FLUSH,                   "goto L%04X;", (NextPC+SEX8(imm_byte()))&0xffff); Accessible = FALSE;
  }
}


void set(char *reg, char *val)
{
  //    DEF                USE                          CODE
  // since it is only X Y, S and U we can skip the cast...
  if (strncmp(val, "(UINT16)", strlen("(UINT16)")) == 0) val = val+strlen("(UINT16)");
  dumpf(R_Always|R_lookup(reg),     R_lookup(val),      "%s = %s;", reg, val);
  if (strcmp(reg, "X") == 0 || strcmp(reg, "Y") == 0) {
    dumpf(R_Z,             R_lookup(reg),               "Z = %s;", reg);
  }
}

/* translate 6809 code */

void translate6809 (int iPC, int NextPCParam, int cycles)
{
  // https://atjs.mbnet.fi/mc6809/Information/6809.htm for checking cycle counts
  /*
     The caller is responsible for identifying code areas and stepping over them. CodePtr is updated here
   in order to point at the second instruction byte if present, then to the start of the operands if present;
   it does *not* step over the operands, which are more reliably found by stepping *back* from NextPC ...
   */
  unsigned opcode;
#define S "S"
#define U "U"
#define X "X"
#define Y "Y"
#define A "A"
#define B "B"

  /* NextPC points to the byte following this instruction. Relative branches etc
     are relative to that address, not to the address of this instruction.
     NextPC is not modified by the opcode fetching process. */
  NextPC = NextPCParam;
  /* CodePTR initially points to the first byte of the current instruction, and
     is moved forward as the instruction and its operands are decoded.  It should
     be equal to NextPC by the time this instruction is fully translated (though
     might not be if the address part is skipped, eg in the "brn" instruction?)
  */
  CodePTR = iPC;

  opcode = memory[CodePTR++];

  // if we want the PC to always be valid (eg for diags) then force it here:
  //dumpf(R_PC, 0, "PC = 0x%04x; /* optional? */", NextPC);
  switch (opcode) {
    case 0x00: CK(4); neg(RDMEM(direct())); break; /* NEG direct */
    case 0x03: CK(4); com(RDMEM(direct())); break; /* COM direct */
    case 0x04: CK(4); lsr(RDMEM(direct())); break; /* LSR direct */
    case 0x06: CK(4); ror(RDMEM(direct())); break; /* ROR direct */
    case 0x07: CK(4); asr(RDMEM(direct())); break; /* ASR direct */
    case 0x08: CK(4); asl(RDMEM(direct())); break; /* ASL direct */
    case 0x09: CK(4); rol(RDMEM(direct())); break; /* ROL direct */
    case 0x0a: CK(4); dec(RDMEM(direct())); break; /* DEC direct */
    case 0x0c: CK(4); inc(RDMEM(direct())); break; /* INC direct */
    case 0x0d: CK(4); tst(RDMEM(direct())); break; /* TST direct */
    case 0x0e: CK(3); jmp(direct());        break; /* JMP direct */
    case 0x0f: CK(4); clr(RDMEM(direct())); break; /* CLR direct */

    case 0x10: {
      opcode = memory[CodePTR++];

      switch (opcode) {
      case 0x21:                        CK(5);      /* PC += 2; */                               break; // lbrn
        case 0x22:                                    long_branch( cond_HI() );                  break;
        case 0x23:                                    long_branch( cond_LS() );                  break;
        case 0x24:                                    long_branch( cond_HS() );                  break;
        case 0x25:                                    long_branch( cond_LO() );                  break;
        case 0x26:                                    long_branch( cond_NE() );                  break;
        case 0x27:                                    long_branch( cond_EQ() );                  break;
        case 0x28:                                    long_branch( cond_VC() );                  break;
        case 0x29:                                    long_branch( cond_VS() );                  break;
        case 0x2a:                                    long_branch( cond_PL() );                  break;
        case 0x2b:                                    long_branch( cond_MI() );                  break;
        case 0x2c:                                    long_branch( cond_GE() );                  break;
        case 0x2d:                                    long_branch( cond_LT() );                  break;
        case 0x2e:                                    long_branch( cond_GT() );                  break;
        case 0x2f:                                    long_branch( cond_LE() );                  break;
        case 0x3f:                                    swi2();                                    break;
        case 0x83:                        CK(5);      cmp16(get_d(),imm_word_s());               break;
        case 0x8c:                        CK(5);      cmp16(Y,imm_word_s());                     break;
        case 0x8e:                        CK(4);      ld16(Y, imm_word_s());                     break;
        case 0x93:                        CK(5);      cmp16(get_d(),RDMEM16(direct())); CK(1);   break;
        case 0x9c:                        CK(5);      cmp16(Y, RDMEM16(direct()));      CK(1);   break;
        case 0x9e:                        CK(5);      ld16(Y, RDMEM16(direct()));                break;
        case 0x9f:                        CK(5);      st16(Y, direct());                         break;
        case 0xa3: CK(1);                             cmp16(get_d(),RDMEM16(indexed())); post(); CK(1);  break;
        case 0xac: CK(1);                             cmp16(Y,RDMEM16(indexed())); post();       CK(1);  break;
        case 0xae: CK(1);                             ld16(Y, RDMEM16(indexed())); post();       break;
        case 0xaf: CK(1);                             st16(Y, indexed()); post();                break;
        case 0xb3:                        CK(6);      cmp16(get_d(),RDMEM16(extended())); CK(1); break;
        case 0xbc:                        CK(6);      cmp16(Y,RDMEM16(extended()));       CK(1); break;
        case 0xbe:                        CK(6);      ld16(Y, RDMEM16(extended()));              break;
        case 0xbf:                        CK(6);      st16(Y, extended());                       break;
        case 0xce:                        CK(4);      ld16(S, imm_word_s());                     break;
        case 0xde:                        CK(5);      ld16(S, RDMEM16(direct()));                break;
        case 0xdf:                        CK(5);      st16(S, direct());                         break; 
        case 0xee: CK(1);                             ld16(S, RDMEM16(indexed())); post();       break;
        case 0xef: CK(1);                             st16(S, indexed()); post();                break; 
        case 0xfe: CK(6);                             ld16(S, RDMEM16(extended()));              break;
        case 0xff:                        CK(6);      st16(S, extended());                       break;
        default:   printf("%X: invalid opcode $10%02X\n", iPC, opcode);
                   // exit(1);
      }           
    }
    break;

    case 0x11: {
      opcode = memory[CodePTR++];

      switch (opcode) {
        case 0x3f:        swi3();                        break;
        case 0x83: CK(5); cmp16(U,imm_word_s());         break;
        case 0x8c: CK(5); cmp16(S,imm_word_s());         break;
        case 0x93: CK(6); cmp16(U,RDMEM16(direct()));    break;
        case 0x9c: CK(6); cmp16(S,RDMEM16(direct()));    break;
        case 0xa3: CK(2); cmp16(U,RDMEM16(indexed())); post();   break;
        case 0xac: CK(2); cmp16(S,RDMEM16(indexed())); post();   break;
        case 0xb3: CK(7); cmp16(U,RDMEM16(extended()));  break;
        case 0xbc: CK(7); cmp16(S,RDMEM16(extended()));  break;
        default:   printf("%X: invalid opcode $11%02X\n",iPC,opcode);
                   // exit(1);
      }
    }
    break;

    case 0x12: nop();                    break;
    case 0x13: sync();                   break;
    case 0x16: long_bra(); CK(5);        break;
    case 0x17: long_bsr();               break;
    case 0x19: daa();                    break;
    case 0x1a: orcc();                   break;
    case 0x1c: andcc();                  break;
    case 0x1d: sex();                    break;
    case 0x1e: exg();                    break;
    case 0x1f: tfr();                    break;

    case 0x20: bra(R_FLUSH);         CK(3);       break;
    case 0x21: brn();                CK(3);       break; // brn
    case 0x22: branch( cond_HI() );               break;
    case 0x23: branch( cond_LS() );               break;
    case 0x24: branch( cond_HS() );               break;
    case 0x25: branch( cond_LO() );               break;
    case 0x26: branch( cond_NE() );               break;
    case 0x27: branch( cond_EQ() );               break;
    case 0x28: branch( cond_VC() );               break;
    case 0x29: branch( cond_VS() );               break;
    case 0x2a: branch( cond_PL() );               break;
    case 0x2b: branch( cond_MI() );               break;
    case 0x2c: branch( cond_GE() );               break;
    case 0x2d: branch( cond_LT() );               break;
    case 0x2e: branch( cond_GT() );               break;
    case 0x2f: branch( cond_LE() );               break;

    case 0x30: set(X, indexed()); post(); break; /* LEAX indexed */  // set_X and set_Y should also set Z
    case 0x31: set(Y, indexed()); post(); break; /* LEAY indexed */
    case 0x32: set(S, indexed()); post(); break; /* LEAS indexed */
    case 0x33: set(U, indexed()); post(); break; /* LEAU indexed */
    case 0x34: pshs();            break; /* PSHS implied */
    case 0x35: puls();            break; /* PULS implied */
    case 0x36: pshu();            break; /* PSHU implied */
    case 0x37: pulu();            break; /* PULU implied */
    case 0x39: rts();             break; /* RTS implied  */
    case 0x3a: abx();             break; /* ABX implied  */
    case 0x3b: rti();             break; /* RTI implied  */
    case 0x3c: cwai();            break; /* CWAI implied */
    case 0x3d: mul();             break; /* MUL implied  */
    case 0x3f: swi();             break; /* SWI implied  */

    case 0x40: neg(A); break; /* NEGA implied */
    case 0x43: com(A); break; /* COMA implied */
    case 0x44: lsr(A); break; /* LSRA implied */
    case 0x46: ror(A); break; /* RORA implied */
    case 0x47: asr(A); break; /* ASRA implied */
    case 0x48: asl(A); break; /* ASLA implied */
    case 0x49: rol(A); break; /* ROLA implied */
    case 0x4a: dec(A); break; /* DECA implied */
    case 0x4c: inc(A); break; /* INCA implied */
    case 0x4d: tst(A); break; /* TSTA implied */
    case 0x4f: clr(A); break; /* CLRA implied */

    case 0x50: neg(B); break; /* NEGB implied */
    case 0x53: com(B); break; /* COMB implied */
    case 0x54: lsr(B); break; /* LSRB implied */
    case 0x56: ror(B); break; /* RORB implied */
    case 0x57: asr(B); break; /* ASRB implied */
    case 0x58: asl(B); break; /* ASLB implied */
    case 0x59: rol(B); break; /* ROLB implied */
    case 0x5a: dec(B); break; /* DECB implied */
    case 0x5c: inc(B); break; /* INCB implied */
    case 0x5d: tst(B); break; /* TSTB implied */
    case 0x5f: clr(B); break; /* CLRB implied */

    case 0x60:        neg(RDMEM(indexed())); post(); break; /* NEG indexed */
    case 0x63:        com(RDMEM(indexed())); post(); break; /* COM indexed */
    case 0x64:        lsr(RDMEM(indexed())); post(); break; /* LSR indexed */
    case 0x66:        ror(RDMEM(indexed())); post(); break; /* ROR indexed */
    case 0x67:        asr(RDMEM(indexed())); post(); break; /* ASR indexed */
    case 0x68:        asl(RDMEM(indexed())); post(); break; /* ASL indexed */
    case 0x69:        rol(RDMEM(indexed())); post(); break; /* ROL indexed */
    case 0x6a:        dec(RDMEM(indexed())); post(); break; /* DEC indexed */
    case 0x6c:        inc(RDMEM(indexed())); post(); break; /* INC indexed */
    case 0x6d:        tst(RDMEM(indexed())); post(); break; /* TST indexed */
    case 0x6e: CK(1); jmp(indexed());                break; /* JMP indexed */   // post() is handled in jmp() code so it comes before JUMP
    case 0x6f:        clr(RDMEM(indexed())); post(); break; /* CLR indexed */

    case 0x70: CK(5); neg(RDMEM(extended())); break; /* NEG extended */
    case 0x73: CK(5); com(RDMEM(extended())); break; /* COM extended */
    case 0x74: CK(5); lsr(RDMEM(extended())); break; /* LSR extended */
    case 0x76: CK(5); ror(RDMEM(extended())); break; /* ROR extended */
    case 0x77: CK(5); asr(RDMEM(extended())); break; /* ASR extended */
    case 0x78: CK(5); asl(RDMEM(extended())); break; /* ASL extended */
    case 0x79: CK(5); rol(RDMEM(extended())); break; /* ROL extended */
    case 0x7a: CK(5); dec(RDMEM(extended())); break; /* DEC extended */
    case 0x7c: CK(5); inc(RDMEM(extended())); break; /* INC extended */
    case 0x7d: CK(5); tst(RDMEM(extended())); break; /* TST extended */
    case 0x7e: CK(4); jmp(extended());        break; /* JMP extended */
    case 0x7f: CK(5); clr(RDMEM(extended())); break; /* CLR extended */

    case 0x80: CK(2); sub(A, imm_byte_s());  break; // TO DO: all these recipients of imm_byte_s need to be checked that they handle signedness correctly
    case 0x81: CK(2); cmp(A, imm_byte_s());  break;
    case 0x82: CK(2); sbc(A, imm_byte_s());  break;
    case 0x83: CK(4); subd(imm_word_s());    break;
    case 0x84: CK(2); and(A, imm_byte_s());  break;
    case 0x85: CK(2); bit(A, imm_byte_s());  break;
    case 0x86: CK(2); ld(A, imm_byte_s());   break;
    case 0x88: CK(2); eor(A, imm_byte_s());  break;
    case 0x89: CK(2); adc(A, imm_byte_s());  break;
    case 0x8a: CK(2); or(A, imm_byte_s());   break;
    case 0x8b: CK(2); add(A, imm_byte_s());  break;
    case 0x8c: CK(4); cmp16(X, imm_word_s());break;
    case 0x8d:        bsr();                 break;
    case 0x8e: CK(3); ld16(X, imm_word_s()); break;

    case 0x90: CK(4); sub(A, RDMEM(direct()));     break;
    case 0x91: CK(4); cmp(A, RDMEM(direct()));     break;
    case 0x92: CK(4); sbc(A, RDMEM(direct()));     break;
    case 0x93: CK(5); subd(RDMEM16(direct()));     break;
    case 0x94: CK(4); and(A, RDMEM(direct()));     break;
    case 0x95: CK(4); bit(A, RDMEM(direct()));     break;
    case 0x96: CK(4); ld(A, RDMEM(direct()));      break;
    case 0x97: CK(4); st(A, direct());             break;
    case 0x98: CK(4); eor(A, RDMEM(direct()));     break;
    case 0x99: CK(4); adc(A, RDMEM(direct()));     break;
    case 0x9a: CK(4); or(A, RDMEM(direct()));      break;
    case 0x9b: CK(4); add(A, RDMEM(direct()));     break;
    case 0x9c: CK(5); cmp16(X, RDMEM16(direct())); break;
    case 0x9d: CK(7); jsr(direct());               break;
    case 0x9e: CK(4); ld16(X, RDMEM16(direct()));  break;
    case 0x9f: CK(4); st16(X, direct());           break;

    case 0xa0:        sub(A, RDMEM(indexed())); post();            break;
    case 0xa1:        cmp(A, RDMEM(indexed())); post();            break;
    case 0xa2:        sbc(A, RDMEM(indexed())); post();            break;
    case 0xa3: CK(1); subd(RDMEM16(indexed())); post();            break;
    case 0xa4:        and(A, RDMEM(indexed())); post();            break;
    case 0xa5:        bit(A, RDMEM(indexed())); post();            break;
    case 0xa6:        ld(A, RDMEM(indexed())); post();             break;
    case 0xa7:        st(A, indexed()); post();                    break;
    case 0xa8:        eor(A, RDMEM(indexed())); post();            break;
    case 0xa9:        adc(A, RDMEM(indexed())); post();            break;
    case 0xaa:        or(A, RDMEM(indexed())); post();             break;
    case 0xab:        add(A, RDMEM(indexed())); post();            break;
    case 0xac: CK(1); cmp16(X, RDMEM16(indexed())); post();        break;
    case 0xad: CK(3); jsr(indexed());                              break;   // AHA!: post() is handled in jsr() code so it comes before JUMP
    case 0xae:        ld16(X, RDMEM16(indexed())); post();         break;
    case 0xaf:        st16(X, indexed()); post();                  break;

    case 0xb0: CK(5); sub(A, RDMEM(extended()));            break;
    case 0xb1: CK(5); cmp(A, RDMEM(extended()));            break;
    case 0xb2: CK(5); sbc(A, RDMEM(extended()));            break;
    case 0xb3: CK(6); subd(RDMEM16(extended()));            break;
    case 0xb4: CK(5); and(A, RDMEM(extended()));            break;
    case 0xb5: CK(5); bit(A, RDMEM(extended()));            break;
    case 0xb6: CK(5); ld(A, RDMEM(extended()));             break;
    case 0xb7: CK(5); st(A, extended());                    break;
    case 0xb8: CK(5); eor(A, RDMEM(extended()));            break;
    case 0xb9: CK(5); adc(A, RDMEM(extended()));            break;
    case 0xba: CK(5); or(A, RDMEM(extended()));             break;
    case 0xbb: CK(5); add(A, RDMEM(extended()));            break;
    case 0xbc: CK(6); cmp16(X, RDMEM16(extended()));        break;
    case 0xbd: CK(8); jsr(extended());                      break;
    case 0xbe: CK(5); ld16(X, RDMEM16(extended()));         break;
    case 0xbf: CK(5); st16(X, extended());                  break;

    case 0xc0: CK(2); sub(B, imm_byte_s());    break;
    case 0xc1: CK(2); cmp(B, imm_byte_s());    break;
    case 0xc2: CK(2); sbc(B, imm_byte_s());    break;
    case 0xc3: CK(4); addd(imm_word_s());      break;
    case 0xc4: CK(2); and(B, imm_byte_s());    break;
    case 0xc5: CK(2); bit(B, imm_byte_s());    break;
    case 0xc6: CK(2); ld(B, imm_byte_s());     break;
    case 0xc8: CK(2); eor(B, imm_byte_s());    break;
    case 0xc9: CK(2); adc(B, imm_byte_s());    break;
    case 0xca: CK(2); or(B, imm_byte_s());     break;
    case 0xcb: CK(2); add(B, imm_byte_s());    break;
    case 0xcc: CK(3); ldd_imm_word();          break;  // ldd_imm_word() == ldd(imm_word_s())
    case 0xce: CK(3); ld16(U, imm_word_s());   break;

    case 0xd0: CK(4); sub(B, RDMEM(direct()));     break;
    case 0xd1: CK(4); cmp(B, RDMEM(direct()));     break;
    case 0xd2: CK(4); sbc(B, RDMEM(direct()));     break;
    case 0xd3: CK(5); addd(RDMEM16(direct()));     break;
    case 0xd4: CK(4); and(B, RDMEM(direct()));     break;
    case 0xd5: CK(4); bit(B, RDMEM(direct()));     break;
    case 0xd6: CK(4); ld(B, RDMEM(direct()));      break;
    case 0xd7: CK(4); st(B, direct());             break;
    case 0xd8: CK(4); eor(B, RDMEM(direct()));     break;
    case 0xd9: CK(4); adc(B, RDMEM(direct()));     break;
    case 0xda: CK(4); or(B, RDMEM(direct()));      break;
    case 0xdb: CK(4); add(B, RDMEM(direct()));     break;
    case 0xdc: CK(4); ldd_direct();                break;  // ldd_direct() == ldd(RDMEM16(direct()))
    case 0xdd: CK(4); std(direct());               break;
    case 0xde: CK(4); ld16(U, RDMEM16(direct()));  break;
    case 0xdf: CK(4); st16(U, direct());           break;

    case 0xe0:        sub(B, RDMEM(indexed())); post();     break;
    case 0xe1:        cmp(B, RDMEM(indexed())); post();     break;
    case 0xe2:        sbc(B, RDMEM(indexed())); post();     break;
    case 0xe3: CK(1); addd(RDMEM16(indexed())); post();     break;
    case 0xe4:        and(B, RDMEM(indexed())); post();     break;
    case 0xe5:        bit(B, RDMEM(indexed())); post();     break;
    case 0xe6:        ld(B, RDMEM(indexed())); post();      break;
    case 0xe7:        st(B, indexed()); post();             break;
    case 0xe8:        eor(B, RDMEM(indexed())); post();     break;
    case 0xe9:        adc(B, RDMEM(indexed())); post();     break;
    case 0xea:        or(B, RDMEM(indexed())); post();      break;
    case 0xeb:        add(B, RDMEM(indexed())); post();     break;
    case 0xec:        ldd_indexed_post();                   break; // ldd_indexed_post() == ldd(RDMEM16(indexed())); post();ldd(RDMEM16(indexed())); post()
    case 0xed:        std(indexed()); post();               break;
    case 0xee:        ld16(U, RDMEM16(indexed())); post();  break;  // TO DO: NEED TO CHECK THINGS LIKE "LDU 3,U++"
    case 0xef:        st16(U, indexed()); post();           break;

    case 0xf0: CK(5); sub(B, RDMEM(extended()));     break;
    case 0xf1: CK(5); cmp(B, RDMEM(extended()));     break;
    case 0xf2: CK(5); sbc(B, RDMEM(extended()));     break;
    case 0xf3: CK(6); addd(RDMEM16(extended()));     break;
    case 0xf4: CK(5); and(B, RDMEM(extended()));     break;
    case 0xf5: CK(5); bit(B, RDMEM(extended()));     break;
    case 0xf6: CK(5); ld(B, RDMEM(extended()));      break;
    case 0xf7: CK(5); st(B, extended());             break;
    case 0xf8: CK(5); eor(B, RDMEM(extended()));     break;
    case 0xf9: CK(5); adc(B, RDMEM(extended()));     break;
    case 0xfa: CK(5); or(B, RDMEM(extended()));      break;
    case 0xfb: CK(5); add(B, RDMEM(extended()));     break;
    case 0xfc: CK(5); ldd_extended();                break; // ldd_extended() == ldd(RDMEM16(extended()))
    case 0xfd: CK(5); std(extended());               break;
    case 0xfe: CK(5); ld16(U, RDMEM16(extended()));  break;
    case 0xff: CK(5); st16(U, extended());           break;
    default:   printf("%04X: invalid opcode $%02X\n",iPC,opcode); exit(1);
  }
}