Skip to content

Commit

Permalink
PPU: implement offset-per-tile
Browse files Browse the repository at this point in the history
  • Loading branch information
twvd committed Jan 14, 2024
1 parent 53c20ca commit ae706ab
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 41 deletions.
55 changes: 52 additions & 3 deletions src/snes/ppu/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,52 @@ impl PPUState {
self.cgram_to_color(palette + idx)
}

fn adjust_offsets_opt(
&self,
bg: usize,
x: usize,
bghofs: usize,
bgvofs: usize,
) -> (usize, usize) {
match self.get_screen_mode() {
2 | 6 => {
// Both vertical and horizontal adjustments
let (opt_hentry, opt_ventry) = self.get_opt_entries(bg, x);
(
if !opt_hentry.is_enabled(bg) {
// Not enabled for this layer
bghofs
} else {
opt_hentry.scrollh() | (bghofs & 0x07)
},
if !opt_ventry.is_enabled(bg) {
// Not enabled for this layer
bgvofs
} else {
opt_ventry.scrollv()
},
)
}
4 => {
// Either horizontal OR vertical adjustment in mode 4
let (opt_entry, _) = self.get_opt_entries(bg, x);
if !opt_entry.is_enabled(bg) {
// Not enabled for this layer
(bghofs, bgvofs)
} else {
if opt_entry.is_vertical() {
// Vertical
(bghofs, opt_entry.scrollv())
} else {
// Horizontal
(opt_entry.scrollh() | (bghofs & 0x07), bgvofs)
}
}
}
_ => (bghofs, bgvofs),
}
}

fn render_scanline_bglayer(
&mut self,
scanline: usize,
Expand All @@ -114,7 +160,10 @@ impl PPUState {

let mut x = 0;
'line: while x < SCREEN_WIDTH {
let entry = self.get_tilemap_entry_xy(bg, x, scanline);
// Get adjusted offsets for offset-per-tile (if applicable)
let (thofs, tvofs) = self.adjust_offsets_opt(bg, x, bghofs, bgvofs);

let entry = self.get_tilemap_entry_xy(bg, x, scanline, thofs, tvofs);
if entry.bgprio() != priority {
x += 1;
continue;
Expand All @@ -128,8 +177,8 @@ impl PPUState {

// Determine coordinates within the tile. This is
// a full tile (so either 8x8 or 16x16).
let px_x = (x + bghofs) % tilesize;
let px_y = (scanline + bgvofs) % tilesize;
let px_x = (x + thofs) % tilesize;
let px_y = (scanline + tvofs) % tilesize;
// get_bg_tile will select the sub-tile (for 16x16).
let tile = self.get_bg_tile(bg, &entry, px_x, px_y);

Expand Down
126 changes: 88 additions & 38 deletions src/snes/ppu/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ pub enum TilemapDimensions {
#[derive(Debug)]
pub struct TilemapEntry(u16);
impl TilemapEntry {
pub fn raw(&self) -> u16 {
self.0
}

pub fn charnr(&self) -> u16 {
self.0 & 0x3FF
}
Expand All @@ -51,6 +55,31 @@ impl TilemapEntry {
pub fn flip_y(&self) -> bool {
(self.0 & (1 << 15)) != 0
}

pub fn as_opt(self) -> OptEntry {
OptEntry(self.0)
}
}

pub struct OptEntry(u16);
impl OptEntry {
pub fn scrollv(&self) -> usize {
(self.0 & 0x3FF) as usize
}

pub fn scrollh(&self) -> usize {
(self.0 & 0x3F8) as usize
}

pub fn is_enabled(&self, bg: usize) -> bool {
debug_assert!(bg < 2);
(self.0 & (1 << (13 + bg))) != 0
}

pub fn is_vertical(&self) -> bool {
// Only mode 4!
(self.0 & (1 << 15)) != 0
}
}

#[derive(Clone, Copy, Debug, ToPrimitive)]
Expand Down Expand Up @@ -247,48 +276,69 @@ impl PPUState {
TilemapDimensions::from_u8(self.bgxsc[bg] & 0x03).unwrap()
}

pub(super) fn get_tilemap_entry_xy(&self, bg: usize, x: usize, y: usize) -> TilemapEntry {
let bghofs = self.bgxhofs[bg] as usize;
let bgvofs = self.bgxvofs[bg] as usize;
pub(super) fn get_tilemap_entry_xy(
&self,
bg: usize,
x: usize,
y: usize,
bghofs: usize,
bgvofs: usize,
) -> TilemapEntry {
debug_assert!(self.get_screen_mode() != 7);

let tilesize = self.get_bg_tile_size(bg);

match self.get_screen_mode() {
0 | 1 | 2 | 3 | 4 => {
// AA BB CC DD, size = 0x800 per sub-map
// 00 32x32 AA
// AA
// 01 64x32 AB
// AB
// 10 32x64 AA
// BB
// 11 64x64 AB
// CD
let (expand_x, expand_y) = match self.get_tilemap_dimensions(bg) {
TilemapDimensions::D32x32 => (false, false),
TilemapDimensions::D32x64 => (false, true),
TilemapDimensions::D64x32 => (true, false),
TilemapDimensions::D64x64 => (true, true),
};
let tm_x = (bghofs + x) / tilesize;
let tm_y = (bgvofs + y) / tilesize;

// 32 tiles per row in the sub-map, 0-31
let mut idx = ((tm_y & 0x1F) << 5) + (tm_x & 0x1F);
if expand_y {
if expand_x {
idx += (tm_y & 0x20) << 6;
} else {
idx += (tm_y & 0x20) << 5;
}
}
if expand_x {
idx += (tm_x & 0x20) << 5;
}

self.get_tilemap_entry(bg, idx)
// AA BB CC DD, size = 0x800 per sub-map
// 00 32x32 AA
// AA
// 01 64x32 AB
// AB
// 10 32x64 AA
// BB
// 11 64x64 AB
// CD
let (expand_x, expand_y) = match self.get_tilemap_dimensions(bg) {
TilemapDimensions::D32x32 => (false, false),
TilemapDimensions::D32x64 => (false, true),
TilemapDimensions::D64x32 => (true, false),
TilemapDimensions::D64x64 => (true, true),
};
let tm_x = (bghofs + x) / tilesize;
let tm_y = (bgvofs + y) / tilesize;

// 32 tiles per row in the sub-map, 0-31
let mut idx = ((tm_y & 0x1F) << 5) + (tm_x & 0x1F);
if expand_y {
if expand_x {
idx += (tm_y & 0x20) << 6;
} else {
idx += (tm_y & 0x20) << 5;
}
_ => todo!(),
}
if expand_x {
idx += (tm_x & 0x20) << 5;
}

self.get_tilemap_entry(bg, idx)
}

pub(super) fn get_opt_entries(&self, bg: usize, x: usize) -> (OptEntry, OptEntry) {
let tilesize = self.get_bg_tile_size(2);
let bghofs = self.bgxhofs[bg] as usize;
let (opthofs, optvofs) = (self.bgxhofs[2] as usize, self.bgxvofs[2] as usize);
let tm_x = (bghofs + x) / tilesize;

// First column has no OPT. Index 0 = second column of tiles
if tm_x == 0 {
return (OptEntry(0), OptEntry(0));
}

(
self.get_tilemap_entry_xy(2, x - tilesize, 0, opthofs, optvofs)
.as_opt(),
self.get_tilemap_entry_xy(2, x - tilesize, tilesize, opthofs, optvofs)
.as_opt(),
)
}

pub(super) fn get_layer_bpp(&self, bg: usize) -> BPP {
Expand Down

0 comments on commit ae706ab

Please sign in to comment.