summaryrefslogtreecommitdiff
path: root/io.c
diff options
context:
space:
mode:
Diffstat (limited to 'io.c')
-rw-r--r--io.c596
1 files changed, 487 insertions, 109 deletions
diff --git a/io.c b/io.c
index 4935321..6199af0 100644
--- a/io.c
+++ b/io.c
@@ -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 @@ typedef enum {
} 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_dpad(int joystick, int dpad, int direction, uint8_t bind_type, uint8_t
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_gamepad(int keycode, int gamepadnum, int button)
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_button_gamepad(int joystick, int joybutton, int gamepadnum, int padbut
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 bind_dpad_ui(int joystick, int dpad, uint8_t direction, ui_action action, u
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 @@ void handle_binding_up(keybinding * binding)
{
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 @@ void handle_binding_up(keybinding * binding)
{
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 @@ void handle_binding_up(keybinding * binding)
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 @@ void handle_joy_dpad(int joystick, int dpadnum, uint8_t value)
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 @@ int parse_binding_target(char * target, tern_node * padbuttons, int * ui_out, in
} 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 @@ int parse_binding_target(char * target, tern_node * padbuttons, int * ui_out, in
*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 process_speeds(tern_node * cur, char * prefix)
}
}
-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 @@ void set_keybindings()
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);
}
}
-void io_data_write(io_port * pad, uint8_t value, uint32_t current_cycle)
+static void service_pipe(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 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));
+ }
+}
+
+static void service_socket(io_port *port)
+{
+ 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;
+ }
+ } else {
+ //no new data, but that's ok
+ break;
+ }
+ }
+
+ 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;
+ }
}
- pad->timeout_cycle = current_cycle + TH_TIMEOUT;
}
}
- pad->output = value;
}
+#endif
-uint8_t io_data_read(io_port * pad, uint32_t current_cycle)
+void io_data_write(io_port * port, uint8_t value, uint32_t current_cycle)
{
- uint8_t control = pad->control | 0x80;
- uint8_t th = control & pad->output;
+ 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 * port, uint32_t current_cycle)
+{
+ 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];
- } else {
- input = pad->input[GAMEPAD_TH1];
+ case IO_GAMEPAD6:
+ {
+ if (current_cycle >= port->device.pad.timeout_cycle) {
+ port->device.pad.th_counter = 0;
}
- } else {
- if (pad->th_counter == 3) {
- input = pad->input[GAMEPAD_TH0] | 0xF;
- } else if(pad->th_counter == 4) {
- input = pad->input[GAMEPAD_TH0] & 0x30;
+ /*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_TH0] | 0xC;
+ 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;
+ }
}
+ 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;