diff options
Diffstat (limited to 'src/mame/machine/rosetta.cpp')
-rw-r--r-- | src/mame/machine/rosetta.cpp | 1202 |
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; +} |