diff --git a/src/peripherals/genesis/ym7101.rs b/src/peripherals/genesis/ym7101.rs index 09d0c80..f35423f 100644 --- a/src/peripherals/genesis/ym7101.rs +++ b/src/peripherals/genesis/ym7101.rs @@ -285,7 +285,6 @@ impl Ym7101Memory { #[derive(Copy, Clone, Debug, PartialEq)] pub enum ColourMode { - Unmasked, Normal, Shadow, Highlight, @@ -305,11 +304,11 @@ pub struct Ym7101State { 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 window_pos: ((usize, usize), (usize, usize)), + pub window_values: (u8, u8), pub background: u8, pub scroll_a_addr: usize, pub scroll_b_addr: usize, @@ -333,11 +332,11 @@ impl Ym7101State { 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), + window_pos: ((0, 0), (0, 0)), + window_values: (0, 0), background: 0, scroll_a_addr: 0, scroll_b_addr: 0, @@ -352,26 +351,6 @@ impl Ym7101State { } } - 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), - } - } - #[inline(always)] fn hsync_int_enabled(&self) -> bool { (self.mode_1 & MODE1_BF_HSYNC_INTERRUPT) != 0 @@ -387,10 +366,32 @@ impl Ym7101State { (self.mode_3 & MODE3_BF_EXTERNAL_INTERRUPT) != 0 } - pub fn get_palette_colour(&self, palette: u8, colour: u8, mode: ColourMode) -> u32 { - if colour == 0 && mode != ColourMode::Unmasked { - return MASK_COLOUR; + 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_position(&mut self) { + let win_h = ((self.window_values.0 & 0x1F) << 1) as usize; + let win_v = (self.window_values.1 & 0x1F) as usize; + let right = (self.window_values.0 & 0x80) != 0; + let down = (self.window_values.1 & 0x80) != 0; + + self.window_pos = match (right, down) { + (false, false) => ((0, 0), (win_h, win_v)), + (true, false) => ((win_h, 0), (self.screen_size.0, win_v)), + (false, true) => ((0, win_v), (win_h, self.screen_size.1)), + (true, true) => ((win_h, win_v), (self.screen_size.0, self.screen_size.1)), } + } + + pub fn is_inside_window(&mut self, x: usize, y: usize) -> bool { + x >= self.window_pos.0.0 && x <= self.window_pos.1.0 && y >= self.window_pos.0.1 && y <= self.window_pos.1.1 + } + + + pub fn get_palette_colour(&self, palette: u8, colour: u8, mode: ColourMode) -> u32 { let shift_enabled = (self.mode_4 & MODE4_BF_SHADOW_HIGHLIGHT) != 0; let rgb = self.memory.read_beu16(Memory::Cram, (((palette * 16) + colour) * 2) as usize); if !shift_enabled || mode == ColourMode::Normal { @@ -461,26 +462,25 @@ impl Ym7101State { (sprites, lines) } - pub fn get_pattern_pixel(&self, pattern_word: u16, x: usize, y: usize) -> u32 { + pub fn get_pattern_pixel(&self, pattern_word: u16, x: usize, y: usize) -> (u8, u8) { let pattern_addr = (pattern_word & 0x07FF) << 5; let palette = ((pattern_word & 0x6000) >> 13) as u8; let h_rev = (pattern_word & 0x0800) != 0; let v_rev = (pattern_word & 0x1000) != 0; - let mode = if (pattern_word & 0x8000) != 0 { ColourMode::Shadow } else { ColourMode::Normal }; let offset = pattern_addr as usize + (if !v_rev { y } else { 7 - y }) as usize * 4 + (if !h_rev { x / 2 } else { 3 - (x / 2) }) as usize; let second = x % 2 == 1; let value = if (!h_rev && !second) || (h_rev && second) { - self.get_palette_colour(palette, self.memory.vram[offset] >> 4, mode) + (palette, self.memory.vram[offset] >> 4) } else { - self.get_palette_colour(palette, self.memory.vram[offset] & 0x0f, mode) + (palette, self.memory.vram[offset] & 0x0f) }; value } pub fn draw_frame(&mut self, frame: &mut Frame) { - let bg_colour = self.get_palette_colour((self.background & 0x30) >> 4, self.background & 0x0f, ColourMode::Unmasked); + let bg_colour = ((self.background & 0x30) >> 4, self.background & 0x0f); let (sprites, sprites_per_line) = self.build_sprites_lists(); for y in 0..(self.screen_size.1 * 8) { @@ -503,7 +503,18 @@ impl Ym7101State { let priority_b = (pattern_b_word & 0x8000) != 0; let pixel_b = self.get_pattern_pixel(pattern_b_word, pixel_b_x % 8, pixel_b_y % 8); - let mut pixel_sprite = MASK_COLOUR; + let pixel_win = if self.window_addr != 0 && self.is_inside_window(x, y) { + let pixel_win_x = x - self.window_pos.0.0 * 8; + let pixel_win_y = y - self.window_pos.0.1 * 8; + let pattern_win_addr = self.get_pattern_addr(self.window_addr, pixel_win_x / 8, pixel_win_y / 8); + let pattern_win_word = self.memory.read_beu16(Memory::Vram, pattern_win_addr); + //let priority_win = (pattern_win_word & 0x8000) != 0; + self.get_pattern_pixel(pattern_win_word, pixel_win_x % 8, pixel_win_y % 8) + } else { + (0, 0) + }; + + let mut pixel_sprite = (0, 0); let mut priority_sprite = false; for sprite_num in sprites_per_line[y].iter() { let offset_x = x as i16 - sprites[*sprite_num].pos.0; @@ -514,7 +525,7 @@ impl Ym7101State { priority_sprite = (pattern & 0x8000) != 0; pixel_sprite = self.get_pattern_pixel(pattern, offset_x as usize % 8, offset_y as usize % 8); - if pixel_sprite != MASK_COLOUR { + if pixel_sprite.1 != 0 { break; } } @@ -522,20 +533,28 @@ impl Ym7101State { let pixels = match (priority_sprite, priority_a, priority_b) { (false, false, true) => - [ pixel_b, pixel_sprite, pixel_a, bg_colour, 0x000000 ], + [ pixel_win, pixel_b, pixel_sprite, pixel_a, bg_colour ], (true, false, true) => - [ pixel_sprite, pixel_b, pixel_a, bg_colour, 0x000000 ], + [ pixel_win, pixel_sprite, pixel_b, pixel_a, bg_colour ], (false, true, false) => - [ pixel_a, pixel_sprite, pixel_b, bg_colour, 0x000000 ], + [ pixel_win, pixel_a, pixel_sprite, pixel_b, bg_colour ], (false, true, true) => - [ pixel_a, pixel_b, pixel_sprite, bg_colour, 0x000000 ], + [ pixel_win, pixel_a, pixel_b, pixel_sprite, bg_colour ], _ => - [ pixel_sprite, pixel_a, pixel_b, bg_colour, 0x000000 ], + [ pixel_win, pixel_sprite, pixel_a, pixel_b, bg_colour ], }; - for pixel in pixels { - if pixel != MASK_COLOUR { - frame.set_pixel(x as u32, y as u32, pixel); + for i in 0..pixels.len() { + if pixels[i].1 != 0 || i == pixels.len() - 1 { + let mode = if pixels[i] == (3, 14) { + ColourMode::Highlight + } else if (!priority_a && !priority_b) || pixels[i] == (3, 15) { + ColourMode::Shadow + } else { + ColourMode::Normal + }; + + frame.set_pixel(x as u32, y as u32, self.get_palette_colour(pixels[i].0, pixels[i].1, mode)); break; } } @@ -859,12 +878,12 @@ impl Ym7101 { self.state.scroll_size = (h, v); }, REG_WINDOW_H_POS => { - self.state.window_pos.0 = data; - self.state.update_window_offset(); + self.state.window_values.0 = data; + self.state.update_window_position(); }, REG_WINDOW_V_POS => { - self.state.window_pos.1 = data; - self.state.update_window_offset(); + self.state.window_values.1 = data; + self.state.update_window_position(); }, REG_DMA_COUNTER_LOW => { self.state.memory.transfer_count = (self.state.memory.transfer_count & 0xFF00) | data as u32; diff --git a/todo.txt b/todo.txt index 0f1577e..b7a4ca0 100644 --- a/todo.txt +++ b/todo.txt @@ -2,14 +2,6 @@ * I need some better function for dealing with memory, like a function that copies data with a loop, or allows offset reading of a fixed piece of data..., the trick is what function are the most common. You can use generics -* should you modify the drawing to always draw line by line and combine all layers at once. It's more complicated and would be - slower because some things would need to be calculated multiple times, but it'd be more accurate - -* you need to refactor the ym7101 stuff -* can you make vram/cram/vsram be Addressables that can be accessed the same way? Would this add too much overhead? -* you can directly use a MemoryBlock, and separate out the dma -* it's not working so well because of the &mut needed by .read() - * there is an issue with Mortal Kombat 2 where it will crash randomly at the start of a fight. The code is actually swapping stacks a bunch of times, and at some point, the stack is corrupted or something and it `rts`s to the wrong address... @@ -66,8 +58,6 @@ Debugger: Genesis/Mega Drive: * there's a bug when Sonic 2 goes to the demo screen, it's all corrupted (could it be a dma copy error) - * the line hscroll mode needs to draw an extra cell in order to cover up the foreground on the left and bottom edges of the screen - * some games seem to be missing a row of cells at the top (Ghostbuster's dialog borders seem cut off) * fix sprite/cell priorities so that they're drawn correctly * implement highlight and shadow mode, which is also related to the priority * in some games the controller doesn't seem to work at all (Earthworm Jim, and Mortal Kombat)