5

I have seen the following topic.

I am interested in contacting the keyboard via the IN / OUT instructions and setting various modes, such as turning on the caps lock led. So far I have encountered problems doing so. The following link may help.

I have tried various combinations such as

mov al,0EDh           ;ED command - Send LED bits. The next byte written to port 60h updates the LEDs on the keyboard.
out 60h,al            ;out on port 60h
mov al,00000111b      ;led status - all leds on. bits 3-7 = reserved(zero)
out 60h,al            ;out on port 60h

I would appreciate any help. Thanks.

EDIT: As I said, using port 60h didn't work I have searched around the net for the usage of 0040:0017. One of the webs stated that bits 5,6,7 contain data about the leds' status

I tried using this code:

mov al,es:[0017h]
or al,11100000b
mov es:[0017h],al

and it didn't work either.

I might be doing that wrong, so could anyone please help me or send me a working code for turning all 3 leds on?

EDIT2: I ran my application on MS-DOS installed on a VM, and the code worked perfectly.

My question is: how can I make it work outside MS-DOS??

Community
  • 1
  • 1
Barak
  • 133
  • 1
  • 6
  • Are you trying this in real mode or protected mode? What OS? – Heath Hunnicutt Aug 08 '10 at 15:51
  • I am not really sure. I am currently using WinXP, however, my code is for a 16bit application which seems to cause problems under winXP, so I am using the DOSBox emulator. – Barak Aug 08 '10 at 16:28
  • Your first example is correct. I've tested it under dosemu running on the console. – ninjalj Aug 08 '10 at 21:22
  • ninjalj, thank you for trying the code. If the code works indeed, than my problem may be protected mode or privilege levels, am I right? – Barak Aug 09 '10 at 00:02
  • @user414423: Lack of keyboard LED emulation/direct access in whatever you're using. Note that I said it worked under dosemu running on the console, dosemu running on X doesn't access the keyboard LEDs. – ninjalj Aug 09 '10 at 14:46
  • How am I supposed to make that code work then? How can I allow direct access? – Barak Aug 09 '10 at 20:41
  • For WinXP http://www.codeguru.com/Cpp/W-P/system/keyboard/article.php/c2825 may work. – ninjalj Aug 09 '10 at 21:27
  • @ninjalj: Thank you, However, I'm interested in getting the job done via X86 Assembly code, probably using one of the two ways mentioned already (IN/OUT or 0040:0017). And, thank you for your time answering my question. – Barak Aug 09 '10 at 23:52
  • I tested my code on MS-DOS on a VM, and it worked well. What should I do to make it work outside MS-DOS? – Barak Aug 10 '10 at 23:27

2 Answers2

3

To access I/O ports from a task running on VM86 mode or protected mode you need special privileges. This privileges can be obtained via:

  • IOPL (only for protected mode tasks): If the current privilege level of the task is <= IOPL of task, access is allowed.
  • I/O permission bitmap (for VM86 tasks and protected mode tasks with insufficient CPL): the TSS may contain a bitmap for allowing/rejecting I/O port access.

When access is rejected, a GPF is generated.

Linux has the iopl() and ioperm() syscalls which allow processes with CAP_SYS_RAWIO to get these privileges. So, accessing keyboard LEDs on Linux can be done like this:

#include <stdio.h>
#include <sys/io.h>

int main()
{
    int ret;

    ret = ioperm(0x60, 0xf, 1);
    if (ret < 0) {
            perror("ioperm");
            return 1;
    }
    while (inb(0x64) & 0x2);
    outb(0xed, 0x60);
    while (inb(0x64) & 0x2);
    outb(0x07, 0x60);
    ioperm(0x60, 0xf, 0);

    return 0;
}

Windows NTVDM and Linux dosemu use VM86 mode to run real mode DOS programs. When a not allowed I/O port access is attempted, a GPF is generated, and these systems may emulate (or not) the I/O port access. dosemu has a -k switch that bypasses the usual tty layer and accesses directly the keyboard. Using this switch your first example works.

Now, to do the same thing on Windows will probably require doing it from a driver running on ring 0. An alternative may be using a driver that allows ring 3 processes access to the I/O ports (very insecure): see for instance ioperm for cygwin.

ninjalj
  • 42,493
  • 9
  • 106
  • 148
  • ninjalj, again, thank you very-much for the effort helping me. I have already understood that accessing I/O ports is somewhat restricted. I got my application to work under clean MS-DOS (running under VM-Ware). Getting the code to work from ring 0 may be complicated and maybe I will even try that. Your help and your answer is much appreciated and is very much clear and kept to the point. Thanks, again, Barak. – Barak Aug 12 '10 at 22:53
0

I've never written to the keyboard using $60, don't know what's there. Try writing the led bits to $0417.

Edit:

procedure writekbd(kbdbyte:byte);
begin
   mem[$0000:$0417]:=kbdbyte;
end;

function readkbd:byte;
begin
   kbdbyte:=mem[$0000:$0417];

   rsh:=kbdbyte and $1;
   lsh:=kbdbyte and $2;
   ctl:=kbdbyte and $4;
   alt:=kbdbyte and $8;
   scr:=kbdbyte and $10;
   num:=kbdbyte and $20;
   cap:=kbdbyte and $40;
   ins:=kbdbyte and $80;

   readkbd:=kbdbyte;
end;

procedure numoff;
begin
   readkbd;
   writekbd(kbdbyte and $df);
end;

procedure numon;
begin
   readkbd;
   writekbd(kbdbyte or $20);
end;

procedure capoff;
begin
   readkbd;
   writekbd(kbdbyte and $bf);
end;

procedure capon;
begin
   readkbd;
   writekbd(kbdbyte or $40);
end;

procedure scroff;
begin
   readkbd;
   writekbd(kbdbyte and $ef);
end;

procedure scron;
begin
   readkbd;
   writekbd(kbdbyte or $10);
end;

Changes at 0000:0417 were effective immediately.

Edit 2:

It turns out my code needed interrupts to update the keyboard status after all.

mov ax, 0100h
int 0016h
Jens Björnhager
  • 5,632
  • 3
  • 27
  • 47
  • 60h is the keyboard port, while 0040:0017 is the address of keyboard flags as maintained by the BIOS. – ninjalj Aug 08 '10 at 18:41
  • I tried using port 60h, and that failed. I have also tried addressing 0040:0017 , and that also failed. I might be doing that wrong. Could anyone show me an example code for turning the 3 LEDs on or try to direct me slowly? thanks – Barak Aug 08 '10 at 20:40
  • When using BIOS 0040:0017 you should call some BIOS keyboard function so the BIOS has a chance to set the LEDs to whatever is on 0040:0017. – ninjalj Aug 08 '10 at 21:10
  • I didn't have to call any BIOS interrupts for flashing the leds back in the day. – Jens Björnhager Aug 09 '10 at 18:17
  • @Jens Björnhager: Can you tell me exactly how did you make the LEDs flash? A source code or something like-that would be really appreciated. – Barak Aug 09 '10 at 20:40
  • If I remember correctly, this code stopped working with the advent of newer Windowses. – Jens Björnhager Aug 10 '10 at 16:41
  • Apparently the exact behaviour of this depends a lot on BIOS. I remember it needing a KeyPressed call to update LEDs, but trying it today on dosemu -k it doesn't change LEDs, but changes lock status. – ninjalj Aug 12 '10 at 17:09
  • Digging deeper into my sources, what do I find? Keypressed. Turns out my program used interrupts after all. – Jens Björnhager Aug 12 '10 at 17:54
  • I tried my second code stated above (accessing 0040:0017 at the BIOS DATA AREA) as well, and as far as I remember, under MS-DOS (running on VM-Ware) the changes were applied right after changing the byte. – Barak Aug 12 '10 at 22:56