1use crate::api::BackgroundTileMap; 2use crate::ppu::fifo::BgTileAttributes; 3use crate::ppu::registers::{CgbPaletteRam, TileDataArea}; 4use crate::ppu::{Ppu, registers}; 5use crate::{HardwareMode, graphics}; 6use jgenesis_common::frontend::Color; 7use jgenesis_common::num::GetBit; 8 9impl Ppu { 10 pub fn is_using_double_height_sprites(&self) -> bool { 11 self.registers.double_height_sprites 12 } 13 14 pub fn copy_background( 15 &self, 16 tile_map: BackgroundTileMap, 17 cgb_dmg_compatibility: bool, 18 out: &mut [Color], 19 ) { 20 let tile_map_addr = match tile_map { 21 BackgroundTileMap::Zero => registers::TILE_MAP_AREA_0, 22 BackgroundTileMap::One => registers::TILE_MAP_AREA_1, 23 }; 24 25 let tile_data_area = self.registers.bg_tile_data_area; 26 27 for tile_map_row in 0..32 { 28 for tile_map_col in 0..32 { 29 let tile_number = 30 self.vram[(tile_map_addr | (tile_map_row * 32 + tile_map_col)) as usize]; 31 32 let attributes = match self.hardware_mode { 33 HardwareMode::Dmg => 0x00, 34 HardwareMode::Cgb => { 35 self.vram 36 [(0x2000 | tile_map_addr | (tile_map_row * 32 + tile_map_col)) as usize] 37 } 38 }; 39 let attributes: BgTileAttributes = attributes.into(); 40 41 let vram_bank_bit = u16::from(attributes.vram_bank) << 13; 42 let tile_addr = (vram_bank_bit | tile_data_area.tile_address(tile_number)) as usize; 43 let tile_data = &self.vram[tile_addr..tile_addr + 16]; 44 45 for tile_row in 0..8 { 46 let pixel_addr = 47 if attributes.vertical_flip { 2 * (7 - tile_row) } else { 2 * tile_row }; 48 49 let pixel_row_lsb = tile_data[pixel_addr as usize]; 50 let pixel_row_msb = tile_data[(pixel_addr + 1) as usize]; 51 52 for tile_col in 0_u16..8 { 53 let pixel_idx = 54 (if attributes.horizontal_flip { tile_col } else { 7 - tile_col }) 55 as u8; 56 let color_id = u8::from(pixel_row_lsb.bit(pixel_idx)) 57 | (u8::from(pixel_row_msb.bit(pixel_idx)) << 1); 58 59 let color = match self.hardware_mode { 60 HardwareMode::Dmg => { 61 resolve_dmg_color(self.registers.bg_palette[color_id as usize]) 62 } 63 HardwareMode::Cgb => { 64 let (palette, color_id) = if cgb_dmg_compatibility { 65 (0, self.registers.bg_palette[color_id as usize]) 66 } else { 67 (attributes.palette, color_id) 68 }; 69 70 resolve_cgb_color(&self.bg_palette_ram, palette, color_id) 71 } 72 }; 73 74 let out_idx = 75 tile_map_row * 8 * 256 + tile_row * 256 + tile_map_col * 8 + tile_col; 76 out[out_idx as usize] = color; 77 } 78 } 79 } 80 } 81 } 82 83 pub fn copy_sprites(&self, cgb_dmg_compatibility: bool, out: &mut [Color]) { 84 let double_height_sprites = self.registers.double_height_sprites; 85 let sprite_height = if double_height_sprites { 16 } else { 8 }; 86 87 for oam_idx in 0..40 { 88 let oam_addr = 4 * oam_idx; 89 90 let mut tile_number = self.oam[(oam_addr + 2) as usize]; 91 let attributes = self.oam[(oam_addr + 3) as usize]; 92 93 if double_height_sprites { 94 tile_number &= !1; 95 } 96 97 let vertical_flip = attributes.bit(6); 98 let horizontal_flip = attributes.bit(5); 99 let dmg_palette: u8 = attributes.bit(4).into(); 100 let vram_bank = match self.hardware_mode { 101 HardwareMode::Dmg => 0, 102 HardwareMode::Cgb => u16::from(attributes.bit(3)), 103 }; 104 let cgb_palette = attributes & 0x07; 105 106 let vram_bank_bit = vram_bank << 13; 107 let tile_addr = vram_bank_bit | TileDataArea::One.tile_address(tile_number); 108 109 for tile_row in 0..sprite_height { 110 let row_addr = if vertical_flip { 111 tile_addr + 2 * (sprite_height - 1 - tile_row) 112 } else { 113 tile_addr + 2 * tile_row 114 }; 115 116 let tile_data_lsb = self.vram[row_addr as usize]; 117 let tile_data_msb = self.vram[(row_addr + 1) as usize]; 118 119 for tile_col in 0_u16..8 { 120 let pixel_idx = (if horizontal_flip { tile_col } else { 7 - tile_col }) as u8; 121 let color_id = u8::from(tile_data_lsb.bit(pixel_idx)) 122 | (u8::from(tile_data_msb.bit(pixel_idx)) << 1); 123 124 let color = match self.hardware_mode { 125 HardwareMode::Dmg => resolve_dmg_color( 126 self.registers.sprite_palettes[dmg_palette as usize][color_id as usize], 127 ), 128 HardwareMode::Cgb => { 129 let (palette, color_id) = if cgb_dmg_compatibility { 130 ( 131 dmg_palette, 132 self.registers.sprite_palettes[dmg_palette as usize] 133 [color_id as usize], 134 ) 135 } else { 136 (cgb_palette, color_id) 137 }; 138 139 resolve_cgb_color(&self.sprite_palette_ram, palette, color_id) 140 } 141 }; 142 143 let out_idx = (oam_idx / 8) * sprite_height * 8 * 8 144 + tile_row * 8 * 8 145 + (oam_idx % 8) * 8 146 + tile_col; 147 out[out_idx as usize] = color; 148 } 149 } 150 } 151 } 152 153 pub fn copy_palettes(&self, out: &mut [Color]) { 154 match self.hardware_mode { 155 HardwareMode::Dmg => self.copy_palettes_dmg(out), 156 HardwareMode::Cgb => self.copy_palettes_cgb(out), 157 } 158 } 159 160 fn copy_palettes_dmg(&self, out: &mut [Color]) { 161 for (bg_color_id, dmg_color) in self.registers.bg_palette.into_iter().enumerate() { 162 let color = resolve_dmg_color(dmg_color); 163 out[bg_color_id] = color; 164 } 165 166 for (palette_id, sprite_palette) in self.registers.sprite_palettes.into_iter().enumerate() { 167 for (sprite_color_id, dmg_color) in sprite_palette.into_iter().enumerate() { 168 let color = resolve_dmg_color(dmg_color); 169 out[4 * (palette_id + 1) + sprite_color_id] = color; 170 } 171 } 172 } 173 174 fn copy_palettes_cgb(&self, out: &mut [Color]) { 175 for bg_palette in 0..8 { 176 for bg_color_id in 0..4 { 177 out[(4 * bg_palette + bg_color_id) as usize] = 178 resolve_cgb_color(&self.bg_palette_ram, bg_palette, bg_color_id); 179 } 180 } 181 182 for obj_palette in 0..8 { 183 for obj_color_id in 0..4 { 184 out[(32 + 4 * obj_palette + obj_color_id) as usize] = 185 resolve_cgb_color(&self.sprite_palette_ram, obj_palette, obj_color_id); 186 } 187 } 188 } 189} 190 191fn resolve_dmg_color(dmg_color: u8) -> Color { 192 let [r, g, b] = graphics::GB_COLOR_TO_RGB_GREEN_TINT[dmg_color as usize]; 193 Color::rgb(r, g, b) 194} 195 196fn resolve_cgb_color(palette_ram: &CgbPaletteRam, palette: u8, color_id: u8) -> Color { 197 let color = u16::from_le_bytes([ 198 palette_ram[(2 * (4 * palette + color_id)) as usize], 199 palette_ram[(2 * (4 * palette + color_id) + 1) as usize], 200 ]); 201 let (r, g, b) = graphics::parse_cgb_color(color); 202 Color::rgb( 203 graphics::RGB_5_TO_8[r as usize], 204 graphics::RGB_5_TO_8[g as usize], 205 graphics::RGB_5_TO_8[b as usize], 206 ) 207}