Physical Button Board Code!
This commit is contained in:
3
.vscode/launch.json
vendored
3
.vscode/launch.json
vendored
@@ -23,7 +23,8 @@
|
||||
{ "name": "__GL_LOG_MAX_ANISO", "value": "0" },
|
||||
{ "name": "__GL_FSAA_MODE", "value": "0" },
|
||||
{ "name": "LD_PRELOAD", "value": "/g3/libvidsync.so" },
|
||||
{ "name": "DISPLAY", "value": ":0" } // adjust if needed
|
||||
{ "name": "DISPLAY", "value": ":0" }, // adjust if needed
|
||||
{ "name": "GAMEBOARD_SERIAL", "value": "/dev/ttyACM0"}
|
||||
],
|
||||
|
||||
/* Run gdb over SSH on the run VM */
|
||||
|
||||
@@ -186,14 +186,15 @@ int InpLoop(void)
|
||||
//
|
||||
// process jamma board
|
||||
//
|
||||
|
||||
if((i=JammaLoop())<0) // every-loop processing for Jamma
|
||||
{
|
||||
if (i != -2) // for some reason, this error will happen
|
||||
SysLog("JammaLoop() failed %d, resetting jamma\n",i);
|
||||
if((i=JammaOp(JAMMAOP_RESET))<0)
|
||||
SysLog("JammaOp(JAMMAOP_RESET) failed %d\n",i);
|
||||
}
|
||||
// DISABLE THE JAMMA BLAH BLAH. WE WILL WORRY ABOUT IT LATER.
|
||||
// TODO: Add jamma support back at some point. But don't forget!
|
||||
// if((i=JammaLoop())<0) // every-loop processing for Jamma
|
||||
// {
|
||||
// if (i != -2) // for some reason, this error will happen
|
||||
// SysLog("JammaLoop() failed %d, resetting jamma\n",i);
|
||||
// if((i=JammaOp(JAMMAOP_RESET))<0)
|
||||
// SysLog("JammaOp(JAMMAOP_RESET) failed %d\n",i);
|
||||
// }
|
||||
#if PRODUCTION
|
||||
if(i>0)
|
||||
SysG.proflags|=M_SYSG_PROFLAGS_JAMMALIVES2;
|
||||
|
||||
@@ -433,7 +433,7 @@ BAIL:
|
||||
while(((i=glGetError())!=GL_NO_ERROR)&&(x++<100))
|
||||
{
|
||||
s=(void*)gluErrorString(i);
|
||||
BP(BP_IMGLOAD5); // tell Joe there is an OpenGL error in imgLoad
|
||||
//BP(BP_IMGLOAD5); // tell Joe there is an OpenGL error in imgLoad
|
||||
} // while
|
||||
|
||||
}
|
||||
|
||||
@@ -4248,7 +4248,7 @@ void dond_randomizeCases(int ignoreCaseNum)
|
||||
for (i = 0; i < NUM_CASES; i++)
|
||||
{
|
||||
possibleTotal += caseState[i];
|
||||
// printf("case %02i : %i\n", i, caseState[i]);
|
||||
//printf("case %02i : %i\n", i, caseState[i]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6455,6 +6455,7 @@ void dond_onLptSwitchHit(int snum)
|
||||
dond_lptUpdateIO(1);
|
||||
}
|
||||
|
||||
// So, does this mean hitting double deal will always kill the attract loop at any time?
|
||||
if ((snum == 18) && (dondGameState == DOND_ATTRACT_LOOP))
|
||||
{
|
||||
dond_quickExitAttract();
|
||||
@@ -6462,6 +6463,16 @@ void dond_onLptSwitchHit(int snum)
|
||||
dond_lptUpdateIO(1);
|
||||
}
|
||||
|
||||
// J added this, my custom controller will send code 20 to start
|
||||
if ((snum == 20) && (dondGameState == DOND_ATTRACT_LOOP || dondGameState == DOND_TITLE_PAGE)){
|
||||
// Kill the attract loop if it's running
|
||||
if (dondGameState == DOND_ATTRACT_LOOP) {
|
||||
dond_quickExitAttract();
|
||||
}
|
||||
dond_onStartButton();
|
||||
dond_lptUpdateIO(1);
|
||||
}
|
||||
|
||||
// - check for a SECRET CODE!
|
||||
|
||||
/*
|
||||
|
||||
@@ -959,7 +959,7 @@ void *dond_lptWriteThreadCont(void *arg)
|
||||
|
||||
pthread_exit(NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -1,43 +1,161 @@
|
||||
// Dummy LPT driver.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h> /* usleep for the animation threads */
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/* ---------------------------- state ----------------------------- */
|
||||
static uint8_t lptLampBank[3] = { 0xFF, 0xFF, 0xFF }; /* active-low */
|
||||
static uint8_t lptSwitchBank[3] = { 0xFF, 0xFF, 0xFF }; /* always “off”*/
|
||||
static uint8_t dond_lptSwitchCount[24] = { 0 };
|
||||
static int dond_lptUpdating = 0;
|
||||
/* --------------- threads ----------------*/
|
||||
static pthread_t poll_thread;
|
||||
static pthread_mutex_t switch_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/* ---------------- helpers --------------------------------------- */
|
||||
/* ---------------- state ---------------- */
|
||||
static uint8_t lptLampBank[3] = { 0xFF, 0xFF, 0xFF };
|
||||
static uint8_t dond_lptSwitchCount[24] = { 0 };
|
||||
static int dond_lptUpdating = 0;
|
||||
|
||||
/* 22 button states */
|
||||
static int dond_switchState[24] = { 0 };
|
||||
static int dond_switchState_prev1[24] = { 0 };
|
||||
static int dond_switchState_prev2[24] = { 0 };
|
||||
static int bufferedLptInput[24] = { 0 };
|
||||
|
||||
/* Serial */
|
||||
static int serial_fd = -1;
|
||||
|
||||
/* ---------------- helpers ---------------- */
|
||||
static void lpt_dump_banks(void)
|
||||
{
|
||||
/* Emits one line; great for “tail -f” or log capture. */
|
||||
printf("[LPT] bank0=%02X bank1=%02X bank2=%02X\n",
|
||||
lptLampBank[0], lptLampBank[1], lptLampBank[2]);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/* ---------------- public API – unchanged signatures ------------- */
|
||||
static void try_open_serial(void)
|
||||
{
|
||||
if (serial_fd > 0) return; // Should only be called if there's no serial.
|
||||
const char* dev = getenv("GAMEBOARD_SERIAL");
|
||||
if (!dev) dev = "/dev/ttyUSB0";
|
||||
|
||||
serial_fd = open(dev, O_RDWR | O_NOCTTY | O_SYNC);
|
||||
if (serial_fd < 0) {
|
||||
printf("[BUTTONBOARD] Warning: could not open serial port %s: %s\n", dev, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
struct termios tty;
|
||||
memset(&tty, 0, sizeof tty);
|
||||
if (tcgetattr(serial_fd, &tty) != 0) {
|
||||
printf("[BUTTONBOARD] tcgetattr error: %s\n", strerror(errno));
|
||||
close(serial_fd);
|
||||
serial_fd = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
cfsetospeed(&tty, B115200);
|
||||
cfsetispeed(&tty, B115200);
|
||||
|
||||
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
|
||||
tty.c_iflag = 0;
|
||||
tty.c_oflag = 0;
|
||||
tty.c_lflag = 0; // no echo, no canonical
|
||||
tty.c_cc[VMIN] = 3; // block until 3 bytes
|
||||
tty.c_cc[VTIME] = 1; // 100ms timeout
|
||||
|
||||
tty.c_cflag |= CREAD | CLOCAL;
|
||||
tty.c_cflag &= ~(PARENB | CSTOPB | CRTSCTS);
|
||||
|
||||
if (tcsetattr(serial_fd, TCSANOW, &tty) != 0) {
|
||||
printf("[BUTTONBOARD] tcsetattr error: %s\n", strerror(errno));
|
||||
close(serial_fd);
|
||||
serial_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void* serial_poll_loop(void* arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
uint8_t last_valid[3] = {0};
|
||||
|
||||
while (1) {
|
||||
try_open_serial();
|
||||
|
||||
if (serial_fd < 0) {
|
||||
usleep(500 * 1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t cmd = 0x01;
|
||||
ssize_t w = write(serial_fd, &cmd, 1);
|
||||
if (w != 1) {
|
||||
printf("[BUTTONBOARD] Failed to write poll byte. Reopening.\n");
|
||||
close(serial_fd);
|
||||
serial_fd = -1;
|
||||
usleep(500 * 1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
fd_set readfds;
|
||||
struct timeval timeout = {0, 100 * 1000}; // 100ms
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(serial_fd, &readfds);
|
||||
|
||||
int sel = select(serial_fd + 1, &readfds, NULL, NULL, &timeout);
|
||||
if (sel <= 0) {
|
||||
// Timeout or error
|
||||
if (sel < 0) perror("[BUTTONBOARD] select()");
|
||||
usleep(50 * 1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t buf[3] = {0};
|
||||
ssize_t r = read(serial_fd, buf, 3);
|
||||
if (r != 3) {
|
||||
printf("[BUTTONBOARD] Partial read (%zd). Dropping frame.\n", r);
|
||||
usleep(50 * 1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(last_valid, buf, 3);
|
||||
|
||||
pthread_mutex_lock(&switch_mutex);
|
||||
int i;
|
||||
for (i = 0; i < 22; i++) {
|
||||
int byte = i / 8;
|
||||
int bit = i % 8;
|
||||
dond_switchState[i] = (buf[byte] >> bit) & 1;
|
||||
}
|
||||
pthread_mutex_unlock(&switch_mutex);
|
||||
|
||||
usleep(20 * 1000); // 50 Hz
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ---------------- core I/O logic ---------------- */
|
||||
void dond_lptInit(void)
|
||||
{
|
||||
/* Nothing to initialise on a PC; just report the reset state. */
|
||||
static int started = 0;
|
||||
if (!started) {
|
||||
pthread_create(&poll_thread, NULL, serial_poll_loop, NULL);
|
||||
started = 1;
|
||||
}
|
||||
lpt_dump_banks();
|
||||
}
|
||||
|
||||
int dond_lptUpdateIO(int writeFlag)
|
||||
{
|
||||
/* writeFlag==0 → “read” phase. We have no inputs – always idle.*/
|
||||
if (!writeFlag) return 0;
|
||||
|
||||
if (dond_lptUpdating) return 0;
|
||||
dond_lptUpdating = 1;
|
||||
|
||||
lpt_dump_banks(); /* single place where we talk to host */
|
||||
lpt_dump_banks();
|
||||
dond_lptUpdating = 0;
|
||||
return 0;
|
||||
}
|
||||
@@ -47,17 +165,14 @@ void dond_lptSetLamp(int lnum, int state)
|
||||
int bank = lnum / 8;
|
||||
int bit = lnum % 8;
|
||||
|
||||
/* Original hardware is active-low: 0=ON, 1=OFF */
|
||||
if (state) lptLampBank[bank] &= ~(1U << bit); /* turn ON */
|
||||
else lptLampBank[bank] |= (1U << bit); /* turn OFF */
|
||||
if (state) lptLampBank[bank] &= ~(1U << bit);
|
||||
else lptLampBank[bank] |= (1U << bit);
|
||||
|
||||
/* Immediately push the change – behaviour identical to original */
|
||||
(void)dond_lptUpdateIO(1);
|
||||
}
|
||||
|
||||
void dond_lptSetLamp_diag(int lnum, int state)
|
||||
{
|
||||
/* Same as normal SetLamp but *does not* flush immediately. */
|
||||
int bank = lnum / 8;
|
||||
int bit = lnum % 8;
|
||||
|
||||
@@ -67,32 +182,55 @@ void dond_lptSetLamp_diag(int lnum, int state)
|
||||
|
||||
int dond_lptGetSwitch(int snum)
|
||||
{
|
||||
/* Always “not pressed” (active-low -> returns 1). */
|
||||
return 1;
|
||||
return dond_switchState[snum] ? 0 : 1; // Active-low behavior
|
||||
}
|
||||
|
||||
int dond_lptSwitchCheck(int writeHits)
|
||||
{
|
||||
/* Nothing changes, therefore no hits. */
|
||||
(void)writeHits;
|
||||
int i;
|
||||
int local_switchState[24];
|
||||
writeHits = 1; // DBG override
|
||||
|
||||
pthread_mutex_lock(&switch_mutex);
|
||||
memcpy(local_switchState, dond_switchState, sizeof(dond_switchState));
|
||||
pthread_mutex_unlock(&switch_mutex);
|
||||
|
||||
for (i = 0; i < 22; i++) {
|
||||
if (local_switchState[i] && !dond_switchState_prev1[i] && !dond_switchState_prev2[i]) {
|
||||
bufferedLptInput[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (writeHits) {
|
||||
for (i = 0; i < 22; i++) {
|
||||
if (bufferedLptInput[i]) {
|
||||
dond_lptSwitchCount[i]++;
|
||||
dond_onLptSwitchHit(i);
|
||||
bufferedLptInput[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 22; i++) dond_switchState_prev2[i] = dond_switchState_prev1[i];
|
||||
for (i = 0; i < 22; i++) dond_switchState_prev1[i] = local_switchState[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dond_lptGetSwitchCount(int snum) { (void)snum; return 0; }
|
||||
int dond_lptCountSwitches(void) { return 0; }
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
* The remaining functions drive animations (fade-ins, attract mode,
|
||||
* etc.). They still run so that higher-level game logic keeps the
|
||||
* same timing, but they merely call the stubbed SetLamp(). *
|
||||
* ----------------------------------------------------------------- */
|
||||
/* Remaining stub functions unchanged */
|
||||
int dond_lptGetSwitchCount(int snum){ return dond_lptSwitchCount[snum]; }
|
||||
int dond_lptCountSwitches(void) {
|
||||
int count = 0;
|
||||
int i;
|
||||
for (i = 0; i < 22; i++) {
|
||||
if (dond_switchState[i]) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Animation and slow-path thread stubs left unchanged */
|
||||
static void * nop_thread(void *arg) { (void)arg; return NULL; }
|
||||
|
||||
/* Each animation collapses to a minimal stub. If you *want* output
|
||||
* spam that shows every intermediate lamp frame, simply keep the
|
||||
* original bodies – the SetLamp() calls already print each change. */
|
||||
|
||||
#define MAKE_THREAD(fn) \
|
||||
void *fn(void *arg) { return nop_thread(arg); }
|
||||
|
||||
@@ -103,16 +241,14 @@ MAKE_THREAD(dond_lptThreadFadeRedOut)
|
||||
MAKE_THREAD(dond_lptAttractAnim)
|
||||
MAKE_THREAD(dond_lptTestThread)
|
||||
MAKE_THREAD(dond_lptTestThread2)
|
||||
MAKE_THREAD(dond_lptWriteThread) /* instant – no I/O delays */
|
||||
MAKE_THREAD(dond_lptWriteThreadCont) /* while(1) loop removed */
|
||||
MAKE_THREAD(dond_lptWriteThread)
|
||||
MAKE_THREAD(dond_lptWriteThreadCont)
|
||||
|
||||
/* Spawn helpers become no-ops because the threads above do nothing. */
|
||||
void dond_lptSpawnTestThread(void) {}
|
||||
void dond_lptSpawnWriteThread(void) {}
|
||||
void dond_lptSpawnWriteThreadCont(void) {}
|
||||
void dond_lptSpawnThread(int t) { (void)t; }
|
||||
void dond_lptSpawnThread(int t) { (void)t; }
|
||||
|
||||
/* Slow path helpers (reads/writes used only by diagnostics) stubbed */
|
||||
int dond_lptReadSlow(void) { return 0; }
|
||||
void dond_lptWriteSlow(void) {}
|
||||
void dond_lptWriteCaseBanks(void) {}
|
||||
|
||||
@@ -634,7 +634,6 @@ int GameStart(void)
|
||||
goto BAIL;
|
||||
|
||||
// - intialize the dongle checking system
|
||||
printf("Harware dongle disabled!");
|
||||
//BankerOfferInitialize();
|
||||
|
||||
//
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#define GAME_NUMBER 6 // game number
|
||||
|
||||
#ifndef NEW_JERSEY
|
||||
#define GAME_FULL_NAME "Deal or No Deal D&B" // used to print name onscreen
|
||||
#define GAME_FULL_NAME "Deal or No Deal" // used to print name onscreen
|
||||
#endif
|
||||
|
||||
#ifdef NEW_JERSEY
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
01.07.03 D&B
|
||||
EXE Version 01.07.03 D&B
|
||||
01.07.03
|
||||
EXE Version 01.07.03
|
||||
Release Date: Tuesday, February 3rd, 2009
|
||||
9
|
||||
|
||||
Reference in New Issue
Block a user