openMSX
Video9000.cc
Go to the documentation of this file.
1#include "Video9000.hh"
2#include "VDP.hh"
3#include "V9990.hh"
4#include "Reactor.hh"
5#include "Display.hh"
6#include "PostProcessor.hh"
7#include "EventDistributor.hh"
8#include "Event.hh"
9#include "MSXMotherBoard.hh"
10#include "VideoSourceSetting.hh"
11#include "CommandException.hh"
12#include "serialize.hh"
13
14namespace openmsx {
15
17 : MSXDevice(config)
18 , VideoLayer(getMotherBoard(), getName())
19 , videoSourceSetting(getMotherBoard().getVideoSource())
20{
21 // we can't set activeLayer yet
24 getReactor().getDisplay().attach(*this);
25}
26
28{
30 auto refs = getReferences(); // make copy
31 bool error = false;
32 if (refs.size() != 2) error = true;
33 if (!error && !dynamic_cast<VDP*>(refs[0])) {
34 std::swap(refs[0], refs[1]); // try reverse order
35 }
36 if (!error) vdp = dynamic_cast<VDP* >(refs[0]);
37 if (!error) v9990 = dynamic_cast<V9990*>(refs[1]);
38 if (error || !vdp || !v9990) {
39 throw MSXException(
40 "Invalid Video9000 configuration: "
41 "need reference to VDP and V9990 device.");
42 }
43}
44
46{
49 getReactor().getDisplay().detach(*this);
50}
51
52void Video9000::reset(EmuTime::param time)
53{
54 Video9000::writeIO(0x6f, 0x10, time);
55}
56
57void Video9000::writeIO(word /*port*/, byte newValue, EmuTime::param /*time*/)
58{
59 if (newValue == value) return;
60 value = newValue;
61 recalculate();
62}
63
64void Video9000::recalculate()
65{
66 int video9000id = getVideoSource();
67 v99x8Layer = vdp ->getPostProcessor();
68 v9990Layer = v9990->getPostProcessor();
69 assert(!!v99x8Layer == !!v9990Layer); // either both or neither
70
71 // ...0.... -> only V9990
72 // ...10... -> only V99x8
73 // ...11... -> superimpose
74 bool showV99x8 = ((value & 0x10) == 0x10);
75 bool showV9990 = ((value & 0x18) != 0x10);
76 assert(showV99x8 || showV9990);
77 if (v99x8Layer) v99x8Layer->setVideo9000Active(
78 video9000id,
79 showV99x8 ? (showV9990 ? VideoLayer::ACTIVE_BACK
82 if (v9990Layer) v9990Layer->setVideo9000Active(
83 video9000id,
85 activeLayer = showV9990 ? v9990Layer : v99x8Layer;
86 // activeLayer==nullptr is possible for renderer=none
87 recalculateVideoSource();
88}
89
90void Video9000::recalculateVideoSource()
91{
92 // Disable superimpose when gfx9000 layer is selected. That way you
93 // can look at the gfx9000-only output even when the video9000 software
94 // enabled superimpose mode (mostly useful for debugging).
95 bool superimpose = ((value & 0x18) == 0x18);
97 superimpose && (videoSourceSetting.getSource() == getVideoSource()));
98}
99
100void Video9000::preVideoSystemChange() noexcept
101{
102 activeLayer = nullptr; // will be recalculated on next paint()
103}
104
105void Video9000::postVideoSystemChange() noexcept
106{
107 // We can't yet re-initialize 'activeLayer' here because the
108 // new v99x8/v9990 layer may not be created yet.
109}
110
111void Video9000::paint(OutputSurface& output)
112{
113 if (!activeLayer) {
114 recalculate();
115 }
116 // activeLayer==nullptr is possible for renderer=none, but in that case
117 // the paint() method will never be called.
118 activeLayer->paint(output);
119}
120
121void Video9000::takeRawScreenShot(unsigned height, const std::string& filename)
122{
123 auto* layer = dynamic_cast<VideoLayer*>(activeLayer);
124 if (!layer) {
125 throw CommandException("TODO");
126 }
127 layer->takeRawScreenShot(height, filename);
128}
129
130int Video9000::signalEvent(const Event& event)
131{
132 int video9000id = getVideoSource();
133
134 assert(getType(event) == EventType::FINISH_FRAME);
135 const auto& ffe = get<FinishFrameEvent>(event);
136 if (ffe.isSkipped()) return 0;
137 if (videoSourceSetting.getSource() != video9000id) return 0;
138
139 bool superimpose = ((value & 0x18) == 0x18);
140 if (superimpose && v99x8Layer && v9990Layer &&
141 (ffe.getSource() == v99x8Layer->getVideoSource())) {
142 // inform V9990 about the new V99x8 frame
143 v9990Layer->setSuperimposeVdpFrame(v99x8Layer->getPaintFrame());
144 }
145
146 bool showV9990 = ((value & 0x18) != 0x10); // v9990 or superimpose
147 if (( showV9990 && v9990Layer && (ffe.getSource() == v9990Layer->getVideoSource())) ||
148 (!showV9990 && v99x8Layer && (ffe.getSource() == v99x8Layer->getVideoSource()))) {
150 Event::create<FinishFrameEvent>(video9000id, video9000id, false));
151 }
152 return 0;
153}
154
155void Video9000::update(const Setting& setting) noexcept
156{
158 if (&setting == &videoSourceSetting) {
159 recalculateVideoSource();
160 }
161}
162
163template<typename Archive>
164void Video9000::serialize(Archive& ar, unsigned /*version*/)
165{
166 ar.template serializeBase<MSXDevice>(*this);
167 ar.serialize("value", value);
168 if constexpr (Archive::IS_LOADER) {
169 recalculate();
170 }
171}
174
175} // namespace openmsx
BaseSetting * setting
Definition: Interpreter.cc:28
void detach(VideoSystemChangeListener &listener)
Definition: Display.cc:137
void attach(VideoSystemChangeListener &listener)
Definition: Display.cc:131
void unregisterEventListener(EventType type, EventListener &listener)
Unregisters a previously registered event listener.
void distributeEvent(Event &&event)
Schedule the given event for delivery.
void registerEventListener(EventType type, EventListener &listener, Priority priority=OTHER)
Registers a given object to receive certain events.
virtual void paint(OutputSurface &output)=0
Paint this layer.
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition: MSXDevice.hh:34
virtual void init()
Definition: MSXDevice.cc:44
Reactor & getReactor() const
Definition: MSXDevice.cc:145
const Devices & getReferences() const
Get the device references that are specified for this device.
Definition: MSXDevice.cc:119
void setSuperimposeVdpFrame(const FrameSource *vdpSource)
Set the VDP frame on which to superimpose the 'normal' output of this PostProcessor.
FrameSource * getPaintFrame() const
Get the frame that would be displayed.
Display & getDisplay()
Definition: Reactor.hh:86
EventDistributor & getEventDistributor()
Definition: Reactor.hh:82
Implementation of the Yamaha V9990 VDP as used in the GFX9000 cartridge by Sunrise.
Definition: V9990.hh:34
void setExternalVideoSource(bool enable)
Is there an external video source available to superimpose on.
Definition: V9990.hh:150
PostProcessor * getPostProcessor() const
Used by Video9000 to be able to couple the VDP and V9990 output.
Definition: V9990.cc:100
Unified implementation of MSX Video Display Processors (VDPs).
Definition: VDP.hh:64
PostProcessor * getPostProcessor() const
Used by Video9000 to be able to couple the VDP and V9990 output.
Definition: VDP.cc:231
Video9000(const DeviceConfig &config)
Definition: Video9000.cc:16
void reset(EmuTime::param time) override
This method is called on reset.
Definition: Video9000.cc:52
~Video9000() override
Definition: Video9000.cc:45
void init() override
Definition: Video9000.cc:27
void serialize(Archive &ar, unsigned version)
Definition: Video9000.cc:164
void writeIO(word port, byte value, EmuTime::param time) override
Write a byte to a given IO port at a certain time to this device.
Definition: Video9000.cc:57
int getVideoSource() const
Returns the ID for this VideoLayer.
Definition: VideoLayer.cc:39
void setVideo9000Active(int video9000Source_, Video9000Active active)
Definition: VideoLayer.hh:42
void update(const Setting &setting) noexcept override
Definition: VideoLayer.cc:48
VideoLayer(const VideoLayer &)=delete
std::string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:730
This file implemented 3 utility functions:
Definition: Autofire.cc:9
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
EventType getType(const Event &event)
Definition: Event.hh:644
void swap(openmsx::MemBuffer< T > &l, openmsx::MemBuffer< T > &r) noexcept
Definition: MemBuffer.hh:202
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1021