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  , activeLayer(nullptr) // we can't set activeLayer yet
21  , v99x8Layer(nullptr)
22  , v9990Layer(nullptr)
23  , value(0x10)
24 {
27  getReactor().getDisplay().attach(*this);
28 }
29 
31 {
33  auto refs = getReferences(); // make copy
34  bool error = false;
35  if (refs.size() != 2) error = true;
36  if (!error && !dynamic_cast<VDP*>(refs[0])) {
37  std::swap(refs[0], refs[1]); // try reverse order
38  }
39  if (!error) vdp = dynamic_cast<VDP* >(refs[0]);
40  if (!error) v9990 = dynamic_cast<V9990*>(refs[1]);
41  if (error || !vdp || !v9990) {
42  throw MSXException(
43  "Invalid Video9000 configuration: "
44  "need reference to VDP and V9990 device.");
45  }
46 }
47 
49 {
52  getReactor().getDisplay().detach(*this);
53 }
54 
55 void Video9000::reset(EmuTime::param time)
56 {
57  Video9000::writeIO(0x6f, 0x10, time);
58 }
59 
60 void Video9000::writeIO(word /*port*/, byte newValue, EmuTime::param /*time*/)
61 {
62  if (newValue == value) return;
63  value = newValue;
64  recalc();
65 }
66 
67 void Video9000::recalc()
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  if (v99x8Layer) v99x8Layer->setVideo9000Active(
81  video9000id,
82  showV99x8 ? (showV9990 ? VideoLayer::ACTIVE_BACK
85  if (v9990Layer) v9990Layer->setVideo9000Active(
86  video9000id,
88  activeLayer = showV9990 ? v9990Layer : v99x8Layer;
89  // activeLayer==nullptr is possible for renderer=none
90  recalcVideoSource();
91 }
92 
93 void Video9000::recalcVideoSource()
94 {
95  // Disable superimpose when gfx9000 layer is selected. That way you
96  // can look at the gfx9000-only output even when the video9000 software
97  // enabled superimpose mode (mostly useful for debugging).
98  bool superimpose = ((value & 0x18) == 0x18);
100  superimpose && (videoSourceSetting.getSource() == getVideoSource()));
101 }
102 
103 void Video9000::preVideoSystemChange() noexcept
104 {
105  activeLayer = nullptr; // will be recalculated on next paint()
106 }
107 
108 void Video9000::postVideoSystemChange() noexcept
109 {
110  // We can't yet re-initialize 'activeLayer' here because the
111  // new v99x8/v9990 layer may not be created yet.
112 }
113 
114 void Video9000::paint(OutputSurface& output)
115 {
116  if (!activeLayer) {
117  recalc();
118  }
119  // activeLayer==nullptr is possible for renderer=none, but in that case
120  // the paint() method will never be called.
121  activeLayer->paint(output);
122 }
123 
124 void Video9000::takeRawScreenShot(unsigned height, const std::string& filename)
125 {
126  auto* layer = dynamic_cast<VideoLayer*>(activeLayer);
127  if (!layer) {
128  throw CommandException("TODO");
129  }
130  layer->takeRawScreenShot(height, filename);
131 }
132 
133 int Video9000::signalEvent(const Event& event) noexcept
134 {
135  int video9000id = getVideoSource();
136 
137  assert(getType(event) == EventType::FINISH_FRAME);
138  const auto& ffe = get<FinishFrameEvent>(event);
139  if (ffe.isSkipped()) return 0;
140  if (videoSourceSetting.getSource() != video9000id) return 0;
141 
142  bool superimpose = ((value & 0x18) == 0x18);
143  if (superimpose && v99x8Layer && v9990Layer &&
144  (ffe.getSource() == v99x8Layer->getVideoSource())) {
145  // inform V9990 about the new V99x8 frame
146  v9990Layer->setSuperimposeVdpFrame(v99x8Layer->getPaintFrame());
147  }
148 
149  bool showV9990 = ((value & 0x18) != 0x10); // v9990 or superimpose
150  if (( showV9990 && v9990Layer && (ffe.getSource() == v9990Layer->getVideoSource())) ||
151  (!showV9990 && v99x8Layer && (ffe.getSource() == v99x8Layer->getVideoSource()))) {
152  getReactor().getEventDistributor().distributeEvent(
153  Event::create<FinishFrameEvent>(video9000id, video9000id, false));
154  }
155  return 0;
156 }
157 
158 void Video9000::update(const Setting& setting) noexcept
159 {
161  if (&setting == &videoSourceSetting) {
162  recalcVideoSource();
163  }
164 }
165 
166 template<typename Archive>
167 void Video9000::serialize(Archive& ar, unsigned /*version*/)
168 {
169  ar.template serializeBase<MSXDevice>(*this);
170  ar.serialize("value", value);
171  if constexpr (Archive::IS_LOADER) {
172  recalc();
173  }
174 }
177 
178 } // 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:240
Video9000(const DeviceConfig &config)
Definition: Video9000.cc:16
void reset(EmuTime::param time) override
This method is called on reset.
Definition: Video9000.cc:55
~Video9000() override
Definition: Video9000.cc:48
void init() override
Definition: Video9000.cc:30
void serialize(Archive &ar, unsigned version)
Definition: Video9000.cc:167
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:60
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:727
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:644
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1009