开发者

Assembly Keyboard IO Port

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 s开发者_如何转开发earched 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??


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.


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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜