mirror of
https://github.com/holub/mame
synced 2025-04-15 21:16:28 +03:00

* Do not build x64 DRC backend when C backend is forced * New CI target (mametiny + kinst) * Switch travis to CI target * Switch appveyor to CI target
1129 lines
39 KiB
C++
1129 lines
39 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Aaron Giles
|
|
/***************************************************************************
|
|
|
|
drcuml.c
|
|
|
|
Universal machine language for dynamic recompiling CPU cores.
|
|
|
|
****************************************************************************
|
|
|
|
Future improvements/changes:
|
|
|
|
* UML optimizer:
|
|
- constant folding
|
|
|
|
* Write a back-end validator:
|
|
- checks all combinations of memory/register/immediate on all params
|
|
- checks behavior of all opcodes
|
|
|
|
* Extend registers to 16? Depends on if PPC can use them
|
|
|
|
* Support for FPU exceptions
|
|
|
|
* New instructions?
|
|
- VALID opcode_desc,handle,param
|
|
checksum/compare code referenced by opcode_desc; if not
|
|
matching, generate exception with handle,param
|
|
|
|
- RECALL handle
|
|
change code at caller to call handle in the future
|
|
|
|
***************************************************************************/
|
|
|
|
#include "emu.h"
|
|
#include "drcuml.h"
|
|
|
|
#include "emuopts.h"
|
|
#include "drcbec.h"
|
|
#ifdef NATIVE_DRC
|
|
#include "drcbex86.h"
|
|
#include "drcbex64.h"
|
|
#endif
|
|
|
|
#include <fstream>
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// DEBUGGING
|
|
//**************************************************************************
|
|
|
|
#define VALIDATE_BACKEND (0)
|
|
#define LOG_SIMPLIFICATIONS (0)
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// TYPE DEFINITIONS
|
|
//**************************************************************************
|
|
|
|
// determine the type of the native DRC, falling back to C
|
|
#ifndef NATIVE_DRC
|
|
typedef drcbe_c drcbe_native;
|
|
#else
|
|
typedef NATIVE_DRC drcbe_native;
|
|
#endif
|
|
|
|
|
|
// structure describing back-end validation test
|
|
struct bevalidate_test
|
|
{
|
|
uml::opcode_t opcode;
|
|
u8 size;
|
|
u8 iflags;
|
|
u8 flags;
|
|
u64 param[4];
|
|
};
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// DRC BACKEND INTERFACE
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// drcbe_interface - constructor
|
|
//-------------------------------------------------
|
|
|
|
drcbe_interface::drcbe_interface(drcuml_state &drcuml, drc_cache &cache, device_t &device)
|
|
: m_drcuml(drcuml)
|
|
, m_cache(cache)
|
|
, m_device(device)
|
|
, m_space()
|
|
, m_state(*reinterpret_cast<drcuml_machine_state *>(cache.alloc_near(sizeof(m_state))))
|
|
, m_accessors(nullptr)
|
|
{
|
|
// reset the machine state
|
|
memset(&m_state, 0, sizeof(m_state));
|
|
|
|
// find the spaces and fetch memory accessors
|
|
device_memory_interface *memory;
|
|
if (device.interface(memory))
|
|
{
|
|
int const count = memory->max_space_count();
|
|
m_accessors = reinterpret_cast<data_accessors *>(cache.alloc_near(sizeof(*m_accessors) * count));
|
|
memset(m_accessors, 0, sizeof(*m_accessors) * count);
|
|
m_space.resize(count, nullptr);
|
|
|
|
for (int spacenum = 0; spacenum < count; ++spacenum)
|
|
{
|
|
if (memory->has_space(spacenum))
|
|
{
|
|
m_space[spacenum] = &memory->space(spacenum);
|
|
m_space[spacenum]->accessors(m_accessors[spacenum]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// ~drcbe_interface - destructor
|
|
//-------------------------------------------------
|
|
|
|
drcbe_interface::~drcbe_interface()
|
|
{
|
|
}
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// DRCUML STATE
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// drcuml_state - constructor
|
|
//-------------------------------------------------
|
|
|
|
drcuml_state::drcuml_state(device_t &device, drc_cache &cache, u32 flags, int modes, int addrbits, int ignorebits)
|
|
: m_device(device)
|
|
, m_cache(cache)
|
|
, m_beintf(device.machine().options().drc_use_c()
|
|
? std::unique_ptr<drcbe_interface>{ new drcbe_c(*this, device, cache, flags, modes, addrbits, ignorebits) }
|
|
: std::unique_ptr<drcbe_interface>{ new drcbe_native(*this, device, cache, flags, modes, addrbits, ignorebits) })
|
|
, m_umllog(device.machine().options().drc_log_uml()
|
|
? new std::ofstream(util::string_format("drcuml_%s.asm", device.shortname()))
|
|
: nullptr)
|
|
, m_blocklist()
|
|
, m_handlelist()
|
|
, m_symlist()
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// ~drcuml_state - destructor
|
|
//-------------------------------------------------
|
|
|
|
drcuml_state::~drcuml_state()
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// reset - reset the state completely, flushing
|
|
// the cache and all information
|
|
//-------------------------------------------------
|
|
|
|
void drcuml_state::reset()
|
|
{
|
|
// if we error here, we are screwed
|
|
try
|
|
{
|
|
// flush the cache
|
|
m_cache.flush();
|
|
|
|
// reset all handle code pointers
|
|
for (uml::code_handle &handle : m_handlelist)
|
|
*handle.codeptr_addr() = nullptr;
|
|
|
|
// call the backend to reset
|
|
m_beintf->reset();
|
|
|
|
// do a one-time validation if requested
|
|
#if 0
|
|
if (VALIDATE_BACKEND)
|
|
{
|
|
static bool validated = false;
|
|
if (!validated)
|
|
{
|
|
validated = true;
|
|
validate_backend(this);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
catch (drcuml_block::abort_compilation &)
|
|
{
|
|
fatalerror("Out of cache space in drcuml_state::reset\n");
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// begin_block - begin a new code block
|
|
//-------------------------------------------------
|
|
|
|
drcuml_block &drcuml_state::begin_block(uint32_t maxinst)
|
|
{
|
|
// find an inactive block that matches our qualifications
|
|
drcuml_block *bestblock(nullptr);
|
|
for (drcuml_block &block : m_blocklist)
|
|
{
|
|
if (!block.inuse() && (block.maxinst() >= maxinst) && (!bestblock || (block.maxinst() < bestblock->maxinst())))
|
|
bestblock = █
|
|
}
|
|
|
|
// if we failed to find one, allocate a new one
|
|
if (!bestblock)
|
|
bestblock = &*m_blocklist.emplace(m_blocklist.end(), *this, maxinst * 3 / 2);
|
|
|
|
// start the block
|
|
bestblock->begin();
|
|
return *bestblock;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// handle_alloc - allocate a new handle
|
|
//-------------------------------------------------
|
|
|
|
uml::code_handle *drcuml_state::handle_alloc(char const *name)
|
|
{
|
|
// allocate the handle, add it to our list, and return it
|
|
return &*m_handlelist.emplace(m_handlelist.end(), *this, name);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// symbol_add - add a symbol to the internal
|
|
// symbol table
|
|
//-------------------------------------------------
|
|
|
|
void drcuml_state::symbol_add(void *base, u32 length, char const *name)
|
|
{
|
|
m_symlist.emplace_back(base, length, name);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// symbol_find - look up a symbol from the
|
|
// internal symbol table or return nullptr if not
|
|
// found
|
|
//-------------------------------------------------
|
|
|
|
const char *drcuml_state::symbol_find(void *base, u32 *offset)
|
|
{
|
|
drccodeptr const search(reinterpret_cast<drccodeptr>(base));
|
|
|
|
// simple linear search
|
|
for (symbol const &cursym : m_symlist)
|
|
{
|
|
// if no offset pointer, only match perfectly
|
|
if (cursym.includes(search) && (offset || (cursym.base() == search)))
|
|
{
|
|
// return the offset and name
|
|
if (offset)
|
|
*offset = search - cursym.base();
|
|
return cursym.name().c_str();
|
|
}
|
|
}
|
|
|
|
// not found; return nullptr
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// log_vprintf - directly printf to the UML log
|
|
// if generated
|
|
//-------------------------------------------------
|
|
|
|
void drcuml_state::log_vprintf(util::format_argument_pack<std::ostream> const &args)
|
|
{
|
|
// if we have a file, print to it
|
|
if (m_umllog)
|
|
{
|
|
util::stream_format(*m_umllog, args);
|
|
m_umllog->flush();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// DRCUML BLOCK
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// drcuml_block - constructor
|
|
//-------------------------------------------------
|
|
|
|
drcuml_block::drcuml_block(drcuml_state &drcuml, u32 maxinst)
|
|
: m_drcuml(drcuml)
|
|
, m_nextinst(0)
|
|
, m_maxinst(maxinst * 3/2)
|
|
, m_inst(m_maxinst)
|
|
, m_inuse(false)
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// ~drcuml_block - destructor
|
|
//-------------------------------------------------
|
|
|
|
drcuml_block::~drcuml_block()
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// begin - begin code generation
|
|
//-------------------------------------------------
|
|
|
|
void drcuml_block::begin()
|
|
{
|
|
// set up the block information and return it
|
|
m_inuse = true;
|
|
m_nextinst = 0;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// end - complete a code block and commit it to
|
|
// the cache via the back-end
|
|
//-------------------------------------------------
|
|
|
|
void drcuml_block::end()
|
|
{
|
|
assert(m_inuse);
|
|
|
|
// optimize the resulting code first
|
|
optimize();
|
|
|
|
// if we have a logfile, generate a disassembly of the block
|
|
if (m_drcuml.logging())
|
|
disassemble();
|
|
|
|
// generate the code via the back-end
|
|
m_drcuml.generate(*this, &m_inst[0], m_nextinst);
|
|
|
|
// block is no longer in use
|
|
m_inuse = false;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// abort - abort a code block in progress
|
|
//-------------------------------------------------
|
|
|
|
void drcuml_block::abort()
|
|
{
|
|
assert(m_inuse);
|
|
|
|
// block is no longer in use
|
|
m_inuse = false;
|
|
|
|
// unwind
|
|
throw abort_compilation();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// append - append an opcode to the block
|
|
//-------------------------------------------------
|
|
|
|
uml::instruction &drcuml_block::append()
|
|
{
|
|
// get a pointer to the next instruction
|
|
uml::instruction &curinst(m_inst[m_nextinst++]);
|
|
if (m_nextinst > m_maxinst)
|
|
fatalerror("Overran maxinst in drcuml_block_append\n");
|
|
|
|
return curinst;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// optimize - apply various optimizations to a
|
|
// block of code
|
|
//-------------------------------------------------
|
|
|
|
void drcuml_block::optimize()
|
|
{
|
|
u32 mapvar[uml::MAPVAR_COUNT] = { 0 };
|
|
|
|
// iterate over instructions
|
|
for (int instnum = 0; instnum < m_nextinst; instnum++)
|
|
{
|
|
uml::instruction &inst(m_inst[instnum]);
|
|
|
|
// first compute what flags we need
|
|
u8 accumflags(0);
|
|
u8 remainingflags(inst.output_flags());
|
|
|
|
// scan ahead until we run out of possible remaining flags
|
|
for (int scannum = instnum + 1; remainingflags != 0 && scannum < m_nextinst; scannum++)
|
|
{
|
|
// any input flags are required
|
|
uml::instruction const &scan(m_inst[scannum]);
|
|
accumflags |= scan.input_flags();
|
|
|
|
// if the scanahead instruction is unconditional, assume his flags are modified
|
|
if (scan.condition() == uml::COND_ALWAYS)
|
|
remainingflags &= ~scan.modified_flags();
|
|
}
|
|
inst.set_flags(accumflags);
|
|
|
|
// track mapvars
|
|
if (inst.opcode() == uml::OP_MAPVAR)
|
|
mapvar[inst.param(0).mapvar() - uml::MAPVAR_M0] = inst.param(1).immediate();
|
|
|
|
// convert all mapvar parameters to immediates
|
|
else if (inst.opcode() != uml::OP_RECOVER)
|
|
for (int pnum = 0; pnum < inst.numparams(); pnum++)
|
|
if (inst.param(pnum).is_mapvar())
|
|
inst.set_mapvar(pnum, mapvar[inst.param(pnum).mapvar() - uml::MAPVAR_M0]);
|
|
|
|
// now that flags are correct, simplify the instruction
|
|
inst.simplify();
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// disassemble - disassemble a block of
|
|
// instructions to the log
|
|
//-------------------------------------------------
|
|
|
|
void drcuml_block::disassemble()
|
|
{
|
|
std::string comment;
|
|
|
|
// iterate over instructions and output
|
|
int firstcomment(-1);
|
|
for (int instnum = 0; instnum < m_nextinst; instnum++)
|
|
{
|
|
uml::instruction const &inst(m_inst[instnum]);
|
|
bool flushcomments(false);
|
|
|
|
// remember comments and mapvars for later
|
|
if (inst.opcode() == uml::OP_COMMENT || inst.opcode() == uml::OP_MAPVAR)
|
|
{
|
|
if (firstcomment == -1)
|
|
firstcomment = instnum;
|
|
}
|
|
|
|
// print labels, handles, and hashes left justified
|
|
else if (inst.opcode() == uml::OP_LABEL)
|
|
m_drcuml.log_printf("$%X:\n", u32(inst.param(0).label()));
|
|
else if (inst.opcode() == uml::OP_HANDLE)
|
|
m_drcuml.log_printf("%s:\n", inst.param(0).handle().string());
|
|
else if (inst.opcode() == uml::OP_HASH)
|
|
m_drcuml.log_printf("(%X,%X):\n", u32(inst.param(0).immediate()), u32(inst.param(1).immediate()));
|
|
|
|
// indent everything else with a tab
|
|
else
|
|
{
|
|
std::string const dasm(m_inst[instnum].disasm(&m_drcuml));
|
|
|
|
// include the first accumulated comment with this line
|
|
if (firstcomment != -1)
|
|
{
|
|
m_drcuml.log_printf("\t%-50.50s; %s\n", dasm, get_comment_text(m_inst[firstcomment], comment));
|
|
firstcomment++;
|
|
flushcomments = true;
|
|
}
|
|
else
|
|
{
|
|
m_drcuml.log_printf("\t%s\n", dasm);
|
|
}
|
|
}
|
|
|
|
// flush any comments pending
|
|
if (firstcomment != -1 && (flushcomments || instnum == m_nextinst - 1))
|
|
{
|
|
while (firstcomment <= instnum)
|
|
{
|
|
char const *const text(get_comment_text(m_inst[firstcomment++], comment));
|
|
if (text)
|
|
m_drcuml.log_printf("\t%50s; %s\n", "", text);
|
|
}
|
|
firstcomment = -1;
|
|
}
|
|
}
|
|
m_drcuml.log_printf("\n\n");
|
|
m_drcuml.log_flush();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// get_comment_text - determine the text
|
|
// associated with a comment or mapvar
|
|
//-------------------------------------------------
|
|
|
|
char const *drcuml_block::get_comment_text(uml::instruction const &inst, std::string &comment)
|
|
{
|
|
if (inst.opcode() == uml::OP_COMMENT)
|
|
{
|
|
// comments return their strings
|
|
return comment.assign(inst.param(0).string()).c_str();
|
|
}
|
|
else if (inst.opcode() == uml::OP_MAPVAR)
|
|
{
|
|
// mapvars comment about their values
|
|
comment = string_format("m%d = $%X", int(inst.param(0).mapvar() - uml::MAPVAR_M0), u32(inst.param(1).immediate()));
|
|
return comment.c_str();
|
|
}
|
|
else
|
|
{
|
|
// everything else is nullptr
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/***************************************************************************
|
|
BACK-END VALIDATION
|
|
***************************************************************************/
|
|
|
|
//-------------------------------------------------
|
|
// effective_test_psize - return the effective
|
|
// parameter size based on the size and fixed
|
|
// array of parameter values
|
|
//-------------------------------------------------
|
|
|
|
inline uint8_t effective_test_psize(const opcode_info &opinfo, int pnum, int instsize, const uint64_t *params)
|
|
{
|
|
switch (opinfo.param[pnum].size)
|
|
{
|
|
case PSIZE_4: return 4;
|
|
case PSIZE_8: return 8;
|
|
case PSIZE_OP: return instsize;
|
|
case PSIZE_P1: return 1 << (params[0] & 3);
|
|
case PSIZE_P2: return 1 << (params[1] & 3);
|
|
case PSIZE_P3: return 1 << (params[2] & 3);
|
|
case PSIZE_P4: return 1 << (params[3] & 3);
|
|
}
|
|
return instsize;
|
|
}
|
|
|
|
#define TEST_ENTRY_2(op, size, p1, p2, flags) { OP_##op, size, 0, flags, { u64(p1), u64(p2) } },
|
|
#define TEST_ENTRY_2F(op, size, p1, p2, iflags, flags) { OP_##op, size, iflags, flags, { u64(p1), u64(p2) } },
|
|
#define TEST_ENTRY_3(op, size, p1, p2, p3, flags) { OP_##op, size, 0, flags, { u64(p1), u64(p2), u64(p3) } },
|
|
#define TEST_ENTRY_3F(op, size, p1, p2, p3, iflags, flags) { OP_##op, size, iflags, flags, { u64(p1), u64(p2), u64(p3) } },
|
|
#define TEST_ENTRY_4(op, size, p1, p2, p3, p4, flags) { OP_##op, size, 0, flags, { u64(p1), u64(p2), u64(p3), u64(p4) } },
|
|
#define TEST_ENTRY_4F(op, size, p1, p2, p3, p4, iflags, flags) { OP_##op, size, iflags, flags, { u64(p1), u64(p2), u64(p3), u64(p4) } },
|
|
|
|
static const bevalidate_test bevalidate_test_list[] =
|
|
{
|
|
TEST_ENTRY_3(ADD, 4, 0x7fffffff, 0x12345678, 0x6dcba987, 0)
|
|
TEST_ENTRY_3(ADD, 4, 0x80000000, 0x12345678, 0x6dcba988, FLAG_V | FLAG_S)
|
|
TEST_ENTRY_3(ADD, 4, 0xffffffff, 0x92345678, 0x6dcba987, FLAG_S)
|
|
TEST_ENTRY_3(ADD, 4, 0x00000000, 0x92345678, 0x6dcba988, FLAG_C | FLAG_Z)
|
|
|
|
TEST_ENTRY_3(ADD, 8, 0x7fffffffffffffff, 0x0123456789abcdef, 0x7edcba9876543210, 0)
|
|
TEST_ENTRY_3(ADD, 8, 0x8000000000000000, 0x0123456789abcdef, 0x7edcba9876543211, FLAG_V | FLAG_S)
|
|
TEST_ENTRY_3(ADD, 8, 0xffffffffffffffff, 0x8123456789abcdef, 0x7edcba9876543210, FLAG_S)
|
|
TEST_ENTRY_3(ADD, 8, 0x0000000000000000, 0x8123456789abcdef, 0x7edcba9876543211, FLAG_C | FLAG_Z)
|
|
|
|
TEST_ENTRY_3F(ADDC, 4, 0x7fffffff, 0x12345678, 0x6dcba987, 0, 0)
|
|
TEST_ENTRY_3F(ADDC, 4, 0x7fffffff, 0x12345678, 0x6dcba986, FLAG_C, 0)
|
|
TEST_ENTRY_3F(ADDC, 4, 0x80000000, 0x12345678, 0x6dcba988, 0, FLAG_V | FLAG_S)
|
|
TEST_ENTRY_3F(ADDC, 4, 0x80000000, 0x12345678, 0x6dcba987, FLAG_C, FLAG_V | FLAG_S)
|
|
TEST_ENTRY_3F(ADDC, 4, 0xffffffff, 0x92345678, 0x6dcba987, 0, FLAG_S)
|
|
TEST_ENTRY_3F(ADDC, 4, 0xffffffff, 0x92345678, 0x6dcba986, FLAG_C, FLAG_S)
|
|
TEST_ENTRY_3F(ADDC, 4, 0x00000000, 0x92345678, 0x6dcba988, 0, FLAG_C | FLAG_Z)
|
|
TEST_ENTRY_3F(ADDC, 4, 0x00000000, 0x92345678, 0x6dcba987, FLAG_C, FLAG_C | FLAG_Z)
|
|
TEST_ENTRY_3F(ADDC, 4, 0x12345678, 0x12345678, 0xffffffff, FLAG_C, FLAG_C)
|
|
|
|
TEST_ENTRY_3F(ADDC, 8, 0x7fffffffffffffff, 0x0123456789abcdef, 0x7edcba9876543210, 0, 0)
|
|
TEST_ENTRY_3F(ADDC, 8, 0x7fffffffffffffff, 0x0123456789abcdef, 0x7edcba987654320f, FLAG_C, 0)
|
|
TEST_ENTRY_3F(ADDC, 8, 0x8000000000000000, 0x0123456789abcdef, 0x7edcba9876543211, 0, FLAG_V | FLAG_S)
|
|
TEST_ENTRY_3F(ADDC, 8, 0x8000000000000000, 0x0123456789abcdef, 0x7edcba9876543210, FLAG_C, FLAG_V | FLAG_S)
|
|
TEST_ENTRY_3F(ADDC, 8, 0xffffffffffffffff, 0x8123456789abcdef, 0x7edcba9876543210, 0, FLAG_S)
|
|
TEST_ENTRY_3F(ADDC, 8, 0xffffffffffffffff, 0x8123456789abcdef, 0x7edcba987654320f, FLAG_C, FLAG_S)
|
|
TEST_ENTRY_3F(ADDC, 8, 0x0000000000000000, 0x8123456789abcdef, 0x7edcba9876543211, 0, FLAG_C | FLAG_Z)
|
|
TEST_ENTRY_3F(ADDC, 8, 0x0000000000000000, 0x8123456789abcdef, 0x7edcba9876543210, FLAG_C, FLAG_C | FLAG_Z)
|
|
TEST_ENTRY_3F(ADDC, 8, 0x123456789abcdef0, 0x123456789abcdef0, 0xffffffffffffffff, FLAG_C, FLAG_C)
|
|
|
|
TEST_ENTRY_3(SUB, 4, 0x12345678, 0x7fffffff, 0x6dcba987, 0)
|
|
TEST_ENTRY_3(SUB, 4, 0x12345678, 0x80000000, 0x6dcba988, FLAG_V)
|
|
TEST_ENTRY_3(SUB, 4, 0x92345678, 0xffffffff, 0x6dcba987, FLAG_S)
|
|
TEST_ENTRY_3(SUB, 4, 0x92345678, 0x00000000, 0x6dcba988, FLAG_C | FLAG_S)
|
|
TEST_ENTRY_3(SUB, 4, 0x00000000, 0x12345678, 0x12345678, FLAG_Z)
|
|
|
|
TEST_ENTRY_3(SUB, 8, 0x0123456789abcdef, 0x7fffffffffffffff, 0x7edcba9876543210, 0)
|
|
TEST_ENTRY_3(SUB, 8, 0x0123456789abcdef, 0x8000000000000000, 0x7edcba9876543211, FLAG_V)
|
|
TEST_ENTRY_3(SUB, 8, 0x8123456789abcdef, 0xffffffffffffffff, 0x7edcba9876543210, FLAG_S)
|
|
TEST_ENTRY_3(SUB, 8, 0x8123456789abcdef, 0x0000000000000000, 0x7edcba9876543211, FLAG_C | FLAG_S)
|
|
TEST_ENTRY_3(SUB, 8, 0x0000000000000000, 0x0123456789abcdef, 0x0123456789abcdef, FLAG_Z)
|
|
|
|
TEST_ENTRY_3F(SUBB, 4, 0x12345678, 0x7fffffff, 0x6dcba987, 0, 0)
|
|
TEST_ENTRY_3F(SUBB, 4, 0x12345678, 0x7fffffff, 0x6dcba986, FLAG_C, 0)
|
|
TEST_ENTRY_3F(SUBB, 4, 0x12345678, 0x80000000, 0x6dcba988, 0, FLAG_V)
|
|
TEST_ENTRY_3F(SUBB, 4, 0x12345678, 0x80000000, 0x6dcba987, FLAG_C, FLAG_V)
|
|
TEST_ENTRY_3F(SUBB, 4, 0x92345678, 0xffffffff, 0x6dcba987, 0, FLAG_S)
|
|
TEST_ENTRY_3F(SUBB, 4, 0x92345678, 0xffffffff, 0x6dcba986, FLAG_C, FLAG_S)
|
|
TEST_ENTRY_3F(SUBB, 4, 0x92345678, 0x00000000, 0x6dcba988, 0, FLAG_C | FLAG_S)
|
|
TEST_ENTRY_3F(SUBB, 4, 0x92345678, 0x00000000, 0x6dcba987, FLAG_C, FLAG_C | FLAG_S)
|
|
TEST_ENTRY_3F(SUBB, 4, 0x12345678, 0x12345678, 0xffffffff, FLAG_C, FLAG_C)
|
|
TEST_ENTRY_3F(SUBB, 4, 0x00000000, 0x12345678, 0x12345677, FLAG_C, FLAG_Z)
|
|
|
|
TEST_ENTRY_3F(SUBB, 8, 0x0123456789abcdef, 0x7fffffffffffffff, 0x7edcba9876543210, 0, 0)
|
|
TEST_ENTRY_3F(SUBB, 8, 0x0123456789abcdef, 0x7fffffffffffffff, 0x7edcba987654320f, FLAG_C, 0)
|
|
TEST_ENTRY_3F(SUBB, 8, 0x0123456789abcdef, 0x8000000000000000, 0x7edcba9876543211, 0, FLAG_V)
|
|
TEST_ENTRY_3F(SUBB, 8, 0x0123456789abcdef, 0x8000000000000000, 0x7edcba9876543210, FLAG_C, FLAG_V)
|
|
TEST_ENTRY_3F(SUBB, 8, 0x8123456789abcdef, 0xffffffffffffffff, 0x7edcba9876543210, 0, FLAG_S)
|
|
TEST_ENTRY_3F(SUBB, 8, 0x8123456789abcdef, 0xffffffffffffffff, 0x7edcba987654320f, FLAG_C, FLAG_S)
|
|
TEST_ENTRY_3F(SUBB, 8, 0x8123456789abcdef, 0x0000000000000000, 0x7edcba9876543211, 0, FLAG_C | FLAG_S)
|
|
TEST_ENTRY_3F(SUBB, 8, 0x8123456789abcdef, 0x0000000000000000, 0x7edcba9876543210, FLAG_C, FLAG_C | FLAG_S)
|
|
TEST_ENTRY_3F(SUBB, 8, 0x123456789abcdef0, 0x123456789abcdef0, 0xffffffffffffffff, FLAG_C, FLAG_C)
|
|
TEST_ENTRY_3F(SUBB, 8, 0x0000000000000000, 0x123456789abcdef0, 0x123456789abcdeef, FLAG_C, FLAG_Z)
|
|
|
|
TEST_ENTRY_2(CMP, 4, 0x7fffffff, 0x6dcba987, 0)
|
|
TEST_ENTRY_2(CMP, 4, 0x80000000, 0x6dcba988, FLAG_V)
|
|
TEST_ENTRY_2(CMP, 4, 0xffffffff, 0x6dcba987, FLAG_S)
|
|
TEST_ENTRY_2(CMP, 4, 0x00000000, 0x6dcba988, FLAG_C | FLAG_S)
|
|
TEST_ENTRY_2(CMP, 4, 0x12345678, 0x12345678, FLAG_Z)
|
|
|
|
TEST_ENTRY_2(CMP, 8, 0x7fffffffffffffff, 0x7edcba9876543210, 0)
|
|
TEST_ENTRY_2(CMP, 8, 0x8000000000000000, 0x7edcba9876543211, FLAG_V)
|
|
TEST_ENTRY_2(CMP, 8, 0xffffffffffffffff, 0x7edcba9876543210, FLAG_S)
|
|
TEST_ENTRY_2(CMP, 8, 0x0000000000000000, 0x7edcba9876543211, FLAG_C | FLAG_S)
|
|
TEST_ENTRY_2(CMP, 8, 0x0123456789abcdef, 0x0123456789abcdef, FLAG_Z)
|
|
|
|
TEST_ENTRY_4(MULU, 4, 0x77777777, 0x00000000, 0x11111111, 0x00000007, 0)
|
|
TEST_ENTRY_4(MULU, 4, 0xffffffff, 0x00000000, 0x11111111, 0x0000000f, 0)
|
|
TEST_ENTRY_4(MULU, 4, 0x00000000, 0x00000000, 0x11111111, 0x00000000, FLAG_Z)
|
|
TEST_ENTRY_4(MULU, 4, 0xea61d951, 0x37c048d0, 0x77777777, 0x77777777, FLAG_V)
|
|
TEST_ENTRY_4(MULU, 4, 0x32323233, 0xcdcdcdcc, 0xcdcdcdcd, 0xffffffff, FLAG_V | FLAG_S)
|
|
|
|
TEST_ENTRY_4(MULU, 8, 0x7777777777777777, 0x0000000000000000, 0x1111111111111111, 0x0000000000000007, 0)
|
|
TEST_ENTRY_4(MULU, 8, 0xffffffffffffffff, 0x0000000000000000, 0x1111111111111111, 0x000000000000000f, 0)
|
|
TEST_ENTRY_4(MULU, 8, 0x0000000000000000, 0x0000000000000000, 0x1111111111111111, 0x0000000000000000, FLAG_Z)
|
|
TEST_ENTRY_4(MULU, 8, 0x0c83fb72ea61d951, 0x37c048d159e26af3, 0x7777777777777777, 0x7777777777777777, FLAG_V)
|
|
TEST_ENTRY_4(MULU, 8, 0x3232323232323233, 0xcdcdcdcdcdcdcdcc, 0xcdcdcdcdcdcdcdcd, 0xffffffffffffffff, FLAG_V | FLAG_S)
|
|
|
|
TEST_ENTRY_4(MULS, 4, 0x77777777, 0x00000000, 0x11111111, 0x00000007, 0)
|
|
TEST_ENTRY_4(MULS, 4, 0xffffffff, 0x00000000, 0x11111111, 0x0000000f, FLAG_V)
|
|
TEST_ENTRY_4(MULS, 4, 0x00000000, 0x00000000, 0x11111111, 0x00000000, FLAG_Z)
|
|
TEST_ENTRY_4(MULS, 4, 0x9e26af38, 0xc83fb72e, 0x77777777, 0x88888888, FLAG_V | FLAG_S)
|
|
TEST_ENTRY_4(MULS, 4, 0x32323233, 0x00000000, 0xcdcdcdcd, 0xffffffff, 0)
|
|
|
|
TEST_ENTRY_4(MULS, 8, 0x7777777777777777, 0x0000000000000000, 0x1111111111111111, 0x0000000000000007, 0)
|
|
TEST_ENTRY_4(MULS, 8, 0xffffffffffffffff, 0x0000000000000000, 0x1111111111111111, 0x000000000000000f, FLAG_V)
|
|
TEST_ENTRY_4(MULS, 8, 0x0000000000000000, 0x0000000000000000, 0x1111111111111111, 0x0000000000000000, FLAG_Z)
|
|
TEST_ENTRY_4(MULS, 8, 0x7c048d159e26af38, 0xc83fb72ea61d950c, 0x7777777777777777, 0x8888888888888888, FLAG_V | FLAG_S)
|
|
TEST_ENTRY_4(MULS, 8, 0x3232323232323233, 0x0000000000000000, 0xcdcdcdcdcdcdcdcd, 0xffffffffffffffff, 0)
|
|
|
|
TEST_ENTRY_4(DIVU, 4, 0x02702702, 0x00000003, 0x11111111, 0x00000007, 0)
|
|
TEST_ENTRY_4(DIVU, 4, 0x00000000, 0x11111111, 0x11111111, 0x11111112, FLAG_Z)
|
|
TEST_ENTRY_4(DIVU, 4, 0x7fffffff, 0x00000000, 0xfffffffe, 0x00000002, 0)
|
|
TEST_ENTRY_4(DIVU, 4, 0xfffffffe, 0x00000000, 0xfffffffe, 0x00000001, FLAG_S)
|
|
TEST_ENTRY_4(DIVU, 4, UNDEFINED, UNDEFINED, 0xffffffff, 0x00000000, FLAG_V)
|
|
|
|
TEST_ENTRY_4(DIVU, 8, 0x0270270270270270, 0x0000000000000001, 0x1111111111111111, 0x0000000000000007, 0)
|
|
TEST_ENTRY_4(DIVU, 8, 0x0000000000000000, 0x1111111111111111, 0x1111111111111111, 0x1111111111111112, FLAG_Z)
|
|
TEST_ENTRY_4(DIVU, 8, 0x7fffffffffffffff, 0x0000000000000000, 0xfffffffffffffffe, 0x0000000000000002, 0)
|
|
TEST_ENTRY_4(DIVU, 8, 0xfffffffffffffffe, 0x0000000000000000, 0xfffffffffffffffe, 0x0000000000000001, FLAG_S)
|
|
TEST_ENTRY_4(DIVU, 8, UNDEFINED, UNDEFINED, 0xffffffffffffffff, 0x0000000000000000, FLAG_V)
|
|
|
|
TEST_ENTRY_4(DIVS, 4, 0x02702702, 0x00000003, 0x11111111, 0x00000007, 0)
|
|
TEST_ENTRY_4(DIVS, 4, 0x00000000, 0x11111111, 0x11111111, 0x11111112, FLAG_Z)
|
|
TEST_ENTRY_4(DIVS, 4, 0xffffffff, 0x00000000, 0xfffffffe, 0x00000002, FLAG_S)
|
|
TEST_ENTRY_4(DIVS, 4, UNDEFINED, UNDEFINED, 0xffffffff, 0x00000000, FLAG_V)
|
|
|
|
TEST_ENTRY_4(DIVS, 8, 0x0270270270270270, 0x0000000000000001, 0x1111111111111111, 0x0000000000000007, 0)
|
|
TEST_ENTRY_4(DIVS, 8, 0x0000000000000000, 0x1111111111111111, 0x1111111111111111, 0x1111111111111112, FLAG_Z)
|
|
TEST_ENTRY_4(DIVS, 8, 0xffffffffffffffff, 0x0000000000000000, 0xfffffffffffffffe, 0x0000000000000002, FLAG_S)
|
|
TEST_ENTRY_4(DIVS, 8, UNDEFINED, UNDEFINED, 0xffffffffffffffff, 0x0000000000000000, FLAG_V)
|
|
};
|
|
|
|
|
|
/*-------------------------------------------------
|
|
validate_backend - execute a number of
|
|
generic tests on the backend code generator
|
|
-------------------------------------------------*/
|
|
|
|
static void validate_backend(drcuml_state *drcuml)
|
|
{
|
|
uml::code_handle *handles[3];
|
|
int tnum;
|
|
|
|
// allocate handles for the code
|
|
handles[0] = drcuml->handle_alloc("test_entry");
|
|
handles[1] = drcuml->handle_alloc("code_start");
|
|
handles[2] = drcuml->handle_alloc("code_end");
|
|
|
|
// iterate over test entries
|
|
printf("Backend validation....\n");
|
|
for (tnum = 31; tnum < ARRAY_LENGTH(bevalidate_test_list); tnum++)
|
|
{
|
|
const bevalidate_test *test = &bevalidate_test_list[tnum];
|
|
parameter param[ARRAY_LENGTH(test->param)];
|
|
char mnemonic[20], *dst;
|
|
const char *src;
|
|
|
|
// progress
|
|
dst = mnemonic;
|
|
for (src = opcode_info_table[test->opcode()]->mnemonic; *src != 0; src++)
|
|
{
|
|
if (*src == '!')
|
|
{
|
|
if (test->size == 8)
|
|
*dst++ = 'd';
|
|
}
|
|
else if (*src == '#')
|
|
*dst++ = (test->size == 8) ? 'd' : 's';
|
|
else
|
|
*dst++ = *src;
|
|
}
|
|
*dst = 0;
|
|
printf("Executing test %d/%d (%s)", tnum + 1, (int)ARRAY_LENGTH(bevalidate_test_list), mnemonic);
|
|
|
|
// reset parameter list and iterate
|
|
memset(param, 0, sizeof(param));
|
|
bevalidate_iterate_over_params(drcuml, handles, test, param, 0);
|
|
printf("\n");
|
|
}
|
|
fatalerror("All tests passed!\n");
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
bevalidate_iterate_over_params - iterate over
|
|
all supported types and values of a parameter
|
|
and recursively hand off to the next parameter,
|
|
or else move on to iterate over the flags
|
|
-------------------------------------------------*/
|
|
|
|
static void bevalidate_iterate_over_params(drcuml_state *drcuml, uml::code_handle **handles, const bevalidate_test *test, parameter *paramlist, int pnum)
|
|
{
|
|
const opcode_info *opinfo = opcode_info_table[test->opcode()];
|
|
drcuml_ptype ptype;
|
|
|
|
// if no parameters, execute now
|
|
if (pnum >= ARRAY_LENGTH(opinfo->param) || opinfo->param[pnum].typemask == PTYPES_NONE)
|
|
{
|
|
bevalidate_iterate_over_flags(drcuml, handles, test, paramlist);
|
|
return;
|
|
}
|
|
|
|
// iterate over valid parameter types
|
|
for (ptype = parameter::PTYPE_IMMEDIATE; ptype < parameter::PTYPE_MAX; ptype++)
|
|
if (opinfo->param[pnum].typemask & (1 << ptype))
|
|
{
|
|
int pindex, pcount;
|
|
|
|
// mapvars can only do 32-bit tests
|
|
if (ptype == parameter::PTYPE_MAPVAR && effective_test_psize(opinfo, pnum, test->size, test->param) == 8)
|
|
continue;
|
|
|
|
// for some parameter types, we wish to iterate over all possibilities
|
|
switch (ptype)
|
|
{
|
|
case parameter::PTYPE_INT_REGISTER: pcount = REG_I_END - REG_I0; break;
|
|
case parameter::PTYPE_FLOAT_REGISTER: pcount = REG_F_END - REG_F0; break;
|
|
default: pcount = 1; break;
|
|
}
|
|
|
|
// iterate over possibilities
|
|
for (pindex = 0; pindex < pcount; pindex++)
|
|
{
|
|
bool skip = false;
|
|
int pscannum;
|
|
|
|
// for param 0, print a dot
|
|
if (pnum == 0)
|
|
printf(".");
|
|
|
|
// can't duplicate multiple source parameters unless they are immediates
|
|
if (ptype != parameter::PTYPE_IMMEDIATE && (opinfo->param[pnum].output & PIO_IN))
|
|
|
|
// loop over all parameters we've done before; if the parameter is a source and matches us, skip this case
|
|
for (pscannum = 0; pscannum < pnum; pscannum++)
|
|
if ((opinfo->param[pscannum].output & PIO_IN) && ptype == paramlist[pscannum].type && pindex == paramlist[pscannum].value)
|
|
skip = true;
|
|
|
|
// can't duplicate multiple dest parameters
|
|
if (opinfo->param[pnum].output & PIO_OUT)
|
|
|
|
// loop over all parameters we've done before; if the parameter is a source and matches us, skip this case
|
|
for (pscannum = 0; pscannum < pnum; pscannum++)
|
|
if ((opinfo->param[pscannum].output & PIO_OUT) && ptype == paramlist[pscannum].type && pindex == paramlist[pscannum].value)
|
|
skip = true;
|
|
|
|
// iterate over the next parameter in line
|
|
if (!skip)
|
|
{
|
|
paramlist[pnum].type = ptype;
|
|
paramlist[pnum].value = pindex;
|
|
bevalidate_iterate_over_params(drcuml, handles, test, paramlist, pnum + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
bevalidate_iterate_over_flags - iterate over
|
|
all supported flag masks
|
|
-------------------------------------------------*/
|
|
|
|
static void bevalidate_iterate_over_flags(drcuml_state *drcuml, uml::code_handle **handles, const bevalidate_test *test, parameter *paramlist)
|
|
{
|
|
const opcode_info *opinfo = opcode_info_table[test->opcode()];
|
|
uint8_t flagmask = opinfo->outflags;
|
|
uint8_t curmask;
|
|
|
|
// iterate over all possible flag combinations
|
|
for (curmask = 0; curmask <= flagmask; curmask++)
|
|
if ((curmask & flagmask) == curmask)
|
|
bevalidate_execute(drcuml, handles, test, paramlist, curmask);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
bevalidate_execute - execute a single instance
|
|
of a test, generating code and verifying the
|
|
results
|
|
-------------------------------------------------*/
|
|
|
|
static void bevalidate_execute(drcuml_state *drcuml, uml::code_handle **handles, const bevalidate_test *test, const parameter *paramlist, uint8_t flagmask)
|
|
{
|
|
parameter params[ARRAY_LENGTH(test->param)];
|
|
drcuml_machine_state istate, fstate;
|
|
instruction testinst;
|
|
drcuml_block *block;
|
|
uint64_t *parammem;
|
|
int numparams;
|
|
|
|
// allocate memory for parameters
|
|
parammem = (uint64_t *)drcuml->cache->alloc_near(sizeof(uint64_t) * (ARRAY_LENGTH(test->param) + 1));
|
|
|
|
// flush the cache
|
|
drcuml->reset();
|
|
|
|
// start a new block
|
|
block = drcuml->block_begin(30);
|
|
UML_HANDLE(block, handles[0]);
|
|
|
|
// set up a random initial state
|
|
bevalidate_initialize_random_state(drcuml, block, &istate);
|
|
|
|
// then populate the state with the parameters
|
|
numparams = bevalidate_populate_state(block, &istate, test, paramlist, params, parammem);
|
|
|
|
// generate the code
|
|
UML_RESTORE(block, &istate);
|
|
UML_HANDLE(block, handles[1]);
|
|
switch (numparams)
|
|
{
|
|
case 0:
|
|
block->append(test->opcode(), test->size);
|
|
break;
|
|
|
|
case 1:
|
|
block->append(test->opcode(), test->size, params[0]);
|
|
break;
|
|
|
|
case 2:
|
|
block->append(test->opcode(), test->size, params[0], params[1]);
|
|
break;
|
|
|
|
case 3:
|
|
block->append(test->opcode(), test->size, params[0], params[1], params[2]);
|
|
break;
|
|
|
|
case 4:
|
|
block->append(test->opcode(), test->size, params[0], params[1], params[2], params[3]);
|
|
break;
|
|
}
|
|
testinst = block->inst[block->nextinst - 1];
|
|
UML_HANDLE(block, handles[2]);
|
|
UML_GETFLGS(block, MEM(¶mmem[ARRAY_LENGTH(test->param)]), flagmask);
|
|
UML_SAVE(block, &fstate);
|
|
UML_EXIT(block, IMM(0));
|
|
|
|
// end the block
|
|
block->end();
|
|
|
|
// execute
|
|
drcuml->execute(*handles[0]);
|
|
|
|
// verify the results
|
|
bevalidate_verify_state(drcuml, &istate, &fstate, test, *(uint32_t *)¶mmem[ARRAY_LENGTH(test->param)], params, &testinst, handles[1]->code, handles[2]->code, flagmask);
|
|
|
|
// free memory
|
|
drcuml->cache->dealloc(parammem, sizeof(uint64_t) * (ARRAY_LENGTH(test->param) + 1));
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
bevalidate_initialize_random_state -
|
|
initialize the machine state to randomness
|
|
-------------------------------------------------*/
|
|
|
|
static void bevalidate_initialize_random_state(drcuml_state *drcuml, drcuml_block *block, drcuml_machine_state *state)
|
|
{
|
|
running_machine &machine = drcuml->device->machine();
|
|
int regnum;
|
|
|
|
// initialize core state to random values
|
|
state->fmod = machine.rand() & 0x03;
|
|
state->flags = machine.rand() & 0x1f;
|
|
state->exp = machine.rand();
|
|
|
|
// initialize integer registers to random values
|
|
for (regnum = 0; regnum < ARRAY_LENGTH(state->r); regnum++)
|
|
{
|
|
state->r[regnum].w.h = machine.rand();
|
|
state->r[regnum].w.l = machine.rand();
|
|
}
|
|
|
|
// initialize float registers to random values
|
|
for (regnum = 0; regnum < ARRAY_LENGTH(state->f); regnum++)
|
|
{
|
|
*(uint32_t *)&state->f[regnum].s.h = machine.rand();
|
|
*(uint32_t *)&state->f[regnum].s.l = machine.rand();
|
|
}
|
|
|
|
// initialize map variables to random values
|
|
for (regnum = 0; regnum < MAPVAR_COUNT; regnum++)
|
|
UML_MAPVAR(block, MVAR(regnum), machine.rand());
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
bevalidate_populate_state - populate the
|
|
machine state with the proper values prior
|
|
to executing a test
|
|
-------------------------------------------------*/
|
|
|
|
static int bevalidate_populate_state(drcuml_block *block, drcuml_machine_state *state, const bevalidate_test *test, const parameter *paramlist, parameter *params, uint64_t *parammem)
|
|
{
|
|
const opcode_info *opinfo = opcode_info_table[test->opcode()];
|
|
int numparams = ARRAY_LENGTH(test->param);
|
|
int pnum;
|
|
|
|
// copy flags as-is
|
|
state->flags = test->iflags;
|
|
|
|
// iterate over parameters
|
|
for (pnum = 0; pnum < ARRAY_LENGTH(test->param); pnum++)
|
|
{
|
|
int psize = effective_test_psize(opinfo, pnum, test->size, test->param);
|
|
parameter *curparam = ¶ms[pnum];
|
|
|
|
// start with a copy of the parameter from the list
|
|
*curparam = paramlist[pnum];
|
|
|
|
// switch off the type
|
|
switch (curparam->type)
|
|
{
|
|
// immediate parameters: take the value from the test entry
|
|
case parameter::PTYPE_IMMEDIATE:
|
|
curparam->value = test->param[pnum];
|
|
break;
|
|
|
|
// register parameters: set the register value in the state and set the parameter value to the register index
|
|
case parameter::PTYPE_INT_REGISTER:
|
|
state->r[curparam->value].d = test->param[pnum];
|
|
curparam->value += REG_I0;
|
|
break;
|
|
|
|
// register parameters: set the register value in the state and set the parameter value to the register index
|
|
case parameter::PTYPE_FLOAT_REGISTER:
|
|
state->f[curparam->value].d = test->param[pnum];
|
|
curparam->value += REG_F0;
|
|
break;
|
|
|
|
// memory parameters: set the memory value in the parameter space and set the parameter value to point to it
|
|
case parameter::PTYPE_MEMORY:
|
|
curparam->value = (uintptr_t)¶mmem[pnum];
|
|
if (psize == 4)
|
|
*(uint32_t *)(uintptr_t)curparam->value = test->param[pnum];
|
|
else
|
|
*(uint64_t *)(uintptr_t)curparam->value = test->param[pnum];
|
|
break;
|
|
|
|
// map variables: issue a MAPVAR instruction to set the value and set the parameter value to the mapvar index
|
|
case parameter::PTYPE_MAPVAR:
|
|
UML_MAPVAR(block, MVAR(curparam->value), test->param[pnum]);
|
|
curparam->value += MAPVAR_M0;
|
|
break;
|
|
|
|
// use anything else to count the number of parameters
|
|
default:
|
|
numparams = MIN(numparams, pnum);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// return the total number of parameters
|
|
return numparams;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
bevalidate_verify_state - verify the final
|
|
state after executing a test, and report any
|
|
discrepancies
|
|
-------------------------------------------------*/
|
|
|
|
static int bevalidate_verify_state(drcuml_state *drcuml, const drcuml_machine_state *istate, drcuml_machine_state *state, const bevalidate_test *test, uint32_t flags, const parameter *params, const instruction *testinst, drccodeptr codestart, drccodeptr codeend, uint8_t flagmask)
|
|
{
|
|
const opcode_info *opinfo = opcode_info_table[test->opcode()];
|
|
uint8_t ireg[REG_I_END - REG_I0] = { 0 };
|
|
uint8_t freg[REG_F_END - REG_F0] = { 0 };
|
|
char errorbuf[1024];
|
|
char *errend = errorbuf;
|
|
int pnum, regnum;
|
|
|
|
*errend = 0;
|
|
|
|
// check flags
|
|
if (flags != (test->flags & flagmask))
|
|
{
|
|
errend += sprintf(errend, " Flags ... result:%c%c%c%c%c expected:%c%c%c%c%c\n",
|
|
(flagmask & FLAG_U) ? ((flags & FLAG_U) ? 'U' : '.') : '-',
|
|
(flagmask & FLAG_S) ? ((flags & FLAG_S) ? 'S' : '.') : '-',
|
|
(flagmask & FLAG_Z) ? ((flags & FLAG_Z) ? 'Z' : '.') : '-',
|
|
(flagmask & FLAG_V) ? ((flags & FLAG_V) ? 'V' : '.') : '-',
|
|
(flagmask & FLAG_C) ? ((flags & FLAG_C) ? 'C' : '.') : '-',
|
|
(flagmask & FLAG_U) ? ((test->flags & FLAG_U) ? 'U' : '.') : '-',
|
|
(flagmask & FLAG_S) ? ((test->flags & FLAG_S) ? 'S' : '.') : '-',
|
|
(flagmask & FLAG_Z) ? ((test->flags & FLAG_Z) ? 'Z' : '.') : '-',
|
|
(flagmask & FLAG_V) ? ((test->flags & FLAG_V) ? 'V' : '.') : '-',
|
|
(flagmask & FLAG_C) ? ((test->flags & FLAG_C) ? 'C' : '.') : '-');
|
|
}
|
|
|
|
// check destination parameters
|
|
for (pnum = 0; pnum < ARRAY_LENGTH(test->param); pnum++)
|
|
if (opinfo->param[pnum].output & PIO_OUT)
|
|
{
|
|
int psize = effective_test_psize(opinfo, pnum, test->size, test->param);
|
|
uint64_t mask = u64(0xffffffffffffffff) >> (64 - 8 * psize);
|
|
uint64_t result = 0;
|
|
|
|
// fetch the result from the parameters
|
|
switch (params[pnum].type)
|
|
{
|
|
// integer registers fetch from the state
|
|
case parameter::PTYPE_INT_REGISTER:
|
|
ireg[params[pnum].value - REG_I0] = 1;
|
|
result = state->r[params[pnum].value - REG_I0].d;
|
|
break;
|
|
|
|
// float registers fetch from the state
|
|
case parameter::PTYPE_FLOAT_REGISTER:
|
|
freg[params[pnum].value - REG_I0] = 1;
|
|
result = state->f[params[pnum].value - REG_F0].d;
|
|
break;
|
|
|
|
// memory registers fetch from the memory address
|
|
case parameter::PTYPE_MEMORY:
|
|
if (psize == 4)
|
|
result = *(uint32_t *)(uintptr_t)params[pnum].value;
|
|
else
|
|
result = *(uint64_t *)(uintptr_t)params[pnum].value;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// check against the mask
|
|
if (test->param[pnum] != UNDEFINED_U64 && (result & mask) != (test->param[pnum] & mask))
|
|
{
|
|
if ((uint32_t)mask == mask)
|
|
errend += sprintf(errend, " Parameter %d ... result:%08X expected:%08X\n", pnum,
|
|
(uint32_t)(result & mask), (uint32_t)(test->param[pnum] & mask));
|
|
else
|
|
errend += sprintf(errend, " Parameter %d ... result:%08X%08X expected:%08X%08X\n", pnum,
|
|
(uint32_t)((result & mask) >> 32), (uint32_t)(result & mask),
|
|
(uint32_t)((test->param[pnum] & mask) >> 32), (uint32_t)(test->param[pnum] & mask));
|
|
}
|
|
}
|
|
|
|
// check source integer parameters for unexpected alterations
|
|
for (regnum = 0; regnum < ARRAY_LENGTH(state->r); regnum++)
|
|
if (ireg[regnum] == 0 && istate->r[regnum].d != state->r[regnum].d)
|
|
errend += sprintf(errend, " Register i%d ... result:%08X%08X originally:%08X%08X\n", regnum,
|
|
(uint32_t)(state->r[regnum].d >> 32), (uint32_t)state->r[regnum].d,
|
|
(uint32_t)(istate->r[regnum].d >> 32), (uint32_t)istate->r[regnum].d);
|
|
|
|
// check source float parameters for unexpected alterations
|
|
for (regnum = 0; regnum < ARRAY_LENGTH(state->f); regnum++)
|
|
if (freg[regnum] == 0 && *(uint64_t *)&istate->f[regnum].d != *(uint64_t *)&state->f[regnum].d)
|
|
errend += sprintf(errend, " Register f%d ... result:%08X%08X originally:%08X%08X\n", regnum,
|
|
(uint32_t)(*(uint64_t *)&state->f[regnum].d >> 32), (uint32_t)*(uint64_t *)&state->f[regnum].d,
|
|
(uint32_t)(*(uint64_t *)&istate->f[regnum].d >> 32), (uint32_t)*(uint64_t *)&istate->f[regnum].d);
|
|
|
|
// output the error if we have one
|
|
if (errend != errorbuf)
|
|
{
|
|
// disassemble the test instruction
|
|
std::string disasm = testinst->disasm(drcuml);
|
|
|
|
// output a description of what went wrong
|
|
printf("\n");
|
|
printf("----------------------------------------------\n");
|
|
printf("Backend validation error:\n");
|
|
printf(" %s\n", disasm.c_str());
|
|
printf("\n");
|
|
printf("Errors:\n");
|
|
printf("%s\n", errorbuf);
|
|
fatalerror("Error during validation\n");
|
|
}
|
|
return errend != errorbuf;
|
|
}
|
|
|
|
#endif
|