mirror of
https://github.com/prickle/GameBloke.git
synced 2024-06-18 06:29:27 +00:00
518 lines
11 KiB
C++
518 lines
11 KiB
C++
|
#include <Arduino.h>
|
||
|
#include "teensy-usb-keyboard.h"
|
||
|
#include "globals.h"
|
||
|
#include <USBHost_t36.h>
|
||
|
#include <Keypad.h>
|
||
|
#include "RingBuf.h"
|
||
|
|
||
|
USBHost myusb;
|
||
|
USBHub hub1(myusb);
|
||
|
//USBHub hub2(myusb);
|
||
|
//USBHub hub3(myusb);
|
||
|
KeyboardController keyboard1(myusb);
|
||
|
|
||
|
const byte ROWS = 3;
|
||
|
const byte COLS = 7;
|
||
|
|
||
|
//Panel keys and joystick
|
||
|
char keys[ROWS][COLS] = {
|
||
|
{ BUT1, LJOY, UJOY, RJOY, DJOY },
|
||
|
{ BUT2, ENTKEY, LKEY, DKEY, RKEY },
|
||
|
{ JOYKEY, NOTEKEY, LWHTKEY, UKEY, RWHTKEY }
|
||
|
};
|
||
|
|
||
|
uint8_t rowsPins[ROWS] = { 24, 25, 26 };
|
||
|
uint8_t colsPins[COLS] = { 27, 28, 29, 30, 31 };
|
||
|
Keypad keypad(makeKeymap(keys), rowsPins, colsPins, ROWS, COLS);
|
||
|
|
||
|
RingBuf bufferUsb(10); // 10 keys should be plenty, right?
|
||
|
|
||
|
|
||
|
static uint8_t panelBIOS[15] = { LARR, RARR, UARR, DARR, RET, ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9' };
|
||
|
|
||
|
uint8_t panelMap[15] = { LJOY, RJOY, UJOY, DJOY, LA, RA, 'j', 'k', '1', UARR, '2', RET, LARR, DARR, RARR };
|
||
|
|
||
|
//PS2 101 key keyboard layout
|
||
|
const char ps2KeyLayout[102] =
|
||
|
{
|
||
|
0, 0, 0, 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
|
||
|
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
|
||
|
'y', 'z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', RET, ESC,
|
||
|
DEL, TAB, ' ', '-', '=', '[', ']','\\', '?', ';','\'', '`', ',', '.',
|
||
|
'/',LOCK, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
SYSRQ, 0, 0, 0, 0, 0, 0, 0, 0,RARR,LARR,DARR,UARR, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, LA, RA
|
||
|
};
|
||
|
|
||
|
static uint8_t shiftedNumber[] = { '<', // ,
|
||
|
'_', // -
|
||
|
'>', // .
|
||
|
'?', // /
|
||
|
')', // 0
|
||
|
'!', // 1
|
||
|
'@', // 2
|
||
|
'#', // 3
|
||
|
'$', // 4
|
||
|
'%', // 5
|
||
|
'^', // 6
|
||
|
'&', // 7
|
||
|
'*', // 8
|
||
|
'(', // 9
|
||
|
0, // (: is not a key)
|
||
|
':' // ;
|
||
|
};
|
||
|
|
||
|
//bool buffered;
|
||
|
bool leftShiftPressed;
|
||
|
bool rightShiftPressed;
|
||
|
bool ctrlPressed;
|
||
|
bool capsLock;
|
||
|
bool leftApplePressed;
|
||
|
bool rightApplePressed;
|
||
|
int8_t numPressed;
|
||
|
bool buffered;
|
||
|
VMKeyboard *vmKeyboard;
|
||
|
|
||
|
int16_t paddle0;
|
||
|
int16_t paddle1;
|
||
|
uint8_t currentJoyMode = JOY_MODE_ANA_ABS;
|
||
|
|
||
|
TeensyUsbKeyboard::TeensyUsbKeyboard(VMKeyboard *k) : PhysicalKeyboard(k)
|
||
|
{
|
||
|
keypad.setDebounceTime(5);
|
||
|
|
||
|
myusb.begin();
|
||
|
keyboard1.attachPress(onPress);
|
||
|
keyboard1.attachRelease(onRelease);
|
||
|
//keyboard1.rawOnly(true);
|
||
|
|
||
|
leftShiftPressed = false;
|
||
|
rightShiftPressed = false;
|
||
|
ctrlPressed = false;
|
||
|
capsLock = true;
|
||
|
leftApplePressed = false;
|
||
|
rightApplePressed = false;
|
||
|
buffered = false;
|
||
|
vmKeyboard = k;
|
||
|
|
||
|
numPressed = 0;
|
||
|
paddle0 = 127;
|
||
|
paddle1 = 127;
|
||
|
}
|
||
|
|
||
|
TeensyUsbKeyboard::~TeensyUsbKeyboard()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
uint8_t TeensyUsbKeyboard::getMapping(uint8_t key) {
|
||
|
return panelMap[key];
|
||
|
}
|
||
|
|
||
|
void TeensyUsbKeyboard::setMapping(uint8_t key, uint8_t val) {
|
||
|
panelMap[key] = val;
|
||
|
}
|
||
|
|
||
|
void onPress(int unicode)
|
||
|
{
|
||
|
uint8_t key = keyboard1.getOemKey();
|
||
|
uint8_t modifiers = keyboard1.getModifiers();
|
||
|
if (key > 101) key = 101;
|
||
|
if (buffered) {
|
||
|
pressedKey(ps2KeyLayout[key], modifiers);
|
||
|
} else {
|
||
|
vmKeyboard->keyDepressed(ps2KeyLayout[key], modifiers);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void onRelease(int unicode)
|
||
|
{
|
||
|
uint8_t key = keyboard1.getOemKey();
|
||
|
uint8_t modifiers = keyboard1.getModifiers();
|
||
|
if (key > 101) key = 101;
|
||
|
if (buffered) {
|
||
|
releasedKey(ps2KeyLayout[key], modifiers);
|
||
|
} else {
|
||
|
vmKeyboard->keyReleased(ps2KeyLayout[key], modifiers);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void pressedKey(uint8_t key, uint8_t mod)
|
||
|
{
|
||
|
numPressed++;
|
||
|
if (key != SYSRQ && key & 0x80) {
|
||
|
// it's a modifier key.
|
||
|
switch (key) {
|
||
|
case _CTRL:
|
||
|
ctrlPressed = 1;
|
||
|
break;
|
||
|
case LSHFT:
|
||
|
leftShiftPressed = 1;
|
||
|
break;
|
||
|
case RSHFT:
|
||
|
rightShiftPressed = 1;
|
||
|
break;
|
||
|
case LOCK:
|
||
|
capsLock = !capsLock;
|
||
|
break;
|
||
|
case LA:
|
||
|
leftApplePressed = 1;
|
||
|
break;
|
||
|
case RA:
|
||
|
rightApplePressed = 1;
|
||
|
break;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (key == ' ' || key == DEL || key == ESC || key == RET || key == TAB || key == SYSRQ) {
|
||
|
//Serial.println((int)key);
|
||
|
|
||
|
bufferUsb.addByte(key);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (key >= 'a' &&
|
||
|
key <= 'z') {
|
||
|
if (ctrlPressed) {
|
||
|
bufferUsb.addByte(key - 'a' + 1);
|
||
|
return;
|
||
|
}
|
||
|
if (leftShiftPressed || rightShiftPressed || capsLock) {
|
||
|
bufferUsb.addByte(key - 'a' + 'A');
|
||
|
return;
|
||
|
}
|
||
|
bufferUsb.addByte(key);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// FIXME: can we control-shift?
|
||
|
if (key >= ',' && key <= ';') {
|
||
|
if (leftShiftPressed || rightShiftPressed) {
|
||
|
bufferUsb.addByte(shiftedNumber[key - ',']);
|
||
|
return;
|
||
|
}
|
||
|
bufferUsb.addByte(key);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (leftShiftPressed || rightShiftPressed) {
|
||
|
uint8_t ret = 0;
|
||
|
switch (key) {
|
||
|
case '=':
|
||
|
ret = '+';
|
||
|
break;
|
||
|
case '[':
|
||
|
ret = '{';
|
||
|
break;
|
||
|
case ']':
|
||
|
ret = '}';
|
||
|
break;
|
||
|
case '\\':
|
||
|
ret = '|';
|
||
|
break;
|
||
|
case '\'':
|
||
|
ret = '"';
|
||
|
break;
|
||
|
case '`':
|
||
|
ret = '~';
|
||
|
break;
|
||
|
}
|
||
|
if (ret) {
|
||
|
bufferUsb.addByte(ret);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Everything else falls through.
|
||
|
bufferUsb.addByte(key);
|
||
|
}
|
||
|
|
||
|
void releasedKey(uint8_t key, uint8_t mod)
|
||
|
{
|
||
|
numPressed--;
|
||
|
if (key & 0x80) {
|
||
|
// it's a modifier key.
|
||
|
switch (key) {
|
||
|
case _CTRL:
|
||
|
ctrlPressed = 0;
|
||
|
break;
|
||
|
case LSHFT:
|
||
|
leftShiftPressed = 0;
|
||
|
break;
|
||
|
case RSHFT:
|
||
|
rightShiftPressed = 0;
|
||
|
break;
|
||
|
case LA:
|
||
|
leftApplePressed = 0;
|
||
|
break;
|
||
|
case RA:
|
||
|
rightApplePressed = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool TeensyUsbKeyboard::kbhit()
|
||
|
{
|
||
|
//USB keyboard
|
||
|
if (!buffered) {
|
||
|
bufferUsb.clear();
|
||
|
buffered = true;
|
||
|
}
|
||
|
myusb.Task();
|
||
|
|
||
|
//Front panel keys
|
||
|
if (keypad.getKeys()) {
|
||
|
for (int i=0; i<LIST_MAX; i++) {
|
||
|
if ( keypad.key[i].stateChanged ) {
|
||
|
switch (keypad.key[i].kstate) {
|
||
|
case PRESSED:
|
||
|
pressedKey(panelBIOS[keypad.key[i].kchar - 0x88], 0);
|
||
|
break;
|
||
|
case RELEASED:
|
||
|
releasedKey(panelBIOS[keypad.key[i].kchar - 0x88], 0);
|
||
|
break;
|
||
|
case HOLD:
|
||
|
case IDLE:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// For debugging: also allow USB serial to act as a keyboard
|
||
|
if (Serial.available()) {
|
||
|
bufferUsb.addByte(Serial.read());
|
||
|
}
|
||
|
|
||
|
return bufferUsb.hasData();
|
||
|
}
|
||
|
|
||
|
uint8_t TeensyUsbKeyboard::read()
|
||
|
{
|
||
|
if (bufferUsb.hasData()) {
|
||
|
return bufferUsb.consumeByte();
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//Set joystick mode to analog, joyport1 or joyport2
|
||
|
void TeensyUsbKeyboard::setJoymode(uint8_t mode) {
|
||
|
currentJoyMode = mode;
|
||
|
if (mode == JOY_MODE_ANA_ABS || mode == JOY_MODE_ANA_REL)
|
||
|
//Regular button interface
|
||
|
vmKeyboard->setButtons(false, false, false);
|
||
|
else if (mode == JOY_MODE_JOYPORT1 || mode == JOY_MODE_JOYPORT2)
|
||
|
//Joyport inputs are active low
|
||
|
vmKeyboard->setButtons(true, true, true);
|
||
|
}
|
||
|
|
||
|
//Joyport stick switches
|
||
|
bool stick_up = false;
|
||
|
bool stick_down = false;
|
||
|
bool stick_left = false;
|
||
|
bool stick_right = false;
|
||
|
bool stick_trig = false;
|
||
|
|
||
|
//Set joystick input buttons based on annunciator outputs
|
||
|
// Emulates joyport circuit
|
||
|
// http://lukazi.blogspot.nl/2009/04/game-controller-atari-joysticks.html
|
||
|
void updateJoyport() {
|
||
|
if (currentJoyMode != JOY_MODE_ANA_ABS && currentJoyMode != JOY_MODE_ANA_REL) {
|
||
|
if ((!vmKeyboard->getAnnunciator(0) && currentJoyMode == JOY_MODE_JOYPORT1) || //Joystick 1 enabled
|
||
|
(vmKeyboard->getAnnunciator(0) && currentJoyMode == JOY_MODE_JOYPORT2)) { //Joystick 2 enabled
|
||
|
vmKeyboard->setButton(0, !stick_trig); //Button
|
||
|
if (!vmKeyboard->getAnnunciator(1)) { //Joystick L/R or U/D
|
||
|
vmKeyboard->setButton(1, !stick_left);
|
||
|
vmKeyboard->setButton(2, !stick_right);
|
||
|
} else {
|
||
|
vmKeyboard->setButton(1, !stick_up);
|
||
|
vmKeyboard->setButton(2, !stick_down);
|
||
|
}
|
||
|
}
|
||
|
else vmKeyboard->setButtons(true, true, true); //No joystick
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Called from MMU when game port annunciators are changed
|
||
|
void TeensyUsbKeyboard::setAnnunciators() {
|
||
|
updateJoyport();
|
||
|
}
|
||
|
|
||
|
void updateRelStick() {
|
||
|
if (currentJoyMode == JOY_MODE_ANA_REL) {
|
||
|
//Relative Joystick Emulation
|
||
|
if (stick_left) {
|
||
|
paddle0 -= g_joySpeed;
|
||
|
if (paddle0 < 0) paddle0 = 0;
|
||
|
}
|
||
|
else if (stick_right) {
|
||
|
paddle0 += g_joySpeed;
|
||
|
if (paddle0 > 255) paddle0 = 255;
|
||
|
}
|
||
|
if (stick_up) {
|
||
|
paddle1 -= g_joySpeed;
|
||
|
if (paddle1 < 0) paddle1 = 0;
|
||
|
}
|
||
|
else if (stick_down) {
|
||
|
paddle1 += g_joySpeed;
|
||
|
if (paddle1 > 255) paddle1 = 255;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Joystick emulation
|
||
|
// Returns true if key was handled by stick
|
||
|
bool pressStick(uint8_t key) {
|
||
|
//Joyport emulation
|
||
|
if (currentJoyMode != JOY_MODE_ANA_ABS) {
|
||
|
if (key == LJOY) {
|
||
|
stick_left = true;
|
||
|
stick_right = false;
|
||
|
updateJoyport();
|
||
|
return true;
|
||
|
}
|
||
|
if (key == RJOY) {
|
||
|
stick_left = false;
|
||
|
stick_right = true;
|
||
|
updateJoyport();
|
||
|
return true;
|
||
|
}
|
||
|
if (key == UJOY) {
|
||
|
stick_up = true;
|
||
|
stick_down = false;
|
||
|
updateJoyport();
|
||
|
return true;
|
||
|
}
|
||
|
if (key == DJOY) {
|
||
|
stick_up = false;
|
||
|
stick_down = true;
|
||
|
updateJoyport();
|
||
|
return true;
|
||
|
}
|
||
|
if (key == LA && currentJoyMode != JOY_MODE_ANA_REL) {
|
||
|
stick_trig = true;
|
||
|
updateJoyport();
|
||
|
return true;
|
||
|
}
|
||
|
if (key == RA && currentJoyMode != JOY_MODE_ANA_REL) {
|
||
|
return true;
|
||
|
}
|
||
|
} else {
|
||
|
//Absolute Analog stick emulation
|
||
|
if (key == LJOY) {
|
||
|
paddle0 = 0;
|
||
|
return true;
|
||
|
}
|
||
|
if (key == RJOY) {
|
||
|
paddle0 = 255;
|
||
|
return true;
|
||
|
}
|
||
|
if (key == UJOY) {
|
||
|
paddle1 = 0;
|
||
|
return true;
|
||
|
}
|
||
|
if (key == DJOY) {
|
||
|
paddle1 = 255;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool releaseStick(uint8_t key) {
|
||
|
//Joyport emulation
|
||
|
if (currentJoyMode != JOY_MODE_ANA_ABS) {
|
||
|
if (key == LJOY || key == RJOY) {
|
||
|
stick_left = false;
|
||
|
stick_right = false;
|
||
|
updateJoyport();
|
||
|
return true;
|
||
|
}
|
||
|
if (key == UJOY || key == DJOY) {
|
||
|
stick_up = false;
|
||
|
stick_down = false;
|
||
|
updateJoyport();
|
||
|
return true;
|
||
|
}
|
||
|
if (key == LA && currentJoyMode != JOY_MODE_ANA_REL) {
|
||
|
stick_trig = false;
|
||
|
updateJoyport();
|
||
|
return true;
|
||
|
}
|
||
|
if (key == RA && currentJoyMode != JOY_MODE_ANA_REL) {
|
||
|
return true;
|
||
|
}
|
||
|
} else {
|
||
|
//Absolute Analog stick emulation
|
||
|
if (key == LJOY || key == RJOY) {
|
||
|
paddle0 = g_joyTrimX;
|
||
|
return true;
|
||
|
}
|
||
|
if (key == UJOY || key == DJOY) {
|
||
|
paddle1 = g_joyTrimY;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool capsLed = false;
|
||
|
// This is a non-buffered interface to the physical keyboard, as used
|
||
|
// by the VM.
|
||
|
void TeensyUsbKeyboard::maintainKeyboard()
|
||
|
{
|
||
|
static bool oldCapsLed = false;
|
||
|
if (oldCapsLed != capsLed) {
|
||
|
oldCapsLed = capsLed;
|
||
|
//if (keyboard1.connected()) keyboard1.capsLock(capsLed);
|
||
|
}
|
||
|
// Serial.println("maintain");
|
||
|
buffered = false;
|
||
|
myusb.Task();
|
||
|
|
||
|
updateRelStick();
|
||
|
|
||
|
if (keypad.getKeys()) {
|
||
|
for (int i=0; i<LIST_MAX; i++) {
|
||
|
if ( keypad.key[i].stateChanged ) {
|
||
|
uint8_t kchar = panelMap[keypad.key[i].kchar - 0x88];
|
||
|
switch (keypad.key[i].kstate) {
|
||
|
case PRESSED:
|
||
|
if (!pressStick(kchar))
|
||
|
vmkeyboard->keyDepressed(kchar);
|
||
|
break;
|
||
|
case RELEASED:
|
||
|
if (!releaseStick(kchar))
|
||
|
vmkeyboard->keyReleased(kchar);
|
||
|
break;
|
||
|
case HOLD:
|
||
|
case IDLE:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// For debugging: also allow USB serial to act as a keyboard
|
||
|
if (Serial.available()) {
|
||
|
int c = Serial.read();
|
||
|
vmkeyboard->keyDepressed(c);
|
||
|
vmkeyboard->keyReleased(c);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void TeensyUsbKeyboard::startReading()
|
||
|
{
|
||
|
g_vm->triggerPaddleInCycles(0, 12 * paddle0);
|
||
|
g_vm->triggerPaddleInCycles(1, 12 * paddle1);
|
||
|
}
|
||
|
|
||
|
void TeensyUsbKeyboard::setCaps(bool enabled) {
|
||
|
capsLed = enabled;
|
||
|
Serial.print("Set Leds:");
|
||
|
Serial.println(enabled?"On":"Off");
|
||
|
}
|