diff --git a/doc/memory_map b/doc/memory_map index 21b02d7..2e6b83a 100644 --- a/doc/memory_map +++ b/doc/memory_map @@ -6,7 +6,7 @@ E000 FCFF Copy of the work RAM FE00 FE9F OAM (Sprite Attribute Table) FEA0 FEFF Unused FF00 FF7F Hardware IO -FE80 FFFE High RAM +FF80 FFFE High RAM FFFF FFFF Interrupt switch Hardware IO: diff --git a/src/cpu.rs b/src/cpu.rs index d330e98..c645d04 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -32,6 +32,8 @@ fn to_u16(bytes: Box<[u8]>) -> u16 { impl CPU { pub fn new(interconnect: interconnect::Interconnect) -> CPU { + // Ugly patch ^.^ + // interconnect.write_byte(0xFF44 as u16, 0x90); CPU { flags: 0, regs: [0, 0, 0, 0, 0, 0, 0], @@ -59,7 +61,8 @@ impl CPU { if reg_id == 7 { self.regs[REG_A] = value; } else if reg_id == 6 { - panic!("(HL) not supported yet."); + let addr: u16 = self.get_pair_value(REG_H, REG_L); + self.interconnect.write_byte(addr, value); } else { self.regs[reg_id] = value; } @@ -70,7 +73,8 @@ impl CPU { if reg_id == 7 { self.regs[REG_A] } else if reg_id == 6 { - panic!("(HL) not supported yet."); + let addr: u16 = self.get_pair_value(REG_H, REG_L); + self.interconnect.read_byte(addr) } else { self.regs[reg_id] } @@ -83,10 +87,75 @@ impl CPU { match instruction { 0x10 ... 0x17 => { let reg_id = (instruction - 0x10) as usize; - let args = self.load_args(1); - let mut val = self.get_8bit_reg(reg_id); - println!("RL {}, {}", REG_NAMES[reg_id], args[0]); - self.set_8bit_reg(reg_id, val.rotate_left(args[0] as u32)); + let val = self.get_8bit_reg(reg_id); + println!("RL {}", REG_NAMES[reg_id]); + self.regs[REG_A].rotate_left(val as u32); + } + 0x40 ... 0x47 => { + // Test 0th bit + let reg_id = (instruction - 0x40) as usize; + let reg_content = self.get_8bit_reg(reg_id); + println!("BIT 0, {}", REG_NAMES[reg_id]); + if reg_content & (1 << 0) == 0 { + self.flags |= FLAG_Z; + } else { + self.flags &= !FLAG_Z; + } + } + 0x48 ... 0x4F => { + // Test 1th bit + let reg_id = (instruction - 0x48) as usize; + let reg_content = self.get_8bit_reg(reg_id); + println!("BIT 1, {}", REG_NAMES[reg_id]); + if reg_content & (1 << 1) == 0 { + self.flags |= FLAG_Z; + } else { + self.flags &= !FLAG_Z; + } + } + 0x50 ... 0x57 => { + // Test 2th bit + let reg_id = (instruction - 0x50) as usize; + let reg_content = self.get_8bit_reg(reg_id); + println!("BIT 2, {}", REG_NAMES[reg_id]); + if reg_content & (1 << 2) == 0 { + self.flags |= FLAG_Z; + } else { + self.flags &= !FLAG_Z; + } + } + 0x58 ... 0x5F => { + // Test 3th bit + let reg_id = (instruction - 0x58) as usize; + let reg_content = self.get_8bit_reg(reg_id); + println!("BIT 3, {}", REG_NAMES[reg_id]); + if reg_content & (1 << 3) == 0 { + self.flags |= FLAG_Z; + } else { + self.flags &= !FLAG_Z; + } + } + 0x60 ... 0x67 => { + // Test 4th bit + let reg_id = (instruction - 0x60) as usize; + let reg_content = self.get_8bit_reg(reg_id); + println!("BIT 4, {}", REG_NAMES[reg_id]); + if reg_content & (1 << 4) == 0 { + self.flags |= FLAG_Z; + } else { + self.flags &= !FLAG_Z; + } + } + 0x68 ... 0x6F => { + // Test 5th bit + let reg_id = (instruction - 0x68) as usize; + let reg_content = self.get_8bit_reg(reg_id); + println!("BIT 5, {}", REG_NAMES[reg_id]); + if reg_content & (1 << 5) == 0 { + self.flags |= FLAG_Z; + } else { + self.flags &= !FLAG_Z; + } } 0x70 ... 0x77 => { // Test 6th bit @@ -117,51 +186,151 @@ impl CPU { } fn get_pair_value(&self, a: usize, b: usize) -> u16 { - (self.regs[a] as u16) | (self.regs[b] as u16) << 8 + (self.regs[a] as u16) << 8 | (self.regs[b] as u16) } fn set_pair_value(&mut self, a: usize, b: usize, value: u16) { - self.regs[a] = value as u8; - self.regs[b] = (value >> 8) as u8; + self.regs[a] = (value >> 8) as u8; + self.regs[b] = value as u8; + } + + fn dump_stack(&self) { + for i in self.sp .. 0xFFFE { + println!("[{:#04X}]: {:#02X}", i, self.interconnect.read_byte(i)); + } + } + + fn reg_inc(&mut self, reg_id: usize) { + self.regs[reg_id] = self.regs[reg_id].wrapping_add(1); + if self.regs[reg_id] == 0 { + self.flags |= FLAG_Z; + } + self.flags &= !FLAG_N; + } + + fn reg_dec(&mut self, reg_id: usize) { + self.regs[reg_id] = self.regs[reg_id].wrapping_sub(1); + if self.regs[reg_id] == 0 { + self.flags |= FLAG_Z; + } else { + self.flags &= !FLAG_Z; + } + self.flags |= FLAG_N; } pub fn run_instruction(&mut self) { let instruction = self.read_byte(self.ip); - print!("{:#04x}: {:#04x}: ", &self.ip, &instruction); + print!("{:#04x}: [SP: {:#04X}] i={:02X}. ", &self.ip, &self.sp, &instruction); + for i in 0 .. 6 { + print!("{}: {:02X} ", REG_NAMES[i], self.get_8bit_reg(i)); + } + print!("A: {:02X} ", self.regs[REG_A]); + print!("I: {:02X} ", self.interconnect.read_byte(0xFFFF)); + + // Flags + print!("Z={} ", self.flags & FLAG_Z != 0); + print!("N={} ", self.flags & FLAG_N != 0); + print!("H={} ", self.flags & FLAG_H != 0); + print!("C={} ", self.flags & FLAG_C != 0); self.ip += 1; match instruction { + 0x04 => { + println!("INC B"); + self.reg_inc(REG_B); + }, 0x05 => { println!("DEC B"); - self.regs[REG_B].wrapping_sub(1); - } + self.reg_dec(REG_B); + }, 0x06 => { let args = self.load_args(1); println!("LD B, {:02x}", args[0]); self.regs[REG_B] = args[0]; - } + }, 0x0C => { println!("INC C"); - self.regs[REG_C] += 1; - } + self.reg_inc(REG_C); + }, + 0x0D => { + println!("DEC C"); + self.reg_dec(REG_C); + }, 0x0E => { let args = self.load_args(1); println!("LD C, {:02x}", args[0]); self.regs[REG_C] = args[0]; - } + }, 0x11 => { let args = self.load_args(2); - println!("LD DE, {:02x}{:02x}", args[1], args[0]); + let val = to_u16(args); + println!("LD DE, {:04x}", val); + self.set_pair_value(REG_D, REG_E, val); + }, + 0x13 => { + println!("INC DE"); + let old_value = self.get_pair_value(REG_D, REG_E); + self.set_pair_value(REG_D, REG_E, old_value + 1); + }, + 0x14 => { + println!("INC D"); + self.reg_inc(REG_D); + }, + 0x15 => { + println!("DEC D"); + self.reg_dec(REG_D); + }, + 0x16 => { + let args = self.load_args(1); + println!("LD D, {:02x}", args[0]); self.regs[REG_D] = args[0]; - self.regs[REG_E] = args[1]; + }, + 0x17 => { + println!("RLA"); + let carry = self.flags & FLAG_C == FLAG_C; + if !carry { + // No carry before, now we got a carry => set it + if self.regs[REG_A] & 0x80 == 0x80 { + self.flags |= FLAG_C + } + self.regs[REG_A] = self.regs[REG_A] << 1; + } else { + if self.regs[REG_A] & 0x80 == 0 { + self.flags &= !FLAG_C; + } + self.regs[REG_A] = self.regs[REG_A] << 1 | 1; + } + }, + 0x18 => { + let args = self.load_args(1); + println!("JR {:02X}", args[0] as i8); + let off: i8 = args[0] as i8; + if off < 0 { + self.ip -= (-off) as u16; + } else { + self.ip += off as u16; + } }, 0x1A => { println!("LD A, (DE)"); self.regs[REG_A] = self.interconnect.read_byte(self.get_pair_value(REG_D, REG_E)); - } + }, + 0x1C => { + println!("INC E"); + self.reg_inc(REG_E); + }, + 0x1D => { + println!("DEC E"); + self.reg_dec(REG_E); + }, + 0x1E => { + let args = self.load_args(1); + println!("LD E, {:02x}", args[0]); + self.regs[REG_E] = args[0]; + }, 0x20 => { let args = self.load_args(1); - println!("JR NZ {:02x}", args[0]); + println!("JR NZ {:02x}", args[0] as i8); if self.flags & FLAG_Z == 0 { let offset = args[0] as i8; if offset < 0 { @@ -172,16 +341,58 @@ impl CPU { } } 0x21 => { - let args = self.load_args(2); - println!("LD HL, {:02x}{:02x}", args[1], args[0]); - self.regs[REG_H] = args[0]; - self.regs[REG_L] = args[1]; + let value = to_u16(self.load_args(2)); + println!("LD HL, {:04x}", value); + self.set_pair_value(REG_H, REG_L, value); + }, + 0x22 => { + println!("LD (HL+), A"); + let addr: u16 = self.get_pair_value(REG_H, REG_L); + self.interconnect.write_byte(addr, self.regs[REG_A]); + self.set_pair_value(REG_H, REG_L, addr + 1); + }, + 0x23 => { + println!("INC HL"); + let old_value = self.get_pair_value(REG_H, REG_L); + self.set_pair_value(REG_H, REG_L, old_value + 1); + }, + 0x24 => { + println!("INC H"); + self.reg_inc(REG_H); + }, + 0x25 => { + println!("DEC H"); + self.reg_dec(REG_H); }, 0x26 => { let args = self.load_args(1); println!("LD H, {:02x}", args[0]); self.regs[REG_H] = args[0]; }, + 0x28 => { + let args = self.load_args(1); + let mut target; + if args[0] < 0 { + target = self.ip - (-(args[0] as i8)) as u16; + } else { + target = self.ip + args[0] as u16; + } + println!("LR Z, {:04X}", target); + self.ip = target; + }, + 0x2C => { + println!("INC L"); + self.reg_inc(REG_L); + }, + 0x2D => { + println!("DEC L"); + self.reg_dec(REG_L); + }, + 0x2E => { + let args = self.load_args(1); + println!("LD L, {:02x}", args[0]); + self.regs[REG_L] = args[0]; + }, 0x31 => { let args = self.load_args(2); self.sp = to_u16(args); @@ -189,18 +400,33 @@ impl CPU { }, 0x32 => { println!("LD (HL-), A"); - let mut addr: u16 = (self.regs[REG_H] as u16) | ((self.regs[REG_L] as u16) << 8); + let mut addr = self.get_pair_value(REG_H, REG_L); self.interconnect.write_byte(addr, self.regs[REG_A]); addr -= 1; - self.regs[REG_H] = addr as u8; - self.regs[REG_L] = (addr >> 8) as u8; + self.set_pair_value(REG_H, REG_L, addr); }, + 0x36 => { + let args = self.load_args(1); + println!("LD (HL), {:02x}", args[0]); + let addr = self.get_pair_value(REG_H, REG_L); + self.interconnect.write_byte(addr, args[0]); + }, + 0x3C => { + println!("INC A"); + self.reg_inc(REG_A); + } + 0x3D => { + println!("DEC A"); + self.reg_dec(REG_A); + } 0x3E => { let args = self.load_args(1); println!("LD A, {:02x}", args[0]); self.regs[REG_A] = args[0]; }, + + // LDs 0x40 ... 0x47 => { let reg_id = (instruction - 0x40) as usize; println!("LD B, {}", REG_NAMES[reg_id]); @@ -235,7 +461,7 @@ impl CPU { let reg_id = (instruction - 0x70) as usize; println!("LD (HL), {}", REG_NAMES[reg_id]); let reg_value: u8 = self.get_8bit_reg(reg_id); - let addr: u16 = (self.regs[REG_L] as u16) << 8 | (self.regs[REG_H] as u16); + let addr: u16 = self.get_pair_value(REG_H, REG_L); self.interconnect.write_byte(addr, reg_value); }, @@ -244,10 +470,81 @@ impl CPU { println!("LD A, {}", REG_NAMES[reg_id]); self.regs[REG_A] = self.get_8bit_reg(reg_id); }, + + // ADD + 0x80 ... 0x87 => { + let reg_id = (instruction - 0x80) as usize; + println!("ADD {}", REG_NAMES[reg_id]); + self.regs[REG_A] = self.regs[REG_A].wrapping_add(self.get_8bit_reg(reg_id)); + self.flags &= !FLAG_N; + } + + // ADC + 0x88 ... 0x8F => { + let reg_id = (instruction - 0x88) as usize; + println!("ADC {}", REG_NAMES[reg_id]); + self.regs[REG_A] = self.regs[REG_A].wrapping_add(self.get_8bit_reg(reg_id)); + if self.flags & FLAG_C == FLAG_C { + self.regs[REG_A] = self.regs[REG_A].wrapping_add(1); + } + self.flags &= !FLAG_N; + } + + // SUBs + 0x90 ... 0x97 => { + let reg_id = (instruction - 0x90) as usize; + println!("SUB {}", REG_NAMES[reg_id]); + self.regs[REG_A] = self.regs[REG_A].wrapping_sub(self.get_8bit_reg(reg_id)); + self.flags |= FLAG_N; + if self.regs[REG_A] == 0 { + self.flags |= FLAG_Z; + } else { + self.flags &= !FLAG_Z; + } + // TODO: H, C + } + + // SBC + 0x98 ... 0x9F => { + let reg_id = (instruction - 0x98) as usize; + println!("SBC {}", REG_NAMES[reg_id]); + self.regs[REG_A] = self.regs[REG_A].wrapping_sub(self.get_8bit_reg(reg_id)); + if self.flags & FLAG_C == FLAG_C { + self.regs[REG_A] = self.regs[REG_A].wrapping_sub(1); + } + self.flags |= FLAG_N; + } + + // XOR 0xA8 ... 0xAF => { let reg_id = (instruction - 0xA8) as usize; println!("XOR {}", REG_NAMES[reg_id]); self.regs[REG_A] ^= self.get_8bit_reg(reg_id); + if self.regs[REG_A] == 0 { + self.flags |= FLAG_Z; + } else { + self.flags &= !FLAG_Z; + } + self.flags &= !FLAG_C; + self.flags &= !FLAG_N; + self.flags &= !FLAG_H; + }, + + // CP + 0xB8 ... 0xBF => { + let reg_id = (instruction - 0xB8) as usize; + println!("CP {}", REG_NAMES[reg_id]); + let val = self.get_8bit_reg(reg_id); + if self.regs[REG_A] < self.get_8bit_reg(reg_id) { + self.flags |= FLAG_C; + self.flags &= !FLAG_Z; + } else if self.regs[REG_A] == self.get_8bit_reg(reg_id) { + self.flags |= FLAG_Z; + self.flags &= !FLAG_C; + } else { + self.flags &= !FLAG_Z; + self.flags &= !FLAG_C; + } }, 0xC1 => { println!("POP BC"); @@ -258,9 +555,15 @@ impl CPU { 0xC5 => { println!("PUSH BC"); let val: u16 = self.get_pair_value(REG_B, REG_C); - self.interconnect.write_word(self.sp, val); + self.interconnect.write_word(self.sp - 2, val); self.sp -= 2; }, + 0xC9 => { + println!("RET"); + self.dump_stack(); + self.ip = self.interconnect.read_word(self.sp+1); + self.sp += 2; + }, 0xCB => { // Prefix CB. This is annoying. self.run_prefix_instruction(); @@ -273,23 +576,56 @@ impl CPU { // self.interconnect.write_byte(self.sp - 1, (self.ip >> 8) as u8); self.interconnect.write_word(self.sp - 1, self.ip); self.sp -= 2; + self.dump_stack(); self.ip = target; - } + }, 0xE0 => { let args = self.load_args(1); println!("LDH {:02X}, A", args[0]); self.interconnect.write_byte(0xFF00 + args[0] as u16, self.regs[REG_A]); - } + }, 0xE2 => { println!("LD (C), A"); + println!("[{:04X}] = {:02X}", 0xFF00 + self.regs[REG_C] as u16, self.regs[REG_A]); self.interconnect.write_byte(0xFF00 + self.regs[REG_C] as u16, self.regs[REG_A]) + }, + 0xEA => { + let addr = to_u16(self.load_args(2)); + println!("LD ({:04X}), A", addr); + self.interconnect.write_byte(addr, self.regs[REG_A]); } + 0xF0 => { + let args = self.load_args(1); + println!("LDH A, {:02X}", args[0]); + self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + args[0] as u16); + }, 0xFB => { // Enable interrupts - TODO println!("EI"); + self.interconnect.write_byte(0xFFFF, 0x01); + panic!("Uh uh"); + }, + 0xFE => { + let args = self.load_args(1); + println!("CP {:02X}", args[0]); + self.flags |= FLAG_N; + if args[0] > self.regs[REG_A] { + self.flags |= FLAG_C; + } else { + self.flags &= !FLAG_C; + } + if args[0] == self.regs[REG_A] { + self.flags |= FLAG_Z; + } else { + self.flags &= !FLAG_Z; + } + + // TODO H } _ => panic!("Unknown instruction: {:02x}", instruction) } + // self.dump_stack(); + self.interconnect.tick(); } } diff --git a/src/display.rs b/src/display.rs new file mode 100644 index 0000000..5672250 --- /dev/null +++ b/src/display.rs @@ -0,0 +1,51 @@ +#[derive(Debug, Default)] +pub struct Display { + control: u8, + status: u8, + background_palette: u8, + scrollx: u8, + scrolly: u8, + curline: u8, + // TODO +} + +impl Display { + pub fn new() -> Display { + Display::default() + } + + pub fn write_byte(&mut self, addr: u16, val: u8) { + match addr { + 0xFF40 => self.control = val, + 0xFF41 => self.status = val, + 0xFF42 => self.scrolly = val, + 0xFF43 => self.scrollx = val, + 0xFF44 => self.curline = 0, + 0xFF47 => self.background_palette = val, + _ => panic!("Display: Write {:02X} to {:04X} unsupported", val, addr), + } + } + + pub fn read_byte(&self, addr: u16) -> u8 { + match addr { + 0xFF40 => self.control, + 0xFF41 => self.status, + 0xFF42 => self.scrolly, + 0xFF43 => self.scrollx, + 0xFF44 => self.curline, + 0xFF47 => self.background_palette, + _ => panic!("Display: Read from {:04X} unsupported", addr), + } + } + + pub fn vblank_interrupt(&mut self) { + self.curline += 1; + if self.curline > 153 { + self.curline = 0; + } + } + + pub fn controller_interrupt(&self) { + + } +} diff --git a/src/interconnect.rs b/src/interconnect.rs index c54cf3f..af4675e 100644 --- a/src/interconnect.rs +++ b/src/interconnect.rs @@ -1,13 +1,24 @@ const RAM_SIZE: usize = 0x2000; const VRAM_SIZE: usize = 0x2000; -const HIRAM_SIZE: usize = (0xFFFE - 0xFE80) + 1; +const HIRAM_SIZE: usize = (0xFFFE - 0xFF80) + 1; + +const INTERRUPT_DISPLAY: u8 = 1 << 1; +const INTERRUPT_DISPLAY_VBLANK: u8 = 1 << 0; + +use super::display; +use super::sound; pub struct Interconnect { bios: Box<[u8]>, rom: Box<[u8]>, ram: Box<[u8]>, vram: Box<[u8]>, - hiram: Box<[u8]> + hiram: Box<[u8]>, + sound: sound::Sound, + display: display::Display, + interrupt: u8, + timer: u8, + disable_bootrom: u8, } impl Interconnect { @@ -18,6 +29,32 @@ impl Interconnect { ram: vec![0; RAM_SIZE].into_boxed_slice(), vram: vec![0; VRAM_SIZE].into_boxed_slice(), hiram: vec![0; HIRAM_SIZE].into_boxed_slice(), + sound: sound::Sound::new(), + display: display::Display::new(), + interrupt: 0, + timer: 0, + disable_bootrom: 0, + } + } + + // Somehow we need different timers for this. + pub fn tick(&mut self) { + self.timer += 1; + if self.timer == 5 { + self.display_blank_interrupt(); + self.timer = 0; + } + } + + pub fn display_blank_interrupt(&mut self) { + if self.interrupt & INTERRUPT_DISPLAY_VBLANK >= 0 { + self.display.vblank_interrupt(); + } + } + + pub fn display_interrupt(&mut self) { + if self.interrupt & INTERRUPT_DISPLAY > 0 { + self.display.controller_interrupt(); } } @@ -28,7 +65,7 @@ impl Interconnect { match addr { 0x0000 ... 0x7FFF => { // TODO: Check if bios or cartridge (additional condition: isEnabled) - if addr < 0x100 { + if addr < 0x100 && self.disable_bootrom == 0 { self.bios[addr as usize] } else { self.rom[addr as usize] @@ -40,8 +77,20 @@ impl Interconnect { 0xC000 ... 0xDFFF => { self.ram[(addr - 0xC000) as usize] }, - 0xFE80 ... 0xFFFE => { - self.hiram[(addr - 0xFE80) as usize] + 0xFF10 ... 0xFF26 => { + self.sound.read_byte(addr) + }, + 0xFF40 ... 0xFF4B => { + self.display.read_byte(addr) + }, + 0xFF50 => { + self.disable_bootrom + }, + 0xFF80 ... 0xFFFE => { + self.hiram[(addr - 0xFF80) as usize] + }, + 0xFFFF => { + self.interrupt } _ => { panic!("Read from {:04X} not supported.", addr); @@ -61,7 +110,7 @@ impl Interconnect { FE00 FE9F OAM (Sprite Attribute Table) FEA0 FEFF Unused FF00 FF7F Hardware IO - FE80 FFFE High RAM + FF80 FFFE High RAM FFFF FFFF Interrupt switch */ @@ -72,9 +121,21 @@ impl Interconnect { 0xC000 ... 0xDFFF => { self.ram[(addr - 0xC000) as usize] = val; }, - 0xFE80 ... 0xFFFE => { - self.hiram[(addr - 0xFE80) as usize] = val; + 0xFF10 ... 0xFF26 => { + self.sound.write_byte(addr, val); + }, + 0xFF40 ... 0xFF4B => { + self.display.write_byte(addr, val); + }, + 0xFF50 => { + self.disable_bootrom = val; } + 0xFF80 ... 0xFFFE => { + self.hiram[(addr - 0xFF80) as usize] = val; + }, + 0xFFFF => { + self.interrupt = val; + }, _ => { panic!("Write {:02X} to {:04X} not supported.", val, addr); } diff --git a/src/main.rs b/src/main.rs index a36d051..6205f70 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,10 @@ use std::fs; use std::env; mod cpu; +mod display; mod interconnect; +mod sound; + fn main() { let bios_path = env::args().nth(1).unwrap(); @@ -16,11 +19,11 @@ fn main() { let rom = read_rom(&rom_path); // Now we need to execute commands - let mut interconnect = interconnect::Interconnect::new(bios, rom); - let mut CPU = cpu::CPU::new(interconnect); + let interconnect = interconnect::Interconnect::new(bios, rom); + let mut cpu = cpu::CPU::new(interconnect); loop { - CPU.run_instruction(); + cpu.run_instruction(); } } diff --git a/src/sound.rs b/src/sound.rs new file mode 100644 index 0000000..d345cd6 --- /dev/null +++ b/src/sound.rs @@ -0,0 +1,42 @@ +#[derive(Debug, Default)] +pub struct Sound { + enabled: u8, + sound_length_1: u8, + sound_control_1: u8, + sound_channel_volume_control: u8, + sound_output_terminal_selector: u8, + sound_freq_low: u8, + sound_freq_high: u8, +} + +impl Sound { + pub fn new() -> 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, + 0xFF24 => self.sound_channel_volume_control = val, + 0xFF25 => self.sound_output_terminal_selector = val, + 0xFF26 => self.enabled = val, + _ => panic!("Sound: Write {:02X} to {:04X} unsupported", val, addr), + } + } + + pub fn read_byte(&self, addr: u16) -> u8 { + match addr { + 0xFF11 => self.sound_length_1, + 0xFF12 => self.sound_control_1, + 0xFF13 => self.sound_freq_low, + 0xFF14 => self.sound_freq_high, + 0xFF24 => self.sound_channel_volume_control, + 0xFF25 => self.sound_output_terminal_selector, + 0xFF26 => self.enabled, + _ => panic!("Sound: Read from {:04X} unsupported", addr), + } + } +}