├── .gitignore ├── Arduino └── AmigaInputDevice │ └── AmigaInputDevice.ino ├── Images ├── 0703_AmigaMouse.png ├── 0703_Box.png ├── 0703_Joystick.png ├── 0703_Parts.png ├── Amiga_to_ST_converters.png ├── D9_pinouts.png ├── Leonardo_Interface_v1.png ├── TI_to_Atari_converter.png └── pinouts.png ├── LICENSE ├── README.md └── reference ├── 00_olaf_QWERTZ.txt ├── 01_steve_reaver_QWERTY.txt └── notes.txt /.gitignore: -------------------------------------------------------------------------------- 1 | #Mac 2 | .DS_Store 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | -------------------------------------------------------------------------------- /Arduino/AmigaInputDevice/AmigaInputDevice.ino: -------------------------------------------------------------------------------- 1 | // AmigaInputDevice 2 | // 3 | // yorgle@gmail.com 4 | // http://geodesicsphere.blogspot.com 5 | // http://yorgle.org 6 | // 7 | // A program for the Arduino Leonardo and other ATmega32u4 based 8 | // devices to read in an Amiga Mouse and Joystick and present them 9 | // as HID USB mouse and keyboard. 10 | // 11 | // This file should be available through the github repository at 12 | // https://github.com/BleuLlama/AmigaInputToUSB 13 | // 14 | // Wiring diagram and such will be available there in image form 15 | //e 16 | // This is distributed under the MIT license. 17 | // No warranty blah blah blah. 18 | 19 | 20 | #define VERSTRING "008 2015-0728" 21 | // version history 22 | // 23 | // v 008 2015-07-28 24 | // v 007 2015-07-20 Atari ST Mouse support tested. Pin define cleanups 25 | // v 006 2015-07-13 Joystick-Keypress support complete with various preset configurations 26 | // v 005 2015-07-12 EEprom saving of settings 27 | // v 004 2015-07-04 Serial Control Shell 28 | // v 003 2015-07-03 Pinout updated to mtch v1 hardware 29 | // v 002 2015-07-02 L/R/M mouse buttons, modes, Mouse button fix, simple acceleration 30 | // v 001 2015-07-02 initial version 31 | // 32 | 33 | //////////////////////////////////////////////////////////////////////////////// 34 | /* 35 | hookup info (tentative) 36 | 37 | Amiga Atari Atari 38 | D9 pin Mouse Mouse Joystick Arduino Pin 39 | 40 | 1 V Xb Forward D6 41 | 2 H Xa Backward D5 42 | 3 VQ Ya Left D4 43 | 4 HQ Yb Right D3 44 | 5 M But n/c n/c D2 45 | 46 | 6 L But L But Button 1 D7 47 | 7 +5V +5V (VCC) 48 | 8 GND GND (GND) 49 | 9 R But R But Button 2 D14 50 | 51 | Keyboard (TBD) 52 | 53 | NOTE: Also tie D6, D7, D8 to VCC via 10k Ohm resistor (tentative) 54 | 55 | NOTE: for Atari mouse, signals on D9 pins 1 and 4 functionality are swapped (in software) 56 | */ 57 | //////////////////////////////////////////////////////////////////////////////// 58 | 59 | #include 60 | 61 | // how the D9 is connected to data pins... 62 | #define kD9_1 (6) 63 | #define kD9_2 (5) 64 | #define kD9_3 (4) 65 | #define kD9_4 (3) 66 | #define kD9_5 (2) 67 | #define kD9_6 (7) 68 | /* pins 7,8 are connected to power, ground */ 69 | #define kD9_9 (14) 70 | 71 | // pin configurations (how it's wired to logical signal lines...) 72 | // Amiga Mouse 73 | #define kMouseAmigaV (kD9_1) 74 | #define kMouseAmigaH (kD9_2) 75 | #define kMouseAmigaVQ (kD9_3) 76 | #define kMouseAmigaHQ (kD9_4) 77 | 78 | // Atari mouse 79 | #define kAtariMouseXb (kD9_1) 80 | #define kAtariMouseXa (kD9_2) 81 | #define kAtariMouseYa (kD9_3) 82 | #define kAtariMouseYb (kD9_4) 83 | 84 | // Mouse buttons (common for both) 85 | #define kMouseB1 (kD9_6) /* left button */ 86 | #define kMouseB2 (kD9_9) /* right button */ 87 | #define kMouseB3 (kD9_5) /* middle button */ 88 | 89 | // Joystick pins 90 | #define kJoyUp (kD9_1) 91 | #define kJoyDown (kD9_2) 92 | #define kJoyLeft (kD9_3) 93 | #define kJoyRight (kD9_4) 94 | 95 | // on-board LED pin 96 | #define kLED (17) 97 | 98 | ///////////////////////////// 99 | #define kNSettings (8) 100 | char settings[kNSettings]; 101 | 102 | // 0..2 are 'S' 'D' 'L' (Sentinel) 103 | #define kSettingVers (3) // settings version 104 | #define kSettingMode (4) // D9 Port usage mode 105 | #define kSettingDevice (5) 106 | #define kSettingOutputs (6) 107 | 108 | ///////////////////////////// 109 | // usage modes 110 | #define kModeAmigaMouse (0) /* amiga mouse quad signals, to HID mouse */ 111 | #define kModeAtariMouse (1) /* atari mouse quad signals, to HID mouse */ 112 | #define kModeJoyMouse (2) /* joystick sends LRUD, convert to mouse */ 113 | 114 | /* joystick sends LRUD... */ 115 | #define kModeJoyFSUAE (3) /* convert to arrow keys, right ctrl/alt */ 116 | #define kModeJoyMame (4) /* convert to arrow keys, left ctrl/alt */ 117 | #define kModeJoyStella (5) /* convert to arrow keys, space, 4, 5 */ 118 | #define kModeJoyWASD (6) /* convert to WASD keys */ 119 | #define kModeJoyHJKL (7) /* convert to vi hjkl keys */ 120 | 121 | #define kModeExplore (8) /* Exploration of ideas... */ 122 | #define kModeMax (kModeExplore) 123 | 124 | 125 | // ---------------------------------------- 126 | 127 | // setup - initialize the hardware 128 | void setup() { 129 | pinMode( kLED, OUTPUT ); 130 | 131 | // set the mouse and button inputs 132 | pinMode( kD9_1, INPUT_PULLUP ); 133 | pinMode( kD9_2, INPUT_PULLUP ); 134 | pinMode( kD9_3, INPUT_PULLUP ); 135 | pinMode( kD9_4, INPUT_PULLUP ); 136 | pinMode( kD9_5, INPUT_PULLUP ); 137 | pinMode( kD9_6, INPUT_PULLUP ); 138 | pinMode( kD9_9, INPUT_PULLUP ); 139 | 140 | // put your setup code here, to run once: 141 | Serial.begin( 9600 ); 142 | Mouse.begin(); // for USB HID Mouse support 143 | Keyboard.begin(); // for USB HID Keyboard support 144 | 145 | loadSettings(); 146 | resetExplore(); 147 | } 148 | 149 | void loadSettings( void ) 150 | { 151 | initGrayMouse(); // use this for an Amiga Mouse 152 | // initJoystick(); // use this for a digital joystick (Atari Joystick) 153 | 154 | for( int i=0 ; i 01 -> 11 -> 10 -> 00 279 | // - 00 -> 10 -> 11 -> 01 -> 00 280 | int grayCompare( int a, int b ) 281 | { 282 | a = a & 0x03; 283 | b = b & 0x03; 284 | if( a == b ) return 0; 285 | switch( a ) { 286 | case( 0x00 ): 287 | if( b == 0x01) return +1; 288 | if( b == 0x10) return -1; 289 | break; 290 | case( 0x01 ): 291 | if( b == 0x11) return +1; 292 | if( b == 0x00) return -1; 293 | break; 294 | case( 0x11 ): 295 | if( b == 0x10) return +1; 296 | if( b == 0x01) return -1; 297 | break; 298 | case( 0x10 ): 299 | if( b == 0x00) return +1; 300 | if( b == 0x11) return -1; 301 | break; 302 | } 303 | return 0; 304 | } 305 | 306 | void dumpMode() 307 | { 308 | switch( settings[kSettingMode] ) { 309 | // -> Mouse 310 | case( kModeAmigaMouse ): Serial.println( "Amiga mouse as HID mouse" ); break; 311 | case( kModeAtariMouse ): Serial.println( "Atari mouse as HID mouse" ); break; 312 | case( kModeJoyMouse ): Serial.println( "Atari joystick as HID mouse" ); break; 313 | 314 | // -> Keyboard 315 | case( kModeJoyFSUAE ): Serial.println( "Atari Joystick as Arrow/r-ctrl/r-alt (FS-UAE, Vice)" ); break; 316 | case( kModeJoyMame ): Serial.println( "Atari Joystick as Arrow/l-ctrl/l-alt (Mame)" ); break; 317 | case( kModeJoyStella ): Serial.println( "Atari Joystick as Arrow/ /4/5 (Stella)" ); break; 318 | case( kModeJoyWASD ): Serial.println( "Atari joystick as WASD keys" ); break; 319 | case( kModeJoyHJKL ): Serial.println( "Atari joystick as vi (hjkl) keys" ); break; 320 | 321 | case( kModeExplore ): Serial.println( "Controller explorer" ); break; 322 | default: break; 323 | } 324 | } 325 | 326 | // stupid serial shell to change usage mode 327 | void serialShell() 328 | { 329 | while( Serial.available() ) { 330 | int ch = Serial.read(); 331 | if( ch == '\n' ) { 332 | Serial.println( "\n> " ); 333 | Serial.flush(); 334 | } 335 | if( ch == '?' || ch == 'h' ) { 336 | Serial.println( "Select mode (type the digit.)" ); 337 | Serial.println( " 0 USB Mouse output from Amiga mouse" ); 338 | Serial.println( " 1 USB Mouse output from Atari mouse" ); 339 | Serial.println( " 2 USB Mouse output from Atari joystick" ); 340 | 341 | Serial.println( " 3 Joystick as FS-UAE/Vice Keyboard (arrows/r-ctrl/r-alt)" ); 342 | Serial.println( " 4 Joystick as MAME Keyboard (arrows/l-ctrl/l-alt)" ); 343 | Serial.println( " 5 Joystick as Stella Keyboard (arrows/ /4/5)" ); 344 | Serial.println( " 6 Joystick as WASD Keyboard (w/s/a/d)" ); 345 | Serial.println( " 7 Joystick as vi Keyboard (h/j/k/l)" ); 346 | 347 | Serial.println( " 8 Controller Explorer" ); 348 | Serial.println( " r Reset the Explorer" ); 349 | 350 | Serial.println( "" ); 351 | Serial.println( "Other options:" ); 352 | Serial.println( " h Help info." ); 353 | Serial.println( " v Version info." ); 354 | Serial.println( " m Display use mode." ); 355 | Serial.println( " d Dump settings from EEPROM." ); 356 | Serial.println( " i initialize settings in EEPROM." ); 357 | } 358 | if( ch == 'r' ) { 359 | resetExplore(); 360 | Serial.println( "Explorer reset." ); 361 | } 362 | if( ch == 'i' ) { 363 | defaultSettings(); 364 | } 365 | if( ch == 'd' ) { 366 | dumpSettings(); 367 | } 368 | if( ch == 'm' ) { 369 | dumpMode(); 370 | } 371 | if( ch == 'v' ) { 372 | Serial.println( "Amiga Input Device tool" ); 373 | Serial.print( " v" ); 374 | Serial.print( VERSTRING ); 375 | Serial.println( " (c) 2015 yorgle@gmail.com" ); 376 | Serial.println( " created for the 2015/07 http://retrochallenge.org" ); 377 | } 378 | 379 | if( ch >= ('0' + kModeAmigaMouse) && ch <= ('0' + kModeMax ) ) { 380 | switchMode( ch - '0' ); 381 | dumpMode(); 382 | } 383 | } 384 | } 385 | 386 | 387 | ////////////////////////////////////////////////////// 388 | // main loop 389 | void loop() { 390 | if( Serial.available() ) { 391 | serialShell(); 392 | } 393 | switch( settings[kSettingMode] ) { 394 | case( kModeAmigaMouse ): 395 | case( kModeAtariMouse ): 396 | loopGrayMouse(); 397 | break; 398 | case( kModeJoyMouse ): 399 | loopJoyMouse(); 400 | break; 401 | case( kModeJoyFSUAE ): 402 | case( kModeJoyMame ): 403 | case( kModeJoyStella ): 404 | case( kModeJoyWASD ): 405 | case( kModeJoyHJKL ): 406 | loopJoyKeys(); 407 | break; 408 | 409 | case( kModeExplore ): 410 | loopExplore(); 411 | break; 412 | 413 | default: 414 | break; 415 | } 416 | } 417 | 418 | ////////////////////////////////////////////////////// 419 | // handler to read the mouse buttons. 420 | // broke this out since it's the same for mouse vs joystick 421 | void handleButtonPresses() 422 | { 423 | // b1 = left 424 | // b2 = right 425 | // b3 = middle 426 | 427 | // left 428 | static int lb1 = LOW; 429 | int b1 = digitalRead( kMouseB1 ); 430 | 431 | if( b1 != lb1 ) { 432 | if( b1 == LOW ) { 433 | Mouse.press( MOUSE_LEFT ); 434 | delay( 50 ); // fakeo debounce 435 | } 436 | if( b1 == HIGH ) Mouse.release( MOUSE_LEFT ); 437 | } 438 | 439 | // necessary to prevent mouse fighting 440 | //if( b1 == HIGH && Mouse.isPressed( MOUSE_LEFT )) { 441 | // Mouse.release( MOUSE_LEFT ); 442 | //} 443 | 444 | lb1 = b1; 445 | 446 | 447 | // right 448 | static int lb2 = LOW; 449 | int b2 = digitalRead( kMouseB2 ); 450 | 451 | if( b2 != lb2 ) { 452 | if( b2 == LOW ) { 453 | Mouse.press( MOUSE_RIGHT ); 454 | delay( 50 ); // fakeo debounce 455 | } 456 | if( b2 == HIGH ) Mouse.release( MOUSE_RIGHT ); 457 | } 458 | // necessary to prevent mouse fighting 459 | //if( b2 == HIGH && Mouse.isPressed( MOUSE_RIGHT )) { 460 | // Mouse.release( MOUSE_RIGHT ); 461 | //} 462 | lb2 = b2; 463 | 464 | 465 | // middle 466 | static int lb3 = LOW; 467 | int b3 = digitalRead( kMouseB3 ); 468 | 469 | if( b3 != lb3 ) { 470 | if( b3 == LOW ) { 471 | Mouse.press( MOUSE_MIDDLE ); 472 | delay( 50 ); // fakeo debounce 473 | } 474 | if( b3 == HIGH ) Mouse.release( MOUSE_MIDDLE ); 475 | } 476 | // necessary to prevent mouse fighting 477 | //if( b3 == HIGH && Mouse.isPressed( MOUSE_MIDDLE )) { 478 | // Mouse.release( MOUSE_MIDDLE ); 479 | //} 480 | lb3 = b3; 481 | } 482 | 483 | 484 | ////////////////////////////////////////////////////// 485 | // quadrature gray as HID mouse 486 | 487 | // the main gray mouse loop 488 | void loopGrayMouse() 489 | { 490 | bool changed = false; // did something change? 491 | 492 | // these are static so they persist between calls 493 | static int x_accum = 0; // accumulated X 494 | static int y_accum = 0; // accumulated Y 495 | 496 | static int last_hq = 0; // last h quadrature 497 | static int last_vq = 0; // last v quadrature 498 | 499 | // read in the quad/gray code 500 | int hq = 0; 501 | int vq = 0; 502 | 503 | if( settings[kSettingMode] == kModeAmigaMouse ) { 504 | // amiga mouse 505 | hq = (digitalRead( kMouseAmigaH ) << 1) | digitalRead( kMouseAmigaHQ ); 506 | vq = (digitalRead( kMouseAmigaV ) << 1) | digitalRead( kMouseAmigaVQ ); 507 | 508 | } else if( settings[kSettingMode] == kModeAtariMouse ) { 509 | // atari mouse ( Xb, Ya swapped wrt Amiga mouse ) 510 | hq = (digitalRead( kAtariMouseXa ) << 1) | digitalRead( kAtariMouseXb ); 511 | vq = (digitalRead( kAtariMouseYb ) << 1) | digitalRead( kAtariMouseYa ); /* vertical swapped! */ 512 | } 513 | 514 | // check horizontal delta 515 | if( hq != last_hq ) { 516 | x_accum += grayCompare( hq, last_hq ); 517 | changed = true; 518 | } 519 | last_hq = hq; 520 | 521 | // and vertical delta 522 | if( vq != last_vq ) { 523 | y_accum += grayCompare( vq, last_vq ); 524 | changed = true; 525 | } 526 | last_vq = vq; 527 | 528 | 529 | // generate the acceleration info from gray tick 530 | historyPos++; 531 | history_x[ (historyPos & 0x7f) ] = x_accum; 532 | history_y[ (historyPos & 0x7f) ] = y_accum; 533 | int tx = total_x() * 2; 534 | int ty = total_y() * 2; 535 | 536 | // if something changed, move the mouse 537 | if( x_accum || y_accum ) { 538 | Mouse.move( tx, ty, 0 ); 539 | x_accum = y_accum = 0; 540 | } 541 | 542 | // and check the mouse buttons too 543 | handleButtonPresses(); 544 | } 545 | 546 | 547 | ////////////////////////////////////////////////////// 548 | // Joystick as mouse 549 | void loopJoyMouse() 550 | { 551 | char u = digitalRead( kJoyUp )?0:-1; 552 | char d = digitalRead( kJoyDown )?0:1; 553 | char l = digitalRead( kJoyLeft )?0:-1; 554 | char r = digitalRead( kJoyRight )?0:1; 555 | 556 | historyPos++; 557 | history_x[ (historyPos & 0x7f) ] = l+r; 558 | history_y[ (historyPos & 0x7f) ] = u+d; 559 | int tx = total_x() / 24; 560 | int ty = total_y() / 24; 561 | 562 | if( u | d | r | l ) { 563 | Mouse.move( tx, ty, 0 ); 564 | } 565 | 566 | // and check the mouse buttons too 567 | handleButtonPresses(); 568 | } 569 | 570 | // compare now with previous. 571 | // send the key press, release, and return the current (to be assigned to previous) 572 | char keyHelper( char now, char previous, char sendKey ) 573 | { 574 | if( !now && previous ) { 575 | // press event 576 | Keyboard.press( sendKey ); 577 | } 578 | if( now && !previous ) { 579 | // release event 580 | Keyboard.release( sendKey ); 581 | } 582 | 583 | return now; 584 | } 585 | 586 | void loopJoyKeys() 587 | { 588 | static char lastU = 0; 589 | static char lastD = 0; 590 | static char lastL = 0; 591 | static char lastR = 0; 592 | static char lastB1 = 0; 593 | static char lastB2 = 0; 594 | static char lastB3 = 0; 595 | char u = digitalRead( kJoyUp ); 596 | char d = digitalRead( kJoyDown ); 597 | char l = digitalRead( kJoyLeft ); 598 | char r = digitalRead( kJoyRight ); 599 | char b1 = digitalRead( kMouseB1 ); 600 | char b2 = digitalRead( kMouseB2 ); 601 | char b3 = digitalRead( kMouseB3 ); 602 | 603 | // ref: https://www.arduino.cc/en/Reference/KeyboardModifiers 604 | char* moves = "UDLR123"; // filler 605 | char movesWASD[] = { 'w', 's', 'a', 'd', ' ', ' ', ' ' }; 606 | char movesHJKL[] = { 'k', 'j', 'h', 'l', KEY_ESC, KEY_ESC, KEY_ESC }; 607 | char movesStella[] = { KEY_UP_ARROW, KEY_DOWN_ARROW, KEY_LEFT_ARROW, KEY_RIGHT_ARROW, ' ', '4', '5' }; 608 | char movesMame[] = { KEY_UP_ARROW, KEY_DOWN_ARROW, KEY_LEFT_ARROW, KEY_RIGHT_ARROW, KEY_LEFT_CTRL, KEY_LEFT_ALT, ' ' }; 609 | char movesFSUAE[] = { KEY_UP_ARROW, KEY_DOWN_ARROW, KEY_LEFT_ARROW, KEY_RIGHT_ARROW, KEY_RIGHT_CTRL, KEY_RIGHT_ALT, ' ' }; 610 | 611 | if( settings[kSettingMode] == kModeJoyWASD ) moves = movesWASD; 612 | if( settings[kSettingMode] == kModeJoyHJKL ) moves = movesHJKL; 613 | if( settings[kSettingMode] == kModeJoyStella ) moves = movesStella; 614 | if( settings[kSettingMode] == kModeJoyMame ) moves = movesMame; 615 | if( settings[kSettingMode] == kModeJoyFSUAE ) moves = movesFSUAE; 616 | 617 | lastU = keyHelper( u, lastU, moves[0] ); 618 | lastD = keyHelper( d, lastD, moves[1] ); 619 | lastL = keyHelper( l, lastL, moves[2] ); 620 | lastR = keyHelper( r, lastR, moves[3] ); 621 | lastB1 = keyHelper( b1, lastB1, moves[4] ); 622 | lastB2 = keyHelper( b2, lastB2, moves[5] ); 623 | lastB3 = keyHelper( b3, lastB3, moves[6] ); 624 | } 625 | 626 | ////////////////////////////////////////////////////// 627 | 628 | bool isJoystick = true; 629 | bool maybeAtari = false; 630 | bool maybeAmiga = false; 631 | int nTransitions = 0; 632 | char lastData = 0xff; 633 | 634 | int nAtari = 0; 635 | int nAmiga = 0; 636 | unsigned char last13, last24, last12, last34; 637 | 638 | void resetExplore() 639 | { 640 | isJoystick = true; 641 | maybeAtari = false; 642 | maybeAmiga = false; 643 | nTransitions = 0; 644 | lastData = 0xff; 645 | nAtari = 0; 646 | nAmiga = 0; 647 | 648 | last13 = last24 = 0x00; // Amiga 649 | last12 = last34 = 0x00; // Atari 650 | } 651 | 652 | void loopExplore() 653 | { 654 | char data = 0x00; 655 | 656 | // read all 4 bits 657 | char d1 = digitalRead( kJoyUp ) ? 0 : 1; 658 | char d2 = digitalRead( kJoyDown ) ? 0 : 1; 659 | char d3 = digitalRead( kJoyLeft ) ? 0 : 1; 660 | char d4 = digitalRead( kJoyRight )? 0 : 1; 661 | 662 | char thisone; 663 | 664 | // Amiga 665 | // check 13 666 | thisone = (d1? 2 : 0) | (d3? 1 : 0); 667 | if( thisone != (last13 & 0x03)) { 668 | last13 = ((last13 << 2 ) & 0xFC) | thisone; 669 | // 01 11 10 00 0x78 670 | // 10 11 01 00 0xB4 671 | if( last13 == 0x78 || last13 == 0xB4 ) nAmiga++; 672 | } 673 | 674 | // check 24 675 | thisone = (d2? 2 : 0) | (d4? 1 : 0); 676 | if( thisone != (last24 & 0x03)) { 677 | last24 = ((last24 << 2 ) & 0xFC) | thisone; 678 | // 01 11 10 00 0x78 679 | // 10 11 01 00 0xB4 680 | if( last24 == 0x78 || last24 == 0xB4 ) nAmiga++; 681 | } 682 | 683 | // Atari 684 | // check 12 685 | thisone = (d1? 2 : 0) | (d2? 1 : 0); 686 | if( thisone != (last12 & 0x03)) { 687 | last12 = ((last12 << 2 ) & 0xFC) | thisone; 688 | // 01 11 10 00 0x78 689 | // 10 11 01 00 0xB4 690 | if( last12 == 0x78 || last12 == 0xB4 ) nAtari++; 691 | } 692 | 693 | // check 34 694 | thisone = (d3? 2 : 0) | (d4? 1 : 0); 695 | if( thisone != (last34 & 0x03)) { 696 | last34 = ((last34 << 2 ) & 0xFC) | thisone; 697 | // 01 11 10 00 0x78 698 | // 10 11 01 00 0xB4 699 | if( last34 == 0x78 || last34 == 0xB4 ) nAtari++; 700 | } 701 | 702 | 703 | // combine them so we can easily detect change 704 | data = d1 | (d2<<1) | (d3<<2) | (d4<<3); 705 | 706 | if( data != lastData ) { 707 | Serial.print( nTransitions++ ); 708 | Serial.print( ": " ); 709 | 710 | // atari pairings 711 | Serial.print( "Atari " ); 712 | Serial.print( d1, DEC ); 713 | Serial.print( d2, DEC ); 714 | Serial.print( " " ); 715 | Serial.print( d3, DEC ); 716 | Serial.print( d4, DEC ); 717 | Serial.print( " " ); 718 | Serial.print( nAtari ); 719 | 720 | // amiga pairings 721 | Serial.print( " Amiga " ); 722 | Serial.print( d4, DEC ); 723 | Serial.print( d2, DEC ); 724 | Serial.print( " " ); 725 | Serial.print( d3, DEC ); 726 | Serial.print( d1, DEC ); 727 | Serial.print( " " ); 728 | Serial.print( nAmiga ); 729 | 730 | if( nAtari > nAmiga ) { 731 | maybeAtari = true; 732 | maybeAmiga = false; 733 | } else { 734 | maybeAtari = false; 735 | maybeAmiga = true; 736 | } 737 | 738 | // up+down or left+right are not possible on a joystick 739 | if( d1 && d2 ) isJoystick = false; 740 | if( d3 && d4 ) isJoystick = false; 741 | 742 | Serial.print( " " ); 743 | if( isJoystick ) { 744 | Serial.print( " Joystick " ); 745 | } else { 746 | if( maybeAtari ) Serial.print( " Atari Mouse" ); 747 | if( maybeAmiga ) Serial.print( " Amiga Mouse" ); 748 | } 749 | Serial.println(); 750 | lastData = data; 751 | } 752 | } 753 | -------------------------------------------------------------------------------- /Images/0703_AmigaMouse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleuLlama/AmigaInputToUSB/988d100e117f57368953b2c4f0c90e2f1ed52cc1/Images/0703_AmigaMouse.png -------------------------------------------------------------------------------- /Images/0703_Box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleuLlama/AmigaInputToUSB/988d100e117f57368953b2c4f0c90e2f1ed52cc1/Images/0703_Box.png -------------------------------------------------------------------------------- /Images/0703_Joystick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleuLlama/AmigaInputToUSB/988d100e117f57368953b2c4f0c90e2f1ed52cc1/Images/0703_Joystick.png -------------------------------------------------------------------------------- /Images/0703_Parts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleuLlama/AmigaInputToUSB/988d100e117f57368953b2c4f0c90e2f1ed52cc1/Images/0703_Parts.png -------------------------------------------------------------------------------- /Images/Amiga_to_ST_converters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleuLlama/AmigaInputToUSB/988d100e117f57368953b2c4f0c90e2f1ed52cc1/Images/Amiga_to_ST_converters.png -------------------------------------------------------------------------------- /Images/D9_pinouts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleuLlama/AmigaInputToUSB/988d100e117f57368953b2c4f0c90e2f1ed52cc1/Images/D9_pinouts.png -------------------------------------------------------------------------------- /Images/Leonardo_Interface_v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleuLlama/AmigaInputToUSB/988d100e117f57368953b2c4f0c90e2f1ed52cc1/Images/Leonardo_Interface_v1.png -------------------------------------------------------------------------------- /Images/TI_to_Atari_converter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleuLlama/AmigaInputToUSB/988d100e117f57368953b2c4f0c90e2f1ed52cc1/Images/TI_to_Atari_converter.png -------------------------------------------------------------------------------- /Images/pinouts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleuLlama/AmigaInputToUSB/988d100e117f57368953b2c4f0c90e2f1ed52cc1/Images/pinouts.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Scott Lawrence 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Amiga Input To USB 2 | 3 | This is a project to connect a Commodore Amiga mouse, Atari joystick 4 | and maybe an Amiga Keyboard as USB HID devices. 5 | 6 | # Overview 7 | 8 | The purpose of this project is to make and document a small, 9 | inexpensive device that you can use to hook up some Commodore Amiga 10 | and Atari input peripherals to modern computers with USB inputs. 11 | It presents itself to the modern host computer as a HID USB keyboard 12 | and HID USB mouse. 13 | 14 | It has the following supported ports: 15 | 16 | - D9 Male mouse/joystick port 17 | - 8 pin Amiga 500 keyboard port. (future) 18 | - Adapters can be made for: 19 | - 4P4C (RJ-22) Amiga 1000 Keyboard port 20 | - 5-Pin DIN connector can be used for A2000/3000 keyboard 21 | - 6-Pin mini-DIN for A4000 keyboard 22 | 23 | It supports the following schemes: 24 | 25 | - Amiga mouse as HID mouse 26 | - Atari mouse as HID mouse 27 | - Atari Joystick as HID mouse 28 | - Atari Joystick as various HID Keyboard control sets 29 | - Amiga Keyboard as HID Keyboard (future) 30 | 31 | This was originally made for the http://retrochallenge.org Retro 32 | Challenge 2015/7. 33 | 34 | # Theory of operation 35 | 36 | The entire design is based around the ATmega 32u4 microcontroller 37 | with the Arduino bootloader installed. This part with support 38 | hardware can be purchased under the name "Arduino Leonardo", or 39 | "Arduino Pro Micro". The Arduino libraries take care of implementing 40 | the full USB HID stacks necessary for the device to present itself 41 | as a common HID keyboard or common HID mouse. ("Authentic" boards 42 | can be purchased for $10-$25 each, or through some Chinese production 43 | places at about $2 for a Pro Micro.) 44 | 45 | The additional hardware necessary to hook these boards up to a 46 | device is basically a male D9 connector, for the mouse or joystick 47 | to plug into. For an Amiga keyboard interface, the necessary 48 | hardware would be an 4P4C (RJ-22) jack (telephone handset), 5-pin 49 | DIN jack, or 6-pin mini-DIN Jack for various Amiga keyboard models. 50 | The native interface connection on the prototyped board uses the 51 | Amiga 500 keyboard pinout, so that is also an option. (They all 52 | speak the same protocol. 53 | 54 | When plugged in, the device will appear to be a USB Keyboard, USB 55 | Mouse and USB Serial Port. (The serial port is used for configuration.) 56 | 57 | Amiga and Atari mice output quadrature for X and Y using two lines 58 | each in gray code. As you move positive X or Y, it outputs on two 59 | data lines the gray code sequence over time: 00 01 11 10 00 and so 60 | on. for negative movement, it outputs the sequence 00 10 11 01 00 61 | etc. By sitting in a tight loop, we can determine these X and Y 62 | movements, and output them using the Arduino's Mouse library. 63 | 64 | Those two function identically, although the Atari mouse essentially 65 | swaps the wiring found on the D9 connector's pin 1 and 4. 66 | 67 | # Development Phases 68 | 69 | ## Prototype / Proof of concept (complete) 70 | - Simple implementation of Amiga mouse movement and button clicking 71 | 72 | ## Amiga Mouse full implementation (complete) 73 | - Left, Right, Middle Mouse button supported 74 | - Movement with reasonable acceleration 75 | 76 | ## Atari ST Mouse full implementation (complete) 77 | - Left, Right buttons supported 78 | - Movement with reasonable acceleration 79 | - pin defines operate the same as Amiga mouse but D9.1 and D9.4 are swapped 80 | 81 | ## Atari Joystick as mouse (complete) 82 | - Joystick movement translated to Mouse movements (like joymouse) 83 | - Joystick button(s) convert to mouse button presses 84 | - Movement with reasonable acceleration 85 | 86 | ## Serial configuration (complete) 87 | - simple serial shell to change configuration parameters 88 | - display currently configured modes 89 | - stored in EEPROM 90 | 91 | ## Atari Joystick as keyboard (complete) 92 | - Joytick movements mapped as keyboard 93 | - Support via preset key configurations for: FS-UAE, Mame, Stella, Vice, WASD, and vi 94 | - FS-UAE: Arrow keys for movement, right ctrl, right alt for buttons 95 | - MAME: Arrow keys for movement, left ctrl, left alt, space for buttons 96 | - WASD: WASD for movement, space bar, ctrl, shift 97 | - vi: HJKL for movement, (yes, it's a joke. yes, it's usable!) 98 | 99 | ## Autodetect Input Device (in progress) 100 | - Start up in this mode, it will automatically figure out if there is an Atari Mouse, Atari Joystick or Amiga mouse plugged in based on the first few movements. 101 | 102 | ## Amiga Keyboard (future, maybe) 103 | - Support for using an Amiga keyboard as a HID USB keyboard 104 | - All Amiga keyboards all use the same protocol, but different connectors 105 | - My hardware uses an 8 position header for the interface, wired as an A500 keyboard port 106 | - Interface cables can be made to connect to 107 | - Amiga 1000 keyboards (4P4C (RJ-22) jack) 108 | - Amiga 2000,3000 keyboards (5-pin DIN jack) 109 | - Amiga 4000 keyboards (6-pin mini-DIN jack) 110 | 111 | # Future possibilities/features 112 | - Pushbuttons with 7 segment display to select mode without serial interface 113 | - N port input (instead of 1 port) via use of a series of 8 bit 114 | parallel in, serial out shift register (74HC165). This could be a 115 | modular, chainable board, where you plug one into the next. Make 116 | 4 identical boards so that Atari 800 M.U.L.E. would be properly 117 | emulated... or make 3, use two for user joysticks, one for Mouse 118 | input, etc. 119 | 120 | # Joystick Support: 121 | ## Supported 122 | - Atari 2600 VCS - supported 123 | - Atari 7800 - somewhat supported (uses B2, B3) 124 | 125 | ## Could be supported with code rework (not currently supported) 126 | - Odyssey - Possible, requires rework of the code 127 | 128 | ## Require rewiring to work (not supported) 129 | - TI 99/4A - Not possible. Requires rewiring 130 | - Sega Master System - Requires 5v on pin 5 131 | - Sega Genesis - Requires 5v on pin 5, rework 132 | - Sega Saturn - Requires rewiring and code rework 133 | 134 | 135 | # Autodetect Feature Theory 136 | 137 | Essentially the idea is that we will profile sets of signals coming 138 | in to determine what they look the most like. 139 | 140 | Joystick data has pairs (up, down) and (left, right) which are 141 | mutually exclusive. That means that you can never have it sending 142 | "up" and "down" at the same time. If we ever see anything like that, 143 | we know it's a mouse plugged in to the input. Mice also generate 144 | a substantial amount more traffic. 1 second of mouse traffic might 145 | be 200 changes of data, whereas the same 1 second of Joystick traffic 146 | is only a few dozen changes. 147 | 148 | If it's a mouse, we can profile pairs of pins to determine if it's 149 | an Amiga or Atari ST mouse. For each of the Amiga pairs (1,3) and (2,4) 150 | and similarly for each of the Atari pairs (1,2) and (3,4), we have an 151 | 8 bit value that contains the past 4 sequence changes. The basic 152 | loop is something like this: 153 | 154 | value = 0; 155 | // store 1,2 as the bottom two bits of "value" 156 | if( readPin(1) ) value |= 0010 157 | if( readPin(2) ) value |= 0001 158 | 159 | // the bottom most 3 bits of "last value" are the 160 | // previous value. 161 | if( (lastValue & 0000 0011) != value )) 162 | { 163 | // it was different. 164 | // shift last value over, add the current value to it 165 | lastValue = (lastValue <<2 ) & 1111 1100; 166 | lastValue |= value; 167 | 168 | // now the tricky part, we do simple checks for the 169 | // last 4 history values (aka "lastValue") for 170 | // something that looks like a mouse gray code sequence 171 | 172 | if( ( lastValue == 10 11 01 00 ) 173 | || ( lastValue == 01 11 10 00 ) ) { 174 | // it was an Atari ST Mouse sequence 175 | atariTransitionDetected++; 176 | } 177 | } 178 | 179 | 180 | We do this for all four pairs of the possible quadrature/gray code 181 | sequences. We certainly get a bunch of false positives, but we get 182 | substantially more true positives. 183 | 184 | After reading a hundred or so readings, we can easily tell by 185 | comparing "atariTransitionDetected" with "amigaTransitionDetected" 186 | to know which it is. There is a substantial difference between 187 | them. It is remarkably reliable! 188 | 189 | 190 | # Current Status Notes Log 191 | 192 | ### 2015/07/28 193 | 194 | Implemented the first run through of the autodetect code in the 195 | "explorer" section of the firmware, and determined that the idea 196 | actually works well! I added the writeup above to explain the 197 | theory. Now I just need to edit the firmware to work this in to 198 | the startup routine. 199 | 200 | ### 2015/07/20 201 | 202 | My Atari ST mouse has arrived in the mail today! I have tweaked the 203 | code, cleaned it up a little, and confirmed that the firmware works 204 | with this mouse. Images have been added to the project to show the 205 | Leonardo hookup diagram, as well as diagrams for converting between 206 | Amiga and Atari mice. 207 | 208 | ### 2015/07/15 209 | 210 | The initial version is done, minus keyboard support. I've been 211 | having power problems getting the Amiga keyboards to be powerable 212 | off of the Arduino/USB power. 213 | 214 | I need to make a small 8 pin adapter to "inject" power into the 215 | cord for the keyboards. Then I need to figure out how to properly 216 | read from the keyboard and adapt it appropriately. There is example 217 | code which I've included in this project that does this, however, 218 | I may end up rewriting it from scratch to get a better understanding 219 | of how it works. 220 | 221 | Last night I did sketch out ideas in figuring out how to autodetect 222 | between Atari Joystick, Amiga Mouse and Atari Mouse. I think that 223 | just by doing some simple analysis of the signals coming in, it 224 | should be possible to determine the device type. When it powers on, 225 | the device will go into a "detection" state. In this state, it waits for 226 | N changes for the U/D/L/R or V/H/VQ/HQ (Amiga) or HQ/H/VQ/V (Atari) 227 | data bits. By profiling these, it should be trivial to detect between 228 | mouse and joystick. By profiling a little more, it should be possible 229 | to detect between Atari and Amiga mice. 230 | 231 | If we look at the signal sets, we can determine easily if it's not 232 | a joystick. On a joystick it is impossible for (Up and Down) to be 233 | low at the same time... same with (Left and Right). If we ever see 234 | a case where either of those two situations occur, we know it's not a 235 | joystick. 236 | 237 | From there, we can profile the data in 4 pairs: (1,3), (2,4), (1,2), 238 | (3,4). By looking at the data, we can determine which of those 239 | four have had only valid transition sequences: (forward or backwards 240 | in the following two bit gray code sequence) 241 | 242 | 00 - 01 - 11 - 10 - 00 243 | 244 | If the valid transitions only happen in the (1,3) and (2,4) 245 | pairs, then it is an Amiga Mouse. If the valid transitions only 246 | happen in the (1,2) and (3,4) pairs, then we know it's an Atari 247 | mouse. 248 | -------------------------------------------------------------------------------- /reference/00_olaf_QWERTZ.txt: -------------------------------------------------------------------------------- 1 | #define BITMASK_A500CLK 0b00010000 // IO 8 2 | #define BITMASK_A500SP 0b00100000 // IO 9 3 | #define BITMASK_A500RES 0b01000000 // IO 10 4 | #define BITMASK_JOY1 0b10011111 // IO 0..4,6 5 | #define BITMASK_JOY2 0b11110011 // IO A0..A5 6 | #define SYNCH_HI 0 7 | #define SYNCH_LO 1 8 | #define HANDSHAKE 2 9 | #define READ 3 10 | #define WAIT_LO 4 11 | #define WAIT_RES 5 12 | 13 | KeyReport _keyReport; 14 | uint32_t counter = 0; 15 | uint8_t Joy, MemoJoy1, MemoJoy2, state, bitn, key, fn,keydown, ktab[0x68]={ 16 | // Tilde, 1-9, 0, sz, Accent, backslash, Num 0 (00 - 0F) 17 | 0x35, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x2D, 0x2E, 0x31, 0, 0x62, 18 | // Q bis +, -, Num 1, Num 2, Num3 (10 - 1F) 19 | 0x14, 0x1A, 0x08, 0x15, 0x17, 0x1C, 0x18, 0x0C, 0x12, 0x13, 0x2F, 0x30, 0 , 0x59, 0x5A, 0x5B, 20 | // A-#, -, Num 4, Num 5, Num 6 (20 - 2F) 21 | 0x04, 0x16, 0x07, 0x09, 0x0A, 0x0B, 0x0D, 0x0E, 0x0F, 0x33, 0x34, 0x32, 0, 0x5C, 0x5D, 0x5E, 22 | // <>,Y- -, -, Num . , Num 7, Num 8, Num 9 (30 - 3F) 23 | 0x64, 0x1D, 0x1B, 0x06, 0x19, 0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0, 0x63, 0x5F, 0x60, 0x61, 24 | // Space, BS, Tab, Enter, Return, ESC, Del, -, -, -, Num -, -, up, down, right, left (40 - 4F) 25 | 0x2C, 0x2A, 0x2B, 0x58, 0x28, 0x29, 0x4C, 0, 0, 0, 0x56, 0, 0x52, 0x51, 0x4F, 0x50, 26 | // F1-F10, -, -, Num /, Num *, Num +, - (50 - 5F) 27 | 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0, 0, 0x54, 0x55, 0x57, 0, 28 | // modifiers: Shift left, Shift right, -, Crtl left, Alt left, Alt right, Win (amiga) left, Ctrl (amiga)right 29 | 0x02, 0x20, 0x00, 0x01, 0x04, 0x40, 0x08, 0x10 30 | }; 31 | 32 | void setup() { 33 | // Joystick 1 (Port D) 34 | DDRD = ~BITMASK_JOY1; // direction INPUT 35 | PORTD = BITMASK_JOY1; // activate PULLUP 36 | 37 | // Joystick 2 (Port F) 38 | DDRF = ~BITMASK_JOY2; // direction INPUT 39 | PORTF = BITMASK_JOY2; // activate PULLUP 40 | 41 | // Keyboard (Port B) 42 | DDRB = ~(BITMASK_A500CLK | BITMASK_A500SP | BITMASK_A500RES); // direction INPUT 43 | } 44 | 45 | 46 | void loop() { 47 | // Joystick 1 48 | Joy = ~PIND & BITMASK_JOY1; 49 | if (Joy != MemoJoy1) { 50 | HID_SendReport(3, &Joy, 1); 51 | MemoJoy1 = Joy; 52 | } 53 | 54 | // Joystick 2 55 | Joy = ~PINF & BITMASK_JOY2; 56 | if (Joy != MemoJoy2) { 57 | HID_SendReport(4, &Joy, 1); 58 | MemoJoy2 = Joy; 59 | } 60 | 61 | // Keyboard 62 | if (((PINB & BITMASK_A500RES)==0) && state!=WAIT_RES) { // Reset 63 | interrupts(); 64 | keystroke(0x4C,05); // CTRL+ALT+DEL 65 | fn=0; 66 | state=WAIT_RES; 67 | } 68 | 69 | else if (state==WAIT_RES) { // Waiting for reset end 70 | if ((PINB & BITMASK_A500RES)!=0) state=SYNCH_HI; 71 | } 72 | 73 | else if (state==SYNCH_HI) { // Sync-Pulse HI 74 | if ((PINB & BITMASK_A500CLK)==0) state=SYNCH_LO; 75 | } 76 | 77 | else if (state==SYNCH_LO) { // Sync-Pulse LOW 78 | if ((PINB & BITMASK_A500CLK)!=0) state=HANDSHAKE; 79 | } 80 | 81 | else if (state==HANDSHAKE) { // Handshake 82 | if (counter==0) { 83 | DDRB |= BITMASK_A500SP; // set IO direction to OUTPUT 84 | PORTB &= ~BITMASK_A500SP; // set OUTPUT to LOW 85 | counter=millis(); 86 | } 87 | else if (millis()-counter>10) { 88 | counter=0; 89 | DDRB &= ~BITMASK_A500SP; // set IO direction to INPUT 90 | state=WAIT_LO; 91 | key=0; 92 | bitn=7; 93 | } 94 | } 95 | 96 | else if (state==READ) { // read key message (8 bits) 97 | if ((PINB & BITMASK_A500CLK)!=0) { 98 | if (bitn--){ 99 | key+=((PINB & BITMASK_A500SP)==0)<<(bitn); // key code (add bits 0...6) 100 | state=WAIT_LO; 101 | } 102 | else { // read last bit (key down) 103 | keydown=((PINB & BITMASK_A500SP)!=0); // true if key down 104 | interrupts(); 105 | state=HANDSHAKE; 106 | if (key==0x5F) fn=keydown; // "Help" key: special function on/off 107 | else if (key==0x62) keystroke(0x39,0x00); // CapsLock 108 | else { 109 | if (keydown){ 110 | // keydown message received 111 | if (fn) { 112 | // special function with "Help" key 113 | if (key==0x50) keystroke(0x44,0); // F11 114 | else if (key==0x51) keystroke(0x45,0); // F12 115 | else if (key==0x5A) keystroke(0x53,0); // NumLock 116 | else if (key==0x5B) keystroke(0x47,0); // ScrollLock 117 | else if (key==0x5D) keystroke(0x46,0); // PrtSc 118 | else if (key==0x02) keystroke(0x14,0x40); // @ 119 | else if (key==0x04) keystroke(0x35,0x02); // ° 120 | } 121 | else { 122 | if ((key==0x2B) && (_keyReport.modifiers & 0x22)) keystroke(0x35,0x00); // ^ (with shift) 123 | else if (key==0x00) if (_keyReport.modifiers & 0x22) keystroke(0x30,0x40); else keystroke(0x35,0x20); // ~,` 124 | else if (key==0x0D) if (_keyReport.modifiers & 0x22) keystroke(0x64,0x40); else keystroke(0x2D,0x40); // |,BS 125 | else if (key==0x5A) if (_keyReport.modifiers & 0x22) keystroke(0x24,0x40); else keystroke(0x25,0x40); // {,[ 126 | else if (key==0x5B) if (_keyReport.modifiers & 0x22) keystroke(0x27,0x40); else keystroke(0x26,0x40); // },] 127 | else if (key < 0x68) keypress(key); // Code table 128 | } 129 | } 130 | else { 131 | // keyrelease message received 132 | if (key < 0x68) keyrelease(key); // Code table 133 | } 134 | } 135 | } 136 | } 137 | } 138 | 139 | else if (state==WAIT_LO) { // waiting for the next bit 140 | if ((PINB & BITMASK_A500CLK)==0) { 141 | noInterrupts(); 142 | state=READ; 143 | } 144 | } 145 | } 146 | 147 | 148 | void keypress(uint8_t k) { 149 | 150 | if (k > 0x5f) _keyReport.modifiers |= ktab[key]; // modifier 151 | else { 152 | for (uint8_t i=0; i<6; i++) { 153 | if (_keyReport.keys[i] == 0) { 154 | _keyReport.keys[i] = ktab[key]; 155 | break; 156 | } 157 | } 158 | } 159 | HID_SendReport(2,&_keyReport,8); 160 | } 161 | 162 | 163 | void keyrelease(uint8_t k) { 164 | 165 | if (k > 0x5f) _keyReport.modifiers &= ~ktab[key]; // modifier 166 | else { 167 | for (uint8_t i=0; i<6; i++) { 168 | if (_keyReport.keys[i] == ktab[key]) _keyReport.keys[i] = 0; 169 | } 170 | } 171 | HID_SendReport(2,&_keyReport,8); 172 | } 173 | 174 | 175 | void keystroke(uint8_t k, uint8_t m) { 176 | 177 | unsigned short memomodifiers = _keyReport.modifiers; // save last modifier state 178 | for (uint8_t i=0; i<6; i++) { 179 | if (_keyReport.keys[i] == 0) { 180 | _keyReport.keys[i] = k; 181 | _keyReport.modifiers = m; 182 | HID_SendReport(2,&_keyReport,8); 183 | _keyReport.keys[i] = 0; 184 | _keyReport.modifiers = memomodifiers; // recover modifier state 185 | HID_SendReport(2,&_keyReport,8); 186 | break; 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /reference/01_steve_reaver_QWERTY.txt: -------------------------------------------------------------------------------- 1 | #define BITMASK_A500CLK 0b00010000 // IO 8 2 | #define BITMASK_A500SP 0b00100000 // IO 9 3 | #define BITMASK_A500RES 0b01000000 // IO 10 4 | #define BITMASK_JOY1 0b10011111 // IO 0..4,6 5 | #define BITMASK_JOY2 0b11110011 // IO A0..A5 6 | #define SYNCH_HI 0 7 | #define SYNCH_LO 1 8 | #define HANDSHAKE 2 9 | #define READ 3 10 | #define WAIT_LO 4 11 | #define WAIT_RES 5 12 | 13 | KeyReport _keyReport; 14 | uint32_t counter = 0; 15 | uint8_t Joy, MemoJoy1, MemoJoy2, state, bitn, key, fn,keydown, ktab[0x68]={ 16 | // Tilde, 1-9, 0, sz, Accent, backslash, Num 0 (00 - 0F) 17 | 0x35, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x2D, 0x2E, 0x31, 0, 0x62, 18 | // Q bis +, -, Num 1, Num 2, Num3 (10 - 1F) 19 | 0x14, 0x1A, 0x08, 0x15, 0x17, 0x1C, 0x18, 0x0C, 0x12, 0x13, 0x2F, 0x30, 0 , 0x59, 0x5A, 0x5B, 20 | // A-#, -, Num 4, Num 5, Num 6 (20 - 2F) 21 | 0x04, 0x16, 0x07, 0x09, 0x0A, 0x0B, 0x0D, 0x0E, 0x0F, 0x33, 0x34, 0x32, 0, 0x5C, 0x5D, 0x5E, 22 | // <>,Y- -, -, Num . , Num 7, Num 8, Num 9 (30 - 3F) 23 | 0x64, 0x1D, 0x1B, 0x06, 0x19, 0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0, 0x63, 0x5F, 0x60, 0x61, 24 | // Space, BS, Tab, Enter, Return, ESC, Del, -, -, -, Num -, -, up, down, right, left (40 - 4F) 25 | 0x2C, 0x2A, 0x2B, 0x58, 0x28, 0x29, 0x4C, 0, 0, 0, 0x56, 0, 0x52, 0x51, 0x4F, 0x50, 26 | // F1-F10, -, -, Num /, Num *, Num +, - (50 - 5F) 27 | 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0, 0, 0x54, 0x55, 0x57, 0, 28 | // modifiers: Shift left, Shift right, -, Crtl left, Alt left, Alt right, Win (amiga) left, Ctrl (amiga)right 29 | 0x02, 0x20, 0x00, 0x01, 0x04, 0x40, 0x08, 0x10 30 | }; 31 | 32 | void setup() { 33 | // Joystick 1 (Port D) 34 | DDRD = ~BITMASK_JOY1; // direction INPUT 35 | PORTD = BITMASK_JOY1; // activate PULLUP 36 | 37 | // Joystick 2 (Port F) 38 | DDRF = ~BITMASK_JOY2; // direction INPUT 39 | PORTF = BITMASK_JOY2; // activate PULLUP 40 | 41 | // Keyboard (Port B) 42 | DDRB = ~(BITMASK_A500CLK | BITMASK_A500SP | BITMASK_A500RES); // direction INPUT 43 | } 44 | 45 | 46 | void loop() { 47 | // Joystick 1 48 | Joy = ~PIND & BITMASK_JOY1; 49 | if (Joy != MemoJoy1) { 50 | HID_SendReport(3, &Joy, 1); 51 | MemoJoy1 = Joy; 52 | } 53 | 54 | // Joystick 2 55 | Joy = ~PINF & BITMASK_JOY2; 56 | if (Joy != MemoJoy2) { 57 | HID_SendReport(4, &Joy, 1); 58 | MemoJoy2 = Joy; 59 | } 60 | 61 | // Keyboard 62 | if (((PINB & BITMASK_A500RES)==0) && state!=WAIT_RES) { // Reset 63 | interrupts(); 64 | keystroke(0x4C,05); // CTRL+ALT+DEL 65 | fn=0; 66 | state=WAIT_RES; 67 | } 68 | 69 | else if (state==WAIT_RES) { // Waiting for reset end 70 | if ((PINB & BITMASK_A500RES)!=0) state=SYNCH_HI; 71 | } 72 | 73 | else if (state==SYNCH_HI) { // Sync-Pulse HI 74 | if ((PINB & BITMASK_A500CLK)==0) state=SYNCH_LO; 75 | } 76 | 77 | else if (state==SYNCH_LO) { // Sync-Pulse LOW 78 | if ((PINB & BITMASK_A500CLK)!=0) state=HANDSHAKE; 79 | } 80 | 81 | else if (state==HANDSHAKE) { // Handshake 82 | if (counter==0) { 83 | DDRB |= BITMASK_A500SP; // set IO direction to OUTPUT 84 | PORTB &= ~BITMASK_A500SP; // set OUTPUT to LOW 85 | counter=millis(); 86 | } 87 | else if (millis()-counter>10) { 88 | counter=0; 89 | DDRB &= ~BITMASK_A500SP; // set IO direction to INPUT 90 | state=WAIT_LO; 91 | key=0; 92 | bitn=7; 93 | } 94 | } 95 | 96 | else if (state==READ) { // read key message (8 bits) 97 | if ((PINB & BITMASK_A500CLK)!=0) { 98 | if (bitn--){ 99 | key+=((PINB & BITMASK_A500SP)==0)<<(bitn); // key code (add bits 0...6) 100 | 101 | state=WAIT_LO; 102 | } 103 | else { // read last bit (key down) 104 | keydown=((PINB & BITMASK_A500SP)!=0); // true if key down 105 | interrupts(); 106 | state=HANDSHAKE; 107 | if (key==0x5F) fn=keydown; // "Help" key: special function on/off 108 | else if (key==0x62) keystroke(0x39,0x00); // CapsLock 109 | else { 110 | if (keydown){ 111 | // keydown message received------ 112 | if (fn) { 113 | // special function with "Help" key 114 | if (key==0x50) keystroke(0x44,0); // F11 115 | else if (key==0x51) keystroke(0x45,0); // F12 116 | else if (key==0x5A) keystroke(0x53,0); // NumLock 117 | else if (key==0x5B) keystroke(0x47,0); // ScrollLock 118 | else if (key==0x5D) keystroke(0x46,0); // PrtSc 119 | } 120 | else { 121 | if (key==0x5A) keystroke(0x26,0x20); // ( 122 | else if (key==0x5B) keystroke(0x27,0x20); // ) 123 | else if (key < 0x68) keypress(key); // Code table 124 | } 125 | } 126 | else { 127 | // keyrelease message received 128 | if (key < 0x68) keyrelease(key); // Code table 129 | } 130 | } 131 | } 132 | } 133 | } 134 | 135 | else if (state==WAIT_LO) { // waiting for the next bit 136 | if ((PINB & BITMASK_A500CLK)==0) { 137 | noInterrupts(); 138 | state=READ; 139 | } 140 | } 141 | } 142 | 143 | 144 | void keypress(uint8_t k) { 145 | 146 | if (k > 0x5f) _keyReport.modifiers |= ktab[key]; // modifier 147 | else { 148 | for (uint8_t i=0; i<6; i++) { 149 | if (_keyReport.keys[i] == 0) { 150 | _keyReport.keys[i] = ktab[key]; 151 | break; 152 | } 153 | } 154 | } 155 | HID_SendReport(2,&_keyReport,8); 156 | } 157 | 158 | 159 | void keyrelease(uint8_t k) { 160 | 161 | if (k > 0x5f) _keyReport.modifiers &= ~ktab[key]; // modifier 162 | else { 163 | for (uint8_t i=0; i<6; i++) { 164 | if (_keyReport.keys[i] == ktab[key]) _keyReport.keys[i] = 0; 165 | } 166 | } 167 | HID_SendReport(2,&_keyReport,8); 168 | } 169 | 170 | 171 | void keystroke(uint8_t k, uint8_t m) { 172 | 173 | unsigned short memomodifiers = _keyReport.modifiers; // save last modifier state 174 | for (uint8_t i=0; i<6; i++) { 175 | if (_keyReport.keys[i] == 0) { 176 | _keyReport.keys[i] = k; 177 | _keyReport.modifiers = m; 178 | HID_SendReport(2,&_keyReport,8); 179 | _keyReport.keys[i] = 0; 180 | _keyReport.modifiers = memomodifiers; // recover modifier state 181 | HID_SendReport(2,&_keyReport,8); 182 | break; 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /reference/notes.txt: -------------------------------------------------------------------------------- 1 | This is just some additional sources that I used for features of the device. 2 | Joystick, Mouse support were done by me (sdl) 3 | What follows are additional features: 4 | - HID Joystick 5 | - Amiga QWERTZ/QWERTY HID support 6 | 7 | - scott 7/2015 8 | 9 | -------------------------------------------------------------------------------- 10 | 11 | HID Joystick code: 12 | 13 | http://www.imaginaryindustries.com/blog/?p=80 14 | 15 | -------------------------------------------------------------------------------- 16 | olaf QWERTZ: 17 | 18 | http://forum.arduino.cc/index.php?topic=139358.0 19 | 20 | Keyboard Leonardo 21 | Connector IO 22 | 23 | 1 KBCLK 8 24 | 2 KBDATA 9 25 | 3 KBRST 10 26 | 4 5v 5v 27 | 5 NC 28 | 6 GND GND 29 | 7 LED1 5V 30 | 8 LED2 - 31 | 32 | IBM Modifiers 33 | // modifiers: Shift left, Shift right, -, Win left, Crtl left, Ctrl right, Alt left, Alt right 34 | 0x02, 0x20, 0x00, 0x08, 0x01, 0x10, 0x04, 0x40 35 | 36 | 37 | -------------------------------------------------------------------------------- 38 | Steve Reaver's QWERTY changes 39 | (page 2 of the above) 40 | 41 | -------------------------------------------------------------------------------- 42 | Koney's changes 43 | 44 | // MORE HELP FUNCTIONS /////////////////////////////// 45 | else if (key==0x4c) keystroke(0x4b,0); // Up = PgUp 46 | else if (key==0x4d) keystroke(0x4e,0); // Down = PgDown 47 | else if (key==0x4f) keystroke(0x4a,0); // Left = Home 48 | else if (key==0x4e) keystroke(0x4d,0); // Right = End 49 | else if (key==0x46) keystroke(0x49,0); // Help + DEL = Ins 50 | else if (key==0x44) keystroke(0x46,0); // Help + RETURN = PrtSc 2 51 | //////////////////////////////////////////////////////// 52 | 53 | help + arrows -> pgup, pgdn, home, end, 54 | help + del -> ins 55 | help + return -> printscr 56 | 57 | 58 | // VOLUME KEYS // 59 | else if (key==0x5e) keystroke(0x80,0); // Help + + = VOL UP 60 | else if (key==0x4a) keystroke(0x81,0); // Help + - = VOL DOWN 61 | else if (key==0x43) keystroke(0x7f,0); // Help + enter = VOL MUTE 62 | 63 | 64 | using http://stefanjones.ca/blog/arduino-leonardo-remote-multimedia-keys/ 65 | // VOLUME KEYS // 66 | else if (key==0x5e) { Remote.increase(); Remote.clear();} // Help + + = VOL UP 67 | else if (key==0x4a) { Remote.decrease(); Remote.clear();} // Help + - = VOL DOWN 68 | else if (key==0x43) { Remote.mute(); Remote.clear();} // Help + enter = VOL MUTE 69 | --------------------------------------------------------------------------------