diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecBuilder.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecBuilder.java index a3ca5bddc..30afba97c 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecBuilder.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecBuilder.java @@ -13,6 +13,7 @@ import dk.camelot64.kickc.model.operators.OperatorBinary; import dk.camelot64.kickc.model.operators.OperatorUnary; import dk.camelot64.kickc.model.operators.Operators; import dk.camelot64.kickc.model.statements.*; +import dk.camelot64.kickc.model.symbols.Bank; import dk.camelot64.kickc.model.symbols.Label; import dk.camelot64.kickc.model.symbols.Procedure; import dk.camelot64.kickc.model.symbols.Symbol; @@ -51,17 +52,15 @@ final public class AsmFragmentInstanceSpecBuilder { * @param program The program * @return the fragment instance spec factory */ - public static AsmFragmentInstanceSpec callBanked(Procedure.CallingDistance callingDistance, String callingConvention, String procedureName, Program program) { + public static AsmFragmentInstanceSpec callBanked(String callingConvention, Procedure.CallingProximity proximity, Bank toBank, String procedureName, Program program) { AsmFragmentBindings bindings = new AsmFragmentBindings(program); - AsmFragmentSignature signature = new AsmFragmentSignature.CallBanked(callingDistance, callingConvention); + AsmFragmentSignature signature = new AsmFragmentSignature.CallBanked(callingConvention, proximity, toBank); ScopeRef codeScope = program.getScope().getRef(); - bindings.bind("c1", new ConstantInteger(callingDistance.getBankNumber())); + bindings.bind("c1", new ConstantInteger(toBank.bankNumber())); bindings.bind("la1", new LabelRef(procedureName)); return new AsmFragmentInstanceSpec(program, signature, bindings, codeScope); } - - /** * Create a fragment instance spec factory for an interrupt routine entry * diff --git a/src/main/java/dk/camelot64/kickc/fragment/signature/AsmFragmentSignature.java b/src/main/java/dk/camelot64/kickc/fragment/signature/AsmFragmentSignature.java index 509680120..4b37b89a5 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/signature/AsmFragmentSignature.java +++ b/src/main/java/dk/camelot64/kickc/fragment/signature/AsmFragmentSignature.java @@ -2,6 +2,7 @@ package dk.camelot64.kickc.fragment.signature; import dk.camelot64.kickc.asm.fragment.signature.AsmFragmentSignatureLexer; import dk.camelot64.kickc.asm.fragment.signature.AsmFragmentSignatureParser; +import dk.camelot64.kickc.model.symbols.Bank; import dk.camelot64.kickc.model.symbols.Procedure; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; @@ -55,7 +56,7 @@ public interface AsmFragmentSignature { } /** - * ASM fragment signature for a conditional jump if(A) goto B. + * ASM fragment signature for a call */ class Call implements AsmFragmentSignature { @@ -72,21 +73,25 @@ public interface AsmFragmentSignature { } /** - * ASM fragment signature for a banked jsr if(A) goto B. + * ASM fragment signature for a banked call */ class CallBanked implements AsmFragmentSignature { - final private Procedure.CallingDistance callingDistance; final private String callingConvention; - public CallBanked(Procedure.CallingDistance callingDistance, String callingConvention) { - this.callingDistance = callingDistance; + final private Procedure.CallingProximity proximity; + + final private Bank toBank; + + public CallBanked(String callingConvention, Procedure.CallingProximity proximity, Bank toBank) { this.callingConvention = callingConvention; + this.proximity = proximity; + this.toBank = toBank; } @Override public String getName() { - return "call_" + callingConvention.toLowerCase() + "_" + callingDistance.getFragmentName().toLowerCase(); + return "call_" + callingConvention.toLowerCase() + "_" + proximity.toString().toLowerCase() + (proximity.equals(Procedure.CallingProximity.NEAR)?"":("_"+toBank.bankArea())); } } diff --git a/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java b/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java index 40da8c1d2..4252fb9d6 100644 --- a/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java +++ b/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java @@ -65,88 +65,33 @@ public class Procedure extends Scope { this.bank = bank; } + /** The different distances between banked code, which will determine the type of call needed. */ public enum CallingProximity { - NEAR("near"), - CLOSE("close"), - FAR("far"); + /** No bank change is needed. Caller and callee are both in the same bank or in the common bank. */ + NEAR, + /** A direct bank change is needed. Caller is in the common bank or a different banking area. */ + CLOSE, + /** A trampoline bank change is needed. Caller and callee are different banks of the same banking area. */ + FAR; - public String getProximity() { - return proximity; - } - - private final String proximity; - - CallingProximity(String proximity) { - this.proximity = proximity; - } - } - - /** The method for expressing the call distance to implement banking - * - * The following variations exist related to banked calls: - * - #1 - unbanked to unbanked and no banking areas - * - #2 - unbanked to banked to any bank area - * - #3 - banked to unbanked from any bank area - * - #4 - banked to same bank in same bank area - * - #5 - banked to different bank in same bank area - * - #6 - banked to any bank between different bank areas - * - * This brings us to the call types: - * - CallingDistance.NEAR - case #1, #3, #4 - * - CallingDistance.CLOSE - case #2, #6 - * - CallingDistance.FAR - case #5 - */ - public static class CallingDistance { - - private CallingProximity proximity; - private String bankArea; - private Long bank; - - public CallingProximity getProximity() { - return proximity; - } - - public String getBankArea() { - return bankArea; - } - - public Long getBankNumber() { - return bank; - } - - - public CallingDistance(Procedure from, Procedure to) { - if (((!from.isBanked() && !to.isBanked())) || - ((from.isBanked() && !to.isBanked())) || - ((from.isBanked() && to.isBanked()) && - (from.getBankNumber() == to.getBankNumber()) && - (from.getBankArea().contentEquals(to.getBankArea())) - ) - ) { - // near call - case #1, #3, #4 - this.proximity = CallingProximity.NEAR; - this.bankArea = ""; - this.bank = 0L; + public static CallingProximity forCall(Bank from, Bank to) { + if(to==null) { + // NEAR: call to the common bank + return NEAR; + } else if(to.equals(from)) { + // NEAR: call to the same bank in the same banking area + return NEAR; + } else if(from==null) { + // CLOSE: call from common bank to any bank + return CLOSE; + } else if(!from.bankArea().equals(to.bankArea())) { + // CLOSE: from one banking area to another + return CLOSE; } else { - if ((!from.isBanked() && to.isBanked()) || - ((from.isBanked() && to.isBanked()) && (!from.getBankArea().contentEquals(to.getBankArea()))) - ) { - // close call - case #2, #6 - this.proximity = CallingProximity.CLOSE; - this.bankArea = to.getBankArea(); - this.bank = to.getBankNumber(); - } else { - // far call - case #5 - this.proximity = CallingProximity.FAR; - this.bankArea = to.getBankArea(); - this.bank = to.getBankNumber(); - } + // FAR: banked to different bank in same bank area + return FAR; } } - - public String getFragmentName() { - return this.proximity.getProximity() + (this.bankArea.isEmpty() ? "" : "_" + this.bankArea); - } } /** The method for passing parameters and return value to the procedure. */ diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 1aaaa8c49..2211d4fd1 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -891,7 +891,6 @@ public class Pass4CodeGeneration { } } else if (Procedure.CallingConvention.PHI_CALL.equals(toProcedure.getCallingConvention())) { // Generate PHI transition - boolean generatedPhis = false; if (genCallPhiEntry) { ControlFlowBlock callSuccessor = getGraph().getCallSuccessor(block); if (callSuccessor != null && callSuccessor.hasPhiBlock()) { @@ -900,18 +899,17 @@ public class Pass4CodeGeneration { throw new InternalError("Error! JSR transition already generated. Must modify PhiTransitions code to ensure this does not happen."); } genBlockPhiTransition(asm, block, callSuccessor, block.getScope()); - generatedPhis = true; } } - final Procedure.CallingDistance callingDistance = new Procedure.CallingDistance(fromProcedure, toProcedure); - if(Procedure.CallingProximity.NEAR.equals(callingDistance.getProximity())) { + final Procedure.CallingProximity callingProximity = Procedure.CallingProximity.forCall(fromProcedure.getBank(), toProcedure.getBank()); + if(Procedure.CallingProximity.NEAR.equals(callingProximity)) { asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false); } else { - AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.callBanked(callingDistance,"phi", call.getProcedure().getFullName(), program), program); + AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.callBanked("phi", callingProximity, toProcedure.getBank(), call.getProcedure().getFullName(), program), program); } } else if (Procedure.CallingConvention.STACK_CALL.equals(toProcedure.getCallingConvention())) { - final Procedure.CallingDistance callingDistance = new Procedure.CallingDistance(fromProcedure, toProcedure); - if(Procedure.CallingProximity.NEAR.equals(callingDistance.getProximity())) { + final Procedure.CallingProximity callingProximity = Procedure.CallingProximity.forCall(fromProcedure.getBank(), toProcedure.getBank()); + if(Procedure.CallingProximity.NEAR.equals(callingProximity)) { asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false); } else { throw new CompileError("Stack Call procedure not supported in banked mode " + toProcedure.toString(program)); @@ -924,8 +922,8 @@ public class Pass4CodeGeneration { ProgramScope scope = getScope(); Procedure toProcedure = scope.getProcedure(procedureRef); Procedure fromProcedure = block.getProcedure(this.program); - final Procedure.CallingDistance callingDistance = new Procedure.CallingDistance(fromProcedure, toProcedure); - if(Procedure.CallingProximity.NEAR.equals(callingDistance.getProximity())) { + final Procedure.CallingProximity callingProximity = Procedure.CallingProximity.forCall(fromProcedure.getBank(), toProcedure.getBank()); + if(Procedure.CallingProximity.NEAR.equals(callingProximity)) { AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program); } else { throw new CompileError("Stack Call procedure not supported in banked mode " + toProcedure.toString(program));