diff --git a/README.md b/README.md index 05a97dc..572f630 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

- 573in1 logo + 573in1 logo

573in1 is a full-featured homebrew maintenance and troubleshooting tool, game @@ -43,17 +43,17 @@ instructions. **Reading the documentation before proceeding is highly** ## Screenshots

- Main menu + Main menu Cartridge information screen + width="320" /> Cartridge system ID editor + width="320" /> Storage device information screen + width="320" /> IDE drive file picker + width="320" /> Monitor test pattern + width="320" />

## Project status @@ -78,6 +78,8 @@ to lowest priority: flash or a PCMCIA card, allowing the 573 BIOS to boot them automatically even with no CD-ROM drive present. - Cleaning up the codebase, which currently contains *many* bad practices. +- Adding support for JVS bus scanning and using JVS I/O boards in place of the + JAMMA button inputs. - Adding UTF-8 support to the font and text rendering code. This is a rather low priority feature, but it would allow 573in1 to be translated to other languages. diff --git a/doc/assets/buttonMapping.png b/doc/assets/buttonMapping.png new file mode 100644 index 0000000..cffd57b Binary files /dev/null and b/doc/assets/buttonMapping.png differ diff --git a/doc/faq.md b/doc/faq.md new file mode 100644 index 0000000..21ed2d9 --- /dev/null +++ b/doc/faq.md @@ -0,0 +1,67 @@ + +# FAQ and troubleshooting + +## Running 573in1 + +### I cannot get past the BIOS "hardware error" screen and into the main menu + +- Make sure the disc image has been burned correctly (and not by e.g. dragging + the image file onto the disc). The disc should appear as `573IN1_xxxxx` and + contain a bunch of files named `README.TXT`, `PSX.EXE` and so on. +- Try using a different disc and/or another CD-ROM drive. A list of drives known + to be compatible with the stock System 573 BIOS can be found + [here](https://psx-spx.consoledev.net/konamisystem573/#known-working-replacement-drives). +- Ensure the BIOS is not reporting an actual hardware error with the 573. A + CD-ROM error will result in `CDR BAD` being displayed, while other errors will + be reported under the PCB location of the chip that failed (e.g. `18E BAD` for + a JVS MCU error). A list of BIOS errors can be found + [here](https://psx-spx.consoledev.net/konamisystem573/#boot-sequence). + +### I get an error about a missing file or the CD-ROM being incorrect + +- You have booted into the game currently installed on the 573's internal flash + rather than from the disc. Turn off DIP switch 4 (the rightmost one) and power + cycle the 573. + +## Security cartridges + +### My cartridge does not get recognized + +- Make sure the cartridge's contacts are clean and the security cartridge + connector on the 573 has no bent pins and is not worn out, corroded or + otherwise damaged. + +## IDE devices + +### My IDE cable does not fit into the 573 + +- The 573 motherboard has pin 20 populated on the IDE connector, but most cables + use it as a keying pin (as per later versions of the IDE specification) and + thus have it blocked off. You may either try to find another cable or drill a + small hole in the cable's connector. +- You can also cut or desolder pin 20 on the 573, however this is not + recommended. + +### My IDE drive, CF card or adapter does not show up + +- Try using a different drive or adapter. Some CF cards, SD card adapters and + IDE-to-SATA converters are known to be problematic. + +### My IDE drive, CF card or adapter shows up but is unreadable + +- Ensure the drive supports LBA addressing. All CF cards, SD card adapters and + the vast majority of hard drives do, however older hard drives (typically ones + with a capacity lower than 8 GB or manufactured before the mid 1990s) may only + support CHS addressing. +- Ensure the drive is formatted with a single FAT12, FAT16, FAT32 or exFAT + partition. Other filesystems such as NTFS or ext4 are not supported. + +## Miscellaneous + +### How do I obtain more information about an error that happened? + +- You may press the test button on your 573 or cabinet at any time to toggle the + log window, which will contain detailed information about errors. +- If an IDE hard drive is connected you can also take screenshots by holding + down the test button. Screenshots are saved to the `573in1` directory in the + root of the filesystem. diff --git a/doc/index.md b/doc/index.md index 39f51c8..661cc60 100644 --- a/doc/index.md +++ b/doc/index.md @@ -8,7 +8,7 @@ - TODO: ~~[Installing games and working with flash memory devices](flash.md)~~ - TODO: ~~[Connecting an IDE hard drive](hdd.md)~~ - TODO: ~~[Installing 573in1 as a BIOS ROM](bios.md)~~ -- TODO: ~~[FAQ and troubleshooting](faq.md)~~ +- [FAQ and troubleshooting](faq.md) ## Development resources diff --git a/src/common/gpu.cpp b/src/common/gpu.cpp index b2a144a..9ac6d93 100644 --- a/src/common/gpu.cpp +++ b/src/common/gpu.cpp @@ -326,11 +326,6 @@ void Context::drawGradientRectD( cmd[7] = gp0_xy(x + width, y + height); } -void Context::drawBackdrop(Color color, BlendMode blendMode) { - setBlendMode(blendMode, true); - drawRect(0, 0, width, height, color, true); -} - /* Image class */ void Image::initFromVRAMRect( diff --git a/src/common/gpu.hpp b/src/common/gpu.hpp index 230e75f..845ee4f 100644 --- a/src/common/gpu.hpp +++ b/src/common/gpu.hpp @@ -143,6 +143,14 @@ public: ); } + inline void drawBackdrop(Color color) { + drawRect(0, 0, width, height, color); + } + inline void drawBackdrop(Color color, BlendMode blendMode) { + setBlendMode(blendMode, true); + drawRect(0, 0, width, height, color, true); + } + void setResolution( VideoMode mode, int width, int height, bool forceInterlace = false, bool sideBySide = false @@ -169,7 +177,6 @@ public: int x, int y, int width, int height, Color top, Color middle, Color bottom, bool blend = false ); - void drawBackdrop(Color color, BlendMode blendMode); }; /* Image class */ diff --git a/src/main/app/app.cpp b/src/main/app/app.cpp index 3ed76f8..582d1c9 100644 --- a/src/main/app/app.cpp +++ b/src/main/app/app.cpp @@ -180,6 +180,8 @@ void FileIOManager::closeResourceFile(void) { static constexpr size_t _WORKER_STACK_SIZE = 0x20000; +static constexpr int _SPLASH_SCREEN_TIMEOUT = 5; + App::App(ui::Context &ctx) #ifdef ENABLE_LOG_BUFFER : _logOverlay(_logBuffer), @@ -238,6 +240,7 @@ void App::_loadResources(void) { res.loadTIM(_background.tile, "assets/textures/background.tim"); res.loadTIM(_ctx.font.image, "assets/textures/font.tim"); res.loadStruct(_ctx.font.metrics, "assets/textures/font.metrics"); + res.loadTIM(_splashOverlay.image, "assets/textures/splash.tim"); res.loadStruct(_ctx.colors, "assets/app.palette"); res.loadData(_stringTable, "assets/app.strings"); @@ -303,6 +306,36 @@ bool App::_takeScreenshot(void) { return true; } +void App::_updateOverlays(void) { + // Date and time overlay + static char dateString[24]; + util::Date date; + + io::getRTCTime(date); + date.toString(dateString); + + _textOverlay.leftText = dateString; + + // Splash screen overlay + int timeout = _ctx.gpuCtx.refreshRate * _SPLASH_SCREEN_TIMEOUT; + + if ((_workerStatus.status == WORKER_DONE) || (_ctx.time > timeout)) + _splashOverlay.hide(_ctx); + + // Log overlay + if ( + _ctx.buttons.released(ui::BTN_DEBUG) && + !_ctx.buttons.longReleased(ui::BTN_DEBUG) + ) + _logOverlay.toggle(_ctx); + + // Screenshot overlay + if (_ctx.buttons.longPressed(ui::BTN_DEBUG)) { + if (_takeScreenshot()) + _screenshotOverlay.animate(_ctx); + } +} + void App::_runWorker( bool (App::*func)(void), ui::Screen &next, bool goBack, bool playSound ) { @@ -361,40 +394,27 @@ void App::_interruptHandler(void) { _fileIO.loadResourceFile(nullptr); _loadResources(); - char dateString[24]; - - _textOverlay.leftText = dateString; - _textOverlay.rightText = "v" VERSION_STRING; - _screenshotOverlay.callback = [](ui::Context &ctx) -> bool { - return APP->_takeScreenshot(); - }; + _textOverlay.rightText = "v" VERSION_STRING; _ctx.backgrounds[0] = &_background; _ctx.backgrounds[1] = &_textOverlay; + _ctx.overlays[0] = &_splashOverlay; #ifdef ENABLE_LOG_BUFFER - _ctx.overlays[0] = &_logOverlay; + _ctx.overlays[1] = &_logOverlay; #endif - _ctx.overlays[1] = &_screenshotOverlay; + _ctx.overlays[2] = &_screenshotOverlay; -#ifdef ENABLE_AUTOBOOT _runWorker(&App::_ideInitWorker, _warningScreen); -#else - // If autoboot is disabled, show the warning screen before initializing the - // drives in order to give them enough time to spin up. - _runWorker(nullptr, _warningScreen); -#endif _setupInterrupts(); + + _splashOverlay.show(_ctx); _ctx.sounds[ui::SOUND_STARTUP].play(); for (;;) { - util::Date date; - - io::getRTCTime(date); - date.toString(dateString); - _ctx.update(); - _ctx.draw(); + _updateOverlays(); + _ctx.draw(); switchThreadImmediate(&_workerThread); _ctx.gpuCtx.flip(); } diff --git a/src/main/app/app.hpp b/src/main/app/app.hpp index 5100dbe..69359ac 100644 --- a/src/main/app/app.hpp +++ b/src/main/app/app.hpp @@ -180,12 +180,13 @@ private: ResolutionScreen _resolutionScreen; AboutScreen _aboutScreen; + ui::TiledBackground _background; + ui::TextOverlay _textOverlay; + ui::SplashOverlay _splashOverlay; #ifdef ENABLE_LOG_BUFFER util::LogBuffer _logBuffer; ui::LogOverlay _logOverlay; #endif - ui::TiledBackground _background; - ui::TextOverlay _textOverlay; ui::ScreenshotOverlay _screenshotOverlay; ui::Context &_ctx; @@ -214,6 +215,7 @@ private: char *output, size_t length, const char *path, int maxIndex = 9999 ); bool _takeScreenshot(void); + void _updateOverlays(void); void _runWorker( bool (App::*func)(void), ui::Screen &next, bool goBack = false, bool playSound = false diff --git a/src/main/app/cartactions.cpp b/src/main/app/cartactions.cpp index 972e1fa..7db6dcb 100644 --- a/src/main/app/cartactions.cpp +++ b/src/main/app/cartactions.cpp @@ -239,6 +239,7 @@ void QRCodeScreen::show(ui::Context &ctx, bool goBack) { _title = STR("QRCodeScreen.title"); _prompt = STR("QRCodeScreen.prompt"); + _image = &_code; _imageScale = _QR_CODE_SCALE; _imagePadding = _QR_CODE_SCALE * _QR_CODE_PADDING; _backdropColor = 0xffffff; diff --git a/src/main/app/cartactions.hpp b/src/main/app/cartactions.hpp index 39e7f29..eb5150a 100644 --- a/src/main/app/cartactions.hpp +++ b/src/main/app/cartactions.hpp @@ -45,6 +45,9 @@ public: }; class QRCodeScreen : public ui::ImageScreen { +private: + gpu::Image _code; + public: bool valid; @@ -52,14 +55,14 @@ public: : valid(false) {} inline bool generateCode(const char *textInput) { - if (!gpu::generateQRCode(_image, 960, 256, textInput)) + if (!gpu::generateQRCode(_code, 960, 256, textInput)) return false; valid = true; return true; } inline bool generateCode(const uint8_t *binaryInput, size_t length) { - if (!gpu::generateQRCode(_image, 960, 256, binaryInput, length)) + if (!gpu::generateQRCode(_code, 960, 256, binaryInput, length)) return false; valid = true; diff --git a/src/main/app/tests.cpp b/src/main/app/tests.cpp index bef5477..77fddd9 100644 --- a/src/main/app/tests.cpp +++ b/src/main/app/tests.cpp @@ -303,9 +303,7 @@ void TestPatternScreen::_drawTextOverlay( 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 - ); + ctx.gpuCtx.drawBackdrop(_BACKGROUND_COLOR); } void TestPatternScreen::update(ui::Context &ctx) { diff --git a/src/main/uibase.cpp b/src/main/uibase.cpp index fc8dc2a..f7a71d1 100644 --- a/src/main/uibase.cpp +++ b/src/main/uibase.cpp @@ -234,11 +234,6 @@ void Context::draw(void) { void Context::update(void) { buttons.update(); - for (auto layer : overlays) { - if (layer) - layer->update(*this); - } - auto screen = getCurrentScreen(); if (screen) @@ -294,22 +289,56 @@ void TextOverlay::draw(Context &ctx, bool active) const { } } -LogOverlay::LogOverlay(util::LogBuffer &buffer) -: _buffer(buffer) { - _slideAnim.setValue(0); +void SplashOverlay::draw(Context &ctx, bool active) const { + int brightness = _fadeAnim.getValue(ctx.time); + + if (!brightness) + return; + + // Backdrop + _newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height); + ctx.gpuCtx.drawBackdrop( + gp0_rgb(brightness, brightness, brightness), GP0_BLEND_SUBTRACT + ); + + if (brightness < 0xff) + return; + + // Image + int x = (ctx.gpuCtx.width - image.width) / 2; + int y = (ctx.gpuCtx.height - image.height) / 2; + + image.draw(ctx.gpuCtx, x, y); +} + +void SplashOverlay::show(Context &ctx) { + if (!_fadeAnim.getTargetValue()) +#if 0 + _fadeAnim.setValue(ctx.time, 0, 0xff, SPEED_SLOWEST); +#else + _fadeAnim.setValue(0xff); +#endif +} + +void SplashOverlay::hide(Context &ctx) { + if (_fadeAnim.getTargetValue()) + _fadeAnim.setValue(ctx.time, 0xff, 0, SPEED_SLOWEST); } void LogOverlay::draw(Context &ctx, bool active) const { int offset = _slideAnim.getValue(ctx.time); + if (!offset) return; + // Backdrop _newLayer( ctx, 0, offset - ctx.gpuCtx.height, ctx.gpuCtx.width, ctx.gpuCtx.height ); ctx.gpuCtx.drawBackdrop(ctx.colors[COLOR_BACKDROP], GP0_BLEND_SUBTRACT); + // Text int screenHeight = ctx.gpuCtx.height - SCREEN_MIN_MARGIN_Y * 2; int linesShown = screenHeight / ctx.font.metrics.lineHeight; @@ -330,15 +359,11 @@ void LogOverlay::draw(Context &ctx, bool active) const { } } -void LogOverlay::update(Context &ctx) { - if ( - ctx.buttons.released(BTN_DEBUG) && !ctx.buttons.longReleased(BTN_DEBUG) - ) { - bool shown = !_slideAnim.getTargetValue(); +void LogOverlay::toggle(Context &ctx) { + bool shown = !_slideAnim.getTargetValue(); - _slideAnim.setValue(ctx.time, shown ? ctx.gpuCtx.height : 0, SPEED_SLOW); - ctx.sounds[shown ? SOUND_ENTER : SOUND_EXIT].play(); - } + _slideAnim.setValue(ctx.time, shown ? ctx.gpuCtx.height : 0, SPEED_SLOW); + ctx.sounds[shown ? SOUND_ENTER : SOUND_EXIT].play(); } void ScreenshotOverlay::draw(Context &ctx, bool active) const { @@ -353,13 +378,9 @@ void ScreenshotOverlay::draw(Context &ctx, bool active) const { ); } -void ScreenshotOverlay::update(Context &ctx) { - if (ctx.buttons.longPressed(BTN_DEBUG)) { - if (callback(ctx)) { - _flashAnim.setValue(ctx.time, 0xff, 0, SPEED_SLOW); - ctx.sounds[ui::SOUND_SCREENSHOT].play(); - } - } +void ScreenshotOverlay::animate(Context &ctx) { + _flashAnim.setValue(ctx.time, 0xff, 0, SPEED_SLOW); + ctx.sounds[ui::SOUND_SCREENSHOT].play(); } /* Base screen classes */ @@ -381,15 +402,17 @@ void AnimatedScreen::hide(Context &ctx, bool goBack) { } void BackdropScreen::show(Context &ctx, bool goBack) { - _backdropAnim.setValue(ctx.time, 0, 0x50, SPEED_FAST); + if (!_fadeAnim.getTargetValue()) + _fadeAnim.setValue(ctx.time, 0, 0x50, SPEED_FAST); } void BackdropScreen::hide(Context &ctx, bool goBack) { - _backdropAnim.setValue(ctx.time, 0x50, 0, SPEED_FAST); + if (_fadeAnim.getTargetValue()) + _fadeAnim.setValue(ctx.time, 0x50, 0, SPEED_FAST); } void BackdropScreen::draw(Context &ctx, bool active) const { - int brightness = _backdropAnim.getValue(ctx.time); + int brightness = _fadeAnim.getValue(ctx.time); if (!brightness) return; diff --git a/src/main/uibase.hpp b/src/main/uibase.hpp index 3928743..16cb4ae 100644 --- a/src/main/uibase.hpp +++ b/src/main/uibase.hpp @@ -64,7 +64,8 @@ enum Sound { enum AnimationSpeed { SPEED_FASTEST = 10, SPEED_FAST = 15, - SPEED_SLOW = 20 + SPEED_SLOW = 20, + SPEED_SLOWEST = 30 }; static constexpr int SCREEN_MARGIN_X = 16; @@ -214,7 +215,6 @@ protected: public: virtual void draw(Context &ctx, bool active = true) const {} - virtual void update(Context &ctx) {} }; class TiledBackground : public Layer { @@ -234,15 +234,29 @@ public: void draw(Context &ctx, bool active = true) const; }; +class SplashOverlay : public Layer { +private: + util::Tween _fadeAnim; + +public: + gpu::Image image; + + void draw(Context &ctx, bool active = true) const; + void show(Context &ctx); + void hide(Context &ctx); +}; + class LogOverlay : public Layer { private: util::LogBuffer &_buffer; util::Tween _slideAnim; public: - LogOverlay(util::LogBuffer &buffer); + inline LogOverlay(util::LogBuffer &buffer) + : _buffer(buffer) {} + void draw(Context &ctx, bool active = true) const; - void update(Context &ctx); + void toggle(Context &ctx); }; class ScreenshotOverlay : public Layer { @@ -250,13 +264,8 @@ private: util::Tween _flashAnim; public: - bool (*callback)(ui::Context &ctx); - - inline ScreenshotOverlay(void) - : callback(nullptr) {} - void draw(Context &ctx, bool active = true) const; - void update(Context &ctx); + void animate(Context &ctx); }; /* Base screen classes */ @@ -286,7 +295,7 @@ public: class BackdropScreen : public Screen { private: - util::Tween _backdropAnim; + util::Tween _fadeAnim; public: virtual void show(Context &ctx, bool goBack = false); diff --git a/src/main/uicommon.cpp b/src/main/uicommon.cpp index 824af1d..d4431a4 100644 --- a/src/main/uicommon.cpp +++ b/src/main/uicommon.cpp @@ -24,13 +24,6 @@ namespace ui { /* Common screens */ -void PlaceholderScreen::draw(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, ctx.colors[COLOR_WINDOW2] - ); -} - TextScreen::TextScreen(void) : _title(nullptr), _body(nullptr), _prompt(nullptr) {} @@ -128,16 +121,17 @@ void TextScreen::update(Context &ctx) { } ImageScreen::ImageScreen(void) -: _imageScale(1), _imagePadding(0), _title(nullptr), _prompt(nullptr) {} +: _image(nullptr), _imageScale(1), _imagePadding(0), _title(nullptr), +_prompt(nullptr) {} void ImageScreen::draw(Context &ctx, bool active) const { _newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height); - if (_image.width && _image.height) { + if (_image) { int x = ctx.gpuCtx.width / 2; int y = ctx.gpuCtx.height / 2; - int width = _image.width * _imageScale / 2; - int height = _image.height * _imageScale / 2; + int width = _image->width * _imageScale / 2; + int height = _image->height * _imageScale / 2; if (_prompt) y -= (SCREEN_PROMPT_HEIGHT - ctx.font.metrics.lineHeight) / 2; @@ -153,9 +147,12 @@ void ImageScreen::draw(Context &ctx, bool active) const { } // Image - _image.drawScaled( - ctx.gpuCtx, x - width - 1, y - height - 1, width * 2, height * 2 - ); + if (_imageScale > 1) + _image->drawScaled( + ctx.gpuCtx, x - width - 1, y - height - 1, width * 2, height * 2 + ); + else + _image->draw(ctx.gpuCtx, x - width, y - height); } // Text diff --git a/src/main/uicommon.hpp b/src/main/uicommon.hpp index 2ba9091..7f72727 100644 --- a/src/main/uicommon.hpp +++ b/src/main/uicommon.hpp @@ -25,11 +25,6 @@ namespace ui { /* Common screens */ -class PlaceholderScreen : public AnimatedScreen { -public: - void draw(Context &ctx, bool active) const; -}; - class TextScreen : public AnimatedScreen { private: util::Tween _scrollAnim; @@ -54,7 +49,7 @@ public: class ImageScreen : public AnimatedScreen { protected: - gpu::Image _image; + gpu::Image *_image; int _imageScale, _imagePadding; gpu::Color _backdropColor;