Go Down

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

Well, the next part of the project would be: A computer keyboard to amiga A500 / 1000 / 2000 / 4000 xd. Some people like me prefers to use a real Amiga, but a working keyboard is my problem right now. So why not build a ps2 PC to ps2 amiga convertor. Ok, you can buy it for only 30 euro but that's the too easy way and i want it "now" xd.
And once the code is written and tested everyone could built one, cheaper then the ones you can buy.

Thanks everyone I have been able successfully  to build this and is  big step towards finishing  my awesome Amiga emulator project!  But I found that the code did not work perfectly with US Qwerty keyboard map, so I have fixed that, and I have also added code for PrtScrn, which was not there.

Code: [Select]
#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, 0x08, 0x10

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

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
    keystroke(0x4C,05);        // CTRL+ALT+DEL
  else if (state==WAIT_RES) {   // Waiting for reset end
    if ((PINB & BITMASK_A500RES)!=0) state=SYNCH_HI;
  else 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==0x5F)  fn=keydown;  // "Help" key: special function on/off
        else if (key==0x62) keystroke(0x39,0x00);  // CapsLock
        else {
          if (keydown){
            // keydown message received------
            if (fn) {
              // special function with "Help" key
              if (key==0x50) keystroke(0x44,0);  // F11
              else if (key==0x51) keystroke(0x45,0);  // F12
              else if (key==0x5A) keystroke(0x53,0);  // NumLock
              else if (key==0x5B) keystroke(0x47,0);  // ScrollLock
              else if (key==0x5D) keystroke(0x46,0);  // PrtSc
            else {
              if (key==0x5A) keystroke(0x26,0x20); // (
              else if (key==0x5B) keystroke(0x27,0x20); // )
              else if (key < 0x68) keypress(key);  // 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

If you are hacking this code or testing your keyboard you may find these resources useful;
  Keyboard Tester - http://www.keyboardtester.com/
  Keyboard Scancode reader - http://delphiforfun.org/programs/utilities/KeyCodes.htm
  Keyboard scancodes explained - http://www.quadibloc.com/comp/scan.htm


Thank you Steve,
I added PrtSc in my  original Sketch and made a hint to your QWERTY Layout in the first  post.
Dipl. Ing. (FH) Olaf Berthold
Hardware- und Softwareentwicklung


Sorry to revive a thread from the dead, but I just picked up an Amiga 2000 and my first ever Arduino.  I am a keyboard enthusiast which is what led me to the Amiga 2000, with the intention that I'd be able to use it based off the information I found in this thread and a Leonardo that I purchased.

Basically, I'm a script kiddie. 

I'm running into an issue, when I "Verify" the Copy Pasta I did from Steve_Reaver I get back an error that 'KeyReport' does not name a type.  Here's the log:

Code: [Select]

Amiga_2000_QWERTY_Copy_Pasta:13: error: 'KeyReport' does not name a type
Amiga_2000_QWERTY_Copy_Pasta.ino: In function 'void setup()':
Amiga_2000_QWERTY_Copy_Pasta:38: error: 'DDRF' was not declared in this scope
Amiga_2000_QWERTY_Copy_Pasta:39: error: 'PORTF' was not declared in this scope
Amiga_2000_QWERTY_Copy_Pasta.ino: In function 'void loop()':
Amiga_2000_QWERTY_Copy_Pasta:50: error: 'HID_SendReport' was not declared in this scope
Amiga_2000_QWERTY_Copy_Pasta:55: error: 'PINF' was not declared in this scope
Amiga_2000_QWERTY_Copy_Pasta:57: error: 'HID_SendReport' was not declared in this scope
Amiga_2000_QWERTY_Copy_Pasta.ino: In function 'void keypress(uint8_t)':
Amiga_2000_QWERTY_Copy_Pasta:146: error: '_keyReport' was not declared in this scope
Amiga_2000_QWERTY_Copy_Pasta:149: error: '_keyReport' was not declared in this scope
Amiga_2000_QWERTY_Copy_Pasta:155: error: '_keyReport' was not declared in this scope
Amiga_2000_QWERTY_Copy_Pasta:155: error: 'HID_SendReport' was not declared in this scope
Amiga_2000_QWERTY_Copy_Pasta.ino: In function 'void keyrelease(uint8_t)':
Amiga_2000_QWERTY_Copy_Pasta:161: error: '_keyReport' was not declared in this scope
Amiga_2000_QWERTY_Copy_Pasta:164: error: '_keyReport' was not declared in this scope
Amiga_2000_QWERTY_Copy_Pasta:167: error: '_keyReport' was not declared in this scope
Amiga_2000_QWERTY_Copy_Pasta:167: error: 'HID_SendReport' was not declared in this scope
Amiga_2000_QWERTY_Copy_Pasta.ino: In function 'void keystroke(uint8_t, uint8_t)':
Amiga_2000_QWERTY_Copy_Pasta:173: error: '_keyReport' was not declared in this scope
Amiga_2000_QWERTY_Copy_Pasta:178: error: 'HID_SendReport' was not declared in this scope

I gotta assume Steve's code is good and since all I did was copy and paste, I gotta assume it's something wrong on my side.

Any input?


So it looks like I need to include a library of some sort, and I've tried all the libraries that the Arduino 1.0.5 IDE comes with but none of them worked. 

Can anyone help?


Mar 02, 2014, 07:49 am Last Edit: Mar 02, 2014, 08:36 am by fl0w3n Reason: 1
Well, as I suspected, the issue was very simple and a case of being a newb.

The IDE was not set to Leonardo, and I needed to "select drivers from list" for the Leonardo and Leonardo Bootloader, and disable my Bluetooth in Device Manager.

Thanks for this code!

I just bought an Arduino Micro and connected an Amiga 2000 keyboard (the Cherry one!) to my MacBook and it's working!

As this keyboard doesn't have separate reset line, I had to remove the parts of the code (about waiting for the reset line) to get it working. Just wanted to point this out in case somebody else tries this with another 5-pin Amiga keyboard.


Bump! I'm trying to use this code on an Amiga 2000 keyboard just like PhysicalPixel but I'm not having much luck removing the rest code. Any enlightenment would be awesome.


I have been using this for a few months now...and recently I am getting stuck repeating keys...anyone else have this problem...I have checked ground and connections...next thing to do is try another arduino leonardo....?

anyway...wonder if any of the code changes that you made to the original have timing issues or if this is just a purely hardware problem with my setup...


Go Up