1 /*
2 * C64.cpp - Put the pieces together
3 *
4 * Frodo (C) 1994-1997,2002 Christian Bauer
5 */
6
7 #include "sysdeps.h"
8
9 #include "C64.h"
10 #include "CPUC64.h"
11 #include "CPU1541.h"
12 #include "VIC.h"
13 #include "SID.h"
14 #include "CIA.h"
15 #include "REU.h"
16 #include "IEC.h"
17 #include "1541job.h"
18 #include "Display.h"
19 #include "Prefs.h"
20
21 #if defined(__unix) && !defined(__svgalib__)
22 #include "CmdPipe.h"
23 #endif
24
25
26 #ifdef FRODO_SC
27 bool IsFrodoSC = true;
28 #else
29 bool IsFrodoSC = false;
30 #endif
31
32
33 /*
34 * Constructor: Allocate objects and memory
35 */
36
C64()37 C64::C64()
38 {
39 int i,j;
40 uint8 *p;
41
42 // The thread is not yet running
43 thread_running = false;
44 quit_thyself = false;
45 have_a_break = false;
46
47 // System-dependent things
48 c64_ctor1();
49
50 // Open display
51 TheDisplay = new C64Display(this);
52
53 // Allocate RAM/ROM memory
54 RAM = new uint8[0x10000];
55 Basic = new uint8[0x2000];
56 Kernal = new uint8[0x2000];
57 Char = new uint8[0x1000];
58 Color = new uint8[0x0400];
59 RAM1541 = new uint8[0x0800];
60 ROM1541 = new uint8[0x4000];
61
62 // Create the chips
63 TheCPU = new MOS6510(this, RAM, Basic, Kernal, Char, Color);
64
65 TheJob1541 = new Job1541(RAM1541);
66 TheCPU1541 = new MOS6502_1541(this, TheJob1541, TheDisplay, RAM1541, ROM1541);
67
68 TheVIC = TheCPU->TheVIC = new MOS6569(this, TheDisplay, TheCPU, RAM, Char, Color);
69 TheSID = TheCPU->TheSID = new MOS6581(this);
70 TheCIA1 = TheCPU->TheCIA1 = new MOS6526_1(TheCPU, TheVIC);
71 TheCIA2 = TheCPU->TheCIA2 = TheCPU1541->TheCIA2 = new MOS6526_2(TheCPU, TheVIC, TheCPU1541);
72 TheIEC = TheCPU->TheIEC = new IEC(TheDisplay);
73 TheREU = TheCPU->TheREU = new REU(TheCPU);
74
75 // Initialize RAM with powerup pattern
76 for (i=0, p=RAM; i<512; i++) {
77 for (j=0; j<64; j++)
78 *p++ = 0;
79 for (j=0; j<64; j++)
80 *p++ = 0xff;
81 }
82
83 // Initialize color RAM with random values
84 for (i=0, p=Color; i<1024; i++)
85 *p++ = rand() & 0x0f;
86
87 // Clear 1541 RAM
88 memset(RAM1541, 0, 0x800);
89
90 // Open joystick drivers if required
91 open_close_joysticks(false, false, ThePrefs.Joystick1On, ThePrefs.Joystick2On);
92 joykey = 0xff;
93
94 #ifdef FRODO_SC
95 CycleCounter = 0;
96 #endif
97
98 // System-dependent things
99 c64_ctor2();
100 }
101
102
103 /*
104 * Destructor: Delete all objects
105 */
106
~C64()107 C64::~C64()
108 {
109 open_close_joysticks(ThePrefs.Joystick1On, ThePrefs.Joystick2On, false, false);
110
111 delete TheJob1541;
112 delete TheREU;
113 delete TheIEC;
114 delete TheCIA2;
115 delete TheCIA1;
116 delete TheSID;
117 delete TheVIC;
118 delete TheCPU1541;
119 delete TheCPU;
120 delete TheDisplay;
121
122 delete[] RAM;
123 delete[] Basic;
124 delete[] Kernal;
125 delete[] Char;
126 delete[] Color;
127 delete[] RAM1541;
128 delete[] ROM1541;
129
130 c64_dtor();
131 }
132
133
134 /*
135 * Reset C64
136 */
137
Reset(void)138 void C64::Reset(void)
139 {
140 TheCPU->AsyncReset();
141 TheCPU1541->AsyncReset();
142 TheSID->Reset();
143 TheCIA1->Reset();
144 TheCIA2->Reset();
145 TheIEC->Reset();
146 }
147
148
149 /*
150 * NMI C64
151 */
152
NMI(void)153 void C64::NMI(void)
154 {
155 TheCPU->AsyncNMI();
156 }
157
158
159 /*
160 * The preferences have changed. prefs is a pointer to the new
161 * preferences, ThePrefs still holds the previous ones.
162 * The emulation must be in the paused state!
163 */
164
NewPrefs(Prefs * prefs)165 void C64::NewPrefs(Prefs *prefs)
166 {
167 open_close_joysticks(ThePrefs.Joystick1On, ThePrefs.Joystick2On, prefs->Joystick1On, prefs->Joystick2On);
168 PatchKernal(prefs->FastReset, prefs->Emul1541Proc);
169
170 TheDisplay->NewPrefs(prefs);
171
172 #ifdef __riscos__
173 // Changed order of calls. If 1541 mode hasn't changed the order is insignificant.
174 if (prefs->Emul1541Proc) {
175 // New prefs have 1541 enabled ==> if old prefs had disabled free drives FIRST
176 TheIEC->NewPrefs(prefs);
177 TheJob1541->NewPrefs(prefs);
178 } else {
179 // New prefs has 1541 disabled ==> if old prefs had enabled free job FIRST
180 TheJob1541->NewPrefs(prefs);
181 TheIEC->NewPrefs(prefs);
182 }
183 #else
184 TheIEC->NewPrefs(prefs);
185 TheJob1541->NewPrefs(prefs);
186 #endif
187
188 TheREU->NewPrefs(prefs);
189 TheSID->NewPrefs(prefs);
190
191 // Reset 1541 processor if turned on
192 if (!ThePrefs.Emul1541Proc && prefs->Emul1541Proc)
193 TheCPU1541->AsyncReset();
194 }
195
196
197 /*
198 * Patch kernal IEC routines
199 */
200
PatchKernal(bool fast_reset,bool emul_1541_proc)201 void C64::PatchKernal(bool fast_reset, bool emul_1541_proc)
202 {
203 if (fast_reset) {
204 Kernal[0x1d84] = 0xa0;
205 Kernal[0x1d85] = 0x00;
206 } else {
207 Kernal[0x1d84] = orig_kernal_1d84;
208 Kernal[0x1d85] = orig_kernal_1d85;
209 }
210
211 if (emul_1541_proc) {
212 Kernal[0x0d40] = 0x78;
213 Kernal[0x0d41] = 0x20;
214 Kernal[0x0d23] = 0x78;
215 Kernal[0x0d24] = 0x20;
216 Kernal[0x0d36] = 0x78;
217 Kernal[0x0d37] = 0x20;
218 Kernal[0x0e13] = 0x78;
219 Kernal[0x0e14] = 0xa9;
220 Kernal[0x0def] = 0x78;
221 Kernal[0x0df0] = 0x20;
222 Kernal[0x0dbe] = 0xad;
223 Kernal[0x0dbf] = 0x00;
224 Kernal[0x0dcc] = 0x78;
225 Kernal[0x0dcd] = 0x20;
226 Kernal[0x0e03] = 0x20;
227 Kernal[0x0e04] = 0xbe;
228 } else {
229 Kernal[0x0d40] = 0xf2; // IECOut
230 Kernal[0x0d41] = 0x00;
231 Kernal[0x0d23] = 0xf2; // IECOutATN
232 Kernal[0x0d24] = 0x01;
233 Kernal[0x0d36] = 0xf2; // IECOutSec
234 Kernal[0x0d37] = 0x02;
235 Kernal[0x0e13] = 0xf2; // IECIn
236 Kernal[0x0e14] = 0x03;
237 Kernal[0x0def] = 0xf2; // IECSetATN
238 Kernal[0x0df0] = 0x04;
239 Kernal[0x0dbe] = 0xf2; // IECRelATN
240 Kernal[0x0dbf] = 0x05;
241 Kernal[0x0dcc] = 0xf2; // IECTurnaround
242 Kernal[0x0dcd] = 0x06;
243 Kernal[0x0e03] = 0xf2; // IECRelease
244 Kernal[0x0e04] = 0x07;
245 }
246
247 // 1541
248 ROM1541[0x2ae4] = 0xea; // Don't check ROM checksum
249 ROM1541[0x2ae5] = 0xea;
250 ROM1541[0x2ae8] = 0xea;
251 ROM1541[0x2ae9] = 0xea;
252 ROM1541[0x2c9b] = 0xf2; // DOS idle loop
253 ROM1541[0x2c9c] = 0x00;
254 ROM1541[0x3594] = 0x20; // Write sector
255 ROM1541[0x3595] = 0xf2;
256 ROM1541[0x3596] = 0xf5;
257 ROM1541[0x3597] = 0xf2;
258 ROM1541[0x3598] = 0x01;
259 ROM1541[0x3b0c] = 0xf2; // Format track
260 ROM1541[0x3b0d] = 0x02;
261 }
262
263
264 /*
265 * Save RAM contents
266 */
267
SaveRAM(char * filename)268 void C64::SaveRAM(char *filename)
269 {
270 FILE *f;
271
272 if ((f = fopen(filename, "wb")) == NULL)
273 ShowRequester("RAM save failed.", "OK", NULL);
274 else {
275 fwrite((void*)RAM, 1, 0x10000, f);
276 fwrite((void*)Color, 1, 0x400, f);
277 if (ThePrefs.Emul1541Proc)
278 fwrite((void*)RAM1541, 1, 0x800, f);
279 fclose(f);
280 }
281 }
282
283
284 /*
285 * Save CPU state to snapshot
286 *
287 * 0: Error
288 * 1: OK
289 * -1: Instruction not completed
290 */
291
SaveCPUState(FILE * f)292 int C64::SaveCPUState(FILE *f)
293 {
294 MOS6510State state;
295 TheCPU->GetState(&state);
296
297 if (!state.instruction_complete)
298 return -1;
299
300 int i = fwrite(RAM, 0x10000, 1, f);
301 i += fwrite(Color, 0x400, 1, f);
302 i += fwrite((void*)&state, sizeof(state), 1, f);
303
304 return i == 3;
305 }
306
307
308 /*
309 * Load CPU state from snapshot
310 */
311
LoadCPUState(FILE * f)312 bool C64::LoadCPUState(FILE *f)
313 {
314 MOS6510State state;
315
316 int i = fread(RAM, 0x10000, 1, f);
317 i += fread(Color, 0x400, 1, f);
318 i += fread((void*)&state, sizeof(state), 1, f);
319
320 if (i == 3) {
321 TheCPU->SetState(&state);
322 return true;
323 } else
324 return false;
325 }
326
327
328 /*
329 * Save 1541 state to snapshot
330 *
331 * 0: Error
332 * 1: OK
333 * -1: Instruction not completed
334 */
335
Save1541State(FILE * f)336 int C64::Save1541State(FILE *f)
337 {
338 MOS6502State state;
339 TheCPU1541->GetState(&state);
340
341 if (!state.idle && !state.instruction_complete)
342 return -1;
343
344 int i = fwrite(RAM1541, 0x800, 1, f);
345 i += fwrite((void*)&state, sizeof(state), 1, f);
346
347 return i == 2;
348 }
349
350
351 /*
352 * Load 1541 state from snapshot
353 */
354
Load1541State(FILE * f)355 bool C64::Load1541State(FILE *f)
356 {
357 MOS6502State state;
358
359 int i = fread(RAM1541, 0x800, 1, f);
360 i += fread((void*)&state, sizeof(state), 1, f);
361
362 if (i == 2) {
363 TheCPU1541->SetState(&state);
364 return true;
365 } else
366 return false;
367 }
368
369
370 /*
371 * Save VIC state to snapshot
372 */
373
SaveVICState(FILE * f)374 bool C64::SaveVICState(FILE *f)
375 {
376 MOS6569State state;
377 TheVIC->GetState(&state);
378 return fwrite((void*)&state, sizeof(state), 1, f) == 1;
379 }
380
381
382 /*
383 * Load VIC state from snapshot
384 */
385
LoadVICState(FILE * f)386 bool C64::LoadVICState(FILE *f)
387 {
388 MOS6569State state;
389
390 if (fread((void*)&state, sizeof(state), 1, f) == 1) {
391 TheVIC->SetState(&state);
392 return true;
393 } else
394 return false;
395 }
396
397
398 /*
399 * Save SID state to snapshot
400 */
401
SaveSIDState(FILE * f)402 bool C64::SaveSIDState(FILE *f)
403 {
404 MOS6581State state;
405 TheSID->GetState(&state);
406 return fwrite((void*)&state, sizeof(state), 1, f) == 1;
407 }
408
409
410 /*
411 * Load SID state from snapshot
412 */
413
LoadSIDState(FILE * f)414 bool C64::LoadSIDState(FILE *f)
415 {
416 MOS6581State state;
417
418 if (fread((void*)&state, sizeof(state), 1, f) == 1) {
419 TheSID->SetState(&state);
420 return true;
421 } else
422 return false;
423 }
424
425
426 /*
427 * Save CIA states to snapshot
428 */
429
SaveCIAState(FILE * f)430 bool C64::SaveCIAState(FILE *f)
431 {
432 MOS6526State state;
433 TheCIA1->GetState(&state);
434
435 if (fwrite((void*)&state, sizeof(state), 1, f) == 1) {
436 TheCIA2->GetState(&state);
437 return fwrite((void*)&state, sizeof(state), 1, f) == 1;
438 } else
439 return false;
440 }
441
442
443 /*
444 * Load CIA states from snapshot
445 */
446
LoadCIAState(FILE * f)447 bool C64::LoadCIAState(FILE *f)
448 {
449 MOS6526State state;
450
451 if (fread((void*)&state, sizeof(state), 1, f) == 1) {
452 TheCIA1->SetState(&state);
453 if (fread((void*)&state, sizeof(state), 1, f) == 1) {
454 TheCIA2->SetState(&state);
455 return true;
456 } else
457 return false;
458 } else
459 return false;
460 }
461
462
463 /*
464 * Save 1541 GCR state to snapshot
465 */
466
Save1541JobState(FILE * f)467 bool C64::Save1541JobState(FILE *f)
468 {
469 Job1541State state;
470 TheJob1541->GetState(&state);
471 return fwrite((void*)&state, sizeof(state), 1, f) == 1;
472 }
473
474
475 /*
476 * Load 1541 GCR state from snapshot
477 */
478
Load1541JobState(FILE * f)479 bool C64::Load1541JobState(FILE *f)
480 {
481 Job1541State state;
482
483 if (fread((void*)&state, sizeof(state), 1, f) == 1) {
484 TheJob1541->SetState(&state);
485 return true;
486 } else
487 return false;
488 }
489
490
491 #define SNAPSHOT_HEADER "FrodoSnapshot"
492 #define SNAPSHOT_1541 1
493
494 #define ADVANCE_CYCLES \
495 TheVIC->EmulateCycle(); \
496 TheCIA1->EmulateCycle(); \
497 TheCIA2->EmulateCycle(); \
498 TheCPU->EmulateCycle(); \
499 if (ThePrefs.Emul1541Proc) { \
500 TheCPU1541->CountVIATimers(1); \
501 if (!TheCPU1541->Idle) \
502 TheCPU1541->EmulateCycle(); \
503 }
504
505
506 /*
507 * Save snapshot (emulation must be paused and in VBlank)
508 *
509 * To be able to use SC snapshots with SL, SC snapshots are made thus that no
510 * partially dealt with instructions are saved. Instead all devices are advanced
511 * cycle by cycle until the current instruction has been finished. The number of
512 * cycles this takes is saved in the snapshot and will be reconstructed if the
513 * snapshot is loaded into FrodoSC again.
514 */
515
SaveSnapshot(char * filename)516 void C64::SaveSnapshot(char *filename)
517 {
518 FILE *f;
519 uint8 flags;
520 uint8 delay;
521 int stat;
522
523 if ((f = fopen(filename, "wb")) == NULL) {
524 ShowRequester("Unable to open snapshot file", "OK", NULL);
525 return;
526 }
527
528 fprintf(f, "%s%c", SNAPSHOT_HEADER, 10);
529 fputc(0, f); // Version number 0
530 flags = 0;
531 if (ThePrefs.Emul1541Proc)
532 flags |= SNAPSHOT_1541;
533 fputc(flags, f);
534 SaveVICState(f);
535 SaveSIDState(f);
536 SaveCIAState(f);
537
538 #ifdef FRODO_SC
539 delay = 0;
540 do {
541 if ((stat = SaveCPUState(f)) == -1) { // -1 -> Instruction not finished yet
542 ADVANCE_CYCLES; // Advance everything by one cycle
543 delay++;
544 }
545 } while (stat == -1);
546 fputc(delay, f); // Number of cycles the saved CPUC64 lags behind the previous chips
547 #else
548 SaveCPUState(f);
549 fputc(0, f); // No delay
550 #endif
551
552 if (ThePrefs.Emul1541Proc) {
553 fwrite(ThePrefs.DrivePath[0], 256, 1, f);
554 #ifdef FRODO_SC
555 delay = 0;
556 do {
557 if ((stat = Save1541State(f)) == -1) {
558 ADVANCE_CYCLES;
559 delay++;
560 }
561 } while (stat == -1);
562 fputc(delay, f);
563 #else
564 Save1541State(f);
565 fputc(0, f); // No delay
566 #endif
567 Save1541JobState(f);
568 }
569 fclose(f);
570
571 #ifdef __riscos__
572 TheWIMP->SnapshotSaved(true);
573 #endif
574 }
575
576
577 /*
578 * Load snapshot (emulation must be paused and in VBlank)
579 */
580
LoadSnapshot(char * filename)581 bool C64::LoadSnapshot(char *filename)
582 {
583 FILE *f;
584
585 if ((f = fopen(filename, "rb")) != NULL) {
586 char Header[] = SNAPSHOT_HEADER;
587 char *b = Header, c = 0;
588 uint8 delay, i;
589
590 // For some reason memcmp()/strcmp() and so forth utterly fail here.
591 while (*b > 32) {
592 if ((c = fgetc(f)) != *b++) {
593 b = NULL;
594 break;
595 }
596 }
597 if (b != NULL) {
598 uint8 flags;
599 bool error = false;
600 #ifndef FRODO_SC
601 long vicptr; // File offset of VIC data
602 #endif
603
604 while (c != 10)
605 c = fgetc(f); // Shouldn't be necessary
606 if (fgetc(f) != 0) {
607 ShowRequester("Unknown snapshot format", "OK", NULL);
608 fclose(f);
609 return false;
610 }
611 flags = fgetc(f);
612 #ifndef FRODO_SC
613 vicptr = ftell(f);
614 #endif
615
616 error |= !LoadVICState(f);
617 error |= !LoadSIDState(f);
618 error |= !LoadCIAState(f);
619 error |= !LoadCPUState(f);
620
621 delay = fgetc(f); // Number of cycles the 6510 is ahead of the previous chips
622 #ifdef FRODO_SC
623 // Make the other chips "catch up" with the 6510
624 for (i=0; i<delay; i++) {
625 TheVIC->EmulateCycle();
626 TheCIA1->EmulateCycle();
627 TheCIA2->EmulateCycle();
628 }
629 #endif
630 if ((flags & SNAPSHOT_1541) != 0) {
631 Prefs *prefs = new Prefs(ThePrefs);
632
633 // First switch on emulation
634 error |= (fread(prefs->DrivePath[0], 256, 1, f) != 1);
635 prefs->Emul1541Proc = true;
636 NewPrefs(prefs);
637 ThePrefs = *prefs;
638 delete prefs;
639
640 // Then read the context
641 error |= !Load1541State(f);
642
643 delay = fgetc(f); // Number of cycles the 6502 is ahead of the previous chips
644 #ifdef FRODO_SC
645 // Make the other chips "catch up" with the 6502
646 for (i=0; i<delay; i++) {
647 TheVIC->EmulateCycle();
648 TheCIA1->EmulateCycle();
649 TheCIA2->EmulateCycle();
650 TheCPU->EmulateCycle();
651 }
652 #endif
653 Load1541JobState(f);
654 #ifdef __riscos__
655 TheWIMP->ThePrefsToWindow();
656 #endif
657 } else if (ThePrefs.Emul1541Proc) { // No emulation in snapshot, but currently active?
658 Prefs *prefs = new Prefs(ThePrefs);
659 prefs->Emul1541Proc = false;
660 NewPrefs(prefs);
661 ThePrefs = *prefs;
662 delete prefs;
663 #ifdef __riscos__
664 TheWIMP->ThePrefsToWindow();
665 #endif
666 }
667
668 #ifndef FRODO_SC
669 fseek(f, vicptr, SEEK_SET);
670 LoadVICState(f); // Load VIC data twice in SL (is REALLY necessary sometimes!)
671 #endif
672 fclose(f);
673
674 if (error) {
675 ShowRequester("Error reading snapshot file", "OK", NULL);
676 Reset();
677 return false;
678 } else
679 return true;
680 } else {
681 fclose(f);
682 ShowRequester("Not a Frodo snapshot file", "OK", NULL);
683 return false;
684 }
685 } else {
686 ShowRequester("Can't open snapshot file", "OK", NULL);
687 return false;
688 }
689 }
690
691
692 #ifdef __BEOS__
693 #include "C64_Be.i"
694 #endif
695
696 #ifdef AMIGA
697 #include "C64_Amiga.i"
698 #endif
699
700 #ifdef __unix
701 #include "C64_x.i"
702 #endif
703
704 #ifdef __mac__
705 #include "C64_mac.i"
706 #endif
707
708 #ifdef WIN32
709 #include "C64_WIN32.i"
710 #endif
711
712 #ifdef __riscos__
713 #include "C64_Acorn.i"
714 #endif
715