]> git.dujemihanovic.xyz Git - nameless-os.git/commitdiff
Document the code
authorDuje Mihanović <duje.mihanovic@skole.hr>
Tue, 7 Sep 2021 18:33:39 +0000 (20:33 +0200)
committerDuje Mihanović <duje.mihanovic@skole.hr>
Tue, 7 Sep 2021 18:33:39 +0000 (20:33 +0200)
boot.s
how-to-compile [new file with mode: 0644]
kernel/entry.s
kernel/kernel.c

diff --git a/boot.s b/boot.s
index a40b7508781e9dbc59b82587e003f2ccca6a21f8..de5887dddc3101695a5ddcdc7e5deeca7f1d3796 100644 (file)
--- a/boot.s
+++ b/boot.s
@@ -1,32 +1,33 @@
-       bits 16
-       org 7C00h
+       bits 16 ; boot sectors run in real mode
+       org 7C00h ; BIOS loads us at 0x7c00
 
 
-KERNEL_OFFSET equ 1000h
+KERNEL_OFFSET equ 1000h ; where we will load our kernel
 
 
-       mov [BOOT_DRIVE], dl
+       mov [BOOT_DRIVE], dl ; BIOS puts the number of our boot drive in dl, so we will want to remember this
 
 
-       mov bp, 9000h
+       mov bp, 9000h ; initialize stack at a spot sufficiently distanced from this code
        mov sp, bp
 
        mov di, 0
 
        mov sp, bp
 
        mov di, 0
 
-       xor ax, ax
-       int 13h
-       jc reset_error
-
-       mov ah, 2
-       mov al, 10
-       xor ch, ch
-       mov cl, 2
-       xor dh, dh
-       xor bx, bx
-       mov es, bx
-       mov bx, KERNEL_OFFSET
-       int 13h
-       jc read_error
-       cmp al, 10
-       jl read_error
-       jmp switch_to_pm
+       xor ax, ax ; clear accumulator
+       int 13h ; BIOS disk services, with a cleared accumulator this will reset the disk controller
+       jc reset_error ; halt if controller reset fails
+
+       mov ah, 2 ; instruct BIOS's interrupt 13h to read sectors
+       mov al, 10 ; load 10 sectors, might be excessive (for now) but it's still good to do so
+       xor ch, ch ; read from 1st cylinder
+       mov cl, 2 ; start reading from 2nd sector, right after the boot sector
+       xor dh, dh ; read from 1st head
+       xor bx, bx ; clear B register
+       mov es, bx ; clear extended segment
+       mov bx, KERNEL_OFFSET ; put the sectors in our desired offset
+       ; dl holds the number of the drive to read from, but BIOS already filled this in
+       int 13h ; do the read
+       jc read_error ; halt if read fails
+       cmp al, 10 ; make sure we actually read 10 sectors
+       jl read_error ; halt if we didn't
+       jmp switch_to_pm ; if all is good, begin the switch to 32-bit protected mode
 
 reset_error
        mov bx, 0B800h
 
 reset_error
        mov bx, 0B800h
@@ -47,64 +48,77 @@ halt
 
 switch_to_pm
        mov bx, 0B800h
 
 switch_to_pm
        mov bx, 0B800h
-       mov es, bx
-       mov byte [es:0], 'L'
+       mov es, bx ; set extra segment to starting address of video RAM
+       mov byte [es:0], 'L' ; print an L to screen, to let us know that we actually got here
 
 
-       cli
-       xor ax, ax
-       mov ds, ax
-       lgdt [gdt_desc]
+       cli ; disable interrupts
+       xor ax, ax ; clear accumulator
+       mov ds, ax ; clear data segment, this makes sure the next instruction reads from the right place
+       lgdt [gdt_desc] ; load the Global Descriptor Table
        
        
-       mov eax, cr0
-       or eax, 1
-       mov cr0, eax
-       jmp CODE_SEG:protected
+       mov eax, cr0 ; move Control Register 0 to accumulator
+       or eax, 1 ; flip the bit which controls memory protection
+       mov cr0, eax ; move the accumulator back to CR0
+       jmp CODE_SEG:protected ; not quite there yet, need to do a far jump to clean the pipeline from any 16-bit instructions
+                                                  ; note that the jump does not have to be physically far away, it just needs to use a segmented address
 
 
-       bits 32
+       bits 32 ; we are finally in 32-bit mode
 protected
 protected
-       mov ax, DATA_SEG
+       mov ax, DATA_SEG ; put the selector for the data segment in the accumulator
+       ; in real mode, segmentation works by taking one of these segment registers, shifting it right by 4 bits or 1 hex digit
+       ; and then adding a 16-bit offset to form a 20-bit address
+       ; example: 1234h:5678h
+       ; 1234h is shifted to 12340h, 12340h + 5678h is 179B8h
+       ; in 32-bit protected mode, these segment registers do not hold the segment address itself, but a selector in the GDT
+       ; so we have to update the segment registers accordingly
        mov ds, ax
        mov ss, ax
        mov es, ax
        mov fs, ax
        mov gs, ax
        
        mov ds, ax
        mov ss, ax
        mov es, ax
        mov fs, ax
        mov gs, ax
        
+       ; reinitialize the stack at a safe location
        mov ebp, 090000h
        mov esp, ebp
 
        mov ebp, 090000h
        mov esp, ebp
 
+       ; print a string to let us know that we survived the switch
        mov ebx, pm_success
        call pmprint
 
        mov ebx, pm_success
        call pmprint
 
+       ; transfer control to the kernel
        call KERNEL_OFFSET
 
        call KERNEL_OFFSET
 
+       ; above call should not return in normal circumstances, but if it does hang forever
        jmp $
 
 pmprint
        jmp $
 
 pmprint
-       pusha
-       mov edx, video_memory
+       pusha ; save registers to stack
+       mov edx, video_memory ; initialize dx with location of VRAM
 
 .loop
 
 .loop
-       mov al, [ebx]
-       mov ah, 0Fh
+       mov al, [ebx] ; read next char and put it in al
+       mov ah, 0Fh ; puts the VGA text mode color white on black into ah
 
 
-       cmp al, 0
-       je .done
+       cmp al, 0 ; if the next character is null, we reached end of string
+       je .done ; so return the instruction
 
 
-       mov [edx], al
-       mov [edx+1], ah
+       mov [edx], al ; otherwise put the next character in the video memory
+       mov [edx+1], ah ; do the same for its color
 
 
-       inc ebx
-       add edx, 2
+       inc ebx ; point to next character in string
+       add edx, 2 ; point to next character in VRAM
 
 
-       jmp .loop
+       jmp .loop ; go back to the loop
 
 .done
 
 .done
-       popa
-       ret
+       popa ; restore registers from stack
+       ret ; return
 
 pm_success db "Now in protected mode", 0
 video_memory equ 0B8000h
 
 
 pm_success db "Now in protected mode", 0
 video_memory equ 0B8000h
 
+; the actual Global Descriptor Table
+; refer to Volume 3, Chapter 2, 2.1.1 of Intel's 64 and IA-32 Software Developer's Manual for more info
 gdt_start
 null_seg
        dq 0
 gdt_start
 null_seg
        dq 0
@@ -129,7 +143,7 @@ gdt_desc
 CODE_SEG equ code_seg - gdt_start
 DATA_SEG equ data_seg - gdt_start
 
 CODE_SEG equ code_seg - gdt_start
 DATA_SEG equ data_seg - gdt_start
 
-BOOT_DRIVE db 0
+BOOT_DRIVE db 0 ; reserve a spot in RAM for our boot drive variable
 
        times 510-($-$$) db 0
 
        times 510-($-$$) db 0
-       dw 0AA55h
+       dw 0AA55h ; MBR signature
diff --git a/how-to-compile b/how-to-compile
new file mode 100644 (file)
index 0000000..bcd0b8b
--- /dev/null
@@ -0,0 +1,10 @@
+1. Compile an i686-elf GCC and binutils following the instructions on this page:
+   https://wiki.osdev.org/GCC_Cross-Compiler
+
+2. Install yasm, or modify the makefile to use nasm if you prefer it
+
+3. Run make in the source root
+
+4. Run boot.img with some emulator, I use qemu:
+   qemu-system-i386 boot.img
+   You can use the x86_64 qemu, Bochs, VirtualBox or something else if you prefer
index 236b81e967e459e4c6f684d137726dbf42743677..a02df92d4703df4ada20654b433158c80ee1373f 100644 (file)
@@ -1,5 +1,12 @@
-       bits 32
-       extern _start
+; when our kernel source has functions before _start and we blindly transfer control to the beginning
+; of the kernel binary, it will wrongly execute the other functions
+; the solution is to link a small assembly routine (this) which executes a known label within the kernel
+; so that this routine comes before the kernel in the resulting binary
+; we cannot link the boot sector code and the kernel because the former needs to be a raw binary, while the
+; kernel is compiled into an ELF object file which contains some metadata on the kernel code
 
 
-       call _start
-       jmp $
+       bits 32 ; this is started in protected mode
+       extern _start ; the known label in the kernel source we will execute is, well, not in this assembly routine
+
+       call _start ; call the known label
+       jmp $ ; it should never return, but hang forever if it does
index 87fbef954a0c4703db02024c1fcf2e1383b34659..e04b875e2959b5963e7c6f4ba394abba1f5f0be5 100644 (file)
@@ -1,5 +1,5 @@
 void _start(void)
 {
 void _start(void)
 {
-       char *video_memory = (char *) 0xB8000;
-       *video_memory = 'A';
+       char *video_memory = (char *) 0xB8000; /* VGA VRAM starts at 0xB8000 */
+       *video_memory = 'A'; /* put an A at the beginning of the VRAM */
 }
 }