From 9611a34dfc05c7e28db7d5a30737072d2ceb8fab Mon Sep 17 00:00:00 2001 From: Bobby Dilley Date: Thu, 7 Jul 2022 13:34:29 +0100 Subject: [PATCH] Add initial files --- .gitignore | 1 + Makefile | 18 + README.md | 44 +++ src/lindbergh/baseboard.c | 242 +++++++++++++ src/lindbergh/baseboard.h | 9 + src/lindbergh/config.c | 17 + src/lindbergh/config.h | 16 + src/lindbergh/driveboard.c | 0 src/lindbergh/driveboard.h | 0 src/lindbergh/eeprom.c | 111 ++++++ src/lindbergh/eeprom.h | 2 + src/lindbergh/graphics.c | 14 + src/lindbergh/graphics.h | 0 src/lindbergh/hook.c | 200 +++++++++++ src/lindbergh/hook.h | 0 src/lindbergh/jvs.c | 657 ++++++++++++++++++++++++++++++++++++ src/lindbergh/jvs.h | 247 ++++++++++++++ src/lindbergh/motionboard.c | 0 src/lindbergh/motionboard.h | 0 src/lindbergh/rideboard.c | 14 + src/lindbergh/rideboard.h | 4 + 21 files changed, 1596 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 src/lindbergh/baseboard.c create mode 100644 src/lindbergh/baseboard.h create mode 100644 src/lindbergh/config.c create mode 100644 src/lindbergh/config.h create mode 100644 src/lindbergh/driveboard.c create mode 100644 src/lindbergh/driveboard.h create mode 100644 src/lindbergh/eeprom.c create mode 100644 src/lindbergh/eeprom.h create mode 100644 src/lindbergh/graphics.c create mode 100644 src/lindbergh/graphics.h create mode 100644 src/lindbergh/hook.c create mode 100644 src/lindbergh/hook.h create mode 100644 src/lindbergh/jvs.c create mode 100644 src/lindbergh/jvs.h create mode 100644 src/lindbergh/motionboard.c create mode 100644 src/lindbergh/motionboard.h create mode 100644 src/lindbergh/rideboard.c create mode 100644 src/lindbergh/rideboard.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0e294ad --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +CC=gcc -m32 +CFLAGS = -g -O0 -fPIC -m32 -D_GNU_SOURCE -Wall -Werror -Wno-unused-variable -Wno-unused-function +LD = g++ -m32 +LDFLAGS = -Wl,-z,defs -rdynamic -static-libstdc++ -static-libgcc -lc -ldl -lGL -lglut -lX11 -lm -lpthread -shared -nostdlib + +BUILD = build + +OBJS := $(patsubst %.c,%.o,$(wildcard src/lindbergh/*.c)) + +all: lindbergh.so + +lindbergh.so: $(OBJS) + mkdir -p $(BUILD) + $(LD) $(OBJS) $(LDFLAGS) $(CFLAGS) -o $(BUILD)/lindbergh.so + rm -f src/lindbergh/*.o + +clean: + rm -rf $(BUILD) diff --git a/README.md b/README.md new file mode 100644 index 0000000..d7a321b --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# SEGA Lindbergh Loader + +## Supported Titles + +The loader can currently run the following titles: + +- The House Of The Dead 4 +- The House Of The Dead 4 Special +- SEGA Race TV +- The House Of The Dead EX +- Outrun 2 SP SDX +- 2Spicy +- Rambo + +## Building & Running + +To build simply run the makefile, and then copy the contents of the build directory into your game directory and run. + +``` +make +cp build/* ~/the-house-of-the-dead-4/disk0/elf/. +cd ~/the-house-of-the-dead-4/disk0/elf +LD_PRELOAD=$(pwd)/lindbergh.so LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. ./hod4M.elf +``` + +## Components + +This section lists the components of the emulator and what they do + +### Rideboard + +This is a high level emulation of the ride control board used in The House Of The Dead 4 Special and Let's Go Jungle Special. + +### Driveboard + +This is a high level emulation of various force feedback drive boards used in Lindbergh games + +### Motion Board + +This is a high level emulation of the motion control board used in Outrun 2 SP SDX + +### libsegaapi.so + +This is an emulation of the driver that games use to route sound out of the Creative Labs soundcard. This routes sound using OpenAL. diff --git a/src/lindbergh/baseboard.c b/src/lindbergh/baseboard.c new file mode 100644 index 0000000..81cf5a1 --- /dev/null +++ b/src/lindbergh/baseboard.c @@ -0,0 +1,242 @@ +#include +#include +#include +#include +#include + +#include "baseboard.h" + +#include "config.h" +#include "jvs.h" + +#define SERIAL_STRING "FE11-X018012022X" + +#define BASEBOARD_INIT 0x300 +#define BASEBOARD_GET_VERSION 0x8004BC02 +#define BASEBOARD_SEEK_SHM 0x400 +#define BASEBOARD_READ_SRAM 0x601 +#define BASEBOARD_WRITE_SRAM 0x600 +#define BASEBOARD_REQUEST 0xC020BC06 +#define BASEBOARD_RECEIVE 0xC020BC07 +#define BASEBOARD_GET_SERIAL 0x120 +#define BASEBOARD_WRITE_FLASH 0x180 +#define BASEBOARD_GET_SENSE_LINE 0x210 +#define BASEBOARD_PROCESS_JVS 0x220 +#define BASEBOARD_READY 0x201 + +typedef struct +{ + uint32_t srcAddress; + uint32_t srcSize; + uint32_t destAddress; + uint32_t destSize; +} BaseboardCommand; + +BaseboardCommand jvsCommand = {0}; +BaseboardCommand serialCommand = {0}; + +typedef struct +{ + uint32_t *data; + uint32_t offset; + uint32_t size; +} readData_t; + +typedef struct +{ + uint32_t offset; + uint32_t *data; + uint32_t size; +} writeData_t; + +FILE *sram = NULL; + +unsigned int sharedMemoryIndex = 0; +uint8_t sharedMemory[1024 * 32] = {0}; + +int selectReply = -1; + +int initBaseboard() +{ + char *sramPath = getConfig()->sramPath; + + sram = fopen(sramPath, "a"); + + // Create file if it doesn't exist + if (sram == NULL) + { + printf("Error: Cannot open %s\n", sramPath); + return 1; + } + + fclose(sram); + + sram = fopen(sramPath, "rb+"); + + fseek(sram, 0, SEEK_SET); + + return 0; +} + +ssize_t baseboardRead(int fd, void *buf, size_t count) +{ + memcpy(buf, &sharedMemory[sharedMemoryIndex], count); + return count; +} + +ssize_t baseboardWrite(int fd, const void *buf, size_t count) +{ + memcpy(&sharedMemory[sharedMemoryIndex], buf, count); + return count; +} + +int baseboardSelect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict timeout) +{ + return selectReply; +} + +int baseboardIoctl(int fd, unsigned int request, void *data) +{ + switch (request) + { + + case BASEBOARD_GET_VERSION: + { + uint8_t versionData[4] = {0x00, 0x19, 0x20, 0x07}; + memcpy(data, versionData, 4); + return 0; + } + break; + + case BASEBOARD_INIT: + { + // selectReply = -1; Considering adding this in + return 0; + } + break; + + case BASEBOARD_READY: // Not sure this is what it should be called + { + selectReply = 0; + return 0; + } + break; + + case BASEBOARD_SEEK_SHM: + { + sharedMemoryIndex = (unsigned int)data; + return 0; + } + break; + + case BASEBOARD_READ_SRAM: + { + readData_t *_data = data; + fseek(sram, _data->offset, SEEK_SET); + fread(_data->data, 1, _data->size, sram); + return 0; + } + break; + + case BASEBOARD_WRITE_SRAM: + { + writeData_t *_data = data; + fseek(sram, _data->offset, SEEK_SET); + fwrite(_data->data, 1, _data->size, sram); + return 0; + } + break; + + case BASEBOARD_REQUEST: + { + uint32_t *_data = data; + + switch (_data[0]) + { + + case BASEBOARD_GET_SERIAL: + { + serialCommand.destAddress = _data[1]; + serialCommand.destSize = _data[2]; + } + break; + + case BASEBOARD_WRITE_FLASH: + { + printf("Warning: The game attempted to write to the baseboard flash\n"); + } + break; + + case BASEBOARD_PROCESS_JVS: + { + jvsCommand.srcAddress = _data[1]; + jvsCommand.srcSize = _data[2]; + jvsCommand.destAddress = _data[3]; + jvsCommand.destSize = _data[4]; + // memcpy(inputBuffer, &shm[jvsCommand.srcAddress], jvsCommand.srcSize); + // processPacket(&io); + } + break; + + case BASEBOARD_GET_SENSE_LINE: + break; + + default: + printf("Error: Unknown baseboard command %X\n", _data[0]); + } + + // Acknowledge the command + _data[0] |= 0xF0000000; + + return 0; + } + break; + + case BASEBOARD_RECEIVE: + { + uint32_t *_data = data; + + switch (_data[0] & 0xFFF) + { + + case BASEBOARD_GET_SERIAL: + { + memcpy(&sharedMemory[serialCommand.destAddress + 96], SERIAL_STRING, strlen(SERIAL_STRING)); + } + break; + + case BASEBOARD_GET_SENSE_LINE: + { + // _data[2] = getSenseLine(); + _data[2] = 3; + } + break; + + case BASEBOARD_PROCESS_JVS: + { + // memcpy(&sharedMemory[jvsCommand.destAddress], outputBuffer, outputPacket.length + 3); + _data[2] = jvsCommand.destAddress; + // dp[3] = outputPacket.length + 3; + } + break; + + default: + printf("Error: Unknown baseboard receive command %X\n", _data[0] & 0xFFF); + } + + // Acknowledge the command + _data[0] |= 0xF0000000; + + // Set the status to success + _data[1] = 1; + + return 0; + } + break; + + default: + printf("Error: Unknown baseboard ioctl %X\n", request); + } + + return 0; +} diff --git a/src/lindbergh/baseboard.h b/src/lindbergh/baseboard.h new file mode 100644 index 0000000..d9d19b4 --- /dev/null +++ b/src/lindbergh/baseboard.h @@ -0,0 +1,9 @@ +#include + +int initBaseboard(); + +ssize_t baseboardRead(int fd, void *buf, size_t count); +ssize_t baseboardWrite(int fd, const void *buf, size_t count); + +int baseboardIoctl(int fd, unsigned int request, void *data); +int baseboardSelect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict timeout); diff --git a/src/lindbergh/config.c b/src/lindbergh/config.c new file mode 100644 index 0000000..000fbf8 --- /dev/null +++ b/src/lindbergh/config.c @@ -0,0 +1,17 @@ +#include + +#include "config.h" + +EmulatorConfig config = {0}; + +int initConfig() { + config.emulateRideboard = 0; + strcpy(config.eepromPath, "eeprom.bin"); + strcpy(config.sramPath, "sram.bin"); + return 0; +} + +EmulatorConfig *getConfig() { + + return &config; +} diff --git a/src/lindbergh/config.h b/src/lindbergh/config.h new file mode 100644 index 0000000..b602d44 --- /dev/null +++ b/src/lindbergh/config.h @@ -0,0 +1,16 @@ +#include + +#define MAX_PATH_LENGTH 1024 + +typedef struct +{ + int emulateRideboard; + char eepromPath[MAX_PATH_LENGTH]; + char sramPath[MAX_PATH_LENGTH]; +} EmulatorConfig; + +int initConfig(); +EmulatorConfig *getConfig(); + + + diff --git a/src/lindbergh/driveboard.c b/src/lindbergh/driveboard.c new file mode 100644 index 0000000..e69de29 diff --git a/src/lindbergh/driveboard.h b/src/lindbergh/driveboard.h new file mode 100644 index 0000000..e69de29 diff --git a/src/lindbergh/eeprom.c b/src/lindbergh/eeprom.c new file mode 100644 index 0000000..2e23786 --- /dev/null +++ b/src/lindbergh/eeprom.c @@ -0,0 +1,111 @@ +#include +#include +#include + +#include "eeprom.h" +#include "config.h" + +#define I2C_SMBUS_BLOCK_MAX 32 +#define I2C_GET_FUNCTIONS 0x705 +#define I2C_SMBUS_TRANSFER 0x720 +#define I2C_SET_SLAVE_MODE 0x703 +#define I2C_BUFFER_CLEAR 0x1261 +#define I2C_READ 1 +#define I2C_SEEK 2 +#define I2C_WRITE 3 + +union i2c_smbus_data { + uint8_t byte; + uint16_t word; + uint8_t block[I2C_SMBUS_BLOCK_MAX + 2]; +}; + +struct i2c_smbus_ioctl_data { + uint8_t read_write; + uint8_t command; + uint32_t size; + union i2c_smbus_data *data; +}; + +FILE *eeprom = NULL; + +int initEeprom() +{ + char *eepromPath = getConfig()->eepromPath; + + eeprom = fopen(eepromPath, "a"); + + // Create file if it doesn't exist + if (eeprom == NULL) + { + printf("Error: Cannot open %s\n", eepromPath); + return 1; + } + + fclose(eeprom); + + eeprom = fopen(eepromPath, "rb+"); + + fseek(eeprom, 0, SEEK_SET); + + return 0; +} + +int eepromIoctl(int fd, unsigned int request, void *data) +{ + switch (request) + { + + case I2C_GET_FUNCTIONS: + { + // The below is copied from what SEGABOOT expects + uint32_t *functions = data; + functions[0] = 0x20000 | 0x100000 | 0x400000 | 0x8000000; + } + break; + + case I2C_SMBUS_TRANSFER: + { + struct i2c_smbus_ioctl_data *_data = data; + + switch (_data->size) + { + + case I2C_READ: + { + fread(&_data->data->byte, 1, sizeof(char), eeprom); + } + break; + + case I2C_SEEK: + { + uint16_t address = (_data->command & 0xFF) << 8 | (_data->data->byte & 0xFF); + fseek(eeprom, address, SEEK_SET); + } + break; + + case I2C_WRITE: + { + uint16_t address = (_data->command & 0xFF) << 8 | (_data->data->byte & 0xFF); + char val = _data->data->word >> 8 & 0xFF; + fseek(eeprom, address, SEEK_SET); + fwrite(&val, 1, sizeof(char), eeprom); + } + break; + + default: + printf("Error: Incorrect I2C transfer size\n"); + } + } + break; + + case I2C_SET_SLAVE_MODE: + case I2C_BUFFER_CLEAR: + break; + + default: + printf("Error: Unkown I2C ioctl %X\n", request); + } + + return 0; +} diff --git a/src/lindbergh/eeprom.h b/src/lindbergh/eeprom.h new file mode 100644 index 0000000..245403c --- /dev/null +++ b/src/lindbergh/eeprom.h @@ -0,0 +1,2 @@ +int initEeprom(); +int eepromIoctl(int fd, unsigned int request, void *data); diff --git a/src/lindbergh/graphics.c b/src/lindbergh/graphics.c new file mode 100644 index 0000000..764bda3 --- /dev/null +++ b/src/lindbergh/graphics.c @@ -0,0 +1,14 @@ +#include +#include + +FGAPI int FGAPIENTRY glutEnterGameMode() +{ + glutCreateWindow("SEGA Lindbergh"); + return 1; +} + +FGAPI void FGAPIENTRY glutLeaveGameMode() +{ + glutDestroyWindow(glutGetWindow()); + return; +} diff --git a/src/lindbergh/graphics.h b/src/lindbergh/graphics.h new file mode 100644 index 0000000..e69de29 diff --git a/src/lindbergh/hook.c b/src/lindbergh/hook.c new file mode 100644 index 0000000..64ac2cd --- /dev/null +++ b/src/lindbergh/hook.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "hook.h" + +#include "baseboard.h" +#include "config.h" +#include "rideboard.h" +#include "eeprom.h" +#include "jvs.h" + +#define HOOK_FILE_NAME "/dev/zero" + +#define BASEBOARD 0 +#define EEPROM 1 +#define SERIAL0 2 +#define SERIAL1 3 + +int hooks[4] = {-1, -1, -1, -1}; + +void __attribute__((constructor)) hook_init() +{ + printf("SEGA Lindbergh Loader\nRobert Dilley 2022\nNot for public consumption\n\n"); + initConfig(); + + if (initEeprom() != 0) + exit(1); + + if (initBaseboard() != 0) + exit(1); + + if (initJVS() != 0) + exit(1); +} + +int open(const char *pathname, int flags) +{ + int (*_open)(const char *pathname, int flags) = dlsym(RTLD_NEXT, "open"); + + if (strcmp(pathname, "/dev/lbb") == 0) + { + hooks[BASEBOARD] = open(HOOK_FILE_NAME, flags); + return hooks[BASEBOARD]; + } + + if (strcmp(pathname, "/dev/i2c/0") == 0) + { + hooks[EEPROM] = open(HOOK_FILE_NAME, flags); + return hooks[EEPROM]; + } + + if (strcmp(pathname, "/dev/ttyS0") == 0 || strcmp(pathname, "/dev/tts/0") == 0) + { + hooks[SERIAL0] = open(HOOK_FILE_NAME, flags); + return hooks[SERIAL0]; + } + + if (strcmp(pathname, "/dev/ttyS1") == 0 || strcmp(pathname, "/dev/tts/1") == 0) + { + hooks[SERIAL1] = open(HOOK_FILE_NAME, flags); + return hooks[SERIAL1]; + } + + return _open(pathname, flags); +} + +int openat(int dirfd, const char *pathname, int flags) +{ + int (*_openat)(int dirfd, const char *pathname, int flags) = dlsym(RTLD_NEXT, "openat"); + + if (strcmp(pathname, "/dev/ttyS0") == 0 || strcmp(pathname, "/dev/ttyS1") == 0 || strcmp(pathname, "/dev/tts/0") == 0 || strcmp(pathname, "/dev/tts/1") == 0) + { + return open(pathname, flags); + } + + return _openat(dirfd, pathname, flags); +} + +int close(int fd) +{ + int (*_close)(int fd) = dlsym(RTLD_NEXT, "close"); + + for (int i = 0; i < (sizeof hooks / sizeof hooks[0]); i++) + { + if (hooks[i] == fd) + { + hooks[i] = -1; + return 0; + } + } + + return _close(fd); +} + +ssize_t read(int fd, void *buf, size_t count) +{ + int (*_read)(int fd, void *buf, size_t count) = dlsym(RTLD_NEXT, "read"); + + if (fd == hooks[BASEBOARD]) + { + return baseboardRead(fd, buf, count); + } + + if (fd == hooks[SERIAL1] && getConfig()->emulateRideboard) + { + return rideboardRead(fd, buf, count); + } + + return _read(fd, buf, count); +} + +ssize_t write(int fd, const void *buf, size_t count) +{ + int (*_write)(int fd, const void *buf, size_t count) = dlsym(RTLD_NEXT, "write"); + + if (fd == hooks[BASEBOARD]) + { + return baseboardWrite(fd, buf, count); + } + + if (fd == hooks[SERIAL1] && getConfig()->emulateRideboard) + { + return rideboardWrite(fd, buf, count); + } + + return _write(fd, buf, count); +} + +int ioctl(int fd, unsigned int request, void *data) +{ + int (*_ioctl)(int fd, int request, void *data) = dlsym(RTLD_NEXT, "ioctl"); + + // Attempt to stop access to the ethernet ports + if ((request == SIOCSIFADDR) || (request == SIOCSIFFLAGS) || (request == SIOCSIFHWADDR) || (request == SIOCSIFHWBROADCAST) || (request == SIOCDELRT) || (request == SIOCADDRT) || (request == SIOCSIFNETMASK)) + { + errno = ENXIO; + return -1; + } + + if (fd == hooks[EEPROM]) + { + return eepromIoctl(fd, request, data); + } + + if (fd == hooks[BASEBOARD]) + { + return baseboardIoctl(fd, request, data); + } + + // Just accept any IOCTL on serial ports and ignore it + if (fd == hooks[SERIAL0] || fd == hooks[SERIAL1]) + { + return 0; + } + + return _ioctl(fd, request, data); +} + +int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict timeout) +{ + int (*_select)(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict timeout) = dlsym(RTLD_NEXT, "select"); + + if (readfds != NULL && FD_ISSET(hooks[BASEBOARD], readfds)) + { + return baseboardSelect(nfds, readfds, writefds, exceptfds, timeout); + } + + if (writefds != NULL && FD_ISSET(hooks[BASEBOARD], writefds)) + { + return baseboardSelect(nfds, readfds, writefds, exceptfds, timeout); + } + + return _select(nfds, readfds, writefds, exceptfds, timeout); +} + + +/** + * Hook for the only function provided by kswapapi.so + * @param p No idea this gets discarded + */ +void kswap_collect(void *p) +{ + return; +} + +/** + * Hook for function used by Primevil + * @param base The number to raise to the exponent + * @param exp The exponent to raise the number to + * @return The result of raising the number to the exponent + */ +float powf(float base, float exponent) +{ + return (float)pow((double)base, (double)exponent); +} diff --git a/src/lindbergh/hook.h b/src/lindbergh/hook.h new file mode 100644 index 0000000..e69de29 diff --git a/src/lindbergh/jvs.c b/src/lindbergh/jvs.c new file mode 100644 index 0000000..9673c8e --- /dev/null +++ b/src/lindbergh/jvs.c @@ -0,0 +1,657 @@ +#include "jvs.h" + +#include +#include +#include + +/* The in and out packets used to read and write to and from*/ +JVSPacket inputPacket, outputPacket; + +/* The in and out buffer used to read and write to and from */ +unsigned char outputBuffer[JVS_MAX_PACKET_SIZE], inputBuffer[JVS_MAX_PACKET_SIZE]; + +/* Holds the status of the sense line */ +int senseLine = 3; + +JVSIO io = {0}; + +/** + * Initialise the JVS emulation + * + * Setup the JVS emulation on a specific device path with an + * IO mapping provided. + * + * @param devicePath The linux filepath for the RS485 adapter + * @param capabilitiesSetup The representation of the IO to emulate + * @returns 1 if the device was initialised successfully, 0 otherwise. + */ +int initJVS() +{ + io.capabilities.switches = 14; + io.capabilities.coins = 2; + io.capabilities.players = 2; + io.capabilities.analogueInBits = 8; + io.capabilities.rightAlignBits = 0; + io.capabilities.analogueInChannels = 20; + io.capabilities.generalPurposeOutputs = 20; + io.capabilities.commandVersion = 19; + io.capabilities.jvsVersion = 48; + io.capabilities.commsVersion = 16; + strcpy(io.capabilities.name, "SEGA CORPORATION;I/O BD JVS;837-14572;Ver1.00;2005/10"); + + if (!io.capabilities.rightAlignBits) + { + io.analogueRestBits = 16 - io.capabilities.analogueInBits; + io.gunXRestBits = 16 - io.capabilities.gunXBits; + io.gunYRestBits = 16 - io.capabilities.gunYBits; + } + + for (int player = 0; player < (io.capabilities.players + 1); player++) + io.state.inputSwitch[player] = 0; + + for (int analogueChannels = 0; analogueChannels < io.capabilities.analogueInChannels; analogueChannels++) + io.state.analogueChannel[analogueChannels] = 0; + + for (int rotaryChannels = 0; rotaryChannels < io.capabilities.rotaryChannels; rotaryChannels++) + io.state.rotaryChannel[rotaryChannels] = 0; + + for (int player = 0; player < io.capabilities.coins; player++) + io.state.coinCount[player] = 0; + + io.analogueMax = pow(2, io.capabilities.analogueInBits) - 1; + io.gunXMax = pow(2, io.capabilities.gunXBits) - 1; + io.gunYMax = pow(2, io.capabilities.gunYBits) - 1; + + /* Float the sense line ready for connection */ + senseLine = 3; + + return 0; +} + +/** + * Writes a single feature to an output packet + * + * Writes a single JVS feature, which are specified + * in the JVS spec, to the output packet. + * + * @param outputPacket The packet to write to. + * @param capability The specific capability to write + * @param arg0 The first argument of the capability + * @param arg1 The second argument of the capability + * @param arg2 The final argument of the capability + */ +void writeFeature(JVSPacket *outputPacket, char capability, char arg0, char arg1, char arg2) +{ + outputPacket->data[outputPacket->length] = capability; + outputPacket->data[outputPacket->length + 1] = arg0; + outputPacket->data[outputPacket->length + 2] = arg1; + outputPacket->data[outputPacket->length + 3] = arg2; + outputPacket->length += 4; +} + +/** + * Write the entire set of features to an output packet + * + * Writes the set of features specified in the JVSCapabilities + * struct to the specified output packet. + * + * @param outputPacket The packet to write to. + * @param capabilities The capabilities object to read from + */ +void writeFeatures(JVSPacket *outputPacket, JVSCapabilities *capabilities) +{ + outputPacket->data[outputPacket->length] = REPORT_SUCCESS; + outputPacket->length += 1; + + /* Input Functions */ + + if (capabilities->players) + writeFeature(outputPacket, CAP_PLAYERS, capabilities->players, capabilities->switches, 0x00); + + if (capabilities->coins) + writeFeature(outputPacket, CAP_COINS, capabilities->coins, 0x00, 0x00); + + if (capabilities->analogueInChannels) + writeFeature(outputPacket, CAP_ANALOG_IN, capabilities->analogueInChannels, capabilities->analogueInBits, 0x00); + + if (capabilities->rotaryChannels) + writeFeature(outputPacket, CAP_ROTARY, capabilities->rotaryChannels, 0x00, 0x00); + + if (capabilities->keypad) + writeFeature(outputPacket, CAP_KEYPAD, 0x00, 0x00, 0x00); + + if (capabilities->gunChannels) + writeFeature(outputPacket, CAP_LIGHTGUN, capabilities->gunXBits, capabilities->gunYBits, capabilities->gunChannels); + + if (capabilities->generalPurposeInputs) + writeFeature(outputPacket, CAP_GPI, 0x00, capabilities->generalPurposeInputs, 0x00); + + /* Output Functions */ + + if (capabilities->card) + writeFeature(outputPacket, CAP_CARD, capabilities->card, 0x00, 0x00); + + if (capabilities->hopper) + writeFeature(outputPacket, CAP_HOPPER, capabilities->hopper, 0x00, 0x00); + + if (capabilities->generalPurposeOutputs) + writeFeature(outputPacket, CAP_GPO, capabilities->generalPurposeOutputs, 0x00, 0x00); + + if (capabilities->analogueOutChannels) + writeFeature(outputPacket, CAP_ANALOG_OUT, capabilities->analogueOutChannels, 0x00, 0x00); + + if (capabilities->displayOutColumns) + writeFeature(outputPacket, CAP_DISPLAY, capabilities->displayOutColumns, capabilities->displayOutRows, capabilities->displayOutEncodings); + + /* Other */ + + if (capabilities->backup) + writeFeature(outputPacket, CAP_BACKUP, 0x00, 0x00, 0x00); + + outputPacket->data[outputPacket->length] = CAP_END; + outputPacket->length += 1; +} + +/** + * Processes and responds to an entire JVS packet + * + * Follows the JVS spec and proceses and responds + * to a single entire JVS packet. + * + * @returns The status of the entire operation + */ +JVSStatus processPacket(JVSIO *jvsIO) +{ + readPacket(&inputPacket); + + /* Check if the packet is for us */ + if (inputPacket.destination != BROADCAST && inputPacket.destination != jvsIO->deviceID) + return JVS_STATUS_NOT_FOR_US; + + /* Setup the output packet */ + outputPacket.length = 0; + outputPacket.destination = BUS_MASTER; + + int index = 0; + + /* Set the entire packet success line */ + outputPacket.data[outputPacket.length++] = STATUS_SUCCESS; + + while (index < inputPacket.length - 1) + { + int size = 1; + switch (inputPacket.data[index]) + { + + /* The arcade hardware sends a reset command and we clear our memory */ + case CMD_RESET: + { + size = 2; + io.deviceID = -1; + senseLine = 3; + // printf("CMD_RESET %d\n", senseLine); + } + break; + + /* The arcade hardware assigns an address to our IO */ + case CMD_ASSIGN_ADDR: + { + size = 2; + io.deviceID = inputPacket.data[index + 1]; + outputPacket.data[outputPacket.length++] = REPORT_SUCCESS; + senseLine = 1; + // printf("CMD_ASSIGN_ADDR %d\n", senseLine); + } + break; + + /* Ask for the name of the IO board */ + case CMD_REQUEST_ID: + { + // printf("CMD_REQUEST_ID\n"); + outputPacket.data[outputPacket.length] = REPORT_SUCCESS; + memcpy(&outputPacket.data[outputPacket.length + 1], jvsIO->capabilities.name, strlen(jvsIO->capabilities.name) + 1); + outputPacket.length += strlen(jvsIO->capabilities.name) + 2; + } + break; + + /* Asks for version information */ + case CMD_COMMAND_VERSION: + { + // printf("CMD_COMMAND_VERSION\n"); + outputPacket.data[outputPacket.length] = REPORT_SUCCESS; + outputPacket.data[outputPacket.length + 1] = jvsIO->capabilities.commandVersion; + outputPacket.length += 2; + } + break; + + /* Asks for version information */ + case CMD_JVS_VERSION: + { + ////printf("CMD_JVS_VERSION\n"); + outputPacket.data[outputPacket.length] = REPORT_SUCCESS; + outputPacket.data[outputPacket.length + 1] = jvsIO->capabilities.jvsVersion; + outputPacket.length += 2; + } + break; + + /* Asks for version information */ + case CMD_COMMS_VERSION: + { + ////printf("CMD_COMMS_VERSION\n"); + outputPacket.data[outputPacket.length] = REPORT_SUCCESS; + outputPacket.data[outputPacket.length + 1] = jvsIO->capabilities.commsVersion; + outputPacket.length += 2; + } + break; + + /* Asks what our IO board supports */ + case CMD_CAPABILITIES: + { + // printf("CMD_CAPABILITIES\n"); + writeFeatures(&outputPacket, &jvsIO->capabilities); + } + break; + + /* Asks for the status of our IO boards switches */ + case CMD_READ_SWITCHES: + { + // printf("CMD_READ_SWITCHES\n"); + size = 3; + outputPacket.data[outputPacket.length] = REPORT_SUCCESS; + outputPacket.data[outputPacket.length + 1] = jvsIO->state.inputSwitch[0]; + outputPacket.length += 2; + for (int i = 0; i < inputPacket.data[index + 1]; i++) + { + for (int j = 0; j < inputPacket.data[index + 2]; j++) + { + outputPacket.data[outputPacket.length++] = jvsIO->state.inputSwitch[i + 1] >> (8 - (j * 8)); + } + } + } + break; + + case CMD_READ_COINS: + { + ////printf("CMD_READ_COINS\n"); + size = 2; + int numberCoinSlots = inputPacket.data[index + 1]; + outputPacket.data[outputPacket.length++] = REPORT_SUCCESS; + + for (int i = 0; i < numberCoinSlots; i++) + { + outputPacket.data[outputPacket.length] = (jvsIO->state.coinCount[i] << 8) & 0x1F; + outputPacket.data[outputPacket.length + 1] = jvsIO->state.coinCount[i] & 0xFF; + outputPacket.length += 2; + } + } + break; + + case CMD_READ_ANALOGS: + { + // printf("CMD_READ_ANALOGS\n"); + size = 2; + + outputPacket.data[outputPacket.length++] = REPORT_SUCCESS; + + for (int i = 0; i < inputPacket.data[index + 1]; i++) + { + /* By default left align the data */ + int analogueData = jvsIO->state.analogueChannel[i] << jvsIO->analogueRestBits; + outputPacket.data[outputPacket.length] = analogueData >> 8; + outputPacket.data[outputPacket.length + 1] = analogueData; + outputPacket.length += 2; + } + } + break; + + case CMD_READ_ROTARY: + { + ////printf("CMD_READ_ROTARY\n"); + size = 2; + + outputPacket.data[outputPacket.length++] = REPORT_SUCCESS; + + for (int i = 0; i < inputPacket.data[index + 1]; i++) + { + outputPacket.data[outputPacket.length] = jvsIO->state.rotaryChannel[i] >> 8; + outputPacket.data[outputPacket.length + 1] = jvsIO->state.rotaryChannel[i]; + outputPacket.length += 2; + } + } + break; + + case CMD_READ_KEYPAD: + { + ////printf("CMD_READ_KEYPAD\n"); + outputPacket.data[outputPacket.length] = REPORT_SUCCESS; + outputPacket.data[outputPacket.length + 1] = 0x00; + outputPacket.length += 2; + } + break; + + case CMD_READ_GPI: + { + ////printf("CMD_READ_GPI\n"); + size = 2; + outputPacket.data[outputPacket.length++] = REPORT_SUCCESS; + for (int i = 0; i < inputPacket.data[index + 1]; i++) + { + outputPacket.data[outputPacket.length++] = 0x00; + } + } + break; + + case CMD_REMAINING_PAYOUT: + { + ////printf("CMD_REMAINING_PAYOUT\n"); + size = 2; + outputPacket.data[outputPacket.length] = REPORT_SUCCESS; + outputPacket.data[outputPacket.length + 1] = 0; + outputPacket.data[outputPacket.length + 2] = 0; + outputPacket.data[outputPacket.length + 3] = 0; + outputPacket.data[outputPacket.length + 4] = 0; + outputPacket.length += 5; + } + break; + + case CMD_SET_PAYOUT: + { + ////printf("CMD_SET_PAYOUT\n"); + size = 4; + outputPacket.data[outputPacket.length++] = REPORT_SUCCESS; + } + break; + + case CMD_WRITE_GPO: + { + ////printf("CMD_WRITE_GPO\n"); + size = 2 + inputPacket.data[index + 1]; + outputPacket.data[outputPacket.length] = REPORT_SUCCESS; + outputPacket.length += 1; + } + break; + + case CMD_WRITE_GPO_BYTE: + { + ////printf("CMD_WRITE_GPO_BYTE\n"); + size = 3; + outputPacket.data[outputPacket.length++] = REPORT_SUCCESS; + } + break; + + case CMD_WRITE_GPO_BIT: + { + ////printf("CMD_WRITE_GPO_BIT\n"); + size = 3; + outputPacket.data[outputPacket.length++] = REPORT_SUCCESS; + } + break; + + case CMD_WRITE_ANALOG: + { + ////printf("CMD_WRITE_ANALOG\n"); + size = inputPacket.data[index + 1] * 2 + 2; + outputPacket.data[outputPacket.length++] = REPORT_SUCCESS; + } + break; + + case CMD_SUBTRACT_PAYOUT: + { + ////printf("CMD_SUBTRACT_PAYOUT\n"); + size = 3; + outputPacket.data[outputPacket.length++] = REPORT_SUCCESS; + } + break; + + case CMD_WRITE_COINS: + { + ////printf("CMD_WRITE_COINS\n"); + size = 4; + // - 1 because JVS is 1-indexed, but our array is 0-indexed + int slot_index = inputPacket.data[index + 1] - 1; + int coin_increment = ((int)(inputPacket.data[index + 3]) | ((int)(inputPacket.data[index + 2]) << 8)); + + outputPacket.data[outputPacket.length++] = REPORT_SUCCESS; + + /* Prevent overflow of coins */ + if (coin_increment + jvsIO->state.coinCount[slot_index] > 16383) + coin_increment = 16383 - jvsIO->state.coinCount[slot_index]; + jvsIO->state.coinCount[slot_index] += coin_increment; + } + break; + + case CMD_WRITE_DISPLAY: + { + ////printf("CMD_WRITE_DISPLAY\n"); + size = (inputPacket.data[index + 1] * 2) + 2; + outputPacket.data[outputPacket.length++] = REPORT_SUCCESS; + } + break; + + case CMD_DECREASE_COINS: + { + ////printf("CMD_DECREASE_COINS\n"); + size = 4; + // - 1 because JVS is 1-indexed, but our array is 0-indexed + int slot_index = inputPacket.data[index + 1] - 1; + int coin_decrement = ((int)(inputPacket.data[index + 3]) | ((int)(inputPacket.data[index + 2]) << 8)); + + outputPacket.data[outputPacket.length++] = REPORT_SUCCESS; + + /* Prevent underflow of coins */ + if (coin_decrement > jvsIO->state.coinCount[slot_index]) + coin_decrement = jvsIO->state.coinCount[slot_index]; + jvsIO->state.coinCount[slot_index] -= coin_decrement; + } + break; + + case CMD_CONVEY_ID: + { + ////printf("CMD_CONVEY_ID\n"); + size = 1; + outputPacket.data[outputPacket.length++] = REPORT_SUCCESS; + char idData[100]; + for (int i = 1; i < 100; i++) + { + idData[i] = (char)inputPacket.data[index + i]; + size++; + if (!inputPacket.data[index + i]) + break; + } + printf("CMD_CONVEY_ID = %s\n", idData); + } + break; + + /* The touch screen and light gun input, simply using analogue channels */ + case CMD_READ_LIGHTGUN: + { + ////printf("CMD_READ_LIGHTGUN\n"); + size = 2; + + int analogueXData = jvsIO->state.gunChannel[0] << jvsIO->gunXRestBits; + int analogueYData = jvsIO->state.gunChannel[1] << jvsIO->gunYRestBits; + outputPacket.data[outputPacket.length] = REPORT_SUCCESS; + outputPacket.data[outputPacket.length + 1] = analogueXData >> 8; + outputPacket.data[outputPacket.length + 2] = analogueXData; + outputPacket.data[outputPacket.length + 3] = analogueYData >> 8; + outputPacket.data[outputPacket.length + 4] = analogueYData; + outputPacket.length += 5; + } + break; + + default: + { + printf("Error: JVS command not supported [0x%02hhX]\n", inputPacket.data[index]); + } + } + index += size; + } + + writePacket(&outputPacket); + + return JVS_STATUS_SUCCESS; +} + +/** + * Read a JVS Packet + * + * A single JVS packet is read into the packet pointer + * after it has been received, unescaped and checked + * for any checksum errors. + * + * @param packet The packet to read into + */ +JVSStatus readPacket(JVSPacket *packet) +{ + int escape = 0, phase = 0, index = 0, dataIndex = 0, finished = 0; + unsigned char checksum = 0x00; + + while (!finished) + { + /* If we encounter a SYNC start again */ + if (!escape && (inputBuffer[index] == SYNC)) + { + phase = 0; + dataIndex = 0; + index++; + continue; + } + + /* If we encounter an ESCAPE byte escape the next byte */ + if (!escape && inputBuffer[index] == ESCAPE) + { + escape = 1; + index++; + continue; + } + + /* Escape next byte by adding 1 to it */ + if (escape) + { + inputBuffer[index]++; + escape = 0; + } + + /* Deal with the main bulk of the data */ + switch (phase) + { + case 0: // If we have not yet got the address + packet->destination = inputBuffer[index]; + checksum = packet->destination & 0xFF; + phase++; + break; + case 1: // If we have not yet got the length + packet->length = inputBuffer[index]; + checksum = (checksum + packet->length) & 0xFF; + phase++; + break; + case 2: // If there is still data to read + if (dataIndex == (packet->length - 1)) + { + if (checksum != inputBuffer[index]) + return JVS_STATUS_ERROR_CHECKSUM; + finished = 1; + break; + } + packet->data[dataIndex++] = inputBuffer[index]; + checksum = (checksum + inputBuffer[index]) & 0xFF; + break; + default: + return JVS_STATUS_ERROR; + } + index++; + } + + return JVS_STATUS_SUCCESS; +} + +/** + * Write a JVS Packet + * + * A single JVS Packet is written to the arcade + * system after it has been escaped and had + * a checksum calculated. + * + * @param packet The packet to send + */ +JVSStatus writePacket(JVSPacket *packet) +{ + /* Don't return anything if there isn't anything to write! */ + if (packet->length < 2) + return JVS_STATUS_SUCCESS; + + /* Get pointer to raw data in packet */ + unsigned char *packetPointer = (unsigned char *)packet; + + /* Add SYNC and reset buffer */ + int checksum = 0; + int outputIndex = 1; + outputBuffer[0] = SYNC; + + packet->length++; + + /* Write out entire packet */ + for (int i = 0; i < packet->length + 1; i++) + { + if (packetPointer[i] == SYNC || packetPointer[i] == ESCAPE) + { + outputBuffer[outputIndex++] = ESCAPE; + outputBuffer[outputIndex++] = (packetPointer[i] - 1); + } + else + { + outputBuffer[outputIndex++] = (packetPointer[i]); + } + checksum = (checksum + packetPointer[i]) & 0xFF; + } + + /* Write out escaped checksum */ + if (checksum == SYNC || checksum == ESCAPE) + { + outputBuffer[outputIndex++] = ESCAPE; + outputBuffer[outputIndex++] = (checksum - 1); + } + else + { + outputBuffer[outputIndex++] = checksum; + } + + return JVS_STATUS_SUCCESS; +} + +int getSenseLine() +{ + return senseLine; +} + +int setSwitch(JVSIO *io, JVSPlayer player, JVSInput switchNumber, int value) +{ + if (player > io->capabilities.players) + return 0; + + if (value) + { + io->state.inputSwitch[player] |= switchNumber; + } + else + { + io->state.inputSwitch[player] &= ~switchNumber; + } + + return 1; +} + +int incrementCoin(JVSIO *io, JVSPlayer player, int amount) +{ + if (player == SYSTEM) + return 0; + + io->state.coinCount[player - 1] = io->state.coinCount[player - 1] + amount; + return 1; +} + +int setAnalogue(JVSIO *io, JVSInput channel, int value) +{ + io->state.analogueChannel[channel] = value; + return 1; +} diff --git a/src/lindbergh/jvs.h b/src/lindbergh/jvs.h new file mode 100644 index 0000000..664c1dd --- /dev/null +++ b/src/lindbergh/jvs.h @@ -0,0 +1,247 @@ +#ifndef JVS_H_ +#define JVS_H_ + +#include +#include +#include +#include +#include +#include +#include + + +#define JVS_RETRY_COUNT 3 +#define JVS_MAX_PACKET_SIZE 255 + + +#define DEVICE_ID 0x01 + +#define SYNC 0xE0 +#define ESCAPE 0xD0 +#define BROADCAST 0xFF +#define BUS_MASTER 0x00 +#define DEVICE_ADDR_START 0x01 + +/* Status for the entire packet */ +#define STATUS_SUCCESS 0x01 +#define STATUS_UNSUPPORTED 0x02 // an unsupported command was sent +#define STATUS_CHECKSUM_FAILURE 0x03 // the checksum on the command packet did not match a computed checksum +#define STATUS_OVERFLOW 0x04 // an overflow occurred while processing the command + +/* Reporting for each individual command */ +#define REPORT_SUCCESS 0x01 // all went well +#define REPORT_PARAMETER_ERROR1 0x02 // TODO: work out difference between this one and the next +#define REPORT_PARAMETER_ERROR2 0x03 +#define REPORT_BUSY 0x04 // some attached hardware was busy, causing the request to fail + +/* All of the commands */ +#define CMD_RESET 0xF0 // reset bus +#define CMD_RESET_ARG 0xD9 // fixed argument to reset command +#define CMD_ASSIGN_ADDR 0xF1 // assign address to slave +#define CMD_SET_COMMS_MODE 0xF2 // switch communications mode for devices that support it, for compatibility +#define CMD_REQUEST_ID 0x10 // requests an ID string from a device +#define CMD_COMMAND_VERSION 0x11 // gets command format version as two BCD digits packed in a byte +#define CMD_JVS_VERSION 0x12 // gets JVS version as two BCD digits packed in a byte +#define CMD_COMMS_VERSION 0x13 // gets communications version as two BCD digits packed in a byte +#define CMD_CAPABILITIES 0x14 // gets a special capability structure from the device +#define CMD_CONVEY_ID 0x15 // convey ID of main board to device +#define CMD_READ_SWITCHES 0x20 // read switch inputs +#define CMD_READ_COINS 0x21 // read coin inputs +#define CMD_READ_ANALOGS 0x22 // read analog inputs +#define CMD_READ_ROTARY 0x23 // read rotary encoder inputs +#define CMD_READ_KEYPAD 0x24 // read keypad inputs +#define CMD_READ_LIGHTGUN 0x25 // read light gun inputs +#define CMD_READ_GPI 0x26 // read general-purpose inputs +#define CMD_RETRANSMIT 0x2F // ask device to retransmit data +#define CMD_DECREASE_COINS 0x30 // decrease number of coins +#define CMD_WRITE_GPO 0x32 // write to general-purpose outputs +#define CMD_WRITE_ANALOG 0x33 // write to analog outputs +#define CMD_WRITE_DISPLAY 0x34 // write to an alphanumeric display +#define CMD_WRITE_COINS 0x35 // add to coins +#define CMD_REMAINING_PAYOUT 0x2E // read remaining payout +#define CMD_SET_PAYOUT 0x31 // write remaining payout +#define CMD_SUBTRACT_PAYOUT 0x36 // subtract from remaining payout +#define CMD_WRITE_GPO_BYTE 0x37 // write single gpo byte +#define CMD_WRITE_GPO_BIT 0x38 // write single gpo bit + +/* Manufacturer specific commands */ +#define CMD_MANUFACTURER_START 0x60 // start of manufacturer-specific commands +#define CMD_NAMCO_SPECIFIC 0x70 +#define CMD_MANUFACTURER_END 0x7F // end of manufacturer-specific commands + +/* Capabilities of the IO board */ +#define CAP_END 0x00 // end of structure +#define CAP_PLAYERS 0x01 // player/switch info +#define CAP_COINS 0x02 // coin slot info +#define CAP_ANALOG_IN 0x03 // analog info +#define CAP_ROTARY 0x04 // rotary encoder info +#define CAP_KEYPAD 0x05 // keypad info +#define CAP_LIGHTGUN 0x06 // light gun info +#define CAP_GPI 0x07 // general purpose input info +#define CAP_CARD 0x10 // card system info +#define CAP_HOPPER 0x11 // token hopper info +#define CAP_GPO 0x12 // general purpose output info +#define CAP_ANALOG_OUT 0x13 // analog output info +#define CAP_DISPLAY 0x14 // character display info +#define CAP_BACKUP 0x15 // backup memory + +#define ENCODINGS [ "unknown", "ascii numeric", "ascii alphanumeric", "alphanumeric/katakana", "alphanumeric/SHIFT-JIS" ] + +#define JVS_MAX_STATE_SIZE 100 +#define MAX_JVS_NAME_SIZE 2048 + +typedef enum +{ + BUTTON_TEST = 1 << 7, // System Buttons + BUTTON_TILT_1 = 1 << 6, + BUTTON_TILT_2 = 1 << 5, + BUTTON_TILT_3 = 1 << 4, + BUTTON_TILT_4 = 1 << 3, + BUTTON_TILT_5 = 1 << 2, + BUTTON_TILT_6 = 1 << 1, + BUTTON_TILT_7 = 1 << 0, + BUTTON_START = 1 << 15, // Player Buttons + BUTTON_SERVICE = 1 << 14, + BUTTON_UP = 1 << 13, + BUTTON_DOWN = 1 << 12, + BUTTON_LEFT = 1 << 11, + BUTTON_RIGHT = 1 << 10, + BUTTON_1 = 1 << 9, + BUTTON_2 = 1 << 8, + BUTTON_3 = 1 << 7, + BUTTON_4 = 1 << 6, + BUTTON_5 = 1 << 5, + BUTTON_6 = 1 << 4, + BUTTON_7 = 1 << 3, + BUTTON_8 = 1 << 2, + BUTTON_9 = 1 << 1, + BUTTON_10 = 1 << 0, + ANALOGUE_1 = 0, // Analogue Inputs + ANALOGUE_2 = 1, + ANALOGUE_3 = 2, + ANALOGUE_4 = 3, + ANALOGUE_5 = 4, + ANALOGUE_6 = 5, + ANALOGUE_7 = 6, + ANALOGUE_8 = 7, + ANALOGUE_9 = 8, + ANALOGUE_10 = 9, + ROTARY_1 = 0, // Rotary Inputs + ROTARY_2 = 1, + ROTARY_3 = 2, + ROTARY_4 = 3, + ROTARY_5 = 4, + ROTARY_6 = 5, + ROTARY_7 = 6, + ROTARY_8 = 7, + ROTARY_9 = 8, + ROTARY_10 = 9, + + /* Things that aren't actually doable */ + COIN = 98, + NONE = 99, +} JVSInput; + +typedef enum +{ + SYSTEM = 0, + PLAYER_1 = 1, + PLAYER_2 = 2, + PLAYER_3 = 3, + PLAYER_4 = 4, +} JVSPlayer; + +typedef struct +{ + int coinCount[JVS_MAX_STATE_SIZE]; + int inputSwitch[JVS_MAX_STATE_SIZE]; + int analogueChannel[JVS_MAX_STATE_SIZE]; + int gunChannel[JVS_MAX_STATE_SIZE]; + int rotaryChannel[JVS_MAX_STATE_SIZE]; +} JVSState; + +typedef struct +{ + char name[MAX_JVS_NAME_SIZE]; + unsigned char commandVersion; + unsigned char jvsVersion; + unsigned char commsVersion; + unsigned char players; + unsigned char switches; + unsigned char coins; + unsigned char analogueInChannels; + unsigned char analogueInBits; + unsigned char rotaryChannels; + unsigned char keypad; + unsigned char gunChannels; + unsigned char gunXBits; + unsigned char gunYBits; + unsigned char generalPurposeInputs; + unsigned char card; + unsigned char hopper; + unsigned char generalPurposeOutputs; + unsigned char analogueOutChannels; + unsigned char displayOutRows; + unsigned char displayOutColumns; + unsigned char displayOutEncodings; + unsigned char backup; + unsigned char rightAlignBits; + char displayName[MAX_JVS_NAME_SIZE]; +} JVSCapabilities; + +typedef struct JVSIO +{ + int deviceID; + int analogueRestBits; + int gunXRestBits; + int gunYRestBits; + int analogueMax; + int gunXMax; + int gunYMax; + JVSState state; + JVSCapabilities capabilities; + struct JVSIO *chainedIO; +} JVSIO; + +typedef struct +{ + unsigned char destination; + unsigned char length; + unsigned char data[JVS_MAX_PACKET_SIZE]; +} JVSPacket; + +typedef enum +{ + JVS_STATUS_SUCCESS, + JVS_STATUS_NOT_FOR_US, + JVS_STATUS_ERROR, + JVS_STATUS_ERROR_TIMEOUT, + JVS_STATUS_ERROR_CHECKSUM, + JVS_STATUS_ERROR_WRITE_FAIL, + JVS_STATUS_ERROR_UNSUPPORTED_COMMAND, +} JVSStatus; + +int initJVS(); + +JVSStatus processPacket(JVSIO *jvsIO); + +JVSStatus readPacket(JVSPacket *packet); +JVSStatus writePacket(JVSPacket *packet); + +/* The in and out packets used to read and write to and from*/ +extern JVSPacket inputPacket, outputPacket; + +/* The in and out buffer used to read and write to and from */ +extern unsigned char outputBuffer[JVS_MAX_PACKET_SIZE], inputBuffer[JVS_MAX_PACKET_SIZE]; + +int getSenseLine(); + +JVSCapabilities *getCapabilities(); +JVSState *getState(); + +int initIO(JVSIO *io); +int setSwitch(JVSIO *io, JVSPlayer player, JVSInput switchNumber, int value); +int incrementCoin(JVSIO *io, JVSPlayer player, int amount); +int setAnalogue(JVSIO *io, JVSInput channel, int value); + +#endif // JVS_H_ diff --git a/src/lindbergh/motionboard.c b/src/lindbergh/motionboard.c new file mode 100644 index 0000000..e69de29 diff --git a/src/lindbergh/motionboard.h b/src/lindbergh/motionboard.h new file mode 100644 index 0000000..e69de29 diff --git a/src/lindbergh/rideboard.c b/src/lindbergh/rideboard.c new file mode 100644 index 0000000..79b2485 --- /dev/null +++ b/src/lindbergh/rideboard.c @@ -0,0 +1,14 @@ +#include +#include +#include + +#include "rideboard.h" + +ssize_t rideboardRead(int fd, void *buf, size_t count) { + return 0; +} + +ssize_t rideboardWrite(int fd, const void *buf, size_t count) { + return 0; +} + diff --git a/src/lindbergh/rideboard.h b/src/lindbergh/rideboard.h new file mode 100644 index 0000000..c06cb27 --- /dev/null +++ b/src/lindbergh/rideboard.h @@ -0,0 +1,4 @@ +#include + +ssize_t rideboardRead(int fd, void *buf, size_t count); +ssize_t rideboardWrite(int fd, const void *buf, size_t count);