openMSX
ReverseManager.hh
Go to the documentation of this file.
1 #ifndef REVERSEMANGER_HH
2 #define REVERSEMANGER_HH
3 
4 #include "Schedulable.hh"
5 #include "EventListener.hh"
6 #include "StateChangeListener.hh"
7 #include "Command.hh"
8 #include "EmuTime.hh"
9 #include "MemBuffer.hh"
10 #include "array_ref.hh"
11 #include "outer.hh"
12 #include <vector>
13 #include <map>
14 #include <memory>
15 #include <cstdint>
16 
17 namespace openmsx {
18 
19 class MSXMotherBoard;
20 class Keyboard;
21 class EventDelay;
22 class EventDistributor;
23 class TclObject;
24 class Interpreter;
25 
26 class ReverseManager final : private EventListener, private StateChangeRecorder
27 {
28 public:
29  ReverseManager(MSXMotherBoard& motherBoard);
31 
32  // Keyboard is special because we need to transfer the host keyboard
33  // state on 'reverse goto' to be able to resynchronize when replay
34  // stops. See Keyboard::transferHostKeyMatrix() for more info.
35  void registerKeyboard(Keyboard& keyboard_) {
36  keyboard = &keyboard_;
37  }
38 
39  // To not loose any events we need to flush delayed events before
40  // switching machine. See comments in goTo() for more info.
41  void registerEventDelay(EventDelay& eventDelay_) {
42  eventDelay = &eventDelay_;
43  }
44 
45  // Should only be used by MSXMotherBoard to be able to transfer
46  // reRecordCount to ReverseManager for version 2 of MSXMotherBoard
47  // serializers.
48  void setReRecordCount(unsigned count) {
49  reRecordCount = count;
50  }
51 
52 private:
53  struct ReverseChunk {
54  ReverseChunk() : time(EmuTime::zero) {}
55 
56  EmuTime time;
57  MemBuffer<uint8_t> savestate;
58  size_t size;
59 
60  // Number of recorded events (or replay index) when this
61  // snapshot was created. So when going back replay should
62  // start at this index.
63  unsigned eventCount;
64  };
65  using Chunks = std::map<unsigned, ReverseChunk>;
66  using Events = std::vector<std::shared_ptr<StateChange>>;
67 
68  struct ReverseHistory {
69  void swap(ReverseHistory& other);
70  void clear();
71  unsigned getNextSeqNum(EmuTime::param time) const;
72 
73  Chunks chunks;
74  Events events;
75  };
76 
77  bool isCollecting() const { return collecting; }
78 
79  void start();
80  void stop();
81  void status(TclObject& result) const;
82  void debugInfo(TclObject& result) const;
83  void goBack(array_ref<TclObject> tokens);
84  void goTo(array_ref<TclObject> tokens);
85  void saveReplay(Interpreter& interp,
86  array_ref<TclObject> tokens, TclObject& result);
87  void loadReplay(Interpreter& interp,
88  array_ref<TclObject> tokens, TclObject& result);
89 
90  void signalStopReplay(EmuTime::param time);
91  EmuTime::param getEndTime(const ReverseHistory& history) const;
92  void goTo(EmuTime::param targetTime, bool novideo);
93  void goTo(EmuTime::param targetTime, bool novideo,
94  ReverseHistory& history, bool sameTimeLine);
95  void transferHistory(ReverseHistory& oldHistory,
96  unsigned oldEventCount);
97  void transferState(MSXMotherBoard& newBoard);
98  void takeSnapshot(EmuTime::param time);
99  void schedule(EmuTime::param time);
100  void replayNextEvent();
101  template<unsigned N> void dropOldSnapshots(unsigned count);
102 
103  // Schedulable
104  struct SyncNewSnapshot : Schedulable {
105  friend class ReverseManager;
106  SyncNewSnapshot(Scheduler& s) : Schedulable(s) {}
107  void executeUntil(EmuTime::param /*time*/) override {
108  auto& rm = OUTER(ReverseManager, syncNewSnapshot);
109  rm.execNewSnapshot();
110  }
111  } syncNewSnapshot;
112  struct SyncInputEvent : Schedulable {
113  friend class ReverseManager;
114  SyncInputEvent(Scheduler& s) : Schedulable(s) {}
115  void executeUntil(EmuTime::param /*time*/) override {
116  auto& rm = OUTER(ReverseManager, syncInputEvent);
117  rm.execInputEvent();
118  }
119  } syncInputEvent;
120 
121  void execNewSnapshot();
122  void execInputEvent();
123  EmuTime::param getCurrentTime() const { return syncNewSnapshot.getCurrentTime(); }
124 
125  // EventListener
126  int signalEvent(const std::shared_ptr<const Event>& event) override;
127 
128  // StateChangeRecorder
129  void signalStateChange(const std::shared_ptr<StateChange>& event) override;
130  void stopReplay(EmuTime::param time) override;
131  bool isReplaying() const override;
132 
133  MSXMotherBoard& motherBoard;
134  EventDistributor& eventDistributor;
135 
136  struct ReverseCmd final : Command {
137  ReverseCmd(CommandController& controller);
138  void execute(array_ref<TclObject> tokens, TclObject& result) override;
139  std::string help(const std::vector<std::string>& tokens) const override;
140  void tabCompletion(std::vector<std::string>& tokens) const override;
141  } reverseCmd;
142 
143  Keyboard* keyboard;
144  EventDelay* eventDelay;
145  ReverseHistory history;
146  unsigned replayIndex;
147  bool collecting;
148  bool pendingTakeSnapshot;
149 
150  unsigned reRecordCount;
151 
152  friend struct Replay;
153 };
154 
155 } // namespace openmsx
156 
157 #endif
This class is responsible for translating host events into MSX events.
Definition: EventDelay.hh:26
void registerEventDelay(EventDelay &eventDelay_)
Every class that wants to get scheduled at some point must inherit from this class.
Definition: Schedulable.hh:33
void registerKeyboard(Keyboard &keyboard_)
This class implements a subset of the proposal for std::array_ref (proposed for the next c++ standard...
Definition: array_ref.hh:19
ReverseManager(MSXMotherBoard &motherBoard)
void setReRecordCount(unsigned count)
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
static const EmuTime zero
Definition: EmuTime.hh:57
size_t size() const
#define OUTER(type, member)
Definition: outer.hh:38