mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-11-23 10:12:11 +00:00
First cgb-acid2 signs of life
This commit is contained in:
parent
3f2824714f
commit
abac2b3bf3
152
src/ppu.rs
152
src/ppu.rs
@ -2,6 +2,7 @@ use crate::utils::{
|
|||||||
BitIndex,
|
BitIndex,
|
||||||
get_bit,
|
get_bit,
|
||||||
set_bit,
|
set_bit,
|
||||||
|
join_bytes,
|
||||||
};
|
};
|
||||||
use crate::bus::SPRITE_ATTRIBUTE_TABLE;
|
use crate::bus::SPRITE_ATTRIBUTE_TABLE;
|
||||||
use crate::cpu::Cycles;
|
use crate::cpu::Cycles;
|
||||||
@ -28,6 +29,11 @@ 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 VRAM_BANK_SELECT_ADDRESS: u16 = 0xFF4F;
|
||||||
|
|
||||||
|
pub const BCPS_BGPI_ADDRESS: u16 = 0xFF68;
|
||||||
|
pub const BCPD_BGPD_ADDRESS: u16 = 0xFF69;
|
||||||
|
pub const OCPS_OBPI_ADDRESS: u16 = 0xFF6A;
|
||||||
|
pub const OCPD_OBPD_ADDRESS: u16 = 0xFF6B;
|
||||||
|
|
||||||
pub const TILE_MAP_ADDRESS: u16 = 0x9800;
|
pub const TILE_MAP_ADDRESS: u16 = 0x9800;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
@ -48,6 +54,29 @@ struct ColorPalette {
|
|||||||
black: RGBA,
|
black: RGBA,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ColorPalette {
|
||||||
|
pub fn new_cgb(cram: &[u8], palette_number: u8) -> Self {
|
||||||
|
let addr = (palette_number as usize) * 8;
|
||||||
|
let white = join_bytes(cram[addr + 1], cram[addr]);
|
||||||
|
let light = join_bytes(cram[addr + 3], cram[addr + 2]);
|
||||||
|
let dark = join_bytes(cram[addr + 5], cram[addr + 4]);
|
||||||
|
let black = join_bytes(cram[addr + 7], cram[addr + 6]);
|
||||||
|
Self {
|
||||||
|
white: extract_rgb(white),
|
||||||
|
light: extract_rgb(light),
|
||||||
|
dark: extract_rgb(dark),
|
||||||
|
black: extract_rgb(black),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_rgb(color: u16) -> RGBA {
|
||||||
|
let red = (color & 0b1111).to_be_bytes()[1];
|
||||||
|
let green = ((color >> 4) & 0b1111).to_be_bytes()[1];
|
||||||
|
let blue = ((color >> 8) & 0b1111).to_be_bytes()[1];
|
||||||
|
RGBA((red << 3) | (red >> 2), (green << 3) | (green >> 2), (blue << 3) | (blue >> 2), 0)
|
||||||
|
}
|
||||||
|
|
||||||
const BACKGROUND_COLORS: ColorPalette = ColorPalette {
|
const BACKGROUND_COLORS: ColorPalette = ColorPalette {
|
||||||
white: RGBA(0x83, 0xE6, 0xCD, 0),
|
white: RGBA(0x83, 0xE6, 0xCD, 0),
|
||||||
light: RGBA(0x66, 0xAD, 0xC6, 0),
|
light: RGBA(0x66, 0xAD, 0xC6, 0),
|
||||||
@ -166,7 +195,7 @@ impl Sprite {
|
|||||||
self.x
|
self.x
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pixel(&mut self, lcd_x: u8, lcd_y: u8, vram: &[u8], last_bg_index: u8, lcd_control: u8) -> Option<(Pixel, bool)> {
|
pub fn get_pixel(&mut self, lcd_x: u8, lcd_y: u8, vram: &[u8], last_bg_index: u8, lcd_control: u8, is_cgb: bool) -> Option<(Pixel, bool, u8)> {
|
||||||
if !LCDControl::ObjectEnable.get(lcd_control) {
|
if !LCDControl::ObjectEnable.get(lcd_control) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -228,7 +257,10 @@ impl Sprite {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((PPU::get_pixel(PPU::get_palette(bit_pixel, self.palette)), self.palette_zero))
|
if !is_cgb {
|
||||||
|
return Some((PPU::get_pixel(PPU::get_palette(bit_pixel, self.palette)), self.palette_zero, 0));
|
||||||
|
}
|
||||||
|
Some((PPU::get_pixel(bit_pixel), self.palette_zero, self.palette_number))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,8 +276,8 @@ pub struct PPU {
|
|||||||
last_bg_index: u8,
|
last_bg_index: u8,
|
||||||
bg_palette: u8,
|
bg_palette: u8,
|
||||||
lcd_control: u8,
|
lcd_control: u8,
|
||||||
current_background_pixels: Option<[u8; 8]>,
|
current_background_pixels: Option<([u8; 8], u8)>,
|
||||||
current_window_pixels: Option<[u8; 8]>,
|
current_window_pixels: Option<([u8; 8], u8)>,
|
||||||
lcd_y: u8,
|
lcd_y: u8,
|
||||||
lcd_x: u8,
|
lcd_x: u8,
|
||||||
scroll_x: u8,
|
scroll_x: u8,
|
||||||
@ -253,7 +285,10 @@ pub struct PPU {
|
|||||||
window_x: u8,
|
window_x: u8,
|
||||||
window_y: u8,
|
window_y: u8,
|
||||||
io_registers: [u8; 16],
|
io_registers: [u8; 16],
|
||||||
|
cram_registers: [u8; 4],
|
||||||
vram: [u8; 0x2000 * 2],
|
vram: [u8; 0x2000 * 2],
|
||||||
|
bg_cram: [u8; 64],
|
||||||
|
obj_cram: [u8; 64],
|
||||||
oam: [u8; 0xA0],
|
oam: [u8; 0xA0],
|
||||||
vram_bank: u8,
|
vram_bank: u8,
|
||||||
cgb_mode: bool,
|
cgb_mode: bool,
|
||||||
@ -282,7 +317,10 @@ impl PPU {
|
|||||||
window_x: 0,
|
window_x: 0,
|
||||||
window_y: 0,
|
window_y: 0,
|
||||||
io_registers: [0; 16],
|
io_registers: [0; 16],
|
||||||
|
cram_registers: [0; 4],
|
||||||
vram: [0; 0x2000 * 2],
|
vram: [0; 0x2000 * 2],
|
||||||
|
bg_cram: [0; 64],
|
||||||
|
obj_cram: [0; 64],
|
||||||
oam: [0; 0xA0],
|
oam: [0; 0xA0],
|
||||||
vram_bank: 0,
|
vram_bank: 0,
|
||||||
cgb_mode,
|
cgb_mode,
|
||||||
@ -300,7 +338,8 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_io_register(address: u16) -> bool {
|
pub fn is_io_register(address: u16) -> bool {
|
||||||
address >= 0xFF40 && address <= 0xFF4F
|
(address >= 0xFF68 && address <= 0xFF6B) ||
|
||||||
|
(address >= 0xFF40 && address <= 0xFF4F)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_vram_external(&self, address: u16) -> u8 {
|
pub fn read_vram_external(&self, address: u16) -> u8 {
|
||||||
@ -328,7 +367,9 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_register(&self, address: u16) -> u8 {
|
pub fn get_register(&self, address: u16) -> u8 {
|
||||||
if address == VRAM_BANK_SELECT_ADDRESS {
|
if address >= 0xFF68 && address <= 0xFF6B {
|
||||||
|
return self.cram_registers[(address as usize) - 0xFF68];
|
||||||
|
} else if address == VRAM_BANK_SELECT_ADDRESS {
|
||||||
return self.get_vram_bank();
|
return self.get_vram_bank();
|
||||||
} else if address == LCD_CONTROL_ADDRESS {
|
} else if address == LCD_CONTROL_ADDRESS {
|
||||||
return self.lcd_control;
|
return self.lcd_control;
|
||||||
@ -339,7 +380,33 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_register(&mut self, address: u16, data: u8) {
|
pub fn set_register(&mut self, address: u16, data: u8) {
|
||||||
if address == VRAM_BANK_SELECT_ADDRESS {
|
if address >= 0xFF68 && address <= 0xFF6B {
|
||||||
|
self.cram_registers[(address as usize) - 0xFF68] = data;
|
||||||
|
|
||||||
|
if address == BCPD_BGPD_ADDRESS {
|
||||||
|
if self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let byte = self.cram_registers[(BCPS_BGPI_ADDRESS as usize) - 0xFF68];
|
||||||
|
let auto_increment = get_bit(byte, BitIndex::I7);
|
||||||
|
let cram_address = byte & 0b11111;
|
||||||
|
self.bg_cram[cram_address as usize] = data;
|
||||||
|
if auto_increment {
|
||||||
|
self.cram_registers[(BCPS_BGPI_ADDRESS as usize) - 0xFF68] = ((byte + 1) & 0b11111) | ((auto_increment as u8) << 7);
|
||||||
|
}
|
||||||
|
} else if address == OCPD_OBPD_ADDRESS {
|
||||||
|
if self.get_lcd_status(LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let byte = self.cram_registers[(OCPS_OBPI_ADDRESS as usize) - 0xFF68];
|
||||||
|
let auto_increment = get_bit(byte, BitIndex::I7);
|
||||||
|
let cram_address = byte & 0b11111;
|
||||||
|
self.obj_cram[cram_address as usize] = data;
|
||||||
|
if auto_increment {
|
||||||
|
self.cram_registers[(OCPS_OBPI_ADDRESS as usize) - 0xFF68] = ((byte + 1) & 0b11111) | ((auto_increment as u8) << 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if address == VRAM_BANK_SELECT_ADDRESS {
|
||||||
return self.set_vram_bank(data);
|
return self.set_vram_bank(data);
|
||||||
} else if address == LCD_Y_ADDRESS {
|
} else if address == LCD_Y_ADDRESS {
|
||||||
return;
|
return;
|
||||||
@ -528,10 +595,10 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_sprite_pixel(&mut self) -> Option<(Pixel, bool)> {
|
fn find_sprite_pixel(&mut self) -> Option<(Pixel, bool, u8)> {
|
||||||
let lcd_y = self.lcd_y;
|
let lcd_y = self.lcd_y;
|
||||||
for sprite in &mut self.sprite_buffer {
|
for sprite in &mut self.sprite_buffer {
|
||||||
if let Some(pixel) = sprite.get_pixel(self.lcd_x, lcd_y, &self.vram, self.last_bg_index, self.lcd_control) {
|
if let Some(pixel) = sprite.get_pixel(self.lcd_x, lcd_y, &self.vram, self.last_bg_index, self.lcd_control, self.cgb_mode) {
|
||||||
return Some(pixel);
|
return Some(pixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -625,7 +692,7 @@ impl PPU {
|
|||||||
return (byte1, byte2, attributes);
|
return (byte1, byte2, attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_window_pixel(&mut self) -> Option<Pixel> {
|
fn get_window_pixel(&mut self) -> Option<(Pixel, u8)> {
|
||||||
if !self.window_enable {
|
if !self.window_enable {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -652,9 +719,9 @@ impl PPU {
|
|||||||
self.current_window_pixels = None;
|
self.current_window_pixels = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let bit_pixel = match self.current_window_pixels {
|
let (bit_pixels_array, palette_number) = match self.current_window_pixels {
|
||||||
Some(bit_pixels_array) => {
|
Some(bit_pixels_array) => {
|
||||||
bit_pixels_array[bit_pixel_index]
|
bit_pixels_array
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
let default_mode = self.get_lcd_control(LCDControl::TileAddressMode);
|
let default_mode = self.get_lcd_control(LCDControl::TileAddressMode);
|
||||||
@ -662,20 +729,23 @@ 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, info) = 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, info.palette_number));
|
||||||
|
|
||||||
bit_pixels_array[bit_pixel_index]
|
(bit_pixels_array, info.palette_number)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
let bit_pixel = bit_pixels_array[bit_pixel_index];
|
||||||
self.last_bg_index = bit_pixel & 0b11;
|
self.last_bg_index = bit_pixel & 0b11;
|
||||||
|
|
||||||
Some(PPU::get_pixel(PPU::get_palette(bit_pixel, self.bg_palette)))
|
if !self.cgb_mode {
|
||||||
|
return Some((PPU::get_pixel(PPU::get_palette(bit_pixel, self.bg_palette)), 0));
|
||||||
|
}
|
||||||
|
Some((PPU::get_pixel(bit_pixel), palette_number))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_background_pixel(&mut self) -> Option<Pixel> {
|
fn get_background_pixel(&mut self) -> Option<(Pixel, u8)> {
|
||||||
if !self.background_priority {
|
if !self.background_priority {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -689,9 +759,9 @@ impl PPU {
|
|||||||
self.current_background_pixels = None;
|
self.current_background_pixels = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let bit_pixel = match self.current_background_pixels {
|
let (bit_pixels_array, palette_number) = match self.current_background_pixels {
|
||||||
Some(bit_pixels_array) => {
|
Some(bit_pixels_array) => {
|
||||||
bit_pixels_array[bit_pixel_index]
|
bit_pixels_array
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
let default_mode = self.get_lcd_control(LCDControl::TileAddressMode);
|
let default_mode = self.get_lcd_control(LCDControl::TileAddressMode);
|
||||||
@ -699,16 +769,20 @@ 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, info) = 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, info.palette_number));
|
||||||
|
|
||||||
bit_pixels_array[bit_pixel_index]
|
(bit_pixels_array, info.palette_number)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
let bit_pixel = bit_pixels_array[bit_pixel_index];
|
||||||
self.last_bg_index = bit_pixel & 0b11;
|
self.last_bg_index = bit_pixel & 0b11;
|
||||||
|
|
||||||
Some(PPU::get_pixel(PPU::get_palette(bit_pixel, self.bg_palette)))
|
if !self.cgb_mode {
|
||||||
|
return Some((PPU::get_pixel(PPU::get_palette(bit_pixel, self.bg_palette)), 0));
|
||||||
|
}
|
||||||
|
Some((PPU::get_pixel(bit_pixel), palette_number))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_line(&mut self, cycles: Cycles, frame_buffer: &mut [u8]) {
|
fn draw_line(&mut self, cycles: Cycles, frame_buffer: &mut [u8]) {
|
||||||
@ -728,24 +802,36 @@ impl PPU {
|
|||||||
while count < cycles.0 && (self.lcd_x as u32) < LCD_WIDTH {
|
while count < cycles.0 && (self.lcd_x as u32) < LCD_WIDTH {
|
||||||
let idx = (self.lcd_x as usize + (self.lcd_y as usize * LCD_WIDTH as usize)) * 4;
|
let idx = (self.lcd_x as usize + (self.lcd_y as usize * LCD_WIDTH as usize)) * 4;
|
||||||
|
|
||||||
if let Some(window_pixel) = self.get_window_pixel() {
|
if let Some((window_pixel, palette_number)) = self.get_window_pixel() {
|
||||||
self.window_drawn = true;
|
self.window_drawn = true;
|
||||||
let rgba = PPU::get_rgba(window_pixel, WINDOW_COLORS);
|
let colors = match self.cgb_mode {
|
||||||
|
true => ColorPalette::new_cgb(&self.bg_cram, palette_number),
|
||||||
|
false => WINDOW_COLORS,
|
||||||
|
};
|
||||||
|
let rgba = PPU::get_rgba(window_pixel, colors);
|
||||||
frame_buffer[idx] = rgba[0];
|
frame_buffer[idx] = rgba[0];
|
||||||
frame_buffer[idx + 1] = rgba[1];
|
frame_buffer[idx + 1] = rgba[1];
|
||||||
frame_buffer[idx + 2] = rgba[2];
|
frame_buffer[idx + 2] = rgba[2];
|
||||||
} else if let Some(background_pixel) = self.get_background_pixel() {
|
} else if let Some((background_pixel, palette_number)) = self.get_background_pixel() {
|
||||||
let rgba = PPU::get_rgba(background_pixel, BACKGROUND_COLORS);
|
let colors = match self.cgb_mode {
|
||||||
|
true => ColorPalette::new_cgb(&self.bg_cram, palette_number),
|
||||||
|
false => BACKGROUND_COLORS,
|
||||||
|
};
|
||||||
|
let rgba = PPU::get_rgba(background_pixel, colors);
|
||||||
frame_buffer[idx] = rgba[0];
|
frame_buffer[idx] = rgba[0];
|
||||||
frame_buffer[idx + 1] = rgba[1];
|
frame_buffer[idx + 1] = rgba[1];
|
||||||
frame_buffer[idx + 2] = rgba[2];
|
frame_buffer[idx + 2] = rgba[2];
|
||||||
}
|
}
|
||||||
if self.get_lcd_control(LCDControl::ObjectEnable) {
|
if self.get_lcd_control(LCDControl::ObjectEnable) {
|
||||||
if let Some((sprite_pixel, palette_zero)) = self.find_sprite_pixel() {
|
if let Some((sprite_pixel, palette_zero, palette_number)) = self.find_sprite_pixel() {
|
||||||
let rgba = PPU::get_rgba(sprite_pixel, match palette_zero {
|
let colors = match self.cgb_mode {
|
||||||
true => SPRITE_0_COLORS,
|
true => ColorPalette::new_cgb(&self.obj_cram, palette_number),
|
||||||
false => SPRITE_1_COLORS,
|
false => match palette_zero {
|
||||||
});
|
true => SPRITE_0_COLORS,
|
||||||
|
false => SPRITE_1_COLORS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let rgba = PPU::get_rgba(sprite_pixel, colors);
|
||||||
frame_buffer[idx] = rgba[0];
|
frame_buffer[idx] = rgba[0];
|
||||||
frame_buffer[idx + 1] = rgba[1];
|
frame_buffer[idx + 1] = rgba[1];
|
||||||
frame_buffer[idx + 2] = rgba[2];
|
frame_buffer[idx + 2] = rgba[2];
|
||||||
|
Loading…
Reference in New Issue
Block a user