summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOxore <oxore@protonmail.com>2020-03-04 02:06:37 +0300
committerOxore <oxore@protonmail.com>2020-03-04 02:19:23 +0300
commit52b665108f6f2203df251eec273184b142ee2e16 (patch)
tree58703ea1a50960a10001cd737f9bb6ec3706bdcd
parente3bf2663362b7362569679bbbd15287e73a8ad2a (diff)
Add Memory trait for Rom, refactor instantiating
-rw-r--r--Cargo.lock16
-rw-r--r--Cargo.toml1
-rw-r--r--Readme.md32
-rw-r--r--src/core.rs45
-rw-r--r--src/main.rs29
-rw-r--r--src/memory.rs18
-rw-r--r--src/rom.rs37
7 files changed, 139 insertions, 39 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 62161f5..b2a8643 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -19,6 +19,11 @@ dependencies = [
]
[[package]]
+name = "autocfg"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -51,6 +56,14 @@ version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "num-traits"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -97,15 +110,18 @@ name = "x51emu"
version = "0.1.0"
dependencies = [
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
"checksum hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8"
"checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
+"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
diff --git a/Cargo.toml b/Cargo.toml
index 7bb745a..e789025 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,3 +6,4 @@ edition = "2018"
[dependencies]
clap = "2.33.0"
+num-traits = "0.2"
diff --git a/Readme.md b/Readme.md
new file mode 100644
index 0000000..9f2c932
--- /dev/null
+++ b/Readme.md
@@ -0,0 +1,32 @@
+# MCS51 emulator
+
+## TODO for MVP of MCS51 emulator
+
+- Fully implements ISA of Atmel 8051.
+- Seek to implement compatibility with AT89S52.
+- Internal Flash ROM (8K to 64K).
+- Internal RAM (256 byte).
+- External ROM (up to 64K).
+- Interrupts support.
+- Peripherals:
+ - At least one I/O Port (P0)
+ - Timer (with interrupts)
+ - Serial UART (with interrupts)
+- Cycle accurate emulation of program execution with synchronization of all the
+ peripherals.
+- Can bind execution speed to real host machine time with speed multiplier (e.g.
+ 1 instruction per second, 1000 cycles per second).
+- Every peripheral produces event with information of it's state if it changes.
+ CPU produces events on each step while in single step mode or when pause
+ occurs.
+- Emulator consumes control events for changing peripherals state, controlling
+ execution speed or stepping CPU.
+- CLI controller.
+
+## TODO Beyond MVP
+
+- Full compatibility with AT89S52.
+- CPU step back command and reverse execution for some amount of steps (from few
+ to billions).
+- TUI controller.
+- Comprehensive GUI in web-browser.
diff --git a/src/core.rs b/src/core.rs
index ef3c115..a85d830 100644
--- a/src/core.rs
+++ b/src/core.rs
@@ -1,4 +1,5 @@
use crate::rom::Rom;
+use crate::memory::Memory;
use std::fmt;
#[derive(Debug, Clone, Copy)]
@@ -360,21 +361,21 @@ impl IncompleteOp {
impl Core {
/// Constructor
- pub fn new_with_rom_from_hex(hex: String) -> Result<Self, String> {
- let mut ram: [u8; u8::max_value() as usize + 1] = [0; u8::max_value() as usize + 1];
+ pub fn new() -> Self {
+ let mut ram = [0; u8::max_value() as usize + 1];
ram[RAM_OFFSET_SP as usize] = RESET_VALUE_SP;
ram[RAM_OFFSET_ACC as usize] = RESET_VALUE_ACC;
- let progmem = match Rom::from_hex(hex) {
- Ok(value) => value,
- Err(err_string) => return Err(err_string),
- };
- let extmem: [u8; u16::max_value() as usize + 1] = [0xFF; u16::max_value() as usize + 1];
- Ok(Self {
+ Self {
pc: 0,
ram,
- progmem,
- extmem,
- })
+ progmem: Rom::new(),
+ extmem: [0xFF; u16::max_value() as usize + 1],
+ }
+ }
+
+ pub fn rom(mut self, rom: Rom) -> Self {
+ self.progmem = rom;
+ self
}
/// Fetch and execute one instruction
@@ -391,14 +392,14 @@ impl Core {
/// Get current instruction
pub fn op(&self) -> Op {
- let op = IncompleteOp::from_u8(self.progmem.array[self.pc as usize]);
+ let op = IncompleteOp::from_u8(self.progmem.get(self.pc));
let operand1 = if let Some(incomplete_operand) = op.operand1 {
Some(self.fetch_operand(incomplete_operand, 1))
} else {
None
};
let operand2_offset = match &operand1 {
- Some(operand) => operand.size(),
+ Some(operand) => operand.size() as u16,
_ => 0,
};
let operand2 = if let Some(incomplete_operand) = op.operand2 {
@@ -452,18 +453,18 @@ impl Core {
}
/// Get operand by offset, based on meta information
- fn fetch_operand(&self, operand: IncompleteOperand, offset: usize) -> Operand {
+ fn fetch_operand(&self, operand: IncompleteOperand, offset: u16) -> Operand {
match operand {
IncompleteOperand::Acc => Operand::Acc,
IncompleteOperand::Direct => {
- Operand::Direct(self.progmem.array[(self.pc as usize + offset)])
+ Operand::Direct(self.progmem.get(self.pc + offset))
}
IncompleteOperand::Data => {
- Operand::Data(self.progmem.array[(self.pc as usize + offset)])
+ Operand::Data(self.progmem.get(self.pc + offset))
}
IncompleteOperand::Data16 => Operand::Data16({
- let high = self.progmem.array[(self.pc as usize + offset)];
- let low = self.progmem.array[(self.pc as usize + offset + 1)];
+ let high = self.progmem.get(self.pc + offset);
+ let low = self.progmem.get(self.pc + offset + 1);
u16::from(high) << 8 & u16::from(low)
}),
IncompleteOperand::Dptr => Operand::Dptr,
@@ -471,15 +472,15 @@ impl Core {
IncompleteOperand::Indirect(r) => Operand::Indirect(r),
IncompleteOperand::Register(r) => Operand::Register(r),
IncompleteOperand::Reladdr => {
- Operand::Reladdr(self.progmem.array[(self.pc as usize + offset)] as i8)
+ Operand::Reladdr(self.progmem.get(self.pc + offset) as i8)
}
IncompleteOperand::Addr11(high) => Operand::Addr11({
- let low = self.progmem.array[(self.pc as usize + offset)];
+ let low = self.progmem.get(self.pc + offset);
u16::from(high) << 8 & u16::from(low)
}),
IncompleteOperand::Addr16 => Operand::Addr16({
- let high = self.progmem.array[(self.pc as usize + offset)];
- let low = self.progmem.array[(self.pc as usize + offset + 1)];
+ let high = self.progmem.get(self.pc + offset);
+ let low = self.progmem.get(self.pc + offset + 1);
u16::from(high) << 8 & u16::from(low)
}),
}
diff --git a/src/main.rs b/src/main.rs
index 8f1acad..05b32c2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,7 +9,10 @@ use std::time::Duration;
mod core;
mod rom;
+mod memory;
+
use self::core::Core;
+use self::rom::Rom;
enum Control {
Run(bool),
@@ -126,34 +129,34 @@ fn main() {
.value_of("file")
.expect("No file name provided");
- let controller = {
- let controller_type_name = cli_args_matches
- .value_of("interface")
- .expect("No interface type specified");
- new_controller(controller_type_name)
- .unwrap_or_else(|_| panic!("Unknown controller type \"{}\"", controller_type_name))
- };
-
/* Read program and instantiate a simulator */
let data =
fs::read_to_string(filename).unwrap_or_else(|_| panic!("Unable to read file {}", filename));
- let core = match Core::new_with_rom_from_hex(data) {
+ let core = Core::new().rom(match Rom::from_hex(data) {
Ok(value) => value,
Err(err_string) => {
println!("{}", err_string);
return;
}
- };
+ });
/* Run core worker and controller */
let (bound_core, bound_controller) = new_bound_pair();
+ core_worker(core, bound_core);
+
thread::spawn(move || {
- core_worker(core, bound_core);
+ let controller = {
+ let controller_type_name = cli_args_matches
+ .value_of("interface")
+ .expect("No interface type specified");
+ new_controller(controller_type_name)
+ .unwrap_or_else(|_| panic!("Unknown controller type \"{}\"", controller_type_name))
+ };
+
+ controller(bound_controller);
});
-
- controller(bound_controller);
}
diff --git a/src/memory.rs b/src/memory.rs
new file mode 100644
index 0000000..372a029
--- /dev/null
+++ b/src/memory.rs
@@ -0,0 +1,18 @@
+extern crate num_traits;
+
+pub trait Memory<Addr>
+ where Addr: Copy + core::ops::Add<Output = Addr> + num_traits::identities::One
+{
+ fn get(&self, a: Addr) -> u8;
+
+ fn set(&mut self, a: Addr, v: u8);
+
+ fn get_word(&self, a: Addr) -> u16 {
+ u16::from(self.get(a)) | (u16::from(self.get(a + Addr::one())) << 8)
+ }
+
+ fn set_word(&mut self, a: Addr, v: u16) {
+ self.set(a, (v & 0xFF) as u8);
+ self.set(a + Addr::one(), (v >> 8) as u8)
+ }
+}
diff --git a/src/rom.rs b/src/rom.rs
index 77e6431..c054575 100644
--- a/src/rom.rs
+++ b/src/rom.rs
@@ -1,7 +1,8 @@
use std::fmt;
+use crate::memory::Memory;
pub struct Rom {
- pub array: [u8; u16::max_value() as usize + 1],
+ array: [u8; u16::max_value() as usize + 1],
}
impl fmt::Debug for Rom {
@@ -83,8 +84,26 @@ impl HexLine {
}
impl Rom {
+ /// Empty constructor
+ pub fn new() -> Self {
+ Self { array: [0; u16::max_value() as usize + 1] }
+ }
+
+ /// Constructor from hex
pub fn from_hex(hex: String) -> Result<Self, String> {
- let mut array = [0; u16::max_value() as usize + 1];
+ Self::new().hex(hex)
+ }
+
+ /// Method for sequential consuming building
+ pub fn hex(mut self, hex: String) -> Result<Self, String> {
+ match self.load_hex(hex) {
+ Ok(_) => Ok(self),
+ Err(string) => Err(string),
+ }
+ }
+
+ /// Method for occasional incremental hex loading
+ pub fn load_hex(&mut self, hex: String) -> Result<(), String> {
for (linenum, line) in hex.lines().enumerate() {
let hex_line = match HexLine::from(line) {
Ok(value) => value,
@@ -94,12 +113,22 @@ impl Rom {
match hex_line.rectyp {
Rectyp::Data => {
for (ptr, byte) in hex_line.data.iter().enumerate() {
- array[ptr + offset as usize] = *byte;
+ self.array[ptr + offset as usize] = *byte;
}
}
Rectyp::EndOfFile => {}
}
}
- Ok(Self { array })
+ Ok(())
+ }
+}
+
+impl Memory<u16> for Rom {
+ fn get(&self, a: u16) -> u8 {
+ self.array[a as usize]
+ }
+
+ fn set(&mut self, a: u16, v: u8) {
+ self.array[a as usize] = v;
}
}