OXYGENHUB
◆ main openmsx / src / MSXMotherBoard.cc
40.6 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "MSXMotherBoard.hh"
2
3
#include "BooleanSetting.hh"
4
#include "CartridgeSlotManager.hh"
5
#include "CassettePort.hh"
6
#include "Command.hh"
7
#include "CommandException.hh"
8
#include "ConfigException.hh"
9
#include "Debugger.hh"
10
#include "DeviceFactory.hh"
11
#include "EventDelay.hh"
12
#include "EventDistributor.hh"
13
#include "FileException.hh"
14
#include "FileOperations.hh"
15
#include "GlobalCliComm.hh"
16
#include "GlobalSettings.hh"
17
#include "HardwareConfig.hh"
18
#include "InfoTopic.hh"
19
#include "JoystickPort.hh"
20
#include "LedStatus.hh"
21
#include "MSXCPU.hh"
22
#include "MSXCPUInterface.hh"
23
#include "MSXCliComm.hh"
24
#include "MSXCommandController.hh"
25
#include "MSXDevice.hh"
26
#include "MSXDeviceSwitch.hh"
27
#include "MSXEventDistributor.hh"
28
#include "MSXMapperIO.hh"
29
#include "MSXMixer.hh"
30
#include "Observer.hh"
31
#include "PanasonicMemory.hh"
32
#include "Pluggable.hh"
33
#include "PluggingController.hh"
34
#include "Reactor.hh"
35
#include "RealTime.hh"
36
#include "RenShaTurbo.hh"
37
#include "ReverseManager.hh"
38
#include "Schedulable.hh"
39
#include "Scheduler.hh"
40
#include "SimpleDebuggable.hh"
41
#include "StateChangeDistributor.hh"
42
#include "TclObject.hh"
43
#include "XMLElement.hh"
44
#include "serialize.hh"
45
#include "serialize_stl.hh"
46
47
#include "ScopedAssign.hh"
48
#include "one_of.hh"
49
#include "stl.hh"
50
#include "unreachable.hh"
51
52
#include <algorithm>
53
#include <cassert>
54
#include <functional>
55
#include <iostream>
56
#include <memory>
57
#include <ranges>
58
59
namespace openmsx {
60
61
class AddRemoveUpdate
62
{
63
public:
64
	explicit AddRemoveUpdate(MSXMotherBoard& motherBoard);
65
	AddRemoveUpdate(const AddRemoveUpdate&) = delete;
66
	AddRemoveUpdate(AddRemoveUpdate&&) = delete;
67
	AddRemoveUpdate& operator=(const AddRemoveUpdate&) = delete;
68
	AddRemoveUpdate& operator=(AddRemoveUpdate&&) = delete;
69
	~AddRemoveUpdate();
70
private:
71
	MSXMotherBoard& motherBoard;
72
};
73
74
class ResetCmd final : public RecordedCommand
75
{
76
public:
77
	explicit ResetCmd(MSXMotherBoard& motherBoard);
78
	void execute(std::span<const TclObject> tokens, TclObject& result,
79
	             EmuTime time) override;
80
	[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
81
private:
82
	MSXMotherBoard& motherBoard;
83
};
84
85
class LoadMachineCmd final : public Command
86
{
87
public:
88
	explicit LoadMachineCmd(MSXMotherBoard& motherBoard);
89
	void execute(std::span<const TclObject> tokens, TclObject& result) override;
90
	[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
91
	void tabCompletion(std::vector<std::string>& tokens) const override;
92
private:
93
	MSXMotherBoard& motherBoard;
94
};
95
96
class ListExtCmd final : public Command
97
{
98
public:
99
	explicit ListExtCmd(MSXMotherBoard& motherBoard);
100
	void execute(std::span<const TclObject> tokens, TclObject& result) override;
101
	[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
102
private:
103
	MSXMotherBoard& motherBoard;
104
};
105
106
class RemoveExtCmd final : public RecordedCommand
107
{
108
public:
109
	explicit RemoveExtCmd(MSXMotherBoard& motherBoard);
110
	void execute(std::span<const TclObject> tokens, TclObject& result,
111
	             EmuTime time) override;
112
	[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
113
	void tabCompletion(std::vector<std::string>& tokens) const override;
114
private:
115
	MSXMotherBoard& motherBoard;
116
};
117
118
class StoreSetupCmd final : public Command
119
{
120
public:
121
	explicit StoreSetupCmd(MSXMotherBoard& motherBoard);
122
	void execute(std::span<const TclObject> tokens, TclObject& result) override;
123
	[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
124
	void tabCompletion(std::vector<std::string>& tokens) const override;
125
private:
126
	MSXMotherBoard& motherBoard;
127
};
128
129
class MachineNameInfo final : public InfoTopic
130
{
131
public:
132
	explicit MachineNameInfo(MSXMotherBoard& motherBoard);
133
	void execute(std::span<const TclObject> tokens,
134
	             TclObject& result) const override;
135
	[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
136
private:
137
	MSXMotherBoard& motherBoard;
138
};
139
140
class MachineTypeInfo final : public InfoTopic
141
{
142
public:
143
	explicit MachineTypeInfo(MSXMotherBoard& motherBoard);
144
	void execute(std::span<const TclObject> tokens,
145
	             TclObject& result) const override;
146
	[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
147
private:
148
	MSXMotherBoard& motherBoard;
149
};
150
151
class MachineExtensionInfo final : public InfoTopic
152
{
153
public:
154
	explicit MachineExtensionInfo(MSXMotherBoard& motherBoard);
155
	void execute(std::span<const TclObject> tokens,
156
	             TclObject& result) const override;
157
	[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
158
	void tabCompletion(std::vector<std::string>& tokens) const override;
159
private:
160
	MSXMotherBoard& motherBoard;
161
};
162
163
class MachineMediaInfo final : public InfoTopic
164
{
165
public:
166
	explicit MachineMediaInfo(MSXMotherBoard& motherBoard);
167
	void execute(std::span<const TclObject> tokens,
168
	             TclObject& result) const override;
169
	[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
170
	void tabCompletion(std::vector<std::string>& tokens) const override;
171
private:
172
	MSXMotherBoard& motherBoard;
173
};
174
175
class DeviceInfo final : public InfoTopic
176
{
177
public:
178
	explicit DeviceInfo(MSXMotherBoard& motherBoard);
179
	void execute(std::span<const TclObject> tokens,
180
	             TclObject& result) const override;
181
	[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
182
	void tabCompletion(std::vector<std::string>& tokens) const override;
183
private:
184
	MSXMotherBoard& motherBoard;
185
};
186
187
class FastForwardHelper final : private Schedulable
188
{
189
public:
190
	explicit FastForwardHelper(MSXMotherBoard& motherBoard);
191
	void setTarget(EmuTime targetTime);
192
private:
193
	void executeUntil(EmuTime time) override;
194
	MSXMotherBoard& motherBoard;
195
};
196
197
class JoyPortDebuggable final : public SimpleDebuggable
198
{
199
public:
200
	explicit JoyPortDebuggable(MSXMotherBoard& motherBoard);
201
	[[nodiscard]] uint8_t read(unsigned address, EmuTime time) override;
202
	void write(unsigned address, uint8_t value) override;
203
};
204
205
class SettingObserver final : public Observer<Setting>
206
{
207
public:
208
	explicit SettingObserver(MSXMotherBoard& motherBoard);
209
	void update(const Setting& setting) noexcept override;
210
private:
211
	MSXMotherBoard& motherBoard;
212
};
213
214
215
static unsigned machineIDCounter = 0;
216
217
MSXMotherBoard::MSXMotherBoard(Reactor& reactor_)
218
	: reactor(reactor_)
219
	, machineID(strCat("machine", ++machineIDCounter))
220
	, msxCliComm(std::make_unique<MSXCliComm>(*this, reactor.getGlobalCliComm()))
221
	, msxEventDistributor(std::make_unique<MSXEventDistributor>())
222
	, stateChangeDistributor(std::make_unique<StateChangeDistributor>())
223
	, msxCommandController(std::make_unique<MSXCommandController>(
224
		reactor.getGlobalCommandController(), reactor,
225
		*this, *msxEventDistributor, machineID))
226
	, scheduler(std::make_unique<Scheduler>())
227
	, msxMixer(std::make_unique<MSXMixer>(
228
		reactor.getMixer(), *this,
229
		reactor.getGlobalSettings()))
230
	, videoSourceSetting(*msxCommandController)
231
	, suppressMessagesSetting(*msxCommandController, "suppressmessages",
232
		"Suppress info, warning and error messages for this machine. "
233
		"Intended use is for scripts that create temporary machines "
234
		"of which you don't want to see warning messages about blank "
235
		"SRAM content or PSG port directions for instance.",
236
		false, Setting::Save::NO)
237
	, fastForwardHelper(std::make_unique<FastForwardHelper>(*this))
238
	, settingObserver(std::make_unique<SettingObserver>(*this))
239
	, powerSetting(reactor.getGlobalSettings().getPowerSetting())
240
{
241
	slotManager = std::make_unique<CartridgeSlotManager>(*this);
242
	reverseManager = std::make_unique<ReverseManager>(*this);
243
	resetCommand = std::make_unique<ResetCmd>(*this);
244
	loadMachineCommand = std::make_unique<LoadMachineCmd>(*this);
245
	listExtCommand = std::make_unique<ListExtCmd>(*this);
246
	extCommand = std::make_unique<ExtCmd>(*this, "ext");
247
	removeExtCommand = std::make_unique<RemoveExtCmd>(*this);
248
	storeSetupCommand = std::make_unique<StoreSetupCmd>(*this);
249
	machineNameInfo = std::make_unique<MachineNameInfo>(*this);
250
	machineTypeInfo = std::make_unique<MachineTypeInfo>(*this);
251
	machineExtensionInfo = std::make_unique<MachineExtensionInfo>(*this);
252
	machineMediaInfo = std::make_unique<MachineMediaInfo>(*this);
253
	deviceInfo = std::make_unique<DeviceInfo>(*this);
254
	debugger = std::make_unique<Debugger>(*this);
255
256
	// Do this before machine-specific settings are created, otherwise
257
	// a setting-info CliComm message is send with a machine id that hasn't
258
	// been announced yet over CliComm.
259
	addRemoveUpdate = std::make_unique<AddRemoveUpdate>(*this);
260
261
	// TODO: Initialization of this field cannot be done much earlier because
262
	//       EventDelay creates a setting, calling getMSXCliComm()
263
	//       on MSXMotherBoard, so "pimpl" has to be set up already.
264
	eventDelay = std::make_unique<EventDelay>(
265
		*scheduler, *msxCommandController,
266
		reactor.getEventDistributor(), *msxEventDistributor,
267
		*reverseManager);
268
	realTime = std::make_unique<RealTime>(
269
		*this, reactor.getGlobalSettings(), *eventDelay);
270
271
	powerSetting.attach(*settingObserver);
272
	suppressMessagesSetting.attach(*settingObserver);
273
}
274
275
MSXMotherBoard::~MSXMotherBoard()
276
{
277
	suppressMessagesSetting.detach(*settingObserver);
278
	powerSetting.detach(*settingObserver);
279
	deleteMachine();
280
281
	assert(mapperIOCounter == 0);
282
	assert(availableDevices.empty());
283
	assert(extensions.empty());
284
	assert(!machineConfig2);
285
	assert(!getMachineConfig());
286
}
287
288
void MSXMotherBoard::deleteMachine()
289
{
290
	while (!extensions.empty()) {
291
		try {
292
			removeExtension(*extensions.back());
293
		} catch (MSXException& e) {
294
			std::cerr << "Internal error: failed to remove "
295
			             "extension while deleting a machine: "
296
			          << e.getMessage() << '\n';
297
			assert(false);
298
		}
299
	}
300
301
	machineConfig2.reset();
302
	machineConfig = nullptr;
303
}
304
305
void MSXMotherBoard::setMachineConfig(HardwareConfig* machineConfig_)
306
{
307
	assert(!getMachineConfig());
308
	machineConfig = machineConfig_;
309
310
	// make sure the CPU gets instantiated from the main thread
311
	assert(!msxCpu);
312
	msxCpu = std::make_unique<MSXCPU>(*this);
313
	msxCpuInterface = std::make_unique<MSXCPUInterface>(*this);
314
}
315
316
std::string_view MSXMotherBoard::getMachineType() const
317
{
318
	if (const HardwareConfig* machine = getMachineConfig()) {
319
		if (const auto* info = machine->getConfig().findChild("info")) {
320
			if (const auto* type = info->findChild("type")) {
321
				return type->getData();
322
			}
323
		}
324
	}
325
	return "";
326
}
327
328
bool MSXMotherBoard::isTurboR() const
329
{
330
	const HardwareConfig* config = getMachineConfig();
331
	assert(config);
332
	return config->getConfig().getChild("devices").findChild("S1990") != nullptr;
333
}
334
335
bool MSXMotherBoard::hasToshibaEngine() const
336
{
337
	const HardwareConfig* config = getMachineConfig();
338
	assert(config);
339
	const XMLElement& devices = config->getConfig().getChild("devices");
340
	return devices.findChild("T7775") != nullptr ||
341
	       devices.findChild("T7937") != nullptr ||
342
		   devices.findChild("T9763") != nullptr ||
343
		   devices.findChild("T9769") != nullptr;
344
}
345
346
std::string MSXMotherBoard::loadMachine(const std::string& machine)
347
{
348
	assert(machineName.empty());
349
	assert(extensions.empty());
350
	assert(!machineConfig2);
351
	assert(!getMachineConfig());
352
353
	try {
354
		machineConfig2 = HardwareConfig::createMachineConfig(*this, machine);
355
		setMachineConfig(machineConfig2.get());
356
	} catch (FileException& e) {
357
		throw MSXException("Machine \"", machine, "\" not found: ",
358
		                   e.getMessage());
359
	} catch (MSXException& e) {
360
		throw MSXException("Error in \"", machine, "\" machine: ",
361
		                   e.getMessage());
362
	}
363
	try {
364
		machineConfig->parseSlots();
365
		machineConfig->createDevices();
366
	} catch (MSXException& e) {
367
		throw MSXException("Error in \"", machine, "\" machine: ",
368
		                   e.getMessage());
369
	}
370
	if (powerSetting.getBoolean()) {
371
		powerUp();
372
	}
373
	machineName = machine;
374
	return machineName;
375
}
376
377
void MSXMotherBoard::storeAsSetup(const std::string& filename, SetupDepth depth)
378
{
379
	// level 0: don't do anything. Added as convenience.
380
	if (depth == SetupDepth::NONE) return;
381
382
	XmlOutputArchive out(filename);
383
384
	if (depth == SetupDepth::COMPLETE_STATE) {
385
		// level 5: just save state to given file
386
		out.serialize("machine", this);
387
		out.close();
388
		return;
389
	}
390
391
	// level 1: create new board based on current board of this machine
392
	auto newBoard = reactor.createEmptyMotherBoard();
393
	newBoard->loadMachine(machineName);
394
	auto newTime = newBoard->getCurrentTime();
395
396
	if (depth >= SetupDepth::EXTENSIONS) {
397
		// level 2: add the extensions of the current board to the new board
398
399
		// suppress any messages from this temporary board
400
		newBoard->getMSXCliComm().setSuppressMessages(true);
401
402
		for (auto& extension: extensions) {
403
			if (extension->getType() == HardwareConfig::Type::EXTENSION) {
404
				const auto& configName = extension->getConfigName();
405
				auto slot = slotManager->findSlotWith(*extension);
406
				const std::string slotSpec = slot ? std::string(1, char('a' + *slot)) : "any";
407
				// TODO: a bit weird that we need to convert the slot
408
				// spec into a string and then parse it again deep down
409
				// in loadExtension...
410
				auto extConfig = newBoard->loadExtension(configName, slotSpec);
411
				newBoard->insertExtension(configName, std::move(extConfig));
412
			}
413
		}
414
	}
415
416
	if (depth >= SetupDepth::CONNECTORS) {
417
		// level 3: add the connectors/pluggables of the current board to the new board
418
		auto& newBoardPluggingController = newBoard->getPluggingController();
419
		for (const auto* connector: getPluggingController().getConnectors()) {
420
			const auto& plugged = connector->getPlugged();
421
			const auto& pluggedName = plugged.getName();
422
			if (!pluggedName.empty()) {
423
				const auto& connectorName = connector->getName();
424
				if (auto* newBoardConnector = newBoardPluggingController.findConnector(connectorName)) {
425
					if (auto* newBoardPluggable = newBoardPluggingController.findPluggable(pluggedName)) {
426
						newBoardConnector->plug(*newBoardPluggable, newTime);
427
					}
428
				}
429
			}
430
		}
431
	}
432
433
	if (depth >= SetupDepth::MEDIA) {
434
		// level 4: add the inserted media of the current board to the new board
435
		for (const auto& oldMedia : getMediaProviders()) {
436
			if (auto* newMediaProvider = newBoard->findMediaProvider(oldMedia.name)) {
437
				TclObject info;
438
				oldMedia.provider->getMediaInfo(info);
439
				newMediaProvider->setMedia(info, newTime);
440
			}
441
		}
442
	}
443
444
	out.serialize("machine", newBoard);
445
	out.close();
446
}
447
448
std::unique_ptr<HardwareConfig> MSXMotherBoard::loadExtension(std::string_view name, std::string_view slotName)
449
{
450
	try {
451
		return HardwareConfig::createExtensionConfig(
452
			*this, std::string(name), slotName);
453
	} catch (FileException& e) {
454
		throw MSXException(
455
			"Extension \"", name, "\" not found: ", e.getMessage());
456
	} catch (MSXException& e) {
457
		throw MSXException(
458
			"Error in \"", name, "\" extension: ", e.getMessage());
459
	}
460
}
461
462
std::string MSXMotherBoard::insertExtension(
463
	std::string_view name, std::unique_ptr<HardwareConfig> extension)
464
{
465
	try {
466
		extension->parseSlots();
467
		extension->createDevices();
468
	} catch (MSXException& e) {
469
		throw MSXException(
470
			"Error in \"", name, "\" extension: ", e.getMessage());
471
	}
472
	std::string result = extension->getName();
473
	extensions.push_back(std::move(extension));
474
	getMSXCliComm().update(CliComm::UpdateType::EXTENSION, result, "add");
475
	return result;
476
}
477
478
HardwareConfig* MSXMotherBoard::findExtension(std::string_view extensionName)
479
{
480
	auto it = std::ranges::find(extensions, extensionName, &HardwareConfig::getName);
481
	return (it != end(extensions)) ? it->get() : nullptr;
482
}
483
484
void MSXMotherBoard::removeExtension(const HardwareConfig& extension)
485
{
486
	extension.testRemove();
487
	getMSXCliComm().update(CliComm::UpdateType::EXTENSION, extension.getName(), "remove");
488
	auto it = rfind_unguarded(extensions, &extension,
489
	                          [](auto& e) { return e.get(); });
490
	extensions.erase(it);
491
}
492
493
MSXCliComm& MSXMotherBoard::getMSXCliComm()
494
{
495
	return *msxCliComm;
496
}
497
498
PluggingController& MSXMotherBoard::getPluggingController()
499
{
500
	assert(getMachineConfig()); // needed for PluggableFactory::createAll()
501
	if (!pluggingController) {
502
		pluggingController = std::make_unique<PluggingController>(*this);
503
	}
504
	return *pluggingController;
505
}
506
507
MSXCPU& MSXMotherBoard::getCPU()
508
{
509
	assert(getMachineConfig()); // because CPU needs to know if we're
510
	                            // emulating turbor or not
511
	return *msxCpu;
512
}
513
514
MSXCPUInterface& MSXMotherBoard::getCPUInterface()
515
{
516
	assert(getMachineConfig());
517
	return *msxCpuInterface;
518
}
519
520
PanasonicMemory& MSXMotherBoard::getPanasonicMemory()
521
{
522
	if (!panasonicMemory) {
523
		panasonicMemory = std::make_unique<PanasonicMemory>(*this);
524
	}
525
	return *panasonicMemory;
526
}
527
528
MSXDeviceSwitch& MSXMotherBoard::getDeviceSwitch()
529
{
530
	if (!deviceSwitch) {
531
		deviceSwitch = DeviceFactory::createDeviceSwitch(*getMachineConfig());
532
	}
533
	return *deviceSwitch;
534
}
535
536
CassettePortInterface& MSXMotherBoard::getCassettePort()
537
{
538
	if (!cassettePort) {
539
		assert(getMachineConfig());
540
		if (getMachineConfig()->getConfig().findChild("CassettePort")) {
541
			cassettePort = std::make_unique<CassettePort>(*getMachineConfig());
542
		} else {
543
			cassettePort = std::make_unique<DummyCassettePort>(*this);
544
		}
545
	}
546
	return *cassettePort;
547
}
548
549
JoystickPortIf& MSXMotherBoard::getJoystickPort(unsigned port)
550
{
551
	assert(port < 2);
552
	if (!joystickPort[0]) {
553
		assert(getMachineConfig());
554
		// some MSX machines only have 1 instead of 2 joystick ports
555
		std::string_view ports = getMachineConfig()->getConfig().getChildData(
556
			"JoystickPorts", "AB");
557
		if (ports != one_of("AB", "", "A", "B")) {
558
			throw ConfigException(
559
				"Invalid JoystickPorts specification, "
560
				"should be one of '', 'A', 'B' or 'AB'.");
561
		}
562
		PluggingController& ctrl = getPluggingController();
563
		if (ports == one_of("AB", "A")) {
564
			joystickPort[0] = std::make_unique<JoystickPort>(
565
				ctrl, "joyporta", "MSX Joystick port A");
566
		} else {
567
			joystickPort[0] = std::make_unique<DummyJoystickPort>();
568
		}
569
		if (ports == one_of("AB", "B")) {
570
			joystickPort[1] = std::make_unique<JoystickPort>(
571
				ctrl, "joyportb", "MSX Joystick port B");
572
		} else {
573
			joystickPort[1] = std::make_unique<DummyJoystickPort>();
574
		}
575
		joyPortDebuggable = std::make_unique<JoyPortDebuggable>(*this);
576
	}
577
	return *joystickPort[port];
578
}
579
580
RenShaTurbo& MSXMotherBoard::getRenShaTurbo()
581
{
582
	if (!renShaTurbo) {
583
		assert(getMachineConfig());
584
		renShaTurbo = std::make_unique<RenShaTurbo>(
585
			*this,
586
			getMachineConfig()->getConfig());
587
	}
588
	return *renShaTurbo;
589
}
590
591
LedStatus& MSXMotherBoard::getLedStatus()
592
{
593
	if (!ledStatus) {
594
		(void)getMSXCliComm(); // force init, to be on the safe side
595
		ledStatus = std::make_unique<LedStatus>(
596
			reactor.getRTScheduler(),
597
			*msxCommandController,
598
			*msxCliComm);
599
	}
600
	return *ledStatus;
601
}
602
603
CommandController& MSXMotherBoard::getCommandController()
604
{
605
	return *msxCommandController;
606
}
607
608
InfoCommand& MSXMotherBoard::getMachineInfoCommand()
609
{
610
	return msxCommandController->getMachineInfoCommand();
611
}
612
613
EmuTime MSXMotherBoard::getCurrentTime() const
614
{
615
	return scheduler->getCurrentTime();
616
}
617
618
bool MSXMotherBoard::execute()
619
{
620
	if (!powered) {
621
		return false;
622
	}
623
	assert(getMachineConfig()); // otherwise powered cannot be true
624
625
	getCPU().execute(false);
626
	return true;
627
}
628
629
void MSXMotherBoard::fastForward(EmuTime time, bool fast)
630
{
631
	assert(powered);
632
	assert(getMachineConfig());
633
634
	if (time <= getCurrentTime()) return;
635
636
	ScopedAssign sa(fastForwarding, fast);
637
	realTime->disable();
638
	msxMixer->mute();
639
	fastForwardHelper->setTarget(time);
640
	while (time > getCurrentTime()) {
641
		// note: this can run (slightly) past the requested time
642
		getCPU().execute(true); // fast-forward mode
643
	}
644
	realTime->enable();
645
	msxMixer->unmute();
646
}
647
648
void MSXMotherBoard::pause()
649
{
650
	if (getMachineConfig()) {
651
		getCPU().setPaused(true);
652
	}
653
	msxMixer->mute();
654
}
655
656
void MSXMotherBoard::unpause()
657
{
658
	if (getMachineConfig()) {
659
		getCPU().setPaused(false);
660
	}
661
	msxMixer->unmute();
662
}
663
664
void MSXMotherBoard::addDevice(MSXDevice& device)
665
{
666
	availableDevices.push_back(&device);
667
}
668
669
void MSXMotherBoard::removeDevice(MSXDevice& device)
670
{
671
	move_pop_back(availableDevices, rfind_unguarded(availableDevices, &device));
672
}
673
674
void MSXMotherBoard::doReset()
675
{
676
	if (!powered) return;
677
	assert(getMachineConfig());
678
679
	EmuTime time = getCurrentTime();
680
	getCPUInterface().reset();
681
	for (auto& d : availableDevices) {
682
		d->reset(time);
683
	}
684
	getCPU().doReset(time);
685
	// let everyone know we're booting, note that the fact that this is
686
	// done after the reset call to the devices is arbitrary here
687
	reactor.getEventDistributor().distributeEvent(BootEvent());
688
}
689
690
uint8_t MSXMotherBoard::readIRQVector() const
691
{
692
	uint8_t result = 0xff;
693
	for (auto& d : availableDevices) {
694
		result &= d->readIRQVector();
695
	}
696
	return result;
697
}
698
699
void MSXMotherBoard::powerUp()
700
{
701
	if (powered) return;
702
	if (!getMachineConfig()) return;
703
704
	powered = true;
705
	// TODO: If our "powered" field is always equal to the power setting,
706
	//       there is not really a point in keeping it.
707
	// TODO: assert disabled see note in Reactor::run() where this method
708
	//       is called
709
	//assert(powerSetting.getBoolean() == powered);
710
	powerSetting.setBoolean(true);
711
	// TODO: We could make the power LED a device, so we don't have to handle
712
	//       it separately here.
713
	getLedStatus().setLed(LedStatus::POWER, true);
714
715
	EmuTime time = getCurrentTime();
716
	getCPUInterface().reset();
717
	for (auto& d : availableDevices) {
718
		d->powerUp(time);
719
	}
720
	getCPU().doReset(time);
721
	msxMixer->unmute();
722
	// let everyone know we're booting, note that the fact that this is
723
	// done after the reset call to the devices is arbitrary here
724
	reactor.getEventDistributor().distributeEvent(BootEvent());
725
}
726
727
void MSXMotherBoard::powerDown()
728
{
729
	if (!powered) return;
730
731
	powered = false;
732
	// TODO: This assertion fails in 1 case: when quitting with a running MSX.
733
	//       How do we want the Reactor to shutdown: immediately or after
734
	//       handling all pending commands/events/updates?
735
	//assert(powerSetting.getBoolean() == powered);
736
	powerSetting.setBoolean(false);
737
	getLedStatus().setLed(LedStatus::POWER, false);
738
739
	msxMixer->mute();
740
741
	EmuTime time = getCurrentTime();
742
	for (auto& d : availableDevices) {
743
		d->powerDown(time);
744
	}
745
}
746
747
void MSXMotherBoard::activate(bool active_)
748
{
749
	active = active_;
750
	auto event = active ? Event(MachineActivatedEvent())
751
	                    : Event(MachineDeactivatedEvent());
752
	msxEventDistributor->distributeEvent(event, scheduler->getCurrentTime());
753
	if (active) {
754
		realTime->resync();
755
	}
756
}
757
758
void MSXMotherBoard::exitCPULoopAsync()
759
{
760
	if (getMachineConfig()) {
761
		getCPU().exitCPULoopAsync();
762
	}
763
}
764
765
void MSXMotherBoard::exitCPULoopSync()
766
{
767
	getCPU().exitCPULoopSync();
768
}
769
770
MSXDevice* MSXMotherBoard::findDevice(std::string_view name)
771
{
772
	auto it = std::ranges::find(availableDevices, name, &MSXDevice::getName);
773
	return (it != end(availableDevices)) ? *it : nullptr;
774
}
775
776
MSXMapperIO& MSXMotherBoard::createMapperIO()
777
{
778
	if (mapperIOCounter == 0) {
779
		mapperIO = DeviceFactory::createMapperIO(*getMachineConfig());
780
		getCPUInterface().register_IO_InOut_range(0xfc, 4, mapperIO.get());
781
	}
782
	++mapperIOCounter;
783
	return *mapperIO;
784
}
785
786
void MSXMotherBoard::destroyMapperIO()
787
{
788
	assert(mapperIO);
789
	assert(mapperIOCounter);
790
	--mapperIOCounter;
791
	if (mapperIOCounter == 0) {
792
		getCPUInterface().unregister_IO_InOut_range(0xfc, 4, mapperIO.get());
793
		mapperIO.reset();
794
	}
795
}
796
797
std::string MSXMotherBoard::getUserName(const std::string& hwName)
798
{
799
	auto& s = userNames[hwName];
800
	unsigned n = 0;
801
	std::string userName;
802
	do {
803
		userName = strCat("untitled", ++n);
804
	} while (contains(s, userName));
805
	s.push_back(userName);
806
	return userName;
807
}
808
809
void MSXMotherBoard::freeUserName(const std::string& hwName, const std::string& userName)
810
{
811
	auto& s = userNames[hwName];
812
	move_pop_back(s, rfind_unguarded(s, userName));
813
}
814
815
void MSXMotherBoard::registerMediaProvider(std::string_view name, MediaProvider& provider)
816
{
817
	assert(!contains(mediaProviders, name, &MediaProviderInfo::name));
818
	assert(!contains(mediaProviders, &provider, &MediaProviderInfo::provider));
819
	mediaProviders.emplace_back(name, &provider);
820
}
821
822
void MSXMotherBoard::unregisterMediaProvider(MediaProvider& provider)
823
{
824
	move_pop_back(mediaProviders,
825
	              rfind_unguarded(mediaProviders, &provider, &MediaProviderInfo::provider));
826
}
827
828
MediaProvider* MSXMotherBoard::findMediaProvider(std::string_view name) const
829
{
830
	auto it = std::ranges::find(mediaProviders, name, &MediaProviderInfo::name);
831
	return (it != mediaProviders.end()) ? it->provider : nullptr;
832
}
833
834
835
// AddRemoveUpdate
836
837
AddRemoveUpdate::AddRemoveUpdate(MSXMotherBoard& motherBoard_)
838
	: motherBoard(motherBoard_)
839
{
840
	motherBoard.getReactor().getGlobalCliComm().update(
841
		CliComm::UpdateType::HARDWARE, motherBoard.getMachineID(), "add");
842
}
843
844
AddRemoveUpdate::~AddRemoveUpdate()
845
{
846
	motherBoard.getReactor().getGlobalCliComm().update(
847
		CliComm::UpdateType::HARDWARE, motherBoard.getMachineID(), "remove");
848
}
849
850
851
// ResetCmd
852
ResetCmd::ResetCmd(MSXMotherBoard& motherBoard_)
853
	: RecordedCommand(motherBoard_.getCommandController(),
854
	                  motherBoard_.getStateChangeDistributor(),
855
	                  motherBoard_.getScheduler(),
856
	                  "reset")
857
	, motherBoard(motherBoard_)
858
{
859
}
860
861
void ResetCmd::execute(std::span<const TclObject> /*tokens*/, TclObject& /*result*/,
862
                       EmuTime /*time*/)
863
{
864
	motherBoard.doReset();
865
}
866
867
std::string ResetCmd::help(std::span<const TclObject> /*tokens*/) const
868
{
869
	return "Resets the MSX.";
870
}
871
872
873
// LoadMachineCmd
874
LoadMachineCmd::LoadMachineCmd(MSXMotherBoard& motherBoard_)
875
	: Command(motherBoard_.getCommandController(), "load_machine")
876
	, motherBoard(motherBoard_)
877
{
878
	// The load_machine command should always directly follow a
879
	// create_machine command:
880
	// - It's not allowed to use load_machine on a machine that has
881
	//   already a machine configuration loaded earlier.
882
	// - We also disallow executing most machine-specific commands on an
883
	//   'empty machine' (an 'empty machine', is a machine returned by
884
	//   create_machine before the load_machine command is executed, so a
885
	//   machine without a machine configuration). The only exception is
886
	//   this load_machine command and machine_info.
887
	//
888
	// So if the only allowed command on an empty machine is
889
	// 'load_machine', (and an empty machine by itself isn't very useful),
890
	// then why isn't create_machine and load_machine merged into a single
891
	// command? The only reason for this is that load_machine sends out
892
	// events (machine specific) and maybe you already want to know the ID
893
	// of the new machine (this is returned by create_machine) before those
894
	// events will be send.
895
	//
896
	// Why not allow all commands on an empty machine? In the past we did
897
	// allow this, though it often was the source of bugs. We could in each
898
	// command (when needed) check for an empty machine and then return
899
	// some dummy/empty result or some error. But because I can't think of
900
	// any really useful command for an empty machine, it seemed easier to
901
	// just disallow most commands.
902
	setAllowedInEmptyMachine(true);
903
}
904
905
void LoadMachineCmd::execute(std::span<const TclObject> tokens, TclObject& result)
906
{
907
	checkNumArgs(tokens, 2, "machine");
908
	if (motherBoard.getMachineConfig()) {
909
		throw CommandException("Already loaded a config in this machine.");
910
	}
911
	result = motherBoard.loadMachine(std::string(tokens[1].getString()));
912
}
913
914
std::string LoadMachineCmd::help(std::span<const TclObject> /*tokens*/) const
915
{
916
	return "Load a msx machine configuration into an empty machine.";
917
}
918
919
void LoadMachineCmd::tabCompletion(std::vector<std::string>& tokens) const
920
{
921
	completeString(tokens, Reactor::getHwConfigs("machines"));
922
}
923
924
925
// ListExtCmd
926
ListExtCmd::ListExtCmd(MSXMotherBoard& motherBoard_)
927
	: Command(motherBoard_.getCommandController(), "list_extensions")
928
	, motherBoard(motherBoard_)
929
{
930
}
931
932
void ListExtCmd::execute(std::span<const TclObject> /*tokens*/, TclObject& result)
933
{
934
	result.addListElements(
935
		std::views::transform(motherBoard.getExtensions(), &HardwareConfig::getName));
936
}
937
938
std::string ListExtCmd::help(std::span<const TclObject> /*tokens*/) const
939
{
940
	return "Return a list of all inserted extensions.";
941
}
942
943
944
// ExtCmd
945
ExtCmd::ExtCmd(MSXMotherBoard& motherBoard_, std::string commandName_)
946
	: RecordedCommand(motherBoard_.getCommandController(),
947
	                  motherBoard_.getStateChangeDistributor(),
948
	                  motherBoard_.getScheduler(),
949
	                  commandName_)
950
	, motherBoard(motherBoard_)
951
	, commandName(std::move(commandName_))
952
{
953
}
954
955
void ExtCmd::execute(std::span<const TclObject> tokens, TclObject& result,
956
                     EmuTime /*time*/)
957
{
958
	checkNumArgs(tokens, Between{2, 3}, "extension");
959
	if (tokens.size() == 3 && tokens[1].getString() != "insert") {
960
		throw SyntaxError();
961
	}
962
	try {
963
		auto name = tokens[tokens.size() - 1].getString();
964
		auto slotName = (commandName.size() == 4)
965
			? std::string_view(&commandName[3], 1)
966
			: "any";
967
		auto extension = motherBoard.loadExtension(name, slotName);
968
		if (slotName != "any") {
969
			const auto& manager = motherBoard.getSlotManager();
970
			if (const auto* extConf = manager.getConfigForSlot(commandName[3] - 'a')) {
971
				// still a cartridge inserted, (try to) remove it now
972
				motherBoard.removeExtension(*extConf);
973
			}
974
		}
975
		result = motherBoard.insertExtension(name, std::move(extension));
976
	} catch (MSXException& e) {
977
		throw CommandException(std::move(e).getMessage());
978
	}
979
}
980
981
std::string ExtCmd::help(std::span<const TclObject> /*tokens*/) const
982
{
983
	return "Insert a hardware extension.";
984
}
985
986
void ExtCmd::tabCompletion(std::vector<std::string>& tokens) const
987
{
988
	completeString(tokens, Reactor::getHwConfigs("extensions"));
989
}
990
991
992
// RemoveExtCmd
993
RemoveExtCmd::RemoveExtCmd(MSXMotherBoard& motherBoard_)
994
	: RecordedCommand(motherBoard_.getCommandController(),
995
	                  motherBoard_.getStateChangeDistributor(),
996
	                  motherBoard_.getScheduler(),
997
	                  "remove_extension")
998
	, motherBoard(motherBoard_)
999
{
1000
}
1001
1002
void RemoveExtCmd::execute(std::span<const TclObject> tokens, TclObject& /*result*/,
1003
                           EmuTime /*time*/)
1004
{
1005
	checkNumArgs(tokens, 2, "extension");
1006
	std::string_view extName = tokens[1].getString();
1007
	HardwareConfig* extension = motherBoard.findExtension(extName);
1008
	if (!extension) {
1009
		throw CommandException("No such extension: ", extName);
1010
	}
1011
	try {
1012
		motherBoard.removeExtension(*extension);
1013
	} catch (MSXException& e) {
1014
		throw CommandException("Can't remove extension '", extName,
1015
		                       "': ", e.getMessage());
1016
	}
1017
}
1018
1019
std::string RemoveExtCmd::help(std::span<const TclObject> /*tokens*/) const
1020
{
1021
	return "Remove an extension from the MSX machine.";
1022
}
1023
1024
void RemoveExtCmd::tabCompletion(std::vector<std::string>& tokens) const
1025
{
1026
	if (tokens.size() == 2) {
1027
		completeString(tokens, std::views::transform(
1028
			motherBoard.getExtensions(),
1029
			[](auto& e) -> std::string_view { return e->getName(); }));
1030
	}
1031
}
1032
1033
// StoreSetupCmd
1034
1035
static const std::map<std::string_view, SetupDepth, std::less<>> depthOptionMap = {
1036
	{"none"          , SetupDepth::NONE          },
1037
	{"machine"       , SetupDepth::MACHINE       },
1038
	{"extensions"    , SetupDepth::EXTENSIONS    },
1039
	{"connectors"    , SetupDepth::CONNECTORS    },
1040
	{"media"         , SetupDepth::MEDIA         },
1041
	{"complete_state", SetupDepth::COMPLETE_STATE},
1042
};
1043
1044
StoreSetupCmd::StoreSetupCmd(MSXMotherBoard& motherBoard_)
1045
	: Command(motherBoard_.getCommandController(), "store_setup")
1046
	, motherBoard(motherBoard_)
1047
{
1048
}
1049
1050
void StoreSetupCmd::execute(std::span<const TclObject> tokens, TclObject& result)
1051
{
1052
	checkNumArgs(tokens, Between{2, 3}, Prefix{1}, "depth ?filename?");
1053
1054
	auto depthArg = tokens[1].getString();
1055
	const auto* depth = lookup(depthOptionMap, depthArg);
1056
	if (!depth) {
1057
		throw CommandException("Unknown depth argument: ", depthArg);
1058
	}
1059
1060
	if (*depth == SetupDepth::NONE) return;
1061
1062
	std::string_view filenameArg;
1063
	if (tokens.size() == 3) {
1064
		filenameArg = tokens[2].getString();
1065
	}
1066
1067
	auto filename = FileOperations::parseCommandFileArgument(
1068
		filenameArg, Reactor::SETUP_DIR, motherBoard.getMachineName(), Reactor::SETUP_EXTENSION);
1069
1070
	// TODO: make parts of levels to be saved configurable?
1071
	motherBoard.storeAsSetup(filename, *depth);
1072
1073
	result = filename;
1074
}
1075
1076
std::string StoreSetupCmd::help(std::span<const TclObject> /*tokens*/) const
1077
{
1078
	return
1079
		"store_setup <depth> [filename]  Save setup based on this machine with given depth to indicated file.";
1080
}
1081
1082
void StoreSetupCmd::tabCompletion(std::vector<std::string>& tokens) const
1083
{
1084
	if (tokens.size() == 2) {
1085
		completeString(tokens, std::views::keys(depthOptionMap));
1086
	} else if (tokens.size() == 3) {
1087
		completeString(tokens, Reactor::getSetups());
1088
	}
1089
}
1090
1091
1092
// MachineNameInfo
1093
1094
MachineNameInfo::MachineNameInfo(MSXMotherBoard& motherBoard_)
1095
	: InfoTopic(motherBoard_.getMachineInfoCommand(), "config_name")
1096
	, motherBoard(motherBoard_)
1097
{
1098
}
1099
1100
void MachineNameInfo::execute(std::span<const TclObject> /*tokens*/,
1101
                              TclObject& result) const
1102
{
1103
	result = motherBoard.getMachineName();
1104
}
1105
1106
std::string MachineNameInfo::help(std::span<const TclObject> /*tokens*/) const
1107
{
1108
	return "Returns the configuration name for this machine.";
1109
}
1110
1111
// MachineTypeInfo
1112
1113
MachineTypeInfo::MachineTypeInfo(MSXMotherBoard& motherBoard_)
1114
	: InfoTopic(motherBoard_.getMachineInfoCommand(), "type")
1115
	, motherBoard(motherBoard_)
1116
{
1117
}
1118
1119
void MachineTypeInfo::execute(std::span<const TclObject> /*tokens*/,
1120
                              TclObject& result) const
1121
{
1122
	result = motherBoard.getMachineType();
1123
}
1124
1125
std::string MachineTypeInfo::help(std::span<const TclObject> /*tokens*/) const
1126
{
1127
	return "Returns the machine type for this machine.";
1128
}
1129
1130
1131
// MachineExtensionInfo
1132
1133
MachineExtensionInfo::MachineExtensionInfo(MSXMotherBoard& motherBoard_)
1134
	: InfoTopic(motherBoard_.getMachineInfoCommand(), "extension")
1135
	, motherBoard(motherBoard_)
1136
{
1137
}
1138
1139
void MachineExtensionInfo::execute(std::span<const TclObject> tokens,
1140
                              TclObject& result) const
1141
{
1142
	checkNumArgs(tokens, Between{2, 3}, Prefix{2}, "?extension-instance-name?");
1143
	if (tokens.size() == 2) {
1144
		result.addListElements(
1145
			std::views::transform(motherBoard.getExtensions(), &HardwareConfig::getName));
1146
	} else if (tokens.size() == 3) {
1147
		std::string_view extName = tokens[2].getString();
1148
		const HardwareConfig* extension = motherBoard.findExtension(extName);
1149
		if (!extension) {
1150
			throw CommandException("No such extension: ", extName);
1151
		}
1152
		if (extension->getType() == HardwareConfig::Type::EXTENSION) {
1153
			// A 'true' extension, as specified in an XML file
1154
			result.addDictKeyValue("config", extension->getConfigName());
1155
		} else {
1156
			assert(extension->getType() == HardwareConfig::Type::ROM);
1157
			// A ROM cartridge, peek into the internal config for the original filename
1158
			const auto& filename = extension->getConfig()
1159
				.getChild("devices").getChild("primary").getChild("secondary")
1160
				.getChild("ROM").getChild("rom").getChildData("filename");
1161
			result.addDictKeyValue("rom", filename);
1162
		}
1163
		TclObject deviceList;
1164
		deviceList.addListElements(
1165
			std::views::transform(extension->getDevices(), &MSXDevice::getName));
1166
		result.addDictKeyValue("devices", deviceList);
1167
	}
1168
}
1169
1170
std::string MachineExtensionInfo::help(std::span<const TclObject> /*tokens*/) const
1171
{
1172
	return "Returns information about the given extension instance.";
1173
}
1174
1175
void MachineExtensionInfo::tabCompletion(std::vector<std::string>& tokens) const
1176
{
1177
	if (tokens.size() == 3) {
1178
		completeString(tokens, std::views::transform(
1179
			motherBoard.getExtensions(),
1180
			[](auto& e) -> std::string_view { return e->getName(); }));
1181
	}
1182
}
1183
1184
1185
// MachineMediaInfo
1186
1187
MachineMediaInfo::MachineMediaInfo(MSXMotherBoard& motherBoard_)
1188
	: InfoTopic(motherBoard_.getMachineInfoCommand(), "media")
1189
	, motherBoard(motherBoard_)
1190
{
1191
}
1192
1193
void MachineMediaInfo::execute(std::span<const TclObject> tokens,
1194
                              TclObject& result) const
1195
{
1196
	checkNumArgs(tokens, Between{2, 3}, Prefix{2}, "?media-slot-name?");
1197
	const auto& providers = motherBoard.getMediaProviders();
1198
	if (tokens.size() == 2) {
1199
		result.addListElements(
1200
			std::views::transform(providers, &MediaProviderInfo::name));
1201
	} else if (tokens.size() == 3) {
1202
		auto name = tokens[2].getString();
1203
		if (auto it = std::ranges::find(providers, name, &MediaProviderInfo::name);
1204
		    it != providers.end()) {
1205
			it->provider->getMediaInfo(result);
1206
		} else {
1207
			throw CommandException("No info about media slot ", name);
1208
		}
1209
	}
1210
}
1211
1212
std::string MachineMediaInfo::help(std::span<const TclObject> /*tokens*/) const
1213
{
1214
	return "Returns information about the given media slot.";
1215
}
1216
1217
void MachineMediaInfo::tabCompletion(std::vector<std::string>& tokens) const
1218
{
1219
	if (tokens.size() == 3) {
1220
		completeString(tokens, std::views::transform(
1221
			motherBoard.getMediaProviders(), &MediaProviderInfo::name));
1222
	}
1223
}
1224
1225
// DeviceInfo
1226
1227
DeviceInfo::DeviceInfo(MSXMotherBoard& motherBoard_)
1228
	: InfoTopic(motherBoard_.getMachineInfoCommand(), "device")
1229
	, motherBoard(motherBoard_)
1230
{
1231
}
1232
1233
void DeviceInfo::execute(std::span<const TclObject> tokens, TclObject& result) const
1234
{
1235
	checkNumArgs(tokens, Between{2, 3}, Prefix{2}, "?device?");
1236
	switch (tokens.size()) {
1237
	case 2:
1238
		result.addListElements(
1239
			std::views::transform(motherBoard.availableDevices,
1240
			                [](auto& d) { return d->getName(); }));
1241
		break;
1242
	case 3: {
1243
		std::string_view deviceName = tokens[2].getString();
1244
		const MSXDevice* device = motherBoard.findDevice(deviceName);
1245
		if (!device) {
1246
			throw CommandException("No such device: ", deviceName);
1247
		}
1248
		device->getDeviceInfo(result);
1249
		break;
1250
	}
1251
	}
1252
}
1253
1254
std::string DeviceInfo::help(std::span<const TclObject> /*tokens*/) const
1255
{
1256
	return "Without any arguments, returns the list of used device names.\n"
1257
	       "With a device name as argument, returns the type (and for some "
1258
	       "devices the subtype) of the given device.";
1259
}
1260
1261
void DeviceInfo::tabCompletion(std::vector<std::string>& tokens) const
1262
{
1263
	if (tokens.size() == 3) {
1264
		completeString(tokens, std::views::transform(
1265
			motherBoard.availableDevices,
1266
			[](auto& d) -> std::string_view { return d->getName(); }));
1267
	}
1268
}
1269
1270
1271
// FastForwardHelper
1272
1273
FastForwardHelper::FastForwardHelper(MSXMotherBoard& motherBoard_)
1274
	: Schedulable(motherBoard_.getScheduler())
1275
	, motherBoard(motherBoard_)
1276
{
1277
}
1278
1279
void FastForwardHelper::setTarget(EmuTime targetTime)
1280
{
1281
	setSyncPoint(targetTime);
1282
}
1283
1284
void FastForwardHelper::executeUntil(EmuTime /*time*/)
1285
{
1286
	motherBoard.exitCPULoopSync();
1287
}
1288
1289
1290
// class JoyPortDebuggable
1291
1292
JoyPortDebuggable::JoyPortDebuggable(MSXMotherBoard& motherBoard_)
1293
	: SimpleDebuggable(motherBoard_, "joystickports", "MSX Joystick Ports", 2)
1294
{
1295
}
1296
1297
uint8_t JoyPortDebuggable::read(unsigned address, EmuTime time)
1298
{
1299
	return getMotherBoard().getJoystickPort(address).read(time);
1300
}
1301
1302
void JoyPortDebuggable::write(unsigned /*address*/, uint8_t /*value*/)
1303
{
1304
	// ignore
1305
}
1306
1307
1308
// class SettingObserver
1309
1310
SettingObserver::SettingObserver(MSXMotherBoard& motherBoard_)
1311
	: motherBoard(motherBoard_)
1312
{
1313
}
1314
1315
void SettingObserver::update(const Setting& setting) noexcept
1316
{
1317
	if (&setting == &motherBoard.powerSetting) {
1318
		if (motherBoard.powerSetting.getBoolean()) {
1319
			motherBoard.powerUp();
1320
		} else {
1321
			motherBoard.powerDown();
1322
		}
1323
	} else if (&setting == &motherBoard.suppressMessagesSetting) {
1324
		motherBoard.msxCliComm->setSuppressMessages(motherBoard.suppressMessagesSetting.getBoolean());
1325
	} else {
1326
		UNREACHABLE;
1327
	}
1328
}
1329
1330
1331
// serialize
1332
// version 1: initial version
1333
// version 2: added reRecordCount
1334
// version 3: removed reRecordCount (moved to ReverseManager)
1335
// version 4: moved joystickportA/B from MSXPSG to here
1336
// version 5: do serialize renShaTurbo
1337
template<typename Archive>
1338
void MSXMotherBoard::serialize(Archive& ar, unsigned version)
1339
{
1340
	// don't serialize:
1341
	//    machineID, userNames, availableDevices, addRemoveUpdate,
1342
	//    sharedStuffMap, msxCliComm, msxEventDistributor,
1343
	//    msxCommandController, slotManager, eventDelay,
1344
	//    debugger, msxMixer, panasonicMemory, ledStatus
1345
1346
	// Scheduler must come early so that devices can query current time
1347
	ar.serialize("scheduler", *scheduler);
1348
	// MSXMixer has already set sync points, those are invalid now
1349
	// the following call will fix this
1350
	if constexpr (Archive::IS_LOADER) {
1351
		msxMixer->reInit();
1352
	}
1353
1354
	ar.serialize("name", machineName);
1355
	ar.serializeWithID("config", machineConfig2, std::ref(*this));
1356
	assert(getMachineConfig() == machineConfig2.get());
1357
	ar.serializeWithID("extensions", extensions, std::ref(*this));
1358
1359
	if (mapperIO) ar.serialize("mapperIO", *mapperIO);
1360
1361
	if (auto& devSwitch = getDeviceSwitch();
1362
	    devSwitch.hasRegisteredDevices()) {
1363
		ar.serialize("deviceSwitch", devSwitch);
1364
	}
1365
1366
	if (getMachineConfig()) {
1367
		ar.serialize("cpu", getCPU());
1368
	}
1369
	ar.serialize("cpuInterface", getCPUInterface());
1370
1371
	if (auto* port = dynamic_cast<CassettePort*>(&getCassettePort())) {
1372
		ar.serialize("cassetteport", *port);
1373
	}
1374
	if (ar.versionAtLeast(version, 4)) {
1375
		if (auto* port = dynamic_cast<JoystickPort*>(
1376
				joystickPort[0].get())) {
1377
			ar.serialize("joystickportA", *port);
1378
		}
1379
		if (auto* port = dynamic_cast<JoystickPort*>(
1380
				joystickPort[1].get())) {
1381
			ar.serialize("joystickportB", *port);
1382
		}
1383
	}
1384
	if (ar.versionAtLeast(version, 5)) {
1385
		if (renShaTurbo) ar.serialize("renShaTurbo", *renShaTurbo);
1386
	}
1387
1388
	if constexpr (Archive::IS_LOADER) {
1389
		powered = true; // must come before changing power setting
1390
		powerSetting.setBoolean(true);
1391
		getLedStatus().setLed(LedStatus::POWER, true);
1392
		msxMixer->unmute();
1393
	}
1394
1395
	if (version == 2) {
1396
		assert(Archive::IS_LOADER);
1397
		unsigned reRecordCount = 0; // silence warning
1398
		ar.serialize("reRecordCount", reRecordCount);
1399
		getReverseManager().setReRecordCount(reRecordCount);
1400
	}
1401
}
1402
INSTANTIATE_SERIALIZE_METHODS(MSXMotherBoard)
1403
1404
} // namespace openmsx