Trying to figure this audio stuff out

This commit is contained in:
Kevin Hamacher 2017-01-17 17:56:11 +01:00
parent 807d65ca53
commit 9ddd8ac21c
6 changed files with 609 additions and 57 deletions

View File

@ -2,7 +2,6 @@ use super::mbc::mbc::MBC;
#[derive(Debug,PartialEq)]
enum MemoryBankControllerType {
None,
MBC1,
MBC2,
MBC3,
@ -24,11 +23,11 @@ pub struct Cartridge {
impl Cartridge {
pub fn new(rom: Box<[u8]>, save_file: Option<String>) -> Cartridge {
let mbc_type: MemoryBankControllerType = match rom[0x0147] {
0x00 | 0x08 ... 0x09 => MemoryBankControllerType::None,
0x01 ... 0x03 => MemoryBankControllerType::MBC1,
0x05 ... 0x06 => MemoryBankControllerType::MBC2,
0x0F ... 0x13 => MemoryBankControllerType::MBC3,
let mbc_type = match rom[0x0147] {
0x00 | 0x08 ... 0x09 => None,
0x01 ... 0x03 => Some(MemoryBankControllerType::MBC1),
0x05 ... 0x06 => Some(MemoryBankControllerType::MBC2),
0x0F ... 0x13 => Some(MemoryBankControllerType::MBC3),
// 0xFF => MemoryBankControllerType::HuC1,
_ => panic!("Unsupported MBC type: {:02X}", rom[0x0147]),
};
@ -58,10 +57,10 @@ impl Cartridge {
let ram = Cartridge::load_savefile(&save_file, ram_size);
let mbc: Box<super::mbc::mbc::MBC> = match mbc_type {
MemoryBankControllerType::None => Box::new(super::mbc::mbc::NoMBC::new(rom, ram)),
MemoryBankControllerType::MBC1 => Box::new(super::mbc::mbc1::MBC1::new(rom, ram)),
MemoryBankControllerType::MBC2 => Box::new(super::mbc::mbc2::MBC2::new(rom, ram)),
MemoryBankControllerType::MBC3 => Box::new(super::mbc::mbc3::MBC3::new(rom, ram)),
None => Box::new(super::mbc::mbc::NoMBC::new(rom, ram)),
Some(MemoryBankControllerType::MBC1) => Box::new(super::mbc::mbc1::MBC1::new(rom, ram)),
Some(MemoryBankControllerType::MBC2) => Box::new(super::mbc::mbc2::MBC2::new(rom, ram)),
Some(MemoryBankControllerType::MBC3) => Box::new(super::mbc::mbc3::MBC3::new(rom, ram)),
};
Cartridge {
@ -71,7 +70,6 @@ impl Cartridge {
}
fn load_savefile(save_file: &Option<String>, ram_size: RamSize) -> Box<[u8]> {
let old_data;
let size = match ram_size {
RamSize::None => 0,
RamSize::Ram2KB => 2048,
@ -81,22 +79,22 @@ impl Cartridge {
if let &Some(ref filename) = save_file {
let data = super::read_file(&filename);
if let Ok(success) = data {
old_data = success
if let Ok(data) = data {
data
} else {
old_data = vec![0u8; size].into_boxed_slice();
// Generate empty buffer
println!("Warning: Could not open save file");
vec![0u8; size].into_boxed_slice()
}
} else {
old_data = vec![0u8; size].into_boxed_slice();
vec![0u8; size].into_boxed_slice()
}
old_data
}
pub fn save(&self) {
if let &Some(ref fil) = &self.savefile {
if let Some(ref fil) = self.savefile {
println!("Updating RAM dump");
self.mbc.dump_ram(fil);
println!("Ram dump updated.")
} else {
println!("Did not update ram dump");
}

View File

@ -102,7 +102,6 @@ impl CPU {
#[inline]
fn adc_r(&mut self, val: u8) {
// Passes the test
let old: u8 = self.regs[REG_A];
let mut new: u8 = old;
let c: u8;
@ -123,7 +122,6 @@ impl CPU {
#[inline]
fn add_r(&mut self, val: u8) {
// Passes the test
let old: u8 = self.regs[REG_A];
let new: u8 = old.wrapping_add(val);
let carry: u16 = (old as u16) ^ (val as u16) ^ (new as u16);
@ -137,7 +135,6 @@ impl CPU {
#[inline]
fn sbc_r(&mut self, val: u8) {
// Passes the test
let old: u8 = self.regs[REG_A];
let mut new: u8 = old as u8;
let c: u8;
@ -159,7 +156,6 @@ impl CPU {
#[inline]
fn sub_r(&mut self, val: u8) {
// Passes the test
let old: u8 = self.regs[REG_A];
let new: u8 = old.wrapping_sub(val);
let carry: u16 = (old as u16) ^ (val as u16) ^ (new as u16);
@ -173,7 +169,6 @@ impl CPU {
#[inline]
fn cp_r(&mut self, val: u8) {
// Passes the test
let old: u8 = self.regs[REG_A];
let new: u8 = old.wrapping_sub(val);
let carry: u16 = (old as u16) ^ (val as u16) ^ (new as u16);
@ -866,6 +861,7 @@ impl CPU {
pub fn run(&mut self) {
loop {
// Some attempt at limiting our speed
let mut cycles: i32 = 0;
let start = Instant::now();
for _ in 0 .. 1000 {
@ -877,8 +873,6 @@ impl CPU {
let delta = gb_dur - our_dur;
if delta > (20 * 238) { // We're at least 20 cycles faster.
sleep(Duration::new(0, delta as u32));
} else if delta < 0 {
print!("-");
}
}
}
@ -889,6 +883,8 @@ impl CPU {
let enabled = self.interconnect.read_byte(0xFFFF);
let e_pending = pending & enabled;
// Handling interrupts. Do only execute the one with the highest
// priority
if e_pending & interconnect::INTERRUPT_DISPLAY_VBLANK > 0 {
if execute {
self.handle_interrupt(0x40, interconnect::INTERRUPT_DISPLAY_VBLANK);
@ -1667,7 +1663,6 @@ impl CPU {
16
},
0xFB => {
// Enable interrupts - TODO
if self.debug {
println!("EI");
}

View File

@ -246,7 +246,7 @@ impl Display {
#[inline]
pub fn vblank_interrupt(&mut self) -> bool {
// Returns whether or not a vblank interrupt should be done
// Yes, this is polling, and yes, this sucks.\
// Yes, this is polling, and yes, this sucks.
if self.vblank_interrupt {
self.vblank_interrupt = false;
true

View File

@ -133,6 +133,9 @@ impl Interconnect {
pub fn tick(&mut self, cycles: u8) {
self.display.tick(cycles as u16);
self.timer.tick(cycles as u16);
for _ in 0..cycles {
self.sound.tick();
}
if self.display.vblank_interrupt() {
self.interrupt_request_flags |= INTERRUPT_DISPLAY_VBLANK;

View File

@ -12,7 +12,7 @@ impl Serial {
pub fn write_byte(&mut self, addr: u16, val: u8) {
match addr {
0xFF01 => {
println!("Serial: Write {:02X}", val);
// println!("Serial: Write {:02X}", val);
self.data = val;
}
0xFF02 => self.control = val,

View File

@ -1,17 +1,501 @@
const TICK_RATE: usize = 4194304;
const OUTPUT_SAMPLE_RATE: usize = 32768;
const TICKS_PER_SAMPLE: usize = TICK_RATE / OUTPUT_SAMPLE_RATE;
#[allow(dead_code)]
#[derive(Debug)]
enum EnvelopeDirection {
Increase,
Decrease
}
#[derive(Debug,Clone)]
struct VolumeEnvelope(u8);
#[allow(dead_code)]
impl VolumeEnvelope {
fn initial_volume(&self) -> u8 {
self.0 >> 4
}
fn envelope_direction(&self) -> EnvelopeDirection {
match self.0 & (1 << 3) {
0 => EnvelopeDirection::Decrease,
_ => EnvelopeDirection::Increase,
}
}
fn nr_of_envelope_sweep(&self) -> u8 {
self.0 & 0x7
}
fn set_nr_of_envelope_sweep(&mut self, nr: u8) {
self.0 &= !0x7;
self.0 |= nr & 0x7;
}
}
impl From<u8> for VolumeEnvelope {
fn from(val: u8) -> VolumeEnvelope {
VolumeEnvelope(val)
}
}
impl Into<u8> for VolumeEnvelope {
fn into(self) -> u8 {
self.0
}
}
impl Default for VolumeEnvelope {
fn default() -> VolumeEnvelope {
VolumeEnvelope(0)
}
}
trait OutputChannel {
fn sample(&mut self) -> i8;
fn clock_length_counter(&mut self); // 256 Hz
fn clock_volume_envelope(&mut self) -> Option<EnvelopeDirection>; // 64 Hz
// A square channel's frequency timer period is set to (2048-frequency)*4.
// Four duty cycles are available, each waveform taking 8 frequency timer clocks to cycle through:
}
#[derive(Debug)]
enum ChannelMode {
CounterMode,
ConsecutiveMode,
}
impl Default for ChannelMode {
fn default() -> Self {
ChannelMode::CounterMode
}
}
// Tone & Sweep
#[derive(Debug, Default)]
struct Channel1 {
// MMaped registers
sweep_register: u8,
sound_length: u8,
volume_envelope: VolumeEnvelope,
active_volume_envelope: VolumeEnvelope,
frequency_low: u8,
frequency_high: u8,
// Registeres used for sound generation
current_phase: f32,
// Depending on sound_length
//sound_enabled: bool,
// Current envelope volume
envelope_volume: u8,
current_mode: ChannelMode,
}
impl OutputChannel for Channel1 {
/*
According to some specification page:
gb = 2048 - (131072 / Hz)
Hz = 131072 / (2048 - gb)
*/
fn sample(&mut self) -> i8 {
// Right now just assume that we have 100% on time
// I also don't know right now how to disable channel1 :(
let freq = self.current_frequency();
// sample rate 128? Dunno!
self.current_phase += 1f32 / (2048f32 * TICKS_PER_SAMPLE as f32);
let sample = 2.0 * 3.1415 * freq * self.current_phase;
let volume = (127f32 * self.envelope_volume as f32 / 16f32) as i8;
if sample.sin() > 0f32 {
volume
} else {
-1 * volume
}
//(sample.sin() * volume as f32) as i8
}
fn clock_length_counter(&mut self) {
if self.frequency_high & (1 << 6) > 0 {
// Use sound length
let sound_length = self.sound_length & 0x3F;
if sound_length > 0 {
self.sound_length &= 0xC0;
self.sound_length |= sound_length - 1;
// self.sound_enabled = true;
} else {
// self.sound_enabled = false;
}
}
}
fn clock_volume_envelope(&mut self) -> Option<EnvelopeDirection> {
if self.active_volume_envelope.nr_of_envelope_sweep() > 0 {
let left = self.active_volume_envelope.nr_of_envelope_sweep();
self.active_volume_envelope.set_nr_of_envelope_sweep(left - 1);
match self.active_volume_envelope.envelope_direction() {
EnvelopeDirection::Decrease => {
if self.envelope_volume > 0 {
self.envelope_volume -= 1;
println!("CH1: Decreased envelope volume to {} (steps left: {})", self.envelope_volume, left-1);
}
},
EnvelopeDirection::Increase => {
if self.envelope_volume < 0xF {
self.envelope_volume += 1;
println!("CH1: Increased envelope volume to {}", self.envelope_volume);
}
}
}
Some(self.active_volume_envelope.envelope_direction())
} else {
match self.current_mode {
ChannelMode::CounterMode => {
self.envelope_volume = 0;
}
ChannelMode::ConsecutiveMode => {
}
};
None
}
}
}
impl Channel1 {
fn current_frequency(&self) -> f32 {
let full_freq = ((self.frequency_high as u16 & 0x7) << 8) | self.frequency_low as u16;
131072f32 / (2048f32 - full_freq as f32)
}
fn write_byte(&mut self, addr: u16, val: u8) {
match addr {
0xFF10 => {
println!("WARNING: Sound0 sweep not supported yet");
self.sweep_register = val
},
0xFF11 => {
// println!("Channel1: Sound length set to {} (wave pattern: {})", val & 0x3F, val >> 6);
self.sound_length = val;
},
0xFF12 => {
self.volume_envelope = val.into();
},
0xFF13 => self.frequency_low = val,
0xFF14 => {
self.frequency_high = val;
if val & (1 << 7) > 0 {
println!("CH1: started");
self.active_volume_envelope = self.volume_envelope.clone();
self.envelope_volume = self.active_volume_envelope.initial_volume();
}
self.current_mode = match val & (1 << 6) {
0 => ChannelMode::CounterMode,
_ => ChannelMode::ConsecutiveMode,
};
// self.dump();
self.frequency_high &= 0x7;
},
_ => panic!("Invalid addr for channel1 write: {:04X}", addr),
}
}
pub fn read_byte(&self, addr: u16) -> u8 {
match addr {
0xFF10 => self.sweep_register,
0xFF11 => self.sound_length,
0xFF12 => self.volume_envelope.0,
// 0xFF13 => self.frequency_low, // R/O
0xFF14 => self.frequency_high & (1 << 6), // Only one bit readable
_ => panic!("Tried to read invalid register {:04X}", addr)
}
}
}
// Tone
#[derive(Debug, Default)]
struct Channel2 {
sound_length: u8,
volume_envelope: VolumeEnvelope,
active_volume_envelope: VolumeEnvelope,
frequency_low: u8,
frequency_high: u8,
// Registeres used for sound generation
current_phase: f32,
// Depending on sound_length
// sound_enabled: bool,
// Current envelope volume
envelope_volume: u8,
current_mode: ChannelMode,
}
impl OutputChannel for Channel2 {
/*
According to some specification page:
gb = 2048 - (131072 / Hz)
Hz = 131072 / (2048 - gb)
*/
fn sample(&mut self) -> i8 {
// Right now just assume that we have 100% on time
// I also don't know right now how to disable channel1 :(
let freq = self.current_frequency();
// sample rate 128? Dunno!
self.current_phase += 1f32 / (2048f32 * TICKS_PER_SAMPLE as f32);
let sample = 2.0 * 3.1415 * freq * self.current_phase;
// let volume = (127f32 * self.envelope_volume as f32 / 16f32) as i8;
if sample.sin() > 0f32 {
40
} else {
-40
}
}
fn clock_length_counter(&mut self) {
if self.frequency_high & (1 << 6) > 0 {
// Use sound length
let sound_length = self.sound_length & 0x3F;
if sound_length > 0 {
self.sound_length &= 0xC0;
self.sound_length |= sound_length - 1;
}
}
}
fn clock_volume_envelope(&mut self) -> Option<EnvelopeDirection> {
if self.active_volume_envelope.nr_of_envelope_sweep() > 0 {
let left = self.active_volume_envelope.nr_of_envelope_sweep();
self.active_volume_envelope.set_nr_of_envelope_sweep(left - 1);
match self.active_volume_envelope.envelope_direction() {
EnvelopeDirection::Decrease => {
if self.envelope_volume > 0 {
self.envelope_volume -= 1;
println!("CH2: Decreased envelope volume to {} (steps left: {})", self.envelope_volume, left-1);
}
},
EnvelopeDirection::Increase => {
if self.envelope_volume < 0xF {
self.envelope_volume += 1;
println!("CH2: Increased envelope volume to {}", self.envelope_volume);
}
}
}
Some(self.active_volume_envelope.envelope_direction())
} else {
match self.current_mode {
ChannelMode::CounterMode => {
self.envelope_volume = 0;
}
ChannelMode::ConsecutiveMode => {
}
};
None
}
}
}
impl Channel2 {
fn current_frequency(&self) -> f32 {
let full_freq = ((self.frequency_high as u16 & 0x7) << 8) | self.frequency_low as u16;
131072f32 / (2048f32 - full_freq as f32)
}
fn write_byte(&mut self, addr: u16, val: u8) {
match addr {
0xFF16 => self.sound_length = val,
0xFF17 => {
self.volume_envelope = val.into();
},
0xFF18 => self.frequency_low = val,
0xFF19 => {
self.frequency_high = val;
if self.frequency_high & 0x80 == 0x80 {
println!("CH2: Started");
self.active_volume_envelope = self.volume_envelope.clone();
self.envelope_volume = self.active_volume_envelope.initial_volume();
}
self.current_mode = match val & (1 << 6) {
0 => ChannelMode::CounterMode,
_ => ChannelMode::ConsecutiveMode,
};
self.frequency_high &= 0x7;
},
_ => panic!("Invalid addr for channel2 write: {:04X}", addr),
}
}
pub fn read_byte(&self, addr: u16) -> u8 {
match addr {
0xFF16 => self.sound_length,
0xFF17 => self.volume_envelope.0,
// 0xFF18 => self.frequency_low, // R/O
0xFF19 => self.frequency_high & (1 << 6), // Only one bit readable
_ => panic!("Tried to read invalid register {:04X}", addr)
}
}
}
// Wave output
#[derive(Debug, Default)]
struct Channel3 {
enable: u8,
sound_length: u8,
output_level: u8,
frequency_low: u8,
frequency_high: u8,
wave_pattern_data: [u8; 16],
// Registeres used for sound generation
ticks_since_enable: usize,
current_pattern_offset: usize,
}
impl OutputChannel for Channel3 {
fn sample(&mut self) -> i8 {
if self.enable > 0 {
self.ticks_since_enable += 1;
let val = self.wave_pattern_data[self.current_pattern_offset/2];
let samp = if val & 1 == 0 {
val >> 4
} else {
val & 0x0F
};
let freq = self.current_frequency();
if (TICK_RATE as f32 / self.ticks_since_enable as f32) < (freq as f32) {
self.current_pattern_offset += 1;
self.current_pattern_offset %= 32;
self.ticks_since_enable = 0;
}
let samp = match self.output_level >> 5 {
0 => 0,
1 => samp,
2 => samp >> 1,
_ => samp >> 2,
};
let samp = samp << 4;
samp as i8
} else {
0
}
}
fn clock_length_counter(&mut self) {
if self.frequency_high & (1 << 6) > 0 {
// Use sound length
let sound_length = self.sound_length & 0x3F;
if sound_length > 0 {
self.sound_length &= 0xC0;
self.sound_length |= sound_length - 1;
// self.sound_enabled = true;
} else {
// self.sound_enabled = false;
self.enable = 0;
}
}
}
fn clock_volume_envelope(&mut self) -> Option<EnvelopeDirection> {
None
}
}
impl Channel3 {
fn current_frequency(&self) -> f32 {
let full_freq = ((self.frequency_high as u16 & 0x7) << 8) | self.frequency_low as u16;
(2048f32 - full_freq as f32) * 2f32
}
fn write_byte(&mut self, addr: u16, val: u8) {
match addr {
0xFF1A => {self.enable = val; self.ticks_since_enable = 0},
0xFF1B => self.sound_length = val,
0xFF1C => self.output_level = val,
0xFF1D => self.frequency_low = val,
0xFF1E => {
self.frequency_high = val;
// TODO: Check for start
println!("Starting sound3?");
self.frequency_high &= 0x7;
},
0xFF30...0xFF3F => {
self.wave_pattern_data[addr as usize - 0xFF30] = val;
}
_ => panic!("Invalid addr for channel3 write: {:04X}", addr),
}
}
pub fn read_byte(&self, addr: u16) -> u8 {
match addr {
0xFF1A => self.enable,
0xFF1B => self.sound_length,
0xFF1C => self.output_level,
//0xFF1D => self.frequency_low,
0xFF1E => self.frequency_high & 1 << 6,
0xFF30...0xFF3F => {
self.wave_pattern_data[addr as usize - 0xFF30]
}
_ => panic!("Tried to read invalid register {:04X}", addr)
}
}
}
// Noise output
#[derive(Debug, Default)]
struct Channel4 {
sound_length: u8,
volume_envelope: VolumeEnvelope,
polynominal_counter: u8,
counter_mode: u8,
}
impl Channel4 {
fn write_byte(&mut self, addr: u16, val: u8) {
match addr {
0xFF20 => self.sound_length = val,
0xFF21 => self.volume_envelope = val.into(),
0xFF22 => self.polynominal_counter = val,
0xFF23 => self.counter_mode = val,
_ => panic!("Invalid addr for channel4 write: {:04X}", addr),
}
}
pub fn read_byte(&self, addr: u16) -> u8 {
match addr {
0xFF20 => self.sound_length,
0xFF21 => self.volume_envelope.0,
0xFF22 => self.polynominal_counter,
0xFF23 => self.counter_mode,
_ => panic!("Tried to read invalid register {:04X}", addr)
}
}
}
#[derive(Debug, Default)]
pub struct Sound {
enabled: u8,
sound_length_1: u8,
sound_control_1: u8,
channel1: Channel1,
channel2: Channel2,
channel3: Channel3,
channel4: Channel4,
sound_channel_volume_control: u8,
sound_output_terminal_selector: u8,
sound_freq_low: u8,
sound_freq_high: u8,
channel2_sound_length: u8,
channel2_volume: u8,
channel2_freq_lo: u8,
channel2_freq_hi: u8,
ticks: usize,
}
impl Sound {
@ -19,42 +503,114 @@ impl Sound {
Sound::default()
}
pub fn write_byte(&mut self, addr: u16, val: u8) {
match addr {
0xFF11 => self.sound_length_1 = val,
0xFF12 => self.sound_control_1 = val,
0xFF13 => self.sound_freq_low = val,
0xFF14 => self.sound_freq_high = val,
pub fn tick(&mut self) {
self.ticks += 1;
// Every 64 Hz volume envelope => all 65536 ticks
// Every 256 Hz length check => all 16384 ticks
0xFF16 => self.channel2_sound_length = val,
0xFF17 => self.channel2_volume = val,
0xFF18 => self.channel2_freq_lo = val,
0xFF19 => self.channel2_freq_hi = val,
if self.ticks % 65536 == 0 {
self.channel1.clock_volume_envelope();
self.channel2.clock_volume_envelope();
}
if self.ticks % 16384 == 0 {
self.channel1.clock_length_counter();
self.channel2.clock_length_counter();
self.channel3.clock_length_counter();
}
// How high is our rate? 32768?
if self.ticks % TICKS_PER_SAMPLE == 0 {
// Mix channels.
let mut s01: i16 = 0;
let mut s02: i16 = 0;
let s01_volume = self.sound_channel_volume_control & 7;
let s02_volume = (self.sound_channel_volume_control >> 4) & 7;
// Take samples from the channels
let c1_sample = self.channel1.sample();
let c2_sample = self.channel2.sample();
// let c3_sample = self.channel3.sample();
// let c4_sample = self.channel4.sample();
//let c1_sample = 0;
//let c2_sample = 0;
let c3_sample = 0;
// Check S01
if s01_volume > 0 {
if self.sound_output_terminal_selector & 1 > 0 {
// Output Channel1
s01 += c1_sample as _;
}
if self.sound_output_terminal_selector & 2 > 0 && false {
// Output Channel2
s01 += c2_sample as _;
}
if self.sound_output_terminal_selector & 4 > 0 {
// Output Channel3
s01 += c3_sample as _;
}
if self.sound_output_terminal_selector & 8 > 0 {
// Output Channel4
}
}
if s02_volume > 0 {
if self.sound_output_terminal_selector & 0x10 > 0 && false {
// Output Channel1
s02 += c1_sample as _;
}
if self.sound_output_terminal_selector & 0x20 > 0 {
// Output Channel2
s02 += c2_sample as _;
}
if self.sound_output_terminal_selector & 0x40 > 0 {
// Output Channel3
s02 += c3_sample as _;
}
if self.sound_output_terminal_selector & 0x80 > 0 {
// Output Channel4
}
}
println!("S:{}:{}", s01 >> 0, s02 >> 0);
}
}
pub fn write_byte(&mut self, addr: u16, val: u8) {
println!("Snd: {:04X} = {:02X} ({:08b})", addr, val, val);
match addr {
0xFF10...0xFF14 => self.channel1.write_byte(addr, val),
0xFF16...0xFF19 => self.channel2.write_byte(addr, val),
0xFF1A...0xFF1E => self.channel3.write_byte(addr, val),
0xFF20...0xFF23 => self.channel4.write_byte(addr, val),
0xFF24 => self.sound_channel_volume_control = val,
0xFF25 => self.sound_output_terminal_selector = val,
0xFF26 => self.enabled = val,
0xFF30...0xFF3F => self.channel3.write_byte(addr, val),
_ => {
// println!("Sound: Write {:02X} to {:04X} unsupported", val, addr);
panic!("Sound: Write {:02X} to {:04X} unsupported", val, addr);
},
}
}
pub fn read_byte(&self, addr: u16) -> u8 {
println!("RD {:04X}", addr);
match addr {
0xFF11 => self.sound_length_1,
0xFF12 => self.sound_control_1,
0xFF13 => self.sound_freq_low,
0xFF14 => self.sound_freq_high,
0xFF16 => self.channel2_sound_length,
0xFF17 => self.channel2_volume,
0xFF18 => self.channel2_freq_lo,
0xFF19 => self.channel2_freq_hi,
0xFF10...0xFF14 => self.channel1.read_byte(addr),
0xFF16...0xFF19 => self.channel2.read_byte(addr),
0xFF1A...0xFF1E => self.channel3.read_byte(addr),
0xFF20...0xFF23 => self.channel4.read_byte(addr),
0xFF24 => self.sound_channel_volume_control,
0xFF25 => self.sound_output_terminal_selector,
0xFF26 => self.enabled,
0xFF30...0xFF3F => self.channel3.read_byte(addr),
_ => panic!("Sound: Read from {:04X} unsupported", addr),
}
}