From a2a46381cbdd4f55635b49631ef95c4bd2f7caec Mon Sep 17 00:00:00 2001 From: Patrick Mackinlay Date: Fri, 21 Apr 2023 14:51:15 +0700 Subject: mc88200: fix cache endianness bugs * add support for debugger address translation --- src/devices/machine/mc88200.cpp | 402 ++++++++++++++++++++++------------------ src/devices/machine/mc88200.h | 12 +- 2 files changed, 230 insertions(+), 184 deletions(-) diff --git a/src/devices/machine/mc88200.cpp b/src/devices/machine/mc88200.cpp index c9d5983eeb6..805fb9538c6 100644 --- a/src/devices/machine/mc88200.cpp +++ b/src/devices/machine/mc88200.cpp @@ -412,14 +412,65 @@ void mc88200_device::cssp_w(u32 data) m_cache[BIT(m_sar, 4, 8)].status = data & CSSP_WM; } -std::optional mc88200_device::translate(u32 virtual_address, bool supervisor, bool write, bool debug) +// abbreviated, side-effect free address translation for debugger +bool mc88200_device::translate(int intention, u32 &address, bool supervisor) { // select area descriptor u32 const apr = supervisor ? m_sapr : m_uapr; if (apr & APR_TE) { - // check block address translation cache - for (u32 const batc : m_batc) + // search block address translation cache + for (u32 const &batc : m_batc) + { + if ((batc & BATC_V) && bool(batc & BATC_S) == supervisor && BIT(address, 19, 13) == BIT(batc, 19, 13)) + { + address = ((batc & BATC_PBA) << 13) | (address & ~BATC_LBA); + return true; + } + } + + // search page address translation cache + for (u64 const &patc : m_patc) + { + if ((patc & PATC_V) && (bool(patc & PATC_S) == supervisor && BIT(address, 12, 20) == BIT(patc, 26, 20))) + { + address = ((patc & PATC_PFA) << 6) | (address & LA_OFS); + return true; + } + } + + // load and check segment descriptor + std::optional const sgd = mbus_read((apr & APR_STBA) | ((address & LA_SEG) >> 20)); + if (!sgd.has_value() || !(sgd.value() & SGD_V) || ((sgd.value() & SGD_SP) && !supervisor)) + return false; + + // load and check page descriptor + std::optional pgd = mbus_read((sgd.value() & SGD_PTBA) | ((address & LA_PAG) >> 10)); + if (!pgd.has_value() || !(pgd.value() & PGD_V) || ((pgd.value() & PGD_SP) && !supervisor)) + return false; + + address = (pgd.value() & PGD_PFA) | (address & LA_OFS); + } + else + { + // apply hard-wired batc entries + if (BIT(address, 19, 13) == BIT(m_batc[8], 19, 13)) + address = ((m_batc[8] & BATC_PBA) << 13) | (address & ~BATC_LBA); + else if (BIT(address, 19, 13) == BIT(m_batc[9], 19, 13)) + address = ((m_batc[9] & BATC_PBA) << 13) | (address & ~BATC_LBA); + } + + return true; +} + +std::optional mc88200_device::translate(u32 virtual_address, bool supervisor, bool write) +{ + // select area descriptor + u32 const apr = supervisor ? m_sapr : m_uapr; + if (apr & APR_TE) + { + // search block address translation cache + for (u32 const &batc : m_batc) { if ((batc & BATC_V) && bool(batc & BATC_S) == supervisor && BIT(virtual_address, 19, 13) == BIT(batc, 19, 13)) { @@ -435,7 +486,7 @@ std::optional mc88200_device::translate(u32 vi } } - // check page address translation cache + // search page address translation cache bool patc_hit = false; for (u64 &patc : m_patc) { @@ -505,24 +556,21 @@ std::optional mc88200_device::translate(u32 vi return std::nullopt; } - if (!debug) + // update page descriptor used and modified bits + if (!(pgd.value() & PGD_U) || (write && !(pgd.value() & PGD_M))) { - // set page descriptor used and modified bits - if (!(pgd.value() & PGD_U) || (write && !(pgd.value() & PGD_M))) - { - pgd.value() |= (write ? PGD_M : 0) | PGD_U; + pgd.value() |= (write ? PGD_M : 0) | PGD_U; - if (!mbus_write((sgd.value() & SGD_PTBA) | ((virtual_address & LA_PAG) >> 10), pgd.value())) - return std::nullopt; - } + if (!mbus_write((sgd.value() & SGD_PTBA) | ((virtual_address & LA_PAG) >> 10), pgd.value())) + return std::nullopt; + } - if (!patc_hit) - { - // create patc entry (lpa,pfa,s,wt,g,ci,m,wp) - m_patc[m_patc_ptr++] = PATC_V | (u64(virtual_address & ~LA_OFS) << 14) | ((pgd.value() & PGD_PFA) >> 6) | (supervisor ? PATC_S : 0) | bitswap(apr | sgd.value() | pgd.value(), 9, 7, 6, 4, 2); - if (m_patc_ptr == std::size(m_patc)) - m_patc_ptr = 0; - } + if (!patc_hit) + { + // create patc entry (lpa,pfa,s,wt,g,ci,m,wp) + m_patc[m_patc_ptr++] = PATC_V | (u64(virtual_address & ~LA_OFS) << 14) | ((pgd.value() & PGD_PFA) >> 6) | (supervisor ? PATC_S : 0) | bitswap(apr | sgd.value() | pgd.value(), 9, 7, 6, 4, 2); + if (m_patc_ptr == std::size(m_patc)) + m_patc_ptr = 0; } return translate_result((pgd.value() & PGD_PFA) | (virtual_address & LA_OFS), @@ -689,213 +737,207 @@ std::optional mc88200_device::cache_replace(cache_set const &cs) return std::nullopt; } -template std::optional mc88200_device::cache_read(u32 physical_address) +void mc88200_device::cache_flush(unsigned const start, unsigned const limit, match_function match, bool const copyback, bool const invalidate) { - unsigned const s = BIT(physical_address, 4, 8); - cache_set &cs = m_cache[s]; - - for (unsigned l = 0; l < std::size(cs.line); l++) + for (unsigned s = start; s < limit; s++) { - // cache line hit: tag match, ensabled, not invalid - if (cs.line[l].match_page(physical_address) && cs.enabled(l) && !cs.invalid(l)) - { - // set most recently used - cs.set_mru(l); - - // return data - u32 const data = cs.line[l].data[BIT(physical_address, 2, 2)]; + cache_set &cs = m_cache[s]; - switch (sizeof(T)) + for (unsigned l = 0; l < std::size(cs.line); l++) + { + if (!match || std::invoke(match, cs.line[l], m_sar)) { - case 1: return T(data >> ((physical_address & 3) * 8)); - case 2: return T(data >> ((physical_address & 3) * 8)); - case 4: return T(data); + // copy back + if (copyback && cs.modified(l)) + if (!cs.line[l].copy_back(*this, cs.line[l].tag | (s << 4), true)) + return; + + // invalidate + if (invalidate) + cs.set_invalid(l); } } } +} - // select cache line for replacement - std::optional const l = cache_replace(cs); +template std::optional mc88200_device::read(u32 virtual_address, bool supervisor) +{ + std::optional result = translate(virtual_address, supervisor, false); + if (!result.has_value()) + return std::nullopt; - if (l.has_value()) + u32 const physical_address = result.value().address; + if (!result.value().ci) { - // copy back modified line - if (cs.modified(l.value())) - if (!cs.line[l.value()].copy_back(*this, cs.line[l.value()].tag | (s << 4))) - return false; - - // mark line invalid - cs.set_invalid(l.value()); - - // update tag - cs.line[l.value()].tag = physical_address & ~LA_OFS; + unsigned const s = BIT(physical_address, 4, 8); + cache_set &cs = m_cache[s]; - // load line from memory - if (!cs.line[l.value()].load_line(*this, physical_address & 0xfffffff0U)) - return false; + for (unsigned l = 0; l < std::size(cs.line); l++) + { + // cache line hit: tag match, enabled, not invalid + if (cs.line[l].match_page(physical_address) && cs.enabled(l) && !cs.invalid(l)) + { + // set most recently used + cs.set_mru(l); - // mark line shared unmodified - cs.set_shared(l.value()); + // return data + u32 const data = cs.line[l].data[BIT(physical_address, 2, 2)]; - // set most recently used - cs.set_mru(l.value()); + switch (sizeof(T)) + { + case 1: return T(data >> (24 - (physical_address & 3) * 8)); + case 2: return T(data >> (16 - (physical_address & 3) * 8)); + case 4: return T(data); + } + } + } - // return data - u32 const data = cs.line[l.value()].data[BIT(physical_address, 2, 2)]; + // select cache line for replacement + std::optional const l = cache_replace(cs); - switch (sizeof(T)) + if (l.has_value()) { - case 1: return T(data >> ((physical_address & 3) * 8)); - case 2: return T(data >> ((physical_address & 3) * 8)); - case 4: return T(data); - } - } + // copy back modified line + if (cs.modified(l.value())) + if (!cs.line[l.value()].copy_back(*this, cs.line[l.value()].tag | (s << 4))) + return false; - return mbus_read(physical_address); -} + // mark line invalid + cs.set_invalid(l.value()); -template bool mc88200_device::cache_write(u32 physical_address, T data, bool writethrough, bool global) -{ - unsigned const s = BIT(physical_address, 4, 8); - cache_set &cs = m_cache[s]; + // update tag + cs.line[l.value()].tag = physical_address & ~LA_OFS; - for (unsigned l = 0; l < std::size(cs.line); l++) - { - // cache line hit: tag match, enabled, not invalid - if (cs.line[l].match_page(physical_address) && cs.enabled(l) && !cs.invalid(l)) - { - // write data to cache - u32 &cache_data = cs.line[l].data[BIT(physical_address, 2, 2)]; - switch (sizeof(T)) - { - case 1: cache_data = (cache_data & ~(0x000000ffU << ((physical_address & 3) * 8))) | (u32(data) << ((physical_address & 3) * 8)); break; - case 2: cache_data = (cache_data & ~(0x0000ffffU << ((physical_address & 3) * 8))) | (u32(data) << ((physical_address & 3) * 8)); break; - case 4: cache_data = data; break; - } + // load line from memory + if (!cs.line[l.value()].load_line(*this, physical_address & 0xfffffff0U)) + return false; + + // mark line shared unmodified + cs.set_shared(l.value()); // set most recently used - cs.set_mru(l); + cs.set_mru(l.value()); - // write data to memory - if (writethrough || global) - if (!mbus_write(physical_address, data)) - return false; + // return data + u32 const data = cs.line[l.value()].data[BIT(physical_address, 2, 2)]; - // update line status - if (cs.shared(l)) + switch (sizeof(T)) { - if (global) - cs.set_unmodified(l); - else if (!writethrough) - cs.set_modified(l); + case 1: return T(data >> (24 - (physical_address & 3) * 8)); + case 2: return T(data >> (16 - (physical_address & 3) * 8)); + case 4: return T(data); } - else - cs.set_modified(l); - - return true; } } - // select cache line for replacement - std::optional const l = cache_replace(cs); + return mbus_read(physical_address); +} + +template bool mc88200_device::write(u32 virtual_address, T data, bool supervisor) +{ + std::optional result = translate(virtual_address, supervisor, true); + if (!result.has_value()) + return false; - if (l.has_value()) + u32 const physical_address = result.value().address; + if (!result.value().ci) { - // copy back modified line - if (cs.modified(l.value())) - if (!cs.line[l.value()].copy_back(*this, cs.line[l.value()].tag | (s << 4))) - return false; + unsigned const s = BIT(physical_address, 4, 8); + cache_set &cs = m_cache[s]; - // mark line invalid - cs.set_invalid(l.value()); + for (unsigned l = 0; l < std::size(cs.line); l++) + { + // cache line hit: tag match, enabled, not invalid + if (cs.line[l].match_page(physical_address) && cs.enabled(l) && !cs.invalid(l)) + { + // write data to cache + u32 &cache_data = cs.line[l].data[BIT(physical_address, 2, 2)]; + switch (sizeof(T)) + { + case 1: cache_data = (cache_data & ~(0x000000ffU << (24 - (physical_address & 3) * 8))) | (u32(data) << (24 - (physical_address & 3) * 8)); break; + case 2: cache_data = (cache_data & ~(0x0000ffffU << (16 - (physical_address & 3) * 8))) | (u32(data) << (16 - (physical_address & 3) * 8)); break; + case 4: cache_data = data; break; + } - // load line from memory - if (!cs.line[l.value()].load_line(*this, physical_address & 0xfffffff0U)) - return false; - } + // set most recently used + cs.set_mru(l); - // write data to memory - if (!mbus_write(physical_address, data)) - return false; + // write data to memory + if (result.value().wt || result.value().g) + if (!mbus_write(physical_address, data)) + return false; - if (l.has_value()) - { - // update tag - cs.line[l.value()].tag = physical_address & ~LA_OFS; + // update line status + if (cs.shared(l)) + { + if (result.value().g) + cs.set_unmodified(l); + else if (!result.value().wt) + cs.set_modified(l); + } + else + cs.set_modified(l); - // write data into cache - u32 &cache_data = cs.line[l.value()].data[BIT(physical_address, 2, 2)]; - switch (sizeof(T)) - { - case 1: cache_data = (cache_data & ~(0x000000ffU << ((physical_address & 3) * 8))) | (u32(data) << ((physical_address & 3) * 8)); break; - case 2: cache_data = (cache_data & ~(0x0000ffffU << ((physical_address & 3) * 8))) | (u32(data) << ((physical_address & 3) * 8)); break; - case 4: cache_data = data; break; + return true; + } } - // mark line exclusive unmodified - cs.set_unmodified(l.value()); + // select cache line for replacement + std::optional const l = cache_replace(cs); - // set most recently used - cs.set_mru(l.value()); - } + if (l.has_value()) + { + // copy back modified line + if (cs.modified(l.value())) + if (!cs.line[l.value()].copy_back(*this, cs.line[l.value()].tag | (s << 4))) + return false; - return true; -} + // mark line invalid + cs.set_invalid(l.value()); -void mc88200_device::cache_flush(unsigned const start, unsigned const limit, match_function match, bool const copyback, bool const invalidate) -{ - for (unsigned s = start; s < limit; s++) - { - cache_set &cs = m_cache[s]; + // load line from memory + if (!cs.line[l.value()].load_line(*this, physical_address & 0xfffffff0U)) + return false; + } - for (unsigned l = 0; l < std::size(cs.line); l++) + // write data to memory + if (!mbus_write(physical_address, data)) + return false; + + if (l.has_value()) { - if (!match || std::invoke(match, cs.line[l], m_sar)) - { - // copy back - if (copyback && cs.modified(l)) - if (!cs.line[l].copy_back(*this, cs.line[l].tag | (s << 4), true)) - return; + // update tag + cs.line[l.value()].tag = physical_address & ~LA_OFS; - // invalidate - if (invalidate) - cs.set_invalid(l); + // write data into cache + u32 &cache_data = cs.line[l.value()].data[BIT(physical_address, 2, 2)]; + switch (sizeof(T)) + { + case 1: cache_data = (cache_data & ~(0x000000ffU << (24 - (physical_address & 3) * 8))) | (u32(data) << (24 - (physical_address & 3) * 8)); break; + case 2: cache_data = (cache_data & ~(0x0000ffffU << (16 - (physical_address & 3) * 8))) | (u32(data) << (16 - (physical_address & 3) * 8)); break; + case 4: cache_data = data; break; } - } - } -} -template std::optional mc88200_device::read(u32 virtual_address, bool supervisor, bool debug) -{ - std::optional result = translate(virtual_address, supervisor, false, debug); - if (!result.has_value()) - return std::nullopt; + // mark line exclusive unmodified + cs.set_unmodified(l.value()); - if (result.value().ci) - return mbus_read(result.value().address); - else - return cache_read(result.value().address); -} + // set most recently used + cs.set_mru(l.value()); + } -template bool mc88200_device::write(u32 virtual_address, T data, bool supervisor, bool debug) -{ - std::optional result = translate(virtual_address, supervisor, true, debug); - if (!result.has_value()) - return false; + return true; + } - if (result.value().ci) - return mbus_write(result.value().address, data); - else - return cache_write(result.value().address, data, result.value().wt, result.value().g); + return mbus_write(result.value().address, data); } -template std::optional mc88200_device::read(u32 virtual_address, bool supervisor, bool debug); -template std::optional mc88200_device::read(u32 virtual_address, bool supervisor, bool debug); -template std::optional mc88200_device::read(u32 virtual_address, bool supervisor, bool debug); +template std::optional mc88200_device::read(u32 virtual_address, bool supervisor); +template std::optional mc88200_device::read(u32 virtual_address, bool supervisor); +template std::optional mc88200_device::read(u32 virtual_address, bool supervisor); -template bool mc88200_device::write(u32 virtual_address, u8 data, bool supervisor, bool debug); -template bool mc88200_device::write(u32 virtual_address, u16 data, bool supervisor, bool debug); -template bool mc88200_device::write(u32 virtual_address, u32 data, bool supervisor, bool debug); +template bool mc88200_device::write(u32 virtual_address, u8 data, bool supervisor); +template bool mc88200_device::write(u32 virtual_address, u16 data, bool supervisor); +template bool mc88200_device::write(u32 virtual_address, u32 data, bool supervisor); template std::optional mc88200_device::mbus_read(u32 address) { @@ -910,9 +952,13 @@ template std::optional mc88200_device::mbus_read(u32 address) if (m_bus_error) { - m_bus_error = false; - m_pfar = address; - m_pfsr = PFSR_BE; + if (!machine().side_effects_disabled()) + { + m_bus_error = false; + m_pfar = address; + m_pfsr = PFSR_BE; + } + data = std::nullopt; } return data; diff --git a/src/devices/machine/mc88200.h b/src/devices/machine/mc88200.h index 2073583a252..e439d0e9096 100644 --- a/src/devices/machine/mc88200.h +++ b/src/devices/machine/mc88200.h @@ -14,9 +14,11 @@ public: template void set_mbus(T &&tag, int spacenum) { m_mbus.set_tag(std::forward(tag), spacenum); } - template std::optional read(u32 virtual_address, bool supervisor, bool debug = false); - template bool write(u32 virtual_address, T data, bool supervisor, bool debug = false); - WRITE_LINE_MEMBER(bus_error_w) { m_bus_error = true; } + bool translate(int intention, u32 &address, bool supervisor); + + template std::optional read(u32 virtual_address, bool supervisor); + template bool write(u32 virtual_address, T data, bool supervisor); + WRITE_LINE_MEMBER(bus_error_w) { if (!machine().side_effects_disabled()) m_bus_error = true; } protected: virtual void device_start() override; @@ -69,7 +71,7 @@ protected: bool const g; // global bool const wt; // writethrough }; - std::optional translate(u32 virtual_address, bool supervisor, bool write, bool debug = false); + std::optional translate(u32 virtual_address, bool supervisor, bool write); struct cache_set { @@ -104,8 +106,6 @@ protected: typedef bool (mc88200_device::cache_set::cache_line::* match_function)(u32 const) const; std::optional cache_replace(cache_set const &cs); - template std::optional cache_read(u32 physical_address); - template bool cache_write(u32 physical_address, T data, bool writethrough, bool global); void cache_flush(unsigned const start, unsigned const limit, match_function match, bool const copyback, bool const invalidate); template std::optional mbus_read(u32 address); -- cgit v1.2.3