You need to log in to create posts and topics.

Possible contribution of a simulated PS/2 Keyboard

First of all thank you and many, many, many congratulations for this marvelous piece of software.

In my attempt to create a simulated version of this project:

http://criss.fun/

I created PS/2 Keyboard component using SimulIDE_1.1.0-SR0_sources, which I tested with the following library:

https://github.com/SteveBenz/PS2KeyboardHost

and works fine.

As instructed in the Resources > Contribute section of SimulIDE, I first start a discussion about it and, if approved, I can send the relevant files.

An image of the test circuit is the following:

Once again, thank you for your fine work.

Regards,

Theodore H. Kaskalis (gmaltk)

Fizik_S and KerimF have reacted to this post.
Fizik_SKerimF

Wow, that's a great project.

My access to the internet is limited right now, so I'm sorry for the delays, hopefully it will get better next days.

I sent you a private message: https://simulide.com/p/messages/

Hello once more,

Thank you for accepting my possible contribution.

In the attached zip file please find the following:

  1. keyboard.h , keyboard.cpp : The relevant source files for the possible inclusion in some Item Library ("Peripherals" maybe?).
  2. keyboard.txt : A small help text file.
  3. keyboard.png : A possible item icon.

Please feel free to edit or change any of the above mentioned files.

I must note that I did not pay much attention to the details of rotating or mirroring the keyboard module. I also mention that, although I tried to be very careful as regards to Windows and MacOS actual key codes, I only tested the whole thing on Linux.

What I implemented is a (rather?) full Scan Code Set 2 version of a PS/2 Keyboard, according to the following description:

http://web.archive.org/web/20030816040547/http://www.computer-engineering.org/ps2protocol/

For conducting functionality tests, I used the following Arduino Library (Ps2KeyboardHost):

https://www.arduino.cc/reference/en/libraries/ps2keyboardhost/

and this simple schematic:

While in simulation, the user can just click on each keyboard button or activate the "GRAB real keyboard" button, in order to use his physical keyboard. Please note, however, that some physical keys (e.g. Tab) are captured in a higher level, so in these cases the user should use the mouse click method.

I created the following test .ino (also attached):

#include "ps2_Keyboard.h"

#define DATA  4
#define CLOCK 3

ps2::Keyboard<DATA, CLOCK> ps2Keyboard;
int scanCode;
char selection = 'Z';
bool inSystem = false;
String key = "";
String command = "";
byte LEDstat = 0x000;

void setup( ) {
  Serial.begin( 115200 );
  ps2Keyboard.begin();
  if ( ps2Keyboard.awaitStartup() ) Serial.println(F("Type something!\n(type HOST to enter host commands)\n"));
}


void loop( ) {
  scanCode = (int) ps2Keyboard.readScanCode();

  if ( scanCode != (int) ps2::KeyboardOutput::none ) {
    if ( scanCode == (int) ps2::KeyboardOutput::extend1 ) { // Eliminate E1 extend code
      while ( ( scanCode = (int) ps2Keyboard.readScanCode() ) == (int) ps2::KeyboardOutput::none ) ;
      scanCode |= 0x200;
    }
    else if ( scanCode == (int) ps2::KeyboardOutput::extend ) { // Eliminate E0 extend code
      while ( ( scanCode = (int) ps2Keyboard.readScanCode() ) == (int) ps2::KeyboardOutput::none ) ;
      scanCode |= 0x100;
    }
    if ( ( scanCode & 0xFF ) == (int) ps2::KeyboardOutput::unmake ) { // Eliminate break code
      while ( ps2Keyboard.readScanCode() == ps2::KeyboardOutput::none ) ;
      return;
    }
    key = "\nError Code !!\n";
    switch ( scanCode ) {
      case 0x01C: key = "A";                break;
      case 0x032: key = "B";                break;
      case 0x021: key = "C";                break;
      case 0x023: key = "D";                break;
      case 0x024: key = "E";                break;
      case 0x02B: key = "F";                break;
      case 0x034: key = "G";                break;
      case 0x033: key = "H";                break;
      case 0x043: key = "I";                break;
      case 0x03B: key = "J";                break;
      case 0x042: key = "K";                break;
      case 0x04B: key = "L";                break;
      case 0x03A: key = "M";                break;
      case 0x031: key = "N";                break;
      case 0x044: key = "O";                break;
      case 0x04D: key = "P";                break;
      case 0x015: key = "Q";                break;
      case 0x02D: key = "R";                break;
      case 0x01B: key = "S";                break;
      case 0x02C: key = "T";                break;
      case 0x03C: key = "U";                break;
      case 0x02A: key = "V";                break;
      case 0x01D: key = "W";                break;
      case 0x022: key = "X";                break;
      case 0x035: key = "Y";                break;
      case 0x01A: key = "Z";                break;
      case 0x045: key = "0";                break;
      case 0x016: key = "1";                break;
      case 0x01E: key = "2";                break;
      case 0x026: key = "3";                break;
      case 0x025: key = "4";                break;
      case 0x02E: key = "5";                break;
      case 0x036: key = "6";                break;
      case 0x03D: key = "7";                break;
      case 0x03E: key = "8";                break;
      case 0x046: key = "9";                break;
      case 0x00E: key = "`";                break;
      case 0x04E: key = "-";                break;
      case 0x055: key = "=";                break;
      case 0x05D: key = "\\";               break;
      case 0x066: key = "<Backspace>";      break;
      case 0x029: key = " ";                break;
      case 0x00D: key = "<Tab>";            break;
      case 0x058: key = "<Caps Lock>";      break;
      case 0x012: key = "<Left Shift>";     break;
      case 0x014: key = "<Left Control>";   break;
      case 0x011: key = "<Left Alt>";       break;
      case 0x059: key = "<Right Shift>";    break;
      case 0x05A: key = "<Enter>\n";        break;
      case 0x076: key = "<Escape>";         break;
      case 0x005: key = "<F1>";             break;
      case 0x006: key = "<F2>";             break;
      case 0x004: key = "<F3>";             break;
      case 0x00C: key = "<F4>";             break;
      case 0x003: key = "<F5>";             break;
      case 0x00B: key = "<F6>";             break;
      case 0x083: key = "<F7>";             break;
      case 0x00A: key = "<F8>";             break;
      case 0x001: key = "<F9>";             break;
      case 0x009: key = "<F10>";            break;
      case 0x078: key = "<F11>";            break;
      case 0x007: key = "<F12>";            break;
      case 0x07E: key = "<Scroll Lock>";    break;
      case 0x054: key = "[";                break;
      case 0x077: key = "<Num Lock>";       break;
      case 0x07C: key = "<Num *>";          break; 
      case 0x07B: key = "<Num ->";          break;
      case 0x079: key = "<Num +>";          break;
      case 0x071: key = "<Num .>";          break;
      case 0x070: key = "<Num 0>";          break;
      case 0x069: key = "<Num 1>";          break;
      case 0x072: key = "<Num 2>";          break;
      case 0x07A: key = "<Num 3>";          break; 
      case 0x06B: key = "<Num 4>";          break; 
      case 0x073: key = "<Num 5>";          break;
      case 0x074: key = "<Num 6>";          break;
      case 0x06C: key = "<Num 7>";          break; 
      case 0x075: key = "<Num 8>";          break;
      case 0x07D: key = "<Num 9>";          break;
      case 0x05B: key = "]";                break;
      case 0x04C: key = ";";                break;
      case 0x052: key = "'";                break;
      case 0x041: key = ",";                break;
      case 0x049: key = ".";                break;
      case 0x04A: key = "/";                break;
      case 0x170: key = "<Insert>";         break;
      case 0x16C: key = "<Home>";           break;
      case 0x17D: key = "<Page Up>";        break;
      case 0x171: key = "<Delete>";         break;
      case 0x169: key = "<End>";            break;
      case 0x17A: key = "<Page Down>";      break;
      case 0x175: key = "<Up Arrow>";       break;
      case 0x16B: key = "<Left Arrow>";     break;
      case 0x172: key = "<Down Arrow>";     break;
      case 0x174: key = "<Right Arrow>";    break;
      case 0x14A: key = "<Num />";          break;
      case 0x15A: key = "<Num Enter>\n";    break;
      case 0x114: key = "<Right Control>";  break;
      case 0x111: key = "<Right Alt>";      break;
      case 0x112: // Possible Print Screen. Wait for 0x7C.
        while ( ( scanCode = (int) ps2Keyboard.readScanCode() ) == (int) ps2::KeyboardOutput::none ) ;
        if ( scanCode == (int) ps2::KeyboardOutput::extend ) { // Eliminate E0 extend code
          while ( ( scanCode = (int) ps2Keyboard.readScanCode() ) == (int) ps2::KeyboardOutput::none ) ;
          if ( scanCode == 0x7C ) key = "<Print Screen>";
        }
        break;
      case 0x214: // Possible Pause / Break. Wait for 0x77.
        while ( ( scanCode = (int) ps2Keyboard.readScanCode() ) == (int) ps2::KeyboardOutput::none ) ;
        if ( scanCode == 0x77 ) key = "<Pause/Break>";
        break;
      default: break;
    }
    if ( inSystem ) {
      selection = key.charAt(0);
      switch ( selection ) {
        case 'A':
          if ( ps2Keyboard.reset() ) {
            LEDstat = 0x000;
            Serial.println(F("\nReset Completed Successfully"));
          }
          else Serial.println(F("\nError in Reset!!"));
          break;
        case 'B':
          if ( ps2Keyboard.echo() ) Serial.println(F("\nEcho Completed Successfully"));
          else Serial.println(F("\nError in Echo!!"));
          break;
        case 'C':
          if ( ps2Keyboard.readId() == 0xAB83 ) Serial.println(F("\nID AB83 Received Successfully"));
          else Serial.println(F("\nError in Read ID!!"));
          break;
        case 'D':
          if ( (int) ps2Keyboard.getScanCodeSet() == 2 ) Serial.println(F("\nScan Code Set 2 Received Successfully"));
          else Serial.println(F("\nError in  Get Scancode Set!!"));
          break;
        case 'E':
          LEDstat = LEDstat ^ 0b010;
          if ( ps2Keyboard.sendLedStatus( (ps2::KeyboardLeds) LEDstat ) ) {
            Serial.print(F("\nNum Lock "));
            ( LEDstat & 0b010 ) ? Serial.println("ON") : Serial.println("OFF");
          }
          else Serial.println(F("\nError in Setting LEDS!!"));
          break;
        case 'F':
          LEDstat = LEDstat ^ 0b100;
          if ( ps2Keyboard.sendLedStatus( (ps2::KeyboardLeds) LEDstat ) ) {
            Serial.print(F("\nCaps Lock "));
            ( LEDstat & 0b100 ) ? Serial.println("ON") : Serial.println("OFF");
          }
          else Serial.println(F("\nError in Setting LEDS!!"));
          break;
        case 'G':
          LEDstat = LEDstat ^ 0b001;
          if ( ps2Keyboard.sendLedStatus( (ps2::KeyboardLeds) LEDstat ) ) {
            Serial.print(F("\nScroll Lock "));
            ( LEDstat & 0b001 ) ? Serial.println("ON") : Serial.println("OFF");
          }
          else Serial.println(F("\nError in Setting LEDS!!"));
          break;
        case 'H':
          if ( ps2Keyboard.disable() ) Serial.println(F("\nKeyboard Disabled Successfully"));
          else Serial.println(F("\nError in Keyboard Disable!!"));
          delay(3000);
          if ( ps2Keyboard.enable() ) Serial.println(F("\nKeyboard Enabled Successfully"));
          else Serial.println(F("\nError in Keyboard Enable!!"));
          break;
        case 'I':
          if ( ps2Keyboard.setTypematicRateAndDelay((ps2::TypematicRate) 0x00, (ps2::TypematicStartDelay) 0b00) )
            Serial.println(F("\nTypematic Rate and Delay are 30.0cps and 0.25s"));
          else Serial.println(F("\nError in Setting Typematic Rate and Delay!!"));
          break;
        case 'J':
          if ( ps2Keyboard.setTypematicRateAndDelay((ps2::TypematicRate) 0x08, (ps2::TypematicStartDelay) 0b01) )
            Serial.println(F("\nTypematic Rate and Delay are 15.0cps and 0.50s"));
          else Serial.println(F("\nError in Setting Typematic Rate and Delay!!"));
          break;
        case 'K':
          if ( ps2Keyboard.setTypematicRateAndDelay((ps2::TypematicRate) 0x10, (ps2::TypematicStartDelay) 0b10) )
            Serial.println(F("\nTypematic Rate and Delay are 7.5cps and 0.75s"));
          else Serial.println(F("\nError in Setting Typematic Rate and Delay!!"));
          break;
        case 'L':
          if ( ps2Keyboard.setTypematicRateAndDelay((ps2::TypematicRate) 0x1F, (ps2::TypematicStartDelay) 0b11) )
            Serial.println(F("\nTypematic Rate and Delay are 2.0cps and 1.00s"));
          else Serial.println(F("\nError in Setting Typematic Rate and Delay!!"));
          break;
        case 'M':
          if ( ps2Keyboard.resetToDefaults() ) Serial.println(F("\nDefault Settings Applied"));
          else Serial.println(F("\nError in Setting Default Values!!"));
          break;
        case 'Z':
          Serial.println(F("\n\nType something!\n(type HOST to enter host commands)\n"));
          inSystem = false;
          break;
        default:
          break;
      }
      return;
    }
    Serial.print( key );
    if ( key == "H" ) command = key;
    else if ( command.length() ) {
      if ( command.length() < 4 ) command += key;
      else command = "";
    }
    if ( command == "HOST" ) {
      Serial.println(F("\n\nPress letter to test command:\n"));
      Serial.println(F("A: Reset"));
      Serial.println(F("B: Echo"));
      Serial.println(F("C: Read ID"));
      Serial.println(F("D: Get Scancode Set"));
      Serial.println(F("E: Toggle   Num  Lock"));
      Serial.println(F("F: Toggle  Caps  Lock"));
      Serial.println(F("G: Toggle Scroll Lock"));
      Serial.println(F("H: Disable Keyboard (will be re-enabled in 3s)"));
      Serial.println(F("I: Typematic Rate 30.0cps, Delay 0.25s"));
      Serial.println(F("J: Typematic Rate 15.0cps, Delay 0.50s"));
      Serial.println(F("K: Typematic Rate  7.5cps, Delay 0.75s"));
      Serial.println(F("L: Typematic Rate  2.0cps, Delay 1.00s"));
      Serial.println(F("M: Set Default Typematic Rate and Delay values"));
      Serial.println(F("Z: Exit host commands"));
      inSystem = true;
    }
  }
}

And during simulation the user can type and see the respective characters in the Serial Monitor:

As instructed above, if the user types HOST, he enters the Host-to-Device section of the program:

where he can test the implemented commands.

Please note that the "Reset" command re-disables the "GRAB real keyboard" button, in case it was enabled.

Moreover, as regards the "Set Typematic Rate and Delay" commands, I really believe that the parameter "startDelay" in line 630 of "ps2_Keyboard.h" in the Ps2KeyboardHost Library should be left-shifted by 5 instead of 4. You have to make this change in order to actually test command I, J, K, L..

I created an issue in GitHub:

https://github.com/SteveBenz/PS2KeyboardHost/issues/4

but it seems that this repository is unattended.

All in all, I hope that this contribution can make it in SimulIDE even after editing and changing.

I really would like some feedback as to how physical keyboards work in Windows and MacOS and maybe if my comment about this minor bug in Ps2KeyboardHost Library is actually valid.

Thank you for reaching this far in reading.

Regards,

Theodore H. Kaskalis (gmaltk)

Uploaded files:

Thank you very much.
I will have a look at the code.

I'm not familiar with the PS/2 protocol, but some ideas that come to my mind:
- Maybe the protocol could be implemented separately so it could be used by some other components.
- Maybe the key code set could be configurable? being the default the OS of the PC running the simulation.

Thank you for your quick feedback and your first ideas.

As regards your first point (the protocol could be implemented separately so it could be used by some other components), I assume that your are referring to a possible mouse implementation (I do not know any other component using this protocol).

Since the common part of the two devices is the communication part, I also assume that you are referring to that.

If both my assumptions were correct, maybe I can separate the communication part from the command processing part in a fashion similar to SpiModule.

Now regarding your second comment (the key code set could be configurable), I do not understand if you are referring to the Scan Code Set 2 implemented by the virtual keyboard or the actual key codes passed from the underlying OS.

If it's the first case, then as mentioned in the description:

  • Scan Code Set 1 : Original XT scan code set; supported by some modern keyboards
  • Scan Code Set 2 : Default scan code set for all modern keyboards
  • Scan Code Set 3 : Optional PS/2 scan code set--rarely used

There is no way that any used library or code of any connected module will not support Set 2.

Of course, for the sake of completeness all Scan Code Sets should be implemented, although set 1 and 3 are rarely used.

If, on the other hand, you are referring to actual key codes, you will notice that inside the "keyboard.cpp" file there are distinct sections for Linux, Windows and MacOS selected during compile-time.

 

Hi.

Since the common part of the two devices is the communication part, I also assume that you are referring to that.

Yes, something similar to other communication protocols like I2C, SPI, etc.
This could be used by scripts (for example) to implement mouses or other versions of keyboards.
Indeed I'm thinking if this device should be implemented as an script...

About the key codes I mean actual key codes.
I see you configure it at compile time, but this is a simulation, so maybe users could configure which type to use.
In any case I'm not familiar with this subject so I don't really know what I'm talking about.
I have to dive a little bit more in the source code and learn something about this to get a better idea.

I will let you know when I have better knowledge and clear ideas...