summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author R. Belmont <rb6502@users.noreply.github.com>2017-11-28 21:24:55 -0500
committer GitHub <noreply@github.com>2017-11-28 21:24:55 -0500
commit07ef6eba876df6a2f99f62061142cbd3926a1534 (patch)
tree4199df1bab8a4dbeafa251c11cd947e2a6f0e6ea
parentf9609d1487f578704b2882daea6f92860bf710db (diff)
parent3c36e304ed4f9e9fa555122bbcf712c467bfcb70 (diff)
Merge pull request #2859 from pmackinlay/interpro
interpro: cpu and mmu improvements
-rw-r--r--src/devices/bus/interpro/sr/sr.cpp38
-rw-r--r--src/devices/bus/interpro/sr/sr.h20
-rw-r--r--src/devices/cpu/clipper/clipper.cpp934
-rw-r--r--src/devices/cpu/clipper/clipper.h315
-rw-r--r--src/devices/cpu/clipper/clipperd.h19
-rw-r--r--src/mame/drivers/interpro.cpp94
-rw-r--r--src/mame/includes/interpro.h29
-rw-r--r--src/mame/machine/cammu.cpp548
-rw-r--r--src/mame/machine/cammu.h404
-rw-r--r--src/mame/machine/interpro_ioga.cpp20
-rw-r--r--src/mame/machine/interpro_ioga.h7
11 files changed, 1540 insertions, 888 deletions
diff --git a/src/devices/bus/interpro/sr/sr.cpp b/src/devices/bus/interpro/sr/sr.cpp
index 5a8f7d23a68..112aa582746 100644
--- a/src/devices/bus/interpro/sr/sr.cpp
+++ b/src/devices/bus/interpro/sr/sr.cpp
@@ -266,16 +266,16 @@ sr_slot_device::sr_slot_device(const machine_config &mconfig, const char *tag, d
: device_t(mconfig, SR_SLOT, tag, owner, clock)
, device_slot_interface(mconfig, *this)
, m_sr_tag(nullptr)
- , m_sr_slottag(nullptr)
+ , m_sr_slot_tag(nullptr)
{
}
-void sr_slot_device::static_set_sr_slot(device_t &device, const char *tag, const char *slottag)
+void sr_slot_device::static_set_sr_slot(device_t &device, const char *tag, const char *slot_tag)
{
sr_slot_device &sr_card = dynamic_cast<sr_slot_device &>(device);
sr_card.m_sr_tag = tag;
- sr_card.m_sr_slottag = slottag;
+ sr_card.m_sr_slot_tag = slot_tag;
}
void sr_slot_device::device_start()
@@ -283,28 +283,38 @@ void sr_slot_device::device_start()
device_sr_card_interface *dev = dynamic_cast<device_sr_card_interface *>(get_card_device());
if (dev)
- device_sr_card_interface::static_set_sr_tag(*dev, m_sr_tag, m_sr_slottag);
+ device_sr_card_interface::static_set_sr_tag(*dev, m_sr_tag, m_sr_slot_tag);
}
DEFINE_DEVICE_TYPE(SR, sr_device, "sr", "InterPro SR bus")
sr_device::sr_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: device_t(mconfig, SR, tag, owner, clock)
- , m_data_space(nullptr)
- , m_io_space(nullptr)
, m_out_irq0_cb(*this)
, m_out_irq1_cb(*this)
, m_out_irq2_cb(*this)
+ , m_memory_tag(nullptr)
{
}
+void sr_device::static_set_memory(device_t &device, const char *const tag, const int data_spacenum, const int io_spacenum)
+{
+ sr_device &sr = dynamic_cast<sr_device &>(device);
+
+ sr.m_memory_tag = tag;
+ sr.m_data_spacenum = data_spacenum;
+ sr.m_io_spacenum = io_spacenum;
+}
+
void sr_device::device_start()
{
- // grab the main memory space from the mmu
- device_memory_interface *mmu;
- siblingdevice("mmu")->interface(mmu);
- m_data_space = &mmu->space(0);
- m_io_space = &mmu->space(1);
+ assert_always(m_memory_tag != nullptr, "memory tag and address spaces must be configured");
+
+ // get the memory spaces
+ device_memory_interface *memory;
+ siblingdevice(m_memory_tag)->interface(memory);
+ m_data_space = &memory->space(m_data_spacenum);
+ m_io_space = &memory->space(m_io_spacenum);
// resolve callbacks
m_out_irq0_cb.resolve_safe();
@@ -325,7 +335,7 @@ device_sr_card_interface::device_sr_card_interface(const machine_config &mconfig
: device_slot_card_interface(mconfig, device)
, m_sr(nullptr)
, m_sr_tag(nullptr)
- , m_sr_slottag(nullptr)
+ , m_sr_slot_tag(nullptr)
{
}
@@ -333,12 +343,12 @@ device_sr_card_interface::~device_sr_card_interface()
{
}
-void device_sr_card_interface::static_set_sr_tag(device_t &device, const char *tag, const char *slottag)
+void device_sr_card_interface::static_set_sr_tag(device_t &device, const char *tag, const char *slot_tag)
{
device_sr_card_interface &sr_card = dynamic_cast<device_sr_card_interface &>(device);
sr_card.m_sr_tag = tag;
- sr_card.m_sr_slottag = slottag;
+ sr_card.m_sr_slot_tag = slot_tag;
}
void device_sr_card_interface::set_sr_device()
diff --git a/src/devices/bus/interpro/sr/sr.h b/src/devices/bus/interpro/sr/sr.h
index 236b4ea674b..f51b7f6a946 100644
--- a/src/devices/bus/interpro/sr/sr.h
+++ b/src/devices/bus/interpro/sr/sr.h
@@ -23,6 +23,9 @@
#define MCFG_SR_SLOT_REMOVE(_tag) \
MCFG_DEVICE_REMOVE(_tag)
+#define MCFG_SR_MEMORY(_tag, _data_spacenum, _io_spacenum) \
+ sr_device::static_set_memory(*device, _tag, _data_spacenum, _io_spacenum);
+
class sr_slot_device : public device_t, public device_slot_interface
{
public:
@@ -30,7 +33,8 @@ public:
sr_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
// inline configuration
- static void static_set_sr_slot(device_t &device, const char *tag, const char *slottag);
+ static void static_set_sr_slot(device_t &device, const char *tag, const char *slot_tag);
+
protected:
sr_slot_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock);
@@ -38,7 +42,8 @@ protected:
virtual void device_start() override;
// configuration
- const char *m_sr_tag, *m_sr_slottag;
+ const char *m_sr_tag;
+ const char *m_sr_slot_tag;
};
class device_sr_card_interface;
@@ -54,6 +59,8 @@ public:
template <class Object> static devcb_base &set_out_irq1_callback(device_t &device, Object &&cb) { return downcast<sr_device &>(device).m_out_irq1_cb.set_callback(std::forward<Object>(cb)); }
template <class Object> static devcb_base &set_out_irq2_callback(device_t &device, Object &&cb) { return downcast<sr_device &>(device).m_out_irq2_cb.set_callback(std::forward<Object>(cb)); }
+ static void static_set_memory(device_t &device, const char *const tag, const int data_spacenum, const int io_spacenum);
+
static const u32 SR_BASE = 0x87000000;
static const u32 SR_SIZE = 0x08000000;
static const int SR_COUNT = 16;
@@ -97,6 +104,10 @@ protected:
private:
device_sr_card_interface *m_slot[SR_COUNT];
int m_slot_count;
+
+ const char *m_memory_tag;
+ int m_data_spacenum;
+ int m_io_spacenum;
};
class device_sr_card_interface : public device_slot_card_interface
@@ -110,13 +121,14 @@ public:
void set_sr_device();
// inline configuration
- static void static_set_sr_tag(device_t &device, const char *tag, const char *slottag);
+ static void static_set_sr_tag(device_t &device, const char *tag, const char *slot_tag);
protected:
device_sr_card_interface(const machine_config &mconfig, device_t &device);
sr_device *m_sr;
- const char *m_sr_tag, *m_sr_slottag;
+ const char *m_sr_tag;
+ const char *m_sr_slot_tag;
};
class sr_card_device_base : public device_t, public device_sr_card_interface
diff --git a/src/devices/cpu/clipper/clipper.cpp b/src/devices/cpu/clipper/clipper.cpp
index c2f8c85dfd8..d9812b4a987 100644
--- a/src/devices/cpu/clipper/clipper.cpp
+++ b/src/devices/cpu/clipper/clipper.cpp
@@ -11,7 +11,6 @@
* - unimplemented C400 instructions
* - correct boot logic
* - condition codes for multiply instructions
- * - most cpu traps/faults
* - instruction timing
* - big endian support (not present in the wild)
*/
@@ -22,9 +21,9 @@
#include "clipperd.h"
#define LOG_GENERAL (1U << 0)
-#define LOG_INTERRUPT (1U << 1)
+#define LOG_EXCEPTION (1U << 1)
-//#define VERBOSE (LOG_GENERAL | LOG_INTERRUPT)
+//#define VERBOSE (LOG_GENERAL | LOG_EXCEPTION)
#include "logmacro.h"
@@ -57,25 +56,30 @@ DEFINE_DEVICE_TYPE(CLIPPER_C300, clipper_c300_device, "clipper_c300", "C300 CLIP
DEFINE_DEVICE_TYPE(CLIPPER_C400, clipper_c400_device, "clipper_c400", "C400 CLIPPER")
clipper_c100_device::clipper_c100_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
- : clipper_device(mconfig, CLIPPER_C100, tag, owner, clock, 0) { }
+ : clipper_device(mconfig, CLIPPER_C100, tag, owner, clock, ENDIANNESS_LITTLE, 0)
+{
+}
clipper_c300_device::clipper_c300_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
- : clipper_device(mconfig, CLIPPER_C300, tag, owner, clock, 0) { }
+ : clipper_device(mconfig, CLIPPER_C300, tag, owner, clock, ENDIANNESS_LITTLE, 0)
+{
+}
clipper_c400_device::clipper_c400_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
- : clipper_device(mconfig, CLIPPER_C400, tag, owner, clock, SSW_ID_C400R4) { }
-
-clipper_device::clipper_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, const u32 cpuid)
- : cpu_device(mconfig, type, tag, owner, clock),
- m_pc(0),
- m_psw(0),
- m_ssw(cpuid),
- m_r(m_rs),
- m_insn_config("insn", ENDIANNESS_LITTLE, 32, 32, 0),
- m_data_config("data", ENDIANNESS_LITTLE, 32, 32, 0),
- m_insn(nullptr),
- m_data(nullptr),
- m_icount(0)
+ : clipper_device(mconfig, CLIPPER_C400, tag, owner, clock, ENDIANNESS_LITTLE, SSW_ID_C400R4)
+{
+}
+
+clipper_device::clipper_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, const endianness_t endianness, const u32 cpuid)
+ : cpu_device(mconfig, type, tag, owner, clock)
+ , m_insn_config("insn", endianness, 32, 32, 0)
+ , m_data_config("data", endianness, 32, 32, 0)
+ , m_insn(nullptr)
+ , m_data(nullptr)
+ , m_icount(0)
+ , m_psw(endianness == ENDIANNESS_BIG ? PSW_BIG : 0)
+ , m_ssw(cpuid)
+ , m_r(m_rs)
{
}
@@ -126,53 +130,22 @@ void clipper_device::device_start()
save_item(NAME(m_irq));
save_item(NAME(m_ivec));
- state_add(STATE_GENPC, "GENPC", m_pc).noshow();
- state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow();
+ state_add(STATE_GENPC, "GENPC", m_ip).noshow();
+ state_add(STATE_GENPCBASE, "CURPC", m_ip).noshow();
state_add(STATE_GENSP, "GENSP", m_r[15]).noshow();
state_add(STATE_GENFLAGS, "GENFLAGS", m_psw).mask(0xf).formatstr("%4s").noshow();
- state_add(CLIPPER_PC, "pc", m_pc);
+ state_add(CLIPPER_PC, "pc", m_ip);
state_add(CLIPPER_PSW, "psw", m_psw);
state_add(CLIPPER_SSW, "ssw", m_ssw);
- state_add(CLIPPER_R0, "r0", m_r[0]);
- state_add(CLIPPER_R1, "r1", m_r[1]);
- state_add(CLIPPER_R2, "r2", m_r[2]);
- state_add(CLIPPER_R3, "r3", m_r[3]);
- state_add(CLIPPER_R4, "r4", m_r[4]);
- state_add(CLIPPER_R5, "r5", m_r[5]);
- state_add(CLIPPER_R6, "r6", m_r[6]);
- state_add(CLIPPER_R7, "r7", m_r[7]);
- state_add(CLIPPER_R8, "r8", m_r[8]);
- state_add(CLIPPER_R9, "r9", m_r[9]);
- state_add(CLIPPER_R10, "r10", m_r[10]);
- state_add(CLIPPER_R11, "r11", m_r[11]);
- state_add(CLIPPER_R12, "r12", m_r[12]);
- state_add(CLIPPER_R13, "r13", m_r[13]);
- state_add(CLIPPER_R14, "r14", m_r[14]);
- state_add(CLIPPER_R15, "r15", m_r[15]);
-
- state_add(CLIPPER_F0, "f0", m_f[0]);
- state_add(CLIPPER_F1, "f1", m_f[1]);
- state_add(CLIPPER_F2, "f2", m_f[2]);
- state_add(CLIPPER_F3, "f3", m_f[3]);
- state_add(CLIPPER_F4, "f4", m_f[4]);
- state_add(CLIPPER_F5, "f5", m_f[5]);
- state_add(CLIPPER_F6, "f6", m_f[6]);
- state_add(CLIPPER_F7, "f7", m_f[7]);
-
- // C400 has 8 additional floating point registers
- if (type() == CLIPPER_C400)
- {
- state_add(CLIPPER_F8, "f8", m_f[8]);
- state_add(CLIPPER_F9, "f9", m_f[9]);
- state_add(CLIPPER_F10, "f10", m_f[10]);
- state_add(CLIPPER_F11, "f11", m_f[11]);
- state_add(CLIPPER_F12, "f12", m_f[12]);
- state_add(CLIPPER_F13, "f13", m_f[13]);
- state_add(CLIPPER_F14, "f14", m_f[14]);
- state_add(CLIPPER_F15, "f15", m_f[15]);
- }
+ // integer regsters
+ for (int i = 0; i < get_ireg_count(); i++)
+ state_add(CLIPPER_IREG + i, util::string_format("r%d", i).c_str(), m_r[i]);
+
+ // floating point registers
+ for (int i = 0; i < get_freg_count(); i++)
+ state_add(CLIPPER_FREG + i, util::string_format("f%d", i).c_str(), m_f[i]);
}
void clipper_device::device_reset()
@@ -182,20 +155,19 @@ void clipper_device::device_reset()
* psw: T cleared, BIG set from hardware, others undefined
* ssw: EI, TP, M, U, K, KU, UU, P cleared, ID set from hardware, others undefined
*/
- m_psw = 0;
- float_rounding_mode = float_round_nearest_even;
- // clear the ssw
+ // clear the psw and ssw
+ set_psw(0);
set_ssw(0);
- // select the register file according to the ssw
- m_r = SSW(U) ? m_ru : m_rs;
-
// FIXME: figure out how to branch to the boot code properly
- m_pc = 0x7f100000;
+ m_pc = 0x6000;
+ m_ip = 0x7f100000;
+
m_irq = CLEAR_LINE;
m_nmi = CLEAR_LINE;
m_ivec = 0;
+ m_exception = 0;
}
void clipper_device::state_string_export(const device_state_entry &entry, std::string &str) const
@@ -214,20 +186,18 @@ void clipper_device::state_string_export(const device_state_entry &entry, std::s
void clipper_device::execute_run()
{
- u16 insn;
-
// check for non-maskable and prioritised interrupts
if (m_nmi)
{
// acknowledge non-maskable interrupt
standard_irq_callback(INPUT_LINE_NMI);
- LOGMASKED(LOG_INTERRUPT, "non-maskable interrupt\n");
- m_pc = intrap(EXCEPTION_INTERRUPT_BASE, m_pc);
+ LOGMASKED(LOG_EXCEPTION, "non-maskable interrupt\n");
+ m_ip = intrap(EXCEPTION_INTERRUPT_BASE, m_ip);
}
else if (SSW(EI) && m_irq)
{
- LOGMASKED(LOG_INTERRUPT, "received prioritised interrupt ivec 0x%02x\n", m_ivec);
+ LOGMASKED(LOG_EXCEPTION, "received prioritised interrupt ivec 0x%02x\n", m_ivec);
// allow equal/higher priority interrupts
if ((m_ivec & IVEC_LEVEL) <= SSW(IL))
@@ -235,30 +205,74 @@ void clipper_device::execute_run()
// acknowledge interrupt
standard_irq_callback(INPUT_LINE_IRQ0);
- LOGMASKED(LOG_INTERRUPT, "transferring control to ivec 0x%02x\n", m_ivec);
- m_pc = intrap(EXCEPTION_INTERRUPT_BASE + m_ivec * 8, m_pc);
+ LOGMASKED(LOG_EXCEPTION, "transferring control to ivec 0x%02x\n", m_ivec);
+ m_ip = intrap(EXCEPTION_INTERRUPT_BASE + m_ivec * 8, m_ip);
}
}
- while (m_icount > 0) {
+ while (m_icount > 0)
+ {
+ debugger_instruction_hook(this, m_ip);
- debugger_instruction_hook(this, m_pc);
+ // fetch and decode an instruction
+ if (decode_instruction())
+ {
+ float_exception_flags = 0;
- // fetch instruction word
- insn = m_insn->read_word(m_pc + 0);
+ // execute instruction
+ execute_instruction();
- // decode instruction
- decode_instruction(insn);
+ // check floating point exceptions
+ if (float_exception_flags)
+ fp_exception();
+ }
- // clear floating point exceptions
- float_exception_flags = 0;
+ if (m_exception)
+ {
+ /*
+ * For traced instructions which are interrupted or cause traps, the TP
+ * flag is set by hardware when the interrupt or trap occurs to ensure
+ * that the trace trap will occur immediately after the interrupt or other
+ * trap has been serviced.
+ */
+ // FIXME: don't know why/when the trace pending flag is needed
+ if (PSW(T))
+ m_ssw |= SSW_TP;
+
+ switch (m_exception)
+ {
+ // data memory trap group
+ case EXCEPTION_D_CORRECTED_MEMORY_ERROR:
+ case EXCEPTION_D_UNCORRECTABLE_MEMORY_ERROR:
+ case EXCEPTION_D_ALIGNMENT_FAULT:
+ case EXCEPTION_D_PAGE_FAULT:
+ case EXCEPTION_D_READ_PROTECT_FAULT:
+ case EXCEPTION_D_WRITE_PROTECT_FAULT:
+
+ // instruction memory trap group
+ case EXCEPTION_I_CORRECTED_MEMORY_ERROR:
+ case EXCEPTION_I_UNCORRECTABLE_MEMORY_ERROR:
+ case EXCEPTION_I_ALIGNMENT_FAULT:
+ case EXCEPTION_I_PAGE_FAULT:
+ case EXCEPTION_I_EXECUTE_PROTECT_FAULT:
+
+ // illegal operation trap group
+ case EXCEPTION_ILLEGAL_OPERATION:
+ case EXCEPTION_PRIVILEGED_INSTRUCTION:
+ // return address is faulting instruction
+ m_ip = intrap(m_exception, m_pc);
+ break;
- // execute instruction, return next pc
- m_pc = execute_instruction();
+ default:
+ // return address is following instruction
+ m_ip = intrap(m_exception, m_ip);
+ break;
+ }
+ }
- // handle floating point exceptions
- if (float_exception_flags)
- fp_exception();
+ // FIXME: trace trap logic not working properly yet
+ //else if (PSW(T))
+ // m_ip = intrap(EXCEPTION_TRACE, m_ip);
// FIXME: some instructions take longer (significantly) than one cycle
// and also the timings are often slower for the C100 and C300
@@ -292,13 +306,29 @@ device_memory_interface::space_config_vector clipper_device::memory_space_config
};
}
+WRITE16_MEMBER(clipper_device::set_exception)
+{
+ LOGMASKED(LOG_EXCEPTION, "external exception 0x%04x triggered\n", data);
+
+ // check if corrected memory errors are masked
+ if (!SSW(ECM) && (data == EXCEPTION_D_CORRECTED_MEMORY_ERROR || data == EXCEPTION_I_CORRECTED_MEMORY_ERROR))
+ return;
+
+ m_exception = data;
+}
+
/*
- * This function decodes instruction operands and computes effective addresses (for
+ * Fetch and decode an instruction and compute an effective address (for
* instructions with addressing modes). The results are contained in the m_info
* structure to simplify passing between here and execute_instruction().
*/
-void clipper_device::decode_instruction (u16 insn)
+bool clipper_device::decode_instruction()
{
+ // fetch instruction word
+ u16 insn = m_insn->read_word(m_ip + 0);
+ if (m_exception)
+ return false;
+
// decode the primary parcel
m_info.opcode = insn >> 8;
m_info.subopcode = insn & 0xff;
@@ -308,16 +338,20 @@ void clipper_device::decode_instruction (u16 insn)
// initialise the other fields
m_info.imm = 0;
m_info.macro = 0;
- m_info.size = 0;
m_info.address = 0;
+ // default instruction size is 2 bytes
+ int size = 2;
+
if ((insn & 0xf800) == 0x3800)
{
// instruction has a 16 bit immediate operand
// fetch 16 bit immediate and sign extend
- m_info.imm = (s16)m_insn->read_word(m_pc + 2);
- m_info.size = 4;
+ m_info.imm = (s16)m_insn->read_word(m_ip + 2);
+ if (m_exception)
+ return false;
+ size = 4;
}
else if ((insn & 0xd300) == 0x8300)
{
@@ -325,14 +359,18 @@ void clipper_device::decode_instruction (u16 insn)
if (insn & 0x0080)
{
// fetch 16 bit immediate and sign extend
- m_info.imm = (s16)m_insn->read_word(m_pc + 2);
- m_info.size = 4;
+ m_info.imm = (s16)m_insn->read_word(m_ip + 2);
+ if (m_exception)
+ return false;
+ size = 4;
}
else
{
// fetch 32 bit immediate
- m_info.imm = m_insn->read_dword_unaligned(m_pc + 2);
- m_info.size = 6;
+ m_info.imm = m_insn->read_dword_unaligned(m_ip + 2);
+ if (m_exception)
+ return false;
+ size = 6;
}
}
else if ((insn & 0xc000) == 0x4000)
@@ -346,87 +384,103 @@ void clipper_device::decode_instruction (u16 insn)
switch (insn & 0x00f0)
{
case ADDR_MODE_PC32:
- m_info.address = m_pc + m_insn->read_dword_unaligned(m_pc + 2);
- m_info.size = 6;
+ m_info.address = m_ip + m_insn->read_dword_unaligned(m_ip + 2);
+ if (m_exception)
+ return false;
+ size = 6;
break;
case ADDR_MODE_ABS32:
- m_info.address = m_insn->read_dword_unaligned(m_pc + 2);
- m_info.size = 6;
+ m_info.address = m_insn->read_dword_unaligned(m_ip + 2);
+ if (m_exception)
+ return false;
+ size = 6;
break;
case ADDR_MODE_REL32:
- m_info.r2 = m_insn->read_word(m_pc + 2) & 0xf;
- m_info.address = m_r[insn & 0xf] + m_insn->read_dword_unaligned(m_pc + 4);
- m_info.size = 8;
+ m_info.r2 = m_insn->read_word(m_ip + 2) & 0xf;
+ if (m_exception)
+ return false;
+
+ m_info.address = m_r[insn & 0xf] + m_insn->read_dword_unaligned(m_ip + 4);
+ if (m_exception)
+ return false;
+ size = 8;
break;
case ADDR_MODE_PC16:
- m_info.address = m_pc + (s16)m_insn->read_word(m_pc + 2);
- m_info.size = 4;
+ m_info.address = m_ip + (s16)m_insn->read_word(m_ip + 2);
+ if (m_exception)
+ return false;
+ size = 4;
break;
case ADDR_MODE_REL12:
- temp = m_insn->read_word(m_pc + 2);
+ temp = m_insn->read_word(m_ip + 2);
+ if (m_exception)
+ return false;
m_info.r2 = temp & 0xf;
m_info.address = m_r[insn & 0xf] + ((s16)temp >> 4);
- m_info.size = 4;
+ size = 4;
break;
case ADDR_MODE_ABS16:
- m_info.address = (s16)m_insn->read_word(m_pc + 2);
- m_info.size = 4;
+ m_info.address = (s16)m_insn->read_word(m_ip + 2);
+ if (m_exception)
+ return false;
+ size = 4;
break;
case ADDR_MODE_PCX:
- temp = m_insn->read_word(m_pc + 2);
+ temp = m_insn->read_word(m_ip + 2);
+ if (m_exception)
+ return false;
m_info.r2 = temp & 0xf;
- m_info.address = m_pc + m_r[(temp >> 4) & 0xf];
- m_info.size = 4;
+ m_info.address = m_ip + m_r[(temp >> 4) & 0xf];
+ size = 4;
break;
case ADDR_MODE_RELX:
- temp = m_insn->read_word(m_pc + 2);
+ temp = m_insn->read_word(m_ip + 2);
+ if (m_exception)
+ return false;
m_info.r2 = temp & 0xf;
m_info.address = m_r[insn & 0xf] + m_r[(temp >> 4) & 0xf];
- m_info.size = 4;
+ size = 4;
break;
default:
- LOG("illegal addressing mode pc 0x%08x\n", m_pc);
- machine().debug_break();
- break;
+ m_exception = EXCEPTION_ILLEGAL_OPERATION;
+ return false;
}
}
else
- {
// relative addressing mode
m_info.address = m_r[m_info.r1];
- m_info.size = 2;
- }
}
else if ((insn & 0xfd00) == 0xb400)
{
// macro instructions
- m_info.macro = m_insn->read_word(m_pc + 2);
- m_info.size = 4;
+ m_info.macro = m_insn->read_word(m_ip + 2);
+ if (m_exception)
+ return false;
+ size = 4;
}
- else
- // all other instruction formats are 16 bits
- m_info.size = 2;
+
+ // instruction fetch and decode complete
+ m_pc = m_ip;
+ m_ip = m_pc + size;
+
+ return true;
}
-int clipper_device::execute_instruction ()
+void clipper_device::execute_instruction()
{
- // the address of the next instruction
- u32 next_pc;
-
- // next instruction follows the current one by default, but
- // may be changed for branch, call or trap instructions
- next_pc = m_pc + m_info.size;
+ // memory fetch temporary
+ u64 temp;
switch (m_info.opcode)
{
@@ -438,15 +492,9 @@ int clipper_device::execute_instruction ()
// treated as a noop if target ssw in user mode
// R1 == 3 means "fast" mode - avoids pipeline flush
if (R1 == 0)
- {
- m_psw = m_r[R2];
- fp_rounding();
- }
+ set_psw(m_r[R2]);
else if (!SSW(U) && (R1 == 1 || R1 == 3))
- {
set_ssw(m_r[R2]);
- m_r = SSW(U) ? m_ru : m_rs;
- }
// FLAGS: CVZN
break;
case 0x11:
@@ -459,12 +507,16 @@ int clipper_device::execute_instruction ()
break;
case 0x12:
// calls: call supervisor
- next_pc = intrap(EXCEPTION_SUPERVISOR_CALL_BASE + (m_info.subopcode & 0x7f) * 8, next_pc);
+ m_exception = EXCEPTION_SUPERVISOR_CALL_BASE + (m_info.subopcode & 0x7f) * 8;
break;
case 0x13:
// ret: return from subroutine
- next_pc = m_data->read_dword(m_r[R2]);
- m_r[R2] += 4;
+ temp = m_data->read_dword(m_r[R2]);
+ if (!m_exception)
+ {
+ m_ip = temp;
+ m_r[R2] += 4;
+ }
// TRAPS: C,U,A,P,R
break;
case 0x14:
@@ -477,79 +529,75 @@ int clipper_device::execute_instruction ()
case 0x16:
// popw: pop word
m_r[R1] += 4;
- m_r[R2] = m_data->read_dword(m_r[R1] - 4);
+ temp = m_data->read_dword(m_r[R1] - 4);
+ if (!m_exception)
+ m_r[R2] = temp;
// TRAPS: C,U,A,P,R
break;
case 0x20:
// adds: add single floating
- set_fp32(R2, float32_add(get_fp32(R2), get_fp32(R1)));
+ set_fp(R2, float32_add(get_fp32(R2), get_fp32(R1)), F_IVUX);
// TRAPS: F_IVUX
- float_exception_flags &= (float_flag_invalid | float_flag_overflow | float_flag_underflow | float_flag_inexact);
break;
case 0x21:
// subs: subtract single floating
- set_fp32(R2, float32_sub(get_fp32(R2), get_fp32(R1)));
+ set_fp(R2, float32_sub(get_fp32(R2), get_fp32(R1)), F_IVUX);
// TRAPS: F_IVUX
- float_exception_flags &= (float_flag_invalid | float_flag_overflow | float_flag_underflow | float_flag_inexact);
break;
case 0x22:
// addd: add double floating
- set_fp64(R2, float64_add(get_fp64(R2), get_fp64(R1)));
+ set_fp(R2, float64_add(get_fp64(R2), get_fp64(R1)), F_IVUX);
// TRAPS: F_IVUX
- float_exception_flags &= (float_flag_invalid | float_flag_overflow | float_flag_underflow | float_flag_inexact);
break;
case 0x23:
// subd: subtract double floating
- set_fp64(R2, float64_sub(get_fp64(R2), get_fp64(R1)));
+ set_fp(R2, float64_sub(get_fp64(R2), get_fp64(R1)), F_IVUX);
// TRAPS: F_IVUX
- float_exception_flags &= (float_flag_invalid | float_flag_overflow | float_flag_underflow | float_flag_inexact);
break;
case 0x24:
// movs: move single floating
- set_fp32(R2, get_fp32(R1));
+ set_fp(R2, get_fp32(R1), F_NONE);
break;
case 0x25:
// cmps: compare single floating
FLAGS(0, 0, float32_eq(get_fp32(R2), get_fp32(R1)), float32_lt(get_fp32(R2), get_fp32(R1)))
+ // flag unordered
if (float_exception_flags & float_flag_invalid)
m_psw |= PSW_Z | PSW_N;
- float_exception_flags &= (0);
+ float_exception_flags &= F_NONE;
break;
case 0x26:
// movd: move double floating
- set_fp64(R2, get_fp64(R1));
+ set_fp(R2, get_fp64(R1), F_NONE);
break;
case 0x27:
// cmpd: compare double floating
FLAGS(0, 0, float64_eq(get_fp64(R2), get_fp64(R1)), float64_lt(get_fp64(R2), get_fp64(R1)))
+ // flag unordered
if (float_exception_flags & float_flag_invalid)
m_psw |= PSW_Z | PSW_N;
- float_exception_flags &= (0);
+ float_exception_flags &= F_NONE;
break;
case 0x28:
// muls: multiply single floating
- set_fp32(R2, float32_mul(get_fp32(R2), get_fp32(R1)));
+ set_fp(R2, float32_mul(get_fp32(R2), get_fp32(R1)), F_IVUX);
// TRAPS: F_IVUX
- float_exception_flags &= (float_flag_invalid | float_flag_overflow | float_flag_underflow | float_flag_inexact);
break;
case 0x29:
// divs: divide single floating
- set_fp32(R2, float32_div(get_fp32(R2), get_fp32(R1)));
+ set_fp(R2, float32_div(get_fp32(R2), get_fp32(R1)), F_IVDUX);
// TRAPS: F_IVDUX
- float_exception_flags &= (float_flag_invalid | float_flag_overflow | float_flag_divbyzero | float_flag_underflow | float_flag_inexact);
break;
case 0x2a:
// muld: multiply double floating
- set_fp64(R2, float64_mul(get_fp64(R2), get_fp64(R1)));
+ set_fp(R2, float64_mul(get_fp64(R2), get_fp64(R1)), F_IVUX);
// TRAPS: F_IVUX
- float_exception_flags &= (float_flag_invalid | float_flag_overflow | float_flag_underflow | float_flag_inexact);
break;
case 0x2b:
// divd: divide double floating
- set_fp64(R2, float64_div(get_fp64(R2), get_fp64(R1)));
+ set_fp(R2, float64_div(get_fp64(R2), get_fp64(R1)), F_IVDUX);
// TRAPS: F_IVDUX
- float_exception_flags &= (float_flag_invalid | float_flag_overflow | float_flag_divbyzero | float_flag_underflow | float_flag_inexact);
break;
case 0x2c:
// movsw: move single floating to word
@@ -557,7 +605,7 @@ int clipper_device::execute_instruction ()
break;
case 0x2d:
// movws: move word to single floating
- set_fp32(R2, m_r[R1]);
+ set_fp(R2, m_r[R1], F_NONE);
break;
case 0x2e:
// movdl: move double floating to longword
@@ -565,7 +613,7 @@ int clipper_device::execute_instruction ()
break;
case 0x2f:
// movld: move longword to double floating
- set_fp64(R2, ((u64 *)m_r)[R1 >> 1]);
+ set_fp(R2, ((u64 *)m_r)[R1 >> 1], F_NONE);
break;
case 0x30:
// shaw: shift arithmetic word
@@ -726,53 +774,53 @@ int clipper_device::execute_instruction ()
case 0x44:
case 0x45:
// call: call subroutine
- m_r[R2] -= 4;
- m_data->write_dword(m_r[R2], next_pc);
- next_pc = m_info.address;
+ m_data->write_dword(m_r[R2] - 4, m_ip);
+ if (!m_exception)
+ {
+ m_ip = m_info.address;
+ m_r[R2] -= 4;
+ }
// TRAPS: A,P,W
break;
-#ifdef UNIMPLEMENTED_C400
- case 0x46:
- case 0x47:
- // loadd2:
- break;
-#endif
+
case 0x48:
case 0x49:
// b*: branch on condition
if (evaluate_branch())
- next_pc = m_info.address;
+ m_ip = m_info.address;
// TRAPS: A,I
break;
-#ifdef UNIMPLEMENTED_C400
- case 0x4a:
- case 0x4b:
- // cdb:
- break;
- case 0x4c:
- case 0x4d:
- // cdbeq:
- break;
- case 0x4e:
- case 0x4f:
- // cdbne:
- break;
- case 0x50:
- case 0x51:
- // db*:
- break;
-#endif
-#ifdef UNIMPLEMENTED
+
case 0x4c:
case 0x4d:
- // bf*:
+ // bf*: branch on floating exception
+ // FIXME: documentation is not clear, implementation is guesswork
+ switch (R2)
+ {
+ case BF_ANY:
+ // bfany: floating any exception
+ if (m_psw & (PSW_FI | PSW_FV | PSW_FD | PSW_FU | PSW_FX))
+ m_ip = m_info.address;
+ break;
+ case BF_BAD:
+ // bfbad: floating bad result
+ if (m_psw & (PSW_FI | PSW_FD))
+ m_ip = m_info.address;
+ break;
+ default:
+ // reserved
+ // FIXME: not sure if this should trigger an exception?
+ m_exception = EXCEPTION_ILLEGAL_OPERATION;
+ break;
+ }
break;
-#endif
case 0x60:
case 0x61:
// loadw: load word
- m_r[R2] = m_data->read_dword(m_info.address);
+ temp = m_data->read_dword(m_info.address);
+ if (!m_exception)
+ m_r[R2] = temp;
// TRAPS: C,U,A,P,R,I
break;
case 0x62:
@@ -784,37 +832,49 @@ int clipper_device::execute_instruction ()
case 0x64:
case 0x65:
// loads: load single floating
- set_fp32(R2, m_data->read_dword(m_info.address));
+ temp = m_data->read_dword(m_info.address);
+ if (!m_exception)
+ set_fp(R2, (float32)temp, F_NONE);
// TRAPS: C,U,A,P,R,I
break;
case 0x66:
case 0x67:
// loadd: load double floating
- set_fp64(R2, m_data->read_qword(m_info.address));
+ temp = m_data->read_qword(m_info.address);
+ if (!m_exception)
+ set_fp(R2, (float64)temp, F_NONE);
// TRAPS: C,U,A,P,R,I
break;
case 0x68:
case 0x69:
// loadb: load byte
- m_r[R2] = (s8)m_data->read_byte(m_info.address);
+ temp = s64(s8(m_data->read_byte(m_info.address)));
+ if (!m_exception)
+ m_r[R2] = temp;
// TRAPS: C,U,A,P,R,I
break;
case 0x6a:
case 0x6b:
// loadbu: load byte unsigned
- m_r[R2] = m_data->read_byte(m_info.address);
+ temp = m_data->read_byte(m_info.address);
+ if (!m_exception)
+ m_r[R2] = temp;
// TRAPS: C,U,A,P,R,I
break;
case 0x6c:
case 0x6d:
// loadh: load halfword
- m_r[R2] = (s16)m_data->read_word(m_info.address);
+ temp = s64(s16(m_data->read_word(m_info.address)));
+ if (!m_exception)
+ m_r[R2] = temp;
// TRAPS: C,U,A,P,R,I
break;
case 0x6e:
case 0x6f:
// loadhu: load halfword unsigned
- m_r[R2] = m_data->read_word(m_info.address);
+ temp = m_data->read_word(m_info.address);
+ if (!m_exception)
+ m_r[R2] = temp;
// TRAPS: C,U,A,P,R,I
break;
case 0x70:
@@ -826,8 +886,13 @@ int clipper_device::execute_instruction ()
case 0x72:
case 0x73:
// tsts: test and set
- m_r[R2] = m_data->read_dword(m_info.address);
- m_data->write_dword(m_info.address, m_r[R2] | 0x80000000);
+ temp = m_data->read_dword(m_info.address);
+ if (!m_exception)
+ {
+ m_data->write_dword(m_info.address, temp | 0x80000000U);
+ if (!m_exception)
+ m_r[R2] = temp;
+ }
// TRAPS: C,U,A,P,R,W,I
break;
case 0x74:
@@ -845,14 +910,14 @@ int clipper_device::execute_instruction ()
case 0x78:
case 0x79:
// storb: store byte
- m_data->write_byte(m_info.address, (u8)m_r[R2]);
+ m_data->write_byte(m_info.address, m_r[R2]);
// TRAPS: A,P,W,I
break;
case 0x7c:
case 0x7d:
// storh: store halfword
- m_data->write_word(m_info.address, (u16)m_r[R2]);
+ m_data->write_word(m_info.address, m_r[R2]);
// TRAPS: A,P,W,I
break;
@@ -974,45 +1039,49 @@ int clipper_device::execute_instruction ()
// divw: divide word
if (m_r[R1] != 0)
{
+ // FLAGS: 0V00
FLAGS(0, m_r[R2] == INT32_MIN && m_r[R1] == -1, 0, 0)
m_r[R2] = (s32)m_r[R2] / (s32)m_r[R1];
}
else
- next_pc = intrap(EXCEPTION_INTEGER_DIVIDE_BY_ZERO, next_pc, CTS_DIVIDE_BY_ZERO);
- // FLAGS: 0V00
- // TRAPS: D
+ // TRAPS: D
+ m_exception = EXCEPTION_INTEGER_DIVIDE_BY_ZERO;
break;
case 0x9d:
// modw: modulus word
if (m_r[R1] != 0)
{
+ // FLAGS: 0V00
FLAGS(0, m_r[R2] == INT32_MIN && m_r[R1] == -1, 0, 0)
m_r[R2] = (s32)m_r[R2] % (s32)m_r[R1];
}
else
- next_pc = intrap(EXCEPTION_INTEGER_DIVIDE_BY_ZERO, next_pc, CTS_DIVIDE_BY_ZERO);
- // FLAGS: 0V00
- // TRAPS: D
+ // TRAPS: D
+ m_exception = EXCEPTION_INTEGER_DIVIDE_BY_ZERO;
break;
case 0x9e:
// divwu: divide word unsigned
if (m_r[R1] != 0)
+ {
m_r[R2] = m_r[R2] / m_r[R1];
+ // FLAGS: 0000
+ FLAGS(0, 0, 0, 0)
+ }
else
- next_pc = intrap(EXCEPTION_INTEGER_DIVIDE_BY_ZERO, next_pc, CTS_DIVIDE_BY_ZERO);
- FLAGS(0, 0, 0, 0)
- // FLAGS: 0000
- // TRAPS: D
+ // TRAPS: D
+ m_exception = EXCEPTION_INTEGER_DIVIDE_BY_ZERO;
break;
case 0x9f:
// modwu: modulus word unsigned
if (m_r[R1] != 0)
+ {
m_r[R2] = m_r[R2] % m_r[R1];
+ // FLAGS: 0000
+ FLAGS(0, 0, 0, 0)
+ }
else
- next_pc = intrap(EXCEPTION_INTEGER_DIVIDE_BY_ZERO, next_pc, CTS_DIVIDE_BY_ZERO);
- FLAGS(0, 0, 0, 0)
- // FLAGS: 0000
- // TRAPS: D
+ // TRAPS: D
+ m_exception = EXCEPTION_INTEGER_DIVIDE_BY_ZERO;
break;
case 0xa0:
// subw: subtract word
@@ -1082,16 +1151,6 @@ int clipper_device::execute_instruction ()
// FLAGS: 0001
break;
-#ifdef UNIMPLEMENTED_C400
- case 0xb0:
- // abss: absolute value single floating?
- break;
-
- case 0xb2:
- // absd: absolute value double floating?
- break;
-#endif
-
case 0xb4:
// unprivileged macro instructions
switch (m_info.subopcode)
@@ -1103,11 +1162,12 @@ int clipper_device::execute_instruction ()
// savew0..savew12: push registers rN:r14
// store ri at sp - 4 * (15 - i)
- for (int i = R2; i < 15; i++)
+ for (int i = R2; i < 15 && !m_exception; i++)
m_data->write_dword(m_r[15] - 4 * (15 - i), m_r[i]);
// decrement sp after push to allow restart on exceptions
- m_r[15] -= 4 * (15 - R2);
+ if (!m_exception)
+ m_r[15] -= 4 * (15 - R2);
// TRAPS: A,P,W
break;
// NOTE: the movc, initc and cmpc macro instructions are implemented in a very basic way because
@@ -1119,7 +1179,13 @@ int clipper_device::execute_instruction ()
while (m_r[0])
{
- m_data->write_byte(m_r[2], m_data->read_byte(m_r[1]));
+ const u8 byte = m_data->read_byte(m_r[1]);
+ if (m_exception)
+ break;
+
+ m_data->write_byte(m_r[2], byte);
+ if (m_exception)
+ break;
m_r[0]--;
m_r[1]++;
@@ -1131,7 +1197,9 @@ int clipper_device::execute_instruction ()
// initc: initialise r0 bytes at r1 with value in r2
while (m_r[0])
{
- m_data->write_byte(m_r[1], m_r[2] & 0xff);
+ m_data->write_byte(m_r[1], m_r[2]);
+ if (m_exception)
+ break;
m_r[0]--;
m_r[1]++;
@@ -1148,8 +1216,12 @@ int clipper_device::execute_instruction ()
while (m_r[0])
{
// set condition codes and abort the loop if the current byte does not match
- s32 byte1 = (s8)m_data->read_byte(m_r[1]);
- s32 byte2 = (s8)m_data->read_byte(m_r[2]);
+ s32 byte1 = s8(m_data->read_byte(m_r[1]));
+ if (m_exception)
+ break;
+ s32 byte2 = s8(m_data->read_byte(m_r[2]));
+ if (m_exception)
+ break;
if (byte1 != byte2)
{
FLAGS(C_SUB(byte2, byte1), V_SUB(byte2, byte1), byte2 == byte1, byte2 < byte1)
@@ -1170,10 +1242,16 @@ int clipper_device::execute_instruction ()
// load ri from sp + 4 * (i - N)
for (int i = R2; i < 15; i++)
- m_r[i] = m_data->read_dword(m_r[15] + 4 * (i - R2));
+ {
+ temp = m_data->read_dword(m_r[15] + 4 * (i - R2));
+ if (m_exception)
+ break;
+ m_r[i] = temp;
+ }
// increment sp after pop to allow restart on exceptions
- m_r[15] += 4 * (15 - R2);
+ if (!m_exception)
+ m_r[15] += 4 * (15 - R2);
// TRAPS: C,U,A,P,R
break;
@@ -1182,11 +1260,12 @@ int clipper_device::execute_instruction ()
// saved0..saved7: push registers fN:f7
// store fi at sp - 8 * (8 - i)
- for (int i = R2; i < 8; i++)
+ for (int i = R2; i < 8 && !m_exception; i++)
m_data->write_qword(m_r[15] - 8 * (8 - i), get_fp64(i));
// decrement sp after push to allow restart on exceptions
- m_r[15] -= 8 * (8 - R2);
+ if (!m_exception)
+ m_r[15] -= 8 * (8 - R2);
// TRAPS: A,P,W
break;
case 0x28: case 0x29: case 0x2a: case 0x2b:
@@ -1195,10 +1274,16 @@ int clipper_device::execute_instruction ()
// load fi from sp + 8 * (i - N)
for (int i = R2; i < 8; i++)
- set_fp64(i, m_data->read_qword(m_r[15] + 8 * (i - R2)));
+ {
+ temp = m_data->read_qword(m_r[15] + 8 * (i - R2));
+ if (m_exception)
+ break;
+ set_fp(i, (float64)temp, F_NONE);
+ }
// increment sp after pop to allow restart on exceptions
- m_r[15] += 8 * (8 - R2);
+ if (!m_exception)
+ m_r[15] += 8 * (8 - R2);
// TRAPS: C,U,A,P,R
break;
case 0x30:
@@ -1207,7 +1292,7 @@ int clipper_device::execute_instruction ()
m_r[m_info.macro & 0xf] = float32_to_int32(get_fp32((m_info.macro >> 4) & 0xf));
// TRAPS: F_IX
- float_exception_flags &= (float_flag_invalid | float_flag_inexact);
+ float_exception_flags &= F_IX;
break;
case 0x31:
// cnvrsw: convert rounding single floating to word (non-IEEE +0.5/-0.5 rounding)
@@ -1220,7 +1305,7 @@ int clipper_device::execute_instruction ()
m_r[m_info.macro & 0xf] = float32_to_int32_round_to_zero(float32_add(get_fp32((m_info.macro >> 4) & 0xf),
float32_div(int32_to_float32(1), int32_to_float32(2))));
// TRAPS: F_IX
- float_exception_flags &= (float_flag_invalid | float_flag_inexact);
+ float_exception_flags &= F_IX;
break;
case 0x32:
// cnvtsw: convert truncating single floating to word
@@ -1228,13 +1313,12 @@ int clipper_device::execute_instruction ()
m_r[m_info.macro & 0xf] = float32_to_int32_round_to_zero(get_fp32((m_info.macro >> 4) & 0xf));
// TRAPS: F_IX
- float_exception_flags &= (float_flag_invalid | float_flag_inexact);
+ float_exception_flags &= F_IX;
break;
case 0x33:
// cnvws: convert word to single floating
- set_fp32(m_info.macro & 0xf, int32_to_float32(m_r[(m_info.macro >> 4) & 0xf]));
+ set_fp(m_info.macro & 0xf, int32_to_float32(m_r[(m_info.macro >> 4) & 0xf]), F_X);
// TRAPS: F_X
- float_exception_flags &= (float_flag_inexact);
break;
case 0x34:
// cnvdw: convert double floating to word
@@ -1242,7 +1326,7 @@ int clipper_device::execute_instruction ()
m_r[m_info.macro & 0xf] = float64_to_int32(get_fp64((m_info.macro >> 4) & 0xf));
// TRAPS: F_IX
- float_exception_flags &= (float_flag_invalid | float_flag_inexact);
+ float_exception_flags &= F_IX;
break;
case 0x35:
// cnvrdw: convert rounding double floating to word (non-IEEE +0.5/-0.5 rounding)
@@ -1255,7 +1339,7 @@ int clipper_device::execute_instruction ()
m_r[m_info.macro & 0xf] = float64_to_int32_round_to_zero(float64_add(get_fp64((m_info.macro >> 4) & 0xf),
float64_div(int32_to_float64(1), int32_to_float64(2))));
// TRAPS: F_IX
- float_exception_flags &= (float_flag_invalid | float_flag_inexact);
+ float_exception_flags &= F_IX;
break;
case 0x36:
// cnvtdw: convert truncating double floating to word
@@ -1263,67 +1347,61 @@ int clipper_device::execute_instruction ()
m_r[m_info.macro & 0xf] = float64_to_int32_round_to_zero(get_fp64((m_info.macro >> 4) & 0xf));
// TRAPS: F_IX
- float_exception_flags &= (float_flag_invalid | float_flag_inexact);
+ float_exception_flags &= F_IX;
break;
case 0x37:
// cnvwd: convert word to double floating
- set_fp64(m_info.macro & 0xf, int32_to_float64(m_r[(m_info.macro >> 4) & 0xf]));
- float_exception_flags &= (0);
+ set_fp(m_info.macro & 0xf, int32_to_float64(m_r[(m_info.macro >> 4) & 0xf]), F_NONE);
break;
case 0x38:
// cnvsd: convert single to double floating
- set_fp64(m_info.macro & 0xf, float32_to_float64(get_fp32((m_info.macro >> 4) & 0xf)));
+ set_fp(m_info.macro & 0xf, float32_to_float64(get_fp32((m_info.macro >> 4) & 0xf)), F_I);
// TRAPS: F_I
- float_exception_flags &= (float_flag_invalid);
break;
case 0x39:
// cnvds: convert double to single floating
- set_fp32(m_info.macro & 0xf, float64_to_float32(get_fp64((m_info.macro >> 4) & 0xf)));
+ set_fp(m_info.macro & 0xf, float64_to_float32(get_fp64((m_info.macro >> 4) & 0xf)), F_IVUX);
// TRAPS: F_IVUX
- float_exception_flags &= (float_flag_invalid | float_flag_overflow | float_flag_underflow | float_flag_inexact);
break;
case 0x3a:
// negs: negate single floating
- set_fp32(m_info.macro & 0xf, float32_mul(get_fp32((m_info.macro >> 4) & 0xf), int32_to_float32(-1)));
- float_exception_flags &= (0);
+ set_fp(m_info.macro & 0xf, float32_mul(get_fp32((m_info.macro >> 4) & 0xf), int32_to_float32(-1)), F_NONE);
break;
case 0x3b:
// negd: negate double floating
- set_fp64(m_info.macro & 0xf, float64_mul(get_fp64((m_info.macro >> 4) & 0xf), int32_to_float64(-1)));
- float_exception_flags &= (0);
+ set_fp(m_info.macro & 0xf, float64_mul(get_fp64((m_info.macro >> 4) & 0xf), int32_to_float64(-1)), F_NONE);
break;
case 0x3c:
/*
- * This implementation for scalbd and scalbs is a bit opaque, but
- * essentially we check if the integer value is within range, and
- * directly create a floating constant representing 2^n or NaN
- * respectively, which is used as an input to a multiply, producing
- * the desired result. While doing an actual multiply is both
- * inefficient and unnecessary, it's a tidy way to ensure the
- * correct exception flags are set.
- */
+ * This implementation for scalbd and scalbs is a bit opaque, but
+ * essentially we check if the integer value is within range, and
+ * directly create a floating constant representing 2^n or NaN
+ * respectively, which is used as an input to a multiply, producing
+ * the desired result. While doing an actual multiply is both
+ * inefficient and unnecessary, it's a tidy way to ensure the
+ * correct exception flags are set.
+ */
// scalbs: scale by, single floating
- set_fp32(m_info.macro & 0xf, float32_mul(get_fp32(m_info.macro & 0xf),
+ set_fp(m_info.macro & 0xf, float32_mul(get_fp32(m_info.macro & 0xf),
(((s32)m_r[(m_info.macro >> 4) & 0xf] > -127 && (s32)m_r[(m_info.macro >> 4) & 0xf] < 128)
? (float32)(((s32)m_r[(m_info.macro >> 4) & 0xf] + 127) << 23)
- : (float32)~u32(0))));
+ : (float32)~u32(0))), F_IVUX);
// TRAPS: F_IVUX
- float_exception_flags &= (float_flag_invalid | float_flag_overflow | float_flag_underflow | float_flag_inexact);
break;
case 0x3d:
// scalbd: scale by, double floating
- set_fp64(m_info.macro & 0xf, float64_mul(get_fp64(m_info.macro & 0xf),
+ set_fp(m_info.macro & 0xf, float64_mul(get_fp64(m_info.macro & 0xf),
((s32)m_r[(m_info.macro >> 4) & 0xf] > -1023 && (s32)m_r[(m_info.macro >> 4) & 0xf] < 1024)
? (float64)((u64)((s32)m_r[(m_info.macro >> 4) & 0xf] + 1023) << 52)
- : (float64)~u64(0)));
+ : (float64)~u64(0)), F_IVUX);
// TRAPS: F_IVUX
- float_exception_flags &= (float_flag_invalid | float_flag_overflow | float_flag_underflow | float_flag_inexact);
break;
case 0x3e:
// trapfn: trap floating unordered
- if (PSW(Z) && PSW(N))
- next_pc = intrap(EXCEPTION_ILLEGAL_OPERATION, next_pc, CTS_ILLEGAL_OPERATION);
// TRAPS: I
+ if (PSW(Z) && PSW(N))
+ m_exception = EXCEPTION_ILLEGAL_OPERATION;
+ break;
case 0x3f:
// loadfs: load floating status
m_r[(m_info.macro >> 4) & 0xf] = m_fp_pc;
@@ -1332,9 +1410,7 @@ int clipper_device::execute_instruction ()
break;
default:
- LOG("illegal unprivileged macro opcode at 0x%08x\n", m_pc);
- next_pc = intrap(EXCEPTION_ILLEGAL_OPERATION, next_pc, CTS_ILLEGAL_OPERATION);
- machine().debug_break();
+ m_exception = EXCEPTION_ILLEGAL_OPERATION;
break;
}
break;
@@ -1361,77 +1437,80 @@ int clipper_device::execute_instruction ()
break;
case 0x02:
// saveur: save user registers
- for (int i = 0; i < 16; i++)
+ for (int i = 0; i < 16 && !m_exception; i++)
m_data->write_dword(m_rs[(m_info.macro >> 4) & 0xf] - 4 * (i + 1), m_ru[15 - i]);
- m_rs[(m_info.macro >> 4) & 0xf] -= 64;
+ if (!m_exception)
+ m_rs[(m_info.macro >> 4) & 0xf] -= 64;
// TRAPS: A,P,W,S
break;
case 0x03:
// restur: restore user registers
for (int i = 0; i < 16; i++)
- m_ru[i] = m_data->read_dword(m_rs[(m_info.macro >> 4) & 0xf] + 4 * i);
+ {
+ temp = m_data->read_dword(m_rs[(m_info.macro >> 4) & 0xf] + 4 * i);
+ if (m_exception)
+ break;
+ m_ru[i] = temp;
+ }
- m_rs[(m_info.macro >> 4) & 0xf] += 64;
+ if (!m_exception)
+ m_rs[(m_info.macro >> 4) & 0xf] += 64;
// TRAPS: C,U,A,P,R,S
break;
case 0x04:
// reti: restore psw, ssw and pc from supervisor stack
- LOGMASKED(LOG_INTERRUPT, "reti r%d ssp 0x%08x pc 0x%08x next_pc 0x%08x\n",
- (m_info.macro >> 4) & 0xf, m_rs[(m_info.macro >> 4) & 0xf], m_pc, m_data->read_dword(m_rs[(m_info.macro >> 4) & 0xf] + 8));
-
- m_psw = m_data->read_dword(m_rs[(m_info.macro >> 4) & 0xf] + 0);
- set_ssw(m_data->read_dword(m_rs[(m_info.macro >> 4) & 0xf] + 4));
- next_pc = m_data->read_dword(m_rs[(m_info.macro >> 4) & 0xf] + 8);
-
- m_rs[(m_info.macro >> 4) & 0xf] += 12;
-
- m_r = SSW(U) ? m_ru : m_rs;
-
- fp_rounding();
-
+ m_ip = reti();
// TRAPS: S
break;
case 0x05:
// wait: wait for interrupt
- next_pc = m_pc;
+ m_ip = m_pc;
// TRAPS: S
break;
-#ifdef UNIMPLEMENTED_C400
- case 0x07:
- // loadts: unknown?
- break;
-#endif
default:
- // illegal operation
- LOG("illegal privileged macro opcode at 0x%08x\n", m_pc);
- next_pc = intrap(EXCEPTION_ILLEGAL_OPERATION, next_pc, CTS_ILLEGAL_OPERATION);
- machine().debug_break();
+ m_exception = EXCEPTION_ILLEGAL_OPERATION;
break;
}
}
else
- next_pc = intrap(EXCEPTION_PRIVILEGED_INSTRUCTION, next_pc, CTS_PRIVILEGED_INSTRUCTION);
- break;
-
-#ifdef UNIMPLEMENTED_C400
- case 0xbc:
- // waitd:
- break;
-
- case 0xc0:
- // s*:
+ m_exception = EXCEPTION_PRIVILEGED_INSTRUCTION;
break;
-#endif
default:
- LOG("illegal opcode at 0x%08x\n", m_pc);
- next_pc = intrap(EXCEPTION_ILLEGAL_OPERATION, next_pc, CTS_ILLEGAL_OPERATION);
+ m_exception = EXCEPTION_ILLEGAL_OPERATION;
break;
}
+}
+
+u32 clipper_device::reti()
+{
+ // fetch the psw, ssw and pc from the supervisor stack
+ const u32 new_psw = m_data->read_dword(m_rs[(m_info.macro >> 4) & 0xf] + 0);
+ if (m_exception)
+ fatalerror("reti unrecoverable fault 0x%04x read psw address 0x%08x pc 0x%08x\n", m_exception, m_rs[(m_info.macro >> 4) & 0xf] + 0, m_pc);
- return next_pc;
+ const u32 new_ssw = m_data->read_dword(m_rs[(m_info.macro >> 4) & 0xf] + 4);
+ if (m_exception)
+ fatalerror("reti unrecoverable fault 0x%04x read ssw address 0x%08x pc 0x%08x\n", m_exception, m_rs[(m_info.macro >> 4) & 0xf] + 4, m_pc);
+
+ const u32 new_pc = m_data->read_dword(m_rs[(m_info.macro >> 4) & 0xf] + 8);
+ if (m_exception)
+ fatalerror("reti unrecoverable fault 0x%04x read pc address 0x%08x pc 0x%08x\n", m_exception, m_rs[(m_info.macro >> 4) & 0xf] + 8, m_pc);
+
+ LOGMASKED(LOG_EXCEPTION, "reti r%d ssp 0x%08x pc 0x%08x ssw 0x%08x psw 0x%08x new_pc 0x%08x new_ssw 0x%08x new_psw 0x%08x\n",
+ (m_info.macro >> 4) & 0xf, m_rs[(m_info.macro >> 4) & 0xf], m_pc, m_ssw, m_psw, new_pc, new_ssw, new_psw);
+
+ // pop the stack
+ m_rs[(m_info.macro >> 4) & 0xf] += 12;
+
+ // restore the psw and ssw
+ set_psw(new_psw);
+ set_ssw(new_ssw);
+
+ // return the restored pc
+ return new_pc;
}
/*
@@ -1439,74 +1518,90 @@ int clipper_device::execute_instruction ()
* exception. Reading between the lines, it appears this logic was implemented
* using the macro instruction ROM and a special macro instruction (intrap).
*/
-u32 clipper_device::intrap(u16 vector, u32 pc, u32 cts, u32 mts)
+u32 clipper_device::intrap(const u16 vector, const u32 old_pc)
{
- // fetch pc and ssw from interrupt vector
- u32 next_pc = m_data->read_dword(vector + 0);
- u32 next_ssw = m_data->read_dword(vector + 4);
-
- LOGMASKED(LOG_INTERRUPT, "intrap vector 0x%04x pc 0x%08x next_pc 0x%08x ssp 0x%08x\n", vector, pc, next_pc, m_rs[15]);
+ const u32 old_ssw = m_ssw;
+ const u32 old_psw = m_psw;
- // set cts and mts to indicate source of exception
- m_psw = (m_psw & ~(PSW_CTS | PSW_MTS)) | mts | cts;
+ // clear ssw bits to enable supervisor memory access
+ m_ssw &= ~(SSW_U | SSW_K | SSW_UU | SSW_KU);
- // push pc, ssw and psw onto supervisor stack
- m_data->write_dword(m_rs[15] - 0x4, pc);
- m_data->write_dword(m_rs[15] - 0x8, m_ssw);
- m_data->write_dword(m_rs[15] - 0xc, m_psw);
+ // clear exception state
+ m_exception = 0;
- // decrement supervisor stack pointer
- m_rs[15] -= 12;
+ // fetch next pc and ssw from interrupt vector
+ const u32 new_pc = m_data->read_dword(vector + get_evpc_offset());
+ if (m_exception)
+ fatalerror("intrap unrecoverable fault 0x%04x read pc address 0x%08x pc 0x%08x\n", m_exception, vector + get_evpc_offset(), old_pc);
- // load ssw from trap vector and set previous mode
- set_ssw((next_ssw & ~(SSW(P))) | (SSW(U) << 1));
+ const u32 new_ssw = m_data->read_dword(vector + get_evssw_offset());
+ if (m_exception)
+ fatalerror("intrap unrecoverable fault 0x%04x read ssw address 0x%08x pc 0x%08x\n", m_exception, vector + get_evssw_offset(), old_pc);
- // clear psw
- m_psw = 0;
- float_rounding_mode = float_round_nearest_even;
+ LOGMASKED(LOG_EXCEPTION, "intrap vector 0x%04x pc 0x%08x ssp 0x%08x new_pc 0x%08x new_ssw 0x%08x\n", vector, old_pc, m_rs[15], new_pc, new_ssw);
- m_r = SSW(U) ? m_ru : m_rs;
+ // derive cts and mts from vector
+ u32 source = 0;
+ switch (vector)
+ {
+ // data memory trap group
+ case EXCEPTION_D_CORRECTED_MEMORY_ERROR:
+ case EXCEPTION_D_UNCORRECTABLE_MEMORY_ERROR:
+ case EXCEPTION_D_ALIGNMENT_FAULT:
+ case EXCEPTION_D_PAGE_FAULT:
+ case EXCEPTION_D_READ_PROTECT_FAULT:
+ case EXCEPTION_D_WRITE_PROTECT_FAULT:
- // return new pc from trap vector
- return next_pc;
-}
+ // instruction memory trap group
+ case EXCEPTION_I_CORRECTED_MEMORY_ERROR:
+ case EXCEPTION_I_UNCORRECTABLE_MEMORY_ERROR:
+ case EXCEPTION_I_ALIGNMENT_FAULT:
+ case EXCEPTION_I_PAGE_FAULT:
+ case EXCEPTION_I_EXECUTE_PROTECT_FAULT:
+ source = (vector & MTS_VMASK) << (MTS_SHIFT - MTS_VSHIFT);
+ break;
-u32 clipper_c400_device::intrap(u16 vector, u32 pc, u32 cts, u32 mts)
-{
- // C400 reverses order of pc and ssw in interrupt vector
- u32 next_pc = m_data->read_dword(vector + 4);
- u32 next_ssw = m_data->read_dword(vector + 0);
+ // integer arithmetic trap group
+ case EXCEPTION_INTEGER_DIVIDE_BY_ZERO:
+ source = CTS_DIVIDE_BY_ZERO;
+ break;
- LOGMASKED(LOG_INTERRUPT, "intrap vector 0x%04x pc 0x%08x next_pc 0x%08x ssp 0x%08x\n", vector, pc, next_pc, m_rs[15]);
+ // illegal operation trap group
+ case EXCEPTION_ILLEGAL_OPERATION:
+ source = CTS_ILLEGAL_OPERATION;
+ break;
+ case EXCEPTION_PRIVILEGED_INSTRUCTION:
+ source = CTS_PRIVILEGED_INSTRUCTION;
+ break;
- // set cts and mts to indicate source of exception
- m_psw = (m_psw & ~(PSW_CTS | PSW_MTS)) | mts | cts;
+ // diagnostic trap group
+ case EXCEPTION_TRACE:
+ source = CTS_TRACE_TRAP;
+ break;
+ }
// push pc, ssw and psw onto supervisor stack
- m_data->write_dword(m_rs[15] - 0x4, pc);
- m_data->write_dword(m_rs[15] - 0x8, m_ssw);
- m_data->write_dword(m_rs[15] - 0xc, m_psw);
+ m_data->write_dword(m_rs[15] - 0x4, old_pc);
+ if (m_exception)
+ fatalerror("intrap unrecoverable fault 0x%04x write pc address 0x%08x pc 0x%08x\n", m_exception, m_rs[15] - 0x4, old_pc);
+ m_data->write_dword(m_rs[15] - 0x8, old_ssw);
+ if (m_exception)
+ fatalerror("intrap unrecoverable fault 0x%04x write ssw address 0x%08x pc 0x%08x\n", m_exception, m_rs[15] - 0x8, old_pc);
+ m_data->write_dword(m_rs[15] - 0xc, (old_psw & ~(PSW_CTS | PSW_MTS)) | source);
+ if (m_exception)
+ fatalerror("intrap unrecoverable fault 0x%04x write psw address 0x%08x pc 0x%08x\n", m_exception, m_rs[15] - 0xc, old_pc);
// decrement supervisor stack pointer
+ m_rs[15] -= get_eframe_size();
- // NOTE: while not explicitly stated anywhere, it seems the InterPro C400 boot code has been
- // developed with the assumption that the SSP is decremented by 24 bytes during an exception,
- // rather than the 12 bytes for the InterPro C300 version. This means the exception handler
- // code must explicitly increment the SSP by 12 prior to executing the RETI instruction,
- // as otherwise the SSP will not be pointing at a valid return frame.
- m_rs[15] -= 24;
-
- // load ssw from trap vector and set previous mode
- set_ssw((next_ssw & ~(SSW(P))) | (SSW(U) << 1));
+ // set ssw from vector and previous mode
+ set_ssw((new_ssw & ~SSW_P) | (old_ssw & SSW_U) << 1);
// clear psw
- m_psw = 0;
- float_rounding_mode = float_round_nearest_even;
-
- m_r = SSW(U) ? m_ru : m_rs;
+ set_psw(0);
// return new pc from trap vector
- return next_pc;
+ return new_pc;
}
bool clipper_device::evaluate_branch() const
@@ -1569,8 +1664,11 @@ bool clipper_device::evaluate_branch() const
return false;
}
-void clipper_device::fp_rounding()
+void clipper_device::set_psw(const u32 psw)
{
+ // retain read-only endianness field
+ m_psw = (m_psw & PSW_BIG) | (psw & ~PSW_BIG);
+
// set the softfloat rounding mode based on the psw rounding mode
switch (PSW(FR))
{
@@ -1581,9 +1679,18 @@ void clipper_device::fp_rounding()
}
}
+void clipper_device::set_ssw(const u32 ssw)
+{
+ // retain read-only id field
+ m_ssw = (m_ssw & SSW_ID) | (ssw & ~SSW_ID);
+
+ // select the register file
+ m_r = SSW(U) ? m_ru : m_rs;
+}
+
void clipper_device::fp_exception()
{
- u16 vector = 0;
+ u16 exception = 0;
/*
* Set the psw floating exception flags, and identify any enabled
@@ -1595,62 +1702,99 @@ void clipper_device::fp_exception()
{
m_psw |= PSW_FX;
if (PSW(EFX))
- vector = EXCEPTION_FLOATING_INEXACT;
+ exception = EXCEPTION_FLOATING_INEXACT;
}
if (float_exception_flags & float_flag_underflow)
{
m_psw |= PSW_FU;
if (PSW(EFU))
- vector = EXCEPTION_FLOATING_UNDERFLOW;
+ exception = EXCEPTION_FLOATING_UNDERFLOW;
}
if (float_exception_flags & float_flag_overflow)
{
m_psw |= PSW_FV;
if (PSW(EFV))
- vector = EXCEPTION_FLOATING_OVERFLOW;
+ exception = EXCEPTION_FLOATING_OVERFLOW;
}
if (float_exception_flags & float_flag_divbyzero)
{
m_psw |= PSW_FD;
if (PSW(EFD))
- vector = EXCEPTION_FLOATING_DIVIDE_BY_ZERO;
+ exception = EXCEPTION_FLOATING_DIVIDE_BY_ZERO;
}
if (float_exception_flags & float_flag_invalid)
{
m_psw |= PSW_FI;
if (PSW(EFI))
- vector = EXCEPTION_FLOATING_INVALID_OPERATION;
+ exception = EXCEPTION_FLOATING_INVALID_OPERATION;
}
// trigger a floating point exception
- if (PSW(EFT) && vector)
- m_pc = intrap(vector, m_pc);
+ if (PSW(EFT) && exception)
+ m_exception = exception;
}
-inline void clipper_device::set_fp32(const u8 reg, const float32 data)
+void clipper_c400_device::execute_instruction()
{
- // save floating exception state
- m_fp_pc = m_pc;
- m_fp_dst = m_f[reg & 0xf];
+ switch (m_info.opcode)
+ {
+#ifdef UNIMPLEMENTED_C400
+ case 0x46:
+ case 0x47:
+ // loadd2:
+ break;
- // assign data
- m_f[reg & 0xf] = data;
+ case 0x4a:
+ case 0x4b:
+ // cdb:
+ break;
+ case 0x4c:
+ case 0x4d:
+ // cdbeq:
+ break;
+ case 0x4e:
+ case 0x4f:
+ // cdbne:
+ break;
+ case 0x50:
+ case 0x51:
+ // db*:
+ break;
- // set floating dirty flag
- m_ssw |= SSW_FRD;
-}
+ case 0xb0:
+ // abss: absolute value single floating?
+ break;
-inline void clipper_device::set_fp64(const u8 reg, const float64 data)
-{
- // save floating exception state
- m_fp_pc = m_pc;
- m_fp_dst = m_f[reg & 0xf];
+ case 0xb2:
+ // absd: absolute value double floating?
+ break;
+
+ case 0xb6:
+ // privileged macro instructions
+ if (!SSW(U))
+ {
+ switch (m_info.subopcode)
+ {
+ case 0x07:
+ // loadts: unknown?
+ break;
+ }
+ }
+ break;
+
+ case 0xbc:
+ // waitd:
+ break;
- // assign data
- m_f[reg & 0xf] = data;
+ case 0xc0:
+ // s*:
+ break;
+#endif
- // set floating dirty flag
- m_ssw |= SSW_FRD;
+ default:
+ clipper_device::execute_instruction();
+ break;
+ }
}
util::disasm_interface *clipper_device::create_disassembler()
diff --git a/src/devices/cpu/clipper/clipper.h b/src/devices/cpu/clipper/clipper.h
index 667e835d8c8..1781401fece 100644
--- a/src/devices/cpu/clipper/clipper.h
+++ b/src/devices/cpu/clipper/clipper.h
@@ -19,55 +19,48 @@ class clipper_device : public cpu_device
public:
DECLARE_READ32_MEMBER(get_ssw) const { return m_ssw; }
DECLARE_WRITE8_MEMBER(set_ivec) { m_ivec = data; }
+ DECLARE_WRITE16_MEMBER(set_exception);
-protected:
- enum registers
+ enum addressing_modes : u8
{
- CLIPPER_R0, CLIPPER_R1, CLIPPER_R2, CLIPPER_R3, CLIPPER_R4, CLIPPER_R5, CLIPPER_R6, CLIPPER_R7,
- CLIPPER_R8, CLIPPER_R9, CLIPPER_R10, CLIPPER_R11, CLIPPER_R12, CLIPPER_R13, CLIPPER_R14, CLIPPER_R15,
-
- CLIPPER_F0, CLIPPER_F1, CLIPPER_F2, CLIPPER_F3, CLIPPER_F4, CLIPPER_F5, CLIPPER_F6, CLIPPER_F7,
- CLIPPER_F8, CLIPPER_F9, CLIPPER_F10, CLIPPER_F11, CLIPPER_F12, CLIPPER_F13, CLIPPER_F14, CLIPPER_F15,
-
- CLIPPER_PSW,
- CLIPPER_SSW,
- CLIPPER_PC,
+ ADDR_MODE_PC32 = 0x10, // pc relative with 32 bit displacement
+ ADDR_MODE_ABS32 = 0x30, // 32 bit absolute
+ ADDR_MODE_REL32 = 0x60, // relative with 32 bit displacement
+ ADDR_MODE_PC16 = 0x90, // pc relative with 16 bit displacement
+ ADDR_MODE_REL12 = 0xa0, // relative with 12 bit displacement
+ ADDR_MODE_ABS16 = 0xb0, // 16 bit absolute
+ ADDR_MODE_PCX = 0xd0, // pc indexed
+ ADDR_MODE_RELX = 0xe0 // relative indexed
};
- enum addressing_modes
+ // branch conditions (first description for comparison, second for move/logical)
+ enum branch_conditions : u8
{
- ADDR_MODE_PC32 = 0x10,
- ADDR_MODE_ABS32 = 0x30,
- ADDR_MODE_REL32 = 0x60,
- ADDR_MODE_PC16 = 0x90,
- ADDR_MODE_REL12 = 0xa0,
- ADDR_MODE_ABS16 = 0xb0,
- ADDR_MODE_PCX = 0xd0,
- ADDR_MODE_RELX = 0xe0,
+ BRANCH_T = 0x0, // always
+ BRANCH_LT = 0x1, // less than greater than
+ BRANCH_LE = 0x2, // less or equal greater or equal
+ BRANCH_EQ = 0x3, // equal
+ BRANCH_GT = 0x4, // greater than less than
+ BRANCH_GE = 0x5, // greater or equal less or equal
+ BRANCH_NE = 0x6, // not equal
+ BRANCH_LTU = 0x7, // less than unsigned greater than unsigned
+ BRANCH_LEU = 0x8, // less or equal unsigned greater or equal unsigned
+ BRANCH_GTU = 0x9, // greater than unsigned less than unsigned
+ BRANCH_GEU = 0xa, // greater or equal unsigned less or equal unsigned
+ BRANCH_V = 0xb, // overflow
+ BRANCH_NV = 0xc, // not overflow
+ BRANCH_N = 0xd, // negative
+ BRANCH_NN = 0xe, // not negative
+ BRANCH_FN = 0xf // floating unordered
};
- // branch conditions
- enum branch_conditions
+ enum bf_conditions : u8
{
- BRANCH_T = 0x0,
- BRANCH_LT = 0x1,
- BRANCH_LE = 0x2,
- BRANCH_EQ = 0x3,
- BRANCH_GT = 0x4,
- BRANCH_GE = 0x5,
- BRANCH_NE = 0x6,
- BRANCH_LTU = 0x7,
- BRANCH_LEU = 0x8,
- BRANCH_GTU = 0x9,
- BRANCH_GEU = 0xa,
- BRANCH_V = 0xb,
- BRANCH_NV = 0xc,
- BRANCH_N = 0xd,
- BRANCH_NN = 0xe,
- BRANCH_FN = 0xf,
+ BF_ANY = 0x0, // floating any exception
+ BF_BAD = 0x1 // floating bad result
};
- enum psw
+ enum psw : u32
{
PSW_N = 0x00000001, // negative
PSW_Z = 0x00000002, // zero
@@ -90,10 +83,10 @@ protected:
PSW_BIG = 0x00400000, // c400 - big endian (hardware)
PSW_T = 0x00800000, // trace trap
PSW_CTS = 0x0f000000, // cpu trap status (4 bits)
- PSW_MTS = 0xf0000000, // memory trap status (4 bits)
+ PSW_MTS = 0xf0000000 // memory trap status (4 bits)
};
- enum psw_fr
+ enum psw_fr : u32
{
FR_0 = 0x00000000, // round to nearest
FR_1 = 0x00008000, // round toward + infinity
@@ -101,7 +94,7 @@ protected:
FR_3 = 0x00018000 // round toward zero
};
- enum ssw
+ enum ssw : u32
{
SSW_IN = 0x0000000f, // interrupt number (4 bits)
SSW_IL = 0x000000f0, // interrupt level (4 bits)
@@ -110,17 +103,17 @@ protected:
// unused (5 bits)
SSW_FRD = 0x00400000, // floating registers dirty
SSW_TP = 0x00800000, // trace trap pending
- SSW_ECM = 0x01000000, // enabled corrected memory error
+ SSW_ECM = 0x01000000, // enable corrected memory error
SSW_DF = 0x02000000, // fpu disabled
SSW_M = 0x04000000, // mapped mode
SSW_KU = 0x08000000, // user protect key
SSW_UU = 0x10000000, // user data mode
SSW_K = 0x20000000, // protect key
SSW_U = 0x40000000, // user mode
- SSW_P = 0x80000000, // previous mode
+ SSW_P = 0x80000000 // previous mode
};
- enum ssw_id
+ enum ssw_id : u32
{
SSW_ID_C400R0 = 0x00000,
SSW_ID_C400R1 = 0x04000,
@@ -129,7 +122,7 @@ protected:
SSW_ID_C400R4 = 0x10000
};
- enum exception_vectors
+ enum exception_vectors : u16
{
// data memory trap group
EXCEPTION_D_CORRECTED_MEMORY_ERROR = 0x108,
@@ -167,37 +160,55 @@ protected:
EXCEPTION_SUPERVISOR_CALL_BASE = 0x400,
// prioritized interrupts (0x800-0xff8)
- EXCEPTION_INTERRUPT_BASE = 0x800,
+ EXCEPTION_INTERRUPT_BASE = 0x800
};
// trap source values are shifted into the correct field in the psw
- enum cpu_trap_sources
+ static const int CTS_SHIFT = 24;
+ enum cpu_trap_sources : u32
{
- CTS_NO_CPU_TRAP = 0 << 24,
- CTS_DIVIDE_BY_ZERO = 2 << 24,
- CTS_ILLEGAL_OPERATION = 4 << 24,
- CTS_PRIVILEGED_INSTRUCTION = 5 << 24,
- CTS_TRACE_TRAP = 7 << 24,
+ CTS_NO_CPU_TRAP = 0 << CTS_SHIFT,
+ CTS_DIVIDE_BY_ZERO = 2 << CTS_SHIFT,
+ CTS_ILLEGAL_OPERATION = 4 << CTS_SHIFT,
+ CTS_PRIVILEGED_INSTRUCTION = 5 << CTS_SHIFT,
+ CTS_TRACE_TRAP = 7 << CTS_SHIFT
};
- enum memory_trap_sources
+ static const int MTS_SHIFT = 28;
+ enum memory_trap_sources : u32
{
- MTS_NO_MEMORY_TRAP = 0 << 28,
- MTS_CORRECTED_MEMORY_ERROR = 1 << 28,
- MTS_UNCORRECTABLE_MEMORY_ERROR = 2 << 28,
- MTS_ALIGNMENT_FAULT = 4 << 28,
- MTS_PAGE_FAULT = 5 << 28,
- MTS_READ_OR_EXECUTE_PROTECT_FAULT = 6 << 28,
- MTS_WRITE_PROTECT_FAULT = 7 << 28,
+ MTS_NO_MEMORY_TRAP = 0 << MTS_SHIFT,
+ MTS_CORRECTED_MEMORY_ERROR = 1 << MTS_SHIFT,
+ MTS_UNCORRECTABLE_MEMORY_ERROR = 2 << MTS_SHIFT,
+ MTS_ALIGNMENT_FAULT = 4 << MTS_SHIFT,
+ MTS_PAGE_FAULT = 5 << MTS_SHIFT,
+ MTS_READ_OR_EXECUTE_PROTECT_FAULT = 6 << MTS_SHIFT,
+ MTS_WRITE_PROTECT_FAULT = 7 << MTS_SHIFT
};
- enum ivec_mask
+ // extract an mts code from a vector
+ static const u32 MTS_VMASK = 0x00000038;
+ static const int MTS_VSHIFT = 3;
+
+ enum ivec_mask : u8
{
IVEC_NUMBER = 0x0f,
IVEC_LEVEL = 0xf0
};
- clipper_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, const u32 cpuid);
+ // combinations of floating point exceptions (from softfloat flags)
+ enum fp_exception_mask : u8
+ {
+ F_NONE = (0),
+ F_I = (float_flag_invalid),
+ F_X = (float_flag_inexact),
+ F_IX = (float_flag_invalid | float_flag_inexact),
+ F_IVUX = (float_flag_invalid | float_flag_overflow | float_flag_underflow | float_flag_inexact),
+ F_IVDUX = (float_flag_invalid | float_flag_overflow | float_flag_divbyzero | float_flag_underflow | float_flag_inexact)
+ };
+
+protected:
+ clipper_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, const endianness_t endianness, const u32 cpuid);
// device-level overrides
virtual void device_start() override;
@@ -214,77 +225,147 @@ protected:
virtual space_config_vector memory_space_config() const override;
// device_state_interface overrides
-#if 0
- virtual void state_import(const device_state_entry &entry) override;
- virtual void state_export(const device_state_entry &entry) override;
-#endif
virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
// device_disasm_interface overrides
virtual util::disasm_interface *create_disassembler() override;
- void set_ssw(u32 data) { m_ssw = (m_ssw & SSW(ID)) | (data & ~SSW(ID)); }
+ // cpu execution logic
+ bool decode_instruction();
+ virtual void execute_instruction();
+ bool evaluate_branch() const;
- inline float32 get_fp32(const u8 reg) const { return m_f[reg & 0xf]; };
- inline float64 get_fp64(const u8 reg) const { return m_f[reg & 0xf]; };
- inline void set_fp32(const u8 reg, const float32 data);
- inline void set_fp64(const u8 reg, const float64 data);
+ // exception entry and return helpers
+ u32 intrap(const u16 vector, const u32 old_pc);
+ u32 reti();
- // core registers
- u32 m_pc;
- u32 m_psw;
- u32 m_ssw;
+ // cpu state helpers
+ void set_psw(const u32 psw);
+ void set_ssw(const u32 ssw);
+ void fp_exception();
- // integer registers
- u32 *m_r; // active registers
- u32 m_ru[16]; // user registers
- u32 m_rs[16]; // supervisor registers
+ // register count helpers
+ virtual int get_ireg_count() const { return 16; }
+ virtual int get_freg_count() const { return 8; }
- // floating point registers
- u64 m_f[16];
+ // exception vector and frame helpers
+ virtual int get_eframe_size() const { return 12; }
+ virtual int get_evpc_offset() const { return 0; }
+ virtual int get_evssw_offset() const { return 4; }
- u32 m_fp_pc; // address of floating point instruction causing exception
- u64 m_fp_dst; // original value of destination register during fp exception
+ // floating point helpers
+ float32 get_fp32(const u8 reg) const { return m_f[reg & 0xf]; }
+ float64 get_fp64(const u8 reg) const { return m_f[reg & 0xf]; }
+ template <typename T> void set_fp(const u8 reg, const T data, const fp_exception_mask exception_mask)
+ {
+ // suppress unexpected exceptions
+ float_exception_flags &= exception_mask;
+
+ // save floating exception state
+ m_fp_pc = m_pc;
+ m_fp_dst = m_f[reg & 0xf];
+
+ // assign data
+ if (float_exception_flags & float_flag_overflow && PSW(EFV))
+ {
+ /*
+ * If the EFV flag is set, the computed result is delivered to the
+ * destination with the normalized rounded fraction of the true
+ * result (though the delivered exponent is usually wrong because
+ * of missing additional leading bits in the exponent field). For
+ * single-precision overflows, if the biased exponent of the true
+ * result is 255, then biased exponent 255 is delivered to the
+ * destination. If the true biased exponent of the result is
+ * 256..408, then the true biased exponent minus 256 is delivered
+ * to the destination. Note that this is not the exponent wrapped
+ * result called for by the IEEE 754 specification; the wrap must
+ * be adjusted by system software before delivery to a user's trap
+ * handler. This is done to allow the user to provide software that
+ * handles traps in an application-specific way. For double-
+ * precision, the overflow exponents (biased) lie in the range
+ * 2047..3120. These are mapped to 2047 and 0..1072 respectively.
+ * These must be adjusted by (3/4)x2^11 (1536) to obtain the IEEE
+ * Standard wrapped exponent.
+ */
+ // FIXME: implement non-IEEE behaviour described above
+ m_f[reg & 0xf] = data;
+ }
+ else if (float_exception_flags & float_flag_underflow && PSW(EFU))
+ {
+ /*
+ * If EFU is set, the floating underflow exception is signalled
+ * when the result of an operation (before rounding) has a biased
+ * exponent less than the minimum representable biased exponent for
+ * a normalized number. If the true biased exponent of the result
+ * is zero, then biased exponent zero is delivered to the
+ * destination. If the true biased exponent is less than zero, then
+ * the exponent delivered to the destination is true biased
+ * exponent plus 256 (2048 for double). The exponent must be
+ * adjusted by system software before delivery to the program's
+ * trap handler in order to conform to the IEEE 754 specification.
+ * The range of underflowed biased exponents for single-precision
+ * is 0..-275; for double-precision the range is 0..-1125.
+ */
+ // FIXME: implement non-IEEE behaviour described above
+ m_f[reg & 0xf] = data;
+ }
+ else
+ m_f[reg & 0xf] = data;
+
+ // set floating dirty flag
+ m_ssw |= SSW_FRD;
+ };
+ // emulation state
address_space_config m_insn_config;
address_space_config m_data_config;
address_space *m_insn;
address_space *m_data;
-private:
- int m_icount;
+ enum registers
+ {
+ CLIPPER_IREG = 0,
+ CLIPPER_FREG = 16,
+ CLIPPER_PSW = 32,
+ CLIPPER_SSW = 33,
+ CLIPPER_PC = 34,
+ };
- int m_irq;
- int m_nmi;
- u8 m_ivec;
+ int m_icount; // instruction cycle count
+
+ // program-visible cpu state
+ u32 m_pc; // current instruction address
+ u32 m_psw; // program status word
+ u32 m_ssw; // system status word
+
+ u32 *m_r; // active registers
+ u32 m_ru[16]; // user registers
+ u32 m_rs[16]; // supervisor registers
+
+ u64 m_f[16]; // floating point registers
+ u32 m_fp_pc; // address of floating point instruction causing exception
+ u64 m_fp_dst; // original value of destination register during fp exception
+
+ // non-visible cpu state
+ u32 m_ip; // next instruction address
+ int m_irq; // interrupt request state
+ int m_nmi; // non-maskable interrupt state
+ u8 m_ivec; // interrupt vector
+ u16 m_exception; // pending exception
// decoded instruction information
struct decode
{
- // various decoded instruction fields
- u8 opcode, subopcode;
- u8 r1, r2;
- u32 imm;
- u16 macro;
-
- // total size of instruction in bytes
- u32 size;
-
- // computed effective address
- u32 address;
+ u8 opcode; // primary instruction opcode
+ u8 subopcode; // secondary instruction opcode
+ u8 r1; // r1 instruction operand
+ u8 r2; // r2 instruction operand
+ u32 imm; // immediate value operand
+ u16 macro; // macro instruction operands
+ u32 address; // computed effective address
}
m_info;
-
- void decode_instruction(u16 insn);
- int execute_instruction();
- bool evaluate_branch() const;
-
- void fp_rounding();
- void fp_exception();
-
-protected:
- virtual u32 intrap(u16 vector, u32 pc, u32 cts = CTS_NO_CPU_TRAP, u32 mts = MTS_NO_MEMORY_TRAP);
};
class clipper_c100_device : public clipper_device
@@ -305,7 +386,19 @@ public:
clipper_c400_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
protected:
- virtual u32 intrap(u16 vector, u32 pc, u32 cts = CTS_NO_CPU_TRAP, u32 mts = MTS_NO_MEMORY_TRAP) override;
+ // C400 has additional 8 floating point registers
+ virtual int get_freg_count() const override { return 16; }
+
+ // C400 creates a 24 byte exception frame (C100/C300 is 12 bytes), but the
+ // service routine must increment sp by 12 prior to executing reti
+ // exception frame size
+ virtual int get_eframe_size() const override { return 24; }
+
+ // C400 pc and ssw are reversed in exception vector compared to C100/C300
+ virtual int get_evpc_offset() const override { return 4; }
+ virtual int get_evssw_offset() const override { return 0; }
+
+ virtual void execute_instruction() override;
};
DECLARE_DEVICE_TYPE(CLIPPER_C100, clipper_c100_device)
diff --git a/src/devices/cpu/clipper/clipperd.h b/src/devices/cpu/clipper/clipperd.h
index cad59a490de..1dd757fde63 100644
--- a/src/devices/cpu/clipper/clipperd.h
+++ b/src/devices/cpu/clipper/clipperd.h
@@ -16,17 +16,16 @@ public:
virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params) override;
private:
- // the CLIPPER addressing modes (unshifted)
- enum
+ enum addressing_modes : u8
{
- ADDR_MODE_PC32 = 0x10,
- ADDR_MODE_ABS32 = 0x30,
- ADDR_MODE_REL32 = 0x60,
- ADDR_MODE_PC16 = 0x90,
- ADDR_MODE_REL12 = 0xa0,
- ADDR_MODE_ABS16 = 0xb0,
- ADDR_MODE_PCX = 0xd0,
- ADDR_MODE_RELX = 0xe0
+ ADDR_MODE_PC32 = 0x10, // pc relative with 32 bit displacement
+ ADDR_MODE_ABS32 = 0x30, // 32 bit absolute
+ ADDR_MODE_REL32 = 0x60, // relative with 32 bit displacement
+ ADDR_MODE_PC16 = 0x90, // pc relative with 16 bit displacement
+ ADDR_MODE_REL12 = 0xa0, // relative with 12 bit displacement
+ ADDR_MODE_ABS16 = 0xb0, // 16 bit absolute
+ ADDR_MODE_PCX = 0xd0, // pc indexed
+ ADDR_MODE_RELX = 0xe0 // relative indexed
};
static const char *const cc[];
diff --git a/src/mame/drivers/interpro.cpp b/src/mame/drivers/interpro.cpp
index 2445c9285b2..a1a80215321 100644
--- a/src/mame/drivers/interpro.cpp
+++ b/src/mame/drivers/interpro.cpp
@@ -182,8 +182,14 @@ void interpro_state::machine_reset()
m_sreg_ctrl1 = CTRL1_FLOPLOW;
}
-DRIVER_INIT_MEMBER(interpro_state, interpro)
+DRIVER_INIT_MEMBER(interpro_state, common)
{
+}
+
+DRIVER_INIT_MEMBER(turquoise_state, turquoise)
+{
+ interpro_state::init_common();
+
// FIXME: not all memory sizes are reported properly using fdm "5 inqhw" and
// "optimum_memory" commands
@@ -193,11 +199,26 @@ DRIVER_INIT_MEMBER(interpro_state, interpro)
// 128 = reports 128M, 16x8
// 256 = reports 256M, 32x8
- // grab the main memory space from the mmu
- address_space &space = m_mmu->space(0);
+ // map the configured ram
+ m_d_cammu->space(0).install_ram(0, m_ram->mask(), m_ram->pointer());
+ m_i_cammu->space(0).install_ram(0, m_ram->mask(), m_ram->pointer());
+}
+
+DRIVER_INIT_MEMBER(sapphire_state, sapphire)
+{
+ interpro_state::init_common();
+
+ // FIXME: not all memory sizes are reported properly using fdm "5 inqhw" and
+ // "optimum_memory" commands
+
+ // 16 = reports 16M, banks empty?
+ // 32 = reports 16M, banks empty?
+ // 64 = reports 128M, 16x8
+ // 128 = reports 128M, 16x8
+ // 256 = reports 256M, 32x8
// map the configured ram
- space.install_ram(0, m_ram->mask(), m_ram->pointer());
+ m_mmu->space(0).install_ram(0, m_ram->mask(), m_ram->pointer());
}
WRITE16_MEMBER(interpro_state::sreg_led_w)
@@ -299,12 +320,20 @@ READ8_MEMBER(interpro_state::nodeid_r)
}
// these maps connect the cpu virtual addresses to the mmu
-static ADDRESS_MAP_START(clipper_insn_map, AS_PROGRAM, 32, interpro_state)
- AM_RANGE(0x00000000, 0xffffffff) AM_DEVREAD32(INTERPRO_MMU_TAG, cammu_device, insn_r, 0xffffffff)
+static ADDRESS_MAP_START(c300_insn_map, AS_PROGRAM, 32, interpro_state)
+ AM_RANGE(0x00000000, 0xffffffff) AM_DEVREAD(INTERPRO_MMU_TAG "_i", cammu_device, read)
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START(c300_data_map, AS_DATA, 32, interpro_state)
+ AM_RANGE(0x00000000, 0xffffffff) AM_DEVREADWRITE(INTERPRO_MMU_TAG "_d", cammu_device, read, write)
ADDRESS_MAP_END
-static ADDRESS_MAP_START(clipper_data_map, AS_DATA, 32, interpro_state)
- AM_RANGE(0x00000000, 0xffffffff) AM_DEVREADWRITE32(INTERPRO_MMU_TAG, cammu_device, data_r, data_w, 0xffffffff)
+static ADDRESS_MAP_START(c400_insn_map, AS_PROGRAM, 32, interpro_state)
+ AM_RANGE(0x00000000, 0xffffffff) AM_DEVREAD(INTERPRO_MMU_TAG, cammu_device, read)
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START(c400_data_map, AS_DATA, 32, interpro_state)
+ AM_RANGE(0x00000000, 0xffffffff) AM_DEVREADWRITE(INTERPRO_MMU_TAG, cammu_device, read, write)
ADDRESS_MAP_END
static ADDRESS_MAP_START(interpro_common_map, 0, 32, interpro_state)
@@ -381,13 +410,15 @@ static ADDRESS_MAP_START(sapphire_main_map, 0, 32, sapphire_state)
ADDRESS_MAP_END
static ADDRESS_MAP_START(turquoise_io_map, 1, 32, interpro_state)
- AM_RANGE(0x00000000, 0x00001fff) AM_DEVICE(INTERPRO_MMU_TAG, cammu_device, map)
+ AM_RANGE(0x00000800, 0x000009ff) AM_DEVICE(INTERPRO_MMU_TAG "_d", cammu_c3_device, map)
+ AM_RANGE(0x00000a00, 0x00000bff) AM_DEVICE(INTERPRO_MMU_TAG "_i", cammu_c3_device, map)
+ AM_RANGE(0x00000c00, 0x00000dff) AM_DEVICE(INTERPRO_MMU_TAG "_d", cammu_c3_device, map_global)
AM_IMPORT_FROM(turquoise_base_map)
ADDRESS_MAP_END
static ADDRESS_MAP_START(sapphire_io_map, 1, 32, interpro_state)
- AM_RANGE(0x00000000, 0x00001fff) AM_DEVICE(INTERPRO_MMU_TAG, cammu_device, map)
+ AM_RANGE(0x00000000, 0x00001fff) AM_DEVICE(INTERPRO_MMU_TAG, cammu_c4_device, map)
AM_IMPORT_FROM(sapphire_base_map)
ADDRESS_MAP_END
@@ -486,8 +517,6 @@ static MACHINE_CONFIG_START(ioga)
// ioga floppy terminal count, ethernet channel attention
MCFG_INTERPRO_IOGA_FDCTC_CB(DEVWRITELINE(INTERPRO_FDC_TAG, upd765_family_device, tc_line_w))
MCFG_INTERPRO_IOGA_ETH_CA_CB(DEVWRITELINE(INTERPRO_ETH_TAG, i82586_base_device, ca))
-
- MCFG_INTERPRO_IOGA_DMA_BUS(INTERPRO_CAMMU_TAG, 0)
MACHINE_CONFIG_END
static INPUT_PORTS_START(interpro)
@@ -541,15 +570,24 @@ MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED(turquoise, interpro)
MCFG_CPU_ADD(INTERPRO_CPU_TAG, CLIPPER_C300, XTAL_12_5MHz)
- MCFG_CPU_PROGRAM_MAP(clipper_insn_map)
- MCFG_CPU_DATA_MAP(clipper_data_map)
+ MCFG_CPU_PROGRAM_MAP(c300_insn_map)
+ MCFG_CPU_DATA_MAP(c300_data_map)
MCFG_CPU_IRQ_ACKNOWLEDGE_DEVICE(INTERPRO_IOGA_TAG, interpro_ioga_device, acknowledge_interrupt)
- MCFG_DEVICE_ADD(INTERPRO_MMU_TAG, CAMMU_C3, 0)
+ MCFG_DEVICE_ADD(INTERPRO_MMU_TAG "_i", CAMMU_C3, 0)
MCFG_DEVICE_ADDRESS_MAP(0, turquoise_main_map)
MCFG_DEVICE_ADDRESS_MAP(1, turquoise_io_map)
MCFG_DEVICE_ADDRESS_MAP(2, interpro_boot_map)
MCFG_CAMMU_SSW_CB(DEVREAD32(INTERPRO_CPU_TAG, clipper_device, get_ssw))
+ MCFG_CAMMU_EXCEPTION_CB(DEVWRITE16(INTERPRO_CPU_TAG, clipper_device, set_exception))
+
+ MCFG_DEVICE_ADD(INTERPRO_MMU_TAG "_d", CAMMU_C3, 0)
+ MCFG_DEVICE_ADDRESS_MAP(0, turquoise_main_map)
+ MCFG_DEVICE_ADDRESS_MAP(1, turquoise_io_map)
+ MCFG_DEVICE_ADDRESS_MAP(2, interpro_boot_map)
+ MCFG_CAMMU_SSW_CB(DEVREAD32(INTERPRO_CPU_TAG, clipper_device, get_ssw))
+ MCFG_CAMMU_EXCEPTION_CB(DEVWRITE16(INTERPRO_CPU_TAG, clipper_device, set_exception))
+ MCFG_CAMMU_LINK(INTERPRO_MMU_TAG "_i")
// boot fails memory test without this
MCFG_DEVICE_MODIFY(RAM_TAG)
@@ -587,13 +625,17 @@ static MACHINE_CONFIG_DERIVED(turquoise, interpro)
// i/o gate array
MCFG_DEVICE_ADD(INTERPRO_IOGA_TAG, TURQUOISE_IOGA, 0)
+ MCFG_INTERPRO_IOGA_MEMORY(INTERPRO_MMU_TAG "_d", 0)
MCFG_FRAGMENT_ADD(ioga)
+
+ MCFG_DEVICE_MODIFY(INTERPRO_SRBUS_TAG)
+ MCFG_SR_MEMORY(INTERPRO_MMU_TAG "_d", 0, 1)
MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED(sapphire, interpro)
MCFG_CPU_ADD(INTERPRO_CPU_TAG, CLIPPER_C400, XTAL_12_5MHz)
- MCFG_CPU_PROGRAM_MAP(clipper_insn_map)
- MCFG_CPU_DATA_MAP(clipper_data_map)
+ MCFG_CPU_PROGRAM_MAP(c400_insn_map)
+ MCFG_CPU_DATA_MAP(c400_data_map)
MCFG_CPU_IRQ_ACKNOWLEDGE_DEVICE(INTERPRO_IOGA_TAG, interpro_ioga_device, acknowledge_interrupt)
MCFG_DEVICE_ADD(INTERPRO_MMU_TAG, CAMMU_C4T, 0)
@@ -601,6 +643,7 @@ static MACHINE_CONFIG_DERIVED(sapphire, interpro)
MCFG_DEVICE_ADDRESS_MAP(1, sapphire_io_map)
MCFG_DEVICE_ADDRESS_MAP(2, interpro_boot_map)
MCFG_CAMMU_SSW_CB(DEVREAD32(INTERPRO_CPU_TAG, clipper_device, get_ssw))
+ MCFG_CAMMU_EXCEPTION_CB(DEVWRITE16(INTERPRO_CPU_TAG, clipper_device, set_exception))
// memory control gate array
MCFG_DEVICE_ADD(INTERPRO_MCGA_TAG, INTERPRO_FMCC, 0)
@@ -632,11 +675,16 @@ static MACHINE_CONFIG_DERIVED(sapphire, interpro)
// i/o gate array
MCFG_DEVICE_ADD(INTERPRO_IOGA_TAG, SAPPHIRE_IOGA, 0)
+ MCFG_INTERPRO_IOGA_MEMORY(INTERPRO_MMU_TAG, 0)
MCFG_FRAGMENT_ADD(ioga)
// flash memory
MCFG_DEVICE_ADD(INTERPRO_FLASH_TAG "_lo", INTEL_28F010, 0)
MCFG_DEVICE_ADD(INTERPRO_FLASH_TAG "_hi", INTEL_28F010, 0)
+
+ // sr bus address spaces
+ MCFG_DEVICE_MODIFY(INTERPRO_SRBUS_TAG)
+ MCFG_SR_MEMORY(INTERPRO_MMU_TAG, 0, 1)
MACHINE_CONFIG_END
MACHINE_CONFIG_DERIVED(ip2000, turquoise)
@@ -734,9 +782,9 @@ ROM_START(ip2800)
ROM_LOAD_OPTIONAL("y226.u67", 0x00000, 0x20000, CRC(54d95730) SHA1(a4e114dee1567d8aa31eed770f7cc366588f395c))
ROM_END
-/* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */
-COMP( 1989, ip2000, 0, 0, ip2000, interpro, turquoise_state, interpro, "Intergraph", "InterPro 2000", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
-COMP( 1991?, ip2400, 0, 0, ip2400, interpro, sapphire_state, interpro, "Intergraph", "InterPro 2400", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
-COMP( 1993?, ip2500, 0, 0, ip2500, interpro, sapphire_state, interpro, "Intergraph", "InterPro 2500", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
-COMP( 1992, ip2700, 0, 0, ip2700, interpro, sapphire_state, interpro, "Intergraph", "InterPro 2700", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
-COMP( 1994, ip2800, 0, 0, ip2800, interpro, sapphire_state, interpro, "Intergraph", "InterPro 2800", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
+/* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */
+COMP( 1989, ip2000, 0, 0, ip2000, interpro, turquoise_state, turquoise, "Intergraph", "InterPro 2000", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
+COMP( 1991?, ip2400, 0, 0, ip2400, interpro, sapphire_state, sapphire, "Intergraph", "InterPro 2400", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
+COMP( 1993?, ip2500, 0, 0, ip2500, interpro, sapphire_state, sapphire, "Intergraph", "InterPro 2500", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
+COMP( 1992, ip2700, 0, 0, ip2700, interpro, sapphire_state, sapphire, "Intergraph", "InterPro 2700", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
+COMP( 1994, ip2800, 0, 0, ip2800, interpro, sapphire_state, sapphire, "Intergraph", "InterPro 2800", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
diff --git a/src/mame/includes/interpro.h b/src/mame/includes/interpro.h
index f65bcbb0c1f..e40969ad172 100644
--- a/src/mame/includes/interpro.h
+++ b/src/mame/includes/interpro.h
@@ -65,7 +65,6 @@ public:
interpro_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, m_maincpu(*this, INTERPRO_CPU_TAG)
- , m_mmu(*this, INTERPRO_MMU_TAG)
, m_ram(*this, RAM_TAG)
, m_mcga(*this, INTERPRO_MCGA_TAG)
, m_sga(*this, INTERPRO_SGA_TAG)
@@ -78,10 +77,10 @@ public:
, m_scsi(*this, INTERPRO_SCSI_DEVICE_TAG)
, m_eth(*this, INTERPRO_ETH_TAG)
, m_ioga(*this, INTERPRO_IOGA_TAG)
- { }
+ {
+ }
required_device<clipper_device> m_maincpu;
- required_device<cammu_device> m_mmu;
required_device<ram_device> m_ram;
required_device<interpro_mcga_device> m_mcga;
@@ -96,7 +95,7 @@ public:
required_device<i82586_base_device> m_eth;
required_device<interpro_ioga_device> m_ioga;
- DECLARE_DRIVER_INIT(interpro);
+ DECLARE_DRIVER_INIT(common);
enum sreg_error_mask
{
@@ -180,9 +179,17 @@ class turquoise_state : public interpro_state
public:
turquoise_state(const machine_config &mconfig, device_type type, const char *tag)
: interpro_state(mconfig, type, tag)
- {}
+ , m_d_cammu(*this, INTERPRO_MMU_TAG "_d")
+ , m_i_cammu(*this, INTERPRO_MMU_TAG "_i")
+ {
+ }
+
+ DECLARE_DRIVER_INIT(turquoise);
DECLARE_WRITE8_MEMBER(sreg_error_w) { m_sreg_error = data; }
+
+ required_device<cammu_c3_device> m_d_cammu;
+ required_device<cammu_c3_device> m_i_cammu;
};
class sapphire_state : public interpro_state
@@ -190,14 +197,20 @@ class sapphire_state : public interpro_state
public:
sapphire_state(const machine_config &mconfig, device_type type, const char *tag)
: interpro_state(mconfig, type, tag)
+ , m_mmu(*this, INTERPRO_MMU_TAG)
, m_flash_lo(*this, INTERPRO_FLASH_TAG "_lo")
, m_flash_hi(*this, INTERPRO_FLASH_TAG "_hi")
- {}
+ {
+ }
- required_device<intel_28f010_device> m_flash_lo;
- required_device<intel_28f010_device> m_flash_hi;
+ DECLARE_DRIVER_INIT(sapphire);
virtual DECLARE_WRITE16_MEMBER(sreg_ctrl2_w) override;
+
+ required_device<cammu_c4_device> m_mmu;
+
+ required_device<intel_28f010_device> m_flash_lo;
+ required_device<intel_28f010_device> m_flash_hi;
};
#endif // MAME_INCLUDES_INTERPRO_H
diff --git a/src/mame/machine/cammu.cpp b/src/mame/machine/cammu.cpp
index 4e1d86b2076..5686cfa7d2e 100644
--- a/src/mame/machine/cammu.cpp
+++ b/src/mame/machine/cammu.cpp
@@ -2,28 +2,43 @@
// copyright-holders:Patrick Mackinlay
/*
- * An implementation of the Fairchild/Intergraph Cache and Memory Management Unit (CAMMU) designed for use with the CLIPPER CPU family.
+ * An implementation of the Fairchild/Intergraph Cache and Memory Management
+ * Unit (CAMMU) designed for use with the CLIPPER CPU family.
*
- * Primary reference: http://bitsavers.trailing-edge.com/pdf/fairchild/clipper/CLIPPER%20C300%2032-Bit%20Compute%20Engine.pdf
- * Another reference: http://www.eecs.berkeley.edu/Pubs/TechRpts/1986/CSD-86-289.pdf
+ * The C100 and C300 designs used a pair of identical CAMMU devices, each
+ * containing a cache, TLB and dynamic translation unit. One device was
+ * configured and used for instruction memory, the other for data. It is
+ * possible to write to multiple CAMMU devices sharing a common system bus by
+ * using "global" register addresses.
+ *
+ * C400 designs initially implemented the memory management and cache functions
+ * using discrete logic, later using a more highly integrated memory management
+ * implementation, but still using discrete cache memory. In these systems, the
+ * mmu is consolidated into a single logical unit handling both instruction and
+ * data memory, with distinctly different program-visible architectures on the
+ * C4I and C4E/T devices. Almost no documentation for these has been located.
*
- * This implementation is currently at a very early stage, and is only sufficient to handle the bare minimum of boot/diagnostic code.
+ * Primary reference: http://bitsavers.org/pdf/fairchild/clipper/CLIPPER%20C300%2032-Bit%20Compute%20Engine.pdf
+ * Another reference: http://www.eecs.berkeley.edu/Pubs/TechRpts/1986/CSD-86-289.pdf
*
* TODO
- * - almost everything
- * - refactor hardware tlb
- * - faults
- * - tlb
+ * - fault register values
+ * - alignment faults
+ * - c3 protection faults
+ * - hard-wired and dynamic tlb
* - cache
* - bus errors
*/
#include "emu.h"
+#include "cammu.h"
-#define VERBOSE 0
-#define DTU 1 // enable preliminary/incomplete address translation
+#define LOG_GENERAL (1U << 0)
+#define LOG_ACCESS (1U << 1)
+#define LOG_DTU (1U << 2)
-#include "cammu.h"
+//#define VERBOSE (LOG_GENERAL | LOG_ACCESS | LOG_DTU)
+#include "logmacro.h"
// each variant of the cammu has different registers and a different addressing map
DEVICE_ADDRESS_MAP_START(map, 32, cammu_c4t_device)
@@ -75,28 +90,21 @@ DEVICE_ADDRESS_MAP_START(map, 32, cammu_c4i_device)
ADDRESS_MAP_END
DEVICE_ADDRESS_MAP_START(map, 32, cammu_c3_device)
- // the first AM_NOP in each range is in fact the TLB in the C3 CAMMU
-
- AM_RANGE(0x800, 0x8ff) AM_NOP
- AM_RANGE(0x904, 0x907) AM_READWRITE(d_s_pdo_r, d_s_pdo_w)
- AM_RANGE(0x908, 0x90b) AM_READWRITE(d_u_pdo_r, d_u_pdo_w)
- AM_RANGE(0x910, 0x913) AM_READWRITE(d_fault_r, d_fault_w)
- AM_RANGE(0x940, 0x943) AM_READWRITE(d_control_r, d_control_w)
- AM_RANGE(0x980, 0x983) AM_READWRITE(d_reset_r, d_reset_w)
-
- AM_RANGE(0xa00, 0xaff) AM_NOP
- AM_RANGE(0xb04, 0xb07) AM_READWRITE(i_s_pdo_r, i_s_pdo_w)
- AM_RANGE(0xb08, 0xb0b) AM_READWRITE(i_u_pdo_r, i_u_pdo_w)
- AM_RANGE(0xb10, 0xb13) AM_READWRITE(i_fault_r, i_fault_w)
- AM_RANGE(0xb40, 0xb43) AM_READWRITE(i_control_r, i_control_w)
- AM_RANGE(0xb80, 0xb83) AM_READWRITE(i_reset_r, i_reset_w)
-
- AM_RANGE(0xc00, 0xcff) AM_NOP
- AM_RANGE(0xd04, 0xd07) AM_WRITE(g_s_pdo_w)
- AM_RANGE(0xd08, 0xd0b) AM_WRITE(g_u_pdo_w)
- AM_RANGE(0xd10, 0xd13) AM_WRITE(g_fault_w)
- AM_RANGE(0xd40, 0xd43) AM_WRITE(g_control_w)
- AM_RANGE(0xd80, 0xd83) AM_WRITE(g_reset_w)
+ AM_RANGE(0x000, 0x0ff) AM_NOP // tlb
+ AM_RANGE(0x104, 0x107) AM_READWRITE(s_pdo_r, s_pdo_w)
+ AM_RANGE(0x108, 0x10b) AM_READWRITE(u_pdo_r, u_pdo_w)
+ AM_RANGE(0x110, 0x113) AM_READWRITE(fault_r, fault_w)
+ AM_RANGE(0x140, 0x143) AM_READWRITE(control_r, control_w)
+ AM_RANGE(0x180, 0x183) AM_READWRITE(reset_r, reset_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(map_global, 32, cammu_c3_device)
+ AM_RANGE(0x000, 0x0ff) AM_NOP // global tlb
+ AM_RANGE(0x104, 0x107) AM_WRITE(g_s_pdo_w)
+ AM_RANGE(0x108, 0x10b) AM_WRITE(g_u_pdo_w)
+ AM_RANGE(0x110, 0x113) AM_WRITE(g_fault_w)
+ AM_RANGE(0x140, 0x143) AM_WRITE(g_control_w)
+ AM_RANGE(0x180, 0x183) AM_WRITE(g_reset_w)
ADDRESS_MAP_END
DEFINE_DEVICE_TYPE(CAMMU_C4T, cammu_c4t_device, "c4t", "C4E/C4T CAMMU")
@@ -104,41 +112,62 @@ DEFINE_DEVICE_TYPE(CAMMU_C4I, cammu_c4i_device, "c4i", "C4I CAMMU")
DEFINE_DEVICE_TYPE(CAMMU_C3, cammu_c3_device, "c3", "C1/C3 CAMMU")
cammu_c4t_device::cammu_c4t_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : cammu_c4_device(mconfig, CAMMU_C4T, tag, owner, clock)
+ : cammu_c4_device(mconfig, CAMMU_C4T, tag, owner, clock, CID_C4T)
{
}
cammu_c4i_device::cammu_c4i_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : cammu_c4_device(mconfig, CAMMU_C4I, tag, owner, clock)
+ : cammu_c4_device(mconfig, CAMMU_C4I, tag, owner, clock, CID_C4I)
{
}
-cammu_c4_device::cammu_c4_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+cammu_c4_device::cammu_c4_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, u32 cammu_id)
: cammu_device(mconfig, type, tag, owner, clock)
+ , m_control(cammu_id)
{
}
cammu_c3_device::cammu_c3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: cammu_device(mconfig, CAMMU_C3, tag, owner, clock)
+ , m_control(CID_C3)
+ , m_linked{ this }
{
}
cammu_device::cammu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, type, tag, owner, clock)
- , device_memory_interface(mconfig, *this),
- m_main_space_config("main", ENDIANNESS_LITTLE, 32, 32, 0),
- m_io_space_config("io", ENDIANNESS_LITTLE, 32, 32, 0),
- m_boot_space_config("boot", ENDIANNESS_LITTLE, 32, 32, 0),
- m_main_space(nullptr),
- m_io_space(nullptr),
- m_boot_space(nullptr),
- m_ssw_func(*this)
+ , device_memory_interface(mconfig, *this)
+ , m_main_space_config("main", ENDIANNESS_LITTLE, 32, 32, 0)
+ , m_io_space_config("io", ENDIANNESS_LITTLE, 32, 32, 0)
+ , m_boot_space_config("boot", ENDIANNESS_LITTLE, 32, 32, 0)
+ , m_main_space(nullptr)
+ , m_io_space(nullptr)
+ , m_boot_space(nullptr)
+ , m_ssw_func(*this)
+ , m_exception_func(*this)
+{
+}
+
+device_memory_interface::space_config_vector cammu_device::memory_space_config() const
{
+ return space_config_vector {
+ std::make_pair(0, &m_main_space_config),
+ std::make_pair(1, &m_io_space_config),
+ std::make_pair(2, &m_boot_space_config)
+ };
+}
+
+void cammu_c3_device::static_add_linked(device_t &device, const char *const tag)
+{
+ cammu_c3_device &parent = downcast<cammu_c3_device &>(device);
+
+ parent.m_linked.push_back(downcast<cammu_c3_device *>(parent.siblingdevice(tag)));
}
void cammu_device::device_start()
{
m_ssw_func.resolve();
+ m_exception_func.resolve();
m_main_space = &space(0);
m_io_space = &space(1);
@@ -149,268 +178,321 @@ void cammu_device::device_reset()
{
}
-device_memory_interface::space_config_vector cammu_device::memory_space_config() const
+void cammu_c4_device::device_start()
{
- return space_config_vector {
- std::make_pair(0, &m_main_space_config),
- std::make_pair(1, &m_io_space_config),
- std::make_pair(2, &m_boot_space_config)
- };
+ cammu_device::device_start();
+
+ save_item(NAME(m_s_pdo));
+ save_item(NAME(m_u_pdo));
+ save_item(NAME(m_control));
+
+ save_item(NAME(m_i_fault));
+ save_item(NAME(m_fault_address_1));
+ save_item(NAME(m_fault_address_2));
+ save_item(NAME(m_fault_data_1_lo));
+ save_item(NAME(m_fault_data_1_hi));
+ save_item(NAME(m_fault_data_2_lo));
+ save_item(NAME(m_fault_data_2_hi));
}
-READ32_MEMBER(cammu_device::insn_r)
+void cammu_c4i_device::device_start()
{
- u32 ssw = m_ssw_func();
- u32 va = offset << 2;
+ cammu_c4_device::device_start();
+
+ save_item(NAME(m_reset));
+ save_item(NAME(m_clr_s_data_tlb));
+ save_item(NAME(m_clr_u_data_tlb));
+ save_item(NAME(m_clr_s_insn_tlb));
+ save_item(NAME(m_clr_u_insn_tlb));
+ save_item(NAME(m_test_data));
+ save_item(NAME(m_test_address));
+}
- // in supervisor mode, the first 8 pages are always mapped via the hard-wired tlb
- if ((ssw & 0x40000000) == 0 && (va & ~0x7fff) == 0)
- {
- switch (va & 0xf000)
- {
- case 0x0000:
- case 0x1000:
- case 0x2000:
- case 0x3000:
- // pages 0-3: main space pages 0-3
- return m_main_space->read_dword(va, mem_mask);
+void cammu_c4t_device::device_start()
+{
+ cammu_c4_device::device_start();
+
+ save_item(NAME(m_ram_line));
+ save_item(NAME(m_htlb_offset));
+ save_item(NAME(m_c4_bus_poll));
+ save_item(NAME(m_bio_control));
+ save_item(NAME(m_bio_address_tag));
+
+ save_item(NAME(m_cache_data_lo));
+ save_item(NAME(m_cache_data_hi));
+ save_item(NAME(m_cache_cpu_tag));
+ save_item(NAME(m_cache_system_tag_valid));
+ save_item(NAME(m_cache_system_tag));
+ save_item(NAME(m_tlb_va_line));
+ save_item(NAME(m_tlb_ra_line));
+}
- case 0x4000:
- case 0x5000:
- // pages 4-5: i/o space pages 0-1
- return m_io_space->read_dword(va & 0x1fff, mem_mask);
+void cammu_c3_device::device_start()
+{
+ cammu_device::device_start();
- case 0x6000:
- case 0x7000:
- // pages 6-7: boot space pages 0-1
- return m_boot_space->read_dword(va & 0x1fff, mem_mask);
- }
- }
+ save_item(NAME(m_s_pdo));
+ save_item(NAME(m_u_pdo));
+ save_item(NAME(m_fault));
+ save_item(NAME(m_control));
+ save_item(NAME(m_reset));
+}
- // if not in mapped mode, default to main memory space
- if ((ssw & 0x04000000) == 0)
- return m_main_space->read_dword(va);
+void cammu_c3_device::device_reset()
+{
+ cammu_device::device_reset();
-#if DTU
- // get the page table entry
- u32 pte = get_pte(va, ssw & 0x40000000, false);
+ m_control = (m_control & CNTL_CID) | (CNTL_ATE | UST_3 | CNTL_EWIR | CNTL_EWIW | CNTL_EWCW | CNTL_EP);
+ m_reset = 0;
+}
- // translate the address
- u32 ra = (pte & 0xfffff000) | (va & 0xfff);
+READ32_MEMBER(cammu_device::read)
+{
+ const u32 virtual_address = (offset << 2) | (
+ (mem_mask & 0x00ffffff) == 0 ? 0x3 :
+ (mem_mask & 0x0000ffff) == 0 ? 0x2 :
+ (mem_mask & 0x000000ff) == 0 ? 0x1 : 0x0);
+ offs_t physical_address;
- // execute the read based on the system tag
- switch ((pte & 0xe00) >> 9)
- {
- case 0:
- case 1:
- case 2:
- case 3:
- return m_main_space->read_dword(ra, mem_mask);
+ LOGMASKED(LOG_ACCESS, "%s read address 0x%08x mem_mask 0x%08x (%s)\n",
+ space.name(), virtual_address, mem_mask, machine().describe_context());
- case 4:
- return m_io_space->read_dword(ra, mem_mask);
+ address_space *physical_space = translate_address(virtual_address, space.spacenum() == AS_PROGRAM ? ACCESS_X : ACCESS_R, &physical_address);
- case 5:
- return m_boot_space->read_dword(ra, mem_mask);
+ if (physical_space != nullptr)
+ return physical_space->read_dword(physical_address, mem_mask);
+ else
+ return space.unmap();
+}
- case 6: // cache purge
- case 7: // main memory, slave mode
- fatalerror("system tag %d not supported %s\n", (pte & 0xe00) >> 9, machine().describe_context().c_str());
- }
+WRITE32_MEMBER(cammu_device::write)
+{
+ const u32 virtual_address = (offset << 2) | (
+ (mem_mask & 0x00ffffff) == 0 ? 0x3 :
+ (mem_mask & 0x0000ffff) == 0 ? 0x2 :
+ (mem_mask & 0x000000ff) == 0 ? 0x1 : 0x0);
+ offs_t physical_address;
- return 0;
-#else
- // FIXME: currently maps addresses with upper bits 0x00 or 0x7f1 to main memory and everything else to I/O
- if ((va & 0xff000000) == 0x00000000 || (va & 0xfff00000) == 0x7f100000)
- return m_main_space->read_dword(va, mem_mask);
- else
- return m_io_space->read_dword(va, mem_mask);
-#endif
+ LOGMASKED(LOG_ACCESS, "%s write address 0x%08x data 0x%08x mem_mask 0x%08x (%s)\n",
+ space.name(), virtual_address, data, mem_mask, machine().describe_context());
+
+ address_space *physical_space = translate_address(virtual_address, ACCESS_W, &physical_address);
+
+ if (physical_space != nullptr)
+ physical_space->write_dword(physical_address, data, mem_mask);
}
-READ32_MEMBER(cammu_device::data_r)
+address_space *cammu_device::translate_address(const offs_t virtual_address, const access_t mode, offs_t *physical_address)
{
- u32 ssw = m_ssw_func();
- u32 va = offset << 2;
+ // get ssw and user/supervisor mode
+ const u32 ssw = m_ssw_func();
+ const bool user = mode == ACCESS_X ? ssw & SSW_U : ssw & (SSW_U | SSW_UU);
+
+ // TODO: check for alignment faults
- // in supervisor mode (and not user data mode), the first 8 pages are always mapped via the hard-wired tlb
- if (((ssw & 0x50000000) == 0) && ((va & ~0x7fff) == 0))
+ // in supervisor mode, the first 8 pages are always mapped via the hard-wired tlb
+ if (!user && (virtual_address & ~0x7fff) == 0)
{
- switch (va & 0xf000)
+ switch (virtual_address & 0x7000)
{
case 0x0000:
case 0x1000:
case 0x2000:
case 0x3000:
// pages 0-3: main space pages 0-3
- return m_main_space->read_dword(va, mem_mask);
+ *physical_address = virtual_address & 0x3fff;
+ return m_main_space;
case 0x4000:
case 0x5000:
// pages 4-5: i/o space pages 0-1
- return m_io_space->read_dword(va & 0x1fff, mem_mask);
+ *physical_address = virtual_address & 0x1fff;
+ return m_io_space;
case 0x6000:
case 0x7000:
// pages 6-7: boot space pages 0-1
- return m_boot_space->read_dword(va & 0x1fff, mem_mask);
+ *physical_address = virtual_address & 0x1fff;
+ return m_boot_space;
}
}
- // if not in mapped mode, default to main memory space
- if ((ssw & 0x04000000) == 0)
- return m_main_space->read_dword(va);
+ // if not in mapped mode, use unmapped system tag
+ if ((ssw & SSW_M) == 0)
+ {
+ *physical_address = virtual_address;
+
+ return get_ust_space();
+ }
-#if DTU
// get the page table entry
- u32 pte = get_pte(va, ssw & 0x50000000, space.spacenum() == AS_DATA);
+ const u32 pte = get_pte(virtual_address, user);
- // translate the address
- u32 ra = (pte & 0xfffff000) | (va & 0xfff);
+ // check for page faults
+ if (pte & PTE_F)
+ {
+ if (!machine().side_effect_disabled())
+ {
+ LOG("%s page fault address 0x%08x ssw 0x%08x pte 0x%08x (%s)\n",
+ mode == ACCESS_X ? "instruction" : "data",
+ virtual_address, ssw, pte, machine().describe_context());
+
+ set_fault_address(virtual_address);
+ m_exception_func(mode == ACCESS_X ? EXCEPTION_I_PAGE_FAULT : EXCEPTION_D_PAGE_FAULT);
+ }
+
+ return nullptr;
+ }
- // execute the read based on the system tag
- switch ((pte & 0xe00) >> 9)
+ // check for protection level faults
+ if (!machine().side_effect_disabled() && !get_access(mode, pte, ssw))
{
- case 0:
- case 1:
- case 2:
- case 3:
- return m_main_space->read_dword(ra, mem_mask);
+ LOG("%s protection fault address 0x%08x ssw 0x%08x pte 0x%08x (%s)\n",
+ mode == ACCESS_X ? "execute" : mode == ACCESS_R ? "read" : "write",
+ virtual_address, ssw, pte, machine().describe_context());
+
+ set_fault_address(virtual_address);
+ m_exception_func(
+ mode == ACCESS_X ? EXCEPTION_I_EXECUTE_PROTECT_FAULT :
+ mode == ACCESS_R ? EXCEPTION_D_READ_PROTECT_FAULT :
+ EXCEPTION_D_WRITE_PROTECT_FAULT);
- case 4:
- return m_io_space->read_dword(ra, mem_mask);
+ return nullptr;
+ }
- case 5:
- return m_boot_space->read_dword(ra, mem_mask);
+ // translate the address
+ *physical_address = (pte & ~CAMMU_PAGE_MASK) | (virtual_address & CAMMU_PAGE_MASK);
+ LOGMASKED(LOG_DTU, "%s address translated 0x%08x\n", mode == ACCESS_X ? "instruction" : "data", *physical_address);
- case 6: // cache purge
- case 7: // main memory, slave mode
- fatalerror("data_r: system tag %d not supported at %s\n", (pte & 0xe00) >> 9, machine().describe_context().c_str());
+ // return the physical space based on the system tag
+ switch (pte & PTE_ST)
+ {
+ case ST_0:
+ case ST_1:
+ case ST_2:
+ case ST_3:
+ // main memory space
+ return m_main_space;
+
+ case ST_4:
+ // i/o space
+ return m_io_space;
+
+ case ST_5:
+ // boot space
+ return m_boot_space;
+
+ case ST_6:
+ // cache purge
+ case ST_7:
+ // main memory, slave mode
+ if (!machine().side_effect_disabled())
+ fatalerror("%s page table entry system tag %d not supported\n",
+ mode == ACCESS_X ? "instruction" : "data", (pte & PTE_ST) >> 9);
+ break;
}
- return 0;
-#else
- // FIXME: currently maps addresses with upper bits 0x00 or 0x7f1 to main memory and everything else to I/O
- if ((va & 0xff000000) == 0x00000000 || (va & 0xfff00000) == 0x7f100000)
- return m_main_space->read_dword(va, mem_mask);
- else
- return m_io_space->read_dword(va, mem_mask);
-#endif
+ return nullptr;
}
-WRITE32_MEMBER(cammu_device::data_w)
+// return the page table entry for a given virtual address
+u32 cammu_device::get_pte(const u32 va, const bool user)
{
- u32 ssw = m_ssw_func();
- u32 va = offset << 2;
-
- // in supervisor mode (and not user data mode), the first 8 pages are always mapped via the hard-wired tlb
- if (((ssw & 0x50000000) == 0) && ((va & ~0x7fff) == 0))
+ const u32 tlb_index = user ? 1 : 0;
+ if ((va & ~CAMMU_PAGE_MASK) != m_tlb[tlb_index].va)
{
- switch (va & 0xf000)
- {
- case 0x0000:
- case 0x1000:
- case 0x2000:
- case 0x3000:
- // pages 0-3: main space pages 0-3
- m_main_space->write_dword(va, data, mem_mask);
- return;
+ // get page table directory origin from user or supervisor pdo register
+ const u32 pdo = get_pdo(user);
- case 0x4000:
- case 0x5000:
- // pages 4-5: i/o space pages 0-1
- m_io_space->write_dword(va & 0x1fff, data, mem_mask);
- return;
+ // get page table directory index from top 12 bits of virtual address
+ const u32 ptdi = (va & VA_PTDI) >> 20;
- case 0x6000:
- case 0x7000:
- // pages 6-7: boot space pages 0-1
- m_boot_space->write_dword(va & 0x1fff, data, mem_mask);
- return;
- }
- }
+ // fetch page table directory entry
+ const u32 ptde = m_main_space->read_dword(pdo | ptdi);
- // if not in mapped mode, default to main memory space
- if ((ssw & 0x04000000) == 0)
- {
- m_main_space->write_dword(va, data, mem_mask);
- return;
- }
+ LOGMASKED(LOG_DTU, "get_pte pdo 0x%08x ptdi 0x%08x ptde 0x%08x\n", pdo, ptdi, ptde);
-#if DTU
- // get the page table entry
- u32 pte = get_pte(va, ssw & 0x50000000, space.spacenum() == AS_DATA);
+ // check for page table directory entry fault
+ if (ptde & PTDE_F)
+ return PTE_F;
- // translate the address
- u32 ra = (pte & 0xfffff000) | (va & 0xfff);
+ // get the page table origin from the page table directory entry
+ const u32 pto = ptde & PTDE_PTO;
- // execute the read based on the system tag
- switch ((pte & 0xe00) >> 9)
- {
- case 0:
- case 1:
- case 2:
- case 3:
- m_main_space->write_dword(ra, data, mem_mask);
- break;
+ // get the page table index from the middle 12 bits of the virtual address
+ const u32 pti = (va & VA_PTI) >> 10;
- case 4:
- m_io_space->write_dword(ra, data, mem_mask);
- break;
+ // fetch page table entry
+ const u32 pte = m_main_space->read_dword(pto | pti);
- case 5:
- m_boot_space->write_dword(ra, data, mem_mask);
- break;
+ LOGMASKED(LOG_DTU, "get_pte pto 0x%08x pti 0x%08x pte 0x%08x\n", pto, pti, pte);
- case 6: // cache purge
- case 7: // main memory, slave mode
- fatalerror("data_w: system tag %d not supported at %s\n", (pte & 0xe00) >> 9, machine().describe_context().c_str());
- break;
+ // check for page table entry fault
+ if (pte & PTE_F)
+ return PTE_F;
+
+ // add the pte to the tlb
+ m_tlb[tlb_index].va = va & ~CAMMU_PAGE_MASK;
+ m_tlb[tlb_index].pte = pte;
+
+ LOGMASKED(LOG_DTU, "get_pte address 0x%08x pte 0x%08x (%s)\n", va, pte, machine().describe_context());
}
-#else
- // FIXME: currently maps addresses with upper bits 0x00 or 0x7f1 to main memory and everything else to I/O
- if ((va & 0xff000000) == 0x00000000 || (va & 0xfff00000) == 0x7f100000)
- m_main_space->write_dword(va, data, mem_mask);
- else
- m_io_space->write_dword(va, data, mem_mask);
-#endif
+
+ return m_tlb[tlb_index].pte;
}
-u32 cammu_c4_device::get_pte(u32 va, int user, bool data)
+address_space *cammu_c4i_device::get_ust_space() const
{
- u32 tlb_index = (user ? 2 : 0) + (data ? 1 : 0);
- if ((va & 0xfffff000) != m_tlb[tlb_index].va)
+ switch (m_control & CNTL_UMM)
{
- // return the page table entry for a given virtual address
- u32 pdo = user ? m_u_pdo : m_s_pdo;
-
- u32 pto = m_main_space->read_dword(pdo | (va & 0xffc00000) >> 20);
- if (pto & 0x1)
- fatalerror("can't deal with pto faults va 0x%08x %s\n", va, machine().describe_context().c_str());
+ case UMM_MM: return m_main_space;
+ case UMM_MMRIO: return m_main_space; // FIXME: what determines main or i/o?
+ case UMM_IO: return m_io_space;
+ }
- u32 pte = m_main_space->read_dword((pto & 0xfffff000) | (va & 0x003ff000) >> 10);
- if (pte & 0x1)
- fatalerror("can't deal with pte faults va 0x%08x %s\n", va, machine().describe_context().c_str());
+ return m_main_space;
+}
- m_tlb[tlb_index].va = va & 0xfffff000;
- m_tlb[tlb_index].pte = pte;
+bool cammu_c4_device::get_access(const access_t mode, const u32 pte, const u32 ssw) const
+{
+ switch (mode)
+ {
+ case ACCESS_R: return pte & 0x20;
+ case ACCESS_W: return pte & 0x10;
+ case ACCESS_X: return pte & 0x08;
}
- return m_tlb[tlb_index].pte;
+ return false;
}
-u32 cammu_c3_device::get_pte(u32 va, int user, bool data)
+bool cammu_c3_device::get_access(const access_t mode, const u32 pte, const u32 ssw) const
{
- // return the page table entry for a given virtual address
- u32 pdo = user ? (data ? m_d_u_pdo : m_i_u_pdo) : (data ? m_d_s_pdo : m_i_s_pdo);
+ // FIXME: logic is not correct yet
+ return true;
- u32 pto = m_main_space->read_dword(pdo | (va & 0xffc00000) >> 20);
- if (pto & 0x1)
- fatalerror("can't deal with pto faults va 0x%08x %s\n", va, machine().describe_context().c_str());
+ const u8 column = (mode == ACCESS_X ? i_cammu_column : d_cammu_column)[(ssw & SSW_PL) >> 9];
+ const u8 access = cammu_matrix[column][(pte & PTE_PL) >> 3];
- u32 pte = m_main_space->read_dword((pto & 0xfffff000) | (va & 0x003ff000) >> 10);
- if (pte & 0x1)
- fatalerror("can't deal with pte faults va 0x%08x %s\n", va, machine().describe_context().c_str());
+ switch (mode)
+ {
+ case ACCESS_R: return access & R;
+ case ACCESS_W: return access & W;
+ case ACCESS_X: return access & E;
+ }
- return pte;
+ return false;
}
+
+// C100/C300 CAMMU protection level matrix
+const u8 cammu_c3_device::i_cammu_column[] = { 1, 1, 0, 0, 1, 1, 0, 0, 3, 3, 2, 2, 3, 3, 2, 2 };
+const u8 cammu_c3_device::d_cammu_column[] = { 1, 1, 0, 0, 3, 2, 3, 2, 3, 3, 2, 2, 3, 3, 2, 2 };
+
+const cammu_c3_device::c3_access_t cammu_c3_device::cammu_matrix[][16] =
+{
+ { RW, RW, RW, RW, RW, RW, RW, RWE, RE, R, R, R, N, N, N, N },
+ { N, RW, RW, RW, RW, RW, R, RWE, N, RE, R, R, RE, N, N, N },
+ { N, N, RW, RW, RW, R, R, RWE, N, N, RE, RE, N, RE, N, N },
+ { N, N, N, RW, R, R, R, RWE, N, N, N, RE, RE, N, RE, N }
+};
+
+
diff --git a/src/mame/machine/cammu.h b/src/mame/machine/cammu.h
index 9ea301f6896..28eb32f07ad 100644
--- a/src/mame/machine/cammu.h
+++ b/src/mame/machine/cammu.h
@@ -9,17 +9,99 @@
#define MCFG_CAMMU_SSW_CB(_sswcb) \
devcb = &cammu_device::static_set_ssw_callback(*device, DEVCB_##_sswcb);
+#define MCFG_CAMMU_EXCEPTION_CB(_exceptioncb) \
+ devcb = &cammu_device::static_set_exception_callback(*device, DEVCB_##_exceptioncb);
+
+#define MCFG_CAMMU_LINK(_tag) \
+ cammu_c3_device::static_add_linked(*device, _tag);
+
class cammu_device : public device_t, public device_memory_interface
{
public:
template <class Object> static devcb_base &static_set_ssw_callback(device_t &device, Object &&cb) { return downcast<cammu_device &>(device).m_ssw_func.set_callback(std::forward<Object>(cb)); }
+ template <class Object> static devcb_base &static_set_exception_callback(device_t &device, Object &&cb) { return downcast<cammu_device &>(device).m_exception_func.set_callback(std::forward<Object>(cb)); }
- virtual DECLARE_ADDRESS_MAP(map, 32) = 0;
+ static const u32 CAMMU_PAGE_SIZE = 0x1000;
+ static const u32 CAMMU_PAGE_MASK = (CAMMU_PAGE_SIZE - 1);
+
+ enum ssw_mask : u32
+ {
+ SSW_M = 0x04000000, // mapped mode
+ SSW_KU = 0x08000000, // user protect key
+ SSW_UU = 0x10000000, // user data mode
+ SSW_K = 0x20000000, // protect key
+ SSW_U = 0x40000000, // user mode
+
+ SSW_PL = 0x78000000 // protection level relevant bits
+ };
+
+ enum exception_vectors : u16
+ {
+ // data memory trap group
+ EXCEPTION_D_CORRECTED_MEMORY_ERROR = 0x108,
+ EXCEPTION_D_UNCORRECTABLE_MEMORY_ERROR = 0x110,
+ EXCEPTION_D_ALIGNMENT_FAULT = 0x120,
+ EXCEPTION_D_PAGE_FAULT = 0x128,
+ EXCEPTION_D_READ_PROTECT_FAULT = 0x130,
+ EXCEPTION_D_WRITE_PROTECT_FAULT = 0x138,
+
+ // instruction memory trap group
+ EXCEPTION_I_CORRECTED_MEMORY_ERROR = 0x288,
+ EXCEPTION_I_UNCORRECTABLE_MEMORY_ERROR = 0x290,
+ EXCEPTION_I_ALIGNMENT_FAULT = 0x2a0,
+ EXCEPTION_I_PAGE_FAULT = 0x2a8,
+ EXCEPTION_I_EXECUTE_PROTECT_FAULT = 0x2b0,
+ };
+
+ enum pdo_mask : u32
+ {
+ PDO_MASK = 0xfffff000
+ };
- DECLARE_READ32_MEMBER(insn_r);
+ enum ptde_mask : u32
+ {
+ PTDE_F = 0x00000001, // page fault
+ PTDE_PTO = 0xfffff000 // page table origin
+ };
- DECLARE_READ32_MEMBER(data_r);
- DECLARE_WRITE32_MEMBER(data_w);
+ enum pte_mask : u32
+ {
+ PTE_F = 0x00000001, // page fault
+ PTE_R = 0x00000002, // referenced flag
+ PTE_D = 0x00000004, // dirty flag
+ PTE_PL = 0x00000078, // protection level
+ PTE_S = 0x00000180, // system reserved
+ PTE_ST = 0x00000e00, // system tag
+ PTE_RA = 0xfffff000, // real address
+
+ PTE_CW = 0x00000040, // copy on write (c400)
+ PTE_NDREF = 0x00000080, // secondary reference (software) / copy on write (fault)?
+ PTE_LOCK = 0x00000100 // page lock (software)
+ };
+
+ enum pte_st_mask : u32
+ {
+ ST_0 = 0x00000000, // private, write-through, main memory space
+ ST_1 = 0x00000200, // shared, write-through, main memory space
+ ST_2 = 0x00000400, // private, copy-back, main memory space
+ ST_3 = 0x00000600, // noncacheable, main memory space
+ ST_4 = 0x00000800, // noncacheable, i/o space
+ ST_5 = 0x00000a00, // noncacheable, boot space
+ ST_6 = 0x00000c00, // cache purge
+ ST_7 = 0x00000e00 // slave i/o
+ };
+
+ enum va_mask : u32
+ {
+ VA_POFS = 0x00000fff, // page offset
+ VA_PTI = 0x003ff000, // page table index
+ VA_PTDI = 0xffc00000 // page table directory index
+ };
+
+ virtual DECLARE_ADDRESS_MAP(map, 32) = 0;
+
+ DECLARE_READ32_MEMBER(read);
+ DECLARE_WRITE32_MEMBER(write);
protected:
cammu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
@@ -30,59 +112,82 @@ protected:
// device_memory_interface overrides
virtual space_config_vector memory_space_config() const override;
+ //virtual bool memory_translate(int spacenum, int intention, offs_t &address) override;
- virtual u32 get_pte(u32 va, int user, bool data) = 0;
+ enum access_t
+ {
+ ACCESS_R = 1,
+ ACCESS_W = 2,
+ ACCESS_X = 3
+ };
+
+ address_space *translate_address(const offs_t virtual_address, const access_t mode, offs_t *physical_address);
+
+ u32 get_pte(const u32 va, const bool user);
+
+ virtual bool get_access(const access_t mode, const u32 pte, const u32 ssw) const = 0;
+ virtual bool get_alignment() const = 0;
+ virtual u32 get_pdo(const bool user) const = 0;
+ virtual address_space *get_ust_space() const = 0;
+
+ virtual void set_fault_address(const u32 va) = 0;
-private:
address_space_config m_main_space_config;
address_space_config m_io_space_config;
address_space_config m_boot_space_config;
-protected:
address_space *m_main_space;
address_space *m_io_space;
address_space *m_boot_space;
+ devcb_read32 m_ssw_func;
+ devcb_write16 m_exception_func;
+
struct
{
u32 va;
u32 pte;
- } m_tlb[4];
+ }
+ m_tlb[2];
private:
- devcb_read32 m_ssw_func;
};
class cammu_c4_device : public cammu_device
{
public:
- cammu_c4_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
-
- DECLARE_READ32_MEMBER(s_pdo_r) { return m_s_pdo; }
- DECLARE_WRITE32_MEMBER(s_pdo_w) { m_s_pdo = data; }
- DECLARE_READ32_MEMBER(u_pdo_r) { return m_u_pdo; }
- DECLARE_WRITE32_MEMBER(u_pdo_w) { m_u_pdo = data; }
+ DECLARE_READ32_MEMBER(s_pdo_r) const { return m_s_pdo; }
+ DECLARE_WRITE32_MEMBER(s_pdo_w) { m_s_pdo = ((m_s_pdo & ~mem_mask) | (data & mem_mask)) & PDO_MASK; }
+ DECLARE_READ32_MEMBER(u_pdo_r) const { return m_u_pdo; }
+ DECLARE_WRITE32_MEMBER(u_pdo_w) { m_u_pdo = ((m_u_pdo & ~mem_mask) | (data & mem_mask)) & PDO_MASK; }
- virtual DECLARE_READ32_MEMBER(control_r) = 0;
+ virtual DECLARE_READ32_MEMBER(control_r) const = 0;
virtual DECLARE_WRITE32_MEMBER(control_w) = 0;
- DECLARE_READ32_MEMBER(i_fault_r) { return m_i_fault; }
+ DECLARE_READ32_MEMBER(i_fault_r) const { return m_i_fault; }
DECLARE_WRITE32_MEMBER(i_fault_w) { m_i_fault = data; }
- DECLARE_READ32_MEMBER(fault_address_1_r) { return m_fault_address_1; }
+ DECLARE_READ32_MEMBER(fault_address_1_r) const { return m_fault_address_1; }
DECLARE_WRITE32_MEMBER(fault_address_1_w) { m_fault_address_1 = data; }
- DECLARE_READ32_MEMBER(fault_address_2_r) { return m_fault_address_2; }
+ DECLARE_READ32_MEMBER(fault_address_2_r) const { return m_fault_address_2; }
DECLARE_WRITE32_MEMBER(fault_address_2_w) { m_fault_address_2 = data; }
- DECLARE_READ32_MEMBER(fault_data_1_lo_r) { return m_fault_data_1_lo; }
+ DECLARE_READ32_MEMBER(fault_data_1_lo_r) const { return m_fault_data_1_lo; }
DECLARE_WRITE32_MEMBER(fault_data_1_lo_w) { m_fault_data_1_lo = data; }
- DECLARE_READ32_MEMBER(fault_data_1_hi_r) { return m_fault_data_1_hi; }
+ DECLARE_READ32_MEMBER(fault_data_1_hi_r) const { return m_fault_data_1_hi; }
DECLARE_WRITE32_MEMBER(fault_data_1_hi_w) { m_fault_data_1_hi = data; }
- DECLARE_READ32_MEMBER(fault_data_2_lo_r) { return m_fault_data_2_lo; }
+ DECLARE_READ32_MEMBER(fault_data_2_lo_r) const { return m_fault_data_2_lo; }
DECLARE_WRITE32_MEMBER(fault_data_2_lo_w) { m_fault_data_2_lo = data; }
- DECLARE_READ32_MEMBER(fault_data_2_hi_r) { return m_fault_data_2_hi; }
+ DECLARE_READ32_MEMBER(fault_data_2_hi_r) const { return m_fault_data_2_hi; }
DECLARE_WRITE32_MEMBER(fault_data_2_hi_w) { m_fault_data_2_hi = data; }
protected:
- u32 get_pte(u32 va, int user, bool data) override;
+ cammu_c4_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, u32 cammu_id);
+
+ virtual void device_start() override;
+
+ virtual bool get_access(const access_t mode, const u32 pte, const u32 ssw) const override;
+ virtual u32 get_pdo(const bool user) const override { return user ? m_u_pdo : m_s_pdo; }
+
+ virtual void set_fault_address(const u32 va) override { m_fault_address_1 = va; }
u32 m_s_pdo;
u32 m_u_pdo;
@@ -104,36 +209,73 @@ public:
virtual DECLARE_ADDRESS_MAP(map, 32) override;
- DECLARE_READ32_MEMBER(ram_line_r) { return m_ram_line; }
+ DECLARE_READ32_MEMBER(ram_line_r) const { return m_ram_line; }
DECLARE_WRITE32_MEMBER(ram_line_w) { m_ram_line = data; }
- DECLARE_READ32_MEMBER(htlb_offset_r) { return m_htlb_offset; }
+ DECLARE_READ32_MEMBER(htlb_offset_r) const { return m_htlb_offset; }
DECLARE_WRITE32_MEMBER(htlb_offset_w) { m_htlb_offset = data; }
- DECLARE_READ32_MEMBER(c4_bus_poll_r) { return m_c4_bus_poll; }
+ DECLARE_READ32_MEMBER(c4_bus_poll_r) const { return m_c4_bus_poll; }
DECLARE_WRITE32_MEMBER(c4_bus_poll_w) { m_c4_bus_poll = data; }
- virtual DECLARE_READ32_MEMBER(control_r) override { return m_control | 0x00000000; }
- virtual DECLARE_WRITE32_MEMBER(control_w) override { m_control = data & 0x00ffffff; }
- DECLARE_READ32_MEMBER(bio_control_r) { return m_bio_control; }
+
+ enum control_mask : u32
+ {
+ CNTL_RUV = 0x00000001, // reset user valid
+ CNTL_RSV = 0x00000002, // reset supervisor valid
+ CNTL_DBWR = 0x00000004, // disable bus watch read
+ CNTL_ATD = 0x00000008, // alignment trap disable
+ CNTL_UST = 0x00000030, // unmapped system tag
+ CNTL_IOTS = 0x00000040, // i/o tag select
+ CNTL_UVS = 0x00000080, // user valid status
+ CNTL_PB = 0x00000100, // purge busy
+ CNTL_CICT = 0x00000200, // clear i-side cache tags
+ CNTL_CFR = 0x00000400, // clear trap registers
+ CNTL_HTLBD = 0x00000800, // htlb disable
+ CNTL_CDCT = 0x00001000, // clear d-side cache tags
+ CNTL_CID = 0xff000000 // cammu id
+ };
+
+ enum control_ust_mask : u32
+ {
+ UST_NCA = 0x00, // unmapped system tag, noncacheable
+ UST_PWT = 0x10, // unmapped system tag, write through
+ UST_PCB = 0x20, // unmapped system tag, copy back
+ UST_PGE = 0x30 // unmapped system tag, purge mode
+ };
+
+ enum control_cid_mask : u32
+ {
+ CID_C4T = 0x00000000 // unknown
+ };
+
+ virtual DECLARE_READ32_MEMBER(control_r) const override { return m_control; }
+ virtual DECLARE_WRITE32_MEMBER(control_w) override { m_control = ((m_control & (~mem_mask | CNTL_CID)) | (data & (mem_mask & ~CNTL_CID))); }
+ DECLARE_READ32_MEMBER(bio_control_r) const { return m_bio_control; }
DECLARE_WRITE32_MEMBER(bio_control_w) { m_bio_control = data; }
- DECLARE_READ32_MEMBER(bio_address_tag_r) { return m_bio_address_tag; }
+ DECLARE_READ32_MEMBER(bio_address_tag_r) const { return m_bio_address_tag; }
DECLARE_WRITE32_MEMBER(bio_address_tag_w) { m_bio_address_tag = data; }
- DECLARE_READ32_MEMBER(cache_data_lo_r) { return m_cache_data_lo; }
+ DECLARE_READ32_MEMBER(cache_data_lo_r) const { return m_cache_data_lo; }
DECLARE_WRITE32_MEMBER(cache_data_lo_w) { m_cache_data_lo = data; }
- DECLARE_READ32_MEMBER(cache_data_hi_r) { return m_cache_data_hi; }
+ DECLARE_READ32_MEMBER(cache_data_hi_r) const { return m_cache_data_hi; }
DECLARE_WRITE32_MEMBER(cache_data_hi_w) { m_cache_data_hi = data; }
- DECLARE_READ32_MEMBER(cache_cpu_tag_r) { return m_cache_cpu_tag; }
+ DECLARE_READ32_MEMBER(cache_cpu_tag_r) const { return m_cache_cpu_tag; }
DECLARE_WRITE32_MEMBER(cache_cpu_tag_w) { m_cache_cpu_tag = data; }
- DECLARE_READ32_MEMBER(cache_system_tag_valid_r) { return m_cache_system_tag_valid; }
+ DECLARE_READ32_MEMBER(cache_system_tag_valid_r) const { return m_cache_system_tag_valid; }
DECLARE_WRITE32_MEMBER(cache_system_tag_valid_w) { m_cache_system_tag_valid = data; }
- DECLARE_READ32_MEMBER(cache_system_tag_r) { return m_cache_system_tag; }
+ DECLARE_READ32_MEMBER(cache_system_tag_r) const { return m_cache_system_tag; }
DECLARE_WRITE32_MEMBER(cache_system_tag_w) { m_cache_system_tag = data; }
- DECLARE_READ32_MEMBER(tlb_va_line_r) { return m_tlb_va_line; }
+ DECLARE_READ32_MEMBER(tlb_va_line_r) const { return m_tlb_va_line; }
DECLARE_WRITE32_MEMBER(tlb_va_line_w) { m_tlb_va_line = data; }
- DECLARE_READ32_MEMBER(tlb_ra_line_r) { return m_tlb_ra_line; }
+ DECLARE_READ32_MEMBER(tlb_ra_line_r) const { return m_tlb_ra_line; }
DECLARE_WRITE32_MEMBER(tlb_ra_line_w) { m_tlb_ra_line = data; }
+protected:
+ virtual void device_start() override;
+
+ virtual bool get_alignment() const override { return (m_control & CNTL_ATD) == 0; }
+ virtual address_space *get_ust_space() const override { return (m_control & CNTL_IOTS) ? m_io_space : m_main_space; }
+
private:
u32 m_ram_line;
u32 m_htlb_offset;
@@ -157,27 +299,83 @@ public:
virtual DECLARE_ADDRESS_MAP(map, 32) override;
- virtual DECLARE_READ32_MEMBER(control_r) override { return m_control | 0x02000000; }
- virtual DECLARE_WRITE32_MEMBER(control_w) override { m_control = data & 0x00ffffff; }
+ enum control_mask : u32
+ {
+ CNTL_LRAS = 0x00000001, // tlb line replacement
+ CNTL_BWWD = 0x00000002, // buswatch write disable
+ CNTL_BWRD = 0x00000004, // buswatch read disable
+ CNTL_FSR = 0x00000010, // fake system response
+ CNTL_ATD = 0x00000100, // alignment trap disable
+ CNTL_UMM = 0x00003000, // unmapped mode address space select
+ CNTL_POLL = 0x00030000, // poll bus signals
+ CNTL_BM = 0x00040000, // burst mode address space select
+ CNTL_PZBS = 0x00080000, // page 0 boot select
+ CNTL_CRR = 0x00700000, // cache memory refresh rate
+ CNTL_CID = 0xff000000 // cammu identification
+ };
+
+ enum control_umm_mask : u32
+ {
+ UMM_MM = 0x00000000, // mm space, noncacheable
+ UMM_MMRIO = 0x00001000, // mm or i/o space, noncacheable
+ UMM_IO = 0x00002000 // i/o space noncacheable
+ };
+
+ enum control_crr_mask : u32
+ {
+ CRR_GT131 = 0x00000000, // clock rate over 131 MHz
+ CRR_GT66 = 0x00100000, // clock rate over 66 MHz
+ CRR_GT33 = 0x00200000, // clock rate over 33 MHz
+ CRR_GT8 = 0x00300000, // clock rate over 8 MHz
+ CRR_GT2 = 0x00400000, // clock rate over 2 MHz
+ CRR_GT1 = 0x00500000, // clock rate over 1 MHz
+ CRR_GTHALF = 0x00600000, // clock rate over 0.5 MHz
+ CRR_OFF = 0x00700000, // refresh off
+ };
+
+ enum control_cid_mask : u32
+ {
+ CID_C4I = 0x02000000 // c4i cammu identification
+ };
+
+ virtual DECLARE_READ32_MEMBER(control_r) const override { return m_control; }
+ virtual DECLARE_WRITE32_MEMBER(control_w) override { m_control = ((m_control & (~mem_mask | CNTL_CID)) | (data & (mem_mask & ~CNTL_CID))); }
- DECLARE_READ32_MEMBER(reset_r) { return m_reset; }
+ enum reset_mask : u32
+ {
+ RESET_CDCT = 0x00000001, // clear data cache tags
+ RESET_RDUV = 0x00000100, // reset all d-side uv flags
+ RESET_RDSV = 0x00001000, // reset all d-side sv flags
+ RESET_CICT = 0x00010000, // clear ins. cache tags
+ RESET_RIUV = 0x01000000, // reset all i-side uv flags
+ RESET_RISV = 0x10000000, // reset all i-side sv flags
+ RESET_FLUSH = 0x40000000, // flush out burst io buffer
+ RESET_CFR = 0x80000000 // clear fault registers
+ };
+ DECLARE_READ32_MEMBER(reset_r) const { return m_reset; }
DECLARE_WRITE32_MEMBER(reset_w) { m_reset = data; }
- DECLARE_READ32_MEMBER(clr_s_data_tlb_r) { return m_clr_s_data_tlb; }
+ DECLARE_READ32_MEMBER(clr_s_data_tlb_r) const { return m_clr_s_data_tlb; }
DECLARE_WRITE32_MEMBER(clr_s_data_tlb_w) { m_clr_s_data_tlb = data; }
- DECLARE_READ32_MEMBER(clr_u_data_tlb_r) { return m_clr_u_data_tlb; }
+ DECLARE_READ32_MEMBER(clr_u_data_tlb_r) const { return m_clr_u_data_tlb; }
DECLARE_WRITE32_MEMBER(clr_u_data_tlb_w) { m_clr_u_data_tlb = data; }
- DECLARE_READ32_MEMBER(clr_s_insn_tlb_r) { return m_clr_s_insn_tlb; }
+ DECLARE_READ32_MEMBER(clr_s_insn_tlb_r) const { return m_clr_s_insn_tlb; }
DECLARE_WRITE32_MEMBER(clr_s_insn_tlb_w) { m_clr_s_insn_tlb = data; }
- DECLARE_READ32_MEMBER(clr_u_insn_tlb_r) { return m_clr_u_insn_tlb; }
+ DECLARE_READ32_MEMBER(clr_u_insn_tlb_r) const { return m_clr_u_insn_tlb; }
DECLARE_WRITE32_MEMBER(clr_u_insn_tlb_w) { m_clr_u_insn_tlb = data; }
- DECLARE_READ32_MEMBER(test_data_r) { return m_test_data; }
+ DECLARE_READ32_MEMBER(test_data_r) const { return m_test_data; }
DECLARE_WRITE32_MEMBER(test_data_w) { m_test_data = data; }
- DECLARE_READ32_MEMBER(test_address_r) { return m_test_address; }
+ DECLARE_READ32_MEMBER(test_address_r) const { return m_test_address; }
DECLARE_WRITE32_MEMBER(test_address_w) { m_test_address = data; }
+protected:
+ virtual void device_start() override;
+
+ virtual bool get_alignment() const override { return (m_control & CNTL_ATD) == 0; }
+ virtual address_space *get_ust_space() const override;
+
private:
u32 m_reset;
u32 m_clr_s_data_tlb;
@@ -193,50 +391,88 @@ class cammu_c3_device : public cammu_device
public:
cammu_c3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+ static void static_add_linked(device_t &device, const char *const tag);
+
virtual DECLARE_ADDRESS_MAP(map, 32) override;
+ virtual DECLARE_ADDRESS_MAP(map_global, 32);
- DECLARE_READ32_MEMBER(d_s_pdo_r) { return m_d_s_pdo; }
- DECLARE_WRITE32_MEMBER(d_s_pdo_w) { m_d_s_pdo = data; }
- DECLARE_READ32_MEMBER(d_u_pdo_r) { return m_d_u_pdo; }
- DECLARE_WRITE32_MEMBER(d_u_pdo_w) { m_d_u_pdo = data; }
- DECLARE_READ32_MEMBER(d_fault_r) { return m_d_fault; }
- DECLARE_WRITE32_MEMBER(d_fault_w) { m_d_fault = data; }
- DECLARE_READ32_MEMBER(d_control_r) { return m_d_control; }
- DECLARE_WRITE32_MEMBER(d_control_w) { m_d_control = data; }
- DECLARE_READ32_MEMBER(d_reset_r) { return m_d_reset; }
- DECLARE_WRITE32_MEMBER(d_reset_w) { m_d_reset = data; }
-
- DECLARE_READ32_MEMBER(i_s_pdo_r) { return m_i_s_pdo; }
- DECLARE_WRITE32_MEMBER(i_s_pdo_w) { m_i_s_pdo = data; }
- DECLARE_READ32_MEMBER(i_u_pdo_r) { return m_i_u_pdo; }
- DECLARE_WRITE32_MEMBER(i_u_pdo_w) { m_i_u_pdo = data; }
- DECLARE_READ32_MEMBER(i_fault_r) { return m_i_fault; }
- DECLARE_WRITE32_MEMBER(i_fault_w) { m_i_fault = data; }
- DECLARE_READ32_MEMBER(i_control_r) { return m_i_control; }
- DECLARE_WRITE32_MEMBER(i_control_w) { m_i_control = data; }
- DECLARE_READ32_MEMBER(i_reset_r) { return m_i_reset; }
- DECLARE_WRITE32_MEMBER(i_reset_w) { m_i_reset = data; }
+ enum control_mask : u32
+ {
+ CNTL_EP = 0x00000001, // enable prefetch
+ CNTL_EWCW = 0x00000002, // enable watch cpu writes
+ CNTL_EWIW = 0x00000004, // enable watch i/o writes
+ CNTL_EWIR = 0x00000008, // enable watch i/o reads
+ CNTL_UST = 0x00000030, // unmapped system tag
+ CNTL_CV = 0x00000100, // clear valid
+ CNTL_ATE = 0x00000200, // alignment trap enable
+ CNTL_CID = 0xff000000 // cammu id
+ };
+
+ enum ust_mask : u32
+ {
+ UST_0 = 0x00000000, // private, write-through, main memory space
+ UST_1 = 0x00000010, // shared, write-through, main memory space
+ UST_2 = 0x00000020, // private, copy-back, main memory space
+ UST_3 = 0x00000030 // noncacheable, main memory space
+ };
+
+ enum control_cid_mask : u32
+ {
+ CID_C3 = 0x00000000 // unknown
+ };
+
+ DECLARE_READ32_MEMBER(s_pdo_r) const { return m_s_pdo; }
+ DECLARE_WRITE32_MEMBER(s_pdo_w) { m_s_pdo = ((m_s_pdo & ~mem_mask) | (data & mem_mask)) & PDO_MASK; }
+ DECLARE_READ32_MEMBER(u_pdo_r) const { return m_u_pdo; }
+ DECLARE_WRITE32_MEMBER(u_pdo_w) { m_u_pdo = ((m_u_pdo & ~mem_mask) | (data & mem_mask)) & PDO_MASK; }
+ DECLARE_READ32_MEMBER(fault_r) const { return m_fault; }
+ DECLARE_WRITE32_MEMBER(fault_w) { m_fault = data; }
+ DECLARE_READ32_MEMBER(control_r) const { return m_control; }
+ DECLARE_WRITE32_MEMBER(control_w) { m_control = ((m_control & (~mem_mask | CNTL_CID)) | (data & (mem_mask & ~CNTL_CID))); }
+ DECLARE_READ32_MEMBER(reset_r) const { return m_reset; }
+ DECLARE_WRITE32_MEMBER(reset_w) { m_reset = data; }
- DECLARE_WRITE32_MEMBER(g_s_pdo_w) { d_s_pdo_w(space, offset, data, mem_mask); i_s_pdo_w(space, offset, data, mem_mask); }
- DECLARE_WRITE32_MEMBER(g_u_pdo_w) { d_u_pdo_w(space, offset, data, mem_mask); i_u_pdo_w(space, offset, data, mem_mask); }
- DECLARE_WRITE32_MEMBER(g_fault_w) { d_fault_w(space, offset, data, mem_mask); i_fault_w(space, offset, data, mem_mask); }
- DECLARE_WRITE32_MEMBER(g_control_w) { d_control_w(space, offset, data, mem_mask); i_control_w(space, offset, data, mem_mask); }
- DECLARE_WRITE32_MEMBER(g_reset_w) { d_reset_w(space, offset, data, mem_mask); i_reset_w(space, offset, data, mem_mask); }
+ // global methods - relay to each linked device
+ DECLARE_WRITE32_MEMBER(g_s_pdo_w) { for (cammu_c3_device *dev : m_linked) dev->s_pdo_w(space, offset, data, mem_mask); }
+ DECLARE_WRITE32_MEMBER(g_u_pdo_w) { for (cammu_c3_device *dev : m_linked) dev->u_pdo_w(space, offset, data, mem_mask); }
+ DECLARE_WRITE32_MEMBER(g_fault_w) { for (cammu_c3_device *dev : m_linked) dev->fault_w(space, offset, data, mem_mask); }
+ DECLARE_WRITE32_MEMBER(g_control_w) { for (cammu_c3_device *dev : m_linked) dev->control_w(space, offset, data, mem_mask); }
+ DECLARE_WRITE32_MEMBER(g_reset_w) { for (cammu_c3_device *dev : m_linked) dev->reset_w(space, offset, data, mem_mask); }
protected:
- u32 get_pte(u32 va, int user, bool data) override;
+ virtual void device_reset() override;
+ virtual void device_start() override;
+
+ virtual bool get_access(const access_t mode, const u32 pte, const u32 ssw) const override;
+ virtual bool get_alignment() const override { return m_control & CNTL_ATE; }
+ virtual u32 get_pdo(const bool user) const override { return user ? m_u_pdo : m_s_pdo; }
+ virtual address_space *get_ust_space() const override { return m_main_space; }
+
+ virtual void set_fault_address(const u32 va) override { m_fault = va; }
private:
- u32 m_d_s_pdo;
- u32 m_d_u_pdo;
- u32 m_d_fault;
- u32 m_d_control;
- u32 m_d_reset;
- u32 m_i_s_pdo;
- u32 m_i_u_pdo;
- u32 m_i_fault;
- u32 m_i_control;
- u32 m_i_reset;
+ enum c3_access_t : u8
+ {
+ N = 0, // no access
+ R = 1, // read permitted
+ W = 2, // write permitted
+ RW = 3, // read and write permitted
+ E = 4, // execute permitted
+ RE = 5, // read and execute permitted
+ RWE = 7 // read, write and execute permitted
+ };
+
+ static const u8 i_cammu_column[];
+ static const u8 d_cammu_column[];
+ static const c3_access_t cammu_matrix[][16];
+
+ u32 m_s_pdo;
+ u32 m_u_pdo;
+ u32 m_fault;
+ u32 m_control;
+ u32 m_reset;
+
+ std::vector<cammu_c3_device *> m_linked;
};
// device type definitions
diff --git a/src/mame/machine/interpro_ioga.cpp b/src/mame/machine/interpro_ioga.cpp
index e790a3ab269..0d6a0dccfbb 100644
--- a/src/mame/machine/interpro_ioga.cpp
+++ b/src/mame/machine/interpro_ioga.cpp
@@ -165,6 +165,7 @@ DEFINE_DEVICE_TYPE(SAPPHIRE_IOGA, sapphire_ioga_device, "ioga_s", "I/O Gate Arra
interpro_ioga_device::interpro_ioga_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, type, tag, owner, clock)
+ , m_memory_tag(nullptr)
, m_memory_space(nullptr)
, m_out_nmi_func(*this)
, m_out_irq_func(*this)
@@ -192,13 +193,22 @@ sapphire_ioga_device::sapphire_ioga_device(const machine_config &mconfig, const
{
}
+void interpro_ioga_device::static_set_memory(device_t &device, const char *const tag, const int spacenum)
+{
+ interpro_ioga_device &ioga = dynamic_cast<interpro_ioga_device &>(device);
+
+ ioga.m_memory_tag = tag;
+ ioga.m_memory_spacenum = spacenum;
+}
+
void interpro_ioga_device::device_start()
{
- // TODO: parameterise the cammu name and space number
- // grab the main memory space from the mmu so we can do DMA to/from it
- device_memory_interface *mmu;
- siblingdevice("mmu")->interface(mmu);
- m_memory_space = &mmu->space(0);
+ assert_always(m_memory_tag != nullptr, "memory tag and address space number must be configured");
+
+ // get the memory space
+ device_memory_interface *memory;
+ siblingdevice(m_memory_tag)->interface(memory);
+ m_memory_space = &memory->space(m_memory_spacenum);
// resolve callbacks
m_out_nmi_func.resolve();
diff --git a/src/mame/machine/interpro_ioga.h b/src/mame/machine/interpro_ioga.h
index 55573a1d6ee..3d7795cd259 100644
--- a/src/mame/machine/interpro_ioga.h
+++ b/src/mame/machine/interpro_ioga.h
@@ -29,7 +29,8 @@
#define MCFG_INTERPRO_IOGA_ETH_CA_CB(_ca) \
devcb = &interpro_ioga_device::static_set_eth_ca_callback(*device, DEVCB_##_ca);
-#define MCFG_INTERPRO_IOGA_DMA_BUS(_mmu, _space)
+#define MCFG_INTERPRO_IOGA_MEMORY(_tag, _spacenum) \
+ interpro_ioga_device::static_set_memory(*device, _tag, _spacenum);
class interpro_ioga_device : public device_t
{
@@ -115,6 +116,8 @@ public:
template<class _Object> static devcb_base &static_set_fdc_tc_callback(device_t &device, _Object object) { return downcast<interpro_ioga_device &>(device).m_fdc_tc_func.set_callback(object); }
template<class _Object> static devcb_base &static_set_eth_ca_callback(device_t &device, _Object object) { return downcast<interpro_ioga_device &>(device).m_eth_ca_func.set_callback(object); }
+ static void static_set_memory(device_t &device, const char *const tag, const int spacenum);
+
virtual DECLARE_ADDRESS_MAP(map, 32) = 0;
// interrupt request lines
@@ -317,6 +320,8 @@ protected:
virtual void device_reset() override;
virtual ioport_constructor device_input_ports() const override;
+ const char *m_memory_tag;
+ int m_memory_spacenum;
address_space *m_memory_space;
// callbacks