/* * C64_x.h - Put the pieces together, X 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 "main.h" #include #ifndef R14 static struct timeval tv_start; #endif #ifndef HAVE_USLEEP /* * NAME: * usleep -- This is the precision timer for Test Set * Automation. It uses the select(2) system * call to delay for the desired number of * micro-seconds. This call returns ZERO * (which is usually ignored) on successful * completion, -1 otherwise. * * ALGORITHM: * 1) We range check the passed in microseconds and log a * warning message if appropriate. We then return without * delay, flagging an error. * 2) Load the Seconds and micro-seconds portion of the * interval timer structure. * 3) Call select(2) with no file descriptors set, just the * timer, this results in either delaying the proper * ammount of time or being interupted early by a signal. * * HISTORY: * Added when the need for a subsecond timer was evident. * * AUTHOR: * Michael J. Dyer Telephone: AT&T 414.647.4044 * General Electric Medical Systems GE DialComm 8 *767.4044 * P.O. Box 414 Mail Stop 12-27 Sect'y AT&T 414.647.4584 * Milwaukee, Wisconsin USA 53201 8 *767.4584 * internet: mike@sherlock.med.ge.com GEMS WIZARD e-mail: DYER */ #include #include #include #include #include #include #include #include int usleep(unsigned long int microSeconds) { unsigned int Seconds, uSec; int nfds, readfds, writefds, exceptfds; struct timeval Timer; nfds = readfds = writefds = exceptfds = 0; if( (microSeconds == (unsigned long) 0) || microSeconds > (unsigned long) 4000000 ) { errno = ERANGE; /* value out of range */ perror( "usleep time out of range ( 0 -> 4000000 ) " ); return -1; } Seconds = microSeconds / (unsigned long) 1000000; uSec = microSeconds % (unsigned long) 1000000; Timer.tv_sec = Seconds; Timer.tv_usec = uSec; if( select( nfds, &readfds, &writefds, &exceptfds, &Timer ) < 0 ) { perror( "usleep (select) failed" ); return -1; } return 0; } #endif #ifdef __linux__ // select() timing is much more accurate under Linux static void Delay_usec(unsigned long usec) { int was_error; struct timeval tv; tv.tv_sec = 0; tv.tv_usec = usec; do { errno = 0; was_error = select(0, NULL, NULL, NULL, &tv); } while (was_error && (errno == EINTR)); } #else static void Delay_usec(unsigned long usec) { usleep(usec); } #endif /* * Constructor, system-dependent things */ void C64::c64_ctor1(void) { // Initialize joystick variables joy_minx[0] = joy_miny[0] = 32767; joy_maxx[0] = joy_maxy[0] = -32768; joy_minx[1] = joy_miny[1] = 32767; joy_maxx[1] = joy_maxy[1] = -32768; } void C64::c64_ctor2(void) { #ifndef R14 gettimeofday(&tv_start, NULL); #endif } /* * Destructor, system-dependent things */ void C64::c64_dtor(void) { } /* * Start main emulation thread */ 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); quit_thyself = false; thread_func(); } char kbd_feedbuf[255]; int kbd_feedbuf_pos; void kbd_buf_feed(char *s) { strcpy(kbd_feedbuf, s); kbd_feedbuf_pos=0; } void kbd_buf_update(C64 *TheC64) { if((kbd_feedbuf[kbd_feedbuf_pos]!=0) && TheC64->RAM[198]==0) { TheC64->RAM[631]=kbd_feedbuf[kbd_feedbuf_pos]; TheC64->RAM[198]=1; kbd_feedbuf_pos++; } } void load_prg(C64 *TheC64, uint8 *prg, int prg_size) { uint8 start_hi, start_lo; uint16 start; int i; start_lo=*prg++; start_hi=*prg++; start=(start_hi<<8)+start_lo; for(i=0; i<(prg_size-2); i++) { TheC64->RAM[start+i]=prg[i]; } } /* * Vertical blank: Poll keyboard and joysticks, update window */ void C64::VBlank(bool draw_frame) { // Poll keyboard TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey); if (TheDisplay->quit_requested) quit_thyself = true; // 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(); #ifndef R14 // Calculate time between VBlanks, display speedometer struct timeval tv; gettimeofday(&tv, NULL); if ((tv.tv_usec -= tv_start.tv_usec) < 0) { tv.tv_usec += 1000000; tv.tv_sec -= 1; } tv.tv_sec -= tv_start.tv_sec; double elapsed_time = (double)tv.tv_sec * 1000000 + tv.tv_usec; speed_index = 20000 / (elapsed_time + 1) * ThePrefs.SkipFrames * 100; // Limit speed to 100% if desired if ((speed_index > 100) && ThePrefs.LimitSpeed) { Delay_usec((unsigned long)(ThePrefs.SkipFrames * 20000 - elapsed_time)); speed_index = 100; } gettimeofday(&tv_start, NULL); #endif kbd_buf_update(this); TheDisplay->Speedometer((int)speed_index); } } /* * The emulation's main loop */ void C64::thread_func(void) { int linecnt = 0; #ifdef FRODO_SC while (!quit_thyself) { // 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 while (!quit_thyself) { // 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 linecnt++; #if !defined(__svgalib__) && !defined(WII) if ((linecnt & 0xfff) == 0 && gui) { // check for command from GUI process // fprintf(stderr,":"); while (gui->probe()) { char c; if (gui->eread(&c, 1) != 1) { delete gui; gui = 0; fprintf(stderr,"Oops, GUI process died...\n"); } else { // fprintf(stderr,"%c",c); switch (c) { case 'q': quit_thyself = true; break; case 'r': Reset(); break; case 'p':{ Prefs *np = Frodo::reload_prefs(); NewPrefs(np); ThePrefs = *np; break; } default: break; } } } } #endif } }