1use crate::vdp::registers::Registers; 2use crate::vdp::{ColorTables, Cram, FrameBufferRam, Vdp, VdpConfig, u16_to_rgb}; 3use genesis_components::debug::CramEntry; 4use jgenesis_common::debug::{DebugMemoryView, DebugWordsView, Endian}; 5 6#[derive(Debug, Clone)] 7pub struct VdpDebugState { 8 frame_buffer_0: Box<FrameBufferRam>, 9 frame_buffer_1: Box<FrameBufferRam>, 10 cram: Box<Cram>, 11 registers: Registers, 12 config: VdpConfig, 13} 14 15impl Vdp { 16 pub fn to_debug_state(&self) -> VdpDebugState { 17 VdpDebugState { 18 frame_buffer_0: self.frame_buffer_0.clone().into(), 19 frame_buffer_1: self.frame_buffer_1.clone().into(), 20 cram: self.cram.clone().into(), 21 registers: self.registers.clone(), 22 config: self.config.clone(), 23 } 24 } 25 26 pub fn debug_frame_buffer_view(&mut self, frame_buffer: usize) -> impl DebugMemoryView { 27 let frame_buffer = match frame_buffer { 28 0 => &mut self.frame_buffer_0, 29 _ => &mut self.frame_buffer_1, 30 }; 31 32 DebugWordsView(frame_buffer.as_mut_slice(), Endian::Big) 33 } 34 35 pub fn debug_palette_ram_view(&mut self) -> impl DebugMemoryView { 36 DebugWordsView(self.cram.as_mut_slice(), Endian::Big) 37 } 38} 39 40impl VdpDebugState { 41 pub fn hen_bit(&self) -> bool { 42 self.registers.h_interrupt_in_vblank 43 } 44 45 pub fn h_interrupt_interval(&self) -> u16 { 46 self.registers.h_interrupt_interval 47 } 48 49 pub fn debug_frame_buffer_view(&mut self, frame_buffer: usize) -> impl DebugMemoryView { 50 let frame_buffer = match frame_buffer { 51 0 => &mut self.frame_buffer_0, 52 _ => &mut self.frame_buffer_1, 53 }; 54 55 DebugWordsView(frame_buffer.as_mut_slice(), Endian::Big) 56 } 57 58 pub fn debug_palette_ram_view(&mut self) -> impl DebugMemoryView { 59 DebugWordsView(self.cram.as_mut_slice(), Endian::Big) 60 } 61 62 pub fn copy_palette(&self, out: &mut [CramEntry]) { 63 let color_tables = ColorTables::from_tint(self.config.color_tint); 64 65 for (i, entry) in out[..256].iter_mut().enumerate() { 66 let s32x_color = self.cram[i]; 67 *entry = CramEntry { value: s32x_color, color: u16_to_rgb(s32x_color, color_tables) }; 68 } 69 } 70 71 pub fn dump_registers(&self, mut callback: impl FnMut(&str, &[(&str, &str)])) { 72 callback( 73 "$4100 / $A15180", 74 &[ 75 ("Mode", &self.registers.frame_buffer_mode.to_string()), 76 ("Vertical resolution", &self.registers.v_resolution.to_string()), 77 ("Invert priority", bool_str(self.registers.priority)), 78 ], 79 ); 80 81 callback( 82 "$4102 / $A15182", 83 &[("Shift screen left", bool_str(self.registers.screen_left_shift))], 84 ); 85 86 callback( 87 "$4104 / $A15184", 88 &[("Auto fill length", &self.registers.auto_fill_length.to_string())], 89 ); 90 91 callback( 92 "$4106 / $A15186", 93 &[( 94 "Auto fill start address", 95 &format!("${:05X}", u32::from(self.registers.auto_fill_start_address) << 1), 96 )], 97 ); 98 99 callback( 100 "$4108 / $A15188", 101 &[("Auto fill data", &format!("0x{:04X}", self.registers.auto_fill_data))], 102 ); 103 104 callback( 105 "$410A / $A1518A", 106 &[("Display frame buffer", ["0", "1"][self.registers.display_frame_buffer as usize])], 107 ); 108 } 109} 110 111fn bool_str(b: bool) -> &'static str { 112 if b { "true" } else { "false" } 113}