divaio: Break out Project Diva IO DLL

This commit is contained in:
Tau 2019-05-03 14:47:09 -04:00
parent 46ab6c3d96
commit 2405526f35
8 changed files with 275 additions and 101 deletions

View File

@ -8,6 +8,8 @@
#include "board/io3.h"
#include "divaio/divaio.h"
#include "jvs/jvs-bus.h"
#include "util/dprintf.h"
@ -21,8 +23,6 @@ static const struct io3_ops diva_jvs_io3_ops = {
};
static struct io3 diva_jvs_io3;
static bool diva_jvs_coin;
static uint16_t diva_jvs_coins;
void diva_jvs_init(void)
{
@ -32,42 +32,44 @@ void diva_jvs_init(void)
static void diva_jvs_read_switches(void *ctx, struct io3_switch_state *out)
{
uint8_t opbtn;
uint8_t gamebtn;
assert(out != NULL);
/* Update gameplay buttons (P2 JVS input is not even polled) */
opbtn = 0;
gamebtn = 0;
if (GetAsyncKeyState('S')) {
out->p1 |= 1 << 9;
}
diva_io_jvs_poll(&opbtn, &gamebtn);
if (GetAsyncKeyState('F')) {
out->p1 |= 1 << 8;
}
if (GetAsyncKeyState('J')) {
out->p1 |= 1 << 7;
}
if (GetAsyncKeyState('L')) {
if (gamebtn & 0x01) {
out->p1 |= 1 << 6;
}
/* Update start button */
if (gamebtn & 0x02) {
out->p1 |= 1 << 7;
}
if (GetAsyncKeyState(VK_SPACE)) {
if (gamebtn & 0x04) {
out->p1 |= 1 << 8;
}
if (gamebtn & 0x08) {
out->p1 |= 1 << 9;
}
if (gamebtn & 0x10) {
out->p1 |= 1 << 15;
}
/* Update test/service buttons */
if (GetAsyncKeyState('1')) {
if (opbtn & 0x01) {
out->system = 0x80;
} else {
out->system = 0;
}
if (GetAsyncKeyState('2')) {
out->p1 |= 0x4000;
if (opbtn & 0x02) {
out->p1 |= 1 << 14;
}
}
@ -77,15 +79,5 @@ static uint16_t diva_jvs_read_coin_counter(void *ctx, uint8_t slot_no)
return 0;
}
if (GetAsyncKeyState('3')) {
if (!diva_jvs_coin) {
dprintf("Diva JVS: Coin drop\n");
diva_jvs_coin = true;
diva_jvs_coins++;
}
} else {
diva_jvs_coin = false;
}
return diva_jvs_coins;
return diva_io_jvs_read_coin_counter();
}

View File

@ -13,6 +13,7 @@ shared_library(
aimeio_dll,
amex_lib,
board_lib,
divaio_dll,
jvs_lib,
platform_lib,
util_lib,

View File

@ -1,7 +1,6 @@
#include <windows.h>
#include <assert.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
@ -11,6 +10,8 @@
#include "divahook/slider-hook.h"
#include "divaio/divaio.h"
#include "hook/iobuf.h"
#include "hook/iohook.h"
@ -29,16 +30,13 @@ static HRESULT slider_req_auto_scan_start(void);
static HRESULT slider_req_auto_scan_stop(void);
static HRESULT slider_req_set_led(const struct slider_req_set_led *req);
static unsigned int __stdcall slider_thread_proc(void *ctx);
static void slider_res_auto_scan(const uint8_t *pressure);
static CRITICAL_SECTION slider_lock;
static struct uart slider_uart;
static uint8_t slider_written_bytes[520];
static uint8_t slider_readable_bytes[520];
static HANDLE slider_thread;
static bool slider_stop;
void slider_hook_init(void)
{
InitializeCriticalSection(&slider_lock);
@ -175,24 +173,7 @@ static HRESULT slider_req_get_board_info(void)
static HRESULT slider_req_auto_scan_start(void)
{
dprintf("Diva slider: Start slider thread\n");
if (slider_thread != NULL) {
dprintf("Thread is already running\n");
return S_OK;
}
slider_thread = (HANDLE) _beginthreadex(
NULL,
0,
slider_thread_proc,
NULL,
0,
NULL);
if (slider_thread == NULL) {
dprintf("Thread launch failed\n");
}
diva_io_slider_start(slider_res_auto_scan);
/* This message is not acknowledged */
@ -205,19 +186,14 @@ static HRESULT slider_req_auto_scan_stop(void)
dprintf("Diva slider: Stop slider thread\n");
if (slider_thread != NULL) {
slider_stop = true;
LeaveCriticalSection(&slider_lock);
/* IO DLL worker thread might attempt to invoke the callback (which needs
to take slider_lock, which we are currently holding) before noticing that
it needs to shut down. Unlock here so that we don't deadlock in that
situation. */
WaitForSingleObject(slider_thread, INFINITE);
CloseHandle(slider_thread);
slider_thread = NULL;
slider_stop = false;
dprintf("Diva slider: Thread has terminated\n");
EnterCriticalSection(&slider_lock);
}
LeaveCriticalSection(&slider_lock);
diva_io_slider_stop();
EnterCriticalSection(&slider_lock);
resp.sync = SLIDER_FRAME_SYNC;
resp.cmd = SLIDER_CMD_AUTO_SCAN_STOP;
@ -229,44 +205,21 @@ static HRESULT slider_req_auto_scan_stop(void)
static HRESULT slider_req_set_led(const struct slider_req_set_led *req)
{
/* This message is not acknowledged */
diva_io_slider_set_leds(req->payload.rgb);
return S_OK;
}
static unsigned int WINAPI slider_thread_proc(void *ctx)
static void slider_res_auto_scan(const uint8_t *pressure)
{
static const int keys[] = {
'Q', 'W', 'E', 'R', 'U', 'I', 'O', 'P',
};
struct slider_resp_auto_scan resp;
uint8_t pressure;
bool stop;
size_t i;
for (;;) {
resp.hdr.sync = SLIDER_FRAME_SYNC;
resp.hdr.cmd = SLIDER_CMD_AUTO_SCAN;
resp.hdr.nbytes = sizeof(resp.pressure);
resp.hdr.sync = SLIDER_FRAME_SYNC;
resp.hdr.cmd = SLIDER_CMD_AUTO_SCAN;
resp.hdr.nbytes = sizeof(resp.pressure);
memcpy(resp.pressure, pressure, sizeof(resp.pressure));
for (i = 0 ; i < 8 ; i++) {
pressure = GetAsyncKeyState(keys[i]) ? 20 : 0;
memset(&resp.pressure[28 - 4 * i], pressure, 4);
}
EnterCriticalSection(&slider_lock);
stop = slider_stop;
if (!stop) {
slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp));
}
LeaveCriticalSection(&slider_lock);
if (stop) {
return 0;
}
Sleep(1);
}
EnterCriticalSection(&slider_lock);
slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp));
LeaveCriticalSection(&slider_lock);
}

124
divaio/divaio.c Normal file
View File

@ -0,0 +1,124 @@
#include <windows.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "divaio/divaio.h"
static unsigned int __stdcall diva_io_slider_thread_proc(void *ctx);
static bool diva_io_coin;
static uint16_t diva_io_coins;
static HANDLE diva_io_slider_thread;
static bool diva_io_slider_stop_flag;
HRESULT diva_io_init(void)
{
return S_OK;
}
void diva_io_jvs_poll(uint8_t *opbtn_out, uint8_t *gamebtn_out)
{
static const int opbtn_vk[] = { '1', '2' };
static const int gamebtn_vk[] = { 'L', 'J', 'F', 'S', ' ' };
uint8_t opbtn;
uint8_t gamebtn;
size_t i;
opbtn = 0;
gamebtn = 0;
for (i = 0 ; i < _countof(opbtn_vk) ; i++) {
if (GetAsyncKeyState(opbtn_vk[i]) & 0x8000) {
opbtn |= 1 << i;
}
}
for (i = 0 ; i < _countof(gamebtn_vk) ; i++) {
if (GetAsyncKeyState(gamebtn_vk[i]) & 0x8000) {
gamebtn |= 1 << i;
}
}
*opbtn_out = opbtn;
*gamebtn_out = gamebtn;
}
uint16_t diva_io_jvs_read_coin_counter(void)
{
if (GetAsyncKeyState('3')) {
if (!diva_io_coin) {
diva_io_coin = true;
diva_io_coins++;
}
} else {
diva_io_coin = false;
}
return diva_io_coins;
}
void diva_io_jvs_set_coin_blocker(bool open)
{}
void diva_io_slider_start(diva_io_slider_callback_t callback)
{
if (diva_io_slider_thread != NULL) {
return;
}
diva_io_slider_thread = (HANDLE) _beginthreadex(
NULL,
0,
diva_io_slider_thread_proc,
callback,
0,
NULL);
}
void diva_io_slider_stop(void)
{
diva_io_slider_stop_flag = true;
WaitForSingleObject(diva_io_slider_thread, INFINITE);
CloseHandle(diva_io_slider_thread);
diva_io_slider_thread = NULL;
diva_io_slider_stop_flag = false;
}
void diva_io_slider_set_leds(const uint8_t *rgb)
{}
static unsigned int __stdcall diva_io_slider_thread_proc(void *ctx)
{
static const int keys[] = {
'Q', 'W', 'E', 'R', 'U', 'I', 'O', 'P',
};
diva_io_slider_callback_t callback;
uint8_t pressure_val;
uint8_t pressure[32];
size_t i;
callback = ctx;
while (!diva_io_slider_stop_flag) {
for (i = 0 ; i < 8 ; i++) {
if(GetAsyncKeyState(keys[i]) & 0x8000) {
pressure_val = 20;
} else {
pressure_val = 0;
}
memset(&pressure[28 - 4 * i], pressure_val, 4);
}
callback(pressure);
Sleep(1);
}
return 0;
}

10
divaio/divaio.def Normal file
View File

@ -0,0 +1,10 @@
LIBRARY divaio
EXPORTS
diva_io_init
diva_io_jvs_poll
diva_io_jvs_read_coin_counter
diva_io_jvs_set_coin_blocker
diva_io_slider_set_leds
diva_io_slider_start
diva_io_slider_stop

81
divaio/divaio.h Normal file
View File

@ -0,0 +1,81 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
/* Initialize the Project Diva IO provider DLL. This is the first function to be
called on this DLL. Returning failure from this function will cause the main
application to immediately exit.
All subsequent calls may originate from arbitrary threads and some may
overlap with each other. Ensuring synchronization inside your IO DLL is
your responsibility. */
HRESULT diva_io_init(void);
/* Poll JVS input.
opbtn returns the cabinet test/service state, where bit 0 is Test and Bit 1
is Service.
gamebtn bits, from least significant to most significant, are:
Circle Cross Square Triangle Start UNUSED UNUSED UNUSED */
void diva_io_jvs_poll(uint8_t *opbtn, uint8_t *gamebtn);
/* Read the current state of the coin counter. This value should be incremented
for every coin detected by the coin acceptor mechanism. This count does not
need to persist beyond the lifetime of the process. */
uint16_t diva_io_jvs_read_coin_counter(void);
/* Set the state of the coin blocker. Parameter is true if the blocker is
disengaged (i.e. coins can be inserted) and false if the blocker is engaged
(i.e. the coin slot should be physically blocked). */
void diva_io_jvs_set_coin_blocker(bool open);
/* Project Diva touch sliders consist of 32 pressure sensitive cells, where
cell 1 (array index 0) is the rightmost cell and cell 32 (array index 31) is
the leftmost cell. */
/* Callback function supplied to your IO DLL. This must be called with a
pointer to a 32-byte array of pressure values, one byte per slider cell.
Cells reporting a pressure value of at least 20 are considered to be pressed.
This threshold is not configurable.
The callback will copy the pressure state data out of your buffer before
returning. The pointer will not be retained. */
typedef void (*diva_io_slider_callback_t)(const uint8_t *state);
/* Start polling the slider. Your DLL must start a polling thread and call the
supplied function periodically from that thread with new input state. The
update interval is up to you, but if your input device doesn't have any
preferred interval then 1 kHz is a reasonable maximum frequency.
Note that you do have to have to call the callback "occasionally" even if
nothing is changing, otherwise the game will raise a comm timeout error. */
void diva_io_slider_start(diva_io_slider_callback_t callback);
/* Stop polling the slider. You must cease to invoke the input callback before
returning from this function.
This *will* be called in the course of regular operation. For example,
every time you go into the operator menu the slider and all of the other I/O
on the cabinet gets restarted.
Following on from the above, the slider polling loop *will* be restarted
after being stopped in the course of regular operation. Do not permanently
tear down your input driver in response to this function call. */
void diva_io_slider_stop(void);
/* Update the RGB lighting on the slider. A pointer to an array of 32 * 3 = 96
bytes is supplied. Layout is probably strictly linear but still TBD. */
void diva_io_slider_set_leds(const uint8_t *rgb);

12
divaio/meson.build Normal file
View File

@ -0,0 +1,12 @@
divaio_dll = shared_library(
'divaio',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'divaio.def',
c_pch : '../precompiled.h',
sources : [
'divaio.c',
'divaio.h',
],
)

View File

@ -38,6 +38,7 @@ subdir('util')
subdir('aimeio')
subdir('chuniio')
subdir('divaio')
subdir('cardhook')
subdir('chunihook')