Added Macintosh 128k/512k work in progress

This commit is contained in:
transistor 2021-12-06 15:04:08 -08:00
parent 9a751e5e90
commit d274186388
13 changed files with 762 additions and 6 deletions

Binary file not shown.

View File

@ -0,0 +1,13 @@
use moa_minifb;
use moa::machines::macintosh::build_macintosh_512k;
fn main() {
let matches = moa_minifb::new("Macintosh 512k Emulator")
.get_matches();
moa_minifb::run(matches, |frontend| {
build_macintosh_512k(frontend)
});
}

130
src/machines/macintosh.rs Normal file
View File

@ -0,0 +1,130 @@
use crate::error::Error;
use crate::system::System;
use crate::devices::{wrap_transmutable, Debuggable};
use crate::memory::{MemoryBlock, AddressAdapter, BusPort};
use crate::cpus::m68k::{M68k, M68kType};
use crate::peripherals::mos6522::Mos6522;
use crate::peripherals::z8530::Z8530;
use crate::peripherals::macintosh::iwm::IWM;
use crate::peripherals::macintosh::video::MacVideo;
use crate::peripherals::macintosh::mainboard::Mainboard;
use crate::host::traits::Host;
pub fn build_macintosh_512k<H: Host>(host: &mut H) -> Result<System, Error> {
let mut system = System::new();
/*
let mut ram = MemoryBlock::new(vec![0; 0x00100000]);
ram.load_at(0, "binaries/macintosh/Macintosh 128k.rom")?;
let boxed_ram = wrap_transmutable(ram);
//system.add_addressable_device(0x00000000, boxed_ram.clone())?;
//system.add_addressable_device(0x00600000, boxed_ram)?;
let mut rom = MemoryBlock::load("binaries/macintosh/Macintosh 128k.rom")?;
rom.read_only();
let boxed_rom = wrap_transmutable(rom);
//system.add_addressable_device(0x00400000, wrap_transmutable(rom))?;
// The ROM accesses 0xf80000 to look for the debugger, and then accesses 0xf0000 which is the phase area
let misc = MemoryBlock::new(vec![0; 0x100000]);
system.add_addressable_device(0x00f00000, wrap_transmutable(misc))?;
let video = MacVideo::create(host)?;
system.add_device("video", wrap_transmutable(video)).unwrap();
let scc1 = Z8530::new();
//launch_terminal_emulator(serial.port_a.connect(Box::new(SimplePty::open()?))?);
//launch_slip_connection(serial.port_b.connect(Box::new(SimplePty::open()?))?);
system.add_addressable_device(0x009FFFF0, wrap_transmutable(scc1))?;
let scc2 = Z8530::new();
//launch_terminal_emulator(serial.port_a.connect(Box::new(SimplePty::open()?))?);
//launch_slip_connection(serial.port_b.connect(Box::new(SimplePty::open()?))?);
system.add_addressable_device(0x00BFFFF0, wrap_transmutable(scc2))?;
let iwm = IWM::new();
let adapter = AddressAdapter::new(wrap_transmutable(iwm), 9);
system.add_addressable_device(0x00DFE1FF, wrap_transmutable(adapter))?;
//let via = wrap_transmutable(Mos6522::new());
let mainboard = Mainboard::new(boxed_ram, boxed_rom);
system.add_addressable_device(0x00000000, mainboard.bus.clone())?;
let mainboard_boxed = wrap_transmutable(mainboard);
system.add_device("via", mainboard_boxed.clone())?;
let adapter = AddressAdapter::new(mainboard_boxed, 9);
system.add_addressable_device(0x00EFE000, wrap_transmutable(adapter))?;
*/
let mut ram = MemoryBlock::new(vec![0; 0x00080000]);
let mut rom = MemoryBlock::load("binaries/macintosh/Macintosh 512k.rom")?;
rom.read_only();
let video = MacVideo::create(host)?;
system.add_device("video", wrap_transmutable(video)).unwrap();
let mainboard = Mainboard::create(wrap_transmutable(ram), wrap_transmutable(rom))?;
system.add_addressable_device(0x00000000, wrap_transmutable(mainboard))?;
let mut cpu = M68k::new(M68kType::MC68000, 7_833_600, BusPort::new(0, 24, 16, system.bus.clone()));
//cpu.enable_tracing();
//system.enable_debugging();
//cpu.add_breakpoint(0x10781a);
//cpu.add_breakpoint(0x40002a);
//cpu.add_breakpoint(0x400694); // Ram Test
//cpu.add_breakpoint(0x400170); // Failed, loops infinitely
cpu.add_breakpoint(0x4000f4); // Failed, should show the sad mac
//cpu.add_breakpoint(0x4006ae);
//cpu.add_breakpoint(0x400706);
//cpu.add_breakpoint(0x400722); // end of ram test
//cpu.add_breakpoint(0x40026c); // System Initialization
//cpu.add_breakpoint(0x402adc);
//cpu.add_breakpoint(0x40078e);
//cpu.add_breakpoint(0x40080a);
//cpu.add_breakpoint(0x400448);
//cpu.add_breakpoint(0x40040a); // InitROMCore (set up trap dispatch table)
//cpu.add_breakpoint(0x402acc); // InitMem
//cpu.add_breakpoint(0x40045c);
//cpu.add_breakpoint(0x400614); // Start of InitIO
cpu.add_breakpoint(0x40062a); // Loop in InitIO
//cpu.add_breakpoint(0x400648);
//cpu.add_breakpoint(0x40064c);
//cpu.add_breakpoint(0x4014a6); // DrvrInstall
//cpu.add_breakpoint(0x401262); // $A000 handler, which is where the rom write happens
//cpu.add_breakpoint(0x4012ec);
//cpu.add_breakpoint(0x40133a);
// Issue of writing to 0x100000 which doesn't exist
cpu.add_breakpoint(0x400d62);
cpu.add_breakpoint(0x400464); // Boot Screen
/*
use crate::devices::Addressable;
use crate::cpus::m68k::state::M68kState;
for i in 0..=65535 {
cpu.state = M68kState::new();
system.get_bus().write_beu16(0, i).unwrap();
match cpu.decode_next(&system) {
Ok(()) => { println!("TestCase {{ cpu: M68kType::MC68000, data: &[{:#06X}]\tins: Some({:?}) }},", i, cpu.decoder.instruction); },
Err(_) => { println!("TestCase {{ cpu: M68kType::MC68000, data: &[{:#06X}]\tins: None }},", i); },
}
}
panic!("");
*/
system.add_interruptable_device("cpu", wrap_transmutable(cpu))?;
Ok(system)
}

View File

@ -1,5 +1,6 @@
pub mod computie;
pub mod genesis;
pub mod macintosh;
pub mod trs80;

View File

@ -0,0 +1,115 @@
use crate::error::Error;
use crate::system::System;
use crate::devices::{ClockElapsed, Address, Addressable, Steppable, Transmutable};
const CA0: u8 = 0x01;
const CA1: u8 = 0x02;
const CA2: u8 = 0x04;
const LSTRB: u8 = 0x08;
const ENABLE: u8 = 0x10;
const SELECT: u8 = 0x20;
const Q6: u8 = 0x40;
const Q7: u8 = 0x80;
const DEV_NAME: &'static str = "iwm";
pub struct IWM {
pub state: u8,
pub mode: u8,
pub handshake: u8,
}
impl IWM {
pub fn new() -> Self {
Self {
state: 0,
mode: 0,
handshake: 0,
}
}
pub fn flip_switches(&mut self, addr: Address) {
let mask = 1 << (addr >> 1);
if (addr & 0x01) != 0 {
self.state |= mask;
} else {
self.state &= !mask;
}
info!("{}: state is now {:x}", DEV_NAME, self.state);
}
}
impl Addressable for IWM {
fn len(&self) -> usize {
0x10
}
fn read(&mut self, addr: Address, data: &mut [u8]) -> Result<(), Error> {
self.flip_switches(addr);
if (addr & 0x01) != 0 {
data[0] = 0xFF;
return Ok(());
}
let i = data.len() - 1;
match self.state & (Q7 | Q6) {
0 => {
// read data register
data[0] = 0xFF;
},
Q6 => {
// read "status" register
data[i] = (self.mode & 0x1F) | if (self.state & ENABLE) != 0 { 0x20 } else { 0x00 };
},
Q7 => {
// read "write-handshake" register
data[i] = 0x3F | self.handshake;
}
b if b == (Q7 | Q6) => {
panic!("");
},
_ => {
warning!("{}: !!! unhandled read of {:0x} with state {:x}", DEV_NAME, addr, self.state);
},
}
info!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data);
Ok(())
}
fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> {
self.flip_switches(addr);
info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
let i = data.len() - 1;
match self.state & (Q7 | Q6 | ENABLE) {
b if b == (Q7 | Q6 | ENABLE) => {
self.handshake &= !0x80;
},
b if b == (Q7 | Q6) => {
// write the mode register
self.mode = data[i] & 0x1f;
},
_ => {
warning!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr);
},
}
Ok(())
}
}
impl Transmutable for IWM {
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
Some(self)
}
//fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
// Some(self)
//}
}

View File

@ -0,0 +1,213 @@
use std::rc::Rc;
use std::cell::RefCell;
use crate::memory::Bus;
use crate::error::Error;
use crate::system::System;
use crate::signals::Signal;
use crate::devices::{Clock, ClockElapsed, Address, Addressable, Steppable, Transmutable, TransmutableBox, wrap_transmutable};
use crate::peripherals::z8530::Z8530;
use crate::peripherals::mos6522::Mos6522;
use crate::peripherals::macintosh::iwm::IWM;
use crate::peripherals::macintosh::video::MacVideo;
const DEV_NAME: &'static str = "mac";
pub struct Mainboard {
pub lower_bus: Rc<RefCell<Bus>>,
pub scc1: Z8530,
pub scc2: Z8530,
pub iwm: IWM,
pub via: Mos6522,
pub phase_read: PhaseRead,
pub last_sec: Clock,
}
impl Mainboard {
pub fn create(ram: TransmutableBox, rom: TransmutableBox) -> Result<Self, Error> {
let scc1 = Z8530::new();
let scc2 = Z8530::new();
let iwm = IWM::new();
let via = Mos6522::new();
let phase_read = PhaseRead::new();
let lower_bus = Rc::new(RefCell::new(Bus::new()));
let ram_len = ram.borrow_mut().as_addressable().unwrap().len();
let rom_len = rom.borrow_mut().as_addressable().unwrap().len();
let mainboard = Self {
lower_bus: lower_bus.clone(),
scc1,
scc2,
iwm,
via,
phase_read,
last_sec: 0,
};
mainboard.via.port_a.set_observer(move |port| {
if (port.data & 0x10) == 0 {
println!("{}: overlay is 0 (normal)", DEV_NAME);
lower_bus.borrow_mut().blocks.clear();
lower_bus.borrow_mut().insert(0x000000, wrap_transmutable(AddressRepeater::new(ram.clone(), 32)));
lower_bus.borrow_mut().insert(0x400000, wrap_transmutable(AddressRepeater::new(rom.clone(), 16)));
lower_bus.borrow_mut().insert(0x600000, wrap_transmutable(AddressRepeater::new(rom.clone(), 16)));
} else {
println!("{}: overlay is 1 (startup)", DEV_NAME);
lower_bus.borrow_mut().blocks.clear();
lower_bus.borrow_mut().insert(0x000000, wrap_transmutable(AddressRepeater::new(rom.clone(), 16)));
lower_bus.borrow_mut().insert(0x200000, wrap_transmutable(AddressRepeater::new(rom.clone(), 16)));
lower_bus.borrow_mut().insert(0x400000, wrap_transmutable(AddressRepeater::new(rom.clone(), 16)));
lower_bus.borrow_mut().insert(0x600000, wrap_transmutable(AddressRepeater::new(ram.clone(), 16)));
}
});
mainboard.via.port_a.notify();
Ok(mainboard)
}
}
impl Addressable for Mainboard {
fn len(&self) -> usize {
0x01000000
}
fn read(&mut self, addr: Address, data: &mut [u8]) -> Result<(), Error> {
if addr < 0x800000 {
self.lower_bus.borrow_mut().read(addr, data)
} else if addr >= 0x900000 && addr < 0xA00000 {
self.scc1.read((addr >> 9) & 0x0F, data)
} else if addr >= 0xB00000 && addr < 0xC00000 {
self.scc1.read((addr >> 9) & 0x0F, data)
} else if addr >= 0xD00000 && addr < 0xE00000 {
self.iwm.read((addr >> 9) & 0x0F, data)
} else if addr >= 0xE80000 && addr < 0xF00000 {
self.via.read((addr >> 9) & 0x0F, data)
} else if addr >= 0xF00000 && addr < 0xF80000 {
// TODO phase read
Ok(())
} else if addr >= 0xF80000 && addr < 0xF80010 {
// Debugger
Ok(())
} else {
Err(Error::new(&format!("Error reading address {:#010x}", addr)))
}
}
fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> {
if addr < 0x800000 {
self.lower_bus.borrow_mut().write(addr, data)
} else if addr >= 0x900000 && addr < 0xA00000 {
self.scc1.write((addr >> 9) & 0x0F, data)
} else if addr >= 0xB00000 && addr < 0xC00000 {
self.scc1.write((addr >> 9) & 0x0F, data)
} else if addr >= 0xD00000 && addr < 0xE00000 {
self.iwm.write((addr >> 9) & 0x0F, data)
} else if addr >= 0xE80000 && addr < 0xF00000 {
self.via.write((addr >> 9) & 0x0F, data)
} else if addr >= 0xF00000 && addr < 0xF80000 {
// TODO phase read
Ok(())
} else {
Err(Error::new(&format!("Error writing address {:#010x}", addr)))
}
}
}
impl Steppable for Mainboard {
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
let elapsed = self.via.step(system)?;
// TODO should this be 1 second, or a multiple of 979_200, which is an 8th of the CPU clock
if self.last_sec + 1_000_000_000 > system.clock {
self.last_sec += 1_000_000_000;
//let port_a = self.via.port_a.borrow_mut();
// TODO how will the ca1/ca2 cb1/cb2 pins work in the via
system.get_interrupt_controller().set(true, 1, 25)?;
}
Ok(elapsed)
}
}
impl Transmutable for Mainboard {
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
Some(self)
}
fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
Some(self)
}
}
pub struct PhaseRead {
}
impl PhaseRead {
pub fn new() -> Self {
Self {
}
}
}
impl Addressable for PhaseRead {
fn len(&self) -> usize {
0x80000
}
fn read(&mut self, addr: Address, data: &mut [u8]) -> Result<(), Error> {
Ok(())
}
fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> {
Ok(())
}
}
pub struct AddressRepeater {
pub subdevice: TransmutableBox,
pub repeat: u8,
}
impl AddressRepeater {
pub fn new(subdevice: TransmutableBox, repeat: u8) -> Self {
Self {
subdevice,
repeat,
}
}
}
impl Addressable for AddressRepeater {
fn len(&self) -> usize {
let len = self.subdevice.borrow_mut().as_addressable().unwrap().len();
len * self.repeat as usize
}
fn read(&mut self, addr: Address, data: &mut [u8]) -> Result<(), Error> {
let len = self.subdevice.borrow_mut().as_addressable().unwrap().len() as Address;
self.subdevice.borrow_mut().as_addressable().unwrap().read(addr % len, data)
}
fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> {
let len = self.subdevice.borrow_mut().as_addressable().unwrap().len() as Address;
self.subdevice.borrow_mut().as_addressable().unwrap().write(addr % len, data)
}
}
impl Transmutable for AddressRepeater {
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
Some(self)
}
}

View File

@ -0,0 +1,5 @@
pub mod iwm;
pub mod video;
pub mod mainboard;

View File

@ -0,0 +1,82 @@
use std::sync::{Arc, Mutex};
use crate::error::Error;
use crate::system::System;
use crate::devices::{ClockElapsed, Address, Addressable, Steppable, Transmutable};
use crate::host::gfx::FrameSwapper;
use crate::host::traits::{Host, BlitableSurface};
const SCRN_BASE: u32 = 0x07A700;
pub struct MacVideo {
pub swapper: Arc<Mutex<FrameSwapper>>,
}
impl MacVideo {
pub fn create<H: Host>(host: &mut H) -> Result<Self, Error> {
let swapper = FrameSwapper::new_shared(512, 342);
host.add_window(FrameSwapper::to_boxed(swapper.clone()))?;
Ok(Self {
swapper,
})
}
}
pub struct BitIter {
pub bit: i8,
pub data: u16,
}
impl BitIter {
pub fn new(data: u16) -> Self {
Self {
bit: 15,
data,
}
}
}
impl Iterator for BitIter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
if self.bit < 0 {
None
} else {
let bit = (self.data & (1 << self.bit)) != 0;
self.bit -= 1;
if bit {
Some(0xC0C0C0)
} else {
Some(0)
}
}
}
}
impl Steppable for MacVideo {
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
let mut memory = system.get_bus();
let mut swapper = self.swapper.lock().unwrap();
for y in 0..342 {
for x in 0..(512 / 16) {
let word = memory.read_beu16((SCRN_BASE + (x * 2) + (y * (512 / 8))) as Address)?;
swapper.current.blit(x * 16, y, BitIter::new(word), 16, 1);
}
}
Ok(16_600_000)
}
}
impl Transmutable for MacVideo {
fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
Some(self)
}
}

View File

@ -1,6 +1,12 @@
pub mod ata;
pub mod mc68681;
pub mod mos6522;
pub mod sn76489;
pub mod ym2612;
pub mod z8530;
pub mod genesis;
pub mod macintosh;
pub mod trs80;

121
src/peripherals/mos6522.rs Normal file
View File

@ -0,0 +1,121 @@
use crate::error::Error;
use crate::system::System;
use crate::signals::{Signal, Register};
use crate::devices::{ClockElapsed, Address, Addressable, Steppable, Transmutable};
const REG_OUTPUT_B: Address = 0x00;
const REG_OUTPUT_A: Address = 0x01;
const REG_DDR_B: Address = 0x02;
const REG_DDR_A: Address = 0x03;
const REG_PERIPH_CTRL: Address = 0x0C;
const REG_INT_FLAGS: Address = 0x0D;
const REG_INT_ENABLE: Address = 0x0E;
const REG_OUTPUT_A_NHS: Address = 0x0F;
const DEV_NAME: &'static str = "mos6522";
pub struct Port {
pub data: u8,
pub ddr: u8,
}
impl Port {
pub fn new() -> Self {
Self {
data: 0xff,
ddr: 0,
}
}
}
pub struct Mos6522 {
pub port_a: Register<Port>,
pub port_b: Register<Port>,
pub peripheral_ctrl: u8,
pub interrupt: Signal<bool>,
pub interrupt_flags: u8,
pub interrupt_enable: u8,
}
impl Mos6522 {
pub fn new() -> Self {
Self {
port_a: Register::new(Port::new()),
port_b: Register::new(Port::new()),
peripheral_ctrl: 0,
interrupt: Signal::new(false),
interrupt_flags: 0,
interrupt_enable: 0,
}
}
}
impl Addressable for Mos6522 {
fn len(&self) -> usize {
0x10
}
fn read(&mut self, addr: Address, data: &mut [u8]) -> Result<(), Error> {
match addr {
REG_OUTPUT_B => { data[0] = self.port_b.borrow_mut().data; },
REG_OUTPUT_A => { data[0] = self.port_a.borrow_mut().data; },
REG_DDR_B => { data[0] = self.port_b.borrow_mut().ddr; },
REG_DDR_A => { data[0] = self.port_a.borrow_mut().ddr; },
REG_INT_FLAGS => { data[0] = self.interrupt_flags; },
REG_INT_ENABLE => { data[0] = self.interrupt_enable | 0x80; },
_ => {
warning!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr);
},
}
debug!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data);
Ok(())
}
fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> {
debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
match addr {
REG_OUTPUT_B => { self.port_b.borrow_mut().data = data[0]; self.port_b.notify(); },
REG_OUTPUT_A => { self.port_a.borrow_mut().data = data[0]; self.port_a.notify(); },
REG_DDR_B => { self.port_b.borrow_mut().ddr = data[0]; self.port_b.notify(); },
REG_DDR_A => { self.port_a.borrow_mut().ddr = data[0]; self.port_a.notify(); },
REG_PERIPH_CTRL => { println!("SET TO {:?}", data[0]); self.peripheral_ctrl = data[0]; },
REG_INT_FLAGS => { self.interrupt_flags &= !data[0] & 0x7F; },
REG_INT_ENABLE => {
if (data[0] & 0x80) == 0 {
self.interrupt_flags &= !data[0];
} else {
self.interrupt_flags |= data[0];
}
},
REG_OUTPUT_A_NHS => { self.port_a.borrow_mut().data = data[0]; self.port_a.notify(); },
_ => {
warning!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr);
},
}
Ok(())
}
}
impl Steppable for Mos6522 {
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
Ok(16_600_000)
}
}
impl Transmutable for Mos6522 {
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
Some(self)
}
fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
Some(self)
}
}

57
src/peripherals/z8530.rs Normal file
View File

@ -0,0 +1,57 @@
use crate::error::Error;
use crate::system::System;
use crate::devices::{ClockElapsed, Address, Addressable, Steppable, Transmutable};
const DEV_NAME: &'static str = "z8530";
pub struct Z8530 {
}
impl Z8530 {
pub fn new() -> Self {
Self {
}
}
}
impl Addressable for Z8530 {
fn len(&self) -> usize {
0x10
}
fn read(&mut self, addr: Address, data: &mut [u8]) -> Result<(), Error> {
match addr {
_ => {
warning!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr);
},
}
debug!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data);
Ok(())
}
fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> {
debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
match addr {
_ => {
warning!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr);
},
}
Ok(())
}
}
impl Transmutable for Z8530 {
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
Some(self)
}
//fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
// Some(self)
//}
}

View File

@ -19,16 +19,28 @@ impl<T: Copy> Signal<T> {
}
}
#[derive(Clone, Debug)]
pub struct Latch<T>(Rc<RefCell<T>>);
#[derive(Clone)]
//pub struct Register<T>(Rc<RefCell<T>>);
pub struct Register<T>(Rc<RefCell<(T, Option<Box<dyn Fn(&T)>>)>>);
impl<T> Latch<T> {
pub fn new(init: T) -> Latch<T> {
Latch(Rc::new(RefCell::new(init)))
impl<T> Register<T> {
pub fn new(init: T) -> Register<T> {
Register(Rc::new(RefCell::new((init, None))))
}
pub fn borrow_mut(&self) -> RefMut<'_, T> {
self.0.borrow_mut()
RefMut::map(self.0.borrow_mut(), |v| &mut v.0)
}
pub fn set_observer<F>(&self, f: F) where F: Fn(&T) + 'static {
self.0.borrow_mut().1 = Some(Box::new(f));
}
pub fn notify(&self) {
let data = self.0.borrow();
if let Some(closure) = &data.1 {
closure(&data.0);
}
}
}

View File

@ -1,4 +1,5 @@
* currently you need to implement the 1.5ms reset in the genesis controllers
* should SharedData be HostData, or something else? I don't think the name is very informative
* can you make the connections between things (like memory adapters), be expressed in a way that's more similar to the electrical design?
like specifying that address pins 10-7 should be ignored/unconnected, pin 11 will connect to "chip select", etc