comparison io.c @ 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 93918a6a8ab7
children 1978bd770023
comparison
equal deleted inserted replaced
2237:f82c090c1e89 2238:0a107b2d5837
34 "Saturn Keyboard", 34 "Saturn Keyboard",
35 "XBAND Keyboard", 35 "XBAND Keyboard",
36 "Menacer", 36 "Menacer",
37 "Justifier", 37 "Justifier",
38 "Sega Multi-tap", 38 "Sega Multi-tap",
39 "EA 4-way Play cable A", 39 "EA 4-way Play",
40 "EA 4-way Play cable B", 40 "EA 4-way Play",
41 "Sega Parallel Transfer Board", 41 "Sega Parallel Transfer Board",
42 "Generic Device", 42 "Generic Device",
43 "Generic Serial", 43 "Generic Serial",
44 "Heartbeat Personal Trainer" 44 "Heartbeat Personal Trainer"
45 }; 45 };
64 HBPT_NEED_INIT, 64 HBPT_NEED_INIT,
65 HBPT_IDLE, 65 HBPT_IDLE,
66 HBPT_CMD_PAYLOAD, 66 HBPT_CMD_PAYLOAD,
67 HBPT_REPLY 67 HBPT_REPLY
68 }; 68 };
69
70 #define EA_PASSTHRU_MODE 0xFF
69 71
70 typedef struct { 72 typedef struct {
71 uint8_t states[2], value; 73 uint8_t states[2], value;
72 } gp_button_def; 74 } gp_button_def;
73 75
96 return port; 98 return port;
97 } 99 }
98 if (port->device_type == IO_HEARTBEAT_TRAINER && port->device.heartbeat_trainer.device_num == gamepad_num) { 100 if (port->device_type == IO_HEARTBEAT_TRAINER && port->device.heartbeat_trainer.device_num == gamepad_num) {
99 return port; 101 return port;
100 } 102 }
101 if (port->device_type == IO_SEGA_MULTI) { 103 if (port->device_type == IO_SEGA_MULTI || port->device_type == IO_EA_MULTI_A) {
102 for (int j = 0; j < 4; j++) 104 for (int j = 0; j < 4; j++)
103 { 105 {
104 io_port *tap_port = port->device.multitap.ports + j; 106 io_port *tap_port = port->device.multitap.ports + j;
105 if (tap_port->device_type < IO_MOUSE && tap_port->device.pad.gamepad_num == gamepad_num) { 107 if (tap_port->device_type < IO_MOUSE && tap_port->device.pad.gamepad_num == gamepad_num) {
106 return tap_port; 108 return tap_port;
116 for (int i = 0; i < 3; i++) 118 for (int i = 0; i < 3; i++)
117 { 119 {
118 io_port *port = io->ports + i; 120 io_port *port = io->ports + i;
119 if (port->device_type == IO_MOUSE && port->device.mouse.mouse_num == mouse_num) { 121 if (port->device_type == IO_MOUSE && port->device.mouse.mouse_num == mouse_num) {
120 return port; 122 return port;
123 }
124 if (port->device_type == IO_SEGA_MULTI) {
125 for (int j = 0; j < 4; j++)
126 {
127 io_port *tap_port = port->device.multitap.ports + j;
128 if (tap_port->device_type == IO_MOUSE && tap_port->device.mouse.mouse_num == mouse_num) {
129 return tap_port;
130 }
131 }
121 } 132 }
122 } 133 }
123 return NULL; 134 return NULL;
124 } 135 }
125 136
249 if (!device_type) 260 if (!device_type)
250 { 261 {
251 return; 262 return;
252 } 263 }
253 io_port *old_ports = NULL; 264 io_port *old_ports = NULL;
254 if (port->device_type == IO_SEGA_MULTI) { 265 if (port->device_type == IO_SEGA_MULTI || port->device_type == IO_EA_MULTI_A) {
255 old_ports = port->device.multitap.ports; 266 old_ports = port->device.multitap.ports;
256 } 267 }
257 268
258 const int gamepad_len = strlen("gamepad"); 269 const int gamepad_len = strlen("gamepad");
259 if (startswith(device_type, "gamepad")) 270 if (startswith(device_type, "gamepad"))
329 port->device.multitap.ready_cycle = CYCLE_NEVER; 340 port->device.multitap.ready_cycle = CYCLE_NEVER;
330 port->input[0] = 0x13; 341 port->input[0] = 0x13;
331 } 342 }
332 old_ports = NULL; 343 old_ports = NULL;
333 } 344 }
345 } else if(!strcmp(device_type, "ea_multitap_port_a")) {
346 if (port->device_type != IO_EA_MULTI_A) {
347 port->device_type = IO_EA_MULTI_A;
348 port->device.multitap.ports = old_ports ? old_ports : calloc(4, sizeof(io_port));
349 port->device.multitap.tap_num = 1;
350 port->device.multitap.cur_port = EA_PASSTHRU_MODE;
351 old_ports = NULL;
352 }
353 } else if(!strcmp(device_type, "ea_multitap_port_b")) {
354 port->device_type = IO_EA_MULTI_B;
355 port->device.multitap.ports = NULL;
356 port->device.multitap.tap_num = 1;
334 } 357 }
335 free(old_ports); 358 free(old_ports);
336 } 359 }
337 360
338 char * io_name(int i) 361 char * io_name(int i)
490 if (ports[i].control & ports[i].output & 0x40) { 513 if (ports[i].control & ports[i].output & 0x40) {
491 io_control_write(ports[i].device.multitap.ports + j, 0x40, 0); 514 io_control_write(ports[i].device.multitap.ports + j, 0x40, 0);
492 io_data_write(ports[i].device.multitap.ports + j, 0x40, 0); 515 io_data_write(ports[i].device.multitap.ports + j, 0x40, 0);
493 } 516 }
494 } 517 }
495 518 } else if (ports[i].device_type == IO_EA_MULTI_A) {
519 char path[] = "io\0ea_multitap\0";
520 tern_node *port_defs = tern_find_path(config, path, TVAL_NODE).ptrval;
521 debug_message("IO port %s connected to EA 4-way Play A-side\n", io_name(i));
522 for (int j = 0; j < 4; j++)
523 {
524 char port_num[] = {'1' + j, 0, 0};
525 char *dev_type = tern_find_ptr(port_defs, port_num);
526 process_device(dev_type, ports[i].device.multitap.ports + j);
527 debug_message("\tTap port %d connected to device '%s'\n", j + 1, device_type_names[ports[i].device.multitap.ports[j].device_type]);
528 io_control_write(ports[i].device.multitap.ports + j, 0x40, 0);
529 io_data_write(ports[i].device.multitap.ports + j, 0, 0);
530 }
531 } else if (ports[i].device_type == IO_EA_MULTI_B) {
532 debug_message("IO port %s connected to EA 4-way Play B-side\n", io_name(i));
533 for (int j = 0; j < 3; j++)
534 {
535 if (ports[j].device_type == IO_EA_MULTI_A) {
536 ports[i].device.multitap.ports = ports + j;
537 break;
538 }
539 }
496 } else { 540 } else {
497 debug_message("IO port %s connected to device '%s'\n", io_name(i), device_type_names[ports[i].device_type]); 541 debug_message("IO port %s connected to device '%s'\n", io_name(i), device_type_names[ports[i].device_type]);
498 } 542 }
499 } 543 }
500 } 544 }
1373 //TODO: measure actual delays 1417 //TODO: measure actual delays
1374 port->device.multitap.ready_cycle = current_cycle + 16 * 7; 1418 port->device.multitap.ready_cycle = current_cycle + 16 * 7;
1375 } 1419 }
1376 } 1420 }
1377 break; 1421 break;
1422 case IO_EA_MULTI_A:
1423 if ((output & TH) != (old_output & TH)) {
1424 uint8_t port_num = port->device.multitap.cur_port == EA_PASSTHRU_MODE ? 1 : port->device.multitap.cur_port;
1425 if (port_num < 4) {
1426 io_data_write(port->device.multitap.ports + port_num, output & 0x40, current_cycle);
1427 }
1428 }
1429 break;
1430 case IO_EA_MULTI_B: {
1431 io_port *main_port = port->device.multitap.ports;
1432 io_port *passthru = main_port->device.multitap.ports + 1;
1433 if (main_port->device.multitap.cur_port == EA_PASSTHRU_MODE) {
1434 output &= port->control | 0x40;
1435 output |= io_data_read(passthru, current_cycle) & ~(port->control | 0x40);
1436 }
1437 uint8_t old_port = main_port->device.multitap.cur_port;
1438 if ((output & 0xF) == 0xC) {
1439 main_port->device.multitap.cur_port = output >> 4 & 7;
1440 } else {
1441 main_port->device.multitap.cur_port = EA_PASSTHRU_MODE;
1442 }
1443 if (old_port == EA_PASSTHRU_MODE && main_port->device.multitap.cur_port < 4) {
1444 //switched from passthru to multitap mode, set TH for selected controller to port A value
1445 output = get_output_value(main_port, current_cycle, SLOW_RISE_DEVICE);
1446 io_data_write(main_port->device.multitap.ports + main_port->device.multitap.cur_port, output & 0x40, current_cycle);
1447 } else if (main_port->device.multitap.cur_port == EA_PASSTHRU_MODE) {
1448 //in passhtru mode, set TH to controller 2 to port B value
1449 io_data_write(main_port->device.multitap.ports + 1, output & 0x40, current_cycle);
1450 }
1451 break;
1452 }
1378 #ifndef _WIN32 1453 #ifndef _WIN32
1379 case IO_GENERIC: 1454 case IO_GENERIC:
1380 wait_for_connection(port); 1455 wait_for_connection(port);
1381 port->input[IO_STATE] = IO_WRITE_PENDING; 1456 port->input[IO_STATE] = IO_WRITE_PENDING;
1382 service_socket(port); 1457 service_socket(port);
1672 case IO_SEGA_MULTI: 1747 case IO_SEGA_MULTI:
1673 multitap_check_ready(port, current_cycle); 1748 multitap_check_ready(port, current_cycle);
1674 device_driven = 0x1F; 1749 device_driven = 0x1F;
1675 input = port->input[0]; 1750 input = port->input[0];
1676 break; 1751 break;
1752 case IO_EA_MULTI_A:
1753 device_driven = 0x3F;
1754 if (port->device.multitap.cur_port == EA_PASSTHRU_MODE) {
1755 input = io_data_read(port->device.multitap.ports, current_cycle);
1756 } else if (port->device.multitap.cur_port < 4) {
1757 input = io_data_read(port->device.multitap.ports + port->device.multitap.cur_port, current_cycle);
1758 } else {
1759 input = 0x3C;
1760 }
1761 break;
1762 case IO_EA_MULTI_B: {
1763 io_port *main_port = port->device.multitap.ports;
1764 if (main_port->device.multitap.cur_port == EA_PASSTHRU_MODE) {
1765 input = io_data_read(main_port->device.multitap.ports + 1, current_cycle);
1766 device_driven = 0x3F;
1767 } else {
1768 input = 0;
1769 device_driven = 0;
1770 }
1771 break;
1772 }
1677 #ifndef _WIN32 1773 #ifndef _WIN32
1678 case IO_SEGA_PARALLEL: 1774 case IO_SEGA_PARALLEL:
1679 if (!th) 1775 if (!th)
1680 { 1776 {
1681 service_pipe(port); 1777 service_pipe(port);