Mercurial > repos > blastem
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); |