From 74234dd40561804188b7e13d1e23c4950a9c0ddf Mon Sep 17 00:00:00 2001 From: Kevin Hamacher Date: Fri, 14 Feb 2020 22:16:26 +0100 Subject: [PATCH] GBC features --- src/cpu.rs | 9 +++ src/display.rs | 152 +++++++++++++++++++++++++++++++++++--------- src/interconnect.rs | 5 ++ src/sound/mod.rs | 3 +- 4 files changed, 137 insertions(+), 32 deletions(-) diff --git a/src/cpu.rs b/src/cpu.rs index b233ad9..0f75062 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -34,6 +34,8 @@ pub struct CPU { debug: bool, halted: bool, + + trigger_once: bool, } fn to_u16(args: Args) -> u16 { @@ -68,6 +70,8 @@ impl CPU { ime: false, debug: false, halted: false, + + trigger_once: false, } } @@ -949,6 +953,11 @@ impl CPU { pub fn run_instruction(&mut self) -> u8 { // self.debug = !self.interconnect.is_boot_rom(); + if !self.trigger_once && !self.interconnect.is_boot_rom() { + self.trigger_once = true; + self.regs[REG_A] = 0x11; + println!("Patching reg"); + } /* if self.ip >= 100 && self.ip < 120 { diff --git a/src/display.rs b/src/display.rs index 92a9876..516b7ca 100644 --- a/src/display.rs +++ b/src/display.rs @@ -42,7 +42,7 @@ const SPRITE_OBJ_BG_PRIORITY: u8 = 1 << 7; const SPRITE_Y_FLIP: u8 = 1 << 6; const SPRITE_X_FLIP: u8 = 1 << 5; const SPRITE_PALETTE_NO: u8 = 1 << 4; // NonCGB only - // const SPRITE_TILE_VRAM_BANK: u8 = 1 << 3; // CGB only +const SPRITE_TILE_VRAM_BANK: u8 = 1 << 3; // CGB only // Display color /* @@ -122,11 +122,25 @@ impl Sprite { } fn palette(&self) -> u8 { + // GB + /* if (self.flags & SPRITE_PALETTE_NO) == 0 { 0 } else { 1 } + */ + // GBC + self.flags & 0b111 + } + + // GBC only + fn vram_bank(&self) -> u8 { + if (self.flags & SPRITE_TILE_VRAM_BANK) == 0 { + 0 + } else { + 1 + } } } @@ -134,8 +148,8 @@ pub struct Display { control: u8, status: u8, background_palette: u8, - object_palette_0: u8, - object_palette_1: u8, + // Only 0 and 1 for GB + object_palette: [u8; 256], //4 * 2 * 8], scrollx: u8, scrolly: u8, windowx: u8, @@ -143,9 +157,11 @@ pub struct Display { curline: u8, lyc: u8, - vram: Box<[u8]>, + vram0: Box<[u8]>, + vram1: Box<[u8]>, oam: Box<[u8]>, + vram_bank: u8, current_ticks: u16, current_mode: DisplayMode, // TODO @@ -160,6 +176,13 @@ pub struct Display { frameskip: u8, frame_no: u8, + + // GBC: + background_palette_autoinc: bool, + background_palette_index: u8, + background_palette_c: [u8; 256], // 0x40], + + object_palette_index: u8, } impl Display { @@ -179,8 +202,7 @@ impl Display { control: 0, status: 0, background_palette: 0, - object_palette_0: 0, - object_palette_1: 0, + object_palette: [0u8; 256], // 64], scrollx: 0, scrolly: 0, windowx: 0, @@ -190,7 +212,9 @@ impl Display { current_ticks: 0, current_mode: DisplayMode::default(), - vram: vec![0; VRAM_SIZE].into_boxed_slice(), + vram0: vec![0; VRAM_SIZE].into_boxed_slice(), + vram1: vec![0; VRAM_SIZE].into_boxed_slice(), + vram_bank: 0, oam: vec![0; OAM_SIZE].into_boxed_slice(), renderer: renderer, @@ -201,6 +225,11 @@ impl Display { pixels: pixels, frameskip: 0, frame_no: 0, + + background_palette_autoinc: false, + background_palette_index: 0, + background_palette_c: [0u8; 256], // 0x40], + object_palette_index: 0, } } @@ -272,7 +301,13 @@ impl Display { #[inline] pub fn write_byte(&mut self, addr: u16, val: u8) { match addr { - 0x8000..=0x9FFF => self.vram[(addr - 0x8000) as usize] = val, + 0x8000..=0x9FFF => { + if self.vram_bank == 0 { + self.vram0[(addr - 0x8000) as usize] = val + } else { + self.vram1[(addr - 0x8000) as usize] = val + } + } 0xFE00..=0xFE9F => self.oam[(addr - 0xFE00) as usize] = val, 0xFF40 => self.control = val, 0xFF41 => self.status = val, @@ -280,9 +315,27 @@ impl Display { 0xFF43 => self.scrollx = val, 0xFF44 => self.curline = 0, 0xFF45 => self.lyc = val, + // GB classic 0xFF47 => self.background_palette = val, - 0xFF48 => self.object_palette_0 = val, - 0xFF49 => self.object_palette_1 = val, + 0xFF48 => self.object_palette[0] = val, + 0xFF49 => self.object_palette[1] = val, + // GBC + 0xFF68 => { + self.background_palette_index = val; + self.background_palette_autoinc = (val >> 7) != 0; + } + 0xFF69 => { + self.background_palette_c[self.background_palette_index as usize] = val; + if self.background_palette_autoinc { + self.background_palette_index += 1; + } + } + 0xFF6A => { + self.object_palette_index = val; + } + 0xFF6B => { + self.object_palette[self.object_palette_index as usize] = val; + } 0xFF4A => { if self.windowy != val { println!("WY set to {:02X}", val); @@ -295,6 +348,7 @@ impl Display { } self.windowx = val; } + 0xFF4f => self.vram_bank = val, _ => panic!("Display: Write {:02X} to {:04X} unsupported", val, addr), } } @@ -302,7 +356,13 @@ impl Display { #[inline] pub fn read_byte(&self, addr: u16) -> u8 { match addr { - 0x8000..=0x9FFF => self.vram[(addr - 0x8000) as usize], + 0x8000..=0x9FFF => { + if self.vram_bank == 0 { + self.vram0[(addr - 0x8000) as usize] + } else { + self.vram1[(addr - 0x8000) as usize] + } + } 0xFE00..=0xFE9F => self.oam[(addr - 0xFE00) as usize], 0xFF40 => self.control, 0xFF41 => self.status, @@ -311,10 +371,11 @@ impl Display { 0xFF44 => self.curline, 0xFF45 => self.lyc, 0xFF47 => self.background_palette, - 0xFF48 => self.object_palette_0, - 0xFF49 => self.object_palette_1, + 0xFF48 => self.object_palette[0], + 0xFF49 => self.object_palette[1], 0xFF4A => self.windowy, 0xFF4B => self.windowx, + 0xFF4F => self.vram_bank | 0b1111_1110, _ => panic!("Display: Read from {:04X} unsupported", addr), } } @@ -452,10 +513,15 @@ impl Display { let tile_base_addr: usize = sprite.tile as usize * 16; // Should this be twice as wide in wide mode? let tile_addr = tile_base_addr + tile_offset_y * 2; - let tile_line_1 = self.vram[tile_addr + 0]; - let tile_line_2 = self.vram[tile_addr + 1]; - let tile_line_3 = self.vram[tile_addr + 2]; - let tile_line_4 = self.vram[tile_addr + 3]; + let vram = if sprite.vram_bank() == 0 { + &*self.vram0 + } else { + &*self.vram1 + }; + let tile_line_1 = vram[tile_addr + 0]; + let tile_line_2 = vram[tile_addr + 1]; + let tile_line_3 = vram[tile_addr + 2]; + let tile_line_4 = vram[tile_addr + 3]; // We need to draw this. let wide_mode = self.control & CTRL_BG_SPRITE_SIZE > 0; @@ -499,19 +565,21 @@ impl Display { continue; } + // GB + /* let c = (b1 as u8) * 2 + b2 as u8; let lookup: [u8; 4] = match sprite.palette() { 0 => [ - self.object_palette_0 & 3, - (self.object_palette_0 >> 2) & 3, - (self.object_palette_0 >> 4) & 3, - (self.object_palette_0 >> 6) & 3, + self.object_palette[0] & 3, + (self.object_palette[0] >> 2) & 3, + (self.object_palette[0] >> 4) & 3, + (self.object_palette[0] >> 6) & 3, ], 1 => [ - self.object_palette_1 & 3, - (self.object_palette_1 >> 2) & 3, - (self.object_palette_1 >> 4) & 3, - (self.object_palette_1 >> 6) & 3, + self.object_palette[1] & 3, + (self.object_palette[1] >> 2) & 3, + (self.object_palette[1] >> 4) & 3, + (self.object_palette[1] >> 6) & 3, ], _ => unreachable!(), }; @@ -524,6 +592,20 @@ impl Display { sdl2::pixels::Color::RGB(entry[0], entry[1], entry[2]), PixelOrigin::Sprite, ); + */ + let c = ((b1 as u8) * 2 + b2 as u8) as usize; + let a = self.object_palette[sprite.palette() as usize * 8 + c * 2]; + let b = self.object_palette[sprite.palette() as usize * 8 + c * 2 + 1]; + let ab = ((b as u16) << 8) | a as u16; + let r = (ab & 0b11111) as u8; + let g = ((ab >> 5) & 0b11111) as u8; + let b = ((ab >> 10) & 0b11111) as u8; + self.set_pixel( + x.wrapping_add(x_o), + render_y, + sdl2::pixels::Color::RGB(r, g, b), + PixelOrigin::Sprite, + ); } } } @@ -608,13 +690,21 @@ impl Display { background_map + ((tile_index_y as usize) * 32) + tile_index_x as usize; // Obtain tile ID in this area - let tile_id = self.vram[vram_offset]; + let tile_id = self.vram0[vram_offset]; // Obtain tile information let tile_base_addr: usize = self.get_bg_window_tile_addr(tile_id); let addr = tile_base_addr + (tile_offset_y as usize) * 2; - let tile_line_1 = self.vram[addr]; - let tile_line_2 = self.vram[addr + 1]; + + // GBC stuff + let vram = if (self.vram1[vram_offset] >> 3) == 1 { + &*self.vram0 + } else { + &*self.vram1 + }; + + let tile_line_1 = vram[addr]; + let tile_line_2 = vram[addr + 1]; // Get the correct bit let b1: bool = (tile_line_1 & 1 << (7 - tile_offset_x)) != 0; @@ -662,13 +752,13 @@ impl Display { window_map + (tile_index_y as usize) * 32 + tile_index_x as usize; // Obtain tile ID in this area - let tile_id = self.vram[vram_offset]; + let tile_id = self.vram0[vram_offset]; // Obtain tile information let tile_base_addr: usize = self.get_bg_window_tile_addr(tile_id); let tile_addr = tile_base_addr + (tile_offset_y as usize) * 2; - let tile_line_1 = self.vram[tile_addr]; - let tile_line_2 = self.vram[tile_addr + 1]; + let tile_line_1 = self.vram0[tile_addr]; + let tile_line_2 = self.vram0[tile_addr + 1]; // Get the correct bit let b1: bool = (tile_line_1 & 1 << (7 - tile_offset_x)) > 0; diff --git a/src/interconnect.rs b/src/interconnect.rs index 3a714a5..6e21e7c 100644 --- a/src/interconnect.rs +++ b/src/interconnect.rs @@ -292,6 +292,7 @@ impl Interconnect { 0xFF10..=0xFF26 => self.sound.sound_object.lock().unwrap().read_byte(addr), 0xFF30..=0xFF3F => self.sound.sound_object.lock().unwrap().read_byte(addr), 0xFF40..=0xFF4B => self.display.read_byte(addr), + 0xFF4F => self.display.read_byte(addr), 0xFF50 => self.disable_bootrom, 0xFF51 => self.vram_dma_source_high, 0xFF52 => self.vram_dma_source_low, @@ -299,6 +300,7 @@ impl Interconnect { 0xFF54 => self.vram_dma_destination_low, 0xFF55 => self.vram_dma_length, 0xFF56 => self.infrared_com_port, + 0xFF6A | 0xFF6B => self.display.read_byte(addr), 0xFF70 => self.wram_bank, 0xFF80..=0xFFFE => self.hiram[(addr - 0xFF80) as usize], 0xFFFF => self.interrupt, @@ -369,6 +371,7 @@ impl Interconnect { self.write_byte(0xFE00 | x, dma_b); } } + 0xFF4F => self.display.write_byte(addr, val), 0xFF50 => { println!("Disabling boot rom."); self.disable_bootrom = val; @@ -413,6 +416,8 @@ impl Interconnect { } } } + 0xFF68 | 0xFF69 => self.display.write_byte(addr, val), + 0xFF6A | 0xFF6B => self.display.write_byte(addr, val), 0xFF80..=0xFFFE => { self.hiram[(addr - 0xFF80) as usize] = val; } diff --git a/src/sound/mod.rs b/src/sound/mod.rs index ae42434..f17b32b 100644 --- a/src/sound/mod.rs +++ b/src/sound/mod.rs @@ -103,7 +103,8 @@ impl Channel1 { if self.tick_state == 7 { self.envelope.clock(); } - + // TODO: Sweep + // TODO: Sweep in adition mode + overflow? stop. self.tick_state += 1; if self.tick_state == 8 {