changeset 2238:0a107b2d5837

Add support for EA 4-way Play
author Michael Pavone <pavone@retrodev.com>
date Sat, 17 Sep 2022 18:29:24 -0700
parents f82c090c1e89
children b2f788f08a31
files config.c default.cfg io.c nuklear_ui/blastem_nuklear.c
diffstat 4 files changed, 144 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/config.c	Sat Sep 17 15:38:40 2022 -0700
+++ b/config.c	Sat Sep 17 18:29:24 2022 -0700
@@ -285,7 +285,7 @@
 	*pads = tern_insert_node(*pads, key, dupe_tree(val.ptrval));
 }
 
-#define CONFIG_VERSION 4
+#define CONFIG_VERSION 5
 static tern_node *migrate_config(tern_node *config, int from_version)
 {
 	tern_node *def_config = parse_bundled_config("default.cfg");
@@ -351,6 +351,16 @@
 		config = tern_insert_path(config, "io\0sega_multitap.1\0""3\0", (tern_val){.ptrval = strdup(tap13)}, TVAL_PTR);
 		config = tern_insert_path(config, "io\0sega_multitap.1\0""4\0", (tern_val){.ptrval = strdup(tap14)}, TVAL_PTR);
 	}
+	case 4: {
+		char *tap11 = tern_find_path_default(config, "io\0ea_multitap\0""1\0", (tern_val){.ptrval = "gamepad6.1"}, TVAL_PTR).ptrval;
+		char *tap12 = tern_find_path_default(config, "io\0ea_multitap\0""2\0", (tern_val){.ptrval = "gamepad6.2"}, TVAL_PTR).ptrval;
+		char *tap13 = tern_find_path_default(config, "io\0ea_multitap\0""3\0", (tern_val){.ptrval = "gamepad6.3"}, TVAL_PTR).ptrval;
+		char *tap14 = tern_find_path_default(config, "io\0ea_multitap\0""4\0", (tern_val){.ptrval = "gamepad6.4"}, TVAL_PTR).ptrval;
+		config = tern_insert_path(config, "io\0ea_multitap\0""1\0", (tern_val){.ptrval = strdup(tap11)}, TVAL_PTR);
+		config = tern_insert_path(config, "io\0ea_multitap\0""2\0", (tern_val){.ptrval = strdup(tap12)}, TVAL_PTR);
+		config = tern_insert_path(config, "io\0ea_multitap\0""3\0", (tern_val){.ptrval = strdup(tap13)}, TVAL_PTR);
+		config = tern_insert_path(config, "io\0ea_multitap\0""4\0", (tern_val){.ptrval = strdup(tap14)}, TVAL_PTR);
+	}
 	}
 	char buffer[16];
 	sprintf(buffer, "%d", CONFIG_VERSION);
--- a/default.cfg	Sat Sep 17 15:38:40 2022 -0700
+++ b/default.cfg	Sat Sep 17 18:29:24 2022 -0700
@@ -282,6 +282,12 @@
 		3 gamepad6.4
 		4 gamepad6.5
 	}
+	ea_multitap {
+		1 gamepad6.1
+		2 gamepad6.2
+		3 gamepad6.3
+		4 gamepad6.4
+	}
 }
 
 video {
--- a/io.c	Sat Sep 17 15:38:40 2022 -0700
+++ b/io.c	Sat Sep 17 18:29:24 2022 -0700
@@ -36,8 +36,8 @@
 	"Menacer",
 	"Justifier",
 	"Sega Multi-tap",
-	"EA 4-way Play cable A",
-	"EA 4-way Play cable B",
+	"EA 4-way Play",
+	"EA 4-way Play",
 	"Sega Parallel Transfer Board",
 	"Generic Device",
 	"Generic Serial",
@@ -67,6 +67,8 @@
 	HBPT_REPLY
 };
 
+#define EA_PASSTHRU_MODE 0xFF
+
 typedef struct {
 	uint8_t states[2], value;
 } gp_button_def;
@@ -98,7 +100,7 @@
 		if (port->device_type == IO_HEARTBEAT_TRAINER && port->device.heartbeat_trainer.device_num == gamepad_num) {
 			return port;
 		}
-		if (port->device_type == IO_SEGA_MULTI) {
+		if (port->device_type == IO_SEGA_MULTI || port->device_type == IO_EA_MULTI_A) {
 			for (int j = 0; j < 4; j++)
 			{
 				io_port *tap_port = port->device.multitap.ports + j;
@@ -119,6 +121,15 @@
 		if (port->device_type == IO_MOUSE && port->device.mouse.mouse_num == mouse_num) {
 			return port;
 		}
+		if (port->device_type == IO_SEGA_MULTI) {
+			for (int j = 0; j < 4; j++)
+			{
+				io_port *tap_port = port->device.multitap.ports + j;
+				if (tap_port->device_type == IO_MOUSE && tap_port->device.mouse.mouse_num == mouse_num) {
+					return tap_port;
+				}
+			}
+		}
 	}
 	return NULL;
 }
@@ -251,7 +262,7 @@
 		return;
 	}
 	io_port *old_ports = NULL;
-	if (port->device_type == IO_SEGA_MULTI) {
+	if (port->device_type == IO_SEGA_MULTI || port->device_type == IO_EA_MULTI_A) {
 		old_ports = port->device.multitap.ports;
 	}
 
@@ -331,6 +342,18 @@
 			}
 			old_ports = NULL;
 		}
+	} else if(!strcmp(device_type, "ea_multitap_port_a")) {
+		if (port->device_type != IO_EA_MULTI_A) {
+			port->device_type = IO_EA_MULTI_A;
+			port->device.multitap.ports = old_ports ? old_ports : calloc(4, sizeof(io_port));
+			port->device.multitap.tap_num = 1;
+			port->device.multitap.cur_port = EA_PASSTHRU_MODE;
+			old_ports = NULL;
+		}
+	} else if(!strcmp(device_type, "ea_multitap_port_b")) {
+		port->device_type = IO_EA_MULTI_B;
+		port->device.multitap.ports = NULL;
+		port->device.multitap.tap_num = 1;
 	}
 	free(old_ports);
 }
@@ -492,7 +515,28 @@
 					io_data_write(ports[i].device.multitap.ports + j, 0x40, 0);
 				}
 			}
-			
+		} else if (ports[i].device_type == IO_EA_MULTI_A) {
+			char path[] = "io\0ea_multitap\0";
+			tern_node *port_defs = tern_find_path(config, path, TVAL_NODE).ptrval;
+			debug_message("IO port %s connected to EA 4-way Play A-side\n", io_name(i));
+			for (int j = 0; j < 4; j++)
+			{
+				char port_num[] = {'1' + j, 0, 0};
+				char *dev_type = tern_find_ptr(port_defs, port_num);
+				process_device(dev_type, ports[i].device.multitap.ports + j);
+				debug_message("\tTap port %d connected to device '%s'\n", j + 1, device_type_names[ports[i].device.multitap.ports[j].device_type]);
+				io_control_write(ports[i].device.multitap.ports + j, 0x40, 0);
+				io_data_write(ports[i].device.multitap.ports + j, 0, 0);
+			}
+		} else if (ports[i].device_type == IO_EA_MULTI_B) {
+			debug_message("IO port %s connected to EA 4-way Play B-side\n", io_name(i));
+			for (int j = 0; j < 3; j++)
+			{
+				if (ports[j].device_type == IO_EA_MULTI_A) {
+					ports[i].device.multitap.ports = ports + j;
+					break;
+				}
+			}
 		} else {
 			debug_message("IO port %s connected to device '%s'\n", io_name(i), device_type_names[ports[i].device_type]);
 		}
@@ -1375,6 +1419,37 @@
 			}
 		}
 		break;
+	case IO_EA_MULTI_A:
+		if ((output & TH) != (old_output & TH)) {
+			uint8_t port_num = port->device.multitap.cur_port == EA_PASSTHRU_MODE ? 1 : port->device.multitap.cur_port;
+			if (port_num < 4) {
+				io_data_write(port->device.multitap.ports + port_num, output & 0x40, current_cycle);
+			}
+		}
+		break;
+	case IO_EA_MULTI_B: {
+		io_port *main_port = port->device.multitap.ports;
+		io_port *passthru = main_port->device.multitap.ports + 1;
+		if (main_port->device.multitap.cur_port == EA_PASSTHRU_MODE) {
+			output &= port->control | 0x40;
+			output |= io_data_read(passthru, current_cycle) & ~(port->control | 0x40);
+		}
+		uint8_t old_port = main_port->device.multitap.cur_port;
+		if ((output & 0xF) == 0xC) {
+			main_port->device.multitap.cur_port = output >> 4 & 7;
+		} else {
+			main_port->device.multitap.cur_port = EA_PASSTHRU_MODE;
+		}
+		if (old_port == EA_PASSTHRU_MODE && main_port->device.multitap.cur_port < 4) {
+			//switched from passthru to multitap mode, set TH for selected controller to port A value
+			output = get_output_value(main_port, current_cycle, SLOW_RISE_DEVICE);
+			io_data_write(main_port->device.multitap.ports + main_port->device.multitap.cur_port, output & 0x40, current_cycle);
+		} else if (main_port->device.multitap.cur_port == EA_PASSTHRU_MODE) {
+			//in passhtru mode, set TH to controller 2 to port B value
+			io_data_write(main_port->device.multitap.ports + 1, output & 0x40, current_cycle);
+		}
+		break;
+	}
 #ifndef _WIN32
 	case IO_GENERIC:
 		wait_for_connection(port);
@@ -1674,6 +1749,27 @@
 		device_driven = 0x1F;
 		input = port->input[0];
 		break;
+	case IO_EA_MULTI_A:
+		device_driven = 0x3F;
+		if (port->device.multitap.cur_port == EA_PASSTHRU_MODE) {
+			input = io_data_read(port->device.multitap.ports, current_cycle);
+		} else if (port->device.multitap.cur_port < 4) {
+			input = io_data_read(port->device.multitap.ports + port->device.multitap.cur_port, current_cycle);
+		} else {
+			input = 0x3C;
+		}
+		break;
+	case IO_EA_MULTI_B: {
+		io_port *main_port = port->device.multitap.ports;
+		if (main_port->device.multitap.cur_port == EA_PASSTHRU_MODE) {
+			input = io_data_read(main_port->device.multitap.ports + 1, current_cycle);
+			device_driven = 0x3F;
+		} else {
+			input = 0;
+			device_driven = 0;
+		}
+		break;
+	}
 #ifndef _WIN32
 	case IO_SEGA_PARALLEL:
 		if (!th)
--- a/nuklear_ui/blastem_nuklear.c	Sat Sep 17 15:38:40 2022 -0700
+++ b/nuklear_ui/blastem_nuklear.c	Sat Sep 17 18:29:24 2022 -0700
@@ -2059,6 +2059,7 @@
 		"gamepad3.1",
 		"gamepad6.1",
 		"sega_multitap.1",
+		"ea_multitap_port_a",
 		"mouse.1",
 		"saturn keyboard",
 		"xband keyboard"
@@ -2069,6 +2070,7 @@
 		"gamepad3.2",
 		"gamepad6.2",
 		"sega_multitap.1",
+		"ea_multitap_port_b",
 		"mouse.1",
 		"saturn keyboard",
 		"xband keyboard"
@@ -2083,9 +2085,10 @@
 		type_names[2] = device_type_names[IO_GAMEPAD3];
 		type_names[3] = device_type_names[IO_GAMEPAD6];
 		type_names[4] = device_type_names[IO_SEGA_MULTI];
-		type_names[5] = device_type_names[IO_MOUSE];
-		type_names[6] = device_type_names[IO_SATURN_KEYBOARD];
-		type_names[7] = device_type_names[IO_XBAND_KEYBOARD];
+		type_names[5] = device_type_names[IO_EA_MULTI_A];
+		type_names[6] = device_type_names[IO_MOUSE];
+		type_names[7] = device_type_names[IO_SATURN_KEYBOARD];
+		type_names[8] = device_type_names[IO_XBAND_KEYBOARD];
 		if (show_sms) {
 			selected_io_1 = find_match(io_opts_1, num_io, "sms\0io\0devices\0""1\0", "gamepad2.1");
 			selected_io_2 = find_match(io_opts_2, num_io, "sms\0io\0devices\0""2\0", "gamepad2.2");
@@ -2107,8 +2110,26 @@
 		} else {
 			selected_model = settings_dropdown_ex(context, "Model", model_opts, model_names, num_models, selected_model, "system\0model\0");
 		}
-		selected_io_1 = settings_dropdown_ex(context, "IO Port 1 Device", io_opts_1, type_names, num_io, selected_io_1, show_sms ? "sms\0io\0devices\0""1\0" : "io\0devices\0""1\0");
-		selected_io_2 = settings_dropdown_ex(context, "IO Port 2 Device", io_opts_2, type_names, num_io, selected_io_2, show_sms ? "sms\0io\0devices\0""2\0" : "io\0devices\0""2\0");
+		int32_t old_selected = selected_io_1;
+		char *config_path1, *config_path2;
+		if (show_sms) {
+			config_path1 = "sms\0io\0devices\0""1\0";
+			config_path2 = "sms\0io\0devices\0""2\0";
+		} else {
+			config_path1 = "io\0devices\0""1\0";
+			config_path2 = "io\0devices\0""2\0";
+		}
+		selected_io_1 = settings_dropdown_ex(context, "IO Port 1 Device", io_opts_1, type_names, num_io, selected_io_1, config_path1);
+		if (old_selected != selected_io_1 && selected_io_1 != selected_io_2 && !strcmp(io_opts_1[selected_io_1], "ea_multitap_port_a")) {
+			selected_io_2 = selected_io_1;
+			config = tern_insert_path(config, config_path2, (tern_val){.ptrval = strdup(io_opts_2[selected_io_2])}, TVAL_PTR);
+		}
+		old_selected = selected_io_2;
+		selected_io_2 = settings_dropdown_ex(context, "IO Port 2 Device", io_opts_2, type_names, num_io, selected_io_2, config_path2);
+		if (old_selected != selected_io_2 && selected_io_1 != selected_io_2 && !strcmp(io_opts_2[selected_io_2], "ea_multitap_port_b")) {
+			selected_io_1 = selected_io_2;
+			config = tern_insert_path(config, config_path1, (tern_val){.ptrval = strdup(io_opts_1[selected_io_1])}, TVAL_PTR);
+		}
 		selected_region = settings_dropdown_ex(context, "Default Region", region_codes, regions, num_regions, selected_region, "system\0default_region\0");
 		selected_sync = settings_dropdown(context, "Sync Source", sync_opts, num_sync_opts, selected_sync, "system\0sync_source\0");
 		if (!show_sms) {