Is this a proper way of reading the KBC?
Posted: Fri Nov 05, 2010 10:18 pm
I have been trying to test some things such as enabling the PS/2 mouse and thus work with the keyboard controller. There are some commands that according to the wiki could or could not return an ACK:
http://wiki.osdev.org/Mouse_Input#Set_C ... able_IRQ12
It uses the RTC without interrupts. In the worst of cases it waits 1-2 seconds (one second is what's intended) when the expected bytes that it tries to read are more than the ones in the keyboard controller. Otherwise it is very fast.
It tries to see if the keyboard controller is offering data while the RTC switches between "updating" and "updated". It reads it as soon as it's available and finishes as soon as the number of expected bytes (as a parameter by the programmer) are actually retrieved.
Then it returns the number of bytes actually read.
But that particular problem still remains. At least when initializing it may not be too important to wait 1-2 more seconds, but more than likely there has to be a better, faster and safe potentially-bug-free way.
Maybe using the PIT would make for better time precision and less noticeable delays, but how to use it without IRQs on each update and the whole IDT and PIT reprogramming dependence would now be the problem just for PS/2 initialization. It would look too bulky.
What do you think about this method?
Of course I only plan to use it for gathering of data bytes during initialization, and once everything is set up I will gather the data using the IRQs.
http://wiki.osdev.org/Mouse_Input#Set_C ... able_IRQ12
The other problem I have is that I need a loop that doesn't wait too much or too little time. I couldn't do it reliably across machines of 500MHz, 2GHz, 2.9GHz, etc., using LOOP instructions, so I had to make the function below.Set Compaq Status/Enable IRQ12
.....
Then send command byte 0x60 ("Set Compaq Status") to port 0x64, followed by the modified Status byte to port 0x60. This might generate a 0xFA ACK byte from the keyboard.
It uses the RTC without interrupts. In the worst of cases it waits 1-2 seconds (one second is what's intended) when the expected bytes that it tries to read are more than the ones in the keyboard controller. Otherwise it is very fast.
It tries to see if the keyboard controller is offering data while the RTC switches between "updating" and "updated". It reads it as soon as it's available and finishes as soon as the number of expected bytes (as a parameter by the programmer) are actually retrieved.
Then it returns the number of bytes actually read.
But that particular problem still remains. At least when initializing it may not be too important to wait 1-2 more seconds, but more than likely there has to be a better, faster and safe potentially-bug-free way.
Maybe using the PIT would make for better time precision and less noticeable delays, but how to use it without IRQs on each update and the whole IDT and PIT reprogramming dependence would now be the problem just for PS/2 initialization. It would look too bulky.
What do you think about this method?
Of course I only plan to use it for gathering of data bytes during initialization, and once everything is set up I will gather the data using the IRQs.
Code: Select all
while(seconds_count!=0)
{
1. Disable interrupts
1.1. Check for a keyboard data byte; retrieve and account for if present. End loop if the expected number of bytes has been read.
2. Check if the CMOS is making a update
3. If not, do a NOP, then go to Step 1
4. Check for a keyboard data byte; retrieve and account for if present. End loop if the expected number of bytes has been read.
4.1. Check if the CMOS is making a update again
5. If so,do a NOP, then go to Step 4
6. Check for a keyboard data byte; retrieve and account for if present. End loop if the expected number of bytes has been read.
7. seconds_count--;
}
Code: Select all
// Inputs:
// EDI==Keyboard controller data destination
// CL==Number of (potential) seconds to wait
// CH==Number of bytes to expect from KBC
//
//Outputs:
//EBX or BL==Number of retrieved bytes
///
/*$EBX */function kbc_MULTI_readDataPort(/*$WIDEDI buffer destination, $CH num_expected_data_bytes*/)
{
push $WIDEAX;
push $WIDECX;
push $WIDEDI;
push $WIDEDX;
$WIDEBX=0; //Set the number of retrieved bytes to 0
$DX=0x60; //keyboard data port {BYTE}
while($CL!=0) //As long as seconds count is not reached
{
.Step1:
//Disable Interrupts
///
asm cli
.Step2:
//Check for KBC data:
///
asm in al,0x64 ;//Port 0x64 is the Status
$AL&=1;
if($AL==1) //Data is waiting to be read
{
asm insb //Read it
$WIDEBX++; //Count one more byte
if($CH==$BL)goto .myends; //If the expected number of bytes
//are already read, end waiting, finish!
}
//Check if the CMOS is making an update
///
$AL=0x0A;
asm out 0x70,al
asm in al,0x71
$AL&=10000000b;
.Step3:
//If not, do a NOP, then go to Step1
/// _
if($AL==00000000b)
{
asm cli
asm nop
goto .Step1;
}
.Step4:
//Check for KBC data:
///
asm in al,0x64 ;//Port 0x64 is the Status
$AL&=1;
if($AL==1) //Data is waiting to be read
{
asm insb //Read it
$WIDEBX++; //Count one more byte
if($CH==$BL)goto .myends; //If the expected number of bytes
//are already read, end waiting, finish!
}
//Check if the CMOS is making an update again
///
$AL=0x0A;
asm out 0x70,al
asm in al,0x71
$AL&=10000000b;
.Step5:
//If so, do a NOP, disable interrupts again,
//then go to Step4
/// _
if($AL==10000000b)
{
asm cli
asm nop
asm cli
goto .Step4;
}
.Step6:
//If not, read (all) the registers as quickly as you can:
///
asm cli
//Check for KBC data:
///
asm in al,0x64 ;//Port 0x64 is the Status
$AL&=1;
if($AL==1) //Data is waiting to be read
{
asm insb //Read it
$WIDEBX++; //Count one more byte
if($CH==$BL)goto .myends; //If the expected number of bytes
//are already read, end waiting, finish!
}
//Count down seconds
$CL--;
}
.myends:
pop $WIDEDX;
pop $WIDEDI;
pop $WIDECX;
pop $WIDEAX;
}