; 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