From f4460b274084ab4c443f88db473c87790f52191d Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Thu, 16 Jan 2020 20:06:22 +0200 Subject: [PATCH] feat/savestates: Implement save/load state API for GameBoyAdvance Using serde & bincode encoding Former-commit-id: f5e4c599497f6bdf3096fa99f8b2d6ce89278ef7 --- src/core/arm7tdmi/arm/mod.rs | 8 ++++--- src/core/arm7tdmi/cpu.rs | 5 +++-- src/core/arm7tdmi/mod.rs | 3 ++- src/core/arm7tdmi/psr.rs | 4 +++- src/core/arm7tdmi/thumb/mod.rs | 4 ++-- src/core/cartridge.rs | 6 +++-- src/core/dma.rs | 40 +++++++++++++++------------------- src/core/gba.rs | 28 ++++++++++++++++++++++++ src/core/gpu/mod.rs | 14 +++++++----- src/core/gpu/regs.rs | 18 +++++++++------ src/core/gpu/rgb15.rs | 4 +++- src/core/interrupt.rs | 8 ++++--- src/core/iodev.rs | 7 ++++-- src/core/sound/dsp.rs | 4 +++- src/core/sound/fifo.rs | 4 +++- src/core/sound/mod.rs | 8 +++---- src/core/sysbus.rs | 13 +++++++---- src/core/timer.rs | 23 ++++++++++--------- 18 files changed, 128 insertions(+), 73 deletions(-) diff --git a/src/core/arm7tdmi/arm/mod.rs b/src/core/arm7tdmi/arm/mod.rs index 226b563..f1979ea 100644 --- a/src/core/arm7tdmi/arm/mod.rs +++ b/src/core/arm7tdmi/arm/mod.rs @@ -1,6 +1,8 @@ pub mod display; pub mod exec; +use serde::{Deserialize, Serialize}; + use super::alu::*; 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 { EQ = 0b0000, NE = 0b0001, @@ -57,7 +59,7 @@ pub enum ArmCond { AL = 0b1110, } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)] #[allow(non_camel_case_types)] pub enum ArmFormat { /// Branch and Exchange @@ -97,7 +99,7 @@ pub enum ArmHalfwordTransferType { SignedHalfwords = 0b11, } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)] pub struct ArmInstruction { pub cond: ArmCond, pub fmt: ArmFormat, diff --git a/src/core/arm7tdmi/cpu.rs b/src/core/arm7tdmi/cpu.rs index 61cb68a..5110b10 100644 --- a/src/core/arm7tdmi/cpu.rs +++ b/src/core/arm7tdmi/cpu.rs @@ -1,6 +1,7 @@ use std::fmt; use ansi_term::{Colour, Style}; +use serde::{Deserialize, Serialize}; pub use super::exception::Exception; use super::{ @@ -13,7 +14,7 @@ use crate::core::sysbus::{ MemoryAccess, MemoryAccessType, MemoryAccessType::*, MemoryAccessWidth::*, SysBus, }; -#[derive(Debug, PartialEq)] +#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)] pub enum PipelineState { Refill1, Refill2, @@ -26,7 +27,7 @@ impl Default for PipelineState { } } -#[derive(Debug, Default)] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct Core { pub pc: u32, pub gpr: [u32; 15], diff --git a/src/core/arm7tdmi/mod.rs b/src/core/arm7tdmi/mod.rs index 3f1a928..f20c9cc 100644 --- a/src/core/arm7tdmi/mod.rs +++ b/src/core/arm7tdmi/mod.rs @@ -1,6 +1,7 @@ use std::fmt; use num::Num; +use serde::{Deserialize, Serialize}; pub mod arm; pub mod thumb; @@ -21,7 +22,7 @@ pub const REG_SP: usize = 13; pub(self) use crate::core::{Addr, Bus}; -#[derive(Debug, PartialEq, Copy, Clone)] +#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)] pub enum DecodedInstruction { Arm(ArmInstruction), Thumb(ThumbInstruction), diff --git a/src/core/arm7tdmi/psr.rs b/src/core/arm7tdmi/psr.rs index c1f0eec..c966851 100644 --- a/src/core/arm7tdmi/psr.rs +++ b/src/core/arm7tdmi/psr.rs @@ -1,6 +1,8 @@ /// The program status register use std::fmt; +use serde::{Deserialize, Serialize}; + use crate::bit::BitIndex; use crate::num::FromPrimitive; @@ -27,7 +29,7 @@ impl From for CpuState { } } -#[derive(Debug, Clone, Copy, Default)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default)] pub struct RegPSR { raw: u32, } diff --git a/src/core/arm7tdmi/thumb/mod.rs b/src/core/arm7tdmi/thumb/mod.rs index 6c96886..82539d3 100644 --- a/src/core/arm7tdmi/thumb/mod.rs +++ b/src/core/arm7tdmi/thumb/mod.rs @@ -35,7 +35,7 @@ impl ThumbDecodeError { } } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)] pub enum ThumbFormat { /// Format 1 MoveShiftedReg, @@ -77,7 +77,7 @@ pub enum ThumbFormat { BranchLongWithLink, } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)] pub struct ThumbInstruction { pub fmt: ThumbFormat, pub raw: u16, diff --git a/src/core/cartridge.rs b/src/core/cartridge.rs index 96c3d6b..13da096 100644 --- a/src/core/cartridge.rs +++ b/src/core/cartridge.rs @@ -4,6 +4,8 @@ use std::io::prelude::*; use std::path::Path; use std::str::from_utf8; +use serde::{Deserialize, Serialize}; + use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use zip::ZipArchive; @@ -35,7 +37,7 @@ use super::{Addr, Bus, GBAResult}; /// 0C6h 26 Not used (seems to be unused) /// 0E0h 4 JOYBUS Entry Pt. (32bit ARM branch opcode, eg. "B joy_start") /// -#[derive(Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct CartridgeHeader { // rom_entry_point: Addr, game_title: String, @@ -69,7 +71,7 @@ impl CartridgeHeader { } } -#[derive(Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct Cartridge { pub header: CartridgeHeader, bytes: Box<[u8]>, diff --git a/src/core/dma.rs b/src/core/dma.rs index f37b231..d5c4a1c 100644 --- a/src/core/dma.rs +++ b/src/core/dma.rs @@ -2,16 +2,10 @@ use super::iodev::consts::{REG_FIFO_A, REG_FIFO_B}; use super::sysbus::SysBus; use super::{Addr, Bus, Interrupt, IrqBitmask}; -use bit_set::BitSet; use num::FromPrimitive; +use serde::{Deserialize, Serialize}; -#[derive(Debug)] -enum DmaTransferType { - Xfer16bit, - Xfer32bit, -} - -#[derive(Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct DmaChannel { id: usize, @@ -30,7 +24,7 @@ pub struct DmaChannel { irq: Interrupt, } -#[derive(Debug, Default)] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] struct DmaInternalRegs { src_addr: u32, dst_addr: u32, @@ -172,10 +166,10 @@ impl DmaChannel { } } -#[derive(Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct DmaController { pub channels: [DmaChannel; 4], - pending_set: BitSet, + pending_set: u8, cycles: usize, } @@ -188,20 +182,22 @@ impl DmaController { DmaChannel::new(2), DmaChannel::new(3), ], - pending_set: BitSet::with_capacity(4), + pending_set: 0, cycles: 0, } } 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) { - for id in self.pending_set.iter() { - self.channels[id].xfer(sb, irqs); + for id in 0..4 { + 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) { @@ -213,9 +209,9 @@ impl DmaController { 8 => self.channels[channel_id].write_word_count(value), 10 => { if self.channels[channel_id].write_dma_ctrl(value) { - self.pending_set.insert(channel_id); + self.pending_set |= 1 << channel_id; } else { - self.pending_set.remove(channel_id); + self.pending_set &= !(1 << channel_id); } } _ => panic!("Invalid dma offset {:x}", ofs), @@ -225,7 +221,7 @@ impl DmaController { pub fn notify_vblank(&mut self) { for i in 0..4 { 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) { for i in 0..4 { 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].dst == fifo_addr { - self.pending_set.insert(i); + self.pending_set |= 1 << i; } } } } bitfield! { - #[derive(Default)] + #[derive(Serialize, Deserialize, Clone, Default)] pub struct DmaChannelCtrl(u16); impl Debug; u16; diff --git a/src/core/gba.rs b/src/core/gba.rs index 9e51b69..a0fec85 100644 --- a/src/core/gba.rs +++ b/src/core/gba.rs @@ -2,6 +2,9 @@ use std::cell::RefCell; use std::rc::Rc; +use bincode; +use serde::{Deserialize, Serialize}; + use super::arm7tdmi::Core; use super::cartridge::Cartridge; use super::gpu::*; @@ -23,6 +26,12 @@ pub struct GameBoyAdvance { cycles_to_next_event: usize, } +#[derive(Serialize, Deserialize)] +struct SaveState { + sysbus: Box, + cpu: Core, +} + impl GameBoyAdvance { pub fn new( cpu: Core, @@ -49,6 +58,25 @@ impl GameBoyAdvance { } } + pub fn save_state(&self) -> bincode::Result> { + 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 = bincode::deserialize_from(bytes)?; + + self.cpu = decoded.cpu; + self.sysbus = decoded.sysbus; + self.cycles_to_next_event = 1; + + Ok(()) + } + #[inline] pub fn key_poll(&mut self) { self.sysbus.io.keyinput = self.input_device.borrow_mut().poll(); diff --git a/src/core/gpu/mod.rs b/src/core/gpu/mod.rs index 2326b00..c4b0c08 100644 --- a/src/core/gpu/mod.rs +++ b/src/core/gpu/mod.rs @@ -2,6 +2,8 @@ use std::cell::RefCell; use std::fmt; use std::rc::Rc; +use serde::{Deserialize, Serialize}; + use super::super::VideoInterface; use super::interrupt::IrqBitmask; use super::sysbus::{BoxedMemory, SysBus}; @@ -50,7 +52,7 @@ pub enum PixelFormat { BPP8 = 1, } -#[derive(Debug, PartialEq, Copy, Clone)] +#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)] pub enum GpuState { HDraw = 0, HBlank, @@ -95,7 +97,7 @@ impl std::ops::IndexMut for Scanline { } } -#[derive(Debug, Default, Copy, Clone)] +#[derive(Serialize, Deserialize, Debug, Default, Clone)] pub struct Background { pub bgcnt: BgControl, pub bgvofs: u16, @@ -107,7 +109,7 @@ pub struct Background { mosaic_first_row: Scanline, } -#[derive(Debug, Default)] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct Window { pub left: u8, pub right: u8, @@ -151,7 +153,7 @@ pub struct AffineMatrix { pub pd: i32, } -#[derive(Debug, Default, Copy, Clone)] +#[derive(Serialize, Deserialize, Debug, Default, Copy, Clone)] pub struct BgAffine { pub pa: i16, // dx pub pb: i16, // dmx @@ -163,7 +165,7 @@ pub struct BgAffine { pub internal_y: i32, } -#[derive(Debug, Copy, Clone)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone)] pub struct ObjBufferEntry { pub(super) color: Rgb15, pub(super) priority: u16, @@ -182,7 +184,7 @@ impl Default for ObjBufferEntry { type VideoDeviceRcRefCell = Rc>; -#[derive(DebugStub)] +#[derive(Serialize, Deserialize, Clone, DebugStub)] pub struct Gpu { pub state: GpuState, diff --git a/src/core/gpu/regs.rs b/src/core/gpu/regs.rs index 4860963..08ad0e8 100644 --- a/src/core/gpu/regs.rs +++ b/src/core/gpu/regs.rs @@ -1,6 +1,8 @@ use super::sfx::BldMode; use super::*; +use serde::{Deserialize, Serialize}; + pub const SCREEN_BLOCK_SIZE: u32 = 0x800; #[derive(Debug, PartialEq)] @@ -60,6 +62,7 @@ impl BgControl { // struct definitions below because the bitfield! macro messes up syntax highlighting in vscode. bitfield! { + #[derive(Serialize, Deserialize, Clone)] pub struct DisplayControl(u16); impl Debug; u16; @@ -79,6 +82,7 @@ bitfield! { } bitfield! { + #[derive(Serialize, Deserialize, Clone)] pub struct DisplayStatus(u16); impl Debug; u16; @@ -92,7 +96,7 @@ bitfield! { } bitfield! { - #[derive(Default, Copy, Clone)] + #[derive(Serialize, Deserialize, Default, Copy, Clone)] pub struct BgControl(u16); impl Debug; u16; @@ -106,7 +110,7 @@ bitfield! { } bitfield! { - #[derive(Default, Copy, Clone)] + #[derive(Serialize, Deserialize, Default, Copy, Clone)] pub struct RegMosaic(u16); impl Debug; u32; @@ -117,7 +121,7 @@ bitfield! { } bitflags! { - #[derive(Default)] + #[derive(Serialize, Deserialize, Default)] pub struct BlendFlags: u32 { const BG0 = 0b00000001; const BG1 = 0b00000010; @@ -148,7 +152,7 @@ impl BlendFlags { } bitfield! { - #[derive(Default, Copy, Clone)] + #[derive(Serialize, Deserialize, Default, Copy, Clone)] pub struct BlendControl(u16); impl Debug; pub into BlendFlags, top, _: 5, 0; @@ -157,7 +161,7 @@ bitfield! { } bitfield! { - #[derive(Default, Copy, Clone)] + #[derive(Serialize, Deserialize, Default, Copy, Clone)] pub struct BlendAlpha(u16); impl Debug; u16; @@ -166,7 +170,7 @@ bitfield! { } bitflags! { - #[derive(Default)] + #[derive(Serialize, Deserialize, Default)] pub struct WindowFlags: u32 { const BG0 = 0b00000001; const BG1 = 0b00000010; @@ -200,7 +204,7 @@ const BG_WIN_FLAG: [WindowFlags; 4] = [ ]; bitfield! { - #[derive(Default, Copy, Clone)] + #[derive(Serialize, Deserialize, Default, Copy, Clone)] pub struct WindowReg(u16); impl Debug; u16; diff --git a/src/core/gpu/rgb15.rs b/src/core/gpu/rgb15.rs index 9e999c0..107ab10 100644 --- a/src/core/gpu/rgb15.rs +++ b/src/core/gpu/rgb15.rs @@ -1,8 +1,10 @@ //! Helper type to deal with the GBA's 15bit color +use serde::{Deserialize, Serialize}; + bitfield! { #[repr(transparent)] - #[derive(Copy, Clone, Default, PartialEq)] + #[derive(Serialize, Deserialize, Copy, Clone, Default, PartialEq)] pub struct Rgb15(u16); impl Debug; pub r, set_r: 4, 0; diff --git a/src/core/interrupt.rs b/src/core/interrupt.rs index 3ce6268..146ae15 100644 --- a/src/core/interrupt.rs +++ b/src/core/interrupt.rs @@ -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)] pub enum Interrupt { LCD_VBlank = 0, @@ -17,7 +19,7 @@ pub enum Interrupt { GamePak = 13, } -#[derive(Debug, Default)] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct InterruptController { pub interrupt_master_enable: bool, pub interrupt_enable: IrqBitmask, @@ -48,7 +50,7 @@ impl IrqBitmask { } bitfield! { - #[derive(Default, Copy, Clone, PartialEq)] + #[derive(Serialize, Deserialize, Default, Copy, Clone, PartialEq)] pub struct IrqBitmask(u16); impl Debug; u16; diff --git a/src/core/iodev.rs b/src/core/iodev.rs index ede2476..4df6e9d 100644 --- a/src/core/iodev.rs +++ b/src/core/iodev.rs @@ -7,15 +7,18 @@ use super::sound::SoundController; use super::timer::Timers; use super::{Addr, Bus}; +use serde::{Deserialize, Serialize}; + use self::consts::*; -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)] pub enum HaltState { Running, 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 } +#[derive(Clone, Serialize, Deserialize)] pub struct IoDevices { pub intc: InterruptController, pub gpu: Box, @@ -262,7 +265,7 @@ impl Bus for IoDevices { } bitfield! { - #[derive(Default, Copy, Clone, PartialEq)] + #[derive(Serialize, Deserialize, Default, Copy, Clone, PartialEq)] pub struct WaitControl(u16); impl Debug; u16; diff --git a/src/core/sound/dsp.rs b/src/core/sound/dsp.rs index cc36761..2d48bcc 100644 --- a/src/core/sound/dsp.rs +++ b/src/core/sound/dsp.rs @@ -1,12 +1,14 @@ use crate::{AudioInterface, StereoSample}; +use serde::{Deserialize, Serialize}; + const PI: f32 = std::f32::consts::PI; pub trait Resampler { fn push_sample(&mut self, s: StereoSample, audio: &mut dyn AudioInterface); } -#[derive(Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct CosineResampler { last_in_sample: StereoSample, phase: f32, diff --git a/src/core/sound/fifo.rs b/src/core/sound/fifo.rs index d4afc15..c9800cf 100644 --- a/src/core/sound/fifo.rs +++ b/src/core/sound/fifo.rs @@ -1,7 +1,9 @@ // TODO write tests or replace with a crate const SOUND_FIFO_CAPACITY: usize = 32; -#[derive(Debug)] +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct SoundFifo { wr_pos: usize, rd_pos: usize, diff --git a/src/core/sound/mod.rs b/src/core/sound/mod.rs index abdeaa3..523864c 100644 --- a/src/core/sound/mod.rs +++ b/src/core/sound/mod.rs @@ -2,6 +2,7 @@ use std::cell::RefCell; use std::rc::Rc; use bit::BitIndex; +use serde::{Deserialize, Serialize}; use super::dma::DmaController; 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 DUTY_RATIOS: [f32; 4] = [0.125, 0.25, 0.5, 0.75]; -#[derive(Debug)] -struct NoiseChannel {} - -#[derive(Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] struct DmaSoundChannel { value: i8, volume_shift: i16, @@ -62,7 +60,7 @@ const REG_FIFO_B_H: u32 = REG_FIFO_B + 2; type AudioDeviceRcRefCell = Rc>; -#[derive(DebugStub)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct SoundController { sample_rate_to_cpu_freq: usize, // how many "cycles" are a sample? cycles: usize, // cycles count when we last provided a new sample. diff --git a/src/core/sysbus.rs b/src/core/sysbus.rs index be32f33..8a5de1d 100644 --- a/src/core/sysbus.rs +++ b/src/core/sysbus.rs @@ -1,6 +1,8 @@ use std::fmt; use std::ops::Add; +use serde::{Deserialize, Serialize}; + use super::cartridge::Cartridge; use super::gpu::{GpuState, VIDEO_RAM_SIZE}; use super::iodev::IoDevices; @@ -64,7 +66,7 @@ impl fmt::Display for MemoryAccess { } } -#[derive(Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct BoxedMemory { pub mem: Box<[u8]>, } @@ -77,15 +79,17 @@ impl BoxedMemory { impl Bus for BoxedMemory { 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) { - 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]); impl Bus for DummyBus { @@ -96,6 +100,7 @@ impl Bus for DummyBus { fn write_8(&mut self, _addr: Addr, _value: u8) {} } +#[derive(Serialize, Deserialize, Clone)] pub struct SysBus { pub io: IoDevices, diff --git a/src/core/timer.rs b/src/core/timer.rs index ba7449a..24330fd 100644 --- a/src/core/timer.rs +++ b/src/core/timer.rs @@ -2,11 +2,10 @@ use super::interrupt::{Interrupt, IrqBitmask}; use super::iodev::consts::*; use super::sysbus::SysBus; -use bit_set::BitSet; - use num::FromPrimitive; +use serde::{Deserialize, Serialize}; -#[derive(Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct Timer { // registers pub ctl: TimerCtl, @@ -66,10 +65,10 @@ impl Timer { } } -#[derive(Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct Timers { timers: [Timer; 4], - running_timers: BitSet, + running_timers: u8, pub trace: bool, } @@ -90,7 +89,7 @@ impl Timers { pub fn new() -> Timers { Timers { timers: [Timer::new(0), Timer::new(1), Timer::new(2), Timer::new(3)], - running_timers: BitSet::with_capacity(4), + running_timers: 0, trace: false, } } @@ -101,9 +100,9 @@ impl Timers { let new_enabled = self[id].ctl.enabled(); let cascade = self.timers[id].ctl.cascade(); if new_enabled && !cascade { - self.running_timers.insert(id); + self.running_timers |= 1 << id; } else { - self.running_timers.remove(id); + self.running_timers &= !(1 << id); } if self.trace && old_enabled != new_enabled { println!( @@ -158,7 +157,11 @@ impl Timers { } 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() { let timer = &mut self.timers[id]; let num_overflows = timer.update(cycles, irqs); @@ -181,7 +184,7 @@ impl Timers { } bitfield! { - #[derive(Default)] + #[derive(Serialize, Deserialize, Clone, Default)] pub struct TimerCtl(u16); impl Debug; u16;