ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/CPUC64_SC.cpp
Revision: 1.3
Committed: 2003-07-09T13:54:22Z (20 years, 9 months ago) by cebix
Branch: MAIN
Changes since 1.2: +0 -3 lines
Log Message:
applied misc fixes that have accumulated over the time

File Contents

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