feat/savestates: Implement save/load state API for GameBoyAdvance
Using serde & bincode encoding Former-commit-id: f5e4c599497f6bdf3096fa99f8b2d6ce89278ef7
This commit is contained in:
parent
16142ee99d
commit
f4460b2740
|
@ -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,
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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<bool> for CpuState {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default)]
|
||||
pub struct RegPSR {
|
||||
raw: u32,
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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]>,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<SysBus>,
|
||||
cpu: Core,
|
||||
}
|
||||
|
||||
impl GameBoyAdvance {
|
||||
pub fn new(
|
||||
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]
|
||||
pub fn key_poll(&mut self) {
|
||||
self.sysbus.io.keyinput = self.input_device.borrow_mut().poll();
|
||||
|
|
|
@ -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<usize> 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<RefCell<dyn VideoInterface>>;
|
||||
|
||||
#[derive(DebugStub)]
|
||||
#[derive(Serialize, Deserialize, Clone, DebugStub)]
|
||||
pub struct Gpu {
|
||||
pub state: GpuState,
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Gpu>,
|
||||
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<RefCell<dyn AudioInterface>>;
|
||||
|
||||
#[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.
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Reference in a new issue