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