First content commit. Added sample code hello world with personal
comments for study Also made a makefile that can build and run the thing. Current run method is directly from QEMU - but the makefile will later make an image with a given bootloader.
This commit is contained in:
parent
4a25828031
commit
aa4f5f070b
4 changed files with 194 additions and 0 deletions
42
Makefile
Normal file
42
Makefile
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# Makefile
|
||||||
|
|
||||||
|
#Some definitions
|
||||||
|
SRC_DIR = src
|
||||||
|
BUILD_DIR = build
|
||||||
|
|
||||||
|
KERNEL_MAIN = $(SRC_DIR)/main.c
|
||||||
|
KERNEL_MAIN_OBJ = $(BUILD_DIR)/main.o
|
||||||
|
|
||||||
|
ENTRY_ASM = $(SRC_DIR)/start.s
|
||||||
|
ENTRY_ASM_OBJ = $(BUILD_DIR)/start.o
|
||||||
|
|
||||||
|
LINKING_RECIPE = $(SRC_DIR)/linker.ld
|
||||||
|
|
||||||
|
KERNEL_IMG = $(BUILD_DIR)/mykernel.elf
|
||||||
|
|
||||||
|
QEMU = qemu-system-i386
|
||||||
|
GCC = i686-elf-gcc
|
||||||
|
|
||||||
|
|
||||||
|
#Actual recipe
|
||||||
|
all: $(KERNEL_IMG)
|
||||||
|
|
||||||
|
$(BUILD_DIR):
|
||||||
|
mkdir -p $(BUILD_DIR)
|
||||||
|
|
||||||
|
$(KERNEL_MAIN_OBJ):
|
||||||
|
$(GCC) -std=gnu99 -ffreestanding -g -c $(KERNEL_MAIN) -o $(BUILD_DIR)/main.o
|
||||||
|
|
||||||
|
$(ENTRY_ASM_OBJ):
|
||||||
|
$(GCC) -std=gnu99 -ffreestanding -g -c $(ENTRY_ASM) -o $(ENTRY_ASM_OBJ)
|
||||||
|
|
||||||
|
#now kith (link)
|
||||||
|
$(KERNEL_IMG): $(KERNEL_MAIN_OBJ) $(ENTRY_ASM_OBJ)
|
||||||
|
$(GCC) -ffreestanding -nostdlib -g -T $(LINKING_RECIPE) $(ENTRY_ASM_OBJ) $(KERNEL_MAIN_OBJ) -o $(KERNEL_IMG) -lgcc
|
||||||
|
|
||||||
|
run: all
|
||||||
|
$(QEMU) -kernel $(KERNEL_IMG)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(BUILD_DIR)/*
|
||||||
|
|
||||||
37
src/linker.ld
Normal file
37
src/linker.ld
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*We have to write out this linker script so the linker knows where to put all the soup of stuff we put in start.s and main.c. i'll write out reasoning as i go. */
|
||||||
|
/*letting em know our entry point is actually label "start" in start.s, and not some function in the c file.*/
|
||||||
|
ENTRY(start)
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/*start me at 1Mb because below that is x86 essential stuff, which we dont want to be written on top of.*/
|
||||||
|
. = 2M;
|
||||||
|
|
||||||
|
/* we're going to maintain 4K alignment - apparently useful for paging, and i'm not complaining about the lost space anyway. */
|
||||||
|
|
||||||
|
/*our multiboot header from start.s - has to go at the beginning of the executable so the bootloader knows we're loadable.*/
|
||||||
|
/*executable section*/
|
||||||
|
.text BLOCK(4K) : ALIGN(4K)
|
||||||
|
{
|
||||||
|
*(.multiboot)
|
||||||
|
*(.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*general read only data*/
|
||||||
|
.rodata BLOCK(4K) : ALIGN(4K)
|
||||||
|
{
|
||||||
|
*(.rodata*)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*initialized rw data. */
|
||||||
|
.data BLOCK(4K) : ALIGN(4K)
|
||||||
|
{
|
||||||
|
*(.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*uninitialized data, and our stack as defined in start.s*/
|
||||||
|
.bss BLOCK(4K) : ALIGN(4K)
|
||||||
|
{
|
||||||
|
*(COMMON)
|
||||||
|
*(.bss)
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/main.c
Normal file
67
src/main.c
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
// Reworked sample code from OSDev! Stripped down some of the extra stuff to leave as an exercise.
|
||||||
|
//Headers provided by GCC :). No need for libs! We don't even have those yet.
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
//VGA buffer is the easiest way to write to the screen at this stage. it's just a massive array with two datapoints: what color, and what character?
|
||||||
|
//array starts at the absolute address 0xB8000
|
||||||
|
volatile uint16_t* vga_buffer = (uint16_t*)0xB8000;
|
||||||
|
//the VGA buffer is 80x25 to represent the screen.
|
||||||
|
const int GRID_COLS = 80;
|
||||||
|
const int GRID_ROWS = 25;
|
||||||
|
|
||||||
|
|
||||||
|
//grid is top left origin. This is our cursor!
|
||||||
|
int cursor_col = 0;
|
||||||
|
int cursor_row = 0;
|
||||||
|
uint16_t term_color = 0x0F00; // Black background, White foreground
|
||||||
|
|
||||||
|
|
||||||
|
//wipe the screen
|
||||||
|
void term_clear() {
|
||||||
|
for (int col = 0; col < GRID_COLS; col ++) {
|
||||||
|
for (int row = 0; row < GRID_ROWS; row ++) {
|
||||||
|
//works out to iterating every cell
|
||||||
|
const size_t index = (GRID_COLS * row) + col;
|
||||||
|
//vga buffer looks something like xxxxyyyyzzzzzzzz
|
||||||
|
//x=bg color
|
||||||
|
//y=fg color
|
||||||
|
//c=character to use
|
||||||
|
//Therefore, to write, we just take our color data and tack on the character to the end.
|
||||||
|
vga_buffer[index] = term_color | ' '; //blank out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
void term_printc(char c)
|
||||||
|
{
|
||||||
|
const size_t index = (GRID_COLS * cursor_row) + cursor_col; //where am i puttin it
|
||||||
|
vga_buffer[index] = term_color | c; //put it there by putting color+char into that spot in the array
|
||||||
|
cursor_col++; //next time put it in the next spot.
|
||||||
|
}
|
||||||
|
|
||||||
|
//print a string!
|
||||||
|
void term_println(const char* out)
|
||||||
|
{
|
||||||
|
for (int i = 0; out[i] != '\0'; i++)
|
||||||
|
term_printc(out[i]);
|
||||||
|
//go to the next line for a println func.
|
||||||
|
cursor_col = 0;
|
||||||
|
cursor_row++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//finally, main.
|
||||||
|
void kern_main()
|
||||||
|
{
|
||||||
|
|
||||||
|
//wipe the screen
|
||||||
|
term_clear();
|
||||||
|
|
||||||
|
//IT IS TIME. TO PRINT.
|
||||||
|
term_println("hello my chungus world");
|
||||||
|
term_println(":100:");
|
||||||
|
}
|
||||||
|
|
||||||
48
src/start.s
Normal file
48
src/start.s
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
//C main function for our kernel
|
||||||
|
//See: kernel.c
|
||||||
|
.extern kern_main
|
||||||
|
|
||||||
|
//This will be our entrypoint function name - gotta initialize it now as global so the linker knows later.
|
||||||
|
.global start
|
||||||
|
|
||||||
|
|
||||||
|
//multiboot for GRUB to boot it. Ideally stage01_bootloader will be able to support the multiboot standard.
|
||||||
|
//regardless of who's doing it, we have to set the required stuff
|
||||||
|
.set MB_MAGIC, 0x1BADB002 // bytes that bootloader will use to find this place
|
||||||
|
.set MB_FLAGS, (1 << 0) | (1 << 1) // flags request the following from the bootloader: maintain page boundaries + provide a memory map
|
||||||
|
.set MB_CHECKSUM, (0 - (MB_MAGIC + MB_FLAGS)) // Fails if checksum doesn't pass. Kind of arbitrary, but required.
|
||||||
|
|
||||||
|
|
||||||
|
//Now we actually place the multiboot stuff into the resulting executable...
|
||||||
|
.section .multiboot
|
||||||
|
.align 4 // 4 byte alignment
|
||||||
|
.long MB_MAGIC
|
||||||
|
.long MB_FLAGS
|
||||||
|
.long MB_CHECKSUM
|
||||||
|
|
||||||
|
// Set up for C code. Practically the only requirement for C-generated assembly to work properly is alignment and the presence of a stack.
|
||||||
|
.section .bss
|
||||||
|
.align 16
|
||||||
|
stack_bottom:
|
||||||
|
.skip 4096 // 4096 bytes (4kb) large stack. by skipping some amount of data (and eventually filling it with zeroes?), we've essentially just reserved space for our stack.
|
||||||
|
//Remember, stack grows DOWNWARD! So the last thing in the section -> the highest memory address -> the very first thing on the stack!
|
||||||
|
//Therefore, we put a label here to represent the top of our stack for later.
|
||||||
|
stack_top:
|
||||||
|
|
||||||
|
|
||||||
|
//Actual code. Entry point goes here!
|
||||||
|
.section .text
|
||||||
|
//Here it is!
|
||||||
|
start:
|
||||||
|
//Lets set up the stack. Stack grows downward on x86. We did the work earlier of defining where the top of the stack is, so just tell esp.
|
||||||
|
mov $stack_top, %esp //set the stack pointer
|
||||||
|
|
||||||
|
//To C-land!
|
||||||
|
call kern_main
|
||||||
|
|
||||||
|
//You should never get here, but in case you do, we will just hang.
|
||||||
|
hang:
|
||||||
|
cli //Interrupts: off
|
||||||
|
hlt //Halt!
|
||||||
|
jmp hang //just in case...
|
||||||
|
|
||||||
Loading…
Reference in a new issue