diff --git a/Cargo.toml b/Cargo.toml index 38dbccd..a599d7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2018" [workspace] -members = [".", "frontends/moa-console"] +members = [".", "frontends/moa-console", "frontends/moa-minifb"] default-members = ["frontends/moa-console"] [features] diff --git a/binaries/genesis/Sonic The Hedgehog (W) (REV 01) [!].bin b/binaries/genesis/Sonic The Hedgehog (W) (REV 01) [!].bin new file mode 100755 index 0000000..f254c3a Binary files /dev/null and b/binaries/genesis/Sonic The Hedgehog (W) (REV 01) [!].bin differ diff --git a/frontends/moa-minifb/Cargo.toml b/frontends/moa-minifb/Cargo.toml new file mode 100644 index 0000000..7d11a0c --- /dev/null +++ b/frontends/moa-minifb/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "moa-minifb" +version = "0.1.0" +edition = "2018" +default-run = "moa-genesis" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +moa = { path = "../../" } +minifb = "0.19" + diff --git a/frontends/moa-minifb/src/bin/moa-genesis.rs b/frontends/moa-minifb/src/bin/moa-genesis.rs new file mode 100644 index 0000000..66c8125 --- /dev/null +++ b/frontends/moa-minifb/src/bin/moa-genesis.rs @@ -0,0 +1,28 @@ + +use std::thread; +use std::sync::Arc; + +use moa::machines::genesis::build_genesis; +use moa_minifb::MiniFrontend; + +fn main() { + /* + let mut frontend = Arc::new(MiniFrontend::init_frontend()); + + { + let frontend = frontend.clone(); + thread::spawn(move || { + let mut system = build_genesis(&*frontend).unwrap(); + system.run_loop(); + }); + } + + frontend.start(); + */ + + let mut frontend = MiniFrontend::init_frontend(); + let mut system = build_genesis(&frontend).unwrap(); + + frontend.start(system); +} + diff --git a/frontends/moa-minifb/src/lib.rs b/frontends/moa-minifb/src/lib.rs new file mode 100644 index 0000000..63a49e2 --- /dev/null +++ b/frontends/moa-minifb/src/lib.rs @@ -0,0 +1,68 @@ + +use std::time::Duration; +use std::sync::{Arc, Mutex}; + +use minifb::{self, Key}; + +use moa::error::Error; +use moa::system::System; +use moa::host::traits::{Host, WindowUpdater}; + + +const WIDTH: usize = 640; +const HEIGHT: usize = 360; + +pub struct MiniFrontend { + pub buffer: Mutex>, + pub updater: Mutex>>, +} + +impl Host for MiniFrontend { + fn add_window(&self, updater: Box) -> Result<(), Error> { + let mut unlocked = self.updater.lock().unwrap(); + if unlocked.is_some() { + return Err(Error::new("A window updater has already been registered with the frontend")); + } + *unlocked = Some(updater); + Ok(()) + } +} + +impl MiniFrontend { + pub fn init_frontend() -> MiniFrontend { + MiniFrontend { + buffer: Mutex::new(vec![0; WIDTH * HEIGHT]), + updater: Mutex::new(None), + } + } + + //pub fn start(&self) { + pub fn start(&self, mut system: System) { + let mut window = minifb::Window::new( + "Test - ESC to exit", + WIDTH, + HEIGHT, + minifb::WindowOptions::default(), + ) + .unwrap_or_else(|e| { + panic!("{}", e); + }); + + // Limit to max ~60 fps update rate + window.limit_update_rate(Some(Duration::from_micros(16600))); + + while window.is_open() && !window.is_key_down(Key::Escape) { + system.run_for(16_600_000); + + match &mut *self.updater.lock().unwrap() { + Some(updater) => { + let mut buffer = self.buffer.lock().unwrap(); + updater.update_frame(WIDTH as u32, HEIGHT as u32, &mut buffer); + window.update_with_buffer(&buffer, WIDTH, HEIGHT).unwrap(); + }, + None => { } + } + } + } +} + diff --git a/src/error.rs b/src/error.rs index 9f5b121..f751e33 100644 --- a/src/error.rs +++ b/src/error.rs @@ -39,7 +39,7 @@ pub enum LogLevel { Debug, } -static mut LOG_LEVEL: LogLevel = LogLevel::Debug; +static mut LOG_LEVEL: LogLevel = LogLevel::Warning; pub fn log_level() -> LogLevel { unsafe { LOG_LEVEL } diff --git a/src/host/gfx.rs b/src/host/gfx.rs index f74dbf9..4806bfb 100644 --- a/src/host/gfx.rs +++ b/src/host/gfx.rs @@ -1,7 +1,7 @@ use std::sync::{Arc, Mutex}; -use crate::host::traits::WindowUpdater; +use crate::host::traits::{WindowUpdater, BlitableSurface}; #[derive(Clone)] @@ -11,6 +11,27 @@ pub struct Frame { pub bitmap: Vec, } +impl BlitableSurface for Frame { + fn set_size(&mut self, width: u32, height: u32) { + self.width = width; + self.height = height; + self.bitmap.resize((width * height) as usize, 0); + } + + fn blit>(&mut self, pos_x: u32, mut pos_y: u32, mut bitmap: B, width: u32, height: u32) { + for y in pos_y..(pos_y + height) { + for x in pos_x..(pos_x + width) { + match bitmap.next().unwrap() { + 0 => { }, + value if x < self.width && y < self.height => { self.bitmap[(x + (y * self.width)) as usize] = value; }, + _ => { }, + } + } + } + } +} + + pub struct FrameSwapper { pub current: Frame, pub previous: Frame, @@ -36,16 +57,11 @@ impl FrameSwapper { impl WindowUpdater for FrameSwapper { fn update_frame(&mut self, width: u32, height: u32, bitmap: &mut [u32]) { std::mem::swap(&mut self.current, &mut self.previous); - if self.current.width != width || self.current.height != height { - self.current.width = width; - self.current.height = height; - self.current.bitmap.resize((width * height) as usize, 0); - self.previous = self.current.clone(); - return; - } - for i in 0..(width as usize * height as usize) { - bitmap[i] = self.current.bitmap[i]; + for y in 0..self.current.height { + for x in 0..self.current.width { + bitmap[(x + (y * width)) as usize] = self.current.bitmap[(x + (y * self.current.width)) as usize]; + } } } } diff --git a/src/host/traits.rs b/src/host/traits.rs index b736955..ad4823f 100644 --- a/src/host/traits.rs +++ b/src/host/traits.rs @@ -19,3 +19,8 @@ pub trait WindowUpdater: Send { fn update_frame(&mut self, width: u32, height: u32, bitmap: &mut [u32]); } +pub trait BlitableSurface { + fn set_size(&mut self, width: u32, height: u32); + fn blit>(&mut self, pos_x: u32, pos_y: u32, bitmap: B, width: u32, height: u32); +} + diff --git a/src/machines/genesis.rs b/src/machines/genesis.rs new file mode 100644 index 0000000..a8fad64 --- /dev/null +++ b/src/machines/genesis.rs @@ -0,0 +1,52 @@ + +use crate::error::Error; +use crate::system::System; +use crate::memory::{MemoryBlock, BusPort}; +use crate::devices::{wrap_transmutable, Debuggable}; + +use crate::cpus::m68k::{M68k, M68kType}; +use crate::peripherals::genesis; + +use crate::host::traits::{Host, WindowUpdater}; + + +use std::sync::Arc; +pub fn build_genesis(host: &H) -> Result { + let mut system = System::new(); + + let rom = MemoryBlock::load("binaries/genesis/Sonic The Hedgehog (W) (REV 01) [!].bin").unwrap(); + //let rom = MemoryBlock::load("binaries/genesis/Sonic The Hedgehog (W) (REV 00) [!].bin").unwrap(); + //let rom = MemoryBlock::load("binaries/genesis/Home Alone (beta).bin").unwrap(); + //let rom = MemoryBlock::load("binaries/genesis/Teenage Mutant Ninja Turtles - The Hyperstone Heist (U) [!].bin").unwrap(); + system.add_addressable_device(0x00000000, wrap_transmutable(rom)).unwrap(); + + let ram = MemoryBlock::new(vec![0; 0x00010000]); + system.add_addressable_device(0x00FF0000, wrap_transmutable(ram)).unwrap(); + + + + let coproc_shared_mem = MemoryBlock::new(vec![0; 0x00010000]); + system.add_addressable_device(0x00A00000, wrap_transmutable(coproc_shared_mem)).unwrap(); + + + let controllers = genesis::controllers::GenesisController::new(); + system.add_addressable_device(0x00a10000, wrap_transmutable(controllers)).unwrap(); + + let coproc = genesis::coproc_memory::CoprocessorMemory::new(); + system.add_addressable_device(0x00a11000, wrap_transmutable(coproc)).unwrap(); + + let vdp = genesis::ym7101::Ym7101::new(host); + system.add_addressable_device(0x00c00000, wrap_transmutable(vdp)).unwrap(); + + + let mut cpu = M68k::new(M68kType::MC68000, 7_670_454, BusPort::new(0, 24, 16, system.bus.clone())); + + //cpu.enable_tracing(); + //cpu.add_breakpoint(0x1dd0); // Sonic: some kind of palette fading function + //cpu.decoder.dump_disassembly(&mut system, 0x206, 0x2000); + + system.add_interruptable_device(wrap_transmutable(cpu)).unwrap(); + + Ok(system) +} + diff --git a/src/machines/mod.rs b/src/machines/mod.rs index 5813805..2565f2a 100644 --- a/src/machines/mod.rs +++ b/src/machines/mod.rs @@ -1,3 +1,4 @@ pub mod computie; +pub mod genesis; diff --git a/src/peripherals/genesis/controllers.rs b/src/peripherals/genesis/controllers.rs new file mode 100644 index 0000000..710ae9f --- /dev/null +++ b/src/peripherals/genesis/controllers.rs @@ -0,0 +1,146 @@ + +use crate::error::Error; +use crate::devices::{Address, Addressable, Transmutable, MAX_READ}; + + +const REG_VERSION: Address = 0x01; +const REG_DATA1: Address = 0x03; +const REG_DATA2: Address = 0x05; +const REG_DATA3: Address = 0x07; +const REG_CTRL1: Address = 0x09; +const REG_CTRL2: Address = 0x0B; +const REG_CTRL3: Address = 0x0D; +const REG_S_CTRL1: Address = 0x13; +const REG_S_CTRL2: Address = 0x19; +const REG_S_CTRL3: Address = 0x1F; + + +const DEV_NAME: &'static str = "genesis_controller"; + +pub struct GenesisControllerPort { + /// Data contains bits: + /// 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + /// X | Y | Z | MODE | START | A | B | C | RIGHT | LEFT | DOWN | UP + pub data: u16, + + pub ctrl: u8, + pub th_count: u8, + pub next_read: u8, + + pub s_ctrl: u8, +} + +impl GenesisControllerPort { + pub fn new() -> Self { + Self { + data: 0, + ctrl: 0, + th_count: 0, + next_read: 0, + s_ctrl: 0, + } + } + + pub fn set_data(&mut self, data: u8) { + let prev_th = self.next_read & 0x40; + self.next_read = data & self.ctrl; + + if (self.next_read ^ prev_th) != 0 { + // TH bit was toggled + self.th_count += 1; + self.next_read = match self.th_count { + 0 => self.next_read | ((self.data & 0x003F) as u8), + 1 => self.next_read | (((self.data & 0x00C0) >> 2) as u8) | ((self.data & 0x0003) as u8), + 2 => self.next_read | ((self.data & 0x003F) as u8), + 3 => self.next_read | (((self.data & 0x00C0) >> 2) as u8), + 4 => self.next_read | ((self.data & 0x0030) as u8) | (((self.data & 0x0F00) >> 8) as u8), + 5 => self.next_read | (((self.data & 0x00C0) >> 2) as u8) | 0x0F, + 6 => self.next_read | ((self.data & 0x003F) as u8), + 7 => { + self.th_count = 0; + self.next_read | (((self.data & 0x00C0) >> 2) as u8) | ((self.data & 0x0003) as u8) + }, + _ => { + self.th_count = 0; + 0 + }, + }; + } + } +} + + + +pub struct GenesisController { + pub port_1: GenesisControllerPort, + pub port_2: GenesisControllerPort, + pub expansion: GenesisControllerPort, +} + +impl GenesisController { + pub fn new() -> Self { + GenesisController { + port_1: GenesisControllerPort::new(), + port_2: GenesisControllerPort::new(), + expansion: GenesisControllerPort::new(), + } + } +} + +impl Addressable for GenesisController { + fn len(&self) -> usize { + 0x30 + } + + fn read(&mut self, mut addr: Address, data: &mut [u8]) -> Result<(), Error> { + // If the address is even, only the second byte (odd byte) will be meaningful + let mut i = 0; + if (addr % 2) == 0 { + addr += 1; + i += 1; + } + + match addr { + REG_VERSION => { data[i] = 0xA0; } // Overseas Version, NTSC, No Expansion + REG_DATA1 => { data[i] = self.port_1.next_read; }, + REG_DATA2 => { data[i] = self.port_2.next_read; }, + REG_DATA3 => { data[i] = self.expansion.next_read; }, + REG_CTRL1 => { data[i] = self.port_1.ctrl; }, + REG_CTRL2 => { data[i] = self.port_2.ctrl; }, + REG_CTRL3 => { data[i] = self.expansion.ctrl; }, + REG_S_CTRL1 => { data[i] = self.port_1.s_ctrl | 0x02; }, + REG_S_CTRL2 => { data[i] = self.port_2.s_ctrl | 0x02; }, + REG_S_CTRL3 => { data[i] = self.expansion.s_ctrl | 0x02; }, + _ => { warning!("{}: !!! unhandled reading from {:0x}", DEV_NAME, addr); }, + } + info!("{}: read from register {:x} the value {:x}", DEV_NAME, addr, data[0]); + Ok(()) + } + + fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> { + info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); + match addr { + REG_DATA1 => { self.port_1.set_data(data[0]); } + REG_DATA2 => { self.port_2.set_data(data[0]); }, + REG_DATA3 => { self.expansion.set_data(data[0]); }, + REG_CTRL1 => { self.port_1.ctrl = data[0]; }, + REG_CTRL2 => { self.port_2.ctrl = data[0]; }, + REG_CTRL3 => { self.expansion.ctrl = data[0]; }, + REG_S_CTRL1 => { self.port_1.s_ctrl = data[0] & 0xF8; }, + REG_S_CTRL2 => { self.port_2.s_ctrl = data[0] & 0xF8; }, + REG_S_CTRL3 => { self.expansion.s_ctrl = data[0] & 0xF8; }, + _ => { warning!("{}: !!! unhandled write of {:0x} to {:0x}", DEV_NAME, data[0], addr); }, + } + Ok(()) + } +} + +// TODO make a step function to reset the TH count after 1.5ms + +impl Transmutable for GenesisController { + fn as_addressable(&mut self) -> Option<&mut dyn Addressable> { + Some(self) + } +} + + diff --git a/src/peripherals/genesis/coproc_memory.rs b/src/peripherals/genesis/coproc_memory.rs new file mode 100644 index 0000000..1227b93 --- /dev/null +++ b/src/peripherals/genesis/coproc_memory.rs @@ -0,0 +1,69 @@ + +use crate::error::Error; +use crate::devices::{Address, Addressable, Transmutable, MAX_READ}; + + +const DEV_NAME: &'static str = "coprocessor"; + +pub struct CoprocessorMemory { + pub bus_request: bool, + pub reset: bool, +} + + +impl CoprocessorMemory { + pub fn new() -> Self { + CoprocessorMemory { + bus_request: false, + reset: false, + } + } +} + +impl Addressable for CoprocessorMemory { + fn len(&self) -> usize { + 0x4000 + } + + fn read(&mut self, addr: Address, data: &mut [u8]) -> Result<(), Error> { + match addr { + 0x100 => { + data[0] = if self.bus_request && self.reset { 0x01 } else { 0x00 }; + }, + _ => { warning!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr); }, + } + info!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); + Ok(()) + } + + fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> { + info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); + match addr { + 0x000 => { /* ROM vs DRAM mode */ }, + 0x100 => { + if data[0] != 0 { + self.bus_request = true; + } else { + self.bus_request = false; + } + }, + 0x200 => { + if data[0] != 0 { + self.reset = false; + } else { + self.reset = true; + } + }, + _ => { warning!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); }, + } + Ok(()) + } +} + +impl Transmutable for CoprocessorMemory { + fn as_addressable(&mut self) -> Option<&mut dyn Addressable> { + Some(self) + } +} + + diff --git a/src/peripherals/genesis/mod.rs b/src/peripherals/genesis/mod.rs new file mode 100644 index 0000000..410319e --- /dev/null +++ b/src/peripherals/genesis/mod.rs @@ -0,0 +1,5 @@ + +pub mod ym7101; +pub mod controllers; +pub mod coproc_memory; + diff --git a/src/peripherals/genesis/ym7101.rs b/src/peripherals/genesis/ym7101.rs index 4ccd5a2..7a3fdf9 100644 --- a/src/peripherals/genesis/ym7101.rs +++ b/src/peripherals/genesis/ym7101.rs @@ -122,7 +122,7 @@ impl Ym7101State { fn set_register(&mut self, data: u16) { let reg = (data & 0x1F00) >> 8; self.regs[reg as usize] = (data & 0x00FF) as u8; - debug!("{}: register {:x} set to {:x}", DEV_NAME, reg, self.regs[reg as usize]); + info!("{}: register {:x} set to {:x}", DEV_NAME, reg, self.regs[reg as usize]); } pub fn set_dma_mode(&mut self, mode: DmaType) { @@ -154,7 +154,7 @@ impl Ym7101State { (((self.regs[REG_DMA_COUNTER_HIGH] as u32) << 8) | (self.regs[REG_DMA_COUNTER_LOW] as u32)) as i32 } - pub fn setup_dma_transfer(&mut self, upper: u16, lower: u16) { + pub fn setup_transfer(&mut self, upper: u16, lower: u16) { self.transfer_upper = None; self.transfer_type = ((((upper & 0xC000) >> 14) | ((lower & 0x00F0) >> 2))) as u8; self.transfer_addr = ((upper & 0x3FFF) | ((lower & 0x0003) << 14)) as u32; @@ -163,7 +163,7 @@ impl Ym7101State { 4 => TargetType::Vsram, _ => TargetType::Cram, }; - debug!("{}: transfer requested of type {:x} ({:?}) to address {:x}", DEV_NAME, self.transfer_type, self.transfer_target, self.transfer_addr); + info!("{}: transfer requested of type {:x} ({:?}) to address {:x}", DEV_NAME, self.transfer_type, self.transfer_target, self.transfer_addr); if (self.transfer_type & 0x20) != 0 { if (self.transfer_type & 0x10) != 0 { self.set_dma_mode(DmaType::Copy); @@ -204,14 +204,19 @@ impl Ym7101State { } pub fn get_palette_colour(&self, palette: u8, colour: u8) -> u32 { - let rgb = read_beu16(&self.cram[((palette * 16) + colour) as usize..]); + if colour == 0 { + return 0; + } + let rgb = read_beu16(&self.cram[(((palette * 16) + colour) * 2) as usize..]); (((rgb & 0xF00) as u32) >> 4) | (((rgb & 0x0F0) as u32) << 8) | (((rgb & 0x00F) as u32) << 20) } pub fn get_pattern_iter<'a>(&'a self, pattern_name: u16) -> PatternIterator<'a> { let pattern_addr = (pattern_name & 0x07FF) << 5; let pattern_palette = ((pattern_name & 0x6000) >> 13) as u8; - PatternIterator::new(&self, pattern_addr as u32, pattern_palette) + let h_rev = (pattern_name & 0x0800) != 0; + let v_rev = (pattern_name & 0x1000) != 0; + PatternIterator::new(&self, pattern_addr as u32, pattern_palette, h_rev, v_rev) } pub fn get_scroll_size(&self) -> (u16, u16) { @@ -243,10 +248,10 @@ impl Ym7101State { frame.bitmap[i] = bg_colour; } - //self.draw_cell_table(frame, self.get_vram_scroll_b_addr(), coords, scroll_size); + self.draw_cell_table(frame, self.get_vram_scroll_b_addr(), coords, scroll_size); self.draw_cell_table(frame, self.get_vram_scroll_a_addr(), coords, scroll_size); - //self.draw_cell_table(frame, self.get_vram_window_addr(), coords, scroll_size); - //self.draw_sprites(frame, (self.regs[REG_SPRITES_ADDR] as u32) << 9, coords, scroll_size); + self.draw_cell_table(frame, self.get_vram_window_addr(), coords, scroll_size); + self.draw_sprites(frame, (self.regs[REG_SPRITES_ADDR] as u32) << 9, coords, scroll_size); } pub fn draw_cell_table(&mut self, frame: &mut Frame, cell_table: u32, coords: ((u16, u16), (u16, u16)), scroll_size: (u16, u16)) { @@ -289,16 +294,24 @@ fn scroll_size(size: u8) -> u16 { pub struct PatternIterator<'a> { state: &'a Ym7101State, palette: u8, - next: usize, + base: usize, + h_rev: bool, + v_rev: bool, + line: i8, + col: i8, second: bool, } impl<'a> PatternIterator<'a> { - pub fn new(state: &'a Ym7101State, start: u32, palette: u8) -> Self { + pub fn new(state: &'a Ym7101State, start: u32, palette: u8, h_rev: bool, v_rev: bool) -> Self { Self { state, palette, - next: start as usize, + base: start as usize, + h_rev, + v_rev, + line: 0, + col: 0, second: false, } } @@ -308,19 +321,25 @@ impl<'a> Iterator for PatternIterator<'a> { type Item = u32; fn next(&mut self) -> Option { - //if self.next >= 32 { - // None - //} - if !self.second { - let value = self.state.get_palette_colour(self.palette, self.state.vram[self.next] >> 4); - self.second = true; - Some(value) + let offset = self.base + (if !self.v_rev { self.line } else { 7 - self.line }) as usize * 4 + (if !self.h_rev { self.col } else { 3 - self.col }) as usize; + let value = if (!self.h_rev && !self.second) || (self.h_rev && self.second) { + self.state.get_palette_colour(self.palette, self.state.vram[offset] >> 4) + } else { + self.state.get_palette_colour(self.palette, self.state.vram[offset] & 0x0f) + }; + + if !self.second { + self.second = true; } else { - let value = self.state.get_palette_colour(self.palette, self.state.vram[self.next] & 0x0f); - self.next += 1; self.second = false; - Some(value) + self.col += 1; + if self.col >= 4 { + self.col = 0; + self.line += 1; + } } + + Some(value) } } @@ -369,18 +388,26 @@ impl Steppable for Ym7101 { } self.state.h_clock += diff; + if (self.state.status & STATUS_IN_HBLANK) == 0 && self.state.v_clock > 58_820 { + self.state.status |= STATUS_IN_HBLANK; + } if self.state.h_clock > 63_500 { + self.state.status &= !STATUS_IN_HBLANK; self.state.h_clock = 0; - self.state.h_scanlines = self.state.h_scanlines.wrapping_add(1); - if self.state.hsync_int_enabled() && self.state.h_scanlines >= self.state.regs[REG_H_INTERRUPT] { - self.state.h_scanlines = 0; + self.state.h_scanlines = self.state.h_scanlines.wrapping_sub(1); + if self.state.hsync_int_enabled() && self.state.h_scanlines == 0 { + self.state.h_scanlines = self.state.regs[REG_H_INTERRUPT]; system.get_interrupt_controller().set(true, 4, 28)?; self.state.reset_int = true; } } self.state.v_clock += diff; + if (self.state.status & STATUS_IN_VBLANK) == 0 && self.state.v_clock > 14_218_000 { + self.state.status |= STATUS_IN_VBLANK; + } if self.state.v_clock > 16_630_000 { + self.state.status &= !STATUS_IN_VBLANK; self.state.v_clock = 0; if self.state.vsync_int_enabled() { system.get_interrupt_controller().set(true, 6, 30)?; @@ -390,6 +417,10 @@ impl Steppable for Ym7101 { let mut swapper = self.swapper.lock().unwrap(); self.state.draw_frame(&mut swapper.current); + //let mut swapper = self.swapper.lock().unwrap(); + //let iter = PatternIterator::new(&self.state, 0x260, 0, true, true); + //swapper.current.blit(0, 0, iter, 8, 8); + /* // Print Palette for i in 0..16 { @@ -420,10 +451,18 @@ impl Steppable for Ym7101 { let mut src_addr = self.state.get_dma_src_addr(); let mut count = self.state.get_dma_count(); - debug!("{}: starting dma transfer {:x} from Mem:{:x} to {:?}:{:x} ({} bytes)", DEV_NAME, self.state.transfer_type, src_addr, self.state.transfer_target, self.state.transfer_addr, count); + info!("{}: starting dma transfer {:x} from Mem:{:x} to {:?}:{:x} ({} bytes)", DEV_NAME, self.state.transfer_type, src_addr, self.state.transfer_target, self.state.transfer_addr, count); let mut bus = system.get_bus(); + + // TODO temporary for debugging, will break at the first cram transfer after the display is on + //if (self.state.regs[REG_MODE_SET_2] & 0x40) != 0 && self.state.transfer_target == TargetType::Cram { + // system.get_interrupt_controller().target.as_ref().map(|cpu| cpu.borrow_mut().as_debuggable().unwrap().enable_debugging()); + //} + + //bus.dump_memory(src_addr as Address, count as Address); while count > 0 { - let data = bus.read(src_addr as Address, 2)?; + let mut data = [0; 2]; + bus.read(src_addr as Address, &mut data)?; { let addr = self.state.transfer_addr; @@ -441,7 +480,7 @@ impl Steppable for Ym7101 { let mut src_addr = self.state.get_dma_src_addr(); let mut count = self.state.get_dma_count(); - debug!("{}: starting dma copy from VRAM:{:x} to VRAM:{:x} ({} bytes)", DEV_NAME, src_addr, self.state.transfer_addr, count); + info!("{}: starting dma copy from VRAM:{:x} to VRAM:{:x} ({} bytes)", DEV_NAME, src_addr, self.state.transfer_addr, count); while count > 0 { self.state.vram[self.state.transfer_addr as usize] = self.state.vram[src_addr as usize]; self.state.transfer_addr += self.state.regs[REG_AUTO_INCREMENT] as u32; @@ -452,7 +491,7 @@ impl Steppable for Ym7101 { DmaType::Fill => { let mut count = self.state.get_dma_count(); - debug!("{}: starting dma fill to VRAM:{:x} ({} bytes) with {:x}", DEV_NAME, self.state.transfer_addr, count, self.state.transfer_fill); + info!("{}: starting dma fill to VRAM:{:x} ({} bytes) with {:x}", DEV_NAME, self.state.transfer_addr, count, self.state.transfer_fill); while count > 0 { self.state.vram[self.state.transfer_addr as usize] = self.state.transfer_fill as u8; self.state.transfer_addr += self.state.regs[REG_AUTO_INCREMENT] as u32; @@ -474,21 +513,19 @@ impl Addressable for Ym7101 { 0x20 } - fn read(&mut self, addr: Address, count: usize) -> Result<[u8; MAX_READ], Error> { - let mut data = [0; MAX_READ]; - + fn read(&mut self, addr: Address, data: &mut [u8]) -> Result<(), Error> { match addr { // Read from Data Port - 0x00 => { + 0x00 | 0x02 => { { let addr = self.state.transfer_addr; let target = self.state.get_transfer_target_mut(); - for i in 0..count { + for i in 0..data.len() { data[i] = target[addr as usize + i]; } } self.state.transfer_addr += self.state.regs[REG_AUTO_INCREMENT] as u32; - debug!("{}: data port read {} bytes from {:?}:{:x} returning {:x},{:x}", DEV_NAME, count, self.state.transfer_target, addr, data[0], data[1]); + info!("{}: data port read {} bytes from {:?}:{:x} returning {:x},{:x}", DEV_NAME, data.len(), self.state.transfer_target, addr, data[0], data[1]); }, // Read from Control Port @@ -500,20 +537,20 @@ impl Addressable for Ym7101 { _ => { println!("{}: !!! unhandled read from {:x}", DEV_NAME, addr); }, } - Ok(data) + Ok(()) } fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> { match addr { // Write to Data Port - 0x00 => { + 0x00 | 0x02 => { if (self.state.transfer_type & 0x30) == 0x20 { self.state.transfer_upper = None; - // TODO this caused problems, might not correct - //self.state.transfer_fill = read_beu16(data); - self.state.transfer_fill = (data[0] as u16) << 8 | (data[1] as u16); + self.state.transfer_fill = read_beu16(data); self.state.set_dma_mode(DmaType::Fill); } else { + info!("{}: data port write {} bytes to {:?}:{:x} with {:?}", DEV_NAME, data.len(), self.state.transfer_target, self.state.transfer_addr, data); + { let addr = self.state.transfer_addr as usize; let target = self.state.get_transfer_target_mut(); @@ -522,13 +559,13 @@ impl Addressable for Ym7101 { } } self.state.transfer_addr += self.state.regs[REG_AUTO_INCREMENT] as u32; - debug!("{}: data port write {} bytes to {:?}:{:x} with {:?}", DEV_NAME, data.len(), self.state.transfer_target, addr, data); } }, // Write to Control Port 0x04 | 0x06 => { - //debug!("{}: write {} bytes to port {:x} with data {:?}", DEV_NAME, data.len(), addr, data); + debug!("{}: write {} bytes to port {:x} with data {:?}", DEV_NAME, data.len(), addr, data); + let value = read_beu16(data); if (value & 0xC000) == 0x8000 { self.state.set_register(value); @@ -542,8 +579,8 @@ impl Addressable for Ym7101 { } else { match (data.len(), self.state.transfer_upper) { (2, None) => { self.state.transfer_upper = Some(value) }, - (2, Some(upper)) => self.state.setup_dma_transfer(upper, read_beu16(data)), - (4, None) => self.state.setup_dma_transfer(value, read_beu16(&data[2..])), + (2, Some(upper)) => self.state.setup_transfer(upper, read_beu16(data)), + (4, None) => self.state.setup_transfer(value, read_beu16(&data[2..])), _ => { error!("{}: !!! error when writing to control port with {} bytes of {:?}", DEV_NAME, data.len(), data); }, } } diff --git a/src/peripherals/mod.rs b/src/peripherals/mod.rs index 6246695..3d5cc3c 100644 --- a/src/peripherals/mod.rs +++ b/src/peripherals/mod.rs @@ -1,4 +1,5 @@ pub mod ata; pub mod mc68681; +pub mod genesis; diff --git a/todo.txt b/todo.txt index 9fe0df5..cecc2d4 100644 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,7 @@ +* modify the interrupt handling to make the interrupted device acknowledge the interrupt, probably via the interrupt controller somehow. It might need to be async + such that the cpu tells the int_controller it's acknowledged, and then the interrupting device can check the interrupt controller if it needs to see the ack + * each device that can make a bus request should have a BusPort which is used to access the bus. Not sure how it'll be created or passed to the device, since the offset should be set by the builder or system, and the mask and data size should be sent by the CPU (although I suppose some systems could hook it up differently) * what about even vs odd accesses? If you access a byte, should the bus port possible turn it into a word access, and return only the byte portion? @@ -9,6 +12,7 @@ * make devices nameable, using a hashmap to store them * can you eventually make the system connections all configurable via a config file? +* you could modify read/write to return the number of bytes read or written for dynamic bus sizing used by the MC68020+ * make tests for each instruction