refactored logging / added clap for cli
This commit is contained in:
parent
d17d946fec
commit
f66bdb9b75
@ -6,3 +6,6 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
clap = { version = "4.2.5", features = ["derive"] }
|
||||||
|
log = "0.4.17"
|
||||||
|
simple_logger = "4.1.0"
|
||||||
|
@ -1,18 +1,25 @@
|
|||||||
|
/// Type alias to represent a register.
|
||||||
type RegisterMem = String;
|
type RegisterMem = String;
|
||||||
|
|
||||||
|
/// Type alias to represent a label used in place of a const.
|
||||||
pub type ConstId = String;
|
pub type ConstId = String;
|
||||||
|
|
||||||
|
/// A const can either be a number or a label of some section.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Const {
|
pub enum Const {
|
||||||
CS(ConstId),
|
CS(ConstId),
|
||||||
C(u8),
|
C(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Operations as they are parsed, before translating them to binary.
|
||||||
|
/// This type is used internally by the parser and
|
||||||
|
/// differs from cpu::Operation.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Operation {
|
pub enum Operation {
|
||||||
|
/// No type.
|
||||||
NOP,
|
NOP,
|
||||||
HALT,
|
HALT,
|
||||||
// R type
|
/// R type
|
||||||
ADD(RegisterMem, RegisterMem, RegisterMem),
|
ADD(RegisterMem, RegisterMem, RegisterMem),
|
||||||
SUB(RegisterMem, RegisterMem, RegisterMem),
|
SUB(RegisterMem, RegisterMem, RegisterMem),
|
||||||
AND(RegisterMem, RegisterMem, RegisterMem),
|
AND(RegisterMem, RegisterMem, RegisterMem),
|
||||||
@ -23,11 +30,11 @@ pub enum Operation {
|
|||||||
LOAD(RegisterMem, RegisterMem, RegisterMem),
|
LOAD(RegisterMem, RegisterMem, RegisterMem),
|
||||||
STORE(RegisterMem, RegisterMem, RegisterMem),
|
STORE(RegisterMem, RegisterMem, RegisterMem),
|
||||||
|
|
||||||
// I Type
|
/// I Type
|
||||||
SLI(RegisterMem, Const),
|
SLI(RegisterMem, Const),
|
||||||
ADDI(RegisterMem, Const),
|
ADDI(RegisterMem, Const),
|
||||||
CALL(RegisterMem, Const),
|
CALL(RegisterMem, Const),
|
||||||
|
|
||||||
// J Type
|
/// J Type
|
||||||
JAL(RegisterMem, RegisterMem, Const),
|
JAL(RegisterMem, RegisterMem, Const),
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,48 @@
|
|||||||
use super::AST::*;
|
use super::AST::*;
|
||||||
use crate::cpu::get_num;
|
use crate::cpu::get_num;
|
||||||
|
|
||||||
|
use log::{trace, warn};
|
||||||
|
|
||||||
|
|
||||||
|
/// Trait to represent a format we can translate our assembly to.
|
||||||
pub trait CodeFormat {
|
pub trait CodeFormat {
|
||||||
fn encode_op(op: &Operation, sy: &SymbolTable, current_pc: u16) -> Option<Self>
|
fn encode_op(op: &Operation, sy: &SymbolTable, current_pc: u16) -> Option<Self>
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Symbol table implemented as a vector.
|
||||||
|
/// This is a zero-size struct used to implement
|
||||||
|
/// lookup as an impl.
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SymbolTable(pub Vec<(String, u16)>);
|
pub struct SymbolTable(pub Vec<(String, u16)>);
|
||||||
|
|
||||||
impl SymbolTable {
|
impl SymbolTable {
|
||||||
pub fn lookup(&self, query: &str) -> u16 {
|
/// Finds a symbol in the symbol table.
|
||||||
|
/// Fails if the symbol is not in the symbol table.
|
||||||
|
pub fn lookup(&self, query: &str) -> Option<u16> {
|
||||||
|
trace!("Looking up {} in the symbol table.", query);
|
||||||
let SymbolTable(sy) = self;
|
let SymbolTable(sy) = self;
|
||||||
|
|
||||||
for (name, loc) in sy.into_iter() {
|
for (name, loc) in sy.into_iter() {
|
||||||
if query == (*name) {
|
if query == (*name) {
|
||||||
return *loc;
|
return Some(*loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic!(
|
// panic!(
|
||||||
"Symbol {} not found in symbol table. \nCurrent symbol table:{:?}",
|
// "Symbol {} not found in symbol table. \nCurrent symbol table:{:?}",
|
||||||
query, sy
|
// query, sy
|
||||||
);
|
// );
|
||||||
|
warn!("Symbol {} not found in symbol table.", query);
|
||||||
|
return None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CodeFormat for u16 {
|
impl CodeFormat for u16 {
|
||||||
fn encode_op(op: &Operation, sy: &SymbolTable, current_pc: u16) -> Option<Self> {
|
fn encode_op(op: &Operation, sy: &SymbolTable, current_pc: u16) -> Option<Self> {
|
||||||
println!("encoding {:?}", op);
|
trace!("encoding {:?}", op);
|
||||||
match op {
|
match op {
|
||||||
Operation::NOP => Some(0b0000000000000000),
|
Operation::NOP => Some(0b0000000000000000),
|
||||||
Operation::HALT => Some(0b1111111111111111),
|
Operation::HALT => Some(0b1111111111111111),
|
||||||
@ -107,7 +121,7 @@ impl CodeFormat for u16 {
|
|||||||
Operation::SLI(r1, c) => {
|
Operation::SLI(r1, c) => {
|
||||||
let r1b = get_num(&r1)? as u16;
|
let r1b = get_num(&r1)? as u16;
|
||||||
let cb = match c {
|
let cb = match c {
|
||||||
Const::CS(label) => sy.lookup(&label),
|
Const::CS(label) => sy.lookup(&label)?,
|
||||||
Const::C(n) => (*n) as u16,
|
Const::C(n) => (*n) as u16,
|
||||||
};
|
};
|
||||||
return Some((0b0110 << 12) + (r1b << 8) + cb);
|
return Some((0b0110 << 12) + (r1b << 8) + cb);
|
||||||
@ -115,7 +129,7 @@ impl CodeFormat for u16 {
|
|||||||
Operation::ADDI(r1, c) => {
|
Operation::ADDI(r1, c) => {
|
||||||
let r1b = get_num(&r1)? as u16;
|
let r1b = get_num(&r1)? as u16;
|
||||||
let cb = match c {
|
let cb = match c {
|
||||||
Const::CS(label) => sy.lookup(&label),
|
Const::CS(label) => sy.lookup(&label)?,
|
||||||
Const::C(n) => (*n) as u16,
|
Const::C(n) => (*n) as u16,
|
||||||
};
|
};
|
||||||
return Some((0b0111 << 12) + (r1b << 8) + cb);
|
return Some((0b0111 << 12) + (r1b << 8) + cb);
|
||||||
@ -123,7 +137,7 @@ impl CodeFormat for u16 {
|
|||||||
Operation::CALL(r1, c) => {
|
Operation::CALL(r1, c) => {
|
||||||
let r1b = get_num(&r1)? as u16;
|
let r1b = get_num(&r1)? as u16;
|
||||||
let cb = match c {
|
let cb = match c {
|
||||||
Const::CS(label) => sy.lookup(&label),
|
Const::CS(label) => sy.lookup(&label)?,
|
||||||
Const::C(n) => (*n) as u16,
|
Const::C(n) => (*n) as u16,
|
||||||
};
|
};
|
||||||
return Some((0b1110 << 12) + (r1b << 8) + cb);
|
return Some((0b1110 << 12) + (r1b << 8) + cb);
|
||||||
@ -132,7 +146,7 @@ impl CodeFormat for u16 {
|
|||||||
let r1b = get_num(&r1)? as u16;
|
let r1b = get_num(&r1)? as u16;
|
||||||
let r2b = get_num(&r2)? as u16;
|
let r2b = get_num(&r2)? as u16;
|
||||||
let cb = match c {
|
let cb = match c {
|
||||||
Const::CS(label) => current_pc - sy.lookup(&label),
|
Const::CS(label) => current_pc - sy.lookup(&label)?,
|
||||||
Const::C(n) => (*n) as u16,
|
Const::C(n) => (*n) as u16,
|
||||||
};
|
};
|
||||||
return Some((0b1010 << 12) + (r1b << 8) + (r2b << 4) + cb);
|
return Some((0b1010 << 12) + (r1b << 8) + (r2b << 4) + cb);
|
||||||
|
@ -6,16 +6,26 @@ mod tests;
|
|||||||
use encoder::CodeFormat;
|
use encoder::CodeFormat;
|
||||||
use encoder::SymbolTable;
|
use encoder::SymbolTable;
|
||||||
|
|
||||||
|
use log::{trace, debug};
|
||||||
|
|
||||||
use parser::Section;
|
use parser::Section;
|
||||||
|
|
||||||
use crate::loader::unloader::make_string;
|
use crate::loader::unloader::make_string;
|
||||||
|
|
||||||
use self::parser::SectionContent;
|
use parser::SectionContent;
|
||||||
|
|
||||||
impl Section {
|
impl Section {
|
||||||
|
/// Calculates the size, in binary, of a section.
|
||||||
fn get_size(&self) -> usize {
|
fn get_size(&self) -> usize {
|
||||||
match &self.content {
|
match &self.content {
|
||||||
|
// code is 1 word for instruction.
|
||||||
SectionContent::Code(c) => c.len(),
|
SectionContent::Code(c) => c.len(),
|
||||||
|
|
||||||
|
// UTF-8 strings are collections of 8-bit chunks, but they're
|
||||||
|
// packed into words of 16 bits + 16 bit NULL.
|
||||||
|
//
|
||||||
|
// If there's an uneven number of bytes in a string,
|
||||||
|
// we add a 8 bit empty padding and then the NULL byte.
|
||||||
SectionContent::CString(s) => {
|
SectionContent::CString(s) => {
|
||||||
let c = s.len();
|
let c = s.len();
|
||||||
if c % 2 != 0 {
|
if c % 2 != 0 {
|
||||||
@ -28,15 +38,22 @@ impl Section {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts a section to binary. Needs symbol table to
|
||||||
|
/// resolve labels, and to quickly get the address of
|
||||||
|
/// this own section.
|
||||||
fn to_binary(&self, sy: &SymbolTable) -> Option<Vec<u16>> {
|
fn to_binary(&self, sy: &SymbolTable) -> Option<Vec<u16>> {
|
||||||
let own_address = sy.lookup(&self.name);
|
let own_address = sy.lookup(&self.name)?;
|
||||||
match &self.content {
|
match &self.content {
|
||||||
SectionContent::Code(c) => {
|
SectionContent::Code(c) => {
|
||||||
let mut res = vec![];
|
let mut res = vec![];
|
||||||
|
|
||||||
|
// we keep track of the program counter because we
|
||||||
|
// need to calculate relative jumps.
|
||||||
let mut pc = own_address;
|
let mut pc = own_address;
|
||||||
for op in c.iter() {
|
for op in c.iter() {
|
||||||
println!("converting {:?}", op);
|
trace!("converting {:?}", op);
|
||||||
res.push(CodeFormat::encode_op(op, sy, pc)?);
|
res.push(CodeFormat::encode_op(op, sy, pc)?);
|
||||||
|
// pc simply increases by one after each operation.
|
||||||
pc += 1;
|
pc += 1;
|
||||||
}
|
}
|
||||||
return Some(res);
|
return Some(res);
|
||||||
@ -49,6 +66,13 @@ impl Section {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Sorts a list of sections.
|
||||||
|
/// All .text sections containing code are
|
||||||
|
/// put at the beginning of the binary file, in the order they
|
||||||
|
/// appear in the assembly file, except the "main" section,
|
||||||
|
/// which is the entrypoint of our program and must be put
|
||||||
|
/// at the very beginning.
|
||||||
fn sort_sections(sections: Vec<Section>) -> Option<Vec<Section>> {
|
fn sort_sections(sections: Vec<Section>) -> Option<Vec<Section>> {
|
||||||
// we start with a mock section that we'll just replace.
|
// we start with a mock section that we'll just replace.
|
||||||
let mut res: Vec<Section> = vec![Section {
|
let mut res: Vec<Section> = vec![Section {
|
||||||
@ -73,9 +97,14 @@ fn sort_sections(sections: Vec<Section>) -> Option<Vec<Section>> {
|
|||||||
|
|
||||||
res.append(&mut nocode);
|
res.append(&mut nocode);
|
||||||
|
|
||||||
|
// TODO: PANIC WHEN NO MAIN;
|
||||||
|
|
||||||
return Some(res);
|
return Some(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates symbol from a list of sorted sections.
|
||||||
|
/// Assumes the sections are already sorted in the
|
||||||
|
/// desired order.
|
||||||
fn make_symbol_table<'a>(sections: &'a Vec<Section>) -> Option<SymbolTable> {
|
fn make_symbol_table<'a>(sections: &'a Vec<Section>) -> Option<SymbolTable> {
|
||||||
let mut res = vec![];
|
let mut res = vec![];
|
||||||
let mut pos: u16 = 0;
|
let mut pos: u16 = 0;
|
||||||
@ -88,16 +117,17 @@ fn make_symbol_table<'a>(sections: &'a Vec<Section>) -> Option<SymbolTable> {
|
|||||||
return Some(SymbolTable(res));
|
return Some(SymbolTable(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts a vector of sections into binary.
|
||||||
pub fn to_binary(sections: Vec<Section>) -> Option<Vec<u16>> {
|
pub fn to_binary(sections: Vec<Section>) -> Option<Vec<u16>> {
|
||||||
let sorted = sort_sections(sections)?;
|
let sorted = sort_sections(sections)?;
|
||||||
println!("sorted sections: {:?}", sorted);
|
trace!("sorted sections: {:?}", sorted);
|
||||||
let sy = make_symbol_table(&sorted)?;
|
let sy = make_symbol_table(&sorted)?;
|
||||||
println!("symbol table: {:?}", sy);
|
debug!("symbol table: {:?}", sy);
|
||||||
let k: Vec<Vec<u16>> = sorted
|
let k: Vec<Vec<u16>> = sorted
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| x.to_binary(&sy))
|
.map(|x| x.to_binary(&sy))
|
||||||
.collect::<Option<Vec<Vec<u16>>>>()?;
|
.collect::<Option<Vec<Vec<u16>>>>()?;
|
||||||
println!("binary sections: {:?}", k);
|
trace!("binary sections: {:?}", k);
|
||||||
|
|
||||||
return Some(k.into_iter().flatten().collect());
|
return Some(k.into_iter().flatten().collect());
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use super::AST::{Const, Operation};
|
use super::AST::{Const, Operation};
|
||||||
use Operation::*;
|
use Operation::*;
|
||||||
|
|
||||||
type Loc = u16;
|
use log::*;
|
||||||
|
|
||||||
|
/// Represents a parsing failure.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
BadSectionHeader,
|
BadSectionHeader,
|
||||||
@ -13,23 +14,20 @@ pub enum ParseError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// represents the state of our parser.
|
/// represents the state of our parser.
|
||||||
|
/// Sadly parsing is stateless,
|
||||||
pub struct Parser {
|
pub struct Parser {
|
||||||
loc: u16, // current number of operations parsed.
|
input: Vec<String>, // input file
|
||||||
symtable: Vec<(String, u16)>, // symbols encountered, position.
|
|
||||||
pub input: Vec<String>, // input file
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parser {
|
impl Parser {
|
||||||
pub fn new(i: String) -> Self {
|
pub fn new(i: String) -> Self {
|
||||||
Parser {
|
Parser {
|
||||||
loc: 0,
|
|
||||||
symtable: vec![],
|
|
||||||
input: sanitize(i),
|
input: sanitize(i),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// removes comments and whitespaces, and splits the input in lines.
|
/// removes comments and whitespaces, and splits the input in lines.
|
||||||
fn sanitize(i: String) -> Vec<String> {
|
fn sanitize(i: String) -> Vec<String> {
|
||||||
i.lines()
|
i.lines()
|
||||||
.map(|x| remove_comments(x))
|
.map(|x| remove_comments(x))
|
||||||
@ -39,6 +37,7 @@ fn sanitize(i: String) -> Vec<String> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes comments.
|
||||||
fn remove_comments(i: &str) -> &str {
|
fn remove_comments(i: &str) -> &str {
|
||||||
if let Some(end) = i.find(';') {
|
if let Some(end) = i.find(';') {
|
||||||
return &i[0..end];
|
return &i[0..end];
|
||||||
@ -105,6 +104,7 @@ fn take_between_test() {
|
|||||||
|
|
||||||
//// SECTION PARSING
|
//// SECTION PARSING
|
||||||
|
|
||||||
|
/// Enum to represent possible section content.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SectionContent {
|
pub enum SectionContent {
|
||||||
Code(Vec<Operation>),
|
Code(Vec<Operation>),
|
||||||
@ -114,13 +114,14 @@ pub enum SectionContent {
|
|||||||
|
|
||||||
use SectionContent::*;
|
use SectionContent::*;
|
||||||
|
|
||||||
|
/// Binary file section, as parsed from a .grasm file.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Section {
|
pub struct Section {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub content: SectionContent,
|
pub content: SectionContent,
|
||||||
}
|
}
|
||||||
|
|
||||||
// A .section has a name and variable content.
|
|
||||||
impl Parser {
|
impl Parser {
|
||||||
pub fn parse_sections(&mut self) -> Result<Vec<Section>, ParseError> {
|
pub fn parse_sections(&mut self) -> Result<Vec<Section>, ParseError> {
|
||||||
let mut res = vec![];
|
let mut res = vec![];
|
||||||
@ -128,12 +129,15 @@ impl Parser {
|
|||||||
let mut lines = self.input.iter().map(|x| x.as_str()).into_iter();
|
let mut lines = self.input.iter().map(|x| x.as_str()).into_iter();
|
||||||
|
|
||||||
while let Some(l) = lines.next() {
|
while let Some(l) = lines.next() {
|
||||||
println!("Examing line: {}", l);
|
debug!("Examining line {}", l);
|
||||||
|
|
||||||
|
// are we looking at a section header?
|
||||||
if l.starts_with(".") {
|
if l.starts_with(".") {
|
||||||
let Some((kind, name)) = take_alpha_till(&l[1..], " ") else {
|
let Some((kind, name)) = take_alpha_till(&l[1..], " ") else {
|
||||||
return Err(ParseError::BadSectionHeader);
|
return Err(ParseError::BadSectionHeader);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// what kind of section?
|
||||||
match kind.as_str() {
|
match kind.as_str() {
|
||||||
"text" => {
|
"text" => {
|
||||||
let s: Vec<&str> = lines
|
let s: Vec<&str> = lines
|
||||||
@ -156,15 +160,19 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
"i16" => {
|
"i16" => {
|
||||||
let _s = lines.next();
|
let _s = lines.next();
|
||||||
|
todo!();
|
||||||
}
|
}
|
||||||
"u16" => {
|
"u16" => {
|
||||||
let _s = lines.next();
|
let _s = lines.next();
|
||||||
|
todo!();
|
||||||
}
|
}
|
||||||
"vi16" => {
|
"vi16" => {
|
||||||
let _s = lines.next();
|
let _s = lines.next();
|
||||||
|
todo!();
|
||||||
}
|
}
|
||||||
"vu16" => {
|
"vu16" => {
|
||||||
let _s = lines.next();
|
let _s = lines.next();
|
||||||
|
todo!();
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParseError::UnknownSectionKind);
|
return Err(ParseError::UnknownSectionKind);
|
||||||
@ -187,10 +195,11 @@ fn parse_code(i: &[&str]) -> Result<Vec<Operation>, ParseError> {
|
|||||||
return Ok(res);
|
return Ok(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses a single line of code.
|
||||||
fn parse_code_line(i: &str) -> Result<Operation, ParseError> {
|
fn parse_code_line(i: &str) -> Result<Operation, ParseError> {
|
||||||
// every operation has at most 3 arguments
|
// every operation has at most 3 arguments
|
||||||
let mut bits = i.split_whitespace();
|
let mut bits = i.split_whitespace();
|
||||||
println!("current parse code line: {}", i);
|
trace!("current parse code line: {}", i);
|
||||||
let Some(op) = bits.next() else {return Err(ParseError::BadInstruction)};
|
let Some(op) = bits.next() else {return Err(ParseError::BadInstruction)};
|
||||||
|
|
||||||
// no type
|
// no type
|
||||||
|
@ -2,6 +2,7 @@ use super::registers::Register;
|
|||||||
|
|
||||||
type Constant = i8; // 8 bits max, so it works.
|
type Constant = i8; // 8 bits max, so it works.
|
||||||
|
|
||||||
|
// TODO: Use macros and a single reference for the ops.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum OP {
|
pub enum OP {
|
||||||
NOP,
|
NOP,
|
||||||
@ -26,6 +27,7 @@ pub enum OP {
|
|||||||
|
|
||||||
pub use OP::*;
|
pub use OP::*;
|
||||||
|
|
||||||
|
/// Decodes a single binary operation.
|
||||||
pub fn decode(op: u16) -> OP {
|
pub fn decode(op: u16) -> OP {
|
||||||
let opcode = op >> 12;
|
let opcode = op >> 12;
|
||||||
let dest = ((op & 0x0F00) >> 8) as Register;
|
let dest = ((op & 0x0F00) >> 8) as Register;
|
||||||
@ -55,6 +57,7 @@ pub fn decode(op: u16) -> OP {
|
|||||||
0b1101 => STORE(dest, r1, r2),
|
0b1101 => STORE(dest, r1, r2),
|
||||||
0b1110 => CALL(dest, c),
|
0b1110 => CALL(dest, c),
|
||||||
0b1111 => HALT,
|
0b1111 => HALT,
|
||||||
_ => panic!("Not an operation."),
|
// opcode, by construction, is a binary 4 bits number.
|
||||||
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,15 @@ mod sysenv;
|
|||||||
|
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
pub use sysenv::*;
|
pub use sysenv::{IOBuffer, Sys};
|
||||||
|
|
||||||
pub use registers::*;
|
pub use registers::*;
|
||||||
|
|
||||||
pub use decoder::OP;
|
pub use decoder::OP;
|
||||||
use ram::Ram;
|
use ram::Ram;
|
||||||
|
|
||||||
|
use log::{debug};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ExecErr {
|
pub enum ExecErr {
|
||||||
InvalidRegister,
|
InvalidRegister,
|
||||||
@ -36,7 +38,6 @@ pub struct CPU<'a, T> {
|
|||||||
pub regs: Registers,
|
pub regs: Registers,
|
||||||
pub ram: Ram,
|
pub ram: Ram,
|
||||||
pub env: &'a mut T,
|
pub env: &'a mut T,
|
||||||
// should execution be halted? not sure if to include this or nah
|
|
||||||
halt: bool,
|
halt: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,20 +161,26 @@ where
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// fetch tne next operation from memory.
|
||||||
|
/// called by step.
|
||||||
fn fetch(&self) -> CPUResult<OP> {
|
fn fetch(&self) -> CPUResult<OP> {
|
||||||
let binop = self.ram.get(self.regs.pc).ok_or(ExecErr::InvalidPC)?;
|
let binop = self.ram.get(self.regs.pc).ok_or(ExecErr::InvalidPC)?;
|
||||||
|
|
||||||
println!("binop: {:#018b}", binop);
|
// debug!("fetched binop: {:#018b}", binop);
|
||||||
|
|
||||||
Ok(decode(binop))
|
Ok(decode(binop))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// fetches an operation and runs one clock cycle.
|
||||||
fn step(&mut self) -> CPUResult<()> {
|
fn step(&mut self) -> CPUResult<()> {
|
||||||
let op = self.fetch()?;
|
let op = self.fetch()?;
|
||||||
println!("fetched op: {:?}, pc: {} ", op, self.regs.pc);
|
debug!("fetched op: {:?}, pc: {} ", op, self.regs.pc);
|
||||||
self.execute_op(op)
|
self.execute_op(op)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// takes binary code as input, puts it at the start of memory, and
|
||||||
|
/// executes the code. Mainly for testing purposes.
|
||||||
pub fn run_code_raw(&mut self, bin_code: &[Word]) -> CPUResult<()> {
|
pub fn run_code_raw(&mut self, bin_code: &[Word]) -> CPUResult<()> {
|
||||||
self.halt = false;
|
self.halt = false;
|
||||||
// put the code in memory:
|
// put the code in memory:
|
||||||
@ -186,6 +193,7 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new CPU from an exising environment.
|
||||||
pub fn new(env: &'a mut T) -> Self {
|
pub fn new(env: &'a mut T) -> Self {
|
||||||
CPU {
|
CPU {
|
||||||
regs: Registers::default(),
|
regs: Registers::default(),
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
use crate::cpu::registers::Word;
|
use crate::cpu::registers::Word;
|
||||||
|
|
||||||
|
// TODO: abstract this into a trait and write different implementation
|
||||||
|
// of RAM, such as a growable cointainer/tree.
|
||||||
|
|
||||||
/// We'll define our RAM as a static array.
|
/// We'll define our RAM as a static array.
|
||||||
/// The maximum adressable memory is, right now, just 65kbit of memory.
|
/// The maximum adressable memory is, right now, just 65kbit of memory.
|
||||||
|
|
||||||
// pub const MAX_MEM: usize = 65536;
|
pub const MAX_MEM: usize = 65535;
|
||||||
pub const MAX_MEM: usize = 40;
|
// pub const MAX_MEM: usize = 40;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Ram {
|
pub struct Ram {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use std::io::stdin;
|
use std::io::stdin;
|
||||||
|
|
||||||
|
use log::{debug, info};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cpu::{ram::MAX_MEM, registers::Register},
|
cpu::{ram::MAX_MEM, registers::Register},
|
||||||
loader::{loader::find_and_read_string, unloader::make_string},
|
loader::{loader::find_and_read_string, unloader::make_string},
|
||||||
@ -7,13 +9,11 @@ use crate::{
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
// first working environment, we get input from stdin and we write output
|
|
||||||
// to a string.
|
|
||||||
//
|
|
||||||
|
|
||||||
// using strings to singal errors kinda sucks.
|
// using strings to singal errors kinda sucks.
|
||||||
// TODO: Fix this
|
// TODO: Fix this
|
||||||
|
|
||||||
|
/// Rudimentary environment, good for testing.
|
||||||
|
/// Gets input from stdin and writes output to a string.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct IOBuffer {
|
pub struct IOBuffer {
|
||||||
pub output: String,
|
pub output: String,
|
||||||
@ -21,7 +21,7 @@ pub struct IOBuffer {
|
|||||||
|
|
||||||
impl Sys for IOBuffer {
|
impl Sys for IOBuffer {
|
||||||
fn call(cpu: &mut CPU<IOBuffer>, r: Register, c: Word) -> CPUResult<()> {
|
fn call(cpu: &mut CPU<IOBuffer>, r: Register, c: Word) -> CPUResult<()> {
|
||||||
println!("called: {}", c);
|
debug!("called syscall: {}", c);
|
||||||
match c {
|
match c {
|
||||||
// 0: write an integer to output
|
// 0: write an integer to output
|
||||||
0 => {
|
0 => {
|
||||||
|
@ -3,7 +3,7 @@ use std::mem::transmute;
|
|||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
pub mod jit;
|
pub mod jit;
|
||||||
pub mod loader;
|
pub mod loader;
|
||||||
// pub mod ;
|
pub mod cli;
|
||||||
pub mod assembler;
|
pub mod assembler;
|
||||||
//
|
//
|
||||||
|
|
||||||
|
14
src/main.rs
14
src/main.rs
@ -5,7 +5,13 @@ use dekejit::assembler::to_binary;
|
|||||||
use dekejit::cpu::IOBuffer;
|
use dekejit::cpu::IOBuffer;
|
||||||
use dekejit::cpu::CPU;
|
use dekejit::cpu::CPU;
|
||||||
|
|
||||||
|
// use simple_logger::SimpleLogger;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
||||||
|
simple_logger::init_with_level(log::Level::Warn).unwrap();
|
||||||
|
|
||||||
|
|
||||||
let args: Vec<String> = args().collect();
|
let args: Vec<String> = args().collect();
|
||||||
|
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
@ -26,7 +32,7 @@ fn main() {
|
|||||||
|
|
||||||
let r = parser.parse_sections().unwrap();
|
let r = parser.parse_sections().unwrap();
|
||||||
|
|
||||||
println!("Parsed sections: {:?}", r);
|
// println!("Parsed sections: {:?}", r);
|
||||||
|
|
||||||
let code = to_binary(r).unwrap();
|
let code = to_binary(r).unwrap();
|
||||||
|
|
||||||
@ -44,9 +50,9 @@ fn main() {
|
|||||||
//
|
//
|
||||||
let mut cpu = CPU::new(&mut env);
|
let mut cpu = CPU::new(&mut env);
|
||||||
//
|
//
|
||||||
for c in &code[..] {
|
// for c in &code[..] {
|
||||||
println!("{:#018b}", c);
|
// // println!("{:#018b}", c);
|
||||||
}
|
// }
|
||||||
//
|
//
|
||||||
match cpu.run_code_raw(&code) {
|
match cpu.run_code_raw(&code) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user