Compare commits

2 Commits

14 changed files with 520 additions and 124 deletions
+28 -97
View File
@@ -2,107 +2,38 @@
#![allow(unused)]
use std::{fmt, sync::mpsc::Sender};
// pub struct Logger {}
// impl Logger {
// pub const fn new() -> Self {
// Self {}
// pub struct Entry {
// etype: EntryType,
// pub message: String,
// }
// pub fn log(&self, message: &str) {
// _ = self;
// println!("\x1b[32mINFO:\x1b[0m {message}");
// #[derive(Copy, Clone, Eq, PartialEq)]
// enum EntryType {
// Debug,
// Info,
// Warn,
// Error,
// Fatal,
// }
// impl fmt::Display for EntryType {
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// write!(
// f,
// "{:<5}",
// match self {
// Self::Debug => "DEBUG",
// Self::Info => "INFO",
// Self::Warn => "WARN",
// Self::Error => "ERROR",
// Self::Fatal => "FATAL",
// }
// )
// }
// }
// #[derive(Debug)]=
// pub struct Logger {
// pub sender: Sender<Entry>,
// }
// impl Logger {
// pub fn new(sender: Sender<Entry>) -> Self {
// Self { sender }
// }
// pub fn debug<T: fmt::Display>(&self, message: T) {
// self.sender
// .send(Entry {
// etype: EntryType::Debug,
// message: message.to_string(),
// })
// .unwrap();
// }
// pub fn info<T: fmt::Display>(&self, message: T) {
// self.sender
// .send(Entry {
// etype: EntryType::Info,
// message: message.to_string(),
// })
// .unwrap();
// }
// pub fn warn<T: fmt::Display>(&self, message: T) {
// self.sender
// .send(Entry {
// etype: EntryType::Warn,
// message: message.to_string(),
// })
// .unwrap();
// }
// pub fn error<T: fmt::Display>(&self, message: T) {
// self.sender
// .send(Entry {
// etype: EntryType::Error,
// message: message.to_string(),
// })
// .unwrap();
// }
// pub fn fatal<T: fmt::Display>(&self, message: T) {
// self.sender
// .send(Entry {
// etype: EntryType::Fatal,
// message: message.to_string(),
// })
// .unwrap();
// impl fmt::Display for Entry {
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// write!(f, "{}: {}", self.etype, self.message)
// }
// }
pub struct Entry {
etype: EntryType,
pub message: String,
}
#[derive(Copy, Clone, Eq, PartialEq)]
enum EntryType {
Debug,
Info,
Warn,
Error,
Fatal,
}
impl fmt::Display for EntryType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{:<5}",
match self {
Self::Debug => "DEBUG",
Self::Info => "INFO",
Self::Warn => "WARN",
Self::Error => "ERROR",
Self::Fatal => "FATAL",
}
)
}
}
impl fmt::Display for Entry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.etype, self.message)
}
}
+2 -1
View File
@@ -12,7 +12,8 @@ pub fn generate_ast(input: &str, logger: &Logger) -> Result<Program, CompilerErr
let lexer = lexer::Lexer::new(&input);
let tokens = lexer.collect::<Vec<_>>();
println!("{tokens:#?}");
// println!("{tokens:#?}");
logger.info(&format!("Parsing {} Tokens...", tokens.len()));
-2
View File
@@ -800,8 +800,6 @@ impl Parser {
}
fn parse_type(&mut self) -> ParseResult<TypeId, CompilerError> {
println!("yes {:?}", self.peek_next()?);
// parse primitive or named type
if expect_tt!(self.peek_next()?, Identifier).accepted() {
return self.parse_type_identifier();
+55
View File
@@ -0,0 +1,55 @@
use std::path::Path;
use std::thread::sleep;
use std::time::Duration;
use std::{env, fs};
use crate::emulator::system::model::{Command, Running};
use crate::emulator::{config::Config, system::model::State};
pub fn run_cli() -> Result<(), Box<dyn std::error::Error>> {
// Initialize channels and read in configuration.
let (cmd_sender, cmd_receiver) = std::sync::mpsc::channel();
let (state_sender, state_reciever) = std::sync::mpsc::channel();
// not needed for now.
// let config = Config::load(Path::new(".dsa.emulator.toml"))?;
crate::setup_emulator(cmd_receiver, state_sender, None);
// run CLI.
let mut state = State::new(cmd_sender, state_reciever);
let mut bin_path: Option<String> = None;
for (i, arg) in env::args().enumerate().skip(1) {
// check for args --bin and <bin_path>
if arg == "--bin" {
bin_path = Some(env::args().nth(i + 1).expect("Binary path not provided"));
}
}
let binary =
fs::read(bin_path.expect("unreachable")).expect("unable to read binary file");
state.send(Command::Write(0, binary));
println!("{:?}", state.running);
sleep(Duration::from_secs(1));
state.update().unwrap();
state.cmd_sender.send(Command::Start).unwrap();
loop {
sleep(Duration::from_millis(20));
state.send(Command::DisplayRequest);
state.send(Command::RunningRequest);
state.update().expect("update failed");
for ch in state.serial_buff.drain(..) {
print!("{}", ch as char);
}
if state.running == Running::Halted {
break;
}
}
Ok(())
}
+1
View File
@@ -1,3 +1,4 @@
pub mod cli;
#[cfg(feature = "config")]
pub mod config;
pub mod misc;
@@ -0,0 +1,2 @@
pub const DISPLAY_ADDRESS: u32 = 0x20000;
pub const SERIAL_ADDRESS: u32 = DISPLAY_ADDRESS + 2000;
+3
View File
@@ -135,6 +135,9 @@ pub fn run_emulator(
Vec::new()
}),
));
let _ = state_tx.send(StateUpdate::Serial(
processor.serial_buff.drain(..).collect(),
));
}
Command::StackRequest if update => {
let _ = state_tx.send(StateUpdate::StackView(
+1
View File
@@ -1,4 +1,5 @@
pub mod cache;
pub mod constants;
pub mod emulator;
pub mod memory;
pub mod model;
+7
View File
@@ -28,6 +28,7 @@ pub enum Command {
WriteBlock(Address, Box<[u8; 256]>),
// request emulator state.
SerialRequest,
MemRequest(Address, u32),
DisplayRequest,
StackRequest,
@@ -79,6 +80,7 @@ pub struct State {
pub error_log: Vec<String>,
pub instruction_history: Vec<(u32, u32)>,
pub serial_buff: Vec<u8>,
}
impl State {
@@ -95,6 +97,7 @@ impl State {
display_view: vec![],
error_log: vec![],
instruction_history: vec![],
serial_buff: vec![],
}
}
@@ -121,6 +124,9 @@ impl State {
StateUpdate::InstructionHistory(history) => {
self.instruction_history.extend(history);
}
StateUpdate::Serial(buffer) => {
self.serial_buff.extend(buffer);
}
}
if self.error_log.len() > 256 {
@@ -148,6 +154,7 @@ impl State {
pub enum StateUpdate {
Registers(RegFile),
Serial(Vec<u8>),
Running(Running),
Instructions(usize),
StackView(Vec<u8>),
+30 -14
View File
@@ -5,6 +5,7 @@ use std::{
use crate::emulator::system::{
cache::Cache,
constants::{DISPLAY_ADDRESS, SERIAL_ADDRESS},
memory::MemoryUnit,
model::{IODevice, ProcessorError, RegFile},
};
@@ -19,6 +20,8 @@ pub struct Processor {
pub void: u32,
pub cache: Cache,
pub serial_buff: Vec<u8>,
}
impl Processor {
@@ -31,6 +34,8 @@ impl Processor {
io_devices,
void: 0,
cache: Cache::new(),
serial_buff: Vec::with_capacity(32768),
}
}
@@ -97,7 +102,7 @@ impl Processor {
}
pub fn display(&mut self) -> Result<Vec<u8>, ProcessorError> {
Ok(self.memory.read_range(0x20000, 2000))
Ok(self.memory.read_range(DISPLAY_ADDRESS, 2000))
}
pub fn cmp(&mut self, a: u32, b: u32) {
@@ -267,30 +272,41 @@ impl Executable for Instruction {
// Stores a byte from SrcReg in memory address (base + offset) The effective
// address must be byte-aligned.
Self::StoreByte(a) => {
cpu.memory.write_byte(
cpu.get(a.r2)? + u32::from(a.immediate),
cpu.get(a.r1)? as u8,
);
let addr = cpu.get(a.r2)? + u32::from(a.immediate);
let val = cpu.get(a.r1)? as u8;
if addr == SERIAL_ADDRESS {
cpu.serial_buff.push(val);
}
cpu.memory.write_byte(addr, val);
}
// Stores a half-word from SrcReg in memory address (base + offset) The
// effective address must be 2-byte-aligned.
Self::StoreHalfword(a) => {
// split the value into bytes and then write two bytes
let addr = cpu.get(a.r2)? + u32::from(a.immediate);
let bytes = (cpu.get(a.r1)? as u16).to_le_bytes();
cpu.memory
.write_byte(cpu.get(a.r2)? + u32::from(a.immediate), bytes[0]);
cpu.memory
.write_byte(cpu.get(a.r2)? + u32::from(a.immediate) + 1, bytes[1]);
if addr == SERIAL_ADDRESS {
cpu.serial_buff.extend(bytes);
}
// split the value into bytes and then write two bytes
cpu.memory.write_byte(addr, bytes[0]);
cpu.memory.write_byte(addr + 1, bytes[1]);
}
// Stores a word from SrcReg in memory address (base + offset) The effective
// address must be 4-byte-aligned.
Self::StoreWord(a) => {
cpu.memory.write_word(
cpu.get(a.r2)? + u32::from(a.immediate),
cpu.get(a.r1)?,
)?;
let addr = cpu.get(a.r2)? + u32::from(a.immediate);
let val = cpu.get(a.r1)?;
let bytes = val.to_le_bytes();
if addr == SERIAL_ADDRESS {
cpu.serial_buff.extend(bytes);
}
cpu.memory.write_word(addr, val)?;
}
// Loads a 16-bit literal value into reg, setting the bottom 16 bits of the
+1 -1
View File
@@ -448,7 +448,7 @@ impl Editor {
}
let mut assembler = Assembler::new(&dsa_path);
compiler.start();
assembler.start();
// Or block until done
self.output = match assembler.output() {
+6
View File
@@ -1,9 +1,15 @@
use std::env;
use std::path::Path;
use std::sync::Arc;
use dsa_rs::emulator::{config::Config, misc::rpc::get_rpc_client_or_none};
fn main() -> Result<(), Box<dyn std::error::Error>> {
if env::args().any(|arg| arg == "--cli") {
dsa_rs::emulator::cli::run_cli()?;
std::process::exit(0);
}
// Initialize channels and read in configuration.
let (cmd_sender, cmd_receiver) = std::sync::mpsc::channel();
let (state_sender, state_reciever) = std::sync::mpsc::channel();
+244
View File
@@ -0,0 +1,244 @@
// lib:
// print_serial.dsa
// usage:
//
// include print_serial "<relative path>"
//
// usage for print:
// push (register containing address of string)
// push pcx
// jmp print_serial::print
//
// usage for print_byte:
// push (register containing byte)
// push pcx
// jmp print_serial::print_byte
//
// usage for print_word:
// push (register containing word)
// push pcx
// jmp print_serial::print_word
//
// usage for print_hex_byte:
// push (register containing byte)
// push pcx
// jmp print_serial::print_hex_byte
//
// usage for print_hex_word:
// push (register containing word)
// push pcx
// jmp print_serial::print_hex_word
//
// usage for print_num:
// push (register containing number to print in decimal)
// push pcx
// jmp print_serial::print_num
//
// usage for println:
// push (register containing address of string)
// push pcx
// jmp print_serial::println
//
include maths "../maths/core.dsa"
dw serial: 0x207D0 // 0x20000 + 2000
// ------------------------------------------
// prints the string at addr(arg[0]) to the serial port.
print:
push bpr
mov spr, bpr
ldw bpr, rg0, 8
lwi 0x207D0, rg1
_print_loop:
ldb rg0, acc
cmp acc, zero
jeq _end
stb acc, rg1
addi rg0, 1
jmp _print_loop
// ------------------------------------------
// prints the string at addr(arg[0]) followed by a newline to the serial port.
println:
push bpr
mov spr, bpr
ldw bpr, rg0, 8
lwi 0x207D0, rg1
_println_loop:
ldb rg0, acc
cmp acc, zero
jeq _println_end
stb acc, rg1
addi rg0, 1
jmp _println_loop
_println_end:
lli 0x0A, rg2 // newline character
stb rg2, rg1
jmp _end
// ------------------------------------------
// prints the word in arg[0] as 4 raw bytes to the serial port.
print_word:
push bpr
mov spr, bpr
ldw bpr, rg0, 8
lwi 0x207D0, rg1
stb rg0, rg1
shr rg0, 8
stb rg0, rg1
shr rg0, 8
stb rg0, rg1
shr rg0, 8
stb rg0, rg1
jmp _end
// ------------------------------------------
// prints the last byte of arg[0] to the serial port.
print_byte:
push bpr
mov spr, bpr
ldw bpr, rg0, 8
lwi 0x207D0, rg1
stb rg0, rg1
jmp _end
// ------------------------------------------
// prints the value of arg[0] to the serial port in hex.
print_hex_word:
push bpr
mov spr, bpr
lwi 0x207D0, rg1
ldb bpr, rg0, 8
push rg0
call _print_hex_byte
addi spr, 4
ldb bpr, rg0, 9
push rg0
call _print_hex_byte
addi spr, 4
ldb bpr, rg0, 10
push rg0
call _print_hex_byte
addi spr, 4
ldb bpr, rg0, 11
push rg0
call _print_hex_byte
addi spr, 4
jmp _end
// ------------------------------------------
// prints the last byte of arg[0] to the serial port in hex.
print_hex_byte:
push bpr
mov spr, bpr
ldw bpr, rg0, 8
lwi 0x207D0, rg1
call _print_hex_byte
jmp _end
// function body
_print_hex_byte:
lli 0xF, rg2
push rg0
shr rg0, 4
and rg0, rg2, rg0
call _print_hex_nibble
pop rg0
and rg0, rg2, rg0
call _print_hex_nibble
return
// print a hex digit
_print_hex_nibble:
lli 10, rg3
cmp rg0, rg3
jlt _print_hex_nibble_number
addi rg0, 0x37, rg0
stb rg0, rg1
return
_print_hex_nibble_number:
addi rg0, 0x30, rg0
stb rg0, rg1
return
// ------------------------------------------
// prints arg[0] as a decimal number to the serial port.
print_num:
push bpr
mov spr, bpr
ldw bpr, rg0, 8
lli 0, rg5
cmp rg0, zero
jne _print_num_extract_digits
lli 0x30, rg6
push rg6
lli 1, rg5
jmp _print_num_output
_print_num_extract_digits:
cmp rg0, zero
jeq _print_num_output
push rg0
lli 10, rg1
push rg1
call maths::divmod
pop rg0
pop rg1
addi rg1, 0x30, rg6
push rg6
inc rg5
jmp _print_num_extract_digits
_print_num_output:
lwi 0x207D0, rg1
_print_num_output_loop:
cmp rg5, zero
jeq _print_num_done
pop rg6
stb rg6, rg1
dec rg5
jmp _print_num_output_loop
_print_num_done:
// fall through to _end
// ------------------------------------------
// return
_end:
mov bpr, spr
pop bpr
return
+140 -9
View File
@@ -1,12 +1,143 @@
// program to just test compute power
// GENERATED BY DSC COMPILER
// Generated at 2026-02-23 17:29:47
dw large_num: 0x333333 // 333,333 instructions
start:
ldw large_num, rg0
// Imports
include print: "./lib/io/print.dsa"
include alloc: "./lib/memory/block_alloc.dsa"
// run approx 1m instructions
loop:
dec rg0
cmp rg0, zero
jgt loop
// Globals & Reserved Memory
// Entry Point
dw stack: 0x010000
db message: "Process Exited with code:"
_init:
ldw stack, bpr, 0
mov bpr, spr
push zero
call main
call print::print_newline
lwi message, rg0
push rg0
call print::print
pop zero
call print::print_hex_word
pop zero
hlt
// Return
_ret:
mov bpr, spr
pop bpr
return
db str_16: "successful free of ptr"
// fn main() -> u32
main:
push bpr
mov spr, bpr
lli 32, rg0
lli 64, rg1
// push arg 1
push rg0
// push arg 0
push rg1
call alloc::init
pop rg2
pop zero
push rg2
// push arg 0
push rg2
call print::print_hex_word
pop zero
call print::print_newline
ldw spr, rg0, 0
stw rg0, spr, 0
// push arg 0
push rg0
call alloc::alloc
pop rg1
push rg1
// push arg 0
push rg1
call print::print_hex_word
pop zero
lli 200, rg0
ldw spr, rg1, 0
stw rg0, rg1, 0
stw rg1, spr, 0
call print::print_newline
ldw spr, rg0, 4
stw rg0, spr, 4
// push arg 0
push rg0
call alloc::alloc
pop rg2
push rg2
// push arg 0
push rg2
call print::print_hex_word
pop zero
call print::print_newline
ldw spr, rg0, 4
ldw rg0, rg2, 0
stw rg0, spr, 4
// push arg 0
push rg2
call print::print_num
pop zero
ldw spr, rg2, 4
stw rg2, spr, 4
addi spr, 4, rg3
ldw spr, rg2, 8
stw rg2, spr, 8
// push arg 1
push rg3
// push arg 0
push rg2
call alloc::free
pop zero
pop zero
ldw spr, rg2, 8
stw rg2, spr, 8
// push arg 0
push rg2
call alloc::alloc
pop rg3
push rg3
call print::print_newline
ldw spr, rg2, 0
stw rg2, spr, 0
// push arg 0
push rg2
call print::print_hex_word
pop zero
call print::print_newline
ldw spr, rg2, 8
stw rg2, spr, 8
// push arg 0
push rg2
call print::print_hex_word
pop zero
ldw spr, rg2, 8
lli 0, rg4
cmp rg2, rg4
lli 1, rg5
jeq _cmp_end_12
lli 0, rg5
_cmp_end_12:
cmp rg5, zero
jeq _else_14
_then_13:
lwi str_16, rg4
stw rg2, spr, 8
// push arg 0
push rg4
call print::print
pop zero
jmp _end_15
_else_14:
nop
_end_15:
lli 0, rg4
stw rg4, bpr, 8
jmp _ret