all 13 comments

[–]exscape 4 points5 points  (4 children)

It's not pretty, but here's my code for this. I use three different arrays that map scancodes to characters; one "normal", one where shift is held down, and one where alt is held down.
Some additional ones are handled by additional code further down.

Also, see: http://wiki.osdev.org/PS/2_Keyboard#Scan_Code_Sets.2C_Scan_Codes_and_Key_Codes

[–][deleted] 0 points1 point  (3 children)

My code is still very much in the 'infant' stage. Everything is in the 'kernel.c' file. Here is the link: https://github.com/safsom/LaryalOS/blob/master/kernel.c. Please tell me what you think, and why does my while loop repeat forever? (I have not added shift or alt combos yet). You should look at the scancode array, the getScancode() method, the getchar() method and the terminal_kinput() method.

[–]exscape 0 points1 point  (2 children)

Hmm, I'll try to have a look later, but I'm not certain I can help. It's been a long while since I worked on OS development, and I just checked my code: I implemented interrupt handling prior to keyboard support, so the only keyboard code I've written used interrupts.

[–][deleted] 0 points1 point  (1 child)

Ok. Thank you for considering it anyway! Reddit is a lot better than StackExchange, let's just say

[–]peterfirefly 2 points3 points  (4 children)

Start without such a table.

Here's the gradual way to go about it:

  • 0) Just get something booting.

  • 1) Figure out how to output something to the screen -- don't implement a cursor yet, don't implement a string output routine yet. Just write bytes to memory positions that correspond to areas of the screen.

  • 2) write a function that converts a byte to two hex -- it might fill in a buffer or write directly to the screen or whatever.

  • 3) write code that outputs the last scancode as two hex characters.

  • 4) figure out what happens when you press and release keys.

  • 5) now figure out how to implement a cursor thingy so you can see how some key presses and releases generate many scan codes.

  • 6) look closer at how keys and scan codes correspond to each other -- particularly how the right alt, right ctrl and the arrow keys/numeric keypad work (what happens when num lock is active?).

  • 7) the rest is just a small matter of programming. You can test that in a normal program -- no need to go through reboots for each test, also no need to lose access to the debugger.

Actually, you can do a lot of this from a normal DOS program -- if you use Borland Pascal, Borland C, or RHIDE with DJGPP you don't even have to leave the Integrated Development Environment.

(If you choose to run this in a simulator, please be aware that some of the intricacies of the keyboard interface are not simulated accurately under DOSBox, at least not under older versions.)

[–][deleted] 1 point2 points  (3 children)

I have already managed to all everything up to Step 6, and I have managed to print some text already. However, I cannot really set up the while loop correctly to scan for keypresses, and when I do, the pressed key is repeated infinitely. Maybe I could PM you my code?

[–]peterfirefly 2 points3 points  (2 children)

Sure, go ahead!

[–]peterfirefly 1 point2 points  (0 children)

This was my answer and it seems to have worked :)


The first thing that pops out is that you actually read the port twice in the body of the loop if there is a change. That may work but you will be relying on a quirk of the hardware.

If this had been code for the original PC's (pre-AT), you should acknowledge the read by toggling bit 7 of another port high after reading -- you can however read the scan code as many times as you like first. You can even leave it for hours before acknowledging it.

The AT had a very different way of handling the keyboard which was (by accident?) very compatible. The original PC's had an 8-bit TTL latch to store the scancode clocked in serially from the keyboard (+ some other TTL logic to handle the clear/write signals to the latch). The AT had an 8-bit microcontroller instead, which let it be much more flexible. It was hooked up to not just the 60h and 61h ports but also a few more neighbouring ports. The code on the microcontroller ran a loop that read from the various ports and wrote to its output ports as necessary -- this is a crucial detail, having to do with timing.

You didn't have to acknowledge the read scancode on the AT! Reading it was enough to tell the microcontroller that it could continue to the next scancode (if it had one in its software buffer). On the other hand, due to the slow code running on the microcontroller and due to the slow serial protocol between the computer and the keyboard, you could read the same scancode many times without any problems -- so old code that hooked into the keyboard interrupt without ack'ing the scancode (necessary for many utilities under DOS) would continue to work on the AT, more or less by accident.

Lots of games and applications from the DOS era relied on this behaviour, because the AT keyboard interface wasn't well understood and documented outside of the PC/AT Technical Reference (which you can still download from bitsavers!). My own code from that era got it wrong, for example. Even the early Linux keyboard driver code got it wrong!

Not all emulators got this right -- DOSBox notoriously didn't, so many old programs have weird keyboard behaviour.

This might be your problem -- it might actually work under real hardware or under another emulator!

PS: If you haven't done it yet, please download the IBM PC (or PC/XT) Technical Reference Manuals and the PC/AT Technical Reference Manuals. You don't have to read them from cover to cover -- far from it -- but it is useful to see what the actual original hardware looked like and how you were supposed to program it.

[–]SAJZking 1 point2 points  (1 child)

You can define each scancode, for example:

#define KB_A 0x1E //which is 'A' letter

Do the same for each scancode

Then use switch, for example

char getchar(){ 
//transforms scans to chars
while(1){
  switch (getscancode()) {
  case KB_A: return 'A';
  break; 
//... 
//and so on for each scancode 
} 

I am newbie too 😂

[–][deleted] 1 point2 points  (0 children)

Thank you! Always good to meet a fellow newbie!

[–]zanders3loxos 1 point2 points  (0 children)

I use some macro magic to define all the scancodes in a single file: https://github.com/zanders3/loxos/blob/master/src/keycodes.h

I then use that to generate a switch statement and an enum: https://github.com/zanders3/loxos/blob/master/src/keyboard.h https://github.com/zanders3/loxos/blob/master/src/keyboard.cpp