#![cfg(feature = "dev-pc")]
use rsemu::core::device::ResetKind;
use rsemu::core::space::MemAttrs;
use rsemu::core::value::Width;
use rsemu::machine::build;
use rsemu::machine::realize::Bindings;
const GATE2: u8 = 0x01;
const TIMER2_IN: u8 = 0x20;
fn options() -> rsemu::machine::BuildOptions {
let mut b = Bindings::new();
rsemu::machine::builtin::bind(&mut b).expect("ram, rom and the gates");
rsemu::dev::pc::bind(&mut b).expect("the chipset");
rsemu::machine::BuildOptions::new()
.with_classes(rsemu::machine::catalog::classes())
.with_bindings(b)
}
fn assemble(source: &str) -> Result<rsemu::machine::Machine, String> {
let registry = rsemu::machine::catalog::registry().expect("this build's registry");
build("gates.machine", source, ®istry, &options()).map_err(|e| e.to_string())
}
fn inb(m: &rsemu::machine::Machine, port: u64) -> u8 {
m.space("port")
.expect("the I/O space")
.read(port, Width::U8, MemAttrs::DEFAULT)
.expect("a decoded port") as u8
}
fn outb(m: &rsemu::machine::Machine, port: u64, value: u8) {
m.space("port")
.expect("the I/O space")
.write(port, Width::U8, u64::from(value), MemAttrs::DEFAULT)
.expect("a decoded port");
}
fn loopback(objects: &str, wiring: &str) -> Result<rsemu::machine::Machine, String> {
assemble(&format!(
r#"machine "gates" {{
space port {{ width = 16, unassigned = read-as-ones }}
object sysctl "pc.sysctl" {{}}
{objects}
map port 0x0061 size 0x0001 = sysctl.portb
{wiring}
}}"#
))
}
#[test]
fn an_inverter_comes_up_driving_high() {
let mut m = loopback(
" object inv \"wire.not\" {}",
" wire inv.out -> sysctl.timer2",
)
.expect("the board realizes");
m.reset(ResetKind::Cold);
m.sweep();
assert_eq!(
inb(&m, 0x61) & TIMER2_IN,
TIMER2_IN,
"the realize sweep never announced the inverter's idle output"
);
}
#[test]
fn a_level_travels_out_of_a_device_through_a_gate_and_back_in() {
let mut m = loopback(
" object inv \"wire.not\" {}",
" wire sysctl.gate2 -> inv.in\n wire inv.out -> sysctl.timer2",
)
.expect("the board realizes");
m.reset(ResetKind::Cold);
m.sweep();
assert_eq!(
inb(&m, 0x61) & TIMER2_IN,
TIMER2_IN,
"gate2 low, so out high"
);
outb(&m, 0x61, GATE2);
assert_eq!(inb(&m, 0x61) & TIMER2_IN, 0, "gate2 high, so out low");
outb(&m, 0x61, 0);
assert_eq!(inb(&m, 0x61) & TIMER2_IN, TIMER2_IN, "and back");
}
#[test]
fn an_and_gate_needs_both_of_its_inputs_from_a_machine_file() {
let mut m = loopback(
" object inv \"wire.not\" {}\n object gate \"wire.and\" { inputs = 2 }",
" wire sysctl.gate2 -> gate.in0\n wire inv.out -> gate.in1\n \
wire gate.out -> sysctl.timer2",
)
.expect("the board realizes");
m.reset(ResetKind::Cold);
m.sweep();
assert_eq!(inb(&m, 0x61) & TIMER2_IN, 0, "one input of two");
outb(&m, 0x61, GATE2);
assert_eq!(inb(&m, 0x61) & TIMER2_IN, TIMER2_IN, "and now both");
}
#[test]
fn a_split_carries_one_level_to_several_pins() {
let mut m = loopback(
" object fan \"wire.split\" { outputs = 2 }",
" wire sysctl.gate2 -> fan.in\n wire fan.out0 -> sysctl.timer2\n \
wire fan.out1 -> sysctl.refresh",
)
.expect("the board realizes");
m.reset(ResetKind::Cold);
m.sweep();
assert_eq!(inb(&m, 0x61) & TIMER2_IN, 0);
let before = inb(&m, 0x61) & 0x10;
outb(&m, 0x61, GATE2);
assert_eq!(inb(&m, 0x61) & TIMER2_IN, TIMER2_IN, "out0 followed `in`");
assert_ne!(inb(&m, 0x61) & 0x10, before, "out1 never moved");
}
#[test]
fn a_ring_of_gates_is_refused_and_a_loop_through_a_device_is_not() {
let e = loopback(
" object a \"wire.not\" {}\n object b \"wire.not\" {}",
" wire a.out -> b.in\n wire b.out -> a.in",
)
.expect_err("a ring of inverters never settles");
assert!(
e.contains("cycle") || e.contains("combinational"),
"the diagnostic should name the loop: {e}"
);
}
#[test]
fn a_pin_a_gate_does_not_have_is_reported_by_name() {
let e = loopback(
" object gate \"wire.and\" { inputs = 2 }",
" wire sysctl.gate2 -> gate.in7\n wire gate.out -> sysctl.timer2",
)
.expect_err("a two-input gate has no `in7`");
assert!(e.contains("in7"), "{e}");
let e = loopback(
" object gate \"wire.and\" { inputs = 2 }",
" wire sysctl.gate2 -> gate.inx\n wire gate.out -> sysctl.timer2",
)
.expect_err("`inx` is not a pin at all");
assert!(e.contains("inx"), "{e}");
let e = loopback(
" object gate \"wire.and\" { inpts = 2 }",
" wire gate.out -> sysctl.timer2",
)
.expect_err("a misspelt property");
assert!(e.contains("inpts"), "{e}");
}