fix(video): read BG maps and tiles through the 128 KiB VRAM mirror window

This commit is contained in:
0xrsydn 2026-10-07 10:06:48 +07:00
commit d7de5f7121
6 changed files with 60 additions and 50 deletions

View file

@ -252,7 +252,7 @@ fn transformed_backgrounds_composite_with_regular_and_affine_sprites() {
}
#[test]
fn diagnostics_leave_output_unchanged_and_disabled_layers_skip_validation() {
fn unsupported_layers_leave_output_unchanged_and_disabled_layers_skip_validation() {
let mut m = setup(2, 3, false);
let mut f = Framebuffer::default();
f.clear(0x001f);
@ -262,15 +262,8 @@ fn diagnostics_leave_output_unchanged_and_disabled_layers_skip_validation() {
m.render_frame(&mut f),
Err(VideoError::UnsupportedLayers(control))
);
assert!(f.pixels().iter().all(|&p| p == 0xff0000));
}
m.write16(DISPCNT, 0x402).unwrap();
m.write16(BG2CNT, 0xdf00).unwrap(); // Map31, 1024 pixels.
m.write32(BG2Y, 128 * 256).unwrap(); // Flat row starts outside first64K.
assert_eq!(
m.render_frame(&mut f),
Err(VideoError::UnsupportedMapAddress(0x10000))
);
assert!(f.pixels().iter().all(|&p| p == 0xff0000));
m.write16(DISPCNT, 2).unwrap();
m.render_frame(&mut f).unwrap();
assert!(f.pixels().iter().all(|&p| p == rgb555_to_rgb888(0x4000)));
@ -279,6 +272,17 @@ fn diagnostics_leave_output_unchanged_and_disabled_layers_skip_validation() {
assert!(f.pixels().iter().all(|&p| p == 0xffffff));
}
#[test]
fn affine_map_at_map_base_31_reads_high_vram_through_the_bus_mirror() {
let mut m = setup(2, 3, false);
m.write16(BG2CNT, 0xdf00).unwrap(); // Map31, 1024 pixels.
m.write32(BG2Y, 128 * 256).unwrap(); // Map row16 starts at offset0x10000.
m.write16(VRAM + 0x18000, 1).unwrap(); // Halfword bus mirror of the selected map byte.
let mut f = Framebuffer::default();
m.render_frame(&mut f).unwrap();
assert_eq!(f.pixels()[0], rgb555_to_rgb888(31));
}
#[test]
fn cpu_demo_pans_rotates_zooms_clips_cancels_opposites_and_resets() {
use gba_core::input::{Button, Buttons};

View file

@ -184,48 +184,40 @@ fn priorities_ties_transparency_and_opaque_black_composite_all_four_layers() {
fn character_base_and_high_tile_numbers_cross_character_blocks() {
for eight in [false, true] {
let mut m = setup();
m.write16(BG0CNT, 0x1004 | if eight { 0x80 } else { 0 })
m.write16(BG0CNT, 0x100c | if eight { 0x80 } else { 0 })
.unwrap();
m.write16(VRAM + 0x8000, 1023).unwrap();
let address = 0x4000 + 1023 * if eight { 64 } else { 32 };
if eight {
assert!(matches!(
m.render_frame(&mut Framebuffer::default()),
Err(VideoError::UnsupportedTileAddress(_))
));
} else {
tile(&mut m, address, 0x1111, 32);
assert_eq!(pixel(&m, 0, 0), 0xff0000);
}
let address = 0xc000 + 1023 * if eight { 64 } else { 32 };
tile(
&mut m,
address,
if eight { 0x0101 } else { 0x1111 },
if eight { 64 } else { 32 },
);
assert_eq!(pixel(&m, 0, 0), 0xff0000);
}
}
#[test]
fn screen_map_outside_supported_bg_area_reports_error_without_wrapping() {
fn wide_screen_block_at_map_base_31_reads_beyond_64k() {
let mut m = setup();
m.write16(BG0CNT, 0x5f04).unwrap(); // Map block31, wide; tile base1.
m.write16(BG0HOFS, 256).unwrap(); // Second block lies outside the BG area.
let mut frame = Framebuffer::default();
frame.clear(0x001f);
assert_eq!(
m.render_frame(&mut frame),
Err(VideoError::UnsupportedMapAddress(0x10000))
);
assert!(frame.pixels().iter().all(|&p| p == 0xff0000));
m.write16(BG0HOFS, 256).unwrap(); // First pixel selects screen block1 at 0x10000.
m.write16(VRAM + 0x18000, 1).unwrap(); // Bus mirror of the selected map entry.
tile(&mut m, 0x4020, 0x1111, 32);
assert_eq!(pixel(&m, 0, 0), 0xff0000);
}
#[test]
fn unsupported_features_leave_output_unchanged_and_forced_blank_bypasses_them() {
fn unsupported_modes_leave_output_unchanged_and_forced_blank_bypasses_them() {
let mut m = setup();
let mut frame = Framebuffer::default();
frame.clear(0x001f);
m.write16(DISPCNT, 0x100).unwrap();
m.write16(BG0CNT, 0x100c).unwrap();
m.write16(VRAM + 0x8002, 1023).unwrap(); // Error after some valid pixels.
assert!(matches!(
m.write16(DISPCNT, 0x106).unwrap();
assert_eq!(
m.render_frame(&mut frame),
Err(VideoError::UnsupportedTileAddress(_))
));
Err(VideoError::UnsupportedMode(6))
);
assert!(frame.pixels().iter().all(|&p| p == 0xff0000));
m.write16(DISPCNT, 0xf180).unwrap();
m.render_frame(&mut frame).unwrap();