1use bincode::{Decode, Encode}; 2use jgenesis_common::frontend::TimingMode; 3use jgenesis_common::num::GetBit; 4use std::fmt::{Display, Formatter}; 5 6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 7pub enum FrameBufferMode { 8 #[default] 9 Blank = 0, 10 PackedPixel = 1, 11 DirectColor = 2, 12 RunLength = 3, 13} 14 15impl FrameBufferMode { 16 fn from_word(value: u16) -> Self { 17 match value & 3 { 18 0 => Self::Blank, 19 1 => Self::PackedPixel, 20 2 => Self::DirectColor, 21 3 => Self::RunLength, 22 _ => unreachable!("value & 3 is always <= 3"), 23 } 24 } 25} 26 27impl Display for FrameBufferMode { 28 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 29 match self { 30 Self::Blank => write!(f, "Blank"), 31 Self::PackedPixel => write!(f, "Packed pixel"), 32 Self::DirectColor => write!(f, "Direct color"), 33 Self::RunLength => write!(f, "Run length"), 34 } 35 } 36} 37 38#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 39pub enum VerticalResolution { 40 #[default] 41 V28 = 0, 42 V30 = 1, 43} 44 45impl VerticalResolution { 46 pub fn active_scanlines_per_frame(self) -> u16 { 47 match self { 48 Self::V28 => super::V28_FRAME_HEIGHT as u16, 49 Self::V30 => super::V30_FRAME_HEIGHT as u16, 50 } 51 } 52 53 fn from_bit(bit: bool) -> Self { 54 if bit { Self::V30 } else { Self::V28 } 55 } 56} 57 58impl Display for VerticalResolution { 59 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 60 match self { 61 Self::V28 => write!(f, "V28 (224px)"), 62 Self::V30 => write!(f, "V30 (240px)"), 63 } 64 } 65} 66 67#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 68pub enum SelectedFrameBuffer { 69 #[default] 70 Zero = 0, 71 One = 1, 72} 73 74impl SelectedFrameBuffer { 75 fn from_bit(bit: bool) -> Self { 76 if bit { Self::One } else { Self::Zero } 77 } 78} 79 80#[derive(Debug, Clone, Encode, Decode)] 81pub struct Registers { 82 pub frame_buffer_mode: FrameBufferMode, 83 pub v_resolution: VerticalResolution, 84 pub active_scanlines_per_frame: u16, 85 pub priority: bool, 86 pub display_frame_buffer: SelectedFrameBuffer, 87 pub screen_left_shift: bool, 88 pub auto_fill_length: u16, 89 pub auto_fill_start_address: u16, 90 pub auto_fill_data: u16, 91 pub h_interrupt_interval: u16, 92 pub h_interrupt_in_vblank: bool, 93} 94 95impl Default for Registers { 96 fn default() -> Self { 97 Self { 98 frame_buffer_mode: FrameBufferMode::default(), 99 v_resolution: VerticalResolution::default(), 100 active_scanlines_per_frame: VerticalResolution::default().active_scanlines_per_frame(), 101 priority: false, 102 display_frame_buffer: SelectedFrameBuffer::default(), 103 screen_left_shift: false, 104 auto_fill_length: 1, 105 auto_fill_start_address: 0, 106 auto_fill_data: 0, 107 h_interrupt_interval: 0, 108 h_interrupt_in_vblank: false, 109 } 110 } 111} 112 113impl Registers { 114 // 68000: $A15180 115 // SH-2: $4100 116 pub fn read_display_mode(&self, timing_mode: TimingMode) -> u16 { 117 (u16::from(timing_mode == TimingMode::Ntsc) << 15) 118 | (u16::from(self.priority) << 7) 119 | ((self.v_resolution as u16) << 6) 120 | (self.frame_buffer_mode as u16) 121 } 122 123 // 68000: $A15180 124 // SH-2: $4100 125 pub fn write_display_mode(&mut self, value: u16) { 126 self.frame_buffer_mode = FrameBufferMode::from_word(value); 127 self.v_resolution = VerticalResolution::from_bit(value.bit(6)); 128 self.priority = value.bit(7); 129 130 self.active_scanlines_per_frame = self.v_resolution.active_scanlines_per_frame(); 131 132 log::debug!("Display mode write: {value:04X}"); 133 log::debug!(" Frame buffer mode: {:?}", self.frame_buffer_mode); 134 log::debug!(" Vertical resolution: {:?}", self.v_resolution); 135 log::debug!(" Priority: {}", self.priority); 136 } 137 138 // 68000: $A15182 139 // SH-2: $4102 140 pub fn read_screen_shift(&self) -> u16 { 141 self.screen_left_shift.into() 142 } 143 144 // 68000: $A15182 145 // SH-2: $4102 146 pub fn write_screen_shift(&mut self, value: u16) { 147 self.screen_left_shift = value.bit(0); 148 149 log::debug!("Screen shift control write: {value:04X}"); 150 log::debug!(" Shift screen left by 1 dot: {}", self.screen_left_shift); 151 } 152 153 // 68000: $A15184 154 // SH-2: $4104 155 pub fn read_auto_fill_length(&self) -> u16 { 156 self.auto_fill_length.wrapping_sub(1) & 0xFF 157 } 158 159 // 68000: $A15184 160 // SH-2: $4104 161 pub fn write_auto_fill_length(&mut self, value: u16) { 162 self.auto_fill_length = (value & 0xFF) + 1; 163 164 log::trace!("Auto fill length write: {value:04X}"); 165 log::trace!(" Auto fill length: {}", self.auto_fill_length); 166 } 167 168 // 68000: $A15186 169 // SH-2: $4106 170 pub fn read_auto_fill_start_address(&self) -> u16 { 171 self.auto_fill_start_address 172 } 173 174 // 68000: $A15186 175 // SH-2: $4106 176 pub fn write_auto_fill_start_address(&mut self, value: u16) { 177 self.auto_fill_start_address = value; 178 log::trace!("Auto fill start address write: {value:04X}"); 179 } 180 181 pub fn increment_auto_fill_address(&mut self) { 182 // Highest 8 bits of address do not increment, only the lowest 8 bits 183 self.auto_fill_start_address = (self.auto_fill_start_address & 0xFF00) 184 | (self.auto_fill_start_address.wrapping_add(1) & 0xFF); 185 } 186 187 // 68000: $A15188 188 // SH-2: $4108 189 pub fn write_auto_fill_data(&mut self, value: u16) { 190 self.auto_fill_data = value; 191 log::trace!("Auto fill data write: {value:04X}"); 192 } 193 194 // 68000: $A1518A 195 // SH-2: $410A 196 pub fn write_frame_buffer_control(&mut self, value: u16) { 197 self.display_frame_buffer = SelectedFrameBuffer::from_bit(value.bit(0)); 198 199 log::debug!("Frame buffer control write: {value:04X}"); 200 log::debug!(" Display frame buffer: {:?}", self.display_frame_buffer); 201 } 202}