diff --git a/README.md b/README.md
index 05a97dc..572f630 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
+
573in1 is a full-featured homebrew maintenance and troubleshooting tool, game
@@ -43,17 +43,17 @@ instructions. **Reading the documentation before proceeding is highly**
## Screenshots
-
+
+ width="320" />
+ width="320" />
+ width="320" />
+ width="320" />
+ 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;