From 6083c0cbe68501829e06a4d19198ea9158a66aff Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Sun, 2 Jun 2024 22:07:50 +0200 Subject: [PATCH] Fix crashes and autobooting, clean up struct attributes --- assets/app.strings.json | 7 +++-- src/common/file.hpp | 2 +- src/common/gpu.hpp | 4 +-- src/common/gpufont.hpp | 2 +- src/common/ide.cpp | 37 ++++++++++++++++------- src/common/ide.hpp | 11 ++++--- src/common/idedefs.hpp | 11 +++++++ src/common/rom.hpp | 4 +-- src/common/spu.hpp | 2 +- src/common/util.hpp | 2 +- src/launcher/main.cpp | 21 +++++++++---- src/main/app/app.cpp | 4 +++ src/main/app/miscworkers.cpp | 38 ++++++++++++++---------- src/main/app/modals.cpp | 20 +++++-------- src/main/cart.hpp | 6 ++-- src/main/cartdata.hpp | 14 ++++----- src/main/main.cpp | 4 +-- src/main/uibase.cpp | 57 +++++++++++++++++++++++------------- 18 files changed, 154 insertions(+), 92 deletions(-) diff --git a/assets/app.strings.json b/assets/app.strings.json index 4e4df64..5138723 100644 --- a/assets/app.strings.json +++ b/assets/app.strings.json @@ -106,8 +106,8 @@ }, "atapiEjectWorker": { "eject": "Sending eject command...", - "noDrive": "No ATAPI CD-ROM drive has been found on the IDE bus or could be successfully initialized. Your drive might be incompatible with the ATAPI driver used by this tool.\n\nPress the Test button to view debug logs.", - "ejectError": "Failed to open the drive's tray. Your drive might be incompatible with the ATAPI driver used by this tool.\n\nError code: %s\nPress the Test button to view debug logs." + "noDrive": "No ATAPI CD-ROM drive has been found on the IDE bus or could be successfully initialized. Your drive may be incompatible with the ATAPI driver used by this tool.\n\nPress the Test button to view debug logs.", + "ejectError": "Failed to open the drive's tray. Make sure the drive's front panel is not blocked off by the 573's case. Your drive may also be incompatible with the ATAPI driver used by this tool.\n\nError code: %s\nPress the Test button to view debug logs." }, "rebootWorker": { "reboot": "Rebooting system..." @@ -308,9 +308,10 @@ "itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back", "host": "{HOST_ICON} Host filesystem (PCDRV)", + "noFS": "no disc or unsupported FS", "noDeviceError": "No drives have been found and successfully initialized on the IDE bus. Make sure the drives are appropriately configured as primary or secondary and are receiving power.\n\nPress the Test button to view debug logs.", - "atapiError": "Failed to initialize the CD-ROM drive or access the filesystem on it. Your drive might be incompatible with the ATAPI driver used by this tool.\n\nPress the Test button to view debug logs.", + "atapiError": "Failed to initialize the CD-ROM drive or access the filesystem on it. Ensure a disc is inserted and formatted with a valid ISO9660 filesystem. Your drive may also be incompatible with the ATAPI driver used by this tool.\n\nPress the Test button to view debug logs.", "ideError": "Failed to initialize the drive or access the filesystem on it. Turn off the system and make sure the drive is connected to the IDE bus properly, set as secondary (if a CD-ROM drive is also present) and formatted with a single FAT16, FAT32 or exFAT partition.\n\nPress the Test button to view debug logs.", "noFilesError": "No files or directories have been found in the root of the selected drive's filesystem." }, diff --git a/src/common/file.hpp b/src/common/file.hpp index bc9f088..c4aaaef 100644 --- a/src/common/file.hpp +++ b/src/common/file.hpp @@ -144,7 +144,7 @@ public: static constexpr int TABLE_BUCKET_COUNT = 256; -struct [[gnu::packed]] StringTableEntry { +struct StringTableEntry { public: uint32_t hash; uint16_t offset, chained; diff --git a/src/common/gpu.hpp b/src/common/gpu.hpp index 4fb7ac0..e298e6a 100644 --- a/src/common/gpu.hpp +++ b/src/common/gpu.hpp @@ -159,12 +159,12 @@ public: /* Image class */ -struct [[gnu::packed]] TIMHeader { +struct TIMHeader { public: uint32_t magic, flags; }; -struct [[gnu::packed]] TIMSectionHeader { +struct TIMSectionHeader { public: uint32_t length; RectWH vram; diff --git a/src/common/gpufont.hpp b/src/common/gpufont.hpp index f87dbbc..0927a40 100644 --- a/src/common/gpufont.hpp +++ b/src/common/gpufont.hpp @@ -10,7 +10,7 @@ namespace gpu { static constexpr char FONT_INVALID_CHAR = 0x7f; -class [[gnu::packed]] FontMetrics { +class FontMetrics { public: uint8_t spaceWidth, tabWidth, lineHeight, _reserved; uint32_t characterSizes[256]; diff --git a/src/common/ide.cpp b/src/common/ide.cpp index accb3e3..b6d3aca 100644 --- a/src/common/ide.cpp +++ b/src/common/ide.cpp @@ -165,7 +165,8 @@ static constexpr int _DMA_TIMEOUT = 10000; Device devices[2]{ (DEVICE_PRIMARY), (DEVICE_SECONDARY) }; Device::Device(uint32_t flags) -: flags(flags), capacity(0), lastStatusReg(0), lastErrorReg(0) { +: flags(flags), capacity(0), lastStatusReg(0), lastErrorReg(0), lastCountReg(0) +{ util::clear(lastSenseData); } @@ -299,10 +300,11 @@ DeviceError Device::_waitForDRQ(int timeout, bool ignoreError) { void Device::_handleError(void) { lastStatusReg = _read(CS0_STATUS); lastErrorReg = _read(CS0_ERROR); + lastCountReg = _read(CS0_COUNT); LOG_IDE( - "drive=%d, st=0x%02x, err=0x%02x", (flags / DEVICE_SECONDARY) & 1, - lastStatusReg, lastErrorReg + "%d, st=0x%02x, err=0x%02x, cnt=0x%02x", getDriveIndex(), lastStatusReg, + lastErrorReg, lastCountReg ); // Issuing a device reset command to an ATAPI drive would result in the @@ -314,10 +316,11 @@ void Device::_handleError(void) { void Device::_handleTimeout(void) { lastStatusReg = _read(CS0_STATUS); lastErrorReg = _read(CS0_ERROR); + lastCountReg = _read(CS0_COUNT); LOG_IDE( - "drive=%d, st=0x%02x, err=0x%02x", (flags / DEVICE_SECONDARY) & 1, - lastStatusReg, lastErrorReg + "%d, st=0x%02x, err=0x%02x, cnt=0x%02x", getDriveIndex(), lastStatusReg, + lastErrorReg, lastCountReg ); _write(CS0_COMMAND, ATA_DEVICE_RESET); @@ -333,7 +336,7 @@ DeviceError Device::_resetDrive(void) { _select(0); if (_waitForIdle(false, _DETECT_TIMEOUT, true)) { - LOG_IDE("drive %d select timeout", (flags / DEVICE_SECONDARY) & 1); + LOG_IDE("drive %d select timeout", getDriveIndex()); return NO_DRIVE; } @@ -361,7 +364,7 @@ DeviceError Device::_resetDrive(void) { #endif } - LOG_IDE("drive %d not responding", (flags / DEVICE_SECONDARY) & 1); + LOG_IDE("drive %d not responding", getDriveIndex()); return NO_DRIVE; } @@ -463,7 +466,12 @@ DeviceError Device::_atapiRequestSense(void) { auto error = _waitForIdle(false, _REQ_SENSE_TIMEOUT, true); if (!error) { + _write(CS0_FEATURES, 0); +#if 0 _setCylinder(sizeof(SenseData)); +#else + _setCylinder(ATAPI_SECTOR_SIZE); +#endif _write(CS0_COMMAND, ATA_PACKET); error = _waitForDRQ(_REQ_SENSE_TIMEOUT, true); @@ -485,7 +493,9 @@ DeviceError Device::_atapiRequestSense(void) { // If the request sense command fails, fall back to reading the sense // key from the error register. lastSenseData.senseKey = lastErrorReg >> 4; - LOG_IDE("%s", DEVICE_ERROR_NAMES[error]); + + LOG_IDE("%s", getErrorString(error)); + _write(CS0_COMMAND, ATA_DEVICE_RESET); } return _senseDataToError(lastSenseData); @@ -510,7 +520,12 @@ DeviceError Device::_atapiPacket(const Packet &packet, size_t dataLength) { auto error = _waitForIdle(); if (!error) { + _write(CS0_FEATURES, 0); +#if 0 _setCylinder(dataLength); +#else + _setCylinder(ATAPI_SECTOR_SIZE); +#endif _write(CS0_COMMAND, ATA_PACKET); error = _waitForDRQ(); @@ -527,12 +542,12 @@ DeviceError Device::_atapiPacket(const Packet &packet, size_t dataLength) { // If an error occurred, fetch sense data to determine whether to resend // the command. - LOG_IDE("%s, cmd=0x%02x", DEVICE_ERROR_NAMES[error], packet.command); + LOG_IDE("%s, cmd=0x%02x", getErrorString(error), packet.command); error = _atapiRequestSense(); if (error && (error != NOT_YET_READY)) { - LOG_IDE("%s (from sense)", DEVICE_ERROR_NAMES[error]); + LOG_IDE("%s (from sense)", getErrorString(error)); return error; } @@ -654,7 +669,7 @@ DeviceError Device::enumerate(void) { if (error) return error; - LOG_IDE("drive %d ready, mode=PIO%d", (flags / DEVICE_SECONDARY) & 1, mode); + LOG_IDE("drive %d ready, mode=PIO%d", getDriveIndex(), mode); flags |= DEVICE_READY; // Make sure any pending ATAPI sense data is cleared. diff --git a/src/common/ide.hpp b/src/common/ide.hpp index 29130d3..f52ef0f 100644 --- a/src/common/ide.hpp +++ b/src/common/ide.hpp @@ -38,7 +38,7 @@ enum IdentifyCapabilitiesFlag : uint16_t { IDENTIFY_CAP_FLAG_DMA_INTERLEAVE = 1 << 15 }; -class IdentifyBlock { +class alignas(uint32_t) IdentifyBlock { public: uint16_t deviceFlags; // 0 uint16_t _reserved[9]; @@ -100,7 +100,7 @@ public: /* ATAPI data structures */ -class SenseData { +class alignas(uint32_t) SenseData { public: uint8_t errorCode; // 0 uint8_t _reserved; // 1 @@ -126,7 +126,7 @@ public: } }; -class Packet { +class alignas(uint32_t) Packet { public: uint8_t command; uint8_t param[11]; @@ -268,9 +268,12 @@ public: #endif uint64_t capacity; - uint8_t lastStatusReg, lastErrorReg; + uint8_t lastStatusReg, lastErrorReg, lastCountReg; SenseData lastSenseData; + inline int getDriveIndex(void) const { + return (flags / DEVICE_SECONDARY) & 1; + } inline size_t getSectorSize(void) const { return (flags & DEVICE_ATAPI) ? ATAPI_SECTOR_SIZE : ATA_SECTOR_SIZE; } diff --git a/src/common/idedefs.hpp b/src/common/idedefs.hpp index b2f5e22..d6ff0a8 100644 --- a/src/common/idedefs.hpp +++ b/src/common/idedefs.hpp @@ -48,6 +48,17 @@ enum CS1DeviceControlFlag : uint8_t { CS1_DEVICE_CTRL_HOB = 1 << 7 // High-order bit (LBA48) }; +enum CS0FeaturesFlag : uint8_t { + CS0_FEATURES_DMA = 1 << 0, // Use DMA for data (ATAPI) + CS0_FEATURES_OVL = 1 << 1 // Overlap (ATAPI) +}; + +enum CS0CountFlag : uint8_t { + CS0_COUNT_CD = 1 << 0, // Command or data (ATAPI) + CS0_COUNT_IO = 1 << 1, // Input or output (ATAPI) + CS0_COUNT_REL = 1 << 2 // Bus release (ATAPI) +}; + /* ATA command definitions */ enum ATACommand : uint8_t { diff --git a/src/common/rom.hpp b/src/common/rom.hpp index 122713d..b44f1e3 100644 --- a/src/common/rom.hpp +++ b/src/common/rom.hpp @@ -78,7 +78,7 @@ extern const FlashRegion flash, pcmcia[2]; /* BIOS ROM headers */ -struct [[gnu::packed]] SonyKernelHeader { +struct SonyKernelHeader { public: uint8_t day, month; uint16_t year; @@ -88,7 +88,7 @@ public: bool validateMagic(void) const; }; -struct [[gnu::packed]] OpenBIOSHeader { +struct OpenBIOSHeader { public: uint8_t magic[8]; uint32_t idNameLength, idDescLength, idType; diff --git a/src/common/spu.hpp b/src/common/spu.hpp index c471787..fe1a5b5 100644 --- a/src/common/spu.hpp +++ b/src/common/spu.hpp @@ -40,7 +40,7 @@ size_t upload(uint32_t ramOffset, const void *data, size_t length, bool wait); /* Sound class */ -struct [[gnu::packed]] VAGHeader { +struct VAGHeader { public: uint32_t magic, version, interleave, length, sampleRate; uint16_t _reserved[5], channels; diff --git a/src/common/util.hpp b/src/common/util.hpp index a009539..6076b66 100644 --- a/src/common/util.hpp +++ b/src/common/util.hpp @@ -288,7 +288,7 @@ extern Logger logger; static constexpr size_t EXECUTABLE_BODY_OFFSET = 2048; static constexpr size_t MAX_EXECUTABLE_ARGS = 32; -class [[gnu::packed]] ExecutableHeader { +class ExecutableHeader { public: uint8_t magic[8], _pad[8]; diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index 49b2f94..06e2fbf 100644 --- a/src/launcher/main.cpp +++ b/src/launcher/main.cpp @@ -15,19 +15,28 @@ int main(int argc, const char **argv) { for (; argc > 0; argc--) args.parseArgument(*(argv++)); -#ifdef ENABLE_LOGGING +#if defined(ENABLE_APP_LOGGING) || defined(ENABLE_IDE_LOGGING) util::logger.setupSyslog(args.baudRate); #endif - if (!args.entryPoint || !args.loadAddress || !args.numFragments) + if (!args.entryPoint || !args.loadAddress || !args.numFragments) { + LOG_APP("required arguments missing"); return 1; + } + if (!args.stackTop) args.stackTop = _textStart - 16; - auto &dev = ide::devices[args.drive]; + auto &dev = ide::devices[args.drive]; + auto error = dev.enumerate(); - if (dev.enumerate()) + while ((error == ide::NOT_YET_READY) || (error == ide::DISC_CHANGED)) + error = dev.poll(); + + if (error) { + LOG_APP("drive %d initialization failed", args.drive); return 2; + } io::clearWatchdog(); @@ -52,8 +61,10 @@ int main(int argc, const char **argv) { length -= skipSectors; } - if (dev.readData(reinterpret_cast(ptr), lba, length)) + if (dev.readData(reinterpret_cast(ptr), lba, length)) { + LOG_APP("read failed, lba=0x%08x", lba); return 3; + } io::clearWatchdog(); ptr += length * sectorSize; diff --git a/src/main/app/app.cpp b/src/main/app/app.cpp index 1e43330..f360bb8 100644 --- a/src/main/app/app.cpp +++ b/src/main/app/app.cpp @@ -141,6 +141,10 @@ bool FileIOManager::loadResourceFile(const char *path) { if (_resourceFile) { if (resource.init(_resourceFile)) return true; + + _resourceFile->close(); + delete _resourceFile; + _resourceFile = nullptr; } resource.init(resourcePtr, resourceLength); diff --git a/src/main/app/miscworkers.cpp b/src/main/app/miscworkers.cpp index 44bdd4b..6c5c828 100644 --- a/src/main/app/miscworkers.cpp +++ b/src/main/app/miscworkers.cpp @@ -30,23 +30,16 @@ bool App::_ideInitWorker(void) { _workerStatus.update( i, util::countOf(ide::devices), WSTR("App.ideInitWorker.init") ); - dev.enumerate(); + + auto error = dev.enumerate(); + + while ((error == ide::NOT_YET_READY) || (error == ide::DISC_CHANGED)) + error = dev.poll(); + + LOG_APP("drive %d: %s", i, ide::getErrorString(error)); } - return _fileInitWorker(); -} - -bool App::_fileInitWorker(void) { - _workerStatus.update(0, 4, WSTR("App.fileInitWorker.unmount")); - _fileIO.closeResourceFile(); - _fileIO.close(); - - _workerStatus.update(1, 4, WSTR("App.fileInitWorker.mount")); - _fileIO.initIDE(); - - _workerStatus.update(2, 4, WSTR("App.fileInitWorker.loadResources")); - if (_fileIO.loadResourceFile(EXTERNAL_DATA_DIR "/resource.zip")) - _loadResources(); + _fileInitWorker(); #ifdef ENABLE_AUTOBOOT // Only try to autoboot if DIP switch 1 is on. @@ -71,6 +64,21 @@ bool App::_fileInitWorker(void) { return true; } +bool App::_fileInitWorker(void) { + _workerStatus.update(0, 4, WSTR("App.fileInitWorker.unmount")); + _fileIO.closeResourceFile(); + _fileIO.close(); + + _workerStatus.update(1, 4, WSTR("App.fileInitWorker.mount")); + _fileIO.initIDE(); + + _workerStatus.update(2, 4, WSTR("App.fileInitWorker.loadResources")); + if (_fileIO.loadResourceFile(EXTERNAL_DATA_DIR "/resource.zip")) + _loadResources(); + + return true; +} + struct Launcher { public: const char *path; diff --git a/src/main/app/modals.cpp b/src/main/app/modals.cpp index 39f195a..7c6a7b1 100644 --- a/src/main/app/modals.cpp +++ b/src/main/app/modals.cpp @@ -135,20 +135,14 @@ const char *FilePickerScreen::_getItemName(ui::Context &ctx, int index) const { auto &dev = ide::devices[drive]; auto fs = APP->_fileIO.ide[drive]; - if (dev.flags & ide::DEVICE_ATAPI) - name[0] = CH_CDROM_ICON; - else - name[0] = CH_HDD_ICON; - - name[1] = ' '; - - if (fs) - snprintf( - &name[2], sizeof(name) - 2, "%s: %s", dev.model, fs->volumeLabel - ); - else - __builtin_strncpy(&name[2], dev.model, sizeof(name) - 2); + auto icon = (dev.flags & ide::DEVICE_ATAPI) + ? CH_CDROM_ICON + : CH_HDD_ICON; + auto label = fs + ? fs->volumeLabel + : STR("FilePickerScreen.noFS"); + snprintf(name, sizeof(name), "%c %s: %s", icon, dev.model, label); return name; } diff --git a/src/main/cart.hpp b/src/main/cart.hpp index 65dac12..6cc1d9d 100644 --- a/src/main/cart.hpp +++ b/src/main/cart.hpp @@ -33,7 +33,7 @@ static constexpr size_t MAX_QR_STRING_LENGTH = 0x600; /* Identifier structure */ -class [[gnu::packed]] Identifier { +class Identifier { public: uint8_t data[8]; @@ -72,7 +72,7 @@ public: extern const ChipSize CHIP_SIZES[NUM_CHIP_TYPES]; -class [[gnu::packed]] CartDump { +class CartDump { public: uint16_t magic; ChipType chipType; @@ -131,7 +131,7 @@ public: /* Flash and RTC header dump structure */ -class [[gnu::packed]] ROMHeaderDump { +class ROMHeaderDump { public: uint16_t magic; uint8_t _reserved, flags; diff --git a/src/main/cartdata.hpp b/src/main/cartdata.hpp index 48bce14..b5f2a80 100644 --- a/src/main/cartdata.hpp +++ b/src/main/cartdata.hpp @@ -53,7 +53,7 @@ static constexpr size_t REGION_MAX_LENGTH = 5; /* Common data structures */ -class [[gnu::packed]] IdentifierSet { +class IdentifierSet { public: Identifier traceID, cartID, installID, systemID; // aka TID, SID, MID, XID @@ -64,7 +64,7 @@ public: ); }; -class [[gnu::packed]] PublicIdentifierSet { +class PublicIdentifierSet { public: Identifier installID, systemID; // aka MID, XID @@ -72,12 +72,12 @@ public: void setInstallID(uint8_t prefix); }; -class [[gnu::packed]] SimpleHeader { +class SimpleHeader { public: char region[4]; }; -class [[gnu::packed]] BasicHeader { +class BasicHeader { public: char region[2], codePrefix[2]; uint8_t checksum, _pad[3]; @@ -86,7 +86,7 @@ public: bool validateChecksum(bool invert = false) const; }; -class [[gnu::packed]] ExtendedHeader { +class ExtendedHeader { public: char code[8]; uint16_t year; // BCD, can be little endian, big endian or zero @@ -245,7 +245,7 @@ ROMHeaderParser *newROMHeaderParser(ROMHeaderDump &dump); /* Cartridge and flash header database */ -class [[gnu::packed]] CartDBEntry { +class CartDBEntry { public: ChipType chipType; FormatType formatType; @@ -287,7 +287,7 @@ public: } }; -class [[gnu::packed]] ROMHeaderDBEntry { +class ROMHeaderDBEntry { public: FormatType formatType; uint8_t flags; diff --git a/src/main/main.cpp b/src/main/main.cpp index 6ca51ba..95b032e 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -21,9 +21,7 @@ int main(int argc, const char **argv) { for (; argc > 0; argc--) args.parseArgument(*(argv++)); -#ifdef ENABLE_LOGGING util::logger.setupSyslog(args.baudRate); -#endif // A pointer to the resource archive is always provided on the command line // by the boot stub. @@ -42,7 +40,7 @@ int main(int argc, const char **argv) { io::resetIDEDevices(); gpu::enableDisplay(true); - spu::setMasterVolume(spu::MAX_VOLUME); + spu::setMasterVolume(spu::MAX_VOLUME / 2); io::setMiscOutput(io::MISC_SPU_ENABLE, true); app->run(args.resourcePtr, args.resourceLength); diff --git a/src/main/uibase.cpp b/src/main/uibase.cpp index 9ffb1ed..cc1fe64 100644 --- a/src/main/uibase.cpp +++ b/src/main/uibase.cpp @@ -13,40 +13,57 @@ namespace ui { /* Button state manager */ static const uint32_t _BUTTON_MAPPINGS[NUM_BUTTON_MAPS][NUM_BUTTONS]{ - { // MAP_JOYSTICK - io::JAMMA_P1_LEFT | io::JAMMA_P1_UP | io::JAMMA_P2_LEFT | io::JAMMA_P2_UP, - io::JAMMA_P1_RIGHT | io::JAMMA_P1_DOWN | io::JAMMA_P2_RIGHT | io::JAMMA_P2_DOWN, - io::JAMMA_P1_START | io::JAMMA_P1_BUTTON1 | io::JAMMA_P2_START | io::JAMMA_P2_BUTTON1, + { + // MAP_JOYSTICK + 0 + | io::JAMMA_P1_LEFT + | io::JAMMA_P2_LEFT + | io::JAMMA_P1_UP + | io::JAMMA_P2_UP, + 0 + | io::JAMMA_P1_RIGHT + | io::JAMMA_P2_RIGHT + | io::JAMMA_P1_DOWN + | io::JAMMA_P2_DOWN, + 0 + | io::JAMMA_P1_START + | io::JAMMA_P2_START + | io::JAMMA_P1_BUTTON1 + | io::JAMMA_P2_BUTTON1, io::JAMMA_TEST | io::JAMMA_SERVICE - }, - { // MAP_DDR_CAB + }, { + // MAP_DDR_CAB io::JAMMA_P1_BUTTON2 | io::JAMMA_P2_BUTTON2, io::JAMMA_P1_BUTTON3 | io::JAMMA_P2_BUTTON3, - io::JAMMA_P1_START | io::JAMMA_P2_START, - io::JAMMA_TEST | io::JAMMA_SERVICE - }, - { // MAP_DDR_SOLO_CAB + io::JAMMA_P1_START | io::JAMMA_P2_START, + io::JAMMA_TEST | io::JAMMA_SERVICE + }, { + // MAP_DDR_SOLO_CAB io::JAMMA_P1_BUTTON5, io::JAMMA_P2_BUTTON5, io::JAMMA_P1_START, io::JAMMA_TEST | io::JAMMA_SERVICE - }, - { // MAP_DM_CAB + }, { + // MAP_DM_CAB io::JAMMA_P2_LEFT, io::JAMMA_P2_RIGHT, io::JAMMA_P1_START, io::JAMMA_TEST | io::JAMMA_SERVICE - }, - { // MAP_DMX_CAB (more or less redundant with MAP_JOYSTICK) - io::JAMMA_P1_UP | io::JAMMA_P2_UP, - io::JAMMA_P1_DOWN | io::JAMMA_P2_DOWN, + }, { + // MAP_DMX_CAB (more or less redundant with MAP_JOYSTICK) + io::JAMMA_P1_UP | io::JAMMA_P2_UP, + io::JAMMA_P1_DOWN | io::JAMMA_P2_DOWN, io::JAMMA_P1_START | io::JAMMA_P2_START, - io::JAMMA_TEST | io::JAMMA_SERVICE - }, - { // MAP_SINGLE_BUTTON + io::JAMMA_TEST | io::JAMMA_SERVICE + }, { + // MAP_SINGLE_BUTTON 0, 0, - io::JAMMA_P1_START | io::JAMMA_P2_START | io::JAMMA_TEST | io::JAMMA_SERVICE, + 0 + | io::JAMMA_P1_START + | io::JAMMA_P2_START + | io::JAMMA_TEST + | io::JAMMA_SERVICE, 0 } };