mirror of
https://github.com/holub/mame
synced 2025-05-31 10:01:51 +03:00
ns32382: new device
This commit is contained in:
parent
a733cc701c
commit
89b850c9d9
@ -4934,3 +4934,14 @@ if (MACHINES["BITMAP_PRINTER"]~=null) then
|
||||
MAME_DIR .. "src/devices/machine/bitmap_printer.h",
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/machine/ns32382.h,MACHINES["NS32382"] = true
|
||||
---------------------------------------------------
|
||||
if (MACHINES["NS32382"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/machine/ns32382.cpp",
|
||||
MAME_DIR .. "src/devices/machine/ns32382.h",
|
||||
}
|
||||
end
|
||||
|
@ -831,6 +831,7 @@ MACHINES["NS32081"] = true
|
||||
MACHINES["NS32202"] = true
|
||||
MACHINES["NS32082"] = true
|
||||
MACHINES["BITMAP_PRINTER"] = true
|
||||
MACHINES["NS32382"] = true
|
||||
|
||||
--------------------------------------------------
|
||||
-- specify available bus cores
|
||||
|
452
src/devices/machine/ns32382.cpp
Normal file
452
src/devices/machine/ns32382.cpp
Normal file
@ -0,0 +1,452 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Patrick Mackinlay
|
||||
|
||||
/*
|
||||
* National Semiconductor 32382 Memory Management Unit.
|
||||
*
|
||||
* Sources:
|
||||
* - http://bitsavers.org/components/national/_dataBooks/1988_National_Series_32000_Microprocessors_Databook.pdf
|
||||
*
|
||||
* TODO:
|
||||
* - tlb
|
||||
* - breakpoints
|
||||
* - cycles
|
||||
* - fast status protocol
|
||||
*/
|
||||
|
||||
#include "emu.h"
|
||||
#include "ns32382.h"
|
||||
|
||||
#define LOG_GENERAL (1U << 0)
|
||||
#define LOG_TRANSLATE (1U << 1)
|
||||
|
||||
//#define VERBOSE (LOG_GENERAL|LOG_TRANSLATE)
|
||||
|
||||
#include "logmacro.h"
|
||||
|
||||
DEFINE_DEVICE_TYPE(NS32382, ns32382_device, "ns32382", "National Semiconductor 32382 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 idbyte : u8
|
||||
{
|
||||
FORMAT_14 = 0x1e,
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
enum st_mask : unsigned
|
||||
{
|
||||
ST_ICI = 0x0, // bus idle (CPU busy)
|
||||
ST_ICW = 0x1, // bus idle (CPU wait)
|
||||
ST_ISE = 0x3, // bus idle (slave execution)
|
||||
ST_IAM = 0x4, // interrupt acknowledge, master
|
||||
ST_IAC = 0x5, // interrupt acknowledge, cascaded
|
||||
ST_EIM = 0x6, // end of interrupt, master
|
||||
ST_EIC = 0x7, // end of interrupt, cascaded
|
||||
ST_SIF = 0x8, // sequential instruction fetch
|
||||
ST_NIF = 0x9, // non-sequential instruction fetch
|
||||
ST_ODT = 0xa, // operand data transfer
|
||||
ST_RMW = 0xb, // read RMW operand
|
||||
ST_EAR = 0xc, // effective address read
|
||||
ST_SOP = 0xd, // slave operand
|
||||
ST_SST = 0xe, // slave status
|
||||
ST_SID = 0xf, // slave ID
|
||||
};
|
||||
|
||||
ns32382_device::ns32382_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
|
||||
: device_t(mconfig, NS32382, tag, owner, clock)
|
||||
, ns32000_fast_slave_interface(mconfig, *this)
|
||||
, ns32000_mmu_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::read_st(int *icount)
|
||||
{
|
||||
if (m_state == STATUS)
|
||||
{
|
||||
m_state = (m_op[2].issued == m_op[2].expected) ? IDLE : RESULT;
|
||||
|
||||
if (icount)
|
||||
*icount -= m_tcy;
|
||||
|
||||
LOG("read_st 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("read_st protocol error reading status (%s)\n", machine().describe_context());
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 ns32382_device::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_op dword %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_op last result dword issued\n");
|
||||
m_state = IDLE;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
logerror("read_op protocol error reading result dword (%s)\n", machine().describe_context());
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ns32382_device::write(u32 data)
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
case IDLE:
|
||||
if (BIT(data, 24, 8) == FORMAT_14)
|
||||
{
|
||||
LOG("write_fast match 0x%08x (%s)\n", data, machine().describe_context());
|
||||
|
||||
m_opword = swapendian_int16(data >> 8);
|
||||
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_op op%d data 0x%04x (%s)\n", n, data, machine().describe_context());
|
||||
|
||||
// insert dword into operand value
|
||||
op.value |= u64(data) << (op.issued * 8);
|
||||
op.issued += 4;
|
||||
}
|
||||
else
|
||||
logerror("write_fast protocol error unexpected operand 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 0001 1110
|
||||
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 == ST_RMW) ? PL_URW : PL_URO) : ((write || st == 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 (pte1 & 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;
|
||||
}
|
66
src/devices/machine/ns32382.h
Normal file
66
src/devices/machine/ns32382.h
Normal file
@ -0,0 +1,66 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Patrick Mackinlay
|
||||
|
||||
#ifndef MAME_MACHINE_NS32382_H
|
||||
#define MAME_MACHINE_NS32382_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cpu/ns32000/slave.h"
|
||||
|
||||
class ns32382_device
|
||||
: public device_t
|
||||
, public ns32000_fast_slave_interface
|
||||
, public ns32000_mmu_interface
|
||||
{
|
||||
public:
|
||||
ns32382_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
|
||||
|
||||
// ns32000_slave_interface overrides
|
||||
virtual void state_add(device_state_interface &parent, int &index) override;
|
||||
|
||||
// ns32000_fast_slave_interface overrides
|
||||
virtual u32 read_st(int *icount = nullptr) override;
|
||||
virtual void write(u32 data) override;
|
||||
virtual u32 read() override;
|
||||
|
||||
// ns32000_mmu_interface overrides
|
||||
virtual translate_result translate(address_space &space, unsigned st, u32 &address, bool user, bool write, bool pfs = false, bool debug = false) override;
|
||||
|
||||
protected:
|
||||
// device_t overrides
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
void execute();
|
||||
|
||||
private:
|
||||
// registers
|
||||
u32 m_bar; // breakpoint address register
|
||||
u32 m_bdr; // breakpoint data register
|
||||
u32 m_bear; // bus error address register
|
||||
u32 m_bmr; // breakpoint mask register
|
||||
u32 m_mcr; // memory management control register
|
||||
u32 m_msr; // memory management status register
|
||||
u32 m_ptb[2]; // page table base registers
|
||||
u32 m_tear; // translation exception address register
|
||||
|
||||
// operating state
|
||||
u16 m_opword;
|
||||
struct operand
|
||||
{
|
||||
u32 expected;
|
||||
u32 issued;
|
||||
u64 value;
|
||||
}
|
||||
m_op[3];
|
||||
u16 m_status;
|
||||
|
||||
// implementation state
|
||||
u32 m_state;
|
||||
u32 m_tcy;
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(NS32382, ns32382_device)
|
||||
|
||||
#endif // MAME_MACHINE_NS32382_H
|
Loading…
Reference in New Issue
Block a user