troubleshooting, experimenting
This commit is contained in:
parent
f936258310
commit
8f3ab6e751
|
@ -60,8 +60,6 @@ impl Apu {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let square_table = (0..31).map(|x| 95.52/((8128.0 / x as f32) + 100.0)).collect();
|
let square_table = (0..31).map(|x| 95.52/((8128.0 / x as f32) + 100.0)).collect();
|
||||||
let tnd_table = (0..203).map(|x| 163.67/((24329.0 / x as f32) + 100.0)).collect();
|
let tnd_table = (0..203).map(|x| 163.67/((24329.0 / x as f32) + 100.0)).collect();
|
||||||
println!("square_table: {:?}", square_table);
|
|
||||||
println!("tnd_table: {:?}", tnd_table);
|
|
||||||
Apu {
|
Apu {
|
||||||
square1: Square::new(false),
|
square1: Square::new(false),
|
||||||
square2: Square::new(true),
|
square2: Square::new(true),
|
||||||
|
|
|
@ -80,7 +80,7 @@ impl Square {
|
||||||
// Update volume for this channel
|
// Update volume for this channel
|
||||||
// The mixer receives the current envelope volume except when
|
// The mixer receives the current envelope volume except when
|
||||||
self.sample = if self.duty_cycle[self.duty_counter] == 0 // the sequencer output is zero, or
|
self.sample = if self.duty_cycle[self.duty_counter] == 0 // the sequencer output is zero, or
|
||||||
|| self.target_period > 0x7FF // overflow from the sweep unit's adder is silencing the channel,
|
|| self.timer_period > 0x7FF // overflow from the sweep unit's adder is silencing the channel,
|
||||||
|| self.length_counter == 0 // the length counter is zero, or
|
|| self.length_counter == 0 // the length counter is zero, or
|
||||||
|| self.timer_period < 8 { // the timer has a value less than eight.
|
|| self.timer_period < 8 { // the timer has a value less than eight.
|
||||||
0
|
0
|
||||||
|
@ -126,9 +126,10 @@ impl Square {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clock_sweep(&mut self) {
|
pub fn clock_sweep(&mut self) {
|
||||||
|
self.calculate_target_period();
|
||||||
// When the frame counter sends a half-frame clock (at 120 or 96 Hz), two things happen.
|
// When the frame counter sends a half-frame clock (at 120 or 96 Hz), two things happen.
|
||||||
// If the divider's counter is zero, the sweep is enabled, and the sweep unit is not muting the channel: The pulse's period is adjusted.
|
// If the divider's counter is zero, the sweep is enabled, and the sweep unit is not muting the channel: The pulse's period is adjusted.
|
||||||
if self.sweep_counter == 0 && self.sweep_enabled && !(self.timer_period < 8 || self.target_period > 0x7FF) {
|
if self.sweep_counter == 0 && self.sweep_enabled && !(self.timer_period < 8 || self.timer_period > 0x7FF) {
|
||||||
self.timer_period = self.target_period;
|
self.timer_period = self.target_period;
|
||||||
println!("timer period adjusted to {}", self.timer_period);
|
println!("timer period adjusted to {}", self.timer_period);
|
||||||
}
|
}
|
||||||
|
@ -139,7 +140,10 @@ impl Square {
|
||||||
} else {
|
} else {
|
||||||
self.sweep_counter -= 1;
|
self.sweep_counter -= 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
// 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.
|
||||||
let change = self.timer_period >> self.shift_count;
|
let change = self.timer_period >> self.shift_count;
|
||||||
|
@ -185,17 +189,21 @@ impl Square {
|
||||||
pub fn write_timer_low(&mut self, value: u8) {
|
pub fn write_timer_low(&mut self, value: u8) {
|
||||||
self.timer_period &= 0b00000111_00000000; // mask off everything but high 3 bits of 11-bit timer
|
self.timer_period &= 0b00000111_00000000; // mask off everything but high 3 bits of 11-bit timer
|
||||||
self.timer_period |= value as u16; // apply low 8 bits
|
self.timer_period |= value as u16; // apply low 8 bits
|
||||||
|
self.calculate_target_period();
|
||||||
}
|
}
|
||||||
|
|
||||||
// $4003/$4007
|
// $4003/$4007
|
||||||
pub fn write_timer_high(&mut self, value: u8) {
|
pub fn write_timer_high(&mut self, value: u8) {
|
||||||
// LLLL.Lttt Pulse channel 1 length counter load and timer (write)
|
// LLLL.Lttt Pulse channel 1 length counter load and timer (write)
|
||||||
|
// TODO: thought the below meant that the length counter was only set if the channel was enabled, but apparently not as not having it fixes start game noise in DK.
|
||||||
|
// When the enabled bit is cleared (via $4015), the length counter is forced to 0 and cannot be changed until enabled is set again (the length counter's previous value is lost).
|
||||||
if self.enabled {
|
if self.enabled {
|
||||||
self.length_counter = super::LENGTH_COUNTER_TABLE[value as usize >> 3];
|
self.length_counter = super::LENGTH_COUNTER_TABLE[value as usize >> 3];
|
||||||
}
|
}
|
||||||
let timer_high = value as u16 & 0b0000_0111;
|
let timer_high = value as u16 & 0b0000_0111;
|
||||||
self.timer_period &= 0b11111000_11111111; // mask off high 3 bits of 11-bit timer
|
self.timer_period &= 0b11111000_11111111; // mask off high 3 bits of 11-bit timer
|
||||||
self.timer_period |= timer_high << 8; // apply high timer bits in their place
|
self.timer_period |= timer_high << 8; // apply high timer bits in their place
|
||||||
|
self.calculate_target_period();
|
||||||
// The sequencer is immediately restarted at the first value of the current sequence. The envelope is also restarted. The period divider is not reset.
|
// The sequencer is immediately restarted at the first value of the current sequence. The envelope is also restarted. The period divider is not reset.
|
||||||
self.duty_counter = 0;
|
self.duty_counter = 0;
|
||||||
self.start = true;
|
self.start = true;
|
||||||
|
|
|
@ -69,7 +69,7 @@ fn main() -> Result<(), String> {
|
||||||
match cpu.apu.clock() {
|
match cpu.apu.clock() {
|
||||||
Some(sample) => {
|
Some(sample) => {
|
||||||
sps += 1;
|
sps += 1;
|
||||||
audio_device.queue(&vec![sample]);
|
if sps < 44100 {audio_device.queue(&vec![sample]);}
|
||||||
},
|
},
|
||||||
None => (),
|
None => (),
|
||||||
};
|
};
|
||||||
|
@ -126,8 +126,14 @@ TODO:
|
||||||
- remaining APU channels
|
- remaining APU channels
|
||||||
- common mappers
|
- common mappers
|
||||||
- battery-backed RAM solution
|
- battery-backed RAM solution
|
||||||
|
- fix mysterious Mario pipe non-locations
|
||||||
- GUI? drag and drop ROMs?
|
- GUI? drag and drop ROMs?
|
||||||
- reset function
|
- reset function
|
||||||
- save/load/pause functionality
|
- save/load/pause functionality
|
||||||
|
|
||||||
|
|
||||||
|
Timing notes:
|
||||||
|
The PPU is throttled to 60Hz by sleeping in the main loop. This locks the CPU to roughly its intended speed, 1.789773MHz NTSC. The APU runs at half that.
|
||||||
|
The SDL audio device samples/outputs at 44,100Hz, so as long as the APU queues up 44,100 samples per second, it works.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue