From 396e87304e6e47a332a9dfeb835763d6a80edec5 Mon Sep 17 00:00:00 2001 From: Kevin Hamacher Date: Thu, 26 May 2016 15:47:08 +0200 Subject: [PATCH] Serial and Timer class, cycle counting Also a little bit of refactoring. Move VRAM to the display object --- src/cpu.rs | 155 ++++++++++++++++++++++++++++++++++++++++---- src/display.rs | 118 ++++++++++++++++++++++++++++++++- src/interconnect.rs | 58 ++++++++++++----- src/main.rs | 3 +- src/serial.rs | 27 ++++++++ src/timer.rs | 30 +++++++++ 6 files changed, 359 insertions(+), 32 deletions(-) create mode 100644 src/serial.rs create mode 100644 src/timer.rs diff --git a/src/cpu.rs b/src/cpu.rs index c645d04..aee7a27 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -24,6 +24,8 @@ pub struct CPU { sp: u16, interconnect: interconnect::Interconnect, + + interrupts_enabled: bool, } fn to_u16(bytes: Box<[u8]>) -> u16 { @@ -39,7 +41,8 @@ impl CPU { regs: [0, 0, 0, 0, 0, 0, 0], ip: 0, sp: 0xFFFE, - interconnect: interconnect + interconnect: interconnect, + interrupts_enabled: false, // Is this correct? } } @@ -234,56 +237,80 @@ impl CPU { print!("C={} ", self.flags & FLAG_C != 0); self.ip += 1; + let mut cycles: u16; + match instruction { + 0x00 => { + println!("NOP"); + cycles = 4; + }, + 0x01 => { + println!("LD (BC), A"); + let addr: u16 = self.get_pair_value(REG_B, REG_C); + let val: u8 = self.regs[REG_A]; + self.interconnect.write_byte(addr, val); + cycles = 12; + } 0x04 => { println!("INC B"); self.reg_inc(REG_B); + cycles = 4; }, 0x05 => { println!("DEC B"); self.reg_dec(REG_B); + cycles = 4; }, 0x06 => { let args = self.load_args(1); println!("LD B, {:02x}", args[0]); self.regs[REG_B] = args[0]; + cycles = 8; }, 0x0C => { println!("INC C"); self.reg_inc(REG_C); + cycles = 4; }, 0x0D => { println!("DEC C"); self.reg_dec(REG_C); + cycles = 4; }, 0x0E => { let args = self.load_args(1); println!("LD C, {:02x}", args[0]); self.regs[REG_C] = args[0]; + cycles = 8; }, 0x11 => { let args = self.load_args(2); let val = to_u16(args); println!("LD DE, {:04x}", val); self.set_pair_value(REG_D, REG_E, val); + cycles = 12; }, 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); + cycles = 8; }, 0x14 => { println!("INC D"); self.reg_inc(REG_D); + cycles = 4; }, 0x15 => { println!("DEC D"); self.reg_dec(REG_D); + cycles = 4; }, 0x16 => { let args = self.load_args(1); println!("LD D, {:02x}", args[0]); self.regs[REG_D] = args[0]; + cycles = 8; }, 0x17 => { println!("RLA"); @@ -300,6 +327,7 @@ impl CPU { } self.regs[REG_A] = self.regs[REG_A] << 1 | 1; } + cycles = 4; }, 0x18 => { let args = self.load_args(1); @@ -310,23 +338,28 @@ impl CPU { } else { self.ip += off as u16; } + cycles = 12; }, 0x1A => { println!("LD A, (DE)"); self.regs[REG_A] = self.interconnect.read_byte(self.get_pair_value(REG_D, REG_E)); + cycles = 8; }, 0x1C => { println!("INC E"); self.reg_inc(REG_E); + cycles = 4; }, 0x1D => { println!("DEC E"); self.reg_dec(REG_E); + cycles = 4; }, 0x1E => { let args = self.load_args(1); println!("LD E, {:02x}", args[0]); self.regs[REG_E] = args[0]; + cycles = 8; }, 0x20 => { let args = self.load_args(1); @@ -338,36 +371,45 @@ impl CPU { } else { self.ip += offset as u16; } + cycles = 12; + } else { + cycles = 8; } } 0x21 => { let value = to_u16(self.load_args(2)); println!("LD HL, {:04x}", value); self.set_pair_value(REG_H, REG_L, value); + cycles = 12; }, 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); + cycles = 8; }, 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); + cycles = 8; }, 0x24 => { println!("INC H"); self.reg_inc(REG_H); + cycles = 4; }, 0x25 => { println!("DEC H"); self.reg_dec(REG_H); + cycles = 4; }, 0x26 => { let args = self.load_args(1); println!("LD H, {:02x}", args[0]); self.regs[REG_H] = args[0]; + cycles = 8; }, 0x28 => { let args = self.load_args(1); @@ -377,26 +419,35 @@ impl CPU { } else { target = self.ip + args[0] as u16; } - println!("LR Z, {:04X}", target); - self.ip = target; + println!("JR Z, {:04X}", target); + if self.flags & FLAG_Z > 0 { + self.ip = target; + cycles = 12; + } else { + cycles = 8; + } }, 0x2C => { println!("INC L"); self.reg_inc(REG_L); + cycles = 4; }, 0x2D => { println!("DEC L"); self.reg_dec(REG_L); + cycles = 4; }, 0x2E => { let args = self.load_args(1); println!("LD L, {:02x}", args[0]); self.regs[REG_L] = args[0]; + cycles = 8; }, 0x31 => { let args = self.load_args(2); self.sp = to_u16(args); println!("LD SP, {:02x}", self.sp); + cycles = 12; }, 0x32 => { println!("LD (HL-), A"); @@ -405,25 +456,30 @@ impl CPU { addr -= 1; self.set_pair_value(REG_H, REG_L, addr); + cycles = 8; }, 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]); + cycles = 12; }, 0x3C => { println!("INC A"); self.reg_inc(REG_A); + cycles = 4; } 0x3D => { println!("DEC A"); self.reg_dec(REG_A); + cycles = 4; } 0x3E => { let args = self.load_args(1); println!("LD A, {:02x}", args[0]); self.regs[REG_A] = args[0]; + cycles = 8; }, // LDs @@ -431,44 +487,52 @@ impl CPU { let reg_id = (instruction - 0x40) as usize; println!("LD B, {}", REG_NAMES[reg_id]); self.regs[REG_B] = self.get_8bit_reg(reg_id); + cycles = 4; }, 0x48 ... 0x4F => { let reg_id = (instruction - 0x48) as usize; println!("LD C, {}", REG_NAMES[reg_id]); self.regs[REG_C] = self.get_8bit_reg(reg_id); + cycles = 4; }, 0x50 ... 0x57 => { let reg_id = (instruction - 0x50) as usize; println!("LD D, {}", REG_NAMES[reg_id]); self.regs[REG_D] = self.get_8bit_reg(reg_id); + cycles = 4; }, 0x58 ... 0x5F => { let reg_id = (instruction - 0x58) as usize; println!("LD E, {}", REG_NAMES[reg_id]); self.regs[REG_E] = self.get_8bit_reg(reg_id); + cycles = 4; }, 0x60 ... 0x67 => { let reg_id = (instruction - 0x60) as usize; println!("LD H, {}", REG_NAMES[reg_id]); self.regs[REG_H] = self.get_8bit_reg(reg_id); + cycles = 4; }, 0x68 ... 0x6F => { let reg_id = (instruction - 0x68) as usize; println!("LD L, {}", REG_NAMES[reg_id]); self.regs[REG_L] = self.get_8bit_reg(reg_id); + cycles = 4; }, 0x70 ... 0x75 | 0x77 => { 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.get_pair_value(REG_H, REG_L); - + cycles = 8; + self.interconnect.write_byte(addr, reg_value); }, 0x78 ... 0x7F => { let reg_id = (instruction - 0x78) as usize; println!("LD A, {}", REG_NAMES[reg_id]); self.regs[REG_A] = self.get_8bit_reg(reg_id); + cycles = 4; }, // ADD @@ -477,6 +541,7 @@ impl CPU { 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; + cycles = 4; } // ADC @@ -488,6 +553,7 @@ impl CPU { self.regs[REG_A] = self.regs[REG_A].wrapping_add(1); } self.flags &= !FLAG_N; + cycles = 4; } // SUBs @@ -502,6 +568,7 @@ impl CPU { self.flags &= !FLAG_Z; } // TODO: H, C + cycles = 4; } // SBC @@ -513,6 +580,7 @@ impl CPU { self.regs[REG_A] = self.regs[REG_A].wrapping_sub(1); } self.flags |= FLAG_N; + cycles = 4; } // XOR @@ -528,6 +596,7 @@ impl CPU { self.flags &= !FLAG_C; self.flags &= !FLAG_N; self.flags &= !FLAG_H; + cycles = 4; }, // CP @@ -545,66 +614,127 @@ impl CPU { self.flags &= !FLAG_Z; self.flags &= !FLAG_C; } + cycles = 4; }, 0xC1 => { println!("POP BC"); let val: u16 = self.interconnect.read_word(self.sp); self.sp += 2; self.set_pair_value(REG_B, REG_C, val); + cycles = 12; + }, + 0xC2 => { + let addr: u16 = to_u16(self.load_args(2)); + println!("JP NZ {:04X}", addr); + if self.flags & FLAG_Z == 0 { + self.ip = addr; + cycles = 16; + } else { + cycles = 12; + } + }, + 0xC3 => { + let addr: u16 = to_u16(self.load_args(2)); + println!("JP {:04X}", addr); + self.ip = addr; + cycles = 16; + } + 0xC4 => { + let target = to_u16(self.load_args(2)); + println!("CALL NZ {:04X}", &target); + if self.flags & FLAG_Z == 0 { + // Push current IP to the stack + self.interconnect.write_word(self.sp - 1, self.ip); + self.sp -= 2; + self.ip = target; + cycles = 24; + } else { + cycles = 12; + } }, 0xC5 => { println!("PUSH BC"); let val: u16 = self.get_pair_value(REG_B, REG_C); self.interconnect.write_word(self.sp - 2, val); self.sp -= 2; + cycles = 16; }, 0xC9 => { println!("RET"); self.dump_stack(); self.ip = self.interconnect.read_word(self.sp+1); self.sp += 2; + cycles = 16; }, 0xCB => { // Prefix CB. This is annoying. self.run_prefix_instruction(); + cycles = 12; // TODO: Verify that this is the case for all prefix instructions. + }, + 0xCC => { + let target = to_u16(self.load_args(2)); + println!("CALL Z {:04X}", &target); + if self.flags & FLAG_Z > 0 { + // Push current IP to the stack + self.interconnect.write_word(self.sp - 1, self.ip); + self.sp -= 2; + self.ip = target; + cycles = 24; + } else { + cycles = 12; + } }, 0xCD => { let target = to_u16(self.load_args(2)); println!("CALL {:04X}", &target); // Push current IP to the stack - // self.interconnect.write_byte(self.sp, (self.ip & 0xFF) as u8); - // 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; + cycles = 24; }, 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]); + cycles = 12; }, 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]) + self.interconnect.write_byte(0xFF00 + self.regs[REG_C] as u16, self.regs[REG_A]); + cycles = 8; }, 0xEA => { let addr = to_u16(self.load_args(2)); println!("LD ({:04X}), A", addr); self.interconnect.write_byte(addr, self.regs[REG_A]); + cycles = 16; } 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); + cycles = 12; }, + 0xF2 => { + println!("LD A, (C)"); + self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + self.regs[REG_C] as u16); + cycles = 8; + }, + 0xF3 => { + println!("DI"); + self.interrupts_enabled = false; + cycles = 4; + } 0xFB => { // Enable interrupts - TODO println!("EI"); - self.interconnect.write_byte(0xFFFF, 0x01); - panic!("Uh uh"); + self.interrupts_enabled = true; + // self.interconnect.write_byte(0xFFFF, 0x01); + // panic!("Uh uh"); + cycles = 4; + panic!("ENABLING INTERRUPTS - TODO"); }, 0xFE => { let args = self.load_args(1); @@ -622,10 +752,11 @@ impl CPU { } // TODO H + cycles = 8; } _ => panic!("Unknown instruction: {:02x}", instruction) } // self.dump_stack(); - self.interconnect.tick(); + self.interconnect.tick(cycles); } } diff --git a/src/display.rs b/src/display.rs index 5672250..949e571 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,43 +1,159 @@ +const VRAM_SIZE: usize = 0x2000; + +const TICKS_END_SCANLINE: u16 = 80; +const TICKS_END_READMODE: u16 = 172; +const TICKS_END_HBLANK: u16 = 204; +const TICKS_END_VBLANK: u16 = 456; + +#[derive(Debug)] +enum DisplayMode { + Scanline, + Readmode, + HBlank, + VBlank, +} + +impl Default for DisplayMode { + fn default() -> DisplayMode { + DisplayMode::Scanline + } +} + #[derive(Debug, Default)] pub struct Display { control: u8, status: u8, background_palette: u8, + object_palette_0: u8, + object_palette_1: u8, scrollx: u8, scrolly: u8, + windowx: u8, + windowy: u8, curline: u8, + + vram: Box<[u8]>, + + current_ticks: u16, + current_mode: DisplayMode, // TODO } impl Display { pub fn new() -> Display { - Display::default() + Display { + control: 0, + status: 0, + background_palette: 0, + object_palette_0: 0, + object_palette_1: 0, + scrollx: 0, + scrolly: 0, + windowx: 0, + windowy: 0, + curline: 0, + + current_ticks: 0, + current_mode: DisplayMode::default(), + vram: vec![0; VRAM_SIZE].into_boxed_slice(), + } } pub fn write_byte(&mut self, addr: u16, val: u8) { match addr { + 0x8000 ... 0x9FFF => self.vram[(addr - 0x8000) as usize] = val, 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, + 0xFF48 => self.object_palette_0 = val, + 0xFF49 => self.object_palette_1 = val, + 0xFF4A => self.windowy = val, + 0xFF4B => self.windowx = val, _ => panic!("Display: Write {:02X} to {:04X} unsupported", val, addr), } } pub fn read_byte(&self, addr: u16) -> u8 { match addr { + 0x8000 ... 0x9FFF => self.vram[(addr - 0x8000) as usize], 0xFF40 => self.control, 0xFF41 => self.status, 0xFF42 => self.scrolly, 0xFF43 => self.scrollx, 0xFF44 => self.curline, 0xFF47 => self.background_palette, + 0xFF48 => self.object_palette_0, + 0xFF49 => self.object_palette_1, + 0xFF4A => self.windowy, + 0xFF4B => self.windowx, _ => panic!("Display: Read from {:04X} unsupported", addr), } } + // Do we want to have a time delta here? + pub fn tick(&mut self, ticks: u16) { + self.current_ticks += ticks; + match self.current_mode { + DisplayMode::Scanline => { + if self.current_ticks > TICKS_END_SCANLINE { + self.current_ticks = 0; + self.current_mode = DisplayMode::Readmode; + } + }, + DisplayMode::Readmode => { + if self.current_ticks > TICKS_END_READMODE { + self.current_ticks = 0; + self.current_mode = DisplayMode::HBlank; + } + }, + DisplayMode::HBlank => { + if self.current_ticks > TICKS_END_HBLANK { + self.current_ticks = 0; + self.curline += 1; + if self.curline == 143 { + self.current_mode = DisplayMode::VBlank; + } else { + self.current_mode = DisplayMode::Scanline; + } + } + }, + DisplayMode::VBlank => { + if self.current_ticks > TICKS_END_VBLANK { + self.current_ticks = 0; + self.curline += 1; + if self.curline > 153 { + self.current_mode = DisplayMode::Scanline; + self.curline = 0; + } + } + } + } + } + + fn renderscan(&mut self) { + // TODO: Allow switching of tile map + let tilemap: u16 = 0x1800; + let map_offset_y: u8 = self.curline.wrapping_add(self.scrolly); + let map_offset_x: u8 = self.scrollx; + + let tile_index_y: u8 = map_offset_y / 8; + + // Render line + for render_x in 0 .. 159 { + let tile_index_x: u8 = render_x / 8; + let tile_offset_x: u8 = render_x % 8; + + // TODO: Draw bit + // let tile_base_addr = tilemap + tile_id*128 + // pixel(render_x, map_offset_y) := *tile_base_addr + 2* *(tile_base_addr+1) + //[tile_index_x][tile_index_y] + } + + } + pub fn vblank_interrupt(&mut self) { self.curline += 1; if self.curline > 153 { diff --git a/src/interconnect.rs b/src/interconnect.rs index af4675e..33aaff6 100644 --- a/src/interconnect.rs +++ b/src/interconnect.rs @@ -1,5 +1,4 @@ const RAM_SIZE: usize = 0x2000; -const VRAM_SIZE: usize = 0x2000; const HIRAM_SIZE: usize = (0xFFFE - 0xFF80) + 1; const INTERRUPT_DISPLAY: u8 = 1 << 1; @@ -7,18 +6,23 @@ const INTERRUPT_DISPLAY_VBLANK: u8 = 1 << 0; use super::display; use super::sound; +use super::timer; +use super::serial; pub struct Interconnect { bios: Box<[u8]>, rom: Box<[u8]>, ram: Box<[u8]>, - vram: Box<[u8]>, hiram: Box<[u8]>, sound: sound::Sound, display: display::Display, interrupt: u8, - timer: u8, + iflags: u8, + itimer: u8, disable_bootrom: u8, + infrared_com_port: u8, + serial: serial::Serial, + timer: timer::Timer, } impl Interconnect { @@ -27,23 +31,30 @@ impl Interconnect { bios: bios, rom: rom, 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(), + // Refactor those + iflags: 0, interrupt: 0, - timer: 0, + infrared_com_port: 0, + itimer: 0, disable_bootrom: 0, + timer: timer::Timer::new(), + serial: serial::Serial::new(), } } // Somehow we need different timers for this. - pub fn tick(&mut self) { - self.timer += 1; - if self.timer == 5 { + pub fn tick(&mut self, cycles: u16) { + /* + self.itimer += 1; + if self.itimer == 5 { self.display_blank_interrupt(); - self.timer = 0; + self.itimer = 0; } + */ + self.display.tick(cycles * 4); } pub fn display_blank_interrupt(&mut self) { @@ -71,12 +82,15 @@ impl Interconnect { self.rom[addr as usize] } } - 0x8000 ... 0x9FFF => { - self.vram[(addr - 0x8000) as usize] - }, + 0x8000 ... 0x9FFF => self.display.read_byte(addr), 0xC000 ... 0xDFFF => { self.ram[(addr - 0xC000) as usize] }, + 0xFF01 ... 0xFF02 => self.serial.read_byte(addr), + 0xFF04 ... 0xFF07 => self.timer.read_byte(addr), + 0xFF0F => { + self.iflags + }, 0xFF10 ... 0xFF26 => { self.sound.read_byte(addr) }, @@ -86,6 +100,9 @@ impl Interconnect { 0xFF50 => { self.disable_bootrom }, + 0xFF56 => { + self.infrared_com_port + } 0xFF80 ... 0xFFFE => { self.hiram[(addr - 0xFF80) as usize] }, @@ -100,7 +117,6 @@ impl Interconnect { pub fn write_byte(&mut self, addr: u16, val: u8) { // TODO: Make this more beautful - // TODO: Write byte /* 0000 7FFF Cartridge 8000 9FFF Video RAM @@ -109,18 +125,21 @@ impl Interconnect { E000 FCFF Copy of the work RAM FE00 FE9F OAM (Sprite Attribute Table) FEA0 FEFF Unused - FF00 FF7F Hardware IO + FF00 FF7F Hardware IO FF80 FFFE High RAM FFFF FFFF Interrupt switch */ match addr { - 0x8000 ... 0x9FFF => { - self.vram[(addr - 0x8000) as usize] = val; - }, + 0x8000 ... 0x9FFF => self.display.write_byte(addr, val), 0xC000 ... 0xDFFF => { self.ram[(addr - 0xC000) as usize] = val; }, + 0xFF01 ... 0xFF02 => self.serial.write_byte(addr, val), + 0xFF04 ... 0xFF07 => self.timer.write_byte(addr, val), + 0xFF0F => { + self.iflags = val; + } 0xFF10 ... 0xFF26 => { self.sound.write_byte(addr, val); }, @@ -129,7 +148,10 @@ impl Interconnect { }, 0xFF50 => { self.disable_bootrom = val; - } + }, + 0xFF56 => { + self.infrared_com_port = val; + }, 0xFF80 ... 0xFFFE => { self.hiram[(addr - 0xFF80) as usize] = val; }, diff --git a/src/main.rs b/src/main.rs index 6205f70..e14b31b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,8 @@ mod cpu; mod display; mod interconnect; mod sound; - +mod timer; +mod serial; fn main() { let bios_path = env::args().nth(1).unwrap(); diff --git a/src/serial.rs b/src/serial.rs new file mode 100644 index 0000000..ab6758b --- /dev/null +++ b/src/serial.rs @@ -0,0 +1,27 @@ +#[derive(Default, Debug)] +pub struct Serial { + data: u8, + control: u8, +} + +impl Serial { + pub fn new() -> Serial { + Serial::default() + } + + pub fn write_byte(&mut self, addr: u16, val: u8) { + match addr { + 0xFF01 => self.data = val, + 0xFF02 => self.control = val, + _ => panic!("Serial: Write {:02X} to {:04X} unsupported", val, addr), + } + } + + pub fn read_byte(&self, addr: u16) -> u8 { + match addr { + 0xFF01 => self.data, + 0xFF02 => self.control, + _ => panic!("Serial: Read from {:04X} unsupported", addr), + } + } +} diff --git a/src/timer.rs b/src/timer.rs new file mode 100644 index 0000000..3645131 --- /dev/null +++ b/src/timer.rs @@ -0,0 +1,30 @@ +#[derive(Default, Debug)] +pub struct Timer { + tima: u8, + tma: u8, + tac: u8 +} + +impl Timer { + pub fn new() -> Timer { + Timer::default() + } + + pub fn write_byte(&mut self, addr: u16, val: u8) { + match addr { + 0xFF05 => self.tima = val, + 0xFF06 => self.tma = val, + 0xFF07 => self.tac = val, + _ => panic!("Timer: Write {:02X} to {:04X} unsupported", val, addr), + } + } + + pub fn read_byte(&self, addr: u16) -> u8 { + match addr { + 0xFF05 => self.tima, + 0xFF06 => self.tma, + 0xFF07 => self.tac, + _ => panic!("Timer: Read from {:04X} unsupported", addr), + } + } +}