many gpu things work now

This commit is contained in:
Alex Janka 2023-04-22 19:00:00 +10:00
parent 9ada49a6d8
commit f1ce5cfc9f
5 changed files with 157 additions and 20 deletions

View file

@ -364,6 +364,7 @@ where
IoAddress::Cgb(address) => {
if let WramBanks::Cgb { banks: _, selected } = &self.ram.banks && let Some(cgb_peripherals) = &self.cgb_peripherals {
match address {
CgbIoAddress::CompatMode => self.gpu.get_compat_byte(),
CgbIoAddress::PrepareSpeed => cgb_peripherals.double_speed.get(),
CgbIoAddress::VramBank => self.gpu.vram.get_vram_bank(),
CgbIoAddress::VramDma(address) => cgb_peripherals.vram_dma.get_register(address),
@ -423,6 +424,7 @@ where
IoAddress::Cgb(address) => {
if let WramBanks::Cgb { banks: _, selected } = &mut self.ram.banks && let Some(cgb_peripherals) = &mut self.cgb_peripherals {
match address {
CgbIoAddress::CompatMode => self.gpu.set_compat_byte(data),
CgbIoAddress::PrepareSpeed => cgb_peripherals.double_speed.set(data),
CgbIoAddress::VramBank => self.gpu.vram.set_vram_bank(data),
CgbIoAddress::VramDma(address) => cgb_peripherals.vram_dma.set_register(address, data),

View file

@ -47,6 +47,7 @@ pub(crate) enum IoAddress {
#[derive(Clone, Copy)]
pub(crate) enum CgbIoAddress {
CompatMode,
PrepareSpeed,
VramBank,
VramDma(VramDmaAddress),
@ -122,7 +123,7 @@ impl TryInto<IoAddress> for u16 {
0xFF10..0xFF27 => Ok(IoAddress::Audio(self.try_into().unwrap())),
0xFF30..0xFF40 => Ok(IoAddress::WaveRam(self.try_into().unwrap())),
0xFF40..0xFF4C => Ok(IoAddress::Video(self.try_into().unwrap())),
0xFF4D..0xFF78 => Ok(IoAddress::Cgb(self.try_into().unwrap())),
0xFF4C..0xFF78 => Ok(IoAddress::Cgb(self.try_into().unwrap())),
0x0..0xFF00 | 0xFFFF => Err(AddressError::OutOfBounds),
_ => Ok(IoAddress::Unused(self)),
}
@ -134,6 +135,7 @@ impl TryInto<CgbIoAddress> for u16 {
fn try_into(self) -> Result<CgbIoAddress, Self::Error> {
match self {
0xFF4C => Ok(CgbIoAddress::CompatMode),
0xFF4D => Ok(CgbIoAddress::PrepareSpeed),
0xFF4F => Ok(CgbIoAddress::VramBank),
0xFF51..0xFF56 => Ok(CgbIoAddress::VramDma(self.try_into().unwrap())),
@ -143,7 +145,7 @@ impl TryInto<CgbIoAddress> for u16 {
0xFF70 => Ok(CgbIoAddress::WramBank),
0xFF76 => Ok(CgbIoAddress::Pcm12),
0xFF77 => Ok(CgbIoAddress::Pcm34),
0x0..0xFF4D | 0xFF78..=0xFFFF => Err(AddressError::OutOfBounds),
0x0..0xFF4C | 0xFF78..=0xFFFF => Err(AddressError::OutOfBounds),
_ => Ok(CgbIoAddress::Unused(self)),
}
}
@ -177,6 +179,7 @@ impl AddressMarker for IoAddress {
impl AddressMarker for CgbIoAddress {
fn inner(&self) -> u16 {
match self {
CgbIoAddress::CompatMode => 0xFF4C,
CgbIoAddress::PrepareSpeed => 0xFF4D,
CgbIoAddress::VramBank => 0xFF4F,
CgbIoAddress::VramDma(v) => v.inner(),

View file

@ -5,8 +5,8 @@ use self::{
cgb::CgbData,
tile_window::TileWindow,
types::{
ColourInner, GpuInterrupts, Lcdc, Oam, ObjPalette, ObjSize, Object, ObjectFlags, Palette,
Stat, TiledataArea, TilemapArea, Vram,
BgAttributes, ColourInner, GpuInterrupts, Lcdc, Oam, ObjPalette, ObjSize, Object,
ObjectFlags, Palette, Stat, TiledataArea, TilemapArea, Vram, VramBank,
},
};
use crate::{
@ -396,11 +396,17 @@ where
behind_bg_and_window: get_bit(flags, 7),
y_flip: get_bit(flags, 6),
x_flip: get_bit(flags, 5),
palette: if get_bit(flags, 4) {
dmg_palette: if get_bit(flags, 4) {
ObjPalette::One
} else {
ObjPalette::Zero
},
cgb_vram_bank: if get_bit(flags, 3) {
VramBank::Bank1
} else {
VramBank::Bank0
},
cgb_palette: flags & 0b111,
},
oam_location: (i - 0xFE00) as u8,
});
@ -429,13 +435,22 @@ where
.get_addr(object.tile_index + if object_row >= 8 { 1 } else { 0 })
+ (tile_row as u16 * 2))
.unwrap();
let lsbs = self.vram.get(tile_addr);
let msbs = self.vram.get((tile_addr + 1).unwrap());
let bank = if self.is_cgb_mode() {
object.flags.cgb_vram_bank
} else {
VramBank::Bank0
};
let lsbs = self.vram.get_with_bank(tile_addr, bank).unwrap();
let msbs = self
.vram
.get_with_bank((tile_addr + 1).unwrap(), bank)
.unwrap();
for px_x in 0..8 {
let x_addr = if object.flags.x_flip { px_x } else { 7 - px_x };
let lsb = get_bit(lsbs, x_addr);
let msb = get_bit(msbs, x_addr);
let (colour, is_zero) = match object.flags.palette {
// maybe have to change this for cgb mode
let (colour, is_zero) = match object.flags.dmg_palette {
ObjPalette::Zero => self.obj_palette_0,
ObjPalette::One => self.obj_palette_1,
}
@ -451,7 +466,16 @@ where
if x_coord < WIDTH {
let buffer_index = (scanline as usize * WIDTH) + x_coord;
if !object.flags.behind_bg_and_window || self.is_bg_zero[x_coord] {
let cgb_data = self.cgb_data.as_ref().map(|v| (v.palettes.obj, 0));
let cgb_data = self.cgb_data.as_ref().map(|v| {
(
v.palettes.obj,
if self.is_cgb_mode() {
object.flags.cgb_palette
} else {
0
},
)
});
self.buffer[buffer_index] = colour.rgb_bytes(cgb_data).into();
}
@ -473,8 +497,6 @@ where
return;
}
let tilemap_row = tile_line_y / 8;
let tile_px_y = (tile_line_y) % 8;
let tiledata_offset = tile_px_y * 2;
let row_addr = ((tilemap_row as usize * 32) % 0x400) as u16;
for x in 0..WIDTH {
let (tile_line_x, did_wrap_x) = (x as u8).overflowing_sub(offset_x);
@ -484,22 +506,49 @@ where
let tilemap_column = (tile_line_x / 8) as u16;
let tile_px_x = tile_line_x % 8;
let tile_addr = (self
.lcdc
.tile_area
.get_addr(self.vram.get(tilemap.get_addr(row_addr + (tilemap_column))))
let tilemap_addr = tilemap.get_addr(row_addr + (tilemap_column));
let attributes = if self.is_cgb_mode() {
self.vram
.get_with_bank(tilemap_addr, VramBank::Bank1)
.map_or(BgAttributes::default(), BgAttributes::from_byte)
} else {
BgAttributes::default()
};
let tile_px_y = if attributes.flip_v {
7 - ((tile_line_y) % 8)
} else {
(tile_line_y) % 8
};
let tiledata_offset = tile_px_y * 2;
let tile_addr = (self.lcdc.tile_area.get_addr(self.vram.get(tilemap_addr))
+ tiledata_offset as u16)
.unwrap();
let lsbs = self.vram.get(tile_addr);
let msbs = self.vram.get((tile_addr + 1).unwrap());
let lsbs = self
.vram
.get_with_bank(tile_addr, attributes.tile_bank)
.unwrap();
let msbs = self
.vram
.get_with_bank((tile_addr + 1).unwrap(), attributes.tile_bank)
.unwrap();
let tile_px_x = if attributes.flip_h {
7 - (tile_line_x % 8)
} else {
tile_line_x % 8
};
let lsb = get_bit(lsbs, 7 - tile_px_x);
let msb = get_bit(msbs, 7 - tile_px_x);
let (colour, is_zero) = self.bg_palette.map_bits(lsb, msb);
self.is_bg_zero[x] = is_zero;
let cgb_data = self.cgb_data.as_ref().map(|v| (v.palettes.bg, 0));
let cgb_data = self
.cgb_data
.as_ref()
.map(|v| (v.palettes.bg, attributes.palette));
self.buffer[(scanline as usize * WIDTH) + x] = colour.rgb_bytes(cgb_data).into();
}

View file

@ -12,6 +12,7 @@ use super::{Colour, Gpu};
pub(super) struct CgbData {
pub(super) palettes: CgbPaletteRegisters,
pub(super) object_priority_mode: ObjectPriorityMode,
compat_byte: u8,
}
impl Default for CgbData {
@ -19,6 +20,7 @@ impl Default for CgbData {
Self {
palettes: Default::default(),
object_priority_mode: ObjectPriorityMode::Coordinate,
compat_byte: 0,
}
}
}
@ -124,4 +126,26 @@ where
}
}
}
pub(crate) fn get_compat_byte(&self) -> u8 {
if let Some(cgb_data) = &self.cgb_data {
cgb_data.compat_byte
} else {
0xFF
}
}
pub(crate) fn set_compat_byte(&mut self, data: u8) {
if let Some(cgb_data) = &mut self.cgb_data {
cgb_data.compat_byte = data;
}
}
pub(super) fn is_cgb_mode(&self) -> bool {
if let Some(cgb_data) = &self.cgb_data {
cgb_data.compat_byte != 0x04
} else {
false
}
}
}

View file

@ -202,7 +202,9 @@ pub(super) struct ObjectFlags {
pub(super) behind_bg_and_window: bool,
pub(super) y_flip: bool,
pub(super) x_flip: bool,
pub(super) palette: ObjPalette,
pub(super) dmg_palette: ObjPalette,
pub(super) cgb_vram_bank: VramBank,
pub(super) cgb_palette: u8,
}
pub(super) enum ObjPalette {
@ -280,6 +282,27 @@ impl Vram {
}
}
pub(super) fn get_with_bank(&self, address: VramAddress, bank: VramBank) -> Option<u8> {
match bank {
VramBank::Bank0 => Some(self.bank0_get(address)),
VramBank::Bank1 => self.bank1_get(address),
}
}
fn bank0_get(&self, address: VramAddress) -> u8 {
match self {
Vram::Dmg { inner } => inner[address.get_local() as usize],
Vram::Cgb { inner, index: _ } => inner[0][address.get_local() as usize],
}
}
fn bank1_get(&self, address: VramAddress) -> Option<u8> {
match self {
Vram::Dmg { inner: _ } => None,
Vram::Cgb { inner, index: _ } => Some(inner[1][address.get_local() as usize]),
}
}
pub(crate) fn set(&mut self, address: VramAddress, data: u8) {
match self {
Vram::Dmg { inner } => inner[address.get_local() as usize] = data,
@ -336,3 +359,39 @@ pub struct GpuInterrupts {
pub lcd_stat: bool,
pub vblank: bool,
}
pub(super) struct BgAttributes {
pub(super) bg_priority: bool,
pub(super) flip_v: bool,
pub(super) flip_h: bool,
pub(super) tile_bank: VramBank,
pub(super) palette: u8,
}
impl Default for BgAttributes {
fn default() -> Self {
Self {
bg_priority: Default::default(),
flip_v: Default::default(),
flip_h: Default::default(),
tile_bank: VramBank::Bank0,
palette: Default::default(),
}
}
}
impl BgAttributes {
pub(super) fn from_byte(byte: u8) -> Self {
Self {
bg_priority: get_bit(byte, 7),
flip_v: get_bit(byte, 6),
flip_h: get_bit(byte, 5),
tile_bank: if get_bit(byte, 3) {
VramBank::Bank1
} else {
VramBank::Bank0
},
palette: byte & 0b111,
}
}
}