diff --git a/api/src/main/java/io/github/applecommander/bastools/api/code/CodeMark.java b/api/src/main/java/io/github/applecommander/bastools/api/code/CodeMark.java index f6ec4de..a2f5ab7 100644 --- a/api/src/main/java/io/github/applecommander/bastools/api/code/CodeMark.java +++ b/api/src/main/java/io/github/applecommander/bastools/api/code/CodeMark.java @@ -1,5 +1,7 @@ package io.github.applecommander.bastools.api.code; +import java.util.HashMap; + /** * A {@code CodeMark} marks a dynamic address within the output stream. When referenced, it will report the * most recent address is knows, forcing the generation to run multiple times until it "settles". @@ -16,6 +18,8 @@ package io.github.applecommander.bastools.api.code; * @author rob */ public class CodeMark { + private static final int LOOP_MAX = 10; + private HashMap loopCounter = new HashMap<>(); private int address; public int getAddress() { @@ -28,6 +32,23 @@ public class CodeMark { */ public boolean update(GeneratorState state) { int currentAddress = state.currentAddress(); + loopCounter.merge(currentAddress, 1, (a,b) -> a+b); + if (loopCounter.get(currentAddress) > LOOP_MAX) { + StringBuilder sb = new StringBuilder(); + sb.append("A circular pattern in a dynamic address was discovered!\n"); + sb.append("This usually indicates that an address was computed to be just below a page boundary.\n"); + sb.append("For example, the 0x1000 mark. However, code using that address then pushed the\n"); + sb.append("address over the 0x1000 mark, but that triggered the address to be recomputed below\n"); + sb.append("the 0x1000 mark. Rinse and repeat.\n"); + sb.append("\n"); + sb.append("For example, shape tables have a POKE 232,255 when the shape table is at 0xFFF, but the\n"); + sb.append("address gets recomputed (due to the 3 digit low address) to be 0x1001, which changes the\n"); + sb.append("low address byte to be a single digit. This starts the cascade.\n"); + sb.append("\n"); + sb.append("Generally, stick a little bit of extra code into the program bypasses this issue.\n"); + sb.append("(Sorry, there is no elegant solution at this time. Pull requests are welcome!).\n"); + throw new IllegalStateException(sb.toString()); + } try { return currentAddress != address; } finally {