mame/src/emu/divtlb.cpp
2023-03-18 21:27:43 +01:00

352 lines
9.5 KiB
C++

// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************
divtlb.cpp
Device generic virtual TLB interface.
***************************************************************************/
#include "emu.h"
#include "divtlb.h"
//**************************************************************************
// DEBUGGING
//**************************************************************************
#define PRINTF_TLB (0)
//**************************************************************************
// DEVICE VTLB INTERFACE
//**************************************************************************
//-------------------------------------------------
// device_vtlb_interface - constructor
//-------------------------------------------------
device_vtlb_interface::device_vtlb_interface(const machine_config &mconfig, device_t &device, int space)
: device_interface(device, "vtlb"),
m_space(space),
m_dynamic(0),
m_fixed(0),
m_dynindex(0),
m_pageshift(0),
m_addrwidth(0),
m_table_base(nullptr)
{
}
//-------------------------------------------------
// device_vtlb_interface - destructor
//-------------------------------------------------
device_vtlb_interface::~device_vtlb_interface()
{
}
//-------------------------------------------------
// interface_validity_check - validation for a
// device after the configuration has been
// constructed
//-------------------------------------------------
void device_vtlb_interface::interface_validity_check(validity_checker &valid) const
{
const device_memory_interface *intf;
if (!device().interface(intf))
osd_printf_error("Device does not have memory interface\n");
else
{
// validate CPU information
const address_space_config *spaceconfig = intf->space_config(m_space);
if (spaceconfig == nullptr)
osd_printf_error("No memory address space configuration found for space %d\n", m_space);
else if ((1 << spaceconfig->page_shift()) <= FLAGS_MASK || spaceconfig->logaddr_width() <= spaceconfig->page_shift())
osd_printf_error("Invalid page shift %d for VTLB\n", spaceconfig->page_shift());
}
}
//-------------------------------------------------
// interface_pre_start - work to be done prior to
// actually starting a device
//-------------------------------------------------
void device_vtlb_interface::interface_pre_start()
{
// fill in CPU information
const address_space_config *spaceconfig = device().memory().space_config(m_space);
m_pageshift = spaceconfig->page_shift();
m_addrwidth = spaceconfig->logaddr_width();
// allocate the entry array
m_live.resize(m_fixed + m_dynamic);
memset(&m_live[0], 0, m_live.size()*sizeof(m_live[0]));
// allocate the lookup table
m_table.resize((size_t)1 << (m_addrwidth - m_pageshift));
memset(&m_table[0], 0, m_table.size() * sizeof(m_table[0]));
m_refcnt.resize((size_t)1 << (m_addrwidth - m_pageshift));
memset(&m_refcnt[0], 0, m_refcnt.size() * sizeof(m_refcnt[0]));
// pointer to first element for quick access
m_table_base = &m_table[0];
// allocate the fixed page count array
if (m_fixed > 0)
{
m_fixedpages.resize(m_fixed);
memset(&m_fixedpages[0], 0, m_fixed*sizeof(m_fixedpages[0]));
}
}
//-------------------------------------------------
// interface_post_start - work to be done after
// actually starting a device
//-------------------------------------------------
void device_vtlb_interface::interface_post_start()
{
device().save_item(NAME(m_live));
device().save_item(NAME(m_table));
device().save_item(NAME(m_refcnt));
if (m_fixed > 0)
device().save_item(NAME(m_fixedpages));
}
//-------------------------------------------------
// interface_pre_reset - work to be done prior to
// actually resetting a device
//-------------------------------------------------
void device_vtlb_interface::interface_pre_reset()
{
vtlb_flush_dynamic();
}
//**************************************************************************
// FILLING
//**************************************************************************
//-------------------------------------------------
// vtlb_fill - called by the CPU core in
// response to an unmapped access
//-------------------------------------------------
bool device_vtlb_interface::vtlb_fill(offs_t address, offs_t taddress, int intention)
{
offs_t tableindex = address >> m_pageshift;
vtlb_entry entry = m_table[tableindex];
#if PRINTF_TLB
osd_printf_debug("vtlb_fill: %08X(%X) ... ", address, intention);
#endif
// should not be called here if the entry is in the table already
// assert((entry & (1 << intention)) == 0);
// if we have no dynamic entries, we always fail
if (m_dynamic == 0)
{
#if PRINTF_TLB
osd_printf_debug("failed: no dynamic entries\n");
#endif
return false;
}
// if this is the first successful translation for this address, allocate a new entry
if ((entry & FLAGS_MASK) == 0)
{
int liveindex = m_dynindex;
m_dynindex = (m_dynindex + 1) % m_dynamic;
// if an entry already exists at this index, free it
if (m_live[liveindex] != 0)
{
if (m_refcnt[m_live[liveindex] - 1] <= 1)
m_table[m_live[liveindex] - 1] = 0;
else
m_refcnt[m_live[liveindex] - 1]--;
}
// claim this new entry
m_live[liveindex] = tableindex + 1;
// form a new blank entry
entry = (taddress >> m_pageshift) << m_pageshift;
entry |= FLAG_VALID;
#if PRINTF_TLB
osd_printf_debug("success (%08X), new entry\n", taddress);
#endif
}
// otherwise, ensure that different intentions do not produce different addresses
else
{
assert((entry >> m_pageshift) == (taddress >> m_pageshift));
assert(entry & FLAG_VALID);
#if PRINTF_TLB
osd_printf_debug("success (%08X), existing entry\n", taddress);
#endif
}
// add the intention to the list of valid intentions and store
entry |= 1 << intention;
m_table[tableindex] = entry;
return true;
}
//-------------------------------------------------
// vtlb_load - load a fixed VTLB entry
//-------------------------------------------------
void device_vtlb_interface::vtlb_load(int entrynum, int numpages, offs_t address, vtlb_entry value)
{
offs_t tableindex = address >> m_pageshift;
int liveindex = m_dynamic + entrynum;
int pagenum;
// must be in range
assert(entrynum >= 0 && entrynum < m_fixed);
#if PRINTF_TLB
osd_printf_debug("vtlb_load %d for %d pages at %08X == %08X\n", entrynum, numpages, address, value);
#endif
// if an entry already exists at this index, free it
if (m_live[liveindex] != 0)
{
int oldtableindex = m_live[liveindex] - 1;
m_refcnt[oldtableindex]--;
if (m_refcnt[oldtableindex] == 0) {
int pagecount = m_fixedpages[entrynum];
for (pagenum = 0; pagenum < pagecount; pagenum++) {
m_table[oldtableindex + pagenum] = 0;
}
}
}
// claim this new entry
m_live[liveindex] = tableindex + 1;
m_refcnt[tableindex]++;
// store the raw value, making sure the "fixed" flag is set
value |= FLAG_FIXED;
m_fixedpages[entrynum] = numpages;
for (pagenum = 0; pagenum < numpages; pagenum++)
m_table[tableindex + pagenum] = value + (pagenum << m_pageshift);
}
//-------------------------------------------------
// vtlb_dynload - load a dynamic VTLB entry
//-------------------------------------------------
void device_vtlb_interface::vtlb_dynload(u32 index, offs_t address, vtlb_entry value)
{
vtlb_entry entry = m_table[index];
if (m_dynamic == 0)
{
#if PRINTF_TLB
osd_printf_debug("failed: no dynamic entries\n");
#endif
return;
}
int liveindex = m_dynindex;
m_dynindex = (m_dynindex + 1) % m_dynamic;
// is entry already live?
if (!(entry & FLAG_VALID))
{
// if an entry already exists at this index, free it
if (m_live[liveindex] != 0)
m_table[m_live[liveindex] - 1] = 0;
// claim this new entry
m_live[liveindex] = index + 1;
}
// form a new blank entry
entry = (address >> m_pageshift) << m_pageshift;
entry |= FLAG_VALID | value;
#if PRINTF_TLB
osd_printf_debug("success (%08X), new entry\n", address);
#endif
m_table[index] = entry;
}
//**************************************************************************
// FLUSHING
//**************************************************************************
//-------------------------------------------------
// vtlb_flush_dynamic - flush all knowledge
// from the dynamic part of the VTLB
//-------------------------------------------------
void device_vtlb_interface::vtlb_flush_dynamic()
{
#if PRINTF_TLB
osd_printf_debug("vtlb_flush_dynamic\n");
#endif
// loop over live entries and release them from the table
for (int liveindex = 0; liveindex < m_dynamic; liveindex++)
if (m_live[liveindex] != 0)
{
offs_t tableindex = m_live[liveindex] - 1;
m_table[tableindex] = 0;
m_live[liveindex] = 0;
}
}
//-------------------------------------------------
// vtlb_flush_address - flush knowledge of a
// particular address from the VTLB
//-------------------------------------------------
void device_vtlb_interface::vtlb_flush_address(offs_t address)
{
offs_t tableindex = address >> m_pageshift;
#if PRINTF_TLB
osd_printf_debug("vtlb_flush_address %08X\n", address);
#endif
// free the entry in the table; for speed, we leave the entry in the live array
m_table[tableindex] = 0;
}
//**************************************************************************
// ACCESSORS
//**************************************************************************
//-------------------------------------------------
// vtlb_table - return a pointer to the base of
// the linear VTLB lookup table
//-------------------------------------------------
const device_vtlb_interface::vtlb_entry *device_vtlb_interface::vtlb_table() const
{
return m_table_base;
}