commit
131ef54972
97 changed files with 1184 additions and 1600 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -29,3 +29,6 @@
|
||||||
*.exe
|
*.exe
|
||||||
*.out
|
*.out
|
||||||
*.app
|
*.app
|
||||||
|
|
||||||
|
# Boot Images
|
||||||
|
*.img
|
|
@ -1,20 +1,21 @@
|
||||||
# SuperVM
|
# SuperVM SPU Mark I
|
||||||
|
|
||||||
SuperVM is a stack machine with a simple, but flexible command
|
The *SPU Mark I* is a stack machine with a simple, but flexible command
|
||||||
set.
|
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
|
## Purpose of this document
|
||||||
|
|
||||||
This document is meant to give a complete overview over the concepts and abstract
|
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
|
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
|
## 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
|
registers operations take their operands from the stack and push their results to
|
||||||
it.
|
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.
|
of 6 local variables per function call.
|
||||||
|
|
||||||
### Data Memory
|
### Data Memory
|
||||||
SuperVM also provides a memory model that allows storing persistent data that is
|
*SPU Mark I* also provides a memory model that allows storing RAM data that is
|
||||||
accessed by different parts of the code.
|
accessible by different parts of the code.
|
||||||
The data memory is byte accessible and can be written or read.
|
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
|
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
|
sparse memory with different sections, it could utilize a software-implemented paging
|
||||||
process or just be a flat chunk of memory.
|
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
|
Every pointer that accesses data memory (e.g. via `store` and `load`) contains the
|
||||||
address of a byte in memory, starting with zero.
|
address of a byte in memory, starting with zero.
|
||||||
|
|
||||||
## Registers and Flags
|
## 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
|
registers that can be set with special instructions. The registers mainly
|
||||||
control stack access or control flow.
|
control stack access or control flow.
|
||||||
|
|
||||||
|
@ -108,7 +106,7 @@ code pointer is equivalent to a jump operation.
|
||||||
|
|
||||||
## Instructions
|
## Instructions
|
||||||
|
|
||||||
An SuperVM instruction is composed of multiple components:
|
An *SPU Mark I* instruction is composed of multiple components:
|
||||||
|
|
||||||
| Component | Range | Size | Function |
|
| 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
|
ALU operations. The ALU operation is selected
|
||||||
by the `cmdinfo`.
|
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 |
|
| cmdinfo | Operation | Forumla |
|
||||||
|---------|-----------------------------|-----------------|
|
|---------|-----------------------------|-----------------|
|
||||||
| 0 | Addition | input1 + input0 |
|
| 0 | Addition | input1 + input0 |
|
||||||
|
@ -259,11 +263,12 @@ is set when the highest bit is set.
|
||||||
### Output
|
### Output
|
||||||
Each instruction can emit an output value. The output can be used in the following ways:
|
Each instruction can emit an output value. The output can be used in the following ways:
|
||||||
|
|
||||||
| # | Output | Effect |
|
| # | Output | Effect |
|
||||||
|---|---------|--------------------------------------------------------------|
|
|---|---------|-----------------------------------------------------------------------------|
|
||||||
| 0 | discard | The output value is discarded. |
|
| 0 | discard | The output value is discarded. |
|
||||||
| 1 | push | The output is pushed to the stack. |
|
| 1 | push | The output is pushed to the stack. |
|
||||||
| 2 | jump | The code pointer is set to the output, thus a jump is taken. |
|
| 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
|
### Argument
|
||||||
The instruction argument can provide static input which can be used
|
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
|
## 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.
|
functions conform to this convention.
|
||||||
|
|
||||||
To call a function, it is required that the return address is pushed to the stack.
|
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
|
jmp @function ; Jumps to the function
|
||||||
returnPoint:
|
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
|
second next instruction which resembles the code above. This behaviour allows position
|
||||||
independent code:
|
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)
|
.PHONY: $(PROJECTS)
|
||||||
$(PROJECTS):
|
$(KERNELS): dirs $(LIBS)
|
||||||
make -C $@
|
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
|
#pragma once
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <inttypes.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define ASM_READ_REG(reg, var) asm volatile("mov %%" #reg ", %0" : "=r" (var));
|
#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));
|
#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
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
struct CpuState
|
struct CpuState
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
class PIC
|
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
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <stdint.h>
|
||||||
#include "driver.hpp"
|
#include "driver.hpp"
|
||||||
|
|
||||||
namespace driver
|
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
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace elf
|
namespace elf
|
||||||
{
|
{
|
|
@ -3,6 +3,6 @@
|
||||||
enum class Error
|
enum class Error
|
||||||
{
|
{
|
||||||
#define ERROR(num, ident, desc) ident = num,
|
#define ERROR(num, ident, desc) ident = num,
|
||||||
#include "errors.lst"
|
#include "lists/errors.lst"
|
||||||
#undef ERROR
|
#undef ERROR
|
||||||
};
|
};
|
|
@ -5,6 +5,6 @@
|
||||||
enum class Exception
|
enum class Exception
|
||||||
{
|
{
|
||||||
#define EXCEPTION(num, shorthand, ident, desc, type) ident = num,
|
#define EXCEPTION(num, shorthand, ident, desc, type) ident = num,
|
||||||
#include "exceptions.lst"
|
#include "lists/exceptions.lst"
|
||||||
#undef EXCEPTION
|
#undef EXCEPTION
|
||||||
};
|
};
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <stdint.h>
|
||||||
#include "enums.hpp"
|
#include "enums.hpp"
|
||||||
|
|
||||||
enum class SegmentAccess : uint8_t
|
enum class SegmentAccess : uint8_t
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <stdint.h>
|
||||||
#include "enums.hpp"
|
#include "enums.hpp"
|
||||||
#include "cpustate.hpp"
|
#include "cpustate.hpp"
|
||||||
|
|
||||||
|
@ -58,9 +58,11 @@ public:
|
||||||
explicit Interrupt(Handler handler);
|
explicit Interrupt(Handler handler);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern "C" CpuState * interrupt_dispatch(CpuState *cpu);
|
||||||
|
|
||||||
class IDT
|
class IDT
|
||||||
{
|
{
|
||||||
|
friend CpuState * interrupt_dispatch(CpuState *cpu);
|
||||||
public:
|
public:
|
||||||
static const uint32_t length = 256;
|
static const uint32_t length = 256;
|
||||||
|
|
|
@ -4,3 +4,4 @@ ERROR(2, UnhandledException, An unhandled exception has occurred.)
|
||||||
ERROR(3, UnhandledInterrupt, An unhandled interrupt has occurred.)
|
ERROR(3, UnhandledInterrupt, An unhandled interrupt has occurred.)
|
||||||
ERROR(4, DriverAlreadyInstalled, A driver was already installed.)
|
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
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
#include "pointer.hpp"
|
#include "pointer.hpp"
|
||||||
|
|
||||||
#define MB_MEMSIZE (1<<0)
|
#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.")
|
#define MB_ASSERT_SIZE(type, len) static_assert(sizeof(type) == len, "multiboot::" #type " must be " #len " bytes large.")
|
||||||
|
|
||||||
|
#include "vbe.hpp"
|
||||||
|
|
||||||
namespace multiboot
|
namespace multiboot
|
||||||
{
|
{
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -154,8 +157,8 @@ namespace multiboot
|
||||||
const char * bootLoaderName;
|
const char * bootLoaderName;
|
||||||
const APMTable * apmTable;
|
const APMTable * apmTable;
|
||||||
struct {
|
struct {
|
||||||
uint32_t ontrolInfo;
|
uint32_t controlInfo;
|
||||||
uint32_t modeInfo;
|
vbe::ModeInfo const * modeInfo;
|
||||||
uint16_t mode;
|
uint16_t mode;
|
||||||
uint16_t interfaceSegment;
|
uint16_t interfaceSegment;
|
||||||
uint16_t interfaceOffset;
|
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
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
class Numeric
|
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
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a strong pointer wrapper which can be used to address
|
* Provides a strong pointer wrapper which can be used to address
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
enum class Color : uint8_t
|
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)();
|
typedef void (*constructor)();
|
||||||
|
|
||||||
constructor start_ctors;
|
extern constructor start_ctors;
|
||||||
constructor end_ctors;
|
extern constructor end_ctors;
|
||||||
|
|
||||||
void compat_call_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) : {
|
.data ALIGN(4096) : {
|
||||||
start_ctors = .;
|
start_ctors = .;
|
||||||
KEEP(*( .init_array ));
|
KEEP(*( .ctors ));
|
||||||
KEEP(*(SORT_BY_INIT_PRIORITY( .init_array.* )));
|
KEEP(*(SORT_BY_INIT_PRIORITY( .ctors.* )));
|
||||||
end_ctors = .;
|
end_ctors = .;
|
||||||
|
|
||||||
start_dtors = .;
|
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 <console.hpp>
|
||||||
#include "pmm.hpp"
|
#include <kernel/pmm.hpp>
|
||||||
#include "numeric.hpp"
|
#include <numeric.hpp>
|
||||||
#include "pointer.hpp"
|
#include <pointer.hpp>
|
||||||
#include "multiboot.hpp"
|
#include <multiboot.hpp>
|
||||||
#include "gdt.hpp"
|
#include <kernel/gdt.hpp>
|
||||||
#include "idt.hpp"
|
#include <kernel/idt.hpp>
|
||||||
#include "compat.h"
|
#include <io.hpp>
|
||||||
#include "io.hpp"
|
#include <kernel/vmm.hpp>
|
||||||
#include "vmm.hpp"
|
#include <elf.hpp>
|
||||||
#include "elf.hpp"
|
#include <kernel/bsod.hpp>
|
||||||
#include "bsod.hpp"
|
|
||||||
|
|
||||||
#include "asm.hpp"
|
#include <asm.hpp>
|
||||||
|
|
||||||
#include "driver/timer.hpp"
|
#include "driver/timer.hpp"
|
||||||
#include "driver/keyboard.hpp"
|
#include "driver/keyboard.hpp"
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <stdint.h>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
using namespace multiboot;
|
using namespace multiboot;
|
||||||
using namespace console_tools;
|
using namespace console_tools;
|
||||||
|
@ -35,76 +33,6 @@ driver::Keyboard keyboardDriver;
|
||||||
|
|
||||||
VMMContext * kernelContext;
|
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)
|
static void initializePMM(Structure const & data)
|
||||||
{
|
{
|
||||||
for(auto &mmap : data.memoryMaps) {
|
for(auto &mmap : data.memoryMaps) {
|
||||||
|
@ -157,6 +85,7 @@ static void initializePMM(Structure const & data)
|
||||||
|
|
||||||
extern "C" void init(Structure const & data)
|
extern "C" void init(Structure const & data)
|
||||||
{
|
{
|
||||||
|
Console::main.clear();
|
||||||
Console::main
|
Console::main
|
||||||
<< "Hello World!\n"
|
<< "Hello World!\n"
|
||||||
<< FColor(Color::Yellow) << "Hello color!" << FColor() << "\n"
|
<< FColor(Color::Yellow) << "Hello color!" << FColor() << "\n"
|
||||||
|
@ -210,18 +139,9 @@ extern "C" void init(Structure const & data)
|
||||||
|
|
||||||
Console::main << "Drivers installed.\n";
|
Console::main << "Drivers installed.\n";
|
||||||
|
|
||||||
//asm volatile("sti");
|
|
||||||
ASM::sti();
|
ASM::sti();
|
||||||
|
|
||||||
Console::main << "Interrupts enabled.\n";
|
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);
|
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++
|
CXX=g++
|
||||||
LD=ld
|
LD=ld
|
||||||
|
|
||||||
|
LIBGCC = $(shell gcc -m32 -print-libgcc-file-name)
|
||||||
|
LIBSTD = ../stdlib/libstd.a
|
||||||
|
LIBVM = ../supervm/libvm.a
|
||||||
|
|
||||||
IDT_DISPATCH = _ZN3IDT8dispatchEP8CpuState
|
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)
|
ASFLAGS = $(FLAGS)
|
||||||
CFLAGS = $(FLAGS)
|
CFLAGS = $(FLAGS)
|
||||||
CXXFLAGS = $(FLAGS) -std=c++14 -fno-rtti -fno-exceptions -fno-leading-underscore -fno-use-cxa-atexit -nostdlib -fno-builtin
|
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)')
|
SRCS = $(shell find -regextype egrep -regex '.*/.*\.(cpp|S|c)')
|
||||||
OBJS = $(addsuffix .o, $(notdir $(basename $(SRCS))))
|
OBJS = $(addsuffix .o, $(notdir $(basename $(SRCS))))
|
||||||
|
LIBS = $(LIBGCC) $(LIBSTD) $(LIBVM)
|
||||||
LIBGCC = $(shell gcc -m32 -print-libgcc-file-name)
|
|
||||||
LIBSTD = ../stdlib/libstd.a
|
|
||||||
|
|
||||||
all: kernel-base.ker
|
all: kernel-base.ker
|
||||||
|
|
||||||
kernel-base.ker: $(OBJS)
|
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
|
%.o: %.cpp
|
||||||
$(CXX) $(CXXFLAGS) -c -o obj/$@ $<
|
$(CXX) $(CXXFLAGS) -c -o obj/$@ $<
|
||||||
|
@ -46,8 +50,8 @@ kernel-base.ker: $(OBJS)
|
||||||
# -initrd file use 'file' as initial ram disk
|
# -initrd file use 'file' as initial ram disk
|
||||||
# -dtb file use 'file' as device tree image
|
# -dtb file use 'file' as device tree image
|
||||||
|
|
||||||
|
|
||||||
run:
|
run:
|
||||||
|
echo `pwd`
|
||||||
qemu-system-i386 \
|
qemu-system-i386 \
|
||||||
-kernel kernel-base.ker \
|
-kernel kernel-base.ker \
|
||||||
-m 64 \
|
-m 64 \
|
||||||
|
@ -55,7 +59,7 @@ run:
|
||||||
-no-reboot \
|
-no-reboot \
|
||||||
-no-shutdown \
|
-no-shutdown \
|
||||||
-serial stdio \
|
-serial stdio \
|
||||||
-initrd ../program0/program
|
-initrd `pwd`/../supervm-asm/testcode.bin
|
||||||
|
|
||||||
insight:
|
insight:
|
||||||
objdump -d kernel-base.ker | c++filt | less
|
objdump -d kernel-base.ker | c++filt | less
|
|
@ -1,13 +1,13 @@
|
||||||
#include "bsod.hpp"
|
#include <kernel/bsod.hpp>
|
||||||
#include "console.hpp"
|
#include <console.hpp>
|
||||||
#include "exceptions.hpp"
|
#include <exceptions.hpp>
|
||||||
|
|
||||||
static const char *toString(int interrupt)
|
static const char *toString(int interrupt)
|
||||||
{
|
{
|
||||||
if(interrupt <= 0x1f) {
|
if(interrupt <= 0x1f) {
|
||||||
switch(interrupt) {
|
switch(interrupt) {
|
||||||
#define EXCEPTION(num, shorthand, ident, desc, type) case num: return #desc;
|
#define EXCEPTION(num, shorthand, ident, desc, type) case num: return #desc;
|
||||||
#include "exceptions.lst"
|
#include <lists/exceptions.lst>
|
||||||
#undef EXCEPTION
|
#undef EXCEPTION
|
||||||
default: return "Unknown Exception";
|
default: return "Unknown Exception";
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ static const char *toString(int interrupt)
|
||||||
if(interrupt >= 0x20 && interrupt <= 0x2F) {
|
if(interrupt >= 0x20 && interrupt <= 0x2F) {
|
||||||
switch(interrupt - 0x20) {
|
switch(interrupt - 0x20) {
|
||||||
#define IRQ(num, ident, desc) case num: return #desc;
|
#define IRQ(num, ident, desc) case num: return #desc;
|
||||||
#include "irqs.lst"
|
#include <lists/irqs.lst>
|
||||||
#undef IRQ
|
#undef IRQ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ void BSOD::die(Error code, const char *msg, CpuState *cpu)
|
||||||
<< "CODE: " << Y;
|
<< "CODE: " << Y;
|
||||||
switch(code) {
|
switch(code) {
|
||||||
#define ERROR(num, ident, desc) case Error::ident: Console::main << #ident << " / " << #desc << "\n"; break;
|
#define ERROR(num, ident, desc) case Error::ident: Console::main << #ident << " / " << #desc << "\n"; break;
|
||||||
#include "errors.lst"
|
#include <lists/errors.lst>
|
||||||
#undef ERROR
|
#undef ERROR
|
||||||
}
|
}
|
||||||
Console::main
|
Console::main
|
|
@ -1,4 +1,4 @@
|
||||||
#include "gdt.hpp"
|
#include <kernel/gdt.hpp>
|
||||||
|
|
||||||
static SegmentDescriptor invalid;
|
static SegmentDescriptor invalid;
|
||||||
SegmentDescriptor GDT::descriptors[GDT::length];
|
SegmentDescriptor GDT::descriptors[GDT::length];
|
|
@ -1,11 +1,11 @@
|
||||||
#include "idt.hpp"
|
#include <kernel/idt.hpp>
|
||||||
#include "asm.hpp"
|
#include <asm.hpp>
|
||||||
#include "bsod.hpp"
|
#include <kernel/bsod.hpp>
|
||||||
#include "pic.hpp"
|
#include <driver/pic.hpp>
|
||||||
|
|
||||||
#define ISR(num) extern "C" void isr_##num();
|
#define ISR(num) extern "C" void isr_##num();
|
||||||
#define ISR_ERR(num) ISR(num)
|
#define ISR_ERR(num) ISR(num)
|
||||||
#include "interrupts.lst"
|
#include <lists/interrupts.lst>
|
||||||
#undef ISR
|
#undef ISR
|
||||||
#undef ISR_ERR
|
#undef ISR_ERR
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ void IDT::initialize()
|
||||||
0x08, \
|
0x08, \
|
||||||
InterruptFlags::Interrupt | InterruptFlags::Use32Bit | InterruptFlags::Ring0 | InterruptFlags::Present);
|
InterruptFlags::Interrupt | InterruptFlags::Use32Bit | InterruptFlags::Ring0 | InterruptFlags::Present);
|
||||||
#define ISR_ERR(num) ISR(num)
|
#define ISR_ERR(num) ISR(num)
|
||||||
#include "interrupts.lst"
|
#include <lists/interrupts.lst>
|
||||||
#undef ISR
|
#undef ISR
|
||||||
#undef ISR_ERR
|
#undef ISR_ERR
|
||||||
|
|
||||||
|
@ -56,6 +56,11 @@ void IDT::setupPIC()
|
||||||
slavePIC.maskInterrupts(0x00);
|
slavePIC.maskInterrupts(0x00);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" CpuState * interrupt_dispatch(CpuState *cpu)
|
||||||
|
{
|
||||||
|
return IDT::dispatch(cpu);
|
||||||
|
}
|
||||||
|
|
||||||
CpuState * IDT::dispatch(CpuState *cpu)
|
CpuState * IDT::dispatch(CpuState *cpu)
|
||||||
{
|
{
|
||||||
bool ackMaster = cpu->interrupt >= 0x20 && cpu->interrupt <= 0x2F;
|
bool ackMaster = cpu->interrupt >= 0x20 && cpu->interrupt <= 0x2F;
|
|
@ -16,7 +16,7 @@ isr_\nr:
|
||||||
|
|
||||||
#define ISR(num) isr_stub num
|
#define ISR(num) isr_stub num
|
||||||
#define ISR_ERR(num) isr_stub_with_err num
|
#define ISR_ERR(num) isr_stub_with_err num
|
||||||
#include "interrupts.lst"
|
#include <lists/interrupts.lst>
|
||||||
#undef ISR
|
#undef ISR
|
||||||
#undef ISR_ERR
|
#undef ISR_ERR
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "driver/keyboard.hpp"
|
#include <driver/keyboard.hpp>
|
||||||
#include "idt.hpp"
|
#include <kernel/idt.hpp>
|
||||||
#include "console.hpp"
|
#include <console.hpp>
|
||||||
|
|
||||||
namespace driver
|
namespace driver
|
||||||
{
|
{
|
|
@ -1,5 +1,5 @@
|
||||||
#include "pic.hpp"
|
#include <driver/pic.hpp>
|
||||||
#include "io.hpp"
|
#include <io.hpp>
|
||||||
|
|
||||||
PIC masterPIC(0x20);
|
PIC masterPIC(0x20);
|
||||||
PIC slavePIC(0xA0);
|
PIC slavePIC(0xA0);
|
|
@ -1,7 +1,7 @@
|
||||||
#include <inttypes.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include "pmm.hpp"
|
#include <kernel/pmm.hpp>
|
||||||
#include "bsod.hpp"
|
#include <kernel/bsod.hpp>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number stored of pages in the bitmap
|
* Number stored of pages in the bitmap
|
|
@ -1,5 +1,5 @@
|
||||||
#include <inttypes.h>
|
#include <stdint.h>
|
||||||
#include "screen.hpp"
|
#include <screen.hpp>
|
||||||
|
|
||||||
ScreenChar Screen::outOfScreen;
|
ScreenChar Screen::outOfScreen;
|
||||||
Screen Screen::main((ScreenChar*)0xb8000, 80, 25);
|
Screen Screen::main((ScreenChar*)0xb8000, 80, 25);
|
|
@ -1,5 +1,5 @@
|
||||||
#include "driver/timer.hpp"
|
#include <driver/timer.hpp>
|
||||||
#include "idt.hpp"
|
#include <kernel/idt.hpp>
|
||||||
|
|
||||||
namespace driver
|
namespace driver
|
||||||
{
|
{
|
|
@ -1,7 +1,7 @@
|
||||||
#include "vmm.hpp"
|
#include <kernel/vmm.hpp>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include "console.hpp"
|
#include <console.hpp>
|
||||||
#include "asm.hpp"
|
#include <asm.hpp>
|
||||||
|
|
||||||
void VMM::enable()
|
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