From e4bd0543c9883d1e7e8cd3ba8aec255bc721c057 Mon Sep 17 00:00:00 2001 From: Dniel97 Date: Sun, 21 Dec 2025 20:43:08 +0100 Subject: [PATCH] diva: add partition and button LED support --- games/divahook/diva-dll.c | 37 +++++++++++++++++--- games/divahook/diva-dll.h | 2 ++ games/divahook/divahook.def | 2 ++ games/divahook/jvs.c | 25 ++++++++++++++ games/divaio/divaio.c | 27 ++++++++++++++- games/divaio/divaio.h | 69 +++++++++++++++++++++++++++++++++---- games/idzhook/jvs.c | 1 + 7 files changed, 150 insertions(+), 13 deletions(-) diff --git a/games/divahook/diva-dll.c b/games/divahook/diva-dll.c index c24cf01..9a5d057 100644 --- a/games/divahook/diva-dll.c +++ b/games/divahook/diva-dll.c @@ -30,9 +30,28 @@ const struct dll_bind_sym diva_dll_syms[] = { }, { .sym = "diva_io_slider_set_leds", .off = offsetof(struct diva_dll, slider_set_leds), + }, { + .sym = "diva_io_led_init", + .off = offsetof(struct diva_dll, led_init), + }, { + .sym = "diva_io_led_set_leds", + .off = offsetof(struct diva_dll, led_set_leds), } }; +/* Helper function to determine upon dll_bind failure whether the required functions were found + NOTE: relies on symbols order declared above */ +static HRESULT has_enough_symbols(uint16_t version, uint8_t count) +{ + if ( version < 0x0101 && count == 7 ) + return S_OK; + + if ( version >= 0x0101 && count == 9 ) + return S_OK; + + return E_FAIL; +} + struct diva_dll diva_dll; // Copypasta DLL binding and diagnostic message boilerplate. @@ -92,16 +111,24 @@ HRESULT diva_dll_init(const struct diva_dll_config *cfg, HINSTANCE self) } sym = diva_dll_syms; + const struct dll_bind_sym *init_sym = &sym[0]; hr = dll_bind(&diva_dll, src, &sym, _countof(diva_dll_syms)); if (FAILED(hr)) { if (src != self) { - dprintf("Diva IO: Custom IO DLL does not provide function " - "\"%s\". Please contact your IO DLL's developer for " - "further assistance.\n", - sym->sym); + // Might still be ok depending on external dll API version + int bind_count = sym - init_sym; + if ( has_enough_symbols(diva_dll.api_version, bind_count) == S_OK ) + { + hr = S_OK; + } else { + dprintf("Diva IO: Custom IO DLL does not provide function " + "\"%s\". Please contact your IO DLL's developer for " + "further assistance.\n", + sym->sym); - goto end; + goto end; + } } else { dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); } diff --git a/games/divahook/diva-dll.h b/games/divahook/diva-dll.h index 1e57947..67cf335 100644 --- a/games/divahook/diva-dll.h +++ b/games/divahook/diva-dll.h @@ -13,6 +13,8 @@ struct diva_dll { void (*slider_start)(diva_io_slider_callback_t callback); void (*slider_stop)(void); void (*slider_set_leds)(const uint8_t *rgb); + HRESULT (*led_init)(void); + void (*led_set_leds)(uint8_t board, const uint8_t *rgb); }; struct diva_dll_config { diff --git a/games/divahook/divahook.def b/games/divahook/divahook.def index 9101509..4d2ae44 100644 --- a/games/divahook/divahook.def +++ b/games/divahook/divahook.def @@ -19,3 +19,5 @@ EXPORTS diva_io_slider_set_leds diva_io_slider_start diva_io_slider_stop + diva_io_led_init + diva_io_led_set_leds diff --git a/games/divahook/jvs.c b/games/divahook/jvs.c index f718438..e53f1ac 100644 --- a/games/divahook/jvs.c +++ b/games/divahook/jvs.c @@ -20,10 +20,12 @@ static void diva_jvs_read_coin_counter( void *ctx, uint8_t slot_no, uint16_t *out); +static void diva_jvs_write_gpio(void *ctx, uint32_t state); static const struct io3_ops diva_jvs_io3_ops = { .read_switches = diva_jvs_read_switches, .read_coin_counter = diva_jvs_read_coin_counter, + .write_gpio = diva_jvs_write_gpio }; static struct io3 diva_jvs_io3; @@ -107,3 +109,26 @@ static void diva_jvs_read_coin_counter( diva_dll.jvs_read_coin_counter(out); } + +static void diva_jvs_write_gpio(void *ctx, uint32_t state) +{ + assert(diva_dll.led_set_leds != NULL); + + // Since Sega uses an odd ordering for the first part of the bitfield, + // let's normalize the data and just send over bytes for the receiver + // to interpret as ON/OFF values. + uint8_t rgb_out[10] = { + state & DIVA_IO_LED_LEFT_PARTITION_RED ? 0xFF : 0x00, + state & DIVA_IO_LED_LEFT_PARTITION_GREEN ? 0xFF : 0x00, + state & DIVA_IO_LED_LEFT_PARTITION_BLUE ? 0xFF : 0x00, + state & DIVA_IO_LED_RIGHT_PARTITION_RED ? 0xFF : 0x00, + state & DIVA_IO_LED_RIGHT_PARTITION_GREEN ? 0xFF : 0x00, + state & DIVA_IO_LED_RIGHT_PARTITION_BLUE ? 0xFF : 0x00, + state & DIVA_IO_LED_BTN_TRIANGLE ? 0xFF : 0x00, + state & DIVA_IO_LED_BTN_CROSS ? 0xFF : 0x00, + state & DIVA_IO_LED_BTN_SQUARE ? 0xFF : 0x00, + state & DIVA_IO_LED_BTN_CIRCLE ? 0xFF : 0x00 + }; + + diva_dll.led_set_leds(0, rgb_out); +} diff --git a/games/divaio/divaio.c b/games/divaio/divaio.c index 0001f2e..d44d723 100644 --- a/games/divaio/divaio.c +++ b/games/divaio/divaio.c @@ -7,7 +7,9 @@ #include "divaio/divaio.h" #include "divaio/config.h" + #include "util/env.h" +#include "util/dprintf.h" static unsigned int __stdcall diva_io_slider_thread_proc(void *ctx); @@ -19,7 +21,7 @@ static struct diva_io_config diva_io_cfg; uint16_t diva_io_get_api_version(void) { - return 0x0100; + return 0x0101; } HRESULT diva_io_jvs_init(void) @@ -132,3 +134,26 @@ static unsigned int __stdcall diva_io_slider_thread_proc(void *ctx) return 0; } + +HRESULT diva_io_led_init(void) +{ + return S_OK; +} + +void diva_io_led_set_leds(uint8_t board, const uint8_t *rgb) +{ +#if 0 + dprintf("DIVA LED: LEFT PARTITION RED: %02X\n", rgb[0]); + dprintf("DIVA LED: LEFT PARTITION GREEN: %02X\n", rgb[1]); + dprintf("DIVA LED: LEFT PARTITION BLUE: %02X\n", rgb[2]); + dprintf("DIVA LED: RIGHT PARTITION RED: %02X\n", rgb[3]); + dprintf("DIVA LED: RIGHT PARTITION GREEN: %02X\n", rgb[4]); + dprintf("DIVA LED: RIGHT PARTITION BLUE: %02X\n", rgb[5]); + dprintf("DIVA LED: BTN TRIANGLE: %02X\n", rgb[6]); + dprintf("DIVA LED: BTN CROSS: %02X\n", rgb[7]); + dprintf("DIVA LED: BTN SQUARE: %02X\n", rgb[8]); + dprintf("DIVA LED: BTN CIRCLE: %02X\n", rgb[9]); +#endif + + return; +} diff --git a/games/divaio/divaio.h b/games/divaio/divaio.h index 7dd13b8..c04dfb0 100644 --- a/games/divaio/divaio.h +++ b/games/divaio/divaio.h @@ -1,21 +1,45 @@ #pragma once +/* + DIVA CUSTOM IO API + + Changelog: + + - 0x0100: Initial API version + - 0x0101: Add partition and button led support +*/ + #include #include #include enum { - DIVA_IO_OPBTN_TEST = 0x01, - DIVA_IO_OPBTN_SERVICE = 0x02 + DIVA_IO_OPBTN_TEST = 0x01, + DIVA_IO_OPBTN_SERVICE = 0x02 }; enum { - DIVA_IO_GAMEBTN_CIRCLE = 0x01, - DIVA_IO_GAMEBTN_CROSS = 0x02, - DIVA_IO_GAMEBTN_SQUARE = 0x04, - DIVA_IO_GAMEBTN_TRIANGLE = 0x08, - DIVA_IO_GAMEBTN_START = 0x10, + DIVA_IO_GAMEBTN_CIRCLE = 0x01, + DIVA_IO_GAMEBTN_CROSS = 0x02, + DIVA_IO_GAMEBTN_SQUARE = 0x04, + DIVA_IO_GAMEBTN_TRIANGLE = 0x08, + DIVA_IO_GAMEBTN_START = 0x10, +}; + +enum { + /* These are the bitmasks to use when checking which + lights are triggered on incoming IO3 GPIO writes. */ + DIVA_IO_LED_LEFT_PARTITION_RED = 1 << 1, + DIVA_IO_LED_LEFT_PARTITION_GREEN = 1 << 0, + DIVA_IO_LED_LEFT_PARTITION_BLUE = 1 << 15, + DIVA_IO_LED_RIGHT_PARTITION_RED = 1 << 14, + DIVA_IO_LED_RIGHT_PARTITION_GREEN = 1 << 13, + DIVA_IO_LED_RIGHT_PARTITION_BLUE = 1 << 12, + DIVA_IO_LED_BTN_TRIANGLE = 1 << 6, + DIVA_IO_LED_BTN_CROSS = 1 << 3, + DIVA_IO_LED_BTN_SQUARE = 1 << 5, + DIVA_IO_LED_BTN_CIRCLE = 1 << 2 }; /* Get the version of the Project Diva IO API that this DLL supports. This @@ -118,3 +142,34 @@ void diva_io_slider_stop(void); Minimum API version: 0x0100 */ void diva_io_slider_set_leds(const uint8_t *rgb); + +/* Initialize LED emulation. This function will be called before any + other diva_io_led_*() function calls. + + All subsequent calls may originate from arbitrary threads and some may + overlap with each other. Ensuring synchronization inside your IO DLL is + your responsibility. + + Minimum API version: 0x0101 */ + +HRESULT diva_io_led_init(void); + +/* Update the cabinet button LEDs. rgb is a pointer to an array up to 10 bytes. + + The LEDs are laid out as follows: + [0]: LEFT PARTITION RED LED + [1]: LEFT PARTITION GREEN LED + [2]: LEFT PARTITION BLUE LED + [3]: RIGHT PARTITION RED LED + [4]: RIGHT PARTITION GREEN LED + [5]: RIGHT PARTITION BLUE LED + [6]: BTN TRIANGLE LED + [7]: BTN CROSS LED + [8]: BTN SQUARE LED + [9]: BTN CIRCLE LED + + The LED is turned on when the byte is 255 and turned off when the byte is 0. + + Minimum API version: 0x0101 */ + +void diva_io_led_set_leds(uint8_t board, const uint8_t *rgb); diff --git a/games/idzhook/jvs.c b/games/idzhook/jvs.c index 2ded71c..64318ca 100644 --- a/games/idzhook/jvs.c +++ b/games/idzhook/jvs.c @@ -176,6 +176,7 @@ static void idz_jvs_read_coin_counter( idz_dll.jvs_read_coin_counter(out); } + static void idz_jvs_write_gpio(void *ctx, uint32_t state) { assert(idz_dll.led_set_leds != NULL);