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