Implementing a Simple Operating System — part 02
Hello everyone! Welcome to the second article of the DHP OS development article series. Last week, in the first article of this series, I demonstrated how to set up the development environment and boot our simple operating system. If you still haven’t gone through the previous article, please refer to the below mentioned link.
In this article, I’ll show you how to utilize C as the operating system’s programming language instead of assembly code.
Assembly is excellent for dealing with the CPU and provides complete control over all aspects of the code. However, C is a far more convenient language to use, at least for the authors. As a result, I’d prefer to use C as much as possible and assembly code only when necessary.
So, let’s get on to the steps.
Step 01: Setting Up a Stack
As all non-trivial C programs use a stack, one prerequisite for utilizing C is a stack. Setting up a stack is no more difficult than correctly aligning the esp
register to the end of a free memory area.
The only things in memory so far are GRUB, BIOS, the OS kernel, and some memory mapped I/O. So, we can’t point the esp
to a random section of memory since we don’t know if that memory space is available. Therefore, it is preferable to leave some uninitialized memory in the kernel’s ELF file’s bss
section. We use bss
section rather than the data section, to reduce the size of the OS executable. Since GRUB recognizes ELF, it will allocate any memory reserved in the bss
section when the OS is loaded.
To declare uninitialized data, the NASM pseudo-instruction resb
can be used.
Copy the below code into the file loader.s
, right after the last definition (CHECKSUM).
Then add the following code to set up the stack pointer: This is done by pointing esp
to the end of the kernel_stack memory.
Step 02: Calling C Code from Assembly
In this step, we need to call a C function using assembly code.
There are numerous conventions for calling C code from assembly code. Here, I’m using the cdecl
calling convention.
In this cdecl
convention, the function’s arguments should be pushed onto the stack in a right-to-left order, with the rightmost argument being pushed first. And the return value is stored in the eax
register.
Copy the code below into a new file called kmain.c
. This will be the C code’s source file.
To achieve the task, put the following code after the esp
instruction in the loader.s
file.
Step 03: Compiling C code
Many flags for GCC must be used while compiling the C code for the OS. Because there is no standard library available for our operating system, the C code should not assume the availability of one. Moreover, we should turn on all the warnings and treat them as errors.
-m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -Wall -Wextra -Werror
Then we can call kmain
function from loader.s
by putting it in the file named kmain.c
"at this stage, it will not take any arguments."
Step 04: Build Tools
Now we’ll set up some build tools to make compiling and testing the operating system easier. Although we used onemake
, there are other different building systems available.
Create a file called Makefile
and put the following text into it.
Now, Our working directory should now look like the below figure.
|-- bochsrc.txt
|-- iso
| |-- boot
| |-- grub
| |-- menu.lst
| |-- stage2_eltorito
|-- kmain.c
|-- loader.s
|-- Makefile
After all these modifications, our loader.s
file should contain the following:
Now we should be able to start the operating system using the simple command make run
and it will compile the kernel and boot it in Bochs.
Then you will get the following output.
Finally, quit the Bochs and use the command cat bochslog.txt
to display the log it generated. If you can find the summation EAX = 000000006 (the sum of the arguments we provided), you are successful!
That’s all for this week. We were able to use the C language to further develop our operating system.
Let’s meet again next week with another exciting part of this article series. Until then, have a great week!