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