KnightOS is a very special sort of operating system. It is radically different from TIOS. It is divided into two parts: the kernel, and userspace. The kernel is the basis of the system. It exists on pages 0-3 (inclusive). The first page is always mapped to bank 0, and is therefore accessible at 0x0000-0x3FFF (inclusive) in memory. This part of the system is responsible for all the core functionality - booting, memory management, hardware drivers, filesystem access, and so on. The kernel very rarely interacts with the user.
The stuff you actually interact with when using KnightOS is the userspace. The castle is in userspace, as well as the application switcher, the math app, the settings app, and so on. These are implemented as userspace programs, the same ones that this document describes. If you are curious about how any of these programs work, or if you want to modify or improve them yourself, you can browse their source code online.
Some of the things commonly handled by each are covered here:
Your programs must use a certain format for the kernel to agree to load them. A simple template is provided here, and you may read additional docs later to learn more about it.
Here is a version of this template with comments explaining each choice:
You may assemble this with any assembler you please. In addition to using KEXC (the format described here, for native code), you may use shebangs for interpreted programs. For example:
#!/bin/bfi indicates that the path to the file being run should be set into
HL and the program
/bin/bfi should be launched to handle it. Of course, you need
to have bfi installed on your system for this to work.
The first and most important thing to understand about KnightOS programs is that
they do not run from a consistent location. On TIOS, you can always be confident
that your code will run at 0x9D95 (and you often start your programs with
.org 0x9D95 as a result). This is not the case on KnightOS. Instead, your
program, as well as many other programs, are given space all over memory. The
exact location is determined at runtime and changes frequently.
To deal with this, KnightOS programs must be relocatable. Luckily, this is not very difficult. The kernel provides some mechanisms with which you are able to run your programs from arbituary addresses.
There are a number of macros included in kernel.inc to assist with relocation. They are:
These function exactly like the z80 instructions
respectively. You must use these when referring to addresses within your own
executable. For example:
At runtime, the actual address will be resolved by the kernel. The first time you run this, it will take longer to run as it calculates the address. However, on subsequent attempts, it will take the original duration of the instruction, plus one NOP. This is a trivial slowdown for nearly all applications.
The benefit gained by going through all this extra effort is enormous: the ability to run several programs at once. In addition to that, your own programs can run several of their own threads, which each run simultaneously. There are a number of advanced topics surrounding this that will be discussed later.
The kernel provides a large number of useful functions that you will likely want
to take advantage of. You may do so by using
pcall. This is similar to TIOS’s
bcalls. You can use them like so:
For example, to use drawStr:
All kernel functions are documented online.
If you wish to interact with the screen, keyboard, or other devices, you need to play nice with other programs. Only one program may use these devices at a time. You must use the relevant kernel functions to claim these devices.
There is no “safe RAM” on KnightOS. Instead, you allocate memory as you need it. There are two acceptable means of setting aside memory:
You should free memory with free when you are done with it. The kernel automatically frees all assigned memory when your program exits, but you should free things that you aren’t using to keep more memory available for other programs.
Here is an example userspace program that says “hello world”, waits for the user to press a key, and exits.
You may find it useful to read the documentation for each of the kernel functions in use here:
KnightOS provides a number of libraries that you are able to use to interact with
the system in a more natural way. Users can also build their own libraries that
you can take advantage of. To use a library, you should
#include that library’s
include file, and then use the appropriate macros to interact with it. You must
also inform the kernel that you wish to use a particular library, via
loadLibrary. Here is an
example of how you might use corelib:
Libraries are generally installed into
/lib. Consult each library’s
documentation for more information on how to use that particular library.