About HolyC

I was always fascinated by the idea that just one man could have created TempleOS. Building an OS alone in 10 years is like building a one-man-built skyscraper. I’ve decided to try to understand HolyC, the C “dialect” the OS is written, even for fun, because I am sure I will never be able to create what Terry A. Davis has done.

This is a little experiment when I document the steps I’ve followed in this first approach to the TempleOS source code. I know it has already been studied, and I can find tons of documentation online. But this isn’t funny.

Where to start

I am an experienced C/C++ programmer, so I’m confident it will be very funny. The TempleOS source code is in the Public Domain, and you can find it at https://github.com/cia-foundation/TempleOS.

Let’s start with something simple. I’ve found a file called Kernel/QSort.HC that, on a rapid look, seems to be an implementation of Quicksort. The Quicksort algorithm is one of the very first you learn when you start talking about sorting stuff; that’s why it’s a good start.

From the file I can extrapolate four functions:

U0 QSortI64(I64 *base,I64 num, I64 (*fp_compare)(I64 e1,I64 e2));
U0 QSort2a(U8 **base,I64 num,I64 (*fp_compare)(U8 **_e1,U8 **_e2));
U0 QSort2b(U8 *base,I64 num, I64 width, I64 (*fp_compare)(U8 *e1,U8 *e2),U8 *tmp);
U0 QSort(U8 *base,I64 num, I64 width, I64 (*fp_compare)(U8 *e1,U8 *e2));

Quick considerations

  1. U0 seems to be a NULL (unsigned zero?).
  2. U8 is a char of 8 characters.
  3. I64 is a 64-bit integer (and so on).
  4. Pointers appear the same as C.

TempleOS is a 64-bit-only OS, so it makes sense to name the variable with the dimensions in bit directly. Other considerations

  1. pivot=base[num/2]; Integer division is truncated toward zero, like in C.
  2. MAlloc(width*2) and Free are the names for the corresponding malloc and free stdlib functions.

The code now reports about a: Maybe, look at $LK,"::/Demo/MultiCore/MPRadix.HC"$. The $LK ... $ is probably some smart link you can select to jump to the other file. It can stand for LinK. The fact that it’s a string surrounded by two characters you don’t usually find in a C file can help select it. But let’s move on to Demo/MultiCore/MPRadix.HC.

On to the first demo

Something new in this file immediately caught my eye.

  1. HolyC supports C++ one-line comments (C does it, too, but only since from C99).
  2. There is a no_warn dummy; it probably mutes the compiler. The dummy variable is not used inside the function.
  3. "$$GREEN$$QSort$$FG$$\n"; probably write something in the console.
  4. "Time:%9.6f\n",tS-t0; it’s a printf without the declaration of the function? Wow!
  5. D(arg2+NUM/4); A debug output?

I had already lost three functionalities on my way, so I decided to clone the repository and search the code broadly. I found the D function very quickly. As the comment says, it’s a debug function.

Now, up to point 3, the $$ syntax. As I imagined, it’s used all over the place. But I founded:

  1. A DocPrint(doc,"$$LTBLUE$$");
  2. And a DocPrint(doc,"$$MU,\"%d\",LE=%d$$\n",i,i);

So, it is a printf, and the format is similar. But I can see a simple dangling string in the code, which is interesting. During my searches, I also found that DocBottom is the same as calling DocBottom(). If you have no parameters to pass to a function, you can save two parentheses characters. Cool!

After searching and digging, I arrived at the lexer. You don’t need to write printf to output or use strange, non-mnemonic ANSI colors. I find this fascinating and wonder how complex the C LARL will become.

Searching for a complete program

I decided to search for a complete program, and given my accumulated knowledge, I searched for “I32 main(“. Nothing. “I64 main(“, nothing again. I don’t want to search for the result online; in this little experiment, I want to understand the code from the OS code, not from any external document.

U0 main(. I found it; the main() function returns NULL and does not accept arguments. In the meantime, I saw a file called HolyC.DD, the documentation of HolyC? Yes, I could have searched for documentation as the first step, but like I already said, it’s not funny. Digging through the kernel files is interesting!

The HolyC documentation file

The file starts with a:

$WW,1$$FG,5$$TX+CX,"HolyC"$$FG$

* See $LK,"::/Doc/CompilerOverview.DD"$.

* See $LK,"Scoping and Linkage",A="FI:::/Doc/ScopingLinkage.DD"$ for details on $FG,2$extern$FG$, $FG,2$import$FG$, $FG,2$_extern$FG$, $FG,2$_import$FG$, etc.

Like I thought, the $$LK,"path"$$ is a link to a filesystem. This is

Built-in types include $FG,2$I0,I8,I16,I32,I64$FG$ for signed 0-8 byte

Like I thought as well. I32 is like writing int32_t. Having HolyC built-in types in C will be easy work. Just a tiny list of type definitions:

#include <stdint.h>

typedef int16_t I16;
typedef int32_t I32;
// and so on.

Now the good part:

Function with no args, or just default args can be called without parentheses.

Default args? Like in PHP? I didn’t notice it! And better:

Default args don't have to be on the end.

It’s not like PHP. So this is working code:

// The function: I64 Dir(U8 *files_find_mask,Bool full)
Dir("*")
Dir();
Dir;

// Default args not at the end
U0 Test(I64 i=4,I64 j,I64 k=5) {
    // ...
}

U0 DemoHolyC(U8 drv,U8 *fmt,U8 *name,I64 age) {
    // This is sent to PutChars()
    "Hello World!\n";

    // To Print()
    "%s age %d\n",name,age;

    // An empty string followed by a string (fmt) is a string
    "" fmt,name,age;

    // To PutChars(). Same behavior as before
    '' drv;

    // To PutChars()
    '*';
}

The main() function

I searched for the main() function, and I didn’t need to because I just found from this file that:

Any code outside of functions gets executed upon start-up in order.

Like in a lot of scripting languages. And this is nothing compared to this:

I64 AddNums(...)
{
  I64 i,res=0;
  for (i=0;i<argc;i++)
    res+=argv[i];
  return res;
}

Every function and any code outside a function can access parameters and the count from the global argc and argv! And also:

  1. The if short-syntax 13<=age<20.
  2. The switch cases automatically start from zero and increment by one, no need to specify the number.
  3. The switch cases can be nested.
  4. All values are extended to 64-bit when accessed. Intermediate calculations are done with 64-bit values.
  5. And so on.

Farewell Terry

I then decided to stop. Such a naive analysis is not the way to study the TempleOS or pay respect to him. Having mental problems is one of the worst things that can happen to you.

I have drug-resistant epilepsy, and programming is one of the few things that keep a smile on my face. Today, I’ve smiled a lot every time I saw something HolyC has, and C does not.

Until next time.

@online{zaerl2024-about-holyc,
  author = {Francesco Bigiarini},
  title = {About HolyC},
  date = {2024-05-20},
  url = {https://zaerl.com/2024/05/20/about-holyc/},
  urldate = {2024-05-20}
}