Mercurial > repos > blastem
diff io.c @ 803:236a184bf6f0
Merge
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sun, 26 Jul 2015 16:51:03 -0700 |
parents | 41f73c76b978 |
children | 9f149f0e98b7 |
line wrap: on
line diff
--- a/io.c Sun Jul 26 16:48:25 2015 -0700 +++ b/io.c Sun Jul 26 16:51:03 2015 -0700 @@ -3,15 +3,47 @@ This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ +#ifndef _WIN32 +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#endif +#include <string.h> +#include <stdlib.h> + #include "io.h" #include "blastem.h" #include "render.h" +const char * device_type_names[] = { + "3-button gamepad", + "6-button gamepad", + "Mega Mouse", + "Menacer", + "Justifier", + "Sega multi-tap", + "EA 4-way Play cable A", + "EA 4-way Play cable B", + "Sega Parallel Transfer Board", + "Generic Device", + "None" +}; + enum { BIND_NONE, + BIND_UI, BIND_GAMEPAD1, BIND_GAMEPAD2, - BIND_UI + BIND_GAMEPAD3, + BIND_GAMEPAD4, + BIND_GAMEPAD5, + BIND_GAMEPAD6, + BIND_GAMEPAD7, + BIND_GAMEPAD8 }; typedef enum { @@ -26,6 +58,7 @@ } ui_action; typedef struct { + io_port *port; uint8_t bind_type; uint8_t subtype_a; uint8_t subtype_b; @@ -117,7 +150,7 @@ void bind_gamepad(int keycode, int gamepadnum, int button) { - if (gamepadnum < 1 || gamepadnum > 2) { + if (gamepadnum < 1 || gamepadnum > 8) { return; } uint8_t bind_type = gamepadnum - 1 + BIND_GAMEPAD1; @@ -126,7 +159,7 @@ void bind_button_gamepad(int joystick, int joybutton, int gamepadnum, int padbutton) { - if (gamepadnum < 1 || gamepadnum > 2) { + if (gamepadnum < 1 || gamepadnum > 8) { return; } uint8_t bind_type = gamepadnum - 1 + BIND_GAMEPAD1; @@ -135,7 +168,7 @@ void bind_dpad_gamepad(int joystick, int dpad, uint8_t direction, int gamepadnum, int button) { - if (gamepadnum < 1 || gamepadnum > 2) { + if (gamepadnum < 1 || gamepadnum > 8) { return; } uint8_t bind_type = gamepadnum - 1 + BIND_GAMEPAD1; @@ -159,17 +192,14 @@ void handle_binding_down(keybinding * binding) { - switch(binding->bind_type) + if (binding->bind_type >= BIND_GAMEPAD1) { - case BIND_GAMEPAD1: - case BIND_GAMEPAD2: - if (binding->subtype_a <= GAMEPAD_EXTRA) { - genesis->ports[binding->bind_type - BIND_GAMEPAD1].input[binding->subtype_a] |= binding->value; + if (binding->subtype_a <= GAMEPAD_EXTRA && binding->port) { + binding->port->input[binding->subtype_a] |= binding->value; } - if (binding->subtype_b <= GAMEPAD_EXTRA) { - genesis->ports[binding->bind_type - BIND_GAMEPAD1].input[binding->subtype_b] |= binding->value; + if (binding->subtype_b <= GAMEPAD_EXTRA && binding->port) { + binding->port->input[binding->subtype_b] |= binding->value; } - break; } } @@ -206,11 +236,11 @@ { case BIND_GAMEPAD1: case BIND_GAMEPAD2: - if (binding->subtype_a <= GAMEPAD_EXTRA) { - genesis->ports[binding->bind_type - BIND_GAMEPAD1].input[binding->subtype_a] &= ~binding->value; + if (binding->subtype_a <= GAMEPAD_EXTRA && binding->port) { + binding->port->input[binding->subtype_a] &= ~binding->value; } - if (binding->subtype_b <= GAMEPAD_EXTRA) { - genesis->ports[binding->bind_type - BIND_GAMEPAD1].input[binding->subtype_b] &= ~binding->value; + if (binding->subtype_b <= GAMEPAD_EXTRA && binding->port) { + binding->port->input[binding->subtype_b] &= ~binding->value; } break; case BIND_UI: @@ -218,7 +248,7 @@ { case UI_DEBUG_MODE_INC: ui_debug_mode++; - if (ui_debug_mode == 4) { + if (ui_debug_mode == 7) { ui_debug_mode = 0; } genesis->vdp->debug = ui_debug_mode; @@ -228,7 +258,7 @@ if (ui_debug_pal == 4) { ui_debug_pal = 0; } - render_debug_pal(ui_debug_pal); + genesis->vdp->debug_pal = ui_debug_pal; break; case UI_ENTER_DEBUGGER: break_on_sync = 1; @@ -310,7 +340,7 @@ int parse_binding_target(char * target, tern_node * padbuttons, int * ui_out, int * padnum_out, int * padbutton_out) { int gpadslen = strlen("gamepads."); - if (!memcmp(target, "gamepads.", gpadslen)) { + if (!strncmp(target, "gamepads.", gpadslen)) { if (target[gpadslen] >= '1' && target[gpadslen] <= '8') { int padnum = target[gpadslen] - '0'; int button = tern_find_int(padbuttons, target + gpadslen + 1, 0); @@ -328,7 +358,7 @@ } else { fprintf(stderr, "Gamepad mapping string '%s' refers to an invalid gamepad number %c\n", target, target[gpadslen]); } - } else if(!memcmp(target, "ui.", strlen("ui."))) { + } else if(!strncmp(target, "ui.", strlen("ui."))) { *padbutton_out = 0; if (!strcmp(target + 3, "vdp_debug_mode")) { *ui_out = UI_DEBUG_MODE_INC; @@ -338,7 +368,7 @@ *ui_out = UI_ENTER_DEBUGGER; } else if(!strcmp(target + 3, "save_state")) { *ui_out = UI_SAVE_STATE; - } else if(!memcmp(target + 3, "set_speed.", strlen("set_speed."))) { + } else if(!strncmp(target + 3, "set_speed.", strlen("set_speed."))) { *ui_out = UI_SET_SPEED; *padbutton_out = atoi(target + 3 + strlen("set_speed.")); } else if(!strcmp(target + 3, "next_speed")) { @@ -447,7 +477,171 @@ } } -void set_keybindings() +void process_device(char * device_type, io_port * port) +{ + port->device_type = IO_NONE; + if (!device_type) + { + return; + } + + const int gamepad_len = strlen("gamepad"); + if (!memcmp(device_type, "gamepad", gamepad_len)) + { + if ( + (device_type[gamepad_len] != '3' && device_type[gamepad_len] != '6') + || device_type[gamepad_len+1] != '.' || device_type[gamepad_len+2] < '1' + || device_type[gamepad_len+2] > '8' || device_type[gamepad_len+3] != 0 + ) + { + fprintf(stderr, "%s is not a valid gamepad type\n", device_type); + } else if (device_type[gamepad_len] == '3') + { + port->device_type = IO_GAMEPAD3; + } else { + port->device_type = IO_GAMEPAD6; + } + port->device.pad.gamepad_num = device_type[gamepad_len+2] - '1'; + } else if(!strcmp(device_type, "sega_parallel")) { + port->device_type = IO_SEGA_PARALLEL; + port->device.stream.data_fd = -1; + port->device.stream.listen_fd = -1; + } else if(!strcmp(device_type, "generic")) { + port->device_type = IO_GENERIC; + port->device.stream.data_fd = -1; + port->device.stream.listen_fd = -1; + } +} + +char * io_name(int i) +{ + switch (i) + { + case 0: + return "1"; + case 1: + return "2"; + case 2: + return "EXT"; + default: + return "invalid"; + } +} + +static char * sockfile_name; +static void cleanup_sockfile() +{ + unlink(sockfile_name); +} + +void setup_io_devices(tern_node * config, io_port * ports) +{ + tern_node *io_nodes = tern_get_node(tern_find_path(config, "io\0devices\0")); + char * io_1 = tern_find_ptr(io_nodes, "1"); + char * io_2 = tern_find_ptr(io_nodes, "2"); + char * io_ext = tern_find_ptr(io_nodes, "ext"); + + process_device(io_1, ports); + process_device(io_2, ports+1); + process_device(io_ext, ports+2); + + for (int i = 0; i < 3; i++) + { +#ifndef _WIN32 + if (ports[i].device_type == IO_SEGA_PARALLEL) + { + char *pipe_name = tern_find_path(config, "io\0parallel_pipe\0").ptrval; + if (!pipe_name) + { + fprintf(stderr, "IO port %s is configured to use the sega parallel board, but no paralell_pipe is set!\n", io_name(i)); + ports[i].device_type = IO_NONE; + } else { + printf("IO port: %s connected to device '%s' with pipe name: %s\n", io_name(i), device_type_names[ports[i].device_type], pipe_name); + if (!strcmp("stdin", pipe_name)) + { + ports[i].device.stream.data_fd = STDIN_FILENO; + } else { + if (mkfifo(pipe_name, 0666) && errno != EEXIST) + { + fprintf(stderr, "Failed to create fifo %s for Sega parallel board emulation: %d %s\n", pipe_name, errno, strerror(errno)); + ports[i].device_type = IO_NONE; + } else { + ports[i].device.stream.data_fd = open(pipe_name, O_NONBLOCK | O_RDONLY); + if (ports[i].device.stream.data_fd == -1) + { + fprintf(stderr, "Failed to open fifo %s for Sega parallel board emulation: %d %s\n", pipe_name, errno, strerror(errno)); + ports[i].device_type = IO_NONE; + } + } + } + } + } else if (ports[i].device_type == IO_GENERIC) { + char *sock_name = tern_find_path(config, "io\0socket\0").ptrval; + if (!sock_name) + { + fprintf(stderr, "IO port %s is configured to use generic IO, but no socket is set!\n", io_name(i)); + ports[i].device_type = IO_NONE; + } else { + printf("IO port: %s connected to device '%s' with socket name: %s\n", io_name(i), device_type_names[ports[i].device_type], sock_name); + ports[i].device.stream.data_fd = -1; + ports[i].device.stream.listen_fd = socket(AF_UNIX, SOCK_STREAM, 0); + size_t pathlen = strlen(sock_name); + size_t addrlen = offsetof(struct sockaddr_un, sun_path) + pathlen + 1; + struct sockaddr_un *saddr = malloc(addrlen); + saddr->sun_family = AF_UNIX; + memcpy(saddr->sun_path, sock_name, pathlen+1); + if (bind(ports[i].device.stream.listen_fd, (struct sockaddr *)saddr, addrlen)) + { + fprintf(stderr, "Failed to bind socket for IO Port %s to path %s: %d %s\n", io_name(i), sock_name, errno, strerror(errno)); + goto cleanup_sock; + } + if (listen(ports[i].device.stream.listen_fd, 1)) + { + fprintf(stderr, "Failed to listen on socket for IO Port %s: %d %s\n", io_name(i), errno, strerror(errno)); + goto cleanup_sockfile; + } + sockfile_name = sock_name; + atexit(cleanup_sockfile); + continue; +cleanup_sockfile: + unlink(sock_name); +cleanup_sock: + close(ports[i].device.stream.listen_fd); + ports[i].device_type = IO_NONE; + } + } else +#endif + if (ports[i].device_type == IO_GAMEPAD3 || ports[i].device_type == IO_GAMEPAD6) { + printf("IO port %s connected to gamepad #%d with type '%s'\n", io_name(i), ports[i].device.pad.gamepad_num + 1, device_type_names[ports[i].device_type]); + } else { + printf("IO port %s connected to device '%s'\n", io_name(i), device_type_names[ports[i].device_type]); + } + } +} + +void map_bindings(io_port *ports, keybinding *bindings, int numbindings) +{ + for (int i = 0; i < numbindings; i++) + { + if (bindings[i].bind_type >= BIND_GAMEPAD1) + { + int num = bindings[i].bind_type - BIND_GAMEPAD1; + for (int j = 0; j < 3; j++) + { + if ((ports[j].device_type == IO_GAMEPAD3 + || ports[j].device_type ==IO_GAMEPAD6) + && ports[j].device.pad.gamepad_num == num + ) + { + bindings[i].port = ports + j; + break; + } + } + } + } +} + +void set_keybindings(io_port *ports) { tern_node * special = tern_insert_int(NULL, "up", RENDERKEY_UP); special = tern_insert_int(special, "down", RENDERKEY_DOWN); @@ -471,137 +665,321 @@ padbuttons = tern_insert_int(padbuttons, ".start", BUTTON_START); padbuttons = tern_insert_int(padbuttons, ".mode", BUTTON_MODE); - tern_node * keys = tern_find_prefix(config, "bindingskeys"); + tern_node * keys = tern_get_node(tern_find_path(config, "bindings\0keys\0")); process_keys(keys, special, padbuttons, NULL); - char prefix[] = "bindingspads00"; - for (int i = 0; i < 100 && i < render_num_joysticks(); i++) - { - if (i < 10) { - prefix[strlen("bindingspads")] = i + '0'; - prefix[strlen("bindingspads")+1] = 0; - } else { - prefix[strlen("bindingspads")] = i/10 + '0'; - prefix[strlen("bindingspads")+1] = i%10 + '0'; - } - tern_node * pad = tern_find_prefix(config, prefix); - if (pad) { - char dprefix[] = "dpads0"; - for (int dpad = 0; dpad < 10 && dpad < render_joystick_num_hats(i); dpad++) - { - dprefix[strlen("dpads")] = dpad + '0'; - tern_node * pad_dpad = tern_find_prefix(pad, dprefix); - char * dirs[] = {"up", "down", "left", "right"}; - int dirnums[] = {RENDER_DPAD_UP, RENDER_DPAD_DOWN, RENDER_DPAD_LEFT, RENDER_DPAD_RIGHT}; - for (int dir = 0; dir < sizeof(dirs)/sizeof(dirs[0]); dir++) { - char * target = tern_find_ptr(pad_dpad, dirs[dir]); - if (target) { - int ui_func, padnum, button; - int bindtype = parse_binding_target(target, padbuttons, &ui_func, &padnum, &button); - if (bindtype == 1) { - bind_dpad_gamepad(i, dpad, dirnums[dir], padnum, button); - } else if (bindtype == 2) { - bind_dpad_ui(i, dpad, dirnums[dir], ui_func, button); + char numstr[] = "00"; + tern_node * pads = tern_get_node(tern_find_path(config, "bindings\0pads\0")); + if (pads) { + for (int i = 0; i < 100 && i < render_num_joysticks(); i++) + { + + if (i < 10) { + numstr[0] = i + '0'; + numstr[1] = 0; + } else { + numstr[0] = i/10 + '0'; + numstr[1] = i%10 + '0'; + } + tern_node * pad = tern_find_ptr(pads, numstr); + if (pad) { + tern_node * dpad_node = tern_find_ptr(pad, "dpads"); + if (dpad_node) { + for (int dpad = 0; dpad < 10 && dpad < render_joystick_num_hats(i); dpad++) + { + numstr[0] = dpad + '0'; + numstr[1] = 0; + tern_node * pad_dpad = tern_find_ptr(dpad_node, numstr); + char * dirs[] = {"up", "down", "left", "right"}; + int dirnums[] = {RENDER_DPAD_UP, RENDER_DPAD_DOWN, RENDER_DPAD_LEFT, RENDER_DPAD_RIGHT}; + for (int dir = 0; dir < sizeof(dirs)/sizeof(dirs[0]); dir++) { + char * target = tern_find_ptr(pad_dpad, dirs[dir]); + if (target) { + int ui_func, padnum, button; + int bindtype = parse_binding_target(target, padbuttons, &ui_func, &padnum, &button); + if (bindtype == 1) { + bind_dpad_gamepad(i, dpad, dirnums[dir], padnum, button); + } else if (bindtype == 2) { + bind_dpad_ui(i, dpad, dirnums[dir], ui_func, button); + } + } } } } - } - char bprefix[] = "buttons00"; - for (int but = 0; but < 100 && but < render_joystick_num_buttons(i); but++) - { - if (but < 10) { - bprefix[strlen("buttons")] = but + '0'; - bprefix[strlen("buttons")+1] = 0; - } else { - bprefix[strlen("buttons")] = but/10 + '0'; - bprefix[strlen("buttons")+1] = but%10 + '0'; - } - char * target = tern_find_ptr(pad, bprefix); - if (target) { - int ui_func, padnum, button; - int bindtype = parse_binding_target(target, padbuttons, &ui_func, &padnum, &button); - if (bindtype == 1) { - bind_button_gamepad(i, but, padnum, button); - } else if (bindtype == 2) { - bind_button_ui(i, but, ui_func, button); + tern_node *button_node = tern_find_ptr(pad, "buttons"); + if (button_node) { + for (int but = 0; but < 100 && but < render_joystick_num_buttons(i); but++) + { + if (but < 10) { + numstr[0] = but + '0'; + numstr[1] = 0; + } else { + numstr[0] = but/10 + '0'; + numstr[1] = but%10 + '0'; + } + char * target = tern_find_ptr(button_node, numstr); + if (target) { + int ui_func, padnum, button; + int bindtype = parse_binding_target(target, padbuttons, &ui_func, &padnum, &button); + if (bindtype == 1) { + bind_button_gamepad(i, but, padnum, button); + } else if (bindtype == 2) { + bind_button_ui(i, but, ui_func, button); + } + } } } } } } - tern_node * speed_nodes = tern_find_prefix(config, "clocksspeeds"); + tern_node * speed_nodes = tern_get_node(tern_find_path(config, "clocks\0speeds\0")); speeds = malloc(sizeof(uint32_t)); speeds[0] = 100; process_speeds(speed_nodes, NULL); - for (int i = 0; i < num_speeds; i++) { + for (int i = 0; i < num_speeds; i++) + { if (!speeds[i]) { fprintf(stderr, "Speed index %d was not set to a valid percentage!", i); speeds[i] = 100; } } + for (int bucket = 0; bucket < 256; bucket++) + { + if (bindings[bucket]) + { + map_bindings(ports, bindings[bucket], 256); + } + } + for (int stick = 0; stick < MAX_JOYSTICKS; stick++) + { + if (joybindings[stick]) + { + int numbuttons = render_joystick_num_buttons(stick); + map_bindings(ports, joybindings[stick], render_joystick_num_buttons(stick)); + } + if (joydpads[stick]) + { + map_bindings(ports, joydpads[stick]->bindings, 4); + } + } } #define TH 0x40 -#define TH_TIMEOUT 8000 +#define TH_TIMEOUT 56000 -void io_adjust_cycles(io_port * pad, uint32_t current_cycle, uint32_t deduction) +void io_adjust_cycles(io_port * port, uint32_t current_cycle, uint32_t deduction) { /*uint8_t control = pad->control | 0x80; uint8_t th = control & pad->output; if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) { printf("adjust_cycles | control: %X, TH: %X, GAMEPAD_TH0: %X, GAMEPAD_TH1: %X, TH Counter: %d, Timeout: %d, Cycle: %d\n", control, th, pad->input[GAMEPAD_TH0], pad->input[GAMEPAD_TH1], pad->th_counter,pad->timeout_cycle, current_cycle); }*/ - if (current_cycle >= pad->timeout_cycle) { - pad->th_counter = 0; - } else { - pad->timeout_cycle -= deduction; + if (port->device_type == IO_GAMEPAD6) + { + if (current_cycle >= port->device.pad.timeout_cycle) + { + port->device.pad.th_counter = 0; + } else { + port->device.pad.timeout_cycle -= deduction; + } + } +} + +#ifndef _WIN32 +static void wait_for_connection(io_port * port) +{ + if (port->device.stream.data_fd == -1) + { + puts("Waiting for socket connection..."); + port->device.stream.data_fd = accept(port->device.stream.listen_fd, NULL, NULL); + fcntl(port->device.stream.data_fd, F_SETFL, O_NONBLOCK | O_RDWR); + } +} + +static void service_pipe(io_port * port) +{ + uint8_t value; + int numRead = read(port->device.stream.data_fd, &value, sizeof(value)); + if (numRead > 0) + { + port->input[IO_TH0] = (value & 0xF) | 0x10; + port->input[IO_TH1] = (value >> 4) | 0x10; + } else if(numRead == -1 && errno != EAGAIN && errno != EWOULDBLOCK) { + fprintf(stderr, "Error reading pipe for IO port: %d %s\n", errno, strerror(errno)); } } -void io_data_write(io_port * pad, uint8_t value, uint32_t current_cycle) +static void service_socket(io_port *port) { - if (pad->control & TH) { - //check if TH has changed - if ((pad->output & TH) ^ (value & TH)) { - if (current_cycle >= pad->timeout_cycle) { - pad->th_counter = 0; + uint8_t buf[32]; + uint8_t blocking = 0; + int numRead = 0; + while (numRead <= 0) + { + numRead = recv(port->device.stream.data_fd, buf, sizeof(buf), 0); + if (numRead > 0) + { + port->input[IO_TH0] = buf[numRead-1]; + if (port->input[IO_STATE] == IO_READ_PENDING) + { + port->input[IO_STATE] = IO_READ; + if (blocking) + { + //pending read satisfied, back to non-blocking mode + fcntl(port->device.stream.data_fd, F_SETFL, O_RDWR | O_NONBLOCK); + } + } else if (port->input[IO_STATE] == IO_WRITTEN) { + port->input[IO_STATE] = IO_READ; } - if (!(value & TH)) { - pad->th_counter++; + } else if (numRead == 0) { + port->device.stream.data_fd = -1; + wait_for_connection(port); + } else if (errno != EAGAIN && errno != EWOULDBLOCK) { + fprintf(stderr, "Error reading from socket for IO port: %d %s\n", errno, strerror(errno)); + close(port->device.stream.data_fd); + wait_for_connection(port); + } else if (port->input[IO_STATE] == IO_READ_PENDING) { + //clear the nonblocking flag so the next read will block + if (!blocking) + { + fcntl(port->device.stream.data_fd, F_SETFL, O_RDWR); + blocking = 1; } - pad->timeout_cycle = current_cycle + TH_TIMEOUT; + } else { + //no new data, but that's ok + break; } } - pad->output = value; + + if (port->input[IO_STATE] == IO_WRITE_PENDING) + { + uint8_t value = port->output & port->control; + int written = 0; + blocking = 0; + while (written <= 0) + { + send(port->device.stream.data_fd, &value, sizeof(value), 0); + if (written > 0) + { + port->input[IO_STATE] = IO_WRITTEN; + if (blocking) + { + //pending write satisfied, back to non-blocking mode + fcntl(port->device.stream.data_fd, F_SETFL, O_RDWR | O_NONBLOCK); + } + } else if (written == 0) { + port->device.stream.data_fd = -1; + wait_for_connection(port); + } else if (errno != EAGAIN && errno != EWOULDBLOCK) { + fprintf(stderr, "Error writing to socket for IO port: %d %s\n", errno, strerror(errno)); + close(port->device.stream.data_fd); + wait_for_connection(port); + } else { + //clear the nonblocking flag so the next write will block + if (!blocking) + { + fcntl(port->device.stream.data_fd, F_SETFL, O_RDWR); + blocking = 1; + } + } + } + } +} +#endif + +void io_data_write(io_port * port, uint8_t value, uint32_t current_cycle) +{ + switch (port->device_type) + { + case IO_GAMEPAD6: + if (port->control & TH) { + //check if TH has changed + if ((port->output & TH) ^ (value & TH)) { + if (current_cycle >= port->device.pad.timeout_cycle) { + port->device.pad.th_counter = 0; + } + if (!(value & TH)) { + port->device.pad.th_counter++; + } + port->device.pad.timeout_cycle = current_cycle + TH_TIMEOUT; + } + } + port->output = value; + break; +#ifndef _WIN32 + case IO_GENERIC: + wait_for_connection(port); + port->input[IO_STATE] = IO_WRITE_PENDING; + port->output = value; + service_socket(port); + break; +#endif + default: + port->output = value; + } + } -uint8_t io_data_read(io_port * pad, uint32_t current_cycle) +uint8_t io_data_read(io_port * port, uint32_t current_cycle) { - uint8_t control = pad->control | 0x80; - uint8_t th = control & pad->output; + uint8_t control = port->control | 0x80; + uint8_t th = control & port->output & 0x40; uint8_t input; - if (current_cycle >= pad->timeout_cycle) { - pad->th_counter = 0; + switch (port->device_type) + { + case IO_GAMEPAD3: + { + input = port->input[th ? GAMEPAD_TH1 : GAMEPAD_TH0]; + break; } - /*if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) { - printf("io_data_read | control: %X, TH: %X, GAMEPAD_TH0: %X, GAMEPAD_TH1: %X, TH Counter: %d, Timeout: %d, Cycle: %d\n", control, th, pad->input[GAMEPAD_TH0], pad->input[GAMEPAD_TH1], pad->th_counter,pad->timeout_cycle, context->current_cycle); - }*/ - if (th) { - if (pad->th_counter == 3) { - input = pad->input[GAMEPAD_EXTRA]; + case IO_GAMEPAD6: + { + if (current_cycle >= port->device.pad.timeout_cycle) { + port->device.pad.th_counter = 0; + } + /*if (port->input[GAMEPAD_TH0] || port->input[GAMEPAD_TH1]) { + printf("io_data_read | control: %X, TH: %X, GAMEPAD_TH0: %X, GAMEPAD_TH1: %X, TH Counter: %d, Timeout: %d, Cycle: %d\n", control, th, port->input[GAMEPAD_TH0], port->input[GAMEPAD_TH1], port->th_counter,port->timeout_cycle, context->current_cycle); + }*/ + if (th) { + if (port->device.pad.th_counter == 3) { + input = port->input[GAMEPAD_EXTRA]; + } else { + input = port->input[GAMEPAD_TH1]; + } } else { - input = pad->input[GAMEPAD_TH1]; + if (port->device.pad.th_counter == 3) { + input = port->input[GAMEPAD_TH0] | 0xF; + } else if(port->device.pad.th_counter == 4) { + input = port->input[GAMEPAD_TH0] & 0x30; + } else { + input = port->input[GAMEPAD_TH0] | 0xC; + } } - } else { - if (pad->th_counter == 3) { - input = pad->input[GAMEPAD_TH0] | 0xF; - } else if(pad->th_counter == 4) { - input = pad->input[GAMEPAD_TH0] & 0x30; - } else { - input = pad->input[GAMEPAD_TH0] | 0xC; + break; + } +#ifndef _WIN32 + case IO_SEGA_PARALLEL: + if (!th) + { + service_pipe(port); } + input = ~port->input[th ? IO_TH1 : IO_TH0]; + break; + case IO_GENERIC: + if (port->input[IO_TH0] & 0x80 && port->input[IO_STATE] == IO_WRITTEN) + { + //device requested a blocking read after writes + port->input[IO_STATE] = IO_READ_PENDING; + } + service_socket(port); + input = ~port->input[IO_TH0]; + break; +#endif + default: + input = 0; + break; } - uint8_t value = ((~input) & (~control)) | (pad->output & control); - /*if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) { + uint8_t value = ((~input) & (~control)) | (port->output & control); + /*if (port->input[GAMEPAD_TH0] || port->input[GAMEPAD_TH1]) { printf ("value: %X\n", value); }*/ return value;