diff options
author | Oxore <oxore@protonmail.com> | 2020-03-04 02:06:37 +0300 |
---|---|---|
committer | Oxore <oxore@protonmail.com> | 2020-03-04 02:19:23 +0300 |
commit | 52b665108f6f2203df251eec273184b142ee2e16 (patch) | |
tree | 58703ea1a50960a10001cd737f9bb6ec3706bdcd | |
parent | e3bf2663362b7362569679bbbd15287e73a8ad2a (diff) |
Add Memory trait for Rom, refactor instantiating
-rw-r--r-- | Cargo.lock | 16 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | Readme.md | 32 | ||||
-rw-r--r-- | src/core.rs | 45 | ||||
-rw-r--r-- | src/main.rs | 29 | ||||
-rw-r--r-- | src/memory.rs | 18 | ||||
-rw-r--r-- | src/rom.rs | 37 |
7 files changed, 139 insertions, 39 deletions
@@ -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" @@ -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) + } +} @@ -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; } } |