initial commit

This commit is contained in:
Felix Kehrer 2021-07-07 22:04:01 +02:00
commit 4a22467df5
8 changed files with 268 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

16
Cargo.lock generated Normal file
View File

@ -0,0 +1,16 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "eis"
version = "0.1.0"
dependencies = [
"byteorder",
]

9
Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "eis"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
byteorder = "1.4.3"

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# README
`cargo run --release -- test/echo.eis`

9
doc/Binary.md Normal file
View File

@ -0,0 +1,9 @@
# Binary program format
The current binary format is super simple, because all CPU state (registers) is part of the memory.
That means you can think of it like this: There is a header (containing the registers), and a memory block.
My suggestion is to just put the program instructions right after the header, and put stack and heap wherever you want (by setting the registers).
Don't forget to point `ip` to the start of your program!
In the future, I might consider some sort of mapping format, but honestly, why make it complicated?
(For ASLR, I will need to define a range or something...)

54
doc/ISA.md Normal file
View File

@ -0,0 +1,54 @@
# ISA
## Current impl
| opcode | instruction |
| --- | --- |
| 0x10 | Input |
| 0x11 | Output |
| 0x20 | Mov |
| 0x90 | Nop |
## Basics
Input, Output, Mov
## Math
Add, Sub, Mov, Div (will need flags...)
## Conditions
Cmp, Jcc
## Stack
Push, Pop
## Funcion stuff
Call, Ret
## x86
| opcode | instruction |
| --- | --- |
| 0x00 - 0x05 | ADD |
| 0x06 | PUSH |
| 0x07 | POP |
| 0x08 - 0xD | OR |
| 0x0E | PUSH |
| 0x0F | POP |
| 0x10 - 0x15 | ADC |
| 0x16 | PUSH |
| 0x17 | POP |
| 0x18 - 0x1D | SBB |
| 0x1E | PUSH |
| 0x1F | POP |
| 0x20 - 0x25 | AND |
| 0x26 | ??? |
| 0x27 | DAA |
| 0x28 - 0x2D | SUB |
| 0x2E | ??? |
| 0x2F | DAS |
| ... | ... |

176
src/main.rs Normal file
View File

@ -0,0 +1,176 @@
use byteorder::{ReadBytesExt, LE};
use std::io::Result;
type Word = u16;
//type Memory = [Word; Word::MAX as usize + 1];
struct Memory {
words: [Word; Word::MAX as usize + 1],
}
impl Memory {
fn get(&self, index: Word) -> Word {
self.words[index as usize]
}
fn get_ref(&self, index: Word) -> Word {
// equivalent to get(get(index))
self.words[self.words[index as usize] as usize]
}
fn put(&mut self, index: Word, value: Word) {
self.words[index as usize] = value;
}
//fn put_ref(&mut self, index: Word, value: Word) {}
fn new(path_str: &str) -> Memory {
let mut file = match std::fs::File::open(path_str) {
Result::Ok(x) => x,
Result::Err(_) => panic!("could not open program"),
};
let mut mem = Memory {
words: [0x0; u16::MAX as usize + 1],
};
let mut index = 0;
loop {
match file.read_u16::<LE>() {
Result::Ok(word) => {
mem.words[index] = word;
//println!("DEBUG: WORD: {:#06x}", word);
index += 1;
}
Result::Err(error) => match error.kind() {
std::io::ErrorKind::UnexpectedEof => {
//println!("DEBUG: EOF");
break;
}
other_error => panic!("Other error: {:?}", other_error),
},
}
}
mem
}
}
struct Cpu {
//ax: Word, // Accumulator
//bx: Word, // Base
//cx: Word, // Counter
//dx: Word, // Data
//si: Word, // Source Index
//di: Word, // Destination Index
//bp: Word, // Base Pointer
//sp: Word, // Stack Pointer
ip: Word, // Instruction Pointer
// Segment & Status registers ignored for now
memory: Memory, // for convenience, the CPU has ownership of the memory
//registers are part of the memory now...
// TODO proper input handling (probably read from command line)
input: Vec<u8>,
input_index: usize,
}
impl Cpu {
fn _read_input(&mut self) -> Word {
// TODO length check or something...
let byte = self.input[self.input_index] as Word;
self.input_index += 1;
//println!("DEBUG: read byte {}", byte);
byte
}
fn _first_arg(&self) -> Word {
self.memory
.get(match self.memory.get(self.ip).checked_add(1) {
Some(x) => x,
None => panic!("malformed instruction at {}", self.memory.get(self.ip)),
})
}
fn _second_arg(&self) -> Word {
self.memory
.get(match self.memory.get(self.ip).checked_add(2) {
Some(x) => x,
None => panic!("malformed instruction at {}", self.memory.get(self.ip)),
})
}
fn step(&mut self) {
//println!("DEBUG: IP: {}", self.memory.get(self.ip));
// fetch and decode instruction
let (op, len) = match self.memory.get_ref(self.ip) {
0x10 => (Op::Input(self._first_arg()), 2),
0x11 => (Op::Output(self._first_arg()), 2),
0x20 => (Op::Mov(self._first_arg(), self._second_arg()), 3),
0x90 => (Op::Nop, 1),
0xFF => (Op::Exit(self._first_arg()), 2),
x => panic!("unknown instruction {} at {}", x, self.memory.get(self.ip)),
};
// increment ip
self.memory
.put(self.ip, self.memory.get(self.ip).wrapping_add(len));
// execute instruction
match op {
Op::Input(address) => {
//println!("DEBUG: INPUT: {}", address);
let x = self._read_input();
self.memory.put(address, x);
}
Op::Mov(dest, src) => self.memory.put(dest, self.memory.get(src)),
Op::Nop => {}
Op::Output(address) => {
//println!("DEBUG: OUTPUT: {}", address);
print!("{} ", self.memory.get(address));
}
Op::Exit(status) => std::process::exit(status as i32),
}
}
fn new(memory: Memory) -> Cpu {
// TODO number registers, leave 0 for NULL
// TODO either put at end of address space
// or offset programs
Cpu {
ip: 9,
memory,
input: vec![54, 65, 73, 74], // TODO proper input from command line or stdin
input_index: 0,
}
}
}
enum Op {
Input(Word),
Mov(Word, Word),
Nop,
Output(Word),
Exit(Word),
}
/*
impl Op {
fn length(&self) -> Word {
match *self {
Op::Nop => 1,
Op::Input(_) | Op::Output(_) => 2,
Op::Mov(_, _) => 3,
}
}
}*/
fn main() {
let args: Vec<String> = std::env::args().collect();
let memory = Memory::new(&args[1]);
let mut cpu = Cpu::new(memory);
for _ in 0..=255 {
cpu.step();
}
println!();
}

BIN
test/echo.eis Normal file

Binary file not shown.