frodo-wii/Src/C64_Amiga.h

408 lines
9.1 KiB
C

/*
* C64_Amiga.h - Put the pieces together, Amiga specific stuff
*
* Frodo (C) 1994-1997,2002-2005 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 <proto/exec.h>
#include <proto/timer.h>
// 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
}
}