initial release.
This commit is contained in:
commit
ad1a74fb85
27 changed files with 2058 additions and 0 deletions
37
.gitignore
vendored
Normal file
37
.gitignore
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Gitignore
|
||||
|
||||
obj/*
|
||||
kernel
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
22
LICENSE
Normal file
22
LICENSE
Normal file
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Felix Queißner
|
||||
|
||||
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.
|
||||
|
29
Makefile
Normal file
29
Makefile
Normal file
|
@ -0,0 +1,29 @@
|
|||
SRCS = $(shell find -name '*.[cS]')
|
||||
OBJS = $(addsuffix .o,$(basename $(SRCS)))
|
||||
|
||||
CC = gcc
|
||||
LD = ld
|
||||
|
||||
# define nullptr, but not NULL.
|
||||
CFLAGS = -m32 -Dnullptr=0
|
||||
ASFLAGS =
|
||||
CCFLAGS = -g -std=c11 -Wall -g -fno-stack-protector -ffreestanding
|
||||
LDFLAGS = -g -melf_i386 -Tkernel.ld
|
||||
|
||||
kernel: $(OBJS)
|
||||
$(LD) $(LDFLAGS) -o $@ $(addprefix obj/, $(notdir $^))
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(CCFLAGS) -c -o $(addprefix obj/, $(notdir $@)) $^
|
||||
|
||||
%.o: %.S
|
||||
$(CC) $(CFLAGS) $(ASFLAGS) -c -o $(addprefix obj/, $(notdir $@)) $^
|
||||
|
||||
clean:
|
||||
rm $(addprefix obj/, $(notdir $(OBJS)))
|
||||
|
||||
run:
|
||||
qemu-system-i386 -kernel kernel
|
||||
|
||||
.PHONY: clean
|
||||
.PHONY: run
|
2
README.md
Normal file
2
README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
# KernelTemplate
|
||||
A pretty primitive kernel base with physical and virtual memory management and a console.
|
88
asm/intr_common_handler.S
Normal file
88
asm/intr_common_handler.S
Normal file
|
@ -0,0 +1,88 @@
|
|||
.macro intr_stub nr
|
||||
.global intr_stub_\nr
|
||||
intr_stub_\nr:
|
||||
pushl $0
|
||||
pushl $\nr
|
||||
jmp intr_common_handler
|
||||
.endm
|
||||
|
||||
.macro intr_stub_error_code nr
|
||||
.global intr_stub_\nr
|
||||
intr_stub_\nr:
|
||||
pushl $\nr
|
||||
jmp intr_common_handler
|
||||
.endm
|
||||
|
||||
// Exceptions
|
||||
intr_stub 0
|
||||
intr_stub 1
|
||||
intr_stub 2
|
||||
intr_stub 3
|
||||
intr_stub 4
|
||||
intr_stub 5
|
||||
intr_stub 6
|
||||
intr_stub 7
|
||||
intr_stub_error_code 8
|
||||
intr_stub 9
|
||||
intr_stub_error_code 10
|
||||
intr_stub_error_code 11
|
||||
intr_stub_error_code 12
|
||||
intr_stub_error_code 13
|
||||
intr_stub_error_code 14
|
||||
intr_stub 15
|
||||
intr_stub 16
|
||||
intr_stub_error_code 17
|
||||
intr_stub 18
|
||||
|
||||
// IRQs
|
||||
intr_stub 32
|
||||
intr_stub 33
|
||||
intr_stub 34
|
||||
intr_stub 35
|
||||
intr_stub 36
|
||||
intr_stub 37
|
||||
intr_stub 38
|
||||
intr_stub 39
|
||||
intr_stub 40
|
||||
intr_stub 41
|
||||
intr_stub 42
|
||||
intr_stub 43
|
||||
intr_stub 44
|
||||
intr_stub 45
|
||||
intr_stub 46
|
||||
intr_stub 47
|
||||
|
||||
// System call
|
||||
intr_stub 48
|
||||
|
||||
intr_stub 80
|
||||
|
||||
.extern intr_routine
|
||||
intr_common_handler:
|
||||
// Save additional cpu state
|
||||
push %ebp
|
||||
push %edi
|
||||
push %esi
|
||||
push %edx
|
||||
push %ecx
|
||||
push %ebx
|
||||
push %eax
|
||||
|
||||
// Call interrupt handler
|
||||
push %esp
|
||||
call intr_routine
|
||||
add $4, %esp
|
||||
|
||||
// Restore cpu state
|
||||
pop %eax
|
||||
pop %ebx
|
||||
pop %ecx
|
||||
pop %edx
|
||||
pop %esi
|
||||
pop %edi
|
||||
pop %ebp
|
||||
|
||||
// Fehlercode und Interruptnummer vom Stack nehmen
|
||||
add $8, %esp
|
||||
|
||||
iret
|
9
asm/multiboot.S
Normal file
9
asm/multiboot.S
Normal file
|
@ -0,0 +1,9 @@
|
|||
.section multiboot
|
||||
#define MB_MAGIC 0x1badb002
|
||||
#define MB_FLAGS 0x0
|
||||
#define MB_CHECKSUM -(MB_MAGIC + MB_FLAGS)
|
||||
|
||||
.align 4
|
||||
.int MB_MAGIC
|
||||
.int MB_FLAGS
|
||||
.int MB_CHECKSUM
|
25
asm/start.S
Normal file
25
asm/start.S
Normal file
|
@ -0,0 +1,25 @@
|
|||
.section .text
|
||||
|
||||
// C-Einstiegspunkt
|
||||
.extern init
|
||||
|
||||
// .global für Sichtbarkeit im Linker (invers zu static aus C)
|
||||
.global _start
|
||||
_start:
|
||||
// Init stack
|
||||
mov $kernel_stack, %esp
|
||||
|
||||
// Start Kernel
|
||||
push %ebx
|
||||
call init
|
||||
|
||||
// Halte an, no matter what
|
||||
_stop:
|
||||
cli
|
||||
hlt
|
||||
jmp _stop
|
||||
|
||||
// 16 KB Stackspace
|
||||
.section .bss
|
||||
.space 16384
|
||||
kernel_stack:
|
39
kernel.ld
Normal file
39
kernel.ld
Normal file
|
@ -0,0 +1,39 @@
|
|||
/* Bei _start soll die Ausfuehrung losgehen */
|
||||
ENTRY(_start)
|
||||
|
||||
/*
|
||||
* Hier wird festgelegt, in welcher Reihenfolge welche Sektionen in die Binary
|
||||
* geschrieben werden sollen
|
||||
*/
|
||||
SECTIONS
|
||||
{
|
||||
/*
|
||||
* Startpunkt des Kernels
|
||||
*/
|
||||
. = 0x100000;
|
||||
|
||||
/* Kernel Start Punkt */
|
||||
kernelStart = .;
|
||||
|
||||
/*
|
||||
* Two text sections: .text and .multiboot
|
||||
* Multiboot must be at the start of the kernel
|
||||
*/
|
||||
.text : {
|
||||
*(multiboot)
|
||||
*(.text)
|
||||
}
|
||||
.data ALIGN(4096) : {
|
||||
*(.data)
|
||||
}
|
||||
.rodata ALIGN(4096) : {
|
||||
*(.rodata)
|
||||
}
|
||||
.bss ALIGN(4096) : {
|
||||
*(.bss)
|
||||
}
|
||||
|
||||
/* Align the end of the kernel to the page size */
|
||||
. = ALIGN(4096);
|
||||
kernelEnd = .;
|
||||
}
|
222
src/console.c
Normal file
222
src/console.c
Normal file
|
@ -0,0 +1,222 @@
|
|||
#include "console.h"
|
||||
#include "stdlib.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
csDefault = 0,
|
||||
csSetColor,
|
||||
csSetForeground,
|
||||
csSetBackground,
|
||||
} ConsoleState;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int foreground:4;
|
||||
int background:4;
|
||||
} ConsoleColor;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char c;
|
||||
uint8_t color;
|
||||
} ConsoleChar;
|
||||
|
||||
static int cursorX = 0;
|
||||
static int cursorY = 0;
|
||||
static ConsoleColor color = { COLOR_LIGHTGRAY, COLOR_BLACK };
|
||||
static ConsoleState state = csDefault;
|
||||
|
||||
static ConsoleChar *screen = (ConsoleChar*)0xB8000;
|
||||
|
||||
void ksetpos(int x, int y)
|
||||
{
|
||||
if((x >= 0) && (x < CONSOLE_WIDTH))
|
||||
{
|
||||
cursorX = x;
|
||||
}
|
||||
|
||||
if((y >= 0) && (y < CONSOLE_HEIGHT))
|
||||
{
|
||||
cursorY = y;
|
||||
}
|
||||
}
|
||||
|
||||
void kgetpos(int *x, int *y)
|
||||
{
|
||||
if(x != nullptr) *x = cursorX;
|
||||
if(y != nullptr) *y = cursorY;
|
||||
}
|
||||
|
||||
|
||||
void ksetcolor(int background, int foreground)
|
||||
{
|
||||
if(background >= 0)
|
||||
{
|
||||
background &= 0xF;
|
||||
color.background = background;
|
||||
}
|
||||
if(foreground >= 0)
|
||||
{
|
||||
foreground &= 0xF;
|
||||
color.foreground = foreground;
|
||||
}
|
||||
}
|
||||
|
||||
void kgetcolor(int *background, int *foreground)
|
||||
{
|
||||
if(background != nullptr) *background = color.background;
|
||||
if(foreground != nullptr) *foreground = color.foreground;
|
||||
}
|
||||
|
||||
void kclear(void)
|
||||
{
|
||||
cursorX = 0;
|
||||
cursorY = 0;
|
||||
for(size_t i = 0; i < (CONSOLE_WIDTH * CONSOLE_HEIGHT); i++)
|
||||
{
|
||||
screen[i].c = ' '; // Fill screen with spaces
|
||||
screen[i].color = *((uint8_t*)&color); // Set screen to current color
|
||||
}
|
||||
}
|
||||
|
||||
static void newline()
|
||||
{
|
||||
cursorX = 0;
|
||||
cursorY += 1;
|
||||
if(cursorY == (CONSOLE_HEIGHT))
|
||||
{
|
||||
// We need to scroll here:
|
||||
|
||||
// Copy everything line on the screen to the previous line
|
||||
for(int line = 1; line < CONSOLE_HEIGHT; line++)
|
||||
{
|
||||
memcpy(&screen[CONSOLE_WIDTH * (line-1)], &screen[CONSOLE_WIDTH * line], CONSOLE_WIDTH * sizeof(ConsoleChar));
|
||||
}
|
||||
// Fill the last line with blanks
|
||||
for(int i = 0; i < CONSOLE_WIDTH; i++)
|
||||
{
|
||||
screen[CONSOLE_WIDTH * (CONSOLE_HEIGHT-1)+i].c = ' ';
|
||||
screen[CONSOLE_WIDTH * (CONSOLE_HEIGHT-1)+i].color = *((uint8_t*)&color);
|
||||
}
|
||||
// Reset y cursor position to previous line (we don't want to write out of bounds)
|
||||
cursorY -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void kputc(char c)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case csSetColor:
|
||||
{
|
||||
color = *((ConsoleColor*)&c);
|
||||
state = csDefault;
|
||||
return;
|
||||
}
|
||||
case csSetForeground:
|
||||
{
|
||||
color.foreground = c & 0xF;
|
||||
state = csDefault;
|
||||
return;
|
||||
}
|
||||
case csSetBackground:
|
||||
{
|
||||
color.background = c & 0xF;
|
||||
state = csDefault;
|
||||
return;
|
||||
}
|
||||
case csDefault:
|
||||
default:
|
||||
{
|
||||
int idx = CONSOLE_WIDTH * cursorY + cursorX;
|
||||
switch(c)
|
||||
{
|
||||
case '\x11':
|
||||
state = csSetColor;
|
||||
return;
|
||||
case '\x12':
|
||||
state = csSetForeground;
|
||||
return;
|
||||
case '\x13':
|
||||
state = csSetBackground;
|
||||
return;
|
||||
case '\r':
|
||||
// Ignore carriage return
|
||||
return;
|
||||
case '\n':
|
||||
newline();
|
||||
return;
|
||||
default:
|
||||
screen[idx].color = *((uint8_t*)&color); // Set screen to current color
|
||||
screen[idx].c = c;
|
||||
|
||||
cursorX += 1;
|
||||
if(cursorX >= CONSOLE_WIDTH)
|
||||
{
|
||||
newline();
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void kputs(const char *str)
|
||||
{
|
||||
while(*str)
|
||||
{
|
||||
kputc(*str);
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
void kprintf(const char *format, ...)
|
||||
{
|
||||
char buffer[32];
|
||||
va_list vl;
|
||||
va_start(vl, format);
|
||||
while(*format != 0)
|
||||
{
|
||||
char c = *(format++);
|
||||
if(c == '%')
|
||||
{
|
||||
c = *(format++);
|
||||
int i;
|
||||
char *str;
|
||||
switch(c)
|
||||
{
|
||||
case 'd':
|
||||
case 'i':
|
||||
i = va_arg(vl, int);
|
||||
kputs(itoa(i, buffer, 10));
|
||||
break;
|
||||
case 'b':
|
||||
i = va_arg(vl, int);
|
||||
kputs(itoa(i, buffer, 2));
|
||||
break;
|
||||
case 'x':
|
||||
case 'X':
|
||||
i = va_arg(vl, int);
|
||||
kputs(itoa(i, buffer, 16));
|
||||
break;
|
||||
case 'c':
|
||||
c = va_arg(vl, int);
|
||||
kputc(c);
|
||||
break;
|
||||
case 's':
|
||||
str = va_arg(vl, char*);
|
||||
kputs(str);
|
||||
break;
|
||||
default:
|
||||
kputc(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
kputc(c);
|
||||
}
|
||||
}
|
||||
va_end(vl);
|
||||
}
|
74
src/console.h
Normal file
74
src/console.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
#pragma once
|
||||
|
||||
#define CONSOLE_WIDTH 80
|
||||
#define CONSOLE_HEIGHT 25
|
||||
|
||||
#define COLOR_BLACK 0x0
|
||||
#define COLOR_BLUE 0x1
|
||||
#define COLOR_GREEN 0x2
|
||||
#define COLOR_CYAN 0x3
|
||||
#define COLOR_RED 0x4
|
||||
#define COLOR_MAGENTA 0x5
|
||||
#define COLOR_BROWN 0x6
|
||||
#define COLOR_LIGHTGRAY 0x7
|
||||
#define COLOR_GRAY 0x8
|
||||
#define COLOR_LIGHTBLUE 0x9
|
||||
#define COLOR_LIGHTGREEN 0xA
|
||||
#define COLOR_LIGHTRED 0xC
|
||||
#define COLOR_LIGHTMAGENTA 0xD
|
||||
#define COLOR_YELLOW 0xE
|
||||
#define COLOR_WHITE 0xF
|
||||
|
||||
/**
|
||||
* @brief Sets the cursor position.
|
||||
* @param x Distance of the cursor from the left border.
|
||||
* @param y Distance of the cursor from the top border.
|
||||
*/
|
||||
void ksetpos(int x, int y);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Gets the cursor position.
|
||||
* @param x Pointer to an integer that should store the distance of the cursor from the left border.
|
||||
* @param y Pointer to an integer that should store the distance of the cursor from the top border.
|
||||
*/
|
||||
void kgetpos(int *x, int *y);
|
||||
|
||||
/**
|
||||
* @brief Sets the color of the console.
|
||||
* @param foreground The foreground color.
|
||||
* @param background The background color.
|
||||
*/
|
||||
void ksetcolor(int background, int foreground);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Gets the color of the console.
|
||||
* @param foreground A pointer to the foreground color.
|
||||
* @param background A pointer to the background color.
|
||||
*/
|
||||
void kgetcolor(int *background, int *foreground);
|
||||
|
||||
/**
|
||||
* @brief Clears the screen and sets the cursor to the top left corner.
|
||||
*/
|
||||
void kclear(void);
|
||||
|
||||
/**
|
||||
* @brief Puts a character onto the screen.
|
||||
* @param c The character that should be printed.
|
||||
*/
|
||||
void kputc(char c);
|
||||
|
||||
/**
|
||||
* @brief Prints a string onto the screen.
|
||||
* @param A null-terminated string that should be printed.
|
||||
*/
|
||||
void kputs(const char *str);
|
||||
|
||||
/**
|
||||
* @brief Prints a formatted string onto the screen.
|
||||
* @param format The format string that will be printed in formatted version.
|
||||
* @param ... The format parameters that will be used to print the string.
|
||||
*/
|
||||
void kprintf(const char *format, ...);
|
24
src/cpustate.h
Normal file
24
src/cpustate.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
#include "inttypes.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// Saved by interrupt routine
|
||||
uint32_t eax;
|
||||
uint32_t ebx;
|
||||
uint32_t ecx;
|
||||
uint32_t edx;
|
||||
uint32_t esi;
|
||||
uint32_t edi;
|
||||
uint32_t ebp;
|
||||
|
||||
uint32_t intr;
|
||||
uint32_t error;
|
||||
|
||||
// Saved by CPU
|
||||
uint32_t eip;
|
||||
uint32_t cs;
|
||||
uint32_t eflags;
|
||||
uint32_t esp;
|
||||
uint32_t ss;
|
||||
} CpuState;
|
149
src/init.c
Normal file
149
src/init.c
Normal file
|
@ -0,0 +1,149 @@
|
|||
#include "kernel.h"
|
||||
#include "stdlib.h"
|
||||
#include "console.h"
|
||||
#include "interrupts.h"
|
||||
#include "pmm.h"
|
||||
#include "vmm.h"
|
||||
|
||||
void die(const char *msg)
|
||||
{
|
||||
kputs("\n");
|
||||
ksetcolor(COLOR_RED, COLOR_WHITE);
|
||||
kputs(msg);
|
||||
while(1)
|
||||
{
|
||||
__asm__ volatile ("cli; hlt;");
|
||||
}
|
||||
}
|
||||
|
||||
void ksleep(uint32_t time)
|
||||
{
|
||||
for(uint32_t i = 0; i < time; i++)
|
||||
{
|
||||
// BURN, CPU, BURN!
|
||||
for(volatile size_t i = 0; i < 40000000; i++);
|
||||
}
|
||||
}
|
||||
|
||||
static void debug_test()
|
||||
{
|
||||
char buffer[64];
|
||||
kputs("itoa test:\n");
|
||||
kputs("10 = "); kputs(itoa(10, buffer, 10)); kputc('\n');
|
||||
kputs("10 = 0x"); kputs(itoa(10, buffer, 16)); kputc('\n');
|
||||
kputs("10 = 0b"); kputs(itoa(10, buffer, 2)); kputc('\n');
|
||||
kputs("10 = 0o"); kputs(itoa(10, buffer, 8)); kputc('\n');
|
||||
|
||||
kputs("printf test:\n");
|
||||
kprintf("This %s %c test line.\n", "is", 'a');
|
||||
kprintf("Numbers: %d %i %x %b\n", 15, 15, 15, 15);
|
||||
|
||||
/*
|
||||
kputs("scroll-test:\n");
|
||||
for(int i = 0; i < 100; i++)
|
||||
{
|
||||
kprintf("They see me scrolling, they hating! %i\n", i);
|
||||
}
|
||||
for(int i = 0; i < 272; i++)
|
||||
{
|
||||
kprintf("x");
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
static void dumpMB(const MultibootStructure *mbHeader)
|
||||
{
|
||||
kputs("Multiboot Information:\n");
|
||||
if(mbHeader->flags & MB_MEMSIZE)
|
||||
{
|
||||
kprintf("Lower Memory: %d kB\n", mbHeader->memLower);
|
||||
kprintf("Upper Memory: %d kB\n", mbHeader->memUpper);
|
||||
}
|
||||
// TODO: MB_BOOTDEVICE
|
||||
if(mbHeader->flags & MB_COMMANDLINE)
|
||||
{
|
||||
kprintf("Commandline: %s\n", mbHeader->commandline);
|
||||
}
|
||||
if(mbHeader->flags & MB_MODULES)
|
||||
{
|
||||
const MultibootModule *mod = (const MultibootModule *)mbHeader->modules;
|
||||
for(size_t i = 0; i < mbHeader->moduleCount; i++)
|
||||
{
|
||||
kprintf("Module %s [%d - %d]\n", mod[i].name, mod[i].start, mod[i].end);
|
||||
}
|
||||
}
|
||||
if(mbHeader->flags & MB_SYMS_AOUT)
|
||||
{
|
||||
kputs("Kernel File Format: a.out\n");
|
||||
}
|
||||
if(mbHeader->flags & MB_SYMS_ELF)
|
||||
{
|
||||
kputs("Kernel File Format: ELF\n");
|
||||
}
|
||||
if(mbHeader->flags & MB_MEMORYMAP)
|
||||
{
|
||||
uintptr_t it = mbHeader->memoryMap;
|
||||
kprintf("Memory Map: %d entries\n", mbHeader->memoryMapLength);
|
||||
for(size_t i = 0; i < mbHeader->memoryMapLength; i++)
|
||||
{
|
||||
const MultibootMemoryMap *mmap = (const MultibootMemoryMap *)it;
|
||||
if(mmap->type == 1)
|
||||
kprintf("Memory Map: [%d + %d] %s\n", (uint32_t)mmap->base, (uint32_t)mmap->length, mmap->type == 1 ? "free" : "preserved");
|
||||
it += mmap->entry_size + 4; // Stupid offset :P
|
||||
}
|
||||
}
|
||||
// TODO: MB_DRIVES
|
||||
// TODO: MB_CONFIG_TABLE
|
||||
if(mbHeader->flags & MB_BOOTLOADER_NAME)
|
||||
{
|
||||
kprintf("Bootloader Name: %s\n", mbHeader->bootLoaderName);
|
||||
}
|
||||
// TODO: MB_APS_TABLE
|
||||
}
|
||||
|
||||
void init(const MultibootStructure *mbHeader)
|
||||
{
|
||||
(void)debug_test;
|
||||
(void)dumpMB;
|
||||
|
||||
ksetcolor(COLOR_BLACK, COLOR_LIGHTGRAY);
|
||||
kclear();
|
||||
kputs("Welcome to \x12\x05nucleo\x12\x07!\n");
|
||||
|
||||
//dumpMB(mbHeader);
|
||||
|
||||
kputs("Initialize physical memory management: ");
|
||||
pmm_init(mbHeader);
|
||||
kputs("success.\n");
|
||||
// uint32_t freeMem = pmm_calc_free();
|
||||
//kprintf("Free memory: %d B, %d kB, %d MB\n", freeMem, freeMem >> 10, freeMem >> 20);
|
||||
|
||||
ksleep(1);
|
||||
|
||||
kputs("Initialize virtual memory management: ");
|
||||
vmm_init();
|
||||
kputs("success.\n");
|
||||
|
||||
kputs("Initialize interrupts: ");
|
||||
intr_init();
|
||||
kputs("success.\n");
|
||||
|
||||
kputs("Enable hw interrupts: ");
|
||||
irq_enable();
|
||||
kputs("success.\n");
|
||||
|
||||
//__asm__ volatile("sti");
|
||||
|
||||
kputs("Prepare heap memory: ");
|
||||
for(uintptr_t ptr = 0x400000; ptr < 0x800000; ptr += 4096)
|
||||
{
|
||||
vmm_map(ptr, (uintptr_t)pmm_alloc(), VM_PROGRAM);
|
||||
}
|
||||
kputs("success.\n");
|
||||
|
||||
while(1)
|
||||
{
|
||||
kputs("x");
|
||||
ksleep(1);
|
||||
}
|
||||
}
|
253
src/interrupts.c
Normal file
253
src/interrupts.c
Normal file
|
@ -0,0 +1,253 @@
|
|||
#include "interrupts.h"
|
||||
#include "console.h"
|
||||
#include "stdlib.h"
|
||||
#include "cpustate.h"
|
||||
#include "io.h"
|
||||
|
||||
#define GDT_ENTRIES 5
|
||||
#define IDT_ENTRIES 256
|
||||
|
||||
static uint64_t gdt[GDT_ENTRIES];
|
||||
static uint64_t idt[IDT_ENTRIES];
|
||||
|
||||
static const char *interruptNames[] = {
|
||||
"Divide-by-zero Error",// 0 (0x0) Fault #DE No
|
||||
"Debug",// 1 (0x1) Fault/Trap #DB No
|
||||
"Non-maskable Interrupt",// 2 (0x2) Interrupt - No
|
||||
"Breakpoint",// 3 (0x3) Trap #BP No
|
||||
"Overflow",// 4 (0x4) Trap #OF No
|
||||
"Bound Range Exceeded",// 5 (0x5) Fault #BR No
|
||||
"Invalid Opcode",// 6 (0x6) Fault #UD No
|
||||
"Device Not Available",// 7 (0x7) Fault #NM No
|
||||
"Double Fault",// 8 (0x8) Abort #DF Yes
|
||||
"Coprocessor Segment Overrun",// 9 (0x9) Fault - No
|
||||
"Invalid TSS",// 10 (0xA) Fault #TS Yes
|
||||
"Segment Not Present",// 11 (0xB) Fault #NP Yes
|
||||
"Stack-Segment Fault",// 12 (0xC) Fault #SS Yes
|
||||
"General Protection Fault",// 13 (0xD) Fault #GP Yes
|
||||
"Page Fault",// 14 (0xE) Fault #PF Yes
|
||||
"Reserved",// 15 (0xF) - - No
|
||||
"x87 Floating-Point Exception",// 16 (0x10) Fault #MF No
|
||||
"Alignment Check",// 17 (0x11) Fault #AC Yes
|
||||
"Machine Check",// 18 (0x12) Abort #MC No
|
||||
"SIMD Floating-Point Exception",// 19 (0x13) Fault #XM/#XF No
|
||||
"Virtualization Exception",// 20 (0x14) Fault #VE No
|
||||
"Reserved 21",// 21-29 (0x15-0x1D) - - No
|
||||
"Reserved 22",// 21-29 (0x15-0x1D) - - No
|
||||
"Reserved 23",// 21-29 (0x15-0x1D) - - No
|
||||
"Reserved 24",// 21-29 (0x15-0x1D) - - No
|
||||
"Reserved 25",// 21-29 (0x15-0x1D) - - No
|
||||
"Reserved 26",// 21-29 (0x15-0x1D) - - No
|
||||
"Reserved 27",// 21-29 (0x15-0x1D) - - No
|
||||
"Reserved 28",// 21-29 (0x15-0x1D) - - No
|
||||
"Reserved 29",// 21-29 (0x15-0x1D) - - No
|
||||
"Security Exception",// 30 (0x1E) - #SX Yes
|
||||
"Reserved 31",// 31 (0x1F) - - No
|
||||
"IRQ 0",
|
||||
"IRQ 1",
|
||||
"IRQ 2",
|
||||
"IRQ 3",
|
||||
"IRQ 4",
|
||||
"IRQ 5",
|
||||
"IRQ 6",
|
||||
"IRQ 7",
|
||||
"IRQ 8",
|
||||
"IRQ 9",
|
||||
"IRQ 10",
|
||||
"IRQ 11",
|
||||
"IRQ 12",
|
||||
"IRQ 13",
|
||||
"IRQ 14",
|
||||
"IRQ 15",
|
||||
};
|
||||
|
||||
static const size_t interruptNameCount = sizeof(interruptNames) / sizeof(interruptNames[0]);
|
||||
|
||||
#include "intr_stubs.h"
|
||||
|
||||
void intr_routine(CpuState *state)
|
||||
{
|
||||
const char *name = "Unknown";
|
||||
if(state->intr < interruptNameCount)
|
||||
name = interruptNames[state->intr];
|
||||
if(state->intr < 0x20)
|
||||
{
|
||||
kprintf("\n\x12\x04Exception [%d] %s!\x12\0x7\n", state->intr, name);
|
||||
while(1)
|
||||
{
|
||||
__asm__ volatile("cli; hlt");
|
||||
}
|
||||
}
|
||||
if (state->intr >= 0x20 && state->intr <= 0x2f)
|
||||
{
|
||||
if (state->intr >= 0x28)
|
||||
{
|
||||
// EOI an Slave-PIC
|
||||
outb(0xa0, 0x20);
|
||||
}
|
||||
// EOI an Master-PIC
|
||||
outb(0x20, 0x20);
|
||||
}
|
||||
else
|
||||
{
|
||||
kprintf("\n\x12\x04Interrupt [%d] %s occurred!\x12\0x7\n", state->intr, name);
|
||||
while(1)
|
||||
{
|
||||
// Prozessor anhalten
|
||||
__asm__ volatile("cli; hlt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gdt_entry(int i, uint32_t base, uint32_t limit, int flags)
|
||||
{
|
||||
gdt[i] = limit & 0xffffLL;
|
||||
gdt[i] |= (base & 0xffffffLL) << 16;
|
||||
gdt[i] |= (flags & 0xffLL) << 40;
|
||||
gdt[i] |= ((limit >> 16) & 0xfLL) << 48;
|
||||
gdt[i] |= ((flags >> 8 )& 0xffLL) << 52;
|
||||
gdt[i] |= ((base >> 24) & 0xffLL) << 56;
|
||||
}
|
||||
|
||||
static void idt_entry(int i, void (*fn)(), unsigned int selector, int flags)
|
||||
{
|
||||
unsigned long int handler = (unsigned long int) fn;
|
||||
idt[i] = handler & 0xffffLL;
|
||||
idt[i] |= (selector & 0xffffLL) << 16;
|
||||
idt[i] |= (flags & 0xffLL) << 40;
|
||||
idt[i] |= ((handler>> 16) & 0xffffLL) << 48;
|
||||
}
|
||||
|
||||
static void init_gdt(void)
|
||||
{
|
||||
memset(gdt, 0, sizeof(gdt));
|
||||
|
||||
gdt_entry(0, 0, 0, 0);
|
||||
gdt_entry(1, 0, 0xfffff, GDTF_SEGMENT | GDTF_32_BIT |
|
||||
GDTF_CODESEG | GDTF_4K_GRAN | GDTF_PRESENT);
|
||||
gdt_entry(2, 0, 0xfffff, GDTF_SEGMENT | GDTF_32_BIT |
|
||||
GDTF_DATASEG | GDTF_4K_GRAN | GDTF_PRESENT);
|
||||
gdt_entry(3, 0, 0xfffff, GDTF_SEGMENT | GDTF_32_BIT |
|
||||
GDTF_CODESEG | GDTF_4K_GRAN | GDTF_PRESENT | GDTF_RING3);
|
||||
gdt_entry(4, 0, 0xfffff, GDTF_SEGMENT | GDTF_32_BIT |
|
||||
GDTF_DATASEG | GDTF_4K_GRAN | GDTF_PRESENT | GDTF_RING3);
|
||||
|
||||
struct
|
||||
{
|
||||
uint16_t limit;
|
||||
void* pointer;
|
||||
} __attribute__((packed)) gdtp =
|
||||
{
|
||||
.limit = GDT_ENTRIES * 8 - 1,
|
||||
.pointer = gdt,
|
||||
};
|
||||
__asm__ volatile("lgdt %0" : : "m" (gdtp));
|
||||
__asm__ volatile(
|
||||
"mov $0x10, %ax;"
|
||||
"mov %ax, %ds;"
|
||||
"mov %ax, %es;"
|
||||
"mov %ax, %ss;"
|
||||
"ljmp $0x8, $.1;"
|
||||
".1:"
|
||||
);
|
||||
}
|
||||
|
||||
#define IDT_FLAG_INTERRUPT_GATE 0xe
|
||||
#define IDT_FLAG_PRESENT 0x80
|
||||
#define IDT_FLAG_RING0 0x00
|
||||
#define IDT_FLAG_RING3 0x60
|
||||
|
||||
static void init_pic(void)
|
||||
{
|
||||
// Master-PIC initialisieren
|
||||
outb(0x20, 0x11); // Initialisierungsbefehl fuer den PIC
|
||||
outb(0x21, 0x20); // Interruptnummer fuer IRQ 0
|
||||
outb(0x21, 0x04); // An IRQ 2 haengt der Slave
|
||||
outb(0x21, 0x01); // ICW 4
|
||||
|
||||
// Slave-PIC initialisieren
|
||||
outb(0xa0, 0x11); // Initialisierungsbefehl fuer den PIC
|
||||
outb(0xa1, 0x28); // Interruptnummer fuer IRQ 8
|
||||
outb(0xa1, 0x02); // An IRQ 2 haengt der Slave
|
||||
outb(0xa1, 0x01); // ICW 4
|
||||
|
||||
// Alle IRQs aktivieren (demaskieren)
|
||||
outb(0x20, 0x0);
|
||||
outb(0xa0, 0x0);
|
||||
}
|
||||
|
||||
static void init_idt(void)
|
||||
{
|
||||
memset(idt, 0, sizeof(idt));
|
||||
|
||||
#define SET_ENTRY(i) idt_entry(i, intr_stub_ ## i, 0x08, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_PRESENT | IDT_FLAG_RING0)
|
||||
|
||||
for(size_t i = 0; i < IDT_ENTRIES; i++) {
|
||||
idt_entry(i, intr_stub_0, 0x08, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_PRESENT | IDT_FLAG_RING0);
|
||||
}
|
||||
|
||||
// System Interrupts
|
||||
SET_ENTRY(0);
|
||||
SET_ENTRY(1);
|
||||
SET_ENTRY(2);
|
||||
SET_ENTRY(3);
|
||||
SET_ENTRY(4);
|
||||
SET_ENTRY(6);
|
||||
SET_ENTRY(7);
|
||||
SET_ENTRY(8);
|
||||
SET_ENTRY(9);
|
||||
SET_ENTRY(10);
|
||||
SET_ENTRY(11);
|
||||
SET_ENTRY(12);
|
||||
SET_ENTRY(13);
|
||||
SET_ENTRY(14);
|
||||
SET_ENTRY(15);
|
||||
SET_ENTRY(16);
|
||||
SET_ENTRY(17);
|
||||
SET_ENTRY(18);
|
||||
|
||||
// Hardware handler
|
||||
SET_ENTRY(32);
|
||||
SET_ENTRY(33);
|
||||
SET_ENTRY(34);
|
||||
SET_ENTRY(35);
|
||||
SET_ENTRY(36);
|
||||
SET_ENTRY(37);
|
||||
SET_ENTRY(38);
|
||||
SET_ENTRY(39);
|
||||
SET_ENTRY(40);
|
||||
SET_ENTRY(41);
|
||||
SET_ENTRY(42);
|
||||
SET_ENTRY(43);
|
||||
SET_ENTRY(44);
|
||||
SET_ENTRY(45);
|
||||
SET_ENTRY(46);
|
||||
SET_ENTRY(47);
|
||||
|
||||
// System call
|
||||
SET_ENTRY(48);
|
||||
|
||||
#undef SET_ENTRY
|
||||
struct
|
||||
{
|
||||
uint16_t limit;
|
||||
void* pointer;
|
||||
} __attribute__((packed)) idtp =
|
||||
{
|
||||
.limit = IDT_ENTRIES * 8 - 1,
|
||||
.pointer = idt,
|
||||
};
|
||||
__asm__ volatile("lidt %0" : : "m" (idtp));
|
||||
}
|
||||
|
||||
void intr_init(void)
|
||||
{
|
||||
// Initialize global descriptor table
|
||||
init_gdt();
|
||||
|
||||
// Initialize interrupt descriptor table
|
||||
init_idt();
|
||||
|
||||
// Initialize Programmable Interrupt Controller
|
||||
init_pic();
|
||||
}
|
39
src/interrupts.h
Normal file
39
src/interrupts.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#define GDTF_DATASEG 0x02
|
||||
#define GDTF_CODESEG 0x0a
|
||||
#define GDTF_TSS 0x09
|
||||
|
||||
#define GDTF_SEGMENT 0x10
|
||||
#define GDTF_RING0 0x00
|
||||
#define GDTF_RING3 0x60
|
||||
#define GDTF_PRESENT 0x80
|
||||
|
||||
#define GDTF_4K_GRAN 0x800
|
||||
#define GDTF_32_BIT 0x400
|
||||
|
||||
#define INTR_GATE 6
|
||||
#define INTR_TRAP_GATE 7
|
||||
#define INTR_TASK_GATE 5
|
||||
|
||||
/**
|
||||
* Initializes interrupt handling and the global descriptor table.
|
||||
*/
|
||||
void intr_init();
|
||||
|
||||
/**
|
||||
* @brief Enables physical interrupts.
|
||||
*/
|
||||
static inline void irq_enable(void)
|
||||
{
|
||||
__asm__ volatile("sti");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables physical interrupts.
|
||||
*/
|
||||
static inline void irq_disable(void)
|
||||
{
|
||||
__asm__ volatile("cli");
|
||||
}
|
||||
|
46
src/intr_stubs.h
Normal file
46
src/intr_stubs.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#define STUB(num) extern void intr_stub_ ## num ()
|
||||
|
||||
STUB(0);
|
||||
STUB(1);
|
||||
STUB(2);
|
||||
STUB(3);
|
||||
STUB(4);
|
||||
STUB(5);
|
||||
STUB(6);
|
||||
STUB(7);
|
||||
STUB(8);
|
||||
STUB(9);
|
||||
STUB(10);
|
||||
STUB(11);
|
||||
STUB(12);
|
||||
STUB(13);
|
||||
STUB(14);
|
||||
STUB(15);
|
||||
STUB(16);
|
||||
STUB(17);
|
||||
STUB(18);
|
||||
|
||||
STUB(32);
|
||||
STUB(33);
|
||||
STUB(34);
|
||||
STUB(35);
|
||||
STUB(36);
|
||||
STUB(37);
|
||||
STUB(38);
|
||||
STUB(39);
|
||||
STUB(40);
|
||||
STUB(41);
|
||||
STUB(42);
|
||||
STUB(43);
|
||||
STUB(44);
|
||||
STUB(45);
|
||||
STUB(46);
|
||||
STUB(47);
|
||||
|
||||
STUB(48);
|
||||
|
||||
STUB(80);
|
||||
|
||||
#undef STUB
|
13
src/io.h
Normal file
13
src/io.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "inttypes.h"
|
||||
|
||||
/**
|
||||
* @brief Outputs a byte on the given port.
|
||||
* @param port The port number.
|
||||
* @param data The byte to send.
|
||||
*/
|
||||
static inline void outb(uint16_t port, uint8_t data)
|
||||
{
|
||||
__asm__ volatile ("outb %0, %1" : : "a" (data), "Nd" (port));
|
||||
}
|
7
src/kernel.h
Normal file
7
src/kernel.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
void die(const char *msg);
|
||||
|
||||
void ksleep(uint32_t time);
|
249
src/malloc.c
Normal file
249
src/malloc.c
Normal file
|
@ -0,0 +1,249 @@
|
|||
/* Copyright (c) 2002, 2004, 2010 Joerg Wunsch
|
||||
Copyright (c) 2010 Gerben van den Broeke
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
/* $Id: malloc.c 2149 2010-06-09 20:45:37Z joerg_wunsch $ */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct __freelist {
|
||||
size_t sz;
|
||||
struct __freelist *nx;
|
||||
};
|
||||
|
||||
size_t __malloc_margin = 32;
|
||||
char * const __malloc_heap_start = (char *)0x400000;
|
||||
char * const __malloc_heap_end = (char *)0x800000;
|
||||
|
||||
char *__brkval;
|
||||
struct __freelist *__flp;
|
||||
|
||||
void *malloc(size_t len)
|
||||
{
|
||||
struct __freelist *fp1, *fp2, *sfp1, *sfp2;
|
||||
char *cp;
|
||||
size_t s, avail;
|
||||
|
||||
/*
|
||||
* Our minimum chunk size is the size of a pointer (plus the
|
||||
* size of the "sz" field, but we don't need to account for
|
||||
* this), otherwise we could not possibly fit a freelist entry
|
||||
* into the chunk later.
|
||||
*/
|
||||
if (len < sizeof(struct __freelist) - sizeof(size_t))
|
||||
len = sizeof(struct __freelist) - sizeof(size_t);
|
||||
|
||||
/*
|
||||
* First, walk the free list and try finding a chunk that
|
||||
* would match exactly. If we found one, we are done. While
|
||||
* walking, note down the smallest chunk we found that would
|
||||
* still fit the request -- we need it for step 2.
|
||||
*
|
||||
*/
|
||||
for (s = 0, fp1 = __flp, fp2 = 0;
|
||||
fp1;
|
||||
fp2 = fp1, fp1 = fp1->nx) {
|
||||
if (fp1->sz < len)
|
||||
continue;
|
||||
if (fp1->sz == len) {
|
||||
/*
|
||||
* Found it. Disconnect the chunk from the
|
||||
* freelist, and return it.
|
||||
*/
|
||||
if (fp2)
|
||||
fp2->nx = fp1->nx;
|
||||
else
|
||||
__flp = fp1->nx;
|
||||
return &(fp1->nx);
|
||||
}
|
||||
else {
|
||||
if (s == 0 || fp1->sz < s) {
|
||||
/* this is the smallest chunk found so far */
|
||||
s = fp1->sz;
|
||||
sfp1 = fp1;
|
||||
sfp2 = fp2;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Step 2: If we found a chunk on the freelist that would fit
|
||||
* (but was too large), look it up again and use it, since it
|
||||
* is our closest match now. Since the freelist entry needs
|
||||
* to be split into two entries then, watch out that the
|
||||
* difference between the requested size and the size of the
|
||||
* chunk found is large enough for another freelist entry; if
|
||||
* not, just enlarge the request size to what we have found,
|
||||
* and use the entire chunk.
|
||||
*/
|
||||
if (s) {
|
||||
if (s - len < sizeof(struct __freelist)) {
|
||||
/* Disconnect it from freelist and return it. */
|
||||
if (sfp2)
|
||||
sfp2->nx = sfp1->nx;
|
||||
else
|
||||
__flp = sfp1->nx;
|
||||
return &(sfp1->nx);
|
||||
}
|
||||
/*
|
||||
* Split them up. Note that we leave the first part
|
||||
* as the new (smaller) freelist entry, and return the
|
||||
* upper portion to the caller. This saves us the
|
||||
* work to fix up the freelist chain; we just need to
|
||||
* fixup the size of the current entry, and note down
|
||||
* the size of the new chunk before returning it to
|
||||
* the caller.
|
||||
*/
|
||||
cp = (char *)sfp1;
|
||||
s -= len;
|
||||
cp += s;
|
||||
sfp2 = (struct __freelist *)cp;
|
||||
sfp2->sz = len;
|
||||
sfp1->sz = s - sizeof(size_t);
|
||||
return &(sfp2->nx);
|
||||
}
|
||||
/*
|
||||
* Step 3: If the request could not be satisfied from a
|
||||
* freelist entry, just prepare a new chunk. This means we
|
||||
* need to obtain more memory first. The largest address just
|
||||
* not allocated so far is remembered in the brkval variable.
|
||||
* Under Unix, the "break value" was the end of the data
|
||||
* segment as dynamically requested from the operating system.
|
||||
* Since we don't have an operating system, just make sure
|
||||
* that we don't collide with the stack.
|
||||
*/
|
||||
if (__brkval == 0)
|
||||
__brkval = __malloc_heap_start;
|
||||
cp = __malloc_heap_end;
|
||||
if (cp <= __brkval)
|
||||
/*
|
||||
* Memory exhausted.
|
||||
*/
|
||||
return 0;
|
||||
avail = cp - __brkval;
|
||||
/*
|
||||
* Both tests below are needed to catch the case len >= 0xfffe.
|
||||
*/
|
||||
if (avail >= len && avail >= len + sizeof(size_t)) {
|
||||
fp1 = (struct __freelist *)__brkval;
|
||||
__brkval += len + sizeof(size_t);
|
||||
fp1->sz = len;
|
||||
return &(fp1->nx);
|
||||
}
|
||||
/*
|
||||
* Step 4: There's no help, just fail. :-/
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void free(void *p)
|
||||
{
|
||||
struct __freelist *fp1, *fp2, *fpnew;
|
||||
char *cp1, *cp2, *cpnew;
|
||||
|
||||
/* ISO C says free(NULL) must be a no-op */
|
||||
if (p == 0)
|
||||
return;
|
||||
|
||||
cpnew = p;
|
||||
cpnew -= sizeof(size_t);
|
||||
fpnew = (struct __freelist *)cpnew;
|
||||
fpnew->nx = 0;
|
||||
|
||||
/*
|
||||
* Trivial case first: if there's no freelist yet, our entry
|
||||
* will be the only one on it. If this is the last entry, we
|
||||
* can reduce __brkval instead.
|
||||
*/
|
||||
if (__flp == 0) {
|
||||
if ((char *)p + fpnew->sz == __brkval)
|
||||
__brkval = cpnew;
|
||||
else
|
||||
__flp = fpnew;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now, find the position where our new entry belongs onto the
|
||||
* freelist. Try to aggregate the chunk with adjacent chunks
|
||||
* if possible.
|
||||
*/
|
||||
for (fp1 = __flp, fp2 = 0;
|
||||
fp1;
|
||||
fp2 = fp1, fp1 = fp1->nx) {
|
||||
if (fp1 < fpnew)
|
||||
continue;
|
||||
cp1 = (char *)fp1;
|
||||
fpnew->nx = fp1;
|
||||
if ((char *)&(fpnew->nx) + fpnew->sz == cp1) {
|
||||
/* upper chunk adjacent, assimilate it */
|
||||
fpnew->sz += fp1->sz + sizeof(size_t);
|
||||
fpnew->nx = fp1->nx;
|
||||
}
|
||||
if (fp2 == 0) {
|
||||
/* new head of freelist */
|
||||
__flp = fpnew;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Note that we get here either if we hit the "break" above,
|
||||
* or if we fell off the end of the loop. The latter means
|
||||
* we've got a new topmost chunk. Either way, try aggregating
|
||||
* with the lower chunk if possible.
|
||||
*/
|
||||
fp2->nx = fpnew;
|
||||
cp2 = (char *)&(fp2->nx);
|
||||
if (cp2 + fp2->sz == cpnew) {
|
||||
/* lower junk adjacent, merge */
|
||||
fp2->sz += fpnew->sz + sizeof(size_t);
|
||||
fp2->nx = fpnew->nx;
|
||||
}
|
||||
/*
|
||||
* If there's a new topmost chunk, lower __brkval instead.
|
||||
*/
|
||||
for (fp1 = __flp, fp2 = 0;
|
||||
fp1->nx != 0;
|
||||
fp2 = fp1, fp1 = fp1->nx)
|
||||
/* advance to entry just before end of list */;
|
||||
cp2 = (char *)&(fp1->nx);
|
||||
if (cp2 + fp1->sz == __brkval) {
|
||||
if (fp2 == NULL)
|
||||
/* Freelist is empty now. */
|
||||
__flp = NULL;
|
||||
else
|
||||
fp2->nx = NULL;
|
||||
__brkval = cp2 - sizeof(size_t);
|
||||
}
|
||||
}
|
91
src/multiboot.h
Normal file
91
src/multiboot.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define MB_MEMSIZE (1<<0)
|
||||
#define MB_BOOTDEVICE (1<<1)
|
||||
#define MB_COMMANDLINE (1<<2)
|
||||
#define MB_MODULES (1<<3)
|
||||
#define MB_SYMS_AOUT (1<<4)
|
||||
#define MB_SYMS_ELF (1<<5)
|
||||
#define MB_MEMORYMAP (1<<6)
|
||||
#define MB_DRIVES (1<<7)
|
||||
#define MB_CONFIG_TABLE (1<<8)
|
||||
#define MB_BOOTLOADER_NAME (1<<9)
|
||||
#define MB_APS_TABLE (1<<10)
|
||||
#define MB_VBE (1<<11)
|
||||
|
||||
typedef struct {
|
||||
uint32_t entry_size;
|
||||
uint64_t base;
|
||||
uint64_t length;
|
||||
uint32_t type;
|
||||
} __attribute__((packed)) MultibootMemoryMap;
|
||||
|
||||
typedef struct {
|
||||
uintptr_t start;
|
||||
uintptr_t end;
|
||||
uintptr_t name;
|
||||
uint32_t reserved;
|
||||
} __attribute__((packed)) MultibootModule;
|
||||
|
||||
typedef struct {
|
||||
uint32_t size;
|
||||
uint8_t number;
|
||||
uint8_t mode;
|
||||
uint16_t cylinders;
|
||||
uint8_t heads;
|
||||
uint8_t sectors;
|
||||
uint16_t ports[0];
|
||||
// 0x10 size-0x10 drive_ports I/O-Ports, die von diesem Gerät benutzt werden
|
||||
} __attribute__((packed)) MultibootDrive;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t version;
|
||||
uint16_t cseg;
|
||||
uint32_t offset;
|
||||
uint16_t cseg_16;
|
||||
uint16_t dseg;
|
||||
uint16_t flags;
|
||||
uint16_t cseg_len;
|
||||
uint16_t cseg_16_len;
|
||||
uint16_t dseg_len;
|
||||
} __attribute__((packed)) MultibootAPMTable;
|
||||
|
||||
typedef struct {
|
||||
uint32_t flags;
|
||||
uint32_t memLower;
|
||||
uint32_t memUpper;
|
||||
uint32_t bootDevice;
|
||||
uintptr_t commandline;
|
||||
uint32_t moduleCount;
|
||||
uintptr_t modules;
|
||||
union {
|
||||
struct {
|
||||
uint32_t tabsize;
|
||||
uint32_t strsize;
|
||||
uint32_t addr;
|
||||
uint32_t reserved;
|
||||
} __attribute__((packed)) symsAssemblerOut;
|
||||
struct {
|
||||
uint32_t num;
|
||||
uint32_t size;
|
||||
uintptr_t addr;
|
||||
uintptr_t shndx;
|
||||
} __attribute__((packed)) symsELF;
|
||||
};
|
||||
uint32_t memoryMapLength;
|
||||
uintptr_t memoryMap;
|
||||
uint32_t drivesLength;
|
||||
uintptr_t drives;
|
||||
uintptr_t configTable;
|
||||
uintptr_t bootLoaderName;
|
||||
uintptr_t apmTable;
|
||||
uint32_t vbeControlInfo;
|
||||
uint32_t vbeModeInfo;
|
||||
uint16_t vbeMode;
|
||||
uint16_t vbeInterfaceSegment;
|
||||
uint16_t vbeInterfaceOffset;
|
||||
uint16_t vbeInterfaceLength;
|
||||
} __attribute__((packed)) MultibootStructure;
|
142
src/pmm.c
Normal file
142
src/pmm.c
Normal file
|
@ -0,0 +1,142 @@
|
|||
#include <inttypes.h>
|
||||
#include "pmm.h"
|
||||
#include "kernel.h"
|
||||
#include "stdlib.h"
|
||||
|
||||
#include "console.h"
|
||||
|
||||
#define BITMAP_SIZE 32768
|
||||
|
||||
extern const void kernelStart;
|
||||
extern const void kernelEnd;
|
||||
|
||||
/**
|
||||
* Stores for every page in 4 GB RAM if it is free or not.
|
||||
* A set bit indicates that the page is free.
|
||||
*/
|
||||
static uint32_t bitmap[BITMAP_SIZE];
|
||||
|
||||
static inline void resolve(void *pptr, uint32_t *idx, uint32_t *bit)
|
||||
{
|
||||
uintptr_t ptr = (uintptr_t)pptr;
|
||||
ptr >>= 12; // Calculate page ID
|
||||
|
||||
*idx = ptr / 32; // B32 offset in bitmap.
|
||||
*bit = ptr % 32; // Bit offset in bitmap
|
||||
}
|
||||
|
||||
static inline void *rebuild(uint32_t idx, uint32_t bit)
|
||||
{
|
||||
uintptr_t ptr = 32 * idx + bit;
|
||||
ptr <<= 12;
|
||||
return (void*)ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks an address used or free
|
||||
* @param addr The address that will be marked
|
||||
* @param used If != 0, the addr will be marked as used, else it will be marked as free.
|
||||
*/
|
||||
static inline void mark(uintptr_t addr, uint32_t used)
|
||||
{
|
||||
uint32_t bit, idx;
|
||||
resolve((void*)addr, &idx, &bit);
|
||||
if(used)
|
||||
bitmap[idx] &= ~(1<<bit);
|
||||
else
|
||||
bitmap[idx] |= (1<<bit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a section in memory as used or free.
|
||||
* @param start The start address of the section.
|
||||
* @param end The end address of the section.
|
||||
* @param used If 0 the section will be freed, else it will be marked as used.
|
||||
*/
|
||||
static inline void markSection(uintptr_t start, uintptr_t end, uint32_t used)
|
||||
{
|
||||
uintptr_t it = start;
|
||||
while(it < end)
|
||||
{
|
||||
mark(it, used);
|
||||
it += 0x1000;
|
||||
}
|
||||
}
|
||||
|
||||
void pmm_init(const MultibootStructure *mb)
|
||||
{
|
||||
if((mb->flags & MB_MEMORYMAP) == 0)
|
||||
die("Multiboot header missing memory map. Cannot initialize PMM.");
|
||||
|
||||
// Make all memory used
|
||||
memset(bitmap, 0, sizeof(bitmap));
|
||||
|
||||
// Free the memory map
|
||||
{
|
||||
uintptr_t it = mb->memoryMap;
|
||||
for(size_t i = 0; i < mb->memoryMapLength; i++)
|
||||
{
|
||||
const MultibootMemoryMap *mmap = (const MultibootMemoryMap *)it;
|
||||
if(mmap->type == 1)
|
||||
{
|
||||
markSection(mmap->base, mmap->base + mmap->length, 0);
|
||||
}
|
||||
it += mmap->entry_size + 4; // Stupid offset :P
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the whole kernel as "used"
|
||||
{
|
||||
uintptr_t start = (uintptr_t)&kernelStart;
|
||||
uintptr_t end = (uintptr_t)&kernelEnd;
|
||||
markSection(start, end, 1);
|
||||
}
|
||||
|
||||
// Mark all kernel modules as "used"
|
||||
if(mb->flags & MB_MODULES)
|
||||
{
|
||||
const MultibootModule *mod = (const MultibootModule *)mb->modules;
|
||||
for(size_t i = 0; i < mb->moduleCount; i++)
|
||||
{
|
||||
markSection(mod[i].start, mod[i].end, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t pmm_calc_free(void)
|
||||
{
|
||||
uint32_t mem = 0;
|
||||
for(uint32_t idx = 0; idx < BITMAP_SIZE; idx++)
|
||||
{
|
||||
for(uint32_t bit = 0; bit < 32; bit++)
|
||||
{
|
||||
if(bitmap[idx] & (1<<bit))
|
||||
mem += 0x1000;
|
||||
}
|
||||
}
|
||||
return mem;
|
||||
}
|
||||
|
||||
void pmm_free(void *pptr)
|
||||
{
|
||||
mark((uintptr_t)pptr, 0);
|
||||
}
|
||||
|
||||
|
||||
void *pmm_alloc(void)
|
||||
{
|
||||
for(uint32_t idx = 0; idx < BITMAP_SIZE; idx++)
|
||||
{
|
||||
for(uint32_t bit = 0; bit < 32; bit++)
|
||||
{
|
||||
if(bitmap[idx] & (1<<bit))
|
||||
{
|
||||
// Allocate here
|
||||
bitmap[idx] &= ~(1<<bit);
|
||||
return rebuild(idx, bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
die("out of physical memory");
|
||||
return (void*)0xDEADBEEF;
|
||||
}
|
24
src/pmm.h
Normal file
24
src/pmm.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "multiboot.h"
|
||||
|
||||
/**
|
||||
* Initializes physical memory management.
|
||||
* @param mb The multi boot structure used for gathering information about free memory.
|
||||
*/
|
||||
void pmm_init(const MultibootStructure *mb);
|
||||
|
||||
/**
|
||||
* Frees a page of physical memory.
|
||||
*/
|
||||
void pmm_free(void *pptr);
|
||||
|
||||
/**
|
||||
* Allocates a page of physical memory.
|
||||
*/
|
||||
void *pmm_alloc(void);
|
||||
|
||||
/**
|
||||
* Calculates the free memory in bytes.
|
||||
*/
|
||||
uint32_t pmm_calc_free(void);
|
73
src/stdlib.c
Normal file
73
src/stdlib.c
Normal file
|
@ -0,0 +1,73 @@
|
|||
#include "stdlib.h"
|
||||
|
||||
static void reverse(char *str, int length)
|
||||
{
|
||||
int start = 0;
|
||||
int end = length -1;
|
||||
while (start < end)
|
||||
{
|
||||
char tmp = *(str+start);
|
||||
*(str+start) = *(str+end);
|
||||
*(str+end) = tmp;
|
||||
start++;
|
||||
end--;
|
||||
}
|
||||
}
|
||||
|
||||
char *itoa(int num, char *str, int base)
|
||||
{
|
||||
int i = 0;
|
||||
int isNegative = 0;
|
||||
|
||||
/* Handle 0 explicitely, otherwise empty string is printed for 0 */
|
||||
if (num == 0)
|
||||
{
|
||||
str[i++] = '0';
|
||||
str[i] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
// In standard itoa(), negative numbers are handled only with
|
||||
// base 10. Otherwise numbers are considered unsigned.
|
||||
if (num < 0 && base == 10)
|
||||
{
|
||||
isNegative = 1;
|
||||
num = -num;
|
||||
}
|
||||
|
||||
// Process individual digits
|
||||
while (num != 0)
|
||||
{
|
||||
int rem = num % base;
|
||||
str[i++] = (rem > 9)? (rem-10) + 'A' : rem + '0';
|
||||
num = num/base;
|
||||
}
|
||||
|
||||
// If number is negative, append '-'
|
||||
if (isNegative)
|
||||
{
|
||||
str[i++] = '-';
|
||||
}
|
||||
str[i] = '\0'; // Append string terminator
|
||||
|
||||
// Reverse the string
|
||||
reverse(str, i);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
int atoi(const char *str)
|
||||
{
|
||||
int res = 0;
|
||||
for (int i = 0; str[i] != '\0'; ++i)
|
||||
{
|
||||
res = res * 10 + str[i] - '0';
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void *memmove( void *destination, const void *source, size_t num)
|
||||
{
|
||||
// TODO: Implement memmove
|
||||
return nullptr;
|
||||
}
|
81
src/stdlib.h
Normal file
81
src/stdlib.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
#include "varargs.h"
|
||||
|
||||
char *itoa(int value, char *str, int base);
|
||||
int atoi(const char *str);
|
||||
float atof(const char *str);
|
||||
|
||||
/**
|
||||
* Allocates a block of memory
|
||||
* @param size Minimum size of the memory block
|
||||
* @return Pointer to the allocated memory area
|
||||
*/
|
||||
void *malloc(size_t size);
|
||||
|
||||
/**
|
||||
* Frees a previously allocated block of memory.
|
||||
* @param mem The block of memory.
|
||||
*/
|
||||
void free(void *mem);
|
||||
|
||||
static inline void *memset(void *ptr, int value, size_t num)
|
||||
{
|
||||
uint8_t *it = (uint8_t*)ptr;
|
||||
while((num--) > 0)
|
||||
{
|
||||
*(it++) = (uint8_t)(value & 0xFF);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static inline void *memcpy(void *destination, const void *source, size_t num)
|
||||
{
|
||||
uint8_t *to = (uint8_t*)destination;
|
||||
uint8_t *from = (uint8_t*)source;
|
||||
while((num--) > 0)
|
||||
{
|
||||
*(to++) = *(from++);
|
||||
}
|
||||
return destination;
|
||||
}
|
||||
|
||||
void *memmove(void *destination, const void *source, size_t num);
|
||||
|
||||
static inline char *strcpy(char *destination, const char *source)
|
||||
{
|
||||
while(*source)
|
||||
{
|
||||
*(destination++) = *(source++);
|
||||
}
|
||||
return destination;
|
||||
}
|
||||
|
||||
static inline char *strcat(char *destination, const char *source)
|
||||
{
|
||||
char *it = destination;
|
||||
while(*it++);
|
||||
it--;
|
||||
while(*source)
|
||||
{
|
||||
*it++ = *source++;
|
||||
}
|
||||
*it = 0;
|
||||
return destination;
|
||||
}
|
||||
|
||||
static inline size_t strlen(const char *str)
|
||||
{
|
||||
size_t size = 0;
|
||||
while(*(str++) != 0) size++;
|
||||
return size;
|
||||
}
|
||||
|
||||
static inline void *calloc(size_t size)
|
||||
{
|
||||
void *mem = malloc(size);
|
||||
memset(mem, 0, size);
|
||||
return mem;
|
||||
}
|
6
src/varargs.h
Normal file
6
src/varargs.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
typedef __builtin_va_list va_list;
|
||||
#define va_start(ap, X) __builtin_va_start(ap, X)
|
||||
#define va_arg(ap, type) __builtin_va_arg(ap, type)
|
||||
#define va_end(ap) __builtin_va_end(ap)
|
81
src/vmm.c
Normal file
81
src/vmm.c
Normal file
|
@ -0,0 +1,81 @@
|
|||
#include "vmm.h"
|
||||
#include "pmm.h"
|
||||
#include "stdlib.h"
|
||||
#include "console.h"
|
||||
#include "kernel.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uintptr_t *directory;
|
||||
} VmmContext;
|
||||
|
||||
VmmContext *context = nullptr;
|
||||
|
||||
VmmContext* vmm_create_context(void)
|
||||
{
|
||||
VmmContext* context = (VmmContext*)pmm_alloc();
|
||||
|
||||
// Initialize page directory
|
||||
context->directory = pmm_alloc();
|
||||
for (uint32_t i = 0; i < 1024; i++)
|
||||
{
|
||||
context->directory[i] = 0;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
static inline void vmm_activate_context(VmmContext* context)
|
||||
{
|
||||
__asm__ volatile("mov %0, %%cr3" : : "r" (context->directory));
|
||||
}
|
||||
|
||||
void vmm_map(uintptr_t virtual, uintptr_t physical, uint32_t flags)
|
||||
{
|
||||
// Prepare parameters
|
||||
// Align 12 bit
|
||||
virtual = (virtual >> 12) << 12;
|
||||
physical = (physical >> 12) << 12;
|
||||
flags &= 0x0007;
|
||||
|
||||
uint32_t pageIndex = virtual / 4096;
|
||||
|
||||
uint32_t dirIdx = pageIndex / 1024; // Index in page directory
|
||||
uint32_t tableIdx = pageIndex % 1024; // Index in page table
|
||||
|
||||
uintptr_t *table = nullptr;
|
||||
if((context->directory[dirIdx] & VM_PRESENT) == 0)
|
||||
{
|
||||
// Allocate page table if not exists.
|
||||
table = pmm_alloc();
|
||||
for(uint32_t i = 0; i < 1024; i++)
|
||||
{
|
||||
table[i] = 0;
|
||||
}
|
||||
context->directory[dirIdx] = (uintptr_t)table | VM_PRESENT | VM_WRITABLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
table = (uintptr_t*)(context->directory[dirIdx] & 0xfffff000);
|
||||
}
|
||||
table[tableIdx] = physical | flags;
|
||||
|
||||
__asm__ volatile("invlpg %0" : : "m" (*(char*)virtual));
|
||||
}
|
||||
|
||||
void vmm_init(void)
|
||||
{
|
||||
context = vmm_create_context();
|
||||
|
||||
/* Die ersten 4 MB an dieselbe physische wie virtuelle Adresse mappen */
|
||||
for (uintptr_t ptr = 0; ptr < 0x400000; ptr += 0x1000)
|
||||
{
|
||||
vmm_map(ptr, ptr, VM_PRESENT | VM_WRITABLE);
|
||||
}
|
||||
|
||||
vmm_activate_context(context);
|
||||
|
||||
uint32_t cr0;
|
||||
__asm__ volatile("mov %%cr0, %0" : "=r" (cr0));
|
||||
cr0 |= (1 << 31);
|
||||
__asm__ volatile("mov %0, %%cr0" : : "r" (cr0));
|
||||
}
|
21
src/vmm.h
Normal file
21
src/vmm.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define VM_PRESENT 0x01
|
||||
#define VM_WRITABLE 0x02
|
||||
#define VM_USER 0x04
|
||||
|
||||
#define VM_KERNEL (VM_PRESENT | VM_WRITABLE
|
||||
#define VM_PROGRAM (VM_PRESENT | VM_WRITABLE | VM_USER)
|
||||
|
||||
void vmm_init(void);
|
||||
|
||||
/**
|
||||
* Maps a physical address to a virtual address.
|
||||
* @param virtual The virtual address to be mapped.
|
||||
* @param physical Target physical address
|
||||
* @param flags Flags for the mapping
|
||||
* @remarks If flags is 0, the mapping will be undone.
|
||||
*/
|
||||
void vmm_map(uintptr_t virtual, uintptr_t physical, uint32_t flags);
|
212
trainscript.md
Normal file
212
trainscript.md
Normal file
|
@ -0,0 +1,212 @@
|
|||
# Trainscript Version 1 Specification
|
||||
|
||||
## Basic Language Features
|
||||
|
||||
The language is not case sensitive.
|
||||
|
||||
Keywords should be written **CAPITALIZED**.
|
||||
|
||||
Identifieres should be written **lowercased**.
|
||||
|
||||
### Comments
|
||||
Comments are written with a starting #
|
||||
|
||||
normal code
|
||||
# comment
|
||||
normal code # comment
|
||||
|
||||
### Types
|
||||
The language has the following built in primitive types:
|
||||
|
||||
- VOID
|
||||
- BOOL
|
||||
- INT
|
||||
- REAL
|
||||
- TEXT
|
||||
|
||||
`VOID` is a special type that can only be used in function
|
||||
return values indicating the function does not return anything
|
||||
|
||||
### Pointers
|
||||
The language supports pointers by using special operators.
|
||||
Pointers can exist to every type the language supports, not
|
||||
including pointers.
|
||||
|
||||
### Blocks
|
||||
Blocks are defined by indentation in this language. An
|
||||
indentation by a 2 spaces is required:
|
||||
|
||||
blockA
|
||||
blockB
|
||||
blockB
|
||||
blockC
|
||||
blockC
|
||||
blockb
|
||||
blockA
|
||||
blockA
|
||||
|
||||
## Grammarless Description
|
||||
|
||||
### Variable Declaration
|
||||
|
||||
VAR name : type;
|
||||
|
||||
### Expressions And Assignments
|
||||
|
||||
An expression is a piece of code that calculates a value.
|
||||
An assignment assigns the result of an expression to a
|
||||
variable. The assignment is either done by `->` or `→`.
|
||||
|
||||
1 → a;
|
||||
a → b;
|
||||
a + b → c;
|
||||
a * (b + c) → d;
|
||||
|
||||
### Control Structures
|
||||
|
||||
The language supports the most basic control structures:
|
||||
|
||||
- IF
|
||||
- IF/ELSE
|
||||
- IF/ELSEIF/ELSE
|
||||
- REPEAT
|
||||
- REPEAT WHILE
|
||||
- REPEAT UNTIL
|
||||
- REPEAT FROM TO
|
||||
|
||||
Each control structure except `REPEAT` takes an expression
|
||||
for defining the control. The expression is executed once or
|
||||
multiple times depending on the behaviour of the control
|
||||
structure.
|
||||
|
||||
# Simple condition
|
||||
IF expr
|
||||
loc;
|
||||
|
||||
# Condition with alternative
|
||||
IF expr
|
||||
loc;
|
||||
ELSE
|
||||
loc;
|
||||
|
||||
# Condition with multiple alternatives
|
||||
IF expr
|
||||
loc;
|
||||
ELSEIF expr2
|
||||
loc;
|
||||
|
||||
IF expr
|
||||
loc;
|
||||
ELSEIF expr2
|
||||
loc;
|
||||
ELSE
|
||||
loc;
|
||||
|
||||
# Endless loop
|
||||
REPEAT
|
||||
loc;
|
||||
|
||||
# "While" loop
|
||||
REPEAT WHILE expr
|
||||
loc;
|
||||
|
||||
# Inverted "Do-While" loop
|
||||
REPEAT UNTIL expr
|
||||
loc;
|
||||
|
||||
# "For" loop
|
||||
REPEAT a FROM 1 TO 10
|
||||
loc;
|
||||
|
||||
### Function Calls
|
||||
|
||||
Every function returns a value and thus can be included in an expression.
|
||||
Functions calls do not have to be in an expression and can discard the return value.
|
||||
|
||||
fn();
|
||||
fn(a);
|
||||
fn(a,b);
|
||||
fn() → a;
|
||||
fn(a) → b;
|
||||
fn(a,b) → c;
|
||||
|
||||
### Function Declaration
|
||||
|
||||
Functions are declared by using either the `PUB` or the `PRI` keyword.
|
||||
Each script file can be seen as a module that exports a set of functions.
|
||||
`PUB` declares a function that can be seen from outside the module, `PRI`
|
||||
declares a hidden function.
|
||||
|
||||
Each function has a set of local variables that have a specific type.
|
||||
|
||||
Returning a value is done by declaring a special local variable that can
|
||||
be named as wanted.
|
||||
|
||||
# Program that returns 0
|
||||
PUB main() → result : INT
|
||||
0 → result;
|
||||
|
||||
PUB noReturn()
|
||||
otherFn();
|
||||
|
||||
# Private function that doubles the given value squared.
|
||||
PRI fn(x : REAL) → y : REAL
|
||||
2 * x * x → y;
|
||||
|
||||
|
||||
### Pointer Declaration And Usage
|
||||
|
||||
A pointer is declared by using `PTR(type)` as the type.
|
||||
|
||||
VAR name : PTR(INT);
|
||||
|
||||
To retrieve a pointer value or write to it, `VAL(pointer)` must be used:
|
||||
|
||||
VAL(pointer) → a;
|
||||
a → VAL(pointer);
|
||||
|
||||
The address of a variable can be retrieved by using `REF(variable)`:
|
||||
|
||||
REF(variable) → pointer;
|
||||
|
||||
## Example File
|
||||
|
||||
PUB inOut() | a : INT
|
||||
printStr("Enter a number:");
|
||||
readInt() → a;
|
||||
printStr("You entered: ");
|
||||
printInt(a);
|
||||
printLn(); # Prints a newline ('\n')
|
||||
|
||||
PUB isEven(x : INT) → even : BOOL
|
||||
IF (x % 2) =/= 0
|
||||
false → even
|
||||
ELSE
|
||||
true → even
|
||||
|
||||
PUB fibonacci(n : INT) → f : INT
|
||||
IF n = 0
|
||||
0 → f;
|
||||
ELSEIF n = 1
|
||||
1 → f;
|
||||
ELSE
|
||||
fibonacci(n - 1) + fibonacci(n - 2) → f;
|
||||
|
||||
PUB factorial(number : INT) → result : INT
|
||||
IF number <= 1
|
||||
1 → result;
|
||||
ELSE
|
||||
number * factorial(number - 1) → result;
|
||||
|
||||
PRI double(ptr : PTR(INT)) → previous : INT
|
||||
VAL(ptr) → previous;
|
||||
2 * previous → VAL(ptr);
|
||||
|
||||
PUB ptrTest() | a : INT, b : INT
|
||||
10 → a;
|
||||
double(REF(a)) → b;
|
||||
IF a = 20
|
||||
printStr("double has doubled a.");
|
||||
IF a = (2 * b)
|
||||
printStr("double has returned the correct previous value");
|
||||
|
Loading…
Reference in a new issue