// Minimal header to allow dkong.c to compile.

//#define INLINE_TRACE 1
#define TRACE_OPCODES 1

#include <stdio.h>
#include <stdlib.h>

#define S_FLAG          0x80 // S = sign, bit 7 of the result byte (accumulator or high byte for 16-bit operations)
#define Z_FLAG          0x40 // zero, set if the result is zero (8 or 16-bit value)
#define X_FLAG          0x20 // undocumented, bit 5 of the result byte
#define H_FLAG          0x10 // half-carry, the carry (theoretical bit 4) of the low nibble of the result byte
#define Y_FLAG          0x20 // undocumented, bit 3 of the result byte
#define V_FLAG          0x04 // overflow (set when crossing the boundary of the signed range)
  #define P_FLAG V_FLAG        // parity (set if the result byte has an even number of bits set)
#define N_FLAG          0x02 // negative, set if the previous operation was a subtraction; always specified
#define C_FLAG          0x01 // carry, the theoretical bit 8 of the result byte

static u_int8_t  IncTable[512];
static u_int8_t  DecTable[512];
static u_int8_t  PostIncCarry[256];
static u_int8_t  AddFlags[256][256];
static u_int8_t  PTable[512];
static u_int8_t  ZSTable[512];
static u_int8_t  ZSPTable[512];
static u_int8_t  ZSPHTable[512];

#include "Z80DAA.h" // static u_int16_t DAATable[2048];

u_int8_t mem[0x10000];

u_int8_t rom[0x4000] =
  #include "dkong-rom.h"

#ifdef INLINE_TRACE
    #ifdef TRACE_OPCODES
#define dumpregs(PC, Mess) fprintf(stdout, "F=%02X (%s%s%s%s%s%s) A=%02X HL=%02X:%02X BC=%02X:%02X DE=%02X:%02X IX=%04X IY=%04X %s", \
                                   RF, \
                                   RF&S_FLAG?"S":" ", \
                                   RF&Z_FLAG?"Z":" ", \
                                   RF&H_FLAG?"H":" ", \
                                   RF&V_FLAG?"V/P":"   ", \
                                   RF&N_FLAG?"N":" ", \
                                   RF&C_FLAG?"C":" ", \
                                   RA, RH, RL, RB, RC, RD, RE, IX, IY, Mess+2)
  #else
  #define dumpregs(PC, Mess)
  #endif
#endif
  

/****************************************************************************/
/* Initialise the various lookup tables used by the emulation code          */
/****************************************************************************/
static void InitTables (void)
{
 static int InitTables_virgin=1;
 u_int8_t zs;
 int i,p;
 if (!InitTables_virgin) return;
 InitTables_virgin=0;
 for (i=0;i<256;++i)
 {
  zs=0;
  if (i==0)
   zs|=Z_FLAG;
  if (i&0x80)
   zs|=S_FLAG;
  p=0;
  if (i&1) ++p;
  if (i&2) ++p;
  if (i&4) ++p;
  if (i&8) ++p;
  if (i&16) ++p;
  if (i&32) ++p;
  if (i&64) ++p;
  if (i&128) ++p;
  PTable[i]    = (p&1) ? 0 : V_FLAG;
  ZSTable[i]   = zs;
  ZSPTable[i]  = zs | PTable[i];
  ZSPHTable[i] = zs | PTable[i] | H_FLAG;
  IncTable[i]  = zs
                 | ( (i==0x80) ? V_FLAG : 0)
                 | ( (i&0x0F)  ? 0 : H_FLAG);
  DecTable[i]  = (zs&C_FLAG)
                 | N_FLAG
                 | ((i==0x7f)    ? V_FLAG : 0)
                 | (((i+1)&0x0F) ? 0 : H_FLAG)
                 | ZSTable[i];
 }
 for (i=0;i<256;++i)
 {
  PostIncCarry[i]  = 0;
  ZSTable[i+256]   = ZSTable[i]  | C_FLAG;
  ZSPTable[i+256]  = ZSPTable[i] | C_FLAG;
  ZSPHTable[i+256] = ZSPTable[i] | C_FLAG;
  IncTable[i+256]  = IncTable[i] | C_FLAG;
  DecTable[i+256]  = DecTable[i] | C_FLAG;
  PTable[i+256]    = PTable[i]   | C_FLAG;
 }
 {int RA, RB, i;
  for (RA = 0; RA < 256; RA++) {
    for (RB = 0; RB < 256; RB++) {
      i = RA+RB;  // Determine flags for additions.
      AddFlags[RA][RB] = ZSTable[i&255]
	| ( (i&256)       >> 8 )   /* or better, (i >> 8)&1 ? */
        | ( (RA^i^RB) & H_FLAG )
        | ( (  (RB^RA^0x80)
             & (RB^i)
	     &  0x80
	    ) >> 5 );
    }
  }
 }
 PostIncCarry[0] = 1; // ff -> 00 means next byte gets a carry
}

void execute(void) {
  typedef union { // 32-bit standard register. We only use 16 bits of it.
    struct {
      u_int32_t i32;
    } s32;
    struct {
#ifdef LSB_FIRST // LITTLE_ENDIAN
      u_int16_t i16;
      u_int16_t dummy;
#else // BIG_ENDIAN
      u_int16_t dummy;
      u_int16_t i16;
#endif
    } s16;
    struct {
#ifdef LSB_FIRST // LITTLE_ENDIAN
      u_int8_t i16_lo;
      u_int8_t i16_hi;
      u_int8_t dummy1;
      u_int8_t dummy2;
#else // BIG_ENDIAN
      u_int8_t dummy2;
      u_int8_t dummy1;
      u_int8_t i16_hi;
      u_int8_t i16_lo;
#endif
    } s8;
  } u16;


#if defined(__arm__)
  register u16 AFU asm ("r6");
#else
  u16 AFU;
#endif
  #define RA AFU.s8.i16_hi
  #define RF AFU.s8.i16_lo
  #define AF AFU.s16.i16

#if defined(__arm__)
  register u16 HLU asm ("r7");
#else
  u16 HLU;
#endif
  #define RH HLU.s8.i16_hi
  #define RL HLU.s8.i16_lo
  #define HL HLU.s16.i16

#if defined(__arm__)
  register u16 BCU asm ("r8");
#else
  u16 BCU;
#endif
  #define RB BCU.s8.i16_hi
  #define RC BCU.s8.i16_lo
  #define BC BCU.s16.i16

#if defined(__arm__)
  register u16 DEU asm ("r9");
#else
  u16 DEU;
#endif
  #define RD DEU.s8.i16_hi
  #define RE DEU.s8.i16_lo
  #define DE DEU.s16.i16
  
  u16 DIX;
  #define RI DIX.s8.i16_hi
  #define RX DIX.s8.i16_lo
  #define IX DIX.s16.i16
  
  u16 DIY;
  #define RJ DIY.s8.i16_hi
  #define RY DIY.s8.i16_lo
  #define IY DIY.s16.i16
  
  u_int16_t HL2, BC2, DE2; // could use structs but not needed as these are only ever exchanged with 16 bit quantities.
  
  //u_int16_t PC;   // PC and SP (and 'i') should be unions as well, even though
                    // they don't exist in the machine.  They will make assigning
                    // to and from the 16 bit quantities from 8 bit components more efficient.
  u16 DPC;
  #define PC DPC.s16.i16
  #define PC_lo DPC.s8.i16_lo
  #define PC_hi DPC.s8.i16_hi
  
#if defined(__arm__)
  //register u_int16_t SP asm ("r10");
  register u16 DSP asm ("r10");
#else
  //u_int16_t SP;
  u16 DSP;
#endif
  #define SP DSP.s16.i16
  #define SP_lo DSP.s8.i16_lo
  #define SP_hi DSP.s8.i16_hi

#ifndef INLINE_TRACE
  #ifdef TRACE_OPCODES
  void dumpregs(u_int16_t PCR, char *Mess) {
    fprintf(stdout, "%04X: F=%02X (%s%s%s%s%s%s) A=%02X HL=%02X:%02X BC=%02X:%02X DE=%02X:%02X IX=%04X IY=%04X %s",
            PCR, RF,				
            RF&S_FLAG?"S":" ", 
            RF&Z_FLAG?"Z":" ", 
            RF&H_FLAG?"H":" ", 
            RF&V_FLAG?"V/P":"   ", 
            RF&N_FLAG?"N":" ", 
            RF&C_FLAG?"C":" ", 
	    RA, RH, RL, RB, RC, RD, RE, IX, IY, Mess+2);
  }
  #else
  #define dumpregs(PC, Mess)
  #endif
#endif

  u_int16_t i;
  u_int8_t j,k;
  static void *lab[0x10000] = {
    #include "dkong-lab.h"
  };
  PC = SP = AF = HL = HL2 = BC = BC2 = DE = DE2 = IX = IY = 0;
  goto *lab[0];  
  #include "dkong-sbt.c"
  L4000: ;
  NotSet: ;
}

int main(int argc, char **argv) {
  int addr;
#if defined(__arm__) && defined(TRACE_OPCODES)
  fprintf(stderr, "* Slaving Z80 registers to ARM registers\n");
#endif
  for (addr = 0; addr < 0x4000; addr++) mem[addr] = rom[addr];
  for (addr = 0x4000; addr < 0x10000; addr++) mem[addr] = 0;
  InitTables();
  execute();
  return 0;
}
