ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/CPUC64_SC.cpp
Revision: 1.2
Committed: 2003-07-01T17:51:17Z (20 years, 9 months ago) by cebix
Branch: MAIN
Changes since 1.1: +1 -1 lines
Log Message:
updated copyright date

File Contents

# Content
1 /*
2 * CPUC64_SC.cpp - Single-cycle 6510 (C64) emulation
3 *
4 * Frodo (C) 1994-1997,2002-2003 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 }