ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/CPUC64_SC.cpp
Revision: 1.1
Committed: 2003-07-01T17:09:43Z (20 years, 9 months ago) by cebix
Branch: MAIN
Log Message:
imported files

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * CPUC64_SC.cpp - Single-cycle 6510 (C64) emulation
3     *
4     * Frodo (C) 1994-1997,2002 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     }
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     #ifdef __i386
280     inline
281     #endif
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     return (ddr & pr) | (~ddr & 0x17);
291     } else
292     return read_byte_io(adr);
293     }
294    
295    
296     /*
297     * $dfa0-$dfff: Emulator identification
298     */
299    
300     const char frodo_id[0x5c] = "FRODO\r(C) 1994-1997 CHRISTIAN BAUER";
301    
302     uint8 MOS6510::read_emulator_id(uint16 adr)
303     {
304     switch (adr) {
305     case 0x7c: // $dffc: revision
306     return FRODO_REVISION << 4;
307     case 0x7d: // $dffd: version
308     return FRODO_VERSION;
309     case 0x7e: // $dffe returns 'F' (Frodo ID)
310     return 'F';
311     case 0x7f: // $dfff alternates between $55 and $aa
312     dfff_byte = ~dfff_byte;
313     return dfff_byte;
314     default:
315     return frodo_id[adr - 0x20];
316     }
317     }
318    
319    
320     /*
321     * Read a word (little-endian) from the CPU's address space
322     */
323    
324     inline uint16 MOS6510::read_word(uint16 adr)
325     {
326     return read_byte(adr) | (read_byte(adr+1) << 8);
327     }
328    
329    
330     /*
331     * Write a byte to I/O space
332     */
333    
334     inline void MOS6510::write_byte_io(uint16 adr, uint8 byte)
335     {
336     if (adr >= 0xe000) {
337     ram[adr] = byte;
338     if (adr == 0xff00)
339     TheREU->FF00Trigger();
340     } else if (io_in)
341     switch ((adr >> 8) & 0x0f) {
342     case 0x0: // VIC
343     case 0x1:
344     case 0x2:
345     case 0x3:
346     TheVIC->WriteRegister(adr & 0x3f, byte);
347     return;
348     case 0x4: // SID
349     case 0x5:
350     case 0x6:
351     case 0x7:
352     TheSID->WriteRegister(adr & 0x1f, byte);
353     return;
354     case 0x8: // Color RAM
355     case 0x9:
356     case 0xa:
357     case 0xb:
358     color_ram[adr & 0x03ff] = byte & 0x0f;
359     return;
360     case 0xc: // CIA 1
361     TheCIA1->WriteRegister(adr & 0x0f, byte);
362     return;
363     case 0xd: // CIA 2
364     TheCIA2->WriteRegister(adr & 0x0f, byte);
365     return;
366     case 0xe: // REU/Open I/O
367     case 0xf:
368     if ((adr & 0xfff0) == 0xdf00)
369     TheREU->WriteRegister(adr & 0x0f, byte);
370     return;
371     }
372     else
373     ram[adr] = byte;
374     }
375    
376    
377     /*
378     * Write a byte to the CPU's address space
379     */
380    
381     void MOS6510::write_byte(uint16 adr, uint8 byte)
382     {
383     if (adr < 0xd000) {
384     if (adr >= 2)
385     ram[adr] = byte;
386     else if (adr == 0) {
387     ddr = byte;
388     ram[0] = TheVIC->LastVICByte;
389     new_config();
390     } else {
391     pr = byte;
392     ram[1] = TheVIC->LastVICByte;
393     new_config();
394     }
395     } else
396     write_byte_io(adr, byte);
397     }
398    
399    
400     /*
401     * Read byte from 6510 address space with special memory config (used by SAM)
402     */
403    
404     uint8 MOS6510::ExtReadByte(uint16 adr)
405     {
406     // Save old memory configuration
407     bool bi = basic_in, ki = kernal_in, ci = char_in, ii = io_in;
408    
409     // Set new configuration
410     basic_in = (ExtConfig & 3) == 3;
411     kernal_in = ExtConfig & 2;
412     char_in = (ExtConfig & 3) && ~(ExtConfig & 4);
413     io_in = (ExtConfig & 3) && (ExtConfig & 4);
414    
415     // Read byte
416     uint8 byte = read_byte(adr);
417    
418     // Restore old configuration
419     basic_in = bi; kernal_in = ki; char_in = ci; io_in = ii;
420    
421     return byte;
422     }
423    
424    
425     /*
426     * Write byte to 6510 address space with special memory config (used by SAM)
427     */
428    
429     void MOS6510::ExtWriteByte(uint16 adr, uint8 byte)
430     {
431     // Save old memory configuration
432     bool bi = basic_in, ki = kernal_in, ci = char_in, ii = io_in;
433    
434     // Set new configuration
435     basic_in = (ExtConfig & 3) == 3;
436     kernal_in = ExtConfig & 2;
437     char_in = (ExtConfig & 3) && ~(ExtConfig & 4);
438     io_in = (ExtConfig & 3) && (ExtConfig & 4);
439    
440     // Write byte
441     write_byte(adr, byte);
442    
443     // Restore old configuration
444     basic_in = bi; kernal_in = ki; char_in = ci; io_in = ii;
445     }
446    
447    
448     /*
449     * Read byte from 6510 address space with current memory config (used by REU)
450     */
451    
452     uint8 MOS6510::REUReadByte(uint16 adr)
453     {
454     return read_byte(adr);
455     }
456    
457    
458     /*
459     * Write byte to 6510 address space with current memory config (used by REU)
460     */
461    
462     void MOS6510::REUWriteByte(uint16 adr, uint8 byte)
463     {
464     write_byte(adr, byte);
465     }
466    
467    
468     /*
469     * Adc instruction
470     */
471    
472     inline void MOS6510::do_adc(uint8 byte)
473     {
474     if (!d_flag) {
475     uint16 tmp;
476    
477     // Binary mode
478     tmp = a + byte + (c_flag ? 1 : 0);
479     c_flag = tmp > 0xff;
480     v_flag = !((a ^ byte) & 0x80) && ((a ^ tmp) & 0x80);
481     z_flag = n_flag = a = tmp;
482    
483     } else {
484     uint16 al, ah;
485    
486     // Decimal mode
487     al = (a & 0x0f) + (byte & 0x0f) + (c_flag ? 1 : 0); // Calculate lower nybble
488     if (al > 9) al += 6; // BCD fixup for lower nybble
489    
490     ah = (a >> 4) + (byte >> 4); // Calculate upper nybble
491     if (al > 0x0f) ah++;
492    
493     z_flag = a + byte + (c_flag ? 1 : 0); // Set flags
494     n_flag = ah << 4; // Only highest bit used
495     v_flag = (((ah << 4) ^ a) & 0x80) && !((a ^ byte) & 0x80);
496    
497     if (ah > 9) ah += 6; // BCD fixup for upper nybble
498     c_flag = ah > 0x0f; // Set carry flag
499     a = (ah << 4) | (al & 0x0f); // Compose result
500     }
501     }
502    
503    
504     /*
505     * Sbc instruction
506     */
507    
508     inline void MOS6510::do_sbc(uint8 byte)
509     {
510     uint16 tmp = a - byte - (c_flag ? 0 : 1);
511    
512     if (!d_flag) {
513    
514     // Binary mode
515     c_flag = tmp < 0x100;
516     v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80);
517     z_flag = n_flag = a = tmp;
518    
519     } else {
520     uint16 al, ah;
521    
522     // Decimal mode
523     al = (a & 0x0f) - (byte & 0x0f) - (c_flag ? 0 : 1); // Calculate lower nybble
524     ah = (a >> 4) - (byte >> 4); // Calculate upper nybble
525     if (al & 0x10) {
526     al -= 6; // BCD fixup for lower nybble
527     ah--;
528     }
529     if (ah & 0x10) ah -= 6; // BCD fixup for upper nybble
530    
531     c_flag = tmp < 0x100; // Set flags
532     v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80);
533     z_flag = n_flag = tmp;
534    
535     a = (ah << 4) | (al & 0x0f); // Compose result
536     }
537     }
538    
539    
540     /*
541     * Reset CPU
542     */
543    
544     void MOS6510::Reset(void)
545     {
546     // Delete 'CBM80' if present
547     if (ram[0x8004] == 0xc3 && ram[0x8005] == 0xc2 && ram[0x8006] == 0xcd
548     && ram[0x8007] == 0x38 && ram[0x8008] == 0x30)
549     ram[0x8004] = 0;
550    
551     // Initialize extra 6510 registers and memory configuration
552     ddr = pr = 0;
553     new_config();
554    
555     // Clear all interrupt lines
556     interrupt.intr_any = 0;
557     nmi_state = false;
558    
559     // Read reset vector
560     pc = read_word(0xfffc);
561     state = 0;
562     }
563    
564    
565     /*
566     * Illegal opcode encountered
567     */
568    
569     void MOS6510::illegal_op(uint8 op, uint16 at)
570     {
571     char illop_msg[80];
572    
573     sprintf(illop_msg, "Illegal opcode %02x at %04x.", op, at);
574     ShowRequester(illop_msg, "Reset");
575     the_c64->Reset();
576     Reset();
577     }
578    
579    
580     /*
581     * Emulate one 6510 clock cycle
582     */
583    
584     // Read byte from memory
585     #define read_to(adr, to) \
586     if (BALow) \
587     return; \
588     to = read_byte(adr);
589    
590     // Read byte from memory, throw away result
591     #define read_idle(adr) \
592     if (BALow) \
593     return; \
594     read_byte(adr);
595    
596     void MOS6510::EmulateCycle(void)
597     {
598     uint8 data, tmp;
599    
600     // Any pending interrupts in state 0 (opcode fetch)?
601     if (!state && interrupt.intr_any) {
602     if (interrupt.intr[INT_RESET])
603     Reset();
604     else if (interrupt.intr[INT_NMI] && (the_c64->CycleCounter-first_nmi_cycle >= 2)) {
605     interrupt.intr[INT_NMI] = false; // Simulate an edge-triggered input
606     state = 0x0010;
607     } else if ((interrupt.intr[INT_VICIRQ] || interrupt.intr[INT_CIAIRQ]) && (the_c64->CycleCounter-first_irq_cycle >= 2) && !i_flag)
608     state = 0x0008;
609     }
610    
611     #include "CPU_emulcycle.h"
612    
613     // Extension opcode
614     case O_EXT:
615     if (pc < 0xe000) {
616     illegal_op(0xf2, pc-1);
617     break;
618     }
619     switch (read_byte(pc++)) {
620     case 0x00:
621     ram[0x90] |= TheIEC->Out(ram[0x95], ram[0xa3] & 0x80);
622     c_flag = false;
623     pc = 0xedac;
624     Last;
625     case 0x01:
626     ram[0x90] |= TheIEC->OutATN(ram[0x95]);
627     c_flag = false;
628     pc = 0xedac;
629     Last;
630     case 0x02:
631     ram[0x90] |= TheIEC->OutSec(ram[0x95]);
632     c_flag = false;
633     pc = 0xedac;
634     Last;
635     case 0x03:
636     ram[0x90] |= TheIEC->In(&a);
637     set_nz(a);
638     c_flag = false;
639     pc = 0xedac;
640     Last;
641     case 0x04:
642     TheIEC->SetATN();
643     pc = 0xedfb;
644     Last;
645     case 0x05:
646     TheIEC->RelATN();
647     pc = 0xedac;
648     Last;
649     case 0x06:
650     TheIEC->Turnaround();
651     pc = 0xedac;
652     Last;
653     case 0x07:
654     TheIEC->Release();
655     pc = 0xedac;
656     Last;
657     default:
658     illegal_op(0xf2, pc-1);
659     break;
660     }
661     break;
662    
663     default:
664     illegal_op(op, pc-1);
665     break;
666     }
667     }