diff --git a/.gitignore b/.gitignore
index c7169a380..0ae827ae2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+/gen
+**/.vscode/*
*/*.mon
*/*.vs
*/*.brk
@@ -5,11 +7,12 @@
*/*.sym
*/.tmpdirs
*/bin/
-/target/
+**/target/*
*/workspace.xml
**/.DS_Store
.project
.tmpdirs
**/.vscode/*.log
/.idea/*
-kickc.iml
\ No newline at end of file
+kickc.iml
+/src/main/fragment/cache
diff --git a/src/main/antlr4/dk/camelot64/kickc/parser/KickCLexer.g4 b/src/main/antlr4/dk/camelot64/kickc/parser/KickCLexer.g4
index b6828d097..98ef7914a 100644
--- a/src/main/antlr4/dk/camelot64/kickc/parser/KickCLexer.g4
+++ b/src/main/antlr4/dk/camelot64/kickc/parser/KickCLexer.g4
@@ -75,6 +75,7 @@ LOCAL_RESERVE: '__zp_reserve' ;
ADDRESS: '__address' ;
ADDRESS_ZEROPAGE: '__zp' ;
ADDRESS_MAINMEM: '__mem' ;
+BANK: '__bank' ;
FORM_SSA: '__ssa' ;
FORM_MA: '__ma' ;
INTRINSIC: '__intrinsic' ;
diff --git a/src/main/antlr4/dk/camelot64/kickc/parser/KickCParser.g4 b/src/main/antlr4/dk/camelot64/kickc/parser/KickCParser.g4
index 574d23c4b..9dcea6f49 100644
--- a/src/main/antlr4/dk/camelot64/kickc/parser/KickCParser.g4
+++ b/src/main/antlr4/dk/camelot64/kickc/parser/KickCParser.g4
@@ -161,6 +161,7 @@ directive
| EXTERN #directiveExtern
| EXPORT #directiveExport
| INLINE #directiveInline
+ | BANK PAR_BEGIN NAME COMMA NUMBER PAR_END #directiveBank
| INTRINSIC #directiveIntrinsic
| INTERRUPT ( PAR_BEGIN NAME PAR_END )? #directiveInterrupt
| LOCAL_RESERVE PAR_BEGIN pragmaParam ( COMMA pragmaParam )* PAR_END #directiveReserveZp
diff --git a/src/main/fragment/mos6502-common/call_phi_close_cx16_ram.asm b/src/main/fragment/mos6502-common/call_phi_close_cx16_ram.asm
new file mode 100644
index 000000000..87678c012
--- /dev/null
+++ b/src/main/fragment/mos6502-common/call_phi_close_cx16_ram.asm
@@ -0,0 +1,11 @@
+sta $ff
+lda $0
+pha
+lda #{c1}
+sta $0
+lda $ff
+jsr {la1}
+sta $ff
+pla
+sta $0
+lda $ff
\ No newline at end of file
diff --git a/src/main/fragment/mos6502-common/call_phi_close_cx16_rom.asm b/src/main/fragment/mos6502-common/call_phi_close_cx16_rom.asm
new file mode 100644
index 000000000..5eb46e849
--- /dev/null
+++ b/src/main/fragment/mos6502-common/call_phi_close_cx16_rom.asm
@@ -0,0 +1,11 @@
+sta $ff
+lda $1
+pha
+lda #{c1}
+sta $1
+lda $ff
+jsr {la1}
+sta $ff
+pla
+sta $1
+lda $ff
\ No newline at end of file
diff --git a/src/main/fragment/mos6502-common/call_phi_far_cx16_ram.asm b/src/main/fragment/mos6502-common/call_phi_far_cx16_ram.asm
new file mode 100644
index 000000000..6be1d836e
--- /dev/null
+++ b/src/main/fragment/mos6502-common/call_phi_far_cx16_ram.asm
@@ -0,0 +1,4 @@
+jsr $FF6E
+.byte <{la1}
+.byte >{la1}
+.byte {c1}
\ No newline at end of file
diff --git a/src/main/fragment/mos6502-common/call_phi_far_cx16_rom.asm b/src/main/fragment/mos6502-common/call_phi_far_cx16_rom.asm
new file mode 100644
index 000000000..6be1d836e
--- /dev/null
+++ b/src/main/fragment/mos6502-common/call_phi_far_cx16_rom.asm
@@ -0,0 +1,4 @@
+jsr $FF6E
+.byte <{la1}
+.byte >{la1}
+.byte {c1}
\ No newline at end of file
diff --git a/src/main/fragment/mos6502-common/call_phi_near.asm b/src/main/fragment/mos6502-common/call_phi_near.asm
new file mode 100644
index 000000000..443c46caa
--- /dev/null
+++ b/src/main/fragment/mos6502-common/call_phi_near.asm
@@ -0,0 +1 @@
+jsr {la1}
\ No newline at end of file
diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java
index cc8b197cd..f5a16b1d9 100644
--- a/src/main/java/dk/camelot64/kickc/Compiler.java
+++ b/src/main/java/dk/camelot64/kickc/Compiler.java
@@ -224,6 +224,7 @@ public class Compiler {
new Pass1Procedures(program).execute();
new PassNTypeInference(program).execute();
new PassNFixIntermediateMemoryArea(program).execute();
+ new Pass1FixProcedureParamSegment(program).execute();
new PassNTypeIdSimplification(program).execute();
new Pass1StructTypeSizeFix(program).execute();
new Pass1PrintfIntrinsicRewrite(program).execute();
diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecBuilder.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecBuilder.java
index 6ab66739f..acd60c256 100644
--- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecBuilder.java
+++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecBuilder.java
@@ -13,7 +13,9 @@ 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;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeInference;
@@ -41,6 +43,27 @@ final public class AsmFragmentInstanceSpecBuilder {
return new AsmFragmentInstanceSpec(program, signature, bindings, codeScope);
}
+ /**
+ * Create a fragment instance spec factory for a banked call
+ *
+ * @param toProcedure The procedure being called
+ * @param callingDistance The calling distance of the call
+ * @param program The program
+ * @return the fragment instance spec factory
+ */
+ public static AsmFragmentInstanceSpec callBanked(Procedure toProcedure, Bank.CallingDistance callingDistance, Program program) {
+ final Bank toBank = toProcedure.getBank();
+ AsmFragmentBindings bindings = new AsmFragmentBindings(program);
+ AsmFragmentSignature signature = new AsmFragmentSignature.CallBanked(
+ toProcedure.getCallingConvention().getShortName(),
+ callingDistance.toString(),
+ (callingDistance.equals(Bank.CallingDistance.NEAR)?null:toBank.bankArea()));
+ ScopeRef codeScope = program.getScope().getRef();
+ bindings.bind("c1", new ConstantInteger(toBank.bankNumber()));
+ bindings.bind("la1", new LabelRef(toProcedure.getFullName()));
+ 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/AsmFragmentTemplate.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java
index 94f2aa4b8..150d8de48 100644
--- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java
+++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java
@@ -143,6 +143,7 @@ public class AsmFragmentTemplate {
if(signature.contains("c6")) bindings.put("c6", new ConstantInteger(360L));
if(signature.contains("la1")) bindings.put("la1", new Label("@1", scope, true));
if(signature.startsWith("call_")) bindings.put("la1", new Label("@1", scope, true));
+ if(signature.startsWith("call_")) bindings.put("c1", new ConstantInteger(400L));
AsmFragmentInstance fragmentInstance =
new AsmFragmentInstance(new Program(), signature, ScopeRef.ROOT, this, bindings);
AsmProgram asm = new AsmProgram(targetCpu);
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 33982bc5c..16c8b76a9 100644
--- a/src/main/java/dk/camelot64/kickc/fragment/signature/AsmFragmentSignature.java
+++ b/src/main/java/dk/camelot64/kickc/fragment/signature/AsmFragmentSignature.java
@@ -54,7 +54,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 {
@@ -70,6 +70,29 @@ public interface AsmFragmentSignature {
}
}
+ /**
+ * ASM fragment signature for a banked call
+ */
+ class CallBanked implements AsmFragmentSignature {
+
+ final private String callingConvention;
+
+ final private String callingDistance;
+
+ final private String toBankArea;
+
+ public CallBanked(String callingConvention, String callingDistance, String toBankArea) {
+ this.callingConvention = callingConvention;
+ this.callingDistance = callingDistance;
+ this.toBankArea = toBankArea;
+ }
+
+ @Override
+ public String getName() {
+ return "call_" + callingConvention + "_" + callingDistance + ((toBankArea==null)?"":("_"+toBankArea));
+ }
+ }
+
/**
* ASM fragment signature for a conditional jump if(A) goto B
.
*/
diff --git a/src/main/java/dk/camelot64/kickc/model/Directive.java b/src/main/java/dk/camelot64/kickc/model/Directive.java
index 968153e0c..248a554e9 100644
--- a/src/main/java/dk/camelot64/kickc/model/Directive.java
+++ b/src/main/java/dk/camelot64/kickc/model/Directive.java
@@ -43,6 +43,29 @@ public class Directive {
public Inline() { super("inline"); }
}
+ /**
+ * Bank to place code into. Used for determining call distance.
+ */
+ static public class Bank extends Directive {
+
+ private String bankArea;
+ private Long bankNumber;
+
+ public Bank(String bankArea, Long bankNumber) {
+ super("bank" );
+ this.bankArea = bankArea;
+ this.bankNumber = bankNumber;
+ }
+
+ public String getBankArea() {
+ return bankArea;
+ }
+
+ public Long getBankNumber() {
+ return bankNumber;
+ }
+ }
+
/** Function declared intrinsic. */
public static class Intrinsic extends Directive {
public Intrinsic() { super("intrinsic"); }
diff --git a/src/main/java/dk/camelot64/kickc/model/symbols/Bank.java b/src/main/java/dk/camelot64/kickc/model/symbols/Bank.java
new file mode 100644
index 000000000..8c2796371
--- /dev/null
+++ b/src/main/java/dk/camelot64/kickc/model/symbols/Bank.java
@@ -0,0 +1,173 @@
+package dk.camelot64.kickc.model.symbols;
+
+/**
+ *
+ * Specific target computer platforms implement a memory layout that can be banked either in ram or rom.
+ * This class models the capability to calculate which function call implementations are banked and which not.
+ *
Since banking implementations are specific to the target computer platform,
+ * specific assembler fragments are available in the compiler,
+ * that model the implementation of the banking, depending on:
+ *
+ *
+ * - the computer target computer platform.
+ * - the bank method of function call implementation at the call location.
+ *
+ *
+ * The method can implement different banking implementations,
+ * depending on the required function call speed,
+ * the banking routine availability in the kernal (rom) of the target computer platform
+ * or even using specific designed banking routines by kickc to provide alternatives for banking methods.
+ * The method and the platform are the key combinations that select the fragments
+ * in kickc to generate the banked function call implementations used by the compiler.
+ *
+ *
Your C-code can be augmented with 3 new directives, that define which function(s) will be declared as banked:
+ *
+ *
+ * - #pragma bank( area, number ) directive, defines for sequent functions
+ * the the area and the bank number.
+ * - A new #pragma nobank( dummy ) directive, resets the calculation of banking
+ * for the sequent functions.
+ * - __bank( area, number ) directive, defines for one function a
+ * target bank area and number.
+ *
+ * Examples of bank areas are different RAM and/or ROM areas at different
+ * zones in the linear memory addressing space, and may or may not overlap.
+ * Each banking area has its own configuration in the target computer platform and
+ * operate independently from each other.
+ * For example, the Commander X16 has 256 RAM bank between 0xA000 and 0xBFFF and
+ * 256 ROM banks from 0xC000 till 0xFFFF. Each RAM bank is configured through zero page $00
+ * and each ROM bank is configured through zero page $01.
+ * The compiler configures for you these registers, depending on the configured banking area of the function,
+ * when a banked call is required.
+ *
+ *
There are different types of function calls that can be implemented, depending on:
+ *
+ * - the banked location of either or both the caller and/or the called function.
+ * - the banking area of either or both the caller and the called function.
+ *
+ *
+ * The types of (banked) function call implementations are:
+ *
+ * - near - jump to subroutine without any implementation of banking logic.
+ * - close - jump to subroutine with inline banking implementation logic.
+ * - far - jump to subroutine using a specific trampoline banking implementation logic.
+ *
+ * Depending on the target platform of the system the far calls are likely the most complex
+ * and will consume the most CPU cycles. Therefore, careful design of the program flow and the
+ * placement of the functions in the banks need to be done, to avoid as much as possible these far calls.
+ *
+ *
The exact rules when near, close and far calls are implemented by the compiler,
+ * can be summarized in the following 6 cases:
+ *
+ *
+ * - case #1 - If the caller function is not banked,
+ * and the called function is not banked,
+ * then a near call will be implemented.
+ * - case #2 - If the caller function is not banked,
+ * and the called function is banked in any bank in any banking area,
+ * then a close call will be implemented.
+ * - case #3 - If the caller function is banked in any bank in any banking area,
+ * and the called function is not banked,
+ * then a near call will be implemented.
+ * - case #4 - If the caller function is banked,
+ * and the called function is banked,
+ * and both functions are in the same bank and in the same bank area,
+ * then a near call will be implemented.
+ * - case #5 - If the caller function is banked,
+ * and the called function is banked,
+ * and both functions are in a different bank but in the same bank area,
+ * then a far call will be implemented.
+ * - case #6 - If the caller function is banked,
+ * and the called function is banked,
+ * and both functions are in any bank but in a different bank area,
+ * then the a close call will be implemented.
+ *
+ *
+ * The usage of #pragma code_seg( segment ) directive
+ * is very important! The #pragma code_seg( segment ) directive utilization
+ * in harmony with the #pragma bank( method, number ) directive
+ * makes the overall banking implementation work for your program.
+ *
+ *
+ *
+ * - KickC uses the #pragma code_seg( segment ) directive to calculate the
+ * addressing of the allocation of the function code within main or banked memory.
+ * - KickC uses the #pragma bank( method, number ) directive or
+ * __bank( method, number ) directive to calculate the function call implementation
+ * at the function calling locations!
+ *
+ *
+ * The KickC compiler contains several test cases and examples which demonstrate the usage of the banking system.
+ *
+ * @param bankArea The bankable area name.
+ * @param bankNumber The bank number.
+ */
+public record Bank(String bankArea, Long bankNumber) {
+
+ /**
+ * Creates a new Bank which collects the necessary data to handle banking.
+ * For example, on the Commander X16, RAM is banked from address 0xA000 till 0xBFFF.
+ * Zeropage 0x00 configures this banked RAM, with a number from 0x00 till 0xff.
+ * So "Banked RAM" is is a bankArea, and the bank is a configurable bank number in the bankArea.
+ *
+ * @param bankArea A bank area is a memory range that is banked on a target platform.
+ * @param bankNumber A bank is a number that defines a bank configuration in a bank area.
+ */
+ public Bank {
+ }
+
+ /** The common/shared bank which is always visible. */
+ public static Bank COMMON = new Bank("", 0L);
+
+ /**
+ * Is this the common/shared bank which is always visible.
+ * @return True if this is the common bank
+ */
+ public boolean isCommon() {
+ return COMMON.equals(this);
+ }
+
+ @Override
+ public String toString() {
+ if(isCommon()) {
+ return "";
+ } else {
+ return "__bank(" + this.bankArea() + ", " + this.bankNumber() + ") ";
+ }
+ }
+
+ /** The bank distance between a caller and callee, which will determine the type of call needed. */
+ public enum CallingDistance {
+ /** Caller and callee are both in the same bank or in the common bank. No bank change is needed. */
+ NEAR,
+ /** Caller is in the common bank or a different banking area. A direct bank change is needed. */
+ CLOSE,
+ /** Caller and callee are different banks of the same banking area. A trampoline bank change is needed. */
+ FAR;
+
+ public static CallingDistance forCall(Bank from, Bank to) {
+ if(to.isCommon()) {
+ // 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.isCommon()) {
+ // 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 {
+ // FAR: banked to different bank in same bank area
+ return FAR;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
+
+ }
+}
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 ce46bb5f1..0f59f17a3 100644
--- a/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java
+++ b/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java
@@ -33,14 +33,19 @@ public class Procedure extends Scope {
private List comments;
/** Reserved zeropage addresses. */
private List reservedZps;
- /** The code segment to put the procedure into. */
- private String codeSegment;
+ /** The code segment to put the procedure code into. When null the procedure is not assigned to the code segment. */
+ private String segmentCode;
/** The list of constructor procedures for this procedure. The constructor procedures are called during program initialization. */
private final List constructorRefs;
/** Is this procedure declared as a constructor procedure. */
private boolean isConstructor;
/** The source of the procedure definition. */
private StatementSource definitionSource;
+ /**
+ * The bank that the procedure code is placed in.
+ * Used to decide whether to produce near, close or far call code when generating calls.
+ */
+ private Bank bank;
/** The names of all legal intrinsic procedures. */
final public static List INTRINSIC_PROCEDURES = Arrays.asList(
@@ -50,27 +55,42 @@ public class Procedure extends Scope {
Pass1ByteXIntrinsicRewrite.INTRINSIC_MAKELONG4
);
+ public Bank getBank() {
+ return bank;
+ }
+
+ public void setBank(Bank bank) {
+ this.bank = Objects.requireNonNull(bank);
+ }
+
/** The method for passing parameters and return value to the procedure. */
public enum CallingConvention {
/** Parameters and return value handled through PHI-transitions. */
- PHI_CALL("__phicall"),
+ PHI_CALL("__phicall", "phi"),
/** Parameters and return value over the stack. */
- STACK_CALL("__stackcall"),
+ STACK_CALL("__stackcall", "stack"),
/** Parameters and return value handled through shared variables. */
- VAR_CALL("__varcall"),
+ VAR_CALL("__varcall", "var"),
/** Intrinsic calling. Will be converted to intrinsic ASM late in the compile. */
- INTRINSIC_CALL("__intrinsiccall");
+ INTRINSIC_CALL("__intrinsiccall", "intrinsic");
private final String name;
- CallingConvention(String name) {
+ private final String shortName;
+
+ CallingConvention(String name, String shortName) {
this.name = name;
+ this.shortName = shortName;
}
public String getName() {
return name;
}
+ public String getShortName() {
+ return shortName;
+ }
+
/** Get a calling convention by name. */
public static CallingConvention getCallingConvension(String name) {
for(CallingConvention value : CallingConvention.values()) {
@@ -85,13 +105,14 @@ public class Procedure extends Scope {
/** The calling convention used for this procedure. */
private CallingConvention callingConvention;
- public Procedure(String name, SymbolTypeProcedure procedureType, Scope parentScope, String codeSegment, String dataSegment, CallingConvention callingConvention) {
- super(name, parentScope, dataSegment);
+ public Procedure(String name, SymbolTypeProcedure procedureType, Scope parentScope, String segmentCode, String segmentData, CallingConvention callingConvention, Bank bank) {
+ super(name, parentScope, segmentData);
this.procedureType = procedureType;
this.declaredInline = false;
+ this.bank = Objects.requireNonNull(bank);
this.interruptType = null;
this.comments = new ArrayList<>();
- this.codeSegment = codeSegment;
+ this.segmentCode = Objects.requireNonNull(segmentCode);
this.callingConvention = callingConvention;
this.constructorRefs = new ArrayList<>();
this.isConstructor = false;
@@ -113,12 +134,12 @@ public class Procedure extends Scope {
this.callingConvention = callingConvention;
}
- public String getCodeSegment() {
- return codeSegment;
+ public String getSegmentCode() {
+ return segmentCode;
}
- public void setCodeSegment(String codeSegment) {
- this.codeSegment = codeSegment;
+ public void setSegmentCode(String segmentCode) {
+ this.segmentCode = segmentCode;
}
public List getParameterNames() {
@@ -262,6 +283,7 @@ public class Procedure extends Scope {
if(declaredIntrinsic) {
res.append("__intrinsic ");
}
+ res.append(bank.toString());
if(!callingConvention.equals(CallingConvention.PHI_CALL)) {
res.append(getCallingConvention().getName()).append(" ");
}
@@ -307,13 +329,14 @@ public class Procedure extends Scope {
Objects.equals(interruptType, procedure.interruptType) &&
Objects.equals(comments, procedure.comments) &&
Objects.equals(reservedZps, procedure.reservedZps) &&
- Objects.equals(codeSegment, procedure.codeSegment) &&
+ Objects.equals(segmentCode, procedure.segmentCode) &&
Objects.equals(constructorRefs, procedure.constructorRefs) &&
+ Objects.equals(bank, procedure.bank) &&
callingConvention == procedure.callingConvention;
}
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), procedureType, parameterNames, variableLengthParameterList, declaredInline, declaredIntrinsic, interruptType, comments, reservedZps, codeSegment, constructorRefs, isConstructor, callingConvention);
+ return Objects.hash(super.hashCode(), procedureType, parameterNames, variableLengthParameterList, declaredInline, declaredIntrinsic, interruptType, comments, reservedZps, segmentCode, constructorRefs, isConstructor, bank, callingConvention);
}
}
diff --git a/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java b/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java
index ed32ce6ba..a4b6e0736 100644
--- a/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java
+++ b/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java
@@ -30,7 +30,7 @@ public abstract class Scope implements Symbol {
this.name = name;
this.parentScope = parentScope;
this.symbols = new LinkedHashMap<>();
- this.segmentData = segmentData;
+ this.segmentData = Objects.requireNonNull(segmentData);
setFullName();
}
@@ -43,6 +43,10 @@ public abstract class Scope implements Symbol {
return segmentData;
}
+ public void setSegmentData(String segmentData) {
+ this.segmentData = segmentData;
+ }
+
public HashMap getSymbols() {
return symbols;
}
diff --git a/src/main/java/dk/camelot64/kickc/parser/CParser.java b/src/main/java/dk/camelot64/kickc/parser/CParser.java
index f56daca13..76b52a81c 100644
--- a/src/main/java/dk/camelot64/kickc/parser/CParser.java
+++ b/src/main/java/dk/camelot64/kickc/parser/CParser.java
@@ -106,6 +106,14 @@ public class CParser {
* The resource-file is copied to the output directory when compiling.
*/
public static final String PRAGMA_RESOURCE = "resource";
+ /**
+ * #pragma bank(...) changes the current bank. Functions and variables will be placed in the specified bank.
+ */
+ public static final String PRAGMA_BANK = "bank";
+ /**
+ * #pragma nobank Changes the current bank to the default/common/shared bank.
+ */
+ public static final String PRAGMA_NOBANK = "nobank";
/**
* The Program.
diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java
index c8604b28d..a62082a72 100644
--- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java
+++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java
@@ -16,7 +16,6 @@ import dk.camelot64.kickc.parser.KickCParserBaseVisitor;
import dk.camelot64.kickc.passes.utils.SizeOfConstants;
import org.antlr.v4.runtime.BufferedTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
-import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
@@ -131,7 +130,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor()), program.getScope(), Scope.SEGMENT_CODE_DEFAULT, Scope.SEGMENT_DATA_DEFAULT, Procedure.CallingConvention.PHI_CALL);
+ initProc = new Procedure(SymbolRef.INIT_PROC_NAME, new SymbolTypeProcedure(SymbolType.VOID, new ArrayList<>()), program.getScope(), Scope.SEGMENT_CODE_DEFAULT, Scope.SEGMENT_DATA_DEFAULT, Procedure.CallingConvention.PHI_CALL, Bank.COMMON);
initProc.setDeclaredInline(true);
initProc.setParameters(new ArrayList<>());
program.getScope().add(initProc);
@@ -188,7 +187,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor()), program.getScope(), Scope.SEGMENT_CODE_DEFAULT, Scope.SEGMENT_DATA_DEFAULT, Procedure.CallingConvention.PHI_CALL);
+ final Procedure startProcedure = new Procedure(SymbolRef.START_PROC_NAME, new SymbolTypeProcedure(SymbolType.VOID, new ArrayList<>()), program.getScope(), Scope.SEGMENT_CODE_DEFAULT, Scope.SEGMENT_DATA_DEFAULT, Procedure.CallingConvention.PHI_CALL, Bank.COMMON);
startProcedure.setParameters(new ArrayList<>());
program.getScope().add(startProcedure);
final ProcedureCompilation startProcedureCompilation = program.createProcedureCompilation(startProcedure.getRef());
@@ -289,10 +288,24 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor effectiveDirectives = varDecl.getDeclDirectives();
final List declComments = varDecl.getDeclComments();
varDecl.exitVar();
- VariableBuilder varBuilder = new VariableBuilder(varName, getCurrentScope(), false, false, effectiveType, effectiveDirectives, currentDataSegment, program.getTargetPlatform().getVariableBuilderConfig());
+ VariableBuilder varBuilder = new VariableBuilder(varName, getCurrentScope(), false, false, effectiveType, effectiveDirectives, currentSegmentData, program.getTargetPlatform().getVariableBuilderConfig());
Variable variable = varBuilder.build();
if(isStructMember && (initializer != null))
throw new CompileError("Initializer not supported inside structs " + effectiveType.toCDecl(), declSource);
@@ -1136,7 +1156,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor allVariables = getScope().getAllVariables(true);
+ for(Variable variable : allVariables) {
+ if(variable.isKindLoadStore() || variable.isKindPhiMaster() || variable.isKindIntermediate()) {
+ if(variable.getRegister() instanceof Registers.RegisterMainMem registerMainMem && registerMainMem.isAddressHardcoded())
+ continue;
+ if(variable.getDataSegment().equals(Scope.SEGMENT_DATA_DEFAULT))
+ continue;
+
+ final Scope scope = variable.getScope();
+ if(scope instanceof Procedure procedure) {
+ if(!procedure.getBank().isCommon()) {
+ List parameters = procedure.getParameters();
+ if(parameters.contains(variable) || variable.getLocalName().equals("return")) {
+ variable.setDataSegment(Scope.SEGMENT_DATA_DEFAULT);
+ getLog().append("Fixing banked procedure parameter/return value to default segment " + variable.getFullName());
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java
index d2f3bc5a1..29000ba0b 100644
--- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java
+++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java
@@ -115,7 +115,7 @@ public class Pass4CodeGeneration {
currentScope = block.getScope();
if (block.isProcedureEntry(program)) {
Procedure procedure = block.getProcedure(program);
- currentCodeSegmentName = procedure.getCodeSegment();
+ currentCodeSegmentName = procedure.getSegmentCode();
}
setCurrentSegment(currentCodeSegmentName, asm);
asm.startChunk(currentScope, null, block.getLabel().getFullName());
@@ -219,7 +219,7 @@ public class Pass4CodeGeneration {
}
/**
- * Generate a comment that describes the procedure signature and parameter transfer
+ * Generate a comment that describes the procedure signature and parameter transfer.
*
* @param asm The assembler program being generated
* @param procedure The procedure
@@ -240,6 +240,9 @@ public class Pass4CodeGeneration {
if (i > 0) {
asm.addComment(signature.toString(), false);
}
+ if(!procedure.getBank().isCommon()) {
+ asm.addComment(" " + procedure.getBank(), false);
+ }
}
/**
@@ -853,14 +856,15 @@ public class Pass4CodeGeneration {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.conditionalJump((StatementConditionalJump) statement, block, program), program);
} else if (statement instanceof StatementCall) {
StatementCall call = (StatementCall) statement;
- Procedure procedure = getScope().getProcedure(call.getProcedure());
- if (procedure.isDeclaredIntrinsic()) {
- if (Pass1ByteXIntrinsicRewrite.INTRINSIC_MAKELONG4.equals(procedure.getFullName())) {
+ Procedure toProcedure = getScope().getProcedure(call.getProcedure());
+ Procedure fromProcedure = block.getProcedure(this.program); // We obtain from where the procedure is called, to validate the bank equality.
+ if (toProcedure.isDeclaredIntrinsic()) {
+ if (Pass1ByteXIntrinsicRewrite.INTRINSIC_MAKELONG4.equals(toProcedure.getFullName())) {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.makelong4(call, program), program);
} else {
- throw new CompileError("Intrinsic procedure not supported " + procedure.toString(program));
+ throw new CompileError("Intrinsic procedure not supported " + toProcedure.toString(program));
}
- } else if (Procedure.CallingConvention.PHI_CALL.equals(procedure.getCallingConvention())) {
+ } else if (Procedure.CallingConvention.PHI_CALL.equals(toProcedure.getCallingConvention())) {
// Generate PHI transition
if (genCallPhiEntry) {
ControlFlowBlock callSuccessor = getGraph().getCallSuccessor(block);
@@ -872,17 +876,44 @@ public class Pass4CodeGeneration {
genBlockPhiTransition(asm, block, callSuccessor, block.getScope());
}
}
- asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
- } else if (Procedure.CallingConvention.STACK_CALL.equals(procedure.getCallingConvention())) {
- asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
+ final Bank.CallingDistance callingDistance = Bank.CallingDistance.forCall(fromProcedure.getBank(), toProcedure.getBank());
+ if(Bank.CallingDistance.NEAR.equals(callingDistance)) {
+ asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
+ } else {
+ AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.callBanked(toProcedure, callingDistance, program), program);
+ }
+ } else if (Procedure.CallingConvention.STACK_CALL.equals(toProcedure.getCallingConvention())) {
+ final Bank.CallingDistance callingDistance = Bank.CallingDistance.forCall(fromProcedure.getBank(), toProcedure.getBank());
+ if(Bank.CallingDistance.NEAR.equals(callingDistance)) {
+ asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
+ } else {
+ throw new CompileError("Stack Call procedure not supported in banked mode " + toProcedure.toString(program));
+ }
}
} else if (statement instanceof StatementCallExecute) {
StatementCallExecute call = (StatementCallExecute) statement;
- RValue procedureRVal = call.getProcedureRVal();
- // Generate ASM for a call
- AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program);
- if (!(procedureRVal instanceof ProcedureRef)) {
- asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
+ ProcedureRef procedureRef = call.getProcedure();
+ if(procedureRef != null) {
+ ProgramScope scope = getScope();
+ Procedure toProcedure = scope.getProcedure(procedureRef);
+ Procedure fromProcedure = block.getProcedure(this.program);
+ final Bank.CallingDistance callingDistance = Bank.CallingDistance.forCall(fromProcedure.getBank(), toProcedure.getBank());
+ if(Bank.CallingDistance.NEAR.equals(callingDistance)) {
+ AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program);
+ } else {
+ throw new CompileError("Stack Call procedure not supported in banked mode " + toProcedure.toString(program));
+ }
+ RValue procedureRVal = call.getProcedureRVal();
+ if (!(procedureRVal instanceof ProcedureRef)) {
+ asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
+ }
+ } else {
+ RValue procedureRVal = call.getProcedureRVal();
+ // Generate ASM for a call
+ AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program);
+ if (!(procedureRVal instanceof ProcedureRef)) {
+ asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
+ }
}
} else if (statement instanceof StatementExprSideEffect) {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.exprSideEffect((StatementExprSideEffect) statement, program), program);
diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4MemoryCoalesce.java b/src/main/java/dk/camelot64/kickc/passes/Pass4MemoryCoalesce.java
index c229e98fe..03c531702 100644
--- a/src/main/java/dk/camelot64/kickc/passes/Pass4MemoryCoalesce.java
+++ b/src/main/java/dk/camelot64/kickc/passes/Pass4MemoryCoalesce.java
@@ -53,6 +53,7 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
return
canCoalesceNotEqual(ec1, ec2) &&
canCoalesceCompatible(ec1, ec2, program) &&
+ canCoalesceSegments(ec1, ec2, program) &&
canCoalesceVolatile(ec1, ec2, program) &&
canCoalesceThreads(ec1, ec2, threadHeads, program) &&
canCoalesceClobber(ec1, ec2, unknownFragments, program);
@@ -69,6 +70,21 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
return !ec1.equals(ec2);
}
+ /**
+ * Determines if two live range equivalence classes are candidates for coalescing by ensuring they are not from different data segments.
+ * @param ec1 One equivalence class
+ * @param ec2 Another equivalence class
+ * @param program The program
+ * @return True if the two equivalence classes can be coalesced into one without problems.
+ */
+ private static boolean canCoalesceSegments(LiveRangeEquivalenceClass ec1, LiveRangeEquivalenceClass ec2, Program program) {
+ final VariableRef variableRef1 = ec1.getVariables().get(0);
+ final String dataSegment1 = program.getScope().getVar(variableRef1).getDataSegment();
+ final VariableRef variableRef2 = ec2.getVariables().get(0);
+ final String dataSegment2 = program.getScope().getVar(variableRef2).getDataSegment();
+ return dataSegment1.equals(dataSegment2);
+ }
+
/**
* Determines if two live range equivalence classes can be coalesced without cross-thread clobber.
* This is possible if they are both only called from the same thread head.
diff --git a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java
index 042f930e2..df4514641 100644
--- a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java
+++ b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java
@@ -767,6 +767,11 @@ public class TestProgramsFast extends TestPrograms {
compileAndCompare("examples/cx16/cx16-rasterbars.c");
}
+ //@Test
+ //public void testCx16Banking() throws IOException {
+ // compileAndCompare("examples/cx16/banking/cx16-banking.c");
+ //}
+
@Test
public void testMega65Camelot1536Dots() throws IOException {
compileAndCompare("examples/mega65/camelot-1536dots.c");
@@ -1516,6 +1521,83 @@ public class TestProgramsFast extends TestPrograms {
compileAndCompare("declared-memory-var-0.c");
}
+ @Test
+ public void testBankedPhiCase1Near0() throws IOException {
+ compileAndCompare("call-banked-phi-case-1-near-0.c");
+ }
+
+ @Test
+ public void testBankedPhiCase1Near1() throws IOException {
+ compileAndCompare("call-banked-phi-case-1-near-1.c");
+ }
+
+ @Test
+ public void testBankedPhiCase2Close0() throws IOException {
+ compileAndCompare("call-banked-phi-case-2-close-0.c");
+ }
+
+ @Test
+ public void testBankedPhiCase2Close1() throws IOException {
+ compileAndCompare("call-banked-phi-case-2-close-1.c");
+ }
+
+ @Test
+ public void testBankedPhiCase3Near0() throws IOException {
+ compileAndCompare("call-banked-phi-case-3-near-0.c");
+ }
+
+ @Test
+ public void testBankedPhiCase3Near1() throws IOException {
+ compileAndCompare("call-banked-phi-case-3-near-1.c");
+ }
+
+ @Test
+ public void testBankedPhiCase4Near0() throws IOException {
+ compileAndCompare("call-banked-phi-case-4-near-0.c");
+ }
+
+ @Test
+ public void testBankedPhiCase4Near1() throws IOException {
+ compileAndCompare("call-banked-phi-case-4-near-1.c");
+ }
+
+ @Test
+ public void testBankedPhiCase5Far0() throws IOException {
+ compileAndCompare("call-banked-phi-case-5-far-0.c");
+ }
+
+ @Test
+ public void testBankedPhiCase5Far1() throws IOException {
+ compileAndCompare("call-banked-phi-case-5-far-1.c");
+ }
+
+ @Test
+ public void testBankedPhiCase6Close0() throws IOException {
+ compileAndCompare("call-banked-phi-case-6-close-0.c");
+ }
+
+ @Test
+ public void testBankedPhiCase6Close1() throws IOException {
+ compileAndCompare("call-banked-phi-case-6-close-1.c");
+ }
+
+ @Test
+ public void testBankedPhiMemvars() throws IOException {
+ compileAndCompare("call-banked-phi-memvars.c");
+ }
+
+
+ @Test
+ public void testBankedStackCase2Close0() throws IOException {
+ assertError("call-banked-stack-case-2-close-0.c", "Stack Call procedure not supported in banked mode");
+ }
+
+ @Test
+ public void testBankedStackCase5Far0() throws IOException {
+ assertError("call-banked-stack-case-5-far-0.c", "Stack Call procedure not supported in banked mode");
+ }
+
+
@Test
public void testProcedureCallingConventionStack13() throws IOException {
compileAndCompare("procedure-callingconvention-stack-13.c");
diff --git a/src/test/kc/call-banked-phi-case-1-near-0.c b/src/test/kc/call-banked-phi-case-1-near-0.c
new file mode 100644
index 000000000..07b4a9513
--- /dev/null
+++ b/src/test/kc/call-banked-phi-case-1-near-0.c
@@ -0,0 +1,41 @@
+/**
+ * @file call-banked-phi-case-1-near-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be)
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #1.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+
+#pragma link("call-banked-phi.ld")
+
+char* const SCREEN = (char*)0x0400;
+
+#pragma code_seg(Code)
+void main(void) {
+ SCREEN[0] = plus('0', 7); // near call
+ SCREEN[1] = plus('1', 6); // near call
+}
+
+#pragma code_seg(Code)
+char plus(char a, char b) {
+ return a+b;
+}
diff --git a/src/test/kc/call-banked-phi-case-1-near-1.c b/src/test/kc/call-banked-phi-case-1-near-1.c
new file mode 100644
index 000000000..7b5b79ef6
--- /dev/null
+++ b/src/test/kc/call-banked-phi-case-1-near-1.c
@@ -0,0 +1,46 @@
+/**
+ * @file call-banked-phi-case-1-near-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #1
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+
+#pragma link("call-banked-phi.ld")
+
+char* const SCREEN = (char*)0x0400;
+
+#pragma code_seg(Code)
+void main(void) {
+ SCREEN[0] = plus('0', 7); // near call
+ SCREEN[1] = plus('1', 6); // near call
+}
+
+#pragma code_seg(Code)
+char plus(char a, char b) {
+ return add(a, b); // near call
+}
+
+#pragma code_seg(Code)
+char add(char a, char b) {
+ return a+b;
+}
diff --git a/src/test/kc/call-banked-phi-case-2-close-0.c b/src/test/kc/call-banked-phi-case-2-close-0.c
new file mode 100644
index 000000000..ed836b407
--- /dev/null
+++ b/src/test/kc/call-banked-phi-case-2-close-0.c
@@ -0,0 +1,42 @@
+/**
+ * @file call-banked-phi-case-2-close-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #2.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+
+#pragma link("call-banked-phi.ld")
+
+char* const SCREEN = (char*)0x0400;
+
+#pragma code_seg(Code)
+void main(void) {
+ SCREEN[0] = plus('0', 7); // close call
+ SCREEN[1] = plus('1', 6); // close call
+}
+
+#pragma code_seg(RAM_Bank1)
+__bank(cx16_ram,1) char plus(char a, char b) {
+ return a+b;
+}
\ No newline at end of file
diff --git a/src/test/kc/call-banked-phi-case-2-close-1.c b/src/test/kc/call-banked-phi-case-2-close-1.c
new file mode 100644
index 000000000..038007c95
--- /dev/null
+++ b/src/test/kc/call-banked-phi-case-2-close-1.c
@@ -0,0 +1,43 @@
+/**
+ * @file call-banked-phi-case-2-close-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #2.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+
+#pragma link("call-banked-phi.ld")
+
+char* const SCREEN = (char*)0x0400;
+
+#pragma code_seg(Code)
+void main(void) {
+ SCREEN[0] = plus('0', 7); // close call
+ SCREEN[1] = plus('1', 6); // close call
+}
+
+#pragma code_seg(RAM_Bank1)
+#pragma bank(cx16_ram, 1)
+char plus(char a, char b) {
+ return a+b;
+}
\ No newline at end of file
diff --git a/src/test/kc/call-banked-phi-case-3-near-0.c b/src/test/kc/call-banked-phi-case-3-near-0.c
new file mode 100644
index 000000000..76812ead5
--- /dev/null
+++ b/src/test/kc/call-banked-phi-case-3-near-0.c
@@ -0,0 +1,50 @@
+/**
+ * @file call-banked-phi-case-3-near-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #3.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+
+#pragma link("call-banked-phi.ld")
+
+char* const SCREEN = (char*)0x0400;
+
+#pragma code_seg(Code)
+void main(void) {
+ SCREEN[0] = plus('0', 7); // close call
+ SCREEN[1] = plus('1', 6); // close call
+}
+
+#pragma code_seg(RAM_Bank1)
+__bank(cx16_ram,1) char plus(char a, char b) {
+ return add(a, b); // near call
+}
+
+#pragma code_seg(Code)
+#pragma nobank
+char add(char a, char b) {
+ return a+b;
+}
+
+
diff --git a/src/test/kc/call-banked-phi-case-3-near-1.c b/src/test/kc/call-banked-phi-case-3-near-1.c
new file mode 100644
index 000000000..25bce37f7
--- /dev/null
+++ b/src/test/kc/call-banked-phi-case-3-near-1.c
@@ -0,0 +1,51 @@
+/**
+ * @file call-banked-phi-case-3-near-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #3.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+
+#pragma link("call-banked-phi.ld")
+
+char* const SCREEN = (char*)0x0400;
+
+#pragma code_seg(Code)
+void main(void) {
+ SCREEN[0] = plus('0', 7); // close call
+ SCREEN[1] = plus('1', 6); // close call
+}
+
+#pragma code_seg(RAM_Bank1)
+#pragma bank(cx16_ram, 1)
+char plus(char a, char b) {
+ return add(a, b); // near call
+}
+
+#pragma code_seg(Code)
+#pragma nobank
+char add(char a, char b) {
+ return a+b;
+}
+
+
diff --git a/src/test/kc/call-banked-phi-case-4-near-0.c b/src/test/kc/call-banked-phi-case-4-near-0.c
new file mode 100644
index 000000000..5c7ffc1f0
--- /dev/null
+++ b/src/test/kc/call-banked-phi-case-4-near-0.c
@@ -0,0 +1,47 @@
+/**
+ * @file call-banked-phi-case-4-near-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #4.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+
+#pragma link("call-banked-phi.ld")
+
+char* const SCREEN = (char*)0x0400;
+
+#pragma code_seg(Code)
+void main(void) {
+ SCREEN[0] = plus('0', 7); // close call
+ SCREEN[1] = plus('1', 6); // close call
+}
+
+#pragma code_seg(RAM_Bank1)
+char __bank(cx16_ram, 1) plus(char a, char b) {
+ return add(a, b); // near call
+}
+
+#pragma code_seg(RAM_Bank1)
+char __bank(cx16_ram, 1) add(char a, char b) {
+ return a+b;
+}
diff --git a/src/test/kc/call-banked-phi-case-4-near-1.c b/src/test/kc/call-banked-phi-case-4-near-1.c
new file mode 100644
index 000000000..319ac2483
--- /dev/null
+++ b/src/test/kc/call-banked-phi-case-4-near-1.c
@@ -0,0 +1,49 @@
+/**
+ * @file call-banked-phi-case-4-near-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #4.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+
+#pragma link("call-banked-phi.ld")
+
+char* const SCREEN = (char*)0x0400;
+
+#pragma code_seg(Code)
+void main(void) {
+ SCREEN[0] = plus('0', 7); // close call
+ SCREEN[1] = plus('1', 6); // close call
+}
+
+#pragma code_seg(RAM_Bank1)
+#pragma bank(cx16_ram, 1)
+char plus(char a, char b) {
+ return add(a, b); // near call
+}
+
+#pragma code_seg(RAM_Bank1)
+#pragma bank(cx16_ram, 1)
+char add(char a, char b) {
+ return a+b;
+}
diff --git a/src/test/kc/call-banked-phi-case-5-far-0.c b/src/test/kc/call-banked-phi-case-5-far-0.c
new file mode 100644
index 000000000..dceba3f63
--- /dev/null
+++ b/src/test/kc/call-banked-phi-case-5-far-0.c
@@ -0,0 +1,47 @@
+/**
+ * @file call-banked-phi-case-5-far-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #5.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+
+#pragma link("call-banked-phi.ld")
+
+char* const SCREEN = (char*)0x0400;
+
+#pragma code_seg(Code)
+void main(void) {
+ SCREEN[0] = plus('0', 7); // close call
+ SCREEN[1] = plus('1', 6); // close call
+}
+
+#pragma code_seg(RAM_Bank1)
+__bank(cx16_ram, 1) char plus(char a, char b) {
+ return add(a, b); // far call
+}
+
+#pragma code_seg(RAM_Bank2)
+__bank(cx16_ram, 2) char add(char a, char b) {
+ return a+b;
+}
diff --git a/src/test/kc/call-banked-phi-case-5-far-1.c b/src/test/kc/call-banked-phi-case-5-far-1.c
new file mode 100644
index 000000000..754dd1402
--- /dev/null
+++ b/src/test/kc/call-banked-phi-case-5-far-1.c
@@ -0,0 +1,49 @@
+/**
+ * @file call-banked-phi-case-5-far-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #5.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+
+#pragma link("call-banked-phi.ld")
+
+char* const SCREEN = (char*)0x0400;
+
+#pragma code_seg(Code)
+void main(void) {
+ SCREEN[0] = plus('0', 7); // close call
+ SCREEN[1] = plus('1', 6); // close call
+}
+
+#pragma code_seg(RAM_Bank1)
+#pragma bank(cx16_ram, 1)
+char plus(char a, char b) {
+ return add(a, b); // far call
+}
+
+#pragma code_seg(RAM_Bank2)
+#pragma bank(cx16_ram, 2)
+char add(char a, char b) {
+ return a+b;
+}
diff --git a/src/test/kc/call-banked-phi-case-6-close-0.c b/src/test/kc/call-banked-phi-case-6-close-0.c
new file mode 100644
index 000000000..8fada9193
--- /dev/null
+++ b/src/test/kc/call-banked-phi-case-6-close-0.c
@@ -0,0 +1,47 @@
+/**
+ * @file call-banked-phi-case-6-close-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #6.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+
+#pragma link("call-banked-phi.ld")
+
+char* const SCREEN = (char*)0x0400;
+
+#pragma code_seg(Code)
+void main(void) {
+ SCREEN[0] = plus('0', 7); // close call
+ SCREEN[1] = plus('1', 6); // close call
+}
+
+#pragma code_seg(RAM_Bank1)
+__bank(cx16_ram, 1) char plus(char a, char b) {
+ return add(a, b); // close call
+}
+
+#pragma code_seg(ROM_Bank1)
+__bank(cx16_rom, 1) char add(char a, char b) {
+ return a+b;
+}
diff --git a/src/test/kc/call-banked-phi-case-6-close-1.c b/src/test/kc/call-banked-phi-case-6-close-1.c
new file mode 100644
index 000000000..5f8141443
--- /dev/null
+++ b/src/test/kc/call-banked-phi-case-6-close-1.c
@@ -0,0 +1,49 @@
+/**
+ * @file call-banked-phi-case-6-close-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #6.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+
+#pragma link("call-banked-phi.ld")
+
+char* const SCREEN = (char*)0x0400;
+
+#pragma code_seg(Code)
+void main(void) {
+ SCREEN[0] = plus('0', 7); // close call
+ SCREEN[1] = plus('1', 6); // close call
+}
+
+#pragma code_seg(RAM_Bank1)
+#pragma bank(cx16_ram, 1)
+char plus(char a, char b) {
+ return add(a, b); // close call
+}
+
+#pragma code_seg(ROM_Bank1)
+#pragma bank(cx16_rom, 1)
+char add(char a, char b) {
+ return a+b;
+}
diff --git a/src/test/kc/call-banked-phi-memvars.c b/src/test/kc/call-banked-phi-memvars.c
new file mode 100644
index 000000000..420b2796f
--- /dev/null
+++ b/src/test/kc/call-banked-phi-memvars.c
@@ -0,0 +1,30 @@
+/**
+ * Test banked calls with memory variables.
+ * The parameters & return should end up in the shared/common bank.
+ */
+
+#pragma link("call-banked-phi.ld")
+#pragma var_model(mem)
+
+int* const SCREEN = (int*)0x0400;
+
+#pragma code_seg(Code)
+#pragma nobank
+void main(void) {
+ for(char i=0;i<5; i++) {
+ SCREEN[i] = plus(100, (int)i);
+ SCREEN[10+i] = plus(200, (int)i);
+ }
+}
+
+#pragma code_seg(RAM_Bank1)
+#pragma data_seg(RAM_Bank1)
+#pragma bank(cx16_ram, 1)
+int plus(int a, int b) {
+ int r = 2;
+ r += a;
+ r += b;
+ r += a;
+ r += b;
+ return r;
+}
diff --git a/src/test/kc/call-banked-phi.ld b/src/test/kc/call-banked-phi.ld
new file mode 100644
index 000000000..de9bdab5d
--- /dev/null
+++ b/src/test/kc/call-banked-phi.ld
@@ -0,0 +1,13 @@
+.file [name="%O", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=%P]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(%E)
+.segment Code
+.segment Data
+
diff --git a/src/test/kc/call-banked-stack-case-2-close-0.c b/src/test/kc/call-banked-stack-case-2-close-0.c
new file mode 100644
index 000000000..91f688bee
--- /dev/null
+++ b/src/test/kc/call-banked-stack-case-2-close-0.c
@@ -0,0 +1,42 @@
+/**
+ * @file call-banked-stack-case-2-close-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #2
+ * The compiler will throw an error because the far call is a __stackcall
+ * and this is not (yet) implemented and supported.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+
+#pragma link("call-banked-stack.ld")
+
+char* const SCREEN = (char*)0x0400;
+
+#pragma code_seg(Code)
+void main(void) {
+ SCREEN[0] = plus('0', 7); // close stack call
+}
+
+#pragma code_seg(Bank1)
+__stackcall __bank(cx16_ram, 1) char plus(char a, char b) {
+ return a+b;
+}
diff --git a/src/test/kc/call-banked-stack-case-5-far-0.c b/src/test/kc/call-banked-stack-case-5-far-0.c
new file mode 100644
index 000000000..a5b7567f3
--- /dev/null
+++ b/src/test/kc/call-banked-stack-case-5-far-0.c
@@ -0,0 +1,47 @@
+/**
+ * @file call-banked-stack-case-5-far-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #5.
+ * The compiler will throw an error because the far call is a __stackcall
+ * and this is not (yet) implemented and supported.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+
+#pragma link("call-banked-phi.ld")
+
+char* const SCREEN = (char*)0x0400;
+
+#pragma code_seg(Code)
+void main(void) {
+ SCREEN[0] = plus('0', 7); // close call
+}
+
+#pragma code_seg(RAM_Bank1)
+__bank(cx16_ram, 1) char plus(char a, char b) {
+ return min(a, b); // far call
+}
+
+#pragma code_seg(RAM_Bank2)
+__stackcall __bank(cx16_ram, 2) char min(char a, char b) {
+ return a+b;
+}
diff --git a/src/test/kc/call-banked-stack.ld b/src/test/kc/call-banked-stack.ld
new file mode 100644
index 000000000..de9bdab5d
--- /dev/null
+++ b/src/test/kc/call-banked-stack.ld
@@ -0,0 +1,13 @@
+.file [name="%O", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=%P]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(%E)
+.segment Code
+.segment Data
+
diff --git a/src/test/kc/examples/cx16/banking/cx16-banking.c b/src/test/kc/examples/cx16/banking/cx16-banking.c
new file mode 100644
index 000000000..8648d45d1
--- /dev/null
+++ b/src/test/kc/examples/cx16/banking/cx16-banking.c
@@ -0,0 +1,164 @@
+/**
+ * @file cx16-banking-1.c
+ * @author your name (you@domain.com)
+ * @brief This program demonstrates a simple example of a banked call.
+ * @version 0.1
+ * @date 2023-04-05
+ *
+ * @copyright Copyright (c) 2023
+ *
+ */
+
+// The linker specification of the different segments.
+#pragma target(cx16)
+#pragma link("cx16-banking.ld")
+#pragma var_model(mem)
+
+#include
+#include
+#include
+
+// The target computer platform is the Commander X16,
+// which implements banking in ram between 0xA0000 and 0xBFFF,
+// and in ram between 0xC000 and 0xFFFF.
+#pragma target(cx16)
+
+// Functional code
+
+#pragma code_seg(Bank1) // The sequent functions will be addressed specified by segment bank1 in the linker.
+#pragma bank(cx16_ram, 1) // The sequent functions will be banked using call method ram in bank number 1.
+
+char add_a(char a) {
+ printf("add_a(%02x), ", bank_get_bram());
+ return a+1;
+}
+
+char add_c(char a) {
+ printf("add_c(%02x), ", bank_get_bram());
+ return add_a(a)+1; // Non banked call in ram bank 1.
+}
+
+char add_d(char a) {
+ printf("add_d(%02x), ",bank_get_bram());
+ return mul_a(a)+1; // Banked call fram ram bank 1 to ram bank 2.
+}
+
+char add_e(char a) {
+ printf("add_e(%02x), ",bank_get_bram());
+ return mul_b(a)+1; // Banked call fram ram bank 1 to ram bank 2.
+}
+
+char add_f(char a) {
+ printf("add_f(%02x), ",bank_get_bram());
+ return add_m(a)+1; // Non banked call fram ram bank 1 to main memory.
+}
+
+
+#pragma code_seg(Bank2) // The sequent functions will be addressed specified by segment bank2 in the linker.
+#pragma bank(cx16_ram, 2) // The sequent functions will be banked using call method ram in bank number 2.
+
+char mul_a(char m) {
+ printf("mul_a(%02x), ",bank_get_bram());
+ return m * 2;
+}
+
+char mul_c(char m) {
+ printf("mul_c(%02x), ",bank_get_bram());
+ return add_a(m)*2; // Banked call fram ram bank 2 to ram bank 1.
+}
+
+char mul_d(char m) {
+ printf("mul_d(%02x), ",bank_get_bram());
+ return mul_a(m)*2; // Non banked call in ram bank 2.
+}
+
+char mul_e(char a) {
+ printf("mul_e(%02x), ",bank_get_bram());
+ return mul_b(a)*2; // Non Banked call in ram bank 2.
+}
+
+char mul_f(char m) {
+ printf("mul_f(%02x), ",bank_get_bram());
+ return add_m(m)*2; // Non banked call fram ram bank 2 to main memory.
+}
+
+
+#pragma nobank // The sequent functions will consider no banking calculations anymore.
+
+#pragma code_seg(Bank1) // The sequent functions will be addressed specified by segment bank1 in the linker.
+// The __bank directive declares this function to be banked using call method ram in bank number 1 of banked ram.
+char __bank(cx16_ram, 1) add_b(char a) {
+ printf("add_b(%02x), ",bank_get_bram());
+ return a+1;
+}
+
+#pragma code_seg(Bank2) // The sequent functions will be addressed specified by segment bank1 in the linker.
+// The __bank directive declares this function to be banked using call method ram in bank number 2 of banked ram.
+char __bank(cx16_ram, 2) mul_b(char m) {
+ printf("mul_b(%02x), ",bank_get_bram());
+ return m*2;
+}
+
+#pragma code_seg(Code) // The sequent functions will be addressed in the default main memory location (segment Code).
+
+// Allocated in main memory.
+char add_m(char a) {
+ printf("add_m(%02x), ",bank_get_bram());
+ return add_e(a)+1; // Banked call to ram in bank 1 fram main memory.
+}
+
+// Allocated in main memory.
+char mul_m(char m) {
+ printf("mul_m(%02x), ",bank_get_bram());
+ return mul_e(m)*2; // Banked call to ram in bank 2 fram main memory.
+}
+
+
+// Practically this means that the main() function is placed in main memory ...
+
+void load_bank(char bank, char *file) {
+ bank_set_bram(bank);
+ cbm_k_setnam(file);
+ cbm_k_setlfs(1,8,2);
+ cbm_k_load((char*)0xA000, 0);
+ cbm_k_close(1);
+}
+
+void main(void) {
+
+ clrscr();
+
+ load_bank(1, "BANK1.BIN");
+ load_bank(2, "BANK2.BIN");
+
+ bank_set_bram(0);
+
+ asm{.byte $db}
+ printf("result = %u\n", add_a(1)); // Banked call to ram in bank 1 fram main memory.
+ asm{.byte $db}
+ printf("result = %u\n", add_b(1)); // Banked call to ram in bank 1 fram main memory.
+ asm{.byte $db}
+ printf("result = %u\n", add_c(1)); // Banked call to ram in bank 1 fram main memory.
+ asm{.byte $db}
+ printf("result = %u\n", add_d(1)); // Banked call to ram in bank 1 fram main memory.
+ asm{.byte $db}
+ printf("result = %u\n", add_e(1)); // Banked call to ram in bank 1 fram main memory.
+ asm{.byte $db}
+ printf("result = %u\n", add_f(1)); // Banked call to ram in bank 1 fram main memory.
+ asm{.byte $db}
+ printf("result = %u\n", mul_a(1)); // Banked call to ram in bank 2 fram main memory.
+ asm{.byte $db}
+ printf("result = %u\n", mul_b(1)); // Banked call to ram in bank 2 fram main memory.
+ asm{.byte $db}
+ printf("result = %u\n", mul_c(1)); // Banked call to ram in bank 2 fram main memory.
+ asm{.byte $db}
+ printf("result = %u\n", mul_d(1)); // Banked call to ram in bank 2 fram main memory.
+ asm{.byte $db}
+ printf("result = %u\n", mul_e(1)); // banked call to ram in bank 2 fram main memory.
+ asm{.byte $db}
+ printf("result = %u\n", mul_f(1)); // banked call to ram in bank 2 fram main memory.
+ asm{.byte $db}
+ printf("result = %u\n", add_m(1)); // Near call in main memory fram main memory.
+ asm{.byte $db}
+ printf("result = %u\n", mul_m(1)); // Near call in main memory fram main memory.
+}
diff --git a/src/test/kc/examples/cx16/banking/cx16-banking.ld b/src/test/kc/examples/cx16/banking/cx16-banking.ld
new file mode 100644
index 000000000..e21277416
--- /dev/null
+++ b/src/test/kc/examples/cx16/banking/cx16-banking.ld
@@ -0,0 +1,14 @@
+.file [name="%O", type="prg", segments="Program"]
+.file [name="BANK1.BIN", type="bin", segments="Bank1"]
+.file [name="BANK2.BIN", type="bin", segments="Bank2"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=%P]
+.segmentdef Data [startAfter="Code"]
+.segmentdef Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segment Basic
+:BasicUpstart(%E)
+.segment Code
+.segment Data
+
diff --git a/src/test/ref/call-banked-phi-case-1-near-0.asm b/src/test/ref/call-banked-phi-case-1-near-0.asm
new file mode 100644
index 000000000..9c88aba31
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-1-near-0.asm
@@ -0,0 +1,70 @@
+/**
+ * @file call-banked-phi-case-1-near-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be)
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #1.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ .file [name="call-banked-phi-case-1-near-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ .label SCREEN = $400
+.segment Code
+main: {
+ // plus('0', 7)
+ lda #7
+ ldx #'0'
+ jsr plus
+ // plus('0', 7)
+ // SCREEN[0] = plus('0', 7)
+ sta SCREEN
+ // plus('1', 6)
+ lda #6
+ ldx #'1'
+ jsr plus
+ // plus('1', 6)
+ // SCREEN[1] = plus('1', 6)
+ // near call
+ sta SCREEN+1
+ // }
+ rts
+}
+// __register(A) char plus(__register(X) char a, __register(A) char b)
+plus: {
+ // a+b
+ stx.z $ff
+ clc
+ adc.z $ff
+ // }
+ rts
+}
diff --git a/src/test/ref/call-banked-phi-case-1-near-0.cfg b/src/test/ref/call-banked-phi-case-1-near-0.cfg
new file mode 100644
index 000000000..936abd391
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-1-near-0.cfg
@@ -0,0 +1,30 @@
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] plus::return#2 = plus::a#2 + plus::b#2
+ to:plus::@return
+plus::@return: scope:[plus] from plus
+ [12] return
+ to:@return
diff --git a/src/test/ref/call-banked-phi-case-1-near-0.log b/src/test/ref/call-banked-phi-case-1-near-0.log
new file mode 100644
index 000000000..2ffa84883
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-1-near-0.log
@@ -0,0 +1,462 @@
+Loading link script "call-banked-phi.ld"
+
+CONTROL FLOW GRAPH SSA
+
+void main()
+main: scope:[main] from __start
+ plus::a#0 = '0'
+ plus::b#0 = 7
+ call plus
+ plus::return#0 = plus::return#3
+ to:main::@1
+main::@1: scope:[main] from main
+ plus::return#4 = phi( main/plus::return#0 )
+ main::$0 = plus::return#4
+ SCREEN[0] = main::$0
+ plus::a#1 = '1'
+ plus::b#1 = 6
+ call plus
+ plus::return#1 = plus::return#3
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ plus::return#5 = phi( main::@1/plus::return#1 )
+ main::$1 = plus::return#5
+ SCREEN[1] = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ return
+ to:@return
+
+char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
+ plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
+ plus::$0 = plus::a#2 + plus::b#2
+ plus::return#2 = plus::$0
+ to:plus::@return
+plus::@return: scope:[plus] from plus
+ plus::return#6 = phi( plus/plus::return#2 )
+ plus::return#3 = plus::return#6
+ return
+ to:@return
+
+void __start()
+__start: scope:[__start] from
+ call main
+ to:__start::@1
+__start::@1: scope:[__start] from __start
+ to:__start::@return
+__start::@return: scope:[__start] from __start::@1
+ return
+ to:@return
+
+SYMBOL TABLE SSA
+__constant char * const SCREEN = (char *)$400
+void __start()
+void main()
+char main::$0
+char main::$1
+char plus(char a , char b)
+char plus::$0
+char plus::a
+char plus::a#0
+char plus::a#1
+char plus::a#2
+char plus::b
+char plus::b#0
+char plus::b#1
+char plus::b#2
+char plus::return
+char plus::return#0
+char plus::return#1
+char plus::return#2
+char plus::return#3
+char plus::return#4
+char plus::return#5
+char plus::return#6
+
+Adding number conversion cast (unumber) 7 in plus::b#0 = 7
+Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
+Adding number conversion cast (unumber) 6 in plus::b#1 = 6
+Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast plus::b#0 = (unumber)7
+Inlining cast plus::b#1 = (unumber)6
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (char *) 1024
+Simplifying constant integer cast 7
+Simplifying constant integer cast 0
+Simplifying constant integer cast 6
+Simplifying constant integer cast 1
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (char) 7
+Finalized unsigned number type (char) 0
+Finalized unsigned number type (char) 6
+Finalized unsigned number type (char) 1
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Alias plus::return#0 = plus::return#4
+Alias plus::return#1 = plus::return#5
+Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
+Successful SSA optimization Pass2AliasElimination
+Constant plus::a#0 = '0'
+Constant plus::b#0 = 7
+Constant plus::a#1 = '1'
+Constant plus::b#1 = 6
+Successful SSA optimization Pass2ConstantIdentification
+Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
+Successful SSA optimization PassNSimplifyExpressionWithZero
+Removing unused procedure __start
+Removing unused procedure block __start
+Removing unused procedure block __start::@1
+Removing unused procedure block __start::@return
+Successful SSA optimization PassNEliminateEmptyStart
+Inlining constant with var siblings plus::a#0
+Inlining constant with var siblings plus::b#0
+Inlining constant with var siblings plus::a#1
+Inlining constant with var siblings plus::b#1
+Constant inlined plus::b#1 = 6
+Constant inlined plus::b#0 = 7
+Constant inlined plus::a#1 = '1'
+Constant inlined plus::a#0 = '0'
+Successful SSA optimization Pass2ConstantInlining
+Consolidated array index constant in *(SCREEN+1)
+Successful SSA optimization Pass2ConstantAdditionElimination
+Adding NOP phi() at start of main
+CALL GRAPH
+Calls in [main] to plus:1 plus:5
+
+Created 2 initial phi equivalence classes
+Coalesced down to 2 phi equivalence classes
+Adding NOP phi() at start of main
+
+FINAL CONTROL FLOW GRAPH
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] plus::return#2 = plus::a#2 + plus::b#2
+ to:plus::@return
+plus::@return: scope:[plus] from plus
+ [12] return
+ to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+void main()
+char main::$0 // 4.0
+char main::$1 // 4.0
+char plus(char a , char b)
+char plus::a
+char plus::a#2 // 11.0
+char plus::b
+char plus::b#2 // 11.0
+char plus::return
+char plus::return#0 // 4.0
+char plus::return#1 // 4.0
+char plus::return#2 // 3.75
+
+Initial phi equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
+Added variable main::$0 to live range equivalence class [ main::$0 ]
+Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
+Added variable main::$1 to live range equivalence class [ main::$1 ]
+Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
+Complete equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+[ plus::return#0 ]
+[ main::$0 ]
+[ plus::return#1 ]
+[ main::$1 ]
+[ plus::return#2 ]
+Allocated zp[1]:2 [ plus::a#2 ]
+Allocated zp[1]:3 [ plus::b#2 ]
+Allocated zp[1]:4 [ plus::return#0 ]
+Allocated zp[1]:5 [ main::$0 ]
+Allocated zp[1]:6 [ plus::return#1 ]
+Allocated zp[1]:7 [ main::$1 ]
+Allocated zp[1]:8 [ plus::return#2 ]
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [11] plus::return#2 = plus::a#2 + plus::b#2 [ plus::return#2 ] ( plus:1 [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } plus:5 [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Potential registers zp[1]:2 [ plus::a#2 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:3 [ plus::b#2 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:4 [ plus::return#0 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:5 [ main::$0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:6 [ plus::return#1 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:7 [ main::$1 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:8 [ plus::return#2 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [plus] 11: zp[1]:2 [ plus::a#2 ] 11: zp[1]:3 [ plus::b#2 ] 4: zp[1]:4 [ plus::return#0 ] 4: zp[1]:6 [ plus::return#1 ] 3.75: zp[1]:8 [ plus::return#2 ]
+Uplift Scope [main] 4: zp[1]:5 [ main::$0 ] 4: zp[1]:7 [ main::$1 ]
+Uplift Scope []
+
+Uplifting [plus] best 81 combination reg byte x [ plus::a#2 ] reg byte a [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:8 [ plus::return#2 ]
+Limited combination testing to 100 combinations of 1024 possible.
+Uplifting [main] best 69 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
+Uplifting [] best 69 combination
+Attempting to uplift remaining variables inzp[1]:8 [ plus::return#2 ]
+Uplifting [plus] best 60 combination reg byte a [ plus::return#2 ]
+
+ASSEMBLER BEFORE OPTIMIZATION
+ // File Comments
+/**
+ * @file call-banked-phi-case-1-near-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be)
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #1.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-1-near-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ plus_from_main:
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuaa=vbuc1
+ lda #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- vbuxx=vbuc1
+ ldx #'0'
+ jsr plus
+ // [2] plus::return#0 = plus::return#2
+ jmp __b1
+ // main::@1
+ __b1:
+ // [3] main::$0 = plus::return#0
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ plus_from___b1:
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuaa=vbuc1
+ lda #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- vbuxx=vbuc1
+ ldx #'1'
+ jsr plus
+ // [6] plus::return#1 = plus::return#2
+ jmp __b2
+ // main::@2
+ __b2:
+ // [7] main::$1 = plus::return#1
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // near call
+ sta SCREEN+1
+ jmp __breturn
+ // main::@return
+ __breturn:
+ // [9] return
+ rts
+}
+ // plus
+// __register(A) char plus(__register(X) char a, __register(A) char b)
+plus: {
+ // [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuaa=vbuxx_plus_vbuaa
+ stx.z $ff
+ clc
+ adc.z $ff
+ jmp __breturn
+ // plus::@return
+ __breturn:
+ // [12] return
+ rts
+}
+ // File Data
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp __b1
+Removing instruction jmp __b2
+Removing instruction jmp __breturn
+Removing instruction jmp __breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction plus_from_main:
+Removing instruction __b1:
+Removing instruction plus_from___b1:
+Removing instruction __b2:
+Removing instruction __breturn:
+Removing instruction __breturn:
+Succesful ASM optimization Pass5UnusedLabelElimination
+
+FINAL SYMBOL TABLE
+__constant char * const SCREEN = (char *) 1024
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte x 11.0
+char plus::b
+char plus::b#2 // reg byte a 11.0
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte a 3.75
+
+reg byte x [ plus::a#2 ]
+reg byte a [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+reg byte a [ plus::return#2 ]
+
+
+FINAL ASSEMBLER
+Score: 48
+
+ // File Comments
+/**
+ * @file call-banked-phi-case-1-near-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be)
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #1.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-1-near-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // plus('0', 7)
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuaa=vbuc1
+ lda #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- vbuxx=vbuc1
+ ldx #'0'
+ jsr plus
+ // plus('0', 7)
+ // [2] plus::return#0 = plus::return#2
+ // main::@1
+ // [3] main::$0 = plus::return#0
+ // SCREEN[0] = plus('0', 7)
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // plus('1', 6)
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuaa=vbuc1
+ lda #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- vbuxx=vbuc1
+ ldx #'1'
+ jsr plus
+ // plus('1', 6)
+ // [6] plus::return#1 = plus::return#2
+ // main::@2
+ // [7] main::$1 = plus::return#1
+ // SCREEN[1] = plus('1', 6)
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // near call
+ sta SCREEN+1
+ // main::@return
+ // }
+ // [9] return
+ rts
+}
+ // plus
+// __register(A) char plus(__register(X) char a, __register(A) char b)
+plus: {
+ // a+b
+ // [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuaa=vbuxx_plus_vbuaa
+ stx.z $ff
+ clc
+ adc.z $ff
+ // plus::@return
+ // }
+ // [12] return
+ rts
+}
+ // File Data
+
diff --git a/src/test/ref/call-banked-phi-case-1-near-0.sym b/src/test/ref/call-banked-phi-case-1-near-0.sym
new file mode 100644
index 000000000..aad9eb173
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-1-near-0.sym
@@ -0,0 +1,21 @@
+__constant char * const SCREEN = (char *) 1024
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte x 11.0
+char plus::b
+char plus::b#2 // reg byte a 11.0
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte a 3.75
+
+reg byte x [ plus::a#2 ]
+reg byte a [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+reg byte a [ plus::return#2 ]
diff --git a/src/test/ref/call-banked-phi-case-1-near-1.asm b/src/test/ref/call-banked-phi-case-1-near-1.asm
new file mode 100644
index 000000000..c5395e6ac
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-1-near-1.asm
@@ -0,0 +1,78 @@
+/**
+ * @file call-banked-phi-case-1-near-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #1
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ .file [name="call-banked-phi-case-1-near-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ .label SCREEN = $400
+.segment Code
+main: {
+ // plus('0', 7)
+ lda #7
+ ldx #'0'
+ jsr plus
+ // plus('0', 7)
+ // SCREEN[0] = plus('0', 7)
+ sta SCREEN
+ // plus('1', 6)
+ lda #6
+ ldx #'1'
+ jsr plus
+ // plus('1', 6)
+ // SCREEN[1] = plus('1', 6)
+ // near call
+ sta SCREEN+1
+ // }
+ rts
+}
+// __register(A) char plus(__register(X) char a, __register(A) char b)
+plus: {
+ // add(a, b)
+ stx.z add.a
+ jsr add
+ // }
+ rts
+}
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+add: {
+ .label a = 2
+ // a+b
+ clc
+ adc.z a
+ // }
+ rts
+}
diff --git a/src/test/ref/call-banked-phi-case-1-near-1.cfg b/src/test/ref/call-banked-phi-case-1-near-1.cfg
new file mode 100644
index 000000000..bd8833228
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-1-near-1.cfg
@@ -0,0 +1,44 @@
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
diff --git a/src/test/ref/call-banked-phi-case-1-near-1.log b/src/test/ref/call-banked-phi-case-1-near-1.log
new file mode 100644
index 000000000..916bd8ab3
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-1-near-1.log
@@ -0,0 +1,599 @@
+Loading link script "call-banked-phi.ld"
+
+CONTROL FLOW GRAPH SSA
+
+void main()
+main: scope:[main] from __start
+ plus::a#0 = '0'
+ plus::b#0 = 7
+ call plus
+ plus::return#0 = plus::return#3
+ to:main::@1
+main::@1: scope:[main] from main
+ plus::return#4 = phi( main/plus::return#0 )
+ main::$0 = plus::return#4
+ SCREEN[0] = main::$0
+ plus::a#1 = '1'
+ plus::b#1 = 6
+ call plus
+ plus::return#1 = plus::return#3
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ plus::return#5 = phi( main::@1/plus::return#1 )
+ main::$1 = plus::return#5
+ SCREEN[1] = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ return
+ to:@return
+
+char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
+ plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
+ add::a#0 = plus::a#2
+ add::b#0 = plus::b#2
+ call add
+ add::return#0 = add::return#2
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ add::return#3 = phi( plus/add::return#0 )
+ plus::$0 = add::return#3
+ plus::return#2 = plus::$0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ plus::return#6 = phi( plus::@1/plus::return#2 )
+ plus::return#3 = plus::return#6
+ return
+ to:@return
+
+char add(char a , char b)
+add: scope:[add] from plus
+ add::b#1 = phi( plus/add::b#0 )
+ add::a#1 = phi( plus/add::a#0 )
+ add::$0 = add::a#1 + add::b#1
+ add::return#1 = add::$0
+ to:add::@return
+add::@return: scope:[add] from add
+ add::return#4 = phi( add/add::return#1 )
+ add::return#2 = add::return#4
+ return
+ to:@return
+
+void __start()
+__start: scope:[__start] from
+ call main
+ to:__start::@1
+__start::@1: scope:[__start] from __start
+ to:__start::@return
+__start::@return: scope:[__start] from __start::@1
+ return
+ to:@return
+
+SYMBOL TABLE SSA
+__constant char * const SCREEN = (char *)$400
+void __start()
+char add(char a , char b)
+char add::$0
+char add::a
+char add::a#0
+char add::a#1
+char add::b
+char add::b#0
+char add::b#1
+char add::return
+char add::return#0
+char add::return#1
+char add::return#2
+char add::return#3
+char add::return#4
+void main()
+char main::$0
+char main::$1
+char plus(char a , char b)
+char plus::$0
+char plus::a
+char plus::a#0
+char plus::a#1
+char plus::a#2
+char plus::b
+char plus::b#0
+char plus::b#1
+char plus::b#2
+char plus::return
+char plus::return#0
+char plus::return#1
+char plus::return#2
+char plus::return#3
+char plus::return#4
+char plus::return#5
+char plus::return#6
+
+Adding number conversion cast (unumber) 7 in plus::b#0 = 7
+Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
+Adding number conversion cast (unumber) 6 in plus::b#1 = 6
+Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast plus::b#0 = (unumber)7
+Inlining cast plus::b#1 = (unumber)6
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (char *) 1024
+Simplifying constant integer cast 7
+Simplifying constant integer cast 0
+Simplifying constant integer cast 6
+Simplifying constant integer cast 1
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (char) 7
+Finalized unsigned number type (char) 0
+Finalized unsigned number type (char) 6
+Finalized unsigned number type (char) 1
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Alias plus::return#0 = plus::return#4
+Alias plus::return#1 = plus::return#5
+Alias add::return#0 = add::return#3
+Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
+Alias add::return#1 = add::$0 add::return#4 add::return#2
+Successful SSA optimization Pass2AliasElimination
+Identical Phi Values add::a#1 add::a#0
+Identical Phi Values add::b#1 add::b#0
+Successful SSA optimization Pass2IdenticalPhiElimination
+Constant plus::a#0 = '0'
+Constant plus::b#0 = 7
+Constant plus::a#1 = '1'
+Constant plus::b#1 = 6
+Successful SSA optimization Pass2ConstantIdentification
+Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
+Successful SSA optimization PassNSimplifyExpressionWithZero
+Removing unused procedure __start
+Removing unused procedure block __start
+Removing unused procedure block __start::@1
+Removing unused procedure block __start::@return
+Successful SSA optimization PassNEliminateEmptyStart
+Inlining constant with var siblings plus::a#0
+Inlining constant with var siblings plus::b#0
+Inlining constant with var siblings plus::a#1
+Inlining constant with var siblings plus::b#1
+Constant inlined plus::b#1 = 6
+Constant inlined plus::b#0 = 7
+Constant inlined plus::a#1 = '1'
+Constant inlined plus::a#0 = '0'
+Successful SSA optimization Pass2ConstantInlining
+Consolidated array index constant in *(SCREEN+1)
+Successful SSA optimization Pass2ConstantAdditionElimination
+Adding NOP phi() at start of main
+CALL GRAPH
+Calls in [main] to plus:1 plus:5
+Calls in [plus] to add:13
+
+Created 2 initial phi equivalence classes
+Coalesced down to 2 phi equivalence classes
+Adding NOP phi() at start of main
+
+FINAL CONTROL FLOW GRAPH
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+char add(char a , char b)
+char add::a
+char add::a#0 // 56.0
+char add::b
+char add::b#0 // 112.0
+char add::return
+char add::return#0 // 22.0
+char add::return#1 // 37.33333333333333
+void main()
+char main::$0 // 4.0
+char main::$1 // 4.0
+char plus(char a , char b)
+char plus::a
+char plus::a#2 // 11.0
+char plus::b
+char plus::b#2 // 5.5
+char plus::return
+char plus::return#0 // 4.0
+char plus::return#1 // 4.0
+char plus::return#2 // 3.75
+
+Initial phi equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
+Added variable main::$0 to live range equivalence class [ main::$0 ]
+Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
+Added variable main::$1 to live range equivalence class [ main::$1 ]
+Added variable add::a#0 to live range equivalence class [ add::a#0 ]
+Added variable add::b#0 to live range equivalence class [ add::b#0 ]
+Added variable add::return#0 to live range equivalence class [ add::return#0 ]
+Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
+Added variable add::return#1 to live range equivalence class [ add::return#1 ]
+Complete equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+[ plus::return#0 ]
+[ main::$0 ]
+[ plus::return#1 ]
+[ main::$1 ]
+[ add::a#0 ]
+[ add::b#0 ]
+[ add::return#0 ]
+[ plus::return#2 ]
+[ add::return#1 ]
+Allocated zp[1]:2 [ add::b#0 ]
+Allocated zp[1]:3 [ add::a#0 ]
+Allocated zp[1]:4 [ add::return#1 ]
+Allocated zp[1]:5 [ add::return#0 ]
+Allocated zp[1]:6 [ plus::a#2 ]
+Allocated zp[1]:7 [ plus::b#2 ]
+Allocated zp[1]:8 [ plus::return#0 ]
+Allocated zp[1]:9 [ main::$0 ]
+Allocated zp[1]:10 [ plus::return#1 ]
+Allocated zp[1]:11 [ main::$1 ]
+Allocated zp[1]:12 [ plus::return#2 ]
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
+Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
+Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
+Uplift Scope []
+
+Uplifting [add] best 129 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ]
+Limited combination testing to 100 combinations of 256 possible.
+Uplifting [plus] best 99 combination reg byte x [ plus::a#2 ] reg byte a [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
+Limited combination testing to 100 combinations of 1024 possible.
+Uplifting [main] best 87 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
+Uplifting [] best 87 combination
+Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
+Uplifting [add] best 87 combination zp[1]:3 [ add::a#0 ]
+Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
+Uplifting [plus] best 78 combination reg byte a [ plus::return#2 ]
+Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
+
+ASSEMBLER BEFORE OPTIMIZATION
+ // File Comments
+/**
+ * @file call-banked-phi-case-1-near-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #1
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-1-near-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ plus_from_main:
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuaa=vbuc1
+ lda #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- vbuxx=vbuc1
+ ldx #'0'
+ jsr plus
+ // [2] plus::return#0 = plus::return#2
+ jmp __b1
+ // main::@1
+ __b1:
+ // [3] main::$0 = plus::return#0
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ plus_from___b1:
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuaa=vbuc1
+ lda #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- vbuxx=vbuc1
+ ldx #'1'
+ jsr plus
+ // [6] plus::return#1 = plus::return#2
+ jmp __b2
+ // main::@2
+ __b2:
+ // [7] main::$1 = plus::return#1
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // near call
+ sta SCREEN+1
+ jmp __breturn
+ // main::@return
+ __breturn:
+ // [9] return
+ rts
+}
+ // plus
+// __register(A) char plus(__register(X) char a, __register(A) char b)
+plus: {
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuxx
+ stx.z add.a
+ // [12] add::b#0 = plus::b#2
+ // [13] call add
+ jsr add
+ // [14] add::return#0 = add::return#1
+ jmp __b1
+ // plus::@1
+ __b1:
+ // [15] plus::return#2 = add::return#0
+ jmp __breturn
+ // plus::@return
+ __breturn:
+ // [16] return
+ rts
+}
+ // add
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+add: {
+ .label a = 2
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ jmp __breturn
+ // add::@return
+ __breturn:
+ // [18] return
+ rts
+}
+ // File Data
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp __b1
+Removing instruction jmp __b2
+Removing instruction jmp __breturn
+Removing instruction jmp __b1
+Removing instruction jmp __breturn
+Removing instruction jmp __breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction __breturn:
+Succesful ASM optimization Pass5RedundantLabelElimination
+Removing instruction plus_from_main:
+Removing instruction __b1:
+Removing instruction plus_from___b1:
+Removing instruction __b2:
+Removing instruction __breturn:
+Removing instruction __b1:
+Removing instruction __breturn:
+Succesful ASM optimization Pass5UnusedLabelElimination
+
+FINAL SYMBOL TABLE
+__constant char * const SCREEN = (char *) 1024
+char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte a 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte x 11.0
+char plus::b
+char plus::b#2 // reg byte a 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte a 3.75
+
+reg byte x [ plus::a#2 ]
+reg byte a [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte a [ plus::return#2 ]
+reg byte a [ add::return#1 ]
+
+
+FINAL ASSEMBLER
+Score: 60
+
+ // File Comments
+/**
+ * @file call-banked-phi-case-1-near-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #1
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-1-near-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // plus('0', 7)
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuaa=vbuc1
+ lda #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- vbuxx=vbuc1
+ ldx #'0'
+ jsr plus
+ // plus('0', 7)
+ // [2] plus::return#0 = plus::return#2
+ // main::@1
+ // [3] main::$0 = plus::return#0
+ // SCREEN[0] = plus('0', 7)
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // plus('1', 6)
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuaa=vbuc1
+ lda #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- vbuxx=vbuc1
+ ldx #'1'
+ jsr plus
+ // plus('1', 6)
+ // [6] plus::return#1 = plus::return#2
+ // main::@2
+ // [7] main::$1 = plus::return#1
+ // SCREEN[1] = plus('1', 6)
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // near call
+ sta SCREEN+1
+ // main::@return
+ // }
+ // [9] return
+ rts
+}
+ // plus
+// __register(A) char plus(__register(X) char a, __register(A) char b)
+plus: {
+ // add(a, b)
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuxx
+ stx.z add.a
+ // [12] add::b#0 = plus::b#2
+ // [13] call add
+ jsr add
+ // [14] add::return#0 = add::return#1
+ // plus::@1
+ // [15] plus::return#2 = add::return#0
+ // plus::@return
+ // }
+ // [16] return
+ rts
+}
+ // add
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+add: {
+ .label a = 2
+ // a+b
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ // add::@return
+ // }
+ // [18] return
+ rts
+}
+ // File Data
+
diff --git a/src/test/ref/call-banked-phi-case-1-near-1.sym b/src/test/ref/call-banked-phi-case-1-near-1.sym
new file mode 100644
index 000000000..bfa554ac0
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-1-near-1.sym
@@ -0,0 +1,33 @@
+__constant char * const SCREEN = (char *) 1024
+char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte a 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte x 11.0
+char plus::b
+char plus::b#2 // reg byte a 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte a 3.75
+
+reg byte x [ plus::a#2 ]
+reg byte a [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte a [ plus::return#2 ]
+reg byte a [ add::return#1 ]
diff --git a/src/test/ref/call-banked-phi-case-2-close-0.asm b/src/test/ref/call-banked-phi-case-2-close-0.asm
new file mode 100644
index 000000000..efdf5fdb6
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-2-close-0.asm
@@ -0,0 +1,96 @@
+/**
+ * @file call-banked-phi-case-2-close-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #2.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ .file [name="call-banked-phi-case-2-close-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ .label SCREEN = $400
+.segment Code
+main: {
+ // plus('0', 7)
+ ldx #7
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ txa
+ // SCREEN[0] = plus('0', 7)
+ sta SCREEN
+ // plus('1', 6)
+ ldx #6
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ txa
+ // SCREEN[1] = plus('1', 6)
+ // close call
+ sta SCREEN+1
+ // }
+ rts
+}
+.segment RAM_Bank1
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // a+b
+ stx.z $ff
+ clc
+ adc.z $ff
+ tax
+ // }
+ rts
+}
diff --git a/src/test/ref/call-banked-phi-case-2-close-0.cfg b/src/test/ref/call-banked-phi-case-2-close-0.cfg
new file mode 100644
index 000000000..b5f3a4af5
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-2-close-0.cfg
@@ -0,0 +1,30 @@
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] plus::return#2 = plus::a#2 + plus::b#2
+ to:plus::@return
+plus::@return: scope:[plus] from plus
+ [12] return
+ to:@return
diff --git a/src/test/ref/call-banked-phi-case-2-close-0.log b/src/test/ref/call-banked-phi-case-2-close-0.log
new file mode 100644
index 000000000..96e2fadd5
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-2-close-0.log
@@ -0,0 +1,520 @@
+Loading link script "call-banked-phi.ld"
+
+CONTROL FLOW GRAPH SSA
+
+void main()
+main: scope:[main] from __start
+ plus::a#0 = '0'
+ plus::b#0 = 7
+ call plus
+ plus::return#0 = plus::return#3
+ to:main::@1
+main::@1: scope:[main] from main
+ plus::return#4 = phi( main/plus::return#0 )
+ main::$0 = plus::return#4
+ SCREEN[0] = main::$0
+ plus::a#1 = '1'
+ plus::b#1 = 6
+ call plus
+ plus::return#1 = plus::return#3
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ plus::return#5 = phi( main::@1/plus::return#1 )
+ main::$1 = plus::return#5
+ SCREEN[1] = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
+ plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
+ plus::$0 = plus::a#2 + plus::b#2
+ plus::return#2 = plus::$0
+ to:plus::@return
+plus::@return: scope:[plus] from plus
+ plus::return#6 = phi( plus/plus::return#2 )
+ plus::return#3 = plus::return#6
+ return
+ to:@return
+
+void __start()
+__start: scope:[__start] from
+ call main
+ to:__start::@1
+__start::@1: scope:[__start] from __start
+ to:__start::@return
+__start::@return: scope:[__start] from __start::@1
+ return
+ to:@return
+
+SYMBOL TABLE SSA
+__constant char * const SCREEN = (char *)$400
+void __start()
+void main()
+char main::$0
+char main::$1
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::$0
+char plus::a
+char plus::a#0
+char plus::a#1
+char plus::a#2
+char plus::b
+char plus::b#0
+char plus::b#1
+char plus::b#2
+char plus::return
+char plus::return#0
+char plus::return#1
+char plus::return#2
+char plus::return#3
+char plus::return#4
+char plus::return#5
+char plus::return#6
+
+Adding number conversion cast (unumber) 7 in plus::b#0 = 7
+Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
+Adding number conversion cast (unumber) 6 in plus::b#1 = 6
+Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast plus::b#0 = (unumber)7
+Inlining cast plus::b#1 = (unumber)6
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (char *) 1024
+Simplifying constant integer cast 7
+Simplifying constant integer cast 0
+Simplifying constant integer cast 6
+Simplifying constant integer cast 1
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (char) 7
+Finalized unsigned number type (char) 0
+Finalized unsigned number type (char) 6
+Finalized unsigned number type (char) 1
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Alias plus::return#0 = plus::return#4
+Alias plus::return#1 = plus::return#5
+Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
+Successful SSA optimization Pass2AliasElimination
+Constant plus::a#0 = '0'
+Constant plus::b#0 = 7
+Constant plus::a#1 = '1'
+Constant plus::b#1 = 6
+Successful SSA optimization Pass2ConstantIdentification
+Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
+Successful SSA optimization PassNSimplifyExpressionWithZero
+Removing unused procedure __start
+Removing unused procedure block __start
+Removing unused procedure block __start::@1
+Removing unused procedure block __start::@return
+Successful SSA optimization PassNEliminateEmptyStart
+Inlining constant with var siblings plus::a#0
+Inlining constant with var siblings plus::b#0
+Inlining constant with var siblings plus::a#1
+Inlining constant with var siblings plus::b#1
+Constant inlined plus::b#1 = 6
+Constant inlined plus::b#0 = 7
+Constant inlined plus::a#1 = '1'
+Constant inlined plus::a#0 = '0'
+Successful SSA optimization Pass2ConstantInlining
+Consolidated array index constant in *(SCREEN+1)
+Successful SSA optimization Pass2ConstantAdditionElimination
+Adding NOP phi() at start of main
+CALL GRAPH
+Calls in [main] to plus:1 plus:5
+
+Created 2 initial phi equivalence classes
+Coalesced down to 2 phi equivalence classes
+Adding NOP phi() at start of main
+
+FINAL CONTROL FLOW GRAPH
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] plus::return#2 = plus::a#2 + plus::b#2
+ to:plus::@return
+plus::@return: scope:[plus] from plus
+ [12] return
+ to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+void main()
+char main::$0 // 4.0
+char main::$1 // 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // 11.0
+char plus::b
+char plus::b#2 // 11.0
+char plus::return
+char plus::return#0 // 4.0
+char plus::return#1 // 4.0
+char plus::return#2 // 3.75
+
+Initial phi equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
+Added variable main::$0 to live range equivalence class [ main::$0 ]
+Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
+Added variable main::$1 to live range equivalence class [ main::$1 ]
+Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
+Complete equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+[ plus::return#0 ]
+[ main::$0 ]
+[ plus::return#1 ]
+[ main::$1 ]
+[ plus::return#2 ]
+Allocated zp[1]:2 [ plus::a#2 ]
+Allocated zp[1]:3 [ plus::b#2 ]
+Allocated zp[1]:4 [ plus::return#0 ]
+Allocated zp[1]:5 [ main::$0 ]
+Allocated zp[1]:6 [ plus::return#1 ]
+Allocated zp[1]:7 [ main::$1 ]
+Allocated zp[1]:8 [ plus::return#2 ]
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Removing always clobbered register reg byte a as potential for zp[1]:8 [ plus::return#2 ]
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [11] plus::return#2 = plus::a#2 + plus::b#2 [ plus::return#2 ] ( plus:1 [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } plus:5 [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [11] plus::return#2 = plus::a#2 + plus::b#2 [ plus::return#2 ] ( plus:1 [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } plus:5 [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Potential registers zp[1]:2 [ plus::a#2 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:3 [ plus::b#2 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:4 [ plus::return#0 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:5 [ main::$0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:6 [ plus::return#1 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:7 [ main::$1 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:8 [ plus::return#2 ] : zp[1]:8 , reg byte x , reg byte y ,
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [plus] 11: zp[1]:2 [ plus::a#2 ] 11: zp[1]:3 [ plus::b#2 ] 4: zp[1]:4 [ plus::return#0 ] 4: zp[1]:6 [ plus::return#1 ] 3.75: zp[1]:8 [ plus::return#2 ]
+Uplift Scope [main] 4: zp[1]:5 [ main::$0 ] 4: zp[1]:7 [ main::$1 ]
+Uplift Scope []
+
+Uplifting [plus] best 141 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:8 [ plus::return#2 ]
+Limited combination testing to 100 combinations of 768 possible.
+Uplifting [main] best 129 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
+Uplifting [] best 129 combination
+Attempting to uplift remaining variables inzp[1]:8 [ plus::return#2 ]
+Uplifting [plus] best 126 combination reg byte x [ plus::return#2 ]
+
+ASSEMBLER BEFORE OPTIMIZATION
+ // File Comments
+/**
+ * @file call-banked-phi-case-2-close-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #2.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-2-close-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ plus_from_main:
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b1
+ // main::@1
+ __b1:
+ // [3] main::$0 = plus::return#0
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ plus_from___b1:
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b2
+ // main::@2
+ __b2:
+ // [7] main::$1 = plus::return#1
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ jmp __breturn
+ // main::@return
+ __breturn:
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuxx=vbuaa_plus_vbuxx
+ stx.z $ff
+ clc
+ adc.z $ff
+ tax
+ jmp __breturn
+ // plus::@return
+ __breturn:
+ // [12] return
+ rts
+}
+ // File Data
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp __b1
+Removing instruction jmp __b2
+Removing instruction jmp __breturn
+Removing instruction jmp __breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction plus_from_main:
+Removing instruction __b1:
+Removing instruction plus_from___b1:
+Removing instruction __b2:
+Removing instruction __breturn:
+Removing instruction __breturn:
+Succesful ASM optimization Pass5UnusedLabelElimination
+
+FINAL SYMBOL TABLE
+__constant char * const SCREEN = (char *) 1024
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 11.0
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+reg byte x [ plus::return#2 ]
+
+
+FINAL ASSEMBLER
+Score: 114
+
+ // File Comments
+/**
+ * @file call-banked-phi-case-2-close-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #2.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-2-close-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // plus('0', 7)
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@1
+ // [3] main::$0 = plus::return#0
+ // SCREEN[0] = plus('0', 7)
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // plus('1', 6)
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@2
+ // [7] main::$1 = plus::return#1
+ // SCREEN[1] = plus('1', 6)
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ // main::@return
+ // }
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // a+b
+ // [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuxx=vbuaa_plus_vbuxx
+ stx.z $ff
+ clc
+ adc.z $ff
+ tax
+ // plus::@return
+ // }
+ // [12] return
+ rts
+}
+ // File Data
+
diff --git a/src/test/ref/call-banked-phi-case-2-close-0.sym b/src/test/ref/call-banked-phi-case-2-close-0.sym
new file mode 100644
index 000000000..00ee4aca5
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-2-close-0.sym
@@ -0,0 +1,21 @@
+__constant char * const SCREEN = (char *) 1024
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 11.0
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+reg byte x [ plus::return#2 ]
diff --git a/src/test/ref/call-banked-phi-case-2-close-1.asm b/src/test/ref/call-banked-phi-case-2-close-1.asm
new file mode 100644
index 000000000..5a15ee9a6
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-2-close-1.asm
@@ -0,0 +1,96 @@
+/**
+ * @file call-banked-phi-case-2-close-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #2.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ .file [name="call-banked-phi-case-2-close-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ .label SCREEN = $400
+.segment Code
+main: {
+ // plus('0', 7)
+ ldx #7
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ txa
+ // SCREEN[0] = plus('0', 7)
+ sta SCREEN
+ // plus('1', 6)
+ ldx #6
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ txa
+ // SCREEN[1] = plus('1', 6)
+ // close call
+ sta SCREEN+1
+ // }
+ rts
+}
+.segment RAM_Bank1
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // a+b
+ stx.z $ff
+ clc
+ adc.z $ff
+ tax
+ // }
+ rts
+}
diff --git a/src/test/ref/call-banked-phi-case-2-close-1.cfg b/src/test/ref/call-banked-phi-case-2-close-1.cfg
new file mode 100644
index 000000000..b5f3a4af5
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-2-close-1.cfg
@@ -0,0 +1,30 @@
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] plus::return#2 = plus::a#2 + plus::b#2
+ to:plus::@return
+plus::@return: scope:[plus] from plus
+ [12] return
+ to:@return
diff --git a/src/test/ref/call-banked-phi-case-2-close-1.log b/src/test/ref/call-banked-phi-case-2-close-1.log
new file mode 100644
index 000000000..9f7544ae5
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-2-close-1.log
@@ -0,0 +1,520 @@
+Loading link script "call-banked-phi.ld"
+
+CONTROL FLOW GRAPH SSA
+
+void main()
+main: scope:[main] from __start
+ plus::a#0 = '0'
+ plus::b#0 = 7
+ call plus
+ plus::return#0 = plus::return#3
+ to:main::@1
+main::@1: scope:[main] from main
+ plus::return#4 = phi( main/plus::return#0 )
+ main::$0 = plus::return#4
+ SCREEN[0] = main::$0
+ plus::a#1 = '1'
+ plus::b#1 = 6
+ call plus
+ plus::return#1 = plus::return#3
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ plus::return#5 = phi( main::@1/plus::return#1 )
+ main::$1 = plus::return#5
+ SCREEN[1] = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
+ plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
+ plus::$0 = plus::a#2 + plus::b#2
+ plus::return#2 = plus::$0
+ to:plus::@return
+plus::@return: scope:[plus] from plus
+ plus::return#6 = phi( plus/plus::return#2 )
+ plus::return#3 = plus::return#6
+ return
+ to:@return
+
+void __start()
+__start: scope:[__start] from
+ call main
+ to:__start::@1
+__start::@1: scope:[__start] from __start
+ to:__start::@return
+__start::@return: scope:[__start] from __start::@1
+ return
+ to:@return
+
+SYMBOL TABLE SSA
+__constant char * const SCREEN = (char *)$400
+void __start()
+void main()
+char main::$0
+char main::$1
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::$0
+char plus::a
+char plus::a#0
+char plus::a#1
+char plus::a#2
+char plus::b
+char plus::b#0
+char plus::b#1
+char plus::b#2
+char plus::return
+char plus::return#0
+char plus::return#1
+char plus::return#2
+char plus::return#3
+char plus::return#4
+char plus::return#5
+char plus::return#6
+
+Adding number conversion cast (unumber) 7 in plus::b#0 = 7
+Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
+Adding number conversion cast (unumber) 6 in plus::b#1 = 6
+Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast plus::b#0 = (unumber)7
+Inlining cast plus::b#1 = (unumber)6
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (char *) 1024
+Simplifying constant integer cast 7
+Simplifying constant integer cast 0
+Simplifying constant integer cast 6
+Simplifying constant integer cast 1
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (char) 7
+Finalized unsigned number type (char) 0
+Finalized unsigned number type (char) 6
+Finalized unsigned number type (char) 1
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Alias plus::return#0 = plus::return#4
+Alias plus::return#1 = plus::return#5
+Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
+Successful SSA optimization Pass2AliasElimination
+Constant plus::a#0 = '0'
+Constant plus::b#0 = 7
+Constant plus::a#1 = '1'
+Constant plus::b#1 = 6
+Successful SSA optimization Pass2ConstantIdentification
+Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
+Successful SSA optimization PassNSimplifyExpressionWithZero
+Removing unused procedure __start
+Removing unused procedure block __start
+Removing unused procedure block __start::@1
+Removing unused procedure block __start::@return
+Successful SSA optimization PassNEliminateEmptyStart
+Inlining constant with var siblings plus::a#0
+Inlining constant with var siblings plus::b#0
+Inlining constant with var siblings plus::a#1
+Inlining constant with var siblings plus::b#1
+Constant inlined plus::b#1 = 6
+Constant inlined plus::b#0 = 7
+Constant inlined plus::a#1 = '1'
+Constant inlined plus::a#0 = '0'
+Successful SSA optimization Pass2ConstantInlining
+Consolidated array index constant in *(SCREEN+1)
+Successful SSA optimization Pass2ConstantAdditionElimination
+Adding NOP phi() at start of main
+CALL GRAPH
+Calls in [main] to plus:1 plus:5
+
+Created 2 initial phi equivalence classes
+Coalesced down to 2 phi equivalence classes
+Adding NOP phi() at start of main
+
+FINAL CONTROL FLOW GRAPH
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] plus::return#2 = plus::a#2 + plus::b#2
+ to:plus::@return
+plus::@return: scope:[plus] from plus
+ [12] return
+ to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+void main()
+char main::$0 // 4.0
+char main::$1 // 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // 11.0
+char plus::b
+char plus::b#2 // 11.0
+char plus::return
+char plus::return#0 // 4.0
+char plus::return#1 // 4.0
+char plus::return#2 // 3.75
+
+Initial phi equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
+Added variable main::$0 to live range equivalence class [ main::$0 ]
+Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
+Added variable main::$1 to live range equivalence class [ main::$1 ]
+Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
+Complete equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+[ plus::return#0 ]
+[ main::$0 ]
+[ plus::return#1 ]
+[ main::$1 ]
+[ plus::return#2 ]
+Allocated zp[1]:2 [ plus::a#2 ]
+Allocated zp[1]:3 [ plus::b#2 ]
+Allocated zp[1]:4 [ plus::return#0 ]
+Allocated zp[1]:5 [ main::$0 ]
+Allocated zp[1]:6 [ plus::return#1 ]
+Allocated zp[1]:7 [ main::$1 ]
+Allocated zp[1]:8 [ plus::return#2 ]
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Removing always clobbered register reg byte a as potential for zp[1]:8 [ plus::return#2 ]
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [11] plus::return#2 = plus::a#2 + plus::b#2 [ plus::return#2 ] ( plus:1 [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } plus:5 [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [11] plus::return#2 = plus::a#2 + plus::b#2 [ plus::return#2 ] ( plus:1 [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } plus:5 [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Potential registers zp[1]:2 [ plus::a#2 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:3 [ plus::b#2 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:4 [ plus::return#0 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:5 [ main::$0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:6 [ plus::return#1 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:7 [ main::$1 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:8 [ plus::return#2 ] : zp[1]:8 , reg byte x , reg byte y ,
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [plus] 11: zp[1]:2 [ plus::a#2 ] 11: zp[1]:3 [ plus::b#2 ] 4: zp[1]:4 [ plus::return#0 ] 4: zp[1]:6 [ plus::return#1 ] 3.75: zp[1]:8 [ plus::return#2 ]
+Uplift Scope [main] 4: zp[1]:5 [ main::$0 ] 4: zp[1]:7 [ main::$1 ]
+Uplift Scope []
+
+Uplifting [plus] best 141 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:8 [ plus::return#2 ]
+Limited combination testing to 100 combinations of 768 possible.
+Uplifting [main] best 129 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
+Uplifting [] best 129 combination
+Attempting to uplift remaining variables inzp[1]:8 [ plus::return#2 ]
+Uplifting [plus] best 126 combination reg byte x [ plus::return#2 ]
+
+ASSEMBLER BEFORE OPTIMIZATION
+ // File Comments
+/**
+ * @file call-banked-phi-case-2-close-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #2.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-2-close-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ plus_from_main:
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b1
+ // main::@1
+ __b1:
+ // [3] main::$0 = plus::return#0
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ plus_from___b1:
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b2
+ // main::@2
+ __b2:
+ // [7] main::$1 = plus::return#1
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ jmp __breturn
+ // main::@return
+ __breturn:
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuxx=vbuaa_plus_vbuxx
+ stx.z $ff
+ clc
+ adc.z $ff
+ tax
+ jmp __breturn
+ // plus::@return
+ __breturn:
+ // [12] return
+ rts
+}
+ // File Data
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp __b1
+Removing instruction jmp __b2
+Removing instruction jmp __breturn
+Removing instruction jmp __breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction plus_from_main:
+Removing instruction __b1:
+Removing instruction plus_from___b1:
+Removing instruction __b2:
+Removing instruction __breturn:
+Removing instruction __breturn:
+Succesful ASM optimization Pass5UnusedLabelElimination
+
+FINAL SYMBOL TABLE
+__constant char * const SCREEN = (char *) 1024
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 11.0
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+reg byte x [ plus::return#2 ]
+
+
+FINAL ASSEMBLER
+Score: 114
+
+ // File Comments
+/**
+ * @file call-banked-phi-case-2-close-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #2.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-2-close-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // plus('0', 7)
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@1
+ // [3] main::$0 = plus::return#0
+ // SCREEN[0] = plus('0', 7)
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // plus('1', 6)
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@2
+ // [7] main::$1 = plus::return#1
+ // SCREEN[1] = plus('1', 6)
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ // main::@return
+ // }
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // a+b
+ // [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuxx=vbuaa_plus_vbuxx
+ stx.z $ff
+ clc
+ adc.z $ff
+ tax
+ // plus::@return
+ // }
+ // [12] return
+ rts
+}
+ // File Data
+
diff --git a/src/test/ref/call-banked-phi-case-2-close-1.sym b/src/test/ref/call-banked-phi-case-2-close-1.sym
new file mode 100644
index 000000000..00ee4aca5
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-2-close-1.sym
@@ -0,0 +1,21 @@
+__constant char * const SCREEN = (char *) 1024
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 11.0
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+reg byte x [ plus::return#2 ]
diff --git a/src/test/ref/call-banked-phi-case-3-near-0.asm b/src/test/ref/call-banked-phi-case-3-near-0.asm
new file mode 100644
index 000000000..60fa654fa
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-3-near-0.asm
@@ -0,0 +1,106 @@
+/**
+ * @file call-banked-phi-case-3-near-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #3.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ .file [name="call-banked-phi-case-3-near-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ .label SCREEN = $400
+.segment Code
+main: {
+ // plus('0', 7)
+ ldx #7
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ txa
+ // SCREEN[0] = plus('0', 7)
+ sta SCREEN
+ // plus('1', 6)
+ ldx #6
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ txa
+ // SCREEN[1] = plus('1', 6)
+ // close call
+ sta SCREEN+1
+ // }
+ rts
+}
+.segment RAM_Bank1
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // add(a, b)
+ sta.z add.a
+ txa
+ jsr add
+ tax
+ // }
+ rts
+}
+.segment Code
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+add: {
+ .label a = 2
+ // a+b
+ clc
+ adc.z a
+ // }
+ rts
+}
diff --git a/src/test/ref/call-banked-phi-case-3-near-0.cfg b/src/test/ref/call-banked-phi-case-3-near-0.cfg
new file mode 100644
index 000000000..f7ec35c09
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-3-near-0.cfg
@@ -0,0 +1,44 @@
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
diff --git a/src/test/ref/call-banked-phi-case-3-near-0.log b/src/test/ref/call-banked-phi-case-3-near-0.log
new file mode 100644
index 000000000..63c05360e
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-3-near-0.log
@@ -0,0 +1,660 @@
+Loading link script "call-banked-phi.ld"
+
+CONTROL FLOW GRAPH SSA
+
+void main()
+main: scope:[main] from __start
+ plus::a#0 = '0'
+ plus::b#0 = 7
+ call plus
+ plus::return#0 = plus::return#3
+ to:main::@1
+main::@1: scope:[main] from main
+ plus::return#4 = phi( main/plus::return#0 )
+ main::$0 = plus::return#4
+ SCREEN[0] = main::$0
+ plus::a#1 = '1'
+ plus::b#1 = 6
+ call plus
+ plus::return#1 = plus::return#3
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ plus::return#5 = phi( main::@1/plus::return#1 )
+ main::$1 = plus::return#5
+ SCREEN[1] = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
+ plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
+ add::a#0 = plus::a#2
+ add::b#0 = plus::b#2
+ call add
+ add::return#0 = add::return#2
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ add::return#3 = phi( plus/add::return#0 )
+ plus::$0 = add::return#3
+ plus::return#2 = plus::$0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ plus::return#6 = phi( plus::@1/plus::return#2 )
+ plus::return#3 = plus::return#6
+ return
+ to:@return
+
+char add(char a , char b)
+add: scope:[add] from plus
+ add::b#1 = phi( plus/add::b#0 )
+ add::a#1 = phi( plus/add::a#0 )
+ add::$0 = add::a#1 + add::b#1
+ add::return#1 = add::$0
+ to:add::@return
+add::@return: scope:[add] from add
+ add::return#4 = phi( add/add::return#1 )
+ add::return#2 = add::return#4
+ return
+ to:@return
+
+void __start()
+__start: scope:[__start] from
+ call main
+ to:__start::@1
+__start::@1: scope:[__start] from __start
+ to:__start::@return
+__start::@return: scope:[__start] from __start::@1
+ return
+ to:@return
+
+SYMBOL TABLE SSA
+__constant char * const SCREEN = (char *)$400
+void __start()
+char add(char a , char b)
+char add::$0
+char add::a
+char add::a#0
+char add::a#1
+char add::b
+char add::b#0
+char add::b#1
+char add::return
+char add::return#0
+char add::return#1
+char add::return#2
+char add::return#3
+char add::return#4
+void main()
+char main::$0
+char main::$1
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::$0
+char plus::a
+char plus::a#0
+char plus::a#1
+char plus::a#2
+char plus::b
+char plus::b#0
+char plus::b#1
+char plus::b#2
+char plus::return
+char plus::return#0
+char plus::return#1
+char plus::return#2
+char plus::return#3
+char plus::return#4
+char plus::return#5
+char plus::return#6
+
+Adding number conversion cast (unumber) 7 in plus::b#0 = 7
+Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
+Adding number conversion cast (unumber) 6 in plus::b#1 = 6
+Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast plus::b#0 = (unumber)7
+Inlining cast plus::b#1 = (unumber)6
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (char *) 1024
+Simplifying constant integer cast 7
+Simplifying constant integer cast 0
+Simplifying constant integer cast 6
+Simplifying constant integer cast 1
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (char) 7
+Finalized unsigned number type (char) 0
+Finalized unsigned number type (char) 6
+Finalized unsigned number type (char) 1
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Alias plus::return#0 = plus::return#4
+Alias plus::return#1 = plus::return#5
+Alias add::return#0 = add::return#3
+Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
+Alias add::return#1 = add::$0 add::return#4 add::return#2
+Successful SSA optimization Pass2AliasElimination
+Identical Phi Values add::a#1 add::a#0
+Identical Phi Values add::b#1 add::b#0
+Successful SSA optimization Pass2IdenticalPhiElimination
+Constant plus::a#0 = '0'
+Constant plus::b#0 = 7
+Constant plus::a#1 = '1'
+Constant plus::b#1 = 6
+Successful SSA optimization Pass2ConstantIdentification
+Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
+Successful SSA optimization PassNSimplifyExpressionWithZero
+Removing unused procedure __start
+Removing unused procedure block __start
+Removing unused procedure block __start::@1
+Removing unused procedure block __start::@return
+Successful SSA optimization PassNEliminateEmptyStart
+Inlining constant with var siblings plus::a#0
+Inlining constant with var siblings plus::b#0
+Inlining constant with var siblings plus::a#1
+Inlining constant with var siblings plus::b#1
+Constant inlined plus::b#1 = 6
+Constant inlined plus::b#0 = 7
+Constant inlined plus::a#1 = '1'
+Constant inlined plus::a#0 = '0'
+Successful SSA optimization Pass2ConstantInlining
+Consolidated array index constant in *(SCREEN+1)
+Successful SSA optimization Pass2ConstantAdditionElimination
+Adding NOP phi() at start of main
+CALL GRAPH
+Calls in [main] to plus:1 plus:5
+Calls in [plus] to add:13
+
+Created 2 initial phi equivalence classes
+Coalesced down to 2 phi equivalence classes
+Adding NOP phi() at start of main
+
+FINAL CONTROL FLOW GRAPH
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+char add(char a , char b)
+char add::a
+char add::a#0 // 56.0
+char add::b
+char add::b#0 // 112.0
+char add::return
+char add::return#0 // 22.0
+char add::return#1 // 37.33333333333333
+void main()
+char main::$0 // 4.0
+char main::$1 // 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // 11.0
+char plus::b
+char plus::b#2 // 5.5
+char plus::return
+char plus::return#0 // 4.0
+char plus::return#1 // 4.0
+char plus::return#2 // 3.75
+
+Initial phi equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
+Added variable main::$0 to live range equivalence class [ main::$0 ]
+Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
+Added variable main::$1 to live range equivalence class [ main::$1 ]
+Added variable add::a#0 to live range equivalence class [ add::a#0 ]
+Added variable add::b#0 to live range equivalence class [ add::b#0 ]
+Added variable add::return#0 to live range equivalence class [ add::return#0 ]
+Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
+Added variable add::return#1 to live range equivalence class [ add::return#1 ]
+Complete equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+[ plus::return#0 ]
+[ main::$0 ]
+[ plus::return#1 ]
+[ main::$1 ]
+[ add::a#0 ]
+[ add::b#0 ]
+[ add::return#0 ]
+[ plus::return#2 ]
+[ add::return#1 ]
+Allocated zp[1]:2 [ add::b#0 ]
+Allocated zp[1]:3 [ add::a#0 ]
+Allocated zp[1]:4 [ add::return#1 ]
+Allocated zp[1]:5 [ add::return#0 ]
+Allocated zp[1]:6 [ plus::a#2 ]
+Allocated zp[1]:7 [ plus::b#2 ]
+Allocated zp[1]:8 [ plus::return#0 ]
+Allocated zp[1]:9 [ main::$0 ]
+Allocated zp[1]:10 [ plus::return#1 ]
+Allocated zp[1]:11 [ main::$1 ]
+Allocated zp[1]:12 [ plus::return#2 ]
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ]
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y ,
+Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
+Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
+Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
+Uplift Scope []
+
+Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ]
+Limited combination testing to 100 combinations of 256 possible.
+Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
+Limited combination testing to 100 combinations of 768 possible.
+Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
+Uplifting [] best 149 combination
+Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
+Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ]
+Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
+Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ]
+Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
+
+ASSEMBLER BEFORE OPTIMIZATION
+ // File Comments
+/**
+ * @file call-banked-phi-case-3-near-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #3.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-3-near-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ plus_from_main:
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b1
+ // main::@1
+ __b1:
+ // [3] main::$0 = plus::return#0
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ plus_from___b1:
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b2
+ // main::@2
+ __b2:
+ // [7] main::$1 = plus::return#1
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ jmp __breturn
+ // main::@return
+ __breturn:
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
+ sta.z add.a
+ // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
+ txa
+ // [13] call add
+ jsr add
+ // [14] add::return#0 = add::return#1
+ jmp __b1
+ // plus::@1
+ __b1:
+ // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
+ tax
+ jmp __breturn
+ // plus::@return
+ __breturn:
+ // [16] return
+ rts
+}
+.segment Code
+ // add
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+add: {
+ .label a = 2
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ jmp __breturn
+ // add::@return
+ __breturn:
+ // [18] return
+ rts
+}
+ // File Data
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp __b1
+Removing instruction jmp __b2
+Removing instruction jmp __breturn
+Removing instruction jmp __b1
+Removing instruction jmp __breturn
+Removing instruction jmp __breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction plus_from_main:
+Removing instruction __b1:
+Removing instruction plus_from___b1:
+Removing instruction __b2:
+Removing instruction __breturn:
+Removing instruction __b1:
+Removing instruction __breturn:
+Removing instruction __breturn:
+Succesful ASM optimization Pass5UnusedLabelElimination
+
+FINAL SYMBOL TABLE
+__constant char * const SCREEN = (char *) 1024
+char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte a 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte x [ plus::return#2 ]
+reg byte a [ add::return#1 ]
+
+
+FINAL ASSEMBLER
+Score: 128
+
+ // File Comments
+/**
+ * @file call-banked-phi-case-3-near-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #3.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-3-near-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // plus('0', 7)
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@1
+ // [3] main::$0 = plus::return#0
+ // SCREEN[0] = plus('0', 7)
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // plus('1', 6)
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@2
+ // [7] main::$1 = plus::return#1
+ // SCREEN[1] = plus('1', 6)
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ // main::@return
+ // }
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // add(a, b)
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
+ sta.z add.a
+ // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
+ txa
+ // [13] call add
+ jsr add
+ // [14] add::return#0 = add::return#1
+ // plus::@1
+ // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
+ tax
+ // plus::@return
+ // }
+ // [16] return
+ rts
+}
+.segment Code
+ // add
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+add: {
+ .label a = 2
+ // a+b
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ // add::@return
+ // }
+ // [18] return
+ rts
+}
+ // File Data
+
diff --git a/src/test/ref/call-banked-phi-case-3-near-0.sym b/src/test/ref/call-banked-phi-case-3-near-0.sym
new file mode 100644
index 000000000..658eb6563
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-3-near-0.sym
@@ -0,0 +1,33 @@
+__constant char * const SCREEN = (char *) 1024
+char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte a 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte x [ plus::return#2 ]
+reg byte a [ add::return#1 ]
diff --git a/src/test/ref/call-banked-phi-case-3-near-1.asm b/src/test/ref/call-banked-phi-case-3-near-1.asm
new file mode 100644
index 000000000..9ffca6b4a
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-3-near-1.asm
@@ -0,0 +1,106 @@
+/**
+ * @file call-banked-phi-case-3-near-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #3.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ .file [name="call-banked-phi-case-3-near-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ .label SCREEN = $400
+.segment Code
+main: {
+ // plus('0', 7)
+ ldx #7
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ txa
+ // SCREEN[0] = plus('0', 7)
+ sta SCREEN
+ // plus('1', 6)
+ ldx #6
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ txa
+ // SCREEN[1] = plus('1', 6)
+ // close call
+ sta SCREEN+1
+ // }
+ rts
+}
+.segment RAM_Bank1
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // add(a, b)
+ sta.z add.a
+ txa
+ jsr add
+ tax
+ // }
+ rts
+}
+.segment Code
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+add: {
+ .label a = 2
+ // a+b
+ clc
+ adc.z a
+ // }
+ rts
+}
diff --git a/src/test/ref/call-banked-phi-case-3-near-1.cfg b/src/test/ref/call-banked-phi-case-3-near-1.cfg
new file mode 100644
index 000000000..f7ec35c09
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-3-near-1.cfg
@@ -0,0 +1,44 @@
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
diff --git a/src/test/ref/call-banked-phi-case-3-near-1.log b/src/test/ref/call-banked-phi-case-3-near-1.log
new file mode 100644
index 000000000..7b34ab84a
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-3-near-1.log
@@ -0,0 +1,660 @@
+Loading link script "call-banked-phi.ld"
+
+CONTROL FLOW GRAPH SSA
+
+void main()
+main: scope:[main] from __start
+ plus::a#0 = '0'
+ plus::b#0 = 7
+ call plus
+ plus::return#0 = plus::return#3
+ to:main::@1
+main::@1: scope:[main] from main
+ plus::return#4 = phi( main/plus::return#0 )
+ main::$0 = plus::return#4
+ SCREEN[0] = main::$0
+ plus::a#1 = '1'
+ plus::b#1 = 6
+ call plus
+ plus::return#1 = plus::return#3
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ plus::return#5 = phi( main::@1/plus::return#1 )
+ main::$1 = plus::return#5
+ SCREEN[1] = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
+ plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
+ add::a#0 = plus::a#2
+ add::b#0 = plus::b#2
+ call add
+ add::return#0 = add::return#2
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ add::return#3 = phi( plus/add::return#0 )
+ plus::$0 = add::return#3
+ plus::return#2 = plus::$0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ plus::return#6 = phi( plus::@1/plus::return#2 )
+ plus::return#3 = plus::return#6
+ return
+ to:@return
+
+char add(char a , char b)
+add: scope:[add] from plus
+ add::b#1 = phi( plus/add::b#0 )
+ add::a#1 = phi( plus/add::a#0 )
+ add::$0 = add::a#1 + add::b#1
+ add::return#1 = add::$0
+ to:add::@return
+add::@return: scope:[add] from add
+ add::return#4 = phi( add/add::return#1 )
+ add::return#2 = add::return#4
+ return
+ to:@return
+
+void __start()
+__start: scope:[__start] from
+ call main
+ to:__start::@1
+__start::@1: scope:[__start] from __start
+ to:__start::@return
+__start::@return: scope:[__start] from __start::@1
+ return
+ to:@return
+
+SYMBOL TABLE SSA
+__constant char * const SCREEN = (char *)$400
+void __start()
+char add(char a , char b)
+char add::$0
+char add::a
+char add::a#0
+char add::a#1
+char add::b
+char add::b#0
+char add::b#1
+char add::return
+char add::return#0
+char add::return#1
+char add::return#2
+char add::return#3
+char add::return#4
+void main()
+char main::$0
+char main::$1
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::$0
+char plus::a
+char plus::a#0
+char plus::a#1
+char plus::a#2
+char plus::b
+char plus::b#0
+char plus::b#1
+char plus::b#2
+char plus::return
+char plus::return#0
+char plus::return#1
+char plus::return#2
+char plus::return#3
+char plus::return#4
+char plus::return#5
+char plus::return#6
+
+Adding number conversion cast (unumber) 7 in plus::b#0 = 7
+Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
+Adding number conversion cast (unumber) 6 in plus::b#1 = 6
+Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast plus::b#0 = (unumber)7
+Inlining cast plus::b#1 = (unumber)6
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (char *) 1024
+Simplifying constant integer cast 7
+Simplifying constant integer cast 0
+Simplifying constant integer cast 6
+Simplifying constant integer cast 1
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (char) 7
+Finalized unsigned number type (char) 0
+Finalized unsigned number type (char) 6
+Finalized unsigned number type (char) 1
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Alias plus::return#0 = plus::return#4
+Alias plus::return#1 = plus::return#5
+Alias add::return#0 = add::return#3
+Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
+Alias add::return#1 = add::$0 add::return#4 add::return#2
+Successful SSA optimization Pass2AliasElimination
+Identical Phi Values add::a#1 add::a#0
+Identical Phi Values add::b#1 add::b#0
+Successful SSA optimization Pass2IdenticalPhiElimination
+Constant plus::a#0 = '0'
+Constant plus::b#0 = 7
+Constant plus::a#1 = '1'
+Constant plus::b#1 = 6
+Successful SSA optimization Pass2ConstantIdentification
+Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
+Successful SSA optimization PassNSimplifyExpressionWithZero
+Removing unused procedure __start
+Removing unused procedure block __start
+Removing unused procedure block __start::@1
+Removing unused procedure block __start::@return
+Successful SSA optimization PassNEliminateEmptyStart
+Inlining constant with var siblings plus::a#0
+Inlining constant with var siblings plus::b#0
+Inlining constant with var siblings plus::a#1
+Inlining constant with var siblings plus::b#1
+Constant inlined plus::b#1 = 6
+Constant inlined plus::b#0 = 7
+Constant inlined plus::a#1 = '1'
+Constant inlined plus::a#0 = '0'
+Successful SSA optimization Pass2ConstantInlining
+Consolidated array index constant in *(SCREEN+1)
+Successful SSA optimization Pass2ConstantAdditionElimination
+Adding NOP phi() at start of main
+CALL GRAPH
+Calls in [main] to plus:1 plus:5
+Calls in [plus] to add:13
+
+Created 2 initial phi equivalence classes
+Coalesced down to 2 phi equivalence classes
+Adding NOP phi() at start of main
+
+FINAL CONTROL FLOW GRAPH
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+char add(char a , char b)
+char add::a
+char add::a#0 // 56.0
+char add::b
+char add::b#0 // 112.0
+char add::return
+char add::return#0 // 22.0
+char add::return#1 // 37.33333333333333
+void main()
+char main::$0 // 4.0
+char main::$1 // 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // 11.0
+char plus::b
+char plus::b#2 // 5.5
+char plus::return
+char plus::return#0 // 4.0
+char plus::return#1 // 4.0
+char plus::return#2 // 3.75
+
+Initial phi equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
+Added variable main::$0 to live range equivalence class [ main::$0 ]
+Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
+Added variable main::$1 to live range equivalence class [ main::$1 ]
+Added variable add::a#0 to live range equivalence class [ add::a#0 ]
+Added variable add::b#0 to live range equivalence class [ add::b#0 ]
+Added variable add::return#0 to live range equivalence class [ add::return#0 ]
+Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
+Added variable add::return#1 to live range equivalence class [ add::return#1 ]
+Complete equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+[ plus::return#0 ]
+[ main::$0 ]
+[ plus::return#1 ]
+[ main::$1 ]
+[ add::a#0 ]
+[ add::b#0 ]
+[ add::return#0 ]
+[ plus::return#2 ]
+[ add::return#1 ]
+Allocated zp[1]:2 [ add::b#0 ]
+Allocated zp[1]:3 [ add::a#0 ]
+Allocated zp[1]:4 [ add::return#1 ]
+Allocated zp[1]:5 [ add::return#0 ]
+Allocated zp[1]:6 [ plus::a#2 ]
+Allocated zp[1]:7 [ plus::b#2 ]
+Allocated zp[1]:8 [ plus::return#0 ]
+Allocated zp[1]:9 [ main::$0 ]
+Allocated zp[1]:10 [ plus::return#1 ]
+Allocated zp[1]:11 [ main::$1 ]
+Allocated zp[1]:12 [ plus::return#2 ]
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ]
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y ,
+Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
+Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
+Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
+Uplift Scope []
+
+Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ]
+Limited combination testing to 100 combinations of 256 possible.
+Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
+Limited combination testing to 100 combinations of 768 possible.
+Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
+Uplifting [] best 149 combination
+Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
+Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ]
+Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
+Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ]
+Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
+
+ASSEMBLER BEFORE OPTIMIZATION
+ // File Comments
+/**
+ * @file call-banked-phi-case-3-near-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #3.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-3-near-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ plus_from_main:
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b1
+ // main::@1
+ __b1:
+ // [3] main::$0 = plus::return#0
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ plus_from___b1:
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b2
+ // main::@2
+ __b2:
+ // [7] main::$1 = plus::return#1
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ jmp __breturn
+ // main::@return
+ __breturn:
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
+ sta.z add.a
+ // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
+ txa
+ // [13] call add
+ jsr add
+ // [14] add::return#0 = add::return#1
+ jmp __b1
+ // plus::@1
+ __b1:
+ // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
+ tax
+ jmp __breturn
+ // plus::@return
+ __breturn:
+ // [16] return
+ rts
+}
+.segment Code
+ // add
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+add: {
+ .label a = 2
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ jmp __breturn
+ // add::@return
+ __breturn:
+ // [18] return
+ rts
+}
+ // File Data
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp __b1
+Removing instruction jmp __b2
+Removing instruction jmp __breturn
+Removing instruction jmp __b1
+Removing instruction jmp __breturn
+Removing instruction jmp __breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction plus_from_main:
+Removing instruction __b1:
+Removing instruction plus_from___b1:
+Removing instruction __b2:
+Removing instruction __breturn:
+Removing instruction __b1:
+Removing instruction __breturn:
+Removing instruction __breturn:
+Succesful ASM optimization Pass5UnusedLabelElimination
+
+FINAL SYMBOL TABLE
+__constant char * const SCREEN = (char *) 1024
+char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte a 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte x [ plus::return#2 ]
+reg byte a [ add::return#1 ]
+
+
+FINAL ASSEMBLER
+Score: 128
+
+ // File Comments
+/**
+ * @file call-banked-phi-case-3-near-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #3.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-3-near-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // plus('0', 7)
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@1
+ // [3] main::$0 = plus::return#0
+ // SCREEN[0] = plus('0', 7)
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // plus('1', 6)
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@2
+ // [7] main::$1 = plus::return#1
+ // SCREEN[1] = plus('1', 6)
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ // main::@return
+ // }
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // add(a, b)
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
+ sta.z add.a
+ // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
+ txa
+ // [13] call add
+ jsr add
+ // [14] add::return#0 = add::return#1
+ // plus::@1
+ // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
+ tax
+ // plus::@return
+ // }
+ // [16] return
+ rts
+}
+.segment Code
+ // add
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+add: {
+ .label a = 2
+ // a+b
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ // add::@return
+ // }
+ // [18] return
+ rts
+}
+ // File Data
+
diff --git a/src/test/ref/call-banked-phi-case-3-near-1.sym b/src/test/ref/call-banked-phi-case-3-near-1.sym
new file mode 100644
index 000000000..658eb6563
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-3-near-1.sym
@@ -0,0 +1,33 @@
+__constant char * const SCREEN = (char *) 1024
+char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte a 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte x [ plus::return#2 ]
+reg byte a [ add::return#1 ]
diff --git a/src/test/ref/call-banked-phi-case-4-near-0.asm b/src/test/ref/call-banked-phi-case-4-near-0.asm
new file mode 100644
index 000000000..a7a1cbbf4
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-4-near-0.asm
@@ -0,0 +1,106 @@
+/**
+ * @file call-banked-phi-case-4-near-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #4.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ .file [name="call-banked-phi-case-4-near-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ .label SCREEN = $400
+.segment Code
+main: {
+ // plus('0', 7)
+ ldx #7
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ txa
+ // SCREEN[0] = plus('0', 7)
+ sta SCREEN
+ // plus('1', 6)
+ ldx #6
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ txa
+ // SCREEN[1] = plus('1', 6)
+ // close call
+ sta SCREEN+1
+ // }
+ rts
+}
+.segment RAM_Bank1
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // add(a, b)
+ sta.z add.a
+ txa
+ jsr add
+ tax
+ // }
+ rts
+}
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_ram, 1)
+add: {
+ .label a = 2
+ // a+b
+ clc
+ adc.z a
+ // }
+ rts
+}
diff --git a/src/test/ref/call-banked-phi-case-4-near-0.cfg b/src/test/ref/call-banked-phi-case-4-near-0.cfg
new file mode 100644
index 000000000..bb224fbfe
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-4-near-0.cfg
@@ -0,0 +1,44 @@
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+__bank(cx16_ram, 1) char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
diff --git a/src/test/ref/call-banked-phi-case-4-near-0.log b/src/test/ref/call-banked-phi-case-4-near-0.log
new file mode 100644
index 000000000..5532553ee
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-4-near-0.log
@@ -0,0 +1,660 @@
+Loading link script "call-banked-phi.ld"
+
+CONTROL FLOW GRAPH SSA
+
+void main()
+main: scope:[main] from __start
+ plus::a#0 = '0'
+ plus::b#0 = 7
+ call plus
+ plus::return#0 = plus::return#3
+ to:main::@1
+main::@1: scope:[main] from main
+ plus::return#4 = phi( main/plus::return#0 )
+ main::$0 = plus::return#4
+ SCREEN[0] = main::$0
+ plus::a#1 = '1'
+ plus::b#1 = 6
+ call plus
+ plus::return#1 = plus::return#3
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ plus::return#5 = phi( main::@1/plus::return#1 )
+ main::$1 = plus::return#5
+ SCREEN[1] = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
+ plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
+ add::a#0 = plus::a#2
+ add::b#0 = plus::b#2
+ call add
+ add::return#0 = add::return#2
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ add::return#3 = phi( plus/add::return#0 )
+ plus::$0 = add::return#3
+ plus::return#2 = plus::$0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ plus::return#6 = phi( plus::@1/plus::return#2 )
+ plus::return#3 = plus::return#6
+ return
+ to:@return
+
+__bank(cx16_ram, 1) char add(char a , char b)
+add: scope:[add] from plus
+ add::b#1 = phi( plus/add::b#0 )
+ add::a#1 = phi( plus/add::a#0 )
+ add::$0 = add::a#1 + add::b#1
+ add::return#1 = add::$0
+ to:add::@return
+add::@return: scope:[add] from add
+ add::return#4 = phi( add/add::return#1 )
+ add::return#2 = add::return#4
+ return
+ to:@return
+
+void __start()
+__start: scope:[__start] from
+ call main
+ to:__start::@1
+__start::@1: scope:[__start] from __start
+ to:__start::@return
+__start::@return: scope:[__start] from __start::@1
+ return
+ to:@return
+
+SYMBOL TABLE SSA
+__constant char * const SCREEN = (char *)$400
+void __start()
+__bank(cx16_ram, 1) char add(char a , char b)
+char add::$0
+char add::a
+char add::a#0
+char add::a#1
+char add::b
+char add::b#0
+char add::b#1
+char add::return
+char add::return#0
+char add::return#1
+char add::return#2
+char add::return#3
+char add::return#4
+void main()
+char main::$0
+char main::$1
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::$0
+char plus::a
+char plus::a#0
+char plus::a#1
+char plus::a#2
+char plus::b
+char plus::b#0
+char plus::b#1
+char plus::b#2
+char plus::return
+char plus::return#0
+char plus::return#1
+char plus::return#2
+char plus::return#3
+char plus::return#4
+char plus::return#5
+char plus::return#6
+
+Adding number conversion cast (unumber) 7 in plus::b#0 = 7
+Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
+Adding number conversion cast (unumber) 6 in plus::b#1 = 6
+Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast plus::b#0 = (unumber)7
+Inlining cast plus::b#1 = (unumber)6
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (char *) 1024
+Simplifying constant integer cast 7
+Simplifying constant integer cast 0
+Simplifying constant integer cast 6
+Simplifying constant integer cast 1
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (char) 7
+Finalized unsigned number type (char) 0
+Finalized unsigned number type (char) 6
+Finalized unsigned number type (char) 1
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Alias plus::return#0 = plus::return#4
+Alias plus::return#1 = plus::return#5
+Alias add::return#0 = add::return#3
+Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
+Alias add::return#1 = add::$0 add::return#4 add::return#2
+Successful SSA optimization Pass2AliasElimination
+Identical Phi Values add::a#1 add::a#0
+Identical Phi Values add::b#1 add::b#0
+Successful SSA optimization Pass2IdenticalPhiElimination
+Constant plus::a#0 = '0'
+Constant plus::b#0 = 7
+Constant plus::a#1 = '1'
+Constant plus::b#1 = 6
+Successful SSA optimization Pass2ConstantIdentification
+Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
+Successful SSA optimization PassNSimplifyExpressionWithZero
+Removing unused procedure __start
+Removing unused procedure block __start
+Removing unused procedure block __start::@1
+Removing unused procedure block __start::@return
+Successful SSA optimization PassNEliminateEmptyStart
+Inlining constant with var siblings plus::a#0
+Inlining constant with var siblings plus::b#0
+Inlining constant with var siblings plus::a#1
+Inlining constant with var siblings plus::b#1
+Constant inlined plus::b#1 = 6
+Constant inlined plus::b#0 = 7
+Constant inlined plus::a#1 = '1'
+Constant inlined plus::a#0 = '0'
+Successful SSA optimization Pass2ConstantInlining
+Consolidated array index constant in *(SCREEN+1)
+Successful SSA optimization Pass2ConstantAdditionElimination
+Adding NOP phi() at start of main
+CALL GRAPH
+Calls in [main] to plus:1 plus:5
+Calls in [plus] to add:13
+
+Created 2 initial phi equivalence classes
+Coalesced down to 2 phi equivalence classes
+Adding NOP phi() at start of main
+
+FINAL CONTROL FLOW GRAPH
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+__bank(cx16_ram, 1) char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+__bank(cx16_ram, 1) char add(char a , char b)
+char add::a
+char add::a#0 // 56.0
+char add::b
+char add::b#0 // 112.0
+char add::return
+char add::return#0 // 22.0
+char add::return#1 // 37.33333333333333
+void main()
+char main::$0 // 4.0
+char main::$1 // 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // 11.0
+char plus::b
+char plus::b#2 // 5.5
+char plus::return
+char plus::return#0 // 4.0
+char plus::return#1 // 4.0
+char plus::return#2 // 3.75
+
+Initial phi equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
+Added variable main::$0 to live range equivalence class [ main::$0 ]
+Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
+Added variable main::$1 to live range equivalence class [ main::$1 ]
+Added variable add::a#0 to live range equivalence class [ add::a#0 ]
+Added variable add::b#0 to live range equivalence class [ add::b#0 ]
+Added variable add::return#0 to live range equivalence class [ add::return#0 ]
+Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
+Added variable add::return#1 to live range equivalence class [ add::return#1 ]
+Complete equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+[ plus::return#0 ]
+[ main::$0 ]
+[ plus::return#1 ]
+[ main::$1 ]
+[ add::a#0 ]
+[ add::b#0 ]
+[ add::return#0 ]
+[ plus::return#2 ]
+[ add::return#1 ]
+Allocated zp[1]:2 [ add::b#0 ]
+Allocated zp[1]:3 [ add::a#0 ]
+Allocated zp[1]:4 [ add::return#1 ]
+Allocated zp[1]:5 [ add::return#0 ]
+Allocated zp[1]:6 [ plus::a#2 ]
+Allocated zp[1]:7 [ plus::b#2 ]
+Allocated zp[1]:8 [ plus::return#0 ]
+Allocated zp[1]:9 [ main::$0 ]
+Allocated zp[1]:10 [ plus::return#1 ]
+Allocated zp[1]:11 [ main::$1 ]
+Allocated zp[1]:12 [ plus::return#2 ]
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ]
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y ,
+Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
+Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
+Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
+Uplift Scope []
+
+Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ]
+Limited combination testing to 100 combinations of 256 possible.
+Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
+Limited combination testing to 100 combinations of 768 possible.
+Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
+Uplifting [] best 149 combination
+Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
+Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ]
+Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
+Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ]
+Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
+
+ASSEMBLER BEFORE OPTIMIZATION
+ // File Comments
+/**
+ * @file call-banked-phi-case-4-near-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #4.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-4-near-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ plus_from_main:
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b1
+ // main::@1
+ __b1:
+ // [3] main::$0 = plus::return#0
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ plus_from___b1:
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b2
+ // main::@2
+ __b2:
+ // [7] main::$1 = plus::return#1
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ jmp __breturn
+ // main::@return
+ __breturn:
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
+ sta.z add.a
+ // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
+ txa
+ // [13] call add
+ jsr add
+ // [14] add::return#0 = add::return#1
+ jmp __b1
+ // plus::@1
+ __b1:
+ // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
+ tax
+ jmp __breturn
+ // plus::@return
+ __breturn:
+ // [16] return
+ rts
+}
+ // add
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_ram, 1)
+add: {
+ .label a = 2
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ jmp __breturn
+ // add::@return
+ __breturn:
+ // [18] return
+ rts
+}
+ // File Data
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp __b1
+Removing instruction jmp __b2
+Removing instruction jmp __breturn
+Removing instruction jmp __b1
+Removing instruction jmp __breturn
+Removing instruction jmp __breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction plus_from_main:
+Removing instruction __b1:
+Removing instruction plus_from___b1:
+Removing instruction __b2:
+Removing instruction __breturn:
+Removing instruction __b1:
+Removing instruction __breturn:
+Removing instruction __breturn:
+Succesful ASM optimization Pass5UnusedLabelElimination
+
+FINAL SYMBOL TABLE
+__constant char * const SCREEN = (char *) 1024
+__bank(cx16_ram, 1) char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte a 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte x [ plus::return#2 ]
+reg byte a [ add::return#1 ]
+
+
+FINAL ASSEMBLER
+Score: 128
+
+ // File Comments
+/**
+ * @file call-banked-phi-case-4-near-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #4.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-4-near-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // plus('0', 7)
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@1
+ // [3] main::$0 = plus::return#0
+ // SCREEN[0] = plus('0', 7)
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // plus('1', 6)
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@2
+ // [7] main::$1 = plus::return#1
+ // SCREEN[1] = plus('1', 6)
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ // main::@return
+ // }
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // add(a, b)
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
+ sta.z add.a
+ // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
+ txa
+ // [13] call add
+ jsr add
+ // [14] add::return#0 = add::return#1
+ // plus::@1
+ // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
+ tax
+ // plus::@return
+ // }
+ // [16] return
+ rts
+}
+ // add
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_ram, 1)
+add: {
+ .label a = 2
+ // a+b
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ // add::@return
+ // }
+ // [18] return
+ rts
+}
+ // File Data
+
diff --git a/src/test/ref/call-banked-phi-case-4-near-0.sym b/src/test/ref/call-banked-phi-case-4-near-0.sym
new file mode 100644
index 000000000..4267f6a35
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-4-near-0.sym
@@ -0,0 +1,33 @@
+__constant char * const SCREEN = (char *) 1024
+__bank(cx16_ram, 1) char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte a 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte x [ plus::return#2 ]
+reg byte a [ add::return#1 ]
diff --git a/src/test/ref/call-banked-phi-case-4-near-1.asm b/src/test/ref/call-banked-phi-case-4-near-1.asm
new file mode 100644
index 000000000..97f10c7d0
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-4-near-1.asm
@@ -0,0 +1,106 @@
+/**
+ * @file call-banked-phi-case-4-near-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #4.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ .file [name="call-banked-phi-case-4-near-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ .label SCREEN = $400
+.segment Code
+main: {
+ // plus('0', 7)
+ ldx #7
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ txa
+ // SCREEN[0] = plus('0', 7)
+ sta SCREEN
+ // plus('1', 6)
+ ldx #6
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ txa
+ // SCREEN[1] = plus('1', 6)
+ // close call
+ sta SCREEN+1
+ // }
+ rts
+}
+.segment RAM_Bank1
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // add(a, b)
+ sta.z add.a
+ txa
+ jsr add
+ tax
+ // }
+ rts
+}
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_ram, 1)
+add: {
+ .label a = 2
+ // a+b
+ clc
+ adc.z a
+ // }
+ rts
+}
diff --git a/src/test/ref/call-banked-phi-case-4-near-1.cfg b/src/test/ref/call-banked-phi-case-4-near-1.cfg
new file mode 100644
index 000000000..bb224fbfe
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-4-near-1.cfg
@@ -0,0 +1,44 @@
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+__bank(cx16_ram, 1) char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
diff --git a/src/test/ref/call-banked-phi-case-4-near-1.log b/src/test/ref/call-banked-phi-case-4-near-1.log
new file mode 100644
index 000000000..4205fd77c
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-4-near-1.log
@@ -0,0 +1,660 @@
+Loading link script "call-banked-phi.ld"
+
+CONTROL FLOW GRAPH SSA
+
+void main()
+main: scope:[main] from __start
+ plus::a#0 = '0'
+ plus::b#0 = 7
+ call plus
+ plus::return#0 = plus::return#3
+ to:main::@1
+main::@1: scope:[main] from main
+ plus::return#4 = phi( main/plus::return#0 )
+ main::$0 = plus::return#4
+ SCREEN[0] = main::$0
+ plus::a#1 = '1'
+ plus::b#1 = 6
+ call plus
+ plus::return#1 = plus::return#3
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ plus::return#5 = phi( main::@1/plus::return#1 )
+ main::$1 = plus::return#5
+ SCREEN[1] = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
+ plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
+ add::a#0 = plus::a#2
+ add::b#0 = plus::b#2
+ call add
+ add::return#0 = add::return#2
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ add::return#3 = phi( plus/add::return#0 )
+ plus::$0 = add::return#3
+ plus::return#2 = plus::$0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ plus::return#6 = phi( plus::@1/plus::return#2 )
+ plus::return#3 = plus::return#6
+ return
+ to:@return
+
+__bank(cx16_ram, 1) char add(char a , char b)
+add: scope:[add] from plus
+ add::b#1 = phi( plus/add::b#0 )
+ add::a#1 = phi( plus/add::a#0 )
+ add::$0 = add::a#1 + add::b#1
+ add::return#1 = add::$0
+ to:add::@return
+add::@return: scope:[add] from add
+ add::return#4 = phi( add/add::return#1 )
+ add::return#2 = add::return#4
+ return
+ to:@return
+
+void __start()
+__start: scope:[__start] from
+ call main
+ to:__start::@1
+__start::@1: scope:[__start] from __start
+ to:__start::@return
+__start::@return: scope:[__start] from __start::@1
+ return
+ to:@return
+
+SYMBOL TABLE SSA
+__constant char * const SCREEN = (char *)$400
+void __start()
+__bank(cx16_ram, 1) char add(char a , char b)
+char add::$0
+char add::a
+char add::a#0
+char add::a#1
+char add::b
+char add::b#0
+char add::b#1
+char add::return
+char add::return#0
+char add::return#1
+char add::return#2
+char add::return#3
+char add::return#4
+void main()
+char main::$0
+char main::$1
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::$0
+char plus::a
+char plus::a#0
+char plus::a#1
+char plus::a#2
+char plus::b
+char plus::b#0
+char plus::b#1
+char plus::b#2
+char plus::return
+char plus::return#0
+char plus::return#1
+char plus::return#2
+char plus::return#3
+char plus::return#4
+char plus::return#5
+char plus::return#6
+
+Adding number conversion cast (unumber) 7 in plus::b#0 = 7
+Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
+Adding number conversion cast (unumber) 6 in plus::b#1 = 6
+Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast plus::b#0 = (unumber)7
+Inlining cast plus::b#1 = (unumber)6
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (char *) 1024
+Simplifying constant integer cast 7
+Simplifying constant integer cast 0
+Simplifying constant integer cast 6
+Simplifying constant integer cast 1
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (char) 7
+Finalized unsigned number type (char) 0
+Finalized unsigned number type (char) 6
+Finalized unsigned number type (char) 1
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Alias plus::return#0 = plus::return#4
+Alias plus::return#1 = plus::return#5
+Alias add::return#0 = add::return#3
+Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
+Alias add::return#1 = add::$0 add::return#4 add::return#2
+Successful SSA optimization Pass2AliasElimination
+Identical Phi Values add::a#1 add::a#0
+Identical Phi Values add::b#1 add::b#0
+Successful SSA optimization Pass2IdenticalPhiElimination
+Constant plus::a#0 = '0'
+Constant plus::b#0 = 7
+Constant plus::a#1 = '1'
+Constant plus::b#1 = 6
+Successful SSA optimization Pass2ConstantIdentification
+Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
+Successful SSA optimization PassNSimplifyExpressionWithZero
+Removing unused procedure __start
+Removing unused procedure block __start
+Removing unused procedure block __start::@1
+Removing unused procedure block __start::@return
+Successful SSA optimization PassNEliminateEmptyStart
+Inlining constant with var siblings plus::a#0
+Inlining constant with var siblings plus::b#0
+Inlining constant with var siblings plus::a#1
+Inlining constant with var siblings plus::b#1
+Constant inlined plus::b#1 = 6
+Constant inlined plus::b#0 = 7
+Constant inlined plus::a#1 = '1'
+Constant inlined plus::a#0 = '0'
+Successful SSA optimization Pass2ConstantInlining
+Consolidated array index constant in *(SCREEN+1)
+Successful SSA optimization Pass2ConstantAdditionElimination
+Adding NOP phi() at start of main
+CALL GRAPH
+Calls in [main] to plus:1 plus:5
+Calls in [plus] to add:13
+
+Created 2 initial phi equivalence classes
+Coalesced down to 2 phi equivalence classes
+Adding NOP phi() at start of main
+
+FINAL CONTROL FLOW GRAPH
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+__bank(cx16_ram, 1) char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+__bank(cx16_ram, 1) char add(char a , char b)
+char add::a
+char add::a#0 // 56.0
+char add::b
+char add::b#0 // 112.0
+char add::return
+char add::return#0 // 22.0
+char add::return#1 // 37.33333333333333
+void main()
+char main::$0 // 4.0
+char main::$1 // 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // 11.0
+char plus::b
+char plus::b#2 // 5.5
+char plus::return
+char plus::return#0 // 4.0
+char plus::return#1 // 4.0
+char plus::return#2 // 3.75
+
+Initial phi equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
+Added variable main::$0 to live range equivalence class [ main::$0 ]
+Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
+Added variable main::$1 to live range equivalence class [ main::$1 ]
+Added variable add::a#0 to live range equivalence class [ add::a#0 ]
+Added variable add::b#0 to live range equivalence class [ add::b#0 ]
+Added variable add::return#0 to live range equivalence class [ add::return#0 ]
+Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
+Added variable add::return#1 to live range equivalence class [ add::return#1 ]
+Complete equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+[ plus::return#0 ]
+[ main::$0 ]
+[ plus::return#1 ]
+[ main::$1 ]
+[ add::a#0 ]
+[ add::b#0 ]
+[ add::return#0 ]
+[ plus::return#2 ]
+[ add::return#1 ]
+Allocated zp[1]:2 [ add::b#0 ]
+Allocated zp[1]:3 [ add::a#0 ]
+Allocated zp[1]:4 [ add::return#1 ]
+Allocated zp[1]:5 [ add::return#0 ]
+Allocated zp[1]:6 [ plus::a#2 ]
+Allocated zp[1]:7 [ plus::b#2 ]
+Allocated zp[1]:8 [ plus::return#0 ]
+Allocated zp[1]:9 [ main::$0 ]
+Allocated zp[1]:10 [ plus::return#1 ]
+Allocated zp[1]:11 [ main::$1 ]
+Allocated zp[1]:12 [ plus::return#2 ]
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ]
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y ,
+Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
+Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
+Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
+Uplift Scope []
+
+Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ]
+Limited combination testing to 100 combinations of 256 possible.
+Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
+Limited combination testing to 100 combinations of 768 possible.
+Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
+Uplifting [] best 149 combination
+Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
+Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ]
+Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
+Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ]
+Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
+
+ASSEMBLER BEFORE OPTIMIZATION
+ // File Comments
+/**
+ * @file call-banked-phi-case-4-near-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #4.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-4-near-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ plus_from_main:
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b1
+ // main::@1
+ __b1:
+ // [3] main::$0 = plus::return#0
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ plus_from___b1:
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b2
+ // main::@2
+ __b2:
+ // [7] main::$1 = plus::return#1
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ jmp __breturn
+ // main::@return
+ __breturn:
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
+ sta.z add.a
+ // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
+ txa
+ // [13] call add
+ jsr add
+ // [14] add::return#0 = add::return#1
+ jmp __b1
+ // plus::@1
+ __b1:
+ // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
+ tax
+ jmp __breturn
+ // plus::@return
+ __breturn:
+ // [16] return
+ rts
+}
+ // add
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_ram, 1)
+add: {
+ .label a = 2
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ jmp __breturn
+ // add::@return
+ __breturn:
+ // [18] return
+ rts
+}
+ // File Data
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp __b1
+Removing instruction jmp __b2
+Removing instruction jmp __breturn
+Removing instruction jmp __b1
+Removing instruction jmp __breturn
+Removing instruction jmp __breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction plus_from_main:
+Removing instruction __b1:
+Removing instruction plus_from___b1:
+Removing instruction __b2:
+Removing instruction __breturn:
+Removing instruction __b1:
+Removing instruction __breturn:
+Removing instruction __breturn:
+Succesful ASM optimization Pass5UnusedLabelElimination
+
+FINAL SYMBOL TABLE
+__constant char * const SCREEN = (char *) 1024
+__bank(cx16_ram, 1) char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte a 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte x [ plus::return#2 ]
+reg byte a [ add::return#1 ]
+
+
+FINAL ASSEMBLER
+Score: 128
+
+ // File Comments
+/**
+ * @file call-banked-phi-case-4-near-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #4.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-4-near-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // plus('0', 7)
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@1
+ // [3] main::$0 = plus::return#0
+ // SCREEN[0] = plus('0', 7)
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // plus('1', 6)
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@2
+ // [7] main::$1 = plus::return#1
+ // SCREEN[1] = plus('1', 6)
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ // main::@return
+ // }
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // add(a, b)
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
+ sta.z add.a
+ // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
+ txa
+ // [13] call add
+ jsr add
+ // [14] add::return#0 = add::return#1
+ // plus::@1
+ // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
+ tax
+ // plus::@return
+ // }
+ // [16] return
+ rts
+}
+ // add
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_ram, 1)
+add: {
+ .label a = 2
+ // a+b
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ // add::@return
+ // }
+ // [18] return
+ rts
+}
+ // File Data
+
diff --git a/src/test/ref/call-banked-phi-case-4-near-1.sym b/src/test/ref/call-banked-phi-case-4-near-1.sym
new file mode 100644
index 000000000..4267f6a35
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-4-near-1.sym
@@ -0,0 +1,33 @@
+__constant char * const SCREEN = (char *) 1024
+__bank(cx16_ram, 1) char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte a 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte x [ plus::return#2 ]
+reg byte a [ add::return#1 ]
diff --git a/src/test/ref/call-banked-phi-case-5-far-0.asm b/src/test/ref/call-banked-phi-case-5-far-0.asm
new file mode 100644
index 000000000..c8603afdc
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-5-far-0.asm
@@ -0,0 +1,110 @@
+/**
+ * @file call-banked-phi-case-5-far-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #5.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ .file [name="call-banked-phi-case-5-far-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ .label SCREEN = $400
+.segment Code
+main: {
+ // plus('0', 7)
+ ldx #7
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ txa
+ // SCREEN[0] = plus('0', 7)
+ sta SCREEN
+ // plus('1', 6)
+ ldx #6
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ txa
+ // SCREEN[1] = plus('1', 6)
+ // close call
+ sta SCREEN+1
+ // }
+ rts
+}
+.segment RAM_Bank1
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // add(a, b)
+ sta.z add.a
+ txa
+ jsr $ff6e
+ .byte add
+ .byte 2
+ tax
+ // }
+ rts
+}
+.segment RAM_Bank2
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_ram, 2)
+add: {
+ .label a = 2
+ // a+b
+ clc
+ adc.z a
+ // }
+ rts
+}
diff --git a/src/test/ref/call-banked-phi-case-5-far-0.cfg b/src/test/ref/call-banked-phi-case-5-far-0.cfg
new file mode 100644
index 000000000..03dfaf36c
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-5-far-0.cfg
@@ -0,0 +1,44 @@
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+__bank(cx16_ram, 2) char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
diff --git a/src/test/ref/call-banked-phi-case-5-far-0.log b/src/test/ref/call-banked-phi-case-5-far-0.log
new file mode 100644
index 000000000..d1d83e6e3
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-5-far-0.log
@@ -0,0 +1,668 @@
+Loading link script "call-banked-phi.ld"
+
+CONTROL FLOW GRAPH SSA
+
+void main()
+main: scope:[main] from __start
+ plus::a#0 = '0'
+ plus::b#0 = 7
+ call plus
+ plus::return#0 = plus::return#3
+ to:main::@1
+main::@1: scope:[main] from main
+ plus::return#4 = phi( main/plus::return#0 )
+ main::$0 = plus::return#4
+ SCREEN[0] = main::$0
+ plus::a#1 = '1'
+ plus::b#1 = 6
+ call plus
+ plus::return#1 = plus::return#3
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ plus::return#5 = phi( main::@1/plus::return#1 )
+ main::$1 = plus::return#5
+ SCREEN[1] = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
+ plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
+ add::a#0 = plus::a#2
+ add::b#0 = plus::b#2
+ call add
+ add::return#0 = add::return#2
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ add::return#3 = phi( plus/add::return#0 )
+ plus::$0 = add::return#3
+ plus::return#2 = plus::$0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ plus::return#6 = phi( plus::@1/plus::return#2 )
+ plus::return#3 = plus::return#6
+ return
+ to:@return
+
+__bank(cx16_ram, 2) char add(char a , char b)
+add: scope:[add] from plus
+ add::b#1 = phi( plus/add::b#0 )
+ add::a#1 = phi( plus/add::a#0 )
+ add::$0 = add::a#1 + add::b#1
+ add::return#1 = add::$0
+ to:add::@return
+add::@return: scope:[add] from add
+ add::return#4 = phi( add/add::return#1 )
+ add::return#2 = add::return#4
+ return
+ to:@return
+
+void __start()
+__start: scope:[__start] from
+ call main
+ to:__start::@1
+__start::@1: scope:[__start] from __start
+ to:__start::@return
+__start::@return: scope:[__start] from __start::@1
+ return
+ to:@return
+
+SYMBOL TABLE SSA
+__constant char * const SCREEN = (char *)$400
+void __start()
+__bank(cx16_ram, 2) char add(char a , char b)
+char add::$0
+char add::a
+char add::a#0
+char add::a#1
+char add::b
+char add::b#0
+char add::b#1
+char add::return
+char add::return#0
+char add::return#1
+char add::return#2
+char add::return#3
+char add::return#4
+void main()
+char main::$0
+char main::$1
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::$0
+char plus::a
+char plus::a#0
+char plus::a#1
+char plus::a#2
+char plus::b
+char plus::b#0
+char plus::b#1
+char plus::b#2
+char plus::return
+char plus::return#0
+char plus::return#1
+char plus::return#2
+char plus::return#3
+char plus::return#4
+char plus::return#5
+char plus::return#6
+
+Adding number conversion cast (unumber) 7 in plus::b#0 = 7
+Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
+Adding number conversion cast (unumber) 6 in plus::b#1 = 6
+Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast plus::b#0 = (unumber)7
+Inlining cast plus::b#1 = (unumber)6
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (char *) 1024
+Simplifying constant integer cast 7
+Simplifying constant integer cast 0
+Simplifying constant integer cast 6
+Simplifying constant integer cast 1
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (char) 7
+Finalized unsigned number type (char) 0
+Finalized unsigned number type (char) 6
+Finalized unsigned number type (char) 1
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Alias plus::return#0 = plus::return#4
+Alias plus::return#1 = plus::return#5
+Alias add::return#0 = add::return#3
+Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
+Alias add::return#1 = add::$0 add::return#4 add::return#2
+Successful SSA optimization Pass2AliasElimination
+Identical Phi Values add::a#1 add::a#0
+Identical Phi Values add::b#1 add::b#0
+Successful SSA optimization Pass2IdenticalPhiElimination
+Constant plus::a#0 = '0'
+Constant plus::b#0 = 7
+Constant plus::a#1 = '1'
+Constant plus::b#1 = 6
+Successful SSA optimization Pass2ConstantIdentification
+Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
+Successful SSA optimization PassNSimplifyExpressionWithZero
+Removing unused procedure __start
+Removing unused procedure block __start
+Removing unused procedure block __start::@1
+Removing unused procedure block __start::@return
+Successful SSA optimization PassNEliminateEmptyStart
+Inlining constant with var siblings plus::a#0
+Inlining constant with var siblings plus::b#0
+Inlining constant with var siblings plus::a#1
+Inlining constant with var siblings plus::b#1
+Constant inlined plus::b#1 = 6
+Constant inlined plus::b#0 = 7
+Constant inlined plus::a#1 = '1'
+Constant inlined plus::a#0 = '0'
+Successful SSA optimization Pass2ConstantInlining
+Consolidated array index constant in *(SCREEN+1)
+Successful SSA optimization Pass2ConstantAdditionElimination
+Adding NOP phi() at start of main
+CALL GRAPH
+Calls in [main] to plus:1 plus:5
+Calls in [plus] to add:13
+
+Created 2 initial phi equivalence classes
+Coalesced down to 2 phi equivalence classes
+Adding NOP phi() at start of main
+
+FINAL CONTROL FLOW GRAPH
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+__bank(cx16_ram, 2) char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+__bank(cx16_ram, 2) char add(char a , char b)
+char add::a
+char add::a#0 // 56.0
+char add::b
+char add::b#0 // 112.0
+char add::return
+char add::return#0 // 22.0
+char add::return#1 // 37.33333333333333
+void main()
+char main::$0 // 4.0
+char main::$1 // 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // 11.0
+char plus::b
+char plus::b#2 // 5.5
+char plus::return
+char plus::return#0 // 4.0
+char plus::return#1 // 4.0
+char plus::return#2 // 3.75
+
+Initial phi equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
+Added variable main::$0 to live range equivalence class [ main::$0 ]
+Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
+Added variable main::$1 to live range equivalence class [ main::$1 ]
+Added variable add::a#0 to live range equivalence class [ add::a#0 ]
+Added variable add::b#0 to live range equivalence class [ add::b#0 ]
+Added variable add::return#0 to live range equivalence class [ add::return#0 ]
+Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
+Added variable add::return#1 to live range equivalence class [ add::return#1 ]
+Complete equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+[ plus::return#0 ]
+[ main::$0 ]
+[ plus::return#1 ]
+[ main::$1 ]
+[ add::a#0 ]
+[ add::b#0 ]
+[ add::return#0 ]
+[ plus::return#2 ]
+[ add::return#1 ]
+Allocated zp[1]:2 [ add::b#0 ]
+Allocated zp[1]:3 [ add::a#0 ]
+Allocated zp[1]:4 [ add::return#1 ]
+Allocated zp[1]:5 [ add::return#0 ]
+Allocated zp[1]:6 [ plus::a#2 ]
+Allocated zp[1]:7 [ plus::b#2 ]
+Allocated zp[1]:8 [ plus::return#0 ]
+Allocated zp[1]:9 [ main::$0 ]
+Allocated zp[1]:10 [ plus::return#1 ]
+Allocated zp[1]:11 [ main::$1 ]
+Allocated zp[1]:12 [ plus::return#2 ]
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ]
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y ,
+Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
+Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
+Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
+Uplift Scope []
+
+Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ]
+Limited combination testing to 100 combinations of 256 possible.
+Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
+Limited combination testing to 100 combinations of 768 possible.
+Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
+Uplifting [] best 149 combination
+Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
+Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ]
+Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
+Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ]
+Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
+
+ASSEMBLER BEFORE OPTIMIZATION
+ // File Comments
+/**
+ * @file call-banked-phi-case-5-far-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #5.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-5-far-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ plus_from_main:
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b1
+ // main::@1
+ __b1:
+ // [3] main::$0 = plus::return#0
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ plus_from___b1:
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b2
+ // main::@2
+ __b2:
+ // [7] main::$1 = plus::return#1
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ jmp __breturn
+ // main::@return
+ __breturn:
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
+ sta.z add.a
+ // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
+ txa
+ // [13] call add -- call_phi_far_cx16_ram
+ jsr $ff6e
+ .byte add
+ .byte 2
+ // [14] add::return#0 = add::return#1
+ jmp __b1
+ // plus::@1
+ __b1:
+ // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
+ tax
+ jmp __breturn
+ // plus::@return
+ __breturn:
+ // [16] return
+ rts
+}
+.segment RAM_Bank2
+ // add
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_ram, 2)
+add: {
+ .label a = 2
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ jmp __breturn
+ // add::@return
+ __breturn:
+ // [18] return
+ rts
+}
+ // File Data
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp __b1
+Removing instruction jmp __b2
+Removing instruction jmp __breturn
+Removing instruction jmp __b1
+Removing instruction jmp __breturn
+Removing instruction jmp __breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction plus_from_main:
+Removing instruction __b1:
+Removing instruction plus_from___b1:
+Removing instruction __b2:
+Removing instruction __breturn:
+Removing instruction __b1:
+Removing instruction __breturn:
+Removing instruction __breturn:
+Succesful ASM optimization Pass5UnusedLabelElimination
+
+FINAL SYMBOL TABLE
+__constant char * const SCREEN = (char *) 1024
+__bank(cx16_ram, 2) char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte a 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte x [ plus::return#2 ]
+reg byte a [ add::return#1 ]
+
+
+FINAL ASSEMBLER
+Score: 128
+
+ // File Comments
+/**
+ * @file call-banked-phi-case-5-far-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #5.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-5-far-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // plus('0', 7)
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@1
+ // [3] main::$0 = plus::return#0
+ // SCREEN[0] = plus('0', 7)
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // plus('1', 6)
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@2
+ // [7] main::$1 = plus::return#1
+ // SCREEN[1] = plus('1', 6)
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ // main::@return
+ // }
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // add(a, b)
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
+ sta.z add.a
+ // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
+ txa
+ // [13] call add -- call_phi_far_cx16_ram
+ jsr $ff6e
+ .byte add
+ .byte 2
+ // [14] add::return#0 = add::return#1
+ // plus::@1
+ // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
+ tax
+ // plus::@return
+ // }
+ // [16] return
+ rts
+}
+.segment RAM_Bank2
+ // add
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_ram, 2)
+add: {
+ .label a = 2
+ // a+b
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ // add::@return
+ // }
+ // [18] return
+ rts
+}
+ // File Data
+
diff --git a/src/test/ref/call-banked-phi-case-5-far-0.sym b/src/test/ref/call-banked-phi-case-5-far-0.sym
new file mode 100644
index 000000000..f777cf8ef
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-5-far-0.sym
@@ -0,0 +1,33 @@
+__constant char * const SCREEN = (char *) 1024
+__bank(cx16_ram, 2) char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte a 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte x [ plus::return#2 ]
+reg byte a [ add::return#1 ]
diff --git a/src/test/ref/call-banked-phi-case-5-far-1.asm b/src/test/ref/call-banked-phi-case-5-far-1.asm
new file mode 100644
index 000000000..93f4f13c5
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-5-far-1.asm
@@ -0,0 +1,110 @@
+/**
+ * @file call-banked-phi-case-5-far-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #5.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ .file [name="call-banked-phi-case-5-far-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ .label SCREEN = $400
+.segment Code
+main: {
+ // plus('0', 7)
+ ldx #7
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ txa
+ // SCREEN[0] = plus('0', 7)
+ sta SCREEN
+ // plus('1', 6)
+ ldx #6
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ txa
+ // SCREEN[1] = plus('1', 6)
+ // close call
+ sta SCREEN+1
+ // }
+ rts
+}
+.segment RAM_Bank1
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // add(a, b)
+ sta.z add.a
+ txa
+ jsr $ff6e
+ .byte add
+ .byte 2
+ tax
+ // }
+ rts
+}
+.segment RAM_Bank2
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_ram, 2)
+add: {
+ .label a = 2
+ // a+b
+ clc
+ adc.z a
+ // }
+ rts
+}
diff --git a/src/test/ref/call-banked-phi-case-5-far-1.cfg b/src/test/ref/call-banked-phi-case-5-far-1.cfg
new file mode 100644
index 000000000..03dfaf36c
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-5-far-1.cfg
@@ -0,0 +1,44 @@
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+__bank(cx16_ram, 2) char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
diff --git a/src/test/ref/call-banked-phi-case-5-far-1.log b/src/test/ref/call-banked-phi-case-5-far-1.log
new file mode 100644
index 000000000..8844e959f
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-5-far-1.log
@@ -0,0 +1,668 @@
+Loading link script "call-banked-phi.ld"
+
+CONTROL FLOW GRAPH SSA
+
+void main()
+main: scope:[main] from __start
+ plus::a#0 = '0'
+ plus::b#0 = 7
+ call plus
+ plus::return#0 = plus::return#3
+ to:main::@1
+main::@1: scope:[main] from main
+ plus::return#4 = phi( main/plus::return#0 )
+ main::$0 = plus::return#4
+ SCREEN[0] = main::$0
+ plus::a#1 = '1'
+ plus::b#1 = 6
+ call plus
+ plus::return#1 = plus::return#3
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ plus::return#5 = phi( main::@1/plus::return#1 )
+ main::$1 = plus::return#5
+ SCREEN[1] = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
+ plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
+ add::a#0 = plus::a#2
+ add::b#0 = plus::b#2
+ call add
+ add::return#0 = add::return#2
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ add::return#3 = phi( plus/add::return#0 )
+ plus::$0 = add::return#3
+ plus::return#2 = plus::$0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ plus::return#6 = phi( plus::@1/plus::return#2 )
+ plus::return#3 = plus::return#6
+ return
+ to:@return
+
+__bank(cx16_ram, 2) char add(char a , char b)
+add: scope:[add] from plus
+ add::b#1 = phi( plus/add::b#0 )
+ add::a#1 = phi( plus/add::a#0 )
+ add::$0 = add::a#1 + add::b#1
+ add::return#1 = add::$0
+ to:add::@return
+add::@return: scope:[add] from add
+ add::return#4 = phi( add/add::return#1 )
+ add::return#2 = add::return#4
+ return
+ to:@return
+
+void __start()
+__start: scope:[__start] from
+ call main
+ to:__start::@1
+__start::@1: scope:[__start] from __start
+ to:__start::@return
+__start::@return: scope:[__start] from __start::@1
+ return
+ to:@return
+
+SYMBOL TABLE SSA
+__constant char * const SCREEN = (char *)$400
+void __start()
+__bank(cx16_ram, 2) char add(char a , char b)
+char add::$0
+char add::a
+char add::a#0
+char add::a#1
+char add::b
+char add::b#0
+char add::b#1
+char add::return
+char add::return#0
+char add::return#1
+char add::return#2
+char add::return#3
+char add::return#4
+void main()
+char main::$0
+char main::$1
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::$0
+char plus::a
+char plus::a#0
+char plus::a#1
+char plus::a#2
+char plus::b
+char plus::b#0
+char plus::b#1
+char plus::b#2
+char plus::return
+char plus::return#0
+char plus::return#1
+char plus::return#2
+char plus::return#3
+char plus::return#4
+char plus::return#5
+char plus::return#6
+
+Adding number conversion cast (unumber) 7 in plus::b#0 = 7
+Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
+Adding number conversion cast (unumber) 6 in plus::b#1 = 6
+Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast plus::b#0 = (unumber)7
+Inlining cast plus::b#1 = (unumber)6
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (char *) 1024
+Simplifying constant integer cast 7
+Simplifying constant integer cast 0
+Simplifying constant integer cast 6
+Simplifying constant integer cast 1
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (char) 7
+Finalized unsigned number type (char) 0
+Finalized unsigned number type (char) 6
+Finalized unsigned number type (char) 1
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Alias plus::return#0 = plus::return#4
+Alias plus::return#1 = plus::return#5
+Alias add::return#0 = add::return#3
+Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
+Alias add::return#1 = add::$0 add::return#4 add::return#2
+Successful SSA optimization Pass2AliasElimination
+Identical Phi Values add::a#1 add::a#0
+Identical Phi Values add::b#1 add::b#0
+Successful SSA optimization Pass2IdenticalPhiElimination
+Constant plus::a#0 = '0'
+Constant plus::b#0 = 7
+Constant plus::a#1 = '1'
+Constant plus::b#1 = 6
+Successful SSA optimization Pass2ConstantIdentification
+Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
+Successful SSA optimization PassNSimplifyExpressionWithZero
+Removing unused procedure __start
+Removing unused procedure block __start
+Removing unused procedure block __start::@1
+Removing unused procedure block __start::@return
+Successful SSA optimization PassNEliminateEmptyStart
+Inlining constant with var siblings plus::a#0
+Inlining constant with var siblings plus::b#0
+Inlining constant with var siblings plus::a#1
+Inlining constant with var siblings plus::b#1
+Constant inlined plus::b#1 = 6
+Constant inlined plus::b#0 = 7
+Constant inlined plus::a#1 = '1'
+Constant inlined plus::a#0 = '0'
+Successful SSA optimization Pass2ConstantInlining
+Consolidated array index constant in *(SCREEN+1)
+Successful SSA optimization Pass2ConstantAdditionElimination
+Adding NOP phi() at start of main
+CALL GRAPH
+Calls in [main] to plus:1 plus:5
+Calls in [plus] to add:13
+
+Created 2 initial phi equivalence classes
+Coalesced down to 2 phi equivalence classes
+Adding NOP phi() at start of main
+
+FINAL CONTROL FLOW GRAPH
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+__bank(cx16_ram, 2) char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+__bank(cx16_ram, 2) char add(char a , char b)
+char add::a
+char add::a#0 // 56.0
+char add::b
+char add::b#0 // 112.0
+char add::return
+char add::return#0 // 22.0
+char add::return#1 // 37.33333333333333
+void main()
+char main::$0 // 4.0
+char main::$1 // 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // 11.0
+char plus::b
+char plus::b#2 // 5.5
+char plus::return
+char plus::return#0 // 4.0
+char plus::return#1 // 4.0
+char plus::return#2 // 3.75
+
+Initial phi equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
+Added variable main::$0 to live range equivalence class [ main::$0 ]
+Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
+Added variable main::$1 to live range equivalence class [ main::$1 ]
+Added variable add::a#0 to live range equivalence class [ add::a#0 ]
+Added variable add::b#0 to live range equivalence class [ add::b#0 ]
+Added variable add::return#0 to live range equivalence class [ add::return#0 ]
+Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
+Added variable add::return#1 to live range equivalence class [ add::return#1 ]
+Complete equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+[ plus::return#0 ]
+[ main::$0 ]
+[ plus::return#1 ]
+[ main::$1 ]
+[ add::a#0 ]
+[ add::b#0 ]
+[ add::return#0 ]
+[ plus::return#2 ]
+[ add::return#1 ]
+Allocated zp[1]:2 [ add::b#0 ]
+Allocated zp[1]:3 [ add::a#0 ]
+Allocated zp[1]:4 [ add::return#1 ]
+Allocated zp[1]:5 [ add::return#0 ]
+Allocated zp[1]:6 [ plus::a#2 ]
+Allocated zp[1]:7 [ plus::b#2 ]
+Allocated zp[1]:8 [ plus::return#0 ]
+Allocated zp[1]:9 [ main::$0 ]
+Allocated zp[1]:10 [ plus::return#1 ]
+Allocated zp[1]:11 [ main::$1 ]
+Allocated zp[1]:12 [ plus::return#2 ]
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ]
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y ,
+Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
+Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
+Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
+Uplift Scope []
+
+Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ]
+Limited combination testing to 100 combinations of 256 possible.
+Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
+Limited combination testing to 100 combinations of 768 possible.
+Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
+Uplifting [] best 149 combination
+Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
+Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ]
+Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
+Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ]
+Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
+
+ASSEMBLER BEFORE OPTIMIZATION
+ // File Comments
+/**
+ * @file call-banked-phi-case-5-far-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #5.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-5-far-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ plus_from_main:
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b1
+ // main::@1
+ __b1:
+ // [3] main::$0 = plus::return#0
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ plus_from___b1:
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b2
+ // main::@2
+ __b2:
+ // [7] main::$1 = plus::return#1
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ jmp __breturn
+ // main::@return
+ __breturn:
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
+ sta.z add.a
+ // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
+ txa
+ // [13] call add -- call_phi_far_cx16_ram
+ jsr $ff6e
+ .byte add
+ .byte 2
+ // [14] add::return#0 = add::return#1
+ jmp __b1
+ // plus::@1
+ __b1:
+ // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
+ tax
+ jmp __breturn
+ // plus::@return
+ __breturn:
+ // [16] return
+ rts
+}
+.segment RAM_Bank2
+ // add
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_ram, 2)
+add: {
+ .label a = 2
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ jmp __breturn
+ // add::@return
+ __breturn:
+ // [18] return
+ rts
+}
+ // File Data
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp __b1
+Removing instruction jmp __b2
+Removing instruction jmp __breturn
+Removing instruction jmp __b1
+Removing instruction jmp __breturn
+Removing instruction jmp __breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction plus_from_main:
+Removing instruction __b1:
+Removing instruction plus_from___b1:
+Removing instruction __b2:
+Removing instruction __breturn:
+Removing instruction __b1:
+Removing instruction __breturn:
+Removing instruction __breturn:
+Succesful ASM optimization Pass5UnusedLabelElimination
+
+FINAL SYMBOL TABLE
+__constant char * const SCREEN = (char *) 1024
+__bank(cx16_ram, 2) char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte a 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte x [ plus::return#2 ]
+reg byte a [ add::return#1 ]
+
+
+FINAL ASSEMBLER
+Score: 128
+
+ // File Comments
+/**
+ * @file call-banked-phi-case-5-far-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #5.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-5-far-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // plus('0', 7)
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@1
+ // [3] main::$0 = plus::return#0
+ // SCREEN[0] = plus('0', 7)
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // plus('1', 6)
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@2
+ // [7] main::$1 = plus::return#1
+ // SCREEN[1] = plus('1', 6)
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ // main::@return
+ // }
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // add(a, b)
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
+ sta.z add.a
+ // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
+ txa
+ // [13] call add -- call_phi_far_cx16_ram
+ jsr $ff6e
+ .byte add
+ .byte 2
+ // [14] add::return#0 = add::return#1
+ // plus::@1
+ // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
+ tax
+ // plus::@return
+ // }
+ // [16] return
+ rts
+}
+.segment RAM_Bank2
+ // add
+// __register(A) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_ram, 2)
+add: {
+ .label a = 2
+ // a+b
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ // add::@return
+ // }
+ // [18] return
+ rts
+}
+ // File Data
+
diff --git a/src/test/ref/call-banked-phi-case-5-far-1.sym b/src/test/ref/call-banked-phi-case-5-far-1.sym
new file mode 100644
index 000000000..f777cf8ef
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-5-far-1.sym
@@ -0,0 +1,33 @@
+__constant char * const SCREEN = (char *) 1024
+__bank(cx16_ram, 2) char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte a 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte x [ plus::return#2 ]
+reg byte a [ add::return#1 ]
diff --git a/src/test/ref/call-banked-phi-case-6-close-0.asm b/src/test/ref/call-banked-phi-case-6-close-0.asm
new file mode 100644
index 000000000..160ff45fa
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-6-close-0.asm
@@ -0,0 +1,119 @@
+/**
+ * @file call-banked-phi-case-6-close-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #6.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ .file [name="call-banked-phi-case-6-close-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ .label SCREEN = $400
+.segment Code
+main: {
+ // plus('0', 7)
+ ldx #7
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ txa
+ // SCREEN[0] = plus('0', 7)
+ sta SCREEN
+ // plus('1', 6)
+ ldx #6
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ txa
+ // SCREEN[1] = plus('1', 6)
+ // close call
+ sta SCREEN+1
+ // }
+ rts
+}
+.segment RAM_Bank1
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // add(a, b)
+ sta.z add.a
+ txa
+ sta.z $ff
+ lda.z 1
+ pha
+ lda #1
+ sta.z 1
+ lda.z $ff
+ jsr add
+ sta.z $ff
+ pla
+ sta.z 1
+ lda.z $ff
+ txa
+ tax
+ // }
+ rts
+}
+.segment ROM_Bank1
+// __register(X) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_rom, 1)
+add: {
+ .label a = 2
+ // a+b
+ clc
+ adc.z a
+ tax
+ // }
+ rts
+}
diff --git a/src/test/ref/call-banked-phi-case-6-close-0.cfg b/src/test/ref/call-banked-phi-case-6-close-0.cfg
new file mode 100644
index 000000000..c10c5c2f7
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-6-close-0.cfg
@@ -0,0 +1,44 @@
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+__bank(cx16_rom, 1) char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
diff --git a/src/test/ref/call-banked-phi-case-6-close-0.log b/src/test/ref/call-banked-phi-case-6-close-0.log
new file mode 100644
index 000000000..b81bf7d9a
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-6-close-0.log
@@ -0,0 +1,689 @@
+Loading link script "call-banked-phi.ld"
+
+CONTROL FLOW GRAPH SSA
+
+void main()
+main: scope:[main] from __start
+ plus::a#0 = '0'
+ plus::b#0 = 7
+ call plus
+ plus::return#0 = plus::return#3
+ to:main::@1
+main::@1: scope:[main] from main
+ plus::return#4 = phi( main/plus::return#0 )
+ main::$0 = plus::return#4
+ SCREEN[0] = main::$0
+ plus::a#1 = '1'
+ plus::b#1 = 6
+ call plus
+ plus::return#1 = plus::return#3
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ plus::return#5 = phi( main::@1/plus::return#1 )
+ main::$1 = plus::return#5
+ SCREEN[1] = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
+ plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
+ add::a#0 = plus::a#2
+ add::b#0 = plus::b#2
+ call add
+ add::return#0 = add::return#2
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ add::return#3 = phi( plus/add::return#0 )
+ plus::$0 = add::return#3
+ plus::return#2 = plus::$0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ plus::return#6 = phi( plus::@1/plus::return#2 )
+ plus::return#3 = plus::return#6
+ return
+ to:@return
+
+__bank(cx16_rom, 1) char add(char a , char b)
+add: scope:[add] from plus
+ add::b#1 = phi( plus/add::b#0 )
+ add::a#1 = phi( plus/add::a#0 )
+ add::$0 = add::a#1 + add::b#1
+ add::return#1 = add::$0
+ to:add::@return
+add::@return: scope:[add] from add
+ add::return#4 = phi( add/add::return#1 )
+ add::return#2 = add::return#4
+ return
+ to:@return
+
+void __start()
+__start: scope:[__start] from
+ call main
+ to:__start::@1
+__start::@1: scope:[__start] from __start
+ to:__start::@return
+__start::@return: scope:[__start] from __start::@1
+ return
+ to:@return
+
+SYMBOL TABLE SSA
+__constant char * const SCREEN = (char *)$400
+void __start()
+__bank(cx16_rom, 1) char add(char a , char b)
+char add::$0
+char add::a
+char add::a#0
+char add::a#1
+char add::b
+char add::b#0
+char add::b#1
+char add::return
+char add::return#0
+char add::return#1
+char add::return#2
+char add::return#3
+char add::return#4
+void main()
+char main::$0
+char main::$1
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::$0
+char plus::a
+char plus::a#0
+char plus::a#1
+char plus::a#2
+char plus::b
+char plus::b#0
+char plus::b#1
+char plus::b#2
+char plus::return
+char plus::return#0
+char plus::return#1
+char plus::return#2
+char plus::return#3
+char plus::return#4
+char plus::return#5
+char plus::return#6
+
+Adding number conversion cast (unumber) 7 in plus::b#0 = 7
+Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
+Adding number conversion cast (unumber) 6 in plus::b#1 = 6
+Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast plus::b#0 = (unumber)7
+Inlining cast plus::b#1 = (unumber)6
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (char *) 1024
+Simplifying constant integer cast 7
+Simplifying constant integer cast 0
+Simplifying constant integer cast 6
+Simplifying constant integer cast 1
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (char) 7
+Finalized unsigned number type (char) 0
+Finalized unsigned number type (char) 6
+Finalized unsigned number type (char) 1
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Alias plus::return#0 = plus::return#4
+Alias plus::return#1 = plus::return#5
+Alias add::return#0 = add::return#3
+Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
+Alias add::return#1 = add::$0 add::return#4 add::return#2
+Successful SSA optimization Pass2AliasElimination
+Identical Phi Values add::a#1 add::a#0
+Identical Phi Values add::b#1 add::b#0
+Successful SSA optimization Pass2IdenticalPhiElimination
+Constant plus::a#0 = '0'
+Constant plus::b#0 = 7
+Constant plus::a#1 = '1'
+Constant plus::b#1 = 6
+Successful SSA optimization Pass2ConstantIdentification
+Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
+Successful SSA optimization PassNSimplifyExpressionWithZero
+Removing unused procedure __start
+Removing unused procedure block __start
+Removing unused procedure block __start::@1
+Removing unused procedure block __start::@return
+Successful SSA optimization PassNEliminateEmptyStart
+Inlining constant with var siblings plus::a#0
+Inlining constant with var siblings plus::b#0
+Inlining constant with var siblings plus::a#1
+Inlining constant with var siblings plus::b#1
+Constant inlined plus::b#1 = 6
+Constant inlined plus::b#0 = 7
+Constant inlined plus::a#1 = '1'
+Constant inlined plus::a#0 = '0'
+Successful SSA optimization Pass2ConstantInlining
+Consolidated array index constant in *(SCREEN+1)
+Successful SSA optimization Pass2ConstantAdditionElimination
+Adding NOP phi() at start of main
+CALL GRAPH
+Calls in [main] to plus:1 plus:5
+Calls in [plus] to add:13
+
+Created 2 initial phi equivalence classes
+Coalesced down to 2 phi equivalence classes
+Adding NOP phi() at start of main
+
+FINAL CONTROL FLOW GRAPH
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+__bank(cx16_rom, 1) char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+__bank(cx16_rom, 1) char add(char a , char b)
+char add::a
+char add::a#0 // 56.0
+char add::b
+char add::b#0 // 112.0
+char add::return
+char add::return#0 // 22.0
+char add::return#1 // 37.33333333333333
+void main()
+char main::$0 // 4.0
+char main::$1 // 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // 11.0
+char plus::b
+char plus::b#2 // 5.5
+char plus::return
+char plus::return#0 // 4.0
+char plus::return#1 // 4.0
+char plus::return#2 // 3.75
+
+Initial phi equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
+Added variable main::$0 to live range equivalence class [ main::$0 ]
+Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
+Added variable main::$1 to live range equivalence class [ main::$1 ]
+Added variable add::a#0 to live range equivalence class [ add::a#0 ]
+Added variable add::b#0 to live range equivalence class [ add::b#0 ]
+Added variable add::return#0 to live range equivalence class [ add::return#0 ]
+Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
+Added variable add::return#1 to live range equivalence class [ add::return#1 ]
+Complete equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+[ plus::return#0 ]
+[ main::$0 ]
+[ plus::return#1 ]
+[ main::$1 ]
+[ add::a#0 ]
+[ add::b#0 ]
+[ add::return#0 ]
+[ plus::return#2 ]
+[ add::return#1 ]
+Allocated zp[1]:2 [ add::b#0 ]
+Allocated zp[1]:3 [ add::a#0 ]
+Allocated zp[1]:4 [ add::return#1 ]
+Allocated zp[1]:5 [ add::return#0 ]
+Allocated zp[1]:6 [ plus::a#2 ]
+Allocated zp[1]:7 [ plus::b#2 ]
+Allocated zp[1]:8 [ plus::return#0 ]
+Allocated zp[1]:9 [ main::$0 ]
+Allocated zp[1]:10 [ plus::return#1 ]
+Allocated zp[1]:11 [ main::$1 ]
+Allocated zp[1]:12 [ plus::return#2 ]
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ]
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [13] call add [ add::return#1 ] ( plus:1 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Removing always clobbered register reg byte a as potential for zp[1]:4 [ add::return#1 ]
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [13] call add [ add::return#1 ] ( plus:1 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y ,
+Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte x , reg byte y ,
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
+Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
+Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
+Uplift Scope []
+
+Uplifting [add] best 223 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte x [ add::return#1 ] reg byte a [ add::return#0 ]
+Limited combination testing to 100 combinations of 192 possible.
+Uplifting [plus] best 195 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
+Limited combination testing to 100 combinations of 768 possible.
+Uplifting [main] best 183 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
+Uplifting [] best 183 combination
+Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
+Uplifting [add] best 183 combination zp[1]:3 [ add::a#0 ]
+Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
+Uplifting [plus] best 180 combination reg byte x [ plus::return#2 ]
+Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
+
+ASSEMBLER BEFORE OPTIMIZATION
+ // File Comments
+/**
+ * @file call-banked-phi-case-6-close-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #6.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-6-close-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ plus_from_main:
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b1
+ // main::@1
+ __b1:
+ // [3] main::$0 = plus::return#0
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ plus_from___b1:
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b2
+ // main::@2
+ __b2:
+ // [7] main::$1 = plus::return#1
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ jmp __breturn
+ // main::@return
+ __breturn:
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
+ sta.z add.a
+ // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
+ txa
+ // [13] call add -- call_phi_close_cx16_rom
+ sta.z $ff
+ lda.z 1
+ pha
+ lda #1
+ sta.z 1
+ lda.z $ff
+ jsr add
+ sta.z $ff
+ pla
+ sta.z 1
+ lda.z $ff
+ // [14] add::return#0 = add::return#1 -- vbuaa=vbuxx
+ txa
+ jmp __b1
+ // plus::@1
+ __b1:
+ // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
+ tax
+ jmp __breturn
+ // plus::@return
+ __breturn:
+ // [16] return
+ rts
+}
+.segment ROM_Bank1
+ // add
+// __register(X) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_rom, 1)
+add: {
+ .label a = 2
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuxx=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ tax
+ jmp __breturn
+ // add::@return
+ __breturn:
+ // [18] return
+ rts
+}
+ // File Data
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp __b1
+Removing instruction jmp __b2
+Removing instruction jmp __breturn
+Removing instruction jmp __b1
+Removing instruction jmp __breturn
+Removing instruction jmp __breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction plus_from_main:
+Removing instruction __b1:
+Removing instruction plus_from___b1:
+Removing instruction __b2:
+Removing instruction __breturn:
+Removing instruction __b1:
+Removing instruction __breturn:
+Removing instruction __breturn:
+Succesful ASM optimization Pass5UnusedLabelElimination
+
+FINAL SYMBOL TABLE
+__constant char * const SCREEN = (char *) 1024
+__bank(cx16_rom, 1) char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte x 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte x [ plus::return#2 ]
+reg byte x [ add::return#1 ]
+
+
+FINAL ASSEMBLER
+Score: 162
+
+ // File Comments
+/**
+ * @file call-banked-phi-case-6-close-0.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #6.
+ * Implementation using the __bank() directive.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-6-close-0.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // plus('0', 7)
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@1
+ // [3] main::$0 = plus::return#0
+ // SCREEN[0] = plus('0', 7)
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // plus('1', 6)
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@2
+ // [7] main::$1 = plus::return#1
+ // SCREEN[1] = plus('1', 6)
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ // main::@return
+ // }
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // add(a, b)
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
+ sta.z add.a
+ // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
+ txa
+ // [13] call add -- call_phi_close_cx16_rom
+ sta.z $ff
+ lda.z 1
+ pha
+ lda #1
+ sta.z 1
+ lda.z $ff
+ jsr add
+ sta.z $ff
+ pla
+ sta.z 1
+ lda.z $ff
+ // [14] add::return#0 = add::return#1 -- vbuaa=vbuxx
+ txa
+ // plus::@1
+ // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
+ tax
+ // plus::@return
+ // }
+ // [16] return
+ rts
+}
+.segment ROM_Bank1
+ // add
+// __register(X) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_rom, 1)
+add: {
+ .label a = 2
+ // a+b
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuxx=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ tax
+ // add::@return
+ // }
+ // [18] return
+ rts
+}
+ // File Data
+
diff --git a/src/test/ref/call-banked-phi-case-6-close-0.sym b/src/test/ref/call-banked-phi-case-6-close-0.sym
new file mode 100644
index 000000000..26eed0f1c
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-6-close-0.sym
@@ -0,0 +1,33 @@
+__constant char * const SCREEN = (char *) 1024
+__bank(cx16_rom, 1) char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte x 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte x [ plus::return#2 ]
+reg byte x [ add::return#1 ]
diff --git a/src/test/ref/call-banked-phi-case-6-close-1.asm b/src/test/ref/call-banked-phi-case-6-close-1.asm
new file mode 100644
index 000000000..89f99c527
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-6-close-1.asm
@@ -0,0 +1,119 @@
+/**
+ * @file call-banked-phi-case-6-close-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #6.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ .file [name="call-banked-phi-case-6-close-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ .label SCREEN = $400
+.segment Code
+main: {
+ // plus('0', 7)
+ ldx #7
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ txa
+ // SCREEN[0] = plus('0', 7)
+ sta SCREEN
+ // plus('1', 6)
+ ldx #6
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ txa
+ // SCREEN[1] = plus('1', 6)
+ // close call
+ sta SCREEN+1
+ // }
+ rts
+}
+.segment RAM_Bank1
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // add(a, b)
+ sta.z add.a
+ txa
+ sta.z $ff
+ lda.z 1
+ pha
+ lda #1
+ sta.z 1
+ lda.z $ff
+ jsr add
+ sta.z $ff
+ pla
+ sta.z 1
+ lda.z $ff
+ txa
+ tax
+ // }
+ rts
+}
+.segment ROM_Bank1
+// __register(X) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_rom, 1)
+add: {
+ .label a = 2
+ // a+b
+ clc
+ adc.z a
+ tax
+ // }
+ rts
+}
diff --git a/src/test/ref/call-banked-phi-case-6-close-1.cfg b/src/test/ref/call-banked-phi-case-6-close-1.cfg
new file mode 100644
index 000000000..c10c5c2f7
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-6-close-1.cfg
@@ -0,0 +1,44 @@
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+__bank(cx16_rom, 1) char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
diff --git a/src/test/ref/call-banked-phi-case-6-close-1.log b/src/test/ref/call-banked-phi-case-6-close-1.log
new file mode 100644
index 000000000..c64b16fba
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-6-close-1.log
@@ -0,0 +1,689 @@
+Loading link script "call-banked-phi.ld"
+
+CONTROL FLOW GRAPH SSA
+
+void main()
+main: scope:[main] from __start
+ plus::a#0 = '0'
+ plus::b#0 = 7
+ call plus
+ plus::return#0 = plus::return#3
+ to:main::@1
+main::@1: scope:[main] from main
+ plus::return#4 = phi( main/plus::return#0 )
+ main::$0 = plus::return#4
+ SCREEN[0] = main::$0
+ plus::a#1 = '1'
+ plus::b#1 = 6
+ call plus
+ plus::return#1 = plus::return#3
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ plus::return#5 = phi( main::@1/plus::return#1 )
+ main::$1 = plus::return#5
+ SCREEN[1] = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
+ plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
+ add::a#0 = plus::a#2
+ add::b#0 = plus::b#2
+ call add
+ add::return#0 = add::return#2
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ add::return#3 = phi( plus/add::return#0 )
+ plus::$0 = add::return#3
+ plus::return#2 = plus::$0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ plus::return#6 = phi( plus::@1/plus::return#2 )
+ plus::return#3 = plus::return#6
+ return
+ to:@return
+
+__bank(cx16_rom, 1) char add(char a , char b)
+add: scope:[add] from plus
+ add::b#1 = phi( plus/add::b#0 )
+ add::a#1 = phi( plus/add::a#0 )
+ add::$0 = add::a#1 + add::b#1
+ add::return#1 = add::$0
+ to:add::@return
+add::@return: scope:[add] from add
+ add::return#4 = phi( add/add::return#1 )
+ add::return#2 = add::return#4
+ return
+ to:@return
+
+void __start()
+__start: scope:[__start] from
+ call main
+ to:__start::@1
+__start::@1: scope:[__start] from __start
+ to:__start::@return
+__start::@return: scope:[__start] from __start::@1
+ return
+ to:@return
+
+SYMBOL TABLE SSA
+__constant char * const SCREEN = (char *)$400
+void __start()
+__bank(cx16_rom, 1) char add(char a , char b)
+char add::$0
+char add::a
+char add::a#0
+char add::a#1
+char add::b
+char add::b#0
+char add::b#1
+char add::return
+char add::return#0
+char add::return#1
+char add::return#2
+char add::return#3
+char add::return#4
+void main()
+char main::$0
+char main::$1
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::$0
+char plus::a
+char plus::a#0
+char plus::a#1
+char plus::a#2
+char plus::b
+char plus::b#0
+char plus::b#1
+char plus::b#2
+char plus::return
+char plus::return#0
+char plus::return#1
+char plus::return#2
+char plus::return#3
+char plus::return#4
+char plus::return#5
+char plus::return#6
+
+Adding number conversion cast (unumber) 7 in plus::b#0 = 7
+Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
+Adding number conversion cast (unumber) 6 in plus::b#1 = 6
+Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast plus::b#0 = (unumber)7
+Inlining cast plus::b#1 = (unumber)6
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (char *) 1024
+Simplifying constant integer cast 7
+Simplifying constant integer cast 0
+Simplifying constant integer cast 6
+Simplifying constant integer cast 1
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (char) 7
+Finalized unsigned number type (char) 0
+Finalized unsigned number type (char) 6
+Finalized unsigned number type (char) 1
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Alias plus::return#0 = plus::return#4
+Alias plus::return#1 = plus::return#5
+Alias add::return#0 = add::return#3
+Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
+Alias add::return#1 = add::$0 add::return#4 add::return#2
+Successful SSA optimization Pass2AliasElimination
+Identical Phi Values add::a#1 add::a#0
+Identical Phi Values add::b#1 add::b#0
+Successful SSA optimization Pass2IdenticalPhiElimination
+Constant plus::a#0 = '0'
+Constant plus::b#0 = 7
+Constant plus::a#1 = '1'
+Constant plus::b#1 = 6
+Successful SSA optimization Pass2ConstantIdentification
+Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
+Successful SSA optimization PassNSimplifyExpressionWithZero
+Removing unused procedure __start
+Removing unused procedure block __start
+Removing unused procedure block __start::@1
+Removing unused procedure block __start::@return
+Successful SSA optimization PassNEliminateEmptyStart
+Inlining constant with var siblings plus::a#0
+Inlining constant with var siblings plus::b#0
+Inlining constant with var siblings plus::a#1
+Inlining constant with var siblings plus::b#1
+Constant inlined plus::b#1 = 6
+Constant inlined plus::b#0 = 7
+Constant inlined plus::a#1 = '1'
+Constant inlined plus::a#0 = '0'
+Successful SSA optimization Pass2ConstantInlining
+Consolidated array index constant in *(SCREEN+1)
+Successful SSA optimization Pass2ConstantAdditionElimination
+Adding NOP phi() at start of main
+CALL GRAPH
+Calls in [main] to plus:1 plus:5
+Calls in [plus] to add:13
+
+Created 2 initial phi equivalence classes
+Coalesced down to 2 phi equivalence classes
+Adding NOP phi() at start of main
+
+FINAL CONTROL FLOW GRAPH
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ [1] call plus
+ [2] plus::return#0 = plus::return#2
+ to:main::@1
+main::@1: scope:[main] from main
+ [3] main::$0 = plus::return#0
+ [4] *SCREEN = main::$0
+ [5] call plus
+ [6] plus::return#1 = plus::return#2
+ to:main::@2
+main::@2: scope:[main] from main::@1
+ [7] main::$1 = plus::return#1
+ [8] *(SCREEN+1) = main::$1
+ to:main::@return
+main::@return: scope:[main] from main::@2
+ [9] return
+ to:@return
+
+__bank(cx16_ram, 1) char plus(char a , char b)
+plus: scope:[plus] from main main::@1
+ [10] plus::b#2 = phi( main/7, main::@1/6 )
+ [10] plus::a#2 = phi( main/'0', main::@1/'1' )
+ [11] add::a#0 = plus::a#2
+ [12] add::b#0 = plus::b#2
+ [13] call add
+ [14] add::return#0 = add::return#1
+ to:plus::@1
+plus::@1: scope:[plus] from plus
+ [15] plus::return#2 = add::return#0
+ to:plus::@return
+plus::@return: scope:[plus] from plus::@1
+ [16] return
+ to:@return
+
+__bank(cx16_rom, 1) char add(char a , char b)
+add: scope:[add] from plus
+ [17] add::return#1 = add::a#0 + add::b#0
+ to:add::@return
+add::@return: scope:[add] from add
+ [18] return
+ to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+__bank(cx16_rom, 1) char add(char a , char b)
+char add::a
+char add::a#0 // 56.0
+char add::b
+char add::b#0 // 112.0
+char add::return
+char add::return#0 // 22.0
+char add::return#1 // 37.33333333333333
+void main()
+char main::$0 // 4.0
+char main::$1 // 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // 11.0
+char plus::b
+char plus::b#2 // 5.5
+char plus::return
+char plus::return#0 // 4.0
+char plus::return#1 // 4.0
+char plus::return#2 // 3.75
+
+Initial phi equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
+Added variable main::$0 to live range equivalence class [ main::$0 ]
+Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
+Added variable main::$1 to live range equivalence class [ main::$1 ]
+Added variable add::a#0 to live range equivalence class [ add::a#0 ]
+Added variable add::b#0 to live range equivalence class [ add::b#0 ]
+Added variable add::return#0 to live range equivalence class [ add::return#0 ]
+Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
+Added variable add::return#1 to live range equivalence class [ add::return#1 ]
+Complete equivalence classes
+[ plus::a#2 ]
+[ plus::b#2 ]
+[ plus::return#0 ]
+[ main::$0 ]
+[ plus::return#1 ]
+[ main::$1 ]
+[ add::a#0 ]
+[ add::b#0 ]
+[ add::return#0 ]
+[ plus::return#2 ]
+[ add::return#1 ]
+Allocated zp[1]:2 [ add::b#0 ]
+Allocated zp[1]:3 [ add::a#0 ]
+Allocated zp[1]:4 [ add::return#1 ]
+Allocated zp[1]:5 [ add::return#0 ]
+Allocated zp[1]:6 [ plus::a#2 ]
+Allocated zp[1]:7 [ plus::b#2 ]
+Allocated zp[1]:8 [ plus::return#0 ]
+Allocated zp[1]:9 [ main::$0 ]
+Allocated zp[1]:10 [ plus::return#1 ]
+Allocated zp[1]:11 [ main::$1 ]
+Allocated zp[1]:12 [ plus::return#2 ]
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ]
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [13] call add [ add::return#1 ] ( plus:1 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Removing always clobbered register reg byte a as potential for zp[1]:4 [ add::return#1 ]
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [13] call add [ add::return#1 ] ( plus:1 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
+Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
+Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y ,
+Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte x , reg byte y ,
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
+Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
+Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
+Uplift Scope []
+
+Uplifting [add] best 223 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte x [ add::return#1 ] reg byte a [ add::return#0 ]
+Limited combination testing to 100 combinations of 192 possible.
+Uplifting [plus] best 195 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
+Limited combination testing to 100 combinations of 768 possible.
+Uplifting [main] best 183 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
+Uplifting [] best 183 combination
+Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
+Uplifting [add] best 183 combination zp[1]:3 [ add::a#0 ]
+Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
+Uplifting [plus] best 180 combination reg byte x [ plus::return#2 ]
+Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
+
+ASSEMBLER BEFORE OPTIMIZATION
+ // File Comments
+/**
+ * @file call-banked-phi-case-6-close-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #6.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-6-close-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ plus_from_main:
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b1
+ // main::@1
+ __b1:
+ // [3] main::$0 = plus::return#0
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ plus_from___b1:
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ jmp __b2
+ // main::@2
+ __b2:
+ // [7] main::$1 = plus::return#1
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ jmp __breturn
+ // main::@return
+ __breturn:
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
+ sta.z add.a
+ // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
+ txa
+ // [13] call add -- call_phi_close_cx16_rom
+ sta.z $ff
+ lda.z 1
+ pha
+ lda #1
+ sta.z 1
+ lda.z $ff
+ jsr add
+ sta.z $ff
+ pla
+ sta.z 1
+ lda.z $ff
+ // [14] add::return#0 = add::return#1 -- vbuaa=vbuxx
+ txa
+ jmp __b1
+ // plus::@1
+ __b1:
+ // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
+ tax
+ jmp __breturn
+ // plus::@return
+ __breturn:
+ // [16] return
+ rts
+}
+.segment ROM_Bank1
+ // add
+// __register(X) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_rom, 1)
+add: {
+ .label a = 2
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuxx=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ tax
+ jmp __breturn
+ // add::@return
+ __breturn:
+ // [18] return
+ rts
+}
+ // File Data
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp __b1
+Removing instruction jmp __b2
+Removing instruction jmp __breturn
+Removing instruction jmp __b1
+Removing instruction jmp __breturn
+Removing instruction jmp __breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction plus_from_main:
+Removing instruction __b1:
+Removing instruction plus_from___b1:
+Removing instruction __b2:
+Removing instruction __breturn:
+Removing instruction __b1:
+Removing instruction __breturn:
+Removing instruction __breturn:
+Succesful ASM optimization Pass5UnusedLabelElimination
+
+FINAL SYMBOL TABLE
+__constant char * const SCREEN = (char *) 1024
+__bank(cx16_rom, 1) char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte x 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte x [ plus::return#2 ]
+reg byte x [ add::return#1 ]
+
+
+FINAL ASSEMBLER
+Score: 162
+
+ // File Comments
+/**
+ * @file call-banked-phi-case-6-close-1.c
+ * @author Sven Van de Velde (sven.van.de.velde@telenet.be),
+ * @author Jesper Gravgaard
+ * @brief Test a procedure with calling convention PHI - case #6.
+ * Implementation using the #pragma bank and nobank directives.
+ * @version 0.1
+ * @date 2023-04-11
+ *
+ * @copyright Copyright (c) 2023
+ *
+ * The following cases exist in banked calling implementations:
+ *
+ * - case #1 - unbanked to unbanked and no banking areas
+ * - case #2 - unbanked to banked to any bank area
+ * - case #3 - banked to unbanked from any bank area
+ * - case #4 - banked to same bank in same bank area
+ * - case #5 - banked to different bank in same bank area
+ * - case #6 - banked to any bank between different bank areas
+ *
+ * This brings us to the call types:
+ *
+ * - near = case #1, #3, #4
+ * - close = case #2, #6
+ * - far = case #5
+ *
+ */
+ // Upstart
+ .file [name="call-banked-phi-case-6-close-1.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // plus('0', 7)
+ // [1] call plus
+ // [10] phi from main to plus [phi:main->plus]
+ // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
+ ldx #7
+ // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
+ lda #'0'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('0', 7)
+ // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@1
+ // [3] main::$0 = plus::return#0
+ // SCREEN[0] = plus('0', 7)
+ // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
+ sta SCREEN
+ // plus('1', 6)
+ // [5] call plus
+ // [10] phi from main::@1 to plus [phi:main::@1->plus]
+ // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
+ ldx #6
+ // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
+ lda #'1'
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus('1', 6)
+ // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
+ txa
+ // main::@2
+ // [7] main::$1 = plus::return#1
+ // SCREEN[1] = plus('1', 6)
+ // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
+ // close call
+ sta SCREEN+1
+ // main::@return
+ // }
+ // [9] return
+ rts
+}
+.segment RAM_Bank1
+ // plus
+// __register(X) char plus(__register(A) char a, __register(X) char b)
+// __bank(cx16_ram, 1)
+plus: {
+ // add(a, b)
+ // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
+ sta.z add.a
+ // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
+ txa
+ // [13] call add -- call_phi_close_cx16_rom
+ sta.z $ff
+ lda.z 1
+ pha
+ lda #1
+ sta.z 1
+ lda.z $ff
+ jsr add
+ sta.z $ff
+ pla
+ sta.z 1
+ lda.z $ff
+ // [14] add::return#0 = add::return#1 -- vbuaa=vbuxx
+ txa
+ // plus::@1
+ // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
+ tax
+ // plus::@return
+ // }
+ // [16] return
+ rts
+}
+.segment ROM_Bank1
+ // add
+// __register(X) char add(__zp(2) char a, __register(A) char b)
+// __bank(cx16_rom, 1)
+add: {
+ .label a = 2
+ // a+b
+ // [17] add::return#1 = add::a#0 + add::b#0 -- vbuxx=vbuz1_plus_vbuaa
+ clc
+ adc.z a
+ tax
+ // add::@return
+ // }
+ // [18] return
+ rts
+}
+ // File Data
+
diff --git a/src/test/ref/call-banked-phi-case-6-close-1.sym b/src/test/ref/call-banked-phi-case-6-close-1.sym
new file mode 100644
index 000000000..26eed0f1c
--- /dev/null
+++ b/src/test/ref/call-banked-phi-case-6-close-1.sym
@@ -0,0 +1,33 @@
+__constant char * const SCREEN = (char *) 1024
+__bank(cx16_rom, 1) char add(char a , char b)
+char add::a
+char add::a#0 // a zp[1]:2 56.0
+char add::b
+char add::b#0 // reg byte a 112.0
+char add::return
+char add::return#0 // reg byte a 22.0
+char add::return#1 // reg byte x 37.33333333333333
+void main()
+char main::$0 // reg byte a 4.0
+char main::$1 // reg byte a 4.0
+__bank(cx16_ram, 1) char plus(char a , char b)
+char plus::a
+char plus::a#2 // reg byte a 11.0
+char plus::b
+char plus::b#2 // reg byte x 5.5
+char plus::return
+char plus::return#0 // reg byte a 4.0
+char plus::return#1 // reg byte a 4.0
+char plus::return#2 // reg byte x 3.75
+
+reg byte a [ plus::a#2 ]
+reg byte x [ plus::b#2 ]
+reg byte a [ plus::return#0 ]
+reg byte a [ main::$0 ]
+reg byte a [ plus::return#1 ]
+reg byte a [ main::$1 ]
+zp[1]:2 [ add::a#0 ]
+reg byte a [ add::b#0 ]
+reg byte a [ add::return#0 ]
+reg byte x [ plus::return#2 ]
+reg byte x [ add::return#1 ]
diff --git a/src/test/ref/call-banked-phi-memvars.asm b/src/test/ref/call-banked-phi-memvars.asm
new file mode 100644
index 000000000..bd4556180
--- /dev/null
+++ b/src/test/ref/call-banked-phi-memvars.asm
@@ -0,0 +1,144 @@
+/**
+ * Test banked calls with memory variables.
+ * The parameters & return should end up in the shared/common bank.
+ */
+ .file [name="call-banked-phi-memvars.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ .label SCREEN = $400
+.segment Code
+main: {
+ ldy #0
+ __b1:
+ // for(char i=0;i<5; i++)
+ cpy #5
+ bcc __b2
+ // }
+ rts
+ __b2:
+ // plus(100, (int)i)
+ tya
+ sta plus.b
+ lda #0
+ sta plus.b+1
+ lda #<$64
+ sta plus.a
+ lda #>$64
+ sta plus.a+1
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus(100, (int)i)
+ // SCREEN[i] = plus(100, (int)i)
+ tya
+ asl
+ tax
+ lda __1
+ sta SCREEN,x
+ lda __1+1
+ sta SCREEN+1,x
+ // 10+i
+ tya
+ tax
+ axs #-[$a]
+ // plus(200, (int)i)
+ tya
+ sta plus.b
+ lda #0
+ sta plus.b+1
+ lda #<$c8
+ sta plus.a
+ lda #>$c8
+ sta plus.a+1
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus(200, (int)i)
+ // SCREEN[10+i] = plus(200, (int)i)
+ txa
+ asl
+ tax
+ lda __3
+ sta SCREEN,x
+ lda __3+1
+ sta SCREEN+1,x
+ // for(char i=0;i<5; i++)
+ iny
+ jmp __b1
+ .segment Data
+ .label __1 = plus.b
+ .label __3 = plus.b
+}
+.segment RAM_Bank1
+// __mem() int plus(__mem() int a, __mem() int b)
+// __bank(cx16_ram, 1)
+plus: {
+ // r += a
+ clc
+ lda a
+ adc #<2
+ sta r
+ lda a+1
+ adc #>2
+ sta r+1
+ // r += b
+ clc
+ lda r
+ adc b
+ sta r
+ lda r+1
+ adc b+1
+ sta r+1
+ // r += a
+ clc
+ lda r
+ adc a
+ sta r
+ lda r+1
+ adc a+1
+ sta r+1
+ // r += b
+ clc
+ lda return
+ adc r
+ sta return
+ lda return+1
+ adc r+1
+ sta return+1
+ // }
+ rts
+ .segment Data
+ b: .word 0
+ .label return = b
+ .segment RAM_Bank1
+ r: .word 0
+ .segment Data
+ a: .word 0
+}
diff --git a/src/test/ref/call-banked-phi-memvars.cfg b/src/test/ref/call-banked-phi-memvars.cfg
new file mode 100644
index 000000000..4cb2646a1
--- /dev/null
+++ b/src/test/ref/call-banked-phi-memvars.cfg
@@ -0,0 +1,45 @@
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ to:main::@1
+main::@1: scope:[main] from main main::@4
+ [1] main::i#2 = phi( main/0, main::@4/main::i#1 )
+ [2] if(main::i#2<5) goto main::@2
+ to:main::@return
+main::@return: scope:[main] from main::@1
+ [3] return
+ to:@return
+main::@2: scope:[main] from main::@1
+ [4] plus::b#0 = (int)main::i#2
+ [5] call plus
+ [6] plus::return#0 = plus::return#2
+ to:main::@3
+main::@3: scope:[main] from main::@2
+ [7] main::$1 = plus::return#0
+ [8] main::$4 = main::i#2 << 1
+ [9] SCREEN[main::$4] = main::$1
+ [10] main::$2 = $a + main::i#2
+ [11] plus::b#1 = (int)main::i#2
+ [12] call plus
+ [13] plus::return#1 = plus::return#2
+ to:main::@4
+main::@4: scope:[main] from main::@3
+ [14] main::$3 = plus::return#1
+ [15] main::$5 = main::$2 << 1
+ [16] SCREEN[main::$5] = main::$3
+ [17] main::i#1 = ++ main::i#2
+ to:main::@1
+
+__bank(cx16_ram, 1) int plus(int a , int b)
+plus: scope:[plus] from main::@2 main::@3
+ [18] plus::b#2 = phi( main::@2/plus::b#0, main::@3/plus::b#1 )
+ [18] plus::a#2 = phi( main::@2/$64, main::@3/$c8 )
+ [19] plus::r#1 = 2 + plus::a#2
+ [20] plus::r#2 = plus::r#1 + plus::b#2
+ [21] plus::r#3 = plus::r#2 + plus::a#2
+ [22] plus::return#2 = plus::r#3 + plus::b#2
+ to:plus::@return
+plus::@return: scope:[plus] from plus
+ [23] return
+ to:@return
diff --git a/src/test/ref/call-banked-phi-memvars.log b/src/test/ref/call-banked-phi-memvars.log
new file mode 100644
index 000000000..6aea5faca
--- /dev/null
+++ b/src/test/ref/call-banked-phi-memvars.log
@@ -0,0 +1,803 @@
+Loading link script "call-banked-phi.ld"
+Updating intermediate variable memory area to MAIN_MEMORY main::$0
+Updating intermediate variable memory area to MAIN_MEMORY main::$1
+Updating intermediate variable memory area to MAIN_MEMORY main::$2
+Updating intermediate variable memory area to MAIN_MEMORY main::$3
+Fixing banked procedure parameter/return value to default segment plus::a
+Fixing banked procedure parameter/return value to default segment plus::b
+Fixing banked procedure parameter/return value to default segment plus::return
+
+CONTROL FLOW GRAPH SSA
+
+void main()
+main: scope:[main] from __start
+ main::i#0 = 0
+ to:main::@1
+main::@1: scope:[main] from main main::@4
+ main::i#2 = phi( main/main::i#0, main::@4/main::i#1 )
+ main::$0 = main::i#2 < 5
+ if(main::$0) goto main::@2
+ to:main::@return
+main::@2: scope:[main] from main::@1
+ main::i#3 = phi( main::@1/main::i#2 )
+ plus::a#0 = $64
+ plus::b#0 = (int)main::i#3
+ call plus
+ plus::return#0 = plus::return#3
+ to:main::@3
+main::@3: scope:[main] from main::@2
+ main::i#4 = phi( main::@2/main::i#3 )
+ plus::return#4 = phi( main::@2/plus::return#0 )
+ main::$1 = plus::return#4
+ main::$4 = main::i#4 * SIZEOF_INT
+ SCREEN[main::$4] = main::$1
+ main::$2 = $a + main::i#4
+ plus::a#1 = $c8
+ plus::b#1 = (int)main::i#4
+ call plus
+ plus::return#1 = plus::return#3
+ to:main::@4
+main::@4: scope:[main] from main::@3
+ main::i#5 = phi( main::@3/main::i#4 )
+ plus::return#5 = phi( main::@3/plus::return#1 )
+ main::$3 = plus::return#5
+ main::$5 = main::$2 * SIZEOF_INT
+ SCREEN[main::$5] = main::$3
+ main::i#1 = ++ main::i#5
+ to:main::@1
+main::@return: scope:[main] from main::@1
+ return
+ to:@return
+
+__bank(cx16_ram, 1) int plus(int a , int b)
+plus: scope:[plus] from main::@2 main::@3
+ plus::b#2 = phi( main::@2/plus::b#0, main::@3/plus::b#1 )
+ plus::a#2 = phi( main::@2/plus::a#0, main::@3/plus::a#1 )
+ plus::r#0 = 2
+ plus::r#1 = plus::r#0 + plus::a#2
+ plus::r#2 = plus::r#1 + plus::b#2
+ plus::r#3 = plus::r#2 + plus::a#2
+ plus::r#4 = plus::r#3 + plus::b#2
+ plus::return#2 = plus::r#4
+ to:plus::@return
+plus::@return: scope:[plus] from plus
+ plus::return#6 = phi( plus/plus::return#2 )
+ plus::return#3 = plus::return#6
+ return
+ to:@return
+
+void __start()
+__start: scope:[__start] from
+ call main
+ to:__start::@1
+__start::@1: scope:[__start] from __start
+ to:__start::@return
+__start::@return: scope:[__start] from __start::@1
+ return
+ to:@return
+
+SYMBOL TABLE SSA
+__constant int * const SCREEN = (int *)$400
+__constant char SIZEOF_INT = 2
+void __start()
+void main()
+bool main::$0
+int main::$1
+number main::$2
+int main::$3
+char main::$4
+number main::$5
+char main::i
+char main::i#0
+char main::i#1
+char main::i#2
+char main::i#3
+char main::i#4
+char main::i#5
+__bank(cx16_ram, 1) int plus(int a , int b)
+int plus::a
+int plus::a#0
+int plus::a#1
+int plus::a#2
+int plus::b
+int plus::b#0
+int plus::b#1
+int plus::b#2
+int plus::r
+int plus::r#0
+int plus::r#1
+int plus::r#2
+int plus::r#3
+int plus::r#4
+int plus::return
+int plus::return#0
+int plus::return#1
+int plus::return#2
+int plus::return#3
+int plus::return#4
+int plus::return#5
+int plus::return#6
+
+Adding number conversion cast (unumber) 5 in main::$0 = main::i#2 < 5
+Adding number conversion cast (snumber) $64 in plus::a#0 = $64
+Adding number conversion cast (unumber) $a in main::$2 = $a + main::i#4
+Adding number conversion cast (unumber) main::$2 in main::$2 = (unumber)$a + main::i#4
+Adding number conversion cast (snumber) $c8 in plus::a#1 = $c8
+Adding number conversion cast (unumber) main::$5 in main::$5 = main::$2 * SIZEOF_INT
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast plus::a#0 = (snumber)$64
+Inlining cast plus::a#1 = (snumber)$c8
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (int *) 1024
+Simplifying constant integer cast 5
+Simplifying constant integer cast $64
+Simplifying constant integer cast $a
+Simplifying constant integer cast $c8
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (char) 5
+Finalized signed number type (signed char) $64
+Finalized unsigned number type (char) $a
+Finalized signed number type (int) $c8
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Inferred type updated to char in main::$2 = $a + main::i#4
+Inferred type updated to char in main::$5 = main::$2 * SIZEOF_INT
+Alias main::i#2 = main::i#3 main::i#4 main::i#5
+Alias plus::return#0 = plus::return#4
+Alias plus::return#1 = plus::return#5
+Alias plus::return#2 = plus::r#4 plus::return#6 plus::return#3
+Successful SSA optimization Pass2AliasElimination
+Simple Condition main::$0 [3] if(main::i#2<5) goto main::@2
+Successful SSA optimization Pass2ConditionalJumpSimplification
+Constant main::i#0 = 0
+Constant plus::a#0 = $64
+Constant plus::a#1 = $c8
+Constant plus::r#0 = 2
+Successful SSA optimization Pass2ConstantIdentification
+Removing unused procedure __start
+Removing unused procedure block __start
+Removing unused procedure block __start::@1
+Removing unused procedure block __start::@return
+Successful SSA optimization PassNEliminateEmptyStart
+Rewriting multiplication to use shift [6] main::$4 = main::i#2 * SIZEOF_INT
+Rewriting multiplication to use shift [13] main::$5 = main::$2 * SIZEOF_INT
+Successful SSA optimization Pass2MultiplyToShiftRewriting
+Inlining constant with var siblings main::i#0
+Inlining constant with var siblings plus::a#0
+Inlining constant with var siblings plus::a#1
+Inlining constant with var siblings plus::r#0
+Constant inlined main::i#0 = 0
+Constant inlined plus::a#1 = $c8
+Constant inlined plus::r#0 = 2
+Constant inlined plus::a#0 = $64
+Successful SSA optimization Pass2ConstantInlining
+Eliminating unused constant SIZEOF_INT
+Successful SSA optimization PassNEliminateUnusedVars
+Adding NOP phi() at start of main
+CALL GRAPH
+Calls in [main] to plus:6 plus:14
+
+Created 3 initial phi equivalence classes
+Coalesced [5] plus::b#3 = plus::b#0
+Coalesced [13] plus::b#4 = plus::b#1
+Coalesced [20] main::i#6 = main::i#1
+Coalesced down to 3 phi equivalence classes
+Adding NOP phi() at start of main
+
+FINAL CONTROL FLOW GRAPH
+
+void main()
+main: scope:[main] from
+ [0] phi()
+ to:main::@1
+main::@1: scope:[main] from main main::@4
+ [1] main::i#2 = phi( main/0, main::@4/main::i#1 )
+ [2] if(main::i#2<5) goto main::@2
+ to:main::@return
+main::@return: scope:[main] from main::@1
+ [3] return
+ to:@return
+main::@2: scope:[main] from main::@1
+ [4] plus::b#0 = (int)main::i#2
+ [5] call plus
+ [6] plus::return#0 = plus::return#2
+ to:main::@3
+main::@3: scope:[main] from main::@2
+ [7] main::$1 = plus::return#0
+ [8] main::$4 = main::i#2 << 1
+ [9] SCREEN[main::$4] = main::$1
+ [10] main::$2 = $a + main::i#2
+ [11] plus::b#1 = (int)main::i#2
+ [12] call plus
+ [13] plus::return#1 = plus::return#2
+ to:main::@4
+main::@4: scope:[main] from main::@3
+ [14] main::$3 = plus::return#1
+ [15] main::$5 = main::$2 << 1
+ [16] SCREEN[main::$5] = main::$3
+ [17] main::i#1 = ++ main::i#2
+ to:main::@1
+
+__bank(cx16_ram, 1) int plus(int a , int b)
+plus: scope:[plus] from main::@2 main::@3
+ [18] plus::b#2 = phi( main::@2/plus::b#0, main::@3/plus::b#1 )
+ [18] plus::a#2 = phi( main::@2/$64, main::@3/$c8 )
+ [19] plus::r#1 = 2 + plus::a#2
+ [20] plus::r#2 = plus::r#1 + plus::b#2
+ [21] plus::r#3 = plus::r#2 + plus::a#2
+ [22] plus::return#2 = plus::r#3 + plus::b#2
+ to:plus::@return
+plus::@return: scope:[plus] from plus
+ [23] return
+ to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+void main()
+int main::$1 // 11.0
+char main::$2 // 4.4
+int main::$3 // 11.0
+char main::$4 // 22.0
+char main::$5 // 22.0
+char main::i
+char main::i#1 // 22.0
+char main::i#2 // 3.6666666666666665
+__bank(cx16_ram, 1) int plus(int a , int b)
+int plus::a
+int plus::a#2 // 67.33333333333333
+int plus::b
+int plus::b#0 // 22.0
+int plus::b#1 // 22.0
+int plus::b#2 // 56.0
+int plus::r
+int plus::r#1 // 202.0
+int plus::r#2 // 202.0
+int plus::r#3 // 202.0
+int plus::return
+int plus::return#0 // 22.0
+int plus::return#1 // 22.0
+int plus::return#2 // 30.75
+
+Initial phi equivalence classes
+[ main::i#2 main::i#1 ]
+[ plus::a#2 ]
+[ plus::b#2 plus::b#0 plus::b#1 ]
+Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
+Added variable main::$1 to live range equivalence class [ main::$1 ]
+Added variable main::$4 to live range equivalence class [ main::$4 ]
+Added variable main::$2 to live range equivalence class [ main::$2 ]
+Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
+Added variable main::$3 to live range equivalence class [ main::$3 ]
+Added variable main::$5 to live range equivalence class [ main::$5 ]
+Added variable plus::r#1 to live range equivalence class [ plus::r#1 ]
+Added variable plus::r#2 to live range equivalence class [ plus::r#2 ]
+Added variable plus::r#3 to live range equivalence class [ plus::r#3 ]
+Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
+Complete equivalence classes
+[ main::i#2 main::i#1 ]
+[ plus::a#2 ]
+[ plus::b#2 plus::b#0 plus::b#1 ]
+[ plus::return#0 ]
+[ main::$1 ]
+[ main::$4 ]
+[ main::$2 ]
+[ plus::return#1 ]
+[ main::$3 ]
+[ main::$5 ]
+[ plus::r#1 ]
+[ plus::r#2 ]
+[ plus::r#3 ]
+[ plus::return#2 ]
+Allocated mem[2] [ plus::r#1 ]
+Allocated mem[2] [ plus::r#2 ]
+Allocated mem[2] [ plus::r#3 ]
+Allocated mem[2] [ plus::b#2 plus::b#0 plus::b#1 ]
+Allocated mem[2] [ plus::a#2 ]
+Allocated mem[2] [ plus::return#2 ]
+Allocated mem[1] [ main::i#2 main::i#1 ]
+Allocated mem[2] [ plus::return#0 ]
+Allocated mem[1] [ main::$4 ]
+Allocated mem[2] [ plus::return#1 ]
+Allocated mem[1] [ main::$5 ]
+Allocated mem[2] [ main::$1 ]
+Allocated mem[2] [ main::$3 ]
+Allocated mem[1] [ main::$2 ]
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [4] plus::b#0 = (int)main::i#2 [ main::i#2 plus::b#0 ] ( [ main::i#2 plus::b#0 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Removing always clobbered register reg byte a as potential for mem[1] [ main::i#2 main::i#1 ]
+Statement [5] call plus [ main::i#2 plus::return#2 ] ( [ main::i#2 plus::return#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Statement [6] plus::return#0 = plus::return#2 [ main::i#2 plus::return#0 ] ( [ main::i#2 plus::return#0 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Statement [7] main::$1 = plus::return#0 [ main::i#2 main::$1 ] ( [ main::i#2 main::$1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [8] main::$4 = main::i#2 << 1 [ main::i#2 main::$1 main::$4 ] ( [ main::i#2 main::$1 main::$4 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [9] SCREEN[main::$4] = main::$1 [ main::i#2 ] ( [ main::i#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [10] main::$2 = $a + main::i#2 [ main::i#2 main::$2 ] ( [ main::i#2 main::$2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [11] plus::b#1 = (int)main::i#2 [ main::i#2 main::$2 plus::b#1 ] ( [ main::i#2 main::$2 plus::b#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Removing always clobbered register reg byte a as potential for mem[1] [ main::$2 ]
+Statement [12] call plus [ main::i#2 plus::return#2 main::$2 ] ( [ main::i#2 plus::return#2 main::$2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [13] plus::return#1 = plus::return#2 [ main::i#2 main::$2 plus::return#1 ] ( [ main::i#2 main::$2 plus::return#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [14] main::$3 = plus::return#1 [ main::i#2 main::$2 main::$3 ] ( [ main::i#2 main::$2 main::$3 ] { } ) always clobbers reg byte a
+Statement [15] main::$5 = main::$2 << 1 [ main::i#2 main::$3 main::$5 ] ( [ main::i#2 main::$3 main::$5 ] { } ) always clobbers reg byte a
+Statement [16] SCREEN[main::$5] = main::$3 [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a
+Statement [19] plus::r#1 = 2 + plus::a#2 [ plus::a#2 plus::b#2 plus::r#1 ] ( plus:5 [ main::i#2 plus::a#2 plus::b#2 plus::r#1 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::a#2 plus::b#2 plus::r#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [20] plus::r#2 = plus::r#1 + plus::b#2 [ plus::a#2 plus::b#2 plus::r#2 ] ( plus:5 [ main::i#2 plus::a#2 plus::b#2 plus::r#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::a#2 plus::b#2 plus::r#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [21] plus::r#3 = plus::r#2 + plus::a#2 [ plus::b#2 plus::r#3 ] ( plus:5 [ main::i#2 plus::b#2 plus::r#3 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::b#2 plus::r#3 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [22] plus::return#2 = plus::r#3 + plus::b#2 [ plus::return#2 ] ( plus:5 [ main::i#2 plus::return#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::return#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [4] plus::b#0 = (int)main::i#2 [ main::i#2 plus::b#0 ] ( [ main::i#2 plus::b#0 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Statement [5] call plus [ main::i#2 plus::return#2 ] ( [ main::i#2 plus::return#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Statement [6] plus::return#0 = plus::return#2 [ main::i#2 plus::return#0 ] ( [ main::i#2 plus::return#0 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
+Statement [7] main::$1 = plus::return#0 [ main::i#2 main::$1 ] ( [ main::i#2 main::$1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [8] main::$4 = main::i#2 << 1 [ main::i#2 main::$1 main::$4 ] ( [ main::i#2 main::$1 main::$4 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [9] SCREEN[main::$4] = main::$1 [ main::i#2 ] ( [ main::i#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [10] main::$2 = $a + main::i#2 [ main::i#2 main::$2 ] ( [ main::i#2 main::$2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [11] plus::b#1 = (int)main::i#2 [ main::i#2 main::$2 plus::b#1 ] ( [ main::i#2 main::$2 plus::b#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [12] call plus [ main::i#2 plus::return#2 main::$2 ] ( [ main::i#2 plus::return#2 main::$2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [13] plus::return#1 = plus::return#2 [ main::i#2 main::$2 plus::return#1 ] ( [ main::i#2 main::$2 plus::return#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [14] main::$3 = plus::return#1 [ main::i#2 main::$2 main::$3 ] ( [ main::i#2 main::$2 main::$3 ] { } ) always clobbers reg byte a
+Statement [15] main::$5 = main::$2 << 1 [ main::i#2 main::$3 main::$5 ] ( [ main::i#2 main::$3 main::$5 ] { } ) always clobbers reg byte a
+Statement [16] SCREEN[main::$5] = main::$3 [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a
+Statement [19] plus::r#1 = 2 + plus::a#2 [ plus::a#2 plus::b#2 plus::r#1 ] ( plus:5 [ main::i#2 plus::a#2 plus::b#2 plus::r#1 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::a#2 plus::b#2 plus::r#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [20] plus::r#2 = plus::r#1 + plus::b#2 [ plus::a#2 plus::b#2 plus::r#2 ] ( plus:5 [ main::i#2 plus::a#2 plus::b#2 plus::r#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::a#2 plus::b#2 plus::r#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [21] plus::r#3 = plus::r#2 + plus::a#2 [ plus::b#2 plus::r#3 ] ( plus:5 [ main::i#2 plus::b#2 plus::r#3 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::b#2 plus::r#3 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Statement [22] plus::return#2 = plus::r#3 + plus::b#2 [ plus::return#2 ] ( plus:5 [ main::i#2 plus::return#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::return#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
+Potential registers mem[1] [ main::i#2 main::i#1 ] : mem[1] , reg byte x , reg byte y ,
+Potential registers mem[2] [ plus::a#2 ] : mem[2] ,
+Potential registers mem[2] [ plus::b#2 plus::b#0 plus::b#1 ] : mem[2] ,
+Potential registers mem[2] [ plus::return#0 ] : mem[2] ,
+Potential registers mem[2] [ main::$1 ] : mem[2] ,
+Potential registers mem[1] [ main::$4 ] : mem[1] , reg byte a , reg byte x , reg byte y ,
+Potential registers mem[1] [ main::$2 ] : mem[1] , reg byte x , reg byte y ,
+Potential registers mem[2] [ plus::return#1 ] : mem[2] ,
+Potential registers mem[2] [ main::$3 ] : mem[2] ,
+Potential registers mem[1] [ main::$5 ] : mem[1] , reg byte a , reg byte x , reg byte y ,
+Potential registers mem[2] [ plus::r#1 ] : mem[2] ,
+Potential registers mem[2] [ plus::r#2 ] : mem[2] ,
+Potential registers mem[2] [ plus::r#3 ] : mem[2] ,
+Potential registers mem[2] [ plus::return#2 ] : mem[2] ,
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [plus] 202: mem[2] [ plus::r#1 ] 202: mem[2] [ plus::r#2 ] 202: mem[2] [ plus::r#3 ] 100: mem[2] [ plus::b#2 plus::b#0 plus::b#1 ] 67.33: mem[2] [ plus::a#2 ] 30.75: mem[2] [ plus::return#2 ] 22: mem[2] [ plus::return#0 ] 22: mem[2] [ plus::return#1 ]
+Uplift Scope [main] 25.67: mem[1] [ main::i#2 main::i#1 ] 22: mem[1] [ main::$4 ] 22: mem[1] [ main::$5 ] 11: mem[2] [ main::$1 ] 11: mem[2] [ main::$3 ] 4.4: mem[1] [ main::$2 ]
+Uplift Scope []
+
+Uplifting [plus] best 2226 combination mem[2] [ plus::r#1 ] mem[2] [ plus::r#2 ] mem[2] [ plus::r#3 ] mem[2] [ plus::b#2 plus::b#0 plus::b#1 ] mem[2] [ plus::a#2 ] mem[2] [ plus::return#2 ] mem[2] [ plus::return#0 ] mem[2] [ plus::return#1 ]
+Uplifting [main] best 1866 combination reg byte y [ main::i#2 main::i#1 ] reg byte x [ main::$4 ] reg byte x [ main::$5 ] mem[2] [ main::$1 ] mem[2] [ main::$3 ] reg byte x [ main::$2 ]
+Limited combination testing to 100 combinations of 144 possible.
+Uplifting [] best 1866 combination
+Coalescing zero page register [ mem[2] [ plus::b#2 plus::b#0 plus::b#1 ] ] with [ mem[2] [ plus::return#2 ] ] - score: 1
+Coalescing zero page register [ mem[2] [ plus::return#0 ] ] with [ mem[2] [ main::$1 ] ] - score: 1
+Coalescing zero page register [ mem[2] [ plus::return#1 ] ] with [ mem[2] [ main::$3 ] ] - score: 1
+Coalescing zero page register [ mem[2] [ plus::r#1 ] ] with [ mem[2] [ plus::r#2 ] ] - score: 1
+Coalescing zero page register [ mem[2] [ plus::b#2 plus::b#0 plus::b#1 plus::return#2 ] ] with [ mem[2] [ plus::return#0 main::$1 ] ] - score: 1
+Coalescing zero page register [ mem[2] [ plus::b#2 plus::b#0 plus::b#1 plus::return#2 plus::return#0 main::$1 ] ] with [ mem[2] [ plus::return#1 main::$3 ] ] - score: 1
+Coalescing zero page register [ mem[2] [ plus::r#1 plus::r#2 ] ] with [ mem[2] [ plus::r#3 ] ] - score: 1
+
+ASSEMBLER BEFORE OPTIMIZATION
+ // File Comments
+/**
+ * Test banked calls with memory variables.
+ * The parameters & return should end up in the shared/common bank.
+ */
+ // Upstart
+ .file [name="call-banked-phi-memvars.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // [1] phi from main to main::@1 [phi:main->main::@1]
+ __b1_from_main:
+ // [1] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbuyy=vbuc1
+ ldy #0
+ jmp __b1
+ // main::@1
+ __b1:
+ // [2] if(main::i#2<5) goto main::@2 -- vbuyy_lt_vbuc1_then_la1
+ cpy #5
+ bcc __b2
+ jmp __breturn
+ // main::@return
+ __breturn:
+ // [3] return
+ rts
+ // main::@2
+ __b2:
+ // [4] plus::b#0 = (int)main::i#2 -- vwsm1=_sword_vbuyy
+ tya
+ sta plus.b
+ lda #0
+ sta plus.b+1
+ // [5] call plus
+ // [18] phi from main::@2 to plus [phi:main::@2->plus]
+ plus_from___b2:
+ // [18] phi plus::b#2 = plus::b#0 [phi:main::@2->plus#0] -- register_copy
+ // [18] phi plus::a#2 = $64 [phi:main::@2->plus#1] -- call_phi_close_cx16_ram
+ lda #<$64
+ sta plus.a
+ lda #>$64
+ sta plus.a+1
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [6] plus::return#0 = plus::return#2
+ jmp __b3
+ // main::@3
+ __b3:
+ // [7] main::$1 = plus::return#0
+ // [8] main::$4 = main::i#2 << 1 -- vbuxx=vbuyy_rol_1
+ tya
+ asl
+ tax
+ // [9] SCREEN[main::$4] = main::$1 -- pwsc1_derefidx_vbuxx=vwsm1
+ lda __1
+ sta SCREEN,x
+ lda __1+1
+ sta SCREEN+1,x
+ // [10] main::$2 = $a + main::i#2 -- vbuxx=vbuc1_plus_vbuyy
+ tya
+ tax
+ axs #-[$a]
+ // [11] plus::b#1 = (int)main::i#2 -- vwsm1=_sword_vbuyy
+ tya
+ sta plus.b
+ lda #0
+ sta plus.b+1
+ // [12] call plus
+ // [18] phi from main::@3 to plus [phi:main::@3->plus]
+ plus_from___b3:
+ // [18] phi plus::b#2 = plus::b#1 [phi:main::@3->plus#0] -- register_copy
+ // [18] phi plus::a#2 = $c8 [phi:main::@3->plus#1] -- call_phi_close_cx16_ram
+ lda #<$c8
+ sta plus.a
+ lda #>$c8
+ sta plus.a+1
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // [13] plus::return#1 = plus::return#2
+ jmp __b4
+ // main::@4
+ __b4:
+ // [14] main::$3 = plus::return#1
+ // [15] main::$5 = main::$2 << 1 -- vbuxx=vbuxx_rol_1
+ txa
+ asl
+ tax
+ // [16] SCREEN[main::$5] = main::$3 -- pwsc1_derefidx_vbuxx=vwsm1
+ lda __3
+ sta SCREEN,x
+ lda __3+1
+ sta SCREEN+1,x
+ // [17] main::i#1 = ++ main::i#2 -- vbuyy=_inc_vbuyy
+ iny
+ // [1] phi from main::@4 to main::@1 [phi:main::@4->main::@1]
+ __b1_from___b4:
+ // [1] phi main::i#2 = main::i#1 [phi:main::@4->main::@1#0] -- register_copy
+ jmp __b1
+ .segment Data
+ .label __1 = plus.b
+ .label __3 = plus.b
+}
+.segment RAM_Bank1
+ // plus
+// __mem() int plus(__mem() int a, __mem() int b)
+// __bank(cx16_ram, 1)
+plus: {
+ // [19] plus::r#1 = 2 + plus::a#2 -- vwsm1=vwsc1_plus_vwsm2
+ clc
+ lda a
+ adc #<2
+ sta r
+ lda a+1
+ adc #>2
+ sta r+1
+ // [20] plus::r#2 = plus::r#1 + plus::b#2 -- vwsm1=vwsm1_plus_vwsm2
+ clc
+ lda r
+ adc b
+ sta r
+ lda r+1
+ adc b+1
+ sta r+1
+ // [21] plus::r#3 = plus::r#2 + plus::a#2 -- vwsm1=vwsm1_plus_vwsm2
+ clc
+ lda r
+ adc a
+ sta r
+ lda r+1
+ adc a+1
+ sta r+1
+ // [22] plus::return#2 = plus::r#3 + plus::b#2 -- vwsm1=vwsm2_plus_vwsm1
+ clc
+ lda return
+ adc r
+ sta return
+ lda return+1
+ adc r+1
+ sta return+1
+ jmp __breturn
+ // plus::@return
+ __breturn:
+ // [23] return
+ rts
+ .segment Data
+ b: .word 0
+ .label return = b
+ .segment RAM_Bank1
+ r: .word 0
+ .segment Data
+ a: .word 0
+}
+ // File Data
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp __b1
+Removing instruction jmp __breturn
+Removing instruction jmp __b3
+Removing instruction jmp __b4
+Removing instruction jmp __breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction __b1_from_main:
+Removing instruction __breturn:
+Removing instruction plus_from___b2:
+Removing instruction __b3:
+Removing instruction plus_from___b3:
+Removing instruction __b4:
+Removing instruction __b1_from___b4:
+Removing instruction __breturn:
+Succesful ASM optimization Pass5UnusedLabelElimination
+
+FINAL SYMBOL TABLE
+__constant int * const SCREEN = (int *) 1024
+void main()
+int main::$1 // mem[2] 11.0
+char main::$2 // reg byte x 4.4
+int main::$3 // mem[2] 11.0
+char main::$4 // reg byte x 22.0
+char main::$5 // reg byte x 22.0
+char main::i
+char main::i#1 // reg byte y 22.0
+char main::i#2 // reg byte y 3.6666666666666665
+__bank(cx16_ram, 1) int plus(int a , int b)
+int plus::a
+int plus::a#2 // a mem[2] 67.33333333333333
+int plus::b
+int plus::b#0 // b mem[2] 22.0
+int plus::b#1 // b mem[2] 22.0
+int plus::b#2 // b mem[2] 56.0
+int plus::r
+int plus::r#1 // r mem[2] 202.0
+int plus::r#2 // r mem[2] 202.0
+int plus::r#3 // r mem[2] 202.0
+int plus::return
+int plus::return#0 // return mem[2] 22.0
+int plus::return#1 // return mem[2] 22.0
+int plus::return#2 // return mem[2] 30.75
+
+reg byte y [ main::i#2 main::i#1 ]
+mem[2] [ plus::a#2 ]
+mem[2] [ plus::b#2 plus::b#0 plus::b#1 plus::return#2 plus::return#0 main::$1 plus::return#1 main::$3 ]
+reg byte x [ main::$4 ]
+reg byte x [ main::$2 ]
+reg byte x [ main::$5 ]
+mem[2] [ plus::r#1 plus::r#2 plus::r#3 ]
+
+
+FINAL ASSEMBLER
+Score: 1103
+
+ // File Comments
+/**
+ * Test banked calls with memory variables.
+ * The parameters & return should end up in the shared/common bank.
+ */
+ // Upstart
+ .file [name="call-banked-phi-memvars.prg", type="prg", segments="Program"]
+.segmentdef Program [segments="Basic, Code, Data"]
+.segmentdef Basic [start=$0801]
+.segmentdef Code [start=$80d]
+.segmentdef Data [startAfter="Code"]
+.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
+.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
+.segment Basic
+:BasicUpstart(main)
+.segment Code
+.segment Data
+
+
+ // Global Constants & labels
+ .label SCREEN = $400
+.segment Code
+ // main
+main: {
+ // [1] phi from main to main::@1 [phi:main->main::@1]
+ // [1] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbuyy=vbuc1
+ ldy #0
+ // main::@1
+ __b1:
+ // for(char i=0;i<5; i++)
+ // [2] if(main::i#2<5) goto main::@2 -- vbuyy_lt_vbuc1_then_la1
+ cpy #5
+ bcc __b2
+ // main::@return
+ // }
+ // [3] return
+ rts
+ // main::@2
+ __b2:
+ // plus(100, (int)i)
+ // [4] plus::b#0 = (int)main::i#2 -- vwsm1=_sword_vbuyy
+ tya
+ sta plus.b
+ lda #0
+ sta plus.b+1
+ // [5] call plus
+ // [18] phi from main::@2 to plus [phi:main::@2->plus]
+ // [18] phi plus::b#2 = plus::b#0 [phi:main::@2->plus#0] -- register_copy
+ // [18] phi plus::a#2 = $64 [phi:main::@2->plus#1] -- call_phi_close_cx16_ram
+ lda #<$64
+ sta plus.a
+ lda #>$64
+ sta plus.a+1
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus(100, (int)i)
+ // [6] plus::return#0 = plus::return#2
+ // main::@3
+ // [7] main::$1 = plus::return#0
+ // SCREEN[i] = plus(100, (int)i)
+ // [8] main::$4 = main::i#2 << 1 -- vbuxx=vbuyy_rol_1
+ tya
+ asl
+ tax
+ // [9] SCREEN[main::$4] = main::$1 -- pwsc1_derefidx_vbuxx=vwsm1
+ lda __1
+ sta SCREEN,x
+ lda __1+1
+ sta SCREEN+1,x
+ // 10+i
+ // [10] main::$2 = $a + main::i#2 -- vbuxx=vbuc1_plus_vbuyy
+ tya
+ tax
+ axs #-[$a]
+ // plus(200, (int)i)
+ // [11] plus::b#1 = (int)main::i#2 -- vwsm1=_sword_vbuyy
+ tya
+ sta plus.b
+ lda #0
+ sta plus.b+1
+ // [12] call plus
+ // [18] phi from main::@3 to plus [phi:main::@3->plus]
+ // [18] phi plus::b#2 = plus::b#1 [phi:main::@3->plus#0] -- register_copy
+ // [18] phi plus::a#2 = $c8 [phi:main::@3->plus#1] -- call_phi_close_cx16_ram
+ lda #<$c8
+ sta plus.a
+ lda #>$c8
+ sta plus.a+1
+ sta.z $ff
+ lda.z 0
+ pha
+ lda #1
+ sta.z 0
+ lda.z $ff
+ jsr plus
+ sta.z $ff
+ pla
+ sta.z 0
+ lda.z $ff
+ // plus(200, (int)i)
+ // [13] plus::return#1 = plus::return#2
+ // main::@4
+ // [14] main::$3 = plus::return#1
+ // SCREEN[10+i] = plus(200, (int)i)
+ // [15] main::$5 = main::$2 << 1 -- vbuxx=vbuxx_rol_1
+ txa
+ asl
+ tax
+ // [16] SCREEN[main::$5] = main::$3 -- pwsc1_derefidx_vbuxx=vwsm1
+ lda __3
+ sta SCREEN,x
+ lda __3+1
+ sta SCREEN+1,x
+ // for(char i=0;i<5; i++)
+ // [17] main::i#1 = ++ main::i#2 -- vbuyy=_inc_vbuyy
+ iny
+ // [1] phi from main::@4 to main::@1 [phi:main::@4->main::@1]
+ // [1] phi main::i#2 = main::i#1 [phi:main::@4->main::@1#0] -- register_copy
+ jmp __b1
+ .segment Data
+ .label __1 = plus.b
+ .label __3 = plus.b
+}
+.segment RAM_Bank1
+ // plus
+// __mem() int plus(__mem() int a, __mem() int b)
+// __bank(cx16_ram, 1)
+plus: {
+ // r += a
+ // [19] plus::r#1 = 2 + plus::a#2 -- vwsm1=vwsc1_plus_vwsm2
+ clc
+ lda a
+ adc #<2
+ sta r
+ lda a+1
+ adc #>2
+ sta r+1
+ // r += b
+ // [20] plus::r#2 = plus::r#1 + plus::b#2 -- vwsm1=vwsm1_plus_vwsm2
+ clc
+ lda r
+ adc b
+ sta r
+ lda r+1
+ adc b+1
+ sta r+1
+ // r += a
+ // [21] plus::r#3 = plus::r#2 + plus::a#2 -- vwsm1=vwsm1_plus_vwsm2
+ clc
+ lda r
+ adc a
+ sta r
+ lda r+1
+ adc a+1
+ sta r+1
+ // r += b
+ // [22] plus::return#2 = plus::r#3 + plus::b#2 -- vwsm1=vwsm2_plus_vwsm1
+ clc
+ lda return
+ adc r
+ sta return
+ lda return+1
+ adc r+1
+ sta return+1
+ // plus::@return
+ // }
+ // [23] return
+ rts
+ .segment Data
+ b: .word 0
+ .label return = b
+ .segment RAM_Bank1
+ r: .word 0
+ .segment Data
+ a: .word 0
+}
+ // File Data
+
diff --git a/src/test/ref/call-banked-phi-memvars.sym b/src/test/ref/call-banked-phi-memvars.sym
new file mode 100644
index 000000000..4300fc0bd
--- /dev/null
+++ b/src/test/ref/call-banked-phi-memvars.sym
@@ -0,0 +1,33 @@
+__constant int * const SCREEN = (int *) 1024
+void main()
+int main::$1 // mem[2] 11.0
+char main::$2 // reg byte x 4.4
+int main::$3 // mem[2] 11.0
+char main::$4 // reg byte x 22.0
+char main::$5 // reg byte x 22.0
+char main::i
+char main::i#1 // reg byte y 22.0
+char main::i#2 // reg byte y 3.6666666666666665
+__bank(cx16_ram, 1) int plus(int a , int b)
+int plus::a
+int plus::a#2 // a mem[2] 67.33333333333333
+int plus::b
+int plus::b#0 // b mem[2] 22.0
+int plus::b#1 // b mem[2] 22.0
+int plus::b#2 // b mem[2] 56.0
+int plus::r
+int plus::r#1 // r mem[2] 202.0
+int plus::r#2 // r mem[2] 202.0
+int plus::r#3 // r mem[2] 202.0
+int plus::return
+int plus::return#0 // return mem[2] 22.0
+int plus::return#1 // return mem[2] 22.0
+int plus::return#2 // return mem[2] 30.75
+
+reg byte y [ main::i#2 main::i#1 ]
+mem[2] [ plus::a#2 ]
+mem[2] [ plus::b#2 plus::b#0 plus::b#1 plus::return#2 plus::return#0 main::$1 plus::return#1 main::$3 ]
+reg byte x [ main::$4 ]
+reg byte x [ main::$2 ]
+reg byte x [ main::$5 ]
+mem[2] [ plus::r#1 plus::r#2 plus::r#3 ]
diff --git a/src/test/ref/inline-asm-ref-scoped.log b/src/test/ref/inline-asm-ref-scoped.log
index 0fe922e7f..87605b4e1 100644
--- a/src/test/ref/inline-asm-ref-scoped.log
+++ b/src/test/ref/inline-asm-ref-scoped.log
@@ -4,7 +4,7 @@ CONTROL FLOW GRAPH SSA
void main()
main: scope:[main] from __start
asm { lda#'c' stasub.ll+1 }
- call sub
+ call sub
to:main::@1
main::@1: scope:[main] from main
to:main::@return
@@ -22,7 +22,7 @@ sub::@return: scope:[sub] from sub
void __start()
__start: scope:[__start] from
- call main
+ call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
@@ -53,7 +53,7 @@ FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
asm { lda#'c' stasub.ll+1 }
- [1] call sub
+ [1] call sub
to:main::@return
main::@return: scope:[main] from main
[2] return
@@ -106,7 +106,7 @@ main: {
// asm { lda#'c' stasub.ll+1 }
lda #'c'
sta sub.ll+1
- // [1] call sub
+ // [1] call sub
jsr sub
jmp __breturn
// main::@return
@@ -165,7 +165,7 @@ main: {
lda #'c'
sta sub.ll+1
// sub()
- // [1] call sub
+ // [1] call sub
jsr sub
// main::@return
// }
diff --git a/src/test/ref/pragma-noparam-noparen.log b/src/test/ref/pragma-noparam-noparen.log
index 68ccb3b9d..ec255c1a4 100644
--- a/src/test/ref/pragma-noparam-noparen.log
+++ b/src/test/ref/pragma-noparam-noparen.log
@@ -1,4 +1,3 @@
-Warning! Unknown #pragma nobank
CONTROL FLOW GRAPH SSA