use std::iter::Iterator; use std::sync::{Arc, Mutex}; use crate::error::Error; use crate::system::System; use crate::memory::dump_slice; use crate::devices::{Clock, ClockElapsed, Address, Addressable, Steppable, Inspectable, Transmutable, read_beu16, read_beu32, write_beu16}; use crate::host::traits::{Host, BlitableSurface, SharedData}; use crate::host::gfx::{Frame, FrameSwapper}; const REG_MODE_SET_1: usize = 0x00; const REG_MODE_SET_2: usize = 0x01; const REG_SCROLL_A_ADDR: usize = 0x02; const REG_WINDOW_ADDR: usize = 0x03; const REG_SCROLL_B_ADDR: usize = 0x04; const REG_SPRITES_ADDR: usize = 0x05; // Register 0x06 Unused const REG_BACKGROUND: usize = 0x07; // Register 0x08 Unused // Register 0x09 Unused const REG_H_INTERRUPT: usize = 0x0A; const REG_MODE_SET_3: usize = 0x0B; const REG_MODE_SET_4: usize = 0x0C; const REG_HSCROLL_ADDR: usize = 0x0D; // Register 0x0E Unused const REG_AUTO_INCREMENT: usize = 0x0F; const REG_SCROLL_SIZE: usize = 0x10; const REG_WINDOW_H_POS: usize = 0x11; const REG_WINDOW_V_POS: usize = 0x12; const REG_DMA_COUNTER_LOW: usize = 0x13; const REG_DMA_COUNTER_HIGH: usize = 0x14; const REG_DMA_ADDR_LOW: usize = 0x15; const REG_DMA_ADDR_MID: usize = 0x16; const REG_DMA_ADDR_HIGH: usize = 0x17; const STATUS_PAL_MODE: u16 = 0x0001; const STATUS_DMA_BUSY: u16 = 0x0002; const STATUS_IN_HBLANK: u16 = 0x0004; const STATUS_IN_VBLANK: u16 = 0x0008; const STATUS_ODD_FRAME: u16 = 0x0010; const STATUS_SPRITE_COLLISION: u16 = 0x0020; const STATUS_SPRITE_OVERFLOW: u16 = 0x0040; const STATUS_V_INTERRUPT: u16 = 0x0080; const STATUS_FIFO_FULL: u16 = 0x0100; const STATUS_FIFO_EMPTY: u16 = 0x0200; const MODE1_BF_ENABLE_HV_COUNTER: u8 = 0x02; const MODE1_BF_HSYNC_INTERRUPT: u8 = 0x10; const MODE2_BF_V_CELL_MODE: u8 = 0x08; const MODE2_BF_DMA_ENABLED: u8 = 0x10; const MODE2_BF_VSYNC_INTERRUPT: u8 = 0x20; const MODE3_BF_EXTERNAL_INTERRUPT: u8 = 0x08; const MODE3_BF_V_SCROLL_MODE: u8 = 0x04; const MODE3_BF_H_SCROLL_MODE: u8 = 0x03; const MODE4_BF_H_CELL_MODE: u8 = 0x01; const MODE4_BF_SHADOW_HIGHLIGHT: u8 = 0x08; const DEV_NAME: &'static str = "ym7101"; #[derive(Copy, Clone, Debug, PartialEq)] pub enum DmaType { None, Memory, Fill, Copy, } #[derive(Copy, Clone, Debug, PartialEq)] pub enum TargetType { Vram, Cram, Vsram, } pub struct Ym7101State { pub status: u16, pub vram: [u8; 0x10000], pub cram: [u8; 128], pub vsram: [u8; 80], pub mode_1: u8, pub mode_2: u8, pub mode_3: u8, pub mode_4: u8, pub window_pos: (u8, u8), pub h_int_lines: u8, pub screen_size: (usize, usize), pub scroll_size: (usize, usize), pub window_offset: (usize, usize), pub background: u8, pub scroll_a_addr: usize, pub scroll_b_addr: usize, pub window_addr: usize, pub sprites_addr: usize, pub hscroll_addr: usize, pub transfer_type: u8, pub transfer_bits: u8, pub transfer_count: u32, pub transfer_remain: u32, pub transfer_src_addr: u32, pub transfer_dest_addr: u32, pub transfer_auto_inc: u32, pub transfer_fill_word: u16, pub transfer_run: DmaType, pub transfer_target: TargetType, pub ctrl_port_buffer: Option, pub last_clock: Clock, pub h_clock: u32, pub v_clock: u32, pub h_scanlines: u8, } impl Ym7101State { pub fn new() -> Self { Self { status: 0x3400 | STATUS_FIFO_EMPTY, vram: [0; 0x10000], cram: [0; 128], vsram: [0; 80], mode_1: 0, mode_2: 0, mode_3: 0, mode_4: 0, window_pos: (0, 0), h_int_lines: 0, screen_size: (0, 0), scroll_size: (0, 0), window_offset: (0, 0), background: 0, scroll_a_addr: 0, scroll_b_addr: 0, window_addr: 0, sprites_addr: 0, hscroll_addr: 0, transfer_type: 0, transfer_bits: 0, transfer_count: 0, transfer_remain: 0, transfer_src_addr: 0, transfer_dest_addr: 0, transfer_auto_inc: 0, transfer_fill_word: 0, transfer_run: DmaType::None, transfer_target: TargetType::Vram, ctrl_port_buffer: None, last_clock: 0, h_clock: 0, v_clock: 0, h_scanlines: 0, } } fn set_register(&mut self, word: u16) { let reg = ((word & 0x1F00) >> 8) as usize; let data = (word & 0x00FF) as u8; info!("{}: register {:x} set to {:x}", DEV_NAME, reg, data); self.update_register_value(reg, data); } fn update_register_value(&mut self, reg: usize, data: u8) { match reg { REG_MODE_SET_1 => { self.mode_1 = data; }, REG_MODE_SET_2 => { self.mode_2 = data; self.update_screen_size(); }, REG_SCROLL_A_ADDR => { self.scroll_a_addr = (data as usize) << 10; }, REG_WINDOW_ADDR => { self.window_addr = (data as usize) << 10; }, REG_SCROLL_B_ADDR => { self.scroll_b_addr = (data as usize) << 13; }, REG_SPRITES_ADDR => { self.sprites_addr = (data as usize) << 9; }, REG_BACKGROUND => { self.background = data; }, REG_H_INTERRUPT => { self.h_int_lines = data; }, REG_MODE_SET_3 => { self.mode_3 = data; }, REG_MODE_SET_4 => { self.mode_4 = data; self.update_screen_size(); }, REG_HSCROLL_ADDR => { self.hscroll_addr = (data as usize) << 10; }, REG_AUTO_INCREMENT => { self.transfer_auto_inc = data as u32; }, REG_SCROLL_SIZE => { let h = decode_scroll_size(data & 0x03); let v = decode_scroll_size((data >> 4) & 0x03); self.scroll_size = (h, v); }, REG_WINDOW_H_POS => { self.window_pos.0 = data; self.update_window_offset(); }, REG_WINDOW_V_POS => { self.window_pos.1 = data; self.update_window_offset(); }, REG_DMA_COUNTER_LOW => { self.transfer_count = (self.transfer_count & 0xFF00) | data as u32; self.transfer_remain = self.transfer_count; }, REG_DMA_COUNTER_HIGH => { self.transfer_count = (self.transfer_count & 0x00FF) | ((data as u32) << 8); self.transfer_remain = self.transfer_count; }, REG_DMA_ADDR_LOW => { self.transfer_src_addr = (self.transfer_src_addr & 0xFFFE00) | ((data as u32) << 1); }, REG_DMA_ADDR_MID => { self.transfer_src_addr = (self.transfer_src_addr & 0xFE01FF) | ((data as u32) << 9); }, REG_DMA_ADDR_HIGH => { let mask = if (data & 0x80) == 0 { 0x7F } else { 0x3F }; self.transfer_bits = data & 0xC0; self.transfer_src_addr = (self.transfer_src_addr & 0x01FFFF) | (((data & mask) as u32) << 17); }, 0x6 | 0x8 | 0x9 | 0xE => { /* Reserved */ }, _ => { panic!("{}: unknown register: {:?}", DEV_NAME, reg); }, } } pub fn update_screen_size(&mut self) { let h_cells = if (self.mode_4 & MODE4_BF_H_CELL_MODE) == 0 { 32 } else { 40 }; let v_cells = if (self.mode_2 & MODE2_BF_V_CELL_MODE) == 0 { 28 } else { 30 }; self.screen_size = (h_cells, v_cells); } pub fn update_window_offset(&mut self) { let win_h = ((self.window_pos.0 & 0x1F) << 1) as usize; let win_v = (self.window_pos.1 & 0x1F) as usize; let right = (self.window_pos.0 & 0x80) != 0; let down = (self.window_pos.1 & 0x80) != 0; self.window_offset = match (right, down) { (false, false) => (win_h, win_v), (true, false) => (win_h - self.screen_size.0, win_v), (false, true) => (win_h, win_v - self.screen_size.1), (true, true) => (win_h - self.screen_size.0, win_v - self.screen_size.1), } } pub fn set_dma_mode(&mut self, mode: DmaType) { match mode { DmaType::None => { self.status &= !STATUS_DMA_BUSY; self.transfer_run = DmaType::None; }, _ => { self.status |= STATUS_DMA_BUSY; self.transfer_run = mode; }, } } pub fn setup_transfer(&mut self, upper: u16, lower: u16) { self.ctrl_port_buffer = None; self.transfer_type = ((((upper & 0xC000) >> 14) | ((lower & 0x00F0) >> 2))) as u8; self.transfer_dest_addr = ((upper & 0x3FFF) | ((lower & 0x0003) << 14)) as u32; self.transfer_target = match self.transfer_type & 0x0E { 0 => TargetType::Vram, 4 => TargetType::Vsram, _ => TargetType::Cram, }; info!("{}: transfer requested of type {:x} ({:?}) to address {:x}", DEV_NAME, self.transfer_type, self.transfer_target, self.transfer_dest_addr); if (self.transfer_type & 0x20) != 0 { if (self.transfer_type & 0x10) != 0 { self.set_dma_mode(DmaType::Copy); } else if (self.transfer_bits & 0x80) == 0 { self.set_dma_mode(DmaType::Memory); } } } pub fn get_transfer_target_mut(&mut self) -> (&mut [u8], usize) { match self.transfer_target { TargetType::Vram => (&mut self.vram, 0x10000), TargetType::Cram => (&mut self.cram, 128), TargetType::Vsram => (&mut self.vsram, 80), } } #[inline(always)] fn hsync_int_enabled(&self) -> bool { (self.mode_1 & MODE1_BF_HSYNC_INTERRUPT) != 0 } #[inline(always)] fn vsync_int_enabled(&self) -> bool { (self.mode_2 & MODE2_BF_VSYNC_INTERRUPT) != 0 } #[inline(always)] fn external_int_enabled(&self) -> bool { (self.mode_3 & MODE3_BF_EXTERNAL_INTERRUPT) != 0 } pub fn get_palette_colour(&self, palette: u8, colour: u8) -> u32 { if colour == 0 { return 0xFFFFFFFF; } 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; 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_hscroll(&self, hcell: usize) -> (u32, u32) { let base_addr = match self.mode_3 & MODE3_BF_H_SCROLL_MODE { 0 => self.hscroll_addr, 2 => self.hscroll_addr + (hcell << 4), 3 => self.hscroll_addr + (hcell << 4), _ => panic!("Unsupported horizontal scroll mode"), }; let scroll_a = read_beu16(&self.vram[base_addr..]) as u32 & 0x3FF; let scroll_b = read_beu16(&self.vram[base_addr + 2..]) as u32 & 0x3FF; (scroll_a, scroll_b) } pub fn get_vscroll(&self, vcell: usize) -> (u32, u32) { let base_addr = if (self.mode_3 & MODE3_BF_V_SCROLL_MODE) == 0 { 0 } else { vcell >> 1 }; let scroll_a = read_beu16(&self.vsram[base_addr..]) as u32 & 0x3FF; let scroll_b = read_beu16(&self.vsram[base_addr + 2..]) as u32 & 0x3FF; (scroll_a, scroll_b) } #[inline(always)] pub fn get_pattern_addr(&self, cell_table: usize, cell_x: usize, cell_y: usize) -> usize { cell_table + ((cell_x + (cell_y * self.scroll_size.0 as usize)) << 1) } pub fn get_scroll_a_pattern(&self, cell_x: usize, cell_y: usize, hscrolling_a: usize, vscrolling_a: usize) -> u16 { let pattern_x = ((cell_x + self.window_offset.0) as usize - (hscrolling_a / 8) as usize) % self.scroll_size.0 as usize; let pattern_y = ((cell_y + self.window_offset.1) as usize + (vscrolling_a / 8) as usize) % self.scroll_size.1 as usize; let pattern_addr = self.get_pattern_addr(self.scroll_a_addr, pattern_x, pattern_y); read_beu16(&self.vram[pattern_addr..]) } pub fn get_scroll_b_pattern(&self, cell_x: usize, cell_y: usize, hscrolling_b: usize, vscrolling_b: usize) -> u16 { let pattern_x = ((cell_x + self.window_offset.0) as usize - (hscrolling_b / 8) as usize) % self.scroll_size.0 as usize; let pattern_y = ((cell_y + self.window_offset.1) as usize + (vscrolling_b / 8) as usize) % self.scroll_size.1 as usize; let pattern_addr = self.get_pattern_addr(self.scroll_b_addr, pattern_x, pattern_y); read_beu16(&self.vram[pattern_addr..]) } pub fn draw_pattern(&mut self, frame: &mut Frame, pattern: u16, pixel_x: u32, pixel_y: u32) { let iter = self.get_pattern_iter(pattern); frame.blit(pixel_x, pixel_y, iter, 8, 8); } pub fn draw_frame(&mut self, frame: &mut Frame) { self.draw_background(frame); self.draw_scrolls(frame); //self.draw_window(frame); self.draw_sprites(frame); } pub fn draw_background(&mut self, frame: &mut Frame) { let bg_colour = self.get_palette_colour((self.background & 0x30) >> 4, self.background & 0x0f); frame.clear(bg_colour); } pub fn draw_scrolls(&mut self, frame: &mut Frame) { let (scroll_h, scroll_v) = self.scroll_size; let (cells_h, cells_v) = self.screen_size; let (offset_x, offset_y) = self.window_offset; if scroll_h == 0 || scroll_v == 0 { return; } for cell_y in 0..cells_v { let (vscrolling_a, vscrolling_b) = self.get_vscroll(cell_y); for cell_x in 0..cells_h { let (hscrolling_a, hscrolling_b) = self.get_hscroll(cell_x); let pattern_b = self.get_scroll_b_pattern(cell_x, cell_y, hscrolling_b as usize, vscrolling_b as usize); let pattern_a = self.get_scroll_a_pattern(cell_x, cell_y, hscrolling_a as usize, vscrolling_a as usize); //if (pattern_b & 0x8000) != 0 && (pattern_a & 0x8000) == 0 { self.draw_pattern(frame, pattern_b, (cell_x << 3) as u32, (cell_y << 3) as u32); self.draw_pattern(frame, pattern_a, (cell_x << 3) as u32, (cell_y << 3) as u32); } } } pub fn draw_window(&mut self, frame: &mut Frame) { let cell_table = self.window_addr; let (cells_h, cells_v) = self.screen_size; // A window address of 0 disables the window if cell_table == 0 { return; } for cell_y in 0..cells_v { for cell_x in 0..cells_h { let pattern_w = read_beu16(&self.vram[self.get_pattern_addr(cell_table, cell_x as usize, cell_y as usize)..]); self.draw_pattern(frame, pattern_w, (cell_x << 3) as u32, (cell_y << 3) as u32); } } } pub fn build_link_list(&mut self, sprite_table: usize, links: &mut [usize]) -> usize { links[0] = 0; let mut i = 0; loop { let link = self.vram[sprite_table + (links[i] * 8) + 3]; if link == 0 || link > 80 { break; } i += 1; links[i] = link as usize; } i } pub fn draw_sprites(&mut self, frame: &mut Frame) { let sprite_table = self.sprites_addr; let (cells_h, cells_v) = self.screen_size; let (pos_limit_h, pos_limit_v) = (if cells_h == 32 { 383 } else { 447 }, if cells_v == 28 { 351 } else { 367 }); let mut links = [0; 80]; let lowest = self.build_link_list(sprite_table, &mut links); for i in (0..lowest + 1).rev() { let sprite_data = &self.vram[(sprite_table + (links[i] * 8))..]; let v_pos = read_beu16(&sprite_data[0..]); let size = sprite_data[2]; let pattern_name = read_beu16(&sprite_data[4..]); let h_pos = read_beu16(&sprite_data[6..]); let (size_h, size_v) = (((size >> 2) & 0x03) as u16 + 1, (size & 0x03) as u16 + 1); let h_rev = (pattern_name & 0x0800) != 0; let v_rev = (pattern_name & 0x1000) != 0; //println!("i: {} ({} {}) {:x} ({}, {}) {:x}", i, h_pos, v_pos, size, size_h, size_v, pattern_name); for ih in 0..size_h { for iv in 0..size_v { let (h, v) = (if !h_rev { ih } else { size_h - 1 - ih }, if !v_rev { iv } else { size_v - 1 - iv }); let (x, y) = (h_pos + ih * 8, v_pos + iv * 8); if x > 128 && x < pos_limit_h && y > 128 && y < pos_limit_v { let iter = self.get_pattern_iter(((pattern_name & 0x07FF) + (h * size_v) + v) | (pattern_name & 0xF800)); //println!("{}: ({} {}), {:x}", i, x, y, ((pattern_name & 0x07FF) + (h * size_v) + v)); frame.blit(x as u32 - 128, y as u32 - 128, iter, 8, 8); } } } } } } fn decode_scroll_size(size: u8) -> usize { match size { 0b00 => 32, 0b01 => 64, 0b11 => 128, _ => panic!("{}: invalid scroll size option {:x}", DEV_NAME, size), } } pub struct PatternIterator<'a> { state: &'a Ym7101State, palette: u8, 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, h_rev: bool, v_rev: bool) -> Self { Self { state, palette, base: start as usize, h_rev, v_rev, line: 0, col: 0, second: false, } } } impl<'a> Iterator for PatternIterator<'a> { type Item = u32; fn next(&mut self) -> Option { 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 { self.second = false; self.col += 1; if self.col >= 4 { self.col = 0; self.line += 1; } } Some(value) } } pub struct Ym7101 { pub swapper: Arc>, pub state: Ym7101State, pub external_interrupt: SharedData, } impl Ym7101 { pub fn new(host: &mut H, external_interrupt: SharedData) -> Ym7101 { let swapper = FrameSwapper::new_shared(320, 224); host.add_window(FrameSwapper::to_boxed(swapper.clone())).unwrap(); Ym7101 { swapper, state: Ym7101State::new(), external_interrupt, } } } impl Transmutable for Ym7101 { fn as_addressable(&mut self) -> Option<&mut dyn Addressable> { Some(self) } fn as_steppable(&mut self) -> Option<&mut dyn Steppable> { Some(self) } fn as_inspectable(&mut self) -> Option<&mut dyn Inspectable> { Some(self) } } impl Steppable for Ym7101 { fn step(&mut self, system: &System) -> Result { let diff = (system.clock - self.state.last_clock) as u32; self.state.last_clock = system.clock; if self.state.external_int_enabled() && self.external_interrupt.get() { self.external_interrupt.set(false); system.get_interrupt_controller().set(true, 2, 26)?; } 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_sub(1); if self.state.hsync_int_enabled() && self.state.h_scanlines == 0 { self.state.h_scanlines = self.state.h_int_lines; system.get_interrupt_controller().set(true, 4, 28)?; } } 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)?; } 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 { println!("{:x}", self.state.get_palette_colour(0, i)); } */ /* // Print Pattern Table let mut swapper = self.swapper.lock().unwrap(); let (cells_h, cells_v) = self.state.get_screen_size(); for cell_y in 0..cells_v { for cell_x in 0..cells_h { let pattern_addr = (cell_x + (cell_y * cells_h)) * 32; let iter = PatternIterator::new(&self.state, pattern_addr as u32, 0, false, false); swapper.current.blit((cell_x << 3) as u32, (cell_y << 3) as u32, iter, 8, 8); } } */ /* // Print Sprite let mut swapper = self.swapper.lock().unwrap(); self.state.draw_background(&mut swapper.current); let sprite_table = self.get_vram_sprites_addr(); let (cells_h, cells_v) = self.state.get_screen_size(); let sprite = 0; println!("{:?}", &self.state.vram[(sprite_table + (sprite * 8))..(sprite_table + (sprite * 8) + 8)].iter().map(|byte| format!("{:02x}", byte)).collect::>()); let size = self.state.vram[sprite_table + (sprite * 8) + 2]; let (size_h, size_v) = (((size >> 2) & 0x03) as u16 + 1, (size & 0x03) as u16 + 1); let pattern_name = ((self.state.vram[sprite_table + (sprite * 8) + 4] as u16) << 8) | (self.state.vram[sprite_table + (sprite * 8) + 5] as u16); let pattern_gen = pattern_name & 0x7FF; println!("{:x}", pattern_name); for cell_y in 0..size_v { for cell_x in 0..size_h { let pattern_addr = (pattern_gen + (cell_y * size_h) + cell_x) as u32; println!("pattern: ({}, {}) {:x}", cell_x, cell_y, pattern_addr); let iter = PatternIterator::new(&self.state, pattern_addr * 32, 3, true, true); swapper.current.blit((cell_x << 3) as u32, (cell_y << 3) as u32, iter, 8, 8); } } */ //let mut swapper = self.swapper.lock().unwrap(); //swapper.current.blit(0, 0, PatternIterator::new(&self.state, 0x408 * 32, 3, false, false), 8, 8); //swapper.current.blit(0, 8, PatternIterator::new(&self.state, 0x409 * 32, 3, false, false), 8, 8); //swapper.current.blit(8, 0, PatternIterator::new(&self.state, 0x402 * 32, 3, false, false), 8, 8); //swapper.current.blit(8, 8, PatternIterator::new(&self.state, 0x403 * 32, 3, false, false), 8, 8); //swapper.current.blit(16, 0, PatternIterator::new(&self.state, 0x404 * 32, 3, false, false), 8, 8); //swapper.current.blit(16, 8, PatternIterator::new(&self.state, 0x405 * 32, 3, false, false), 8, 8); } if self.state.transfer_run != DmaType::None && (self.state.mode_2 & MODE2_BF_DMA_ENABLED) != 0 { // TODO we will just do the full dma transfer here, but it really should be stepped match self.state.transfer_run { DmaType::Memory => { info!("{}: starting dma transfer {:x} from Mem:{:x} to {:?}:{:x} ({} bytes)", DEV_NAME, self.state.transfer_type, self.state.transfer_src_addr, self.state.transfer_target, self.state.transfer_dest_addr, self.state.transfer_remain); let mut bus = system.get_bus(); while self.state.transfer_remain > 0 { let mut data = [0; 2]; bus.read(self.state.transfer_src_addr as Address, &mut data)?; { let addr = self.state.transfer_dest_addr; let (target, length) = self.state.get_transfer_target_mut(); target[(addr as usize) % length] = data[0]; target[(addr as usize + 1) % length] = data[1]; } self.state.transfer_dest_addr += self.state.transfer_auto_inc; self.state.transfer_src_addr += 2; self.state.transfer_remain -= 1; } }, DmaType::Copy => { info!("{}: starting dma copy from VRAM:{:x} to VRAM:{:x} ({} bytes)", DEV_NAME, self.state.transfer_src_addr, self.state.transfer_dest_addr, self.state.transfer_remain); while self.state.transfer_remain > 0 { self.state.vram[self.state.transfer_dest_addr as usize] = self.state.vram[self.state.transfer_src_addr as usize]; self.state.transfer_dest_addr += self.state.transfer_auto_inc; self.state.transfer_src_addr += 1; self.state.transfer_remain -= 1; } }, DmaType::Fill => { info!("{}: starting dma fill to VRAM:{:x} ({} bytes) with {:x}", DEV_NAME, self.state.transfer_dest_addr, self.state.transfer_remain, self.state.transfer_fill_word); while self.state.transfer_remain > 0 { self.state.vram[self.state.transfer_dest_addr as usize] = self.state.transfer_fill_word as u8; self.state.transfer_dest_addr += self.state.transfer_auto_inc; self.state.transfer_remain -= 1; } }, _ => { warning!("{}: !!! error unexpected transfer mode {:x}", DEV_NAME, self.state.transfer_type); }, } self.state.set_dma_mode(DmaType::None); } Ok((1_000_000_000 / 13_423_294) * 4) } } impl Addressable for Ym7101 { fn len(&self) -> usize { 0x20 } fn read(&mut self, addr: Address, data: &mut [u8]) -> Result<(), Error> { match addr { // Read from Data Port 0x00 | 0x02 => { { let addr = self.state.transfer_dest_addr; let (target, length) = self.state.get_transfer_target_mut(); for i in 0..data.len() { data[i] = target[(addr as usize + i) % length]; } } self.state.transfer_dest_addr += self.state.transfer_auto_inc; debug!("{}: 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 0x04 | 0x06 => { debug!("{}: read status byte {:x}", DEV_NAME, self.state.status); data[0] = (self.state.status >> 8) as u8; data[1] = (self.state.status & 0x00FF) as u8; }, _ => { println!("{}: !!! unhandled read from {:x}", DEV_NAME, addr); }, } Ok(()) } fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> { match addr { // Write to Data Port 0x00 | 0x02 => { if (self.state.transfer_type & 0x30) == 0x20 { self.state.ctrl_port_buffer = None; self.state.transfer_fill_word = if data.len() >= 2 { read_beu16(data) } else { data[0] as u16 }; self.state.set_dma_mode(DmaType::Fill); } else { debug!("{}: data port write {} bytes to {:?}:{:x} with {:?}", DEV_NAME, data.len(), self.state.transfer_target, self.state.transfer_dest_addr, data); { let addr = self.state.transfer_dest_addr as usize; let (target, length) = self.state.get_transfer_target_mut(); for i in 0..data.len() { target[(addr + i) % length] = data[i]; } } self.state.transfer_dest_addr += self.state.transfer_auto_inc; } }, // Write to Control Port 0x04 | 0x06 => { 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); if data.len() == 4 { let value = read_beu16(&data[2..]); if (value & 0xC000) != 0x8000 { panic!("Unexpected"); } self.state.set_register(value); } } else { match (data.len(), self.state.ctrl_port_buffer) { (2, None) => { self.state.ctrl_port_buffer = Some(value) }, (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); }, } } }, _ => { warning!("{}: !!! unhandled write to {:x} with {:?}", DEV_NAME, addr, data); }, } Ok(()) } } impl Inspectable for Ym7101 { fn inspect(&mut self, system: &System, args: &[&str]) -> Result<(), Error> { match args[0] { "" | "state" => { self.state.dump_state(); }, "vram" => { self.state.dump_vram(); }, "vsram" => { self.state.dump_vsram(); }, _ => { }, } Ok(()) } } impl Ym7101State { pub fn dump_state(&self) { println!(""); println!("Mode1: {:#04x}", self.mode_1); println!("Mode2: {:#04x}", self.mode_2); println!("Mode3: {:#04x}", self.mode_3); println!("Mode4: {:#04x}", self.mode_4); println!(""); println!("Scroll A : {:#06x}", self.scroll_a_addr); println!("Window : {:#06x}", self.window_addr); println!("Scroll B : {:#06x}", self.scroll_b_addr); println!("HScroll : {:#06x}", self.hscroll_addr); println!("Sprites : {:#06x}", self.sprites_addr); println!(""); println!("DMA type : {:?}", self.transfer_type); println!("DMA Source: {:#06x}", self.transfer_src_addr); println!("DMA Dest : {:#06x}", self.transfer_dest_addr); println!("DMA Count : {:#06x}", self.transfer_count); } pub fn dump_vram(&self) { dump_slice(&self.vram, 65536); } pub fn dump_vsram(&self) { dump_slice(&self.vsram, 80); } }