/*  Copyright 2003-2004 Stephane Dallongeville

    This file is part of Yabause.

    Yabause 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.

    Yabause 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 Yabause; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

#include <stdarg.h>
#include <string.h>

#define	EA_DREG         0
#define	EA_AREG         1
#define	EA_AIND         2
#define	EA_AINC         3
#define	EA_ADEC         4
#define	EA_D16A         5
#define	EA_D8AX         6
#define	EA_A16          7
#define	EA_A32          8
#define	EA_D16P         9
#define	EA_D8PX		    10
#define	EA_IMM		    11
#define	EA_AINC7        12
#define	EA_ADEC7	    13
#define	EA_ILLEGAL      15

#define	SIZE_BYTE       0
#define	SIZE_WORD       1
#define	SIZE_LONG       2

#define COND_TR         0
#define COND_FA         1
#define COND_HI         2
#define COND_LS         3
#define COND_CC         4
#define COND_CS         5
#define COND_NE         6
#define COND_EQ         7
#define COND_VC         8
#define COND_VS         9
#define COND_PL         10
#define COND_MI         11
#define COND_GE         12
#define COND_LT         13
#define COND_GT         14
#define COND_LE         15

#define COND_NOT_TR     COND_FA
#define COND_NOT_FA     COND_TR
#define COND_NOT_HI     COND_LS
#define COND_NOT_LS     COND_HI
#define COND_NOT_CC     COND_CS
#define COND_NOT_CS     COND_CC
#define COND_NOT_NE     COND_EQ
#define COND_NOT_EQ     COND_NE
#define COND_NOT_VC     COND_VS
#define COND_NOT_VS     COND_VC
#define COND_NOT_PL     COND_MI
#define COND_NOT_MI     COND_PL
#define COND_NOT_GE     COND_LT
#define COND_NOT_LT     COND_GE
#define COND_NOT_GT     COND_LE
#define COND_NOT_LE     COND_GT

#define	OP_ILLEGAL      0x4AFC

static void GenORI(void);
static void GenORICCR(void);
static void GenORISR(void);
static void GenANDI(void);
static void GenANDICCR(void);
static void GenANDISR(void);
static void GenEORI(void);
static void GenEORICCR(void);
static void GenEORISR(void);
static void GenSUBI(void);
static void GenADDI(void);
static void GenCMPI(void);
static void GenBTSTn(void);
static void GenBCHGn(void);
static void GenBCLRn(void);
static void GenBSETn(void);
static void GenBTST(void);
static void GenBCHG(void);
static void GenBCLR(void);
static void GenBSET(void);
static void GenMOVEPWaD(void);
static void GenMOVEPLaD(void);
static void GenMOVEPWDa(void);
static void GenMOVEPLDa(void);
static void GenMOVEB(void);
static void GenMOVEL(void);
static void GenMOVEW(void);
static void GenMOVEAL(void);
static void GenMOVEAW(void);
static void GenNEGX(void);
static void GenCLR(void);
static void GenNEG(void);
static void GenNOT(void);
static void GenMOVESRa(void);
static void GenMOVEaSR(void);
static void GenMOVEaCCR(void);
static void GenNBCD(void);
static void GenPEA(void);
static void GenSWAP(void);
static void GenMOVEMaR(void);
static void GenEXT(void);
static void GenTST(void);
static void GenTAS(void);
static void GenILLEGAL(void);
static void GenMOVEMRa(void);
static void GenTRAP(void);
static void GenLINK(void);
static void GenLINKA7(void);
static void GenULNK(void);
static void GenULNKA7(void);
static void GenMOVEAUSP(void);
static void GenMOVEUSPA(void);
static void GenRESET(void);
static void GenNOP(void);
static void GenSTOP(void);
static void GenRTE(void);
static void GenRTS(void);
static void GenTRAPV(void);
static void GenRTR(void);
static void GenJSR(void);
static void GenJMP(void);
static void GenCHK(void);
static void GenLEA(void);
static void GenSTCC(void);
static void GenDBCC(void);
static void GenADDQ(void);
static void GenSUBQ(void);
static void GenBCC(void);
static void GenBCC16(void);
static void GenBRA(void);
static void GenBRA16(void);
static void GenBSR(void);
static void GenBSR16(void);
static void GenMOVEQ(void);
static void GenORaD(void);
static void GenORDa(void);
static void GenSBCD(void);
static void GenSBCDM(void);
static void GenSBCD7M(void);
static void GenSBCDM7(void);
static void GenSBCD7M7(void);
static void GenDIVU(void);
static void GenDIVS(void);
static void GenSUBaD(void);
static void GenSUBDa(void);
static void GenSUBX(void);
static void GenSUBXM(void);
static void GenSUBX7M(void);
static void GenSUBXM7(void);
static void GenSUBX7M7(void);
static void GenSUBA(void);
static void GenCMP(void);
static void GenCMPM(void);
static void GenCMP7M(void);
static void GenCMPM7(void);
static void GenCMP7M7(void);
static void GenEORDa(void);
static void GenCMPA(void);
static void GenANDaD(void);
static void GenANDDa(void);
static void GenABCD(void);
static void GenABCDM(void);
static void GenABCD7M(void);
static void GenABCDM7(void);
static void GenABCD7M7(void);
static void GenMULU(void);
static void GenMULS(void);
static void GenEXGDD(void);
static void GenEXGAA(void);
static void GenEXGAD(void);
static void GenADDaD(void);
static void GenADDDa(void);
static void GenADDX(void);
static void GenADDXM(void);
static void GenADDX7M(void);
static void GenADDXM7(void);
static void GenADDX7M7(void);
static void GenADDA(void);
static void GenASRk(void);
static void GenLSRk(void);
static void GenROXRk(void);
static void GenRORk(void);
static void GenASLk(void);
static void GenLSLk(void);
static void GenROXLk(void);
static void GenROLk(void);
static void GenASRD(void);
static void GenLSRD(void);
static void GenROXRD(void);
static void GenRORD(void);
static void GenASLD(void);
static void GenLSLD(void);
static void GenROXLD(void);
static void GenROLD(void);
static void GenASR(void);
static void GenLSR(void);
static void GenROXR(void);
static void GenROR(void);
static void GenASL(void);
static void GenLSL(void);
static void GenROXL(void);
static void GenROL(void);
static void Gen1010(void);
static void Gen1111(void);
#ifdef NEOCD_HLE
static void Gen0xFABE(void);
static void Gen0xFABF(void);
static void Gen0xFAC0(void);
static void Gen0xFAC1(void);
static void Gen0xFAC2(void);
static void Gen0xFAC3(void);
#endif

#ifdef NEOCD_HLE
#define OP_INFO_TABLE_LEN   (142 + 6)
#else
#define OP_INFO_TABLE_LEN   144
#endif

static c68k_op_info_struc op_info_table[OP_INFO_TABLE_LEN] =
{   //                                                       DAAAAddaaddi    DAAAAddaaddi
    //                                                         iid181318m      iid181318m
    //                              siz siz eam ear eam ear    nne6A626Pm      nne6A626Pm
    //  opname      opbase  opmask  typ sft  1   1   2   2     dccAX  PX       dccAX  PX    GenFunc
    {   "1010",     0xA000, 0xF000, 0,  0,  -1, -1, -1, -1, "------------", "------------", Gen1010     },
    {   "1111",     0xF000, 0xF000, 0,  0,  -1, -1, -1, -1, "------------", "------------", Gen1111     },
    {   "ORI",      0x0000, 0xFF00, 2,  6,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenORI      },
    {   "ORICCR",   0x003C, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenORICCR   },
    {   "ORISR",    0x007C, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenORISR    },
    {   "ANDI",     0x0200, 0xFF00, 2,  6,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenANDI     },
    {   "ANDICCR",  0x023C, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenANDICCR  },
    {   "ANDISR",   0x027C, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenANDISR   },
    {   "EORI",     0x0A00, 0xFF00, 2,  6,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenEORI     },
    {   "EORICCR",  0x0A3C, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenEORICCR  },
    {   "EORISR",   0x0A7C, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenEORISR   },

    {   "SUBI",     0x0400, 0xFF00, 2,  6,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenSUBI     },
    {   "ADDI",     0x0600, 0xFF00, 2,  6,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenADDI     },
    {   "CMPI",     0x0C00, 0xFF00, 2,  6,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenCMPI     },

    {   "BTSTn",    0x0800, 0xFFC0, 0,  0,  3,  0,  -1, -1, "o-ooooooooo-", "------------", GenBTSTn    },
    {   "BCHGn",    0x0840, 0xFFC0, 0,  0,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenBCHGn    },
    {   "BCLRn",    0x0880, 0xFFC0, 0,  0,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenBCLRn    },
    {   "BSETn",    0x08C0, 0xFFC0, 0,  0,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenBSETn    },

    {   "BTST",     0x0100, 0xF1C0, 0,  0,  3,  0,  -1, 9,  "o-oooooooooo", "------------", GenBTST     },
    {   "BCHG",     0x0140, 0xF1C0, 0,  0,  3,  0,  -1, 9,  "o-ooooooo---", "------------", GenBCHG     },
    {   "BCLR",     0x0180, 0xF1C0, 0,  0,  3,  0,  -1, 9,  "o-ooooooo---", "------------", GenBCLR     },
    {   "BSET",     0x01C0, 0xF1C0, 0,  0,  3,  0,  -1, 9,  "o-ooooooo---", "------------", GenBSET     },

    {   "MOVEPWaD", 0x0108, 0xF1F8, 0,  0,  -1, 0,  -1, 9,  "------------", "------------", GenMOVEPWaD },
    {   "MOVEPLaD", 0x0148, 0xF1F8, 0,  0,  -1, 0,  -1, 9,  "------------", "------------", GenMOVEPLaD },
    {   "MOVEPWDa", 0x0188, 0xF1F8, 0,  0,  -1, 0,  -1, 9,  "------------", "------------", GenMOVEPWDa },
    {   "MOVEPLDa", 0x01C8, 0xF1F8, 0,  0,  -1, 0,  -1, 9,  "------------", "------------", GenMOVEPLDa },

    {   "MOVEB",    0x1000, 0xF000, 0,  0,  3,  0,  6,  9,  "oooooooooooo", "o-ooooooo---", GenMOVEB    },
    {   "MOVEL",    0x2000, 0xF000, 0,  0,  3,  0,  6,  9,  "oooooooooooo", "o-ooooooo---", GenMOVEL    },
    {   "MOVEW",    0x3000, 0xF000, 0,  0,  3,  0,  6,  9,  "oooooooooooo", "o-ooooooo---", GenMOVEW    },
    {   "MOVEAL",   0x2040, 0xF1C0, 0,  0,  3,  0,  -1, 9,  "oooooooooooo", "------------", GenMOVEAL   },
    {   "MOVEAW",   0x3040, 0xF1C0, 0,  0,  3,  0,  -1, 9,  "oooooooooooo", "------------", GenMOVEAW   },

    {   "NEGX",     0x4000, 0xFF00, 2,  6,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenNEGX     },
    {   "CLR",      0x4200, 0xFF00, 2,  6,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenCLR      },
    {   "NEG",      0x4400, 0xFF00, 2,  6,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenNEG      },
    {   "NOT",      0x4600, 0xFF00, 2,  6,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenNOT      },

    {   "MOVESRa",  0x40C0, 0xFFC0, 0,  0,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenMOVESRa  },
    {   "MOVEaCCR", 0x44C0, 0xFFC0, 0,  0,  3,  0,  -1, -1, "o-oooooooooo", "------------", GenMOVEaCCR },
    {   "MOVEaSR",  0x46C0, 0xFFC0, 0,  0,  3,  0,  -1, -1, "o-oooooooooo", "------------", GenMOVEaSR  },

    {   "NBCD",     0x4800, 0xFFC0, 0,  0,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenNBCD     },
    {   "PEA",      0x4840, 0xFFC0, 0,  0,  3,  0,  -1, -1, "--o--oooooo-", "------------", GenPEA      },
    {   "SWAP",     0x4840, 0xFFF8, 0,  0,  -1, 0,  -1, -1, "------------", "------------", GenSWAP     },

    {   "MOVEMRa",  0x4880, 0xFF80, 1,  6,  3,  0,  -1, -1, "--o-ooooo---", "------------", GenMOVEMRa  },
    {   "EXT",      0x4880, 0xFFB8, 1,  6,  -1, 0,  -1, -1, "------------", "------------", GenEXT      },
    {   "TST",      0x4A00, 0xFF00, 2,  6,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenTST      },
    {   "TAS",      0x4AC0, 0xFFC0, 0,  0,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenTAS      },
    {   "ILLEGAL",  0x4AFC, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenILLEGAL  },
    {   "MOVEMaR",  0x4C80, 0xFF80, 1,  6,  3,  0,  -1, -1, "--oo-oooooo-", "------------", GenMOVEMaR  },

    {   "TRAP",     0x4E40, 0xFFF0, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenTRAP     },
    {   "LINK",     0x4E50, 0xFFF8, 0,  0,  -1, 0,  -1, -1, "------------", "------------", GenLINK     },
    {   "LINKA7",   0x4E57, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenLINKA7   },
    {   "ULNK",     0x4E58, 0xFFF8, 0,  0,  -1, 0,  -1, -1, "------------", "------------", GenULNK     },
    {   "ULNKA7",   0x4E5F, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenULNKA7   },
    {   "MOVEAUSP", 0x4E60, 0xFFF8, 0,  0,  -1, 0,  -1, -1, "------------", "------------", GenMOVEAUSP },
    {   "MOVEUSPA", 0x4E68, 0xFFF8, 0,  0,  -1, 0,  -1, -1, "------------", "------------", GenMOVEUSPA },

    {   "RESET",    0x4E70, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenRESET    },
    {   "NOP",      0x4E71, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenNOP      },
    {   "STOP",     0x4E72, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenSTOP     },
    {   "RTE",      0x4E73, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenRTE      },
    {   "RTS",      0x4E75, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenRTS      },
    {   "TRAPV",    0x4E76, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenTRAPV    },
    {   "RTR",      0x4E77, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenRTR      },

    {   "JSR",      0x4E80, 0xFFC0, 0,  0,  3,  0,  -1, -1, "--o--oooooo-", "------------", GenJSR      },
    {   "JMP",      0x4EC0, 0xFFC0, 0,  0,  3,  0,  -1, -1, "--o--oooooo-", "------------", GenJMP      },

    {   "CHK",      0x4180, 0xF1C0, 0,  0,  3,  0,  -1, 9,  "o-oooooooooo", "------------", GenCHK      },
    {   "LEA",      0x41C0, 0xF1C0, 0,  0,  3,  0,  -1, 9,  "--o--oooooo-", "------------", GenLEA      },

    {   "STCC",     0x50C0, 0xF0C0, 0,  0,  3,  0,  -1, -1, "o-ooooooo---", "------------", GenSTCC     },
    {   "DBCC",     0x50C8, 0xF0F8, 0,  0,  -1, 0,  -1, -1, "------------", "------------", GenDBCC     },

    {   "ADDQ",     0x5000, 0xF100, 2,  6,  3,  0,  -1, -1, "ooooooooo---", "------------", GenADDQ     },
    {   "SUBQ",     0x5100, 0xF100, 2,  6,  3,  0,  -1, -1, "ooooooooo---", "------------", GenSUBQ     },

    {   "BCC",      0x6000, 0xF000, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenBCC      },
    {   "BCC16",    0x6000, 0xF0FF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenBCC16    },
    {   "BRA",      0x6000, 0xFF00, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenBRA      },
    {   "BRA16",    0x6000, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenBRA16    },
    {   "BSR",      0x6100, 0xFF00, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenBSR      },
    {   "BSR16",    0x6100, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenBSR16    },

    {   "MOVEQ",    0x7000, 0xF100, 0,  0,  -1, 9,  -1, -1, "------------", "------------", GenMOVEQ    },

    {   "ORaD",     0x8000, 0xF100, 2,  6,  3,  0,  -1, 9,  "o-oooooooooo", "------------", GenORaD     },
    {   "ORDa",     0x8100, 0xF100, 2,  6,  3,  0,  -1, 9,  "--ooooooo---", "------------", GenORDa     },
    {   "SBCD",     0x8100, 0xF1F8, 0,  0,  -1, 0,  -1, 9,  "------------", "------------", GenSBCD     },
    {   "SBCDM",    0x8108, 0xF1F8, 0,  0,  -1, 0,  -1, 9,  "------------", "------------", GenSBCDM    },
    {   "SBCD7M",   0x810F, 0xF1FF, 0,  0,  -1, 0,  -1, 9,  "------------", "------------", GenSBCD7M   },
    {   "SBCDM7",   0x8F08, 0xFFF8, 0,  0,  -1, 0,  -1, 9,  "------------", "------------", GenSBCDM7   },
    {   "SBCD7M7",  0x8F0F, 0xFFFF, 0,  0,  -1, 0,  -1, 9,  "------------", "------------", GenSBCD7M7  },
    {   "DIVU",     0x80C0, 0xF1C0, 0,  0,  3,  0,  -1, 9,  "o-oooooooooo", "------------", GenDIVU     },
    {   "DIVS",     0x81C0, 0xF1C0, 0,  0,  3,  0,  -1, 9,  "o-oooooooooo", "------------", GenDIVS     },

    {   "SUBaD",    0x9000, 0xF100, 2,  6,  3,  0,  -1, 9,  "oooooooooooo", "------------", GenSUBaD    },
    {   "SUBDa",    0x9100, 0xF100, 2,  6,  3,  0,  -1, 9,  "--ooooooo---", "------------", GenSUBDa    },
    {   "SUBX",     0x9100, 0xF138, 2,  6,  -1, 0,  -1, 9,  "------------", "------------", GenSUBX     },
    {   "SUBXM",    0x9108, 0xF138, 2,  6,  -1, 0,  -1, 9,  "------------", "------------", GenSUBXM    },
    {   "SUBX7M",   0x910F, 0xF13F, 2,  6,  -1, -1, -1, 9,  "------------", "------------", GenSUBX7M   },
    {   "SUBXM7",   0x9F08, 0xFF38, 2,  6,  -1, 0,  -1, -1, "------------", "------------", GenSUBXM7   },
    {   "SUBX7M7",  0x9F0F, 0xFF3F, 2,  6,  -1, -1, -1, -1, "------------", "------------", GenSUBX7M7  },
    {   "SUBA",     0x90C0, 0xF0C0, 1,  8,  3,  0,  -1, 9,  "oooooooooooo", "------------", GenSUBA     },

    {   "CMP",      0xB000, 0xF100, 2,  6,  3,  0,  -1, 9,  "oooooooooooo", "------------", GenCMP      },
    {   "CMPM",     0xB108, 0xF138, 2,  6,  -1, 0,  -1, 9,  "------------", "------------", GenCMPM     },
    {   "CMP7M",    0xB10F, 0xF13F, 2,  6,  -1, -1, -1, 9,  "------------", "------------", GenCMP7M    },
    {   "CMPM7",    0xBF08, 0xFF38, 2,  6,  -1, 0,  -1, -1, "------------", "------------", GenCMPM7    },
    {   "CMP7M7",   0xBF0F, 0xFF3F, 2,  6,  -1, -1, -1, -1, "------------", "------------", GenCMP7M7   },
    {   "EORDa",    0xB100, 0xF100, 2,  6,  3,  0,  -1, 9,  "o-ooooooo---", "------------", GenEORDa    },
    {   "CMPA",     0xB0C0, 0xF0C0, 1,  8,  3,  0,  -1, 9,  "oooooooooooo", "------------", GenCMPA     },

    {   "ANDaD",    0xC000, 0xF100, 2,  6,  3,  0,  -1, 9,  "o-oooooooooo", "------------", GenANDaD    },
    {   "ANDDa",    0xC100, 0xF100, 2,  6,  3,  0,  -1, 9,  "--ooooooo---", "------------", GenANDDa    },
    {   "ABCD",     0xC100, 0xF1F8, 0,  0,  -1, 0,  -1, 9,  "------------", "------------", GenABCD     },
    {   "ABCDM",    0xC108, 0xF1F8, 0,  0,  -1, 0,  -1, 9,  "------------", "------------", GenABCDM    },
    {   "ABCD7M",   0xC10F, 0xF1FF, 0,  0,  -1, -1, -1, 9,  "------------", "------------", GenABCD7M   },
    {   "ABCDM7",   0xCF08, 0xFFF8, 0,  0,  -1, 0,  -1, -1, "------------", "------------", GenABCDM7   },
    {   "ABCD7M7",  0xCF0F, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", GenABCD7M7  },
    {   "MULU",     0xC0C0, 0xF1C0, 0,  0,  3,  0,  -1, 9,  "o-oooooooooo", "------------", GenMULU     },
    {   "MULS",     0xC1C0, 0xF1C0, 0,  0,  3,  0,  -1, 9,  "o-oooooooooo", "------------", GenMULS     },
    {   "EXGDD",    0xC140, 0xF1F8, 0,  0,  -1, 0,  -1, 9,  "------------", "------------", GenEXGDD    },
    {   "EXGAA",    0xC148, 0xF1F8, 0,  0,  -1, 0,  -1, 9,  "------------", "------------", GenEXGAA    },
    {   "EXGAD",    0xC188, 0xF1F8, 0,  0,  -1, 0,  -1, 9,  "------------", "------------", GenEXGAD    },

    {   "ADDaD",    0xD000, 0xF100, 2,  6,  3,  0,  -1, 9,  "oooooooooooo", "------------", GenADDaD    },
    {   "ADDDa",    0xD100, 0xF100, 2,  6,  3,  0,  -1, 9,  "--ooooooo---", "------------", GenADDDa    },
    {   "ADDX",     0xD100, 0xF138, 2,  6,  -1, 0,  -1, 9,  "------------", "------------", GenADDX     },
    {   "ADDXM",    0xD108, 0xF138, 2,  6,  -1, 0,  -1, 9,  "------------", "------------", GenADDXM    },
    {   "ADDX7M",   0xD10F, 0xF13F, 2,  6,  -1, -1, -1, 9,  "------------", "------------", GenADDX7M   },
    {   "ADDXM7",   0xDF08, 0xFF38, 2,  6,  -1, 0,  -1, -1, "------------", "------------", GenADDXM7   },
    {   "ADDX7M7",  0xDF0F, 0xFF3F, 2,  6,  -1, -1, -1, -1, "------------", "------------", GenADDX7M7  },
    {   "ADDA",     0xD0C0, 0xF0C0, 1,  8,  3,  0,  -1, 9,  "oooooooooooo", "------------", GenADDA     },

    {   "ASRk",     0xE000, 0xF138, 2,  6,  -1, 0, -1, -1,  "o-----------", "------------", GenASRk     },
    {   "LSRk",     0xE008, 0xF138, 2,  6,  -1, 0, -1, -1,  "o-----------", "------------", GenLSRk     },
    {   "ROXRk",    0xE010, 0xF138, 2,  6,  -1, 0, -1, -1,  "o-----------", "------------", GenROXRk    },
    {   "RORk",     0xE018, 0xF138, 2,  6,  -1, 0, -1, -1,  "o-----------", "------------", GenRORk     },
    {   "ASLk",     0xE100, 0xF138, 2,  6,  -1, 0, -1, -1,  "o-----------", "------------", GenASLk     },
    {   "LSLk",     0xE108, 0xF138, 2,  6,  -1, 0, -1, -1,  "o-----------", "------------", GenLSLk     },
    {   "ROXLk",    0xE110, 0xF138, 2,  6,  -1, 0, -1, -1,  "o-----------", "------------", GenROXLk    },
    {   "ROLk",     0xE118, 0xF138, 2,  6,  -1, 0, -1, -1,  "o-----------", "------------", GenROLk     },

    {   "ASRD",     0xE020, 0xF138, 2,  6,  -1, 0,  -1, 9,  "o-----------", "o-----------", GenASRD     },
    {   "LSRD",     0xE028, 0xF138, 2,  6,  -1, 0,  -1, 9,  "o-----------", "o-----------", GenLSRD     },
    {   "ROXRD",    0xE030, 0xF138, 2,  6,  -1, 0,  -1, 9,  "o-----------", "o-----------", GenROXRD    },
    {   "RORD",     0xE038, 0xF138, 2,  6,  -1, 0,  -1, 9,  "o-----------", "o-----------", GenRORD     },
    {   "ASLD",     0xE120, 0xF138, 2,  6,  -1, 0,  -1, 9,  "o-----------", "o-----------", GenASLD     },
    {   "LSLD",     0xE128, 0xF138, 2,  6,  -1, 0,  -1, 9,  "o-----------", "o-----------", GenLSLD     },
    {   "ROXLD",    0xE130, 0xF138, 2,  6,  -1, 0,  -1, 9,  "o-----------", "o-----------", GenROXLD    },
    {   "ROLD",     0xE138, 0xF138, 2,  6,  -1, 0,  -1, 9,  "o-----------", "o-----------", GenROLD     },

    {   "ASR",      0xE0C0, 0xFFC0, 0,  0,  3,  0,  -1, -1, "--ooooooo---", "------------", GenASR      },
    {   "LSR",      0xE2C0, 0xFFC0, 0,  0,  3,  0,  -1, -1, "--ooooooo---", "------------", GenLSR      },
    {   "ROXR",     0xE4C0, 0xFFC0, 0,  0,  3,  0,  -1, -1, "--ooooooo---", "------------", GenROXR     },
    {   "ROR",      0xE6C0, 0xFFC0, 0,  0,  3,  0,  -1, -1, "--ooooooo---", "------------", GenROR      },
    {   "ASL",      0xE1C0, 0xFFC0, 0,  0,  3,  0,  -1, -1, "--ooooooo---", "------------", GenASL      },
    {   "LSL",      0xE3C0, 0xFFC0, 0,  0,  3,  0,  -1, -1, "--ooooooo---", "------------", GenLSL      },
    {   "ROXL",     0xE5C0, 0xFFC0, 0,  0,  3,  0,  -1, -1, "--ooooooo---", "------------", GenROXL     },
    {   "ROL",      0xE7C0, 0xFFC0, 0,  0,  3,  0,  -1, -1, "--ooooooo---", "------------", GenROL      }

#ifdef NEOCD_HLE
    ,
    {   "0xFABE",   0xFABE, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", Gen0xFABE   },
    {   "0xFABF",   0xFABF, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", Gen0xFABF   },
    {   "0xFAC0",   0xFAC0, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", Gen0xFAC0   },
    {   "0xFAC1",   0xFAC1, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", Gen0xFAC1   },
    {   "0xFAC2",   0xFAC2, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", Gen0xFAC2   },
    {   "0xFAC3",   0xFAC3, 0xFFFF, 0,  0,  -1, -1, -1, -1, "------------", "------------", Gen0xFAC3   }
#endif
};

#ifndef C68K_NO_JUMP_TABLE
#ifdef C68K_CONST_JUMP_TABLE
static u16 op_jump_table[0x10000];
#endif
#endif

// files where code is generated
static FILE* ini_file = NULL;
static FILE* opcode_file = NULL;

// current generated instruction infos
static c68k_op_info_struc *current_op;
static u32 current_ea;
static u32 current_eam;
static u32 current_reg;
static u32 current_ea2;
static u32 current_eam2;
static u32 current_reg2;
static u32 current_size;
static u32 current_cycle;
static u32 current_io_sav;

static char current_cond_char[128];

static char szc[20];
static char szcs[20];
static char szcf[20];
static char szcsf[20];

static u32  current_bits_mask;
static u8   current_sft_mask;

#define	EA_DREG         0
#define	EA_AREG         1
#define	EA_AIND         2
#define	EA_AINC         3
#define	EA_ADEC         4
#define	EA_D16A         5
#define	EA_D8AX         6
#define	EA_A16          7
#define	EA_A32          8
#define	EA_D16P         9
#define	EA_D8PX		    10
#define	EA_IMM		    11
#define	EA_AINC7        12
#define	EA_ADEC7	    13
#define	EA_ILLEGAL      15

static const u32 jmp_jsr_cycle_table[16] =
{
    0, 0,
    4,
    0,
    0,
    6,
    10,
    6,
    8,
    6,
    10,
    0, 0, 0, 0
};

static const u32 lea_pea_cycle_table[16] =
{
    0, 0,
    0,
    0,
    0,
    4,
    8,
    4,
    8,
    4,
    8,
    0, 0, 0, 0
};

static const u32 movem_cycle_table[16] =
{
    0, 0,
    0,
    0,
    0,
    4,
    6,
    4,
    8,
    4,
    6,
    0, 0, 0, 0
};

// general emitter function
////////////////////////////

static u32 prepare_generate(void)
{
    char filename[32];

    sprintf(filename, "c68k_op%.1X.inc", (current_op->op_base >> 12) & 0xF);
    if (opcode_file != NULL)
    {
        fclose(opcode_file);
        opcode_file = NULL;
    }
	opcode_file = fopen(filename, "at");
	if (opcode_file == NULL)
	{
	    printf("Can't open %s\n", filename);
	    return 1;
	}
	return 0;
}

static void wf_op(char* fmt, ...)
{
	va_list args;

    if (opcode_file == NULL) return;

	va_start(args, fmt);
	vfprintf(opcode_file, fmt, args);
	va_end(args);
}

static void gen_jumptable(u32 base, u32 start1, u32 end1, u32 step1, u32 start2, u32 end2, u32 step2, u32 start3, u32 end3, u32 step3, u32 op)
{
#ifdef C68K_CONST_JUMP_TABLE
    u32 i, j, k;
#endif
#ifdef C68K_NO_JUMP_TABLE
    u32 i, j, k;
#endif


    base &= 0xFFFF;
    start1 &= 0xFFFF;
    end1 &= 0xFFFF;
    step1 &= 0xFFFF;
    if (end1 < start1) end1 = start1;
    start2 &= 0xFFFF;
    end2 &= 0xFFFF;
    step2 &= 0xFFFF;
    if (end2 < start2) end2 = start2;
    op &= 0xFFFF;

#ifndef C68K_NO_JUMP_TABLE
#ifdef C68K_CONST_JUMP_TABLE
    if (step1 == 0) step1 = 1;
    if (step2 == 0) step2 = 1;
    if (step3 == 0) step3 = 1;

    for(i = start1; i <= end1; i += step1)
        for(j = start2; j <= end2; j += step2)
            for(k = start3; k <= end3; k += step3)
                op_jump_table[base + i + j + k] = op;
                
#else
    if (ini_file == NULL) return;

    if (start1 != end1)
    {
        fprintf(ini_file, "\t\tfor(i = 0x%.4X; i <= 0x%.4X; i += 0x%.4X)\n", (int)start1, (int)end1, (int)step1);
    	if (start2 != end2)
        {
            fprintf(ini_file, "\t\t\tfor(j = 0x%.4X; j <= 0x%.4X; j += 0x%.4X)\n\t", (int)start2, (int)end2, (int)step2);
        	if (start3 != end3)
            {
                fprintf(ini_file, "\t\t\t\tfor(k = 0x%.4X; k <= 0x%.4X; k += 0x%.4X)\n\t", (int)start3, (int)end3, (int)step3);
                fprintf(ini_file, "\t\t\t\t\tJumpTable[0x%.4X + i + j + k] = &&OP_0x%.4X;\n", (int)base, (int)op);
            }
            else fprintf(ini_file, "\t\t\t\tJumpTable[0x%.4X + i + j] = &&OP_0x%.4X;\n", (int)base, (int)op);
        }
        else fprintf(ini_file, "\t\t\tJumpTable[0x%.4X + i] = &&OP_0x%.4X;\n", (int)base, (int)op);
    }
    else fprintf(ini_file, "\t\tJumpTable[0x%.4X] = &&OP_0x%.4X;\n", (int)base, (int)op);
#endif
#else
    if (step1 == 0) step1 = 1;
    if (step2 == 0) step2 = 1;
    if (step3 == 0) step3 = 1;

    for(i = start1; i <= end1; i += step1)
        for(j = start2; j <= end2; j += step2)
            for(k = start3; k <= end3; k += step3)
            {
                u32 temp=(base + i + j + k);
                if (temp != op && temp != 0x4E57 && temp != 0x4E5F)
                   wf_op("case 0x%.4X:\n", base + i + j + k);
            }    
#endif
}

static void gen_opjumptable_ext(u32 base, u32 start3, u32 end3, u32 step3, u32 op)
{
    u32 start1, end1, step1, start2, end2, step2;

    start1 = end1 = step1 = 0;
    start2 = end2 = step2 = 0;

    if ((current_op->reg_sft != -1) && (current_ea < 7))
    {
        if ((current_ea == EA_AINC) || (current_ea == EA_ADEC)) end1 = 6 << current_op->reg_sft;
        else end1 = 7 << current_op->reg_sft;
        step1 = 1 << current_op->reg_sft;
    }
    if ((current_op->reg2_sft != -1) && (current_ea2 < 7))
    {
        if ((current_ea2 == EA_AINC) || (current_ea2 == EA_ADEC)) end2 = 6 << current_op->reg2_sft;
        else end2 = 7 << current_op->reg2_sft;
        step2 = 1 << current_op->reg2_sft;
    }

    if (start1 != end1)
    {
        if (start2 != end2) gen_jumptable(base, start1, end1, step1, start2, end2, step2, start3, end3, step3, op);
        else gen_jumptable(base, start1, end1, step1, start3, end3, step3, start2, end2, step2, op);
    }
    else if (start2 != end2) gen_jumptable(base, start2, end2, step2, start3, end3, step3, start1, end1, step1, op);
    else gen_jumptable(base, start3, end3, step3, start2, end2, step2, start1, end1, step1, op);
}

static void gen_opjumptable(u32 op)
{
    gen_opjumptable_ext(op, 0, 0, 0, op);
}

#define GEN_ADR 1
#define GEN_RES 2
#define GEN_SRC 4
#define GEN_DST 8
#define GEN_ALL 15

static void start_op(u32 op, int v)
{
    current_io_sav = 0;
    current_cycle = 0;

    wf_op("\n// %s\n", current_op->op_name);
#ifndef C68K_NO_JUMP_TABLE
    wf_op("OP_0x%.4X:\n", op & 0xFFFF);
#else
    wf_op("case 0x%.4X:\n", op & 0xFFFF);
#endif
    wf_op("{\n");
    if (v & GEN_ADR) wf_op("\tu32 adr;\n");
    if (v & GEN_RES) wf_op("\tu32 res;\n");
    if (v & GEN_DST) wf_op("\tpointer dst;\n");
    if (v & GEN_SRC) wf_op("\tpointer src;\n");
}

static void add_CCnt(u32 cycle)
{
    if (current_io_sav) wf_op("\tPOST_IO\n");
    current_io_sav = 0;
    wf_op("\tCCnt -= %d;\n", cycle);
}

static void adds_CCnt(char *str)
{
    if (current_io_sav) wf_op("\tPOST_IO\n");
    current_io_sav = 0;
    wf_op("\tCCnt -= %s;\n", str);
}

#if 0  // FIXME: warning removal
static void sub_CCnt(u32 cycle)
{
    if (current_io_sav) wf_op("\tPOST_IO\n");
    current_io_sav = 0;
    wf_op("\tCCnt += %d;\n", cycle);
}

static void subs_CCnt(char *str)
{
    if (current_io_sav) wf_op("\tPOST_IO\n");
    current_io_sav = 0;
    wf_op("\tCCnt += %s;\n", str);
}

static void quick_fterminate_op(u32 cycle)
{
    if (current_io_sav) wf_op("\tPOST_IO\n");
    current_io_sav = 0;
    wf_op("\tCCnt -= %d;\n", current_cycle + cycle);
    wf_op("\tgoto C68k_Exec_End;\n");
}
#endif

static void fterminate_op(u32 cycle)
{
    wf_op("}\n");
    if (current_io_sav) wf_op("POST_IO\n");
    current_io_sav = 0;
    wf_op("CCnt -= %d;\n", current_cycle + cycle);
    wf_op("goto C68k_Exec_End;\n");
}

static void quick_terminate_op(u32 cycle)
{
    if (current_io_sav) wf_op("\tPOST_IO\n");
    current_io_sav = 0;
    wf_op("\tRET(%d)\n", current_cycle + cycle);
}

static void terminate_op(u32 cycle)
{
    if (current_io_sav) wf_op("\tPOST_IO\n");
    wf_op("}\n");
    current_io_sav = 0;
    wf_op("RET(%d)\n", current_cycle + cycle);
}

static void do_pre_io(void)
{
    if (!current_io_sav) fprintf(opcode_file, "\tPRE_IO\n");
    current_io_sav = 1;
}

static void mem_op(char* fmt, ...)
{
	va_list args;

    if (opcode_file == NULL) return;

    do_pre_io();

	va_start(args, fmt);
	vfprintf(opcode_file, fmt, args);
	va_end(args);
}

// flag emitter function
/////////////////////////

static void set_logic_flag(void)
{
    wf_op("\tCPU->flag_C = 0;\n");
    wf_op("\tCPU->flag_V = 0;\n");
    wf_op("\tCPU->flag_notZ = res;\n");
    switch(current_size)
    {
        case SIZE_BYTE:
            wf_op("\tCPU->flag_N = res;\n");
        	break;

        case SIZE_WORD:
            wf_op("\tCPU->flag_N = res >> 8;\n");
        	break;

        case SIZE_LONG:
            wf_op("\tCPU->flag_N = res >> 24;\n");
        	break;
	}
}

static void set_logicl_flag(void)
{
    wf_op("\tCPU->flag_C = 0;\n");
    wf_op("\tCPU->flag_V = 0;\n");
    switch(current_size)
    {
        case SIZE_BYTE:
            wf_op("\tCPU->flag_N = res;\n");
            wf_op("\tCPU->flag_notZ = res & 0xFF;\n");
        	break;

        case SIZE_WORD:
            wf_op("\tCPU->flag_notZ = res & 0xFFFF;\n");
            wf_op("\tCPU->flag_N = res >> 8;\n");
        	break;

        case SIZE_LONG:
            wf_op("\tCPU->flag_notZ = res;\n");
            wf_op("\tCPU->flag_N = res >> 24;\n");
        	break;
	}
}

static void set_add_flag(void)
{
    switch(current_size)
    {
        case SIZE_BYTE:
        	wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n");
        	wf_op("\tCPU->flag_V = (src ^ res) & (dst ^ res);\n");
            wf_op("\tCPU->flag_notZ = res & 0xFF;\n");
        	break;

        case SIZE_WORD:
        	wf_op("\tCPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8;\n");
        	wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n");
            wf_op("\tCPU->flag_notZ = res & 0xFFFF;\n");
        	break;

        case SIZE_LONG:
            wf_op("\tCPU->flag_notZ = res;\n");
	        wf_op("\tCPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23;\n");
//            wf_op("\tCPU->flag_X = CPU->flag_C = ((src & dst) | (~res & (src | dst))) >> 23;\n");
        	wf_op("\tCPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24;\n");
            wf_op("\tCPU->flag_N = res >> 24;\n");
        	break;
	}
}

static void set_addx_flag(void)
{
    switch(current_size)
    {
        case SIZE_BYTE:
        	wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n");
        	wf_op("\tCPU->flag_V = (src ^ res) & (dst ^ res);\n");
            wf_op("\tCPU->flag_notZ |= res & 0xFF;\n");
        	break;

        case SIZE_WORD:
        	wf_op("\tCPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8;\n");
        	wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n");
            wf_op("\tCPU->flag_notZ |= res & 0xFFFF;\n");
        	break;

        case SIZE_LONG:
            wf_op("\tCPU->flag_notZ |= res;\n");
	        wf_op("\tCPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23;\n");
//            wf_op("\tCPU->flag_X = CPU->flag_C = ((src & dst) | (~res & (src | dst))) >> 23;\n");
        	wf_op("\tCPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24;\n");
            wf_op("\tCPU->flag_N = res >> 24;\n");
        	break;
	}
}

static void set_sub_flag(void)
{
    switch(current_size)
    {
        case SIZE_BYTE:
        	wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n");
        	wf_op("\tCPU->flag_V = (src ^ dst) & (res ^ dst);\n");
            wf_op("\tCPU->flag_notZ = res & 0xFF;\n");
        	break;

        case SIZE_WORD:
        	wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8;\n");
        	wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n");
            wf_op("\tCPU->flag_notZ = res & 0xFFFF;\n");
        	break;

        case SIZE_LONG:
            wf_op("\tCPU->flag_notZ = res;\n");
	        wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23;\n");
//            wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res) | (~dst & (src | res))) >> 23;\n");
        	wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24;\n");
            wf_op("\tCPU->flag_N = res >> 24;\n");
        	break;
	}
}

static void set_subx_flag(void)
{
    switch(current_size)
    {
        case SIZE_BYTE:
        	wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n");
        	wf_op("\tCPU->flag_V = (src ^ dst) & (res ^ dst);\n");
            wf_op("\tCPU->flag_notZ |= res & 0xFF;\n");
        	break;

        case SIZE_WORD:
        	wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8;\n");
        	wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n");
            wf_op("\tCPU->flag_notZ |= res & 0xFFFF;\n");
        	break;

        case SIZE_LONG:
            wf_op("\tCPU->flag_notZ |= res;\n");
	        wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23;\n");
//            wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res) | (~dst & (src | res))) >> 23;\n");
        	wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24;\n");
            wf_op("\tCPU->flag_N = res >> 24;\n");
        	break;
	}
}

static void set_cmp_flag(void)
{
    switch(current_size)
    {
        case SIZE_BYTE:
        	wf_op("\tCPU->flag_N = CPU->flag_C = res;\n");
        	wf_op("\tCPU->flag_V = (src ^ dst) & (res ^ dst);\n");
            wf_op("\tCPU->flag_notZ = res & 0xFF;\n");
        	break;

        case SIZE_WORD:
        	wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8;\n");
        	wf_op("\tCPU->flag_N = CPU->flag_C = res >> 8;\n");
            wf_op("\tCPU->flag_notZ = res & 0xFFFF;\n");
        	break;

        case SIZE_LONG:
            wf_op("\tCPU->flag_notZ = res;\n");
	        wf_op("\tCPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23;\n");
//            wf_op("\tCPU->flag_C = ((src & res) | (~dst & (src | res))) >> 23;\n");
        	wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24;\n");
            wf_op("\tCPU->flag_N = res >> 24;\n");
        	break;
	}
}

static void set_negx_flag(void)
{
    switch(current_size)
    {
        case SIZE_BYTE:
            wf_op("\tCPU->flag_V = res & src;\n");
            wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n");
            wf_op("\tCPU->flag_notZ |= res & 0xFF;\n");
        	break;

        case SIZE_WORD:
            wf_op("\tCPU->flag_V = (res & src) >> 8;\n");
            wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n");
            wf_op("\tCPU->flag_notZ |= res & 0xFFFF;\n");
        	break;

        case SIZE_LONG:
            wf_op("\tCPU->flag_notZ |= res;\n");
            wf_op("\tCPU->flag_V = (res & src) >> 24;\n");
	        wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23;\n");
//            wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res) | (~dst & (src | res))) >> 23;\n");
            wf_op("\tCPU->flag_N = res >> 24;\n");
        	break;
	}
}

static void set_neg_flag(void)
{
    switch(current_size)
    {
        case SIZE_BYTE:
            wf_op("\tCPU->flag_V = res & src;\n");
            wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n");
            wf_op("\tCPU->flag_notZ = res & 0xFF;\n");
        	break;

        case SIZE_WORD:
            wf_op("\tCPU->flag_V = (res & src) >> 8;\n");
            wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n");
            wf_op("\tCPU->flag_notZ = res & 0xFFFF;\n");
        	break;

        case SIZE_LONG:
            wf_op("\tCPU->flag_notZ = res;\n");
            wf_op("\tCPU->flag_V = (res & src) >> 24;\n");
	        wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23;\n");
//            wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res) | (~dst & (src | res))) >> 23;\n");
            wf_op("\tCPU->flag_N = res >> 24;\n");
        	break;
	}
}

static char* get_cond_as_cond(u32 cond, u32 notvar)
{
    if (notvar) cond ^= 1;

    switch(cond)
    {
        case COND_TR:
            sprintf(current_cond_char, "(1)");
            break;

        case COND_FA:
            sprintf(current_cond_char, "(0)");
            break;

        case COND_HI:
            sprintf(current_cond_char, "(CPU->flag_notZ && (!(CPU->flag_C & 0x100)))");
            break;

        case COND_LS:
            sprintf(current_cond_char, "((!CPU->flag_notZ) || (CPU->flag_C & 0x100))");
            break;

        case COND_CC:
            sprintf(current_cond_char, "(!(CPU->flag_C & 0x100))");
            break;

        case COND_CS:
            sprintf(current_cond_char, "(CPU->flag_C & 0x100)");
            break;

        case COND_NE:
            sprintf(current_cond_char, "(CPU->flag_notZ)");
            break;

        case COND_EQ:
            sprintf(current_cond_char, "(!CPU->flag_notZ)");
            break;

        case COND_VC:
            sprintf(current_cond_char, "(!(CPU->flag_V & 0x80))");
            break;

        case COND_VS:
            sprintf(current_cond_char, "(CPU->flag_V & 0x80)");
            break;

        case COND_PL:
            sprintf(current_cond_char, "(!(CPU->flag_N & 0x80))");
            break;

        case COND_MI:
            sprintf(current_cond_char, "(CPU->flag_N & 0x80)");
            break;

        case COND_GE:
            sprintf(current_cond_char, "(!((CPU->flag_N ^ CPU->flag_V) & 0x80))");
            break;

        case COND_LT:
            sprintf(current_cond_char, "((CPU->flag_N ^ CPU->flag_V) & 0x80)");
            break;

        case COND_GT:
            sprintf(current_cond_char, "(CPU->flag_notZ && (!((CPU->flag_N ^ CPU->flag_V) & 0x80)))");
            break;

        case COND_LE:
            sprintf(current_cond_char, "((!CPU->flag_notZ) || ((CPU->flag_N ^ CPU->flag_V) & 0x80))");
            break;
    }

    return current_cond_char;
}

// effective address related function
//////////////////////////////////////

static u32 has_ea(u32 ea)
{
    if (ea == EA_AINC7) return (current_op->ea_supported[EA_AINC] == 'o');
    if (ea == EA_ADEC7) return (current_op->ea_supported[EA_ADEC] == 'o');
    if (ea <= EA_IMM) return (current_op->ea_supported[ea] == 'o');
    return 0;
}

static u32 has_ea2(u32 ea)
{
    if (ea == EA_AINC7) return (current_op->ea2_supported[EA_AINC] == 'o');
    if (ea == EA_ADEC7) return (current_op->ea2_supported[EA_ADEC] == 'o');
    if (ea <= EA_IMM) return (current_op->ea2_supported[ea] == 'o');
    return 0;
}

#if 0  // FIXME: warning removal
static u32 _eamreg_to_ea(u32 eam, u32 reg)
{
    if ((eam > 7) || (reg > 7)) return EA_ILLEGAL;
	if ((eam == 3) && (reg == 7)) return EA_AINC7;
	if ((eam == 4) && (reg == 7)) return EA_ADEC7;
	if (eam != 7) return eam;
	if (reg < 5) return (eam + reg);
	return EA_ILLEGAL;
}
#endif

static u32 _ea_to_eamreg(u32 ea)
{
    if (ea < 7) return (ea << 3) | 0;
    if (ea == EA_AINC7) return (EA_AINC << 3) | 7;
    if (ea == EA_ADEC7) return (EA_ADEC << 3) | 7;
    if (ea <= EA_IMM) return (7 << 3) | (ea - 7);
	return (7 << 3) | 7;
}

static u32 is_ea_memory(u32 ea)
{
    if ((ea > EA_AREG) && (ea != EA_IMM)) return 1;
    else return 0;
}

static void _ea_calc_free(u32 ea, u32 rsft)
{
	u32 step;

    step = 0;
    switch (current_size)
    {
        case SIZE_BYTE:
            if ((ea == EA_AINC7) || (ea == EA_ADEC7)) step = 2;
            else step = 1;
            break;

        case SIZE_WORD:
            step = 2;
            break;

        case SIZE_LONG:
            step = 4;
            break;
    }

  	switch (ea)
	{
		case EA_DREG:
//            wf_op("\tadr = (u32)(&CPU->D[(Opcode >> %d) & 7]);\n", rsft);
			break;

		case EA_AREG:
//            wf_op("\tadr = (u32)(&CPU->A[(Opcode >> %d) & 7]);\n", rsft);
			break;

		case EA_AIND:
            wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft);
			break;

		case EA_AINC:
            wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft);
            wf_op("\tCPU->A[(Opcode >> %d) & 7] += %d;\n", rsft, step);
			break;

		case EA_AINC7:
            wf_op("\tadr = CPU->A[7];\n");
            wf_op("\tCPU->A[7] += %d;\n", step);
			break;

		case EA_ADEC:
            wf_op("\tadr = CPU->A[(Opcode >> %d) & 7] - %d;\n", rsft, step);
            wf_op("\tCPU->A[(Opcode >> %d) & 7] = adr;\n", rsft);
			break;

		case EA_ADEC7:
            wf_op("\tadr = CPU->A[7] - %d;\n", step);
            wf_op("\tCPU->A[7] = adr;\n");
			break;

		case EA_D16A:
            wf_op("\tadr = CPU->A[(Opcode >> %d) & 7] + (s32)(s16)FETCH_WORD;\n", rsft);
            wf_op("\tPC += 2;\n");
			break;

		case EA_D8AX:
            wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft);
            wf_op("\tDECODE_EXT_WORD\n");
			break;

		case EA_A16:
            wf_op("\tadr = (s32)(s16)FETCH_WORD;\n");
            wf_op("\tPC += 2;\n");
			break;

		case EA_A32:
            wf_op("\tadr = (s32)FETCH_LONG;\n");
            wf_op("\tPC += 4;\n");
			break;

		case EA_D16P:
            wf_op("\tadr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD;\n");
            wf_op("\tPC += 2;\n");
			break;

		case EA_D8PX:
            wf_op("\tadr = PC - CPU->BasePC;\n");
            wf_op("\tDECODE_EXT_WORD\n");
			break;
	}
}

static void _ea_calc(u32 ea, u32 rsft)
{
	u32 step;
	u32 cycle_sft;

    step = 0;
  	cycle_sft = 0;
    switch (current_size)
    {
        case SIZE_BYTE:
            if ((ea == EA_AINC7) || (ea == EA_ADEC7)) step = 2;
            else step = 1;
            break;

        case SIZE_WORD:
            step = 2;
            break;

        case SIZE_LONG:
        	cycle_sft = 1;
            step = 4;
            break;
    }

  	switch (ea)
	{
		case EA_DREG:
//            wf_op("\tadr = (u32)(&CPU->D[(Opcode >> %d) & 7]);\n", rsft);
			break;

		case EA_AREG:
//            wf_op("\tadr = (u32)(&CPU->A[(Opcode >> %d) & 7]);\n", rsft);
			break;

		case EA_IMM:
            current_cycle += (4 << cycle_sft) + 0;
            break;

		case EA_AIND:
            current_cycle += (4 << cycle_sft) + 0;
            wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft);
			break;

		case EA_AINC:
            current_cycle += (4 << cycle_sft) + 0;
            wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft);
            wf_op("\tCPU->A[(Opcode >> %d) & 7] += %d;\n", rsft, step);
			break;

		case EA_AINC7:
            current_cycle += (4 << cycle_sft) + 0;
            wf_op("\tadr = CPU->A[7];\n");
            wf_op("\tCPU->A[7] += %d;\n", step);
			break;

		case EA_ADEC:
            current_cycle += (4 << cycle_sft) + 2;
            wf_op("\tadr = CPU->A[(Opcode >> %d) & 7] - %d;\n", rsft, step);
            wf_op("\tCPU->A[(Opcode >> %d) & 7] = adr;\n", rsft);
			break;

		case EA_ADEC7:
            current_cycle += (4 << cycle_sft) + 2;
            wf_op("\tadr = CPU->A[7] - %d;\n", step);
            wf_op("\tCPU->A[7] = adr;\n");
			break;

		case EA_D16A:
            current_cycle += (4 << cycle_sft) + 4;
            wf_op("\tadr = CPU->A[(Opcode >> %d) & 7] + (s32)(s16)FETCH_WORD;\n", rsft);
            wf_op("\tPC += 2;\n");
			break;

		case EA_D8AX:
            current_cycle += (4 << cycle_sft) + 6;
            wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft);
            wf_op("\tDECODE_EXT_WORD\n");
			break;

		case EA_A16:
            current_cycle += (4 << cycle_sft) + 4;
            wf_op("\tadr = (s32)(s16)FETCH_WORD;\n");
            wf_op("\tPC += 2;\n");
			break;

		case EA_A32:
            current_cycle += (4 << cycle_sft) + 8;
            wf_op("\tadr = (s32)FETCH_LONG;\n");
            wf_op("\tPC += 4;\n");
			break;

		case EA_D16P:
            current_cycle += (4 << cycle_sft) + 4;
            wf_op("\tadr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD;\n");
            wf_op("\tPC += 2;\n");
			break;

		case EA_D8PX:
            current_cycle += (4 << cycle_sft) + 6;
            wf_op("\tadr = PC - CPU->BasePC;\n");
            wf_op("\tDECODE_EXT_WORD\n");
			break;
	}
}

static void _ea_read_(u32 ea, u32 rsft, char dest[4])
{
	char sz[8];

    switch (current_size)
    {
        case SIZE_BYTE:
            strcpy(sz, "BYTE");
            break;

        case SIZE_WORD:
            strcpy(sz, "WORD");
            break;

        case SIZE_LONG:
            strcpy(sz, "LONG");
            break;
    }

	switch (ea)
	{
		case EA_DREG:
            wf_op("\t%s = (%s)CPU->D[(Opcode >> %d) & 7];\n", dest, szc, rsft);
			break;

		case EA_AREG:
            if (current_size == SIZE_BYTE)
            {
                wf_op("\t// can't read byte from Ax registers !\n");
                wf_op("\tCPU->Status |= C68K_FAULTED;\n");
                wf_op("\tCCnt = 0;\n");
                wf_op("\tgoto C68k_Exec_Really_End;\n");
            }
            else wf_op("\t%s = (%s)CPU->A[(Opcode >> %d) & 7];\n", dest, szc, rsft);
			break;
/*
		case EA_DREG:
		case EA_AREG:
            wf_op("\t%s = %s(adr);\n", dest, szcf);
            break;
*/
		case EA_A32:
		case EA_D8AX:
		case EA_D8PX:
		case EA_D16A:
		case EA_D16P:
		case EA_A16:
		case EA_ADEC:
		case EA_ADEC7:
		case EA_AIND:
		case EA_AINC:
		case EA_AINC7:
            mem_op("\tREAD_%s_F(adr, %s)\n", sz, dest);
			break;

		case EA_IMM:
            switch (current_size)
            {
                case SIZE_BYTE:
                    wf_op("\t%s = FETCH_BYTE;\n", dest);
                    wf_op("\tPC += 2;\n");
                    break;

                case SIZE_WORD:
                    wf_op("\t%s = FETCH_WORD;\n", dest);
                    wf_op("\tPC += 2;\n");
                    break;

                case SIZE_LONG:
                    wf_op("\t%s = FETCH_LONG;\n", dest);
                    wf_op("\tPC += 4;\n");
                    break;
            }
			break;
	}
}

static void _ea_read(u32 ea, u32 rsft)
{
    _ea_read_(ea, rsft, "res");
}

static void _ea_read_src(u32 ea, u32 rsft)
{
    _ea_read_(ea, rsft, "src");
}

static void _ea_read_dst(u32 ea, u32 rsft)
{
    _ea_read_(ea, rsft, "dst");
}

static void _ea_read_sx_(u32 ea, u32 rsft, char dest[4])
{
	char sz[8];

    switch (current_size)
    {
        case SIZE_BYTE:
            strcpy(sz, "BYTE");
            break;

        case SIZE_WORD:
            strcpy(sz, "WORD");
            break;

        case SIZE_LONG:
            strcpy(sz, "LONG");
            break;
    }

	switch (ea)
	{
		case EA_DREG:
            wf_op("\t%s = (s32)(%s)CPU->D[(Opcode >> %d) & 7];\n", dest, szcs, rsft);
			break;

		case EA_AREG:
            if (current_size == SIZE_BYTE)
            {
                wf_op("\t// can't read byte from Ax registers !\n");
                wf_op("\tCPU->Status |= C68K_FAULTED;\n");
                wf_op("\tCCnt = 0;\n");
                wf_op("\tgoto C68k_Exec_Really_End;\n");
            }
            else wf_op("\t%s = (s32)(%s)CPU->A[(Opcode >> %d) & 7];\n", dest, szcs, rsft);
			break;
/*
		case EA_DREG:
		case EA_AREG:
            wf_op("\t%s = (s32)(%s(adr));\n", dest, szcsf);
			break;
*/
		case EA_A32:
		case EA_D8AX:
		case EA_D8PX:
		case EA_D16A:
		case EA_D16P:
		case EA_A16:
		case EA_ADEC:
		case EA_ADEC7:
		case EA_AIND:
		case EA_AINC:
		case EA_AINC7:
            mem_op("\tREADSX_%s_F(adr, %s)\n", sz, dest);
			break;

		case EA_IMM:
            switch (current_size)
            {
                case SIZE_BYTE:
                    wf_op("\t%s = (s32)(%s(PC)));\n", dest, szcsf);
                    wf_op("\tPC += 2;\n");
                    break;

                case SIZE_WORD:
                    wf_op("\t%s = (s32)(%s)FETCH_WORD;\n", dest, szcs);
                    wf_op("\tPC += 2;\n");
                    break;

                case SIZE_LONG:
                    wf_op("\t%s = (s32)(%s)FETCH_LONG;\n", dest, szcs);
                    wf_op("\tPC += 4;\n");
                    break;
            }
			break;
	}
}

static void _ea_read_sx(u32 ea, u32 rsft)
{
    _ea_read_sx_(ea, rsft, "res");
}

static void _ea_read_src_sx(u32 ea, u32 rsft)
{
    _ea_read_sx_(ea, rsft, "src");
}

static void _ea_write(u32 ea, u32 rsft)
{
	char sz[8];

    switch (current_size)
    {
        case SIZE_BYTE:
            strcpy(sz, "BYTE");
            break;

        case SIZE_WORD:
            strcpy(sz, "WORD");
            break;

        case SIZE_LONG:
            strcpy(sz, "LONG");
            break;
    }

	switch (ea)
	{
		case EA_DREG:
            wf_op("\t%s(&CPU->D[(Opcode >> %d) & 7])) = res;\n", szcf, 
rsft);
			break;

		case EA_AREG:
            // writes in Ax registers are always 32 bits sized
            wf_op("\tCPU->A[(Opcode >> %d) & 7] = res;\n", rsft);
			break;
/*
		case EA_DREG:
		case EA_AREG:
            wf_op("\t%s(adr) = res;\n", szcf);
			break;
*/
		case EA_A32:
		case EA_D8AX:
		case EA_D8PX:
		case EA_D16A:
		case EA_D16P:
		case EA_A16:
		case EA_ADEC:
		case EA_ADEC7:
		case EA_AIND:
		case EA_AINC:
		case EA_AINC7:
            mem_op("\tWRITE_%s_F(adr, res)\n", sz);
			break;
	}
}

// misc function
/////////////////

static u32 get_current_opcode_base(void)
{
    u32 base;

    base = current_op->op_base;
    if (current_op->eam_sft != -1) base += (current_eam & 7) << current_op->eam_sft;
    if (current_op->reg_sft != -1) base += (current_reg & 7) << current_op->reg_sft;
    if (current_op->eam2_sft != -1) base += (current_eam2 & 7) << current_op->eam2_sft;
    if (current_op->reg2_sft != -1) base += (current_reg2 & 7) << current_op->reg2_sft;
    if (current_op->size_type == 1) base += (current_size - 1) << current_op->size_sft;
    else if (current_op->size_type == 2) base += (current_size & 3) << current_op->size_sft;

    return base;
}

static void start_all(int v)
{
    u32 base;

    base = get_current_opcode_base();

    // generate jump table
    gen_opjumptable(base);

    // generate label & declarations
    start_op(base, v);
}

static void set_current_size(u32 sz)
{
    current_size = sz;
    switch(current_size)
    {
        case SIZE_BYTE:
            current_bits_mask = 0xFF;
            current_sft_mask = 7;
            strcpy(szc, "u8");
            strcpy(szcf, "*(BYTE_OFF + (u8*)");
            strcpy(szcs, "s8");
            strcpy(szcsf, "*(BYTE_OFF + (s8*)");
            break;

        case SIZE_WORD:
            current_bits_mask = 0xFFFF;
            current_sft_mask = 15;
            strcpy(szc, "u16");
            strcpy(szcf, "*(WORD_OFF + (u16*)");
            strcpy(szcs, "s16");
            strcpy(szcsf, "*(WORD_OFF + (s16*)");
            break;

        case SIZE_LONG:
            current_bits_mask = 0xFFFFFFFF;
            current_sft_mask = 31;
            strcpy(szc, "u32");
            strcpy(szcf, "*((u32*)");
            strcpy(szcs, "s32");
            strcpy(szcsf, "*((s32*)");
            break;
    }
}

// gen privilege exception (happen when S flag is not set)
static void gen_privilege_exception(char *pre)
{
    // swap A7 and USP (because S not set)
    wf_op("%sres = CPU->USP;\n", pre);
    wf_op("%sCPU->USP = CPU->A[7];\n", pre);
    wf_op("%sCPU->A[7] = res;\n", pre);

    // get vector & add cycle
    wf_op("%sres = C68K_PRIVILEGE_VIOLATION_EX;\n", pre);
    adds_CCnt("c68k_exception_cycle_table[res]");

    // we will do some mem/io access
    do_pre_io();

    // push PC and SR
    mem_op("%sPUSH_32_F(PC - CPU->BasePC)\n", pre);
    mem_op("%sPUSH_16_F(GET_SR)\n", pre);

    // adjust SR
    wf_op("%sCPU->flag_S = C68K_SR_S;\n", pre);

    // fetch new PC
    mem_op("%sREAD_LONG_F(res * 4, PC)\n", pre);
    wf_op("%sSET_PC(PC)\n", pre);
}

static void gen_exception(char *pre, char* exception)
{
    // swap A7 and USP if needed
    wf_op("%sif (!CPU->flag_S)\n", pre);
    wf_op("%s{\n", pre);
        wf_op("%s\tres = CPU->USP;\n", pre);
        wf_op("%s\tCPU->USP = CPU->A[7];\n", pre);
        wf_op("%s\tCPU->A[7] = res;\n", pre);
    wf_op("%s}\n", pre);

    // get vector & add cycle
    wf_op("%sres = %s;\n", pre, exception);
    adds_CCnt("c68k_exception_cycle_table[res]");

    // we will do some mem/io access
    do_pre_io();

    // push PC and SR
    mem_op("%sPUSH_32_F(PC - CPU->BasePC)\n", pre);
    mem_op("%sPUSH_16_F(GET_SR)\n", pre);

    // adjust SR
    wf_op("%sCPU->flag_S = C68K_SR_S;\n", pre);

    // fetch new PC
    mem_op("%sREAD_LONG_F(res * 4, PC)\n", pre);
    wf_op("%sSET_PC(PC)\n", pre);
}

