Page 1 of 2

Keyboard in QEMU extremly slow

Posted: Sun Sep 28, 2014 7:16 am
by Muazzam
I usually used to test my OS on virtualbox and real hardware. Today I tested it on QEMU. My PS/2 keyboard getch like function runs normal on virtualbox as well as real hardware but keyboard input on QEMU become painfully slow. Also I can not type anything correct with this. Code for my getch like funtion:

Code: Select all

;______________________________________________________________________________________________________
;Wait for keyboard keypress
;IN: 	nothing
;OUT:	CF sets on keyup and CF clears on keydown
;	AL ASCII code
;	AH SCAN code
waitKeyboard:
	push ebx
	mov byte[kbdHandler.keyboardChanged], 0
.keyLoop:
	cmp byte[kbdHandler.keyboardChanged], 1		;Check if key press
	jne .keyLoop


	mov ah, bl			;BL contains scancode
	mov al, ah
	
	bt ax, 7			;Check if keyup
	jc .end
  
	;Check if shift is pressed
	cmp al, 54			;Right shift key
	je .shiftSet
	cmp al, 42			;Left shift key
	je .shiftSet
	jmp .next
.shiftSet:
	mov byte[.shiftFlag], 1		;.shiftFlag variable indicated weather shift is pressed or released
	mov al, 0
	stc
	jmp .actualEnd
.next:
	cmp byte[.shiftFlag], 1		;Use shift key characters if shift is pressed
	je .shiftIsSet
	
	 	
	
	movzx ebx, al
	add ebx, .keys			;Index scan code in Keys array
	mov al, byte[ebx]
	clc
	jmp .actualEnd
.shiftIsSet:
	movzx ebx, al
	add ebx, .keysShift		;Index scan code in shift-Keys array
	mov al, byte[ebx]
	clc
	jmp .actualEnd
;Now we have to check if shift is released 
.end:
	stc
	cmp al, 182			;Right shift released
	je .noShift
	cmp al, 170			;Left shift released
	je .noShift

	mov al, 0
	stc
	jmp .actualEnd
.noShift:
	mov al, 0
	mov byte[.shiftFlag], 0
	stc
.actualEnd:
	pop ebx	
	ret

;--------Variables----------;
.shiftFlag:
	db 0
.keys:
	db 27,0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 8, 9, 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']'
	db 13, 29, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', "'", '`', 42, '\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/'
	db 54, 55, 56, ' '
.keysShift:
	db 27,0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 8, 9, 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}'
	db 13, 29, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 42, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?'
	db 54, 55, 56, ' '
;_________________________________________________________________________________________________
Code for keyboard handler:

Code: Select all

Keyboard interrupt IRQ 1
kbdHandler:
	push eax
	xor eax,eax

	in al,0x60
 
.next:	
        cmp byte[.keyboardChanged], 0		;If any code is waiting for keyboard
	je .send				;send it in BL
	jmp .end
.send:
	mov bl, al
.end:
	mov byte[.keyboardChanged], 1

	mov al, 0x20
	out 0x20, al
	pop eax
	iret

.keyboardChanged:	db 0
I have tested other OSs in QEMU they work fine.
Any suggesions?

Re: Keyboard in QEMU extremly slow

Posted: Sun Sep 28, 2014 8:09 am
by ExeTwezz
Did you try to run your OS on Bochs?

Re: Keyboard in QEMU extremly slow

Posted: Sun Sep 28, 2014 8:16 am
by Muazzam
ExeTwezz wrote:Did you try to run your OS on Bochs?
It is also slow on Bochs.

Re: Keyboard in QEMU extremly slow

Posted: Sun Sep 28, 2014 8:20 am
by ExeTwezz
Are there warning messages when you press a key?

Re: Keyboard in QEMU extremly slow

Posted: Sun Sep 28, 2014 8:26 am
by Muazzam
ExeTwezz wrote:Are there warning messages when you press a key?
No

Re: Keyboard in QEMU extremly slow

Posted: Sun Sep 28, 2014 8:32 am
by zhiayang
You might want to change the key repeat factor in the PS/2 keyboard controller, I've found that this helps in QEMU.

Re: Keyboard in QEMU extremly slow

Posted: Sun Sep 28, 2014 8:37 am
by ExeTwezz
I think this is emulator's configuration problem.

Re: Keyboard in QEMU extremly slow

Posted: Sun Sep 28, 2014 11:51 am
by Combuster
I see that your key grabbing function only handles a key when the interrupt fires inside the key requesting function. That means that any keypress when you are not waiting for it is lost. That can have all sorts of practical consequences, most of them undesired.

And by virtue of the process, if you add more code, either debugging or functionality to this, or switch to a slower machine like bochs, the problem actually gets worse. Implement a proper event queue rather than letting yourself be tormented by kludges around race conditions.

Re: Keyboard in QEMU extremly slow

Posted: Mon Sep 29, 2014 4:45 am
by Muazzam
Combuster wrote:I see that your key grabbing function only handles a key when the interrupt fires inside the key requesting function. That means that any keypress when you are not waiting for it is lost. That can have all sorts of practical consequences, most of them undesired.

And by virtue of the process, if you add more code, either debugging or functionality to this, or switch to a slower machine like bochs, the problem actually gets worse. Implement a proper event queue rather than letting yourself be tormented by kludges around race conditions.
Thanks for help, my problem solved by this:

Code: Select all

;Keyboard interrupt IRQ 1
kbdHandler:
	push eax
	xor eax,eax

	in al,0x60
 
	mov byte[.scanCode], al
	mov byte[.keyboardChanged], 1

	mov al, 0x20
	out 0x20, al
	pop eax
	iret

.keyboardChanged:	db 0
.scanCode		db 0

Re: Keyboard in QEMU extremly slow

Posted: Mon Sep 29, 2014 6:11 am
by Combuster
I'm still missing the event queue, and I'll be waiting for your next thread for that exact reason. :wink:

Re: Keyboard in QEMU extremly slow

Posted: Mon Sep 29, 2014 6:20 am
by Muazzam
Combuster wrote:I'm still missing the event queue, and I'll be waiting for your next thread for that exact reason. :wink:
But it still works best without event queue. What is need of event queue?

Re: Keyboard in QEMU extremly slow

Posted: Mon Sep 29, 2014 11:51 am
by SpyderTL
Because the keyboard buffer isn't big enough to guarantee that you wont lose key press events if the system is busy, or if there are a lot of key press events fired at the same time. As a matter of fact, some key presses end up sending two or more key press events, back-to-back. (The "media" buttons come immediately to mind. Play, Pause, Volume Up/Down, etc.)

In this case, you will get two or more IRQ1 interrupts back to back, but you are only storing one value. Your .scanCode variable will be overwritten with the last scan code value, and all previous scan codes will be lost.

You need to read the key press events from the (small) keyboard buffer, and move them to the (larger) key press event queue, so that you can guarantee that they will be handled when the system is ready for them, and not lost.

As long as you understand the timing issues with your solution, and as long as it works for you, feel free to keep using it as-is. But just remember that this code will probably need to be redesigned at some point in the future.

Re: Keyboard in QEMU extremly slow

Posted: Tue Sep 30, 2014 5:15 am
by Muazzam
SpyderTL wrote: In this case, you will get two or more IRQ1 interrupts back to back, but you are only storing one value. Your .scanCode variable will be overwritten with the last scan code value, and all previous scan codes will be lost.

You need to read the key press events from the (small) keyboard buffer, and move them to the (larger) key press event queue, so that you can guarantee that they will be handled when the system is ready for them, and not lost.
But how should I do it?

Re: Keyboard in QEMU extremly slow

Posted: Tue Sep 30, 2014 6:02 am
by iansjack
muazzam wrote:
SpyderTL wrote: In this case, you will get two or more IRQ1 interrupts back to back, but you are only storing one value. Your .scanCode variable will be overwritten with the last scan code value, and all previous scan codes will be lost.

You need to read the key press events from the (small) keyboard buffer, and move them to the (larger) key press event queue, so that you can guarantee that they will be handled when the system is ready for them, and not lost.
But how should I do it?
SpyderTL explained in his post exactly what you need to do. You even quoted that explanation. So what is the problem?

You can use either an array or a linked list to store the key press events; personally I would use a linked list as that places no limit on the number of characters, but there are valid arguments as to why an array might be better (or at least easier).

Re: Keyboard in QEMU extremly slow

Posted: Wed Oct 01, 2014 8:56 am
by SpyderTL
muazzam wrote:
SpyderTL wrote: In this case, you will get two or more IRQ1 interrupts back to back, but you are only storing one value. Your .scanCode variable will be overwritten with the last scan code value, and all previous scan codes will be lost.

You need to read the key press events from the (small) keyboard buffer, and move them to the (larger) key press event queue, so that you can guarantee that they will be handled when the system is ready for them, and not lost.
But how should I do it?
That's up to you, but I would start by changing .scanCode from a 32-bit number to an array of 32 bit numbers. Then change the .keyboardChanged variable from a Boolean 32-bit number to a counter. (i.e. change mov [.keyboardChanged], 1 to inc [.keyboardChanged]).

Then just loop through the .scanCode array using the count from the .keyboardChanged counter, and read the scan codes. Then when you are done, you can just set the .keyboardChanged back to 0.

In your INT 0x01 handler, you will need to use the .keyboardChanged counter to figure out where to store the scan code in the .scanCode array.

This is the easiest solution I can think of, given your current code. It should be usable until you decide to replace it with something a lot more complex, like a keyboard driver and an event system.