mirror of
https://github.com/transistorfet/moa.git
synced 2024-11-21 19:30:52 +00:00
Added Macintosh 128k/512k work in progress
This commit is contained in:
parent
9a751e5e90
commit
d274186388
BIN
binaries/macintosh/Macintosh 512k.rom
Normal file
BIN
binaries/macintosh/Macintosh 512k.rom
Normal file
Binary file not shown.
13
frontends/moa-minifb/src/bin/moa-macintosh.rs
Normal file
13
frontends/moa-minifb/src/bin/moa-macintosh.rs
Normal 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
130
src/machines/macintosh.rs
Normal 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)
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
pub mod computie;
|
pub mod computie;
|
||||||
pub mod genesis;
|
pub mod genesis;
|
||||||
|
pub mod macintosh;
|
||||||
pub mod trs80;
|
pub mod trs80;
|
||||||
|
|
||||||
|
115
src/peripherals/macintosh/iwm.rs
Normal file
115
src/peripherals/macintosh/iwm.rs
Normal 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)
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
213
src/peripherals/macintosh/mainboard.rs
Normal file
213
src/peripherals/macintosh/mainboard.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
5
src/peripherals/macintosh/mod.rs
Normal file
5
src/peripherals/macintosh/mod.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
pub mod iwm;
|
||||||
|
pub mod video;
|
||||||
|
pub mod mainboard;
|
||||||
|
|
82
src/peripherals/macintosh/video.rs
Normal file
82
src/peripherals/macintosh/video.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,12 @@
|
|||||||
|
|
||||||
pub mod ata;
|
pub mod ata;
|
||||||
pub mod mc68681;
|
pub mod mc68681;
|
||||||
|
pub mod mos6522;
|
||||||
|
pub mod sn76489;
|
||||||
|
pub mod ym2612;
|
||||||
|
pub mod z8530;
|
||||||
|
|
||||||
pub mod genesis;
|
pub mod genesis;
|
||||||
|
pub mod macintosh;
|
||||||
pub mod trs80;
|
pub mod trs80;
|
||||||
|
|
||||||
|
121
src/peripherals/mos6522.rs
Normal file
121
src/peripherals/mos6522.rs
Normal 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
57
src/peripherals/z8530.rs
Normal 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)
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -19,16 +19,28 @@ impl<T: Copy> Signal<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone)]
|
||||||
pub struct Latch<T>(Rc<RefCell<T>>);
|
//pub struct Register<T>(Rc<RefCell<T>>);
|
||||||
|
pub struct Register<T>(Rc<RefCell<(T, Option<Box<dyn Fn(&T)>>)>>);
|
||||||
|
|
||||||
impl<T> Latch<T> {
|
impl<T> Register<T> {
|
||||||
pub fn new(init: T) -> Latch<T> {
|
pub fn new(init: T) -> Register<T> {
|
||||||
Latch(Rc::new(RefCell::new(init)))
|
Register(Rc::new(RefCell::new((init, None))))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn borrow_mut(&self) -> RefMut<'_, T> {
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
todo.txt
1
todo.txt
@ -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
|
* 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?
|
* 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
|
like specifying that address pins 10-7 should be ignored/unconnected, pin 11 will connect to "chip select", etc
|
||||||
|
Loading…
Reference in New Issue
Block a user