diva: add partition and button LED support

This commit is contained in:
Dniel97 2025-12-21 20:43:08 +01:00
parent ce77d60905
commit e4bd0543c9
No known key found for this signature in database
GPG Key ID: DE105D481972329C
7 changed files with 150 additions and 13 deletions

View File

@ -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);
}

View File

@ -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 {

View File

@ -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

View File

@ -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);
}

View File

@ -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;
}

View File

@ -1,21 +1,45 @@
#pragma once
/*
DIVA CUSTOM IO API
Changelog:
- 0x0100: Initial API version
- 0x0101: Add partition and button led support
*/
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
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);

View File

@ -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);