Adding characters and fitting your keyboard in inside TempleOS

[linkstandalone]

Introduction

This blog post is written with purpose of memorizing the process of making my university assignment for bachelor thesis, and I'm making it public in case someone makes use of it for their purposes too.

My assignment was creating keyboard layout and custom characters for TempleOS operating system, and adding a feature for dynamic switching between different keyboards, but before describing the process, some background would be nice.

If you're reading this blog post, you most certainly know about the operating system I work in and its creator, Terry A. Davis, but for those who don't know Wikipedia has most of the stuff not relevant to this blog post covered.

What is important pointing out though is the fact that all the source code and documentation is located in the operating system itself. But to spare you the tedious process of creating virtual machine and installing OS itself, most of the content is located on website.

So, let's get to the process of creating this thing, shall we?

Making external characters

Browsing through the OS demos, which can be accessed through Cd("/Demo");, with a little assistance of more experienced TempleOS developers (big shout out to TempleOS Riot community), I discovered a file called ExtChars.HC.Z, which documented the process of creating external characters.

In the example provided by author himself, mr. Davis, he specifically made a smiley character. Upon closer inspection, one can conclude that TempleOS uses bit-per-pixel system, meaning that one 1 represents pixel, and 0 represents nothing. He uses 8x8 bits for drawing characters.

His character looked like

U8 face[8]={

0b00111100,
0b01000010,
0b10100101,
0b10000001,
0b11000011,
0b10111101,
0b01000010,
0b00111100,

};
text.font[255]=face[0](U64);
extChars

So, we can see that 1s make the shape of smiley face, however upon creating custom character, I noticed that it was inverted on horizontal axis, so I basically just mirrored the rows horizontally, and now I got my external character.

Also, below this array of 8-bit values there is also a part you can notice starting with text.font, so there is some overwriting going on too. And indeed, after opening list of all characters, which can be opened by pressing Ctrl+Alt+A, I discovered the last character, which was just a block (every bit was 1), got overwritten with a smiley face

So, now that we figured out how to make custom characters, I decided to look into how to make custom key combos which would allow us to dynamically switch the keyboards.

Creating custom key combos

This process was fairly easy, all it took for me to find this is some digging through /Doc folder, and finding CtrlAltCBSet function, which is located inside /Kernel/KeyDev.HC.Z file. The process of creating custom function is very intuitive and I have no need to describe it, as it is really self-evident, but yeah, I decided to create a function which would print "Hello World" on a Ctrl+Alt+p keypress.

After modifying that file, as with any other file in kernel, recompilation is needed. We do that with BootHDIns;, selecting the drive (C in my case), selecting default value for partition num, pressing p to probe, selecting the number left of your disk (I overlooked this one, so I kept entering Base0 and Base1 values located below the disk), pressing enter til it starts compiling, and that's it. Simple and straightforward process

And that's it, after pressing Ctrl+Alt+p, I got TempleOS to print "Hello World", although in debugger mode, or as the author calls it, raw mode.

Creating the font file - part 1

This one took me WAY too long to figure out how it worked, way more than it should've, especially since I figured out how to make external characters.

File we're modifying is located at /Kernel/FontStd.HC.Z. Due to the way characters are written here, it reminded me of actual addresses, which I thought were hardcoded in the operating system itself somewhere. But my hacker spirit was stronger, and I decided to experiment with those "addresses", and I slapped myself in the face when I discovered these were just characters represented in hex value rather than the binary array as described in the ExtChars.HC.Z file mentioned above. So I got myself to manually draw a character on a piece of paper, write it down in binary, convert it to hexadecimal value, and then write it down. Also it's worth noting that I followed "horizontal axis reversed" logic as was described in ExtChars. But this time I found myself surprised, this one was vertically flipped as well, so I mirrored XX pairs of bytes, meaning 0xAABBCC would become 0xCCBBAA.

Upon the recompilation, my character was replaced with the one I created, meaning there was a success.

Creating the font file - part 2

I thought to myself, just modifying this file would overwrite actual english keyboard which is default, so making a copy would be wise, so I created FontHr.HC.Z inside /Kernel directory

I renamed sys_font_std array to sys_font_hr, and I modified keys I needed in the last few elements of the array. Now the actual problem was how to include that array into the system, how to make use of it.

This is where the most helpful function in TempleOS helped, and that is Find function, whose syntax goes like F("my search query");.

find

Pictured above is the example of find. So I went and found where else was sys_font_std located, and I stumbled upon /Kernel/KMain.HC.Z file, but I also realized that alongside earlier seen text.font value, there was also text.aux_font, which was font supporting Cyrillic characters. So I added the text.hr_font=sys_font_hr; line too, but obvious next move is to find text pointer and to create hr_font value.

Again, thanks to great find function built in the OS, I discovered that it was located in KernelA.HH.Z file, so I modified that file too adding the pointer I needed.

All I needed to do next is to create a function similar to already existing function of swapping standard font and Cyrillic font. And so I did, but the operating system didn't compile at that point

The easy solution was to #include FontHr in the file located at Kernel.PRJ.Z, because kernel was being compiled ahead of time, so we had to inform it that the file is there to compile.

After doing all of this, seemingly all is done... Unless...

Font/Layout problem

I thought all work was done conceptually, but then the problem arose. Just switching fonts wasn't good enough, since I effectively just replaced values of character Z to character Y (English keyboards start with "qwerty", Croatian keyboards startwith "qwertz"), so when I actually switched fonts, words displayed lost their meaning (e.g. "TempleOS rulz;" would be printed as "TempleOS rulyč") due to fundamentally different layout of keyboards, so I had to switch layouts alongside fonts too.

Layout file was located in /Kernel/SerialDev/Keyboard.HC.Z, and I saw some part included Assembly language, and the other part defined behavior you can expect from keyboards, like how it handles single keypress, reading scan codes, etc.

After literally hours of thinking and experimenting about possible solutions, which included MANY good but sadly failed attempts, some of which wouldn't compile at all due to AOT compilation, I FINALLY discovered the real solution.

I got an idea to split keyboard files into the two, one would contain assembler layout, and the other would contain rest of the functions which defined keyboard behavior.

After doing so, I copied the Keyboard1.HC.Z file, which was assembler part, to another file called KeyboardHr.HC.Z. After doing so, I had to modify MakeSerialDev.HC.Z file located inside /Kernel/SerialDev directory to exclude assembler part. Not splitting the Keyboard.HC.Z file as described above or leaving the assembler part inside makefile would cause conflicts with /Kernel/KeyDev.HC.Z file, which must be #include-d inside Kernel.PRJ.Z file because of inherent contradiction (A must be #include-d before B, and B must be #include-d before A, both at the same time).

After doing that, since I wanted dynamic switching on the keypress, I had to copy one keyboard layout to another's location. I accomplished that by making a copy of main english file, and repurposing main english file to be "main" instead. Then I copied memory blocks from Croatian keyboard layout to the main one using MemCpy function (used as MemCpy(NORMAL_KEY_SCAN_DECODE_TABLE, HR_NORMAL_KEY_SCAN_DECODE_TABLE, 64);. I repeated the process for shift/ctrl memory blocks too. Then I implemented mechanism for MemCpy-ing on a keypress, I named one simple variable which would be binary in nature, and I checked it upon calling the keypress function, then it would go to switch statement and reset its value inside that, alongside MemCpy that happens.

function

Pictured above is the example of function I described above.

After doing so, I only basically had to #include SerialDev/KeyboardHr and SerialDev/Keyboard before #include-ing KeyDev

And after recompiling and rebooting, I finally had working Croatian keyboard layout and characters, switched dynamically on demand.

Conclusion

As you can see, the whole process didn't involve much modifying besides the font file, but it was incredibly tough to accomplish these concepts, and my main goal was to go the least against the philosophy of the author (and against God), which in my view I accomplished very well.

In the near future, I plan to release a setup system which would modify and install these files onto default TempleOS installation automatically, but before doing so I have to actually finish the Croatian keyboard til end, since 0.001% features are still missing, and I want it to be 100% perfect.

Thank you for reading this very long post, I sincerely hope we all learned something today and that this post was informative. Any suggestions or feedback, regarding both programming or writing style would be much appreciated, and see you until next reading :)