Day 01 - Hello World Assembly¶
Today, we will write a tiny program (to far away to be called an OS) that will print “Hello World” on screen. In the meanwhile, we will talk a little bit about Makefile.
Here are the download links to files that is mentioned in today’s work.
Baisc Knowledge¶
When the computer powered up, the hardware system follows a pre-defined process to initialize everything. The BIOS (Basic Input Output System) chip automatically loads the first sector (512 Bytes) into the memory starting at address 0x7C00 (and ends at 0x7E00). When the CPU verifies that the first sector is a startup sector, the CPU will execute the inside code. Note that, the startup sector must end with “AA55”.
You also need to know a little bit about Assembly Language. Don’t be afraid, since we are not going to go to far away. I wrote detailed comments to the source code and it should be friendly to beginners.
Source Code¶
This is the first assembly language, we will spend some time on explaining the code.
bits 16 ; tell NASM this is 16 bit code
org 0x7c00 ; tell NASM to start outputting stuff at offset 0x7c00,
; Or to say, load the following code to memory address 0x7c00
boot:
mov si, hello ; point si register to hello label memory location
mov ah, 0x0e ; 0x0e means 'Write Character in TTY mode'
.loop:
lodsb ; load string [DS:SI]into AL
; check whether it's the end of the string (seeing an zero)
or al,al ; is al == 0 ? Note that AL OR AL = AL. Simply to set the flags
jz halt ; if (al == 0) jump to halt if Zero Flag (ZF) is set
; another way to write is
; CMP AL, 0 ; compare AL
; JE HALT ; Jump if Equal
int 0x10 ; runs BIOS interrupt 0x10 - Video Services for printing the character in AL
jmp .loop
halt:
cli ; clear interrupt flag
hlt ; halt execution until wake up
hello: db "Hello world!",0
times 510 - ($-$$) db 0 ; pad remaining 510 bytes with zeroes
; the above line is equivalent to RESB 510 - ($-$$)
dw 0xaa55 ; magic bootloader magic - marks this 512 byte sector bootable for no reason! Checked by system.
Execute¶
In terminal, we first generate the startup file loader.bin using nasm
nasm -f bin -o loader.bin loader.asm
If you simply want to see the result, you can directly use qemu to run your newly made by
qemu-system-x86_64 -fda loader.bin
If you want to make a bootable image, you can
# generate a 1.44MB floppy disk img
dd if=/dev/zero of=myos.img bs=512 count=2880
# write the .bin into the img
dd if=loader.bin of=myos.img seek=0 count=1 conv=notrunc
# execute
qemu-system-x86_64 -fda myos.img
You will see
Write a Makefile¶
In the development process, we would like to simply the process of typing commands. Write all commands in to a Bash scipt is an option, but what if we want more? For instance, we may want to generate the bin file only, may want to clean all unrelated file, may want to conduct the dry run. Using Makefile is a good idea.
The basic syntax for makefile rules is
target ... : prerequisites ...
[tab] one command to generate target
For our case, in order to generate the .bin file, we need the assembly file. We can have
loader.bin : loader.asm
nasm -f bin -o loader.bin loader.asm
The provided makefile reads:
myos.img : loader.bin
dd if=/dev/zero of=myos.img bs=512 count=2880 &&\
dd if=loader.bin of=myos.img seek=0 count=1 conv=notrunc
loader.bin : loader.asm
nasm -f bin -o loader.bin loader.asm
run : myos.img
qemu-system-x86_64 -fda myos.img
.PHONY : clean # .PHONY means clean is not a file or an object
clean:
rm *.bin *.img
The possible commands you can try are
make # equivalent to "make myos.img"
make myos.img
make loader.bin
make run
make clean
The make command automatically figures out which files are required to generate the target file. For example, when executing “make run”, make command knows that it needs myos.img, and loader.bin must be generated before myos.img.
Makefile is a efficient tool to manage big project with complex file dependency.