Compare commits
14 Commits
7ccbd9258f
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7b18922cc7 | |||
| a0b02cb955 | |||
| 240f0e553f | |||
| 25a59a6b19 | |||
| c67217a6b8 | |||
| 458661b02a | |||
| c41e5328e6 | |||
| 67ebf48d6f | |||
| 98668c681e | |||
| 05a25447b2 | |||
| 56d2abe17f | |||
| eaaefd1b07 | |||
| 5302ad3876 | |||
| 2280f1e5d9 |
@@ -5,3 +5,7 @@ rustc-wrapper = "sccache"
|
||||
|
||||
[future-incompat-report]
|
||||
frequency = "always"
|
||||
|
||||
[profile.profiling]
|
||||
inherits = "release"
|
||||
debug = true
|
||||
|
||||
Vendored
+4
@@ -8,4 +8,8 @@
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"gitea.owner": "LowLevelDevs",
|
||||
"gitea.repo": "damn_simple_architecture",
|
||||
"[markdown]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnPaste": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
[
|
||||
{
|
||||
"label": "Run Emulator",
|
||||
"command": "cargo run --bin emulator",
|
||||
"use_new_terminal": true,
|
||||
},
|
||||
{
|
||||
"label": "Run Compiler",
|
||||
"command": "cargo run --bin compiler",
|
||||
"use_new_terminal": true,
|
||||
},
|
||||
{
|
||||
"label": "Run Assembler",
|
||||
"command": "cargo run --bin assembler",
|
||||
"use_new_terminal": true,
|
||||
},
|
||||
{
|
||||
"label": "Run Build System (dsx-build)",
|
||||
"command": "cargo run --bin dsx-build",
|
||||
"use_new_terminal": true,
|
||||
},
|
||||
{
|
||||
"label": "Build All (Release)",
|
||||
"command": "cargo build --release",
|
||||
"use_new_terminal": false,
|
||||
},
|
||||
{
|
||||
"label": "Run Tests",
|
||||
"command": "cargo test",
|
||||
"use_new_terminal": true,
|
||||
},
|
||||
{
|
||||
"label": "Profile Emulator with perf",
|
||||
"command": "cargo build --profile profiling; perf record -g -F 999 target/profiling/emulator; perf script -F +pid | save test.perf",
|
||||
"use_new_terminal": true,
|
||||
},
|
||||
]
|
||||
+6
-2
@@ -11,7 +11,11 @@ authors = ["zxq5", "nullndvoid"]
|
||||
|
||||
[profile.dev]
|
||||
codegen-backend = "cranelift"
|
||||
panic = "abort" # Cranelift does not support stack unwinds.
|
||||
panic = "abort" # Cranelift does not support stack unwinds.
|
||||
lto = false
|
||||
debug = true
|
||||
incremental = false # sccache does not support caching incremental crates.
|
||||
incremental = false # sccache does not support caching incremental crates.
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
lto = "fat"
|
||||
|
||||
@@ -4,7 +4,8 @@ use crate::assembler::model::{Node, Opcode, Symbol, Token};
|
||||
/// Parse DSA assembly code with optional formatting
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// ```rs
|
||||
/// use assembler::macros::dsa;
|
||||
/// // With formatting:
|
||||
/// let nodes = dsa!(hash, "mov r1, {}", 42)?;
|
||||
///
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
disallowed-types = ["std::collections::HashMap", "std::collections::HashSet"]
|
||||
+2
-1
@@ -20,10 +20,11 @@ compiler = { path = "../compiler" }
|
||||
dsa_editor = { path = "../dsa_editor" }
|
||||
egui = "0.31.1"
|
||||
dirs = "6.0.0"
|
||||
discord-presence = { version = "1.6.0", optional = true }
|
||||
discord-presence = { version = "2.0.0", optional = true }
|
||||
toml = { version = "0.8.23", optional = true }
|
||||
serde = { version = "1.0.219", features = ["derive"], optional = true }
|
||||
egui_file = "0.22.1"
|
||||
rustc-hash = "2.1.1"
|
||||
|
||||
[features]
|
||||
default = ["config"]
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
use common::prelude::Instruction;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Cache {
|
||||
addr: u32,
|
||||
instruction_block: Option<[u8; 256]>,
|
||||
instruction_lookup: FxHashMap<u32, Instruction>,
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
addr: 0,
|
||||
instruction_block: None,
|
||||
instruction_lookup: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_value(&mut self, addr: u32) -> Option<u32> {
|
||||
if addr < self.addr || addr >= self.addr + 256 || self.instruction_block.is_none()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(u32::from_be_bytes(
|
||||
self.instruction_block.expect("this should not be none!")
|
||||
[(addr - self.addr) as usize..(addr - self.addr + 4) as usize]
|
||||
.try_into()
|
||||
.expect("Failed to convert bytes to u32"),
|
||||
))
|
||||
}
|
||||
|
||||
pub const fn set(&mut self, addr: u32, block: &[u8; 256]) {
|
||||
self.addr = addr - addr % 256;
|
||||
self.instruction_block = Some(*block);
|
||||
}
|
||||
|
||||
pub fn lookup_instruction(&mut self, instruction: u32) -> Option<Instruction> {
|
||||
self.instruction_lookup.get(&instruction).copied()
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, value: u32, instruction: Instruction) {
|
||||
self.instruction_lookup.insert(value, instruction);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Cache {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
@@ -25,9 +25,11 @@ pub fn run_emulator(
|
||||
let mut running = Running::Paused;
|
||||
let mut step = 0;
|
||||
let mut addr;
|
||||
let mut history = Vec::<(u32, Instruction)>::new();
|
||||
let mut history = Vec::<(u32, u32)>::with_capacity(32768);
|
||||
let size = 256;
|
||||
|
||||
let record_history = true;
|
||||
|
||||
state_tx
|
||||
.send(StateUpdate::Running(Running::Paused))
|
||||
.expect("Failed to send initial state!");
|
||||
@@ -36,7 +38,9 @@ pub fn run_emulator(
|
||||
let mut update = false;
|
||||
|
||||
loop {
|
||||
let cmd = if running == Running::Running || step > 0 {
|
||||
let cmd = if step > 0 {
|
||||
None
|
||||
} else if running == Running::Running && step == 0 {
|
||||
match cmd_rx.try_recv() {
|
||||
Ok(cmd) => Some(cmd),
|
||||
Err(mpsc::TryRecvError::Empty) => {
|
||||
@@ -52,10 +56,15 @@ pub fn run_emulator(
|
||||
}
|
||||
};
|
||||
|
||||
if running == Running::Running && step == 0 {
|
||||
step = 32768;
|
||||
}
|
||||
|
||||
if let Some(cmd) = cmd {
|
||||
match cmd {
|
||||
Command::Start => {
|
||||
running = Running::Running;
|
||||
step = 32768;
|
||||
|
||||
// Update RPC with current state. TODO: Make this only occur on state
|
||||
// changes.
|
||||
@@ -71,9 +80,11 @@ pub fn run_emulator(
|
||||
}
|
||||
Command::Stop => {
|
||||
running = Running::Paused;
|
||||
step = 0;
|
||||
}
|
||||
Command::Reset(x) => {
|
||||
running = Running::Paused;
|
||||
step = 0;
|
||||
|
||||
match x {
|
||||
0 => {
|
||||
@@ -95,20 +106,12 @@ pub fn run_emulator(
|
||||
}
|
||||
Command::Step(x) => {
|
||||
step = x;
|
||||
running = Running::Paused;
|
||||
}
|
||||
Command::Write(offset, data) => {
|
||||
update = true;
|
||||
|
||||
processor
|
||||
.memory
|
||||
.write_range(offset, data)
|
||||
.unwrap_or_else(|_| {
|
||||
report_err(
|
||||
state_tx,
|
||||
"Failed to write memory range!",
|
||||
&mut processor,
|
||||
);
|
||||
});
|
||||
processor.memory.write_range(offset, data);
|
||||
}
|
||||
Command::Interrupt(_interrupt) => {
|
||||
update = true;
|
||||
@@ -118,14 +121,7 @@ pub fn run_emulator(
|
||||
Command::MemRequest(new, size) if update => {
|
||||
addr = new;
|
||||
let _ = state_tx.send(StateUpdate::MemoryView(
|
||||
processor.memory.read_range(addr, size).unwrap_or_else(|_| {
|
||||
report_err(
|
||||
state_tx,
|
||||
"Failed to read memory range!",
|
||||
&mut processor,
|
||||
);
|
||||
Vec::new()
|
||||
}),
|
||||
processor.memory.read_range(addr, size),
|
||||
));
|
||||
}
|
||||
Command::DisplayRequest if update => {
|
||||
@@ -163,50 +159,19 @@ pub fn run_emulator(
|
||||
let _ = state_tx.send(StateUpdate::Instructions(instruction_count));
|
||||
}
|
||||
Command::WriteBlock(addr, block) => {
|
||||
processor
|
||||
.memory
|
||||
.write_range(addr, block.to_vec())
|
||||
.unwrap_or_else(|_| {
|
||||
report_err(
|
||||
state_tx,
|
||||
"Failed to write memory block!",
|
||||
&mut processor,
|
||||
);
|
||||
});
|
||||
processor.memory.write_range(addr, block.to_vec());
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if step > 0 {
|
||||
step -= 1;
|
||||
update = true;
|
||||
running = Running::Paused;
|
||||
|
||||
// Execute one cycle.
|
||||
match processor.cycle() {
|
||||
Ok((addr, instruction)) => {
|
||||
history.push((addr, instruction));
|
||||
}
|
||||
Err(why) => {
|
||||
let pcx = processor
|
||||
.get(Register::Pcx)
|
||||
.expect("SPR should never be invalid");
|
||||
report_err(
|
||||
state_tx,
|
||||
&format!(
|
||||
"Could not decode instruction at {pcx:x}. Reason: {why}"
|
||||
),
|
||||
&mut processor,
|
||||
);
|
||||
}
|
||||
}
|
||||
instruction_count += 1;
|
||||
continue;
|
||||
if running == Running::Running {
|
||||
step += 1;
|
||||
}
|
||||
|
||||
if running == Running::Running {
|
||||
if step > 0 {
|
||||
step -= 1;
|
||||
update = true;
|
||||
|
||||
// Execute one cycle.
|
||||
@@ -227,9 +192,18 @@ pub fn run_emulator(
|
||||
}
|
||||
};
|
||||
|
||||
history.push(instruction);
|
||||
if matches!(instruction.1, Instruction::Halt) {
|
||||
if record_history {
|
||||
history.push((
|
||||
instruction.0,
|
||||
processor
|
||||
.get(Register::Cir)
|
||||
.expect("CIR should never be invalid"),
|
||||
));
|
||||
}
|
||||
|
||||
if matches!(instruction, (_, Instruction::Halt)) {
|
||||
running = Running::Halted;
|
||||
step = 0;
|
||||
}
|
||||
|
||||
instruction_count += 1;
|
||||
|
||||
@@ -1,52 +1,42 @@
|
||||
use std::collections::HashMap;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::emulator::system::model::ProcessorError;
|
||||
|
||||
pub trait MemoryUnit: Send + Sync {
|
||||
fn reset(&mut self);
|
||||
fn read_byte(&mut self, addr: u32) -> Result<u8, ProcessorError>;
|
||||
fn write_byte(&mut self, addr: u32, value: u8) -> Result<(), ProcessorError>;
|
||||
fn read_byte(&mut self, addr: u32) -> u8;
|
||||
fn write_byte(&mut self, addr: u32, value: u8);
|
||||
fn read_word(&mut self, addr: u32) -> Result<u32, ProcessorError>;
|
||||
fn write_word(&mut self, addr: u32, value: u32) -> Result<(), ProcessorError>;
|
||||
|
||||
fn read_range(&mut self, addr: u32, size: u32) -> Result<Vec<u8>, ProcessorError> {
|
||||
fn read_range(&mut self, addr: u32, size: u32) -> Vec<u8> {
|
||||
let mut data = Vec::with_capacity(size as usize);
|
||||
for i in 0..size {
|
||||
data.push(self.read_byte(addr + i)?);
|
||||
data.push(self.read_byte(addr + i));
|
||||
}
|
||||
Ok(data)
|
||||
data
|
||||
}
|
||||
|
||||
fn write_range(&mut self, addr: u32, value: Vec<u8>) -> Result<(), ProcessorError> {
|
||||
fn write_range(&mut self, addr: u32, value: Vec<u8>) {
|
||||
for (i, byte) in value.into_iter().enumerate() {
|
||||
self.write_byte(addr + i as u32, byte)?;
|
||||
self.write_byte(addr + i as u32, byte);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_block(&mut self, addr: u32) -> Result<[u8; 256], ProcessorError> {
|
||||
let mut data = [0; 256];
|
||||
for (i, byte) in data.iter_mut().enumerate() {
|
||||
*byte = self.read_byte(addr + i as u32)?;
|
||||
}
|
||||
Ok(data)
|
||||
}
|
||||
fn read_block(&mut self, addr: u32) -> &[u8; 256];
|
||||
|
||||
fn write_block(&mut self, addr: u32, data: [u8; 256]) -> Result<(), ProcessorError> {
|
||||
fn write_block(&mut self, addr: u32, data: &[u8; 256]) {
|
||||
for (i, byte) in data.iter().enumerate() {
|
||||
self.write_byte(addr + i as u32, *byte)?;
|
||||
self.write_byte(addr + i as u32, *byte);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MainStore {
|
||||
pub data: HashMap<u32, Block>,
|
||||
pub data: FxHashMap<u32, Block>,
|
||||
}
|
||||
|
||||
pub struct Block {
|
||||
data: [u8; 256],
|
||||
}
|
||||
pub type Block = [u8; 256];
|
||||
|
||||
impl Default for MainStore {
|
||||
fn default() -> Self {
|
||||
@@ -58,113 +48,110 @@ impl MainStore {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: HashMap::new(),
|
||||
data: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn segment_addr(addr: u32) -> (u32, u8) {
|
||||
(addr / 256, (addr % 256) as u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mut_block(&mut self, addr: u32) -> &mut Block {
|
||||
self.data
|
||||
.entry(addr)
|
||||
.or_insert_with(|| Block { data: [0; 256] });
|
||||
|
||||
self.data.get_mut(&addr).map_or_else(
|
||||
|| panic!("Could not fetch block with address {addr:x?}"),
|
||||
|block| block,
|
||||
)
|
||||
self.data.entry(addr).or_insert([0; 256])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn block(&mut self, addr: u32) -> &Block {
|
||||
self.data
|
||||
.entry(addr)
|
||||
.or_insert_with(|| Block { data: [0; 256] });
|
||||
|
||||
self.data.get(&addr).map_or_else(
|
||||
|| panic!("Could not fetch block with address {addr:x?}"),
|
||||
|block| block,
|
||||
)
|
||||
self.data.entry(addr).or_insert([0; 256])
|
||||
}
|
||||
}
|
||||
|
||||
impl MemoryUnit for MainStore {
|
||||
#[inline]
|
||||
fn reset(&mut self) {
|
||||
self.data.clear();
|
||||
}
|
||||
|
||||
fn read_byte(&mut self, addr: u32) -> Result<u8, ProcessorError> {
|
||||
#[inline]
|
||||
fn read_byte(&mut self, addr: u32) -> u8 {
|
||||
let (block_addr, offset) = Self::segment_addr(addr);
|
||||
let block = self.block(block_addr);
|
||||
Ok(block.data[offset as usize])
|
||||
block[offset as usize]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_word(&mut self, addr: u32) -> Result<u32, ProcessorError> {
|
||||
if addr % 4 != 0 {
|
||||
if !addr.is_multiple_of(4) {
|
||||
return Err(ProcessorError::BadMemoryAccess(addr));
|
||||
}
|
||||
|
||||
let (block_addr, offset) = Self::segment_addr(addr);
|
||||
let block = self.mut_block(block_addr);
|
||||
let mut bytes = [0; 4];
|
||||
bytes[0] = block.data[offset as usize];
|
||||
bytes[1] = block.data[(offset + 1) as usize];
|
||||
bytes[2] = block.data[(offset + 2) as usize];
|
||||
bytes[3] = block.data[(offset + 3) as usize];
|
||||
Ok(u32::from_be_bytes(bytes))
|
||||
let offset = offset as usize;
|
||||
let block = self.block(block_addr);
|
||||
Ok(u32::from_be_bytes(
|
||||
block[offset..=offset + 3]
|
||||
.try_into()
|
||||
.expect("Failed to read word!"),
|
||||
))
|
||||
}
|
||||
|
||||
fn read_range(&mut self, addr: u32, size: u32) -> Result<Vec<u8>, ProcessorError> {
|
||||
#[inline]
|
||||
fn read_range(&mut self, addr: u32, size: u32) -> Vec<u8> {
|
||||
let mut data = Vec::with_capacity(size as usize);
|
||||
for i in 0..size {
|
||||
data.push(self.read_byte(addr + i)?);
|
||||
data.push(self.read_byte(addr + i));
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
data
|
||||
}
|
||||
|
||||
fn write_byte(&mut self, addr: u32, value: u8) -> Result<(), ProcessorError> {
|
||||
#[inline]
|
||||
fn write_byte(&mut self, addr: u32, value: u8) {
|
||||
let (block_addr, offset) = Self::segment_addr(addr);
|
||||
let block = self.mut_block(block_addr);
|
||||
block.data[offset as usize] = value;
|
||||
Ok(())
|
||||
block[offset as usize] = value;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_word(&mut self, addr: u32, value: u32) -> Result<(), ProcessorError> {
|
||||
if addr % 4 != 0 {
|
||||
if !addr.is_multiple_of(4) {
|
||||
return Err(ProcessorError::BadMemoryAccess(addr));
|
||||
}
|
||||
|
||||
let (block_addr, offset) = Self::segment_addr(addr);
|
||||
let block = self.mut_block(block_addr);
|
||||
block.data[offset as usize] = (value >> 24) as u8;
|
||||
block.data[(offset + 1) as usize] = (value >> 16) as u8;
|
||||
block.data[(offset + 2) as usize] = (value >> 8) as u8;
|
||||
block.data[(offset + 3) as usize] = value as u8;
|
||||
block[offset as usize..=(offset + 3) as usize]
|
||||
.copy_from_slice(&value.to_be_bytes());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_range(&mut self, addr: u32, value: Vec<u8>) -> Result<(), ProcessorError> {
|
||||
for (i, byte) in value.into_iter().enumerate() {
|
||||
let (block_addr, offset) = Self::segment_addr(addr + i as u32);
|
||||
let block = self.mut_block(block_addr);
|
||||
block.data[offset as usize] = byte;
|
||||
#[inline]
|
||||
fn write_range(&mut self, addr: u32, value: Vec<u8>) {
|
||||
let mut current_block_addr = addr / 256;
|
||||
let mut current_block = self.mut_block(current_block_addr);
|
||||
let mut offset = addr % 256;
|
||||
for byte in value {
|
||||
current_block[offset as usize] = byte;
|
||||
offset += 1;
|
||||
if offset >= 256 {
|
||||
offset = 0;
|
||||
current_block_addr += 1;
|
||||
current_block = self.mut_block(current_block_addr);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_block(&mut self, addr: u32) -> Result<[u8; 256], ProcessorError> {
|
||||
#[inline]
|
||||
fn read_block(&mut self, addr: u32) -> &[u8; 256] {
|
||||
let (block_addr, _) = Self::segment_addr(addr);
|
||||
let block = self.block(block_addr);
|
||||
Ok(block.data)
|
||||
self.block(block_addr)
|
||||
}
|
||||
|
||||
fn write_block(&mut self, addr: u32, data: [u8; 256]) -> Result<(), ProcessorError> {
|
||||
#[inline]
|
||||
fn write_block(&mut self, addr: u32, data: &[u8; 256]) {
|
||||
let (block_addr, _) = Self::segment_addr(addr);
|
||||
let block = self.mut_block(block_addr);
|
||||
block.data = data;
|
||||
Ok(())
|
||||
let _ = self.data.insert(block_addr, *data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod cache;
|
||||
pub mod emulator;
|
||||
pub mod memory;
|
||||
pub mod model;
|
||||
|
||||
@@ -78,7 +78,7 @@ pub struct State {
|
||||
|
||||
pub error_log: Vec<String>,
|
||||
|
||||
pub instruction_history: Vec<(u32, Instruction)>,
|
||||
pub instruction_history: Vec<(u32, u32)>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
@@ -154,7 +154,7 @@ pub enum StateUpdate {
|
||||
MemoryView(Vec<u8>),
|
||||
DisplayView(Vec<u8>),
|
||||
Error(String),
|
||||
InstructionHistory(Vec<(u32, Instruction)>),
|
||||
InstructionHistory(Vec<(u32, u32)>),
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -290,7 +290,6 @@ impl RegFile {
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get(&self, reg: Register) -> Result<u32, ProcessorError> {
|
||||
Ok(match reg {
|
||||
Register::Rg0 => self.rg0,
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::{
|
||||
};
|
||||
|
||||
use crate::emulator::system::{
|
||||
cache::Cache,
|
||||
memory::MemoryUnit,
|
||||
model::{IODevice, ProcessorError, RegFile},
|
||||
};
|
||||
@@ -17,10 +18,7 @@ pub struct Processor {
|
||||
pub io_devices: Vec<Arc<dyn IODevice>>,
|
||||
|
||||
pub void: u32,
|
||||
}
|
||||
|
||||
fn log(message: &str) {
|
||||
println!("\x1b[32mINFO:\x1b[0m {message}");
|
||||
pub cache: Cache,
|
||||
}
|
||||
|
||||
impl Processor {
|
||||
@@ -32,6 +30,7 @@ impl Processor {
|
||||
halted: false,
|
||||
io_devices,
|
||||
void: 0,
|
||||
cache: Cache::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,21 +50,35 @@ impl Processor {
|
||||
// Get value from PCX.
|
||||
let addr = self.fetch()?;
|
||||
// Increment PCX.
|
||||
self.advance();
|
||||
self.advance()?;
|
||||
|
||||
// Set MAR to the previous value of PCX.
|
||||
*self.reg(Register::Mar)? = addr;
|
||||
let val = self.memory.read_word(addr)?;
|
||||
|
||||
let encoded = if let Some(val) = self.cache.lookup_value(addr) {
|
||||
val
|
||||
} else {
|
||||
let block = self.memory.read_block(addr);
|
||||
self.cache.set(addr, block);
|
||||
self.cache
|
||||
.lookup_value(addr)
|
||||
.expect("Failed to lookup value!")
|
||||
};
|
||||
|
||||
// Set CIR to the value of RAM[MAR].
|
||||
*self.reg(Register::Mar)? = val;
|
||||
*self.reg(Register::Cir)? = encoded;
|
||||
|
||||
// Decode and execute the instruction.
|
||||
let instruction = Instruction::decode(val)
|
||||
.map_err(|_| ProcessorError::InvalidInstruction(val))?;
|
||||
let decoded = if let Some(val) = self.cache.lookup_instruction(addr) {
|
||||
val
|
||||
} else {
|
||||
let decoded = Instruction::decode(encoded)
|
||||
.map_err(|_| ProcessorError::InvalidInstruction(encoded))?;
|
||||
self.cache.insert(addr, decoded);
|
||||
decoded
|
||||
};
|
||||
|
||||
instruction.execute(self)?;
|
||||
Ok((addr, instruction))
|
||||
decoded.execute(self)?;
|
||||
Ok((addr, decoded))
|
||||
}
|
||||
|
||||
const fn fetch(&self) -> Result<u32, ProcessorError> {
|
||||
@@ -84,7 +97,7 @@ impl Processor {
|
||||
}
|
||||
|
||||
pub fn display(&mut self) -> Result<Vec<u8>, ProcessorError> {
|
||||
self.memory.read_range(0x20000, 2000)
|
||||
Ok(self.memory.read_range(0x20000, 2000))
|
||||
}
|
||||
|
||||
pub fn cmp(&mut self, a: u32, b: u32) {
|
||||
@@ -163,10 +176,10 @@ impl Processor {
|
||||
let addr = self.get(Register::Spr)?;
|
||||
let size = n * 4;
|
||||
// returns the stack
|
||||
self.memory.read_range(
|
||||
Ok(self.memory.read_range(
|
||||
max(addr, 0), // ensures that we cannot read from a negative address
|
||||
min(size, addr), // ensures we don't read above the top of the stack
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +222,7 @@ impl Executable for Instruction {
|
||||
Self::LoadByte(a) => {
|
||||
*cpu.reg(a.r2)? = u32::from(
|
||||
cpu.memory
|
||||
.read_byte(cpu.get(a.r1)? + u32::from(a.immediate))?,
|
||||
.read_byte(cpu.get(a.r1)? + u32::from(a.immediate)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -218,7 +231,7 @@ impl Executable for Instruction {
|
||||
Self::LoadByteSigned(a) => {
|
||||
*cpu.reg(a.r2)? = sign_extend(u32::from(
|
||||
cpu.memory
|
||||
.read_byte(cpu.get(a.r1)? + u32::from(a.immediate))?,
|
||||
.read_byte(cpu.get(a.r1)? + u32::from(a.immediate)),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -257,7 +270,7 @@ impl Executable for Instruction {
|
||||
cpu.memory.write_byte(
|
||||
cpu.get(a.r2)? + u32::from(a.immediate),
|
||||
cpu.get(a.r1)? as u8,
|
||||
)?;
|
||||
);
|
||||
}
|
||||
|
||||
// Stores a half-word from SrcReg in memory address (base + offset) The
|
||||
@@ -266,9 +279,9 @@ impl Executable for Instruction {
|
||||
// split the value into bytes and then write two bytes
|
||||
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])?;
|
||||
.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])?;
|
||||
.write_byte(cpu.get(a.r2)? + u32::from(a.immediate) + 1, bytes[1]);
|
||||
}
|
||||
|
||||
// Stores a word from SrcReg in memory address (base + offset) The effective
|
||||
|
||||
@@ -81,9 +81,7 @@ fn test_mov_signed_instruction() {
|
||||
fn test_load_byte_instruction() {
|
||||
let mut cpu = create_test_processor();
|
||||
let addr = 0x100;
|
||||
cpu.memory
|
||||
.write_byte(addr, 0xAB)
|
||||
.expect("Failed to write byte to memory");
|
||||
cpu.memory.write_byte(addr, 0xAB);
|
||||
*cpu.reg(Register::Rg1).expect("Failed to get register Rg1") = addr - 4;
|
||||
|
||||
let load_byte_instr = Instruction::LoadByte(ITypeArgs::new(
|
||||
@@ -105,9 +103,7 @@ fn test_load_byte_instruction() {
|
||||
fn test_load_byte_signed_instruction() {
|
||||
let mut cpu = create_test_processor();
|
||||
let addr = 0x100;
|
||||
cpu.memory
|
||||
.write_byte(addr, 0xFF)
|
||||
.expect("Failed to write byte to memory");
|
||||
cpu.memory.write_byte(addr, 0xFF);
|
||||
*cpu.reg(Register::Rg1).expect("Failed to get register Rg1") = addr;
|
||||
|
||||
let load_byte_signed_instr = Instruction::LoadByteSigned(ITypeArgs::new(
|
||||
@@ -189,7 +185,7 @@ fn test_store_byte_instruction() {
|
||||
store_byte_instr.execute(&mut cpu).expect(
|
||||
"Emulator was slain by losing the game while attempting to execute instruction",
|
||||
);
|
||||
assert_eq!(cpu.memory.read_byte(addr).expect("Emulator was slain by losing the game while attempting to execute instruction"), 0xAB);
|
||||
assert_eq!(cpu.memory.read_byte(addr), 0xAB);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -468,7 +464,7 @@ fn test_shift_left_with_shamt() {
|
||||
let shl_instr = Instruction::ShiftLeft(RTypeArgs::new(
|
||||
Some(Register::Rg1),
|
||||
Some(Register::Zero),
|
||||
None,
|
||||
Some(Register::Rg1),
|
||||
Some(2),
|
||||
));
|
||||
|
||||
@@ -489,7 +485,7 @@ fn test_shift_right_with_shamt() {
|
||||
let shr_instr = Instruction::ShiftRight(RTypeArgs::new(
|
||||
Some(Register::Rg1),
|
||||
Some(Register::Zero),
|
||||
None,
|
||||
Some(Register::Rg1),
|
||||
Some(2),
|
||||
));
|
||||
|
||||
|
||||
@@ -117,10 +117,7 @@ impl Editor {
|
||||
.file_name()
|
||||
.unwrap_or_else(|| OsStr::new("Unnamed!"))
|
||||
.to_str()
|
||||
.map_or_else(
|
||||
|| unreachable!("File name should be valid UTF-8."),
|
||||
|ext| ext,
|
||||
);
|
||||
.unwrap_or_else(|| unreachable!("File name should be valid UTF-8."));
|
||||
}
|
||||
"Unnamed!"
|
||||
}
|
||||
@@ -129,12 +126,9 @@ impl Editor {
|
||||
if let Some(path) = &self.path {
|
||||
return path
|
||||
.extension()
|
||||
.map_or_else(|| OsStr::new("Unknown!"), |ext| ext)
|
||||
.unwrap_or_else(|| OsStr::new("Unknown!"))
|
||||
.to_str()
|
||||
.map_or_else(
|
||||
|| unreachable!("File name should be valid UTF-8."),
|
||||
|ext| ext,
|
||||
);
|
||||
.unwrap_or_else(|| unreachable!("File name should be valid UTF-8."));
|
||||
}
|
||||
"Unknown!"
|
||||
}
|
||||
@@ -398,7 +392,7 @@ impl Editor {
|
||||
_ => Syntax::default(),
|
||||
};
|
||||
|
||||
let ed = CodeEditor::default()
|
||||
let mut editor = CodeEditor::default()
|
||||
.id_source("editor")
|
||||
.with_fontsize(12.0)
|
||||
.with_rows(0)
|
||||
@@ -407,8 +401,6 @@ impl Editor {
|
||||
.with_numlines(true)
|
||||
.desired_width(available_width - 500.0);
|
||||
|
||||
let mut editor = ed.clone();
|
||||
|
||||
editor.show(ui, &mut self.text);
|
||||
}
|
||||
|
||||
@@ -451,7 +443,7 @@ impl Editor {
|
||||
Some("dsc") => {
|
||||
let output_path = Path::new(path).with_extension("dsa");
|
||||
if let Err(e) = compiler::compile_file(path, &output_path) {
|
||||
self.error = Some(format!("Compiler error: {}", e));
|
||||
self.error = Some(format!("Compiler error: {e}"));
|
||||
}
|
||||
|
||||
let mut compiler = CompilerEngine::new();
|
||||
@@ -461,7 +453,7 @@ impl Editor {
|
||||
let instructions = match compiler.wait_for_result() {
|
||||
Ok(instructions) => instructions,
|
||||
Err(e) => {
|
||||
self.error = Some(format!("Assembler error: {}", e));
|
||||
self.error = Some(format!("Assembler error: {e}"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use common::prelude::Instruction;
|
||||
use egui::{Context, Ui};
|
||||
|
||||
use crate::emulator::{
|
||||
@@ -57,8 +58,11 @@ impl Component for History {
|
||||
.color(egui::Color32::from_rgb(255, 200, 200)),
|
||||
);
|
||||
|
||||
let decoded = Instruction::decode(instruction.1)
|
||||
.unwrap_or(Instruction::Nop);
|
||||
|
||||
ui.label(
|
||||
egui::RichText::new(instruction.1.to_string())
|
||||
egui::RichText::new(decoded.to_string())
|
||||
.font(egui::FontId::monospace(12.0))
|
||||
.color(egui::Color32::from_rgb(200, 255, 200)),
|
||||
);
|
||||
|
||||
@@ -79,10 +79,7 @@ impl Loader {
|
||||
.file_name()
|
||||
.unwrap_or_else(|| OsStr::new("Unnamed!"))
|
||||
.to_str()
|
||||
.map_or_else(
|
||||
|| unreachable!("File name should be valid UTF-8."),
|
||||
|ext| ext,
|
||||
);
|
||||
.unwrap_or_else(|| unreachable!("File name should be valid UTF-8."));
|
||||
}
|
||||
"Unnamed!"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// program to just test compute power
|
||||
|
||||
dw large_num: 0x333333 // 333,333 instructions
|
||||
start:
|
||||
ldw large_num, rg0
|
||||
|
||||
// run approx 1m instructions
|
||||
loop:
|
||||
dec rg0
|
||||
cmp rg0, zero
|
||||
jgt loop
|
||||
hlt
|
||||
Reference in New Issue
Block a user