ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/CPUC64_SC.cpp
Revision: 1.7
Committed: 2009-01-11T11:21:01Z (15 years, 3 months ago) by cebix
Branch: MAIN
Changes since 1.6: +13 -7 lines
Log Message:
improved processor port emulation

File Contents

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