// 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(m_ram_size); m_ecc = std::make_unique(m_ram_size); m_rca = std::make_unique(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 < std::size(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 < std::size(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 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 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 bool rosetta_device::modify(u32 address, std::function 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; }