comparison io.c @ 2235:93918a6a8ab7

Initial support for Sega multi-tap
author Michael Pavone <pavone@retrodev.com>
date Tue, 13 Sep 2022 20:08:26 -0700
parents e597572f45ce
children 0a107b2d5837
comparison
equal deleted inserted replaced
2234:b6fdedd3b070 2235:93918a6a8ab7
33 "Mega Mouse", 33 "Mega Mouse",
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 cable A",
40 "EA 4-way Play cable B", 40 "EA 4-way Play cable B",
41 "Sega Parallel Transfer Board", 41 "Sega Parallel Transfer Board",
42 "Generic Device", 42 "Generic Device",
43 "Generic Serial", 43 "Generic Serial",
95 if (port->device_type < IO_MOUSE && port->device.pad.gamepad_num == gamepad_num) { 95 if (port->device_type < IO_MOUSE && port->device.pad.gamepad_num == gamepad_num) {
96 return port; 96 return port;
97 } 97 }
98 if (port->device_type == IO_HEARTBEAT_TRAINER && port->device.heartbeat_trainer.device_num == gamepad_num) { 98 if (port->device_type == IO_HEARTBEAT_TRAINER && port->device.heartbeat_trainer.device_num == gamepad_num) {
99 return port; 99 return port;
100 } 100 }
101 if (port->device_type == IO_SEGA_MULTI) {
102 for (int j = 0; j < 4; j++)
103 {
104 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) {
106 return tap_port;
107 }
108 }
109 }
101 } 110 }
102 return NULL; 111 return NULL;
103 } 112 }
104 113
105 static io_port *find_mouse(sega_io *io, uint8_t mouse_num) 114 static io_port *find_mouse(sega_io *io, uint8_t mouse_num)
238 set_serial_clock(port); 247 set_serial_clock(port);
239 //assuming that the io_port struct has been zeroed if this is the first time this has been called 248 //assuming that the io_port struct has been zeroed if this is the first time this has been called
240 if (!device_type) 249 if (!device_type)
241 { 250 {
242 return; 251 return;
252 }
253 io_port *old_ports = NULL;
254 if (port->device_type == IO_SEGA_MULTI) {
255 old_ports = port->device.multitap.ports;
243 } 256 }
244 257
245 const int gamepad_len = strlen("gamepad"); 258 const int gamepad_len = strlen("gamepad");
246 if (startswith(device_type, "gamepad")) 259 if (startswith(device_type, "gamepad"))
247 { 260 {
304 if (port->device_type != IO_GENERIC_SERIAL) { 317 if (port->device_type != IO_GENERIC_SERIAL) {
305 port->device_type = IO_GENERIC_SERIAL; 318 port->device_type = IO_GENERIC_SERIAL;
306 port->device.stream.data_fd = -1; 319 port->device.stream.data_fd = -1;
307 port->device.stream.listen_fd = -1; 320 port->device.stream.listen_fd = -1;
308 } 321 }
309 } 322 } else if(startswith(device_type, "sega_multitap.")) {
323 if (port->device_type != IO_SEGA_MULTI) {
324 port->device_type = IO_SEGA_MULTI;
325 port->device.multitap.ports = old_ports ? old_ports : calloc(4, sizeof(io_port));
326 port->device.multitap.tap_num = device_type[strlen("sega_multitap.")] - '0';
327 if (!old_ports) {
328 port->device.multitap.tr_counter = 0;
329 port->device.multitap.ready_cycle = CYCLE_NEVER;
330 port->input[0] = 0x13;
331 }
332 old_ports = NULL;
333 }
334 }
335 free(old_ports);
310 } 336 }
311 337
312 char * io_name(int i) 338 char * io_name(int i)
313 { 339 {
314 switch (i) 340 switch (i)
448 + 5 + 8; 474 + 5 + 8;
449 ports[i].device.heartbeat_trainer.nv_memory = malloc(bufsize); 475 ports[i].device.heartbeat_trainer.nv_memory = malloc(bufsize);
450 memset(ports[i].device.heartbeat_trainer.nv_memory, 0xFF, bufsize); 476 memset(ports[i].device.heartbeat_trainer.nv_memory, 0xFF, bufsize);
451 } 477 }
452 ports[i].device.heartbeat_trainer.state = HBPT_NEED_INIT; 478 ports[i].device.heartbeat_trainer.state = HBPT_NEED_INIT;
479 } else if (ports[i].device_type == IO_SEGA_MULTI) {
480 char path[] = "io\0sega_multitap.1\0";
481 path[17] = '0' + ports[i].device.multitap.tap_num;
482 tern_node *port_defs = tern_find_path(config, path, TVAL_NODE).ptrval;
483 debug_message("IO port %s connected to Sega multitap %d\n", io_name(i), ports[i].device.multitap.tap_num);
484 for (int j = 0; j < 4; j++)
485 {
486 char port_num[] = {'1' + j, 0, 0};
487 char *dev_type = tern_find_ptr(port_defs, port_num);
488 process_device(dev_type, ports[i].device.multitap.ports + j);
489 debug_message("\tTap port %d connected to device '%s'\n", j + 1, device_type_names[ports[i].device.multitap.ports[j].device_type]);
490 if (ports[i].control & ports[i].output & 0x40) {
491 io_control_write(ports[i].device.multitap.ports + j, 0x40, 0);
492 io_data_write(ports[i].device.multitap.ports + j, 0x40, 0);
493 }
494 }
495
453 } else { 496 } else {
454 debug_message("IO port %s connected to device '%s'\n", io_name(i), device_type_names[ports[i].device_type]); 497 debug_message("IO port %s connected to device '%s'\n", io_name(i), device_type_names[ports[i].device_type]);
455 } 498 }
456 } 499 }
457 } 500 }
458 501
459 502
460 #define TH 0x40 503 #define TH 0x40
461 #define TR 0x20 504 #define TR 0x20
505 #define TL 0x10
462 #define TH_TIMEOUT 56000 506 #define TH_TIMEOUT 56000
507 #define SLOW_RISE_DEVICE (30*7)
508 #define SLOW_RISE_INPUT (12*7)
509
510 static uint8_t get_output_value(io_port *port, uint32_t current_cycle, uint32_t slow_rise_delay)
511 {
512 uint8_t output = (port->control | 0x80) & port->output;
513 for (int i = 0; i < 8; i++)
514 {
515 if (!(port->control & 1 << i)) {
516 if (port->slow_rise_start[i] != CYCLE_NEVER) {
517 if (current_cycle - port->slow_rise_start[i] >= slow_rise_delay) {
518 output |= 1 << i;
519 }
520 } else {
521 output |= 1 << i;
522 }
523 }
524 }
525 return output;
526 }
463 527
464 void mouse_check_ready(io_port *port, uint32_t current_cycle) 528 void mouse_check_ready(io_port *port, uint32_t current_cycle)
465 { 529 {
466 if (current_cycle >= port->device.mouse.ready_cycle) { 530 if (current_cycle >= port->device.mouse.ready_cycle) {
467 port->device.mouse.tr_counter++; 531 port->device.mouse.tr_counter++;
483 }*/ 547 }*/
484 } 548 }
485 } 549 }
486 } 550 }
487 551
552 void multitap_check_ready(io_port *port, uint32_t current_cycle)
553 {
554 if (current_cycle >= port->device.multitap.ready_cycle) {
555 if (port->device.multitap.reset_state) {
556 uint8_t output = get_output_value(port, current_cycle, SLOW_RISE_DEVICE);
557 if (output & TR) {
558 port->input[0] |= TL;
559 port->device.multitap.reset_state = 0;
560 } else {
561 port->input[0] &= TR;
562 }
563 port->device.multitap.ready_cycle = CYCLE_NEVER;
564 return;
565 }
566 port->device.multitap.tr_counter++;
567 port->device.multitap.ready_cycle = CYCLE_NEVER;
568 switch (port->device.multitap.tr_counter)
569 {
570 case 1:
571 for (int i = 0; i < 4; i++)
572 {
573 uint8_t id = io_data_read(port->device.multitap.ports + i, current_cycle);
574 io_data_write(port->device.multitap.ports + i, 0, current_cycle);
575 uint8_t value = io_data_read(port->device.multitap.ports + i, current_cycle);
576 uint8_t pad_data = (id & 0x3F) | (value << 2 & 0xC0);
577 id = (id & 0xA) | (id << 1 & 0xA);
578 id |= (value & 0x5) | (value >> 1 & 0x5);
579 id = (id & 0x9) | (id << 1 & 0x4) | (id >> 1 & 0x2);
580 if (id == 0xD || id == 0xC) {
581 port->device.multitap.data[i] = pad_data;
582 io_data_write(port->device.multitap.ports + i, 0x40, current_cycle);
583 } else if (id == 0x3) {
584 //set TR to output for mouse
585 io_control_write(port->device.multitap.ports + i, 0x60, current_cycle);
586 }
587 port->device.multitap.device_ids[i] = id;
588 }
589 port->input[0] = 0;
590 break;
591 case 2:
592 for (int i = 0; i < 4; i++)
593 {
594 if (port->device.multitap.device_ids[i] == 0xC || port->device.multitap.device_ids[i] == 0xD) {
595 io_data_write(port->device.multitap.ports + i, 0, current_cycle);
596 } else if (port->device.multitap.device_ids[i] == 0x3) {
597 //TODO: Fix delays so mouse has enough time to respond
598 io_data_write(port->device.multitap.ports + i, 0x20, current_cycle);
599 }
600 }
601 port->input[0] = 0x10;
602 break;
603 case 3:
604 for (int i = 0; i < 4; i++)
605 {
606 if (port->device.multitap.device_ids[i] == 0xC || port->device.multitap.device_ids[i] == 0xD) {
607 io_data_write(port->device.multitap.ports + i, 0x40, current_cycle);
608 io_data_write(port->device.multitap.ports + i, 0, current_cycle);
609 uint8_t value = io_data_read(port->device.multitap.ports + i, current_cycle);
610 if (value & 0xF) {
611 //3 button
612 port->device.multitap.device_ids[i] = 0;
613 } else {
614 port->device.multitap.device_ids[i] = 1;
615 }
616 } else if (port->device.multitap.device_ids[i] == 0xC) {
617 //TODO: Fix delays so mouse has enough time to respond
618 io_data_write(port->device.multitap.ports + i, 0, current_cycle);
619 port->device.multitap.device_ids[i] = 2;
620 } else {
621 port->device.multitap.device_ids[i] = 0xF;
622 }
623 }
624 port->input[0] = port->device.multitap.device_ids[0];
625 break;
626 case 4:
627 port->input[0] = 0x10 | port->device.multitap.device_ids[1];
628 break;
629 case 5:
630 port->input[0] = port->device.multitap.device_ids[2];
631 break;
632 case 6:
633 port->input[0] = 0x10 | port->device.multitap.device_ids[3];
634 port->device.multitap.cur_port = 0;
635 port->device.multitap.port_start = 7;
636 break;
637 default: {
638 port->input[0] = (port->input[0] & ~TL) | ((~port->input[0]) & TL);
639 uint8_t tr_diff = port->device.multitap.tr_counter - port->device.multitap.port_start;
640 for (;;)
641 {
642 if (port->device.multitap.cur_port > 3) {
643 return;
644 }
645 switch (port->device.multitap.device_ids[port->device.multitap.cur_port])
646 {
647 case 0:
648 if (tr_diff) {
649 port->input[0] = (port->input[0] & 0xF0) | (port->device.multitap.data[port->device.multitap.cur_port] >> 4);
650 port->device.multitap.cur_port++;
651 port->device.multitap.port_start = port->device.multitap.tr_counter + 1;
652 } else {
653 port->input[0] = (port->input[0] & 0xF0) | (port->device.multitap.data[port->device.multitap.cur_port] & 0xF);
654 }
655 return;
656 case 1:
657 if (tr_diff == 2) {
658 uint8_t value = io_data_read(port->device.multitap.ports + port->device.multitap.cur_port, current_cycle);
659 port->input[0] = (port->input[0] & 0xF0) | (value & 0xF);
660 //finish 6-button cycle
661 io_data_write(port->device.multitap.ports + port->device.multitap.cur_port, 0, current_cycle);
662 port->device.multitap.cur_port++;
663 port->device.multitap.port_start = port->device.multitap.tr_counter + 1;
664 } else if (tr_diff) {
665 io_data_write(port->device.multitap.ports + port->device.multitap.cur_port, 0x40, current_cycle);
666 port->input[0] = (port->input[0] & 0xF0) | (port->device.multitap.data[port->device.multitap.cur_port] >> 4);
667 } else {
668 port->input[0] = (port->input[0] & 0xF0) | (port->device.multitap.data[port->device.multitap.cur_port] & 0xF);
669 }
670 return;
671 case 2: {
672 io_port *dst_port = port->device.multitap.ports + port->device.multitap.cur_port;
673 uint8_t value = io_data_read(dst_port, current_cycle);
674 io_data_write(dst_port, (~dst_port->output) & TR, current_cycle);
675 if (tr_diff == 5) {
676 port->device.multitap.cur_port++;
677 port->device.multitap.port_start = port->device.multitap.tr_counter + 1;
678 }
679 return;
680 }
681 default:
682 port->device.multitap.cur_port++;
683 port->device.multitap.port_start = port->device.multitap.tr_counter;
684 }
685 }
686 }
687 }
688 }
689 }
690
488 void io_adjust_cycles(io_port * port, uint32_t current_cycle, uint32_t deduction) 691 void io_adjust_cycles(io_port * port, uint32_t current_cycle, uint32_t deduction)
489 { 692 {
490 /*uint8_t control = pad->control | 0x80; 693 /*uint8_t control = pad->control | 0x80;
491 uint8_t th = control & pad->output; 694 uint8_t th = control & pad->output;
492 if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) { 695 if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) {
502 } 705 }
503 } else if (port->device_type == IO_MOUSE) { 706 } else if (port->device_type == IO_MOUSE) {
504 mouse_check_ready(port, current_cycle); 707 mouse_check_ready(port, current_cycle);
505 if (port->device.mouse.ready_cycle != CYCLE_NEVER) { 708 if (port->device.mouse.ready_cycle != CYCLE_NEVER) {
506 port->device.mouse.ready_cycle -= deduction; 709 port->device.mouse.ready_cycle -= deduction;
710 }
711 } else if (port->device_type == IO_SEGA_MULTI) {
712 multitap_check_ready(port, current_cycle);
713 if (port->device.multitap.ready_cycle != CYCLE_NEVER) {
714 port->device.multitap.ready_cycle -= deduction;
715 }
716 for (int i = 0; i < 4; i++)
717 {
718 io_adjust_cycles(port->device.multitap.ports + i, current_cycle, deduction);
507 } 719 }
508 } 720 }
509 for (int i = 0; i < 8; i++) 721 for (int i = 0; i < 8; i++)
510 { 722 {
511 if (port->slow_rise_start[i] != CYCLE_NEVER) { 723 if (port->slow_rise_start[i] != CYCLE_NEVER) {
1019 } 1231 }
1020 } 1232 }
1021 1233
1022 void io_data_write(io_port * port, uint8_t value, uint32_t current_cycle) 1234 void io_data_write(io_port * port, uint8_t value, uint32_t current_cycle)
1023 { 1235 {
1024 uint8_t old_output = (port->control & port->output) | (~port->control & 0xFF); 1236 uint8_t old_output = get_output_value(port, current_cycle, SLOW_RISE_DEVICE);
1025 uint8_t output = (port->control & value) | (~port->control & 0xFF); 1237 port->output = value;
1238 uint8_t output = get_output_value(port, current_cycle, SLOW_RISE_DEVICE);
1026 switch (port->device_type) 1239 switch (port->device_type)
1027 { 1240 {
1028 case IO_GAMEPAD6: 1241 case IO_GAMEPAD6:
1029 //check if TH has changed 1242 //check if TH has changed
1030 if ((old_output & TH) ^ (output & TH)) { 1243 if ((old_output & TH) ^ (output & TH)) {
1133 } 1346 }
1134 } 1347 }
1135 } 1348 }
1136 } 1349 }
1137 break; 1350 break;
1351 case IO_SEGA_MULTI:
1352 multitap_check_ready(port, current_cycle);
1353 if (output & TH) {
1354 //request is over
1355 port->device.multitap.tr_counter = 0;
1356 if ((output & TR) != (old_output & TR)) {
1357 port->device.multitap.ready_cycle = current_cycle + 16 * 7;
1358 port->device.multitap.reset_state = 1;
1359 } else if (!port->device.multitap.reset_state) {
1360 port->device.multitap.ready_cycle = CYCLE_NEVER;
1361 port->input[0] = 0x13;
1362 }
1363 for (int i = 0; i < 4; i++)
1364 {
1365 io_control_write(port->device.multitap.ports + i, 0x40, current_cycle);
1366 io_data_write(port->device.multitap.ports + i, 0x40, current_cycle);
1367 }
1368 } else {
1369 if (old_output & TH) {
1370 port->input[0] = 0x1F;
1371 }
1372 if ((output & TR) != (old_output & TR)) {
1373 //TODO: measure actual delays
1374 port->device.multitap.ready_cycle = current_cycle + 16 * 7;
1375 }
1376 }
1377 break;
1138 #ifndef _WIN32 1378 #ifndef _WIN32
1139 case IO_GENERIC: 1379 case IO_GENERIC:
1140 wait_for_connection(port); 1380 wait_for_connection(port);
1141 port->input[IO_STATE] = IO_WRITE_PENDING; 1381 port->input[IO_STATE] = IO_WRITE_PENDING;
1142 service_socket(port); 1382 service_socket(port);
1172 read_pos++; 1412 read_pos++;
1173 read_pos &= 7; 1413 read_pos &= 7;
1174 } while (read_pos != port->device.keyboard.write_pos); 1414 } while (read_pos != port->device.keyboard.write_pos);
1175 1415
1176 return bytes; 1416 return bytes;
1177 }
1178
1179 #define SLOW_RISE_DEVICE (30*7)
1180 #define SLOW_RISE_INPUT (12*7)
1181
1182 static uint8_t get_output_value(io_port *port, uint32_t current_cycle, uint32_t slow_rise_delay)
1183 {
1184 uint8_t output = (port->control | 0x80) & port->output;
1185 for (int i = 0; i < 8; i++)
1186 {
1187 if (!(port->control & 1 << i)) {
1188 if (port->slow_rise_start[i] != CYCLE_NEVER) {
1189 if (current_cycle - port->slow_rise_start[i] >= slow_rise_delay) {
1190 output |= 1 << i;
1191 }
1192 } else {
1193 output |= 1 << i;
1194 }
1195 }
1196 }
1197 return output;
1198 } 1417 }
1199 1418
1200 uint8_t io_data_read(io_port * port, uint32_t current_cycle) 1419 uint8_t io_data_read(io_port * port, uint32_t current_cycle)
1201 { 1420 {
1202 uint8_t output = get_output_value(port, current_cycle, SLOW_RISE_DEVICE); 1421 uint8_t output = get_output_value(port, current_cycle, SLOW_RISE_DEVICE);
1448 } 1667 }
1449 //this is not strictly correct at all times, but good enough for now 1668 //this is not strictly correct at all times, but good enough for now
1450 device_driven = 0x1F; 1669 device_driven = 0x1F;
1451 break; 1670 break;
1452 } 1671 }
1672 case IO_SEGA_MULTI:
1673 multitap_check_ready(port, current_cycle);
1674 device_driven = 0x1F;
1675 input = port->input[0];
1676 break;
1453 #ifndef _WIN32 1677 #ifndef _WIN32
1454 case IO_SEGA_PARALLEL: 1678 case IO_SEGA_PARALLEL:
1455 if (!th) 1679 if (!th)
1456 { 1680 {
1457 service_pipe(port); 1681 service_pipe(port);