diff --git a/src/emucore/FrameBuffer.cxx b/src/emucore/FrameBuffer.cxx index 4db75a9c4..63ec56137 100644 --- a/src/emucore/FrameBuffer.cxx +++ b/src/emucore/FrameBuffer.cxx @@ -258,30 +258,17 @@ FBInitStatus FrameBuffer::createDisplay(const string& title, } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Int64 FrameBuffer::update(uInt32 maxCycles) +void FrameBuffer::update() { // Determine which mode we are in (from the EventHandler) // Take care of S_EMULATE mode here, otherwise let the GUI // figure out what to draw - Int64 cycles = -1; - invalidate(); switch(myOSystem.eventHandler().state()) { case EventHandlerState::EMULATION: { - // Run the console for one frame - // Note that the debugger can cause a breakpoint to occur, which changes - // the EventHandler state 'behind our back' - we need to check for that - cycles = myOSystem.console().tia().update(maxCycles); - #ifdef DEBUGGER_SUPPORT - if(myOSystem.eventHandler().state() != EventHandlerState::EMULATION) break; - #endif - if(myOSystem.eventHandler().frying()) - myOSystem.console().fry(); - - // And update the screen myTIASurface->render(); // Show frame statistics @@ -342,7 +329,7 @@ Int64 FrameBuffer::update(uInt32 maxCycles) } case EventHandlerState::NONE: - return -1; + return; } // Draw any pending messages @@ -351,8 +338,6 @@ Int64 FrameBuffer::update(uInt32 maxCycles) // Do any post-frame stuff postFrameUpdate(); - - return cycles; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/FrameBuffer.hxx b/src/emucore/FrameBuffer.hxx index b1806a963..c6533eca4 100644 --- a/src/emucore/FrameBuffer.hxx +++ b/src/emucore/FrameBuffer.hxx @@ -116,7 +116,7 @@ class FrameBuffer drawing the TIA, any pending menus, etc. Returns the numbers of CPU cycles spent during emulation, or -1 if not applicable. */ - Int64 update(uInt32 maxCycles = 50000); + void update(); /** Shows a message onscreen. diff --git a/src/emucore/OSystem.cxx b/src/emucore/OSystem.cxx index d6c9b1760..e0126e4aa 100644 --- a/src/emucore/OSystem.cxx +++ b/src/emucore/OSystem.cxx @@ -52,6 +52,7 @@ #include "SerialPort.hxx" #include "StateManager.hxx" #include "Version.hxx" +#include "TIA.hxx" #include "OSystem.hxx" @@ -646,23 +647,38 @@ void OSystem::mainLoop() myEventHandler->poll(getTicks()); if(myQuitLoop) break; // Exit if the user wants to quit - Int64 totalCycles = 0; - const Int64 minCycles = myConsole ? myConsole->emulationTiming().minCyclesPerTimeslice() : 50000; - const Int64 maxCycles = myConsole ? myConsole->emulationTiming().maxCyclesPerTimeslice() : 0; - const uInt32 cyclesPerSecond = myConsole ? myConsole->emulationTiming().cyclesPerSecond() : 1; + double timesliceSeconds; - do { - Int64 cycles = myFrameBuffer->update(totalCycles > 0 ? minCycles - totalCycles : maxCycles); - if (cycles < 0) break; + if (myEventHandler->state() == EventHandlerState::EMULATION) { + Int64 totalCycles = 0; + const Int64 minCycles = myConsole ? myConsole->emulationTiming().minCyclesPerTimeslice() : 50000; + const Int64 maxCycles = myConsole ? myConsole->emulationTiming().maxCyclesPerTimeslice() : 0; + const uInt32 cyclesPerSecond = myConsole ? myConsole->emulationTiming().cyclesPerSecond() : 1; - totalCycles += cycles; - } while (totalCycles < minCycles); + do { + Int64 cycles = myConsole ? myConsole->tia().update(totalCycles > 0 ? minCycles - totalCycles : maxCycles) : 0; - duration timeslice ( - (totalCycles > 0) ? - static_cast(totalCycles) / static_cast(cyclesPerSecond) : - 1. / 30. - ); + totalCycles += cycles; + } while (myConsole && totalCycles < minCycles && myEventHandler->state() == EventHandlerState::EMULATION); + + if (myEventHandler->state() == EventHandlerState::EMULATION && myEventHandler->frying()) + myConsole->fry(); + + timesliceSeconds = static_cast(totalCycles) / static_cast(cyclesPerSecond); + } + else + timesliceSeconds = 1. / 30.; + + if (myEventHandler->state() == EventHandlerState::EMULATION) { + if (myConsole && myConsole->tia().newFramePending()) { + myFrameBuffer->update(); + myConsole->tia().clearNewFramePending(); + } + } + else + myFrameBuffer->update(); + + duration timeslice(timesliceSeconds); virtualTime += duration_cast(timeslice); time_point now = high_resolution_clock::now(); @@ -670,7 +686,7 @@ void OSystem::mainLoop() if (duration_cast>(now - virtualTime).count() > 0) virtualTime = now; else if (virtualTime > now) { - if (busyWait && totalCycles > 0) { + if (busyWait && myEventHandler->state() == EventHandlerState::EMULATION) { while (high_resolution_clock::now() < virtualTime); } else std::this_thread::sleep_until(virtualTime); diff --git a/src/emucore/tia/TIA.cxx b/src/emucore/tia/TIA.cxx index 84f2bd8d1..d3722bc16 100644 --- a/src/emucore/tia/TIA.cxx +++ b/src/emucore/tia/TIA.cxx @@ -180,6 +180,8 @@ void TIA::reset() frameReset(); // Recalculate the size of the display } + myNewFramePending = false; + // Must be done last, after all other items have reset enableFixedColors(mySettings.getBool(mySettings.getBool("dev.settings") ? "dev.debugcolors" : "plr.debugcolors")); setFixedColorPalette(mySettings.getString("tia.dbgcolors")); @@ -192,6 +194,7 @@ void TIA::reset() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TIA::frameReset() { + memset(myBackBuffer, 0, 160 * TIAConstants::frameBufferHeight); memset(myFramebuffer, 0, 160 * TIAConstants::frameBufferHeight); enableColorLoss(mySettings.getBool("dev.settings") ? "dev.colorloss" : "plr.colorloss"); } @@ -778,7 +781,9 @@ bool TIA::saveDisplay(Serializer& out) const { try { - out.putByteArray(myFramebuffer, 160*TIAConstants::frameBufferHeight); + out.putByteArray(myFramebuffer, 160* TIAConstants::frameBufferHeight); + out.putByteArray(myBackBuffer, 160 * TIAConstants::frameBufferHeight); + out.putBool(myNewFramePending); } catch(...) { @@ -796,6 +801,8 @@ bool TIA::loadDisplay(Serializer& in) { // Reset frame buffer pointer and data in.getByteArray(myFramebuffer, 160*TIAConstants::frameBufferHeight); + in.getByteArray(myBackBuffer, 160 * TIAConstants::frameBufferHeight); + myNewFramePending = in.getBool(); } catch(...) { @@ -1149,12 +1156,15 @@ void TIA::onFrameComplete() myCyclesAtFrameStart = mySystem->cycles(); if (myXAtRenderingStart > 0) - memset(myFramebuffer, 0, myXAtRenderingStart); + memset(myBackBuffer, 0, myXAtRenderingStart); // Blank out any extra lines not drawn this frame const Int32 missingScanlines = myFrameManager->missingScanlines(); if (missingScanlines > 0) - memset(myFramebuffer + 160 * myFrameManager->getY(), 0, missingScanlines * 160); + memset(myBackBuffer + 160 * myFrameManager->getY(), 0, missingScanlines * 160); + + memcpy(&myFramebuffer, &myBackBuffer, 160 * TIAConstants::frameBufferHeight); + myNewFramePending = true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1265,7 +1275,7 @@ void TIA::applyRsync() myHctrDelta = 225 - myHctr; if (myFrameManager->isRendering()) - memset(myFramebuffer + myFrameManager->getY() * 160 + x, 0, 160 - x); + memset(myBackBuffer + myFrameManager->getY() * 160 + x, 0, 160 - x); myHctr = 225; } @@ -1304,7 +1314,7 @@ void TIA::cloneLastLine() if (!myFrameManager->isRendering() || y == 0) return; - uInt8* buffer = myFramebuffer; + uInt8* buffer = myBackBuffer; memcpy(buffer + y * 160, buffer + (y-1) * 160, 160); } @@ -1377,7 +1387,7 @@ void TIA::renderPixel(uInt32 x, uInt32 y) } } - myFramebuffer[y * 160 + x] = color; + myBackBuffer[y * 160 + x] = color; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1403,7 +1413,7 @@ void TIA::flushLineCache() void TIA::clearHmoveComb() { if (myFrameManager->isRendering() && myHstate == HState::blank) - memset(myFramebuffer + myFrameManager->getY() * 160, myColorHBlank, 8); + memset(myBackBuffer + myFrameManager->getY() * 160, myColorHBlank, 8); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/tia/TIA.hxx b/src/emucore/tia/TIA.hxx index 03b1161b8..d0eced062 100644 --- a/src/emucore/tia/TIA.hxx +++ b/src/emucore/tia/TIA.hxx @@ -201,6 +201,16 @@ class TIA : public Device */ uInt64 update(uInt32 maxCycles = 50000); + /** + Did we generate a new frame? + */ + bool newFramePending() { return myNewFramePending; } + + /** + Clear the flag + */ + void clearNewFramePending() { myNewFramePending = false; } + /** Returns a pointer to the internal frame buffer. */ @@ -657,6 +667,13 @@ class TIA : public Device // Pointer to the internal color-index-based frame buffer uInt8 myFramebuffer[160 * TIAConstants::frameBufferHeight]; + // The frame is rendered to the backbuffer and only copied to the framebuffer + // upon completion + uInt8 myBackBuffer[160 * TIAConstants::frameBufferHeight]; + + // Did we emit a frame? + bool myNewFramePending; + /** * Setting this to true injects random values into undefined reads. */