;=----------------------------------------------------------------------------=; ; GNU GPL OS/K ; ; ; ; Authors: spectral` ; ; NeoX ; ; ; ; Desc: Bootsector for OS/K ; ; (x86_64 architecture only) ; ;=----------------------------------------------------------------------------=; ;; BOOT "SEGMENT" %define BOOT_SEG 0x07c0 ; (BOOT_SEG << 4) + BOOT_OFF = 0x007c00 %define BOOT_OFF 0x0000 ;; STACK "SEGMENT" %define STACK_SEG 0x0600 ; (STACK_SEG << 4) + STACK_OFF = 0x007000 %define STACK_OFF 0x1000 ;; DISK BUFFER "SEGMENT" %define BUFFER_SEG 0x2000 ; (BUFFER_SEG << 4) + BUFFER_OFF = 0x020000 %define BUFFER_OFF 0x0000 ;; SECOND STAGE LOADER "SEGMENT" %define LOAD_SEG 0x0000 ; (LOAD_SEG << 4) + LOAD_OFF = 0x001000 %define LOAD_OFF 0x1000 [BITS 16] ; Ensure 16-bit code (because fuck UEFI) ;---------------------------------------------------; ; Disk description table ; ;---------------------------------------------------; Intro: jmp short _start ; Jump over the BIOS PARAMETER BLOCK nop ; Required by BIOS to recognize the Disk BPB: %define OEMName bp+0x03 ; Disk label %define bytesPerSector bp+0x0b ; Bytes per sector %define sectorsPerCluster bp+0x0d ; Sectors per cluster %define reservedSectors bp+0x0e ; Reserved sectors %define fats bp+0x10 ; Number of fats %define rootDirEntries bp+0x11 ; Number of entries in root dir %define sectors bp+0x13 ; Logical sectors %define mediaType bp+0x15 ; Media descriptor byte %define fatSectors bp+0x16 ; Sectors per FAT %define sectorsPerTrack bp+0x18 ; Sectors per track %define heads bp+0x1a ; Number of sides/heads %define hiddenSectors bp+0x1c ; Hidden sectors %define hugeSectors bp+0x20 ; LBA sectors %define biosBootdrvNum bp+0x24 ; Bootdrv number %define reserved bp+0x25 ; This is not used %define bootSignature bp+0x26 ; Bootdrv signature %define volumeId bp+0x27 ; Volume ID %define volumeLabel bp+0x2b ; Volume Label %define fatTypeLabel bp+0x36 ; File system type times 0x3b db 0x00 ;; ENTRY POINT _start: jmp BOOT_SEG:$+5 ; Fix the cs:ip registers with a vaudou magical trip bootstrap: jmp go ;; LOVELY DATA FileNotFound db "FStage ERR : NO LOADER", 0 ; File was not found DiskError db "FStage ERR : DISK", 0 ; Error while reading from the disk UserData dw 0 ; Start of the data sectors Bootdrv db 0 ; Boot Bootdrv number filename db "LOADER BIN" ; Filename ;; GO ! go: mov ax, BOOT_SEG ; Set segments to the location of the bootloader mov ds, ax mov es, ax ;; INIT STACK cli mov ax, STACK_SEG ; Init the staaaaaack mov ss, ax ; Continue init the staaaaaaaack mov sp, STACK_OFF ; Ok man, the stack is in 4K :O sti mov bp, (0x7c0-STACK_SEG) << 4 ; Correct bp for the disk description table ! ;; INITIALIZE BOOT DISK or dl, dl ; Verifying dl points actually to the boot drive jz load_root mov byte [Bootdrv], dl ; Another soul (the disk) saved ! mov ah, 0x08 int 0x13 ; int 0x13 : read drive parameters/geom jc load_root and cx, 0x003f ; Maximum sector number is the high bits 6-7 of cl mov word [sectorsPerTrack], cx ; And whose low 8 bits are in ch mov dl, dh ; Convert the maximum head number to a word with another vaudou magical trip xor dh, dh inc dx ; because head numbers start at zero mov word [heads], dx ; Another soul (the heads number) saved ! ;; LOAD THE ROOT DIRECTORY FROM DISK load_root: xor cx, cx mov ax, 32 ; Size of root dir = (rootDirEntries * 32) / bytesPerSector mul word [rootDirEntries] ; multiply by the total size of the root directory div word [bytesPerSector] ; divide by the number of bytes used per sector xchg cx, ax mov al, byte [fats] ; Location of root dir = (fats * fatSectors) + reservedSectors mul word [fatSectors] ; multiply by the sectors used add ax, word [reservedSectors] ; increase ax by the reserved sectors mov word [UserData], ax ; Start of user data = startOfRoot + numberOfRoot add word [UserData], cx ; Add the size and location of the root directory mov di, BUFFER_SEG ; Set the extra segment to the disk buffer mov es, di mov di, BUFFER_OFF ; Set es:di and load the root directory into the disk buffer call read_sectors ; Read the sectoooooooors ! ;; FIND THE SECOND STAGE LOADER mov di, BUFFER_OFF ; Set es:di to the disk buffer mov cx, word [rootDirEntries] ; Search through all of the root dir entries xor ax, ax ; Clear ax for the file entry offset search_root: xchg cx, dx ; Save cx because it's a loop counter mov si, filename ; Load the filename mov cx, 11 ; Compare first 11 bytes rep cmpsb ; Compare si and di cx times je load_fat ; We found the LOADEEEEEEEER!!! add ax, 32 ; File entry offset mov di, BUFFER_OFF ; Point back to the start of the entry add di, ax ; Add the offset to point to the next entry xchg dx, cx loop search_root ; Continue to search for the file ;; ERROR... mov si, FileNotFound ; Could not find the file call print ;; REBOOT reboot: xor ax, ax int 0x16 ; Get a single keypress mov ah, 0x0e ; Teletype output mov al, 0x0d ; Carriage return int 0x10 ; Video interupt mov al, 0x0a ; Line feed int 0x10 ; Video interupt mov al, 0x0a ; Line feed int 0x10 ; Video interupt xor ax, ax int 0x19 ; Reboot the system ;; LOAD THE FAT FROM THE FILE load_fat: mov ax, word [es:di + 15] ; Get the file cluster at offset 26 push ax ; Store the FAT cluster xor ax, ax ; Size of fat = (fats * fatSectors) mov al, byte [fats] ; Move number of fats into al mul word [fatSectors] ; Move fat sectors into bx mov cx, ax ; Store in cx mov ax, word [reservedSectors] ; Convert the first fat on the disk mov di, BUFFER_OFF ; Set es:di and load the fat sectors into the disk buffer call read_sectors ; Read the sectooooooooooors !!! ;; LOAD THE CLUSTERS OF THE LOADER AND JUMP mov di, LOAD_SEG mov es, di ; Set es:bx to where the file will load mov di, LOAD_OFF pop ax ; File cluster restored call read_clusters ; Read clusters from the file mov dl, byte [Bootdrv] ; Pass the boot Bootdrv into dl jmp LOAD_SEG:LOAD_OFF ; Jump to the file loaded! hlt ; This should never be hit... ; ... ; ... ; I hope.... ; ... ;---------------------------------------------------; ; FUNCTIONS ; ;---------------------------------------------------; read_clusters: ;---------------------------------------------------; ; Read file clusters, starting at the given cluster,; ; expects FAT to be loaded into the disk buffer. ; ; Please note that this may allocate up to 128KB ; ; of ram. ; ; ; ; Expects: AX = Starting cluster ; ; ES:DI = Location to load clusters ; ; ; ; Returns: None ; ; ; ;---------------------------------------------------; pusha push es .cluster_loop: xor bh, bh xor dx, dx push ax ; Get the cluster start = (cluster - 2) * sectorsPerCluster + UserData sub ax, 2 ; Subtract 2 mov bl, byte [sectorsPerCluster] ; Sectors per cluster is a byte value mul bx ; multiply (cluster - 2) * sectorsPerCluster add ax, word [UserData] ; add the UserData xor ch, ch mov cl, byte [sectorsPerCluster] ; Sectors to read call read_sectors ; Read the sectors pop ax ; Current cluster number xor dx, dx ;; Calculate next sector for FAT16 (cluster * 2) mov bx, 2 ; Multiply the cluster by two (cluster is in ax) mul bx ;; Load sector in RAM push ds push si mov si, BUFFER_SEG mov ds, si ; Temporarly set ds:si to the FAT buffer mov si, BUFFER_OFF add si, ax ; Point to the next cluster in the FAT entry mov ax, word [ds:si] ; Load ax to the next cluster in FAT pop si pop ds ;; Next cmp ax, 0xfff8 ; Check if we are at the end of the file? jae .done add di, 512 ; Add to the pointer offset jnc .cluster_loop ;; Correct the buffer because an error will occur if the buffer in memory mov dx, es ; overlaps a 64k page boundry, when di overflows add dh, 0x10 ; it will trigger the carry flag, so correct mov es, dx ; extra segment by 0x1000 jmp .cluster_loop ; Load the next file cluster .done: pop es popa ret read_sectors: ;---------------------------------------------------; ; Read sectors starting at a given sector by ; ; the given times and load into a buffer. Please ; ; note that this may allocate up to 128KB of ram. ; ; ; ; Expects: AX = Starting sector ; ; CX = Number of sectors to read ; ; ES:DI = Location to load sectors ; ; ; ; Returns: None ; ; ; ;---------------------------------------------------; pusha push es mov bx, di ; Convert es:di to es:bx for int 13h .sector_loop: push ax push cx xor dx, dx div word [sectorsPerTrack] ; Divide the lba (value in ax) by sectorsPerTrack mov cx, dx ; Save the absolute sector value inc cx xor dx, dx ; Divide by the number of heads div word [heads] ; to get absolute head and track values mov dh, dl ; Move the absolute head into dh mov ch, al ; Low 8 bits of absolute track shl ah, 1 ; High 2 bits of absolute track shl ah, 1 shl ah, 1 shl ah, 1 shl ah, 1 shl ah, 1 or cl, ah ; Now cx is set with respective track and sector numbers mov dl, byte [Bootdrv] ; Set correct Bootdrv for int 13h mov di, 5 ; Try five times to read the sector because i love 5 .attempt_read: mov ax, 0x0201 ; Read Sectors func of int 13h, read one sector int 0x13 ; Call int 13h (BIOS disk I/O) jnc .read_ok ; If no carry set, the sector has been read xor ah, ah ; Reset Bootdrv func of int 13h int 0x13 ; Call int 13h (BIOS disk I/O) dec di ; Decrease read attempt counter jnz .attempt_read ; Try to read the sector again mov si, DiskError ; Error reading the disk :/ call print jmp reboot .read_ok: pop cx pop ax inc ax ; Increase the next sector to read add bx, word [bytesPerSector] ; Add to the buffer address for the next sector jnc .next_sector ;; Fixing buffer because an error will occur if the buffer in memory mov dx, es ; overlaps a 64k page boundry, when bx overflows add dh, 0x10 ; it will trigger the carry flag, so correct mov es, dx ; es segment by 0x1000 .next_sector: loop .sector_loop pop es popa ret print: ;---------------------------------------------------; ; Print out a simple string. ; ; ; ; Expects: DS:SI = String to print ; ; ; ; Returns: None ; ; ; ;---------------------------------------------------; lodsb ; Load byte from ds:si to al or al, al ; If al is empty stop looping jz .done ; Done looping and return mov ah, 0x0e ; Teletype output int 0x10 ; Video interupt jmp print ; Loop untill string is null .done: ret ;; END times 510 - ($ - $$) db 0 ; Pad remainder of boot sector with zeros dw 0xaa55 ; Boot signature