From 3a895024985f55a086d166e73e3edc2e6b37f27a Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 17:18:22 +0200 Subject: [PATCH] feat(p3io-ddr-tool): CLI tool to test and debug a real DDR P3IO Extensive tool with subcommands to trigger the various P3IO commands and run the P3IO + EXTIO DDR hardware combo with a hardware test menu. --- Module.mk | 1 + src/main/p3io-ddr-tool/Module.mk | 16 + src/main/p3io-ddr-tool/main.c | 774 +++++++++++++++++++++++++++++ src/main/p3io-ddr-tool/mode-test.c | 406 +++++++++++++++ src/main/p3io-ddr-tool/mode-test.h | 11 + 5 files changed, 1208 insertions(+) create mode 100644 src/main/p3io-ddr-tool/Module.mk create mode 100644 src/main/p3io-ddr-tool/main.c create mode 100644 src/main/p3io-ddr-tool/mode-test.c create mode 100644 src/main/p3io-ddr-tool/mode-test.h diff --git a/Module.mk b/Module.mk index 36339ea..31741fa 100644 --- a/Module.mk +++ b/Module.mk @@ -171,6 +171,7 @@ include src/main/mm/Module.mk include src/main/p3io/Module.mk include src/main/p3iodrv/Module.mk include src/main/p3ioemu/Module.mk +include src/main/p3io-ddr-tool/Module.mk include src/main/p4iodrv/Module.mk include src/main/p4ioemu/Module.mk include src/main/popnhook-util/Module.mk diff --git a/src/main/p3io-ddr-tool/Module.mk b/src/main/p3io-ddr-tool/Module.mk new file mode 100644 index 0000000..2c76b42 --- /dev/null +++ b/src/main/p3io-ddr-tool/Module.mk @@ -0,0 +1,16 @@ +exes += p3io-ddr-tool \ + +ldflags_p3io-ddr-tool := \ + -lsetupapi \ + +libs_p3io-ddr-tool := \ + extiodrv \ + extio \ + p3iodrv \ + p3io \ + hook \ + util \ + +src_p3io-ddr-tool := \ + main.c \ + mode-test.c \ diff --git a/src/main/p3io-ddr-tool/main.c b/src/main/p3io-ddr-tool/main.c new file mode 100644 index 0000000..939e1e7 --- /dev/null +++ b/src/main/p3io-ddr-tool/main.c @@ -0,0 +1,774 @@ +#define LOG_MODULE "p3io-ddr-tool" + +#include + +#include +#include +#include +#include + +#include "extiodrv/extio.h" + +#include "p3io/ddr.h" +#include "p3iodrv/ddr.h" +#include "p3iodrv/device.h" + +#include "util/log.h" +#include "util/thread.h" + +#include "mode-test.h" + +enum mode { + MODE_INVALID = 0, + MODE_SCAN = 1, + MODE_VERP3IO = 2, + MODE_INIT = 3, + MODE_VERDDR = 4, + MODE_WATCHDOGON = 5, + MODE_WATCHDOGOFF = 6, + MODE_CABTYPE = 7, + MODE_DIPSW = 8, + MODE_VIDEOFREQ = 9, + MODE_TEST = 10, + MODE_LIGHTOFF = 11, + MODE_POLL = 12, + MODE_SENSORES = 13, + MODE_TOTAL_COUNT = 14, +}; + +typedef bool (*mode_proc)(HANDLE handle); + +static bool _mode_invalid(HANDLE handle); +static bool _mode_scan(HANDLE handle); +static bool _mode_verp3io(HANDLE handle); +static bool _mode_init(HANDLE handle); +static bool _mode_verddr(HANDLE handle); +static bool _mode_watchdogon(HANDLE handle); +static bool _mode_watchdogoff(HANDLE handle); +static bool _mode_cabtype(HANDLE handle); +static bool _mode_dipsw(HANDLE handle); +static bool _mode_videofreq(HANDLE handle); +static bool _mode_test(HANDLE handle); +static bool _mode_lightoff(HANDLE handle); +static bool _mode_poll(HANDLE handle); +static bool _mode_sensores(HANDLE handle); + +static mode_proc _mode_procs[MODE_TOTAL_COUNT] = { + _mode_invalid, + _mode_scan, + _mode_verp3io, + _mode_init, + _mode_verddr, + _mode_watchdogon, + _mode_watchdogoff, + _mode_cabtype, + _mode_dipsw, + _mode_videofreq, + _mode_test, + _mode_lightoff, + _mode_poll, + _mode_sensores, +}; + +static enum log_level _log_level = LOG_LEVEL_FATAL; +static bool _extio_enabled = true; +static const char *_p3io_device_path = ""; +static const char *_extio_com_port = "COM1"; +static enum mode _mode = MODE_INVALID; + +static bool _scan_and_open(HANDLE *handle) +{ + HRESULT hr; + char path[MAX_PATH]; + + log_info("Scanning for p3io..."); + + hr = p3iodrv_device_scan(path); + + if (FAILED(hr)) { + log_warning("Cannot find a connected p3io: %lX", hr); + return false; + } + + log_info("Opening p3io: %s", path); + + hr = p3iodrv_device_open(path, handle); + + if (FAILED(hr)) { + log_warning("Opening p3io failed: %lX", hr); + return false; + } + + return true; +} + +static bool _close(HANDLE handle) +{ + HRESULT hr; + + log_info("Closing p3io..."); + + hr = p3iodrv_device_close(handle); + + if (FAILED(hr)) { + log_warning("Closing p3io failed: %lX", hr); + return false; + } + + return true; +} + +static bool _process_cmd_args(int argc, char **argv) +{ + char *mode; + + if (argc < 2) { + fprintf(stderr, "DDR P3IO and EXTIO CLI testing tool\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Usage: [ARG ...] MODE\n"); + fprintf(stderr, "Arguments:\n"); + fprintf( + stderr, + " -v Enable additional logging, log level 'warning'\n"); + fprintf( + stderr, + " -vv Enable additional logging, log level 'info'\n"); + fprintf( + stderr, + " -vvv Enable additional logging, log level 'misc' (very " + "verbose)\n"); + fprintf( + stderr, + " -noextio Run the tool without an EXTIO. This also impacts " + "various functionality, e.g. sensor polling, light outputs\n"); + fprintf( + stderr, + " -p3io Explicit device path pointing to a P3IO device to " + "open. If not provided, the device will be automatically scanned " + "for.\n"); + fprintf( + stderr, + " -extio Explicitly set the COM port the EXTIO is connected " + "to, e.g. COM4. If not provided COM1 is used.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Modes:\n"); + fprintf( + stderr, + " scan: Scan for a connected P3IO device. Outputs the full device " + "path to stdout if found.\n"); + fprintf( + stderr, + " verp3io: Get and print the P3IO version information to " + "stdout.\n"); + fprintf(stderr, " init: Initialize the P3IO (\"DDR mode\").\n"); + fprintf( + stderr, + " verddr: Get and print the DDR P3IO specific information to " + "stdout.\n"); + fprintf(stderr, " watchdogon: Turn the watchdog on.\n"); + fprintf(stderr, " watchdogoff: Turn the watchdog off.\n"); + fprintf( + stderr, + " cabtype: Get and print the cabinet type information to " + "stdout.\n"); + fprintf( + stderr, " dipsw: Get and print the DIP switch state to stdout.\n"); + fprintf( + stderr, + " videofreq: Get and print the detected video frequency to " + "stdout.\n"); + fprintf( + stderr, + " test: Run interactive test mode, executes a polling read/write " + "loop to drive the IO and displays the current input states.\n"); + fprintf(stderr, " lightoff: Turn all the lights off\n"); + fprintf( + stderr, + " poll: Run a simple polling loop that polls data from the JAMMA " + "endpoint with input state output.\n"); + fprintf( + stderr, + " sensores: Run cycling individual sensores in a simple polling " + "loop with input state ouptut.\n"); + + return false; + } + + for (int i = 0; i < argc; i++) { + if (!strcmp(argv[i], "-v")) { + _log_level = LOG_LEVEL_WARNING; + } else if (!strcmp(argv[i], "-vv")) { + _log_level = LOG_LEVEL_INFO; + } else if (!strcmp(argv[i], "-vvv")) { + _log_level = LOG_LEVEL_MISC; + } else if (!strcmp(argv[i], "-noextio")) { + _extio_enabled = false; + } else if (!strcmp(argv[i], "-p3io")) { + if (i + 1 < argc) { + _p3io_device_path = argv[++i]; + } else { + fprintf(stderr, "Missing value for -p3io argument\n"); + } + } else if (!strcmp(argv[i], "-extio")) { + if (i + 1 < argc) { + _extio_com_port = argv[++i]; + } else { + fprintf(stderr, "Missing value for -extio argument\n"); + } + } + } + + mode = argv[argc - 1]; + + if (!strcmp(mode, "scan")) { + _mode = MODE_SCAN; + } else if (!strcmp(mode, "verp3io")) { + _mode = MODE_VERP3IO; + } else if (!strcmp(mode, "init")) { + _mode = MODE_INIT; + } else if (!strcmp(mode, "verddr")) { + _mode = MODE_VERDDR; + } else if (!strcmp(mode, "watchdogon")) { + _mode = MODE_WATCHDOGON; + } else if (!strcmp(mode, "watchdogoff")) { + _mode = MODE_WATCHDOGOFF; + } else if (!strcmp(mode, "cabtype")) { + _mode = MODE_CABTYPE; + } else if (!strcmp(mode, "dipsw")) { + _mode = MODE_DIPSW; + } else if (!strcmp(mode, "videofreq")) { + _mode = MODE_VIDEOFREQ; + } else if (!strcmp(mode, "test")) { + _mode = MODE_TEST; + } else if (!strcmp(mode, "lightoff")) { + _mode = MODE_LIGHTOFF; + } else if (!strcmp(mode, "poll")) { + _mode = MODE_POLL; + } else if (!strcmp(mode, "sensores")) { + _mode = MODE_SENSORES; + } else { + fprintf(stderr, "Invalid mode '%s'", mode); + return false; + } + + return true; +} + +static void _init_logging() +{ + log_to_writer(log_writer_stderr, NULL); + log_set_level(_log_level); +} + +static bool _mode_invalid(HANDLE handle) +{ + fprintf(stderr, "Invalid mode"); + return false; +} + +static bool _mode_scan(HANDLE handle) +{ + HRESULT hr; + char path[MAX_PATH]; + + log_info("Scanning for p3io..."); + + hr = p3iodrv_device_scan(path); + + if (FAILED(hr)) { + log_warning("Cannot find a connected p3io: %lX", hr); + return false; + } + + log_info("Found p3io: %s", path); + + printf("%s\n", path); + + return true; +} + +static bool _mode_verp3io(HANDLE handle) +{ + HRESULT hr; + char version[P3IODRV_VERSION_MAX_LEN]; + + memset(version, 0, P3IODRV_VERSION_MAX_LEN); + + hr = p3iodrv_device_read_version(handle, version); + + if (FAILED(hr)) { + log_warning("Getting version failed: %lX", hr); + return _close(handle); + } + + log_info("Version (P3IO): %s", version); + + printf("%s\n", version); + + return true; +} + +static bool _mode_init(HANDLE handle) +{ + HRESULT hr; + + hr = p3iodrv_ddr_init(handle); + + if (FAILED(hr)) { + log_warning("Init failed: %lX", hr); + return false; + } + + log_info("Initialized"); + + return true; +} + +static bool _mode_verddr(HANDLE handle) +{ + HRESULT hr; + char str[4]; + uint32_t major; + uint32_t minor; + uint32_t patch; + + hr = p3iodrv_ddr_get_version(handle, str, &major, &minor, &patch); + + if (FAILED(hr)) { + log_warning("Getting DDR version failed: %lX", hr); + return false; + } + + log_info("Version (DDR): %s %d.%d.%d", str, major, minor, patch); + + printf("%s %d.%d.%d\n", str, major, minor, patch); + + return true; +} + +static bool _mode_watchdogon(HANDLE handle) +{ + HRESULT hr; + + hr = p3iodrv_ddr_set_watchdog(handle, true); + + if (FAILED(hr)) { + log_warning("Enabling watchdog failed: %lX", hr); + return false; + } + + log_info("Watchdog on"); + + return true; +} + +static bool _mode_watchdogoff(HANDLE handle) +{ + HRESULT hr; + + hr = p3iodrv_ddr_set_watchdog(handle, false); + + if (FAILED(hr)) { + log_warning("Disabling watchdog failed: %lX", hr); + return false; + } + + log_info("Watchdog off"); + + return true; +} + +static bool _mode_cabtype(HANDLE handle) +{ + HRESULT hr; + enum p3io_cab_type type; + const char *type_str; + + hr = p3iodrv_ddr_get_cab_type(handle, &type); + + if (FAILED(hr)) { + log_warning("Getting cab type failed: %lX", hr); + return false; + } + + log_info("Cabinet type: %d", type); + + switch (type) { + case P3IO_CAB_TYPE_SD: + type_str = "sd"; + break; + + case P3IO_CAB_TYPE_HD: + type_str = "hd"; + break; + + default: + type_str = "unknown"; + break; + } + + printf("%s\n", type_str); + + return true; +} + +static bool _mode_dipsw(HANDLE handle) +{ + HRESULT hr; + uint8_t dip_sw; + + hr = p3iodrv_ddr_get_dipsw(handle, &dip_sw); + + if (FAILED(hr)) { + log_warning("Getting dip switches failed: %lX", hr); + return false; + } + + log_info("Dip switches: 0x%X", dip_sw); + + printf("0x%X\n", dip_sw); + + return true; +} + +static bool _mode_videofreq(HANDLE handle) +{ + HRESULT hr; + enum p3io_video_freq video_freq; + const char *video_freq_str; + + hr = p3iodrv_ddr_get_video_freq(handle, &video_freq); + + if (FAILED(hr)) { + log_warning("Getting video freq failed: %lX", hr); + return false; + } + + log_info("Video freq: 0x%X", video_freq); + + switch (video_freq) { + case P3IO_VIDEO_FREQ_15KHZ: + video_freq_str = "15khz"; + break; + + case P3IO_VIDEO_FREQ_31KHZ: + video_freq_str = "31khz"; + break; + + default: + video_freq_str = "unknown"; + break; + } + + printf("%s\n", video_freq_str); + + return true; +} + +static bool _mode_test(HANDLE handle_p3io) +{ + HRESULT hr; + HANDLE handle_extio; + bool result; + + if (_extio_enabled) { + hr = extiodrv_device_open(_extio_com_port, &handle_extio); + + if (FAILED(hr)) { + log_warning( + "Failed opening EXTIO on com port '%s': %lX", + _extio_com_port, + hr); + return false; + } + } else { + handle_extio = INVALID_HANDLE_VALUE; + } + + result = mode_test_proc(handle_p3io, handle_extio); + + if (_extio_enabled) { + hr = extiodrv_device_close(&handle_extio); + + if (FAILED(hr)) { + log_warning("Failed closing EXTIO: %lX", hr); + result = false; + } + } + + return result; +} + +static bool _mode_lightoff(HANDLE handle_p3io) +{ + HRESULT hr; + HANDLE handle_extio; + + struct p3io_ddr_output output; + struct extiodrv_extio_pad_lights pad_lights[EXTIO_PAD_LIGHT_MAX_PLAYERS]; + bool neons; + + memset(&output, 0, sizeof(output)); + memset(&pad_lights, 0, sizeof(pad_lights)); + neons = false; + + if (_extio_enabled) { + hr = extiodrv_device_open(_extio_com_port, &handle_extio); + + if (FAILED(hr)) { + log_warning( + "Failed opening EXTIO on com port '%s': %lX", + _extio_com_port, + hr); + return false; + } + } else { + handle_extio = INVALID_HANDLE_VALUE; + } + + hr = p3iodrv_ddr_set_outputs(handle_p3io, &output); + + if (FAILED(hr)) { + log_warning("Setting outputs failed: %lX", hr); + return false; + } + + if (_extio_enabled) { + hr = extiodrv_extio_transfer( + handle_extio, + EXTIODRV_EXTIO_SENSOR_READ_MODE_ALL, + pad_lights, + neons); + + if (FAILED(hr)) { + log_warning("extio transfer failed: %lX", hr); + return false; + } + } + + if (_extio_enabled) { + hr = extiodrv_device_close(&handle_extio); + + if (FAILED(hr)) { + log_warning("Failed closing EXTIO: %lX", hr); + return false; + } + } + + return true; +} + +static bool _mode_poll(HANDLE handle_p3io) +{ + HRESULT hr; + HANDLE handle_extio; + + struct p3io_ddr_jamma jamma; + struct p3io_ddr_output output; + struct extiodrv_extio_pad_lights pad_lights[EXTIO_PAD_LIGHT_MAX_PLAYERS]; + bool neons; + + uint32_t cnt; + + memset(&output, 0, sizeof(output)); + memset(&pad_lights, 0, sizeof(pad_lights)); + neons = false; + + if (_extio_enabled) { + hr = extiodrv_device_open(_extio_com_port, &handle_extio); + + if (FAILED(hr)) { + log_warning( + "Failed opening EXTIO on com port '%s': %lX", + _extio_com_port, + hr); + return false; + } + } else { + handle_extio = INVALID_HANDLE_VALUE; + } + + fprintf( + stderr, + ">>> Press enter to start endless polling loop. Press Escape to exit " + "to exit polling loop <<<\n"); + + if (getchar() != '\n') { + return true; + } + + cnt = 0; + + while ((GetAsyncKeyState(VK_ESCAPE) & 0x8000) == 0) { + hr = p3iodrv_ddr_set_outputs(handle_p3io, &output); + + if (FAILED(hr)) { + log_warning("Setting outputs failed: %lX", hr); + return false; + } + + if (_extio_enabled) { + hr = extiodrv_extio_transfer( + handle_extio, + EXTIODRV_EXTIO_SENSOR_READ_MODE_ALL, + pad_lights, + neons); + + if (FAILED(hr)) { + log_warning("extio transfer failed: %lX", hr); + return false; + } + } + + hr = p3iodrv_ddr_get_jamma(handle_p3io, &jamma); + + if (FAILED(hr)) { + log_warning("Reading jamma failed: %lX", hr); + return false; + } + + printf( + "%d: %08X %08X %08X\n", + cnt, + *((uint32_t *) &jamma) & 0xFFFFFF00, + jamma.unused, + jamma.unused2); + + cnt++; + } + + if (_extio_enabled) { + hr = extiodrv_device_close(&handle_extio); + + if (FAILED(hr)) { + log_warning("Failed closing EXTIO: %lX", hr); + return false; + } + } + + return true; +} + +static bool _mode_sensores(HANDLE handle_p3io) +{ + HRESULT hr; + HANDLE handle_extio; + + struct p3io_ddr_jamma jamma; + struct p3io_ddr_output output; + struct extiodrv_extio_pad_lights pad_lights[EXTIO_PAD_LIGHT_MAX_PLAYERS]; + bool neons; + + uint32_t cnt; + uint8_t sensor_read; + + memset(&output, 0, sizeof(output)); + memset(&pad_lights, 0, sizeof(pad_lights)); + neons = false; + + if (_extio_enabled) { + hr = extiodrv_device_open(_extio_com_port, &handle_extio); + + if (FAILED(hr)) { + log_warning( + "Failed opening EXTIO on com port '%s': %lX", + _extio_com_port, + hr); + return false; + } + } else { + handle_extio = INVALID_HANDLE_VALUE; + } + + fprintf( + stderr, + ">>> Press enter to start endless sensor cycling loop. Press Escape to " + "exit to exit sensor cycling loop <<<\n"); + + if (getchar() != '\n') { + return true; + } + + cnt = 0; + sensor_read = 0; + + while ((GetAsyncKeyState(VK_ESCAPE) & 0x8000) == 0) { + hr = p3iodrv_ddr_set_outputs(handle_p3io, &output); + + if (FAILED(hr)) { + log_warning("Setting outputs failed: %lX", hr); + return false; + } + + if (_extio_enabled) { + hr = extiodrv_extio_transfer( + handle_extio, sensor_read + 1, pad_lights, neons); + + if (FAILED(hr)) { + log_warning("extio transfer failed: %lX", hr); + return false; + } + } + + hr = p3iodrv_ddr_get_jamma(handle_p3io, &jamma); + + if (FAILED(hr)) { + log_warning("Reading jamma failed: %lX", hr); + return false; + } + + printf( + "%d (%d): %08X %08X %08X\n", + cnt, + sensor_read, + *((uint32_t *) &jamma) & 0xFFFFFF00, + jamma.unused, + jamma.unused2); + + cnt++; + sensor_read++; + + if (sensor_read > 3) { + sensor_read = 0; + } + } + + if (_extio_enabled) { + hr = extiodrv_device_close(&handle_extio); + + if (FAILED(hr)) { + log_warning("Failed closing EXTIO: %lX", hr); + return false; + } + } + + return true; +} + +int main(int argc, char **argv) +{ + HANDLE handle; + bool result; + + if (!_process_cmd_args(argc, argv)) { + return -1; + } + + _init_logging(); + + if (_mode != MODE_SCAN) { + if (!_scan_and_open(&handle)) { + result = false; + } else { + result = _mode_procs[_mode](handle); + + if (!_close(&handle)) { + result = false; + } + } + } else { + result = _mode_procs[_mode](INVALID_HANDLE_VALUE); + } + + return result ? 0 : -1; +} \ No newline at end of file diff --git a/src/main/p3io-ddr-tool/mode-test.c b/src/main/p3io-ddr-tool/mode-test.c new file mode 100644 index 0000000..791e6d2 --- /dev/null +++ b/src/main/p3io-ddr-tool/mode-test.c @@ -0,0 +1,406 @@ +#define LOG_MODULE "mode-test" + +#include +#include + +#include "extiodrv/extio.h" + +#include "p3io/ddr.h" +#include "p3iodrv/ddr.h" +#include "p3iodrv/device.h" + +#include "util/log.h" + +#include "mode-test.h" + +struct mode_test_output_state { + struct p3io_ddr_output output; + struct extiodrv_extio_pad_lights pad_lights[EXTIO_PAD_LIGHT_MAX_PLAYERS]; + bool neons; +}; + +struct mode_test_input_state { + struct p3io_ddr_jamma jamma[4]; +}; + +static bool _check_input_state_change( + const struct mode_test_input_state *cur_state, + const struct mode_test_input_state *prev_state) +{ + return !memcmp(cur_state, prev_state, sizeof(struct mode_test_input_state)); +} + +static void _input_state_swap( + struct mode_test_input_state **cur_state, + struct mode_test_input_state **prev_state) +{ + struct mode_test_input_state *tmp; + + tmp = *cur_state; + *cur_state = *prev_state; + *prev_state = tmp; +} + +static bool _update_io_frame( + HANDLE handle_p3io, + HANDLE handle_extio, + bool polling_mode, + struct mode_test_input_state *input, + const struct mode_test_output_state *output) +{ + HRESULT hr; + + hr = p3iodrv_ddr_set_outputs(handle_p3io, &output->output); + + if (FAILED(hr)) { + log_warning("Setting outputs failed: %lX", hr); + return false; + } + + if (polling_mode) { + hr = extiodrv_extio_transfer( + handle_extio, + EXTIODRV_EXTIO_SENSOR_READ_MODE_ALL, + output->pad_lights, + output->neons); + + if (FAILED(hr)) { + log_warning("extio transfer failed: %lX", hr); + return false; + } + + hr = p3iodrv_ddr_get_jamma(handle_p3io, &input->jamma[0]); + + if (FAILED(hr)) { + log_warning("Reading jamma failed: %lX", hr); + return false; + } + } else { + for (uint8_t i = 0; i < 4; i++) { + hr = extiodrv_extio_transfer( + handle_extio, i + 1, output->pad_lights, output->neons); + + if (FAILED(hr)) { + log_warning("extio transfer failed: %lX", hr); + return false; + } + + hr = p3iodrv_ddr_get_jamma(handle_p3io, &input->jamma[i]); + + if (FAILED(hr)) { + log_warning("Reading jamma failed: %lX", hr); + return false; + } + } + } + + return true; +} + +static void _set_outputs_on_inputs( + const struct mode_test_input_state *input, + struct mode_test_output_state *output) +{ + memset(&output->pad_lights, 0, sizeof(output->pad_lights)); + + output->output.cabinet.p1_menu_buttons = 0; + output->output.cabinet.p2_menu_buttons = 0; + + for (uint8_t i = 0; i < 4; i++) { + output->pad_lights[0].up |= input->jamma[i].p1.pad_up ? 1 : 0; + output->pad_lights[0].down |= input->jamma[i].p1.pad_down ? 1 : 0; + output->pad_lights[0].left |= input->jamma[i].p1.pad_left ? 1 : 0; + output->pad_lights[0].right |= input->jamma[i].p1.pad_right ? 1 : 0; + + output->pad_lights[1].up |= input->jamma[i].p2.pad_up ? 1 : 0; + output->pad_lights[1].down |= input->jamma[i].p2.pad_down ? 1 : 0; + output->pad_lights[1].left |= input->jamma[i].p2.pad_left ? 1 : 0; + output->pad_lights[1].right |= input->jamma[i].p2.pad_right ? 1 : 0; + + output->output.cabinet.p1_menu_buttons |= + (input->jamma[i].p1.menu_left || input->jamma[i].p1.menu_right || + input->jamma[i].p1.menu_start) ? + 1 : + 0; + output->output.cabinet.p2_menu_buttons |= + (input->jamma[i].p2.menu_left || input->jamma[i].p2.menu_right || + input->jamma[i].p2.menu_start) ? + 1 : + 0; + } +} + +static void _render_main( + uint8_t cnt, bool polling_mode, const struct mode_test_input_state *state) +{ + printf("Counter: %d\n", cnt); + printf("Polling mode: %d\n", polling_mode); + printf("Player 1 Player 2\n"); + printf("\n"); + printf("Pad\n"); + + if (polling_mode) { + printf( + "Up: %d Up: %d\n", + state->jamma[0].p1.pad_up, + state->jamma[0].p2.pad_up); + printf( + "Down: %d Down: %d\n", + state->jamma[0].p1.pad_down, + state->jamma[0].p2.pad_down); + printf( + "Left: %d Left: %d\n", + state->jamma[0].p1.pad_left, + state->jamma[0].p2.pad_left); + printf( + "Right: %d Right: %d\n", + state->jamma[0].p1.pad_right, + state->jamma[0].p2.pad_right); + } else { + printf( + "Up: %c%c%c%c Up: %c%c%c%c\n", + state->jamma[0].p1.pad_up ? 'U' : '_', + state->jamma[1].p1.pad_up ? 'D' : '_', + state->jamma[2].p1.pad_up ? 'L' : '_', + state->jamma[3].p1.pad_up ? 'R' : '_', + state->jamma[0].p2.pad_up ? 'U' : '_', + state->jamma[1].p2.pad_up ? 'D' : '_', + state->jamma[2].p2.pad_up ? 'L' : '_', + state->jamma[3].p2.pad_up) ? + 'R' : + '_'; + printf( + "Down: %c%c%c%c Down: %c%c%c%c\n", + state->jamma[0].p1.pad_down ? 'U' : '_', + state->jamma[1].p1.pad_down ? 'D' : '_', + state->jamma[2].p1.pad_down ? 'L' : '_', + state->jamma[3].p1.pad_down ? 'R' : '_', + state->jamma[0].p2.pad_down ? 'U' : '_', + state->jamma[1].p2.pad_down ? 'D' : '_', + state->jamma[2].p2.pad_down ? 'L' : '_', + state->jamma[3].p2.pad_down ? 'R' : '_'); + printf( + "Left: %c%c%c%c Left: %c%c%c%c\n", + state->jamma[0].p1.pad_left ? 'U' : '_', + state->jamma[1].p1.pad_left ? 'D' : '_', + state->jamma[2].p1.pad_left ? 'L' : '_', + state->jamma[3].p1.pad_left ? 'R' : '_', + state->jamma[0].p2.pad_left ? 'U' : '_', + state->jamma[1].p2.pad_left ? 'D' : '_', + state->jamma[2].p2.pad_left ? 'L' : '_', + state->jamma[3].p2.pad_left ? 'R' : '_'); + printf( + "Right: %c%c%c%c Right: %c%c%c%c\n", + state->jamma[0].p1.pad_right ? 'U' : '_', + state->jamma[1].p1.pad_right ? 'D' : '_', + state->jamma[2].p1.pad_right ? 'L' : '_', + state->jamma[3].p1.pad_right ? 'R' : '_', + state->jamma[0].p2.pad_right ? 'U' : '_', + state->jamma[1].p2.pad_right ? 'D' : '_', + state->jamma[2].p2.pad_right ? 'L' : '_', + state->jamma[3].p2.pad_right ? 'R' : '_'); + } + + printf("\n"); + printf("Menu\n"); + printf( + "Start: %d Start: %d\n", + state->jamma[0].p1.menu_start, + state->jamma[0].p2.menu_start); + printf( + "Up: %d Up: %d\n", + state->jamma[0].operator.p1_menu_up, + state->jamma[0].operator.p2_menu_up); + printf( + "Down: %d Down: %d\n", + state->jamma[0].operator.p1_menu_down, + state->jamma[0].operator.p2_menu_down); + printf( + "Left: %d Left: %d\n", + state->jamma[0].p1.menu_left, + state->jamma[0].p2.menu_left); + printf( + "Right: %d Right: %d\n", + state->jamma[0].p1.menu_right, + state->jamma[0].p2.menu_right); + printf("\n"); + printf("Operator\n"); + printf( + "Test: %d Service: %d Coin: %d\n", + state->jamma[0].operator.test, + state->jamma[0].operator.service, + state->jamma[0].operator.coin); +} + +static void +_render_menu(bool *continue_loop, struct mode_test_output_state *output) +{ + printf( + "Menu options:\n" + " 0: Exit menu and continue loop\n" + " 1: Exit\n" + " 2: Set neon state\n" + " 3: Top lamp state\n" + " 4: Set all lights on\n" + " 5: Set all lights off\n" + "Waiting for input: "); + + char c = getchar(); + + // Keyboard input debounce + Sleep(10); + + switch (c) { + case '1': { + *continue_loop = false; + break; + } + + case '2': { + int state; + int n; + + printf("Enter neon state (0/1): "); + + n = scanf("%d", &state); + + if (n > 0) { + output->neons = state > 0; + } + + break; + } + + case '3': { + char buf[4]; + int n; + + printf("Enter top lamp state, chain of 0/1s: "); + + n = scanf("%8s", buf); + + if (n > 0) { + output->output.cabinet.top_p1_upper = buf[0] == '1'; + output->output.cabinet.top_p1_lower = buf[1] == '1'; + output->output.cabinet.top_p2_upper = buf[2] == '1'; + output->output.cabinet.top_p2_lower = buf[3] == '1'; + } + + break; + } + + case '4': { + output->output.cabinet.top_p1_upper = 1; + output->output.cabinet.top_p1_lower = 1; + output->output.cabinet.top_p2_upper = 1; + output->output.cabinet.top_p2_lower = 1; + + output->output.cabinet.p1_menu_buttons = 1; + output->output.cabinet.p2_menu_buttons = 1; + + output->neons = true; + + output->pad_lights[0].up = true; + output->pad_lights[0].down = true; + output->pad_lights[0].left = true; + output->pad_lights[0].right = true; + + output->pad_lights[1].up = true; + output->pad_lights[1].down = true; + output->pad_lights[1].left = true; + output->pad_lights[1].right = true; + + break; + } + + case '5': { + output->output.cabinet.top_p1_upper = 0; + output->output.cabinet.top_p1_lower = 0; + output->output.cabinet.top_p2_upper = 0; + output->output.cabinet.top_p2_lower = 0; + + output->output.cabinet.p1_menu_buttons = 0; + output->output.cabinet.p2_menu_buttons = 0; + + output->neons = false; + + output->pad_lights[0].up = false; + output->pad_lights[0].down = false; + output->pad_lights[0].left = false; + output->pad_lights[0].right = false; + + output->pad_lights[1].up = false; + output->pad_lights[1].down = false; + output->pad_lights[1].left = false; + output->pad_lights[1].right = false; + + break; + } + + case '0': + default: + break; + } +} + +bool mode_test_proc(HANDLE handle_p3io, HANDLE handle_extio) +{ + struct mode_test_input_state state[2]; + struct mode_test_input_state *cur_input_state; + struct mode_test_input_state *prev_input_state; + struct mode_test_output_state output_state; + + bool polling_mode; + bool loop; + uint8_t cnt; + + memset(&state, 0, sizeof(state)); + cur_input_state = &state[0]; + prev_input_state = &state[1]; + memset(&output_state, 0, sizeof(output_state)); + + polling_mode = true; + loop = true; + cnt = 0; + + fprintf(stderr, ">>> Press enter to start running test mode <<<\n"); + + if (getchar() != '\n') { + return true; + } + + while (loop) { + if ((GetAsyncKeyState(VK_ESCAPE) & 0x8000) != 0) { + system("cls"); + + _render_menu(&loop, &output_state); + } else { + _input_state_swap(&cur_input_state, &prev_input_state); + + memset(cur_input_state, 0, sizeof(struct mode_test_input_state)); + + if (!_update_io_frame( + handle_p3io, + handle_extio, + polling_mode, + cur_input_state, + &output_state)) { + return false; + } + + _set_outputs_on_inputs(cur_input_state, &output_state); + + if (_check_input_state_change(cur_input_state, prev_input_state)) { + system("cls"); + + _render_main(cnt, polling_mode, cur_input_state); + } + } + + /* avoid CPU banging */ + Sleep(1); + ++cnt; + } + + return true; +} \ No newline at end of file diff --git a/src/main/p3io-ddr-tool/mode-test.h b/src/main/p3io-ddr-tool/mode-test.h new file mode 100644 index 0000000..884ad66 --- /dev/null +++ b/src/main/p3io-ddr-tool/mode-test.h @@ -0,0 +1,11 @@ +#ifndef P3IO_DDR_TOOL_MODE_TEST_H +#define P3IO_DDR_TOOL_MODE_TEST_H + +#include + +#include +#include + +bool mode_test_proc(HANDLE handle_p3io, HANDLE handle_extio); + +#endif \ No newline at end of file