diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java
index 81b39b69c..6a96eb14c 100644
--- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java
+++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java
@@ -40,7 +40,6 @@ public interface AsmFragmentTemplate {
*/
KickCParser.AsmLinesContext getBodyAsm();
-
/**
* Get the registers clobbered by the ASM fragment
*
diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateCache.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateCache.java
index 6711a56f5..d9884a762 100644
--- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateCache.java
+++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateCache.java
@@ -47,7 +47,7 @@ public class AsmFragmentTemplateCache {
/** Special singleton representing that the fragment can not be synthesized or loaded. */
public static AsmFragmentTemplate NO_SYNTHESIS =
- AsmFragmentTemplateParser.create(new AsmFragmentSignature.Singleton("NO_SYNTHESIS"), "NO_SYNTHESIS", null);
+ new AsmFragmentTemplateImpl(new AsmFragmentSignature.Singleton("NO_SYNTHESIS"), "NO_SYNTHESIS", null, null, null, null);
/** Prefix for the fragment hash file header. */
public static final String HASH_HEADER = "//KICKC FRAGMENT CACHE ";
@@ -187,7 +187,7 @@ public class AsmFragmentTemplateCache {
if(bodyString.startsWith(NO_SYNTHESIS.getBody())) {
cache.put(signature, NO_SYNTHESIS);
} else {
- AsmFragmentTemplate template = AsmFragmentTemplateParser.create(signature, AsmFragmentTemplateParser.fixNewlines(bodyString), targetCpu);
+ AsmFragmentTemplate template = AsmFragmentTemplateParser.parse(signature, AsmFragmentTemplateParser.fixNewlines(bodyString), targetCpu);
cache.put(signature, template);
}
}
diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateFactory.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateFactory.java
index 6536ec3f3..cbc867839 100644
--- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateFactory.java
+++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateFactory.java
@@ -19,9 +19,10 @@ public interface AsmFragmentTemplateFactory {
/**
* Get ASM templates for a signature.
+ *
* Returns zero to many templates that can generate ASM for the signature.
* If many templates are returned they will all generate valid ASM, but may have different characteristics such as clobber and cycles.
- * Return null or zero templates if the factory cannot find a fragment template for the signature.
+ * Returns null
or zero templates if the factory cannot find a fragment template for the signature.
*
* @param signature The signature describing the ASM fragment
* @return ASM fragment templates that can be used for generating ASM
diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateImpl.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateImpl.java
index 9fedb4e9b..c5a6a8954 100644
--- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateImpl.java
+++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateImpl.java
@@ -1,24 +1,9 @@
package dk.camelot64.kickc.fragment;
-import dk.camelot64.cpufamily6502.CpuClobber;
-import dk.camelot64.kickc.asm.AsmProgram;
import dk.camelot64.kickc.fragment.signature.AsmFragmentSignature;
-import dk.camelot64.kickc.model.Program;
-import dk.camelot64.kickc.model.Registers;
import dk.camelot64.kickc.model.TargetCpu;
-import dk.camelot64.kickc.model.statements.StatementSource;
-import dk.camelot64.kickc.model.symbols.Label;
-import dk.camelot64.kickc.model.symbols.ProgramScope;
-import dk.camelot64.kickc.model.symbols.Scope;
-import dk.camelot64.kickc.model.symbols.Variable;
-import dk.camelot64.kickc.model.types.SymbolType;
-import dk.camelot64.kickc.model.values.ConstantInteger;
-import dk.camelot64.kickc.model.values.ScopeRef;
-import dk.camelot64.kickc.model.values.Value;
-import dk.camelot64.kickc.parser.AsmParser;
import dk.camelot64.kickc.parser.KickCParser;
-import java.util.LinkedHashMap;
import java.util.Objects;
/**
@@ -27,181 +12,64 @@ import java.util.Objects;
*/
public class AsmFragmentTemplateImpl implements AsmFragmentTemplate {
- /** The fragment template signature name. */
- private final AsmFragmentSignature signature;
- /** The target CPU. */
- private final TargetCpu targetCpu;
- /** The fragment template body */
- private String body;
+ /** The fragment template signature name. */
+ private final AsmFragmentSignature signature;
+ /** The target CPU. */
+ private final TargetCpu targetCpu;
+ /** The fragment template body */
+ private final String body;
- /** The parsed ASM lines. Initially null. Non-null after the template is used to generate ASM code. */
- private KickCParser.AsmLinesContext bodyAsm;
- /** The ASM clobber of the fragment. Initially null. Non-null after the template is used to generate ASM code.*/
- private AsmFragmentClobber clobber;
- /** The cycles consumed by the ASM of the fragment. Initially null. Non-null after the template is used to generate ASM code.*/
- private Double cycles;
+ /** The parsed ASM lines. Initially null. Non-null after the template is used to generate ASM code. */
+ private final KickCParser.AsmLinesContext bodyAsm;
+ /** The ASM clobber of the fragment. Initially null. Non-null after the template is used to generate ASM code. */
+ private final AsmFragmentClobber clobber;
+ /** The cycles consumed by the ASM of the fragment. Initially null. Non-null after the template is used to generate ASM code. */
+ private final Double cycles;
- AsmFragmentTemplateImpl(AsmFragmentSignature signature, String body, TargetCpu targetCpu) {
- this.signature = signature;
- this.body = body;
- this.targetCpu = targetCpu;
- }
+ AsmFragmentTemplateImpl(AsmFragmentSignature signature, String body, TargetCpu targetCpu, KickCParser.AsmLinesContext bodyAsm, AsmFragmentClobber clobber, Double cycles) {
+ this.signature = signature;
+ this.body = body;
+ this.targetCpu = targetCpu;
+ this.bodyAsm = bodyAsm;
+ this.clobber = clobber;
+ this.cycles = cycles;
+ }
- /**
- * Creates an inline ASM fragment template
- *
- * @param bodyLines Parsed ASM body
- */
- public AsmFragmentTemplateImpl(KickCParser.AsmLinesContext bodyLines, TargetCpu targetCpu) {
- this.signature = new AsmFragmentSignature.Singleton("INLINE");
- this.bodyAsm = bodyLines;
- this.targetCpu = targetCpu;
- }
+ public AsmFragmentSignature getSignature() {
+ return signature;
+ }
- /**
- * Create a load/store variable
- *
- * @param name The name
- * @param type The type
- * @param scope The scope
- * @param memoryArea The memory area (zeropage/main memory)
- * @param dataSegment The data segment (in main memory)
- * @return The new PHI-master variable
- */
- public static Variable createLoadStore(String name, SymbolType type, Scope scope, Variable.MemoryArea memoryArea, String dataSegment) {
- return new Variable(name, Variable.Kind.LOAD_STORE, type, scope, memoryArea, dataSegment, null);
- }
+ public String getBody() {
+ return body;
+ }
- /**
- * Create a PHI master variable
- *
- * @param name The name
- * @param type The type
- * @param scope The scope
- * @param memoryArea The memory area (zeropage/main memory)
- * @param dataSegment The data segment (in main memory)
- * @return The new PHI-master variable
- */
- public static Variable createPhiMaster(String name, SymbolType type, Scope scope, Variable.MemoryArea memoryArea, String dataSegment) {
- return new Variable(name, Variable.Kind.PHI_MASTER, type, scope, memoryArea, dataSegment, null);
- }
+ @Override
+ public KickCParser.AsmLinesContext getBodyAsm() {
+ return bodyAsm;
+ }
- /**
- * Initialize the fields that require parsing the ASM (bodyAsm, clobber, cycles).
- */
- private void initAsm() {
- String signatureName = signature.getName();
+ public TargetCpu getTargetCpu() {
+ return targetCpu;
+ }
- // Parse the body ASM
- this.bodyAsm = AsmParser.parseAsm(this.body, new StatementSource(signature + ".asm", 1, 1, this.body, -1, -1));
- // Generate a dummy instance to find clobber & cycles
- ProgramScope scope = new ProgramScope();
- LinkedHashMap bindings = new LinkedHashMap<>();
- {
- Variable master = createPhiMaster("z", SymbolType.BYTE, scope, Variable.MemoryArea.ZEROPAGE_MEMORY, null);
- Variable v1 = Variable.createPhiVersion(master, 1); v1.setName("z1");
- Variable v2 = Variable.createPhiVersion(master, 2); v2.setName("z2");
- Variable v3 = Variable.createPhiVersion(master, 3); v3.setName("z3");
- Variable v4 = Variable.createPhiVersion(master, 4); v4.setName("z4");
- Variable v5 = Variable.createPhiVersion(master, 5); v5.setName("z5");
- Variable v6 = Variable.createPhiVersion(master, 6); v6.setName("z6");
- v1.setAllocation(new Registers.RegisterZpMem(2, 1));
- v2.setAllocation(new Registers.RegisterZpMem(4, 1));
- v3.setAllocation(new Registers.RegisterZpMem(6, 1));
- v4.setAllocation(new Registers.RegisterZpMem(8, 1));
- v5.setAllocation(new Registers.RegisterZpMem(9, 1));
- v6.setAllocation(new Registers.RegisterZpMem(10, 1));
- if(signatureName.contains("z1")) bindings.put("z1", v1);
- if(signatureName.contains("z2")) bindings.put("z2", v2);
- if(signatureName.contains("z3")) bindings.put("z3", v3);
- if(signatureName.contains("z4")) bindings.put("z4", v4);
- if(signatureName.contains("z5")) bindings.put("z5", v5);
- if(signatureName.contains("z6")) bindings.put("z6", v6);
- }
- {
- Variable v1 = createLoadStore("m1", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null);
- Variable v2 = createLoadStore("m2", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null);
- Variable v3 = createLoadStore("m3", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null);
- Variable v4 = createLoadStore("m4", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null);
- Variable v5 = createLoadStore("m5", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null);
- Variable v6 = createLoadStore("m6", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null);
- v1.setAllocation(new Registers.RegisterMainMem(v1.getVariableRef(), 1, null, false));
- v2.setAllocation(new Registers.RegisterMainMem(v2.getVariableRef(), 1, null, false));
- v3.setAllocation(new Registers.RegisterMainMem(v3.getVariableRef(), 1, null, false));
- v4.setAllocation(new Registers.RegisterMainMem(v4.getVariableRef(), 1, null, false));
- v5.setAllocation(new Registers.RegisterMainMem(v5.getVariableRef(), 1, null, false));
- v6.setAllocation(new Registers.RegisterMainMem(v6.getVariableRef(), 1, null, false));
- if(signatureName.contains("m1")) bindings.put("m1", v1);
- if(signatureName.contains("m2")) bindings.put("m2", v2);
- if(signatureName.contains("m3")) bindings.put("m3", v3);
- if(signatureName.contains("m4")) bindings.put("m4", v4);
- if(signatureName.contains("m5")) bindings.put("m5", v5);
- if(signatureName.contains("m6")) bindings.put("m6", v6);
- }
- if(signatureName.contains("c1")) bindings.put("c1", new ConstantInteger(310L));
- if(signatureName.contains("c2")) bindings.put("c2", new ConstantInteger(320L));
- if(signatureName.contains("c3")) bindings.put("c3", new ConstantInteger(330L));
- if(signatureName.contains("c4")) bindings.put("c4", new ConstantInteger(340L));
- if(signatureName.contains("c5")) bindings.put("c5", new ConstantInteger(350L));
- if(signatureName.contains("c6")) bindings.put("c6", new ConstantInteger(360L));
- if(signatureName.contains("la1")) bindings.put("la1", new Label("@1", scope, true));
- if(signatureName.startsWith("call_")) bindings.put("la1", new Label("@1", scope, true));
- AsmFragmentInstance fragmentInstance =
- new AsmFragmentInstance(new Program(), signature, ScopeRef.ROOT, this, bindings);
- AsmProgram asm = new AsmProgram(targetCpu);
- asm.startChunk(ScopeRef.ROOT, null, signature.getName());
- fragmentInstance.generate(asm);
- CpuClobber cpuClobber = asm.getClobber();
- this.clobber = new AsmFragmentClobber(cpuClobber);
- asm.addStash();
- this.cycles = asm.getCycles();
- }
+ public AsmFragmentClobber getClobber() {
+ return clobber;
+ }
- public AsmFragmentSignature getSignature() {
- return signature;
- }
+ public double getCycles() {
+ return cycles;
+ }
- public String getBody() {
- return body;
- }
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AsmFragmentTemplateImpl that = (AsmFragmentTemplateImpl) o;
+ return Objects.equals(signature, that.signature) && targetCpu == that.targetCpu && Objects.equals(body, that.body);
+ }
- @Override
- public KickCParser.AsmLinesContext getBodyAsm() {
- if(bodyAsm == null) {
- initAsm();
- }
- return bodyAsm;
- }
-
- public TargetCpu getTargetCpu() {
- return targetCpu;
- }
-
- public AsmFragmentClobber getClobber() {
- if(clobber == null) {
- initAsm();
- }
- return clobber;
- }
-
- public double getCycles() {
- if(cycles == null) {
- initAsm();
- }
- return cycles;
- }
-
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- AsmFragmentTemplateImpl that = (AsmFragmentTemplateImpl) o;
- return Objects.equals(signature, that.signature) && targetCpu == that.targetCpu && Objects.equals(body, that.body);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(signature, targetCpu, body);
- }
+ @Override
+ public int hashCode() {
+ return Objects.hash(signature, targetCpu, body);
+ }
}
diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateParser.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateParser.java
index 5987ee600..4b8a58526 100644
--- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateParser.java
+++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateParser.java
@@ -1,10 +1,29 @@
package dk.camelot64.kickc.fragment;
+import dk.camelot64.cpufamily6502.CpuClobber;
+import dk.camelot64.kickc.asm.AsmProgram;
import dk.camelot64.kickc.fragment.signature.AsmFragmentSignature;
+import dk.camelot64.kickc.model.Program;
+import dk.camelot64.kickc.model.Registers;
import dk.camelot64.kickc.model.TargetCpu;
+import dk.camelot64.kickc.model.statements.StatementSource;
+import dk.camelot64.kickc.model.symbols.Label;
+import dk.camelot64.kickc.model.symbols.ProgramScope;
+import dk.camelot64.kickc.model.symbols.Scope;
+import dk.camelot64.kickc.model.symbols.Variable;
+import dk.camelot64.kickc.model.types.SymbolType;
+import dk.camelot64.kickc.model.values.ConstantInteger;
+import dk.camelot64.kickc.model.values.ScopeRef;
+import dk.camelot64.kickc.model.values.Value;
+import dk.camelot64.kickc.parser.AsmParser;
+import dk.camelot64.kickc.parser.KickCParser;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.antlr.v4.runtime.tree.TerminalNode;
+
+import java.util.LinkedHashMap;
/**
- *
+ * Creates ASM fragment templates by parsing ASM in string form.
*/
public class AsmFragmentTemplateParser {
@@ -12,12 +31,191 @@ public class AsmFragmentTemplateParser {
* Creates an ASM fragment from a body.
*
* @param signature The ASM fragment signature
- * @param body The ASM body
+ * @param body The ASM body
* @param targetCpu The target CPU
* @return The ASM fragment template
*/
- public static AsmFragmentTemplate create(AsmFragmentSignature signature, String body, TargetCpu targetCpu) {
- return new AsmFragmentTemplateImpl(signature, body, targetCpu);
+ public static AsmFragmentTemplate parse(AsmFragmentSignature signature, String body, TargetCpu targetCpu) {
+ final ParsedAsmBody parsedAsmBody = parseAsm(signature, targetCpu, body);
+ final AsmFragmentTemplateImpl asmFragmentTemplate = new AsmFragmentTemplateImpl(signature, body, targetCpu, parsedAsmBody.bodyAsm, parsedAsmBody.clobber, parsedAsmBody.cycles);
+ // assertBodyPrint(signature, body, parsedAsmBody.bodyAsm);
+ return asmFragmentTemplate;
+ }
+
+ /**
+ * Assert that the body can be printed and parsed again - adn result in the same parsed value
+ * @param signature The signature
+ * @param body The original string body
+ * @param parsedBody The parsed body
+ */
+ private static void assertBodyPrint(AsmFragmentSignature signature, String body, KickCParser.AsmLinesContext parsedBody) {
+ final String printedBody = AsmTemplateBodyPrinter.print(parsedBody);
+ try {
+ final KickCParser.AsmLinesContext reparsed = AsmParser.parseAsm(printedBody, new StatementSource(parsedBody));
+ if (!isEqual(reparsed, parsedBody))
+ throw new RuntimeException("ASM fragment body mismatch for " + signature.getName() + ".\nACTUAL: '" + normalize(printedBody) + "'\nEXPECTED:'" + normalize(body) + "'\n");
+ } catch (Throwable e) {
+ throw new RuntimeException("Error parsing printed ASM fragment body for " + signature.getName() + ".\nPRINTED: '" + normalize(printedBody) + "'\nORIGINAL:'" + normalize(body) + "'\n");
+ }
+ }
+
+ private static boolean isEqual(ParseTree ctx1, ParseTree ctx2) {
+ if (ctx1 == ctx2) return true;
+ if (ctx2 == null || ctx1.getClass() != ctx2.getClass()) return false;
+ if (ctx1 instanceof TerminalNode)
+ return ctx1.getText().equals(ctx2.getText());
+ if (ctx1.getChildCount() != ctx2.getChildCount()) return false;
+ for (int i = 0; i < ctx1.getChildCount(); i++) {
+ if (!isEqual(ctx1.getChild(i), ctx2.getChild(i))) return false;
+ }
+ return true;
+ }
+
+ private static String normalize(String body) {
+ body = body.replace("\r", "");
+ body = body.replace("\n", " ");
+ body = body.replace(" ", " ");
+ while (body.endsWith(" ")) {
+ body = body.substring(0, body.length() - 1);
+ }
+ return body;
+ }
+
+ /**
+ * Create an inline ASM fragment template.
+ *
+ * @param signature The ASM fragment signature
+ * @param asmBody The parsed ASM body
+ * @param targetCpu The target CPU
+ * @return The ASM fragment template
+ */
+ public static AsmFragmentTemplate inline(AsmFragmentSignature signature, KickCParser.AsmLinesContext asmBody, TargetCpu targetCpu) {
+ return new AsmFragmentTemplateImpl(signature, null, targetCpu, asmBody, null, null);
+ }
+
+ /**
+ * Create a load/store variable
+ *
+ * @param name The name
+ * @param type The type
+ * @param scope The scope
+ * @param memoryArea The memory area (zeropage/main memory)
+ * @param dataSegment The data segment (in main memory)
+ * @return The new PHI-master variable
+ */
+ private static Variable createLoadStore(String name, SymbolType type, Scope scope, Variable.MemoryArea memoryArea, String dataSegment) {
+ return new Variable(name, Variable.Kind.LOAD_STORE, type, scope, memoryArea, dataSegment, null);
+ }
+
+ /**
+ * Create a PHI master variable
+ *
+ * @param name The name
+ * @param type The type
+ * @param scope The scope
+ * @param memoryArea The memory area (zeropage/main memory)
+ * @param dataSegment The data segment (in main memory)
+ * @return The new PHI-master variable
+ */
+ private static Variable createPhiMaster(String name, SymbolType type, Scope scope, Variable.MemoryArea memoryArea, String dataSegment) {
+ return new Variable(name, Variable.Kind.PHI_MASTER, type, scope, memoryArea, dataSegment, null);
+ }
+
+ /**
+ * Result from parsing an ASM string body.
+ */
+ private static class ParsedAsmBody {
+ final KickCParser.AsmLinesContext bodyAsm;
+ final AsmFragmentClobber clobber;
+ final Double cycles;
+
+ public ParsedAsmBody(KickCParser.AsmLinesContext bodyAsm, AsmFragmentClobber clobber, Double cycles) {
+ this.bodyAsm = bodyAsm;
+ this.clobber = clobber;
+ this.cycles = cycles;
+ }
+ }
+
+ public static int parseCount = 0;
+
+ /**
+ * Initialize the fields that require parsing the ASM (bodyAsm, clobber, cycles).
+ */
+ private static ParsedAsmBody parseAsm(AsmFragmentSignature signature, TargetCpu targetCpu, String body) {
+ String signatureName = signature.getName();
+
+ parseCount++;
+ if ((parseCount % 1000) == 0) System.out.println("Parsed " + parseCount + " ASM fragment bodies.");
+
+ // Parse the body ASM
+ final KickCParser.AsmLinesContext bodyAsm = AsmParser.parseAsm(body, new StatementSource(signature + ".asm", 1, 1, body, -1, -1));
+ // Generate a dummy instance to find clobber & cycles
+ ProgramScope scope = new ProgramScope();
+ LinkedHashMap bindings = new LinkedHashMap<>();
+ {
+ Variable master = createPhiMaster("z", SymbolType.BYTE, scope, Variable.MemoryArea.ZEROPAGE_MEMORY, null);
+ Variable v1 = Variable.createPhiVersion(master, 1);
+ v1.setName("z1");
+ Variable v2 = Variable.createPhiVersion(master, 2);
+ v2.setName("z2");
+ Variable v3 = Variable.createPhiVersion(master, 3);
+ v3.setName("z3");
+ Variable v4 = Variable.createPhiVersion(master, 4);
+ v4.setName("z4");
+ Variable v5 = Variable.createPhiVersion(master, 5);
+ v5.setName("z5");
+ Variable v6 = Variable.createPhiVersion(master, 6);
+ v6.setName("z6");
+ v1.setAllocation(new Registers.RegisterZpMem(2, 1));
+ v2.setAllocation(new Registers.RegisterZpMem(4, 1));
+ v3.setAllocation(new Registers.RegisterZpMem(6, 1));
+ v4.setAllocation(new Registers.RegisterZpMem(8, 1));
+ v5.setAllocation(new Registers.RegisterZpMem(9, 1));
+ v6.setAllocation(new Registers.RegisterZpMem(10, 1));
+ if (signatureName.contains("z1")) bindings.put("z1", v1);
+ if (signatureName.contains("z2")) bindings.put("z2", v2);
+ if (signatureName.contains("z3")) bindings.put("z3", v3);
+ if (signatureName.contains("z4")) bindings.put("z4", v4);
+ if (signatureName.contains("z5")) bindings.put("z5", v5);
+ if (signatureName.contains("z6")) bindings.put("z6", v6);
+ }
+ {
+ Variable v1 = createLoadStore("m1", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null);
+ Variable v2 = createLoadStore("m2", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null);
+ Variable v3 = createLoadStore("m3", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null);
+ Variable v4 = createLoadStore("m4", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null);
+ Variable v5 = createLoadStore("m5", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null);
+ Variable v6 = createLoadStore("m6", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null);
+ v1.setAllocation(new Registers.RegisterMainMem(v1.getVariableRef(), 1, null, false));
+ v2.setAllocation(new Registers.RegisterMainMem(v2.getVariableRef(), 1, null, false));
+ v3.setAllocation(new Registers.RegisterMainMem(v3.getVariableRef(), 1, null, false));
+ v4.setAllocation(new Registers.RegisterMainMem(v4.getVariableRef(), 1, null, false));
+ v5.setAllocation(new Registers.RegisterMainMem(v5.getVariableRef(), 1, null, false));
+ v6.setAllocation(new Registers.RegisterMainMem(v6.getVariableRef(), 1, null, false));
+ if (signatureName.contains("m1")) bindings.put("m1", v1);
+ if (signatureName.contains("m2")) bindings.put("m2", v2);
+ if (signatureName.contains("m3")) bindings.put("m3", v3);
+ if (signatureName.contains("m4")) bindings.put("m4", v4);
+ if (signatureName.contains("m5")) bindings.put("m5", v5);
+ if (signatureName.contains("m6")) bindings.put("m6", v6);
+ }
+ if (signatureName.contains("c1")) bindings.put("c1", new ConstantInteger(310L));
+ if (signatureName.contains("c2")) bindings.put("c2", new ConstantInteger(320L));
+ if (signatureName.contains("c3")) bindings.put("c3", new ConstantInteger(330L));
+ if (signatureName.contains("c4")) bindings.put("c4", new ConstantInteger(340L));
+ if (signatureName.contains("c5")) bindings.put("c5", new ConstantInteger(350L));
+ if (signatureName.contains("c6")) bindings.put("c6", new ConstantInteger(360L));
+ if (signatureName.contains("la1")) bindings.put("la1", new Label("@1", scope, true));
+ if (signatureName.startsWith("call_")) bindings.put("la1", new Label("@1", scope, true));
+ final AsmFragmentTemplateImpl fragmentTemplate = new AsmFragmentTemplateImpl(signature, body, targetCpu, bodyAsm, null, null);
+ AsmFragmentInstance fragmentInstance =
+ new AsmFragmentInstance(new Program(), signature, ScopeRef.ROOT, fragmentTemplate, bindings);
+ AsmProgram asm = new AsmProgram(targetCpu);
+ asm.startChunk(ScopeRef.ROOT, null, signature.getName());
+ fragmentInstance.generate(asm);
+ CpuClobber cpuClobber = asm.getClobber();
+ asm.addStash();
+ return new ParsedAsmBody(bodyAsm, new AsmFragmentClobber(cpuClobber), asm.getCycles());
}
/**
@@ -29,10 +227,10 @@ public class AsmFragmentTemplateParser {
* @return The body with fixed newlines
*/
public static String fixNewlines(String body) {
- body = body.replace("\r", "");
- while (body.length() > 0 && body.charAt(body.length() - 1) == '\n') {
- body = body.substring(0, body.length() - 1);
- }
- return body;
+ body = body.replace("\r", "");
+ while (body.length() > 0 && body.charAt(body.length() - 1) == '\n') {
+ body = body.substring(0, body.length() - 1);
+ }
+ return body;
}
}
diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmTemplateBodyPrinter.java b/src/main/java/dk/camelot64/kickc/fragment/AsmTemplateBodyPrinter.java
new file mode 100644
index 000000000..bbbee9939
--- /dev/null
+++ b/src/main/java/dk/camelot64/kickc/fragment/AsmTemplateBodyPrinter.java
@@ -0,0 +1,167 @@
+package dk.camelot64.kickc.fragment;
+
+import dk.camelot64.kickc.parser.KickCParser;
+import dk.camelot64.kickc.parser.KickCParserBaseVisitor;
+import org.antlr.v4.runtime.tree.TerminalNode;
+
+import java.util.List;
+
+/**
+ * Prints ASM template body as string.
+ */
+public class AsmTemplateBodyPrinter extends KickCParserBaseVisitor {
+
+ private StringBuffer out;
+
+ private AsmTemplateBodyPrinter(StringBuffer out) {
+ this.out = out;
+ }
+
+ public static String print(KickCParser.AsmLinesContext asmBody) {
+ final StringBuffer stringBuffer = new StringBuffer();
+ final AsmTemplateBodyPrinter bodyPrinter = new AsmTemplateBodyPrinter(stringBuffer);
+ bodyPrinter.visit(asmBody);
+ return stringBuffer.toString();
+ }
+
+ private void printTags(List tags) {
+ tags.stream().forEach(tag -> out.append(" ").append(tag));
+ }
+
+ @Override
+ public Object visitAsmLabelName(KickCParser.AsmLabelNameContext ctx) {
+ out.append(ctx.ASM_NAME()).append(ctx.ASM_COLON());
+ final List tags = ctx.ASM_TAG();
+ printTags(tags);
+ out.append("\n");
+ return null;
+ }
+
+
+ @Override
+ public Object visitAsmLabelReplace(KickCParser.AsmLabelReplaceContext ctx) {
+ out.append(ctx.ASM_CURLY_BEGIN());
+ out.append(ctx.ASM_NAME());
+ out.append(ctx.ASM_CURLY_END());
+ out.append(ctx.ASM_COLON());
+ printTags(ctx.ASM_TAG());
+ out.append("\n");
+ return null;
+ }
+
+ @Override
+ public Object visitAsmLabelMulti(KickCParser.AsmLabelMultiContext ctx) {
+ out.append(ctx.ASM_MULTI_NAME());
+ out.append(ctx.ASM_COLON());
+ printTags(ctx.ASM_TAG());
+ out.append("\n");
+ return null;
+ }
+
+ @Override
+ public Object visitAsmBytes(KickCParser.AsmBytesContext ctx) {
+ printTags(ctx.ASM_TAG());
+ return null;
+ }
+
+ @Override
+ public Object visitAsmInstruction(KickCParser.AsmInstructionContext ctx) {
+ out.append(ctx.ASM_MNEMONIC());
+ if (ctx.asmParamMode() != null) {
+ out.append(" ");
+ this.visit(ctx.asmParamMode());
+ }
+ printTags(ctx.ASM_TAG());
+ out.append("\n");
+ return null;
+ }
+
+ @Override
+ public Object visitAsmModeImm(KickCParser.AsmModeImmContext ctx) {
+ out.append(ctx.ASM_IMM());
+ this.visit(ctx.asmExpr());
+ return null;
+ }
+
+ @Override
+ public Object visitAsmModeAbs(KickCParser.AsmModeAbsContext ctx) {
+ this.visit(ctx.asmExpr());
+ return null;
+ }
+
+ @Override
+ public Object visitAsmModeAbs2(KickCParser.AsmModeAbs2Context ctx) {
+ this.visit(ctx.asmExpr(0));
+ out.append(ctx.ASM_COMMA());
+ this.visit(ctx.asmExpr(1));
+ return null;
+ }
+
+ @Override
+ public Object visitAsmModeInd(KickCParser.AsmModeIndContext ctx) {
+ out.append(ctx.ASM_PAR_BEGIN());
+ this.visit(ctx.asmExpr());
+ out.append(ctx.ASM_PAR_END());
+ return null;
+ }
+
+ @Override
+ public Object visitAsmModeIndIdxXY(KickCParser.AsmModeIndIdxXYContext ctx) {
+ out.append(ctx.ASM_PAR_BEGIN());
+ this.visit(ctx.asmExpr());
+ out.append(ctx.ASM_PAR_END());
+ out.append(ctx.ASM_COMMA());
+ out.append(ctx.ASM_NAME());
+ return null;
+ }
+
+ @Override
+ public Object visitAsmExprPar(KickCParser.AsmExprParContext ctx) {
+ out.append(ctx.ASM_BRACKET_BEGIN());
+ this.visit(ctx.asmExpr());
+ out.append(ctx.ASM_BRACKET_END());
+ return null;
+ }
+
+ @Override
+ public Object visitAsmExprInt(KickCParser.AsmExprIntContext ctx) {
+ out.append(ctx.ASM_NUMBER());
+ return null;
+ }
+
+ @Override
+ public Object visitAsmExprLabel(KickCParser.AsmExprLabelContext ctx) {
+ out.append(ctx.ASM_NAME());
+ return null;
+ }
+
+ @Override
+ public Object visitAsmExprLabelRel(KickCParser.AsmExprLabelRelContext ctx) {
+ out.append(ctx.ASM_MULTI_REL());
+ out.append(" ");
+ return null;
+ }
+
+ @Override
+ public Object visitAsmExprUnary(KickCParser.AsmExprUnaryContext ctx) {
+ out.append((TerminalNode) ctx.getChild(0));
+ this.visit(ctx.asmExpr());
+ return null;
+ }
+
+ @Override
+ public Object visitAsmExprBinary(KickCParser.AsmExprBinaryContext ctx) {
+ this.visit(ctx.asmExpr(0));
+ out.append((TerminalNode) ctx.getChild(1));
+ this.visit(ctx.asmExpr(1));
+ return null;
+ }
+
+ @Override
+ public Object visitAsmExprReplace(KickCParser.AsmExprReplaceContext ctx) {
+ out.append(ctx.ASM_CURLY_BEGIN());
+ out.append(ctx.ASM_NAME());
+ out.append(ctx.ASM_CURLY_END());
+ return null;
+ }
+}
diff --git a/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentTemplateSynthesisRuleRegex.java b/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentTemplateSynthesisRuleRegex.java
index bb1328c6a..a22be95e2 100644
--- a/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentTemplateSynthesisRuleRegex.java
+++ b/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentTemplateSynthesisRuleRegex.java
@@ -1,7 +1,6 @@
package dk.camelot64.kickc.fragment.synthesis;
import dk.camelot64.kickc.fragment.AsmFragmentTemplate;
-import dk.camelot64.kickc.fragment.AsmFragmentTemplateImpl;
import dk.camelot64.kickc.fragment.AsmFragmentTemplateParser;
import dk.camelot64.kickc.fragment.signature.AsmFragmentSignature;
@@ -154,7 +153,7 @@ class AsmFragmentTemplateSynthesisRuleRegex implements AsmFragmentTemplateSynthe
if(newFragment.length() > 0 && newFragment.charAt(newFragment.length() - 1) == '\n') {
newFragment = new StringBuilder(newFragment.substring(0, newFragment.length() - 1));
}
- return AsmFragmentTemplateParser.create(signature, newFragment.toString(), subTemplate.getTargetCpu());
+ return AsmFragmentTemplateParser.parse(signature, newFragment.toString(), subTemplate.getTargetCpu());
}
static String regexpRewriteSignature(String signature, String match, String replace) {
diff --git a/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentTemplateSynthesizer.java b/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentTemplateSynthesizer.java
index 81e9fd94e..f692fe9d0 100644
--- a/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentTemplateSynthesizer.java
+++ b/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentTemplateSynthesizer.java
@@ -365,7 +365,7 @@ public class AsmFragmentTemplateSynthesizer {
CharStream fragmentCharStream = CharStreams.fromStream(fragmentStream);
body = AsmFragmentTemplateParser.fixNewlines(fragmentCharStream.toString());
}
- final AsmFragmentTemplate fragment = AsmFragmentTemplateParser.create(signature, body, targetCpu);
+ final AsmFragmentTemplate fragment = AsmFragmentTemplateParser.parse(signature, body, targetCpu);
return new AsmFragmentSynthesisResult(fragment, true, false, null, new ArrayList<>());
} catch (IOException e) {
throw new RuntimeException("Error loading fragment file " + signature, e);
diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java
index 8b3a48298..b538367eb 100644
--- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java
+++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java
@@ -894,12 +894,13 @@ public class Pass4CodeGeneration {
} else if (statement instanceof StatementAsm) {
StatementAsm statementAsm = (StatementAsm) statement;
HashMap bindings = new HashMap<>();
+ final AsmFragmentSignature.Singleton signature = new AsmFragmentSignature.Singleton("INLINE");
AsmFragmentInstance asmFragmentInstance =
new AsmFragmentInstance(
program,
- new AsmFragmentSignature.Singleton("INLINE"),
+ signature,
block.getScope(),
- new AsmFragmentTemplateImpl(statementAsm.getAsmLines(), program.getTargetCpu()),
+ AsmFragmentTemplateParser.inline(signature, statementAsm.getAsmLines(), program.getTargetCpu()),
bindings);
asmFragmentInstance.generate(asm);
AsmChunk currentChunk = asm.getCurrentChunk();
diff --git a/src/test/java/dk/camelot64/kickc/test/TestFragments.java b/src/test/java/dk/camelot64/kickc/test/TestFragments.java
index 850a99c80..5444d83f1 100644
--- a/src/test/java/dk/camelot64/kickc/test/TestFragments.java
+++ b/src/test/java/dk/camelot64/kickc/test/TestFragments.java
@@ -33,6 +33,11 @@ public class TestFragments {
log.setSysOut(true);
}
+ // @Test
+ // public void testEvilSynthesis() {
+ // testFragmentExists("vbuz1=pbuz2_derefidx_(pbuz3_derefidx_vbuz4)_plus_pbuz5_derefidx_(pbuz6_derefidx_vbuz4)");
+ // }
+
@Test
public void testSynthesis() {
testFragmentExists("pbuz1=pbuz2_plus_pwuc1_derefidx_vbuxx");