1mod debug; 2 3pub use debug::{PatternTable, copy_nametables, copy_oam, copy_palette_ram}; 4 5use crate::ppu; 6use crate::ppu::{ColorEmphasis, FrameBuffer}; 7use jgenesis_common::frontend::{Color, TimingMode}; 8use nes_config::{NesPalette, Overscan}; 9 10pub trait TimingModeGraphicsExt { 11 fn visible_screen_height(self) -> u16; 12 13 fn starting_row(self) -> u16; 14} 15 16impl TimingModeGraphicsExt for TimingMode { 17 fn visible_screen_height(self) -> u16 { 18 match self { 19 Self::Ntsc => 224, 20 Self::Pal => 240, 21 } 22 } 23 24 fn starting_row(self) -> u16 { 25 match self { 26 Self::Ntsc => 8, 27 Self::Pal => 0, 28 } 29 } 30} 31 32pub fn ppu_frame_buffer_to_rgba( 33 ppu_frame_buffer: &FrameBuffer, 34 rgba_frame_buffer: &mut [Color], 35 overscan: Overscan, 36 display_mode: TimingMode, 37 palette: &NesPalette, 38 emulate_ntsc_output: bool, 39) { 40 if emulate_ntsc_output { 41 convert_to_rgba::<true>( 42 ppu_frame_buffer, 43 rgba_frame_buffer, 44 overscan, 45 display_mode, 46 palette, 47 ); 48 } else { 49 convert_to_rgba::<false>( 50 ppu_frame_buffer, 51 rgba_frame_buffer, 52 overscan, 53 display_mode, 54 palette, 55 ); 56 } 57} 58 59fn convert_to_rgba<const NTSC_OUTPUT: bool>( 60 ppu_frame_buffer: &FrameBuffer, 61 rgba_frame_buffer: &mut [Color], 62 overscan: Overscan, 63 display_mode: TimingMode, 64 palette: &NesPalette, 65) { 66 rgba_frame_buffer.fill(Color::BLACK); 67 68 let row_offset = display_mode.starting_row(); 69 let visible_screen_height = display_mode.visible_screen_height(); 70 71 let num_rows_rendered = 72 visible_screen_height.saturating_sub(overscan.top).saturating_sub(overscan.bottom) as usize; 73 let num_cols_rendered = 74 ppu::SCREEN_WIDTH.saturating_sub(overscan.left).saturating_sub(overscan.right) as usize; 75 76 for (row, scanline) in ppu_frame_buffer 77 .iter() 78 .skip(row_offset as usize + overscan.top as usize) 79 .take(num_rows_rendered) 80 .enumerate() 81 { 82 for (col, &(nes_color, color_emphasis)) in 83 scanline.iter().skip(overscan.left as usize).take(num_cols_rendered).enumerate() 84 { 85 let rgba_color = if NTSC_OUTPUT { 86 // NTSC shader expects R to contain the 6-bit color and G to contain color emphasis 87 Color::rgb(nes_color, color_emphasis.into(), 0) 88 } else { 89 nes_color_to_rgba(nes_color, color_emphasis, palette) 90 }; 91 rgba_frame_buffer[row * num_cols_rendered + col] = rgba_color; 92 } 93 } 94} 95 96pub fn nes_color_to_rgba( 97 nes_color: u8, 98 color_emphasis: ColorEmphasis, 99 palette: &NesPalette, 100) -> Color { 101 let palette_idx = usize::from(nes_color) 102 + 64 * usize::from(color_emphasis.red()) 103 + 128 * usize::from(color_emphasis.green()) 104 + 256 * usize::from(color_emphasis.blue()); 105 106 let (r, g, b) = palette[palette_idx]; 107 Color::rgb(r, g, b) 108}