use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::ops::ControlFlow;
use crate::{
apu::{Apu, Channel},
bus::CpuBus,
cart::Cart,
common::{Clock, ResetKind, NesRegion, Regional, Reset},
cpu::Cpu,
input::{FourPlayer, Joypad, Player},
mapper::Mapper,
mem::RamState,
ppu::Ppu,
video::{Video, VideoFilter},
};
use anyhow::{anyhow,Result};
#[derive(Debug, Clone)]
#[must_use]
pub struct ControlDeck {
running: bool,
ram_state: RamState,
region: NesRegion,
video: Video,
loaded_rom: Option<String>,
cycles_remaining: f32,
cpu: Cpu,
}
impl Default for ControlDeck {
fn default() -> Self {
Self::new(RamState::default())
}
}
impl ControlDeck {
pub fn new(ram_state: RamState) -> Self {
let cpu = Cpu::new(CpuBus::new(ram_state));
Self {
running: false,
ram_state,
region: NesRegion::default(),
video: Video::default(),
loaded_rom: None,
cycles_remaining: 0.0,
cpu,
}
}
pub fn load_rom(&mut self, name: String, rom: Vec<u8>) -> Result<()> {
self.loaded_rom = Some(name.clone());
let cart = Cart::from_rom(name, rom, self.ram_state)?;
self.set_region(cart.region());
self.cpu.load_cart(cart);
self.reset(ResetKind::Hard);
Ok(())
}
#[inline]
pub fn load_cpu(&mut self, cpu: Cpu) {
self.cpu = cpu;
}
#[inline]
#[must_use]
pub const fn loaded_rom(&self) -> &Option<String> {
&self.loaded_rom
}
#[inline]
#[must_use]
pub const fn cart_battery_backed(&self) -> bool {
self.cpu.cart_battery_backed()
}
#[inline]
#[must_use]
pub fn sram(&self) -> &[u8] {
self.cpu.sram()
}
#[inline]
pub fn load_sram(&mut self, sram: Vec<u8>) {
self.cpu.load_sram(sram);
}
#[inline]
#[must_use]
pub fn wram(&self) -> &[u8] {
self.cpu.wram()
}
#[inline]
#[must_use]
pub fn frame_buffer(&mut self) -> &[u8] {
self.video
.apply_filter(self.cpu.frame_buffer(), self.cpu.frame_number());
self.video.output()
}
#[inline]
#[must_use]
pub const fn frame_number(&self) -> u32 {
self.cpu.frame_number()
}
#[inline]
#[must_use]
pub const fn sample_rate(&self) -> f32 {
self.cpu.clock_rate()
}
#[inline]
#[must_use]
pub fn audio_samples(&self) -> &[f32] {
self.cpu.audio_samples()
}
#[inline]
pub fn clear_audio_samples(&mut self) {
self.cpu.clear_audio_samples();
}
#[inline]
pub fn clock_rate(&mut self) -> f32 {
self.cpu.clock_rate()
}
pub fn clock_instr(&mut self) -> Result<ControlFlow<usize, usize>> {
let cycles = self.clock();
if self.cpu_corrupted() {
Err(anyhow!("cpu corrupted"))
} else {
Ok(ControlFlow::Continue(cycles))
}
}
pub fn clock_seconds(&mut self, seconds: f32) -> Result<ControlFlow<usize, usize>> {
self.cycles_remaining += self.clock_rate() * seconds;
let mut total_cycles = 0;
while self.cycles_remaining > 0.0 {
match self.clock_instr()? {
ControlFlow::Break(cycles) => {
total_cycles += cycles;
self.cycles_remaining -= cycles as f32;
return Ok(ControlFlow::Break(total_cycles));
}
ControlFlow::Continue(cycles) => {
total_cycles += cycles;
self.cycles_remaining -= cycles as f32;
}
}
}
Ok(ControlFlow::Continue(total_cycles))
}
pub fn clock_seconds_inspect<F>(
&mut self,
seconds: f32,
mut inspect: F,
) -> Result<ControlFlow<usize, usize>>
where
F: FnMut(&mut Cpu),
{
self.cycles_remaining += self.clock_rate() * seconds;
let mut total_cycles = 0;
while self.cycles_remaining > 0.0 {
let cycles = self.cpu.clock_inspect(&mut inspect);
total_cycles += cycles;
self.cycles_remaining -= cycles as f32;
}
Ok(ControlFlow::Continue(total_cycles))
}
pub fn clock_frame(&mut self) -> Result<ControlFlow<usize, usize>> {
let mut total_cycles = 0;
let frame = self.frame_number();
while frame == self.frame_number() {
match self.clock_instr()? {
ControlFlow::Break(cycles) => {
total_cycles += cycles;
return Ok(ControlFlow::Break(total_cycles));
}
ControlFlow::Continue(cycles) => {
total_cycles += cycles;
}
}
}
Ok(ControlFlow::Continue(total_cycles))
}
pub fn clock_scanline(&mut self) -> Result<ControlFlow<usize, usize>> {
let current_scanline = self.cpu.ppu_scanline();
let mut total_cycles = 0;
while current_scanline == self.cpu.ppu_scanline() {
match self.clock_instr()? {
ControlFlow::Break(cycles) => {
total_cycles += cycles;
return Ok(ControlFlow::Break(total_cycles));
}
ControlFlow::Continue(cycles) => {
total_cycles += cycles;
}
}
}
Ok(ControlFlow::Continue(total_cycles))
}
#[inline]
#[must_use]
pub const fn cpu_corrupted(&self) -> bool {
self.cpu.corrupted()
}
#[inline]
pub const fn cpu(&self) -> &Cpu {
&self.cpu
}
#[inline]
pub fn cpu_mut(&mut self) -> &mut Cpu {
&mut self.cpu
}
#[inline]
pub const fn ppu(&self) -> &Ppu {
self.cpu.ppu()
}
#[inline]
pub fn ppu_mut(&mut self) -> &mut Ppu {
self.cpu.ppu_mut()
}
#[inline]
pub const fn apu(&self) -> &Apu {
self.cpu.apu()
}
#[inline]
pub const fn mapper(&self) -> &Mapper {
self.cpu.mapper()
}
#[inline]
pub fn mapper_mut(&mut self) -> &mut Mapper {
self.cpu.mapper_mut()
}
#[inline]
pub const fn four_player(&self) -> FourPlayer {
self.cpu.four_player()
}
#[inline]
pub fn set_four_player(&mut self, four_player: FourPlayer) {
self.cpu.set_four_player(four_player);
}
#[inline]
pub fn set_cycle_accurate(&mut self, enabled: bool) {
self.cpu.set_cycle_accurate(enabled);
}
#[inline]
pub fn joypad_mut(&mut self, slot: Player) -> &mut Joypad {
self.cpu.joypad_mut(slot)
}
#[inline]
#[must_use]
pub const fn zapper_pos(&self) -> (i32, i32) {
let zapper = self.cpu.zapper();
(zapper.x(), zapper.y())
}
#[inline]
pub fn trigger_zapper(&mut self) {
self.cpu.zapper_mut().trigger();
}
#[inline]
pub fn aim_zapper(&mut self, x: i32, y: i32) {
self.cpu.zapper_mut().aim(x, y);
}
#[inline]
pub fn set_filter(&mut self, filter: VideoFilter) {
self.video.set_filter(filter);
}
#[inline]
pub fn connect_zapper(&mut self, enabled: bool) {
self.cpu.connect_zapper(enabled);
}
#[inline]
pub fn add_genie_code(&mut self, genie_code: String) -> Result<()> {
self.cpu.add_genie_code(genie_code)
}
#[inline]
pub fn remove_genie_code(&mut self, genie_code: &str) {
self.cpu.remove_genie_code(genie_code);
}
#[inline]
#[must_use]
pub const fn channel_enabled(&self, channel: Channel) -> bool {
self.cpu.audio_channel_enabled(channel)
}
#[inline]
pub fn toggle_channel(&mut self, channel: Channel) {
self.cpu.toggle_audio_channel(channel);
}
#[inline]
#[must_use]
pub const fn is_running(&self) -> bool {
self.running
}
}
impl Clock for ControlDeck {
fn clock(&mut self) -> usize {
self.cpu.clock()
}
}
impl Regional for ControlDeck {
#[inline]
fn region(&self) -> NesRegion {
self.region
}
#[inline]
fn set_region(&mut self, region: NesRegion) {
self.region = region;
self.cpu.set_region(region);
}
}
impl Reset for ControlDeck {
fn reset(&mut self, kind: ResetKind) {
self.cpu.reset(kind);
self.running = true;
}
}