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