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, 8 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

# 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 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 }