added serial-out support to emulator + serial lib & command line mode for dsa emulator
This commit is contained in:
@@ -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,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;
|
||||
@@ -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,4 +1,5 @@
|
||||
pub mod cache;
|
||||
pub mod constants;
|
||||
pub mod emulator;
|
||||
pub mod memory;
|
||||
pub mod model;
|
||||
|
||||
@@ -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>),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user