#include "ibm.h"
#include "cpu.h"
#include "x86.h"
#include "x86_ops.h"
#include "x87.h"
#include "mem.h"
#include "codegen.h"
#include "codegen_ops.h"
#include "codegen_timing_common.h"

#define CYCLES(c) (int *)c
#define CYCLES2(c16, c32) (int *)((-1 & ~0xffff) | c16 | (c32 << 8))

static int *opcode_timings[256] =
{
/*00*/  &timing_mr,     &timing_mr,     &timing_rm,     &timing_rm,     &timing_rr,     &timing_rr,     CYCLES(2),      CYCLES(3),      &timing_mr,     &timing_mr,     &timing_rm,     &timing_rm,     &timing_rr,     &timing_rr,     CYCLES(2),      NULL,
/*10*/  &timing_mr,     &timing_mr,     &timing_rm,     &timing_rm,     &timing_rr,     &timing_rr,     CYCLES(2),      CYCLES(3),      &timing_mr,     &timing_mr,     &timing_rm,     &timing_rm,     &timing_rr,     &timing_rr,     CYCLES(2),      CYCLES(3),
/*20*/  &timing_mr,     &timing_mr,     &timing_rm,     &timing_rm,     &timing_rr,     &timing_rr,     CYCLES(4),      CYCLES(3),      &timing_mr,     &timing_mr,     &timing_rm,     &timing_rm,     &timing_rr,     &timing_rr,     CYCLES(4),      CYCLES(3),
/*30*/  &timing_mr,     &timing_mr,     &timing_rm,     &timing_rm,     &timing_rr,     &timing_rr,     CYCLES(4),      CYCLES(2),      &timing_mr,     &timing_mr,     &timing_rm,     &timing_rm,     &timing_rr,     &timing_rr,     CYCLES(4),      CYCLES(2),

/*40*/  &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,
/*50*/  CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),
/*60*/  CYCLES(11),     CYCLES(9),      CYCLES(7),      CYCLES(9),      CYCLES(4),      CYCLES(4),      CYCLES(2),      CYCLES(2),      CYCLES(1),      CYCLES2(17,25), CYCLES(1),      CYCLES2(17,20), CYCLES(17),     CYCLES(17),     CYCLES(17),     CYCLES(17),
/*70*/  &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,

/*80*/  &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_rm,     &timing_rm,     CYCLES(5),      CYCLES(5),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(3),      CYCLES(1),      CYCLES(5),      CYCLES(6),
/*90*/  CYCLES(1),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(0),      CYCLES(4),      CYCLES(4),      CYCLES(5),      CYCLES(2),      CYCLES(3),
/*a0*/  CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(7),      CYCLES(7),      CYCLES(8),      CYCLES(8),      CYCLES(1),      CYCLES(1),      CYCLES(5),      CYCLES(5),      CYCLES(5),      CYCLES(5),      CYCLES(6),      CYCLES(6),
/*b0*/  &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,

/*c0*/  CYCLES(4),      CYCLES(4),      CYCLES(5),      CYCLES(5),      CYCLES(6),      CYCLES(6),      CYCLES(1),      CYCLES(1),      CYCLES(14),     CYCLES(5),      CYCLES(0),      CYCLES(0),      &timing_int,    &timing_int,    CYCLES(3),      CYCLES(0),
/*d0*/  CYCLES(4),      CYCLES(4),      CYCLES(4),      CYCLES(4),      CYCLES(15),     CYCLES(14),     CYCLES(2),      CYCLES(4),      NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
/*e0*/  CYCLES(6),      CYCLES(6),      CYCLES(6),      CYCLES(5),      CYCLES(14),     CYCLES(14),     CYCLES(16),     CYCLES(16),     CYCLES(3),      CYCLES(3),      CYCLES(17),     CYCLES(3),      CYCLES(14),     CYCLES(14),     CYCLES(14),     CYCLES(14),
/*f0*/  CYCLES(4),      CYCLES(0),      CYCLES(0),      CYCLES(0),      CYCLES(4),      CYCLES(2),      NULL,           NULL,           CYCLES(2),      CYCLES(2),      CYCLES(3),      CYCLES(2),      CYCLES(2),      CYCLES(2),      CYCLES(3),      NULL
};

static int *opcode_timings_mod3[256] =
{
/*00*/  &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     CYCLES(2),      CYCLES(3),      &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     CYCLES(2),      NULL,
/*10*/  &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     CYCLES(2),      CYCLES(3),      &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     CYCLES(2),      CYCLES(3),
/*20*/  &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     CYCLES(4),      CYCLES(3),      &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     CYCLES(4),      CYCLES(3),
/*30*/  &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     CYCLES(4),      CYCLES(2),      &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     CYCLES(4),      CYCLES(2),

/*40*/  &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,
/*50*/  CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),
/*60*/  CYCLES(11),     CYCLES(9),      CYCLES(7),      CYCLES(9),      CYCLES(4),      CYCLES(4),      CYCLES(2),      CYCLES(2),      CYCLES(1),      CYCLES2(14,25), CYCLES(1),      CYCLES2(17,20), CYCLES(17),     CYCLES(17),     CYCLES(17),     CYCLES(17),
/*70*/  &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,

/*80*/  &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     CYCLES(5),      CYCLES(5),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(2),      CYCLES(1),      CYCLES(2),      CYCLES(1),
/*90*/  CYCLES(1),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(0),      CYCLES(4),      CYCLES(4),      CYCLES(5),      CYCLES(2),      CYCLES(3),
/*a0*/  CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(7),      CYCLES(7),      CYCLES(8),      CYCLES(8),      CYCLES(1),      CYCLES(1),      CYCLES(5),      CYCLES(5),      CYCLES(5),      CYCLES(5),      CYCLES(6),      CYCLES(6),
/*b0*/  &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,

/*c0*/  CYCLES(4),      CYCLES(4),      CYCLES(5),      CYCLES(5),      CYCLES(6),      CYCLES(6),      CYCLES(1),      CYCLES(1),      CYCLES(14),     CYCLES(5),      CYCLES(0),      CYCLES(0),      &timing_int,    &timing_int,    CYCLES(3),      CYCLES(0),
/*d0*/  CYCLES(4),      CYCLES(4),      CYCLES(4),      CYCLES(4),      CYCLES(15),     CYCLES(14),     CYCLES(2),      CYCLES(4),      NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
/*e0*/  CYCLES(6),      CYCLES(6),      CYCLES(6),      CYCLES(5),      CYCLES(14),     CYCLES(14),     CYCLES(16),     CYCLES(16),     CYCLES(3),      CYCLES(3),      CYCLES(17),     CYCLES(3),      CYCLES(14),     CYCLES(14),     CYCLES(14),     CYCLES(14),
/*f0*/  CYCLES(4),      CYCLES(0),      CYCLES(0),      CYCLES(0),      CYCLES(4),      CYCLES(2),      NULL,           NULL,           CYCLES(2),      CYCLES(2),      CYCLES(3),      CYCLES(2),      CYCLES(2),      CYCLES(2),      CYCLES(3),      NULL
};

static int *opcode_timings_0f[256] =
{
/*00*/  CYCLES(20),     CYCLES(11),     CYCLES(11),     CYCLES(10),     NULL,           CYCLES(195),    CYCLES(7),      NULL,           CYCLES(1000),   CYCLES(10000),  NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
/*10*/  NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
/*20*/  CYCLES(6),      CYCLES(6),      CYCLES(6),      CYCLES(6),      CYCLES(6),      CYCLES(6),      NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
/*30*/  CYCLES(9),      CYCLES(1),      CYCLES(9),      NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,

/*40*/  NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
/*50*/  NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
/*60*/  &timing_rm,     &timing_rm,     &timing_rm,     &timing_rm,     &timing_rm,     &timing_rm,     &timing_rm,     &timing_rm,     &timing_rm,     &timing_rm,     &timing_rm,     &timing_rm,     NULL,           NULL,           &timing_rm,     &timing_rm,
/*70*/  NULL,           &timing_rm,     &timing_rm,     &timing_rm,     &timing_rm,     &timing_rm,     &timing_rm,     CYCLES(100),    NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           &timing_rm,     &timing_rm,

/*80*/  &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,
/*90*/  CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),
/*a0*/  CYCLES(3),      CYCLES(3),      CYCLES(14),     CYCLES(8),      CYCLES(3),      CYCLES(4),      NULL,           NULL,           CYCLES(3),      CYCLES(3),      NULL,           CYCLES(13),     CYCLES(3),      CYCLES(3),      NULL,           CYCLES2(18,30),
/*b0*/  CYCLES(10),     CYCLES(10),     CYCLES(6),      CYCLES(13),     CYCLES(6),      CYCLES(6),      CYCLES(3),      CYCLES(3),      NULL,           NULL,           CYCLES(6),      CYCLES(13),     CYCLES(7),      CYCLES(7),      CYCLES(3),      CYCLES(3),

/*c0*/  CYCLES(4),      CYCLES(4),      NULL,           NULL,           NULL,           NULL,           NULL,           CYCLES(3),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),
/*d0*/  NULL,           &timing_rm,     &timing_rm,     &timing_rm,     NULL,           &timing_rm,     NULL,           NULL,           &timing_rm,     &timing_rm,     NULL,           &timing_rm,     &timing_rm,     &timing_rm,     NULL,           &timing_rm,
/*e0*/  NULL,           &timing_rm,     &timing_rm,     NULL,           NULL,           &timing_rm,     NULL,           NULL,           &timing_rm,     &timing_rm,     NULL,           &timing_rm,     &timing_rm,     &timing_rm,     NULL,           &timing_rm,
/*f0*/  NULL,           &timing_rm,     &timing_rm,     &timing_rm,     NULL,           &timing_rm,     NULL,           NULL,           &timing_rm,     &timing_rm,     &timing_rm,     NULL,           &timing_rm,     &timing_rm,     &timing_rm,     NULL,
};
static int *opcode_timings_0f_mod3[256] =
{
/*00*/  CYCLES(20),     CYCLES(11),     CYCLES(11),     CYCLES(10),     NULL,           CYCLES(195),    CYCLES(7),      NULL,           CYCLES(1000),   CYCLES(10000),  NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
/*10*/  NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
/*20*/  CYCLES(6),      CYCLES(6),      CYCLES(6),      CYCLES(6),      CYCLES(6),      CYCLES(6),      NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
/*30*/  CYCLES(9),      CYCLES(1),      CYCLES(9),      NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,

/*40*/  NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
/*50*/  NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
/*60*/  &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     NULL,           NULL,           &timing_rr,     &timing_rr,
/*70*/  NULL,           &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     &timing_rr,     CYCLES(100),    NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           &timing_rr,     &timing_rr,

/*80*/  &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,    &timing_bnt,
/*90*/  CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3),
/*a0*/  CYCLES(3),      CYCLES(3),      CYCLES(14),     CYCLES(8),      CYCLES(3),      CYCLES(4),      NULL,           NULL,           CYCLES(3),      CYCLES(3),      NULL,           CYCLES(13),     CYCLES(3),      CYCLES(3),      NULL,           CYCLES2(18,30),
/*b0*/  CYCLES(10),     CYCLES(10),     CYCLES(6),      CYCLES(13),     CYCLES(6),      CYCLES(6),      CYCLES(3),      CYCLES(3),      NULL,           NULL,           CYCLES(6),      CYCLES(13),     CYCLES(7),      CYCLES(7),      CYCLES(3),      CYCLES(3),

/*c0*/  CYCLES(4),      CYCLES(4),      NULL,           NULL,           NULL,           NULL,           NULL,           CYCLES(3),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),
/*d0*/  NULL,           &timing_rr,     &timing_rr,     &timing_rr,     NULL,           &timing_rr,     NULL,           NULL,           &timing_rr,     &timing_rr,     NULL,           &timing_rr,     &timing_rr,     &timing_rr,     NULL,           &timing_rr,
/*e0*/  NULL,           &timing_rr,     &timing_rr,     NULL,           NULL,           &timing_rr,     NULL,           NULL,           &timing_rr,     &timing_rr,     NULL,           &timing_rr,     &timing_rr,     &timing_rr,     NULL,           &timing_rr,
/*f0*/  NULL,           &timing_rr,     &timing_rr,     &timing_rr,     NULL,           &timing_rr,     NULL,           NULL,           &timing_rr,     &timing_rr,     &timing_rr,     NULL,           &timing_rr,     &timing_rr,     &timing_rr,     NULL,
};

static int *opcode_timings_shift[8] =
{
        CYCLES(7),      CYCLES(7),      CYCLES(10),     CYCLES(10),     CYCLES(7),      CYCLES(7),      CYCLES(7),      CYCLES(7)
};
static int *opcode_timings_shift_mod3[8] =
{
        CYCLES(3),      CYCLES(3),      CYCLES(9),      CYCLES(9),      CYCLES(3),      CYCLES(3),      CYCLES(3),      CYCLES(3)
};

static int *opcode_timings_f6[8] =
{
        &timing_rm,     NULL,           &timing_mm,     &timing_mm,     CYCLES(13),     CYCLES(14),     CYCLES(16),     CYCLES(19)
};
static int *opcode_timings_f6_mod3[8] =
{
        &timing_rr,     NULL,           &timing_rr,     &timing_rr,     CYCLES(13),     CYCLES(14),     CYCLES(16),     CYCLES(19)
};
static int *opcode_timings_f7[8] =
{
        &timing_rm,     NULL,           &timing_mm,     &timing_mm,     CYCLES(21),     CYCLES2(22,38), CYCLES2(24,40), CYCLES2(27,43)
};
static int *opcode_timings_f7_mod3[8] =
{
        &timing_rr,     NULL,           &timing_rr,     &timing_rr,     CYCLES(21),     CYCLES2(22,38), CYCLES2(24,40), CYCLES2(27,43)
};
static int *opcode_timings_ff[8] =
{
        &timing_mm,     &timing_mm,     CYCLES(5),      CYCLES(0),      CYCLES(5),      CYCLES(0),      CYCLES(5),      NULL
};
static int *opcode_timings_ff_mod3[8] =
{
        &timing_rr,     &timing_rr,     CYCLES(5),      CYCLES(0),      CYCLES(5),      CYCLES(0),      CYCLES(5),      NULL
};

static int *opcode_timings_d8[8] =
{
/*      FADDil          FMULil          FCOMil          FCOMPil         FSUBil          FSUBRil         FDIVil          FDIVRil*/
        CYCLES(10),     CYCLES(12),     CYCLES(9),      CYCLES(9),      CYCLES(10),     CYCLES(10),     CYCLES(78),     CYCLES(78)
};
static int *opcode_timings_d8_mod3[8] =
{
/*      FADD            FMUL            FCOM            FCOMP           FSUB            FSUBR           FDIV            FDIVR*/
        CYCLES(4),      CYCLES(6),      CYCLES(3),      CYCLES(3),      CYCLES(4),      CYCLES(4),      CYCLES(72),     CYCLES(72)
};

static int *opcode_timings_d9[8] =
{
/*      FLDs                            FSTs            FSTPs           FLDENV          FLDCW           FSTENV          FSTCW*/
        CYCLES(2),      NULL,           CYCLES(7),      CYCLES(7),      CYCLES(34),     CYCLES(4),      CYCLES(67),     CYCLES(3)
};
static int *opcode_timings_d9_mod3[64] =
{
        /*FLD*/
        CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),
        /*FXCH*/
        CYCLES(4),      CYCLES(4),      CYCLES(4),      CYCLES(4),      CYCLES(4),      CYCLES(4),      CYCLES(4),      CYCLES(4),
        /*FNOP*/
        CYCLES(7),      NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
        /*FSTP*/
        CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),      CYCLES(1),
/*      opFCHS          opFABS                                          opFTST          opFXAM*/
        CYCLES(2),      CYCLES(2),      NULL,           NULL,           CYCLES(5),      CYCLES(7),      NULL,           NULL,
/*      opFLD1          opFLDL2T        opFLDL2E        opFLDPI         opFLDEG2        opFLDLN2        opFLDZ*/
        CYCLES(5),      CYCLES(7),      CYCLES(7),      CYCLES(7),      CYCLES(7),      CYCLES(7),      CYCLES(5),      NULL,
/*      opF2XM1         opFYL2X         opFPTAN         opFPATAN                                        opFDECSTP       opFINCSTP,*/
        CYCLES(300),    CYCLES(58),     CYCLES(676),    CYCLES(355),    NULL,           NULL,           CYCLES(3),      CYCLES(3),
/*      opFPREM                         opFSQRT         opFSINCOS       opFRNDINT       opFSCALE        opFSIN          opFCOS*/
        CYCLES(70),     NULL,           CYCLES(72),     CYCLES(292),    CYCLES(21),     CYCLES(30),     CYCLES(474),    CYCLES(474)
};

static int *opcode_timings_da[8] =
{
/*      FADDil          FMULil          FCOMil          FCOMPil         FSUBil          FSUBRil         FDIVil          FDIVRil*/
        CYCLES(10),     CYCLES(12),     CYCLES(9),      CYCLES(9),      CYCLES(10),     CYCLES(10),     CYCLES(78),     CYCLES(78)
};
static int *opcode_timings_da_mod3[8] =
{
        NULL,           NULL,           NULL,           NULL,           NULL,           CYCLES(5),      NULL,           NULL
};


static int *opcode_timings_db[8] =
{
/*      FLDil                           FSTil           FSTPil                          FLDe                            FSTPe*/
        CYCLES(6),      NULL,           CYCLES(7),      CYCLES(7),      NULL,           CYCLES(8),      NULL,           CYCLES(8)
};
static int *opcode_timings_db_mod3[64] =
{
        NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
        NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
        NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
        NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
/*                      opFNOP          opFCLEX         opFINIT         opFNOP          opFNOP*/
        NULL,           CYCLES(7),      CYCLES(18),     CYCLES(27),     CYCLES(7),      CYCLES(7),      NULL,
        NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
        NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
        NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,           NULL,
};

static int *opcode_timings_dc[8] =
{
/*      opFADDd_a16     opFMULd_a16     opFCOMd_a16     opFCOMPd_a16    opFSUBd_a16     opFSUBRd_a16    opFDIVd_a16     opFDIVRd_a16*/
        CYCLES(6),      CYCLES(8),      CYCLES(5),      CYCLES(5),      CYCLES(6),      CYCLES(6),      CYCLES(74),     CYCLES(74)
};
static int *opcode_timings_dc_mod3[8] =
{
/*      opFADDr         opFMULr                                         opFSUBRr        opFSUBr         opFDIVRr        opFDIVr*/
        CYCLES(4),      CYCLES(6),      NULL,           NULL,           CYCLES(4),      CYCLES(4),      CYCLES(72),     CYCLES(72)
};

static int *opcode_timings_dd[8] =
{
/*      FLDd                            FSTd            FSTPd           FRSTOR                           FSAVE          FSTSW*/
        CYCLES(2),      NULL,           CYCLES(8),      CYCLES(8),      CYCLES(131),     NULL,           CYCLES(154),   CYCLES(5)
};
static int *opcode_timings_dd_mod3[8] =
{
/*      FFFREE                          FST             FSTP            FUCOM            FUCOMP*/
        CYCLES(3),      NULL,           CYCLES(1),      CYCLES(1),      CYCLES(3),       CYCLES(3),      NULL,          NULL
};

static int *opcode_timings_de[8] =
{
/*      FADDiw          FMULiw          FCOMiw          FCOMPiw         FSUBil          FSUBRil         FDIVil          FDIVRil*/
        CYCLES(10),     CYCLES(12),     CYCLES(9),      CYCLES(9),      CYCLES(10),     CYCLES(10),     CYCLES(78),     CYCLES(78)
};
static int *opcode_timings_de_mod3[8] =
{
/*      FADD            FMUL                            FCOMPP          FSUB            FSUBR           FDIV            FDIVR*/
        CYCLES(4),      CYCLES(6),      NULL,           CYCLES(3),      CYCLES(4),      CYCLES(4),      CYCLES(72),     CYCLES(72)
};

static int *opcode_timings_df[8] =
{
/*      FILDiw                          FISTiw          FISTPiw                          FILDiq          FBSTP          FISTPiq*/
        CYCLES(6),      NULL,           CYCLES(7),      CYCLES(7),      NULL,            CYCLES(8),      CYCLES(172),   CYCLES(8)
};
static int *opcode_timings_df_mod3[8] =
{
/*      FFREE                           FST             FSTP            FUCOM            FUCOMP*/
        CYCLES(3),      NULL,           CYCLES(1),      CYCLES(1),      CYCLES(3),       CYCLES(3),      NULL,          NULL
};

static int *opcode_timings_8x[8] =
{
        &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_rm
};
static int *opcode_timings_8x_mod3[8] =
{
        &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_rm
};
static int *opcode_timings_81[8] =
{
        &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_rm
};
static int *opcode_timings_81_mod3[8] =
{
        &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_mr,     &timing_rm
};

static int timing_count;
static uint8_t last_prefix;
static uint32_t regmask_modified;

static inline int COUNT(int *c, int op_32)
{
        if ((uintptr_t)c <= 10000)
                return (int)(uintptr_t)c;
        if (((uintptr_t)c & ~0xffff) == (-1 & ~0xffff))
        {
                if (op_32 & 0x100)
                        return ((uintptr_t)c >> 8) & 0xff;
                return (uintptr_t)c & 0xff;
        }
        return *c;
}

void codegen_timing_winchip_block_start()
{
        regmask_modified = 0;
}

void codegen_timing_winchip_start()
{
        timing_count = 0;
        last_prefix = 0;
}

void codegen_timing_winchip_prefix(uint8_t prefix, uint32_t fetchdat)
{
        timing_count += COUNT(opcode_timings[prefix], 0);
        last_prefix = prefix;
}

void codegen_timing_winchip_opcode(uint8_t opcode, uint32_t fetchdat, int op_32, uint32_t op_pc)
{
        int **timings;
        uint64_t *deps;
        int mod3 = ((fetchdat & 0xc0) == 0xc0);
        int bit8 = !(opcode & 1);

        switch (last_prefix)
        {
                case 0x0f:
                timings = mod3 ? opcode_timings_0f_mod3 : opcode_timings_0f;
                deps = mod3 ? opcode_deps_0f_mod3 : opcode_deps_0f;
//                pclog("timings 0f\n");
                break;
                
                case 0xd8:
                timings = mod3 ? opcode_timings_d8_mod3 : opcode_timings_d8;
                deps = mod3 ? opcode_deps_d8_mod3 : opcode_deps_d8;
                opcode = (opcode >> 3) & 7;
//                pclog("timings d8\n");
                break;
                case 0xd9:
                timings = mod3 ? opcode_timings_d9_mod3 : opcode_timings_d9;
                deps = mod3 ? opcode_deps_d9_mod3 : opcode_deps_d9;
                opcode = mod3 ? opcode & 0x3f : (opcode >> 3) & 7;
//                pclog("timings d9\n");
                break;
                case 0xda:
                timings = mod3 ? opcode_timings_da_mod3 : opcode_timings_da;
                deps = mod3 ? opcode_deps_da_mod3 : opcode_deps_da;
                opcode = (opcode >> 3) & 7;
//                pclog("timings da\n");
                break;
                case 0xdb:
                timings = mod3 ? opcode_timings_db_mod3 : opcode_timings_db;
                deps = mod3 ? opcode_deps_db_mod3 : opcode_deps_db;
                opcode = mod3 ? opcode & 0x3f : (opcode >> 3) & 7;
//                pclog("timings db\n");
                break;
                case 0xdc:
                timings = mod3 ? opcode_timings_dc_mod3 : opcode_timings_dc;
                deps = mod3 ? opcode_deps_dc_mod3 : opcode_deps_dc;
                opcode = (opcode >> 3) & 7;
//                pclog("timings dc\n");
                break;
                case 0xdd:
                timings = mod3 ? opcode_timings_dd_mod3 : opcode_timings_dd;
                deps = mod3 ? opcode_deps_dd_mod3 : opcode_deps_dd;
                opcode = (opcode >> 3) & 7;
//                pclog("timings dd\n");
                break;
                case 0xde:
                timings = mod3 ? opcode_timings_de_mod3 : opcode_timings_de;
                deps = mod3 ? opcode_deps_de_mod3 : opcode_deps_de;
                opcode = (opcode >> 3) & 7;
//                pclog("timings de\n");
                break;
                case 0xdf:
                timings = mod3 ? opcode_timings_df_mod3 : opcode_timings_df;
                deps = mod3 ? opcode_deps_df_mod3 : opcode_deps_df;
                opcode = (opcode >> 3) & 7;
//                pclog("timings df\n");
                break;

                default:
                switch (opcode)
                {
                        case 0x80: case 0x82: case 0x83:
                        timings = mod3 ? opcode_timings_8x_mod3 : opcode_timings_8x;
                        deps = mod3 ? opcode_deps_8x_mod3 : opcode_deps_8x_mod3;
                        opcode = (fetchdat >> 3) & 7;
//                        pclog("timings 80 %p %p %p\n", (void *)timings, (void *)opcode_timings_mod3, (void *)opcode_timings_8x);
                        break;
                        case 0x81:
                        timings = mod3 ? opcode_timings_81_mod3 : opcode_timings_81;
                        deps = mod3 ? opcode_deps_81_mod3 : opcode_deps_81;
                        opcode = (fetchdat >> 3) & 7;
//                        pclog("timings 80 %p %p %p\n", (void *)timings, (void *)opcode_timings_mod3, (void *)opcode_timings_8x);
                        break;
                                
                        case 0xc0: case 0xc1: case 0xd0: case 0xd1: case 0xd2: case 0xd3:
                        timings = mod3 ? opcode_timings_shift_mod3 : opcode_timings_shift;
                        deps = mod3 ? opcode_deps_shift_mod3 : opcode_deps_shift;
                        opcode = (fetchdat >> 3) & 7;
//                        pclog("timings c0\n");
                        break;
                        
                        case 0xf6:
                        timings = mod3 ? opcode_timings_f6_mod3 : opcode_timings_f6;
                        deps = mod3 ? opcode_deps_f6_mod3 : opcode_deps_f6;
                        opcode = (fetchdat >> 3) & 7;
//                        pclog("timings f6\n");
                        break;
                        case 0xf7:
                        timings = mod3 ? opcode_timings_f7_mod3 : opcode_timings_f7;
                        deps = mod3 ? opcode_deps_f7_mod3 : opcode_deps_f7;
                        opcode = (fetchdat >> 3) & 7;
//                        pclog("timings f7\n");
                        break;
                        case 0xff:
                        timings = mod3 ? opcode_timings_ff_mod3 : opcode_timings_ff;
                        deps = mod3 ? opcode_deps_ff_mod3 : opcode_deps_ff;
                        opcode = (fetchdat >> 3) & 7;
//                        pclog("timings ff\n");
                        break;

                        default:
                        timings = mod3 ? opcode_timings_mod3 : opcode_timings;
                        deps = mod3 ? opcode_deps_mod3 : opcode_deps;
//                        pclog("timings normal\n");
                        break;
                }
        }
        
        timing_count += COUNT(timings[opcode], op_32);
        if (regmask_modified & get_addr_regmask(deps[opcode], fetchdat, op_32))
                timing_count++; /*AGI stall*/
        codegen_block_cycles += timing_count;
        
        regmask_modified = get_dstdep_mask(deps[opcode], fetchdat, bit8);
}

void codegen_timing_winchip_block_end()
{
}

int codegen_timing_winchip_jump_cycles()
{
        return 0;
}

codegen_timing_t codegen_timing_winchip =
{
        codegen_timing_winchip_start,
        codegen_timing_winchip_prefix,
        codegen_timing_winchip_opcode,
        codegen_timing_winchip_block_start,
        codegen_timing_winchip_block_end,
        codegen_timing_winchip_jump_cycles
};
