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 
321 {
322  unique_ptr<HardwareConfig> extension;
323  try {
324  extension = HardwareConfig::createExtensionConfig(*this, name, slotname);
325  } catch (FileException& e) {
326  throw MSXException(
327  "Extension \"", name, "\" not found: ", e.getMessage());
328  } catch (MSXException& e) {
329  throw MSXException(
330  "Error in \"", name, "\" extension: ", e.getMessage());
331  }
332  return insertExtension(name, std::move(extension));
333 }
334 
336  string_view name, unique_ptr<HardwareConfig> extension)
337 {
338  try {
339  extension->parseSlots();
340  extension->createDevices();
341  } catch (MSXException& e) {
342  throw MSXException(
343  "Error in \"", name, "\" extension: ", e.getMessage());
344  }
345  string result = extension->getName();
346  extensions.push_back(std::move(extension));
347  getMSXCliComm().update(CliComm::EXTENSION, result, "add");
348  return result;
349 }
350 
352 {
353  auto it = ranges::find_if(extensions, [&](auto& v) {
354  return v->getName() == extensionName;
355  });
356  return (it != end(extensions)) ? it->get() : nullptr;
357 }
358 
360 {
361  extension.testRemove();
362  getMSXCliComm().update(CliComm::EXTENSION, extension.getName(), "remove");
363  auto it = rfind_if_unguarded(extensions,
364  [&](Extensions::value_type& v) { return v.get() == &extension; });
365  extensions.erase(it);
366 }
367 
369 {
370  return *msxCliComm;
371 }
372 
374 {
375  assert(getMachineConfig()); // needed for PluggableFactory::createAll()
376  if (!pluggingController) {
377  pluggingController = make_unique<PluggingController>(*this);
378  }
379  return *pluggingController;
380 }
381 
383 {
384  assert(getMachineConfig()); // because CPU needs to know if we're
385  // emulating turbor or not
386  return *msxCpu;
387 }
388 
390 {
391  assert(getMachineConfig());
392  return *msxCpuInterface;
393 }
394 
396 {
397  if (!panasonicMemory) {
398  panasonicMemory = make_unique<PanasonicMemory>(*this);
399  }
400  return *panasonicMemory;
401 }
402 
404 {
405  if (!deviceSwitch) {
407  }
408  return *deviceSwitch;
409 }
410 
412 {
413  if (!cassettePort) {
414  assert(getMachineConfig());
415  if (getMachineConfig()->getConfig().findChild("CassettePort")) {
416  cassettePort = make_unique<CassettePort>(*getMachineConfig());
417  } else {
418  cassettePort = make_unique<DummyCassettePort>();
419  }
420  }
421  return *cassettePort;
422 }
423 
425 {
426  assert(port < 2);
427  if (!joystickPort[0]) {
428  assert(getMachineConfig());
429  // some MSX machines only have 1 instead of 2 joystick ports
431  "JoystickPorts", "AB");
432  if ((ports != "AB") && (!ports.empty()) &&
433  (ports != "A") && (ports != "B")) {
434  throw ConfigException(
435  "Invalid JoystickPorts specification, "
436  "should be one of '', 'A', 'B' or 'AB'.");
437  }
439  if ((ports == "AB") || (ports == "A")) {
440  joystickPort[0] = make_unique<JoystickPort>(
441  ctrl, "joyporta", "MSX Joystick port A");
442  } else {
443  joystickPort[0] = make_unique<DummyJoystickPort>();
444  }
445  if ((ports == "AB") || (ports == "B")) {
446  joystickPort[1] = make_unique<JoystickPort>(
447  ctrl, "joyportb", "MSX Joystick port B");
448  } else {
449  joystickPort[1] = make_unique<DummyJoystickPort>();
450  }
451  joyPortDebuggable = make_unique<JoyPortDebuggable>(*this);
452  }
453  return *joystickPort[port];
454 }
455 
457 {
458  if (!renShaTurbo) {
459  assert(getMachineConfig());
460  renShaTurbo = make_unique<RenShaTurbo>(
461  *msxCommandController,
463  }
464  return *renShaTurbo;
465 }
466 
468 {
469  if (!ledStatus) {
470  getMSXCliComm(); // force init, to be on the safe side
471  ledStatus = make_unique<LedStatus>(
472  reactor.getRTScheduler(),
473  *msxCommandController,
474  *msxCliComm);
475  }
476  return *ledStatus;
477 }
478 
480 {
481  return *msxCommandController;
482 }
483 
485 {
486  return msxCommandController->getMachineInfoCommand();
487 }
488 
490 {
491  return scheduler->getCurrentTime();
492 }
493 
495 {
496  if (!powered) {
497  return false;
498  }
499  assert(getMachineConfig()); // otherwise powered cannot be true
500 
501  getCPU().execute(false);
502  return true;
503 }
504 
505 void MSXMotherBoard::fastForward(EmuTime::param time, bool fast)
506 {
507  assert(powered);
508  assert(getMachineConfig());
509 
510  if (time <= getCurrentTime()) return;
511 
512  ScopedAssign<bool> sa(fastForwarding, fast);
513  realTime->disable();
514  msxMixer->mute();
515  fastForwardHelper->setTarget(time);
516  while (time > getCurrentTime()) {
517  // note: this can run (slightly) past the requested time
518  getCPU().execute(true); // fast-forward mode
519  }
520  realTime->enable();
521  msxMixer->unmute();
522 }
523 
525 {
526  if (getMachineConfig()) {
527  getCPU().setPaused(true);
528  }
529  msxMixer->mute();
530 }
531 
533 {
534  if (getMachineConfig()) {
535  getCPU().setPaused(false);
536  }
537  msxMixer->unmute();
538 }
539 
541 {
542  availableDevices.push_back(&device);
543 }
544 
546 {
547  move_pop_back(availableDevices, rfind_unguarded(availableDevices, &device));
548 }
549 
551 {
552  if (!powered) return;
553  assert(getMachineConfig());
554 
555  EmuTime::param time = getCurrentTime();
557  for (auto& d : availableDevices) {
558  d->reset(time);
559  }
560  getCPU().doReset(time);
561  // let everyone know we're booting, note that the fact that this is
562  // done after the reset call to the devices is arbitrary here
564  std::make_shared<SimpleEvent>(OPENMSX_BOOT_EVENT));
565 }
566 
568 {
569  byte result = 0xff;
570  for (auto& d : availableDevices) {
571  result &= d->readIRQVector();
572  }
573  return result;
574 }
575 
577 {
578  if (powered) return;
579  if (!getMachineConfig()) return;
580 
581  powered = true;
582  // TODO: If our "powered" field is always equal to the power setting,
583  // there is not really a point in keeping it.
584  // TODO: assert disabled see note in Reactor::run() where this method
585  // is called
586  //assert(powerSetting.getBoolean() == powered);
587  powerSetting.setBoolean(true);
588  // TODO: We could make the power LED a device, so we don't have to handle
589  // it separately here.
591 
592  EmuTime::param time = getCurrentTime();
594  for (auto& d : availableDevices) {
595  d->powerUp(time);
596  }
597  getCPU().doReset(time);
598  msxMixer->unmute();
599  // let everyone know we're booting, note that the fact that this is
600  // done after the reset call to the devices is arbitrary here
602  std::make_shared<SimpleEvent>(OPENMSX_BOOT_EVENT));
603 }
604 
605 void MSXMotherBoard::powerDown()
606 {
607  if (!powered) return;
608 
609  powered = false;
610  // TODO: This assertion fails in 1 case: when quitting with a running MSX.
611  // How do we want the Reactor to shutdown: immediately or after
612  // handling all pending commands/events/updates?
613  //assert(powerSetting.getBoolean() == powered);
614  powerSetting.setBoolean(false);
616 
617  msxMixer->mute();
618 
619  EmuTime::param time = getCurrentTime();
620  for (auto& d : availableDevices) {
621  d->powerDown(time);
622  }
623 }
624 
625 void MSXMotherBoard::activate(bool active_)
626 {
627  active = active_;
628  auto event = std::make_shared<SimpleEvent>(
630  msxEventDistributor->distributeEvent(event, scheduler->getCurrentTime());
631  if (active) {
632  realTime->resync();
633  }
634 }
635 
637 {
638  if (getMachineConfig()) {
640  }
641 }
642 
644 {
646 }
647 
649 {
650  auto it = ranges::find_if(availableDevices,
651  [&](auto* d) { return d->getName() == name; });
652  return (it != end(availableDevices)) ? *it : nullptr;
653 }
654 
656 {
657  if (mapperIOCounter == 0) {
659 
660  MSXCPUInterface& cpuInterface = getCPUInterface();
661  cpuInterface.register_IO_Out(0xFC, mapperIO.get());
662  cpuInterface.register_IO_Out(0xFD, mapperIO.get());
663  cpuInterface.register_IO_Out(0xFE, mapperIO.get());
664  cpuInterface.register_IO_Out(0xFF, mapperIO.get());
665  cpuInterface.register_IO_In (0xFC, mapperIO.get());
666  cpuInterface.register_IO_In (0xFD, mapperIO.get());
667  cpuInterface.register_IO_In (0xFE, mapperIO.get());
668  cpuInterface.register_IO_In (0xFF, mapperIO.get());
669  }
670  ++mapperIOCounter;
671  return *mapperIO;
672 }
673 
675 {
676  assert(mapperIO);
677  assert(mapperIOCounter);
678  --mapperIOCounter;
679  if (mapperIOCounter == 0) {
680  MSXCPUInterface& cpuInterface = getCPUInterface();
681  cpuInterface.unregister_IO_Out(0xFC, mapperIO.get());
682  cpuInterface.unregister_IO_Out(0xFD, mapperIO.get());
683  cpuInterface.unregister_IO_Out(0xFE, mapperIO.get());
684  cpuInterface.unregister_IO_Out(0xFF, mapperIO.get());
685  cpuInterface.unregister_IO_In (0xFC, mapperIO.get());
686  cpuInterface.unregister_IO_In (0xFD, mapperIO.get());
687  cpuInterface.unregister_IO_In (0xFE, mapperIO.get());
688  cpuInterface.unregister_IO_In (0xFF, mapperIO.get());
689 
690  mapperIO.reset();
691  }
692 }
693 
694 string MSXMotherBoard::getUserName(const string& hwName)
695 {
696  auto& s = userNames[hwName];
697  unsigned n = 0;
698  string userName;
699  do {
700  userName = strCat("untitled", ++n);
701  } while (contains(s, userName));
702  s.push_back(userName);
703  return userName;
704 }
705 
706 void MSXMotherBoard::freeUserName(const string& hwName, const string& userName)
707 {
708  auto& s = userNames[hwName];
709  move_pop_back(s, rfind_unguarded(s, userName));
710 }
711 
712 // AddRemoveUpdate
713 
715  : motherBoard(motherBoard_)
716 {
717  motherBoard.getReactor().getGlobalCliComm().update(
718  CliComm::HARDWARE, motherBoard.getMachineID(), "add");
719 }
720 
722 {
723  motherBoard.getReactor().getGlobalCliComm().update(
724  CliComm::HARDWARE, motherBoard.getMachineID(), "remove");
725 }
726 
727 
728 // ResetCmd
730  : RecordedCommand(motherBoard_.getCommandController(),
731  motherBoard_.getStateChangeDistributor(),
732  motherBoard_.getScheduler(),
733  "reset")
734  , motherBoard(motherBoard_)
735 {
736 }
737 
738 void ResetCmd::execute(span<const TclObject> /*tokens*/, TclObject& /*result*/,
739  EmuTime::param /*time*/)
740 {
741  motherBoard.doReset();
742 }
743 
744 string ResetCmd::help(const vector<string>& /*tokens*/) const
745 {
746  return "Resets the MSX.";
747 }
748 
749 
750 // LoadMachineCmd
752  : Command(motherBoard_.getCommandController(), "load_machine")
753  , motherBoard(motherBoard_)
754 {
755  // The load_machine command should always directly follow a
756  // create_machine command:
757  // - It's not allowed to use load_machine on a machine that has
758  // already a machine configuration loaded earlier.
759  // - We also disallow executing most machine-specifc commands on an
760  // 'empty machine' (an 'empty machine', is a machine returned by
761  // create_machine before the load_machine command is executed, so a
762  // machine without a machine configuration). The only exception is
763  // this load_machine command and machine_info.
764  //
765  // So if the only allowed command on an empty machine is
766  // 'load_machine', (and an empty machine by itself isn't very useful),
767  // then why isn't create_machine and load_machine merged into a single
768  // command? The only reason for this is that load_machine sends out
769  // events (machine specific) and maybe you already want to know the ID
770  // of the new machine (this is returned by create_machine) before those
771  // events will be send.
772  //
773  // Why not allow all commands on an empty machine? In the past we did
774  // allow this, though it often was the source of bugs. We could in each
775  // command (when needed) check for an empty machine and then return
776  // some dummy/empty result or some error. But because I can't think of
777  // any really useful command for an empty machine, it seemed easier to
778  // just disallow most commands.
780 }
781 
783 {
784  if (tokens.size() != 2) {
785  throw SyntaxError();
786  }
787  if (motherBoard.getMachineConfig()) {
788  throw CommandException("Already loaded a config in this machine.");
789  }
790  result = motherBoard.loadMachine(tokens[1].getString().str());
791 }
792 
793 string LoadMachineCmd::help(const vector<string>& /*tokens*/) const
794 {
795  return "Load a msx machine configuration into an empty machine.";
796 }
797 
798 void LoadMachineCmd::tabCompletion(vector<string>& tokens) const
799 {
800  auto machines = Reactor::getHwConfigs("machines");
801  completeString(tokens, machines);
802 }
803 
804 
805 // ListExtCmd
807  : Command(motherBoard_.getCommandController(), "list_extensions")
808  , motherBoard(motherBoard_)
809 {
810 }
811 
813 {
814  result.addListElements(
815  view::transform(motherBoard.getExtensions(),
816  [&](auto& e) { return e->getName(); }));
817 }
818 
819 string ListExtCmd::help(const vector<string>& /*tokens*/) const
820 {
821  return "Return a list of all inserted extensions.";
822 }
823 
824 
825 // ExtCmd
826 ExtCmd::ExtCmd(MSXMotherBoard& motherBoard_, std::string commandName_)
827  : RecordedCommand(motherBoard_.getCommandController(),
828  motherBoard_.getStateChangeDistributor(),
829  motherBoard_.getScheduler(),
830  commandName_)
831  , motherBoard(motherBoard_)
832  , commandName(std::move(commandName_))
833 {
834 }
835 
837  EmuTime::param /*time*/)
838 {
839  if (tokens.size() != 2) {
840  throw SyntaxError();
841  }
842  try {
843  auto slotname = (commandName.size() == 4)
844  ? string_view(&commandName[3], 1)
845  : "any";
846  result = motherBoard.loadExtension(
847  tokens[1].getString(), slotname);
848  } catch (MSXException& e) {
849  throw CommandException(std::move(e).getMessage());
850  }
851 }
852 
853 string ExtCmd::help(const vector<string>& /*tokens*/) const
854 {
855  return "Insert a hardware extension.";
856 }
857 
858 void ExtCmd::tabCompletion(vector<string>& tokens) const
859 {
860  auto extensions = Reactor::getHwConfigs("extensions");
861  completeString(tokens, extensions);
862 }
863 
864 
865 // RemoveExtCmd
867  : RecordedCommand(motherBoard_.getCommandController(),
868  motherBoard_.getStateChangeDistributor(),
869  motherBoard_.getScheduler(),
870  "remove_extension")
871  , motherBoard(motherBoard_)
872 {
873 }
874 
876  EmuTime::param /*time*/)
877 {
878  if (tokens.size() != 2) {
879  throw SyntaxError();
880  }
881  string_view extName = tokens[1].getString();
882  HardwareConfig* extension = motherBoard.findExtension(extName);
883  if (!extension) {
884  throw CommandException("No such extension: ", extName);
885  }
886  try {
887  motherBoard.removeExtension(*extension);
888  } catch (MSXException& e) {
889  throw CommandException("Can't remove extension '", extName,
890  "': ", e.getMessage());
891  }
892 }
893 
894 string RemoveExtCmd::help(const vector<string>& /*tokens*/) const
895 {
896  return "Remove an extension from the MSX machine.";
897 }
898 
899 void RemoveExtCmd::tabCompletion(vector<string>& tokens) const
900 {
901  if (tokens.size() == 2) {
902  auto names = to_vector(view::transform(
903  motherBoard.getExtensions(),
904  [](auto& e) { return e->getName(); }));
905  completeString(tokens, names);
906  }
907 }
908 
909 
910 // MachineNameInfo
911 
913  : InfoTopic(motherBoard_.getMachineInfoCommand(), "config_name")
914  , motherBoard(motherBoard_)
915 {
916 }
917 
919  TclObject& result) const
920 {
921  result = motherBoard.getMachineName();
922 }
923 
924 string MachineNameInfo::help(const vector<string>& /*tokens*/) const
925 {
926  return "Returns the configuration name for this machine.";
927 }
928 
929 // MachineTypeInfo
930 
932  : InfoTopic(motherBoard_.getMachineInfoCommand(), "type")
933  , motherBoard(motherBoard_)
934 {
935 }
936 
938  TclObject& result) const
939 {
940  result = motherBoard.getMachineType();
941 }
942 
943 string MachineTypeInfo::help(const vector<string>& /*tokens*/) const
944 {
945  return "Returns the machine type for this machine.";
946 }
947 
948 
949 // DeviceInfo
950 
952  : InfoTopic(motherBoard_.getMachineInfoCommand(), "device")
953  , motherBoard(motherBoard_)
954 {
955 }
956 
958 {
959  switch (tokens.size()) {
960  case 2:
961  result.addListElements(
962  view::transform(motherBoard.availableDevices,
963  [](auto& d) { return d->getName(); }));
964  break;
965  case 3: {
966  string_view deviceName = tokens[2].getString();
967  MSXDevice* device = motherBoard.findDevice(deviceName);
968  if (!device) {
969  throw CommandException("No such device: ", deviceName);
970  }
971  device->getDeviceInfo(result);
972  break;
973  }
974  default:
975  throw SyntaxError();
976  }
977 }
978 
979 string DeviceInfo::help(const vector<string>& /*tokens*/) const
980 {
981  return "Without any arguments, returns the list of used device names.\n"
982  "With a device name as argument, returns the type (and for some "
983  "devices the subtype) of the given device.";
984 }
985 
986 void DeviceInfo::tabCompletion(vector<string>& tokens) const
987 {
988  if (tokens.size() == 3) {
989  auto names = to_vector(view::transform(
990  motherBoard.availableDevices,
991  [](auto& d) { return d->getName(); }));
992  completeString(tokens, names);
993  }
994 }
995 
996 
997 // FastForwardHelper
998 
1000  : Schedulable(motherBoard_.getScheduler())
1001  , motherBoard(motherBoard_)
1002 {
1003 }
1004 
1005 void FastForwardHelper::setTarget(EmuTime::param targetTime)
1006 {
1007  setSyncPoint(targetTime);
1008 }
1009 
1010 void FastForwardHelper::executeUntil(EmuTime::param /*time*/)
1011 {
1012  motherBoard.exitCPULoopSync();
1013 }
1014 
1015 
1016 // class JoyPortDebuggable
1017 
1019  : SimpleDebuggable(motherBoard_, "joystickports", "MSX Joystick Ports", 2)
1020 {
1021 }
1022 
1023 byte JoyPortDebuggable::read(unsigned address, EmuTime::param time)
1024 {
1025  return getMotherBoard().getJoystickPort(address).read(time);
1026 }
1027 
1028 void JoyPortDebuggable::write(unsigned /*address*/, byte /*value*/)
1029 {
1030  // ignore
1031 }
1032 
1033 
1034 // class SettingObserver
1035 
1037  : motherBoard(motherBoard_)
1038 {
1039 }
1040 
1041 void SettingObserver::update(const Setting& setting)
1042 {
1043  if (&setting == &motherBoard.powerSetting) {
1044  if (motherBoard.powerSetting.getBoolean()) {
1045  motherBoard.powerUp();
1046  } else {
1047  motherBoard.powerDown();
1048  }
1049  } else {
1050  UNREACHABLE;
1051  }
1052 }
1053 
1054 
1055 // serialize
1056 // version 1: initial version
1057 // version 2: added reRecordCount
1058 // version 3: removed reRecordCount (moved to ReverseManager)
1059 // version 4: moved joystickportA/B from MSXPSG to here
1060 template<typename Archive>
1061 void MSXMotherBoard::serialize(Archive& ar, unsigned version)
1062 {
1063  // don't serialize:
1064  // machineID, userNames, availableDevices, addRemoveUpdate,
1065  // sharedStuffMap, msxCliComm, msxEventDistributor,
1066  // msxCommandController, slotManager, eventDelay,
1067  // debugger, msxMixer, panasonicMemory, renShaTurbo,
1068  // ledStatus
1069 
1070  // Scheduler must come early so that devices can query current time
1071  ar.serialize("scheduler", *scheduler);
1072  // MSXMixer has already set syncpoints, those are invalid now
1073  // the following call will fix this
1074  if (ar.isLoader()) {
1075  msxMixer->reInit();
1076  }
1077 
1078  ar.serialize("name", machineName);
1079  ar.serializeWithID("config", machineConfig2, std::ref(*this));
1080  assert(getMachineConfig() == machineConfig2.get());
1081  ar.serializeWithID("extensions", extensions, std::ref(*this));
1082 
1083  if (mapperIO) ar.serialize("mapperIO", *mapperIO);
1084 
1085  MSXDeviceSwitch& devSwitch = getDeviceSwitch();
1086  if (devSwitch.hasRegisteredDevices()) {
1087  ar.serialize("deviceSwitch", devSwitch);
1088  }
1089 
1090  if (getMachineConfig()) {
1091  ar.serialize("cpu", getCPU());
1092  }
1093  ar.serialize("cpuInterface", getCPUInterface());
1094 
1095  if (auto port = dynamic_cast<CassettePort*>(&getCassettePort())) {
1096  ar.serialize("cassetteport", *port);
1097  }
1098  if (ar.versionAtLeast(version, 4)) {
1099  if (auto port = dynamic_cast<JoystickPort*>(
1100  joystickPort[0].get())) {
1101  ar.serialize("joystickportA", *port);
1102  }
1103  if (auto port = dynamic_cast<JoystickPort*>(
1104  joystickPort[1].get())) {
1105  ar.serialize("joystickportB", *port);
1106  }
1107  }
1108 
1109  if (ar.isLoader()) {
1110  powered = true; // must come before changing power setting
1111  powerSetting.setBoolean(true);
1112  getLedStatus().setLed(LedStatus::POWER, true);
1113  msxMixer->unmute();
1114  }
1115 
1116  if (version == 2) {
1117  assert(ar.isLoader());
1118  unsigned reRecordCount = 0; // silence warning
1119  ar.serialize("reRecordCount", reRecordCount);
1120  getReverseManager().setReRecordCount(reRecordCount);
1121  }
1122 }
1124 
1125 } // namespace openmsx
MachineNameInfo(MSXMotherBoard &motherBoard)
MSXDevice * findDevice(string_view name)
Find a MSXDevice by name.
auto transform(Range &&range, UnaryOp op)
Definition: view.hh:312
Contains the main loop of openMSX.
Definition: Reactor.hh:66
const std::string & getMessage() const &
Definition: MSXException.hh:23
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
void execute(span< const TclObject > tokens, TclObject &result) const override
Show info on this topic.
PluggingController & getPluggingController()
string_view getMachineName() const
MSXCPUInterface & getCPUInterface()
SettingObserver(MSXMotherBoard &motherBoard)
void exitCPULoopAsync()
See CPUCore::exitCPULoopAsync()
Definition: MSXCPU.cc:123
ListExtCmd(MSXMotherBoard &motherBoard)
std::string insertExtension(string_view name, std::unique_ptr< HardwareConfig > extension)
Definition: span.hh:34
static std::unique_ptr< HardwareConfig > createExtensionConfig(MSXMotherBoard &motherBoard, string_view extensionName, string_view slotname)
Send when a machine is (de)activated.
Definition: Event.hh:55
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
const XMLElement * findChild(string_view name) const
Definition: XMLElement.cc:114
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:106
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)
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.
static std::vector< std::string > getHwConfigs(string_view type)
Definition: Reactor.cc:300
static std::unique_ptr< HardwareConfig > createMachineConfig(MSXMotherBoard &motherBoard, const std::string &machineName)
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:191
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 update(UpdateType type, string_view name, string_view value) override
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:52
void getDeviceInfo(TclObject &result) const
Get device info.
Definition: MSXDevice.cc:384
bool hasRegisteredDevices() const
HardwareConfig * findExtension(string_view extensionName)
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:31
bool execute()
Run emulation.
InfoCommand & getMachineInfoCommand()
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)
std::string loadMachine(const std::string &machine)
void setAllowedInEmptyMachine(bool value)
Definition: Command.hh:60
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()
const std::string & getChildData(string_view name) const
Definition: XMLElement.cc:187
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)
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:108
string help(const vector< string > &tokens) const override
Print help for this command.
CommandController & getCommandController() const
Definition: Command.hh:23
void execute(span< const TclObject > tokens, TclObject &result) const override
Show info on this topic.
MSXMotherBoard & getMotherBoard() const
void removeExtension(const HardwareConfig &extension)
std::string loadExtension(string_view extensionName, string_view slotname)
bool empty() const
Definition: string_view.hh:53
This class implements a (close approximation) of the std::string_view class.
Definition: string_view.hh:15
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:840
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:58
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.
virtual void update(UpdateType type, string_view name, string_view value)=0
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:577
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:166
void setSyncPoint(EmuTime::param timestamp)
Definition: Schedulable.cc:23
void addListElements(ITER first, ITER last)
Definition: TclObject.hh:109
string_view getMachineID() const
void addDevice(MSXDevice &device)
All MSXDevices should be registered by the MotherBoard.
const XMLElement & getChild(string_view name) const
Definition: XMLElement.cc:161
auto rfind_if_unguarded(RANGE &range, PRED pred)
Definition: stl.hh:174
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:325
auto end(const string_view &x)
Definition: string_view.hh:153
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