Compare commits
	
		
			2 Commits
		
	
	
		
			d9a3a7b0dd
			...
			903cfacad3
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 903cfacad3 | |||
| 9d0e099a97 | 
							
								
								
									
										17
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										17
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -178,6 +178,12 @@ version = "1.0.1" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" | checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "cache-padded" | ||||||
|  | version = "1.1.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "calloop" | name = "calloop" | ||||||
| version = "0.6.5" | version = "0.6.5" | ||||||
| @@ -684,10 +690,10 @@ dependencies = [ | |||||||
|  "anyhow", |  "anyhow", | ||||||
|  "bitfield", |  "bitfield", | ||||||
|  "clap", |  "clap", | ||||||
|  "crossbeam-channel", |  | ||||||
|  "gilrs", |  "gilrs", | ||||||
|  "pixels", |  "pixels", | ||||||
|  "rodio", |  "rodio", | ||||||
|  |  "rtrb", | ||||||
|  "winit", |  "winit", | ||||||
|  "winit_input_helper", |  "winit_input_helper", | ||||||
| ] | ] | ||||||
| @@ -1677,6 +1683,15 @@ dependencies = [ | |||||||
|  "petgraph", |  "petgraph", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "rtrb" | ||||||
|  | version = "0.1.4" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "318256ac02f7e11a48a10339ba5dca8bd7eb17496abf384e8ea909bb2ae5275f" | ||||||
|  | dependencies = [ | ||||||
|  |  "cache-padded", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "rustc-hash" | name = "rustc-hash" | ||||||
| version = "1.1.0" | version = "1.1.0" | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ pixels = "^0.5" | |||||||
| winit = "^0.25" | winit = "^0.25" | ||||||
| winit_input_helper = "^0.10" | winit_input_helper = "^0.10" | ||||||
| rodio = "^0.14" | rodio = "^0.14" | ||||||
| crossbeam-channel = "^0.5" | rtrb = "^0.1.4" | ||||||
|  |  | ||||||
| [profile.release] | [profile.release] | ||||||
| debug = true | debug = true | ||||||
|   | |||||||
							
								
								
									
										89
									
								
								src/apu.rs
									
									
									
									
									
								
							
							
						
						
									
										89
									
								
								src/apu.rs
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| use crate::bus::BusIo; | use crate::bus::BusIo; | ||||||
| use crate::emu::SM83_CLOCK_SPEED; | use crate::emu::SM83_CLOCK_SPEED; | ||||||
| use gen::{AudioBuffer, AudioSender}; | use gen::SampleProducer; | ||||||
| use types::ch1::{Sweep, SweepDirection}; | use types::ch1::{Sweep, SweepDirection}; | ||||||
| use types::ch3::Volume as Ch3Volume; | use types::ch3::Volume as Ch3Volume; | ||||||
| use types::ch4::{CounterWidth, Frequency as Ch4Frequency, PolynomialCounter}; | use types::ch4::{CounterWidth, Frequency as Ch4Frequency, PolynomialCounter}; | ||||||
| @@ -10,14 +10,10 @@ use types::{ChannelControl, FrameSequencerState, SoundOutput}; | |||||||
| pub mod gen; | pub mod gen; | ||||||
| mod types; | mod types; | ||||||
|  |  | ||||||
|  | const SAMPLE_INCREMENT: u64 = gen::SAMPLE_RATE as u64; | ||||||
| const WAVE_PATTERN_RAM_LEN: usize = 0x10; | const WAVE_PATTERN_RAM_LEN: usize = 0x10; | ||||||
|  |  | ||||||
| const SAMPLE_RATE: u32 = 48000; // Hz | #[derive(Default, Debug)] | ||||||
| const AUDIO_BUFFER_LEN: usize = 512; |  | ||||||
| const CHANNEL_COUNT: usize = 2; |  | ||||||
| const SAMPLE_INCREMENT: u64 = SAMPLE_RATE as u64; |  | ||||||
|  |  | ||||||
| #[derive(Default, Debug, Clone)] |  | ||||||
| pub struct Apu { | pub struct Apu { | ||||||
|     ctrl: SoundControl, |     ctrl: SoundControl, | ||||||
|     /// Tone & Sweep |     /// Tone & Sweep | ||||||
| @@ -33,15 +29,14 @@ pub struct Apu { | |||||||
|     frame_seq_state: FrameSequencerState, |     frame_seq_state: FrameSequencerState, | ||||||
|     div_prev: Option<u8>, |     div_prev: Option<u8>, | ||||||
|  |  | ||||||
|     sender: Option<AudioSender<f32>>, |     prod: Option<SampleProducer<f32>>, | ||||||
|     sample_counter: u64, |     sample_counter: u64, | ||||||
|  |  | ||||||
|     buffer: AudioBuffer<(f32, f32)>, |  | ||||||
| } | } | ||||||
|  |  | ||||||
| impl BusIo for Apu { | impl BusIo for Apu { | ||||||
|     fn read_byte(&self, addr: u16) -> u8 { |     fn read_byte(&self, addr: u16) -> u8 { | ||||||
|         match addr & 0x00FF { |         match addr & 0x00FF { | ||||||
|  |             0x10 => self.ch1.sweep(), | ||||||
|             0x11 => self.ch1.duty(), |             0x11 => self.ch1.duty(), | ||||||
|             0x12 => self.ch1.envelope(), |             0x12 => self.ch1.envelope(), | ||||||
|             0x14 => self.ch1.freq_hi(), |             0x14 => self.ch1.freq_hi(), | ||||||
| @@ -136,55 +131,45 @@ impl Apu { | |||||||
|  |  | ||||||
|         self.div_prev = Some(bit_5); |         self.div_prev = Some(bit_5); | ||||||
|  |  | ||||||
|         if self.sender.is_some() && self.sample_counter >= SM83_CLOCK_SPEED { |         if let Some(ref mut prod) = self.prod { | ||||||
|             self.sample_counter %= SM83_CLOCK_SPEED; |             if self.sample_counter >= SM83_CLOCK_SPEED { | ||||||
|             // Sample the APU |                 self.sample_counter %= SM83_CLOCK_SPEED; | ||||||
|  |  | ||||||
|             let ch1_amplitude = self.ch1.amplitude(); |                 // Sample the APU | ||||||
|             let ch1_left = self.ctrl.output.ch1_left() as u8 as f32 * ch1_amplitude; |                 let ch1_amplitude = self.ch1.amplitude(); | ||||||
|             let ch1_right = self.ctrl.output.ch1_right() as u8 as f32 * ch1_amplitude; |                 let ch1_left = self.ctrl.output.ch1_left() as u8 as f32 * ch1_amplitude; | ||||||
|  |                 let ch1_right = self.ctrl.output.ch1_right() as u8 as f32 * ch1_amplitude; | ||||||
|  |  | ||||||
|             let ch2_amplitude = self.ch2.amplitude(); |                 let ch2_amplitude = self.ch2.amplitude(); | ||||||
|             let ch2_left = self.ctrl.output.ch2_left() as u8 as f32 * ch2_amplitude; |                 let ch2_left = self.ctrl.output.ch2_left() as u8 as f32 * ch2_amplitude; | ||||||
|             let ch2_right = self.ctrl.output.ch2_right() as u8 as f32 * ch2_amplitude; |                 let ch2_right = self.ctrl.output.ch2_right() as u8 as f32 * ch2_amplitude; | ||||||
|  |  | ||||||
|             let ch3_amplitude = self.ch3.amplitude(); |                 let ch3_amplitude = self.ch3.amplitude(); | ||||||
|             let ch3_left = self.ctrl.output.ch3_left() as u8 as f32 * ch3_amplitude; |                 let ch3_left = self.ctrl.output.ch3_left() as u8 as f32 * ch3_amplitude; | ||||||
|             let ch3_right = self.ctrl.output.ch3_right() as u8 as f32 * ch3_amplitude; |                 let ch3_right = self.ctrl.output.ch3_right() as u8 as f32 * ch3_amplitude; | ||||||
|  |  | ||||||
|             let ch4_amplitude = self.ch4.amplitude(); |                 let ch4_amplitude = self.ch4.amplitude(); | ||||||
|             let ch4_left = self.ctrl.output.ch4_left() as u8 as f32 * ch4_amplitude; |                 let ch4_left = self.ctrl.output.ch4_left() as u8 as f32 * ch4_amplitude; | ||||||
|             let ch4_right = self.ctrl.output.ch4_right() as u8 as f32 * ch4_amplitude; |                 let ch4_right = self.ctrl.output.ch4_right() as u8 as f32 * ch4_amplitude; | ||||||
|  |  | ||||||
|             let left = (ch1_left + ch2_left + ch3_left + ch4_left) / 4.0; |                 let left = (ch1_left + ch2_left + ch3_left + ch4_left) / 4.0; | ||||||
|             let right = (ch1_right + ch2_right + ch3_right + ch4_right) / 4.0; |                 let right = (ch1_right + ch2_right + ch3_right + ch4_right) / 4.0; | ||||||
|  |  | ||||||
|             self.buffer.push_back((left, right)); |                 prod.push(left) | ||||||
|  |                     .and(prod.push(right)) | ||||||
|  |                     .expect("Add samples to ring buffer"); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn set_audio_sender(&mut self, sender: AudioSender<f32>) { |     pub fn set_producer(&mut self, prod: SampleProducer<f32>) { | ||||||
|         self.sender = Some(sender); |         self.prod = Some(prod); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub(crate) fn is_full(&self) -> bool { |     pub(crate) fn is_full(&self) -> bool { | ||||||
|         self.buffer.len() >= AUDIO_BUFFER_LEN * CHANNEL_COUNT |         match self.prod.as_ref() { | ||||||
|     } |             Some(prod) => prod.is_full(), | ||||||
|  |             None => false, | ||||||
|     pub(crate) fn flush_samples(&mut self) { |  | ||||||
|         if let Some(sender) = self.sender.as_ref() { |  | ||||||
|             while self.buffer.len() >= CHANNEL_COUNT { |  | ||||||
|                 match self.buffer.pop_front() { |  | ||||||
|                     Some((left, right)) => { |  | ||||||
|                         sender |  | ||||||
|                             .send_samples(left, right) |  | ||||||
|                             .expect("Successfully sent samples across threads"); |  | ||||||
|                     } |  | ||||||
|                     None => unreachable!( |  | ||||||
|                         "While loop ensures that there are at least two items in AudioBuffer" |  | ||||||
|                     ), |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -349,7 +334,7 @@ impl Apu { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| pub(crate) struct SoundControl { | pub(crate) struct SoundControl { | ||||||
|     /// 0xFF24 | NR50 - Channel Control |     /// 0xFF24 | NR50 - Channel Control | ||||||
|     channel: ChannelControl, |     channel: ChannelControl, | ||||||
| @@ -394,7 +379,7 @@ impl SoundControl { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| pub(crate) struct Channel1 { | pub(crate) struct Channel1 { | ||||||
|     /// 0xFF10 | NR10 - Channel 1 Sweep Register |     /// 0xFF10 | NR10 - Channel 1 Sweep Register | ||||||
|     sweep: Sweep, |     sweep: Sweep, | ||||||
| @@ -557,7 +542,7 @@ impl Channel1 { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| pub(crate) struct Channel2 { | pub(crate) struct Channel2 { | ||||||
|     /// 0xFF16 | NR21 - Channel 2 Sound length / Wave Pattern Duty |     /// 0xFF16 | NR21 - Channel 2 Sound length / Wave Pattern Duty | ||||||
|     duty: SoundDuty, |     duty: SoundDuty, | ||||||
| @@ -660,7 +645,7 @@ impl Channel2 { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| pub(crate) struct Channel3 { | pub(crate) struct Channel3 { | ||||||
|     /// 0xFF1A | NR30 - Channel 3 Sound on/off |     /// 0xFF1A | NR30 - Channel 3 Sound on/off | ||||||
|     enabled: bool, |     enabled: bool, | ||||||
| @@ -798,7 +783,7 @@ impl Channel3 { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| pub(crate) struct Channel4 { | pub(crate) struct Channel4 { | ||||||
|     /// 0xFF20 | NR41 - Channel 4 Sound Length |     /// 0xFF20 | NR41 - Channel 4 Sound Length | ||||||
|     len: u8, |     len: u8, | ||||||
|   | |||||||
							
								
								
									
										106
									
								
								src/apu/gen.rs
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								src/apu/gen.rs
									
									
									
									
									
								
							| @@ -1,47 +1,74 @@ | |||||||
| use super::{AUDIO_BUFFER_LEN, CHANNEL_COUNT, SAMPLE_RATE}; |  | ||||||
| use crossbeam_channel::{Receiver, SendError, Sender}; |  | ||||||
| use rodio::Source; | use rodio::Source; | ||||||
| use std::collections::VecDeque; | use rtrb::{Consumer, Producer, PushError, RingBuffer}; | ||||||
|  |  | ||||||
| pub struct AudioMPSC; | pub(crate) const SAMPLE_RATE: u32 = 48000; // Hz | ||||||
|  | const CHANNEL_COUNT: usize = 2; | ||||||
|  | const AUDIO_BUFFER_LEN: usize = 4096; | ||||||
|  |  | ||||||
| impl AudioMPSC { | pub struct AudioSPSC<T> { | ||||||
|     pub fn init() -> (AudioSender<f32>, AudioReceiver<f32>) { |     inner: RingBuffer<T>, | ||||||
|         // TODO: Can we provide an upper limit for this? | } | ||||||
|         // The larger this channel is, the more lag there is between the Audio and |  | ||||||
|         // Emulator |  | ||||||
|         let (send, recv) = crossbeam_channel::unbounded(); |  | ||||||
|  |  | ||||||
|         (AudioSender { inner: send }, AudioReceiver { inner: recv }) | impl<T> Default for AudioSPSC<T> { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         Self { | ||||||
|  |             inner: RingBuffer::new(AUDIO_BUFFER_LEN * CHANNEL_COUNT), | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone)] | impl<T> AudioSPSC<T> { | ||||||
| pub struct AudioSender<T> { |     pub fn new(capacity: usize) -> Self { | ||||||
|     inner: Sender<T>, |         Self { | ||||||
| } |             inner: RingBuffer::new(capacity), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
| impl<T> AudioSender<T> { |     pub fn init(self) -> (SampleProducer<T>, SampleConsumer<T>) { | ||||||
|     pub(crate) fn send_samples(&self, left: T, right: T) -> Result<(), SendError<T>> { |         let (prod, cons) = self.inner.split(); | ||||||
|         self.inner.send(left).and(self.inner.send(right))?; |  | ||||||
|         Ok(()) |         ( | ||||||
|  |             SampleProducer { inner: prod }, | ||||||
|  |             SampleConsumer { inner: cons }, | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| pub struct AudioReceiver<T> { | pub struct SampleProducer<T> { | ||||||
|     inner: Receiver<T>, |     inner: Producer<T>, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<T> Iterator for AudioReceiver<T> { | impl<T> SampleProducer<T> { | ||||||
|     type Item = T; |     pub(crate) fn push(&mut self, value: T) -> Result<(), PushError<T>> { | ||||||
|  |         self.inner.push(value) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub(crate) fn is_full(&self) -> bool { | ||||||
|  |         self.inner.is_full() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<T> std::fmt::Debug for SampleProducer<T> { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         f.debug_struct(&format!("SampleProducer<{}>", std::any::type_name::<T>())) | ||||||
|  |             .finish_non_exhaustive() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub struct SampleConsumer<T> { | ||||||
|  |     inner: Consumer<T>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Iterator for SampleConsumer<f32> { | ||||||
|  |     type Item = f32; | ||||||
|  |  | ||||||
|     fn next(&mut self) -> Option<Self::Item> { |     fn next(&mut self) -> Option<Self::Item> { | ||||||
|         // TODO: Should this never return none? |         // As of 2021-07-28, PopError can only be Empty | ||||||
|         self.inner.recv().ok() |         Some(self.inner.pop().unwrap_or_default()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<T: rodio::Sample> Source for AudioReceiver<T> { | impl Source for SampleConsumer<f32> { | ||||||
|     fn current_frame_len(&self) -> Option<usize> { |     fn current_frame_len(&self) -> Option<usize> { | ||||||
|         // A frame changes when the samples rate or |         // A frame changes when the samples rate or | ||||||
|         // number of channels change. This will never happen, so |         // number of channels change. This will never happen, so | ||||||
| @@ -63,30 +90,3 @@ impl<T: rodio::Sample> Source for AudioReceiver<T> { | |||||||
|         None |         None | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone)] |  | ||||||
| pub(crate) struct AudioBuffer<T> { |  | ||||||
|     inner: VecDeque<T>, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<T> Default for AudioBuffer<T> { |  | ||||||
|     fn default() -> Self { |  | ||||||
|         Self { |  | ||||||
|             inner: VecDeque::with_capacity(AUDIO_BUFFER_LEN * CHANNEL_COUNT), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<T> AudioBuffer<T> { |  | ||||||
|     pub(crate) fn push_back(&mut self, value: T) { |  | ||||||
|         self.inner.push_back(value) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pub(crate) fn pop_front(&mut self) -> Option<T> { |  | ||||||
|         self.inner.pop_front() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pub(crate) fn len(&self) -> usize { |  | ||||||
|         self.inner.len() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ use std::{fs::File, io::Read}; | |||||||
|  |  | ||||||
| const BOOT_ROM_SIZE: usize = 0x100; | const BOOT_ROM_SIZE: usize = 0x100; | ||||||
|  |  | ||||||
| #[derive(Debug, Clone)] | #[derive(Debug)] | ||||||
| pub struct Bus { | pub struct Bus { | ||||||
|     boot: Option<[u8; BOOT_ROM_SIZE]>, // Boot ROM is 256b long |     boot: Option<[u8; BOOT_ROM_SIZE]>, // Boot ROM is 256b long | ||||||
|     cartridge: Option<Cartridge>, |     cartridge: Option<Cartridge>, | ||||||
| @@ -70,10 +70,6 @@ impl Bus { | |||||||
|         &self.apu |         &self.apu | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub(crate) fn apu_mut(&mut self) -> &mut Apu { |  | ||||||
|         &mut self.apu |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pub(crate) fn clock(&mut self) { |     pub(crate) fn clock(&mut self) { | ||||||
|         self.ppu.clock(); |         self.ppu.clock(); | ||||||
|         self.timer.clock(); |         self.timer.clock(); | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ const RAM_SIZE_ADDRESS: usize = 0x0149; | |||||||
| const ROM_SIZE_ADDRESS: usize = 0x0148; | const ROM_SIZE_ADDRESS: usize = 0x0148; | ||||||
| const MBC_TYPE_ADDRESS: usize = 0x0147; | const MBC_TYPE_ADDRESS: usize = 0x0147; | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Default)] | #[derive(Debug, Default)] | ||||||
| pub(crate) struct Cartridge { | pub(crate) struct Cartridge { | ||||||
|     memory: Vec<u8>, |     memory: Vec<u8>, | ||||||
|     title: Option<String>, |     title: Option<String>, | ||||||
| @@ -144,7 +144,7 @@ impl BusIo for Cartridge { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone)] | #[derive(Debug)] | ||||||
| struct Mbc1 { | struct Mbc1 { | ||||||
|     /// 5-bit number |     /// 5-bit number | ||||||
|     rom_bank: u8, |     rom_bank: u8, | ||||||
| @@ -293,7 +293,7 @@ enum MBC3Device { | |||||||
|     RealTimeClock, |     RealTimeClock, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Default)] | #[derive(Debug, Default)] | ||||||
| struct MBC3 { | struct MBC3 { | ||||||
|     /// 7-bit Number |     /// 7-bit Number | ||||||
|     rom_bank: u8, |     rom_bank: u8, | ||||||
| @@ -361,7 +361,7 @@ impl MemoryBankController for MBC3 { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy)] | #[derive(Debug)] | ||||||
| struct NoMbc {} | struct NoMbc {} | ||||||
|  |  | ||||||
| impl MemoryBankController for NoMbc { | impl MemoryBankController for NoMbc { | ||||||
| @@ -374,24 +374,11 @@ impl MemoryBankController for NoMbc { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| trait MemoryBankController: CloneMbc { | trait MemoryBankController { | ||||||
|     fn handle_read(&self, addr: u16) -> MbcResult; |     fn handle_read(&self, addr: u16) -> MbcResult; | ||||||
|     fn handle_write(&mut self, addr: u16, byte: u8); |     fn handle_write(&mut self, addr: u16, byte: u8); | ||||||
| } | } | ||||||
|  |  | ||||||
| trait CloneMbc { |  | ||||||
|     fn clone_mbc(&self) -> Box<dyn MemoryBankController>; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<T> CloneMbc for T |  | ||||||
| where |  | ||||||
|     T: MemoryBankController + Clone + 'static, |  | ||||||
| { |  | ||||||
|     fn clone_mbc<'a>(&self) -> Box<dyn MemoryBankController> { |  | ||||||
|         Box::new(self.clone()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy)] | #[derive(Debug, Clone, Copy)] | ||||||
| enum MbcResult { | enum MbcResult { | ||||||
|     Address(usize), |     Address(usize), | ||||||
| @@ -485,7 +472,7 @@ impl Default for BankCount { | |||||||
|  |  | ||||||
| impl BankCount { | impl BankCount { | ||||||
|     // https://hacktix.github.io/GBEDG/mbcs/#rom-size |     // https://hacktix.github.io/GBEDG/mbcs/#rom-size | ||||||
|     fn size(self) -> u32 { |     fn size(&self) -> u32 { | ||||||
|         use BankCount::*; |         use BankCount::*; | ||||||
|  |  | ||||||
|         match self { |         match self { | ||||||
| @@ -533,12 +520,6 @@ impl std::fmt::Debug for Box<dyn MemoryBankController> { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl std::clone::Clone for Box<dyn MemoryBankController> { |  | ||||||
|     fn clone(&self) -> Self { |  | ||||||
|         self.clone_mbc() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Default for Box<dyn MemoryBankController> { | impl Default for Box<dyn MemoryBankController> { | ||||||
|     fn default() -> Self { |     fn default() -> Self { | ||||||
|         Box::new(Mbc1::default()) |         Box::new(Mbc1::default()) | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								src/cpu.rs
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								src/cpu.rs
									
									
									
									
									
								
							| @@ -4,11 +4,10 @@ use crate::instruction::{Cycle, Instruction}; | |||||||
| use crate::interrupt::{InterruptEnable, InterruptFlag}; | use crate::interrupt::{InterruptEnable, InterruptFlag}; | ||||||
| use crate::joypad::Joypad; | use crate::joypad::Joypad; | ||||||
| use crate::ppu::Ppu; | use crate::ppu::Ppu; | ||||||
| use crate::timer::Timer; |  | ||||||
| use bitfield::bitfield; | use bitfield::bitfield; | ||||||
| use std::fmt::{Display, Formatter, Result as FmtResult}; | use std::fmt::{Display, Formatter, Result as FmtResult}; | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Default)] | #[derive(Debug, Default)] | ||||||
| pub struct Cpu { | pub struct Cpu { | ||||||
|     pub bus: Bus, |     pub bus: Bus, | ||||||
|     reg: Registers, |     reg: Registers, | ||||||
| @@ -45,8 +44,8 @@ impl Cpu { | |||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub(crate) fn ime(&self) -> ImeState { |     pub(crate) fn ime(&self) -> &ImeState { | ||||||
|         self.ime |         &self.ime | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub(crate) fn set_ime(&mut self, state: ImeState) { |     pub(crate) fn set_ime(&mut self, state: ImeState) { | ||||||
| @@ -61,8 +60,8 @@ impl Cpu { | |||||||
|         self.halted = None; |         self.halted = None; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub(crate) fn halted(&self) -> Option<HaltState> { |     pub(crate) fn halted(&self) -> Option<&HaltState> { | ||||||
|         self.halted |         self.halted.as_ref() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn inc_pc(&mut self) { |     fn inc_pc(&mut self) { | ||||||
| @@ -127,8 +126,6 @@ impl Cpu { | |||||||
|             if !self.bus.apu().is_full() { |             if !self.bus.apu().is_full() { | ||||||
|                 self.bus.clock(); |                 self.bus.clock(); | ||||||
|                 elapsed += 1; |                 elapsed += 1; | ||||||
|             } else { |  | ||||||
|                 self.bus.apu_mut().flush_samples(); |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -178,10 +175,6 @@ impl Cpu { | |||||||
|         &mut self.bus.joypad |         &mut self.bus.joypad | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub(crate) fn timer(&self) -> &Timer { |  | ||||||
|         &self.bus.timer |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn check_ime(&mut self) { |     fn check_ime(&mut self) { | ||||||
|         match self.ime { |         match self.ime { | ||||||
|             ImeState::Pending => { |             ImeState::Pending => { | ||||||
| @@ -267,7 +260,7 @@ impl Cpu { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Copy, Clone)] | #[derive(Debug, Clone, Copy)] | ||||||
| enum State { | enum State { | ||||||
|     Execute, |     Execute, | ||||||
|     // Halt, |     // Halt, | ||||||
| @@ -292,7 +285,6 @@ impl Cpu { | |||||||
|             E => self.reg.e = value, |             E => self.reg.e = value, | ||||||
|             H => self.reg.h = value, |             H => self.reg.h = value, | ||||||
|             L => self.reg.l = value, |             L => self.reg.l = value, | ||||||
|             Flag => self.flags = value.into(), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -307,7 +299,6 @@ impl Cpu { | |||||||
|             E => self.reg.e, |             E => self.reg.e, | ||||||
|             H => self.reg.h, |             H => self.reg.h, | ||||||
|             L => self.reg.l, |             L => self.reg.l, | ||||||
|             Flag => self.flags.into(), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -400,7 +391,7 @@ impl Cpu { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Copy, Clone)] | #[derive(Debug, Clone, Copy)] | ||||||
| pub(crate) enum Register { | pub(crate) enum Register { | ||||||
|     A, |     A, | ||||||
|     B, |     B, | ||||||
| @@ -409,10 +400,9 @@ pub(crate) enum Register { | |||||||
|     E, |     E, | ||||||
|     H, |     H, | ||||||
|     L, |     L, | ||||||
|     Flag, |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Copy, Clone)] | #[derive(Debug, Clone, Copy)] | ||||||
| pub(crate) enum RegisterPair { | pub(crate) enum RegisterPair { | ||||||
|     AF, |     AF, | ||||||
|     BC, |     BC, | ||||||
| @@ -422,7 +412,7 @@ pub(crate) enum RegisterPair { | |||||||
|     PC, |     PC, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Copy, Clone, Default)] | #[derive(Debug, Default)] | ||||||
| struct Registers { | struct Registers { | ||||||
|     a: u8, |     a: u8, | ||||||
|     b: u8, |     b: u8, | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ use crate::bus::BusIo; | |||||||
| use crate::cpu::{Cpu, Flags, HaltState, ImeState, Register, RegisterPair}; | use crate::cpu::{Cpu, Flags, HaltState, ImeState, Register, RegisterPair}; | ||||||
| use std::{convert::TryFrom, fmt::Debug}; | use std::{convert::TryFrom, fmt::Debug}; | ||||||
|  |  | ||||||
| #[derive(Copy, Clone)] | #[derive(Clone, Copy)] | ||||||
| #[allow(clippy::upper_case_acronyms)] | #[allow(clippy::upper_case_acronyms)] | ||||||
| pub(crate) enum Instruction { | pub(crate) enum Instruction { | ||||||
|     NOP, |     NOP, | ||||||
| @@ -51,26 +51,26 @@ pub(crate) enum Instruction { | |||||||
|     SET(u8, InstrRegister), |     SET(u8, InstrRegister), | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Copy, Clone)] | #[derive(Clone, Copy)] | ||||||
| pub(crate) enum JPTarget { | pub(crate) enum JPTarget { | ||||||
|     RegisterPair(RegisterPair), |     RegisterPair(RegisterPair), | ||||||
|     ImmediateWord(u16), |     ImmediateWord(u16), | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Copy, Clone)] | #[derive(Clone, Copy)] | ||||||
| pub(crate) enum Registers { | pub(crate) enum Registers { | ||||||
|     Byte(InstrRegister), |     Byte(InstrRegister), | ||||||
|     Word(RegisterPair), |     Word(RegisterPair), | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Copy, Clone)] | #[derive(Clone, Copy)] | ||||||
| pub(crate) enum MATHTarget { | pub(crate) enum MATHTarget { | ||||||
|     Register(InstrRegister), |     Register(InstrRegister), | ||||||
|     RegisterPair(RegisterPair), |     RegisterPair(RegisterPair), | ||||||
|     ImmediateByte(u8), |     ImmediateByte(u8), | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Copy, Clone)] | #[derive(Clone, Copy)] | ||||||
| pub(crate) enum LDTarget { | pub(crate) enum LDTarget { | ||||||
|     IndirectC, |     IndirectC, | ||||||
|     Register(InstrRegister), |     Register(InstrRegister), | ||||||
| @@ -82,7 +82,7 @@ pub(crate) enum LDTarget { | |||||||
|     ByteAtAddressWithOffset(u8), |     ByteAtAddressWithOffset(u8), | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Copy, Clone)] | #[derive(Clone, Copy)] | ||||||
| pub(crate) enum InstrRegisterPair { | pub(crate) enum InstrRegisterPair { | ||||||
|     AF, |     AF, | ||||||
|     BC, |     BC, | ||||||
| @@ -94,7 +94,7 @@ pub(crate) enum InstrRegisterPair { | |||||||
|     DecrementHL, |     DecrementHL, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Copy, Clone)] | #[derive(Clone, Copy)] | ||||||
| pub(crate) enum InstrRegister { | pub(crate) enum InstrRegister { | ||||||
|     A, |     A, | ||||||
|     B, |     B, | ||||||
| @@ -106,7 +106,7 @@ pub(crate) enum InstrRegister { | |||||||
|     IndirectHL, // (HL) |     IndirectHL, // (HL) | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Copy, Clone)] | #[derive(Clone, Copy)] | ||||||
| pub(crate) enum JumpCondition { | pub(crate) enum JumpCondition { | ||||||
|     NotZero, |     NotZero, | ||||||
|     Zero, |     Zero, | ||||||
| @@ -115,7 +115,7 @@ pub(crate) enum JumpCondition { | |||||||
|     Always, |     Always, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Copy, Clone)] | #[derive(Debug)] | ||||||
| struct Table; | struct Table; | ||||||
|  |  | ||||||
| #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] | ||||||
| @@ -1887,7 +1887,6 @@ impl TryFrom<Register> for InstrRegister { | |||||||
|             Register::E => Ok(Self::E), |             Register::E => Ok(Self::E), | ||||||
|             Register::H => Ok(Self::H), |             Register::H => Ok(Self::H), | ||||||
|             Register::L => Ok(Self::L), |             Register::L => Ok(Self::L), | ||||||
|             Register::Flag => Err("Can not convert Register::Flag to InstrRegister"), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use bitfield::bitfield; | use bitfield::bitfield; | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| pub(crate) struct Interrupt { | pub(crate) struct Interrupt { | ||||||
|     pub(crate) flag: InterruptFlag, |     pub(crate) flag: InterruptFlag, | ||||||
|     pub(crate) enable: InterruptEnable, |     pub(crate) enable: InterruptEnable, | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| use gilrs::{Button, Event as GamepadEvent, EventType as GamepadEventType}; | use gilrs::{Button, Event as GamepadEvent, EventType as GamepadEventType}; | ||||||
| use winit_input_helper::WinitInputHelper; | use winit_input_helper::WinitInputHelper; | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy)] | #[derive(Debug)] | ||||||
| pub struct Joypad { | pub struct Joypad { | ||||||
|     /// 0xFF00 | P1/JOYP - Player 1 Joypad |     /// 0xFF00 | P1/JOYP - Player 1 Joypad | ||||||
|     pub(crate) p1: u8, |     pub(crate) p1: u8, | ||||||
| @@ -48,7 +48,7 @@ impl Joypad { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| struct JoypadState { | struct JoypadState { | ||||||
|     // Direction Row |     // Direction Row | ||||||
|     dpad_down: ButtonEvent, |     dpad_down: ButtonEvent, | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| pub use apu::gen::AudioMPSC; | pub use apu::gen::AudioSPSC; | ||||||
| pub use instruction::Cycle; | pub use instruction::Cycle; | ||||||
|  |  | ||||||
| pub const GB_WIDTH: usize = 160; | pub const GB_WIDTH: usize = 160; | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								src/main.rs
									
									
									
									
									
								
							| @@ -1,9 +1,9 @@ | |||||||
| use anyhow::{anyhow, Result}; | use anyhow::{anyhow, Result}; | ||||||
| use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; | use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; | ||||||
| use gb::{AudioMPSC, Cycle, GB_HEIGHT, GB_WIDTH}; | use gb::{AudioSPSC, Cycle, GB_HEIGHT, GB_WIDTH}; | ||||||
| use gilrs::Gilrs; | use gilrs::Gilrs; | ||||||
| use pixels::{PixelsBuilder, SurfaceTexture}; | use pixels::{PixelsBuilder, SurfaceTexture}; | ||||||
| use rodio::OutputStream; | use rodio::{OutputStream, Sink}; | ||||||
| use std::time::Instant; | use std::time::Instant; | ||||||
| use winit::dpi::LogicalSize; | use winit::dpi::LogicalSize; | ||||||
| use winit::event::{Event, VirtualKeyCode}; | use winit::event::{Event, VirtualKeyCode}; | ||||||
| @@ -65,16 +65,18 @@ fn main() -> Result<()> { | |||||||
|             .build()? |             .build()? | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let (send, recv) = AudioMPSC::init(); |     let spsc: AudioSPSC<f32> = Default::default(); | ||||||
|     game_boy.apu_mut().set_audio_sender(send); |     let (prod, cons) = spsc.init(); | ||||||
|  |  | ||||||
|  |     game_boy.apu_mut().set_producer(prod); | ||||||
|  |  | ||||||
|     // Initialize Audio |     // Initialize Audio | ||||||
|     let (_stream, stream_handle) = OutputStream::try_default().expect("Initialized Audio"); |     let (_stream, stream_handle) = OutputStream::try_default().expect("Initialized Audio"); | ||||||
|  |     let sink = Sink::try_new(&stream_handle)?; | ||||||
|  |     sink.append(cons); | ||||||
|  |  | ||||||
|     std::thread::spawn(move || { |     std::thread::spawn(move || { | ||||||
|         stream_handle |         sink.sleep_until_end(); | ||||||
|             .play_raw(recv) |  | ||||||
|             .expect("Failed to play Audio Source"); |  | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     let mut now = Instant::now(); |     let mut now = Instant::now(); | ||||||
|   | |||||||
							
								
								
									
										36
									
								
								src/ppu.rs
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								src/ppu.rs
									
									
									
									
									
								
							| @@ -33,7 +33,7 @@ const LIGHT_GRAY: [u8; 4] = 0xAEBA89FFu32.to_be_bytes(); | |||||||
| const DARK_GRAY: [u8; 4] = 0x5E6745FFu32.to_be_bytes(); | const DARK_GRAY: [u8; 4] = 0x5E6745FFu32.to_be_bytes(); | ||||||
| const BLACK: [u8; 4] = 0x202020FFu32.to_be_bytes(); | const BLACK: [u8; 4] = 0x202020FFu32.to_be_bytes(); | ||||||
|  |  | ||||||
| #[derive(Debug, Clone)] | #[derive(Debug)] | ||||||
| pub struct Ppu { | pub struct Ppu { | ||||||
|     pub(crate) int: Interrupt, |     pub(crate) int: Interrupt, | ||||||
|     /// 0xFF40 | LCDC - LCD Control |     /// 0xFF40 | LCDC - LCD Control | ||||||
| @@ -489,7 +489,7 @@ impl Default for Ppu { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| pub(crate) struct Interrupt { | pub(crate) struct Interrupt { | ||||||
|     _vblank: bool, |     _vblank: bool, | ||||||
|     _lcd_stat: bool, |     _lcd_stat: bool, | ||||||
| @@ -513,7 +513,7 @@ impl Interrupt { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| pub(crate) struct ScreenPosition { | pub(crate) struct ScreenPosition { | ||||||
|     /// 0xFF42 | SCY - Scroll Y |     /// 0xFF42 | SCY - Scroll Y | ||||||
|     pub(crate) scroll_y: u8, |     pub(crate) scroll_y: u8, | ||||||
| @@ -529,7 +529,7 @@ pub(crate) struct ScreenPosition { | |||||||
|     pub(crate) window_x: u8, |     pub(crate) window_x: u8, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| pub(crate) struct Monochrome { | pub(crate) struct Monochrome { | ||||||
|     /// 0xFF47 | BGP - Background Palette Data |     /// 0xFF47 | BGP - Background Palette Data | ||||||
|     pub(crate) bg_palette: BackgroundPalette, |     pub(crate) bg_palette: BackgroundPalette, | ||||||
| @@ -539,7 +539,7 @@ pub(crate) struct Monochrome { | |||||||
|     pub(crate) obj_palette_1: ObjectPalette, |     pub(crate) obj_palette_1: ObjectPalette, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone)] | #[derive(Debug)] | ||||||
| pub(crate) struct ObjectAttributeTable { | pub(crate) struct ObjectAttributeTable { | ||||||
|     buf: Box<[u8; OAM_SIZE]>, |     buf: Box<[u8; OAM_SIZE]>, | ||||||
| } | } | ||||||
| @@ -606,7 +606,7 @@ impl<'a> From<&'a [u8; 4]> for ObjectAttribute { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy)] | #[derive(Debug)] | ||||||
| struct ObjectBuffer { | struct ObjectBuffer { | ||||||
|     buf: [Option<ObjectAttribute>; OBJECT_LIMIT], |     buf: [Option<ObjectAttribute>; OBJECT_LIMIT], | ||||||
|     len: usize, |     len: usize, | ||||||
| @@ -674,7 +674,7 @@ impl Default for ObjectBuffer { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| struct PixelFetcher { | struct PixelFetcher { | ||||||
|     x_pos: u8, |     x_pos: u8, | ||||||
|     back: BackgroundFetcher, |     back: BackgroundFetcher, | ||||||
| @@ -793,7 +793,7 @@ trait Fetcher { | |||||||
|     fn hblank_reset(&mut self); |     fn hblank_reset(&mut self); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy)] | #[derive(Debug)] | ||||||
| struct BackgroundFetcher { | struct BackgroundFetcher { | ||||||
|     state: FetcherState, |     state: FetcherState, | ||||||
|     tile: TileBuilder, |     tile: TileBuilder, | ||||||
| @@ -859,7 +859,7 @@ impl Default for BackgroundFetcher { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| struct ObjectFetcher { | struct ObjectFetcher { | ||||||
|     state: FetcherState, |     state: FetcherState, | ||||||
|     tile: TileBuilder, |     tile: TileBuilder, | ||||||
| @@ -881,7 +881,7 @@ impl Fetcher for ObjectFetcher { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| struct WindowLineCounter { | struct WindowLineCounter { | ||||||
|     count: u8, |     count: u8, | ||||||
| } | } | ||||||
| @@ -918,12 +918,12 @@ impl Default for FetcherState { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| struct BgPixelProperty { | struct BgPixelProperty { | ||||||
|     shade_id: u8, |     shade_id: u8, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| struct ObjPixelProperty { | struct ObjPixelProperty { | ||||||
|     shade_id: u8, |     shade_id: u8, | ||||||
|     palette_kind: ObjectPaletteKind, |     palette_kind: ObjectPaletteKind, | ||||||
| @@ -932,7 +932,7 @@ struct ObjPixelProperty { | |||||||
|  |  | ||||||
| // FIXME: Fifo Registers have a known size. Are heap allocations | // FIXME: Fifo Registers have a known size. Are heap allocations | ||||||
| // really necessary here? | // really necessary here? | ||||||
| #[derive(Debug, Clone)] | #[derive(Debug)] | ||||||
| struct PixelFifo { | struct PixelFifo { | ||||||
|     back: VecDeque<BgPixelProperty>, |     back: VecDeque<BgPixelProperty>, | ||||||
|     obj: VecDeque<ObjPixelProperty>, |     obj: VecDeque<ObjPixelProperty>, | ||||||
| @@ -963,7 +963,7 @@ impl Default for PixelFifo { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| struct TileBuilder { | struct TileBuilder { | ||||||
|     id: Option<u8>, |     id: Option<u8>, | ||||||
|     low: Option<u8>, |     low: Option<u8>, | ||||||
| @@ -988,7 +988,7 @@ impl TileBuilder { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| struct OamScanState { | struct OamScanState { | ||||||
|     count: u8, |     count: u8, | ||||||
|     mode: OamScanMode, |     mode: OamScanMode, | ||||||
| @@ -1009,8 +1009,8 @@ impl OamScanState { | |||||||
|         self.count |         self.count | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn mode(&self) -> OamScanMode { |     fn mode(&self) -> &OamScanMode { | ||||||
|         self.mode |         &self.mode | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn next(&mut self) { |     fn next(&mut self) { | ||||||
| @@ -1035,7 +1035,7 @@ impl Default for OamScanMode { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| struct WindowStatus { | struct WindowStatus { | ||||||
|     /// This will be true if WY == LY at any point in the frame thus far |     /// This will be true if WY == LY at any point in the frame thus far | ||||||
|     coincidence: bool, |     coincidence: bool, | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use crate::instruction::Cycle; | use crate::instruction::Cycle; | ||||||
|  |  | ||||||
| #[derive(Debug, Default, Clone)] | #[derive(Debug, Default)] | ||||||
| pub(crate) struct DirectMemoryAccess { | pub(crate) struct DirectMemoryAccess { | ||||||
|     pub(crate) state: DmaState, |     pub(crate) state: DmaState, | ||||||
|     cycle: Cycle, |     cycle: Cycle, | ||||||
| @@ -28,7 +28,7 @@ impl DirectMemoryAccess { | |||||||
|  |  | ||||||
|                 let src_addr = self |                 let src_addr = self | ||||||
|                     .start |                     .start | ||||||
|                     .addr |                     .0 | ||||||
|                     .as_mut() |                     .as_mut() | ||||||
|                     .expect("Source Address present during DMA Transfer"); |                     .expect("Source Address present during DMA Transfer"); | ||||||
|  |  | ||||||
| @@ -58,7 +58,7 @@ impl DirectMemoryAccess { | |||||||
|     fn reset(&mut self) { |     fn reset(&mut self) { | ||||||
|         self.cycle = Cycle::new(0); |         self.cycle = Cycle::new(0); | ||||||
|         self.state = DmaState::Disabled; |         self.state = DmaState::Disabled; | ||||||
|         self.start.addr = None; |         self.start.0 = None; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -75,26 +75,21 @@ impl Default for DmaState { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Default, Clone, Copy)] | #[derive(Debug, Clone, Copy, Default)] | ||||||
| pub(crate) struct DmaAddress { | pub(crate) struct DmaAddress(Option<u16>); | ||||||
|     /// The current *source* address of the DMA Transfer |  | ||||||
|     /// |  | ||||||
|     /// NB: Will be None if no DMA Transfer is in progress |  | ||||||
|     addr: Option<u16>, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl DmaAddress { | impl DmaAddress { | ||||||
|     pub(crate) fn update(&mut self, byte: u8, state: &mut DmaState) { |     pub(crate) fn update(&mut self, byte: u8, state: &mut DmaState) { | ||||||
|         let start = (byte as u16) << 8; |         let start = (byte as u16) << 8; | ||||||
|  |  | ||||||
|         self.addr = Some(start); |         self.0 = Some(start); | ||||||
|         *state = DmaState::Pending; |         *state = DmaState::Pending; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl From<DmaAddress> for u8 { | impl From<DmaAddress> for u8 { | ||||||
|     fn from(ctrl: DmaAddress) -> Self { |     fn from(ctrl: DmaAddress) -> Self { | ||||||
|         match ctrl.addr { |         match ctrl.0 { | ||||||
|             Some(addr) => (addr >> 8) as u8, |             Some(addr) => (addr >> 8) as u8, | ||||||
|             None => 0xFF, // TODO: What garbage value should be here? |             None => 0xFF, // TODO: What garbage value should be here? | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use bitfield::bitfield; | use bitfield::bitfield; | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Default)] | #[derive(Debug, Default)] | ||||||
| pub(crate) struct Serial { | pub(crate) struct Serial { | ||||||
|     /// 0xFF01 | SB - Serial Transfer Data |     /// 0xFF01 | SB - Serial Transfer Data | ||||||
|     pub(crate) next: u8, |     pub(crate) next: u8, | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use bitfield::bitfield; | use bitfield::bitfield; | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy)] | #[derive(Debug)] | ||||||
| pub(crate) struct Timer { | pub(crate) struct Timer { | ||||||
|     /// 0xFF07 | TAC - Timer Control |     /// 0xFF07 | TAC - Timer Control | ||||||
|     pub(crate) ctrl: TimerControl, |     pub(crate) ctrl: TimerControl, | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ const VARIABLE_WORK_RAM_SIZE: usize = WORK_RAM_SIZE; | |||||||
| const WORK_RAM_START_ADDRESS: usize = 0xC000; | const WORK_RAM_START_ADDRESS: usize = 0xC000; | ||||||
| const VARIABLE_WORK_RAM_START_ADDRESS: usize = 0xD000; | const VARIABLE_WORK_RAM_START_ADDRESS: usize = 0xD000; | ||||||
|  |  | ||||||
| #[derive(Debug, Clone)] | #[derive(Debug)] | ||||||
| pub(crate) struct WorkRam { | pub(crate) struct WorkRam { | ||||||
|     bank: Box<[u8; WORK_RAM_SIZE]>, |     bank: Box<[u8; WORK_RAM_SIZE]>, | ||||||
| } | } | ||||||
| @@ -39,7 +39,7 @@ enum BankNumber { | |||||||
|     Seven = 7, |     Seven = 7, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone)] | #[derive(Debug)] | ||||||
| pub(crate) struct VariableWorkRam { | pub(crate) struct VariableWorkRam { | ||||||
|     current: BankNumber, |     current: BankNumber, | ||||||
|     bank_n: Box<[[u8; VARIABLE_WORK_RAM_SIZE]; 7]>, // 4K for Variable amount of Banks (Banks 1 -> 7) in Game Boy Colour |     bank_n: Box<[[u8; VARIABLE_WORK_RAM_SIZE]; 7]>, // 4K for Variable amount of Banks (Banks 1 -> 7) in Game Boy Colour | ||||||
| @@ -59,8 +59,8 @@ impl VariableWorkRam { | |||||||
|         self.current = bank; |         self.current = bank; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn get_current_bank(&self) -> BankNumber { |     fn get_current_bank(&self) -> &BankNumber { | ||||||
|         self.current |         &self.current | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user