--- previous/src/main.c 2018/04/24 19:25:10 1.1 +++ previous/src/main.c 2018/04/24 19:29:49 1.1.1.5 @@ -9,6 +9,7 @@ const char Main_fileid[] = "Hatari main.c : " __DATE__ " " __TIME__; #include +#include #include #include "main.h" @@ -17,11 +18,13 @@ const char Main_fileid[] = "Hatari main. #include "options.h" #include "dialog.h" #include "ioMem.h" +#include "keymap.h" #include "log.h" #include "m68000.h" #include "memorySnapShot.h" #include "paths.h" #include "reset.h" +#include "resolution.h" #include "screen.h" #include "sdlgui.h" #include "shortcut.h" @@ -29,16 +32,24 @@ const char Main_fileid[] = "Hatari main. #include "nextMemory.h" #include "str.h" #include "video.h" +#include "audio.h" #include "avi_record.h" #include "debugui.h" -#include "keymap.h" +#include "clocks_timings.h" +#include "file.h" +#include "dsp.h" + #include "hatari-glue.h" +#if HAVE_GETTIMEOFDAY +#include +#endif +int nFrameSkips; bool bQuitProgram = false; /* Flag to quit program cleanly */ -Uint32 nRunVBLs; /* Whether and how many VBLS to run before exit */ +static Uint32 nRunVBLs; /* Whether and how many VBLS to run before exit */ static Uint32 nFirstMilliTick; /* Ticks when VBL counting started */ static Uint32 nVBLCount; /* Frame count */ @@ -79,6 +90,58 @@ static Uint32 Main_GetTicks(void) #endif +//#undef HAVE_GETTIMEOFDAY +//#undef HAVE_NANOSLEEP + +/*-----------------------------------------------------------------------*/ +/** + * Return a time counter in micro seconds. + * If gettimeofday is available, we use it directly, else we convert the + * return of SDL_GetTicks in micro sec. + */ + +static Sint64 Time_GetTicks ( void ) +{ + Sint64 ticks_micro; + +#if HAVE_GETTIMEOFDAY + struct timeval now; + gettimeofday ( &now , NULL ); + ticks_micro = (Sint64)now.tv_sec * 1000000 + now.tv_usec; +#else + ticks_micro = (Sint64)SDL_GetTicks() * 1000; /* milli sec -> micro sec */ +#endif + + return ticks_micro; +} + + +/*-----------------------------------------------------------------------*/ +/** + * Sleep for a given number of micro seconds. + * If nanosleep is available, we use it directly, else we use SDL_Delay + * (which is portable, but less accurate as is uses milli-seconds) + */ + +static void Time_Delay ( Sint64 ticks_micro ) +{ +#if HAVE_NANOSLEEP + struct timespec ts; + int ret; + ts.tv_sec = ticks_micro / 1000000; + ts.tv_nsec = (ticks_micro % 1000000) * 1000; /* micro sec -> nano sec */ + /* wait until all the delay is elapsed, including possible interruptions by signals */ + do + { + errno = 0; + ret = nanosleep(&ts, &ts); + } while ( ret && ( errno == EINTR ) ); /* keep on sleeping if we were interrupted */ +#else + SDL_Delay ( (Uint32)(ticks_micro / 1000) ) ; /* micro sec -> milli sec */ +#endif +} + + /*-----------------------------------------------------------------------*/ /** * Pause emulation, stop sound. 'visualize' should be set true, @@ -91,6 +154,7 @@ bool Main_PauseEmulation(bool visualize) if ( !bEmulationActive ) return false; + //Audio_Output_Enable(false); bEmulationActive = false; if (visualize) { @@ -103,7 +167,7 @@ bool Main_PauseEmulation(bool visualize) current = (1000.0 * nVBLCount) / interval; printf("SPEED: %.1f VBL/s (%d/%.1fs), diff=%.1f%%\n", current, nVBLCount, interval/1000.0, - previous ? 100*(current-previous)/previous : 0.0); + previous>0.0 ? 100*(current-previous)/previous : 0.0); nVBLCount = nFirstMilliTick = 0; previous = current; } @@ -112,9 +176,11 @@ bool Main_PauseEmulation(bool visualize) /* make sure msg gets shown */ Statusbar_Update(sdlscrn); - if (bGrabMouse && !bInFullScreen) + if (bGrabMouse && !bInFullScreen) { /* Un-grab mouse pointer in windowed mode */ - SDL_WM_GrabInput(SDL_GRAB_OFF); + SDL_SetRelativeMouseMode(SDL_FALSE); + SDL_SetWindowGrab(sdlWindow, SDL_FALSE); + } } return true; } @@ -130,14 +196,17 @@ bool Main_UnPauseEmulation(void) if ( bEmulationActive ) return false; + //Audio_Output_Enable(ConfigureParams.Sound.bEnableSound); bEmulationActive = true; /* Cause full screen update (to clear all) */ Screen_SetFullUpdate(); - if (bGrabMouse) + if (bGrabMouse) { /* Grab mouse pointer again */ - SDL_WM_GrabInput(SDL_GRAB_ON); + SDL_SetRelativeMouseMode(SDL_TRUE); + SDL_SetWindowGrab(sdlWindow, SDL_TRUE); + } return true; } @@ -171,19 +240,32 @@ void Main_RequestQuit(void) /*-----------------------------------------------------------------------*/ /** + * Set how many VBLs Hatari should run, from the moment this function + * is called. + */ +void Main_SetRunVBLs(Uint32 vbls) +{ + fprintf(stderr, "Exit after %d VBLs.\n", vbls); + nRunVBLs = vbls; + nVBLCount = 0; +} + +/*-----------------------------------------------------------------------*/ +/** * This function waits on each emulated VBL to synchronize the real time * with the emulated ST. * Unfortunately SDL_Delay and other sleep functions like usleep or nanosleep * are very inaccurate on some systems like Linux 2.4 or Mac OS X (they can only * wait for a multiple of 10ms due to the scheduler on these systems), so we have * to "busy wait" there to get an accurate timing. + * All times are expressed as micro seconds, to avoid too much rounding error. */ void Main_WaitOnVbl(void) { - int nCurrentMilliTicks; - static int nDestMilliTicks = 0; - int nFrameDuration; - signed int nDelay; + Sint64 CurrentTicks; + static Sint64 DestTicks = 0; + Sint64 FrameDuration_micro; + Sint64 nDelay; nVBLCount++; if (nRunVBLs && nVBLCount >= nRunVBLs) @@ -192,62 +274,75 @@ void Main_WaitOnVbl(void) Main_PauseEmulation(true); exit(0); } - nCurrentMilliTicks = SDL_GetTicks(); - nFrameDuration = 1000/50; - nDelay = nDestMilliTicks - nCurrentMilliTicks; +// FrameDuration_micro = (Sint64) ( 1000000.0 / nScreenRefreshRate + 0.5 ); /* round to closest integer */ + FrameDuration_micro = ClocksTimings_GetVBLDuration_micro ( ConfigureParams.System.nMachineType , 68 ); +// FrameDuration_micro = 1000000/50; + CurrentTicks = Time_GetTicks(); + + if ( DestTicks == 0 ) /* first call, init DestTicks */ + { + DestTicks = CurrentTicks + FrameDuration_micro; + } + + nDelay = DestTicks - CurrentTicks; /* Do not wait if we are in fast forward mode or if we are totally out of sync */ if (ConfigureParams.System.bFastForward == true - || nDelay < -4*nFrameDuration) + || nDelay < -4*FrameDuration_micro || nDelay > 50*FrameDuration_micro) { if (ConfigureParams.System.bFastForward == true) { if (!nFirstMilliTick) nFirstMilliTick = Main_GetTicks(); } -// if (nFrameSkips < ConfigureParams.Screen.nFrameSkips) -// { -// nFrameSkips += 1; - // Log_Printf(LOG_DEBUG, "Increased frameskip to %d\n", nFrameSkips); -// } - /* Only update nDestMilliTicks for next VBL */ - nDestMilliTicks = nCurrentMilliTicks + nFrameDuration; + if (nFrameSkips < ConfigureParams.Screen.nFrameSkips) + { + nFrameSkips += 1; + Log_Printf(LOG_DEBUG, "Increased frameskip to %d\n", nFrameSkips); + } + /* Only update DestTicks for next VBL */ + DestTicks = CurrentTicks + FrameDuration_micro; return; } /* If automatic frameskip is enabled and delay's more than twice * the effect of single frameskip, decrease frameskip */ -// if (nFrameSkips > 0 -// && ConfigureParams.Screen.nFrameSkips >= AUTO_FRAMESKIP_LIMIT -// && 2*nDelay > nFrameDuration/nFrameSkips) -// { -// nFrameSkips -= 1; - // Log_Printf(LOG_DEBUG, "Decreased frameskip to %d\n", nFrameSkips); -// } + if (nFrameSkips > 0 + && ConfigureParams.Screen.nFrameSkips >= AUTO_FRAMESKIP_LIMIT + && 2*nDelay > FrameDuration_micro/nFrameSkips) + { + nFrameSkips -= 1; + Log_Printf(LOG_DEBUG, "Decreased frameskip to %d\n", nFrameSkips); + } if (bAccurateDelays) { /* Accurate sleeping is possible -> use SDL_Delay to free the CPU */ - if (nDelay > 1) - SDL_Delay(nDelay - 1); + if (nDelay > 1000) + Time_Delay(nDelay - 1000); } else { /* No accurate SDL_Delay -> only wait if more than 5ms to go... */ - if (nDelay > 5) - SDL_Delay(nDelay<10 ? nDelay-1 : 9); + if (nDelay > 5000) + Time_Delay(nDelay<10000 ? nDelay-1000 : 9000); } /* Now busy-wait for the right tick: */ while (nDelay > 0) { - nCurrentMilliTicks = SDL_GetTicks(); - nDelay = nDestMilliTicks - nCurrentMilliTicks; + CurrentTicks = Time_GetTicks(); + nDelay = DestTicks - CurrentTicks; + /* If the delay is still bigger than one frame, somebody + * played tricks with the system clock and we have to abort */ + if (nDelay > FrameDuration_micro) + break; } - /* Update nDestMilliTicks for next VBL */ - nDestMilliTicks += nFrameDuration; +//printf ( "tick %lld\n" , CurrentTicks ); + /* Update DestTicks for next VBL */ + DestTicks += FrameDuration_micro; } @@ -284,8 +379,8 @@ static void Main_CheckForAccurateDelays( */ void Main_WarpMouse(int x, int y) { - SDL_WarpMouse(x, y); /* Set mouse pointer to new position */ - bIgnoreNextMouseMotion = true; /* Ignore mouse motion event from SDL_WarpMouse */ + SDL_WarpMouseInWindow(sdlWindow, x, y); /* Set mouse pointer to new position */ + bIgnoreNextMouseMotion = true; /* Ignore mouse motion event from SDL_WarpMouse */ } @@ -293,40 +388,28 @@ void Main_WarpMouse(int x, int y) /** * Handle mouse motion event. */ +SDL_Event mymouse[100]; static void Main_HandleMouseMotion(SDL_Event *pEvent) { int dx, dy; - static int ax = 0, ay = 0; - - /* Ignore motion when position has changed right after a reset or TOS - * (especially version 4.04) might get confused and play key clicks */ - if (bIgnoreNextMouseMotion ) - { - bIgnoreNextMouseMotion = false; - return; - } + int i,nb; dx = pEvent->motion.xrel; dy = pEvent->motion.yrel; - /* In zoomed low res mode, we divide dx and dy by the zoom factor so that - * the ST mouse cursor stays in sync with the host mouse. However, we have - * to take care of lowest bit of dx and dy which will get lost when - * dividing. So we store these bits in ax and ay and add them to dx and dy - * the next time. */ - if (nScreenZoomX != 1) - { - dx += ax; - ax = dx % nScreenZoomX; - dx /= nScreenZoomX; - } - if (nScreenZoomY != 1) - { - dy += ay; - ay = dy % nScreenZoomY; - dy /= nScreenZoomY; + /* get all mouse event to clean the queue and sum them */ + nb=SDL_PeepEvents(&mymouse[0], 100, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION); + + for (i=0;i true */ Configuration_Apply(true); @@ -609,6 +745,7 @@ int main(int argc, char *argv[]) Main_UnPauseEmulation(); M68000_Start(); /* Start emulation */ + /* Un-init emulation system */ Main_UnInit();