1use crate::bus::SystemBus;
40use crate::controller::Controller;
41use crate::input_device::{
42 FamilyKeyboardState, InputDevice, SnesMouseState, VausState, ZapperState,
43};
44use crate::save_state::{BinReader, BinWriter, SnapshotError};
45use alloc::format;
46use alloc::vec::Vec;
47
48pub const BUS_SECTION_VERSION: u8 = 3;
53
54pub const EXPANSION_DEVICE_MAX_LEN: usize = 14;
60
61pub const SAVE_STATE_DEVICE_HEADROOM: usize = 2 * (EXPANSION_DEVICE_MAX_LEN - 1);
73
74pub fn encode_bus(bus: &SystemBus) -> Vec<u8> {
77 let mut w = BinWriter::with_capacity(0x900);
78 w.u64(bus.cycle());
80 w.bytes(bus.ram_bytes());
82 for c in bus.controllers_ref() {
84 encode_controller(&mut w, *c);
85 }
86 let s = bus.bus_misc_state();
87 w.u8(s.dma_pending.unwrap_or(0));
89 w.u8(u8::from(s.dma_pending.is_some()));
90 w.u8(s.dma_byte);
92 w.u8(s.dma_page);
93 w.u16(s.dma_halt_addr);
94 w.u8(s.open_bus);
96 w.u16(s.last_read_addr);
97 w.u8(u8::from(s.in_dmc_dma));
98 w.u16(s.deferred_dma_replay_addr);
99 w.u8(s.controller_write_pending);
101 w.u8(s.controller_write_value);
102 w.u8(u8::from(s.four_score));
105 for c in bus.controllers34_ref() {
106 encode_controller(&mut w, *c);
107 }
108 w.u8(s.four_score_idx[0]);
109 w.u8(s.four_score_idx[1]);
110 w.u8(s.four_score_sig[0]);
111 w.u8(s.four_score_sig[1]);
112 w.u8(u8::from(s.dmc_halt));
114 w.u8(u8::from(s.uni_oam_active));
115 w.u8(u8::from(s.uni_oam_halt));
116 w.u8(u8::from(s.uni_oam_aligned));
117 w.u16(s.uni_oam_addr);
118 w.u64(s.ppu_clock);
123 for port in 0..2 {
128 encode_expansion_device(&mut w, bus.expansion_device(port).as_ref());
129 }
130 w.u8(encode_mirroring_override(bus.mirroring_override()));
132 for c in bus.controllers_ref() {
138 w.bool(c.pending_shift);
139 }
140 for c in bus.controllers34_ref() {
141 w.bool(c.pending_shift);
142 }
143 for port in 0..2 {
144 w.u64(bus.port_read_cycle(port));
145 }
146 for f in bus.four_score_pending() {
148 w.bool(f);
149 }
150 w.u8(s.internal_data_bus);
154 w.u8(u8::from(s.dmc_load_write_delayed));
156 w.u8(s.overclock_phase);
158 w.u64(s.apu_cycle);
159 w.into_vec()
160}
161
162const fn encode_mirroring_override(m: Option<rustynes_mappers::Mirroring>) -> u8 {
164 use rustynes_mappers::Mirroring;
165 match m {
166 None => 0,
167 Some(Mirroring::Horizontal) => 1,
168 Some(Mirroring::Vertical) => 2,
169 Some(Mirroring::SingleScreenA) => 3,
170 Some(Mirroring::SingleScreenB) => 4,
171 Some(Mirroring::FourScreen) => 5,
172 Some(Mirroring::MapperControlled) => 6,
173 }
174}
175
176const fn decode_mirroring_override(tag: u8) -> Option<rustynes_mappers::Mirroring> {
178 use rustynes_mappers::Mirroring;
179 match tag {
180 1 => Some(Mirroring::Horizontal),
181 2 => Some(Mirroring::Vertical),
182 3 => Some(Mirroring::SingleScreenA),
183 4 => Some(Mirroring::SingleScreenB),
184 5 => Some(Mirroring::FourScreen),
185 6 => Some(Mirroring::MapperControlled),
186 _ => None,
187 }
188}
189
190fn encode_expansion_device(w: &mut BinWriter, device: Option<&InputDevice>) {
192 match device {
193 None => w.u8(0),
194 Some(InputDevice::Zapper(z)) => {
195 w.u8(1);
196 w.u16(z.x_raw());
197 w.u16(z.y_raw());
198 w.bool(z.trigger_raw());
199 w.bool(z.light_seen_raw());
200 }
201 Some(InputDevice::Vaus(v)) => {
202 w.u8(2);
203 w.u8(v.position_raw());
204 w.bool(v.fire_raw());
205 w.u8(v.shift_raw());
206 w.bool(v.strobe_raw());
207 }
208 Some(InputDevice::PowerPad(p)) => {
209 w.u8(3);
210 w.u16(p.buttons_raw());
211 w.u8(p.shift_l_raw());
212 w.u8(p.shift_h_raw());
213 w.bool(p.strobe_raw());
214 }
215 Some(InputDevice::SnesMouse(m)) => {
216 w.u8(4);
217 w.i16(m.dx_raw());
218 w.i16(m.dy_raw());
219 w.bool(m.left_raw());
220 w.bool(m.right_raw());
221 w.u8(m.sensitivity_raw());
222 w.u32(m.shift_raw());
223 w.u8(m.read_count_raw());
224 w.bool(m.strobe_raw());
225 }
226 Some(InputDevice::FamilyKeyboard(k)) => {
227 w.u8(5);
228 w.bytes(&k.keys_raw());
229 w.u8(k.row_raw());
230 w.bool(k.column_raw());
231 w.bool(k.enabled_raw());
232 w.bool(k.clock_raw());
233 }
234 Some(InputDevice::FamilyTrainer(p)) => {
238 w.u8(6);
239 w.u16(p.buttons_raw());
240 w.u8(p.shift_l_raw());
241 w.u8(p.shift_h_raw());
242 w.bool(p.strobe_raw());
243 }
244 Some(InputDevice::SuborKeyboard(k)) => {
245 w.u8(7);
246 w.bytes(&k.keys_raw());
247 w.u8(k.row_raw());
248 w.bool(k.column_raw());
249 w.bool(k.enabled_raw());
250 w.bool(k.clock_raw());
251 }
252 Some(InputDevice::KonamiHyperShot(h)) => {
253 w.u8(8);
254 w.u8(h.buttons_raw());
255 w.bool(h.p1_enabled_raw());
256 w.bool(h.p2_enabled_raw());
257 }
258 Some(InputDevice::BandaiHyperShot(b)) => {
259 w.u8(9);
260 w.u8(b.sensors_raw());
261 w.bool(b.select_raw());
262 }
263 }
264}
265
266#[allow(clippy::too_many_lines)]
270fn decode_expansion_device(r: &mut BinReader<'_>) -> Result<Option<InputDevice>, SnapshotError> {
271 Ok(match r.u8()? {
272 0 => None,
273 1 => {
274 let x = r.u16()?;
275 let y = r.u16()?;
276 let trigger = r.bool()?;
277 let light_seen = r.bool()?;
278 Some(InputDevice::Zapper(ZapperState::from_parts(
279 x, y, trigger, light_seen,
280 )))
281 }
282 2 => {
283 let position = r.u8()?;
284 let fire = r.bool()?;
285 let shift = r.u8()?;
286 let strobe = r.bool()?;
287 Some(InputDevice::Vaus(VausState::from_parts(
288 position, fire, shift, strobe,
289 )))
290 }
291 3 => {
292 let buttons = r.u16()?;
293 let shift_l = r.u8()?;
294 let shift_h = r.u8()?;
295 let strobe = r.bool()?;
296 Some(InputDevice::PowerPad(
297 crate::input_device::PowerPadState::from_parts(buttons, shift_l, shift_h, strobe),
298 ))
299 }
300 4 => {
301 let dx = r.i16()?;
302 let dy = r.i16()?;
303 let left = r.bool()?;
304 let right = r.bool()?;
305 let sensitivity = r.u8()?;
306 let shift = r.u32()?;
307 let read_count = r.u8()?;
308 let strobe = r.bool()?;
309 Some(InputDevice::SnesMouse(SnesMouseState::from_parts(
310 dx,
311 dy,
312 left,
313 right,
314 sensitivity,
315 shift,
316 read_count,
317 strobe,
318 )))
319 }
320 5 => {
321 let mut keys = [0u8; 9];
322 r.read_into(&mut keys)?;
323 let row = r.u8()?;
324 let column = r.bool()?;
325 let enabled = r.bool()?;
326 let clock = r.bool()?;
327 Some(InputDevice::FamilyKeyboard(
328 FamilyKeyboardState::from_parts(keys, row, column, enabled, clock),
329 ))
330 }
331 6 => {
332 let buttons = r.u16()?;
333 let shift_l = r.u8()?;
334 let shift_h = r.u8()?;
335 let strobe = r.bool()?;
336 Some(InputDevice::FamilyTrainer(
337 crate::input_device::PowerPadState::from_parts(buttons, shift_l, shift_h, strobe),
338 ))
339 }
340 7 => {
341 let mut keys = [0u8; 9];
342 r.read_into(&mut keys)?;
343 let row = r.u8()?;
344 let column = r.bool()?;
345 let enabled = r.bool()?;
346 let clock = r.bool()?;
347 Some(InputDevice::SuborKeyboard(FamilyKeyboardState::from_parts(
348 keys, row, column, enabled, clock,
349 )))
350 }
351 8 => {
352 let buttons = r.u8()?;
353 let p1_enabled = r.bool()?;
354 let p2_enabled = r.bool()?;
355 Some(InputDevice::KonamiHyperShot(
356 crate::input_device::KonamiHyperShotState::from_parts(
357 buttons, p1_enabled, p2_enabled,
358 ),
359 ))
360 }
361 9 => {
362 let sensors = r.u8()?;
363 let select = r.bool()?;
364 Some(InputDevice::BandaiHyperShot(
365 crate::input_device::BandaiHyperShotState::from_parts(sensors, select),
366 ))
367 }
368 other => {
369 return Err(SnapshotError::SectionInvalid {
370 tag: "BUS ".into(),
371 reason: format!("unknown expansion-device tag {other}"),
372 });
373 }
374 })
375}
376
377#[allow(clippy::too_many_lines)]
389pub fn decode_bus(bus: &mut SystemBus, data: &[u8]) -> Result<(), SnapshotError> {
390 let mut r = BinReader::new(data);
391 let cycle = r.u64()?;
392 let ram = r.take(0x800)?;
393 bus.set_cycle(cycle);
394 bus.set_ram_bytes(ram)?;
395 let mut controllers = [Controller::new(); 2];
396 for c in &mut controllers {
397 decode_controller(&mut r, c)?;
398 }
399 bus.set_controllers(controllers);
400
401 let dma_page_pending = r.u8()?;
402 let dma_present = r.u8()?;
403 let dma_pending = match dma_present {
404 0 => None,
405 1 => Some(dma_page_pending),
406 other => {
407 return Err(SnapshotError::SectionInvalid {
408 tag: "BUS ".into(),
409 reason: format!("invalid dma-pending presence {other}"),
410 });
411 }
412 };
413 let dma_byte = r.u8()?;
414 let dma_page = r.u8()?;
415 let dma_halt_addr = r.u16()?;
416 let open_bus = r.u8()?;
417 let last_read_addr = r.u16()?;
418 let in_dmc_dma = r.bool()?;
419 let deferred_dma_replay_addr = r.u16()?;
420 let controller_write_pending = r.u8()?;
421 let controller_write_value = r.u8()?;
422 let four_score = r.bool()?;
423 let mut controllers34 = [Controller::new(); 2];
424 for c in &mut controllers34 {
425 decode_controller(&mut r, c)?;
426 }
427 bus.set_controllers34(controllers34);
428 let four_score_idx = [r.u8()?, r.u8()?];
429 let four_score_sig = [r.u8()?, r.u8()?];
430 let dmc_halt = r.bool()?;
431 let uni_oam_active = r.bool()?;
432 let uni_oam_halt = r.bool()?;
433 let uni_oam_aligned = r.bool()?;
434 let uni_oam_addr = r.u16()?;
435 if uni_oam_addr > 256 || (uni_oam_active && uni_oam_addr > 255) {
442 return Err(SnapshotError::SectionInvalid {
443 tag: "BUS ".into(),
444 reason: format!(
445 "OAM-DMA byte index {uni_oam_addr} out of range (active: {uni_oam_active})"
446 ),
447 });
448 }
449 let ppu_clock = r.u64()?;
450 let device0 = decode_expansion_device(&mut r)?;
451 let device1 = decode_expansion_device(&mut r)?;
452 bus.set_expansion_device(0, device0);
453 bus.set_expansion_device(1, device1);
454 bus.set_mirroring_override(decode_mirroring_override(r.u8()?));
455 let mut pending = [false; 4];
456 for p in &mut pending {
457 *p = r.bool()?;
458 }
459 let mut cycles = [0u64; 2];
460 for c in &mut cycles {
461 *c = r.u64()?;
462 }
463 bus.set_controller_run_state(pending, cycles);
466 let mut fs = [false; 2];
467 for f in &mut fs {
468 *f = r.bool()?;
469 }
470 bus.set_four_score_pending(fs);
471 let internal_data_bus = r.u8()?;
472 let dmc_load_write_delayed = r.bool()?;
473 let overclock_phase = r.u8()?;
474 if overclock_phase >= crate::MAX_CPU_OVERCLOCK {
480 return Err(SnapshotError::SectionInvalid {
481 tag: "BUS ".into(),
482 reason: format!("CPU-overclock phase {overclock_phase} out of range"),
483 });
484 }
485 let apu_cycle = r.u64()?;
486 if r.remaining() != 0 {
487 return Err(SnapshotError::SectionInvalid {
488 tag: "BUS ".into(),
489 reason: format!("{} unexpected trailing bytes", r.remaining()),
490 });
491 }
492 bus.set_bus_misc_state(BusMiscState {
493 dma_pending,
494 dma_byte,
495 dma_page,
496 dma_halt_addr,
497 deferred_dma_replay_addr,
498 open_bus,
499 internal_data_bus,
500 last_read_addr,
501 in_dmc_dma,
502 controller_write_pending,
503 controller_write_value,
504 four_score,
505 four_score_idx,
506 four_score_sig,
507 dmc_halt,
508 dmc_load_write_delayed,
509 overclock_phase,
510 apu_cycle,
511 uni_oam_active,
512 uni_oam_halt,
513 uni_oam_aligned,
514 uni_oam_addr,
515 ppu_clock,
516 });
517 Ok(())
518}
519
520fn encode_controller(w: &mut BinWriter, c: Controller) {
521 w.u8(c.buttons.bits());
522 w.u8(c.shift);
523 w.bool(c.strobe);
524}
525fn decode_controller(r: &mut BinReader<'_>, c: &mut Controller) -> Result<(), SnapshotError> {
526 let bits = r.u8()?;
527 c.buttons = crate::controller::Buttons::from_bits_truncate(bits);
528 c.shift = r.u8()?;
529 c.strobe = r.bool()?;
530 Ok(())
531}
532
533#[derive(Debug, Clone, Copy)]
538#[allow(clippy::struct_excessive_bools)] pub struct BusMiscState {
540 pub dma_pending: Option<u8>,
542 pub dma_byte: u8,
544 pub dma_page: u8,
546 pub dma_halt_addr: u16,
548 pub deferred_dma_replay_addr: u16,
550 pub open_bus: u8,
552 pub internal_data_bus: u8,
555 pub last_read_addr: u16,
557 pub in_dmc_dma: bool,
559 pub controller_write_pending: u8,
563 pub controller_write_value: u8,
566 pub four_score: bool,
568 pub four_score_idx: [u8; 2],
570 pub four_score_sig: [u8; 2],
572 pub dmc_halt: bool,
575 pub dmc_load_write_delayed: bool,
578 pub overclock_phase: u8,
582 pub apu_cycle: u64,
585 pub uni_oam_active: bool,
588 pub uni_oam_halt: bool,
590 pub uni_oam_aligned: bool,
592 pub uni_oam_addr: u16,
595 pub ppu_clock: u64,
598}
599
600#[cfg(test)]
601mod tests {
602 use super::*;
603 use crate::input_device::{
604 BandaiHyperShotState, KonamiHyperShotState, PowerPadState, SnesMouseState,
605 };
606 use alloc::vec;
607
608 fn every_device() -> Vec<InputDevice> {
612 let all = vec![
613 InputDevice::Zapper(ZapperState::from_parts(0, 0, false, false)),
614 InputDevice::Vaus(VausState::from_parts(0, false, 0, false)),
615 InputDevice::PowerPad(PowerPadState::from_parts(0, 0, 0, false)),
616 InputDevice::SnesMouse(SnesMouseState::from_parts(
617 0, 0, false, false, 0, 0, 0, false,
618 )),
619 InputDevice::FamilyKeyboard(FamilyKeyboardState::from_parts(
620 [0; 9], 0, false, false, false,
621 )),
622 InputDevice::FamilyTrainer(PowerPadState::from_parts(0, 0, 0, false)),
623 InputDevice::SuborKeyboard(FamilyKeyboardState::from_parts(
624 [0; 9], 0, false, false, false,
625 )),
626 InputDevice::KonamiHyperShot(KonamiHyperShotState::from_parts(0, false, false)),
627 InputDevice::BandaiHyperShot(BandaiHyperShotState::from_parts(0, false)),
628 ];
629 for d in &all {
630 match d {
631 InputDevice::Zapper(_)
632 | InputDevice::Vaus(_)
633 | InputDevice::PowerPad(_)
634 | InputDevice::SnesMouse(_)
635 | InputDevice::FamilyKeyboard(_)
636 | InputDevice::FamilyTrainer(_)
637 | InputDevice::SuborKeyboard(_)
638 | InputDevice::KonamiHyperShot(_)
639 | InputDevice::BandaiHyperShot(_) => {}
640 }
641 }
642 all
643 }
644
645 fn encoded_len(device: Option<&InputDevice>) -> usize {
646 let mut w = BinWriter::with_capacity(32);
647 encode_expansion_device(&mut w, device);
648 w.into_vec().len()
649 }
650
651 #[test]
652 fn no_expansion_device_encodes_past_the_documented_maximum() {
653 assert_eq!(
654 encoded_len(None),
655 1,
656 "an unplugged port is the tag byte alone"
657 );
658 let largest = every_device()
659 .iter()
660 .map(|d| encoded_len(Some(d)))
661 .max()
662 .expect("at least one device");
663 assert_eq!(
664 largest, EXPANSION_DEVICE_MAX_LEN,
665 "EXPANSION_DEVICE_MAX_LEN must be the largest device encoding exactly: \
666 larger and the libretro core's save-state buffer is too small, smaller \
667 and it is merely wasteful but the documented figure is wrong"
668 );
669 }
670}