Skip to content

Transpiler: support complex array index expressions#234

Merged
jonathanpeppers merged 5 commits intomainfrom
copilot/support-complex-array-index
Mar 18, 2026
Merged

Transpiler: support complex array index expressions#234
jonathanpeppers merged 5 commits intomainfrom
copilot/support-complex-array-index

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 16, 2026

HandleLdelemU1 only supported simple array indices (local variable or constant). Complex index expressions like array[(x >> 3) + ((y >> 3) << 4)] threw a TranspileException. This pattern is common in NES tile map lookups.

Changes

  • IL2NESWriter.ArrayHandling.cs: Added HandleLdelemU1ComplexIndex() — when the index is not a simple ldloc/ldc, walks backward through IL using stack depth tracking to locate the array instruction, removes dead array-load instructions from the block buffer via Block.RemoveAt, strips any state-dependent JSR pusha/pushax left by ROM array loads, then emits TAX + LDA array,X.
  • Simplified constant index detection by replacing the inline switch with the existing GetLdcValue() helper.
  • RoslynTests.cs: Two new tests — ComplexArrayIndexExpression (full (x >> 3) + ((y >> 3) << 4) pattern) and SimpleShiftArrayIndex (x >> 3) — verifying LSR, ASL, CLC, TAX, and LDA absolute,X opcodes in output.

Example

Previously required a workaround:

byte idx = (byte)((x >> 3) + ((y >> 3) << 4));
byte result = collision_top_left[idx];

Now works directly:

byte result = collision_top_left[(byte)((x >> 3) + ((y >> 3) << 4))];
Original prompt

This section details on the original issue you should resolve

<issue_title>Transpiler: support complex array index expressions</issue_title>
<issue_description>Follow-up from PR #211 (crypto.c sample port).

The transpiler currently only supports simple array index expressions where the index is a local variable or constant. Complex expressions like:

collision_top_left[(x >> 3) + ((y >> 3) << 4)]

fail with: Array element access requires the index to be a local variable or constant. Complex index expressions are not supported.

This pattern is common in NES games for tile map lookups. The C code equivalent is straightforward array indexing that the 6502 handles with indexed addressing modes.

Workaround

Compute the index into a temp variable first:

byte idx = (byte)((x >> 3) + ((y >> 3) << 4));
byte result = collision_top_left[idx];

Implementation

The HandleLdelemU1 method in IL2NESWriter.ArrayHandling.cs needs to handle more complex IL patterns before the ldelem.u1 instruction, such as shift/add/sub operations on the index.
</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits March 16, 2026 01:20
Add HandleLdelemU1ComplexIndex method that handles patterns like
array[(x >> 3) + ((y >> 3) << 4)] where the index is computed by
preceding arithmetic operations (shr, shl, add, sub, and, or, etc.).

The method walks backward through IL using stack depth tracking to
find the array instruction, removes array-load code from the block
buffer, and emits TAX + LDA array,X to access the element using the
computed index in the A register.

Add two new tests: ComplexArrayIndexExpression and SimpleShiftArrayIndex.

Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com>
Add LSR, ASL, CLC assertions to verify the full expression is transpiled.

Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com>
Copilot AI changed the title [WIP] Add support for complex array index expressions Transpiler: support complex array index expressions Mar 16, 2026
Copilot AI requested a review from jonathanpeppers March 16, 2026 01:24
@jonathanpeppers jonathanpeppers marked this pull request as ready for review March 17, 2026 23:40
Copilot AI review requested due to automatic review settings March 17, 2026 23:40
Copy link
Copy Markdown
Owner

@jonathanpeppers jonathanpeppers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Code review complete — approving.

What I checked:

  • Stack depth walk-back in HandleLdelemU1ComplexIndex correctly locates the array instruction by tracking net IL stack contributions backward from ldelem.u1
  • Block buffer removal logic properly cleans up dead array-load instructions (including JSR pusha/pushax for ROM arrays), following the same pattern as HandleStelemI1
  • State flags (_runtimeValueInA, _savedRuntimeToTemp, _immediateInA, _lastLoadedLocalIndex) are all correctly set/cleared after emission
  • GetLdcValue refactor is a clean simplification
  • Conv_u1/Conv_i1-family opcodes correctly treated as net-zero in the depth tracker
  • Both new tests pass and follow existing RoslynTests hex-assertion conventions

Ran locally: All 509 tests pass (445 dotnes.tests + 64 analyzer tests).

Looks good to merge.

@jonathanpeppers
Copy link
Copy Markdown
Owner

🤖 CI is green. All 509 tests pass locally. Ready for human review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the IL→6502 transpiler to support ldelem.u1 when the array index is produced by a non-trivial expression (e.g., shifts/adds), which is a common pattern for NES tilemap lookups.

Changes:

  • Added HandleLdelemU1ComplexIndex() to locate the array load via backward IL stack-depth scanning and emit TAX + LDA array,X.
  • Simplified constant-index detection by using the existing GetLdcValue() helper.
  • Added two Roslyn-based tests intended to validate opcode patterns for complex and shift-only index expressions.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/dotnes.tasks/Utilities/IL2NESWriter.ArrayHandling.cs Adds complex-index support for ldelem.u1 and refactors constant index detection.
src/dotnes.tests/RoslynTests.cs Adds tests for complex array index emission patterns.

Comment thread src/dotnes.tasks/Utilities/IL2NESWriter.ArrayHandling.cs Outdated
Comment thread src/dotnes.tests/RoslynTests.cs Outdated
Comment thread src/dotnes.tests/RoslynTests.cs Outdated
Comment thread src/dotnes.tasks/Utilities/IL2NESWriter.ArrayHandling.cs
jonathanpeppers and others added 2 commits March 17, 2026 18:55
- Preserve STA TEMP and JSR pusha/pushax operand-saving instructions
  during block removal in HandleLdelemU1ComplexIndex, so prior runtime
  values survive across complex index computation
- Remove unconditional _savedRuntimeToTemp = false; the flag now
  correctly reflects whether a TEMP save exists in the block
- Replace single-byte hex assertions (4A, AA, BD) with multi-byte
  instruction sequences (4A4A4A, AABD, 0A0A0A0A) to eliminate
  false positives from operand bytes
- Add ComplexArrayIndex_PreservesOperandInTemp test verifying that
  (x - y) + arr[(byte)(x >> 3)] preserves the subtraction result

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve conflict in RoslynTests.cs: keep both complex array index
tests and word static field tests from main.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jonathanpeppers jonathanpeppers merged commit 69712f9 into main Mar 18, 2026
1 check passed
@jonathanpeppers jonathanpeppers deleted the copilot/support-complex-array-index branch March 18, 2026 00:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Transpiler: support complex array index expressions

3 participants