views:

242

answers:

2

Hello, 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??

A: 

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
60h is the keyboard port, while 0040:0017 is the address of keyboard flags as maintained by the BIOS.
ninjalj
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
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
I didn't have to call any BIOS interrupts for flashing the leds back in the day.
Jens Björnhager
@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
If I remember correctly, this code stopped working with the advent of newer Windowses.
Jens Björnhager
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
Digging deeper into my sources, what do I find? Keypressed. Turns out my program used interrupts after all.
Jens Björnhager
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
A: 

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
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