Keyboard Leds

  That's an explanation on how the keyboard leds routines works in Allegro. To understand it you must see the allegro code first.

  First of all I'll explain how to send commands to the keyboard. The 0x60 port is the one used to send commands and data in our case. The 0x64 port is the status port and the bits are (when reading, when writing is a command port):

0064   r   KB controller read status (ISA, EISA)
           bit 7 = 1 parity error on transmission from keyboard
           bit 6 = 1 receive timeout
           bit 5 = 1 transmit timeout
           bit 4 = 0 keyboard inhibit
           bit 3 = 1 data in input register is command
               0 data in input register is data
           bit 2   system flag status: 0=power up or reset  1=selftest OK
           bit 1 = 1 input buffer full (input 60/64 has data for 8042)
           bit 0 = 1 output buffer full (output 60 has data for system)
  So in order to send data to the keyboard we must check the bit 1, if this bit is 1 the keyboard can't receive a new command.
  The following routine waits until the keyboard can receive commands:

#define MAX_TRYES_FOR_READY 1000000L static void kbWaitForReady(void) { volatile long i=MAX_TRYES_FOR_READY; while (i-- && (inportb(0x64) & 0x02)); }   To send a command we can simply make:

static inline void KbSendCommand(unsigned char c) { kbWaitForReady(); outportb(0x64,c); }   Note that here we used 0x64 and not 0x60 because formally 0x64 is the one used for commands.
  Now to set the leds we need to send a 0xED command followed by the new status for the leds, here is the command:

        ED dbl   set/reset mode indicators Caps Num Scrl
                 bit 2 = CapsLk, bit 1 = NumLk, bit 0 = ScrlLk
  And here is the code used:

#define UpdateLeds() \ { \ if (!kbSendDataToKb(0xed) || !kbSendDataToKb(_shifts_flags>>3)) \ kbSendDataToKb(0xf4); \ }   The 0xF4 command is just used in the case that all failed and the keyboard self entered in disable mode.
  Now comes the problem, in order to send the data to the keyboard we must send the command and wait for an acknowledge from the keyboard, WE MUST receive the acknowledge before sending a new command. To achieve that we can use the following routine:

#define MAX_TRYES_TO_SEND_DATA 4 #define MAX_TRYES_TO_RESPONSE 2000000L /* * kbSendDataToKb: * Sends a byte to the keyboard. * Returns 1 if all OK. */ static int kbSendDataToKb(unsigned char data) { volatile long i; int kbResends = MAX_TRYES_TO_SEND_DATA; do { kbWaitForReady(); /* Wait until the input buffer is free */ kbNeedReply = 1; /* We are waiting for an answer from the kbd chip */ /* We don't have an acknowledge yet */ /* The kbd doesn't asked for resend */ kbAckResponse = kbResendResponse = 0; outportb(0x60,data); /* Now we send the data */ i=MAX_TRYES_TO_RESPONSE; while (--i) { inportb(0x64); /* Just to force a delay */ if (kbAckResponse) return 1; if (kbResendResponse) break; } } while (kbResends-->0 && i); return 0; }   Note that we are using EVER the 0x60 port, I mean, we use port 0x60 for data and commands. That's correct even when you can use 0x64 for commands and 0x60 for data. Using 0x60 for both makes the code more simple.
  The most complex thing to implement is the "wait for ack.". The ack. is send by the keyboard as a 0xFA value, for that we NEED the interrupts enabled or we'll never get the ack. For this reason the keyboard leds code is at the end of the ISR like that:

SetKeyboardLeds: outportb(0x20,0x20); /* End Of Interrupt */ enable(); /* Enable IRQs */ /* From here we are virtually outside the ISR BUT we still inside of the ISR so the ISR will be reentered, yes, we are using an stack and the new IRQ will interfer if it uses the same stack !!!!! */ UpdateLeds(); return RetValFromHandler; }   So here you can see that some problems can appear if you use a fixed area of the RAM as stack for your ISR. In real-mode programs the ISRs simply use the stack of the interrupted routine, so if the interrupted routine is the ISR it will use the next free bytes in the stack, here is the code generated by BCC (The code can be used with BCC):

; ; static fISR my_keyint(VarArgList) ; ?debug L 402 assume cs:KEYBOARD_TEXT @my_keyint$qve proc far ?debug B push ax push bx push cx push dx push es push ds push si push di push bp mov bp,DGROUP mov ds,bp mov bp,sp sub sp,6 ?debug B   What about djgpp code? Well Allegro uses the following:

movl $IRQ_STACKS-1, %ecx /* look for a free stack */ ; \ ; \ stack_search_loop_##x: ; \ leal __irq_stack(, %ecx, 4), %ebx ; \ cmpl $0, (%ebx) ; \ jnz found_stack_##x /* found one! */ ; \ ; \ decl %ecx ; \ jge stack_search_loop_##x ; \ ; \ jmp get_out_##x /* oh shit.. */ ; \ ; \ found_stack_##x: ; \ movl %esp, %ecx /* old stack in ecx + dx */ ; \ movw %ss, %dx ; \ ; \ movl (%ebx), %esp /* set up our stack */ ; \ movw %ax, %ss ; \ ; \ movl $0, (%ebx) /* flag the stack is in use */ ; \ ; \ pushl %edx /* push old stack onto new */ ; \ pushl %ecx ; \ pushl %ebx ; \   Allegro have an array with ISR stacks and assigns the first free stack to the IRQ so there is no problem and the new IRQ will use a different stack. Be carefull, you can't use the same portion of memory as stack twice.

  Then in the ISR we simply make:

if (kbNeedReply) { /* 0xFA == ack. */ /* 0xFE == resend */ if (temp==0xFA) { kbAckResponse = 1; kbNeedReply = 0; goto Exit_Keyboard_Handle; } else if (temp==0xFE) { kbResendResponse = 1; kbNeedReply = 0; goto Exit_Keyboard_Handle; } }   Setting the kbAckResponse and kbResendResponse vars according to the response.

  If you need more information just contact me.


Created by SET, [Home Page] [Feedback] [DJGPP stuff]

This page hosted by Get your own Free Home Page


1