1 // license:BSD-3-Clause
2 // copyright-holders:Patrick Mackinlay
3 
4 /*
5  * An implementation of the Fairchild/Intergraph CLIPPER CPU family.
6  *
7  * Primary source: http://bitsavers.org/pdf/fairchild/clipper/Clipper_Instruction_Set_Oct85.pdf
8  *
9  * TODO:
10  *   - unimplemented C400 instructions (cdb, cnvx[ds]w, loadts, waitd)
11  *   - correct boot logic
12  *   - instruction timing
13  *   - big endian support (not present in the wild)
14  */
15 
16 #include "emu.h"
17 #include "debugger.h"
18 #include "clipper.h"
19 #include "clipperd.h"
20 
21 #define LOG_GENERAL   (1U << 0)
22 #define LOG_EXCEPTION (1U << 1)
23 #define LOG_SYSCALLS  (1U << 2)
24 
25 //#define VERBOSE (LOG_GENERAL | LOG_EXCEPTION)
26 #define VERBOSE (LOG_SYSCALLS)
27 
28 #include "logmacro.h"
29 
30 // convenience macros for frequently used instruction fields
31 #define R1 (m_info.r1)
32 #define R2 (m_info.r2)
33 
34 #define BIT31(x) BIT(x, 31)
35 #define BIT63(x) BIT(x, 63)
36 
37 // macros for computing and setting condition codes
38 #define FLAGS(C,V,Z,N) \
39 	m_psw = (m_psw & ~(PSW_C | PSW_V | PSW_Z | PSW_N)) | (((C) << 3) | ((V) << 2) | ((Z) << 1) | ((N) << 0))
40 
41 #define FLAGS_ADD(op2, op1, result) FLAGS(                                        \
42 	(BIT31(op2) && BIT31(op1)) || (!BIT31(result) && (BIT31(op2) || BIT31(op1))), \
43 	(BIT31(op2) == BIT31(op1)) && (BIT31(result) != BIT31(op2)),                  \
44 	result == 0, BIT31(result))
45 
46 #define FLAGS_SUB(op2, op1, result) FLAGS(                                         \
47 	(!BIT31(op2) && BIT31(op1)) || (BIT31(result) && (!BIT31(op2) || BIT31(op1))), \
48 	(BIT31(op2) != BIT31(op1)) && (BIT31(result) != BIT31(op2)),                   \
49 	result == 0, BIT31(result))
50 
51 DEFINE_DEVICE_TYPE(CLIPPER_C100, clipper_c100_device, "clipper_c100", "C100 CLIPPER")
52 DEFINE_DEVICE_TYPE(CLIPPER_C300, clipper_c300_device, "clipper_c300", "C300 CLIPPER")
53 DEFINE_DEVICE_TYPE(CLIPPER_C400, clipper_c400_device, "clipper_c400", "C400 CLIPPER")
54 
clipper_c100_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)55 clipper_c100_device::clipper_c100_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
56 	: clipper_device(mconfig, CLIPPER_C100, tag, owner, clock, ENDIANNESS_LITTLE, SSW_ID_C1R1)
57 	, m_icammu(*this, "^cammu_i")
58 	, m_dcammu(*this, "^cammu_d")
59 {
60 }
61 
clipper_c300_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)62 clipper_c300_device::clipper_c300_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
63 	: clipper_device(mconfig, CLIPPER_C300, tag, owner, clock, ENDIANNESS_LITTLE, SSW_ID_C3R1)
64 	, m_icammu(*this, "^cammu_i")
65 	, m_dcammu(*this, "^cammu_d")
66 {
67 }
68 
clipper_c400_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)69 clipper_c400_device::clipper_c400_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
70 	: clipper_device(mconfig, CLIPPER_C400, tag, owner, clock, ENDIANNESS_LITTLE, SSW_ID_C4R4)
71 	, m_db_pc(0)
72 	, m_cammu(*this, "^cammu")
73 {
74 }
75 
clipper_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,u32 clock,const endianness_t endianness,const u32 cpuid)76 clipper_device::clipper_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, const endianness_t endianness, const u32 cpuid)
77 	: cpu_device(mconfig, type, tag, owner, clock)
78 	, m_main_config("main", endianness, 32, 32, 0)
79 	, m_io_config("io", endianness, 32, 32, 0)
80 	, m_boot_config("boot", endianness, 32, 32, 0)
81 	, m_icount(0)
82 	, m_psw(endianness == ENDIANNESS_BIG ? PSW_BIG : 0)
83 	, m_ssw(cpuid)
84 	, m_r(m_rs)
85 	, m_ru{0}
86 	, m_rs{0}
87 	, m_f{0}
88 	, m_fp_pc(0)
89 	, m_fp_dst(0)
90 	, m_info{0}
91 {
92 }
93 
94 // rotate helpers to replace MSVC intrinsics
rotl32(u32 x,u8 shift)95 inline u32 rotl32(u32 x, u8 shift)
96 {
97   shift &= 31;
98   return (x << shift) | (x >> ((32 - shift) & 31));
99 }
100 
rotr32(u32 x,u8 shift)101 inline u32 rotr32(u32 x, u8 shift)
102 {
103   shift &= 31;
104   return (x >> shift) | (x << ((32 - shift) & 31));
105 }
106 
rotl64(u64 x,u8 shift)107 inline u64 rotl64(u64 x, u8 shift)
108 {
109   shift &= 63;
110   return (x << shift) | (x >> ((64 - shift) & 63));
111 }
112 
rotr64(u64 x,u8 shift)113 inline u64 rotr64(u64 x, u8 shift)
114 {
115   shift &= 63;
116   return (x >> shift) | (x << ((64 - shift) & 63));
117 }
118 
device_start()119 void clipper_device::device_start()
120 {
121 	// configure the cammu address spaces
122 	get_dcammu().set_spaces(space(0), space(1), space(2));
123 	get_icammu().set_spaces(space(0), space(1), space(2));
124 
125 	// set our instruction counter
126 	set_icountptr(m_icount);
127 
128 	// program-visible cpu state
129 	save_item(NAME(m_pc));
130 	save_item(NAME(m_psw));
131 	save_item(NAME(m_ssw));
132 	save_item(NAME(m_ru));
133 	save_item(NAME(m_rs));
134 	save_item(NAME(m_f));
135 	save_item(NAME(m_fp_pc));
136 	save_item(NAME(m_fp_dst));
137 
138 	// non-visible cpu state
139 	save_item(NAME(m_wait));
140 	save_item(NAME(m_nmi));
141 	save_item(NAME(m_irq));
142 	save_item(NAME(m_ivec));
143 	save_item(NAME(m_exception));
144 
145 	state_add(STATE_GENPC, "GENPC", m_pc).noshow();
146 	state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow();
147 	state_add(STATE_GENFLAGS, "GENFLAGS", m_psw).mask(0xf).formatstr("%4s").noshow();
148 
149 	state_add(CLIPPER_PC, "pc", m_pc);
150 	state_add(CLIPPER_PSW, "psw", m_psw);
151 	state_add(CLIPPER_SSW, "ssw", m_ssw);
152 
153 	// integer regsters
154 	for (int i = 0; i < get_ireg_count(); i++)
155 		state_add(CLIPPER_UREG + i, util::string_format("ur%d", i).c_str(), m_ru[i]);
156 	for (int i = 0; i < get_ireg_count(); i++)
157 		state_add(CLIPPER_SREG + i, util::string_format("sr%d", i).c_str(), m_rs[i]);
158 
159 	// floating point registers
160 	for (int i = 0; i < get_freg_count(); i++)
161 		state_add(CLIPPER_FREG + i, util::string_format("f%d", i).c_str(), m_f[i]);
162 }
163 
device_start()164 void clipper_c400_device::device_start()
165 {
166 	clipper_device::device_start();
167 
168 	save_item(NAME(m_db_pc));
169 }
170 
device_reset()171 void clipper_device::device_reset()
172 {
173 	/*
174 	 * From C300 documentation, on reset:
175 	 *   psw: T cleared, BIG set from hardware, others undefined
176 	 *   ssw: EI, TP, M, U, K, KU, UU, P cleared, ID set from hardware, others undefined
177 	 */
178 
179 	// clear the psw and ssw
180 	set_psw(0);
181 	set_ssw(0);
182 
183 	// FIXME: figure out how to branch to the boot code properly
184 	m_pc = 0x7f100000;
185 
186 	m_wait = false;
187 	m_nmi = CLEAR_LINE;
188 	m_irq = CLEAR_LINE;
189 	m_ivec = 0;
190 	m_exception = 0;
191 }
192 
state_string_export(const device_state_entry & entry,std::string & str) const193 void clipper_device::state_string_export(const device_state_entry &entry, std::string &str) const
194 {
195 	switch (entry.index())
196 	{
197 	case STATE_GENFLAGS:
198 		str = string_format("%c%c%c%c",
199 			PSW(C) ? 'C' : '.',
200 			PSW(V) ? 'V' : '.',
201 			PSW(Z) ? 'Z' : '.',
202 			PSW(N) ? 'N' : '.');
203 		break;
204 	}
205 }
206 
execute_run()207 void clipper_device::execute_run()
208 {
209 	// check for non-maskable and prioritised interrupts
210 	if (m_nmi)
211 	{
212 		// acknowledge non-maskable interrupt
213 		standard_irq_callback(INPUT_LINE_NMI);
214 
215 		LOGMASKED(LOG_EXCEPTION, "non-maskable interrupt\n");
216 		m_pc = intrap(EXCEPTION_INTERRUPT_BASE, m_pc);
217 	}
218 	else if (SSW(EI) && m_irq)
219 	{
220 		LOGMASKED(LOG_EXCEPTION, "prioritised interrupt vector 0x%02x\n", m_ivec);
221 
222 		// allow equal/higher priority interrupts
223 		if ((m_ivec & IVEC_LEVEL) <= SSW(IL))
224 		{
225 			// acknowledge interrupt
226 			standard_irq_callback(INPUT_LINE_IRQ0);
227 
228 			m_pc = intrap(EXCEPTION_INTERRUPT_BASE + m_ivec * 8, m_pc);
229 
230 			LOGMASKED(LOG_EXCEPTION, "transferring control to vector 0x%02x address 0x%08x\n", m_ivec, m_pc);
231 		}
232 	}
233 
234 	while (m_icount > 0)
235 	{
236 		debugger_instruction_hook(m_pc);
237 
238 		if (m_wait)
239 		{
240 			m_icount = 0;
241 			continue;
242 		}
243 
244 		// fetch and decode an instruction
245 		if (decode_instruction())
246 		{
247 			softfloat_exceptionFlags = 0;
248 
249 			// execute instruction
250 			execute_instruction();
251 
252 			// check floating point exceptions
253 			if (softfloat_exceptionFlags)
254 				fp_exception();
255 		}
256 
257 		if (m_exception)
258 		{
259 			debugger_exception_hook(m_exception);
260 
261 			/*
262 			 * For traced instructions which are interrupted or cause traps, the TP
263 			 * flag is set by hardware when the interrupt or trap occurs to ensure
264 			 * that the trace trap will occur immediately after the interrupt or other
265 			 * trap has been serviced.
266 			 */
267 			// FIXME: don't know why/when the trace pending flag is needed
268 			if (PSW(T))
269 				m_ssw |= SSW_TP;
270 
271 			switch (m_exception)
272 			{
273 				// data memory trap group
274 			case EXCEPTION_D_CORRECTED_MEMORY_ERROR:
275 			case EXCEPTION_D_UNCORRECTABLE_MEMORY_ERROR:
276 			case EXCEPTION_D_ALIGNMENT_FAULT:
277 			case EXCEPTION_D_PAGE_FAULT:
278 			case EXCEPTION_D_READ_PROTECT_FAULT:
279 			case EXCEPTION_D_WRITE_PROTECT_FAULT:
280 
281 				// instruction memory trap group
282 			case EXCEPTION_I_CORRECTED_MEMORY_ERROR:
283 			case EXCEPTION_I_UNCORRECTABLE_MEMORY_ERROR:
284 			case EXCEPTION_I_ALIGNMENT_FAULT:
285 			case EXCEPTION_I_PAGE_FAULT:
286 			case EXCEPTION_I_EXECUTE_PROTECT_FAULT:
287 
288 				// illegal operation trap group
289 			case EXCEPTION_ILLEGAL_OPERATION:
290 			case EXCEPTION_PRIVILEGED_INSTRUCTION:
291 				// return address is faulting instruction
292 				m_pc = intrap(m_exception, m_info.pc);
293 				break;
294 
295 			default:
296 				// return address is following instruction
297 				m_pc = intrap(m_exception, m_pc);
298 				break;
299 			}
300 		}
301 
302 		// FIXME: trace trap logic not working properly yet
303 		//else if (PSW(T))
304 		//  m_pc = intrap(EXCEPTION_TRACE, m_pc);
305 
306 		// FIXME: some instructions take longer (significantly) than one cycle
307 		// and also the timings are often slower for the C100 and C300
308 		m_icount -= 4;
309 	}
310 }
311 
execute_set_input(int inputnum,int state)312 void clipper_device::execute_set_input(int inputnum, int state)
313 {
314 	if (state)
315 		m_wait = false;
316 
317 	switch (inputnum)
318 	{
319 	case INPUT_LINE_IRQ0:
320 		m_irq = state;
321 		break;
322 
323 	case INPUT_LINE_NMI:
324 		m_nmi = state;
325 		break;
326 	}
327 }
328 
memory_space_config() const329 device_memory_interface::space_config_vector clipper_device::memory_space_config() const
330 {
331 	return space_config_vector {
332 		std::make_pair(0, &m_main_config),
333 		std::make_pair(1, &m_io_config),
334 		std::make_pair(2, &m_boot_config)
335 	};
336 }
337 
memory_translate(int spacenum,int intention,offs_t & address)338 bool clipper_device::memory_translate(int spacenum, int intention, offs_t &address)
339 {
340 	return ((intention & TRANSLATE_TYPE_MASK) == TRANSLATE_FETCH ? get_icammu() : get_dcammu()).memory_translate(m_ssw, spacenum, intention, address);
341 }
342 
set_exception(u16 data)343 void clipper_device::set_exception(u16 data)
344 {
345 	LOGMASKED(LOG_EXCEPTION, "external exception 0x%04x triggered\n", data);
346 
347 	// check if corrected memory errors are masked
348 	if (!SSW(ECM) && (data == EXCEPTION_D_CORRECTED_MEMORY_ERROR || data == EXCEPTION_I_CORRECTED_MEMORY_ERROR))
349 		return;
350 
351 	m_exception = data;
352 }
353 
354 /*
355  * Fetch and decode an instruction and compute an effective address (for
356  * instructions with addressing modes). The results are contained in the m_info
357  * structure to simplify passing between here and execute_instruction().
358  */
decode_instruction()359 bool clipper_device::decode_instruction()
360 {
361 	// record the current instruction address
362 	m_info.pc = m_pc;
363 
364 	// fetch and decode the primary parcel
365 	if (!get_icammu().fetch<u16>(m_ssw, m_pc + 0, [this](u16 insn) {
366 		m_info.opcode = insn >> 8;
367 		m_info.subopcode = insn & 0xff;
368 		m_info.r1 = (insn & 0x00f0) >> 4;
369 		m_info.r2 = insn & 0x000f;
370 	}))
371 		return false;
372 
373 	// initialise the other fields
374 	m_info.imm = 0;
375 	m_info.macro = 0;
376 	m_info.address = 0;
377 
378 	// default instruction size is 2 bytes
379 	int size = 2;
380 
381 	if ((m_info.opcode & 0xf8) == 0x38)
382 	{
383 		// fetch 16 bit immediate and sign extend
384 		if (!get_icammu().fetch<s16>(m_ssw, m_pc + 2, [this](s32 v) { m_info.imm = v; }))
385 			return false;
386 		size = 4;
387 	}
388 	else if ((m_info.opcode & 0xd3) == 0x83)
389 	{
390 		// instruction has an immediate operand, either 16 or 32 bit
391 		if (m_info.subopcode & 0x80)
392 		{
393 			// fetch 16 bit immediate and sign extend
394 			if (!get_icammu().fetch<s16>(m_ssw, m_pc + 2, [this](s32 v) { m_info.imm = v; }))
395 				return false;
396 			size = 4;
397 		}
398 		else
399 		{
400 			// fetch 32 bit immediate
401 			if (!get_icammu().fetch<u32>(m_ssw, m_pc + 2, [this](u32 v) { m_info.imm = v; }))
402 				return false;
403 			size = 6;
404 		}
405 	}
406 	else if ((m_info.opcode & 0xc0) == 0x40)
407 	{
408 		// instructions with addresses
409 		if (m_info.opcode & 0x01)
410 		{
411 			// instructions with complex modes
412 			switch (m_info.subopcode & 0xf0)
413 			{
414 			case ADDR_MODE_PC32:
415 				if (!get_icammu().fetch<u32>(m_ssw, m_pc + 2, [this](u32 v) { m_info.address = m_pc + v; }))
416 					return false;
417 				size = 6;
418 				break;
419 
420 			case ADDR_MODE_ABS32:
421 				if (!get_icammu().fetch<u32>(m_ssw, m_pc + 2, [this](u32 v) { m_info.address = v; }))
422 					return false;
423 				size = 6;
424 				break;
425 
426 			case ADDR_MODE_REL32:
427 				if (!get_icammu().fetch<u16>(m_ssw, m_pc + 2, [this](u16 v) { m_info.r2 = v & 0xf; }))
428 					return false;
429 
430 				if (!get_icammu().fetch<u32>(m_ssw, m_pc + 4, [this](u32 v) { m_info.address = m_r[m_info.subopcode & 0xf] + v; }))
431 					return false;
432 				size = 8;
433 				break;
434 
435 			case ADDR_MODE_PC16:
436 				if (!get_icammu().fetch<s16>(m_ssw, m_pc + 2, [this](s16 v) { m_info.address = m_pc + v; }))
437 					return false;
438 				size = 4;
439 				break;
440 
441 			case ADDR_MODE_REL12:
442 				if (!get_icammu().fetch<s16>(m_ssw, m_pc + 2, [this](s16 v) {
443 					m_info.r2 = v & 0xf;
444 					m_info.address = m_r[m_info.subopcode & 0xf] + (v >> 4);
445 				}))
446 					return false;
447 				size = 4;
448 				break;
449 
450 			case ADDR_MODE_ABS16:
451 				if (!get_icammu().fetch<s16>(m_ssw, m_pc + 2, [this](s32 v) { m_info.address = v; }))
452 					return false;
453 				size = 4;
454 				break;
455 
456 			case ADDR_MODE_PCX:
457 				if (!get_icammu().fetch<u16>(m_ssw, m_pc + 2, [this](u16 v) {
458 					m_info.r2 = v & 0xf;
459 					m_info.address = m_pc + m_r[(v >> 4) & 0xf];
460 				}))
461 					return false;
462 				size = 4;
463 				break;
464 
465 			case ADDR_MODE_RELX:
466 				if (!get_icammu().fetch<u16>(m_ssw, m_pc + 2, [this](u16 v) {
467 					m_info.r2 = v & 0xf;
468 					m_info.address = m_r[m_info.subopcode & 0xf] + m_r[(v >> 4) & 0xf];
469 				}))
470 					return false;
471 				size = 4;
472 				break;
473 
474 			default:
475 				m_exception = EXCEPTION_ILLEGAL_OPERATION;
476 				return false;
477 			}
478 		}
479 		else
480 			// relative addressing mode
481 			m_info.address = m_r[m_info.r1];
482 	}
483 	else if ((m_info.opcode & 0xfd) == 0xb4)
484 	{
485 		// macro instructions
486 		if (!get_icammu().fetch<u16>(m_ssw, m_pc + 2, [this](u16 v) { m_info.macro = v; }))
487 			return false;
488 		size = 4;
489 	}
490 
491 	// instruction fetch and decode complete
492 	m_pc = m_pc + size;
493 
494 	return true;
495 }
496 
execute_instruction()497 void clipper_device::execute_instruction()
498 {
499 	switch (m_info.opcode)
500 	{
501 	case 0x00: // noop
502 		break;
503 
504 	case 0x10:
505 		// movwp: move word to processor register
506 		// treated as a noop if target ssw in user mode
507 		// R1 == 3 means "fast" mode - avoids pipeline flush
508 		if (R1 == 0)
509 			set_psw(m_r[R2]);
510 		else if (!SSW(U) && (R1 == 1 || R1 == 3))
511 			set_ssw(m_r[R2]);
512 		// FLAGS: CVZN
513 		break;
514 	case 0x11:
515 		// movpw: move processor register to word
516 		switch (R1)
517 		{
518 		case 0: m_r[R2] = m_psw; break;
519 		case 1: m_r[R2] = m_ssw; break;
520 		}
521 		break;
522 	case 0x12:
523 		// calls: call supervisor
524 		m_exception = EXCEPTION_SUPERVISOR_CALL_BASE + (m_info.subopcode & 0x7f) * 8;
525 		if (VERBOSE & LOG_SYSCALLS)
526 			switch (m_info.subopcode & 0x7f)
527 			{
528 			case 0x3b: // execve
529 				LOGMASKED(LOG_SYSCALLS, "execve(\"%s\", [ %s ], envp)\n",
530 					debug_string(m_r[0]), debug_string_array(m_r[1]));
531 				break;
532 			}
533 		break;
534 	case 0x13:
535 		// ret: return from subroutine
536 		get_dcammu().load<u32>(m_ssw, m_r[R2], [this](u32 v) {
537 			m_pc = v;
538 			m_r[R2] += 4;
539 		});
540 		// TRAPS: C,U,A,P,R
541 		break;
542 	case 0x14:
543 		// pushw: push word
544 		get_dcammu().store<u32>(m_ssw, m_r[R1] - 4, m_r[R2]);
545 		m_r[R1] -= 4;
546 		// TRAPS: A,P,W
547 		break;
548 
549 	case 0x16:
550 		// popw: pop word
551 		get_dcammu().load<u32>(m_ssw, m_r[R1], [this](u32 v) {
552 			m_r[R1] += 4;
553 			m_r[R2] = v;
554 		});
555 		// TRAPS: C,U,A,P,R
556 		break;
557 
558 	case 0x20:
559 		// adds: add single floating
560 		set_fp(R2, f32_add(get_fp32(R2), get_fp32(R1)), F_IVUX);
561 		// TRAPS: F_IVUX
562 		break;
563 	case 0x21:
564 		// subs: subtract single floating
565 		set_fp(R2, f32_sub(get_fp32(R2), get_fp32(R1)), F_IVUX);
566 		// TRAPS: F_IVUX
567 		break;
568 	case 0x22:
569 		// addd: add double floating
570 		set_fp(R2, f64_add(get_fp64(R2), get_fp64(R1)), F_IVUX);
571 		// TRAPS: F_IVUX
572 		break;
573 	case 0x23:
574 		// subd: subtract double floating
575 		set_fp(R2, f64_sub(get_fp64(R2), get_fp64(R1)), F_IVUX);
576 		// TRAPS: F_IVUX
577 		break;
578 	case 0x24:
579 		// movs: move single floating
580 		set_fp(R2, get_fp32(R1), F_NONE);
581 		break;
582 	case 0x25:
583 		// cmps: compare single floating
584 		FLAGS(0, 0, f32_eq(get_fp32(R2), get_fp32(R1)), f32_lt(get_fp32(R2), get_fp32(R1)));
585 		// flag unordered
586 		if (softfloat_exceptionFlags & softfloat_flag_invalid)
587 			m_psw |= PSW_Z | PSW_N;
588 		softfloat_exceptionFlags &= F_NONE;
589 		break;
590 	case 0x26:
591 		// movd: move double floating
592 		set_fp(R2, get_fp64(R1), F_NONE);
593 		break;
594 	case 0x27:
595 		// cmpd: compare double floating
596 		FLAGS(0, 0, f64_eq(get_fp64(R2), get_fp64(R1)), f64_lt(get_fp64(R2), get_fp64(R1)));
597 		// flag unordered
598 		if (softfloat_exceptionFlags & softfloat_flag_invalid)
599 			m_psw |= PSW_Z | PSW_N;
600 		softfloat_exceptionFlags &= F_NONE;
601 		break;
602 	case 0x28:
603 		// muls: multiply single floating
604 		set_fp(R2, f32_mul(get_fp32(R2), get_fp32(R1)), F_IVUX);
605 		// TRAPS: F_IVUX
606 		break;
607 	case 0x29:
608 		// divs: divide single floating
609 		set_fp(R2, f32_div(get_fp32(R2), get_fp32(R1)), F_IVDUX);
610 		// TRAPS: F_IVDUX
611 		break;
612 	case 0x2a:
613 		// muld: multiply double floating
614 		set_fp(R2, f64_mul(get_fp64(R2), get_fp64(R1)), F_IVUX);
615 		// TRAPS: F_IVUX
616 		break;
617 	case 0x2b:
618 		// divd: divide double floating
619 		set_fp(R2, f64_div(get_fp64(R2), get_fp64(R1)), F_IVDUX);
620 		// TRAPS: F_IVDUX
621 		break;
622 	case 0x2c:
623 		// movsw: move single floating to word
624 		m_r[R2] = get_fp32(R1).v;
625 		break;
626 	case 0x2d:
627 		// movws: move word to single floating
628 		set_fp(R2, float32_t{ m_r[R1] }, F_NONE);
629 		break;
630 	case 0x2e:
631 		// movdl: move double floating to longword
632 		set_64(R2, get_fp64(R1).v);
633 		break;
634 	case 0x2f:
635 		// movld: move longword to double floating
636 		set_fp(R2, float64_t{ get_64(R1) }, F_NONE);
637 		break;
638 	case 0x30:
639 		// shaw: shift arithmetic word
640 		if (!BIT31(m_r[R1]))
641 		{
642 			// save the bits that will be shifted out plus new sign bit
643 			const s32 v = s32(m_r[R2]) >> (31 - m_r[R1]);
644 
645 			m_r[R2] <<= m_r[R1];
646 
647 			// overflow is set if sign changes during shift
648 			FLAGS(0, v != 0 && v != -1, m_r[R2] == 0, BIT31(m_r[R2]));
649 		}
650 		else
651 		{
652 			m_r[R2] = s32(m_r[R2]) >> -m_r[R1];
653 			FLAGS(0, 0, m_r[R2] == 0, BIT31(m_r[R2]));
654 		}
655 		// FLAGS: 0VZN
656 		break;
657 	case 0x31:
658 		// shal: shift arithmetic longword
659 		if (!BIT31(m_r[R1]))
660 		{
661 			// save the bits that will be shifted out plus new sign bit
662 			const s64 v = s64(get_64(R2)) >> (63 - m_r[R1]);
663 
664 			set_64(R2, get_64(R2) << m_r[R1]);
665 
666 			// overflow is set if sign changes during shift
667 			FLAGS(0, v != 0 && v != -1, get_64(R2) == 0, BIT63(get_64(R2)));
668 		}
669 		else
670 		{
671 			set_64(R2, s64(get_64(R2)) >> -m_r[R1]);
672 			FLAGS(0, 0, get_64(R2) == 0, BIT63(get_64(R2)));
673 		}
674 		// FLAGS: 0VZN
675 		break;
676 	case 0x32:
677 		// shlw: shift logical word
678 		if (!BIT31(m_r[R1]))
679 			m_r[R2] <<= m_r[R1];
680 		else
681 			m_r[R2] >>= -m_r[R1];
682 		// FLAGS: 00ZN
683 		FLAGS(0, 0, m_r[R2] == 0, BIT31(m_r[R2]));
684 		break;
685 	case 0x33:
686 		// shll: shift logical longword
687 		if (!BIT31(m_r[R1]))
688 			set_64(R2, get_64(R2) << m_r[R1]);
689 		else
690 			set_64(R2, get_64(R2) >> -m_r[R1]);
691 		// FLAGS: 00ZN
692 		FLAGS(0, 0, get_64(R2) == 0, BIT63(get_64(R2)));
693 		break;
694 	case 0x34:
695 		// rotw: rotate word
696 		if (!BIT31(m_r[R1]))
697 			m_r[R2] = rotl32(m_r[R2], m_r[R1]);
698 		else
699 			m_r[R2] = rotr32(m_r[R2], -m_r[R1]);
700 		// FLAGS: 00ZN
701 		FLAGS(0, 0, m_r[R2] == 0, BIT31(m_r[R2]));
702 		break;
703 	case 0x35:
704 		// rotl: rotate longword
705 		if (!BIT31(m_r[R1]))
706 			set_64(R2, rotl64(get_64(R2), m_r[R1]));
707 		else
708 			set_64(R2, rotr64(get_64(R2), -m_r[R1]));
709 		// FLAGS: 00ZN
710 		FLAGS(0, 0, get_64(R2) == 0, BIT63(get_64(R2)));
711 		break;
712 
713 	case 0x38:
714 		// shai: shift arithmetic immediate
715 		if (!BIT31(m_info.imm))
716 		{
717 			// save the bits that will be shifted out plus new sign bit
718 			const s32 v = s32(m_r[R2]) >> (31 - m_info.imm);
719 
720 			m_r[R2] <<= m_info.imm;
721 
722 			// overflow is set if sign changes during shift
723 			FLAGS(0, v != 0 && v != -1, m_r[R2] == 0, BIT31(m_r[R2]));
724 		}
725 		else
726 		{
727 			m_r[R2] = s32(m_r[R2]) >> -m_info.imm;
728 			FLAGS(0, 0, m_r[R2] == 0, BIT31(m_r[R2]));
729 		}
730 		// FLAGS: 0VZN
731 		// TRAPS: I
732 		break;
733 	case 0x39:
734 		// shali: shift arithmetic longword immediate
735 		if (!BIT31(m_info.imm))
736 		{
737 			// save the bits that will be shifted out plus new sign bit
738 			const s64 v = s64(get_64(R2)) >> (63 - m_info.imm);
739 
740 			set_64(R2, get_64(R2) << m_info.imm);
741 
742 			// overflow is set if sign changes during shift
743 			FLAGS(0, v != 0 && v != -1, get_64(R2) == 0, BIT63(get_64(R2)));
744 		}
745 		else
746 		{
747 			set_64(R2, s64(get_64(R2)) >> -m_info.imm);
748 			FLAGS(0, 0, get_64(R2) == 0, BIT63(get_64(R2)));
749 		}
750 		// FLAGS: 0VZN
751 		// TRAPS: I
752 		break;
753 	case 0x3a:
754 		// shli: shift logical immediate
755 		if (!BIT31(m_info.imm))
756 			m_r[R2] <<= m_info.imm;
757 		else
758 			m_r[R2] >>= -m_info.imm;
759 		FLAGS(0, 0, m_r[R2] == 0, BIT31(m_r[R2]));
760 		// FLAGS: 00ZN
761 		// TRAPS: I
762 		break;
763 	case 0x3b:
764 		// shlli: shift logical longword immediate
765 		if (!BIT31(m_info.imm))
766 			set_64(R2, get_64(R2) << m_info.imm);
767 		else
768 			set_64(R2, get_64(R2) >> -m_info.imm);
769 		FLAGS(0, 0, get_64(R2) == 0, BIT63(get_64(R2)));
770 		// FLAGS: 00ZN
771 		// TRAPS: I
772 		break;
773 	case 0x3c:
774 		// roti: rotate immediate
775 		if (!BIT31(m_info.imm))
776 			m_r[R2] = rotl32(m_r[R2], m_info.imm);
777 		else
778 			m_r[R2] = rotr32(m_r[R2], -m_info.imm);
779 		FLAGS(0, 0, m_r[R2] == 0, BIT31(m_r[R2]));
780 		// FLAGS: 00ZN
781 		// TRAPS: I
782 		break;
783 	case 0x3d:
784 		// rotli: rotate longword immediate
785 		if (!BIT31(m_info.imm))
786 			set_64(R2, rotl64(get_64(R2), m_info.imm));
787 		else
788 			set_64(R2, rotr64(get_64(R2), -m_info.imm));
789 		FLAGS(0, 0, get_64(R2) == 0, BIT63(get_64(R2)));
790 		// FLAGS: 00ZN
791 		// TRAPS: I
792 		break;
793 
794 	case 0x44:
795 	case 0x45:
796 		// call: call subroutine
797 		if (get_dcammu().store<u32>(m_ssw, m_r[R2] - 4, m_pc))
798 		{
799 			m_pc = m_info.address;
800 			m_r[R2] -= 4;
801 		}
802 		// TRAPS: A,P,W
803 		break;
804 
805 	case 0x48:
806 	case 0x49:
807 		// b*: branch on condition
808 		if (evaluate_branch())
809 			m_pc = m_info.address;
810 		// TRAPS: A,I
811 		break;
812 
813 	case 0x4c:
814 	case 0x4d:
815 		// bf*: branch on floating exception
816 		// FIXME: documentation is not clear, implementation is guesswork
817 		switch (R2)
818 		{
819 		case BF_ANY:
820 			// bfany: floating any exception
821 			if (m_psw & (PSW_FI | PSW_FV | PSW_FD | PSW_FU | PSW_FX))
822 				m_pc = m_info.address;
823 			break;
824 		case BF_BAD:
825 			// bfbad: floating bad result
826 			if (m_psw & (PSW_FI | PSW_FD))
827 				m_pc = m_info.address;
828 			break;
829 		default:
830 			// reserved
831 			// FIXME: not sure if this should trigger an exception?
832 			m_exception = EXCEPTION_ILLEGAL_OPERATION;
833 			break;
834 		}
835 		break;
836 
837 	case 0x60:
838 	case 0x61:
839 		// loadw: load word
840 		get_dcammu().load<u32>(m_ssw, m_info.address, [this](u32 v) { m_r[R2] = v; });
841 		// TRAPS: C,U,A,P,R,I
842 		break;
843 	case 0x62:
844 	case 0x63:
845 		// loada: load address
846 		m_r[R2] = m_info.address;
847 		// TRAPS: I
848 		break;
849 	case 0x64:
850 	case 0x65:
851 		// loads: load single floating
852 		get_dcammu().load<u32>(m_ssw, m_info.address, [this](u32 v) { set_fp(R2, float32_t{ v }, F_NONE); });
853 		// TRAPS: C,U,A,P,R,I
854 		break;
855 	case 0x66:
856 	case 0x67:
857 		// loadd: load double floating
858 		get_dcammu().load<u64>(m_ssw, m_info.address, [this](u64 v) { set_fp(R2, float64_t{ v }, F_NONE); });
859 		// TRAPS: C,U,A,P,R,I
860 		break;
861 	case 0x68:
862 	case 0x69:
863 		// loadb: load byte
864 		get_dcammu().load<s8>(m_ssw, m_info.address, [this](s32 v) { m_r[R2] = v; });
865 		// TRAPS: C,U,A,P,R,I
866 		break;
867 	case 0x6a:
868 	case 0x6b:
869 		// loadbu: load byte unsigned
870 		get_dcammu().load<u8>(m_ssw, m_info.address, [this](u32 v) { m_r[R2] = v; });
871 		// TRAPS: C,U,A,P,R,I
872 		break;
873 	case 0x6c:
874 	case 0x6d:
875 		// loadh: load halfword
876 		get_dcammu().load<s16>(m_ssw, m_info.address, [this](s32 v) { m_r[R2] = v; });
877 		// TRAPS: C,U,A,P,R,I
878 		break;
879 	case 0x6e:
880 	case 0x6f:
881 		// loadhu: load halfword unsigned
882 		get_dcammu().load<u16>(m_ssw, m_info.address, [this](u32 v) { m_r[R2] = v; });
883 		// TRAPS: C,U,A,P,R,I
884 		break;
885 	case 0x70:
886 	case 0x71:
887 		// storw: store word
888 		get_dcammu().store<u32>(m_ssw, m_info.address, m_r[R2]);
889 		// TRAPS: A,P,W,I
890 		break;
891 	case 0x72:
892 	case 0x73:
893 		// tsts: test and set
894 		get_dcammu().modify<u32>(m_ssw, m_info.address, [this](u32 v) {
895 			m_r[R2] = v;
896 			return v | 0x80000000U;
897 		});
898 		// TRAPS: C,U,A,P,R,W,I
899 		break;
900 	case 0x74:
901 	case 0x75:
902 		// stors: store single floating
903 		get_dcammu().store<u32>(m_ssw, m_info.address, get_fp32(R2).v);
904 		// TRAPS: A,P,W,I
905 		break;
906 	case 0x76:
907 	case 0x77:
908 		// stord: store double floating
909 		get_dcammu().store<u64>(m_ssw, m_info.address, get_fp64(R2).v);
910 		// TRAPS: A,P,W,I
911 		break;
912 	case 0x78:
913 	case 0x79:
914 		// storb: store byte
915 		get_dcammu().store<u8>(m_ssw, m_info.address, m_r[R2]);
916 		// TRAPS: A,P,W,I
917 		break;
918 
919 	case 0x7c:
920 	case 0x7d:
921 		// storh: store halfword
922 		get_dcammu().store<u16>(m_ssw, m_info.address, m_r[R2]);
923 		// TRAPS: A,P,W,I
924 		break;
925 
926 	case 0x80:
927 		// addw: add word
928 		{
929 			const u32 result = m_r[R2] + m_r[R1];
930 
931 			FLAGS_ADD(m_r[R2], m_r[R1], result);
932 
933 			m_r[R2] = result;
934 		}
935 		// FLAGS: CVZN
936 		break;
937 
938 	case 0x82:
939 		// addq: add quick
940 		{
941 			const u32 result = m_r[R2] + m_info.r1;
942 
943 			FLAGS_ADD(m_r[R2], m_info.r1, result);
944 
945 			m_r[R2] = result;
946 		}
947 		// FLAGS: CVZN
948 		break;
949 	case 0x83:
950 		// addi: add immediate
951 		{
952 			const u32 result = m_r[R2] + m_info.imm;
953 
954 			FLAGS_ADD(m_r[R2], m_info.imm, result);
955 
956 			m_r[R2] = result;
957 		}
958 		// FLAGS: CVZN
959 		// TRAPS: I
960 		break;
961 	case 0x84:
962 		// movw: move word
963 		m_r[R2] = m_r[R1];
964 		FLAGS(0, 0, m_r[R2] == 0, BIT31(m_r[R2]));
965 		// FLAGS: 00ZN
966 		break;
967 
968 	case 0x86:
969 		// loadq: load quick
970 		m_r[R2] = m_info.r1;
971 		FLAGS(0, 0, m_r[R2] == 0, 0);
972 		// FLAGS: 00Z0
973 		break;
974 	case 0x87:
975 		// loadi: load immediate
976 		m_r[R2] = m_info.imm;
977 		FLAGS(0, 0, m_r[R2] == 0, BIT31(m_r[R2]));
978 		// FLAGS: 00ZN
979 		// TRAPS: I
980 		break;
981 	case 0x88:
982 		// andw: and word
983 		m_r[R2] &= m_r[R1];
984 		FLAGS(0, 0, m_r[R2] == 0, BIT31(m_r[R2]));
985 		// FLAGS: 00ZN
986 		break;
987 
988 	case 0x8b:
989 		// andi: and immediate
990 		m_r[R2] &= m_info.imm;
991 		FLAGS(0, 0, m_r[R2] == 0, BIT31(m_r[R2]));
992 		// FLAGS: 00ZN
993 		// TRAPS: I
994 		break;
995 	case 0x8c:
996 		// orw: or word
997 		m_r[R2] |= m_r[R1];
998 		FLAGS(0, 0, m_r[R2] == 0, BIT31(m_r[R2]));
999 		// FLAGS: 00ZN
1000 		break;
1001 
1002 	case 0x8f:
1003 		// ori: or immediate
1004 		m_r[R2] |= m_info.imm;
1005 		FLAGS(0, 0, m_r[R2] == 0, BIT31(m_r[R2]));
1006 		// FLAGS: 00ZN
1007 		// TRAPS: I
1008 		break;
1009 	case 0x90:
1010 		// addwc: add word with carry
1011 		{
1012 			const u32 result = m_r[R2] + m_r[R1] + (PSW(C) ? 1 : 0);
1013 
1014 			FLAGS_ADD(m_r[R2], m_r[R1], result);
1015 
1016 			m_r[R2] = result;
1017 		}
1018 		// FLAGS: CVZN
1019 		break;
1020 	case 0x91:
1021 		// subwc: subtract word with carry
1022 		{
1023 			const u32 result = m_r[R2] - m_r[R1] - (PSW(C) ? 1 : 0);
1024 
1025 			FLAGS_SUB(m_r[R2], m_r[R1], result);
1026 
1027 			m_r[R2] = result;
1028 		}
1029 		// FLAGS: CVZN
1030 		break;
1031 
1032 	case 0x93:
1033 		// negw: negate word
1034 		{
1035 			const u32 result = -m_r[R1];
1036 
1037 			FLAGS(
1038 				m_r[R1] != 0,
1039 				s32(m_r[R1]) == INT32_MIN,
1040 				result == 0,
1041 				BIT31(result));
1042 
1043 			m_r[R2] = result;
1044 		}
1045 		// FLAGS: CVZN
1046 		break;
1047 
1048 	case 0x98:
1049 		// mulw: multiply word
1050 		{
1051 			const s64 product = mul_32x32(m_r[R1], m_r[R2]);
1052 			m_r[R2] = s32(product);
1053 			FLAGS(0, (u64(product) >> 32) != (BIT31(product) ? ~u32(0) : 0), 0, 0);
1054 			// FLAGS: 0V00
1055 		}
1056 		break;
1057 	case 0x99:
1058 		// mulwx: multiply word extended
1059 		{
1060 			const s64 product = mul_32x32(m_r[R1], m_r[R2]);
1061 			set_64(R2, product);
1062 			FLAGS(0, (u64(product) >> 32) != (BIT31(product) ? ~u32(0) : 0), 0, 0);
1063 			// FLAGS: 0V00
1064 		}
1065 		break;
1066 	case 0x9a:
1067 		// mulwu: multiply word unsigned
1068 		{
1069 			const u64 product = mulu_32x32(m_r[R1], m_r[R2]);
1070 			m_r[R2] = u32(product);
1071 			FLAGS(0, (product >> 32) != 0, 0, 0);
1072 			// FLAGS: 0V00
1073 		}
1074 		break;
1075 	case 0x9b:
1076 		// mulwux: multiply word unsigned extended
1077 		{
1078 			const u64 product = mulu_32x32(m_r[R1], m_r[R2]);
1079 			set_64(R2, product);
1080 			FLAGS(0, (product >> 32) != 0, 0, 0);
1081 			// FLAGS: 0V00
1082 		}
1083 		break;
1084 	case 0x9c:
1085 		// divw: divide word
1086 		if (m_r[R1] != 0)
1087 		{
1088 			// FLAGS: 0V00
1089 			FLAGS(0, s32(m_r[R2]) == INT32_MIN && s32(m_r[R1]) == -1, 0, 0);
1090 			m_r[R2] = s32(m_r[R2]) / s32(m_r[R1]);
1091 		}
1092 		else
1093 			// TRAPS: D
1094 			m_exception = EXCEPTION_INTEGER_DIVIDE_BY_ZERO;
1095 		break;
1096 	case 0x9d:
1097 		// modw: modulus word
1098 		if (m_r[R1] != 0)
1099 		{
1100 			// FLAGS: 0V00
1101 			FLAGS(0, s32(m_r[R2]) == INT32_MIN && s32(m_r[R1]) == -1, 0, 0);
1102 			m_r[R2] = s32(m_r[R2]) % s32(m_r[R1]);
1103 		}
1104 		else
1105 			// TRAPS: D
1106 			m_exception = EXCEPTION_INTEGER_DIVIDE_BY_ZERO;
1107 		break;
1108 	case 0x9e:
1109 		// divwu: divide word unsigned
1110 		if (m_r[R1] != 0)
1111 		{
1112 			m_r[R2] = m_r[R2] / m_r[R1];
1113 			// FLAGS: 0000
1114 			FLAGS(0, 0, 0, 0);
1115 		}
1116 		else
1117 			// TRAPS: D
1118 			m_exception = EXCEPTION_INTEGER_DIVIDE_BY_ZERO;
1119 		break;
1120 	case 0x9f:
1121 		// modwu: modulus word unsigned
1122 		if (m_r[R1] != 0)
1123 		{
1124 			m_r[R2] = m_r[R2] % m_r[R1];
1125 			// FLAGS: 0000
1126 			FLAGS(0, 0, 0, 0);
1127 		}
1128 		else
1129 			// TRAPS: D
1130 			m_exception = EXCEPTION_INTEGER_DIVIDE_BY_ZERO;
1131 		break;
1132 	case 0xa0:
1133 		// subw: subtract word
1134 		{
1135 			const u32 result = m_r[R2] - m_r[R1];
1136 
1137 			FLAGS_SUB(m_r[R2], m_r[R1], result);
1138 
1139 			m_r[R2] = result;
1140 		}
1141 		// FLAGS: CVZN
1142 		break;
1143 
1144 	case 0xa2:
1145 		// subq: subtract quick
1146 		{
1147 			const u32 result = m_r[R2] - m_info.r1;
1148 
1149 			FLAGS_SUB(m_r[R2], m_info.r1, result);
1150 
1151 			m_r[R2] = result;
1152 		}
1153 		// FLAGS: CVZN
1154 		break;
1155 	case 0xa3:
1156 		// subi: subtract immediate
1157 		{
1158 			const u32 result = m_r[R2] - m_info.imm;
1159 
1160 			FLAGS_SUB(m_r[R2], m_info.imm, result);
1161 
1162 			m_r[R2] = result;
1163 		}
1164 		// FLAGS: CVZN
1165 		// TRAPS: I
1166 		break;
1167 	case 0xa4:
1168 		// cmpw: compare word
1169 		{
1170 			const u32 result = m_r[R2] - m_r[R1];
1171 
1172 			FLAGS_SUB(m_r[R2], m_r[R1], result);
1173 		}
1174 		// FLAGS: CVZN
1175 		break;
1176 
1177 	case 0xa6:
1178 		// cmpq: compare quick
1179 		{
1180 			const u32 result = m_r[R2] - m_info.r1;
1181 
1182 			FLAGS_SUB(m_r[R2], m_info.r1, result);
1183 		}
1184 		// FLAGS: CVZN
1185 		break;
1186 	case 0xa7:
1187 		// cmpi: compare immediate
1188 		{
1189 			const u32 result = m_r[R2] - m_info.imm;
1190 
1191 			FLAGS_SUB(m_r[R2], m_info.imm, result);
1192 		}
1193 		// FLAGS: CVZN
1194 		// TRAPS: I
1195 		break;
1196 	case 0xa8:
1197 		// xorw: exclusive or word
1198 		m_r[R2] ^= m_r[R1];
1199 		FLAGS(0, 0, m_r[R2] == 0, BIT31(m_r[R2]));
1200 		// FLAGS: 00ZN
1201 		break;
1202 
1203 	case 0xab:
1204 		// xori: exclusive or immediate
1205 		m_r[R2] ^= m_info.imm;
1206 		FLAGS(0, 0, m_r[R2] == 0, BIT31(m_r[R2]));
1207 		// FLAGS: 00ZN
1208 		// TRAPS: I
1209 		break;
1210 	case 0xac:
1211 		// notw: not word
1212 		m_r[R2] = ~m_r[R1];
1213 		FLAGS(0, 0, m_r[R2] == 0, BIT31(m_r[R2]));
1214 		// FLAGS: 00ZN
1215 		break;
1216 
1217 	case 0xae:
1218 		// notq: not quick
1219 		m_r[R2] = ~R1;
1220 		FLAGS(0, 0, 0, 1);
1221 		// FLAGS: 0001
1222 		break;
1223 
1224 	case 0xb4:
1225 		// unprivileged macro instructions
1226 		switch (m_info.subopcode)
1227 		{
1228 		case 0x00: case 0x01: case 0x02: case 0x03:
1229 		case 0x04: case 0x05: case 0x06: case 0x07:
1230 		case 0x08: case 0x09: case 0x0a: case 0x0b:
1231 		case 0x0c:
1232 			// savew0..savew12: push registers rN:r14
1233 
1234 			// store ri at sp - 4 * (15 - i)
1235 			for (int i = R2; i < 15 && !m_exception; i++)
1236 				get_dcammu().store<u32>(m_ssw, m_r[15] - 4 * (15 - i), m_r[i]);
1237 
1238 			// decrement sp after push to allow restart on exceptions
1239 			if (!m_exception)
1240 				m_r[15] -= 4 * (15 - R2);
1241 			// TRAPS: A,P,W
1242 			break;
1243 		// NOTE: the movc, initc and cmpc macro instructions are implemented in a very basic way because
1244 		// at some point they will need to be improved to deal with possible exceptions (e.g. page faults)
1245 		// that may occur during execution. The implementation here is intended to allow the instructions
1246 		// to be "continued" after such exceptions.
1247 		case 0x0d:
1248 			// movc: copy r0 bytes from r1 to r2
1249 
1250 			while (m_r[0])
1251 			{
1252 				get_dcammu().load<u8>(m_ssw, m_r[1], [this](u8 byte) { get_dcammu().store<u8>(m_ssw, m_r[2], byte); });
1253 
1254 				if (m_exception)
1255 					break;
1256 
1257 				m_r[0]--;
1258 				m_r[1]++;
1259 				m_r[2]++;
1260 			}
1261 			// TRAPS: C,U,P,R,W
1262 			break;
1263 		case 0x0e:
1264 			// initc: initialise r0 bytes at r1 with value in r2
1265 			while (m_r[0])
1266 			{
1267 				if (!get_dcammu().store<u8>(m_ssw, m_r[1], m_r[2]))
1268 					break;
1269 
1270 				m_r[0]--;
1271 				m_r[1]++;
1272 				m_r[2] = rotr32(m_r[2], 8);
1273 			}
1274 			// TRAPS: P,W
1275 			break;
1276 		case 0x0f:
1277 			// cmpc: compare r0 bytes at r1 with r2
1278 
1279 			// set condition codes assuming strings match
1280 			FLAGS(0, 0, 1, 0);
1281 
1282 			while (m_r[0])
1283 			{
1284 				// read and compare bytes (as signed 32 bit integers)
1285 				get_dcammu().load<s8>(m_ssw, m_r[1], [this](s32 byte1)
1286 				{
1287 					get_dcammu().load<s8>(m_ssw, m_r[2], [this, byte1](s32 byte2)
1288 					{
1289 						const s32 result = byte2 - byte1;
1290 
1291 						FLAGS_SUB(byte2, byte1, result);
1292 					});
1293 				});
1294 
1295 				// abort on exception or mismatch
1296 				if (m_exception || !PSW(Z))
1297 					break;
1298 
1299 				m_r[0]--;
1300 				m_r[1]++;
1301 				m_r[2]++;
1302 			}
1303 			// TRAPS: C,U,P,R
1304 			break;
1305 		case 0x10: case 0x11: case 0x12: case 0x13:
1306 		case 0x14: case 0x15: case 0x16: case 0x17:
1307 		case 0x18: case 0x19: case 0x1a: case 0x1b:
1308 		case 0x1c:
1309 			// restwN..restw12: pop registers rN:r14
1310 
1311 			// load ri from sp + 4 * (i - N)
1312 			for (int i = R2; i < 15 && !m_exception; i++)
1313 				get_dcammu().load<u32>(m_ssw, m_r[15] + 4 * (i - R2), [this, i](u32 v) { m_r[i] = v; });
1314 
1315 			// increment sp after pop to allow restart on exceptions
1316 			if (!m_exception)
1317 				m_r[15] += 4 * (15 - R2);
1318 			// TRAPS: C,U,A,P,R
1319 			break;
1320 
1321 		case 0x20: case 0x21: case 0x22: case 0x23:
1322 		case 0x24: case 0x25: case 0x26: case 0x27:
1323 			// saved0..saved7: push registers fN:f7
1324 
1325 			// store fi at sp - 8 * (8 - i)
1326 			for (int i = m_info.subopcode & 0x7; i < 8 && !m_exception; i++)
1327 				get_dcammu().store<u64>(m_ssw, m_r[15] - 8 * (8 - i), get_fp64(i).v);
1328 
1329 			// decrement sp after push to allow restart on exceptions
1330 			if (!m_exception)
1331 				m_r[15] -= 8 * (8 - (m_info.subopcode & 0x7));
1332 			// TRAPS: A,P,W
1333 			break;
1334 		case 0x28: case 0x29: case 0x2a: case 0x2b:
1335 		case 0x2c: case 0x2d: case 0x2e: case 0x2f:
1336 			// restd0..restd7: pop registers fN:f7
1337 
1338 			// load fi from sp + 8 * (i - N)
1339 			for (int i = m_info.subopcode & 0x7; i < 8 && !m_exception; i++)
1340 				get_dcammu().load<u64>(m_ssw, m_r[15] + 8 * (i - (m_info.subopcode & 0x7)), [this, i](u64 v) { set_fp(i, float64_t{ v }, F_NONE); });
1341 
1342 			// increment sp after pop to allow restart on exceptions
1343 			if (!m_exception)
1344 				m_r[15] += 8 * (8 - (m_info.subopcode & 0x7));
1345 			// TRAPS: C,U,A,P,R
1346 			break;
1347 		case 0x30:
1348 			// cnvsw: convert single floating to word
1349 			m_fp_pc = m_info.pc;
1350 
1351 			m_r[m_info.macro & 0xf] = f32_to_i32(get_fp32((m_info.macro >> 4) & 0xf), softfloat_roundingMode, true);
1352 			// TRAPS: F_IX
1353 			softfloat_exceptionFlags &= F_IX;
1354 			break;
1355 		case 0x31:
1356 			// cnvrsw: convert rounding single floating to word (non-IEEE +0.5/-0.5 rounding)
1357 			m_fp_pc = m_info.pc;
1358 
1359 			if (f32_lt(get_fp32((m_info.macro >> 4) & 0xf), float32_t{ 0 }))
1360 				m_r[m_info.macro & 0xf] = f32_to_i32(f32_sub(get_fp32((m_info.macro >> 4) & 0xf),
1361 					f32_div(i32_to_f32(1), i32_to_f32(2))), softfloat_round_minMag, true);
1362 			else
1363 				m_r[m_info.macro & 0xf] = f32_to_i32(f32_add(get_fp32((m_info.macro >> 4) & 0xf),
1364 					f32_div(i32_to_f32(1), i32_to_f32(2))), softfloat_round_minMag, true);
1365 			// TRAPS: F_IX
1366 			softfloat_exceptionFlags &= F_IX;
1367 			break;
1368 		case 0x32:
1369 			// cnvtsw: convert truncating single floating to word
1370 			m_fp_pc = m_info.pc;
1371 
1372 			m_r[m_info.macro & 0xf] = f32_to_i32(get_fp32((m_info.macro >> 4) & 0xf), softfloat_round_minMag, true);
1373 			// TRAPS: F_IX
1374 			softfloat_exceptionFlags &= F_IX;
1375 			break;
1376 		case 0x33:
1377 			// cnvws: convert word to single floating
1378 			set_fp(m_info.macro & 0xf, i32_to_f32(m_r[(m_info.macro >> 4) & 0xf]), F_X);
1379 			// TRAPS: F_X
1380 			break;
1381 		case 0x34:
1382 			// cnvdw: convert double floating to word
1383 			m_fp_pc = m_info.pc;
1384 
1385 			m_r[m_info.macro & 0xf] = f64_to_i32(get_fp64((m_info.macro >> 4) & 0xf), softfloat_roundingMode, true);
1386 			// TRAPS: F_IX
1387 			softfloat_exceptionFlags &= F_IX;
1388 			break;
1389 		case 0x35:
1390 			// cnvrdw: convert rounding double floating to word (non-IEEE +0.5/-0.5 rounding)
1391 			m_fp_pc = m_info.pc;
1392 
1393 			if (f64_lt(get_fp64((m_info.macro >> 4) & 0xf), float64_t{ 0 }))
1394 				m_r[m_info.macro & 0xf] = f64_to_i32(f64_sub(get_fp64((m_info.macro >> 4) & 0xf),
1395 					f64_div(i32_to_f64(1), i32_to_f64(2))), softfloat_round_minMag, true);
1396 			else
1397 				m_r[m_info.macro & 0xf] = f64_to_i32(f64_add(get_fp64((m_info.macro >> 4) & 0xf),
1398 					f64_div(i32_to_f64(1), i32_to_f64(2))), softfloat_round_minMag, true);
1399 			// TRAPS: F_IX
1400 			softfloat_exceptionFlags &= F_IX;
1401 			break;
1402 		case 0x36:
1403 			// cnvtdw: convert truncating double floating to word
1404 			m_fp_pc = m_info.pc;
1405 
1406 			m_r[m_info.macro & 0xf] = f64_to_i32(get_fp64((m_info.macro >> 4) & 0xf), softfloat_round_minMag, true);
1407 			// TRAPS: F_IX
1408 			softfloat_exceptionFlags &= F_IX;
1409 			break;
1410 		case 0x37:
1411 			// cnvwd: convert word to double floating
1412 			set_fp(m_info.macro & 0xf, i32_to_f64(m_r[(m_info.macro >> 4) & 0xf]), F_NONE);
1413 			break;
1414 		case 0x38:
1415 			// cnvsd: convert single to double floating
1416 			set_fp(m_info.macro & 0xf, f32_to_f64(get_fp32((m_info.macro >> 4) & 0xf)), F_I);
1417 			// TRAPS: F_I
1418 			break;
1419 		case 0x39:
1420 			// cnvds: convert double to single floating
1421 			set_fp(m_info.macro & 0xf, f64_to_f32(get_fp64((m_info.macro >> 4) & 0xf)), F_IVUX);
1422 			// TRAPS: F_IVUX
1423 			break;
1424 		case 0x3a:
1425 			// negs: negate single floating
1426 			set_fp(m_info.macro & 0xf, f32_mul(get_fp32((m_info.macro >> 4) & 0xf), i32_to_f32(-1)), F_NONE);
1427 			break;
1428 		case 0x3b:
1429 			// negd: negate double floating
1430 			set_fp(m_info.macro & 0xf, f64_mul(get_fp64((m_info.macro >> 4) & 0xf), i32_to_f64(-1)), F_NONE);
1431 			break;
1432 		case 0x3c:
1433 			/*
1434 			 * This implementation for scalbd and scalbs is a bit opaque, but
1435 			 * essentially we check if the integer value is within range, and
1436 			 * directly create a floating constant representing 2^n or NaN
1437 			 * respectively, which is used as an input to a multiply, producing
1438 			 * the desired result. While doing an actual multiply is both
1439 			 * inefficient and unnecessary, it's a tidy way to ensure the
1440 			 * correct exception flags are set.
1441 			 */
1442 			// scalbs: scale by, single floating
1443 			set_fp(m_info.macro & 0xf, f32_mul(get_fp32(m_info.macro & 0xf),
1444 				((s32(m_r[(m_info.macro >> 4) & 0xf]) > -127 && s32(m_r[(m_info.macro >> 4) & 0xf]) < 128)
1445 					? float32_t{ u32(s32(m_r[(m_info.macro >> 4) & 0xf]) + 127) << 23 }
1446 					: float32_t{ ~u32(0) })), F_IVUX);
1447 			// TRAPS: F_IVUX
1448 			break;
1449 		case 0x3d:
1450 			// scalbd: scale by, double floating
1451 			set_fp(m_info.macro & 0xf, f64_mul(get_fp64(m_info.macro & 0xf),
1452 				(s32(m_r[(m_info.macro >> 4) & 0xf]) > -1023 && s32(m_r[(m_info.macro >> 4) & 0xf]) < 1024)
1453 					? float64_t{ u64(s32(m_r[(m_info.macro >> 4) & 0xf]) + 1023) << 52 }
1454 					: float64_t{ ~u64(0) }), F_IVUX);
1455 			// TRAPS: F_IVUX
1456 			break;
1457 		case 0x3e:
1458 			// trapfn: trap floating unordered
1459 			// TRAPS: I
1460 			if (PSW(Z) && PSW(N))
1461 				m_exception = EXCEPTION_ILLEGAL_OPERATION;
1462 			break;
1463 		case 0x3f:
1464 			// loadfs: load floating status
1465 			m_r[(m_info.macro >> 4) & 0xf] = m_fp_pc;
1466 			m_f[m_info.macro & 0xf] = m_fp_dst;
1467 			m_ssw |= SSW_FRD;
1468 			break;
1469 
1470 		default:
1471 			m_exception = EXCEPTION_ILLEGAL_OPERATION;
1472 			break;
1473 		}
1474 		break;
1475 
1476 	case 0xb6:
1477 		// privileged macro instructions
1478 		if (!SSW(U))
1479 		{
1480 			switch (m_info.subopcode)
1481 			{
1482 			case 0x00:
1483 				// movus: move user to supervisor
1484 				m_rs[m_info.macro & 0xf] = m_ru[(m_info.macro >> 4) & 0xf];
1485 				FLAGS(0, 0, m_rs[m_info.macro & 0xf] == 0, BIT31(m_rs[m_info.macro & 0xf]));
1486 				// FLAGS: 00ZN
1487 				// TRAPS: S
1488 				break;
1489 			case 0x01:
1490 				// movsu: move supervisor to user
1491 				m_ru[m_info.macro & 0xf] = m_rs[(m_info.macro >> 4) & 0xf];
1492 				FLAGS(0, 0, m_ru[m_info.macro & 0xf] == 0, BIT31(m_ru[m_info.macro & 0xf]));
1493 				// FLAGS: 00ZN
1494 				// TRAPS: S
1495 				break;
1496 			case 0x02:
1497 				// saveur: save user registers
1498 				for (int i = 0; i < 16 && !m_exception; i++)
1499 					get_dcammu().store<u32>(m_ssw, m_rs[(m_info.macro >> 4) & 0xf] - 4 * (i + 1), m_ru[15 - i]);
1500 
1501 				if (!m_exception)
1502 					m_rs[(m_info.macro >> 4) & 0xf] -= 64;
1503 				// TRAPS: A,P,W,S
1504 				break;
1505 			case 0x03:
1506 				// restur: restore user registers
1507 				for (int i = 0; i < 16 && !m_exception; i++)
1508 					get_dcammu().load<u32>(m_ssw, m_rs[(m_info.macro >> 4) & 0xf] + 4 * i, [this, i](u32 v) { m_ru[i] = v; });
1509 
1510 				if (!m_exception)
1511 					m_rs[(m_info.macro >> 4) & 0xf] += 64;
1512 				// TRAPS: C,U,A,P,R,S
1513 				break;
1514 			case 0x04:
1515 				// reti: restore psw, ssw and pc from supervisor stack
1516 				m_pc = reti();
1517 				// TRAPS: S
1518 				break;
1519 			case 0x05:
1520 				// wait: wait for interrupt
1521 				m_wait = true;
1522 				// TRAPS: S
1523 				break;
1524 
1525 			default:
1526 				m_exception = EXCEPTION_ILLEGAL_OPERATION;
1527 				break;
1528 			}
1529 		}
1530 		else
1531 			m_exception = EXCEPTION_PRIVILEGED_INSTRUCTION;
1532 		break;
1533 
1534 	default:
1535 		m_exception = EXCEPTION_ILLEGAL_OPERATION;
1536 		break;
1537 	}
1538 }
1539 
reti()1540 u32 clipper_device::reti()
1541 {
1542 	u32 new_psw = 0, new_ssw = 0, new_pc = 0;
1543 
1544 	// pop the psw, ssw and pc from the supervisor stack
1545 	if (!get_dcammu().load<u32>(m_ssw, m_rs[(m_info.macro >> 4) & 0xf] + 0, [&new_psw](u32 v) { new_psw = v; }))
1546 		fatalerror("reti unrecoverable fault 0x%04x pop psw address 0x%08x pc 0x%08x\n", m_exception, m_rs[(m_info.macro >> 4) & 0xf] + 0, m_info.pc);
1547 
1548 	if (!get_dcammu().load<u32>(m_ssw, m_rs[(m_info.macro >> 4) & 0xf] + 4, [&new_ssw](u32 v) { new_ssw = v; }))
1549 		fatalerror("reti unrecoverable fault 0x%04x pop ssw address 0x%08x pc 0x%08x\n", m_exception, m_rs[(m_info.macro >> 4) & 0xf] + 4, m_info.pc);
1550 
1551 	if (!get_dcammu().load<u32>(m_ssw, m_rs[(m_info.macro >> 4) & 0xf] + 8, [&new_pc](u32 v) { new_pc = v; }))
1552 		fatalerror("reti unrecoverable fault 0x%04x pop pc address 0x%08x pc 0x%08x\n", m_exception, m_rs[(m_info.macro >> 4) & 0xf] + 8, m_info.pc);
1553 
1554 	LOGMASKED(LOG_EXCEPTION, "reti r%d ssp 0x%08x pc 0x%08x ssw 0x%08x psw 0x%08x new_pc 0x%08x new_ssw 0x%08x new_psw 0x%08x\n",
1555 		(m_info.macro >> 4) & 0xf, m_rs[(m_info.macro >> 4) & 0xf], m_info.pc, m_ssw, m_psw, new_pc, new_ssw, new_psw);
1556 
1557 	// adjust the stack pointer
1558 	m_rs[(m_info.macro >> 4) & 0xf] += 12;
1559 
1560 	// restore the psw and ssw
1561 	set_psw(new_psw);
1562 	set_ssw(new_ssw);
1563 
1564 	// return the restored pc
1565 	return new_pc;
1566 }
1567 
1568 /*
1569  * Common entry point for transferring control in the event of an interrupt or
1570  * exception. Reading between the lines, it appears this logic was implemented
1571  * using the macro instruction ROM and a special macro instruction (intrap).
1572  */
intrap(const u16 vector,const u32 old_pc)1573 u32 clipper_device::intrap(const u16 vector, const u32 old_pc)
1574 {
1575 	const u32 old_ssw = m_ssw;
1576 	const u32 old_psw = m_psw;
1577 	u32 new_pc = 0, new_ssw = 0;
1578 
1579 	// clear ssw bits to enable supervisor memory access
1580 	m_ssw &= ~(SSW_U | SSW_K | SSW_UU | SSW_KU);
1581 
1582 	// clear exception state
1583 	m_exception = 0;
1584 
1585 	// fetch next pc and ssw from interrupt vector
1586 	if (!get_dcammu().load<u32>(m_ssw, vector + 0, [&new_pc](u32 v) { new_pc = v; }))
1587 		fatalerror("intrap unrecoverable fault 0x%04x load pc address 0x%08x pc 0x%08x\n", m_exception, vector + 0, old_pc);
1588 	if (!get_dcammu().load<u32>(m_ssw, vector + 4, [&new_ssw](u32 v) { new_ssw = v; }))
1589 		fatalerror("intrap unrecoverable fault 0x%04x load ssw address 0x%08x pc 0x%08x\n", m_exception, vector + 4, old_pc);
1590 
1591 	LOGMASKED(LOG_EXCEPTION, "intrap vector 0x%04x pc 0x%08x ssp 0x%08x new_pc 0x%08x new_ssw 0x%08x\n", vector, old_pc, m_rs[15], new_pc, new_ssw);
1592 
1593 	// derive cts and mts from vector
1594 	u32 source = 0;
1595 	switch (vector)
1596 	{
1597 		// data memory trap group
1598 	case EXCEPTION_D_CORRECTED_MEMORY_ERROR:
1599 	case EXCEPTION_D_UNCORRECTABLE_MEMORY_ERROR:
1600 	case EXCEPTION_D_ALIGNMENT_FAULT:
1601 	case EXCEPTION_D_PAGE_FAULT:
1602 	case EXCEPTION_D_READ_PROTECT_FAULT:
1603 	case EXCEPTION_D_WRITE_PROTECT_FAULT:
1604 
1605 		// instruction memory trap group
1606 	case EXCEPTION_I_CORRECTED_MEMORY_ERROR:
1607 	case EXCEPTION_I_UNCORRECTABLE_MEMORY_ERROR:
1608 	case EXCEPTION_I_ALIGNMENT_FAULT:
1609 	case EXCEPTION_I_PAGE_FAULT:
1610 	case EXCEPTION_I_EXECUTE_PROTECT_FAULT:
1611 		source = (vector & MTS_VMASK) << (MTS_SHIFT - MTS_VSHIFT);
1612 		break;
1613 
1614 		// integer arithmetic trap group
1615 	case EXCEPTION_INTEGER_DIVIDE_BY_ZERO:
1616 		source = CTS_DIVIDE_BY_ZERO;
1617 		break;
1618 
1619 		// illegal operation trap group
1620 	case EXCEPTION_ILLEGAL_OPERATION:
1621 		source = CTS_ILLEGAL_OPERATION;
1622 		break;
1623 	case EXCEPTION_PRIVILEGED_INSTRUCTION:
1624 		source = CTS_PRIVILEGED_INSTRUCTION;
1625 		break;
1626 
1627 		// diagnostic trap group
1628 	case EXCEPTION_TRACE:
1629 		source = CTS_TRACE_TRAP;
1630 		break;
1631 	}
1632 
1633 	// push pc, ssw and psw onto supervisor stack
1634 	if (!get_dcammu().store<u32>(m_ssw, m_rs[15] - 0x4, old_pc))
1635 		fatalerror("intrap unrecoverable fault 0x%04x push pc ssp 0x%08x pc 0x%08x\n", m_exception, m_rs[15] - 0x4, old_pc);
1636 
1637 	if (!get_dcammu().store<u32>(m_ssw, m_rs[15] - 0x8, old_ssw))
1638 		fatalerror("intrap unrecoverable fault 0x%04x push ssw ssp 0x%08x pc 0x%08x\n", m_exception, m_rs[15] - 0x8, old_pc);
1639 
1640 	if (!get_dcammu().store<u32>(m_ssw, m_rs[15] - 0xc, (old_psw & ~(PSW_CTS | PSW_MTS)) | source))
1641 		fatalerror("intrap unrecoverable fault 0x%04x push psw ssp 0x%08x pc 0x%08x\n", m_exception, m_rs[15] - 0xc, old_pc);
1642 
1643 	// decrement supervisor stack pointer
1644 	m_rs[15] -= 12;
1645 
1646 	// set ssw from vector and previous mode
1647 	set_ssw((new_ssw & ~SSW_P) | (old_ssw & SSW_U) << 1);
1648 
1649 	// clear psw
1650 	set_psw(0);
1651 
1652 	// return new pc from trap vector
1653 	return new_pc;
1654 }
1655 
intrap(const u16 vector,const u32 old_pc)1656 u32 clipper_c400_device::intrap(const u16 vector, const u32 old_pc)
1657 {
1658 	const u32 old_ssw = m_ssw;
1659 	const u32 old_psw = m_psw;
1660 	u32 new_pc = 0, new_ssw = 0;
1661 
1662 	// clear ssw bits to enable supervisor memory access
1663 	m_ssw &= ~(SSW_U | SSW_K | SSW_UU | SSW_KU);
1664 
1665 	// clear exception state
1666 	m_exception = 0;
1667 
1668 	// fetch ssw and pc from interrupt vector (C400 reversed wrt C100/C300)
1669 	if (!get_dcammu().load<u32>(m_ssw, vector + 0, [&new_ssw](u32 v) { new_ssw = v; }))
1670 		fatalerror("intrap unrecoverable fault 0x%04x load ssw address 0x%08x pc 0x%08x\n", m_exception, vector + 0, old_pc);
1671 	if (!get_dcammu().load<u32>(m_ssw, vector + 4, [&new_pc](u32 v) { new_pc = v; }))
1672 		fatalerror("intrap unrecoverable fault 0x%04x load pc address 0x%08x pc 0x%08x\n", m_exception, vector + 4, old_pc);
1673 
1674 	LOGMASKED(LOG_EXCEPTION, "intrap vector 0x%04x pc 0x%08x ssp 0x%08x new_pc 0x%08x new_ssw 0x%08x\n", vector, old_pc, m_rs[15], new_pc, new_ssw);
1675 
1676 	// derive cts and mts from vector
1677 	u32 source = 0;
1678 	switch (vector)
1679 	{
1680 		// data memory trap group
1681 	case EXCEPTION_D_CORRECTED_MEMORY_ERROR:
1682 	case EXCEPTION_D_UNCORRECTABLE_MEMORY_ERROR:
1683 	case EXCEPTION_D_ALIGNMENT_FAULT:
1684 	case EXCEPTION_D_PAGE_FAULT:
1685 	case EXCEPTION_D_READ_PROTECT_FAULT:
1686 	case EXCEPTION_D_WRITE_PROTECT_FAULT:
1687 
1688 		// instruction memory trap group
1689 	case EXCEPTION_I_CORRECTED_MEMORY_ERROR:
1690 	case EXCEPTION_I_UNCORRECTABLE_MEMORY_ERROR:
1691 	case EXCEPTION_I_ALIGNMENT_FAULT:
1692 	case EXCEPTION_I_PAGE_FAULT:
1693 	case EXCEPTION_I_EXECUTE_PROTECT_FAULT:
1694 		source = (vector & MTS_VMASK) << (MTS_SHIFT - MTS_VSHIFT);
1695 		break;
1696 
1697 		// integer arithmetic trap group
1698 	case EXCEPTION_INTEGER_DIVIDE_BY_ZERO:
1699 		source = CTS_DIVIDE_BY_ZERO;
1700 		break;
1701 
1702 		// illegal operation trap group
1703 	case EXCEPTION_ILLEGAL_OPERATION:
1704 		source = CTS_ILLEGAL_OPERATION;
1705 		break;
1706 	case EXCEPTION_PRIVILEGED_INSTRUCTION:
1707 		source = CTS_PRIVILEGED_INSTRUCTION;
1708 		break;
1709 
1710 		// diagnostic trap group
1711 	case EXCEPTION_TRACE:
1712 		source = CTS_TRACE_TRAP;
1713 		break;
1714 	}
1715 
1716 	// push pc, ssw and psw onto supervisor stack
1717 	if (!get_dcammu().store<u32>(m_ssw, m_rs[15] - 0x4, old_pc))
1718 		fatalerror("intrap unrecoverable fault 0x%04x push pc ssp 0x%08x pc 0x%08x\n", m_exception, m_rs[15] - 0x4, old_pc);
1719 
1720 	if (!get_dcammu().store<u32>(m_ssw, m_rs[15] - 0x8, old_ssw))
1721 		fatalerror("intrap unrecoverable fault 0x%04x push ssw ssp 0x%08x pc 0x%08x\n", m_exception, m_rs[15] - 0x8, old_pc);
1722 
1723 	if (!get_dcammu().store<u32>(m_ssw, m_rs[15] - 0xc, (old_psw & ~(PSW_CTS | PSW_MTS)) | source))
1724 		fatalerror("intrap unrecoverable fault 0x%04x push psw ssp 0x%08x pc 0x%08x\n", m_exception, m_rs[15] - 0xc, old_pc);
1725 
1726 	// TODO: push pc1
1727 	// TODO: push pc2
1728 
1729 	// push delayed branch pc onto supervisor stack
1730 	if (!get_dcammu().store<u32>(m_ssw, m_rs[15] - 0x18, m_db_pc))
1731 		fatalerror("intrap unrecoverable fault 0x%04x push db_pc address 0x%08x pc 0x%08x\n", m_exception, m_rs[15] - 0x18, old_pc);
1732 
1733 	// decrement supervisor stack pointer
1734 	m_rs[15] -= 24;
1735 
1736 	// set ssw from vector and previous mode
1737 	set_ssw((new_ssw & ~SSW_P) | (old_ssw & SSW_U) << 1);
1738 
1739 	// clear psw
1740 	set_psw(0);
1741 
1742 	// return new pc from trap vector
1743 	return new_pc;
1744 }
1745 
evaluate_branch() const1746 bool clipper_device::evaluate_branch() const
1747 {
1748 	switch (m_info.r2)
1749 	{
1750 	case BRANCH_T:
1751 		return true;
1752 
1753 	case BRANCH_LT:
1754 		return (!PSW(V) && !PSW(Z) && !PSW(N))
1755 			|| (PSW(V) && !PSW(Z) && PSW(N));
1756 
1757 	case BRANCH_LE:
1758 		return (!PSW(V) && !PSW(N))
1759 			|| (PSW(V) && !PSW(Z) && PSW(N));
1760 
1761 	case BRANCH_EQ:
1762 		return PSW(Z) && !PSW(N);
1763 
1764 	case BRANCH_GT:
1765 		return (!PSW(V) && !PSW(Z) && PSW(N))
1766 			|| (PSW(V) && !PSW(N));
1767 
1768 	case BRANCH_GE:
1769 		return (PSW(V) && !PSW(N))
1770 			|| (!PSW(V) && !PSW(Z) && PSW(N))
1771 			|| (PSW(Z) && !PSW(N));
1772 
1773 	case BRANCH_NE:
1774 		return (!PSW(Z))
1775 			|| (PSW(Z) && PSW(N));
1776 
1777 	case BRANCH_LTU:
1778 		return (!PSW(C) && !PSW(Z));
1779 
1780 	case BRANCH_LEU:
1781 		return !PSW(C);
1782 
1783 	case BRANCH_GTU:
1784 		return PSW(C);
1785 
1786 	case BRANCH_GEU:
1787 		return PSW(C) || PSW(Z);
1788 
1789 	case BRANCH_V:
1790 		return PSW(V);
1791 	case BRANCH_NV:
1792 		return !PSW(V);
1793 
1794 	case BRANCH_N:
1795 		return !PSW(Z) && PSW(N);
1796 	case BRANCH_NN:
1797 		return !PSW(N);
1798 
1799 	case BRANCH_FN:
1800 		return PSW(Z) && PSW(N);
1801 	}
1802 
1803 	return false;
1804 }
1805 
set_psw(const u32 psw)1806 void clipper_device::set_psw(const u32 psw)
1807 {
1808 	// retain read-only endianness field
1809 	m_psw = (m_psw & PSW_BIG) | (psw & ~PSW_BIG);
1810 
1811 	// set the softfloat rounding mode based on the psw rounding mode
1812 	switch (PSW(FR))
1813 	{
1814 	case FR_0: softfloat_roundingMode = softfloat_round_near_even; break;
1815 	case FR_1: softfloat_roundingMode = softfloat_round_max; break;
1816 	case FR_2: softfloat_roundingMode = softfloat_round_min; break;
1817 	case FR_3: softfloat_roundingMode = softfloat_round_minMag; break;
1818 	}
1819 }
1820 
set_ssw(const u32 ssw)1821 void clipper_device::set_ssw(const u32 ssw)
1822 {
1823 	// retain read-only id field
1824 	m_ssw = (m_ssw & SSW_ID) | (ssw & ~SSW_ID);
1825 
1826 	// select the register file
1827 	m_r = SSW(U) ? m_ru : m_rs;
1828 }
1829 
fp_exception()1830 void clipper_device::fp_exception()
1831 {
1832 	u16 exception = 0;
1833 
1834 	/*
1835 	 * Set the psw floating exception flags, and identify any enabled
1836 	 * exceptions. The order here is important, but since the documentation
1837 	 * doesn't explicitly specify, this is a guess. Simply put, exceptions
1838 	 * are considered in sequence with an increasing order of priority.
1839 	 */
1840 	if (softfloat_exceptionFlags & softfloat_flag_inexact)
1841 	{
1842 		m_psw |= PSW_FX;
1843 		if (PSW(EFX))
1844 			exception = EXCEPTION_FLOATING_INEXACT;
1845 	}
1846 	if (softfloat_exceptionFlags & softfloat_flag_underflow)
1847 	{
1848 		m_psw |= PSW_FU;
1849 		if (PSW(EFU))
1850 			exception = EXCEPTION_FLOATING_UNDERFLOW;
1851 	}
1852 	if (softfloat_exceptionFlags & softfloat_flag_overflow)
1853 	{
1854 		m_psw |= PSW_FV;
1855 		if (PSW(EFV))
1856 			exception = EXCEPTION_FLOATING_OVERFLOW;
1857 	}
1858 	if (softfloat_exceptionFlags & softfloat_flag_infinite)
1859 	{
1860 		m_psw |= PSW_FD;
1861 		if (PSW(EFD))
1862 			exception = EXCEPTION_FLOATING_DIVIDE_BY_ZERO;
1863 	}
1864 	if (softfloat_exceptionFlags & softfloat_flag_invalid)
1865 	{
1866 		m_psw |= PSW_FI;
1867 		if (PSW(EFI))
1868 			exception = EXCEPTION_FLOATING_INVALID_OPERATION;
1869 	}
1870 
1871 	// trigger a floating point exception
1872 	if (PSW(EFT) && exception)
1873 		m_exception = exception;
1874 }
1875 
execute_instruction()1876 void clipper_c400_device::execute_instruction()
1877 {
1878 	// update delay slot pointer
1879 	switch (PSW(DSP))
1880 	{
1881 	case DSP_S1:
1882 		// take delayed branch
1883 		m_psw &= ~PSW_DSP;
1884 		m_pc = m_db_pc;
1885 		return;
1886 
1887 	case DSP_SALL:
1888 		// one delay slot still active
1889 		m_psw &= ~PSW_DSP;
1890 		m_psw |= DSP_S1;
1891 		break;
1892 
1893 	case DSP_SETUP:
1894 		// two delay slots active
1895 		m_psw &= ~PSW_DSP;
1896 		m_psw |= DSP_SALL;
1897 		break;
1898 	}
1899 
1900 	// if executing a delay slot instruction, test for valid type
1901 	if (PSW(DSP))
1902 	{
1903 		switch (m_info.opcode)
1904 		{
1905 		case 0x13: // ret
1906 		case 0x44: // call
1907 		case 0x45:
1908 		case 0x48: // b*
1909 		case 0x49:
1910 		case 0x4a: // cdb
1911 		case 0x4b:
1912 		case 0x4c: // cdbeq
1913 		case 0x4d:
1914 		case 0x4e: // cdbne
1915 		case 0x4f:
1916 		case 0x50: // db*
1917 		case 0x51:
1918 			// TODO: this should throw some kind of illegal instruction trap, not abort
1919 			fatalerror("instruction type 0x%02x invalid in branch delay slot pc 0x%08x\n", m_info.opcode, m_info.pc);
1920 
1921 		default:
1922 			break;
1923 		}
1924 	}
1925 
1926 	switch (m_info.opcode)
1927 	{
1928 	case 0x46:
1929 	case 0x47:
1930 		// loadd2: load double floating double
1931 		// TODO: 128-bit load
1932 		get_dcammu().load<u64>(m_ssw, m_info.address + 0, [this](u64 v) { set_fp(R2 + 0, float64_t{ v }, F_NONE); });
1933 		get_dcammu().load<u64>(m_ssw, m_info.address + 8, [this](u64 v) { set_fp(R2 + 1, float64_t{ v }, F_NONE); });
1934 		// TRAPS: C,U,A,P,R,I
1935 		break;
1936 
1937 	case 0x4a:
1938 	case 0x4b:
1939 		// cdb: compare and delayed branch?
1940 		// emulate.h: "cdb is special because it does not support all addressing modes", 2-3 parcels
1941 		fatalerror("cdb pc 0x%08x\n", m_info.pc);
1942 	case 0x4c:
1943 	case 0x4d:
1944 		// cdbeq: compare and delayed branch if equal?
1945 		if (m_r[R2] == 0)
1946 		{
1947 			m_psw |= DSP_SETUP;
1948 			m_db_pc = m_info.address;
1949 		}
1950 		break;
1951 	case 0x4e:
1952 	case 0x4f:
1953 		// cdbne: compare and delayed branch if not equal?
1954 		if (m_r[R2] != 0)
1955 		{
1956 			m_psw |= DSP_SETUP;
1957 			m_db_pc = m_info.address;
1958 		}
1959 		break;
1960 	case 0x50:
1961 	case 0x51:
1962 		// db*: delayed branch on condition
1963 		if (evaluate_branch())
1964 		{
1965 			m_psw |= DSP_SETUP;
1966 			m_db_pc = m_info.address;
1967 		}
1968 		break;
1969 
1970 	case 0xb0:
1971 		// abss: absolute value single floating?
1972 		if (f32_lt(get_fp32(R1), float32_t{ 0 }))
1973 			set_fp(R2, f32_mul(get_fp32(R1), i32_to_f32(-1)), F_IVUX);
1974 		else
1975 			set_fp(R2, get_fp32(R1), F_IVUX);
1976 		break;
1977 
1978 	case 0xb2:
1979 		// absd: absolute value double floating?
1980 		if (f64_lt(get_fp64(R1), float64_t{ 0 }))
1981 			set_fp(R2, f64_mul(get_fp64(R1), i32_to_f64(-1)), F_IVUX);
1982 		else
1983 			set_fp(R2, get_fp64(R1), F_IVUX);
1984 		break;
1985 
1986 	case 0xb4:
1987 		// unprivileged macro instructions
1988 		switch (m_info.subopcode)
1989 		{
1990 		case 0x44:
1991 			// cnvxsw: ??
1992 			fatalerror("cnvxsw pc 0x%08x\n", m_info.pc);
1993 		case 0x46:
1994 			// cnvxdw: ??
1995 			fatalerror("cnvxdw pc 0x%08x\n", m_info.pc);
1996 
1997 		default:
1998 			clipper_device::execute_instruction();
1999 			break;
2000 		}
2001 		break;
2002 
2003 	case 0xb6:
2004 		// privileged macro instructions
2005 		if (!SSW(U))
2006 		{
2007 			switch (m_info.subopcode)
2008 			{
2009 			case 0x07:
2010 				// loadts: unknown?
2011 				fatalerror("loadts pc 0x%08x\n", m_info.pc);
2012 
2013 			default:
2014 				clipper_device::execute_instruction();
2015 				break;
2016 			}
2017 		}
2018 		else
2019 			m_exception = EXCEPTION_PRIVILEGED_INSTRUCTION;
2020 		break;
2021 
2022 	case 0xbc:
2023 		// waitd:
2024 		if (!SSW(U))
2025 			; // TODO: don't know what this instruction does
2026 		else
2027 			m_exception = EXCEPTION_PRIVILEGED_INSTRUCTION;
2028 		break;
2029 
2030 	case 0xc0:
2031 		// s*: set register on condition
2032 		m_r[R1] = evaluate_branch() ? 1 : 0;
2033 		break;
2034 
2035 	default:
2036 		clipper_device::execute_instruction();
2037 		break;
2038 	}
2039 }
2040 
create_disassembler()2041 std::unique_ptr<util::disasm_interface> clipper_device::create_disassembler()
2042 {
2043 	return std::make_unique<clipper_disassembler>();
2044 }
2045 
debug_string(u32 pointer)2046 std::string clipper_device::debug_string(u32 pointer)
2047 {
2048 	auto const suppressor(machine().disable_side_effects());
2049 
2050 	std::string s("");
2051 
2052 	while (true)
2053 	{
2054 		char c;
2055 
2056 		if (!get_dcammu().load<u8>(m_ssw, pointer++, [&c](u8 v) { c = v; }))
2057 			break;
2058 
2059 		if (c == '\0')
2060 			break;
2061 
2062 		s += c;
2063 	}
2064 
2065 	return s;
2066 }
2067 
debug_string_array(u32 array_pointer)2068 std::string clipper_device::debug_string_array(u32 array_pointer)
2069 {
2070 	auto const suppressor(machine().disable_side_effects());
2071 
2072 	std::string s("");
2073 
2074 	while (true)
2075 	{
2076 		u32 string_pointer;
2077 
2078 		if (!get_dcammu().load<u32>(m_ssw, array_pointer, [&string_pointer](u32 v) { string_pointer = v; }))
2079 			break;
2080 
2081 		if (string_pointer == 0)
2082 			break;
2083 
2084 		if (!s.empty())
2085 			s += ", ";
2086 
2087 		s += '\"' + debug_string(string_pointer) + '\"';
2088 		array_pointer += 4;
2089 	}
2090 
2091 	return s;
2092 }
2093