I guess that is a fun thing to do, to get a better understanding of your own device, that you use everyday ! First, let me introduce the x86_64. x86_64 is Assembly language, which basically means it's nearly what the computers reads and understand. Every single file/software executed on you computer has to be compiled to assembly code to be executed by your processor. In this tutorial, we will be making a bootloader, which is basically the first piece of code the computer reads on startup. Without the bootloader, you wouldn't be able to start your kernel, file system, or anything. So let's have some fun and write one, wouldn't we ?
First starting with the base:
`MOV AX, 0x07C0`
Lets analyse our first line of code : The mov instruction is pretty much in every assembly language (arm, z80, ...). As its name suggest, its used to move a value from one location to another. Here we move 0x07c0 in the AX register. Why do we do that ? Because the 0x07C0 is the memory adress that contains the start of the bootloader program. And the AX register is used to store values that are used to control the flow of the program ! Got it ? Easy peasy as we say in english !
`MOV DS, AX:`
So now we need to move the value in AX into DS. The DS register is the Data segment register. I see you coming : Why don't we just do `MOV DS, 0x07C0`? Think of it like a robot is trying to put this number directly into the a compartment without putting it in the first compartment first. It won't work, because it needs to go in the first compartment first in order to be moved to the second compartment. It's like a chain, where the number can go up and down, without skipping any compartement :
DS |
AX |
0x07C0 goes up
See, the number goes up into the Data segment register, so the computer understands where the bootloader code is located (in our program). Great, so now that we have the start address of the bootloader program stored in the DS register, we can move on to the next instruction.
`MOV [0x0000], AX`
So now, we are basically putting our AX register at the 0x0000 adress memory, the first bloc of your memory.We use the brackets to indicate that we're using memory, not a register. This is used to set up the stack pointer, which is used to store data and instructions while the program is running.
This memory location is the start address of the bootloader program. In this instruction, our robot is taking the value that is currently stored in the AX register and placing it into a specific memory location at address 0x0000, so the computer knows where to start.This is done so that the computer can easily locate and execute the bootloader code when the computer starts up.
`MOV [0x0010], 0xFFFF `
The instruction here is telling the robot to take the value "0xFFFF" and store it in a specific location in memory. The memory location is specified by the address "0x0010". 0x0000 is the start, and then, 0x0010, which is the next adress, gets the 0xFFFF, which is a hexadecimal value which is equal to 65535 in decimal. This is used to define the end of the bootloader code, so the computer can know when to stop executing the bootloader code
`JMP 0x07C0`
The JMP instruction tells the computer to change the flow of execution by jumping to a different location in the program. Here in our case, we are jumping to the 0x07C0 memory adress, where there is the start of the bootloader code. This instruction here is used at the end of the bootloader code to tell the computer to start executing the bootloader program.
We did it ! Lets take look at the full code
`MOV AX, 0x07C0
MOV DS, AX
MOV [0x0000], AX
MOV [0x0010], 0xFFFF
JMP 0x07C0`
Great job, we have just written a simple x86_64 bootloader! By following this tutorial, you should now have a better understanding of how a bootloader works and how assembly code is used to control the flow of a program.
Now to test it, we will need an assembler, we will use nasm here (You can install it on your linux distro by using your package manager !). So use nasm like :
`nasm -f bin -o bootloader.bin code.asm`
This command will use nasm to assemble the file "code.asm"(to write the code.asm, just write the instructions we wrote in it with any text editor) into a binary file called "bootloader.bin". The "-f bin" option tells NASM to output a binary file, and the "-o bootloader.bin" option specifies the output file name.
Once you have your binary file, we can use QEMU to test it.
`qemu-system-x86_64 -drive format=raw,file=bootloader.bin`