/* * C64.cpp - Put the pieces together * * Frodo (C) 1994-1997,2002-2004 Christian Bauer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "sysdeps.h" #include "C64.h" #include "CPUC64.h" #include "CPU1541.h" #include "VIC.h" #include "SID.h" #include "CIA.h" #include "REU.h" #include "IEC.h" #include "1541job.h" #include "Display.h" #include "Prefs.h" #if defined(__unix) && !defined(__svgalib__) #include "CmdPipe.h" #endif #ifdef FRODO_SC bool IsFrodoSC = true; #else bool IsFrodoSC = false; #endif /* * Constructor: Allocate objects and memory */ C64::C64() { uint8 *p; // The thread is not yet running thread_running = false; quit_thyself = false; have_a_break = false; // System-dependent things c64_ctor1(); // Open display TheDisplay = new C64Display(this); // Allocate RAM/ROM memory RAM = new uint8[C64_RAM_SIZE]; Basic = new uint8[BASIC_ROM_SIZE]; Kernal = new uint8[KERNAL_ROM_SIZE]; Char = new uint8[CHAR_ROM_SIZE]; Color = new uint8[COLOR_RAM_SIZE]; RAM1541 = new uint8[DRIVE_RAM_SIZE]; ROM1541 = new uint8[DRIVE_ROM_SIZE]; // Create the chips TheCPU = new MOS6510(this, RAM, Basic, Kernal, Char, Color); TheJob1541 = new Job1541(RAM1541); TheCPU1541 = new MOS6502_1541(this, TheJob1541, TheDisplay, RAM1541, ROM1541); TheVIC = TheCPU->TheVIC = new MOS6569(this, TheDisplay, TheCPU, RAM, Char, Color); TheSID = TheCPU->TheSID = new MOS6581(this); TheCIA1 = TheCPU->TheCIA1 = new MOS6526_1(TheCPU, TheVIC); TheCIA2 = TheCPU->TheCIA2 = TheCPU1541->TheCIA2 = new MOS6526_2(TheCPU, TheVIC, TheCPU1541); TheIEC = TheCPU->TheIEC = new IEC(TheDisplay); TheREU = TheCPU->TheREU = new REU(TheCPU); // Initialize RAM with powerup pattern p = RAM; for (unsigned i=0; i<512; i++) { for (unsigned j=0; j<64; j++) *p++ = 0; for (unsigned j=0; j<64; j++) *p++ = 0xff; } // Initialize color RAM with random values p = Color; for (unsigned i=0; iAsyncReset(); TheCPU1541->AsyncReset(); TheSID->Reset(); TheCIA1->Reset(); TheCIA2->Reset(); TheIEC->Reset(); } /* * NMI C64 */ void C64::NMI(void) { TheCPU->AsyncNMI(); } /* * The preferences have changed. prefs is a pointer to the new * preferences, ThePrefs still holds the previous ones. * The emulation must be in the paused state! */ void C64::NewPrefs(Prefs *prefs) { open_close_joysticks(ThePrefs.Joystick1Port, ThePrefs.Joystick2Port, prefs->Joystick1Port, prefs->Joystick2Port); PatchKernal(prefs->FastReset, prefs->Emul1541Proc); TheDisplay->NewPrefs(prefs); #ifdef __riscos__ // Changed order of calls. If 1541 mode hasn't changed the order is insignificant. if (prefs->Emul1541Proc) { // New prefs have 1541 enabled ==> if old prefs had disabled free drives FIRST TheIEC->NewPrefs(prefs); TheJob1541->NewPrefs(prefs); } else { // New prefs has 1541 disabled ==> if old prefs had enabled free job FIRST TheJob1541->NewPrefs(prefs); TheIEC->NewPrefs(prefs); } #else TheIEC->NewPrefs(prefs); TheJob1541->NewPrefs(prefs); #endif TheREU->NewPrefs(prefs); TheSID->NewPrefs(prefs); // Reset 1541 processor if turned on if (!ThePrefs.Emul1541Proc && prefs->Emul1541Proc) TheCPU1541->AsyncReset(); } /* * Patch kernal IEC routines */ void C64::PatchKernal(bool fast_reset, bool emul_1541_proc) { if (fast_reset) { Kernal[0x1d84] = 0xa0; Kernal[0x1d85] = 0x00; } else { Kernal[0x1d84] = orig_kernal_1d84; Kernal[0x1d85] = orig_kernal_1d85; } if (emul_1541_proc) { Kernal[0x0d40] = 0x78; Kernal[0x0d41] = 0x20; Kernal[0x0d23] = 0x78; Kernal[0x0d24] = 0x20; Kernal[0x0d36] = 0x78; Kernal[0x0d37] = 0x20; Kernal[0x0e13] = 0x78; Kernal[0x0e14] = 0xa9; Kernal[0x0def] = 0x78; Kernal[0x0df0] = 0x20; Kernal[0x0dbe] = 0xad; Kernal[0x0dbf] = 0x00; Kernal[0x0dcc] = 0x78; Kernal[0x0dcd] = 0x20; Kernal[0x0e03] = 0x20; Kernal[0x0e04] = 0xbe; } else { Kernal[0x0d40] = 0xf2; // IECOut Kernal[0x0d41] = 0x00; Kernal[0x0d23] = 0xf2; // IECOutATN Kernal[0x0d24] = 0x01; Kernal[0x0d36] = 0xf2; // IECOutSec Kernal[0x0d37] = 0x02; Kernal[0x0e13] = 0xf2; // IECIn Kernal[0x0e14] = 0x03; Kernal[0x0def] = 0xf2; // IECSetATN Kernal[0x0df0] = 0x04; Kernal[0x0dbe] = 0xf2; // IECRelATN Kernal[0x0dbf] = 0x05; Kernal[0x0dcc] = 0xf2; // IECTurnaround Kernal[0x0dcd] = 0x06; Kernal[0x0e03] = 0xf2; // IECRelease Kernal[0x0e04] = 0x07; } // 1541 ROM1541[0x2ae4] = 0xea; // Don't check ROM checksum ROM1541[0x2ae5] = 0xea; ROM1541[0x2ae8] = 0xea; ROM1541[0x2ae9] = 0xea; ROM1541[0x2c9b] = 0xf2; // DOS idle loop ROM1541[0x2c9c] = 0x00; ROM1541[0x3594] = 0x20; // Write sector ROM1541[0x3595] = 0xf2; ROM1541[0x3596] = 0xf5; ROM1541[0x3597] = 0xf2; ROM1541[0x3598] = 0x01; ROM1541[0x3b0c] = 0xf2; // Format track ROM1541[0x3b0d] = 0x02; } /* * Save RAM contents */ void C64::SaveRAM(char *filename) { FILE *f; if ((f = fopen(filename, "wb")) == NULL) ShowRequester("RAM save failed.", "OK", NULL); else { fwrite((void*)RAM, 1, 0x10000, f); fwrite((void*)Color, 1, 0x400, f); if (ThePrefs.Emul1541Proc) fwrite((void*)RAM1541, 1, 0x800, f); fclose(f); } } /* * Save CPU state to snapshot * * 0: Error * 1: OK * -1: Instruction not completed */ int C64::SaveCPUState(FILE *f) { MOS6510State state; TheCPU->GetState(&state); if (!state.instruction_complete) return -1; int i = fwrite(RAM, 0x10000, 1, f); i += fwrite(Color, 0x400, 1, f); i += fwrite((void*)&state, sizeof(state), 1, f); return i == 3; } /* * Load CPU state from snapshot */ bool C64::LoadCPUState(FILE *f) { MOS6510State state; int i = fread(RAM, 0x10000, 1, f); i += fread(Color, 0x400, 1, f); i += fread((void*)&state, sizeof(state), 1, f); if (i == 3) { TheCPU->SetState(&state); return true; } else return false; } /* * Save 1541 state to snapshot * * 0: Error * 1: OK * -1: Instruction not completed */ int C64::Save1541State(FILE *f) { MOS6502State state; TheCPU1541->GetState(&state); if (!state.idle && !state.instruction_complete) return -1; int i = fwrite(RAM1541, 0x800, 1, f); i += fwrite((void*)&state, sizeof(state), 1, f); return i == 2; } /* * Load 1541 state from snapshot */ bool C64::Load1541State(FILE *f) { MOS6502State state; int i = fread(RAM1541, 0x800, 1, f); i += fread((void*)&state, sizeof(state), 1, f); if (i == 2) { TheCPU1541->SetState(&state); return true; } else return false; } /* * Save VIC state to snapshot */ bool C64::SaveVICState(FILE *f) { MOS6569State state; TheVIC->GetState(&state); return fwrite((void*)&state, sizeof(state), 1, f) == 1; } /* * Load VIC state from snapshot */ bool C64::LoadVICState(FILE *f) { MOS6569State state; if (fread((void*)&state, sizeof(state), 1, f) == 1) { TheVIC->SetState(&state); return true; } else return false; } /* * Save SID state to snapshot */ bool C64::SaveSIDState(FILE *f) { MOS6581State state; TheSID->GetState(&state); return fwrite((void*)&state, sizeof(state), 1, f) == 1; } /* * Load SID state from snapshot */ bool C64::LoadSIDState(FILE *f) { MOS6581State state; if (fread((void*)&state, sizeof(state), 1, f) == 1) { TheSID->SetState(&state); return true; } else return false; } /* * Save CIA states to snapshot */ bool C64::SaveCIAState(FILE *f) { MOS6526State state; TheCIA1->GetState(&state); if (fwrite((void*)&state, sizeof(state), 1, f) == 1) { TheCIA2->GetState(&state); return fwrite((void*)&state, sizeof(state), 1, f) == 1; } else return false; } /* * Load CIA states from snapshot */ bool C64::LoadCIAState(FILE *f) { MOS6526State state; if (fread((void*)&state, sizeof(state), 1, f) == 1) { TheCIA1->SetState(&state); if (fread((void*)&state, sizeof(state), 1, f) == 1) { TheCIA2->SetState(&state); return true; } else return false; } else return false; } /* * Save 1541 GCR state to snapshot */ bool C64::Save1541JobState(FILE *f) { Job1541State state; TheJob1541->GetState(&state); return fwrite((void*)&state, sizeof(state), 1, f) == 1; } /* * Load 1541 GCR state from snapshot */ bool C64::Load1541JobState(FILE *f) { Job1541State state; if (fread((void*)&state, sizeof(state), 1, f) == 1) { TheJob1541->SetState(&state); return true; } else return false; } #define SNAPSHOT_HEADER "FrodoSnapshot" #define SNAPSHOT_1541 1 #define ADVANCE_CYCLES \ TheVIC->EmulateCycle(); \ TheCIA1->EmulateCycle(); \ TheCIA2->EmulateCycle(); \ TheCPU->EmulateCycle(); \ if (ThePrefs.Emul1541Proc) { \ TheCPU1541->CountVIATimers(1); \ if (!TheCPU1541->Idle) \ TheCPU1541->EmulateCycle(); \ } /* * Save snapshot (emulation must be paused and in VBlank) * * To be able to use SC snapshots with SL, SC snapshots are made thus that no * partially dealt with instructions are saved. Instead all devices are advanced * cycle by cycle until the current instruction has been finished. The number of * cycles this takes is saved in the snapshot and will be reconstructed if the * snapshot is loaded into FrodoSC again. */ void C64::SaveSnapshot(char *filename) { FILE *f; uint8 flags; uint8 delay; int stat; if ((f = fopen(filename, "wb")) == NULL) { ShowRequester("Unable to open snapshot file", "OK", NULL); return; } fprintf(f, "%s%c", SNAPSHOT_HEADER, 10); fputc(0, f); // Version number 0 flags = 0; if (ThePrefs.Emul1541Proc) flags |= SNAPSHOT_1541; fputc(flags, f); SaveVICState(f); SaveSIDState(f); SaveCIAState(f); #ifdef FRODO_SC delay = 0; do { if ((stat = SaveCPUState(f)) == -1) { // -1 -> Instruction not finished yet ADVANCE_CYCLES; // Advance everything by one cycle delay++; } } while (stat == -1); fputc(delay, f); // Number of cycles the saved CPUC64 lags behind the previous chips #else SaveCPUState(f); fputc(0, f); // No delay #endif if (ThePrefs.Emul1541Proc) { fwrite(ThePrefs.DrivePath[0], 256, 1, f); #ifdef FRODO_SC delay = 0; do { if ((stat = Save1541State(f)) == -1) { ADVANCE_CYCLES; delay++; } } while (stat == -1); fputc(delay, f); #else Save1541State(f); fputc(0, f); // No delay #endif Save1541JobState(f); } fclose(f); #ifdef __riscos__ TheWIMP->SnapshotSaved(true); #endif } /* * Load snapshot (emulation must be paused and in VBlank) */ bool C64::LoadSnapshot(char *filename) { FILE *f; if ((f = fopen(filename, "rb")) != NULL) { char Header[] = SNAPSHOT_HEADER; char *b = Header, c = 0; uint8 delay, i; // For some reason memcmp()/strcmp() and so forth utterly fail here. while (*b > 32) { if ((c = fgetc(f)) != *b++) { b = NULL; break; } } if (b != NULL) { uint8 flags; bool error = false; #ifndef FRODO_SC long vicptr; // File offset of VIC data #endif while (c != 10) c = fgetc(f); // Shouldn't be necessary if (fgetc(f) != 0) { ShowRequester("Unknown snapshot format", "OK", NULL); fclose(f); return false; } flags = fgetc(f); #ifndef FRODO_SC vicptr = ftell(f); #endif error |= !LoadVICState(f); error |= !LoadSIDState(f); error |= !LoadCIAState(f); error |= !LoadCPUState(f); delay = fgetc(f); // Number of cycles the 6510 is ahead of the previous chips #ifdef FRODO_SC // Make the other chips "catch up" with the 6510 for (i=0; iEmulateCycle(); TheCIA1->EmulateCycle(); TheCIA2->EmulateCycle(); } #endif if ((flags & SNAPSHOT_1541) != 0) { Prefs *prefs = new Prefs(ThePrefs); // First switch on emulation error |= (fread(prefs->DrivePath[0], 256, 1, f) != 1); prefs->Emul1541Proc = true; NewPrefs(prefs); ThePrefs = *prefs; delete prefs; // Then read the context error |= !Load1541State(f); delay = fgetc(f); // Number of cycles the 6502 is ahead of the previous chips #ifdef FRODO_SC // Make the other chips "catch up" with the 6502 for (i=0; iEmulateCycle(); TheCIA1->EmulateCycle(); TheCIA2->EmulateCycle(); TheCPU->EmulateCycle(); } #endif Load1541JobState(f); #ifdef __riscos__ TheWIMP->ThePrefsToWindow(); #endif } else if (ThePrefs.Emul1541Proc) { // No emulation in snapshot, but currently active? Prefs *prefs = new Prefs(ThePrefs); prefs->Emul1541Proc = false; NewPrefs(prefs); ThePrefs = *prefs; delete prefs; #ifdef __riscos__ TheWIMP->ThePrefsToWindow(); #endif } #ifndef FRODO_SC fseek(f, vicptr, SEEK_SET); LoadVICState(f); // Load VIC data twice in SL (is REALLY necessary sometimes!) #endif fclose(f); if (error) { ShowRequester("Error reading snapshot file", "OK", NULL); Reset(); return false; } else return true; } else { fclose(f); ShowRequester("Not a Frodo snapshot file", "OK", NULL); return false; } } else { ShowRequester("Can't open snapshot file", "OK", NULL); return false; } } #ifdef __BEOS__ #include "C64_Be.h" #endif #ifdef AMIGA #include "C64_Amiga.h" #endif #ifdef __unix #include "C64_x.h" #endif #ifdef __mac__ #include "C64_mac.h" #endif #ifdef WIN32 #include "C64_WIN32.h" #endif #ifdef __riscos__ #include "C64_Acorn.h" #endif