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 "memory.hh"
45 #include "stl.hh"
46 #include "unreachable.hh"
47 #include <cassert>
48 #include <functional>
49 #include <iostream>
50 
51 using std::string;
52 using std::vector;
53 using std::unique_ptr;
54 
55 namespace openmsx {
56 
58 {
59 public:
60  explicit AddRemoveUpdate(MSXMotherBoard& motherBoard);
62 private:
63  MSXMotherBoard& motherBoard;
64 };
65 
66 class ResetCmd final : public RecordedCommand
67 {
68 public:
69  explicit ResetCmd(MSXMotherBoard& motherBoard);
70  void execute(array_ref<TclObject> tokens, TclObject& result,
71  EmuTime::param time) override;
72  string help(const vector<string>& tokens) const override;
73 private:
74  MSXMotherBoard& motherBoard;
75 };
76 
77 class LoadMachineCmd final : public Command
78 {
79 public:
80  explicit LoadMachineCmd(MSXMotherBoard& motherBoard);
81  void execute(array_ref<TclObject> tokens, TclObject& result) override;
82  string help(const vector<string>& tokens) const override;
83  void tabCompletion(vector<string>& tokens) const override;
84 private:
85  MSXMotherBoard& motherBoard;
86 };
87 
88 class ListExtCmd final : public Command
89 {
90 public:
91  explicit ListExtCmd(MSXMotherBoard& motherBoard);
92  void execute(array_ref<TclObject> tokens, TclObject& result) override;
93  string help(const vector<string>& tokens) const override;
94 private:
95  MSXMotherBoard& motherBoard;
96 };
97 
98 class RemoveExtCmd final : public RecordedCommand
99 {
100 public:
101  explicit RemoveExtCmd(MSXMotherBoard& motherBoard);
102  void execute(array_ref<TclObject> tokens, TclObject& result,
103  EmuTime::param time) override;
104  string help(const vector<string>& tokens) const override;
105  void tabCompletion(vector<string>& tokens) const override;
106 private:
107  MSXMotherBoard& motherBoard;
108 };
109 
110 class MachineNameInfo final : public InfoTopic
111 {
112 public:
113  explicit MachineNameInfo(MSXMotherBoard& motherBoard);
114  void execute(array_ref<TclObject> tokens,
115  TclObject& result) const override;
116  string help(const vector<string>& tokens) const override;
117 private:
118  MSXMotherBoard& motherBoard;
119 };
120 
121 class MachineTypeInfo final : public InfoTopic
122 {
123 public:
124  explicit MachineTypeInfo(MSXMotherBoard& motherBoard);
125  void execute(array_ref<TclObject> tokens,
126  TclObject& result) const override;
127  string help(const vector<string>& tokens) const override;
128 private:
129  MSXMotherBoard& motherBoard;
130 };
131 
132 class DeviceInfo final : public InfoTopic
133 {
134 public:
135  explicit DeviceInfo(MSXMotherBoard& motherBoard);
136  void execute(array_ref<TclObject> tokens,
137  TclObject& result) const override;
138  string help(const vector<string>& tokens) const override;
139  void tabCompletion(vector<string>& tokens) const override;
140 private:
141  MSXMotherBoard& motherBoard;
142 };
143 
144 class FastForwardHelper final : private Schedulable
145 {
146 public:
147  explicit FastForwardHelper(MSXMotherBoard& msxMotherBoardImpl);
148  void setTarget(EmuTime::param targetTime);
149 private:
150  void executeUntil(EmuTime::param time) override;
151  MSXMotherBoard& motherBoard;
152 };
153 
155 {
156 public:
157  explicit JoyPortDebuggable(MSXMotherBoard& motherBoard);
158  byte read(unsigned address, EmuTime::param time) override;
159  void write(unsigned address, byte value) override;
160 };
161 
162 class SettingObserver final : public Observer<Setting>
163 {
164 public:
165  explicit SettingObserver(MSXMotherBoard& motherBoard);
166  void update(const Setting& setting) override;
167 private:
168  MSXMotherBoard& motherBoard;
169 };
170 
171 
172 static unsigned machineIDCounter = 0;
173 
175  : reactor(reactor_)
176  , machineID(strCat("machine", ++machineIDCounter))
177  , mapperIOCounter(0)
178  , machineConfig(nullptr)
179  , msxCliComm(make_unique<MSXCliComm>(*this, reactor.getGlobalCliComm()))
180  , msxEventDistributor(make_unique<MSXEventDistributor>())
181  , stateChangeDistributor(make_unique<StateChangeDistributor>())
182  , msxCommandController(make_unique<MSXCommandController>(
183  reactor.getGlobalCommandController(), reactor,
184  *this, *msxEventDistributor, machineID))
185  , scheduler(make_unique<Scheduler>())
186  , msxMixer(make_unique<MSXMixer>(
187  reactor.getMixer(), *this,
188  reactor.getGlobalSettings()))
189  , videoSourceSetting(*msxCommandController)
190  , fastForwardHelper(make_unique<FastForwardHelper>(*this))
191  , settingObserver(make_unique<SettingObserver>(*this))
192  , powerSetting(reactor.getGlobalSettings().getPowerSetting())
193  , powered(false)
194  , active(false)
195  , fastForwarding(false)
196 {
197  slotManager = make_unique<CartridgeSlotManager>(*this);
198  reverseManager = make_unique<ReverseManager>(*this);
199  resetCommand = make_unique<ResetCmd>(*this);
200  loadMachineCommand = make_unique<LoadMachineCmd>(*this);
201  listExtCommand = make_unique<ListExtCmd>(*this);
202  extCommand = make_unique<ExtCmd>(*this, "ext");
203  removeExtCommand = make_unique<RemoveExtCmd>(*this);
204  machineNameInfo = make_unique<MachineNameInfo>(*this);
205  machineTypeInfo = make_unique<MachineTypeInfo>(*this);
206  deviceInfo = make_unique<DeviceInfo>(*this);
207  debugger = make_unique<Debugger>(*this);
208 
209  msxMixer->mute(); // powered down
210 
211  // Do this before machine-specific settings are created, otherwise
212  // a setting-info clicomm message is send with a machine id that hasn't
213  // been announced yet over clicomm.
214  addRemoveUpdate = make_unique<AddRemoveUpdate>(*this);
215 
216  // TODO: Initialization of this field cannot be done much earlier because
217  // EventDelay creates a setting, calling getMSXCliComm()
218  // on MSXMotherBoard, so "pimpl" has to be set up already.
219  eventDelay = make_unique<EventDelay>(
220  *scheduler, *msxCommandController,
221  reactor.getEventDistributor(), *msxEventDistributor,
222  *reverseManager);
223  realTime = make_unique<RealTime>(
224  *this, reactor.getGlobalSettings(), *eventDelay);
225 
226  powerSetting.attach(*settingObserver);
227 }
228 
230 {
231  powerSetting.detach(*settingObserver);
232  deleteMachine();
233 
234  assert(mapperIOCounter == 0);
235  assert(availableDevices.empty());
236  assert(extensions.empty());
237  assert(!machineConfig2);
238  assert(!getMachineConfig());
239 }
240 
241 void MSXMotherBoard::deleteMachine()
242 {
243  while (!extensions.empty()) {
244  try {
245  removeExtension(*extensions.back());
246  } catch (MSXException& e) {
247  std::cerr << "Internal error: failed to remove "
248  "extension while deleting a machine: "
249  << e.getMessage() << std::endl;
250  UNREACHABLE;
251  }
252  }
253 
254  machineConfig2.reset();
255  machineConfig = nullptr;
256 }
257 
259 {
260  assert(!getMachineConfig());
261  machineConfig = machineConfig_;
262 
263  // make sure the CPU gets instantiated from the main thread
264  assert(!msxCpu);
265  msxCpu = make_unique<MSXCPU>(*this);
266  msxCpuInterface = make_unique<MSXCPUInterface>(*this);
267 }
268 
270 {
271  const HardwareConfig* machine = getMachineConfig();
272  if (machine) {
273  return machine->getConfig().getChild("info").getChildData("type");
274  } else {
275  return "";
276  }
277 }
278 
280 {
281  const HardwareConfig* config = getMachineConfig();
282  assert(config);
283  return config->getConfig().getChild("devices").findChild("S1990") != nullptr;
284 }
285 
286 string MSXMotherBoard::loadMachine(const string& machine)
287 {
288  assert(machineName.empty());
289  assert(extensions.empty());
290  assert(!machineConfig2);
291  assert(!getMachineConfig());
292 
293  try {
294  machineConfig2 = HardwareConfig::createMachineConfig(*this, machine);
295  setMachineConfig(machineConfig2.get());
296  } catch (FileException& e) {
297  throw MSXException("Machine \"", machine, "\" not found: ",
298  e.getMessage());
299  } catch (MSXException& e) {
300  throw MSXException("Error in \"", machine, "\" machine: ",
301  e.getMessage());
302  }
303  try {
304  machineConfig->parseSlots();
305  machineConfig->createDevices();
306  } catch (MSXException& e) {
307  throw MSXException("Error in \"", machine, "\" machine: ",
308  e.getMessage());
309  }
310  if (powerSetting.getBoolean()) {
311  powerUp();
312  }
313  machineName = machine;
314  return machineName;
315 }
316 
318 {
319  unique_ptr<HardwareConfig> extension;
320  try {
321  extension = HardwareConfig::createExtensionConfig(*this, name, slotname);
322  } catch (FileException& e) {
323  throw MSXException(
324  "Extension \"", name, "\" not found: ", e.getMessage());
325  } catch (MSXException& e) {
326  throw MSXException(
327  "Error in \"", name, "\" extension: ", e.getMessage());
328  }
329  return insertExtension(name, std::move(extension));
330 }
331 
333  string_view name, unique_ptr<HardwareConfig> extension)
334 {
335  try {
336  extension->parseSlots();
337  extension->createDevices();
338  } catch (MSXException& e) {
339  throw MSXException(
340  "Error in \"", name, "\" extension: ", e.getMessage());
341  }
342  string result = extension->getName();
343  extensions.push_back(std::move(extension));
344  getMSXCliComm().update(CliComm::EXTENSION, result, "add");
345  return result;
346 }
347 
349 {
350  auto it = std::find_if(begin(extensions), end(extensions),
351  [&](Extensions::value_type& v) {
352  return v->getName() == extensionName; });
353  return (it != end(extensions)) ? it->get() : nullptr;
354 }
355 
357 {
358  extension.testRemove();
359  getMSXCliComm().update(CliComm::EXTENSION, extension.getName(), "remove");
360  auto it = rfind_if_unguarded(extensions,
361  [&](Extensions::value_type& v) { return v.get() == &extension; });
362  extensions.erase(it);
363 }
364 
366 {
367  return *msxCliComm;
368 }
369 
371 {
372  assert(getMachineConfig()); // needed for PluggableFactory::createAll()
373  if (!pluggingController) {
374  pluggingController = make_unique<PluggingController>(*this);
375  }
376  return *pluggingController;
377 }
378 
380 {
381  assert(getMachineConfig()); // because CPU needs to know if we're
382  // emulating turbor or not
383  return *msxCpu;
384 }
385 
387 {
388  assert(getMachineConfig());
389  return *msxCpuInterface;
390 }
391 
393 {
394  if (!panasonicMemory) {
395  panasonicMemory = make_unique<PanasonicMemory>(*this);
396  }
397  return *panasonicMemory;
398 }
399 
401 {
402  if (!deviceSwitch) {
404  }
405  return *deviceSwitch;
406 }
407 
409 {
410  if (!cassettePort) {
411  assert(getMachineConfig());
412  if (getMachineConfig()->getConfig().findChild("CassettePort")) {
413  cassettePort = make_unique<CassettePort>(*getMachineConfig());
414  } else {
415  cassettePort = make_unique<DummyCassettePort>();
416  }
417  }
418  return *cassettePort;
419 }
420 
422 {
423  assert(port < 2);
424  if (!joystickPort[0]) {
425  assert(getMachineConfig());
426  // some MSX machines only have 1 instead of 2 joystick ports
428  "JoystickPorts", "AB");
429  if ((ports != "AB") && (!ports.empty()) &&
430  (ports != "A") && (ports != "B")) {
431  throw ConfigException(
432  "Invalid JoystickPorts specification, "
433  "should be one of '', 'A', 'B' or 'AB'.");
434  }
436  if ((ports == "AB") || (ports == "A")) {
437  joystickPort[0] = make_unique<JoystickPort>(
438  ctrl, "joyporta", "MSX Joystick port A");
439  } else {
440  joystickPort[0] = make_unique<DummyJoystickPort>();
441  }
442  if ((ports == "AB") || (ports == "B")) {
443  joystickPort[1] = make_unique<JoystickPort>(
444  ctrl, "joyportb", "MSX Joystick port B");
445  } else {
446  joystickPort[1] = make_unique<DummyJoystickPort>();
447  }
448  joyPortDebuggable = make_unique<JoyPortDebuggable>(*this);
449  }
450  return *joystickPort[port];
451 }
452 
454 {
455  if (!renShaTurbo) {
456  assert(getMachineConfig());
457  renShaTurbo = make_unique<RenShaTurbo>(
458  *msxCommandController,
460  }
461  return *renShaTurbo;
462 }
463 
465 {
466  if (!ledStatus) {
467  getMSXCliComm(); // force init, to be on the safe side
468  ledStatus = make_unique<LedStatus>(
469  reactor.getRTScheduler(),
470  *msxCommandController,
471  *msxCliComm);
472  }
473  return *ledStatus;
474 }
475 
477 {
478  return *msxCommandController;
479 }
480 
482 {
483  return msxCommandController->getMachineInfoCommand();
484 }
485 
487 {
488  return scheduler->getCurrentTime();
489 }
490 
492 {
493  if (!powered) {
494  return false;
495  }
496  assert(getMachineConfig()); // otherwise powered cannot be true
497 
498  getCPU().execute(false);
499  return true;
500 }
501 
502 void MSXMotherBoard::fastForward(EmuTime::param time, bool fast)
503 {
504  assert(powered);
505  assert(getMachineConfig());
506 
507  if (time <= getCurrentTime()) return;
508 
509  ScopedAssign<bool> sa(fastForwarding, fast);
510  realTime->disable();
511  msxMixer->mute();
512  fastForwardHelper->setTarget(time);
513  while (time > getCurrentTime()) {
514  // note: this can run (slightly) past the requested time
515  getCPU().execute(true); // fast-forward mode
516  }
517  realTime->enable();
518  msxMixer->unmute();
519 }
520 
522 {
523  if (getMachineConfig()) {
524  getCPU().setPaused(true);
525  }
526  msxMixer->mute();
527 }
528 
530 {
531  if (getMachineConfig()) {
532  getCPU().setPaused(false);
533  }
534  msxMixer->unmute();
535 }
536 
538 {
539  availableDevices.push_back(&device);
540 }
541 
543 {
544  move_pop_back(availableDevices, rfind_unguarded(availableDevices, &device));
545 }
546 
548 {
549  if (!powered) return;
550  assert(getMachineConfig());
551 
552  EmuTime::param time = getCurrentTime();
554  for (auto& d : availableDevices) {
555  d->reset(time);
556  }
557  getCPU().doReset(time);
558  // let everyone know we're booting, note that the fact that this is
559  // done after the reset call to the devices is arbitrary here
561  std::make_shared<SimpleEvent>(OPENMSX_BOOT_EVENT));
562 }
563 
565 {
566  byte result = 0xff;
567  for (auto& d : availableDevices) {
568  result &= d->readIRQVector();
569  }
570  return result;
571 }
572 
574 {
575  if (powered) return;
576  if (!getMachineConfig()) return;
577 
578  powered = true;
579  // TODO: If our "powered" field is always equal to the power setting,
580  // there is not really a point in keeping it.
581  // TODO: assert disabled see note in Reactor::run() where this method
582  // is called
583  //assert(powerSetting.getBoolean() == powered);
584  powerSetting.setBoolean(true);
585  // TODO: We could make the power LED a device, so we don't have to handle
586  // it separately here.
588 
589  EmuTime::param time = getCurrentTime();
591  for (auto& d : availableDevices) {
592  d->powerUp(time);
593  }
594  getCPU().doReset(time);
595  msxMixer->unmute();
596  // let everyone know we're booting, note that the fact that this is
597  // done after the reset call to the devices is arbitrary here
599  std::make_shared<SimpleEvent>(OPENMSX_BOOT_EVENT));
600 }
601 
602 void MSXMotherBoard::powerDown()
603 {
604  if (!powered) return;
605 
606  powered = false;
607  // TODO: This assertion fails in 1 case: when quitting with a running MSX.
608  // How do we want the Reactor to shutdown: immediately or after
609  // handling all pending commands/events/updates?
610  //assert(powerSetting.getBoolean() == powered);
611  powerSetting.setBoolean(false);
613 
614  msxMixer->mute();
615 
616  EmuTime::param time = getCurrentTime();
617  for (auto& d : availableDevices) {
618  d->powerDown(time);
619  }
620 }
621 
622 void MSXMotherBoard::activate(bool active_)
623 {
624  active = active_;
625  auto event = std::make_shared<SimpleEvent>(
627  msxEventDistributor->distributeEvent(event, scheduler->getCurrentTime());
628  if (active) {
629  realTime->resync();
630  }
631 }
632 
634 {
635  if (getMachineConfig()) {
637  }
638 }
639 
641 {
643 }
644 
646 {
647  for (auto& d : availableDevices) {
648  if (d->getName() == name) {
649  return d;
650  }
651  }
652  return 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 (find(begin(s), end(s), userName) != end(s));
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(array_ref<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.setString(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  for (auto& e : motherBoard.getExtensions()) {
815  result.addListElement(e->getName());
816  }
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.setString(motherBoard.loadExtension(
847  tokens[1].getString(), slotname));
848  } catch (MSXException& e) {
849  throw CommandException(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  vector<string_view> names;
903  for (auto& e : motherBoard.getExtensions()) {
904  names.emplace_back(e->getName());
905  }
906  completeString(tokens, names);
907  }
908 }
909 
910 
911 // MachineNameInfo
912 
914  : InfoTopic(motherBoard_.getMachineInfoCommand(), "config_name")
915  , motherBoard(motherBoard_)
916 {
917 }
918 
920  TclObject& result) const
921 {
922  result.setString(motherBoard.getMachineName());
923 }
924 
925 string MachineNameInfo::help(const vector<string>& /*tokens*/) const
926 {
927  return "Returns the configuration name for this machine.";
928 }
929 
930 // MachineTypeInfo
931 
933  : InfoTopic(motherBoard_.getMachineInfoCommand(), "type")
934  , motherBoard(motherBoard_)
935 {
936 }
937 
939  TclObject& result) const
940 {
941  result.setString(motherBoard.getMachineType());
942 }
943 
944 string MachineTypeInfo::help(const vector<string>& /*tokens*/) const
945 {
946  return "Returns the machine type for this machine.";
947 }
948 
949 
950 // DeviceInfo
951 
953  : InfoTopic(motherBoard_.getMachineInfoCommand(), "device")
954  , motherBoard(motherBoard_)
955 {
956 }
957 
959 {
960  switch (tokens.size()) {
961  case 2:
962  for (auto& d : motherBoard.availableDevices) {
963  result.addListElement(d->getName());
964  }
965  break;
966  case 3: {
967  string_view deviceName = tokens[2].getString();
968  MSXDevice* device = motherBoard.findDevice(deviceName);
969  if (!device) {
970  throw CommandException("No such device: ", deviceName);
971  }
972  device->getDeviceInfo(result);
973  break;
974  }
975  default:
976  throw SyntaxError();
977  }
978 }
979 
980 string DeviceInfo::help(const vector<string>& /*tokens*/) const
981 {
982  return "Without any arguments, returns the list of used device names.\n"
983  "With a device name as argument, returns the type (and for some "
984  "devices the subtype) of the given device.";
985 }
986 
987 void DeviceInfo::tabCompletion(vector<string>& tokens) const
988 {
989  if (tokens.size() == 3) {
990  vector<string> names;
991  for (auto& d : motherBoard.availableDevices) {
992  names.push_back(d->getName());
993  }
994  completeString(tokens, names);
995  }
996 }
997 
998 
999 // FastForwardHelper
1000 
1002  : Schedulable(motherBoard_.getScheduler())
1003  , motherBoard(motherBoard_)
1004 {
1005 }
1006 
1007 void FastForwardHelper::setTarget(EmuTime::param targetTime)
1008 {
1009  setSyncPoint(targetTime);
1010 }
1011 
1012 void FastForwardHelper::executeUntil(EmuTime::param /*time*/)
1013 {
1014  motherBoard.exitCPULoopSync();
1015 }
1016 
1017 
1018 // class JoyPortDebuggable
1019 
1021  : SimpleDebuggable(motherBoard_, "joystickports", "MSX Joystick Ports", 2)
1022 {
1023 }
1024 
1025 byte JoyPortDebuggable::read(unsigned address, EmuTime::param time)
1026 {
1027  return getMotherBoard().getJoystickPort(address).read(time);
1028 }
1029 
1030 void JoyPortDebuggable::write(unsigned /*address*/, byte /*value*/)
1031 {
1032  // ignore
1033 }
1034 
1035 
1036 // class SettingObserver
1037 
1039  : motherBoard(motherBoard_)
1040 {
1041 }
1042 
1043 void SettingObserver::update(const Setting& setting)
1044 {
1045  if (&setting == &motherBoard.powerSetting) {
1046  if (motherBoard.powerSetting.getBoolean()) {
1047  motherBoard.powerUp();
1048  } else {
1049  motherBoard.powerDown();
1050  }
1051  } else {
1052  UNREACHABLE;
1053  }
1054 }
1055 
1056 
1057 // serialize
1058 // version 1: initial version
1059 // version 2: added reRecordCount
1060 // version 3: removed reRecordCount (moved to ReverseManager)
1061 // version 4: moved joystickportA/B from MSXPSG to here
1062 template<typename Archive>
1063 void MSXMotherBoard::serialize(Archive& ar, unsigned version)
1064 {
1065  // don't serialize:
1066  // machineID, userNames, availableDevices, addRemoveUpdate,
1067  // sharedStuffMap, msxCliComm, msxEventDistributor,
1068  // msxCommandController, slotManager, eventDelay,
1069  // debugger, msxMixer, panasonicMemory, renShaTurbo,
1070  // ledStatus
1071 
1072  // Scheduler must come early so that devices can query current time
1073  ar.serialize("scheduler", *scheduler);
1074  // MSXMixer has already set syncpoints, those are invalid now
1075  // the following call will fix this
1076  if (ar.isLoader()) {
1077  msxMixer->reInit();
1078  }
1079 
1080  ar.serialize("name", machineName);
1081  ar.serializeWithID("config", machineConfig2, std::ref(*this));
1082  assert(getMachineConfig() == machineConfig2.get());
1083  ar.serializeWithID("extensions", extensions, std::ref(*this));
1084 
1085  if (mapperIO) ar.serialize("mapperIO", *mapperIO);
1086 
1087  MSXDeviceSwitch& devSwitch = getDeviceSwitch();
1088  if (devSwitch.hasRegisteredDevices()) {
1089  ar.serialize("deviceSwitch", devSwitch);
1090  }
1091 
1092  if (getMachineConfig()) {
1093  ar.serialize("cpu", getCPU());
1094  }
1095  ar.serialize("cpuInterface", getCPUInterface());
1096 
1097  if (auto port = dynamic_cast<CassettePort*>(&getCassettePort())) {
1098  ar.serialize("cassetteport", *port);
1099  }
1100  if (ar.versionAtLeast(version, 4)) {
1101  if (auto port = dynamic_cast<JoystickPort*>(
1102  joystickPort[0].get())) {
1103  ar.serialize("joystickportA", *port);
1104  }
1105  if (auto port = dynamic_cast<JoystickPort*>(
1106  joystickPort[1].get())) {
1107  ar.serialize("joystickportB", *port);
1108  }
1109  }
1110 
1111  if (ar.isLoader()) {
1112  powered = true; // must come before changing power setting
1113  powerSetting.setBoolean(true);
1114  getLedStatus().setLed(LedStatus::POWER, true);
1115  msxMixer->unmute();
1116  }
1117 
1118  if (version == 2) {
1119  assert(ar.isLoader());
1120  unsigned reRecordCount = 0; // silence warning
1121  ar.serialize("reRecordCount", reRecordCount);
1122  getReverseManager().setReRecordCount(reRecordCount);
1123  }
1124 }
1126 
1127 } // 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
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:292
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)
const std::string & getMessage() const
Definition: MSXException.hh:23
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
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
FastForwardHelper(MSXMotherBoard &msxMotherBoardImpl)
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
std::unique_ptr< T > make_unique()
Definition: memory.hh:27
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