22 CartridgeSlotManager::Slot::~Slot()
25 assert(useCount == 0);
30 return cartCommand.has_value();
33 bool CartridgeSlotManager::Slot::used(
const HardwareConfig* allowed)
const
35 assert((useCount == 0) == (config ==
nullptr));
36 return config && (config != allowed);
42 : motherBoard(motherBoard_)
43 , cartCmd(*this, motherBoard,
"cart")
44 , extSlotInfo(motherBoard.getMachineInfoCommand())
50 for (
auto slot :
xrange(MAX_SLOTS)) {
52 assert(!slots[slot].
exists());
53 assert(!slots[slot].used());
59 if (slot.size() == 1) {
60 if ((
'0' <= slot[0]) && (slot[0] <=
'3')) {
62 }
else if ((
'a' <= slot[0]) && (slot[0] <=
'p')) {
63 return -(1 + slot[0] -
'a');
64 }
else if (slot[0] ==
'X') {
67 }
else if (slot.size() == 2) {
68 if ((slot[0] ==
'?') && (
'0' <= slot[1]) && (slot[1] <=
'3')) {
69 return slot[1] -
'0' - 128;
71 }
else if (slot ==
"any") {
74 throw MSXException(
"Invalid slot specification: ", slot);
87 for (
auto slot :
xrange(MAX_SLOTS)) {
88 if (!slots[slot].
exists()) {
91 char slotName[] =
"carta";
93 char extName[] =
"exta";
97 slots[slot].cartCommand.emplace(
98 *
this, motherBoard, slotName);
99 slots[slot].extCommand.emplace(
100 motherBoard, extName);
108 int CartridgeSlotManager::getSlot(
int ps,
int ss)
const
110 for (
auto slot :
xrange(MAX_SLOTS)) {
111 if (slots[slot].
exists() &&
112 (slots[slot].ps == ps) && (slots[slot].ss == ss)) {
129 int slot = getSlot(ps, ss);
130 if (slots[slot].used(&allowed)) {
142 int slot = getSlot(ps, ss);
143 assert(!slots[slot].used());
144 const auto& slotName = slots[slot].cartCommand->getName();
147 slots[slot].cartCommand.reset();
148 slots[slot].extCommand.reset();
153 assert(slot < MAX_SLOTS);
154 if (!slots[slot].
exists()) {
155 throw MSXException(
"slot-",
char(
'a' + slot),
" not defined.");
157 if (slots[slot].used()) {
158 throw MSXException(
"slot-",
char(
'a' + slot),
" already in use.");
166 assert(slot < MAX_SLOTS);
167 if (!slots[slot].
exists()) {
168 throw MSXException(
"slot-",
char(
'a' + slot),
" not defined.");
170 if (slots[slot].used()) {
171 throw MSXException(
"slot-",
char(
'a' + slot),
" already in use.");
173 if (slots[slot].ss != -1) {
174 throw MSXException(
"slot-",
char(
'a' + slot),
" is not a primary slot.");
176 assert(slots[slot].useCount == 0);
177 slots[slot].config = &hwConfig;
178 slots[slot].useCount = 1;
179 return slots[slot].ps;
186 for (
auto slot :
xrange(MAX_SLOTS)) {
187 if (slots[slot].
exists() && !slots[slot].used()) {
188 int p = slots[slot].ps;
189 int s = slots[slot].ss;
190 if ((p < ps) || ((p == ps) && (s < ss))) {
203 for (
auto slot :
xrange(MAX_SLOTS)) {
204 if (slots[slot].
exists() && (slots[slot].ss == -1) &&
205 !slots[slot].used()) {
206 assert(slots[slot].useCount == 0);
207 slots[slot].config = &hwConfig;
208 slots[slot].useCount = 1;
209 return slots[slot].ps;
218 int slot = getSlot(ps, -1);
219 assert(slots[slot].config == &hwConfig); (void)hwConfig;
220 assert(slots[slot].useCount == 1);
221 slots[slot].config =
nullptr;
222 slots[slot].useCount = 0;
228 for (
auto slot :
xrange(MAX_SLOTS)) {
229 if (!slots[slot].
exists())
continue;
230 if ((slots[slot].ps == ps) && (slots[slot].ss == ss)) {
231 if (slots[slot].useCount == 0) {
232 slots[slot].config = &hwConfig;
234 if (slots[slot].config != &hwConfig) {
236 "Slot ", ps,
'-', ss,
237 " already in use by ",
238 slots[slot].config->getName());
241 ++slots[slot].useCount;
250 for (
auto slot :
xrange(MAX_SLOTS)) {
251 if (!slots[slot].
exists())
continue;
252 if ((slots[slot].ps == ps) && (slots[slot].ss == ss)) {
253 assert(slots[slot].config == &hwConfig); (void)hwConfig;
254 assert(slots[slot].useCount > 0);
255 --slots[slot].useCount;
256 if (slots[slot].useCount == 0) {
257 slots[slot].config =
nullptr;
268 int tmp = (convert && (slots[slot].ss == -1)) ? 0 : slots[slot].ss;
269 return slots[slot].exists() && (slots[slot].ps == ps) && (tmp == ss);
275 CartridgeSlotManager::CartCmd::CartCmd(
277 std::string_view commandName)
279 motherBoard_.getStateChangeDistributor(),
280 motherBoard_.getScheduler(),
283 , cliComm(motherBoard_.getMSXCliComm())
287 const HardwareConfig* CartridgeSlotManager::CartCmd::getExtensionConfig(
288 std::string_view cartname)
290 if (cartname.size() != 5) {
293 int slot = cartname[4] -
'a';
294 return manager.slots[slot].config;
297 void CartridgeSlotManager::CartCmd::execute(
298 std::span<const TclObject> tokens, TclObject& result, EmuTime::param )
300 std::string_view cartname = tokens[0].getString();
306 if (
auto pos = cartname.rfind(
"::"); pos != std::string_view::npos) {
307 cartname = cartname.substr(pos + 2);
309 if (tokens.size() == 1) {
311 const auto* extConf = getExtensionConfig(cartname);
312 result.addListElement(
tmpStrCat(cartname,
':'),
313 extConf ? extConf->getName() :
string{});
316 result.addListElement(options);
318 }
else if (tokens[1] ==
one_of(
"eject",
"-eject")) {
320 if (tokens[1] ==
"-eject") {
322 "Warning: use of '-eject' is deprecated, "
323 "instead use the 'eject' subcommand";
325 if (
const auto* extConf = getExtensionConfig(cartname)) {
327 manager.motherBoard.removeExtension(*extConf);
329 }
catch (MSXException&
e) {
330 throw CommandException(
"Can't remove cartridge: ",
336 auto slotname = (cartname.size() == 5)
337 ? cartname.substr(4, 1)
339 size_t extensionNameToken = 1;
340 if (tokens[1] ==
"insert") {
341 if (tokens.size() > 2) {
342 extensionNameToken = 2;
344 throw CommandException(
"Missing argument to insert subcommand");
347 auto options = tokens.subspan(extensionNameToken + 1);
349 std::string_view romname = tokens[extensionNameToken].getString();
351 manager.motherBoard,
string(romname),
string(slotname), options);
352 if (slotname !=
"any") {
353 if (
const auto* extConf = getExtensionConfig(cartname)) {
355 manager.motherBoard.removeExtension(*extConf);
358 result = manager.motherBoard.insertExtension(
359 "ROM", std::move(extension));
361 }
catch (MSXException&
e) {
362 throw CommandException(std::move(
e).getMessage());
367 string CartridgeSlotManager::CartCmd::help(std::span<const TclObject> tokens)
const
369 auto cart = tokens[0].getString();
371 cart,
" eject : remove the ROM cartridge from this slot\n",
372 cart,
" insert <filename> : insert ROM cartridge with <filename>\n",
373 cart,
" <filename> : insert ROM cartridge with <filename>\n",
374 cart,
" : show which ROM cartridge is in this slot\n",
375 "The following options are supported when inserting a cartridge:\n"
376 "-ips <filename> : apply the given IPS patch to the ROM image\n"
377 "-romtype <romtype> : specify the ROM mapper type\n");
380 void CartridgeSlotManager::CartCmd::tabCompletion(std::vector<string>& tokens)
const
382 using namespace std::literals;
383 static constexpr std::array extra = {
"eject"sv,
"insert"sv};
385 (tokens.size() < 3) ? extra : std::span<const std::string_view>{});
389 bool CartridgeSlotManager::CartCmd::needRecord(std::span<const TclObject> tokens)
const
391 return tokens.size() > 1;
397 CartridgeSlotManager::CartridgeSlotInfo::CartridgeSlotInfo(
398 InfoCommand& machineInfoCommand)
399 : InfoTopic(machineInfoCommand,
"external_slot")
403 void CartridgeSlotManager::CartridgeSlotInfo::execute(
404 std::span<const TclObject> tokens, TclObject& result)
const
406 checkNumArgs(tokens, Between{2, 3}, Prefix{2},
"?slot?");
407 auto& manager =
OUTER(CartridgeSlotManager, extSlotInfo);
408 switch (tokens.size()) {
411 string slot =
"slotX";
412 for (
auto i :
xrange(CartridgeSlotManager::MAX_SLOTS)) {
413 if (!manager.slots[i].exists())
continue;
414 slot[4] = char(
'a' + i);
415 result.addListElement(slot);
421 const auto& slotName = tokens[2].getString();
422 if ((slotName.size() != 5) || !slotName.starts_with(
"slot")) {
423 throw CommandException(
"Invalid slot name: ", slotName);
425 unsigned num = slotName[4] -
'a';
426 if (num >= CartridgeSlotManager::MAX_SLOTS) {
427 throw CommandException(
"Invalid slot name: ", slotName);
429 auto& slot = manager.slots[num];
430 if (!slot.exists()) {
431 throw CommandException(
"Slot '", slotName,
"' doesn't currently exist in this msx machine.");
433 result.addListElement(slot.ps);
435 result.addListElement(
"X");
437 result.addListElement(slot.ss);
440 result.addListElement(slot.config->getName());
442 result.addListElement(std::string_view{});
449 string CartridgeSlotManager::CartridgeSlotInfo::help(
450 std::span<const TclObject> )
const
452 return "Without argument: show list of available external slots.\n"
453 "With argument: show primary and secondary slot number for "
454 "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
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)
virtual void update(UpdateType type, std::string_view name, std::string_view value)=0
static std::unique_ptr< HardwareConfig > createRomConfig(MSXMotherBoard &motherBoard, std::string romfile, std::string slotname, std::span< const TclObject > options)
CliComm & getMSXCliComm()
Commands that directly influence the MSX state should send and events so that they can be recorded by...
bool exists(zstring_view filename)
Does this file (directory) exists?
This file implemented 3 utility functions:
FileContext userFileContext(string_view savePath)
TclObject makeTclList(Args &&... args)
bool any_of(InputRange &&range, UnaryPredicate pred)
#define OUTER(type, member)
TemporaryString tmpStrCat(Ts &&... ts)
std::string strCat(Ts &&...ts)
constexpr auto xrange(T e)