1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4 
5     drcuml.c
6 
7     Universal machine language for dynamic recompiling CPU cores.
8 
9 ****************************************************************************
10 
11     Future improvements/changes:
12 
13     * UML optimizer:
14         - constant folding
15 
16     * Write a back-end validator:
17         - checks all combinations of memory/register/immediate on all params
18         - checks behavior of all opcodes
19 
20     * Extend registers to 16? Depends on if PPC can use them
21 
22     * Support for FPU exceptions
23 
24     * New instructions?
25         - VALID opcode_desc,handle,param
26             checksum/compare code referenced by opcode_desc; if not
27             matching, generate exception with handle,param
28 
29         - RECALL handle
30             change code at caller to call handle in the future
31 
32 ***************************************************************************/
33 
34 #include "emu.h"
35 #include "drcuml.h"
36 
37 #include "emuopts.h"
38 #include "drcbec.h"
39 #ifdef NATIVE_DRC
40 #include "drcbex86.h"
41 #include "drcbex64.h"
42 #endif
43 
44 #include <fstream>
45 
46 
47 
48 //**************************************************************************
49 //  DEBUGGING
50 //**************************************************************************
51 
52 #define VALIDATE_BACKEND        (0)
53 #define LOG_SIMPLIFICATIONS     (0)
54 
55 
56 
57 //**************************************************************************
58 //  TYPE DEFINITIONS
59 //**************************************************************************
60 
61 // determine the type of the native DRC, falling back to C
62 #ifndef NATIVE_DRC
63 typedef drcbe_c drcbe_native;
64 #else
65 typedef NATIVE_DRC drcbe_native;
66 #endif
67 
68 
69 // structure describing back-end validation test
70 struct bevalidate_test
71 {
72 	uml::opcode_t   opcode;
73 	u8              size;
74 	u8              iflags;
75 	u8              flags;
76 	u64             param[4];
77 };
78 
79 
80 
81 //**************************************************************************
82 //  DRC BACKEND INTERFACE
83 //**************************************************************************
84 
85 //-------------------------------------------------
86 //  drcbe_interface - constructor
87 //-------------------------------------------------
88 
drcbe_interface(drcuml_state & drcuml,drc_cache & cache,device_t & device)89 drcbe_interface::drcbe_interface(drcuml_state &drcuml, drc_cache &cache, device_t &device)
90 	: m_drcuml(drcuml)
91 	, m_cache(cache)
92 	, m_device(device)
93 	, m_space()
94 	, m_state(*reinterpret_cast<drcuml_machine_state *>(cache.alloc_near(sizeof(m_state))))
95 	, m_accessors(nullptr)
96 {
97 	// reset the machine state
98 	memset(&m_state, 0, sizeof(m_state));
99 
100 	// find the spaces and fetch memory accessors
101 	device_memory_interface *memory;
102 	if (device.interface(memory))
103 	{
104 		int const count = memory->max_space_count();
105 		m_accessors = reinterpret_cast<data_accessors *>(cache.alloc_near(sizeof(*m_accessors) * count));
106 		memset(m_accessors, 0, sizeof(*m_accessors) * count);
107 		m_space.resize(count, nullptr);
108 
109 		for (int spacenum = 0; spacenum < count; ++spacenum)
110 		{
111 			if (memory->has_space(spacenum))
112 			{
113 				m_space[spacenum] = &memory->space(spacenum);
114 				m_space[spacenum]->accessors(m_accessors[spacenum]);
115 			}
116 		}
117 	}
118 }
119 
120 
121 //-------------------------------------------------
122 //  ~drcbe_interface - destructor
123 //-------------------------------------------------
124 
~drcbe_interface()125 drcbe_interface::~drcbe_interface()
126 {
127 }
128 
129 
130 
131 //**************************************************************************
132 //  DRCUML STATE
133 //**************************************************************************
134 
135 //-------------------------------------------------
136 //  drcuml_state - constructor
137 //-------------------------------------------------
138 
drcuml_state(device_t & device,drc_cache & cache,u32 flags,int modes,int addrbits,int ignorebits)139 drcuml_state::drcuml_state(device_t &device, drc_cache &cache, u32 flags, int modes, int addrbits, int ignorebits)
140 	: m_device(device)
141 	, m_cache(cache)
142 	, m_beintf(device.machine().options().drc_use_c()
143 			? std::unique_ptr<drcbe_interface>{ new drcbe_c(*this, device, cache, flags, modes, addrbits, ignorebits) }
144 			: std::unique_ptr<drcbe_interface>{ new drcbe_native(*this, device, cache, flags, modes, addrbits, ignorebits) })
145 	, m_umllog(device.machine().options().drc_log_uml()
146 			? new std::ofstream(util::string_format("drcuml_%s.asm", device.shortname()))
147 			: nullptr)
148 	, m_blocklist()
149 	, m_handlelist()
150 	, m_symlist()
151 {
152 }
153 
154 
155 //-------------------------------------------------
156 //  ~drcuml_state - destructor
157 //-------------------------------------------------
158 
~drcuml_state()159 drcuml_state::~drcuml_state()
160 {
161 }
162 
163 
164 //-------------------------------------------------
165 //  reset - reset the state completely, flushing
166 //  the cache and all information
167 //-------------------------------------------------
168 
reset()169 void drcuml_state::reset()
170 {
171 	// if we error here, we are screwed
172 	try
173 	{
174 		// flush the cache
175 		m_cache.flush();
176 
177 		// reset all handle code pointers
178 		for (uml::code_handle &handle : m_handlelist)
179 			*handle.codeptr_addr() = nullptr;
180 
181 		// call the backend to reset
182 		m_beintf->reset();
183 
184 		// do a one-time validation if requested
185 #if 0
186 		if (VALIDATE_BACKEND)
187 		{
188 			static bool validated = false;
189 			if (!validated)
190 			{
191 				validated = true;
192 				validate_backend(this);
193 			}
194 		}
195 #endif
196 	}
197 	catch (drcuml_block::abort_compilation &)
198 	{
199 		fatalerror("Out of cache space in drcuml_state::reset\n");
200 	}
201 }
202 
203 
204 //-------------------------------------------------
205 //  begin_block - begin a new code block
206 //-------------------------------------------------
207 
begin_block(uint32_t maxinst)208 drcuml_block &drcuml_state::begin_block(uint32_t maxinst)
209 {
210 	// find an inactive block that matches our qualifications
211 	drcuml_block *bestblock(nullptr);
212 	for (drcuml_block &block : m_blocklist)
213 	{
214 		if (!block.inuse() && (block.maxinst() >= maxinst) && (!bestblock || (block.maxinst() < bestblock->maxinst())))
215 			bestblock = &block;
216 	}
217 
218 	// if we failed to find one, allocate a new one
219 	if (!bestblock)
220 		bestblock = &*m_blocklist.emplace(m_blocklist.end(), *this, maxinst * 3 / 2);
221 
222 	// start the block
223 	bestblock->begin();
224 	return *bestblock;
225 }
226 
227 
228 //-------------------------------------------------
229 //  handle_alloc - allocate a new handle
230 //-------------------------------------------------
231 
handle_alloc(char const * name)232 uml::code_handle *drcuml_state::handle_alloc(char const *name)
233 {
234 	// allocate the handle, add it to our list, and return it
235 	return &*m_handlelist.emplace(m_handlelist.end(), *this, name);
236 }
237 
238 
239 //-------------------------------------------------
240 //  symbol_add - add a symbol to the internal
241 //  symbol table
242 //-------------------------------------------------
243 
symbol_add(void * base,u32 length,char const * name)244 void drcuml_state::symbol_add(void *base, u32 length, char const *name)
245 {
246 	m_symlist.emplace_back(base, length, name);
247 }
248 
249 
250 //-------------------------------------------------
251 //  symbol_find - look up a symbol from the
252 //  internal symbol table or return nullptr if not
253 //  found
254 //-------------------------------------------------
255 
symbol_find(void * base,u32 * offset)256 const char *drcuml_state::symbol_find(void *base, u32 *offset)
257 {
258 	drccodeptr const search(reinterpret_cast<drccodeptr>(base));
259 
260 	// simple linear search
261 	for (symbol const &cursym : m_symlist)
262 	{
263 		// if no offset pointer, only match perfectly
264 		if (cursym.includes(search) && (offset || (cursym.base() == search)))
265 		{
266 			// return the offset and name
267 			if (offset)
268 				*offset = search - cursym.base();
269 			return cursym.name().c_str();
270 		}
271 	}
272 
273 	// not found; return nullptr
274 	return nullptr;
275 }
276 
277 
278 //-------------------------------------------------
279 //  log_vprintf - directly printf to the UML log
280 //  if generated
281 //-------------------------------------------------
282 
log_vprintf(util::format_argument_pack<std::ostream> const & args)283 void drcuml_state::log_vprintf(util::format_argument_pack<std::ostream> const &args)
284 {
285 	// if we have a file, print to it
286 	if (m_umllog)
287 	{
288 		util::stream_format(*m_umllog, args);
289 		m_umllog->flush();
290 	}
291 }
292 
293 
294 
295 //**************************************************************************
296 //  DRCUML BLOCK
297 //**************************************************************************
298 
299 //-------------------------------------------------
300 //  drcuml_block - constructor
301 //-------------------------------------------------
302 
drcuml_block(drcuml_state & drcuml,u32 maxinst)303 drcuml_block::drcuml_block(drcuml_state &drcuml, u32 maxinst)
304 	: m_drcuml(drcuml)
305 	, m_nextinst(0)
306 	, m_maxinst(maxinst * 3/2)
307 	, m_inst(m_maxinst)
308 	, m_inuse(false)
309 {
310 }
311 
312 
313 //-------------------------------------------------
314 //  ~drcuml_block - destructor
315 //-------------------------------------------------
316 
~drcuml_block()317 drcuml_block::~drcuml_block()
318 {
319 }
320 
321 
322 //-------------------------------------------------
323 //  begin - begin code generation
324 //-------------------------------------------------
325 
begin()326 void drcuml_block::begin()
327 {
328 	// set up the block information and return it
329 	m_inuse = true;
330 	m_nextinst = 0;
331 }
332 
333 
334 //-------------------------------------------------
335 //  end - complete a code block and commit it to
336 //  the cache via the back-end
337 //-------------------------------------------------
338 
end()339 void drcuml_block::end()
340 {
341 	assert(m_inuse);
342 
343 	// optimize the resulting code first
344 	optimize();
345 
346 	// if we have a logfile, generate a disassembly of the block
347 	if (m_drcuml.logging())
348 		disassemble();
349 
350 	// generate the code via the back-end
351 	m_drcuml.generate(*this, &m_inst[0], m_nextinst);
352 
353 	// block is no longer in use
354 	m_inuse = false;
355 }
356 
357 
358 //-------------------------------------------------
359 //  abort - abort a code block in progress
360 //-------------------------------------------------
361 
abort()362 void drcuml_block::abort()
363 {
364 	assert(m_inuse);
365 
366 	// block is no longer in use
367 	m_inuse = false;
368 
369 	// unwind
370 	throw abort_compilation();
371 }
372 
373 
374 //-------------------------------------------------
375 //  append - append an opcode to the block
376 //-------------------------------------------------
377 
append()378 uml::instruction &drcuml_block::append()
379 {
380 	// get a pointer to the next instruction
381 	uml::instruction &curinst(m_inst[m_nextinst++]);
382 	if (m_nextinst > m_maxinst)
383 		fatalerror("Overran maxinst in drcuml_block_append\n");
384 
385 	return curinst;
386 }
387 
388 
389 //-------------------------------------------------
390 //  optimize - apply various optimizations to a
391 //  block of code
392 //-------------------------------------------------
393 
optimize()394 void drcuml_block::optimize()
395 {
396 	u32 mapvar[uml::MAPVAR_COUNT] = { 0 };
397 
398 	// iterate over instructions
399 	for (int instnum = 0; instnum < m_nextinst; instnum++)
400 	{
401 		uml::instruction &inst(m_inst[instnum]);
402 
403 		// first compute what flags we need
404 		u8 accumflags(0);
405 		u8 remainingflags(inst.output_flags());
406 
407 		// scan ahead until we run out of possible remaining flags
408 		for (int scannum = instnum + 1; remainingflags != 0 && scannum < m_nextinst; scannum++)
409 		{
410 			// any input flags are required
411 			uml::instruction const &scan(m_inst[scannum]);
412 			accumflags |= scan.input_flags();
413 
414 			// if the scanahead instruction is unconditional, assume his flags are modified
415 			if (scan.condition() == uml::COND_ALWAYS)
416 				remainingflags &= ~scan.modified_flags();
417 		}
418 		inst.set_flags(accumflags);
419 
420 		// track mapvars
421 		if (inst.opcode() == uml::OP_MAPVAR)
422 			mapvar[inst.param(0).mapvar() - uml::MAPVAR_M0] = inst.param(1).immediate();
423 
424 		// convert all mapvar parameters to immediates
425 		else if (inst.opcode() != uml::OP_RECOVER)
426 			for (int pnum = 0; pnum < inst.numparams(); pnum++)
427 				if (inst.param(pnum).is_mapvar())
428 					inst.set_mapvar(pnum, mapvar[inst.param(pnum).mapvar() - uml::MAPVAR_M0]);
429 
430 		// now that flags are correct, simplify the instruction
431 		inst.simplify();
432 	}
433 }
434 
435 
436 //-------------------------------------------------
437 //  disassemble - disassemble a block of
438 //  instructions to the log
439 //-------------------------------------------------
440 
disassemble()441 void drcuml_block::disassemble()
442 {
443 	std::string comment;
444 
445 	// iterate over instructions and output
446 	int firstcomment(-1);
447 	for (int instnum = 0; instnum < m_nextinst; instnum++)
448 	{
449 		uml::instruction const &inst(m_inst[instnum]);
450 		bool flushcomments(false);
451 
452 		// remember comments and mapvars for later
453 		if (inst.opcode() == uml::OP_COMMENT || inst.opcode() == uml::OP_MAPVAR)
454 		{
455 			if (firstcomment == -1)
456 				firstcomment = instnum;
457 		}
458 
459 		// print labels, handles, and hashes left justified
460 		else if (inst.opcode() == uml::OP_LABEL)
461 			m_drcuml.log_printf("$%X:\n", u32(inst.param(0).label()));
462 		else if (inst.opcode() == uml::OP_HANDLE)
463 			m_drcuml.log_printf("%s:\n", inst.param(0).handle().string());
464 		else if (inst.opcode() == uml::OP_HASH)
465 			m_drcuml.log_printf("(%X,%X):\n", u32(inst.param(0).immediate()), u32(inst.param(1).immediate()));
466 
467 		// indent everything else with a tab
468 		else
469 		{
470 			std::string const dasm(m_inst[instnum].disasm(&m_drcuml));
471 
472 			// include the first accumulated comment with this line
473 			if (firstcomment != -1)
474 			{
475 				m_drcuml.log_printf("\t%-50.50s; %s\n", dasm, get_comment_text(m_inst[firstcomment], comment));
476 				firstcomment++;
477 				flushcomments = true;
478 			}
479 			else
480 			{
481 				m_drcuml.log_printf("\t%s\n", dasm);
482 			}
483 		}
484 
485 		// flush any comments pending
486 		if (firstcomment != -1 && (flushcomments || instnum == m_nextinst - 1))
487 		{
488 			while (firstcomment <= instnum)
489 			{
490 				char const *const text(get_comment_text(m_inst[firstcomment++], comment));
491 				if (text)
492 					m_drcuml.log_printf("\t%50s; %s\n", "", text);
493 			}
494 			firstcomment = -1;
495 		}
496 	}
497 	m_drcuml.log_printf("\n\n");
498 	m_drcuml.log_flush();
499 }
500 
501 
502 //-------------------------------------------------
503 //  get_comment_text - determine the text
504 //  associated with a comment or mapvar
505 //-------------------------------------------------
506 
get_comment_text(uml::instruction const & inst,std::string & comment)507 char const *drcuml_block::get_comment_text(uml::instruction const &inst, std::string &comment)
508 {
509 	if (inst.opcode() == uml::OP_COMMENT)
510 	{
511 		// comments return their strings
512 		return comment.assign(inst.param(0).string()).c_str();
513 	}
514 	else if (inst.opcode() == uml::OP_MAPVAR)
515 	{
516 		// mapvars comment about their values
517 		comment = string_format("m%d = $%X", int(inst.param(0).mapvar() - uml::MAPVAR_M0), u32(inst.param(1).immediate()));
518 		return comment.c_str();
519 	}
520 	else
521 	{
522 		// everything else is nullptr
523 		return nullptr;
524 	}
525 }
526 
527 
528 
529 #if 0
530 
531 /***************************************************************************
532     BACK-END VALIDATION
533 ***************************************************************************/
534 
535 //-------------------------------------------------
536 //  effective_test_psize - return the effective
537 //  parameter size based on the size and fixed
538 //  array of parameter values
539 //-------------------------------------------------
540 
541 inline uint8_t effective_test_psize(const opcode_info &opinfo, int pnum, int instsize, const uint64_t *params)
542 {
543 	switch (opinfo.param[pnum].size)
544 	{
545 		case PSIZE_4:   return 4;
546 		case PSIZE_8:   return 8;
547 		case PSIZE_OP:  return instsize;
548 		case PSIZE_P1:  return 1 << (params[0] & 3);
549 		case PSIZE_P2:  return 1 << (params[1] & 3);
550 		case PSIZE_P3:  return 1 << (params[2] & 3);
551 		case PSIZE_P4:  return 1 << (params[3] & 3);
552 	}
553 	return instsize;
554 }
555 
556 #define TEST_ENTRY_2(op, size, p1, p2, flags) { OP_##op, size, 0, flags, { u64(p1), u64(p2) } },
557 #define TEST_ENTRY_2F(op, size, p1, p2, iflags, flags) { OP_##op, size, iflags, flags, { u64(p1), u64(p2) } },
558 #define TEST_ENTRY_3(op, size, p1, p2, p3, flags) { OP_##op, size, 0, flags, { u64(p1), u64(p2), u64(p3) } },
559 #define TEST_ENTRY_3F(op, size, p1, p2, p3, iflags, flags) { OP_##op, size, iflags, flags, { u64(p1), u64(p2), u64(p3) } },
560 #define TEST_ENTRY_4(op, size, p1, p2, p3, p4, flags) { OP_##op, size, 0, flags, { u64(p1), u64(p2), u64(p3), u64(p4) } },
561 #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) } },
562 
563 static const bevalidate_test bevalidate_test_list[] =
564 {
565 	TEST_ENTRY_3(ADD, 4, 0x7fffffff, 0x12345678, 0x6dcba987, 0)
566 	TEST_ENTRY_3(ADD, 4, 0x80000000, 0x12345678, 0x6dcba988, FLAG_V | FLAG_S)
567 	TEST_ENTRY_3(ADD, 4, 0xffffffff, 0x92345678, 0x6dcba987, FLAG_S)
568 	TEST_ENTRY_3(ADD, 4, 0x00000000, 0x92345678, 0x6dcba988, FLAG_C | FLAG_Z)
569 
570 	TEST_ENTRY_3(ADD, 8, 0x7fffffffffffffff, 0x0123456789abcdef, 0x7edcba9876543210, 0)
571 	TEST_ENTRY_3(ADD, 8, 0x8000000000000000, 0x0123456789abcdef, 0x7edcba9876543211, FLAG_V | FLAG_S)
572 	TEST_ENTRY_3(ADD, 8, 0xffffffffffffffff, 0x8123456789abcdef, 0x7edcba9876543210, FLAG_S)
573 	TEST_ENTRY_3(ADD, 8, 0x0000000000000000, 0x8123456789abcdef, 0x7edcba9876543211, FLAG_C | FLAG_Z)
574 
575 	TEST_ENTRY_3F(ADDC, 4, 0x7fffffff, 0x12345678, 0x6dcba987, 0,       0)
576 	TEST_ENTRY_3F(ADDC, 4, 0x7fffffff, 0x12345678, 0x6dcba986, FLAG_C, 0)
577 	TEST_ENTRY_3F(ADDC, 4, 0x80000000, 0x12345678, 0x6dcba988, 0,             FLAG_V | FLAG_S)
578 	TEST_ENTRY_3F(ADDC, 4, 0x80000000, 0x12345678, 0x6dcba987, FLAG_C, FLAG_V | FLAG_S)
579 	TEST_ENTRY_3F(ADDC, 4, 0xffffffff, 0x92345678, 0x6dcba987, 0,             FLAG_S)
580 	TEST_ENTRY_3F(ADDC, 4, 0xffffffff, 0x92345678, 0x6dcba986, FLAG_C, FLAG_S)
581 	TEST_ENTRY_3F(ADDC, 4, 0x00000000, 0x92345678, 0x6dcba988, 0,             FLAG_C | FLAG_Z)
582 	TEST_ENTRY_3F(ADDC, 4, 0x00000000, 0x92345678, 0x6dcba987, FLAG_C, FLAG_C | FLAG_Z)
583 	TEST_ENTRY_3F(ADDC, 4, 0x12345678, 0x12345678, 0xffffffff, FLAG_C, FLAG_C)
584 
585 	TEST_ENTRY_3F(ADDC, 8, 0x7fffffffffffffff, 0x0123456789abcdef, 0x7edcba9876543210, 0,             0)
586 	TEST_ENTRY_3F(ADDC, 8, 0x7fffffffffffffff, 0x0123456789abcdef, 0x7edcba987654320f, FLAG_C, 0)
587 	TEST_ENTRY_3F(ADDC, 8, 0x8000000000000000, 0x0123456789abcdef, 0x7edcba9876543211, 0,             FLAG_V | FLAG_S)
588 	TEST_ENTRY_3F(ADDC, 8, 0x8000000000000000, 0x0123456789abcdef, 0x7edcba9876543210, FLAG_C, FLAG_V | FLAG_S)
589 	TEST_ENTRY_3F(ADDC, 8, 0xffffffffffffffff, 0x8123456789abcdef, 0x7edcba9876543210, 0,             FLAG_S)
590 	TEST_ENTRY_3F(ADDC, 8, 0xffffffffffffffff, 0x8123456789abcdef, 0x7edcba987654320f, FLAG_C, FLAG_S)
591 	TEST_ENTRY_3F(ADDC, 8, 0x0000000000000000, 0x8123456789abcdef, 0x7edcba9876543211, 0,             FLAG_C | FLAG_Z)
592 	TEST_ENTRY_3F(ADDC, 8, 0x0000000000000000, 0x8123456789abcdef, 0x7edcba9876543210, FLAG_C, FLAG_C | FLAG_Z)
593 	TEST_ENTRY_3F(ADDC, 8, 0x123456789abcdef0, 0x123456789abcdef0, 0xffffffffffffffff, FLAG_C, FLAG_C)
594 
595 	TEST_ENTRY_3(SUB, 4, 0x12345678, 0x7fffffff, 0x6dcba987, 0)
596 	TEST_ENTRY_3(SUB, 4, 0x12345678, 0x80000000, 0x6dcba988, FLAG_V)
597 	TEST_ENTRY_3(SUB, 4, 0x92345678, 0xffffffff, 0x6dcba987, FLAG_S)
598 	TEST_ENTRY_3(SUB, 4, 0x92345678, 0x00000000, 0x6dcba988, FLAG_C | FLAG_S)
599 	TEST_ENTRY_3(SUB, 4, 0x00000000, 0x12345678, 0x12345678, FLAG_Z)
600 
601 	TEST_ENTRY_3(SUB, 8, 0x0123456789abcdef, 0x7fffffffffffffff, 0x7edcba9876543210, 0)
602 	TEST_ENTRY_3(SUB, 8, 0x0123456789abcdef, 0x8000000000000000, 0x7edcba9876543211, FLAG_V)
603 	TEST_ENTRY_3(SUB, 8, 0x8123456789abcdef, 0xffffffffffffffff, 0x7edcba9876543210, FLAG_S)
604 	TEST_ENTRY_3(SUB, 8, 0x8123456789abcdef, 0x0000000000000000, 0x7edcba9876543211, FLAG_C | FLAG_S)
605 	TEST_ENTRY_3(SUB, 8, 0x0000000000000000, 0x0123456789abcdef, 0x0123456789abcdef, FLAG_Z)
606 
607 	TEST_ENTRY_3F(SUBB, 4, 0x12345678, 0x7fffffff, 0x6dcba987, 0,             0)
608 	TEST_ENTRY_3F(SUBB, 4, 0x12345678, 0x7fffffff, 0x6dcba986, FLAG_C, 0)
609 	TEST_ENTRY_3F(SUBB, 4, 0x12345678, 0x80000000, 0x6dcba988, 0,             FLAG_V)
610 	TEST_ENTRY_3F(SUBB, 4, 0x12345678, 0x80000000, 0x6dcba987, FLAG_C, FLAG_V)
611 	TEST_ENTRY_3F(SUBB, 4, 0x92345678, 0xffffffff, 0x6dcba987, 0,             FLAG_S)
612 	TEST_ENTRY_3F(SUBB, 4, 0x92345678, 0xffffffff, 0x6dcba986, FLAG_C, FLAG_S)
613 	TEST_ENTRY_3F(SUBB, 4, 0x92345678, 0x00000000, 0x6dcba988, 0,             FLAG_C | FLAG_S)
614 	TEST_ENTRY_3F(SUBB, 4, 0x92345678, 0x00000000, 0x6dcba987, FLAG_C, FLAG_C | FLAG_S)
615 	TEST_ENTRY_3F(SUBB, 4, 0x12345678, 0x12345678, 0xffffffff, FLAG_C, FLAG_C)
616 	TEST_ENTRY_3F(SUBB, 4, 0x00000000, 0x12345678, 0x12345677, FLAG_C, FLAG_Z)
617 
618 	TEST_ENTRY_3F(SUBB, 8, 0x0123456789abcdef, 0x7fffffffffffffff, 0x7edcba9876543210, 0,             0)
619 	TEST_ENTRY_3F(SUBB, 8, 0x0123456789abcdef, 0x7fffffffffffffff, 0x7edcba987654320f, FLAG_C, 0)
620 	TEST_ENTRY_3F(SUBB, 8, 0x0123456789abcdef, 0x8000000000000000, 0x7edcba9876543211, 0,             FLAG_V)
621 	TEST_ENTRY_3F(SUBB, 8, 0x0123456789abcdef, 0x8000000000000000, 0x7edcba9876543210, FLAG_C, FLAG_V)
622 	TEST_ENTRY_3F(SUBB, 8, 0x8123456789abcdef, 0xffffffffffffffff, 0x7edcba9876543210, 0,             FLAG_S)
623 	TEST_ENTRY_3F(SUBB, 8, 0x8123456789abcdef, 0xffffffffffffffff, 0x7edcba987654320f, FLAG_C, FLAG_S)
624 	TEST_ENTRY_3F(SUBB, 8, 0x8123456789abcdef, 0x0000000000000000, 0x7edcba9876543211, 0,             FLAG_C | FLAG_S)
625 	TEST_ENTRY_3F(SUBB, 8, 0x8123456789abcdef, 0x0000000000000000, 0x7edcba9876543210, FLAG_C, FLAG_C | FLAG_S)
626 	TEST_ENTRY_3F(SUBB, 8, 0x123456789abcdef0, 0x123456789abcdef0, 0xffffffffffffffff, FLAG_C, FLAG_C)
627 	TEST_ENTRY_3F(SUBB, 8, 0x0000000000000000, 0x123456789abcdef0, 0x123456789abcdeef, FLAG_C, FLAG_Z)
628 
629 	TEST_ENTRY_2(CMP, 4, 0x7fffffff, 0x6dcba987, 0)
630 	TEST_ENTRY_2(CMP, 4, 0x80000000, 0x6dcba988, FLAG_V)
631 	TEST_ENTRY_2(CMP, 4, 0xffffffff, 0x6dcba987, FLAG_S)
632 	TEST_ENTRY_2(CMP, 4, 0x00000000, 0x6dcba988, FLAG_C | FLAG_S)
633 	TEST_ENTRY_2(CMP, 4, 0x12345678, 0x12345678, FLAG_Z)
634 
635 	TEST_ENTRY_2(CMP, 8, 0x7fffffffffffffff, 0x7edcba9876543210, 0)
636 	TEST_ENTRY_2(CMP, 8, 0x8000000000000000, 0x7edcba9876543211, FLAG_V)
637 	TEST_ENTRY_2(CMP, 8, 0xffffffffffffffff, 0x7edcba9876543210, FLAG_S)
638 	TEST_ENTRY_2(CMP, 8, 0x0000000000000000, 0x7edcba9876543211, FLAG_C | FLAG_S)
639 	TEST_ENTRY_2(CMP, 8, 0x0123456789abcdef, 0x0123456789abcdef, FLAG_Z)
640 
641 	TEST_ENTRY_4(MULU, 4, 0x77777777, 0x00000000, 0x11111111, 0x00000007, 0)
642 	TEST_ENTRY_4(MULU, 4, 0xffffffff, 0x00000000, 0x11111111, 0x0000000f, 0)
643 	TEST_ENTRY_4(MULU, 4, 0x00000000, 0x00000000, 0x11111111, 0x00000000, FLAG_Z)
644 	TEST_ENTRY_4(MULU, 4, 0xea61d951, 0x37c048d0, 0x77777777, 0x77777777, FLAG_V)
645 	TEST_ENTRY_4(MULU, 4, 0x32323233, 0xcdcdcdcc, 0xcdcdcdcd, 0xffffffff, FLAG_V | FLAG_S)
646 
647 	TEST_ENTRY_4(MULU, 8, 0x7777777777777777, 0x0000000000000000, 0x1111111111111111, 0x0000000000000007, 0)
648 	TEST_ENTRY_4(MULU, 8, 0xffffffffffffffff, 0x0000000000000000, 0x1111111111111111, 0x000000000000000f, 0)
649 	TEST_ENTRY_4(MULU, 8, 0x0000000000000000, 0x0000000000000000, 0x1111111111111111, 0x0000000000000000, FLAG_Z)
650 	TEST_ENTRY_4(MULU, 8, 0x0c83fb72ea61d951, 0x37c048d159e26af3, 0x7777777777777777, 0x7777777777777777, FLAG_V)
651 	TEST_ENTRY_4(MULU, 8, 0x3232323232323233, 0xcdcdcdcdcdcdcdcc, 0xcdcdcdcdcdcdcdcd, 0xffffffffffffffff, FLAG_V | FLAG_S)
652 
653 	TEST_ENTRY_4(MULS, 4, 0x77777777, 0x00000000, 0x11111111, 0x00000007, 0)
654 	TEST_ENTRY_4(MULS, 4, 0xffffffff, 0x00000000, 0x11111111, 0x0000000f, FLAG_V)
655 	TEST_ENTRY_4(MULS, 4, 0x00000000, 0x00000000, 0x11111111, 0x00000000, FLAG_Z)
656 	TEST_ENTRY_4(MULS, 4, 0x9e26af38, 0xc83fb72e, 0x77777777, 0x88888888, FLAG_V | FLAG_S)
657 	TEST_ENTRY_4(MULS, 4, 0x32323233, 0x00000000, 0xcdcdcdcd, 0xffffffff, 0)
658 
659 	TEST_ENTRY_4(MULS, 8, 0x7777777777777777, 0x0000000000000000, 0x1111111111111111, 0x0000000000000007, 0)
660 	TEST_ENTRY_4(MULS, 8, 0xffffffffffffffff, 0x0000000000000000, 0x1111111111111111, 0x000000000000000f, FLAG_V)
661 	TEST_ENTRY_4(MULS, 8, 0x0000000000000000, 0x0000000000000000, 0x1111111111111111, 0x0000000000000000, FLAG_Z)
662 	TEST_ENTRY_4(MULS, 8, 0x7c048d159e26af38, 0xc83fb72ea61d950c, 0x7777777777777777, 0x8888888888888888, FLAG_V | FLAG_S)
663 	TEST_ENTRY_4(MULS, 8, 0x3232323232323233, 0x0000000000000000, 0xcdcdcdcdcdcdcdcd, 0xffffffffffffffff, 0)
664 
665 	TEST_ENTRY_4(DIVU, 4, 0x02702702, 0x00000003, 0x11111111, 0x00000007, 0)
666 	TEST_ENTRY_4(DIVU, 4, 0x00000000, 0x11111111, 0x11111111, 0x11111112, FLAG_Z)
667 	TEST_ENTRY_4(DIVU, 4, 0x7fffffff, 0x00000000, 0xfffffffe, 0x00000002, 0)
668 	TEST_ENTRY_4(DIVU, 4, 0xfffffffe, 0x00000000, 0xfffffffe, 0x00000001, FLAG_S)
669 	TEST_ENTRY_4(DIVU, 4, UNDEFINED,  UNDEFINED,  0xffffffff, 0x00000000, FLAG_V)
670 
671 	TEST_ENTRY_4(DIVU, 8, 0x0270270270270270, 0x0000000000000001, 0x1111111111111111, 0x0000000000000007, 0)
672 	TEST_ENTRY_4(DIVU, 8, 0x0000000000000000, 0x1111111111111111, 0x1111111111111111, 0x1111111111111112, FLAG_Z)
673 	TEST_ENTRY_4(DIVU, 8, 0x7fffffffffffffff, 0x0000000000000000, 0xfffffffffffffffe, 0x0000000000000002, 0)
674 	TEST_ENTRY_4(DIVU, 8, 0xfffffffffffffffe, 0x0000000000000000, 0xfffffffffffffffe, 0x0000000000000001, FLAG_S)
675 	TEST_ENTRY_4(DIVU, 8, UNDEFINED,          UNDEFINED,          0xffffffffffffffff, 0x0000000000000000, FLAG_V)
676 
677 	TEST_ENTRY_4(DIVS, 4, 0x02702702, 0x00000003, 0x11111111, 0x00000007, 0)
678 	TEST_ENTRY_4(DIVS, 4, 0x00000000, 0x11111111, 0x11111111, 0x11111112, FLAG_Z)
679 	TEST_ENTRY_4(DIVS, 4, 0xffffffff, 0x00000000, 0xfffffffe, 0x00000002, FLAG_S)
680 	TEST_ENTRY_4(DIVS, 4, UNDEFINED,  UNDEFINED,  0xffffffff, 0x00000000, FLAG_V)
681 
682 	TEST_ENTRY_4(DIVS, 8, 0x0270270270270270, 0x0000000000000001, 0x1111111111111111, 0x0000000000000007, 0)
683 	TEST_ENTRY_4(DIVS, 8, 0x0000000000000000, 0x1111111111111111, 0x1111111111111111, 0x1111111111111112, FLAG_Z)
684 	TEST_ENTRY_4(DIVS, 8, 0xffffffffffffffff, 0x0000000000000000, 0xfffffffffffffffe, 0x0000000000000002, FLAG_S)
685 	TEST_ENTRY_4(DIVS, 8, UNDEFINED,          UNDEFINED,          0xffffffffffffffff, 0x0000000000000000, FLAG_V)
686 };
687 
688 
689 /*-------------------------------------------------
690     validate_backend - execute a number of
691     generic tests on the backend code generator
692 -------------------------------------------------*/
693 
694 static void validate_backend(drcuml_state *drcuml)
695 {
696 	uml::code_handle *handles[3];
697 	int tnum;
698 
699 	// allocate handles for the code
700 	handles[0] = drcuml->handle_alloc("test_entry");
701 	handles[1] = drcuml->handle_alloc("code_start");
702 	handles[2] = drcuml->handle_alloc("code_end");
703 
704 	// iterate over test entries
705 	printf("Backend validation....\n");
706 	for (tnum = 31; tnum < ARRAY_LENGTH(bevalidate_test_list); tnum++)
707 	{
708 		const bevalidate_test *test = &bevalidate_test_list[tnum];
709 		parameter param[ARRAY_LENGTH(test->param)];
710 		char mnemonic[20], *dst;
711 		const char *src;
712 
713 		// progress
714 		dst = mnemonic;
715 		for (src = opcode_info_table[test->opcode()]->mnemonic; *src != 0; src++)
716 		{
717 			if (*src == '!')
718 			{
719 				if (test->size == 8)
720 					*dst++ = 'd';
721 			}
722 			else if (*src == '#')
723 				*dst++ = (test->size == 8) ? 'd' : 's';
724 			else
725 				*dst++ = *src;
726 		}
727 		*dst = 0;
728 		printf("Executing test %d/%d (%s)", tnum + 1, (int)ARRAY_LENGTH(bevalidate_test_list), mnemonic);
729 
730 		// reset parameter list and iterate
731 		memset(param, 0, sizeof(param));
732 		bevalidate_iterate_over_params(drcuml, handles, test, param, 0);
733 		printf("\n");
734 	}
735 	fatalerror("All tests passed!\n");
736 }
737 
738 
739 /*-------------------------------------------------
740     bevalidate_iterate_over_params - iterate over
741     all supported types and values of a parameter
742     and recursively hand off to the next parameter,
743     or else move on to iterate over the flags
744 -------------------------------------------------*/
745 
746 static void bevalidate_iterate_over_params(drcuml_state *drcuml, uml::code_handle **handles, const bevalidate_test *test, parameter *paramlist, int pnum)
747 {
748 	const opcode_info *opinfo = opcode_info_table[test->opcode()];
749 	drcuml_ptype ptype;
750 
751 	// if no parameters, execute now
752 	if (pnum >= ARRAY_LENGTH(opinfo->param) || opinfo->param[pnum].typemask == PTYPES_NONE)
753 	{
754 		bevalidate_iterate_over_flags(drcuml, handles, test, paramlist);
755 		return;
756 	}
757 
758 	// iterate over valid parameter types
759 	for (ptype = parameter::PTYPE_IMMEDIATE; ptype < parameter::PTYPE_MAX; ptype++)
760 		if (opinfo->param[pnum].typemask & (1 << ptype))
761 		{
762 			int pindex, pcount;
763 
764 			// mapvars can only do 32-bit tests
765 			if (ptype == parameter::PTYPE_MAPVAR && effective_test_psize(opinfo, pnum, test->size, test->param) == 8)
766 				continue;
767 
768 			// for some parameter types, we wish to iterate over all possibilities
769 			switch (ptype)
770 			{
771 				case parameter::PTYPE_INT_REGISTER:     pcount = REG_I_END - REG_I0;        break;
772 				case parameter::PTYPE_FLOAT_REGISTER:   pcount = REG_F_END - REG_F0;        break;
773 				default:                            pcount = 1;                                     break;
774 			}
775 
776 			// iterate over possibilities
777 			for (pindex = 0; pindex < pcount; pindex++)
778 			{
779 				bool skip = false;
780 				int pscannum;
781 
782 				// for param 0, print a dot
783 				if (pnum == 0)
784 					printf(".");
785 
786 				// can't duplicate multiple source parameters unless they are immediates
787 				if (ptype != parameter::PTYPE_IMMEDIATE && (opinfo->param[pnum].output & PIO_IN))
788 
789 					// loop over all parameters we've done before; if the parameter is a source and matches us, skip this case
790 					for (pscannum = 0; pscannum < pnum; pscannum++)
791 						if ((opinfo->param[pscannum].output & PIO_IN) && ptype == paramlist[pscannum].type && pindex == paramlist[pscannum].value)
792 							skip = true;
793 
794 				// can't duplicate multiple dest parameters
795 				if (opinfo->param[pnum].output & PIO_OUT)
796 
797 					// loop over all parameters we've done before; if the parameter is a source and matches us, skip this case
798 					for (pscannum = 0; pscannum < pnum; pscannum++)
799 						if ((opinfo->param[pscannum].output & PIO_OUT) && ptype == paramlist[pscannum].type && pindex == paramlist[pscannum].value)
800 							skip = true;
801 
802 				// iterate over the next parameter in line
803 				if (!skip)
804 				{
805 					paramlist[pnum].type = ptype;
806 					paramlist[pnum].value = pindex;
807 					bevalidate_iterate_over_params(drcuml, handles, test, paramlist, pnum + 1);
808 				}
809 			}
810 		}
811 }
812 
813 
814 /*-------------------------------------------------
815     bevalidate_iterate_over_flags - iterate over
816     all supported flag masks
817 -------------------------------------------------*/
818 
819 static void bevalidate_iterate_over_flags(drcuml_state *drcuml, uml::code_handle **handles, const bevalidate_test *test, parameter *paramlist)
820 {
821 	const opcode_info *opinfo = opcode_info_table[test->opcode()];
822 	uint8_t flagmask = opinfo->outflags;
823 	uint8_t curmask;
824 
825 	// iterate over all possible flag combinations
826 	for (curmask = 0; curmask <= flagmask; curmask++)
827 		if ((curmask & flagmask) == curmask)
828 			bevalidate_execute(drcuml, handles, test, paramlist, curmask);
829 }
830 
831 
832 /*-------------------------------------------------
833     bevalidate_execute - execute a single instance
834     of a test, generating code and verifying the
835     results
836 -------------------------------------------------*/
837 
838 static void bevalidate_execute(drcuml_state *drcuml, uml::code_handle **handles, const bevalidate_test *test, const parameter *paramlist, uint8_t flagmask)
839 {
840 	parameter params[ARRAY_LENGTH(test->param)];
841 	drcuml_machine_state istate, fstate;
842 	instruction testinst;
843 	drcuml_block *block;
844 	uint64_t *parammem;
845 	int numparams;
846 
847 	// allocate memory for parameters
848 	parammem = (uint64_t *)drcuml->cache->alloc_near(sizeof(uint64_t) * (ARRAY_LENGTH(test->param) + 1));
849 
850 	// flush the cache
851 	drcuml->reset();
852 
853 	// start a new block
854 	block = drcuml->block_begin(30);
855 	UML_HANDLE(block, handles[0]);
856 
857 	// set up a random initial state
858 	bevalidate_initialize_random_state(drcuml, block, &istate);
859 
860 	// then populate the state with the parameters
861 	numparams = bevalidate_populate_state(block, &istate, test, paramlist, params, parammem);
862 
863 	// generate the code
864 	UML_RESTORE(block, &istate);
865 	UML_HANDLE(block, handles[1]);
866 	switch (numparams)
867 	{
868 		case 0:
869 			block->append(test->opcode(), test->size);
870 			break;
871 
872 		case 1:
873 			block->append(test->opcode(), test->size, params[0]);
874 			break;
875 
876 		case 2:
877 			block->append(test->opcode(), test->size, params[0], params[1]);
878 			break;
879 
880 		case 3:
881 			block->append(test->opcode(), test->size, params[0], params[1], params[2]);
882 			break;
883 
884 		case 4:
885 			block->append(test->opcode(), test->size, params[0], params[1], params[2], params[3]);
886 			break;
887 	}
888 	testinst = block->inst[block->nextinst - 1];
889 	UML_HANDLE(block, handles[2]);
890 	UML_GETFLGS(block, MEM(&parammem[ARRAY_LENGTH(test->param)]), flagmask);
891 	UML_SAVE(block, &fstate);
892 	UML_EXIT(block, IMM(0));
893 
894 	// end the block
895 	block->end();
896 
897 	// execute
898 	drcuml->execute(*handles[0]);
899 
900 	// verify the results
901 	bevalidate_verify_state(drcuml, &istate, &fstate, test, *(uint32_t *)&parammem[ARRAY_LENGTH(test->param)], params, &testinst, handles[1]->code, handles[2]->code, flagmask);
902 
903 	// free memory
904 	drcuml->cache->dealloc(parammem, sizeof(uint64_t) * (ARRAY_LENGTH(test->param) + 1));
905 }
906 
907 
908 /*-------------------------------------------------
909     bevalidate_initialize_random_state -
910     initialize the machine state to randomness
911 -------------------------------------------------*/
912 
913 static void bevalidate_initialize_random_state(drcuml_state *drcuml, drcuml_block *block, drcuml_machine_state *state)
914 {
915 	running_machine &machine = drcuml->device->machine();
916 	int regnum;
917 
918 	// initialize core state to random values
919 	state->fmod = machine.rand() & 0x03;
920 	state->flags = machine.rand() & 0x1f;
921 	state->exp = machine.rand();
922 
923 	// initialize integer registers to random values
924 	for (regnum = 0; regnum < ARRAY_LENGTH(state->r); regnum++)
925 	{
926 		state->r[regnum].w.h = machine.rand();
927 		state->r[regnum].w.l = machine.rand();
928 	}
929 
930 	// initialize float registers to random values
931 	for (regnum = 0; regnum < ARRAY_LENGTH(state->f); regnum++)
932 	{
933 		*(uint32_t *)&state->f[regnum].s.h = machine.rand();
934 		*(uint32_t *)&state->f[regnum].s.l = machine.rand();
935 	}
936 
937 	// initialize map variables to random values
938 	for (regnum = 0; regnum < MAPVAR_COUNT; regnum++)
939 		UML_MAPVAR(block, MVAR(regnum), machine.rand());
940 }
941 
942 
943 /*-------------------------------------------------
944     bevalidate_populate_state - populate the
945     machine state with the proper values prior
946     to executing a test
947 -------------------------------------------------*/
948 
949 static int bevalidate_populate_state(drcuml_block *block, drcuml_machine_state *state, const bevalidate_test *test, const parameter *paramlist, parameter *params, uint64_t *parammem)
950 {
951 	const opcode_info *opinfo = opcode_info_table[test->opcode()];
952 	int numparams = ARRAY_LENGTH(test->param);
953 	int pnum;
954 
955 	// copy flags as-is
956 	state->flags = test->iflags;
957 
958 	// iterate over parameters
959 	for (pnum = 0; pnum < ARRAY_LENGTH(test->param); pnum++)
960 	{
961 		int psize = effective_test_psize(opinfo, pnum, test->size, test->param);
962 		parameter *curparam = &params[pnum];
963 
964 		// start with a copy of the parameter from the list
965 		*curparam = paramlist[pnum];
966 
967 		// switch off the type
968 		switch (curparam->type)
969 		{
970 			// immediate parameters: take the value from the test entry
971 			case parameter::PTYPE_IMMEDIATE:
972 				curparam->value = test->param[pnum];
973 				break;
974 
975 			// register parameters: set the register value in the state and set the parameter value to the register index
976 			case parameter::PTYPE_INT_REGISTER:
977 				state->r[curparam->value].d = test->param[pnum];
978 				curparam->value += REG_I0;
979 				break;
980 
981 			// register parameters: set the register value in the state and set the parameter value to the register index
982 			case parameter::PTYPE_FLOAT_REGISTER:
983 				state->f[curparam->value].d = test->param[pnum];
984 				curparam->value += REG_F0;
985 				break;
986 
987 			// memory parameters: set the memory value in the parameter space and set the parameter value to point to it
988 			case parameter::PTYPE_MEMORY:
989 				curparam->value = (uintptr_t)&parammem[pnum];
990 				if (psize == 4)
991 					*(uint32_t *)(uintptr_t)curparam->value = test->param[pnum];
992 				else
993 					*(uint64_t *)(uintptr_t)curparam->value = test->param[pnum];
994 				break;
995 
996 			// map variables: issue a MAPVAR instruction to set the value and set the parameter value to the mapvar index
997 			case parameter::PTYPE_MAPVAR:
998 				UML_MAPVAR(block, MVAR(curparam->value), test->param[pnum]);
999 				curparam->value += MAPVAR_M0;
1000 				break;
1001 
1002 			// use anything else to count the number of parameters
1003 			default:
1004 				numparams = MIN(numparams, pnum);
1005 				break;
1006 		}
1007 	}
1008 
1009 	// return the total number of parameters
1010 	return numparams;
1011 }
1012 
1013 
1014 /*-------------------------------------------------
1015     bevalidate_verify_state - verify the final
1016     state after executing a test, and report any
1017     discrepancies
1018 -------------------------------------------------*/
1019 
1020 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)
1021 {
1022 	const opcode_info *opinfo = opcode_info_table[test->opcode()];
1023 	uint8_t ireg[REG_I_END - REG_I0] = { 0 };
1024 	uint8_t freg[REG_F_END - REG_F0] = { 0 };
1025 	char errorbuf[1024];
1026 	char *errend = errorbuf;
1027 	int pnum, regnum;
1028 
1029 	*errend = 0;
1030 
1031 	// check flags
1032 	if (flags != (test->flags & flagmask))
1033 	{
1034 		errend += sprintf(errend, "  Flags ... result:%c%c%c%c%c  expected:%c%c%c%c%c\n",
1035 			(flagmask & FLAG_U) ? ((flags & FLAG_U) ? 'U' : '.') : '-',
1036 			(flagmask & FLAG_S) ? ((flags & FLAG_S) ? 'S' : '.') : '-',
1037 			(flagmask & FLAG_Z) ? ((flags & FLAG_Z) ? 'Z' : '.') : '-',
1038 			(flagmask & FLAG_V) ? ((flags & FLAG_V) ? 'V' : '.') : '-',
1039 			(flagmask & FLAG_C) ? ((flags & FLAG_C) ? 'C' : '.') : '-',
1040 			(flagmask & FLAG_U) ? ((test->flags & FLAG_U) ? 'U' : '.') : '-',
1041 			(flagmask & FLAG_S) ? ((test->flags & FLAG_S) ? 'S' : '.') : '-',
1042 			(flagmask & FLAG_Z) ? ((test->flags & FLAG_Z) ? 'Z' : '.') : '-',
1043 			(flagmask & FLAG_V) ? ((test->flags & FLAG_V) ? 'V' : '.') : '-',
1044 			(flagmask & FLAG_C) ? ((test->flags & FLAG_C) ? 'C' : '.') : '-');
1045 	}
1046 
1047 	// check destination parameters
1048 	for (pnum = 0; pnum < ARRAY_LENGTH(test->param); pnum++)
1049 		if (opinfo->param[pnum].output & PIO_OUT)
1050 		{
1051 			int psize = effective_test_psize(opinfo, pnum, test->size, test->param);
1052 			uint64_t mask = u64(0xffffffffffffffff) >> (64 - 8 * psize);
1053 			uint64_t result = 0;
1054 
1055 			// fetch the result from the parameters
1056 			switch (params[pnum].type)
1057 			{
1058 				// integer registers fetch from the state
1059 				case parameter::PTYPE_INT_REGISTER:
1060 					ireg[params[pnum].value - REG_I0] = 1;
1061 					result = state->r[params[pnum].value - REG_I0].d;
1062 					break;
1063 
1064 				// float registers fetch from the state
1065 				case parameter::PTYPE_FLOAT_REGISTER:
1066 					freg[params[pnum].value - REG_I0] = 1;
1067 					result = state->f[params[pnum].value - REG_F0].d;
1068 					break;
1069 
1070 				// memory registers fetch from the memory address
1071 				case parameter::PTYPE_MEMORY:
1072 					if (psize == 4)
1073 						result = *(uint32_t *)(uintptr_t)params[pnum].value;
1074 					else
1075 						result = *(uint64_t *)(uintptr_t)params[pnum].value;
1076 					break;
1077 
1078 				default:
1079 					break;
1080 			}
1081 
1082 			// check against the mask
1083 			if (test->param[pnum] != UNDEFINED_U64 && (result & mask) != (test->param[pnum] & mask))
1084 			{
1085 				if ((uint32_t)mask == mask)
1086 					errend += sprintf(errend, "  Parameter %d ... result:%08X  expected:%08X\n", pnum,
1087 										(uint32_t)(result & mask), (uint32_t)(test->param[pnum] & mask));
1088 				else
1089 					errend += sprintf(errend, "  Parameter %d ... result:%08X%08X  expected:%08X%08X\n", pnum,
1090 										(uint32_t)((result & mask) >> 32), (uint32_t)(result & mask),
1091 										(uint32_t)((test->param[pnum] & mask) >> 32), (uint32_t)(test->param[pnum] & mask));
1092 			}
1093 		}
1094 
1095 	// check source integer parameters for unexpected alterations
1096 	for (regnum = 0; regnum < ARRAY_LENGTH(state->r); regnum++)
1097 		if (ireg[regnum] == 0 && istate->r[regnum].d != state->r[regnum].d)
1098 			errend += sprintf(errend, "  Register i%d ... result:%08X%08X  originally:%08X%08X\n", regnum,
1099 								(uint32_t)(state->r[regnum].d >> 32), (uint32_t)state->r[regnum].d,
1100 								(uint32_t)(istate->r[regnum].d >> 32), (uint32_t)istate->r[regnum].d);
1101 
1102 	// check source float parameters for unexpected alterations
1103 	for (regnum = 0; regnum < ARRAY_LENGTH(state->f); regnum++)
1104 		if (freg[regnum] == 0 && *(uint64_t *)&istate->f[regnum].d != *(uint64_t *)&state->f[regnum].d)
1105 			errend += sprintf(errend, "  Register f%d ... result:%08X%08X  originally:%08X%08X\n", regnum,
1106 								(uint32_t)(*(uint64_t *)&state->f[regnum].d >> 32), (uint32_t)*(uint64_t *)&state->f[regnum].d,
1107 								(uint32_t)(*(uint64_t *)&istate->f[regnum].d >> 32), (uint32_t)*(uint64_t *)&istate->f[regnum].d);
1108 
1109 	// output the error if we have one
1110 	if (errend != errorbuf)
1111 	{
1112 		// disassemble the test instruction
1113 		std::string disasm = testinst->disasm(drcuml);
1114 
1115 		// output a description of what went wrong
1116 		printf("\n");
1117 		printf("----------------------------------------------\n");
1118 		printf("Backend validation error:\n");
1119 		printf("   %s\n", disasm.c_str());
1120 		printf("\n");
1121 		printf("Errors:\n");
1122 		printf("%s\n", errorbuf);
1123 		fatalerror("Error during validation\n");
1124 	}
1125 	return errend != errorbuf;
1126 }
1127 
1128 #endif
1129