yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
spc700.cc
Go to the documentation of this file.
2
3#include <iomanip>
4#include <iostream>
5#include <sstream>
6#include <string>
7#include "util/log.h"
8#include "app/core/features.h"
9
12
13namespace yaze {
14namespace emu {
15
16void Spc700::Reset(bool hard) {
17 if (hard) {
18 // DON'T set PC = 0 here! The reset sequence in Step() will load PC from the reset vector.
19 // Setting PC = 0 here would overwrite the correct value loaded from $FFFE-$FFFF.
20 A = 0;
21 X = 0;
22 Y = 0;
23 SP = 0x00;
24 PSW = ByteToFlags(0x00);
25 }
26 step = 0;
27 stopped_ = false;
28 reset_wanted_ = true;
29}
30
32 // Handle reset sequence (based on 6502, brk without writes)
33 if (reset_wanted_) {
34 reset_wanted_ = false;
35 read(PC);
36 read(PC);
37 read(0x100 | SP--);
38 read(0x100 | SP--);
39 read(0x100 | SP--);
40 callbacks_.idle(false);
41 PSW.I = false;
42
43 // Load PC from reset vector ($FFFE-$FFFF)
44 uint8_t lo = read(0xfffe);
45 uint8_t hi = read(0xffff);
46 PC = lo | (hi << 8);
47
48 return 8;
49 }
50
51 // Handle stopped state (SLEEP/STOP instructions)
52 if (stopped_) {
53 callbacks_.idle(true);
54 return 2;
55 }
56
57 // Reset extra cycle counter for new instruction
58 extra_cycles_ = 0;
59
60 // Fetch and execute one complete instruction
61 uint8_t opcode = ReadOpcode();
62
63 // Get base cycle count from the new accurate lookup table
64 int cycles = spc700_accurate_cycles[opcode];
65
66 // Execute the instruction completely (atomic execution)
67 // This will set extra_cycles_ if a branch is taken
69
70 // Return the base cycles plus any extra cycles from branching
71 return cycles + extra_cycles_;
72}
73
75 static int entry_log = 0;
76 if ((PC >= 0xFFF0 && PC <= 0xFFFF) && entry_log++ < 5) {
77 LOG_DEBUG("SPC", "RunOpcode ENTRY: PC=$%04X step=%d bstep=%d", PC, step, bstep);
78 }
79
80 if (reset_wanted_) {
81 // based on 6502, brk without writes
82 reset_wanted_ = false;
83 read(PC);
84 read(PC);
85 read(0x100 | SP--);
86 read(0x100 | SP--);
87 read(0x100 | SP--);
88 callbacks_.idle(false);
89 PSW.I = false;
90 PC = read_word(0xfffe);
91 last_opcode_cycles_ = 8; // Reset sequence takes 8 cycles
92 return;
93 }
94 if (stopped_) {
95 // Allow timers/DSP to continue advancing while SPC is stopped/sleeping.
96 callbacks_.idle(true);
97 last_opcode_cycles_ = 2; // Stopped state consumes minimal cycles
98 return;
99 }
100 if (step == 0) {
101 // Debug: Comprehensive IPL ROM tracing for transfer protocol debugging
102 // (Only enabled for first few iterations to avoid log spam)
103 static int spc_exec_count = 0;
104 bool in_critical_range = (PC >= 0xFFCF && PC <= 0xFFFF);
105 bool is_transfer_loop = (PC >= 0xFFD6 && PC <= 0xFFED);
106
107 // Reduced logging limits - only log first few iterations
108 if (in_critical_range && spc_exec_count++ < 5) {
109 LOG_DEBUG("SPC", "Execute: PC=$%04X step=0 bstep=%d Y=%02X A=%02X", PC, bstep, Y, A);
110 }
111 if (is_transfer_loop && spc_exec_count < 10) {
112 // Read ports and RAM[$00] to track transfer state
113 uint8_t f4_val = callbacks_.read(0xF4);
114 uint8_t f5_val = callbacks_.read(0xF5);
115 uint8_t ram0_val = callbacks_.read(0x00);
116 LOG_DEBUG("SPC", "TRANSFER LOOP: PC=$%04X Y=%02X A=%02X F4=%02X F5=%02X RAM0=%02X bstep=%d",
117 PC, Y, A, f4_val, f5_val, ram0_val, bstep);
118 }
119
120 // Only read new opcode if previous instruction is complete
121 if (bstep == 0) {
122 opcode = ReadOpcode();
123 // Set base cycle count from lookup table
124 last_opcode_cycles_ = spc700_accurate_cycles[opcode];
125 } else {
126 if (spc_exec_count < 5) {
127 LOG_DEBUG("SPC", "Continuing multi-step: PC=$%04X bstep=%d opcode=$%02X", PC, bstep, opcode);
128 }
129 }
130 step = 1;
131 return;
132 }
133 // TODO: Add SPC700 DisassemblyViewer similar to CPU
134 // For now, skip logging to avoid performance overhead
135 // SPC700 runs at ~1.024 MHz, logging every instruction would be expensive
136 // without the sparse address-map optimization
137
138 static int exec_log = 0;
139 if ((PC >= 0xFFF0 && PC <= 0xFFFF) && exec_log++ < 5) {
140 LOG_DEBUG("SPC", "About to ExecuteInstructions: PC=$%04X step=%d bstep=%d opcode=$%02X", PC, step, bstep, opcode);
141 }
142
144 // Only reset step if instruction is complete (bstep back to 0)
145 static int reset_log = 0;
146 if (step == 1) {
147 if (bstep == 0) {
148 if ((PC >= 0xFFF0 && PC <= 0xFFFF) && reset_log++ < 5) {
149 LOG_DEBUG("SPC", "Resetting step: PC=$%04X opcode=$%02X bstep=%d", PC, opcode, bstep);
150 }
151 step = 0;
152 } else {
153 if ((PC >= 0xFFF0 && PC <= 0xFFFF) && reset_log++ < 5) {
154 LOG_DEBUG("SPC", "NOT resetting step: PC=$%04X opcode=$%02X bstep=%d", PC, opcode, bstep);
155 }
156 }
157 }
158}
159
160void Spc700::ExecuteInstructions(uint8_t opcode) {
161 switch (opcode) {
162 case 0x00: { // nop imp
163 read(PC);
164 // no operation
165 break;
166 }
167 case 0x01:
168 case 0x11:
169 case 0x21:
170 case 0x31:
171 case 0x41:
172 case 0x51:
173 case 0x61:
174 case 0x71:
175 case 0x81:
176 case 0x91:
177 case 0xa1:
178 case 0xb1:
179 case 0xc1:
180 case 0xd1:
181 case 0xe1:
182 case 0xf1: { // tcall imp
183 read(PC);
184 callbacks_.idle(false);
185 push_word(PC);
186 callbacks_.idle(false);
187 uint16_t adr = 0xffde - (2 * (opcode >> 4));
188 PC = read_word(adr);
189 break;
190 }
191 case 0x02:
192 case 0x22:
193 case 0x42:
194 case 0x62:
195 case 0x82:
196 case 0xa2:
197 case 0xc2:
198 case 0xe2: { // set1 dp
199 uint16_t adr = dp();
200 write(adr, read(adr) | (1 << (opcode >> 5)));
201 break;
202 }
203 case 0x12:
204 case 0x32:
205 case 0x52:
206 case 0x72:
207 case 0x92:
208 case 0xb2:
209 case 0xd2:
210 case 0xf2: { // clr1 dp
211 uint16_t adr = dp();
212 write(adr, read(adr) & ~(1 << (opcode >> 5)));
213 break;
214 }
215 case 0x03:
216 case 0x23:
217 case 0x43:
218 case 0x63:
219 case 0x83:
220 case 0xa3:
221 case 0xc3:
222 case 0xe3: { // bbs dp, rel
223 uint8_t val = read(dp());
224 callbacks_.idle(false);
225 DoBranch(ReadOpcode(), val & (1 << (opcode >> 5)));
226 break;
227 }
228 case 0x13:
229 case 0x33:
230 case 0x53:
231 case 0x73:
232 case 0x93:
233 case 0xb3:
234 case 0xd3:
235 case 0xf3: { // bbc dp, rel
236 uint8_t val = read(dp());
237 callbacks_.idle(false);
238 DoBranch(ReadOpcode(), (val & (1 << (opcode >> 5))) == 0);
239 break;
240 }
241
242 case 0x04: { // or dp
243 OR(dp());
244 break;
245 }
246 case 0x05: { // or abs
247 OR(abs());
248 break;
249 }
250 case 0x06: { // or ind
251 OR(ind());
252 break;
253 }
254 case 0x07: { // or idx
255 OR(idx());
256 break;
257 }
258 case 0x08: { // or imm
259 OR(imm());
260 break;
261 }
262 case 0x09: { // orm dp, dp
263 uint8_t src = 0;
264 uint16_t dst = dp_dp(&src);
265 ORM(dst, src);
266 break;
267 }
268 case 0x0a: { // or1 abs.bit
269 uint16_t adr = 0;
270 uint8_t bit = abs_bit(&adr);
271 PSW.C = PSW.C | ((read(adr) >> bit) & 1);
272 callbacks_.idle(false);
273 break;
274 }
275 case 0x0b: { // asl dp
276 ASL(dp());
277 break;
278 }
279 case 0x0c: { // asl abs
280 ASL(abs());
281 break;
282 }
283 case 0x0d: { // pushp imp
284 read(PC);
286 callbacks_.idle(false);
287 break;
288 }
289 case 0x0e: { // tset1 abs
290 uint16_t adr = abs();
291 uint8_t val = read(adr);
292 read(adr);
293 uint8_t result = A + (val ^ 0xff) + 1;
294 PSW.Z = (result == 0);
295 PSW.N = (result & 0x80);
296 write(adr, val | A);
297 break;
298 }
299 case 0x0f: { // brk imp
300 read(PC);
301 push_word(PC);
303 callbacks_.idle(false);
304 PSW.I = false;
305 PSW.B = true;
306 PC = read_word(0xffde);
307 break;
308 }
309 case 0x10: { // bpl rel
311 break;
312 }
313 case 0x14: { // or dpx
314 OR(dpx());
315 break;
316 }
317 case 0x15: { // or abx
318 OR(abs_x());
319 break;
320 }
321 case 0x16: { // or aby
322 OR(abs_y());
323 break;
324 }
325 case 0x17: { // or idy
326 OR(idy());
327 break;
328 }
329 case 0x18: { // orm dp, imm
330 uint8_t src = 0;
331 uint16_t dst = dp_imm(&src);
332 ORM(dst, src);
333 break;
334 }
335 case 0x19: { // orm ind, ind
336 uint8_t src = 0;
337 uint16_t dst = ind_ind(&src);
338 ORM(dst, src);
339 break;
340 }
341 case 0x1a: { // decw dp
342 uint16_t low = 0;
343 uint16_t high = dp_word(&low);
344 uint16_t value = read(low) - 1;
345 write(low, value & 0xff);
346 value += read(high) << 8;
347 write(high, value >> 8);
348 PSW.Z = value == 0;
349 PSW.N = value & 0x8000;
350 break;
351 }
352 case 0x1b: { // asl dpx
353 ASL(dpx());
354 break;
355 }
356 case 0x1c: { // asla imp
357 read(PC);
358 PSW.C = A & 0x80;
359 A <<= 1;
360 PSW.Z = (A == 0);
361 PSW.N = (A & 0x80);
362 break;
363 }
364 case 0x1d: { // decx imp
365 read(PC);
366 X--;
367 PSW.Z = (X == 0);
368 PSW.N = (X & 0x80);
369 break;
370 }
371 case 0x1e: { // cmpx abs
372 CMPX(abs());
373 break;
374 }
375 case 0x1f: { // jmp iax
376 uint16_t pointer = ReadOpcodeWord();
377 callbacks_.idle(false);
378 PC = read_word((pointer + X) & 0xffff);
379 break;
380 }
381 case 0x20: { // clrp imp
382 read(PC);
383 PSW.P = false;
384 break;
385 }
386 case 0x24: { // and dp
387 AND(dp());
388 break;
389 }
390 case 0x25: { // and abs
391 AND(abs());
392 break;
393 }
394 case 0x26: { // and ind
395 AND(ind());
396 break;
397 }
398 case 0x27: { // and idx
399 AND(idx());
400 break;
401 }
402 case 0x28: { // and imm
403 AND(imm());
404 break;
405 }
406 case 0x29: { // andm dp, dp
407 uint8_t src = 0;
408 uint16_t dst = dp_dp(&src);
409 ANDM(dst, src);
410 break;
411 }
412 case 0x2a: { // or1n abs.bit
413 uint16_t adr = 0;
414 uint8_t bit = abs_bit(&adr);
415 PSW.C = PSW.C | (~(read(adr) >> bit) & 1);
416 callbacks_.idle(false);
417 break;
418 }
419 case 0x2b: { // rol dp
420 ROL(dp());
421 break;
422 }
423 case 0x2c: { // rol abs
424 ROL(abs());
425 break;
426 }
427 case 0x2d: { // pusha imp
428 read(PC);
429 push_byte(A);
430 callbacks_.idle(false);
431 break;
432 }
433 case 0x2e: { // cbne dp, rel
434 uint8_t val = read(dp()) ^ 0xff;
435 callbacks_.idle(false);
436 uint8_t result = A + val + 1;
437 DoBranch(ReadOpcode(), result != 0);
438 break;
439 }
440 case 0x2f: { // bra rel
441 DoBranch(ReadOpcode(), true);
442 break;
443 }
444 case 0x30: { // bmi rel
446 break;
447 }
448 case 0x34: { // and dpx
449 AND(dpx());
450 break;
451 }
452 case 0x35: { // and abx
453 AND(abs_x());
454 break;
455 }
456 case 0x36: { // and aby
457 AND(abs_y());
458 break;
459 }
460 case 0x37: { // and idy
461 AND(idy());
462 break;
463 }
464 case 0x38: { // andm dp, imm
465 uint8_t src = 0;
466 uint16_t dst = dp_imm(&src);
467 ANDM(dst, src);
468 break;
469 }
470 case 0x39: { // andm ind, ind
471 uint8_t src = 0;
472 uint16_t dst = ind_ind(&src);
473 ANDM(dst, src);
474 break;
475 }
476 case 0x3a: { // incw dp
477 uint16_t low = 0;
478 uint16_t high = dp_word(&low);
479 uint16_t value = read(low) + 1;
480 write(low, value & 0xff);
481 value += read(high) << 8;
482 write(high, value >> 8);
483 PSW.Z = value == 0;
484 PSW.N = value & 0x8000;
485 break;
486 }
487 case 0x3b: { // rol dpx
488 ROL(dpx());
489 break;
490 }
491 case 0x3c: { // rola imp
492 read(PC);
493 bool newC = A & 0x80;
494 A = (A << 1) | PSW.C;
495 PSW.C = newC;
496 PSW.Z = (A == 0);
497 PSW.N = (A & 0x80);
498 break;
499 }
500 case 0x3d: { // incx imp
501 read(PC);
502 X++;
503 PSW.Z = (X == 0);
504 PSW.N = (X & 0x80);
505 break;
506 }
507 case 0x3e: { // cmpx dp
508 CMPX(dp());
509 break;
510 }
511 case 0x3f: { // call abs
512 uint16_t dst = ReadOpcodeWord();
513 callbacks_.idle(false);
514 push_word(PC);
515 callbacks_.idle(false);
516 callbacks_.idle(false);
517 PC = dst;
518 break;
519 }
520 case 0x40: { // setp imp
521 read(PC);
522 PSW.P = true;
523 break;
524 }
525 case 0x44: { // eor dp
526 EOR(dp());
527 break;
528 }
529 case 0x45: { // eor abs
530 EOR(abs());
531 break;
532 }
533 case 0x46: { // eor ind
534 EOR(ind());
535 break;
536 }
537 case 0x47: { // eor idx
538 EOR(idx());
539 break;
540 }
541 case 0x48: { // eor imm
542 EOR(imm());
543 break;
544 }
545 case 0x49: { // eorm dp, dp
546 uint8_t src = 0;
547 uint16_t dst = dp_dp(&src);
548 EORM(dst, src);
549 break;
550 }
551 case 0x4a: { // and1 abs.bit
552 uint16_t adr = 0;
553 uint8_t bit = abs_bit(&adr);
554 PSW.C = PSW.C & ((read(adr) >> bit) & 1);
555 break;
556 }
557 case 0x4b: { // lsr dp
558 LSR(dp());
559 break;
560 }
561 case 0x4c: { // lsr abs
562 LSR(abs());
563 break;
564 }
565 case 0x4d: { // pushx imp
566 read(PC);
567 push_byte(X);
568 callbacks_.idle(false);
569 break;
570 }
571 case 0x4e: { // tclr1 abs
572 uint16_t adr = abs();
573 uint8_t val = read(adr);
574 read(adr);
575 uint8_t result = A + (val ^ 0xff) + 1;
576 PSW.Z = (result == 0);
577 PSW.N = (result & 0x80);
578 write(adr, val & ~A);
579 break;
580 }
581 case 0x4f: { // pcall dp
582 uint8_t dst = ReadOpcode();
583 callbacks_.idle(false);
584 push_word(PC);
585 callbacks_.idle(false);
586 PC = 0xff00 | dst;
587 break;
588 }
589 case 0x50: { // bvc rel
591 break;
592 }
593 case 0x54: { // eor dpx
594 EOR(dpx());
595 break;
596 }
597 case 0x55: { // eor abx
598 EOR(abs_x());
599 break;
600 }
601 case 0x56: { // eor aby
602 EOR(abs_y());
603 break;
604 }
605 case 0x57: { // eor idy
606 EOR(idy());
607 break;
608 }
609 case 0x58: { // eorm dp, imm
610 uint8_t src = 0;
611 uint16_t dst = dp_imm(&src);
612 EORM(dst, src);
613 break;
614 }
615 case 0x59: { // eorm ind, ind
616 uint8_t src = 0;
617 uint16_t dst = ind_ind(&src);
618 EORM(dst, src);
619 break;
620 }
621 case 0x5a: { // cmpw dp
622 uint16_t low = 0;
623 // uint16_t high = dp_word(&low);
624 uint16_t value = read_word(low) ^ 0xffff;
625 uint16_t ya = A | (Y << 8);
626 int result = ya + value + 1;
627 PSW.C = result > 0xffff;
628 PSW.Z = (result & 0xffff) == 0;
629 PSW.N = result & 0x8000;
630 break;
631 }
632 case 0x5b: { // lsr dpx
633 LSR(dpx());
634 break;
635 }
636 case 0x5c: { // lsra imp
637 read(PC);
638 PSW.C = A & 1;
639 A >>= 1;
640 PSW.Z = (A == 0);
641 PSW.N = (A & 0x80);
642 break;
643 }
644 case 0x5d: { // movxa imp
645 read(PC);
646 X = A;
647 PSW.Z = (X == 0);
648 PSW.N = (X & 0x80);
649 break;
650 }
651 case 0x5e: { // cmpy abs
652 CMPY(abs());
653 break;
654 }
655 case 0x5f: { // jmp abs
656 PC = ReadOpcodeWord();
657 break;
658 }
659 case 0x60: { // clrc imp
660 read(PC);
661 PSW.C = false;
662 break;
663 }
664 case 0x64: { // cmp dp
665 CMP(dp());
666 break;
667 }
668 case 0x65: { // cmp abs
669 CMP(abs());
670 break;
671 }
672 case 0x66: { // cmp ind
673 CMP(ind());
674 break;
675 }
676 case 0x67: { // cmp idx
677 CMP(idx());
678 break;
679 }
680 case 0x68: { // cmp imm
681 CMP(imm());
682 break;
683 }
684 case 0x69: { // cmpm dp, dp
685 uint8_t src = 0;
686 uint16_t dst = dp_dp(&src);
687 CMPM(dst, src);
688 break;
689 }
690 case 0x6a: { // and1n abs.bit
691 uint16_t adr = 0;
692 uint8_t bit = abs_bit(&adr);
693 PSW.C = PSW.C & (~(read(adr) >> bit) & 1);
694 break;
695 }
696 case 0x6b: { // ror dp
697 ROR(dp());
698 break;
699 }
700 case 0x6c: { // ror abs
701 ROR(abs());
702 break;
703 }
704 case 0x6d: { // pushy imp
705 read(PC);
706 push_byte(Y);
707 callbacks_.idle(false);
708 break;
709 }
710 case 0x6e: { // dbnz dp, rel
711 uint16_t adr = dp();
712 uint8_t result = read(adr) - 1;
713 write(adr, result);
714 DoBranch(ReadOpcode(), result != 0);
715 break;
716 }
717 case 0x6f: { // ret imp
718 read(PC);
719 callbacks_.idle(false);
720 PC = pull_word();
721 break;
722 }
723 case 0x70: { // bvs rel
725 break;
726 }
727 case 0x74: { // cmp dpx
728 CMP(dpx());
729 break;
730 }
731 case 0x75: { // cmp abx
732 CMP(abs_x());
733 break;
734 }
735 case 0x76: { // cmp aby
736 CMP(abs_y());
737 break;
738 }
739 case 0x77: { // cmp idy
740 CMP(idy());
741 break;
742 }
743 case 0x78: { // cmp d, #i
744 uint8_t imm = ReadOpcode();
745 uint16_t adr = (PSW.P << 8) | ReadOpcode();
746 uint8_t val = read(adr);
747 callbacks_.idle(false); // Add missing cycle
748 callbacks_.idle(false); // Add missing cycle
749 int result = val - imm;
750 PSW.C = (val >= imm);
751 PSW.Z = (result == 0);
752 PSW.N = (result & 0x80);
753 break;
754 }
755 case 0x79: { // cmpm ind, ind
756 uint8_t src = 0;
757 uint16_t dst = ind_ind(&src);
758 CMPM(dst, src);
759 break;
760 }
761 case 0x7a: { // addw dp
762 uint16_t low = 0;
763 uint16_t high = dp_word(&low);
764 uint8_t vall = read(low);
765 callbacks_.idle(false);
766 uint16_t value = vall | (read(high) << 8);
767 uint16_t ya = A | (Y << 8);
768 int result = ya + value;
769 PSW.V = (ya & 0x8000) == (value & 0x8000) &&
770 (value & 0x8000) != (result & 0x8000);
771 PSW.H = ((ya & 0xfff) + (value & 0xfff)) > 0xfff;
772 PSW.C = result > 0xffff;
773 PSW.Z = (result & 0xffff) == 0;
774 PSW.N = result & 0x8000;
775 A = result & 0xff;
776 Y = result >> 8;
777 break;
778 }
779 case 0x7b: { // ror dpx
780 ROR(dpx());
781 break;
782 }
783 case 0x7c: { // rora imp
784 read(PC);
785 bool newC = A & 1;
786 A = (A >> 1) | (PSW.C << 7);
787 PSW.C = newC;
788 PSW.Z = (A == 0);
789 PSW.N = (A & 0x80);
790 break;
791 }
792 case 0x7d: { // movax imp
793 read(PC);
794 A = X;
795 PSW.Z = (A == 0);
796 PSW.N = (A & 0x80);
797 break;
798 }
799 case 0x7e: { // cmpy dp
800 CMPY(dp());
801 break;
802 }
803 case 0x7f: { // reti imp
804 read(PC);
805 callbacks_.idle(false);
807 PC = pull_word();
808 break;
809 }
810 case 0x80: { // setc imp
811 read(PC);
812 PSW.C = true;
813 break;
814 }
815 case 0x84: { // adc dp
816 ADC(dp());
817 break;
818 }
819 case 0x85: { // adc abs
820 ADC(abs());
821 break;
822 }
823 case 0x86: { // adc ind
824 ADC(ind());
825 break;
826 }
827 case 0x87: { // adc idx
828 ADC(idx());
829 break;
830 }
831 case 0x88: { // adc imm
832 ADC(imm());
833 break;
834 }
835 case 0x89: { // adcm dp, dp
836 uint8_t src = 0;
837 uint16_t dst = dp_dp(&src);
838 ADCM(dst, src);
839 break;
840 }
841 case 0x8a: { // eor1 abs.bit
842 uint16_t adr = 0;
843 uint8_t bit = abs_bit(&adr);
844 PSW.C = PSW.C ^ ((read(adr) >> bit) & 1);
845 callbacks_.idle(false);
846 break;
847 }
848 case 0x8b: { // dec dp
849 DEC(dp());
850 break;
851 }
852 case 0x8c: { // dec abs
853 DEC(abs());
854 break;
855 }
856 case 0x8d: { // movy imm
857 MOVY(imm());
858 break;
859 }
860 case 0x8e: { // popp imp
861 read(PC);
862 callbacks_.idle(false);
864 break;
865 }
866 case 0x8f: { // movm dp, imm
867 uint8_t val = 0;
868 uint16_t dst = dp_imm(&val);
869 read(dst);
870 write(dst, val);
871 break;
872 }
873 case 0x90: { // bcc rel
875 break;
876 }
877 case 0x94: { // adc dpx
878 ADC(dpx());
879 break;
880 }
881 case 0x95: { // adc abx
882 ADC(abs_x());
883 break;
884 }
885 case 0x96: { // adc aby
886 ADC(abs_y());
887 break;
888 }
889 case 0x97: { // adc idy
890 ADC(idy());
891 break;
892 }
893 case 0x98: { // adcm dp, imm
894 uint8_t src = 0;
895 uint16_t dst = dp_imm(&src);
896 ADCM(dst, src);
897 break;
898 }
899 case 0x99: { // adcm ind, ind
900 uint8_t src = 0;
901 uint16_t dst = ind_ind(&src);
902 ADCM(dst, src);
903 break;
904 }
905 case 0x9a: { // subw dp
906 uint16_t low = 0;
907 uint16_t high = dp_word(&low);
908 uint8_t vall = read(low);
909 callbacks_.idle(false);
910 uint16_t value = (vall | (read(high) << 8)) ^ 0xffff;
911 uint16_t ya = A | (Y << 8);
912 int result = ya + value + 1;
913 PSW.V = (ya & 0x8000) == (value & 0x8000) &&
914 (value & 0x8000) != (result & 0x8000);
915 PSW.H = ((ya & 0xfff) + (value & 0xfff) + 1) > 0xfff;
916 PSW.C = result > 0xffff;
917 PSW.Z = (result & 0xffff) == 0;
918 PSW.N = result & 0x8000;
919 A = result & 0xff;
920 Y = result >> 8;
921 break;
922 }
923 case 0x9b: { // dec dpx
924 DEC(dpx());
925 break;
926 }
927 case 0x9c: { // deca imp
928 read(PC);
929 A--;
930 PSW.Z = (A == 0);
931 PSW.N = (A & 0x80);
932 break;
933 }
934 case 0x9d: { // movxp imp
935 read(PC);
936 X = SP;
937 PSW.Z = (X == 0);
938 PSW.N = (X & 0x80);
939 break;
940 }
941 case 0x9e: { // div imp
942 read(PC);
943 for (int i = 0; i < 10; i++) callbacks_.idle(false);
944 PSW.H = (X & 0xf) <= (Y & 0xf);
945 int yva = (Y << 8) | A;
946 int x = X << 9;
947 for (int i = 0; i < 9; i++) {
948 yva <<= 1;
949 yva |= (yva & 0x20000) ? 1 : 0;
950 yva &= 0x1ffff;
951 if (yva >= x) yva ^= 1;
952 if (yva & 1) yva -= x;
953 yva &= 0x1ffff;
954 }
955 Y = yva >> 9;
956 PSW.V = yva & 0x100;
957 A = yva & 0xff;
958 PSW.Z = (A == 0);
959 PSW.N = (A & 0x80);
960 break;
961 }
962 case 0x9f: { // xcn imp
963 read(PC);
964 callbacks_.idle(false);
965 callbacks_.idle(false);
966 callbacks_.idle(false);
967 A = (A >> 4) | (A << 4);
968 PSW.Z = (A == 0);
969 PSW.N = (A & 0x80);
970 break;
971 }
972 case 0xa0: { // ei imp
973 read(PC);
974 callbacks_.idle(false);
975 PSW.I = true;
976 break;
977 }
978 case 0xa4: { // sbc dp
979 SBC(dp());
980 break;
981 }
982 case 0xa5: { // sbc abs
983 SBC(abs());
984 break;
985 }
986 case 0xa6: { // sbc ind
987 SBC(ind());
988 break;
989 }
990 case 0xa7: { // sbc idx
991 SBC(idx());
992 break;
993 }
994 case 0xa8: { // sbc imm
995 SBC(imm());
996 break;
997 }
998 case 0xa9: { // sbcm dp, dp
999 uint8_t src = 0;
1000 uint16_t dst = dp_dp(&src);
1001 SBCM(dst, src);
1002 break;
1003 }
1004 case 0xaa: { // mov1 abs.bit
1005 uint16_t adr = 0;
1006 uint8_t bit = abs_bit(&adr);
1007 PSW.C = (read(adr) >> bit) & 1;
1008 break;
1009 }
1010 case 0xab: { // inc dp
1011 INC(dp());
1012 break;
1013 }
1014 case 0xac: { // inc abs
1015 INC(abs());
1016 break;
1017 }
1018 case 0xad: { // cmpy imm
1019 CMPY(imm());
1020 break;
1021 }
1022 case 0xae: { // popa imp
1023 read(PC);
1024 callbacks_.idle(false);
1025 A = pull_byte();
1026 break;
1027 }
1028 case 0xaf: { // movs ind+
1029 uint16_t adr = ind_p();
1030 callbacks_.idle(false);
1031 write(adr, A);
1032 break;
1033 }
1034 case 0xb0: { // bcs rel
1036 break;
1037 }
1038 case 0xb4: { // sbc dpx
1039 SBC(dpx());
1040 break;
1041 }
1042 case 0xb5: { // sbc abx
1043 SBC(abs_x());
1044 break;
1045 }
1046 case 0xb6: { // sbc aby
1047 SBC(abs_y());
1048 break;
1049 }
1050 case 0xb7: { // sbc idy
1051 SBC(idy());
1052 break;
1053 }
1054 case 0xb8: { // sbcm dp, imm
1055 uint8_t src = 0;
1056 uint16_t dst = dp_imm(&src);
1057 SBCM(dst, src);
1058 break;
1059 }
1060 case 0xb9: { // sbcm ind, ind
1061 uint8_t src = 0;
1062 uint16_t dst = ind_ind(&src);
1063 SBCM(dst, src);
1064 break;
1065 }
1066 case 0xba: { // movw dp
1067 uint16_t low = 0;
1068 uint16_t high = dp_word(&low);
1069 uint8_t vall = read(low);
1070 callbacks_.idle(false);
1071 uint16_t val = vall | (read(high) << 8);
1072 A = val & 0xff;
1073 Y = val >> 8;
1074 PSW.Z = val == 0;
1075 PSW.N = val & 0x8000;
1076 break;
1077 }
1078 case 0xbb: { // inc dpx
1079 INC(dpx());
1080 break;
1081 }
1082 case 0xbc: { // inca imp
1083 read(PC);
1084 A++;
1085 PSW.Z = (A == 0);
1086 PSW.N = (A & 0x80);
1087 ;
1088 break;
1089 }
1090 case 0xbd: { // movpx imp
1091 read(PC);
1092 SP = X;
1093 break;
1094 }
1095 case 0xbe: { // das imp
1096 read(PC);
1097 callbacks_.idle(false);
1098 if (A > 0x99 || !PSW.C) {
1099 A -= 0x60;
1100 PSW.C = false;
1101 }
1102 if ((A & 0xf) > 9 || !PSW.H) {
1103 A -= 6;
1104 }
1105 PSW.Z = (A == 0);
1106 PSW.N = (A & 0x80);
1107 break;
1108 }
1109 case 0xbf: { // mov ind+
1110 uint16_t adr = ind_p();
1111 A = read(adr);
1112 callbacks_.idle(false);
1113 PSW.Z = (A == 0);
1114 PSW.N = (A & 0x80);
1115 break;
1116 }
1117 case 0xc0: { // di imp
1118 read(PC);
1119 callbacks_.idle(false);
1120 PSW.I = false;
1121 break;
1122 }
1123 case 0xc4: { // movs dp
1124 uint16_t adr = dp();
1125 MOVS(adr);
1126 break;
1127 }
1128 case 0xc5: { // movs abs
1129 MOVS(abs());
1130 break;
1131 }
1132 case 0xc6: { // movs ind
1133 MOVS(ind());
1134 break;
1135 }
1136 case 0xc7: { // movs idx
1137 MOVS(idx());
1138 break;
1139 }
1140 case 0xc8: { // cmpx imm
1141 CMPX(imm());
1142 break;
1143 }
1144 case 0xc9: { // movsx abs
1145 MOVSX(abs());
1146 break;
1147 }
1148 case 0xca: { // mov1s abs.bit
1149 uint16_t adr = 0;
1150 uint8_t bit = abs_bit(&adr);
1151 uint8_t result = (read(adr) & (~(1 << bit))) | (PSW.C << bit);
1152 callbacks_.idle(false);
1153 write(adr, result);
1154 break;
1155 }
1156 case 0xcb: { // mov d, Y
1157 uint16_t adr = (PSW.P << 8) | ReadOpcode();
1158 read(adr);
1159 callbacks_.idle(false); // Add one extra cycle delay
1160 write(adr, Y);
1161 break;
1162 }
1163 case 0xcc: { // movsy abs
1164 MOVSY(abs());
1165 break;
1166 }
1167 case 0xcd: { // movx imm
1168 MOVX(imm());
1169 break;
1170 }
1171 case 0xce: { // popx imp
1172 read(PC);
1173 callbacks_.idle(false);
1174 X = pull_byte();
1175 break;
1176 }
1177 case 0xcf: { // mul imp
1178 read(PC);
1179 for (int i = 0; i < 7; i++) callbacks_.idle(false);
1180 uint16_t result = A * Y;
1181 A = result & 0xff;
1182 Y = result >> 8;
1183 PSW.Z = (Y == 0);
1184 PSW.N = (Y & 0x80);
1185 break;
1186 }
1187 case 0xd0: { // bne rel
1188 DoBranch(ReadOpcode(), !PSW.Z);
1189 break;
1190 }
1191 case 0xd4: { // movs dpx
1192 MOVS(dpx());
1193 break;
1194 }
1195 case 0xd5: { // movs abx
1196 MOVS(abs_x());
1197 break;
1198 }
1199 case 0xd6: { // movs aby
1200 MOVS(abs_y());
1201 break;
1202 }
1203 case 0xd7: { // movs idy
1204 // CRITICAL: Only call idy() once in bstep=0, reuse saved address in bstep=1
1205 if (bstep == 0) {
1206 adr = idy(); // Save address for bstep=1
1207 }
1208 MOVS(adr); // Use saved address
1209 break;
1210 }
1211 case 0xd8: { // movsx dp
1212 MOVSX(dp());
1213 break;
1214 }
1215 case 0xd9: { // movsx dpy
1216 MOVSX(dp_y());
1217 break;
1218 }
1219 case 0xda: { // movws dp
1220 uint16_t low = 0;
1221 uint16_t high = dp_word(&low);
1222 read(low);
1223 write(low, A);
1224 write(high, Y);
1225 break;
1226 }
1227 case 0xdb: { // movsy dpx
1228 MOVSY(dpx());
1229 break;
1230 }
1231 case 0xdc: { // decy imp
1232 read(PC);
1233 Y--;
1234 PSW.Z = (Y == 0);
1235 PSW.N = (Y & 0x80);
1236 break;
1237 }
1238 case 0xdd: { // movay imp
1239 read(PC);
1240 A = Y;
1241 PSW.Z = (A == 0);
1242 PSW.N = (A & 0x80);
1243 break;
1244 }
1245 case 0xde: { // cbne dpx, rel
1246 uint8_t val = read(dpx()) ^ 0xff;
1247 callbacks_.idle(false);
1248 uint8_t result = A + val + 1;
1249 DoBranch(ReadOpcode(), result != 0);
1250 break;
1251 }
1252 case 0xdf: { // daa imp
1253 read(PC);
1254 callbacks_.idle(false);
1255 if (A > 0x99 || PSW.C) {
1256 A += 0x60;
1257 PSW.C = true;
1258 }
1259 if ((A & 0xf) > 9 || PSW.H) {
1260 A += 6;
1261 }
1262 PSW.Z = (A == 0);
1263 PSW.N = (A & 0x80);
1264 break;
1265 }
1266 case 0xe0: { // clrv imp
1267 read(PC);
1268 PSW.V = false;
1269 PSW.H = false;
1270 break;
1271 }
1272 case 0xe4: { // mov A, dp
1273 uint16_t adr = (PSW.P << 8) | ReadOpcode();
1274 A = read(adr);
1275 PSW.Z = (A == 0);
1276 PSW.N = (A & 0x80);
1277 break;
1278 }
1279 case 0xe5: { // mov A, abs
1280 uint16_t adr = ReadOpcodeWord();
1281 A = read(adr);
1282 PSW.Z = (A == 0);
1283 PSW.N = (A & 0x80);
1284 break;
1285 }
1286 case 0xe6: { // mov A, (X)
1287 uint16_t adr = X;
1288 A = read(adr);
1289 PSW.Z = (A == 0);
1290 PSW.N = (A & 0x80);
1291 break;
1292 }
1293 case 0xe7: { // mov A, [dp+X]
1294 uint16_t dp_adr = (PSW.P << 8) | ReadOpcode();
1295 callbacks_.idle(false);
1296 uint16_t adr = read_word(dp_adr + X);
1297 A = read(adr);
1298 PSW.Z = (A == 0);
1299 PSW.N = (A & 0x80);
1300 break;
1301 }
1302 case 0xe8: { // mov A, #imm
1303 A = ReadOpcode();
1304 PSW.Z = (A == 0);
1305 PSW.N = (A & 0x80);
1306 break;
1307 }
1308 case 0xe9: { // movx abs
1309 uint16_t adr = ReadOpcodeWord();
1310 X = read(adr);
1311 PSW.Z = (X == 0);
1312 PSW.N = (X & 0x80);
1313 break;
1314 }
1315 case 0xeb: { // movy dp
1316 uint16_t adr = (PSW.P << 8) | ReadOpcode();
1317 callbacks_.idle(false); // Add missing cycle
1318 Y = read(adr);
1319 PSW.Z = (Y == 0);
1320 PSW.N = (Y & 0x80);
1321 break;
1322 }
1323 case 0xec: { // movy abs
1324 uint16_t adr = ReadOpcodeWord();
1325 Y = read(adr);
1326 PSW.Z = (Y == 0);
1327 PSW.N = (Y & 0x80);
1328 break;
1329 }
1330 case 0xed: { // notc imp
1331 read(PC);
1332 callbacks_.idle(false);
1333 PSW.C = !PSW.C;
1334 break;
1335 }
1336 case 0xee: { // popy imp
1337 read(PC);
1338 callbacks_.idle(false);
1339 Y = pull_byte();
1340 break;
1341 }
1342 case 0xef: { // sleep imp
1343 // Emulate low-power idle without halting the core permanently.
1344 // Advance timers/DSP via idle callbacks, but do not set stopped_.
1345 static int sleep_log = 0;
1346 if (sleep_log++ < 5) {
1347 LOG_DEBUG("SPC", "SLEEP executed at PC=$%04X - entering low power mode", PC - 1);
1348 }
1349 read(PC);
1350 for (int i = 0; i < 4; ++i) callbacks_.idle(true);
1351 break;
1352 }
1353 case 0xf0: { // beq rel
1355 break;
1356 }
1357 case 0xf4: { // mov dpx
1358 MOV(dpx());
1359 break;
1360 }
1361 case 0xf5: { // mov abx
1362 MOV(abs_x());
1363 break;
1364 }
1365 case 0xf6: { // mov aby
1366 MOV(abs_y());
1367 break;
1368 }
1369 case 0xf7: { // mov idy
1370 MOV(idy());
1371 break;
1372 }
1373 case 0xf8: { // movx dp
1374 MOVX(dp());
1375 break;
1376 }
1377 case 0xf9: { // movx dpy
1378 MOVX(dp_y());
1379 break;
1380 }
1381 case 0xfa: { // movm dp, dp
1382 uint8_t val = 0;
1383 uint16_t dst = dp_dp(&val);
1384 write(dst, val);
1385 break;
1386 }
1387 case 0xfb: { // movy dpx
1388 MOVY(dpx());
1389 break;
1390 }
1391 case 0xfc: { // incy imp
1392 uint8_t old_y = Y;
1393 read(PC);
1394 Y++;
1395 PSW.Z = (Y == 0);
1396 PSW.N = (Y & 0x80);
1397 // Log Y increment in transfer loop for first few iterations only
1398 static int incy_log = 0;
1399 if (PC >= 0xFFE4 && PC <= 0xFFE6 && incy_log++ < 10) {
1400 LOG_DEBUG("SPC", "INC Y executed at PC=$%04X: Y changed from $%02X to $%02X (Z=%d N=%d)",
1401 PC - 1, old_y, Y, PSW.Z, PSW.N);
1402 }
1403 break;
1404 }
1405 case 0xfd: { // movya imp
1406 read(PC);
1407 Y = A;
1408 PSW.Z = (Y == 0);
1409 PSW.N = (Y & 0x80);
1410 break;
1411 }
1412 case 0xfe: { // dbnzy rel
1413 read(PC);
1414 callbacks_.idle(false);
1415 Y--;
1416 DoBranch(ReadOpcode(), Y != 0);
1417 break;
1418 }
1419 case 0xff: { // stop imp
1420 read(PC);
1421 callbacks_.idle(false);
1422 stopped_ = true;
1423 break;
1424 }
1425
1426 default:
1427 throw std::runtime_error("Unknown SPC opcode: " + std::to_string(opcode));
1428 break;
1429 }
1430}
1431
1432void Spc700::LogInstruction(uint16_t initial_pc, uint8_t opcode) {
1433 const std::string& mnemonic = spc_opcode_map.at(opcode);
1434
1435 std::stringstream ss;
1436 ss << "$" << std::hex << std::setw(4) << std::setfill('0') << initial_pc
1437 << ": 0x" << std::setw(2) << std::setfill('0')
1438 << static_cast<int>(opcode) << " " << mnemonic
1439 << " A:" << std::setw(2) << std::setfill('0') << std::hex
1440 << static_cast<int>(A)
1441 << " X:" << std::setw(2) << std::setfill('0') << std::hex
1442 << static_cast<int>(X)
1443 << " Y:" << std::setw(2) << std::setfill('0') << std::hex
1444 << static_cast<int>(Y);
1445
1447 ss.str());
1448}
1449
1450} // namespace emu
1451} // namespace yaze
const std::unordered_map< uint8_t, std::string > spc_opcode_map
Definition opcodes.h:7
void CMPM(uint16_t dst, uint8_t value)
void MOVS(uint16_t adr)
uint16_t pull_word()
Definition spc700.h:205
uint8_t read(uint16_t address)
Definition spc700.h:154
void MOVSX(uint16_t adr)
uint16_t ind_p()
Definition addressing.cc:73
uint16_t adr
Definition spc700.h:80
void MOVX(uint16_t adr)
uint16_t dp_y()
Definition addressing.cc:25
uint32_t bstep
Definition spc700.h:79
uint8_t abs_bit(uint16_t *adr)
Definition addressing.cc:61
void SBC(uint16_t adr)
void CMPY(uint16_t adr)
void ANDM(uint16_t dest, uint8_t operand)
uint8_t opcode
Definition spc700.h:77
void EORM(uint16_t dest, uint8_t operand)
void DEC(uint16_t operand)
uint16_t ind_ind(uint8_t *srcVal)
Definition addressing.cc:55
uint32_t step
Definition spc700.h:78
void MOV(uint16_t adr)
void SBCM(uint16_t &dest, uint8_t operand)
void Reset(bool hard=false)
Definition spc700.cc:16
void LSR(uint16_t adr)
void push_byte(uint8_t value)
Definition spc700.h:190
void CMPX(uint16_t adr)
uint8_t FlagsToByte(Flags flags)
Definition spc700.h:121
bool reset_wanted_
Definition spc700.h:75
void LogInstruction(uint16_t initial_pc, uint8_t opcode)
Definition spc700.cc:1432
uint16_t abs_y()
Definition addressing.cc:37
uint16_t dp_imm(uint8_t *srcVal)
Definition addressing.cc:50
uint16_t ReadOpcodeWord()
Definition spc700.h:168
void CMP(uint16_t adr)
uint16_t dp_dp(uint8_t *src)
void ROL(uint16_t operand)
void write(uint16_t address, uint8_t value)
Definition spc700.h:186
void INC(uint16_t adr)
uint16_t abs_x()
Definition addressing.cc:31
uint16_t dp_word(uint16_t *low)
Definition addressing.cc:67
void push_word(uint16_t value)
Definition spc700.h:195
void ADC(uint16_t adr)
void EOR(uint16_t adr)
void ROR(uint16_t adr)
uint16_t read_word(uint16_t address)
Definition spc700.h:156
void ASL(uint16_t operand)
void OR(uint16_t adr)
void ADCM(uint16_t &dest, uint8_t operand)
int last_opcode_cycles_
Definition spc700.h:88
void DoBranch(uint8_t value, bool check)
Definition spc700.h:174
uint16_t PC
Definition spc700.h:106
void AND(uint16_t adr)
uint8_t pull_byte()
Definition spc700.h:200
ApuCallbacks callbacks_
Definition spc700.h:71
void MOVSY(uint16_t adr)
void ExecuteInstructions(uint8_t opcode)
Definition spc700.cc:160
void MOVY(uint16_t adr)
void ORM(uint16_t dest, uint8_t operand)
uint16_t ind()
Definition addressing.cc:8
uint8_t ReadOpcode()
Definition spc700.h:163
Flags ByteToFlags(uint8_t byte)
Definition spc700.h:126
void RunOpcode()
Definition spc700.cc:74
static LogManager & instance()
Definition log.cc:29
void log(LogLevel level, absl::string_view category, absl::string_view message)
The primary logging function.
Definition log.cc:74
#define LOG_DEBUG(category, format,...)
Definition log.h:104
Main namespace for the application.
std::function< void(bool)> idle
Definition spc700.h:55
std::function< uint8_t(uint16_t)> read
Definition spc700.h:54