diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index c44b5d5b0..13901b4c8 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -121,6 +121,7 @@ public class Compiler { new Pass1AssertNoLValueIntermediate(program).execute(); new Pass1AddTypePromotions(program).execute(); new Pass1AssertNoRecursion(program).execute(); + new Pass1AssertInterrupts(program).execute(); getLog().append("INITIAL CONTROL FLOW GRAPH"); getLog().append(program.getGraph().toString(program)); 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 f87374478..7acf16c25 100644 --- a/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java +++ b/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java @@ -16,11 +16,13 @@ public class Procedure extends Scope { private final SymbolType returnType; private List parameterNames; private boolean declaredInline; + private boolean declaredInterrupt; public Procedure(String name, SymbolType returnType, Scope parentScope) { super(name, parentScope); this.returnType = returnType; this.declaredInline = false; + this.declaredInterrupt = false; } public List getParameterNames() { @@ -73,6 +75,18 @@ public class Procedure extends Scope { return declaredInline; } + public void setDeclaredInline(boolean declaredInline) { + this.declaredInline = declaredInline; + } + + public boolean isDeclaredInterrupt() { + return declaredInterrupt; + } + + public void setDeclaredInterrupt(boolean declaredInterrupt) { + this.declaredInterrupt = declaredInterrupt; + } + @Override public String toString() { return toString(null); @@ -84,6 +98,9 @@ public class Procedure extends Scope { if(declaredInline) { res.append("inline "); } + if(declaredInterrupt) { + res.append("interrupt "); + } res.append("(" + getType().getTypeName() + ") "); res.append(getFullName()); res.append("("); @@ -123,8 +140,4 @@ public class Procedure extends Scope { return result; } - public void setDeclaredInline(boolean declaredInline) { - this.declaredInline = declaredInline; - } - } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index e221d9bdd..49a8fb623 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -348,6 +348,8 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { StatementSource source = new StatementSource(directivesCtx.get(0)); if(directive instanceof DirectiveInline) { procedure.setDeclaredInline(true); + } else if(directive instanceof DirectiveInterrupt) { + procedure.setDeclaredInterrupt(true); } else { throw new CompileError("Unsupported function directive " + directive, source); } @@ -364,6 +366,11 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { return new DirectiveInline(); } + @Override + public Object visitDirectiveInterrupt(KickCParser.DirectiveInterruptContext ctx) { + return new DirectiveInterrupt(); + } + @Override public Directive visitDirectiveAlign(KickCParser.DirectiveAlignContext ctx) { Number alignment = NumberParser.parseLiteral(ctx.NUMBER().getText()); @@ -831,6 +838,10 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { private static class DirectiveInline implements Directive { } + /** Function declared interrupt. */ + private static class DirectiveInterrupt implements Directive { + } + /** Variable memory alignment. */ private static class DirectiveAlign implements Directive { private int alignment; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1AssertInterrupts.java b/src/main/java/dk/camelot64/kickc/passes/Pass1AssertInterrupts.java new file mode 100644 index 000000000..cf03db580 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1AssertInterrupts.java @@ -0,0 +1,40 @@ +package dk.camelot64.kickc.passes; + +import dk.camelot64.kickc.model.CompileError; +import dk.camelot64.kickc.model.ControlFlowBlock; +import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.statements.Statement; +import dk.camelot64.kickc.model.statements.StatementCall; +import dk.camelot64.kickc.model.symbols.Procedure; +import dk.camelot64.kickc.model.values.ProcedureRef; + +/** Asserts that interrupts are never called and are not declared inline */ +public class Pass1AssertInterrupts extends Pass1Base { + + public Pass1AssertInterrupts(Program program) { + super(program); + } + + @Override + public boolean step() { + for(ControlFlowBlock block : getGraph().getAllBlocks()) { + for(Statement statement : block.getStatements()) { + if(statement instanceof StatementCall) { + ProcedureRef procedureRef = ((StatementCall) statement).getProcedure(); + Procedure procedure = getScope().getProcedure(procedureRef); + if(procedure.isDeclaredInterrupt()) { + throw new CompileError("Error! Interrupts cannot be called.", statement.getSource()); + } + } + } + for(Procedure procedure : getScope().getAllProcedures(true)) { + if(procedure.isDeclaredInline() && procedure.isDeclaredInterrupt()) { + throw new CompileError("Error! Interrupts cannot be inlined. " + procedure.toString()); + } + } + } + return false; + } + + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureInline.java b/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureInline.java index b02729ad7..8e4f9df28 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureInline.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureInline.java @@ -37,6 +37,9 @@ public class Pass1ProcedureInline extends Pass1Base { ProcedureRef procedureRef = call.getProcedure(); Procedure procedure = getScope().getProcedure(procedureRef); if(procedure.isDeclaredInline()) { + if(procedure.isDeclaredInterrupt()) { + throw new CompileError("Error! Interrupts cannot be inlined. "+procedure.getRef().toString()); + } Scope callScope = getScope().getScope(block.getScope()); // Remove call statementsIt.remove(); diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 5d3d6e194..7b7656e8b 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -881,6 +881,15 @@ public class TestPrograms { assertError("no-mod-runtime", "Runtime modulo not supported"); } + @Test + public void testNoInlineInterrupt() throws IOException, URISyntaxException { + assertError("no-inlineinterrupt", "Interrupts cannot be inlined"); + } + + @Test + public void testNoCalledInterrupt() throws IOException, URISyntaxException { + assertError("no-calledinterrupt", "Interrupts cannot be called."); + } private void assertError(String kcFile, String expectError) throws IOException, URISyntaxException { try { diff --git a/src/test/java/dk/camelot64/kickc/test/kc/multiplexer.kc b/src/test/java/dk/camelot64/kickc/test/kc/multiplexer.kc index 73da5d3dc..b63580f5f 100644 --- a/src/test/java/dk/camelot64/kickc/test/kc/multiplexer.kc +++ b/src/test/java/dk/camelot64/kickc/test/kc/multiplexer.kc @@ -1,4 +1,4 @@ -// A flexible sprite multiplexer routine for 24 sprites. +// A flexible sprite multiplexer routine for 32 sprites. // Usage: // - Once: // - plexInit(screen): Initialize the data structures and set the screen address @@ -9,8 +9,8 @@ // - plexFreeNextYpos() Returns the Y-position where the next sprite is available to be shown (ie. the next pos where the next sprite is no longer in use showing something else). // - plexShowNextYpos() Returns the Y-position of the next sprite to show. // -// In practice a good method is to wait until the raster is beyond plexFreeNextYpos() and then call plexShowSprite(). Repeat until all 24 sprites have been shown. -// TODO: Let the caller specify the number of sprites (PLEX_COUNT) +// In practice a good method is to wait until the raster is beyond plexFreeNextYpos() and then call plexShowSprite(). Repeat until all 32 sprites have been shown. +// TODO: Let the caller specify the number of sprites to use (or add PLEX_ENABLE[PLEX_COUNT]) import "c64" diff --git a/src/test/java/dk/camelot64/kickc/test/kc/no-calledinterrupt.kc b/src/test/java/dk/camelot64/kickc/test/kc/no-calledinterrupt.kc new file mode 100644 index 000000000..6204ed70d --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/kc/no-calledinterrupt.kc @@ -0,0 +1,12 @@ +// Test that inline interrupts not allowed + +byte* SCREEN = $400; + +void main() { + SCREEN[0]++; + irq(); +} + +interrupt void irq() { + SCREEN[1]++; +} \ No newline at end of file diff --git a/src/test/java/dk/camelot64/kickc/test/kc/no-inlineinterrupt.kc b/src/test/java/dk/camelot64/kickc/test/kc/no-inlineinterrupt.kc new file mode 100644 index 000000000..1cfef84bc --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/kc/no-inlineinterrupt.kc @@ -0,0 +1,11 @@ +// Test that inline interrupts not allowed + +byte* SCREEN = $400; + +void main() { + SCREEN[0]++; +} + +inline interrupt void irq() { + SCREEN[1]++; +} \ No newline at end of file diff --git a/src/test/java/dk/camelot64/kickc/test/kc/simple-multiplexer.kc b/src/test/java/dk/camelot64/kickc/test/kc/simple-multiplexer.kc index 635a6fcc4..4ef2ce299 100644 --- a/src/test/java/dk/camelot64/kickc/test/kc/simple-multiplexer.kc +++ b/src/test/java/dk/camelot64/kickc/test/kc/simple-multiplexer.kc @@ -3,8 +3,8 @@ import "c64" import "multiplexer" // Location of screen & sprites -byte* SPRITE = $2000; byte* SCREEN = $400; +byte* SPRITE = $2000; byte* YSIN = $2100; kickasm(pc YSIN) {{ diff --git a/src/test/java/dk/camelot64/kickc/test/ref/simple-multiplexer.log b/src/test/java/dk/camelot64/kickc/test/ref/simple-multiplexer.log index e94ed7781..e6cd65cd3 100644 --- a/src/test/java/dk/camelot64/kickc/test/ref/simple-multiplexer.log +++ b/src/test/java/dk/camelot64/kickc/test/ref/simple-multiplexer.log @@ -194,7 +194,7 @@ inline void vicSelectGfxBank(byte* gfx) { Importing multiplexer PARSING src/test/java/dk/camelot64/kickc/test/kc/multiplexer.kc -// A flexible sprite multiplexer routine for 24 sprites. +// A flexible sprite multiplexer routine for 32 sprites. // Usage: // - Once: // - plexInit(screen): Initialize the data structures and set the screen address @@ -205,8 +205,8 @@ PARSING src/test/java/dk/camelot64/kickc/test/kc/multiplexer.kc // - plexFreeNextYpos() Returns the Y-position where the next sprite is available to be shown (ie. the next pos where the next sprite is no longer in use showing something else). // - plexShowNextYpos() Returns the Y-position of the next sprite to show. // -// In practice a good method is to wait until the raster is beyond plexFreeNextYpos() and then call plexShowSprite(). Repeat until all 24 sprites have been shown. -// TODO: Let the caller specify the number of sprites (PLEX_COUNT) +// In practice a good method is to wait until the raster is beyond plexFreeNextYpos() and then call plexShowSprite(). Repeat until all 32 sprites have been shown. +// TODO: Let the caller specify the number of sprites to use (or add PLEX_ENABLE[PLEX_COUNT]) import "c64"