From b71397d16a6b50e3fa2c029970e8a2cdd75d774a Mon Sep 17 00:00:00 2001 From: Kevin Hamacher Date: Sun, 29 May 2016 13:01:36 +0200 Subject: [PATCH] Improve timer; Add 16 px sprite support --- src/cartridge.rs | 11 ++++++++++- src/cpu.rs | 36 ++++++++++++++++++++++++++++++++++-- src/display.rs | 43 +++++++++++++++++++++++++++++++++---------- src/interconnect.rs | 6 +++--- src/sound.rs | 4 +++- src/timer.rs | 41 ++++++++++++++++++++++++++++++++++------- 6 files changed, 117 insertions(+), 24 deletions(-) diff --git a/src/cartridge.rs b/src/cartridge.rs index 35c3bd7..8456d9e 100644 --- a/src/cartridge.rs +++ b/src/cartridge.rs @@ -106,6 +106,15 @@ impl Cartridge { } } + fn write_byte_none(&mut self, addr: u16, val: u8) { + match addr { + 0xA000 ... 0xBFFF => { + self.ram[addr as usize - 0xA000] = val; + } + _ => println!("No MBC, unable to write {:02X} to {:04X}", val, addr), + } + } + fn write_byte_mbc3(&mut self, addr: u16, val: u8) { match addr { 0x0000 ... 0x1FFF => { @@ -143,7 +152,7 @@ impl Cartridge { pub fn write_byte(&mut self, addr: u16, val: u8) { match self.mbc_type { - MemoryBankControllerType::None => println!("Cartridge: No MBC found, can not write to ROM ({:02X} to {:04X})", val, addr), + MemoryBankControllerType::None => self.write_byte_none(addr, val), MemoryBankControllerType::MBC3 => self.write_byte_mbc3(addr, val), _ => panic!("MBC not supported.") } diff --git a/src/cpu.rs b/src/cpu.rs index 4236af8..11c1e15 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -742,7 +742,7 @@ impl CPU { let e_pending = pending & enabled; if e_pending & interconnect::INTERRUPT_DISPLAY_VBLANK > 0 { - println!("Handling vblank interrupt"); + // println!("Handling vblank interrupt"); self.handle_interrupt(0x40, interconnect::INTERRUPT_DISPLAY_VBLANK); } else if e_pending & interconnect::INTERRUPT_DISPLAY_STAT > 0 { println!("Handling display stat interrupt"); @@ -906,7 +906,39 @@ impl CPU { 0x24 => self.reg_inc(REG_N_H), 0x25 => self.reg_dec(REG_N_H), 0x26 => self.ld_r_v(REG_N_H), - 0x27 => panic!("DAA not implemented!"), + 0x27 => { + // Logic copied from some other emulator + let mut v = self.regs[REG_A] as u16; + + if self.flags & FLAG_N > 0{ + // Lower nibble + if self.flags & FLAG_H > 0 || v & 0xF > 9 { + v += 0x06; + } + + // Higher nibble + if self.flags & FLAG_C > 0 || v > 0x9F { + v += 0x60; + } + } else { + // Lower nibble + if self.flags & FLAG_H > 0 { + v = v.wrapping_sub(6) & 0xFF; + } + + // Higher nibble + if self.flags & FLAG_C > 0 { + v = v.wrapping_sub(0x60); + } + } + + self.clear_flag(FLAG_H); + self.set_clear_flag(FLAG_C, v == 0x100); + self.set_clear_flag(FLAG_Z, v & 0xFF == 0); + self.regs[REG_A] = v as u8; + + 4 + }, 0x28 => { let c = self.flags & FLAG_Z > 0; self.jmp_r_condition("Z".to_owned(), c) diff --git a/src/display.rs b/src/display.rs index 2588ad6..178359a 100644 --- a/src/display.rs +++ b/src/display.rs @@ -327,33 +327,56 @@ impl Display { } - if self.control & CTRL_BG_SPRITE_ENABLE > 0 && false { - // panic!("Sprites not supported"); + if self.control & CTRL_BG_SPRITE_ENABLE > 0 { // Let's draw sprites. // TODO: Sprites with smaller X coordinate should // should be in front // TODO: Draw only up to 10 sprites per line + if self.control & CTRL_BG_SPRITE_SIZE > 0 { + println!("Wide sprites not tested!"); + } for i in 0 .. 39 { - let y: u8 = self.oam[i * 4 + 0]; - let x: u8 = self.oam[i * 4 + 1]; + let mut y: u8 = self.oam[i * 4 + 0]; + let mut x: u8 = self.oam[i * 4 + 1]; let t_num: u8 = self.oam[i * 4 + 2]; let flags: u8 = self.oam[i * 4 + 3]; + if x == 0 || y == 0 { + // This sprite is hidden + continue; + } + + x = x.wrapping_sub(8); + y = y.wrapping_sub(16); // Is this sprite on the current line? - if (y + 8) >= render_y && y <= render_y { + if y.wrapping_add(8) >= render_y && y <= render_y { let tile_offset_y: usize = render_y as usize - y as usize; - let tile_base_addr: usize = 0x1000 + t_num as usize * 16; + let tile_base_addr: usize = 0 + t_num as usize * 16; let tile_line_1 = self.vram[tile_base_addr + tile_offset_y * 2]; let tile_line_2 = self.vram[tile_base_addr + tile_offset_y * 2 + 1]; + let tile_line_3 = self.vram[tile_base_addr + tile_offset_y * 2 + 2]; + let tile_line_4 = self.vram[tile_base_addr + tile_offset_y * 2 + 3]; // We need to draw this. - for x_o in 0 .. 8 { - let b1: bool = (tile_line_1 & 1 << (7 - x_o)) > 0; - let b2: bool = (tile_line_2 & 1 << (7 - x_o)) > 0; - + let wide_mode = self.control & CTRL_BG_SPRITE_SIZE > 0; + let limit = match wide_mode { + true => 16, + false => 8 + }; + for x_o in 0 .. limit { + let mut b1: bool; + let mut b2: bool; let mut factor = 0; + if wide_mode && x_o > 7 { + b1 = (tile_line_3 & 1 << (14 - x_o)) > 0; + b2 = (tile_line_4 & 1 << (14 - x_o)) > 0; + } else { + b1 = (tile_line_1 & 1 << (7 - x_o)) > 0; + b2 = (tile_line_2 & 1 << (7 - x_o)) > 0; + } + if b1 { factor += 64; } diff --git a/src/interconnect.rs b/src/interconnect.rs index 791d8af..3e1600f 100644 --- a/src/interconnect.rs +++ b/src/interconnect.rs @@ -210,6 +210,7 @@ impl Interconnect { 0xFF10 ... 0xFF26 => { self.sound.read_byte(addr) }, + 0xFF30 ... 0xFF3F => self.sound.read_byte(addr), 0xFF40 ... 0xFF4B => { self.display.read_byte(addr) }, @@ -278,18 +279,17 @@ impl Interconnect { 0xFF10 ... 0xFF26 => { self.sound.write_byte(addr, val); }, + 0xFF30 ... 0xFF3F => self.sound.write_byte(addr, val), // Exclude DMA transfer, we will do this below 0xFF40 ... 0xFF45 | 0xFF47 ... 0xFF4B => { self.display.write_byte(addr, val); }, 0xFF46 => { - println!("OAM DMA transfer: "); + // println!("OAM DMA transfer"); for x in 0x00 .. 0x9F { let dma_b = self.read_byte(((val as u16) << 8) | x); self.write_byte(0xFE00 | x, dma_b); - print!("{:02X} ", val); } - println!(""); } 0xFF50 => { println!("Disabling boot rom."); diff --git a/src/sound.rs b/src/sound.rs index 54f0e6a..cc0c918 100644 --- a/src/sound.rs +++ b/src/sound.rs @@ -23,7 +23,9 @@ impl Sound { 0xFF24 => self.sound_channel_volume_control = val, 0xFF25 => self.sound_output_terminal_selector = val, 0xFF26 => self.enabled = val, - _ => println!("Sound: Write {:02X} to {:04X} unsupported", val, addr), + _ => { + // println!("Sound: Write {:02X} to {:04X} unsupported", val, addr); + }, } } diff --git a/src/timer.rs b/src/timer.rs index 9854774..3f1e2d7 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -1,10 +1,12 @@ #[derive(Default, Debug)] pub struct Timer { - tima: u8, + tima: u8, // Timer counter, interrupt on overflow tma: u8, - tac: u8, + tac: u8, // 00 = 4096 Hz [10240 ticks], 01 = 262144 Hz [160 ticks], 10 = 65536 Hz [640], 11 = 16384 Hz[2560]. Bit 2 0 = stop, 1 = start + div: u8, // Incremented at speed of 16384Hz timer_interrupt: bool, - ctr: u16, + tick_counter: u16, + div_tick_counter: u16, } impl Timer { @@ -13,15 +15,39 @@ impl Timer { } pub fn tick(&mut self, ticks: u16) { - self.ctr += ticks; - if self.ctr > 0x1000 { - self.ctr = 0; - self.timer_interrupt = true; + // One tick 1/4.194304MHz on regular speed? + if self.tac & 1 << 2 == 1 { // Is timer enabled? + self.tick_counter += ticks; + let req_ticks = match self.tac & 3 { + 0 => 10240, + 1 => 160, + 2 => 640, + 3 => 2560, + _ => unreachable!() + }; + + if self.tick_counter > req_ticks { + if self.tima == 0xFF { + self.timer_interrupt = true; + self.tima = 0; + } else { + self.tima += 1; + } + self.tick_counter -= req_ticks; + } + } + + // The div reg will always tick + self.div_tick_counter += ticks; + if self.div_tick_counter > 2560 { + self.div = self.div.wrapping_add(1); + self.div_tick_counter -= 2560; } } pub fn write_byte(&mut self, addr: u16, val: u8) { match addr { + 0xFF04 => self.div = 0, 0xFF05 => self.tima = val, 0xFF06 => self.tma = val, 0xFF07 => self.tac = val, @@ -31,6 +57,7 @@ impl Timer { pub fn read_byte(&self, addr: u16) -> u8 { match addr { + 0xFF04 => self.div, 0xFF05 => self.tima, 0xFF06 => self.tma, 0xFF07 => self.tac,