Informationen zum Anschluss der Tastatur:
https://mikrosk.github.io/doitarchive/doit_st/0f01.htm

Umgesetzt mit einen Arduino Pro Micro und dieser Software:
https://github.com/devonshire/arduino-atari-hid-keyboard

Der Code für das Keyboard musste für den MISTer angepasst werden:

https://github.com/sascha-schieferdecker/arduino-atari-hid-keyboard/

#include <Keyboard.h>
 
/*
 
* -------------------------------------------------------------------------
 
* Interface Atari ST Keyboard to USB HID Keyboard
 
* -------------------------------------------------------------------------
 
* Initial idea and some original code provided by user 'joska' of
 
* http://www.atari-forum.com - license unknown
 
* -------------------------------------------------------------------------
 
* Copyright Kevin Peat 2017
 
* kevin@kevinpeat.com
 
* My changes and additions are licensed public domain
 
* -------------------------------------------------------------------------
 
* Developed for use with an Arduino Leonardo as it is able to act directly
 
* as a USB keyboard controller so doesn't require the Arduino firmware to
 
* be modified as some of the other Arduinos (eg. Uno) would do
 
* -------------------------------------------------------------------------
 
*/
 
//#define DEBUG
 
  
 
// ST keyboard reset pin
 
const int ST_KB_RESET = 4;
 
  
 
// Atari modifier key codes
 
const uint8_t ST_LEFT_CTRL = 0x1D;
 
const uint8_t ST_LEFT_SHIFT = 0x2A;
 
const uint8_t ST_LEFT_ALT = 0x38;
 
const uint8_t ST_RIGHT_SHIFT = 0x36;
 
const uint8_t ST_CAPS_LOCK = 0x3A;
 
  
 
// Arduino Leonardo modifier key codes
 
const uint8_t ARD_LEFT_CTRL = 0x80;
 
const uint8_t ARD_LEFT_SHIFT = 0x81;
 
const uint8_t ARD_LEFT_ALT = 0x82;
 
const uint8_t ARD_RIGHT_SHIFT = 0x85;
 
const uint8_t ARD_CAPS_LOCK = 0xC1;
 
  
 
// Arduino Leonardo special key codes
 
const uint8_t ARD_UP_ARROW = 0xDA;
 
const uint8_t ARD_DOWN_ARROW = 0xD9;
 
const uint8_t ARD_LEFT_ARROW = 0xD8;
 
const uint8_t ARD_RIGHT_ARROW = 0xD7;
 
const uint8_t ARD_BACKSPACE = 0xB2;
 
const uint8_t ARD_TAB = 0xB3;
 
const uint8_t ARD_RETURN = 0xB0;
 
const uint8_t ARD_ESC = 0xB1;
 
const uint8_t ARD_INSERT = 0xD1;
 
const uint8_t ARD_DELETE = 0xD4;
 
const uint8_t ARD_HOME = 0xD2;
 
const uint8_t ARD_F1 = 0xC2;
 
const uint8_t ARD_F2 = 0xC3;
 
const uint8_t ARD_F3 = 0xC4;
 
const uint8_t ARD_F4 = 0xC5;
 
const uint8_t ARD_F5 = 0xC6;
 
const uint8_t ARD_F6 = 0xC7;
 
const uint8_t ARD_F7 = 0xC8;
 
const uint8_t ARD_F8 = 0xC9;
 
const uint8_t ARD_F9 = 0xCA;
 
const uint8_t ARD_F10 = 0xCB;
 
const uint8_t ARD_F11 = 0xCC;
 
const uint8_t ARD_F12 = 0xCD;
 
  
 
// Keyboard auto-repeat
 
static uint8_t last_make; // Last make char
 
static unsigned long last_make_time; // Last make time (milliseconds)
 
int auto_repeat_delay = 500; // Keyboard auto-repeat delay (milliseconds)
 
int auto_repeat_rate = 25; // Keyboard auto-repeat rate (milliseconds)
 
  
 
// Key scancodes
 
// Use ST scancode as index to find the corresponding USB scancode.
 
// Make-codes are single byte, with some exceptions.
 
// These are escaped with a 0xe0 byte, when this appear in this table a
 
// simple switch is used to look up the correct scancode.
 
// All break codes are the same as the scancodes, but are prefixed with 0xf0.
 
// The escaped keys are first escaped, then 0xf0, then scancode.
 
uint8_t scanCodes[] =
 
{
 
0x00, // (Nothing)
 
ARD_ESC, // Esc
 
0x31, // 1
 
0x32, // 2
 
0x33, // 3
 
0x34, // 4
 
0x35, // 5
 
0x36, // 6
 
0x37, // 7
 
0x38, // 8
 
0x39, // 9
 
0x30, // 0
 
0x2D, // -
 
0x3D, // == (Mapped to =)
 
ARD_BACKSPACE, // Backspace
 
ARD_TAB, // Tab
 
0x71, // q
 
0x77, // w
 
0x65, // e
 
0x72, // r
 
0x74, // t
 
0x79, // y
 
0x75, // u
 
0x69, // i
 
0x6F, // o
 
0x70, // p
 
0x5B, // [
 
0x5D, // ]
 
ARD_RETURN, // Enter
 
ARD_LEFT_CTRL, // Control
 
0x61, // a
 
0x73, // s
 
0x64, // d
 
0x66, // f
 
0x67, // g
 
0x68, // h
 
0x6A, // j
 
0x6B, // k
 
0x6C, // l
 
0x3B, // ;
 
0x27, // ' (Mapped to '")
 
0xE0, // #
 
ARD_LEFT_SHIFT, // Lshift
 
0x7E, // #~ (Mapped to ~)
 
0x7A, // z
 
0x78, // x
 
0x63, // c
 
0x76, // v
 
0x62, // b
 
0x6E, // n
 
0x6D, // m
 
0x2C, // ,
 
0x2E, // .
 
0x2F, // /
 
ARD_RIGHT_SHIFT, // Rshift
 
0x37, // (Not used)
 
ARD_LEFT_ALT, // Alternate
 
0x20, // Space
 
ARD_CAPS_LOCK, // CapsLock
 
ARD_F1, // F1
 
ARD_F2, // F2
 
ARD_F3, // F3
 
ARD_F4, // F4
 
ARD_F5, // F5
 
ARD_F6, // F6
 
ARD_F7, // F7
 
ARD_F8, // F8
 
ARD_F9, // F9
 
ARD_F10, // F10
 
0x45, // (Not used)
 
0x46, // (Not used)
 
0xE0, // Clr/Home
 
0xE0, // Up Arrow
 
0x49, // (Not used)
 
0x2D, // N-
 
0xE0, // Left Arrow
 
0x4c, // (Not used)
 
0xE0, // Right Arrow
 
0x2B, // N+
 
0x4f, // (Not used)
 
0xE0, // Down Arrow
 
0x51, // (Not used)
 
0xE0, // Insert
 
0xE0, // Delete
 
0x54, // (Not used)
 
0x55, // (Not used)
 
0x56, // (Not used)
 
0x57, // (Not used)
 
0x58, // (Not used)
 
0x59, // (Not used)
 
0x5a, // (Not used)
 
0x5b, // (Not used)
 
0x5c, // (Not used)
 
0x5d, // (Not used)
 
0x5e, // (Not used)
 
0x5f, // (Not used)
 
0x5C, // ISO Key
 
0xB2, // Undo (Mapped to Backspace)
 
ARD_F12, // Help (Mapped to F12 which is the Hatari menu key)
 
0x28, // N(
 
0x29, // N)
 
0xE0, // N/
 
0x2A, // N*
 
0x37, // N7
 
0x38, // N8
 
0x39, // N9
 
0x34, // N4
 
0x35, // N5
 
0x36, // N6
 
0x31, // N1
 
0x32, // N2
 
0x33, // N3
 
0x30, // N0
 
0x2E, // N.
 
0xE0 // NEnter
 
};
 
  
 
// Forward declarations for functions used before they are defined
 
void send_key_event(uint8_t code, bool is_press);
 
boolean process_modifier(uint8_t key);
 
  
 
void setup(void)
 
{
 
// Initialize keyboard:
 
Keyboard.begin();
 
// Open serial port from Atari keyboard
 
Serial1.begin(7812);
 
  
 
#ifdef DEBUG
 
// Open serial port to PC
 
Serial.begin(9600);
 
#endif
 
// Reset ST keyboard
 
delay(200);
 
reset_st_keyboard();
 
delay(200);
 
  
 
// Empty serial buffer before starting
 
while(Serial1.available() > 0) Serial1.read();
 
}
 
  
 
void loop()
 
{
 
// Process incoming Atari keypresses
 
if (Serial1.available() > 0) process_keypress(Serial1.read());
 
  
 
// Handle keyboard auto-repeat
 
// auto_repeat(); // disabled: we now honor real make/break
 
}
 
  
 
// Reset ST Keyboard
 
void reset_st_keyboard(void)
 
{
 
Serial1.write((uint8_t)0x80);
 
Serial1.write((uint8_t)0x01);
 
pinMode(ST_KB_RESET, OUTPUT);
 
digitalWrite(ST_KB_RESET, HIGH);
 
delay(20);
 
digitalWrite(ST_KB_RESET, LOW);
 
delay(20);
 
digitalWrite(ST_KB_RESET, HIGH);
 
}
 
  
 
// Process each keypress
 
  
  
 
void process_keypress(uint8_t key)
 
{
 
// Valid ST range
 
if (((key & 0x7f) > 0) && ((key & 0x7f) < 0x73))
 
{
 
if (key & 0x80) // Break
 
{
 
last_make = 0;
 
last_make_time = 0;
 
// still forward to release non-modifier keys
 
convert_scancode(key);
 
}
 
else // Make
 
{
 
last_make = key;
 
last_make_time = millis();
 
convert_scancode(key);
 
}
 
}
 
}
 
  
  
 
void convert_scancode(uint8_t key)
 
{
 
uint8_t break_code = key & 0x80;
 
uint8_t st_sc = key & 0x7f;
 
uint8_t pc_code = scanCodes[st_sc];
 
bool is_break = break_code;
 
bool escaped = (pc_code == 0xe0);
 
  
 
#ifdef DEBUG
 
Serial.print("Atari scancode: ");
 
Serial.println(key, DEC);
 
Serial.print("PC scancode: ");
 
Serial.println(pc_code, DEC);
 
Serial.print("Break code: ");
 
Serial.println(break_code, DEC);
 
Serial.print("Escaped: ");
 
Serial.println(escaped, DEC);
 
#endif
 
// Handle modifier key presses (and CapsLock tap)
 
if (process_modifier(key)) return;
 
  
 
// Map escaped keys to Arduino HID keycodes, then send make/break
 
if (escaped)
 
{
 
switch (st_sc)
 
{
 
case 0x48: send_key_event(ARD_UP_ARROW, !is_break); break; // Up
 
case 0x4b: send_key_event(ARD_LEFT_ARROW, !is_break); break; // Left
 
case 0x4d: send_key_event(ARD_RIGHT_ARROW, !is_break); break; // Right
 
case 0x50: send_key_event(ARD_DOWN_ARROW, !is_break); break; // Down
 
case 0x52: send_key_event(ARD_INSERT, !is_break); break; // Insert
 
case 0x53: send_key_event(ARD_DELETE, !is_break); break; // Delete
 
case 0x47: send_key_event(ARD_HOME, !is_break); break; // Home
 
case 0x65: send_key_event('/', !is_break); break; // Numpad /
 
case 0x72: send_key_event(ARD_RETURN, !is_break); break; // Numpad Enter
 
case 0x2b: send_key_event('#', !is_break); break; // Tilde/# mapping
 
case 0x62: send_key_event(ARD_F1, !is_break); break; // Help -> F1
 
default: break;
 
}
 
}
 
else
 
{
 
// Non-escaped keys: send make/break with the mapped code
 
send_key_event(pc_code, !is_break);
 
}
 
}
 
  
 
// Send code for escaped Atari keypresses
 
void send_escaped_key(uint8_t key)
 
{
 
Keyboard.press(key);
 
delay(20);
 
Keyboard.release(key);
 
}
 
  
  
 
// Press or release a key (ASCII for printable, ARD_* for specials)
 
void send_key_event(uint8_t code, bool is_press)
 
{
 
if (is_press) {
 
Keyboard.press(code);
 
} else {
 
Keyboard.release(code);
 
}
 
}
 
// Process modifier keypresses
 
  
 
boolean process_modifier(uint8_t key)
 
{
 
// Modifier key press
 
switch (key)
 
{
 
case ST_LEFT_CTRL:
 
Keyboard.press(ARD_LEFT_CTRL);
 
return true;
 
case ST_LEFT_SHIFT:
 
Keyboard.press(ARD_LEFT_SHIFT);
 
return true;
 
case ST_LEFT_ALT:
 
Keyboard.press(ARD_LEFT_ALT);
 
return true;
 
case ST_RIGHT_SHIFT:
 
Keyboard.press(ARD_RIGHT_SHIFT);
 
return true;
 
case ST_CAPS_LOCK:
 
// Treat as a tap (toggle), not a hold
 
Keyboard.press(ARD_CAPS_LOCK);
 
delay(20);
 
Keyboard.release(ARD_CAPS_LOCK);
 
return true;
 
}
 
  
 
// Modifier key release (no Caps Lock here)
 
switch (key & 0x7f)
 
{
 
case ST_LEFT_CTRL:
 
Keyboard.release(ARD_LEFT_CTRL);
 
return true;
 
case ST_LEFT_SHIFT:
 
Keyboard.release(ARD_LEFT_SHIFT);
 
return true;
 
case ST_LEFT_ALT:
 
Keyboard.release(ARD_LEFT_ALT);
 
return true;
 
case ST_RIGHT_SHIFT:
 
Keyboard.release(ARD_RIGHT_SHIFT);
 
return true;
 
}
 
return false;
 
}
 
// Keyboard auto repeat
 
void auto_repeat(void)
 
{
 
static unsigned long last_repeat;
 
static byte key_repeating; // True if key being repeated
 
// Don't want to repeat modifiers
 
switch (last_make)
 
{
 
case ST_LEFT_CTRL:
 
case ST_LEFT_SHIFT:
 
case ST_RIGHT_SHIFT:
 
case ST_LEFT_ALT:
 
case ST_CAPS_LOCK:
 
case 0x00: // No key held down
 
key_repeating = false;
 
return;
 
}
 
  
 
// Delay to first repeat
 
if (!key_repeating && (millis() - last_make_time > auto_repeat_delay))
 
{
 
key_repeating = true;
 
last_repeat = millis();
 
return;
 
}
 
  
 
// Start repeating
 
if (key_repeating && (millis() - last_repeat > auto_repeat_rate))
 
{
 
last_repeat = millis();
 
convert_scancode(last_make);
 
}
 
}