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