From 0ce660c9cb3477ad7cce809e7543e077626070ed Mon Sep 17 00:00:00 2001 From: zohassadar Date: Tue, 24 Feb 2026 14:42:56 +0000 Subject: [PATCH 01/25] lua script for tas testing in mesen --- tools/tas.lua | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tools/tas.lua diff --git a/tools/tas.lua b/tools/tas.lua new file mode 100644 index 00000000..b43ec574 --- /dev/null +++ b/tools/tas.lua @@ -0,0 +1,38 @@ +-- uses https://tasvideos.org/7625S +local rom = emu.getRomInfo() +local romPath, _ = string.gsub(rom.path, rom.name, "") +local filename = romPath .. "r57shell_archanfel-tetris-maxscore.fm2" + +local inputs = {} +for line in io.lines(filename) do + local buttons = line:match("^|0|(........)|||$") + if buttons then + local input = {} + local mapped = { + "right", + "left", + "down", + "up", + "start", + "select", + "b", + "a", + } + for i = 1, 8 do + input[mapped[i]] = not (buttons:sub(i, i) == ".") + end + table.insert(inputs, input) + end +end + +function applyInputs() + local state = emu.getState() + local input = inputs[state["frameCount"]+1] + if not input then + emu.breakExecution() + else + emu.setInput(input) + end +end + +emu.addEventCallback(applyInputs, emu.eventType.inputPolled); From 19094dc217f6534c2cc2410f32b420095e51f5cb Mon Sep 17 00:00:00 2001 From: zohassadar Date: Sun, 8 Mar 2026 16:45:40 +0000 Subject: [PATCH 02/25] cycle parity test log.lua - outputs tetris.log or clean.log with games rng, frame counter and game mode. sends start 4 times at arbitrary frames to start a game and logs 1001 frames cycle_parity.rs - compares same variables each frame and sends starts at the same time, panics at the first frame that doesn't match test currently fails, goal is to support tas parity for any game that starts within ~1792 frames (time for demo to start) --- .gitignore | 1 + tests/src/cycle_parity.rs | 76 +++++++++++++++++++++++++++++++++++++++ tests/src/main.rs | 4 ++- tools/log.lua | 53 +++++++++++++++++++++++++++ tools/tas.lua | 4 +-- 5 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 tests/src/cycle_parity.rs create mode 100644 tools/log.lua diff --git a/.gitignore b/.gitignore index 07ad212d..7910a645 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.pyc *.bin *.txt +*.log tetris.lst tetris.lbl tetris.map diff --git a/tests/src/cycle_parity.rs b/tests/src/cycle_parity.rs new file mode 100644 index 00000000..76bf9e86 --- /dev/null +++ b/tests/src/cycle_parity.rs @@ -0,0 +1,76 @@ +use crate::{input, labels, util}; + +pub fn test() { + const TEST_LENGTH: usize = 1000; + // 265 is earliest frame that responds to start + const START_FRAMES: [usize; 5] = [270, 280, 290, 300, TEST_LENGTH + 1]; + + let rng_seed1 = labels::get("rng_seed") as usize; + let rng_seed2 = (labels::get("rng_seed") + 1) as usize; + let frame_counter1 = labels::get("frameCounter") as usize; + let frame_counter2 = (labels::get("frameCounter") + 1) as usize; + let game_mode = labels::get("gameMode") as usize; + + let mut og_rng1: u8; + let mut og_rng2: u8; + let mut og_fc1: u8; + let mut og_fc2: u8; + let mut og_gm: u8; + + let mut gym_rng1: u8; + let mut gym_rng2: u8; + let mut gym_fc1: u8; + let mut gym_fc2: u8; + let mut gym_gm: u8; + + let mut og = util::emulator(Some(util::OG_ROM)); + let mut gym = util::emulator(None); + let mut start_idx = 0; + let mut unset_buttons = false; + + for i in 0..=TEST_LENGTH { + og.run_until_vblank(); + gym.run_until_vblank(); + if unset_buttons { + util::set_controller_raw(&mut og, 0); + util::set_controller_raw(&mut gym, 0); + unset_buttons = false; + } + + if i == START_FRAMES[start_idx] { + start_idx += 1; + util::set_controller_raw(&mut og, input::START); + util::set_controller_raw(&mut gym, input::START); + unset_buttons = true; + } + + og_rng1 = og.memory.iram_raw[rng_seed1]; + og_rng2 = og.memory.iram_raw[rng_seed2]; + og_fc1 = og.memory.iram_raw[frame_counter1]; + og_fc2 = og.memory.iram_raw[frame_counter2]; + og_gm = og.memory.iram_raw[game_mode]; + + gym_rng1 = gym.memory.iram_raw[rng_seed1]; + gym_rng2 = gym.memory.iram_raw[rng_seed2]; + gym_fc1 = gym.memory.iram_raw[frame_counter1]; + gym_fc2 = gym.memory.iram_raw[frame_counter2]; + gym_gm = gym.memory.iram_raw[game_mode]; + + if (og_rng1 != gym_rng1) + || (og_rng2 != gym_rng2) + || (og_fc1 != gym_fc1) + || (og_fc2 != gym_fc2) + || (og_gm != gym_gm) + { + println!( + "Expected:\n{:04} {:02X}{:02X} {:02X}{:02X} {:02X}", + i, og_rng2, og_rng1, og_fc2, og_fc1, og_gm, + ); + println!( + "Actual:\n{:04} {:02X}{:02X} {:02X}{:02X} {:02X}", + i, gym_rng2, gym_rng1, gym_fc2, gym_fc1, gym_gm, + ); + panic!("tools/log.lua to generate log"); + } + } +} diff --git a/tests/src/main.rs b/tests/src/main.rs index c47905fd..bd538f36 100644 --- a/tests/src/main.rs +++ b/tests/src/main.rs @@ -6,6 +6,7 @@ mod util; mod video; mod cycle_count; +mod cycle_parity; mod crash; mod crunch; @@ -57,7 +58,7 @@ struct TestOptions { fn main() { let options = TestOptions::parse_args_default_or_exit(); - let tests: [(&str, fn()); 17] = [ + let tests: [(&str, fn()); 18] = [ ("garbage4", garbage::test_garbage4_crash), ("floor", floor::test), ("tspins", tspins::test), @@ -75,6 +76,7 @@ fn main() { ("patch", patch::test), ("crunch", crunch::test), ("harddrop", harddrop::test), + ("parity", cycle_parity::test), ]; // run tests diff --git a/tools/log.lua b/tools/log.lua new file mode 100644 index 00000000..529d1485 --- /dev/null +++ b/tools/log.lua @@ -0,0 +1,53 @@ +-- line up with cycle_parity.rs indexing +-- frameCount starts at 2 +local OFFSET = -2 +local TEST_LENGTH = 1000 +local START_FRAMES = {270, 280, 290, 300, 0} + +local BREAK_FRAME = nil + +local rom = emu.getRomInfo() +local romPath, _ = string.gsub(rom.path, rom.name, "") +local filename = rom.name:gsub("%.%w+$", "") .. ".log" +local path = string.gsub(rom.path, rom.name, filename) +emu.log("Opening " .. path) +local file = io.open(path, "w") + +local startIdx = {1} +function logFrame() + state = emu.getState() + local frameCount = state.frameCount + OFFSET + + if BREAK_FRAME == frameCount then + emu.breakExecution() + end + + if frameCount == START_FRAMES[startIdx[1]] then + emu.setInput({start = true}) + startIdx[1] = startIdx[1] + 1 + end + + local rng_addr = emu.getLabelAddress('rng_seed') + local rng_seed = emu.read16(rng_addr.address, rng_addr.memType) + + local fc_addr = emu.getLabelAddress('frameCounter') + local frameCounter = emu.read16(fc_addr.address, fc_addr.memType) + + local gc_addr = emu.getLabelAddress('gameMode') + local gameMode = emu.read16(gc_addr.address, gc_addr.memType) + + file:write( + string.format("%04d", frameCount) .. " " + .. string.format("%04X", rng_seed) .. " " + .. string.format("%04X", frameCounter) .. " " + .. string.format("%02X", gameMode) .. "\n" + ) + + if frameCount == TEST_LENGTH then + file:close() + emu.breakExecution() + emu.log("Wrote " .. frameCount + 1 .. " lines to " .. path) + end +end + +emu.addEventCallback(logFrame, emu.eventType.inputPolled); diff --git a/tools/tas.lua b/tools/tas.lua index b43ec574..a9e9306e 100644 --- a/tools/tas.lua +++ b/tools/tas.lua @@ -1,7 +1,7 @@ --- uses https://tasvideos.org/7625S +-- https://tasvideos.org/7625S local rom = emu.getRomInfo() local romPath, _ = string.gsub(rom.path, rom.name, "") -local filename = romPath .. "r57shell_archanfel-tetris-maxscore.fm2" +local filename = romPath .. "r57shell-Archanfel-Tetris-fastest999999.fm2" local inputs = {} for line in io.lines(filename) do From 55d0827501eeebad84854d0b793db49f2e10112d Mon Sep 17 00:00:00 2001 From: zohassadar Date: Tue, 10 Mar 2026 14:30:39 +0000 Subject: [PATCH 03/25] vanilla parity passes a set of tests that compare frameCounter's low byte, both rng_bytes and the gameMode byte with vanilla's at the same time. current state: game modes 0 1 and 2 are all defined in branch.asm menu is not reachable first 255 frames of legal screen can't be skipped intentional differences: vanilla switches to gameMode 6 (demo setup) (5 by the the the next frame comes around) when gameMode is 1 (title screen) and the high byte of the frameCounter is 5. This switches to gameMode 2 (game type menu) instead When doing nothing, this happens at frame 1795 --- src/boot.asm | 10 + src/gamemode/branch.asm | 257 ++++++++++++++++++++++- src/gamemode/levelmenu.asm | 10 + src/gamemodestate/initbackground.asm | 4 +- src/modes/crash.asm | 2 +- src/nametables.asm | 20 +- src/nmi/render.asm | 2 +- tests/Cargo.lock | 42 ++++ tests/Cargo.toml | 1 + tests/src/main.rs | 4 +- tests/src/{cycle_parity.rs => parity.rs} | 63 ++++-- tools/log.lua | 28 ++- tools/tas.lua | 1 + 13 files changed, 411 insertions(+), 33 deletions(-) rename tests/src/{cycle_parity.rs => parity.rs} (50%) diff --git a/src/boot.asm b/src/boot.asm index 707d2ac2..ce4dda6d 100644 --- a/src/boot.asm +++ b/src/boot.asm @@ -80,9 +80,19 @@ jsr disableNmi jsr drawBlackBGPalette ; instead of clearing vram like the original, blank out the palette +; align with vanilla at frame 0004 + ldx #$0 + ldy #$20 +@wait: + dex + bne @wait + dey + bne @wait + lda #$EF ldx #$04 ldy #$04 ; used to be 5, but we dont need to clear 2p playfield + jsr memset_page jsr waitForVBlankAndEnableNmi jsr updateAudioWaitForNmiAndResetOamStaging diff --git a/src/gamemode/branch.asm b/src/gamemode/branch.asm index b3111a1c..e46204c3 100644 --- a/src/gamemode/branch.asm +++ b/src/gamemode/branch.asm @@ -1,9 +1,9 @@ ; 2nd and 3rd instances of playAndEndingHighScore_jmp used to be demo and startDemo respectively branchOnGameMode: branchTo gameMode, \ - gameMode_bootScreen, \ - gameMode_waitScreen, \ - gameMode_gameTypeMenu, \ + gameMode0, \ + gameMode1, \ + gameMode2, \ gameMode_levelMenu, \ gameMode_playAndEndingHighScore_jmp, \ gameMode_playAndEndingHighScore_jmp, \ @@ -15,8 +15,259 @@ branchOnGameMode: .include "gametypemenu/menu.asm" .include "levelmenu.asm" +gameMode0: + lda #$00 + sta renderMode + + ; reset cursors + lda #$0 + sta practiseType + sta menuSeedCursorIndex + + ; levelMenu stuff + sta levelControlMode + lda #INITIAL_CUSTOM_LEVEL + sta customLevel + + jsr updateAudioWaitForNmiAndDisablePpuRendering + jsr checkRegion +.if KEYBOARD = 1 + ; todo test timing for keyboard + jsr detectKeyboard +.endif + jsr updateAudioWaitForNmiAndDisablePpuRendering + jsr disableNmi + + lda #NMIEnable + sta currentPpuCtrl +.if INES_MAPPER <> 0 +; NROM (and possibly FDS in the future) won't load the 2nd bankset +; and will instead use the title/menu chrset letters. This won't be noticeable +; unless a graphic is added + lda #CHRBankSet1 + jsr changeCHRBanks +.endif + jsr bulkCopyToPpu + .addr wait_palette + jsr copyRleNametableToPpu + .addr legal_nametable + + jsr bulkCopyToPpu + .addr legal_nametable_patch + + jsr waitForVBlankAndEnableNmi + + jsr stageBootSprites + jsr updateAudioWaitForNmiAndResetOamStaging + + dec sleepCounter + jsr stageBootSprites + jsr updateAudioWaitForNmiAndEnablePpuRendering + lda #$01 + sta qualFlag + + lda #$00 + sta frameCounter+1 +gameMode0Loop: + lda qualFlag + bne @wait + + lda newlyPressedButtons_player1 + and #BUTTON_START + bne @goToGameMode1 +@wait: + jsr stageBootSprites + jsr updateAudioWaitForNmiAndResetOamStaging + + lda sleepCounter + bne gameMode0Loop + + lda frameCounter+1 + cmp #$02 + beq @goToGameMode1 + + ; this is the point where start can be pressed + lda #$00 + sta qualFlag + + dec sleepCounter + bne gameMode0Loop +@goToGameMode1: + inc gameMode + rts + + +gameMode1: + lda #$00 + sta renderMode + + ; reset cursors + lda #$0 + sta practiseType + sta menuSeedCursorIndex + + ; levelMenu stuff + sta levelControlMode + lda #INITIAL_CUSTOM_LEVEL + sta customLevel + + jsr updateAudioWaitForNmiAndDisablePpuRendering + jsr updateAudioWaitForNmiAndDisablePpuRendering + jsr disableNmi + + lda #NMIEnable + sta currentPpuCtrl +.if INES_MAPPER <> 0 +; NROM (and possibly FDS in the future) won't load the 2nd bankset +; and will instead use the title/menu chrset letters. This won't be noticeable +; unless a graphic is added + lda #CHRBankSet1 + jsr changeCHRBanks +.endif + jsr bulkCopyToPpu + .addr wait_palette + jsr copyRleNametableToPpu + .addr legal_nametable + + jsr bulkCopyToPpu + .addr title_nametable_patch + + jsr waitForVBlankAndEnableNmi + jsr stageBootSprites + jsr updateAudioWaitForNmiAndResetOamStaging + jsr stageBootSprites + jsr updateAudioWaitForNmiAndEnablePpuRendering + lda #$00 + sta frameCounter+1 + dec sleepCounter + +gameMode1Loop: + jsr stageBootSprites + jsr updateAudioWaitForNmiAndResetOamStaging + + lda newlyPressedButtons_player1 + and #BUTTON_START + bne @goToGameMode2 + +; this is the point vanilla enters demo mode +; go to game select menu page instead + lda frameCounter+1 + cmp #$05 + beq @goToGameMode2 + bne gameMode1Loop + +@goToGameMode2: + inc gameMode + rts + +gameMode2: + lda #$00 + sta renderMode + + ; reset cursors + lda #$0 + sta practiseType + sta menuSeedCursorIndex + + ; levelMenu stuff + sta levelControlMode + lda #INITIAL_CUSTOM_LEVEL + sta customLevel + + jsr updateAudioWaitForNmiAndDisablePpuRendering + jsr updateAudioWaitForNmiAndDisablePpuRendering + jsr disableNmi + + lda #NMIEnable + sta currentPpuCtrl +.if INES_MAPPER <> 0 +; NROM (and possibly FDS in the future) won't load the 2nd bankset +; and will instead use the title/menu chrset letters. This won't be noticeable +; unless a graphic is added + lda #CHRBankSet1 + jsr changeCHRBanks +.endif + jsr bulkCopyToPpu + .addr wait_palette + jsr copyRleNametableToPpu + .addr legal_nametable + + jsr bulkCopyToPpu + .addr menu_nametable_patch + + jsr waitForVBlankAndEnableNmi + jsr stageBootSprites + jsr updateAudioWaitForNmiAndResetOamStaging + jsr stageBootSprites + jsr updateAudioWaitForNmiAndEnablePpuRendering +gameMode2Loop: + lda newlyPressedButtons_player1 + and #BUTTON_START + bne @goToGameMode3 + + jsr stageBootSprites + jsr updateAudioWaitForNmiAndResetOamStaging + jmp gameMode2Loop + +@goToGameMode3: + inc gameMode + rts + gameMode_playAndEndingHighScore_jmp: jsr branchOnGameModeState rts .include "speedtest.asm" + +stageBootSprites: + ldx #frameCounter+1 + jsr stageZeroPage + ldx #frameCounter + jsr stageZeroPage + + ldx #rng_seed+1 + jsr stageZeroPage + ldx #rng_seed + jsr stageZeroPage + + ldx #sleepCounter + jsr stageZeroPage + ldx #generalCounter + jsr stageZeroPage + + ldx #gameMode + jsr stageZeroPage + + rts + +stageZeroPage: + lda oamStagingLength + and #$0F + asl + clc + adc #$1A + sta spriteXOffset + lda oamStagingLength + and #$F0 + clc + adc #$20 + sta spriteYOffset + stx byteSpriteAddr + lda #0 + sta byteSpriteAddr+1 + sta byteSpriteTile + lda #1 + sta byteSpriteLen + jmp byteSprite + +render_mode_0: +; rustico's run_until_vblank stops a few cycles into scanline 242 where nmi +; business is being done and messes up tests. for testing purposes while nothing +; is needed in nmi, a minimum amount of wait time is needed so that test polling +; always reads after game logic but before the nmi affects memory values, +; specifically the frame counter and rng + ldx #$40 +@wait: + dex + bne @wait + rts diff --git a/src/gamemode/levelmenu.asm b/src/gamemode/levelmenu.asm index 751b4810..97e6c3d2 100644 --- a/src/gamemode/levelmenu.asm +++ b/src/gamemode/levelmenu.asm @@ -28,6 +28,16 @@ gameMode_levelMenu: lda #RENDER_LINES sta renderFlags jsr resetScroll + +; skip an nmi cycle to align with vanilla + ldy #$10 + ldx #$00 +@wait: + dex + bne @wait + dey + bne @wait + jsr waitForVBlankAndEnableNmi jsr updateAudioWaitForNmiAndResetOamStaging jsr updateAudioWaitForNmiAndEnablePpuRendering diff --git a/src/gamemodestate/initbackground.asm b/src/gamemodestate/initbackground.asm index cc9658ac..3ae3e3ac 100644 --- a/src/gamemodestate/initbackground.asm +++ b/src/gamemodestate/initbackground.asm @@ -45,12 +45,14 @@ gameModeState_initGameBackground: sta PPUDATA @heartEnd: + lda #NMIEnable|BGPattern1|SpritePattern1 sta PPUCTRL sta currentPpuCtrl jsr resetScroll jsr waitForVBlankAndEnableNmi - jsr updateAudioWaitForNmiAndResetOamStaging + ; gym setup takes longer than vanilla, skip a frame to line up + ; jsr updateAudioWaitForNmiAndResetOamStaging jsr updateAudioWaitForNmiAndEnablePpuRendering jsr updateAudioWaitForNmiAndResetOamStaging lda #$01 diff --git a/src/modes/crash.asm b/src/modes/crash.asm index 6bc3d8a9..67549844 100644 --- a/src/modes/crash.asm +++ b/src/modes/crash.asm @@ -221,7 +221,7 @@ testCrash: inc allegroIndex @newBit0: lda nmiReturnAddr - cmp #= TEST_LENGTH then file:close() emu.breakExecution() emu.log("Wrote " .. frameCount + 1 .. " lines to " .. path) diff --git a/tools/tas.lua b/tools/tas.lua index a9e9306e..5e5c8281 100644 --- a/tools/tas.lua +++ b/tools/tas.lua @@ -24,6 +24,7 @@ for line in io.lines(filename) do table.insert(inputs, input) end end +table.insert(inputs, {}) function applyInputs() local state = emu.getState() From c882076a1ba91b631eeae41a9f04629e1b875c4e Mon Sep 17 00:00:00 2001 From: zohassadar Date: Tue, 10 Mar 2026 15:30:37 +0000 Subject: [PATCH 04/25] lua --- tools/log.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/log.lua b/tools/log.lua index c9f3d84b..5c7409eb 100644 --- a/tools/log.lua +++ b/tools/log.lua @@ -3,8 +3,9 @@ local OFFSET = -2 -- local TEST_LENGTH = 1794 -- local START_FRAMES = {265, 270, 274, 286} -local TEST_LENGTH = 2000 -local START_FRAMES = {1794, 1800, 1900} +local TEST_LENGTH = 700 +local START_FRAMES = {300, 400, 500, 600} +-- local START_FRAMES = {} local BREAK_FRAME = nil @@ -25,8 +26,9 @@ function logFrame() emu.breakExecution() end - if startIdx[1] < #START_FRAMES then + if startIdx[1] < #START_FRAMES + 1 then if frameCount == START_FRAMES[startIdx[1]] then + emu.log(string.format("%04d", frameCount) .. " pressing start") emu.setInput({start = true}) startIdx[1] = startIdx[1] + 1 end From 07fd20836abd618692f040f5cea17acc4a73d3f1 Mon Sep 17 00:00:00 2001 From: zohassadar Date: Tue, 10 Mar 2026 15:31:03 +0000 Subject: [PATCH 05/25] shorter tests first --- tests/src/parity.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/src/parity.rs b/tests/src/parity.rs index 1331f5a5..71402479 100644 --- a/tests/src/parity.rs +++ b/tests/src/parity.rs @@ -3,12 +3,13 @@ use crate::{input, labels, util}; pub fn test() { // 1795 is frame vanilla enters demo mode (gameMode = 5) + run_and_press_start(291, &[265, 270, 274, 286]); + run_and_press_start(291, &[264, 269, 273, 286]); + run_and_press_start(700, &[300, 400, 500, 600]); run_and_press_start(1794, &[]); run_and_press_start(1905, &[1794, 1800, 1900]); // fastest999999 pattern - run_and_press_start(291, &[265, 270, 274, 286]); - run_and_press_start(291, &[264, 269, 273, 286]); run_and_press_start(2000, &[265, 1000, 1500, 1900]); // other test cases From 027106b083042549094d13c2d942e20ae5b055c2 Mon Sep 17 00:00:00 2001 From: zohassadar Date: Tue, 10 Mar 2026 15:31:52 +0000 Subject: [PATCH 06/25] restore game menu --- src/gamemode/branch.asm | 2 +- src/gamemode/gametypemenu/menu.asm | 12 +- src/gamemode/gametypemenu/menudata.asm | 434 ++++++++++++++++++ src/gamemode/gametypemenu/menuram.asm | 37 ++ .../level_menu_sprite_replacements.asm | 21 + 5 files changed, 504 insertions(+), 2 deletions(-) create mode 100644 src/gamemode/gametypemenu/menudata.asm create mode 100644 src/gamemode/gametypemenu/menuram.asm create mode 100644 src/nametables/level_menu_sprite_replacements.asm diff --git a/src/gamemode/branch.asm b/src/gamemode/branch.asm index e46204c3..326d08af 100644 --- a/src/gamemode/branch.asm +++ b/src/gamemode/branch.asm @@ -3,7 +3,7 @@ branchOnGameMode: branchTo gameMode, \ gameMode0, \ gameMode1, \ - gameMode2, \ + gameMode_gameTypeMenu, \ gameMode_levelMenu, \ gameMode_playAndEndingHighScore_jmp, \ gameMode_playAndEndingHighScore_jmp, \ diff --git a/src/gamemode/gametypemenu/menu.asm b/src/gamemode/gametypemenu/menu.asm index f4fb67b1..eaeda36e 100644 --- a/src/gamemode/gametypemenu/menu.asm +++ b/src/gamemode/gametypemenu/menu.asm @@ -22,6 +22,15 @@ gameMode_gameTypeMenu: sta tmp3 jsr copyRleNametableToPpuOffset .addr game_type_menu_nametable_extra + + ; account for extra nametable loading time + inc frameCounter + bne :+ + inc frameCounter+1 +: + ldx #rng_seed + jsr generateNextPseudorandomNumber + .if INES_MAPPER <> 0 lda #CHRBankSet0 jsr changeCHRBanks @@ -29,7 +38,8 @@ gameMode_gameTypeMenu: lda #NMIEnable sta currentPpuCtrl jsr waitForVBlankAndEnableNmi - jsr updateAudioWaitForNmiAndResetOamStaging +; skip this wait, rng advanced manually above +; jsr updateAudioWaitForNmiAndResetOamStaging jsr updateAudioWaitForNmiAndEnablePpuRendering jsr updateAudioWaitForNmiAndResetOamStaging diff --git a/src/gamemode/gametypemenu/menudata.asm b/src/gamemode/gametypemenu/menudata.asm new file mode 100644 index 00000000..cf93b0ac --- /dev/null +++ b/src/gamemode/gametypemenu/menudata.asm @@ -0,0 +1,434 @@ +; generated by menu.js +; will be overwritten unless built with -M + +STRING_PAUSE = stringPause-stringTable +STRING_BLOCK = stringBlock-stringTable +STRING_CLEAR = stringClear-stringTable +STRING_SURE = stringSure-stringTable +STRING_CONFETTI = stringConfetti-stringTable + +.enum +MAIN_MENU +SUBMENU_OPTIONS +SUBMENU_TOURNAMENT +SUBMENU_BOARD +SUBMENU_CURSED +SUBMENU_SETTINGS +SUBMENU_DISPLAY +SUBMENU_ANYDAS +MENU_COUNT +.endenum + +.out .sprintf("%d/32 menus", MENU_COUNT) + + +.enum +CHOICESET_OFFON +CHOICESET_KS2FLOORINVIZHALT +CHOICESET_OFFLINESLEVEL +CHOICESET_OFFSHOWTOPCRASH +CHOICESET_CLASSILETTER7DIGITMCAPPEDHIDDEN +CHOICESET_OFFONNEONLITETEALOG +CHOICESET_VANILLPRIDE +CHOICESET_OFFHYDRANKITARU +CHOICESET_COUNT +.endenum + +.out .sprintf("%d/32 choicesets", CHOICESET_COUNT) + + +; index activeMenu +startPageByMenu: + .byte $00 ; main menu + .byte $01 ; options + .byte $02 ; tournament + .byte $03 ; board + .byte $04 ; cursed + .byte $05 ; settings + .byte $06 ; display + .byte $07 ; anydas + +pageCountByMenu: + .byte $01 ; main menu + .byte $01 ; options + .byte $01 ; tournament + .byte $01 ; board + .byte $01 ; cursed + .byte $01 ; settings + .byte $01 ; display + .byte $01 ; anydas + +; index activePage +pageTypes: + .byte PAGE_DEFAULT | MODE_TETRIS ; play tetris1 + .byte PAGE_DEFAULT | MODE_TETRIS ; options + .byte PAGE_DEFAULT | MODE_DEFAULT ; tournament + .byte PAGE_DEFAULT | MODE_DEFAULT ; board + .byte PAGE_DEFAULT | MODE_DEFAULT ; modifiers + .byte PAGE_DEFAULT | MODE_DEFAULT ; settings + .byte PAGE_DEFAULT | MODE_DEFAULT ; display + .byte PAGE_DEFAULT | MODE_DEFAULT ; anydas + +itemCountByPage: + .byte $01 ; play tetris1 + .byte $06 ; options + .byte $07 ; tournament + .byte $06 ; board + .byte $03 ; modifiers + .byte $08 ; settings + .byte $07 ; display + .byte $03 ; anydas + +pageLabelsHi: + .byte >pageLabelsPlayTetris1 ; play tetris1 + .byte >pageLabelsOptions ; options + .byte >pageLabelsTournament ; tournament + .byte >pageLabelsBoard ; board + .byte >pageLabelsModifiers ; modifiers + .byte >pageLabelsSettings ; settings + .byte >pageLabelsDisplay ; display + .byte >pageLabelsAnydas ; anydas + +pageLabelsLo: + .byte Date: Wed, 11 Mar 2026 11:32:23 +0000 Subject: [PATCH 07/25] sleeping not necessary here --- src/gamemode/branch.asm | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gamemode/branch.asm b/src/gamemode/branch.asm index 326d08af..57b8fa10 100644 --- a/src/gamemode/branch.asm +++ b/src/gamemode/branch.asm @@ -139,7 +139,6 @@ gameMode1: jsr updateAudioWaitForNmiAndEnablePpuRendering lda #$00 sta frameCounter+1 - dec sleepCounter gameMode1Loop: jsr stageBootSprites From 17439786c6fec0e534af67bf8d94b4ba55d91025 Mon Sep 17 00:00:00 2001 From: zohassadar Date: Wed, 11 Mar 2026 11:33:04 +0000 Subject: [PATCH 08/25] lua script compares against txt results --- tests/src/parity.rs | 3 +- tools/log.lua | 113 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 88 insertions(+), 28 deletions(-) diff --git a/tests/src/parity.rs b/tests/src/parity.rs index 71402479..cb0edaca 100644 --- a/tests/src/parity.rs +++ b/tests/src/parity.rs @@ -8,9 +8,8 @@ pub fn test() { run_and_press_start(700, &[300, 400, 500, 600]); run_and_press_start(1794, &[]); run_and_press_start(1905, &[1794, 1800, 1900]); - - // fastest999999 pattern run_and_press_start(2000, &[265, 1000, 1500, 1900]); + // fastest999999 pattern // other test cases } diff --git a/tools/log.lua b/tools/log.lua index 5c7409eb..af06e85b 100644 --- a/tools/log.lua +++ b/tools/log.lua @@ -1,23 +1,49 @@ --- line up with cycle_parity.rs indexing +-- line up with parity.rs indexing -- frameCount starts at 2 local OFFSET = -2 + -- local TEST_LENGTH = 1794 -- local START_FRAMES = {265, 270, 274, 286} -local TEST_LENGTH = 700 +local TEST_LENGTH = 1000 local START_FRAMES = {300, 400, 500, 600} -- local START_FRAMES = {} local BREAK_FRAME = nil +local COMPARE = true + local rom = emu.getRomInfo() local romPath, _ = string.gsub(rom.path, rom.name, "") local filename = rom.name:gsub("%.%w+$", "") .. ".log" local path = string.gsub(rom.path, rom.name, filename) + emu.log("Opening " .. path) +local original_results = {} +local file +if COMPARE then + local og_path = string.gsub(rom.path, rom.name, "clean.log") + for line in io.lines(og_path) do + line_nos = {} + for num in string.gmatch(line, "%S+") do + table.insert(line_nos, num) + end + emu.log(table.concat(line_nos, " ")) + table.insert( + original_results, + { + tonumber(line_nos[2], 16), + tonumber(line_nos[3], 16), + tonumber(line_nos[4], 16) + }) + end + emu.log("Comparing. Loaded " .. #original_results .. " lines") +end local file = io.open(path, "w") - -- workaround to get a global state local startIdx = {1} +local cmpIdx = {1} + + function logFrame() state = emu.getState() local frameCount = state.frameCount + OFFSET @@ -34,34 +60,69 @@ function logFrame() end end - local rng_addr = emu.getLabelAddress('rng_seed') - local rng_seed = emu.read16(rng_addr.address, rng_addr.memType) + local rng_seed = valueAtLabel16('rng_seed') + local frameCounter = valueAtLabel16('frameCounter') + local gameMode = valueAtLabel('gameMode') + local generalCounter = valueAtLabel('generalCounter') + local sleepCounter = valueAtLabel('sleepCounter') - local fc_addr = emu.getLabelAddress('frameCounter') - local frameCounter = emu.read16(fc_addr.address, fc_addr.memType) - - local gm_addr = emu.getLabelAddress('gameMode') - local gameMode = emu.read(gm_addr.address, gm_addr.memType) + if COMPARE then + if not original_results[cmpIdx[1]] then + emu.log("Ran out of frames to compare") + emu.breakExecution() + return + end + expected_rng = original_results[cmpIdx[1]][1] + expected_fc = original_results[cmpIdx[1]][2] + expected_gm = original_results[cmpIdx[1]][3] + cmpIdx[1] = cmpIdx[1] + 1 - local gc_addr = emu.getLabelAddress('generalCounter') - local generalCounter = emu.read(gc_addr.address, gc_addr.memType) + if ( + expected_rng ~= rng_seed or + expected_fc & 0xFF ~= frameCounter & 0xFF or + expected_gm ~= gameMode + ) then + emu.log( + "Expect: " + .. string.format("%04X", expected_rng) .. " " + .. string.format("%04X", expected_fc) .. " " + .. string.format("%02X", expected_gm) + ) + emu.log( + "Actual: " + .. string.format("%04X", rng_seed) .. " " + .. string.format("%04X", frameCounter) .. " " + .. string.format("%02X", gameMode) + ) + emu.breakExecution() + end + else - local sc_addr = emu.getLabelAddress('sleepCounter') - local sleepCounter = emu.read(sc_addr.address, sc_addr.memType) - file:write( - string.format("%04d", frameCount) .. " " - .. string.format("%04X", rng_seed) .. " " - .. string.format("%04X", frameCounter) .. " " - -- .. string.format("%02X", sleepCounter) .. " " - -- .. string.format("%02X", generalCounter) .. " " - .. string.format("%02X", gameMode) .. "\n" - ) + file:write( + string.format("%04d", frameCount) .. " " + .. string.format("%04X", rng_seed) .. " " + .. string.format("%04X", frameCounter) .. " " + -- .. string.format("%02X", sleepCounter) .. " " + -- .. string.format("%02X", generalCounter) .. " " + .. string.format("%02X", gameMode) .. "\n" + ) - if frameCount >= TEST_LENGTH then - file:close() - emu.breakExecution() - emu.log("Wrote " .. frameCount + 1 .. " lines to " .. path) + if frameCount >= TEST_LENGTH then + file:close() + emu.breakExecution() + emu.log("Wrote " .. frameCount + 1 .. " lines to " .. path) + end end end +function valueAtLabel(label) + local addr = emu.getLabelAddress(label) + return emu.read(addr.address, addr.memType) +end + +function valueAtLabel16(label) + local addr = emu.getLabelAddress(label) + return emu.read16(addr.address, addr.memType) +end + emu.addEventCallback(logFrame, emu.eventType.inputPolled); From beec6600237be611452387156e41a54b237fe068 Mon Sep 17 00:00:00 2001 From: zohassadar Date: Wed, 11 Mar 2026 11:39:38 +0000 Subject: [PATCH 09/25] these don't go here --- src/gamemode/gametypemenu/menudata.asm | 434 ------------------ src/gamemode/gametypemenu/menuram.asm | 37 -- .../level_menu_sprite_replacements.asm | 21 - 3 files changed, 492 deletions(-) delete mode 100644 src/gamemode/gametypemenu/menudata.asm delete mode 100644 src/gamemode/gametypemenu/menuram.asm delete mode 100644 src/nametables/level_menu_sprite_replacements.asm diff --git a/src/gamemode/gametypemenu/menudata.asm b/src/gamemode/gametypemenu/menudata.asm deleted file mode 100644 index cf93b0ac..00000000 --- a/src/gamemode/gametypemenu/menudata.asm +++ /dev/null @@ -1,434 +0,0 @@ -; generated by menu.js -; will be overwritten unless built with -M - -STRING_PAUSE = stringPause-stringTable -STRING_BLOCK = stringBlock-stringTable -STRING_CLEAR = stringClear-stringTable -STRING_SURE = stringSure-stringTable -STRING_CONFETTI = stringConfetti-stringTable - -.enum -MAIN_MENU -SUBMENU_OPTIONS -SUBMENU_TOURNAMENT -SUBMENU_BOARD -SUBMENU_CURSED -SUBMENU_SETTINGS -SUBMENU_DISPLAY -SUBMENU_ANYDAS -MENU_COUNT -.endenum - -.out .sprintf("%d/32 menus", MENU_COUNT) - - -.enum -CHOICESET_OFFON -CHOICESET_KS2FLOORINVIZHALT -CHOICESET_OFFLINESLEVEL -CHOICESET_OFFSHOWTOPCRASH -CHOICESET_CLASSILETTER7DIGITMCAPPEDHIDDEN -CHOICESET_OFFONNEONLITETEALOG -CHOICESET_VANILLPRIDE -CHOICESET_OFFHYDRANKITARU -CHOICESET_COUNT -.endenum - -.out .sprintf("%d/32 choicesets", CHOICESET_COUNT) - - -; index activeMenu -startPageByMenu: - .byte $00 ; main menu - .byte $01 ; options - .byte $02 ; tournament - .byte $03 ; board - .byte $04 ; cursed - .byte $05 ; settings - .byte $06 ; display - .byte $07 ; anydas - -pageCountByMenu: - .byte $01 ; main menu - .byte $01 ; options - .byte $01 ; tournament - .byte $01 ; board - .byte $01 ; cursed - .byte $01 ; settings - .byte $01 ; display - .byte $01 ; anydas - -; index activePage -pageTypes: - .byte PAGE_DEFAULT | MODE_TETRIS ; play tetris1 - .byte PAGE_DEFAULT | MODE_TETRIS ; options - .byte PAGE_DEFAULT | MODE_DEFAULT ; tournament - .byte PAGE_DEFAULT | MODE_DEFAULT ; board - .byte PAGE_DEFAULT | MODE_DEFAULT ; modifiers - .byte PAGE_DEFAULT | MODE_DEFAULT ; settings - .byte PAGE_DEFAULT | MODE_DEFAULT ; display - .byte PAGE_DEFAULT | MODE_DEFAULT ; anydas - -itemCountByPage: - .byte $01 ; play tetris1 - .byte $06 ; options - .byte $07 ; tournament - .byte $06 ; board - .byte $03 ; modifiers - .byte $08 ; settings - .byte $07 ; display - .byte $03 ; anydas - -pageLabelsHi: - .byte >pageLabelsPlayTetris1 ; play tetris1 - .byte >pageLabelsOptions ; options - .byte >pageLabelsTournament ; tournament - .byte >pageLabelsBoard ; board - .byte >pageLabelsModifiers ; modifiers - .byte >pageLabelsSettings ; settings - .byte >pageLabelsDisplay ; display - .byte >pageLabelsAnydas ; anydas - -pageLabelsLo: - .byte Date: Sat, 14 Mar 2026 22:18:36 +0000 Subject: [PATCH 10/25] setup longer test --- src/gamemode/branch.asm | 16 ++++++++++++++-- tests/src/parity.rs | 6 ++++-- tools/log.lua | 8 ++++---- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/gamemode/branch.asm b/src/gamemode/branch.asm index 57b8fa10..f31fba4e 100644 --- a/src/gamemode/branch.asm +++ b/src/gamemode/branch.asm @@ -3,7 +3,7 @@ branchOnGameMode: branchTo gameMode, \ gameMode0, \ gameMode1, \ - gameMode_gameTypeMenu, \ + gameMode2, \ gameMode_levelMenu, \ gameMode_playAndEndingHighScore_jmp, \ gameMode_playAndEndingHighScore_jmp, \ @@ -152,9 +152,12 @@ gameMode1Loop: ; go to game select menu page instead lda frameCounter+1 cmp #$05 - beq @goToGameMode2 bne gameMode1Loop + ; use this for now to signal demo start + lda #$01 + sta qualFlag + @goToGameMode2: inc gameMode rts @@ -199,6 +202,15 @@ gameMode2: jsr updateAudioWaitForNmiAndResetOamStaging jsr stageBootSprites jsr updateAudioWaitForNmiAndEnablePpuRendering + + + lda qualFlag + beq @skipDemoShuffle + ldx #rng_seed + jsr generateNextPseudorandomNumber + lda qualFlag ; temporary use to signal demo start +@skipDemoShuffle: + gameMode2Loop: lda newlyPressedButtons_player1 and #BUTTON_START diff --git a/tests/src/parity.rs b/tests/src/parity.rs index cb0edaca..65401117 100644 --- a/tests/src/parity.rs +++ b/tests/src/parity.rs @@ -2,8 +2,10 @@ use gag::Gag; use crate::{input, labels, util}; pub fn test() { - // 1795 is frame vanilla enters demo mode (gameMode = 5) + run_and_press_start(10000, &[265]); run_and_press_start(291, &[265, 270, 274, 286]); + run_and_press_start(5000, &[]); + // 1795 is frame vanilla enters demo mode (gameMode = 5) run_and_press_start(291, &[264, 269, 273, 286]); run_and_press_start(700, &[300, 400, 500, 600]); run_and_press_start(1794, &[]); @@ -91,7 +93,7 @@ fn run_and_press_start(test_length: usize, start_frames: &[usize]) { || (og_fc1 != gym_fc1) // this one doesn't matter (pretty sure anyway) // || (og_fc2 != gym_fc2) - || (og_gm != gym_gm) + // || (og_gm != gym_gm) { println!( "Expected:\n{:04} {:02X}{:02X} {:02X}{:02X} {:02X} {:02X} {:02X}", diff --git a/tools/log.lua b/tools/log.lua index af06e85b..625906bc 100644 --- a/tools/log.lua +++ b/tools/log.lua @@ -4,8 +4,8 @@ local OFFSET = -2 -- local TEST_LENGTH = 1794 -- local START_FRAMES = {265, 270, 274, 286} -local TEST_LENGTH = 1000 -local START_FRAMES = {300, 400, 500, 600} +local TEST_LENGTH = 50000 +local START_FRAMES = {} -- local START_FRAMES = {} local BREAK_FRAME = nil @@ -79,8 +79,8 @@ function logFrame() if ( expected_rng ~= rng_seed or - expected_fc & 0xFF ~= frameCounter & 0xFF or - expected_gm ~= gameMode + expected_fc & 0xFF ~= frameCounter & 0xFF -- or + -- expected_gm ~= gameMode ) then emu.log( "Expect: " From 3f3bd384ab8d4e2be38a1ebb39aafbbdf56da13f Mon Sep 17 00:00:00 2001 From: zohassadar Date: Sun, 15 Mar 2026 00:14:03 +0000 Subject: [PATCH 11/25] trim menus to see how much space is saved --- src/gamemode/branch.asm | 6 +- src/menu/menudata.asm | 434 +++++ src/menu/menuram.asm | 37 + src/nametables.asm | 8 +- .../level_menu_sprite_replacements.asm | 21 + src/words.asm | 1551 +++++++++++++++++ 6 files changed, 2050 insertions(+), 7 deletions(-) create mode 100644 src/menu/menudata.asm create mode 100644 src/menu/menuram.asm create mode 100644 src/nametables/level_menu_sprite_replacements.asm create mode 100644 src/words.asm diff --git a/src/gamemode/branch.asm b/src/gamemode/branch.asm index f31fba4e..50f6d08e 100644 --- a/src/gamemode/branch.asm +++ b/src/gamemode/branch.asm @@ -10,9 +10,9 @@ branchOnGameMode: gameMode_playAndEndingHighScore_jmp, \ gameMode_speedTest -.include "bootscreen.asm" -.include "waitscreen.asm" -.include "gametypemenu/menu.asm" +; .include "bootscreen.asm" +; .include "waitscreen.asm" +; .include "gametypemenu/menu.asm" .include "levelmenu.asm" gameMode0: diff --git a/src/menu/menudata.asm b/src/menu/menudata.asm new file mode 100644 index 00000000..cf93b0ac --- /dev/null +++ b/src/menu/menudata.asm @@ -0,0 +1,434 @@ +; generated by menu.js +; will be overwritten unless built with -M + +STRING_PAUSE = stringPause-stringTable +STRING_BLOCK = stringBlock-stringTable +STRING_CLEAR = stringClear-stringTable +STRING_SURE = stringSure-stringTable +STRING_CONFETTI = stringConfetti-stringTable + +.enum +MAIN_MENU +SUBMENU_OPTIONS +SUBMENU_TOURNAMENT +SUBMENU_BOARD +SUBMENU_CURSED +SUBMENU_SETTINGS +SUBMENU_DISPLAY +SUBMENU_ANYDAS +MENU_COUNT +.endenum + +.out .sprintf("%d/32 menus", MENU_COUNT) + + +.enum +CHOICESET_OFFON +CHOICESET_KS2FLOORINVIZHALT +CHOICESET_OFFLINESLEVEL +CHOICESET_OFFSHOWTOPCRASH +CHOICESET_CLASSILETTER7DIGITMCAPPEDHIDDEN +CHOICESET_OFFONNEONLITETEALOG +CHOICESET_VANILLPRIDE +CHOICESET_OFFHYDRANKITARU +CHOICESET_COUNT +.endenum + +.out .sprintf("%d/32 choicesets", CHOICESET_COUNT) + + +; index activeMenu +startPageByMenu: + .byte $00 ; main menu + .byte $01 ; options + .byte $02 ; tournament + .byte $03 ; board + .byte $04 ; cursed + .byte $05 ; settings + .byte $06 ; display + .byte $07 ; anydas + +pageCountByMenu: + .byte $01 ; main menu + .byte $01 ; options + .byte $01 ; tournament + .byte $01 ; board + .byte $01 ; cursed + .byte $01 ; settings + .byte $01 ; display + .byte $01 ; anydas + +; index activePage +pageTypes: + .byte PAGE_DEFAULT | MODE_TETRIS ; play tetris1 + .byte PAGE_DEFAULT | MODE_TETRIS ; options + .byte PAGE_DEFAULT | MODE_DEFAULT ; tournament + .byte PAGE_DEFAULT | MODE_DEFAULT ; board + .byte PAGE_DEFAULT | MODE_DEFAULT ; modifiers + .byte PAGE_DEFAULT | MODE_DEFAULT ; settings + .byte PAGE_DEFAULT | MODE_DEFAULT ; display + .byte PAGE_DEFAULT | MODE_DEFAULT ; anydas + +itemCountByPage: + .byte $01 ; play tetris1 + .byte $06 ; options + .byte $07 ; tournament + .byte $06 ; board + .byte $03 ; modifiers + .byte $08 ; settings + .byte $07 ; display + .byte $03 ; anydas + +pageLabelsHi: + .byte >pageLabelsPlayTetris1 ; play tetris1 + .byte >pageLabelsOptions ; options + .byte >pageLabelsTournament ; tournament + .byte >pageLabelsBoard ; board + .byte >pageLabelsModifiers ; modifiers + .byte >pageLabelsSettings ; settings + .byte >pageLabelsDisplay ; display + .byte >pageLabelsAnydas ; anydas + +pageLabelsLo: + .byte wordlist0 +.byte >wordlist1 +.byte >wordlist2 +.byte >wordlist3 +wordListsLo: +.byte Date: Sun, 15 Mar 2026 00:21:02 +0000 Subject: [PATCH 12/25] small tweak moved mult_orient.asm in main to the beginning of the romspace to prevent the necessary alignment from taking up any space. this also wraps a label offset in parentheses to prevent overflow when the label is within 10 bytes of a page boundary. --- src/main.asm | 8 +++++--- src/modes/crash.asm | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main.asm b/src/main.asm index 1fb4c13d..d8f008cf 100644 --- a/src/main.asm +++ b/src/main.asm @@ -17,7 +17,12 @@ .segment "PRG_chunk1": absolute +; consumes exactly 1 page +.assert <* = 0, error, "mult_orient needs to be at page boundary" +.include "data/mult_orient.asm" + ; region code at start of page to keep cycle count consistent +.assert <* = 0, error, "check_region needs to be at page boundary" .include "util/check_region.asm" .include "audio.asm" @@ -92,9 +97,6 @@ mainLoop: .include "modes/qtap.asm" .include "modes/garbage.asm" -.align $100 -; these tables benefit from page alignment -.include "data/mult_orient.asm" .segment "PRG_chunk3": absolute diff --git a/src/modes/crash.asm b/src/modes/crash.asm index 6bc3d8a9..67549844 100644 --- a/src/modes/crash.asm +++ b/src/modes/crash.asm @@ -221,7 +221,7 @@ testCrash: inc allegroIndex @newBit0: lda nmiReturnAddr - cmp # Date: Sun, 15 Mar 2026 01:14:10 +0000 Subject: [PATCH 13/25] fix tspins test --- tests/src/tspins.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/tspins.rs b/tests/src/tspins.rs index 6029f11e..098d2346 100644 --- a/tests/src/tspins.rs +++ b/tests/src/tspins.rs @@ -3,7 +3,7 @@ use crate::{util, labels, playfield}; pub fn test() { let mut emu = util::emulator(None); - for _ in 0..4 { + for _ in 0..6 { emu.run_until_vblank(); } From 60c717d67314c934528c0f485678ca50d3c20801 Mon Sep 17 00:00:00 2001 From: zohassadar Date: Mon, 16 Mar 2026 10:57:20 +0000 Subject: [PATCH 14/25] setup failing demo to title test --- src/gamemode/branch.asm | 48 +++++++++++++++++++++++++++++++++++++++-- tools/log.lua | 5 ++--- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/gamemode/branch.asm b/src/gamemode/branch.asm index 50f6d08e..166e8b16 100644 --- a/src/gamemode/branch.asm +++ b/src/gamemode/branch.asm @@ -6,7 +6,7 @@ branchOnGameMode: gameMode2, \ gameMode_levelMenu, \ gameMode_playAndEndingHighScore_jmp, \ - gameMode_playAndEndingHighScore_jmp, \ + gameMode2, \ gameMode_playAndEndingHighScore_jmp, \ gameMode_speedTest @@ -157,12 +157,31 @@ gameMode1Loop: ; use this for now to signal demo start lda #$01 sta qualFlag + lda #$5 + sta gameMode + rts @goToGameMode2: inc gameMode rts -gameMode2: + +gameMode2: ; also game mode 5 + ; check to see if really demo + lda gameMode + cmp #$5 + bne @actually2 + + @DEMO_FRAMES = 4759 + lda #>@DEMO_FRAMES + sta endingSleepCounter+1 + lda #<@DEMO_FRAMES + sta endingSleepCounter + + +@actually2: + + lda #$00 sta renderMode @@ -212,6 +231,31 @@ gameMode2: @skipDemoShuffle: gameMode2Loop: + ; check to see if really demo + lda gameMode + cmp #$5 + bne @still2 + + dec endingSleepCounter + bne :+ + dec endingSleepCounter+1 + bne :+ + +; end demo + lda #$2 + sta gameMode + + lda #rng_seed + jsr generateNextPseudorandomNumber + + lda #rng_seed + jsr generateNextPseudorandomNumber +: + + + +@still2: + lda newlyPressedButtons_player1 and #BUTTON_START bne @goToGameMode3 diff --git a/tools/log.lua b/tools/log.lua index 625906bc..bc64745c 100644 --- a/tools/log.lua +++ b/tools/log.lua @@ -4,8 +4,8 @@ local OFFSET = -2 -- local TEST_LENGTH = 1794 -- local START_FRAMES = {265, 270, 274, 286} -local TEST_LENGTH = 50000 -local START_FRAMES = {} +local TEST_LENGTH = 10000 +local START_FRAMES = {265} -- local START_FRAMES = {} local BREAK_FRAME = nil @@ -27,7 +27,6 @@ if COMPARE then for num in string.gmatch(line, "%S+") do table.insert(line_nos, num) end - emu.log(table.concat(line_nos, " ")) table.insert( original_results, { From 7fad09d4b1bbf5b7dee12a3705bed48598e23306 Mon Sep 17 00:00:00 2001 From: zohassadar Date: Mon, 16 Mar 2026 23:43:40 +0000 Subject: [PATCH 15/25] asdf --- src/menu/menudata.asm | 434 ------------ src/menu/menuram.asm | 37 - src/ram.asm | 2 + src/words.asm | 1551 ----------------------------------------- tests/Cargo.lock | 39 ++ tests/Cargo.toml | 1 + tests/rusty.out | 560 +++++++++++++++ tests/src/main.rs | 34 +- tests/src/parity.rs | 280 +++++--- tests/src/util.rs | 4 + 10 files changed, 826 insertions(+), 2116 deletions(-) delete mode 100644 src/menu/menudata.asm delete mode 100644 src/menu/menuram.asm delete mode 100644 src/words.asm create mode 100644 tests/rusty.out diff --git a/src/menu/menudata.asm b/src/menu/menudata.asm deleted file mode 100644 index cf93b0ac..00000000 --- a/src/menu/menudata.asm +++ /dev/null @@ -1,434 +0,0 @@ -; generated by menu.js -; will be overwritten unless built with -M - -STRING_PAUSE = stringPause-stringTable -STRING_BLOCK = stringBlock-stringTable -STRING_CLEAR = stringClear-stringTable -STRING_SURE = stringSure-stringTable -STRING_CONFETTI = stringConfetti-stringTable - -.enum -MAIN_MENU -SUBMENU_OPTIONS -SUBMENU_TOURNAMENT -SUBMENU_BOARD -SUBMENU_CURSED -SUBMENU_SETTINGS -SUBMENU_DISPLAY -SUBMENU_ANYDAS -MENU_COUNT -.endenum - -.out .sprintf("%d/32 menus", MENU_COUNT) - - -.enum -CHOICESET_OFFON -CHOICESET_KS2FLOORINVIZHALT -CHOICESET_OFFLINESLEVEL -CHOICESET_OFFSHOWTOPCRASH -CHOICESET_CLASSILETTER7DIGITMCAPPEDHIDDEN -CHOICESET_OFFONNEONLITETEALOG -CHOICESET_VANILLPRIDE -CHOICESET_OFFHYDRANKITARU -CHOICESET_COUNT -.endenum - -.out .sprintf("%d/32 choicesets", CHOICESET_COUNT) - - -; index activeMenu -startPageByMenu: - .byte $00 ; main menu - .byte $01 ; options - .byte $02 ; tournament - .byte $03 ; board - .byte $04 ; cursed - .byte $05 ; settings - .byte $06 ; display - .byte $07 ; anydas - -pageCountByMenu: - .byte $01 ; main menu - .byte $01 ; options - .byte $01 ; tournament - .byte $01 ; board - .byte $01 ; cursed - .byte $01 ; settings - .byte $01 ; display - .byte $01 ; anydas - -; index activePage -pageTypes: - .byte PAGE_DEFAULT | MODE_TETRIS ; play tetris1 - .byte PAGE_DEFAULT | MODE_TETRIS ; options - .byte PAGE_DEFAULT | MODE_DEFAULT ; tournament - .byte PAGE_DEFAULT | MODE_DEFAULT ; board - .byte PAGE_DEFAULT | MODE_DEFAULT ; modifiers - .byte PAGE_DEFAULT | MODE_DEFAULT ; settings - .byte PAGE_DEFAULT | MODE_DEFAULT ; display - .byte PAGE_DEFAULT | MODE_DEFAULT ; anydas - -itemCountByPage: - .byte $01 ; play tetris1 - .byte $06 ; options - .byte $07 ; tournament - .byte $06 ; board - .byte $03 ; modifiers - .byte $08 ; settings - .byte $07 ; display - .byte $03 ; anydas - -pageLabelsHi: - .byte >pageLabelsPlayTetris1 ; play tetris1 - .byte >pageLabelsOptions ; options - .byte >pageLabelsTournament ; tournament - .byte >pageLabelsBoard ; board - .byte >pageLabelsModifiers ; modifiers - .byte >pageLabelsSettings ; settings - .byte >pageLabelsDisplay ; display - .byte >pageLabelsAnydas ; anydas - -pageLabelsLo: - .byte wordlist0 -.byte >wordlist1 -.byte >wordlist2 -.byte >wordlist3 -wordListsLo: -.byte Result { u32::from_str_radix(s, 16) } +#[derive(Debug, Options)] +enum Command { + #[options(help = "run parity tests against vanilla rom")] + Parity(ParityOptions), +} + +#[derive(Debug, Options)] +struct ParityOptions { + #[options(help = ".fm2 file", free)] + tasfile: PathBuf, + #[options(help = "Write logfiles")] + write: bool, +} + #[derive(Debug, Options)] struct TestOptions { help: bool, @@ -53,12 +68,14 @@ struct TestOptions { #[options(help = "list drought mode probabilities")] drought_probs: bool, foo: bool, + #[options(command)] + command: Option, } fn main() { let options = TestOptions::parse_args_default_or_exit(); - let tests: [(&str, fn()); 18] = [ + let tests: [(&str, fn()); 17] = [ ("garbage4", garbage::test_garbage4_crash), ("floor", floor::test), ("tspins", tspins::test), @@ -76,9 +93,22 @@ fn main() { ("patch", patch::test), ("crunch", crunch::test), ("harddrop", harddrop::test), - ("parity", parity::test), ]; + match options.command { + None => {} + Some(Command::Parity(ref opts)) => { + if *opts.tasfile == *"" { + println!("tas file required!"); + return; + } + parity::compare( + &opts.tasfile, + &opts.write, + ) + } + } + // run tests if options.test { tests.iter().for_each(|(name, test)| { diff --git a/tests/src/parity.rs b/tests/src/parity.rs index 65401117..0d8cf793 100644 --- a/tests/src/parity.rs +++ b/tests/src/parity.rs @@ -1,46 +1,153 @@ +use regex::Regex; +use std::fs; +use std::path::PathBuf; +use rustico_core::nes::NesState; + use gag::Gag; -use crate::{input, labels, util}; - -pub fn test() { - run_and_press_start(10000, &[265]); - run_and_press_start(291, &[265, 270, 274, 286]); - run_and_press_start(5000, &[]); - // 1795 is frame vanilla enters demo mode (gameMode = 5) - run_and_press_start(291, &[264, 269, 273, 286]); - run_and_press_start(700, &[300, 400, 500, 600]); - run_and_press_start(1794, &[]); - run_and_press_start(1905, &[1794, 1800, 1900]); - run_and_press_start(2000, &[265, 1000, 1500, 1900]); - // fastest999999 pattern - - // other test cases + +use crate::{input, labels, util, video}; + +// const COMPARE_BYTES: usize = 5; + +fn get_buttons_from_tasfile(filename: &PathBuf) -> Vec { + let contents = fs::read_to_string(filename).expect("can't open tasfile"); + + // let reader = BufReader::new(file); + let mut tas_buttons: Vec = Vec::new(); + let tas_line = + // RLDUTSBA + Regex::new(r"(?m)^\|0\|([R.])([L.])([D.])([U.])([T.])([S.])([B.])([A.])\|\|\|").unwrap(); + + for (i, (_, [right, left, down, up, start, select, b, a])) in tas_line + .captures_iter(&contents) + .map(|c| c.extract()) + .enumerate() + { + if i < 2 { + continue; + } + let mut button: u8 = 0; + if right == "R" { + button |= 0x80 // input::RIGHT; // 0x01 + } + if left == "L" { + button |= 0x40 // input::LEFT; // 0x02 + } + if down == "D" { + button |= 0x20 // input::DOWN; // 0x04 + } + if up == "U" { + button |= 0x10 // input::UP; // 0x08 + } + if start == "T" { + button |= 0x08 // input::START; // 0x10 + } + if select == "S" { + button |= 0x04 // input::SELECT; // 0x20 + } + if b == "B" { + button |= 0x02 // input::B; // 0x40 + } + if a == "A" { + button |= 0x01 // input::A; // 0x80 + } + tas_buttons.push(button); + // println!( + // "{:?} {:?} {:?} {:?} {:?} {:?} {:?} {:?} {:08b}", + // right, left, down, up, start, select, a, b, button, + // ); + } + return tas_buttons; +} +// +// struct LoggedGameState { +// logs: Vec, +// state: usize, +// } +// +// impl LoggedGameState { +// fn new(filename: &Path) -> Self { +// let file = File::open(filename).unwrap(); +// let reader = BufReader::new(file); +// +// let mut values: Vec = Vec::new(); +// +// for line in reader.lines() { +// let line = line.expect("REASON"); +// line.split_whitespace() +// .map(|num| values.push(u8::from_str_radix(num, 16).unwrap())); +// +// println!("{:?}", values); +// } +// +// let abc: Vec = vec![1, 2, 3, 4, 5]; +// Self { +// logs: abc, +// state: 0, +// } +// } +// } +// +// impl Iterator for LoggedGameState { +// type Item = [u8; 5]; +// fn next(&mut self) -> Option<[u8; COMPARE_BYTES]> { +// if self.state > 100 { +// return None; +// } +// +// self.state += 1; +// +// Some([1, 2, 3, 4, 5]) +// } +// } + +pub fn compare(tasfile: &PathBuf, write: &bool) { + // println!("{:?} {:?}", tasfile, write,); + let tas_buttons = get_buttons_from_tasfile(tasfile); + // run_and_press_start(frames, &start_frames, &vanilla_log, &gym_log, &write); + run_and_press_start(&tas_buttons); } +static LABELS: &[&str] = &[ + "rng_seed", + "rng_seed_hi", + "frameCounter", + "frameCounterHi", + "sleepCounter", + "generalCounter", + "gameMode", +]; + +fn get_labels_from_emu(emu: &mut NesState) -> Vec { + + let mut result: Vec = Vec::new(); + for label in LABELS { + let addr = labels::get(label) as usize; + let value = emu.memory.iram_raw[addr]; + result.push(value) + } + + return result + +} + + -fn run_and_press_start(test_length: usize, start_frames: &[usize]) { - println!("{} {:?}", test_length, start_frames); - let rng_seed1 = labels::get("rng_seed") as usize; - let rng_seed2 = (labels::get("rng_seed") + 1) as usize; - let frame_counter1 = labels::get("frameCounter") as usize; - let frame_counter2 = (labels::get("frameCounter") + 1) as usize; - let sleep_counter = labels::get("sleepCounter") as usize; - let general_counter = labels::get("generalCounter") as usize; - let game_mode = labels::get("gameMode") as usize; - - let mut og_rng1: u8; - let mut og_rng2: u8; - let mut og_fc1: u8; - let mut og_fc2: u8; - let mut og_sc: u8; - let mut og_gc: u8; - let mut og_gm: u8; - - let mut gym_rng1: u8; - let mut gym_rng2: u8; - let mut gym_fc1: u8; - let mut gym_fc2: u8; - let mut gym_sc: u8; - let mut gym_gc: u8; - let mut gym_gm: u8; + +fn run_and_press_start( + tas_buttons: &Vec, + // test_length: usize, + // start_frames: &Vec, + // + // vanilla_log: &String, + // gym_log: &String, + // write: &bool, +) { + // if vanilla_log == "" { + // panic!("vanilla logfile is required") + // } + // + // println!("{} {:?}", test_length, start_frames); + // let mut og; let mut gym; @@ -50,60 +157,49 @@ fn run_and_press_start(test_length: usize, start_frames: &[usize]) { og = util::emulator(Some(util::OG_ROM)); gym = util::emulator(None); } - let mut start_idx = 0; - let mut unset_buttons = false; + let mut view = video::Video::new(); + let mut vview = video::Video::new(); - for i in 0..=test_length { - og.run_until_vblank(); - gym.run_until_vblank(); + for (i, buttons) in tas_buttons.iter().enumerate() { + // if i > 300 { + // println!("Pausing! Press enter to continue..."); + // + // let mut buffer = String::new(); + // + // std::io::stdin() + // .read_line(&mut buffer) + // .expect("Failed to read line"); + // return; + // } + let frame_no: usize = i; - if unset_buttons { - util::set_controller_raw(&mut og, 0); - util::set_controller_raw(&mut gym, 0); - unset_buttons = false; - } + let gym_labels = get_labels_from_emu(&mut gym); + let og_labels = get_labels_from_emu(&mut og); + println!("Gym: {:?}", gym_labels); + println!("OG: {:?}", og_labels); - if start_idx < start_frames.len() { - if i == start_frames[start_idx] { - start_idx += 1; - util::set_controller_raw(&mut og, input::START); - util::set_controller_raw(&mut gym, input::START); - unset_buttons = true; - } - } - - og_rng1 = og.memory.iram_raw[rng_seed1]; - og_rng2 = og.memory.iram_raw[rng_seed2]; - og_fc1 = og.memory.iram_raw[frame_counter1]; - og_fc2 = og.memory.iram_raw[frame_counter2]; - og_sc = og.memory.iram_raw[sleep_counter]; - og_gc = og.memory.iram_raw[general_counter]; - og_gm = og.memory.iram_raw[game_mode]; - - gym_rng1 = gym.memory.iram_raw[rng_seed1]; - gym_rng2 = gym.memory.iram_raw[rng_seed2]; - gym_fc1 = gym.memory.iram_raw[frame_counter1]; - gym_fc2 = gym.memory.iram_raw[frame_counter2]; - gym_sc = gym.memory.iram_raw[sleep_counter]; - gym_gc = og.memory.iram_raw[general_counter]; - gym_gm = gym.memory.iram_raw[game_mode]; - - if (og_rng1 != gym_rng1) - || (og_rng2 != gym_rng2) - || (og_fc1 != gym_fc1) - // this one doesn't matter (pretty sure anyway) - // || (og_fc2 != gym_fc2) - // || (og_gm != gym_gm) - { - println!( - "Expected:\n{:04} {:02X}{:02X} {:02X}{:02X} {:02X} {:02X} {:02X}", - i, og_rng2, og_rng1, og_fc2, og_fc1, og_sc, og_gc, og_gm, - ); - println!( - "Actual:\n{:04} {:02X}{:02X} {:02X}{:02X} {:02X} {:02X} {:02X}", - i, gym_rng2, gym_rng1, gym_fc2, gym_fc1, gym_sc, gym_gc, gym_gm, - ); - panic!("tools/log.lua to generate log"); - } + // println!( + // "{:02X} {:02X} {:02X} {:02X} {:02X}", + // og_rng2, og_rng1, og_fc2, og_fc1, og_gm, + // ); + // if (og_rng1 != gym_rng1) || (og_rng2 != gym_rng2) || (og_fc1 != gym_fc1) + // || gym_gm != og_gm + // { + // println!( + // "Expected: {:04} {:02X} {:02X} {:02X} {:02X} {:02X}", + // frame_no, og_rng2, og_rng1, og_fc2, og_fc1, og_gm, + // ); + // println!( + // "Actual: {:04} {:02X} {:02X} {:02X} {:02X} {:02X}", + // frame_no, gym_rng2, gym_rng1, gym_fc2, gym_fc1, gym_gm, + // ); + // panic!("tools/log.lua to generate log"); + // } + og.run_until_vblank(); + gym.run_until_vblank(); + util::set_controller_emu_native(&mut og, *buttons); + util::set_controller_emu_native(&mut gym, *buttons); + view.render(&mut gym); + vview.render(&mut og); } } diff --git a/tests/src/util.rs b/tests/src/util.rs index a27fe0de..279ed0dc 100644 --- a/tests/src/util.rs +++ b/tests/src/util.rs @@ -35,6 +35,10 @@ pub fn set_controller_raw(emu: &mut NesState, buttons: u8) { emu.p1_input = flipped_buttons; } +pub fn set_controller_emu_native(emu: &mut NesState, buttons: u8) { + emu.p1_input = buttons; +} + pub fn set_controller(emu: &mut NesState, button: char) { set_controller_raw(emu, match button { 'L' => input::LEFT, From 8c611faae69f41c81f1ce838a4916d346c7aeb2d Mon Sep 17 00:00:00 2001 From: zohassadar Date: Tue, 17 Mar 2026 19:58:56 +0000 Subject: [PATCH 16/25] this is neat --- gentas.py | 101 +++++++++++++ maketas.sh | 9 ++ tests/src/main.rs | 6 + tests/src/parity.rs | 361 ++++++++++++++++++++++++-------------------- tools/tas.lua | 77 +++++++++- 5 files changed, 390 insertions(+), 164 deletions(-) create mode 100644 gentas.py create mode 100755 maketas.sh diff --git a/gentas.py b/gentas.py new file mode 100644 index 00000000..0f388d32 --- /dev/null +++ b/gentas.py @@ -0,0 +1,101 @@ +import argparse +import logging + +logger = logging.getLogger(__name__) +OFFSET=2 +INPUT_LETTERS = "RLDUTSBA" +INPUT_VALUES = dict( + right=0x80, + left=0x40, + down=0x20, + up=0x10, + start=0x08, + select=0x04, + b=0x02, + a=0x01, +) + + +# TASLINE = "|0|........|||" +def get_tas_line(input_byte: int): + result = [] + result.extend("|0|") + binary = f"{input_byte & 0xFF:08b}" + for i, char in enumerate(binary): + result.append(INPUT_LETTERS[i] if char == "1" else ".") + result.extend("|||") + return "".join(result) + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("frames", type=int, help="length of tas") + parser.add_argument( + "-R", + "--right", + type=int, + nargs="+", + default=[], + ) + parser.add_argument( + "-L", + "--left", + type=int, + nargs="+", + default=[], + ) + parser.add_argument( + "-D", + "--down", + type=int, + nargs="+", + default=[], + ) + parser.add_argument( + "-U", + "--up", + type=int, + nargs="+", + default=[], + ) + parser.add_argument( + "-T", + "--start", + type=int, + nargs="+", + default=[], + ) + parser.add_argument( + "-S", + "--select", + type=int, + nargs="+", + default=[], + ) + parser.add_argument( + "-B", + "--b", + type=int, + nargs="+", + default=[], + ) + parser.add_argument( + "-A", + "--a", + type=int, + nargs="+", + default=[], + ) + args = parser.parse_args() + + output = bytearray(args.frames) + for label, value in INPUT_VALUES.items(): + for frame in vars(args)[label]: + # if frame < OFFSET: + # raise RuntimeError(f"{frame} needs to be less than {OFFSET}") + output[frame+OFFSET] |= value + + print('\n'.join(get_tas_line(f) for f in output)) + + +if __name__ == "__main__": + main() diff --git a/maketas.sh b/maketas.sh new file mode 100755 index 00000000..16866393 --- /dev/null +++ b/maketas.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +python gentas.py 1400 \ + -T 265 273 290 310 1300 1310 \ + -R 302 304 306 308 {700..1000..2} \ + -D 303 \ + -A 309 310 {401..1000..2} \ + -L {401..699} \ + && : diff --git a/tests/src/main.rs b/tests/src/main.rs index 4debe35c..df88c6ca 100644 --- a/tests/src/main.rs +++ b/tests/src/main.rs @@ -46,6 +46,10 @@ struct ParityOptions { tasfile: PathBuf, #[options(help = "Write logfiles")] write: bool, + #[options(help = "Be printy")] + verbose: bool, + #[options(help = "Show video")] + render: bool, } #[derive(Debug, Options)] @@ -105,6 +109,8 @@ fn main() { parity::compare( &opts.tasfile, &opts.write, + &opts.verbose, + &opts.render, ) } } diff --git a/tests/src/parity.rs b/tests/src/parity.rs index 0d8cf793..9761568a 100644 --- a/tests/src/parity.rs +++ b/tests/src/parity.rs @@ -1,16 +1,192 @@ use regex::Regex; -use std::fs; -use std::path::PathBuf; use rustico_core::nes::NesState; +use std::fs::read_to_string; +use std::fs::File; +use std::io::BufWriter; +use std::io::Result; +use std::io::Write; +use std::path::PathBuf; use gag::Gag; -use crate::{input, labels, util, video}; +use crate::labels; +use crate::util; +use crate::video; + +static EXTRA_FRAMES: usize = 10; +static FAILURE_LOG_FRAMES: usize = 100; +static COMPARE_BYTES: &[&str] = &[ + "rng_seed", + "rng_seed_hi", + "frameCounter", + "frameCounterHi", + "gameMode", + "autorepeatX", + "spawnCount", +]; + +pub fn compare(tasfile: &PathBuf, write: &bool, verbose: &bool, render: &bool) { + // println!("{:?} {:?}", tasfile, write,); + let tas_buttons = get_buttons_from_tasfile(tasfile, verbose); + + if *write { + let (vanilla, gym) = run_tas_and_compare(&tas_buttons, verbose, &render); + let _ = write_bytes_to_file(&get_new_filename(tasfile, "clean"), &vanilla); + let _ = write_bytes_to_file(&get_new_filename(tasfile, "gym"), &gym); + } else { + let vanilla_logfile = get_new_filename(tasfile, "clean"); + let compare_bytes: Vec = read_bytes_from_file(&vanilla_logfile); + compare_with_vanilla(&tas_buttons, &compare_bytes, &verbose, &render); + } +} + +struct OptionalVideo { + video: Option, +} + +impl OptionalVideo { + fn new(render: &bool) -> Self { + let video = if *render { + Some(video::Video::new()) + } else { + None + }; + + Self { video } + } + fn set_position(&mut self, x: isize, y: isize) { + if let Some(video) = &mut self.video { + video.window.set_position(x, y); + } + } + fn render(&mut self, emu: &mut rustico_core::nes::NesState) { + if let Some(video) = &mut self.video { + video.render(emu); + } + } +} + +fn read_bytes_from_file(filename: &PathBuf) -> Vec { + let mut result = Vec::new(); + + for line in read_to_string(filename).unwrap().lines() { + for byte in line.split_whitespace() { + result.push(u8::from_str_radix(byte, 16).unwrap()) + } + } + + result +} + +fn write_bytes_to_file(filename: &PathBuf, bytes: &Vec) -> Result<()> { + let file = File::create(filename)?; + let mut writer = BufWriter::new(file); + + for chunk in bytes.chunks(COMPARE_BYTES.len()) { + for (i, b) in chunk.iter().enumerate() { + if i > 0 { + write!(writer, " ")?; + } + write!(writer, "{:02X}", b)?; + } + writeln!(writer)?; + } + Ok(()) +} + +fn get_new_filename(filename: &PathBuf, suffix: &str) -> PathBuf { + let stem = filename.file_stem().unwrap(); + let mut name = stem.to_os_string(); + name.push(format!("-{}.log", suffix)); + filename.with_file_name(name) +} + +fn extract_values_from_labels(emu: &mut NesState) -> Vec { + let mut result: Vec = Vec::new(); + for label in COMPARE_BYTES { + let addr = labels::get(label) as usize; + let value = emu.memory.iram_raw[addr]; + result.push(value) + } + + return result; +} + +fn compare_with_vanilla( + tas_buttons: &Vec, + compare_bytes: &Vec, + verbose: &bool, + render: &bool, +) { + let mut gym; + { + let _print_gag = Gag::stdout().unwrap(); + gym = util::emulator(None); + } + let mut ptr = 0; + let mut view = OptionalVideo::new(render); + for (i, buttons) in tas_buttons.into_iter().enumerate() { + let expected = &compare_bytes[ptr..ptr + COMPARE_BYTES.len()]; + ptr += COMPARE_BYTES.len(); + let values = extract_values_from_labels(&mut gym); + if expected != values { + println!("Gym: {:?}", values); + println!("Vog: {:?}", expected); + panic!("Mismatch on line {}!", i + 1); + } + + gym.run_until_vblank(); + util::set_controller_emu_native(&mut gym, *buttons); + view.render(&mut gym); + } +} +fn run_tas_and_compare(tas_buttons: &Vec, verbose: &bool, render: &bool) -> (Vec, Vec) { + let mut og; + let mut gym; + { + // suppress rustico's output when loading roms + let _print_gag = Gag::stdout().unwrap(); + og = util::emulator(Some(util::OG_ROM)); + gym = util::emulator(None); + } + let mut og_bytes = Vec::new(); + let mut gym_bytes = Vec::new(); + let mut vanilla_view = OptionalVideo::new(render); + let mut gym_view = OptionalVideo::new(render); + let mut fail_frames: usize = 0; + gym_view.set_position(512, 30); + for buttons in tas_buttons.iter() { + if fail_frames > FAILURE_LOG_FRAMES { + break; + } + let gym_values = extract_values_from_labels(&mut gym); + gym_bytes.extend(gym_values.clone()); + + let og_values = extract_values_from_labels(&mut og); + og_bytes.extend(og_values.clone()); + + if gym_values.as_slice() != og_values.as_slice() { + fail_frames += 1; + } + + if *verbose { + println!("OG: {:?}", og_values); + println!("Gym: {:?}", gym_values); + } + + og.run_until_vblank(); + gym.run_until_vblank(); + util::set_controller_emu_native(&mut og, *buttons); + util::set_controller_emu_native(&mut gym, *buttons); + vanilla_view.render(&mut og); + gym_view.render(&mut gym); + } -// const COMPARE_BYTES: usize = 5; + return (og_bytes, gym_bytes); +} -fn get_buttons_from_tasfile(filename: &PathBuf) -> Vec { - let contents = fs::read_to_string(filename).expect("can't open tasfile"); +fn get_buttons_from_tasfile(filename: &PathBuf, verbose: &bool) -> Vec { + let contents = read_to_string(filename).expect("can't open tasfile"); // let reader = BufReader::new(file); let mut tas_buttons: Vec = Vec::new(); @@ -23,183 +199,46 @@ fn get_buttons_from_tasfile(filename: &PathBuf) -> Vec { .map(|c| c.extract()) .enumerate() { + // helps line things up if i < 2 { continue; } let mut button: u8 = 0; if right == "R" { - button |= 0x80 // input::RIGHT; // 0x01 + button |= 0x80 // input::RIGHT 0x01 } if left == "L" { - button |= 0x40 // input::LEFT; // 0x02 + button |= 0x40 // input::LEFT 0x02 } if down == "D" { - button |= 0x20 // input::DOWN; // 0x04 + button |= 0x20 // input::DOWN 0x04 } if up == "U" { - button |= 0x10 // input::UP; // 0x08 + button |= 0x10 // input::UP 0x08 } if start == "T" { - button |= 0x08 // input::START; // 0x10 + button |= 0x08 // input::START 0x10 } if select == "S" { - button |= 0x04 // input::SELECT; // 0x20 + button |= 0x04 // input::SELECT 0x20 } if b == "B" { - button |= 0x02 // input::B; // 0x40 + button |= 0x02 // input::B 0x40 } if a == "A" { - button |= 0x01 // input::A; // 0x80 + button |= 0x01 // input::A 0x80 } tas_buttons.push(button); - // println!( - // "{:?} {:?} {:?} {:?} {:?} {:?} {:?} {:?} {:08b}", - // right, left, down, up, start, select, a, b, button, - // ); - } - return tas_buttons; -} -// -// struct LoggedGameState { -// logs: Vec, -// state: usize, -// } -// -// impl LoggedGameState { -// fn new(filename: &Path) -> Self { -// let file = File::open(filename).unwrap(); -// let reader = BufReader::new(file); -// -// let mut values: Vec = Vec::new(); -// -// for line in reader.lines() { -// let line = line.expect("REASON"); -// line.split_whitespace() -// .map(|num| values.push(u8::from_str_radix(num, 16).unwrap())); -// -// println!("{:?}", values); -// } -// -// let abc: Vec = vec![1, 2, 3, 4, 5]; -// Self { -// logs: abc, -// state: 0, -// } -// } -// } -// -// impl Iterator for LoggedGameState { -// type Item = [u8; 5]; -// fn next(&mut self) -> Option<[u8; COMPARE_BYTES]> { -// if self.state > 100 { -// return None; -// } -// -// self.state += 1; -// -// Some([1, 2, 3, 4, 5]) -// } -// } - -pub fn compare(tasfile: &PathBuf, write: &bool) { - // println!("{:?} {:?}", tasfile, write,); - let tas_buttons = get_buttons_from_tasfile(tasfile); - // run_and_press_start(frames, &start_frames, &vanilla_log, &gym_log, &write); - run_and_press_start(&tas_buttons); -} -static LABELS: &[&str] = &[ - "rng_seed", - "rng_seed_hi", - "frameCounter", - "frameCounterHi", - "sleepCounter", - "generalCounter", - "gameMode", -]; -fn get_labels_from_emu(emu: &mut NesState) -> Vec { - - let mut result: Vec = Vec::new(); - for label in LABELS { - let addr = labels::get(label) as usize; - let value = emu.memory.iram_raw[addr]; - result.push(value) - } - - return result - -} - - - - -fn run_and_press_start( - tas_buttons: &Vec, - // test_length: usize, - // start_frames: &Vec, - // - // vanilla_log: &String, - // gym_log: &String, - // write: &bool, -) { - // if vanilla_log == "" { - // panic!("vanilla logfile is required") - // } - // - // println!("{} {:?}", test_length, start_frames); - // - - let mut og; - let mut gym; - { - // suppress rustico's output when loading roms - let print_gag = Gag::stdout().unwrap(); - og = util::emulator(Some(util::OG_ROM)); - gym = util::emulator(None); + if *verbose { + eprintln!( + "{:?} {:?} {:?} {:?} {:?} {:?} {:?} {:?} {:08b}", + right, left, down, up, start, select, a, b, button, + ); + } } - let mut view = video::Video::new(); - let mut vview = video::Video::new(); - - for (i, buttons) in tas_buttons.iter().enumerate() { - // if i > 300 { - // println!("Pausing! Press enter to continue..."); - // - // let mut buffer = String::new(); - // - // std::io::stdin() - // .read_line(&mut buffer) - // .expect("Failed to read line"); - // return; - // } - let frame_no: usize = i; - - let gym_labels = get_labels_from_emu(&mut gym); - let og_labels = get_labels_from_emu(&mut og); - println!("Gym: {:?}", gym_labels); - println!("OG: {:?}", og_labels); - - // println!( - // "{:02X} {:02X} {:02X} {:02X} {:02X}", - // og_rng2, og_rng1, og_fc2, og_fc1, og_gm, - // ); - // if (og_rng1 != gym_rng1) || (og_rng2 != gym_rng2) || (og_fc1 != gym_fc1) - // || gym_gm != og_gm - // { - // println!( - // "Expected: {:04} {:02X} {:02X} {:02X} {:02X} {:02X}", - // frame_no, og_rng2, og_rng1, og_fc2, og_fc1, og_gm, - // ); - // println!( - // "Actual: {:04} {:02X} {:02X} {:02X} {:02X} {:02X}", - // frame_no, gym_rng2, gym_rng1, gym_fc2, gym_fc1, gym_gm, - // ); - // panic!("tools/log.lua to generate log"); - // } - og.run_until_vblank(); - gym.run_until_vblank(); - util::set_controller_emu_native(&mut og, *buttons); - util::set_controller_emu_native(&mut gym, *buttons); - view.render(&mut gym); - vview.render(&mut og); + for _ in 0..EXTRA_FRAMES { + tas_buttons.push(0); } + return tas_buttons; } diff --git a/tools/tas.lua b/tools/tas.lua index 5e5c8281..8a275eca 100644 --- a/tools/tas.lua +++ b/tools/tas.lua @@ -1,10 +1,26 @@ -- https://tasvideos.org/7625S +-- local TASFILE = "tases/r57shell-Archanfel-Tetris-fastest999999.fm2" +local TASFILE = "heh.fm2" + local rom = emu.getRomInfo() local romPath, _ = string.gsub(rom.path, rom.name, "") -local filename = romPath .. "r57shell-Archanfel-Tetris-fastest999999.fm2" +local tasFile = romPath .. TASFILE +local compareFile = tasFile:gsub("%.%w+$", "") .. "-clean.log" + +local COMPARE_BYTES = { + "rng_seed", + "rng_seed_hi", + "frameCounter", + "frameCounterHi", + "gameMode", + "autorepeatX", + "spawnCount", +} + +local cmpIdx = {1 + #COMPARE_BYTES} -- skip a frame to line things up local inputs = {} -for line in io.lines(filename) do +for line in io.lines(tasFile) do local buttons = line:match("^|0|(........)|||$") if buttons then local input = {} @@ -24,16 +40,71 @@ for line in io.lines(filename) do table.insert(inputs, input) end end + +-- extra input to force extra frame (so last input is processed) table.insert(inputs, {}) +local compareBytes = {} +for line in io.lines(compareFile) do + for num in string.gmatch(line, "%S+") do + table.insert(compareBytes, tonumber(num, 16)) + end +end + + function applyInputs() local state = emu.getState() - local input = inputs[state["frameCount"]+1] + local input = inputs[state["frameCount"] + 1] if not input then emu.breakExecution() + return else emu.setInput(input) end + local expect = slice(compareBytes, cmpIdx[1], cmpIdx[1] + #COMPARE_BYTES) + local actual = getValuesFromLabels() + + local expectStr = getHexString(expect) + local actualStr = getHexString(actual) + if expectStr ~= actualStr then + emu.log("Expect: " .. expectStr) + emu.log("Actual: " .. actualStr) + emu.breakExecution() + end + cmpIdx[1] = cmpIdx[1] + #COMPARE_BYTES +end + +function getHexString(numbers) + result = {} + for _, number in ipairs(numbers) do + table.insert(result, string.format("%02X", number)) + end + return table.concat(result, " ") + +end + +function slice(values, i, n) + local result = {} + local idx = i + while idx < n do + table.insert(result, values[idx]) + idx = idx + 1 + end + return result +end + +function getValuesFromLabels() + local result = {} + for _, label in ipairs(COMPARE_BYTES) do + local value = valueAtLabel(label) + table.insert(result, value ) + end + return result +end + +function valueAtLabel(label) + local addr = emu.getLabelAddress(label) + return emu.read(addr.address, addr.memType) end emu.addEventCallback(applyInputs, emu.eventType.inputPolled); From b16757294d5d5781c5c1009af9253817906a5bdd Mon Sep 17 00:00:00 2001 From: zohassadar Date: Tue, 17 Mar 2026 20:00:31 +0000 Subject: [PATCH 17/25] crap --- tests/rusty.out | 560 ------------------------------------------------ 1 file changed, 560 deletions(-) delete mode 100644 tests/rusty.out diff --git a/tests/rusty.out b/tests/rusty.out deleted file mode 100644 index 449c9f0c..00000000 --- a/tests/rusty.out +++ /dev/null @@ -1,560 +0,0 @@ -00 00 00 00 00 -00 00 00 00 00 -00 00 00 00 00 -88 89 00 00 00 -C4 44 00 01 00 -C4 44 00 01 00 -62 22 00 02 00 -31 11 00 03 00 -98 08 00 04 00 -4C 04 00 05 00 -26 02 00 06 00 -13 01 00 07 00 -89 80 00 08 00 -44 40 00 09 00 -22 20 00 0A 00 -11 90 00 0B 00 -08 48 00 0C 00 -04 24 00 0D 00 -02 12 00 0E 00 -01 09 00 0F 00 -80 04 00 10 00 -40 02 00 11 00 -20 81 00 12 00 -90 40 00 13 00 -48 20 00 14 00 -24 10 00 15 00 -12 08 00 16 00 -09 84 00 17 00 -04 42 00 18 00 -02 A1 00 19 00 -81 D0 00 1A 00 -40 68 00 1B 00 -20 34 00 1C 00 -10 1A 00 1D 00 -08 8D 00 1E 00 -84 46 00 1F 00 -42 A3 00 20 00 -A1 51 00 21 00 -D0 28 00 22 00 -68 14 00 23 00 -34 0A 00 24 00 -1A 85 00 25 00 -8D C2 00 26 00 -46 E1 00 27 00 -A3 F0 00 28 00 -51 F8 00 29 00 -28 7C 00 2A 00 -14 3E 00 2B 00 -0A 9F 00 2C 00 -85 4F 00 2D 00 -C2 A7 00 2E 00 -E1 53 00 2F 00 -F0 A9 00 30 00 -F8 54 00 31 00 -7C 2A 00 32 00 -3E 95 00 33 00 -9F CA 00 34 00 -4F 65 00 35 00 -A7 B2 00 36 00 -53 59 00 37 00 -A9 AC 00 38 00 -54 56 00 39 00 -2A AB 00 3A 00 -95 55 00 3B 00 -CA 2A 00 3C 00 -65 15 00 3D 00 -B2 0A 00 3E 00 -59 05 00 3F 00 -AC 02 00 40 00 -56 81 00 41 00 -AB C0 00 42 00 -55 E0 00 43 00 -2A 70 00 44 00 -15 B8 00 45 00 -0A 5C 00 46 00 -05 AE 00 47 00 -02 D7 00 48 00 -81 6B 00 49 00 -C0 B5 00 4A 00 -E0 5A 00 4B 00 -70 AD 00 4C 00 -B8 56 00 4D 00 -5C AB 00 4E 00 -AE D5 00 4F 00 -D7 EA 00 50 00 -6B 75 00 51 00 -B5 BA 00 52 00 -5A DD 00 53 00 -AD EE 00 54 00 -56 F7 00 55 00 -AB 7B 00 56 00 -D5 3D 00 57 00 -EA 1E 00 58 00 -75 0F 00 59 00 -BA 87 00 5A 00 -DD 43 00 5B 00 -EE A1 00 5C 00 -F7 D0 00 5D 00 -7B E8 00 5E 00 -3D F4 00 5F 00 -1E 7A 00 60 00 -0F 3D 00 61 00 -87 9E 00 62 00 -43 4F 00 63 00 -A1 27 00 64 00 -D0 93 00 65 00 -E8 C9 00 66 00 -F4 64 00 67 00 -7A 32 00 68 00 -3D 19 00 69 00 -9E 0C 00 6A 00 -4F 86 00 6B 00 -27 43 00 6C 00 -93 21 00 6D 00 -C9 90 00 6E 00 -64 48 00 6F 00 -32 24 00 70 00 -19 92 00 71 00 -0C C9 00 72 00 -86 64 00 73 00 -43 B2 00 74 00 -21 59 00 75 00 -90 2C 00 76 00 -48 16 00 77 00 -24 8B 00 78 00 -92 C5 00 79 00 -C9 E2 00 7A 00 -64 F1 00 7B 00 -B2 78 00 7C 00 -59 BC 00 7D 00 -2C 5E 00 7E 00 -16 AF 00 7F 00 -8B 57 00 80 00 -C5 2B 00 81 00 -E2 95 00 82 00 -F1 CA 00 83 00 -78 E5 00 84 00 -BC 72 00 85 00 -5E B9 00 86 00 -AF DC 00 87 00 -57 EE 00 88 00 -2B 77 00 89 00 -95 3B 00 8A 00 -CA 9D 00 8B 00 -E5 CE 00 8C 00 -72 E7 00 8D 00 -B9 73 00 8E 00 -DC B9 00 8F 00 -EE 5C 00 90 00 -77 AE 00 91 00 -3B 57 00 92 00 -9D 2B 00 93 00 -CE 95 00 94 00 -E7 CA 00 95 00 -73 65 00 96 00 -B9 B2 00 97 00 -5C D9 00 98 00 -AE 6C 00 99 00 -57 B6 00 9A 00 -2B 5B 00 9B 00 -95 2D 00 9C 00 -CA 16 00 9D 00 -65 0B 00 9E 00 -B2 85 00 9F 00 -D9 C2 00 A0 00 -6C E1 00 A1 00 -B6 70 00 A2 00 -5B B8 00 A3 00 -2D DC 00 A4 00 -16 6E 00 A5 00 -0B 37 00 A6 00 -85 1B 00 A7 00 -C2 8D 00 A8 00 -E1 C6 00 A9 00 -70 E3 00 AA 00 -B8 F1 00 AB 00 -DC 78 00 AC 00 -6E 3C 00 AD 00 -37 9E 00 AE 00 -1B 4F 00 AF 00 -8D 27 00 B0 00 -C6 93 00 B1 00 -E3 49 00 B2 00 -F1 A4 00 B3 00 -78 52 00 B4 00 -3C A9 00 B5 00 -9E 54 00 B6 00 -4F AA 00 B7 00 -27 55 00 B8 00 -93 AA 00 B9 00 -49 55 00 BA 00 -A4 2A 00 BB 00 -52 95 00 BC 00 -A9 CA 00 BD 00 -54 E5 00 BE 00 -AA 72 00 BF 00 -55 39 00 C0 00 -AA 1C 00 C1 00 -55 8E 00 C2 00 -2A C7 00 C3 00 -95 63 00 C4 00 -CA B1 00 C5 00 -E5 D8 00 C6 00 -72 6C 00 C7 00 -39 B6 00 C8 00 -1C DB 00 C9 00 -8E ED 00 CA 00 -C7 F6 00 CB 00 -63 7B 00 CC 00 -B1 3D 00 CD 00 -D8 1E 00 CE 00 -6C 8F 00 CF 00 -B6 C7 00 D0 00 -DB 63 00 D1 00 -ED 31 00 D2 00 -F6 18 00 D3 00 -7B 8C 00 D4 00 -3D C6 00 D5 00 -1E E3 00 D6 00 -8F 71 00 D7 00 -C7 B8 00 D8 00 -63 DC 00 D9 00 -31 EE 00 DA 00 -18 F7 00 DB 00 -8C FB 00 DC 00 -C6 FD 00 DD 00 -E3 FE 00 DE 00 -71 7F 00 DF 00 -B8 BF 00 E0 00 -DC DF 00 E1 00 -EE EF 00 E2 00 -F7 77 00 E3 00 -FB 3B 00 E4 00 -FD 1D 00 E5 00 -FE 0E 00 E6 00 -7F 07 00 E7 00 -BF 03 00 E8 00 -DF 01 00 E9 00 -EF 80 00 EA 00 -77 C0 00 EB 00 -3B E0 00 EC 00 -1D F0 00 ED 00 -0E 78 00 EE 00 -07 BC 00 EF 00 -03 DE 00 F0 00 -01 6F 00 F1 00 -80 B7 00 F2 00 -C0 DB 00 F3 00 -E0 ED 00 F4 00 -F0 76 00 F5 00 -78 BB 00 F6 00 -BC DD 00 F7 00 -DE 6E 00 F8 00 -6F 37 00 F9 00 -B7 1B 00 FA 00 -DB 0D 00 FB 00 -ED 86 00 FC 00 -76 C3 00 FD 00 -BB 61 00 FE 00 -DD B0 00 FF 00 -6E 58 01 00 00 -37 AC 01 01 00 -1B D6 01 02 00 -0D 6B 01 03 00 -86 B5 01 04 00 -C3 DA 01 05 00 -61 6D 01 06 00 -B0 36 01 07 01 -58 9B 01 08 01 -AC CD 01 09 01 -D6 66 01 0A 01 -6B 33 00 0B 01 -B5 19 00 0C 02 -DA 0C 00 0D 02 -6D 86 00 0E 02 -36 C3 00 0F 02 -9B 61 00 10 03 -CD B0 00 11 03 -CD B0 00 11 03 -66 58 00 12 03 -33 AC 00 13 03 -D6 E7 00 14 03 -3E C7 00 15 03 -E7 98 00 16 03 -1C F3 00 17 03 -7F 27 00 18 03 -EF 84 00 19 03 -9D B0 00 1A 03 -4E 58 00 1B 04 -27 AC 00 1C 04 -13 D6 00 1D 04 -09 6B 00 1E 04 -C2 5A 00 1F 04 -61 2D 00 20 04 -B0 16 00 21 04 -58 8B 00 22 04 -AC C5 00 23 04 -D6 62 00 24 04 -6B 31 00 25 04 -B5 98 00 26 04 -5A 4C 00 27 04 -2D A6 00 28 04 -16 D3 00 29 04 -8B 69 00 2A 04 -C5 B4 00 2B 04 -62 5A 00 2C 04 -31 2D 00 2D 04 -98 16 00 2E 04 -4C 8B 00 2F 04 -A6 C5 00 30 04 -D3 E2 00 31 04 -69 71 00 32 04 -B4 38 00 33 04 -5A 1C 00 34 04 -2D 8E 00 35 04 -16 C7 00 36 04 -8B 63 00 37 04 -C5 31 00 38 04 -E2 18 00 39 04 -71 8C 00 3A 04 -38 46 00 3B 04 -1C A3 00 3C 04 -8E D1 00 3D 04 -C7 E8 00 3E 04 -63 F4 00 3F 04 -31 FA 00 40 04 -18 FD 00 41 04 -8C 7E 00 42 04 -46 BF 00 43 04 -A3 5F 00 44 04 -D1 2F 00 45 04 -E8 97 00 46 04 -F4 CB 00 47 04 -FA E5 00 48 04 -FD F2 00 49 04 -7E F9 00 4A 04 -BF FC 00 4B 04 -5F FE 00 4C 04 -2F 7F 00 4D 04 -97 3F 00 4E 04 -CB 1F 00 4F 04 -E5 0F 00 50 04 -F2 87 00 51 04 -F9 43 00 52 04 -FC A1 00 53 04 -FE 50 00 54 04 -7F A8 00 55 04 -3F D4 00 56 04 -1F EA 00 57 04 -0F 75 00 58 04 -87 BA 00 59 04 -43 5D 00 5A 04 -A1 AE 00 5B 04 -50 D7 00 5C 04 -A8 EB 00 5D 04 -D4 F5 00 5E 04 -EA 7A 00 5F 04 -75 3D 00 60 04 -BA 1E 00 61 04 -5D 0F 00 62 04 -AE 87 00 63 04 -D7 43 00 64 04 -EB 21 00 65 04 -F5 90 00 66 04 -7A 48 00 67 04 -3D A4 00 68 04 -1E 52 00 69 04 -0F 29 00 6A 04 -87 94 00 6B 04 -43 CA 00 6C 04 -21 65 00 6D 04 -90 32 00 6E 04 -48 99 00 6F 04 -A4 4C 00 70 04 -52 26 00 71 04 -29 13 00 72 04 -94 89 00 73 04 -CA 44 00 74 04 -65 A2 00 75 04 -32 D1 00 76 04 -99 E8 00 77 04 -4C 74 00 78 04 -26 3A 00 79 04 -13 1D 00 7A 04 -89 8E 00 7B 04 -44 C7 00 7C 04 -D1 71 00 7D 04 -E8 38 00 7E 04 -74 1C 00 7F 04 -3A 0E 00 80 04 -1D 07 00 81 04 -8E 83 00 82 04 -C7 41 00 83 04 -E3 A0 00 84 04 -71 D0 00 85 04 -38 68 00 86 04 -1C 34 00 87 04 -0E 1A 00 88 04 -07 0D 00 89 04 -83 86 00 8A 04 -41 43 00 8B 04 -A0 A1 00 8C 04 -D0 50 00 8D 04 -68 28 00 8E 04 -34 14 00 8F 04 -1A 0A 00 90 04 -0D 05 00 91 04 -86 02 00 92 04 -43 01 00 93 04 -A1 80 00 94 04 -50 40 00 95 04 -28 20 00 96 04 -14 10 00 97 04 -0A 08 00 98 04 -05 84 00 99 04 -02 42 00 9A 04 -01 21 00 9B 04 -80 10 00 9C 04 -40 08 00 9D 04 -20 04 00 9E 04 -10 02 00 9F 04 -08 81 00 A0 04 -84 40 00 A1 04 -42 20 00 A2 04 -10 48 00 A3 04 -08 24 00 A4 04 -04 12 00 A5 04 -02 89 00 A6 04 -81 C4 00 A7 04 -40 62 00 A8 04 -20 B1 00 A9 04 -90 58 00 AA 04 -48 2C 00 AB 04 -24 16 00 AC 04 -12 8B 00 AD 04 -89 45 00 AE 04 -C4 22 00 AF 04 -62 91 00 B0 04 -B1 C8 00 B1 04 -58 64 00 B2 04 -2C 32 00 B3 04 -16 99 00 B4 04 -8B CC 00 B5 04 -45 E6 00 B6 04 -22 F3 00 B7 04 -91 79 00 B8 04 -C8 3C 00 B9 04 -64 1E 00 BA 04 -32 8F 00 BB 04 -99 47 00 BC 04 -CC A3 00 BD 04 -E6 D1 00 BE 04 -F3 E8 00 BF 04 -79 F4 00 C0 04 -3C 7A 00 C1 04 -1E BD 00 C2 04 -8F DE 00 C3 04 -47 6F 00 C4 04 -A3 37 00 C5 04 -D1 1B 00 C6 04 -E8 8D 00 C7 04 -F4 46 00 C8 04 -7A A3 00 C9 04 -BD 51 00 CA 04 -DE 28 00 CB 04 -6F 94 00 CC 04 -37 CA 00 CD 04 -1B 65 00 CE 04 -8D B2 00 CF 04 -46 D9 00 D0 04 -A3 EC 00 D1 04 -51 F6 00 D2 04 -28 FB 00 D3 04 -94 FD 00 D4 04 -CA 7E 00 D5 04 -65 3F 00 D6 04 -B2 9F 00 D7 04 -D9 4F 00 D8 04 -EC A7 00 D9 04 -F6 D3 00 DA 04 -FB 69 00 DB 04 -FD B4 00 DC 04 -7E 5A 00 DD 04 -3F 2D 00 DE 04 -9F 96 00 DF 04 -4F 4B 00 E0 04 -A7 25 00 E1 04 -D3 92 00 E2 04 -69 49 00 E3 04 -B4 24 00 E4 04 -5A 12 00 E5 04 -2D 09 00 E6 04 -96 04 00 E7 04 -4B 82 00 E8 04 -25 41 00 E9 04 -92 20 00 EA 04 -49 90 00 EB 04 -24 48 00 EC 04 -12 24 00 ED 04 -09 92 00 EE 04 -04 C9 00 EF 04 -82 64 00 F0 04 -41 B2 00 F1 04 -20 D9 00 F2 04 -90 6C 00 F3 04 -48 36 00 F4 04 -24 9B 00 F5 04 -92 CD 00 F6 04 -C9 E6 00 F7 04 -64 F3 00 F8 04 -B2 F9 00 F9 04 -D9 FC 00 FA 04 -6C 7E 00 FB 04 -36 BF 00 FC 04 -9B 5F 00 FD 04 -CD 2F 00 FE 04 -E6 97 00 FF 04 -F3 4B 01 00 04 -F9 25 01 01 04 -FC 12 01 02 04 -7E 89 01 03 04 -BF C4 01 04 04 -5F E2 01 05 04 -2F 71 01 06 04 -97 B8 01 07 04 -4B DC 01 08 04 -25 EE 01 09 04 -12 F7 01 0A 04 -89 7B 01 0B 04 -C4 BD 01 0C 04 -E2 5E 01 0D 04 -71 2F 01 0E 04 -B8 97 01 0F 04 -DC CB 01 10 04 -EE E5 01 11 04 -F7 F2 01 12 04 -7B 79 01 13 04 -BD BC 01 14 04 -5E 5E 01 15 04 -2F 2F 01 16 04 -97 17 01 17 04 -CB 0B 01 18 04 -E5 05 01 19 04 -F2 02 01 1A 04 -79 01 01 1B 04 -BC 00 01 1C 04 -5E 00 01 1D 04 -2F 80 01 1E 04 -17 C0 01 1F 04 -0B E0 01 20 04 -05 F0 01 21 04 -02 78 01 22 04 -01 BC 01 23 04 -00 5E 01 24 04 -00 AF 01 25 04 -80 D7 01 26 04 -C0 EB 01 27 04 -E0 F5 01 28 04 -F0 7A 01 29 04 -78 BD 01 2A 04 From 40b8ba53b13162a06014dde12307d82e40bab71d Mon Sep 17 00:00:00 2001 From: zohassadar Date: Tue, 17 Mar 2026 20:02:56 +0000 Subject: [PATCH 18/25] crap --- .../level_menu_sprite_replacements.asm | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 src/nametables/level_menu_sprite_replacements.asm diff --git a/src/nametables/level_menu_sprite_replacements.asm b/src/nametables/level_menu_sprite_replacements.asm deleted file mode 100644 index b54a72e0..00000000 --- a/src/nametables/level_menu_sprite_replacements.asm +++ /dev/null @@ -1,21 +0,0 @@ - ; generated by /home/ntc/src/TetrisGYM/src/nametables/level_menu.js - -LEVEL_MENU_SPRITE_COUNT = 55 - -SPRITE0_TILE = $72 -ROW_COUNT = 14 -SPRITE0_Y = $26 - -RESTORE_SCROLL_Y = $98 - -.macro scrollSleep -; macro because ca65 doesn't like calling a macro on a label -sleepFor 12529 -.endmacro - -levelMenuSpriteTiles: - .byte $42,$3B,$3C,$A1,$82,$3B,$3C,$80,$62,$3B,$3C,$61,$40,$3B,$3C,$71,$82,$3B,$3C,$80,$60,$3B,$3C,$83,$70,$3B,$3C,$83,$60,$3B,$3C,$62,$70,$3B,$3C,$40,$80,$3B,$3C,$82,$62,$3B,$3C,$62,$40,$3B,$3C,$72,$82,$3B,$3C,$72,$83,$3B,$3C,$82 -levelMenuSpritesX: - .byte $0,$8,$F0,$F8,$0,$8,$F0,$F8,$0,$8,$F0,$F8,$0,$8,$F0,$F8,$0,$8,$F0,$F8,$0,$8,$F0,$F8,$0,$8,$F0,$F8,$0,$8,$F0,$F8,$0,$8,$F0,$F8,$0,$8,$F0,$F8,$0,$8,$F0,$F8,$0,$8,$F0,$F8,$0,$8,$F0,$F8,$0,$8,$F0,$F8 -levelMenuSpritesY: - .byte $27,$27,$27,$27,$2F,$2F,$2F,$2F,$37,$37,$37,$37,$3F,$3F,$3F,$3F,$47,$47,$47,$47,$4F,$4F,$4F,$4F,$57,$57,$57,$57,$5F,$5F,$5F,$5F,$67,$67,$67,$67,$6F,$6F,$6F,$6F,$77,$77,$77,$77,$7F,$7F,$7F,$7F,$87,$87,$87,$87,$8F,$8F,$8F,$8F From 3daa5d1cf9e946b57906dec5019602ef6d8210d2 Mon Sep 17 00:00:00 2001 From: zohassadar Date: Wed, 18 Mar 2026 12:37:12 +0000 Subject: [PATCH 19/25] test updates --- gentas.py | 11 ++++++++++- maketas.sh | 30 +++++++++++++++++++++++------- tests/src/nmi.rs | 2 +- tests/src/parity.rs | 11 ++++++++--- tools/tas.lua | 2 +- 5 files changed, 43 insertions(+), 13 deletions(-) diff --git a/gentas.py b/gentas.py index 0f388d32..7fbb15c9 100644 --- a/gentas.py +++ b/gentas.py @@ -1,5 +1,7 @@ import argparse import logging +import pathlib +import sys logger = logging.getLogger(__name__) OFFSET=2 @@ -29,6 +31,7 @@ def get_tas_line(input_byte: int): def main(): parser = argparse.ArgumentParser() parser.add_argument("frames", type=int, help="length of tas") + parser.add_argument("-f", "--file", type=pathlib.Path, help="output file (stdout otherwise)") parser.add_argument( "-R", "--right", @@ -94,7 +97,13 @@ def main(): # raise RuntimeError(f"{frame} needs to be less than {OFFSET}") output[frame+OFFSET] |= value - print('\n'.join(get_tas_line(f) for f in output)) + + output = ('\n'.join(get_tas_line(f) for f in output)) + if args.file: + with open(args.file, 'w+') as file: + print(output, file=file) + else: + print(output) if __name__ == "__main__": diff --git a/maketas.sh b/maketas.sh index 16866393..d9e0f430 100755 --- a/maketas.sh +++ b/maketas.sh @@ -1,9 +1,25 @@ #!/usr/bin/env bash -python gentas.py 1400 \ - -T 265 273 290 310 1300 1310 \ - -R 302 304 306 308 {700..1000..2} \ - -D 303 \ - -A 309 310 {401..1000..2} \ - -L {401..699} \ - && : +mkdir -p tases + +for i in {100..2000..100}; do + python gentas.py $i -f tases/no-start-$(printf '%04d' $i).fm2 +done + +python gentas.py 300 -T 265 -f tases/earliest-start.fm2 +python gentas.py 1800 -T 1794 -f tases/latest-start.fm2 +python gentas.py 300 -T 265 270 274 286 -f tases/fastest-999999-start.fm2 + +python gentas.py 500 -T 400 420 440 460 -f tases/start-pattern-a.fm2 +python gentas.py 800 -T 700 720 740 -f tases/start-pattern-b.fm2 +python gentas.py 1000 -T 900 920 940 -f tases/start-pattern-c.fm2 + + +while read -r -d '' tas; do + clean="${tas%.*}"-clean.log + if ! test -f "$clean"; then + echo $clean not found, running test + time cargo run --release --manifest-path tests/Cargo.toml -- \ + parity "$tas" -w + fi +done < <(find tases/ -name "*.fm2" -print0) diff --git a/tests/src/nmi.rs b/tests/src/nmi.rs index eca1d6f1..37650688 100644 --- a/tests/src/nmi.rs +++ b/tests/src/nmi.rs @@ -7,7 +7,7 @@ pub fn test() { let main_loop = labels::get("mainLoop"); let game_mode = labels::get("gameMode") as usize; let level_number = labels::get("levelNumber") as usize; - let nmi_label = labels::get("nmi"); + let _nmi_label = labels::get("nmi"); let hz_flag = labels::get("hzFlag") as usize; let render_flags = labels::get("renderFlags") as usize; diff --git a/tests/src/parity.rs b/tests/src/parity.rs index 9761568a..3c071517 100644 --- a/tests/src/parity.rs +++ b/tests/src/parity.rs @@ -5,6 +5,7 @@ use std::fs::File; use std::io::BufWriter; use std::io::Result; use std::io::Write; +use std::io::stdout; use std::path::PathBuf; use gag::Gag; @@ -34,9 +35,12 @@ pub fn compare(tasfile: &PathBuf, write: &bool, verbose: &bool, render: &bool) { let _ = write_bytes_to_file(&get_new_filename(tasfile, "clean"), &vanilla); let _ = write_bytes_to_file(&get_new_filename(tasfile, "gym"), &gym); } else { + print!(">> comparing results for {tasfile:?}... "); + stdout().flush().unwrap(); let vanilla_logfile = get_new_filename(tasfile, "clean"); let compare_bytes: Vec = read_bytes_from_file(&vanilla_logfile); compare_with_vanilla(&tas_buttons, &compare_bytes, &verbose, &render); + println!(" ✅"); } } @@ -115,7 +119,7 @@ fn extract_values_from_labels(emu: &mut NesState) -> Vec { fn compare_with_vanilla( tas_buttons: &Vec, compare_bytes: &Vec, - verbose: &bool, + _verbose: &bool, render: &bool, ) { let mut gym; @@ -130,8 +134,9 @@ fn compare_with_vanilla( ptr += COMPARE_BYTES.len(); let values = extract_values_from_labels(&mut gym); if expected != values { - println!("Gym: {:?}", values); - println!("Vog: {:?}", expected); + eprintln!("❌"); + eprintln!("Gym: {:?}", values); + eprintln!("Vog: {:?}", expected); panic!("Mismatch on line {}!", i + 1); } diff --git a/tools/tas.lua b/tools/tas.lua index 8a275eca..ac2b61cd 100644 --- a/tools/tas.lua +++ b/tools/tas.lua @@ -1,6 +1,6 @@ -- https://tasvideos.org/7625S -- local TASFILE = "tases/r57shell-Archanfel-Tetris-fastest999999.fm2" -local TASFILE = "heh.fm2" +local TASFILE = "tases/start-pattern-a.fm2" local rom = emu.getRomInfo() local romPath, _ = string.gsub(rom.path, rom.name, "") From a0deabca70e4a73c6c4a1a68f1bd8823ad9993ba Mon Sep 17 00:00:00 2001 From: zohassadar Date: Wed, 18 Mar 2026 12:45:36 +0000 Subject: [PATCH 20/25] leaving this here --- src/boot.asm | 4 ++++ src/gamemode/branch.asm | 2 +- src/ram.asm | 8 ++++---- testtas.sh | 12 ++++++++++++ 4 files changed, 21 insertions(+), 5 deletions(-) create mode 100755 testtas.sh diff --git a/src/boot.asm b/src/boot.asm index ce4dda6d..5f67c1b1 100644 --- a/src/boot.asm +++ b/src/boot.asm @@ -30,6 +30,10 @@ lda #$A sta paceModifier + inc hzFlag + inc inputDisplayFlag + ; inc darkModifier + lda #$10 sta dasModifier diff --git a/src/gamemode/branch.asm b/src/gamemode/branch.asm index 166e8b16..769e5ecc 100644 --- a/src/gamemode/branch.asm +++ b/src/gamemode/branch.asm @@ -280,7 +280,7 @@ stageBootSprites: ldx #frameCounter jsr stageZeroPage - ldx #rng_seed+1 + ldx #rng_seed_hi jsr stageZeroPage ldx #rng_seed jsr stageZeroPage diff --git a/src/ram.asm b/src/ram.asm index 0a6ace64..4fedf22e 100644 --- a/src/ram.asm +++ b/src/ram.asm @@ -15,8 +15,8 @@ cycleCount: .res 2 ; $0012 ; 2 bytes ; used for crash oneThirdPRNG: .res 1 ; $0014 ; used for crash .res $2 -rng_seed: .res 2 ; $0017 -rng_seed_hi := rng_seed + 1 +rng_seed: .res 1 ; $0017 +rng_seed_hi: .res 1; spawnID: .res 1 ; $0019 spawnCount: .res 1 ; $001A pointerAddr: .res 2 ; $001B ; used in debug, harddrop @@ -103,8 +103,8 @@ positionValidTmp: .res 1 ; $00AD ; 0-level, 1-height originalY: .res 1 ; $00AE dropSpeed: .res 1 ; $00AF tmpCurrentPiece: .res 1 ; $00B0 ; Only used as a temporary -frameCounter: .res 2 ; $00B1 -frameCounterHi := frameCounter + 1 +frameCounter: .res 1 ; $00B1 +frameCounterHi: .res 1 oamStagingLength: .res 1 ; $00B3 .res 1 newlyPressedButtons: .res 1 ; $00B5 ; Active player's buttons diff --git a/testtas.sh b/testtas.sh new file mode 100755 index 00000000..9590fe77 --- /dev/null +++ b/testtas.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +cargo build --release --manifest-path tests/Cargo.toml + +node build.js + +while read -r -d '' tas; do + clean="${tas%.*}"-clean.log + if test -f "$clean"; then + ./tests/target/release/gym-tests parity "$tas" + fi +done < <(find tases/ -name "*.fm2" -print0) From e8ed04c2c41f8fa39b3c50a2932c7042e7be04ad Mon Sep 17 00:00:00 2001 From: kirjavascript Date: Wed, 3 Jun 2026 01:39:28 +0100 Subject: [PATCH 21/25] gamemodestatefix --- src/gamemodestate/branch.asm | 5 ++++- src/gamemodestate/checkforabss.asm | 6 +++++- src/gamemodestate/handlegameover.asm | 1 - src/gamemodestate/initbackground.asm | 1 - src/gamemodestate/initstate.asm | 1 - src/gamemodestate/pause.asm | 1 - src/gamemodestate/updatecounters.asm | 2 -- src/gamemodestate/updateplayer1.asm | 1 - src/main.asm | 4 ++-- src/ram.asm | 3 ++- 10 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/gamemodestate/branch.asm b/src/gamemodestate/branch.asm index 8ae60ecd..1705ced4 100644 --- a/src/gamemodestate/branch.asm +++ b/src/gamemodestate/branch.asm @@ -10,6 +10,8 @@ ; vblankThenRunState2 gms: 2 acc eq (set to $2) branchOnGameModeState: + lda #0 + sta mainLoopWait branchTo gameModeState, \ gameModeState_initGameBackground, \ gameModeState_initGameState, \ @@ -23,12 +25,13 @@ branchOnGameModeState: gameModeState_next: ; used to be updatePlayer2 inc gameModeState - lda #$1 ; acc should not be equal rts gameModeState_vblankThenRunState2: lda #$02 sta gameModeState + lda #$1 + sta mainLoopWait rts .include "initbackground.asm" diff --git a/src/gamemodestate/checkforabss.asm b/src/gamemodestate/checkforabss.asm index 432a4679..b7129d99 100644 --- a/src/gamemodestate/checkforabss.asm +++ b/src/gamemodestate/checkforabss.asm @@ -4,7 +4,11 @@ gameModeState_checkForResetKeyCombo: cmp #BUTTON_A+BUTTON_B+BUTTON_START+BUTTON_SELECT beq @reset inc gameModeState - ; acc has to be heldButtons_player1 here + cmp #BUTTON_LEFT+BUTTON_DOWN+BUTTON_RIGHT + bne @continue + lda #1 + sta mainLoopWait +@continue: rts @reset: jsr updateAudio2 diff --git a/src/gamemodestate/handlegameover.asm b/src/gamemodestate/handlegameover.asm index 2a6a4afe..cfec037c 100644 --- a/src/gamemodestate/handlegameover.asm +++ b/src/gamemodestate/handlegameover.asm @@ -46,5 +46,4 @@ gameModeState_handleGameOver: rts @ret: inc gameModeState ; 4 - lda #$1 ; acc should not be equal (always $1 in original game) rts diff --git a/src/gamemodestate/initbackground.asm b/src/gamemodestate/initbackground.asm index cc9658ac..99c9a839 100644 --- a/src/gamemodestate/initbackground.asm +++ b/src/gamemodestate/initbackground.asm @@ -56,7 +56,6 @@ gameModeState_initGameBackground: lda #$01 sta playState inc gameModeState ; 1 - lda #0 ; acc should not be equal rts scoringBackground: diff --git a/src/gamemodestate/initstate.asm b/src/gamemodestate/initstate.asm index 5bda81a6..23ca3bfd 100644 --- a/src/gamemodestate/initstate.asm +++ b/src/gamemodestate/initstate.asm @@ -153,7 +153,6 @@ gameModeState_initGameState: lda musicSelectionTable,x jsr setMusicTrack inc gameModeState ; 2 - lda #4 ; acc should not be equal initGameState_return: rts diff --git a/src/gamemodestate/pause.asm b/src/gamemodestate/pause.asm index a3b5c70f..34fcb550 100644 --- a/src/gamemodestate/pause.asm +++ b/src/gamemodestate/pause.asm @@ -17,7 +17,6 @@ gameModeState_handlePause: jsr pause @ret: inc gameModeState ; 8 - lda #$0 ; acc must not be equal rts pause: diff --git a/src/gamemodestate/updatecounters.asm b/src/gamemodestate/updatecounters.asm index b35e93c5..d3b8f65f 100644 --- a/src/gamemodestate/updatecounters.asm +++ b/src/gamemodestate/updatecounters.asm @@ -3,8 +3,6 @@ gameModeState_updateCountersAndNonPlayerState: lda #$00 sta oamStagingLength inc fallTimer - ; next code makes acc behave as normal - ; (dont edit unless you know what you're doing) lda newlyPressedButtons_player1 and #BUTTON_SELECT beq @ret diff --git a/src/gamemodestate/updateplayer1.asm b/src/gamemodestate/updateplayer1.asm index edf9da24..a00be232 100644 --- a/src/gamemodestate/updateplayer1.asm +++ b/src/gamemodestate/updateplayer1.asm @@ -15,5 +15,4 @@ gameModeState_updatePlayer1: jsr stageSpriteForNextPiece inc gameModeState ; 5 - lda #$FF ; acc from stateSpriteForNextPiece rts diff --git a/src/main.asm b/src/main.asm index 1fb4c13d..6445d86f 100644 --- a/src/main.asm +++ b/src/main.asm @@ -27,8 +27,8 @@ initRam: mainLoop: jsr branchOnGameMode - cmp gameModeState - bne @continue + lda mainLoopWait + beq @continue jsr updateAudioWaitForNmiAndResetOamStaging @continue: jmp mainLoop diff --git a/src/ram.asm b/src/ram.asm index 4015c984..a7e36c09 100644 --- a/src/ram.asm +++ b/src/ram.asm @@ -24,8 +24,9 @@ allegroIndex: .res 1 ; $001F for crash wasAllegro: .res 1 ; $0020 for crash startParity: .res 1 ; $0021 for crash lagState: .res 1 ; $0022 for lagged lines & score - .res $10 + .res $F +mainLoopWait: .res 1 ; $0032 verticalBlankingInterval: .res 1 ; $0033 set_seed: .res 3 ; $0034 ; rng_seed, rng_seed+1, spawnCount set_seed_input: .res 3 ; $0037 ; copied to set_seed during gameModeState_initGameState From 7c6f5bf95a1e818714f1149837f3e4d0ef38ef04 Mon Sep 17 00:00:00 2001 From: kirjavascript Date: Wed, 3 Jun 2026 01:47:30 +0100 Subject: [PATCH 22/25] gamemodestatefix --- src/gamemodestate/branch.asm | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/gamemodestate/branch.asm b/src/gamemodestate/branch.asm index 1705ced4..7af38053 100644 --- a/src/gamemodestate/branch.asm +++ b/src/gamemodestate/branch.asm @@ -1,14 +1,3 @@ -; the return value of this routine dictates if we should wait for nmi or not right after -; initGameBackground gms: 1 acc: 0 - ne -; initGameState gms: 2 acc: 4/0 - ne -; updateCountersAndNonPlayerState gms: 3 acc: 0/1 - ne -; handleGameOver gms: 4 acc: eq (set to $9) if gameOver, $1 otherwise (ne) -; updatePlayer1 gms: 5 acc: $FF - ne -; next gms: 6 acc: $1 ne -; checkForResetKeyCombo gms: 7 acc: 0 or heldButtons - eq if holding down, left and right -; handlePause gms: 8 acc: 0/3 - ne -; vblankThenRunState2 gms: 2 acc eq (set to $2) - branchOnGameModeState: lda #0 sta mainLoopWait From 94303d9f09550e3b0d993fe5b45bbaef27751a68 Mon Sep 17 00:00:00 2001 From: kirjavascript Date: Wed, 3 Jun 2026 09:24:30 +0100 Subject: [PATCH 23/25] inline --- src/gamemodestate/branch.asm | 6 +----- src/gamemodestate/checkforabss.asm | 3 +-- src/main.asm | 4 ---- src/ram.asm | 2 +- 4 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/gamemodestate/branch.asm b/src/gamemodestate/branch.asm index 7af38053..772fb6b5 100644 --- a/src/gamemodestate/branch.asm +++ b/src/gamemodestate/branch.asm @@ -1,6 +1,4 @@ branchOnGameModeState: - lda #0 - sta mainLoopWait branchTo gameModeState, \ gameModeState_initGameBackground, \ gameModeState_initGameState, \ @@ -19,9 +17,7 @@ gameModeState_next: ; used to be updatePlayer2 gameModeState_vblankThenRunState2: lda #$02 sta gameModeState - lda #$1 - sta mainLoopWait - rts + jmp updateAudioWaitForNmiAndResetOamStaging .include "initbackground.asm" .include "initstate.asm" diff --git a/src/gamemodestate/checkforabss.asm b/src/gamemodestate/checkforabss.asm index b7129d99..7d4977b7 100644 --- a/src/gamemodestate/checkforabss.asm +++ b/src/gamemodestate/checkforabss.asm @@ -6,8 +6,7 @@ gameModeState_checkForResetKeyCombo: inc gameModeState cmp #BUTTON_LEFT+BUTTON_DOWN+BUTTON_RIGHT bne @continue - lda #1 - sta mainLoopWait + jsr updateAudioWaitForNmiAndResetOamStaging @continue: rts diff --git a/src/main.asm b/src/main.asm index 6445d86f..e240c812 100644 --- a/src/main.asm +++ b/src/main.asm @@ -27,10 +27,6 @@ initRam: mainLoop: jsr branchOnGameMode - lda mainLoopWait - beq @continue - jsr updateAudioWaitForNmiAndResetOamStaging -@continue: jmp mainLoop .include "nmi/nmi.asm" diff --git a/src/ram.asm b/src/ram.asm index a7e36c09..fca16a07 100644 --- a/src/ram.asm +++ b/src/ram.asm @@ -26,7 +26,7 @@ startParity: .res 1 ; $0021 for crash lagState: .res 1 ; $0022 for lagged lines & score .res $F -mainLoopWait: .res 1 ; $0032 + .res 1 ; $0032 verticalBlankingInterval: .res 1 ; $0033 set_seed: .res 3 ; $0034 ; rng_seed, rng_seed+1, spawnCount set_seed_input: .res 3 ; $0037 ; copied to set_seed during gameModeState_initGameState From 8b40bf90b23ec7d0d5992a701414393a509c3596 Mon Sep 17 00:00:00 2001 From: kirjavascript Date: Wed, 3 Jun 2026 09:29:06 +0100 Subject: [PATCH 24/25] squash --- src/ram.asm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ram.asm b/src/ram.asm index fca16a07..4015c984 100644 --- a/src/ram.asm +++ b/src/ram.asm @@ -24,9 +24,8 @@ allegroIndex: .res 1 ; $001F for crash wasAllegro: .res 1 ; $0020 for crash startParity: .res 1 ; $0021 for crash lagState: .res 1 ; $0022 for lagged lines & score - .res $F + .res $10 - .res 1 ; $0032 verticalBlankingInterval: .res 1 ; $0033 set_seed: .res 3 ; $0034 ; rng_seed, rng_seed+1, spawnCount set_seed_input: .res 3 ; $0037 ; copied to set_seed during gameModeState_initGameState From 0f727a43d1b38781643a6b0fd193a3276716e3d9 Mon Sep 17 00:00:00 2001 From: zohassadar Date: Wed, 3 Jun 2026 17:55:24 +0000 Subject: [PATCH 25/25] adjust init timing re: #154 --- src/gamemodestate/initbackground.asm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/gamemodestate/initbackground.asm b/src/gamemodestate/initbackground.asm index 15d128a4..798ff641 100644 --- a/src/gamemodestate/initbackground.asm +++ b/src/gamemodestate/initbackground.asm @@ -51,8 +51,19 @@ gameModeState_initGameBackground: sta currentPpuCtrl jsr resetScroll jsr waitForVBlankAndEnableNmi + + + + ; this no longer applies: ; gym setup takes longer than vanilla, skip a frame to line up ; jsr updateAudioWaitForNmiAndResetOamStaging + + + ; https://github.com/kirjavascript/TetrisGYM/pull/154 + ; restored wait jsr after mainloop wait inlining + ; the changes in this branch may have introduced a bug that have been fixed. + ; exact cause tbd (if ever) + jsr updateAudioWaitForNmiAndResetOamStaging jsr updateAudioWaitForNmiAndEnablePpuRendering jsr updateAudioWaitForNmiAndResetOamStaging lda #$01