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