summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/powerpc/ppccom.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/powerpc/ppccom.cpp')
-rw-r--r--src/devices/cpu/powerpc/ppccom.cpp2879
1 files changed, 2879 insertions, 0 deletions
diff --git a/src/devices/cpu/powerpc/ppccom.cpp b/src/devices/cpu/powerpc/ppccom.cpp
new file mode 100644
index 00000000000..a98872029ec
--- /dev/null
+++ b/src/devices/cpu/powerpc/ppccom.cpp
@@ -0,0 +1,2879 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ ppccom.c
+
+ Common PowerPC definitions and functions
+
+***************************************************************************/
+
+#include "emu.h"
+#include "ppccom.h"
+#include "ppcfe.h"
+
+
+/***************************************************************************
+ DEBUGGING
+***************************************************************************/
+
+#define PRINTF_SPU (0)
+#define PRINTF_DECREMENTER (0)
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define DOUBLE_SIGN (U64(0x8000000000000000))
+#define DOUBLE_EXP (U64(0x7ff0000000000000))
+#define DOUBLE_FRAC (U64(0x000fffffffffffff))
+#define DOUBLE_ZERO (0)
+
+
+
+/***************************************************************************
+ PRIVATE GLOBAL VARIABLES
+***************************************************************************/
+
+/* lookup table for FP modes */
+static const UINT8 fpmode_source[4] =
+{
+ uml::ROUND_ROUND,
+ uml::ROUND_TRUNC,
+ uml::ROUND_CEIL,
+ uml::ROUND_FLOOR
+};
+
+/* flag lookup table for SZ */
+static const UINT8 sz_cr_table_source[32] =
+{
+ /* ..... */ 0x4,
+ /* ....C */ 0x4,
+ /* ...V. */ 0x4,
+ /* ...VC */ 0x4,
+ /* ..Z.. */ 0x2,
+ /* ..Z.C */ 0x2,
+ /* ..ZV. */ 0x2,
+ /* ..ZVC */ 0x2,
+ /* .S... */ 0x8,
+ /* .S..C */ 0x8,
+ /* .S.V. */ 0x8,
+ /* .S.VC */ 0x8,
+ /* .SZ.. */ 0x2,
+ /* .SZ.C */ 0x2,
+ /* .SZV. */ 0x2,
+ /* .SZVC */ 0x2,
+ /* U.... */ 0x4,
+ /* U...C */ 0x4,
+ /* U..V. */ 0x4,
+ /* U..VC */ 0x4,
+ /* U.Z.. */ 0x2,
+ /* U.Z.C */ 0x2,
+ /* U.ZV. */ 0x2,
+ /* U.ZVC */ 0x2,
+ /* US... */ 0x8,
+ /* US..C */ 0x8,
+ /* US.V. */ 0x8,
+ /* US.VC */ 0x8,
+ /* USZ.. */ 0x2,
+ /* USZ.C */ 0x2,
+ /* USZV. */ 0x2,
+ /* USZVC */ 0x2
+};
+
+/* flag lookup table for CMP */
+static const UINT8 cmp_cr_table_source[32] =
+{
+ /* ..... */ 0x4,
+ /* ....C */ 0x4,
+ /* ...V. */ 0x8,
+ /* ...VC */ 0x8,
+ /* ..Z.. */ 0x2,
+ /* ..Z.C */ 0x2,
+ /* ..ZV. */ 0x2,
+ /* ..ZVC */ 0x2,
+ /* .S... */ 0x8,
+ /* .S..C */ 0x8,
+ /* .S.V. */ 0x4,
+ /* .S.VC */ 0x4,
+ /* .SZ.. */ 0x2,
+ /* .SZ.C */ 0x2,
+ /* .SZV. */ 0x2,
+ /* .SZVC */ 0x2,
+ /* U.... */ 0x4,
+ /* U...C */ 0x4,
+ /* U..V. */ 0x8,
+ /* U..VC */ 0x8,
+ /* U.Z.. */ 0x2,
+ /* U.Z.C */ 0x2,
+ /* U.ZV. */ 0x2,
+ /* U.ZVC */ 0x2,
+ /* US... */ 0x8,
+ /* US..C */ 0x8,
+ /* US.V. */ 0x4,
+ /* US.VC */ 0x4,
+ /* USZ.. */ 0x2,
+ /* USZ.C */ 0x2,
+ /* USZV. */ 0x2,
+ /* USZVC */ 0x2
+};
+
+/* flag lookup table for CMPL */
+static const UINT8 cmpl_cr_table_source[32] =
+{
+ /* ..... */ 0x4,
+ /* ....C */ 0x8,
+ /* ...V. */ 0x4,
+ /* ...VC */ 0x8,
+ /* ..Z.. */ 0x2,
+ /* ..Z.C */ 0x2,
+ /* ..ZV. */ 0x2,
+ /* ..ZVC */ 0x2,
+ /* .S... */ 0x4,
+ /* .S..C */ 0x8,
+ /* .S.V. */ 0x4,
+ /* .S.VC */ 0x8,
+ /* .SZ.. */ 0x2,
+ /* .SZ.C */ 0x2,
+ /* .SZV. */ 0x2,
+ /* .SZVC */ 0x2,
+ /* U.... */ 0x4,
+ /* U...C */ 0x8,
+ /* U..V. */ 0x4,
+ /* U..VC */ 0x8,
+ /* U.Z.. */ 0x2,
+ /* U.Z.C */ 0x2,
+ /* U.ZV. */ 0x2,
+ /* U.ZVC */ 0x2,
+ /* US... */ 0x4,
+ /* US..C */ 0x8,
+ /* US.V. */ 0x4,
+ /* US.VC */ 0x8,
+ /* USZ.. */ 0x2,
+ /* USZ.C */ 0x2,
+ /* USZV. */ 0x2,
+ /* USZVC */ 0x2
+};
+
+/* flag lookup table for FCMP */
+static const UINT8 fcmp_cr_table_source[32] =
+{
+ /* ..... */ 0x4,
+ /* ....C */ 0x8,
+ /* ...V. */ 0x4,
+ /* ...VC */ 0x8,
+ /* ..Z.. */ 0x2,
+ /* ..Z.C */ 0xa,
+ /* ..ZV. */ 0x2,
+ /* ..ZVC */ 0xa,
+ /* .S... */ 0x4,
+ /* .S..C */ 0x8,
+ /* .S.V. */ 0x4,
+ /* .S.VC */ 0x8,
+ /* .SZ.. */ 0x2,
+ /* .SZ.C */ 0xa,
+ /* .SZV. */ 0x2,
+ /* .SZVC */ 0xa,
+ /* U.... */ 0x5,
+ /* U...C */ 0x9,
+ /* U..V. */ 0x5,
+ /* U..VC */ 0x9,
+ /* U.Z.. */ 0x3,
+ /* U.Z.C */ 0xb,
+ /* U.ZV. */ 0x3,
+ /* U.ZVC */ 0xb,
+ /* US... */ 0x5,
+ /* US..C */ 0x9,
+ /* US.V. */ 0x5,
+ /* US.VC */ 0x9,
+ /* USZ.. */ 0x3,
+ /* USZ.C */ 0xb,
+ /* USZV. */ 0x3,
+ /* USZVC */ 0xb
+};
+
+
+const device_type PPC601 = &device_creator<ppc601_device>;
+const device_type PPC602 = &device_creator<ppc602_device>;
+const device_type PPC603 = &device_creator<ppc603_device>;
+const device_type PPC603E = &device_creator<ppc603e_device>;
+const device_type PPC603R = &device_creator<ppc603r_device>;
+const device_type PPC604 = &device_creator<ppc604_device>;
+const device_type MPC8240 = &device_creator<mpc8240_device>;
+const device_type PPC403GA = &device_creator<ppc403ga_device>;
+const device_type PPC403GCX = &device_creator<ppc403gcx_device>;
+const device_type PPC405GP = &device_creator<ppc405gp_device>;
+
+
+ppc_device::ppc_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, int address_bits, int data_bits, powerpc_flavor flavor, UINT32 cap, UINT32 tb_divisor, address_map_constructor internal_map)
+ : cpu_device(mconfig, type, name, tag, owner, clock, shortname, __FILE__)
+ , m_program_config("program", ENDIANNESS_BIG, data_bits, address_bits, 0, internal_map)
+ , c_bus_frequency(0)
+ , m_core(NULL)
+ , m_bus_freq_multiplier(1)
+ , m_vtlb(NULL)
+ , m_flavor(flavor)
+ , m_cap(cap)
+ , m_tb_divisor(tb_divisor)
+ , m_cache(CACHE_SIZE + sizeof(internal_ppc_state))
+ , m_drcuml(NULL)
+ , m_drcfe(NULL)
+ , m_drcoptions(0)
+{
+ m_program_config.m_logaddr_width = 32;
+ m_program_config.m_page_shift = POWERPC_MIN_PAGE_SHIFT;
+}
+
+//ppc403_device::ppc403_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+// : ppc_device(mconfig, PPC403, "PPC403", tag, owner, clock, "ppc403", 32?, 64?)
+//{
+//}
+//
+//ppc405_device::ppc405_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+// : ppc_device(mconfig, PPC405, "PPC405", tag, owner, clock, "ppc405", 32?, 64?)
+//{
+//}
+
+ppc603_device::ppc603_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ppc_device(mconfig, PPC603, "PowerPC 603", tag, owner, clock, "ppc603", 32, 64, PPC_MODEL_603, PPCCAP_OEA | PPCCAP_VEA | PPCCAP_FPU | PPCCAP_MISALIGNED | PPCCAP_603_MMU, 4, NULL)
+{
+}
+
+ppc603e_device::ppc603e_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ppc_device(mconfig, PPC603E, "PowerPC 603e", tag, owner, clock, "ppc603e", 32, 64, PPC_MODEL_603E, PPCCAP_OEA | PPCCAP_VEA | PPCCAP_FPU | PPCCAP_MISALIGNED | PPCCAP_603_MMU, 4, NULL)
+{
+}
+
+ppc603r_device::ppc603r_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ppc_device(mconfig, PPC603R, "PowerPC 603R", tag, owner, clock, "ppc603r", 32, 64, PPC_MODEL_603R, PPCCAP_OEA | PPCCAP_VEA | PPCCAP_FPU | PPCCAP_MISALIGNED | PPCCAP_603_MMU, 4, NULL)
+{
+}
+
+ppc602_device::ppc602_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ppc_device(mconfig, PPC602, "PowerPC 602", tag, owner, clock, "ppc602", 32, 64, PPC_MODEL_602, PPCCAP_OEA | PPCCAP_VEA | PPCCAP_FPU | PPCCAP_MISALIGNED | PPCCAP_603_MMU, 4, NULL)
+{
+}
+
+mpc8240_device::mpc8240_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ppc_device(mconfig, MPC8240, "PowerPC MPC8240", tag, owner, clock, "mpc8240", 32, 64, PPC_MODEL_MPC8240, PPCCAP_OEA | PPCCAP_VEA | PPCCAP_FPU | PPCCAP_MISALIGNED | PPCCAP_603_MMU, 4/* unknown */, NULL)
+{
+}
+
+ppc601_device::ppc601_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ppc_device(mconfig, PPC601, "PowerPC 601", tag, owner, clock, "ppc601", 32, 64, PPC_MODEL_601, PPCCAP_OEA | PPCCAP_VEA | PPCCAP_FPU | PPCCAP_MISALIGNED | PPCCAP_MFIOC | PPCCAP_601BAT, 0/* no TB */, NULL)
+{
+}
+
+ppc604_device::ppc604_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ppc_device(mconfig, PPC604, "PowerPC 604", tag, owner, clock, "ppc604", 32, 64, PPC_MODEL_604, PPCCAP_OEA | PPCCAP_VEA | PPCCAP_FPU | PPCCAP_MISALIGNED | PPCCAP_604_MMU, 4, NULL)
+{
+}
+
+static ADDRESS_MAP_START( internal_ppc4xx, AS_PROGRAM, 32, ppc4xx_device )
+ AM_RANGE(0x40000000, 0x4000000f) AM_READWRITE8(ppc4xx_spu_r, ppc4xx_spu_w, 0xffffffff)
+ADDRESS_MAP_END
+
+ppc4xx_device::ppc4xx_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, powerpc_flavor flavor, UINT32 cap, UINT32 tb_divisor)
+ : ppc_device(mconfig, type, name, tag, owner, clock, shortname, 31, 32, flavor, cap, tb_divisor, ADDRESS_MAP_NAME(internal_ppc4xx))
+{
+}
+
+ppc403ga_device::ppc403ga_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ppc4xx_device(mconfig, PPC403GA, "PowerPC 403GA", tag, owner, clock, "ppc403ga", PPC_MODEL_403GA, PPCCAP_4XX, 1)
+{
+}
+
+ppc403gcx_device::ppc403gcx_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ppc4xx_device(mconfig, PPC403GCX, "PowerPC 403GCX", tag, owner, clock, "ppc403gcx", PPC_MODEL_403GCX, PPCCAP_4XX, 1)
+{
+}
+
+ppc405gp_device::ppc405gp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ppc4xx_device(mconfig, PPC405GP, "PowerPC 405GP", tag, owner, clock, "ppc405gp", PPC_MODEL_405GP, PPCCAP_4XX | PPCCAP_VEA, 1)
+{
+}
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ page_access_allowed - return true if we are
+ allowed to access memory based on the type
+ of access and the protection bits
+-------------------------------------------------*/
+
+INLINE int page_access_allowed(int transtype, UINT8 key, UINT8 protbits)
+{
+ if (key == 0)
+ return (transtype == TRANSLATE_WRITE) ? (protbits != 3) : TRUE;
+ else
+ return (transtype == TRANSLATE_WRITE) ? (protbits == 2) : (protbits != 0);
+}
+
+
+/*-------------------------------------------------
+ get_cr - return the current CR value
+-------------------------------------------------*/
+
+inline UINT32 ppc_device::get_cr()
+{
+ return ((m_core->cr[0] & 0x0f) << 28) |
+ ((m_core->cr[1] & 0x0f) << 24) |
+ ((m_core->cr[2] & 0x0f) << 20) |
+ ((m_core->cr[3] & 0x0f) << 16) |
+ ((m_core->cr[4] & 0x0f) << 12) |
+ ((m_core->cr[5] & 0x0f) << 8) |
+ ((m_core->cr[6] & 0x0f) << 4) |
+ ((m_core->cr[7] & 0x0f) << 0);
+}
+
+
+/*-------------------------------------------------
+ set_cr - set the current CR value
+-------------------------------------------------*/
+
+inline void ppc_device::set_cr(UINT32 value)
+{
+ m_core->cr[0] = value >> 28;
+ m_core->cr[1] = value >> 24;
+ m_core->cr[2] = value >> 20;
+ m_core->cr[3] = value >> 16;
+ m_core->cr[4] = value >> 12;
+ m_core->cr[5] = value >> 8;
+ m_core->cr[6] = value >> 4;
+ m_core->cr[7] = value >> 0;
+}
+
+
+/*-------------------------------------------------
+ get_xer - return the current XER value
+-------------------------------------------------*/
+
+inline UINT32 ppc_device::get_xer()
+{
+ return m_core->spr[SPR_XER] | (m_core->xerso << 31);
+}
+
+
+/*-------------------------------------------------
+ set_xer - set the current XER value
+-------------------------------------------------*/
+
+inline void ppc_device::set_xer(UINT32 value)
+{
+ m_core->spr[SPR_XER] = value & ~XER_SO;
+ m_core->xerso = value >> 31;
+}
+
+
+/*-------------------------------------------------
+ get_timebase - return the current timebase
+ value
+-------------------------------------------------*/
+
+inline UINT64 ppc_device::get_timebase()
+{
+ if (!m_tb_divisor)
+ {
+ return (total_cycles() - m_tb_zero_cycles);
+ }
+
+ return (total_cycles() - m_tb_zero_cycles) / m_tb_divisor;
+}
+
+
+/*-------------------------------------------------
+ set_timebase - set the timebase
+-------------------------------------------------*/
+
+inline void ppc_device::set_timebase(UINT64 newtb)
+{
+ m_tb_zero_cycles = total_cycles() - newtb * m_tb_divisor;
+}
+
+
+/*-------------------------------------------------
+ get_decremeter - return the current
+ decrementer value
+-------------------------------------------------*/
+
+inline UINT32 ppc_device::get_decrementer()
+{
+ INT64 cycles_until_zero = m_dec_zero_cycles - total_cycles();
+ cycles_until_zero = MAX(cycles_until_zero, 0);
+
+ if (!m_tb_divisor)
+ {
+ return 0;
+ }
+
+ return cycles_until_zero / m_tb_divisor;
+}
+
+
+/*-------------------------------------------------
+ set_decrementer - set the decremeter
+-------------------------------------------------*/
+
+inline void ppc_device::set_decrementer(UINT32 newdec)
+{
+ UINT64 cycles_until_done = ((UINT64)newdec + 1) * m_tb_divisor;
+ UINT32 curdec = get_decrementer();
+
+ if (!m_tb_divisor)
+ {
+ return;
+ }
+
+ if (PRINTF_DECREMENTER)
+ {
+ UINT64 total = total_cycles();
+ osd_printf_debug("set_decrementer: olddec=%08X newdec=%08X divisor=%d totalcyc=%08X%08X timer=%08X%08X\n",
+ curdec, newdec, m_tb_divisor,
+ (UINT32)(total >> 32), (UINT32)total, (UINT32)(cycles_until_done >> 32), (UINT32)cycles_until_done);
+ }
+
+ m_dec_zero_cycles = total_cycles() + cycles_until_done;
+ m_decrementer_int_timer->adjust(cycles_to_attotime(cycles_until_done));
+
+ if ((INT32)curdec >= 0 && (INT32)newdec < 0)
+ m_core->irq_pending |= 0x02;
+}
+
+
+#if 0
+/*-------------------------------------------------
+ is_nan_double - is a double value a NaN
+-------------------------------------------------*/
+
+INLINE int is_nan_double(double x)
+{
+ UINT64 xi = *(UINT64*)&x;
+ return( ((xi & DOUBLE_EXP) == DOUBLE_EXP) &&
+ ((xi & DOUBLE_FRAC) != DOUBLE_ZERO) );
+}
+#endif
+
+
+/*-------------------------------------------------
+ is_qnan_double - is a double value a
+ quiet NaN
+-------------------------------------------------*/
+
+INLINE int is_qnan_double(double x)
+{
+ UINT64 xi = *(UINT64*)&x;
+ return( ((xi & DOUBLE_EXP) == DOUBLE_EXP) &&
+ ((xi & U64(0x0007fffffffffff)) == U64(0x000000000000000)) &&
+ ((xi & U64(0x000800000000000)) == U64(0x000800000000000)) );
+}
+
+
+#if 0
+/*-------------------------------------------------
+ is_snan_double - is a double value a
+ signaling NaN
+-------------------------------------------------*/
+
+INLINE int is_snan_double(double x)
+{
+ UINT64 xi = *(UINT64*)&x;
+ return( ((xi & DOUBLE_EXP) == DOUBLE_EXP) &&
+ ((xi & DOUBLE_FRAC) != DOUBLE_ZERO) &&
+ ((xi & U64(0x0008000000000000)) == DOUBLE_ZERO) );
+}
+#endif
+
+
+/*-------------------------------------------------
+ is_infinity_double - is a double value
+ infinity
+-------------------------------------------------*/
+
+INLINE int is_infinity_double(double x)
+{
+ UINT64 xi = *(UINT64*)&x;
+ return( ((xi & DOUBLE_EXP) == DOUBLE_EXP) &&
+ ((xi & DOUBLE_FRAC) == DOUBLE_ZERO) );
+}
+
+
+/*-------------------------------------------------
+ is_normalized_double - is a double value
+ normalized
+-------------------------------------------------*/
+
+INLINE int is_normalized_double(double x)
+{
+ UINT64 exp;
+ UINT64 xi = *(UINT64*)&x;
+ exp = (xi & DOUBLE_EXP) >> 52;
+
+ return (exp >= 1) && (exp <= 2046);
+}
+
+
+/*-------------------------------------------------
+ is_denormalized_double - is a double value
+ denormalized
+-------------------------------------------------*/
+
+INLINE int is_denormalized_double(double x)
+{
+ UINT64 xi = *(UINT64*)&x;
+ return( ((xi & DOUBLE_EXP) == 0) &&
+ ((xi & DOUBLE_FRAC) != DOUBLE_ZERO) );
+}
+
+
+/*-------------------------------------------------
+ sign_double - return sign of a double value
+-------------------------------------------------*/
+
+INLINE int sign_double(double x)
+{
+ UINT64 xi = *(UINT64*)&x;
+ return ((xi & DOUBLE_SIGN) != 0);
+}
+
+
+
+/***************************************************************************
+ INITIALIZATION AND SHUTDOWN
+***************************************************************************/
+
+/*-------------------------------------------------
+ device_start - initialize the powerpc_state
+ structure based on the configured type
+-------------------------------------------------*/
+
+void ppc_device::device_start()
+{
+ /* allocate the core from the near cache */
+ m_core = (internal_ppc_state *)m_cache.alloc_near(sizeof(internal_ppc_state));
+ memset(m_core, 0, sizeof(internal_ppc_state));
+
+ m_entry = NULL;
+ m_nocode = NULL;
+ m_out_of_cycles = NULL;
+ m_tlb_mismatch = NULL;
+ m_swap_tgpr = NULL;
+ memset(m_lsw, 0, sizeof(m_lsw));
+ memset(m_stsw, 0, sizeof(m_stsw));
+ memset(m_read8, 0, sizeof(m_read8));
+ memset(m_write8, 0, sizeof(m_write8));
+ memset(m_read16, 0, sizeof(m_read16));
+ memset(m_read16mask, 0, sizeof(m_read16mask));
+ memset(m_write16, 0, sizeof(m_write16));
+ memset(m_write16mask, 0, sizeof(m_write16mask));
+ memset(m_read32, 0, sizeof(m_read32));
+ memset(m_read32align, 0, sizeof(m_read32align));
+ memset(m_read32mask, 0, sizeof(m_read32mask));
+ memset(m_write32, 0, sizeof(m_write32));
+ memset(m_write32align, 0, sizeof(m_write32align));
+ memset(m_write32mask, 0, sizeof(m_write32mask));
+ memset(m_read64, 0, sizeof(m_read64));
+ memset(m_read64mask, 0, sizeof(m_read64mask));
+ memset(m_write64, 0, sizeof(m_write64));
+ memset(m_write64mask, 0, sizeof(m_write64mask));
+ memset(m_exception, 0, sizeof(m_exception));
+ memset(m_exception_norecover, 0, sizeof(m_exception_norecover));
+
+ /* initialize the implementation state tables */
+ memcpy(m_fpmode, fpmode_source, sizeof(fpmode_source));
+ memcpy(m_sz_cr_table, sz_cr_table_source, sizeof(sz_cr_table_source));
+ memcpy(m_cmp_cr_table, cmp_cr_table_source, sizeof(cmp_cr_table_source));
+ memcpy(m_cmpl_cr_table, cmpl_cr_table_source, sizeof(cmpl_cr_table_source));
+ memcpy(m_fcmp_cr_table, fcmp_cr_table_source, sizeof(fcmp_cr_table_source));
+
+ /* initialize based on the config */
+ m_ppc_tb_base_icount = 0;
+ m_ppc_dec_base_icount = 0;
+ m_ppc_dec_trigger_cycle = 0;
+ m_bus_freq_multiplier = 0;
+
+ m_npc = 0;
+ memset(m_dcr, 0, sizeof(m_dcr));
+
+ m_lr = 0;
+ m_ctr = 0;
+ m_xer = 0;
+ m_pvr = 0;
+ m_srr0 = 0;
+ m_srr1 = 0;
+ m_srr2 = 0;
+ m_srr3 = 0;
+ m_hid0 = 0;
+ m_hid1 = 0;
+ m_hid2 = 0;
+ m_sdr1 = 0;
+ memset(m_sprg, 0, sizeof(m_sprg));
+
+ m_dsisr = 0;
+ m_dar = 0;
+ m_ear = 0;
+ m_dmiss = 0;
+ m_dcmp = 0;
+ m_hash1 = 0;
+ m_hash2 = 0;
+ m_imiss = 0;
+ m_icmp = 0;
+ m_rpa = 0;
+
+ memset(m_ibat, 0, sizeof(m_ibat));
+ memset(m_dbat, 0, sizeof(m_dbat));
+
+ m_evpr = 0;
+ m_exier = 0;
+ m_exisr = 0;
+ m_bear = 0;
+ m_besr = 0;
+ m_iocr = 0;
+ memset(m_br, 0, sizeof(m_br));
+ m_iabr = 0;
+ m_esr = 0;
+ m_iccr = 0;
+ m_dccr = 0;
+ m_pit = 0;
+ m_pit_counter = 0;
+ m_pit_int_enable = 0;
+ m_tsr = 0;
+ m_dbsr = 0;
+ m_sgr = 0;
+ m_pid = 0;
+ m_pbl1 = 0;
+ m_pbl2 = 0;
+ m_pbu1 = 0;
+ m_pbu2 = 0;
+ m_fit_bit = 0;
+ m_fit_int_enable = 0;
+ m_wdt_bit = 0;
+ m_wdt_int_enable = 0;
+ m_dac1 = 0;
+ m_dac2 = 0;
+ m_iac1 = 0;
+ m_iac2 = 0;
+
+ memset(&m_spu_old, 0, sizeof(m_spu_old));
+ memset(m_dma, 0, sizeof(m_dma));
+ m_dmasr = 0;
+
+ m_reserved = 0;
+ m_reserved_address = 0;
+ m_interrupt_pending = 0;
+ m_tb = 0;
+ m_dec = 0;
+ m_dec_frac = 0;
+ memset(m_fpr, 0, sizeof(m_fpr));
+ m_lt = 0;
+ m_sp = 0;
+ m_tcr = 0;
+ m_ibr = 0;
+ m_esasrr = 0;
+ m_sebr = 0;
+ m_ser = 0;
+
+ memset(&m_spu, 0, sizeof(m_spu));
+ m_pit_reload = 0;
+ m_irqstate = 0;
+ memset(m_buffered_dma_rate, 0, sizeof(m_buffered_dma_rate));
+ m_codexor = 0;
+ m_system_clock = 0;
+ m_cpu_clock = 0;
+ m_tb_zero_cycles = 0;
+ m_dec_zero_cycles = 0;
+
+ m_arg1 = 0;
+ m_fastram_select = 0;
+ memset(m_fastram, 0, sizeof(m_fastram));
+ m_hotspot_select = 0;
+ memset(m_hotspot, 0, sizeof(m_hotspot));
+
+ m_debugger_temp = 0;
+
+ m_cache_line_size = 32;
+ m_cpu_clock = clock();
+ m_program = &space(AS_PROGRAM);
+ m_direct = &m_program->direct();
+ m_system_clock = c_bus_frequency != 0 ? c_bus_frequency : clock();
+ m_dcr_read_func = read32_delegate();
+ m_dcr_write_func = write32_delegate();
+
+ m_tb_divisor = (m_tb_divisor * clock() + m_system_clock / 2 - 1) / m_system_clock;
+ m_codexor = 0;
+ if (!(m_cap & PPCCAP_4XX) && space_config()->m_endianness != ENDIANNESS_NATIVE)
+ m_codexor = 4;
+
+ /* allocate the virtual TLB */
+ m_vtlb = vtlb_alloc(this, AS_PROGRAM, (m_cap & PPCCAP_603_MMU) ? PPC603_FIXED_TLB_ENTRIES : 0, POWERPC_TLB_ENTRIES);
+
+ /* allocate a timer for the compare interrupt */
+ if ((m_cap & PPCCAP_OEA) && (m_tb_divisor))
+ m_decrementer_int_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ppc_device::decrementer_int_callback), this));
+
+ /* and for the 4XX interrupts if needed */
+ if (m_cap & PPCCAP_4XX)
+ {
+ m_fit_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ppc_device::ppc4xx_fit_callback), this));
+ m_pit_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ppc_device::ppc4xx_pit_callback), this));
+ m_spu.timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ppc_device::ppc4xx_spu_callback), this));
+ }
+
+ if (m_cap & PPCCAP_4XX)
+ {
+ m_buffered_dma_timer[0] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ppc_device::ppc4xx_buffered_dma_callback), this));
+ m_buffered_dma_timer[1] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ppc_device::ppc4xx_buffered_dma_callback), this));
+ m_buffered_dma_timer[2] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ppc_device::ppc4xx_buffered_dma_callback), this));
+ m_buffered_dma_timer[3] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ppc_device::ppc4xx_buffered_dma_callback), this));
+
+ m_buffered_dma_rate[0] = 10000;
+ m_buffered_dma_rate[1] = 10000;
+ m_buffered_dma_rate[2] = 10000;
+ m_buffered_dma_rate[3] = 10000;
+ }
+
+ /* register for save states */
+ save_item(NAME(m_core->pc));
+ save_item(NAME(m_core->r));
+ save_item(NAME(m_core->f));
+ save_item(NAME(m_core->cr));
+ save_item(NAME(m_core->xerso));
+ save_item(NAME(m_core->fpscr));
+ save_item(NAME(m_core->msr));
+ save_item(NAME(m_core->sr));
+ save_item(NAME(m_core->spr));
+ save_item(NAME(m_dcr));
+ if (m_cap & PPCCAP_4XX)
+ {
+ save_item(NAME(m_spu.regs));
+ save_item(NAME(m_spu.txbuf));
+ save_item(NAME(m_spu.rxbuf));
+ save_item(NAME(m_spu.rxbuffer));
+ save_item(NAME(m_spu.rxin));
+ save_item(NAME(m_spu.rxout));
+ save_item(NAME(m_pit_reload));
+ save_item(NAME(m_irqstate));
+ }
+ if (m_cap & PPCCAP_603_MMU)
+ {
+ save_item(NAME(m_core->mmu603_cmp));
+ save_item(NAME(m_core->mmu603_hash));
+ save_item(NAME(m_core->mmu603_r));
+ }
+ save_item(NAME(m_core->irq_pending));
+ save_item(NAME(m_tb_zero_cycles));
+ save_item(NAME(m_dec_zero_cycles));
+
+ // Register debugger state
+ state_add(PPC_PC, "PC", m_core->pc).formatstr("%08X");
+ state_add(PPC_MSR, "MSR", m_core->msr).formatstr("%08X");
+ state_add(PPC_CR, "CR", m_debugger_temp).callimport().callexport().formatstr("%08X");
+ state_add(PPC_LR, "LR", m_core->spr[SPR_LR]).formatstr("%08X");
+ state_add(PPC_CTR, "CTR", m_core->spr[SPR_CTR]).formatstr("%08X");
+ state_add(PPC_XER, "XER", m_debugger_temp).callimport().callexport().formatstr("%08X");
+ state_add(PPC_SRR0, "SRR0", m_core->spr[SPROEA_SRR0]).formatstr("%08X");
+ state_add(PPC_SRR1, "SRR1", m_core->spr[SPROEA_SRR1]).formatstr("%08X");
+ state_add(PPC_SPRG0, "SPRG0", m_core->spr[SPROEA_SPRG0]).formatstr("%08X");
+ state_add(PPC_SPRG1, "SPRG1", m_core->spr[SPROEA_SPRG1]).formatstr("%08X");
+ state_add(PPC_SPRG2, "SPRG2", m_core->spr[SPROEA_SPRG2]).formatstr("%08X");
+ state_add(PPC_SPRG3, "SPRG3", m_core->spr[SPROEA_SPRG3]).formatstr("%08X");
+ state_add(PPC_SDR1, "SDR1", m_core->spr[SPROEA_SDR1]).formatstr("%08X");
+ state_add(PPC_EXIER, "EXIER", m_dcr[DCR4XX_EXIER]).formatstr("%08X");
+ state_add(PPC_EXISR, "EXISR", m_dcr[DCR4XX_EXISR]).formatstr("%08X");
+ state_add(PPC_EVPR, "EVPR", m_core->spr[SPR4XX_EVPR]).formatstr("%08X");
+ state_add(PPC_IOCR, "IOCR", m_dcr[DCR4XX_EXISR]).formatstr("%08X");
+ state_add(PPC_TBH, "TBH", m_debugger_temp).callimport().callexport().formatstr("%08X");
+ state_add(PPC_TBL, "TBL", m_debugger_temp).callimport().callexport().formatstr("%08X");
+ state_add(PPC_DEC, "DEC", m_debugger_temp).callimport().callexport().formatstr("%08X");
+
+ state_add(PPC_SR0, "SR0", m_core->sr[0]).formatstr("%08X");
+ state_add(PPC_SR1, "SR1", m_core->sr[1]).formatstr("%08X");
+ state_add(PPC_SR2, "SR2", m_core->sr[2]).formatstr("%08X");
+ state_add(PPC_SR3, "SR3", m_core->sr[3]).formatstr("%08X");
+ state_add(PPC_SR4, "SR4", m_core->sr[4]).formatstr("%08X");
+ state_add(PPC_SR5, "SR5", m_core->sr[5]).formatstr("%08X");
+ state_add(PPC_SR6, "SR6", m_core->sr[6]).formatstr("%08X");
+ state_add(PPC_SR7, "SR7", m_core->sr[7]).formatstr("%08X");
+ state_add(PPC_SR8, "SR8", m_core->sr[8]).formatstr("%08X");
+ state_add(PPC_SR9, "SR9", m_core->sr[9]).formatstr("%08X");
+ state_add(PPC_SR10, "SR10", m_core->sr[10]).formatstr("%08X");
+ state_add(PPC_SR11, "SR11", m_core->sr[11]).formatstr("%08X");
+ state_add(PPC_SR12, "SR12", m_core->sr[12]).formatstr("%08X");
+ state_add(PPC_SR13, "SR13", m_core->sr[13]).formatstr("%08X");
+ state_add(PPC_SR14, "SR14", m_core->sr[14]).formatstr("%08X");
+ state_add(PPC_SR15, "SR15", m_core->sr[15]).formatstr("%08X");
+
+ state_add(PPC_R0, "R0", m_core->r[0]).formatstr("%08X");
+ state_add(PPC_R1, "R1", m_core->r[1]).formatstr("%08X");
+ state_add(PPC_R2, "R2", m_core->r[2]).formatstr("%08X");
+ state_add(PPC_R3, "R3", m_core->r[3]).formatstr("%08X");
+ state_add(PPC_R4, "R4", m_core->r[4]).formatstr("%08X");
+ state_add(PPC_R5, "R5", m_core->r[5]).formatstr("%08X");
+ state_add(PPC_R6, "R6", m_core->r[6]).formatstr("%08X");
+ state_add(PPC_R7, "R7", m_core->r[7]).formatstr("%08X");
+ state_add(PPC_R8, "R8", m_core->r[8]).formatstr("%08X");
+ state_add(PPC_R9, "R9", m_core->r[9]).formatstr("%08X");
+ state_add(PPC_R10, "R10", m_core->r[10]).formatstr("%08X");
+ state_add(PPC_R11, "R11", m_core->r[11]).formatstr("%08X");
+ state_add(PPC_R12, "R12", m_core->r[12]).formatstr("%08X");
+ state_add(PPC_R13, "R13", m_core->r[13]).formatstr("%08X");
+ state_add(PPC_R14, "R14", m_core->r[14]).formatstr("%08X");
+ state_add(PPC_R15, "R15", m_core->r[15]).formatstr("%08X");
+ state_add(PPC_R16, "R16", m_core->r[16]).formatstr("%08X");
+ state_add(PPC_R17, "R17", m_core->r[17]).formatstr("%08X");
+ state_add(PPC_R18, "R18", m_core->r[18]).formatstr("%08X");
+ state_add(PPC_R19, "R19", m_core->r[19]).formatstr("%08X");
+ state_add(PPC_R20, "R20", m_core->r[20]).formatstr("%08X");
+ state_add(PPC_R21, "R21", m_core->r[21]).formatstr("%08X");
+ state_add(PPC_R22, "R22", m_core->r[22]).formatstr("%08X");
+ state_add(PPC_R23, "R23", m_core->r[23]).formatstr("%08X");
+ state_add(PPC_R24, "R24", m_core->r[24]).formatstr("%08X");
+ state_add(PPC_R25, "R25", m_core->r[25]).formatstr("%08X");
+ state_add(PPC_R26, "R26", m_core->r[26]).formatstr("%08X");
+ state_add(PPC_R27, "R27", m_core->r[27]).formatstr("%08X");
+ state_add(PPC_R28, "R28", m_core->r[28]).formatstr("%08X");
+ state_add(PPC_R29, "R29", m_core->r[29]).formatstr("%08X");
+ state_add(PPC_R30, "R30", m_core->r[30]).formatstr("%08X");
+ state_add(PPC_R31, "R31", m_core->r[31]).formatstr("%08X");
+
+ state_add(PPC_F0, "F0", m_core->f[0]).formatstr("%12s");
+ state_add(PPC_F1, "F1", m_core->f[1]).formatstr("%12s");
+ state_add(PPC_F2, "F2", m_core->f[2]).formatstr("%12s");
+ state_add(PPC_F3, "F3", m_core->f[3]).formatstr("%12s");
+ state_add(PPC_F4, "F4", m_core->f[4]).formatstr("%12s");
+ state_add(PPC_F5, "F5", m_core->f[5]).formatstr("%12s");
+ state_add(PPC_F6, "F6", m_core->f[6]).formatstr("%12s");
+ state_add(PPC_F7, "F7", m_core->f[7]).formatstr("%12s");
+ state_add(PPC_F8, "F8", m_core->f[8]).formatstr("%12s");
+ state_add(PPC_F9, "F9", m_core->f[9]).formatstr("%12s");
+ state_add(PPC_F10, "F10", m_core->f[10]).formatstr("%12s");
+ state_add(PPC_F11, "F11", m_core->f[11]).formatstr("%12s");
+ state_add(PPC_F12, "F12", m_core->f[12]).formatstr("%12s");
+ state_add(PPC_F13, "F13", m_core->f[13]).formatstr("%12s");
+ state_add(PPC_F14, "F14", m_core->f[14]).formatstr("%12s");
+ state_add(PPC_F15, "F15", m_core->f[15]).formatstr("%12s");
+ state_add(PPC_F16, "F16", m_core->f[16]).formatstr("%12s");
+ state_add(PPC_F17, "F17", m_core->f[17]).formatstr("%12s");
+ state_add(PPC_F18, "F18", m_core->f[18]).formatstr("%12s");
+ state_add(PPC_F19, "F19", m_core->f[19]).formatstr("%12s");
+ state_add(PPC_F20, "F20", m_core->f[20]).formatstr("%12s");
+ state_add(PPC_F21, "F21", m_core->f[21]).formatstr("%12s");
+ state_add(PPC_F22, "F22", m_core->f[22]).formatstr("%12s");
+ state_add(PPC_F23, "F23", m_core->f[23]).formatstr("%12s");
+ state_add(PPC_F24, "F24", m_core->f[24]).formatstr("%12s");
+ state_add(PPC_F25, "F25", m_core->f[25]).formatstr("%12s");
+ state_add(PPC_F26, "F26", m_core->f[26]).formatstr("%12s");
+ state_add(PPC_F27, "F27", m_core->f[27]).formatstr("%12s");
+ state_add(PPC_F28, "F28", m_core->f[28]).formatstr("%12s");
+ state_add(PPC_F29, "F29", m_core->f[29]).formatstr("%12s");
+ state_add(PPC_F30, "F30", m_core->f[30]).formatstr("%12s");
+ state_add(PPC_F31, "F31", m_core->f[31]).formatstr("%12s");
+ state_add(PPC_FPSCR, "FPSCR", m_core->fpscr).formatstr("%08X");
+
+ state_add(STATE_GENPC, "GENPC", m_core->pc).noshow();
+ state_add(STATE_GENSP, "GENSP", m_core->r[31]).noshow();
+ state_add(STATE_GENFLAGS, "GENFLAGS", m_debugger_temp).noshow().formatstr("%1s");
+
+ m_icountptr = &m_core->icount;
+
+ UINT32 flags = 0;
+ /* initialize the UML generator */
+ m_drcuml = auto_alloc(machine(), drcuml_state(*this, m_cache, flags, 8, 32, 2));
+
+ /* add symbols for our stuff */
+ m_drcuml->symbol_add(&m_core->pc, sizeof(m_core->pc), "pc");
+ m_drcuml->symbol_add(&m_core->icount, sizeof(m_core->icount), "icount");
+ for (int regnum = 0; regnum < 32; regnum++)
+ {
+ char buf[10];
+ sprintf(buf, "r%d", regnum);
+ m_drcuml->symbol_add(&m_core->r[regnum], sizeof(m_core->r[regnum]), buf);
+ sprintf(buf, "fpr%d", regnum);
+ m_drcuml->symbol_add(&m_core->f[regnum], sizeof(m_core->f[regnum]), buf);
+ }
+ for (int regnum = 0; regnum < 8; regnum++)
+ {
+ char buf[10];
+ sprintf(buf, "cr%d", regnum);
+ m_drcuml->symbol_add(&m_core->cr[regnum], sizeof(m_core->cr[regnum]), buf);
+ }
+ m_drcuml->symbol_add(&m_core->xerso, sizeof(m_core->xerso), "xerso");
+ m_drcuml->symbol_add(&m_core->fpscr, sizeof(m_core->fpscr), "fpscr");
+ m_drcuml->symbol_add(&m_core->msr, sizeof(m_core->msr), "msr");
+ m_drcuml->symbol_add(&m_core->sr, sizeof(m_core->sr), "sr");
+ m_drcuml->symbol_add(&m_core->spr[SPR_XER], sizeof(m_core->spr[SPR_XER]), "xer");
+ m_drcuml->symbol_add(&m_core->spr[SPR_LR], sizeof(m_core->spr[SPR_LR]), "lr");
+ m_drcuml->symbol_add(&m_core->spr[SPR_CTR], sizeof(m_core->spr[SPR_CTR]), "ctr");
+ m_drcuml->symbol_add(&m_core->spr, sizeof(m_core->spr), "spr");
+ m_drcuml->symbol_add(&m_dcr, sizeof(m_dcr), "dcr");
+ m_drcuml->symbol_add(&m_core->param0, sizeof(m_core->param0), "param0");
+ m_drcuml->symbol_add(&m_core->param1, sizeof(m_core->param1), "param1");
+ m_drcuml->symbol_add(&m_core->irq_pending, sizeof(m_core->irq_pending), "irq_pending");
+ m_drcuml->symbol_add(&m_core->mode, sizeof(m_core->mode), "mode");
+ m_drcuml->symbol_add(&m_core->arg0, sizeof(m_core->arg0), "arg0");
+ m_drcuml->symbol_add(&m_arg1, sizeof(m_arg1), "arg1");
+ m_drcuml->symbol_add(&m_core->updateaddr, sizeof(m_core->updateaddr), "updateaddr");
+ m_drcuml->symbol_add(&m_core->swcount, sizeof(m_core->swcount), "swcount");
+ m_drcuml->symbol_add(&m_core->tempaddr, sizeof(m_core->tempaddr), "tempaddr");
+ m_drcuml->symbol_add(&m_core->tempdata, sizeof(m_core->tempdata), "tempdata");
+ m_drcuml->symbol_add(&m_core->fp0, sizeof(m_core->fp0), "fp0");
+ m_drcuml->symbol_add(&m_fpmode, sizeof(m_fpmode), "fpmode");
+ m_drcuml->symbol_add(&m_sz_cr_table, sizeof(m_sz_cr_table), "sz_cr_table");
+ m_drcuml->symbol_add(&m_cmp_cr_table, sizeof(m_cmp_cr_table), "cmp_cr_table");
+ m_drcuml->symbol_add(&m_cmpl_cr_table, sizeof(m_cmpl_cr_table), "cmpl_cr_table");
+ m_drcuml->symbol_add(&m_fcmp_cr_table, sizeof(m_fcmp_cr_table), "fcmp_cr_table");
+
+ /* initialize the front-end helper */
+ m_drcfe = auto_alloc(machine(), ppc_frontend(this, COMPILE_BACKWARDS_BYTES, COMPILE_FORWARDS_BYTES, SINGLE_INSTRUCTION_MODE ? 1 : COMPILE_MAX_SEQUENCE));
+
+ /* compute the register parameters */
+ for (int regnum = 0; regnum < 32; regnum++)
+ {
+ m_regmap[regnum] = uml::mem(&m_core->r[regnum]);
+ m_fdregmap[regnum] = uml::mem(&m_core->f[regnum]);
+ }
+
+ /* if we have registers to spare, assign r0, r1, r2 to leftovers */
+ if (!DISABLE_FAST_REGISTERS)
+ {
+ drcbe_info beinfo;
+ m_drcuml->get_backend_info(beinfo);
+ if (beinfo.direct_iregs > 5)
+ m_regmap[0] = uml::I5;
+ if (beinfo.direct_iregs > 6)
+ m_regmap[1] = uml::I6;
+ if (beinfo.direct_iregs > 7)
+ m_regmap[2] = uml::I7;
+ }
+
+ /* mark the cache dirty so it is updated on next execute */
+ m_cache_dirty = TRUE;
+}
+
+void ppc_device::state_export(const device_state_entry &entry)
+{
+ switch (entry.index())
+ {
+ case PPC_CR:
+ m_debugger_temp = get_cr();
+ break;
+
+ case PPC_XER:
+ m_debugger_temp = get_xer();
+ break;
+
+ case PPC_TBH:
+ m_debugger_temp = get_timebase() >> 32;
+ break;
+
+ case PPC_TBL:
+ m_debugger_temp = (UINT32)get_timebase();
+ break;
+
+ case PPC_DEC:
+ m_debugger_temp = get_decrementer();
+ break;
+ }
+}
+
+void ppc_device::state_import(const device_state_entry &entry)
+{
+ switch (entry.index())
+ {
+ case PPC_CR:
+ set_cr(m_debugger_temp);
+ break;
+
+ case PPC_XER:
+ set_xer(m_debugger_temp);
+ break;
+
+ case PPC_TBL:
+ set_timebase((get_timebase() & ~U64(0x00ffffff00000000)) | m_debugger_temp);
+ break;
+
+ case PPC_TBH:
+ set_timebase((get_timebase() & ~U64(0x00000000ffffffff)) | ((UINT64)(m_debugger_temp & 0x00ffffff) << 32));
+ break;
+
+ case PPC_DEC:
+ set_decrementer(m_debugger_temp);
+ break;
+ }
+}
+
+
+void ppc_device::state_string_export(const device_state_entry &entry, std::string &str)
+{
+ switch (entry.index())
+ {
+ case PPC_F0:
+ strprintf(str, "%12f", m_core->f[0]);
+ break;
+
+ case PPC_F1:
+ strprintf(str, "%12f", m_core->f[1]);
+ break;
+
+ case PPC_F2:
+ strprintf(str, "%12f", m_core->f[2]);
+ break;
+
+ case PPC_F3:
+ strprintf(str, "%12f", m_core->f[3]);
+ break;
+
+ case PPC_F4:
+ strprintf(str, "%12f", m_core->f[4]);
+ break;
+
+ case PPC_F5:
+ strprintf(str, "%12f", m_core->f[5]);
+ break;
+
+ case PPC_F6:
+ strprintf(str, "%12f", m_core->f[6]);
+ break;
+
+ case PPC_F7:
+ strprintf(str, "%12f", m_core->f[7]);
+ break;
+
+ case PPC_F8:
+ strprintf(str, "%12f", m_core->f[8]);
+ break;
+
+ case PPC_F9:
+ strprintf(str, "%12f", m_core->f[9]);
+ break;
+
+ case PPC_F10:
+ strprintf(str, "%12f", m_core->f[10]);
+ break;
+
+ case PPC_F11:
+ strprintf(str, "%12f", m_core->f[11]);
+ break;
+
+ case PPC_F12:
+ strprintf(str, "%12f", m_core->f[12]);
+ break;
+
+ case PPC_F13:
+ strprintf(str, "%12f", m_core->f[13]);
+ break;
+
+ case PPC_F14:
+ strprintf(str, "%12f", m_core->f[14]);
+ break;
+
+ case PPC_F15:
+ strprintf(str, "%12f", m_core->f[15]);
+ break;
+
+ case PPC_F16:
+ strprintf(str, "%12f", m_core->f[16]);
+ break;
+
+ case PPC_F17:
+ strprintf(str, "%12f", m_core->f[17]);
+ break;
+
+ case PPC_F18:
+ strprintf(str, "%12f", m_core->f[18]);
+ break;
+
+ case PPC_F19:
+ strprintf(str, "%12f", m_core->f[19]);
+ break;
+
+ case PPC_F20:
+ strprintf(str, "%12f", m_core->f[20]);
+ break;
+
+ case PPC_F21:
+ strprintf(str, "%12f", m_core->f[21]);
+ break;
+
+ case PPC_F22:
+ strprintf(str, "%12f", m_core->f[22]);
+ break;
+
+ case PPC_F23:
+ strprintf(str, "%12f", m_core->f[23]);
+ break;
+
+ case PPC_F24:
+ strprintf(str, "%12f", m_core->f[24]);
+ break;
+
+ case PPC_F25:
+ strprintf(str, "%12f", m_core->f[25]);
+ break;
+
+ case PPC_F26:
+ strprintf(str, "%12f", m_core->f[26]);
+ break;
+
+ case PPC_F27:
+ strprintf(str, "%12f", m_core->f[27]);
+ break;
+
+ case PPC_F28:
+ strprintf(str, "%12f", m_core->f[28]);
+ break;
+
+ case PPC_F29:
+ strprintf(str, "%12f", m_core->f[29]);
+ break;
+
+ case PPC_F30:
+ strprintf(str, "%12f", m_core->f[30]);
+ break;
+
+ case PPC_F31:
+ strprintf(str, "%12f", m_core->f[31]);
+ break;
+ }
+}
+
+
+/*-------------------------------------------------
+ ppccom_exit - common cleanup/exit
+-------------------------------------------------*/
+
+void ppc_device::device_stop()
+{
+ if (m_vtlb != NULL)
+ vtlb_free(m_vtlb);
+ m_vtlb = NULL;
+
+ /* clean up the DRC */
+ auto_free(machine(), m_drcfe);
+ auto_free(machine(), m_drcuml);
+}
+
+
+/*-------------------------------------------------
+ ppccom_reset - reset the state of all the
+ registers
+-------------------------------------------------*/
+
+void ppc_device::device_reset()
+{
+ /* initialize the OEA state */
+ if (m_cap & PPCCAP_OEA)
+ {
+ /* PC to the reset vector; MSR has IP set to start */
+ m_core->pc = 0xfff00100;
+ m_core->msr = MSROEA_IP;
+
+ /* reset the decrementer */
+ m_dec_zero_cycles = total_cycles();
+ if (m_tb_divisor)
+ {
+ decrementer_int_callback(NULL, 0);
+ }
+ }
+
+ /* initialize the 4XX state */
+ if (m_cap & PPCCAP_4XX)
+ {
+ /* PC to the last word; MSR to 0 */
+ m_core->pc = 0xfffffffc;
+ m_core->msr = 0;
+
+ /* reset the SPU status */
+ m_core->spr[SPR4XX_TCR] &= ~PPC4XX_TCR_WRC_MASK;
+ m_spu.regs[SPU4XX_LINE_STATUS] = 0x06;
+ }
+
+ /* initialize the 602 HID0 register */
+ if (m_flavor == PPC_MODEL_602)
+ m_core->spr[SPR603_HID0] = 1;
+
+ /* time base starts here */
+ m_tb_zero_cycles = total_cycles();
+
+ /* clear interrupts */
+ m_core->irq_pending = 0;
+
+ /* flush the TLB */
+ vtlb_flush_dynamic(m_vtlb);
+ if (m_cap & PPCCAP_603_MMU)
+ {
+ for (int tlbindex = 0; tlbindex < PPC603_FIXED_TLB_ENTRIES; tlbindex++)
+ {
+ vtlb_load(m_vtlb, tlbindex, 0, 0, 0);
+ }
+ }
+
+ /* Mark the cache dirty */
+ m_core->mode = 0;
+ m_cache_dirty = TRUE;
+}
+
+
+/*-------------------------------------------------
+ ppccom_dasm - handle disassembly for a
+ CPU
+-------------------------------------------------*/
+
+offs_t ppc_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options)
+{
+ extern offs_t ppc_dasm_one(char *buffer, UINT32 pc, UINT32 op);
+ UINT32 op = *(UINT32 *)oprom;
+ op = BIG_ENDIANIZE_INT32(op);
+ return ppc_dasm_one(buffer, pc, op);
+}
+
+
+/*-------------------------------------------------
+ ppccom_dcstore_callback - call the dcstore
+ callback if installed
+-------------------------------------------------*/
+
+void ppc_device::ppccom_dcstore_callback()
+{
+ if (!m_dcstore_cb.isnull())
+ {
+ m_dcstore_cb(*m_program, m_core->param0, 0, 0xffffffff);
+ }
+}
+
+
+/***************************************************************************
+ TLB HANDLING
+***************************************************************************/
+
+/*-------------------------------------------------
+ ppccom_translate_address_internal - translate
+ an address from logical to physical; shared
+ between external requests and internal TLB
+ filling
+-------------------------------------------------*/
+
+UINT32 ppc_device::ppccom_translate_address_internal(int intention, offs_t &address)
+{
+ int transpriv = ((intention & TRANSLATE_USER_MASK) == 0); // 1 for supervisor, 0 for user
+ int transtype = intention & TRANSLATE_TYPE_MASK;
+ offs_t hash, hashbase, hashmask;
+ int batbase, batnum, hashnum;
+ UINT32 segreg;
+
+ /* 4xx case: "TLB" really just caches writes and checks compare registers */
+ if (m_cap & PPCCAP_4XX)
+ {
+ /* we don't support the MMU of the 403GCX */
+ if (m_flavor == PPC_MODEL_403GCX && (m_core->msr & MSROEA_DR))
+ fatalerror("MMU enabled but not supported!\n");
+
+ /* only check if PE is enabled */
+ if (transtype == TRANSLATE_WRITE && (m_core->msr & MSR4XX_PE))
+ {
+ /* are we within one of the protection ranges? */
+ int inrange1 = ((address >> 12) >= (m_core->spr[SPR4XX_PBL1] >> 12) && (address >> 12) < (m_core->spr[SPR4XX_PBU1] >> 12));
+ int inrange2 = ((address >> 12) >= (m_core->spr[SPR4XX_PBL2] >> 12) && (address >> 12) < (m_core->spr[SPR4XX_PBU2] >> 12));
+
+ /* if PX == 1, writes are only allowed OUTSIDE of the bounds */
+ if (((m_core->msr & MSR4XX_PX) && (inrange1 || inrange2)) || (!(m_core->msr & MSR4XX_PX) && (!inrange1 && !inrange2)))
+ return 0x002;
+ }
+ address &= 0x7fffffff;
+ return 0x001;
+ }
+
+ /* only applies if we support the OEA */
+ if (!(m_cap & PPCCAP_OEA))
+ return 0x001;
+
+ /* also no translation necessary if translation is disabled */
+ if ((transtype == TRANSLATE_FETCH && (m_core->msr & MSROEA_IR) == 0) || (transtype != TRANSLATE_FETCH && (m_core->msr & MSROEA_DR) == 0))
+ return 0x001;
+
+ /* first scan the appropriate BAT */
+ if (m_cap & PPCCAP_601BAT)
+ {
+ for (batnum = 0; batnum < 4; batnum++)
+ {
+ UINT32 upper = m_core->spr[SPROEA_IBAT0U + 2*batnum + 0];
+ UINT32 lower = m_core->spr[SPROEA_IBAT0U + 2*batnum + 1];
+ int privbit = ((intention & TRANSLATE_USER_MASK) == 0) ? 3 : 2;
+
+// printf("bat %d upper = %08x privbit %d\n", batnum, upper, privbit);
+
+ // is this pair valid?
+ if (lower & 0x40)
+ {
+ UINT32 mask = ((lower & 0x3f) << 17) ^ 0xfffe0000;
+ UINT32 addrout;
+ UINT32 key = (upper >> privbit) & 1;
+
+ /* check for a hit against this bucket */
+ if ((address & mask) == (upper & mask))
+ {
+ /* verify protection; if we fail, return false and indicate a protection violation */
+ if (!page_access_allowed(transtype, key, upper & 3))
+ {
+ return DSISR_PROTECTED | ((transtype == TRANSLATE_WRITE) ? DSISR_STORE : 0);
+ }
+
+ /* otherwise we're good */
+ addrout = (lower & mask) | (address & ~mask);
+ address = addrout; // top 9 bits from top 9 of PBN
+ return 0x001;
+ }
+ }
+ }
+ }
+ else
+ {
+ batbase = (transtype == TRANSLATE_FETCH) ? SPROEA_IBAT0U : SPROEA_DBAT0U;
+
+ for (batnum = 0; batnum < 4; batnum++)
+ {
+ UINT32 upper = m_core->spr[batbase + 2*batnum + 0];
+
+ /* check user/supervisor valid bit */
+ if ((upper >> transpriv) & 0x01)
+ {
+ UINT32 mask = (~upper << 15) & 0xfffe0000;
+
+ /* check for a hit against this bucket */
+ if ((address & mask) == (upper & mask))
+ {
+ UINT32 lower = m_core->spr[batbase + 2*batnum + 1];
+
+ /* verify protection; if we fail, return false and indicate a protection violation */
+ if (!page_access_allowed(transtype, 1, lower & 3))
+ {
+ return DSISR_PROTECTED | ((transtype == TRANSLATE_WRITE) ? DSISR_STORE : 0);
+ }
+
+ /* otherwise we're good */
+ address = (lower & mask) | (address & ~mask);
+ return 0x001;
+ }
+ }
+ }
+ }
+
+ /* look up the segment register */
+ segreg = m_core->sr[address >> 28];
+ if (transtype == TRANSLATE_FETCH && (segreg & 0x10000000))
+ return DSISR_PROTECTED | ((transtype == TRANSLATE_WRITE) ? DSISR_STORE : 0);
+
+ /* check for memory-forced I/O */
+ if (m_cap & PPCCAP_MFIOC)
+ {
+ if ((transtype != TRANSLATE_FETCH) && ((segreg & 0x87f00000) == 0x87f00000))
+ {
+ address = ((segreg & 0xf)<<28) | (address & 0x0fffffff);
+ return 1;
+ }
+ else if (segreg & 0x80000000)
+ {
+ fatalerror("PPC: Unhandled segment register %08x with T=1\n", segreg);
+ }
+ }
+
+ /* get hash table information from SD1 */
+ hashbase = m_core->spr[SPROEA_SDR1] & 0xffff0000;
+ hashmask = ((m_core->spr[SPROEA_SDR1] & 0x1ff) << 16) | 0xffff;
+ hash = (segreg & 0x7ffff) ^ ((address >> 12) & 0xffff);
+
+ /* if we're simulating the 603 MMU, fill in the data and stop here */
+ if (m_cap & PPCCAP_603_MMU)
+ {
+ UINT32 entry = vtlb_table(m_vtlb)[address >> 12];
+ m_core->mmu603_cmp = 0x80000000 | ((segreg & 0xffffff) << 7) | (0 << 6) | ((address >> 22) & 0x3f);
+ m_core->mmu603_hash[0] = hashbase | ((hash << 6) & hashmask);
+ m_core->mmu603_hash[1] = hashbase | ((~hash << 6) & hashmask);
+ if ((entry & (VTLB_FLAG_FIXED | VTLB_FLAG_VALID)) == (VTLB_FLAG_FIXED | VTLB_FLAG_VALID))
+ {
+ address = (entry & 0xfffff000) | (address & 0x00000fff);
+ return 0x001;
+ }
+ return DSISR_NOT_FOUND | ((transtype == TRANSLATE_WRITE) ? DSISR_STORE : 0);
+ }
+
+ /* loop twice over hashes */
+ for (hashnum = 0; hashnum < 2; hashnum++)
+ {
+ offs_t ptegaddr = hashbase | ((hash << 6) & hashmask);
+ UINT32 *ptegptr = (UINT32 *)m_program->get_read_ptr(ptegaddr);
+
+ /* should only have valid memory here, but make sure */
+ if (ptegptr != NULL)
+ {
+ UINT32 targetupper = 0x80000000 | ((segreg & 0xffffff) << 7) | (hashnum << 6) | ((address >> 22) & 0x3f);
+ int ptenum;
+
+ /* scan PTEs */
+ for (ptenum = 0; ptenum < 8; ptenum++)
+ if (ptegptr[BYTE_XOR_BE(ptenum * 2)] == targetupper)
+ {
+ UINT32 pteglower = ptegptr[BYTE_XOR_BE(ptenum * 2 + 1)];
+
+ /* verify protection; if we fail, return false and indicate a protection violation */
+ if (!page_access_allowed(transtype, (segreg >> (29 + transpriv)) & 1, pteglower & 3))
+ return DSISR_PROTECTED | ((transtype == TRANSLATE_WRITE) ? DSISR_STORE : 0);
+
+ /* update page table bits */
+ if (!(intention & TRANSLATE_DEBUG_MASK))
+ {
+ pteglower |= 0x100;
+ if (transtype == TRANSLATE_WRITE)
+ pteglower |= 0x080;
+ ptegptr[BYTE_XOR_BE(ptenum * 2 + 1)] = pteglower;
+ }
+
+ /* otherwise we're good */
+ address = (pteglower & 0xfffff000) | (address & 0x00000fff);
+ return (pteglower >> 7) & 1;
+ }
+ }
+
+ /* invert the hash after the first round */
+ hash = ~hash;
+ }
+
+ /* we failed to find any match: not found */
+ return DSISR_NOT_FOUND | ((transtype == TRANSLATE_WRITE) ? DSISR_STORE : 0);
+}
+
+
+/*-------------------------------------------------
+ ppccom_translate_address - translate an address
+ from logical to physical
+-------------------------------------------------*/
+
+bool ppc_device::memory_translate(address_spacenum spacenum, int intention, offs_t &address)
+{
+ /* only applies to the program address space */
+ if (spacenum != AS_PROGRAM)
+ return TRUE;
+
+ /* translation is successful if the internal routine returns 0 or 1 */
+ return (ppccom_translate_address_internal(intention, address) <= 1);
+}
+
+
+/*-------------------------------------------------
+ ppccom_tlb_fill - handle a missing TLB entry
+-------------------------------------------------*/
+
+void ppc_device::ppccom_tlb_fill()
+{
+ vtlb_fill(m_vtlb, m_core->param0, m_core->param1);
+}
+
+
+/*-------------------------------------------------
+ ppccom_tlb_flush - flush the entire TLB,
+ including fixed entries
+-------------------------------------------------*/
+
+void ppc_device::ppccom_tlb_flush()
+{
+ vtlb_flush_dynamic(m_vtlb);
+}
+
+
+
+/***************************************************************************
+ OPCODE HANDLING
+***************************************************************************/
+
+/*-------------------------------------------------
+ ppccom_execute_tlbie - execute a TLBIE
+ instruction
+-------------------------------------------------*/
+
+void ppc_device::ppccom_execute_tlbie()
+{
+ vtlb_flush_address(m_vtlb, m_core->param0);
+}
+
+
+/*-------------------------------------------------
+ ppccom_execute_tlbia - execute a TLBIA
+ instruction
+-------------------------------------------------*/
+
+void ppc_device::ppccom_execute_tlbia()
+{
+ vtlb_flush_dynamic(m_vtlb);
+}
+
+
+/*-------------------------------------------------
+ ppccom_execute_tlbl - execute a TLBLD/TLBLI
+ instruction
+-------------------------------------------------*/
+
+void ppc_device::ppccom_execute_tlbl()
+{
+ UINT32 address = m_core->param0;
+ int isitlb = m_core->param1;
+ vtlb_entry flags = 0;
+ int entrynum;
+
+ /* determine entry number; we use rand() for associativity */
+ entrynum = ((address >> 12) & 0x1f) | (machine().rand() & 0x20) | (isitlb ? 0x40 : 0);
+
+ /* determine the flags */
+ flags = VTLB_FLAG_VALID | VTLB_READ_ALLOWED | VTLB_FETCH_ALLOWED;
+ if (m_core->spr[SPR603_RPA] & 0x80)
+ flags |= VTLB_WRITE_ALLOWED;
+ if (isitlb)
+ flags |= VTLB_FETCH_ALLOWED;
+
+ /* load the entry */
+ vtlb_load(m_vtlb, entrynum, 1, address, (m_core->spr[SPR603_RPA] & 0xfffff000) | flags);
+}
+
+
+/*-------------------------------------------------
+ ppccom_execute_mftb - execute an MFTB
+ instruction
+-------------------------------------------------*/
+
+void ppc_device::ppccom_execute_mftb()
+{
+ switch (m_core->param0)
+ {
+ /* user mode timebase read */
+ case SPRVEA_TBL_R:
+ m_core->param1 = get_timebase();
+ break;
+ case SPRVEA_TBU_R:
+ m_core->param1 = get_timebase() >> 32;
+ break;
+ }
+}
+
+
+/*-------------------------------------------------
+ ppccom_execute_mfspr - execute an MFSPR
+ instruction
+-------------------------------------------------*/
+
+void ppc_device::ppccom_execute_mfspr()
+{
+ /* handle OEA SPRs */
+ if (m_cap & PPCCAP_OEA)
+ {
+ switch (m_core->param0)
+ {
+ /* read-through no-ops */
+ case SPROEA_DSISR:
+ case SPROEA_DAR:
+ case SPROEA_SDR1:
+ case SPROEA_SRR0:
+ case SPROEA_SRR1:
+ case SPROEA_EAR:
+ case SPROEA_IBAT0L:
+ case SPROEA_IBAT0U:
+ case SPROEA_IBAT1L:
+ case SPROEA_IBAT1U:
+ case SPROEA_IBAT2L:
+ case SPROEA_IBAT2U:
+ case SPROEA_IBAT3L:
+ case SPROEA_IBAT3U:
+ case SPROEA_DBAT0L:
+ case SPROEA_DBAT0U:
+ case SPROEA_DBAT1L:
+ case SPROEA_DBAT1U:
+ case SPROEA_DBAT2L:
+ case SPROEA_DBAT2U:
+ case SPROEA_DBAT3L:
+ case SPROEA_DBAT3U:
+ case SPROEA_DABR:
+ m_core->param1 = m_core->spr[m_core->param0];
+ return;
+
+ /* decrementer */
+ case SPROEA_DEC:
+ m_core->param1 = get_decrementer();
+ return;
+ }
+ }
+
+ /* handle 603 SPRs */
+ if (m_cap & PPCCAP_603_MMU)
+ {
+ switch (m_core->param0)
+ {
+ /* read-through no-ops */
+ case SPR603_DMISS:
+ case SPR603_DCMP:
+ case SPR603_HASH1:
+ case SPR603_HASH2:
+ case SPR603_IMISS:
+ case SPR603_ICMP:
+ case SPR603_RPA:
+ case SPR603_HID0:
+ case SPR603_HID1:
+ case SPR603_IABR:
+ case SPR603_HID2:
+ m_core->param1 = m_core->spr[m_core->param0];
+ return;
+
+ /* timebase */
+ case SPR603_TBL_R:
+ m_core->param1 = get_timebase();
+ return;
+ case SPR603_TBU_R:
+ m_core->param1 = (get_timebase() >> 32) & 0xffffff;
+ return;
+ }
+ }
+
+ /* handle 4XX SPRs */
+ if (m_cap & PPCCAP_4XX)
+ {
+ switch (m_core->param0)
+ {
+ /* read-through no-ops */
+ case SPR4XX_EVPR:
+ case SPR4XX_ESR:
+ case SPR4XX_SRR0:
+ case SPR4XX_SRR1:
+ case SPR4XX_SRR2:
+ case SPR4XX_SRR3:
+ case SPR4XX_TCR:
+ case SPR4XX_TSR:
+ case SPR4XX_IAC1:
+ case SPR4XX_IAC2:
+ case SPR4XX_DAC1:
+ case SPR4XX_DAC2:
+ case SPR4XX_DCCR:
+ case SPR4XX_ICCR:
+ case SPR4XX_PBL1:
+ case SPR4XX_PBU1:
+ case SPR4XX_PBL2:
+ case SPR4XX_PBU2:
+ m_core->param1 = m_core->spr[m_core->param0];
+ return;
+
+ /* timebase */
+ case SPR4XX_TBLO:
+ case SPR4XX_TBLU:
+ m_core->param1 = get_timebase();
+ return;
+ case SPR4XX_TBHI:
+ case SPR4XX_TBHU:
+ m_core->param1 = (get_timebase() >> 32) & 0xffffff;
+ return;
+ }
+ }
+
+ /* default handling */
+ osd_printf_debug("SPR %03X read\n", m_core->param0);
+ m_core->param1 = m_core->spr[m_core->param0];
+}
+
+
+/*-------------------------------------------------
+ ppccom_execute_mtspr - execute an MTSPR
+ instruction
+-------------------------------------------------*/
+
+void ppc_device::ppccom_execute_mtspr()
+{
+ /* handle OEA SPRs */
+ if (m_cap & PPCCAP_OEA)
+ {
+ switch (m_core->param0)
+ {
+ /* write-through no-ops */
+ case SPROEA_DSISR:
+ case SPROEA_DAR:
+ case SPROEA_SRR0:
+ case SPROEA_SRR1:
+ case SPROEA_EAR:
+ case SPROEA_DABR:
+ m_core->spr[m_core->param0] = m_core->param1;
+ return;
+
+ /* registers that affect the memory map */
+ case SPROEA_SDR1:
+ case SPROEA_IBAT0L:
+ case SPROEA_IBAT0U:
+ case SPROEA_IBAT1L:
+ case SPROEA_IBAT1U:
+ case SPROEA_IBAT2L:
+ case SPROEA_IBAT2U:
+ case SPROEA_IBAT3L:
+ case SPROEA_IBAT3U:
+ case SPROEA_DBAT0L:
+ case SPROEA_DBAT0U:
+ case SPROEA_DBAT1L:
+ case SPROEA_DBAT1U:
+ case SPROEA_DBAT2L:
+ case SPROEA_DBAT2U:
+ case SPROEA_DBAT3L:
+ case SPROEA_DBAT3U:
+ m_core->spr[m_core->param0] = m_core->param1;
+ ppccom_tlb_flush();
+ return;
+
+ /* decrementer */
+ case SPROEA_DEC:
+ set_decrementer(m_core->param1);
+ return;
+ }
+ }
+
+ /* handle 603 SPRs */
+ if (m_cap & PPCCAP_603_MMU)
+ {
+ switch (m_core->param0)
+ {
+ /* read-only */
+ case SPR603_DMISS:
+ case SPR603_DCMP:
+ case SPR603_HASH1:
+ case SPR603_HASH2:
+ case SPR603_IMISS:
+ case SPR603_ICMP:
+ return;
+
+ /* write-through no-ops */
+ case SPR603_RPA:
+ case SPR603_HID0:
+ case SPR603_HID1:
+ case SPR603_IABR:
+ case SPR603_HID2:
+ m_core->spr[m_core->param0] = m_core->param1;
+ return;
+
+ /* timebase */
+ case SPR603_TBL_W:
+ set_timebase((get_timebase() & ~U64(0xffffffff00000000)) | m_core->param1);
+ return;
+ case SPR603_TBU_W:
+ set_timebase((get_timebase() & ~U64(0x00000000ffffffff)) | ((UINT64)m_core->param1 << 32));
+ return;
+ }
+ }
+
+ /* handle 4XX SPRs */
+ if (m_cap & PPCCAP_4XX)
+ {
+ UINT32 oldval = m_core->spr[m_core->param0];
+ switch (m_core->param0)
+ {
+ /* write-through no-ops */
+ case SPR4XX_EVPR:
+ case SPR4XX_ESR:
+ case SPR4XX_DCCR:
+ case SPR4XX_ICCR:
+ case SPR4XX_SRR0:
+ case SPR4XX_SRR1:
+ case SPR4XX_SRR2:
+ case SPR4XX_SRR3:
+ m_core->spr[m_core->param0] = m_core->param1;
+ return;
+
+ /* registers that affect the memory map */
+ case SPR4XX_PBL1:
+ case SPR4XX_PBU1:
+ case SPR4XX_PBL2:
+ case SPR4XX_PBU2:
+ m_core->spr[m_core->param0] = m_core->param1;
+ ppccom_tlb_flush();
+ return;
+
+ /* timer control register */
+ case SPR4XX_TCR:
+ m_core->spr[SPR4XX_TCR] = m_core->param1 | (oldval & PPC4XX_TCR_WRC_MASK);
+ if ((oldval ^ m_core->spr[SPR4XX_TCR]) & PPC4XX_TCR_FIE)
+ ppc4xx_fit_callback(NULL, FALSE);
+ if ((oldval ^ m_core->spr[SPR4XX_TCR]) & PPC4XX_TCR_PIE)
+ ppc4xx_pit_callback(NULL, FALSE);
+ return;
+
+ /* timer status register */
+ case SPR4XX_TSR:
+ m_core->spr[SPR4XX_TSR] &= ~m_core->param1;
+ ppc4xx_set_irq_line(0, 0);
+ return;
+
+ /* PIT */
+ case SPR4XX_PIT:
+ m_core->spr[SPR4XX_PIT] = m_core->param1;
+ m_pit_reload = m_core->param1;
+ ppc4xx_pit_callback(NULL, FALSE);
+ return;
+
+ /* timebase */
+ case SPR4XX_TBLO:
+ set_timebase((get_timebase() & ~U64(0x00ffffff00000000)) | m_core->param1);
+ return;
+ case SPR4XX_TBHI:
+ set_timebase((get_timebase() & ~U64(0x00000000ffffffff)) | ((UINT64)(m_core->param1 & 0x00ffffff) << 32));
+ return;
+ }
+ }
+
+ /* default handling */
+ osd_printf_debug("SPR %03X write = %08X\n", m_core->param0, m_core->param1);
+ m_core->spr[m_core->param0] = m_core->param1;
+}
+
+
+/*-------------------------------------------------
+ ppccom_execute_mfdcr - execute an MFDCR
+ instruction
+-------------------------------------------------*/
+
+void ppc_device::ppccom_execute_mfdcr()
+{
+ /* handle various DCRs */
+ switch (m_core->param0)
+ {
+ /* read-through no-ops */
+ case DCR4XX_BR0:
+ case DCR4XX_BR1:
+ case DCR4XX_BR2:
+ case DCR4XX_BR3:
+ case DCR4XX_BR4:
+ case DCR4XX_BR5:
+ case DCR4XX_BR6:
+ case DCR4XX_BR7:
+ case DCR4XX_BESR:
+ case DCR4XX_DMASR:
+ case DCR4XX_DMACT0:
+ case DCR4XX_DMADA0:
+ case DCR4XX_DMASA0:
+ case DCR4XX_DMACC0:
+ case DCR4XX_DMACR0:
+ case DCR4XX_DMACT1:
+ case DCR4XX_DMADA1:
+ case DCR4XX_DMASA1:
+ case DCR4XX_DMACC1:
+ case DCR4XX_DMACR1:
+ case DCR4XX_DMACT2:
+ case DCR4XX_DMADA2:
+ case DCR4XX_DMASA2:
+ case DCR4XX_DMACC2:
+ case DCR4XX_DMACR2:
+ case DCR4XX_DMACT3:
+ case DCR4XX_DMADA3:
+ case DCR4XX_DMASA3:
+ case DCR4XX_DMACC3:
+ case DCR4XX_DMACR3:
+ case DCR4XX_EXIER:
+ case DCR4XX_EXISR:
+ case DCR4XX_IOCR:
+ m_core->param1 = m_dcr[m_core->param0];
+ return;
+ }
+
+ /* default handling */
+ if (m_dcr_read_func.isnull()) {
+ osd_printf_debug("DCR %03X read\n", m_core->param0);
+ if (m_core->param0 < ARRAY_LENGTH(m_dcr))
+ m_core->param1 = m_dcr[m_core->param0];
+ else
+ m_core->param1 = 0;
+ } else {
+ m_core->param1 = m_dcr_read_func(*m_program,m_core->param0,0xffffffff);
+ }
+}
+
+
+/*-------------------------------------------------
+ ppccom_execute_mtdcr - execute an MTDCR
+ instruction
+-------------------------------------------------*/
+
+void ppc_device::ppccom_execute_mtdcr()
+{
+ UINT8 oldval;
+
+ /* handle various DCRs */
+ switch (m_core->param0)
+ {
+ /* write-through no-ops */
+ case DCR4XX_BR0:
+ case DCR4XX_BR1:
+ case DCR4XX_BR2:
+ case DCR4XX_BR3:
+ case DCR4XX_BR4:
+ case DCR4XX_BR5:
+ case DCR4XX_BR6:
+ case DCR4XX_BR7:
+ case DCR4XX_BESR:
+ case DCR4XX_DMACT0:
+ case DCR4XX_DMADA0:
+ case DCR4XX_DMASA0:
+ case DCR4XX_DMACC0:
+ case DCR4XX_DMACT1:
+ case DCR4XX_DMADA1:
+ case DCR4XX_DMASA1:
+ case DCR4XX_DMACC1:
+ case DCR4XX_DMACT2:
+ case DCR4XX_DMADA2:
+ case DCR4XX_DMASA2:
+ case DCR4XX_DMACC2:
+ case DCR4XX_DMACT3:
+ case DCR4XX_DMADA3:
+ case DCR4XX_DMASA3:
+ case DCR4XX_DMACC3:
+ m_dcr[m_core->param0] = m_core->param1;
+ return;
+
+ /* DMA status */
+ case DCR4XX_DMASR:
+ m_dcr[DCR4XX_DMASR] &= ~(m_core->param1 & 0xfff80070);
+ ppc4xx_dma_update_irq_states();
+ return;
+
+ /* interrupt enables */
+ case DCR4XX_EXIER:
+ m_dcr[DCR4XX_EXIER] = m_core->param1;
+ ppc4xx_set_irq_line(0, 0);
+ return;
+
+ /* interrupt clear */
+ case DCR4XX_EXISR:
+ m_dcr[m_core->param0] &= ~m_core->param1;
+ ppc4xx_set_irq_line(0, 0);
+ return;
+
+ /* DMA controls */
+ case DCR4XX_DMACR0:
+ case DCR4XX_DMACR1:
+ case DCR4XX_DMACR2:
+ case DCR4XX_DMACR3:
+ m_dcr[m_core->param0] = m_core->param1;
+ if (m_core->param1 & PPC4XX_DMACR_CE)
+ ppc4xx_dma_exec((m_core->param0 - DCR4XX_DMACR0) / 8);
+ ppc4xx_dma_update_irq_states();
+ return;
+
+ /* I/O control */
+ case DCR4XX_IOCR:
+ oldval = m_dcr[m_core->param0];
+ m_dcr[m_core->param0] = m_core->param1;
+ if ((oldval ^ m_core->param1) & 0x02)
+ ppc4xx_spu_timer_reset();
+ return;
+ }
+
+ /* default handling */
+ if (m_dcr_write_func.isnull()) {
+ osd_printf_debug("DCR %03X write = %08X\n", m_core->param0, m_core->param1);
+ if (m_core->param0 < ARRAY_LENGTH(m_dcr))
+ m_dcr[m_core->param0] = m_core->param1;
+ } else {
+ m_dcr_write_func(*m_program,m_core->param0,m_core->param1,0xffffffff);
+ }
+}
+
+
+
+/***************************************************************************
+ FLOATING POINT STATUS FLAGS HANDLING
+***************************************************************************/
+
+/*-------------------------------------------------
+ ppccom_update_fprf - update the FPRF field
+ of the FPSCR register
+-------------------------------------------------*/
+
+void ppc_device::ppccom_update_fprf()
+{
+ UINT32 fprf;
+ double f = m_core->f[m_core->param0];
+
+ if (is_qnan_double(f))
+ {
+ fprf = 0x11;
+ }
+ else if (is_infinity_double(f))
+ {
+ if (sign_double(f)) /* -Infinity */
+ fprf = 0x09;
+ else /* +Infinity */
+ fprf = 0x05;
+ }
+ else if (is_normalized_double(f))
+ {
+ if (sign_double(f)) /* -Normalized */
+ fprf = 0x08;
+ else /* +Normalized */
+ fprf = 0x04;
+ }
+ else if (is_denormalized_double(f))
+ {
+ if (sign_double(f)) /* -Denormalized */
+ fprf = 0x18;
+ else /* +Denormalized */
+ fprf = 0x14;
+ }
+ else
+ {
+ if (sign_double(f)) /* -Zero */
+ fprf = 0x12;
+ else /* +Zero */
+ fprf = 0x02;
+ }
+
+ m_core->fpscr &= ~0x0001f000;
+ m_core->fpscr |= fprf << 12;
+}
+
+
+/***************************************************************************
+ OEA HELPERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ decrementer_int_callback - callback that fires
+ whenever a decrementer interrupt is generated
+-------------------------------------------------*/
+
+TIMER_CALLBACK_MEMBER( ppc_device::decrementer_int_callback )
+{
+ UINT64 cycles_until_next;
+
+ /* set the decrementer IRQ state */
+ m_core->irq_pending |= 0x02;
+
+ /* advance by another full rev */
+ m_dec_zero_cycles += (UINT64)m_tb_divisor << 32;
+ cycles_until_next = m_dec_zero_cycles - total_cycles();
+ m_decrementer_int_timer->adjust(cycles_to_attotime(cycles_until_next));
+}
+
+/*-------------------------------------------------
+ ppc_set_dcstore_callback - installs a callback
+ for detecting datacache stores with dcbst
+-------------------------------------------------*/
+
+void ppc_device::ppc_set_dcstore_callback(write32_delegate callback)
+{
+ m_dcstore_cb = callback;
+}
+
+
+void ppc_device::execute_set_input(int inputnum, int state)
+{
+ switch (inputnum)
+ {
+ case PPC_IRQ:
+ m_core->irq_pending = (m_core->irq_pending & ~1) | ((state != CLEAR_LINE) ? 1 : 0);
+ break;
+ }
+}
+
+
+void ppc4xx_device::execute_set_input(int inputnum, int state)
+{
+ switch (inputnum)
+ {
+ case PPC_IRQ_LINE_0:
+ ppc4xx_set_irq_line(PPC4XX_IRQ_BIT_EXT0, state);
+ break;
+
+ case PPC_IRQ_LINE_1:
+ ppc4xx_set_irq_line(PPC4XX_IRQ_BIT_EXT1, state);
+ break;
+
+ case PPC_IRQ_LINE_2:
+ ppc4xx_set_irq_line(PPC4XX_IRQ_BIT_EXT2, state);
+ break;
+
+ case PPC_IRQ_LINE_3:
+ ppc4xx_set_irq_line(PPC4XX_IRQ_BIT_EXT3, state);
+ break;
+
+ case PPC_IRQ_LINE_4:
+ ppc4xx_set_irq_line(PPC4XX_IRQ_BIT_EXT4, state);
+ break;
+ }
+}
+
+
+/***************************************************************************
+ EMBEDDED 4XX HELPERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ ppc4xx_set_irq_line - PowerPC 4XX-specific
+ IRQ line management
+-------------------------------------------------*/
+
+void ppc_device::ppc4xx_set_irq_line(UINT32 bitmask, int state)
+{
+ UINT32 oldstate = m_irqstate;
+ UINT32 levelmask;
+
+ /* set or clear the appropriate bit */
+ if (state != CLEAR_LINE)
+ m_irqstate |= bitmask;
+ else
+ m_irqstate &= ~bitmask;
+
+ /* if the state changed to on, edge trigger the interrupt */
+ if (((m_irqstate ^ oldstate) & bitmask) && (m_irqstate & bitmask))
+ m_dcr[DCR4XX_EXISR] |= bitmask;
+
+ /* pass through all level-triggered interrupts */
+ levelmask = PPC4XX_IRQ_BIT_CRITICAL | PPC4XX_IRQ_BIT_SPUR | PPC4XX_IRQ_BIT_SPUT;
+ levelmask |= PPC4XX_IRQ_BIT_JTAGR | PPC4XX_IRQ_BIT_JTAGT;
+ levelmask |= PPC4XX_IRQ_BIT_DMA0 | PPC4XX_IRQ_BIT_DMA1 | PPC4XX_IRQ_BIT_DMA2 | PPC4XX_IRQ_BIT_DMA3;
+ if (!(m_dcr[DCR4XX_IOCR] & 0x80000000)) levelmask |= PPC4XX_IRQ_BIT_EXT0;
+ if (!(m_dcr[DCR4XX_IOCR] & 0x20000000)) levelmask |= PPC4XX_IRQ_BIT_EXT1;
+ if (!(m_dcr[DCR4XX_IOCR] & 0x08000000)) levelmask |= PPC4XX_IRQ_BIT_EXT2;
+ if (!(m_dcr[DCR4XX_IOCR] & 0x02000000)) levelmask |= PPC4XX_IRQ_BIT_EXT3;
+ if (!(m_dcr[DCR4XX_IOCR] & 0x00800000)) levelmask |= PPC4XX_IRQ_BIT_EXT4;
+ m_dcr[DCR4XX_EXISR] = (m_dcr[DCR4XX_EXISR] & ~levelmask) | (m_irqstate & levelmask);
+
+ /* update the IRQ status */
+ m_core->irq_pending = ((m_dcr[DCR4XX_EXISR] & m_dcr[DCR4XX_EXIER]) != 0);
+ if ((m_core->spr[SPR4XX_TCR] & PPC4XX_TCR_FIE) && (m_core->spr[SPR4XX_TSR] & PPC4XX_TSR_FIS))
+ m_core->irq_pending = TRUE;
+ if ((m_core->spr[SPR4XX_TCR] & PPC4XX_TCR_PIE) && (m_core->spr[SPR4XX_TSR] & PPC4XX_TSR_PIS))
+ m_core->irq_pending = TRUE;
+}
+
+
+/*-------------------------------------------------
+ ppc4xx_get_irq_line - PowerPC 4XX-specific
+ IRQ line state getter
+-------------------------------------------------*/
+
+int ppc_device::ppc4xx_get_irq_line(UINT32 bitmask)
+{
+ return (m_irqstate & bitmask) ? ASSERT_LINE : CLEAR_LINE;
+}
+
+
+/*-------------------------------------------------
+ ppc4xx_dma_update_irq_states - update the IRQ
+ state for each DMA channel
+-------------------------------------------------*/
+
+void ppc_device::ppc4xx_dma_update_irq_states()
+{
+ /* update the IRQ state for each DMA channel */
+ for (int dmachan = 0; dmachan < 4; dmachan++)
+ {
+ bool irq_pending = false;
+
+ // Channel interrupt enabled?
+ if ((m_dcr[DCR4XX_DMACR0 + 8 * dmachan] & PPC4XX_DMACR_CIE))
+ {
+ // Terminal count and end-of-transfer status bits
+ int bitmask = 0x11 << (27 - dmachan);
+
+ // Chained transfer status bit
+ switch (dmachan)
+ {
+ case 0:
+ bitmask |= 0x00080000;
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ bitmask |= 1 << (7 - dmachan);
+ break;
+ }
+
+ irq_pending = (m_dcr[DCR4XX_DMASR] & bitmask) != 0;
+ }
+
+ ppc4xx_set_irq_line(PPC4XX_IRQ_BIT_DMA(dmachan), irq_pending ? ASSERT_LINE : CLEAR_LINE);
+ }
+}
+
+
+/*-------------------------------------------------
+ ppc4xx_dma_decrement_count - decrement the
+ count on a channel and interrupt if configured
+ to do so
+-------------------------------------------------*/
+
+int ppc_device::ppc4xx_dma_decrement_count(int dmachan)
+{
+ UINT32 *dmaregs = &m_dcr[8 * dmachan];
+
+ /* decrement the counter */
+ dmaregs[DCR4XX_DMACT0]--;
+
+ /* if non-zero, we keep going */
+ if ((dmaregs[DCR4XX_DMACT0] & 0xffff) != 0)
+ return FALSE;
+
+ // if chained mode
+ if (dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_CH)
+ {
+ dmaregs[DCR4XX_DMADA0] = dmaregs[DCR4XX_DMASA0];
+ dmaregs[DCR4XX_DMACT0] = dmaregs[DCR4XX_DMACC0];
+ dmaregs[DCR4XX_DMACR0] &= ~PPC4XX_DMACR_CH;
+
+ switch (dmachan)
+ {
+ case 0:
+ m_dcr[DCR4XX_DMASR] |= 0x00080000;
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ m_dcr[DCR4XX_DMASR] |= 1 << (7 - dmachan);
+ break;
+ }
+
+ ppc4xx_dma_update_irq_states();
+
+ INT64 numdata = dmaregs[DCR4XX_DMACT0];
+ if (numdata == 0)
+ numdata = 65536;
+
+ INT64 time = (numdata * 1000000) / m_buffered_dma_rate[dmachan];
+
+ m_buffered_dma_timer[dmachan]->adjust(attotime::from_usec(time), dmachan);
+ }
+ else
+ {
+ /* set the complete bit and handle interrupts */
+ m_dcr[DCR4XX_DMASR] |= 1 << (31 - dmachan);
+ // m_dcr[DCR4XX_DMASR] |= 1 << (27 - dmachan);
+ ppc4xx_dma_update_irq_states();
+
+ m_buffered_dma_timer[dmachan]->adjust(attotime::never, FALSE);
+ }
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ buffered_dma_callback - callback that fires
+ when buffered DMA transfer is ready
+-------------------------------------------------*/
+
+TIMER_CALLBACK_MEMBER( ppc_device::ppc4xx_buffered_dma_callback )
+{
+ int dmachan = param;
+
+ static const UINT8 dma_transfer_width[4] = { 1, 2, 4, 16 };
+ UINT32 *dmaregs = &m_dcr[8 * dmachan];
+ INT32 destinc;
+ UINT8 width;
+
+ width = dma_transfer_width[(dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_PW_MASK) >> 26];
+ destinc = (dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_DAI) ? width : 0;
+
+ if (dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_TD)
+ {
+ /* peripheral to memory */
+
+ switch (width)
+ {
+ /* byte transfer */
+ case 1:
+ do
+ {
+ UINT8 data = 0;
+ if (!m_ext_dma_read_cb[dmachan].isnull())
+ data = (m_ext_dma_read_cb[dmachan])(*m_program, 1, 0xffffffff);
+ m_program->write_byte(dmaregs[DCR4XX_DMADA0], data);
+ dmaregs[DCR4XX_DMADA0] += destinc;
+ } while (!ppc4xx_dma_decrement_count(dmachan));
+ break;
+
+ /* word transfer */
+ case 2:
+ do
+ {
+ UINT16 data = 0;
+ if (!m_ext_dma_read_cb[dmachan].isnull())
+ data = (m_ext_dma_read_cb[dmachan])(*m_program, 2, 0xffffffff);
+ m_program->write_word(dmaregs[DCR4XX_DMADA0], data);
+ dmaregs[DCR4XX_DMADA0] += destinc;
+ } while (!ppc4xx_dma_decrement_count(dmachan));
+ break;
+
+ /* dword transfer */
+ case 4:
+ do
+ {
+ UINT32 data = 0;
+ if (!m_ext_dma_read_cb[dmachan].isnull())
+ data = (m_ext_dma_read_cb[dmachan])(*m_program, 4, 0xffffffff);
+ m_program->write_dword(dmaregs[DCR4XX_DMADA0], data);
+ dmaregs[DCR4XX_DMADA0] += destinc;
+ } while (!ppc4xx_dma_decrement_count(dmachan));
+ break;
+ }
+ }
+ else
+ {
+ /* memory to peripheral */
+
+ // data is read from destination address!
+ switch (width)
+ {
+ /* byte transfer */
+ case 1:
+ do
+ {
+ UINT8 data = m_program->read_byte(dmaregs[DCR4XX_DMADA0]);
+ if (!m_ext_dma_write_cb[dmachan].isnull())
+ (m_ext_dma_write_cb[dmachan])(*m_program, 1, data, 0xffffffff);
+ dmaregs[DCR4XX_DMADA0] += destinc;
+ } while (!ppc4xx_dma_decrement_count(dmachan));
+ break;
+
+ /* word transfer */
+ case 2:
+ do
+ {
+ UINT16 data = m_program->read_word(dmaregs[DCR4XX_DMADA0]);
+ if (!m_ext_dma_write_cb[dmachan].isnull())
+ (m_ext_dma_write_cb[dmachan])(*m_program, 2, data, 0xffffffff);
+ dmaregs[DCR4XX_DMADA0] += destinc;
+ } while (!ppc4xx_dma_decrement_count(dmachan));
+ break;
+
+ /* dword transfer */
+ case 4:
+ do
+ {
+ UINT32 data = m_program->read_dword(dmaregs[DCR4XX_DMADA0]);
+ if (!m_ext_dma_write_cb[dmachan].isnull())
+ (m_ext_dma_write_cb[dmachan])(*m_program, 4, data, 0xffffffff);
+ dmaregs[DCR4XX_DMADA0] += destinc;
+ } while (!ppc4xx_dma_decrement_count(dmachan));
+ break;
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ ppc4xx_dma_fetch_transmit_byte - fetch a byte
+ to send to a peripheral
+-------------------------------------------------*/
+
+int ppc_device::ppc4xx_dma_fetch_transmit_byte(int dmachan, UINT8 *byte)
+{
+ UINT32 *dmaregs = &m_dcr[8 * dmachan];
+
+ /* if the channel is not enabled, fail */
+ if (!(dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_CE))
+ return FALSE;
+
+ /* if no transfers remaining, fail */
+ if ((dmaregs[DCR4XX_DMACT0] & 0xffff) == 0)
+ return FALSE;
+
+ /* fetch the data */
+ *byte = m_program->read_byte(dmaregs[DCR4XX_DMADA0]++);
+ ppc4xx_dma_decrement_count(dmachan);
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ ppc4xx_dma_handle_receive_byte - receive a byte
+ transmitted by a peripheral
+-------------------------------------------------*/
+
+int ppc_device::ppc4xx_dma_handle_receive_byte(int dmachan, UINT8 byte)
+{
+ UINT32 *dmaregs = &m_dcr[8 * dmachan];
+
+ /* if the channel is not enabled, fail */
+ if (!(dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_CE))
+ return FALSE;
+
+ /* if no transfers remaining, fail */
+ if ((dmaregs[DCR4XX_DMACT0] & 0xffff) == 0)
+ return FALSE;
+
+ /* store the data */
+ m_program->write_byte(dmaregs[DCR4XX_DMADA0]++, byte);
+ ppc4xx_dma_decrement_count(dmachan);
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ ppc4xx_dma_execute - execute a DMA operation
+ if one is pending
+-------------------------------------------------*/
+
+void ppc_device::ppc4xx_dma_exec(int dmachan)
+{
+ static const UINT8 dma_transfer_width[4] = { 1, 2, 4, 16 };
+ UINT32 *dmaregs = &m_dcr[8 * dmachan];
+ INT32 destinc, srcinc;
+ UINT8 width;
+
+ /* skip if not enabled */
+ if (!(dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_CE))
+ return;
+
+ /* check for unsupported features */
+ if (!(dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_TCE))
+ fatalerror("ppc4xx_dma_exec: DMA_TCE == 0\n");
+
+ /* transfer mode */
+ switch ((dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_TM_MASK) >> 21)
+ {
+ /* buffered mode DMA */
+ case 0:
+ if (((dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_PL) >> 28) == 0)
+ {
+ /* buffered DMA with external peripheral */
+
+ INT64 numdata = dmaregs[DCR4XX_DMACT0];
+ if (numdata == 0)
+ numdata = 65536;
+
+ INT64 time;
+ if (numdata > 100)
+ {
+ time = (numdata * 1000000) / m_buffered_dma_rate[dmachan];
+ }
+ else
+ {
+ time = 0; // let very short transfers occur instantly
+ }
+
+ m_buffered_dma_timer[dmachan]->adjust(attotime::from_usec(time), dmachan);
+ }
+ else /* buffered DMA with internal peripheral (SPU) */
+ {
+ /* nothing to do; this happens asynchronously and is driven by the SPU */
+ }
+ break;
+
+ /* fly-by mode DMA */
+ case 1:
+ fatalerror("ppc4xx_dma_exec: fly-by DMA not implemented\n");
+
+ /* software initiated memory-to-memory mode DMA */
+ case 2:
+ width = dma_transfer_width[(dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_PW_MASK) >> 26];
+ srcinc = (dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_SAI) ? width : 0;
+ destinc = (dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_DAI) ? width : 0;
+
+ switch (width)
+ {
+ /* byte transfer */
+ case 1:
+ do
+ {
+ m_program->write_byte(dmaregs[DCR4XX_DMADA0], m_program->read_byte(dmaregs[DCR4XX_DMASA0]));
+ dmaregs[DCR4XX_DMASA0] += srcinc;
+ dmaregs[DCR4XX_DMADA0] += destinc;
+ } while (!ppc4xx_dma_decrement_count(dmachan));
+ break;
+
+ /* word transfer */
+ case 2:
+ do
+ {
+ m_program->write_word(dmaregs[DCR4XX_DMADA0], m_program->read_word(dmaregs[DCR4XX_DMASA0]));
+ dmaregs[DCR4XX_DMASA0] += srcinc;
+ dmaregs[DCR4XX_DMADA0] += destinc;
+ } while (!ppc4xx_dma_decrement_count(dmachan));
+ break;
+
+ /* dword transfer */
+ case 4:
+ do
+ {
+ m_program->write_dword(dmaregs[DCR4XX_DMADA0], m_program->read_dword(dmaregs[DCR4XX_DMASA0]));
+ dmaregs[DCR4XX_DMASA0] += srcinc;
+ dmaregs[DCR4XX_DMADA0] += destinc;
+ } while (!ppc4xx_dma_decrement_count(dmachan));
+ break;
+
+ /* 16-byte transfer */
+ case 16:
+ do
+ {
+ m_program->write_qword(dmaregs[DCR4XX_DMADA0], m_program->read_qword(dmaregs[DCR4XX_DMASA0]));
+ m_program->write_qword(dmaregs[DCR4XX_DMADA0] + 8, m_program->read_qword(dmaregs[DCR4XX_DMASA0] + 8));
+ dmaregs[DCR4XX_DMASA0] += srcinc;
+ dmaregs[DCR4XX_DMADA0] += destinc;
+ } while (!ppc4xx_dma_decrement_count(dmachan));
+ break;
+ }
+ break;
+
+ /* hardware initiated memory-to-memory mode DMA */
+ case 3:
+ fatalerror("ppc4xx_dma_exec: HW mem-to-mem DMA not implemented\n");
+ }
+}
+
+
+/*-------------------------------------------------
+ ppc4xx_fit_callback - FIT timer callback
+-------------------------------------------------*/
+
+TIMER_CALLBACK_MEMBER( ppc_device::ppc4xx_fit_callback )
+{
+ /* if this is a real callback and we are enabled, signal an interrupt */
+ if (param)
+ {
+ m_core->spr[SPR4XX_TSR] |= PPC4XX_TSR_FIS;
+ ppc4xx_set_irq_line(0, 0);
+ }
+
+ /* update ourself for the next interval if we are enabled */
+ if (m_core->spr[SPR4XX_TCR] & PPC4XX_TCR_FIE)
+ {
+ UINT32 timebase = get_timebase();
+ UINT32 interval = 0x200 << (4 * ((m_core->spr[SPR4XX_TCR] & PPC4XX_TCR_FP_MASK) >> 24));
+ UINT32 target = (timebase + interval) & ~(interval - 1);
+ m_fit_timer->adjust(cycles_to_attotime((target + 1 - timebase) / m_tb_divisor), TRUE);
+ }
+
+ /* otherwise, turn ourself off */
+ else
+ m_fit_timer->adjust(attotime::never, FALSE);
+}
+
+
+/*-------------------------------------------------
+ ppc4xx_pit_callback - PIT timer callback
+-------------------------------------------------*/
+
+TIMER_CALLBACK_MEMBER( ppc_device::ppc4xx_pit_callback )
+{
+ /* if this is a real callback and we are enabled, signal an interrupt */
+ if (param)
+ {
+ m_core->spr[SPR4XX_TSR] |= PPC4XX_TSR_PIS;
+ ppc4xx_set_irq_line(0, 0);
+ }
+
+ /* update ourself for the next interval if we are enabled and we are either being
+ forced to update, or we are in auto-reload mode */
+ if ((m_core->spr[SPR4XX_TCR] & PPC4XX_TCR_PIE) && m_pit_reload != 0 && (!param || (m_core->spr[SPR4XX_TCR] & PPC4XX_TCR_ARE)))
+ {
+ UINT32 timebase = get_timebase();
+ UINT32 interval = m_pit_reload;
+ UINT32 target = timebase + interval;
+ m_pit_timer->adjust(cycles_to_attotime((target + 1 - timebase) / m_tb_divisor), TRUE);
+ }
+
+ /* otherwise, turn ourself off */
+ else
+ m_pit_timer->adjust(attotime::never, FALSE);
+}
+
+
+/*-------------------------------------------------
+ ppc4xx_spu_update_irq_states - update the IRQ
+ state for the SPU
+-------------------------------------------------*/
+
+void ppc_device::ppc4xx_spu_update_irq_states()
+{
+ /* check for receive buffer full interrupt */
+ if ((m_spu.regs[SPU4XX_RX_COMMAND] & 0x60) == 0x20 && (m_spu.regs[SPU4XX_LINE_STATUS] & 0x80))
+ ppc4xx_set_irq_line(PPC4XX_IRQ_BIT_SPUR, ASSERT_LINE);
+
+ /* check for receive error interrupt */
+ else if ((m_spu.regs[SPU4XX_RX_COMMAND] & 0x10) && (m_spu.regs[SPU4XX_LINE_STATUS] & 0x78))
+ ppc4xx_set_irq_line(PPC4XX_IRQ_BIT_SPUR, ASSERT_LINE);
+
+ /* clear otherwise */
+ else
+ ppc4xx_set_irq_line(PPC4XX_IRQ_BIT_SPUR, CLEAR_LINE);
+
+ /* check for transmit buffer empty interrupt */
+ if ((m_spu.regs[SPU4XX_TX_COMMAND] & 0x60) == 0x20 && (m_spu.regs[SPU4XX_LINE_STATUS] & 0x04))
+ ppc4xx_set_irq_line(PPC4XX_IRQ_BIT_SPUT, ASSERT_LINE);
+
+ /* check for shift register empty interrupt */
+ else if ((m_spu.regs[SPU4XX_TX_COMMAND] & 0x10) && (m_spu.regs[SPU4XX_LINE_STATUS] & 0x02))
+ ppc4xx_set_irq_line(PPC4XX_IRQ_BIT_SPUT, ASSERT_LINE);
+
+ /* clear otherwise */
+ else
+ ppc4xx_set_irq_line(PPC4XX_IRQ_BIT_SPUT, CLEAR_LINE);
+}
+
+
+/*-------------------------------------------------
+ ppc4xx_spu_rx_data - serial port data receive
+-------------------------------------------------*/
+
+void ppc_device::ppc4xx_spu_rx_data(UINT8 data)
+{
+ UINT32 new_rxin;
+
+ /* fail if we are going to overflow */
+ new_rxin = (m_spu.rxin + 1) % ARRAY_LENGTH(m_spu.rxbuffer);
+ if (new_rxin == m_spu.rxout)
+ fatalerror("ppc4xx_spu_rx_data: buffer overrun!\n");
+
+ /* store the data and accept the new in index */
+ m_spu.rxbuffer[m_spu.rxin] = data;
+ m_spu.rxin = new_rxin;
+}
+
+
+/*-------------------------------------------------
+ ppc4xx_spu_timer_reset - reset and recompute
+ the transmit/receive timer
+-------------------------------------------------*/
+
+void ppc_device::ppc4xx_spu_timer_reset()
+{
+ UINT8 enabled = (m_spu.regs[SPU4XX_RX_COMMAND] | m_spu.regs[SPU4XX_TX_COMMAND]) & 0x80;
+
+ /* if we're enabled, reset at the current baud rate */
+ if (enabled)
+ {
+ attotime clockperiod = attotime::from_hz((m_dcr[DCR4XX_IOCR] & 0x02) ? 3686400 : 33333333);
+ int divisor = ((m_spu.regs[SPU4XX_BAUD_DIVISOR_H] * 256 + m_spu.regs[SPU4XX_BAUD_DIVISOR_L]) & 0xfff) + 1;
+ int bpc = 7 + ((m_spu.regs[SPU4XX_CONTROL] & 8) >> 3) + 1 + (m_spu.regs[SPU4XX_CONTROL] & 1);
+ attotime charperiod = clockperiod * (divisor * 16 * bpc);
+ m_spu.timer->adjust(charperiod, 0, charperiod);
+ if (PRINTF_SPU)
+ printf("ppc4xx_spu_timer_reset: baud rate = %.0f\n", ATTOSECONDS_TO_HZ(charperiod.attoseconds()) * bpc);
+ }
+
+ /* otherwise, disable the timer */
+ else
+ m_spu.timer->adjust(attotime::never);
+}
+
+
+/*-------------------------------------------------
+ ppc4xx_spu_callback - serial port send/receive
+ timer
+-------------------------------------------------*/
+
+TIMER_CALLBACK_MEMBER( ppc_device::ppc4xx_spu_callback )
+{
+ /* transmit enabled? */
+ if (m_spu.regs[SPU4XX_TX_COMMAND] & 0x80)
+ {
+ int operation = (m_spu.regs[SPU4XX_TX_COMMAND] >> 5) & 3;
+
+ /* if we have data to transmit, do it now */
+ if (!(m_spu.regs[SPU4XX_LINE_STATUS] & 0x04))
+ {
+ /* if we have a transmit handler, send it that way */
+ if (!m_spu.tx_cb.isnull())
+ (m_spu.tx_cb)(*m_program, 0, m_spu.txbuf, 0xff);
+
+ /* indicate that we have moved it to the shift register */
+ m_spu.regs[SPU4XX_LINE_STATUS] |= 0x04;
+ m_spu.regs[SPU4XX_LINE_STATUS] &= ~0x02;
+ }
+
+ /* otherwise, clear the shift register */
+ else if (!(m_spu.regs[SPU4XX_LINE_STATUS] & 0x02))
+ m_spu.regs[SPU4XX_LINE_STATUS] |= 0x02;
+
+ /* handle DMA */
+ if (operation >= 2 && ppc4xx_dma_fetch_transmit_byte(operation, &m_spu.txbuf))
+ m_spu.regs[SPU4XX_LINE_STATUS] &= ~0x04;
+ }
+
+ /* receive enabled? */
+ if (m_spu.regs[SPU4XX_RX_COMMAND] & 0x80)
+ if (m_spu.rxout != m_spu.rxin)
+ {
+ int operation = (m_spu.regs[SPU4XX_RX_COMMAND] >> 5) & 3;
+ UINT8 rxbyte;
+
+ /* consume the byte and advance the out pointer */
+ rxbyte = m_spu.rxbuffer[m_spu.rxout];
+ m_spu.rxout = (m_spu.rxout + 1) % ARRAY_LENGTH(m_spu.rxbuffer);
+
+ /* if we're not full, copy data to the buffer and update the line status */
+ if (!(m_spu.regs[SPU4XX_LINE_STATUS] & 0x80))
+ {
+ m_spu.rxbuf = rxbyte;
+ m_spu.regs[SPU4XX_LINE_STATUS] |= 0x80;
+ }
+
+ /* otherwise signal an overrun */
+ else
+ {
+ m_spu.regs[SPU4XX_LINE_STATUS] |= 0x20;
+ goto updateirq;
+ }
+
+ /* handle DMA */
+ if (operation >= 2 && ppc4xx_dma_handle_receive_byte(operation, m_spu.rxbuf))
+ m_spu.regs[SPU4XX_LINE_STATUS] &= ~0x80;
+ }
+
+ /* update the final IRQ states */
+updateirq:
+ ppc4xx_spu_update_irq_states();
+}
+
+
+/*-------------------------------------------------
+ ppc4xx_spu_r - serial port read handler
+-------------------------------------------------*/
+
+READ8_MEMBER( ppc4xx_device::ppc4xx_spu_r )
+{
+ UINT8 result = 0xff;
+
+ switch (offset)
+ {
+ case SPU4XX_BUFFER:
+ result = m_spu.rxbuf;
+ m_spu.regs[SPU4XX_LINE_STATUS] &= ~0x80;
+ break;
+
+ default:
+ if (offset < ARRAY_LENGTH(m_spu.regs))
+ result = m_spu.regs[offset];
+ break;
+ }
+ if (PRINTF_SPU)
+ printf("spu_r(%d) = %02X\n", offset, result);
+ return result;
+}
+
+
+/*-------------------------------------------------
+ ppc4xx_spu_w - serial port write handler
+-------------------------------------------------*/
+
+WRITE8_MEMBER( ppc4xx_device::ppc4xx_spu_w )
+{
+ UINT8 oldstate, newstate;
+
+ if (PRINTF_SPU)
+ printf("spu_w(%d) = %02X\n", offset, data);
+ switch (offset)
+ {
+ /* clear error bits */
+ case SPU4XX_LINE_STATUS:
+ m_spu.regs[SPU4XX_LINE_STATUS] &= ~(data & 0xf8);
+ ppc4xx_spu_update_irq_states();
+ break;
+
+ /* enable/disable the timer if one of these is enabled */
+ case SPU4XX_RX_COMMAND:
+ case SPU4XX_TX_COMMAND:
+ oldstate = m_spu.regs[SPU4XX_RX_COMMAND] | m_spu.regs[SPU4XX_TX_COMMAND];
+ m_spu.regs[offset] = data;
+ newstate = m_spu.regs[SPU4XX_RX_COMMAND] | m_spu.regs[SPU4XX_TX_COMMAND];
+ if ((oldstate ^ newstate) & 0x80)
+ ppc4xx_spu_timer_reset();
+ ppc4xx_spu_update_irq_states();
+ break;
+
+ /* if the divisor changes, we need to update the timer */
+ case SPU4XX_BAUD_DIVISOR_H:
+ case SPU4XX_BAUD_DIVISOR_L:
+ if (data != m_spu.regs[offset])
+ {
+ m_spu.regs[offset] = data;
+ ppc4xx_spu_timer_reset();
+ }
+ break;
+
+ /* if the number of data bits or stop bits changes, we need to update the timer */
+ case SPU4XX_CONTROL:
+ oldstate = m_spu.regs[offset];
+ m_spu.regs[offset] = data;
+ if ((oldstate ^ data) & 0x09)
+ ppc4xx_spu_timer_reset();
+ break;
+
+ case SPU4XX_BUFFER:
+ /* write to the transmit buffer and mark it full */
+ m_spu.txbuf = data;
+ m_spu.regs[SPU4XX_LINE_STATUS] &= ~0x04;
+ break;
+
+ default:
+ if (offset < ARRAY_LENGTH(m_spu.regs))
+ m_spu.regs[offset] = data;
+ break;
+ }
+}
+
+
+
+/*-------------------------------------------------
+ ppc4xx_spu_set_tx_handler - PowerPC 4XX-
+ specific TX handler configuration
+-------------------------------------------------*/
+
+void ppc4xx_device::ppc4xx_spu_set_tx_handler(write8_delegate callback)
+{
+ m_spu.tx_cb = callback;
+}
+
+
+/*-------------------------------------------------
+ ppc4xx_spu_receive_byte - PowerPC 4XX-
+ specific serial byte receive
+-------------------------------------------------*/
+
+void ppc4xx_device::ppc4xx_spu_receive_byte(UINT8 byteval)
+{
+ ppc4xx_spu_rx_data(byteval);
+}
+
+/*-------------------------------------------------
+ ppc4xx_set_dma_read_handler - PowerPC 4XX-
+ specific external DMA read handler configuration
+-------------------------------------------------*/
+
+void ppc4xx_device::ppc4xx_set_dma_read_handler(int channel, read32_delegate callback, int rate)
+{
+ m_ext_dma_read_cb[channel] = callback;
+ m_buffered_dma_rate[channel] = rate;
+}
+
+/*-------------------------------------------------
+ ppc4xx_set_dma_write_handler - PowerPC 4XX-
+ specific external DMA write handler configuration
+-------------------------------------------------*/
+
+void ppc4xx_device::ppc4xx_set_dma_write_handler(int channel, write32_delegate callback, int rate)
+{
+ m_ext_dma_write_cb[channel] = callback;
+ m_buffered_dma_rate[channel] = rate;
+}
+
+/*-------------------------------------------------
+ ppc4xx_set_dcr_read_handler
+-------------------------------------------------*/
+
+void ppc4xx_device::ppc4xx_set_dcr_read_handler(read32_delegate dcr_read_func)
+{
+ m_dcr_read_func = dcr_read_func;
+
+}
+
+/*-------------------------------------------------
+ ppc4xx_set_dcr_write_handler
+-------------------------------------------------*/
+
+void ppc4xx_device::ppc4xx_set_dcr_write_handler(write32_delegate dcr_write_func)
+{
+ m_dcr_write_func = dcr_write_func;
+}