1 /*
2 * CPUC64_SC.cpp - Single-cycle 6510 (C64) emulation
3 *
4 * Frodo (C) 1994-1997,2002 Christian Bauer
5 *
6
7 *
8 * Notes:
9 * ------
10 *
11 * Opcode execution:
12 * - All opcodes are resolved into single clock cycles. There is one
13 * switch case for each cycle.
14 * - The "state" variable specifies the routine to be executed in the
15 * next cycle. Its upper 8 bits contain the current opcode, its lower
16 * 8 bits contain the cycle number (0..7) within the opcode.
17 * - Opcodes are fetched in cycle 0 (state = 0)
18 * - The states 0x0010..0x0027 are used for interrupts
19 * - There is exactly one memory access in each clock cycle
20 *
21 * Memory configurations:
22 *
23 * $01 $a000-$bfff $d000-$dfff $e000-$ffff
24 * -----------------------------------------------
25 * 0 RAM RAM RAM
26 * 1 RAM Char ROM RAM
27 * 2 RAM Char ROM Kernal ROM
28 * 3 Basic ROM Char ROM Kernal ROM
29 * 4 RAM RAM RAM
30 * 5 RAM I/O RAM
31 * 6 RAM I/O Kernal ROM
32 * 7 Basic ROM I/O Kernal ROM
33 *
34 * - All memory accesses are done with the read_byte() and
35 * write_byte() functions which also do the memory address
36 * decoding.
37 * - If a write occurs to addresses 0 or 1, new_config is
38 * called to check whether the memory configuration has
39 * changed
40 * - The possible interrupt sources are:
41 * INT_VICIRQ: I flag is checked, jump to ($fffe)
42 * INT_CIAIRQ: I flag is checked, jump to ($fffe)
43 * INT_NMI: Jump to ($fffa)
44 * INT_RESET: Jump to ($fffc)
45 * - The z_flag variable has the inverse meaning of the
46 * 6510 Z flag
47 * - Only the highest bit of the n_flag variable is used
48 * - The $f2 opcode that would normally crash the 6510 is
49 * used to implement emulator-specific functions, mainly
50 * those for the IEC routines
51 *
52 * Incompatibilities:
53 * ------------------
54 *
55 * - If BA is low and AEC is high, read accesses should occur
56 */
57
58 #include "sysdeps.h"
59
60 #include "CPUC64.h"
61 #include "CPU_common.h"
62 #include "C64.h"
63 #include "VIC.h"
64 #include "SID.h"
65 #include "CIA.h"
66 #include "REU.h"
67 #include "IEC.h"
68 #include "Display.h"
69 #include "Version.h"
70
71
72 enum {
73 INT_RESET = 3
74 };
75
76
77 /*
78 * 6510 constructor: Initialize registers
79 */
80
MOS6510(C64 * c64,uint8 * Ram,uint8 * Basic,uint8 * Kernal,uint8 * Char,uint8 * Color)81 MOS6510::MOS6510(C64 *c64, uint8 *Ram, uint8 *Basic, uint8 *Kernal, uint8 *Char, uint8 *Color)
82 : the_c64(c64), ram(Ram), basic_rom(Basic), kernal_rom(Kernal), char_rom(Char), color_ram(Color)
83 {
84 a = x = y = 0;
85 sp = 0xff;
86 n_flag = z_flag = 0;
87 v_flag = d_flag = c_flag = false;
88 i_flag = true;
89 dfff_byte = 0x55;
90 BALow = false;
91 first_irq_cycle = first_nmi_cycle = 0;
92 }
93
94
95 /*
96 * Reset CPU asynchronously
97 */
98
AsyncReset(void)99 void MOS6510::AsyncReset(void)
100 {
101 interrupt.intr[INT_RESET] = true;
102 }
103
104
105 /*
106 * Raise NMI asynchronously (Restore key)
107 */
108
AsyncNMI(void)109 void MOS6510::AsyncNMI(void)
110 {
111 if (!nmi_state)
112 interrupt.intr[INT_NMI] = true;
113 }
114
115
116 /*
117 * Get 6510 register state
118 */
119
GetState(MOS6510State * s)120 void MOS6510::GetState(MOS6510State *s)
121 {
122 s->a = a;
123 s->x = x;
124 s->y = y;
125
126 s->p = 0x20 | (n_flag & 0x80);
127 if (v_flag) s->p |= 0x40;
128 if (d_flag) s->p |= 0x08;
129 if (i_flag) s->p |= 0x04;
130 if (!z_flag) s->p |= 0x02;
131 if (c_flag) s->p |= 0x01;
132
133 s->ddr = ddr;
134 s->pr = pr;
135
136 s->pc = pc;
137 s->sp = sp | 0x0100;
138
139 s->intr[INT_VICIRQ] = interrupt.intr[INT_VICIRQ];
140 s->intr[INT_CIAIRQ] = interrupt.intr[INT_CIAIRQ];
141 s->intr[INT_NMI] = interrupt.intr[INT_NMI];
142 s->intr[INT_RESET] = interrupt.intr[INT_RESET];
143 s->nmi_state = nmi_state;
144 s->dfff_byte = dfff_byte;
145 s->instruction_complete = (state == 0);
146 }
147
148
149 /*
150 * Restore 6510 state
151 */
152
SetState(MOS6510State * s)153 void MOS6510::SetState(MOS6510State *s)
154 {
155 a = s->a;
156 x = s->x;
157 y = s->y;
158
159 n_flag = s->p;
160 v_flag = s->p & 0x40;
161 d_flag = s->p & 0x08;
162 i_flag = s->p & 0x04;
163 z_flag = !(s->p & 0x02);
164 c_flag = s->p & 0x01;
165
166 ddr = s->ddr;
167 pr = s->pr;
168 new_config();
169
170 pc = s->pc;
171 sp = s->sp & 0xff;
172
173 interrupt.intr[INT_VICIRQ] = s->intr[INT_VICIRQ];
174 interrupt.intr[INT_CIAIRQ] = s->intr[INT_CIAIRQ];
175 interrupt.intr[INT_NMI] = s->intr[INT_NMI];
176 interrupt.intr[INT_RESET] = s->intr[INT_RESET];
177 nmi_state = s->nmi_state;
178 dfff_byte = s->dfff_byte;
179 if (s->instruction_complete)
180 state = 0;
181 }
182
183
184 /*
185 * Memory configuration has probably changed
186 */
187
new_config(void)188 void MOS6510::new_config(void)
189 {
190 uint8 port = ~ddr | pr;
191
192 basic_in = (port & 3) == 3;
193 kernal_in = port & 2;
194 char_in = (port & 3) && !(port & 4);
195 io_in = (port & 3) && (port & 4);
196 }
197
198
199 /*
200 * Read a byte from I/O / ROM space
201 */
202
read_byte_io(uint16 adr)203 inline uint8 MOS6510::read_byte_io(uint16 adr)
204 {
205 switch (adr >> 12) {
206 case 0xa:
207 case 0xb:
208 if (basic_in)
209 return basic_rom[adr & 0x1fff];
210 else
211 return ram[adr];
212 case 0xc:
213 return ram[adr];
214 case 0xd:
215 if (io_in)
216 switch ((adr >> 8) & 0x0f) {
217 case 0x0: // VIC
218 case 0x1:
219 case 0x2:
220 case 0x3:
221 return TheVIC->ReadRegister(adr & 0x3f);
222 case 0x4: // SID
223 case 0x5:
224 case 0x6:
225 case 0x7:
226 return TheSID->ReadRegister(adr & 0x1f);
227 case 0x8: // Color RAM
228 case 0x9:
229 case 0xa:
230 case 0xb:
231 return color_ram[adr & 0x03ff] & 0x0f | TheVIC->LastVICByte & 0xf0;
232 case 0xc: // CIA 1
233 return TheCIA1->ReadRegister(adr & 0x0f);
234 case 0xd: // CIA 2
235 return TheCIA2->ReadRegister(adr & 0x0f);
236 case 0xe: // REU/Open I/O
237 case 0xf:
238 if ((adr & 0xfff0) == 0xdf00)
239 return TheREU->ReadRegister(adr & 0x0f);
240 else if (adr < 0xdfa0)
241 return TheVIC->LastVICByte;
242 else
243 return read_emulator_id(adr & 0x7f);
244 }
245 else if (char_in)
246 return char_rom[adr & 0x0fff];
247 else
248 return ram[adr];
249 case 0xe:
250 case 0xf:
251 if (kernal_in)
252 return kernal_rom[adr & 0x1fff];
253 else
254 return ram[adr];
255 default: // Can't happen
256 return 0;
257 }
258 }
259
260
261 /*
262 * Read a byte from the CPU's address space
263 */
264
265 #ifdef __i386
266 inline
267 #endif
read_byte(uint16 adr)268 uint8 MOS6510::read_byte(uint16 adr)
269 {
270 if (adr < 0xa000) {
271 if (adr >= 2)
272 return ram[adr];
273 else if (adr == 0)
274 return ddr;
275 else
276 return (ddr & pr) | (~ddr & 0x17);
277 } else
278 return read_byte_io(adr);
279 }
280
281
282 /*
283 * $dfa0-$dfff: Emulator identification
284 */
285
286 const char frodo_id[0x5c] = "FRODO\r(C) 1994-1997 CHRISTIAN BAUER";
287
read_emulator_id(uint16 adr)288 uint8 MOS6510::read_emulator_id(uint16 adr)
289 {
290 switch (adr) {
291 case 0x7c: // $dffc: revision
292 return FRODO_REVISION << 4;
293 case 0x7d: // $dffd: version
294 return FRODO_VERSION;
295 case 0x7e: // $dffe returns 'F' (Frodo ID)
296 return 'F';
297 case 0x7f: // $dfff alternates between $55 and $aa
298 dfff_byte = ~dfff_byte;
299 return dfff_byte;
300 default:
301 return frodo_id[adr - 0x20];
302 }
303 }
304
305
306 /*
307 * Read a word (little-endian) from the CPU's address space
308 */
309
read_word(uint16 adr)310 inline uint16 MOS6510::read_word(uint16 adr)
311 {
312 return read_byte(adr) | (read_byte(adr+1) << 8);
313 }
314
315
316 /*
317 * Write a byte to I/O space
318 */
319
write_byte_io(uint16 adr,uint8 byte)320 inline void MOS6510::write_byte_io(uint16 adr, uint8 byte)
321 {
322 if (adr >= 0xe000) {
323 ram[adr] = byte;
324 if (adr == 0xff00)
325 TheREU->FF00Trigger();
326 } else if (io_in)
327 switch ((adr >> 8) & 0x0f) {
328 case 0x0: // VIC
329 case 0x1:
330 case 0x2:
331 case 0x3:
332 TheVIC->WriteRegister(adr & 0x3f, byte);
333 return;
334 case 0x4: // SID
335 case 0x5:
336 case 0x6:
337 case 0x7:
338 TheSID->WriteRegister(adr & 0x1f, byte);
339 return;
340 case 0x8: // Color RAM
341 case 0x9:
342 case 0xa:
343 case 0xb:
344 color_ram[adr & 0x03ff] = byte & 0x0f;
345 return;
346 case 0xc: // CIA 1
347 TheCIA1->WriteRegister(adr & 0x0f, byte);
348 return;
349 case 0xd: // CIA 2
350 TheCIA2->WriteRegister(adr & 0x0f, byte);
351 return;
352 case 0xe: // REU/Open I/O
353 case 0xf:
354 if ((adr & 0xfff0) == 0xdf00)
355 TheREU->WriteRegister(adr & 0x0f, byte);
356 return;
357 }
358 else
359 ram[adr] = byte;
360 }
361
362
363 /*
364 * Write a byte to the CPU's address space
365 */
366
write_byte(uint16 adr,uint8 byte)367 void MOS6510::write_byte(uint16 adr, uint8 byte)
368 {
369 if (adr < 0xd000) {
370 if (adr >= 2)
371 ram[adr] = byte;
372 else if (adr == 0) {
373 ddr = byte;
374 ram[0] = TheVIC->LastVICByte;
375 new_config();
376 } else {
377 pr = byte;
378 ram[1] = TheVIC->LastVICByte;
379 new_config();
380 }
381 } else
382 write_byte_io(adr, byte);
383 }
384
385
386 /*
387 * Read byte from 6510 address space with special memory config (used by SAM)
388 */
389
ExtReadByte(uint16 adr)390 uint8 MOS6510::ExtReadByte(uint16 adr)
391 {
392 // Save old memory configuration
393 bool bi = basic_in, ki = kernal_in, ci = char_in, ii = io_in;
394
395 // Set new configuration
396 basic_in = (ExtConfig & 3) == 3;
397 kernal_in = ExtConfig & 2;
398 char_in = (ExtConfig & 3) && ~(ExtConfig & 4);
399 io_in = (ExtConfig & 3) && (ExtConfig & 4);
400
401 // Read byte
402 uint8 byte = read_byte(adr);
403
404 // Restore old configuration
405 basic_in = bi; kernal_in = ki; char_in = ci; io_in = ii;
406
407 return byte;
408 }
409
410
411 /*
412 * Write byte to 6510 address space with special memory config (used by SAM)
413 */
414
ExtWriteByte(uint16 adr,uint8 byte)415 void MOS6510::ExtWriteByte(uint16 adr, uint8 byte)
416 {
417 // Save old memory configuration
418 bool bi = basic_in, ki = kernal_in, ci = char_in, ii = io_in;
419
420 // Set new configuration
421 basic_in = (ExtConfig & 3) == 3;
422 kernal_in = ExtConfig & 2;
423 char_in = (ExtConfig & 3) && ~(ExtConfig & 4);
424 io_in = (ExtConfig & 3) && (ExtConfig & 4);
425
426 // Write byte
427 write_byte(adr, byte);
428
429 // Restore old configuration
430 basic_in = bi; kernal_in = ki; char_in = ci; io_in = ii;
431 }
432
433
434 /*
435 * Read byte from 6510 address space with current memory config (used by REU)
436 */
437
REUReadByte(uint16 adr)438 uint8 MOS6510::REUReadByte(uint16 adr)
439 {
440 return read_byte(adr);
441 }
442
443
444 /*
445 * Write byte to 6510 address space with current memory config (used by REU)
446 */
447
REUWriteByte(uint16 adr,uint8 byte)448 void MOS6510::REUWriteByte(uint16 adr, uint8 byte)
449 {
450 write_byte(adr, byte);
451 }
452
453
454 /*
455 * Adc instruction
456 */
457
do_adc(uint8 byte)458 inline void MOS6510::do_adc(uint8 byte)
459 {
460 if (!d_flag) {
461 uint16 tmp;
462
463 // Binary mode
464 tmp = a + byte + (c_flag ? 1 : 0);
465 c_flag = tmp > 0xff;
466 v_flag = !((a ^ byte) & 0x80) && ((a ^ tmp) & 0x80);
467 z_flag = n_flag = a = tmp;
468
469 } else {
470 uint16 al, ah;
471
472 // Decimal mode
473 al = (a & 0x0f) + (byte & 0x0f) + (c_flag ? 1 : 0); // Calculate lower nybble
474 if (al > 9) al += 6; // BCD fixup for lower nybble
475
476 ah = (a >> 4) + (byte >> 4); // Calculate upper nybble
477 if (al > 0x0f) ah++;
478
479 z_flag = a + byte + (c_flag ? 1 : 0); // Set flags
480 n_flag = ah << 4; // Only highest bit used
481 v_flag = (((ah << 4) ^ a) & 0x80) && !((a ^ byte) & 0x80);
482
483 if (ah > 9) ah += 6; // BCD fixup for upper nybble
484 c_flag = ah > 0x0f; // Set carry flag
485 a = (ah << 4) | (al & 0x0f); // Compose result
486 }
487 }
488
489
490 /*
491 * Sbc instruction
492 */
493
do_sbc(uint8 byte)494 inline void MOS6510::do_sbc(uint8 byte)
495 {
496 uint16 tmp = a - byte - (c_flag ? 0 : 1);
497
498 if (!d_flag) {
499
500 // Binary mode
501 c_flag = tmp < 0x100;
502 v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80);
503 z_flag = n_flag = a = tmp;
504
505 } else {
506 uint16 al, ah;
507
508 // Decimal mode
509 al = (a & 0x0f) - (byte & 0x0f) - (c_flag ? 0 : 1); // Calculate lower nybble
510 ah = (a >> 4) - (byte >> 4); // Calculate upper nybble
511 if (al & 0x10) {
512 al -= 6; // BCD fixup for lower nybble
513 ah--;
514 }
515 if (ah & 0x10) ah -= 6; // BCD fixup for upper nybble
516
517 c_flag = tmp < 0x100; // Set flags
518 v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80);
519 z_flag = n_flag = tmp;
520
521 a = (ah << 4) | (al & 0x0f); // Compose result
522 }
523 }
524
525
526 /*
527 * Reset CPU
528 */
529
Reset(void)530 void MOS6510::Reset(void)
531 {
532 // Delete 'CBM80' if present
533 if (ram[0x8004] == 0xc3 && ram[0x8005] == 0xc2 && ram[0x8006] == 0xcd
534 && ram[0x8007] == 0x38 && ram[0x8008] == 0x30)
535 ram[0x8004] = 0;
536
537 // Initialize extra 6510 registers and memory configuration
538 ddr = pr = 0;
539 new_config();
540
541 // Clear all interrupt lines
542 interrupt.intr_any = 0;
543 nmi_state = false;
544
545 // Read reset vector
546 pc = read_word(0xfffc);
547 state = 0;
548 }
549
550
551 /*
552 * Illegal opcode encountered
553 */
554
illegal_op(uint8 op,uint16 at)555 void MOS6510::illegal_op(uint8 op, uint16 at)
556 {
557 char illop_msg[80];
558
559 sprintf(illop_msg, "Illegal opcode %02x at %04x.", op, at);
560 ShowRequester(illop_msg, "Reset");
561 the_c64->Reset();
562 Reset();
563 }
564
565
566 /*
567 * Emulate one 6510 clock cycle
568 */
569
570 // Read byte from memory
571 #define read_to(adr, to) \
572 if (BALow) \
573 return; \
574 to = read_byte(adr);
575
576 // Read byte from memory, throw away result
577 #define read_idle(adr) \
578 if (BALow) \
579 return; \
580 read_byte(adr);
581
EmulateCycle(void)582 void MOS6510::EmulateCycle(void)
583 {
584 uint8 data, tmp;
585
586 // Any pending interrupts in state 0 (opcode fetch)?
587 if (!state && interrupt.intr_any) {
588 if (interrupt.intr[INT_RESET])
589 Reset();
590 else if (interrupt.intr[INT_NMI] && (the_c64->CycleCounter-first_nmi_cycle >= 2)) {
591 interrupt.intr[INT_NMI] = false; // Simulate an edge-triggered input
592 state = 0x0010;
593 } else if ((interrupt.intr[INT_VICIRQ] || interrupt.intr[INT_CIAIRQ]) && (the_c64->CycleCounter-first_irq_cycle >= 2) && !i_flag)
594 state = 0x0008;
595 }
596
597 #include "CPU_emulcycle.i"
598
599 // Extension opcode
600 case O_EXT:
601 if (pc < 0xe000) {
602 illegal_op(0xf2, pc-1);
603 break;
604 }
605 switch (read_byte(pc++)) {
606 case 0x00:
607 ram[0x90] |= TheIEC->Out(ram[0x95], ram[0xa3] & 0x80);
608 c_flag = false;
609 pc = 0xedac;
610 Last;
611 case 0x01:
612 ram[0x90] |= TheIEC->OutATN(ram[0x95]);
613 c_flag = false;
614 pc = 0xedac;
615 Last;
616 case 0x02:
617 ram[0x90] |= TheIEC->OutSec(ram[0x95]);
618 c_flag = false;
619 pc = 0xedac;
620 Last;
621 case 0x03:
622 ram[0x90] |= TheIEC->In(&a);
623 set_nz(a);
624 c_flag = false;
625 pc = 0xedac;
626 Last;
627 case 0x04:
628 TheIEC->SetATN();
629 pc = 0xedfb;
630 Last;
631 case 0x05:
632 TheIEC->RelATN();
633 pc = 0xedac;
634 Last;
635 case 0x06:
636 TheIEC->Turnaround();
637 pc = 0xedac;
638 Last;
639 case 0x07:
640 TheIEC->Release();
641 pc = 0xedac;
642 Last;
643 default:
644 illegal_op(0xf2, pc-1);
645 break;
646 }
647 break;
648
649 default:
650 illegal_op(op, pc-1);
651 break;
652 }
653 }
654