Fix OamSpr crash and Block.RemoveAt label loss#294
Merged
jonathanpeppers merged 3 commits intomainfrom Mar 20, 2026
Merged
Conversation
EmitOamSprDecsp4 crashed with KeyNotFoundException when compoundLocalIdx was -1 (unset) in compound expressions where the source was a constant-only expression. Also fix checked byte casts for negative constant values (Ldc_i4_m1 = 0xFF). - Add defensive fallback when compoundLocalIdx < 0: emit constant directly - Use (byte)(value & 0xFF) instead of checked((byte)value) for constValue Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a crash in the transpiler’s oam_spr/decsp4 emission path when the backward-scan identifies a “compound” argument but fails to record a valid source local (e.g., constant-only sub-expressions), and adjusts constant→byte handling to correctly match IL truncation semantics for negative constants (e.g., -1 → 0xFF).
Changes:
- Guard
compoundLocalIdxaccess to avoidLocals[-1]and add a fallback path for constant-only compound args. - Replace
checked((byte)...)with(byte)(value & 0xFF)for certain constant argument emissions (including the 5thidarg).
Comments suppressed due to low confidence (1)
src/dotnes.tasks/Utilities/IL2NESWriter.OamSprites.cs:438
- The "A already has the right value from attr" optimization can misfire when
attrArgis not actually an immediate constant (e.g.,isCompoundorisStaticField). In those casesattrArg.constValueis still 0, so ifidArg.constValueis 0 the code may skip reloading A and pass the attribute value as the 5th (id) argument. This should be gated onattrArgbeing a true constant argument before comparingconstValue.
// Check if A already has the right value from the 4th arg STA
if (argInfos.Count >= 4)
{
var attrArg = argInfos[3];
if (!attrArg.isLocal && !attrArg.isArrayElem && !idArg.isLocal
&& attrArg.constValue == idArg.constValue)
{
Block.RemoveAt(index) was silently discarding labels attached to removed instructions. This caused unresolved branch labels in large programs where pattern matching handlers remove instructions at arbitrary indices. RemoveLast already preserved labels correctly — applied the same pattern. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jonathanpeppers
commented
Mar 20, 2026
Owner
Author
jonathanpeppers
left a comment
There was a problem hiding this comment.
Review comment addressed in 7074ca7. All 563 tests pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two fixes discovered while getting the crypto sample (PR #211) to transpile.
Fix 1: OamSpr backward scan crash with negative constants
EmitOamSprDecsp4crashed withKeyNotFoundExceptionwhencompoundLocalIdxwas -1 (unset) in compound expressions where the source was a constant-only expression. Also fixes checked byte casts for negative constant values (Ldc_i4_m1= 0xFF).compoundLocalIdx < 0: emit constant directly instead of crashing onLocals[-1](byte)(value & 0xFF)instead ofchecked((byte)value)forconstValueand 5th arg emissionFix 2: Preserve labels in Block.RemoveAt
Block.RemoveAt(index)silently discarded labels attached to removed instructions. This caused unresolved branch labels (UnresolvedLabelException) in large programs where pattern matching handlers remove instructions at arbitrary indices.RemoveLastalready preserved labels correctly -- applied the same pattern toRemoveAt.Result
With these fixes plus sample-level workarounds (compound assignments, sfx_play), the 2,270-line crypto dungeon crawler sample transpiles to a working NES ROM and renders graphics in the emulator.
All existing tests pass.