1use crate::bus::PpuBus; 2use crate::graphics; 3use crate::ppu::ColorEmphasis; 4use jgenesis_common::frontend::Color; 5use jgenesis_common::num::GetBit; 6use nes_config::NesPalette; 7 8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] 9pub enum PatternTable { 10 #[default] 11 Zero, 12 One, 13} 14 15pub fn copy_nametables( 16 pattern_table: PatternTable, 17 bus: &mut PpuBus<'_>, 18 out: &mut [Color], 19 nes_palette: &NesPalette, 20) { 21 let backdrop_color = bus.get_palette_ram()[0] & 0x3F; 22 23 // Dump the pattern tables and nametables into Vecs because this function is horrendously slow if it needs to do 24 // a bus lookup for each nametable/pattern table byte 25 let pattern_table = dump_pattern_table(pattern_table, bus); 26 27 let mut nametables = vec![0; 0x1000]; 28 for (i, value) in nametables.iter_mut().enumerate() { 29 *value = bus.read_address(0x2000 | (i as u16)); 30 } 31 32 for nametable in 0..4 { 33 for row in 0..240 { 34 for col in 0..256 { 35 let nametable_addr = 0x400 * nametable + 32 * (row / 8) + col / 8; 36 let tile_number = nametables[nametable_addr as usize]; 37 38 let attributes_addr = 0x3C0 + 0x400 * nametable + 8 * (row / 32) + col / 32; 39 let attributes_shift = 2 * (2 * ((row % 32) / 16) + ((col % 32) / 16)); 40 let palette = (nametables[attributes_addr as usize] >> attributes_shift) & 0x03; 41 42 let pattern_table_addr = (u16::from(tile_number) << 4) | (row % 8); 43 let pattern_table_shift = 7 - (col % 8); 44 let color_0 = 45 (pattern_table[pattern_table_addr as usize] >> pattern_table_shift) & 0x01; 46 let color_1 = (pattern_table[(pattern_table_addr + 8) as usize] 47 >> pattern_table_shift) 48 & 0x01; 49 let color = (color_1 << 1) | color_0; 50 51 let nes_color = if color != 0 { 52 let color_addr = (palette << 2) | color; 53 bus.get_palette_ram()[color_addr as usize] & 0x3F 54 } else { 55 backdrop_color 56 }; 57 58 let out_idx = u32::from(nametable & 0x02) * 256 * 240 59 + u32::from(nametable & 0x01) * 256 60 + u32::from(row) * 256 * 2 61 + u32::from(col); 62 out[out_idx as usize] = 63 graphics::nes_color_to_rgba(nes_color, ColorEmphasis::NONE, nes_palette); 64 } 65 } 66 } 67} 68 69fn dump_pattern_table(pattern_table: PatternTable, bus: &mut PpuBus<'_>) -> Vec<u8> { 70 let mut out = vec![0; 0x1000]; 71 dump_pattern_table_into(pattern_table, bus, &mut out); 72 73 out 74} 75 76fn dump_pattern_table_into(pattern_table: PatternTable, bus: &mut PpuBus<'_>, out: &mut [u8]) { 77 let pattern_table_addr = match pattern_table { 78 PatternTable::Zero => 0x0000, 79 PatternTable::One => 0x1000, 80 }; 81 82 for (i, value) in out.iter_mut().enumerate() { 83 *value = bus.read_address(pattern_table_addr | (i as u16)); 84 } 85} 86 87pub fn copy_oam( 88 pattern_table: PatternTable, 89 bus: &mut PpuBus<'_>, 90 out: &mut [Color], 91 nes_palette: &NesPalette, 92) { 93 let backdrop_color = bus.get_palette_ram()[0] & 0x3F; 94 95 let mut pattern_tables = vec![0; 0x2000]; 96 dump_pattern_table_into(PatternTable::Zero, bus, &mut pattern_tables[..0x1000]); 97 dump_pattern_table_into(PatternTable::One, bus, &mut pattern_tables[0x1000..]); 98 99 let sprite_pattern_table_addr = match pattern_table { 100 PatternTable::Zero => 0x0000, 101 PatternTable::One => 0x1000, 102 }; 103 104 let double_height_sprites = bus.get_ppu_registers().double_height_sprites(); 105 106 let oam = bus.get_oam(); 107 for sprite in 0_u16..64 { 108 let tile_number = oam[(4 * sprite + 1) as usize]; 109 110 let attributes = oam[(4 * sprite + 2) as usize]; 111 let palette = 4 + (attributes & 0x03); 112 let x_flip = attributes.bit(6); 113 let y_flip = attributes.bit(7); 114 115 let (tile_number, base_pattern_table_addr) = if double_height_sprites { 116 (tile_number & !0x01, u16::from(tile_number & 0x01) << 12) 117 } else { 118 (tile_number, sprite_pattern_table_addr) 119 }; 120 121 let rows = if double_height_sprites { 16 } else { 8 }; 122 for row in 0_u16..rows { 123 for col in 0..8 { 124 let row = if y_flip { rows - 1 - row } else { row }; 125 let col = if x_flip { 7 - col } else { col }; 126 127 let double_height_offset = if row >= 8 { 0x0010 } else { 0x0000 }; 128 129 let pattern_table_addr = base_pattern_table_addr 130 | (u16::from(tile_number) << 4) 131 | double_height_offset 132 | (row & 0x07); 133 let pattern_table_shift = 7 - col; 134 let color_0 = 135 (pattern_tables[pattern_table_addr as usize] >> pattern_table_shift) & 0x01; 136 let color_1 = (pattern_tables[(pattern_table_addr + 8) as usize] 137 >> pattern_table_shift) 138 & 0x01; 139 let color = (color_1 << 1) | color_0; 140 141 let nes_color = if color != 0 { 142 let color_addr = (palette << 2) | color; 143 bus.get_palette_ram()[color_addr as usize] & 0x3F 144 } else { 145 backdrop_color 146 }; 147 148 let out_idx = (sprite / 8) * 64 * rows + (sprite % 8) * 8 + row * 64 + col; 149 out[out_idx as usize] = 150 graphics::nes_color_to_rgba(nes_color, ColorEmphasis::NONE, nes_palette); 151 } 152 } 153 } 154} 155 156pub fn copy_palette_ram(bus: &PpuBus<'_>, out: &mut [Color], nes_palette: &NesPalette) { 157 let palette_ram = bus.get_palette_ram(); 158 for (&nes_color, out_color) in palette_ram.iter().zip(out) { 159 *out_color = 160 graphics::nes_color_to_rgba(nes_color & 0x3F, ColorEmphasis::NONE, nes_palette); 161 } 162}