CHAPTER 5. WRITING, BUILDING, AND LOADING YOUR
KERNEL 59
# Run bochs to simulate booting of our code.
run: all
bochs
# This is the actual disk image that the computer loads
# which is the combination of our compiled bootsector and kernel
os-image: boot/boot_sect.bin kernel.bin
cat $^ > os -image
# This builds the binary of our kernel from two object files:
# - the kernel_entry , which jumps to main() in our kernel
# - the compiled C kernel
kernel.bin: kernel/kernel_entry.o ${OBJ}
ld -o $@ -Ttext 0x1000 $^ --oformat binary
# Generic rule for compiling C code to an object file
# For simplicity , we C files depend on all header files.
%.o : %.c ${HEADERS}
gcc -ffreestanding -c $< -o $@
# Assemble the kernel_entry.
%.o : %.asm
nasm $< -f elf -o $@
%.bin : %.asm
nasm $< -f bin -I ’../../16 bit/’ -o $@
clean:
rm -fr *.bin *.dis *.o os-image
rm -fr kernel /*.o boot /*.bin drivers /*.o
5.4 C Primer
C has a few quirks that can unsettle a new programmer of the language.
5.4.1 The Pre-processor and Directives
Before a C file is compiled into an object file, a pre-processor scans it for pre-processor
directives and variables, and then usually substitutes them with code, such as macros
and values of constants, or with nothing at all. The pre-processor is not essential for
compiling C code, but serves rather to offer some convenience that makes the code more
managable.
#define PI 3.141592
...
float radius = 3.0;
float circumference = 2 * radius * PI;
...
The pre-processor would output the following code, ready for compilation: