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 "stl.hh"
45 #include "unreachable.hh"
46 #include <cassert>
47 #include <functional>
48 #include <iostream>
49 #include <memory>
50 
51 using std::make_unique;
52 using std::string;
53 using std::unique_ptr;
54 using std::vector;
55 
56 namespace openmsx {
57 
59 {
60 public:
61  explicit AddRemoveUpdate(MSXMotherBoard& motherBoard);
63 private:
64  MSXMotherBoard& motherBoard;
65 };
66 
67 class ResetCmd final : public RecordedCommand
68 {
69 public:
70  explicit ResetCmd(MSXMotherBoard& motherBoard);
71  void execute(array_ref<TclObject> tokens, TclObject& result,
72  EmuTime::param time) override;
73  string help(const vector<string>& tokens) const override;
74 private:
75  MSXMotherBoard& motherBoard;
76 };
77 
78 class LoadMachineCmd final : public Command
79 {
80 public:
81  explicit LoadMachineCmd(MSXMotherBoard& motherBoard);
82  void execute(array_ref<TclObject> tokens, TclObject& result) override;
83  string help(const vector<string>& tokens) const override;
84  void tabCompletion(vector<string>& tokens) const override;
85 private:
86  MSXMotherBoard& motherBoard;
87 };
88 
89 class ListExtCmd final : public Command
90 {
91 public:
92  explicit ListExtCmd(MSXMotherBoard& motherBoard);
93  void execute(array_ref<TclObject> tokens, TclObject& result) override;
94  string help(const vector<string>& tokens) const override;
95 private:
96  MSXMotherBoard& motherBoard;
97 };
98 
99 class RemoveExtCmd final : public RecordedCommand
100 {
101 public:
102  explicit RemoveExtCmd(MSXMotherBoard& motherBoard);
103  void execute(array_ref<TclObject> tokens, TclObject& result,
104  EmuTime::param time) override;
105  string help(const vector<string>& tokens) const override;
106  void tabCompletion(vector<string>& tokens) const override;
107 private:
108  MSXMotherBoard& motherBoard;
109 };
110 
111 class MachineNameInfo final : public InfoTopic
112 {
113 public:
114  explicit MachineNameInfo(MSXMotherBoard& motherBoard);
115  void execute(array_ref<TclObject> tokens,
116  TclObject& result) const override;
117  string help(const vector<string>& tokens) const override;
118 private:
119  MSXMotherBoard& motherBoard;
120 };
121 
122 class MachineTypeInfo final : public InfoTopic
123 {
124 public:
125  explicit MachineTypeInfo(MSXMotherBoard& motherBoard);
126  void execute(array_ref<TclObject> tokens,
127  TclObject& result) const override;
128  string help(const vector<string>& tokens) const override;
129 private:
130  MSXMotherBoard& motherBoard;
131 };
132 
133 class DeviceInfo final : public InfoTopic
134 {
135 public:
136  explicit DeviceInfo(MSXMotherBoard& motherBoard);
137  void execute(array_ref<TclObject> tokens,
138  TclObject& result) const override;
139  string help(const vector<string>& tokens) const override;
140  void tabCompletion(vector<string>& tokens) const override;
141 private:
142  MSXMotherBoard& motherBoard;
143 };
144 
145 class FastForwardHelper final : private Schedulable
146 {
147 public:
148  explicit FastForwardHelper(MSXMotherBoard& motherBoard);
149  void setTarget(EmuTime::param targetTime);
150 private:
151  void executeUntil(EmuTime::param time) override;
152  MSXMotherBoard& motherBoard;
153 };
154 
156 {
157 public:
158  explicit JoyPortDebuggable(MSXMotherBoard& motherBoard);
159  byte read(unsigned address, EmuTime::param time) override;
160  void write(unsigned address, byte value) override;
161 };
162 
163 class SettingObserver final : public Observer<Setting>
164 {
165 public:
166  explicit SettingObserver(MSXMotherBoard& motherBoard);
167  void update(const Setting& setting) override;
168 private:
169  MSXMotherBoard& motherBoard;
170 };
171 
172 
173 static unsigned machineIDCounter = 0;
174 
176  : reactor(reactor_)
177  , machineID(strCat("machine", ++machineIDCounter))
178  , mapperIOCounter(0)
179  , machineConfig(nullptr)
180  , msxCliComm(make_unique<MSXCliComm>(*this, reactor.getGlobalCliComm()))
181  , msxEventDistributor(make_unique<MSXEventDistributor>())
182  , stateChangeDistributor(make_unique<StateChangeDistributor>())
183  , msxCommandController(make_unique<MSXCommandController>(
184  reactor.getGlobalCommandController(), reactor,
185  *this, *msxEventDistributor, machineID))
186  , scheduler(make_unique<Scheduler>())
187  , msxMixer(make_unique<MSXMixer>(
188  reactor.getMixer(), *this,
189  reactor.getGlobalSettings()))
190  , videoSourceSetting(*msxCommandController)
191  , fastForwardHelper(make_unique<FastForwardHelper>(*this))
192  , settingObserver(make_unique<SettingObserver>(*this))
193  , powerSetting(reactor.getGlobalSettings().getPowerSetting())
194  , powered(false)
195  , active(false)
196  , fastForwarding(false)
197 {
198  slotManager = make_unique<CartridgeSlotManager>(*this);
199  reverseManager = make_unique<ReverseManager>(*this);
200  resetCommand = make_unique<ResetCmd>(*this);
201  loadMachineCommand = make_unique<LoadMachineCmd>(*this);
202  listExtCommand = make_unique<ListExtCmd>(*this);
203  extCommand = make_unique<ExtCmd>(*this, "ext");
204  removeExtCommand = make_unique<RemoveExtCmd>(*this);
205  machineNameInfo = make_unique<MachineNameInfo>(*this);
206  machineTypeInfo = make_unique<MachineTypeInfo>(*this);
207  deviceInfo = make_unique<DeviceInfo>(*this);
208  debugger = make_unique<Debugger>(*this);
209 
210  msxMixer->mute(); // powered down
211 
212  // Do this before machine-specific settings are created, otherwise
213  // a setting-info clicomm message is send with a machine id that hasn't
214  // been announced yet over clicomm.
215  addRemoveUpdate = make_unique<AddRemoveUpdate>(*this);
216 
217  // TODO: Initialization of this field cannot be done much earlier because
218  // EventDelay creates a setting, calling getMSXCliComm()
219  // on MSXMotherBoard, so "pimpl" has to be set up already.
220  eventDelay = make_unique<EventDelay>(
221  *scheduler, *msxCommandController,
222  reactor.getEventDistributor(), *msxEventDistributor,
223  *reverseManager);
224  realTime = make_unique<RealTime>(
225  *this, reactor.getGlobalSettings(), *eventDelay);
226 
227  powerSetting.attach(*settingObserver);
228 }
229 
231 {
232  powerSetting.detach(*settingObserver);
233  deleteMachine();
234 
235  assert(mapperIOCounter == 0);
236  assert(availableDevices.empty());
237  assert(extensions.empty());
238  assert(!machineConfig2);
239  assert(!getMachineConfig());
240 }
241 
242 void MSXMotherBoard::deleteMachine()
243 {
244  while (!extensions.empty()) {
245  try {
246  removeExtension(*extensions.back());
247  } catch (MSXException& e) {
248  std::cerr << "Internal error: failed to remove "
249  "extension while deleting a machine: "
250  << e.getMessage() << std::endl;
251  UNREACHABLE;
252  }
253  }
254 
255  machineConfig2.reset();
256  machineConfig = nullptr;
257 }
258 
260 {
261  assert(!getMachineConfig());
262  machineConfig = machineConfig_;
263 
264  // make sure the CPU gets instantiated from the main thread
265  assert(!msxCpu);
266  msxCpu = make_unique<MSXCPU>(*this);
267  msxCpuInterface = make_unique<MSXCPUInterface>(*this);
268 }
269 
271 {
272  const HardwareConfig* machine = getMachineConfig();
273  if (machine) {
274  return machine->getConfig().getChild("info").getChildData("type");
275  } else {
276  return "";
277  }
278 }
279 
281 {
282  const HardwareConfig* config = getMachineConfig();
283  assert(config);
284  return config->getConfig().getChild("devices").findChild("S1990") != nullptr;
285 }
286 
287 string MSXMotherBoard::loadMachine(const string& machine)
288 {
289  assert(machineName.empty());
290  assert(extensions.empty());
291  assert(!machineConfig2);
292  assert(!getMachineConfig());
293 
294  try {
295  machineConfig2 = HardwareConfig::createMachineConfig(*this, machine);
296  setMachineConfig(machineConfig2.get());
297  } catch (FileException& e) {
298  throw MSXException("Machine \"", machine, "\" not found: ",
299  e.getMessage());
300  } catch (MSXException& e) {
301  throw MSXException("Error in \"", machine, "\" machine: ",
302  e.getMessage());
303  }
304  try {
305  machineConfig->parseSlots();
306  machineConfig->createDevices();
307  } catch (MSXException& e) {
308  throw MSXException("Error in \"", machine, "\" machine: ",
309  e.getMessage());
310  }
311  if (powerSetting.getBoolean()) {
312  powerUp();
313  }
314  machineName = machine;
315  return machineName;
316 }
317 
319 {
320  unique_ptr<HardwareConfig> extension;
321  try {
322  extension = HardwareConfig::createExtensionConfig(*this, name, slotname);
323  } catch (FileException& e) {
324  throw MSXException(
325  "Extension \"", name, "\" not found: ", e.getMessage());
326  } catch (MSXException& e) {
327  throw MSXException(
328  "Error in \"", name, "\" extension: ", e.getMessage());
329  }
330  return insertExtension(name, std::move(extension));
331 }
332 
334  string_view name, unique_ptr<HardwareConfig> extension)
335 {
336  try {
337  extension->parseSlots();
338  extension->createDevices();
339  } catch (MSXException& e) {
340  throw MSXException(
341  "Error in \"", name, "\" extension: ", e.getMessage());
342  }
343  string result = extension->getName();
344  extensions.push_back(std::move(extension));
345  getMSXCliComm().update(CliComm::EXTENSION, result, "add");
346  return result;
347 }
348 
350 {
351  auto it = std::find_if(begin(extensions), end(extensions),
352  [&](Extensions::value_type& v) {
353  return v->getName() == extensionName; });
354  return (it != end(extensions)) ? it->get() : nullptr;
355 }
356 
358 {
359  extension.testRemove();
360  getMSXCliComm().update(CliComm::EXTENSION, extension.getName(), "remove");
361  auto it = rfind_if_unguarded(extensions,
362  [&](Extensions::value_type& v) { return v.get() == &extension; });
363  extensions.erase(it);
364 }
365 
367 {
368  return *msxCliComm;
369 }
370 
372 {
373  assert(getMachineConfig()); // needed for PluggableFactory::createAll()
374  if (!pluggingController) {
375  pluggingController = make_unique<PluggingController>(*this);
376  }
377  return *pluggingController;
378 }
379 
381 {
382  assert(getMachineConfig()); // because CPU needs to know if we're
383  // emulating turbor or not
384  return *msxCpu;
385 }
386 
388 {
389  assert(getMachineConfig());
390  return *msxCpuInterface;
391 }
392 
394 {
395  if (!panasonicMemory) {
396  panasonicMemory = make_unique<PanasonicMemory>(*this);
397  }
398  return *panasonicMemory;
399 }
400 
402 {
403  if (!deviceSwitch) {
405  }
406  return *deviceSwitch;
407 }
408 
410 {
411  if (!cassettePort) {
412  assert(getMachineConfig());
413  if (getMachineConfig()->getConfig().findChild("CassettePort")) {
414  cassettePort = make_unique<CassettePort>(*getMachineConfig());
415  } else {
416  cassettePort = make_unique<DummyCassettePort>();
417  }
418  }
419  return *cassettePort;
420 }
421 
423 {
424  assert(port < 2);
425  if (!joystickPort[0]) {
426  assert(getMachineConfig());
427  // some MSX machines only have 1 instead of 2 joystick ports
429  "JoystickPorts", "AB");
430  if ((ports != "AB") && (!ports.empty()) &&
431  (ports != "A") && (ports != "B")) {
432  throw ConfigException(
433  "Invalid JoystickPorts specification, "
434  "should be one of '', 'A', 'B' or 'AB'.");
435  }
437  if ((ports == "AB") || (ports == "A")) {
438  joystickPort[0] = make_unique<JoystickPort>(
439  ctrl, "joyporta", "MSX Joystick port A");
440  } else {
441  joystickPort[0] = make_unique<DummyJoystickPort>();
442  }
443  if ((ports == "AB") || (ports == "B")) {
444  joystickPort[1] = make_unique<JoystickPort>(
445  ctrl, "joyportb", "MSX Joystick port B");
446  } else {
447  joystickPort[1] = make_unique<DummyJoystickPort>();
448  }
449  joyPortDebuggable = make_unique<JoyPortDebuggable>(*this);
450  }
451  return *joystickPort[port];
452 }
453 
455 {
456  if (!renShaTurbo) {
457  assert(getMachineConfig());
458  renShaTurbo = make_unique<RenShaTurbo>(
459  *msxCommandController,
461  }
462  return *renShaTurbo;
463 }
464 
466 {
467  if (!ledStatus) {
468  getMSXCliComm(); // force init, to be on the safe side
469  ledStatus = make_unique<LedStatus>(
470  reactor.getRTScheduler(),
471  *msxCommandController,
472  *msxCliComm);
473  }
474  return *ledStatus;
475 }
476 
478 {
479  return *msxCommandController;
480 }
481 
483 {
484  return msxCommandController->getMachineInfoCommand();
485 }
486 
488 {
489  return scheduler->getCurrentTime();
490 }
491 
493 {
494  if (!powered) {
495  return false;
496  }
497  assert(getMachineConfig()); // otherwise powered cannot be true
498 
499  getCPU().execute(false);
500  return true;
501 }
502 
503 void MSXMotherBoard::fastForward(EmuTime::param time, bool fast)
504 {
505  assert(powered);
506  assert(getMachineConfig());
507 
508  if (time <= getCurrentTime()) return;
509 
510  ScopedAssign<bool> sa(fastForwarding, fast);
511  realTime->disable();
512  msxMixer->mute();
513  fastForwardHelper->setTarget(time);
514  while (time > getCurrentTime()) {
515  // note: this can run (slightly) past the requested time
516  getCPU().execute(true); // fast-forward mode
517  }
518  realTime->enable();
519  msxMixer->unmute();
520 }
521 
523 {
524  if (getMachineConfig()) {
525  getCPU().setPaused(true);
526  }
527  msxMixer->mute();
528 }
529 
531 {
532  if (getMachineConfig()) {
533  getCPU().setPaused(false);
534  }
535  msxMixer->unmute();
536 }
537 
539 {
540  availableDevices.push_back(&device);
541 }
542 
544 {
545  move_pop_back(availableDevices, rfind_unguarded(availableDevices, &device));
546 }
547 
549 {
550  if (!powered) return;
551  assert(getMachineConfig());
552 
553  EmuTime::param time = getCurrentTime();
555  for (auto& d : availableDevices) {
556  d->reset(time);
557  }
558  getCPU().doReset(time);
559  // let everyone know we're booting, note that the fact that this is
560  // done after the reset call to the devices is arbitrary here
562  std::make_shared<SimpleEvent>(OPENMSX_BOOT_EVENT));
563 }
564 
566 {
567  byte result = 0xff;
568  for (auto& d : availableDevices) {
569  result &= d->readIRQVector();
570  }
571  return result;
572 }
573 
575 {
576  if (powered) return;
577  if (!getMachineConfig()) return;
578 
579  powered = true;
580  // TODO: If our "powered" field is always equal to the power setting,
581  // there is not really a point in keeping it.
582  // TODO: assert disabled see note in Reactor::run() where this method
583  // is called
584  //assert(powerSetting.getBoolean() == powered);
585  powerSetting.setBoolean(true);
586  // TODO: We could make the power LED a device, so we don't have to handle
587  // it separately here.
589 
590  EmuTime::param time = getCurrentTime();
592  for (auto& d : availableDevices) {
593  d->powerUp(time);
594  }
595  getCPU().doReset(time);
596  msxMixer->unmute();
597  // let everyone know we're booting, note that the fact that this is
598  // done after the reset call to the devices is arbitrary here
600  std::make_shared<SimpleEvent>(OPENMSX_BOOT_EVENT));
601 }
602 
603 void MSXMotherBoard::powerDown()
604 {
605  if (!powered) return;
606 
607  powered = false;
608  // TODO: This assertion fails in 1 case: when quitting with a running MSX.
609  // How do we want the Reactor to shutdown: immediately or after
610  // handling all pending commands/events/updates?
611  //assert(powerSetting.getBoolean() == powered);
612  powerSetting.setBoolean(false);
614 
615  msxMixer->mute();
616 
617  EmuTime::param time = getCurrentTime();
618  for (auto& d : availableDevices) {
619  d->powerDown(time);
620  }
621 }
622 
623 void MSXMotherBoard::activate(bool active_)
624 {
625  active = active_;
626  auto event = std::make_shared<SimpleEvent>(
628  msxEventDistributor->distributeEvent(event, scheduler->getCurrentTime());
629  if (active) {
630  realTime->resync();
631  }
632 }
633 
635 {
636  if (getMachineConfig()) {
638  }
639 }
640 
642 {
644 }
645 
647 {
648  for (auto& d : availableDevices) {
649  if (d->getName() == name) {
650  return d;
651  }
652  }
653  return 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 (find(begin(s), end(s), userName) != end(s));
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(array_ref<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  if (tokens.size() != 2) {
786  throw SyntaxError();
787  }
788  if (motherBoard.getMachineConfig()) {
789  throw CommandException("Already loaded a config in this machine.");
790  }
791  result.setString(motherBoard.loadMachine(tokens[1].getString().str()));
792 }
793 
794 string LoadMachineCmd::help(const vector<string>& /*tokens*/) const
795 {
796  return "Load a msx machine configuration into an empty machine.";
797 }
798 
799 void LoadMachineCmd::tabCompletion(vector<string>& tokens) const
800 {
801  auto machines = Reactor::getHwConfigs("machines");
802  completeString(tokens, machines);
803 }
804 
805 
806 // ListExtCmd
808  : Command(motherBoard_.getCommandController(), "list_extensions")
809  , motherBoard(motherBoard_)
810 {
811 }
812 
814 {
815  for (auto& e : motherBoard.getExtensions()) {
816  result.addListElement(e->getName());
817  }
818 }
819 
820 string ListExtCmd::help(const vector<string>& /*tokens*/) const
821 {
822  return "Return a list of all inserted extensions.";
823 }
824 
825 
826 // ExtCmd
827 ExtCmd::ExtCmd(MSXMotherBoard& motherBoard_, std::string commandName_)
828  : RecordedCommand(motherBoard_.getCommandController(),
829  motherBoard_.getStateChangeDistributor(),
830  motherBoard_.getScheduler(),
831  commandName_)
832  , motherBoard(motherBoard_)
833  , commandName(std::move(commandName_))
834 {
835 }
836 
838  EmuTime::param /*time*/)
839 {
840  if (tokens.size() != 2) {
841  throw SyntaxError();
842  }
843  try {
844  auto slotname = (commandName.size() == 4)
845  ? string_view(&commandName[3], 1)
846  : "any";
847  result.setString(motherBoard.loadExtension(
848  tokens[1].getString(), slotname));
849  } catch (MSXException& e) {
850  throw CommandException(std::move(e).getMessage());
851  }
852 }
853 
854 string ExtCmd::help(const vector<string>& /*tokens*/) const
855 {
856  return "Insert a hardware extension.";
857 }
858 
859 void ExtCmd::tabCompletion(vector<string>& tokens) const
860 {
861  auto extensions = Reactor::getHwConfigs("extensions");
862  completeString(tokens, extensions);
863 }
864 
865 
866 // RemoveExtCmd
868  : RecordedCommand(motherBoard_.getCommandController(),
869  motherBoard_.getStateChangeDistributor(),
870  motherBoard_.getScheduler(),
871  "remove_extension")
872  , motherBoard(motherBoard_)
873 {
874 }
875 
877  EmuTime::param /*time*/)
878 {
879  if (tokens.size() != 2) {
880  throw SyntaxError();
881  }
882  string_view extName = tokens[1].getString();
883  HardwareConfig* extension = motherBoard.findExtension(extName);
884  if (!extension) {
885  throw CommandException("No such extension: ", extName);
886  }
887  try {
888  motherBoard.removeExtension(*extension);
889  } catch (MSXException& e) {
890  throw CommandException("Can't remove extension '", extName,
891  "': ", e.getMessage());
892  }
893 }
894 
895 string RemoveExtCmd::help(const vector<string>& /*tokens*/) const
896 {
897  return "Remove an extension from the MSX machine.";
898 }
899 
900 void RemoveExtCmd::tabCompletion(vector<string>& tokens) const
901 {
902  if (tokens.size() == 2) {
903  vector<string_view> names;
904  for (auto& e : motherBoard.getExtensions()) {
905  names.emplace_back(e->getName());
906  }
907  completeString(tokens, names);
908  }
909 }
910 
911 
912 // MachineNameInfo
913 
915  : InfoTopic(motherBoard_.getMachineInfoCommand(), "config_name")
916  , motherBoard(motherBoard_)
917 {
918 }
919 
921  TclObject& result) const
922 {
923  result.setString(motherBoard.getMachineName());
924 }
925 
926 string MachineNameInfo::help(const vector<string>& /*tokens*/) const
927 {
928  return "Returns the configuration name for this machine.";
929 }
930 
931 // MachineTypeInfo
932 
934  : InfoTopic(motherBoard_.getMachineInfoCommand(), "type")
935  , motherBoard(motherBoard_)
936 {
937 }
938 
940  TclObject& result) const
941 {
942  result.setString(motherBoard.getMachineType());
943 }
944 
945 string MachineTypeInfo::help(const vector<string>& /*tokens*/) const
946 {
947  return "Returns the machine type for this machine.";
948 }
949 
950 
951 // DeviceInfo
952 
954  : InfoTopic(motherBoard_.getMachineInfoCommand(), "device")
955  , motherBoard(motherBoard_)
956 {
957 }
958 
960 {
961  switch (tokens.size()) {
962  case 2:
963  for (auto& d : motherBoard.availableDevices) {
964  result.addListElement(d->getName());
965  }
966  break;
967  case 3: {
968  string_view deviceName = tokens[2].getString();
969  MSXDevice* device = motherBoard.findDevice(deviceName);
970  if (!device) {
971  throw CommandException("No such device: ", deviceName);
972  }
973  device->getDeviceInfo(result);
974  break;
975  }
976  default:
977  throw SyntaxError();
978  }
979 }
980 
981 string DeviceInfo::help(const vector<string>& /*tokens*/) const
982 {
983  return "Without any arguments, returns the list of used device names.\n"
984  "With a device name as argument, returns the type (and for some "
985  "devices the subtype) of the given device.";
986 }
987 
988 void DeviceInfo::tabCompletion(vector<string>& tokens) const
989 {
990  if (tokens.size() == 3) {
991  vector<string> names;
992  for (auto& d : motherBoard.availableDevices) {
993  names.push_back(d->getName());
994  }
995  completeString(tokens, names);
996  }
997 }
998 
999 
1000 // FastForwardHelper
1001 
1003  : Schedulable(motherBoard_.getScheduler())
1004  , motherBoard(motherBoard_)
1005 {
1006 }
1007 
1008 void FastForwardHelper::setTarget(EmuTime::param targetTime)
1009 {
1010  setSyncPoint(targetTime);
1011 }
1012 
1013 void FastForwardHelper::executeUntil(EmuTime::param /*time*/)
1014 {
1015  motherBoard.exitCPULoopSync();
1016 }
1017 
1018 
1019 // class JoyPortDebuggable
1020 
1022  : SimpleDebuggable(motherBoard_, "joystickports", "MSX Joystick Ports", 2)
1023 {
1024 }
1025 
1026 byte JoyPortDebuggable::read(unsigned address, EmuTime::param time)
1027 {
1028  return getMotherBoard().getJoystickPort(address).read(time);
1029 }
1030 
1031 void JoyPortDebuggable::write(unsigned /*address*/, byte /*value*/)
1032 {
1033  // ignore
1034 }
1035 
1036 
1037 // class SettingObserver
1038 
1040  : motherBoard(motherBoard_)
1041 {
1042 }
1043 
1044 void SettingObserver::update(const Setting& setting)
1045 {
1046  if (&setting == &motherBoard.powerSetting) {
1047  if (motherBoard.powerSetting.getBoolean()) {
1048  motherBoard.powerUp();
1049  } else {
1050  motherBoard.powerDown();
1051  }
1052  } else {
1053  UNREACHABLE;
1054  }
1055 }
1056 
1057 
1058 // serialize
1059 // version 1: initial version
1060 // version 2: added reRecordCount
1061 // version 3: removed reRecordCount (moved to ReverseManager)
1062 // version 4: moved joystickportA/B from MSXPSG to here
1063 template<typename Archive>
1064 void MSXMotherBoard::serialize(Archive& ar, unsigned version)
1065 {
1066  // don't serialize:
1067  // machineID, userNames, availableDevices, addRemoveUpdate,
1068  // sharedStuffMap, msxCliComm, msxEventDistributor,
1069  // msxCommandController, slotManager, eventDelay,
1070  // debugger, msxMixer, panasonicMemory, renShaTurbo,
1071  // ledStatus
1072 
1073  // Scheduler must come early so that devices can query current time
1074  ar.serialize("scheduler", *scheduler);
1075  // MSXMixer has already set syncpoints, those are invalid now
1076  // the following call will fix this
1077  if (ar.isLoader()) {
1078  msxMixer->reInit();
1079  }
1080 
1081  ar.serialize("name", machineName);
1082  ar.serializeWithID("config", machineConfig2, std::ref(*this));
1083  assert(getMachineConfig() == machineConfig2.get());
1084  ar.serializeWithID("extensions", extensions, std::ref(*this));
1085 
1086  if (mapperIO) ar.serialize("mapperIO", *mapperIO);
1087 
1088  MSXDeviceSwitch& devSwitch = getDeviceSwitch();
1089  if (devSwitch.hasRegisteredDevices()) {
1090  ar.serialize("deviceSwitch", devSwitch);
1091  }
1092 
1093  if (getMachineConfig()) {
1094  ar.serialize("cpu", getCPU());
1095  }
1096  ar.serialize("cpuInterface", getCPUInterface());
1097 
1098  if (auto port = dynamic_cast<CassettePort*>(&getCassettePort())) {
1099  ar.serialize("cassetteport", *port);
1100  }
1101  if (ar.versionAtLeast(version, 4)) {
1102  if (auto port = dynamic_cast<JoystickPort*>(
1103  joystickPort[0].get())) {
1104  ar.serialize("joystickportA", *port);
1105  }
1106  if (auto port = dynamic_cast<JoystickPort*>(
1107  joystickPort[1].get())) {
1108  ar.serialize("joystickportB", *port);
1109  }
1110  }
1111 
1112  if (ar.isLoader()) {
1113  powered = true; // must come before changing power setting
1114  powerSetting.setBoolean(true);
1115  getLedStatus().setLed(LedStatus::POWER, true);
1116  msxMixer->unmute();
1117  }
1118 
1119  if (version == 2) {
1120  assert(ar.isLoader());
1121  unsigned reRecordCount = 0; // silence warning
1122  ar.serialize("reRecordCount", reRecordCount);
1123  getReverseManager().setReRecordCount(reRecordCount);
1124  }
1125 }
1127 
1128 } // namespace openmsx
MachineNameInfo(MSXMotherBoard &motherBoard)
MSXDevice * findDevice(string_view name)
Find a MSXDevice by name.
Contains the main loop of openMSX.
Definition: Reactor.hh:63
string_view::const_iterator begin(const string_view &x)
Definition: string_view.hh:152
const std::string & getMessage() const &
Definition: MSXException.hh:23
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
void execute(array_ref< TclObject > tokens, TclObject &result) const override
Show info on this topic.
PluggingController & getPluggingController()
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)
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:117
void pause()
Pause MSX machine.
void doReset(EmuTime::param time)
Reset CPU.
Definition: MSXCPU.cc:80
void removeDevice(MSXDevice &device)
const std::string & getMachineName() const
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 execute(array_ref< TclObject > tokens, TclObject &result) const override
Show info on this topic.
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:293
static std::unique_ptr< HardwareConfig > createMachineConfig(MSXMotherBoard &motherBoard, const std::string &machineName)
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition: stl.hh:192
ResetCmd(MSXMotherBoard &motherBoard)
void tabCompletion(std::vector< std::string > &tokens) const override
Attempt tab completion for this command.
void execute(array_ref< TclObject > tokens, TclObject &result) override
Execute this command.
CassettePortInterface & getCassettePort()
const Extensions & getExtensions() const
void update(UpdateType type, string_view name, string_view value) override
EventDistributor & getEventDistributor()
Definition: Reactor.hh:78
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)
void execute(array_ref< 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 attach(Observer< T > &observer)
Definition: Subject.hh:52
void execute(array_ref< 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 getDeviceInfo(TclObject &result) const
Get device info.
Definition: MSXDevice.cc:386
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:77
GlobalSettings & getGlobalSettings()
Definition: Reactor.hh:100
Every class that wants to get scheduled at some point must inherit from this class.
Definition: Schedulable.hh:33
bool execute()
Run emulation.
auto rfind_unguarded(RANGE &range, const VAL &val) -> decltype(std::begin(range))
Similar to the find(_if)_unguarded functions above, but searches from the back to front...
Definition: stl.hh:164
InfoCommand & getMachineInfoCommand()
This class implements a subset of the proposal for std::array_ref (proposed for the next c++ standard...
Definition: array_ref.hh:19
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 & getMachineID() const
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.
string_view::const_iterator end(const string_view &x)
Definition: string_view.hh:153
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.
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:79
CommandController & getCommandController()
const std::string & getChildData(string_view name) const
Definition: XMLElement.cc:192
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)
void execute(array_ref< TclObject > tokens, TclObject &result) override
Execute this command.
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
void setString(string_view value)
Definition: TclObject.cc:14
string help(const vector< string > &tokens) const override
Print help for this command.
CommandController & getCommandController() const
Definition: Command.hh:23
MSXMotherBoard & getMotherBoard() const
size_type size() const
Definition: array_ref.hh:61
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)
#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)
void addListElement(string_view element)
Definition: TclObject.cc:69
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
void execute(array_ref< 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...
Generic Gang-of-Four Observer class, templatized edition.
Definition: Observer.hh:9
void setSyncPoint(EmuTime::param timestamp)
Definition: Schedulable.cc:23
void addDevice(MSXDevice &device)
All MSXDevices should be registered by the MotherBoard.
void execute(array_ref< TclObject > tokens, TclObject &result) const override
Show info on this topic.
const XMLElement & getChild(string_view name) const
Definition: XMLElement.cc:166
auto rfind_if_unguarded(RANGE &range, PRED pred) -> decltype(std::begin(range))
Definition: stl.hh:174
MSXDeviceSwitch & getDeviceSwitch()
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:35