OXYGENHUB
◆ main openmsx / src / video / v9990 / Video9000.cc
5.0 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "Video9000.hh"
2
3
#include "Display.hh"
4
#include "PostProcessor.hh"
5
#include "V9990.hh"
6
#include "VDP.hh"
7
8
#include "CommandException.hh"
9
#include "Event.hh"
10
#include "EventDistributor.hh"
11
#include "MSXMotherBoard.hh"
12
#include "Reactor.hh"
13
#include "VideoSourceSetting.hh"
14
#include "serialize.hh"
15
16
namespace openmsx {
17
18
Video9000::Video9000(const DeviceConfig& config)
19
	: MSXDevice(config)
20
	, VideoLayer(getMotherBoard(), getName())
21
	, videoSourceSetting(getMotherBoard().getVideoSource())
22
{
23
	// we can't set activeLayer yet
24
	EventDistributor& distributor = getReactor().getEventDistributor();
25
	distributor.registerEventListener(EventType::FINISH_FRAME, *this);
26
	getReactor().getDisplay().attach(*this);
27
}
28
29
void Video9000::init()
30
{
31
	MSXDevice::init();
32
	auto refs = getReferences(); // make copy
33
	bool error = false;
34
	if (refs.size() != 2) error = true;
35
	if (!error && !dynamic_cast<VDP*>(refs[0])) {
36
		std::swap(refs[0], refs[1]); // try reverse order
37
	}
38
	if (!error) vdp   = dynamic_cast<VDP*  >(refs[0]);
39
	if (!error) v9990 = dynamic_cast<V9990*>(refs[1]);
40
	if (error || !vdp || !v9990) {
41
		throw MSXException(
42
			"Invalid Video9000 configuration: "
43
			"need reference to VDP and V9990 device.");
44
	}
45
	reset(EmuTime::dummy());
46
}
47
48
Video9000::~Video9000()
49
{
50
	EventDistributor& distributor = getReactor().getEventDistributor();
51
	distributor.unregisterEventListener(EventType::FINISH_FRAME, *this);
52
	getReactor().getDisplay().detach(*this);
53
}
54
55
void Video9000::reset(EmuTime time)
56
{
57
	Video9000::writeIO(0x6f, 0x10, time);
58
}
59
60
void Video9000::writeIO(uint16_t /*port*/, uint8_t newValue, EmuTime /*time*/)
61
{
62
	if (newValue == value) return;
63
	value = newValue;
64
	recalculate();
65
}
66
67
void Video9000::recalculate()
68
{
69
	int video9000id = getVideoSource();
70
	v99x8Layer = vdp  ->getPostProcessor();
71
	v9990Layer = v9990->getPostProcessor();
72
	assert(!!v99x8Layer == !!v9990Layer); // either both or neither
73
74
	// ...0.... -> only V9990
75
	// ...10... -> only V99x8
76
	// ...11... -> superimpose
77
	bool showV99x8   = ((value & 0x10) == 0x10);
78
	bool showV9990   = ((value & 0x18) != 0x10);
79
	assert(showV99x8 || showV9990);
80
	using enum VideoLayer::Video9000Active;
81
	if (v99x8Layer) v99x8Layer->setVideo9000Active(
82
		video9000id,
83
		showV99x8 ? (showV9990 ? BACK : FRONT) : NO);
84
	if (v9990Layer) v9990Layer->setVideo9000Active(
85
		video9000id, showV9990 ? FRONT : NO);
86
	activeLayer = showV9990 ? v9990Layer : v99x8Layer;
87
	// activeLayer==nullptr is possible for renderer=none
88
	recalculateVideoSource();
89
}
90
91
void Video9000::recalculateVideoSource()
92
{
93
	// Disable superimpose when gfx9000 layer is selected. That way you
94
	// can look at the gfx9000-only output even when the video9000 software
95
	// enabled superimpose mode (mostly useful for debugging).
96
	bool superimpose = ((value & 0x18) == 0x18);
97
	v9990->setExternalVideoSource(
98
		superimpose && (videoSourceSetting.getSource() == getVideoSource()));
99
}
100
101
void Video9000::preVideoSystemChange() noexcept
102
{
103
	activeLayer = nullptr; // will be recalculated on next paint()
104
}
105
106
void Video9000::postVideoSystemChange() noexcept
107
{
108
	// We can't yet re-initialize 'activeLayer' here because the
109
	// new v99x8/v9990 layer may not be created yet.
110
}
111
112
void Video9000::paint(OutputSurface& output)
113
{
114
	if (!activeLayer) {
115
		recalculate();
116
	}
117
	// activeLayer==nullptr is possible for renderer=none, but in that case
118
	// the paint() method will never be called.
119
	activeLayer->paint(output);
120
}
121
122
void Video9000::takeRawScreenShot(std::optional<unsigned> height, const std::string& filename)
123
{
124
	auto* layer = dynamic_cast<VideoLayer*>(activeLayer);
125
	if (!layer) {
126
		throw CommandException("TODO");
127
	}
128
	layer->takeRawScreenShot(height, filename);
129
}
130
131
bool Video9000::signalEvent(const Event& event)
132
{
133
	int video9000id = getVideoSource();
134
135
	assert(getType(event) == EventType::FINISH_FRAME);
136
	const auto& ffe = get_event<FinishFrameEvent>(event);
137
	if (ffe.isSkipped()) return false;
138
	if (videoSourceSetting.getSource() != video9000id) return false;
139
140
	bool superimpose = ((value & 0x18) == 0x18);
141
	if (superimpose && v99x8Layer && v9990Layer &&
142
	    (ffe.getSource() == v99x8Layer->getVideoSource())) {
143
		// inform V9990 about the new V99x8 frame
144
		v9990Layer->setSuperimposeVdpFrame(v99x8Layer->getPaintFrame());
145
	}
146
147
	bool showV9990   = ((value & 0x18) != 0x10); // v9990 or superimpose
148
	if (( showV9990 && v9990Layer && (ffe.getSource() == v9990Layer->getVideoSource())) ||
149
	    (!showV9990 && v99x8Layer && (ffe.getSource() == v99x8Layer->getVideoSource()))) {
150
		getReactor().getEventDistributor().distributeEvent(
151
			FinishFrameEvent(video9000id, video9000id, false));
152
	}
153
	return false;
154
}
155
156
void Video9000::update(const Setting& setting) noexcept
157
{
158
	VideoLayer::update(setting);
159
	if (&setting == &videoSourceSetting) {
160
		recalculateVideoSource();
161
	}
162
}
163
164
template<typename Archive>
165
void Video9000::serialize(Archive& ar, unsigned /*version*/)
166
{
167
	ar.template serializeBase<MSXDevice>(*this);
168
	ar.serialize("value", value);
169
	if constexpr (Archive::IS_LOADER) {
170
		recalculate();
171
	}
172
}
173
INSTANTIATE_SERIALIZE_METHODS(Video9000);
174
REGISTER_MSXDEVICE(Video9000, "Video9000");
175
176
} // namespace openmsx