1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-10-11 12:23:45 +00:00

Refactoring fragment synthesis to allow scoring of files & synthesis.

This commit is contained in:
jespergravgaard 2017-12-30 19:22:44 +01:00
parent 58c206cef4
commit 203af62173
8 changed files with 890 additions and 842 deletions

View File

@ -2,8 +2,11 @@
<project version="4">
<component name="ProjectCodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value />
<value>
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
<option name="JD_DO_NOT_WRAP_ONE_LINE_COMMENTS" value="true" />
</value>
</option>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default (1)" />
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</component>
</project>

View File

@ -1,6 +1,6 @@
package dk.camelot64.kickc.asm;
import dk.camelot64.kickc.fragment.AsmFragment;
import dk.camelot64.kickc.fragment.AsmFormat;
/** A labelled numeric data directive. */
public class AsmDataFill implements AsmLine {
@ -38,7 +38,7 @@ public class AsmDataFill implements AsmLine {
StringBuilder asm = new StringBuilder();
asm.append(label+": ");
asm.append(".fill ");
asm.append(AsmFragment.getAsmNumber(size*type.bytes));
asm.append(AsmFormat.getAsmNumber(size*type.bytes));
asm.append(", ");
asm.append(fillValue);
return asm.toString();

View File

@ -1,6 +1,6 @@
package dk.camelot64.kickc.asm;
import dk.camelot64.kickc.fragment.AsmFragment;
import dk.camelot64.kickc.fragment.AsmFormat;
/** A label declaration .label lbl = val */
public class AsmLabelDecl implements AsmLine {
@ -26,7 +26,7 @@ public class AsmLabelDecl implements AsmLine {
@Override
public String getAsm() {
return ".label "+name+" = "+ AsmFragment.getAsmNumber(address);
return ".label "+name+" = "+ AsmFormat.getAsmNumber(address);
}
@Override

View File

@ -1,7 +1,6 @@
package dk.camelot64.kickc.asm;
import dk.camelot64.kickc.NumberParser;
import dk.camelot64.kickc.fragment.AsmFragment;
import dk.camelot64.kickc.fragment.AsmFormat;
/** Set the program counter */
public class AsmSetPc implements AsmLine {
@ -27,7 +26,7 @@ public class AsmSetPc implements AsmLine {
@Override
public String getAsm() {
return ".pc = "+ AsmFragment.getAsmNumber(address)+" \""+name+"\"";
return ".pc = "+ AsmFormat.getAsmNumber(address)+" \""+name+"\"";
}
@Override

View File

@ -0,0 +1,142 @@
package dk.camelot64.kickc.fragment;
import dk.camelot64.kickc.model.*;
/** Formatting of numbers, constants, names and more for KickAssembler */
public class AsmFormat {
/**
* Get ASM code for a constant value
*
* @param value The constant value
* @param precedence The precedence of the outer expression operator. Used to generate perenthesis when needed.
* @param codeScope The scope containing the code being generated. Used for adding scope to the name when needed (eg. line.x1 when referencing x1 variable inside line scope from outside line scope).
* @return The ASM string representing the constant value
*/
public static String getAsmConstant(Program program, ConstantValue value, int precedence, ScopeRef codeScope) {
if (value instanceof ConstantRef) {
ConstantVar constantVar = program.getScope().getConstant((ConstantRef) value);
String asmName = constantVar.getAsmName() == null ? constantVar.getLocalName() : constantVar.getAsmName();
return getAsmParamName(constantVar.getScope().getRef(), asmName, codeScope);
} else if (value instanceof ConstantInteger) {
return getAsmNumber(((ConstantInteger) value).getNumber());
} else if (value instanceof ConstantChar) {
return "'" + ((ConstantChar) value).getValue() + "'";
} else if (value instanceof ConstantString) {
return "\"" + ((ConstantString) value).getValue() + "\"";
} else if (value instanceof ConstantUnary) {
ConstantUnary unary = (ConstantUnary) value;
Operator operator = unary.getOperator();
boolean parenthesis = operator.getPrecedence() > precedence;
return (parenthesis ? "(" : "") +
getAsmConstantUnary(program, codeScope, operator, unary.getOperand(), precedence) +
(parenthesis ? ")" : "");
} else if (value instanceof ConstantBinary) {
ConstantBinary binary = (ConstantBinary) value;
Operator operator = binary.getOperator();
boolean parenthesis = operator.getPrecedence() > precedence;
return
(parenthesis ? "(" : "") +
getAsmConstant(program, binary.getLeft(), operator.getPrecedence(), codeScope) +
operator.getOperator() +
getAsmConstant(program, binary.getRight(), operator.getPrecedence(), codeScope) +
(parenthesis ? ")" : "");
} else {
throw new RuntimeException("Constant type not supported " + value);
}
}
/**
* Get ASM code for a constant unary expression
*
* @param program The program
* @param codeScope The scope containing the code being generated. Used for adding scope to the name when needed (eg. line.x1 when referencing x1 variable inside line scope from outside line scope).
* @param operator The unary operator
* @param operand The operand of the unary expression
* @return The ASM string representing the constant value
*/
private static String getAsmConstantUnary(Program program, ScopeRef codeScope, Operator operator, ConstantValue operand, int outerPrecedence) {
if (Operator.CAST_BYTE.equals(operator) || Operator.CAST_SBYTE.equals(operator)) {
SymbolType operandType = SymbolTypeInference.inferType(program.getScope(), operand);
if (SymbolType.isByte(operandType) || SymbolType.isSByte(operandType)) {
// No cast needed
return getAsmConstant(program, operand, outerPrecedence, codeScope);
} else {
return "$ff & " + getAsmConstant(program, operand, Operator.BOOL_AND.getPrecedence(), codeScope);
}
} else if (Operator.CAST_WORD.equals(operator) || Operator.CAST_SWORD.equals(operator) || Operator.CAST_PTRBY.equals(operator)) {
SymbolType operandType = SymbolTypeInference.inferType(program.getScope(), operand);
if (SymbolType.isWord(operandType) || SymbolType.isSWord(operandType)) {
// No cast needed
return getAsmConstant(program, operand, outerPrecedence, codeScope);
} else {
return "$ffff & " + getAsmConstant(program, operand, Operator.BOOL_AND.getPrecedence(), codeScope);
}
} else if (Operator.INCREMENT.equals(operator)) {
return getAsmConstant(program, operand, Operator.PLUS.getPrecedence(), codeScope) + "+1";
} else if (Operator.DECREMENT.equals(operator)) {
return getAsmConstant(program, operand, Operator.PLUS.getPrecedence(), codeScope) + "-1";
} else {
return operator.getOperator() +
getAsmConstant(program, operand, operator.getPrecedence(), codeScope);
}
}
public static String getAsmNumber(Number number) {
if (number instanceof Integer) {
if (number.intValue() >= 0 && number.intValue() <= 9) {
return String.format("%d", number.intValue());
} else {
return String.format("$%x", number);
}
}
throw new RuntimeException("Unsupported number type " + number);
}
/**
* Get the ASM parameter for a specific bound constant/ variable
*
* @param varScopeRef The scope containing the var/const
* @param asmName The ASM name of the variable (local name or specific ASM name).
* @param codeScopeRef The scope containing the code being generated. Used for adding scope to the name when needed (eg. line.x1 when referencing x1 variable inside line scope from outside line scope).
* @return The ASM parameter to use in the ASM code
*/
static String getAsmParamName(ScopeRef varScopeRef, String asmName, ScopeRef codeScopeRef) {
if (!varScopeRef.equals(codeScopeRef) && varScopeRef.getFullName().length() > 0) {
String param = varScopeRef.getFullName() + "." + asmName
.replace('@', 'b')
.replace(':', '_')
.replace("#", "_")
.replace("$", "_");
//param = ""+((Registers.RegisterZp) register).getZp();
return param;
} else {
String param = asmName.replace('@', 'b').replace(':', '_').replace("#", "_").replace("$", "_");
//param = ""+((Registers.RegisterZp) register).getZp();
return param;
}
}
/**
* Get the ASM parameter for a specific bound variable
*
* @param boundVar The variable
* @return The ASM parameter to use in the ASM code
*/
static String getAsmParamName(Variable boundVar, ScopeRef codeScopeRef) {
ScopeRef varScopeRef = boundVar.getScope().getRef();
String asmName = boundVar.getAsmName() == null ? boundVar.getLocalName() : boundVar.getAsmName();
return getAsmParamName(varScopeRef, asmName, codeScopeRef);
}
/**
* Get the ASM parameter for a specific bound constant
*
* @param boundVar The constant
* @return The ASM parameter to use in the ASM code
*/
private static String getAsmParamName(ConstantVar boundVar, ScopeRef codeScopeRef) {
ScopeRef varScopeRef = boundVar.getScope().getRef();
String asmName = boundVar.getAsmName() == null ? boundVar.getLocalName() : boundVar.getAsmName();
return getAsmParamName(varScopeRef, asmName, codeScopeRef);
}
}

View File

@ -9,459 +9,311 @@ import dk.camelot64.kickc.parser.KickCParser;
import java.util.ArrayList;
import java.util.Map;
/**
* Code Generation Fragment that can handle loading of fragment file and binding of values / registers
*/
/** ASM Code Fragment with register/variable bindings that can be used for generating ASM code for a specific statement . */
public class AsmFragment {
/**
* The symbol table.
*/
private Program program;
/** The symbol table. */
private Program program;
/**
* The name of the fragment used in error messages.
*/
private String name;
/** The name of the fragment used in error messages. */
private String name;
/**
* The fragment template ASM code.
*/
private KickCParser.AsmLinesContext fragmentFile;
/** The fragment template ASM code. */
private KickCParser.AsmLinesContext fragmentFile;
/**
* Binding of named values in the fragment to values (constants, variables, ...) .
*/
private Map<String, Value> bindings;
/** Binding of named values in the fragment to values (constants, variables, ...) . */
private Map<String, Value> bindings;
/**
* The scope containing the fragment. Used when referencing symbols defined in other scopes.
*/
private ScopeRef codeScopeRef;
/** The scope containing the fragment. Used when referencing symbols defined in other scopes. */
private ScopeRef codeScopeRef;
public AsmFragment(
Program program,
String name,
ScopeRef codeScopeRef,
KickCParser.AsmLinesContext fragmentFile,
Map<String, Value> bindings) {
this.program = program;
this.name = name;
this.fragmentFile = fragmentFile;
this.bindings = bindings;
this.codeScopeRef = codeScopeRef;
}
public AsmFragment(
Program program,
String name,
ScopeRef codeScopeRef,
KickCParser.AsmLinesContext fragmentFile,
Map<String, Value> bindings) {
this.program = program;
this.name = name;
this.fragmentFile = fragmentFile;
this.bindings = bindings;
this.codeScopeRef = codeScopeRef;
}
public Value getBinding(String name) {
return bindings.get(name);
}
public Value getBinding(String name) {
return bindings.get(name);
}
/**
* Get the value to getReplacement a bound name with from the fragment signature
*
* @param name The name of the bound value in the fragment
* @return The bound value to use in the generated ASM code
*/
public AsmParameter getBoundValue(String name) {
Value boundValue = null;
if(name.length()==2) {
// Short name!
for (String boundName : bindings.keySet()) {
if(boundName.substring(boundName.length()-2).equals(name)) {
boundValue = getBinding(boundName);
break;
/**
* Get the value to replace a bound name with from the fragment signature
*
* @param name The name of the bound value in the fragment
* @return The bound value to use in the generated ASM code
*/
public AsmParameter getBoundValue(String name) {
Value boundValue = null;
if (name.length() == 2) {
// Short name!
for (String boundName : bindings.keySet()) {
if (boundName.substring(boundName.length() - 2).equals(name)) {
boundValue = getBinding(boundName);
break;
}
}
}
} else {
// Long name
boundValue = getBinding(name);
}
} else {
// Long name
boundValue = getBinding(name);
}
if (boundValue == null) {
throw new RuntimeException("Binding '" + name + "' not found in fragment " + this.name + ".asm");
}
if (boundValue instanceof Variable) {
Variable boundVar = (Variable) boundValue;
Registers.Register register = boundVar.getAllocation();
if (register != null && register instanceof Registers.RegisterZp) {
return new AsmParameter(getAsmParamName(boundVar), true);
} else {
throw new RuntimeException("Register Type not implemented " + register);
}
} else if (boundValue instanceof ConstantVar) {
ConstantVar constantVar = (ConstantVar) boundValue;
String constantValueAsm = getAsmConstant(program, constantVar.getRef(), 99, codeScopeRef);
boolean constantValueZp = SymbolType.BYTE.equals(constantVar.getType(program.getScope()));
return new AsmParameter(constantValueAsm, constantValueZp);
} else if (boundValue instanceof ConstantValue) {
ConstantValue boundConst = (ConstantValue) boundValue;
String constantValueAsm = getAsmConstant(program, boundConst, 99, codeScopeRef);
boolean constantValueZp = SymbolType.BYTE.equals(boundConst.getType(program.getScope()));
return new AsmParameter(constantValueAsm, constantValueZp);
} else if (boundValue instanceof Label) {
String param = ((Label) boundValue).getLocalName().replace('@', 'b').replace(':', '_').replace("$", "_");
return new AsmParameter(param, false);
} else {
throw new RuntimeException("Bound Value Type not implemented " + boundValue);
}
}
if (boundValue == null) {
throw new RuntimeException("Binding '" + name + "' not found in fragment " + this.name + ".asm");
}
if (boundValue instanceof Variable) {
Variable boundVar = (Variable) boundValue;
Registers.Register register = boundVar.getAllocation();
if (register != null && register instanceof Registers.RegisterZp) {
return new AsmParameter(AsmFormat.getAsmParamName(boundVar, codeScopeRef), true);
} else {
throw new RuntimeException("Register Type not implemented " + register);
}
} else if (boundValue instanceof ConstantVar) {
ConstantVar constantVar = (ConstantVar) boundValue;
String constantValueAsm = AsmFormat.getAsmConstant(program, constantVar.getRef(), 99, codeScopeRef);
boolean constantValueZp = SymbolType.BYTE.equals(constantVar.getType(program.getScope()));
return new AsmParameter(constantValueAsm, constantValueZp);
} else if (boundValue instanceof ConstantValue) {
ConstantValue boundConst = (ConstantValue) boundValue;
String constantValueAsm = AsmFormat.getAsmConstant(program, boundConst, 99, codeScopeRef);
boolean constantValueZp = SymbolType.BYTE.equals(boundConst.getType(program.getScope()));
return new AsmParameter(constantValueAsm, constantValueZp);
} else if (boundValue instanceof Label) {
String param = ((Label) boundValue).getLocalName().replace('@', 'b').replace(':', '_').replace("$", "_");
return new AsmParameter(param, false);
} else {
throw new RuntimeException("Bound Value Type not implemented " + boundValue);
}
}
/**
* Get ASM code for a constant value
*
* @param value The constant value
* @param precedence The precedence of the outer expression operator. Used to generate perenthesis when needed.
* @param codeScope The scope containing the code being generated. Used for adding scope to the name when needed (eg. line.x1 when referencing x1 variable inside line scope from outside line scope).
* @return The ASM string representing the constant value
*/
public static String getAsmConstant(Program program, ConstantValue value, int precedence, ScopeRef codeScope) {
if (value instanceof ConstantRef) {
ConstantVar constantVar = program.getScope().getConstant((ConstantRef) value);
String asmName = constantVar.getAsmName() == null ? constantVar.getLocalName() : constantVar.getAsmName();
return getAsmParamName(constantVar.getScope().getRef(), asmName, codeScope);
} else if (value instanceof ConstantInteger) {
return getAsmNumber(((ConstantInteger) value).getNumber());
} else if (value instanceof ConstantChar) {
return "'" + ((ConstantChar) value).getValue() + "'";
} else if (value instanceof ConstantString) {
return "\"" + ((ConstantString) value).getValue() + "\"";
} else if (value instanceof ConstantUnary) {
ConstantUnary unary = (ConstantUnary) value;
Operator operator = unary.getOperator();
boolean parenthesis = operator.getPrecedence() > precedence;
return (parenthesis ? "(" : "") +
getAsmConstantUnary(program, codeScope, operator, unary.getOperand(), precedence) +
(parenthesis ? ")" : "");
} else if (value instanceof ConstantBinary) {
ConstantBinary binary = (ConstantBinary) value;
Operator operator = binary.getOperator();
boolean parenthesis = operator.getPrecedence() > precedence;
return
(parenthesis ? "(" : "") +
getAsmConstant(program, binary.getLeft(), operator.getPrecedence(), codeScope) +
operator.getOperator() +
getAsmConstant(program, binary.getRight(), operator.getPrecedence(), codeScope) +
(parenthesis ? ")" : "");
} else {
throw new RuntimeException("Constant type not supported " + value);
}
}
public String getFragmentName() {
return name;
}
/**
* Get ASM code for a constant unary expression
*
* @param program The program
* @param codeScope The scope containing the code being generated. Used for adding scope to the name when needed (eg. line.x1 when referencing x1 variable inside line scope from outside line scope).
* @param operator The unary operator
* @param operand The operand of the unary expression
* @return The ASM string representing the constant value
*/
private static String getAsmConstantUnary(Program program, ScopeRef codeScope, Operator operator, ConstantValue operand, int outerPrecedence) {
if (Operator.CAST_BYTE.equals(operator) || Operator.CAST_SBYTE.equals(operator)) {
SymbolType operandType = SymbolTypeInference.inferType(program.getScope(), operand);
if(SymbolType.isByte(operandType) || SymbolType.isSByte(operandType)) {
// No cast needed
return getAsmConstant(program, operand, outerPrecedence, codeScope);
} else {
return "$ff & " + getAsmConstant(program, operand, Operator.BOOL_AND.getPrecedence(), codeScope);
}
} else if (Operator.CAST_WORD.equals(operator) || Operator.CAST_SWORD.equals(operator) || Operator.CAST_PTRBY.equals(operator)) {
SymbolType operandType = SymbolTypeInference.inferType(program.getScope(), operand);
if(SymbolType.isWord(operandType) || SymbolType.isSWord(operandType)) {
// No cast needed
return getAsmConstant(program, operand, outerPrecedence, codeScope);
} else {
return "$ffff & " + getAsmConstant(program, operand, Operator.BOOL_AND.getPrecedence(), codeScope);
}
} else if (Operator.INCREMENT.equals(operator) ) {
return getAsmConstant(program, operand, Operator.PLUS.getPrecedence(), codeScope)+"+1";
} else if (Operator.DECREMENT.equals(operator) ) {
return getAsmConstant(program, operand, Operator.PLUS.getPrecedence(), codeScope)+"-1";
} else {
return operator.getOperator() +
getAsmConstant(program, operand, operator.getPrecedence(), codeScope);
}
}
/**
* A parameter of an ASM instruction from a bound value.
*/
public static class AsmParameter {
public static String getAsmNumber(Number number) {
if (number instanceof Integer) {
if (number.intValue() >= 0 && number.intValue() <= 9) {
return String.format("%d", number.intValue());
} else {
return String.format("$%x", number);
}
}
throw new RuntimeException("Unsupported number type " + number);
}
private String param;
private boolean zp;
/**
* Get the ASM parameter for a specific bound variable
*
* @param boundVar The variable
* @return The ASM parameter to use in the ASM code
*/
private String getAsmParamName(Variable boundVar) {
ScopeRef varScopeRef = boundVar.getScope().getRef();
String asmName = boundVar.getAsmName() == null ? boundVar.getLocalName() : boundVar.getAsmName();
return getAsmParamName(varScopeRef, asmName, codeScopeRef);
}
public AsmParameter(String param, boolean zp) {
this.param = param;
this.zp = zp;
}
/**
* Get the ASM parameter for a specific bound constant
*
* @param boundVar The constant
* @return The ASM parameter to use in the ASM code
*/
private String getAsmParamName(ConstantVar boundVar) {
ScopeRef varScopeRef = boundVar.getScope().getRef();
String asmName = boundVar.getAsmName() == null ? boundVar.getLocalName() : boundVar.getAsmName();
return getAsmParamName(varScopeRef, asmName, codeScopeRef);
}
public String getParam() {
return param;
}
/**
* Get the ASM parameter for a specific bound constant/ variable
*
* @param varScopeRef The scope containing the var/const
* @param asmName The ASM name of the variable (local name or specific ASM name).
* @param codeScopeRef The scope containing the code being generated. Used for adding scope to the name when needed (eg. line.x1 when referencing x1 variable inside line scope from outside line scope).
* @return The ASM parameter to use in the ASM code
*/
private static String getAsmParamName(ScopeRef varScopeRef, String asmName, ScopeRef codeScopeRef) {
if (!varScopeRef.equals(codeScopeRef) && varScopeRef.getFullName().length() > 0) {
String param = varScopeRef.getFullName() + "." + asmName
.replace('@', 'b')
.replace(':', '_')
.replace("#", "_")
.replace("$", "_");
//param = ""+((Registers.RegisterZp) register).getZp();
return param;
} else {
String param = asmName.replace('@', 'b').replace(':', '_').replace("#", "_").replace("$", "_");
//param = ""+((Registers.RegisterZp) register).getZp();
return param;
}
}
public boolean isZp() {
return zp;
}
}
public String getName() {
return name;
}
/**
* Generate assembler code for the assembler fragment.
*
* @param asm The assembler sequence to generate into.
*/
public void generate(AsmProgram asm) {
AsmSequenceGenerator asmSequenceGenerator = new AsmSequenceGenerator(name, this, asm);
asmSequenceGenerator.generate(fragmentFile);
}
/**
* A parameter of an ASM instruction from a bound value.
*/
public static class AsmParameter {
private static class AsmSequenceGenerator extends KickCBaseVisitor {
private String param;
private boolean zp;
private String name;
private AsmProgram program;
private AsmFragment bindings;
public AsmParameter(String param, boolean zp) {
this.param = param;
this.zp = zp;
}
public AsmSequenceGenerator(String name, AsmFragment bindings, AsmProgram program) {
this.name = name;
this.bindings = bindings;
this.program = program;
}
public String getParam() {
return param;
}
public AsmProgram getProgram() {
return program;
}
public boolean isZp() {
return zp;
}
}
public void generate(KickCParser.AsmLinesContext context) {
this.visit(context);
}
/**
* Generate assembler code for the assembler fragment.
*
* @param asm The assembler sequence to generate into.
*/
public void generate(AsmProgram asm) {
AsmSequenceGenerator asmSequenceGenerator = new AsmSequenceGenerator(name, this, asm);
asmSequenceGenerator.generate(fragmentFile);
}
@Override
public Object visitAsmLabel(KickCParser.AsmLabelContext ctx) {
program.addLine(new AsmLabel(ctx.getChild(0).getText()));
return null;
}
private static class AsmSequenceGenerator extends KickCBaseVisitor {
@Override
public Object visitAsmBytes(KickCParser.AsmBytesContext ctx) {
ArrayList<String> values = new ArrayList<>();
for (int i = 1; i < ctx.getChildCount(); i = i + 2) {
values.add(ctx.getChild(i).getText());
}
program.addLine(new AsmDataNumeric(null, AsmDataNumeric.Type.BYTE, values));
return null;
}
private String name;
private AsmProgram program;
private AsmFragment bindings;
@Override
public Object visitAsmInstruction(KickCParser.AsmInstructionContext ctx) {
KickCParser.AsmParamModeContext paramModeCtx = ctx.asmParamMode();
AsmInstruction instruction;
if (paramModeCtx == null) {
AsmInstructionType type = AsmInstructionSet.getInstructionType(
ctx.MNEMONIC().getText(),
AsmAddressingMode.NON,
false);
instruction = new AsmInstruction(type, null);
} else {
instruction = (AsmInstruction) this.visit(paramModeCtx);
}
if (instruction != null) {
program.addLine(instruction);
} else {
throw new RuntimeException("Error parsing ASM fragment line in dk/camelot64/kickc/fragment/asm/" + name + ".asm\n - Line: " + ctx.getText());
}
return null;
}
public AsmSequenceGenerator(String name, AsmFragment bindings, AsmProgram program) {
this.name = name;
this.bindings = bindings;
this.program = program;
}
@Override
public Object visitAsmModeAbs(KickCParser.AsmModeAbsContext ctx) {
return createAsmInstruction(ctx, ctx.asmExpr(), AsmAddressingMode.ABS);
}
public AsmProgram getProgram() {
return program;
}
@Override
public Object visitAsmModeImm(KickCParser.AsmModeImmContext ctx) {
return createAsmInstruction(ctx, ctx.asmExpr(), AsmAddressingMode.IMM);
}
public void generate(KickCParser.AsmLinesContext context) {
this.visit(context);
}
@Override
public Object visitAsmModeAbsXY(KickCParser.AsmModeAbsXYContext ctx) {
String xy = ctx.getChild(ctx.getChildCount() - 1).getText();
if (xy.equals("x")) {
return createAsmInstruction(ctx, ctx.asmExpr(), AsmAddressingMode.ABX);
} else if (xy.equals("y")) {
return createAsmInstruction(ctx, ctx.asmExpr(), AsmAddressingMode.ABY);
} else {
throw new RuntimeException("Unknown addressing mode " + ctx.getText());
}
}
@Override
public Object visitAsmLabel(KickCParser.AsmLabelContext ctx) {
program.addLine(new AsmLabel(ctx.getChild(0).getText()));
return null;
}
@Override
public Object visitAsmModeIndIdxXY(KickCParser.AsmModeIndIdxXYContext ctx) {
String xy = ctx.getChild(ctx.getChildCount() - 1).getText();
if (xy.equals("y")) {
return createAsmInstruction(ctx, ctx.asmExpr(), AsmAddressingMode.IZY);
} else {
throw new RuntimeException("Unknown addressing mode " + ctx.getText());
}
}
@Override
public Object visitAsmBytes(KickCParser.AsmBytesContext ctx) {
ArrayList<String> values = new ArrayList<>();
for(int i = 1; i < ctx.getChildCount(); i=i+2) {
values.add(ctx.getChild(i).getText());
}
program.addLine(new AsmDataNumeric(null, AsmDataNumeric.Type.BYTE, values));
return null;
}
@Override
public Object visitAsmModeIdxIndXY(KickCParser.AsmModeIdxIndXYContext ctx) {
String xy = ctx.getChild(ctx.getChildCount() - 1).getText();
if (xy.equals("x")) {
return createAsmInstruction(ctx, ctx.asmExpr(), AsmAddressingMode.IZX);
} else {
throw new RuntimeException("Unknown addressing mode " + ctx.getText());
}
}
@Override
public Object visitAsmInstruction(KickCParser.AsmInstructionContext ctx) {
KickCParser.AsmParamModeContext paramModeCtx = ctx.asmParamMode();
AsmInstruction instruction;
if (paramModeCtx == null) {
@Override
public Object visitAsmModeInd(KickCParser.AsmModeIndContext ctx) {
return createAsmInstruction(ctx, ctx.asmExpr(), AsmAddressingMode.IND);
}
private AsmInstruction createAsmInstruction(
KickCParser.AsmParamModeContext ctx,
KickCParser.AsmExprContext exprCtx,
AsmAddressingMode addressingMode) {
KickCParser.AsmInstructionContext instructionCtx = (KickCParser.AsmInstructionContext) ctx.getParent();
String mnemonic = instructionCtx.MNEMONIC().getSymbol().getText();
AsmParameter parameter = (AsmParameter) this.visit(exprCtx);
AsmInstructionType type = AsmInstructionSet.getInstructionType(
ctx.MNEMONIC().getText(),
AsmAddressingMode.NON,
false);
instruction = new AsmInstruction(type, null);
} else {
instruction = (AsmInstruction) this.visit(paramModeCtx);
}
if (instruction != null) {
program.addLine(instruction);
} else {
throw new RuntimeException("Error parsing ASM fragment line in dk/camelot64/kickc/fragment/asm/" + name + ".asm\n - Line: " + ctx.getText());
}
return null;
}
mnemonic,
addressingMode,
parameter.isZp());
if (type == null) {
throw new RuntimeException("Error in " + name + ".asm line " + ctx.getStart().getLine() + " - Instruction type unknown " + mnemonic + " " + addressingMode + " " + parameter);
}
return new AsmInstruction(type, parameter.getParam());
}
@Override
public Object visitAsmModeAbs(KickCParser.AsmModeAbsContext ctx) {
return createAsmInstruction(ctx, ctx.asmExpr(), AsmAddressingMode.ABS);
}
@Override
public AsmParameter visitAsmExprBinary(KickCParser.AsmExprBinaryContext ctx) {
AsmParameter left = (AsmParameter) this.visit(ctx.asmExpr(0));
AsmParameter right = (AsmParameter) this.visit(ctx.asmExpr(1));
String param = "" + left.getParam() + ctx.getChild(1).getText() + right.getParam();
boolean zp = left.isZp() && right.isZp();
return new AsmParameter(param, zp);
}
@Override
public Object visitAsmModeImm(KickCParser.AsmModeImmContext ctx) {
return createAsmInstruction(ctx, ctx.asmExpr(), AsmAddressingMode.IMM);
}
@Override
public AsmParameter visitAsmExprUnary(KickCParser.AsmExprUnaryContext ctx) {
AsmParameter sub = (AsmParameter) this.visit(ctx.asmExpr());
String operator = ctx.getChild(0).getText();
String param = operator + sub.getParam();
boolean isZp = sub.isZp();
if (operator.equals("<") || operator.equals(">")) {
isZp = true;
}
return new AsmParameter(param, isZp);
}
@Override
public Object visitAsmModeAbsXY(KickCParser.AsmModeAbsXYContext ctx) {
String xy = ctx.getChild(ctx.getChildCount() - 1).getText();
if (xy.equals("x")) {
return createAsmInstruction(ctx, ctx.asmExpr(), AsmAddressingMode.ABX);
} else if (xy.equals("y")) {
return createAsmInstruction(ctx, ctx.asmExpr(), AsmAddressingMode.ABY);
} else {
throw new RuntimeException("Unknown addressing mode " + ctx.getText());
}
}
@Override
public AsmParameter visitAsmExprInt(KickCParser.AsmExprIntContext ctx) {
Number number = NumberParser.parseLiteral(ctx.NUMBER().getText());
ConstantInteger intVal = new ConstantInteger(number.intValue());
boolean isZp = SymbolType.isByte(intVal.getType()) || SymbolType.isSByte(intVal.getType());
String param = AsmFormat.getAsmNumber(number);
return new AsmParameter(param, isZp);
}
@Override
public Object visitAsmModeIndIdxXY(KickCParser.AsmModeIndIdxXYContext ctx) {
String xy = ctx.getChild(ctx.getChildCount() - 1).getText();
if (xy.equals("y")) {
return createAsmInstruction(ctx, ctx.asmExpr(), AsmAddressingMode.IZY);
} else {
throw new RuntimeException("Unknown addressing mode " + ctx.getText());
}
}
@Override
public Object visitAsmExprChar(KickCParser.AsmExprCharContext ctx) {
return new AsmParameter(ctx.getText(), true);
}
@Override
public Object visitAsmModeIdxIndXY(KickCParser.AsmModeIdxIndXYContext ctx) {
String xy = ctx.getChild(ctx.getChildCount() - 1).getText();
if (xy.equals("x")) {
return createAsmInstruction(ctx, ctx.asmExpr(), AsmAddressingMode.IZX);
} else {
throw new RuntimeException("Unknown addressing mode " + ctx.getText());
}
}
@Override
public AsmParameter visitAsmExprLabel(KickCParser.AsmExprLabelContext ctx) {
String param = ctx.NAME().getSymbol().getText();
return new AsmParameter(param, false);
}
@Override
public Object visitAsmModeInd(KickCParser.AsmModeIndContext ctx) {
return createAsmInstruction(ctx, ctx.asmExpr(), AsmAddressingMode.IND);
}
@Override
public Object visitAsmExprLabelRel(KickCParser.AsmExprLabelRelContext ctx) {
String param = ctx.ASMREL().getSymbol().getText();
return new AsmParameter(param, false);
}
private AsmInstruction createAsmInstruction(
KickCParser.AsmParamModeContext ctx,
KickCParser.AsmExprContext exprCtx,
AsmAddressingMode addressingMode) {
KickCParser.AsmInstructionContext instructionCtx = (KickCParser.AsmInstructionContext) ctx.getParent();
String mnemonic = instructionCtx.MNEMONIC().getSymbol().getText();
AsmParameter parameter = (AsmParameter) this.visit(exprCtx);
AsmInstructionType type = AsmInstructionSet.getInstructionType(
mnemonic,
addressingMode,
parameter.isZp());
if (type == null) {
throw new RuntimeException("Error in " + name + ".asm line " + ctx.getStart().getLine() + " - Instruction type unknown " + mnemonic + " " + addressingMode + " " + parameter);
}
return new AsmInstruction(type, parameter.getParam());
}
@Override
public AsmParameter visitAsmExprReplace(KickCParser.AsmExprReplaceContext ctx) {
String replaceName = ctx.NAME().getSymbol().getText();
return bindings.getBoundValue(replaceName);
}
}
@Override
public AsmParameter visitAsmExprBinary(KickCParser.AsmExprBinaryContext ctx) {
AsmParameter left = (AsmParameter) this.visit(ctx.asmExpr(0));
AsmParameter right = (AsmParameter) this.visit(ctx.asmExpr(1));
String param = "" + left.getParam() + ctx.getChild(1).getText() + right.getParam();
boolean zp = left.isZp() && right.isZp();
return new AsmParameter(param, zp);
}
public static class AluNotApplicableException extends RuntimeException {
@Override
public AsmParameter visitAsmExprUnary(KickCParser.AsmExprUnaryContext ctx) {
AsmParameter sub = (AsmParameter) this.visit(ctx.asmExpr());
String operator = ctx.getChild(0).getText();
String param = operator + sub.getParam();
boolean isZp = sub.isZp();
if(operator.equals("<") || operator.equals(">")) {
isZp = true;
}
return new AsmParameter(param, isZp);
}
public AluNotApplicableException() {
super("ALU register not appicable.");
}
@Override
public AsmParameter visitAsmExprInt(KickCParser.AsmExprIntContext ctx) {
Number number = NumberParser.parseLiteral(ctx.NUMBER().getText());
ConstantInteger intVal = new ConstantInteger(number.intValue());
boolean isZp = SymbolType.isByte(intVal.getType()) || SymbolType.isSByte(intVal.getType()) ;
String param = getAsmNumber(number);
return new AsmParameter(param, isZp);
}
@Override
public Object visitAsmExprChar(KickCParser.AsmExprCharContext ctx) {
return new AsmParameter(ctx.getText(), true);
}
@Override
public AsmParameter visitAsmExprLabel(KickCParser.AsmExprLabelContext ctx) {
String param = ctx.NAME().getSymbol().getText();
return new AsmParameter(param, false);
}
@Override
public Object visitAsmExprLabelRel(KickCParser.AsmExprLabelRelContext ctx) {
String param = ctx.ASMREL().getSymbol().getText();
return new AsmParameter(param, false);
}
@Override
public AsmParameter visitAsmExprReplace(KickCParser.AsmExprReplaceContext ctx) {
String replaceName = ctx.NAME().getSymbol().getText();
return bindings.getBoundValue(replaceName);
}
}
public static class AluNotApplicableException extends RuntimeException {
public AluNotApplicableException() {
super("ALU register not appicable.");
}
public AluNotApplicableException(String message) {
super(message);
}
}
public AluNotApplicableException(String message) {
super(message);
}
}
}

View File

@ -21,468 +21,519 @@ import java.util.regex.Pattern;
*/
public class AsmFragmentManager {
/**
* Cache for fragment files. Maps signature to the parsed file.
*/
private static Map<String, KickCParser.AsmFileContext> fragmentFileCache = new HashMap<>();
/** Cache for fragment files. Maps signature to the parsed file. */
private static Map<String, KickCParser.AsmLinesContext> fragmentFileCache = new HashMap<>();
private static KickCParser.AsmFileContext UNKNOWN = new KickCParser.AsmFileContext(null, 0);
private static KickCParser.AsmLinesContext UNKNOWN = new KickCParser.AsmLinesContext(null, 0);
public static AsmFragment getFragment(AsmFragmentSignature signature, CompileLog log) {
KickCParser.AsmFileContext fragmentFile = getFragmentFile(signature, log);
AsmFragment fragment = new AsmFragment(
signature.getProgram(),
signature.getSignature(),
signature.getCodeScope(),
fragmentFile.asmLines(),
signature.getBindings());
return fragment;
}
private static KickCParser.AsmFileContext getFragmentFile(AsmFragmentSignature signature, CompileLog log) {
KickCParser.AsmFileContext fragmentCtx = fragmentFileCache.get(signature.getSignature());
if(fragmentCtx == UNKNOWN) {
if(log.isVerboseFragmentLog()) {
log.append("Unknown fragment " + signature.getSignature());
}
throw new UnknownFragmentException(signature.toString());
}
if(fragmentCtx == null) {
FragmentSynthesizer synthesizer = new FragmentSynthesizer(signature, log);
List<CharStream> candidates = synthesizer.loadOrSynthesizeFragment(signature.getSignature());
if(candidates.size() == 0) {
if(log.isVerboseFragmentLog()) {
log.append("Unknown fragment " + signature.toString());
public static AsmFragment getFragment(AsmFragmentSignature signature, CompileLog log) {
KickCParser.AsmLinesContext fragmentAsmLines = fragmentFileCache.get(signature.getSignature());
if (fragmentAsmLines == UNKNOWN) {
if (log.isVerboseFragmentLog()) {
log.append("Unknown fragment " + signature.getSignature());
}
fragmentFileCache.put(signature.getSignature(), UNKNOWN);
throw new UnknownFragmentException(signature.toString());
}
if (fragmentAsmLines == null) {
AsmFragmentTemplateSynthesizer synthesizer = new AsmFragmentTemplateSynthesizer(signature, log);
List<AsmFragmentTemplate> candidates = synthesizer.loadOrSynthesizeFragment(signature.getSignature());
if (candidates.size() == 0) {
if (log.isVerboseFragmentLog()) {
log.append("Unknown fragment " + signature.toString());
}
fragmentFileCache.put(signature.getSignature(), UNKNOWN);
throw new UnknownFragmentException(signature.toString());
}
double minScore = Double.MAX_VALUE;
for(CharStream candidate : candidates) {
KickCParser.AsmFileContext candidateCtx = parseFragment(candidate, signature.getSignature());
AsmFragment fragment = new AsmFragment(
signature.getProgram(),
signature.getSignature(),
signature.getCodeScope(),
candidateCtx.asmLines(),
signature.getBindings());
AsmProgram asm = new AsmProgram();
asm.startSegment(null, signature.toString());
fragment.generate(asm);
double score = asm.getCycles();
if(score < minScore) {
minScore = score;
fragmentCtx = candidateCtx;
}
}
if(log.isVerboseFragmentLog()) {
log.append("Found fragment " + signature + " score: " + minScore + " from " + candidates.size() + " candidates");
}
fragmentFileCache.put(signature.getSignature(), fragmentCtx);
}
return fragmentCtx;
}
/**
* Capable of creating fragments from signatures by loading them or synthesizing them from other smaller fragments.
* <p>
* The synthesizer tries a lot of different combinations and keeps track of what has already been attempted.
*/
public static class FragmentSynthesizer {
/**
* Signature of the fragment being synthesized.
*/
private AsmFragmentSignature signature;
/**
* The log.
*/
private CompileLog log;
public FragmentSynthesizer(AsmFragmentSignature signature, CompileLog log) {
this.signature = signature;
this.log = log;
}
public List<CharStream> loadOrSynthesizeFragment(String signature) {
List<CharStream> candidates = new ArrayList<>();
// Load the fragment from disk
CharStream fragmentCharStream = loadFragment(signature);
if(fragmentCharStream != null) {
candidates.add(fragmentCharStream);
if(log.isVerboseFragmentLog()) {
log.append("Finding fragment "+this.signature.getSignature()+" - Successfully loaded fragment " + signature);
double minScore = Double.MAX_VALUE;
for (AsmFragmentTemplate candidate : candidates) {
KickCParser.AsmLinesContext candidateAsmLines =
parseFragment(candidate.getBody(), signature.getSignature());
AsmFragment candidateFragment = new AsmFragment(
signature.getProgram(),
signature.getSignature(),
signature.getCodeScope(),
candidateAsmLines,
signature.getBindings());
AsmProgram condidateAsm = new AsmProgram();
condidateAsm.startSegment(null, signature.toString());
candidateFragment.generate(condidateAsm);
double score = condidateAsm.getCycles();
if (score < minScore) {
minScore = score;
fragmentAsmLines = candidateAsmLines;
}
}
}
// Synthesize the fragment from other fragments
List<FragmentSynthesis> synths = getFragmentSyntheses();
for(FragmentSynthesis synth : synths) {
List<CharStream> synthesized = synth.synthesize(signature, this);
if(synthesized != null) {
if(log.isVerboseFragmentLog() && synthesized.size() > 0) {
log.append("Finding fragment "+this.signature.getSignature()+" - Successfully synthesized " + synthesized.size() + " fragments " + signature + " (from " + synth.getSubSignature() + ")");
}
candidates.addAll(synthesized);
if (log.isVerboseFragmentLog()) {
log.append("Found fragment " + signature + " score: " + minScore + " from " + candidates.size() + " candidates");
}
}
return candidates;
}
}
fragmentFileCache.put(signature.getSignature(), fragmentAsmLines);
}
return new AsmFragment(
signature.getProgram(),
signature.getSignature(),
signature.getCodeScope(),
fragmentAsmLines,
signature.getBindings());
}
/**
* AsmFragment synthesis based on matching fragment signature and reusing another fragment with added prefix/postfix and some bind-mappings
*/
private static class FragmentSynthesis {
/**
* Capable of creating fragments from signatures by loading them or synthesizing them from other smaller fragments.
* <p>
* The synthesizer tries a lot of different combinations and keeps track of what has already been attempted.
*/
public static class AsmFragmentTemplateSynthesizer {
private String sigMatch;
private String sigAvoid;
private String asmPrefix;
private String sigReplace;
private String asmPostfix;
private Map<String, String> bindMappings;
private boolean mapSignature;
private String subSignature;
/** Signature of the fragment being synthesized. */
private AsmFragmentSignature signature;
public FragmentSynthesis(String sigMatch, String sigAvoid, String asmPrefix, String sigReplace, String asmPostfix, Map<String, String> bindMappings, boolean mapSignature) {
this.sigMatch = sigMatch;
this.sigAvoid = sigAvoid;
this.asmPrefix = asmPrefix;
this.sigReplace = sigReplace;
this.asmPostfix = asmPostfix;
this.bindMappings = bindMappings;
this.mapSignature = mapSignature;
}
/** The log. */
private CompileLog log;
public FragmentSynthesis(String sigMatch, String sigAvoid, String asmPrefix, String sigReplace, String asmPostfix, Map<String, String> bindMappings) {
this(sigMatch, sigAvoid, asmPrefix, sigReplace, asmPostfix, bindMappings, true);
}
/** Caches all asm fragment templates for all encountered signatures. */
private static Map<String, List<AsmFragmentTemplate>> templateCache = new LinkedHashMap<>();
public List<CharStream> synthesize(String signature, FragmentSynthesizer synthesizer) {
ArrayList<CharStream> candidates = new ArrayList<>();
if(signature.matches(sigMatch)) {
if(sigAvoid == null || !signature.matches(sigAvoid)) {
subSignature = regexpRewriteSignature(signature, sigMatch, sigReplace);
if(mapSignature && bindMappings != null) {
// When mapping the signature we do the map replacement in the signature
for(String bound : bindMappings.keySet()) {
subSignature = subSignature.replace(bound, bindMappings.get(bound));
}
}
List<CharStream> subCharStreams = synthesizer.loadOrSynthesizeFragment(subSignature);
for(CharStream subCharStream : subCharStreams) {
if(subCharStream != null) {
StringBuilder newFragment = new StringBuilder();
if(asmPrefix != null) {
newFragment.append(asmPrefix);
}
String subFragment = subCharStream.toString();
if(bindMappings != null) {
if(mapSignature) {
// When mapping the signature we do the reverse replacement in the ASM
List<String> reverse = new ArrayList<>(bindMappings.keySet());
Collections.reverse(reverse);
for(String bound : reverse) {
subFragment = subFragment.replace("{" + bindMappings.get(bound) + "}", "{" + bound + "}");
}
} else {
// When not mapping the signature we do the replacement directly in the ASM
for(String bound : bindMappings.keySet()) {
subFragment = subFragment.replace("{" + bound + "}", "{" + bindMappings.get(bound) + "}");
}
public AsmFragmentTemplateSynthesizer(AsmFragmentSignature signature, CompileLog log) {
this.signature = signature;
this.log = log;
}
public List<AsmFragmentTemplate> loadOrSynthesizeFragment(String signature) {
if (templateCache.get(signature) != null) {
return templateCache.get(signature);
}
List<AsmFragmentTemplate> candidates = new ArrayList<>();
// Load the fragment from disk
CharStream fragmentCharStream = loadFragment(signature);
if (fragmentCharStream != null) {
candidates.add(new AsmFragmentTemplate(signature, fragmentCharStream.toString()));
if (log.isVerboseFragmentLog()) {
log.append("Finding fragment " + this.signature.getSignature() + " - Successfully loaded fragment " + signature);
}
}
// Synthesize the fragment from other fragments
List<AsmFragmentSynthesis> synths = getFragmentSyntheses();
for (AsmFragmentSynthesis synth : synths) {
List<AsmFragmentTemplate> synthesized = synth.synthesize(signature, this);
if (synthesized != null) {
if (log.isVerboseFragmentLog() && synthesized.size() > 0) {
log.append("Finding fragment " + this.signature.getSignature() + " - Successfully synthesized " + synthesized.size() + " fragments " + signature + " (from " + synth.getSubSignature() + ")");
}
candidates.addAll(synthesized);
}
}
templateCache.put(signature, candidates);
return candidates;
}
}
/**
* An ASM fragment template usable for generating KickAssembler code for different bindings.
* The AsmFragmentTemplateSynthesizer can generate multiple different templates usable for a specific fragment signature.
*/
public static class AsmFragmentTemplate {
/** The fragment template signature name. */
private String signature;
/** The fragment template body */
private String body;
/** true if the fragment was loaded from disk. */
boolean loaded;
/** The synthesis that created the fragment. null if the fragment template was loaded. */
private AsmFragmentSynthesis synthesis;
/** The sub fragment template that the synthesis modified to create this. null if the fragment template was loaded. */
private AsmFragmentTemplate subFragment;
public AsmFragmentTemplate(String signature, String body) {
this.signature = signature;
this.body = body;
this.loaded = true;
}
public AsmFragmentTemplate(String signature, String body, AsmFragmentSynthesis synthesis, AsmFragmentTemplate subFragment) {
this.signature = signature;
this.body = body;
this.synthesis = synthesis;
this.subFragment = subFragment;
this.loaded = false;
}
public String getSignature() {
return signature;
}
public String getBody() {
return body;
}
public boolean isLoaded() {
return loaded;
}
public AsmFragmentSynthesis getSynthesis() {
return synthesis;
}
public AsmFragmentTemplate getSubFragment() {
return subFragment;
}
}
/** AsmFragment synthesis mechanism based on matching fragment signature and reusing another fragment with added prefix/postfix and some bind-mappings */
private static class AsmFragmentSynthesis {
private String sigMatch;
private String sigAvoid;
private String asmPrefix;
private String sigReplace;
private String asmPostfix;
private Map<String, String> bindMappings;
private boolean mapSignature;
private String subSignature;
public AsmFragmentSynthesis(String sigMatch, String sigAvoid, String asmPrefix, String sigReplace, String asmPostfix, Map<String, String> bindMappings, boolean mapSignature) {
this.sigMatch = sigMatch;
this.sigAvoid = sigAvoid;
this.asmPrefix = asmPrefix;
this.sigReplace = sigReplace;
this.asmPostfix = asmPostfix;
this.bindMappings = bindMappings;
this.mapSignature = mapSignature;
}
public AsmFragmentSynthesis(String sigMatch, String sigAvoid, String asmPrefix, String sigReplace, String asmPostfix, Map<String, String> bindMappings) {
this(sigMatch, sigAvoid, asmPrefix, sigReplace, asmPostfix, bindMappings, true);
}
public List<AsmFragmentTemplate> synthesize(String signature, AsmFragmentTemplateSynthesizer synthesizer) {
ArrayList<AsmFragmentTemplate> candidates = new ArrayList<>();
if (signature.matches(sigMatch)) {
if (sigAvoid == null || !signature.matches(sigAvoid)) {
subSignature = regexpRewriteSignature(signature, sigMatch, sigReplace);
if (mapSignature && bindMappings != null) {
// When mapping the signature we do the map replacement in the signature
for (String bound : bindMappings.keySet()) {
subSignature = subSignature.replace(bound, bindMappings.get(bound));
}
}
newFragment.append(subFragment);
if(asmPostfix != null) {
newFragment.append("\n");
newFragment.append(asmPostfix);
}
candidates.add(CharStreams.fromString(newFragment.toString()));
}
}
}
List<AsmFragmentTemplate> subFragmentTemplates = synthesizer.loadOrSynthesizeFragment(subSignature);
for (AsmFragmentTemplate subFragmentTemplate : subFragmentTemplates) {
if (subFragmentTemplate != null) {
StringBuilder newFragment = new StringBuilder();
if (asmPrefix != null) {
newFragment.append(asmPrefix);
}
String subFragment = subFragmentTemplate.getBody();
if (bindMappings != null) {
if (mapSignature) {
// When mapping the signature we do the reverse replacement in the ASM
List<String> reverse = new ArrayList<>(bindMappings.keySet());
Collections.reverse(reverse);
for (String bound : reverse) {
subFragment = subFragment.replace("{" + bindMappings.get(bound) + "}", "{" + bound + "}");
}
} else {
// When not mapping the signature we do the replacement directly in the ASM
for (String bound : bindMappings.keySet()) {
subFragment = subFragment.replace("{" + bound + "}", "{" + bindMappings.get(bound) + "}");
}
}
}
newFragment.append(subFragment);
if (asmPostfix != null) {
newFragment.append("\n");
newFragment.append(asmPostfix);
}
candidates.add(new AsmFragmentTemplate(signature, newFragment.toString(), this, subFragmentTemplate));
}
}
}
}
}
return candidates;
}
return candidates;
}
public String getSubSignature() {
return subSignature;
}
public String getSubSignature() {
return subSignature;
}
}
}
private static String regexpRewriteSignature(String signature, String match, String replace) {
Pattern p = Pattern.compile(match);
Matcher m = p.matcher(signature);
String output = signature;
if(m.find()) {
// getReplacement first number with "number" and second number with the first
output = m.replaceAll(replace);
}
return output;
}
private static String regexpRewriteSignature(String signature, String match, String replace) {
Pattern p = Pattern.compile(match);
Matcher m = p.matcher(signature);
String output = signature;
if (m.find()) {
// getReplacement first number with "number" and second number with the first
output = m.replaceAll(replace);
}
return output;
}
/**
* Look for a fragment on the disk.
*
* @param signature The fragment signature
* @return The fragment file contents. Null if the fragment is not on the disk.
*/
private static CharStream loadFragment(String signature) {
ClassLoader classLoader = AsmFragmentManager.class.getClassLoader();
URL fragmentUrl = classLoader.getResource("dk/camelot64/kickc/fragment/asm/" + signature + ".asm");
if(fragmentUrl == null) {
return null;
}
try {
InputStream fragmentStream = fragmentUrl.openStream();
return CharStreams.fromStream(fragmentStream);
} catch(IOException e) {
throw new RuntimeException("Error loading fragment file " + fragmentUrl);
}
}
/**
* Look for a fragment on the disk.
*
* @param signature The fragment signature
* @return The fragment file contents. Null if the fragment is not on the disk.
*/
private static CharStream loadFragment(String signature) {
ClassLoader classLoader = AsmFragmentManager.class.getClassLoader();
URL fragmentUrl = classLoader.getResource("dk/camelot64/kickc/fragment/asm/" + signature + ".asm");
if (fragmentUrl == null) {
return null;
}
try {
InputStream fragmentStream = fragmentUrl.openStream();
return CharStreams.fromStream(fragmentStream);
} catch (IOException e) {
throw new RuntimeException("Error loading fragment file " + fragmentUrl);
}
}
/**
* Parse an ASM fragment.
*
* @param fragmentCharStream The stream containing the fragment syntax
* @param fragmentFileName The filename (used in error messages)
* @return The parsed fragment ready for generating
* @throws IOException if the parsing/loading fails
*/
public static KickCParser.AsmFileContext parseFragment(CharStream fragmentCharStream, final String fragmentFileName) {
KickCLexer kickCLexer = new KickCLexer(fragmentCharStream);
KickCParser kickCParser = new KickCParser(new CommonTokenStream(kickCLexer));
kickCParser.addErrorListener(new BaseErrorListener() {
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
throw new RuntimeException("Error parsing fragment " + fragmentFileName + "\n - Line: " + line + "\n - Message: " + msg);
}
});
kickCParser.setBuildParseTree(true);
KickCParser.AsmFileContext asmFileContext = kickCParser.asmFile();
return asmFileContext;
}
/**
* Parse an ASM fragment.
*
* @param fragmentCharStream The stream containing the fragment syntax
* @param fragmentFileName The filename (used in error messages)
* @return The parsed fragment ready for generating
* @throws IOException if the parsing/loading fails
*/
public static KickCParser.AsmLinesContext parseFragment(String fragmentBody, final String fragmentFileName) {
CodePointCharStream fragmentCharStream = CharStreams.fromString(fragmentBody);
KickCLexer kickCLexer = new KickCLexer(fragmentCharStream);
KickCParser kickCParser = new KickCParser(new CommonTokenStream(kickCLexer));
kickCParser.addErrorListener(new BaseErrorListener() {
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
throw new RuntimeException("Error parsing fragment " + fragmentFileName + "\n - Line: " + line + "\n - Message: " + msg);
}
});
kickCParser.setBuildParseTree(true);
KickCParser.AsmFileContext asmFile = kickCParser.asmFile();
return asmFile.asmLines();
}
public static class UnknownFragmentException extends RuntimeException {
public static class UnknownFragmentException extends RuntimeException {
private String fragmentSignature;
private String fragmentSignature;
public UnknownFragmentException(String signature) {
super("Fragment not found " + signature + ".asm");
this.fragmentSignature = signature;
}
public UnknownFragmentException(String signature) {
super("Fragment not found " + signature + ".asm");
this.fragmentSignature = signature;
}
public String getFragmentSignature() {
return fragmentSignature;
}
}
public String getFragmentSignature() {
return fragmentSignature;
}
}
/**
* All the synthesize rules available.
*/
private static List<FragmentSynthesis> fragmentSyntheses;
/**
* All the synthesize rules available.
*/
private static List<AsmFragmentSynthesis> fragmentSyntheses;
private static List<FragmentSynthesis> getFragmentSyntheses() {
if(fragmentSyntheses == null) {
fragmentSyntheses = initFragmentSyntheses();
}
return fragmentSyntheses;
}
private static List<AsmFragmentSynthesis> getFragmentSyntheses() {
if (fragmentSyntheses == null) {
fragmentSyntheses = initFragmentSyntheses();
}
return fragmentSyntheses;
}
private static List<FragmentSynthesis> initFragmentSyntheses() {
Map<String, String> mapZ = new LinkedHashMap<>();
mapZ.put("z2", "z1");
mapZ.put("z3", "z2");
Map<String, String> mapZ2 = new LinkedHashMap<>();
mapZ2.put("z3", "z1");
Map<String, String> mapZ3 = new LinkedHashMap<>();
mapZ3.put("z3", "z2");
Map<String, String> mapC = new LinkedHashMap<>();
mapC.put("c2", "c1");
mapC.put("c3", "c2");
Map<String, String> mapC3 = new LinkedHashMap<>();
mapC3.put("c3", "c2");
Map<String, String> mapZC = new LinkedHashMap<>();
mapZC.putAll(mapZ);
mapZC.putAll(mapC);
Map<String, String> mapSToU = new LinkedHashMap<>();
mapSToU.put("vbsz1", "vbuz1");
mapSToU.put("vbsz2", "vbuz2");
mapSToU.put("vbsz3", "vbuz3");
mapSToU.put("vbsc1", "vbuc1");
mapSToU.put("vbsc2", "vbuc2");
mapSToU.put("vbsc3", "vbuc3");
mapSToU.put("vbsaa", "vbuaa");
mapSToU.put("vbsxx", "vbuxx");
mapSToU.put("vbsyy", "vbuyy");
mapSToU.put("vwsz1", "vwuz1");
mapSToU.put("vwsz2", "vwuz2");
mapSToU.put("vwsz3", "vwuz3");
mapSToU.put("vwsc1", "vwuc1");
mapSToU.put("vwsc2", "vwuc2");
mapSToU.put("vwsc3", "vwuc3");
private static List<AsmFragmentSynthesis> initFragmentSyntheses() {
Map<String, String> mapZ = new LinkedHashMap<>();
mapZ.put("z2", "z1");
mapZ.put("z3", "z2");
Map<String, String> mapZ2 = new LinkedHashMap<>();
mapZ2.put("z3", "z1");
Map<String, String> mapZ3 = new LinkedHashMap<>();
mapZ3.put("z3", "z2");
Map<String, String> mapC = new LinkedHashMap<>();
mapC.put("c2", "c1");
mapC.put("c3", "c2");
Map<String, String> mapC3 = new LinkedHashMap<>();
mapC3.put("c3", "c2");
Map<String, String> mapZC = new LinkedHashMap<>();
mapZC.putAll(mapZ);
mapZC.putAll(mapC);
Map<String, String> mapSToU = new LinkedHashMap<>();
mapSToU.put("vbsz1", "vbuz1");
mapSToU.put("vbsz2", "vbuz2");
mapSToU.put("vbsz3", "vbuz3");
mapSToU.put("vbsc1", "vbuc1");
mapSToU.put("vbsc2", "vbuc2");
mapSToU.put("vbsc3", "vbuc3");
mapSToU.put("vbsaa", "vbuaa");
mapSToU.put("vbsxx", "vbuxx");
mapSToU.put("vbsyy", "vbuyy");
mapSToU.put("vwsz1", "vwuz1");
mapSToU.put("vwsz2", "vwuz2");
mapSToU.put("vwsz3", "vwuz3");
mapSToU.put("vwsc1", "vwuc1");
mapSToU.put("vwsc2", "vwuc2");
mapSToU.put("vwsc3", "vwuc3");
List<FragmentSynthesis> synths = new ArrayList<>();
List<AsmFragmentSynthesis> synths = new ArrayList<>();
synths.add(new FragmentSynthesis("(.*)=(.*)_(band|bor|bxor|plus)_(vb.aa)", ".*=vb.aa_.*", null, "$1=$4_$3_$2", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)_(band|bor|bxor|plus)_(vb.xx)", ".*=vb.[ax][ax]_.*", null, "$1=$4_$3_$2", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)_(band|bor|bxor|plus)_(vb.yy)", ".*=vb.[axy][axy]_.*", null, "$1=$4_$3_$2", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_(band|bor|bxor|plus)_(vb.aa)", ".*=vb.aa_.*", null, "$1=$4_$3_$2", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_(band|bor|bxor|plus)_(vb.xx)", ".*=vb.[ax][ax]_.*", null, "$1=$4_$3_$2", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_(band|bor|bxor|plus)_(vb.yy)", ".*=vb.[axy][axy]_.*", null, "$1=$4_$3_$2", null, null));
synths.add(new FragmentSynthesis("vbuxx=(.*)", null, null, "vbuaa=$1", "tax\n", null));
synths.add(new FragmentSynthesis("vbsxx=(.*)", null, null, "vbsaa=$1", "tax\n", null));
synths.add(new FragmentSynthesis("vbuyy=(.*)", null, null, "vbuaa=$1", "tay\n", null));
synths.add(new FragmentSynthesis("vbsyy=(.*)", null, null, "vbsaa=$1", "tay\n", null));
synths.add(new FragmentSynthesis("vbuz1=(.*)", ".*=.*vb.z1.*", null, "vbuaa=$1", "sta {z1}\n", mapZ));
synths.add(new FragmentSynthesis("vbsz1=(.*)", ".*=.*vb.z1.*", null, "vbsaa=$1", "sta {z1}\n", mapZ));
synths.add(new FragmentSynthesis("_deref_pb(.)c1=(.*)", ".*c1.*c1.*", null, "vb$1aa=$2", "sta {c1}\n", mapC));
synths.add(new FragmentSynthesis("_deref_pb(.)c1=(.*c1.*)", null, null, "vb$1aa=$2", "sta {c1}\n", null));
synths.add(new FragmentSynthesis("_deref_pbuz1=(.*)", ".*z1.*z1.*", null, "vbuaa=$1", "ldy #0\n" + "sta ({z1}),y\n", mapZ));
synths.add(new FragmentSynthesis("_deref_pbuz1=(.*z1.*)", null, null, "vbuaa=$1", "ldy #0\n" + "sta ({z1}),y\n", null));
synths.add(new AsmFragmentSynthesis("vbuxx=(.*)", null, null, "vbuaa=$1", "tax\n", null));
synths.add(new AsmFragmentSynthesis("vbsxx=(.*)", null, null, "vbsaa=$1", "tax\n", null));
synths.add(new AsmFragmentSynthesis("vbuyy=(.*)", null, null, "vbuaa=$1", "tay\n", null));
synths.add(new AsmFragmentSynthesis("vbsyy=(.*)", null, null, "vbsaa=$1", "tay\n", null));
synths.add(new AsmFragmentSynthesis("vbuz1=(.*)", ".*=.*vb.z1.*", null, "vbuaa=$1", "sta {z1}\n", mapZ));
synths.add(new AsmFragmentSynthesis("vbsz1=(.*)", ".*=.*vb.z1.*", null, "vbsaa=$1", "sta {z1}\n", mapZ));
synths.add(new AsmFragmentSynthesis("_deref_pb(.)c1=(.*)", ".*c1.*c1.*", null, "vb$1aa=$2", "sta {c1}\n", mapC));
synths.add(new AsmFragmentSynthesis("_deref_pb(.)c1=(.*c1.*)", null, null, "vb$1aa=$2", "sta {c1}\n", null));
synths.add(new AsmFragmentSynthesis("_deref_pbuz1=(.*)", ".*z1.*z1.*", null, "vbuaa=$1", "ldy #0\n" + "sta ({z1}),y\n", mapZ));
synths.add(new AsmFragmentSynthesis("_deref_pbuz1=(.*z1.*)", null, null, "vbuaa=$1", "ldy #0\n" + "sta ({z1}),y\n", null));
synths.add(new FragmentSynthesis("pb(.)c1_derefidx_vbuz1=(.*)", ".*z1.*z1.*|.*c1.*c1.*", null, "vb$1aa=$2", "ldx {z1}\n" + "sta {c1},x\n", mapZC));
synths.add(new FragmentSynthesis("pb(.)c1_derefidx_vbuyy=(.*)", ".*c1.*c1.*", null, "vb$1aa=$2", "sta {c1},y\n", mapC));
synths.add(new FragmentSynthesis("pb(.)c1_derefidx_vbuxx=(.*)", ".*c1.*c1.*", null, "vb$1aa=$2", "sta {c1},x\n", mapC));
synths.add(new FragmentSynthesis("pb(.)z1_derefidx_vbuz2=(.*)", ".*z1.*z1.*|.*z2.*z2.*", null, "vb$1aa=$2", "ldy {z2}\n" + "sta ({z1}),y\n", mapZ2));
synths.add(new AsmFragmentSynthesis("pb(.)c1_derefidx_vbuz1=(.*)", ".*z1.*z1.*|.*c1.*c1.*", null, "vb$1aa=$2", "ldx {z1}\n" + "sta {c1},x\n", mapZC));
synths.add(new AsmFragmentSynthesis("pb(.)c1_derefidx_vbuyy=(.*)", ".*c1.*c1.*", null, "vb$1aa=$2", "sta {c1},y\n", mapC));
synths.add(new AsmFragmentSynthesis("pb(.)c1_derefidx_vbuxx=(.*)", ".*c1.*c1.*", null, "vb$1aa=$2", "sta {c1},x\n", mapC));
synths.add(new AsmFragmentSynthesis("pb(.)z1_derefidx_vbuz2=(.*)", ".*z1.*z1.*|.*z2.*z2.*", null, "vb$1aa=$2", "ldy {z2}\n" + "sta ({z1}),y\n", mapZ2));
synths.add(new FragmentSynthesis("(.*)=_deref_pb(.)c1(.*)", ".*=.*aa.*", "lda {c1}\n", "$1=vb$2aa$3", null, mapC));
synths.add(new FragmentSynthesis("(.*)=_deref_pb(.)z1(.*)", ".*z1.*z1.*|.*=.*aa.*|.*=.*yy.*", "ldy #0\n" + "lda ({z1}),y\n", "$1=vb$2aa$3", null, mapZ));
synths.add(new AsmFragmentSynthesis("(.*)=_deref_pb(.)c1(.*)", ".*=.*aa.*", "lda {c1}\n", "$1=vb$2aa$3", null, mapC));
synths.add(new AsmFragmentSynthesis("(.*)=_deref_pb(.)z1(.*)", ".*z1.*z1.*|.*=.*aa.*|.*=.*yy.*", "ldy #0\n" + "lda ({z1}),y\n", "$1=vb$2aa$3", null, mapZ));
// Convert array indexing with A register to X/Y register by prefixing tax/tay (..._derefidx_vbuaa... -> ..._derefidx_vbuxx... /... _derefidx_vbuyy... )
synths.add(new FragmentSynthesis("(.*)=(.*)_derefidx_vbuaa(.*)", ".*=.*xx.*", "tax\n", "$1=$2_derefidx_vbuxx$3", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)_derefidx_vbuaa(.*)", ".*=.*yy.*", "tay\n", "$1=$2_derefidx_vbuyy$3", null, null));
// Convert array indexing with zero page to x/y register by prefixing ldx z1 / ldy z1 ( ..._derefidx_vbuzn... -> ..._derefidx_vbuxx... / ..._derefidx_vbuyy... )
synths.add(new FragmentSynthesis("(.*)=(.*)_derefidx_vbuz1(.*)", ".*=.*xx.*|.*z1.*z1.*", "ldx {z1}\n", "$1=$2_derefidx_vbuxx$3", null, mapZ));
synths.add(new FragmentSynthesis("(.*)=(.*)_derefidx_vbuz1(.*)", ".*=.*yy.*|.*z1.*z1.*", "ldy {z1}\n", "$1=$2_derefidx_vbuyy$3", null, mapZ));
synths.add(new FragmentSynthesis("(.*)=(.*)_derefidx_vbuz2(.*)", ".*=.*xx.*|.*z2.*z2.*", "ldx {z2}\n", "$1=$2_derefidx_vbuxx$3", null, mapZ3));
synths.add(new FragmentSynthesis("(.*)=(.*)_derefidx_vbuz2(.*)", ".*=.*yy.*|.*z2.*z2.*", "ldy {z2}\n", "$1=$2_derefidx_vbuyy$3", null, mapZ3));
synths.add(new FragmentSynthesis("(.*)=(.*)_derefidx_vbuz3(.*)", ".*=.*yy.*", "ldy {z3}\n", "$1=$2_derefidx_vbuyy$3", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)_derefidx_vbuz3(.*)", ".*=.*xx.*", "ldx {z3}\n", "$1=$2_derefidx_vbuxx$3", null, null));
// Convert array indexing twice with A/zp1/zp2 to X/Y register with a ldx/ldy prefix ( ..._derefidx_vbunn..._derefidx_vbunn... -> ..._derefidx_vbuxx..._derefidx_vbuxx... )
synths.add(new FragmentSynthesis("(.*)_derefidx_vbuaa(.*)_derefidx_vbuaa(.*)", ".*aa.*aa.*aa.*|.*xx.*", null, "$1_derefidx_vbuxx$2_derefidx_vbuxx$3", "tax\n", null));
synths.add(new FragmentSynthesis("(.*)_derefidx_vbuaa(.*)_derefidx_vbuaa(.*)", ".*aa.*aa.*aa.*|.*yy.*", null, "$1_derefidx_vbuyy$2_derefidx_vbuyy$3", "tay\n", null));
synths.add(new FragmentSynthesis("(.*)_derefidx_vbuz1(.*)_derefidx_vbuz1(.*)", ".*z1.*z1.*z1.*|.*xx.*", null, "$1_derefidx_vbuxx$2_derefidx_vbuxx$3", "ldx {z1}\n", mapZ));
synths.add(new FragmentSynthesis("(.*)_derefidx_vbuz1(.*)_derefidx_vbuz1(.*)", ".*z1.*z1.*z1.*|.*yy.*", null, "$1_derefidx_vbuyy$2_derefidx_vbuyy$3", "ldy {z1}\n", mapZ));
synths.add(new FragmentSynthesis("(.*)_derefidx_vbuz2(.*)_derefidx_vbuz2(.*)", ".*z2.*z2.*z2.*|.*xx.*", null, "$1_derefidx_vbuxx$2_derefidx_vbuxx$3", "ldx {z2}\n", mapZ));
synths.add(new FragmentSynthesis("(.*)_derefidx_vbuz2(.*)_derefidx_vbuz2(.*)", ".*z2.*z2.*z2.*|.*yy.*", null, "$1_derefidx_vbuyy$2_derefidx_vbuyy$3", "ldy {z2}\n", mapZ));
synths.add(new FragmentSynthesis("pb(.)c1_derefidx_vbuz1=(.*c1.*)", ".*z1.*z1.*", null, "vb$1aa=$2", "ldx {z1}\n" + "sta {c1},x\n", mapZ));
synths.add(new FragmentSynthesis("pb(.)c1_derefidx_vbuz1=(.*z1.*)", ".*c1.*c1.*", null, "vb$1aa=$2", "ldx {z1}\n" + "sta {c1},x\n", mapC));
// Convert array indexing with A register to X/Y register by prefixing tax/tay (..._derefidx_vbuaa... -> ..._derefidx_vbuxx... /... _derefidx_vbuyy... )
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_derefidx_vbuaa(.*)", ".*=.*xx.*", "tax\n", "$1=$2_derefidx_vbuxx$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_derefidx_vbuaa(.*)", ".*=.*yy.*", "tay\n", "$1=$2_derefidx_vbuyy$3", null, null));
// Convert array indexing with zero page to x/y register by prefixing ldx z1 / ldy z1 ( ..._derefidx_vbuzn... -> ..._derefidx_vbuxx... / ..._derefidx_vbuyy... )
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_derefidx_vbuz1(.*)", ".*=.*xx.*|.*z1.*z1.*", "ldx {z1}\n", "$1=$2_derefidx_vbuxx$3", null, mapZ));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_derefidx_vbuz1(.*)", ".*=.*yy.*|.*z1.*z1.*", "ldy {z1}\n", "$1=$2_derefidx_vbuyy$3", null, mapZ));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_derefidx_vbuz2(.*)", ".*=.*xx.*|.*z2.*z2.*", "ldx {z2}\n", "$1=$2_derefidx_vbuxx$3", null, mapZ3));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_derefidx_vbuz2(.*)", ".*=.*yy.*|.*z2.*z2.*", "ldy {z2}\n", "$1=$2_derefidx_vbuyy$3", null, mapZ3));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_derefidx_vbuz3(.*)", ".*=.*yy.*", "ldy {z3}\n", "$1=$2_derefidx_vbuyy$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_derefidx_vbuz3(.*)", ".*=.*xx.*", "ldx {z3}\n", "$1=$2_derefidx_vbuxx$3", null, null));
// Convert array indexing twice with A/zp1/zp2 to X/Y register with a ldx/ldy prefix ( ..._derefidx_vbunn..._derefidx_vbunn... -> ..._derefidx_vbuxx..._derefidx_vbuxx... )
synths.add(new AsmFragmentSynthesis("(.*)_derefidx_vbuaa(.*)_derefidx_vbuaa(.*)", ".*aa.*aa.*aa.*|.*xx.*", null, "$1_derefidx_vbuxx$2_derefidx_vbuxx$3", "tax\n", null));
synths.add(new AsmFragmentSynthesis("(.*)_derefidx_vbuaa(.*)_derefidx_vbuaa(.*)", ".*aa.*aa.*aa.*|.*yy.*", null, "$1_derefidx_vbuyy$2_derefidx_vbuyy$3", "tay\n", null));
synths.add(new AsmFragmentSynthesis("(.*)_derefidx_vbuz1(.*)_derefidx_vbuz1(.*)", ".*z1.*z1.*z1.*|.*xx.*", null, "$1_derefidx_vbuxx$2_derefidx_vbuxx$3", "ldx {z1}\n", mapZ));
synths.add(new AsmFragmentSynthesis("(.*)_derefidx_vbuz1(.*)_derefidx_vbuz1(.*)", ".*z1.*z1.*z1.*|.*yy.*", null, "$1_derefidx_vbuyy$2_derefidx_vbuyy$3", "ldy {z1}\n", mapZ));
synths.add(new AsmFragmentSynthesis("(.*)_derefidx_vbuz2(.*)_derefidx_vbuz2(.*)", ".*z2.*z2.*z2.*|.*xx.*", null, "$1_derefidx_vbuxx$2_derefidx_vbuxx$3", "ldx {z2}\n", mapZ));
synths.add(new AsmFragmentSynthesis("(.*)_derefidx_vbuz2(.*)_derefidx_vbuz2(.*)", ".*z2.*z2.*z2.*|.*yy.*", null, "$1_derefidx_vbuyy$2_derefidx_vbuyy$3", "ldy {z2}\n", mapZ));
synths.add(new AsmFragmentSynthesis("pb(.)c1_derefidx_vbuz1=(.*c1.*)", ".*z1.*z1.*", null, "vb$1aa=$2", "ldx {z1}\n" + "sta {c1},x\n", mapZ));
synths.add(new AsmFragmentSynthesis("pb(.)c1_derefidx_vbuz1=(.*z1.*)", ".*c1.*c1.*", null, "vb$1aa=$2", "ldx {z1}\n" + "sta {c1},x\n", mapC));
// Convert X/Y-based array indexing of a constant pointer into A-register by prefixing lda cn,x / lda cn,y ( ...pb.c1_derefidx_vbuxx... / ...pb.c1_derefidx_vbuyy... -> ...vb.aa... )
synths.add(new FragmentSynthesis("(.*)=(.*)pb(.)c1_derefidx_vbuxx(.*)", ".*=.*aa.*|.*c1.*c1.*", "lda {c1},x\n", "$1=$2vb$3aa$4", null, mapC));
synths.add(new FragmentSynthesis("(.*)=(.*c1.*)pb(.)c1_derefidx_vbuxx(.*)", ".*=.*aa.*", "lda {c1},x\n", "$1=$2vb$3aa$4", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)pb(.)c1_derefidx_vbuxx(.*c1.*)", ".*=.*aa.*", "lda {c1},x\n", "$1=$2vb$3aa$4", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)pb(.)c1_derefidx_vbuyy(.*)", ".*=.*aa.*|.*c1.*c1.*", "lda {c1},y\n", "$1=$2vb$3aa$4", null, mapC));
synths.add(new FragmentSynthesis("(.*)=(.*c1.*)pb(.)c1_derefidx_vbuyy(.*)", ".*=.*aa.*", "lda {c1},y\n", "$1=$2vb$3aa$4", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)pb(.)c1_derefidx_vbuyy(.*c1.*)", ".*=.*aa.*", "lda {c1},y\n", "$1=$2vb$3aa$4", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)pb(.)c2_derefidx_vbuxx(.*)", ".*=.*aa.*|.*c2.*c2.*", "lda {c2},x\n", "$1=$2vb$3aa$4", null, mapC3));
synths.add(new FragmentSynthesis("(.*)=(.*c2.*)pb(.)c2_derefidx_vbuxx(.*)", ".*=.*aa.*", "lda {c2},x\n", "$1=$2vb$3aa$4", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)pb(.)c2_derefidx_vbuxx(.*c2.*)", ".*=.*aa.*", "lda {c2},x\n", "$1=$2vb$3aa$4", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)pb(.)c2_derefidx_vbuyy(.*)", ".*=.*aa.*|.*c2.*c2.*", "lda {c2},y\n", "$1=$2vb$3aa$4", null, mapC3));
synths.add(new FragmentSynthesis("(.*)=(.*c2.*)pb(.)c2_derefidx_vbuyy(.*)", ".*=.*aa.*", "lda {c2},y\n", "$1=$2vb$3aa$4", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)pb(.)c2_derefidx_vbuyy(.*c2.*)", ".*=.*aa.*", "lda {c2},y\n", "$1=$2vb$3aa$4", null, null));
// Convert X/Y-based array indexing of a constant pointer into A-register by prefixing lda cn,x / lda cn,y ( ...pb.c1_derefidx_vbuxx... / ...pb.c1_derefidx_vbuyy... -> ...vb.aa... )
synths.add(new AsmFragmentSynthesis("(.*)=(.*)pb(.)c1_derefidx_vbuxx(.*)", ".*=.*aa.*|.*c1.*c1.*", "lda {c1},x\n", "$1=$2vb$3aa$4", null, mapC));
synths.add(new AsmFragmentSynthesis("(.*)=(.*c1.*)pb(.)c1_derefidx_vbuxx(.*)", ".*=.*aa.*", "lda {c1},x\n", "$1=$2vb$3aa$4", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)pb(.)c1_derefidx_vbuxx(.*c1.*)", ".*=.*aa.*", "lda {c1},x\n", "$1=$2vb$3aa$4", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)pb(.)c1_derefidx_vbuyy(.*)", ".*=.*aa.*|.*c1.*c1.*", "lda {c1},y\n", "$1=$2vb$3aa$4", null, mapC));
synths.add(new AsmFragmentSynthesis("(.*)=(.*c1.*)pb(.)c1_derefidx_vbuyy(.*)", ".*=.*aa.*", "lda {c1},y\n", "$1=$2vb$3aa$4", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)pb(.)c1_derefidx_vbuyy(.*c1.*)", ".*=.*aa.*", "lda {c1},y\n", "$1=$2vb$3aa$4", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)pb(.)c2_derefidx_vbuxx(.*)", ".*=.*aa.*|.*c2.*c2.*", "lda {c2},x\n", "$1=$2vb$3aa$4", null, mapC3));
synths.add(new AsmFragmentSynthesis("(.*)=(.*c2.*)pb(.)c2_derefidx_vbuxx(.*)", ".*=.*aa.*", "lda {c2},x\n", "$1=$2vb$3aa$4", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)pb(.)c2_derefidx_vbuxx(.*c2.*)", ".*=.*aa.*", "lda {c2},x\n", "$1=$2vb$3aa$4", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)pb(.)c2_derefidx_vbuyy(.*)", ".*=.*aa.*|.*c2.*c2.*", "lda {c2},y\n", "$1=$2vb$3aa$4", null, mapC3));
synths.add(new AsmFragmentSynthesis("(.*)=(.*c2.*)pb(.)c2_derefidx_vbuyy(.*)", ".*=.*aa.*", "lda {c2},y\n", "$1=$2vb$3aa$4", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)pb(.)c2_derefidx_vbuyy(.*c2.*)", ".*=.*aa.*", "lda {c2},y\n", "$1=$2vb$3aa$4", null, null));
// Convert zeropage/constants/X/Y in assignments to A-register using LDA/TXA/TYA prefix
synths.add(new FragmentSynthesis("(.*)=(.*)vbuz1(.*)", ".*z1.*=.*|.*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2vbuaa$3", null, mapZ));
synths.add(new FragmentSynthesis("(.*)=(.*)vbsz1(.*)", ".*z1.*=.*|.*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2vbsaa$3", null, mapZ));
synths.add(new FragmentSynthesis("(.*)=(.*)vbuz2(.*)", ".*z2.*=.*|.*=.*aa.*|.*z2.*z2.*|.*z3.*", "lda {z2}\n", "$1=$2vbuaa$3", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)vbsz2(.*)", ".*z2.*=.*|.*=.*aa.*|.*z2.*z2.*|.*z3.*", "lda {z2}\n", "$1=$2vbsaa$3", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)vbuz2(.*z3.*)", ".*z2.*=.*|.*=.*aa.*|.*z2.*z2.*|.*z3.*", "lda {z2}\n", "$1=$2vbuaa$3", null, mapZ3));
synths.add(new FragmentSynthesis("(.*)=(.*)vbsz2(.*z3.*)", ".*z2.*=.*|.*=.*aa.*|.*z2.*z2.*|.*z3.*", "lda {z2}\n", "$1=$2vbsaa$3", null, mapZ3));
// Convert zeropage/constants/X/Y in assignments to A-register using LDA/TXA/TYA prefix
synths.add(new AsmFragmentSynthesis("(.*)=(.*)vbuz1(.*)", ".*z1.*=.*|.*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2vbuaa$3", null, mapZ));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)vbsz1(.*)", ".*z1.*=.*|.*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2vbsaa$3", null, mapZ));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)vbuz2(.*)", ".*z2.*=.*|.*=.*aa.*|.*z2.*z2.*|.*z3.*", "lda {z2}\n", "$1=$2vbuaa$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)vbsz2(.*)", ".*z2.*=.*|.*=.*aa.*|.*z2.*z2.*|.*z3.*", "lda {z2}\n", "$1=$2vbsaa$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)vbuz2(.*z3.*)", ".*z2.*=.*|.*=.*aa.*|.*z2.*z2.*|.*z3.*", "lda {z2}\n", "$1=$2vbuaa$3", null, mapZ3));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)vbsz2(.*z3.*)", ".*z2.*=.*|.*=.*aa.*|.*z2.*z2.*|.*z3.*", "lda {z2}\n", "$1=$2vbsaa$3", null, mapZ3));
synths.add(new FragmentSynthesis("(.*)=(.*)_vbuxx", ".*=.*[ax][ax].*xx|.*derefidx_vb.xx", "txa\n", "$1=$2_vbuaa", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)_vbsxx", ".*=.*[ax][ax].*xx|.*derefidx_vb.xx", "txa\n", "$1=$2_vbsaa", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)_vbuyy", ".*=.*[ay][ay].*yy|.*derefidx_vb.yy", "tya\n", "$1=$2_vbuaa", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)_vbsyy", ".*=-*[ay][ay].*yy|.*derefidx_vb.yy", "tya\n", "$1=$2_vbsaa", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)_vbuz1", ".*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2_vbuaa", null, mapZ));
synths.add(new FragmentSynthesis("(.*)=(.*)_vbsz1", ".*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2_vbsaa", null, mapZ));
synths.add(new FragmentSynthesis("(.*)=(.*)_vbuz2", ".*=.*aa.*|.*z2.*z2.*", "lda {z2}\n", "$1=$2_vbuaa", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)_vbuz3", ".*=.*aa.*|.*z3.*z3.*", "lda {z3}\n", "$1=$2_vbuaa", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_vbuxx", ".*=.*[ax][ax].*xx|.*derefidx_vb.xx", "txa\n", "$1=$2_vbuaa", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_vbsxx", ".*=.*[ax][ax].*xx|.*derefidx_vb.xx", "txa\n", "$1=$2_vbsaa", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_vbuyy", ".*=.*[ay][ay].*yy|.*derefidx_vb.yy", "tya\n", "$1=$2_vbuaa", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_vbsyy", ".*=-*[ay][ay].*yy|.*derefidx_vb.yy", "tya\n", "$1=$2_vbsaa", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_vbuz1", ".*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2_vbuaa", null, mapZ));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_vbsz1", ".*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2_vbsaa", null, mapZ));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_vbuz2", ".*=.*aa.*|.*z2.*z2.*", "lda {z2}\n", "$1=$2_vbuaa", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_vbuz3", ".*=.*aa.*|.*z3.*z3.*", "lda {z3}\n", "$1=$2_vbuaa", null, null));
synths.add(new FragmentSynthesis("vbuz1=vbuz1(.*)", ".*=.*vb.aa.*|.*z1.*z1.*z1.*", "lda {z1}\n", "vbuaa=vbuaa$1", "sta {z1}\n", mapZ));
synths.add(new FragmentSynthesis("vbsz1=vbsz1(.*)", ".*=.*vb.aa.*|.*z1.*z1.*z1.*", "lda {z1}\n", "vbsaa=vbsaa$1", "sta {z1}\n", mapZ));
synths.add(new AsmFragmentSynthesis("vbuz1=vbuz1(.*)", ".*=.*vb.aa.*|.*z1.*z1.*z1.*", "lda {z1}\n", "vbuaa=vbuaa$1", "sta {z1}\n", mapZ));
synths.add(new AsmFragmentSynthesis("vbsz1=vbsz1(.*)", ".*=.*vb.aa.*|.*z1.*z1.*z1.*", "lda {z1}\n", "vbsaa=vbsaa$1", "sta {z1}\n", mapZ));
synths.add(new FragmentSynthesis("vbuz1_(lt|gt|le|ge|eq|neq)_(.*)", ".*vb.aa.*", "lda {z1}\n", "vbuaa_$1_$2", null, mapZ));
synths.add(new FragmentSynthesis("vbsz1_(lt|gt|le|ge|eq|neq)_(.*)", ".*vb.aa.*", "lda {z1}\n", "vbsaa_$1_$2", null, mapZ));
synths.add(new FragmentSynthesis("_deref_pb(.)c1_(lt|gt|le|ge|eq|neq)_(.*)", ".*vb.aa.*", "lda {c1}\n", "vb$1aa_$2_$3", null, mapC));
synths.add(new FragmentSynthesis("_deref_pb(.)z1_(lt|gt|le|ge|eq|neq)_(.*)", ".*vb.aa.*|.*vb.yy.*|.*z1.*z1.*", "ldy #0\n" + "lda ({z1}),y\n", "vb$1aa_$2_$3", null, mapZ));
synths.add(new AsmFragmentSynthesis("vbuz1_(lt|gt|le|ge|eq|neq)_(.*)", ".*vb.aa.*", "lda {z1}\n", "vbuaa_$1_$2", null, mapZ));
synths.add(new AsmFragmentSynthesis("vbsz1_(lt|gt|le|ge|eq|neq)_(.*)", ".*vb.aa.*", "lda {z1}\n", "vbsaa_$1_$2", null, mapZ));
synths.add(new AsmFragmentSynthesis("_deref_pb(.)c1_(lt|gt|le|ge|eq|neq)_(.*)", ".*vb.aa.*", "lda {c1}\n", "vb$1aa_$2_$3", null, mapC));
synths.add(new AsmFragmentSynthesis("_deref_pb(.)z1_(lt|gt|le|ge|eq|neq)_(.*)", ".*vb.aa.*|.*vb.yy.*|.*z1.*z1.*", "ldy #0\n" + "lda ({z1}),y\n", "vb$1aa_$2_$3", null, mapZ));
synths.add(new FragmentSynthesis("(.*)_derefidx_vbuz1_(lt|gt|le|ge|eq|neq)_(.*)", ".*z1.*z1.*|.*vb.yy.*", "ldy {z1}\n", "$1_derefidx_vbuyy_$2_$3", null, mapZ));
synths.add(new FragmentSynthesis("(.*)_derefidx_vbuz1_(lt|gt|le|ge|eq|neq)_(.*)", ".*z1.*z1.*|.*vb.xx.*", "ldx {z1}\n", "$1_derefidx_vbuxx_$2_$3", null, mapZ));
synths.add(new FragmentSynthesis("pb(.)c1_derefidx_vbuyy_(lt|gt|le|ge|eq|neq)_(.*)", ".*c1.*c1.*|.*aa.*", "lda {c1},y\n", "vb$1aa_$2_$3", null, mapC));
synths.add(new FragmentSynthesis("pb(.)c1_derefidx_vbuyy_(lt|gt|le|ge|eq|neq)_(.*c1.*)", ".*aa.*", "lda {c1},y\n", "vb$1aa_$2_$3", null, null));
synths.add(new FragmentSynthesis("pb(.)c1_derefidx_vbuxx_(lt|gt|le|ge|eq|neq)_(.*)", ".*c1.*c1.*|.*aa.*", "lda {c1},x\n", "vb$1aa_$2_$3", null, mapC));
synths.add(new FragmentSynthesis("pb(.)c1_derefidx_vbuxx_(lt|gt|le|ge|eq|neq)_(.*c1.*)", ".*aa.*", "lda {c1},x\n", "vb$1aa_$2_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_derefidx_vbuz1_(lt|gt|le|ge|eq|neq)_(.*)", ".*z1.*z1.*|.*vb.yy.*", "ldy {z1}\n", "$1_derefidx_vbuyy_$2_$3", null, mapZ));
synths.add(new AsmFragmentSynthesis("(.*)_derefidx_vbuz1_(lt|gt|le|ge|eq|neq)_(.*)", ".*z1.*z1.*|.*vb.xx.*", "ldx {z1}\n", "$1_derefidx_vbuxx_$2_$3", null, mapZ));
synths.add(new AsmFragmentSynthesis("pb(.)c1_derefidx_vbuyy_(lt|gt|le|ge|eq|neq)_(.*)", ".*c1.*c1.*|.*aa.*", "lda {c1},y\n", "vb$1aa_$2_$3", null, mapC));
synths.add(new AsmFragmentSynthesis("pb(.)c1_derefidx_vbuyy_(lt|gt|le|ge|eq|neq)_(.*c1.*)", ".*aa.*", "lda {c1},y\n", "vb$1aa_$2_$3", null, null));
synths.add(new AsmFragmentSynthesis("pb(.)c1_derefidx_vbuxx_(lt|gt|le|ge|eq|neq)_(.*)", ".*c1.*c1.*|.*aa.*", "lda {c1},x\n", "vb$1aa_$2_$3", null, mapC));
synths.add(new AsmFragmentSynthesis("pb(.)c1_derefidx_vbuxx_(lt|gt|le|ge|eq|neq)_(.*c1.*)", ".*aa.*", "lda {c1},x\n", "vb$1aa_$2_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_ge_(vb.aa)_then_(.*)", ".*vb.aa.*_ge.*", null, "$2_lt_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_ge_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_ge.*", null, "$2_lt_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_ge_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_ge.*", null, "$2_lt_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_lt_(vb.aa)_then_(.*)", ".*vb.aa.*_lt.*", null, "$2_ge_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_lt_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_lt.*", null, "$2_ge_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_lt_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_lt.*", null, "$2_ge_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_gt_(vb.aa)_then_(.*)", ".*vb.aa.*_gt.*", null, "$2_le_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_gt_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_gt.*", null, "$2_le_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_gt_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_gt.*", null, "$2_le_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_le_(vb.aa)_then_(.*)", ".*vb.aa.*_le.*", null, "$2_gt_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_le_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_le.*", null, "$2_gt_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_le_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_le.*", null, "$2_gt_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_neq_(vb.aa)_then_(.*)", ".*vb.aa.*_neq.*", null, "$2_neq_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_neq_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_neq.*", null, "$2_neq_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_neq_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_neq.*", null, "$2_neq_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_eq_(vb.aa)_then_(.*)", ".*vb.aa.*_eq.*", null, "$2_eq_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_eq_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_eq.*", null, "$2_eq_$1_then_$3", null, null));
synths.add(new FragmentSynthesis("(.*)_eq_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_eq.*", null, "$2_eq_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_ge_(vb.aa)_then_(.*)", ".*vb.aa.*_ge.*", null, "$2_lt_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_ge_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_ge.*", null, "$2_lt_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_ge_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_ge.*", null, "$2_lt_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_lt_(vb.aa)_then_(.*)", ".*vb.aa.*_lt.*", null, "$2_ge_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_lt_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_lt.*", null, "$2_ge_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_lt_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_lt.*", null, "$2_ge_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_gt_(vb.aa)_then_(.*)", ".*vb.aa.*_gt.*", null, "$2_le_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_gt_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_gt.*", null, "$2_le_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_gt_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_gt.*", null, "$2_le_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_le_(vb.aa)_then_(.*)", ".*vb.aa.*_le.*", null, "$2_gt_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_le_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_le.*", null, "$2_gt_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_le_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_le.*", null, "$2_gt_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_neq_(vb.aa)_then_(.*)", ".*vb.aa.*_neq.*", null, "$2_neq_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_neq_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_neq.*", null, "$2_neq_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_neq_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_neq.*", null, "$2_neq_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_eq_(vb.aa)_then_(.*)", ".*vb.aa.*_eq.*", null, "$2_eq_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_eq_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_eq.*", null, "$2_eq_$1_then_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)_eq_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_eq.*", null, "$2_eq_$1_then_$3", null, null));
// Use unsigned ASM to synthesize signed ASM ( ...vbs... -> ...vbu... )
synths.add(new FragmentSynthesis("(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)_(eq|neq)_(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)_then_(.*)", null, null, "$1_$2_$3_then_$4", null, mapSToU));
synths.add(new FragmentSynthesis("(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)=(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)", null, null, "$1=$2", null, mapSToU));
synths.add(new FragmentSynthesis("(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)=(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)_(plus|band|bxor|bor)_(vbsz.|csoby.|vbsaa|vbsxx|vbsyy)", null, null, "$1=$2_$3_$4", null, mapSToU));
synths.add(new FragmentSynthesis("(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)=_(inc|dec)_(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)", null, null, "$1=_$2_$3", null, mapSToU));
synths.add(new FragmentSynthesis("(vwsz.|vwsc.)_(eq|neq)_(vwsz.|vwsc.)_then_(.*)", null, null, "$1_$2_$3_then_$4", null, mapSToU));
synths.add(new FragmentSynthesis("(vwsz.)=(vwsz.|vwsc.)", null, null, "$1=$2", null, mapSToU));
synths.add(new FragmentSynthesis("(v.sz.)=(v.s..)_(band|bxor|bor)_(v.s..)", null, null, "$1=$2_$3_$4", null, mapSToU));
synths.add(new FragmentSynthesis("(vbuz.|vbuaa|vbuxx|vbuyy)=_(lo|hi)_vws(z.|c.)", null, null, "$1=_$2_vwu$3", null, mapSToU));
// Use unsigned ASM to synthesize signed ASM ( ...vbs... -> ...vbu... )
synths.add(new AsmFragmentSynthesis("(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)_(eq|neq)_(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)_then_(.*)", null, null, "$1_$2_$3_then_$4", null, mapSToU));
synths.add(new AsmFragmentSynthesis("(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)=(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)", null, null, "$1=$2", null, mapSToU));
synths.add(new AsmFragmentSynthesis("(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)=(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)_(plus|band|bxor|bor)_(vbsz.|csoby.|vbsaa|vbsxx|vbsyy)", null, null, "$1=$2_$3_$4", null, mapSToU));
synths.add(new AsmFragmentSynthesis("(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)=_(inc|dec)_(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)", null, null, "$1=_$2_$3", null, mapSToU));
synths.add(new AsmFragmentSynthesis("(vwsz.|vwsc.)_(eq|neq)_(vwsz.|vwsc.)_then_(.*)", null, null, "$1_$2_$3_then_$4", null, mapSToU));
synths.add(new AsmFragmentSynthesis("(vwsz.)=(vwsz.|vwsc.)", null, null, "$1=$2", null, mapSToU));
synths.add(new AsmFragmentSynthesis("(v.sz.)=(v.s..)_(band|bxor|bor)_(v.s..)", null, null, "$1=$2_$3_$4", null, mapSToU));
synths.add(new AsmFragmentSynthesis("(vbuz.|vbuaa|vbuxx|vbuyy)=_(lo|hi)_vws(z.|c.)", null, null, "$1=_$2_vwu$3", null, mapSToU));
// Use constant word ASM to synthesize unsigned constant byte ASM ( ...vb.c... -> vw.c... )
synths.add(new FragmentSynthesis("(vwuz.)=(vwuz.)_(plus|minus|band|bxor|bor)_vb.c(.)", null, null, "$1=$2_$3_vwuc$4", null, null));
synths.add(new FragmentSynthesis("(vwuz.)=vb.c(.)_(plus|minus|band|bxor|bor)_(vwuz.)", null, null, "$1=vwuc$2_$3_$4", null, null));
synths.add(new FragmentSynthesis("(vwsz.)=(vwsz.)_(plus|minus|band|bxor|bor)_vb.c(.)", null, null, "$1=$2_$3_vwsc$4", null, null));
synths.add(new FragmentSynthesis("(vwsz.)=vb.c(.)_(plus|minus|band|bxor|bor)_(vwsz.)", null, null, "$1=vwsc$2_$3_$4", null, null));
// Use constant word ASM to synthesize unsigned constant byte ASM ( ...vb.c... -> vw.c... )
synths.add(new AsmFragmentSynthesis("(vwuz.)=(vwuz.)_(plus|minus|band|bxor|bor)_vb.c(.)", null, null, "$1=$2_$3_vwuc$4", null, null));
synths.add(new AsmFragmentSynthesis("(vwuz.)=vb.c(.)_(plus|minus|band|bxor|bor)_(vwuz.)", null, null, "$1=vwuc$2_$3_$4", null, null));
synths.add(new AsmFragmentSynthesis("(vwsz.)=(vwsz.)_(plus|minus|band|bxor|bor)_vb.c(.)", null, null, "$1=$2_$3_vwsc$4", null, null));
synths.add(new AsmFragmentSynthesis("(vwsz.)=vb.c(.)_(plus|minus|band|bxor|bor)_(vwsz.)", null, null, "$1=vwsc$2_$3_$4", null, null));
// Move constant words to the end of the ASM signature for symmetric operators ( ...vw.c...vw.z... -> ...vw.z...vw.c... )
synths.add(new FragmentSynthesis("(vwuz.)=(vwuc.)_(plus|band|bxor|bor)_(vwuz.)", null, null, "$1=$4_$3_$2", null, null));
synths.add(new FragmentSynthesis("(vwsz.)=(vwsc.)_(plus|band|bxor|bor)_(vwsz.)", null, null, "$1=$4_$3_$2", null, null));
// Move constant words to the end of the ASM signature for symmetric operators ( ...vw.c...vw.z... -> ...vw.z...vw.c... )
synths.add(new AsmFragmentSynthesis("(vwuz.)=(vwuc.)_(plus|band|bxor|bor)_(vwuz.)", null, null, "$1=$4_$3_$2", null, null));
synths.add(new AsmFragmentSynthesis("(vwsz.)=(vwsc.)_(plus|band|bxor|bor)_(vwsz.)", null, null, "$1=$4_$3_$2", null, null));
// Use Z1/Z2 ASM to synthesize Z1-only code ( ...z1...z1... -> ...z1...z2... )
synths.add(new FragmentSynthesis("(v..)z1=(v..)z1_(plus|minus|band|bxor|bor)_(.*)", ".*z2.*", null, "$1z1=$2z2_$3_$4", null, mapZ, false));
synths.add(new FragmentSynthesis("(v..)z1=(.*)_(plus|minus|band|bxor|bor)_(v..)z1", ".*z2.*", null, "$1z1=$2_$3_$4z2", null, mapZ, false));
synths.add(new FragmentSynthesis("(v..)z1=_(neg|lo|hi)_(v..)z1", ".*z2.*", null, "$1z1=_$2_$3z2", null, mapZ, false));
// Use Z1/Z2 ASM to synthesize Z1-only code ( ...z1...z1... -> ...z1...z2... )
synths.add(new AsmFragmentSynthesis("(v..)z1=(v..)z1_(plus|minus|band|bxor|bor)_(.*)", ".*z2.*", null, "$1z1=$2z2_$3_$4", null, mapZ, false));
synths.add(new AsmFragmentSynthesis("(v..)z1=(.*)_(plus|minus|band|bxor|bor)_(v..)z1", ".*z2.*", null, "$1z1=$2_$3_$4z2", null, mapZ, false));
synths.add(new AsmFragmentSynthesis("(v..)z1=_(neg|lo|hi)_(v..)z1", ".*z2.*", null, "$1z1=_$2_$3z2", null, mapZ, false));
// Convert INC/DEC to +1/-1 ( ..._inc_xxx... -> ...xxx_plus_1_... / ..._dec_xxx... -> ...xxx_minus_1_... )
synths.add(new FragmentSynthesis("vb(.)aa=_inc_(.*)", null, null, "vb$1aa=$2_plus_1", null, null));
synths.add(new FragmentSynthesis("vb(.)aa=_dec_(.*)", null, null, "vb$1aa=$2_minus_1", null, null));
// Convert INC/DEC to +1/-1 ( ..._inc_xxx... -> ...xxx_plus_1_... / ..._dec_xxx... -> ...xxx_minus_1_... )
synths.add(new AsmFragmentSynthesis("vb(.)aa=_inc_(.*)", null, null, "vb$1aa=$2_plus_1", null, null));
synths.add(new AsmFragmentSynthesis("vb(.)aa=_dec_(.*)", null, null, "vb$1aa=$2_minus_1", null, null));
// Synthesize XX/YY using AA
synths.add(new FragmentSynthesis("(.*)=(.*)vbuxx(.*)", ".*=.*aa.*|.*derefidx_vb.xx.*", "txa\n", "$1=$2vbuaa$3", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)vbsxx(.*)", ".*=.*aa.*|.*derefidx_vb.xx.*", "txa\n", "$1=$2vbsaa$3", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)vbuyy(.*)", ".*=.*aa.*|.*derefidx_vb.yy.*", "tya\n", "$1=$2vbuaa$3", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)vbsyy(.*)", ".*=.*aa.*|.*derefidx_vb.yy.*", "tya\n", "$1=$2vbsaa$3", null, null));
// Synthesize constants using AA
synths.add(new FragmentSynthesis("(.*)=(.*)vbuc1(.*)", ".*=.*aa.*|.*c1.*c1.*|.*c1_deref.*", "lda #{c1}\n", "$1=$2vbuaa$3", null, mapC));
synths.add(new FragmentSynthesis("(.*)=(.*)vbsc1(.*)", ".*=.*aa.*|.*c1.*c1.*|.*c1_deref.*", "lda #{c1}\n", "$1=$2vbsaa$3", null, mapC));
// Synthesize XX/YY using AA
synths.add(new AsmFragmentSynthesis("(.*)=(.*)vbuxx(.*)", ".*=.*aa.*|.*derefidx_vb.xx.*", "txa\n", "$1=$2vbuaa$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)vbsxx(.*)", ".*=.*aa.*|.*derefidx_vb.xx.*", "txa\n", "$1=$2vbsaa$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)vbuyy(.*)", ".*=.*aa.*|.*derefidx_vb.yy.*", "tya\n", "$1=$2vbuaa$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)vbsyy(.*)", ".*=.*aa.*|.*derefidx_vb.yy.*", "tya\n", "$1=$2vbsaa$3", null, null));
// Synthesize constants using AA
synths.add(new AsmFragmentSynthesis("(.*)=(.*)vbuc1(.*)", ".*=.*aa.*|.*c1.*c1.*|.*c1_deref.*", "lda #{c1}\n", "$1=$2vbuaa$3", null, mapC));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)vbsc1(.*)", ".*=.*aa.*|.*c1.*c1.*|.*c1_deref.*", "lda #{c1}\n", "$1=$2vbsaa$3", null, mapC));
// Synthesize some constant pointers as constant words
synths.add(new FragmentSynthesis("(.*)_(lt|gt|le|ge|eq|neq)_p..([cz].)_then_(.*)", null, null, "$1_$2_vwu$3_then_$4", null, null));
synths.add(new FragmentSynthesis("p..([cz].)_(lt|gt|le|ge|eq|neq)_(.*)", null, null, "vwu$1_$2_$3", null, null));
synths.add(new FragmentSynthesis("(.*)=p..([zc].)", null, null, "$1=vwu$2", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)_(plus|minus|bor|bxor)_p..([cz].)", null, null, "$1=$2_$3_vwu$4", null, null));
synths.add(new FragmentSynthesis("(.*)=p..([cz].)_(plus|minus|bor|bxor)_(.*)", null, null, "$1=vwu$2_$3_$4", null, null));
synths.add(new FragmentSynthesis("p..([cz].)=(.*)_(sethi|setlo|plus|minus)_(.*)", null, null, "vwu$1=$2_$3_$4", null, null));
synths.add(new FragmentSynthesis("(.*)=p..([cz].)_(sethi|setlo|plus|minus)_(.*)", null, null, "$1=vwu$2_$3_$4", null, null));
return synths;
}
// Synthesize some constant pointers as constant words
synths.add(new AsmFragmentSynthesis("(.*)_(lt|gt|le|ge|eq|neq)_p..([cz].)_then_(.*)", null, null, "$1_$2_vwu$3_then_$4", null, null));
synths.add(new AsmFragmentSynthesis("p..([cz].)_(lt|gt|le|ge|eq|neq)_(.*)", null, null, "vwu$1_$2_$3", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=p..([zc].)", null, null, "$1=vwu$2", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=(.*)_(plus|minus|bor|bxor)_p..([cz].)", null, null, "$1=$2_$3_vwu$4", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=p..([cz].)_(plus|minus|bor|bxor)_(.*)", null, null, "$1=vwu$2_$3_$4", null, null));
synths.add(new AsmFragmentSynthesis("p..([cz].)=(.*)_(sethi|setlo|plus|minus)_(.*)", null, null, "vwu$1=$2_$3_$4", null, null));
synths.add(new AsmFragmentSynthesis("(.*)=p..([cz].)_(sethi|setlo|plus|minus)_(.*)", null, null, "$1=vwu$2_$3_$4", null, null));
return synths;
}
}

View File

@ -1,6 +1,7 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.asm.*;
import dk.camelot64.kickc.fragment.AsmFormat;
import dk.camelot64.kickc.fragment.AsmFragment;
import dk.camelot64.kickc.fragment.AsmFragmentManager;
import dk.camelot64.kickc.fragment.AsmFragmentSignature;
@ -107,7 +108,7 @@ public class Pass4CodeGeneration {
if(!(constantVar.getValue() instanceof ConstantArrayList || constantVar.getValue() instanceof ConstantArrayFilled|| constantVar.getType().equals(SymbolType.STRING))) {
String asmName = constantVar.getAsmName() == null ? constantVar.getLocalName() : constantVar.getAsmName();
if (asmName != null && !added.contains(asmName)) {
asm.addConstant(asmName.replace("#", "_").replace("$", "_"), AsmFragment.getAsmConstant(program, constantVar.getValue(), 99, scopeRef));
asm.addConstant(asmName.replace("#", "_").replace("$", "_"), AsmFormat.getAsmConstant(program, constantVar.getValue(), 99, scopeRef));
added.add(asmName);
}
}
@ -127,7 +128,7 @@ public class Pass4CodeGeneration {
for (ConstantVar constantVar : scopeConstants) {
Integer declaredAlignment = constantVar.getDeclaredAlignment();
if(declaredAlignment !=null) {
String alignment = AsmFragment.getAsmNumber(declaredAlignment);
String alignment = AsmFormat.getAsmNumber(declaredAlignment);
asm.addDataAlignment(alignment);
}
if(constantVar.getValue() instanceof ConstantArrayList) {
@ -136,7 +137,7 @@ public class Pass4CodeGeneration {
if (asmName != null && !added.contains(asmName)) {
List<String> asmElements = new ArrayList<>();
for (ConstantValue element : constantArrayList.getElements()) {
String asmElement = AsmFragment.getAsmConstant(program, element, 99, scopeRef);
String asmElement = AsmFormat.getAsmConstant(program, element, 99, scopeRef);
asmElements.add(asmElement);
}
if(SymbolType.isByte(constantArrayList.getElementType())) {
@ -157,7 +158,7 @@ public class Pass4CodeGeneration {
}
} else if(constantVar.getType().equals(SymbolType.STRING)) {
String asmName = constantVar.getAsmName() == null ? constantVar.getLocalName() : constantVar.getAsmName();
String asmConstant = AsmFragment.getAsmConstant(program, constantVar.getValue(), 99, scopeRef);
String asmConstant = AsmFormat.getAsmConstant(program, constantVar.getValue(), 99, scopeRef);
asm.addDataString(asmName.replace("#", "_").replace("$", "_"), asmConstant);
added.add(asmName);
}
@ -221,7 +222,7 @@ public class Pass4CodeGeneration {
StatementAssignment assignment = (StatementAssignment) statement;
AsmFragmentSignature signature = new AsmFragmentSignature(assignment, assignmentAlu, program);
AsmFragment asmFragment = AsmFragmentManager.getFragment(signature, program.getLog());
asm.getCurrentSegment().setFragment(asmFragment.getName());
asm.getCurrentSegment().setFragment(asmFragment.getFragmentName());
asmFragment.generate(asm);
aluState.clear();
return;
@ -248,14 +249,14 @@ public class Pass4CodeGeneration {
} else {
AsmFragmentSignature asmFragmentSignature = new AsmFragmentSignature(assignment, program);
AsmFragment asmFragment = AsmFragmentManager.getFragment(asmFragmentSignature, program.getLog());
asm.getCurrentSegment().setFragment(asmFragment.getName());
asm.getCurrentSegment().setFragment(asmFragment.getFragmentName());
asmFragment.generate(asm);
}
}
} else if (statement instanceof StatementConditionalJump) {
AsmFragmentSignature asmSignature = new AsmFragmentSignature((StatementConditionalJump) statement, block, program, getGraph());
AsmFragment asmFragment = AsmFragmentManager.getFragment(asmSignature, program.getLog());
asm.getCurrentSegment().setFragment(asmFragment.getName());
asm.getCurrentSegment().setFragment(asmFragment.getFragmentName());
asmFragment.generate(asm);
} else if (statement instanceof StatementCall) {
StatementCall call = (StatementCall) statement;
@ -365,7 +366,7 @@ public class Pass4CodeGeneration {
} else {
AsmFragmentSignature asmSignature = new AsmFragmentSignature(lValue, rValue, program, scope);
AsmFragment asmFragment = AsmFragmentManager.getFragment(asmSignature, program.getLog());
asm.getCurrentSegment().setFragment(asmFragment.getName());
asm.getCurrentSegment().setFragment(asmFragment.getFragmentName());
asmFragment.generate(asm);
}
}