Mercurial > repos > blastem
comparison io.c @ 645:d77c79cec800
Initial support for configurable IO, custom IO and sega transfer board emulation
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Wed, 03 Dec 2014 09:32:32 -0800 |
parents | 7b0df1aaf384 |
children | 7497334bb548 |
comparison
equal
deleted
inserted
replaced
644:2d7e84ae818c | 645:d77c79cec800 |
---|---|
1 /* | 1 /* |
2 Copyright 2013 Michael Pavone | 2 Copyright 2013 Michael Pavone |
3 This file is part of BlastEm. | 3 This file is part of BlastEm. |
4 BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. | 4 BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. |
5 */ | 5 */ |
6 #include <unistd.h> | |
7 #include <fcntl.h> | |
8 #include <sys/socket.h> | |
9 #include <sys/un.h> | |
10 #include <sys/types.h> | |
11 #include <sys/stat.h> | |
12 #include <errno.h> | |
13 #include <string.h> | |
14 | |
6 #include "io.h" | 15 #include "io.h" |
7 #include "blastem.h" | 16 #include "blastem.h" |
8 #include "render.h" | 17 #include "render.h" |
9 | 18 |
19 const char * device_type_names[] = { | |
20 "3-button gamepad", | |
21 "6-button gamepad", | |
22 "Mega Mouse", | |
23 "Menacer", | |
24 "Justifier", | |
25 "Sega multi-tap", | |
26 "EA 4-way Play cable A", | |
27 "EA 4-way Play cable B", | |
28 "Sega Parallel Transfer Board", | |
29 "Generic Device", | |
30 "None" | |
31 }; | |
32 | |
10 enum { | 33 enum { |
11 BIND_NONE, | 34 BIND_NONE, |
35 BIND_UI, | |
12 BIND_GAMEPAD1, | 36 BIND_GAMEPAD1, |
13 BIND_GAMEPAD2, | 37 BIND_GAMEPAD2, |
14 BIND_UI | 38 BIND_GAMEPAD3, |
39 BIND_GAMEPAD4, | |
40 BIND_GAMEPAD5, | |
41 BIND_GAMEPAD6, | |
42 BIND_GAMEPAD7, | |
43 BIND_GAMEPAD8 | |
15 }; | 44 }; |
16 | 45 |
17 typedef enum { | 46 typedef enum { |
18 UI_DEBUG_MODE_INC, | 47 UI_DEBUG_MODE_INC, |
19 UI_DEBUG_PAL_INC, | 48 UI_DEBUG_PAL_INC, |
24 UI_PREV_SPEED, | 53 UI_PREV_SPEED, |
25 UI_EXIT | 54 UI_EXIT |
26 } ui_action; | 55 } ui_action; |
27 | 56 |
28 typedef struct { | 57 typedef struct { |
58 io_port *port; | |
29 uint8_t bind_type; | 59 uint8_t bind_type; |
30 uint8_t subtype_a; | 60 uint8_t subtype_a; |
31 uint8_t subtype_b; | 61 uint8_t subtype_b; |
32 uint8_t value; | 62 uint8_t value; |
33 } keybinding; | 63 } keybinding; |
115 #define BUTTON_C GAMEPAD_BUTTON(GAMEPAD_TH1, GAMEPAD_NONE, 0x20) | 145 #define BUTTON_C GAMEPAD_BUTTON(GAMEPAD_TH1, GAMEPAD_NONE, 0x20) |
116 | 146 |
117 void bind_gamepad(int keycode, int gamepadnum, int button) | 147 void bind_gamepad(int keycode, int gamepadnum, int button) |
118 { | 148 { |
119 | 149 |
120 if (gamepadnum < 1 || gamepadnum > 2) { | 150 if (gamepadnum < 1 || gamepadnum > 8) { |
121 return; | 151 return; |
122 } | 152 } |
123 uint8_t bind_type = gamepadnum - 1 + BIND_GAMEPAD1; | 153 uint8_t bind_type = gamepadnum - 1 + BIND_GAMEPAD1; |
124 bind_key(keycode, bind_type, button >> 12, button >> 8 & 0xF, button & 0xFF); | 154 bind_key(keycode, bind_type, button >> 12, button >> 8 & 0xF, button & 0xFF); |
125 } | 155 } |
126 | 156 |
127 void bind_button_gamepad(int joystick, int joybutton, int gamepadnum, int padbutton) | 157 void bind_button_gamepad(int joystick, int joybutton, int gamepadnum, int padbutton) |
128 { | 158 { |
129 if (gamepadnum < 1 || gamepadnum > 2) { | 159 if (gamepadnum < 1 || gamepadnum > 8) { |
130 return; | 160 return; |
131 } | 161 } |
132 uint8_t bind_type = gamepadnum - 1 + BIND_GAMEPAD1; | 162 uint8_t bind_type = gamepadnum - 1 + BIND_GAMEPAD1; |
133 bind_button(joystick, joybutton, bind_type, padbutton >> 12, padbutton >> 8 & 0xF, padbutton & 0xFF); | 163 bind_button(joystick, joybutton, bind_type, padbutton >> 12, padbutton >> 8 & 0xF, padbutton & 0xFF); |
134 } | 164 } |
135 | 165 |
136 void bind_dpad_gamepad(int joystick, int dpad, uint8_t direction, int gamepadnum, int button) | 166 void bind_dpad_gamepad(int joystick, int dpad, uint8_t direction, int gamepadnum, int button) |
137 { | 167 { |
138 if (gamepadnum < 1 || gamepadnum > 2) { | 168 if (gamepadnum < 1 || gamepadnum > 8) { |
139 return; | 169 return; |
140 } | 170 } |
141 uint8_t bind_type = gamepadnum - 1 + BIND_GAMEPAD1; | 171 uint8_t bind_type = gamepadnum - 1 + BIND_GAMEPAD1; |
142 bind_dpad(joystick, dpad, direction, bind_type, button >> 12, button >> 8 & 0xF, button & 0xFF); | 172 bind_dpad(joystick, dpad, direction, bind_type, button >> 12, button >> 8 & 0xF, button & 0xFF); |
143 } | 173 } |
157 bind_dpad(joystick, dpad, direction, BIND_UI, action, 0, param); | 187 bind_dpad(joystick, dpad, direction, BIND_UI, action, 0, param); |
158 } | 188 } |
159 | 189 |
160 void handle_binding_down(keybinding * binding) | 190 void handle_binding_down(keybinding * binding) |
161 { | 191 { |
162 switch(binding->bind_type) | 192 if (binding->bind_type >= BIND_GAMEPAD1) |
163 { | 193 { |
164 case BIND_GAMEPAD1: | 194 if (binding->subtype_a <= GAMEPAD_EXTRA && binding->port) { |
165 case BIND_GAMEPAD2: | 195 binding->port->input[binding->subtype_a] |= binding->value; |
166 if (binding->subtype_a <= GAMEPAD_EXTRA) { | 196 } |
167 genesis->ports[binding->bind_type - BIND_GAMEPAD1].input[binding->subtype_a] |= binding->value; | 197 if (binding->subtype_b <= GAMEPAD_EXTRA && binding->port) { |
168 } | 198 binding->port->input[binding->subtype_b] |= binding->value; |
169 if (binding->subtype_b <= GAMEPAD_EXTRA) { | 199 } |
170 genesis->ports[binding->bind_type - BIND_GAMEPAD1].input[binding->subtype_b] |= binding->value; | |
171 } | |
172 break; | |
173 } | 200 } |
174 } | 201 } |
175 | 202 |
176 void handle_keydown(int keycode) | 203 void handle_keydown(int keycode) |
177 { | 204 { |
204 { | 231 { |
205 switch(binding->bind_type) | 232 switch(binding->bind_type) |
206 { | 233 { |
207 case BIND_GAMEPAD1: | 234 case BIND_GAMEPAD1: |
208 case BIND_GAMEPAD2: | 235 case BIND_GAMEPAD2: |
209 if (binding->subtype_a <= GAMEPAD_EXTRA) { | 236 if (binding->subtype_a <= GAMEPAD_EXTRA && binding->port) { |
210 genesis->ports[binding->bind_type - BIND_GAMEPAD1].input[binding->subtype_a] &= ~binding->value; | 237 binding->port->input[binding->subtype_a] &= ~binding->value; |
211 } | 238 } |
212 if (binding->subtype_b <= GAMEPAD_EXTRA) { | 239 if (binding->subtype_b <= GAMEPAD_EXTRA && binding->port) { |
213 genesis->ports[binding->bind_type - BIND_GAMEPAD1].input[binding->subtype_b] &= ~binding->value; | 240 binding->port->input[binding->subtype_b] &= ~binding->value; |
214 } | 241 } |
215 break; | 242 break; |
216 case BIND_UI: | 243 case BIND_UI: |
217 switch (binding->subtype_a) | 244 switch (binding->subtype_a) |
218 { | 245 { |
445 if (curstr && len) { | 472 if (curstr && len) { |
446 free(curstr); | 473 free(curstr); |
447 } | 474 } |
448 } | 475 } |
449 | 476 |
450 void set_keybindings() | 477 void process_device(char * device_type, io_port * port) |
478 { | |
479 port->device_type = IO_NONE; | |
480 if (!device_type) | |
481 { | |
482 return; | |
483 } | |
484 | |
485 const int gamepad_len = strlen("gamepad"); | |
486 if (!memcmp(device_type, "gamepad", gamepad_len)) | |
487 { | |
488 if ( | |
489 (device_type[gamepad_len] != '3' && device_type[gamepad_len] != '6') | |
490 || device_type[gamepad_len+1] != '.' || device_type[gamepad_len+2] < '1' | |
491 || device_type[gamepad_len+2] > '8' || device_type[gamepad_len+3] != 0 | |
492 ) | |
493 { | |
494 fprintf(stderr, "%s is not a valid gamepad type\n", device_type); | |
495 } else if (device_type[gamepad_len] == '3') | |
496 { | |
497 port->device_type = IO_GAMEPAD3; | |
498 } else { | |
499 port->device_type = IO_GAMEPAD6; | |
500 } | |
501 port->device.pad.gamepad_num = device_type[gamepad_len+2] - '1'; | |
502 } else if(!strcmp(device_type, "sega_parallel")) { | |
503 port->device_type = IO_SEGA_PARALLEL; | |
504 port->device.stream.data_fd = -1; | |
505 port->device.stream.listen_fd = -1; | |
506 } else if(!strcmp(device_type, "generic")) { | |
507 port->device_type = IO_GENERIC; | |
508 port->device.stream.data_fd = -1; | |
509 port->device.stream.listen_fd = -1; | |
510 } | |
511 } | |
512 | |
513 char * io_name(int i) | |
514 { | |
515 switch (i) | |
516 { | |
517 case 0: | |
518 return "1"; | |
519 case 1: | |
520 return "2"; | |
521 case 2: | |
522 return "EXT"; | |
523 default: | |
524 return "invalid"; | |
525 } | |
526 } | |
527 | |
528 static char * sockfile_name; | |
529 static void cleanup_sockfile() | |
530 { | |
531 unlink(sockfile_name); | |
532 } | |
533 | |
534 void setup_io_devices(tern_node * config, io_port * ports) | |
535 { | |
536 tern_node *io_nodes = tern_find_prefix(config, "iodevices"); | |
537 char * io_1 = tern_find_ptr(io_nodes, "1"); | |
538 char * io_2 = tern_find_ptr(io_nodes, "2"); | |
539 char * io_ext = tern_find_ptr(io_nodes, "ext"); | |
540 | |
541 process_device(io_1, ports); | |
542 process_device(io_2, ports+1); | |
543 process_device(io_ext, ports+2); | |
544 | |
545 for (int i = 0; i < 3; i++) | |
546 { | |
547 | |
548 if (ports[i].device_type == IO_SEGA_PARALLEL) | |
549 { | |
550 char *pipe_name = tern_find_ptr(config, "ioparallel_pipe"); | |
551 if (!pipe_name) | |
552 { | |
553 fprintf(stderr, "IO port %s is configured to use the sega parallel board, but no paralell_pipe is set!\n", io_name(i)); | |
554 ports[i].device_type = IO_NONE; | |
555 } else { | |
556 printf("IO port: %s connected to device '%s' with pipe name: %s\n", io_name(i), device_type_names[ports[i].device_type], pipe_name); | |
557 if (!strcmp("stdin", pipe_name)) | |
558 { | |
559 ports[i].device.stream.data_fd = STDIN_FILENO; | |
560 } else { | |
561 if (mkfifo(pipe_name, 0666) && errno != EEXIST) | |
562 { | |
563 fprintf(stderr, "Failed to create fifo %s for Sega parallel board emulation: %d %s\n", pipe_name, errno, strerror(errno)); | |
564 ports[i].device_type = IO_NONE; | |
565 } else { | |
566 ports[i].device.stream.data_fd = open(pipe_name, O_NONBLOCK | O_RDONLY); | |
567 if (ports[i].device.stream.data_fd == -1) | |
568 { | |
569 fprintf(stderr, "Failed to open fifo %s for Sega parallel board emulation: %d %s\n", pipe_name, errno, strerror(errno)); | |
570 ports[i].device_type = IO_NONE; | |
571 } | |
572 } | |
573 } | |
574 } | |
575 } else if (ports[i].device_type == IO_GENERIC) { | |
576 char *sock_name = tern_find_ptr(config, "iosocket"); | |
577 if (!sock_name) | |
578 { | |
579 fprintf(stderr, "IO port %s is configured to use generic IO, but no socket is set!\n", io_name(i)); | |
580 ports[i].device_type = IO_NONE; | |
581 } else { | |
582 printf("IO port: %s connected to device '%s' with socket name: %s\n", io_name(i), device_type_names[ports[i].device_type], sock_name); | |
583 ports[i].device.stream.data_fd = -1; | |
584 ports[i].device.stream.listen_fd = socket(AF_UNIX, SOCK_STREAM, 0); | |
585 size_t pathlen = strlen(sock_name); | |
586 size_t addrlen = offsetof(struct sockaddr_un, sun_path) + pathlen + 1; | |
587 struct sockaddr_un *saddr = malloc(addrlen); | |
588 saddr->sun_family = AF_UNIX; | |
589 memcpy(saddr->sun_path, sock_name, pathlen+1); | |
590 if (bind(ports[i].device.stream.listen_fd, (struct sockaddr *)saddr, addrlen)) | |
591 { | |
592 fprintf(stderr, "Failed to bind socket for IO Port %s to path %s: %d %s\n", io_name(i), sock_name, errno, strerror(errno)); | |
593 goto cleanup_sock; | |
594 } | |
595 if (listen(ports[i].device.stream.listen_fd, 1)) | |
596 { | |
597 fprintf(stderr, "Failed to listen on socket for IO Port %s: %d %s\n", io_name(i), errno, strerror(errno)); | |
598 goto cleanup_sockfile; | |
599 } | |
600 sockfile_name = sock_name; | |
601 atexit(cleanup_sockfile); | |
602 continue; | |
603 cleanup_sockfile: | |
604 unlink(sock_name); | |
605 cleanup_sock: | |
606 close(ports[i].device.stream.listen_fd); | |
607 ports[i].device_type = IO_NONE; | |
608 } | |
609 } else if (ports[i].device_type == IO_GAMEPAD3 || ports[i].device_type == IO_GAMEPAD6) { | |
610 printf("IO port %s connected to gamepad #%d with type '%s'\n", io_name(i), ports[i].device.pad.gamepad_num + 1, device_type_names[ports[i].device_type]); | |
611 } else { | |
612 printf("IO port %s connected to device '%s'\n", io_name(i), device_type_names[ports[i].device_type]); | |
613 } | |
614 } | |
615 } | |
616 | |
617 void map_bindings(io_port *ports, keybinding *bindings, int numbindings) | |
618 { | |
619 for (int i = 0; i < numbindings; i++) | |
620 { | |
621 if (bindings[i].bind_type >= BIND_GAMEPAD1) | |
622 { | |
623 int num = bindings[i].bind_type - BIND_GAMEPAD1; | |
624 for (int j = 0; j < 3; j++) | |
625 { | |
626 if ((ports[j].device_type == IO_GAMEPAD3 | |
627 || ports[j].device_type ==IO_GAMEPAD6) | |
628 && ports[j].device.pad.gamepad_num == num | |
629 ) | |
630 { | |
631 bindings[i].port = ports + j; | |
632 break; | |
633 } | |
634 } | |
635 } | |
636 } | |
637 } | |
638 | |
639 void set_keybindings(io_port *ports) | |
451 { | 640 { |
452 tern_node * special = tern_insert_int(NULL, "up", RENDERKEY_UP); | 641 tern_node * special = tern_insert_int(NULL, "up", RENDERKEY_UP); |
453 special = tern_insert_int(special, "down", RENDERKEY_DOWN); | 642 special = tern_insert_int(special, "down", RENDERKEY_DOWN); |
454 special = tern_insert_int(special, "left", RENDERKEY_LEFT); | 643 special = tern_insert_int(special, "left", RENDERKEY_LEFT); |
455 special = tern_insert_int(special, "right", RENDERKEY_RIGHT); | 644 special = tern_insert_int(special, "right", RENDERKEY_RIGHT); |
530 } | 719 } |
531 tern_node * speed_nodes = tern_find_prefix(config, "clocksspeeds"); | 720 tern_node * speed_nodes = tern_find_prefix(config, "clocksspeeds"); |
532 speeds = malloc(sizeof(uint32_t)); | 721 speeds = malloc(sizeof(uint32_t)); |
533 speeds[0] = 100; | 722 speeds[0] = 100; |
534 process_speeds(speed_nodes, NULL); | 723 process_speeds(speed_nodes, NULL); |
535 for (int i = 0; i < num_speeds; i++) { | 724 for (int i = 0; i < num_speeds; i++) |
725 { | |
536 if (!speeds[i]) { | 726 if (!speeds[i]) { |
537 fprintf(stderr, "Speed index %d was not set to a valid percentage!", i); | 727 fprintf(stderr, "Speed index %d was not set to a valid percentage!", i); |
538 speeds[i] = 100; | 728 speeds[i] = 100; |
539 } | 729 } |
540 } | 730 } |
731 for (int bucket = 0; bucket < 256; bucket++) | |
732 { | |
733 if (bindings[bucket]) | |
734 { | |
735 map_bindings(ports, bindings[bucket], 256); | |
736 } | |
737 } | |
738 for (int stick = 0; stick < MAX_JOYSTICKS; stick++) | |
739 { | |
740 if (joybindings[stick]) | |
741 { | |
742 int numbuttons = render_joystick_num_buttons(stick); | |
743 map_bindings(ports, joybindings[stick], render_joystick_num_buttons(stick)); | |
744 } | |
745 if (joydpads[stick]) | |
746 { | |
747 map_bindings(ports, joydpads[stick]->bindings, 4); | |
748 } | |
749 } | |
541 } | 750 } |
542 | 751 |
543 #define TH 0x40 | 752 #define TH 0x40 |
544 #define TH_TIMEOUT 8000 | 753 #define TH_TIMEOUT 8000 |
545 | 754 |
546 void io_adjust_cycles(io_port * pad, uint32_t current_cycle, uint32_t deduction) | 755 void io_adjust_cycles(io_port * port, uint32_t current_cycle, uint32_t deduction) |
547 { | 756 { |
548 /*uint8_t control = pad->control | 0x80; | 757 /*uint8_t control = pad->control | 0x80; |
549 uint8_t th = control & pad->output; | 758 uint8_t th = control & pad->output; |
550 if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) { | 759 if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) { |
551 printf("adjust_cycles | control: %X, TH: %X, GAMEPAD_TH0: %X, GAMEPAD_TH1: %X, TH Counter: %d, Timeout: %d, Cycle: %d\n", control, th, pad->input[GAMEPAD_TH0], pad->input[GAMEPAD_TH1], pad->th_counter,pad->timeout_cycle, current_cycle); | 760 printf("adjust_cycles | control: %X, TH: %X, GAMEPAD_TH0: %X, GAMEPAD_TH1: %X, TH Counter: %d, Timeout: %d, Cycle: %d\n", control, th, pad->input[GAMEPAD_TH0], pad->input[GAMEPAD_TH1], pad->th_counter,pad->timeout_cycle, current_cycle); |
552 }*/ | 761 }*/ |
553 if (current_cycle >= pad->timeout_cycle) { | 762 if (port->device_type == IO_GAMEPAD6) |
554 pad->th_counter = 0; | 763 { |
555 } else { | 764 if (current_cycle >= port->device.pad.timeout_cycle) |
556 pad->timeout_cycle -= deduction; | 765 { |
557 } | 766 port->device.pad.th_counter = 0; |
558 } | 767 } else { |
559 | 768 port->device.pad.timeout_cycle -= deduction; |
560 void io_data_write(io_port * pad, uint8_t value, uint32_t current_cycle) | 769 } |
561 { | 770 } |
562 if (pad->control & TH) { | 771 } |
563 //check if TH has changed | 772 |
564 if ((pad->output & TH) ^ (value & TH)) { | 773 static void wait_for_connection(io_port * port) |
565 if (current_cycle >= pad->timeout_cycle) { | 774 { |
566 pad->th_counter = 0; | 775 if (port->device.stream.data_fd == -1) |
567 } | 776 { |
568 if (!(value & TH)) { | 777 puts("Waiting for socket connection..."); |
569 pad->th_counter++; | 778 port->device.stream.data_fd = accept(port->device.stream.listen_fd, NULL, NULL); |
570 } | 779 fcntl(port->device.stream.data_fd, F_SETFL, O_NONBLOCK | O_RDWR); |
571 pad->timeout_cycle = current_cycle + TH_TIMEOUT; | 780 } |
572 } | 781 } |
573 } | 782 |
574 pad->output = value; | 783 static void service_pipe(io_port * port) |
575 } | 784 { |
576 | 785 uint8_t value; |
577 uint8_t io_data_read(io_port * pad, uint32_t current_cycle) | 786 int numRead = read(port->device.stream.data_fd, &value, sizeof(value)); |
578 { | 787 if (numRead > 0) |
579 uint8_t control = pad->control | 0x80; | 788 { |
580 uint8_t th = control & pad->output; | 789 port->input[IO_TH0] = (value & 0xF) | 0x10; |
790 port->input[IO_TH1] = (value >> 4) | 0x10; | |
791 } else if(numRead == -1 && errno != EAGAIN && errno != EWOULDBLOCK) { | |
792 fprintf(stderr, "Error reading pipe for IO port: %d %s\n", errno, strerror(errno)); | |
793 } | |
794 } | |
795 | |
796 static void service_socket(io_port *port) | |
797 { | |
798 uint8_t buf[32]; | |
799 uint8_t blocking = 0; | |
800 int numRead = 0; | |
801 while (numRead <= 0) | |
802 { | |
803 numRead = recv(port->device.stream.data_fd, buf, sizeof(buf), 0); | |
804 if (numRead > 0) | |
805 { | |
806 port->input[IO_TH0] = buf[numRead-1]; | |
807 if (port->input[IO_STATE] == IO_READ_PENDING) | |
808 { | |
809 port->input[IO_STATE] = IO_READ; | |
810 if (blocking) | |
811 { | |
812 //pending read satisfied, back to non-blocking mode | |
813 fcntl(port->device.stream.data_fd, F_SETFL, O_RDWR | O_NONBLOCK); | |
814 } | |
815 } else if (port->input[IO_STATE] == IO_WRITTEN) { | |
816 port->input[IO_STATE] = IO_READ; | |
817 } | |
818 } else if (numRead == 0) { | |
819 port->device.stream.data_fd = -1; | |
820 wait_for_connection(port); | |
821 } else if (errno != EAGAIN && errno != EWOULDBLOCK) { | |
822 fprintf(stderr, "Error reading from socket for IO port: %d %s\n", errno, strerror(errno)); | |
823 close(port->device.stream.data_fd); | |
824 wait_for_connection(port); | |
825 } else if (port->input[IO_STATE] == IO_READ_PENDING) { | |
826 //clear the nonblocking flag so the next read will block | |
827 if (!blocking) | |
828 { | |
829 fcntl(port->device.stream.data_fd, F_SETFL, O_RDWR); | |
830 blocking = 1; | |
831 } | |
832 } else { | |
833 //no new data, but that's ok | |
834 break; | |
835 } | |
836 } | |
837 | |
838 if (port->input[IO_STATE] == IO_WRITE_PENDING) | |
839 { | |
840 uint8_t value = port->output & port->control; | |
841 int written = 0; | |
842 blocking = 0; | |
843 while (written <= 0) | |
844 { | |
845 send(port->device.stream.data_fd, &value, sizeof(value), 0); | |
846 if (written > 0) | |
847 { | |
848 port->input[IO_STATE] = IO_WRITTEN; | |
849 if (blocking) | |
850 { | |
851 //pending write satisfied, back to non-blocking mode | |
852 fcntl(port->device.stream.data_fd, F_SETFL, O_RDWR | O_NONBLOCK); | |
853 } | |
854 } else if (written == 0) { | |
855 port->device.stream.data_fd = -1; | |
856 wait_for_connection(port); | |
857 } else if (errno != EAGAIN && errno != EWOULDBLOCK) { | |
858 fprintf(stderr, "Error writing to socket for IO port: %d %s\n", errno, strerror(errno)); | |
859 close(port->device.stream.data_fd); | |
860 wait_for_connection(port); | |
861 } else { | |
862 //clear the nonblocking flag so the next write will block | |
863 if (!blocking) | |
864 { | |
865 fcntl(port->device.stream.data_fd, F_SETFL, O_RDWR); | |
866 blocking = 1; | |
867 } | |
868 } | |
869 } | |
870 } | |
871 } | |
872 | |
873 void io_data_write(io_port * port, uint8_t value, uint32_t current_cycle) | |
874 { | |
875 switch (port->device_type) | |
876 { | |
877 case IO_GAMEPAD6: | |
878 if (port->control & TH) { | |
879 //check if TH has changed | |
880 if ((port->output & TH) ^ (value & TH)) { | |
881 if (current_cycle >= port->device.pad.timeout_cycle) { | |
882 port->device.pad.th_counter = 0; | |
883 } | |
884 if (!(value & TH)) { | |
885 port->device.pad.th_counter++; | |
886 } | |
887 port->device.pad.timeout_cycle = current_cycle + TH_TIMEOUT; | |
888 } | |
889 } | |
890 port->output = value; | |
891 break; | |
892 case IO_GENERIC: | |
893 wait_for_connection(port); | |
894 port->input[IO_STATE] = IO_WRITE_PENDING; | |
895 port->output = value; | |
896 service_socket(port); | |
897 break; | |
898 default: | |
899 port->output = value; | |
900 } | |
901 | |
902 } | |
903 | |
904 uint8_t io_data_read(io_port * port, uint32_t current_cycle) | |
905 { | |
906 uint8_t control = port->control | 0x80; | |
907 uint8_t th = control & port->output & 0x40; | |
581 uint8_t input; | 908 uint8_t input; |
582 if (current_cycle >= pad->timeout_cycle) { | 909 switch (port->device_type) |
583 pad->th_counter = 0; | 910 { |
584 } | 911 case IO_GAMEPAD3: |
585 /*if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) { | 912 { |
586 printf("io_data_read | control: %X, TH: %X, GAMEPAD_TH0: %X, GAMEPAD_TH1: %X, TH Counter: %d, Timeout: %d, Cycle: %d\n", control, th, pad->input[GAMEPAD_TH0], pad->input[GAMEPAD_TH1], pad->th_counter,pad->timeout_cycle, context->current_cycle); | 913 input = port->input[th ? GAMEPAD_TH1 : GAMEPAD_TH0]; |
587 }*/ | 914 break; |
588 if (th) { | 915 } |
589 if (pad->th_counter == 3) { | 916 case IO_GAMEPAD6: |
590 input = pad->input[GAMEPAD_EXTRA]; | 917 { |
918 if (current_cycle >= port->device.pad.timeout_cycle) { | |
919 port->device.pad.th_counter = 0; | |
920 } | |
921 /*if (port->input[GAMEPAD_TH0] || port->input[GAMEPAD_TH1]) { | |
922 printf("io_data_read | control: %X, TH: %X, GAMEPAD_TH0: %X, GAMEPAD_TH1: %X, TH Counter: %d, Timeout: %d, Cycle: %d\n", control, th, port->input[GAMEPAD_TH0], port->input[GAMEPAD_TH1], port->th_counter,port->timeout_cycle, context->current_cycle); | |
923 }*/ | |
924 if (th) { | |
925 if (port->device.pad.th_counter == 3) { | |
926 input = port->input[GAMEPAD_EXTRA]; | |
927 } else { | |
928 input = port->input[GAMEPAD_TH1]; | |
929 } | |
591 } else { | 930 } else { |
592 input = pad->input[GAMEPAD_TH1]; | 931 if (port->device.pad.th_counter == 3) { |
593 } | 932 input = port->input[GAMEPAD_TH0] | 0xF; |
594 } else { | 933 } else if(port->device.pad.th_counter == 4) { |
595 if (pad->th_counter == 3) { | 934 input = port->input[GAMEPAD_TH0] & 0x30; |
596 input = pad->input[GAMEPAD_TH0] | 0xF; | 935 } else { |
597 } else if(pad->th_counter == 4) { | 936 input = port->input[GAMEPAD_TH0] | 0xC; |
598 input = pad->input[GAMEPAD_TH0] & 0x30; | 937 } |
599 } else { | 938 } |
600 input = pad->input[GAMEPAD_TH0] | 0xC; | 939 break; |
601 } | 940 } |
602 } | 941 case IO_SEGA_PARALLEL: |
603 uint8_t value = ((~input) & (~control)) | (pad->output & control); | 942 if (!th) |
604 /*if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) { | 943 { |
944 service_pipe(port); | |
945 } | |
946 input = ~port->input[th ? IO_TH1 : IO_TH0]; | |
947 break; | |
948 case IO_GENERIC: | |
949 if (port->input[IO_TH0] & 0x80 && port->input[IO_STATE] == IO_WRITTEN) | |
950 { | |
951 //device requested a blocking read after writes | |
952 port->input[IO_STATE] = IO_READ_PENDING; | |
953 } | |
954 service_socket(port); | |
955 input = ~port->input[IO_TH0]; | |
956 break; | |
957 default: | |
958 input = 0; | |
959 break; | |
960 } | |
961 uint8_t value = ((~input) & (~control)) | (port->output & control); | |
962 /*if (port->input[GAMEPAD_TH0] || port->input[GAMEPAD_TH1]) { | |
605 printf ("value: %X\n", value); | 963 printf ("value: %X\n", value); |
606 }*/ | 964 }*/ |
607 return value; | 965 return value; |
608 } | 966 } |
609 | 967 |