Keyboard Input

  • Follow


I have been trying to figure out how to write a text input driver that
is loaded by a boot sector. I recently started a post about printing
characters on the screen using B800h, which I now have in my grasp
(thanks to some help from everyone who replied :).

Now I am going to try to get it to accept inputs from the keyboard to
try to make a command prompt or similar (as an exercise at this
point).

The bootsector boots the PC into real mode. I haven't even looked at
protected mode yet. I would eventually like my code or parts of it to
be reused in a protected mode environment.

I read in a tutorial (http://www.joelgompert.com/OS/lesson7.htm) that
using the BIOS to get the keyboard input is slow, and is not supported
in protected mode.

My question then is how can I get input without using BIOS interrupts?
If I were to guess, I would think that a keypress would be stored in
memory, and that I would just have to read it to find the key. But
that's just my guess...

If anyone can start me off or point me in the right direction, that'd
be great :)
Thanks.

0
Reply spamtrap2 (1628) 2/14/2007 5:06:55 AM

Luke schrieb:
> My question then is how can I get input without using BIOS interrupts?

in    al, 64h   ; Status
test  al, 1     ; output buffer full?
jz  short NOKEY
test  al, 20h   ; PS2-Mouse?
jnz short NOKEY
in    al, 60h

dec   al
jz  HOME        ; escape?


;---------
NOKEY:


;---------
HOME:

;---------

Dirk

0
Reply Dirk 2/14/2007 6:43:29 AM


Luke wrote:
> I have been trying to figure out how to write a text input driver that
> is loaded by a boot sector. I recently started a post about printing
> characters on the screen using B800h, which I now have in my grasp
> (thanks to some help from everyone who replied :).
> 
> Now I am going to try to get it to accept inputs from the keyboard to
> try to make a command prompt or similar (as an exercise at this
> point).

Logical next step, I guess. What commands do you propose to support?

> The bootsector boots the PC into real mode. I haven't even looked at
> protected mode yet. I would eventually like my code or parts of it to
> be reused in a protected mode environment.

Better count on reusing the knowledge you gain from coding it. I doubt 
if you'll find that much of the code you're writing now can be used in 
pmode - you'll want to do things a slightly different way.

> I read in a tutorial (http://www.joelgompert.com/OS/lesson7.htm) that
> using the BIOS to get the keyboard input is slow,

True. Do we care? We'll be done long before the user gets his fat thumb 
off the key, anyway.

> and is not supported
> in protected mode.

True. And writing a keyboard handler for rmode will prepare you for 
writing one for pmode, and will introduce you to handling hardware 
interrupts - which you'll be doing a lot of, I suspect.

> My question then is how can I get input without using BIOS interrupts?
> If I were to guess, I would think that a keypress would be stored in
> memory, and that I would just have to read it to find the key. But
> that's just my guess...

That's true, but the keypress has been put in memory by the bios! The 
"user mode" bios interface to the keyboard is int 16h. But the bios also 
installs the "int 9 handler" which responds to actual keyboard 
interrupts. When the user presses a key (or releases it!), an IRQ is 
generated, which the bios has programmed the interrupt controller to 
direct to int 9. The code there gets the raw scancode from the port - 
Dirk has given you the basics of that - and "processes" it. The raw 
scancode is looked up in a table and converted to an ascii code, in the 
most common case - different table for US keyboard, Dvorak... who knows 
what they do in China? :) This info is stored in the keyboard buffer, 
and a pointer to "next" is updated.

In most cases, the "release" interrupt (high bit set in the scancode) is 
just ignored. But in some cases, the pressed/released state must be 
stored - shift-key is an obvious one. The bios stores it as bit-flags in 
a couple bytes in the bios data area. You'll have to keep track of what 
you want, somewhere. Some game programmers reprogram the int 9 handler 
to handle multiple keys - up-arrow + left-arrow = diagonal, e.g.

Some keys cause more than a single byte to appear at the port 
(sequentially). This has to be watched for and handled. Not all keys 
have an ascii code! :)

I recall seeing a nicely-commented int 9 handler somewhere on the 
Webster site - in the old dos section - examples, or laboratory 
exercises(?). It was pretty complete, except it didn't handle alt-keypad 
numeric entries - and there were comments where the code for that needed 
to go...

> If anyone can start me off or point me in the right direction, that'd
> be great :)

I think you should *consider* going ahead with your "command processor" 
using bios services for keyboard input. You'll learn a lot by writing 
your own keyboard handler, and you'll have to do it (or swipe one) for 
pmode, but it may "stall" you for a while. Programmer's choice! :)

Best,
Frank

0
Reply Frank 2/14/2007 1:33:27 PM

Thanks to Dirk and Frank for their help. I now have a keyboard
handler. Well, a very basic one, but it's the start of something good.
I also used an example that I found at http://inglorion.net/documents/tutorials/x86ostut/keyboard/

There is a copy of the code below if anyone is interested in the
future. I have commented it as best as I can, but there is still a
line that I don't really understand (I got it from the example).
If anyone has any comments, better ways to explain what's going on, or
better coding techniques, please post them :)




Start:
  XOR AX,     AX			;Clear DS
  MOV DS,     AX

  ;To run an interupt handler, the system multiplies the interupt by
4,
  ;and then jumps to that location in memory.

  ;Numbers in square brackets (eg, [36]) are locations in memory.

  ;Interupts need to be disabled so the keyboard driver doesn't get
called
  ;while we're running our handler (and pointing to a weird place in
memory)

  CLI					;Disable interupts
  MOV [36],   WORD Handler		;4 x keyboard interupt (9)
MOV [4 * 9 + 2], CS		;I'm not sure what this does
  STI					;Reenable interupts


  JMP Start				;Loop continually



;--------------------------------------------------------------------------
;Procedures


  ;IN and OUT commands are used with the I/O ports, which the CPU uses
to
  ;communicate with hardware. IN reads information from a port. OUT
sends
  ;information to a port.

Handler:
  PUSHA					;Save all registers

  ;Port 60h is the port where keyboard data goes

  IN AL,     60h			;Read keyboard information into AL


  ;Testing the high bits of AL (80h is the 4 high bits). This is where
the
  ;'release' information is stored (used with the shift key, etc).

  ;If there is no high bits set, the zero flag is set.

  TEST AL,   80h			;Test codes for set high bits
  JNZ        End			;Skip procedure if there are high bits


  ;Use the scan code and the table (below) to get the ASCII code

  XOR BX,    BX				;Clear BX
  MOV BL,    AL				;'Copy' AL (the scancode) into BL
  MOV AL,    [CS:BX + KeyTable]		;Convert scancode to ASCII and put it
in AL


  ;Display characters on the screen

  PUSH DI				;Save ES:DI
  PUSH ES				;ES:DI changes while writing to video memory
  MOV DX,     0B800h			;Move video memory location to ES
  MOV ES,     DX
  MOV DI,     [CS:intCursor]		;Point to the cursor location in video
memory
  MOV [ES:DI], AL			;Move the ASCII code into video memory
  POP ES				;Restore ES:DI
  POP DI
  ADD WORD    [CS:intCursor], 2		;Move the cursor to the next
character position.
					;(1 byte for the character, 1 for the attribute)

End:
  ;When an IRQ is triggered, the IRQ is disabled while the event is
being
  ;handled. Port 61h enables it again, and port 20h sends data to the
  ;interrupt controller. (EOI - End Of Interupt).

  MOV AL,     61h
  OUT 20h,    AL

  POPA					;Restore registers
  IRET					;Interupt return



;--------------------------------------------------------------------------
;Data


intCursor dw 0


;A table to convert scancodes to ASCII characters.
;QWERTY table

KeyTable db  0, 27,'1','2','3','4','5','6','7','8','9','0','-','=', 8
db  9, 'q','w','e','r','t','y','u','i','o','p','[',']', 13
db  0, 'a','s','d','f','g','h','j','k','l',';',"'",'`'
db '\\','z','x','c','v','b','n','m',',','.','/', 0
db '*', 0 ,' ', 0 , 0 , 0 , 0 , 0 , 0 , 0
db  0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
db  0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
db  0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
db  0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0



0
Reply Luke 2/20/2007 5:49:48 AM

Luke wrote:
> Thanks to Dirk and Frank for their help. I now have a keyboard
> handler. Well, a very basic one, but it's the start of something good.
> I also used an example that I found at http://inglorion.net/documents/tutorials/x86ostut/keyboard/
> 
> There is a copy of the code below if anyone is interested in the
> future. I have commented it as best as I can, but there is still a
> line that I don't really understand (I got it from the example).
> If anyone has any comments, better ways to explain what's going on, or
> better coding techniques, please post them :)
> 
> 
> 
> 
> Start:
>   XOR AX,     AX			;Clear DS
>   MOV DS,     AX
> 
>   ;To run an interupt handler, the system multiplies the interupt by
> 4,
>   ;and then jumps to that location in memory.

Well... jumps to the address that it finds at that address in memory. 
Each address is 4 bytes - 2 bytes offset, and two bytes segment.

>   ;Numbers in square brackets (eg, [36]) are locations in memory.
> 
>   ;Interupts need to be disabled so the keyboard driver doesn't get
> called
>   ;while we're running our handler (and pointing to a weird place in
> memory)
> 
>   CLI					;Disable interupts
>   MOV [36],   WORD Handler		;4 x keyboard interupt (9)
> MOV [4 * 9 + 2], CS		;I'm not sure what this does

It fills in the segment part of the address. "Handler" is only the offset.

>   STI					;Reenable interupts
> 
> 
>   JMP Start				;Loop continually

This seems silly - I suppose you've gotta do something, but...

> ;--------------------------------------------------------------------------
> ;Procedures
> 
> 
>   ;IN and OUT commands are used with the I/O ports, which the CPU uses
> to
>   ;communicate with hardware. IN reads information from a port. OUT
> sends
>   ;information to a port.
> 
> Handler:
>   PUSHA					;Save all registers
> 
>   ;Port 60h is the port where keyboard data goes
> 
>   IN AL,     60h			;Read keyboard information into AL
> 
> 
>   ;Testing the high bits of AL (80h is the 4 high bits). This is where
> the
>   ;'release' information is stored (used with the shift key, etc).
> 
>   ;If there is no high bits set, the zero flag is set.

80h is only one high bit (10000000b). And only that bit indicates a 
"release" scancode. Your code's okay, but the comment is misleading.

>   TEST AL,   80h			;Test codes for set high bits
>   JNZ        End			;Skip procedure if there are high bits
> 
> 
>   ;Use the scan code and the table (below) to get the ASCII code
> 
>   XOR BX,    BX				;Clear BX
>   MOV BL,    AL				;'Copy' AL (the scancode) into BL
>   MOV AL,    [CS:BX + KeyTable]		;Convert scancode to ASCII and put it
> in AL
> 
> 
>   ;Display characters on the screen
> 
>   PUSH DI				;Save ES:DI
>   PUSH ES				;ES:DI changes while writing to video memory
>   MOV DX,     0B800h			;Move video memory location to ES
>   MOV ES,     DX
>   MOV DI,     [CS:intCursor]		;Point to the cursor location in video
> memory
>   MOV [ES:DI], AL			;Move the ASCII code into video memory

You might want to set the attribute here, too, rather than let it be 
"what it was"...

>   POP ES				;Restore ES:DI
>   POP DI
>   ADD WORD    [CS:intCursor], 2		;Move the cursor to the next
> character position.
> 					;(1 byte for the character, 1 for the attribute)
> 
> End:
>   ;When an IRQ is triggered, the IRQ is disabled while the event is
> being
>   ;handled. Port 61h enables it again, and port 20h sends data to the
>   ;interrupt controller. (EOI - End Of Interupt).
> 
>   MOV AL,     61h
>   OUT 20h,    AL

Well... you're not doing anything with "port 61h" here. You're sending 
the value 61h to port 20h. You do want to send an EOI to port 20h, but 
my fuzzy memory is that it's 20h we want to send, not 61h. I'd have to 
look it up. I see your example does it that way, but I suspect it may be 
"working but not right".

>   POPA					;Restore registers
>   IRET					;Interupt return
[snip]

Good start! You might want to replace the section where you display the 
key with a routine that just saves the key in a buffer. Then, up where 
you loop continually through the install process, instead loop through a 
routine that sees if there's a key available, and prints it if there is. 
Then you can think about seeing if the user has entered a "command". 
(not sure what the "commands" should be - "dir" is obvious, but that 
requires a filesystem... :)

Best,
Frank

0
Reply Frank 2/20/2007 4:10:20 PM

>
> >   STI                                      ;Reenable interupts
>
> >   JMP Start                                ;Loop continually
>
> This seems silly - I suppose you've gotta do something, but...
>
>

I have adjusted it so it does not loop through the whole program
again. For the purposes of this exercise, I just put in:

Hang:
  JMP Hang

>
> Good start! You might want to replace the section where you display the
> key with a routine that just saves the key in a buffer. Then, up where
> you loop continually through the install process, instead loop through a
> routine that sees if there's a key available, and prints it if there is.
> Then you can think about seeing if the user has entered a "command".
> (not sure what the "commands" should be - "dir" is obvious, but that
> requires a filesystem... :)
>
> Best,
> Frank



I have updated the code with your suggestions. Thanks, you have helped
me understand it a lot better.
My next challenge is the input buffer.
Once I've got that going, I will work on the command interpreter.
Initally I won't have any useful commands. I'll figure out how to get
it to recognise commands, be able to tell if their real or garbage
(but I won't be using "bad command or file name" :) ), etc.

0
Reply Luke 2/21/2007 4:58:56 AM

5 Replies
671 Views

(page loaded in 0.467 seconds)

Similiar Articles:













7/22/2012 5:57:59 PM


Reply: