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