212 lines
7.3 KiB
Rust
212 lines
7.3 KiB
Rust
use super::*;
|
|
|
|
fn bitmap(mode: u16, page: u16) -> Memory {
|
|
let mut m = memory();
|
|
m.write16(DISPCNT, 0x400 | mode | (page * 16)).unwrap();
|
|
m.write16(BG2PA, 256).unwrap();
|
|
m.write16(BG2PD, 256).unwrap();
|
|
let (w, h) = if mode == 5 { (160, 128) } else { (240, 160) };
|
|
for y in 0..h {
|
|
for x in 0..w {
|
|
let offset = (y * w + x) as u32;
|
|
let index = (x * 3 + y * 7) as u16 & 255;
|
|
if mode == 4 {
|
|
if x % 2 == 0 {
|
|
let next = ((x + 1) * 3 + y * 7) as u16 & 255;
|
|
m.write16(VRAM + page as u32 * 0xa000 + offset, index | (next << 8))
|
|
.unwrap();
|
|
}
|
|
} else {
|
|
let color =
|
|
(x as u16 & 31) | ((y as u16 & 31) << 5) | (((x + y) as u16 & 31) << 10);
|
|
m.write16(VRAM + page as u32 * 0xa000 + offset * 2, color)
|
|
.unwrap();
|
|
}
|
|
}
|
|
}
|
|
m
|
|
}
|
|
|
|
#[test]
|
|
fn write_only_register_merges_bytes_and_ignores_upper_word_half() {
|
|
let mut m = bitmap(3, 0);
|
|
m.write16(BG2CNT, 0x40).unwrap();
|
|
m.write32(MOSAIC, 0xffff_0034).unwrap(); // BG 5x4, OBJ1x1.
|
|
assert_eq!(m.read32(MOSAIC).unwrap(), 0);
|
|
m.write8(MOSAIC + 1, 0xff).unwrap(); // Must retain BG dimensions.
|
|
let f = render(&m);
|
|
assert_eq!(at(&f, 4, 3), at(&f, 0, 0));
|
|
assert_ne!(at(&f, 5, 4), at(&f, 0, 0));
|
|
m.write8(MOSAIC, 0).unwrap();
|
|
assert_ne!(at(&render(&m), 1, 0), at(&f, 0, 0));
|
|
assert_eq!(m.read16(MOSAIC + 2).unwrap(), 0);
|
|
assert_eq!(
|
|
m.read8(MOSAIC + 0x10000),
|
|
Err(MemoryError::Unmapped(MOSAIC + 0x10000))
|
|
);
|
|
assert_eq!(
|
|
m.write16(MOSAIC + 1, 0),
|
|
Err(MemoryError::Unaligned(MOSAIC + 1))
|
|
);
|
|
assert_eq!(m.cycles(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn every_bg_size_and_bitmap_page_repeats_screen_grid_samples() {
|
|
for mode in [3, 4, 5] {
|
|
for page in 0..=u16::from(mode != 3) {
|
|
let mut m = bitmap(mode, page);
|
|
let base = render(&m);
|
|
m.write16(BG2CNT, 0x40).unwrap();
|
|
// Exercise every field value, including asymmetric blocks and partial LCD blocks.
|
|
for w in 1..=16 {
|
|
let h = 17 - w;
|
|
m.write16(MOSAIC, ((h - 1) * 16 + w - 1) as u16).unwrap();
|
|
let f = render(&m);
|
|
for y in 0..HEIGHT {
|
|
for x in 0..WIDTH {
|
|
assert_eq!(
|
|
at(&f, x, y),
|
|
at(&base, x / w * w, y / h * h),
|
|
"mode={mode} page={page} size={w}x{h} at={x},{y}"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn mosaic_flag_is_independent_of_dimensions_and_one_by_one_is_identity() {
|
|
let mut m = bitmap(3, 0);
|
|
let base = render(&m);
|
|
m.write16(MOSAIC, 0xffff).unwrap();
|
|
assert_eq!(render(&m).pixels(), base.pixels()); // Flag is off.
|
|
m.write16(BG2CNT, 0x40).unwrap();
|
|
m.write16(MOSAIC, 0xff00).unwrap(); // OBJ fields do not affect BG.
|
|
assert_eq!(render(&m).pixels(), base.pixels());
|
|
m.write16(MOSAIC, 0x000f).unwrap();
|
|
let f = render(&m);
|
|
assert_eq!(at(&f, 15, 1), at(&base, 0, 1));
|
|
assert_ne!(at(&f, 15, 1), at(&base, 0, 0));
|
|
}
|
|
|
|
#[test]
|
|
fn text_mosaic_samples_before_scroll_and_tile_flips_in_both_depths() {
|
|
for eight_bit in [false, true] {
|
|
let mut m = memory();
|
|
m.write16(DISPCNT, 0x100).unwrap();
|
|
let control = 0x1000 | if eight_bit { 0x80 } else { 0 };
|
|
m.write16(BG0CNT, control).unwrap();
|
|
m.write16(BG0HOFS, 7).unwrap();
|
|
m.write16(BG0VOFS, 5).unwrap();
|
|
fill(&mut m, VRAM + 0x8000, 0x800, 0x0c00); // Both tile flips.
|
|
for y in 0..8 {
|
|
for x in (0..8).step_by(if eight_bit { 2 } else { 4 }) {
|
|
let mut value = 0;
|
|
for i in 0..if eight_bit { 2 } else { 4 } {
|
|
let index = ((x + i + y * 3) % 15) as u16;
|
|
value |= index << (i * if eight_bit { 8 } else { 4 });
|
|
}
|
|
m.write16(
|
|
VRAM + (y * if eight_bit { 8 } else { 4 } + x / if eight_bit { 1 } else { 2 })
|
|
as u32,
|
|
value,
|
|
)
|
|
.unwrap();
|
|
}
|
|
}
|
|
let base = render(&m);
|
|
m.write16(BG0CNT, control | 0x40).unwrap();
|
|
m.write16(MOSAIC, 0x54).unwrap(); // 5x6 crosses tile boundaries.
|
|
let f = render(&m);
|
|
for y in 0..HEIGHT {
|
|
for x in 0..WIDTH {
|
|
assert_eq!(at(&f, x, y), at(&base, x / 5 * 5, y / 6 * 6));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn affine_mosaic_samples_screen_positions_before_signed_transform() {
|
|
for mode in [1, 2, 3, 4, 5] {
|
|
let mut m = if mode >= 3 { bitmap(mode, 0) } else { memory() };
|
|
m.write16(DISPCNT, 0x400 | mode).unwrap();
|
|
let control = if mode <= 2 { 0x3000 } else { 0 };
|
|
m.write16(BG2CNT, control).unwrap();
|
|
if mode <= 2 {
|
|
for offset in (0..64).step_by(2) {
|
|
m.write16(VRAM + offset, (offset as u16 * 0x101).wrapping_add(0x100))
|
|
.unwrap();
|
|
}
|
|
}
|
|
for (reg, value) in [
|
|
(BG2PA, 181),
|
|
(BG2PB, 91),
|
|
(BG2PC, (-181i16) as u16),
|
|
(BG2PD, 256),
|
|
] {
|
|
m.write16(reg, value).unwrap();
|
|
}
|
|
m.write32(BG2X, (-769i32) as u32).unwrap();
|
|
m.write32(BG2Y, 31 * 256 + 127).unwrap();
|
|
let base = render(&m);
|
|
m.write16(BG2CNT, control | 0x40).unwrap();
|
|
m.write16(MOSAIC, 0x76).unwrap();
|
|
let f = render(&m);
|
|
for y in 0..HEIGHT {
|
|
for x in 0..WIDTH {
|
|
assert_eq!(
|
|
at(&f, x, y),
|
|
at(&base, x / 7 * 7, y / 8 * 8),
|
|
"mode={mode} pixel={x},{y}"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn independent_background_flags_preserve_transparency_and_priority() {
|
|
let mut m = memory();
|
|
m.write16(DISPCNT, 0x300).unwrap();
|
|
m.write16(BG0CNT, 0x1040).unwrap();
|
|
m.write16(BG1CNT, 0x1105).unwrap();
|
|
m.write16(PAL + 2, 31).unwrap();
|
|
m.write16(PAL + 4, 0x3e0).unwrap();
|
|
fill(&mut m, VRAM, 32, 0x1010);
|
|
fill(&mut m, VRAM + 0x4000, 32, 0x2222);
|
|
m.write16(MOSAIC, 2).unwrap();
|
|
let f = render(&m);
|
|
for x in 0..18 {
|
|
assert_eq!(at(&f, x, 0), rgb(if x / 3 % 2 == 0 { 0x3e0 } else { 31 }));
|
|
}
|
|
m.write16(BG0CNT, 0x1000).unwrap();
|
|
m.write16(BG1CNT, 0x1145).unwrap();
|
|
let f = render(&m);
|
|
for x in 0..18 {
|
|
assert_eq!(at(&f, x, 0), rgb(if x % 2 == 0 { 0x3e0 } else { 31 }));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn windows_and_color_effects_use_output_coordinate_not_mosaic_sample() {
|
|
let mut m = bitmap(3, 0);
|
|
m.write16(BG2CNT, 0x40).unwrap();
|
|
m.write16(MOSAIC, 0x77).unwrap();
|
|
m.write16(DISPCNT, 0x2403).unwrap();
|
|
m.write16(WIN0H, 0x0306).unwrap();
|
|
m.write16(WIN0V, 0x0008).unwrap();
|
|
m.write16(WININ, 4).unwrap();
|
|
m.write16(WINOUT, 0x24).unwrap();
|
|
m.write16(BLDCNT, 0x84).unwrap();
|
|
m.write16(BLDY, 16).unwrap();
|
|
let f = render(&m);
|
|
for x in 0..8 {
|
|
assert_eq!(at(&f, x, 0), if (3..6).contains(&x) { 0 } else { 0xffffff });
|
|
}
|
|
m.write16(DISPCNT, 0xffff).unwrap();
|
|
assert!(render(&m).pixels().iter().all(|&p| p == 0xffffff));
|
|
}
|