Big bang
This commit is contained in:
commit
b7156335cd
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
build/
|
||||
.idea/
|
54
Makefile
Normal file
54
Makefile
Normal file
@ -0,0 +1,54 @@
|
||||
BUILD_DIR ?= ./build
|
||||
SRC_DIRS ?= ./src
|
||||
BOOTLOADER_DIR ?= ./asm/boot
|
||||
|
||||
SRCS := $(shell find $(SRC_DIRS) -name *.cpp -or -name *.c -or -name *.s)
|
||||
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
|
||||
DEPS := $(OBJS:.o=.d)
|
||||
|
||||
INC_DIRS := $(shell find $(SRC_DIRS) -type d)
|
||||
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
|
||||
|
||||
CPPFLAGS ?= $(INC_FLAGS) -MMD -MP -g -std=c++11 -Wall
|
||||
|
||||
# Default makefile target
|
||||
all: os-image
|
||||
|
||||
run: os-image
|
||||
qemu-system-x86_64 -hda build/os-image.bin
|
||||
|
||||
# Builds a bootable x86 image that loads the kernel
|
||||
os-image: $(BUILD_DIR)/kernel.bin $(BUILD_DIR)/bootloader.bin
|
||||
cat $(BUILD_DIR)/bootloader.bin $(BUILD_DIR)/kernel.bin > build/os-image.bin
|
||||
|
||||
# Builds the binary kernel image with the entry prefix
|
||||
$(BUILD_DIR)/kernel.bin: $(BUILD_DIR)/kernel_entry.o $(OBJS)
|
||||
ld -m elf_i386 -o $@ -Ttext 0x1000 --oformat binary $(BUILD_DIR)/kernel_entry.o $(OBJS)
|
||||
|
||||
# Builds the x86 -> 32-bit bootloader
|
||||
$(BUILD_DIR)/bootloader.bin: $(BOOTLOADER_DIR)/main16.asm
|
||||
cd $(BOOTLOADER_DIR); nasm -f bin -o ../../build/bootloader.bin main16.asm
|
||||
|
||||
# Builds the kernel entry object file
|
||||
$(BUILD_DIR)/kernel_entry.o: asm/boot.asm
|
||||
$(MKDIR_P) $(dir $@)
|
||||
nasm asm/boot.asm -f elf -o $@
|
||||
|
||||
# Build C++ sources
|
||||
$(BUILD_DIR)/%.cpp.o: %.cpp
|
||||
$(MKDIR_P) $(dir $@)
|
||||
g++ $(CPPFLAGS) $(CXXFLAGS) -m32 -ffreestanding -c $< -o $@ -fno-pie
|
||||
|
||||
# Build C sources
|
||||
$(BUILD_DIR)/%.c.o: %.c
|
||||
$(MKDIR_P) $(dir $@)
|
||||
gcc $(CFLAGS) $(CXXFLAGS) -m32 -ffreestanding -c $< -o $@ -fno-pie
|
||||
|
||||
# Clean all build objects
|
||||
.PHONY: clean
|
||||
clean:
|
||||
$(RM) -rf $(BUILD_DIR)
|
||||
|
||||
-include $(DEPS)
|
||||
|
||||
MKDIR_P ?= mkdir -p
|
8
asm/boot.asm
Normal file
8
asm/boot.asm
Normal file
@ -0,0 +1,8 @@
|
||||
; NOT part of the bootloader
|
||||
; this is the kernel entry code that calls main() in kernel.cpp
|
||||
|
||||
[bits 32] ; we're in 32-bit protected mode at this point
|
||||
[extern main] ; going to call main in kernel.c
|
||||
|
||||
call main
|
||||
jmp $
|
10
asm/boot/bootable.asm
Normal file
10
asm/boot/bootable.asm
Normal file
@ -0,0 +1,10 @@
|
||||
[bits 16]
|
||||
|
||||
; Catch end-of-execution in an infinite loop
|
||||
g_infinite_loop:
|
||||
jmp g_infinite_loop
|
||||
|
||||
; Fill in the remaining space with 00s and set the
|
||||
; magic bit to make the bios boot the sector.
|
||||
times 510-($-$$) db 0
|
||||
dw 0xaa55
|
27
asm/boot/disk16.asm
Normal file
27
asm/boot/disk16.asm
Normal file
@ -0,0 +1,27 @@
|
||||
[bits 16]
|
||||
|
||||
i_disk16_error_message: db "Disk read error!", 0
|
||||
|
||||
disk16_load:
|
||||
push dx ; keep track of the # of sectors requested to be read
|
||||
|
||||
mov ah, 0x02 ; bios read sector
|
||||
mov al, dh ; read dh # sectors
|
||||
mov ch, 0x00 ; select cylinder 0
|
||||
mov dh, 0x00 ; select head 0
|
||||
mov cl, 0x02 ; start reading from 2nd sector
|
||||
; (i.e. the sector after the boot sector)
|
||||
|
||||
int 0x13 ; bios interrupt to trigger read
|
||||
|
||||
jc disk16_on_error ; jump if carry (fault) flag was set
|
||||
|
||||
pop dx ; restore original # of registers in case it was modified
|
||||
cmp dh, al ; compare # sectors read (al) to # sectors expected (dh)
|
||||
jne disk16_on_error ; error if # sectors differs
|
||||
ret
|
||||
|
||||
disk16_on_error:
|
||||
mov bx, i_disk16_error_message
|
||||
call print16
|
||||
jmp g_infinite_loop
|
38
asm/boot/gdt16.asm
Normal file
38
asm/boot/gdt16.asm
Normal file
@ -0,0 +1,38 @@
|
||||
[bits 16]
|
||||
; Sets up the GDT for the switch to 32-bit protected mode
|
||||
|
||||
gdt_setup_flat:
|
||||
gdt_flat_null: ; mandatory null descriptor
|
||||
dd 0x0 ; dd means define double-word (i.e. 4 bytes)
|
||||
dd 0x0
|
||||
|
||||
gdt_flat_code_section:
|
||||
; base: 0x0, limit: 0xfffff
|
||||
; 1st flags: (present) 1 (privilege) 00 (descriptor type) 1 -> 1001b
|
||||
; Type flags: (code) 1 (conforming) 0 (readable) 1 (accessed) 0 -> 1010b
|
||||
; 2nd flags: (granularity) 1 (32-bit default) 1 (64-bit segment) 0 (AVL) 0 -> 1100b
|
||||
dw 0xffff ; limit (bits 0 - 15)
|
||||
dw 0x0 ; base (bits 0 - 15)
|
||||
db 0x0 ; base (bits 16 - 23)
|
||||
db 10011010b ; 1st flags, type flags
|
||||
db 11001111b ; 2nd flags, limit (bits 16 - 19)
|
||||
db 0x0 ; base (bits 24 - 31)
|
||||
|
||||
gdt_flat_data_section:
|
||||
; Same as code segment except for the type flags:
|
||||
; type flags: (code) 0 (expand down) 0 (writable) 1 (accessed) 0 -> 0010b
|
||||
dw 0xffff ; limit (bits 0 - 15)
|
||||
dw 0x0 ; base (bits 0 - 15)
|
||||
db 0x0 ; base (bits 16 - 23)
|
||||
db 10010010b ; 1st flags, type flags
|
||||
db 11001111b ; 2nd flags, limit (bits 16 - 19)
|
||||
db 0x0 ; base (bits 24 - 31)
|
||||
|
||||
gdt_flat_end: ; so the assembler can calculate the size of the gdt section
|
||||
|
||||
gdt_flat_descriptor:
|
||||
dw gdt_flat_end - gdt_setup_flat - 1 ; size of our GDT, always less 1 of the true size
|
||||
dd gdt_setup_flat ; start address of the gdt
|
||||
|
||||
i_gdt_code_segment equ gdt_flat_code_section - gdt_setup_flat
|
||||
i_gdt_data_segment equ gdt_flat_data_section - gdt_setup_flat
|
27
asm/boot/header.asm
Normal file
27
asm/boot/header.asm
Normal file
@ -0,0 +1,27 @@
|
||||
; this offsets all memory addresses in the asm code
|
||||
; to be relative to the start of the assembled bytecode
|
||||
; in memory, where the BIOS loads it.
|
||||
[org 0x7c00]
|
||||
[bits 16]
|
||||
|
||||
; store the boot drive - bios puts in dl
|
||||
mov [i_global_boot_drive], dl
|
||||
|
||||
jmp main16 ; start the main label
|
||||
|
||||
i_global_boot_drive: db 0
|
||||
|
||||
; Helper label that pops all stack registers and returns
|
||||
; Used as a one-line return in service routines
|
||||
; e.g. jmp global_return
|
||||
g_return:
|
||||
popa
|
||||
ret
|
||||
|
||||
; Include common code
|
||||
%include "print16.asm"
|
||||
%include "disk16.asm"
|
||||
%include "gdt16.asm"
|
||||
%include "switch16.asm"
|
||||
%include "kern16.asm"
|
||||
%include "print32.asm"
|
19
asm/boot/kern16.asm
Normal file
19
asm/boot/kern16.asm
Normal file
@ -0,0 +1,19 @@
|
||||
[bits 16]
|
||||
|
||||
; Loads the 32-bit kernel
|
||||
|
||||
i_kernel16_string: db 'Loading 32-bit kernel...', 0
|
||||
|
||||
i_kernel16_offset equ 0x1000
|
||||
|
||||
kernel16_load:
|
||||
pusha
|
||||
mov bx, i_kernel16_string
|
||||
call print16
|
||||
|
||||
mov bx, i_kernel16_offset ; specify the disk load location
|
||||
mov dh, 15 ; load first 15 sectors
|
||||
mov dl, [i_global_boot_drive] ; from the boot disk
|
||||
call disk16_load
|
||||
|
||||
jmp g_return
|
20
asm/boot/main16.asm
Normal file
20
asm/boot/main16.asm
Normal file
@ -0,0 +1,20 @@
|
||||
[bits 16]
|
||||
%include "header.asm"
|
||||
|
||||
i_16bit_string: db 'Started 16-bit real mode.', 0
|
||||
|
||||
main16:
|
||||
mov bp, 0x9000 ; move the stack out of the way of anything important
|
||||
mov sp, bp
|
||||
|
||||
mov bx, i_16bit_string
|
||||
call print16
|
||||
|
||||
call kernel16_load ; load the kernel
|
||||
|
||||
call switch16_to_32 ; never returns. Calls main32.
|
||||
jmp g_infinite_loop
|
||||
|
||||
|
||||
%include "main32.asm"
|
||||
%include "bootable.asm"
|
11
asm/boot/main32.asm
Normal file
11
asm/boot/main32.asm
Normal file
@ -0,0 +1,11 @@
|
||||
[bits 32]
|
||||
|
||||
i_32bit_string: db 'Switched to 32-bit protected mode.', 0
|
||||
|
||||
main32:
|
||||
mov ebx, i_32bit_string
|
||||
call print32 ; print the 32-bit protected mode message
|
||||
|
||||
call i_kernel16_offset ; jump to the address of the loaded kernel
|
||||
|
||||
jmp g_infinite_loop ; hang
|
76
asm/boot/print16.asm
Normal file
76
asm/boot/print16.asm
Normal file
@ -0,0 +1,76 @@
|
||||
[bits 16]
|
||||
|
||||
; Prints \r\n using BIOS teletype output
|
||||
print16_newline:
|
||||
push ax
|
||||
|
||||
mov ah, 0x0e ; bios teletype output
|
||||
mov al, 0x0a ; newline char
|
||||
int 0x10
|
||||
mov al, 0x0d ; carriage return
|
||||
int 0x10
|
||||
|
||||
pop ax
|
||||
ret
|
||||
|
||||
; Prints a null-terminated string beginning in the addr in bx
|
||||
print16_string:
|
||||
pusha
|
||||
|
||||
print16_string_char_loop:
|
||||
mov al, [bx] ; move current value of bx into lower-half of ax
|
||||
cmp al, 0 ; check if char is null
|
||||
je g_return ; if so, end of string, so return
|
||||
|
||||
; otherwise, print the character
|
||||
mov ah, 0x0e ; bios teletype output
|
||||
int 0x10
|
||||
|
||||
; Now, advance mem addr of bx by 1 to point to next char
|
||||
add bx, 1
|
||||
jmp print16_string_char_loop
|
||||
|
||||
; Calls print16_string then print16_newline
|
||||
print16:
|
||||
call print16_string
|
||||
call print16_newline
|
||||
ret
|
||||
|
||||
; Template for printing hex strings
|
||||
i_print16_hex_template: db '0x0000', 0
|
||||
|
||||
; Prints the value of dx register as a hex-string
|
||||
print16_hex_string:
|
||||
pusha
|
||||
mov cx, 0 ; init looping variable
|
||||
|
||||
print16_hex_string_loop:
|
||||
cmp cx, 4 ; check if looping is == 4
|
||||
je print16_hex_string_end ; if so, break the loop
|
||||
|
||||
mov ax, dx ; copy the hex value to a mutable register
|
||||
and ax, 0x000f ; mask off all but the last bit
|
||||
add al, 0x30 ; convert the number to ASCII 0-9
|
||||
cmp al, 0x39 ; check if number is 0-9 or a-f
|
||||
jle print16_hex_string_update_template ; if 0-9, no further adding needed
|
||||
add al, 39 ; otherwise, a-f so offset to proper ASCII range
|
||||
|
||||
print16_hex_string_update_template:
|
||||
mov bx, i_print16_hex_template + 5 ; end of ascii template
|
||||
sub bx, cx ; make bx point to currently-updated char
|
||||
mov [bx], al ; copy ASCII char we computed above into current position of bx
|
||||
shr dx, 4 ; shift dx over 1 byte so we can compute next char
|
||||
|
||||
add cx, 1 ; increment looping var
|
||||
jmp print16_hex_string_loop
|
||||
|
||||
print16_hex_string_end:
|
||||
mov bx, i_print16_hex_template ; copy the filled-out template addr to bx
|
||||
call print16_string ; print it out
|
||||
jmp g_return
|
||||
|
||||
; Calls print16_hex_string then print16_newline
|
||||
print16_hex:
|
||||
call print16_hex_string
|
||||
call print16_newline
|
||||
ret
|
80
asm/boot/print32.asm
Normal file
80
asm/boot/print32.asm
Normal file
@ -0,0 +1,80 @@
|
||||
[bits 32]
|
||||
|
||||
; 32-bit protected print helpers
|
||||
; VGA is 80 x 25 characters starting at 0xb8000
|
||||
; 1st byte is ASCII code of char to display, 2nd byte is display info
|
||||
; Address of particular row/col as: 0xb8000 + 2 * (row * 80 + col)
|
||||
|
||||
; Constants
|
||||
i_video_memory equ 0xb8000
|
||||
i_white_on_black equ 0x0f
|
||||
|
||||
; Variables
|
||||
i_print32_row_start equ 0xb8000
|
||||
i_print32_row_current equ 0xb8000
|
||||
i_print32_row_end equ 0xb8002 + 160
|
||||
|
||||
print32_raw_simple:
|
||||
; prints null-terminated string pointed to by edx
|
||||
pusha
|
||||
mov edx, i_video_memory ; set edx to start of the row
|
||||
|
||||
print32_raw_simple_loop:
|
||||
mov al, [ebx] ; store char at ebx in al
|
||||
mov ah, i_white_on_black ; store display info in ah
|
||||
|
||||
cmp al, 0 ; if al is null, then this is the end of the string
|
||||
je g_return
|
||||
|
||||
mov [edx], ax ; store char and attributes at current char cell
|
||||
|
||||
add ebx, 1 ; increment ebx to next char in string
|
||||
add edx, 2 ; move to next char cell in video memory
|
||||
|
||||
jmp print32_raw_simple_loop
|
||||
|
||||
|
||||
print32_newline: ; TODO fix this...
|
||||
pusha
|
||||
mov eax, i_print32_row_start
|
||||
mov ebx, i_print32_row_current
|
||||
mov ecx, i_print32_row_end
|
||||
add eax, 0xa0
|
||||
add ebx, 0xa0
|
||||
mov ecx, eax
|
||||
jmp g_return
|
||||
|
||||
|
||||
print32_raw:
|
||||
; prints null-terminated string pointed to by edx
|
||||
pusha
|
||||
mov edx, i_print32_row_current ; set edx to start of the row
|
||||
|
||||
jmp print32_raw_loop
|
||||
print32_raw_next_line:
|
||||
call print32_newline
|
||||
|
||||
print32_raw_loop:
|
||||
cmp edx, i_print32_row_end
|
||||
je print32_raw_next_line
|
||||
|
||||
mov al, [ebx] ; store char at ebx in al
|
||||
mov ah, i_white_on_black ; store display info in ah
|
||||
|
||||
cmp al, 0 ; if al is null, then this is the end of the string
|
||||
je g_return
|
||||
|
||||
mov [edx], ax ; store char and attributes at current char cell
|
||||
|
||||
add ebx, 1 ; increment ebx to next char in string
|
||||
add edx, 2 ; move to next char cell in video memory
|
||||
|
||||
jmp print32_raw_loop
|
||||
|
||||
|
||||
print32:
|
||||
call print32_raw
|
||||
call print32_newline
|
||||
ret
|
||||
|
||||
[bits 16]
|
30
asm/boot/switch16.asm
Normal file
30
asm/boot/switch16.asm
Normal file
@ -0,0 +1,30 @@
|
||||
[bits 16]
|
||||
|
||||
switch16_to_32:
|
||||
cli ; disable interrupts until we finish setup, since the
|
||||
; 16-bit interrupt vector will be invalid
|
||||
|
||||
lgdt [gdt_flat_descriptor] ; load the GDT which defines the flat code and data segments
|
||||
|
||||
mov eax, cr0 ; move the control register to eax so we can modify it
|
||||
or eax, 0x1 ; set the 1st bit to enable 32-bit pm
|
||||
mov cr0, eax ; write the modified control value back
|
||||
|
||||
jmp i_gdt_code_segment:initialize32 ; far-jump to our new 32-bit code, forcing CPU to clear the pipeline of 16-bit instructions
|
||||
|
||||
|
||||
[bits 32]
|
||||
|
||||
initialize32:
|
||||
; Set up registers and stack once in 32-bit protected mode
|
||||
mov ax, i_gdt_data_segment ; point the segment registers to the new data segment in 32-bit
|
||||
mov ds, ax
|
||||
mov ss, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
mov ebp, 0x90000 ; update stack position to be at the top of free space
|
||||
mov esp, ebp
|
||||
|
||||
call main32
|
8
src/kernel.cpp
Normal file
8
src/kernel.cpp
Normal file
@ -0,0 +1,8 @@
|
||||
// Called by boot.asm
|
||||
void main() {
|
||||
// Create pointer to a char and point to the first text cell of VGA
|
||||
char* video_memory = (char*) 0xb8000;
|
||||
|
||||
// Write the char X to the video memory to display it on the top-left of screen
|
||||
*video_memory = 'X';
|
||||
}
|
Loading…
Reference in New Issue
Block a user