1#![allow(clippy::too_many_lines)]
13
14use crate::{Ppu, Region};
15
16impl Ppu {
17 const fn vram_mapped_address(&self) -> usize {
19 let a = self.io.vram_address;
20 let mapped = match self.io.vram_mapping {
21 1 => (a & 0xff00) | ((a & 0x001f) << 3) | ((a >> 5) & 0x07),
22 2 => (a & 0xfe00) | ((a & 0x003f) << 3) | ((a >> 6) & 0x07),
23 3 => (a & 0xfc00) | ((a & 0x007f) << 3) | ((a >> 7) & 0x07),
24 _ => a,
25 };
26 (mapped & 0x7fff) as usize
27 }
28
29 const fn vram_accessible(&self) -> bool {
31 self.io.display_disable || self.v == 0 || self.v > self.visible_height()
32 }
33
34 fn vram_read_word(&self) -> u16 {
35 self.vram[self.vram_mapped_address()]
36 }
37
38 const fn vram_step(&mut self) {
39 self.io.vram_address = self
40 .io
41 .vram_address
42 .wrapping_add(self.io.vram_increment_size);
43 }
44
45 pub fn write_reg(&mut self, addr: u16, val: u8) {
47 self.io.ppu1_mdr = val;
49 let v = u16::from(val);
50 match addr {
51 0x2100 => {
53 self.io.display_brightness = val & 0x0f;
54 self.io.display_disable = val & 0x80 != 0;
55 }
56 0x2101 => {
58 self.io.obj_tiledata_addr = u16::from(val & 0x07) << 13;
59 self.io.obj_nameselect = u16::from((val >> 3) & 0x03);
60 self.io.obj_base_size = (val >> 5) & 0x07;
61 }
62 0x2102 => {
64 self.io.oam_base_address = (self.io.oam_base_address & 0x0200) | (v << 1);
65 self.io.oam_address = self.io.oam_base_address;
66 }
67 0x2103 => {
69 self.io.oam_base_address =
70 (self.io.oam_base_address & 0x01ff) | (u16::from(val & 0x01) << 9);
71 self.io.oam_priority_rotation = val & 0x80 != 0;
72 self.io.oam_address = self.io.oam_base_address;
73 }
74 0x2104 => self.write_oamdata(val),
76 0x2105 => {
78 self.io.bg_mode = val & 0x07;
79 self.io.bg3_priority = val & 0x08 != 0;
80 self.io.tile_size[0] = val & 0x10 != 0;
81 self.io.tile_size[1] = val & 0x20 != 0;
82 self.io.tile_size[2] = val & 0x40 != 0;
83 self.io.tile_size[3] = val & 0x80 != 0;
84 }
85 0x2106 => {
87 self.io.mosaic_enable[0] = val & 0x01 != 0;
88 self.io.mosaic_enable[1] = val & 0x02 != 0;
89 self.io.mosaic_enable[2] = val & 0x04 != 0;
90 self.io.mosaic_enable[3] = val & 0x08 != 0;
91 self.io.mosaic_size = ((val >> 4) & 0x0f) + 1;
92 }
93 0x2107..=0x210a => {
95 let bg = (addr - 0x2107) as usize;
96 self.io.bg_screen_size[bg] = val & 0x03;
97 self.io.bg_screen_addr[bg] = u16::from(val & 0xfc) << 8;
98 }
99 0x210b => {
101 self.io.bg_tiledata_addr[0] = u16::from(val & 0x0f) << 12;
102 self.io.bg_tiledata_addr[1] = u16::from((val >> 4) & 0x0f) << 12;
103 }
104 0x210c => {
106 self.io.bg_tiledata_addr[2] = u16::from(val & 0x0f) << 12;
107 self.io.bg_tiledata_addr[3] = u16::from((val >> 4) & 0x0f) << 12;
108 }
109 0x210d => self.write_bg_hofs(0, val, true),
111 0x210e => self.write_bg_vofs(0, val, true),
112 0x210f => self.write_bg_hofs(1, val, false),
113 0x2110 => self.write_bg_vofs(1, val, false),
114 0x2111 => self.write_bg_hofs(2, val, false),
115 0x2112 => self.write_bg_vofs(2, val, false),
116 0x2113 => self.write_bg_hofs(3, val, false),
117 0x2114 => self.write_bg_vofs(3, val, false),
118 0x2115 => {
120 self.io.vram_increment_size = match val & 0x03 {
121 0 => 1,
122 1 => 32,
123 _ => 128,
124 };
125 self.io.vram_mapping = (val >> 2) & 0x03;
126 self.io.vram_increment_high = val & 0x80 != 0;
127 }
128 0x2116 => {
130 self.io.vram_address = (self.io.vram_address & 0xff00) | v;
131 self.io.vram_read_latch = self.vram_read_word();
132 }
133 0x2117 => {
135 self.io.vram_address = (self.io.vram_address & 0x00ff) | (v << 8);
136 self.io.vram_read_latch = self.vram_read_word();
137 }
138 0x2118 => {
140 if self.vram_accessible() {
141 let idx = self.vram_mapped_address();
142 self.vram[idx] = (self.vram[idx] & 0xff00) | v;
143 }
144 if !self.io.vram_increment_high {
145 self.vram_step();
146 }
147 }
148 0x2119 => {
150 if self.vram_accessible() {
151 let idx = self.vram_mapped_address();
152 self.vram[idx] = (self.vram[idx] & 0x00ff) | (v << 8);
153 }
154 if self.io.vram_increment_high {
155 self.vram_step();
156 }
157 }
158 0x211a => {
160 self.io.m7_hflip = val & 0x01 != 0;
161 self.io.m7_vflip = val & 0x02 != 0;
162 self.io.m7_repeat = (val >> 6) & 0x03;
163 }
164 0x211b => self.io.m7a = self.mode7_latch(val),
166 0x211c => self.io.m7b = self.mode7_latch(val),
167 0x211d => self.io.m7c = self.mode7_latch(val),
168 0x211e => self.io.m7d = self.mode7_latch(val),
169 0x211f => self.io.m7x = self.mode7_latch(val),
170 0x2120 => self.io.m7y = self.mode7_latch(val),
171 0x2121 => {
173 self.io.cgram_address = val;
174 self.io.cgram_latch_high = false;
175 }
176 0x2122 => {
178 if self.io.cgram_latch_high {
179 let word = (u16::from(val & 0x7f) << 8) | u16::from(self.io.cgram_byte_latch);
180 self.cgram[self.io.cgram_address as usize] = word;
181 self.io.cgram_address = self.io.cgram_address.wrapping_add(1);
182 self.io.cgram_latch_high = false;
183 } else {
184 self.io.cgram_byte_latch = val;
185 self.io.cgram_latch_high = true;
186 }
187 }
188 0x2123 => self.write_wsel(0, 1, val),
190 0x2124 => self.write_wsel(2, 3, val),
191 0x2125 => self.write_wsel(4, 5, val),
192 0x2126 => self.io.win.one_left = val,
193 0x2127 => self.io.win.one_right = val,
194 0x2128 => self.io.win.two_left = val,
195 0x2129 => self.io.win.two_right = val,
196 0x212a => {
197 self.io.win.layer[0].mask = val & 0x03;
198 self.io.win.layer[1].mask = (val >> 2) & 0x03;
199 self.io.win.layer[2].mask = (val >> 4) & 0x03;
200 self.io.win.layer[3].mask = (val >> 6) & 0x03;
201 }
202 0x212b => {
203 self.io.win.layer[4].mask = val & 0x03;
204 self.io.win.layer[5].mask = (val >> 2) & 0x03;
205 }
206 0x212c => self.set_enable(&mut Self::enable_main, val),
208 0x212d => self.set_enable(&mut Self::enable_sub, val),
209 0x212e => self.set_enable(&mut Self::enable_win_main, val),
211 0x212f => self.set_enable(&mut Self::enable_win_sub, val),
212 0x2130 => {
214 self.io.direct_color = val & 0x01 != 0;
215 self.io.add_subscreen = val & 0x02 != 0;
216 self.io.color_window_below = (val >> 4) & 0x03;
217 self.io.color_window_above = (val >> 6) & 0x03;
218 }
219 0x2131 => {
221 for (i, e) in self.io.color_math_enable.iter_mut().enumerate() {
222 *e = val & (1 << i) != 0;
223 }
224 self.io.color_halve = val & 0x40 != 0;
225 self.io.color_subtract = val & 0x80 != 0;
226 }
227 0x2132 => {
229 let intensity = u16::from(val & 0x1f);
230 if val & 0x20 != 0 {
231 self.io.fixed_color = (self.io.fixed_color & !0x001f) | intensity;
232 }
233 if val & 0x40 != 0 {
234 self.io.fixed_color = (self.io.fixed_color & !0x03e0) | (intensity << 5);
235 }
236 if val & 0x80 != 0 {
237 self.io.fixed_color = (self.io.fixed_color & !0x7c00) | (intensity << 10);
238 }
239 }
240 0x2133 => {
242 self.io.interlace = val & 0x01 != 0;
243 self.io.obj_interlace = val & 0x02 != 0;
244 self.io.overscan = val & 0x04 != 0;
245 self.io.pseudo_hires = val & 0x08 != 0;
246 self.io.extbg = val & 0x40 != 0;
247 }
248 _ => {}
249 }
250 }
251
252 fn write_bg_hofs(&mut self, bg: usize, val: u8, is_bg1: bool) {
253 let prev1 = self.bgofs_prev1;
254 let prev2 = self.bgofs_prev2;
255 self.io.bg_hofs[bg] = (u16::from(val) << 8) | (prev1 & !0x07) | (prev2 & 0x07);
256 self.bgofs_prev1 = u16::from(val);
257 self.bgofs_prev2 = u16::from(val);
258 if is_bg1 {
259 self.io.m7_hofs = (u16::from(val) << 8) | self.mode7_byte_latch;
260 self.mode7_byte_latch = u16::from(val);
261 }
262 }
263
264 fn write_bg_vofs(&mut self, bg: usize, val: u8, is_bg1: bool) {
265 let prev1 = self.bgofs_prev1;
266 self.io.bg_vofs[bg] = (u16::from(val) << 8) | prev1;
267 self.bgofs_prev1 = u16::from(val);
268 if is_bg1 {
269 self.io.m7_vofs = (u16::from(val) << 8) | self.mode7_byte_latch;
270 self.mode7_byte_latch = u16::from(val);
271 }
272 }
273
274 fn mode7_latch(&mut self, val: u8) -> u16 {
275 let out = (u16::from(val) << 8) | self.mode7_byte_latch;
276 self.mode7_byte_latch = u16::from(val);
277 out
278 }
279
280 const fn write_wsel(&mut self, la: usize, lb: usize, val: u8) {
281 self.io.win.layer[la].one_invert = val & 0x01 != 0;
282 self.io.win.layer[la].one_enable = val & 0x02 != 0;
283 self.io.win.layer[la].two_invert = val & 0x04 != 0;
284 self.io.win.layer[la].two_enable = val & 0x08 != 0;
285 self.io.win.layer[lb].one_invert = val & 0x10 != 0;
286 self.io.win.layer[lb].one_enable = val & 0x20 != 0;
287 self.io.win.layer[lb].two_invert = val & 0x40 != 0;
288 self.io.win.layer[lb].two_enable = val & 0x80 != 0;
289 }
290
291 const fn enable_main(io: &mut crate::Io, i: usize, b: bool) {
292 io.main_enable[i] = b;
293 }
294 const fn enable_sub(io: &mut crate::Io, i: usize, b: bool) {
295 io.sub_enable[i] = b;
296 }
297 const fn enable_win_main(io: &mut crate::Io, i: usize, b: bool) {
298 io.win_main_enable[i] = b;
299 }
300 const fn enable_win_sub(io: &mut crate::Io, i: usize, b: bool) {
301 io.win_sub_enable[i] = b;
302 }
303
304 fn set_enable(&mut self, f: &mut dyn FnMut(&mut crate::Io, usize, bool), val: u8) {
305 for i in 0..5 {
306 f(&mut self.io, i, val & (1 << i) != 0);
307 }
308 }
309
310 const fn write_oamdata(&mut self, val: u8) {
311 let addr = self.io.oam_address & 0x03ff;
312 let even = addr & 1 == 0;
313 if addr >= 0x200 {
314 self.oam[(0x200 + (addr & 0x1f)) as usize] = val;
316 } else if even {
317 self.io.oam_byte_latch = val;
318 } else {
319 let base = (addr & !1) as usize;
321 self.oam[base] = self.io.oam_byte_latch;
322 self.oam[base + 1] = val;
323 }
324 self.io.oam_address = (self.io.oam_address + 1) & 0x03ff;
325 }
326
327 pub fn read_reg(&mut self, addr: u16) -> u8 {
330 match addr {
331 0x2134..=0x2136 => {
333 let product = i32::from(self.io.m7a as i16) * i32::from((self.io.m7b >> 8) as i8);
334 let byte = (addr - 0x2134) as u32 * 8;
335 let out = ((product as u32) >> byte) as u8;
336 self.io.ppu1_mdr = out;
337 out
338 }
339 0x2137 => {
341 self.latch_hv_counters();
342 self.io.ppu1_mdr
343 }
344 0x2138 => {
346 let addr = self.io.oam_address & 0x03ff;
347 let out = if addr >= 0x200 {
348 self.oam[(0x200 + (addr & 0x1f)) as usize]
349 } else {
350 self.oam[addr as usize]
351 };
352 self.io.oam_address = (self.io.oam_address + 1) & 0x03ff;
353 self.io.ppu1_mdr = out;
354 out
355 }
356 0x2139 => {
358 let out = (self.io.vram_read_latch & 0xff) as u8;
359 if !self.io.vram_increment_high {
360 self.io.vram_read_latch = self.vram_read_word();
361 self.vram_step();
362 }
363 self.io.ppu1_mdr = out;
364 out
365 }
366 0x213a => {
368 let out = (self.io.vram_read_latch >> 8) as u8;
369 if self.io.vram_increment_high {
370 self.io.vram_read_latch = self.vram_read_word();
371 self.vram_step();
372 }
373 self.io.ppu1_mdr = out;
374 out
375 }
376 0x213b => {
378 let out = if self.io.cgram_latch_high {
379 self.io.cgram_latch_high = false;
380 let hi = ((self.cgram[self.io.cgram_address as usize] >> 8) & 0x7f) as u8;
381 self.io.cgram_address = self.io.cgram_address.wrapping_add(1);
382 (self.io.ppu2_mdr & 0x80) | hi
383 } else {
384 self.io.cgram_latch_high = true;
385 (self.cgram[self.io.cgram_address as usize] & 0xff) as u8
386 };
387 self.io.ppu2_mdr = out;
388 out
389 }
390 0x213c => {
392 let out = if self.io.ophct_high_toggle {
393 self.io.ophct_high_toggle = false;
394 (self.io.ppu2_mdr & 0xfe) | ((self.io.latch_h >> 8) & 1) as u8
395 } else {
396 self.io.ophct_high_toggle = true;
397 (self.io.latch_h & 0xff) as u8
398 };
399 self.io.ppu2_mdr = out;
400 out
401 }
402 0x213d => {
404 let out = if self.io.opvct_high_toggle {
405 self.io.opvct_high_toggle = false;
406 (self.io.ppu2_mdr & 0xfe) | ((self.io.latch_v >> 8) & 1) as u8
407 } else {
408 self.io.opvct_high_toggle = true;
409 (self.io.latch_v & 0xff) as u8
410 };
411 self.io.ppu2_mdr = out;
412 out
413 }
414 0x213e => {
416 let mut out = 0x01u8; if self.io.range_over {
418 out |= 0x40;
419 }
420 if self.io.time_over {
421 out |= 0x80;
422 }
423 out |= self.io.ppu1_mdr & 0x10;
424 self.io.ppu1_mdr = out;
425 out
426 }
427 0x213f => {
429 let mut out = 0x03u8; if self.region == Region::Pal {
431 out |= 0x10;
432 }
433 if self.io.counter_latched {
434 out |= 0x40;
435 }
436 if self.field {
437 out |= 0x80;
438 }
439 out |= self.io.ppu2_mdr & 0x20;
440 self.io.counter_latched = false;
442 self.io.ophct_high_toggle = false;
443 self.io.opvct_high_toggle = false;
444 self.io.ppu2_mdr = out;
445 out
446 }
447 _ => self.io.ppu1_mdr,
449 }
450 }
451}