This page covers talking to a Windows PC via the PS/2 port.  
 
Sometimes its useful to be able to emulate a keyboard or a mouse - that's what this code does.  For example, it might be better to have a device with a few buttons on it that provide the same functions as a combination of keystrokes, rather than having a dedicated keyboard.   
 
The PS/2 port is used for Keyboards and Mice on Windows based PCs.
PCs still have a PS/2 port, although most Keyboards and Mice are USB based now.  A device designed to work on the PS/2 port will also work on USB via a PS/2-USB adapter.  I have also tested this on a Macintosh, using a PS/2 to USB adapter, and it works just fine.

I used information from this website here to figure out how to work with the PS/2 standard.  
Some of the information from that website is included in the code comments.

These routines are written in Keil Micro C for the Silicon Labs C8051F005 MCU.

I've provided functions to send and receive data and commands on the PS/2 bus.
They work just fine in the application I'm using them for, although error recovery is not fully implemented (I'm too confident ;) ).
You just need to fill in the blanks for your needs.


//Include the 8051 register declarations
#include        <c8051f000.h>

#define         TRUE                             1
#define         FALSE                           0
#define         NORMAL                        0
#define         EXTENDED                    1
#define         BAT_SUCCESSFUL        0xAA
#define         PS2_ERROR                  0xFE
#define         PS2_ACK                       0xFA

sbit NUM_LOCK_LED = P1^7;
sbit CAPS_LOCK_LED = P1^6;
sbit SCROLL_LOCK_LED  = P1^5;
sbit ACTIVITY_LED  = P1^4;
sbit PS2_DATA_OUT  = P1^0;
sbit PS2_DATA_IN  = P1^0;     
sbit PS2_CLOCK  = P1^1;         
sbit TEST_SWITCH = P2^0;

//Prototypes
void ps2_send_data(unsigned char);
unsigned char ps2_receive_data(void);
void process_ps2_command(unsigned char);
void send_scan_code(unsigned char, unsigned char);

//Global variables
bit parity_error = FALSE;
unsigned char ps2_command;                     

void main(void)
{
    unsigned char state = 0;

    P1 = 0;    

    // do self test
    NUM_LOCK_LED = TRUE;
    CAPS_LOCK_LED 
= TRUE;
    SCROLL_LOCK_LED 
= TRUE;
    ACTIVITY_LED 
= TRUE;

    delay_ms(500);
    ps2_send_data(BAT_SUCCESSFUL);

    while (
TRUE)
    {       
        // Poll the PS2 lines to see if the PC wants to send data.
        // poll clock and data lines to see if the host PC has performed the following 3 steps indicating it wants to send a command
        //1)   Bring the Clock line low for at least 100 microseconds.
        //2)   Bring the Data line low.
        //3)   Release the Clock line.

        if((PS2_CLOCK == 0) && (PS2_DATA_IN == 
1)) state = 1;
        
if(((PS2_CLOCK == 0) && (PS2_DATA_IN == 0)) && state == 1) state = 2;
        
if(((PS2_CLOCK == 1) && (PS2_DATA_IN == 0)) && state == 2) state = 3;
        
if(state == 3)     //PC has sent the start bit
        {
            delay_us(48);
            ps2_command
= ps2_receive_data();
            process_ps2_command(ps2_command);
            state
0;
        }

        //test code to see if protocol works:  send CTRL-ESC to activate Windows Start menu
        //this is the same as pressing the Windows key on keyboards equiped with this button
        //it corresponds to the Command Key on a Mac - eg Command-Q to quit a program

        if (TEST_SWITCH == FALSE)   //button on PORT2.0 pressed?
        {
            ps2_send_data(0x14);       //CTRL down
            delay_ms(10);
            ps2_send_data(0x76);       //ESC down
            delay_ms(
10);
            ps2_send_data(0xF0
);       //ESC  up
            
ps2_send_data(0x76);       //ESC up  
            
ps2_send_data(0xF0);       //CTRL up
            
ps2_send_data(0x14);       //CTRL up
        }       

        //your code here to "scan" keys and send the correct scan code out the PS/2 port
                                                                   

    };
}

 /************************************ PS/2  ROUTINES **********************************************/

//send scan codes out the PS2 port
void ps2_send_data(unsigned char scan_code)
{
    /*
    When the keyboard or mouse wants to send information, it first checks the Clock line to make sure it's at a high logic level.  If it's not, the host is
    inhibiting communication and the device must buffer any to-be-sent data until the host releases Clock.  The Clock line must be continuously high for at least
    50 microseconds before the device can begin to transmit its data.
        
    The keyboard/mouse writes a bit on the Data line when Clock is high, and it is read by the host when Clock is low.

    * All data is transmitted one byte at a time and each byte is sent in a frame consisting of 11 bits.  These bits are:
    * 1 start bit.  This is always 0
    * 8 data bits, LSB first
    * 1 parity bit (odd parity)
    * 1 stop bit.  This is always 1
    */        

    //PORT1.0 is the data out pin (PS2_DATA)
    //PORT1.1 is the clock pin (PS2_CLOCK)
      
    unsigned char k = 0;
    unsigned char parity_count = 0;
    unsigned char bit_mask = 0x01;        

    while ((PS2_CLOCK == 0) && (PS2_DATA_IN == 1));  //if bus in in an idle state, wait until freed

    if((P1 & 0x03) && (scan_code != '\0')) //Clock and Data lines high?
    {
        last_byte = scan_code;          
        //set ports as outputs

        P1 |=
0x03;         
        PS2_CLOCK = TRUE;  //Set up data line while clock is high
           
delay_us(5);  //wait at least 5uS after clock rises
        //1 start bit.  This is always 0
        PS2_DATA_OUT = FALSE;

        //toggle the clock pin
        delay_us(48);  
        PS2_CLOCK
FALSE //Host reads data line
        
delay_us(48);
        
PS2_CLOCKTRUE;  //Set up data line while clock is high
           delay_us(5);
        //8 data bits, LSB first       
        for (k = 0;  k < 8; k++)
        {
            //determine whether to put a 0 or 1 on to the DATA pin (PB6)
            if ((scan_code & bit_mask) > 0)
            {
                PS2_DATA_OUT = TRUE;
                parity_count++;
            }  
            //toggle the clock pin
            
delay_us(48);  
            
PS2_CLOCKFALSE;  //Host reads data line  
            
delay_us(48);
            
PS2_CLOCKTRUE;  //Set up data line while clock is high
               delay_us(5);
            PS2_DATA_OUT = FALSE;  

            //Shift bit_mask right 1 bit 0x01->0x02->0x04->0x08->0x10->0x20->0x40->0x80
            bit_mask = bit_mask << 1;   
        }  

        //1 parity bit (odd parity)
        if ((parity_count == 0) || (parity_count 
== 2|| (parity_count == 4) || (parity_count == 6) || (parity_count == 8)) PS2_DATA_OUT = TRUE;  
        else PS2_DATA_OUT
FALSE;                                   

        //toggle the clock pin
        
delay_us(48);  
        
PS2_CLOCKFALSE;  //Host reads data line  
        
delay_us(48);
        
PS2_CLOCKTRUE;  //Set up data line while clock is high
           delay_us(5);

        //1 stop bit.  This is always 1
        
PS2_DATA_OUT = TRUE;

        //toggle the clock pin
        
delay_us(48);  
        
PS2_CLOCKFALSE;  //Host reads data line  
        
delay_us(48);
        
PS2_CLOCKTRUE;  //Set up data line while clock is high
           delay_us(5);
    }         

    P1 |= 0x03;     
    delay_us(100);
}

//receive commands from the host
unsigned char ps2_receive_data(void)
{                          
    
unsigned char parity0;
   
unsigned char m1;
   
unsigned char n1;
   
unsigned char parity_count0;
   
unsigned char received_data0;
   
unsigned char temp_data0;

    PS2_DATA_IN = 
TRUE;;        //P1.0 input for data, P1.1 output for clocking               
    
delay_us(48);   

    //8 data bits, LSB first
    //bit 0
    //toggle the clock pin
    
PS2_CLOCKFALSE;  //Host sets up data line while clock is low
    
delay_us(48);    
    
PS2_CLOCKTRUE;   //Read data line while clock is high
    //determine whether to put a 0 or 1 into received_data
    temp_data = PS2_DATA_IN;
    if (
temp_data == 1) parity_count++;
    received_data += 
temp_data;
    
temp_data = 0;
    
delay_us(48);        

    //bit 1 to 7    
    for (m
1; m < 8; m++)
    {
        
PS2_CLOCKFALSE;  //Host sets up data line while clock is low
        
delay_us(48);    
    
   PS2_CLOCKTRUE;   //Read data line while clock is high
        //determine whether to put a 0 or 1 into received_data
        
temp_data = PS2_DATA_IN;
        if (temp_data == 1) parity_count++;  
        
temp_datatemp_data << n;
        n++;
        received_data += temp_data;
        temp_data = 0;
        
delay_us(48);
    }      

    //get the parity bit (odd parity)                             
    //toggle the clock pin
    
PS2_CLOCKFALSE;  //Host sets up data line while clock is low
    
delay_us(48);    
    
PS2_CLOCKTRUE;   //Read data line while clock is high
    //determine whether to put a 0 or 1 in to received_data
    parity = PS2_DATA_IN;              

    //receive 1 stop bit.  This is always 1
    //toggle the clock pin
    
delay_us(48);
    
PS2_CLOCKFALSE;  //Host sets up data line while clock is low
    
delay_us(48);    
    
PS2_CLOCKTRUE;   //Read data line while clock is high              

    /*
    After the stop bit is received, the device will acknowledge the received byte by bringing the Data line low and generating one last clock pulse.  
    If the host does not release the Data line after the 11th clock pulse, the device will continue to generate clock pulses until the the Data line is released
    (the device will then generate an error.)
    */

    
delay_us(24);
    PS2_DATA_OUTFALSE; 
    
delay_us(24);   
    
PS2_CLOCKFALSE;   
    
delay_us(48);
    
PS2_DATA_OUTTRUE;  
    
delay_us(48);
    
PS2_CLOCK = TRUE;   
    
delay_us(48);               

    switch(parity)
    {
        case 0:
            if ((parity_count == 
0) || (parity_count == 2) || (parity_count == 4) || (parity_count == 6) || (parity_count == 8)) parity_error FALSE;   
        break;
 
        
case 1:
            if ((parity_count == 
1) || (parity_count == 3) || (parity_count == 5) || (parity_count == 7))
            {
                parity_error
TRUE;   
                ps2_send_data(PS2_ERROR);
            }
        break;

        default:
            
parity_error FALSE;
    }        

    return received_data;  
}

void send_scan_code(unsigned char _code, unsigned char mode)
{     
    switch(mode)
    {
        
case NORMAL:
            //send make scan code
            
ps2_send_data(_code);
        break;  
 
        case SHIFT:
            //send make scan code
            
ps2_send_data(0x12);
            
ps2_send_data(_code);
            //then send break scan code
            
ps2_send_data(0xF0);
            
ps2_send_data(0x12);
        break;  

        case CTRL:            
        break;   
                
        case EXTENDED:
            //send make scan code
            
ps2_send_data(0xE0);
            
ps2_send_data(_code);
            //then send break scan code
            
ps2_send_data(0xE0);                
        break;

        
ps2_send_data(0xF0);
        
ps2_send_data(_code);
    }
}

void process_ps2_command(unsigned char command)
{
    bit SetScanCode = FALSE;
    bit SetLEDs = FALSE;   

    switch(command)
    {
       
 case(0xFF):     //*0xFF (Reset) - Keyboard responds with "ack" (0xFA), then enters "Reset" mode.
            
ps2_send_data(PS2_ACK);
            //go into reset mode...  
            //do "self test" and tell host all ok
            NUM_LOCK_LED = TRUE;
            CAPS_LOCK_LED
TRUE;
            SCROLL_LOCK_LED
TRUE;
            
ps2_send_data(BAT_SUCCESSFUL);
        break;
                
        
case(0xFE):    
            //*0xFE (Resend) - Keyboard responds by resending the last-sent byte.  The exception to this is if the last-sent byte was "resend" (0xFE).  

            //If this is the case, the keyboard resends the last non-0xFE byte.  This command is used by the host to indicate an error in reception.
            ps2_send_data(PS2_ACK);
            
ps2_send_data(last_byte);  
        break;

        //case(0xF7):     //*0xF7 (Set All Keys Typematic) - Keyboard responds with "ack" (0xFA).
        //    ps2_send_data(PS2_ACK);
        //break;  

        //case(0xF6):     // *0xF6 (Set Default) - Load default typematic rate/delay (10.9cps / 500ms), key types (all keys typematic/make/break), and scan code set (2).
        //    ps2_send_data(PS2_ACK);                
        //break;  
                
        //case(0xF5):     //*0xF5 (Disable) - Keyboard stops scanning, loads default values (see "Set Default" command), and waits further instructions.
        //    ps2_send_data(PS2_ACK);               
        //break;                 

        //case(0xF4):     //*0xF4 (Enable) - Re-enables keyboard after disabled using previous command.
        //    ps2_send_data(PS2_ACK);                
        //break;                 

        
case(0xF2):    
            //*0xF2 (Read ID) - The keyboard responds by sending a two-byte device ID of 0xAB, 0x83. (0xAB is sent first, followed by 0x83.)

            ps2_send_data(PS2_ACK);
            
ps2_send_data(0xAB);
            
ps2_send_data(0x83);
        break;
                
        
case(0xF0):    
            //*0xF0 (Set Scan Code) -  Keyboard responds with "ack", then reads argument byte from the host.  

            //This argument byte may be 0x01, 0x02, or 0x03 to select scan code set 1, 2, or 3, respectively.  
            //The keyboard responds to this argument byte with "ack".  
            //If the argument byte is 0x00, the keyboard responds with "ack" followed by the current scan code set.
            SetScanCode =
TRUE;
            ps2_send_data(PS2_ACK);
        break;

        
case(0xEE):    
            //*0xEE (Echo) - The keyboard responds with "Echo" (0xEE).

            
ps2_send_data(0xEE);               
        break;
                
        
case(0xED):    
            //*0xED (Set/Reset LEDs) - The host follows this command with one argument byte, that specifies the state of the keyboard's

            //Num Lock, Caps Lock, and Scroll Lock LEDs.  This argument byte is defined as follows: 0,0,0,0,0,0,caps,num,scroll
            SetLEDs
TRUE;
            ps2_send_data(PS2_ACK);
        break;

        
case(0x00):     
            if (SetScanCode == 
TRUE)
            {
                ps2_send_data(PS2_ACK);
                
ps2_send_data(0x02);
                SetScanCode
FALSE;
            }
            else
            {
                NUM_LOCK_LED
FALSE;
                CAPS_LOCK_LED
FALSE;
                SCROLL_LOCK_LED
FALSE;
                SetLEDs
FALSE;
                ps2_send_data(PS2_ACK);
            }                
        break;
                
        
case(0x01):
                if(SetLEDs ==
TRUE)
                {
                    NUM_LOCK_LED
FALSE;
                    CAPS_LOCK_LED
FALSE;
                    SCROLL_LOCK_LED
TRUE;
                    SetLEDs
FALSE;      
                }
                ps2_send_data(PS2_ACK);
        break;
                
        
case(0x02):    
                if (SetScanCode == 
FALSE) NUM_LOCK_LEDTRUE;
                ps2_send_data(PS2_ACK);
        break;

        
case(0x03):
            if(SetLEDs == 
TRUE)
            {
                NUM_LOCK_LED
TRUE;
                CAPS_LOCK_LED
FALSE;
                SCROLL_LOCK_LED
TRUE;
                SetLEDs
FALSE;        
            }
            ps2_send_data(PS2_ACK);
        break;

        
case(0x04):
            if(SetLEDs == 
TRUE)
            {
                NUM_LOCK_LED
FALSE;
                CAPS_LOCK_LED
TRUE;
                SCROLL_LOCK_LED
FALSE;
                SetLEDs
FALSE;        
            }
            ps2_send_data(PS2_ACK);
        break;
                
        
case(0x05):
            if(SetLEDs == 
TRUE)
            {
                NUM_LOCK_LED
FALSE;
                CAPS_LOCK_LED =
TRUE;
                SCROLL_LOCK_LED
TRUE;
                SetLEDs
FALSE;        
            }
            ps2_send_data(PS2_ACK);
        break;

        
case(0x06):
            if(SetLEDs ==
TRUE)
            {
                NUM_LOCK_LED
TRUE;
                CAPS_LOCK_LED =
TRUE;
                SCROLL_LOCK_LED
FALSE;
                SetLEDs
FALSE;        
            }
            ps2_send_data(PS2_ACK);
        break;

        
case(0x07):
            if(SetLEDs ==
TRUE)
            {
                NUM_LOCK_LED
TRUE;
                CAPS_LOCK_LED
TRUE;
                SCROLL_LOCK_LED
TRUE;    
            }
            ps2_send_data(PS2_ACK);
        break;

        default:
            ps2_send_data(PS2_ACK);
    }      
}

//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ end of PS/2 routines ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^