1 /*
2 * Copyright (c) 2014 The KnightOS Group
3 * Modified to support the eZ80 processor by CEmu developers
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
8 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in all copies or substantial portions
11 * of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
14 * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
15 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
16 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17 * DEALINGS IN THE SOFTWARE.
18 */
19
20 #include "cpu.h"
21 #include "emu.h"
22 #include "mem.h"
23 #include "bus.h"
24 #include "defines.h"
25 #include "control.h"
26 #include "registers.h"
27 #include "schedule.h"
28 #include "interrupt.h"
29 #include "debug/debug.h"
30
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdio.h>
34
35 /* Global CPU state */
36 eZ80cpu_t cpu;
37
cpu_clear_context(void)38 static void cpu_clear_context(void) {
39 cpu.PREFIX = cpu.SUFFIX = 0;
40 cpu.L = cpu.ADL;
41 cpu.IL = cpu.ADL;
42 }
43
cpu_inst_start(void)44 static void cpu_inst_start(void) {
45 cpu_clear_context();
46 #ifdef DEBUG_SUPPORT
47 debug_inst_start();
48 #endif
49 }
50
cpu_address_mode(uint32_t address,bool mode)51 uint32_t cpu_address_mode(uint32_t address, bool mode) {
52 if (mode) {
53 return address & 0xFFFFFF;
54 }
55 return (cpu.registers.MBASE << 16) | (address & 0xFFFF);
56 }
cpu_prefetch(uint32_t address,bool mode)57 static void cpu_prefetch(uint32_t address, bool mode) {
58 cpu.ADL = mode;
59 /* rawPC the PC after the next prefetch (which we do late), before adding MBASE. */
60 cpu.registers.rawPC = cpu_mask_mode(address + 1, mode);
61 cpu.registers.PC = cpu_address_mode(address, mode);
62 cpu.prefetch = mem_read_cpu(cpu.registers.PC, true);
63 }
cpu_fetch_byte(void)64 static uint8_t cpu_fetch_byte(void) {
65 uint8_t value;
66 #ifdef DEBUG_SUPPORT
67 debug_inst_fetch();
68 #endif
69 value = cpu.prefetch;
70 cpu_prefetch(cpu.registers.PC + 1, cpu.ADL);
71 return value;
72 }
cpu_prefetch_discard(void)73 static void cpu_prefetch_discard(void) {
74 mem_read_cpu(cpu_address_mode(cpu.registers.PC + 1, cpu.ADL), true);
75 }
cpu_fetch_offset(void)76 static int8_t cpu_fetch_offset(void) {
77 return (int8_t)cpu_fetch_byte();
78 }
cpu_fetch_word(void)79 static uint32_t cpu_fetch_word(void) {
80 uint32_t value = cpu_fetch_byte();
81 value |= cpu_fetch_byte() << 8;
82 if (cpu.IL) {
83 value |= cpu_fetch_byte() << 16;
84 }
85 return value;
86 }
cpu_fetch_word_no_prefetch(void)87 static uint32_t cpu_fetch_word_no_prefetch(void) {
88 uint32_t value = cpu_fetch_byte();
89 value |= cpu.prefetch << 8;
90 if (cpu.IL) {
91 cpu_fetch_byte();
92 value |= cpu.prefetch << 16;
93 }
94 cpu.registers.PC++;
95 return value;
96 }
97
cpu_read_byte(uint32_t address)98 static uint8_t cpu_read_byte(uint32_t address) {
99 uint32_t cpuAddress = cpu_address_mode(address, cpu.L);
100 return mem_read_cpu(cpuAddress, false);
101 }
cpu_write_byte(uint32_t address,uint8_t value)102 static void cpu_write_byte(uint32_t address, uint8_t value) {
103 uint32_t cpuAddress = cpu_address_mode(address, cpu.L);
104 mem_write_cpu(cpuAddress, value);
105 }
106
cpu_read_word(uint32_t address)107 static uint32_t cpu_read_word(uint32_t address) {
108 uint32_t value = cpu_read_byte(address);
109 value |= cpu_read_byte(address + 1) << 8;
110 if (cpu.L) {
111 value |= cpu_read_byte(address + 2) << 16;
112 }
113 return value;
114 }
cpu_write_word(uint32_t address,uint32_t value)115 static void cpu_write_word(uint32_t address, uint32_t value) {
116 cpu_write_byte(address, value);
117 cpu_write_byte(address + 1, value >> 8);
118 if (cpu.L) {
119 cpu_write_byte(address + 2, value >> 16);
120 }
121 }
122
cpu_pop_byte_mode(bool mode)123 static uint8_t cpu_pop_byte_mode(bool mode) {
124 return mem_read_cpu(cpu_address_mode(cpu.registers.stack[mode].hl++, mode), false);
125 }
cpu_pop_byte(void)126 static uint8_t cpu_pop_byte(void) {
127 return cpu_pop_byte_mode(cpu.L);
128 }
cpu_push_byte_mode(uint8_t value,bool mode)129 static void cpu_push_byte_mode(uint8_t value, bool mode) {
130 mem_write_cpu(cpu_address_mode(--cpu.registers.stack[mode].hl, mode), value);
131 }
cpu_push_byte(uint8_t value)132 static void cpu_push_byte(uint8_t value) {
133 cpu_push_byte_mode(value, cpu.L);
134 }
135
cpu_push_word(uint32_t value)136 static void cpu_push_word(uint32_t value) {
137 if (cpu.L) {
138 cpu_push_byte(value >> 16);
139 }
140 cpu_push_byte(value >> 8);
141 cpu_push_byte(value);
142 }
143
cpu_pop_word(void)144 static uint32_t cpu_pop_word(void) {
145 uint32_t value = cpu_pop_byte();
146 value |= cpu_pop_byte() << 8;
147 if (cpu.L) {
148 value |= cpu_pop_byte() << 16;
149 }
150 return value;
151 }
152
cpu_read_in(uint16_t pio)153 static uint8_t cpu_read_in(uint16_t pio) {
154 if (unprivileged_code()) {
155 return 0; /* in returns 0 in unprivileged code */
156 }
157 return port_read_byte(pio);
158 }
159
cpu_write_out(uint16_t pio,uint8_t value)160 static void cpu_write_out(uint16_t pio, uint8_t value) {
161 if (unprivileged_code()) {
162 control.protectionStatus |= 2;
163 gui_console_printf("[CEmu] NMI reset cause by an out instruction in unpriviledged code.\n");
164 cpu_nmi();
165 }
166 port_write_byte(pio, value);
167 }
168
cpu_read_sp(void)169 static uint32_t cpu_read_sp(void) {
170 return cpu.registers.stack[cpu.L].hl;
171 }
cpu_write_sp(uint32_t value)172 static void cpu_write_sp(uint32_t value) {
173 cpu.registers.stack[cpu.L].hl = value;
174 }
175
cpu_read_index_low(void)176 static uint8_t cpu_read_index_low(void) {
177 return cpu.registers.index[cpu.PREFIX].l;
178 }
cpu_write_index_low(uint8_t value)179 static void cpu_write_index_low(uint8_t value) {
180 cpu.registers.index[cpu.PREFIX].l = value;
181 }
182
cpu_read_index_high(void)183 static uint8_t cpu_read_index_high(void) {
184 return cpu.registers.index[cpu.PREFIX].h;
185 }
cpu_write_index_high(uint8_t value)186 static void cpu_write_index_high(uint8_t value) {
187 cpu.registers.index[cpu.PREFIX].h = value;
188 }
189
cpu_read_index(void)190 static uint32_t cpu_read_index(void) {
191 return cpu.registers.index[cpu.PREFIX].hl;
192 }
cpu_write_index(uint32_t value)193 static void cpu_write_index(uint32_t value) {
194 cpu.registers.index[cpu.PREFIX].hl = value;
195 }
196
cpu_read_other_index(void)197 static uint32_t cpu_read_other_index(void) {
198 return cpu.registers.index[cpu.PREFIX ^ 1].hl;
199 }
cpu_write_other_index(uint32_t value)200 static void cpu_write_other_index(uint32_t value) {
201 cpu.registers.index[cpu.PREFIX ^ 1].hl = value;
202 }
203
cpu_index_address(void)204 static uint32_t cpu_index_address(void) {
205 int32_t value = cpu_read_index();
206 if (cpu.PREFIX) {
207 value += cpu_fetch_offset();
208 }
209 return cpu_mask_mode(value, cpu.L);
210 }
211
cpu_read_reg(int i)212 static uint8_t cpu_read_reg(int i) {
213 uint8_t value;
214 switch (i) {
215 case 0: value = cpu.registers.B; break;
216 case 1: value = cpu.registers.C; break;
217 case 2: value = cpu.registers.D; break;
218 case 3: value = cpu.registers.E; break;
219 case 4: value = cpu_read_index_high(); break;
220 case 5: value = cpu_read_index_low(); break;
221 case 6: value = cpu_read_byte(cpu_index_address()); break;
222 case 7: value = cpu.registers.A; break;
223 default: abort();
224 }
225 return value;
226 }
cpu_write_reg(int i,uint8_t value)227 static void cpu_write_reg(int i, uint8_t value) {
228 switch (i) {
229 case 0: cpu.registers.B = value; break;
230 case 1: cpu.registers.C = value; break;
231 case 2: cpu.registers.D = value; break;
232 case 3: cpu.registers.E = value; break;
233 case 4: cpu_write_index_high(value); break;
234 case 5: cpu_write_index_low(value); break;
235 case 6: cpu_write_byte(cpu_index_address(), value); break;
236 case 7: cpu.registers.A = value; break;
237 default: abort();
238 }
239 }
cpu_read_write_reg(int read,int write)240 static void cpu_read_write_reg(int read, int write) {
241 uint8_t value;
242 int old_prefix = cpu.PREFIX;
243 cpu.PREFIX = (write != 6) ? old_prefix : 0;
244 value = cpu_read_reg(read);
245 cpu.PREFIX = (read != 6) ? old_prefix : 0;
246 cpu_write_reg(write, value);
247 }
248
cpu_read_reg_prefetched(int i,uint32_t address)249 static uint8_t cpu_read_reg_prefetched(int i, uint32_t address) {
250 uint8_t value;
251 switch (i) {
252 case 0: value = cpu.registers.B; break;
253 case 1: value = cpu.registers.C; break;
254 case 2: value = cpu.registers.D; break;
255 case 3: value = cpu.registers.E; break;
256 case 4: value = cpu_read_index_high(); break;
257 case 5: value = cpu_read_index_low(); break;
258 case 6: value = cpu_read_byte(address); break;
259 case 7: value = cpu.registers.A; break;
260 default: abort();
261 }
262 return value;
263 }
cpu_write_reg_prefetched(int i,uint32_t address,uint8_t value)264 static void cpu_write_reg_prefetched(int i, uint32_t address, uint8_t value) {
265 switch (i) {
266 case 0: cpu.registers.B = value; break;
267 case 1: cpu.registers.C = value; break;
268 case 2: cpu.registers.D = value; break;
269 case 3: cpu.registers.E = value; break;
270 case 4: cpu_write_index_high(value); break;
271 case 5: cpu_write_index_low(value); break;
272 case 6: cpu_write_byte(address, value); break;
273 case 7: cpu.registers.A = value; break;
274 default: abort();
275 }
276 }
277
cpu_read_rp(int i)278 static uint32_t cpu_read_rp(int i) {
279 uint32_t value;
280 switch (i) {
281 case 0: value = cpu.registers.BC; break;
282 case 1: value = cpu.registers.DE; break;
283 case 2: value = cpu_read_index(); break;
284 case 3: value = cpu_read_sp(); break;
285 default: abort();
286 }
287 return cpu_mask_mode(value, cpu.L);
288 }
cpu_write_rp(int i,uint32_t value)289 static void cpu_write_rp(int i, uint32_t value) {
290 value = cpu_mask_mode(value, cpu.L);
291 switch (i) {
292 case 0: cpu.registers.BC = value; break;
293 case 1: cpu.registers.DE = value; break;
294 case 2: cpu_write_index(value); break;
295 case 3: cpu_write_sp(value); break;
296 default: abort();
297 }
298 }
299
cpu_read_rp2(int i)300 static uint32_t cpu_read_rp2(int i) {
301 if (i == 3) {
302 return cpu.registers.AF;
303 } else {
304 return cpu_read_rp(i);
305 }
306 }
cpu_write_rp2(int i,uint32_t value)307 static void cpu_write_rp2(int i, uint32_t value) {
308 if (i == 3) {
309 cpu.registers.AF = value;
310 } else {
311 cpu_write_rp(i, value);
312 }
313 }
314
cpu_read_rp3(int i)315 static uint32_t cpu_read_rp3(int i) {
316 uint32_t value;
317 switch (i) {
318 case 0: value = cpu.registers.BC; break;
319 case 1: value = cpu.registers.DE; break;
320 case 2: value = cpu.registers.HL; break;
321 case 3: value = cpu_read_index(); break;
322 default: abort();
323 }
324 return cpu_mask_mode(value, cpu.L);
325 }
cpu_write_rp3(int i,uint32_t value)326 static void cpu_write_rp3(int i, uint32_t value) {
327 value = cpu_mask_mode(value, cpu.L);
328 switch (i) {
329 case 0: cpu.registers.BC = value; break;
330 case 1: cpu.registers.DE = value; break;
331 case 2: cpu.registers.HL = value; break;
332 case 3: cpu_write_index(value); break;
333 default: abort();
334 }
335 }
336
cpu_read_cc(const int i)337 static bool cpu_read_cc(const int i) {
338 switch (i) {
339 case 0: return !cpu.registers.flags.Z;
340 case 1: return cpu.registers.flags.Z;
341 case 2: return !cpu.registers.flags.C;
342 case 3: return cpu.registers.flags.C;
343 case 4: return !cpu.registers.flags.PV;
344 case 5: return cpu.registers.flags.PV;
345 case 6: return !cpu.registers.flags.S;
346 case 7: return cpu.registers.flags.S;
347 default: abort();
348 }
349 return true;
350 }
351
cpu_execute_daa(void)352 static void cpu_execute_daa(void) {
353 eZ80registers_t *r = &cpu.registers;
354 uint8_t old = r->A;
355 uint8_t v = 0;
356 if ((r->A & 0xF) > 9 || r->flags.H) {
357 v += 6;
358 }
359 if (((r->A + v) >> 4) > 9 || cpuflag_carry_b(r->A + v) || r->flags.C) {
360 v += 0x60;
361 }
362 if (r->flags.N) {
363 r->A -= v;
364 r->F = cpuflag_sign_b(r->A) | cpuflag_zero(r->A)
365 | cpuflag_undef(r->F) | cpuflag_parity(r->A)
366 | cpuflag_subtract(r->flags.N) | cpuflag_c(v >= 0x60)
367 | cpuflag_halfcarry_b_sub(old, v, 0);
368 } else {
369 r->A += v;
370 r->F = cpuflag_sign_b(r->A) | cpuflag_zero(r->A)
371 | cpuflag_undef(r->F) | cpuflag_parity(r->A)
372 | cpuflag_subtract(r->flags.N) | cpuflag_c(v >= 0x60)
373 | cpuflag_halfcarry_b_add(old, v, 0);
374 }
375 }
376
cpu_dec_bc_partial_mode()377 static uint32_t cpu_dec_bc_partial_mode() {
378 uint32_t value = cpu_mask_mode((int32_t)cpu.registers.BC - 1, cpu.L);
379 if (cpu.L) {
380 cpu.registers.BC = value;
381 } else {
382 cpu.registers.BCS = value;
383 }
384 return value;
385 }
386
cpu_call(uint32_t address,bool mixed)387 static void cpu_call(uint32_t address, bool mixed) {
388 #ifdef DEBUG_SUPPORT
389 debug_record_call(cpu.registers.PC, cpu.L);
390 #endif
391 if (mixed) {
392 bool stack = cpu.IL || (cpu.L && !cpu.ADL);
393 if (cpu.ADL) {
394 cpu_push_byte_mode(cpu.registers.PCU, true);
395 }
396 cpu_push_byte_mode(cpu.registers.PCH, stack);
397 cpu_push_byte_mode(cpu.registers.PCL, stack);
398 cpu_push_byte_mode((cpu.MADL << 1) | cpu.ADL, true);
399 } else {
400 cpu_push_word(cpu.registers.PC);
401 }
402 cpu_prefetch(address, cpu.IL);
403 }
404
cpu_trap_rewind(uint_fast8_t rewind)405 static void cpu_trap_rewind(uint_fast8_t rewind) {
406 eZ80registers_t *r = &cpu.registers;
407 cpu_prefetch_discard();
408 cpu.cycles++;
409 r->PC = cpu_mask_mode(r->PC - rewind, cpu.ADL);
410 cpu_clear_context();
411 cpu_call(0x00, cpu.MADL);
412 }
413
cpu_trap(void)414 static void cpu_trap(void) {
415 cpu_trap_rewind(1);
416 }
417
cpu_jump(uint32_t address,bool mode)418 static void cpu_jump(uint32_t address, bool mode) {
419 cpu_prefetch(address, mode);
420 #ifdef DEBUG_SUPPORT
421 debug_record_ret(address, mode);
422 #endif
423 }
424
cpu_return(void)425 static void cpu_return(void) {
426 uint32_t address;
427 bool mode = cpu.ADL;
428 cpu.cycles++;
429 if (cpu.SUFFIX) {
430 mode = cpu_pop_byte_mode(true) & 1;
431 address = cpu_pop_byte_mode(cpu.ADL);
432 address |= cpu_pop_byte_mode(cpu.ADL) << 8;
433 if (mode) {
434 address |= cpu_mask_mode(cpu_pop_byte_mode(true) << 16, cpu.ADL || cpu.L);
435 }
436 } else {
437 address = cpu_pop_word();
438 }
439 cpu_jump(address, mode);
440 }
441
cpu_execute_alu(int i,uint8_t v)442 static void cpu_execute_alu(int i, uint8_t v) {
443 uint8_t old;
444 eZ80registers_t *r = &cpu.registers;
445 switch (i) {
446 case 0: /* ADD A, v */
447 old = r->A;
448 r->A += v;
449 r->F = cpuflag_sign_b(r->A) | cpuflag_zero(r->A)
450 | cpuflag_undef(r->F) | cpuflag_overflow_b_add(old, v, r->A)
451 | cpuflag_subtract(0) | cpuflag_carry_b(old + v)
452 | cpuflag_halfcarry_b_add(old, v, 0);
453 break;
454 case 1: /* ADC A, v */
455 old = r->A;
456 r->A += v + r->flags.C;
457 r->F = cpuflag_sign_b(r->A) | cpuflag_zero(r->A)
458 | cpuflag_undef(r->F) | cpuflag_overflow_b_add(old, v, r->A)
459 | cpuflag_subtract(0) | cpuflag_carry_b(old + v + r->flags.C)
460 | cpuflag_halfcarry_b_add(old, v, r->flags.C);
461 break;
462 case 2: /* SUB v */
463 old = r->A;
464 r->A -= v;
465 r->F = cpuflag_sign_b(r->A) | cpuflag_zero(r->A)
466 | cpuflag_undef(r->F) | cpuflag_overflow_b_sub(old, v, r->A)
467 | cpuflag_subtract(1) | cpuflag_carry_b(old - v)
468 | cpuflag_halfcarry_b_sub(old, v, 0);
469 break;
470 case 3: /* SBC v */
471 old = r->A;
472 r->A -= v + r->flags.C;
473 r->F = cpuflag_sign_b(r->A) | cpuflag_zero(r->A)
474 | cpuflag_undef(r->F) | cpuflag_overflow_b_sub(old, v, r->A)
475 | cpuflag_subtract(1) | cpuflag_carry_b(old - v - r->flags.C)
476 | cpuflag_halfcarry_b_sub(old, v, r->flags.C);
477 break;
478 case 4: /* AND v */
479 r->A &= v;
480 r->F = cpuflag_sign_b(r->A) | cpuflag_zero(r->A)
481 | cpuflag_undef(r->F) | cpuflag_parity(r->A)
482 | FLAG_H;
483 break;
484 case 5: /* XOR v */
485 r->A ^= v;
486 r->F = cpuflag_sign_b(r->A) | cpuflag_zero(r->A)
487 | cpuflag_undef(r->F) | cpuflag_parity(r->A);
488 break;
489 case 6: /* OR v */
490 r->A |= v;
491 r->F = cpuflag_sign_b(r->A) | cpuflag_zero(r->A)
492 | cpuflag_undef(r->F) | cpuflag_parity(r->A);
493 break;
494 case 7: /* CP v */
495 old = r->A - v;
496 r->F = cpuflag_sign_b(old) | cpuflag_zero(old)
497 | cpuflag_undef(r->F) | cpuflag_subtract(1)
498 | cpuflag_carry_b(r->A - v)
499 | cpuflag_overflow_b_sub(r->A, v, old)
500 | cpuflag_halfcarry_b_sub(r->A, v, 0);
501 break;
502 }
503 }
504
cpu_execute_rot(int y,int z,uint32_t address,uint8_t value)505 static void cpu_execute_rot(int y, int z, uint32_t address, uint8_t value) {
506 eZ80registers_t *r = &cpu.registers;
507 uint8_t old_7 = (value & 0x80) != 0;
508 uint8_t old_0 = (value & 0x01) != 0;
509 uint8_t old_c = r->flags.C;
510 uint8_t new_c;
511 switch (y) {
512 case 0: /* RLC value[z] */
513 value <<= 1;
514 value |= old_7;
515 new_c = old_7;
516 break;
517 case 1: /* RRC value[z] */
518 value >>= 1;
519 value |= old_0 << 7;
520 new_c = old_0;
521 break;
522 case 2: /* RL value[z] */
523 value <<= 1;
524 value |= old_c;
525 new_c = old_7;
526 break;
527 case 3: /* RR value[z] */
528 value >>= 1;
529 value |= old_c << 7;
530 new_c = old_0;
531 break;
532 case 4: /* SLA value[z] */
533 value <<= 1;
534 new_c = old_7;
535 break;
536 case 5: /* SRA value[z] */
537 value >>= 1;
538 value |= old_7 << 7;
539 new_c = old_0;
540 break;
541 case 6: /* OPCODETRAP */
542 cpu_trap_rewind(1 + (cpu.PREFIX != 0));
543 return;
544 case 7: /* SRL value[z] */
545 value >>= 1;
546 new_c = old_0;
547 break;
548 default:
549 abort();
550 }
551 cpu_write_reg_prefetched(z, address, value);
552 r->F = cpuflag_c(new_c) | cpuflag_sign_b(value) | cpuflag_parity(value)
553 | cpuflag_undef(r->F) | cpuflag_zero(value);
554 }
555
cpu_execute_rot_acc(int y)556 static void cpu_execute_rot_acc(int y)
557 {
558 eZ80registers_t *r = &cpu.registers;
559 uint8_t old;
560 switch (y) {
561 case 0: /* RLCA */
562 old = (r->A & 0x80) > 0;
563 r->flags.C = old;
564 r->A <<= 1;
565 r->A |= old;
566 r->flags.N = r->flags.H = 0;
567 break;
568 case 1: /* RRCA */
569 old = (r->A & 1) > 0;
570 r->flags.C = old;
571 r->A >>= 1;
572 r->A |= old << 7;
573 r->flags.N = r->flags.H = 0;
574 break;
575 case 2: /* RLA */
576 old = r->flags.C;
577 r->flags.C = (r->A & 0x80) > 0;
578 r->A <<= 1;
579 r->A |= old;
580 r->flags.N = r->flags.H = 0;
581 break;
582 case 3: /* RRA */
583 old = r->flags.C;
584 r->flags.C = (r->A & 1) > 0;
585 r->A >>= 1;
586 r->A |= old << 7;
587 r->flags.N = r->flags.H = 0;
588 break;
589 case 4: /* DAA */
590 cpu_execute_daa();
591 break;
592 case 5: /* CPL */
593 r->A = ~r->A;
594 r->flags.N = r->flags.H = 1;
595 break;
596 case 6: /* SCF */
597 r->flags.C = 1;
598 r->flags.N = r->flags.H = 0;
599 break;
600 case 7: /* CCF */
601 r->flags.H = r->flags.C;
602 r->flags.C = !r->flags.C;
603 r->flags.N = 0;
604 break;
605 }
606 }
607
cpu_execute_bli()608 static void cpu_execute_bli() {
609 eZ80registers_t *r = &cpu.registers;
610 uint8_t old, new = 0;
611 uint_fast8_t internalCycles = 1;
612 uint_fast8_t xp = cpu.context.x << 2 | cpu.context.p;
613 int_fast8_t delta = cpu.context.q ? -1 : 1;
614 bool repeat = (cpu.context.x | cpu.context.p) & 1;
615 do {
616 #ifdef DEBUG_SUPPORT
617 if (cpu.inBlock) {
618 debug_inst_repeat();
619 }
620 #endif
621 switch (cpu.context.z) {
622 case 0:
623 switch (xp) {
624 case 0xA: /* LDI, LDD */
625 case 0xB: /* LDIR, LDDR */
626 break;
627 default:
628 cpu_trap();
629 return;
630 }
631 /* LDI, LDD, LDIR, LDDR */
632 cpu_write_byte(r->DE, cpu_read_byte(r->HL));
633 r->DE = cpu_mask_mode((int32_t)r->DE + delta, cpu.L);
634 r->flags.H = 0;
635 r->flags.PV = cpu_dec_bc_partial_mode() != 0; /* Do not mask BC */
636 r->flags.N = 0;
637 repeat &= r->flags.PV;
638 break;
639 case 1:
640 switch (xp) {
641 case 0xA: /* CPI, CPD */
642 break;
643 case 0xB: /* CPIR, CPDR */
644 internalCycles = 2;
645 break;
646 default:
647 cpu_trap();
648 return;
649 }
650 /* CPI, CPD, CPIR, CPDR */
651 old = cpu_read_byte(r->HL);
652 new = r->A - old;
653 r->F = cpuflag_sign_b(new) | cpuflag_zero(new)
654 | cpuflag_halfcarry_b_sub(r->A, old, 0)
655 | cpuflag_pv(cpu_dec_bc_partial_mode()) /* Do not mask BC */
656 | cpuflag_subtract(1) | cpuflag_c(r->flags.C)
657 | cpuflag_undef(r->F);
658 repeat &= !r->flags.Z && r->flags.PV;
659 if (!repeat) {
660 internalCycles--;
661 }
662 break;
663 case 2:
664 switch (xp) {
665 case 0x8: /* INIM, INDM */
666 cpu_write_byte(r->HL, new = cpu_read_in(r->C));
667 r->C += delta;
668 old = r->B--;
669 r->F = cpuflag_sign_b(r->B) | cpuflag_zero(r->B)
670 | cpuflag_halfcarry_b_sub(old, 0, 1)
671 | cpuflag_subtract(cpuflag_sign_b(new)) | cpuflag_undef(r->F);
672 break;
673 case 0x9: /* INIMR, INDMR */
674 cpu_write_byte(r->HL, new = cpu_read_in(r->C));
675 r->C += delta;
676 r->flags.Z = --r->B == 0;
677 r->flags.N = cpuflag_sign_b(new) != 0;
678 break;
679 case 0xA: /* INI, IND */
680 case 0xB: /* INIR, INDR */
681 cpu_write_byte(r->HL, new = cpu_read_in(r->BC));
682 r->flags.Z = --r->B == 0;
683 r->flags.N = cpuflag_sign_b(new) != 0;
684 break;
685 case 0xC: /* INIRX, INDRX */
686 cpu_write_byte(r->HL, new = cpu_read_in(r->DE));
687 r->flags.Z = cpu_dec_bc_partial_mode() == 0; /* Do not mask BC */
688 r->flags.N = cpuflag_sign_b(new) != 0;
689 break;
690 default:
691 cpu_trap();
692 return;
693 }
694 /* INIM, INDM, INIMR, INDMR, INI, IND, INIR, INDR, INIRX, INDRX */
695 repeat &= !r->flags.Z;
696 break;
697 case 3:
698 switch (xp) {
699 case 0x8: /* OTIM, OTDM */
700 case 0x9: /* OTIMR, OTDMR */
701 cpu_write_out(r->C, new = cpu_read_byte(r->HL));
702 r->C += delta;
703 old = r->B--;
704 r->F = cpuflag_sign_b(r->B) | cpuflag_zero(r->B)
705 | cpuflag_halfcarry_b_sub(old, 0, 1)
706 | cpuflag_subtract(cpuflag_sign_b(new)) | cpuflag_undef(r->F);
707 break;
708 case 0xA: /* OUTI, OUTD */
709 case 0xB: /* OTIR, OTDR */
710 cpu_write_out(r->BC, new = cpu_read_byte(r->HL));
711 r->flags.Z = --r->B == 0;
712 r->flags.N = cpuflag_sign_b(new) != 0;
713 break;
714 case 0xC: /* OTIRX, OTDRX */
715 cpu_write_out(r->DE, new = cpu_read_byte(r->HL));
716 r->flags.Z = cpu_dec_bc_partial_mode() == 0; /* Do not mask BC */
717 r->flags.N = cpuflag_sign_b(new) != 0;
718 break;
719 default:
720 cpu_trap();
721 return;
722 }
723 /* OTIM, OTDM, OTIMR, OTDMR, OUTI, OUTD, OTIR, OTDR, OTIRX, OTDRX */
724 repeat &= !r->flags.Z;
725 break;
726 case 4:
727 if (xp & 1) {
728 if (xp & 2) { /* OTI2R, OTD2R */
729 cpu_write_out(r->DE, new = cpu_read_byte(r->HL));
730 } else { /* INI2R, IND2R */
731 cpu_write_byte(r->HL, new = cpu_read_in(r->DE));
732 }
733 /* INI2R, IND2R, OTI2R, OTD2R */
734 r->DE = cpu_mask_mode((int32_t)r->DE + delta, cpu.L);
735 r->flags.Z = cpu_dec_bc_partial_mode() == 0; /* Do not mask BC */
736 repeat &= !r->flags.Z;
737 } else {
738 if (xp & 2) { /* OUTI2, OUTD2 */
739 cpu_write_out(r->BC, new = cpu_read_byte(r->HL));
740 } else { /* INI2, IND2 */
741 cpu_write_byte(r->HL, new = cpu_read_in(r->BC));
742 }
743 /* INI2, IND2, OUTI2, OUTD2 */
744 r->C += delta;
745 r->flags.Z = --r->B == 0;
746 }
747 r->flags.N = cpuflag_sign_b(new) != 0;
748 break;
749 default:
750 cpu_trap();
751 return;
752 }
753 /* All block instructions */
754 r->HL = cpu_mask_mode((int32_t)r->HL + delta, cpu.L);
755 cpu.cycles += internalCycles;
756 } while ((cpu.inBlock = repeat) && cpu.cycles < cpu.next);
757 }
758
cpu_init(void)759 void cpu_init(void) {
760 memset(&cpu, 0, sizeof(eZ80cpu_t));
761 cpu.abort = CPU_ABORT_NONE;
762 gui_console_printf("[CEmu] Initialized CPU...\n");
763 }
764
cpu_reset(void)765 void cpu_reset(void) {
766 bool preI = cpu.preI;
767 memset(&cpu, 0, sizeof(cpu));
768 cpu.preI = preI;
769 cpu_restore_next();
770 cpu_flush(0, false);
771 gui_console_printf("[CEmu] CPU reset.\n");
772 }
773
cpu_flush(uint32_t address,bool mode)774 void cpu_flush(uint32_t address, bool mode) {
775 cpu_prefetch(address, mode);
776 cpu_inst_start();
777 cpu.inBlock = false;
778 }
779
cpu_nmi(void)780 void cpu_nmi(void) {
781 cpu.NMI = 1;
782 cpu_restore_next();
783 #ifdef DEBUG_SUPPORT
784 if (debug.openOnReset) {
785 debug_open(DBG_NMI_TRIGGERED, cpu.registers.PC);
786 }
787 #endif
788 }
789
cpu_transition_abort(uint8_t from,uint8_t to)790 void cpu_transition_abort(uint8_t from, uint8_t to) {
791 atomic_compare_exchange_strong(&cpu.abort, &from, to);
792 }
793
cpu_crash(const char * msg)794 void cpu_crash(const char *msg) {
795 cpu_transition_abort(CPU_ABORT_NONE, CPU_ABORT_RESET);
796 gui_console_printf("[CEmu] Reset caused by %s.\n", msg);
797 #ifdef DEBUG_SUPPORT
798 if (debug.openOnReset) {
799 debug_open(DBG_MISC_RESET, cpu.registers.PC);
800 }
801 #endif
802 }
803
cpu_halt(void)804 static void cpu_halt(void) {
805 cpu.halted = true;
806 cpu_restore_next();
807 #ifdef DEBUG_SUPPORT
808 while (cpu.cycles < cpu.next && !cpu.IEF1 && debug.step) {
809 cpu.haltCycles++;
810 cpu.cycles++;
811 debug_open(DBG_FROZEN, cpu.registers.PC);
812 }
813 #endif
814 if (cpu.cycles < cpu.next) {
815 cpu.haltCycles += cpu.next - cpu.cycles;
816 cpu.cycles = cpu.next; /* consume all of the cycles */
817 }
818 }
819
cpu_restore_next(void)820 void cpu_restore_next(void) {
821 if (cpu.NMI || (cpu.IEF1 && (intrpt->status & intrpt->enabled)) || cpu.abort != CPU_ABORT_NONE) {
822 cpu.next = cpu.cycles;
823 } else if (cpu.IEF_wait) {
824 cpu.next = cpu.eiDelay; /* execute one instruction */
825 } else {
826 cpu.next = sched_event_next_cycle();
827 }
828 }
829
cpu_execute(void)830 void cpu_execute(void) {
831 /* variable declarations */
832 int8_t s;
833 int32_t sw;
834 uint32_t w = 0;
835
836 uint8_t old = 0;
837 uint32_t old_word;
838
839 uint8_t new = 0;
840 uint32_t new_word;
841
842 uint32_t op_word;
843
844 eZ80registers_t *r = &cpu.registers;
845 eZ80context_t context;
846
847 while (true) {
848 cpu_execute_continue:
849 if (cpu.IEF_wait && cpu.cycles >= cpu.eiDelay) {
850 cpu.IEF_wait = false;
851 cpu.IEF1 = cpu.IEF2 = true;
852 }
853 if (cpu.NMI || (cpu.IEF1 && (intrpt->status & intrpt->enabled))) {
854 cpu_prefetch_discard();
855 cpu.cycles += 2;
856 cpu.L = cpu.IL = cpu.ADL || cpu.MADL;
857 cpu.IEF1 = cpu.halted = cpu.inBlock = false;
858 if (cpu.NMI) {
859 cpu.NMI = false;
860 cpu_call(0x66, cpu.MADL);
861 } else {
862 cpu.IEF2 = false;
863 if (cpu.IM == 2) {
864 cpu_call(0x38, cpu.MADL);
865 } else {
866 if (cpu.preI && cpu.IM == 3) {
867 cpu.cycles++;
868 cpu_call(cpu_read_word(r->I << 8 | (bus_rand() & 0xFF)), cpu.MADL);
869 } else {
870 cpu_call(bus_rand() & 0x38, cpu.MADL);
871 }
872 }
873 }
874 cpu_restore_next();
875 cpu_inst_start();
876 } else if (cpu.halted) {
877 cpu_halt();
878 } else {
879 cpu_restore_next();
880 }
881 if (cpu.cycles >= cpu.next || cpu.abort != CPU_ABORT_NONE) {
882 break;
883 }
884 if (cpu.inBlock) {
885 goto cpu_execute_bli_continue;
886 }
887 do {
888 /* fetch opcode */
889 context.opcode = cpu_fetch_byte();
890 r->R += 2;
891 switch (context.x) {
892 case 0:
893 switch (context.z) {
894 case 0:
895 if (cpu.PREFIX) {
896 cpu_trap();
897 break;
898 }
899 switch (context.y) {
900 case 0: /* NOP */
901 break;
902 case 1: /* EX af,af' */
903 w = r->AF;
904 r->AF = r->_AF;
905 r->_AF = w;
906 break;
907 case 2: /* DJNZ d */
908 s = cpu_fetch_offset();
909 if (--r->B) {
910 cpu.cycles++;
911 cpu_prefetch(cpu_mask_mode((int32_t)r->PC + s, cpu.L), cpu.ADL);
912 }
913 break;
914 case 3: /* JR d */
915 s = cpu_fetch_offset();
916 cpu_prefetch(cpu_mask_mode((int32_t)r->PC + s, cpu.L), cpu.ADL);
917 break;
918 case 4:
919 case 5:
920 case 6:
921 case 7: /* JR cc[y-4], d */
922 s = cpu_fetch_offset();
923 if (cpu_read_cc(context.y - 4)) {
924 cpu.cycles++;
925 cpu_prefetch(cpu_mask_mode((int32_t)r->PC + s, cpu.L), cpu.ADL);
926 }
927 break;
928 }
929 break;
930 case 1:
931 switch (context.q) {
932 case 0: /* LD rr, Mmn */
933 if (cpu.PREFIX && context.p != 2) {
934 if (context.y == 6) { /* LD IY/IX, (IX/IY + d) */
935 cpu_write_other_index(cpu_read_word(cpu_index_address()));
936 } else {
937 cpu_trap();
938 }
939 break;
940 }
941 cpu_write_rp(context.p, cpu_fetch_word());
942 break;
943 case 1: /* ADD HL,rr */
944 old_word = cpu_mask_mode(cpu_read_index(), cpu.L);
945 op_word = cpu_mask_mode(cpu_read_rp(context.p), cpu.L);
946 new_word = old_word + op_word;
947 cpu_write_index(cpu_mask_mode(new_word, cpu.L));
948 r->F = cpuflag_s(r->flags.S) | cpuflag_zero(!r->flags.Z)
949 | cpuflag_undef(r->F) | cpuflag_pv(r->flags.PV)
950 | cpuflag_subtract(0) | cpuflag_carry_w(new_word, cpu.L)
951 | cpuflag_halfcarry_w_add(old_word, op_word, 0);
952 break;
953 }
954 break;
955 case 2:
956 if (cpu.PREFIX && context.p != 2) {
957 cpu_trap();
958 break;
959 }
960 switch (context.q) {
961 case 0:
962 switch (context.p) {
963 case 0: /* LD (BC), A */
964 cpu_write_byte(r->BC, r->A);
965 break;
966 case 1: /* LD (DE), A */
967 cpu_write_byte(r->DE, r->A);
968 break;
969 case 2: /* LD (Mmn), HL */
970 cpu_write_word(cpu_fetch_word(), cpu_read_index());
971 break;
972 case 3: /* LD (Mmn), A */
973 cpu_write_byte(cpu_fetch_word(), r->A);
974 break;
975 }
976 break;
977 case 1:
978 switch (context.p) {
979 case 0: /* LD A, (BC) */
980 r->A = cpu_read_byte(r->BC);
981 break;
982 case 1: /* LD A, (DE) */
983 r->A = cpu_read_byte(r->DE);
984 break;
985 case 2: /* LD HL, (Mmn) */
986 cpu_write_index(cpu_read_word(cpu_fetch_word()));
987 break;
988 case 3: /* LD A, (Mmn) */
989 r->A = cpu_read_byte(cpu_fetch_word());
990 break;
991 }
992 break;
993 }
994 break;
995 case 3:
996 if (cpu.PREFIX && context.p != 2) {
997 cpu_trap();
998 break;
999 }
1000 switch (context.q) {
1001 case 0: /* INC rp[p] */
1002 cpu_write_rp(context.p, (int32_t)cpu_read_rp(context.p) + 1);
1003 break;
1004 case 1: /* DEC rp[p] */
1005 cpu_write_rp(context.p, (int32_t)cpu_read_rp(context.p) - 1);
1006 break;
1007 }
1008 break;
1009 case 4: /* INC r[y] */
1010 if (cpu.PREFIX && (context.y < 4 || context.y == 7)) {
1011 cpu_trap();
1012 break;
1013 }
1014 w = (context.y == 6) ? cpu_index_address() : 0;
1015 old = cpu_read_reg_prefetched(context.y, w);
1016 new = old + 1;
1017 cpu_write_reg_prefetched(context.y, w, new);
1018 r->F = cpuflag_c(r->flags.C) | cpuflag_sign_b(new) | cpuflag_zero(new)
1019 | cpuflag_halfcarry_b_add(old, 0, 1) | cpuflag_pv(new == 0x80)
1020 | cpuflag_subtract(0) | cpuflag_undef(r->F);
1021 break;
1022 case 5: /* DEC r[y] */
1023 if (cpu.PREFIX && (context.y < 4 || context.y == 7)) {
1024 cpu_trap();
1025 break;
1026 }
1027 w = (context.y == 6) ? cpu_index_address() : 0;
1028 old = cpu_read_reg_prefetched(context.y, w);
1029 new = old - 1;
1030 cpu_write_reg_prefetched(context.y, w, new);
1031 r->F = cpuflag_c(r->flags.C) | cpuflag_sign_b(new) | cpuflag_zero(new)
1032 | cpuflag_halfcarry_b_sub(old, 0, 1) | cpuflag_pv(old == 0x80)
1033 | cpuflag_subtract(1) | cpuflag_undef(r->F);
1034 break;
1035 case 6: /* LD r[y], n */
1036 if (cpu.PREFIX) {
1037 if (context.y < 4) {
1038 cpu_trap();
1039 break;
1040 }
1041 if (context.y == 7) { /* LD (IX/IY + d), IY/IX */
1042 cpu_write_word(cpu_index_address(), cpu_read_other_index());
1043 break;
1044 }
1045 }
1046 if (context.y == 6) {
1047 w = cpu_index_address();
1048 }
1049 cpu_write_reg_prefetched(context.y, w, cpu_fetch_byte());
1050 break;
1051 case 7:
1052 if (cpu.PREFIX) {
1053 if (context.q) { /* LD (IX/IY + d), rp3[p] */
1054 cpu_write_word(cpu_index_address(), cpu_read_rp3(context.p));
1055 } else { /* LD rp3[p], (IX/IY + d) */
1056 cpu_write_rp3(context.p, cpu_read_word(cpu_index_address()));
1057 }
1058 } else {
1059 cpu_execute_rot_acc(context.y);
1060 }
1061 break;
1062 }
1063 break;
1064 case 1: /* ignore prefixed prefixes */
1065 if (context.z == context.y) {
1066 if (cpu.PREFIX && context.z != 4 && context.z != 5) {
1067 cpu_trap();
1068 break;
1069 }
1070 if (context.z < 4) {
1071 if (cpu.SUFFIX && cpu.abort) { /* suffix can infinite loop */
1072 return;
1073 }
1074 cpu.L = context.s;
1075 cpu.IL = context.r;
1076 cpu.SUFFIX = true;
1077 continue;
1078 }
1079 if (context.z == 6) { /* HALT */
1080 cpu_halt();
1081 }
1082 } else {
1083 if (cpu.PREFIX &&
1084 (context.z < 4 || context.z == 7) &&
1085 (context.y < 4 || context.y == 7)) {
1086 cpu_trap();
1087 break;
1088 }
1089 cpu_read_write_reg(context.z, context.y);
1090 }
1091 break;
1092 case 2: /* ALU[y] r[z] */
1093 if (cpu.PREFIX && (context.z < 4 || context.z == 7)) {
1094 cpu_trap();
1095 break;
1096 }
1097 cpu_execute_alu(context.y, cpu_read_reg(context.z));
1098 break;
1099 case 3:
1100 if (cpu.PREFIX && !context.s) {
1101 cpu_trap();
1102 break;
1103 }
1104 switch (context.z) {
1105 case 0: /* RET cc[y] */
1106 cpu.cycles++;
1107 if (cpu_read_cc(context.y)) {
1108 cpu_return();
1109 }
1110 break;
1111 case 1:
1112 switch (context.q) {
1113 case 0: /* POP rp2[p] */
1114 if (cpu.PREFIX && context.p != 2) {
1115 cpu_trap();
1116 break;
1117 }
1118 cpu_write_rp2(context.p, cpu_pop_word());
1119 break;
1120 case 1:
1121 if (cpu.PREFIX && context.p < 2) {
1122 cpu_trap();
1123 break;
1124 }
1125 switch (context.p) {
1126 case 0: /* RET */
1127 cpu_return();
1128 break;
1129 case 1: /* EXX */
1130 w = r->BC;
1131 r->BC = r->_BC;
1132 r->_BC = w;
1133 w = r->DE;
1134 r->DE = r->_DE;
1135 r->_DE = w;
1136 w = r->HL;
1137 r->HL = r->_HL;
1138 r->_HL = w;
1139 break;
1140 case 2: /* JP (rr) */
1141 cpu_prefetch_discard();
1142 cpu_jump(cpu_read_index(), cpu.L);
1143 break;
1144 case 3: /* LD SP, HL */
1145 cpu_write_sp(cpu_read_index());
1146 break;
1147 }
1148 break;
1149 }
1150 break;
1151 case 2: /* JP cc[y], nn */
1152 if (cpu_read_cc(context.y)) {
1153 cpu.cycles++;
1154 cpu_jump(cpu_fetch_word_no_prefetch(), cpu.L);
1155 } else {
1156 cpu_fetch_word();
1157 }
1158 break;
1159 case 3:
1160 if (cpu.PREFIX && context.y != 1 && context.y != 4) {
1161 cpu_trap();
1162 break;
1163 }
1164 switch (context.y) {
1165 case 0: /* JP nn */
1166 cpu.cycles++;
1167 cpu_jump(cpu_fetch_word_no_prefetch(), cpu.L);
1168 break;
1169 case 1: /* 0xCB prefixed opcodes */
1170 w = cpu_index_address();
1171 context.opcode = cpu_fetch_byte();
1172 r->R += ~cpu.PREFIX & 2;
1173 if (cpu.PREFIX && context.z != 6) { /* OPCODETRAP */
1174 cpu_trap_rewind(2);
1175 break;
1176 }
1177 old = cpu_read_reg_prefetched(context.z, w);
1178 switch (context.x) {
1179 case 0: /* rot[y] r[z] */
1180 cpu_execute_rot(context.y, context.z, w, old);
1181 break;
1182 case 1: /* BIT y, r[z] */
1183 old &= (1 << context.y);
1184 r->F = cpuflag_sign_b(old) | cpuflag_zero(old) | cpuflag_undef(r->F)
1185 | cpuflag_parity(old) | cpuflag_c(r->flags.C)
1186 | FLAG_H;
1187 break;
1188 case 2: /* RES y, r[z] */
1189 cpu.cycles += context.z == 6;
1190 old &= ~(1 << context.y);
1191 cpu_write_reg_prefetched(context.z, w, old);
1192 break;
1193 case 3: /* SET y, r[z] */
1194 cpu.cycles += context.z == 6;
1195 old |= 1 << context.y;
1196 cpu_write_reg_prefetched(context.z, w, old);
1197 break;
1198 }
1199 break;
1200 case 2: /* OUT (n), A */
1201 cpu_write_out((r->A << 8) | cpu_fetch_byte(), r->A);
1202 break;
1203 case 3: /* IN A, (n) */
1204 r->A = cpu_read_in((r->A << 8) | cpu_fetch_byte());
1205 break;
1206 case 4: /* EX (SP), HL/I */
1207 w = cpu_read_sp();
1208 old_word = cpu_read_word(w);
1209 new_word = cpu_read_index();
1210 cpu_write_index(old_word);
1211 cpu_write_word(w, new_word);
1212 break;
1213 case 5: /* EX DE, HL */
1214 w = cpu_mask_mode(r->DE, cpu.L);
1215 r->DE = cpu_mask_mode(r->HL, cpu.L);
1216 r->HL = w;
1217 break;
1218 case 6: /* DI */
1219 cpu.IEF_wait = cpu.IEF1 = cpu.IEF2 = false;
1220 cpu_restore_next();
1221 break;
1222 case 7: /* EI */
1223 cpu.IEF_wait = true;
1224 cpu.eiDelay = cpu.cycles + 1;
1225 cpu_restore_next();
1226 break;
1227 }
1228 break;
1229 case 4: /* CALL cc[y], nn */
1230 if (cpu_read_cc(context.y)) {
1231 cpu_call(cpu_fetch_word_no_prefetch(), cpu.SUFFIX);
1232 } else {
1233 cpu_fetch_word();
1234 }
1235 break;
1236 case 5:
1237 if (cpu.PREFIX && context.y != 4) {
1238 cpu_trap();
1239 break;
1240 }
1241 switch (context.q) {
1242 case 0: /* PUSH r2p[p] */
1243 cpu_push_word(cpu_read_rp2(context.p));
1244 break;
1245 case 1:
1246 switch (context.p) {
1247 case 0: /* CALL nn */
1248 cpu_call(cpu_fetch_word_no_prefetch(), cpu.SUFFIX);
1249 break;
1250 case 1: /* 0xDD prefixed opcodes */
1251 if (cpu.PREFIX) {
1252 cpu_trap();
1253 break;
1254 }
1255 cpu.PREFIX = 2;
1256 continue;
1257 case 2: /* 0xED prefixed opcodes */
1258 cpu.PREFIX = 0; /* ED cancels effect of DD/FD prefix */
1259 context.opcode = cpu_fetch_byte();
1260 r->R += 2;
1261 switch (context.x) {
1262 case 0:
1263 switch (context.z) {
1264 case 0: /* IN0 r[y], (n) */
1265 new = cpu_read_in(cpu_fetch_byte());
1266 if (context.y != 6) {
1267 cpu_write_reg(context.y, new);
1268 }
1269 r->F = cpuflag_sign_b(new) | cpuflag_zero(new)
1270 | cpuflag_undef(r->F) | cpuflag_parity(new)
1271 | cpuflag_c(r->flags.C);
1272 break;
1273 case 1:
1274 if (context.y == 6) { /* LD IY, (HL) */
1275 r->IY = cpu_read_word(r->HL);
1276 } else { /* OUT0 (n), r[y] */
1277 cpu_write_out(cpu_fetch_byte(), cpu_read_reg(context.y));
1278 }
1279 break;
1280 case 2: /* LEA rp3[p], IX */
1281 case 3: /* LEA rp3[p], IY */
1282 if (context.q) { /* OPCODETRAP */
1283 cpu_trap();
1284 break;
1285 }
1286 cpu.PREFIX = context.z;
1287 cpu_write_rp3(context.p, cpu_index_address());
1288 break;
1289 case 4: /* TST A, r[y] */
1290 new = r->A & cpu_read_reg(context.y);
1291 r->F = cpuflag_sign_b(new) | cpuflag_zero(new)
1292 | cpuflag_undef(r->F) | cpuflag_parity(new)
1293 | FLAG_H;
1294 break;
1295 case 6:
1296 if (context.y == 7) { /* LD (HL), IY */
1297 cpu_write_word(r->HL, r->IY);
1298 break;
1299 }
1300 /* fallthrough */
1301 case 5: /* OPCODETRAP */
1302 cpu_trap();
1303 break;
1304 case 7:
1305 cpu.PREFIX = 2;
1306 if (context.q) { /* LD (HL), rp3[p] */
1307 cpu_write_word(r->HL, cpu_read_rp3(context.p));
1308 } else { /* LD rp3[p], (HL) */
1309 cpu_write_rp3(context.p, cpu_read_word(r->HL));
1310 }
1311 break;
1312 }
1313 break;
1314 case 1:
1315 switch (context.z) {
1316 case 0: /* IN r[y], (BC) */
1317 new = cpu_read_in(r->BC);
1318 if (context.y != 6) {
1319 cpu_write_reg(context.y, new);
1320 }
1321 r->F = cpuflag_sign_b(new) | cpuflag_zero(new)
1322 | cpuflag_undef(r->F) | cpuflag_parity(new)
1323 | cpuflag_c(r->flags.C);
1324 break;
1325 case 1: /* OUT (BC), r[y] */
1326 if (context.y == 6) { /* OPCODETRAP (ADL) */
1327 cpu_trap();
1328 break;
1329 }
1330 cpu_write_out(r->BC, cpu_read_reg(context.y));
1331 break;
1332 case 2:
1333 old_word = cpu_mask_mode(r->HL, cpu.L);
1334 op_word = cpu_mask_mode(cpu_read_rp(context.p), cpu.L);
1335 if (context.q == 0) { /* SBC HL, rp[p] */
1336 r->HL = cpu_mask_mode(sw = (int32_t)old_word - (int32_t)op_word - r->flags.C, cpu.L);
1337 r->F = cpuflag_sign_w(r->HL, cpu.L) | cpuflag_zero(r->HL)
1338 | cpuflag_undef(r->F) | cpuflag_overflow_w_sub(old_word, op_word, r->HL, cpu.L)
1339 | cpuflag_subtract(1) | cpuflag_carry_w(sw, cpu.L)
1340 | cpuflag_halfcarry_w_sub(old_word, op_word, r->flags.C);
1341 } else { /* ADC HL, rp[p] */
1342 r->HL = cpu_mask_mode(sw = (int32_t)old_word + (int32_t)op_word + r->flags.C, cpu.L);
1343 r->F = cpuflag_sign_w(sw, cpu.L) | cpuflag_zero(r->HL)
1344 | cpuflag_undef(r->F) | cpuflag_overflow_w_add(old_word, op_word, r->HL, cpu.L)
1345 | cpuflag_subtract(0) | cpuflag_carry_w(sw, cpu.L)
1346 | cpuflag_halfcarry_w_add(old_word, op_word, r->flags.C);
1347 }
1348 break;
1349 case 3:
1350 if (context.q == 0) { /* LD (nn), rp[p] */
1351 cpu_write_word(cpu_fetch_word(), cpu_read_rp(context.p));
1352 } else { /* LD rp[p], (nn) */
1353 cpu_write_rp(context.p, cpu_read_word(cpu_fetch_word()));
1354 }
1355 break;
1356 case 4:
1357 if (context.q == 0) {
1358 switch (context.p) {
1359 case 0: /* NEG */
1360 old = r->A;
1361 r->A = -r->A;
1362 r->F = cpuflag_sign_b(r->A) | cpuflag_zero(r->A)
1363 | cpuflag_undef(r->F) | cpuflag_pv(old == 0x80)
1364 | cpuflag_subtract(1) | cpuflag_c(old != 0)
1365 | cpuflag_halfcarry_b_sub(0, old, 0);
1366 break;
1367 case 1: /* LEA IX, IY + d */
1368 cpu.PREFIX = 3;
1369 r->IX = cpu_index_address();
1370 break;
1371 case 2: /* TST A, n */
1372 new = r->A & cpu_fetch_byte();
1373 r->F = cpuflag_sign_b(new) | cpuflag_zero(new)
1374 | cpuflag_undef(r->F) | cpuflag_parity(new)
1375 | FLAG_H;
1376 break;
1377 case 3: /* TSTIO n */
1378 new = cpu_read_in(r->C) & cpu_fetch_byte();
1379 r->F = cpuflag_sign_b(new) | cpuflag_zero(new)
1380 | cpuflag_undef(r->F) | cpuflag_parity(new)
1381 | FLAG_H;
1382 break;
1383 }
1384 }
1385 else { /* MLT rp[p] */
1386 cpu.cycles += 4;
1387 old_word = cpu_read_rp(context.p);
1388 new_word = (old_word&0xFF) * ((old_word>>8)&0xFF);
1389 cpu_write_rp(context.p, new_word);
1390 break;
1391 }
1392 break;
1393 case 5:
1394 switch (context.y) {
1395 case 0: /* RETN */
1396 /* This is actually identical to reti on the z80 */
1397 case 1: /* RETI */
1398 cpu.IEF1 = cpu.IEF2;
1399 cpu_return();
1400 break;
1401 case 2: /* LEA IY, IX + d */
1402 cpu.PREFIX = 2;
1403 r->IY = cpu_index_address();
1404 break;
1405 case 3:
1406 case 6: /* OPCODETRAP */
1407 cpu_trap();
1408 break;
1409 case 4: /* PEA IX + d */
1410 cpu_push_word((int32_t)r->IX + cpu_fetch_offset());
1411 break;
1412 case 5: /* LD MB, A */
1413 if (cpu.ADL) {
1414 r->MBASE = r->A;
1415 }
1416 break;
1417 case 7: /* STMIX */
1418 cpu.MADL = 1;
1419 break;
1420 }
1421 break;
1422 case 6: /* IM im[y] */
1423 switch (context.y) {
1424 case 0:
1425 case 2:
1426 case 3: /* IM im[y] */
1427 cpu.IM = context.y;
1428 break;
1429 case 1: /* OPCODETRAP */
1430 cpu_trap();
1431 break;
1432 case 4: /* PEA IY + d */
1433 cpu_push_word((int32_t)r->IY + cpu_fetch_offset());
1434 break;
1435 case 5: /* LD A, MB */
1436 r->A = r->MBASE;
1437 break;
1438 case 6: /* SLP */
1439 cpu_halt();
1440 break;
1441 case 7: /* RSMIX */
1442 cpu.MADL = 0;
1443 break;
1444 }
1445 break;
1446 case 7:
1447 switch (context.y) {
1448 case 0: /* LD I, A */
1449 r->I = r->A;
1450 break;
1451 case 1: /* LD R, A */
1452 r->R = r->A << 1 | r->A >> 7;
1453 break;
1454 case 2: /* LD A, I */
1455 r->A = r->I;
1456 r->F = cpuflag_sign_b(r->A) | cpuflag_zero(r->A)
1457 | cpuflag_undef(r->F) | cpuflag_pv(cpu.IEF1)
1458 | cpuflag_subtract(0) | cpuflag_c(r->flags.C);
1459 break;
1460 case 3: /* LD A, R */
1461 r->A = r->R >> 1 | r->R << 7;
1462 r->F = cpuflag_sign_b(r->A) | cpuflag_zero(r->A)
1463 | cpuflag_undef(r->F) | cpuflag_pv(cpu.IEF1)
1464 | cpuflag_subtract(0) | cpuflag_c(r->flags.C);
1465 break;
1466 case 4: /* RRD */
1467 old = r->A;
1468 new = cpu_read_byte(r->HL);
1469 r->A &= 0xF0;
1470 r->A |= new & 0x0F;
1471 new >>= 4;
1472 new |= old << 4;
1473 cpu_write_byte(r->HL, new);
1474 r->F = cpuflag_c(r->flags.C) | cpuflag_sign_b(r->A) | cpuflag_zero(r->A)
1475 | cpuflag_parity(r->A) | cpuflag_undef(r->F);
1476 break;
1477 case 5: /* RLD */
1478 old = r->A;
1479 new = cpu_read_byte(r->HL);
1480 r->A &= 0xF0;
1481 r->A |= new >> 4;
1482 new <<= 4;
1483 new |= old & 0x0F;
1484 cpu_write_byte(r->HL, new);
1485 r->F = cpuflag_c(r->flags.C) | cpuflag_sign_b(r->A) | cpuflag_zero(r->A)
1486 | cpuflag_parity(r->A) | cpuflag_undef(r->F);
1487 break;
1488 default: /* OPCODETRAP */
1489 cpu_trap();
1490 break;
1491 }
1492 break;
1493 }
1494 break;
1495 case 2:
1496 cpu_execute_bli_start:
1497 r->PC = cpu_address_mode(r->PC - 2 - cpu.SUFFIX, cpu.ADL);
1498 cpu.context = context;
1499 cpu_execute_bli_continue:
1500 cpu_execute_bli();
1501 if (cpu.inBlock) {
1502 goto cpu_execute_continue;
1503 } else {
1504 r->PC = cpu_address_mode(r->PC + 2 + cpu.SUFFIX, cpu.ADL);
1505 }
1506 break;
1507 case 3: /* There are only a few of these, so a simple switch for these shouldn't matter too much */
1508 switch(context.opcode) {
1509 case 0xC2: /* INIRX */
1510 case 0xC3: /* OTIRX */
1511 case 0xCA: /* INDRX */
1512 case 0xCB: /* OTDRX */
1513 goto cpu_execute_bli_start;
1514 case 0xC7: /* LD I, HL */
1515 r->I = r->HL;
1516 break;
1517 case 0xD7: /* LD HL, I */
1518 r->HL = cpu_mask_mode(r->I | (r->MBASE << 16), cpu.L);
1519 r->F = cpuflag_undef(r->F);
1520 break;
1521 default: /* OPCODETRAP */
1522 cpu_trap();
1523 break;
1524 }
1525 break;
1526 default: /* OPCODETRAP */
1527 cpu_trap();
1528 break;
1529 }
1530 break;
1531 case 3: /* 0xFD prefixed opcodes */
1532 if (cpu.PREFIX) {
1533 cpu_trap();
1534 break;
1535 }
1536 cpu.PREFIX = 3;
1537 continue;
1538 }
1539 break;
1540 }
1541 break;
1542 case 6: /* alu[y] n */
1543 cpu_execute_alu(context.y, cpu_fetch_byte());
1544 break;
1545 case 7: /* RST y*8 */
1546 if (cpu.PREFIX) {
1547 cpu_trap();
1548 break;
1549 }
1550 cpu.cycles++;
1551 cpu_call(context.y << 3, cpu.SUFFIX);
1552 break;
1553 }
1554 break;
1555 }
1556 cpu_inst_start();
1557 } while (cpu.PREFIX || cpu.SUFFIX || cpu.cycles < cpu.next);
1558 }
1559 }
1560
cpu_restore(FILE * image)1561 bool cpu_restore(FILE *image) {
1562 return fread(&cpu, sizeof(cpu), 1, image) == 1;
1563 }
1564
cpu_save(FILE * image)1565 bool cpu_save(FILE *image) {
1566 return fwrite(&cpu, sizeof(cpu), 1, image) == 1;
1567 }
1568