-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 3d3857c
Showing
9 changed files
with
406 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# Declare constants used for creating a multiboot header. | ||
.set ALIGN, 1<<0 # align loaded modules on page boundaries | ||
.set MEMINFO, 1<<1 # provide memory map | ||
.set FLAGS, ALIGN | MEMINFO # this is the Multiboot 'flag' field | ||
.set MAGIC, 0x1BADB002 # 'magic number' lets bootloader find the header | ||
.set CHECKSUM, -(MAGIC + FLAGS) # checksum of above, to prove we are multiboot | ||
|
||
# Declare a header as in the Multiboot Standard. We put this into a special | ||
# section so we can force the header to be in the start of the final program. | ||
# You don't need to understand all these details as it is just magic values that | ||
# is documented in the multiboot standard. The bootloader will search for this | ||
# magic sequence and recognize us as a multiboot kernel. | ||
.section .multiboot | ||
.align 4 | ||
.long MAGIC | ||
.long FLAGS | ||
.long CHECKSUM | ||
|
||
# Currently the stack pointer register (esp) points at anything and using it may | ||
# cause massive harm. Instead, we'll provide our own stack. We will allocate | ||
# room for a small temporary stack by creating a symbol at the bottom of it, | ||
# then allocating 16384 bytes for it, and finally creating a symbol at the top. | ||
.section .bootstrap_stack | ||
stack_bottom: | ||
.skip 16384 # 16 KiB | ||
stack_top: | ||
|
||
# The linker script specifies _start as the entry point to the kernel and the | ||
# bootloader will jump to this position once the kernel has been loaded. It | ||
# doesn't make sense to return from this function as the bootloader is gone. | ||
.section .text | ||
.global _start | ||
.type _start, @function | ||
_start: | ||
# Welcome to kernel mode! We now have sufficient code for the bootloader to | ||
# load and run our operating system. It doesn't do anything interesting yet. | ||
# Perhaps we would like to call printf("Hello, World\n"). You should now | ||
# realize one of the profound truths about kernel mode: There is nothing | ||
# there unless you provide it yourself. There is no printf function. There | ||
# is no <stdio.h> header. If you want a function, you will have to code it | ||
# yourself. And that is one of the best things about kernel development: | ||
# you get to make the entire system yourself. You have absolute and complete | ||
# power over the machine, there are no security restrictions, no safe | ||
# guards, no debugging mechanisms, there is nothing but what you build. | ||
|
||
# By now, you are perhaps tired of assembly language. You realize some | ||
# things simply cannot be done in C, such as making the multiboot header in | ||
# the right section and setting up the stack. However, you would like to | ||
# write the operating system in a higher level language, such as C or C++. | ||
# To that end, the next task is preparing the processor for execution of | ||
# such code. C doesn't expect much at this point and we only need to set up | ||
# a stack. Note that the processor is not fully initialized yet and stuff | ||
# such as floating point instructions are not available yet. | ||
|
||
# To set up a stack, we simply set the esp register to point to the top of | ||
# our stack (as it grows downwards). | ||
movl $stack_top, %esp | ||
|
||
# We are now ready to actually execute C code. We cannot embed that in an | ||
# assembly file, so we'll create a kernel.c file in a moment. In that file, | ||
# we'll create a C entry point called kernel_main and call it here. | ||
call kmain | ||
|
||
# In case the function returns, we'll want to put the computer into an | ||
# infinite loop. To do that, we use the clear interrupt ('cli') instruction | ||
# to disable interrupts, the halt instruction ('hlt') to stop the CPU until | ||
# the next interrupt arrives, and jumping to the halt instruction if it ever | ||
# continues execution, just to be safe. We will create a local label rather | ||
# than real symbol and jump to there endlessly. | ||
cli | ||
hlt | ||
.Lhang: | ||
jmp .Lhang | ||
|
||
# Set the size of the _start symbol to the current location '.' minus its start. | ||
# This is useful when debugging or when you implement call tracing. | ||
.size _start, . - _start |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import unsigned | ||
type | ||
PVIDMem* = ptr array[0..65_000, TEntry] | ||
|
||
TVGAColor* = enum | ||
Black = 0, | ||
Blue = 1, | ||
Green = 2, | ||
Cyan = 3, | ||
Red = 4, | ||
Magenta = 5, | ||
Brown = 6, | ||
LightGrey = 7, | ||
DarkGrey = 8, | ||
LightBlue = 9, | ||
LightGreen = 10, | ||
LightCyan = 11, | ||
LightRed = 12, | ||
LightMagenta = 13, | ||
Yellow = 14, | ||
White = 15 | ||
|
||
TPos* = tuple[x: int, y: int] | ||
|
||
TAttribute* = distinct uint8 | ||
TEntry* = distinct uint16 | ||
|
||
const | ||
VGAWidth* = 80 | ||
VGAHeight* = 25 | ||
|
||
proc makeColor*(fg: TVGAColor, bg: TVGAColor): TAttribute = | ||
## Combines a foreground and background color into a ``TAttribute``. | ||
return (ord(bg).uint8 or (ord(fg).uint8 shl 4)).TAttribute | ||
|
||
proc makeEntry*(c: char, color: TAttribute): TEntry = | ||
## Combines a char and a *TAttribute* into a format which can be | ||
## directly written to the Video memory. | ||
let c16 = ord(c).uint16 | ||
let color16 = color.uint16 | ||
return (c16 or (color16 shl 8)).TEntry | ||
|
||
proc writeChar*(vram: PVidMem, entry: TEntry, pos: TPos) = | ||
## Writes a character at the specified ``pos``. | ||
let index = (80 * pos.y) + pos.x | ||
vram[index] = entry | ||
|
||
proc rainbow*(vram: PVidMem, text: string, pos: TPos) = | ||
## Writes a string at the specified ``pos`` with varying colors which, despite | ||
## the name of this function, do not resemble a rainbow. | ||
var colorBG = Blue | ||
var colorFG = DarkGrey | ||
proc nextColor(color: TVGAColor, skip: set[TVGAColor]): TVGAColor = | ||
if color == White: | ||
result = Black | ||
else: | ||
result = (ord(color) + 1).TVGAColor | ||
if result in skip: result = nextColor(result, skip) | ||
|
||
for i in 0 .. text.len-1: | ||
colorBG = nextColor(colorBG, {Black, Cyan, DarkGrey, Magenta, Red, | ||
Blue, LightBlue, LightMagenta}) | ||
let attr = makeColor(colorFG, colorBG) | ||
|
||
vram.writeChar(makeEntry(text[i], attr), (pos.x+i, pos.y)) | ||
|
||
proc writeString*(vram: PVidMem, text: string, color: TAttribute, pos: TPos) = | ||
## Writes a string at the specified ``pos`` with the specified ``color``. | ||
for i in 0 .. text.len-1: | ||
vram.writeChar(makeEntry(text[i], color), (pos.x+i, pos.y)) | ||
|
||
proc screenClear*(video_mem: PVidMem, color: TVGAColor) = | ||
## Clears the screen with a specified ``color``. | ||
let attr = makeColor(color, color) | ||
let space = makeEntry(' ', attr) | ||
|
||
var i = 0 | ||
while i <=% VGAWidth*VGAHeight: | ||
video_mem[i] = space | ||
inc(i) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
=============================================================================== | ||
Nimkernel -- A small kernel written in Nimrod. | ||
|
||
Copyright (C) 2013 Dominik Picheta. All rights reserved. | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. | ||
|
||
[ MIT license: http://www.opensource.org/licenses/mit-license.php ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* The bootloader will look at this image and start execution at the symbol | ||
designated as the entry point. */ | ||
ENTRY(_start) | ||
|
||
/* Tell where the various sections of the object files will be put in the final | ||
kernel image. */ | ||
SECTIONS | ||
{ | ||
/* Begin putting sections at 1 MiB, a conventional place for kernels to be | ||
loaded at by the bootloader. */ | ||
. = 1M; | ||
|
||
/* First put the multiboot header, as it is required to be put very early | ||
early in the image or the bootloader won't recognize the file format. | ||
Next we'll put the .text section. */ | ||
.text BLOCK(4K) : ALIGN(4K) | ||
{ | ||
*(.multiboot) | ||
*(.text) | ||
} | ||
|
||
/* Read-only data. */ | ||
.rodata BLOCK(4K) : ALIGN(4K) | ||
{ | ||
*(.rodata) | ||
} | ||
|
||
/* Read-write data (initialized) */ | ||
.data BLOCK(4K) : ALIGN(4K) | ||
{ | ||
*(.data) | ||
} | ||
|
||
/* Read-write data (uninitialized) and stack */ | ||
.bss BLOCK(4K) : ALIGN(4K) | ||
{ | ||
*(COMMON) | ||
*(.bss) | ||
*(.bootstrap_stack) | ||
} | ||
|
||
/* The compiler may produce other sections, by default it will put them in | ||
a segment with the same name. Simply add stuff here as needed. */ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import ioutils | ||
type | ||
TMultiboot_header{.pure, final.} = object | ||
PMultiboot_header = ptr TMultiboot_header | ||
|
||
proc kmain(mb_header: PMultiboot_header, magic: int) {.exportc.} = | ||
if magic != 0x2BADB002: | ||
# Something went wrong? | ||
|
||
var vram = cast[PVIDMem](0xB8000) | ||
screenClear(vram, Yellow) # Make the screen yellow. | ||
|
||
# Demonstration of error handling. | ||
var outOfBounds = vram[len(vram[])] | ||
|
||
let attr = makeColor(Yellow, DarkGrey) | ||
writeString(vram, "Nimrod", attr, (25, 9)) | ||
writeString(vram, "Expressive. Efficient. Elegant.", attr, (25, 10)) | ||
rainbow(vram, "It's pure pleasure.", (x: 25, y: 11)) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
|
||
--cpu:i386 | ||
--boundChecks:on | ||
--passc:"-w -I$lib -ffreestanding -O2 -Wall -Wextra" | ||
|
||
--noLinking | ||
|
||
--os:standalone | ||
|
||
--deadCodeElim:on | ||
--gc:none | ||
-d:useMalloc | ||
--noMain | ||
--parallelBuild:"1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import nake | ||
import os | ||
|
||
const | ||
CC = "i586-elf-gcc" | ||
asmC = "i586-elf-as" | ||
|
||
task "clean", "Removes build files.": | ||
removeFile("boot.o") | ||
removeFile("main.bin") | ||
removeDir("nimcache") | ||
echo "Done." | ||
|
||
task "build", "Builds the operating system.": | ||
echo "Compiling..." | ||
direShell "nimrod c -d:release --gcc.exe:$1 main.nim" % CC | ||
|
||
direShell asmC, "boot.s -o boot.o" | ||
|
||
echo "Linking..." | ||
|
||
direShell CC, "-T linker.ld -o main.bin -ffreestanding -O2 -nostdlib boot.o nimcache/main.o nimcache/system.o nimcache/unsigned.o nimcache/ioutils.o" | ||
|
||
echo "Done." | ||
|
||
task "run", "Runs the operating system using QEMU.": | ||
if not existsFile("main.bin"): runTask("build") | ||
direShell "qemu-system-i386 -kernel main.bin" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import ioutils | ||
{.push stack_trace: off, profiler:off.} | ||
|
||
proc rawoutput(s: string) = | ||
var vram = cast[PVIDMem](0xB8000) | ||
writeString(vram, "Error: ", makeColor(White, Red), (0, 24)) | ||
writeString(vram, s, makeColor(White, Red), (7, 24)) | ||
|
||
proc panic(s: string) = | ||
rawoutput(s) | ||
|
||
# Alternatively we also could implement these 2 here: | ||
# | ||
# template sysFatal(exceptn: typeDesc, message: string) | ||
# template sysFatal(exceptn: typeDesc, message, arg: string) | ||
|
||
{.pop.} |
Oops, something went wrong.