debug.rsannotateddebug.rssource162 lines · 5.9 KB · raw
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}