Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
dom96 committed Jun 30, 2013
0 parents commit 3d3857c
Show file tree
Hide file tree
Showing 9 changed files with 406 additions and 0 deletions.
77 changes: 77 additions & 0 deletions boot.s
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
80 changes: 80 additions & 0 deletions ioutils.nim
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)
24 changes: 24 additions & 0 deletions license.txt
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 ]
44 changes: 44 additions & 0 deletions linker.ld
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. */
}
20 changes: 20 additions & 0 deletions main.nim
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))

14 changes: 14 additions & 0 deletions main.nimrod.cfg
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"
28 changes: 28 additions & 0 deletions nakefile.nim
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"
17 changes: 17 additions & 0 deletions panicoverride.nim
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.}
Loading

0 comments on commit 3d3857c

Please sign in to comment.