mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-02-02 12:37:19 +01:00
Add hardware test suite, fix up IDE driver again
This commit is contained in:
parent
82e95ab8d5
commit
db5eaabe57
@ -127,6 +127,7 @@ addExecutable(
|
|||||||
src/main/app/modals.cpp
|
src/main/app/modals.cpp
|
||||||
src/main/app/romactions.cpp
|
src/main/app/romactions.cpp
|
||||||
src/main/app/romworkers.cpp
|
src/main/app/romworkers.cpp
|
||||||
|
src/main/app/tests.cpp
|
||||||
src/vendor/ff.c
|
src/vendor/ff.c
|
||||||
src/vendor/ffunicode.c
|
src/vendor/ffunicode.c
|
||||||
src/vendor/miniz.c
|
src/vendor/miniz.c
|
||||||
|
@ -111,6 +111,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"AudioTestScreen": {
|
||||||
|
"title": "{RIGHT_ARROW} Audio output test",
|
||||||
|
"prompt": "Note that the speaker amplifier and analog audio passthrough are disabled by default and will be disabled again once this screen is closed.",
|
||||||
|
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back",
|
||||||
|
|
||||||
|
"playLeft": "Play sound on left channel",
|
||||||
|
"playRight": "Play sound on right channel",
|
||||||
|
"playBoth": "Play sound on both channels",
|
||||||
|
"enableAmp": "Turn on built-in speaker amplifier",
|
||||||
|
"disableAmp": "Turn off built-in speaker amplifier",
|
||||||
|
"enableCDDA": "Unmute CD-DA/MP3 analog audio passthrough",
|
||||||
|
"disableCDDA": "Mute CD-DA/MP3 analog audio passthrough"
|
||||||
|
},
|
||||||
|
|
||||||
"AutobootScreen": {
|
"AutobootScreen": {
|
||||||
"title": "Note",
|
"title": "Note",
|
||||||
"body": "A valid boot executable has been found and will be launched shortly. You may disable automatic booting by turning off DIP switch 1 or creating a file named noboot.txt in the root of the filesystem.\n\nFile: %s",
|
"body": "A valid boot executable has been found and will be launched shortly. You may disable automatic booting by turning off DIP switch 1 or creating a file named noboot.txt in the root of the filesystem.\n\nFile: %s",
|
||||||
@ -262,6 +276,16 @@
|
|||||||
"description": "All checksums are computed using the standard CRC32 algorithm and parameters (polynomial 04C11DB7, initial value FFFFFFFF, input and output reflected, output bits negated)."
|
"description": "All checksums are computed using the standard CRC32 algorithm and parameters (polynomial 04C11DB7, initial value FFFFFFFF, input and output reflected, output bits negated)."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"ColorIntensityScreen": {
|
||||||
|
"title": "{RIGHT_ARROW} Monitor color intensity test",
|
||||||
|
"prompt": "{RIGHT_ARROW} Press {START_BUTTON} to go back.",
|
||||||
|
|
||||||
|
"white": "White",
|
||||||
|
"red": "Red",
|
||||||
|
"green": "Green",
|
||||||
|
"blue": "Blue"
|
||||||
|
},
|
||||||
|
|
||||||
"ConfirmScreen": {
|
"ConfirmScreen": {
|
||||||
"title": "Confirm operation",
|
"title": "Confirm operation",
|
||||||
"no": "No, go back",
|
"no": "No, go back",
|
||||||
@ -288,6 +312,11 @@
|
|||||||
"noFilesError": "No files or directories have been found in the root of the selected drive's filesystem."
|
"noFilesError": "No files or directories have been found in the root of the selected drive's filesystem."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"GeometryScreen": {
|
||||||
|
"title": "{RIGHT_ARROW} Monitor geometry test",
|
||||||
|
"prompt": "{RIGHT_ARROW} Press {START_BUTTON} to go back."
|
||||||
|
},
|
||||||
|
|
||||||
"HexdumpScreen": {
|
"HexdumpScreen": {
|
||||||
"title": "{RIGHT_ARROW} Cartridge dump",
|
"title": "{RIGHT_ARROW} Cartridge dump",
|
||||||
"prompt": "{RIGHT_ARROW} Use {LEFT_BUTTON}{RIGHT_BUTTON} to scroll. Press {START_BUTTON} to go back."
|
"prompt": "{RIGHT_ARROW} Use {LEFT_BUTTON}{RIGHT_BUTTON} to scroll. Press {START_BUTTON} to go back."
|
||||||
@ -328,6 +357,47 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"JAMMATestScreen": {
|
||||||
|
"title": "{RIGHT_ARROW} JAMMA input test",
|
||||||
|
"prompt": "{RIGHT_ARROW} Press and hold {START_BUTTON} to go back.",
|
||||||
|
|
||||||
|
"noInputs": "No button is currently held down. If a button does not appear here when pressed, make sure the JAMMA harness is wired up correctly and the button is not damaged.\n",
|
||||||
|
"inputs": "The following buttons are currently held down:\n",
|
||||||
|
"inputsNote": "\nNote that in DDR cabinets each player's up and right inputs may appear to be stuck due to the presence of the stage I/O boards, which use the joystick inputs as a communication bus.\n",
|
||||||
|
|
||||||
|
"p1": {
|
||||||
|
"left": " Player 1 joystick left\t\tJAMMA pin 20\n",
|
||||||
|
"right": " Player 1 joystick right\t\tJAMMA pin 21\n",
|
||||||
|
"up": " Player 1 joystick up\t\tJAMMA pin 18\n",
|
||||||
|
"down": " Player 1 joystick down\t\tJAMMA pin 19\n",
|
||||||
|
"button1": " Player 1 button 1\t\tJAMMA pin 22\n",
|
||||||
|
"button2": " Player 1 button 2\t\tJAMMA pin 23\n",
|
||||||
|
"button3": " Player 1 button 3\t\tJAMMA pin 24\n",
|
||||||
|
"button4": " Player 1 button 4\t\tJAMMA pin 25\n",
|
||||||
|
"button5": " Player 1 button 5\t\tJAMMA pin 26\n",
|
||||||
|
"button6": " Player 1 button 6\n",
|
||||||
|
"start": " Player 1 start button ({START_BUTTON})\tJAMMA pin 17\n"
|
||||||
|
},
|
||||||
|
"p2": {
|
||||||
|
"left": " Player 2 joystick left\t\tJAMMA pin X\n",
|
||||||
|
"right": " Player 2 joystick right\t\tJAMMA pin Y\n",
|
||||||
|
"up": " Player 2 joystick up\t\tJAMMA pin V\n",
|
||||||
|
"down": " Player 2 joystick down\t\tJAMMA pin W\n",
|
||||||
|
"button1": " Player 2 button 1\t\tJAMMA pin Z\n",
|
||||||
|
"button2": " Player 2 button 2\t\tJAMMA pin a\n",
|
||||||
|
"button3": " Player 2 button 3\t\tJAMMA pin b\n",
|
||||||
|
"button4": " Player 2 button 4\t\tJAMMA pin c\n",
|
||||||
|
"button5": " Player 2 button 5\t\tJAMMA pin d\n",
|
||||||
|
"button6": " Player 2 button 6\n",
|
||||||
|
"start": " Player 2 start button ({START_BUTTON})\tJAMMA pin U\n"
|
||||||
|
},
|
||||||
|
|
||||||
|
"coin1": " Coin switch 1\t\t\tJAMMA pin 16\n",
|
||||||
|
"coin2": " Coin switch 2\t\t\tJAMMA pin T\n",
|
||||||
|
"test": " Test button\t\t\tJAMMA pin 15\n",
|
||||||
|
"service": " Service button\t\t\tJAMMA pin R\n"
|
||||||
|
},
|
||||||
|
|
||||||
"KeyEntryScreen": {
|
"KeyEntryScreen": {
|
||||||
"title": "Enter unlocking key",
|
"title": "Enter unlocking key",
|
||||||
"body": "Enter the 8-byte key this cartridge was last locked with.\n\nUse {LEFT_BUTTON}{RIGHT_BUTTON} to move the cursor, hold {START_BUTTON} and use {LEFT_BUTTON}{RIGHT_BUTTON} to edit the highlighted digit.",
|
"body": "Enter the 8-byte key this cartridge was last locked with.\n\nUse {LEFT_BUTTON}{RIGHT_BUTTON} to move the cursor, hold {START_BUTTON} and use {LEFT_BUTTON}{RIGHT_BUTTON} to edit the highlighted digit.",
|
||||||
@ -360,6 +430,10 @@
|
|||||||
"name": "Set RTC date and time",
|
"name": "Set RTC date and time",
|
||||||
"prompt": "Adjust the current date and time. Note that the time will not persist after a power cycle if the RTC's internal battery is empty."
|
"prompt": "Adjust the current date and time. Note that the time will not persist after a power cycle if the RTC's internal battery is empty."
|
||||||
},
|
},
|
||||||
|
"testMenu": {
|
||||||
|
"name": "Hardware test suite",
|
||||||
|
"prompt": "Display monitor test patterns, check if all inputs are functional or test the system and cabinet's hardware."
|
||||||
|
},
|
||||||
"setResolution": {
|
"setResolution": {
|
||||||
"name": "Change screen resolution",
|
"name": "Change screen resolution",
|
||||||
"prompt": "Switch to a different screen resolution and aspect ratio. Some monitors and upscalers may not support all resolutions."
|
"prompt": "Switch to a different screen resolution and aspect ratio. Some monitors and upscalers may not support all resolutions."
|
||||||
@ -549,6 +623,28 @@
|
|||||||
"ok": "Confirm"
|
"ok": "Confirm"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"TestMenuScreen": {
|
||||||
|
"title": "{RIGHT_ARROW} Hardware test suite",
|
||||||
|
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back",
|
||||||
|
|
||||||
|
"jammaTest": {
|
||||||
|
"name": "Test JAMMA inputs",
|
||||||
|
"prompt": "Show the state of all buttons and inputs wired to the system's JAMMA connector."
|
||||||
|
},
|
||||||
|
"audioTest": {
|
||||||
|
"name": "Test audio output and speaker amplifier",
|
||||||
|
"prompt": "Play a sound on either audio output channel and test the system's onboard speaker amplification circuitry."
|
||||||
|
},
|
||||||
|
"colorIntensity": {
|
||||||
|
"name": "Check monitor color intensity levels",
|
||||||
|
"prompt": "Display a series of undithered and dithered color bars to help with monitor color level adjustment and calibration."
|
||||||
|
},
|
||||||
|
"geometry": {
|
||||||
|
"name": "Check monitor geometry and overscan",
|
||||||
|
"prompt": "Display a grid to help with monitor geometry adjustment. The grid's aspect ratio can be changed by switching to a different screen resolution from the main menu."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"UnlockKeyScreen": {
|
"UnlockKeyScreen": {
|
||||||
"title": "{RIGHT_ARROW} Select unlocking key",
|
"title": "{RIGHT_ARROW} Select unlocking key",
|
||||||
"prompt": "If the cartridge has been converted before, select the game it was last converted to. If it is currently blank, select 00-00-00-00-00-00-00-00.",
|
"prompt": "If the cartridge has been converted before, select the game it was last converted to. If it is currently blank, select 00-00-00-00-00-00-00-00.",
|
||||||
|
@ -98,7 +98,7 @@ bool ISOVolumeDesc::validateMagic(void) const {
|
|||||||
bool ISO9660File::_loadSector(uint32_t lba) {
|
bool ISO9660File::_loadSector(uint32_t lba) {
|
||||||
if (lba == _bufferedLBA)
|
if (lba == _bufferedLBA)
|
||||||
return true;
|
return true;
|
||||||
if (_device->read(_sectorBuffer, lba, 1))
|
if (_device->readData(_sectorBuffer, lba, 1))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_bufferedLBA = lba;
|
_bufferedLBA = lba;
|
||||||
@ -131,7 +131,7 @@ size_t ISO9660File::read(void *output, size_t length) {
|
|||||||
auto spanLength = numSectors * ide::ATAPI_SECTOR_SIZE;
|
auto spanLength = numSectors * ide::ATAPI_SECTOR_SIZE;
|
||||||
|
|
||||||
if (numSectors > 0) {
|
if (numSectors > 0) {
|
||||||
if (_device->read(currentPtr, lba, numSectors))
|
if (_device->readData(currentPtr, lba, numSectors))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
offset += spanLength;
|
offset += spanLength;
|
||||||
@ -204,7 +204,7 @@ bool ISO9660Provider::_readData(
|
|||||||
) {
|
) {
|
||||||
if (!output.allocate(numSectors * ide::ATAPI_SECTOR_SIZE))
|
if (!output.allocate(numSectors * ide::ATAPI_SECTOR_SIZE))
|
||||||
return false;
|
return false;
|
||||||
if (_device->read(output.ptr, lba, numSectors))
|
if (_device->readData(output.ptr, lba, numSectors))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -272,7 +272,7 @@ bool ISO9660Provider::init(int drive) {
|
|||||||
for (
|
for (
|
||||||
uint32_t lba = _VOLUME_DESC_START_LBA; lba < _VOLUME_DESC_END_LBA; lba++
|
uint32_t lba = _VOLUME_DESC_START_LBA; lba < _VOLUME_DESC_END_LBA; lba++
|
||||||
) {
|
) {
|
||||||
if (_device->read(&pvd, lba, 1))
|
if (_device->readData(&pvd, lba, 1))
|
||||||
return false;
|
return false;
|
||||||
if (!pvd.validateMagic()) {
|
if (!pvd.validateMagic()) {
|
||||||
LOG("invalid ISO descriptor, lba=0x%x", lba);
|
LOG("invalid ISO descriptor, lba=0x%x", lba);
|
||||||
|
@ -298,7 +298,7 @@ extern "C" DRESULT disk_read(
|
|||||||
|
|
||||||
if (!(dev.flags & ide::DEVICE_READY))
|
if (!(dev.flags & ide::DEVICE_READY))
|
||||||
return RES_NOTRDY;
|
return RES_NOTRDY;
|
||||||
if (dev.read(data, lba, count))
|
if (dev.readData(data, lba, count))
|
||||||
return RES_ERROR;
|
return RES_ERROR;
|
||||||
|
|
||||||
return RES_OK;
|
return RES_OK;
|
||||||
@ -313,7 +313,7 @@ extern "C" DRESULT disk_write(
|
|||||||
return RES_NOTRDY;
|
return RES_NOTRDY;
|
||||||
if (dev.flags & ide::DEVICE_READ_ONLY)
|
if (dev.flags & ide::DEVICE_READ_ONLY)
|
||||||
return RES_WRPRT;
|
return RES_WRPRT;
|
||||||
if (dev.write(data, lba, count))
|
if (dev.writeData(data, lba, count))
|
||||||
return RES_ERROR;
|
return RES_ERROR;
|
||||||
|
|
||||||
return RES_OK;
|
return RES_OK;
|
||||||
|
@ -22,8 +22,10 @@
|
|||||||
|
|
||||||
namespace ide {
|
namespace ide {
|
||||||
|
|
||||||
static constexpr int _WAIT_TIMEOUT = 30000000;
|
static constexpr int _COMMAND_TIMEOUT = 30000000;
|
||||||
static constexpr int _DETECT_TIMEOUT = 500000;
|
static constexpr int _REQUEST_SENSE_TIMEOUT = 500000;
|
||||||
|
static constexpr int _DETECT_DRIVE_TIMEOUT = 500000;
|
||||||
|
|
||||||
static constexpr int _DMA_TIMEOUT = 10000;
|
static constexpr int _DMA_TIMEOUT = 10000;
|
||||||
static constexpr int _SRST_DELAY = 5000;
|
static constexpr int _SRST_DELAY = 5000;
|
||||||
|
|
||||||
@ -52,6 +54,8 @@ const char *const DEVICE_ERROR_NAMES[]{
|
|||||||
"NO_DRIVE",
|
"NO_DRIVE",
|
||||||
"STATUS_TIMEOUT",
|
"STATUS_TIMEOUT",
|
||||||
"DRIVE_ERROR",
|
"DRIVE_ERROR",
|
||||||
|
"DISC_ERROR",
|
||||||
|
"DISC_CHANGED",
|
||||||
"INCOMPLETE_DATA",
|
"INCOMPLETE_DATA",
|
||||||
"CHECKSUM_MISMATCH"
|
"CHECKSUM_MISMATCH"
|
||||||
};
|
};
|
||||||
@ -120,42 +124,19 @@ int IdentifyBlock::getHighestPIOMode(void) const {
|
|||||||
|
|
||||||
Device devices[2]{ (DEVICE_PRIMARY), (DEVICE_SECONDARY) };
|
Device devices[2]{ (DEVICE_PRIMARY), (DEVICE_SECONDARY) };
|
||||||
|
|
||||||
void Device::_setLBA(uint64_t lba, size_t count) {
|
|
||||||
if (flags & DEVICE_HAS_LBA48) {
|
|
||||||
//assert(lba < (1ULL << 48));
|
|
||||||
//assert(count <= (1 << 16));
|
|
||||||
|
|
||||||
_select(CS0_DEVICE_SEL_LBA);
|
|
||||||
|
|
||||||
_write(CS0_COUNT, (count >> 8) & 0xff);
|
|
||||||
_write(CS0_SECTOR, (lba >> 24) & 0xff);
|
|
||||||
_write(CS0_CYLINDER_L, (lba >> 32) & 0xff);
|
|
||||||
_write(CS0_CYLINDER_H, (lba >> 40) & 0xff);
|
|
||||||
} else {
|
|
||||||
//assert(lba < (1ULL << 28));
|
|
||||||
//assert(count <= (1 << 8));
|
|
||||||
|
|
||||||
_select(CS0_DEVICE_SEL_LBA | ((lba >> 24) & 15));
|
|
||||||
}
|
|
||||||
|
|
||||||
_write(CS0_COUNT, (count >> 0) & 0xff);
|
|
||||||
_write(CS0_SECTOR, (lba >> 0) & 0xff);
|
|
||||||
_write(CS0_CYLINDER_L, (lba >> 8) & 0xff);
|
|
||||||
_write(CS0_CYLINDER_H, (lba >> 16) & 0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceError Device::_waitForStatus(
|
DeviceError Device::_waitForStatus(
|
||||||
uint8_t mask, uint8_t value, int timeout, bool ignoreErrors
|
uint8_t mask, uint8_t value, int timeout, bool ignoreErrors
|
||||||
) {
|
) {
|
||||||
if (!timeout)
|
if (!timeout)
|
||||||
timeout = _WAIT_TIMEOUT;
|
timeout = _COMMAND_TIMEOUT;
|
||||||
|
|
||||||
for (; timeout > 0; timeout -= 10) {
|
for (; timeout > 0; timeout -= 10) {
|
||||||
uint8_t status = _read(CS0_STATUS);
|
uint8_t status = _read(CS0_STATUS);
|
||||||
|
|
||||||
if (!ignoreErrors && (status & CS0_STATUS_ERR)) {
|
if (!ignoreErrors && (status & CS0_STATUS_ERR)) {
|
||||||
LOG(
|
LOG(
|
||||||
"IDE error, stat=0x%02x, err=0x%02x", _read(CS0_STATUS),
|
"drive %d error, stat=0x%02x, err=0x%02x",
|
||||||
|
(flags / DEVICE_SECONDARY) & 1, _read(CS0_STATUS),
|
||||||
_read(CS0_ERROR)
|
_read(CS0_ERROR)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -173,26 +154,55 @@ DeviceError Device::_waitForStatus(
|
|||||||
}
|
}
|
||||||
|
|
||||||
LOG(
|
LOG(
|
||||||
"IDE timeout, stat=0x%02x, err=0x%02x", _read(CS0_STATUS),
|
"drive %d timeout, stat=0x%02x, err=0x%02x",
|
||||||
_read(CS0_ERROR)
|
(flags / DEVICE_SECONDARY) & 1, _read(CS0_STATUS), _read(CS0_ERROR)
|
||||||
);
|
);
|
||||||
|
|
||||||
_write(CS0_COMMAND, ATA_DEVICE_RESET);
|
_write(CS0_COMMAND, ATA_DEVICE_RESET);
|
||||||
return STATUS_TIMEOUT;
|
return STATUS_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::_command(
|
DeviceError Device::_select(
|
||||||
uint8_t cmd, uint8_t status, int timeout, bool ignoreErrors
|
uint8_t devSelFlags, int timeout, bool ignoreErrors
|
||||||
) {
|
) {
|
||||||
auto error = _waitForStatus(
|
if (flags & DEVICE_SECONDARY)
|
||||||
CS0_STATUS_BSY | status, status, timeout, ignoreErrors
|
_write(CS0_DEVICE_SEL, devSelFlags | CS0_DEVICE_SEL_SECONDARY);
|
||||||
);
|
else
|
||||||
|
_write(CS0_DEVICE_SEL, devSelFlags | CS0_DEVICE_SEL_PRIMARY);
|
||||||
|
|
||||||
|
return _waitForStatus(CS0_STATUS_BSY, 0, timeout, ignoreErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError Device::_setLBA(uint64_t lba, size_t count, int timeout) {
|
||||||
|
if (flags & DEVICE_HAS_LBA48) {
|
||||||
|
//assert(lba < (1ULL << 48));
|
||||||
|
//assert(count <= (1 << 16));
|
||||||
|
|
||||||
|
auto error = _select(CS0_DEVICE_SEL_LBA, timeout);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
_write(CS0_COMMAND, cmd);
|
_write(CS0_COUNT, (count >> 8) & 0xff);
|
||||||
return _waitForStatus(CS0_STATUS_BSY, 0, timeout);
|
_write(CS0_SECTOR, (lba >> 24) & 0xff);
|
||||||
|
_write(CS0_CYLINDER_L, (lba >> 32) & 0xff);
|
||||||
|
_write(CS0_CYLINDER_H, (lba >> 40) & 0xff);
|
||||||
|
} else {
|
||||||
|
//assert(lba < (1ULL << 28));
|
||||||
|
//assert(count <= (1 << 8));
|
||||||
|
|
||||||
|
auto error = _select(CS0_DEVICE_SEL_LBA | ((lba >> 24) & 15), timeout);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
_write(CS0_COUNT, (count >> 0) & 0xff);
|
||||||
|
_write(CS0_SECTOR, (lba >> 0) & 0xff);
|
||||||
|
_write(CS0_CYLINDER_L, (lba >> 8) & 0xff);
|
||||||
|
_write(CS0_CYLINDER_H, (lba >> 16) & 0xff);
|
||||||
|
|
||||||
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::_detectDrive(void) {
|
DeviceError Device::_detectDrive(void) {
|
||||||
@ -202,7 +212,11 @@ DeviceError Device::_detectDrive(void) {
|
|||||||
_write(CS1_DEVICE_CTRL, CS1_DEVICE_CTRL_IEN);
|
_write(CS1_DEVICE_CTRL, CS1_DEVICE_CTRL_IEN);
|
||||||
delayMicroseconds(_SRST_DELAY);
|
delayMicroseconds(_SRST_DELAY);
|
||||||
|
|
||||||
_select();
|
if (_select(0, _DETECT_DRIVE_TIMEOUT, true)) {
|
||||||
|
LOG("drive %d select timeout", (flags / DEVICE_SECONDARY) & 1);
|
||||||
|
return NO_DRIVE;
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef ENABLE_FULL_IDE_DRIVER
|
#ifndef ENABLE_FULL_IDE_DRIVER
|
||||||
io::clearWatchdog();
|
io::clearWatchdog();
|
||||||
#endif
|
#endif
|
||||||
@ -211,7 +225,7 @@ DeviceError Device::_detectDrive(void) {
|
|||||||
// the written value. This should not fail even if the drive is busy.
|
// the written value. This should not fail even if the drive is busy.
|
||||||
uint8_t pattern = 0x55;
|
uint8_t pattern = 0x55;
|
||||||
|
|
||||||
for (int timeout = _DETECT_TIMEOUT; timeout > 0; timeout -= 10) {
|
for (int timeout = _DETECT_DRIVE_TIMEOUT; timeout > 0; timeout -= 100) {
|
||||||
_write(CS0_COUNT, pattern);
|
_write(CS0_COUNT, pattern);
|
||||||
|
|
||||||
// Note that ATA drives will also assert DRDY when ready, but ATAPI
|
// Note that ATA drives will also assert DRDY when ready, but ATAPI
|
||||||
@ -223,7 +237,7 @@ DeviceError Device::_detectDrive(void) {
|
|||||||
if (!(pattern & 1))
|
if (!(pattern & 1))
|
||||||
pattern |= 1 << 7;
|
pattern |= 1 << 7;
|
||||||
|
|
||||||
delayMicroseconds(10);
|
delayMicroseconds(100);
|
||||||
#ifndef ENABLE_FULL_IDE_DRIVER
|
#ifndef ENABLE_FULL_IDE_DRIVER
|
||||||
io::clearWatchdog();
|
io::clearWatchdog();
|
||||||
#endif
|
#endif
|
||||||
@ -233,10 +247,14 @@ DeviceError Device::_detectDrive(void) {
|
|||||||
return NO_DRIVE;
|
return NO_DRIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::_readPIO(void *data, size_t length, int timeout) {
|
DeviceError Device::_readPIO(
|
||||||
|
void *data, size_t length, int timeout, bool ignoreErrors
|
||||||
|
) {
|
||||||
util::assertAligned<uint16_t>(data);
|
util::assertAligned<uint16_t>(data);
|
||||||
|
|
||||||
auto error = _waitForStatus(CS0_STATUS_DRQ, CS0_STATUS_DRQ, timeout);
|
auto error = _waitForStatus(
|
||||||
|
CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout
|
||||||
|
);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
@ -249,10 +267,14 @@ DeviceError Device::_readPIO(void *data, size_t length, int timeout) {
|
|||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::_writePIO(const void *data, size_t length, int timeout) {
|
DeviceError Device::_writePIO(
|
||||||
|
const void *data, size_t length, int timeout, bool ignoreErrors
|
||||||
|
) {
|
||||||
util::assertAligned<uint16_t>(data);
|
util::assertAligned<uint16_t>(data);
|
||||||
|
|
||||||
auto error = _waitForStatus(CS0_STATUS_DRQ, CS0_STATUS_DRQ, timeout);
|
auto error = _waitForStatus(
|
||||||
|
CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout
|
||||||
|
);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
@ -265,12 +287,16 @@ DeviceError Device::_writePIO(const void *data, size_t length, int timeout) {
|
|||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::_readDMA(void *data, size_t length, int timeout) {
|
DeviceError Device::_readDMA(
|
||||||
|
void *data, size_t length, int timeout, bool ignoreErrors
|
||||||
|
) {
|
||||||
length /= 4;
|
length /= 4;
|
||||||
|
|
||||||
util::assertAligned<uint32_t>(data);
|
util::assertAligned<uint32_t>(data);
|
||||||
|
|
||||||
auto error = _waitForStatus(CS0_STATUS_DRQ, CS0_STATUS_DRQ, timeout);
|
auto error = _waitForStatus(
|
||||||
|
CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout
|
||||||
|
);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
@ -291,12 +317,16 @@ DeviceError Device::_readDMA(void *data, size_t length, int timeout) {
|
|||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::_writeDMA(const void *data, size_t length, int timeout) {
|
DeviceError Device::_writeDMA(
|
||||||
|
const void *data, size_t length, int timeout, bool ignoreErrors
|
||||||
|
) {
|
||||||
length /= 4;
|
length /= 4;
|
||||||
|
|
||||||
util::assertAligned<uint32_t>(data);
|
util::assertAligned<uint32_t>(data);
|
||||||
|
|
||||||
auto error = _waitForStatus(CS0_STATUS_DRQ, CS0_STATUS_DRQ, timeout);
|
auto error = _waitForStatus(
|
||||||
|
CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout
|
||||||
|
);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
@ -334,12 +364,13 @@ DeviceError Device::_ideReadWrite(
|
|||||||
while (count) {
|
while (count) {
|
||||||
size_t chunkLength = util::min(count, maxLength);
|
size_t chunkLength = util::min(count, maxLength);
|
||||||
|
|
||||||
_setLBA(lba, chunkLength);
|
auto error = _setLBA(lba, chunkLength);
|
||||||
auto error = _command(cmd, CS0_STATUS_DRDY);
|
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
_write(CS0_COMMAND, cmd);
|
||||||
|
|
||||||
// Data must be transferred one sector at a time as the drive may
|
// Data must be transferred one sector at a time as the drive may
|
||||||
// deassert DRQ between sectors.
|
// deassert DRQ between sectors.
|
||||||
for (size_t i = chunkLength; i; i--) {
|
for (size_t i = chunkLength; i; i--) {
|
||||||
@ -359,7 +390,7 @@ DeviceError Device::_ideReadWrite(
|
|||||||
}
|
}
|
||||||
|
|
||||||
error = _waitForStatus(
|
error = _waitForStatus(
|
||||||
CS0_STATUS_BSY | CS0_STATUS_DRDY, CS0_STATUS_DRDY
|
CS0_STATUS_DRDY | CS0_STATUS_BSY, CS0_STATUS_DRDY
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
@ -376,7 +407,7 @@ DeviceError Device::_atapiRead(uintptr_t ptr, uint32_t lba, size_t count) {
|
|||||||
Packet packet;
|
Packet packet;
|
||||||
|
|
||||||
packet.setRead(lba, count);
|
packet.setRead(lba, count);
|
||||||
auto error = atapiPacket(packet);
|
auto error = atapiPacket(packet, ATAPI_SECTOR_SIZE);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
@ -384,10 +415,8 @@ DeviceError Device::_atapiRead(uintptr_t ptr, uint32_t lba, size_t count) {
|
|||||||
// Data must be transferred one sector at a time as the drive may deassert
|
// Data must be transferred one sector at a time as the drive may deassert
|
||||||
// DRQ between sectors.
|
// DRQ between sectors.
|
||||||
for (; count; count--) {
|
for (; count; count--) {
|
||||||
error = _readPIO(reinterpret_cast<void *>(ptr), ATAPI_SECTOR_SIZE);
|
if (_readPIO(reinterpret_cast<void *>(ptr), ATAPI_SECTOR_SIZE))
|
||||||
|
return atapiPoll();
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
ptr += ATAPI_SECTOR_SIZE;
|
ptr += ATAPI_SECTOR_SIZE;
|
||||||
}
|
}
|
||||||
@ -411,16 +440,18 @@ DeviceError Device::enumerate(void) {
|
|||||||
// to prevent blocking for too long.
|
// to prevent blocking for too long.
|
||||||
IdentifyBlock block;
|
IdentifyBlock block;
|
||||||
|
|
||||||
|
//_select();
|
||||||
|
|
||||||
if ((_read(CS0_CYLINDER_L) == 0x14) && (_read(CS0_CYLINDER_H) == 0xeb)) {
|
if ((_read(CS0_CYLINDER_L) == 0x14) && (_read(CS0_CYLINDER_H) == 0xeb)) {
|
||||||
flags |= DEVICE_ATAPI;
|
flags |= DEVICE_ATAPI;
|
||||||
|
|
||||||
if (_command(ATA_IDENTIFY_PACKET, 0, _DETECT_TIMEOUT))
|
_write(CS0_COMMAND, ATA_IDENTIFY_PACKET);
|
||||||
return NO_DRIVE;
|
|
||||||
if (_readPIO(&block, sizeof(IdentifyBlock), _DETECT_TIMEOUT))
|
|
||||||
return NO_DRIVE;
|
|
||||||
|
|
||||||
|
if (_readPIO(&block, sizeof(IdentifyBlock), _DETECT_DRIVE_TIMEOUT))
|
||||||
|
return NO_DRIVE;
|
||||||
if (!block.validateChecksum())
|
if (!block.validateChecksum())
|
||||||
return CHECKSUM_MISMATCH;
|
return CHECKSUM_MISMATCH;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(block.deviceFlags & IDENTIFY_DEV_ATAPI_TYPE_BITMASK)
|
(block.deviceFlags & IDENTIFY_DEV_ATAPI_TYPE_BITMASK)
|
||||||
== IDENTIFY_DEV_ATAPI_TYPE_CDROM
|
== IDENTIFY_DEV_ATAPI_TYPE_CDROM
|
||||||
@ -432,13 +463,13 @@ DeviceError Device::enumerate(void) {
|
|||||||
)
|
)
|
||||||
flags |= DEVICE_HAS_PACKET16;
|
flags |= DEVICE_HAS_PACKET16;
|
||||||
} else {
|
} else {
|
||||||
if (_command(ATA_IDENTIFY, CS0_STATUS_DRDY, _DETECT_TIMEOUT))
|
_write(CS0_COMMAND, ATA_IDENTIFY);
|
||||||
return NO_DRIVE;
|
|
||||||
if (_readPIO(&block, sizeof(IdentifyBlock), _DETECT_TIMEOUT))
|
|
||||||
return NO_DRIVE;
|
|
||||||
|
|
||||||
|
if (_readPIO(&block, sizeof(IdentifyBlock), _DETECT_DRIVE_TIMEOUT))
|
||||||
|
return NO_DRIVE;
|
||||||
if (!block.validateChecksum())
|
if (!block.validateChecksum())
|
||||||
return CHECKSUM_MISMATCH;
|
return CHECKSUM_MISMATCH;
|
||||||
|
|
||||||
if (block.commandSetFlags[1] & (1 << 10)) {
|
if (block.commandSetFlags[1] & (1 << 10)) {
|
||||||
flags |= DEVICE_HAS_LBA48;
|
flags |= DEVICE_HAS_LBA48;
|
||||||
capacity = block.getSectorCountExt();
|
capacity = block.getSectorCountExt();
|
||||||
@ -462,8 +493,9 @@ DeviceError Device::enumerate(void) {
|
|||||||
|
|
||||||
_write(CS0_FEATURES, FEATURE_TRANSFER_MODE);
|
_write(CS0_FEATURES, FEATURE_TRANSFER_MODE);
|
||||||
_write(CS0_COUNT, (1 << 3) | mode);
|
_write(CS0_COUNT, (1 << 3) | mode);
|
||||||
|
_write(CS0_COMMAND, ATA_SET_FEATURES);
|
||||||
|
|
||||||
error = _command(ATA_SET_FEATURES, 0);
|
error = _waitForStatus(CS0_STATUS_BSY, 0);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
@ -479,22 +511,29 @@ DeviceError Device::atapiPacket(const Packet &packet, size_t transferLength) {
|
|||||||
if (!(flags & DEVICE_ATAPI))
|
if (!(flags & DEVICE_ATAPI))
|
||||||
return UNSUPPORTED_OP;
|
return UNSUPPORTED_OP;
|
||||||
|
|
||||||
_select();
|
auto error = _select(0);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return atapiPoll();
|
||||||
|
|
||||||
_write(CS0_CYLINDER_L, (transferLength >> 0) & 0xff);
|
_write(CS0_CYLINDER_L, (transferLength >> 0) & 0xff);
|
||||||
_write(CS0_CYLINDER_H, (transferLength >> 8) & 0xff);
|
_write(CS0_CYLINDER_H, (transferLength >> 8) & 0xff);
|
||||||
|
_write(CS0_COMMAND, ATA_PACKET);
|
||||||
|
|
||||||
auto error = _command(ATA_PACKET, 0);
|
|
||||||
|
|
||||||
if (!error)
|
|
||||||
error = _writePIO(&packet, (flags & DEVICE_HAS_PACKET16) ? 16 : 12);
|
error = _writePIO(&packet, (flags & DEVICE_HAS_PACKET16) ? 16 : 12);
|
||||||
if (!error)
|
|
||||||
return _waitForStatus(CS0_STATUS_BSY, 0);
|
|
||||||
|
|
||||||
|
if (error)
|
||||||
return atapiPoll();
|
return atapiPoll();
|
||||||
|
|
||||||
|
return _waitForStatus(CS0_STATUS_BSY, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::atapiPoll(void) {
|
DeviceError Device::atapiPoll(void) {
|
||||||
|
if (!(flags & DEVICE_READY))
|
||||||
|
return NO_DRIVE;
|
||||||
|
if (!(flags & DEVICE_ATAPI))
|
||||||
|
return UNSUPPORTED_OP;
|
||||||
|
|
||||||
Packet packet;
|
Packet packet;
|
||||||
SenseData data;
|
SenseData data;
|
||||||
|
|
||||||
@ -502,14 +541,23 @@ DeviceError Device::atapiPoll(void) {
|
|||||||
|
|
||||||
// If an error occurs, the error flag in the status register will be set but
|
// If an error occurs, the error flag in the status register will be set but
|
||||||
// the drive will still accept a request sense command.
|
// the drive will still accept a request sense command.
|
||||||
auto error = _command(ATA_PACKET, 0, 0, true);
|
auto error = _select(0, _REQUEST_SENSE_TIMEOUT, true);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
_write(CS0_CYLINDER_L, (sizeof(data) >> 0) & 0xff);
|
||||||
|
_write(CS0_CYLINDER_H, (sizeof(data) >> 8) & 0xff);
|
||||||
|
_write(CS0_COMMAND, ATA_PACKET);
|
||||||
|
|
||||||
|
error = _writePIO(
|
||||||
|
&packet, (flags & DEVICE_HAS_PACKET16) ? 16 : 12, _REQUEST_SENSE_TIMEOUT
|
||||||
|
);
|
||||||
|
|
||||||
|
//if (!error)
|
||||||
|
//error = _waitForStatus(CS0_STATUS_BSY, 0, _REQUEST_SENSE_TIMEOUT);
|
||||||
if (!error)
|
if (!error)
|
||||||
error = _writePIO(&packet, (flags & DEVICE_HAS_PACKET16) ? 16 : 12);
|
error = _readPIO(&data, sizeof(data), _REQUEST_SENSE_TIMEOUT);
|
||||||
if (!error)
|
|
||||||
error = _waitForStatus(CS0_STATUS_BSY, 0);
|
|
||||||
if (!error)
|
|
||||||
error = _readPIO(&data, sizeof(data));
|
|
||||||
|
|
||||||
int senseKey;
|
int senseKey;
|
||||||
|
|
||||||
@ -526,10 +574,11 @@ DeviceError Device::atapiPoll(void) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG("%s (%d)", _SENSE_KEY_NAMES[senseKey], senseKey);
|
LOG("sense key: %s (%d)", _SENSE_KEY_NAMES[senseKey], senseKey);
|
||||||
|
|
||||||
switch (senseKey) {
|
switch (senseKey) {
|
||||||
case SENSE_KEY_NO_SENSE:
|
case SENSE_KEY_NO_SENSE:
|
||||||
|
case SENSE_KEY_RECOVERED_ERROR:
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
|
|
||||||
case SENSE_KEY_NOT_READY:
|
case SENSE_KEY_NOT_READY:
|
||||||
@ -545,31 +594,23 @@ DeviceError Device::atapiPoll(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError Device::read(void *data, uint64_t lba, size_t count) {
|
DeviceError Device::readData(void *data, uint64_t lba, size_t count) {
|
||||||
util::assertAligned<uint32_t>(data);
|
util::assertAligned<uint32_t>(data);
|
||||||
|
|
||||||
if (!(flags & DEVICE_READY))
|
if (!(flags & DEVICE_READY))
|
||||||
return NO_DRIVE;
|
return NO_DRIVE;
|
||||||
|
|
||||||
if (flags & DEVICE_ATAPI) {
|
if (flags & DEVICE_ATAPI)
|
||||||
auto error = _atapiRead(
|
return _atapiRead(
|
||||||
reinterpret_cast<uintptr_t>(data), static_cast<uint32_t>(lba), count
|
reinterpret_cast<uintptr_t>(data), static_cast<uint32_t>(lba), count
|
||||||
);
|
);
|
||||||
|
else
|
||||||
#ifdef ENABLE_FULL_IDE_DRIVER
|
|
||||||
if (error)
|
|
||||||
error = atapiPoll();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return error;
|
|
||||||
} else {
|
|
||||||
return _ideReadWrite(
|
return _ideReadWrite(
|
||||||
reinterpret_cast<uintptr_t>(data), lba, count, false
|
reinterpret_cast<uintptr_t>(data), lba, count, false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
DeviceError Device::write(const void *data, uint64_t lba, size_t count) {
|
DeviceError Device::writeData(const void *data, uint64_t lba, size_t count) {
|
||||||
util::assertAligned<uint32_t>(data);
|
util::assertAligned<uint32_t>(data);
|
||||||
|
|
||||||
if (!(flags & DEVICE_READY))
|
if (!(flags & DEVICE_READY))
|
||||||
@ -590,25 +631,17 @@ DeviceError Device::goIdle(bool standby) {
|
|||||||
packet.setStartStopUnit(START_STOP_MODE_STOP_DISC);
|
packet.setStartStopUnit(START_STOP_MODE_STOP_DISC);
|
||||||
return atapiPacket(packet);
|
return atapiPacket(packet);
|
||||||
} else {
|
} else {
|
||||||
_select();
|
auto error = _select(CS0_DEVICE_SEL_LBA);
|
||||||
|
|
||||||
auto error = _command(
|
|
||||||
standby ? ATA_STANDBY_IMMEDIATE : ATA_IDLE_IMMEDIATE,
|
|
||||||
CS0_STATUS_DRDY
|
|
||||||
);
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (error) {
|
|
||||||
// If the immediate command failed, fall back to setting the
|
|
||||||
// inactivity timeout to the lowest allowed value (5 seconds).
|
|
||||||
// FIXME: the original timeout would have to be restored once the
|
|
||||||
// drive is accessed again
|
|
||||||
_write(CS0_COUNT, 1);
|
|
||||||
error = _command(standby ? ATA_STANDBY : ATA_IDLE, CS0_STATUS_DRDY);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
if (standby)
|
||||||
|
_write(CS0_COMMAND, ATA_STANDBY_IMMEDIATE);
|
||||||
|
else
|
||||||
|
_write(CS0_COMMAND, ATA_IDLE_IMMEDIATE);
|
||||||
|
|
||||||
|
return _waitForStatus(CS0_STATUS_BSY, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,12 +652,17 @@ DeviceError Device::flushCache(void) {
|
|||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
//return UNSUPPORTED_OP;
|
//return UNSUPPORTED_OP;
|
||||||
|
|
||||||
_select();
|
auto error = _select(CS0_DEVICE_SEL_LBA);
|
||||||
|
|
||||||
return _command(
|
if (error)
|
||||||
(flags & DEVICE_HAS_LBA48) ? ATA_FLUSH_CACHE_EXT : ATA_FLUSH_CACHE,
|
return error;
|
||||||
CS0_STATUS_DRDY
|
|
||||||
);
|
if (flags & DEVICE_HAS_LBA48)
|
||||||
|
_write(CS0_COMMAND, ATA_FLUSH_CACHE_EXT);
|
||||||
|
else
|
||||||
|
_write(CS0_COMMAND, ATA_FLUSH_CACHE);
|
||||||
|
|
||||||
|
return _waitForStatus(CS0_STATUS_DRDY | CS0_STATUS_BSY, CS0_STATUS_DRDY);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -370,26 +370,29 @@ private:
|
|||||||
SYS573_IDE_CS1_BASE[reg] = value;
|
SYS573_IDE_CS1_BASE[reg] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void _select(uint8_t regFlags = 0) {
|
|
||||||
if (flags & DEVICE_SECONDARY)
|
|
||||||
_write(CS0_DEVICE_SEL, regFlags | CS0_DEVICE_SEL_SECONDARY);
|
|
||||||
else
|
|
||||||
_write(CS0_DEVICE_SEL, regFlags | CS0_DEVICE_SEL_PRIMARY);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _setLBA(uint64_t lba, size_t count);
|
|
||||||
DeviceError _waitForStatus(
|
DeviceError _waitForStatus(
|
||||||
uint8_t mask, uint8_t value, int timeout = 0, bool ignoreErrors = false
|
uint8_t mask, uint8_t value, int timeout = 0, bool ignoreErrors = false
|
||||||
);
|
);
|
||||||
DeviceError _command(
|
DeviceError _select(
|
||||||
uint8_t cmd, uint8_t status, int timeout = 0, bool ignoreErrors = false
|
uint8_t devSelFlags, int timeout = 0, bool ignoreErrors = false
|
||||||
);
|
);
|
||||||
|
DeviceError _setLBA(uint64_t lba, size_t count, int timeout = 0);
|
||||||
DeviceError _detectDrive(void);
|
DeviceError _detectDrive(void);
|
||||||
|
|
||||||
DeviceError _readPIO(void *data, size_t length, int timeout = 0);
|
DeviceError _readPIO(
|
||||||
DeviceError _writePIO(const void *data, size_t length, int timeout = 0);
|
void *data, size_t length, int timeout = 0, bool ignoreErrors = false
|
||||||
DeviceError _readDMA(void *data, size_t length, int timeout = 0);
|
);
|
||||||
DeviceError _writeDMA(const void *data, size_t length, int timeout = 0);
|
DeviceError _writePIO(
|
||||||
|
const void *data, size_t length, int timeout = 0,
|
||||||
|
bool ignoreErrors = false
|
||||||
|
);
|
||||||
|
DeviceError _readDMA(
|
||||||
|
void *data, size_t length, int timeout = 0, bool ignoreErrors = false
|
||||||
|
);
|
||||||
|
DeviceError _writeDMA(
|
||||||
|
const void *data, size_t length, int timeout = 0,
|
||||||
|
bool ignoreErrors = false
|
||||||
|
);
|
||||||
|
|
||||||
DeviceError _ideReadWrite(
|
DeviceError _ideReadWrite(
|
||||||
uintptr_t ptr, uint64_t lba, size_t count, bool write
|
uintptr_t ptr, uint64_t lba, size_t count, bool write
|
||||||
@ -421,13 +424,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
DeviceError enumerate(void);
|
DeviceError enumerate(void);
|
||||||
DeviceError atapiPacket(
|
DeviceError atapiPacket(const Packet &packet, size_t transferLength = 0);
|
||||||
const Packet &packet, size_t transferLength = ATAPI_SECTOR_SIZE
|
|
||||||
);
|
|
||||||
DeviceError atapiPoll(void);
|
DeviceError atapiPoll(void);
|
||||||
|
|
||||||
DeviceError read(void *data, uint64_t lba, size_t count);
|
DeviceError readData(void *data, uint64_t lba, size_t count);
|
||||||
DeviceError write(const void *data, uint64_t lba, size_t count);
|
DeviceError writeData(const void *data, uint64_t lba, size_t count);
|
||||||
DeviceError goIdle(bool standby = false);
|
DeviceError goIdle(bool standby = false);
|
||||||
DeviceError flushCache(void);
|
DeviceError flushCache(void);
|
||||||
};
|
};
|
||||||
|
@ -165,14 +165,14 @@ bool Sound::initFromVAGHeader(const VAGHeader *header, uint32_t ramOffset) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Channel Sound::play(uint16_t volume, Channel ch) const {
|
Channel Sound::play(uint16_t left, uint16_t right, Channel ch) const {
|
||||||
if ((ch < 0) || (ch >= NUM_CHANNELS))
|
if ((ch < 0) || (ch >= NUM_CHANNELS))
|
||||||
return -1;
|
return -1;
|
||||||
if (!offset)
|
if (!offset)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
SPU_CH_VOL_L(ch) = volume;
|
SPU_CH_VOL_L(ch) = left;
|
||||||
SPU_CH_VOL_R(ch) = volume;
|
SPU_CH_VOL_R(ch) = right;
|
||||||
SPU_CH_FREQ (ch) = sampleRate;
|
SPU_CH_FREQ (ch) = sampleRate;
|
||||||
SPU_CH_ADDR (ch) = offset;
|
SPU_CH_ADDR (ch) = offset;
|
||||||
SPU_CH_ADSR1(ch) = 0x00ff;
|
SPU_CH_ADSR1(ch) = 0x00ff;
|
||||||
|
@ -24,12 +24,12 @@ static inline void setMasterVolume(uint16_t master, uint16_t reverb = 0) {
|
|||||||
SPU_REVERB_VOL_R = reverb;
|
SPU_REVERB_VOL_R = reverb;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void setChannelVolume(Channel ch, uint16_t volume) {
|
static inline void setChannelVolume(Channel ch, uint16_t left, uint16_t right) {
|
||||||
if ((ch < 0) || (ch >= NUM_CHANNELS))
|
if ((ch < 0) || (ch >= NUM_CHANNELS))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SPU_CH_VOL_L(ch) = volume;
|
SPU_CH_VOL_L(ch) = left;
|
||||||
SPU_CH_VOL_R(ch) = volume;
|
SPU_CH_VOL_R(ch) = right;
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(void);
|
void init(void);
|
||||||
@ -54,12 +54,14 @@ public:
|
|||||||
|
|
||||||
inline Sound(void)
|
inline Sound(void)
|
||||||
: offset(0), length(0) {}
|
: offset(0), length(0) {}
|
||||||
inline Channel play(uint16_t volume = MAX_VOLUME) const {
|
inline Channel play(
|
||||||
return play(volume, getFreeChannel());
|
uint16_t left = MAX_VOLUME, uint16_t right = MAX_VOLUME
|
||||||
|
) const {
|
||||||
|
return play(left, right, getFreeChannel());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool initFromVAGHeader(const VAGHeader *header, uint32_t ramOffset);
|
bool initFromVAGHeader(const VAGHeader *header, uint32_t ramOffset);
|
||||||
Channel play(uint16_t volume, Channel ch) const;
|
Channel play(uint16_t left, uint16_t right, Channel ch) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ int main(int argc, const char **argv) {
|
|||||||
args.parseArgument(*(argv++));
|
args.parseArgument(*(argv++));
|
||||||
|
|
||||||
#ifdef ENABLE_LOGGING
|
#ifdef ENABLE_LOGGING
|
||||||
util::logger.setupSyslog(launcher.args.baudRate);
|
util::logger.setupSyslog(args.baudRate);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!args.entryPoint || !args.loadAddress || !args.numFragments)
|
if (!args.entryPoint || !args.loadAddress || !args.numFragments)
|
||||||
@ -52,7 +52,7 @@ int main(int argc, const char **argv) {
|
|||||||
length -= skipSectors;
|
length -= skipSectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dev.read(reinterpret_cast<void *>(ptr), lba, length))
|
if (dev.readData(reinterpret_cast<void *>(ptr), lba, length))
|
||||||
return 3;
|
return 3;
|
||||||
|
|
||||||
io::clearWatchdog();
|
io::clearWatchdog();
|
||||||
|
@ -342,6 +342,7 @@ void App::_interruptHandler(void) {
|
|||||||
|
|
||||||
_runWorker(&App::_ideInitWorker, _warningScreen);
|
_runWorker(&App::_ideInitWorker, _warningScreen);
|
||||||
_setupInterrupts();
|
_setupInterrupts();
|
||||||
|
_ctx.sounds[ui::SOUND_STARTUP].play();
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
util::Date date;
|
util::Date date;
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "main/app/misc.hpp"
|
#include "main/app/misc.hpp"
|
||||||
#include "main/app/modals.hpp"
|
#include "main/app/modals.hpp"
|
||||||
#include "main/app/romactions.hpp"
|
#include "main/app/romactions.hpp"
|
||||||
|
#include "main/app/tests.hpp"
|
||||||
#include "main/cart.hpp"
|
#include "main/cart.hpp"
|
||||||
#include "main/cartdata.hpp"
|
#include "main/cartdata.hpp"
|
||||||
#include "main/cartio.hpp"
|
#include "main/cartio.hpp"
|
||||||
@ -55,7 +56,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
file::Provider *ide[util::countOf(ide::devices)];
|
file::Provider *ide[util::countOf(ide::devices)];
|
||||||
file::ZIPProvider resource;
|
file::ZIPProvider resource;
|
||||||
#ifndef NDEBUG
|
#ifdef ENABLE_PCDRV
|
||||||
file::HostProvider host;
|
file::HostProvider host;
|
||||||
#endif
|
#endif
|
||||||
file::VFSProvider vfs;
|
file::VFSProvider vfs;
|
||||||
@ -81,54 +82,84 @@ class App {
|
|||||||
friend class ConfirmScreen;
|
friend class ConfirmScreen;
|
||||||
friend class FilePickerScreen;
|
friend class FilePickerScreen;
|
||||||
friend class FileBrowserScreen;
|
friend class FileBrowserScreen;
|
||||||
friend class AutobootScreen;
|
|
||||||
friend class WarningScreen;
|
friend class WarningScreen;
|
||||||
|
friend class AutobootScreen;
|
||||||
friend class ButtonMappingScreen;
|
friend class ButtonMappingScreen;
|
||||||
friend class MainMenuScreen;
|
friend class MainMenuScreen;
|
||||||
friend class StorageInfoScreen;
|
|
||||||
friend class StorageActionsScreen;
|
|
||||||
friend class IDEInfoScreen;
|
|
||||||
friend class RTCTimeScreen;
|
|
||||||
friend class ResolutionScreen;
|
|
||||||
friend class AboutScreen;
|
|
||||||
friend class CartInfoScreen;
|
friend class CartInfoScreen;
|
||||||
friend class UnlockKeyScreen;
|
friend class UnlockKeyScreen;
|
||||||
friend class KeyEntryScreen;
|
friend class KeyEntryScreen;
|
||||||
|
|
||||||
friend class CartActionsScreen;
|
friend class CartActionsScreen;
|
||||||
friend class QRCodeScreen;
|
friend class QRCodeScreen;
|
||||||
friend class HexdumpScreen;
|
friend class HexdumpScreen;
|
||||||
friend class ReflashGameScreen;
|
friend class ReflashGameScreen;
|
||||||
friend class SystemIDEntryScreen;
|
friend class SystemIDEntryScreen;
|
||||||
|
|
||||||
|
friend class StorageInfoScreen;
|
||||||
|
friend class StorageActionsScreen;
|
||||||
friend class CardSizeScreen;
|
friend class CardSizeScreen;
|
||||||
friend class ChecksumScreen;
|
friend class ChecksumScreen;
|
||||||
|
|
||||||
|
friend class TestMenuScreen;
|
||||||
|
friend class JAMMATestScreen;
|
||||||
|
friend class AudioTestScreen;
|
||||||
|
friend class TestPatternScreen;
|
||||||
|
friend class ColorIntensityScreen;
|
||||||
|
friend class GeometryScreen;
|
||||||
|
|
||||||
|
friend class IDEInfoScreen;
|
||||||
|
friend class RTCTimeScreen;
|
||||||
|
friend class ResolutionScreen;
|
||||||
|
friend class AboutScreen;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// modals.cpp
|
||||||
WorkerStatusScreen _workerStatusScreen;
|
WorkerStatusScreen _workerStatusScreen;
|
||||||
MessageScreen _messageScreen;
|
MessageScreen _messageScreen;
|
||||||
ConfirmScreen _confirmScreen;
|
ConfirmScreen _confirmScreen;
|
||||||
FilePickerScreen _filePickerScreen;
|
FilePickerScreen _filePickerScreen;
|
||||||
FileBrowserScreen _fileBrowserScreen;
|
FileBrowserScreen _fileBrowserScreen;
|
||||||
AutobootScreen _autobootScreen;
|
|
||||||
|
// main.cpp
|
||||||
WarningScreen _warningScreen;
|
WarningScreen _warningScreen;
|
||||||
|
AutobootScreen _autobootScreen;
|
||||||
ButtonMappingScreen _buttonMappingScreen;
|
ButtonMappingScreen _buttonMappingScreen;
|
||||||
MainMenuScreen _mainMenuScreen;
|
MainMenuScreen _mainMenuScreen;
|
||||||
StorageInfoScreen _storageInfoScreen;
|
|
||||||
StorageActionsScreen _storageActionsScreen;
|
// cartunlock.cpp
|
||||||
IDEInfoScreen _ideInfoScreen;
|
|
||||||
RTCTimeScreen _rtcTimeScreen;
|
|
||||||
ResolutionScreen _resolutionScreen;
|
|
||||||
AboutScreen _aboutScreen;
|
|
||||||
CartInfoScreen _cartInfoScreen;
|
CartInfoScreen _cartInfoScreen;
|
||||||
UnlockKeyScreen _unlockKeyScreen;
|
UnlockKeyScreen _unlockKeyScreen;
|
||||||
KeyEntryScreen _keyEntryScreen;
|
KeyEntryScreen _keyEntryScreen;
|
||||||
|
|
||||||
|
// cartactions.cpp
|
||||||
CartActionsScreen _cartActionsScreen;
|
CartActionsScreen _cartActionsScreen;
|
||||||
QRCodeScreen _qrCodeScreen;
|
QRCodeScreen _qrCodeScreen;
|
||||||
HexdumpScreen _hexdumpScreen;
|
HexdumpScreen _hexdumpScreen;
|
||||||
ReflashGameScreen _reflashGameScreen;
|
ReflashGameScreen _reflashGameScreen;
|
||||||
SystemIDEntryScreen _systemIDEntryScreen;
|
SystemIDEntryScreen _systemIDEntryScreen;
|
||||||
|
|
||||||
|
// romactions.cpp
|
||||||
|
StorageInfoScreen _storageInfoScreen;
|
||||||
|
StorageActionsScreen _storageActionsScreen;
|
||||||
CardSizeScreen _cardSizeScreen;
|
CardSizeScreen _cardSizeScreen;
|
||||||
ChecksumScreen _checksumScreen;
|
ChecksumScreen _checksumScreen;
|
||||||
|
|
||||||
|
// tests.cpp
|
||||||
|
TestMenuScreen _testMenuScreen;
|
||||||
|
JAMMATestScreen _jammaTestScreen;
|
||||||
|
AudioTestScreen _audioTestScreen;
|
||||||
|
ColorIntensityScreen _colorIntensityScreen;
|
||||||
|
GeometryScreen _geometryScreen;
|
||||||
|
|
||||||
|
// misc.cpp
|
||||||
|
IDEInfoScreen _ideInfoScreen;
|
||||||
|
RTCTimeScreen _rtcTimeScreen;
|
||||||
|
ResolutionScreen _resolutionScreen;
|
||||||
|
AboutScreen _aboutScreen;
|
||||||
|
|
||||||
#ifdef ENABLE_LOG_BUFFER
|
#ifdef ENABLE_LOG_BUFFER
|
||||||
util::LogBuffer _logBuffer;
|
util::LogBuffer _logBuffer;
|
||||||
ui::LogOverlay _logOverlay;
|
ui::LogOverlay _logOverlay;
|
||||||
@ -166,6 +197,9 @@ private:
|
|||||||
bool playSound = false
|
bool playSound = false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void _worker(void);
|
||||||
|
void _interruptHandler(void);
|
||||||
|
|
||||||
// cartworkers.cpp
|
// cartworkers.cpp
|
||||||
bool _cartDetectWorker(void);
|
bool _cartDetectWorker(void);
|
||||||
bool _cartUnlockWorker(void);
|
bool _cartUnlockWorker(void);
|
||||||
@ -189,9 +223,6 @@ private:
|
|||||||
bool _atapiEjectWorker(void);
|
bool _atapiEjectWorker(void);
|
||||||
bool _rebootWorker(void);
|
bool _rebootWorker(void);
|
||||||
|
|
||||||
void _worker(void);
|
|
||||||
void _interruptHandler(void);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
App(ui::Context &ctx);
|
App(ui::Context &ctx);
|
||||||
~App(void);
|
~App(void);
|
||||||
|
@ -154,6 +154,10 @@ static const MenuEntry _MENU_ENTRIES[]{
|
|||||||
.name = "MainMenuScreen.setRTCTime.name"_h,
|
.name = "MainMenuScreen.setRTCTime.name"_h,
|
||||||
.prompt = "MainMenuScreen.setRTCTime.prompt"_h,
|
.prompt = "MainMenuScreen.setRTCTime.prompt"_h,
|
||||||
.target = &MainMenuScreen::setRTCTime
|
.target = &MainMenuScreen::setRTCTime
|
||||||
|
}, {
|
||||||
|
.name = "MainMenuScreen.testMenu.name"_h,
|
||||||
|
.prompt = "MainMenuScreen.testMenu.prompt"_h,
|
||||||
|
.target = &MainMenuScreen::testMenu
|
||||||
}, {
|
}, {
|
||||||
.name = "MainMenuScreen.setResolution.name"_h,
|
.name = "MainMenuScreen.setResolution.name"_h,
|
||||||
.prompt = "MainMenuScreen.setResolution.prompt"_h,
|
.prompt = "MainMenuScreen.setResolution.prompt"_h,
|
||||||
@ -215,6 +219,10 @@ void MainMenuScreen::setRTCTime(ui::Context &ctx) {
|
|||||||
ctx.show(APP->_rtcTimeScreen, false, true);
|
ctx.show(APP->_rtcTimeScreen, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainMenuScreen::testMenu(ui::Context &ctx) {
|
||||||
|
ctx.show(APP->_testMenuScreen, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
void MainMenuScreen::setResolution(ui::Context &ctx) {
|
void MainMenuScreen::setResolution(ui::Context &ctx) {
|
||||||
ctx.show(APP->_resolutionScreen, false, true);
|
ctx.show(APP->_resolutionScreen, false, true);
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ public:
|
|||||||
void ideInfo(ui::Context &ctx);
|
void ideInfo(ui::Context &ctx);
|
||||||
void runExecutable(ui::Context &ctx);
|
void runExecutable(ui::Context &ctx);
|
||||||
void setRTCTime(ui::Context &ctx);
|
void setRTCTime(ui::Context &ctx);
|
||||||
|
void testMenu(ui::Context &ctx);
|
||||||
void setResolution(ui::Context &ctx);
|
void setResolution(ui::Context &ctx);
|
||||||
void about(ui::Context &ctx);
|
void about(ui::Context &ctx);
|
||||||
void ejectCD(ui::Context &ctx);
|
void ejectCD(ui::Context &ctx);
|
||||||
|
@ -277,7 +277,7 @@ void AboutScreen::show(ui::Context &ctx, bool goBack) {
|
|||||||
ctx.time, 0, _LOOP_FADE_IN_VOLUME,
|
ctx.time, 0, _LOOP_FADE_IN_VOLUME,
|
||||||
ctx.gpuCtx.refreshRate * _LOOP_FADE_IN_TIME
|
ctx.gpuCtx.refreshRate * _LOOP_FADE_IN_TIME
|
||||||
);
|
);
|
||||||
_loopChannel = ctx.sounds[ui::SOUND_ABOUT_SCREEN].play(0);
|
_loopChannel = ctx.sounds[ui::SOUND_ABOUT_SCREEN].play(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AboutScreen::hide(ui::Context &ctx, bool goBack) {
|
void AboutScreen::hide(ui::Context &ctx, bool goBack) {
|
||||||
@ -290,7 +290,9 @@ void AboutScreen::hide(ui::Context &ctx, bool goBack) {
|
|||||||
|
|
||||||
void AboutScreen::update(ui::Context &ctx) {
|
void AboutScreen::update(ui::Context &ctx) {
|
||||||
TextScreen::update(ctx);
|
TextScreen::update(ctx);
|
||||||
spu::setChannelVolume(_loopChannel, _loopVolume.getValue(ctx.time));
|
|
||||||
|
auto volume = _loopVolume.getValue(ctx.time);
|
||||||
|
spu::setChannelVolume(_loopChannel, volume, volume);
|
||||||
|
|
||||||
if (ctx.buttons.pressed(ui::BTN_START))
|
if (ctx.buttons.pressed(ui::BTN_START))
|
||||||
ctx.show(APP->_mainMenuScreen, true, true);
|
ctx.show(APP->_mainMenuScreen, true, true);
|
||||||
|
@ -59,7 +59,6 @@ bool App::_ideInitWorker(void) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
_ctx.sounds[ui::SOUND_STARTUP].play();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
416
src/main/app/tests.cpp
Normal file
416
src/main/app/tests.cpp
Normal file
@ -0,0 +1,416 @@
|
|||||||
|
|
||||||
|
#include "common/gpu.hpp"
|
||||||
|
#include "common/io.hpp"
|
||||||
|
#include "common/spu.hpp"
|
||||||
|
#include "common/util.hpp"
|
||||||
|
#include "main/app/app.hpp"
|
||||||
|
#include "main/app/tests.hpp"
|
||||||
|
#include "main/uibase.hpp"
|
||||||
|
#include "main/uicommon.hpp"
|
||||||
|
#include "tests.hpp"
|
||||||
|
|
||||||
|
/* Top-level test menu */
|
||||||
|
|
||||||
|
struct TestMenuEntry {
|
||||||
|
public:
|
||||||
|
util::Hash name, prompt;
|
||||||
|
void (TestMenuScreen::*target)(ui::Context &ctx);
|
||||||
|
};
|
||||||
|
|
||||||
|
static const TestMenuEntry _TEST_MENU_ENTRIES[]{
|
||||||
|
{
|
||||||
|
.name = "TestMenuScreen.jammaTest.name"_h,
|
||||||
|
.prompt = "TestMenuScreen.jammaTest.prompt"_h,
|
||||||
|
.target = &TestMenuScreen::jammaTest
|
||||||
|
}, {
|
||||||
|
.name = "TestMenuScreen.audioTest.name"_h,
|
||||||
|
.prompt = "TestMenuScreen.audioTest.prompt"_h,
|
||||||
|
.target = &TestMenuScreen::audioTest
|
||||||
|
}, {
|
||||||
|
.name = "TestMenuScreen.colorIntensity.name"_h,
|
||||||
|
.prompt = "TestMenuScreen.colorIntensity.prompt"_h,
|
||||||
|
.target = &TestMenuScreen::colorIntensity
|
||||||
|
}, {
|
||||||
|
.name = "TestMenuScreen.geometry.name"_h,
|
||||||
|
.prompt = "TestMenuScreen.geometry.prompt"_h,
|
||||||
|
.target = &TestMenuScreen::geometry
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *TestMenuScreen::_getItemName(ui::Context &ctx, int index) const {
|
||||||
|
return STRH(_TEST_MENU_ENTRIES[index].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestMenuScreen::jammaTest(ui::Context &ctx) {
|
||||||
|
ctx.show(APP->_jammaTestScreen, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestMenuScreen::audioTest(ui::Context &ctx) {
|
||||||
|
ctx.show(APP->_audioTestScreen, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestMenuScreen::colorIntensity(ui::Context &ctx) {
|
||||||
|
ctx.show(APP->_colorIntensityScreen, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestMenuScreen::geometry(ui::Context &ctx) {
|
||||||
|
ctx.show(APP->_geometryScreen, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestMenuScreen::show(ui::Context &ctx, bool goBack) {
|
||||||
|
_title = STR("TestMenuScreen.title");
|
||||||
|
_prompt = STRH(_TEST_MENU_ENTRIES[0].prompt);
|
||||||
|
_itemPrompt = STR("TestMenuScreen.itemPrompt");
|
||||||
|
|
||||||
|
_listLength = util::countOf(_TEST_MENU_ENTRIES);
|
||||||
|
|
||||||
|
ListScreen::show(ctx, goBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestMenuScreen::update(ui::Context &ctx) {
|
||||||
|
auto &entry = _TEST_MENU_ENTRIES[_activeItem];
|
||||||
|
_prompt = STRH(entry.prompt);
|
||||||
|
|
||||||
|
ListScreen::update(ctx);
|
||||||
|
|
||||||
|
if (ctx.buttons.pressed(ui::BTN_START)) {
|
||||||
|
if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT))
|
||||||
|
ctx.show(APP->_mainMenuScreen, true, true);
|
||||||
|
else
|
||||||
|
(this->*entry.target)(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test submenus */
|
||||||
|
|
||||||
|
static const util::Hash _JAMMA_INPUT_NAMES[]{
|
||||||
|
"JAMMATestScreen.p2.left"_h, // io::JAMMA_P2_LEFT
|
||||||
|
"JAMMATestScreen.p2.right"_h, // io::JAMMA_P2_RIGHT
|
||||||
|
"JAMMATestScreen.p2.up"_h, // io::JAMMA_P2_UP
|
||||||
|
"JAMMATestScreen.p2.down"_h, // io::JAMMA_P2_DOWN
|
||||||
|
"JAMMATestScreen.p2.button1"_h, // io::JAMMA_P2_BUTTON1
|
||||||
|
"JAMMATestScreen.p2.button2"_h, // io::JAMMA_P2_BUTTON2
|
||||||
|
"JAMMATestScreen.p2.button3"_h, // io::JAMMA_P2_BUTTON3
|
||||||
|
"JAMMATestScreen.p2.start"_h, // io::JAMMA_P2_START
|
||||||
|
"JAMMATestScreen.p1.left"_h, // io::JAMMA_P1_LEFT
|
||||||
|
"JAMMATestScreen.p1.right"_h, // io::JAMMA_P1_RIGHT
|
||||||
|
"JAMMATestScreen.p1.up"_h, // io::JAMMA_P1_UP
|
||||||
|
"JAMMATestScreen.p1.down"_h, // io::JAMMA_P1_DOWN
|
||||||
|
"JAMMATestScreen.p1.button1"_h, // io::JAMMA_P1_BUTTON1
|
||||||
|
"JAMMATestScreen.p1.button2"_h, // io::JAMMA_P1_BUTTON2
|
||||||
|
"JAMMATestScreen.p1.button3"_h, // io::JAMMA_P1_BUTTON3
|
||||||
|
"JAMMATestScreen.p1.start"_h, // io::JAMMA_P1_START
|
||||||
|
"JAMMATestScreen.p1.button4"_h, // io::JAMMA_P1_BUTTON4
|
||||||
|
"JAMMATestScreen.p1.button5"_h, // io::JAMMA_P1_BUTTON5
|
||||||
|
"JAMMATestScreen.test"_h, // io::JAMMA_TEST
|
||||||
|
"JAMMATestScreen.p1.button6"_h, // io::JAMMA_P1_BUTTON6
|
||||||
|
"JAMMATestScreen.p2.button4"_h, // io::JAMMA_P2_BUTTON4
|
||||||
|
"JAMMATestScreen.p2.button5"_h, // io::JAMMA_P2_BUTTON5
|
||||||
|
0, // io::JAMMA_RAM_LAYOUT
|
||||||
|
"JAMMATestScreen.p2.button6"_h, // io::JAMMA_P2_BUTTON6
|
||||||
|
"JAMMATestScreen.coin1"_h, // io::JAMMA_COIN1
|
||||||
|
"JAMMATestScreen.coin2"_h, // io::JAMMA_COIN2
|
||||||
|
0, // io::JAMMA_PCMCIA_CD1
|
||||||
|
0, // io::JAMMA_PCMCIA_CD2
|
||||||
|
"JAMMATestScreen.service"_h // io::JAMMA_SERVICE
|
||||||
|
};
|
||||||
|
|
||||||
|
#define _PRINT(...) (ptr += snprintf(ptr, end - ptr __VA_OPT__(,) __VA_ARGS__))
|
||||||
|
#define _PRINTLN() (*(ptr++) = '\n')
|
||||||
|
|
||||||
|
void JAMMATestScreen::show(ui::Context &ctx, bool goBack) {
|
||||||
|
_title = STR("JAMMATestScreen.title");
|
||||||
|
_body = _bodyText;
|
||||||
|
_prompt = STR("JAMMATestScreen.prompt");
|
||||||
|
|
||||||
|
_bodyText[0] = 0;
|
||||||
|
|
||||||
|
TextScreen::show(ctx, goBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JAMMATestScreen::update(ui::Context &ctx) {
|
||||||
|
char *ptr = _bodyText, *end = &_bodyText[sizeof(_bodyText)];
|
||||||
|
|
||||||
|
auto inputs = io::getJAMMAInputs();
|
||||||
|
|
||||||
|
if (inputs) {
|
||||||
|
_PRINT(STR("JAMMATestScreen.inputs"));
|
||||||
|
|
||||||
|
for (auto name : _JAMMA_INPUT_NAMES) {
|
||||||
|
if ((inputs & 1) && name)
|
||||||
|
_PRINT(STRH(name));
|
||||||
|
|
||||||
|
inputs >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_PRINT(STR("JAMMATestScreen.inputsNote"));
|
||||||
|
} else {
|
||||||
|
_PRINT(STR("JAMMATestScreen.noInputs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
*(--ptr) = 0;
|
||||||
|
//LOG("remaining=%d", end - ptr);
|
||||||
|
|
||||||
|
//TextScreen::update(ctx);
|
||||||
|
|
||||||
|
if (ctx.buttons.longPressed(ui::BTN_START))
|
||||||
|
ctx.show(APP->_testMenuScreen, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AudioTestEntry {
|
||||||
|
public:
|
||||||
|
util::Hash name;
|
||||||
|
void (AudioTestScreen::*target)(ui::Context &ctx);
|
||||||
|
};
|
||||||
|
|
||||||
|
static const AudioTestEntry _AUDIO_TEST_ENTRIES[]{
|
||||||
|
{
|
||||||
|
.name = "AudioTestScreen.playLeft"_h,
|
||||||
|
.target = &AudioTestScreen::playLeft
|
||||||
|
}, {
|
||||||
|
.name = "AudioTestScreen.playRight"_h,
|
||||||
|
.target = &AudioTestScreen::playRight
|
||||||
|
}, {
|
||||||
|
.name = "AudioTestScreen.playBoth"_h,
|
||||||
|
.target = &AudioTestScreen::playBoth
|
||||||
|
}, {
|
||||||
|
.name = "AudioTestScreen.enableAmp"_h,
|
||||||
|
.target = &AudioTestScreen::enableAmp
|
||||||
|
}, {
|
||||||
|
.name = "AudioTestScreen.disableAmp"_h,
|
||||||
|
.target = &AudioTestScreen::disableAmp
|
||||||
|
}, {
|
||||||
|
.name = "AudioTestScreen.enableCDDA"_h,
|
||||||
|
.target = &AudioTestScreen::enableCDDA
|
||||||
|
}, {
|
||||||
|
.name = "AudioTestScreen.disableCDDA"_h,
|
||||||
|
.target = &AudioTestScreen::disableCDDA
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *AudioTestScreen::_getItemName(ui::Context &ctx, int index) const {
|
||||||
|
return STRH(_AUDIO_TEST_ENTRIES[index].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioTestScreen::playLeft(ui::Context &ctx) {
|
||||||
|
ctx.sounds[ui::SOUND_STARTUP].play(spu::MAX_VOLUME, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioTestScreen::playRight(ui::Context &ctx) {
|
||||||
|
ctx.sounds[ui::SOUND_STARTUP].play(0, spu::MAX_VOLUME);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioTestScreen::playBoth(ui::Context &ctx) {
|
||||||
|
ctx.sounds[ui::SOUND_STARTUP].play();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioTestScreen::enableAmp(ui::Context &ctx) {
|
||||||
|
io::setMiscOutput(io::MISC_AMP_ENABLE, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioTestScreen::disableAmp(ui::Context &ctx) {
|
||||||
|
io::setMiscOutput(io::MISC_AMP_ENABLE, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioTestScreen::enableCDDA(ui::Context &ctx) {
|
||||||
|
io::setMiscOutput(io::MISC_CDDA_ENABLE, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioTestScreen::disableCDDA(ui::Context &ctx) {
|
||||||
|
io::setMiscOutput(io::MISC_CDDA_ENABLE, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioTestScreen::show(ui::Context &ctx, bool goBack) {
|
||||||
|
_title = STR("AudioTestScreen.title");
|
||||||
|
_prompt = STR("AudioTestScreen.prompt");
|
||||||
|
_itemPrompt = STR("AudioTestScreen.itemPrompt");
|
||||||
|
|
||||||
|
_listLength = util::countOf(_AUDIO_TEST_ENTRIES);
|
||||||
|
|
||||||
|
ListScreen::show(ctx, goBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioTestScreen::update(ui::Context &ctx) {
|
||||||
|
ListScreen::update(ctx);
|
||||||
|
|
||||||
|
if (ctx.buttons.pressed(ui::BTN_START)) {
|
||||||
|
if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) {
|
||||||
|
io::setMiscOutput(io::MISC_AMP_ENABLE, false);
|
||||||
|
io::setMiscOutput(io::MISC_CDDA_ENABLE, false);
|
||||||
|
|
||||||
|
ctx.show(APP->_testMenuScreen, true, true);
|
||||||
|
} else {
|
||||||
|
(this->*_AUDIO_TEST_ENTRIES[_activeItem].target)(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base test pattern screen class */
|
||||||
|
|
||||||
|
static constexpr gpu::Color _BACKGROUND_COLOR = 0x000000;
|
||||||
|
static constexpr gpu::Color _FOREGROUND_COLOR = 0xffffff;
|
||||||
|
|
||||||
|
void TestPatternScreen::_drawTextOverlay(
|
||||||
|
ui::Context &ctx, const char *title, const char *prompt
|
||||||
|
) const {
|
||||||
|
int screenWidth = ctx.gpuCtx.width - ui::SCREEN_MARGIN_X * 2;
|
||||||
|
int screenHeight = ctx.gpuCtx.height - ui::SCREEN_MARGIN_Y * 2;
|
||||||
|
|
||||||
|
_newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height);
|
||||||
|
|
||||||
|
gpu::RectWH backdropRect;
|
||||||
|
|
||||||
|
backdropRect.x = ui::SCREEN_MARGIN_X - ui::SHADOW_OFFSET;
|
||||||
|
backdropRect.y = ui::SCREEN_MARGIN_Y - ui::SHADOW_OFFSET;
|
||||||
|
backdropRect.w = ui::SHADOW_OFFSET * 2 + screenWidth;
|
||||||
|
backdropRect.h = ui::SHADOW_OFFSET * 2 + ctx.font.metrics.lineHeight;
|
||||||
|
ctx.gpuCtx.drawRect(backdropRect, ctx.colors[ui::COLOR_SHADOW], true);
|
||||||
|
|
||||||
|
backdropRect.y += screenHeight - ui::SCREEN_PROMPT_HEIGHT_MIN;
|
||||||
|
ctx.gpuCtx.drawRect(backdropRect, ctx.colors[ui::COLOR_SHADOW], true);
|
||||||
|
|
||||||
|
gpu::Rect textRect;
|
||||||
|
|
||||||
|
textRect.x1 = ui::SCREEN_MARGIN_X;
|
||||||
|
textRect.y1 = ui::SCREEN_MARGIN_Y;
|
||||||
|
textRect.x2 = textRect.x1 + screenWidth;
|
||||||
|
textRect.y2 = textRect.y1 + ctx.font.metrics.lineHeight;
|
||||||
|
ctx.font.draw(ctx.gpuCtx, title, textRect, ctx.colors[ui::COLOR_TITLE]);
|
||||||
|
|
||||||
|
textRect.y1 += screenHeight - ui::SCREEN_PROMPT_HEIGHT_MIN;
|
||||||
|
textRect.y2 += textRect.y1;
|
||||||
|
ctx.font.draw(
|
||||||
|
ctx.gpuCtx, prompt, textRect, ctx.colors[ui::COLOR_TEXT1], true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestPatternScreen::draw(ui::Context &ctx, bool active) const {
|
||||||
|
_newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height);
|
||||||
|
ctx.gpuCtx.drawRect(
|
||||||
|
0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height, _BACKGROUND_COLOR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestPatternScreen::update(ui::Context &ctx) {
|
||||||
|
if (ctx.buttons.pressed(ui::BTN_START))
|
||||||
|
ctx.show(APP->_testMenuScreen, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Color intensity test screen */
|
||||||
|
|
||||||
|
struct IntensityBar {
|
||||||
|
public:
|
||||||
|
util::Hash name;
|
||||||
|
gpu::Color color;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr int _INTENSITY_BAR_NAME_WIDTH = 32;
|
||||||
|
static constexpr int _INTENSITY_BAR_WIDTH = 256;
|
||||||
|
static constexpr int _INTENSITY_BAR_HEIGHT = 32;
|
||||||
|
|
||||||
|
static const IntensityBar _INTENSITY_BARS[]{
|
||||||
|
{
|
||||||
|
.name = "ColorIntensityScreen.white"_h,
|
||||||
|
.color = 0xffffff
|
||||||
|
}, {
|
||||||
|
.name = "ColorIntensityScreen.red"_h,
|
||||||
|
.color = 0x0000ff
|
||||||
|
}, {
|
||||||
|
.name = "ColorIntensityScreen.green"_h,
|
||||||
|
.color = 0x00ff00
|
||||||
|
}, {
|
||||||
|
.name = "ColorIntensityScreen.blue"_h,
|
||||||
|
.color = 0xff0000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void ColorIntensityScreen::draw(ui::Context &ctx, bool active) const {
|
||||||
|
TestPatternScreen::draw(ctx, active);
|
||||||
|
|
||||||
|
int barWidth = _INTENSITY_BAR_NAME_WIDTH + _INTENSITY_BAR_WIDTH;
|
||||||
|
int barHeight = _INTENSITY_BAR_HEIGHT * util::countOf(_INTENSITY_BARS);
|
||||||
|
int offsetX = (ctx.gpuCtx.width - barWidth) / 2;
|
||||||
|
int offsetY = (ctx.gpuCtx.height - barHeight) / 2;
|
||||||
|
|
||||||
|
gpu::RectWH textRect, barRect;
|
||||||
|
|
||||||
|
textRect.x = offsetX;
|
||||||
|
textRect.y =
|
||||||
|
offsetY + (_INTENSITY_BAR_HEIGHT - ctx.font.metrics.lineHeight) / 2;
|
||||||
|
textRect.w = _INTENSITY_BAR_NAME_WIDTH;
|
||||||
|
textRect.h = ctx.font.metrics.lineHeight;
|
||||||
|
|
||||||
|
barRect.x = offsetX + _INTENSITY_BAR_NAME_WIDTH;
|
||||||
|
barRect.y = offsetY;
|
||||||
|
barRect.w = _INTENSITY_BAR_WIDTH;
|
||||||
|
barRect.h = _INTENSITY_BAR_HEIGHT / 2;
|
||||||
|
|
||||||
|
for (auto &bar : _INTENSITY_BARS) {
|
||||||
|
ctx.font.draw(
|
||||||
|
ctx.gpuCtx, STRH(bar.name), textRect, ctx.colors[ui::COLOR_TEXT1]
|
||||||
|
);
|
||||||
|
textRect.y += _INTENSITY_BAR_HEIGHT;
|
||||||
|
|
||||||
|
ctx.gpuCtx.setTexturePage(0, false);
|
||||||
|
ctx.gpuCtx.drawGradientRectH(barRect, _BACKGROUND_COLOR, bar.color);
|
||||||
|
barRect.y += _INTENSITY_BAR_HEIGHT / 2;
|
||||||
|
|
||||||
|
ctx.gpuCtx.setTexturePage(0, true);
|
||||||
|
ctx.gpuCtx.drawGradientRectH(barRect, _BACKGROUND_COLOR, bar.color);
|
||||||
|
barRect.y += _INTENSITY_BAR_HEIGHT / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
char value[2]{ 0, 0 };
|
||||||
|
|
||||||
|
textRect.x = barRect.x + 1;
|
||||||
|
textRect.y = offsetY - ctx.font.metrics.lineHeight;
|
||||||
|
textRect.w = _INTENSITY_BAR_WIDTH / 32;
|
||||||
|
|
||||||
|
for (int i = 0; i < 32; i++, textRect.x += textRect.w) {
|
||||||
|
value[0] = util::HEX_CHARSET[i & 15];
|
||||||
|
ctx.font.draw(ctx.gpuCtx, value, textRect, ctx.colors[ui::COLOR_TEXT2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_drawTextOverlay(
|
||||||
|
ctx, STR("ColorIntensityScreen.title"),
|
||||||
|
STR("ColorIntensityScreen.prompt")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Geometry test screen */
|
||||||
|
|
||||||
|
static constexpr int _GRID_CELL_SIZE = 16;
|
||||||
|
|
||||||
|
void GeometryScreen::draw(ui::Context &ctx, bool active) const {
|
||||||
|
TestPatternScreen::draw(ctx, active);
|
||||||
|
|
||||||
|
for (int x = -1; x < ctx.gpuCtx.width; x += _GRID_CELL_SIZE)
|
||||||
|
ctx.gpuCtx.drawRect(
|
||||||
|
x, 0, 2, ctx.gpuCtx.height, ctx.colors[ui::COLOR_TEXT1]
|
||||||
|
);
|
||||||
|
for (int y = -1; y < ctx.gpuCtx.height; y += _GRID_CELL_SIZE)
|
||||||
|
ctx.gpuCtx.drawRect(
|
||||||
|
0, y, ctx.gpuCtx.width, 2, ctx.colors[ui::COLOR_TEXT1]
|
||||||
|
);
|
||||||
|
|
||||||
|
int offset = (_GRID_CELL_SIZE / 2) - 1;
|
||||||
|
int rightOffset = ctx.gpuCtx.width - (offset + 2);
|
||||||
|
int bottomOffset = ctx.gpuCtx.height - (offset + 2);
|
||||||
|
|
||||||
|
for (int x = offset; x <= rightOffset; x += _GRID_CELL_SIZE) {
|
||||||
|
for (int y = offset; y <= bottomOffset; y += _GRID_CELL_SIZE) {
|
||||||
|
auto color = (
|
||||||
|
(x == offset) || (y == offset) || (x == rightOffset) ||
|
||||||
|
(y == bottomOffset)
|
||||||
|
)
|
||||||
|
? ctx.colors[ui::COLOR_ACCENT1]
|
||||||
|
: _FOREGROUND_COLOR;
|
||||||
|
|
||||||
|
ctx.gpuCtx.drawRect(x, y, 2, 2, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_drawTextOverlay(
|
||||||
|
ctx, STR("GeometryScreen.title"), STR("GeometryScreen.prompt")
|
||||||
|
);
|
||||||
|
}
|
72
src/main/app/tests.hpp
Normal file
72
src/main/app/tests.hpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "main/uibase.hpp"
|
||||||
|
#include "main/uicommon.hpp"
|
||||||
|
|
||||||
|
/* Top-level test menu */
|
||||||
|
|
||||||
|
class TestMenuScreen : public ui::ListScreen {
|
||||||
|
protected:
|
||||||
|
const char *_getItemName(ui::Context &ctx, int index) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void jammaTest(ui::Context &ctx);
|
||||||
|
void audioTest(ui::Context &ctx);
|
||||||
|
void colorIntensity(ui::Context &ctx);
|
||||||
|
void geometry(ui::Context &ctx);
|
||||||
|
|
||||||
|
void show(ui::Context &ctx, bool goBack = false);
|
||||||
|
void update(ui::Context &ctx);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Test submenus */
|
||||||
|
|
||||||
|
class JAMMATestScreen : public ui::TextScreen {
|
||||||
|
private:
|
||||||
|
char _bodyText[2048];
|
||||||
|
|
||||||
|
public:
|
||||||
|
void show(ui::Context &ctx, bool goBack = false);
|
||||||
|
void update(ui::Context &ctx);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AudioTestScreen : public ui::ListScreen {
|
||||||
|
protected:
|
||||||
|
const char *_getItemName(ui::Context &ctx, int index) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void playLeft(ui::Context &ctx);
|
||||||
|
void playRight(ui::Context &ctx);
|
||||||
|
void playBoth(ui::Context &ctx);
|
||||||
|
void enableAmp(ui::Context &ctx);
|
||||||
|
void disableAmp(ui::Context &ctx);
|
||||||
|
void enableCDDA(ui::Context &ctx);
|
||||||
|
void disableCDDA(ui::Context &ctx);
|
||||||
|
|
||||||
|
void show(ui::Context &ctx, bool goBack = false);
|
||||||
|
void update(ui::Context &ctx);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Test pattern screens */
|
||||||
|
|
||||||
|
class TestPatternScreen : public ui::AnimatedScreen {
|
||||||
|
protected:
|
||||||
|
void _drawTextOverlay(
|
||||||
|
ui::Context &ctx, const char *title, const char *prompt
|
||||||
|
) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual void draw(ui::Context &ctx, bool active) const;
|
||||||
|
virtual void update(ui::Context &ctx);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ColorIntensityScreen : public TestPatternScreen {
|
||||||
|
public:
|
||||||
|
void draw(ui::Context &ctx, bool active) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GeometryScreen : public TestPatternScreen {
|
||||||
|
public:
|
||||||
|
void draw(ui::Context &ctx, bool active) const;
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user