diff options
Diffstat (limited to 'src/devices/machine/ns32382.cpp')
-rw-r--r-- | src/devices/machine/ns32382.cpp | 429 |
1 files changed, 429 insertions, 0 deletions
diff --git a/src/devices/machine/ns32382.cpp b/src/devices/machine/ns32382.cpp new file mode 100644 index 00000000000..d801342a102 --- /dev/null +++ b/src/devices/machine/ns32382.cpp @@ -0,0 +1,429 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +/* + * National Semiconductor NS32382 Memory Management Unit. + * + * Sources: + * - Microprocessor Databook, Series 32000, NSC800, 1989 Edition, National Semiconductor + * + * TODO: + * - tlb + * - breakpoints + * - cycles + * - fast status protocol + */ + +#include "emu.h" +#include "ns32382.h" + +#define LOG_TRANSLATE (1U << 1) + +//#define VERBOSE (LOG_GENERAL|LOG_TRANSLATE) + +#include "logmacro.h" + +DEFINE_DEVICE_TYPE(NS32382, ns32382_device, "ns32382", "National Semiconductor NS32382 Memory Management Unit") + +enum state : unsigned +{ + IDLE = 0, + OPERAND = 2, // awaiting operands + RDVAL = 3, // rdval pending + WRVAL = 4, // wrval pending + STATUS = 5, // status word available + RESULT = 6, // result word available +}; + +enum reg_mask : unsigned +{ + BAR = 0x0, // breakpoint address register + BMR = 0x2, // breakpoint mask register + BDR = 0x3, // breakpoint data register + BEAR = 0x6, // bus error address register + MCR = 0x9, // memory management control register + MSR = 0xa, // memory management status register + TEAR = 0xb, // translation exception address register + PTB0 = 0xc, // page table base register 0 + PTB1 = 0xd, // page table base register 1 + IVAR0 = 0xe, // invalidate virtual address register 0 + IVAR1 = 0xf, // invalidate virtual address register 1 +}; + +enum mcr_mask : u32 +{ + MCR_TU = 0x00000001, // translate user-mode + MCR_TS = 0x00000002, // translate supervisor-mode + MCR_DS = 0x00000004, // dual-space translation + MCR_AO = 0x00000008, // access level override + MCR_BR = 0x00000010, // break on read + MCR_BW = 0x00000020, // break on write + MCR_BE = 0x00000040, // break on execution + MCR_BAS = 0x00000080, // breakpoint address space + + MCR_WM = 0x000000ff, +}; + +enum msr_mask : u32 +{ + MSR_TEX = 0x00000003, // translation exception + MSR_DDT = 0x00000004, // data direction (translate exception) + MSR_UST = 0x00000008, // user/supervisor + MSR_STT = 0x000000f0, // cpu status + MSR_BP = 0x00000200, // break + MSR_CE = 0x00000400, // cpu error + MSR_ME = 0x00000800, // mmu error + MSR_DDE = 0x00001000, // data direction (bus error) + MSR_USE = 0x00002000, // user/supervisor + MSR_STE = 0x0003c000, // cpu status + + MSR_WM = 0x0003feff, +}; + +enum msr_tex_mask : u32 +{ + TEX_IL1 = 0x00000001, // first level pte invalid + TEX_IL2 = 0x00000002, // second level pte invalid + TEX_PL = 0x00000003, // protection violation +}; + +enum ptb_mask : u32 +{ + PTB_AB = 0xfffff000, // address bits +}; + +enum pte_mask : u32 +{ + PTE_V = 0x00000001, // valid + PTE_PL = 0x00000006, // protection level + PTE_CI = 0x00000040, // (level 2 only) cache inhibit + PTE_R = 0x00000080, // referenced + PTE_M = 0x00000100, // modified + PTE_USR = 0x00000e00, // user + PTE_PFN = 0xfffff000, // page frame number +}; + +enum pte_pl_mask : u32 +{ + PL_SRO = 0x00000000, // supervisor read only + PL_SRW = 0x00000002, // supervisor read write + PL_URO = 0x00000004, // user read only + PL_URW = 0x00000006, // user read write +}; + +enum va_mask : u32 +{ + VA_INDEX1 = 0xffc00000, + VA_INDEX2 = 0x003ff000, + VA_OFFSET = 0x00000fff, +}; + +ns32382_device::ns32382_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) + : device_t(mconfig, NS32382, tag, owner, clock) + , ns32000_mmu_interface(mconfig, *this) + , ns32000_fast_slave_interface(mconfig, *this) +{ +} + +void ns32382_device::device_start() +{ + save_item(NAME(m_bar)); + save_item(NAME(m_bdr)); + save_item(NAME(m_bear)); + save_item(NAME(m_bmr)); + save_item(NAME(m_mcr)); + save_item(NAME(m_msr)); + save_item(NAME(m_ptb)); + save_item(NAME(m_tear)); + + save_item(NAME(m_opword)); + save_item(STRUCT_MEMBER(m_op, expected)); + save_item(STRUCT_MEMBER(m_op, issued)); + save_item(STRUCT_MEMBER(m_op, value)); + save_item(NAME(m_status)); + + save_item(NAME(m_state)); + save_item(NAME(m_tcy)); +} + +void ns32382_device::device_reset() +{ + m_mcr = 0; + m_msr = 0; + + m_state = IDLE; +} + +void ns32382_device::state_add(device_state_interface &parent, int &index) +{ + parent.state_add(index++, "MCR", m_mcr).formatstr("%02X"); + parent.state_add(index++, "MSR", m_msr).formatstr("%08X"); +} + +u32 ns32382_device::fast_status(int *icount) +{ + if (m_state == STATUS) + { + m_state = (m_op[2].issued == m_op[2].expected) ? IDLE : RESULT; + + if (icount) + *icount -= m_tcy; + + LOG("status 0x%04x tcy %d %s (%s)\n", m_status, m_tcy, + (m_state == RESULT ? "results pending" : "complete"), machine().describe_context()); + + return m_status; + } + + logerror("status protocol error (%s)\n", machine().describe_context()); + return 0; +} + +u32 ns32382_device::fast_read() +{ + if (m_state == RESULT && m_op[2].issued < m_op[2].expected) + { + u32 const data = u32(m_op[2].value >> (m_op[2].issued * 8)); + LOG("read %d data 0x%08x (%s)\n", m_op[2].issued >> 2, data, machine().describe_context()); + + m_op[2].issued += 4; + + if (m_op[2].issued == m_op[2].expected) + { + LOG("read complete\n"); + m_state = IDLE; + } + + return data; + } + + logerror("read protocol error (%s)\n", machine().describe_context()); + return 0; +} + +void ns32382_device::fast_write(u32 data) +{ + switch (m_state) + { + case IDLE: + if (BIT(data, 24, 8) == FORMAT_14) + { + m_opword = swapendian_int16(data >> 8); + + LOG("write idbyte 0x%02x opword 0x%04x (%s)\n", FORMAT_14, m_opword, machine().describe_context()); + + m_tcy = 0; + + // initialize operands + for (operand &op : m_op) + { + op.expected = 0; + op.issued = 0; + op.value = 0; + } + + // format 14: xxxx xsss s0oo ooii 0001 1110 + unsigned const size = m_opword & 3; + + switch (BIT(m_opword, 2, 4)) + { + case 0: // rdval + break; + case 1: // wrval + break; + case 2: // lmr + m_op[0].expected = size + 1; + break; + case 3: // smr + m_op[2].expected = size + 1; + break; + } + + m_state = OPERAND; + } + break; + + case OPERAND: + // check awaiting operand word + if (m_op[0].issued < m_op[0].expected || m_op[1].issued < m_op[1].expected) + { + unsigned const n = (m_op[0].issued < m_op[0].expected) ? 0 : 1; + operand &op = m_op[n]; + + LOG("write opword 0x%08x (%s)\n", data, machine().describe_context()); + + // insert dword into operand value + op.value |= u64(data) << (op.issued * 8); + op.issued += 4; + } + else + logerror("write protocol error unexpected operand data 0x%08x (%s)\n", + data, machine().describe_context()); + break; + } + + // start execution when all operands are available + if (m_state == OPERAND && m_op[0].issued >= m_op[0].expected && m_op[1].issued >= m_op[1].expected) + execute(); +} + +void ns32382_device::execute() +{ + m_status = 0; + + // format 14: xxxx xsss s0oo ooii + unsigned const quick = BIT(m_opword, 7, 4); + + switch (BIT(m_opword, 2, 4)) + { + case 0: // rdval + m_tcy = 21; + m_state = RDVAL; + break; + case 1: // wrval + m_tcy = 21; + m_state = WRVAL; + break; + case 2: // lmr + switch (quick) + { + case BAR: m_bar = m_op[0].value; break; + case BMR: m_bmr = m_op[0].value; break; + case MCR: m_mcr = m_op[0].value; break; + case MSR: m_msr = m_op[0].value; break; + case TEAR: m_tear = m_op[0].value; break; + case PTB0: m_ptb[0] = m_op[0].value & PTB_AB; break; + case PTB1: m_ptb[1] = m_op[0].value & PTB_AB; break; + case IVAR0: break; // TODO: invalidate tlb entry + case IVAR1: break; // TODO: invalidate tlb entry + default: + logerror("lmr unknown register %d (%s)\n", quick, machine().describe_context()); + break; + } + m_tcy = 30; + break; + case 3: // smr + switch (quick) + { + case BAR: m_op[2].value = m_bar; break; + case BMR: m_op[2].value = m_bmr; break; + case BDR: m_op[2].value = m_bdr; break; + case BEAR: m_op[2].value = m_bear; break; + case MCR: m_op[2].value = m_mcr & MCR_WM; break; + case MSR: m_op[2].value = m_msr & MSR_WM; break; + case TEAR: m_op[2].value = m_tear; break; + case PTB0: m_op[2].value = m_ptb[0]; break; + case PTB1: m_op[2].value = m_ptb[1]; break; + default: + logerror("smr unknown register %d (%s)\n", quick, machine().describe_context()); + break; + } + m_tcy = 25; + break; + } + + // exceptions suppress result issue + if (m_status & SLAVE_Q) + m_op[2].expected = 0; + + if (m_state == OPERAND) + m_state = STATUS; +} + +ns32382_device::translate_result ns32382_device::translate(address_space &space, unsigned st, u32 &address, bool user, bool write, bool pfs, bool debug) +{ + // check translation required + if ((!(m_mcr & MCR_TU) && user) || (!(m_mcr & MCR_TS) && !user)) + return COMPLETE; + + // treat WRVAL as write + write |= m_state == WRVAL; + + bool const address_space = (m_mcr & MCR_DS) && user; + unsigned const access_level = (user && !(m_mcr & MCR_AO)) + ? ((write || st == ns32000::ST_RMW) ? PL_URW : PL_URO) : ((write || st == ns32000::ST_RMW) ? PL_SRW : PL_SRO); + + if (m_state == IDLE && !debug) + m_msr &= ~(MSR_STT | MSR_UST | MSR_DDT | MSR_TEX); + + // read level 1 page table entry + u32 const pte1_address = m_ptb[address_space] | ((address & VA_INDEX1) >> 20); + u32 const pte1 = space.read_dword(pte1_address); + LOGMASKED(LOG_TRANSLATE, "translate level 1 page table address 0x%06x entry 0x%08x\n", pte1_address, pte1); + + if (access_level > (pte1 & PTE_PL) || !(pte1 & PTE_V)) + { + if (m_state == IDLE && !debug) + { + m_msr |= ((st & 15) << 4) | (user ? MSR_UST : 0) | (write ? MSR_DDT : 0); + if (access_level > (pte1 & PTE_PL)) + m_msr |= TEX_PL; + else if (!(pte1 & PTE_V)) + m_msr |= TEX_IL1; + + m_tear = address; + } + + if (m_state == RDVAL || m_state == WRVAL) + { + if (pte1 & PTE_V) + { + m_state = STATUS; + m_status |= SLAVE_F; + + return CANCEL; + } + else + m_state = IDLE; + } + + LOGMASKED(LOG_TRANSLATE, "translate level 1 abort address 0x%08x\n", address); + return ABORT; + } + + // set referenced + if (!(pte1 & PTE_R) && !debug) + space.write_dword(pte1_address, pte1 | PTE_R); + + // read level 2 page table entry + u32 const pte2_address = (pte1 & PTE_PFN) | ((address & VA_INDEX2) >> 10); + u32 const pte2 = space.read_dword(pte2_address); + LOGMASKED(LOG_TRANSLATE, "translate level 2 page table address 0x%06x entry 0x%08x\n", pte2_address, pte2); + + if (access_level > (pte2 & PTE_PL) || !(pte2 & PTE_V)) + { + if (m_state == IDLE && !debug) + { + m_msr |= ((st & 15) << 4) | (user ? MSR_UST : 0) | (write ? MSR_DDT : 0); + if (access_level > (pte2 & PTE_PL)) + m_msr |= TEX_PL; + else if (!(pte2 & PTE_V)) + m_msr |= TEX_IL2; + + m_tear = address; + } + + if (m_state == RDVAL || m_state == WRVAL) + { + m_state = STATUS; + if (pte2 & PTE_V) + m_status |= SLAVE_F; + + return CANCEL; + } + else + { + LOGMASKED(LOG_TRANSLATE, "translate level 2 abort address 0x%08x\n", address); + return ABORT; + } + } + + // set modified and referenced + if ((!(pte2 & PTE_R) || (write && !(pte2 & PTE_M))) && !debug) + space.write_dword(pte2_address, pte2 | (write ? PTE_M : 0) | PTE_R); + + address = (pte2 & PTE_PFN) | (address & VA_OFFSET); + LOGMASKED(LOG_TRANSLATE, "translate complete 0x%08x\n", address); + + return COMPLETE; +} |