ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/CPUC64_SC.cpp
Revision: 1.8
Committed: 2010-04-22T15:08:18Z (12 years, 7 months ago) by cebix
Branch: MAIN
CVS Tags: HEAD
Changes since 1.7: +20 -8 lines
Log Message:
better interrupt timing

File Contents

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