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();
55  ReverseChunk(ReverseChunk&& rhs);
56  ReverseChunk& operator=(ReverseChunk&& rhs);
57 
58  EmuTime time;
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  };
78 
79  bool isCollecting() const { return collecting; }
80 
81  void start();
82  void stop();
83  void status(TclObject& result) const;
84  void debugInfo(TclObject& result) const;
85  void goBack(array_ref<TclObject> tokens);
86  void goTo(array_ref<TclObject> tokens);
87  void saveReplay(Interpreter& interp,
88  array_ref<TclObject> tokens, TclObject& result);
89  void loadReplay(Interpreter& interp,
90  array_ref<TclObject> tokens, TclObject& result);
91 
92  void signalStopReplay(EmuTime::param time);
93  EmuTime::param getEndTime(const ReverseHistory& history) const;
94  void goTo(EmuTime::param targetTime, bool novideo);
95  void goTo(EmuTime::param targetTime, bool novideo,
96  ReverseHistory& history, bool sameTimeLine);
97  void transferHistory(ReverseHistory& oldHistory,
98  unsigned oldEventCount);
99  void transferState(MSXMotherBoard& newBoard);
100  void takeSnapshot(EmuTime::param time);
101  void schedule(EmuTime::param time);
102  void replayNextEvent();
103  template<unsigned N> void dropOldSnapshots(unsigned count);
104 
105  // Schedulable
106  struct SyncNewSnapshot : Schedulable {
107  friend class ReverseManager;
108  SyncNewSnapshot(Scheduler& s) : Schedulable(s) {}
109  void executeUntil(EmuTime::param /*time*/) override {
110  auto& rm = OUTER(ReverseManager, syncNewSnapshot);
111  rm.execNewSnapshot();
112  }
113  } syncNewSnapshot;
114  struct SyncInputEvent : Schedulable {
115  friend class ReverseManager;
116  SyncInputEvent(Scheduler& s) : Schedulable(s) {}
117  void executeUntil(EmuTime::param /*time*/) override {
118  auto& rm = OUTER(ReverseManager, syncInputEvent);
119  rm.execInputEvent();
120  }
121  } syncInputEvent;
122 
123  void execNewSnapshot();
124  void execInputEvent();
125  EmuTime::param getCurrentTime() const { return syncNewSnapshot.getCurrentTime(); }
126 
127  // EventListener
128  int signalEvent(const std::shared_ptr<const Event>& event) override;
129 
130  // StateChangeRecorder
131  void signalStateChange(const std::shared_ptr<StateChange>& event) override;
132  void stopReplay(EmuTime::param time) override;
133  bool isReplaying() const override;
134 
135  MSXMotherBoard& motherBoard;
136  EventDistributor& eventDistributor;
137 
138  struct ReverseCmd final : Command {
139  ReverseCmd(CommandController& controller);
140  void execute(array_ref<TclObject> tokens, TclObject& result) override;
141  std::string help(const std::vector<std::string>& tokens) const override;
142  void tabCompletion(std::vector<std::string>& tokens) const override;
143  } reverseCmd;
144 
145  Keyboard* keyboard;
146  EventDelay* eventDelay;
147  ReverseHistory history;
148  unsigned replayIndex;
149  bool collecting;
150  bool pendingTakeSnapshot;
151 
152  unsigned reRecordCount;
153 
154  friend struct Replay;
155 };
156 
157 } // namespace openmsx
158 
159 #endif
This class is responsible for translating host events into MSX events.
Definition: EventDelay.hh:26
void registerEventDelay(EventDelay &eventDelay_)
const EmuTime & param
Definition: EmuTime.hh:20
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
size_t size() const
#define OUTER(type, member)
Definition: outer.hh:38