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