TSOS/src/bootloader/mbr.s

127 lines
4.9 KiB
ArmAsm

; Definition of the MBR (Master Boot Record). This is basically our bootloader and
; is located in the first 512 bytes of the drive we're booting from. From here, we
; 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
[bits 16] ; All x86 CPUs start in 16 bit (aka "real") mode, so we tell nasm to emit 16-bit code
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
; 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"
; 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
times 510 - ($-$$) db 0
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