]> git.dujemihanovic.xyz Git - nameless-os.git/blobdiff - boot.s
Add linker script and partial VGA driver
[nameless-os.git] / boot.s
diff --git a/boot.s b/boot.s
index 50730d0c39dd9566f3eef2e0b87582758af13444..6181a73528610dcaf7c18ea6beb51dbf03865ffe 100644 (file)
--- a/boot.s
+++ b/boot.s
        bits 16 ; boot sectors run in real mode
-       org 7c00h ; boot sector is always loaded at 0x7c00
-
-       mov al, 1 ; update cursor after write
-       mov bh, 0 ; write to page 0
-       mov bl, 00000111b ; attribute: high nibble is foreground (light gray),
-                                         ; low nibble is background (black)
-                                         ; NOTE: in the binary, these will be inverted, so low nibble here will become high nibble in binary and vice versa!
-       mov cx, msg1end - msg1 ; size of message
-       mov dl, 0 ; column to write to
-       mov dh, 0 ; row to write to
-       push cs ; push code segment to stack
-       pop es ; pop pushed code segment to extra segment, this serves as the base to the string
-       mov bp, msg1 ; offset where the string is
-       mov ah, 13h ; write string subservice 
-       int 10h ; call BIOS interrupt 0x10
-       jmp msg1end ; jmp to endless loop
-       msg1 db "Gotta start somewhere"
-       msg1end: jmp msg1end
-
-       times 510-($-$$) db 0 ; fill with 0 until 0x1fc is reached
-       dw 0xaa55 ; BIOS MBR signature
+       org 7C00h ; BIOS loads us at 0x7c00
+
+KERNEL_OFFSET equ 1000h ; where we will load our kernel
+
+       mov [BOOT_DRIVE], dl ; BIOS puts the number of our boot drive in dl, so we will want to remember this
+
+       mov bp, 9000h ; initialize stack at a spot sufficiently distanced from this code
+       mov sp, bp
+
+       mov di, 0
+
+       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
+       mov es, bx
+       mov byte [es:di], '1'
+       jmp halt
+
+read_error
+       mov bx, 0B800h
+       mov es, bx
+       mov byte [es:di], '2'
+       jmp halt
+
+       call switch_to_pm
+
+halt
+       jmp $
+
+switch_to_pm
+       mov bx, 0B800h
+       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 ; 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 ; 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 ; we are finally in 32-bit mode
+protected
+       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
+       
+       ; reinitialize the stack at a safe location
+       mov ebp, 090000h
+       mov esp, ebp
+
+       ; print a string to let us know that we survived the switch
+       mov ebx, pm_success
+       call pmprint
+
+       ; transfer control to the kernel
+       call KERNEL_OFFSET
+
+       ; above call should not return in normal circumstances, but if it does hang forever
+       jmp $
+
+pmprint
+       pusha ; save registers to stack
+       mov edx, video_memory ; initialize dx with location of VRAM
+
+.loop
+       mov al, [ebx] ; read next char and put it in al
+       mov ah, 00000111b ; puts the VGA text mode color white on black into ah
+
+       cmp al, 0 ; if the next character is null, we reached end of string
+       je .done ; so return the instruction
+
+       mov [edx], al ; otherwise put the next character in the video memory
+       mov [edx+1], ah ; do the same for its color
+
+       inc ebx ; point to next character in string
+       add edx, 2 ; point to next character in VRAM
+
+       jmp .loop ; go back to the loop
+
+.done
+       popa ; restore registers from stack
+       ret ; return
+
+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
+code_seg
+       dw 0FFFFh
+       dw 0
+       db 0
+       db 10011010b
+       db 11001111b
+       db 0
+data_seg
+       dw 0FFFFh
+       dw 0
+       db 0
+       db 10010010b
+       db 11001111b
+       db 0
+gdt_end
+gdt_desc
+       dw gdt_end - gdt_start - 1
+       dd gdt_start
+CODE_SEG equ code_seg - gdt_start
+DATA_SEG equ data_seg - gdt_start
+
+BOOT_DRIVE db 0 ; reserve a spot in RAM for our boot drive variable
+
+       times 510-($-$$) db 0
+       dw 0AA55h ; MBR signature