From 2c437b27da24a0a022364379c859399ee98f6689 Mon Sep 17 00:00:00 2001
From: Brad Grantham <brad.grantham@gmail.com>
Date: Wed, 8 Aug 2018 16:18:16 -0700
Subject: [PATCH] record at 50ms simulation per frame

Change bare "5" to more descriptive "recording_frame_duration_hundredths".

Create APPLE2Einterface events that allow interface to limit main loop speed.

Implement rate limiting on start_record and release limiting on stop_record.

During recording, simulation will not run at clock rate.
---
 apple2e.cpp   | 22 +++++++++++++++++-----
 interface.cpp |  7 +++++--
 interface.h   |  8 +++++++-
 3 files changed, 29 insertions(+), 8 deletions(-)

diff --git a/apple2e.cpp b/apple2e.cpp
index 5b197fd..7d175f1 100644
--- a/apple2e.cpp
+++ b/apple2e.cpp
@@ -41,6 +41,9 @@ volatile bool exit_on_memory_fallthrough = true;
 volatile bool run_fast = false;
 volatile bool pause_cpu = false;
 
+bool run_rate_limited = false;
+int rate_limit_millis;
+
 // XXX - this should be handled through a function passed to MAINboard
 APPLE2Einterface::ModeHistory mode_history;
 
@@ -3053,8 +3056,14 @@ enum APPLE2Einterface::EventType process_events(MAINboard *board, bus_frontend&
             pause_cpu = e.value;
         } else if(e.type == APPLE2Einterface::SPEED) {
             run_fast = e.value;
-        } else if(e.type == APPLE2Einterface::QUIT)
-            return e.type;
+        } else if(e.type == APPLE2Einterface::QUIT) {
+            return e.type; 
+        } else if(e.type == APPLE2Einterface::REQUEST_ITERATION_PERIOD_IN_MILLIS) {
+            run_rate_limited = true;
+            rate_limit_millis = e.value;
+        } else if(e.type == APPLE2Einterface::WITHDRAW_ITERATION_PERIOD_REQUEST) {
+            run_rate_limited = false;
+        }
     }
     return APPLE2Einterface::NONE;
 }
@@ -3215,10 +3224,13 @@ int main(int argc, char **argv)
             if(pause_cpu)
                 clocks_per_slice = 0;
             else {
-                if(run_fast)
+                if(run_rate_limited) {
+                    clocks_per_slice = machine_clock_rate / 1000 * rate_limit_millis; 
+                } else if(run_fast) {
                     clocks_per_slice = machine_clock_rate / 5; 
-                else
+                } else {
                     clocks_per_slice = millis_per_slice * machine_clock_rate / 1000 * 1.05;
+                }
             }
             clk_t prev_clock = clk;
             while(clk - prev_clock < clocks_per_slice) {
@@ -3243,7 +3255,7 @@ int main(int argc, char **argv)
 
             auto elapsed_millis = chrono::duration_cast<chrono::milliseconds>(now - then);
             if(!run_fast || pause_cpu)
-                this_thread::sleep_for(chrono::milliseconds(millis_per_slice) - elapsed_millis);
+                this_thread::sleep_for(chrono::milliseconds(clocks_per_slice * 1000 / machine_clock_rate) - elapsed_millis);
 
             then = now;
             
diff --git a/interface.cpp b/interface.cpp
index 2950fff..aad0d50 100644
--- a/interface.cpp
+++ b/interface.cpp
@@ -179,6 +179,7 @@ render_target::render_target(int w, int h)
 const int apple2_screen_width = 280;
 const int apple2_screen_height = 192;
 const int recording_scale = 2;
+const int recording_frame_duration_hundredths = 5;
 
 chrono::time_point<chrono::system_clock> start_time;
 
@@ -1753,6 +1754,7 @@ static void stop_record()
     if (gif_recording) {
         GifEnd(&gif_writer);
         gif_recording = false;
+        event_queue.push_back({WITHDRAW_ITERATION_PERIOD_REQUEST, 0});
     }
 }
 
@@ -1769,7 +1771,8 @@ static void start_record()
         rendertarget_for_recording = new render_target(apple2_screen_width * recording_scale, apple2_screen_height * recording_scale);
     }
 
-    GifBegin(&gif_writer, "out.gif", apple2_screen_width * recording_scale, apple2_screen_height * recording_scale, 5);
+    GifBegin(&gif_writer, "out.gif", apple2_screen_width * recording_scale, apple2_screen_height * recording_scale, recording_frame_duration_hundredths);
+    event_queue.push_back({REQUEST_ITERATION_PERIOD_IN_MILLIS, recording_frame_duration_hundredths * 10});
     gif_recording = true;
 }
 
@@ -1886,7 +1889,7 @@ void add_rendertarget_to_gif(double now, render_target *rt)
             save_rgba_to_ppm(image_recorded, apple2_screen_width * recording_scale, apple2_screen_height * recording_scale, "screen.ppm");
         }
 
-        GifWriteFrame(&gif_writer, image_recorded, apple2_screen_width * recording_scale, apple2_screen_height * recording_scale, 5, 8, true);
+        GifWriteFrame(&gif_writer, image_recorded, apple2_screen_width * recording_scale, apple2_screen_height * recording_scale, recording_frame_duration_hundredths, 8, true);
 
     rt->stop_reading();
 }
diff --git a/interface.h b/interface.h
index 68fbf3b..bd5b9d2 100644
--- a/interface.h
+++ b/interface.h
@@ -3,7 +3,13 @@
 
 namespace APPLE2Einterface
 {
-enum EventType {NONE, KEYDOWN, KEYUP, RESET, REBOOT, PASTE, SPEED, QUIT, PAUSE, EJECT_FLOPPY, INSERT_FLOPPY};
+
+enum EventType
+{
+    NONE, KEYDOWN, KEYUP, RESET, REBOOT, PASTE, SPEED, QUIT, PAUSE, EJECT_FLOPPY, INSERT_FLOPPY,
+    REQUEST_ITERATION_PERIOD_IN_MILLIS,         /* request fixed simulation time period between calls to iterate() */
+    WITHDRAW_ITERATION_PERIOD_REQUEST,          /* withdraw request for fixed simulation time */
+};
 
 const int LEFT_SHIFT = 340;
 const int LEFT_CONTROL = 341;