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