/* * C64_Amiga.h - Put the pieces together, Amiga specific stuff * * 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 #include // Library bases struct Device *TimerBase; /* * Constructor, system-dependent things */ void C64::c64_ctor1(void) { // Open game_io game_port = CreateMsgPort(); game_io = (struct IOStdReq *)CreateIORequest(game_port, sizeof(IOStdReq)); game_io->io_Message.mn_Node.ln_Type = NT_UNKNOWN; game_open = port_allocated = false; if (!OpenDevice("gameport.device", 1, (struct IORequest *)game_io, 0)) game_open = true; } void C64::c64_ctor2(void) { // Initialize joystick variables joy_state = 0xff; // Open timer_io timer_port = CreateMsgPort(); timer_io = (struct timerequest *)CreateIORequest(timer_port, sizeof(struct timerequest)); OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)timer_io, 0); // Get timer base TimerBase = timer_io->tr_node.io_Device; // Preset speedometer start time GetSysTime(&start_time); } /* * Destructor, system-dependent things */ void C64::c64_dtor(void) { // Stop and delete timer_io if (timer_io != NULL) { if (!CheckIO((struct IORequest *)timer_io)) WaitIO((struct IORequest *)timer_io); CloseDevice((struct IORequest *)timer_io); DeleteIORequest((struct IORequest *)timer_io); } if (timer_port != NULL) DeleteMsgPort(timer_port); if (game_open) { if (!CheckIO((struct IORequest *)game_io)) { AbortIO((struct IORequest *)game_io); WaitIO((struct IORequest *)game_io); } CloseDevice((struct IORequest *)game_io); } if (game_io != NULL) DeleteIORequest((struct IORequest *)game_io); if (game_port != NULL) DeleteMsgPort(game_port); } /* * Start emulation */ void C64::Run(void) { // Reset chips TheCPU->Reset(); TheSID->Reset(); TheCIA1->Reset(); TheCIA2->Reset(); TheCPU1541->Reset(); // Patch kernal IEC routines orig_kernal_1d84 = Kernal[0x1d84]; orig_kernal_1d85 = Kernal[0x1d85]; PatchKernal(ThePrefs.FastReset, ThePrefs.Emul1541Proc); // Start timer_io timer_io->tr_node.io_Command = TR_ADDREQUEST; timer_io->tr_time.tv_secs = 0; timer_io->tr_time.tv_micro = ThePrefs.SkipFrames * 20000; // 20ms per frame SendIO((struct IORequest *)timer_io); // Start the CPU thread thread_running = true; quit_thyself = false; have_a_break = false; thread_func(); } /* * Stop emulation */ void C64::Quit(void) { // Ask the thread to quit itself if it is running if (thread_running) { quit_thyself = true; thread_running = false; } } /* * Pause emulation */ void C64::Pause(void) { TheSID->PauseSound(); } /* * Resume emulation */ void C64::Resume(void) { TheSID->ResumeSound(); } /* * Vertical blank: Poll keyboard and joysticks, update window */ void C64::VBlank(bool draw_frame) { struct timeval end_time; long speed_index; // Poll keyboard TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey); // Poll joysticks TheCIA1->Joystick1 = poll_joystick(0); TheCIA1->Joystick2 = poll_joystick(1); if (ThePrefs.JoystickSwap) { uint8 tmp = TheCIA1->Joystick1; TheCIA1->Joystick1 = TheCIA1->Joystick2; TheCIA1->Joystick2 = tmp; } // Joystick keyboard emulation if (TheDisplay->NumLock()) TheCIA1->Joystick1 &= joykey; else TheCIA1->Joystick2 &= joykey; // Count TOD clocks TheCIA1->CountTOD(); TheCIA2->CountTOD(); // Update window if needed if (draw_frame) { TheDisplay->Update(); // Calculate time between VBlanks, display speedometer GetSysTime(&end_time); SubTime(&end_time, &start_time); speed_index = 20000 * 100 * ThePrefs.SkipFrames / (end_time.tv_micro + 1); // Abort timer_io if speed limiter is off if (!ThePrefs.LimitSpeed) { if (!CheckIO((struct IORequest *)timer_io)) AbortIO((struct IORequest *)timer_io); } else if (speed_index > 100) speed_index = 100; // Wait for timer_io (limit speed) WaitIO((struct IORequest *)timer_io); // Restart timer_io timer_io->tr_node.io_Command = TR_ADDREQUEST; timer_io->tr_time.tv_secs = 0; timer_io->tr_time.tv_micro = ThePrefs.SkipFrames * 20000; // 20ms per frame SendIO((struct IORequest *)timer_io); GetSysTime(&start_time); TheDisplay->Speedometer(speed_index); } } /* * Open/close joystick drivers given old and new state of * joystick preferences */ void C64::open_close_joysticks(int oldjoy1, int oldjoy2, int newjoy1, int newjoy2) { if (game_open && (oldjoy2 != newjoy2)) if (newjoy2) { // Open joystick joy_state = 0xff; port_allocated = false; // Allocate game port BYTE ctype; Forbid(); game_io->io_Command = GPD_ASKCTYPE; game_io->io_Data = &ctype; game_io->io_Length = 1; DoIO((struct IORequest *)game_io); if (ctype != GPCT_NOCONTROLLER) Permit(); else { ctype = GPCT_ABSJOYSTICK; game_io->io_Command = GPD_SETCTYPE; game_io->io_Data = &ctype; game_io->io_Length = 1; DoIO((struct IORequest *)game_io); Permit(); port_allocated = true; // Set trigger conditions game_trigger.gpt_Keys = GPTF_UPKEYS | GPTF_DOWNKEYS; game_trigger.gpt_Timeout = 65535; game_trigger.gpt_XDelta = 1; game_trigger.gpt_YDelta = 1; game_io->io_Command = GPD_SETTRIGGER; game_io->io_Data = &game_trigger; game_io->io_Length = sizeof(struct GamePortTrigger); DoIO((struct IORequest *)game_io); // Flush device buffer game_io->io_Command = CMD_CLEAR; DoIO((struct IORequest *)game_io); // Start reading joystick events game_io->io_Command = GPD_READEVENT; game_io->io_Data = &game_event; game_io->io_Length = sizeof(struct InputEvent); SendIO((struct IORequest *)game_io); } } else { // Close joystick // Abort game_io if (!CheckIO((struct IORequest *)game_io)) { AbortIO((struct IORequest *)game_io); WaitIO((struct IORequest *)game_io); } // Free game port if (port_allocated) { BYTE ctype = GPCT_NOCONTROLLER; game_io->io_Command = GPD_SETCTYPE; game_io->io_Data = &ctype; game_io->io_Length = 1; DoIO((struct IORequest *)game_io); port_allocated = false; } } } /* * Poll joystick port, return CIA mask */ uint8 C64::poll_joystick(int port) { if (port == 0) return 0xff; if (game_open && port_allocated) { // Joystick event arrived? while (GetMsg(game_port) != NULL) { // Yes, analyze event switch (game_event.ie_Code) { case IECODE_LBUTTON: // Button pressed joy_state &= 0xef; break; case IECODE_LBUTTON | IECODE_UP_PREFIX: // Button released joy_state |= 0x10; break; case IECODE_NOBUTTON: // Joystick moved if (game_event.ie_X == 1) joy_state &= 0xf7; // Right if (game_event.ie_X == -1) joy_state &= 0xfb; // Left if (game_event.ie_X == 0) joy_state |= 0x0c; if (game_event.ie_Y == 1) joy_state &= 0xfd; // Down if (game_event.ie_Y == -1) joy_state &= 0xfe; // Up if (game_event.ie_Y == 0) joy_state |= 0x03; break; } // Start reading the next event game_io->io_Command = GPD_READEVENT; game_io->io_Data = &game_event; game_io->io_Length = sizeof(struct InputEvent); SendIO((struct IORequest *)game_io); } return joy_state; } else return 0xff; } /* * The emulation's main loop */ void C64::thread_func(void) { while (!quit_thyself) { #ifdef FRODO_SC // The order of calls is important here if (TheVIC->EmulateCycle()) TheSID->EmulateLine(); TheCIA1->CheckIRQs(); TheCIA2->CheckIRQs(); TheCIA1->EmulateCycle(); TheCIA2->EmulateCycle(); TheCPU->EmulateCycle(); if (ThePrefs.Emul1541Proc) { TheCPU1541->CountVIATimers(1); if (!TheCPU1541->Idle) TheCPU1541->EmulateCycle(); } CycleCounter++; #else // The order of calls is important here int cycles = TheVIC->EmulateLine(); TheSID->EmulateLine(); #if !PRECISE_CIA_CYCLES TheCIA1->EmulateLine(ThePrefs.CIACycles); TheCIA2->EmulateLine(ThePrefs.CIACycles); #endif if (ThePrefs.Emul1541Proc) { int cycles_1541 = ThePrefs.FloppyCycles; TheCPU1541->CountVIATimers(cycles_1541); if (!TheCPU1541->Idle) { // 1541 processor active, alternately execute // 6502 and 6510 instructions until both have // used up their cycles while (cycles >= 0 || cycles_1541 >= 0) if (cycles > cycles_1541) cycles -= TheCPU->EmulateLine(1); else cycles_1541 -= TheCPU1541->EmulateLine(1); } else TheCPU->EmulateLine(cycles); } else // 1541 processor disabled, only emulate 6510 TheCPU->EmulateLine(cycles); #endif } }