25CartridgeSlotManager::Slot::~Slot()
28 assert(useCount == 0);
31bool CartridgeSlotManager::Slot::exists()
const
33 return cartCommand.has_value();
36bool CartridgeSlotManager::Slot::used(
const HardwareConfig* allowed)
const
38 assert((useCount == 0) == (config ==
nullptr));
39 return config && (config != allowed);
42void CartridgeSlotManager::Slot::getMediaInfo(TclObject& result)
47 result.addDictKeyValues(
"target", config->getConfigName(),
48 "devicename", config->getName(),
52 result.addDictKeyValue(
"type",
"rom");
54 const auto& romConfig = config->getConfig()
55 .getChild(
"devices").getChild(
"primary").getChild(
"secondary")
56 .getChild(
"ROM").getChild(
"rom");
57 result.addDictKeyValue(
"target", romConfig.getChildData(
"filename"));
59 if (
const auto* patchesElem = romConfig.findChild(
"patches")) {
60 for (
const auto* p : patchesElem->getChildren(
"ips")) {
61 patches.addListElement(p->getData());
64 result.addDictKeyValue(
"patches", patches);
65 if (
const auto* rom =
dynamic_cast<const MSXRom*
>(cpuInterface->getMSXDevice(ps, ss < 0 ? 0: ss, 1))) {
70 result.addDictKeyValue(
"target", std::string_view{});
77 : motherBoard(motherBoard_)
78 , cartCmd(*this, motherBoard,
"cart")
79 , extSlotInfo(motherBoard.getMachineInfoCommand())
87 assert(!slots[slot].exists());
88 assert(!slots[slot].used());
94 if (slot.size() == 1) {
95 if ((
'0' <= slot[0]) && (slot[0] <=
'3')) {
97 }
else if ((
'a' <= slot[0]) && (slot[0] <=
'p')) {
98 return -(1 + slot[0] -
'a');
99 }
else if (slot[0] ==
'X') {
102 }
else if (slot.size() == 2) {
103 if ((slot[0] ==
'?') && (
'0' <= slot[1]) && (slot[1] <=
'3')) {
104 return slot[1] -
'0' - 128;
106 }
else if (slot ==
"any") {
109 throw MSXException(
"Invalid slot specification: ", slot);
120 throw MSXException(
"Slot is already an external slot.");
123 auto& slot = slots[slotNum];
124 if (!slot.exists()) {
128 std::array slotName = {
'c',
'a',
'r',
't',
'X',
'\0'};
129 slotName[4] = narrow<char>(
'a' + slotNum);
132 slot.cartCommand.emplace(
133 *
this, motherBoard, slotName.data());
135 slot.cartCommand->getName(), slot);
137 std::array extName = {
'e',
'x',
't',
'X',
'\0'};
138 extName[3] = narrow<char>(
'a' + slotNum);
139 slot.extCommand.emplace(
140 motherBoard, extName.data());
149unsigned CartridgeSlotManager::getSlot(
int ps,
int ss)
const
152 if (slots[slot].exists() &&
153 (slots[slot].ps == ps) && (slots[slot].ss == ss)) {
169 auto slot = getSlot(ps, ss);
170 if (slots[slot].used(&allowed)) {
182 auto slotNum = getSlot(ps, ss);
183 auto& slot = slots[slotNum];
184 assert(!slot.used());
186 assert(slot.cartCommand);
189 slot.cartCommand.reset();
190 slot.extCommand.reset();
196 if (!slots[slot].exists()) {
197 throw MSXException(
"slot-",
char(
'a' + slot),
" not defined.");
199 if (slots[slot].used()) {
200 throw MSXException(
"slot-",
char(
'a' + slot),
" already in use.");
209 if (!slots[slot].exists()) {
210 throw MSXException(
"slot-",
char(
'a' + slot),
" not defined.");
212 if (slots[slot].used()) {
213 throw MSXException(
"slot-",
char(
'a' + slot),
" already in use.");
215 if (slots[slot].ss != -1) {
216 throw MSXException(
"slot-",
char(
'a' + slot),
" is not a primary slot.");
218 assert(slots[slot].useCount == 0);
219 slots[slot].config = &hwConfig;
220 slots[slot].useCount = 1;
221 return slots[slot].ps;
229 if (slots[slot].exists() && !slots[slot].used()) {
230 int p = slots[slot].ps;
231 int s = slots[slot].ss;
232 if ((p < ps) || ((p == ps) && (s < ss))) {
246 if (slots[slot].exists() && (slots[slot].ss == -1) &&
247 !slots[slot].used()) {
248 assert(slots[slot].useCount == 0);
249 slots[slot].config = &hwConfig;
250 slots[slot].useCount = 1;
251 return slots[slot].ps;
260 auto slot = getSlot(ps, -1);
261 assert(slots[slot].config == &hwConfig); (void)hwConfig;
262 assert(slots[slot].useCount == 1);
263 slots[slot].config =
nullptr;
264 slots[slot].useCount = 0;
271 if (!slots[slot].exists())
continue;
272 if ((slots[slot].ps == ps) && (slots[slot].ss == ss)) {
273 if (slots[slot].useCount == 0) {
274 slots[slot].config = &hwConfig;
276 if (slots[slot].config != &hwConfig) {
278 "Slot ", ps,
'-', ss,
279 " already in use by ",
280 slots[slot].config->getName());
283 ++slots[slot].useCount;
293 if (!slots[slot].exists())
continue;
294 if ((slots[slot].ps == ps) && (slots[slot].ss == ss)) {
295 assert(slots[slot].config == &hwConfig); (void)hwConfig;
296 assert(slots[slot].useCount > 0);
297 --slots[slot].useCount;
298 if (slots[slot].useCount == 0) {
299 slots[slot].config =
nullptr;
310 int tmp = (convert && (slots[slot].ss == -1)) ? 0 : slots[slot].ss;
311 return slots[slot].exists() && (slots[slot].ps == ps) && (tmp == ss);
317CartridgeSlotManager::CartCmd::CartCmd(
319 std::string_view commandName)
321 motherBoard_.getStateChangeDistributor(),
322 motherBoard_.getScheduler(),
325 , cliComm(motherBoard_.getMSXCliComm())
329const HardwareConfig* CartridgeSlotManager::CartCmd::getExtensionConfig(
330 std::string_view cartName)
const
332 if (cartName.size() != 5) {
335 return manager.getConfigForSlot(cartName[4] -
'a');
338void CartridgeSlotManager::CartCmd::execute(
339 std::span<const TclObject> tokens, TclObject& result, EmuTime::param )
341 std::string_view cartName = tokens[0].getString();
347 if (
auto pos = cartName.rfind(
"::"); pos != std::string_view::npos) {
348 cartName = cartName.substr(pos + 2);
350 if (tokens.size() == 1) {
352 const auto* extConf = getExtensionConfig(cartName);
353 result.addListElement(
tmpStrCat(cartName,
':'),
354 extConf ? extConf->getName() : string{});
357 result.addListElement(options);
359 }
else if (tokens[1] ==
one_of(
"eject",
"-eject")) {
361 if (tokens[1] ==
"-eject") {
363 "Warning: use of '-eject' is deprecated, "
364 "instead use the 'eject' subcommand";
366 if (
const auto* extConf = getExtensionConfig(cartName)) {
368 manager.motherBoard.removeExtension(*extConf);
370 }
catch (MSXException& e) {
371 throw CommandException(
"Can't remove cartridge: ",
377 auto slotName = (cartName.size() == 5)
378 ? cartName.substr(4, 1)
380 size_t extensionNameToken = 1;
381 if (tokens[1] ==
"insert") {
382 if (tokens.size() > 2) {
383 extensionNameToken = 2;
385 throw CommandException(
"Missing argument to insert subcommand");
388 auto options = tokens.subspan(extensionNameToken + 1);
390 std::string_view romName = tokens[extensionNameToken].getString();
392 manager.motherBoard, romName, slotName, options);
393 if (slotName !=
"any") {
394 if (
const auto* extConf = getExtensionConfig(cartName)) {
396 manager.motherBoard.removeExtension(*extConf);
399 result = manager.motherBoard.insertExtension(
400 "ROM", std::move(extension));
402 }
catch (MSXException& e) {
403 throw CommandException(std::move(e).getMessage());
408string CartridgeSlotManager::CartCmd::help(std::span<const TclObject> tokens)
const
410 auto cart = tokens[0].getString();
412 cart,
" eject : remove the ROM cartridge from this slot\n",
413 cart,
" insert <filename> : insert ROM cartridge with <filename>\n",
414 cart,
" <filename> : insert ROM cartridge with <filename>\n",
415 cart,
" : show which ROM cartridge is in this slot\n",
416 "The following options are supported when inserting a cartridge:\n"
417 "-ips <filename> : apply the given IPS patch to the ROM image\n"
418 "-romtype <romtype> : specify the ROM mapper type\n");
421void CartridgeSlotManager::CartCmd::tabCompletion(std::vector<string>& tokens)
const
423 using namespace std::literals;
424 static constexpr std::array extra = {
"eject"sv,
"insert"sv};
426 (tokens.size() < 3) ? extra :
std::span<const
std::string_view>{});
430bool CartridgeSlotManager::CartCmd::needRecord(std::span<const TclObject> tokens)
const
432 return tokens.size() > 1;
438CartridgeSlotManager::CartridgeSlotInfo::CartridgeSlotInfo(
439 InfoCommand& machineInfoCommand)
440 : InfoTopic(machineInfoCommand,
"external_slot")
444void CartridgeSlotManager::CartridgeSlotInfo::execute(
445 std::span<const TclObject> tokens, TclObject& result)
const
447 checkNumArgs(tokens, Between{2, 3}, Prefix{2},
"?slot?");
448 auto& manager =
OUTER(CartridgeSlotManager, extSlotInfo);
449 switch (tokens.size()) {
452 string slot =
"slotX";
453 for (
auto i :
xrange(CartridgeSlotManager::MAX_SLOTS)) {
454 if (!manager.slots[i].exists())
continue;
455 slot[4] = char(
'a' + i);
456 result.addListElement(slot);
462 const auto& slotName = tokens[2].getString();
463 if ((slotName.size() != 5) || !slotName.starts_with(
"slot")) {
464 throw CommandException(
"Invalid slot name: ", slotName);
466 unsigned num = slotName[4] -
'a';
468 throw CommandException(
"Invalid slot name: ", slotName);
470 auto& slot = manager.slots[num];
471 if (!slot.exists()) {
472 throw CommandException(
"Slot '", slotName,
"' doesn't currently exist in this msx machine.");
474 result.addListElement(slot.ps);
476 result.addListElement(
"X");
478 result.addListElement(slot.ss);
481 result.addListElement(slot.config->getName());
483 result.addListElement(std::string_view{});
490string CartridgeSlotManager::CartridgeSlotInfo::help(
491 std::span<const TclObject> )
const
493 return "Without argument: show list of available external slots.\n"
494 "With argument: show primary and secondary slot number for "
495 "given external slot.\n";
CartridgeSlotManager(MSXMotherBoard &motherBoard)
void getAnyFreeSlot(int &ps, int &ss) const
static int getSlotNum(std::string_view slot)
void createExternalSlot(int ps)
void testRemoveExternalSlot(int ps, const HardwareConfig &allowed) const
static constexpr unsigned MAX_SLOTS
void getSpecificSlot(unsigned slot, int &ps, int &ss) const
bool isExternalSlot(int ps, int ss, bool convert) const
void removeExternalSlot(int ps)
void freeSlot(int ps, int ss, const HardwareConfig &hwConfig)
int allocateAnyPrimarySlot(const HardwareConfig &hwConfig)
int allocateSpecificPrimarySlot(unsigned slot, const HardwareConfig &hwConfig)
void allocateSlot(int ps, int ss, const HardwareConfig &hwConfig)
void freePrimarySlot(int ps, const HardwareConfig &hwConfig)
static std::unique_ptr< HardwareConfig > createRomConfig(MSXMotherBoard &motherBoard, std::string_view romFile, std::string_view slotName, std::span< const TclObject > options)
void update(UpdateType type, std::string_view name, std::string_view value) override
MSXCPUInterface & getCPUInterface()
void registerMediaInfo(std::string_view name, MediaInfoProvider &provider)
Register and unregister providers of media info, for the media info topic.
void unregisterMediaInfo(MediaInfoProvider &provider)
MSXCliComm & getMSXCliComm()
Commands that directly influence the MSX state should send and events so that they can be recorded by...
This file implemented 3 utility functions:
const FileContext & userFileContext()
TclObject makeTclList(Args &&... args)
constexpr bool any_of(InputRange &&range, UnaryPredicate pred)
#define OUTER(type, member)
TemporaryString tmpStrCat(Ts &&... ts)
constexpr auto xrange(T e)