24 CartridgeSlotManager::Slot::~Slot()
27 assert(useCount == 0);
32 return cartCommand !=
nullptr;
35 bool CartridgeSlotManager::Slot::used(
const HardwareConfig* allowed)
const
37 assert((useCount == 0) == (config ==
nullptr));
38 return config && (config != allowed);
44 : motherBoard(motherBoard_)
45 , cartCmd(*this, motherBoard,
"cart")
46 , extSlotInfo(motherBoard.getMachineInfoCommand())
52 for (
auto slot :
xrange(MAX_SLOTS)) {
54 assert(!slots[slot].
exists());
55 assert(!slots[slot].used());
61 if (slot.size() == 1) {
62 if ((
'0' <= slot[0]) && (slot[0] <=
'3')) {
64 }
else if ((
'a' <= slot[0]) && (slot[0] <=
'p')) {
65 return -(1 + slot[0] -
'a');
66 }
else if (slot[0] ==
'X') {
69 }
else if (slot.size() == 2) {
70 if ((slot[0] ==
'?') && (
'0' <= slot[1]) && (slot[1] <=
'3')) {
71 return slot[1] -
'0' - 128;
73 }
else if (slot ==
"any") {
76 throw MSXException(
"Invalid slot specification: ", slot);
89 for (
auto slot :
xrange(MAX_SLOTS)) {
90 if (!slots[slot].
exists()) {
93 char slotName[] =
"carta";
95 char extName[] =
"exta";
99 slots[slot].cartCommand = std::make_unique<CartCmd>(
100 *
this, motherBoard, slotName);
101 slots[slot].extCommand = std::make_unique<ExtCmd>(
102 motherBoard, extName);
110 int CartridgeSlotManager::getSlot(
int ps,
int ss)
const
112 for (
auto slot :
xrange(MAX_SLOTS)) {
113 if (slots[slot].
exists() &&
114 (slots[slot].ps == ps) && (slots[slot].ss == ss)) {
131 int slot = getSlot(ps, ss);
132 if (slots[slot].used(&allowed)) {
144 int slot = getSlot(ps, ss);
145 assert(!slots[slot].used());
146 const auto& slotName = slots[slot].cartCommand->getName();
149 slots[slot].cartCommand.reset();
150 slots[slot].extCommand.reset();
155 assert(slot < MAX_SLOTS);
156 if (!slots[slot].
exists()) {
157 throw MSXException(
"slot-",
char(
'a' + slot),
" not defined.");
159 if (slots[slot].used()) {
160 throw MSXException(
"slot-",
char(
'a' + slot),
" already in use.");
168 assert(slot < MAX_SLOTS);
169 if (!slots[slot].
exists()) {
170 throw MSXException(
"slot-",
char(
'a' + slot),
" not defined.");
172 if (slots[slot].used()) {
173 throw MSXException(
"slot-",
char(
'a' + slot),
" already in use.");
175 if (slots[slot].ss != -1) {
176 throw MSXException(
"slot-",
char(
'a' + slot),
" is not a primary slot.");
178 assert(slots[slot].useCount == 0);
179 slots[slot].config = &hwConfig;
180 slots[slot].useCount = 1;
181 return slots[slot].ps;
188 for (
auto slot :
xrange(MAX_SLOTS)) {
189 if (slots[slot].
exists() && !slots[slot].used()) {
190 int p = slots[slot].ps;
191 int s = slots[slot].ss;
192 if ((p < ps) || ((p == ps) && (s < ss))) {
205 for (
auto slot :
xrange(MAX_SLOTS)) {
206 if (slots[slot].
exists() && (slots[slot].ss == -1) &&
207 !slots[slot].used()) {
208 assert(slots[slot].useCount == 0);
209 slots[slot].config = &hwConfig;
210 slots[slot].useCount = 1;
211 return slots[slot].ps;
220 int slot = getSlot(ps, -1);
221 assert(slots[slot].config == &hwConfig); (void)hwConfig;
222 assert(slots[slot].useCount == 1);
223 slots[slot].config =
nullptr;
224 slots[slot].useCount = 0;
230 for (
auto slot :
xrange(MAX_SLOTS)) {
231 if (!slots[slot].
exists())
continue;
232 if ((slots[slot].ps == ps) && (slots[slot].ss == ss)) {
233 if (slots[slot].useCount == 0) {
234 slots[slot].config = &hwConfig;
236 if (slots[slot].config != &hwConfig) {
238 "Slot ", ps,
'-', ss,
239 " already in use by ",
240 slots[slot].config->getName());
243 ++slots[slot].useCount;
252 for (
auto slot :
xrange(MAX_SLOTS)) {
253 if (!slots[slot].
exists())
continue;
254 if ((slots[slot].ps == ps) && (slots[slot].ss == ss)) {
255 assert(slots[slot].config == &hwConfig); (void)hwConfig;
256 assert(slots[slot].useCount > 0);
257 --slots[slot].useCount;
258 if (slots[slot].useCount == 0) {
259 slots[slot].config =
nullptr;
270 int tmp = (
convert && (slots[slot].ss == -1)) ? 0 : slots[slot].ss;
271 return slots[slot].exists() && (slots[slot].ps == ps) && (tmp == ss);
277 CartridgeSlotManager::CartCmd::CartCmd(
279 std::string_view commandName)
281 motherBoard_.getStateChangeDistributor(),
282 motherBoard_.getScheduler(),
285 , cliComm(motherBoard_.getMSXCliComm())
289 const HardwareConfig* CartridgeSlotManager::CartCmd::getExtensionConfig(
290 std::string_view cartname)
292 if (cartname.size() != 5) {
295 int slot = cartname[4] -
'a';
296 return manager.slots[slot].config;
299 void CartridgeSlotManager::CartCmd::execute(
302 std::string_view cartname = tokens[0].getString();
308 if (
auto pos = cartname.rfind(
"::"); pos != std::string_view::npos) {
309 cartname = cartname.substr(pos + 2);
311 if (tokens.
size() == 1) {
313 const auto* extConf = getExtensionConfig(cartname);
314 result.addListElement(
tmpStrCat(cartname,
':'),
315 extConf ? extConf->getName() :
string{});
318 result.addListElement(options);
320 }
else if (tokens[1] ==
one_of(
"eject",
"-eject")) {
322 if (tokens[1] ==
"-eject") {
324 "Warning: use of '-eject' is deprecated, "
325 "instead use the 'eject' subcommand";
327 if (
auto* extConf = getExtensionConfig(cartname)) {
329 manager.motherBoard.removeExtension(*extConf);
331 }
catch (MSXException& e) {
332 throw CommandException(
"Can't remove cartridge: ",
338 auto slotname = (cartname.size() == 5)
339 ? cartname.substr(4, 1)
341 size_t extensionNameToken = 1;
342 if (tokens[1] ==
"insert") {
343 if (tokens.
size() > 2) {
344 extensionNameToken = 2;
346 throw CommandException(
"Missing argument to insert subcommand");
349 auto options = tokens.
subspan(extensionNameToken + 1);
351 std::string_view romname = tokens[extensionNameToken].getString();
353 manager.motherBoard,
string(romname),
string(slotname), options);
354 if (slotname !=
"any") {
355 if (
auto* extConf = getExtensionConfig(cartname)) {
357 manager.motherBoard.removeExtension(*extConf);
360 result = manager.motherBoard.insertExtension(
361 "ROM", std::move(extension));
363 }
catch (MSXException& e) {
364 throw CommandException(std::move(e).getMessage());
369 string CartridgeSlotManager::CartCmd::help(
const vector<string>& tokens)
const
372 tokens[0],
" eject : remove the ROM cartridge from this slot\n",
373 tokens[0],
" insert <filename> : insert ROM cartridge with <filename>\n",
374 tokens[0],
" <filename> : insert ROM cartridge with <filename>\n",
375 tokens[0],
" : show which ROM cartridge is in this slot\n",
376 "The following options are supported when inserting a cartridge:\n"
377 "-ips <filename> : apply the given IPS patch to the ROM image\n"
378 "-romtype <romtype> : specify the ROM mapper type\n");
381 void CartridgeSlotManager::CartCmd::tabCompletion(vector<string>& tokens)
const
383 using namespace std::literals;
384 static constexpr std::array extra = {
"eject"sv,
"insert"sv};
392 return tokens.
size() > 1;
398 CartridgeSlotManager::CartridgeSlotInfo::CartridgeSlotInfo(
399 InfoCommand& machineInfoCommand)
400 : InfoTopic(machineInfoCommand,
"external_slot")
404 void CartridgeSlotManager::CartridgeSlotInfo::execute(
407 checkNumArgs(tokens, Between{2, 3}, Prefix{2},
"?slot?");
408 auto& manager =
OUTER(CartridgeSlotManager, extSlotInfo);
409 switch (tokens.
size()) {
412 string slot =
"slotX";
413 for (
auto i :
xrange(CartridgeSlotManager::MAX_SLOTS)) {
414 if (!manager.slots[i].exists())
continue;
415 slot[4] = char(
'a' + i);
416 result.addListElement(slot);
422 const auto& slotName = tokens[2].getString();
424 throw CommandException(
"Invalid slot name: ", slotName);
426 unsigned num = slotName[4] -
'a';
427 if (num >= CartridgeSlotManager::MAX_SLOTS) {
428 throw CommandException(
"Invalid slot name: ", slotName);
430 auto& slot = manager.slots[num];
431 if (!slot.exists()) {
432 throw CommandException(
"Slot '", slotName,
"' doesn't currently exist in this msx machine.");
434 result.addListElement(slot.ps);
436 result.addListElement(
"X");
438 result.addListElement(slot.ss);
441 result.addListElement(slot.config->getName());
443 result.addListElement(std::string_view{});
450 string CartridgeSlotManager::CartridgeSlotInfo::help(
451 const vector<string>& )
const
453 return "Without argument: show list of available external slots.\n"
454 "With argument: show primary and secondary slot number for "
455 "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, span< const TclObject > options)
CliComm & getMSXCliComm()
Commands that directly influence the MSX state should send and events so that they can be recorded by...
constexpr subspan_return_t< Offset, Count > subspan() const
constexpr index_type size() const noexcept
bool startsWith(string_view total, string_view part)
bool exists(zstring_view filename)
Does this file (directory) exists?
void convert(const th_ycbcr_buffer &input, RawFrame &output)
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)