diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..6d2edec --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at sup.vfoulon@gmail.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..cc916b1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,10 @@ +**If you want to contribute to this project, first of all, thank you for this initiative.** + +Here's a list of things you can do for contributing in this project : + ++ develop new features, fix bugs, according to Issues or your own initiative ++ Help peoples having troubles installing, configuring or maintaining the project ++ Find bugs, request new features and report them into the Issues ++ Help write and correct the wiki / documentation + +Every suggestion can be interesting to implement, so don't hesitate ! diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..11d32ee --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "virt-ic" +description = "virtual integrated circuits - an backend IC emulator" +version = "0.1.0" +authors = ["Vincent Foulon "] +repository = "https://github.com/VincentFoulon80/virt-ic" +keywords = ["emulator","integrated-circuit", "backend"] +license = "MIT" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = "0.7.3" \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1be3378 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Vincent Foulon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6631dbd --- /dev/null +++ b/README.md @@ -0,0 +1,100 @@ +# Virtual Integrated Circuits + +[Changelog](https://github.com/VincentFoulon80/virt-ic/releases) + +This library is a Integrated Circuit Emulator backend that can simulate interractions between multiple chips. + +You start by creating a Board, and then you add Traces or Sockets, and then you plug Chips and link Pins together to form a virtual circuit. +You can then run the circuit to emulate the chips and links between them. + +This library is a Backend emulator, it means that there is no interface (yet) to create boards. It'll only emulate chips. + +## Chips + +- Generator +- Buttons +- Logic Gates (And, Or, Not) +- Clocks +- Memory (RAM, ROM) +- CPU (right now there is only one fictional CPU) + +# example usage + +```rust +use virt_ic::chip::gates::GateAnd; +use virt_ic::chip::generators::Generator; +use virt_ic::chip::Chip; +use virt_ic::{Board,State}; +use std::time::Duration; + +fn main() { + // create a board + let mut board = Board::new(); + // place sockets with chips on the board + let gen = board.new_socket_with(Box::new(Generator::new())); + let and_gate = board.new_socket_with(Box::new(GateAnd::new())); + // place traces + { + // VCC + let trc = board.new_trace(); + trc.borrow_mut().connect(gen.borrow_mut().get_pin(Generator::VCC).unwrap()); + trc.borrow_mut().connect(and_gate.borrow_mut().get_pin(GateAnd::VCC).unwrap()); + } + { + // GND + let trc = board.new_trace(); + trc.borrow_mut().connect(gen.borrow_mut().get_pin(Generator::GND).unwrap()); + trc.borrow_mut().connect(and_gate.borrow_mut().get_pin(GateAnd::GND).unwrap()); + } + { + // link pin "A&B" to pin "C" + let trc = board.new_trace(); + trc.borrow_mut().connect(and_gate.borrow_mut().get_pin(GateAnd::A_AND_B).unwrap()); + trc.borrow_mut().connect(and_gate.borrow_mut().get_pin(GateAnd::D).unwrap()); + } + // run the board to update its state + // we simulate 1 second segmented by 100 milliseconds + board.run_during(Duration::from_secs(1), Duration::from_millis(100)); + // test the chip + println!("ABC:\tA&B\tA&B&C"); + let a_b = and_gate.borrow_mut().get_pin_state(GateAnd::A_AND_B).as_bool(); + println!("000:\t{}\t{}", a_b, and_gate.borrow_mut().get_pin_state(GateAnd::C_AND_D).as_bool()); + + + // set some pins manually and test the result + and_gate.borrow_mut().set_pin_state(GateAnd::A, &State::High); + and_gate.borrow_mut().set_pin_state(GateAnd::B, &State::High); + and_gate.borrow_mut().set_pin_state(GateAnd::C, &State::Low); + board.run_during(Duration::from_secs(1), Duration::from_millis(100)); + + let a_b = and_gate.borrow_mut().get_pin_state(GateAnd::A_AND_B).as_bool(); + println!("110:\t{}\t{}", a_b, and_gate.borrow_mut().get_pin_state(GateAnd::C_AND_D).as_bool()); + + + and_gate.borrow_mut().set_pin_state(GateAnd::C, &State::High); + board.run_during(Duration::from_secs(1), Duration::from_millis(100)); + + let a_b = and_gate.borrow_mut().get_pin_state(GateAnd::A_AND_B).as_bool(); + println!("111:\t{}\t{}", a_b, and_gate.borrow_mut().get_pin_state(GateAnd::C_AND_D).as_bool()); + + + and_gate.borrow_mut().set_pin_state(GateAnd::A, &State::Low); + and_gate.borrow_mut().set_pin_state(GateAnd::B, &State::Low); + and_gate.borrow_mut().set_pin_state(GateAnd::C, &State::High); + board.run_during(Duration::from_secs(1), Duration::from_millis(100)); + + let a_b = and_gate.borrow_mut().get_pin_state(GateAnd::A_AND_B).as_bool(); + println!("001:\t{}\t{}", a_b, and_gate.borrow_mut().get_pin_state(GateAnd::C_AND_D).as_bool()); +} +``` + +# Documentation + +Take a look at the [generated documentation](https://docs.rs/virt-ic/). + +# Examples + +See [examples](https://github.com/VincentFoulon80/virt-ic/tree/master/examples) : +- **readme** : Same example as provided in this readme +- **ram-test** : A simple test of a RAM chip +- **cpu-test** : A simple circuit containing a minimal setup running a CPU processing factorial of 4 \ No newline at end of file diff --git a/examples/cpu-test.rs b/examples/cpu-test.rs new file mode 100644 index 0000000..13da473 --- /dev/null +++ b/examples/cpu-test.rs @@ -0,0 +1,350 @@ +use virt_ic::*; +use virt_ic::chip::gates::*; +use virt_ic::chip::generators::*; +use virt_ic::chip::memory::*; +use virt_ic::chip::cpu::*; +use virt_ic::chip::clocks::*; +use std::time::Duration; + +#[allow(dead_code)] +const HLT: u8 = 0x00; +#[allow(dead_code)] +const INA: u8 = 0x01; +#[allow(dead_code)] +const DEA: u8 = 0x02; +#[allow(dead_code)] +const INL: u8 = 0x03; +#[allow(dead_code)] +const DEL: u8 = 0x04; +#[allow(dead_code)] +const CLC: u8 = 0x05; +#[allow(dead_code)] +const ADB: u8 = 0x06; +#[allow(dead_code)] +const ADC: u8 = 0x07; +#[allow(dead_code)] +const TAB: u8 = 0x08; +#[allow(dead_code)] +const TBA: u8 = 0x09; +#[allow(dead_code)] +const TAC: u8 = 0x0A; +#[allow(dead_code)] +const TCA: u8 = 0x0B; +#[allow(dead_code)] +const TAH: u8 = 0x0C; +#[allow(dead_code)] +const THA: u8 = 0x0D; +#[allow(dead_code)] +const TAL: u8 = 0x0E; +#[allow(dead_code)] +const TLA: u8 = 0x0F; +#[allow(dead_code)] +const PHA: u8 = 0x10; +#[allow(dead_code)] +const PLA: u8 = 0x11; +#[allow(dead_code)] +const PHL: u8 = 0x12; +#[allow(dead_code)] +const PLL: u8 = 0x13; +#[allow(dead_code)] +const CPB_ACC: u8 = 0x14; +#[allow(dead_code)] +const CPC_ACC: u8 = 0x15; +#[allow(dead_code)] +const SUB: u8 = 0x16; +#[allow(dead_code)] +const SUC: u8 = 0x17; +#[allow(dead_code)] +const SAL: u8 = 0x18; +#[allow(dead_code)] +const SAR: u8 = 0x19; +#[allow(dead_code)] +const INB: u8 = 0x1A; +#[allow(dead_code)] +const DEB: u8 = 0x1B; +#[allow(dead_code)] +const INC: u8 = 0x1C; +#[allow(dead_code)] +const DEC: u8 = 0x1D; +#[allow(dead_code)] +const JML: u8 = 0x20; +#[allow(dead_code)] +const JSL: u8 = 0x21; +#[allow(dead_code)] +const RTN: u8 = 0x22; +#[allow(dead_code)] +const STA_HL: u8 = 0x48; +#[allow(dead_code)] +const STB_HL: u8 = 0x49; +#[allow(dead_code)] +const STC_HL: u8 = 0x4A; +#[allow(dead_code)] +const LDB_HL: u8 = 0x4B; +#[allow(dead_code)] +const LDC_HL: u8 = 0x4C; +#[allow(dead_code)] +const LDA_HL: u8 = 0x4D; +#[allow(dead_code)] +const LDA_HLB: u8 = 0x4E; +#[allow(dead_code)] +const LDA_HLC: u8 = 0x4F; +#[allow(dead_code)] +const LDA_NB: u8 = 0x50; +#[allow(dead_code)] +const LDA_ZP: u8 = 0x51; +#[allow(dead_code)] +const LDA_H0P: u8 = 0x52; +#[allow(dead_code)] +const LDA_HLP: u8 = 0x53; +#[allow(dead_code)] +const LDB_NB: u8 = 0x54; +#[allow(dead_code)] +const LDB_ZP: u8 = 0x55; +#[allow(dead_code)] +const LDC_NB: u8 = 0x56; +#[allow(dead_code)] +const LDC_ZP: u8 = 0x57; +#[allow(dead_code)] +const STA_ZP: u8 = 0x58; +#[allow(dead_code)] +const STA_H0P: u8 = 0x59; +#[allow(dead_code)] +const STA_HLP: u8 = 0x5A; +#[allow(dead_code)] +const STB_ZP: u8 = 0x5B; +#[allow(dead_code)] +const STC_ZP: u8 = 0x5C; +#[allow(dead_code)] +const CMP: u8 = 0x60; +#[allow(dead_code)] +const CPB_NB: u8 = 0x61; +#[allow(dead_code)] +const CPC_NB: u8 = 0x62; +#[allow(dead_code)] +const JMP: u8 = 0xB0; +#[allow(dead_code)] +const JSR: u8 = 0xB1; +#[allow(dead_code)] +const BCF: u8 = 0xB2; +#[allow(dead_code)] +const BNF: u8 = 0xB3; +#[allow(dead_code)] +const BZF: u8 = 0xB4; +#[allow(dead_code)] +const LDA_ADR: u8 = 0xC0; +#[allow(dead_code)] +const STA_ADR: u8 = 0xC1; + +fn main() { + // basic CPU setup with the following mapping : + // RAM = 0x000 to 0x0FF + // ROM = 0xF00 to 0xFFF + // Stack will be on bank 0x0 + let mut board = Board::new(); + let cpu = board.new_socket_with(Box::new(SimpleCPU::new())); + let ram = board.new_socket_with(Box::new(Ram256B::new())); + // rom chip with a simple factorial calculation program + // pre-compiled to perform a factorial of 4 + let rom = board.new_socket_with(Box::new(Rom256B::from_data( + [ + // init + // 0x00 + JSR, 0x0F, 0x28,// JSR :zeroing ram + // 0x03 + LDA_NB, 0x00, // A = 0 + // 0x05 + TAH,TAL, // HL = 0 + // 0x07 + LDA_NB, 0x01, // A = 1 + // 0x09 + LDC_NB, 0x02, // C = 2 + + // loop + // 0x0B + STA_HL, // [0xHL] = A + // 0x0C + INL, // HL++ + // 0x0D + TAB, // B = A + // 0x0E + CPC_NB, 0x05, // CMP C with 5 + // 0x10 + BZF, 0x0F, 0x27,// IF Z JMP :end + // 0x13 + LDA_NB, 0x00, // A = 0 + // 0x15 + JSR, 0x0F, 0x1C,// JSR :multiply routine : A = B*C + // 0x18 + INC, // C++ + // 0x19 + JMP, 0x0F, 0x0B,// JMP :loop + + // multiply routine A += B*C + // 0x1C + CPB_NB, 0x00, // CMP B with 0 + // 0x1E + BZF, 0x0F, 0x26,// IF Z JMP :multiply rtn + // 0x21 + ADC, // A += C + // 0x22 + DEB, // B-- + // 0x23 + JMP, 0x0F, 0x1C,// JMP :multiply routine + // multiply rtn + // 0x26 + RTN, // Return + // end + // 0x27 + HLT, // stop the processor + // zeroing ram + // 0x28 + LDA_NB, 0x00, + // 0x2A + STA_HL, // [HL] = 0 + // 0x2B + INL, // HL++ + // 0x2C + TLA, // A = L + // 0x2D + CMP, 0x10, // CMP A with 0x10 + // 0x2F + BZF, 0x0F, 0x35,// IF Z JMP :zeroing rtn + // 0x32 + JMP, 0x0F, 0x28,// JMP :zeroing ram + // zeroing rtn + // 0x35 + RTN, // Return + // padding + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + // IRQ address at 0xF00 + 0x0F,0x00, + // startup address at 0xF00 + 0x0F,0x00, + // stack bank at 0x000 to 0x0FF + 0x00 + ] + ))); + + let gen = board.new_socket_with(Box::new(Generator::new())); + + let clk = board.new_socket_with(Box::new(Clock100Hz::new())); + + let and = board.new_socket_with(Box::new(GateAnd::new())); + let not = board.new_socket_with(Box::new(GateNot::new())); + + { + // VCC + let trc = board.new_trace(); + trc.borrow_mut().connect(gen.borrow_mut().get_pin(Generator::VCC).unwrap()); + trc.borrow_mut().connect(ram.borrow_mut().get_pin(Ram256B::VCC).unwrap()); + trc.borrow_mut().connect(rom.borrow_mut().get_pin(Rom256B::VCC).unwrap()); + trc.borrow_mut().connect(cpu.borrow_mut().get_pin(SimpleCPU::VCC).unwrap()); + trc.borrow_mut().connect(cpu.borrow_mut().get_pin(SimpleCPU::RESET).unwrap()); + trc.borrow_mut().connect(cpu.borrow_mut().get_pin(SimpleCPU::IRQ).unwrap()); + trc.borrow_mut().connect(clk.borrow_mut().get_pin(Clock100Hz::VCC).unwrap()); + trc.borrow_mut().connect(and.borrow_mut().get_pin(GateAnd::VCC).unwrap()); + trc.borrow_mut().connect(not.borrow_mut().get_pin(GateNot::VCC).unwrap()); + } + { + // GND + let trc = board.new_trace(); + trc.borrow_mut().connect(gen.borrow_mut().get_pin(Generator::GND).unwrap()); + trc.borrow_mut().connect(ram.borrow_mut().get_pin(Ram256B::GND).unwrap()); + trc.borrow_mut().connect(rom.borrow_mut().get_pin(Rom256B::GND).unwrap()); + trc.borrow_mut().connect(cpu.borrow_mut().get_pin(SimpleCPU::GND).unwrap()); + trc.borrow_mut().connect(clk.borrow_mut().get_pin(Clock100Hz::GND).unwrap()); + trc.borrow_mut().connect(and.borrow_mut().get_pin(GateAnd::GND).unwrap()); + trc.borrow_mut().connect(not.borrow_mut().get_pin(GateNot::GND).unwrap()); + } + { + // CLK + let trc = board.new_trace(); + trc.borrow_mut().connect(clk.borrow_mut().get_pin(Clock100Hz::CLK).unwrap()); + trc.borrow_mut().connect(cpu.borrow_mut().get_pin(SimpleCPU::CLOCK).unwrap()); + } + { + // AND + // link A&B with C&D to make (A&B)&(C&D) + // also link the result in a not gate + let trc = board.new_trace(); + trc.borrow_mut().connect(and.borrow_mut().get_pin(GateAnd::A_AND_B).unwrap()); + trc.borrow_mut().connect(and.borrow_mut().get_pin(GateAnd::G).unwrap()); + + let trc = board.new_trace(); + trc.borrow_mut().connect(and.borrow_mut().get_pin(GateAnd::C_AND_D).unwrap()); + trc.borrow_mut().connect(and.borrow_mut().get_pin(GateAnd::H).unwrap()); + } + + // CPU connections + for i in 0..=6 { + // A0 - A6 + let trc = board.new_trace(); + trc.borrow_mut().connect(cpu.borrow_mut().get_pin(SimpleCPU::A0+i).unwrap()); + trc.borrow_mut().connect(ram.borrow_mut().get_pin(Ram256B::A0+i).unwrap()); + trc.borrow_mut().connect(rom.borrow_mut().get_pin(Rom256B::A0+i).unwrap()); + } + { + // A7 + let trc = board.new_trace(); + trc.borrow_mut().connect(cpu.borrow_mut().get_pin(SimpleCPU::A7).unwrap()); + trc.borrow_mut().connect(ram.borrow_mut().get_pin(Ram256B::A7).unwrap()); + trc.borrow_mut().connect(rom.borrow_mut().get_pin(Rom256B::A7).unwrap()); + } + { + // CPU A8 - 12 + let trc = board.new_trace(); + trc.borrow_mut().connect(cpu.borrow_mut().get_pin(SimpleCPU::A8).unwrap()); + trc.borrow_mut().connect(and.borrow_mut().get_pin(GateAnd::A).unwrap()); + + let trc = board.new_trace(); + trc.borrow_mut().connect(cpu.borrow_mut().get_pin(SimpleCPU::A9).unwrap()); + trc.borrow_mut().connect(and.borrow_mut().get_pin(GateAnd::B).unwrap()); + + let trc = board.new_trace(); + trc.borrow_mut().connect(cpu.borrow_mut().get_pin(SimpleCPU::A10).unwrap()); + trc.borrow_mut().connect(and.borrow_mut().get_pin(GateAnd::C).unwrap()); + + let trc = board.new_trace(); + trc.borrow_mut().connect(cpu.borrow_mut().get_pin(SimpleCPU::A11).unwrap()); + trc.borrow_mut().connect(and.borrow_mut().get_pin(GateAnd::D).unwrap()); + + let trc = board.new_trace(); + trc.borrow_mut().connect(and.borrow_mut().get_pin(GateAnd::G_AND_H).unwrap()); + trc.borrow_mut().connect(not.borrow_mut().get_pin(GateNot::A).unwrap()); + trc.borrow_mut().connect(ram.borrow_mut().get_pin(Ram256B::CS).unwrap()); + + } + for i in 0..=7{ + // CPU IO0-7 + let trc = board.new_trace(); + trc.borrow_mut().connect(cpu.borrow_mut().get_pin(SimpleCPU::IO0+i).unwrap()); + trc.borrow_mut().connect(ram.borrow_mut().get_pin(Ram256B::IO0+i).unwrap()); + trc.borrow_mut().connect(rom.borrow_mut().get_pin(Rom256B::IO0+i).unwrap()); + } + { + // Ram and Rom CS, WE and OE + + let trc = board.new_trace(); + trc.borrow_mut().connect(not.borrow_mut().get_pin(GateNot::NOT_A).unwrap()); + trc.borrow_mut().connect(rom.borrow_mut().get_pin(Rom256B::CS).unwrap()); + + let trc = board.new_trace(); + trc.borrow_mut().connect(cpu.borrow_mut().get_pin(SimpleCPU::RW).unwrap()); + trc.borrow_mut().connect(ram.borrow_mut().get_pin(Ram256B::WE).unwrap()); + trc.borrow_mut().connect(not.borrow_mut().get_pin(GateNot::B).unwrap()); + + let trc = board.new_trace(); + trc.borrow_mut().connect(not.borrow_mut().get_pin(GateNot::NOT_B).unwrap()); + trc.borrow_mut().connect(ram.borrow_mut().get_pin(Ram256B::OE).unwrap()); + trc.borrow_mut().connect(rom.borrow_mut().get_pin(Rom256B::OE).unwrap()); + } + board.run(Duration::from_millis(1)); + + println!("ROM:\n{:?}", rom.borrow_mut().get_chip().as_ref().unwrap()); + println!("RAM before:\n{:?}", ram.borrow_mut().get_chip().as_ref().unwrap()); + + board.run_during(Duration::from_secs(10), Duration::from_millis(1)); + + println!("RAM after:\n{:?}", ram.borrow_mut().get_chip().as_ref().unwrap()); + println!("CPU:\n{:?}", cpu.borrow_mut().get_chip().as_ref().unwrap()); +} \ No newline at end of file diff --git a/examples/ram-test.rs b/examples/ram-test.rs new file mode 100644 index 0000000..b000a72 --- /dev/null +++ b/examples/ram-test.rs @@ -0,0 +1,92 @@ +use virt_ic::chip::memory::Ram256B; +use virt_ic::chip::Chip; +use virt_ic::State; + +fn main() { + let mut ram = Ram256B::new(); + + ram.run(std::time::Duration::from_secs(1)); + + println!("non alimented:\n{}", ram.to_string()); + + + ram.get_pin(11).unwrap().borrow_mut().state = State::Low; + ram.get_pin(22).unwrap().borrow_mut().state = State::High; + + ram.run(std::time::Duration::from_secs(1)); + + println!("alimented:\n{}", ram.to_string()); + + + ram.get_pin(4).unwrap().borrow_mut().state = State::Low; + ram.get_pin(5).unwrap().borrow_mut().state = State::Low; + ram.get_pin(6).unwrap().borrow_mut().state = State::Low; + ram.get_pin(7).unwrap().borrow_mut().state = State::Low; + ram.get_pin(8).unwrap().borrow_mut().state = State::Low; + ram.get_pin(9).unwrap().borrow_mut().state = State::Low; + ram.get_pin(10).unwrap().borrow_mut().state = State::Low; + ram.get_pin(12).unwrap().borrow_mut().state = State::Low; + + ram.get_pin(13).unwrap().borrow_mut().state = State::Low; + ram.get_pin(14).unwrap().borrow_mut().state = State::Low; + ram.get_pin(15).unwrap().borrow_mut().state = State::Low; + ram.get_pin(16).unwrap().borrow_mut().state = State::Low; + ram.get_pin(17).unwrap().borrow_mut().state = State::Low; + ram.get_pin(18).unwrap().borrow_mut().state = State::Low; + ram.get_pin(19).unwrap().borrow_mut().state = State::Low; + ram.get_pin(20).unwrap().borrow_mut().state = State::Low; + + ram.get_pin(1).unwrap().borrow_mut().state = State::Low; + ram.get_pin(2).unwrap().borrow_mut().state = State::Low; + ram.get_pin(3).unwrap().borrow_mut().state = State::High; + + ram.run(std::time::Duration::from_secs(1)); + + println!("write first byte:\n{}", ram.to_string()); + + + ram.get_pin(4).unwrap().borrow_mut().state = State::High; + + ram.get_pin(14).unwrap().borrow_mut().state = State::High; + + ram.run(std::time::Duration::from_secs(1)); + + println!("write second byte:\n{}", ram.to_string()); + + + ram.get_pin(1).unwrap().borrow_mut().state = State::Low; + ram.get_pin(2).unwrap().borrow_mut().state = State::High; + ram.get_pin(3).unwrap().borrow_mut().state = State::Low; + + ram.run(std::time::Duration::from_secs(1)); + + println!("read second byte:\n{}{}{}{}{}{}{}{}", + if ram.get_pin(20).unwrap().borrow().state == State::High {1} else {0}, + if ram.get_pin(19).unwrap().borrow().state == State::High {1} else {0}, + if ram.get_pin(18).unwrap().borrow().state == State::High {1} else {0}, + if ram.get_pin(17).unwrap().borrow().state == State::High {1} else {0}, + if ram.get_pin(16).unwrap().borrow().state == State::High {1} else {0}, + if ram.get_pin(15).unwrap().borrow().state == State::High {1} else {0}, + if ram.get_pin(14).unwrap().borrow().state == State::High {1} else {0}, + if ram.get_pin(13).unwrap().borrow().state == State::High {1} else {0} + ); + + + ram.get_pin(4).unwrap().borrow_mut().state = State::Low; + ram.get_pin(5).unwrap().borrow_mut().state = State::High; + + ram.run(std::time::Duration::from_secs(1)); + + println!("read third byte:\n{}{}{}{}{}{}{}{}", + if ram.get_pin(20).unwrap().borrow().state == State::High {1} else {0}, + if ram.get_pin(19).unwrap().borrow().state == State::High {1} else {0}, + if ram.get_pin(18).unwrap().borrow().state == State::High {1} else {0}, + if ram.get_pin(17).unwrap().borrow().state == State::High {1} else {0}, + if ram.get_pin(16).unwrap().borrow().state == State::High {1} else {0}, + if ram.get_pin(15).unwrap().borrow().state == State::High {1} else {0}, + if ram.get_pin(14).unwrap().borrow().state == State::High {1} else {0}, + if ram.get_pin(13).unwrap().borrow().state == State::High {1} else {0} + ); + + println!("\n\n"); +} \ No newline at end of file diff --git a/examples/readme.rs b/examples/readme.rs new file mode 100644 index 0000000..bcefe60 --- /dev/null +++ b/examples/readme.rs @@ -0,0 +1,65 @@ +use virt_ic::chip::gates::GateAnd; +use virt_ic::chip::generators::Generator; +use virt_ic::chip::Chip; +use virt_ic::{Board,State}; +use std::time::Duration; + +fn main() { + // create a board + let mut board = Board::new(); + // place sockets with chips on the board + let gen = board.new_socket_with(Box::new(Generator::new())); + let and_gate = board.new_socket_with(Box::new(GateAnd::new())); + // place traces + { + // VCC + let trc = board.new_trace(); + trc.borrow_mut().connect(gen.borrow_mut().get_pin(Generator::VCC).unwrap()); + trc.borrow_mut().connect(and_gate.borrow_mut().get_pin(GateAnd::VCC).unwrap()); + } + { + // GND + let trc = board.new_trace(); + trc.borrow_mut().connect(gen.borrow_mut().get_pin(Generator::GND).unwrap()); + trc.borrow_mut().connect(and_gate.borrow_mut().get_pin(GateAnd::GND).unwrap()); + } + { + // link pin "A&B" to pin "C" + let trc = board.new_trace(); + trc.borrow_mut().connect(and_gate.borrow_mut().get_pin(GateAnd::A_AND_B).unwrap()); + trc.borrow_mut().connect(and_gate.borrow_mut().get_pin(GateAnd::D).unwrap()); + } + // run the board to update its state + // we simulate 1 second segmented by 100 milliseconds + board.run_during(Duration::from_secs(1), Duration::from_millis(100)); + // test the chip + println!("ABC:\tA&B\tA&B&C"); + let a_b = and_gate.borrow_mut().get_pin_state(GateAnd::A_AND_B).as_bool(); + println!("000:\t{}\t{}", a_b, and_gate.borrow_mut().get_pin_state(GateAnd::C_AND_D).as_bool()); + + + // set some pins manually and test the result + and_gate.borrow_mut().set_pin_state(GateAnd::A, &State::High); + and_gate.borrow_mut().set_pin_state(GateAnd::B, &State::High); + and_gate.borrow_mut().set_pin_state(GateAnd::C, &State::Low); + board.run_during(Duration::from_secs(1), Duration::from_millis(100)); + + let a_b = and_gate.borrow_mut().get_pin_state(GateAnd::A_AND_B).as_bool(); + println!("110:\t{}\t{}", a_b, and_gate.borrow_mut().get_pin_state(GateAnd::C_AND_D).as_bool()); + + + and_gate.borrow_mut().set_pin_state(GateAnd::C, &State::High); + board.run_during(Duration::from_secs(1), Duration::from_millis(100)); + + let a_b = and_gate.borrow_mut().get_pin_state(GateAnd::A_AND_B).as_bool(); + println!("111:\t{}\t{}", a_b, and_gate.borrow_mut().get_pin_state(GateAnd::C_AND_D).as_bool()); + + + and_gate.borrow_mut().set_pin_state(GateAnd::A, &State::Low); + and_gate.borrow_mut().set_pin_state(GateAnd::B, &State::Low); + and_gate.borrow_mut().set_pin_state(GateAnd::C, &State::High); + board.run_during(Duration::from_secs(1), Duration::from_millis(100)); + + let a_b = and_gate.borrow_mut().get_pin_state(GateAnd::A_AND_B).as_bool(); + println!("001:\t{}\t{}", a_b, and_gate.borrow_mut().get_pin_state(GateAnd::C_AND_D).as_bool()); +} \ No newline at end of file diff --git a/src/board.rs b/src/board.rs new file mode 100644 index 0000000..d528738 --- /dev/null +++ b/src/board.rs @@ -0,0 +1,62 @@ +use super::{Trace, Socket, Chip}; +use std::cell::RefCell; +use std::rc::Rc; + +#[derive(Default)] +pub struct Board { + traces: Vec>>, + sockets: Vec>> +} + +impl Board { + /// Create a new empty Board + pub fn new() -> Board { + Board { + traces: vec![], + sockets: vec![] + } + } + + /// Create a new trace and return it + pub fn new_trace(&mut self) -> Rc> { + let trace = Rc::new(RefCell::new(Trace::new())); + self.traces.push(trace); + self.traces.last_mut().unwrap().clone() + } + + /// Create a new socket and return it + pub fn new_socket(&mut self) -> Rc> { + let socket = Rc::new(RefCell::new(Socket::new())); + self.sockets.push(socket); + self.sockets.last_mut().unwrap().clone() + } + + /// Create a new socket with a chip and return it + pub fn new_socket_with(&mut self, chip: Box) -> Rc> { + let socket = Rc::new(RefCell::new(Socket::new())); + socket.borrow_mut().plug(chip); + self.sockets.push(socket); + self.sockets.last_mut().unwrap().clone() + } + + /// Run the circuit for a certain amount of time + pub fn run(&mut self, time_elapsed : std::time::Duration) { + // TODO: find a way to update the traces accurately + // current issue : the order of the traces affects the order of the links + for trc in self.traces.iter_mut() { + trc.borrow_mut().communicate(); + } + for skt in self.sockets.iter_mut() { + skt.borrow_mut().run(time_elapsed); + } + } + + /// Run the circuit for a certain amount of time segmented by a step + pub fn run_during(&mut self, duration: std::time::Duration, step: std::time::Duration) { + let mut elapsed = std::time::Duration::new(0,0); + while elapsed < duration { + self.run(step); + elapsed += step; + } + } +} \ No newline at end of file diff --git a/src/chip/buttons.rs b/src/chip/buttons.rs new file mode 100644 index 0000000..46419f1 --- /dev/null +++ b/src/chip/buttons.rs @@ -0,0 +1,68 @@ +use super::super::State; +use super::{Pin, PinType, Chip}; +use std::cell::RefCell; +use std::rc::Rc; + +/// # A simple button +/// Transmit the IN signal in the OUT pin when he is down +/// you'll need to use `press()` and `release()` to change its state +/// +/// # Diagram +/// ``` +/// -------- +/// IN --|1 2|-- OUT +/// -------- +/// ``` +#[derive(Debug)] +pub struct Button { + pin: [Rc>; 2], + down: bool +} +impl Default for Button { + fn default() -> Self { + Self::new() + } +} + +impl Button { + pub const IN: u8 = 1; + pub const OUT: u8 = 2; + + pub fn new() -> Self { + Button { + pin: [ + Rc::new(RefCell::new(Pin::new(1, PinType::Input))), + Rc::new(RefCell::new(Pin::new(2, PinType::Output))), + ], + down: false + } + } + + pub fn press(&mut self) { + self.down = true; + } + + pub fn release(&mut self) { + self.down = false; + } +} +impl Chip for Button { + fn get_pin_qty(&self) -> u8 { + 2 + } + + fn get_pin(&mut self, pin: u8) -> Result>, &str> { + if pin > 0 && pin <= 2 { + Ok(self.pin[pin as usize-1].clone()) + } else { + Err("Pin out of bounds") + } + } + fn run(&mut self, _: std::time::Duration) { + if self.down { + self.pin[1].borrow_mut().unwrap().state = self.pin[0].borrow().unwrap().state; + } else { + self.pin[1].borrow_mut().unwrap().state = State::Undefined; + } + } +} \ No newline at end of file diff --git a/src/chip/clocks.rs b/src/chip/clocks.rs new file mode 100644 index 0000000..c512b4c --- /dev/null +++ b/src/chip/clocks.rs @@ -0,0 +1,77 @@ +use super::super::State; +use super::{Pin, PinType, Chip}; +use std::cell::RefCell; +use std::rc::Rc; +use std::time::Duration; + +/// A 100 Hz simple clock +/// CLK: clock +/// ``` +/// -------- +/// CLK --|1 4|-- VCC +/// GND --|2 3|-- UNUSED +/// -------- +/// ``` +#[derive(Debug)] +pub struct Clock100Hz { + pin: [Rc>; 4], + timer: Duration, + active: bool +} +impl Default for Clock100Hz { + fn default() -> Self { + Self::new() + } +} + +impl Clock100Hz { + pub const CLK: u8 = 1; + pub const VCC: u8 = 4; + pub const GND: u8 = 2; + + pub fn new() -> Self { + Clock100Hz { + pin: [ + Rc::new(RefCell::new(Pin::new(1, PinType::Output))), + Rc::new(RefCell::new(Pin::new(2, PinType::Input))), + Rc::new(RefCell::new(Pin::new(3, PinType::Input))), + Rc::new(RefCell::new(Pin::new(4, PinType::Input))), + ], + timer: Duration::new(0,0), + active: false, + } + } +} +impl Chip for Clock100Hz { + fn get_pin_qty(&self) -> u8 { + 4 + } + + fn get_pin(&mut self, pin: u8) -> Result>, &str> { + if pin > 0 && pin <= 4 { + Ok(self.pin[pin as usize-1].clone()) + } else { + Err("Pin out of bounds") + } + } + fn run(&mut self, time_elapsed: std::time::Duration) { + if self.active { + self.active = false; + self.pin[0].borrow_mut().state = State::Low; + } + // check alimented + static LIMIT: Duration = Duration::from_millis(10); + if self.pin[1].borrow().state == State::Low && self.pin[3].borrow().state == State::High { + self.timer += time_elapsed; + if self.timer > LIMIT { + while self.timer > LIMIT { + self.timer -= LIMIT; + } + self.active = true; + self.pin[0].borrow_mut().state = State::High; + } + } else { + self.timer = Duration::new(0,0); + } + } +} \ No newline at end of file diff --git a/src/chip/cpu.rs b/src/chip/cpu.rs new file mode 100644 index 0000000..3dcbea9 --- /dev/null +++ b/src/chip/cpu.rs @@ -0,0 +1,932 @@ +use super::super::State; +use super::{Pin, PinType, Chip}; +use std::cell::RefCell; +use std::rc::Rc; + +/// # A simple example CPU +/// - 1M of address space (10 ADDR pins) +/// - 8-bit IO Pins +/// - 3 data register (Accumulator, B and C) +/// - 2 address registers (H and L forming the full address HL) +/// +/// # Instructions +/// +/// On startup or RESET, the CPU will fetch the boot address at 0xFFD and 0xFFE. +/// The Addresses are stored in this order : MSD, LSD, so 0x0F and 0x12 will create address 0xF12. +/// +/// The stack pointer will be initialized in the bank specified at 0xFFF. +/// If 0xFFF contains 0x0E, the CPU will use 0x0E00 to 0x0EFF for his stack. +/// Note that the bank can't go beyond 0x0F since the CPU only has a 12-bit address space. +/// +/// TODO: Implement IRQ +/// On IRQ, the CPU will fetch the Interrupt code address at 0xFFB and 0xFFC. +/// When IRQ is triggered, the Address at 0xFFC and 0xFFD will be used as a JSR opcode. +/// To return to the main code, you'll just need to execute a RTN opcode. +/// +/// +/// # Opcodes +/// MSD\LSD| x0| x1| x2| x3| x4| x5| x6| x7| x8| x9| xA| xB| xC| xD| xE| xF| | +/// -------|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +/// 0x |HLT|INA|DEA|INL|DEL|CLC|ADB|ADC|TAB|TBA|TAC|TCA|TAH|THA|TAL|TLA| 0x| +/// 1x |PHA|PLA|PHL|PLL|CPB|CPC|SUB|SUC|SAL|SAR| | | | | | | 1x| +/// 2x |JML|JSL|RTN| | | | | | | | | | | | | | 2x| +/// 3x | | | | | | | | | | | | | | | | | 3x| +/// 4x | | | | | | | | |STA|STB|STC|LDB|LDC|LDA|LDA|LDA| 4x| +/// 5x |LDA|LDA|LDA|LDA|LDB|LDB|LDC|LDC|STA|STA|STA|STB|STC| | | | 5x| +/// 6x |CMP|CPB|CPC| | | | | | | | | | | | | | 6x| +/// 7x | | | | | | | | | | | | | | | | | 7x| +/// 8x | | | | | | | | | | | | | | | | | 8x| +/// 9x | | | | | | | | | | | | | | | | | 9x| +/// Ax | | | | | | | | | | | | | | | | | Ax| +/// Bx |JMP|JSR|BCF|BNF|BZF| | | | | | | | | | | | Bx| +/// Cx |LDA|STA| | | | | | | | | | | | | | | Cx| +/// Dx | | | | | | | | | | | | | | | | | Dx| +/// Ex | | | | | | | | | | | | | | | | | Ex| +/// Fx | | | | | | | | | | | | | | | | | Fx| +/// - | x0| x1| x2| x3| x4| x5| x6| x7| x8| x9| xA| xB| xC| xD| xE| xF| | +/// +/// Opcode|Parameters|Description| +/// ------|----------|-----------| +/// HLT (0x00) | - |Halts the CPU| +/// INA (0x01) | - |Increments the accumulator| +/// DEA (0x02) | - |Decrements the accumulator| +/// INL (0x03) | - |Increments the HL register| +/// DEL (0x04) | - |Decrements the HL register| +/// CLC (0x05) | - |Clear the Carry flag| +/// ADB (0x06) | - |Add B to Accumulator| +/// ADC (0x07) | - |Add C to Accumulator| +/// TAB (0x08) | - |Transfer Accumulator into B register| +/// TBA (0x09) | - |Transfer B register into Accumulator| +/// TAC (0x0A) | - |Transfer Accumulator into C register| +/// TCA (0x0B) | - |Transfer C register into Accumulator| +/// TAH (0x0C) | - |Transfer Accumulator into H register| +/// THA (0x0D) | - |Transfer H register into Accumulator| +/// TAL (0x0E) | - |Transfer Accumulator into L register| +/// TLA (0x0F) | - |Transfer L register into Accumulator| +/// PHA (0x10) | - |Push the accumulator's value in the stack| +/// PLA (0x11) | - |Pull the stack value in the accumulator| +/// PHL (0x12) | - |Push the HL's value in the stack| +/// PLL (0x13) | - |Pull two bytes from the stack in the HL register| +/// CPB (0x14) | - |Compare B with the Accumulator| +/// CPC (0x15) | - |Compare C with the Accumulator| +/// SUB (0x16) | - |Substract B to accumulator| +/// SUC (0x17) | - |Substract C to accumulator| +/// SAL (0x18) | - |Shift Accumulator Left| +/// SAR (0x19) | - |Shift Accumulator Right| +/// INB (0x1A) | - |Increments the B register| +/// DEB (0x1B) | - |Decrements the B register| +/// INC (0x1C) | - |Increments the C register| +/// DEC (0x1D) | - |Decrements the C register| +/// - | - | - | +/// JML (0x20) | - |Jumps to the address HL +/// JSL (0x21) | - |Jumps to subroutine at address HL +/// RTN (0x22) | - |Return from SubRoutine| +/// - | - | - | +/// STA (0x48) | - |Stores the value of accumulator into address [HL]| +/// STB (0x49) | - |Stores the value of B register into address [HL]| +/// STC (0x4A) | - |Stores the value of C register into address [HL]| +/// LDB (0x4B) | - |Loads the value of address [HL] into the B register| +/// LDC (0x4C) | - |Loads the value of address [HL] into the C register| +/// LDA (0x4D) | - |Loads the value of address [HL] into the accumulator| +/// LDA (0x4E) | - |Loads the value of address [HL]+B into the accumulator| +/// LDA (0x4F) | - |Loads the value of address [HL]+C into the accumulator| +/// LDA (0x50) | $1: number |Loads $1 into the accumulator| +/// LDA (0x51) | [$1]: zero page address |Loads the value of address 0x0$1 into the accumulator| +/// LDA (0x52) | $1: number |Loads the value of address H0 + $1 into the accumulator| +/// LDA (0x53) | $1: number |Loads the value of address HL + $1 into the accumulator| +/// LDB (0x54) | $1: number |Loads $1 into the B register| +/// LDB (0x55) | [$1]: zero page address |Loads the value of address 0x0$1 into the B register| +/// LDC (0x56) | $1: number |Loads $1 into the C register| +/// LDC (0x57) | [$1]: zero page address |Loads the value of address 0x0$1 into the C register| +/// STA (0x58) | [$1]: zero page address |Stores the value of Accumulator into address 0x0$1| +/// STA (0x59) | $1: number |Stores the value of Accumulator into address H0 + $1 | +/// STA (0x5A) | $1: number |Stores the value of Accumulator into address HL + $1| +/// STB (0x5B) | [$1]: zero page address |Stores the value of B register into address 0x0$1| +/// STC (0x5C) | [$1]: zero page address |Stores the value of C register into address 0x0$1| +/// - | - | - | +/// CMP (0x60) | $1: number |Compares the accumulator with $1| +/// CPB (0x61) | $1: number |Compares the B register with $1| +/// CPC (0x62) | $1: number |Compares the C register with $1| +/// - | - | - | +/// JMP (0xB0) | $1$2: address| Jumps to the address $1$2 +/// JSR (0xB1) | $1$2: address| Jumps to subroutine at address $1$2 +/// BCF (0xB2) | $1$2: address| Branch on Carry flag +/// BNF (0xB3) | $1$2: address| Branch on Negative flag +/// BZF (0xB4) | $1$2: address| Branch on Zero flag +/// - | - | - | +/// LDA (0xC0) | $1$2: address| Load the value of address $1$2 in the accumulator +/// STA (0xC1) | $1$2: address| Store the value of accumulator into address $1$2 +/// +/// # diagram +/// IRQ: Interrupt Request +/// A0-9: Addresses +/// IO0-7: Input/Output +/// ``` +/// ---__--- +/// A0 --|1 26|-- VCC +/// A1 --|2 25|-- R/!W +/// A2 --|3 24|-- IO7 +/// A3 --|4 23|-- IO6 +/// A4 --|5 22|-- IO5 +/// A5 --|6 21|-- IO4 +/// A6 --|7 20|-- IO3 +/// A7 --|8 19|-- IO2 +/// A8 --|9 18|-- IO1 +/// A9 --|10 17|-- IO0 +/// A10 --|11 16|-- !IRQ +/// A11 --|12 15|-- !RESET +/// GND --|13 14|-- CLOCK +/// -------- +/// ``` +pub struct SimpleCPU { + pin: [Rc>; 26], + program_counter: u16, + accumulator: u8, + stack_bank: u8, + stack_pointer: u8, + reg_b: u8, + reg_c: u8, + reg_h: u8, + reg_l: u8, + flag_zero: bool, + flag_neg: bool, + flag_carry: bool, + flag_overflow: bool, + current_opcode: u8, + param_first: u8, + param_second: u8, + microcode_state: u8, + executing: bool, + initializing: bool, + halted: bool +} +impl Default for SimpleCPU { + fn default() -> Self { + Self::new() + } +} +impl std::fmt::Debug for SimpleCPU { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + fmt.write_str(format!("PC: {:03X}\tADR: {:03X}\tIO: {:02X}\tOp: {:02X}\t$1: {:02X}\t$2: {:02X}\tA: {:02X}\tB: {:02X}\tC: {:02X}\tH: {:02X}\tL: {:02X}\tSP: {:02X}\tmc: {}\texec: {}", self.program_counter, self.get_address(), self.get_data(), self.current_opcode, self.param_first, self.param_second, self.accumulator, self.reg_b, self.reg_c, self.reg_h, self.reg_l, self.stack_pointer, self.microcode_state, self.executing).as_str())?; + Ok(()) + } +} + +impl SimpleCPU { + pub const A0: u8 = 1; + pub const A1: u8 = 2; + pub const A2: u8 = 3; + pub const A3: u8 = 4; + pub const A4: u8 = 5; + pub const A5: u8 = 6; + pub const A6: u8 = 7; + pub const A7: u8 = 8; + pub const A8: u8 = 9; + pub const A9: u8 = 10; + pub const A10: u8 = 11; + pub const A11: u8 = 12; + pub const CLOCK: u8 = 14; + pub const RESET: u8 = 15; + pub const IRQ: u8 = 16; + pub const IO0: u8 = 17; + pub const IO1: u8 = 18; + pub const IO2: u8 = 19; + pub const IO3: u8 = 20; + pub const IO4: u8 = 21; + pub const IO5: u8 = 22; + pub const IO6: u8 = 23; + pub const IO7: u8 = 24; + pub const RW: u8 = 25; + pub const VCC: u8 = 26; + pub const GND: u8 = 13; + + pub fn new() -> Self { + SimpleCPU { + pin: [ + Rc::new(RefCell::new(Pin::new(1, PinType::Output))), + Rc::new(RefCell::new(Pin::new(2, PinType::Output))), + Rc::new(RefCell::new(Pin::new(3, PinType::Output))), + Rc::new(RefCell::new(Pin::new(4, PinType::Output))), + Rc::new(RefCell::new(Pin::new(5, PinType::Output))), + Rc::new(RefCell::new(Pin::new(6, PinType::Output))), + Rc::new(RefCell::new(Pin::new(7, PinType::Output))), + Rc::new(RefCell::new(Pin::new(8, PinType::Output))), + Rc::new(RefCell::new(Pin::new(9, PinType::Output))), + Rc::new(RefCell::new(Pin::new(10, PinType::Output))), + Rc::new(RefCell::new(Pin::new(11, PinType::Output))), + Rc::new(RefCell::new(Pin::new(12, PinType::Output))), + Rc::new(RefCell::new(Pin::new(13, PinType::Input))), + Rc::new(RefCell::new(Pin::new(14, PinType::Input))), + Rc::new(RefCell::new(Pin::new(15, PinType::Input))), + Rc::new(RefCell::new(Pin::new(16, PinType::Input))), + Rc::new(RefCell::new(Pin::new(17, PinType::Input))), + Rc::new(RefCell::new(Pin::new(18, PinType::Input))), + Rc::new(RefCell::new(Pin::new(19, PinType::Input))), + Rc::new(RefCell::new(Pin::new(20, PinType::Input))), + Rc::new(RefCell::new(Pin::new(21, PinType::Input))), + Rc::new(RefCell::new(Pin::new(22, PinType::Input))), + Rc::new(RefCell::new(Pin::new(23, PinType::Input))), + Rc::new(RefCell::new(Pin::new(24, PinType::Input))), + Rc::new(RefCell::new(Pin::new(25, PinType::Output))), + Rc::new(RefCell::new(Pin::new(26, PinType::Input))) + ], + program_counter: 0, + accumulator: 0, + stack_bank: 0, + stack_pointer: 0, + reg_b: 0, + reg_c: 0, + reg_h: 0, + reg_l: 0, + flag_zero: false, + flag_neg: false, + flag_carry: false, + flag_overflow: false, + current_opcode: 0, + param_first: 0, + param_second: 0, + microcode_state: 0, + executing: false, + initializing: true, + halted: false + } + } + + fn get_address(&self) -> u16 { + let mut addr: u16 = 0; + for i in 0..12 { + let bit = if self.pin[i].borrow().state == State::High {1} else {0}; + addr += bit << i; + } + addr + } + + fn set_address(&mut self, addr: u16) { + // mask overflowing addr + let mut addr = addr; + if addr > 0xFFF { + addr = 0; + } + // set the address pins + for i in 0..12 { + self.pin[i].borrow_mut().state = State::from_u16(addr, i); + } + } + + fn get_data(&self) -> u8 { + let mut addr: u8 = 0; + for i in 16..24 { + let bit = if self.pin[i].borrow().state == State::High {1} else {0}; + addr += bit << (i-16); + } + addr + } + + fn set_data(&mut self, data: u8) { + // set the IO pins + for i in 0..8 { + let mut pin = self.pin[i+16].borrow_mut(); + pin.pin_type = PinType::Output; + pin.state = State::from_u8(data, i); + } + // set R/!W pin + self.pin[24].borrow_mut().state = State::Low; + } + + fn set_iopin_type(&mut self, pin_type: PinType) { + // set IO pins + for i in 0..8 { + self.pin[i+16].borrow_mut().pin_type = pin_type.clone(); + } + // set R/!W pin + self.pin[24].borrow_mut().state = match pin_type { + PinType::Input => State::High, + PinType::Output => State::Low, + PinType::Undefined => State::Undefined + } + } + + /// CPU's boot sequence + fn boot(&mut self){ + match self.microcode_state { + 0 => { + // initialize registers + // + query MSB address + self.program_counter = 0x00; + self.stack_pointer = 0; + self.accumulator = 0; + self.reg_b = 0; + self.reg_c = 0; + self.reg_h = 0; + self.reg_l = 0; + self.set_address(0xFFD); + }, + 1 => { + // get first part of starting address + // + query LSB address + self.program_counter += (self.get_data() as u16) << 8; + self.program_counter &= 0xFFF; + self.set_address(0xFFE); + }, + 2 => { + // get second part of starting address + // + query Stack pointer bank + self.program_counter += self.get_data() as u16; + self.set_address(0xFFF); + }, + 3 => { + // get stack bank and stop the init process + self.stack_bank = self.get_data(); + self.stack_pointer = 0xFF; + self.initializing = false; + self.microcode_state = 0xFF; + }, + _ => { + self.initializing = false; + self.microcode_state = 0xFF; + } + } + } + + /// CPU's execution process + fn execute(&mut self) { + self.executing = false; + let mut check_zero = true; + + let push_stack = |myself: &mut SimpleCPU, data: u8| { + let sp = (((myself.stack_bank as u16) << 8) + myself.stack_pointer as u16) & 0xFFF; + myself.set_address(sp); + myself.set_data(data); + myself.stack_pointer = myself.stack_pointer.wrapping_sub(1); + if myself.stack_pointer == 0 { + myself.flag_overflow = true; + } + }; + let req_pull_stack = |myself: &mut SimpleCPU| { + myself.stack_pointer = myself.stack_pointer.wrapping_add(1); + let sp = (((myself.stack_bank as u16) << 8) + myself.stack_pointer as u16) & 0xFFF; + if myself.stack_pointer == 0xFF { + myself.flag_neg = false; + } + myself.set_address(sp); + }; + + match self.current_opcode { + 0x00 => { // HLT : Halt + self.halted = true; + }, + 0x01 => { // INA : Increment Acc + self.accumulator = self.accumulator.wrapping_add(1); + if self.accumulator == 0 { + self.flag_overflow = true; + self.flag_neg = false; + } + } + 0x02 => { // DEA : Decrement Acc + self.accumulator = self.accumulator.wrapping_sub(1); + if self.accumulator == 0xFF { + self.flag_overflow = false; + self.flag_neg = true; + } + }, + 0x03 => { // INL : Increment HL + let mut hl = ((self.reg_h as u16) << 8) + self.reg_l as u16; + hl = hl.wrapping_add(1); + if hl == 0 { + self.flag_overflow = true; + self.flag_neg = false; + } + self.reg_h = (hl >> 8) as u8; + self.reg_l = (hl & 0xFF) as u8; + }, + 0x04 => { // DEL : Decrement HL + let mut hl = ((self.reg_h as u16) << 8) + self.reg_l as u16; + hl = hl.wrapping_sub(1); + if hl == 0xFFFF { + self.flag_overflow = false; + self.flag_neg = true; + } + self.reg_h = (hl >> 8) as u8; + self.reg_l = (hl & 0xFF) as u8; + }, + 0x05 => { // CLC : Clear Carry + self.flag_carry = false; + }, + 0x06 => { // ADB : Add B to Acc + let add = self.accumulator as u16 + self.reg_b as u16; + if add > u8::MAX as u16 { + self.flag_carry = true; + self.flag_overflow = true; + } + self.accumulator = self.accumulator.wrapping_add(self.reg_b); + }, + 0x07 => { // ADC : Add C to Acc + let add = self.accumulator as u16 + self.reg_c as u16; + if add > u8::MAX as u16 { + self.flag_carry = true; + self.flag_overflow = true; + } + self.accumulator = self.accumulator.wrapping_add(self.reg_c); + }, + 0x08 => { // TAB : Transfer Acc > B + self.reg_b = self.accumulator; + }, + 0x09 => { // TBA : Transfer B > Acc + self.accumulator = self.reg_b; + }, + 0x0A => { // TAC : Transfer Acc > C + self.reg_c = self.accumulator; + }, + 0x0B => { // TCA : Transfer C > Acc + self.accumulator = self.reg_c; + }, + 0x0C => { // TAH : Transfer Acc > H + self.reg_h = self.accumulator; + }, + 0x0D => { // THA : Transfer H > Acc + self.accumulator = self.reg_h; + }, + 0x0E => { // TAL : Transfer Acc > L + self.reg_l = self.accumulator; + }, + 0x0F => { // TLA : Transfer L > Acc + self.accumulator = self.reg_l; + }, + 0x10 => { // PHA : Push Acc + push_stack(self, self.accumulator); + }, + 0x11 => { // PLA : Pull Acc + match self.microcode_state { + 0 => { + req_pull_stack(self); + self.executing = true; + }, + 1 => { + self.accumulator = self.get_data(); + } + _ => {} + } + }, + 0x12 => { // PHL : Push HL + match self.microcode_state { + 0 => { + push_stack(self, self.reg_l); + self.executing = true; + }, + 1 => { + push_stack(self, self.reg_h) + } + _ => {} + } + }, + 0x13 => { // PLL : Pull HL + match self.microcode_state { + 0 => { + req_pull_stack(self); + self.executing = true; + }, + 1 => { + self.reg_h = self.get_data(); + req_pull_stack(self); + self.executing = true; + }, + 2 => { + self.reg_l = self.get_data(); + }, + _ => {} + } + }, + 0x14 => { // CPB : Compare B with Acc + check_zero = false; + let compare = self.accumulator as i16 - self.reg_b as i16; + self.flag_zero = compare == 0; + self.flag_neg = compare < 0; + }, + 0x15 => { // CPC : Compare C with Acc + check_zero = false; + let compare = self.accumulator as i16 - self.reg_c as i16; + self.flag_zero = compare == 0; + self.flag_neg = compare < 0; + }, + 0x16 => { // SUB : Substract B to Acc + let sub = self.accumulator as i16 - self.reg_b as i16; + self.flag_neg = sub < 0; + self.accumulator -= self.reg_b; + }, + 0x17 => { // SUC : Substract C with Acc + let sub = self.accumulator as i16 - self.reg_c as i16; + self.flag_neg = sub < 0; + self.accumulator -= self.reg_c; + }, + 0x18 => { // SAL : Shift Acc left + self.accumulator <<= 1; + }, + 0x19 => { // SAR : Shift Acc Right + self.accumulator >>= 1; + } + 0x1A => { // INB : Increment B + self.reg_b = self.reg_b.wrapping_add(1); + if self.reg_b == 0 { + self.flag_overflow = true; + self.flag_neg = false; + } + } + 0x1B => { // DEB : Decrement B + self.reg_b = self.reg_b.wrapping_sub(1); + if self.reg_b == 0xFF { + self.flag_overflow = false; + self.flag_neg = true; + } + }, + 0x1C => { // INC : Increment C + self.reg_c = self.reg_c.wrapping_add(1); + if self.reg_c == 0 { + self.flag_overflow = true; + self.flag_neg = false; + } + } + 0x1D => { // DEC : Decrement C + self.reg_c = self.reg_c.wrapping_sub(1); + if self.reg_c == 0xFF { + self.flag_overflow = false; + self.flag_neg = true; + } + }, + 0x20 => { // JML : Jump to HL + self.program_counter = ((self.reg_h as u16) << 8) + self.reg_l as u16; + }, + 0x21 => { // JSL : Jump Subroutine to HL + match self.microcode_state { + 0 => { + let addr_l = (self.program_counter & 0xFF) as u8; + push_stack(self, addr_l); + self.executing = true; + }, + 1 => { + let addr_h = (self.program_counter >> 8) as u8; + push_stack(self, addr_h); + self.program_counter = ((self.reg_h as u16) << 8) + self.reg_l as u16; + }, + _ => {} + } + }, + 0x22 => { // RTN : Return Subroutine + match self.microcode_state { + 0 => { + req_pull_stack(self); + self.executing = true; + }, + 1 => { + self.program_counter = 0; + self.program_counter += (self.get_data() as u16) << 8; + self.program_counter &= 0xFFF; + req_pull_stack(self); + self.executing = true; + }, + 2 => { + self.program_counter += self.get_data() as u16; + } + _ => {} + } + }, + 0x48 => { // STA [HL]: Store Acc in addr [HL] + let hl = ((self.reg_h as u16) << 8) + self.reg_l as u16; + self.set_address(hl); + self.set_data(self.accumulator); + }, + 0x49 => { // STB [HL]: Store B in addr [HL] + let hl = ((self.reg_h as u16) << 8) + self.reg_l as u16; + self.set_address(hl); + self.set_data(self.reg_b); + }, + 0x4A => { // STC [HL]: Store C in addr [HL] + let hl = ((self.reg_h as u16) << 8) + self.reg_l as u16; + self.set_address(hl); + self.set_data(self.reg_c); + }, + 0x4B => { // LDB [HL]: Load value of addr [HL] in B + match self.microcode_state { + 0 => { + let hl = ((self.reg_h as u16) << 8) + self.reg_l as u16; + self.set_address(hl); + self.executing = true; + }, + 1 => { + self.reg_b = self.get_data(); + }, + _ => {} + } + }, + 0x4C => { // LDC [HL]: Load value of addr [HL] in C + match self.microcode_state { + 0 => { + let hl = ((self.reg_h as u16) << 8) + self.reg_l as u16; + self.set_address(hl); + self.executing = true; + }, + 1 => { + self.reg_c = self.get_data(); + }, + _ => {} + } + }, + 0x4D => { // LDA [HL]: Load value of addr [HL] in Acc + match self.microcode_state { + 0 => { + let hl = ((self.reg_h as u16) << 8) + self.reg_l as u16; + self.set_address(hl); + self.executing = true; + }, + 1 => { + self.accumulator = self.get_data(); + }, + _ => {} + } + }, + 0x4E => { // LDA [HL+B]: Load value of addr [HL+B] in Acc + match self.microcode_state { + 0 => { + let hl = ((self.reg_h as u16) << 8) + self.reg_l as u16; + self.set_address(hl + self.reg_b as u16); + self.executing = true; + }, + 1 => { + self.accumulator = self.get_data(); + }, + _ => {} + } + }, + 0x4F => { // LDA [HL+C]: Load value of addr [HL+C] in Acc + match self.microcode_state { + 0 => { + let hl = ((self.reg_h as u16) << 8) + self.reg_l as u16; + self.set_address(hl + self.reg_c as u16); + self.executing = true; + }, + 1 => { + self.accumulator = self.get_data(); + }, + _ => {} + } + }, + 0x50 => { // LDA $1 : load $1 in Acc + self.accumulator = self.param_first; + }, + 0x51 => { // LDA [$1] : Zero page load to Acc + match self.microcode_state { + 0 => { + self.set_address(self.param_first as u16); + self.executing = true; + }, + 1 => { + self.accumulator = self.get_data(); + }, + _ => {} + } + }, + 0x52 => { // LDA [H$1] : Load [H$1] into Acc + match self.microcode_state { + 0 => { + let h0 = (self.reg_h as u16) << 8; + self.set_address(h0 + self.param_first as u16); + self.executing = true; + }, + 1 => { + self.accumulator = self.get_data(); + }, + _ => {} + } + }, + 0x53 => { // LDA [HL]+$1 : Load [HL]+$1 into Acc + match self.microcode_state { + 0 => { + let hl = ((self.reg_h as u16) << 8) + self.reg_l as u16; + self.set_address(hl + self.param_first as u16); + self.executing = true; + }, + 1 => { + self.accumulator = self.get_data(); + }, + _ => {} + } + }, + 0x54 => { // LDB $1 : Load $1 into B + self.reg_b = self.param_first + } + 0x55 => { // LDB [$1] : Zero page load to B + match self.microcode_state { + 0 => { + self.set_address(self.param_first as u16); + self.executing = true; + }, + 1 => { + self.reg_b = self.get_data(); + }, + _ => {} + } + }, + 0x56 => { // LDC $1 : Load $1 into C + self.reg_c = self.param_first + } + 0x57 => { // LDC [$1] : Zero page load to C + match self.microcode_state { + 0 => { + self.set_address(self.param_first as u16); + self.executing = true; + }, + 1 => { + self.reg_c = self.get_data(); + }, + _ => {} + } + }, + 0x58 => { // STA [$1] : Zero page store Acc + self.set_address(self.param_first as u16); + self.set_data(self.accumulator); + }, + 0x59 => { // STA [H0+$1] : Store Acc to [H$1] + let h0 = (self.reg_h as u16) << 8; + self.set_address(h0 + self.param_first as u16); + self.set_data(self.accumulator); + }, + 0x5A => { // STA [HL+$1] : Store Acc to [HL+$1] + let hl = ((self.reg_h as u16) << 8) + self.reg_l as u16; + self.set_address(hl + self.param_first as u16); + self.set_data(self.accumulator); + }, + 0x5B => { // STB [$1] : Zero page store B + self.set_address(self.param_first as u16); + self.set_data(self.reg_b); + }, + 0x5C => { // STC [$1] : Zero page store C + self.set_address(self.param_first as u16); + self.set_data(self.reg_c); + }, + 0x60 => { // CMP : compare Acc with $1 + check_zero = false; + let compare = self.accumulator as i16 - self.param_first as i16; + self.flag_zero = compare == 0; + self.flag_neg = compare < 0; + } + 0x61 => { // CPB : compare B with $1 + check_zero = false; + let compare = self.reg_b as i16 - self.param_first as i16; + self.flag_zero = compare == 0; + self.flag_neg = compare < 0; + } + 0x62 => { // CPC : compare C with $1 + check_zero = false; + let compare = self.reg_c as i16 - self.param_first as i16; + self.flag_zero = compare == 0; + self.flag_neg = compare < 0; + } + 0xB0 => { // JMP : Jump to $1$2 + self.program_counter = ((self.param_first as u16) << 8) + self.param_second as u16; + }, + 0xB1 => { // JSR : Jump Subroutine to $1$2 + match self.microcode_state { + 0 => { + let addr_l = (self.program_counter & 0xFF) as u8; + push_stack(self, addr_l); + self.executing = true; + }, + 1 => { + let addr_h = (self.program_counter >> 8) as u8; + push_stack(self, addr_h); + self.program_counter = ((self.param_first as u16) << 8) + self.param_second as u16; + }, + _ => {} + } + }, + 0xB2 => { // BCF : Branch on Carry flag to $1$2 + if self.flag_carry { + self.program_counter = ((self.param_first as u16) << 8) + self.param_second as u16; + } + }, + 0xB3 => { // BNF : Branch on Negative flag to $1$2 + if self.flag_neg { + self.program_counter = ((self.param_first as u16) << 8) + self.param_second as u16; + } + }, + 0xB4 => { // BZF : Branch on Zero flag to $1$2 + if self.flag_zero { + self.program_counter = ((self.param_first as u16) << 8) + self.param_second as u16; + } + }, + 0xC0 => { // LDA [$1$2] : load [$1$2] into Acc + match self.microcode_state { + 0 => { + self.set_address(((self.param_first as u16) << 8) + self.param_second as u16); + self.executing = true; + }, + 1 => { + self.accumulator = self.get_data(); + }, + _ => {} + } + }, + 0xC1 => { // STA [$1$2] : store Acc into [$1$2] + self.set_address(self.param_first as u16); + self.set_data(self.accumulator); + } + _ => {} // do nothing, NOP + } + if check_zero { + self.flag_zero = self.accumulator == 0; + } + if !self.executing { + self.microcode_state = 0xFF; + } + } +} +impl Chip for SimpleCPU { + fn get_pin_qty(&self) -> u8 { + 26 + } + + fn get_pin(&mut self, pin: u8) -> Result>, &str> { + if pin > 0 && pin <= 26 { + Ok(self.pin[pin as usize-1].clone()) + } else { + Err("Pin out of bounds") + } + } + fn run(&mut self, _: std::time::Duration) { + if self.pin[14].borrow().state == State::Low { + self.initializing = true; + self.microcode_state = 0; + self.halted = false; + } + // check alimented + if self.pin[12].borrow().state == State::Low && self.pin[25].borrow().state == State::High { + if self.pin[13].borrow().state == State::High && !self.halted { + self.set_iopin_type(PinType::Input); + if self.initializing { + // executes boot sequence + self.boot(); + } else if self.executing { + // run program + self.execute(); + } else { + let launch_execution = |myself: &mut SimpleCPU| { + // execute opcode with its params + myself.executing = true; + myself.microcode_state = 0xFF; + }; + // fetch program + match self.microcode_state { + 0 => { + // set the program counter as address + self.set_address(self.program_counter); + self.current_opcode = 0; + self.param_first = 0; + self.param_second = 0; + }, + 1 => { + self.current_opcode = self.get_data(); + self.program_counter += 1; + if self.current_opcode >= 0x50 { + // opcode need at least 1 parameter + self.set_address(self.program_counter); + } else { + launch_execution(self); + } + }, + 2 => { + if self.current_opcode >= 0x50 { + self.param_first = self.get_data(); + self.program_counter += 1; + if self.current_opcode >= 0xB0 { + // opcode need at least 2 parameters + self.set_address(self.program_counter); + } else { + launch_execution(self); + } + } + }, + 3 => { + if self.current_opcode >= 0xB0 { + self.param_second = self.get_data(); + self.program_counter += 1; + } + launch_execution(self); + }, + _ => {} + } + + } + //println!("PC: {:03X}\tADR: {:03X}\tIO: {:02X}\tOp: {:02X}\t$1: {:02X}\t$2: {:02X}\tA: {:02X}\tB: {:02X}\tC: {:02X}\tH: {:02X}\tL: {:02X}\tSP: {:02X}\tmc: {}\texec: {}", self.program_counter, self.get_address(), self.get_data(), self.current_opcode, self.param_first, self.param_second, self.accumulator, self.reg_b, self.reg_c, self.reg_h, self.reg_l, self.stack_pointer, self.microcode_state, self.executing); + self.microcode_state = self.microcode_state.wrapping_add(1); + if self.program_counter > 0xFFF { + self.program_counter = 0; + } + } + } else { + // turn off every pin + for i in 0..22 { + self.pin[i].borrow_mut().state = State::Undefined + } + self.initializing = true; + } + } +} \ No newline at end of file diff --git a/src/chip/gates.rs b/src/chip/gates.rs new file mode 100644 index 0000000..ba904a7 --- /dev/null +++ b/src/chip/gates.rs @@ -0,0 +1,288 @@ +use super::super::State; +use super::{Pin, PinType, Chip}; +use std::cell::RefCell; +use std::rc::Rc; + +/// # A chip with 4 bundled "OR" gates +/// +/// # Diagram +/// ``` +/// ---__--- +/// A --|1 14|-- VCC +/// B --|2 13|-- E +/// A|B --|3 12|-- F +/// C --|4 11|-- E|F +/// D --|5 10|-- G +/// C|D --|6 9|-- H +/// GND --|7 8|-- G|H +/// -------- +/// ``` +#[derive(Debug)] +pub struct GateOr { + pin: [Rc>; 14], +} +impl Default for GateOr { + fn default() -> Self { + Self::new() + } +} + +impl GateOr { + pub const A: u8 = 1; + pub const B: u8 = 2; + pub const A_OR_B: u8 = 3; + pub const C: u8 = 4; + pub const D: u8 = 5; + pub const C_OR_D: u8 = 6; + pub const E: u8 = 13; + pub const F: u8 = 12; + pub const E_OR_F: u8 = 11; + pub const G: u8 = 10; + pub const H: u8 = 9; + pub const G_OR_H: u8 = 8; + pub const VCC: u8 = 14; + pub const GND: u8 = 7; + + pub fn new() -> Self { + GateOr { + pin: [ + Rc::new(RefCell::new(Pin::new(1, PinType::Input))), + Rc::new(RefCell::new(Pin::new(2, PinType::Input))), + Rc::new(RefCell::new(Pin::new(3, PinType::Output))), + Rc::new(RefCell::new(Pin::new(4, PinType::Input))), + Rc::new(RefCell::new(Pin::new(5, PinType::Input))), + Rc::new(RefCell::new(Pin::new(6, PinType::Output))), + Rc::new(RefCell::new(Pin::new(7, PinType::Input))), + Rc::new(RefCell::new(Pin::new(8, PinType::Output))), + Rc::new(RefCell::new(Pin::new(9, PinType::Input))), + Rc::new(RefCell::new(Pin::new(10, PinType::Input))), + Rc::new(RefCell::new(Pin::new(11, PinType::Output))), + Rc::new(RefCell::new(Pin::new(12, PinType::Input))), + Rc::new(RefCell::new(Pin::new(13, PinType::Input))), + Rc::new(RefCell::new(Pin::new(14, PinType::Input))) + ] + } + } +} +impl Chip for GateOr { + fn get_pin_qty(&self) -> u8 { + 14 + } + + fn get_pin(&mut self, pin: u8) -> Result>, &str> { + if pin > 0 && pin <= 14 { + Ok(self.pin[pin as usize-1].clone()) + } else { + Err("Pin out of bounds") + } + } + fn run(&mut self, _: std::time::Duration) { + // check alimented + if self.pin[6].borrow().state == State::Low && self.pin[13].borrow().state == State::High { + // A && B + self.pin[2].borrow_mut().state = if self.pin[0].borrow().state == State::High || self.pin[1].borrow().state == State::High {State::High} else {State::Low}; + // C && D + self.pin[5].borrow_mut().state = if self.pin[3].borrow().state == State::High || self.pin[4].borrow().state == State::High {State::High} else {State::Low}; + // E && F + self.pin[10].borrow_mut().state = if self.pin[11].borrow().state == State::High || self.pin[12].borrow().state == State::High {State::High} else {State::Low}; + // G && H + self.pin[7].borrow_mut().state = if self.pin[8].borrow().state == State::High || self.pin[9].borrow().state == State::High {State::High} else {State::Low}; + } else { + // turn off every pin + for i in 0..14 { + self.pin[i].borrow_mut().state = State::Low + } + } + } +} + + +/// # A chip with 4 bundled "AND" gates +/// +/// # Diagram +/// ``` +/// ---__--- +/// A --|1 14|-- VCC +/// B --|2 13|-- E +/// A&B --|3 12|-- F +/// C --|4 11|-- E&F +/// D --|5 10|-- G +/// C&D --|6 9|-- H +/// GND --|7 8|-- G&H +/// -------- +/// ``` +#[derive(Debug)] +pub struct GateAnd { + pin: [Rc>; 14], +} +impl Default for GateAnd { + fn default() -> Self { + Self::new() + } +} + +impl GateAnd { + pub const A: u8 = 1; + pub const B: u8 = 2; + pub const A_AND_B: u8 = 3; + pub const C: u8 = 4; + pub const D: u8 = 5; + pub const C_AND_D: u8 = 6; + pub const E: u8 = 13; + pub const F: u8 = 12; + pub const E_AND_F: u8 = 11; + pub const G: u8 = 10; + pub const H: u8 = 9; + pub const G_AND_H: u8 = 8; + pub const VCC: u8 = 14; + pub const GND: u8 = 7; + + pub fn new() -> Self { + GateAnd { + pin: [ + Rc::new(RefCell::new(Pin::new(1, PinType::Input))), + Rc::new(RefCell::new(Pin::new(2, PinType::Input))), + Rc::new(RefCell::new(Pin::new(3, PinType::Output))), + Rc::new(RefCell::new(Pin::new(4, PinType::Input))), + Rc::new(RefCell::new(Pin::new(5, PinType::Input))), + Rc::new(RefCell::new(Pin::new(6, PinType::Output))), + Rc::new(RefCell::new(Pin::new(7, PinType::Input))), + Rc::new(RefCell::new(Pin::new(8, PinType::Output))), + Rc::new(RefCell::new(Pin::new(9, PinType::Input))), + Rc::new(RefCell::new(Pin::new(10, PinType::Input))), + Rc::new(RefCell::new(Pin::new(11, PinType::Output))), + Rc::new(RefCell::new(Pin::new(12, PinType::Input))), + Rc::new(RefCell::new(Pin::new(13, PinType::Input))), + Rc::new(RefCell::new(Pin::new(14, PinType::Input))) + ] + } + } +} +impl Chip for GateAnd { + fn get_pin_qty(&self) -> u8 { + 14 + } + + fn get_pin(&mut self, pin: u8) -> Result>, &str> { + if pin > 0 && pin <= 14 { + Ok(self.pin[pin as usize-1].clone()) + } else { + Err("Pin out of bounds") + } + } + fn run(&mut self, _: std::time::Duration) { + // check alimented + if self.pin[6].borrow().state == State::Low && self.pin[13].borrow().state == State::High { + // A && B + self.pin[2].borrow_mut().state = if self.pin[0].borrow().state == State::High && self.pin[1].borrow().state == State::High {State::High} else {State::Low}; + // C && D + self.pin[5].borrow_mut().state = if self.pin[3].borrow().state == State::High && self.pin[4].borrow().state == State::High {State::High} else {State::Low}; + // E && F + self.pin[10].borrow_mut().state = if self.pin[11].borrow().state == State::High && self.pin[12].borrow().state == State::High {State::High} else {State::Low}; + // G && H + self.pin[7].borrow_mut().state = if self.pin[8].borrow().state == State::High && self.pin[9].borrow().state == State::High {State::High} else {State::Low}; + } else { + // turn off every pin + for i in 0..14 { + self.pin[i].borrow_mut().state = State::Undefined + } + } + } +} + +/// # A chip with 6 bundled "NOT" gates +/// +/// # Diagram +/// ``` +/// ---__--- +/// A --|1 14|-- VCC +/// !A --|2 13|-- D +/// B --|3 12|-- !D +/// !B --|4 11|-- E +/// C --|5 10|-- !E +/// !C --|6 9|-- F +/// GND --|7 8|-- !F +/// -------- +/// ``` +#[derive(Debug)] +pub struct GateNot { + pin: [Rc>; 14], +} +impl Default for GateNot { + fn default() -> Self { + Self::new() + } +} + +impl GateNot { + pub const A: u8 = 1; + pub const NOT_A: u8 = 2; + pub const B: u8 = 3; + pub const NOT_B: u8 = 4; + pub const C: u8 = 5; + pub const NOT_C: u8 = 6; + pub const D: u8 = 13; + pub const NOT_D: u8 = 12; + pub const E: u8 = 11; + pub const NOT_E: u8 = 10; + pub const F: u8 = 9; + pub const NOT_F: u8 = 8; + pub const VCC: u8 = 14; + pub const GND: u8 = 7; + + pub fn new() -> Self { + GateNot { + pin: [ + Rc::new(RefCell::new(Pin::new(1, PinType::Input))), + Rc::new(RefCell::new(Pin::new(2, PinType::Output))), + Rc::new(RefCell::new(Pin::new(3, PinType::Input))), + Rc::new(RefCell::new(Pin::new(4, PinType::Output))), + Rc::new(RefCell::new(Pin::new(5, PinType::Input))), + Rc::new(RefCell::new(Pin::new(6, PinType::Output))), + Rc::new(RefCell::new(Pin::new(7, PinType::Input))), + Rc::new(RefCell::new(Pin::new(8, PinType::Output))), + Rc::new(RefCell::new(Pin::new(9, PinType::Input))), + Rc::new(RefCell::new(Pin::new(10, PinType::Output))), + Rc::new(RefCell::new(Pin::new(11, PinType::Input))), + Rc::new(RefCell::new(Pin::new(12, PinType::Output))), + Rc::new(RefCell::new(Pin::new(13, PinType::Input))), + Rc::new(RefCell::new(Pin::new(14, PinType::Input))) + ] + } + } +} +impl Chip for GateNot { + fn get_pin_qty(&self) -> u8 { + 14 + } + + fn get_pin(&mut self, pin: u8) -> Result>, &str> { + if pin > 0 && pin <= 14 { + Ok(self.pin[pin as usize-1].clone()) + } else { + Err("Pin out of bounds") + } + } + fn run(&mut self, _: std::time::Duration) { + // check alimented + if self.pin[6].borrow().state == State::Low && self.pin[13].borrow().state == State::High { + // !A + self.pin[1].borrow_mut().state = if self.pin[0].borrow().state == State::High {State::Low} else {State::High}; + // !B + self.pin[3].borrow_mut().state = if self.pin[2].borrow().state == State::High {State::Low} else {State::High}; + // !C + self.pin[5].borrow_mut().state = if self.pin[4].borrow().state == State::High {State::Low} else {State::High}; + // !D + self.pin[11].borrow_mut().state = if self.pin[12].borrow().state == State::High {State::Low} else {State::High}; + // !E + self.pin[9].borrow_mut().state = if self.pin[10].borrow().state == State::High {State::Low} else {State::High}; + // !F + self.pin[7].borrow_mut().state = if self.pin[8].borrow().state == State::High {State::Low} else {State::High}; + } else { + // turn off every pin + for i in 0..14 { + self.pin[i].borrow_mut().state = State::Undefined + } + } + } +} \ No newline at end of file diff --git a/src/chip/generators.rs b/src/chip/generators.rs new file mode 100644 index 0000000..5311e3f --- /dev/null +++ b/src/chip/generators.rs @@ -0,0 +1,56 @@ +use super::super::State; +use super::{Pin, PinType, Chip}; +use std::cell::RefCell; +use std::rc::Rc; + +/// # A simple generator providing VCC and GND +/// +/// # Diagram +/// ``` +/// -------- +/// VCC --|1 2|-- GND +/// -------- +/// ``` +#[derive(Debug)] +pub struct Generator { + pin: [Rc>; 2], +} +impl Default for Generator { + fn default() -> Self { + Self::new() + } +} + +impl Generator { + pub const VCC: u8 = 1; + pub const GND: u8 = 2; + + pub fn new() -> Self { + let gen = Generator { + pin: [ + Rc::new(RefCell::new(Pin::new(1, PinType::Output))), + Rc::new(RefCell::new(Pin::new(2, PinType::Output))), + ] + }; + gen.pin[0].borrow_mut().state = State::High; + gen.pin[1].borrow_mut().state = State::Low; + gen + } +} +impl Chip for Generator { + fn get_pin_qty(&self) -> u8 { + 2 + } + + fn get_pin(&mut self, pin: u8) -> Result>, &str> { + if pin > 0 && pin <= 2 { + Ok(self.pin[pin as usize-1].clone()) + } else { + Err("Pin out of bounds") + } + } + fn run(&mut self, _: std::time::Duration) { + self.pin[0].borrow_mut().state = State::High; + self.pin[1].borrow_mut().state = State::Low; + } +} \ No newline at end of file diff --git a/src/chip/memory.rs b/src/chip/memory.rs new file mode 100644 index 0000000..e440ff1 --- /dev/null +++ b/src/chip/memory.rs @@ -0,0 +1,383 @@ +use super::super::State; +use super::{Pin, PinType, Chip}; +use std::cell::RefCell; +use std::rc::Rc; +use rand::random; + +/// # A 256-bytes RAM chip +/// +/// # Diagram +/// CS: Chip Select +/// WE: Write Enable +/// OE: Output Enable +/// A0-7: Addresses +/// IO0-7: Input/Output +/// ``` +/// ---__--- +/// !CS --|1 22|-- VCC +/// !WE --|2 21|-- UNUSED +/// !OE --|3 20|-- IO7 +/// A0 --|4 19|-- IO6 +/// A1 --|5 18|-- IO5 +/// A2 --|6 17|-- IO4 +/// A3 --|7 16|-- IO3 +/// A4 --|8 15|-- IO2 +/// A5 --|9 14|-- IO1 +/// A6 --|10 13|-- IO0 +/// GND --|11 12|-- A7 +/// -------- +/// ``` +pub struct Ram256B { + pin: [Rc>; 22], + ram: [u8; 256], + powered: bool +} +impl Default for Ram256B { + fn default() -> Self { + Self::new() + } +} +impl std::fmt::Debug for Ram256B { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + fmt.write_str("----------\nRam256B\n")?; + fmt.write_str(self.to_string().as_str())?; + fmt.write_str(format!("\naddress: {:02X}", self.get_address()).as_str())?; + fmt.write_str(format!("\ndata: {:02X}", self.ram[self.get_address() as usize]).as_str())?; + fmt.write_str(format!("\nCS: {}\tWE: {}\tOE: {}", !self.pin[0].borrow().state.as_bool(), !self.pin[1].borrow().state.as_bool(), !self.pin[2].borrow().state.as_bool()).as_str())?; + fmt.write_str("\n----------")?; + + Ok(()) + } +} +impl ToString for Ram256B { + fn to_string(&self) -> std::string::String { + let mut string = String::new(); + for byte in self.ram.iter() { + string.push_str(format!("{:02X}", byte).as_str()); + } + string + } +} + +impl Ram256B { + pub const CS: u8 = 1; + pub const WE: u8 = 2; + pub const OE: u8 = 3; + pub const A0: u8 = 4; + pub const A1: u8 = 5; + pub const A2: u8 = 6; + pub const A3: u8 = 7; + pub const A4: u8 = 8; + pub const A5: u8 = 9; + pub const A6: u8 = 10; + pub const A7: u8 = 12; + pub const IO0: u8 = 13; + pub const IO1: u8 = 14; + pub const IO2: u8 = 15; + pub const IO3: u8 = 16; + pub const IO4: u8 = 17; + pub const IO5: u8 = 18; + pub const IO6: u8 = 19; + pub const IO7: u8 = 20; + pub const VCC: u8 = 22; + pub const GND: u8 = 11; + + pub fn new() -> Self { + Ram256B { + pin: [ + Rc::new(RefCell::new(Pin::new(1, PinType::Input))), + Rc::new(RefCell::new(Pin::new(2, PinType::Input))), + Rc::new(RefCell::new(Pin::new(3, PinType::Input))), + Rc::new(RefCell::new(Pin::new(4, PinType::Input))), + Rc::new(RefCell::new(Pin::new(5, PinType::Input))), + Rc::new(RefCell::new(Pin::new(6, PinType::Input))), + Rc::new(RefCell::new(Pin::new(7, PinType::Input))), + Rc::new(RefCell::new(Pin::new(8, PinType::Input))), + Rc::new(RefCell::new(Pin::new(9, PinType::Input))), + Rc::new(RefCell::new(Pin::new(10, PinType::Input))), + Rc::new(RefCell::new(Pin::new(11, PinType::Input))), + Rc::new(RefCell::new(Pin::new(12, PinType::Input))), + Rc::new(RefCell::new(Pin::new(13, PinType::Output))), + Rc::new(RefCell::new(Pin::new(14, PinType::Output))), + Rc::new(RefCell::new(Pin::new(15, PinType::Output))), + Rc::new(RefCell::new(Pin::new(16, PinType::Output))), + Rc::new(RefCell::new(Pin::new(17, PinType::Output))), + Rc::new(RefCell::new(Pin::new(18, PinType::Output))), + Rc::new(RefCell::new(Pin::new(19, PinType::Output))), + Rc::new(RefCell::new(Pin::new(20, PinType::Output))), + Rc::new(RefCell::new(Pin::new(21, PinType::Input))), + Rc::new(RefCell::new(Pin::new(22, PinType::Input))) + ], + ram: [0; 256], + powered: false + } + } + + fn get_address(&self) -> u8 { + let mut addr: u8 = 0; + for i in 3..10 { + let bit = if self.pin[i].borrow().state == State::High {1} else {0}; + addr += bit << (i-3); + } + let bit = if self.pin[11].borrow().state == State::High {1} else {0}; + addr += bit << 7; + addr + } + + fn get_data(&self) -> u8 { + let mut addr: u8 = 0; + for i in 12..20 { + let bit = if self.pin[i].borrow().state == State::High {1} else {0}; + addr += bit << (i-12); + } + addr + } +} +impl Chip for Ram256B { + fn get_pin_qty(&self) -> u8 { + 22 + } + + fn get_pin(&mut self, pin: u8) -> Result>, &str> { + if pin > 0 && pin <= 22 { + Ok(self.pin[pin as usize-1].clone()) + } else { + Err("Pin out of bounds") + } + } + fn run(&mut self, _: std::time::Duration) { + // check alimented + if self.pin[10].borrow().state == State::Low && self.pin[21].borrow().state == State::High { + if !self.powered { + for i in 0..256 { + self.ram[i] = random::(); + } + self.powered = true; + } + // check Chip Select (active low) + if self.pin[0].borrow().state == State::Low { + //print!("RAM: selected\t"); + // check Write Enable (active low) + if self.pin[1].borrow().state == State::Low { + // IO = Input + for i in 12..20 { + self.pin[i].borrow_mut().pin_type = PinType::Input; + } + // read data on IO pins + let addr = self.get_address() as usize; + //print!("RAM: write [{:02X}]: {:02X} \t", addr, self.get_data()); + self.ram[addr] = self.get_data(); + } + + // check Output Enable (active low) + if self.pin[2].borrow().state == State::Low { + // IO = Output + for i in 12..21 { + self.pin[i].borrow_mut().pin_type = PinType::Output; + } + // display data on IO pins + let addr = self.get_address() as usize; + //print!("RAM: read [{:02X}]: {:02X} \t", addr, self.ram[addr]); + self.pin[12].borrow_mut().state = State::from_u8(self.ram[addr], 0); + self.pin[13].borrow_mut().state = State::from_u8(self.ram[addr], 1); + self.pin[14].borrow_mut().state = State::from_u8(self.ram[addr], 2); + self.pin[15].borrow_mut().state = State::from_u8(self.ram[addr], 3); + self.pin[16].borrow_mut().state = State::from_u8(self.ram[addr], 4); + self.pin[17].borrow_mut().state = State::from_u8(self.ram[addr], 5); + self.pin[18].borrow_mut().state = State::from_u8(self.ram[addr], 6); + self.pin[19].borrow_mut().state = State::from_u8(self.ram[addr], 7); + } + //println!(); + } else { + // IO : undefined + for i in 12..20 { + self.pin[i].borrow_mut().pin_type = PinType::Undefined; + } + } + } else if self.powered { + // turn off every pin + for i in 0..22 { + self.pin[i].borrow_mut().state = State::Undefined + } + self.powered = false; + } + } +} + +/// # A 256-bytes ROM chip +/// +/// # Diagram +/// CS: Chip Select +/// WE: Write Enable +/// OE: Output Enable +/// A0-7: Addresses +/// IO0-7: Input/Output +/// ``` +/// ---__--- +/// !CS --|1 22|-- VCC +/// UNUSED--|2 21|-- UNUSED +/// !OE --|3 20|-- IO7 +/// A0 --|4 19|-- IO6 +/// A1 --|5 18|-- IO5 +/// A2 --|6 17|-- IO4 +/// A3 --|7 16|-- IO3 +/// A4 --|8 15|-- IO2 +/// A5 --|9 14|-- IO1 +/// A6 --|10 13|-- IO0 +/// GND --|11 12|-- A7 +/// -------- +/// ``` +pub struct Rom256B { + pin: [Rc>; 22], + rom: [u8; 256], +} +impl Default for Rom256B { + fn default() -> Self { + Self::new() + } +} +impl std::fmt::Debug for Rom256B { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + fmt.write_str("----------\nRom256B\n")?; + fmt.write_str(self.to_string().as_str())?; + fmt.write_str(format!("\naddress: {:02X}", self.get_address()).as_str())?; + fmt.write_str(format!("\ndata: {:02X}", self.rom[self.get_address() as usize]).as_str())?; + fmt.write_str(format!("\nCS: {}\tOE: {}", !self.pin[0].borrow().state.as_bool(), !self.pin[2].borrow().state.as_bool()).as_str())?; + fmt.write_str("\n----------")?; + Ok(()) + } +} +impl ToString for Rom256B { + fn to_string(&self) -> std::string::String { + let mut string = String::new(); + for byte in self.rom.iter() { + string.push_str(format!("{:02X}", byte).as_str()); + } + string + } +} + +impl Rom256B { + pub const CS: u8 = 1; + pub const OE: u8 = 3; + pub const A0: u8 = 4; + pub const A1: u8 = 5; + pub const A2: u8 = 6; + pub const A3: u8 = 7; + pub const A4: u8 = 8; + pub const A5: u8 = 9; + pub const A6: u8 = 10; + pub const A7: u8 = 12; + pub const IO0: u8 = 13; + pub const IO1: u8 = 14; + pub const IO2: u8 = 15; + pub const IO3: u8 = 16; + pub const IO4: u8 = 17; + pub const IO5: u8 = 18; + pub const IO6: u8 = 19; + pub const IO7: u8 = 20; + pub const VCC: u8 = 22; + pub const GND: u8 = 11; + + pub fn new() -> Self { + Rom256B { + pin: [ + Rc::new(RefCell::new(Pin::new(1, PinType::Input))), + Rc::new(RefCell::new(Pin::new(2, PinType::Input))), + Rc::new(RefCell::new(Pin::new(3, PinType::Input))), + Rc::new(RefCell::new(Pin::new(4, PinType::Input))), + Rc::new(RefCell::new(Pin::new(5, PinType::Input))), + Rc::new(RefCell::new(Pin::new(6, PinType::Input))), + Rc::new(RefCell::new(Pin::new(7, PinType::Input))), + Rc::new(RefCell::new(Pin::new(8, PinType::Input))), + Rc::new(RefCell::new(Pin::new(9, PinType::Input))), + Rc::new(RefCell::new(Pin::new(10, PinType::Input))), + Rc::new(RefCell::new(Pin::new(11, PinType::Input))), + Rc::new(RefCell::new(Pin::new(12, PinType::Input))), + Rc::new(RefCell::new(Pin::new(13, PinType::Output))), + Rc::new(RefCell::new(Pin::new(14, PinType::Output))), + Rc::new(RefCell::new(Pin::new(15, PinType::Output))), + Rc::new(RefCell::new(Pin::new(16, PinType::Output))), + Rc::new(RefCell::new(Pin::new(17, PinType::Output))), + Rc::new(RefCell::new(Pin::new(18, PinType::Output))), + Rc::new(RefCell::new(Pin::new(19, PinType::Output))), + Rc::new(RefCell::new(Pin::new(20, PinType::Output))), + Rc::new(RefCell::new(Pin::new(21, PinType::Input))), + Rc::new(RefCell::new(Pin::new(22, PinType::Input))) + ], + rom: [0; 256], + } + } + + pub fn from_data(data: [u8;256]) -> Rom256B { + let mut rom = Rom256B::new(); + rom.load_data(data); + rom + } + + pub fn load_data(&mut self, data: [u8;256]) { + self.rom.clone_from_slice(&data); + } + + fn get_address(&self) -> u8 { + let mut addr: u8 = 0; + for i in 3..10 { + let bit = if self.pin[i].borrow().state == State::High {1} else {0}; + addr += bit << (i-3); + } + let bit = if self.pin[11].borrow().state == State::High {1} else {0}; + addr += bit << 7; + addr + } +} +impl Chip for Rom256B { + fn get_pin_qty(&self) -> u8 { + 22 + } + + fn get_pin(&mut self, pin: u8) -> Result>, &str> { + if pin > 0 && pin <= 22 { + Ok(self.pin[pin as usize-1].clone()) + } else { + Err("Pin out of bounds") + } + } + fn run(&mut self, _: std::time::Duration) { + // check alimented + if self.pin[10].borrow().state == State::Low && self.pin[21].borrow().state == State::High { + // check Chip Select (active low) + if self.pin[0].borrow().state == State::Low { + //print!("ROM: selected\t"); + // check Output Enable (active low) + if self.pin[2].borrow().state == State::Low { + // IO = Output + for i in 12..21 { + self.pin[i].borrow_mut().pin_type = PinType::Output; + } + // display data on IO pins + let addr = self.get_address() as usize; + //print!("ROM: read [{:02X}]: {:02X} \t", addr, self.rom[addr]); + self.pin[12].borrow_mut().state = State::from_u8(self.rom[addr], 0); + self.pin[13].borrow_mut().state = State::from_u8(self.rom[addr], 1); + self.pin[14].borrow_mut().state = State::from_u8(self.rom[addr], 2); + self.pin[15].borrow_mut().state = State::from_u8(self.rom[addr], 3); + self.pin[16].borrow_mut().state = State::from_u8(self.rom[addr], 4); + self.pin[17].borrow_mut().state = State::from_u8(self.rom[addr], 5); + self.pin[18].borrow_mut().state = State::from_u8(self.rom[addr], 6); + self.pin[19].borrow_mut().state = State::from_u8(self.rom[addr], 7); + } + //println!(); + } else { + // IO : undefined + for i in 12..20 { + self.pin[i].borrow_mut().pin_type = PinType::Undefined; + } + } + } else { + // turn off every pin + for i in 0..22 { + self.pin[i].borrow_mut().state = State::Undefined + } + } + } +} \ No newline at end of file diff --git a/src/chip/mod.rs b/src/chip/mod.rs new file mode 100644 index 0000000..81dae52 --- /dev/null +++ b/src/chip/mod.rs @@ -0,0 +1,39 @@ +use super::State; +pub mod gates; +pub mod generators; +pub mod memory; +pub mod cpu; +pub mod clocks; +use std::cell::RefCell; +use std::rc::Rc; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum PinType { + Undefined, + Input, + Output, + // Both can cause issues on Trace::communicate() +} + +#[derive(Debug)] +pub struct Pin { + pub number: u8, + pub pin_type: PinType, + pub state: State, +} +impl Pin { + pub fn new(number: u8, pin_type: PinType) -> Pin { + Pin { + number, + pin_type, + state: State::Undefined + } + } +} + +pub trait Chip: std::fmt::Debug { + fn run(&mut self, elapsed_time: std::time::Duration); + fn get_pin_qty(&self) -> u8; + fn get_pin(&mut self, pin: u8) -> Result>, &str>; + // fn set_pin(&mut self, pin: u8, state: &State); +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..d9edc86 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,58 @@ +pub mod chip; +mod trace; +mod board; +mod socket; +pub use chip::{Chip, Pin, PinType}; +pub use board::Board; +pub use trace::Trace; +pub use socket::Socket; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum State { + Undefined, + High, + Low +} + +impl From for State { + fn from(bit: bool) -> Self { + if bit { + State::High + } else { + State::Low + } + } +} +impl State { + pub fn from_u8(data: u8, position: usize) -> Self { + let bit = (data >> position) & 1; + if bit == 1 { + State::High + } else { + State::Low + } + } + pub fn from_u16(data: u16, position: usize) -> Self { + let bit = (data >> position) & 1; + if bit == 1 { + State::High + } else { + State::Low + } + } + pub fn from_u32(data: u32, position: usize) -> Self { + let bit = (data >> position) & 1; + if bit == 1 { + State::High + } else { + State::Low + } + } + + pub fn as_bool(&self) -> bool { + match self { + State::High => true, + _ => false + } + } +} \ No newline at end of file diff --git a/src/socket.rs b/src/socket.rs new file mode 100644 index 0000000..471c787 --- /dev/null +++ b/src/socket.rs @@ -0,0 +1,86 @@ +use super::{Chip, Pin, PinType, State}; +use std::cell::RefCell; +use std::rc::Rc; + +#[derive(Default, Debug)] +pub struct Socket { + chip: Option>, +} + +impl Socket { + pub fn new() -> Socket { + Socket { + chip: None + } + } + + pub fn with(chip: Box) -> Socket { + Socket { + chip: Some(chip) + } + } + + pub fn plug(&mut self, chip: Box) { + self.chip = Some(chip); + } + + pub fn has_chip(&self) -> bool { + self.chip.is_some() + } + + pub fn get_chip(&mut self) -> &mut Option> { + &mut self.chip + } + + pub fn get_pin_state(&mut self, pin: u8) -> State { + if self.chip.is_some() { + self.chip.as_mut().unwrap().get_pin(pin).unwrap().borrow().state.clone() + } else { + State::Undefined + } + } + + pub fn set_pin_state(&mut self, pin: u8, state: &State) { + if self.chip.is_some() { + self.chip.as_mut().unwrap().get_pin(pin).unwrap().borrow_mut().state = state.clone(); + } + } + + pub fn get_pin_type(&mut self, pin: u8) -> PinType { + if self.chip.is_some() { + self.chip.as_mut().unwrap().get_pin(pin).unwrap().borrow().pin_type.clone() + } else { + PinType::Undefined + } + } + + pub fn set_pin_type(&mut self, pin: u8, pin_type: &PinType) { + if self.chip.is_some() { + self.chip.as_mut().unwrap().get_pin(pin).unwrap().borrow_mut().pin_type = pin_type.clone(); + } + } +} + +impl Chip for Socket { + fn get_pin_qty(&self) -> u8 { + if self.chip.is_some() { + self.chip.as_ref().unwrap().get_pin_qty() + } else { + 0 + } + } + + fn get_pin(&mut self, pin: u8) -> Result>, &str> { + if self.chip.is_some() { + self.chip.as_mut().unwrap().get_pin(pin) + } else { + Err("No chip connected") + } + } + + fn run(&mut self, elapsed_time: std::time::Duration) { + if self.chip.is_some() { + self.chip.as_mut().unwrap().run(elapsed_time) + } + } +} \ No newline at end of file diff --git a/src/trace.rs b/src/trace.rs new file mode 100644 index 0000000..0e2fbb5 --- /dev/null +++ b/src/trace.rs @@ -0,0 +1,38 @@ +use super::{Pin, PinType, State}; +use std::cell::RefCell; +use std::rc::Rc; + +#[derive(Default, Debug)] +pub struct Trace { + link: Vec>> +} + +impl Trace { + pub fn new() -> Trace { + Trace { + link: vec![] + } + } + + pub fn connect(&mut self, pin: Rc>) { + self.link.push(pin) + } + + pub fn communicate(&mut self) { + let mut main_state = State::Undefined; + for pin in self.link.iter() { + if pin.borrow().pin_type == PinType::Output { + match pin.borrow().state { + State::High => main_state = State::High, + State::Low => if main_state == State::Undefined { main_state = State::Low }, + State::Undefined => {} + } + } + } + for pin in self.link.iter_mut() { + if pin.borrow().pin_type != PinType::Output { + pin.borrow_mut().state = main_state.clone(); + } + } + } +} \ No newline at end of file