summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/cpu/drcuml.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/cpu/drcuml.c')
-rw-r--r--trunk/src/emu/cpu/drcuml.c1169
1 files changed, 1169 insertions, 0 deletions
diff --git a/trunk/src/emu/cpu/drcuml.c b/trunk/src/emu/cpu/drcuml.c
new file mode 100644
index 00000000000..b19ff71f555
--- /dev/null
+++ b/trunk/src/emu/cpu/drcuml.c
@@ -0,0 +1,1169 @@
+/***************************************************************************
+
+ drcuml.c
+
+ Universal machine language for dynamic recompiling CPU cores.
+
+****************************************************************************
+
+ Copyright Aaron Giles
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+****************************************************************************
+
+ 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?
+ - FCOPYI, ICOPYF
+ copy raw between float and integer registers
+
+ - 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 "drcbec.h"
+#include "drcbex86.h"
+#include "drcbex64.h"
+
+using namespace uml;
+
+
+
+//**************************************************************************
+// DEBUGGING
+//**************************************************************************
+
+#define VALIDATE_BACKEND (0)
+#define LOG_SIMPLIFICATIONS (0)
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// structure describing back-end validation test
+struct bevalidate_test
+{
+ opcode_t opcode;
+ UINT8 size;
+ UINT8 iflags;
+ UINT8 flags;
+ UINT64 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_state(*(drcuml_machine_state *)cache.alloc_near(sizeof(m_state))),
+ m_accessors((data_accessors *)cache.alloc_near(sizeof(*m_accessors) * ADDRESS_SPACES))
+{
+ // reset the machine state
+ memset(m_accessors, 0, sizeof(*m_accessors) * ADDRESS_SPACES);
+ memset(&m_state, 0, sizeof(m_state));
+
+ // find the spaces and fetch memory accessors
+ device_memory_interface *memory;
+ if (device.interface(memory))
+ for (address_spacenum spacenum = AS_0; spacenum < ARRAY_LENGTH(m_space); spacenum++)
+ {
+ m_space[spacenum] = memory->space(spacenum);
+ if (m_space[spacenum] != NULL)
+ 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, UINT32 flags, int modes, int addrbits, int ignorebits)
+ : m_device(device),
+ m_cache(cache),
+ m_beintf((flags & DRCUML_OPTION_USE_C) ?
+ *static_cast<drcbe_interface *>(auto_alloc(device.machine(), drcbe_c(*this, device, cache, flags, modes, addrbits, ignorebits))) :
+ *static_cast<drcbe_interface *>(auto_alloc(device.machine(), drcbe_native(*this, device, cache, flags, modes, addrbits, ignorebits)))),
+ m_umllog(NULL),
+ m_blocklist(device.machine().respool()),
+ m_symlist(device.machine().respool())
+{
+ // if we're to log, create the logfile
+ if (flags & DRCUML_OPTION_LOG_UML)
+ m_umllog = fopen("drcuml.asm", "w");
+}
+
+
+//-------------------------------------------------
+// ~drcuml_state - destructor
+//-------------------------------------------------
+
+drcuml_state::~drcuml_state()
+{
+ // free the back-end
+ auto_free(m_device.machine(), &m_beintf);
+
+ // close any files
+ if (m_umllog != NULL)
+ fclose(m_umllog);
+}
+
+
+//-------------------------------------------------
+// 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 (code_handle *handle = m_handlelist.first(); handle != NULL; handle = handle->next())
+ *handle->m_code = NULL;
+
+ // call the backend to reset
+ m_beintf.reset();
+
+ // do a one-time validation if requested
+/* if (VALIDATE_BACKEND)
+ {
+ static bool validated = false;
+ if (!validated)
+ {
+ validated = true;
+ validate_backend(this);
+ }
+ }*/
+ }
+ catch (drcuml_block::abort_compilation &)
+ {
+ fatalerror("Out of cache space in drcuml_state::reset");
+ }
+}
+
+
+//-------------------------------------------------
+// begin_block - begin a new code block
+//-------------------------------------------------
+
+drcuml_block *drcuml_state::begin_block(UINT32 maxinst)
+{
+ // find an inactive block that matches our qualifications
+ drcuml_block *bestblock = NULL;
+ for (drcuml_block *block = m_blocklist.first(); block != NULL; block = block->next())
+ if (!block->inuse() && block->maxinst() >= maxinst && (bestblock == NULL || block->maxinst() < bestblock->maxinst()))
+ bestblock = block;
+
+ // if we failed to find one, allocate a new one
+ if (bestblock == NULL)
+ bestblock = &m_blocklist.append(*auto_alloc(m_device.machine(), drcuml_block(*this, maxinst * 3/2)));
+
+ // start the block
+ bestblock->begin();
+ return bestblock;
+}
+
+
+//-------------------------------------------------
+// handle_alloc - allocate a new handle
+//-------------------------------------------------
+
+code_handle *drcuml_state::handle_alloc(const char *name)
+{
+ // allocate the handle, add it to our list, and return it
+ return &m_handlelist.append(*auto_alloc(m_device.machine(), code_handle(*this, name)));
+}
+
+
+//-------------------------------------------------
+// symbol_add - add a symbol to the internal
+// symbol table
+//-------------------------------------------------
+
+void drcuml_state::symbol_add(void *base, UINT32 length, const char *name)
+{
+ m_symlist.append(*auto_alloc(m_device.machine(), symbol(base, length, name)));
+}
+
+
+//-------------------------------------------------
+// symbol_find - look up a symbol from the
+// internal symbol table or return NULL if not
+// found
+//-------------------------------------------------
+
+const char *drcuml_state::symbol_find(void *base, UINT32 *offset)
+{
+ drccodeptr search = drccodeptr(base);
+
+ // simple linear search
+ for (symbol *cursym = m_symlist.first(); cursym != NULL; cursym = cursym->next())
+ if (search >= cursym->m_base && search < cursym->m_base + cursym->m_length)
+ {
+ // if no offset pointer, only match perfectly
+ if (offset == NULL && search != cursym->m_base)
+ continue;
+
+ // return the offset and name
+ if (offset != NULL)
+ *offset = search - cursym->m_base;
+ return cursym->m_name;
+ }
+
+ // not found; return NULL
+ return NULL;
+}
+
+
+//-------------------------------------------------
+// log_printf - directly printf to the UML log
+// if generated
+//-------------------------------------------------
+
+void drcuml_state::log_printf(const char *format, ...)
+{
+ // if we have a file, print to it
+ if (m_umllog != NULL)
+ {
+ va_list va;
+
+ // do the printf
+ va_start(va, format);
+ vfprintf(m_umllog, format, va);
+ va_end(va);
+ }
+}
+
+
+
+//**************************************************************************
+// DRCUML BLOCK
+//**************************************************************************
+
+//-------------------------------------------------
+// drcuml_block - constructor
+//-------------------------------------------------
+
+drcuml_block::drcuml_block(drcuml_state &drcuml, UINT32 maxinst)
+ : m_drcuml(drcuml),
+ m_next(NULL),
+ m_nextinst(0),
+ m_maxinst(maxinst * 3/2),
+ m_inst(auto_alloc_array(drcuml.device().machine(), instruction, m_maxinst)),
+ m_inuse(false)
+{
+}
+
+
+//-------------------------------------------------
+// ~drcuml_block - destructor
+//-------------------------------------------------
+
+drcuml_block::~drcuml_block()
+{
+ // free the instruction list
+ auto_free(m_drcuml.device().machine(), m_inst);
+}
+
+
+//-------------------------------------------------
+// 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, 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
+ instruction &curinst = m_inst[m_nextinst++];
+ if (m_nextinst > m_maxinst)
+ fatalerror("Overran maxinst in drcuml_block_append");
+
+ return curinst;
+}
+
+
+//-------------------------------------------------
+// comment - attach a comment to the current
+// output location in the specified block
+//-------------------------------------------------
+
+void drcuml_block::append_comment(const char *format, ...)
+{
+ // do the printf
+ astring temp;
+ va_list va;
+ va_start(va, format);
+ temp.vprintf(format, va);
+ va_end(va);
+
+ // allocate space in the cache to hold the comment
+ char *comment = (char *)m_drcuml.cache().alloc_temporary(temp.len() + 1);
+ if (comment == NULL)
+ return;
+ strcpy(comment, temp);
+
+ // add an instruction with a pointer
+ append().comment(comment);
+}
+
+
+//-------------------------------------------------
+// optimize - apply various optimizations to a
+// block of code
+//-------------------------------------------------
+
+void drcuml_block::optimize()
+{
+ UINT32 mapvar[MAPVAR_COUNT] = { 0 };
+
+ // iterate over instructions
+ for (int instnum = 0; instnum < m_nextinst; instnum++)
+ {
+ instruction &inst = m_inst[instnum];
+
+ // first compute what flags we need
+ UINT8 accumflags = 0;
+ UINT8 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
+ const instruction &scan = m_inst[scannum];
+ accumflags |= scan.input_flags();
+
+ // if the scanahead instruction is unconditional, assume his flags are modified
+ if (scan.condition() == COND_ALWAYS)
+ remainingflags &= ~scan.modified_flags();
+ }
+ inst.set_flags(accumflags);
+
+ // track mapvars
+ if (inst.opcode() == OP_MAPVAR)
+ mapvar[inst.param(0).mapvar() - MAPVAR_M0] = inst.param(1).immediate();
+
+ // convert all mapvar parameters to immediates
+ else if (inst.opcode() != 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() - 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()
+{
+ astring comment;
+ astring dasm;
+
+ // iterate over instructions and output
+ int firstcomment = -1;
+ for (int instnum = 0; instnum < m_nextinst; instnum++)
+ {
+ const instruction &inst = m_inst[instnum];
+ bool flushcomments = false;
+
+ // remember comments and mapvars for later
+ if (inst.opcode() == OP_COMMENT || inst.opcode() == OP_MAPVAR)
+ {
+ if (firstcomment == -1)
+ firstcomment = instnum;
+ }
+
+ // print labels, handles, and hashes left justified
+ else if (inst.opcode() == OP_LABEL)
+ m_drcuml.log_printf("$%X:\n", UINT32(inst.param(0).label()));
+ else if (inst.opcode() == OP_HANDLE)
+ m_drcuml.log_printf("%s:\n", inst.param(0).handle().string());
+ else if (inst.opcode() == OP_HASH)
+ m_drcuml.log_printf("(%X,%X):\n", UINT32(inst.param(0).immediate()), UINT32(inst.param(1).immediate()));
+
+ // indent everything else with a tab
+ else
+ {
+ m_inst[instnum].disasm(dasm, &m_drcuml);
+
+ // include the first accumulated comment with this line
+ if (firstcomment != -1)
+ {
+ m_drcuml.log_printf("\t%-50.50s; %s\n", dasm.cstr(), get_comment_text(m_inst[firstcomment], comment));
+ firstcomment++;
+ flushcomments = TRUE;
+ }
+ else
+ m_drcuml.log_printf("\t%s\n", dasm.cstr());
+ }
+
+ // flush any comments pending
+ if (firstcomment != -1 && (flushcomments || instnum == m_nextinst - 1))
+ {
+ while (firstcomment <= instnum)
+ {
+ const char *text = get_comment_text(m_inst[firstcomment++], comment);
+ if (text != NULL)
+ 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
+//-------------------------------------------------
+
+const char *drcuml_block::get_comment_text(const instruction &inst, astring &comment)
+{
+ // comments return their strings
+ if (inst.opcode() == OP_COMMENT)
+ return comment.cpy(inst.param(0).string());
+
+ // mapvars comment about their values
+ else if (inst.opcode() == OP_MAPVAR)
+ return comment.format("m%d = $%X", (int)inst.param(0).mapvar() - MAPVAR_M0, (UINT32)inst.param(1).immediate());
+
+ // everything else is NULL
+ return NULL;
+}
+
+
+
+#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 effective_test_psize(const opcode_info &opinfo, int pnum, int instsize, const UINT64 *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)
+{
+ 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!");
+}
+
+
+/*-------------------------------------------------
+ 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, 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++)
+ {
+ int 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, code_handle **handles, const bevalidate_test *test, parameter *paramlist)
+{
+ const opcode_info *opinfo = opcode_info_table[test->opcode()];
+ UINT8 flagmask = opinfo->outflags;
+ UINT8 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, code_handle **handles, const bevalidate_test *test, const parameter *paramlist, UINT8 flagmask)
+{
+ parameter params[ARRAY_LENGTH(test->param)];
+ drcuml_machine_state istate, fstate;
+ instruction testinst;
+ drcuml_block *block;
+ UINT64 *parammem;
+ int numparams;
+
+ // allocate memory for parameters
+ parammem = (UINT64 *)drcuml->cache->alloc_near(sizeof(UINT64) * (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(&parammem[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 *)&parammem[ARRAY_LENGTH(test->param)], params, &testinst, handles[1]->code, handles[2]->code, flagmask);
+
+ // free memory
+ drcuml->cache->dealloc(parammem, sizeof(UINT64) * (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 *)&state->f[regnum].s.h = machine.rand();
+ *(UINT32 *)&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 *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 = &params[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 = (FPTR)&parammem[pnum];
+ if (psize == 4)
+ *(UINT32 *)(FPTR)curparam->value = test->param[pnum];
+ else
+ *(UINT64 *)(FPTR)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 flags, const parameter *params, const instruction *testinst, drccodeptr codestart, drccodeptr codeend, UINT8 flagmask)
+{
+ const opcode_info *opinfo = opcode_info_table[test->opcode()];
+ UINT8 ireg[REG_I_END - REG_I0] = { 0 };
+ UINT8 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 mask = U64(0xffffffffffffffff) >> (64 - 8 * psize);
+ UINT64 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 *)(FPTR)params[pnum].value;
+ else
+ result = *(UINT64 *)(FPTR)params[pnum].value;
+ break;
+
+ default:
+ break;
+ }
+
+ // check against the mask
+ if (test->param[pnum] != UNDEFINED_U64 && (result & mask) != (test->param[pnum] & mask))
+ {
+ if ((UINT32)mask == mask)
+ errend += sprintf(errend, " Parameter %d ... result:%08X expected:%08X\n", pnum,
+ (UINT32)(result & mask), (UINT32)(test->param[pnum] & mask));
+ else
+ errend += sprintf(errend, " Parameter %d ... result:%08X%08X expected:%08X%08X\n", pnum,
+ (UINT32)((result & mask) >> 32), (UINT32)(result & mask),
+ (UINT32)((test->param[pnum] & mask) >> 32), (UINT32)(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)(state->r[regnum].d >> 32), (UINT32)state->r[regnum].d,
+ (UINT32)(istate->r[regnum].d >> 32), (UINT32)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 *)&istate->f[regnum].d != *(UINT64 *)&state->f[regnum].d)
+ errend += sprintf(errend, " Register f%d ... result:%08X%08X originally:%08X%08X\n", regnum,
+ (UINT32)(*(UINT64 *)&state->f[regnum].d >> 32), (UINT32)*(UINT64 *)&state->f[regnum].d,
+ (UINT32)(*(UINT64 *)&istate->f[regnum].d >> 32), (UINT32)*(UINT64 *)&istate->f[regnum].d);
+
+ // output the error if we have one
+ if (errend != errorbuf)
+ {
+ char disasm[256];
+
+ // disassemble the test instruction
+ testinst->disasm(disasm, drcuml);
+
+ // output a description of what went wrong
+ printf("\n");
+ printf("----------------------------------------------\n");
+ printf("Backend validation error:\n");
+ printf(" %s\n", disasm);
+ printf("\n");
+ printf("Errors:\n");
+ printf("%s\n", errorbuf);
+ fatalerror("Error during validation");
+ }
+ return errend != errorbuf;
+}
+
+#endif