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