Get Some Input

2021-08-06

This is continuation post for my own kernel series:

Suggest reading them before continuing here.

Last time I managed to get MicroPython working on my kernel. It was able to run apps from the disk and provided filesystem. The interactive input was disabled, since my kernel didn’t actually support reading from stdin. Thus I decided it was time to try to get that working.

Framebuffer issues

First thing I knew was framebuffer issues. It didn’t really support scrolling. It just simply wrapped over, and wrote on top of whatever was there. And since my font didn’t write background the text was mixed with whatever was written or drawn there.

One easy fix could have been just writing black background, but I wanted to write text on top of drawn context without having ugly black boxes there. Solution was scrolling, and there was indeed support for text mode. Just needed to extend it to support the framebuffer and font drawing.

In that sense it’s simple, you just copy the current buffer upwards defined number of lines, fill the rest of the screen with black, and update text locations accordingly.

And yes, in the end it was quite easy, but mixed up few things and it took bit longer than anticipated. Also at the same time I wanted to use the whole framebuffer area. For now the text area was fixed to traditional 80x25, and I wanted it to scale to whatever size the screen had. Thus I needed to get the screen size, and divide with the font size. It was luckily quite simple. However spotted some bugs like missing scrolls on empty lines. Once all this was fixed it worked like charm.

Reading keyboard

I had primitive hook in interrupt handler to read generated keycode. For now I needed to extend it to convert those into ASCII codes (or UTF-8). And to make it worked properly in the wide world I needed different layout supports. My local keyboard layout is Finnish (or Swedish) and I wanted to support that one. And of course I wanted to default to the US keyboard layout, which is way much more common out there.

There’s now support for different keyboard layouts, but the configuration is still missing and it has to be changed in source code. Later on I might do something for it but for now it’s good enough.

After having layouts I extended keyboard interrupt to convert the key codes into text. And to fully support it, I added modifiers for certain keys like Alt, Shift, and Ctrl. With Shift I could write uppercase and special characters as well!

But this was only the first step. I could convert the keys to something, but everything was still in the interrupt context.

Key ring buffer

On issue in interrupt based input is that the interrupt just happens whenever key is pressed. Application or system reading the key might not happen at the same time. Or there might not be any indication of it. And how to decide where the keypress was meant to go?

My solution for this was a simple ring buffer. This way I could store multiple keypress, and feed them in order to whoever is requesting them. In case nobody reads for a long time, the first keypresses from the buffer just disappears. On my simple tests that seemed to work, so wont pay more attention for that for now but it might need some improvements later on.

After the ring buffer worked like planned, needed to get stdin read to fetch values from there. Thus hooked into read() and readv() system calls to get the text from the keyboard ring buffer when fd == 0 ie. stdin.

It was bit tricky to get it all working like I wanted, but finally it worked as expected. I don’t fully like it, and it’s not that elegant but it works for now. One thing is that it only supports ASCII, but I would like it to be full UTF-8, and to support that means way much more steps to be done. So it’s future problem.

MicroPython

I wanted MicroPython to work well with this, and to be able to input simple apps. First issue was backspace. It looks like the simple fake readline didn’t handle it well. Thus I hacked a patch to support it. It is not nice and it’s ugly, etc. But it does it’s job.

There was issues still with newline and tabs, and after fixing those (or there’s still issues, like deleting a tab causes problems) I could write programs in the MicroPython prompt.

micropython prompt working

Thus I would say it is getting there. There’s lot of things that are not working yet, like Ctrl or functions buttons. Or ESC (so no vi!). However somehow I’m proud that it does that much already…

As a reminder the sources are available in Github

For now I’ll share the binary I made the screenshot with: kernel-20210806-1144.bin. In order to run it with kvm just do (one can drop “serial” option):

kvm -serial mon:stdio -kernel kernel-20210806-1144.bin