feat/savestates: Implement save/load state API for GameBoyAdvance

Using serde & bincode encoding


Former-commit-id: f5e4c599497f6bdf3096fa99f8b2d6ce89278ef7
This commit is contained in:
Michel Heily 2020-01-16 20:06:22 +02:00
parent 16142ee99d
commit f4460b2740
18 changed files with 128 additions and 73 deletions

View file

@ -1,6 +1,8 @@
pub mod display; pub mod display;
pub mod exec; pub mod exec;
use serde::{Deserialize, Serialize};
use super::alu::*; use super::alu::*;
use crate::core::arm7tdmi::{Addr, InstructionDecoder, InstructionDecoderError}; use crate::core::arm7tdmi::{Addr, InstructionDecoder, InstructionDecoderError};
@ -38,7 +40,7 @@ impl ArmDecodeError {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Primitive)] #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Primitive)]
pub enum ArmCond { pub enum ArmCond {
EQ = 0b0000, EQ = 0b0000,
NE = 0b0001, NE = 0b0001,
@ -57,7 +59,7 @@ pub enum ArmCond {
AL = 0b1110, AL = 0b1110,
} }
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub enum ArmFormat { pub enum ArmFormat {
/// Branch and Exchange /// Branch and Exchange
@ -97,7 +99,7 @@ pub enum ArmHalfwordTransferType {
SignedHalfwords = 0b11, SignedHalfwords = 0b11,
} }
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)]
pub struct ArmInstruction { pub struct ArmInstruction {
pub cond: ArmCond, pub cond: ArmCond,
pub fmt: ArmFormat, pub fmt: ArmFormat,

View file

@ -1,6 +1,7 @@
use std::fmt; use std::fmt;
use ansi_term::{Colour, Style}; use ansi_term::{Colour, Style};
use serde::{Deserialize, Serialize};
pub use super::exception::Exception; pub use super::exception::Exception;
use super::{ use super::{
@ -13,7 +14,7 @@ use crate::core::sysbus::{
MemoryAccess, MemoryAccessType, MemoryAccessType::*, MemoryAccessWidth::*, SysBus, MemoryAccess, MemoryAccessType, MemoryAccessType::*, MemoryAccessWidth::*, SysBus,
}; };
#[derive(Debug, PartialEq)] #[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
pub enum PipelineState { pub enum PipelineState {
Refill1, Refill1,
Refill2, Refill2,
@ -26,7 +27,7 @@ impl Default for PipelineState {
} }
} }
#[derive(Debug, Default)] #[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct Core { pub struct Core {
pub pc: u32, pub pc: u32,
pub gpr: [u32; 15], pub gpr: [u32; 15],

View file

@ -1,6 +1,7 @@
use std::fmt; use std::fmt;
use num::Num; use num::Num;
use serde::{Deserialize, Serialize};
pub mod arm; pub mod arm;
pub mod thumb; pub mod thumb;
@ -21,7 +22,7 @@ pub const REG_SP: usize = 13;
pub(self) use crate::core::{Addr, Bus}; pub(self) use crate::core::{Addr, Bus};
#[derive(Debug, PartialEq, Copy, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
pub enum DecodedInstruction { pub enum DecodedInstruction {
Arm(ArmInstruction), Arm(ArmInstruction),
Thumb(ThumbInstruction), Thumb(ThumbInstruction),

View file

@ -1,6 +1,8 @@
/// The program status register /// The program status register
use std::fmt; use std::fmt;
use serde::{Deserialize, Serialize};
use crate::bit::BitIndex; use crate::bit::BitIndex;
use crate::num::FromPrimitive; use crate::num::FromPrimitive;
@ -27,7 +29,7 @@ impl From<bool> for CpuState {
} }
} }
#[derive(Debug, Clone, Copy, Default)] #[derive(Serialize, Deserialize, Debug, Clone, Copy, Default)]
pub struct RegPSR { pub struct RegPSR {
raw: u32, raw: u32,
} }

View file

@ -35,7 +35,7 @@ impl ThumbDecodeError {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)]
pub enum ThumbFormat { pub enum ThumbFormat {
/// Format 1 /// Format 1
MoveShiftedReg, MoveShiftedReg,
@ -77,7 +77,7 @@ pub enum ThumbFormat {
BranchLongWithLink, BranchLongWithLink,
} }
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)]
pub struct ThumbInstruction { pub struct ThumbInstruction {
pub fmt: ThumbFormat, pub fmt: ThumbFormat,
pub raw: u16, pub raw: u16,

View file

@ -4,6 +4,8 @@ use std::io::prelude::*;
use std::path::Path; use std::path::Path;
use std::str::from_utf8; use std::str::from_utf8;
use serde::{Deserialize, Serialize};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use zip::ZipArchive; use zip::ZipArchive;
@ -35,7 +37,7 @@ use super::{Addr, Bus, GBAResult};
/// 0C6h 26 Not used (seems to be unused) /// 0C6h 26 Not used (seems to be unused)
/// 0E0h 4 JOYBUS Entry Pt. (32bit ARM branch opcode, eg. "B joy_start") /// 0E0h 4 JOYBUS Entry Pt. (32bit ARM branch opcode, eg. "B joy_start")
/// ///
#[derive(Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CartridgeHeader { pub struct CartridgeHeader {
// rom_entry_point: Addr, // rom_entry_point: Addr,
game_title: String, game_title: String,
@ -69,7 +71,7 @@ impl CartridgeHeader {
} }
} }
#[derive(Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Cartridge { pub struct Cartridge {
pub header: CartridgeHeader, pub header: CartridgeHeader,
bytes: Box<[u8]>, bytes: Box<[u8]>,

View file

@ -2,16 +2,10 @@ use super::iodev::consts::{REG_FIFO_A, REG_FIFO_B};
use super::sysbus::SysBus; use super::sysbus::SysBus;
use super::{Addr, Bus, Interrupt, IrqBitmask}; use super::{Addr, Bus, Interrupt, IrqBitmask};
use bit_set::BitSet;
use num::FromPrimitive; use num::FromPrimitive;
use serde::{Deserialize, Serialize};
#[derive(Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
enum DmaTransferType {
Xfer16bit,
Xfer32bit,
}
#[derive(Debug)]
pub struct DmaChannel { pub struct DmaChannel {
id: usize, id: usize,
@ -30,7 +24,7 @@ pub struct DmaChannel {
irq: Interrupt, irq: Interrupt,
} }
#[derive(Debug, Default)] #[derive(Serialize, Deserialize, Clone, Debug, Default)]
struct DmaInternalRegs { struct DmaInternalRegs {
src_addr: u32, src_addr: u32,
dst_addr: u32, dst_addr: u32,
@ -172,10 +166,10 @@ impl DmaChannel {
} }
} }
#[derive(Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct DmaController { pub struct DmaController {
pub channels: [DmaChannel; 4], pub channels: [DmaChannel; 4],
pending_set: BitSet, pending_set: u8,
cycles: usize, cycles: usize,
} }
@ -188,20 +182,22 @@ impl DmaController {
DmaChannel::new(2), DmaChannel::new(2),
DmaChannel::new(3), DmaChannel::new(3),
], ],
pending_set: BitSet::with_capacity(4), pending_set: 0,
cycles: 0, cycles: 0,
} }
} }
pub fn is_active(&self) -> bool { pub fn is_active(&self) -> bool {
!self.pending_set.is_empty() self.pending_set != 0
} }
pub fn perform_work(&mut self, sb: &mut SysBus, irqs: &mut IrqBitmask) { pub fn perform_work(&mut self, sb: &mut SysBus, irqs: &mut IrqBitmask) {
for id in self.pending_set.iter() { for id in 0..4 {
self.channels[id].xfer(sb, irqs); if self.pending_set & (1 << id) != 0 {
self.channels[id].xfer(sb, irqs);
}
} }
self.pending_set.clear(); self.pending_set = 0;
} }
pub fn write_16(&mut self, channel_id: usize, ofs: u32, value: u16) { pub fn write_16(&mut self, channel_id: usize, ofs: u32, value: u16) {
@ -213,9 +209,9 @@ impl DmaController {
8 => self.channels[channel_id].write_word_count(value), 8 => self.channels[channel_id].write_word_count(value),
10 => { 10 => {
if self.channels[channel_id].write_dma_ctrl(value) { if self.channels[channel_id].write_dma_ctrl(value) {
self.pending_set.insert(channel_id); self.pending_set |= 1 << channel_id;
} else { } else {
self.pending_set.remove(channel_id); self.pending_set &= !(1 << channel_id);
} }
} }
_ => panic!("Invalid dma offset {:x}", ofs), _ => panic!("Invalid dma offset {:x}", ofs),
@ -225,7 +221,7 @@ impl DmaController {
pub fn notify_vblank(&mut self) { pub fn notify_vblank(&mut self) {
for i in 0..4 { for i in 0..4 {
if self.channels[i].ctrl.is_enabled() && self.channels[i].ctrl.timing() == 1 { if self.channels[i].ctrl.is_enabled() && self.channels[i].ctrl.timing() == 1 {
self.pending_set.insert(i); self.pending_set |= 1 << i;
} }
} }
} }
@ -233,7 +229,7 @@ impl DmaController {
pub fn notify_hblank(&mut self) { pub fn notify_hblank(&mut self) {
for i in 0..4 { for i in 0..4 {
if self.channels[i].ctrl.is_enabled() && self.channels[i].ctrl.timing() == 2 { if self.channels[i].ctrl.is_enabled() && self.channels[i].ctrl.timing() == 2 {
self.pending_set.insert(i); self.pending_set |= 1 << i;
} }
} }
} }
@ -245,14 +241,14 @@ impl DmaController {
&& self.channels[i].ctrl.timing() == 3 && self.channels[i].ctrl.timing() == 3
&& self.channels[i].dst == fifo_addr && self.channels[i].dst == fifo_addr
{ {
self.pending_set.insert(i); self.pending_set |= 1 << i;
} }
} }
} }
} }
bitfield! { bitfield! {
#[derive(Default)] #[derive(Serialize, Deserialize, Clone, Default)]
pub struct DmaChannelCtrl(u16); pub struct DmaChannelCtrl(u16);
impl Debug; impl Debug;
u16; u16;

View file

@ -2,6 +2,9 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use bincode;
use serde::{Deserialize, Serialize};
use super::arm7tdmi::Core; use super::arm7tdmi::Core;
use super::cartridge::Cartridge; use super::cartridge::Cartridge;
use super::gpu::*; use super::gpu::*;
@ -23,6 +26,12 @@ pub struct GameBoyAdvance {
cycles_to_next_event: usize, cycles_to_next_event: usize,
} }
#[derive(Serialize, Deserialize)]
struct SaveState {
sysbus: Box<SysBus>,
cpu: Core,
}
impl GameBoyAdvance { impl GameBoyAdvance {
pub fn new( pub fn new(
cpu: Core, cpu: Core,
@ -49,6 +58,25 @@ impl GameBoyAdvance {
} }
} }
pub fn save_state(&self) -> bincode::Result<Vec<u8>> {
let s = SaveState {
cpu: self.cpu.clone(),
sysbus: self.sysbus.clone(),
};
bincode::serialize(&s)
}
pub fn restore_state(&mut self, bytes: &[u8]) -> bincode::Result<()> {
let decoded: Box<SaveState> = bincode::deserialize_from(bytes)?;
self.cpu = decoded.cpu;
self.sysbus = decoded.sysbus;
self.cycles_to_next_event = 1;
Ok(())
}
#[inline] #[inline]
pub fn key_poll(&mut self) { pub fn key_poll(&mut self) {
self.sysbus.io.keyinput = self.input_device.borrow_mut().poll(); self.sysbus.io.keyinput = self.input_device.borrow_mut().poll();

View file

@ -2,6 +2,8 @@ use std::cell::RefCell;
use std::fmt; use std::fmt;
use std::rc::Rc; use std::rc::Rc;
use serde::{Deserialize, Serialize};
use super::super::VideoInterface; use super::super::VideoInterface;
use super::interrupt::IrqBitmask; use super::interrupt::IrqBitmask;
use super::sysbus::{BoxedMemory, SysBus}; use super::sysbus::{BoxedMemory, SysBus};
@ -50,7 +52,7 @@ pub enum PixelFormat {
BPP8 = 1, BPP8 = 1,
} }
#[derive(Debug, PartialEq, Copy, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
pub enum GpuState { pub enum GpuState {
HDraw = 0, HDraw = 0,
HBlank, HBlank,
@ -95,7 +97,7 @@ impl std::ops::IndexMut<usize> for Scanline {
} }
} }
#[derive(Debug, Default, Copy, Clone)] #[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct Background { pub struct Background {
pub bgcnt: BgControl, pub bgcnt: BgControl,
pub bgvofs: u16, pub bgvofs: u16,
@ -107,7 +109,7 @@ pub struct Background {
mosaic_first_row: Scanline, mosaic_first_row: Scanline,
} }
#[derive(Debug, Default)] #[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct Window { pub struct Window {
pub left: u8, pub left: u8,
pub right: u8, pub right: u8,
@ -151,7 +153,7 @@ pub struct AffineMatrix {
pub pd: i32, pub pd: i32,
} }
#[derive(Debug, Default, Copy, Clone)] #[derive(Serialize, Deserialize, Debug, Default, Copy, Clone)]
pub struct BgAffine { pub struct BgAffine {
pub pa: i16, // dx pub pa: i16, // dx
pub pb: i16, // dmx pub pb: i16, // dmx
@ -163,7 +165,7 @@ pub struct BgAffine {
pub internal_y: i32, pub internal_y: i32,
} }
#[derive(Debug, Copy, Clone)] #[derive(Serialize, Deserialize, Debug, Copy, Clone)]
pub struct ObjBufferEntry { pub struct ObjBufferEntry {
pub(super) color: Rgb15, pub(super) color: Rgb15,
pub(super) priority: u16, pub(super) priority: u16,
@ -182,7 +184,7 @@ impl Default for ObjBufferEntry {
type VideoDeviceRcRefCell = Rc<RefCell<dyn VideoInterface>>; type VideoDeviceRcRefCell = Rc<RefCell<dyn VideoInterface>>;
#[derive(DebugStub)] #[derive(Serialize, Deserialize, Clone, DebugStub)]
pub struct Gpu { pub struct Gpu {
pub state: GpuState, pub state: GpuState,

View file

@ -1,6 +1,8 @@
use super::sfx::BldMode; use super::sfx::BldMode;
use super::*; use super::*;
use serde::{Deserialize, Serialize};
pub const SCREEN_BLOCK_SIZE: u32 = 0x800; pub const SCREEN_BLOCK_SIZE: u32 = 0x800;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -60,6 +62,7 @@ impl BgControl {
// struct definitions below because the bitfield! macro messes up syntax highlighting in vscode. // struct definitions below because the bitfield! macro messes up syntax highlighting in vscode.
bitfield! { bitfield! {
#[derive(Serialize, Deserialize, Clone)]
pub struct DisplayControl(u16); pub struct DisplayControl(u16);
impl Debug; impl Debug;
u16; u16;
@ -79,6 +82,7 @@ bitfield! {
} }
bitfield! { bitfield! {
#[derive(Serialize, Deserialize, Clone)]
pub struct DisplayStatus(u16); pub struct DisplayStatus(u16);
impl Debug; impl Debug;
u16; u16;
@ -92,7 +96,7 @@ bitfield! {
} }
bitfield! { bitfield! {
#[derive(Default, Copy, Clone)] #[derive(Serialize, Deserialize, Default, Copy, Clone)]
pub struct BgControl(u16); pub struct BgControl(u16);
impl Debug; impl Debug;
u16; u16;
@ -106,7 +110,7 @@ bitfield! {
} }
bitfield! { bitfield! {
#[derive(Default, Copy, Clone)] #[derive(Serialize, Deserialize, Default, Copy, Clone)]
pub struct RegMosaic(u16); pub struct RegMosaic(u16);
impl Debug; impl Debug;
u32; u32;
@ -117,7 +121,7 @@ bitfield! {
} }
bitflags! { bitflags! {
#[derive(Default)] #[derive(Serialize, Deserialize, Default)]
pub struct BlendFlags: u32 { pub struct BlendFlags: u32 {
const BG0 = 0b00000001; const BG0 = 0b00000001;
const BG1 = 0b00000010; const BG1 = 0b00000010;
@ -148,7 +152,7 @@ impl BlendFlags {
} }
bitfield! { bitfield! {
#[derive(Default, Copy, Clone)] #[derive(Serialize, Deserialize, Default, Copy, Clone)]
pub struct BlendControl(u16); pub struct BlendControl(u16);
impl Debug; impl Debug;
pub into BlendFlags, top, _: 5, 0; pub into BlendFlags, top, _: 5, 0;
@ -157,7 +161,7 @@ bitfield! {
} }
bitfield! { bitfield! {
#[derive(Default, Copy, Clone)] #[derive(Serialize, Deserialize, Default, Copy, Clone)]
pub struct BlendAlpha(u16); pub struct BlendAlpha(u16);
impl Debug; impl Debug;
u16; u16;
@ -166,7 +170,7 @@ bitfield! {
} }
bitflags! { bitflags! {
#[derive(Default)] #[derive(Serialize, Deserialize, Default)]
pub struct WindowFlags: u32 { pub struct WindowFlags: u32 {
const BG0 = 0b00000001; const BG0 = 0b00000001;
const BG1 = 0b00000010; const BG1 = 0b00000010;
@ -200,7 +204,7 @@ const BG_WIN_FLAG: [WindowFlags; 4] = [
]; ];
bitfield! { bitfield! {
#[derive(Default, Copy, Clone)] #[derive(Serialize, Deserialize, Default, Copy, Clone)]
pub struct WindowReg(u16); pub struct WindowReg(u16);
impl Debug; impl Debug;
u16; u16;

View file

@ -1,8 +1,10 @@
//! Helper type to deal with the GBA's 15bit color //! Helper type to deal with the GBA's 15bit color
use serde::{Deserialize, Serialize};
bitfield! { bitfield! {
#[repr(transparent)] #[repr(transparent)]
#[derive(Copy, Clone, Default, PartialEq)] #[derive(Serialize, Deserialize, Copy, Clone, Default, PartialEq)]
pub struct Rgb15(u16); pub struct Rgb15(u16);
impl Debug; impl Debug;
pub r, set_r: 4, 0; pub r, set_r: 4, 0;

View file

@ -1,4 +1,6 @@
#[derive(Debug, Primitive, Copy, Clone, PartialEq)] use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Primitive, Copy, Clone, PartialEq)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub enum Interrupt { pub enum Interrupt {
LCD_VBlank = 0, LCD_VBlank = 0,
@ -17,7 +19,7 @@ pub enum Interrupt {
GamePak = 13, GamePak = 13,
} }
#[derive(Debug, Default)] #[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct InterruptController { pub struct InterruptController {
pub interrupt_master_enable: bool, pub interrupt_master_enable: bool,
pub interrupt_enable: IrqBitmask, pub interrupt_enable: IrqBitmask,
@ -48,7 +50,7 @@ impl IrqBitmask {
} }
bitfield! { bitfield! {
#[derive(Default, Copy, Clone, PartialEq)] #[derive(Serialize, Deserialize, Default, Copy, Clone, PartialEq)]
pub struct IrqBitmask(u16); pub struct IrqBitmask(u16);
impl Debug; impl Debug;
u16; u16;

View file

@ -7,15 +7,18 @@ use super::sound::SoundController;
use super::timer::Timers; use super::timer::Timers;
use super::{Addr, Bus}; use super::{Addr, Bus};
use serde::{Deserialize, Serialize};
use self::consts::*; use self::consts::*;
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
pub enum HaltState { pub enum HaltState {
Running, Running,
Halt, // In Halt mode, the CPU is paused as long as (IE AND IF)=0, Halt, // In Halt mode, the CPU is paused as long as (IE AND IF)=0,
Stop, // In Stop mode, most of the hardware including sound and video are paused Stop, // In Stop mode, most of the hardware including sound and video are paused
} }
#[derive(Clone, Serialize, Deserialize)]
pub struct IoDevices { pub struct IoDevices {
pub intc: InterruptController, pub intc: InterruptController,
pub gpu: Box<Gpu>, pub gpu: Box<Gpu>,
@ -262,7 +265,7 @@ impl Bus for IoDevices {
} }
bitfield! { bitfield! {
#[derive(Default, Copy, Clone, PartialEq)] #[derive(Serialize, Deserialize, Default, Copy, Clone, PartialEq)]
pub struct WaitControl(u16); pub struct WaitControl(u16);
impl Debug; impl Debug;
u16; u16;

View file

@ -1,12 +1,14 @@
use crate::{AudioInterface, StereoSample}; use crate::{AudioInterface, StereoSample};
use serde::{Deserialize, Serialize};
const PI: f32 = std::f32::consts::PI; const PI: f32 = std::f32::consts::PI;
pub trait Resampler { pub trait Resampler {
fn push_sample(&mut self, s: StereoSample, audio: &mut dyn AudioInterface); fn push_sample(&mut self, s: StereoSample, audio: &mut dyn AudioInterface);
} }
#[derive(Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CosineResampler { pub struct CosineResampler {
last_in_sample: StereoSample, last_in_sample: StereoSample,
phase: f32, phase: f32,

View file

@ -1,7 +1,9 @@
// TODO write tests or replace with a crate // TODO write tests or replace with a crate
const SOUND_FIFO_CAPACITY: usize = 32; const SOUND_FIFO_CAPACITY: usize = 32;
#[derive(Debug)] use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SoundFifo { pub struct SoundFifo {
wr_pos: usize, wr_pos: usize,
rd_pos: usize, rd_pos: usize,

View file

@ -2,6 +2,7 @@ use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use bit::BitIndex; use bit::BitIndex;
use serde::{Deserialize, Serialize};
use super::dma::DmaController; use super::dma::DmaController;
use super::iodev::consts::*; use super::iodev::consts::*;
@ -18,10 +19,7 @@ const DMG_RATIOS: [f32; 4] = [0.25, 0.5, 1.0, 0.0];
const DMA_TIMERS: [usize; 2] = [0, 1]; const DMA_TIMERS: [usize; 2] = [0, 1];
const DUTY_RATIOS: [f32; 4] = [0.125, 0.25, 0.5, 0.75]; const DUTY_RATIOS: [f32; 4] = [0.125, 0.25, 0.5, 0.75];
#[derive(Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
struct NoiseChannel {}
#[derive(Debug)]
struct DmaSoundChannel { struct DmaSoundChannel {
value: i8, value: i8,
volume_shift: i16, volume_shift: i16,
@ -62,7 +60,7 @@ const REG_FIFO_B_H: u32 = REG_FIFO_B + 2;
type AudioDeviceRcRefCell = Rc<RefCell<dyn AudioInterface>>; type AudioDeviceRcRefCell = Rc<RefCell<dyn AudioInterface>>;
#[derive(DebugStub)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SoundController { pub struct SoundController {
sample_rate_to_cpu_freq: usize, // how many "cycles" are a sample? sample_rate_to_cpu_freq: usize, // how many "cycles" are a sample?
cycles: usize, // cycles count when we last provided a new sample. cycles: usize, // cycles count when we last provided a new sample.

View file

@ -1,6 +1,8 @@
use std::fmt; use std::fmt;
use std::ops::Add; use std::ops::Add;
use serde::{Deserialize, Serialize};
use super::cartridge::Cartridge; use super::cartridge::Cartridge;
use super::gpu::{GpuState, VIDEO_RAM_SIZE}; use super::gpu::{GpuState, VIDEO_RAM_SIZE};
use super::iodev::IoDevices; use super::iodev::IoDevices;
@ -64,7 +66,7 @@ impl fmt::Display for MemoryAccess {
} }
} }
#[derive(Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct BoxedMemory { pub struct BoxedMemory {
pub mem: Box<[u8]>, pub mem: Box<[u8]>,
} }
@ -77,15 +79,17 @@ impl BoxedMemory {
impl Bus for BoxedMemory { impl Bus for BoxedMemory {
fn read_8(&self, addr: Addr) -> u8 { fn read_8(&self, addr: Addr) -> u8 {
self.mem[addr as usize] unsafe { *self.mem.get_unchecked(addr as usize) }
} }
fn write_8(&mut self, addr: Addr, value: u8) { fn write_8(&mut self, addr: Addr, value: u8) {
self.mem[addr as usize] = value; unsafe {
*self.mem.get_unchecked_mut(addr as usize) = value;
}
} }
} }
#[derive(Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
struct DummyBus([u8; 4]); struct DummyBus([u8; 4]);
impl Bus for DummyBus { impl Bus for DummyBus {
@ -96,6 +100,7 @@ impl Bus for DummyBus {
fn write_8(&mut self, _addr: Addr, _value: u8) {} fn write_8(&mut self, _addr: Addr, _value: u8) {}
} }
#[derive(Serialize, Deserialize, Clone)]
pub struct SysBus { pub struct SysBus {
pub io: IoDevices, pub io: IoDevices,

View file

@ -2,11 +2,10 @@ use super::interrupt::{Interrupt, IrqBitmask};
use super::iodev::consts::*; use super::iodev::consts::*;
use super::sysbus::SysBus; use super::sysbus::SysBus;
use bit_set::BitSet;
use num::FromPrimitive; use num::FromPrimitive;
use serde::{Deserialize, Serialize};
#[derive(Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Timer { pub struct Timer {
// registers // registers
pub ctl: TimerCtl, pub ctl: TimerCtl,
@ -66,10 +65,10 @@ impl Timer {
} }
} }
#[derive(Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Timers { pub struct Timers {
timers: [Timer; 4], timers: [Timer; 4],
running_timers: BitSet, running_timers: u8,
pub trace: bool, pub trace: bool,
} }
@ -90,7 +89,7 @@ impl Timers {
pub fn new() -> Timers { pub fn new() -> Timers {
Timers { Timers {
timers: [Timer::new(0), Timer::new(1), Timer::new(2), Timer::new(3)], timers: [Timer::new(0), Timer::new(1), Timer::new(2), Timer::new(3)],
running_timers: BitSet::with_capacity(4), running_timers: 0,
trace: false, trace: false,
} }
} }
@ -101,9 +100,9 @@ impl Timers {
let new_enabled = self[id].ctl.enabled(); let new_enabled = self[id].ctl.enabled();
let cascade = self.timers[id].ctl.cascade(); let cascade = self.timers[id].ctl.cascade();
if new_enabled && !cascade { if new_enabled && !cascade {
self.running_timers.insert(id); self.running_timers |= 1 << id;
} else { } else {
self.running_timers.remove(id); self.running_timers &= !(1 << id);
} }
if self.trace && old_enabled != new_enabled { if self.trace && old_enabled != new_enabled {
println!( println!(
@ -158,7 +157,11 @@ impl Timers {
} }
pub fn update(&mut self, cycles: usize, sb: &mut SysBus, irqs: &mut IrqBitmask) { pub fn update(&mut self, cycles: usize, sb: &mut SysBus, irqs: &mut IrqBitmask) {
for id in self.running_timers.iter() { for id in 0..4 {
if self.running_timers & (1 << id) == 0 {
continue;
}
if !self.timers[id].ctl.cascade() { if !self.timers[id].ctl.cascade() {
let timer = &mut self.timers[id]; let timer = &mut self.timers[id];
let num_overflows = timer.update(cycles, irqs); let num_overflows = timer.update(cycles, irqs);
@ -181,7 +184,7 @@ impl Timers {
} }
bitfield! { bitfield! {
#[derive(Default)] #[derive(Serialize, Deserialize, Clone, Default)]
pub struct TimerCtl(u16); pub struct TimerCtl(u16);
impl Debug; impl Debug;
u16; u16;