1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-01-09 20:31:44 +00:00

- Refactored far call logic during parsing. The pragmaFarSegs hash table has been removed from the Pass0GenerateStatementSequence. Instead, a currentFarSegment is controlling the #pragma logic.

- Refactored pragmaCodeSegs HashMap, which has been removed from Pass0GenerateStatementSequence and moved to Program. It controls which code segment have been defined, and a validation is implemented in the far_seg pragmas.
- Refactored __far() to use the code segments, in analogy to far_seg pragmas.
- Refactored Procedure, which implements functions to use the FarSegment to decide on far declaration and far bank retrieval.
- refactored CallingConvention. Removed earlier quick implementation of bankFar variable as a local property, and declaredFar in the Procedure class. Now everyting is controlled through the farSegment.
- Refactored Procedure, the removal of FAR_CALL calling convention. It is wrong to implement FAR_CALL calling convention, because far and near calls can be stack calls, phi calls and even a varcall can be near of far.
- Removed the Pass1ProcedureFar because far and near implementation is not defined in statement level, it is defined on procedure level. But I've kept Pass1ProcedureFar to suppor the logic for inline prepare, execute, finalize routines during further implementation.
- Refactored directive FAR to match the directive far_seg() structure.
- Refactored complete logic during Pass4CodeGeneration to process statement call fragment generation to decide on near or far calls within the logic, taking into account the calling convention used.
- Added new fragments call_far[platform]_prepare.asm, call_far[platform]_execute.asm, call_far[platform]_finalize.asm and deleted call_far[platform]_entry.asm, call_far[platform]_exit.asm
- Renamed pragmas far_seg and near_seg to simply far and near respectively. Updated test cases with this change.
- Added test cases.

TODO: The actual inline implementation of prepare, execute, finalize functions when platform .asm fragments are not used.
TODO: Implementation of the near directive.
This commit is contained in:
Flight_Control 2022-11-20 09:56:10 +01:00
parent d19c005d79
commit 1a63d437a0
35 changed files with 775 additions and 208 deletions

@ -162,7 +162,7 @@ directive
| EXTERN #directiveExtern
| EXPORT #directiveExport
| INLINE #directiveInline
| FAR PAR_BEGIN ( NUMBER ) PAR_END #directiveFar
| FAR PAR_BEGIN NAME COMMA NUMBER (COMMA NAME COMMA NAME COMMA NAME)? PAR_END #directiveFar
| INTRINSIC #directiveIntrinsic
| INTERRUPT ( PAR_BEGIN NAME PAR_END )? #directiveInterrupt
| LOCAL_RESERVE PAR_BEGIN pragmaParam ( COMMA pragmaParam )* PAR_END #directiveReserveZp

@ -278,7 +278,6 @@ public class Compiler {
getLog().append(program.getGraph().toString(program));
}
new Pass1ProcedureInline(program).execute();
new Pass1ProcedureFar(program).execute(); // Implements far calls to procedures defined in a bank. See https://gitlab.com/Flight_Control/kickc/-/commits/far-call-isolated
new PassNStatementIndices(program).step();
program.clearCallGraph();
new Pass1AssertNoRecursion(program).execute();

@ -18,7 +18,6 @@ import dk.camelot64.kickc.model.symbols.Symbol;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeInference;
import dk.camelot64.kickc.model.values.*;
import kickass.pass.values.StringValue;
/**
* Creates an ASM Fragment specification for a statement in the control flow graph.
@ -43,16 +42,16 @@ final public class AsmFragmentInstanceSpecBuilder {
}
/**
* Create a fragment instance spec factory for a far call entry
* Create a fragment instance spec factory for a far call prepare
*
* @param bankFar The bank where the procedure is to be called.
* @param procedureName The full name of the procedure.
* @param program The program
* @return the fragment instance spec factory
*/
public static AsmFragmentInstanceSpec farCallEntry(Long bankFar, String procedureName, Program program) {
public static AsmFragmentInstanceSpec farCallPrepare(Long bankFar, String procedureName, Program program) {
AsmFragmentBindings bindings = new AsmFragmentBindings(program);
AsmFragmentSignature signature = new AsmFragmentSignature.CallFar(bankFar, program.getTargetPlatform().getName(), AsmFragmentSignature.CallFar.EntryExit.Entry);
AsmFragmentSignature signature = new AsmFragmentSignature.CallFar(bankFar, program.getTargetPlatform().getName(), AsmFragmentSignature.CallFar.PrepareExecuteFinalize.Prepare);
ScopeRef codeScope = program.getScope().getRef();
// ScopeRef codeScope = program.getStatementInfos().getBlock(call).getScope();
bindings.bind("c1", new ConstantInteger(bankFar));
@ -61,16 +60,34 @@ final public class AsmFragmentInstanceSpecBuilder {
}
/**
* Create a fragment instance spec factory for a far call exit
* Create a fragment instance spec factory for a far call execute
*
* @param bankFar The bank where the procedure is to be called.
* @param procedureName The full name of the procedure.
* @param program The program
* @return the fragment instance spec factory
*/
public static AsmFragmentInstanceSpec farCallExecute(Long bankFar, String procedureName, Program program) {
AsmFragmentBindings bindings = new AsmFragmentBindings(program);
AsmFragmentSignature signature = new AsmFragmentSignature.CallFar(bankFar, program.getTargetPlatform().getName(), AsmFragmentSignature.CallFar.PrepareExecuteFinalize.Execute);
ScopeRef codeScope = program.getScope().getRef();
// ScopeRef codeScope = program.getStatementInfos().getBlock(call).getScope();
bindings.bind("c1", new ConstantInteger(bankFar));
bindings.bind("la1", new LabelRef(procedureName));
return new AsmFragmentInstanceSpec(program, signature, bindings, codeScope);
}
/**
* Create a fragment instance spec factory for a far call finalize
*
* @param bankFar The bank where the procedure is to be called.
* @param procedureName The full name of the procedure.
* @param program The program
* @return the fragment instance spec factory
*/
public static AsmFragmentInstanceSpec farCallExit(Long bankFar, String procedureName, Program program) {
public static AsmFragmentInstanceSpec farCallFinalize(Long bankFar, String procedureName, Program program) {
AsmFragmentBindings bindings = new AsmFragmentBindings(program);
AsmFragmentSignature signature = new AsmFragmentSignature.CallFar(bankFar, program.getTargetPlatform().getName(), AsmFragmentSignature.CallFar.EntryExit.Exit);
AsmFragmentSignature signature = new AsmFragmentSignature.CallFar(bankFar, program.getTargetPlatform().getName(), AsmFragmentSignature.CallFar.PrepareExecuteFinalize.Finalize);
ScopeRef codeScope = program.getScope().getRef();
bindings.bind("c1", new ConstantInteger(bankFar));
bindings.bind("la1", new LabelRef(procedureName));

@ -78,23 +78,24 @@ public interface AsmFragmentSignature {
final private Long bankFar;
final private String targetPlatform;
public enum EntryExit {
Exit,
Entry
public enum PrepareExecuteFinalize {
Prepare,
Execute,
Finalize
}
final private CallFar.EntryExit entryExit;
final private PrepareExecuteFinalize far;
public CallFar(Long bankFar, String targetPlatform, CallFar.EntryExit entryExit) {
public CallFar(Long bankFar, String targetPlatform, PrepareExecuteFinalize far) {
this.bankFar = bankFar;
this.targetPlatform = targetPlatform;
this.entryExit = entryExit;
this.far = far;
}
@Override
public String getName() {
return "call_far" + "_" + targetPlatform + "_" + entryExit.name().toLowerCase();
return "call_far" + "_" + targetPlatform + "_" + far.name().toLowerCase();
}
}

@ -46,13 +46,41 @@ public class Directive {
/** Function declared far. */
static public class Far extends Directive {
public Long bankFar;
private String farSegmentName;
private Long farSegmentBank;
private String farProcedurePrepare;
private String farProcedureExecute;
private String farProcedureFinalize;
public Far(Long bankFar) {
super("__far");
this.bankFar = bankFar;
public Far(String farSegmentName, Long farSegmentBank, String farProcedurePrepare, String farProcedureExecute, String farProcedureFinalize) {
super("far" );
this.farSegmentName = farSegmentName;
this.farSegmentBank = farSegmentBank;
this.farProcedurePrepare = farProcedurePrepare;
this.farProcedureExecute = farProcedureExecute;
this.farProcedureFinalize = farProcedureFinalize;
}
public String getFarSegmentName() {
return farSegmentName;
}
public Long getFarSegmentBank() {
return farSegmentBank;
}
public String getFarProcedurePrepare() {
return farProcedurePrepare;
}
public String getFarProcedureExecute() {
return farProcedureExecute;
}
public String getFarProcedureFinalize() {
return farProcedureFinalize;
}
}
/** Function declared intrinsic. */

@ -1,32 +1,43 @@
package dk.camelot64.kickc.model;
import dk.camelot64.kickc.model.symbols.Procedure;
import java.util.List;
/** A far segment. */
public class FarSegment {
private String name;
private Integer bank;
private String call_prepare;
private String call_execute;
private String call_finalize;
private final String farSegment;
private Long farBank;
private final String procedurePrepare;
private final String procedureExecute;
private final String procedureFinalize;
public FarSegment(String name, Integer bank, String call_prepare, String call_execute, String call_finalize) {
this.name = name;
this.bank = bank;
this.call_prepare = call_prepare;
this.call_execute = call_execute;
this.call_finalize = call_finalize;
public FarSegment(String name, Long bank, String procedurePrepare, String procedureExecute, String procedureFinalize) {
this.farSegment = name;
this.farBank = bank;
this.procedurePrepare = procedurePrepare;
this.procedureExecute = procedureExecute;
this.procedureFinalize = procedureFinalize;
}
public String getName() {
return name;
public String getFarSegment() {
return farSegment;
}
public Long getFarBank() {
return farBank;
}
public Integer getBank() {
return bank;
public void setFarBank(Long farBank) {
this.farBank = farBank;
}
public String getProcedurePrepare() {
return procedurePrepare;
}
public String getProcedureExecute() {
return procedureExecute;
}
public String getProcedureFinalize() {
return procedureFinalize;
}
}

@ -8,13 +8,11 @@ import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.symbols.ProgramScope;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.ProcedureRef;
import dk.camelot64.kickc.parser.KickCParser;
import dk.camelot64.kickc.passes.calcs.*;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/** A KickC Intermediate Compiler Language (ICL) Program */
public class Program {
@ -107,6 +105,8 @@ public class Program {
private NaturalLoopSet loopSet;
/** The register weight of all variables describing how much the variable would theoretically gain from being in a register. PASS 3-5 (CACHED ON-DEMAND) */
private VariableRegisterWeights variableRegisterWeights;
/** All #pragma code segments. Collected during parsing. These are used by the far() pragmas to validate if the code segment exists during compilation.*/
private final Map<String, KickCParser.PragmaContext> pragmaCodeSegs;
public Program() {
this.outputFileManager = new OutputFileManager();
@ -119,6 +119,8 @@ public class Program {
this.asmResourceFiles = new ArrayList<>();
this.reservedZps = new ArrayList<>();
this.procedureCompilations = new LinkedHashMap<>();
this.pragmaCodeSegs = new HashMap<>(); // Used to collect all pragma code segments.
}
/**
@ -539,4 +541,7 @@ public class Program {
return sizeInfo.toString();
}
public Map<String, KickCParser.PragmaContext> getPragmaCodeSegs() {
return pragmaCodeSegs;
}
}

@ -1,6 +1,7 @@
package dk.camelot64.kickc.model.symbols;
import dk.camelot64.kickc.model.Comment;
import dk.camelot64.kickc.model.FarSegment;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.statements.StatementSource;
import dk.camelot64.kickc.model.types.SymbolType;
@ -27,8 +28,6 @@ public class Procedure extends Scope {
private boolean declaredInline;
/** true if the procedure is declared far. */
private boolean declaredFar;
/** contains the far bank. */
private Long bankFar;
/** True if the procedure is declared intrinsic. */
private boolean declaredIntrinsic;
/** The type of interrupt that the procedure serves. Null for all procedures not serving an interrupt. */
@ -45,6 +44,9 @@ public class Procedure extends Scope {
private boolean isConstructor;
/** The source of the procedure definition. */
private StatementSource definitionSource;
/** The far segment information. Collected during parsing. These are used to compare with the current currentFarSegment to decide a near or a far call, and to keep inline calling routines.*/
private FarSegment farSegment;
/** The names of all legal intrinsic procedures. */
final public static List<String> INTRINSIC_PROCEDURES = Arrays.asList(
@ -54,10 +56,17 @@ public class Procedure extends Scope {
Pass1ByteXIntrinsicRewrite.INTRINSIC_MAKELONG4
);
public FarSegment getFarSegment() {
return farSegment;
}
public void setFarSegment(FarSegment farSegment) {
this.farSegment = farSegment;
}
/** The method for passing parameters and return value to the procedure. */
public enum CallingConvention {
/** Far call in a cx16 bank using the https://github.com/commanderx16/x16-docs/blob/master/X16%20Reference%20-%2004%20-%20KERNAL.md#function-name-jsrfar routine*/
FAR_CALL("__far"),
/** Parameters and return value handled through PHI-transitions. */
PHI_CALL("__phicall"),
/** Parameters and return value over the stack. */
@ -91,12 +100,11 @@ 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) {
public Procedure(String name, SymbolTypeProcedure procedureType, Scope parentScope, String codeSegment, String dataSegment, CallingConvention callingConvention, FarSegment farSegment) {
super(name, parentScope, dataSegment);
this.procedureType = procedureType;
this.declaredInline = false;
this.declaredFar = false;
this.bankFar = 0L;
this.farSegment = farSegment;
this.interruptType = null;
this.comments = new ArrayList<>();
this.codeSegment = codeSegment;
@ -207,17 +215,14 @@ public class Procedure extends Scope {
}
public boolean isDeclaredFar() {
return declaredFar;
return farSegment != null;
}
public void setDeclaredFar(boolean declaredFar) {
this.declaredFar = declaredFar;
}
public Long getBankFar() { return this.bankFar; }
public void setBankFar(Long bankFar) {
this.bankFar = bankFar;
public Long getFarBank() {
if(farSegment!=null)
return farSegment.getFarBank();
else
return 0L;
}
public String getInterruptType() {

@ -107,13 +107,13 @@ public class CParser {
*/
public static final String PRAGMA_RESOURCE = "resource";
/**
* #pragma far_seg(...) specifies the scope of the sequent functions to be far. Segments are defined in the linker file.
* #pragma far(...) specifies the scope of the sequent functions to be far. Segments are defined in the linker file.
*/
public static final String PRAGMA_FAR_SEG = "far_seg";
public static final String PRAGMA_FAR = "far";
/**
* #pragma near_seg specifies the scope of the sequent functions to be near. Segments are defined in the linker file.
* #pragma near specifies the scope of the sequent functions to be near. Segments are defined in the linker file.
*/
public static final String PRAGMA_NEAR_SEG = "near_seg";
public static final String PRAGMA_NEAR = "near";
/**
* The Program.

@ -43,10 +43,6 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
private final Stack<Scope> scopeStack;
/** All #pragma constructor_for() statements. Collected during parsing and handled by {@link #generate()} before returning. */
private final List<KickCParser.PragmaContext> pragmaConstructorFors;
/** All #pragma code segments. Collected during parsing. These are used by the far_seg pragmas to validate if the code segment exists during compilation.*/
private final Map<String, KickCParser.PragmaContext> pragmaCodeSegs;
/** All #pragma far segments. Collected during parsing. These are used to compare with the current currentFarSegment to decide a near or a far call, and to keep inline calling routines.*/
private final Map<String, FarSegment> pragmaFarSegs;
public Pass0GenerateStatementSequence(CParser cParser, KickCParser.FileContext fileCtx, Program program, Procedure.CallingConvention initialCallingConvention, StringEncoding defaultEncoding, String defaultInterruptType) {
@ -57,8 +53,6 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
this.currentCallingConvention = initialCallingConvention;
this.currentEncoding = defaultEncoding;
this.pragmaConstructorFors = new ArrayList();
this.pragmaCodeSegs = new HashMap<>(); // Used to collect all pragma code segments.
this.pragmaFarSegs = new HashMap<>(); // Used to collect all pragma far segments.
this.currentInterruptType = defaultInterruptType;
scopeStack.push(program.getScope());
}
@ -137,7 +131,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
Procedure initProc = program.getScope().getLocalProcedure(SymbolRef.INIT_PROC_NAME);
if(initProc == null) {
// Create the _init() procedure
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);
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, null);
initProc.setDeclaredInline(true);
initProc.setParameters(new ArrayList<>());
program.getScope().add(initProc);
@ -194,7 +188,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
// Add the _start() procedure to the program
{
program.setStartProcedure(new ProcedureRef(SymbolRef.START_PROC_NAME));
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);
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, null);
startProcedure.setParameters(new ArrayList<>());
program.getScope().add(startProcedure);
final ProcedureCompilation startProcedureCompilation = program.createProcedureCompilation(startProcedure.getRef());
@ -296,27 +290,24 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
break;
case CParser.PRAGMA_CODE_SEG:
this.currentCodeSegment = pragmaParamName(pragmaParamSingle(ctx));
this.pragmaCodeSegs.put(this.currentCodeSegment, ctx);
this.program.getPragmaCodeSegs().put(this.currentCodeSegment, ctx);
break;
case CParser.PRAGMA_DATA_SEG:
this.currentDataSegment = pragmaParamName(pragmaParamSingle(ctx));
break;
case CParser.PRAGMA_FAR_SEG:
case CParser.PRAGMA_FAR:
try {
final int size = ctx.getChildCount();
if(size==7) {
final String pragmaFarSegment = pragmaParamFarSegment(ctx.pragmaParam(0));
final Number pragmaFarBank = pragmaParamNumber(ctx.pragmaParam(1));
if (this.pragmaCodeSegs.get(pragmaFarSegment) != null) {
this.currentFarSegment = pragmaFarSegment;
if (size > 7) {
final String call_prepare = pragmaParamName(ctx.pragmaParam(2));
final String call_execute = pragmaParamName(ctx.pragmaParam(3));
final String call_finalize = pragmaParamName(ctx.pragmaParam(4));
this.pragmaFarSegs.put(this.currentFarSegment, new FarSegment(pragmaFarSegment, pragmaFarBank.intValue(), call_prepare, call_execute, call_finalize));
} else {
this.pragmaFarSegs.put(this.currentFarSegment, new FarSegment(pragmaFarSegment, pragmaFarBank.intValue(), "", "", ""));
}
if (size > 7) {
final String call_prepare = pragmaParamName(ctx.pragmaParam(2));
final String call_execute = pragmaParamName(ctx.pragmaParam(3));
final String call_finalize = pragmaParamName(ctx.pragmaParam(4));
this.currentFarSegment = new FarSegment(pragmaFarSegment, pragmaFarBank.longValue(), call_prepare, call_execute, call_finalize);
} else {
this.currentFarSegment = new FarSegment(pragmaFarSegment, pragmaFarBank.longValue(), "", "", "");
}
} else {
throw new CompileError("Expected at least 2 pragma parameters. Found '" + ctx.getText() + "'.", new StatementSource(ctx));
@ -325,8 +316,8 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
throw new CompileError("Illegal parameter " + ctx.getText(), new StatementSource(ctx));
}
break;
case CParser.PRAGMA_NEAR_SEG:
this.currentFarSegment = ""; // When the current far segment is "", any function that is far will be called as far.
case CParser.PRAGMA_NEAR:
this.currentFarSegment = null; // When the current far segment is null, any function that is far will be called as far.
break;
case CParser.PRAGMA_RESOURCE:
String resourceFileName = pragmaParamString(pragmaParamSingle(ctx));
@ -402,17 +393,17 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
}
/**
* Parse the FAR_SEG parameter of a #pragma
* If the parameter is not a FAR_SEG the compiler will fail out
* Parse the FAR parameter of a #pragma
* If the parameter is not a FAR the compiler will fail out
*
* @param paramCtx The parameter to parse
* @return The name
*/
private String pragmaParamFarSegment(KickCParser.PragmaParamContext paramCtx) {
if(!(paramCtx instanceof KickCParser.PragmaParamNameContext))
throw new CompileError("Expected a FAR_SEG parameter. Found '" + paramCtx.getText() + "'.", new StatementSource(paramCtx.getParent()));
throw new CompileError("Expected a FAR parameter. Found '" + paramCtx.getText() + "'.", new StatementSource(paramCtx.getParent()));
final String pragmaFarSegment = ((KickCParser.PragmaParamNameContext) paramCtx).NAME().getText();
if(this.pragmaCodeSegs.get(pragmaFarSegment) != null) {
if(this.program.getPragmaCodeSegs().get(pragmaFarSegment) != null) {
return pragmaFarSegment;
} else {
throw new CompileError("Expected a previously declared CODE_SEG parameter. Found '" + paramCtx.getText() + "'.", new StatementSource(paramCtx.getParent()));
@ -488,8 +479,8 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
/** The current data segment. */
private String currentDataSegment = Scope.SEGMENT_DATA_DEFAULT;
/** The current code segment. */
private String currentFarSegment = Scope.SEGMENT_FAR_DEFAULT;
/** The current far segment. */
private FarSegment currentFarSegment;
/** The current default interrupt type. */
private String currentInterruptType;
@ -546,7 +537,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
*/
private Procedure declareProcedure(boolean defineProcedure, ParserRuleContext ctx, StatementSource statementSource) {
Procedure procedure = new Procedure(varDecl.getVarName(), (SymbolTypeProcedure) varDecl.getEffectiveType(), program.getScope(), currentCodeSegment, currentDataSegment, currentCallingConvention);
Procedure procedure = new Procedure(varDecl.getVarName(), (SymbolTypeProcedure) varDecl.getEffectiveType(), program.getScope(), currentCodeSegment, currentDataSegment, currentCallingConvention, currentFarSegment);
addDirectives(procedure, varDecl.getDeclDirectives(), statementSource);
// Check if the declaration matches any existing declaration!
final Symbol existingSymbol = program.getScope().getSymbol(procedure.getRef());
@ -1226,8 +1217,8 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
procedure.setDeclaredInline(true);
procedure.setCallingConvention(Procedure.CallingConvention.PHI_CALL);
} else if(directive instanceof Directive.Far) {
procedure.setDeclaredFar(true);
procedure.setBankFar(((Directive.Far) directive).bankFar);
FarSegment farSegment = new FarSegment(((Directive.Far) directive).getFarSegmentName(), ((Directive.Far) directive).getFarSegmentBank(), ((Directive.Far) directive).getFarProcedurePrepare(), ((Directive.Far) directive).getFarProcedureExecute(), ((Directive.Far) directive).getFarProcedureFinalize());
procedure.setFarSegment(farSegment);
} else if(directive instanceof Directive.CallingConvention) {
procedure.setCallingConvention(((Directive.CallingConvention) directive).callingConvention);
} else if(directive instanceof Directive.Interrupt) {
@ -1273,13 +1264,31 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
@Override
public Object visitDirectiveFar(KickCParser.DirectiveFarContext ctx) {
Long bankFar;
if(ctx.getChildCount() > 1) {
bankFar = Long.valueOf(ctx.getChild(2).getText());
} else {
bankFar = 0L;
String farSegmentName = "";
Long farSegmentBank = 0L;
String farProcedurePrepare = null;
String farProcedureExecute = null;
String farProcedureFinalize = null;
if(this.currentFarSegment != null) {
farSegmentName = this.currentFarSegment.getFarSegment();
farSegmentBank = this.currentFarSegment.getFarBank();
farProcedurePrepare = this.currentFarSegment.getProcedurePrepare();
farProcedureExecute = this.currentFarSegment.getProcedureExecute();
farProcedureFinalize = this.currentFarSegment.getProcedureFinalize();
}
return new Directive.Far(bankFar);
if(ctx.getChildCount() >= 5) {
farSegmentName = ctx.getChild(2).getText();
farSegmentBank = Long.valueOf(ctx.getChild(4).getText());
}
if(ctx.getChildCount() == 11) {
farProcedurePrepare = ctx.getChild(6).getText();
farProcedureExecute = ctx.getChild(8).getText();
farProcedureFinalize = ctx.getChild(10).getText();
}
return new Directive.Far(farSegmentName, farSegmentBank, farProcedurePrepare, farProcedureExecute, farProcedureFinalize);
}
@Override
@ -2539,7 +2548,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
new SymbolTypeProcedure(SymbolType.DWORD, Arrays.asList(new SymbolType[]{SymbolType.BYTE, SymbolType.BYTE, SymbolType.BYTE, SymbolType.BYTE})),
program.getScope(),
Scope.SEGMENT_CODE_DEFAULT, Scope.SEGMENT_DATA_DEFAULT,
Procedure.CallingConvention.INTRINSIC_CALL);
Procedure.CallingConvention.INTRINSIC_CALL, null);
makeword4.setDeclaredIntrinsic(true);
final Variable hihi = new Variable("hihi", Variable.Kind.PHI_MASTER, SymbolType.BYTE, makeword4, Variable.MemoryArea.ZEROPAGE_MEMORY, Scope.SEGMENT_DATA_DEFAULT, null);
makeword4.add(hihi);

@ -65,7 +65,7 @@ public class Pass1ProcedureFar extends Pass1Base {
// The all properties have been entered in the __far() directive in the source code.
// These properties are then used in pass4 of the compiler, to build the platform dependent fragment to execute the far call.
call.setBankFar(procedure.getBankFar());
// call.setBankFar(procedure.getBankFar());
getLog().append("Far call " + call.toString(getProgram(), false));
}
}

@ -847,6 +847,7 @@ public class Pass4CodeGeneration {
} else if (statement instanceof StatementCall) {
StatementCall call = (StatementCall) statement;
Procedure procedure = getScope().getProcedure(call.getProcedure());
Procedure procedureFrom = block.getProcedure(this.program); // We obtain from where the procedure is called, to validate the bank equality.
if (procedure.isDeclaredIntrinsic()) {
if (Pass1ByteXIntrinsicRewrite.INTRINSIC_MAKELONG4.equals(procedure.getFullName())) {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.makelong4(call, program), program);
@ -865,31 +866,52 @@ public class Pass4CodeGeneration {
genBlockPhiTransition(asm, block, callSuccessor, block.getScope());
}
}
if(procedure.isDeclaredFar()) {
// Generate ASM for a call (in a bank or other)
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallEntry(procedure.getBankFar(), call.getProcedure().getFullName(), program), program);
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallExit(procedure.getBankFar(), call.getProcedure().getFullName(), program), program);
// Note: I've chosen to keep this code duplication between phi and stack calling convention, for later maintenance flexibility, if any.
// We check if the procedure is declared as far, and if the calling procedure is not in the same bank as the procedure called.
if(procedure.isDeclaredFar() && procedureFrom.getFarBank() != procedure.getFarBank()) {
// In this case, Generate ASM for a far call.
// The call is constructed in a prepare, execute and finalize compiler .asm fragments respectively.
// The bank and other preparations are set in the far_call_[platform]_prepare.asm fragment.
// The actual jsr statement is embedded in the far_call_[platform]_execute.asm fragment.
// After the jsr, finalization of the call is defined in the far_call_[platform]_finalize.asm fragment.
// TODO: rework to prepare, execute, finalize
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallPrepare(procedure.getFarSegment().getFarBank(), call.getProcedure().getFullName(), program), program);
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallExecute(procedure.getFarSegment().getFarBank(), call.getProcedure().getFullName(), program), program);
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallFinalize(procedure.getFarSegment().getFarBank(), call.getProcedure().getFullName(), program), program);
// asm.addInstruction("jsr far", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
} else {
// Otherwise, Generate AM for a normal near call.
// In case of a far call, we assume the bank does not need to be changed.
asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
}
} else if (Procedure.CallingConvention.STACK_CALL.equals(procedure.getCallingConvention())) {
if(procedure.isDeclaredFar()) {
// Generate ASM for a far call (in a bank or other)
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallEntry(procedure.getBankFar(), call.getProcedure().getFullName(), program), program);
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallExit(procedure.getBankFar(), call.getProcedure().getFullName(), program), program);
// Note: I've chosen to keep this code duplication between phi and stack calling convention, for later maintenance flexibility, if any.
// We check if the procedure is declared as far, and if the calling procedure is not in the same bank as the procedure called.
if(procedure.isDeclaredFar() && procedure.getFarBank() != procedureFrom.getFarBank()) {
// In this case, Generate ASM for a far call.
// The call is constructed in a prepare, execute and finalize compiler .asm fragments respectively.
// The bank and other preparations are set in the far_call_[platform]_prepare.asm fragment.
// The actual jsr statement is embedded in the far_call_[platform]_execute.asm fragment.
// After the jsr, finalization of the call is defined in the far_call_[platform]_finalize.asm fragment.
// TODO: rework to prepare, execute, finalize
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallPrepare(procedure.getFarSegment().getFarBank(), call.getProcedure().getFullName(), program), program);
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallExecute(procedure.getFarSegment().getFarBank(), call.getProcedure().getFullName(), program), program);
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallFinalize(procedure.getFarSegment().getFarBank(), call.getProcedure().getFullName(), program), program);
} else {
asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
}
}
} else if (statement instanceof StatementCallExecute) {
// TODO: This part seems never to be executed! Old code?
StatementCallExecute call = (StatementCallExecute) statement;
Procedure procedure = getScope().getProcedure(call.getProcedure());
Procedure procedureFrom = block.getProcedure(this.program); // We obtain from where the procedure is called, to validate the bank equality.
RValue procedureRVal = call.getProcedureRVal();
// Generate ASM for a call
if(procedure.isDeclaredFar()) {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallEntry(procedure.getBankFar(), call.getProcedure().getFullName(), program), program);
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallExit(procedure.getBankFar(), call.getProcedure().getFullName(), program), program);
if(procedure.isDeclaredFar() && procedureFrom.getFarBank() != procedure.getFarBank()) {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallPrepare(procedure.getFarSegment().getFarBank(), call.getProcedure().getFullName(), program), program);
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallExecute(procedure.getFarSegment().getFarBank(), call.getProcedure().getFullName(), program), program);
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallFinalize(procedure.getFarSegment().getFarBank(), call.getProcedure().getFullName(), program), program);
} else {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program);
}

@ -1496,13 +1496,38 @@ public class TestProgramsFast extends TestPrograms {
}
@Test
public void testProcedureCallingConventionStackFar13() throws IOException {
compileAndCompare("procedure-callingconvention-stack-far-13.c");
public void testProcedureCallingConventionPhiFarx() throws IOException {
compileAndCompare("procedure-callingconvention-phi-far-x.c");
}
@Test
public void testProcedureCallingConventionStackFar6() throws IOException {
compileAndCompare("procedure-callingconvention-stack-far-6.c");
public void testProcedureCallingConventionPhiFar0() throws IOException {
compileAndCompare("procedure-callingconvention-phi-far-0.c");
}
@Test
public void testProcedureCallingConventionPhiFar1() throws IOException {
compileAndCompare("procedure-callingconvention-phi-far-1.c");
}
@Test
public void testProcedureCallingConventionPhiFar2() throws IOException {
compileAndCompare("procedure-callingconvention-phi-far-2.c");
}
@Test
public void testProcedureCallingConventionPhiFar3() throws IOException {
compileAndCompare("procedure-callingconvention-phi-far-3.c");
}
@Test
public void testProcedureCallingConventionPhiFar4() throws IOException {
compileAndCompare("procedure-callingconvention-phi-far-4.c");
}
@Test
public void testProcedureCallingConventionPhiFar5() throws IOException {
compileAndCompare("procedure-callingconvention-phi-far-5.c");
}
@Test
@ -1535,11 +1560,6 @@ public class TestProgramsFast extends TestPrograms {
compileAndCompare("procedure-callingconvention-stack-far-0.c");
}
@Test
public void testProcedureCallingConventionPhiFar0() throws IOException {
compileAndCompare("procedure-callingconvention-phi-far-0.c");
}
@Test
public void testProcedureCallingConventionStack13() throws IOException {
compileAndCompare("procedure-callingconvention-stack-13.c");

@ -6,6 +6,7 @@ void main(void) {
SCREEN[0] = plus('0', 7);
}
char __far(1) plus(char a, char b) {
#pragma code_seg(stage)
char __far(stage,2) plus(char a, char b) {
return a+b;
}

@ -7,7 +7,7 @@ void main(void) {
}
#pragma code_seg(stage)
#pragma far_seg(stage, 1)
#pragma far(stage, 1)
char plus(char a, char b) {
return a+b;

@ -7,7 +7,7 @@ void main(void) {
}
#pragma code_seg(stage)
#pragma far_seg(rubbish, 1, stage_prepare, stage_execution, stage_exit)
#pragma far(rubbish, 1, stage_prepare, stage_execution, stage_exit)
char plus(char a, char b) {
return a+b;

@ -7,7 +7,7 @@ void main(void) {
}
#pragma code_seg(stage)
#pragma far_seg(stage)
#pragma far(stage)
char plus(char a, char b) {
return a+b;

@ -3,13 +3,13 @@
char* const SCREEN = (char*)0x0400;
#pragma code_seg(stage)
#pragma far_seg(stage, 20)
#pragma far(stage, 20)
char plus(char a, char b) {
return a+b;
}
#pragma near_seg
#pragma near
void main(void) {
SCREEN[0] = plus('0', 7);

@ -0,0 +1,100 @@
// Test a far call procedure with a calling convention phi
char* const SCREEN = (char*)0x0400;
#pragma code_seg(bank_1)
#pragma far(bank_1, 1)
char func_bank1_a(char a, char b) {
return a+b;
}
char func_bank1_c(char a, char b) {
// this should be a near call, because the call is from the same bank.
return func_bank1_a(a,b);
}
char func_bank1_d(char a, char b) {
// this should be a far call, because the call is to an other bank.
return func_bank2_a(a,b);
}
#pragma code_seg(bank_2)
#pragma far(bank_2, 2)
char func_bank2_a(char a, char b) {
return a+b;
}
char func_bank2_c(char a, char b) {
// this should be a far call, because the call is from another bank.
return func_bank1_a(a,b);
}
char func_bank2_d(char a, char b) {
// this should be a near call, because the call is from the same bank.
return func_bank2_a(a,b);
}
char func_bank2_e(char a, char b) {
// this should be a near call, because the call is from the same bank.
return func_bank2_b(a,b);
}
char func_bank2_f(char a, char b) {
// this should be a far call, because the call is from another bank.
return func_bank1_b(a,b);
}
#pragma near
char __far(bank_1, 1) func_bank1_b(char a, char b) {
return a+b;
}
char __far(bank_2, 2) func_bank2_b(char a, char b) {
return a+b;
}
#pragma near
char func_bank1_e(char a, char b) {
// this should be a far call, because the call is to bank 1.
return func_bank1_a(a,b);
}
char func_bank1_f(char a, char b) {
// this should be a far call, because the call is to bank 2.
return func_bank2_a(a,b);
}
#pragma code_seg(Code)
void main(void) {
// far call
SCREEN[0] = func_bank1_a('0', 7);
// far call
SCREEN[0] = func_bank1_b('0', 7);
// far call
SCREEN[0] = func_bank1_c('0', 7);
// far call
SCREEN[0] = func_bank1_d('0', 7);
// near call
SCREEN[0] = func_bank1_e('0', 7);
// near call
SCREEN[0] = func_bank1_f('0', 7);
// far call
SCREEN[0] = func_bank2_a('0', 7);
// far call
SCREEN[0] = func_bank2_b('0', 7);
// far call
SCREEN[0] = func_bank2_c('0', 7);
// far call
SCREEN[0] = func_bank2_d('0', 7);
// far call
SCREEN[0] = func_bank2_e('0', 7);
// far call
SCREEN[0] = func_bank2_f('0', 7);
}

@ -6,6 +6,7 @@ void main(void) {
SCREEN[0] = plus('0', 7);
}
char __far(1) __stackcall plus(char a, char b) {
#pragma code_seg(test)
char __far(test, 2) __stackcall plus(char a, char b) {
return a+b;
}

@ -7,7 +7,8 @@ void main(void) {
}
#pragma calling(__stackcall)
#pragma code_seg(test)
char __far(1) plus(char a, char b) {
char __far(test,20) plus(char a, char b) {
return a+b;
}

@ -13,7 +13,8 @@ void main(void) {
}
}
char __far(1) __stackcall plus(char a, char b) {
// this should give a pragma error during compile, as test is not declared yet.
char __far(test, 20) __stackcall plus(char a, char b) {
i++;
return a+b;
}

@ -1,15 +1,50 @@
// Test a procedure with calling convention stack
// Recursive fibonacci
// Illustrates live range problem with function variable printother::i and global variable val
#pragma calling(__stackcall)
char* const SCREEN = (char*)0x0400;
char val = 0;
void main(void) {
*SCREEN = fib(5);
for(char i:0..5) {
pval();
printother();
ival();
}
}
char __far(1) __stackcall fib(char n) {
if (n == 0 || n == 1)
return n;
else
return (fib(n-1) + fib(n-2));
}
#pragma code_seg(test)
#pragma code_seg(test2)
#pragma code_seg(test3)
#pragma code_seg(Code)
void __far(test, 20) pval() {
printval();
}
void __far(test2, 21) ival() {
incval();
}
void __far(test, 20) printval() {
SCREEN[0] = val;
}
void __far(test2, 21) incval() {
val++;
}
#pragma near
void printother() {
for(char i:0..5) {
(SCREEN+40)[i]++;
}
}

@ -1,40 +1,32 @@
// Test a procedure with calling convention stack
// Illustrates live range problem with function variable printother::i and global variable val
// Returning and passing struct values
#pragma calling(__stackcall)
#pragma struct_model(classic)
char* const SCREEN = (char*)0x0400;
char idx = 0;
char val = 0;
struct Point {
char x;
char y;
};
void main(void) {
for(char i:0..5) {
pval();
printother();
ival();
for(char i=0;i<5;i++) {
struct Point p = get(i);
print(p);
}
}
void __far(1) pval() {
printval();
struct Point __far(1) get(char i) {
struct Point p = { i, i/2 };
return p;
}
void __far(2) ival() {
incval();
}
void __far(3) printval() {
SCREEN[0] = val;
}
void __far(4) incval() {
val++;
}
void __far(5) printother() {
for(char i:0..5) {
(SCREEN+40)[i]++;
}
void __far(2) print(struct Point p) {
SCREEN[idx++] = p.x;
SCREEN[idx++] = p.y;
}

@ -1,35 +1,18 @@
// Test a procedure with calling convention stack
// Returning and passing struct values
#pragma calling(__stackcall)
#pragma struct_model(classic)
// Recursion that works (no local variables)
char* const SCREEN = (char*)0x0400;
char idx = 0;
struct Point {
char x;
char y;
};
void main(void) {
for(char i=0;i<5;i++) {
struct Point p = get(i);
print(p);
}
*SCREEN = pow2(6);
}
struct Point __far(1) get(char i) {
struct Point p = { i, i/2 };
return p;
}
void __far(2) print(struct Point p) {
SCREEN[idx++] = p.x;
SCREEN[idx++] = p.y;
}
char __stackcall __far(1) pow2(char n) {
if (n == 0)
return 1;
else {
char c = pow2(n-1);
return c+c;
}
}

@ -1,18 +0,0 @@
// Test a procedure with calling convention stack
// Recursion that works (no local variables)
char* const SCREEN = (char*)0x0400;
void main(void) {
*SCREEN = pow2(6);
}
char __stackcall __far(1) pow2(char n) {
if (n == 0)
return 1;
else {
char c = pow2(n-1);
return c+c;
}
}

@ -0,0 +1,145 @@
// File Comments
// Test a procedure with calling convention stack
// Illustrates live range problem with function variable printother::i and global variable val
// Upstart
.cpu _65c02
// Commodore 64 PRG executable file
.file [name="procedure-callingconvention-stack-far-3.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(__start)
// Global Constants & labels
.label SCREEN = $400
.label val = 3
.segment Code
// __start
__start: {
// __start::__init1
// char val = 0
// [1] val = 0 -- vbuz1=vbuc1
lda #0
sta.z val
// [2] phi from __start::__init1 to __start::@1 [phi:__start::__init1->__start::@1]
// __start::@1
// [3] callexecute main -- call_vprc1
jsr main
// __start::@return
// [4] return
rts
}
// printother
printother: {
.label i = 2
// [6] phi from printother to printother::@1 [phi:printother->printother::@1]
// [6] phi printother::i#2 = 0 [phi:printother->printother::@1#0] -- vbuz1=vbuc1
lda #0
sta.z i
// [6] phi from printother::@1 to printother::@1 [phi:printother::@1->printother::@1]
// [6] phi printother::i#2 = printother::i#1 [phi:printother::@1->printother::@1#0] -- register_copy
// printother::@1
__b1:
// (SCREEN+40)[i]++;
// [7] (SCREEN+$28)[printother::i#2] = ++ (SCREEN+$28)[printother::i#2] -- pbuc1_derefidx_vbuz1=_inc_pbuc1_derefidx_vbuz1
ldx.z i
inc SCREEN+$28,x
// for(char i:0..5)
// [8] printother::i#1 = ++ printother::i#2 -- vbuz1=_inc_vbuz1
inc.z i
// [9] if(printother::i#1!=6) goto printother::@1 -- vbuz1_neq_vbuc1_then_la1
lda #6
cmp.z i
bne __b1
// printother::@return
// }
// [10] return
rts
}
// incval
incval: {
// val++;
// [11] val = ++ val -- vbuz1=_inc_vbuz1
inc.z val
// incval::@return
// }
// [12] return
rts
}
// printval
printval: {
// SCREEN[0] = val
// [13] *SCREEN = val -- _deref_pbuc1=vbuz1
lda.z val
sta SCREEN
// printval::@return
// }
// [14] return
rts
}
// ival
ival: {
// incval()
// [16] callexecute incval -- call_far_cx16_exit
jsr $ff6e
.byte <incval
.byte >incval
.byte $15
// ival::@return
// }
// [17] return
rts
}
// pval
pval: {
// printval()
// [19] callexecute printval -- call_far_cx16_exit
jsr $ff6e
.byte <printval
.byte >printval
.byte $14
// pval::@return
// }
// [20] return
rts
}
// main
main: {
.label i = 4
// [22] phi from main to main::@1 [phi:main->main::@1]
// [22] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbuz1=vbuc1
lda #0
sta.z i
// [22] phi from main::@1 to main::@1 [phi:main::@1->main::@1]
// [22] phi main::i#2 = main::i#1 [phi:main::@1->main::@1#0] -- register_copy
// main::@1
__b1:
// pval()
// [23] callexecute pval -- call_far_cx16_exit
jsr $ff6e
.byte <pval
.byte >pval
.byte $14
// printother()
// [24] callexecute printother -- call_vprc1
jsr printother
// ival()
// [25] callexecute ival -- call_far_cx16_exit
jsr $ff6e
.byte <ival
.byte >ival
.byte $15
// for(char i:0..5)
// [26] main::i#1 = ++ main::i#2 -- vbuz1=_inc_vbuz1
inc.z i
// [27] if(main::i#1!=6) goto main::@1 -- vbuz1_neq_vbuc1_then_la1
lda #6
cmp.z i
bne __b1
// main::@return
// }
// [28] return
rts
}
// File Data

@ -0,0 +1,153 @@
<C64debugger version="1.0">
<Sources values="INDEX,FILE">
0,KickAss.jar:/include/autoinclude.asm
1,D:\Users\svenv\OneDrive\Documents\GitHub\kickc\src\test\target\procedure-callingconvention-stack-far-3.asm
</Sources>
<Segment name="Default" dest="" values="START,END,FILE_IDX,LINE1,COL1,LINE2,COL2">
</Segment>
<Segment name="Program" dest="" values="START,END,FILE_IDX,LINE1,COL1,LINE2,COL2">
<Block name="Basic">
$0801,$0802,0,38,2,38,6
$0803,$0804,0,39,5,39,9
$0805,$0805,0,40,5,40,9
$0806,$0809,0,41,2,41,6
$080a,$080a,0,42,2,42,6
$080b,$080c,0,44,5,44,9
</Block>
<Block name="Code">
$080d,$080e,1,23,5,23,7
$080f,$0810,1,24,5,24,7
$0811,$0813,1,28,5,28,7
$0814,$0814,1,31,5,31,7
$0815,$0816,1,38,5,38,7
$0817,$0818,1,39,5,39,7
$0819,$081a,1,46,5,46,7
$081b,$081d,1,47,5,47,7
$081e,$081f,1,50,5,50,7
$0820,$0821,1,52,5,52,7
$0822,$0823,1,53,5,53,7
$0824,$0825,1,54,5,54,7
$0826,$0826,1,58,5,58,7
$0827,$0828,1,64,5,64,7
$0829,$0829,1,68,5,68,7
$082a,$082b,1,74,5,74,7
$082c,$082e,1,75,5,75,7
$082f,$082f,1,79,5,79,7
$0830,$0832,1,85,5,85,7
$0833,$0833,1,86,5,86,9
$0834,$0834,1,87,5,87,9
$0835,$0835,1,88,5,88,9
$0836,$0836,1,92,5,92,7
$0837,$0839,1,98,5,98,7
$083a,$083a,1,99,5,99,9
$083b,$083b,1,100,5,100,9
$083c,$083c,1,101,5,101,9
$083d,$083d,1,105,5,105,7
$083e,$083f,1,112,5,112,7
$0840,$0841,1,113,5,113,7
$0842,$0844,1,120,5,120,7
$0845,$0845,1,121,5,121,9
$0846,$0846,1,122,5,122,9
$0847,$0847,1,123,5,123,9
$0848,$084a,1,126,5,126,7
$084b,$084d,1,129,5,129,7
$084e,$084e,1,130,5,130,9
$084f,$084f,1,131,5,131,9
$0850,$0850,1,132,5,132,9
$0851,$0852,1,135,5,135,7
$0853,$0854,1,137,5,137,7
$0855,$0856,1,138,5,138,7
$0857,$0858,1,139,5,139,7
$0859,$0859,1,143,5,143,7
</Block>
</Segment>
<Segment name="Basic" dest="" values="START,END,FILE_IDX,LINE1,COL1,LINE2,COL2">
<Block name="Basic">
$0801,$0802,0,38,2,38,6
$0803,$0804,0,39,5,39,9
$0805,$0805,0,40,5,40,9
$0806,$0809,0,41,2,41,6
$080a,$080a,0,42,2,42,6
$080b,$080c,0,44,5,44,9
</Block>
</Segment>
<Segment name="Code" dest="" values="START,END,FILE_IDX,LINE1,COL1,LINE2,COL2">
<Block name="Code">
$080d,$080e,1,23,5,23,7
$080f,$0810,1,24,5,24,7
$0811,$0813,1,28,5,28,7
$0814,$0814,1,31,5,31,7
$0815,$0816,1,38,5,38,7
$0817,$0818,1,39,5,39,7
$0819,$081a,1,46,5,46,7
$081b,$081d,1,47,5,47,7
$081e,$081f,1,50,5,50,7
$0820,$0821,1,52,5,52,7
$0822,$0823,1,53,5,53,7
$0824,$0825,1,54,5,54,7
$0826,$0826,1,58,5,58,7
$0827,$0828,1,64,5,64,7
$0829,$0829,1,68,5,68,7
$082a,$082b,1,74,5,74,7
$082c,$082e,1,75,5,75,7
$082f,$082f,1,79,5,79,7
$0830,$0832,1,85,5,85,7
$0833,$0833,1,86,5,86,9
$0834,$0834,1,87,5,87,9
$0835,$0835,1,88,5,88,9
$0836,$0836,1,92,5,92,7
$0837,$0839,1,98,5,98,7
$083a,$083a,1,99,5,99,9
$083b,$083b,1,100,5,100,9
$083c,$083c,1,101,5,101,9
$083d,$083d,1,105,5,105,7
$083e,$083f,1,112,5,112,7
$0840,$0841,1,113,5,113,7
$0842,$0844,1,120,5,120,7
$0845,$0845,1,121,5,121,9
$0846,$0846,1,122,5,122,9
$0847,$0847,1,123,5,123,9
$0848,$084a,1,126,5,126,7
$084b,$084d,1,129,5,129,7
$084e,$084e,1,130,5,130,9
$084f,$084f,1,131,5,131,9
$0850,$0850,1,132,5,132,9
$0851,$0852,1,135,5,135,7
$0853,$0854,1,137,5,137,7
$0855,$0856,1,138,5,138,7
$0857,$0858,1,139,5,139,7
$0859,$0859,1,143,5,143,7
</Block>
</Segment>
<Segment name="Data" dest="" values="START,END,FILE_IDX,LINE1,COL1,LINE2,COL2">
</Segment>
<Labels values="SEGMENT,ADDRESS,NAME,START,END,FILE_IDX,LINE1,COL1,LINE2,COL2">
Basic,$0400,SCREEN,1,15,10,15,15
Basic,$0003,val,1,16,10,16,12
Code,$080d,__start,1,19,1,19,8
Code,$0815,printother,1,34,1,34,11
Code,$0002,i,1,35,12,35,12
Code,$0819,__b1,1,43,3,43,7
Code,$0827,incval,1,61,1,61,7
Code,$082a,printval,1,71,1,71,9
Code,$0830,ival,1,82,1,82,5
Code,$0837,pval,1,95,1,95,5
Code,$083e,main,1,108,1,108,5
Code,$0004,i,1,109,12,109,12
Code,$0842,__b1,1,117,3,117,7
Basic,$080b,upstartEnd,0,43,1,43,11
</Labels>
<Breakpoints values="SEGMENT,ADDRESS,ARGUMENT">
</Breakpoints>
<Watchpoints values="SEGMENT,ADDRESS1,ADDRESS2,ARGUMENT">
</Watchpoints>
</C64debugger>

@ -0,0 +1,22 @@
Output dir: D:\Users\svenv\OneDrive\Documents\GitHub\kickc\src\test\target
parsing
flex pass 1
flex pass 2
flex pass 3
Output pass
Writing prg file: procedure-callingconvention-stack-far-3.prg
Memory Map
----------
Program-segment:
Basic-segment:
$0801-$080c Basic
Code-segment:
$080d-$0859 Code
Data-segment:
Writing Vice symbol file: procedure-callingconvention-stack-far-3.vs
Writing Symbol file: procedure-callingconvention-stack-far-3.sym

Binary file not shown.

@ -0,0 +1,20 @@
.label val=$3
.label SCREEN=$400
.label __start=$80d {
}
.label pval=$837 {
}
.label ival=$830 {
}
.label incval=$827 {
}
.label main=$83e {
.label __b1=$842
.label i=$4
}
.label printother=$815 {
.label __b1=$819
.label i=$2
}
.label printval=$82a {
}

@ -0,0 +1,14 @@
al C:3 .val
al C:819 .__b1__0
al C:842 .__b1__1
al C:400 .SCREEN
al C:80d .__start
al C:837 .pval
al C:2 .i__0
al C:4 .i__1
al C:830 .ival
al C:80b .upstartEnd
al C:827 .incval
al C:83e .main
al C:815 .printother
al C:82a .printval