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