/* * PSn00bSDK Konami System 573 example * (C) 2022 spicyjpeg - MPL licensed * * This is a minimal example demonstrating how to target the Konami System 573 * using PSn00bSDK. The System 573 is a PS1-based arcade motherboard that * powered various Konami arcade games throughout the late 1990s, most notably * Dance Dance Revolution and other Bemani rhythm games. It came in several * configurations, with slightly different I/O connectors depending on the game * and optional add-on boards providing extra features such as light control * outputs or MP3 audio playback. * * Unlike other arcade systems based on PS1 hardware, the 573 is mostly * identical to a regular PS1, with almost all custom extensions mapped into * the expansion port region at 0x1f000000. The major differences are: * * - RAM is 4 MB instead of 2, and VRAM is 2 MB instead of 1. It is recommended * *not* to use the additional memory to preserve PS1 compatibility. * * - The CD drive is replaced by a standard IDE/ATAPI drive (which most of the * time is going to be an aftermarket DVD drive, as the original drives the * system shipped with were prone to failure and couldn't read CD-Rs). This * also means the 573 has no support at all for XA audio playback, as XA is * not part of the CD-ROM specification implemented by IDE drives. CD audio * is supported by most IDE drives, but 573 units with the digital I/O board * installed have the 4-pin audio cable plugged into that instead of the * drive. The IDE bus is connected to IRQ10 and DMA5 (expansion port) instead * of IRQ2 and DMA3, which go unused. * * - The BIOS seems to have most file I/O APIs removed and exposes no functions * whatsoever for accessing the IDE drive or the filesystem on the disc. The * launcher/shell is completely different from Sony's shell and is capable of * loading an executable from the CD drive, a PCMCIA memory-mapped flash card * or the internal 16 MB flash memory. * * - The SPI controller bus seems to be left unconnected. Inputs are routed to * a JAMMA PCB edge connector and handled through two custom/relabeled chips, * which expose the inputs as memory-mapped registers. There is also a JVS * port (i.e. RS-485 serial bus wired to a USB-A connector, commonly used for * daisy-chaining peripherals in arcade cabinets) managed by a * microcontroller. * * - There is a "security cartridge" slot, which breaks out the serial port as * well as several GPIO pins. All security cartridge communication and DRM is * handled by games rather than by the BIOS, so a security cartridge is *not* * required to boot homebrew. Each game came with a different cartridge type; * many of them expose the serial port or provide additional game-specific * I/O connectors. * * Currently the only publicly available documentation for the custom registers * is the System 573 MAME driver. Also keep in mind that the psxcd library does * not yet support IDE drives, so the 573's drive can only be accessed by * writing a custom ATAPI driver and ISO9660 parser (which is out of the scope * of this example). * * https://github.com/mamedev/mame/blob/master/src/mame/drivers/ksys573.cpp * https://github.com/mamedev/mame/blob/master/src/mame/machine/k573dio.cpp */ #include #include #include #include #include #include #include "k573io.h" const char *const IO_BOARD_TYPES[] = { "ANALOG", "DIGITAL" }; #define btoi(x) ((((x) >> 4) & 0xf) * 10 + ((x) & 0xf)) /* Display/GPU context utilities */ #define SCREEN_XRES 320 #define SCREEN_YRES 240 #define BGCOLOR_R 48 #define BGCOLOR_G 24 #define BGCOLOR_B 0 typedef struct { DISPENV disp; DRAWENV draw; } Framebuffer; typedef struct { Framebuffer db[2]; int db_active; } RenderContext; void init_context(RenderContext *ctx) { Framebuffer *db; ResetGraph(0); ctx->db_active = 0; db = &(ctx->db[0]); SetDefDispEnv(&(db->disp), 0, 0, SCREEN_XRES, SCREEN_YRES); SetDefDrawEnv(&(db->draw), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B); db->draw.isbg = 1; db->draw.dtd = 1; db = &(ctx->db[1]); SetDefDispEnv(&(db->disp), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); SetDefDrawEnv(&(db->draw), 0, 0, SCREEN_XRES, SCREEN_YRES); setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B); db->draw.isbg = 1; db->draw.dtd = 1; PutDrawEnv(&(db->draw)); //PutDispEnv(&(db->disp)); // Create a text stream at the top of the screen. FntLoad(960, 0); FntOpen(8, 16, 304, 208, 2, 512); } void display(RenderContext *ctx) { Framebuffer *db; DrawSync(0); VSync(0); ctx->db_active ^= 1; db = &(ctx->db[ctx->db_active]); PutDrawEnv(&(db->draw)); PutDispEnv(&(db->disp)); SetDispMask(1); } /* Main */ static RenderContext ctx; #define SHOW_STATUS(...) { FntPrint(-1, __VA_ARGS__); FntFlush(-1); display(&ctx); } #define SHOW_ERROR(...) { SHOW_STATUS(__VA_ARGS__); while (1) __asm__("nop"); } int main(int argc, const char* argv[]) { init_context(&ctx); K573_Init(); const char *const version = (const char *const) GetSystemInfo(0x02); //if (strncmp(version, "Konami OS", 9)) //SHOW_ERROR("ERROR: NOT RUNNING ON A SYSTEM 573!\n\n[%s]\n", version); uint32_t counter = 0; uint32_t inputs = K573_GetJAMMAInputs(); uint32_t last_inputs = 0xff; uint32_t current_light = 0; // DIP switch 1 is used to determine if an analog or digital I/O board is // installed. K573_IOBoardType io_type = (inputs & JAMMA_DIP1) ? IO_TYPE_ANALOG : IO_TYPE_DIGITAL; K573_SetBoardType(io_type); K573_SetLights(1); while (1) { inputs = K573_GetJAMMAInputs(); FntPrint(-1, "COUNTER=%d\n", counter++); FntPrint(-1, "\nJAMMA INPUTS:\n"); FntPrint(-1, " IN2 =%016@\n", inputs & 0xffff); FntPrint(-1, " IN3_L=%04@\n", (inputs >> 16) & 0x0f); FntPrint(-1, " IN3_H=%04@\n", (inputs >> 20) & 0x0f); FntPrint(-1, " IN1_H=%05@\n", (inputs >> 24) & 0x1f); FntPrint(-1, "\nCABINET LIGHTS:\n"); FntPrint(-1, " BOARD=%s\n", IO_BOARD_TYPES[io_type]); FntPrint(-1, " LIGHT=%d\n", current_light); FntPrint(-1, "\n [DIP SW1] CHANGE BOARD TYPE\n"); FntPrint(-1, "\n [TEST SW] CHANGE ACTIVE LIGHT\n"); // Request the current date/time from the RTC and display it. K573_RTC[RTC_REG_CTRL] |= 0x40; FntPrint(-1, "\nRTC:\n"); FntPrint( -1, " %02d-%02d-%02d %02d:%02d:%02d\n", btoi(K573_RTC[RTC_REG_YEAR]), btoi(K573_RTC[RTC_REG_MONTH]), btoi(K573_RTC[RTC_REG_DAY_OF_MONTH] & 0x3f), btoi(K573_RTC[RTC_REG_HOURS]), btoi(K573_RTC[RTC_REG_MINUTES]), btoi(K573_RTC[RTC_REG_SECONDS] & 0x7f) ); FntPrint(-1, "\nSYSTEM:\n"); FntPrint(-1, " KERNEL=%s\n", version); FntPrint(-1, " DIP SW=%03@\n", inputs >> 29); FntPrint(-1, " PCMCIA=%02@\n", (inputs >> 26) & 0x3); FntFlush(-1); display(&ctx); // Reset the watchdog. This must be done at least once per frame to // prevent the 573 from rebooting. K573_RESET_WATCHDOG(); // Change the currently active light if the test button on the 573's // front panel is pressed. DDR non-light outputs are skipped. if (!(last_inputs & JAMMA_TEST) && (inputs & JAMMA_TEST)) { current_light++; if ( (current_light == 4) || // DDR_LIGHT_P1_MUX_DATA (current_light == 7) || // DDR_LIGHT_P1_MUX_CLK (current_light == 12) || // DDR_LIGHT_P2_MUX_DATA (current_light == 15) // DDR_LIGHT_P2_MUX_CLK ) current_light++; current_light %= 32; K573_SetLights(1 << current_light); } // if DIP switch 1 is toggled, change the I/O board type. if ((last_inputs & JAMMA_DIP1) != (inputs & JAMMA_DIP1)) { io_type = (inputs & JAMMA_DIP1) ? IO_TYPE_ANALOG : IO_TYPE_DIGITAL; K573_SetBoardType(io_type); K573_SetLights(1 << current_light); } last_inputs = inputs; } return 0; }