This commit is contained in:
Theron 2020-01-30 00:00:12 -06:00
parent 53b4454f98
commit e4d8bba720
16 changed files with 50 additions and 49 deletions

View File

@ -18,22 +18,22 @@ impl DMC {
} }
pub fn clock(&mut self) { pub fn clock(&mut self) {
} }
pub fn write_control(&mut self, value: u8) { pub fn write_control(&mut self, value: u8) {
} }
pub fn direct_load(&mut self, value: u8) { pub fn direct_load(&mut self, value: u8) {
} }
pub fn write_sample_address(&mut self, value: u8) { pub fn write_sample_address(&mut self, value: u8) {
} }
pub fn write_sample_length(&mut self, value: u8) { pub fn write_sample_length(&mut self, value: u8) {
} }
} }

View File

@ -25,7 +25,7 @@ pub struct Apu {
triangle: Triangle, triangle: Triangle,
noise: Noise, noise: Noise,
dmc: DMC, dmc: DMC,
square_table: Vec<f32>, square_table: Vec<f32>,
tnd_table: Vec<f32>, tnd_table: Vec<f32>,
@ -234,7 +234,7 @@ impl Apu {
fn write_frame_counter(&mut self, value: u8) { fn write_frame_counter(&mut self, value: u8) {
// 0 selects 4-step sequence, 1 selects 5-step sequence // 0 selects 4-step sequence, 1 selects 5-step sequence
self.frame_sequence = if value & (1<<7) == 0 { 4 } else { 5 }; self.frame_sequence = if value & (1<<7) == 0 { 4 } else { 5 };
// If set, the frame interrupt flag is cleared, otherwise it is unaffected. // If set, the frame interrupt flag is cleared, otherwise it is unaffected.
if value & (1<<6) != 0 { if value & (1<<6) != 0 {
self.interrupt_inhibit = false; self.interrupt_inhibit = false;
} }

View File

@ -3,7 +3,7 @@ use super::envelope::Envelope;
const NOISE_TABLE: [u16; 16] = [4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068]; const NOISE_TABLE: [u16; 16] = [4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068];
// $400E M---.PPPP Mode and period (write) // $400E M---.PPPP Mode and period (write)
// bit 7 M--- ---- Mode flag // bit 7 M--- ---- Mode flag
pub struct Noise { pub struct Noise {
pub sample: u16, // output value that gets sent to the mixer pub sample: u16, // output value that gets sent to the mixer
pub enabled: bool, pub enabled: bool,
@ -37,7 +37,7 @@ impl Noise {
} else { } else {
self.timer -= 1; self.timer -= 1;
} }
// The mixer receives the current envelope volume except when // The mixer receives the current envelope volume except when
// Bit 0 of the shift register is set, or the length counter is zero // Bit 0 of the shift register is set, or the length counter is zero
self.sample = if self.linear_feedback_sr & 1 == 1 || self.length_counter == 0 { self.sample = if self.linear_feedback_sr & 1 == 1 || self.length_counter == 0 {
0 0

View File

@ -100,7 +100,7 @@ impl Square {
} }
} }
// Whenever the current period changes for any reason, whether by $400x writes or by sweep, the target period also changes. // Whenever the current period changes for any reason, whether by $400x writes or by sweep, the target period also changes.
pub fn calculate_target_period(&mut self) { pub fn calculate_target_period(&mut self) {
// The sweep unit continuously calculates each channel's target period in this way: // The sweep unit continuously calculates each channel's target period in this way:
// A barrel shifter shifts the channel's 11-bit raw timer period right by the shift count, producing the change amount. // A barrel shifter shifts the channel's 11-bit raw timer period right by the shift count, producing the change amount.

View File

@ -12,7 +12,7 @@ pub struct Triangle {
waveform_counter: usize, waveform_counter: usize,
pub length_counter: u8, pub length_counter: u8,
length_counter_halt: bool, // (this bit is also the linear counter's control flag) length_counter_halt: bool, // (this bit is also the linear counter's control flag)
linear_counter: u8, linear_counter: u8,
counter_reload_value: u8, counter_reload_value: u8,
linear_counter_reload: bool, linear_counter_reload: bool,
@ -37,9 +37,9 @@ impl Triangle {
pub fn clock(&mut self) { pub fn clock(&mut self) {
if self.timer == 0 { if self.timer == 0 {
self.timer = self.timer_period; self.timer = self.timer_period;
// The sequencer is clocked by the timer as long as both the linear counter and the length counter are nonzero. // The sequencer is clocked by the timer as long as both the linear counter and the length counter are nonzero.
if self.linear_counter != 0 && self.length_counter != 0 { if self.linear_counter != 0 && self.length_counter != 0 {
self.waveform_counter = (self.waveform_counter + 1) % 32; self.waveform_counter = (self.waveform_counter + 1) % 32;
} }
} else { } else {
self.timer -= 1; self.timer -= 1;
@ -60,7 +60,7 @@ impl Triangle {
self.linear_counter_reload = false; self.linear_counter_reload = false;
} }
} }
pub fn clock_length_counter(&mut self) { pub fn clock_length_counter(&mut self) {
if !(self.length_counter == 0 || self.length_counter_halt) { if !(self.length_counter == 0 || self.length_counter_halt) {
self.length_counter -= 1; self.length_counter -= 1;

View File

@ -42,8 +42,8 @@ impl AudioCallback for ApuSampler {
} }
} }
pub fn initialize(sdl_context: &Sdl, buffer: Arc<Mutex<Vec<f32>>>) pub fn initialize(sdl_context: &Sdl, buffer: Arc<Mutex<Vec<f32>>>)
-> Result<sdl2::audio::AudioDevice<ApuSampler>, String> -> Result<sdl2::audio::AudioDevice<ApuSampler>, String>
{ {
let audio_subsystem = sdl_context.audio()?; let audio_subsystem = sdl_context.audio()?;
let desired_spec = AudioSpecDesired { let desired_spec = AudioSpecDesired {

View File

@ -107,7 +107,7 @@ impl Mapper for Mmc3 {
}, },
0x6000..=0x7FFF => self.prg_ram_bank[address % 0x2000], // PRG-RAM 0x6000..=0x7FFF => self.prg_ram_bank[address % 0x2000], // PRG-RAM
0x8000..=0xFFFF => { // reading from PRG ROM, dealing with 8K banks of 16K chunks 0x8000..=0xFFFF => { // reading from PRG ROM, dealing with 8K banks of 16K chunks
let offset_8k = address % 0x2000; let offset_8k = address % 0x2000;
let num_banks = self.cart.prg_rom_size * 2; let num_banks = self.cart.prg_rom_size * 2;
@ -118,7 +118,7 @@ impl Mapper for Mmc3 {
0xA000..=0xBFFF => self.bank_registers[7], 0xA000..=0xBFFF => self.bank_registers[7],
0xC000..=0xDFFF => self.bank_registers[6], 0xC000..=0xDFFF => self.bank_registers[6],
0xE000..=0xFFFF => num_banks - 1, 0xE000..=0xFFFF => num_banks - 1,
_ => panic!("oh no"), _ => panic!("oh no"),
} }
}, },
false => { false => {
@ -134,7 +134,7 @@ impl Mapper for Mmc3 {
let chunk_num = bank_num / 2; let chunk_num = bank_num / 2;
let chunk_half = (bank_num % 2) * 0x2000; let chunk_half = (bank_num % 2) * 0x2000;
self.cart.prg_rom[chunk_num][chunk_half + offset_8k] self.cart.prg_rom[chunk_num][chunk_half + offset_8k]
}, },
_ => { _ => {
println!("bad address read from MMC3: 0x{:X}", address); println!("bad address read from MMC3: 0x{:X}", address);

View File

@ -67,7 +67,7 @@ impl super::Cpu {
let low_byte = self.read(operand_address) as usize; let low_byte = self.read(operand_address) as usize;
// BUG TIME! from https://wiki.nesdev.com/w/index.php/Errata // BUG TIME! from https://wiki.nesdev.com/w/index.php/Errata
// "JMP ($xxyy), or JMP indirect, does not advance pages if the lower eight bits // "JMP ($xxyy), or JMP indirect, does not advance pages if the lower eight bits
// of the specified address is $FF; the upper eight bits are fetched from $xx00, // of the specified address is $FF; the upper eight bits are fetched from $xx00,
// 255 bytes earlier, instead of the expected following byte." // 255 bytes earlier, instead of the expected following byte."
let high_byte = if operand_address & 0xFF == 0xFF { let high_byte = if operand_address & 0xFF == 0xFF {
(self.read(operand_address as usize - 0xFF) as usize) << 8 (self.read(operand_address as usize - 0xFF) as usize) << 8

View File

@ -137,7 +137,7 @@ impl Cpu {
} }
pub fn step(&mut self) -> u64 { pub fn step(&mut self) -> u64 {
// skip cycles from OAM DMA if necessary // skip cycles from OAM DMA if necessary
if self.delay > 0 { if self.delay > 0 {
self.delay -= 1; self.delay -= 1;

View File

@ -164,7 +164,7 @@ impl super::Cpu {
pub fn cli(&mut self, _address: usize, _mode: Mode) { pub fn cli(&mut self, _address: usize, _mode: Mode) {
self.P &= 0xFF - INTERRUPT_DISABLE_FLAG; self.P &= 0xFF - INTERRUPT_DISABLE_FLAG;
} }
pub fn clv(&mut self, _address: usize, _mode: Mode) { pub fn clv(&mut self, _address: usize, _mode: Mode) {
self.P &= 0xFF - OVERFLOW_FLAG; self.P &= 0xFF - OVERFLOW_FLAG;
} }

View File

@ -19,7 +19,7 @@ impl super::Cpu {
Mode::ZPY => 2, Mode::ZPY => 2,
} }
} }
pub fn add_offset_to_pc(&mut self, offset: i8) { pub fn add_offset_to_pc(&mut self, offset: i8) {
match offset >= 0 { match offset >= 0 {
true => { true => {

View File

@ -147,7 +147,8 @@ Failed tests from instr_test-v5/rom_singles/:
1. A12 stuff controls when IRQs 1. A12 stuff controls when IRQs fire. Don't think there are serious problems with when IRQs fire anymore,
2. Don't think the timing stuff is related to the palette and background table issues in SMB3 though vertical scroll is still shaky in Kirby.
2. Don't think the timing stuff is related to the palette and background table issues in SMB2/3.
How can the palette be wrong? Or is it not, but the attribute bits are reading wrong?
*/ */

View File

@ -1,5 +1,5 @@
impl super::Ppu { impl super::Ppu {
// cpu writes to 0x2000, PPUCTRL // cpu writes to 0x2000, PPUCTRL
pub fn write_controller(&mut self, byte: u8) { pub fn write_controller(&mut self, byte: u8) {
@ -144,14 +144,14 @@ impl super::Ppu {
As for 0x3F00 through 0x3FFF, the palette RAM indexes and their mirrors, need to find corresponding nametable? As for 0x3F00 through 0x3FFF, the palette RAM indexes and their mirrors, need to find corresponding nametable?
There are 4 nametables, duplicated once, so 8. There is one palette RAM index, mirrored 7 times, so 8. There are 4 nametables, duplicated once, so 8. There is one palette RAM index, mirrored 7 times, so 8.
So to get from the fifth pallete RAM mirror, which would be 0x3F80, you'd select the 5th nametable, So to get from the fifth pallete RAM mirror, which would be 0x3F80, you'd select the 5th nametable,
which would be the first mirrored nametable, 0x3000? which would be the first mirrored nametable, 0x3000?
No, just subtract 0x1000. https://forums.nesdev.com/viewtopic.php?f=3&t=18627: No, just subtract 0x1000. https://forums.nesdev.com/viewtopic.php?f=3&t=18627:
"However, I couldn't find any info on exactly which address should be used to populate the read buffer in this scenario. "However, I couldn't find any info on exactly which address should be used to populate the read buffer in this scenario.
From other emulators, it appears to be PPU_ADDR - 0x1000, but I can't really intuit why that is the case." From other emulators, it appears to be PPU_ADDR - 0x1000, but I can't really intuit why that is the case."
"It's the case because the majority of the time (that is, on just about every board but GTROM), "It's the case because the majority of the time (that is, on just about every board but GTROM),
video memory $3000-$3FFF mirrors $2000-$2FFF. When PA13 is high ($2000-$3FFF), nothing is listening video memory $3000-$3FFF mirrors $2000-$2FFF. When PA13 is high ($2000-$3FFF), nothing is listening
to PA12 (the line that distinguishes $0000-$0FFF from $1000-$1FFF and distinguishes $2000-$2FFF from $3000-$3FFF)." to PA12 (the line that distinguishes $0000-$0FFF from $1000-$1FFF and distinguishes $2000-$2FFF from $3000-$3FFF)."
*/ */

View File

@ -1,7 +1,7 @@
use crate::cartridge::Mirror; use crate::cartridge::Mirror;
impl super::Ppu { impl super::Ppu {
pub fn read(&mut self, addr: usize) -> u8 { pub fn read(&mut self, addr: usize) -> u8 {
let address = addr % 0x4000; let address = addr % 0x4000;
match addr { match addr {
@ -26,7 +26,7 @@ impl super::Ppu {
// Addresses $3F10/$3F14/$3F18/$3F1C are mirrors of $3F00/$3F04/$3F08/$3F0C. // Addresses $3F10/$3F14/$3F18/$3F1C are mirrors of $3F00/$3F04/$3F08/$3F0C.
// Note that this goes for writing as well as reading. // Note that this goes for writing as well as reading.
// A symptom of not having implemented this correctly in an emulator is the sky being black in Super Mario Bros., // A symptom of not having implemented this correctly in an emulator is the sky being black in Super Mario Bros.,
// which writes the backdrop color through $3F10. // which writes the backdrop color through $3F10.
match address % 0x10 { match address % 0x10 {
0x00 => { 0x00 => {
self.palette_ram[0] = value; self.palette_ram[0] = value;

View File

@ -31,7 +31,7 @@ pub struct Ppu {
nametable_C: Vec<u8>, nametable_C: Vec<u8>,
nametable_D: Vec<u8>, nametable_D: Vec<u8>,
// The palette shared by both background and sprites. // The palette shared by both background and sprites.
// Consists of 32 bytes, each of which represents an index into the global PALETTE_TABLE. // Consists of 32 bytes, each of which represents an index into the global PALETTE_TABLE.
// The first 16 bytes are for the background, the second half for the sprites. // The first 16 bytes are for the background, the second half for the sprites.
palette_ram: Vec<u8>, // Palette RAM indexes. palette_ram: Vec<u8>, // Palette RAM indexes.
@ -197,10 +197,10 @@ impl Ppu {
_ => (), _ => (),
} }
} }
// During dots 280 to 304 of the pre-render scanline (end of vblank) // During dots 280 to 304 of the pre-render scanline (end of vblank)
// If rendering is enabled, at the end of vblank, shortly after the horizontal bits // If rendering is enabled, at the end of vblank, shortly after the horizontal bits
// are copied from t to v at dot 257, the PPU will repeatedly copy the vertical bits // are copied from t to v at dot 257, the PPU will repeatedly copy the vertical bits
// from t to v from dots 280 to 304, completing the full initialization of v from t: // from t to v from dots 280 to 304, completing the full initialization of v from t:
if rendering && self.scanline == 261 && self.line_cycle >= 280 && self.line_cycle <= 304 { if rendering && self.scanline == 261 && self.line_cycle >= 280 && self.line_cycle <= 304 {
self.copy_vertical(); self.copy_vertical();

View File

@ -87,7 +87,7 @@ impl super::Ppu {
let low_palette_bit = (self.background_palette_sr_low & (1 << (7-self.x)) != 0) as u8; let low_palette_bit = (self.background_palette_sr_low & (1 << (7-self.x)) != 0) as u8;
let high_palette_bit = (self.background_palette_sr_high & (1 << (7-self.x)) != 0) as u8; let high_palette_bit = (self.background_palette_sr_high & (1 << (7-self.x)) != 0) as u8;
let palette_offset = (high_palette_bit << 1) | low_palette_bit; let palette_offset = (high_palette_bit << 1) | low_palette_bit;
if x < 8 && !self.show_background_left { if x < 8 && !self.show_background_left {
background_pixel = 0; background_pixel = 0;
} }
@ -118,7 +118,7 @@ impl super::Ppu {
// let pixel = self.read(palette_address as usize) as usize; // let pixel = self.read(palette_address as usize) as usize;
let pixel = self.palette_ram[palette_address as usize] as usize; let pixel = self.palette_ram[palette_address as usize] as usize;
let color: (u8, u8, u8) = super::PALETTE_TABLE[pixel]; let color: (u8, u8, u8) = super::PALETTE_TABLE[pixel];
(x,y,color) (x,y,color)
} }
@ -175,7 +175,7 @@ impl super::Ppu {
} }
} }
pub fn evaluate_sprites(&mut self) { pub fn evaluate_sprites(&mut self) {
let mut sprite_count = 0; let mut sprite_count = 0;
for n in 0..64 { for n in 0..64 {
let y_coord = self.primary_oam[(n*4)+0]; let y_coord = self.primary_oam[(n*4)+0];
@ -214,7 +214,7 @@ impl super::Ppu {
} else { } else {
self.sprite_size as usize - 1 - (self.scanline - sprite_y_position) self.sprite_size as usize - 1 - (self.scanline - sprite_y_position)
}; };
// For 8x16 sprites, the PPU ignores the pattern table selection and selects a pattern table from bit 0 of this number. // For 8x16 sprites, the PPU ignores the pattern table selection and selects a pattern table from bit 0 of this number.
} else { } else {
address = if sprite_tile_index & 1 == 0 { 0x0 } else { 0x1000 }; address = if sprite_tile_index & 1 == 0 { 0x0 } else { 0x1000 };
address += (sprite_tile_index & 0xFFFF-1) << 4; // turn off bottom bit BEFORE shifting address += (sprite_tile_index & 0xFFFF-1) << 4; // turn off bottom bit BEFORE shifting
@ -264,8 +264,8 @@ impl super::Ppu {
} }
pub fn inc_y(&mut self) { pub fn inc_y(&mut self) {
// If rendering is enabled, fine Y is incremented at dot 256 of each scanline, // If rendering is enabled, fine Y is incremented at dot 256 of each scanline,
// overflowing to coarse Y, and finally adjusted to wrap among the nametables vertically. // overflowing to coarse Y, and finally adjusted to wrap among the nametables vertically.
let mut fine_y = (self.v & 0b01110000_00000000) >> 12; let mut fine_y = (self.v & 0b01110000_00000000) >> 12;
let mut coarse_y = (self.v & 0b00000011_11100000) >> 5; let mut coarse_y = (self.v & 0b00000011_11100000) >> 5;
if fine_y < 7 { if fine_y < 7 {
@ -273,7 +273,7 @@ impl super::Ppu {
} else { } else {
fine_y = 0; fine_y = 0;
// Row 29 is the last row of tiles in a nametable. To wrap to the next nametable when // Row 29 is the last row of tiles in a nametable. To wrap to the next nametable when
// incrementing coarse Y from 29, the vertical nametable is switched by toggling bit // incrementing coarse Y from 29, the vertical nametable is switched by toggling bit
// 11, and coarse Y wraps to row 0. // 11, and coarse Y wraps to row 0.
if coarse_y == 29 { if coarse_y == 29 {
self.v ^= 1<<11; self.v ^= 1<<11;
@ -320,7 +320,7 @@ impl super::Ppu {
} }
pub fn y_in_range(&self, y_coord: u8) -> bool { pub fn y_in_range(&self, y_coord: u8) -> bool {
self.scanline >= (y_coord as usize) && self.scanline >= (y_coord as usize) &&
self.scanline - (y_coord as usize) < self.sprite_size as usize self.scanline - (y_coord as usize) < self.sprite_size as usize
} }