Merge pull request #1 from MasterQ32/master

just merge
This commit is contained in:
Morten Delenk 2016-07-01 16:22:41 +02:00 committed by GitHub
commit 131ef54972
97 changed files with 1184 additions and 1600 deletions

3
.gitignore vendored
View file

@ -29,3 +29,6 @@
*.exe
*.out
*.app
# Boot Images
*.img

View file

@ -1,20 +1,21 @@
# SuperVM
# SuperVM SPU Mark I
SuperVM is a stack machine with a simple, but flexible command
set.
The *SPU Mark I* is a stack machine with a simple, but flexible command
set. The abbreviation SPU stands for Stack Processing Unit, Mark I declares
that it is the first version of the SPU.
## Purpose of this document
This document is meant to give a complete overview over the concepts and abstract
workings of SuperVM.
workings of the *SPU Mark I*.
It is targeted at uses who program SuperVM with the native assembly language,
It is targeted at uses who program the *SPU Mark I* with the native assembly language,
system programmers who want to include the virtual machine in their system or
create their own SuperVM implementation.
create their own *SPU Mark I* implementation.
## Concepts
SuperVM is a virtual machine that emulates a 32 bit stack machine. Instead of utilizing
*SPU Mark I* is a virtual CPU that emulates a 32 bit stack machine. Instead of utilizing
registers operations take their operands from the stack and push their results to
it.
@ -44,23 +45,20 @@ least 1024 entries. This allows a fair recursive depth of 128 recursions with an
of 6 local variables per function call.
### Data Memory
SuperVM also provides a memory model that allows storing persistent data that is
accessed by different parts of the code.
*SPU Mark I* also provides a memory model that allows storing RAM data that is
accessible by different parts of the code.
The data memory is byte accessible and can be written or read.
It is implementation defined how the memory is managed and accessible. It can be a
sparse memory with different sections, it could utilize a software-implemented paging
process or just be a flat chunk of memory.
As most programs require a minimum of global variables, the data memory should be
at least 16kB large.
Every pointer that accesses data memory (e.g. via `store` and `load`) contains the
address of a byte in memory, starting with zero.
## Registers and Flags
The SuperVM virtual machine is a stack machine, but has also some control
The *SPU Mark I* is a stack machine, but has also some control
registers that can be set with special instructions. The registers mainly
control stack access or control flow.
@ -108,7 +106,7 @@ code pointer is equivalent to a jump operation.
## Instructions
An SuperVM instruction is composed of multiple components:
An *SPU Mark I* instruction is composed of multiple components:
| Component | Range | Size | Function |
|-------------|-------------------|------|------------------------------------------|
@ -213,6 +211,12 @@ The math command is a compound operator that contains all
ALU operations. The ALU operation is selected
by the `cmdinfo`.
If an ALU operation takes two operands, the right hand side is
defined by `input0`, the left hand side is defined by `input1`.
This allows a configuration such that the right hand side operand
is taken by the argument of the instruction instead of beeing popped
from the stack.
| cmdinfo | Operation | Forumla |
|---------|-----------------------------|-----------------|
| 0 | Addition | input1 + input0 |
@ -260,10 +264,11 @@ is set when the highest bit is set.
Each instruction can emit an output value. The output can be used in the following ways:
| # | Output | Effect |
|---|---------|--------------------------------------------------------------|
|---|---------|-----------------------------------------------------------------------------|
| 0 | discard | The output value is discarded. |
| 1 | push | The output is pushed to the stack. |
| 2 | jump | The code pointer is set to the output, thus a jump is taken. |
| 3 | jumpr | The code pointer is increased by the output, thus a relative jump is taken. |
### Argument
The instruction argument can provide static input which can be used
@ -271,7 +276,7 @@ as a value source for the first input value.
## Function Calls
The following chapter defines the SuperVM calling convention. It is required that all
The following chapter defines the *SPU Mark I* calling convention. It is required that all
functions conform to this convention.
To call a function, it is required that the return address is pushed to the stack.
@ -282,7 +287,7 @@ After this, a jump is taken to the function address.
jmp @function ; Jumps to the function
returnPoint:
SuperVM provides the instruction `cpget` which pushes by default the address of the
*SPU Mark I* provides the instruction `cpget` which pushes by default the address of the
second next instruction which resembles the code above. This behaviour allows position
independent code:

View file

@ -0,0 +1,24 @@
# SuperVM Syscall Definition
This document contains a list of pre-defined syscalls
for the SuperVM.
## 0: Exit
This syscall quits the execution of the current program.
It takes no inputs and does not output any value.
## 1: putc(char)
This syscalls prints a single ASCII character which is
stored in *Input0*. It has no outputs.
## 2: puti(int)
This syscalls prints an unsigned integer which is stored
in *Input0*. It has no outputs.
## 3:
## 4:
## 5:
## 6:

View file

@ -0,0 +1,24 @@
# SuperVM System Documentation
This document contains descriptions of the SuperVM host system which
utilized a SPU Mark I to execute code.
## Executable Program Format EXP
SuperVM uses a simple format for storing its executables.
The format is an archive which stores key-value-pairs for configuration
and sections containing code or data.
### Sections
A section can be either code or data and has a defined memory position and length.
## System Memory
SuperVM provides access to up to 4 GBs of RAM with the x86 paging concept:
The memory is organized in page tables and a page directories. The directory
contains pointers to a page table which then contains the pointers to 4096 byte
large pages.

View file

@ -0,0 +1,115 @@
# DasOS Virtual Device
DasOS emulates a virtual device that is shared between
all platform implementations. This device provides a
uniform environment with fixed specifications of hardware
and software as well as syscalls.
## Hardware
This section describes the virtual hardware components.
### CPU
The virtual device uses an *SPU Mark I* as the main processor.
### RAM
The *CPU* has a 32 MB *RAM* connected. In this RAM, both
data and code resides and the RAM has no memory protection.
### Screen
The screen connected to the *vGPU* has a resolution of
640x480 pixels and a color depth of 24 bits.
### vGPU
The *vGPU* provides access to the *Screen* and allows the
manipulation of its contents.
The gpu uses a double buffering system with a front and a
back buffer where as the front buffer is displayed on the
screen and the back buffer is modified by the CPU.
The gpu provides functions for printing text to the screen
as well as drawing visual primitives and clearing or swapping
the back buffer.
In addition to the features mentioned above, the back buffer is
accessible by a memory mapped area in the *RAM*.
### Persistent Storage
Two persistent storage devices are connected to the virtual device.
Both storage devices behave like IDE block devices where as the first
device is a fixed device and the second device is hot-pluggable.
The size and file format of the storage device is not specified.
## Software
This section describes the basic software components and interfaces
of the virtual device.
### BIOS
Each device implementation provides a *BIOS* that initialized
the device and provides host-dependant configuration.
### Boot Sequence
The boot sequence is initiated by the *BIOS* of the device
and can only be changed with a BIOS configuration.
- Determine existence of persistant storage devices
- Fixed device
- Swappable device
- Bootloading
- Searching for a fitting partition
- FAT32 formatted partition
- `/system.exp` must exist
- *Exploading* `/system.exp` into RAM
- Clearing *vGPU* text screen.
- Jump to *Entry Point*
### Memory Areas
#### Memory Mapped IO
The memory between `0x200000` and `0x400000` is reserved for
hardware use.
#### Entry Point
After successfully initialization, the virtual device jumps
to the address `0x400000` which is equal to the instruction
index `0x100000`.
### vGPU Interface
#### Back Buffer
The back buffer of the vGPU is organized in n RGB24 format
where pixels are stored in a 24 bpp format with R as the first
byte, G as the second and B as the last.
It is 900 kB large and maps the full screen area of 640x480 pixels
as a linear frame buffer from `0x200000` to `0x2E1000`.
#### MMIO Registers
The *vGPU* also uses some registers accessible via memory mapped io.
| Register | Address | Type | Description |
|----------|----------|------|-----------------------------------------|
| COLOR | 0x2E1000 | u32 | The color used for any draw commands. |
| CURSORX | 0x2E1004 | u16 | The x position of the draw cursor |
| CURSORY | 0x2E1006 | u16 | The y position of the draw cursor |
| SPRITE | 0x2E1008 | ptr | The address of the sprite to copy |
| WIDTH | 0x2E100C | u16 | The width of the sprite. |
| HEIGHT | 0x2E101E | u16 | The height of the sprite. |
| PITCH | 0x2E1010 | u32 | The pitch (memory width) of the sprite. |
#### HWIO Interface
The vGPU is controllable via the HWIO command of the SPU
with the command info >= `0x10000`.
| Command | CmdInfo | Input0 | Input1 | Description |
|---------|---------|--------|--------|-----------------------------------------------------------|
| Clear | 0x10000 | y | x | Clears the screen and moves the cursor to (x,y) |
| Line | 0x10001 | y | x | Draws a line from the cursor to (x,y). |
| Rect | 0x10002 | y | x | Draws a rectangle between the cursor and (x,y). |
| Fill | 0x10003 | y | x | Fills a rectangle between the cursor and (x,y). |
| Copy | 0x10004 | y | x | Renders the current sprite at (x,y) and moves the cursor. |
| Pixel | 0x10005 | y | x | Sets the pixel at (x,y). |
| Move | 0x10006 | y | x | Moves the cursor to (x,y). |
# TODO: Text Commands

View file

@ -1,8 +1,40 @@
##
# Build all projects
##
PROJECTS = $(shell ls --file-type | grep / | sed "s|/||")
PROJECTS = $(shell ls --file-type --ignore=libs --ignore=kernels --ignore=include | grep / | sed "s|/||")
all: $(PROJECTS)
LIBS = $(filter lib%, $(PROJECTS))
KERNELS = $(filter-out lib%, $(PROJECTS))
all: $(KERNELS) boot.img
.PHONY: $(PROJECTS)
$(PROJECTS):
make -C $@
$(KERNELS): dirs $(LIBS)
make -C $@ $(ARGS)
$(LIBS): dirs
make -C $@ $(ARGS)
dirs:
mkdir -p libs
mkdir -p kernels
boot.img: $(PROJECTS)
mformat -C -f 1440 -v VIDEO -i boot.img ::
mcopy -i boot.img \
kernels/* \
syslinux.cfg \
/boot/syslinux/libcom32.c32 \
/boot/syslinux/libutil.c32 \
/boot/syslinux/menu.c32 \
/boot/syslinux/mboot.c32 \
::
syslinux boot.img
mdir -i boot.img ::
deploy: $(KERNELS)
cp ./kernels/* /srv/tftp/
run: boot.img
qemu-system-i386 boot.img -serial stdio

View file

@ -1,45 +0,0 @@
# DasOS Concepts
## Memory Layout
| Start | Length | Length -h | Description |
|------------|------------|-----------|--------------------------------------------------------------------------|
| 0x00000000 | 0x40000000 | 1 GB | Kernel Space. This is where the kernel and its functionality is located. |
| 0x00100000 | ??? | ??? | Kernel entry point and load target. Here the kernel will be loaded. |
| 0x20000000 | 0x20000000 | 512 MB | Kernel Heap. This memory is used for dynamic allocations in the kernel. |
| 0x40000000 | 0xC0000000 | 3 GB | User Space. This is where Artifacts are loaded and executed. |
### Kernel Heap
A simple storage allocator.
Storage of:
- Task Descriptions
- Memory Mapping Information
- Location of loaded artifacts
- ...
## Starting a Task
### Requirements
- allocate task structure
- allocate mapping directory
- fill mapping directory with
- Lower End: Copy kernel mappings
- Upper End: Copy artifact data
- allocate task stack
## Executables, Libraries and Stuff
Artifact = Executable + Library + Shared Memory
### Artifact Types
| Type | Entry Point | Description |
|------------|-------------|--------------------------------------------------------------------------------------------------------------------------------------|
| Program | _main | A program targeted at user or system administrator. Can be executed with command line arguments. Uses stdin, stdout, stderr. |
| Library | _libmain | Can be loaded by other artifacts and allows utilization of a shared set of functions. |
| Service | _svcmain | A service is a background worker that won't be terminated when its main function returns. Can be 'woken up' by an external artifact. |
| Driver | _drvinit | A driver is loaded at system start and is allowed to request and dispatch interrupts. Can be used to create a flexible OS. |

View file

@ -1,10 +0,0 @@
[0-1] GB Kernel Space
[1-4] GB User Space
Entry Point: 0x40000000
Stack Start: 0xFFFFFFFF

View file

@ -1,11 +0,0 @@
.section multiboot
#define MB_MAGIC 0x1badb002
#define MB_FLAGS 0x0
#define MB_CHECKSUM -(MB_MAGIC + MB_FLAGS)
// Der Multiboot-Header
.align 4
.int MB_MAGIC
.int MB_FLAGS
.int MB_CHECKSUM

36
prototypes/common.mk Normal file
View file

@ -0,0 +1,36 @@
##
# DasOS common Makefile targets
##
INCLUDE_DIRS += ../include/ /opt/lib/gcc/i686-elf/6.1.0/include/
FLAGS += -ffreestanding -mno-sse -Werror -Wall -iquote include $(addprefix -I, $(INCLUDE_DIRS)) -O3 -g
ASFLAGS += $(FLAGS)
CFLAGS += $(FLAGS)
CXXFLAGS += $(FLAGS) -std=c++14 -fno-rtti -fno-exceptions -fno-leading-underscore -fno-use-cxa-atexit -nostdlib -fno-builtin
LDFLAGS += -L../libs/
SRCS += $(shell find -regextype egrep -regex '.*/.*\.(cpp|S|c)')
OBJS += $(addsuffix .o, $(notdir $(basename $(SRCS))))
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o obj/$@ $<
%.o: %.c
$(CC) $(CFLAGS) -c -o obj/$@ $<
%.o: %.S
$(AS) $(ASFLAGS) -c -o obj/$@ $<
%.o: src/%.cpp
$(CXX) $(CXXFLAGS) -c -o obj/$@ $<
%.o: src/%.c
$(CC) $(CFLAGS) -c -o obj/$@ $<
%.o: src/%.S
$(AS) $(ASFLAGS) -c -o obj/$@ $<
builddir:
mkdir -p ./obj/

12
prototypes/config.mk Normal file
View file

@ -0,0 +1,12 @@
##
# DasOS Makefile configuration
##
# Configure for target platform
CC=/opt/bin/i686-elf-gcc
CXX=/opt/bin/i686-elf-g++
LD=/opt/i686-elf/bin/ld
AR=/opt/i686-elf/bin/ar
AS=/opt/bin/i686-elf-gcc
LIBS += -L/opt/lib/gcc/i686-elf/6.1.0/

View file

@ -1,7 +1,7 @@
#pragma once
#include <stddef.h>
#include <inttypes.h>
#include <stdint.h>
#define ASM_READ_REG(reg, var) asm volatile("mov %%" #reg ", %0" : "=r" (var));
#define ASM_WRITE_REG(reg, var) asm volatile("mov %0, %%" #reg : : "r" (var));

View file

@ -0,0 +1,94 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
template<typename T>
struct NumericFormat
{
T value;
uint32_t base;
/**
* Applies a padding to the number.
* A positive number will apply padding the right side,
* a negative number will pad the left side.
*/
int32_t padding = 0;
char padchar = ' ';
NumericFormat(T value) : value(value), base(10) { }
NumericFormat(T value, uint32_t base) : value(value), base(base) { }
};
namespace utils
{
template<typename T>
auto hex(T value) { return NumericFormat<T>(value, 16); }
template<typename T>
auto dec(T value) { return NumericFormat<T>(value, 10); }
template<typename T>
auto oct(T value) { return NumericFormat<T>(value, 8); }
template<typename T>
auto bin(T value) { return NumericFormat<T>(value, 2); }
template<typename T>
auto nbase(T value, uint32_t base) { return NumericFormat<T>(value, base); }
template<typename T>
auto pad(NumericFormat<T> value, int32_t padding, char c = ' ') {
value.padding = padding;
return value;
}
template<typename T>
auto pad(T value, int32_t padding, char c = ' ') {
return pad(NumericFormat<T>(value), padding, c);
}
}
class CharacterDevice
{
private:
/**
* Prints the prefix for a given numeric base.
* @returns the prefix length.
*/
uint32_t printNumericPrefix(uint32_t base);
/**
* Prints a character several times.
*/
void putrep(char c, uint32_t repetitions);
public:
virtual void write(char c) = 0;
inline CharacterDevice & operator << (char c)
{
this->write(c);
return *this;
}
inline CharacterDevice & operator << (const char *str)
{
while(*str) {
*this << *str++;
}
return *this;
}
CharacterDevice & operator << (uint32_t value);
CharacterDevice & operator << (int32_t value);
CharacterDevice & operator << (void *value);
CharacterDevice & operator << (bool value);
template<typename T>
CharacterDevice & operator << (const NumericFormat<T> &fmt);
};

View file

@ -1,6 +1,6 @@
#pragma once
#include <inttypes.h>
#include <stdint.h>
struct CpuState
{

View file

@ -1,6 +1,6 @@
#pragma once
#include <inttypes.h>
#include <stdint.h>
class PIC
{

View file

@ -0,0 +1,39 @@
#pragma once
#include <stdint.h>
#include <chardev.hpp>
#define SERIAL_COM1 0x3F8
#define SERIAL_COM2 0x2F8
#define SERIAL_COM3 0x3E8
#define SERIAL_COM4 0x2E8
enum class Partiy
{
None = 0x0,
Even = 0x4,
Odd = 0x6,
High = 0x5,
Low = 0x7,
};
class SerialPort :
public CharacterDevice
{
private:
uint16_t mBase;
public:
SerialPort(
uint32_t portBase,
uint32_t baud = 9600,
Partiy parity = Partiy::None,
uint8_t dataBits = 8);
bool isTransmitEmpty() const;
bool isReceiveEmpty() const;
void write(char c) override;
char read();
};

View file

@ -1,6 +1,6 @@
#pragma once
#include <inttypes.h>
#include <stdint.h>
#include "driver.hpp"
namespace driver

View file

@ -0,0 +1,56 @@
#pragma once
#include <stdint.h>
#include <vbe.hpp>
#include "picture.hpp"
class VideoScreen :
public Picture
{
private:
static Pixel outOfScreen;
private:
Pixel *mFramebuffer;
uint32_t mPitch;
public:
const uint32_t mWidth, mHeight;
public:
VideoScreen(vbe::ModeInfo const & modeInfo);
uint32_t width() const override {
return this->mWidth;
}
uint32_t height() const override {
return this->mHeight;
}
Pixel & operator() (uint32_t x, uint32_t y) override {
if(x >= this->mWidth || y >= this->mHeight) {
return VideoScreen::outOfScreen;
} else {
return this->scanline(y)[x];
}
}
Pixel const & operator() (uint32_t x, uint32_t y) const override {
if(x >= this->mWidth || y >= this->mHeight) {
return VideoScreen::outOfScreen;
} else {
return this->scanline(y)[x];
}
}
private:
Pixel * scanline(uint32_t y) {
return reinterpret_cast<Pixel*>(
reinterpret_cast<uint32_t>(this->mFramebuffer) + y * this->mPitch
);
}
Pixel const * scanline(uint32_t y) const {
return reinterpret_cast<Pixel*>(
reinterpret_cast<uint32_t>(this->mFramebuffer) + y * this->mPitch
);
}
};

View file

@ -1,6 +1,6 @@
#pragma once
#include <inttypes.h>
#include <stdint.h>
namespace elf
{

View file

@ -3,6 +3,6 @@
enum class Error
{
#define ERROR(num, ident, desc) ident = num,
#include "errors.lst"
#include "lists/errors.lst"
#undef ERROR
};

View file

@ -5,6 +5,6 @@
enum class Exception
{
#define EXCEPTION(num, shorthand, ident, desc, type) ident = num,
#include "exceptions.lst"
#include "lists/exceptions.lst"
#undef EXCEPTION
};

View file

@ -1,6 +1,6 @@
#pragma once
#include <inttypes.h>
#include <stdint.h>
#include "enums.hpp"
enum class SegmentAccess : uint8_t

View file

@ -1,6 +1,6 @@
#pragma once
#include <inttypes.h>
#include <stdint.h>
#include "enums.hpp"
#include "cpustate.hpp"
@ -58,9 +58,11 @@ public:
explicit Interrupt(Handler handler);
};
extern "C" CpuState * interrupt_dispatch(CpuState *cpu);
class IDT
{
friend CpuState * interrupt_dispatch(CpuState *cpu);
public:
static const uint32_t length = 256;

View file

@ -4,3 +4,4 @@ ERROR(2, UnhandledException, An unhandled exception has occurred.)
ERROR(3, UnhandledInterrupt, An unhandled interrupt has occurred.)
ERROR(4, DriverAlreadyInstalled, A driver was already installed.)
ERROR(5, InvalidELFImage, The file beeing loaded is not a valid ELF file.)
ERROR(6, VMError, The virtual machine has failed.)

View file

@ -1,6 +1,7 @@
#pragma once
#include <inttypes.h>
#include <stdint.h>
#include <stddef.h>
#include "pointer.hpp"
#define MB_MEMSIZE (1<<0)
@ -18,6 +19,8 @@
#define MB_ASSERT_SIZE(type, len) static_assert(sizeof(type) == len, "multiboot::" #type " must be " #len " bytes large.")
#include "vbe.hpp"
namespace multiboot
{
template<typename T>
@ -154,8 +157,8 @@ namespace multiboot
const char * bootLoaderName;
const APMTable * apmTable;
struct {
uint32_t ontrolInfo;
uint32_t modeInfo;
uint32_t controlInfo;
vbe::ModeInfo const * modeInfo;
uint16_t mode;
uint16_t interfaceSegment;
uint16_t interfaceOffset;

29
prototypes/include/new Normal file
View file

@ -0,0 +1,29 @@
#ifndef _NEW
#define _NEW
#include <stddef.h>
#pragma GCC visibility push(default)
extern "C++" {
void* operator new(size_t) __attribute__((__externally_visible__));
void* operator new[](size_t) __attribute__((__externally_visible__));
void operator delete(void*) __attribute__((__externally_visible__));
void operator delete[](void*) __attribute__((__externally_visible__));
#if __cpp_sized_deallocation
void operator delete(void*, size_t) __attribute__((__externally_visible__));
void operator delete[](void*, size_t) __attribute__((__externally_visible__));
#endif
// Default placement versions of operator new.
inline void* operator new(size_t, void* __p) { return __p; }
inline void* operator new[](size_t, void* __p) { return __p; }
// Default placement versions of operator delete.
inline void operator delete (void*, void*) { }
inline void operator delete[](void*, void*) { }
} // extern "C++"
#endif

View file

@ -1,6 +1,6 @@
#pragma once
#include <inttypes.h>
#include <stdint.h>
#include <stddef.h>
class Numeric

View file

@ -0,0 +1,25 @@
#pragma once
#include <stdint.h>
struct Pixel
{
uint8_t b;
uint8_t g;
uint8_t r;
uint8_t a;
} __attribute__((packed));
static inline Pixel COLOR(uint8_t r, uint8_t g, uint8_t b)
{
return Pixel { b, g, r, 0xFF };
}
class Picture
{
public:
virtual uint32_t width() const = 0;
virtual uint32_t height() const = 0;
virtual Pixel & operator() (uint32_t x, uint32_t y) = 0;
virtual Pixel const & operator() (uint32_t x, uint32_t y) const = 0;
};

View file

@ -1,6 +1,6 @@
#pragma once
#include <inttypes.h>
#include <stdint.h>
/**
* Provides a strong pointer wrapper which can be used to address

View file

@ -1,6 +1,6 @@
#pragma once
#include <inttypes.h>
#include <stdint.h>
enum class Color : uint8_t
{

View file

@ -0,0 +1,39 @@
#pragma once
#include <stddef.h>
#define VBE_FAR(name) uint32_t name;
namespace vbe
{
struct ModeInfo
{
uint16_t attributes;
uint8_t winA,winB;
uint16_t granularity;
uint16_t winsize;
uint16_t segmentA, segmentB;
VBE_FAR(realFctPtr);
uint16_t pitch; // bytes per scanline
struct {
uint16_t x;
uint16_t y;
} __attribute__ ((packed)) res;
uint8_t Wchar, Ychar;
uint8_t planes, bpp, banks;
uint8_t memoryModel, bankSize, imagePages;
uint8_t reserved0;
uint8_t redMask, redPosition;
uint8_t greenMask, greenPosition;
uint8_t blueMask, bluePosition;
uint8_t rsvMask, rsvPosition;
uint8_t directcolor_attributes;
uint32_t framebuffer;
uint32_t reserved1;
uint16_t reserved2;
} __attribute__((packed));
}

7
prototypes/kernel.mk Normal file
View file

@ -0,0 +1,7 @@
$(KERNEL): $(OBJS)
$(LD) -T../linker.ld -o ../kernels/$@ $(addprefix obj/, $^) $(LDFLAGS) $(LIBS)
deploy: $(KERNEL)
cp ../kernels/$(KERNEL) /srv/tftp/$(KERNEL)

View file

@ -0,0 +1,12 @@
##
# Build base library parts of the libc.
##
include ../config.mk
LIBRARY = libbase.a
all: builddir $(LIBRARY)
include ../common.mk
include ../library.mk

View file

@ -0,0 +1,133 @@
#include <numeric.hpp>
static char getDigit(uint32_t i)
{
if(i >= 0 && i <= 9) {
return '0' + i;
}
return 'A' + (i-10);
}
size_t Numeric::toString(
char *buffer,
size_t length,
uint32_t number,
uint32_t radix)
{
if(length == 0) {
return 0;
}
if(number == 0) {
buffer[0] = '0';
return 1;
}
size_t len = 0;
while(number > 0)
{
buffer[len++] = getDigit(number % radix);
if(len >= length)
break;
number /= radix;
}
int half = len / 2;
for(int i = 0; i < half; i++)
{
char c = buffer[i];
buffer[i] = buffer[len - i - 1];
buffer[len - i - 1] = c;
}
return len;
}
size_t Numeric::toString(
char *buffer,
size_t length,
int32_t number,
uint32_t radix)
{
if(length == 0) {
return 0;
}
if(number == 0) {
buffer[0] = '0';
return 1;
}
if(number < 0) {
buffer[0] = '-';
length -= 1;
if(length == 0) {
return 0;
}
return Numeric::toString(&buffer[1], length, static_cast<uint32_t>(-number), radix) + 1;
}
else {
return Numeric::toString(buffer, length, static_cast<uint32_t>(number), radix);
}
}
size_t Numeric::toString(
char *buffer,
size_t length,
uint64_t number,
uint32_t radix)
{
if(length == 0) {
return 0;
}
if(number == 0) {
buffer[0] = '0';
return 1;
}
size_t len = 0;
while(number > 0)
{
buffer[len++] = getDigit(number % radix);
if(len >= length)
break;
number /= radix;
}
int half = len / 2;
for(int i = 0; i < half; i++)
{
char c = buffer[i];
buffer[i] = buffer[len - i - 1];
buffer[len - i - 1] = c;
}
return len;
}
size_t Numeric::toString(
char *buffer,
size_t length,
int64_t number,
uint32_t radix)
{
if(length == 0) {
return 0;
}
if(number == 0) {
buffer[0] = '0';
return 1;
}
if(number < 0) {
buffer[0] = '-';
length -= 1;
if(length == 0) {
return 0;
}
return Numeric::toString(&buffer[1], length, static_cast<uint64_t>(-number), radix) + 1;
}
else {
return Numeric::toString(buffer, length, static_cast<uint64_t>(number), radix);
}
}

View file

@ -0,0 +1,19 @@
char * strcpy(char * dst, const char *src)
{
char * tmp = dst;
while(((*tmp++) = (*src++)));
return dst;
}
char *strcat(char * dst, const char *src)
{
char * tmp = dst;
while(*tmp++);
tmp--;
while(((*tmp++) = (*src++)));
return dst;
}

View file

@ -0,0 +1,12 @@
##
# Build multiboot entry point library.
##
include ../config.mk
LIBRARY = libboot.a
all: builddir $(LIBRARY)
include ../common.mk
include ../library.mk

View file

@ -2,8 +2,8 @@
typedef void (*constructor)();
constructor start_ctors;
constructor end_ctors;
extern constructor start_ctors;
extern constructor end_ctors;
void compat_call_ctors()
{

View file

@ -0,0 +1,12 @@
##
# Build libchardev.
##
include ../config.mk
LIBRARY = libchardev.a
all: builddir $(LIBRARY)
include ../common.mk
include ../library.mk

View file

@ -0,0 +1,105 @@
#include <chardev.hpp>
#include <numeric.hpp>
CharacterDevice & CharacterDevice::operator << (uint32_t value)
{
char buffer[12];
size_t len = Numeric::toString(buffer, sizeof(buffer), value, 10);
for(size_t i = 0; i < len; i++) {
this->write(buffer[i]);
}
return *this;
}
CharacterDevice & CharacterDevice::operator << (int32_t value)
{
char buffer[13];
size_t len = Numeric::toString(buffer, sizeof(buffer), value, 10);
for(size_t i = 0; i < len; i++) {
this->write(buffer[i]);
}
return *this;
}
CharacterDevice & CharacterDevice::operator << (void *value)
{
char buffer[13];
size_t len = Numeric::toString(buffer, sizeof(buffer), reinterpret_cast<uint32_t>(value), 16);
for(size_t i = 0; i < len; i++) {
this->write(buffer[i]);
}
return *this;
}
CharacterDevice & CharacterDevice::operator << (bool value)
{
if(value == true) {
*this << "true";
} else {
*this << "false";
}
return *this;
}
#define NUMERIC_FMT_HANDLER char buffer[13]; \
size_t prefixlen = this->printNumericPrefix(fmt.base); \
size_t len = Numeric::toString(buffer, sizeof(buffer), fmt.value, fmt.base); \
int delta = prefixlen + len; \
if(fmt.padding < 0 && delta < -fmt.padding) { \
this->putrep(fmt.padchar, -fmt.padding - delta); \
} \
for(size_t i = 0; i < len; i++) { \
this->write(buffer[i]); \
} \
if(fmt.padding > 0 && delta < fmt.padding) { \
this->putrep(fmt.padchar, fmt.padding - delta); \
} \
return *this
template<>
CharacterDevice & CharacterDevice::operator << <uint32_t>(const NumericFormat<uint32_t> & fmt)
{
NUMERIC_FMT_HANDLER;
}
template<>
CharacterDevice & CharacterDevice::operator << <int32_t>(const NumericFormat<int32_t> & fmt)
{
NUMERIC_FMT_HANDLER;
}
template<>
CharacterDevice & CharacterDevice::operator << <uint64_t>(const NumericFormat<uint64_t> & fmt)
{
NUMERIC_FMT_HANDLER;
}
template<>
CharacterDevice & CharacterDevice::operator << <int64_t>(const NumericFormat<int64_t> & fmt)
{
NUMERIC_FMT_HANDLER;
}
uint32_t CharacterDevice::printNumericPrefix(uint32_t base)
{
switch(base) {
case 2: *this << "0b"; return 2;
case 8: *this << "0o"; return 2;
case 10: return 0;
case 16: *this << "0x"; return 2;
default:
*this << "[" << base << "]x";
if(base < 10) return 4;
if(base < 100) return 5;
return 6;
}
}
void CharacterDevice::putrep(char c, uint32_t repetitions)
{
for(uint32_t i = 0; i < repetitions; i++)
this->write(c);
}

3
prototypes/library.mk Normal file
View file

@ -0,0 +1,3 @@
$(LIBRARY): $(OBJS)
$(AR) rcs ../libs/$(LIBRARY) $(addprefix obj/, $^)

View file

@ -0,0 +1,12 @@
##
# Build serial port library.
##
include ../config.mk
LIBRARY = libserial.a
all: builddir $(LIBRARY)
include ../common.mk
include ../library.mk

View file

@ -0,0 +1,66 @@
#include <driver/serial.hpp>
#include <io.hpp>
// Code adapted from:
// http://www.lowlevel.eu/wiki/Serielle_Schnittstelle
#define IER 1
#define IIR 2
#define FCR 2
#define LCR 3
#define MCR 4
#define LSR 5
#define MSR 6
SerialPort::SerialPort(uint32_t portBase, uint32_t baud, Partiy parity, uint8_t dataBits) :
mBase(portBase)
{
union {
uint8_t b[2];
uint16_t w;
} divisor;
divisor.w = 115200 / baud;
// Interrupt ausschalten
outb(this->mBase + IER, 0x00);
// DLAB-Bit setzen
outb(this->mBase + LCR, 0x80);
// Teiler (low) setzen
outb(this->mBase + 0, divisor.b[0]);
// Teiler (high) setzen
outb(this->mBase + 1, divisor.b[1]);
int iparity = (int)parity;
// Anzahl Bits, Parität, usw setzen (DLAB zurücksetzen)
outb(this->mBase + LCR, ((iparity&0x7)<<3)|((dataBits-5)&0x3));
// Initialisierung abschließen
outb(this->mBase + FCR, 0xC7);
outb(this->mBase + MCR, 0x0B);
}
bool SerialPort::isTransmitEmpty() const
{
return inb(this->mBase + LSR) & 0x20;
}
bool SerialPort::isReceiveEmpty() const
{
return (inb(this->mBase + LSR) & 1) == 0;
}
void SerialPort::write(char c)
{
while (this->isTransmitEmpty() == false);
outb(this->mBase, c);
}
char SerialPort::read()
{
while (this->isReceiveEmpty());
return inb(this->mBase);
}

View file

@ -0,0 +1,12 @@
##
# Build video library.
##
include ../config.mk
LIBRARY = libvideo.a
all: builddir $(LIBRARY)
include ../common.mk
include ../library.mk

View file

@ -0,0 +1,12 @@
#include <driver/video.hpp>
Pixel VideoScreen::outOfScreen;
VideoScreen::VideoScreen(vbe::ModeInfo const & modeInfo) :
mFramebuffer((Pixel*)modeInfo.framebuffer),
mPitch(modeInfo.pitch),
mWidth(modeInfo.res.x),
mHeight(modeInfo.res.y)
{
}

View file

@ -14,8 +14,8 @@ SECTIONS
}
.data ALIGN(4096) : {
start_ctors = .;
KEEP(*( .init_array ));
KEEP(*(SORT_BY_INIT_PRIORITY( .init_array.* )));
KEEP(*( .ctors ));
KEEP(*(SORT_BY_INIT_PRIORITY( .ctors.* )));
end_ctors = .;
start_dtors = .;

View file

@ -0,0 +1,14 @@
##
# Builds the os initialization kernel
##
include ../config.mk
KERNEL = os-init.ker
LIBS += -lbase -lgcc -lboot
FLAGS += -D IDT_DISPATCH=interrupt_dispatch
all: builddir $(KERNEL)
include ../common.mk
include ../kernel.mk

View file

@ -1,25 +1,23 @@
#include "console.hpp"
#include "pmm.hpp"
#include "numeric.hpp"
#include "pointer.hpp"
#include "multiboot.hpp"
#include "gdt.hpp"
#include "idt.hpp"
#include "compat.h"
#include "io.hpp"
#include "vmm.hpp"
#include "elf.hpp"
#include "bsod.hpp"
#include <console.hpp>
#include <kernel/pmm.hpp>
#include <numeric.hpp>
#include <pointer.hpp>
#include <multiboot.hpp>
#include <kernel/gdt.hpp>
#include <kernel/idt.hpp>
#include <io.hpp>
#include <kernel/vmm.hpp>
#include <elf.hpp>
#include <kernel/bsod.hpp>
#include "asm.hpp"
#include <asm.hpp>
#include "driver/timer.hpp"
#include "driver/keyboard.hpp"
#include <inttypes.h>
#include <stdint.h>
#include <new>
#include <string.h>
using namespace multiboot;
using namespace console_tools;
@ -35,76 +33,6 @@ driver::Keyboard keyboardDriver;
VMMContext * kernelContext;
static const uint32_t entryPointAddress = 0x40000000;
void run_program0(Module const & module)
{
using namespace elf;
const Header *header = module.start.data<elf::Header>();
ProgramHeader *ph;
int i;
if (header->magic != MAGIC) {
BSOD::die(Error::InvalidELFImage, "Keine gueltige ELF-Magic!\n");
return;
}
ph = (ProgramHeader*)(((char*) header) + header->ph_offset);
for (i = 0; i < header->ph_entry_count; i++, ph++) {
void* dest = (void*) ph->virt_addr;
void* src = ((char*) header) + ph->offset;
/* Nur Program Header vom Typ LOAD laden */
if (ph->type != 1) {
continue;
}
if(ph->virt_addr < entryPointAddress) {
BSOD::die(Error::InvalidELFImage, "A LOAD section tried to sneak into the kernel!");
}
for(uint32_t i = 0; i < ph->mem_size; i += 0x1000) {
kernelContext->provide(
virtual_t(ph->virt_addr + i),
VMMFlags::Writable | VMMFlags::UserSpace);
}
memset(dest, 0, ph->mem_size);
memcpy(dest, src, ph->file_size);
}
using EntryPoint = void (*)();
EntryPoint ep = (EntryPoint)entryPointAddress;
ep();
}
static void dump_elf(elf::Header *header)
{
using namespace elf;
ProgramHeader *ph;
int i;
/* Ist es ueberhaupt eine ELF-Datei? */
if (header->magic != MAGIC) {
BSOD::die(Error::InvalidELFImage, "Keine gueltige ELF-Magic!\n");
return;
}
ph = (ProgramHeader*)(((char*) header) + header->ph_offset);
for (i = 0; i < header->ph_entry_count; i++, ph++) {
void* dest = (void*) ph->virt_addr;
void* src = ((char*) header) + ph->offset;
Console::main
<< "Header: " << ph->type << ", "
<< "Source: " << src << ", "
<< "Dest: " << dest << ", "
<< "Memsize: " << ph->mem_size << ", "
<< "Filesize: " << ph->file_size
<< "\n";
}
}
static void initializePMM(Structure const & data)
{
for(auto &mmap : data.memoryMaps) {
@ -157,6 +85,7 @@ static void initializePMM(Structure const & data)
extern "C" void init(Structure const & data)
{
Console::main.clear();
Console::main
<< "Hello World!\n"
<< FColor(Color::Yellow) << "Hello color!" << FColor() << "\n"
@ -210,18 +139,9 @@ extern "C" void init(Structure const & data)
Console::main << "Drivers installed.\n";
//asm volatile("sti");
ASM::sti();
Console::main << "Interrupts enabled.\n";
if(data.modules.length > 0)
{
Console::main << "ELF Modukle:\n";
dump_elf(data.modules[0].start.data<elf::Header>());
run_program0(data.modules[0]);
}
while(true);
}

View file

@ -0,0 +1,28 @@
.section multiboot
#define MB_MAGIC 0x1badb002
#define MB_FLAGS 0x00
#define MB_CHECKSUM -(MB_MAGIC + MB_FLAGS)
.align 4
// Offset Type Field Name Note
// 0 u32 magic required
// 4 u32 flags required
// 8 u32 checksum required
.int MB_MAGIC
.int MB_FLAGS
.int MB_CHECKSUM
// 12 u32 header_addr if flags[16] is set
// 16 u32 load_addr if flags[16] is set
// 20 u32 load_end_addr if flags[16] is set
// 24 u32 bss_end_addr if flags[16] is set
// 28 u32 entry_addr if flags[16] is set
.int 0, 0, 0, 0, 0
// 32 u32 mode_type if flags[2] is set
// 36 u32 width if flags[2] is set
// 40 u32 height if flags[2] is set
// 44 u32 depth if flags[2] is set
.int 0, 0, 0, 0

View file

@ -4,23 +4,27 @@ CC=gcc
CXX=g++
LD=ld
LIBGCC = $(shell gcc -m32 -print-libgcc-file-name)
LIBSTD = ../stdlib/libstd.a
LIBVM = ../supervm/libvm.a
IDT_DISPATCH = _ZN3IDT8dispatchEP8CpuState
FLAGS = -mno-sse -DIDT_DISPATCH=$(IDT_DISPATCH) -ffreestanding -m32 -Werror -Wall -iquote include -iquote lists -I../stdlib/include -O3 -g
INCLUDE_DIRS = ../stdlib/include ../supervm/
FLAGS = -mno-sse -DIDT_DISPATCH=$(IDT_DISPATCH) -ffreestanding -m32 -Werror -Wall -iquote include -iquote lists $(addprefix -I, $(INCLUDE_DIRS)) -O3 -g
ASFLAGS = $(FLAGS)
CFLAGS = $(FLAGS)
CXXFLAGS = $(FLAGS) -std=c++14 -fno-rtti -fno-exceptions -fno-leading-underscore -fno-use-cxa-atexit -nostdlib -fno-builtin
SRCS = $(shell find -regextype egrep -regex '.*/.*\.(cpp|S|c)')
OBJS = $(addsuffix .o, $(notdir $(basename $(SRCS))))
LIBGCC = $(shell gcc -m32 -print-libgcc-file-name)
LIBSTD = ../stdlib/libstd.a
LIBS = $(LIBGCC) $(LIBSTD) $(LIBVM)
all: kernel-base.ker
kernel-base.ker: $(OBJS)
$(LD) -melf_i386 -Tlinker.ld -o kernel-base.ker $(addprefix obj/, $^) $(LIBGCC) $(LIBSTD)
$(LD) -melf_i386 -Tlinker.ld -o kernel-base.ker $(addprefix obj/, $^) $(LIBS)
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o obj/$@ $<
@ -46,8 +50,8 @@ kernel-base.ker: $(OBJS)
# -initrd file use 'file' as initial ram disk
# -dtb file use 'file' as device tree image
run:
echo `pwd`
qemu-system-i386 \
-kernel kernel-base.ker \
-m 64 \
@ -55,7 +59,7 @@ run:
-no-reboot \
-no-shutdown \
-serial stdio \
-initrd ../program0/program
-initrd `pwd`/../supervm-asm/testcode.bin
insight:
objdump -d kernel-base.ker | c++filt | less

View file

@ -1,13 +1,13 @@
#include "bsod.hpp"
#include "console.hpp"
#include "exceptions.hpp"
#include <kernel/bsod.hpp>
#include <console.hpp>
#include <exceptions.hpp>
static const char *toString(int interrupt)
{
if(interrupt <= 0x1f) {
switch(interrupt) {
#define EXCEPTION(num, shorthand, ident, desc, type) case num: return #desc;
#include "exceptions.lst"
#include <lists/exceptions.lst>
#undef EXCEPTION
default: return "Unknown Exception";
}
@ -15,7 +15,7 @@ static const char *toString(int interrupt)
if(interrupt >= 0x20 && interrupt <= 0x2F) {
switch(interrupt - 0x20) {
#define IRQ(num, ident, desc) case num: return #desc;
#include "irqs.lst"
#include <lists/irqs.lst>
#undef IRQ
}
}
@ -61,7 +61,7 @@ void BSOD::die(Error code, const char *msg, CpuState *cpu)
<< "CODE: " << Y;
switch(code) {
#define ERROR(num, ident, desc) case Error::ident: Console::main << #ident << " / " << #desc << "\n"; break;
#include "errors.lst"
#include <lists/errors.lst>
#undef ERROR
}
Console::main

View file

@ -1,4 +1,4 @@
#include "gdt.hpp"
#include <kernel/gdt.hpp>
static SegmentDescriptor invalid;
SegmentDescriptor GDT::descriptors[GDT::length];

View file

@ -1,11 +1,11 @@
#include "idt.hpp"
#include "asm.hpp"
#include "bsod.hpp"
#include "pic.hpp"
#include <kernel/idt.hpp>
#include <asm.hpp>
#include <kernel/bsod.hpp>
#include <driver/pic.hpp>
#define ISR(num) extern "C" void isr_##num();
#define ISR_ERR(num) ISR(num)
#include "interrupts.lst"
#include <lists/interrupts.lst>
#undef ISR
#undef ISR_ERR
@ -38,7 +38,7 @@ void IDT::initialize()
0x08, \
InterruptFlags::Interrupt | InterruptFlags::Use32Bit | InterruptFlags::Ring0 | InterruptFlags::Present);
#define ISR_ERR(num) ISR(num)
#include "interrupts.lst"
#include <lists/interrupts.lst>
#undef ISR
#undef ISR_ERR
@ -56,6 +56,11 @@ void IDT::setupPIC()
slavePIC.maskInterrupts(0x00);
}
extern "C" CpuState * interrupt_dispatch(CpuState *cpu)
{
return IDT::dispatch(cpu);
}
CpuState * IDT::dispatch(CpuState *cpu)
{
bool ackMaster = cpu->interrupt >= 0x20 && cpu->interrupt <= 0x2F;

View file

@ -16,7 +16,7 @@ isr_\nr:
#define ISR(num) isr_stub num
#define ISR_ERR(num) isr_stub_with_err num
#include "interrupts.lst"
#include <lists/interrupts.lst>
#undef ISR
#undef ISR_ERR

View file

@ -1,6 +1,6 @@
#include "driver/keyboard.hpp"
#include "idt.hpp"
#include "console.hpp"
#include <driver/keyboard.hpp>
#include <kernel/idt.hpp>
#include <console.hpp>
namespace driver
{

View file

@ -1,5 +1,5 @@
#include "pic.hpp"
#include "io.hpp"
#include <driver/pic.hpp>
#include <io.hpp>
PIC masterPIC(0x20);
PIC slavePIC(0xA0);

View file

@ -1,7 +1,7 @@
#include <inttypes.h>
#include <stdint.h>
#include <stddef.h>
#include "pmm.hpp"
#include "bsod.hpp"
#include <kernel/pmm.hpp>
#include <kernel/bsod.hpp>
/**
* Number stored of pages in the bitmap

View file

@ -1,5 +1,5 @@
#include <inttypes.h>
#include "screen.hpp"
#include <stdint.h>
#include <screen.hpp>
ScreenChar Screen::outOfScreen;
Screen Screen::main((ScreenChar*)0xb8000, 80, 25);

View file

@ -1,5 +1,5 @@
#include "driver/timer.hpp"
#include "idt.hpp"
#include <driver/timer.hpp>
#include <kernel/idt.hpp>
namespace driver
{

View file

@ -1,7 +1,7 @@
#include "vmm.hpp"
#include <kernel/vmm.hpp>
#include <new>
#include "console.hpp"
#include "asm.hpp"
#include <console.hpp>
#include <asm.hpp>
void VMM::enable()
{

View file

@ -1,43 +0,0 @@
AS=gcc
CC=gcc
CXX=g++
LD=ld
FLAGS = -mno-sse -DIDT_DISPATCH=$(IDT_DISPATCH) -ffreestanding -m32 -Werror -Wall -iquote include -iquote lists -O3 -g
ASFLAGS = $(FLAGS)
CFLAGS = $(FLAGS)
CXXFLAGS = $(FLAGS) -std=c++14 -fno-rtti -fno-exceptions -fno-leading-underscore -fno-use-cxa-atexit -nostdlib -fno-builtin
ARTIFACT = program
SRCS = $(shell find -regextype egrep -regex '.*/.*\.(cpp|S|c)')
OBJS = $(addsuffix .o, $(notdir $(basename $(SRCS))))
LIBGCC = $(shell gcc -m32 -print-libgcc-file-name)
all: $(ARTIFACT)
$(ARTIFACT): $(OBJS)
$(LD) -Tlinker.ld -o $(ARTIFACT) $(addprefix obj/, $^) $(LIBGCC)
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o obj/$@ $<
%.o: %.c
$(CC) $(ASFLAGS) -c -o obj/$@ $<
%.o: %.S
$(AS) $(CFLAGS) -c -o obj/$@ $<
%.o: src/%.cpp
$(CXX) $(CXXFLAGS) -c -o obj/$@ $<
%.o: src/%.c
$(CC) $(ASFLAGS) -c -o obj/$@ $<
%.o: src/%.S
$(AS) $(CFLAGS) -c -o obj/$@ $<
deploy: $(ARTIFACT)
cp $(ARTIFACT) /srv/tftp/$(ARTIFACT)

View file

@ -1,30 +0,0 @@
ENTRY(_start)
OUTPUT_FORMAT(elf32-i386)
OUTPUT_ARCH(i386:i386)
SECTIONS
{
. = 0x40000000;
.text : {
*(.text)
}
.data ALIGN(4096) : {
start_ctors = .;
KEEP(*( .init_array ));
KEEP(*(SORT_BY_INIT_PRIORITY( .init_array.* )));
end_ctors = .;
start_dtors = .;
KEEP(*( .fini_array ));
end_dtors = .;
*(.data)
}
.rodata ALIGN(4096) : {
*(.rodata)
}
.bss ALIGN(4096) : {
*(.bss)
}
. = ALIGN(4096);
}

Binary file not shown.

View file

@ -1,11 +0,0 @@
#include <inttypes.h>
uint16_t *video = (uint16_t*)0xB8000;
void _start()
{
for(int i = 0; i < 256; i++) {
video[i] = 0xF000 | i;
}
while(1);
}

View file

@ -1,42 +0,0 @@
AS=gcc
CC=gcc
CXX=g++
LD=ld
AR=ar
FLAGS = -mno-sse -ffreestanding -m32 -Werror -Wall -iquote include -O3 -g
ASFLAGS = $(FLAGS)
CFLAGS = $(FLAGS)
CXXFLAGS = $(FLAGS) -std=c++14 -fno-rtti -fno-exceptions -fno-leading-underscore -fno-use-cxa-atexit -nostdlib -fno-builtin
ARTIFACT = libstd.a
SRCS = $(shell find -regextype egrep -regex '.*/.*\.(cpp|S|c)')
OBJS = $(addsuffix .o, $(notdir $(basename $(SRCS))))
all: $(ARTIFACT)
$(ARTIFACT): $(OBJS)
$(AR) rcs $(ARTIFACT) $(addprefix obj/, $^)
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o obj/$@ $<
%.o: %.c
$(CC) $(ASFLAGS) -c -o obj/$@ $<
%.o: %.S
$(AS) $(CFLAGS) -c -o obj/$@ $<
%.o: src/%.cpp
$(CXX) $(CXXFLAGS) -c -o obj/$@ $<
%.o: src/%.c
$(CC) $(ASFLAGS) -c -o obj/$@ $<
%.o: src/%.S
$(AS) $(CFLAGS) -c -o obj/$@ $<
deploy: $(ARTIFACT)
cp $(ARTIFACT) /srv/tftp/$(ARTIFACT)

View file

@ -1,16 +0,0 @@
#pragma once
#include <stddef.h>
#if defined(__cplusplus)
extern "C" {
#endif
void * memcpy ( void * destination, const void * source, size_t num );
void * memset ( void * ptr, int value, size_t num );
#if defined(__cplusplus)
}
#endif

View file

@ -1,20 +0,0 @@
#include "string.h"
void * memcpy ( void * destination, const void * source, size_t num )
{
char *d = destination;
char const * s = source;
for(size_t i = 0; i < num; i++) {
d[i] = s[i];
}
return destination;
}
void * memset ( void * ptr, int value, size_t num )
{
char *p = ptr;
for(size_t i = 0; i < num; i++) {
p[i] = value;
}
return ptr;
}

View file

@ -1 +0,0 @@
*.bin

View file

@ -1,7 +0,0 @@
CSC = mcs
all: assembler.exe
assembler.exe: assembler.cs
$(CSC) -out:$@ -target:exe $^

View file

@ -1 +0,0 @@
#!/usr/bin/mono assembler.exe

View file

@ -1,493 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace supervm_asm
{
class Program
{
static void Main(string[] args)
{
if(args.Contains("-gen-code"))
{
MnemonicParser.GenerateFromDocumentation(
@"../supervm/supervm.md");
return;
}
foreach(var file in args.Where(a => !a.StartsWith("-") && Path.GetExtension(a) == ".asm"))
{
var output = Path.ChangeExtension(file, ".bin");
var assembly = Assembler.Assemble(File.ReadAllText(file));
var code = assembly.Code;
Console.WriteLine("{0}*{1}:", output, code.Count);
for (int i = 0; i < code.Count; i++)
{
Console.Write("; {0:D3} ", i);
PrintInstruction(code[i], assembly.Annotation[i]);
}
using(var fs = File.Open(output, FileMode.Create, FileAccess.Write))
{
for(int i = 0; i < code.Count; i++)
{
var bits = BitConverter.GetBytes(code[i]);
if(BitConverter.IsLittleEndian == false)
{
bits = bits.Reverse().ToArray();
}
fs.Write(bits, 0, bits.Length);
}
}
}
}
static void PrintInstruction(ulong instr, string comment)
{
var str = Convert.ToString((long)instr, 2).PadLeft(64, '0');
var portions = new []
{
new { Start = 0, Length = 32, Color = ConsoleColor.Red },
new { Start = 32, Length = 2, Color = ConsoleColor.DarkGreen },
new { Start = 34, Length = 1, Color = ConsoleColor.Green },
new { Start = 35, Length = 16, Color = ConsoleColor.Magenta },
new { Start = 51, Length = 6, Color = ConsoleColor.Yellow },
new { Start = 57, Length = 1, Color = ConsoleColor.DarkCyan },
new { Start = 58, Length = 2, Color = ConsoleColor.Cyan },
new { Start = 60, Length = 2, Color = ConsoleColor.DarkBlue },
new { Start = 62, Length = 2, Color = ConsoleColor.Blue },
};
var fg = Console.ForegroundColor;
foreach(var portion in portions)
{
Console.ForegroundColor = portion.Color;
Console.Write("{0} ", str.Substring(portion.Start, portion.Length));
}
Console.ForegroundColor = fg;
Console.WriteLine(" {0}", comment);
}
}
public class VMAssembly
{
private readonly ulong[] code;
private readonly string[] origins;
public VMAssembly(ulong[] code, string[] origins)
{
this.code = code;
this.origins = origins;
}
public IReadOnlyList<ulong> Code => this.code;
public IReadOnlyList<string> Annotation => this.origins;
}
public static class Assembler
{
static Regex annotationMatcher = new Regex(@"\[\s*(.*?)\s*\]", RegexOptions.Compiled);
static Regex labelMatcher = new Regex(@"^(\w+):\s*(.*)\s*$", RegexOptions.Compiled);
static Regex instructionMatcher = new Regex(@"(\w+)(?:\s+([@-]?\w+|'.'))?", RegexOptions.Compiled);
public static VMAssembly Assemble(string src)
{
var lines = src.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
var patches = new Dictionary<int, string>();
var labels = new Dictionary<string, int>();
var code = new List<ulong>();
var source = new List<string>();
for (int i = 0; i < lines.Length; i++)
{
var line = lines[i].Trim();
{ // Process comments
var idx = line.IndexOf(';');
if (idx >= 0)
line = line.Substring(0, idx);
}
var uncommented = line;
{ // Process labels
var match = labelMatcher.Match(line);
if (match.Success)
{
var label = match.Groups[1].Value;
labels.Add(label, code.Count);
line = match.Groups[2].Value;
}
}
if (string.IsNullOrWhiteSpace(line))
continue;
var annotations = new HashSet<string>();
line = annotationMatcher.Replace(line, (m) =>
{
annotations.Add(m.Groups[1].Value.ToLower());
return "";
});
line = line.Trim();
{
var match = instructionMatcher.Match(line);
if (match.Success == false)
throw new InvalidOperationException("Invalid instruction: " + line);
var mnemonic = match.Groups[1].Value;
uint argument = 0;
if (match.Groups[2].Length > 0)
{
var argstring = match.Groups[2].Value;
if (argstring.StartsWith("@"))
{
// Add patch note for labels.
patches.Add(code.Count, argstring.Substring(1));
}
else if (argstring.StartsWith("'"))
{
argument = (uint)argstring[1];
}
else if (argstring.StartsWith("0x"))
{
argument = Convert.ToUInt32(argstring.Substring(2), 16);
}
else if (argstring.StartsWith("0b"))
{
argument = Convert.ToUInt32(argstring.Substring(2), 10);
}
else if (argstring.StartsWith("0d"))
{
argument = Convert.ToUInt32(argstring.Substring(2), 10);
}
else
{
if(argstring.StartsWith("-"))
{
unchecked
{
argument = (uint)Convert.ToInt32(argstring, 10);;
}
}
else
argument = Convert.ToUInt32(argstring, 10);
}
}
if(mnemonics.ContainsKey(mnemonic) == false)
{
throw new InvalidOperationException("Unknown mnemonic: " + mnemonic);
}
var instruction = mnemonics[mnemonic];
foreach(var annotation in annotations)
{
if(annotation.StartsWith("ci:"))
{
instruction.CommandInfo = UInt16.Parse(annotation.Substring(3));
continue;
}
if (annotation.StartsWith("cmd:"))
{
instruction.Command = (Command)Enum.Parse(typeof(Command), annotation.Substring(4));
continue;
}
switch (annotation)
{
case "f:yes":
instruction.ModifyFlags = true;
break;
case "f:no":
instruction.ModifyFlags = false;
break;
case "r:discard":
instruction.Output = OutputType.Discard;
break;
case "r:push":
instruction.Output = OutputType.Push;
break;
case "r:jump":
instruction.Output = OutputType.Jump;
break;
case "i0:zero":
instruction.Input0 = InputType.Zero;
break;
case "i0:pop":
instruction.Input0 = InputType.Pop;
break;
case "i0:peek":
instruction.Input0 = InputType.Peek;
break;
case "i0:arg":
instruction.Input0 = InputType.Argument;
break;
case "i1:zero":
instruction.Input1 = InputType.Zero;
break;
case "i1:pop":
instruction.Input1 = InputType.Pop;
break;
case "ex(z)=x":
instruction.ExecutionZ = ExecutionMode.Always;
break;
case "ex(z)=0":
instruction.ExecutionZ = ExecutionMode.Zero;
break;
case "ex(z)=1":
instruction.ExecutionZ = ExecutionMode.One;
break;
case "ex(n)=x":
instruction.ExecutionN = ExecutionMode.Always;
break;
case "ex(n)=0":
instruction.ExecutionN = ExecutionMode.Zero;
break;
case "ex(n)=1":
instruction.ExecutionN = ExecutionMode.One;
break;
default:
throw new InvalidOperationException("Unrecognized annotation: " + annotation);
}
}
ulong encoded = 0;
encoded |= ((uint)(instruction.ExecutionZ) << 0);
encoded |= ((uint)(instruction.ExecutionN) << 2);
encoded |= ((uint)(instruction.Input0) << 4);
encoded |= ((uint)(instruction.Input1) << 6);
encoded |= ((uint)(instruction.Command) << 7);
encoded |= ((uint)(instruction.CommandInfo) << 13);
encoded |= ((uint)(instruction.ModifyFlags ? 1 : 0) << 29);
encoded |= ((uint)(instruction.Output) << 30);
encoded |= ((ulong)argument << 32);
code.Add(encoded);
source.Add(uncommented);
}
}
{ // Install patches
foreach (var patch in patches)
{
var target = patch.Value;
var position = labels[target];
code[patch.Key] =
(code[patch.Key] & 0xFFFFFFFF) |
((ulong)position << 32);
}
}
return new VMAssembly(code.ToArray(), source.ToArray());
}
static Dictionary<string, Instruction> mnemonics = new Dictionary<string, Instruction>()
{
{ "nop", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Zero, Input1 = InputType.Zero, Command = Command.Copy, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } },
{ "push", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Argument, Input1 = InputType.Zero, Command = Command.Copy, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "drop", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Zero, Command = Command.Copy, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } },
{ "dup", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Peek, Input1 = InputType.Zero, Command = Command.Copy, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "jmp", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Argument, Input1 = InputType.Zero, Command = Command.Copy, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Jump, Argument = 0, } },
{ "jmpi", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Zero, Command = Command.Copy, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Jump, Argument = 0, } },
{ "ret", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Zero, Command = Command.Copy, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Jump, Argument = 0, } },
{ "load", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Argument, Input1 = InputType.Zero, Command = Command.Load, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "loadi", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Zero, Command = Command.Load, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "store", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Argument, Input1 = InputType.Pop, Command = Command.Store, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } },
{ "storei", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Store, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } },
{ "get", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Argument, Input1 = InputType.Zero, Command = Command.Get, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "geti", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Zero, Command = Command.Get, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "set", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Argument, Input1 = InputType.Pop, Command = Command.Set, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } },
{ "seti", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Set, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } },
{ "bpget", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Zero, Input1 = InputType.Zero, Command = Command.BpGet, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "bpset", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Zero, Command = Command.BpSet, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } },
{ "spget", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Zero, Input1 = InputType.Zero, Command = Command.SpGet, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "spset", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Zero, Command = Command.SpSet, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } },
{ "cpget", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Zero, Input1 = InputType.Zero, Command = Command.CpGet, CommandInfo = 1, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "add", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "sub", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 1, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "cmp", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 1, ModifyFlags = true, Output = OutputType.Discard, Argument = 0, } },
{ "mul", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 2, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "div", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 3, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "mod", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 4, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "and", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 5, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "or", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 6, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "xor", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 7, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "not", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Zero, Command = Command.Math, CommandInfo = 8, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "rol", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 9, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "ror", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 10, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "asl", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 11, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "asr", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 12, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "shl", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 13, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "shr", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Pop, Input1 = InputType.Pop, Command = Command.Math, CommandInfo = 14, ModifyFlags = false, Output = OutputType.Push, Argument = 0, } },
{ "syscall", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Zero, Input1 = InputType.Zero, Command = Command.SysCall, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } },
{ "hwio", new Instruction() { ExecutionZ = ExecutionMode.Always, ExecutionN = ExecutionMode.Always, Input0 = InputType.Zero, Input1 = InputType.Zero, Command = Command.HwIO, CommandInfo = 0, ModifyFlags = false, Output = OutputType.Discard, Argument = 0, } },
};
}
class MnemonicParser
{
public static void GenerateFromDocumentation(string file)
{
var relevantLines = File
.ReadAllLines(file)
.SkipWhile(l => l != "## Assembler Mnemonics")
.Skip(1)
.SkipWhile(l => string.IsNullOrWhiteSpace(l))
.TakeWhile(l => l.StartsWith("|"))
.Skip(2)
.ToArray();
var instructions = relevantLines
.Select(l => l
.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Trim())
.ToArray())
.ToDictionary(a => a[0], a => new Instruction()
{
ExecutionN = ExecutionMode.Always,
ExecutionZ = ExecutionMode.Always,
Input0 = ToInputMode(a[2]),
Input1 = ToInputMode(a[3]),
Command = ToCommand(a[4]),
CommandInfo = ushort.Parse(a[5]),
ModifyFlags = (a[7].ToLower() == "yes"),
Output = ToOutput(a[6]),
Argument = 0,
});
var fields = typeof(Instruction).GetFields();
foreach (var item in instructions)
{
// { "a", new Instruction() { } },
Console.Write("{{ \"{0}\", new Instruction() {{ ", item.Key);
foreach (var field in fields)
{
var value = field.GetValue(item.Value);
if (field.FieldType.IsEnum)
Console.Write("{0} = {1}.{2}, ", field.Name, field.FieldType.Name, value);
else
Console.Write("{0} = {1}, ", field.Name, value.ToString().ToLower());
}
Console.WriteLine(" } },");
}
}
private static OutputType ToOutput(string v)
{
switch (v.ToLower())
{
case "discard": return OutputType.Discard;
case "push": return OutputType.Push;
case "jump": return OutputType.Jump;
default:
throw new NotSupportedException();
}
}
private static Command ToCommand(string v)
{
return (Command)Enum.Parse(typeof(Command), v, true);
}
private static InputType ToInputMode(string v)
{
switch (v.ToLower())
{
case "zero": return InputType.Zero;
case "peek": return InputType.Peek;
case "pop": return InputType.Pop;
case "arg": return InputType.Argument;
default:
throw new NotSupportedException();
}
}
}
public struct Instruction
{
public ExecutionMode ExecutionZ;
public ExecutionMode ExecutionN;
public InputType Input0;
public InputType Input1;
public Command Command;
public ushort CommandInfo;
public bool ModifyFlags;
public OutputType Output;
public uint Argument;
}
public enum MathCommand
{
Add = 0,
Subtract = 1,
Multiplicate = 2,
Divide = 3,
Modulo = 4,
And = 5,
Or = 6,
Xor = 7,
Not = 8,
RotShiftLeft = 9,
RotShiftRight = 10,
ArithmeticShiftLeft = 11,
ArithmeticShiftRight = 12,
LogicShiftLeft = 13,
LogicShiftRight = 14,
}
public enum Command
{
Copy = 0,
Store = 1,
Load = 2,
Get = 3,
Set = 4,
BpGet = 5,
BpSet = 6,
CpGet = 7,
Math = 8,
SpGet = 9,
SpSet = 10,
SysCall = 11,
HwIO = 12,
}
public enum OutputType
{
Discard = 0,
Push = 1,
Jump = 2,
}
public enum ExecutionMode
{
Always = 0,
Zero = 2,
One = 3,
}
public enum InputType
{
Zero = 0,
Pop = 1,
Peek = 2,
Argument = 3,
}
}

View file

@ -1,51 +0,0 @@

; Just print a "START" flag
[i0:arg] syscall [ci:1] 'S'
push 0 ; pushs string pointer argument
cpget ; pushs return addr
jmp @print_str ; Calls print_str(0)
drop ; releases argument
; Print the "END" marker
[i0:arg] syscall [ci:1] 'Q'
; End the program
syscall [ci:0]
; void print_str(char *string);
print_str:
bpget ; enter function by
spget ; saving the parents base pointer
bpset ; and storing the current stack pointer
; char *ptr = string;
get -2 ; get argument 0 into our local variable '#1'
; while(*ptr) {
print_str_loop:
[i0:peek] loadi [f:yes] ; write flags, also load result, don't discard pointer
[ex(z)=1] jmp @print_str_end_loop ; when *ptr == 0, then goto print_str_end_loop
; char c = *ptr; // which is implicitly done by our while(*ptr)
; putc(c);
[i0:pop] syscall [ci:1] ; removes the loaded character and prints it
; ptr++;
[i0:arg] add 1 ; adds 1 to the stack top
; }
jmp @print_str_loop
print_str_end_loop:
drop ; discard the result from loadi
drop ; discard our pointer
; return
bpget ; leave function
spset ; by restoring parent base pointer
bpset
jmpi ; and jumping back.

View file

@ -1,2 +0,0 @@
obj/*
vm

View file

@ -1,21 +0,0 @@
AS=gcc
CC=gcc
CXX=g++
LD=ld
AR=ar
FLAGS = -Werror -Wall -iquote include -O3 -g
VMFLAGS = $(FLAGS) -m32 -mno-sse -ffreestanding
ARTIFACT = libvm.a
PROGRAM = vm
all: $(ARTIFACT) $(PROGRAM)
$(PROGRAM): main.c vm.c vm.h
$(CC) -o $@ $(FLAGS) main.c vm.c
$(ARTIFACT): vm.c vm.h
$(CC) $(VMFLAGS) -c -o obj/vm.o.32 vm.c
$(AR) rcs $(ARTIFACT) obj/vm.o.32

View file

@ -1,178 +0,0 @@
#include "vm.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void vm_assert(int assertion, const char *msg)
{
if(assertion != 0)
return;
printf("Assertion failed: %s\n", msg);
exit(1);
}
void vm_syscall(Process *p, CommandInfo *info)
{
switch(info->additional)
{
case 0: // EXIT
p->tag = NULL;
break;
case 1:
printf("%c", info->input0);
break;
case 2:
printf("%d", info->input0);
break;
default:
printf("Some syscall: (%d, %d, %d, %d)\n",
info->input0,
info->input1,
info->argument,
info->additional);
break;
}
}
void vm_hwio(Process *p, CommandInfo *info)
{
printf("Some hardware IO: (%d, %d, %d, %d)\n",
info->input0,
info->input1,
info->argument,
info->additional);
}
void dump_proc(Process *process)
{
if(process->flags & VM_FLAG_Z)
printf("z");
else
printf(" ");
if(process->flags & VM_FLAG_N)
printf("n");
else
printf(" ");
printf(
"%2d %2d ",
process->codePointer,
process->basePointer);
printf("[ ");
for(int i = 1; i <= process->stackPointer; i++) {
printf("%d ", process->stack[i]);
}
printf("]\n");
}
void dump_memory(Process *process)
{
printf("Memory Dump:\n");
for(int i = 0; i < process->mmap.length; i++) {
printf("Page %d\n", i);
// Just assume that pageSize is a multiple of 16
for(int j = 0; j < process->mmap.pageSize; j++) {
if(j % 16 == 0)
printf("%3x ", j);
printf("%2.2X ", process->mmap.pages[i][j]);
if(j % 16 == 15) {
printf(" '");
for(int x = 16*(j/16); x < 16*(j/16+1); x++) {
char c = process->mmap.pages[i][x];
if(c < 0x20) c = '.';
if(c > 0x7E) c = '.';
printf("%c", c);
}
printf("'\n");
}
}
}
}
int main(int argc, const char **argv)
{
if(argc < 2) {
printf("Usage: vm [binary]\n");
printf(" [binary]: The file to be executed.\n");
return 1;
}
Module module = { 0 };
{ // Read module
FILE *f = fopen(argv[1], "rb");
fseek(f, 0, SEEK_END);
size_t len = ftell(f);
fseek(f, 0, SEEK_SET);
module.code = malloc(len);
module.length = len / sizeof(Instruction);
char *ptr = (char*)module.code;
for(int p = 0; p < len; p += fread(&ptr[p], 1, sizeof(Instruction), f));
fclose(f);
}
printf("Loaded module %s with %d instructions.\n", argv[1], module.length);
for(int i = 0; i < module.length; i++)
{
Instruction c = module.code[i];
printf(
"%3d: %10d %1d %1d %5d %3d %1d %1d %1d %1d\n",
i,
c.argument,
c.output,
c.flags,
c.cmdinfo,
c.command,
c.input1,
c.input0,
c.execN,
c.execZ);
}
uint8_t page0[64];
uint8_t page1[64];
strcpy((char*)page0, "Hallo Welt!");
uint8_t *mmapDirectory[2] = {
page0,
page1,
};
VirtualMemoryMap mmap = {
.pageSize = 64,
.length = 2,
.pages = mmapDirectory,
};
Process process = {
&module,
.codePointer = 0,
.stackPointer = 0,
.basePointer = 0,
.flags = 0,
.mmap = mmap,
};
Process *p = &process;
p->tag = p;
dump_memory(p);
dump_proc(p);
while(vm_step_process(p) && p->tag) {
dump_proc(p);
}
dump_proc(p);
dump_memory(p);
return 0;
}

View file

@ -1,261 +0,0 @@
#include "vm.h"
#include <stdio.h>
static void cmd_copy(CommandInfo *info)
{
info->output = info->input0;
}
static void cmd_load(Process *p, CommandInfo *info)
{
info->output = 0;
switch(info->additional) {
case 2:
info->output|=(uint32_t)(vm_read_byte(p, info->input0+3))<<24;
info->output|=(uint32_t)(vm_read_byte(p, info->input0+2))<<16;
case 1:
info->output|=(uint32_t)(vm_read_byte(p, info->input0+1))<< 8;
case 0:
info->output|=(uint32_t)(vm_read_byte(p, info->input0+0))<< 0;
break;
}
}
static void cmd_store(Process *p, CommandInfo *info)
{
switch(info->additional) {
case 2:
vm_write_byte(p, info->input0+3, info->input1>>24);
vm_write_byte(p, info->input0+2, info->input1>>16);
case 1:
vm_write_byte(p, info->input0+1, info->input1>>8);
case 0:
vm_write_byte(p, info->input0, info->input1);
break;
}
info->output = info->input1;
}
static void cmd_spget(Process *p, CommandInfo *info)
{
info->output = p->stackPointer;
}
static void cmd_spset(Process *p, CommandInfo *info)
{
info->output = p->stackPointer = info->input0;
}
static void cmd_bpget(Process *p, CommandInfo *info)
{
info->output = p->basePointer;
}
static void cmd_bpset(Process *p, CommandInfo *info)
{
info->output = p->basePointer = info->input0;
}
static void cmd_cpget(Process *p, CommandInfo *info)
{
info->output = p->codePointer + info->additional;
}
static inline int16_t makeSigned(uint16_t val)
{
return *((int16_t*)&val);
}
static void cmd_get(Process *p, CommandInfo *info)
{
info->output = p->stack[p->basePointer + makeSigned(info->input0)];
}
static void cmd_set(Process *p, CommandInfo *info)
{
info->output = p->stack[p->basePointer + makeSigned(info->input0)] = info->input1;
}
static void cmd_math(CommandInfo *info)
{
switch(info->additional)
{
// IMPORTANT:
// input1 - input0 because then input0 can be a fixed value
#define S(name, op) case name: info->output = info->input1 op info->input0; break;
S(VM_MATH_ADD, +)
S(VM_MATH_SUB, -)
S(VM_MATH_MUL, *)
S(VM_MATH_DIV, /)
S(VM_MATH_MOD, %)
S(VM_MATH_AND, &)
S(VM_MATH_OR, |)
S(VM_MATH_XOR, ^)
#undef S
case VM_MATH_NOT: info->output = ~info->input0; break;
default: vm_assert(0, "Invalid instruction: MATH command not defined."); break;
}
}
int vm_step_process(Process *process)
{
vm_assert(process != NULL, "process must not be NULL.");
Instruction instr = process->module->code[process->codePointer++];
int exec = 1;
switch(instr.execZ)
{
case VM_EXEC_X:
/* Don't modify execution. */
break;
case VM_EXEC_0:
if((process->flags & VM_FLAG_Z) != 0)
exec = 0;
break;
case VM_EXEC_1:
if((process->flags & VM_FLAG_Z) == 0)
exec = 0;
break;
default:
vm_assert(0, "Invalid instruction: execZ undefined.");
break;
}
switch(instr.execN)
{
case VM_EXEC_X:
/* Don't modify execution. */
break;
case VM_EXEC_0:
if((process->flags & VM_FLAG_N) != 0)
exec = 0;
break;
case VM_EXEC_1:
if((process->flags & VM_FLAG_N) == 0)
exec = 0;
break;
default:
vm_assert(0, "Invalid instruction: execN undefined.");
break;
}
// Only do further instruction execution when
// the execution condition is met.
if(exec)
{
CommandInfo info = { 0 };
switch(instr.input0)
{
case VM_INPUT_ZERO: info.input0 = 0; break;
case VM_INPUT_POP: info.input0 = vm_pop(process); break;
case VM_INPUT_PEEK: info.input0 = vm_peek(process); break;
case VM_INPUT_ARG: info.input0 = instr.argument; break;
default: vm_assert(0, "Invalid instruction: input0 undefined.");
}
switch(instr.input1)
{
case VM_INPUT_ZERO: info.input1 = 0; break;
case VM_INPUT_POP: info.input1 = vm_pop(process); break;
default: vm_assert(0, "Invalid instruction: input1 undefined.");
}
info.argument = instr.argument;
info.additional = instr.cmdinfo;
switch(instr.command)
{
case VM_CMD_COPY: cmd_copy(&info); break;
case VM_CMD_STORE: cmd_store(process, &info); break;
case VM_CMD_LOAD: cmd_load(process, &info); break;
case VM_CMD_MATH: cmd_math(&info); break;
case VM_CMD_SYSCALL: vm_syscall(process, &info); break;
case VM_CMD_HWIO: vm_hwio(process, &info); break;
case VM_CMD_SPGET: cmd_spget(process, &info); break;
case VM_CMD_SPSET: cmd_spset(process, &info); break;
case VM_CMD_BPGET: cmd_bpget(process, &info); break;
case VM_CMD_BPSET: cmd_bpset(process, &info); break;
case VM_CMD_CPGET: cmd_cpget(process, &info); break;
case VM_CMD_GET: cmd_get(process, &info); break;
case VM_CMD_SET: cmd_set(process, &info); break;
default: vm_assert(0, "Invalid instruction: command undefined.");
}
switch(instr.flags)
{
case VM_FLAG_YES:
process->flags = 0;
if(info.output == 0)
process->flags |= VM_FLAG_Z;
else if((info.output & (1<<31)) != 0)
process->flags |= VM_FLAG_N;
break;
case VM_FLAG_NO: break;
default:
vm_assert(0, "Invalid instruction: invalid flags.");
}
switch(instr.output)
{
case VM_OUTPUT_DISCARD: break;
case VM_OUTPUT_PUSH: vm_push(process, info.output); break;
case VM_OUTPUT_JUMP: process->codePointer = info.output; break;
default:
vm_assert(0, "Invalid instruction: invalid output.");
}
}
return process->codePointer < process->module->length;
}
void vm_push(Process *process, uint32_t value)
{
vm_assert(process != NULL, "process must not be NULL.");
vm_assert(process->stackPointer < VM_STACKSIZE, "Stack overflow");
process->stack[++process->stackPointer] = value;
}
uint32_t vm_pop(Process *process)
{
vm_assert(process != NULL, "process must not be NULL.");
uint32_t psp = process->stackPointer;
uint32_t val = process->stack[process->stackPointer--];
// Underflow check works because unsigned overflow is defined ;)
vm_assert(psp >= process->stackPointer, "Stack underflow");
return val;
}
uint32_t vm_peek(Process *process)
{
vm_assert(process != NULL, "process must not be NULL.");
return process->stack[process->stackPointer];
}
uint8_t vm_read_byte(Process *process, uint32_t address)
{
vm_assert(process != NULL, "process must not be NULL.");
uint32_t page = address / process->mmap.pageSize;
uint32_t index = address % process->mmap.pageSize;
vm_assert(page < process->mmap.length, "Out of memory.");
return process->mmap.pages[page][index];
}
void vm_write_byte(Process *process, uint32_t address, uint8_t value)
{
vm_assert(process != NULL, "process must not be NULL.");
uint32_t page = address / process->mmap.pageSize;
uint32_t index = address % process->mmap.pageSize;
vm_assert(page < process->mmap.length, "Out of memory.");
process->mmap.pages[page][index] = value;
}

View file

@ -1,182 +0,0 @@
#pragma once
#include <stddef.h>
#include <inttypes.h>
#if defined(__cplusplus)
extern "C" {
#endif
#if !defined(VM_STACKSIZE)
#define VM_STACKSIZE 512
#endif
// Binary Encoding : (enabled, value)
#define VM_EXEC_X 0
#define VM_EXEC_0 2
#define VM_EXEC_1 3
#define VM_INPUT_ZERO 0
#define VM_INPUT_POP 1
#define VM_INPUT_PEEK 2
#define VM_INPUT_ARG 3
#define VM_CMD_COPY 0
#define VM_CMD_STORE 1
#define VM_CMD_LOAD 2
#define VM_CMD_GET 3
#define VM_CMD_SET 4
#define VM_CMD_BPGET 5
#define VM_CMD_BPSET 6
#define VM_CMD_CPGET 7
#define VM_CMD_MATH 8
#define VM_CMD_SPGET 9
#define VM_CMD_SPSET 10
#define VM_CMD_SYSCALL 11
#define VM_CMD_HWIO 12
#define VM_MATH_ADD 0
#define VM_MATH_SUB 1
#define VM_MATH_MUL 2
#define VM_MATH_DIV 3
#define VM_MATH_MOD 4
#define VM_MATH_AND 5
#define VM_MATH_OR 6
#define VM_MATH_XOR 7
#define VM_MATH_NOT 8
#define VM_MATH_ROL 9
#define VM_MATH_ROR 10
#define VM_MATH_ASL 11
#define VM_MATH_ASR 12
#define VM_MATH_SHL 13
#define VM_MATH_SHR 14
#define VM_FLAG_NO 0
#define VM_FLAG_YES 1
#define VM_OUTPUT_DISCARD 0
#define VM_OUTPUT_PUSH 1
#define VM_OUTPUT_JUMP 2
#define VM_FLAG_Z (1<<0)
#define VM_FLAG_N (1<<1)
typedef struct
{
unsigned int execZ : 2;
unsigned int execN : 2;
unsigned int input0 : 2;
unsigned int input1 : 1;
unsigned int command : 6;
unsigned int cmdinfo : 16;
unsigned int flags : 1;
unsigned int output : 2;
uint32_t argument;
} __attribute__ ((packed)) Instruction;
_Static_assert(sizeof(Instruction) == 8, "Instruction must be 8 bytes large.");
_Static_assert(offsetof(Instruction, argument) == 4, "Argument must be must be 8 bytes large.");
typedef struct
{
Instruction *code;
uint32_t length;
} Module;
typedef struct
{
uint32_t pageSize;
uint32_t length;
uint8_t **pages;
} VirtualMemoryMap;
typedef struct
{
Module *module;
void *tag;
uint32_t codePointer;
uint32_t stackPointer;
uint32_t basePointer;
uint32_t flags;
uint32_t stack[VM_STACKSIZE];
VirtualMemoryMap mmap;
} Process;
typedef struct
{
uint32_t input0;
uint32_t input1;
uint32_t argument;
uint32_t additional;
uint32_t output;
} CommandInfo;
/**
* @brief Steps a given process.
*
* Executes a single instruction and processes input and output.
*
* @param process The process to be stepped.
* @returns 1 if the process is still running or 0 if the process is terminated.
*/
int vm_step_process(Process *process);
/**
* @brief Pushes a value onto the process' stack.
*/
void vm_push(Process *process, uint32_t value);
/**
* @brief Pops a value from the process' stack.
*/
uint32_t vm_pop(Process *process);
/**
* @brief Returns the top value of the process' stack.
*/
uint32_t vm_peek(Process *process);
/**
* Reads a byte from process memory.
* @arg process
* @arg address The address to read from.
*/
uint8_t vm_read_byte(Process *process, uint32_t address);
/**
* Writes a byte to process memory.
* @arg process
* @arg address The address to read from.
*/
void vm_write_byte(Process *process, uint32_t address, uint8_t value);
// The following functions need to be host-implemented.
/**
* An assertion the VM does.
* @param assertion If zero, the assertion failed.
* @param msg The message that should be shown when the assertion fails.
*/
void vm_assert(int assertion, const char *msg);
/**
* The hosts syscall implementation.
* @param process The process that calls the syscall.
* @param info Additional information for the syscall. Contains arguments and results.
*/
void vm_syscall(Process *process, CommandInfo *info);
/**
* The hosts hardware IO implementation.
* @param process The process that wants to do IO.
* @param info Additional information for the HWIO. Contains arguments and results.
*/
void vm_hwio(Process *process, CommandInfo *info);
#if defined(__cplusplus)
}
#endif

28
prototypes/syslinux.cfg Normal file
View file

@ -0,0 +1,28 @@
DEFAULT menu.c32
prompt 0
MENU TITLE DasOS Boot Menu
MENU AUTOBOOT Starting DasOS in # seconds
TIMEOUT 300
TOTALTIMEOUT 9000
LABEL das-os
MENU DEFAULT
MENU LABEL DasOS
KERNEL mboot.c32
APPEND das-os.ker
LABEL os-init
MENU DEFAULT
MENU LABEL OS Initialization Test
KERNEL mboot.c32
APPEND os-init.ker
LABEL video
MENU LABEL VBE Video Test
KERNEL mboot.c32
APPEND video.ker
LABEL poweroff
MENU LABEL Poweroff
KERNEL poweroff.c32

13
prototypes/video/Makefile Normal file
View file

@ -0,0 +1,13 @@
##
# Builds the video test kernel
##
include ../config.mk
KERNEL = video.ker
LIBS += -lboot -lvideo -lchardev -lserial -lbase -lgcc
all: builddir $(KERNEL)
include ../common.mk
include ../kernel.mk

View file

@ -0,0 +1,28 @@
.section multiboot
#define MB_MAGIC 0x1badb002
#define MB_FLAGS 0x07
#define MB_CHECKSUM -(MB_MAGIC + MB_FLAGS)
.align 4
// Offset Type Field Name Note
// 0 u32 magic required
// 4 u32 flags required
// 8 u32 checksum required
.int MB_MAGIC
.int MB_FLAGS
.int MB_CHECKSUM
// 12 u32 header_addr if flags[16] is set
// 16 u32 load_addr if flags[16] is set
// 20 u32 load_end_addr if flags[16] is set
// 24 u32 bss_end_addr if flags[16] is set
// 28 u32 entry_addr if flags[16] is set
.int 0, 0, 0, 0, 0
// 32 u32 mode_type if flags[2] is set
// 36 u32 width if flags[2] is set
// 40 u32 height if flags[2] is set
// 44 u32 depth if flags[2] is set
.int 0, 640, 480, 32

View file

@ -0,0 +1,25 @@
#include <stdint.h>
#include <multiboot.hpp>
#include <driver/video.hpp>
#include <driver/serial.hpp>
extern "C" void init(multiboot::Structure const & data)
{
SerialPort serial(SERIAL_COM1);
serial << "Hi!\n";
VideoScreen video(*data.vbe.modeInfo);
for(uint32_t y = 0; y < video.height(); y++)
{
for(uint32_t x = 0; x < video.width(); x++)
{
video(x,y) = COLOR(255, 0, 0);
}
}
serial << "w=" << video.width() << " h=" << video.height() << "\n";
while(true);
}