/*****************************************************************************
 *
 *   z8001.c
 *   Portable Z8001 emulator
 *   Z8001 MAME interface
 *   Copyright Matt Knoth 2008 (p8000@knothusa.net)
 *
 *   based on Juergen Buchmueller's Z8002 MAME model
 *
 *   Z8002: Copyright Juergen Buchmueller, all rights reserved.
 *   You can contact me at juergen@mame.net or pullmoll@stop1984.com
 *
 *   - This source code is released as freeware for non-commercial purposes
 *     as part of the M.A.M.E. (Multiple Arcade Machine Emulator) project.
 *     The licensing terms of MAME apply to this piece of code for the MAME
 *     project and derviative works, as defined by the MAME license. You
 *     may opt to make modifications, improvements or derivative works under
 *     that same conditions, and the MAME project may opt to keep
 *     modifications, improvements or derivatives under their terms exclusively.
 *
 *   - Alternatively you can choose to apply the terms of the "GPL" (see
 *     below) to this - and only this - piece of code or your derivative works.
 *     Note that in no case your choice can have any impact on any other
 *     source code of the MAME project, or binary, or executable, be it closely
 *     or losely related to this piece of code.
 *
 *  -  At your choice you are also free to remove either licensing terms from
 *     this file and continue to use it under only one of the two licenses. Do this
 *     if you think that licenses are not compatible (enough) for you, or if you
 *     consider either license 'too restrictive' or 'too free'.
 *
 *  -  GPL (GNU General Public License)
 *     This program is free software; you can redistribute it and/or
 *     modify it under the terms of the GNU General Public License
 *     as published by the Free Software Foundation; either version 2
 *     of the License, or (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *   Changes for P8000EMU [Matt Knoth]
 *   - Added z8001_debug_cpu_readop function to fix debugger crash. The debugger
 *     does not consider the 'MMU lite' banking style and fetches straight ahead
 *     This caused crashes due to address space overrun. By defining a cpu_readop
 *     the debugger uses the same access methodology as the CPU itself. This is
 *     slower but save.
 *   - byte io accessors had to be changed to their 16be equivalents due to
 *     return type conflict crashed in -O3 compilation
 *
 *
 *****************************************************************************/

#include "debugger.h"
#include "deprecat.h"
#include "z8001.h"
#include "z8001cpu.h"
#include "osd_cpu.h"
#include "../../debug/debugcpu.h"


#define VERBOSE 0

#define LOG(x)	do { if (VERBOSE) logerror x; } while (0)

/* opcode execution table */
Z8001_exec *z8001_exec = NULL;
Z8001_exec *z8001_exec_seg = NULL;

#ifdef Z8001_CPU_INST_COVERAGE
unsigned int *z8001_exec_cov = NULL;
unsigned int *z8001_exec_seg_cov = NULL;
#endif

typedef union {
    UINT8   B[16]; /* RL0,RH0,RL1,RH1...RL7,RH7 */
    UINT16  W[16]; /* R0,R1,R2...R15 */
    UINT32  L[8];  /* RR0,RR2,RR4..RR14 */
    UINT64  Q[4];  /* RQ0,RQ4,..RQ12 */
}   z8001_reg_file;

typedef struct {
    UINT16  op[4];      /* opcodes/data of current instruction */
    UINT32  ppc;	/* previous program counter */
    UINT32  pc;         /* program counter */
    UINT32  psap;       /* program status pointer */
    UINT16  fcw;        /* flags and control word */
    UINT16  refresh;    /* refresh timer/counter */
    UINT16  r14n;       /* user mode R14 shadow  */
    UINT16  r14s;       /* system mode R14 shadow  */
    UINT16  r15n;       /* user mode R15 shadow  */
    UINT16  r15s;       /* system mode R15 shadow  */
    UINT16  irq_req;    /* CPU is halted, interrupt or trap request */
    int  irq_state[4];	/* IRQ line states (NVI, VI, SEGTRAP, NMI) */
    z8001_reg_file regs;/* registers */
    int seg_state;      /* cpu in segmented mode */
    int (*irq_callback)(int irqline);
}   z8001_Regs;

static int z8001_ICount;

/* current CPU context */
static z8001_Regs Z;

/* zero, sign and parity flags for logical byte operations */
static UINT8 z8001_zsp[256];

/**************************************************************************
 * This is the register file layout:
 *
 * BYTE        WORD         LONG           QUAD
 * msb   lsb       bits           bits           bits
 * RH0 - RL0   R 0 15- 0    RR 0  31-16    RQ 0  63-48
 * RH1 - RL1   R 1 15- 0          15- 0          47-32
 * RH2 - RL2   R 2 15- 0    RR 2  31-16          31-16
 * RH3 - RL3   R 3 15- 0          15- 0          15- 0
 * RH4 - RL4   R 4 15- 0    RR 4  31-16    RQ 4  63-48
 * RH5 - RL5   R 5 15- 0          15- 0          47-32
 * RH6 - RL6   R 6 15- 0    RR 6  31-16          31-16
 * RH7 - RL7   R 7 15- 0          15- 0          15- 0
 *             R 8 15- 0    RR 8  31-16    RQ 8  63-48
 *             R 9 15- 0          15- 0          47-32
 *             R10 15- 0    RR10  31-16          31-16
 *             R11 15- 0          15- 0          15- 0
 *             R12 15- 0    RR12  31-16    RQ12  63-48
 *             R13 15- 0          15- 0          47-32
 *             R14 15- 0    RR14  31-16          31-16
 *             R15 15- 0          15- 0          15- 0
 *
 * Note that for LSB_FIRST machines we have the case that the RR registers
 * use the lower numbered R registers in the higher bit positions.
 * And also the RQ registers use the lower numbered RR registers in the
 * higher bit positions.
 * That's the reason for the ordering in the following pointer table.
 **************************************************************************/
#ifdef	LSB_FIRST
	/* pointers to byte (8bit) registers */
	static UINT8	*const pRB[16] =
	{
		&Z.regs.B[ 7],&Z.regs.B[ 5],&Z.regs.B[ 3],&Z.regs.B[ 1],
		&Z.regs.B[15],&Z.regs.B[13],&Z.regs.B[11],&Z.regs.B[ 9],
		&Z.regs.B[ 6],&Z.regs.B[ 4],&Z.regs.B[ 2],&Z.regs.B[ 0],
		&Z.regs.B[14],&Z.regs.B[12],&Z.regs.B[10],&Z.regs.B[ 8]
	};

	static UINT16	*const pRW[16] =
	{
        &Z.regs.W[ 3],&Z.regs.W[ 2],&Z.regs.W[ 1],&Z.regs.W[ 0],
        &Z.regs.W[ 7],&Z.regs.W[ 6],&Z.regs.W[ 5],&Z.regs.W[ 4],
        &Z.regs.W[11],&Z.regs.W[10],&Z.regs.W[ 9],&Z.regs.W[ 8],
        &Z.regs.W[15],&Z.regs.W[14],&Z.regs.W[13],&Z.regs.W[12]
    };

    /* pointers to long (32bit) registers */
	static UINT32	*const pRL[16] =
	{
		&Z.regs.L[ 1],&Z.regs.L[ 1],&Z.regs.L[ 0],&Z.regs.L[ 0],
		&Z.regs.L[ 3],&Z.regs.L[ 3],&Z.regs.L[ 2],&Z.regs.L[ 2],
		&Z.regs.L[ 5],&Z.regs.L[ 5],&Z.regs.L[ 4],&Z.regs.L[ 4],
		&Z.regs.L[ 7],&Z.regs.L[ 7],&Z.regs.L[ 6],&Z.regs.L[ 6]
    };

#else	/* MSB_FIRST */

    /* pointers to byte (8bit) registers */
	static UINT8	*const pRB[16] =
	{
		&Z.regs.B[ 0],&Z.regs.B[ 2],&Z.regs.B[ 4],&Z.regs.B[ 6],
		&Z.regs.B[ 8],&Z.regs.B[10],&Z.regs.B[12],&Z.regs.B[14],
		&Z.regs.B[ 1],&Z.regs.B[ 3],&Z.regs.B[ 5],&Z.regs.B[ 7],
		&Z.regs.B[ 9],&Z.regs.B[11],&Z.regs.B[13],&Z.regs.B[15]
	};

	/* pointers to word (16bit) registers */
	static UINT16	*const pRW[16] =
	{
		&Z.regs.W[ 0],&Z.regs.W[ 1],&Z.regs.W[ 2],&Z.regs.W[ 3],
		&Z.regs.W[ 4],&Z.regs.W[ 5],&Z.regs.W[ 6],&Z.regs.W[ 7],
		&Z.regs.W[ 8],&Z.regs.W[ 9],&Z.regs.W[10],&Z.regs.W[11],
		&Z.regs.W[12],&Z.regs.W[13],&Z.regs.W[14],&Z.regs.W[15]
	};

	/* pointers to long (32bit) registers */
	static UINT32	*const pRL[16] =
	{
		&Z.regs.L[ 0],&Z.regs.L[ 0],&Z.regs.L[ 1],&Z.regs.L[ 1],
		&Z.regs.L[ 2],&Z.regs.L[ 2],&Z.regs.L[ 3],&Z.regs.L[ 3],
		&Z.regs.L[ 4],&Z.regs.L[ 4],&Z.regs.L[ 5],&Z.regs.L[ 5],
		&Z.regs.L[ 6],&Z.regs.L[ 6],&Z.regs.L[ 7],&Z.regs.L[ 7]
	};

#endif

/* pointers to quad word (64bit) registers */
static UINT64   *const pRQ[16] = {
    &Z.regs.Q[ 0],&Z.regs.Q[ 0],&Z.regs.Q[ 0],&Z.regs.Q[ 0],
    &Z.regs.Q[ 1],&Z.regs.Q[ 1],&Z.regs.Q[ 1],&Z.regs.Q[ 1],
    &Z.regs.Q[ 2],&Z.regs.Q[ 2],&Z.regs.Q[ 2],&Z.regs.Q[ 2],
    &Z.regs.Q[ 3],&Z.regs.Q[ 3],&Z.regs.Q[ 3],&Z.regs.Q[ 3]};

///////////////////////////////////////////////////////////////////////////// Native Translation - none
static int z8001_translate(int space, offs_t *address)
{
       return 1;  // mapped response, access ok
}

// central translation hook, accessible via info->CPUINFO_PTR_TRANSLATE
// FIXME: should be part of the CPU context - therefore part of the Z structure for multi CPU systems
static cpufunc_translate pTranslate = z8001_translate;

///////////////////////////////////////////////////////////////////////////// Native Accessors - Fetch
UINT16 RDOP_z8001(UINT32 seg_addr)
{
    if(!(*pTranslate)(ADDRESS_SPACE_PROGRAM,&seg_addr)) {
         return 0;
    }
    return cpu_readop16(seg_addr);
}

///////////////////////////////////////////////////////////////////////////// Native Accessors - non-segmented Data
UINT8 RDMEM_B_z8001(UINT32 seg_addr)
{
    if(!(*pTranslate)(ADDRESS_SPACE_DATA,&seg_addr)) {
         return 0;
    }
    return program_read_byte_16be(seg_addr);
}

UINT16 RDMEM_W_z8001(UINT32 seg_addr)
{
    if(!(*pTranslate)(ADDRESS_SPACE_DATA,&seg_addr)) {
         return 0;
    }
    return program_read_word_16be(seg_addr);
}

UINT32 RDMEM_L_z8001(UINT32 seg_addr)
{
    UINT32 result;
    if(!(*pTranslate)(ADDRESS_SPACE_DATA,&seg_addr)) {
         return 0;
    }
    result = program_read_word_16be(seg_addr) << 16;
    return result + program_read_word_16be(seg_addr + 2);
}

void WRMEM_B_z8001(UINT32 seg_addr, UINT8 value)
{
    if(!(*pTranslate)(ADDRESS_SPACE_DATA | 2,&seg_addr)) {
         return;
    }
    program_write_byte_16be(seg_addr, value);
}

void WRMEM_W_z8001(UINT32 seg_addr, UINT16 value)
{
    if(!(*pTranslate)(ADDRESS_SPACE_DATA | 2,&seg_addr)) {
         return;
    }
    program_write_word_16be(seg_addr, value);
}

void WRMEM_L_z8001(UINT32 seg_addr, UINT32 value)
{
    if(!(*pTranslate)(ADDRESS_SPACE_DATA | 2,&seg_addr)) {
         return;
    }
    program_write_word_16be(seg_addr, value >> 16);
    program_write_word_16be(seg_addr + 2, value & 0xffff);
}

///////////////////////////////////////////////////////////////////////////// Native Accessors - segmented Data
UINT8 RDMEMSEG_B_z8001(UINT32 seg_addr)
{
    if(!(*pTranslate)(ADDRESS_SPACE_DATA,&seg_addr)) {
         return 0;
    }
    return program_read_byte_16be(seg_addr);
}

UINT16 RDMEMSEG_W_z8001(UINT32 seg_addr)
{
    if(!(*pTranslate)(ADDRESS_SPACE_DATA,&seg_addr)) {
         return 0;
    }
    return program_read_word_16be(seg_addr);
}

UINT32 RDMEMSEG_L_z8001(UINT32 seg_addr)
{
    UINT32 result;
    if(!(*pTranslate)(ADDRESS_SPACE_DATA,&seg_addr)) {
         return 0;
    }
    result = program_read_word_16be(seg_addr) << 16;
    return result + program_read_word_16be(seg_addr+2);
}

void WRMEMSEG_B_z8001(UINT32 seg_addr, UINT8 value)
{
    if(!(*pTranslate)(ADDRESS_SPACE_DATA | 2,&seg_addr)) {
         return;
    }
    program_write_byte_16be(seg_addr, value);
}

void WRMEMSEG_W_z8001(UINT32 seg_addr, UINT16 value)
{
    if(!(*pTranslate)(ADDRESS_SPACE_DATA | 2,&seg_addr)) {
         return;
    }
    program_write_word_16be(seg_addr, value);
}

void WRMEMSEG_L_z8001(UINT32 seg_addr, UINT32 value)
{
    if(!(*pTranslate)(ADDRESS_SPACE_DATA | 2,&seg_addr)) {
         return;
    }
    program_write_word_16be(seg_addr,   value >> 16);
    program_write_word_16be(seg_addr+2, value & 0xffff);
}

INLINE UINT8 RDPORT_B(int mode, UINT16 addr)
{
	if( mode == 0 )
	{
//TYPEERROR!    return io_read_byte_8( 0x0000ffff & addr);
       		return io_read_byte_16be( 0x0000ffff & addr);
	}
	else
	{
		/* special reads */
//TYPEERROR!    return io_read_byte_8( 0x00010000 | addr);
                return io_read_byte_16be( 0x00010000 | addr);
	}
}

INLINE UINT16 RDPORT_W(int mode, UINT16 addr)
{
	if( mode == 0 )
	{
		return io_read_byte_16be(0x0000ffff & addr);
	}
	else
	{
		/* special reads */
		return io_read_byte_16be(0x00010000 | addr);
	}
}

INLINE UINT32 RDPORT_L(int mode, UINT16 addr)
{
	if( mode == 0 )
	{
		return (io_read_byte_16be(0x0000ffff & addr)<<16) | (io_read_byte_16be(0x0000ffff & (addr+2)));
	}
	else
	{       /* special reads */
		return (io_read_byte_16be(0x00010000 | addr)<<16) | (io_read_byte_16be(0x00010000 | (addr+2)));
	}
}

INLINE void WRPORT_B(int mode, UINT16 addr, UINT8 value)
{
	if( mode == 0 )
	{
//TYPEERROR!    io_write_byte_8(0x0000ffff & addr,value);
                io_write_byte_16be(0x0000ffff & addr,value);
	}
	else
	{       /* special writes */
//TYPEERROR!    io_write_byte_8( 0x00010000 | addr, value);
                io_write_byte_16be( 0x00010000 | addr, value);
        }
}

INLINE void WRPORT_W(int mode, UINT16 addr, UINT16 value)
{
	if( mode == 0 )
	{
		io_write_byte_16be( 0x0000ffff & addr,value & 0xffff);
	}
	else
	{       /* special writes */
		io_write_byte_16be( 0x00010000 | addr,value & 0xffff);
        }
}

INLINE void WRPORT_L(int mode, UINT16 addr, UINT32 value)
{
	if( mode == 0 )
	{
		io_write_byte_16be( 0x0000ffff & addr,    (value>>16) & 0xffff);
		io_write_byte_16be( 0x0000ffff & (addr+2), value      & 0xffff);
	}
	else
	{
		/* special writes */
		io_write_byte_16be( 0x00010000 | addr,    (value>>16) & 0xffff);
		io_write_byte_16be( 0x00010000 | (addr+2), value      & 0xffff);
	}
}

// generic memory access functions, MMU usage will overwrite with info->CPUINFO_PTR_CPU_SPECIFIC
static Z8001_accessors z8001_native_accessors =
{
        RDOP_z8001,    RDOP_z8001,    RDOP_z8001,       RDOP_z8001,          // Fetch accessors - Read NS, NU, SS, SU
        RDMEM_B_z8001, RDMEM_B_z8001, RDMEMSEG_B_z8001, RDMEMSEG_B_z8001,    // Data Byte accessors - Read NS, NU, SS, SU
        WRMEM_B_z8001, WRMEM_B_z8001, WRMEMSEG_B_z8001, WRMEMSEG_B_z8001,    // Data Byte accessors - Write NS, NU, SS, SU
        RDMEM_W_z8001, RDMEM_W_z8001, RDMEMSEG_W_z8001, RDMEMSEG_W_z8001,    // Data Word accessors - Read NS, NU, SS, SU
        WRMEM_W_z8001, WRMEM_W_z8001, WRMEMSEG_W_z8001, WRMEMSEG_W_z8001,    // Data Word accessors - Write NS, NU, SS, SU
        RDMEM_L_z8001, RDMEM_L_z8001, RDMEMSEG_L_z8001, RDMEMSEG_L_z8001,    // Data DWord accessors - Read NS, NU, SS, SU
        WRMEM_L_z8001, WRMEM_L_z8001, WRMEMSEG_L_z8001, WRMEMSEG_L_z8001     // Data DWord accessors - Write NS, NU, SS, SU
};

// central accessor hook, writeable with info->CPUINFO_PTR_CPU_SPECIFIC to change access pointers when processor status change and context switch
// FIXME: should be part of the CPU context - therefore part of the Z structure for multi CPU systems
static Z8001_accessors* pAccessors = &z8001_native_accessors;

// memory access pointers used in z8001ops(_seg) ////////////////////////////////
static UINT16 (*code_acc_Wr)(UINT32);
static UINT8  (*data_acc_Br)(UINT32);
static UINT16 (*data_acc_Wr)(UINT32);
static UINT32 (*data_acc_Lr)(UINT32);
static void   (*data_acc_Bw)(UINT32,UINT8 );
static void   (*data_acc_Ww)(UINT32,UINT16);
static void   (*data_acc_Lw)(UINT32,UINT32);

#define RDOP(a)         (*code_acc_Wr)(a)
#define RDMEM_B(a)      (*data_acc_Br)(((PC & 0x7f000000))|(a & 0xffff))
#define RDMEM_W(a)      (*data_acc_Wr)(((PC & 0x7f000000))|(a & 0xfffe))
#define RDMEM_L(a)      (*data_acc_Lr)(((PC & 0x7f000000))|(a & 0xfffe))
#define WRMEM_B(a,d)    (*data_acc_Bw)(((PC & 0x7f000000))|(a & 0xffff),d)
#define WRMEM_W(a,d)    (*data_acc_Ww)(((PC & 0x7f000000))|(a & 0xfffe),d)
#define WRMEM_L(a,d)    (*data_acc_Lw)(((PC & 0x7f000000))|(a & 0xfffe),d)
#define RDMEMSEG_B(a)   (*data_acc_Br)(a)
#define RDMEMSEG_W(a)   (*data_acc_Wr)(a & 0x7f00fffe)
#define RDMEMSEG_L(a)   (*data_acc_Lr)(a & 0x7f00fffe)
#define WRMEMSEG_B(a,d) (*data_acc_Bw)(a & 0x7f00ffff,d)
#define WRMEMSEG_W(a,d) (*data_acc_Ww)(a & 0x7f00fffe,d)
#define WRMEMSEG_L(a,d) (*data_acc_Lw)(a & 0x7f00fffe,d)

// change_pc(PC) must be called with physical address - done by this macro
#define CHANGE_PC_PA(ch_var) ch_var=PC; (*pTranslate)(ADDRESS_SPACE_PROGRAM,&ch_var); change_pc(ch_var);

// called on FCW and context switch//////////////////////////////////
INLINE void z8001_init_accessors(void)
{
        // initialize accessor pointers
        switch(FCW & 0xc000) {
        case 0x0000:   // NonSeg,User
                       code_acc_Wr = pAccessors->nseg_usr_code;
                       data_acc_Br = pAccessors->nseg_usr_data_Br;
                       data_acc_Wr = pAccessors->nseg_usr_data_Wr;
                       data_acc_Lr = pAccessors->nseg_usr_data_Lr;
                       data_acc_Bw = pAccessors->nseg_usr_data_Bw;
                       data_acc_Ww = pAccessors->nseg_usr_data_Ww;
                       data_acc_Lw = pAccessors->nseg_usr_data_Lw;
                       break;
        case 0x4000:   // NonSeg,Sys
                       code_acc_Wr = pAccessors->nseg_sys_code;
                       data_acc_Br = pAccessors->nseg_sys_data_Br;
                       data_acc_Wr = pAccessors->nseg_sys_data_Wr;
                       data_acc_Lr = pAccessors->nseg_sys_data_Lr;
                       data_acc_Bw = pAccessors->nseg_sys_data_Bw;
                       data_acc_Ww = pAccessors->nseg_sys_data_Ww;
                       data_acc_Lw = pAccessors->nseg_sys_data_Lw;
                       break;
        case 0x8000:   // Seg,User
                       code_acc_Wr = pAccessors->seg_usr_code;
                       data_acc_Br = pAccessors->seg_usr_data_Br;
                       data_acc_Wr = pAccessors->seg_usr_data_Wr;
                       data_acc_Lr = pAccessors->seg_usr_data_Lr;
                       data_acc_Bw = pAccessors->seg_usr_data_Bw;
                       data_acc_Ww = pAccessors->seg_usr_data_Ww;
                       data_acc_Lw = pAccessors->seg_usr_data_Lw;
                       break;
        case 0xc000:   // Seg,Sys
                       code_acc_Wr = pAccessors->seg_sys_code;
                       data_acc_Br = pAccessors->seg_sys_data_Br;
                       data_acc_Wr = pAccessors->seg_sys_data_Wr;
                       data_acc_Lr = pAccessors->seg_sys_data_Lr;
                       data_acc_Bw = pAccessors->seg_sys_data_Bw;
                       data_acc_Ww = pAccessors->seg_sys_data_Ww;
                       data_acc_Lw = pAccessors->seg_sys_data_Lw;
                       break;
        }
}

#define OPCALL(x) x
#include "z8001ops.c"
#include "z8001ops_seg.c"
#include "z8001tbl.c"

INLINE void Interrupt(void)
{
   UINT16 fcw = FCW;

   IRQ_REQ &= ~Z8001_HALT;

   if(IRQ_REQ & (Z8001_EXTTRAP|Z8001_PRIVTRAP|Z8001_SYSCALL)) {   /* highest priority and treated as equal */

      if ( IRQ_REQ & Z8001_EXTTRAP )        /* Extended Instruction Trap */
      {
           CHANGE_FCW(fcw | F_S_N);         /* swap to system stack */
           if(Z.seg_state) {
              PUSHLSEG( SPSEG, PC );        /* save current PC */
              PUSHWSEG( SPSEG, fcw );       /* save current FCW */
              PUSHWSEG( SPSEG, IRQ_REQ );   /* save interrupt/trap type tag */
           } else {
              PUSHL( SP, PC );        /* save current PC */
              PUSHW( SP, fcw );       /* save current FCW */
              PUSHW( SP, IRQ_REQ );   /* save interrupt/trap type tag */
           }
           fcw = RDMEMSEG_W( EPU + 2 );
           PC = 0x7f00ffff & RDMEMSEG_L( EPU + 4 );
           IRQ_REQ &= ~Z8001_EXTTRAP;
           CHANGE_FCW(fcw);
           LOG(("Z8K#%d trap $%04x\n", cpu_getactivecpu(), PC ));
      }
   
      if ( IRQ_REQ & Z8001_PRIVTRAP )
      {
           CHANGE_FCW(fcw | F_S_N);/* swap to system stack */
           if(Z.seg_state) {
              PUSHLSEG( SPSEG, PC );        /* save current PC */
              PUSHWSEG( SPSEG, fcw );       /* save current FCW */
              PUSHWSEG( SPSEG, IRQ_REQ );   /* save interrupt/trap type tag */
           } else {
              PUSHL( SP, PC );        /* save current PC */
              PUSHW( SP, fcw );       /* save current FCW */
              PUSHW( SP, IRQ_REQ );   /* save interrupt/trap type tag */
           }
           fcw = RDMEMSEG_W( TRAP + 2 );
           PC = 0x7f00ffff & RDMEMSEG_L( TRAP + 4 );
           IRQ_REQ &= ~Z8001_PRIVTRAP;
           CHANGE_FCW(fcw);
           LOG(("Z8K#%d trap $%04x\n", cpu_getactivecpu(), PC ));
      }
      
      if ( IRQ_REQ & Z8001_SYSCALL )
      {
           CHANGE_FCW(fcw | F_SEG | F_S_N);/* swap to system stack */
           PUSHLSEG( SPSEG, PC );        /* save current PC */
           PUSHWSEG( SPSEG, fcw );       /* save current FCW */
           PUSHWSEG( SPSEG, 0x7f00 | (0xff & IRQ_REQ ));   /* save sc opcode */
           fcw = RDMEMSEG_W( SYSCALL + 2 );
           PC = 0x7f00ffff & RDMEMSEG_L( SYSCALL + 4 );
           IRQ_REQ &= ~Z8001_SYSCALL;
           CHANGE_FCW(fcw);
           LOG(("Z8K#%d syscall $%04x\n", cpu_getactivecpu(), PC ));
      }
   } 
   else
   if ( IRQ_REQ & Z8001_NMI )
   {
        CHANGE_FCW(fcw | F_SEG | F_S_N);   /* swap to system stack */
        PUSHLSEG( SPSEG, PC );             /* save current PC */
        PUSHWSEG( SPSEG, fcw );            /* save current FCW */
        PUSHWSEG( SPSEG, Z.irq_state[3] ); /* save nmi request data */
        fcw = RDMEMSEG_W( NMI + 2 );
        PC = 0x7f00ffff & RDMEMSEG_L( NMI + 4 );
        IRQ_REQ &= ~Z8001_NMI;
        CHANGE_FCW(fcw);
        LOG(("Z8K#%d NMI $%04x\n", cpu_getactivecpu(), PC ));
    }
   else
   if ( IRQ_REQ & Z8001_SEGTRAP )
   {
        CHANGE_FCW(fcw | F_SEG | F_S_N);                  /* swap to system stack */
        PUSHLSEG( SPSEG, PC );                            /* save current PC */
        PUSHWSEG( SPSEG, fcw );                           /* save current FCW */
        PUSHWSEG( SPSEG, 0x100<<(Z.irq_state[2] & 0x07)); /* mmu enumerator tag */
        fcw = RDMEMSEG_W( SEGTRAP + 2 );
        PC = 0x7f00ffff & RDMEMSEG_L( SEGTRAP + 4 );
        IRQ_REQ &= ~Z8001_SEGTRAP;
        CHANGE_FCW(fcw);
        LOG(("Z8K#%d segtrap $%04x\n", cpu_getactivecpu(), PC ));
//fprintf(stderr,"Z8K#%d segtrap $%04x FCW: %02x\n", cpu_getactivecpu(), PC, fcw );
   }
   else
   if ( (IRQ_REQ & Z8001_VI) && (FCW & F_VIE) )
   {    
        IRQ_REQ &= ~Z8001_VI;
        Z.irq_state[1] = (*Z.irq_callback)(1);

        CHANGE_FCW(fcw | F_SEG | F_S_N);   /* swap to system stack */
        PUSHLSEG( SPSEG, PC );             /* save current PC */
        PUSHWSEG( SPSEG, fcw );            /* save current FCW */
        PUSHWSEG( SPSEG, Z.irq_state[1] ); /* save interrupt/trap type tag */
        fcw = RDMEMSEG_W( VI + 2 );
        PC = 0x7f00ffff & RDMEMSEG_L( VEC00 + ((Z.irq_state[1] & 0xff)<<1));
        CHANGE_FCW(fcw);
        LOG(("Z8K#%d VI [$%04x/$%04x] fcw $%04x, pc $%04x\n", cpu_getactivecpu(), 0, VEC00 + VEC00 + 2 * (IRQ_REQ & 0xff), FCW, PC ));
   }
   else
   if ( (IRQ_REQ & Z8001_NVI) && (FCW & F_NVIE) )
   {
        IRQ_REQ &= ~Z8001_NVI;
        Z.irq_state[0] = (*Z.irq_callback)(0);  /* acknowledge NVI */

        CHANGE_FCW(fcw | F_SEG | F_S_N);   /* swap to system stack */
        PUSHLSEG( SPSEG, PC );             /* save current PC */
        PUSHWSEG( SPSEG, fcw );            /* save current FCW */
        PUSHWSEG( SPSEG, Z.irq_state[0] ); /* save nvi request data */
        fcw = RDMEMSEG_W( NVI + 2 );
        PC = 0x7f00ffff & RDMEMSEG_L( NVI + 4 );
        CHANGE_FCW(fcw);
        LOG(("Z8K#%d NVI $%04x\n", cpu_getactivecpu(), PC ));
   }
}


static void z8001_reset(void)
{
	int (*save_irqcallback)(int) = Z.irq_callback;

        FCW = 0;                 // prepare first accessor set
        z8001_init_accessors();
	memset(&Z, 0, sizeof(z8001_Regs));

	Z.irq_callback = save_irqcallback;

	FCW = RDMEM_W( 2 ); /* get reset FCW */
        PC  = RDMEM_W( 4 ) & 0x7f00; /* get reset SEG */
	PC |= RDMEM_W( 6 ) & 0xffff; /* get reset PC  */
        if(FCW & 0x8000) Z.seg_state=1;
        else             Z.seg_state=0;

        z8001_init_accessors(); // adjust accessors
	change_pc(PC);
}

static void z8001_exit(void)
{
	z8001_deinit();
}

static int z8001_execute(int cycles)
{
    z8001_ICount = cycles;

    do
    {
        /* any interrupt request pending? */
        if (IRQ_REQ & 0xff00 & ~Z8001_HALT) {
	    Interrupt();
        }

	CALL_DEBUGGER(PC);

	if (IRQ_REQ & Z8001_HALT)
        {
            z8001_ICount = 0;
        }
        else
        {

            Z8001_exec *exec;

            Z.ppc = PC;
            Z.op[0] = RDOP(PC); PC +=2;
            exec = Z.seg_state? &z8001_exec_seg[Z.op[0]] :  &z8001_exec[Z.op[0]];

            if (exec->size > 1) {
                Z.op[1] = RDOP(PC); PC +=2;
            }
            if (exec->size > 2) {
                Z.op[2] = RDOP(PC); PC +=2;
            }
            z8001_ICount -= exec->cycles;
            (*exec->opcode)();

#ifdef Z8001_CPU_INST_COVERAGE
            if(Z.seg_state)
               z8001_exec_seg_cov[Z.op[0]]++;
            else
               z8001_exec_cov[Z.op[0]]++;
#endif

        }
    } while (z8001_ICount > 0);

    return cycles - z8001_ICount;

}

static void z8001_get_context(void *dst)
{
	if( dst )
		*(z8001_Regs*)dst = Z;
}

static void z8001_set_context(void *src)
{
//	if( src )
//	{
//		Z = *(z8001_Regs*)src;  // FIXME! grober unfug!!!!
//		change_pc(PC);
//	}
}

static void set_irq_line(int irqline, int state)      /* only called for external events!!!! */
{       
        if(state==CLEAR_LINE) // line is reset by processor
           return;

        switch(irqline) {
        case 0:               IRQ_REQ |=  Z8001_NVI;
                              break;

        case 1:               IRQ_REQ |=  Z8001_VI;
                              break;

        case 2:               IRQ_REQ |=  Z8001_SEGTRAP;
                              Z.irq_state[2] = (*Z.irq_callback)(2); /* get MMU ID */
                              break;

        case INPUT_LINE_NMI:  IRQ_REQ |=  Z8001_NMI; 
                              Z.irq_state[3] = state;
                              break;
        }
}


/**************************************************************************
 * Generic set_info
 **************************************************************************/

static void z8001_set_info(UINT32 state, cpuinfo *info)
{       UINT32 seg_addr;
	switch (state)
	{
		/* --- the following bits of info are set as 64-bit signed integers --- */
		case CPUINFO_INT_INPUT_STATE + INPUT_LINE_NMI:	        set_irq_line(INPUT_LINE_NMI, info->i);	                	break;
		case CPUINFO_INT_INPUT_STATE + 0:			set_irq_line(0, info->i);					break;
		case CPUINFO_INT_INPUT_STATE + 1:			set_irq_line(1, info->i);					break;
		case CPUINFO_INT_INPUT_STATE + 2:			set_irq_line(2, info->i);					break;

		case CPUINFO_INT_PC:					PC = info->i; CHANGE_PC_PA(seg_addr);		        	break;
		case CPUINFO_INT_REGISTER + Z8001_PC:			PC = info->i;							break;
		case CPUINFO_INT_SP:
		case CPUINFO_INT_REGISTER + Z8001_R14N:			REG_R14N = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R14S:			REG_R14S = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R15N:			REG_R15N = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R15S:			REG_R15S = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_FCW:			FCW = info->i;							break;
		case CPUINFO_INT_REGISTER + Z8001_PSAP:			PSAP = info->i;							break;
		case CPUINFO_INT_REGISTER + Z8001_REFRESH:		REFRESH = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_IRQ_REQ:		IRQ_REQ = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R0:			RW( 0) = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R1:			RW( 1) = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R2:			RW( 2) = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R3:			RW( 3) = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R4:			RW( 4) = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R5:			RW( 5) = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R6:			RW( 6) = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R7:			RW( 7) = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R8:			RW( 8) = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R9:			RW( 9) = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R10:			RW(10) = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R11:			RW(11) = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R12:			RW(12) = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R13:			RW(13) = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R14:			RW(14) = info->i;						break;
		case CPUINFO_INT_REGISTER + Z8001_R15:			RW(15) = info->i;						break;
                case CPUINFO_PTR_TRANSLATE:                             pTranslate = info->translate;                                   break;
                case CPUINFO_PTR_CPU_SPECIFIC + Z8001_MEM_ACCESSOR:     pAccessors = (Z8001_accessors*)info->p; z8001_init_accessors(); break;
	}
}



/**************************************************************************
 * Generic get_info
 **************************************************************************/

void z8001_get_info(UINT32 state, cpuinfo *info)
{
	switch (state)
	{
		/* --- the following bits of info are returned as 64-bit signed integers --- */
		case CPUINFO_INT_CONTEXT_SIZE:					info->i = sizeof(Z);					break;
		case CPUINFO_INT_INPUT_LINES:					info->i = 2;							break;
		case CPUINFO_INT_DEFAULT_IRQ_VECTOR:			info->i = 0xff;							break;
		case CPUINFO_INT_ENDIANNESS:					info->i = CPU_IS_BE;					break;
		case CPUINFO_INT_CLOCK_MULTIPLIER:				info->i = 1;							break;
		case CPUINFO_INT_CLOCK_DIVIDER:					info->i = 1;							break;
		case CPUINFO_INT_MIN_INSTRUCTION_BYTES:			info->i = 2;							break;
		case CPUINFO_INT_MAX_INSTRUCTION_BYTES:			info->i = 8;							break;
		case CPUINFO_INT_MIN_CYCLES:					info->i = 1;							break;
		case CPUINFO_INT_MAX_CYCLES:					info->i = 25;							break;

		case CPUINFO_INT_DATABUS_WIDTH + ADDRESS_SPACE_PROGRAM:	info->i = 16;					break;
		case CPUINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_PROGRAM: info->i = 32;					break;
		case CPUINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACE_PROGRAM: info->i = 0;					break;
		case CPUINFO_INT_DATABUS_WIDTH + ADDRESS_SPACE_DATA:	info->i = 0;					break;
		case CPUINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_DATA: 	info->i = 0;					break;
		case CPUINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACE_DATA: 	info->i = 0;					break;
		case CPUINFO_INT_DATABUS_WIDTH + ADDRESS_SPACE_IO:		info->i = 16;					break;
		case CPUINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_IO: 		info->i = 32;					break;
		case CPUINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACE_IO: 		info->i = 0;					break;

		case CPUINFO_INT_INPUT_STATE + 0:				info->i = Z.irq_state[0];				break;
		case CPUINFO_INT_INPUT_STATE + 1:				info->i = Z.irq_state[1];				break;
		case CPUINFO_INT_INPUT_STATE + 2:				info->i = Z.irq_state[2];				break;
		case CPUINFO_INT_INPUT_STATE + INPUT_LINE_NMI:	                info->i = Z.irq_state[3];				break;

		case CPUINFO_INT_PREVIOUSPC:					info->i = PPC;							break;

		case CPUINFO_INT_PC:
		case CPUINFO_INT_REGISTER + Z8001_PC:			info->i = PC;        					        break;
		case CPUINFO_INT_SP:
                case CPUINFO_INT_REGISTER + Z8001_R14N:			info->i = REG_R14N;						break;
                case CPUINFO_INT_REGISTER + Z8001_R14S:			info->i = REG_R14S;						break;
                case CPUINFO_INT_REGISTER + Z8001_R15N:			info->i = REG_R15N;						break;
                case CPUINFO_INT_REGISTER + Z8001_R15S:			info->i = REG_R15S;						break;
                case CPUINFO_INT_REGISTER + Z8001_FCW:			info->i = FCW;							break;
		case CPUINFO_INT_REGISTER + Z8001_PSAP:			info->i = PSAP;							break;
		case CPUINFO_INT_REGISTER + Z8001_REFRESH:		info->i = REFRESH;						break;
		case CPUINFO_INT_REGISTER + Z8001_IRQ_REQ:		info->i = IRQ_REQ;						break;
		case CPUINFO_INT_REGISTER + Z8001_R0:			info->i = RW( 0);						break;
		case CPUINFO_INT_REGISTER + Z8001_R1:			info->i = RW( 1);						break;
		case CPUINFO_INT_REGISTER + Z8001_R2:			info->i = RW( 2);						break;
		case CPUINFO_INT_REGISTER + Z8001_R3:			info->i = RW( 3);						break;
		case CPUINFO_INT_REGISTER + Z8001_R4:			info->i = RW( 4);						break;
		case CPUINFO_INT_REGISTER + Z8001_R5:			info->i = RW( 5);						break;
		case CPUINFO_INT_REGISTER + Z8001_R6:			info->i = RW( 6);						break;
		case CPUINFO_INT_REGISTER + Z8001_R7:			info->i = RW( 7);						break;
		case CPUINFO_INT_REGISTER + Z8001_R8:			info->i = RW( 8);						break;
		case CPUINFO_INT_REGISTER + Z8001_R9:			info->i = RW( 9);						break;
		case CPUINFO_INT_REGISTER + Z8001_R10:			info->i = RW(10);						break;
		case CPUINFO_INT_REGISTER + Z8001_R11:			info->i = RW(11);						break;
		case CPUINFO_INT_REGISTER + Z8001_R12:			info->i = RW(12);						break;
		case CPUINFO_INT_REGISTER + Z8001_R13:			info->i = RW(13);						break;
		case CPUINFO_INT_REGISTER + Z8001_R14:			info->i = RW(14);						break;
		case CPUINFO_INT_REGISTER + Z8001_R15:			info->i = RW(15);						break;

		/* --- the following bits of info are returned as pointers to data or functions --- */
		case CPUINFO_PTR_SET_INFO:						info->setinfo = z8001_set_info;			break;
		case CPUINFO_PTR_GET_CONTEXT:					info->getcontext = z8001_get_context;	break;
		case CPUINFO_PTR_SET_CONTEXT:					info->setcontext = z8001_set_context;	break;
		case CPUINFO_PTR_INIT:							info->init = z8001_init;				break;
		case CPUINFO_PTR_RESET:							info->reset = z8001_reset;				break;
		case CPUINFO_PTR_EXIT:							info->exit = z8001_exit;				break;
		case CPUINFO_PTR_EXECUTE:						info->execute = z8001_execute;			break;
                case CPUINFO_PTR_TRANSLATE:                                             info->translate = pTranslate;                   break;
		case CPUINFO_PTR_BURN:							info->burn = NULL;						break;
#ifdef ENABLE_DEBUGGER
		case CPUINFO_PTR_DISASSEMBLE:					info->disassemble = z8001_dasm;			break;
#endif /* ENABLE_DEBUGGER */
		case CPUINFO_PTR_INSTRUCTION_COUNTER:			info->icount = &z8001_ICount;			break;
                case CPUINFO_PTR_CPU_SPECIFIC + Z8001_FCW_CHANGE_PTR:   info->p = &Z.fcw;                                        break;

		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case CPUINFO_STR_NAME:							strcpy(info->s, "Z8001");				break;
		case CPUINFO_STR_CORE_FAMILY:					strcpy(info->s, "Zilog Z8001");			break;
		case CPUINFO_STR_CORE_VERSION:					strcpy(info->s, "1.1");					break;
		case CPUINFO_STR_CORE_FILE:						strcpy(info->s, __FILE__);				break;
		case CPUINFO_STR_CORE_CREDITS:					strcpy(info->s, "Copyright Matt Knoth, Juergen Buchmueller, all rights reserved."); break;

		case CPUINFO_STR_FLAGS:
			sprintf(info->s, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
				Z.fcw & 0x8000 ? 's':'.',
				Z.fcw & 0x4000 ? 'n':'.',
				Z.fcw & 0x2000 ? 'e':'.',
				Z.fcw & 0x1000 ? '2':'.',
				Z.fcw & 0x0800 ? '1':'.',
				Z.fcw & 0x0400 ? '?':'.',
				Z.fcw & 0x0200 ? '?':'.',
				Z.fcw & 0x0100 ? '?':'.',
				Z.fcw & 0x0080 ? 'C':'.',
				Z.fcw & 0x0040 ? 'Z':'.',
				Z.fcw & 0x0020 ? 'S':'.',
				Z.fcw & 0x0010 ? 'V':'.',
				Z.fcw & 0x0008 ? 'D':'.',
				Z.fcw & 0x0004 ? 'H':'.',
				Z.fcw & 0x0002 ? '?':'.',
				Z.fcw & 0x0001 ? '?':'.');
            break;

		case CPUINFO_STR_REGISTER + Z8001_PC:			sprintf(info->s, "PC :%08X", Z.pc);	break;
		case CPUINFO_STR_REGISTER + Z8001_R14N:			sprintf(info->s, "R14N:%04X", Z.r14n);	break;
		case CPUINFO_STR_REGISTER + Z8001_R14S:			sprintf(info->s, "R14S:%04X", Z.r14s);	break;
		case CPUINFO_STR_REGISTER + Z8001_R15N:			sprintf(info->s, "R15N:%04X", Z.r15n);	break;
		case CPUINFO_STR_REGISTER + Z8001_R15S:			sprintf(info->s, "R15S:%04X", Z.r15s);	break;
		case CPUINFO_STR_REGISTER + Z8001_FCW:			sprintf(info->s, "FCW:%04X", Z.fcw);	break;
		case CPUINFO_STR_REGISTER + Z8001_PSAP:			sprintf(info->s, "PSAP:%08X", Z.psap);	break;
		case CPUINFO_STR_REGISTER + Z8001_REFRESH:		sprintf(info->s, "REFR:%04X", Z.refresh); break;
		case CPUINFO_STR_REGISTER + Z8001_IRQ_REQ:		sprintf(info->s, "IRQR:%04X", Z.irq_req); break;
#ifdef	LSB_FIRST
#define REG_XOR 3
#else
#define REG_XOR 0
#endif
		case CPUINFO_STR_REGISTER + Z8001_R0:			sprintf(info->s, "R0 :%04X", Z.regs.W[ 0^REG_XOR]); break;
		case CPUINFO_STR_REGISTER + Z8001_R1:			sprintf(info->s, "R1 :%04X", Z.regs.W[ 1^REG_XOR]); break;
		case CPUINFO_STR_REGISTER + Z8001_R2:			sprintf(info->s, "R2 :%04X", Z.regs.W[ 2^REG_XOR]); break;
		case CPUINFO_STR_REGISTER + Z8001_R3:			sprintf(info->s, "R3 :%04X", Z.regs.W[ 3^REG_XOR]); break;
		case CPUINFO_STR_REGISTER + Z8001_R4:			sprintf(info->s, "R4 :%04X", Z.regs.W[ 4^REG_XOR]); break;
		case CPUINFO_STR_REGISTER + Z8001_R5:			sprintf(info->s, "R5 :%04X", Z.regs.W[ 5^REG_XOR]); break;
		case CPUINFO_STR_REGISTER + Z8001_R6:			sprintf(info->s, "R6 :%04X", Z.regs.W[ 6^REG_XOR]); break;
		case CPUINFO_STR_REGISTER + Z8001_R7:			sprintf(info->s, "R7 :%04X", Z.regs.W[ 7^REG_XOR]); break;
		case CPUINFO_STR_REGISTER + Z8001_R8:			sprintf(info->s, "R8 :%04X", Z.regs.W[ 8^REG_XOR]); break;
		case CPUINFO_STR_REGISTER + Z8001_R9:			sprintf(info->s, "R9 :%04X", Z.regs.W[ 9^REG_XOR]); break;
		case CPUINFO_STR_REGISTER + Z8001_R10:			sprintf(info->s, "R10:%04X", Z.regs.W[10^REG_XOR]); break;
		case CPUINFO_STR_REGISTER + Z8001_R11:			sprintf(info->s, "R11:%04X", Z.regs.W[11^REG_XOR]); break;
		case CPUINFO_STR_REGISTER + Z8001_R12:			sprintf(info->s, "R12:%04X", Z.regs.W[12^REG_XOR]); break;
		case CPUINFO_STR_REGISTER + Z8001_R13:			sprintf(info->s, "R13:%04X", Z.regs.W[13^REG_XOR]); break;
		case CPUINFO_STR_REGISTER + Z8001_R14:			sprintf(info->s, "R14:%04X", Z.regs.W[14^REG_XOR]); break;
		case CPUINFO_STR_REGISTER + Z8001_R15:			sprintf(info->s, "R15:%04X", Z.regs.W[15^REG_XOR]); break;
	}
}
