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 (13 years, 11 months ago) by cebix
Branch: MAIN
CVS Tags: HEAD
Changes since 1.7: +20 -8 lines
Log Message:
better interrupt timing

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * CPUC64_SC.cpp - Single-cycle 6510 (C64) emulation
3     *
4 cebix 1.8 * Frodo Copyright (C) Christian Bauer
5 cebix 1.1 *
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 cebix 1.8 opflags = 0;
107 cebix 1.1 }
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 cebix 1.7 pr_out = 0; // FIXME: should be saved in MOS6510State
184 cebix 1.1 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 cebix 1.7 pr_out = (pr_out & ~ddr) | (pr & ddr);
207     uint8 port = pr | ~ddr;
208 cebix 1.1
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 cebix 1.7 if (adr >= 2) {
286 cebix 1.1 return ram[adr];
287 cebix 1.7 } else if (adr == 0) {
288 cebix 1.1 return ddr;
289 cebix 1.7 } else {
290     uint8 byte = (pr | ~ddr) & (pr_out | 0x17);
291     if (!(ddr & 0x20))
292     byte &= 0xdf;
293     return byte;
294     }
295 cebix 1.1 } else
296     return read_byte_io(adr);
297     }
298    
299    
300     /*
301     * $dfa0-$dfff: Emulator identification
302     */
303    
304 cebix 1.8 const char frodo_id[0x5c] = "FRODO\r(C) CHRISTIAN BAUER";
305 cebix 1.1
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 cebix 1.7 ddr = pr = pr_out = 0;
557 cebix 1.1 new_config();
558    
559     // Clear all interrupt lines
560     interrupt.intr_any = 0;
561     nmi_state = false;
562 cebix 1.8 opflags = 0;
563 cebix 1.1
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 cebix 1.8 if (interrupt.intr[INT_RESET]) {
608 cebix 1.1 Reset();
609 cebix 1.8 } 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 cebix 1.1 }
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 cebix 1.4 ram[0x90] |= TheIEC->In(a);
652 cebix 1.1 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     }