This way that branch can be deleted without any history being lost.
toolchain/
-boot
+boot.img
+boot/x86/boot
+**/*.o
+**/*.bin
+**/*.elf
+**/*.dbg
-AS = yasm # choose yasm or nasm here
-ASFLAGS = -f bin # compile a raw binary
+export AS = yasm
+export CC = i686-elf-gcc
+QEMU = qemu-system-i386 -monitor stdio
-boot: boot.s print.s disk.s
- $(AS) $(ASFLAGS) -o $@ boot.s
+export GIT_REV = $(shell git describe --long HEAD)
-clean:
- -rm boot
+CFLAGS = -std=gnu89 -g -Iinclude/arch/x86 -ffreestanding -DGIT_COMMIT=\"$(GIT_REV)\"
+
+KERNEL_OBJ = kernel/entry.o kernel/arch/x86/tty/tty.o kernel/drivers/irq/i8259a.o kernel/arch/x86/irq/idt.o kernel/arch/x86/irq/sample_handler.o kernel/kernel.o
+BOOTLOADER_OBJ = boot/x86/mbr boot/x86/vbr-fat32 boot/x86/stage3/LOADER.BIN
+
+default: kernel/kernel.elf bootloader
+
+all: default boot/x86/disk.img
+
+bootloader: $(BOOTLOADER_OBJ)
+
+run: all
+ $(QEMU) boot/x86/disk.img
-run:
- qemu-system-i386 boot
+boot/x86/mbr: boot/x86/mbr.s
+boot/x86/vbr-fat32: boot/x86/vbr-fat32.s boot/x86/fat32/*.s
+boot/x86/stage3/LOADER.BIN: boot/x86/stage3/*.s boot/x86/fat32/*.s
+$(BOOTLOADER_OBJ):
+ $(MAKE) -C boot/x86
-help:
- @echo "Run 'make' to compile."
- @echo "Run 'make clean' to delete compiled objects."
- @echo "Run 'make run' to run the compiled object. Must have qemu-system-i386 (but x86_64 should work too)."
+boot/x86/disk.img: boot/x86/mbr boot/x86/vbr-fat32 boot/x86/stage3/LOADER.BIN boot/x86/disk.dump kernel/kernel.bin
+ truncate -s1G boot/x86/disk.img
+ sfdisk boot/x86/disk.img < boot/x86/disk.dump
+ mkfs.fat -F 32 --offset 2048 boot/x86/disk.img
+ dd if=boot/x86/mbr of=boot/x86/disk.img bs=440 count=1 conv=notrunc
+ dd if=boot/x86/vbr-fat32 of=boot/x86/disk.img bs=1 skip=90 seek=1048666 conv=notrunc
+ mcopy -i boot/x86/disk.img@@1M boot/x86/stage3/LOADER.BIN ::.
+ mcopy -i boot/x86/disk.img@@1M kernel/kernel.bin ::./KERNEL.BIN
+
+kernel/kernel.bin: ${KERNEL_OBJ} kernel/linker.ld
+ $(CC) -ffreestanding -nostdlib -o $@ -T kernel/linker.ld ${KERNEL_OBJ}
+
+kernel/entry.o: kernel/entry.s
+ $(AS) -f elf $< -o $@
+
+kernel/arch/x86/irq/sample_handler.o: kernel/arch/x86/irq/sample_handler.c
+ $(CC) $(CFLAGS) -mgeneral-regs-only -c $< -o $@
+
+kernel/kernel.elf: kernel/kernel.bin
+ $(CC) -ffreestanding -nostdlib -o $@ -T kernel/linker.ld ${KERNEL_OBJ} -Wl,--oformat=elf32-i386
+ i686-elf-objcopy --only-keep-debug kernel/kernel.elf kernel/kernel.dbg
+ i686-elf-objcopy --add-gnu-debuglink=kernel/kernel.dbg kernel/kernel.elf
+ i686-elf-strip --strip-unneeded kernel/kernel.elf
+
+clean:
+ -rm kernel/kernel.{bin,dbg,elf} ${KERNEL_OBJ}
+ cd boot/x86 && $(MAKE) clean
-.PHONY = clean run help
+.PHONY: default all clean run bootloader
+++ /dev/null
- bits 16 ; boot sectors run in real mode
- org 7c00h ; boot sector is loaded at 0x7c00
-
-_start
- mov [boot_drive], dl ; BIOS sets dl to number of boot drive, we don't want to forget this
-
- mov bp, 8000h ; Move the stack away
- mov sp, bp
-
- push cs
- pop es
- mov bx, 9000h ; Load at 0000:9000
- mov dh, 2 ; Load 5 sectors
- mov dl, [boot_drive] ; Load from boot drive
- call read_sectors
-
- mov dx, [9000h] ; Print value at 9000h, should be 0xDEAD
- call print_word
- mov dx, [9000h + 512] ; Print value at 9200h, should be 0xBEEF
- call print_word
-
- jmp $ ; Endless loop
-
-%include "print.s"
-%include "disk.s"
-
-boot_drive resb 1 ; Reserve 1 byte for boot drive number
-
-times 510-($-$$) db 0 ; fill with 0 until 0x1fc is reached
-dw 0AA55h ; BIOS MBR signature
-
-times 256 dw 0DEADh ; Fill in 2nd sector with 0xDEAD
-times 256 dw 0BEEFh ; Fill in 3rd sector with 0xBEEF
--- /dev/null
+mbr
+vbr-fat32
+LOADER.BIN
+disk.img
--- /dev/null
+default: mbr vbr-fat32 stage3/LOADER.BIN
+
+mbr: mbr.s
+ $(AS) $(ASFLAGS) -w-zeroing -o $@ $<
+
+vbr-fat32: vbr-fat32.s fat32/*.s
+ $(AS) $(ASFLAGS) -o $@ $<
+
+stage3/LOADER.BIN: stage3/loader.s stage3/*.s
+ $(AS) $(ASFLAGS) -DGIT_REVISION=\"$(GIT_REV)\" -o $@ $<
+
+clean:
+ -rm mbr vbr-fat32 stage3/LOADER.BIN disk.img
+
+.PHONY : default clean
--- /dev/null
+label: dos
+label-id: 0x8bb0766f
+device: disk.img
+unit: sectors
+sector-size: 512
+
+disk.img1 : start= 2048, size= 2095104, type=c, bootable
--- /dev/null
+; FAT32 data structures
+; Because they have to be defined before use
+
+struc dir_entry
+ .name: resb 11
+ .attr: resb 1
+ .ntres: resb 1
+ .crttimetenth: resb 1
+ .crttime: resw 1
+ .crtdate: resw 1
+ .lstaccdate: resw 1
+ .firstclushi: resw 1
+ .wrttime: resw 1
+ .wrtdate: resw 1
+ .firstcluslo: resw 1
+ .filesize: resd 1
+endstruc
+
+; BPB definition, we use offsets to bp to save space
+ %define BS_jmpBoot [bp]
+ %define BS_OemName [bp+0x3]
+ %define BPB_BytsPerSec [bp+0xb]
+ %define BPB_SecPerClus [bp+0xd]
+ %define BPB_RsvdSecCnt [bp+0xe]
+ %define BPB_NumFats [bp+0x10]
+ %define BPB_RootEntCnt [bp+0x11]
+ %define BPB_TotSec16 [bp+0x13]
+ %define BPB_Media [bp+0x15]
+ %define BPB_FatSz16 [bp+0x16] ; count of sectors
+ %define BPB_SecPerTrk [bp+0x18]
+ %define BPB_NumHeads [bp+0x1a]
+ %define BPB_HiddSec [bp+0x1c]
+ %define BPB_TotSec32 [bp+0x20]
+ %define BPB_FatSz32 [bp+0x24]
+ %define BPB_ExtFlags [bp+0x28]
+ %define BPB_FSVer [bp+0x2a]
+ %define BPB_RootClus [bp+0x2c]
+ %define BPB_FsInfo [bp+0x30]
+ %define BPB_BkBootSec [bp+0x32] ; number of sector
+ %define BPB_Reserved [bp+0x34]
+ %define BS_DrvNum [bp+0x40]
+ %define BS_Reserved1 [bp+0x41]
+ %define BS_BootSig [bp+0x42]
+ %define BS_VolId [bp+0x43]
+ %define BS_VolLab [bp+0x47]
+ %define BS_FilSysType [bp+0x52]
+
--- /dev/null
+; FAT32 driver
+; BOOT_DRIVE, si and bp must be set up by the code using this driver.
+; (Specifically, si must be a pointer to the partition's partition
+; table entry and bp must be a pointer to the BPB.)
+; Before reading any cluster chains, callers must call get_1st_data_sec
+; and must be careful not to trash ecx after calling it.
+
+; eax - start cluster number
+; es:di - where to load the cluster chain
+; RETURN: CF set = error
+; NOTE: Cluster chain might be the root directory or a file.
+read_cluster_chain:
+ push eax
+ push es
+ push di
+ .loop:
+ call read_cluster
+ jc .done
+ cmp eax, 0xffffff7
+ jl .loop
+.done:
+ pop di
+ pop es
+ pop eax
+ ret
+
+; eax - cluster number
+; es:di - where to load the cluster (incremented automatically)
+; RETURN: eax - next cluster number
+; CF set = error
+read_cluster:
+ push ebx
+ push ecx
+ push edx
+.loop:
+ ; get the first sector of the cluster to read
+ push eax
+ sub eax, 2
+ xor ebx, ebx
+ mov bl, BPB_SecPerClus
+ mul ebx
+ add eax, ecx
+ mov ebx, eax
+ pop eax
+
+ ; read the cluster
+ push cx
+ movzx cx, BPB_SecPerClus
+ call read_sectors
+ pop cx
+ cmp dl, 0
+ je .error
+
+ ; increment the load offset (and segment if needed)
+ push eax
+ xor eax, eax
+ movzx ax, BPB_SecPerClus
+ mul word BPB_BytsPerSec
+ add di, ax
+ jno .get_next_cluster
+ mov ax, es
+ add ax, 0x1000
+ mov es, ax
+
+.get_next_cluster:
+ pop eax
+ ; get FAT sector number and offset containing the next cluster number
+ xor ebx, ebx
+ mov bl, 4 ; put 4 in EBX, doing this instead of "mov ebx, 4" saves us an entire byte
+ mul ebx
+ movzx ebx, word BPB_BytsPerSec
+ div ebx
+ movzx ebx, word BPB_RsvdSecCnt
+ add eax, ebx
+ add eax, [si]
+ mov ebx, edx
+ ; reminder for myself: EAX is FAT sector number, (E)BX is offset into FAT sector
+
+ ; load the FAT sector we're looking for
+ push di
+ push ebx ; offset
+ push es ; we want to read at 0:800, not STAGE3_SEGMENT:800
+ push eax ; desired LBA
+ xor ax, ax
+ mov es, ax
+ pop ebx ; pop LBA into EBX
+ mov di, 0x800
+ mov cx, 1
+ call read_sectors
+ cmp dl, 0
+ ; find the cluster we're looking for
+ pop es ; restore STAGE3_SEGMENT
+ pop ebx ; pop FAT offset back into EBX for cmp
+ mov eax, [di+bx]
+ pop di
+ jne .end
+ ; cleanup and return
+.error:
+ stc
+.end:
+ pop edx
+ pop ecx
+ pop ebx
+ ret
+
+; es:di - where to load the sector(s)
+; ebx - start LBA address
+; cx - how many sectors to read
+; RETURN: DL=0 means error
+read_sectors:
+ clc
+ push ax
+ push si
+ mov ah, 0x42
+ push dword 0
+ push dword ebx
+ push es
+ push di
+ push cx
+ push word 0x10
+ mov si, sp
+ mov dl, [BOOT_DRIVE]
+ int 0x13
+ mov dl, 1
+ jnc .done
+ mov dl, 0
+.done:
+ add sp, 16
+ pop si
+ pop ax
+ ret
+
+; ds:si - pointer to partition table entry
+; RETURN: ecx - first data sector
+get_1st_data_sec:
+ push eax
+ push ebx
+ movzx ax, BPB_NumFats
+ mul dword BPB_FatSz32 ; space occupied by all FATs
+ movzx ebx, word BPB_RsvdSecCnt
+ add eax, ebx ; space occupied by the reserved area
+ add eax, [si] ; space before the partition
+ mov ecx, eax
+ pop ebx
+ pop eax
+ ret
+
+read_error: db "Read error", 0
+
+BOOT_DRIVE: db 0
--- /dev/null
+; x86 bootloader for nameless-os, MBR portion
+
+bits 16
+cpu 686
+org 0x600
+
+_start:
+ cli ; we really don't want to be interrupted during relocation
+ ; set up segment registers
+ xor ax, ax
+ mov ds, ax
+ mov es, ax
+ mov ss, ax
+ mov sp, 0xfe00 ; just under the soon-to-be-loaded VBR, should be more than sufficient space
+
+ ; perform self-relocation
+ cld
+ mov si, 0x7c00
+ mov di, 0x600
+ mov cx, 0x100 ; 256 words = 512 bytes
+ rep movsw
+ ; some BIOSes may set CS to 0x7c0, work around that
+ jmp 0:real_start
+real_start:
+ sti
+ ; check for int 13h ext
+ call check_int13_ext
+ mov byte [BOOT_DRIVE], dl
+ ; look for active partition
+ mov si, part_1
+ mov cx, 4
+ .check_part_loop:
+ mov al, [si]
+ test al, 0x80
+ jnz .part_found
+ add si, 16
+ loop .check_part_loop
+ .error:
+ mov si, no_os
+ call print
+ hlt
+ jmp short $-1
+ .part_found:
+ ; look for any other active partitions, if they exist the partition table is invalid
+ cmp cx, 0
+ je .load_vbr
+ push si
+ .look_other_loop:
+ add si, 16
+ mov al, [si]
+ test al, 0x80
+ jnz .invalid_mbr
+ loop .look_other_loop
+ .load_vbr:
+ ; load active partition's VBR
+ pop si
+ mov ax, 0xfe0
+ add si, 8
+ mov edx, [si]
+ xor bx, bx
+ call read_sector
+ ; check is the VBR bootable (ends with 0x55 0xaa), if not halt
+ cmp word [0xfffe], 0xaa55
+ jne .not_bootable
+ mov dl, [BOOT_DRIVE]
+ call 0xfe00
+ .not_bootable:
+ mov si, no_os
+ call print
+ hlt
+ jmp short $-1
+ .invalid_mbr:
+ mov si, invalid_mbr
+ call print
+ hlt
+ jmp short $-1
+check_int13_ext:
+ pusha
+ mov ah, 0x41
+ mov bx, 0x55aa
+ mov dl, 0x80
+ int 0x13
+ jc .no_ext
+ test cx, 1
+ jz .no_ext
+ popa
+ ret
+ .no_ext:
+ mov si, no_int13_ext
+ call print
+ hlt
+ jmp short $-1
+
+; ax = segment
+; bx = offset
+; edx = start LBA of where to read
+read_sector:
+ pusha
+ ; set up temporary DAP
+ push dword 0
+ push edx
+ push ax
+ push bx
+ push word 1
+ push word 0x10
+ mov ah, 0x42
+ mov dl, byte [BOOT_DRIVE]
+ mov si, sp
+ int 0x13
+ jc .error
+ add sp, 16 ; dump our temporary DAP
+ popa
+ ret
+ .error:
+ mov si, read_fail
+ call print
+ hlt
+ jmp short $-1
+
+; DS:SI = string
+print:
+ pusha
+ mov ah, 0xe
+ xor bh, bh
+.loop:
+ lodsb
+ cmp al, 0
+ je .done
+ int 10h
+ jmp .loop
+.done:
+ mov al, 0xd
+ int 10h
+ mov al, 0xa
+ int 10h
+ popa
+ ret
+
+BOOT_DRIVE: resb 1
+
+read_fail: db "Error reading stage 2", 0
+invalid_mbr: db "Invalid partition table", 0
+no_os: db "No OS found", 0
+no_int13_ext: db "INT 13h extensions not found", 0
+
+times 440-($-$$) db 0
+
+part_table:
+disk_id: dd 0
+reserved: dw 0
+part_1:
+ .attrib db 0
+ .chs_start times 3 db 0
+ .part_type db 0
+ .chs_end times 3 db 0
+ .lba_start dd 0
+ .sect_count dd 0
+part_2:
+ .attrib db 0
+ .chs_start times 3 db 0
+ .part_type db 0
+ .chs_end times 3 db 0
+ .lba_start dd 0
+ .sect_count dd 0
+part_3:
+ .attrib db 0
+ .chs_start times 3 db 0
+ .part_type db 0
+ .chs_end times 3 db 0
+ .lba_start dd 0
+ .sect_count dd 0
+part_4:
+ .attrib db 0
+ .chs_start times 3 db 0
+ .part_type db 0
+ .chs_end times 3 db 0
+ .lba_start dd 0
+ .sect_count dd 0
+signature: dw 0xaa55
--- /dev/null
+%macro in_wait 0
+ push ax
+%%loop:
+ in al, 0x64
+ test al, 2
+ jnz %%loop
+ pop ax
+%endmacro
+
+%macro out_wait 0
+ push ax
+%%loop:
+ in al, 0x64
+ test al, 1
+ jz %%loop
+ pop ax
+%endmacro
+
+; CF set = A20 enabled
+check_a20:
+ push esi
+ push edi
+ push ax
+ mov esi, 0x500
+ mov edi, 0x100500
+
+ mov al, [esi]
+ mov ah, [edi]
+
+ mov [esi], byte 0
+ mov [edi], byte 0xff
+ cmp [esi], byte 0xff
+
+ mov [esi], al
+ mov [edi], ah
+
+ pop ax
+ pop edi
+ pop esi
+ ret
+
+enable_a20:
+ push ax
+ mov ax, 0x2401
+ int 0x15
+ jc .i8042_method
+ call check_a20
+ jnc .i8042_method
+ pop ax
+ ret
+ .i8042_method:
+ in_wait
+ mov al, 0xad
+ out 0x64, al
+ in_wait
+ mov al, 0xd0
+ out 0x64, al
+ out_wait
+ in al, 0x60
+ or al, 2
+ push ax
+ in_wait
+ mov al, 0xd1
+ out 0x64, al
+ in_wait
+ pop ax
+ out 0x60, al
+ in_wait
+ mov al, 0xae
+ out 0x60, al
+ call check_a20
+ pop ax
+ ret
+
--- /dev/null
+; Initial GDT for kernel, which wants a flat code and data descriptor
+gdt:
+ dw .end-.start-1
+ dd .start
+.start: dq 0 ; null descriptor
+.code:
+ dw 0xffff
+ dw 0
+ db 0
+ db 10011010b
+ db 11001111b
+ db 0
+.data:
+ dw 0xffff
+ dw 0
+ db 0
+ db 10010010b
+ db 11001111b
+ db 0
+.end:
+
--- /dev/null
+bits 16
+cpu 686
+org 0x1800
+
+%include "../fat32/fat32-structs.s"
+
+%macro print 1
+ push si
+ mov si, %1
+ call print_str
+ pop si
+%endmacro
+
+_start:
+ mov [BOOT_DRIVE], dl
+ call enable_unreal
+ print begin
+ call check_a20
+ jc .a20_enabled
+ call enable_a20
+ jnc .a20_enable_fail
+.a20_enabled:
+ print a20_enabled
+ call get_1st_data_sec
+ mov ax, 0x1000
+ mov es, ax
+ mov eax, BPB_RootClus
+ xor di, di
+ call read_cluster_chain
+ jc critical_error
+
+ push cx
+ push si
+.find_kernel:
+ mov si, kernel_name
+ cmp byte [es:di], 0 ; end of root directory
+ je .kernel_missing
+ cmp byte [es:di], 0xe5 ; unused entry
+ je .increment
+ mov cx, 11
+ push si
+ push di
+ repe cmpsb
+ pop di
+ pop si
+ je .kernel_found
+.increment:
+ add di, 32
+ jno .find_kernel
+ mov bx, es
+ add bx, 0x1000
+ mov es, bx
+ jmp .find_kernel
+.kernel_found:
+ pop si
+ pop cx
+ print kernel_found
+ mov ax, [es:di+dir_entry.firstclushi]
+ shl eax, 16
+ mov ax, [es:di+(dir_entry.firstcluslo)]
+ call print_dword
+ mov edi, 0x100000
+ print kernel_loading
+ call read_clus_chain_unreal ; load kernel
+ print kernel_loaded
+
+ cli
+ lgdt [gdt]
+ mov eax, cr0
+ or al, 1
+ mov cr0, eax
+ jmp 0x8:in_protected
+.kernel_missing:
+ print missing_kernel
+ jmp .halt
+
+.a20_enable_fail:
+ print a20_fail
+.halt:
+ hlt
+ jmp $-1
+
+; eax - start cluster
+; edi - address to load to
+read_clus_chain_unreal:
+ push eax
+ push edi
+.loop:
+ push di
+ push es
+ push ax
+ xor di, di
+ mov ax, 0x1000
+ mov es, ax
+ pop ax
+ call read_cluster
+ pop es
+ pop di
+ jc .done
+ push esi
+ push eax
+ push ebx
+ push ecx
+ mov esi, 0x10000
+ xor ebx, ebx
+ movzx eax, word BPB_BytsPerSec
+ movzx bx, byte BPB_SecPerClus
+ mul ebx
+ mov ecx, eax
+ call memcpy
+ add edi, ecx
+ pop ecx
+ pop ebx
+ pop eax
+ pop esi
+ cmp eax, 0xffffff7
+ jl .loop
+.done:
+ pop edi
+ pop eax
+ ret
+
+; esi - copy from
+; edi - copy to
+; ecx - bytes to copy
+memcpy:
+ push esi
+ push edi
+ push ecx
+ push dx
+
+.loop:
+ mov dl, [esi]
+ mov [edi], dl
+ dec ecx
+ cmp ecx, 0
+ je .done
+ inc esi
+ inc edi
+ jmp .loop
+.done:
+ pop dx
+ pop ecx
+ pop edi
+ pop esi
+ ret
+
+
+%include "unreal.s"
+%include "a20.s"
+%include "../fat32/fat32.s"
+%include "gdt.s"
+%include "print.s"
+
+in_protected:
+bits 32
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov ss, ax
+ mov fs, ax
+ mov gs, ax
+ call 0x100000
+ hlt
+ jmp $-1
+
+kernel_name: db "KERNEL BIN"
+begin: db "Nameless Bootloader revision ", GIT_REVISION, 0xd, 0xa, 0
+a20_enabled: db "A20 has been enabled", 0xd, 0xa, "Searching for kernel...", 0xd, 0xa, 0
+a20_fail: db "Failed to enable A20, giving up!", 0xd, 0xa, 0
+crit_err: db "A critical error occurred, dumping registers now: ", 0xd, 0xa, 0
+kernel_found: db "Found kernel at cluster ", 0
+kernel_loading: db 0xd, 0xa, "Loading kernel...", 0xd, 0xa, 0
+kernel_loaded: db "Kernel successfully loaded.", 0xd, 0xa, "Setting up kernel environment and running kernel...", 0xd, 0xa, 0
+missing_kernel: db "Could not find KERNEL.BIN", 0xd, 0xa, 0
+eax_s: db "EAX: ", 0
+ebx_s: db "EBX: ", 0
+ecx_s: db "ECX: ", 0
+edx_s: db "EDX: ", 0
+esi_s: db "ESI: ", 0
+edi_s: db "EDI: ", 0
+cs_s: db "CS: ", 0
+ds_s: db "DS: ", 0
+es_s: db "ES: ", 0
+ss_s: db "SS: ", 0
+space: db " ", 0
+hex_delm: db "0x", 0
+newline: db 0xd, 0xa, 0
--- /dev/null
+; Routines for printing
+
+print_str:
+ push ax
+ push bx
+ push si
+ mov ah, 0xe
+ xor bh, bh
+ mov bl, 0xf
+.loop:
+ lodsb
+ cmp al, 0
+ je .done
+ int 0x10
+ jmp .loop
+.done:
+ pop si
+ pop bx
+ pop ax
+ ret
+
+; AL - nibble to print
+; CF - clear prints low nibble, set prints high nibble
+print_nibble:
+ print hex_delm
+.no_delm:
+ pushf
+ push ax
+ push bx
+ pushf
+ xor bh, bh
+ mov bl, 0xf
+ mov ah, 0xe
+ popf
+ jnc .no_shift
+ shr al, 4
+.no_shift:
+ and al, 0xf
+ cmp al, 0xa
+ jge .letter
+ .numeric:
+ add al, '0'
+ int 0x10
+ jmp .done
+ .letter:
+ add al, 'a'-10
+ int 0x10
+.done:
+ pop bx
+ pop ax
+ popf
+ ret
+
+; AL - byte to print
+print_byte:
+ print hex_delm
+.no_delm:
+ pushf
+ stc
+ call print_nibble.no_delm
+ clc
+ call print_nibble.no_delm
+ popf
+ ret
+
+print_word:
+ print hex_delm
+.no_delm:
+ ror ax, 8
+ call print_byte.no_delm
+ ror ax, 8
+ call print_byte.no_delm
+ ret
+
+print_dword:
+ print hex_delm
+.no_delm:
+ ror eax, 16
+ call print_word.no_delm
+ ror eax, 16
+ call print_word.no_delm
+ ret
+
+; Do a dump of all general purpose regs
+critical_error:
+ print crit_err
+ print eax_s
+ call print_dword
+ print space
+ print ebx_s
+ mov eax, ebx
+ call print_dword
+ print space
+ print ecx_s
+ mov eax, ecx
+ call print_dword
+ print space
+ print edx_s
+ mov eax, edx
+ call print_dword
+ print newline
+ print esi_s
+ mov eax, esi
+ call print_dword
+ print space
+ print edi_s
+ mov eax, edi
+ call print_dword
+ print newline
+ print cs_s
+ mov ax, cs
+ call print_word
+ print space
+ print ds_s
+ mov ax, ds
+ call print_word
+ print space
+ print es_s
+ mov ax, es
+ call print_word
+ print space
+ print ss_s
+ mov ax, ss
+ call print_word
+ hlt
+ jmp $-1
--- /dev/null
+; Routine for enabling unreal mode, which allows using 32-bit offsets in real mode
+
+enable_unreal:
+ cli
+ push eax
+ push bx
+ push ds
+
+ lgdt [gdt_info]
+ mov eax, cr0
+ or al, 1
+ mov cr0, eax
+
+ jmp $+2
+
+ mov bx, 0x8
+ mov ds, bx
+
+ and al, 0xfe
+ mov cr0, eax
+
+ pop ds
+ pop bx
+ pop eax
+ sti
+ ret
+
+; GDT with 1 flat data segment descriptor
+gdt_info:
+ dw gdt_end-gdt_start-1
+ dd gdt_start
+gdt_start: dq 0 ; null entry
+gdt_flat:
+ dw 0xffff
+ dw 0
+ db 0
+ db 10010010b
+ db 11001111b
+ db 0
+gdt_end:
--- /dev/null
+; x86 bootloader for nameless-os, FAT32 VBR portion
+; This is what's going to be on most USB sticks and HDDs, for now
+
+bits 16
+org 0xfe00
+cpu 686
+
+STAGE3_ADDRESS equ 0x1800
+STAGE3_SEGMENT equ STAGE3_ADDRESS >> 4
+STAGE3_OFFSET equ STAGE3_ADDRESS & 0xf
+
+%include "fat32/fat32-structs.s"
+
+%macro print 1
+ mov si, %1
+ call print_str
+%endmacro
+
+_start:
+ jmp short real_start
+ nop
+
+times 0x57 db 0 ; skip past BPB
+
+real_start:
+ sti
+ ; no need to set up segments and stack again, because MBR did it for us
+ mov bp, 0xfe00
+
+ ; we expect the boot drive to be in DL and our partition table entry in DS:SI
+ mov [BOOT_DRIVE], dl
+
+ ; calculate the 1st sector of the data area
+ call get_1st_data_sec
+
+.load_root:
+ ; load the root directory
+ mov ax, 0x1000
+ mov es, ax
+ xor di, di
+ mov eax, BPB_RootClus
+ call read_cluster_chain
+
+ push cx
+ mov cx, 11
+ push si
+.find_stage_3:
+ mov si, STAGE3_NAME
+ cmp byte [es:di], 0 ; we have no more entries to look at
+ je .stage3_missing
+ cmp byte [es:di], 0xe5 ; the entry was only previously used and as such not worth looking at
+ je .increment
+ repe cmpsb
+ je .stage3_found
+.increment:
+ add di, 32
+ jno .find_stage_3
+ mov bx, es
+ add bx, 0x1000
+ mov es, bx
+.stage3_found:
+ pop si
+ pop cx
+ add di, 9 ; knowing that cmpsb incremented this by 11, we can also increment it by 9
+ ; right here so we can use 1 less offset below
+ ; stage 3 has been found and ES:DI points to its directory entry
+ mov ax, [es:di] ; load high half of the 1st cluster
+ shl eax, 16
+ mov ax, [es:di+(dir_entry.firstcluslo-dir_entry.firstclushi)] ; load low half of the 1st cluster
+ mov bx, STAGE3_SEGMENT
+ mov es, bx
+ mov di, STAGE3_OFFSET
+ call read_cluster_chain ; read stage 3
+ mov dl, [BOOT_DRIVE]
+ call STAGE3_ADDRESS ; call stage 3
+ jmp .halt ; halt in case we return, which should never happen
+
+.stage3_missing:
+ print stage3_missing
+ jmp .halt
+.error:
+ print read_error
+.halt:
+ hlt
+ jmp short $-1
+
+; ds:si - string
+print_str:
+ pusha
+ mov ah, 0xe
+ xor bh, bh
+.loop:
+ lodsb
+ cmp al, 0
+ je .done
+ int 0x10
+ jmp .loop
+.done:
+ mov al, 0xd
+ int 0x10
+ mov al, 0xa
+ int 0x10
+ popa
+ ret
+
+%include "fat32/fat32.s"
+
+stage3_missing: db "LOADER.BIN is missing", 0
+STAGE3_NAME: db "LOADER BIN"
+
+times 510-($-$$) db 0
+dw 0xaa55
+++ /dev/null
-; ES:BX - where to load
-; DH - sector count
-; DL - drive number
-
-read_sectors:
- xor bl, bl
- mov di, disk_reading
- call print
- push dx
- mov ah, 02h
- mov al, dh
- mov ch, 0
- mov dh, 0
- mov cl, 2
-
- int 13h
-
- jc read_error
-
- pop dx
- cmp dh, al
- jne read_error
- ret
-
-read_error:
- xor bl, bl
- mov di, disk_error
- call print
- mov dl, ah
- xor dh, dh
- call print_word
- mov dl, al
- call print_word
- jmp $
-
-disk_reading db "Reading from disk...", 0
-disk_error db "Failed to read from disk! Take a look at this error code:", 0
-
- ret
--- /dev/null
+1. Compile an i686-elf GCC and binutils following the instructions on this page:
+ https://wiki.osdev.org/GCC_Cross-Compiler
+ Or use some tool to automate it, like I use crossdev on my Gentoo machine.
+
+2. Install yasm, or modify the makefile to use nasm if you prefer it
+
+3. Run make in the source root
+
+4. Run the OS:
+ make run
+ This will create a disk image (if needed) and run it. Assumes that sfdisk, mtools and dosfstools are installed.
--- /dev/null
+#ifndef X86_IO_H
+#define X86_IO_H
+
+#include <stdint.h>
+
+static inline void outb(uint16_t port, uint8_t value)
+{
+ asm volatile ("outb %0, %1": : "a" (value), "Nd" (port));
+}
+
+static inline uint8_t inb(uint16_t port)
+{
+ uint8_t ret;
+ asm volatile ("inb %1, %0"
+ : "=a" (ret)
+ : "d" (port));
+ return ret;
+}
+
+static inline void io_wait(void)
+{
+ inb(0x80);
+}
+
+#endif
--- /dev/null
+#ifndef X86_I8259A_H
+#define X86_I8259A_H
+
+#define PIC1_COMMAND 0x20
+#define PIC1_DATA 0x21
+#define PIC2_COMMAND 0xA0
+#define PIC2_DATA 0xA1
+
+#define PIC_ICW1_INIT 0x10
+#define PIC_ICW1_ICW4 0x1
+
+#define PIC_ICW4_8086 0x1
+
+#define PIC_EOI 0x20
+
+extern void pic_init(int offset1, int offset2);
+extern void pic_mask(uint8_t irq);
+extern void pic_unmask(uint8_t irq);
+extern void pic_mask_all(void);
+extern void pic_unmask_all(void);
+extern void pic_send_eoi(uint8_t irq);
+
+#endif
--- /dev/null
+#ifndef X86_IDT_H
+#define X86_IDT_H
+
+#include <stdint.h>
+
+#define IDT_VECTOR_COUNT 256 /* our IDT will have 256 vectors */
+#define IDT_DESCRIPTOR_SIZE 8 /* each IDT descriptor is 8 bytes long */
+
+#define IDT_TRAP_GATE 0xF
+#define IDT_INTERRUPT_GATE 0xE
+
+struct idt_descriptor {
+ uint16_t offset_1; /* bits 0-15 of offset */
+ uint16_t segsel; /* segment selector */
+ unsigned unused : 8, type : 5, dpl : 2, present : 1;
+ uint16_t offset_2; /* bits 16-31 of offset */
+} __attribute__((packed));
+
+struct idtr {
+ uint16_t limit; /* size of IDT minus 1 */
+ uint32_t base; /* starting address of IDT */
+} __attribute__((packed));
+
+extern void idt_set_descriptor(struct idt_descriptor *idt, uint8_t vector, uint16_t segment, uint32_t offset, uint8_t type, uint8_t dpl);
+extern inline void load_idt(struct idtr idtr);
+extern inline void populate_idtr(struct idtr *idtr, struct idt_descriptor *idt);
+
+#endif
--- /dev/null
+#ifndef X86_TTY_H
+#define X86_TTY_H
+
+#include <stdint.h>
+
+#define VGA_COLOR_BLACK 0x0
+#define VGA_COLOR_BLUE 0x1
+#define VGA_COLOR_GREEN 0x2
+#define VGA_COLOR_TEAL 0x3
+#define VGA_COLOR_DARK_RED 0x4
+#define VGA_COLOR_MAGENTA 0x5
+#define VGA_COLOR_BROWN 0x6
+#define VGA_COLOR_LIGHT_GRAY 0x7
+#define VGA_COLOR_DARK_GRAY 0x8
+#define VGA_COLOR_PURPLE 0x9
+#define VGA_COLOR_LIME 0xA
+#define VGA_COLOR_CYAN 0xB
+#define VGA_COLOR_BRIGHT_RED 0xC
+#define VGA_COLOR_PINK 0xD
+#define VGA_COLOR_YELLOW 0xE
+#define VGA_COLOR_WHITE 0xF
+
+extern void screen_clear(void);
+extern void kprint(const char *string, uint8_t color);
+extern void kprintb(const uint8_t byte);
+extern void kprintw(const uint16_t word);
+extern void kprintd(const uint32_t dword);
+#endif
--- /dev/null
+#include <irq/idt.h>
+#include <stdint.h>
+
+/* Note to self: passing a pointer to an asm constraint as "m" will give a pointer to pointer */
+inline void load_idt(struct idtr idtr)
+{
+ asm volatile ("lidt %0": : "m" (idtr));
+}
+
+void idt_set_descriptor(struct idt_descriptor *idt, uint8_t vector, uint16_t segment, uint32_t offset, uint8_t type, uint8_t dpl)
+{
+ struct idt_descriptor *descriptor = &idt[vector];
+
+ descriptor->offset_1 = offset & 0xFFFF;
+ descriptor->segsel = segment;
+ descriptor->unused = 0;
+ descriptor->type = type;
+ descriptor->dpl = dpl;
+ descriptor->present = 1;
+ descriptor->offset_2 = offset >> 16;
+}
+
+inline void populate_idtr(struct idtr *idtr, struct idt_descriptor *idt)
+{
+ idtr->limit = ((IDT_VECTOR_COUNT * IDT_DESCRIPTOR_SIZE) - 1); /* limit must be size - 1 */
+ idtr->base = (uint32_t) idt;
+}
--- /dev/null
+#include <tty.h>
+#include <irq/i8259a.h>
+#include <io.h>
+#include <stdint.h>
+
+typedef uint32_t uword_t;
+
+struct interrupt_frame {
+ uword_t ip;
+ uword_t cs;
+ uword_t flags;
+};
+
+struct abort_frame;
+
+__attribute__((interrupt))
+void keyb_handler(struct interrupt_frame *frame)
+{
+ pic_send_eoi(1);
+ kprint("Got a keyboard interrupt!\n", 0);
+ inb(0x60);
+}
+
+
+__attribute__((interrupt))
+void double_fault(struct abort_frame *frame)
+{
+ *(volatile uint32_t *) (0xb8000) = 0xcf28cf3a;
+halt:
+ asm volatile ("cli; hlt");
+ goto halt;
+}
+
--- /dev/null
+#include <io.h>
+#include <tty.h>
+#include <stdint.h>
+
+#define VGA_WIDTH 80
+#define VGA_HEIGHT 25
+
+volatile char *video_memory = (char *) 0xB8000; /* VGA VRAM starts at 0xB8000 */
+
+static int cursor_x = 0; /* keep track of where cursor is */
+static int cursor_y = 0;
+
+char *hex_chars = "0123456789ABCDEF";
+
+void screen_clear(void)
+{
+ int x, y;
+ for ( y = 0; y < VGA_HEIGHT; y++ ) {
+ for ( x = 0; x < VGA_WIDTH; x++ ) {
+ video_memory[(y * VGA_WIDTH + x) * 2 + 1] = VGA_COLOR_LIGHT_GRAY;
+ video_memory[(y * VGA_WIDTH + x) * 2] = ' ';
+ }
+ }
+
+ cursor_x = 0;
+ cursor_y = 0;
+}
+
+void scroll_up(void)
+{
+ int x, y;
+ for ( y = 1; y < VGA_HEIGHT; y++ ) {
+ for ( x = 0; x < VGA_WIDTH; x++ ) {
+ video_memory[((y - 1) * VGA_WIDTH + x) * 2] = video_memory[(y * VGA_WIDTH + x) * 2];
+ }
+ }
+ for ( x = 0; x < VGA_WIDTH; x++ ) {
+ video_memory[((VGA_HEIGHT - 1) * VGA_WIDTH + x) * 2] = ' ';
+ }
+ cursor_y = VGA_HEIGHT - 1;
+}
+
+void kprint(const char *string, uint8_t color)
+{
+ char next_char;
+ uint8_t vga_misc_output;
+ uint16_t crtc_port;
+ next_char = *string;
+
+ while ( next_char != 0 ) {
+ if ( next_char == '\n') { cursor_x = 0; cursor_y++; }
+ else { video_memory[(cursor_y * VGA_WIDTH + cursor_x) * 2] = next_char; video_memory[((cursor_y * VGA_WIDTH + cursor_x++) * 2)+ 1] = color != 0 ? color : VGA_COLOR_LIGHT_GRAY; }
+ if ( cursor_x >= VGA_WIDTH ) { cursor_x = 0; cursor_y++; }
+ if ( cursor_y >= VGA_HEIGHT ) { scroll_up(); }
+ next_char = *++string;
+ }
+
+ vga_misc_output = inb(0x3CC);
+ if (vga_misc_output & 0x1 == 0) {
+ crtc_port = 0x3B4;
+ } else {
+ crtc_port = 0x3D4;
+ }
+
+ outb(crtc_port, 0xE);
+ outb(crtc_port + 1, (cursor_y * VGA_WIDTH + cursor_x) >> 8);
+ outb(crtc_port, 0xF);
+ outb(crtc_port + 1, (cursor_y * VGA_WIDTH + cursor_x) & 0xFF);
+}
+
+void kprintc(const char character, uint8_t color)
+{
+ uint8_t vga_misc_output;
+ uint16_t crtc_port;
+
+ if ( character == '\n') { cursor_x = 0; cursor_y++; }
+ else { video_memory[(cursor_y * VGA_WIDTH + cursor_x) * 2] = character; video_memory[((cursor_y * VGA_WIDTH + cursor_x++) * 2)+ 1] = color != 0 ? color : VGA_COLOR_LIGHT_GRAY; }
+ if ( cursor_x >= VGA_WIDTH ) { cursor_x = 0; cursor_y++; }
+ if ( cursor_y >= VGA_HEIGHT ) { scroll_up(); }
+
+ vga_misc_output = inb(0x3CC);
+ if (vga_misc_output & 0x1 == 0) {
+ crtc_port = 0x3B4;
+ } else {
+ crtc_port = 0x3D4;
+ }
+
+ outb(crtc_port, 0xE);
+ outb(crtc_port + 1, (cursor_y * VGA_WIDTH + cursor_x) >> 8);
+ outb(crtc_port, 0xF);
+ outb(crtc_port + 1, (cursor_y * VGA_WIDTH + cursor_x) & 0xFF);
+
+}
+
+void kprintb(uint8_t byte)
+{
+ uint8_t temp;
+ temp = byte >> 4;
+ kprint("0x", VGA_COLOR_LIGHT_GRAY);
+ kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+ temp = byte & 0xF;
+ kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+}
+
+void kprintw(uint16_t word)
+{
+ uint8_t temp;
+ temp = word >> 12;
+ kprint("0x", VGA_COLOR_LIGHT_GRAY);
+ kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+ temp = (word >> 8) & 0xF;
+ kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+ temp = (word >> 4) & 0xF;
+ kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+ temp = word & 0xF;
+ kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+}
+
+void kprintd(uint32_t dword)
+{
+ uint8_t temp;
+ temp = dword >> 28;
+ kprint("0x", VGA_COLOR_LIGHT_GRAY);
+ kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+ temp = (dword >> 24) & 0xF;
+ kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+ temp = (dword >> 20) & 0xF;
+ kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+ temp = (dword >> 16) & 0xF;
+ kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+ temp = (dword >> 12) & 0xF;
+ kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+ temp = (dword >> 8) & 0xF;
+ kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+ temp = (dword >> 4) & 0xF;
+ kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+ temp = dword & 0xF;
+ kprintc(hex_chars[temp], VGA_COLOR_LIGHT_GRAY);
+}
--- /dev/null
+#include <io.h>
+#include <tty.h>
+#include <irq/i8259a.h>
+
+void pic_init(int offset1, int offset2)
+{
+ uint8_t a1, a2;
+
+ a1 = inb(PIC1_DATA);
+ a2 = inb(PIC2_DATA);
+
+ outb(PIC1_COMMAND, PIC_ICW1_INIT | PIC_ICW1_ICW4);
+ io_wait();
+ outb(PIC2_COMMAND, PIC_ICW1_INIT | PIC_ICW1_ICW4);
+ io_wait();
+ outb(PIC1_DATA, offset1);
+ io_wait();
+ outb(PIC2_DATA, offset2);
+ io_wait();
+ outb(PIC1_DATA, 4);
+ io_wait();
+ outb(PIC2_DATA, 2);
+ io_wait();
+
+ outb(PIC1_DATA, PIC_ICW4_8086);
+ io_wait();
+ outb(PIC2_DATA, PIC_ICW4_8086);
+ io_wait();
+
+ outb(PIC1_DATA, a1);
+ outb(PIC2_DATA, a2);
+}
+
+void pic_mask(uint8_t irq)
+{
+ uint16_t port;
+ uint8_t value;
+
+ if (irq < 8) {
+ port = PIC1_DATA;
+ } else {
+ port = PIC2_DATA;
+ }
+
+ value = inb(port) | (1 << irq);
+ outb(port, value);
+}
+
+void pic_unmask(uint8_t irq)
+{
+ uint16_t port;
+ uint8_t value;
+
+ if (irq < 8) {
+ port = PIC1_DATA;
+ } else {
+ port = PIC2_DATA;
+ }
+
+ value = inb(port) & ~(1 << irq);
+ outb(port, value);
+}
+
+void pic_mask_all(void)
+{
+ outb(PIC1_DATA, 0xFF);
+}
+
+void pic_unmask_all(void)
+{
+ outb(PIC1_DATA, 0);
+}
+
+void pic_send_eoi(uint8_t irq)
+{
+ if (irq >= 8)
+ outb(PIC2_COMMAND, PIC_EOI);
+
+ outb(PIC1_COMMAND, PIC_EOI);
+}
--- /dev/null
+bits 32
+extern kmain
+extern __STACK_BOTTOM__
+
+global _start
+_start:
+ mov ebp, __STACK_BOTTOM__
+ mov esp, ebp
+ call kmain
+ hlt
+ jmp $-1
--- /dev/null
+#include <tty.h>
+#include <io.h>
+#include <irq/idt.h>
+#include <irq/i8259a.h>
+#include <stdint.h>
+
+extern void double_fault(struct abort_frame *frame);
+extern void keyb_handler(struct interrupt_frame *frame);
+struct idt_descriptor idt[256] __attribute__((aligned(0x10)));
+struct idtr idtr __attribute__((aligned(0x10)));
+
+void kmain(void)
+{
+ screen_clear();
+ kprint("Welcome to Nameless OS!\nRunning revision: ", 0);
+ kprint(GIT_COMMIT, 0);
+ kprint("\n", 0);
+ kprint("Preparing IDT...\n", 0);
+ idt_set_descriptor(idt, 0x8, 0x8, (uint32_t) double_fault, IDT_TRAP_GATE, 0x0);
+ idt_set_descriptor(idt, 0x21, 0x8, (uint32_t) keyb_handler, IDT_INTERRUPT_GATE, 0x0);
+ kprint("IDT prepared, loading...\n", 0);
+ populate_idtr(&idtr, idt);
+ load_idt(idtr);
+ kprint("IDT loaded, enabling interrupts...\n", 0);
+ pic_init(0x20, 0x28);
+ pic_mask_all();
+ pic_unmask(1);
+ asm volatile ("sti");
+ kprint("All done\n", 0);
+ while(1);
+}
--- /dev/null
+ENTRY(_start)
+OUTPUT_FORMAT(binary)
+
+SECTIONS
+{
+ . = 0x100000;
+ __KERNEL_BASE__ = .;
+
+ .text : ALIGN(4K) {
+ kernel/entry.o (.text)
+ *(.text)
+ }
+ .rodata : ALIGN(4K) { *(.rodata) }
+
+ /* .rodata is put after .text so that the read-only entry in the program
+ * header covers .rodata as well. When .rodata is after .data, that entry
+ * only covers .text, leaving .rodata in the writable program header
+ * entry. The problem with this approach is that the program header
+ * entry where .text and .rodata are is marked as executable, which
+ * would mean that .rodata should be executable too, but that doesn't
+ * matter yet because we (currently) assume that the NX bit is not
+ * supported anyway. */
+
+ .data : ALIGN(4K) { *(.data) }
+ .bss : ALIGN(4K) {
+ *(.bss)
+ /* Reserving 16KiB for the stack. A __STACK_TOP__ is not really
+ * needed (yet). */
+ . += 16K;
+ __STACK_BOTTOM__ = .;
+ }
+}
--- /dev/null
+#!/bin/sh
+# Shell script to install Nameless to a disk.
+
+BINARIES=( boot/x86/mbr boot/x86/vbr-fat32 boot/x86/stage3/LOADER.BIN kernel/kernel.bin )
+
+check_binaries() {
+ for i in "${BINARIES[@]}"; do
+ if ! [ -e $i ]; then
+ echo $i does not exist, have you compiled Nameless?
+ exit 1
+ fi
+ done
+}
+
+install_blkdev() {
+ # Ask the user are they sure they want to install.
+ local prompt
+ echo About to install Nameless to real disk $target.
+ echo This will WIPE ALL DATA on $target, type YES if you want to continue.
+ read -r prompt
+ if [ $prompt != "YES" ]; then
+ echo -e "Aborting."
+ exit 1
+ fi
+
+ echo OK, installing Nameless to $target.
+ echo Creating partition table on $target...
+
+ # Create a partition table on the disk.
+ fdisk $target <<- END > /dev/null
+ o
+ n
+ p
+ 1
+
+
+ a
+ t
+ 0c
+ w
+ END
+ if ! [ $? -eq 0 ]; then
+ echo An error occurred!
+ exit 1
+ fi
+ # Create a FAT32 filesystem on the disk.
+ echo Formatting $target...
+ mkfs.fat -F 32 "$target"1
+ if ! [ $? -eq 0 ]; then
+ echo An error occurred!
+ exit 1
+ fi
+ # Write MBR and VBR to the disk.
+ # TODO: write VBR to the backup BPB as well?
+ echo Writing bootsectors to $target...
+ dd 'if=boot/x86/mbr' "of=$target" 'bs=440' 'count=1'
+ if ! [ $? -eq 0 ]; then
+ echo An error occurred!
+ exit 1
+ fi
+ dd 'if=boot/x86/vbr-fat32' 'of='"$target"1 'bs=1' 'skip=90' 'seek=90'
+ if ! [ $? -eq 0 ]; then
+ echo An error occurred!
+ exit 1
+ fi
+ # Mount the partition and copy stage3 and kernel to it.
+ echo Copying files to $target...
+ local mountpoint=$(mktemp -d)
+ mount "$target"1 $mountpoint
+ if ! [ $? -eq 0 ]; then
+ echo An error occurred!
+ exit 1
+ fi
+ cp boot/x86/stage3/LOADER.BIN $mountpoint/LOADER.BIN
+ cp kernel/kernel.bin $mountpoint/KERNEL.BIN
+ if ! [ $? -eq 0 ]; then
+ echo An error occurred!
+ exit 1
+ fi
+ # Unmount the partition and flush write cache to make sure the OS
+ # binaries have actually been written to the disk.
+ echo Unmounting $target...
+ umount $mountpoint
+ rm -rf $mountpoint
+ echo Flushing cache...
+ sync
+ echo Nameless has been successfully installed to $target!
+ return
+}
+
+# Check if all the required tools are installed.
+[ -z $(command -v fdisk) ] && echo fdisk not found, is util-linux installed? && exit 127
+[ -z $(command -v mkfs.fat) ] && echo mkfs.fat not found, is dosfstools installed? && exit 127
+
+# Make sure that Nameless has been compiled.
+check_binaries
+
+# cd to the directory we're in.
+cd $(dirname $(realpath $0))
+
+# For convenience, list all connected disks.
+fdisk -l
+
+# Ask the user where to install.
+echo -n "Choose the disk you want to install to: "
+read -r target
+
+# If the target does not exist or is not a blkdev, exit.
+[ -b $target ] || echo $target does not exist or is not a block device! || exit 1
+install_blkdev
+exit 0
+++ /dev/null
-%define CURRENT_ADDR cs:di
-
-; Prints a null terminated string of your choice.
-; Arguments:
-; CS:DI - pointer to the string you want to print
-; BL - 0 prints newline, otherwise no
-print
- pusha
- mov ah, 0Eh ; teletype print
-.write2
- mov al, [CURRENT_ADDR] ; get current char and put it in al
- cmp al, 0 ; check if al is null (string terminator)
- je .newline ; if it is, start a new line
- int 10h ; otherwise write the char
- inc di ; increment pointer to string
- jmp .write2 ; jump back to this section
-.newline
- cmp bl, 1
- je .done
- mov al, 0Dh ; carriage return
- int 10h
- mov al, 0Ah ; line feed
- int 10h
-.done
- popa
- ret ; return
-
-; Prints a word (16-bit value) in hex format.
-; Arguments:
-; DX - word to print
-; BL - 0 will print a newline, 1 won't
-print_word
- pusha ; push all regs to stack
- mov di, HEX_OUT+5 ; set destination index to last char in HEX_OUT
- mov ax, dx ; copy argument to accumulator
- and al, 00001111b ; extract the low nibble of the low half of the accumulator
- call .compare ; fill in the string with correct value
- dec di ; decrement string pointer
- mov ax, dx ; copy argument to accumulator
- and al, 11110000b ; extract high nibble of low half of accumulator
- shr al, 4 ; shift high nibble to low nibble
- call .compare ; fill in string with correct value
- dec di
- mov al, ah ; copy high portion of accumulator to low
- and al, 00001111b ;
- call .compare
- dec di
- mov al, ah
- and al, 11110000b
- shr al, 4
- call .compare
- mov di, HEX_OUT
- call print ; write string
- popa
- ret
-
-.compare
- cmp al, 0 ; compare al with 0
- jne .one ; if not equal, compare with 1
- mov byte [CURRENT_ADDR], '0' ; set character to ASCII 0
- ret ; return
-.one
- cmp al, 1
- jne .two
- mov byte [CURRENT_ADDR], '1'
- ret
-.two
- cmp al, 2
- jne .three
- mov byte [CURRENT_ADDR], '2'
- ret
-.three
- cmp al, 3
- jne .four
- mov byte [CURRENT_ADDR], '3'
- ret
-.four
- cmp al, 4
- jne .five
- mov byte [CURRENT_ADDR], '4'
- ret
-.five
- cmp al, 5
- jne .six
- mov byte [CURRENT_ADDR], '5'
- ret
-.six
- cmp al, 6
- jne .seven
- mov byte [CURRENT_ADDR], '6'
- ret
-.seven
- cmp al, 7
- jne .eight
- mov byte [CURRENT_ADDR], '7'
- ret
-.eight
- cmp al, 8
- jne .nine
- mov byte [CURRENT_ADDR], '8'
- ret
-.nine
- cmp al, 9
- jne .ten
- mov byte [CURRENT_ADDR], '9'
- ret
-.ten
- cmp al, 0Ah
- jne .eleven
- mov byte [CURRENT_ADDR], 'A'
- ret
-.eleven
- cmp al, 0Bh
- jne .twelve
- mov byte [CURRENT_ADDR], 'B'
- ret
-.twelve
- cmp al, 0Ch
- jne .thirteen
- mov byte [CURRENT_ADDR], 'C'
- ret
-.thirteen
- cmp al, 0Dh
- jne .fourteen
- mov byte [CURRENT_ADDR], 'D'
- ret
-.fourteen
- cmp al, 0Eh
- jne .fifteen
- mov byte [CURRENT_ADDR], 'E'
- ret
-.fifteen
- mov byte [CURRENT_ADDR], 'F'
- ret
-
-HEX_OUT db "0x0000", 0