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}