Skip to content

Commit

Permalink
Ben Eater machine built with circuit componet (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
ddrcode committed Nov 7, 2023
1 parent 81e5ae1 commit 930730d
Show file tree
Hide file tree
Showing 13 changed files with 114 additions and 71 deletions.
19 changes: 16 additions & 3 deletions crates/machine/src/emulator/abstractions/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ impl Circuit {
cb(pin);
}
}

pub fn write_to_pin(&self, component_name: &str, pin_name: &str, val: bool) -> Result<bool, EmulatorError> {
let c = self.component(component_name).borrow();
if let Some(pin) = c.get_pin(pin_name) {
pin.write(val)
} else {
Err(EmulatorError::PinNotFound(component_name.to_string(), pin_name.to_string()))
}
}
}

// --------------------------------------------------------------------
Expand Down Expand Up @@ -209,12 +218,16 @@ impl CircuitBuilder {
// QUESTION #4
// Achieving the same functionality without callback would, most likely,
// result in a cleaner code. But is there an alternative to it?
pin.set_handler(Rc::clone(&handler) as Rc<RefCell<dyn PinStateChange>>)?;
pin.set_inner_id(*key);
if pin.inner_id().is_none() {
pin.set_handler(Rc::clone(&handler) as Rc<RefCell<dyn PinStateChange>>)?;
pin.set_inner_id(*key);
}

let data = &cref.pins[rkey];
let component = cref.components[&data.0].borrow();
let pin = component.get_pin(&data.1).unwrap();
let pin = component
.get_pin(&data.1)
.ok_or(EmulatorError::PinNotFound(data.0.clone(), data.1.clone()))?;
pin.set_inner_id(*rkey);
}

Expand Down
1 change: 0 additions & 1 deletion crates/machine/src/emulator/abstractions/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@ use super::{Pin, PinStateChange};
pub trait Component: PinStateChange {
fn get_pin(&self, name: &str) -> Option<&Pin>;
}

8 changes: 4 additions & 4 deletions crates/machine/src/emulator/abstractions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
mod addressable;
mod async_device;
mod circuit;
mod component;
mod machine;
mod pin;
mod pin_builder;
mod port;
mod tickable;
mod component;
mod circuit;

pub use addressable::*;
pub use async_device::*;
pub use circuit::*;
pub use component::*;
pub use machine::*;
pub use pin::*;
pub use pin_builder::*;
pub use port::*;
pub use tickable::*;
pub use circuit::*;
pub use component::*;
49 changes: 31 additions & 18 deletions crates/machine/src/emulator/abstractions/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,13 @@ impl Pin {
let _ = self.group_id.set(id);
}

pub(crate) fn set_handler(&self, handler: Rc<RefCell<dyn PinStateChange>>) -> Result<(), EmulatorError> {
pub(crate) fn set_handler(
&self,
handler: Rc<RefCell<dyn PinStateChange>>,
) -> Result<(), EmulatorError> {
self.handler
.set(handler)
.map_err(|_| EmulatorError::HandlerAlreadyDefined)
.map_err(|_| EmulatorError::HandlerAlreadyDefined(self.name()))
}

pub fn set_enable(&self, val: bool) -> Result<(), EmulatorError> {
Expand All @@ -125,11 +128,21 @@ impl Pin {
!self.state()
}

pub fn name(&self) -> String {
let name = &self.name;
let group_id = self.group_id();
if group_id.is_some() {
format!("{}{}", name, group_id.unwrap())
} else {
name.to_string()
}
}

pub fn group_name(&self) -> Option<String> {
let name = self.name();
let group_id = self.group_id();
if group_id.is_some() {
Some(format!("{}{}", name, group_id.unwrap()))
Some(name)
} else {
None
}
Expand All @@ -139,19 +152,17 @@ impl Pin {
self.direction() == PinDirection::Output
}

pub fn set_high(&self) {
self.write(true);
pub fn set_high(&self) -> Result<bool, EmulatorError> {
self.write(true)
}

pub fn set_low(&self) {
self.write(false);
pub fn set_low(&self) -> Result<bool, EmulatorError> {
self.write(false)
}

pub fn toggle(&self) {
if self.is_output() {
let v = self.state();
self.write(!v);
}
pub fn toggle(&self) -> Result<bool, EmulatorError> {
let v = self.state();
self.write(!v)
}

pub fn enabled(&self) -> bool {
Expand All @@ -166,16 +177,22 @@ impl Pin {
*self.direction.borrow()
}

pub fn write(&self, val: bool) {
pub fn write(&self, val: bool) -> Result<bool, EmulatorError> {
if self.is_output() {
if *self.value.borrow() == val {
return Ok(false);
}
*self.value.borrow_mut() = val;
if let Some(handler) = self.handler.get() {
handler.borrow_mut().on_state_change(self);
}
Ok(true)
} else {
Err(EmulatorError::CantWriteToReadPin(self.name()))
}
}

pub fn set_val(&self, val: bool) {
pub(crate) fn set_val(&self, val: bool) {
if !self.is_output() {
*self.value.borrow_mut() = val;
}
Expand All @@ -193,10 +210,6 @@ impl Pin {
self.group_id.get().copied()
}

pub fn name(&self) -> &str {
&self.name
}

pub fn tri_state(&self) -> bool {
self.tri_state
}
Expand Down
17 changes: 11 additions & 6 deletions crates/machine/src/emulator/abstractions/port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ where
}

pub fn from_pins(width: T, pins: Vec<Rc<Pin>>) -> Rc<Self> {
let mut port = Rc::new(Port {
let port = Rc::new(Port {
width,
pins: pins.into_boxed_slice(),
handler: OnceCell::new(),
Expand Down Expand Up @@ -75,7 +75,9 @@ where
for i in 0..self.width().into() {
let flag: T = (<T as NumCast>::from(1 << i)).unwrap();
let val = state & flag;
self.pins[i].write(val > T::zero());
self.pins[i]
.write(val > T::zero())
.expect("Can't write to input pin");
}
}

Expand All @@ -93,14 +95,17 @@ where
}
}

pub fn set_handler(&self, handler: Rc<RefCell<dyn PinStateChange>>) -> Result<(), EmulatorError> {
pub fn set_handler(
&self,
handler: Rc<RefCell<dyn PinStateChange>>,
) -> Result<(), EmulatorError> {
// for i in 0..self.width().into() {
// let h = Rc::clone(&self.self_ref.get().unwrap());
// self.pins[i].set_handler(h)?;
// }
self.handler
.set(handler)
.map_err(|_| EmulatorError::HandlerAlreadyDefined)
.map_err(|_| EmulatorError::HandlerAlreadyDefined("port".to_string()))
}
}

Expand All @@ -116,7 +121,7 @@ mod tests {

#[test]
fn test_u8_port_creation() {
let p: Rc<Port<u8>> = Port::new("A", 8, PinDirection::Input);
let p: Rc<Port<u8>> = Port::new("A", 8, PinDirection::Output);
assert_eq!(0, p.read());

p.set_directions(0xff);
Expand All @@ -128,7 +133,7 @@ mod tests {

#[test]
fn test_u16_port_creation() {
let p: Rc<Port<u16>> = Port::new("A", 16, PinDirection::Input);
let p: Rc<Port<u16>> = Port::new("A", 16, PinDirection::Output);
assert_eq!(0, p.read());

p.set_directions(0xff);
Expand Down
9 changes: 4 additions & 5 deletions crates/machine/src/emulator/components/hm62256b.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::emulator::abstractions::{
Addr, Addressable, Pin, PinBuilder,
Addr, Addressable, Component, Pin, PinBuilder,
PinDirection::{self, *},
PinStateChange, Port, Component,
PinStateChange, Port,
};
use std::collections::HashMap;
use std::{cell::RefCell, rc::Rc};
use std::rc::Rc;

const ADDR_PINS: [usize; 15] = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 25, 24, 21, 23, 26];
const DATA_PINS: [usize; 8] = [11, 12, 13, 15, 16, 17, 18, 19];
Expand Down Expand Up @@ -35,8 +35,7 @@ impl HM62256BPins {
.build()
.iter()
.map(|pin| Rc::new(pin.clone()))
.collect()
;
.collect();

let data_pins: Vec<Rc<Pin>> = DATA_PINS.map(|id| Rc::clone(&pins[id - 1])).to_vec();
let addr_pins: Vec<Rc<Pin>> = ADDR_PINS.map(|id| Rc::clone(&pins[id - 1])).to_vec();
Expand Down
2 changes: 1 addition & 1 deletion crates/machine/src/emulator/components/nand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl<T: INand> PinStateChange for Nand<T> {
let val = self
.logic
.execute(self.pins.in1.read(), self.pins.in2.read());
self.pins.out.write(val);
self.pins.out.write(val).expect("Pin must be writeable");
}
}

Expand Down
9 changes: 6 additions & 3 deletions crates/machine/src/emulator/components/oscilator.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::{emulator::abstractions::{Pin, Tickable, Component, PinStateChange}, utils::if_else};
use crate::{
emulator::abstractions::{Component, Pin, PinStateChange, Tickable},
utils::if_else,
};
use gametime::{Frequency, FrequencyTicker, TimeStamp};

pub struct Oscilator {
Expand All @@ -17,13 +20,13 @@ impl Oscilator {

impl Tickable for Oscilator {
fn tick(&self) {
self.pin.toggle();
self.pin.toggle().unwrap();
}
}

impl Component for Oscilator {
fn get_pin(&self, name: &str) -> Option<&Pin> {
if_else(name=="OUT", Some(&self.pin), None)
if_else(name == "OUT", Some(&self.pin), None)
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/machine/src/emulator/cpus/mos6510/mos6510.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ impl MOS6510 {
};

let cpu_rc = Rc::new(cpu);
let cloned = cpu_rc.clone();
// let cloned = cpu_rc.clone();
// cpu_rc.pins.phi0.observe(move |_val| cloned.tick());

cpu_rc
}

fn tick(&self) {
self.pins.phi2.write(self.pins.phi0.state());
self.pins.phi2.write(self.pins.phi0.state()).unwrap();
self.ticker.tick();
}

Expand Down
17 changes: 9 additions & 8 deletions crates/machine/src/emulator/cpus/w65c02/pins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ pub struct W65C02_Pins {
impl W65C02_Pins {
pub fn new() -> Self {
let pins: Vec<Rc<Pin>> = PinBuilder::new(40)
.set(1, "VPB", Output)
.set(1, "VP", Output)
.set(2, "RDY", Input)
.set(3, "PHI1O", Output)
.set(4, "IRQB", Input)
.set(5, "MLB", Output)
.set(6, "NMIB", Input)
.set(4, "IRQ", Input)
.set(5, "ML", Output)
.set(6, "NMI", Input)
.set(7, "SYNC", Output)
.set(8, "VDD", Input)
.set_range(9..=20, "A", 0, Output)
Expand All @@ -28,14 +28,14 @@ impl W65C02_Pins {
.group_dec("D", 7)
.tri_state()
.io()
.set(34, "RWB", Output)
.set(34, "RW", Output)
.tri_state()
.set(35, "NC", Input)
.set(36, "BE", Input)
.set(37, "PHI2", Input)
.set(38, "SOB", Output)
.set(38, "SO", Output)
.set(39, "PHI2O", Output)
.set(40, "RESB", Input)
.set(40, "RES", Input)
.build()
.iter()
.map(move |pin| Rc::new(pin.clone()))
Expand Down Expand Up @@ -66,6 +66,7 @@ impl W65C02_Pins {
pub fn by_name(&self, name: &str) -> Option<&Pin> {
self.pins
.iter()
.find(|&pin| pin.name() == name).map(|pin| pin.as_ref())
.find(|&pin| pin.name() == name)
.map(|pin| pin.as_ref())
}
}
13 changes: 7 additions & 6 deletions crates/machine/src/emulator/cpus/w65c02/w65c02.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use corosensei::{Coroutine, CoroutineResult};
use std::{cell::RefCell, rc::Rc};

use crate::emulator::abstractions::{Addr, Addressable, CPUCycles, PinStateChange, CPU, Pin, Component};
use crate::emulator::abstractions::{
Addr, Addressable, CPUCycles, Component, Pin, PinStateChange, CPU,
};
use crate::emulator::cpus::mos6502::{get_stepper, nop, OperationDef, Stepper, OPERATIONS};

use super::W65C02_Pins;
Expand Down Expand Up @@ -46,9 +48,8 @@ impl W65C02 {
// cpu.pins
// .by_name("PHI2")
// .unwrap()
// .set_handler(Rc::clone(&cpu) as Rc<dyn PinStateChange>)
// .unwrap();

// .set_handler(Rc::clone(&cpu) as Rc<dyn PinStateChange>)
// .unwrap();
}
}

Expand Down Expand Up @@ -147,13 +148,13 @@ impl CPU for W65C02Logic {
}

fn read_byte(&self, addr: Addr) -> u8 {
self.pins.by_name("RWB").unwrap().set_high();
self.pins.by_name("RWB").unwrap().set_high().unwrap();
self.pins.addr.write(addr);
self.pins.data.read()
}

fn write_byte(&mut self, addr: Addr, val: u8) {
self.pins.by_name("RWB").unwrap().set_low();
self.pins.by_name("RWB").unwrap().set_low().unwrap();
self.pins.addr.write(addr);
self.pins.data.write(val);
}
Expand Down
Loading

0 comments on commit 930730d

Please sign in to comment.