Search code examples
assemblyx86keyboardx86-16ioports

Turning keyboard LED lights on


I was given some code to analyze. This code enables the numLock and scrollLock LEDs on a keyboard. I understand the majority of this code, however I don't understand the loop part (0104). I know this is for waiting for an input buffer to be empty. But is it necessary? The code works well without this part.

0100: MOV AL, ED
0102: OUT 60, AL
0104: IN  AL, 64
0106: TEST AL, 02
0108: JNZ 0104
010A: MOV AL, 03
010C: OUT 60, AL

Solution

  • There is good information about the AT keyboard controller here. The bits in the Status Register (port 0x64) you'll probably be most interested in for doing reading and writing of data on port 0x60 are:

    Bit 1: Input buffer status

    0: Input buffer empty, can be written. 1: Input buffer full, don't write yet.

    Bit 0: Output buffer status

    0: Output buffer empty, don't read yet. 1: Output buffer full, can be read. (In the PS/2 situation bit 5 tells whether the available data is from keyboard or mouse.) This bit is cleared when port 0x60 is read.

    You have to wait until the Input Buffer Status Bit is clear before writing to port 0x60. Failure to wait could cause data sent to the controller to be lost. Before reading data from port 0x60 you should wait until the Output Buffer Status Bit is set since it means there's data for reading. Reading data that isn't available will result in whatever is read from the port to be treated as data when it isn't data at all.

    The terms Input and Output in the status register may seem counterintuitive at first glance. The bits got their name from the perspective of the keyboard controller, not the PC. The output buffer on the controller is the input buffer on the PC and vice versa.

    Emulators and virtual machines seem far more forgiving. If you want your code to have the best chance of working on a variety of real hardware and emulators you will want to insert the loops that wait for the appropriate status before proceeding.


    The first part of the code sends the 0xED command1 to the keyboard:

    0100: MOV AL, ED
    0102: OUT 60, AL
    

    This command is documented as:

    Command 0xED: Write LEDs

    This command is followed by a byte indicating the desired LEDs setting. Bits 7-3: unused, 0. Bit 2: 1: CapsLock LED on. Bit 1: 1: NumLock LED on. Bit 0: 1: ScrollLock LED on. When OK, both bytes are ACKed. If the second byte is recognized as a command, that command is ACKed and done instead. Otherwise a NACK is returned (and a keyboard enable may be needed).

    This section of code is waiting for bit 1 (Input Buffer Status) to become 0:

    0104  IN  AL, 64
    0106: TEST AL, 02
    0108: JNZ 0104
    

    When the keyboard controller is ready to receive data then the PC is free to write data to port 0x60 which is what this code does:

    010A: MOV AL, 03
    010C: OUT 60, AL
    

    This is the LED data associated with command 0xED. The value 03=00000011. Bit 1 set means 'enable NumLock', and bit 0 set means 'enable ScrollLock'.


    Footnotes

    • 1The code should have waited for the Input Buffer Status bit to become 0 before writing the keyboard command 0xED to port 0x60.