openMSX
MSXMotherBoard.cc
Go to the documentation of this file.
1#include "MSXMotherBoard.hh"
2
3#include "BooleanSetting.hh"
5#include "CassettePort.hh"
6#include "Command.hh"
7#include "CommandException.hh"
8#include "ConfigException.hh"
9#include "Debugger.hh"
10#include "DeviceFactory.hh"
11#include "EventDelay.hh"
12#include "EventDistributor.hh"
13#include "FileException.hh"
14#include "GlobalCliComm.hh"
15#include "GlobalSettings.hh"
16#include "HardwareConfig.hh"
17#include "InfoTopic.hh"
18#include "JoystickPort.hh"
19#include "LedStatus.hh"
20#include "MSXCPU.hh"
21#include "MSXCPUInterface.hh"
22#include "MSXCliComm.hh"
24#include "MSXDevice.hh"
25#include "MSXDeviceSwitch.hh"
27#include "MSXMapperIO.hh"
28#include "MSXMixer.hh"
29#include "Observer.hh"
30#include "PanasonicMemory.hh"
31#include "PluggingController.hh"
32#include "Reactor.hh"
33#include "RealTime.hh"
34#include "RenShaTurbo.hh"
35#include "ReverseManager.hh"
36#include "Schedulable.hh"
37#include "Scheduler.hh"
38#include "SimpleDebuggable.hh"
40#include "TclObject.hh"
41#include "XMLElement.hh"
42#include "serialize.hh"
43#include "serialize_stl.hh"
44
45#include "ScopedAssign.hh"
46#include "one_of.hh"
47#include "ranges.hh"
48#include "stl.hh"
49#include "unreachable.hh"
50#include "view.hh"
51
52#include <cassert>
53#include <functional>
54#include <iostream>
55#include <memory>
56
57using std::make_unique;
58using std::string;
59
60namespace openmsx {
61
63{
64public:
65 explicit AddRemoveUpdate(MSXMotherBoard& motherBoard);
69private:
70 MSXMotherBoard& motherBoard;
71};
72
73class ResetCmd final : public RecordedCommand
74{
75public:
76 explicit ResetCmd(MSXMotherBoard& motherBoard);
77 void execute(std::span<const TclObject> tokens, TclObject& result,
78 EmuTime::param time) override;
79 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
80private:
81 MSXMotherBoard& motherBoard;
82};
83
84class LoadMachineCmd final : public Command
85{
86public:
87 explicit LoadMachineCmd(MSXMotherBoard& motherBoard);
88 void execute(std::span<const TclObject> tokens, TclObject& result) override;
89 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
90 void tabCompletion(std::vector<string>& tokens) const override;
91private:
92 MSXMotherBoard& motherBoard;
93};
94
95class ListExtCmd final : public Command
96{
97public:
98 explicit ListExtCmd(MSXMotherBoard& motherBoard);
99 void execute(std::span<const TclObject> tokens, TclObject& result) override;
100 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
101private:
102 MSXMotherBoard& motherBoard;
103};
104
105class RemoveExtCmd final : public RecordedCommand
106{
107public:
108 explicit RemoveExtCmd(MSXMotherBoard& motherBoard);
109 void execute(std::span<const TclObject> tokens, TclObject& result,
110 EmuTime::param time) override;
111 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
112 void tabCompletion(std::vector<string>& tokens) const override;
113private:
114 MSXMotherBoard& motherBoard;
115};
116
117class MachineNameInfo final : public InfoTopic
118{
119public:
120 explicit MachineNameInfo(MSXMotherBoard& motherBoard);
121 void execute(std::span<const TclObject> tokens,
122 TclObject& result) const override;
123 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
124private:
125 MSXMotherBoard& motherBoard;
126};
127
128class MachineTypeInfo final : public InfoTopic
129{
130public:
131 explicit MachineTypeInfo(MSXMotherBoard& motherBoard);
132 void execute(std::span<const TclObject> tokens,
133 TclObject& result) const override;
134 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
135private:
136 MSXMotherBoard& motherBoard;
137};
138
139class MachineExtensionInfo final : public InfoTopic
140{
141public:
142 explicit MachineExtensionInfo(MSXMotherBoard& motherBoard);
143 void execute(std::span<const TclObject> tokens,
144 TclObject& result) const override;
145 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
146 void tabCompletion(std::vector<string>& tokens) const override;
147private:
148 MSXMotherBoard& motherBoard;
149};
150
151class MachineMediaInfo final : public InfoTopic
152{
153public:
154 explicit MachineMediaInfo(MSXMotherBoard& motherBoard);
156 assert(providers.empty());
157 }
158 void execute(std::span<const TclObject> tokens,
159 TclObject& result) const override;
160 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
161 void tabCompletion(std::vector<string>& tokens) const override;
162 void registerProvider(std::string_view name, MediaInfoProvider& provider);
164private:
165 struct ProviderInfo {
166 ProviderInfo(std::string_view n, MediaInfoProvider* p)
167 : name(n), provider(p) {} // clang-15 workaround
168 std::string_view name;
169 MediaInfoProvider* provider;
170 };
171 // There will only be a handful of providers, use an unsorted vector.
172 std::vector<ProviderInfo> providers;
173};
174
175class DeviceInfo final : public InfoTopic
176{
177public:
178 explicit DeviceInfo(MSXMotherBoard& motherBoard);
179 void execute(std::span<const TclObject> tokens,
180 TclObject& result) const override;
181 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
182 void tabCompletion(std::vector<string>& tokens) const override;
183private:
184 MSXMotherBoard& motherBoard;
185};
186
187class FastForwardHelper final : private Schedulable
188{
189public:
190 explicit FastForwardHelper(MSXMotherBoard& motherBoard);
191 void setTarget(EmuTime::param targetTime);
192private:
193 void executeUntil(EmuTime::param time) override;
194 MSXMotherBoard& motherBoard;
195};
196
198{
199public:
200 explicit JoyPortDebuggable(MSXMotherBoard& motherBoard);
201 [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
202 void write(unsigned address, byte value) override;
203};
204
205class SettingObserver final : public Observer<Setting>
206{
207public:
208 explicit SettingObserver(MSXMotherBoard& motherBoard);
209 void update(const Setting& setting) noexcept override;
210private:
211 MSXMotherBoard& motherBoard;
212};
213
214
215static unsigned machineIDCounter = 0;
216
218 : reactor(reactor_)
219 , machineID(strCat("machine", ++machineIDCounter))
220 , msxCliComm(make_unique<MSXCliComm>(*this, reactor.getGlobalCliComm()))
221 , msxEventDistributor(make_unique<MSXEventDistributor>())
222 , stateChangeDistributor(make_unique<StateChangeDistributor>())
223 , msxCommandController(make_unique<MSXCommandController>(
224 reactor.getGlobalCommandController(), reactor,
225 *this, *msxEventDistributor, machineID))
226 , scheduler(make_unique<Scheduler>())
227 , msxMixer(make_unique<MSXMixer>(
228 reactor.getMixer(), *this,
229 reactor.getGlobalSettings()))
230 , videoSourceSetting(*msxCommandController)
231 , suppressMessagesSetting(*msxCommandController, "suppressmessages",
232 "Suppress info, warning and error messages for this machine. "
233 "Intended use is for scripts that create temporary machines "
234 "of which you don't want to see warning messages about blank "
235 "SRAM content or PSG port directions for instance.",
236 false, Setting::DONT_SAVE)
237 , fastForwardHelper(make_unique<FastForwardHelper>(*this))
238 , settingObserver(make_unique<SettingObserver>(*this))
239 , powerSetting(reactor.getGlobalSettings().getPowerSetting())
240{
241 slotManager = make_unique<CartridgeSlotManager>(*this);
242 reverseManager = make_unique<ReverseManager>(*this);
243 resetCommand = make_unique<ResetCmd>(*this);
244 loadMachineCommand = make_unique<LoadMachineCmd>(*this);
245 listExtCommand = make_unique<ListExtCmd>(*this);
246 extCommand = make_unique<ExtCmd>(*this, "ext");
247 removeExtCommand = make_unique<RemoveExtCmd>(*this);
248 machineNameInfo = make_unique<MachineNameInfo>(*this);
249 machineTypeInfo = make_unique<MachineTypeInfo>(*this);
250 machineExtensionInfo = make_unique<MachineExtensionInfo>(*this);
251 machineMediaInfo = make_unique<MachineMediaInfo>(*this);
252 deviceInfo = make_unique<DeviceInfo>(*this);
253 debugger = make_unique<Debugger>(*this);
254
255 msxMixer->mute(); // powered down
256
257 // Do this before machine-specific settings are created, otherwise
258 // a setting-info CliComm message is send with a machine id that hasn't
259 // been announced yet over CliComm.
260 addRemoveUpdate = make_unique<AddRemoveUpdate>(*this);
261
262 // TODO: Initialization of this field cannot be done much earlier because
263 // EventDelay creates a setting, calling getMSXCliComm()
264 // on MSXMotherBoard, so "pimpl" has to be set up already.
265 eventDelay = make_unique<EventDelay>(
266 *scheduler, *msxCommandController,
267 reactor.getEventDistributor(), *msxEventDistributor,
268 *reverseManager);
269 realTime = make_unique<RealTime>(
270 *this, reactor.getGlobalSettings(), *eventDelay);
271
272 powerSetting.attach(*settingObserver);
273 suppressMessagesSetting.attach(*settingObserver);
274}
275
277{
278 suppressMessagesSetting.detach(*settingObserver);
279 powerSetting.detach(*settingObserver);
280 deleteMachine();
281
282 assert(mapperIOCounter == 0);
283 assert(availableDevices.empty());
284 assert(extensions.empty());
285 assert(!machineConfig2);
286 assert(!getMachineConfig());
287}
288
289void MSXMotherBoard::deleteMachine()
290{
291 while (!extensions.empty()) {
292 try {
293 removeExtension(*extensions.back());
294 } catch (MSXException& e) {
295 std::cerr << "Internal error: failed to remove "
296 "extension while deleting a machine: "
297 << e.getMessage() << '\n';
298 assert(false);
299 }
300 }
301
302 machineConfig2.reset();
303 machineConfig = nullptr;
304}
305
307{
308 assert(!getMachineConfig());
309 machineConfig = machineConfig_;
310
311 // make sure the CPU gets instantiated from the main thread
312 assert(!msxCpu);
313 msxCpu = make_unique<MSXCPU>(*this);
314 msxCpuInterface = make_unique<MSXCPUInterface>(*this);
315}
316
317std::string_view MSXMotherBoard::getMachineType() const
318{
319 if (const HardwareConfig* machine = getMachineConfig()) {
320 if (const auto* info = machine->getConfig().findChild("info")) {
321 if (const auto* type = info->findChild("type")) {
322 return type->getData();
323 }
324 }
325 }
326 return "";
327}
328
330{
331 const HardwareConfig* config = getMachineConfig();
332 assert(config);
333 return config->getConfig().getChild("devices").findChild("S1990") != nullptr;
334}
335
337{
338 const HardwareConfig* config = getMachineConfig();
339 assert(config);
340 const XMLElement& devices = config->getConfig().getChild("devices");
341 return devices.findChild("T7775") != nullptr ||
342 devices.findChild("T7937") != nullptr ||
343 devices.findChild("T9763") != nullptr ||
344 devices.findChild("T9769") != nullptr;
345}
346
347string MSXMotherBoard::loadMachine(const string& machine)
348{
349 assert(machineName.empty());
350 assert(extensions.empty());
351 assert(!machineConfig2);
352 assert(!getMachineConfig());
353
354 try {
355 machineConfig2 = HardwareConfig::createMachineConfig(*this, machine);
356 setMachineConfig(machineConfig2.get());
357 } catch (FileException& e) {
358 throw MSXException("Machine \"", machine, "\" not found: ",
359 e.getMessage());
360 } catch (MSXException& e) {
361 throw MSXException("Error in \"", machine, "\" machine: ",
362 e.getMessage());
363 }
364 try {
365 machineConfig->parseSlots();
366 machineConfig->createDevices();
367 } catch (MSXException& e) {
368 throw MSXException("Error in \"", machine, "\" machine: ",
369 e.getMessage());
370 }
371 if (powerSetting.getBoolean()) {
372 powerUp();
373 }
374 machineName = machine;
375 return machineName;
376}
377
378std::unique_ptr<HardwareConfig> MSXMotherBoard::loadExtension(std::string_view name, std::string_view slotName)
379{
380 try {
382 *this, string(name), slotName);
383 } catch (FileException& e) {
384 throw MSXException(
385 "Extension \"", name, "\" not found: ", e.getMessage());
386 } catch (MSXException& e) {
387 throw MSXException(
388 "Error in \"", name, "\" extension: ", e.getMessage());
389 }
390}
391
393 std::string_view name, std::unique_ptr<HardwareConfig> extension)
394{
395 try {
396 extension->parseSlots();
397 extension->createDevices();
398 } catch (MSXException& e) {
399 throw MSXException(
400 "Error in \"", name, "\" extension: ", e.getMessage());
401 }
402 string result = extension->getName();
403 extensions.push_back(std::move(extension));
404 getMSXCliComm().update(CliComm::EXTENSION, result, "add");
405 return result;
406}
407
408HardwareConfig* MSXMotherBoard::findExtension(std::string_view extensionName)
409{
410 auto it = ranges::find(extensions, extensionName, &HardwareConfig::getName);
411 return (it != end(extensions)) ? it->get() : nullptr;
412}
413
415{
416 extension.testRemove();
417 getMSXCliComm().update(CliComm::EXTENSION, extension.getName(), "remove");
418 auto it = rfind_unguarded(extensions, &extension,
419 [](auto& e) { return e.get(); });
420 extensions.erase(it);
421}
422
424{
425 return *msxCliComm;
426}
427
429{
430 assert(getMachineConfig()); // needed for PluggableFactory::createAll()
431 if (!pluggingController) {
432 pluggingController = make_unique<PluggingController>(*this);
433 }
434 return *pluggingController;
435}
436
438{
439 assert(getMachineConfig()); // because CPU needs to know if we're
440 // emulating turbor or not
441 return *msxCpu;
442}
443
445{
446 assert(getMachineConfig());
447 return *msxCpuInterface;
448}
449
451{
452 if (!panasonicMemory) {
453 panasonicMemory = make_unique<PanasonicMemory>(*this);
454 }
455 return *panasonicMemory;
456}
457
459{
460 if (!deviceSwitch) {
462 }
463 return *deviceSwitch;
464}
465
467{
468 if (!cassettePort) {
469 assert(getMachineConfig());
470 if (getMachineConfig()->getConfig().findChild("CassettePort")) {
471 cassettePort = make_unique<CassettePort>(*getMachineConfig());
472 } else {
473 cassettePort = make_unique<DummyCassettePort>();
474 }
475 }
476 return *cassettePort;
477}
478
480{
481 assert(port < 2);
482 if (!joystickPort[0]) {
483 assert(getMachineConfig());
484 // some MSX machines only have 1 instead of 2 joystick ports
485 std::string_view ports = getMachineConfig()->getConfig().getChildData(
486 "JoystickPorts", "AB");
487 if (ports != one_of("AB", "", "A", "B")) {
488 throw ConfigException(
489 "Invalid JoystickPorts specification, "
490 "should be one of '', 'A', 'B' or 'AB'.");
491 }
493 if (ports == one_of("AB", "A")) {
494 joystickPort[0] = make_unique<JoystickPort>(
495 ctrl, "joyporta", "MSX Joystick port A");
496 } else {
497 joystickPort[0] = make_unique<DummyJoystickPort>();
498 }
499 if (ports == one_of("AB", "B")) {
500 joystickPort[1] = make_unique<JoystickPort>(
501 ctrl, "joyportb", "MSX Joystick port B");
502 } else {
503 joystickPort[1] = make_unique<DummyJoystickPort>();
504 }
505 joyPortDebuggable = make_unique<JoyPortDebuggable>(*this);
506 }
507 return *joystickPort[port];
508}
509
511{
512 if (!renShaTurbo) {
513 assert(getMachineConfig());
514 renShaTurbo = make_unique<RenShaTurbo>(
515 *this,
516 getMachineConfig()->getConfig());
517 }
518 return *renShaTurbo;
519}
520
522{
523 if (!ledStatus) {
524 (void)getMSXCliComm(); // force init, to be on the safe side
525 ledStatus = make_unique<LedStatus>(
526 reactor.getRTScheduler(),
527 *msxCommandController,
528 *msxCliComm);
529 }
530 return *ledStatus;
531}
532
534{
535 return *msxCommandController;
536}
537
539{
540 return msxCommandController->getMachineInfoCommand();
541}
542
543EmuTime::param MSXMotherBoard::getCurrentTime() const
544{
545 return scheduler->getCurrentTime();
546}
547
549{
550 if (!powered) {
551 return false;
552 }
553 assert(getMachineConfig()); // otherwise powered cannot be true
554
555 getCPU().execute(false);
556 return true;
557}
558
559void MSXMotherBoard::fastForward(EmuTime::param time, bool fast)
560{
561 assert(powered);
562 assert(getMachineConfig());
563
564 if (time <= getCurrentTime()) return;
565
566 ScopedAssign sa(fastForwarding, fast);
567 realTime->disable();
568 msxMixer->mute();
569 fastForwardHelper->setTarget(time);
570 while (time > getCurrentTime()) {
571 // note: this can run (slightly) past the requested time
572 getCPU().execute(true); // fast-forward mode
573 }
574 realTime->enable();
575 msxMixer->unmute();
576}
577
579{
580 if (getMachineConfig()) {
581 getCPU().setPaused(true);
582 }
583 msxMixer->mute();
584}
585
587{
588 if (getMachineConfig()) {
589 getCPU().setPaused(false);
590 }
591 msxMixer->unmute();
592}
593
595{
596 availableDevices.push_back(&device);
597}
598
600{
601 move_pop_back(availableDevices, rfind_unguarded(availableDevices, &device));
602}
603
605{
606 if (!powered) return;
607 assert(getMachineConfig());
608
609 EmuTime::param time = getCurrentTime();
611 for (auto& d : availableDevices) {
612 d->reset(time);
613 }
614 getCPU().doReset(time);
615 // let everyone know we're booting, note that the fact that this is
616 // done after the reset call to the devices is arbitrary here
618}
619
621{
622 byte result = 0xff;
623 for (auto& d : availableDevices) {
624 result &= d->readIRQVector();
625 }
626 return result;
627}
628
630{
631 if (powered) return;
632 if (!getMachineConfig()) return;
633
634 powered = true;
635 // TODO: If our "powered" field is always equal to the power setting,
636 // there is not really a point in keeping it.
637 // TODO: assert disabled see note in Reactor::run() where this method
638 // is called
639 //assert(powerSetting.getBoolean() == powered);
640 powerSetting.setBoolean(true);
641 // TODO: We could make the power LED a device, so we don't have to handle
642 // it separately here.
644
645 EmuTime::param time = getCurrentTime();
647 for (auto& d : availableDevices) {
648 d->powerUp(time);
649 }
650 getCPU().doReset(time);
651 msxMixer->unmute();
652 // let everyone know we're booting, note that the fact that this is
653 // done after the reset call to the devices is arbitrary here
655}
656
657void MSXMotherBoard::powerDown()
658{
659 if (!powered) return;
660
661 powered = false;
662 // TODO: This assertion fails in 1 case: when quitting with a running MSX.
663 // How do we want the Reactor to shutdown: immediately or after
664 // handling all pending commands/events/updates?
665 //assert(powerSetting.getBoolean() == powered);
666 powerSetting.setBoolean(false);
668
669 msxMixer->mute();
670
671 EmuTime::param time = getCurrentTime();
672 for (auto& d : availableDevices) {
673 d->powerDown(time);
674 }
675}
676
677void MSXMotherBoard::activate(bool active_)
678{
679 active = active_;
680 auto event = active ? Event(MachineActivatedEvent())
682 msxEventDistributor->distributeEvent(event, scheduler->getCurrentTime());
683 if (active) {
684 realTime->resync();
685 }
686}
687
694
699
701{
702 auto it = ranges::find(availableDevices, name, &MSXDevice::getName);
703 return (it != end(availableDevices)) ? *it : nullptr;
704}
705
707{
708 if (mapperIOCounter == 0) {
710
711 MSXCPUInterface& cpuInterface = getCPUInterface();
712 for (auto port : {0xfc, 0xfd, 0xfe, 0xff}) {
713 cpuInterface.register_IO_Out(narrow_cast<byte>(port), mapperIO.get());
714 cpuInterface.register_IO_In (narrow_cast<byte>(port), mapperIO.get());
715 }
716 }
717 ++mapperIOCounter;
718 return *mapperIO;
719}
720
722{
723 assert(mapperIO);
724 assert(mapperIOCounter);
725 --mapperIOCounter;
726 if (mapperIOCounter == 0) {
727 MSXCPUInterface& cpuInterface = getCPUInterface();
728 for (auto port : {0xfc, 0xfd, 0xfe, 0xff}) {
729 cpuInterface.unregister_IO_Out(narrow_cast<byte>(port), mapperIO.get());
730 cpuInterface.unregister_IO_In (narrow_cast<byte>(port), mapperIO.get());
731 }
732
733 mapperIO.reset();
734 }
735}
736
737string MSXMotherBoard::getUserName(const string& hwName)
738{
739 auto& s = userNames[hwName];
740 unsigned n = 0;
741 string userName;
742 do {
743 userName = strCat("untitled", ++n);
744 } while (contains(s, userName));
745 s.push_back(userName);
746 return userName;
747}
748
749void MSXMotherBoard::freeUserName(const string& hwName, const string& userName)
750{
751 auto& s = userNames[hwName];
752 move_pop_back(s, rfind_unguarded(s, userName));
753}
754
755void MSXMotherBoard::registerMediaInfo(std::string_view name, MediaInfoProvider& provider)
756{
757 machineMediaInfo->registerProvider(name, provider);
758}
759
761{
762 machineMediaInfo->unregisterProvider(provider);
763}
764
765
766// AddRemoveUpdate
767
769 : motherBoard(motherBoard_)
770{
771 motherBoard.getReactor().getGlobalCliComm().update(
772 CliComm::HARDWARE, motherBoard.getMachineID(), "add");
773}
774
776{
777 motherBoard.getReactor().getGlobalCliComm().update(
778 CliComm::HARDWARE, motherBoard.getMachineID(), "remove");
779}
780
781
782// ResetCmd
784 : RecordedCommand(motherBoard_.getCommandController(),
785 motherBoard_.getStateChangeDistributor(),
786 motherBoard_.getScheduler(),
787 "reset")
788 , motherBoard(motherBoard_)
789{
790}
791
792void ResetCmd::execute(std::span<const TclObject> /*tokens*/, TclObject& /*result*/,
793 EmuTime::param /*time*/)
794{
795 motherBoard.doReset();
796}
797
798string ResetCmd::help(std::span<const TclObject> /*tokens*/) const
799{
800 return "Resets the MSX.";
801}
802
803
804// LoadMachineCmd
806 : Command(motherBoard_.getCommandController(), "load_machine")
807 , motherBoard(motherBoard_)
808{
809 // The load_machine command should always directly follow a
810 // create_machine command:
811 // - It's not allowed to use load_machine on a machine that has
812 // already a machine configuration loaded earlier.
813 // - We also disallow executing most machine-specific commands on an
814 // 'empty machine' (an 'empty machine', is a machine returned by
815 // create_machine before the load_machine command is executed, so a
816 // machine without a machine configuration). The only exception is
817 // this load_machine command and machine_info.
818 //
819 // So if the only allowed command on an empty machine is
820 // 'load_machine', (and an empty machine by itself isn't very useful),
821 // then why isn't create_machine and load_machine merged into a single
822 // command? The only reason for this is that load_machine sends out
823 // events (machine specific) and maybe you already want to know the ID
824 // of the new machine (this is returned by create_machine) before those
825 // events will be send.
826 //
827 // Why not allow all commands on an empty machine? In the past we did
828 // allow this, though it often was the source of bugs. We could in each
829 // command (when needed) check for an empty machine and then return
830 // some dummy/empty result or some error. But because I can't think of
831 // any really useful command for an empty machine, it seemed easier to
832 // just disallow most commands.
834}
835
836void LoadMachineCmd::execute(std::span<const TclObject> tokens, TclObject& result)
837{
838 checkNumArgs(tokens, 2, "machine");
839 if (motherBoard.getMachineConfig()) {
840 throw CommandException("Already loaded a config in this machine.");
841 }
842 result = motherBoard.loadMachine(string(tokens[1].getString()));
843}
844
845string LoadMachineCmd::help(std::span<const TclObject> /*tokens*/) const
846{
847 return "Load a msx machine configuration into an empty machine.";
848}
849
850void LoadMachineCmd::tabCompletion(std::vector<string>& tokens) const
851{
852 completeString(tokens, Reactor::getHwConfigs("machines"));
853}
854
855
856// ListExtCmd
858 : Command(motherBoard_.getCommandController(), "list_extensions")
859 , motherBoard(motherBoard_)
860{
861}
862
863void ListExtCmd::execute(std::span<const TclObject> /*tokens*/, TclObject& result)
864{
865 result.addListElements(
866 view::transform(motherBoard.getExtensions(),
867 [&](auto& e) { return e->getName(); }));
868}
869
870string ListExtCmd::help(std::span<const TclObject> /*tokens*/) const
871{
872 return "Return a list of all inserted extensions.";
873}
874
875
876// ExtCmd
877ExtCmd::ExtCmd(MSXMotherBoard& motherBoard_, std::string commandName_)
878 : RecordedCommand(motherBoard_.getCommandController(),
879 motherBoard_.getStateChangeDistributor(),
880 motherBoard_.getScheduler(),
881 commandName_)
882 , motherBoard(motherBoard_)
883 , commandName(std::move(commandName_))
884{
885}
886
887void ExtCmd::execute(std::span<const TclObject> tokens, TclObject& result,
888 EmuTime::param /*time*/)
889{
890 checkNumArgs(tokens, Between{2, 3}, "extension");
891 if (tokens.size() == 3 && tokens[1].getString() != "insert") {
892 throw SyntaxError();
893 }
894 try {
895 auto name = tokens[tokens.size() - 1].getString();
896 auto slotName = (commandName.size() == 4)
897 ? std::string_view(&commandName[3], 1)
898 : "any";
899 auto extension = motherBoard.loadExtension(name, slotName);
900 if (slotName != "any") {
901 auto& manager = motherBoard.getSlotManager();
902 if (const auto* extConf = manager.getConfigForSlot(commandName[3] - 'a')) {
903 // still a cartridge inserted, (try to) remove it now
904 motherBoard.removeExtension(*extConf);
905 }
906 }
907 result = motherBoard.insertExtension(name, std::move(extension));
908 } catch (MSXException& e) {
909 throw CommandException(std::move(e).getMessage());
910 }
911}
912
913string ExtCmd::help(std::span<const TclObject> /*tokens*/) const
914{
915 return "Insert a hardware extension.";
916}
917
918void ExtCmd::tabCompletion(std::vector<string>& tokens) const
919{
920 completeString(tokens, Reactor::getHwConfigs("extensions"));
921}
922
923
924// RemoveExtCmd
926 : RecordedCommand(motherBoard_.getCommandController(),
927 motherBoard_.getStateChangeDistributor(),
928 motherBoard_.getScheduler(),
929 "remove_extension")
930 , motherBoard(motherBoard_)
931{
932}
933
934void RemoveExtCmd::execute(std::span<const TclObject> tokens, TclObject& /*result*/,
935 EmuTime::param /*time*/)
936{
937 checkNumArgs(tokens, 2, "extension");
938 std::string_view extName = tokens[1].getString();
939 HardwareConfig* extension = motherBoard.findExtension(extName);
940 if (!extension) {
941 throw CommandException("No such extension: ", extName);
942 }
943 try {
944 motherBoard.removeExtension(*extension);
945 } catch (MSXException& e) {
946 throw CommandException("Can't remove extension '", extName,
947 "': ", e.getMessage());
948 }
949}
950
951string RemoveExtCmd::help(std::span<const TclObject> /*tokens*/) const
952{
953 return "Remove an extension from the MSX machine.";
954}
955
956void RemoveExtCmd::tabCompletion(std::vector<string>& tokens) const
957{
958 if (tokens.size() == 2) {
960 motherBoard.getExtensions(),
961 [](auto& e) -> std::string_view { return e->getName(); }));
962 }
963}
964
965
966// MachineNameInfo
967
969 : InfoTopic(motherBoard_.getMachineInfoCommand(), "config_name")
970 , motherBoard(motherBoard_)
971{
972}
973
974void MachineNameInfo::execute(std::span<const TclObject> /*tokens*/,
975 TclObject& result) const
976{
977 result = motherBoard.getMachineName();
978}
979
980string MachineNameInfo::help(std::span<const TclObject> /*tokens*/) const
981{
982 return "Returns the configuration name for this machine.";
983}
984
985// MachineTypeInfo
986
988 : InfoTopic(motherBoard_.getMachineInfoCommand(), "type")
989 , motherBoard(motherBoard_)
990{
991}
992
993void MachineTypeInfo::execute(std::span<const TclObject> /*tokens*/,
994 TclObject& result) const
995{
996 result = motherBoard.getMachineType();
997}
998
999string MachineTypeInfo::help(std::span<const TclObject> /*tokens*/) const
1000{
1001 return "Returns the machine type for this machine.";
1002}
1003
1004
1005// MachineExtensionInfo
1006
1008 : InfoTopic(motherBoard_.getMachineInfoCommand(), "extension")
1009 , motherBoard(motherBoard_)
1010{
1011}
1012
1013void MachineExtensionInfo::execute(std::span<const TclObject> tokens,
1014 TclObject& result) const
1015{
1016 checkNumArgs(tokens, Between{2, 3}, Prefix{2}, "?extension-instance-name?");
1017 if (tokens.size() == 2) {
1018 result.addListElements(
1019 view::transform(motherBoard.getExtensions(),
1020 [&](auto& e) { return e->getName(); }));
1021 } else if (tokens.size() == 3) {
1022 std::string_view extName = tokens[2].getString();
1023 HardwareConfig* extension = motherBoard.findExtension(extName);
1024 if (!extension) {
1025 throw CommandException("No such extension: ", extName);
1026 }
1027 if (extension->getType() == HardwareConfig::Type::EXTENSION) {
1028 // A 'true' extension, as specified in an XML file
1029 result.addDictKeyValue("config", extension->getConfigName());
1030 } else {
1031 assert(extension->getType() == HardwareConfig::Type::ROM);
1032 // A ROM cartridge, peek into the internal config for the original filename
1033 const auto& filename = extension->getConfig()
1034 .getChild("devices").getChild("primary").getChild("secondary")
1035 .getChild("ROM").getChild("rom").getChildData("filename");
1036 result.addDictKeyValue("rom", filename);
1037 }
1038 TclObject deviceList;
1039 deviceList.addListElements(
1040 view::transform(extension->getDevices(),
1041 [&](auto& e) { return e->getName(); }));
1042 result.addDictKeyValue("devices", deviceList);
1043 }
1044}
1045
1046string MachineExtensionInfo::help(std::span<const TclObject> /*tokens*/) const
1047{
1048 return "Returns information about the given extension instance.";
1049}
1050
1051void MachineExtensionInfo::tabCompletion(std::vector<string>& tokens) const
1052{
1053 if (tokens.size() == 3) {
1055 motherBoard.getExtensions(),
1056 [](auto& e) -> std::string_view { return e->getName(); }));
1057 }
1058}
1059
1060
1061// MachineMediaInfo
1062
1064 : InfoTopic(motherBoard_.getMachineInfoCommand(), "media")
1065{
1066}
1067
1068void MachineMediaInfo::execute(std::span<const TclObject> tokens,
1069 TclObject& result) const
1070{
1071 checkNumArgs(tokens, Between{2, 3}, Prefix{2}, "?media-slot-name?");
1072 if (tokens.size() == 2) {
1073 result.addListElements(
1074 view::transform(providers, &ProviderInfo::name));
1075 } else if (tokens.size() == 3) {
1076 auto name = tokens[2].getString();
1077 if (auto it = ranges::find(providers, name, &ProviderInfo::name);
1078 it != providers.end()) {
1079 it->provider->getMediaInfo(result);
1080 } else {
1081 throw CommandException("No info about media slot ", name);
1082 }
1083 }
1084}
1085
1086string MachineMediaInfo::help(std::span<const TclObject> /*tokens*/) const
1087{
1088 return "Returns information about the given media slot.";
1089}
1090
1091void MachineMediaInfo::tabCompletion(std::vector<string>& tokens) const
1092{
1093 if (tokens.size() == 3) {
1095 providers, &ProviderInfo::name));
1096 }
1097}
1098
1099void MachineMediaInfo::registerProvider(std::string_view name, MediaInfoProvider& provider)
1100{
1101 assert(!contains(providers, name, &ProviderInfo::name));
1102 assert(!contains(providers, &provider, &ProviderInfo::provider));
1103 providers.emplace_back(name, &provider);
1104}
1105
1107{
1108 move_pop_back(providers,
1109 rfind_unguarded(providers, &provider, &ProviderInfo::provider));
1110}
1111
1112// DeviceInfo
1113
1115 : InfoTopic(motherBoard_.getMachineInfoCommand(), "device")
1116 , motherBoard(motherBoard_)
1117{
1118}
1119
1120void DeviceInfo::execute(std::span<const TclObject> tokens, TclObject& result) const
1121{
1122 checkNumArgs(tokens, Between{2, 3}, Prefix{2}, "?device?");
1123 switch (tokens.size()) {
1124 case 2:
1125 result.addListElements(
1126 view::transform(motherBoard.availableDevices,
1127 [](auto& d) { return d->getName(); }));
1128 break;
1129 case 3: {
1130 std::string_view deviceName = tokens[2].getString();
1131 MSXDevice* device = motherBoard.findDevice(deviceName);
1132 if (!device) {
1133 throw CommandException("No such device: ", deviceName);
1134 }
1135 device->getDeviceInfo(result);
1136 break;
1137 }
1138 }
1139}
1140
1141string DeviceInfo::help(std::span<const TclObject> /*tokens*/) const
1142{
1143 return "Without any arguments, returns the list of used device names.\n"
1144 "With a device name as argument, returns the type (and for some "
1145 "devices the subtype) of the given device.";
1146}
1147
1148void DeviceInfo::tabCompletion(std::vector<string>& tokens) const
1149{
1150 if (tokens.size() == 3) {
1152 motherBoard.availableDevices,
1153 [](auto& d) -> std::string_view { return d->getName(); }));
1154 }
1155}
1156
1157
1158// FastForwardHelper
1159
1161 : Schedulable(motherBoard_.getScheduler())
1162 , motherBoard(motherBoard_)
1163{
1164}
1165
1166void FastForwardHelper::setTarget(EmuTime::param targetTime)
1167{
1168 setSyncPoint(targetTime);
1169}
1170
1171void FastForwardHelper::executeUntil(EmuTime::param /*time*/)
1172{
1173 motherBoard.exitCPULoopSync();
1174}
1175
1176
1177// class JoyPortDebuggable
1178
1180 : SimpleDebuggable(motherBoard_, "joystickports", "MSX Joystick Ports", 2)
1181{
1182}
1183
1184byte JoyPortDebuggable::read(unsigned address, EmuTime::param time)
1185{
1186 return getMotherBoard().getJoystickPort(address).read(time);
1187}
1188
1189void JoyPortDebuggable::write(unsigned /*address*/, byte /*value*/)
1190{
1191 // ignore
1192}
1193
1194
1195// class SettingObserver
1196
1198 : motherBoard(motherBoard_)
1199{
1200}
1201
1203{
1204 if (&setting == &motherBoard.powerSetting) {
1205 if (motherBoard.powerSetting.getBoolean()) {
1206 motherBoard.powerUp();
1207 } else {
1208 motherBoard.powerDown();
1209 }
1210 } else if (&setting == &motherBoard.suppressMessagesSetting) {
1211 motherBoard.msxCliComm->setSuppressMessages(motherBoard.suppressMessagesSetting.getBoolean());
1212 } else {
1214 }
1215}
1216
1217
1218// serialize
1219// version 1: initial version
1220// version 2: added reRecordCount
1221// version 3: removed reRecordCount (moved to ReverseManager)
1222// version 4: moved joystickportA/B from MSXPSG to here
1223// version 5: do serialize renShaTurbo
1224template<typename Archive>
1225void MSXMotherBoard::serialize(Archive& ar, unsigned version)
1226{
1227 // don't serialize:
1228 // machineID, userNames, availableDevices, addRemoveUpdate,
1229 // sharedStuffMap, msxCliComm, msxEventDistributor,
1230 // msxCommandController, slotManager, eventDelay,
1231 // debugger, msxMixer, panasonicMemory, ledStatus
1232
1233 // Scheduler must come early so that devices can query current time
1234 ar.serialize("scheduler", *scheduler);
1235 // MSXMixer has already set sync points, those are invalid now
1236 // the following call will fix this
1237 if constexpr (Archive::IS_LOADER) {
1238 msxMixer->reInit();
1239 }
1240
1241 ar.serialize("name", machineName);
1242 ar.serializeWithID("config", machineConfig2, std::ref(*this));
1243 assert(getMachineConfig() == machineConfig2.get());
1244 ar.serializeWithID("extensions", extensions, std::ref(*this));
1245
1246 if (mapperIO) ar.serialize("mapperIO", *mapperIO);
1247
1248 MSXDeviceSwitch& devSwitch = getDeviceSwitch();
1249 if (devSwitch.hasRegisteredDevices()) {
1250 ar.serialize("deviceSwitch", devSwitch);
1251 }
1252
1253 if (getMachineConfig()) {
1254 ar.serialize("cpu", getCPU());
1255 }
1256 ar.serialize("cpuInterface", getCPUInterface());
1257
1258 if (auto* port = dynamic_cast<CassettePort*>(&getCassettePort())) {
1259 ar.serialize("cassetteport", *port);
1260 }
1261 if (ar.versionAtLeast(version, 4)) {
1262 if (auto* port = dynamic_cast<JoystickPort*>(
1263 joystickPort[0].get())) {
1264 ar.serialize("joystickportA", *port);
1265 }
1266 if (auto* port = dynamic_cast<JoystickPort*>(
1267 joystickPort[1].get())) {
1268 ar.serialize("joystickportB", *port);
1269 }
1270 }
1271 if (ar.versionAtLeast(version, 5)) {
1272 if (renShaTurbo) ar.serialize("renShaTurbo", *renShaTurbo);
1273 }
1274
1275 if constexpr (Archive::IS_LOADER) {
1276 powered = true; // must come before changing power setting
1277 powerSetting.setBoolean(true);
1279 msxMixer->unmute();
1280 }
1281
1282 if (version == 2) {
1283 assert(Archive::IS_LOADER);
1284 unsigned reRecordCount = 0; // silence warning
1285 ar.serialize("reRecordCount", reRecordCount);
1286 getReverseManager().setReRecordCount(reRecordCount);
1287 }
1288}
1290
1291} // namespace openmsx
BaseSetting * setting
Assign new value to some variable and restore the original value when this object goes out of scope.
AddRemoveUpdate(const AddRemoveUpdate &)=delete
AddRemoveUpdate(MSXMotherBoard &motherBoard)
AddRemoveUpdate & operator=(const AddRemoveUpdate &)=delete
bool getBoolean() const noexcept
Sent when the MSX resets or powers up.
Definition Event.hh:369
void setAllowedInEmptyMachine(bool value)
Definition Command.hh:65
static void completeString(std::vector< std::string > &tokens, ITER begin, ITER end, bool caseSensitive=true)
Definition Completer.hh:136
void checkNumArgs(std::span< const TclObject > tokens, unsigned exactly, const char *errMessage) const
Definition Completer.cc:181
static std::unique_ptr< MSXDeviceSwitch > createDeviceSwitch(const HardwareConfig &hwConf)
static std::unique_ptr< MSXMapperIO > createMapperIO(const HardwareConfig &hwConf)
DeviceInfo(MSXMotherBoard &motherBoard)
void execute(std::span< const TclObject > tokens, TclObject &result) const override
Show info on this topic.
string help(std::span< const TclObject > tokens) const override
Print help for this topic.
void tabCompletion(std::vector< string > &tokens) const override
Attempt tab completion for this command.
void distributeEvent(Event &&event)
Schedule the given event for delivery.
void tabCompletion(std::vector< std::string > &tokens) const override
Attempt tab completion for this command.
ExtCmd(MSXMotherBoard &motherBoard, std::string commandName)
void execute(std::span< const TclObject > tokens, TclObject &result, EmuTime::param time) override
This is like the execute() method of the Command class, it only has an extra time parameter.
std::string help(std::span< const TclObject > tokens) const override
Print help for this command.
void setTarget(EmuTime::param targetTime)
FastForwardHelper(MSXMotherBoard &motherBoard)
void update(UpdateType type, std::string_view name, std::string_view value) override
const std::string & getConfigName() const
static std::unique_ptr< HardwareConfig > createMachineConfig(MSXMotherBoard &motherBoard, std::string machineName)
const auto & getDevices() const
const std::string & getName() const
const XMLElement & getConfig() const
static std::unique_ptr< HardwareConfig > createExtensionConfig(MSXMotherBoard &motherBoard, std::string extensionName, std::string_view slotName)
void testRemove() const
Checks whether this HardwareConfig can be deleted.
void write(unsigned address, byte value) override
JoyPortDebuggable(MSXMotherBoard &motherBoard)
byte read(unsigned address, EmuTime::param time) override
virtual uint8_t read(EmuTime::param time)=0
void setLed(Led led, bool status)
Definition LedStatus.cc:41
void execute(std::span< const TclObject > tokens, TclObject &result) override
Execute this command.
ListExtCmd(MSXMotherBoard &motherBoard)
string help(std::span< const TclObject > tokens) const override
Print help for this command.
LoadMachineCmd(MSXMotherBoard &motherBoard)
string help(std::span< const TclObject > tokens) const override
Print help for this command.
void execute(std::span< const TclObject > tokens, TclObject &result) override
Execute this command.
void tabCompletion(std::vector< string > &tokens) const override
Attempt tab completion for this command.
void register_IO_Out(byte port, MSXDevice *device)
Devices can register their Out ports.
void register_IO_In(byte port, MSXDevice *device)
Devices can register their In ports.
void unregister_IO_In(byte port, MSXDevice *device)
void reset()
Reset (the slot state)
void unregister_IO_Out(byte port, MSXDevice *device)
void exitCPULoopSync()
See CPUCore::exitCPULoopSync()
Definition MSXCPU.cc:126
void doReset(EmuTime::param time)
Reset CPU.
Definition MSXCPU.cc:80
void setPaused(bool paused)
(un)pause CPU.
Definition MSXCPU.cc:365
void exitCPULoopAsync()
See CPUCore::exitCPULoopAsync()
Definition MSXCPU.cc:131
void update(UpdateType type, std::string_view name, std::string_view value) override
Definition MSXCliComm.cc:21
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition MSXDevice.hh:36
void getDeviceInfo(TclObject &result) const
Get device info.
Definition MSXDevice.cc:385
virtual const std::string & getName() const
Returns a human-readable name for this device.
Definition MSXDevice.cc:375
HardwareConfig * findExtension(std::string_view extensionName)
PluggingController & getPluggingController()
void activate(bool active)
void setMachineConfig(HardwareConfig *machineConfig)
RenShaTurbo & getRenShaTurbo()
void freeUserName(const std::string &hwName, const std::string &userName)
bool execute()
Run emulation.
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
const HardwareConfig * getMachineConfig() const
MSXCPUInterface & getCPUInterface()
std::string getUserName(const std::string &hwName)
Keep track of which 'usernames' are in use.
std::unique_ptr< HardwareConfig > loadExtension(std::string_view extensionName, std::string_view slotName)
std::string loadMachine(const std::string &machine)
std::string_view getMachineName() const
void registerMediaInfo(std::string_view name, MediaInfoProvider &provider)
Register and unregister providers of media info, for the media info topic.
MSXDevice * findDevice(std::string_view name)
Find a MSXDevice by name.
CommandController & getCommandController()
void pause()
Pause MSX machine.
std::string insertExtension(std::string_view name, std::unique_ptr< HardwareConfig > extension)
void fastForward(EmuTime::param time, bool fast)
Run emulation until a certain time in fast forward mode.
void exitCPULoopAsync()
See CPU::exitCPULoopAsync().
MSXMotherBoard(const MSXMotherBoard &)=delete
void unregisterMediaInfo(MediaInfoProvider &provider)
MSXDeviceSwitch & getDeviceSwitch()
void serialize(Archive &ar, unsigned version)
CartridgeSlotManager & getSlotManager()
void removeDevice(MSXDevice &device)
std::string_view getMachineID() const
void removeExtension(const HardwareConfig &extension)
ReverseManager & getReverseManager()
JoystickPortIf & getJoystickPort(unsigned port)
std::string_view getMachineType() const
const Extensions & getExtensions() const
CassettePortInterface & getCassettePort()
void addDevice(MSXDevice &device)
All MSXDevices should be registered by the MotherBoard.
PanasonicMemory & getPanasonicMemory()
MSXMapperIO & createMapperIO()
All memory mappers in one MSX machine share the same four (logical) memory mapper registers.
InfoCommand & getMachineInfoCommand()
Send when a machine is (de)activated.
Definition Event.hh:391
void execute(std::span< const TclObject > tokens, TclObject &result) const override
Show info on this topic.
string help(std::span< const TclObject > tokens) const override
Print help for this topic.
void tabCompletion(std::vector< string > &tokens) const override
Attempt tab completion for this topic.
MachineExtensionInfo(MSXMotherBoard &motherBoard)
void unregisterProvider(MediaInfoProvider &provider)
void registerProvider(std::string_view name, MediaInfoProvider &provider)
string help(std::span< const TclObject > tokens) const override
Print help for this topic.
void execute(std::span< const TclObject > tokens, TclObject &result) const override
Show info on this topic.
MachineMediaInfo(MSXMotherBoard &motherBoard)
void tabCompletion(std::vector< string > &tokens) const override
Attempt tab completion for this topic.
void execute(std::span< const TclObject > tokens, TclObject &result) const override
Show info on this topic.
MachineNameInfo(MSXMotherBoard &motherBoard)
string help(std::span< const TclObject > tokens) const override
Print help for this topic.
string help(std::span< const TclObject > tokens) const override
Print help for this topic.
void execute(std::span< const TclObject > tokens, TclObject &result) const override
Show info on this topic.
MachineTypeInfo(MSXMotherBoard &motherBoard)
Generic Gang-of-Four Observer class, templatized edition.
Definition Observer.hh:10
Central administration of Connectors and Pluggables.
Contains the main loop of openMSX.
Definition Reactor.hh:72
GlobalSettings & getGlobalSettings()
Definition Reactor.hh:114
GlobalCliComm & getGlobalCliComm()
Definition Reactor.hh:87
RTScheduler & getRTScheduler()
Definition Reactor.hh:85
EventDistributor & getEventDistributor()
Definition Reactor.hh:86
static std::vector< std::string > getHwConfigs(std::string_view type)
Definition Reactor.cc:349
Commands that directly influence the MSX state should send and events so that they can be recorded by...
RemoveExtCmd(MSXMotherBoard &motherBoard)
void tabCompletion(std::vector< string > &tokens) const override
Attempt tab completion for this command.
void execute(std::span< const TclObject > tokens, TclObject &result, EmuTime::param time) override
This is like the execute() method of the Command class, it only has an extra time parameter.
string help(std::span< const TclObject > tokens) const override
Print help for this command.
Ren-Sha Turbo is the autofire in several MSX 2+ models and in the MSX turbo R.
string help(std::span< const TclObject > tokens) const override
Print help for this command.
void execute(std::span< const TclObject > tokens, TclObject &result, EmuTime::param time) override
This is like the execute() method of the Command class, it only has an extra time parameter.
ResetCmd(MSXMotherBoard &motherBoard)
void setReRecordCount(unsigned count)
Every class that wants to get scheduled at some point must inherit from this class.
void setSyncPoint(EmuTime::param timestamp)
SettingObserver(MSXMotherBoard &motherBoard)
void update(const Setting &setting) noexcept override
MSXMotherBoard & getMotherBoard() const
void detach(Observer< T > &observer)
Definition Subject.hh:56
void attach(Observer< T > &observer)
Definition Subject.hh:50
void addListElements(ITER first, ITER last)
Definition TclObject.hh:132
void addDictKeyValue(const Key &key, const Value &value)
Definition TclObject.hh:145
const XMLElement * findChild(std::string_view childName) const
Definition XMLElement.cc:21
const XMLElement & getChild(std::string_view childName) const
Definition XMLElement.cc:55
std::string_view getChildData(std::string_view childName) const
Definition XMLElement.cc:64
This file implemented 3 utility functions:
Definition Autofire.cc:11
std::variant< KeyUpEvent, KeyDownEvent, MouseMotionEvent, MouseButtonUpEvent, MouseButtonDownEvent, MouseWheelEvent, JoystickAxisMotionEvent, JoystickHatEvent, JoystickButtonUpEvent, JoystickButtonDownEvent, OsdControlReleaseEvent, OsdControlPressEvent, WindowEvent, TextEvent, FileDropEvent, QuitEvent, FinishFrameEvent, CliCommandEvent, GroupEvent, BootEvent, FrameDrawnEvent, BreakEvent, SwitchRendererEvent, TakeReverseSnapshotEvent, AfterTimedEvent, MachineLoadedEvent, MachineActivatedEvent, MachineDeactivatedEvent, MidiInReaderEvent, MidiInWindowsEvent, MidiInCoreMidiEvent, MidiInCoreMidiVirtualEvent, MidiInALSAEvent, Rs232TesterEvent, Rs232NetEvent, ImGuiDelayedActionEvent, ImGuiActiveEvent > Event
Definition Event.hh:444
auto find(InputRange &&range, const T &value)
Definition ranges.hh:160
STL namespace.
constexpr auto transform(Range &&range, UnaryOp op)
Definition view.hh:520
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition stl.hh:134
auto rfind_unguarded(RANGE &range, const VAL &val, Proj proj={})
Similar to the find(_if)_unguarded functions above, but searches from the back to front.
Definition stl.hh:109
constexpr bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
Definition stl.hh:32
std::string strCat()
Definition strCat.hh:703
#define UNREACHABLE
constexpr auto end(const zstring_view &x)