commit
131ef54972
97 changed files with 1184 additions and 1600 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -29,3 +29,6 @@
|
|||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# Boot Images
|
||||
*.img
|
|
@ -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 |
|
||||
|
@ -259,11 +263,12 @@ is set when the highest bit is set.
|
|||
### Output
|
||||
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. |
|
||||
| # | 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:
|
||||
|
24
documentation/supervm-syscalls.md
Normal file
24
documentation/supervm-syscalls.md
Normal 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:
|
24
documentation/supervm-system.md
Normal file
24
documentation/supervm-system.md
Normal 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.
|
||||
|
115
documentation/virtual-device.md
Normal file
115
documentation/virtual-device.md
Normal 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
|
|
@ -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
|
|
@ -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. |
|
|
@ -1,10 +0,0 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
[0-1] GB Kernel Space
|
||||
|
||||
[1-4] GB User Space
|
||||
Entry Point: 0x40000000
|
||||
Stack Start: 0xFFFFFFFF
|
|
@ -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
36
prototypes/common.mk
Normal 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
12
prototypes/config.mk
Normal 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/
|
|
@ -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));
|
94
prototypes/include/chardev.hpp
Normal file
94
prototypes/include/chardev.hpp
Normal 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);
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct CpuState
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
class PIC
|
||||
{
|
39
prototypes/include/driver/serial.hpp
Normal file
39
prototypes/include/driver/serial.hpp
Normal 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();
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include "driver.hpp"
|
||||
|
||||
namespace driver
|
56
prototypes/include/driver/video.hpp
Normal file
56
prototypes/include/driver/video.hpp
Normal 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
|
||||
);
|
||||
}
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace elf
|
||||
{
|
|
@ -3,6 +3,6 @@
|
|||
enum class Error
|
||||
{
|
||||
#define ERROR(num, ident, desc) ident = num,
|
||||
#include "errors.lst"
|
||||
#include "lists/errors.lst"
|
||||
#undef ERROR
|
||||
};
|
|
@ -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
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include "enums.hpp"
|
||||
|
||||
enum class SegmentAccess : uint8_t
|
|
@ -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;
|
||||
|
|
@ -3,4 +3,5 @@ ERROR(1, OutOfMemory, The system has run out of memory.)
|
|||
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(5, InvalidELFImage, The file beeing loaded is not a valid ELF file.)
|
||||
ERROR(6, VMError, The virtual machine has failed.)
|
|
@ -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
29
prototypes/include/new
Normal 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
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
class Numeric
|
25
prototypes/include/picture.hpp
Normal file
25
prototypes/include/picture.hpp
Normal 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;
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Provides a strong pointer wrapper which can be used to address
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum class Color : uint8_t
|
||||
{
|
39
prototypes/include/vbe.hpp
Normal file
39
prototypes/include/vbe.hpp
Normal 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
7
prototypes/kernel.mk
Normal 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)
|
12
prototypes/libbase/Makefile
Normal file
12
prototypes/libbase/Makefile
Normal 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
|
133
prototypes/libbase/numeric.cpp
Normal file
133
prototypes/libbase/numeric.cpp
Normal 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);
|
||||
}
|
||||
}
|
19
prototypes/libbase/string.c
Normal file
19
prototypes/libbase/string.c
Normal 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;
|
||||
}
|
12
prototypes/libboot/Makefile
Normal file
12
prototypes/libboot/Makefile
Normal 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
|
|
@ -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()
|
||||
{
|
12
prototypes/libchardev/Makefile
Normal file
12
prototypes/libchardev/Makefile
Normal file
|
@ -0,0 +1,12 @@
|
|||
##
|
||||
# Build libchardev.
|
||||
##
|
||||
|
||||
include ../config.mk
|
||||
|
||||
LIBRARY = libchardev.a
|
||||
|
||||
all: builddir $(LIBRARY)
|
||||
|
||||
include ../common.mk
|
||||
include ../library.mk
|
105
prototypes/libchardev/chardev.cpp
Normal file
105
prototypes/libchardev/chardev.cpp
Normal 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
3
prototypes/library.mk
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
$(LIBRARY): $(OBJS)
|
||||
$(AR) rcs ../libs/$(LIBRARY) $(addprefix obj/, $^)
|
12
prototypes/libserial/Makefile
Normal file
12
prototypes/libserial/Makefile
Normal file
|
@ -0,0 +1,12 @@
|
|||
##
|
||||
# Build serial port library.
|
||||
##
|
||||
|
||||
include ../config.mk
|
||||
|
||||
LIBRARY = libserial.a
|
||||
|
||||
all: builddir $(LIBRARY)
|
||||
|
||||
include ../common.mk
|
||||
include ../library.mk
|
66
prototypes/libserial/serial.cpp
Normal file
66
prototypes/libserial/serial.cpp
Normal 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);
|
||||
}
|
12
prototypes/libvideo/Makefile
Normal file
12
prototypes/libvideo/Makefile
Normal file
|
@ -0,0 +1,12 @@
|
|||
##
|
||||
# Build video library.
|
||||
##
|
||||
|
||||
include ../config.mk
|
||||
|
||||
LIBRARY = libvideo.a
|
||||
|
||||
all: builddir $(LIBRARY)
|
||||
|
||||
include ../common.mk
|
||||
include ../library.mk
|
12
prototypes/libvideo/src/video.cpp
Normal file
12
prototypes/libvideo/src/video.cpp
Normal 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)
|
||||
{
|
||||
|
||||
}
|
|
@ -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 = .;
|
14
prototypes/os-init/Makefile
Normal file
14
prototypes/os-init/Makefile
Normal 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
|
|
@ -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);
|
||||
}
|
||||
|
28
prototypes/os-init/multiboot.S
Normal file
28
prototypes/os-init/multiboot.S
Normal 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
|
|
@ -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
|
|
@ -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
|
|
@ -1,4 +1,4 @@
|
|||
#include "gdt.hpp"
|
||||
#include <kernel/gdt.hpp>
|
||||
|
||||
static SegmentDescriptor invalid;
|
||||
SegmentDescriptor GDT::descriptors[GDT::length];
|
|
@ -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;
|
|
@ -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
|
||||
|
|
@ -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
|
||||
{
|
|
@ -1,5 +1,5 @@
|
|||
#include "pic.hpp"
|
||||
#include "io.hpp"
|
||||
#include <driver/pic.hpp>
|
||||
#include <io.hpp>
|
||||
|
||||
PIC masterPIC(0x20);
|
||||
PIC slavePIC(0xA0);
|
|
@ -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
|
|
@ -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);
|
|
@ -1,5 +1,5 @@
|
|||
#include "driver/timer.hpp"
|
||||
#include "idt.hpp"
|
||||
#include <driver/timer.hpp>
|
||||
#include <kernel/idt.hpp>
|
||||
|
||||
namespace driver
|
||||
{
|
|
@ -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()
|
||||
{
|
|
@ -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)
|
|
@ -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.
|
@ -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);
|
||||
}
|
|
@ -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)
|
|
@ -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
|
|
@ -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;
|
||||
}
|
1
prototypes/supervm-asm/.gitignore
vendored
1
prototypes/supervm-asm/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
*.bin
|
|
@ -1,7 +0,0 @@
|
|||
|
||||
CSC = mcs
|
||||
|
||||
all: assembler.exe
|
||||
|
||||
assembler.exe: assembler.cs
|
||||
$(CSC) -out:$@ -target:exe $^
|
|
@ -1 +0,0 @@
|
|||
#!/usr/bin/mono assembler.exe
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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.
|
2
prototypes/supervm/.gitignore
vendored
2
prototypes/supervm/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
|||
obj/*
|
||||
vm
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
28
prototypes/syslinux.cfg
Normal 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
13
prototypes/video/Makefile
Normal 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
|
28
prototypes/video/multiboot.S
Normal file
28
prototypes/video/multiboot.S
Normal 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
|
25
prototypes/video/src/init.cpp
Normal file
25
prototypes/video/src/init.cpp
Normal 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);
|
||||
}
|
Loading…
Reference in a new issue