1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-04-21 11:42:30 +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_MAINMEM: '__mem' ;
FAR: '__far' ;
NEAR: '__near' ;
FORM_SSA: '__ssa' ;
FORM_MA: '__ma' ;
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";
/** The default data segment. */
public static final String SEGMENT_DATA_DEFAULT = "Data";
/** The default far segment. */
public static final String SEGMENT_FAR_DEFAULT = "";
private String name;
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.
*/
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.

View File

@ -43,6 +43,10 @@ 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) {
@ -53,6 +57,8 @@ 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());
}
@ -290,10 +296,35 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
break;
case CParser.PRAGMA_CODE_SEG:
this.currentCodeSegment = pragmaParamName(pragmaParamSingle(ctx));
this.pragmaCodeSegs.put(this.currentCodeSegment, ctx);
break;
case CParser.PRAGMA_DATA_SEG:
this.currentDataSegment = pragmaParamName(pragmaParamSingle(ctx));
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:
String resourceFileName = pragmaParamString(pragmaParamSingle(ctx));
addResourceFile(ctx, resourceFileName);
@ -367,6 +398,24 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
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
@ -436,6 +485,9 @@ 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 default interrupt type. */
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
}
}