From 1ab2898f5e59f5e847fb570ff352f6512ad0466e Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Tue, 28 Apr 2026 18:17:12 +0100 Subject: [PATCH] Internal Viewport 270p (480x270) --- crates/tools/pbxgen-stress/src/lib.rs | 87 +++++++++++++-------- test-cartridges/stress-console/assets.pa | Bin 35452 -> 100989 bytes test-cartridges/stress-console/program.pbx | Bin 1205 -> 1205 bytes 3 files changed, 53 insertions(+), 34 deletions(-) diff --git a/crates/tools/pbxgen-stress/src/lib.rs b/crates/tools/pbxgen-stress/src/lib.rs index 284f7b87..e89b2479 100644 --- a/crates/tools/pbxgen-stress/src/lib.rs +++ b/crates/tools/pbxgen-stress/src/lib.rs @@ -26,6 +26,13 @@ use std::path::PathBuf; const STRESS_GLYPH_ASSET_ID: AssetId = 100; const STRESS_SCENE_ASSET_ID: AssetId = 1; +const STRESS_VIEWPORT_W: i32 = 480; +const STRESS_VIEWPORT_H: i32 = 270; +const STRESS_SPRITE_COLS: i32 = 48; +const STRESS_SPRITE_ROWS: i32 = 24; +const STRESS_SPRITE_SPACING_PX: i32 = 10; +const STRESS_SCENE_TILE_W: usize = 96; +const STRESS_SCENE_TILE_H: usize = 64; fn asm(s: &str) -> Vec { assemble(s).expect("assemble") @@ -141,34 +148,38 @@ fn heavy_load(rom: &mut Vec) { rom.extend(asm("PUSH_I32 0\nHOSTCALL 0")); - rom.extend(asm( - "GET_GLOBAL 0\nPUSH_I32 2\nMUL\nPUSH_I32 192\nMOD\nGET_GLOBAL 0\nPUSH_I32 76\nMOD\nHOSTCALL 4", - )); + let scene_width_px = (STRESS_SCENE_TILE_W * 8) as i32; + let scene_height_px = (STRESS_SCENE_TILE_H * 8) as i32; + let camera_x_span = scene_width_px - STRESS_VIEWPORT_W; + let camera_y_span = scene_height_px - STRESS_VIEWPORT_H; + rom.extend(asm(&format!( + "GET_GLOBAL 0\nPUSH_I32 2\nMUL\nPUSH_I32 {camera_x_span}\nMOD\nGET_GLOBAL 0\nPUSH_I32 {camera_y_span}\nMOD\nHOSTCALL 4" + ))); rom.extend(asm("PUSH_I32 0\nSET_LOCAL 0")); let row_loop_start = rom.len() as u32; - rom.extend(asm("GET_LOCAL 0\nPUSH_I32 16\nLT")); + rom.extend(asm(&format!("GET_LOCAL 0\nPUSH_I32 {STRESS_SPRITE_ROWS}\nLT"))); let jif_row_end_offset = rom.len() + 2; rom.extend(asm("JMP_IF_FALSE 0")); rom.extend(asm("PUSH_I32 0\nSET_LOCAL 1")); let col_loop_start = rom.len() as u32; - rom.extend(asm("GET_LOCAL 1\nPUSH_I32 32\nLT")); + rom.extend(asm(&format!("GET_LOCAL 1\nPUSH_I32 {STRESS_SPRITE_COLS}\nLT"))); let jif_col_end_offset = rom.len() + 2; rom.extend(asm("JMP_IF_FALSE 0")); - rom.extend(asm( + rom.extend(asm(&format!( "PUSH_I32 0\n\ - GET_LOCAL 0\nPUSH_I32 32\nMUL\nGET_LOCAL 1\nADD\nGET_GLOBAL 0\nADD\nPUSH_I32 15\nMOD\nPUSH_I32 1\nADD\n\ - GET_LOCAL 1\nPUSH_I32 10\nMUL\nGET_GLOBAL 0\nPUSH_I32 10\nMOD\nADD\nPUSH_I32 320\nMOD\n\ - GET_LOCAL 0\nPUSH_I32 10\nMUL\nGET_GLOBAL 0\nPUSH_I32 2\nMUL\nPUSH_I32 10\nMOD\nADD\nPUSH_I32 180\nMOD\n\ + GET_LOCAL 0\nPUSH_I32 {STRESS_SPRITE_COLS}\nMUL\nGET_LOCAL 1\nADD\nGET_GLOBAL 0\nADD\nPUSH_I32 15\nMOD\nPUSH_I32 1\nADD\n\ + GET_LOCAL 1\nPUSH_I32 {STRESS_SPRITE_SPACING_PX}\nMUL\nGET_GLOBAL 0\nPUSH_I32 10\nMOD\nADD\nPUSH_I32 {STRESS_VIEWPORT_W}\nMOD\n\ + GET_LOCAL 0\nPUSH_I32 {STRESS_SPRITE_SPACING_PX}\nMUL\nGET_GLOBAL 0\nPUSH_I32 2\nMUL\nPUSH_I32 10\nMOD\nADD\nPUSH_I32 {STRESS_VIEWPORT_H}\nMOD\n\ GET_LOCAL 0\nGET_LOCAL 1\nADD\nGET_GLOBAL 0\nADD\nPUSH_I32 4\nMOD\n\ PUSH_I32 0\n\ GET_LOCAL 1\nGET_GLOBAL 0\nADD\nPUSH_I32 1\nBIT_AND\nPUSH_I32 0\nNEQ\n\ GET_LOCAL 0\nGET_GLOBAL 0\nADD\nPUSH_I32 1\nBIT_AND\nPUSH_I32 0\nNEQ\n\ GET_LOCAL 0\nGET_LOCAL 1\nADD\nPUSH_I32 4\nMOD\n\ - HOSTCALL 5\nPOP_N 1", - )); + HOSTCALL 5\nPOP_N 1" + ))); rom.extend(asm("GET_LOCAL 1\nPUSH_I32 1\nADD\nSET_LOCAL 1")); let jmp_col_loop_offset = rom.len() + 2; @@ -180,23 +191,23 @@ fn heavy_load(rom: &mut Vec) { rom.extend(asm("JMP 0")); let row_loop_end = rom.len() as u32; - rom.extend(asm("GET_GLOBAL 0\nPUSH_I32 3\nMUL\nPUSH_I32 220\nMOD\n\ + rom.extend(asm("GET_GLOBAL 0\nPUSH_I32 3\nMUL\nPUSH_I32 380\nMOD\n\ PUSH_I32 8\n\ PUSH_CONST 0\n\ GET_GLOBAL 0\nPUSH_I32 2047\nMUL\nPUSH_I32 65535\nBIT_AND\n\ HOSTCALL 1")); - rom.extend(asm("PUSH_I32 12\n\ - GET_GLOBAL 0\nPUSH_I32 2\nMUL\nPUSH_I32 120\nMOD\nPUSH_I32 24\nADD\n\ + rom.extend(asm("PUSH_I32 16\n\ + GET_GLOBAL 0\nPUSH_I32 2\nMUL\nPUSH_I32 186\nMOD\nPUSH_I32 32\nADD\n\ PUSH_CONST 1\n\ GET_GLOBAL 0\nPUSH_I32 4093\nMUL\nPUSH_I32 65535\nBIT_AND\n\ HOSTCALL 1")); - rom.extend(asm("PUSH_I32 220\n\ - GET_GLOBAL 0\nPUSH_I32 5\nMUL\nPUSH_I32 140\nMOD\n\ + rom.extend(asm("PUSH_I32 336\n\ + GET_GLOBAL 0\nPUSH_I32 5\nMUL\nPUSH_I32 194\nMOD\n\ PUSH_CONST 2\n\ GET_GLOBAL 0\nPUSH_I32 1237\nMUL\nPUSH_I32 65535\nBIT_AND\n\ HOSTCALL 1")); - rom.extend(asm("GET_GLOBAL 0\nPUSH_I32 4\nMUL\nPUSH_I32 180\nMOD\nPUSH_I32 80\nADD\n\ - GET_GLOBAL 0\nPUSH_I32 3\nMUL\nPUSH_I32 90\nMOD\nPUSH_I32 70\nADD\n\ + rom.extend(asm("GET_GLOBAL 0\nPUSH_I32 4\nMUL\nPUSH_I32 240\nMOD\nPUSH_I32 112\nADD\n\ + GET_GLOBAL 0\nPUSH_I32 3\nMUL\nPUSH_I32 120\nMOD\nPUSH_I32 88\nADD\n\ PUSH_CONST 3\n\ GET_GLOBAL 0\nPUSH_I32 3001\nMUL\nPUSH_I32 65535\nBIT_AND\n\ HOSTCALL 1")); @@ -326,19 +337,23 @@ fn build_scene_bank() -> SceneBank { flip_x: false, flip_y: false, }; - 64 * 32 + STRESS_SCENE_TILE_W * STRESS_SCENE_TILE_H ]; - for step in 0..8 { - let x = 4 + step * 7 + layer_index * 2; - let y = 2 + step * 3 + layer_index * 2; - let index = y * 64 + x; - tiles[index] = Tile { - active: true, - glyph: Glyph { glyph_id: 0, palette_id: layer_index as u8 + 1 }, - flip_x: false, - flip_y: false, - }; + for y in 0..STRESS_SCENE_TILE_H { + for x in 0..STRESS_SCENE_TILE_W { + let diagonal = (x + layer_index * 2) % 11 == (y * 2) % 11; + let stripe = (x / 6 + y / 5 + layer_index) % 5 == 0; + if diagonal || stripe { + let index = y * STRESS_SCENE_TILE_W + x; + tiles[index] = Tile { + active: true, + glyph: Glyph { glyph_id: 0, palette_id: layer_index as u8 + 1 }, + flip_x: false, + flip_y: false, + }; + } + } } SceneLayer { @@ -351,15 +366,19 @@ fn build_scene_bank() -> SceneBank { 2 => ParallaxFactor { x: 0.5, y: 0.5 }, _ => ParallaxFactor { x: 0.25, y: 0.25 }, }, - tilemap: TileMap { width: 64, height: 32, tiles }, + tilemap: TileMap { + width: STRESS_SCENE_TILE_W, + height: STRESS_SCENE_TILE_H, + tiles, + }, } }); // Keep the farthest layer a bit sparser so the diagonal remains visually readable. - for step in 0..4 { - let x = 10 + step * 12; - let y = 4 + step * 5; - let index = y * 64 + x; + for step in 0..12 { + let x = 10 + step * 7; + let y = 4 + step * 3; + let index = y * STRESS_SCENE_TILE_W + x; layers[3].tilemap.tiles[index].active = false; } diff --git a/test-cartridges/stress-console/assets.pa b/test-cartridges/stress-console/assets.pa index ca43164c50f6549648ce3bc8bdc03c745d70fe71..aad01aef04d41d430cdb613f37a26d8d556fe01e 100644 GIT binary patch literal 100989 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 SgA8yTP&2@8sB`(BdHDn5j3`84<_$t)Mesh zocxaQ6jKf3k delta 170 zcmdnWxs`K+03-ha1_lNd1_vO?z#z`xGg+Qdo>5@3J&;tG9M32TBr6$R8676?XVhid z!Z7(A<0+;)43l>;>B;f{bvS^u0rfEfNhKg%0i;!cw8UgqW=+vMK%N5-gH*5rNsx-3 g$RW(3kh%aMtpHcU3{?{am37;^kXeKg0MY9iGXMYp