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 <vector>
12 #include <map>
13 #include <memory>
14 #include <cstdint>
15 
16 namespace openmsx {
17 
18 class MSXMotherBoard;
19 class Keyboard;
20 class EventDelay;
21 class EventDistributor;
22 class TclObject;
23 class Interpreter;
24 
25 class ReverseManager final : private EventListener, private StateChangeRecorder
26 {
27 public:
28  ReverseManager(MSXMotherBoard& motherBoard);
30 
31  // Keyboard is special because we need to transfer the host keyboard
32  // state on 'reverse goto' to be able to resynchronize when replay
33  // stops. See Keyboard::transferHostKeyMatrix() for more info.
34  void registerKeyboard(Keyboard& keyboard_) {
35  keyboard = &keyboard_;
36  }
37 
38  // To not loose any events we need to flush delayed events before
39  // switching machine. See comments in goTo() for more info.
40  void registerEventDelay(EventDelay& eventDelay_) {
41  eventDelay = &eventDelay_;
42  }
43 
44  // Should only be used by MSXMotherBoard to be able to transfer
45  // reRecordCount to ReverseManager for version 2 of MSXMotherBoard
46  // serializers.
47  void setReRecordCount(unsigned count) {
48  reRecordCount = count;
49  }
50 
51 private:
52  struct ReverseChunk {
53  ReverseChunk();
54  ReverseChunk(ReverseChunk&& rhs);
55  ReverseChunk& operator=(ReverseChunk&& rhs);
56 
57  EmuTime time;
58  MemBuffer<uint8_t> savestate;
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(array_ref<TclObject> tokens, TclObject& result);
86  void loadReplay(Interpreter& interp,
87  array_ref<TclObject> tokens, TclObject& result);
88 
89  void signalStopReplay(EmuTime::param time);
90  EmuTime::param getEndTime(const ReverseHistory& history) const;
91  void goTo(EmuTime::param targetTime, bool novideo);
92  void goTo(EmuTime::param targetTime, bool novideo,
93  ReverseHistory& history, bool sameTimeLine);
94  void transferHistory(ReverseHistory& oldHistory,
95  unsigned oldEventCount);
96  void transferState(MSXMotherBoard& newBoard);
97  void takeSnapshot(EmuTime::param time);
98  void schedule(EmuTime::param time);
99  void replayNextEvent();
100  template<unsigned N> void dropOldSnapshots(unsigned count);
101 
102  // Schedulable
103  struct SyncBase : public Schedulable {
104  SyncBase(Scheduler& s, ReverseManager& rm_)
105  : Schedulable(s), rm(rm_) {}
106  ReverseManager& rm;
107  friend class ReverseManager;
108  };
109  struct SyncNewSnapshot : public SyncBase {
110  SyncNewSnapshot(Scheduler& s, ReverseManager& m) : SyncBase(s, m) {}
111  void executeUntil(EmuTime::param /*time*/) override {
112  rm.execNewSnapshot();
113  }
114  } syncNewSnapshot;
115  struct SyncInputEvent : public SyncBase {
116  SyncInputEvent(Scheduler& s, ReverseManager& m) : SyncBase(s, m) {}
117  void executeUntil(EmuTime::param /*time*/) override {
118  rm.execInputEvent();
119  }
120  } syncInputEvent;
121 
122  void execNewSnapshot();
123  void execInputEvent();
124  EmuTime::param getCurrentTime() const { return syncNewSnapshot.getCurrentTime(); }
125 
126  // EventListener
127  int signalEvent(const std::shared_ptr<const Event>& event) override;
128 
129  // StateChangeRecorder
130  void signalStateChange(const std::shared_ptr<StateChange>& event) override;
131  void stopReplay(EmuTime::param time) override;
132  bool isReplaying() const override;
133 
134  MSXMotherBoard& motherBoard;
135  EventDistributor& eventDistributor;
136 
137  class ReverseCmd final : public Command {
138  public:
139  ReverseCmd(ReverseManager& manager, 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  private:
144  ReverseManager& manager;
145  } reverseCmd;
146 
147  Keyboard* keyboard;
148  EventDelay* eventDelay;
149  ReverseHistory history;
150  unsigned replayIndex;
151  bool collecting;
152  bool pendingTakeSnapshot;
153 
154  unsigned reRecordCount;
155 
156  friend struct Replay;
157 };
158 
159 } // namespace openmsx
160 
161 #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