Go Down

Topic: AMIGA 500/1000/2000 Keyboard Interface (Read 48705 times) previous topic - next topic


Attached is my version of the sketch for use with an Amiga Emulator on Raspberry Pi, "Amibian". The RasPi fits very easily into an A500 case with the motherboard/floppy removed, so I wanted to use the stock A500 keyboard.
I loaded this sketch into the Leonardo with the web editor, and it works. For testing, I actually used an Amiga 2000 keyboard with only 4 pins. It is missing the Reset pin, so I had to comment out the CTRL ALT DEL code otherwise I get a constant signal of soft reset from the Leonardo. I don't really need this function for the emulator anyway. It was trial-and-error trying to find the pinout for the A2000 keyboard. This might save someone time:

I set my help key and numpad up for use with the emulator. I didn't need all the extra navigation stuff, so I mapped the "Help" key to be F12 for easy access to the emulator GUI, and I set the NumL and ScrL as the primary function for those keys. Also added a LED on pin 5 to light when NumL is active (not sure if I will wind up using it, but someone may).
I found a chart with Amiga keycodes here: http://wiki.amigaos.net/wiki/Keymap_Library
Scroll down to the "Raw Key Table", and look at the 3rd Column "USB Code". Substitute the first two digits with a 0x (Ex: 0034 needs to be 0x34 in your code).
Thanks to the OP, et. all.

Code: [Select]
#include <Keyboard.h> // FOR NEWEST IDE 1.6.9
#define HID_SendReport(id,data,len) HID().SendReport(id,data,len) // IDE 1.6.9

#define BITMASK_A500CLK 0b00010000    // IO 8
#define BITMASK_A500SP  0b00100000    // IO 9
#define BITMASK_A500RES 0b01000000    // IO 10
#define BITMASK_JOY1    0b10011111    // IO 0..4,6
#define BITMASK_JOY2    0b11110011    // IO A0..A5   
#define SYNCH_HI        0
#define SYNCH_LO        1
#define HANDSHAKE       2
#define READ            3
#define WAIT_LO         4
#define WAIT_RES        5

KeyReport _keyReport;
uint32_t counter = 0;
uint8_t Joy, MemoJoy1, MemoJoy2, state, bitn, key, fn,keydown, ktab[0x68]={
  // Tilde, 1-9, 0, sz, Accent, backslash, Num 0 (00 - 0F)
  0x35, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x2D, 0x2E, 0x31,    0, 0x62,
  // Q bis +, -, Num 1, Num 2, Num3 (10 - 1F)
  0x14, 0x1A, 0x08, 0x15, 0x17, 0x1C, 0x18, 0x0C, 0x12, 0x13, 0x2F, 0x30, 0   , 0x59, 0x5A, 0x5B,
  // A-#, -, Num 4, Num 5, Num 6 (20 - 2F)
  0x04, 0x16, 0x07, 0x09, 0x0A, 0x0B, 0x0D, 0x0E, 0x0F, 0x33, 0x34, 0x32, 0,    0x5C, 0x5D, 0x5E,
  // <>,Y- -, -, Num . , Num 7, Num 8, Num 9 (30 - 3F)
  0x64, 0x1D, 0x1B, 0x06, 0x19, 0x05, 0x11, 0x10, 0x36, 0x37, 0x38,    0, 0x63, 0x5F, 0x60, 0x61,
  // Space, BS, Tab, Enter, Return, ESC, Del, -, -, -, Num -, -, up, down, right, left (40 - 4F)
  0x2C, 0x2A, 0x2B, 0x58, 0x28, 0x29, 0x4C,    0,    0,    0, 0x56,    0, 0x52, 0x51, 0x4F, 0x50,
  // F1-F10, -, -, Num /, Num *, Num +, - (50 - 5F)
  0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43,    0,    0, 0x54, 0x55, 0x57,    0,
  // modifiers: Shift left, Shift right, -, Crtl left, Alt left, Alt right, Win (amiga) left, Ctrl (amiga)right
  0x02, 0x20, 0x00, 0x01, 0x04, 0x40, 0xE3, 0xE7

void setup() {
  // Joystick 1 (Port D)
  DDRD = ~(BITMASK_JOY1); // direction INPUT
  PORTD = BITMASK_JOY1; // activate PULLUP
  // Joystick 2 (Port F)
  DDRF = ~(BITMASK_JOY2); // direction INPUT
  PORTF = BITMASK_JOY2; // activate PULLUP

  // Keyboard (Port B)
  DDRB = ~(BITMASK_A500CLK | BITMASK_A500SP | BITMASK_A500RES);  // direction INPUT

  //numlock LED
  pinMode(5, OUTPUT);

void loop() {
  // Joystick 1
  if (Joy != MemoJoy1) {
    HID_SendReport(3, &Joy, 1);
    MemoJoy1 = Joy;

// Joystick 2
  if (Joy != MemoJoy2) {
    HID_SendReport(4, &Joy, 1);
    MemoJoy2 = Joy;
  // Keyboard

//   if (((PINB & BITMASK_A500RES)==0) && state!=WAIT_RES) {   // Reset
//    interrupts();                   //this doesn't work with 4 pin A2000 keyboards
//    keystroke(0x4C,05);        // CTRL+ALT+DEL
//    fn=0;
//    state=WAIT_RES;
//  }
//  else if (state==WAIT_RES) {   // Waiting for reset end
//    if ((PINB & BITMASK_A500RES)!=0) state=SYNCH_HI;
//  }
//  else if
   if (state==SYNCH_HI) {   // Sync-Pulse HI
    if ((PINB & BITMASK_A500CLK)==0) state=SYNCH_LO;

  else if (state==SYNCH_LO) {   // Sync-Pulse LOW
    if ((PINB & BITMASK_A500CLK)!=0) state=HANDSHAKE;

  else if (state==HANDSHAKE) {  // Handshake
    if (counter==0) {
      DDRB |= BITMASK_A500SP;   // set IO direction to OUTPUT
      PORTB &= ~BITMASK_A500SP; // set OUTPUT to LOW
    else if (millis()-counter>10) {
      DDRB &= ~BITMASK_A500SP;   // set IO direction to INPUT
  else if (state==READ) {        // read key message (8 bits)
    if ((PINB & BITMASK_A500CLK)!=0) { 
      if (bitn--){
        key+=((PINB & BITMASK_A500SP)==0)<<(bitn); // key code (add bits 0...6)
      else {  // read last bit (key down)   
        keydown=((PINB & BITMASK_A500SP)!=0); // true if key down
        if (key==0x62) keystroke(0x39,0x00);  // CapsLock
        else {
          if (keydown){
            // keydown message received------
              if (key==0x5A) {
                keystroke(0x53,0); // Num lock active without modifier
                if (digitalRead(5)) // Num lock LED on Pin
                  digitalWrite(5, LOW);
                  digitalWrite(5, HIGH);
              else if (key==0x5B) keystroke(0x47,0); // Scroll lock active without modifier
              else if (key==0x5F) keystroke(0x45,0); // Help is F12 to bring up UAE emulator interface
              else if (key < 0x68) keypress(key);  // All remaining keys via Code table
          else {
            // keyrelease message received
            if (key < 0x68) keyrelease(key);  // Code table
  else if (state==WAIT_LO) {   // waiting for the next bit
    if ((PINB & BITMASK_A500CLK)==0) {

void keypress(uint8_t k) {
  if (k > 0x5f) _keyReport.modifiers |= ktab[key];  // modifier
  else { 
    for (uint8_t i=0; i<6; i++) {
      if (_keyReport.keys[i] == 0) {
         _keyReport.keys[i] = ktab[key];

void keyrelease(uint8_t k) {

  if (k > 0x5f) _keyReport.modifiers &= ~ktab[key];  // modifier
  else { 
    for (uint8_t i=0; i<6; i++) {
      if (_keyReport.keys[i] == ktab[key]) _keyReport.keys[i] = 0;

void keystroke(uint8_t k, uint8_t m) {
  unsigned short memomodifiers = _keyReport.modifiers; // save last modifier state
    for (uint8_t i=0; i<6; i++) {
      if (_keyReport.keys[i] == 0) {
         _keyReport.keys[i] = k;
         _keyReport.modifiers = m;
         _keyReport.keys[i] = 0;
         _keyReport.modifiers = memomodifiers; // recover modifier state


Sorry, another question.
My former problem is solved, the controller of the keyboard itself was defect.
Now I added Joystick Ports to the Leonardo, but I don´t know how to configure them under WINUAE.
Can anybody help?

Kind regards



This thing is working like a charm!

Typing this on an A500 keyboard, that's really a wonderful feeling. Thanks, guys!

Got a question. In earlier versions of the Arduino IDE, one could simply add stuff to HID.cpp, but in the later versions, it seems like this has been abstracted into separate places, such as Keyboard.h and Mouse.h. Any ideas where to add the Atari DB9 joystick information nowadays?


Is there anything special that needs to be done to interface this with an A1000 keyboard? I have it running on my Leo, working with an A500 keyboard, but my A1000 keyboard is just outputting garbage.  I got it to send ok data once, but generally, it just spews random characters.   

I do notice a difference electrically in the A500 and A1000 schematics between the keyboard connection and the 8250... (not including the reset circuit of course.)


The A1000 keyboard is the same electronically as the A2000, A3000, and A4000 keyboards. It has been a while, but it seems to me the A500 keyboard is a little different.

Do you have a way to test your A1000 keyboard to confirm it is operating correctly?
Steve Greenfield AE7HD
Drawing Schematics: tinyurl.com/23mo9pf - tinyurl.com/o97ysyx - tinyurl.com/q7uqnvn
Multitasking: forum.arduino.cc/index.php?topic=223286.0
gammon.com.au/blink - gammon.com.au/serial - gammon.com.au/interrupts


Yeah. I'll double check it with my A1000.  I did notice that with the A1000 keyboard, I needed an external power supply for it (the Arduino kept on browning-out) and also if i touched the data/clock pins, it would generate more noise.

A couple things I'm going to try (and will report back here afterwards, to document it)

  • Try out the keyboard on my real A1000, confirm it is workign
  • Try out my second A1000 keyboard (it's packed away currently)
  • Hit the data/clock pins on the A500 and A1000 keyboards with my scope to see if they're outputting different voltage levels
  • if all of that looks ok. i'll solder directly to the A1000 keyboard connectors, avoiding the RJ-12 cable and its interconnects... admittedly, this could be a HUGE factor

But yeah, the pullups in the A500 schematic are different than the A1000 (4.7k vs 10k) so that, combined with the RJ-12 cable could be pushing the voltage levels down to the point that the Ardy can't quite read the right levels...


Okay.  I just tried a whole bunch of things...

Just tried two A1000 keyboards on my A1000, and both work perfectly.  Also tried them on my A500 with an adapter I made, and they work as-expected there too. I measured the output from the keyboards.  The A500 keyboard shows a 0 - 2.8v range on its clock and data lines. The A1000 keyboard shows a 0 - 1.9v range on the same lines.  I did connect wiring directly to the A1000 keyboard internally as well, and it showed no difference nor improvement.

So I think I need to build some sort of voltage level converter to boost the A1000 keyboard's signaling range.


How did you measure the output voltages? Oscilloscope?
Steve Greenfield AE7HD
Drawing Schematics: tinyurl.com/23mo9pf - tinyurl.com/o97ysyx - tinyurl.com/q7uqnvn
Multitasking: forum.arduino.cc/index.php?topic=223286.0
gammon.com.au/blink - gammon.com.au/serial - gammon.com.au/interrupts


Jan 17, 2018, 04:25 am Last Edit: Jan 17, 2018, 04:32 am by yorgle
Yes. on a scope. Although I just got a better one on loan from a friend and I've found something interesting... 

The signalling looks the same (Since i've run the A1k keyboard on my A500, i know it's compatible) however, it's MUCH quicker.

Clock pulses on the A500 keyboard are around 50us.  On the A1000 keyboard, they're about 25us, so this code won't work as it is.  I'm looking into optimizing it.

For the A1k keyboard, you also will probably need to clean up the signal by throwing some external pullup resistors.  I'm using 3.3k pullups (as per the A1k schematic) and it squares out the A1000's signal pretty well. The A500 keyboard works fine with these pullups as well.

Updated code soonish... :)  Although I may end up rewriting it...

(and the voltage measurement was wrong. they're both rock solid around 4v)



Hello @all!

As I'm also using a German keyboard layout, I noticed two errors in CypherXG's code.

Code: [Select]

                  if ((key==0x2B) && (_keyReport.modifiers & 0x22)) keystroke(0x35,0x00);  // ^ (with shift)
                  else if (key==0x00) if (_keyReport.modifiers & 0x22) keystroke(0x30,0x40); else keystroke(0x35,0x20); // ~,`
                  else if (key==0x0D) if (_keyReport.modifiers & 0x22) keystroke(0x64,0x40); else keystroke(0x2D,0x40); // |,\
                  else if (key==0x5A) if (_keyReport.modifiers & 0x22) keystroke(0x24,0x40); else keystroke(0x25,0x40); // {,[
                  else if (key==0x5B) if (_keyReport.modifiers & 0x22) keystroke(0x27,0x40); else keystroke(0x26,0x40); // },]
                  else if (key < 0x68) keypress(key);  // Code table

Has to be replaced with

Code: [Select]

                  if ((key==0x2B) && (_keyReport.modifiers & 0x22)) keystroke(0x35,0x00);  // ^ (with shift)
                  else if (key==0x00) if (_keyReport.modifiers & 0x22) keystroke(0x30,0x40); else keystroke(0x32,0x02); // ~,'
                  else if (key==0x0D) if (_keyReport.modifiers & 0x22) keystroke(0x64,0x40); else keystroke(0x2D,0x40); // |,\
                  else if (key==0x5A) if (_keyReport.modifiers & 0x22) keystroke(0x24,0x40); else keystroke(0x25,0x40); // {,[
                  else if (key==0x5B) if (_keyReport.modifiers & 0x22) keystroke(0x27,0x40); else keystroke(0x26,0x40); // },]
                  else if (key < 0x68) keypress(key);  // Code table

This works with the current WebIDE (Web App Version - 3.0.8 ). Note the additional free line behind the definition of " |,\ " (otherwise the next line will be ignored) and the corrected keystroke for " ~,' " (was actually " ~,° ").

Also, I'm experiencing weird behaviour when using "Ctrl+Amiga+Amiga", regardless of activating the reset code or not. Every time, "Ctrl-Amiga+Amiga" is pressed, the keyboard seems to be stuck at holding "Ctrl+Amiga". I verified this a lot of times, after "Ctrl+Amiga+Amiga" p.e. pressing "d" opens a new virtual desktop in Windows 10. After pressing each of the three keys once (and alone), everything is as normal.

"Ctrl+Amiga" (one, not both Amiga keys) does not produce this "stuck keys" behaviour.

Anyone an idea for a solution?

Greetings from Germany



You're probably seeing that behavior with Ctrl-A-A because you get most of those key-down messages (at least for whatever the first two keys that are pressed, and then the keyboard controller resets itself and toggles the RESET line.  You never actually get the key up messages.  maybe.  I haven't fully figured out the capabilities of the code provided here...

I'm working on new firmware which has slightly different wiring, to improve compatibility with A1000's speed and the A2000/3000 keyboard behaviors.  -- Clock and data connect to pins D0 and D1 instead of D8 and D9.  I am not yet ready to share this, but i will post on this thread when i am. 

A2000/3000 have a different reset (ctrl-a-a) behavior than the A500..  You can simulate this by disconnecting Q1 from the circuit on your keyboard's controller.  It won't be necessary for mine...

Go Up