mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-11-23 10:12:11 +00:00
WIP PPU CGB support
This commit is contained in:
parent
c109346dcf
commit
3f2824714f
@ -62,7 +62,7 @@ impl Bus {
|
|||||||
true => Box::new(CGBRAM::new()),
|
true => Box::new(CGBRAM::new()),
|
||||||
false => Box::new(DMGRAM::new()),
|
false => Box::new(DMGRAM::new()),
|
||||||
},
|
},
|
||||||
ppu: PPU::new(),
|
ppu: PPU::new(cgb_mode),
|
||||||
joypad: Joypad::new(),
|
joypad: Joypad::new(),
|
||||||
timer: Timer::new(),
|
timer: Timer::new(),
|
||||||
sound: Sound::new(),
|
sound: Sound::new(),
|
||||||
@ -105,7 +105,7 @@ impl Bus {
|
|||||||
} else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
|
} else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
|
||||||
return self.interrupts.read(address);
|
return self.interrupts.read(address);
|
||||||
} else if VIDEO_RAM.contains(&address) {
|
} else if VIDEO_RAM.contains(&address) {
|
||||||
return self.ppu.read_vram(address);
|
return self.ppu.read_vram_external(address);
|
||||||
} else if SPRITE_ATTRIBUTE_TABLE.contains(&address) {
|
} else if SPRITE_ATTRIBUTE_TABLE.contains(&address) {
|
||||||
return self.ppu.read_oam(address);
|
return self.ppu.read_oam(address);
|
||||||
} else if PPU::is_io_register(address) {
|
} else if PPU::is_io_register(address) {
|
||||||
@ -147,7 +147,7 @@ impl Bus {
|
|||||||
let byte = self.data[address as usize];
|
let byte = self.data[address as usize];
|
||||||
self.data[address as usize] = (data & 0b11110000) | (byte & 0b00001111);
|
self.data[address as usize] = (data & 0b11110000) | (byte & 0b00001111);
|
||||||
} else if VIDEO_RAM.contains(&address) {
|
} else if VIDEO_RAM.contains(&address) {
|
||||||
return self.ppu.write_vram(address, data);
|
return self.ppu.write_vram_external(address, data);
|
||||||
} else if SPRITE_ATTRIBUTE_TABLE.contains(&address) {
|
} else if SPRITE_ATTRIBUTE_TABLE.contains(&address) {
|
||||||
return self.ppu.write_oam(address, data);
|
return self.ppu.write_oam(address, data);
|
||||||
} else if address == DMA_ADDRESS {
|
} else if address == DMA_ADDRESS {
|
||||||
|
115
src/ppu.rs
115
src/ppu.rs
@ -26,6 +26,7 @@ pub const OBJECT_PALETTE_0_ADDRESS: u16 = 0xFF48;
|
|||||||
pub const OBJECT_PALETTE_1_ADDRESS: u16 = 0xFF49;
|
pub const OBJECT_PALETTE_1_ADDRESS: u16 = 0xFF49;
|
||||||
pub const WINDOW_Y_ADDRESS: u16 = 0xFF4A;
|
pub const WINDOW_Y_ADDRESS: u16 = 0xFF4A;
|
||||||
pub const WINDOW_X_ADDRESS: u16 = 0xFF4B;
|
pub const WINDOW_X_ADDRESS: u16 = 0xFF4B;
|
||||||
|
pub const VRAM_BANK_SELECT_ADDRESS: u16 = 0xFF4F;
|
||||||
|
|
||||||
pub const TILE_MAP_ADDRESS: u16 = 0x9800;
|
pub const TILE_MAP_ADDRESS: u16 = 0x9800;
|
||||||
|
|
||||||
@ -126,6 +127,26 @@ pub enum LCDStatus {
|
|||||||
ModeFlag(LCDStatusModeFlag),
|
ModeFlag(LCDStatusModeFlag),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct BgAttributes {
|
||||||
|
bg_to_oam_priority: bool,
|
||||||
|
vertical_flip: bool,
|
||||||
|
horizontal_flip: bool,
|
||||||
|
vram_bank: u8,
|
||||||
|
palette_number: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BgAttributes {
|
||||||
|
pub fn new(byte: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
bg_to_oam_priority: get_bit(byte, BitIndex::I7),
|
||||||
|
vertical_flip: get_bit(byte, BitIndex::I6),
|
||||||
|
horizontal_flip: get_bit(byte, BitIndex::I5),
|
||||||
|
vram_bank: get_bit(byte, BitIndex::I3) as u8,
|
||||||
|
palette_number: byte & 0b111,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Sprite {
|
struct Sprite {
|
||||||
x: u8,
|
x: u8,
|
||||||
y: u8,
|
y: u8,
|
||||||
@ -136,6 +157,8 @@ struct Sprite {
|
|||||||
y_flip: bool,
|
y_flip: bool,
|
||||||
over_bg: bool,
|
over_bg: bool,
|
||||||
bit_pixels: Option<[u8; 8]>,
|
bit_pixels: Option<[u8; 8]>,
|
||||||
|
vram_bank: u8,
|
||||||
|
palette_number: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sprite {
|
impl Sprite {
|
||||||
@ -229,13 +252,15 @@ pub struct PPU {
|
|||||||
scroll_y: u8,
|
scroll_y: u8,
|
||||||
window_x: u8,
|
window_x: u8,
|
||||||
window_y: u8,
|
window_y: u8,
|
||||||
io_registers: [u8; 12],
|
io_registers: [u8; 16],
|
||||||
vram: [u8; 0x2000],
|
vram: [u8; 0x2000 * 2],
|
||||||
oam: [u8; 0xA0],
|
oam: [u8; 0xA0],
|
||||||
|
vram_bank: u8,
|
||||||
|
cgb_mode: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PPU {
|
impl PPU {
|
||||||
pub fn new() -> Self {
|
pub fn new(cgb_mode: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: false,
|
state: false,
|
||||||
background_priority: false,
|
background_priority: false,
|
||||||
@ -256,21 +281,41 @@ impl PPU {
|
|||||||
scroll_y: 0,
|
scroll_y: 0,
|
||||||
window_x: 0,
|
window_x: 0,
|
||||||
window_y: 0,
|
window_y: 0,
|
||||||
io_registers: [0; 12],
|
io_registers: [0; 16],
|
||||||
vram: [0; 0x2000],
|
vram: [0; 0x2000 * 2],
|
||||||
oam: [0; 0xA0],
|
oam: [0; 0xA0],
|
||||||
|
vram_bank: 0,
|
||||||
|
cgb_mode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_vram_bank(&mut self, bank: u8) {
|
||||||
|
if self.cgb_mode {
|
||||||
|
self.vram_bank = bank & 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_vram_bank(&self) -> u8 {
|
||||||
|
self.vram_bank | 0xFE
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_io_register(address: u16) -> bool {
|
pub fn is_io_register(address: u16) -> bool {
|
||||||
address >= 0xFF40 && address <= 0xFF4B
|
address >= 0xFF40 && address <= 0xFF4F
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_vram(&self, address: u16) -> u8 {
|
pub fn read_vram_external(&self, address: u16) -> u8 {
|
||||||
|
self.vram[((address + (0x2000 * self.vram_bank as u16)) - 0x8000) as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_vram_external(&mut self, address: u16, data: u8) {
|
||||||
|
self.vram[((address + (0x2000 * self.vram_bank as u16)) - 0x8000) as usize] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_vram(&self, address: u16) -> u8 {
|
||||||
self.vram[(address - 0x8000) as usize]
|
self.vram[(address - 0x8000) as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_vram(&mut self, address: u16, data: u8) {
|
fn write_vram(&mut self, address: u16, data: u8) {
|
||||||
self.vram[(address - 0x8000) as usize] = data;
|
self.vram[(address - 0x8000) as usize] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,7 +328,9 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_register(&self, address: u16) -> u8 {
|
pub fn get_register(&self, address: u16) -> u8 {
|
||||||
if address == LCD_CONTROL_ADDRESS {
|
if address == VRAM_BANK_SELECT_ADDRESS {
|
||||||
|
return self.get_vram_bank();
|
||||||
|
} else if address == LCD_CONTROL_ADDRESS {
|
||||||
return self.lcd_control;
|
return self.lcd_control;
|
||||||
} else if address == LCD_Y_ADDRESS {
|
} else if address == LCD_Y_ADDRESS {
|
||||||
return self.lcd_y;
|
return self.lcd_y;
|
||||||
@ -292,7 +339,9 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_register(&mut self, address: u16, data: u8) {
|
pub fn set_register(&mut self, address: u16, data: u8) {
|
||||||
if address == LCD_Y_ADDRESS {
|
if address == VRAM_BANK_SELECT_ADDRESS {
|
||||||
|
return self.set_vram_bank(data);
|
||||||
|
} else if address == LCD_Y_ADDRESS {
|
||||||
return;
|
return;
|
||||||
} else if address == LCD_CONTROL_ADDRESS {
|
} else if address == LCD_CONTROL_ADDRESS {
|
||||||
self.lcd_control = data;
|
self.lcd_control = data;
|
||||||
@ -465,12 +514,19 @@ impl PPU {
|
|||||||
y_flip: get_bit(attributes, BitIndex::I6),
|
y_flip: get_bit(attributes, BitIndex::I6),
|
||||||
over_bg: get_bit(attributes, BitIndex::I7),
|
over_bg: get_bit(attributes, BitIndex::I7),
|
||||||
bit_pixels: None,
|
bit_pixels: None,
|
||||||
|
vram_bank: match self.cgb_mode && get_bit(attributes, BitIndex::I3) {
|
||||||
|
true => 1,
|
||||||
|
false => 0,
|
||||||
|
},
|
||||||
|
palette_number: attributes & 0b111,
|
||||||
});
|
});
|
||||||
|
|
||||||
addr += 4;
|
addr += 4;
|
||||||
}
|
}
|
||||||
|
if !self.cgb_mode {
|
||||||
self.sprite_buffer.sort_by(|a, b| a.x().cmp(&b.x()));
|
self.sprite_buffer.sort_by(|a, b| a.x().cmp(&b.x()));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn find_sprite_pixel(&mut self) -> Option<(Pixel, bool)> {
|
fn find_sprite_pixel(&mut self) -> Option<(Pixel, bool)> {
|
||||||
let lcd_y = self.lcd_y;
|
let lcd_y = self.lcd_y;
|
||||||
@ -522,12 +578,17 @@ impl PPU {
|
|||||||
self.force_set_register(LCD_STATUS_ADDRESS, byte);
|
self.force_set_register(LCD_STATUS_ADDRESS, byte);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_tile_bytes(&self, x: u8, y: u8, tilemap_area: u16, default_method: bool) -> (u8, u8) {
|
fn get_tile_bytes(&self, x: u8, y: u8, tilemap_area: u16, default_method: bool) -> (u8, u8, BgAttributes) {
|
||||||
let index_x = x as u16 / 8;
|
let index_x = x as u16 / 8;
|
||||||
let index_y = (y as u16 / 8) * 32;
|
let index_y = (y as u16 / 8) * 32;
|
||||||
let index = index_x + index_y;
|
let index = index_x + index_y;
|
||||||
let tile_line = (y).rem_euclid(8) * 2;
|
|
||||||
let tile_number = self.read_vram(tilemap_area + index as u16) as u16;
|
let tile_number = self.read_vram(tilemap_area + index as u16) as u16;
|
||||||
|
let mut tile_line = (y).rem_euclid(8);
|
||||||
|
let attributes = BgAttributes::new(self.read_vram(tilemap_area + 0x2000 + index as u16));
|
||||||
|
if self.cgb_mode && attributes.vertical_flip {
|
||||||
|
tile_line = 7 - tile_line;
|
||||||
|
}
|
||||||
|
tile_line = tile_line * 2;
|
||||||
let addr = if default_method {
|
let addr = if default_method {
|
||||||
0x8000 + tile_line as u16 + (tile_number * 16)
|
0x8000 + tile_line as u16 + (tile_number * 16)
|
||||||
} else {
|
} else {
|
||||||
@ -537,7 +598,31 @@ impl PPU {
|
|||||||
(base + tile_line + (tile_number * 16)) as u16
|
(base + tile_line + (tile_number * 16)) as u16
|
||||||
};
|
};
|
||||||
|
|
||||||
(self.read_vram(addr), self.read_vram(addr + 1))
|
if !self.cgb_mode {
|
||||||
|
return (self.read_vram(addr), self.read_vram(addr + 1), attributes);
|
||||||
|
}
|
||||||
|
let byte1 = self.read_vram(addr + (0x2000 * (attributes.vram_bank as u16)));
|
||||||
|
let byte2 = self.read_vram(addr + (0x2000 * (attributes.vram_bank as u16)) + 1);
|
||||||
|
if attributes.horizontal_flip {
|
||||||
|
let byte1 = ((byte1 >> 7) & 0b1) |
|
||||||
|
((byte1 >> 5) & 0b10) |
|
||||||
|
((byte1 >> 3) & 0b100) |
|
||||||
|
((byte1 >> 1) & 0b1000) |
|
||||||
|
((byte1 << 1) & 0b10000) |
|
||||||
|
((byte1 << 3) & 0b100000) |
|
||||||
|
((byte1 << 5) & 0b1000000) |
|
||||||
|
((byte1 << 7) & 0b10000000);
|
||||||
|
let byte2 = ((byte2 >> 7) & 0b1) |
|
||||||
|
((byte2 >> 5) & 0b10) |
|
||||||
|
((byte2 >> 3) & 0b100) |
|
||||||
|
((byte2 >> 1) & 0b1000) |
|
||||||
|
((byte2 << 1) & 0b10000) |
|
||||||
|
((byte2 << 3) & 0b100000) |
|
||||||
|
((byte2 << 5) & 0b1000000) |
|
||||||
|
((byte2 << 7) & 0b10000000);
|
||||||
|
return (byte1, byte2, attributes);
|
||||||
|
}
|
||||||
|
return (byte1, byte2, attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_window_pixel(&mut self) -> Option<Pixel> {
|
fn get_window_pixel(&mut self) -> Option<Pixel> {
|
||||||
@ -577,7 +662,7 @@ impl PPU {
|
|||||||
true => 0x9C00,
|
true => 0x9C00,
|
||||||
false => 0x9800,
|
false => 0x9800,
|
||||||
};
|
};
|
||||||
let (tile_byte_1, tile_byte_2) = self.get_tile_bytes(x, y, tilemap_area, default_mode);
|
let (tile_byte_1, tile_byte_2, _) = self.get_tile_bytes(x, y, tilemap_area, default_mode);
|
||||||
let bit_pixels_array = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
let bit_pixels_array = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
||||||
self.current_window_pixels = Some(bit_pixels_array);
|
self.current_window_pixels = Some(bit_pixels_array);
|
||||||
|
|
||||||
@ -614,7 +699,7 @@ impl PPU {
|
|||||||
true => 0x9C00,
|
true => 0x9C00,
|
||||||
false => 0x9800,
|
false => 0x9800,
|
||||||
};
|
};
|
||||||
let (tile_byte_1, tile_byte_2) = self.get_tile_bytes(x, y, tilemap_area, default_mode);
|
let (tile_byte_1, tile_byte_2, _) = self.get_tile_bytes(x, y, tilemap_area, default_mode);
|
||||||
let bit_pixels_array = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
let bit_pixels_array = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
||||||
self.current_background_pixels = Some(bit_pixels_array);
|
self.current_background_pixels = Some(bit_pixels_array);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user