summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/rosetta.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/machine/rosetta.cpp')
-rw-r--r--src/mame/machine/rosetta.cpp1202
1 files changed, 1202 insertions, 0 deletions
diff --git a/src/mame/machine/rosetta.cpp b/src/mame/machine/rosetta.cpp
new file mode 100644
index 00000000000..12b6d5ec036
--- /dev/null
+++ b/src/mame/machine/rosetta.cpp
@@ -0,0 +1,1202 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * IBM Rosetta MMU.
+ *
+ * Sources:
+ * - http://bitsavers.org/pdf/ibm/pc/rt/75X0232_RT_PC_Technical_Reference_Volume_1_Jun87.pdf
+ *
+ * TODO:
+ * - tighten error detection/reporting
+ * - external device and multiple errors
+ * - ras diagnostic modes
+ * - advanced/enhanced variants
+ * - ram holes
+ */
+
+#include "emu.h"
+#include "rosetta.h"
+
+#define LOG_GENERAL (1U << 0)
+#define LOG_TLB (1U << 1)
+#define LOG_RELOAD (1U << 2)
+#define LOG_ECC (1U << 3)
+#define LOG_INVALID (1U << 4)
+
+//#define VERBOSE (LOG_GENERAL|LOG_TLB|LOG_RELOAD|LOG_ECC|LOG_INVALID)
+#include "logmacro.h"
+
+enum roms_mask : u32
+{
+ ROMS_SIZE = 0x0000'000f, // rom size
+ ROMS_START = 0x0000'0ff0, // rom address
+ ROMS_P = 0x0000'1000, // parity enable
+};
+
+enum segment_mask : u32
+{
+ SEGMENT_K = 0x0000'0001, // key
+ SEGMENT_S = 0x0000'0002, // special
+ SEGMENT_ID = 0x0000'3ffc, // identifier
+ SEGMENT_I = 0x0000'4000, // i/o access protect
+ SEGMENT_R = 0x0000'8000, // system processor access protect
+ SEGMENT_P = 0x0001'0000, // present
+};
+
+enum rams_mask : u32
+{
+ RAMS_SIZE = 0x0000'000f, // ram size
+ RAMS_START = 0x0000'0ff0, // ram address
+};
+
+enum tcr_mask : u32
+{
+ TCR_HIB = 0x0000'00ff, // hat/ipt base address
+ TCR_S = 0x0000'0100, // page size (1=4k pages)
+ TCR_P = 0x0000'0200, // r/c array parity enable (advanced/enhanced only?)
+ TCR_R = 0x0000'0400, // enable interrupt on successful tlb reload
+ TCR_C = 0x0000'0800, // enable interrupt on correctable ecc error
+ TCR_I = 0x0000'1000, // terminate long ipt search
+ TCR_D = 0x0000'2000, // enable ras diagnostic mode
+ TCR_E = 0x0000'4000, // interrupt on successful parity error retry
+ TCR_V = 0x0000'8000, // segment register zero virtual equal to real
+};
+
+enum mer_mask : u32
+{
+ MER_D = 0x0000'0001, // data
+ MER_P = 0x0000'0002, // protection
+ MER_S = 0x0000'0004, // tlb specification
+ MER_F = 0x0000'0008, // page fault
+ MER_M = 0x0000'0010, // multiple exception
+ MER_E = 0x0000'0020, // external device exception
+ MER_I = 0x0000'0040, // ipt specification error
+ MER_W = 0x0000'0080, // write to rom
+ MER_R = 0x0000'0100, // r/c array parity error (advanced/enhanced only?)
+ MER_T = 0x0000'0200, // successful tlb reload
+ MER_C = 0x0000'0400, // correctable ecc error
+ MER_U = 0x0000'0800, // uncorrectable memory error
+ MER_L = 0x0000'1000, // access type (1=load)
+ MER_O = 0x0000'2000, // invalid i/o address
+ MER_B = 0x0000'4000, // invalid memory address
+ MER_N = 0x0000'8000, // processor channel nakd
+ MER_A = 0x0001'0000, // processor channel ackd
+ MER_V = 0x0002'0000, // segment protection violation
+};
+
+enum trar_mask : u32
+{
+ TRAR_A = 0x00ff'ffff, // real memory address
+ TRAR_I = 0x8000'0000, // invalid
+};
+
+enum rmdr_mask : u32
+{
+ RMDR_CHECK = 0x0000'ff00, // array check bits
+ RMDR_ALT = 0x0000'00ff, // alternate check bits
+};
+
+enum tlb_mask : u32
+{
+ // field 0
+ TLB_SEG = 0x1ffe'0000, // segment identifier
+ TLB_AT2K = 0x1fff'fff0, // address tag (2k page)
+ TLB_AT4K = 0x1fff'ffe0, // address tag (4k page)
+
+ // field 1
+ TLB_KEY = 0x0000'0003, // key bits
+ TLB_V = 0x0000'0004, // valid bit
+ TLB_RPN2K = 0x0000'fff8, // real page number (2k page)
+ TLB_RPN4K = 0x0000'fff0, // real page number (4k page)
+
+ // field 2
+ TLB_LB = 0x0000'ffff, // lock bits
+ TLB_TID = 0x00ff'0000, // transaction identifier
+ TLB_W = 0x0100'0000, // write bit
+
+};
+
+enum hat_mask : u32
+{
+ HAT_AT2K = 0x1fff'ffff, // address tag (2k page)
+ HAT_AT4K = 0x1fff'fffe, // address tag (4k page)
+ HAT_KEY = 0xc000'0000, // key
+
+ HAT_IPTP = 0x0000'1fff, // ipt pointer
+ HAT_L = 0x0000'8000, // last
+ HAT_HATP = 0x1fff'0000, // hat pointer
+ HAT_E = 0x8000'0000, // empty
+
+ HAT_LB = 0x0000'ffff, // lock bits
+ HAT_TID = 0x00ff'0000, // transaction identifier
+ HAT_W = 0x0100'0000, // write protect
+};
+
+enum rca_mask : u8
+{
+ RCA_C = 0x01, // change
+ RCA_R = 0x02, // reference
+};
+
+enum reg : unsigned
+{
+ IOBA = 0, // i/o base address
+ MER = 1, // memory exception
+ MEAR = 2, // memory exception address
+ TRAR = 3, // translated real address
+ TID = 4, // transaction identifier
+ TCR = 5, // translation control
+ RAMS = 6, // ram specification
+ ROMS = 7, // rom specification
+ RMDR = 8, // ras mode diagnostic
+};
+
+static char const *const control_names[] = { "IOBA", "MER", "MEAR", "TRAR", "TID", "TCR", "RAMS", "ROMS", "RMDR" };
+
+static u8 const ecc_bits[] =
+{
+ 0xa8, 0x68, 0xa4, 0x64, 0xa2, 0x62, 0xa1, 0x61,
+ 0x98, 0x58, 0x94, 0x54, 0x92, 0x52, 0x91, 0x51,
+ 0x8a, 0x89, 0x4a, 0x49, 0x2a, 0x29, 0x1a, 0x19,
+ 0x86, 0x85, 0x46, 0x45, 0x26, 0x25, 0x16, 0x15,
+};
+
+// 7-segment diagnostic led
+static u8 const led_pattern[16] =
+{
+ 0x3f, 0x06, 0x5b, 0x4f,
+ 0x66, 0x6d, 0x7d, 0x07,
+ 0x7f, 0x6f, 0x77, 0x7c,
+ 0x39, 0x5e, 0x79, 0x00,
+};
+
+DEFINE_DEVICE_TYPE(ROSETTA, rosetta_device, "rosetta", "IBM Rosetta")
+
+ALLOW_SAVE_TYPE(rosetta_device::mear_state)
+
+rosetta_device::rosetta_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, ram_size ram)
+ : device_t(mconfig, ROSETTA, tag, owner, clock)
+ , rsc_cpu_interface(mconfig, *this)
+ , m_mem_space(*this, finder_base::DUMMY_TAG, -1, 32)
+ , m_rom(*this, finder_base::DUMMY_TAG)
+ , m_leds(*this, "led%u", 0U)
+ , m_out_pchk(*this)
+ , m_out_mchk(*this)
+ , m_segment{}
+ , m_control{}
+ , m_mear_lock(UNLOCKED)
+ , m_rmdr_lock(false)
+ , m_led_lock(true)
+ , m_pchk_state(false)
+ , m_tlb{ {}, {} }
+ , m_tlb_lru(0)
+ , m_ram_size(ram)
+{
+}
+
+void rosetta_device::device_validity_check(validity_checker &valid) const
+{
+ if (!m_ram_size)
+ osd_printf_error("invalid ram size\n");
+}
+
+void rosetta_device::device_start()
+{
+ m_out_pchk.resolve_safe();
+ m_out_mchk.resolve_safe();
+ m_leds.resolve();
+
+ save_item(NAME(m_segment));
+ save_item(NAME(m_control));
+ save_item(NAME(m_mear_lock));
+ save_item(NAME(m_rmdr_lock));
+ save_item(NAME(m_led_lock));
+ save_item(NAME(m_pchk_state));
+
+ save_item(STRUCT_MEMBER(m_tlb, field0));
+ save_item(STRUCT_MEMBER(m_tlb, field1));
+ save_item(STRUCT_MEMBER(m_tlb, field2));
+ save_item(NAME(m_tlb_lru));
+
+ save_pointer(NAME(m_ram), m_ram_size);
+ save_pointer(NAME(m_ecc), m_ram_size);
+ save_pointer(NAME(m_rca), 2048);
+
+ config_tlb();
+
+ m_ram = std::make_unique<u32[]>(m_ram_size);
+ m_ecc = std::make_unique<u8[]>(m_ram_size);
+ m_rca = std::make_unique<u8[]>(2048);
+
+ offs_t const mask = m_rom.bytes() - 1;
+ m_mem_space->install_rom(0, mask, 0xffffff & ~mask, m_rom);
+ m_mem_space->cache(m_mem);
+}
+
+void rosetta_device::device_reset()
+{
+ m_mear_lock = UNLOCKED;
+ m_rmdr_lock = false;
+ m_led_lock = true;
+
+ set_pchk(false);
+ set_mchk(false);
+}
+
+void rosetta_device::device_post_load()
+{
+ config_tlb();
+}
+
+bool rosetta_device::ior(u32 address, u32 &data)
+{
+ if ((address >> 16) == u8(m_control[IOBA]))
+ {
+ u16 const offset = u16(address);
+
+ switch (offset)
+ {
+ case 0x00: case 0x01: case 0x02: case 0x03:
+ case 0x04: case 0x05: case 0x06: case 0x07:
+ case 0x08: case 0x09: case 0x0a: case 0x0b:
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ data = segment_r(offset & 0xf);
+ return true;
+
+ case 0x10: case 0x11: case 0x12: case 0x13:
+ case 0x14: case 0x15: case 0x16: case 0x17:
+ case 0x18:
+ data = control_r(offset & 0xf);
+ return true;
+
+ default:
+ if (offset - 0x20U < 0x60U)
+ {
+ data = tlb_r(offset - 0x20U);
+ return true;
+ }
+ else if (offset - 0x1000U < 0x2000U)
+ {
+ data = rca_r(offset - 0x1000U);
+ return true;
+ }
+ break;
+ }
+ }
+ else if (address == 0x80'8000U)
+ {
+ data = m_control[IOBA];
+ return true;
+ }
+
+ LOGMASKED(LOG_INVALID, "ior invalid address 0x%06x (%s)\n", address, machine().describe_context());
+ m_control[MER] |= MER_O;
+ set_mear(address, MEMORY);
+
+ return bool(m_control[TCR] & TCR_D);
+}
+
+bool rosetta_device::iow(u32 address, u32 data)
+{
+ if ((address >> 16) == u8(m_control[IOBA]))
+ {
+ u16 const offset = u16(address);
+
+ switch (offset)
+ {
+ case 0x00: case 0x01: case 0x02: case 0x03:
+ case 0x04: case 0x05: case 0x06: case 0x07:
+ case 0x08: case 0x09: case 0x0a: case 0x0b:
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ segment_w(offset & 0xf, data);
+ return true;
+
+ case 0x10: case 0x11: case 0x12: case 0x13:
+ case 0x14: case 0x15: case 0x16: case 0x17:
+ case 0x18:
+ control_w(offset & 0xf, data);
+ return true;
+
+ case 0x80: tlb_inv_all(data); return true;
+ case 0x81: tlb_inv_segment(data); return true;
+ case 0x82: tlb_inv_address(data); return true;
+ case 0x83: compute_address(data); return true;
+
+ default:
+ if (offset - 0x20U < 0x60U)
+ {
+ tlb_w(offset - 0x20U, data);
+ return true;
+ }
+ else if (offset - 0x1000U < 0x2000U)
+ {
+ rca_w(offset - 0x1000U, data);
+ return true;
+ }
+ break;
+ }
+ }
+ else if (address == 0x80'8000U)
+ {
+ m_control[IOBA] = data;
+ return true;
+ }
+
+ LOGMASKED(LOG_INVALID, "iow invalid address 0x%06x data 0x%08x (%s)\n", address, data, machine().describe_context());
+ m_control[MER] |= MER_O;
+ set_mear(address, MEMORY);
+ set_pchk(true);
+
+ return true;
+}
+
+bool rosetta_device::translate(u32 &address, bool system_processor, bool store)
+{
+ unsigned const segment = address >> 28;
+
+ // segment present
+ if (!(m_segment[segment] & SEGMENT_P))
+ fatalerror("rosetta_device::translate() segment %d absent (%s)\n", segment, machine().describe_context());
+
+ // segment access protection
+ if (system_processor && (m_segment[segment] & SEGMENT_R))
+ {
+ m_control[MER] |= MER_V;
+
+ return false;
+ }
+ else if (!system_processor && (m_segment[segment] & SEGMENT_I))
+ return false;
+
+ // segment zero virtual equal to real
+ if ((m_control[TCR] & TCR_V) && !segment)
+ {
+ address &= 0x00ff'ffffU;
+
+ return true;
+ }
+
+ u64 const virtual_address = (u64(m_segment[segment] & SEGMENT_ID) << 26) | (address & 0x0fff'ffffU);
+
+ tlb_entry const te = tlb_search(virtual_address, m_segment[segment] & SEGMENT_S);
+ if (!(te.field1 & TLB_V))
+ return false;
+
+ u32 const real_page = (te.field1 << 8) & m_page_mask;
+
+ // special segment lockbit processing
+ if (m_segment[segment] & SEGMENT_S)
+ {
+ // check transaction identifier
+ if ((m_control[TID] & 0xff) != ((te.field2 & TLB_TID) >> 16))
+ {
+ if (system_processor)
+ m_control[MER] |= MER_D;
+
+ return false;
+ }
+
+ // check write bit, lock bit and operation
+ bool const lockbit = BIT(te.field2, 15 - ((address >> (m_page_shift - 4)) & 15));
+ if (te.field2 & TLB_W)
+ {
+ if (!lockbit && store)
+ {
+ if (system_processor)
+ m_control[MER] |= MER_D;
+
+ return false;
+ }
+ }
+ else
+ {
+ if (!lockbit || store)
+ {
+ if (system_processor)
+ m_control[MER] |= MER_D;
+
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // non-special segment memory protection processing
+ switch (te.field1 & TLB_KEY)
+ {
+ case 0:
+ // key 0 fetch-protected
+ if (m_segment[segment] & SEGMENT_K)
+ {
+ if (system_processor)
+ m_control[MER] |= MER_P;
+
+ return false;
+ }
+ break;
+ case 1:
+ // key 0 read/write
+ if ((m_segment[segment] & SEGMENT_K) && store)
+ {
+ if (system_processor)
+ m_control[MER] |= MER_P;
+
+ return false;
+ }
+ break;
+ case 2:
+ // public read/write
+ break;
+ case 3:
+ // public read-only
+ if (store)
+ {
+ if (system_processor)
+ m_control[MER] |= MER_P;
+
+ return false;
+ }
+ break;
+ }
+ }
+
+ address = real_page | (address & ~m_page_mask);
+
+ return true;
+}
+
+rosetta_device::tlb_entry rosetta_device::tlb_search(u64 const virtual_address, bool const special)
+{
+ unsigned const tlb_index = (virtual_address >> m_page_shift) & 15;
+ tlb_entry te = { 0U, 0U, 0U };
+
+ // check first set
+ if ((m_tlb[tlb_index][0].field1 & TLB_V) && !((m_tlb[tlb_index][0].field0 ^ (virtual_address >> 11)) & m_atag_mask))
+ {
+ te = m_tlb[tlb_index][0];
+
+ // tlb set 1 is least-recently used
+ m_tlb_lru |= (1U << tlb_index);
+ }
+
+ // check second set
+ if ((m_tlb[tlb_index][1].field1 & TLB_V) && !((m_tlb[tlb_index][1].field0 ^ (virtual_address >> 11)) & m_atag_mask))
+ {
+ if (!(te.field1 & TLB_V))
+ {
+ te = m_tlb[tlb_index][1];
+
+ // tlb set 0 is least-recently used
+ m_tlb_lru &= ~(1U << tlb_index);
+ }
+ else
+ {
+ // double tlb set hit
+ m_control[MER] |= MER_S;
+
+ fatalerror("rosetta_device::tlb_search() double tlb hit 0x%010x (%s)\n",
+ virtual_address, machine().describe_context());
+
+ if (!(m_control[TCR] & TCR_D))
+ {
+ set_mchk(true);
+
+ return tlb_entry{ 0U, 0U, 0U };
+ }
+ else
+ // if store, address is logical OR of both real page numbers
+ // TODO: what about load?
+ te.field1 |= m_tlb[tlb_index][1].field1;
+ }
+ }
+
+ // handle tlb miss
+ if (!(te.field1 & TLB_V))
+ {
+ unsigned const lru = BIT(m_tlb_lru, tlb_index);
+
+ u32 const mer = tlb_reload(te, virtual_address, special);
+ if (mer & (MER_T | MER_I | MER_F))
+ {
+ // FIXME: set I&F only for system processor
+ m_control[MER] |= mer;
+
+ return te;
+ }
+ else
+ m_tlb[tlb_index][lru] = te;
+ }
+
+ return te;
+}
+
+u32 rosetta_device::tlb_reload(rosetta_device::tlb_entry &tlb_entry, u64 const virtual_address, bool special)
+{
+ // compute hat index
+ u16 hat_offset = (((virtual_address >> 26) & SEGMENT_ID) ^ (virtual_address >> (m_page_shift - 2))) & m_hat_mask;
+
+ // fetch hat entry
+ u32 hat_entry = m_ram[m_hat_base + hat_offset + 1];
+
+ if (!machine().side_effects_disabled())
+ LOGMASKED(LOG_RELOAD, "reload 0x%010x hat base 0x%x index 0x%x entry 0x%08x\n",
+ virtual_address, m_hat_base, hat_offset, hat_entry);
+
+ // set reference bit
+ m_rca[(m_hat_base + hat_offset) >> 11] |= (RCA_R << (((m_hat_base + hat_offset) >> 8) & 6));
+
+ if (hat_entry & HAT_E)
+ return MER_F;
+
+ // compute ipt entry pointer
+ hat_offset = ((hat_entry & HAT_HATP) >> 14);
+ u32 const address = (virtual_address >> 11) & m_atag_mask;
+
+ // ipt search
+ for (unsigned count = 0; count < 1024; count++)
+ {
+ // fetch ipt entry
+ u32 const ipt_entry = m_ram[m_hat_base + hat_offset + 0];
+
+ // set reference bit
+ m_rca[(m_hat_base + hat_offset) >> 11] |= (RCA_R << (((m_hat_base + hat_offset) >> 8) & 6));
+
+ if ((ipt_entry & m_atag_mask) == address)
+ {
+ // reload tlb
+ tlb_entry.field0 = ipt_entry & m_atag_mask;
+ tlb_entry.field1 = (hat_offset << 1) | TLB_V | (ipt_entry >> 30);
+
+ if (special)
+ tlb_entry.field2 = m_ram[m_hat_base + hat_offset + 2];
+
+ if (!machine().side_effects_disabled())
+ LOGMASKED(LOG_RELOAD, "reload complete count %d f0 0x%08x f1 0x%08x f2 0x%08x\n",
+ count, tlb_entry.field0, tlb_entry.field1, tlb_entry.field2);
+
+ if (m_control[TCR] & TCR_R)
+ return MER_T;
+
+ return 0;
+ }
+
+ // terminate long ipt search
+ if ((m_control[TCR] & TCR_I) && (count == 127))
+ {
+ LOGMASKED(LOG_RELOAD, "reload long search abort\n");
+
+ return MER_I;
+ }
+
+ // fetch next hat entry
+ hat_entry = m_ram[m_hat_base + hat_offset + 1];
+ if (hat_entry & HAT_L)
+ {
+ LOGMASKED(LOG_RELOAD, "reload fault\n");
+
+ return MER_F;
+ }
+
+ // select next ipt entry
+ hat_offset = (hat_entry & HAT_IPTP) << 2;
+ }
+
+ fatalerror("rosetta_device::reload() endless loop detected\n");
+}
+
+void rosetta_device::set_mear(u32 const address, mear_state lock)
+{
+ if (m_mear_lock == LOCKED)
+ return;
+
+ if (m_mear_lock == MEMORY && lock == MEMORY)
+ return;
+
+ m_control[MEAR] = address;
+ m_mear_lock = lock;
+}
+
+void rosetta_device::set_rmdr(u8 const ecc, bool lock)
+{
+ if (m_rmdr_lock)
+ return;
+
+ m_control[RMDR] = (m_control[RMDR] & ~RMDR_CHECK) | (ecc << 8);
+ m_rmdr_lock = lock;
+}
+
+u32 rosetta_device::segment_r(offs_t offset)
+{
+ return m_segment[offset];
+}
+
+void rosetta_device::segment_w(offs_t offset, u32 data)
+{
+ LOG("segment_w 0x%x data 0x%x (%s)\n", offset, data, machine().describe_context());
+
+ m_segment[offset] = data;
+}
+
+u32 rosetta_device::control_r(offs_t offset)
+{
+ u32 data = 0;
+
+ switch (offset)
+ {
+ case MER:
+ data = m_control[offset];
+ set_pchk(false);
+ break;
+
+ case MEAR:
+ m_mear_lock = UNLOCKED;
+ data = m_control[offset];
+ break;
+
+ case RMDR:
+ m_mear_lock = UNLOCKED;
+ m_rmdr_lock = false;
+ data = m_control[offset];
+ break;
+
+ default:
+ data = m_control[offset];
+ break;
+ }
+
+ LOG("control_r %s data 0x%08x (%s)\n", control_names[offset], data, machine().describe_context());
+
+ return data;
+}
+
+void rosetta_device::control_w(offs_t offset, u32 data)
+{
+ bool reconfig_tlb = false;
+ bool reconfig_map = false;
+
+ LOG("control_w %s data 0x%08x (%s)\n", control_names[offset], data, machine().describe_context());
+
+ switch (offset)
+ {
+ case MEAR:
+ m_mear_lock = UNLOCKED;
+ m_control[offset] = data;
+ break;
+
+ case TCR:
+ reconfig_tlb = (m_control[offset] ^ data) & (TCR_S | TCR_HIB);
+ m_control[offset] = data;
+ break;
+
+ case RAMS:
+ reconfig_tlb = m_control[offset] ^ data;
+ reconfig_map = m_control[offset] ^ data;
+ m_control[offset] = data;
+ break;
+
+ case ROMS:
+ reconfig_map = m_control[offset] ^ data;
+ m_control[offset] = data;
+ break;
+
+ case RMDR:
+ // only alternate check bits are writeable
+ m_control[offset] = (m_control[offset] & ~RMDR_ALT) | (data & RMDR_ALT);
+ break;
+
+ default:
+ m_control[offset] = data;
+ break;
+ }
+
+ if (reconfig_map)
+ config_map();
+
+ if (reconfig_tlb)
+ config_tlb();
+}
+
+u32 rosetta_device::tlb_r(offs_t offset)
+{
+ unsigned const tlb_set = BIT(offset, 4);
+ u32 data = 0;
+
+ switch (offset & 0x60)
+ {
+ case 0x00: data = m_tlb[offset & 0xf][tlb_set].field0; break;
+ case 0x20: data = m_tlb[offset & 0xf][tlb_set].field1; break;
+ case 0x40: data = m_tlb[offset & 0xf][tlb_set].field2; break;
+ }
+
+ LOG("tlb_r offset %x data %x\n", offset, data);
+
+ return data;
+}
+
+void rosetta_device::tlb_w(offs_t offset, u32 data)
+{
+ unsigned const tlb_set = BIT(offset, 4);
+
+ switch (offset & 0x60)
+ {
+ case 0x00: m_tlb[offset & 0xf][tlb_set].field0 = data; break;
+ case 0x20: m_tlb[offset & 0xf][tlb_set].field1 = data; break;
+ case 0x40: m_tlb[offset & 0xf][tlb_set].field2 = data; break;
+ }
+
+ LOG("tlb_w offset %x data %x\n", offset, data);
+}
+
+u32 rosetta_device::rca_r(offs_t offset)
+{
+ unsigned const shift = (offset & 3) * 2;
+
+ if (!m_led_lock)
+ {
+ LOG("led 0x%02x (%s)\n", u8(offset), machine().describe_context());
+
+ m_leds[0] = led_pattern[(offset >> 0) & 15];
+ m_leds[1] = led_pattern[(offset >> 4) & 15];
+ }
+
+ return (m_rca[offset >> 2] >> shift) & 3;
+}
+
+void rosetta_device::rca_w(offs_t offset, u32 data)
+{
+ unsigned const shift = (offset & 3) * 2;
+
+ m_rca[offset >> 2] &= ~(3 << shift);
+ m_rca[offset >> 2] |= (data & 3) << shift;
+
+ m_led_lock = true;
+}
+
+u32 rosetta_device::rom_r(offs_t offset, u32 mem_mask)
+{
+ if (!machine().side_effects_disabled())
+ m_led_lock = false;
+
+ return m_rom[offset];
+}
+
+void rosetta_device::rom_w(offs_t offset, u32 data, u32 mem_mask)
+{
+ LOGMASKED(LOG_INVALID, "rom_w invalid 0x%06x data 0x%08x mask 0x%08x (%s)\n",
+ offset << 2, data, mem_mask, machine().describe_context());
+
+ if (!machine().side_effects_disabled())
+ m_control[MER] |= MER_W;
+}
+
+void rosetta_device::ram_w(offs_t offset, u32 data, u32 mem_mask)
+{
+ if (offset < m_ram_size)
+ {
+ m_ram[offset] = (m_ram[offset] & ~mem_mask) | (data & mem_mask);
+
+ // set reference and change bits
+ if (!machine().side_effects_disabled())
+ m_rca[offset >> 11] |= ((RCA_R | RCA_C) << ((offset >> 8) & 6));
+
+ m_ecc[offset] = (m_control[TCR] & TCR_D) ? (m_control[RMDR] & RMDR_ALT) : compute_ecc(m_ram[offset]);
+ }
+ else
+ {
+ LOGMASKED(LOG_INVALID, "ram_w invalid 0x%06x data 0x%08x mask 0x%08x (%s)\n",
+ offset << 2, data, mem_mask, machine().describe_context());
+
+ m_control[MER] |= MER_B;
+ }
+}
+
+u32 rosetta_device::ram_r(offs_t offset, u32 mem_mask)
+{
+ if (offset < m_ram_size)
+ {
+ u32 data = m_ram[offset];
+
+ if (!machine().side_effects_disabled())
+ {
+ // set reference bit
+ m_rca[offset >> 11] |= (RCA_R << ((offset >> 8) & 6));
+
+ u8 const ecc = m_ecc[offset];
+
+ switch (check_ecc(data, ecc))
+ {
+ case 1:
+ // correctable error
+ if ((m_control[TCR] & TCR_C) || (m_control[TCR] & TCR_D))
+ {
+ if (!(m_control[MER] & MER_U))
+ m_control[MER] |= MER_C;
+ set_rmdr(ecc, true);
+ }
+ break;
+ case 2:
+ // uncorrectable error
+ m_control[MER] |= MER_U;
+ set_rmdr(ecc, true);
+ break;
+ }
+ }
+
+ return data;
+ }
+ else
+ {
+ LOGMASKED(LOG_INVALID, "ram_r invalid 0x%06x mask 0x%08x (%s)\n",
+ offset << 2, mem_mask, machine().describe_context());
+
+ m_control[MER] |= MER_B;
+
+ return 0;
+ }
+}
+
+u8 rosetta_device::compute_ecc(u32 const data) const
+{
+ u8 result = 0;
+
+ for (unsigned i = 0; i < ARRAY_LENGTH(ecc_bits); i++)
+ result ^= BIT(data, 31 - i) ? ecc_bits[i] : 0;
+
+ return result;
+}
+
+unsigned rosetta_device::check_ecc(u32 &data, u8 const ecc) const
+{
+ u8 const error = compute_ecc(data) ^ ecc;
+
+ if (error)
+ {
+ for (unsigned i = 0; i < ARRAY_LENGTH(ecc_bits); i++)
+ {
+ if (error == ecc_bits[i])
+ {
+ LOGMASKED(LOG_ECC, "check_ecc single-bit error 0x%08x ecc 0x%02x error 0x%02x\n", data, ecc, error);
+
+ // correct error
+ data ^= (0x8000'0000U >> i);
+
+ return 1;
+ }
+ }
+
+ // multiple-bit error
+ LOGMASKED(LOG_ECC, "check_ecc multiple-bit error 0x%08x ecc 0x%02x error 0x%02x\n", data, ecc, error);
+ return 2;
+ }
+ else
+ return 0;
+}
+
+void rosetta_device::config_map()
+{
+ // unmap everything
+ m_mem_space->unmap_readwrite(0x000000, 0xffffff);
+
+ // map rom
+ if (m_control[ROMS] & ROMS_SIZE)
+ {
+ unsigned const shift = (m_control[ROMS] & ROMS_SIZE) > 7 ? (m_control[ROMS] & ROMS_SIZE) - 7 : 0;
+ size_t const size = 0x10000ULL << shift;
+ unsigned const factor = (m_control[ROMS] & ROMS_START) >> (4 + shift);
+
+ LOG("config rom size %dk map 0x%06x-0x%06x\n", size >> 10, size * factor, size * factor + size - 1);
+
+ /*
+ * IPL code assumes ROM is mirrored within the configured range, making it
+ * difficult to identify under what circumstances an invalid memory access to
+ * the configured ROM address range would be reported.
+ */
+ size_t const available = std::min(size, m_rom.bytes());
+ m_mem_space->install_readwrite_handler(size * factor, size * factor + available - 1, available - 1, (size - 1) & ~(available - 1), 0,
+ read32s_delegate(*this, FUNC(rosetta_device::rom_r)),
+ write32s_delegate(*this, FUNC(rosetta_device::rom_w)));
+ }
+
+ // map ram
+ if (m_control[RAMS] & RAMS_SIZE)
+ {
+ unsigned const shift = (m_control[RAMS] & RAMS_SIZE) > 7 ? (m_control[RAMS] & RAMS_SIZE) - 7 : 0;
+ unsigned const size = 0x10000U << shift;
+ unsigned const factor = (m_control[RAMS] & RAMS_START) >> (4 + shift);
+
+ LOG("config ram size %dk map 0x%06x-0x%06x\n", size >> 10, size * factor, size * factor + size - 1);
+
+ m_mem_space->install_readwrite_handler(size * factor, size * factor + size - 1,
+ read32s_delegate(*this, FUNC(rosetta_device::ram_r)),
+ write32s_delegate(*this, FUNC(rosetta_device::ram_w)));
+ }
+}
+
+void rosetta_device::config_tlb()
+{
+ unsigned const ram_size = std::max(m_control[RAMS] & RAMS_SIZE, 7U);
+
+ if (m_control[TCR] & TCR_S)
+ {
+ m_atag_mask = TLB_AT4K;
+ m_page_mask = 0xffff'f000U;
+ m_page_shift = 12;
+
+ m_hat_base = (m_control[TCR] & TCR_HIB) << (ram_size - 1);
+ m_hat_mask = (1U << (ram_size - 1)) - 4;
+ }
+ else
+ {
+ m_atag_mask = TLB_AT2K;
+ m_page_mask = 0xffff'f800U;
+ m_page_shift = 11;
+
+ m_hat_base = (m_control[TCR] & TCR_HIB) << ram_size;
+ m_hat_mask = (1U << ram_size) - 4;
+ }
+
+ LOG("config page size %d hat base 0x%08x mask 0x%04x\n", 1 << m_page_shift, m_hat_base, m_hat_mask);
+}
+
+void rosetta_device::tlb_inv_all(u32 data)
+{
+ LOGMASKED(LOG_TLB, "tlb_inv_all (%s)\n", machine().describe_context());
+
+ for (unsigned i = 0; i < 16; i++)
+ {
+ m_tlb[i][0].field1 &= ~TLB_V;
+ m_tlb[i][1].field1 &= ~TLB_V;
+ }
+}
+
+void rosetta_device::tlb_inv_segment(u32 data)
+{
+ LOGMASKED(LOG_TLB, "tlb_inv_segment %x (%s)\n", data & 15, machine().describe_context());
+
+ unsigned const identifier = (m_segment[data & 15] & SEGMENT_ID) >> 2;
+
+ for (unsigned i = 0; i < 16; i++)
+ {
+ if (((m_tlb[i][0].field0 & TLB_SEG) >> 17) == identifier)
+ m_tlb[i][0].field1 &= ~TLB_V;
+
+ if (((m_tlb[i][1].field0 & TLB_SEG) >> 17) == identifier)
+ m_tlb[i][1].field1 &= ~TLB_V;
+ }
+}
+
+void rosetta_device::tlb_inv_address(u32 data)
+{
+ u64 const virtual_address = (u64(m_segment[data >> 28] & SEGMENT_ID) << 26) | (data & 0x0fff'ffffU);
+ unsigned const tlb_index = (data >> m_page_shift) & 15;
+
+ if ((m_tlb[tlb_index][0].field1 & TLB_V) && !((m_tlb[tlb_index][0].field0 ^ (virtual_address >> 11)) & m_atag_mask))
+ {
+ LOGMASKED(LOG_TLB, "tlb_inv_address 0x%08x set 0 (%s)\n", data, machine().describe_context());
+
+ m_tlb[tlb_index][0].field1 &= ~TLB_V;
+ m_tlb_lru &= ~(1U << tlb_index);
+ }
+
+ if ((m_tlb[tlb_index][1].field1 & TLB_V) && !((m_tlb[tlb_index][1].field0 ^ (virtual_address >> 11)) & m_atag_mask))
+ {
+ LOGMASKED(LOG_TLB, "tlb_inv_address 0x%08x set 1 (%s)\n", data, machine().describe_context());
+
+ m_tlb[tlb_index][1].field1 &= ~TLB_V;
+ m_tlb_lru |= (1U << tlb_index);
+ }
+}
+
+void rosetta_device::compute_address(u32 data)
+{
+ unsigned const segment = data >> 28;
+ tlb_entry t;
+
+ m_control[TRAR] = TRAR_I;
+
+ if ((m_control[TCR] & TCR_V) && !segment)
+ m_control[TRAR] = data & 0x00ff'ffffU;
+ else if (!tlb_reload(t, (u64(m_segment[segment] & SEGMENT_ID) << 26) | (data & 0x0fff'ffffU)))
+ m_control[TRAR] = (m_control[TCR] & TCR_S)
+ ? ((t.field1 & TLB_RPN4K) << 8) | (data & 0x0fffU)
+ : ((t.field1 & TLB_RPN2K) << 8) | (data & 0x07ffU);
+}
+
+bool rosetta_device::fetch(u32 address, u16 &data, rsc_mode const mode)
+{
+ if (mode & rsc_mode::RSC_T)
+ {
+ // FIXME: differences between fetch and load
+ if (!translate(address, true, false))
+ return false;
+
+ // FIXME: unconditionally access RAM
+ }
+
+ // access memory and handle errors
+ u32 const mer = m_control[MER] & (MER_B | MER_U | MER_C);
+ data = m_mem.read_word(address);
+
+ switch ((mer ^ m_control[MER]) & (MER_B | MER_U | MER_C))
+ {
+ case MER_U:
+ set_mear(address, MEMORY);
+ if (!(m_control[TCR] & TCR_D))
+ {
+ set_mchk(true);
+ return false;
+ }
+ break;
+
+ case MER_C:
+ set_mear(address, MEMORY);
+ if (!(m_control[TCR] & TCR_D))
+ set_mchk(true);
+ break;
+
+ case MER_B:
+ set_mear(address, MEMORY);
+ return false;
+ }
+
+ return true;
+}
+
+template <typename T> bool rosetta_device::load(u32 address, T &data, rsc_mode const mode, bool sp)
+{
+ if (mode & rsc_mode::RSC_T)
+ {
+ if (!translate(address, sp, false))
+ {
+ m_control[MER] |= MER_L;
+ if ((sp && (m_control[MER] & (MER_V | MER_I | MER_F | MER_S | MER_P | MER_D))) || (m_control[MER] & (MER_S)))
+ set_mear(address, LOCKED);
+
+ return false;
+ }
+
+ // FIXME: unconditionally access RAM
+ }
+
+ // access memory and handle errors
+ u32 const mer = m_control[MER] & (MER_B | MER_U | MER_C);
+ switch (sizeof(T))
+ {
+ case 1: data = m_mem.read_byte(address); break;
+ case 2: data = m_mem.read_word(address); break;
+ case 4: data = m_mem.read_dword(address); break;
+ }
+
+ switch ((mer ^ m_control[MER]) & (MER_B | MER_U | MER_C))
+ {
+ case MER_U:
+ set_mear(address, MEMORY);
+ if (!(m_control[TCR] & TCR_D))
+ {
+ set_mchk(true);
+ return false;
+ }
+ break;
+
+ case MER_C:
+ set_mear(address, MEMORY);
+ if (!(m_control[TCR] & TCR_D))
+ set_mchk(true);
+ break;
+
+ case MER_B:
+ set_mear(address, MEMORY);
+ return false;
+ }
+
+ return true;
+}
+
+template <typename T> bool rosetta_device::store(u32 address, T data, rsc_mode const mode, bool sp)
+{
+ if (mode & rsc_mode::RSC_T)
+ {
+ if (!translate(address, sp, true))
+ {
+ m_control[MER] &= ~MER_L;
+ if ((sp && (m_control[MER] & (MER_V | MER_I | MER_F | MER_S | MER_P | MER_D))) || (m_control[MER] & (MER_S)))
+ set_mear(address, LOCKED);
+
+ return false;
+ }
+
+ // FIXME: unconditionally access RAM
+ }
+
+ // access memory and handle errors
+ u32 const mer = m_control[MER] & (MER_B | MER_W);
+ switch (sizeof(T))
+ {
+ case 1: m_mem.write_byte(address, data); break;
+ case 2: m_mem.write_word(address, data); break;
+ case 4: m_mem.write_dword(address, data); break;
+ }
+
+ switch ((mer ^ m_control[MER]) & (MER_B | MER_W))
+ {
+ case MER_W:
+ set_mear(address, MEMORY);
+ set_pchk(true);
+ break;
+
+ case MER_B:
+ set_mear(address, MEMORY);
+ if (mode & RSC_T)
+ return false;
+ else
+ set_pchk(true);
+ break;
+ }
+
+ return true;
+}
+
+template <typename T> bool rosetta_device::modify(u32 address, std::function<T(T)> f, rsc_mode const mode)
+{
+ if (mode & rsc_mode::RSC_T)
+ {
+ if (!translate(address, true, true))
+ {
+ m_control[MER] &= ~MER_L;
+ if (m_control[MER] & (MER_V | MER_I | MER_F | MER_S | MER_P | MER_D))
+ set_mear(address, LOCKED);
+
+ return false;
+ }
+
+ // FIXME: unconditionally access RAM
+ }
+
+ // access memory and handle errors
+ u32 const mer = m_control[MER] & (MER_B | MER_W);
+ switch (sizeof(T))
+ {
+ case 1: m_mem.write_byte(address, f(m_mem.read_byte(address))); break;
+ case 2: m_mem.write_word(address, f(m_mem.read_word(address))); break;
+ case 4: m_mem.write_dword(address, f(m_mem.read_dword(address))); break;
+ }
+
+ switch ((mer ^ m_control[MER]) & (MER_B | MER_W))
+ {
+ case MER_W:
+ set_mear(address, MEMORY);
+ set_pchk(true);
+ break;
+
+ case MER_B:
+ set_mear(address, MEMORY);
+ if (mode & RSC_T)
+ return false;
+ else
+ set_pchk(true);
+ break;
+ }
+
+ return true;
+}