mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-01-22 19:52:05 +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/romactions.cpp
|
||||
src/main/app/romworkers.cpp
|
||||
src/main/app/tests.cpp
|
||||
src/vendor/ff.c
|
||||
src/vendor/ffunicode.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": {
|
||||
"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",
|
||||
@ -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)."
|
||||
},
|
||||
|
||||
"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": {
|
||||
"title": "Confirm operation",
|
||||
"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."
|
||||
},
|
||||
|
||||
"GeometryScreen": {
|
||||
"title": "{RIGHT_ARROW} Monitor geometry test",
|
||||
"prompt": "{RIGHT_ARROW} Press {START_BUTTON} to go back."
|
||||
},
|
||||
|
||||
"HexdumpScreen": {
|
||||
"title": "{RIGHT_ARROW} Cartridge dump",
|
||||
"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": {
|
||||
"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.",
|
||||
@ -360,6 +430,10 @@
|
||||
"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."
|
||||
},
|
||||
"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": {
|
||||
"name": "Change screen resolution",
|
||||
"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"
|
||||
},
|
||||
|
||||
"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": {
|
||||
"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.",
|
||||
|
@ -98,7 +98,7 @@ bool ISOVolumeDesc::validateMagic(void) const {
|
||||
bool ISO9660File::_loadSector(uint32_t lba) {
|
||||
if (lba == _bufferedLBA)
|
||||
return true;
|
||||
if (_device->read(_sectorBuffer, lba, 1))
|
||||
if (_device->readData(_sectorBuffer, lba, 1))
|
||||
return false;
|
||||
|
||||
_bufferedLBA = lba;
|
||||
@ -131,7 +131,7 @@ size_t ISO9660File::read(void *output, size_t length) {
|
||||
auto spanLength = numSectors * ide::ATAPI_SECTOR_SIZE;
|
||||
|
||||
if (numSectors > 0) {
|
||||
if (_device->read(currentPtr, lba, numSectors))
|
||||
if (_device->readData(currentPtr, lba, numSectors))
|
||||
return false;
|
||||
|
||||
offset += spanLength;
|
||||
@ -204,7 +204,7 @@ bool ISO9660Provider::_readData(
|
||||
) {
|
||||
if (!output.allocate(numSectors * ide::ATAPI_SECTOR_SIZE))
|
||||
return false;
|
||||
if (_device->read(output.ptr, lba, numSectors))
|
||||
if (_device->readData(output.ptr, lba, numSectors))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -272,7 +272,7 @@ bool ISO9660Provider::init(int drive) {
|
||||
for (
|
||||
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;
|
||||
if (!pvd.validateMagic()) {
|
||||
LOG("invalid ISO descriptor, lba=0x%x", lba);
|
||||
|
@ -298,7 +298,7 @@ extern "C" DRESULT disk_read(
|
||||
|
||||
if (!(dev.flags & ide::DEVICE_READY))
|
||||
return RES_NOTRDY;
|
||||
if (dev.read(data, lba, count))
|
||||
if (dev.readData(data, lba, count))
|
||||
return RES_ERROR;
|
||||
|
||||
return RES_OK;
|
||||
@ -313,7 +313,7 @@ extern "C" DRESULT disk_write(
|
||||
return RES_NOTRDY;
|
||||
if (dev.flags & ide::DEVICE_READ_ONLY)
|
||||
return RES_WRPRT;
|
||||
if (dev.write(data, lba, count))
|
||||
if (dev.writeData(data, lba, count))
|
||||
return RES_ERROR;
|
||||
|
||||
return RES_OK;
|
||||
|
@ -22,10 +22,12 @@
|
||||
|
||||
namespace ide {
|
||||
|
||||
static constexpr int _WAIT_TIMEOUT = 30000000;
|
||||
static constexpr int _DETECT_TIMEOUT = 500000;
|
||||
static constexpr int _DMA_TIMEOUT = 10000;
|
||||
static constexpr int _SRST_DELAY = 5000;
|
||||
static constexpr int _COMMAND_TIMEOUT = 30000000;
|
||||
static constexpr int _REQUEST_SENSE_TIMEOUT = 500000;
|
||||
static constexpr int _DETECT_DRIVE_TIMEOUT = 500000;
|
||||
|
||||
static constexpr int _DMA_TIMEOUT = 10000;
|
||||
static constexpr int _SRST_DELAY = 5000;
|
||||
|
||||
static const char *const _SENSE_KEY_NAMES[]{
|
||||
"NO_SENSE",
|
||||
@ -52,6 +54,8 @@ const char *const DEVICE_ERROR_NAMES[]{
|
||||
"NO_DRIVE",
|
||||
"STATUS_TIMEOUT",
|
||||
"DRIVE_ERROR",
|
||||
"DISC_ERROR",
|
||||
"DISC_CHANGED",
|
||||
"INCOMPLETE_DATA",
|
||||
"CHECKSUM_MISMATCH"
|
||||
};
|
||||
@ -120,42 +124,19 @@ int IdentifyBlock::getHighestPIOMode(void) const {
|
||||
|
||||
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(
|
||||
uint8_t mask, uint8_t value, int timeout, bool ignoreErrors
|
||||
) {
|
||||
if (!timeout)
|
||||
timeout = _WAIT_TIMEOUT;
|
||||
timeout = _COMMAND_TIMEOUT;
|
||||
|
||||
for (; timeout > 0; timeout -= 10) {
|
||||
uint8_t status = _read(CS0_STATUS);
|
||||
|
||||
if (!ignoreErrors && (status & CS0_STATUS_ERR)) {
|
||||
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)
|
||||
);
|
||||
|
||||
@ -173,26 +154,55 @@ DeviceError Device::_waitForStatus(
|
||||
}
|
||||
|
||||
LOG(
|
||||
"IDE timeout, stat=0x%02x, err=0x%02x", _read(CS0_STATUS),
|
||||
_read(CS0_ERROR)
|
||||
"drive %d timeout, stat=0x%02x, err=0x%02x",
|
||||
(flags / DEVICE_SECONDARY) & 1, _read(CS0_STATUS), _read(CS0_ERROR)
|
||||
);
|
||||
|
||||
_write(CS0_COMMAND, ATA_DEVICE_RESET);
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
DeviceError Device::_command(
|
||||
uint8_t cmd, uint8_t status, int timeout, bool ignoreErrors
|
||||
DeviceError Device::_select(
|
||||
uint8_t devSelFlags, int timeout, bool ignoreErrors
|
||||
) {
|
||||
auto error = _waitForStatus(
|
||||
CS0_STATUS_BSY | status, status, timeout, ignoreErrors
|
||||
);
|
||||
if (flags & DEVICE_SECONDARY)
|
||||
_write(CS0_DEVICE_SEL, devSelFlags | CS0_DEVICE_SEL_SECONDARY);
|
||||
else
|
||||
_write(CS0_DEVICE_SEL, devSelFlags | CS0_DEVICE_SEL_PRIMARY);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
return _waitForStatus(CS0_STATUS_BSY, 0, timeout, ignoreErrors);
|
||||
}
|
||||
|
||||
_write(CS0_COMMAND, cmd);
|
||||
return _waitForStatus(CS0_STATUS_BSY, 0, timeout);
|
||||
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)
|
||||
return error;
|
||||
|
||||
_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));
|
||||
|
||||
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) {
|
||||
@ -202,7 +212,11 @@ DeviceError Device::_detectDrive(void) {
|
||||
_write(CS1_DEVICE_CTRL, CS1_DEVICE_CTRL_IEN);
|
||||
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
|
||||
io::clearWatchdog();
|
||||
#endif
|
||||
@ -211,7 +225,7 @@ DeviceError Device::_detectDrive(void) {
|
||||
// the written value. This should not fail even if the drive is busy.
|
||||
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);
|
||||
|
||||
// Note that ATA drives will also assert DRDY when ready, but ATAPI
|
||||
@ -223,7 +237,7 @@ DeviceError Device::_detectDrive(void) {
|
||||
if (!(pattern & 1))
|
||||
pattern |= 1 << 7;
|
||||
|
||||
delayMicroseconds(10);
|
||||
delayMicroseconds(100);
|
||||
#ifndef ENABLE_FULL_IDE_DRIVER
|
||||
io::clearWatchdog();
|
||||
#endif
|
||||
@ -233,10 +247,14 @@ DeviceError Device::_detectDrive(void) {
|
||||
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);
|
||||
|
||||
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)
|
||||
return error;
|
||||
@ -249,10 +267,14 @@ DeviceError Device::_readPIO(void *data, size_t length, int timeout) {
|
||||
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);
|
||||
|
||||
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)
|
||||
return error;
|
||||
@ -265,12 +287,16 @@ DeviceError Device::_writePIO(const void *data, size_t length, int timeout) {
|
||||
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;
|
||||
|
||||
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)
|
||||
return error;
|
||||
@ -291,12 +317,16 @@ DeviceError Device::_readDMA(void *data, size_t length, int timeout) {
|
||||
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;
|
||||
|
||||
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)
|
||||
return error;
|
||||
@ -334,12 +364,13 @@ DeviceError Device::_ideReadWrite(
|
||||
while (count) {
|
||||
size_t chunkLength = util::min(count, maxLength);
|
||||
|
||||
_setLBA(lba, chunkLength);
|
||||
auto error = _command(cmd, CS0_STATUS_DRDY);
|
||||
auto error = _setLBA(lba, chunkLength);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
_write(CS0_COMMAND, cmd);
|
||||
|
||||
// Data must be transferred one sector at a time as the drive may
|
||||
// deassert DRQ between sectors.
|
||||
for (size_t i = chunkLength; i; i--) {
|
||||
@ -359,7 +390,7 @@ DeviceError Device::_ideReadWrite(
|
||||
}
|
||||
|
||||
error = _waitForStatus(
|
||||
CS0_STATUS_BSY | CS0_STATUS_DRDY, CS0_STATUS_DRDY
|
||||
CS0_STATUS_DRDY | CS0_STATUS_BSY, CS0_STATUS_DRDY
|
||||
);
|
||||
|
||||
if (error)
|
||||
@ -376,7 +407,7 @@ DeviceError Device::_atapiRead(uintptr_t ptr, uint32_t lba, size_t count) {
|
||||
Packet packet;
|
||||
|
||||
packet.setRead(lba, count);
|
||||
auto error = atapiPacket(packet);
|
||||
auto error = atapiPacket(packet, ATAPI_SECTOR_SIZE);
|
||||
|
||||
if (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
|
||||
// DRQ between sectors.
|
||||
for (; count; count--) {
|
||||
error = _readPIO(reinterpret_cast<void *>(ptr), ATAPI_SECTOR_SIZE);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
if (_readPIO(reinterpret_cast<void *>(ptr), ATAPI_SECTOR_SIZE))
|
||||
return atapiPoll();
|
||||
|
||||
ptr += ATAPI_SECTOR_SIZE;
|
||||
}
|
||||
@ -411,16 +440,18 @@ DeviceError Device::enumerate(void) {
|
||||
// to prevent blocking for too long.
|
||||
IdentifyBlock block;
|
||||
|
||||
//_select();
|
||||
|
||||
if ((_read(CS0_CYLINDER_L) == 0x14) && (_read(CS0_CYLINDER_H) == 0xeb)) {
|
||||
flags |= DEVICE_ATAPI;
|
||||
|
||||
if (_command(ATA_IDENTIFY_PACKET, 0, _DETECT_TIMEOUT))
|
||||
return NO_DRIVE;
|
||||
if (_readPIO(&block, sizeof(IdentifyBlock), _DETECT_TIMEOUT))
|
||||
return NO_DRIVE;
|
||||
_write(CS0_COMMAND, ATA_IDENTIFY_PACKET);
|
||||
|
||||
if (_readPIO(&block, sizeof(IdentifyBlock), _DETECT_DRIVE_TIMEOUT))
|
||||
return NO_DRIVE;
|
||||
if (!block.validateChecksum())
|
||||
return CHECKSUM_MISMATCH;
|
||||
|
||||
if (
|
||||
(block.deviceFlags & IDENTIFY_DEV_ATAPI_TYPE_BITMASK)
|
||||
== IDENTIFY_DEV_ATAPI_TYPE_CDROM
|
||||
@ -432,13 +463,13 @@ DeviceError Device::enumerate(void) {
|
||||
)
|
||||
flags |= DEVICE_HAS_PACKET16;
|
||||
} else {
|
||||
if (_command(ATA_IDENTIFY, CS0_STATUS_DRDY, _DETECT_TIMEOUT))
|
||||
return NO_DRIVE;
|
||||
if (_readPIO(&block, sizeof(IdentifyBlock), _DETECT_TIMEOUT))
|
||||
return NO_DRIVE;
|
||||
_write(CS0_COMMAND, ATA_IDENTIFY);
|
||||
|
||||
if (_readPIO(&block, sizeof(IdentifyBlock), _DETECT_DRIVE_TIMEOUT))
|
||||
return NO_DRIVE;
|
||||
if (!block.validateChecksum())
|
||||
return CHECKSUM_MISMATCH;
|
||||
|
||||
if (block.commandSetFlags[1] & (1 << 10)) {
|
||||
flags |= DEVICE_HAS_LBA48;
|
||||
capacity = block.getSectorCountExt();
|
||||
@ -462,8 +493,9 @@ DeviceError Device::enumerate(void) {
|
||||
|
||||
_write(CS0_FEATURES, FEATURE_TRANSFER_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)
|
||||
return error;
|
||||
@ -479,22 +511,29 @@ DeviceError Device::atapiPacket(const Packet &packet, size_t transferLength) {
|
||||
if (!(flags & DEVICE_ATAPI))
|
||||
return UNSUPPORTED_OP;
|
||||
|
||||
_select();
|
||||
auto error = _select(0);
|
||||
|
||||
if (error)
|
||||
return atapiPoll();
|
||||
|
||||
_write(CS0_CYLINDER_L, (transferLength >> 0) & 0xff);
|
||||
_write(CS0_CYLINDER_H, (transferLength >> 8) & 0xff);
|
||||
_write(CS0_COMMAND, ATA_PACKET);
|
||||
|
||||
auto error = _command(ATA_PACKET, 0);
|
||||
error = _writePIO(&packet, (flags & DEVICE_HAS_PACKET16) ? 16 : 12);
|
||||
|
||||
if (!error)
|
||||
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) {
|
||||
if (!(flags & DEVICE_READY))
|
||||
return NO_DRIVE;
|
||||
if (!(flags & DEVICE_ATAPI))
|
||||
return UNSUPPORTED_OP;
|
||||
|
||||
Packet packet;
|
||||
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
|
||||
// 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)
|
||||
error = _writePIO(&packet, (flags & DEVICE_HAS_PACKET16) ? 16 : 12);
|
||||
if (!error)
|
||||
error = _waitForStatus(CS0_STATUS_BSY, 0);
|
||||
if (!error)
|
||||
error = _readPIO(&data, sizeof(data));
|
||||
error = _readPIO(&data, sizeof(data), _REQUEST_SENSE_TIMEOUT);
|
||||
|
||||
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) {
|
||||
case SENSE_KEY_NO_SENSE:
|
||||
case SENSE_KEY_RECOVERED_ERROR:
|
||||
return NO_ERROR;
|
||||
|
||||
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);
|
||||
|
||||
if (!(flags & DEVICE_READY))
|
||||
return NO_DRIVE;
|
||||
|
||||
if (flags & DEVICE_ATAPI) {
|
||||
auto error = _atapiRead(
|
||||
if (flags & DEVICE_ATAPI)
|
||||
return _atapiRead(
|
||||
reinterpret_cast<uintptr_t>(data), static_cast<uint32_t>(lba), count
|
||||
);
|
||||
|
||||
#ifdef ENABLE_FULL_IDE_DRIVER
|
||||
if (error)
|
||||
error = atapiPoll();
|
||||
#endif
|
||||
|
||||
return error;
|
||||
} else {
|
||||
else
|
||||
return _ideReadWrite(
|
||||
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);
|
||||
|
||||
if (!(flags & DEVICE_READY))
|
||||
@ -590,25 +631,17 @@ DeviceError Device::goIdle(bool standby) {
|
||||
packet.setStartStopUnit(START_STOP_MODE_STOP_DISC);
|
||||
return atapiPacket(packet);
|
||||
} else {
|
||||
_select();
|
||||
auto error = _select(CS0_DEVICE_SEL_LBA);
|
||||
|
||||
auto error = _command(
|
||||
standby ? ATA_STANDBY_IMMEDIATE : ATA_IDLE_IMMEDIATE,
|
||||
CS0_STATUS_DRDY
|
||||
);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
#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 (standby)
|
||||
_write(CS0_COMMAND, ATA_STANDBY_IMMEDIATE);
|
||||
else
|
||||
_write(CS0_COMMAND, ATA_IDLE_IMMEDIATE);
|
||||
|
||||
return error;
|
||||
return _waitForStatus(CS0_STATUS_BSY, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -619,12 +652,17 @@ DeviceError Device::flushCache(void) {
|
||||
return NO_ERROR;
|
||||
//return UNSUPPORTED_OP;
|
||||
|
||||
_select();
|
||||
auto error = _select(CS0_DEVICE_SEL_LBA);
|
||||
|
||||
return _command(
|
||||
(flags & DEVICE_HAS_LBA48) ? ATA_FLUSH_CACHE_EXT : ATA_FLUSH_CACHE,
|
||||
CS0_STATUS_DRDY
|
||||
);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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(
|
||||
uint8_t mask, uint8_t value, int timeout = 0, bool ignoreErrors = false
|
||||
);
|
||||
DeviceError _command(
|
||||
uint8_t cmd, uint8_t status, int timeout = 0, bool ignoreErrors = false
|
||||
DeviceError _select(
|
||||
uint8_t devSelFlags, int timeout = 0, bool ignoreErrors = false
|
||||
);
|
||||
DeviceError _setLBA(uint64_t lba, size_t count, int timeout = 0);
|
||||
DeviceError _detectDrive(void);
|
||||
|
||||
DeviceError _readPIO(void *data, size_t length, int timeout = 0);
|
||||
DeviceError _writePIO(const void *data, size_t length, int timeout = 0);
|
||||
DeviceError _readDMA(void *data, size_t length, int timeout = 0);
|
||||
DeviceError _writeDMA(const void *data, size_t length, int timeout = 0);
|
||||
DeviceError _readPIO(
|
||||
void *data, size_t length, int timeout = 0, bool ignoreErrors = false
|
||||
);
|
||||
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(
|
||||
uintptr_t ptr, uint64_t lba, size_t count, bool write
|
||||
@ -421,13 +424,11 @@ public:
|
||||
}
|
||||
|
||||
DeviceError enumerate(void);
|
||||
DeviceError atapiPacket(
|
||||
const Packet &packet, size_t transferLength = ATAPI_SECTOR_SIZE
|
||||
);
|
||||
DeviceError atapiPacket(const Packet &packet, size_t transferLength = 0);
|
||||
DeviceError atapiPoll(void);
|
||||
|
||||
DeviceError read(void *data, uint64_t lba, size_t count);
|
||||
DeviceError write(const void *data, uint64_t lba, size_t count);
|
||||
DeviceError readData(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 flushCache(void);
|
||||
};
|
||||
|
@ -165,14 +165,14 @@ bool Sound::initFromVAGHeader(const VAGHeader *header, uint32_t ramOffset) {
|
||||
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))
|
||||
return -1;
|
||||
if (!offset)
|
||||
return -1;
|
||||
|
||||
SPU_CH_VOL_L(ch) = volume;
|
||||
SPU_CH_VOL_R(ch) = volume;
|
||||
SPU_CH_VOL_L(ch) = left;
|
||||
SPU_CH_VOL_R(ch) = right;
|
||||
SPU_CH_FREQ (ch) = sampleRate;
|
||||
SPU_CH_ADDR (ch) = offset;
|
||||
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;
|
||||
}
|
||||
|
||||
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))
|
||||
return;
|
||||
|
||||
SPU_CH_VOL_L(ch) = volume;
|
||||
SPU_CH_VOL_R(ch) = volume;
|
||||
SPU_CH_VOL_L(ch) = left;
|
||||
SPU_CH_VOL_R(ch) = right;
|
||||
}
|
||||
|
||||
void init(void);
|
||||
@ -54,12 +54,14 @@ public:
|
||||
|
||||
inline Sound(void)
|
||||
: offset(0), length(0) {}
|
||||
inline Channel play(uint16_t volume = MAX_VOLUME) const {
|
||||
return play(volume, getFreeChannel());
|
||||
inline Channel play(
|
||||
uint16_t left = MAX_VOLUME, uint16_t right = MAX_VOLUME
|
||||
) const {
|
||||
return play(left, right, getFreeChannel());
|
||||
}
|
||||
|
||||
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++));
|
||||
|
||||
#ifdef ENABLE_LOGGING
|
||||
util::logger.setupSyslog(launcher.args.baudRate);
|
||||
util::logger.setupSyslog(args.baudRate);
|
||||
#endif
|
||||
|
||||
if (!args.entryPoint || !args.loadAddress || !args.numFragments)
|
||||
@ -52,7 +52,7 @@ int main(int argc, const char **argv) {
|
||||
length -= skipSectors;
|
||||
}
|
||||
|
||||
if (dev.read(reinterpret_cast<void *>(ptr), lba, length))
|
||||
if (dev.readData(reinterpret_cast<void *>(ptr), lba, length))
|
||||
return 3;
|
||||
|
||||
io::clearWatchdog();
|
||||
|
@ -342,6 +342,7 @@ void App::_interruptHandler(void) {
|
||||
|
||||
_runWorker(&App::_ideInitWorker, _warningScreen);
|
||||
_setupInterrupts();
|
||||
_ctx.sounds[ui::SOUND_STARTUP].play();
|
||||
|
||||
for (;;) {
|
||||
util::Date date;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "main/app/misc.hpp"
|
||||
#include "main/app/modals.hpp"
|
||||
#include "main/app/romactions.hpp"
|
||||
#include "main/app/tests.hpp"
|
||||
#include "main/cart.hpp"
|
||||
#include "main/cartdata.hpp"
|
||||
#include "main/cartio.hpp"
|
||||
@ -55,7 +56,7 @@ private:
|
||||
public:
|
||||
file::Provider *ide[util::countOf(ide::devices)];
|
||||
file::ZIPProvider resource;
|
||||
#ifndef NDEBUG
|
||||
#ifdef ENABLE_PCDRV
|
||||
file::HostProvider host;
|
||||
#endif
|
||||
file::VFSProvider vfs;
|
||||
@ -81,54 +82,84 @@ class App {
|
||||
friend class ConfirmScreen;
|
||||
friend class FilePickerScreen;
|
||||
friend class FileBrowserScreen;
|
||||
friend class AutobootScreen;
|
||||
|
||||
friend class WarningScreen;
|
||||
friend class AutobootScreen;
|
||||
friend class ButtonMappingScreen;
|
||||
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 UnlockKeyScreen;
|
||||
friend class KeyEntryScreen;
|
||||
|
||||
friend class CartActionsScreen;
|
||||
friend class QRCodeScreen;
|
||||
friend class HexdumpScreen;
|
||||
friend class ReflashGameScreen;
|
||||
friend class SystemIDEntryScreen;
|
||||
|
||||
friend class StorageInfoScreen;
|
||||
friend class StorageActionsScreen;
|
||||
friend class CardSizeScreen;
|
||||
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:
|
||||
WorkerStatusScreen _workerStatusScreen;
|
||||
MessageScreen _messageScreen;
|
||||
ConfirmScreen _confirmScreen;
|
||||
FilePickerScreen _filePickerScreen;
|
||||
FileBrowserScreen _fileBrowserScreen;
|
||||
AutobootScreen _autobootScreen;
|
||||
WarningScreen _warningScreen;
|
||||
ButtonMappingScreen _buttonMappingScreen;
|
||||
MainMenuScreen _mainMenuScreen;
|
||||
// modals.cpp
|
||||
WorkerStatusScreen _workerStatusScreen;
|
||||
MessageScreen _messageScreen;
|
||||
ConfirmScreen _confirmScreen;
|
||||
FilePickerScreen _filePickerScreen;
|
||||
FileBrowserScreen _fileBrowserScreen;
|
||||
|
||||
// main.cpp
|
||||
WarningScreen _warningScreen;
|
||||
AutobootScreen _autobootScreen;
|
||||
ButtonMappingScreen _buttonMappingScreen;
|
||||
MainMenuScreen _mainMenuScreen;
|
||||
|
||||
// cartunlock.cpp
|
||||
CartInfoScreen _cartInfoScreen;
|
||||
UnlockKeyScreen _unlockKeyScreen;
|
||||
KeyEntryScreen _keyEntryScreen;
|
||||
|
||||
// cartactions.cpp
|
||||
CartActionsScreen _cartActionsScreen;
|
||||
QRCodeScreen _qrCodeScreen;
|
||||
HexdumpScreen _hexdumpScreen;
|
||||
ReflashGameScreen _reflashGameScreen;
|
||||
SystemIDEntryScreen _systemIDEntryScreen;
|
||||
|
||||
// romactions.cpp
|
||||
StorageInfoScreen _storageInfoScreen;
|
||||
StorageActionsScreen _storageActionsScreen;
|
||||
IDEInfoScreen _ideInfoScreen;
|
||||
RTCTimeScreen _rtcTimeScreen;
|
||||
ResolutionScreen _resolutionScreen;
|
||||
AboutScreen _aboutScreen;
|
||||
CartInfoScreen _cartInfoScreen;
|
||||
UnlockKeyScreen _unlockKeyScreen;
|
||||
KeyEntryScreen _keyEntryScreen;
|
||||
CartActionsScreen _cartActionsScreen;
|
||||
QRCodeScreen _qrCodeScreen;
|
||||
HexdumpScreen _hexdumpScreen;
|
||||
ReflashGameScreen _reflashGameScreen;
|
||||
SystemIDEntryScreen _systemIDEntryScreen;
|
||||
CardSizeScreen _cardSizeScreen;
|
||||
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
|
||||
util::LogBuffer _logBuffer;
|
||||
ui::LogOverlay _logOverlay;
|
||||
@ -166,6 +197,9 @@ private:
|
||||
bool playSound = false
|
||||
);
|
||||
|
||||
void _worker(void);
|
||||
void _interruptHandler(void);
|
||||
|
||||
// cartworkers.cpp
|
||||
bool _cartDetectWorker(void);
|
||||
bool _cartUnlockWorker(void);
|
||||
@ -189,9 +223,6 @@ private:
|
||||
bool _atapiEjectWorker(void);
|
||||
bool _rebootWorker(void);
|
||||
|
||||
void _worker(void);
|
||||
void _interruptHandler(void);
|
||||
|
||||
public:
|
||||
App(ui::Context &ctx);
|
||||
~App(void);
|
||||
|
@ -154,6 +154,10 @@ static const MenuEntry _MENU_ENTRIES[]{
|
||||
.name = "MainMenuScreen.setRTCTime.name"_h,
|
||||
.prompt = "MainMenuScreen.setRTCTime.prompt"_h,
|
||||
.target = &MainMenuScreen::setRTCTime
|
||||
}, {
|
||||
.name = "MainMenuScreen.testMenu.name"_h,
|
||||
.prompt = "MainMenuScreen.testMenu.prompt"_h,
|
||||
.target = &MainMenuScreen::testMenu
|
||||
}, {
|
||||
.name = "MainMenuScreen.setResolution.name"_h,
|
||||
.prompt = "MainMenuScreen.setResolution.prompt"_h,
|
||||
@ -215,6 +219,10 @@ void MainMenuScreen::setRTCTime(ui::Context &ctx) {
|
||||
ctx.show(APP->_rtcTimeScreen, false, true);
|
||||
}
|
||||
|
||||
void MainMenuScreen::testMenu(ui::Context &ctx) {
|
||||
ctx.show(APP->_testMenuScreen, false, true);
|
||||
}
|
||||
|
||||
void MainMenuScreen::setResolution(ui::Context &ctx) {
|
||||
ctx.show(APP->_resolutionScreen, false, true);
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ public:
|
||||
void ideInfo(ui::Context &ctx);
|
||||
void runExecutable(ui::Context &ctx);
|
||||
void setRTCTime(ui::Context &ctx);
|
||||
void testMenu(ui::Context &ctx);
|
||||
void setResolution(ui::Context &ctx);
|
||||
void about(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.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) {
|
||||
@ -290,7 +290,9 @@ void AboutScreen::hide(ui::Context &ctx, bool goBack) {
|
||||
|
||||
void AboutScreen::update(ui::Context &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))
|
||||
ctx.show(APP->_mainMenuScreen, true, true);
|
||||
|
@ -59,7 +59,6 @@ bool App::_ideInitWorker(void) {
|
||||
}
|
||||
#endif
|
||||
|
||||
_ctx.sounds[ui::SOUND_STARTUP].play();
|
||||
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