Initial kernel work
This commit is contained in:
parent
aa830de9c2
commit
c06dbb7280
|
@ -69,3 +69,4 @@ _deps
|
||||||
|
|
||||||
*.bin
|
*.bin
|
||||||
dist/
|
dist/
|
||||||
|
|
||||||
|
|
|
@ -3,124 +3,64 @@
|
||||||
; do some basic setup and then call into the kernel
|
; do some basic setup and then call into the kernel
|
||||||
|
|
||||||
[org 0x7c00] ; Address where the code expects to be loaded in. The BIOS always loads us here
|
[org 0x7c00] ; Address where the code expects to be loaded in. The BIOS always loads us here
|
||||||
|
kernel_offset: equ 0x1000
|
||||||
|
|
||||||
|
; We save the value of the current boot drive
|
||||||
|
mov [boot_drive], dl
|
||||||
|
|
||||||
|
; Now we setup the stack by setting the
|
||||||
|
; base pointer to address 0x8000. The address
|
||||||
|
; itself doesn't matter as long as it's far
|
||||||
|
; enough away from memory already in use by
|
||||||
|
; the BIOS
|
||||||
|
mov bp, 0x9000
|
||||||
|
mov sp, bp
|
||||||
|
call bios_cls
|
||||||
|
mov si, real_mode_msg
|
||||||
|
call bios_println
|
||||||
|
call load_kernel
|
||||||
|
call switch_to_protected_mode
|
||||||
|
jmp $ ; Keeps jumping at the current address (loops forever)
|
||||||
|
|
||||||
|
|
||||||
|
; Now we include our "function definitions" (after the
|
||||||
|
; loop, so they're never executed unless explicitly called)
|
||||||
|
%include "src/bootloader/util/disk.s"
|
||||||
|
%include "src/bootloader/util/io.s"
|
||||||
|
%include "src/bootloader/gdt.s"
|
||||||
|
%include "src/bootloader/switch32.s"
|
||||||
|
|
||||||
[bits 16] ; All x86 CPUs start in 16 bit (aka "real") mode, so we tell nasm to emit 16-bit code
|
[bits 16] ; All x86 CPUs start in 16 bit (aka "real") mode, so we tell nasm to emit 16-bit code
|
||||||
|
load_kernel: ; Loads the kernel into memory
|
||||||
|
mov si, loading_kernel_msg
|
||||||
start:
|
|
||||||
; Copied from https://github.com/limine-bootloader/limine/blob/trunk/stage1/hdd/bootsect.asm.
|
|
||||||
; Quote:
|
|
||||||
; Some BIOSes will do a funny and decide to overwrite bytes of code in
|
|
||||||
; the section where a FAT BPB would be, potentially overwriting
|
|
||||||
; bootsector code. Avoid that by filling the BPB area with dummy values.
|
|
||||||
; Some of the values have to be set to certain values in order to boot
|
|
||||||
; on even quirkier machines.
|
|
||||||
; Source: https://github.com/freebsd/freebsd-src/blob/82a21151cf1d7a3e9e95b9edbbf74ac10f386d6a/stand/i386/boot2/boot1.S
|
|
||||||
jmp skip_bpb
|
|
||||||
nop
|
|
||||||
bpb:
|
|
||||||
times 3-($-$$) db 0
|
|
||||||
.bpb_oem_id: db "TSOS "
|
|
||||||
.bpb_sector_size: dw 512
|
|
||||||
.bpb_sects_per_cluster: db 0
|
|
||||||
.bpb_reserved_sects: dw 0
|
|
||||||
.bpb_fat_count: db 0
|
|
||||||
.bpb_root_dir_entries: dw 0
|
|
||||||
.bpb_sector_count: dw 0
|
|
||||||
.bpb_media_type: db 0
|
|
||||||
.bpb_sects_per_fat: dw 0
|
|
||||||
.bpb_sects_per_track: dw 18
|
|
||||||
.bpb_heads_count: dw 2
|
|
||||||
.bpb_hidden_sects: dd 0
|
|
||||||
.bpb_sector_count_big: dd 0
|
|
||||||
.bpb_drive_num: db 0
|
|
||||||
.bpb_reserved: db 0
|
|
||||||
.bpb_signature: db 0
|
|
||||||
.bpb_volume_id: dd 0
|
|
||||||
.bpb_volume_label: db "TSOS "
|
|
||||||
.bpb_filesystem_type: times 8 db 0
|
|
||||||
; The skip_bpb and initialize_cs code is adapted
|
|
||||||
; from the Limine bootloader as well
|
|
||||||
skip_bpb:
|
|
||||||
cli
|
|
||||||
cld
|
|
||||||
jmp 0x0000:initialise_cs
|
|
||||||
initialise_cs:
|
|
||||||
xor si, si
|
|
||||||
mov ds, si
|
|
||||||
mov es, si
|
|
||||||
mov ss, si
|
|
||||||
sti
|
|
||||||
; We're not made for floppy disks, these are dead anyways.
|
|
||||||
; So if the value the BIOS passed is <0x80, just assume it has passed
|
|
||||||
; an incorrect value.
|
|
||||||
cmp dl, 0x80
|
|
||||||
jb invalid_boot_device
|
|
||||||
; Values above 0x8f are dubious so we assume we weren't booted properly
|
|
||||||
; for those either
|
|
||||||
cmp dl, 0x8f
|
|
||||||
ja invalid_boot_device
|
|
||||||
continue:
|
|
||||||
; First off, we setup the stack by setting the
|
|
||||||
; base pointer to address 0x8000. The address
|
|
||||||
; itself doesn't matter as long as it's far
|
|
||||||
; enough away from memory already in use by
|
|
||||||
; the BIOS
|
|
||||||
mov bp, 0x8000
|
|
||||||
mov sp, bp ; The stack starts out empty, so sp == bp
|
|
||||||
|
|
||||||
; Since we have a stack, we can now call functions,
|
|
||||||
; so we print a simple startup message using BIOS
|
|
||||||
; routines
|
|
||||||
mov si, startup_msg
|
|
||||||
call bios_print
|
|
||||||
; We also inform the user we're loading a few
|
|
||||||
; sectors from the boot drive
|
|
||||||
mov si, disk_read_info
|
|
||||||
and dx, 0xff ; Gets rid of the high 8 bits of dx so
|
|
||||||
; we only print the hex value of dh,
|
|
||||||
; which is the type of storage device
|
|
||||||
; we're booting from (0 = floppy,
|
|
||||||
; 1 = floppy2, 0x80 = hdd, 0x81 = hdd2)
|
|
||||||
call bios_print
|
|
||||||
call bios_printh
|
|
||||||
call bios_newline
|
|
||||||
|
|
||||||
; We load data from the current boot drive. The data is copied
|
|
||||||
; to memory starting at address 0x9000
|
|
||||||
mov bx, 0x9000
|
|
||||||
mov dh, 2 ; Read 3 sectors (2 for our dummy sectors, 1 for our variables)
|
|
||||||
; The dl register is already set by the BIOS
|
|
||||||
call load_disk
|
|
||||||
|
|
||||||
; Now we retrieve the test data we placed in the other
|
|
||||||
; sectors and print it in hexadecimal format
|
|
||||||
mov dx, [0x9000]
|
|
||||||
call bios_printh ; Should print 0xDADA
|
|
||||||
call bios_newline
|
|
||||||
mov dx, [0x9000 + 512]
|
|
||||||
call bios_printh ; Should print OxFACE
|
|
||||||
|
|
||||||
endless_loop:
|
|
||||||
jmp $ ; Keeps jumping at the current address (loops forever)
|
|
||||||
|
|
||||||
invalid_boot_device:
|
|
||||||
mov si, invalid_boot_device_msg
|
|
||||||
call bios_println
|
call bios_println
|
||||||
|
mov bx, kernel_offset
|
||||||
|
mov dh, 1
|
||||||
|
mov dl, [boot_drive]
|
||||||
|
call load_disk
|
||||||
|
ret
|
||||||
|
|
||||||
; Now we include our "function definitions" (after the
|
; Here we define our variables: They need to be defined after the
|
||||||
; loop, so they're never executed unless explicitly called)
|
; halting because otherwise they will be executed as code
|
||||||
%include "src/bootloader/util/disk.s"
|
real_mode_msg: db "Booted in real mode", 0
|
||||||
%include "src/bootloader/util/io.s"
|
protected_mode_msg: db "Switched to protected mode", 0
|
||||||
|
loading_kernel_msg: db "Loading kernel", 0
|
||||||
|
boot_drive: db 0
|
||||||
|
|
||||||
; Here we define our variables: They need to be defined after the
|
|
||||||
; halting because otherwise they will be executed as code
|
|
||||||
startup_msg: db "TSOS is starting up", 0xA, 0xD, 0
|
|
||||||
invalid_boot_device_msg: db "Invalid boot device", 0
|
|
||||||
|
|
||||||
; padding and magic number
|
[bits 32]
|
||||||
|
BEGIN_32BIT: ; After the switch we will get here
|
||||||
|
mov esi, protected_mode_msg
|
||||||
|
; My modified print function takes an offset
|
||||||
|
; to add to the start of the video memory that
|
||||||
|
; is added before writing. We skip the first
|
||||||
|
; 320 bytes so that we don't overwrite the log
|
||||||
|
; messages we may have written beforehand
|
||||||
|
mov ecx, 0x140
|
||||||
|
call vga_println
|
||||||
|
call kernel_offset
|
||||||
|
jmp $
|
||||||
|
|
||||||
|
; Padding and magic number
|
||||||
times 510 - ($-$$) db 0
|
times 510 - ($-$$) db 0
|
||||||
dw 0xaa55
|
dw 0xaa55
|
||||||
|
|
||||||
; We add more sectors to our binary so we can read them
|
|
||||||
times 256 dw 0xdada ; sector 2 = 512 bytes
|
|
||||||
times 256 dw 0xface ; sector 3 = 512 bytes
|
|
||||||
|
|
|
@ -43,6 +43,5 @@ disk_loop:
|
||||||
jmp $
|
jmp $
|
||||||
|
|
||||||
|
|
||||||
disk_read_info: db "Booting from disk type ", 0
|
|
||||||
disk_read_error_msg: db "Read error: ", 0
|
disk_read_error_msg: db "Read error: ", 0
|
||||||
disk_sectors_error_msg: db "Read error (wrong number of sectors)", 0
|
disk_sectors_error_msg: db "Sector read count error", 0
|
||||||
|
|
|
@ -97,6 +97,14 @@ bios_printh:
|
||||||
popa
|
popa
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
bios_cls:
|
||||||
|
pusha
|
||||||
|
mov ah, 0x00
|
||||||
|
mov al, 0x03 ; text mode 80x25 16 colours
|
||||||
|
int 0x10
|
||||||
|
popa
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
HEX_OUT: db '0x0000', 0 ; reserve memory for our new string
|
HEX_OUT: db '0x0000', 0 ; reserve memory for our new string
|
||||||
|
|
||||||
|
@ -108,23 +116,24 @@ VMEM_START: equ 0xb8000 ; Video memory always starts at this address
|
||||||
; the first byte is the ASCII codepoint to be printed, while the next
|
; the first byte is the ASCII codepoint to be printed, while the next
|
||||||
; octet represents additional formatting information (color, blink,
|
; octet represents additional formatting information (color, blink,
|
||||||
; underline, etc.). More info: https://en.wikipedia.org/wiki/VGA_text_mode
|
; underline, etc.). More info: https://en.wikipedia.org/wiki/VGA_text_mode
|
||||||
WHITE_ON_BLACK: equ 0x0f
|
TEXT_COLOR: equ 0x07
|
||||||
|
|
||||||
|
|
||||||
vga_print:
|
vga_print:
|
||||||
; Prints a null-terminated string located
|
; Prints a null-terminated string located
|
||||||
; on the esi register
|
; on the esi register. Offsets the write
|
||||||
|
; by ecx bytes
|
||||||
pusha
|
pusha
|
||||||
mov edx, VMEM_START
|
mov edx, VMEM_START
|
||||||
|
add edx, ecx
|
||||||
vga_print_loop:
|
vga_print_loop:
|
||||||
mov al, [esi]
|
mov al, [esi]
|
||||||
mov ah, WHITE_ON_BLACK
|
mov ah, TEXT_COLOR
|
||||||
cmp al, 0
|
cmp al, 0
|
||||||
je vga_print_done ; If we're at the null byte, we exit
|
je vga_print_done ; If we're at the null byte, we exit
|
||||||
mov [edx], ax ; Write the 2-byte character to video memory
|
mov [edx], ax ; Write the 2-byte character to video memory
|
||||||
inc esi ; Go to the next character in the string
|
inc esi ; Go to the next character in the string
|
||||||
add ebx, 2 ; Go to the next character in video memory
|
add edx, 2 ; Go to the next character in video memory
|
||||||
jmp vga_print_loop
|
jmp vga_print_loop
|
||||||
|
|
||||||
vga_print_done:
|
vga_print_done:
|
||||||
|
@ -137,8 +146,8 @@ vga_println:
|
||||||
; esi register and terminates it with a newline
|
; esi register and terminates it with a newline
|
||||||
pusha
|
pusha
|
||||||
call vga_print
|
call vga_print
|
||||||
mov esi, NEWLINE
|
;mov esi, NEWLINE
|
||||||
call vga_print
|
;call vga_print
|
||||||
popa
|
popa
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue