1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-08-08 13:25:12 +00:00

Parsing implementation of

#pragma far_seg(segment, bank, call_prepare, call_execute, call_finalize)
with variable parameters, other option is:
   #pragma far_seg(segment, bank)

After parsing a FarSegment HashMap is created that is later used for the far or near call determination.
Added currentFarSegment variable.
Added near in KickCLexer for __near directive.
Added far_seg and near_seg #pragma keywords.

near_seg is to be parsed in the next commit.
This commit is contained in:
Flight_Control
2022-11-19 07:45:07 +01:00
parent 2f2028f3a1
commit 1ef785a254
8 changed files with 185 additions and 0 deletions

View File

@@ -76,6 +76,7 @@ ADDRESS: '__address' ;
ADDRESS_ZEROPAGE: '__zp' ; ADDRESS_ZEROPAGE: '__zp' ;
ADDRESS_MAINMEM: '__mem' ; ADDRESS_MAINMEM: '__mem' ;
FAR: '__far' ; FAR: '__far' ;
NEAR: '__near' ;
FORM_SSA: '__ssa' ; FORM_SSA: '__ssa' ;
FORM_MA: '__ma' ; FORM_MA: '__ma' ;
INTRINSIC: '__intrinsic' ; INTRINSIC: '__intrinsic' ;

View File

@@ -0,0 +1,32 @@
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;
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 String getName() {
return name;
}
public Integer getBank() {
return bank;
}
}

View File

@@ -16,6 +16,8 @@ public abstract class Scope implements Symbol {
public static final String SEGMENT_CODE_DEFAULT = "Code"; public static final String SEGMENT_CODE_DEFAULT = "Code";
/** The default data segment. */ /** The default data segment. */
public static final String SEGMENT_DATA_DEFAULT = "Data"; public static final String SEGMENT_DATA_DEFAULT = "Data";
/** The default far segment. */
public static final String SEGMENT_FAR_DEFAULT = "";
private String name; private String name;
private HashMap<String, Symbol> symbols; private HashMap<String, Symbol> symbols;

View File

@@ -106,6 +106,14 @@ public class CParser {
* The resource-file is copied to the output directory when compiling. * The resource-file is copied to the output directory when compiling.
*/ */
public static final String PRAGMA_RESOURCE = "resource"; 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.
*/
public static final String PRAGMA_FAR_SEG = "far_seg";
/**
* #pragma near_seg 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";
/** /**
* The Program. * The Program.

View File

@@ -43,6 +43,10 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
private final Stack<Scope> scopeStack; private final Stack<Scope> scopeStack;
/** All #pragma constructor_for() statements. Collected during parsing and handled by {@link #generate()} before returning. */ /** All #pragma constructor_for() statements. Collected during parsing and handled by {@link #generate()} before returning. */
private final List<KickCParser.PragmaContext> pragmaConstructorFors; 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) { public Pass0GenerateStatementSequence(CParser cParser, KickCParser.FileContext fileCtx, Program program, Procedure.CallingConvention initialCallingConvention, StringEncoding defaultEncoding, String defaultInterruptType) {
@@ -53,6 +57,8 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
this.currentCallingConvention = initialCallingConvention; this.currentCallingConvention = initialCallingConvention;
this.currentEncoding = defaultEncoding; this.currentEncoding = defaultEncoding;
this.pragmaConstructorFors = new ArrayList(); 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; this.currentInterruptType = defaultInterruptType;
scopeStack.push(program.getScope()); scopeStack.push(program.getScope());
} }
@@ -290,10 +296,35 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
break; break;
case CParser.PRAGMA_CODE_SEG: case CParser.PRAGMA_CODE_SEG:
this.currentCodeSegment = pragmaParamName(pragmaParamSingle(ctx)); this.currentCodeSegment = pragmaParamName(pragmaParamSingle(ctx));
this.pragmaCodeSegs.put(this.currentCodeSegment, ctx);
break; break;
case CParser.PRAGMA_DATA_SEG: case CParser.PRAGMA_DATA_SEG:
this.currentDataSegment = pragmaParamName(pragmaParamSingle(ctx)); this.currentDataSegment = pragmaParamName(pragmaParamSingle(ctx));
break; break;
case CParser.PRAGMA_FAR_SEG:
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(), "", "", ""));
}
}
} else {
throw new CompileError("Expected at least 2 pragma parameters. Found '" + ctx.getText() + "'.", new StatementSource(ctx));
}
} catch(IllegalArgumentException e) {
throw new CompileError("Illegal parameter " + ctx.getText(), new StatementSource(ctx));
}
break;
case CParser.PRAGMA_RESOURCE: case CParser.PRAGMA_RESOURCE:
String resourceFileName = pragmaParamString(pragmaParamSingle(ctx)); String resourceFileName = pragmaParamString(pragmaParamSingle(ctx));
addResourceFile(ctx, resourceFileName); addResourceFile(ctx, resourceFileName);
@@ -367,6 +398,24 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
return callingConvention; return callingConvention;
} }
/**
* Parse the FAR_SEG parameter of a #pragma
* If the parameter is not a FAR_SEG 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()));
final String pragmaFarSegment = ((KickCParser.PragmaParamNameContext) paramCtx).NAME().getText();
if(this.pragmaCodeSegs.get(pragmaFarSegment) != null) {
return pragmaFarSegment;
} else {
throw new CompileError("Expected a previously declared CODE_SEG parameter. Found '" + paramCtx.getText() + "'.", new StatementSource(paramCtx.getParent()));
}
}
/** /**
* Parse a single NAME parameter of a #pragma * Parse a single NAME parameter of a #pragma
@@ -436,6 +485,9 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
/** The current data segment. */ /** The current data segment. */
private String currentDataSegment = Scope.SEGMENT_DATA_DEFAULT; private String currentDataSegment = Scope.SEGMENT_DATA_DEFAULT;
/** The current code segment. */
private String currentFarSegment = Scope.SEGMENT_FAR_DEFAULT;
/** The current default interrupt type. */ /** The current default interrupt type. */
private String currentInterruptType; private String currentInterruptType;

View File

@@ -0,0 +1,30 @@
// Test a far call procedure with a calling convention sp
char* const SCREEN = (char*)0x0400;
void main(void) {
SCREEN[0] = plus('0', 7);
}
#pragma code_seg(stage)
#pragma far_seg(stage, 1)
char plus(char a, char b) {
return a+b;
}
void stage_entry() {
asm {
lda 0
pha
lda #1
sta 0
}
}
void stage_exit() {
asm {
pla
sta 0
}
}

View File

@@ -0,0 +1,30 @@
// Test a far call procedure with a calling convention sp
char* const SCREEN = (char*)0x0400;
void main(void) {
SCREEN[0] = plus('0', 7);
}
#pragma code_seg(stage)
#pragma far_seg(rubbish, 1, stage_prepare, stage_execution, stage_exit)
char plus(char a, char b) {
return a+b;
}
void stage_entry() {
asm {
lda 0
pha
lda #1
sta 0
}
}
void stage_exit() {
asm {
pla
sta 0
}
}

View File

@@ -0,0 +1,30 @@
// Test a far call procedure with a calling convention sp
char* const SCREEN = (char*)0x0400;
void main(void) {
SCREEN[0] = plus('0', 7);
}
#pragma code_seg(stage)
#pragma far_seg(stage)
char plus(char a, char b) {
return a+b;
}
void stage_entry() {
asm {
lda 0
pha
lda #1
sta 0
}
}
void stage_exit() {
asm {
pla
sta 0
}
}