pub fn fetch_bg_pixel(x: u32, scanline: u8, vram: &[u8; 0x2000], lcdc: u8, scx: u8, scy: u8) -> u8 {
let bg_x = scx.wrapping_add(x as u8);
let bg_y = scy.wrapping_add(scanline);
let map_base: usize = if lcdc & 0x08 != 0 { 0x1C00 } else { 0x1800 };
let tile_col = (bg_x / 8) as usize;
let tile_row = (bg_y / 8) as usize;
let tile_index_raw = vram[map_base + tile_row * 32 + tile_col];
let tile_data_start: usize = if lcdc & 0x10 != 0 {
(tile_index_raw as usize) * 16
} else {
(0x1000i32 + (tile_index_raw as i8 as i32) * 16) as usize
};
let row_in_tile = (bg_y % 8) as usize;
let bit = 7 - (bg_x % 8);
let addr = tile_data_start + row_in_tile * 2;
let low = vram[addr];
let high = vram[addr + 1];
((high >> bit) & 1) << 1 | ((low >> bit) & 1)
}
#[cfg(test)]
mod tests {
use super::*;
fn blank_vram() -> [u8; 0x2000] {
[0u8; 0x2000]
}
#[test]
fn test_blank_vram_returns_colour_index_0() {
let vram = blank_vram();
let idx = fetch_bg_pixel(0, 0, &vram, 0x91, 0, 0);
assert_eq!(idx, 0);
}
#[test]
fn test_tile_data_colour_bits_are_fetched_correctly() {
let mut vram = blank_vram();
vram[0x0010] = 0xFF; vram[0x0011] = 0x00; vram[0x1800] = 0x01;
let lcdc = 0x91u8; let idx = fetch_bg_pixel(0, 0, &vram, lcdc, 0, 0);
assert_eq!(idx, 1);
}
#[test]
fn test_scx_shifts_bg_map_lookup() {
let mut vram = blank_vram();
vram[0x0020] = 0xFF;
vram[0x0021] = 0xFF;
vram[0x1801] = 0x02;
let lcdc = 0x91u8;
let idx = fetch_bg_pixel(0, 0, &vram, lcdc, 8, 0);
assert_eq!(idx, 3);
}
#[test]
fn test_scy_shifts_bg_map_row_lookup() {
let mut vram = blank_vram();
vram[0x0010] = 0x00;
vram[0x0011] = 0xFF;
vram[0x1820] = 0x01;
let lcdc = 0x91u8;
let idx = fetch_bg_pixel(0, 0, &vram, lcdc, 0, 8);
assert_eq!(idx, 2);
}
}