/* This is a hack to let Vectrex developers who happen to own a raspberry pi use a USB keyboard with their Vectrex program. (Note: Nothing to do with Pitrex if you know what that is, other than you might borrow the Pi Zero out of it...) You need to connect two GPIO output pins and a ground pin to the inputs for buttons one and two on the second controller input port on the Vectrex (the leftmost socket). With the software as currently implemented those are the two leftmost buttons and the GPIO pins I'm using in this build are 23 and 24. There's no other hardware needed such as resistors etc. Just wires. (I'll put photos and a diagram in this directory soon) Characters might sometimes be missed, so type slowly and methodically, and check the echo as you type. However they do seem to be recognised quite reliably. The missed characters can most likely be improved by removing the timeout code on bit reception. */ #include <stdio.h> #include <pigpio.h> #include <unistd.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termio.h> #define KEY_ESCAPE 27 #define KEY_UP 128 #define KEY_DOWN 129 #define KEY_LEFT 130 #define KEY_RIGHT 131 int kbhit(void) { char buff[2]; struct termios original; struct termios term; int characters_buffered = 0; tcgetattr(STDIN_FILENO, &original); memcpy(&term, &original, sizeof(term)); // term = original; term.c_lflag &= ~ICANON; tcsetattr(STDIN_FILENO, TCSANOW, &term); ioctl(STDIN_FILENO, FIONREAD, &characters_buffered); tcsetattr(STDIN_FILENO, TCSANOW, &original); if (characters_buffered-- > 0) { int rc = read(STDIN_FILENO, buff, 1); if ((characters_buffered > 1) && (buff[0] == KEY_ESCAPE)) { char escbuff[2]; rc = read(STDIN_FILENO, escbuff, 2); if (escbuff[0] == '[') { if (escbuff[1] == 'A') return KEY_UP; if (escbuff[1] == 'B') return KEY_DOWN; if (escbuff[1] == 'C') return KEY_RIGHT; if (escbuff[1] == 'D') return KEY_LEFT; } } return buff[0]; } return -1; } void echoOff(void) { struct termios term; tcgetattr(STDIN_FILENO, &term); term.c_lflag &= ~ECHO; tcsetattr(STDIN_FILENO, TCSANOW, &term); } void echoOn(void) { struct termios term; tcgetattr(STDIN_FILENO, &term); term.c_lflag |= ECHO; tcsetattr(STDIN_FILENO, TCSANOW, &term); } #define DELAY 1000 void slow_write(int pin, int bit) { gpioWrite(pin, bit); // Slowed down tremendously so that I can watch the button presses with my // utility at https://gtoal.com/vectrex/joystick/AnaJoyTest.bin // Now that it seems to be working, I'll test with https://gtoal.com/vectrex/keyboard/ // and then shorten the delay until it's just before the point where it stops working... usleep(pin == 23 ? 4*DELAY : DELAY); // give data time to settle before strobe // usleep((useconds_t)1000000/60 /* uS per frame */ / 128); } void send(int key) { fprintf(stderr, "Sending key '%c' (%d)\r\n", key, key); int bit = key&1; slow_write(23, 0); // start bit slow_write(24, 0); key = key>>1; slow_write(23, bit^1); // set up the data bit slow_write(24, 1); // and clock it out... bit = key&1; key = key>>1; slow_write(23, bit^1); // set up the data bit slow_write(24, 0); // and clock it out... bit = key&1; key = key>>1; slow_write(23, bit^1); // set up the data bit slow_write(24, 1); // and clock it out... bit = key&1; key = key>>1; slow_write(23, bit^1); // set up the data bit slow_write(24, 0); // and clock it out... bit = key&1; key = key>>1; slow_write(23, bit^1); // set up the data bit slow_write(24, 1); // and clock it out... bit = key&1; key = key>>1; slow_write(23, bit^1); // set up the data bit slow_write(24, 0); // and clock it out... bit = key&1; key = key>>1; slow_write(23, bit^1); // set up the data bit slow_write(24, 1); // and clock it out... bit = key&1; slow_write(23, bit^1); // set up the data bit slow_write(24, 0); // and clock it out... slow_write(23, 1); // start bit slow_write(24, 1); // leave default clock level at 1 usleep(DELAY*100); // long frame gap between keystrokes to allow screen update } int main(int argc, char *argv[]) { double start; if (gpioInitialise() < 0) { // For some reason this fails if "sudo pigpiod" already started... fprintf(stderr, "pigpio initialisation failed\n"); return 1; } /* Set GPIO modes */ gpioSetMode(23, PI_OUTPUT); gpioSetMode(24, PI_OUTPUT); slow_write(23, 1); // OFF slow_write(24, 1); // OFF usleep(DELAY*10); echoOff(); fprintf(stderr, "Start up the Vectrex program now.\r\n"); for (;;) { int key = kbhit(); // TO DO: Single-character input, no line buffering if (key < 0) continue; if (key == KEY_ESCAPE) break; send(key); } echoOn(); /* Stop DMA, release resources */ gpioTerminate(); return 0; }