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 77d545bed..ec9b6d930 100644 --- a/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java +++ b/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java @@ -100,8 +100,14 @@ public class Procedure extends Scope { HARDWARE_ALL, /** Interrupt served directly from hardware through $fffe-f. Will exit through RTI and will save necessary registers based on clobber. */ HARDWARE_CLOBBER + ; + + /** The default interrupt type if none is explicitly declared (KERNEL_MIN). */ + public static InterruptType DEFAULT = InterruptType.KERNEL_MIN; + } + @Override public String toString() { return toString(null); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index 72945e58e..d59fd7e4a 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -410,9 +410,12 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { @Override public Object visitDirectiveInterrupt(KickCParser.DirectiveInterruptContext ctx) { - String interruptType = "KERNEL_MIN"; + String interruptType; if(ctx.getChildCount() > 1) { interruptType = ctx.getChild(2).getText().toUpperCase(); + } else { + // The default interrupt type + interruptType = Procedure.InterruptType.DEFAULT.name(); } Procedure.InterruptType type = Procedure.InterruptType.valueOf(interruptType); return new DirectiveInterrupt(type); diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index f4175741b..ad15be343 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -132,6 +132,11 @@ public class TestPrograms { compileAndCompare("test-interrupt"); } + @Test + public void testInterruptNoType() throws IOException, URISyntaxException { + compileAndCompare("test-interrupt-notype"); + } + @Test public void testMultiplexer() throws IOException, URISyntaxException { compileAndCompare("simple-multiplexer", 10); diff --git a/src/test/java/dk/camelot64/kickc/test/kc/test-interrupt-notype.kc b/src/test/java/dk/camelot64/kickc/test/kc/test-interrupt-notype.kc new file mode 100644 index 000000000..da3493fae --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/kc/test-interrupt-notype.kc @@ -0,0 +1,17 @@ +const void()** KERNEL_IRQ = $0314; +const byte* BGCOL = $d020; +const byte* FGCOL = $d021; + +void main() { + *KERNEL_IRQ = &irq; + while(true) { + (*FGCOL)++; + } +} + +interrupt void irq() { + (*BGCOL)++; + asm { + lda $dc0d + } +} diff --git a/src/test/java/dk/camelot64/kickc/test/ref/test-interrupt-notype.asm b/src/test/java/dk/camelot64/kickc/test/ref/test-interrupt-notype.asm new file mode 100644 index 000000000..948b5dd6c --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/ref/test-interrupt-notype.asm @@ -0,0 +1,21 @@ +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label KERNEL_IRQ = $314 + .label BGCOL = $d020 + .label FGCOL = $d021 + jsr main +main: { + lda #irq + sta KERNEL_IRQ+1 + b2: + inc FGCOL + jmp b2 +} +irq: { + inc BGCOL + lda $dc0d + jmp $ea81 +} diff --git a/src/test/java/dk/camelot64/kickc/test/ref/test-interrupt-notype.cfg b/src/test/java/dk/camelot64/kickc/test/ref/test-interrupt-notype.cfg new file mode 100644 index 000000000..4601ca983 --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/ref/test-interrupt-notype.cfg @@ -0,0 +1,22 @@ +@begin: scope:[] from + [0] phi() [ ] ( ) + to:@2 +@2: scope:[] from @begin + [1] phi() [ ] ( ) + [2] call main [ ] ( ) + to:@end +@end: scope:[] from @2 + [3] phi() [ ] ( ) +main: scope:[main] from @2 + [4] *((const void()**) KERNEL_IRQ#0) ← &interrupt(KERNEL_MIN)(void()) irq() [ ] ( main:2 [ ] ) + to:main::@2 +main::@2: scope:[main] from main main::@2 + [5] *((const byte*) FGCOL#0) ← ++ *((const byte*) FGCOL#0) [ ] ( main:2 [ ] ) + to:main::@2 +irq: scope:[irq] from + [6] *((const byte*) BGCOL#0) ← ++ *((const byte*) BGCOL#0) [ ] ( ) + asm { lda$dc0d } + to:irq::@return +irq::@return: scope:[irq] from irq + [8] return [ ] ( ) + to:@return diff --git a/src/test/java/dk/camelot64/kickc/test/ref/test-interrupt-notype.log b/src/test/java/dk/camelot64/kickc/test/ref/test-interrupt-notype.log new file mode 100644 index 000000000..a93169989 --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/ref/test-interrupt-notype.log @@ -0,0 +1,306 @@ +Resolved forward reference irq to interrupt(KERNEL_MIN)(void()) irq() + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (void()**) KERNEL_IRQ#0 ← ((void()**)) (word/signed word/dword/signed dword) 788 + (byte*) BGCOL#0 ← ((byte*)) (word/dword/signed dword) 53280 + (byte*) FGCOL#0 ← ((byte*)) (word/dword/signed dword) 53281 + to:@2 +main: scope:[main] from @2 + (void()*~) main::$0 ← & interrupt(KERNEL_MIN)(void()) irq() + *((void()**) KERNEL_IRQ#0) ← (void()*~) main::$0 + to:main::@1 +main::@1: scope:[main] from main main::@2 + if(true) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + *((byte*) FGCOL#0) ← ++ *((byte*) FGCOL#0) + to:main::@1 +main::@return: scope:[main] from main::@1 + return + to:@return +irq: scope:[irq] from + *((byte*) BGCOL#0) ← ++ *((byte*) BGCOL#0) + asm { lda$dc0d } + to:irq::@return +irq::@return: scope:[irq] from irq + return + to:@return +@2: scope:[] from @begin + call main + to:@3 +@3: scope:[] from @2 + to:@end +@end: scope:[] from @3 + +SYMBOL TABLE SSA +(label) @2 +(label) @3 +(label) @begin +(label) @end +(byte*) BGCOL +(byte*) BGCOL#0 +(byte*) FGCOL +(byte*) FGCOL#0 +(void()**) KERNEL_IRQ +(void()**) KERNEL_IRQ#0 +interrupt(KERNEL_MIN)(void()) irq() +(label) irq::@return +(void()) main() +(void()*~) main::$0 +(label) main::@1 +(label) main::@2 +(label) main::@return + +Culled Empty Block (label) @3 +Successful SSA optimization Pass2CullEmptyBlocks +Constant (const void()**) KERNEL_IRQ#0 = ((void()**))788 +Constant (const byte*) BGCOL#0 = ((byte*))53280 +Constant (const byte*) FGCOL#0 = ((byte*))53281 +Constant (const void()*) main::$0 = &irq +Successful SSA optimization Pass2ConstantIdentification +if() condition always true - replacing block destination if(true) goto main::@2 +Successful SSA optimization Pass2ConstantIfs +Removing unused block main::@return +Successful SSA optimization Pass2EliminateUnusedBlocks +Culled Empty Block (label) main::@1 +Successful SSA optimization Pass2CullEmptyBlocks +Constant inlined main::$0 = &interrupt(KERNEL_MIN)(void()) irq() +Successful SSA optimization Pass2ConstantInlining +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +CALL GRAPH +Calls in [] to main:2 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() [ ] ( ) + to:@2 +@2: scope:[] from @begin + [1] phi() [ ] ( ) + [2] call main [ ] ( ) + to:@end +@end: scope:[] from @2 + [3] phi() [ ] ( ) +main: scope:[main] from @2 + [4] *((const void()**) KERNEL_IRQ#0) ← &interrupt(KERNEL_MIN)(void()) irq() [ ] ( main:2 [ ] ) + to:main::@2 +main::@2: scope:[main] from main main::@2 + [5] *((const byte*) FGCOL#0) ← ++ *((const byte*) FGCOL#0) [ ] ( main:2 [ ] ) + to:main::@2 +irq: scope:[irq] from + [6] *((const byte*) BGCOL#0) ← ++ *((const byte*) BGCOL#0) [ ] ( ) + asm { lda$dc0d } + to:irq::@return +irq::@return: scope:[irq] from irq + [8] return [ ] ( ) + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte*) BGCOL +(byte*) FGCOL +(void()**) KERNEL_IRQ +interrupt(KERNEL_MIN)(void()) irq() +(void()) main() + +Initial phi equivalence classes +Complete equivalence classes + +INITIAL ASM +//SEG0 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG1 Global Constants & labels + .label KERNEL_IRQ = $314 + .label BGCOL = $d020 + .label FGCOL = $d021 +//SEG2 @begin +bbegin: +//SEG3 [1] phi from @begin to @2 [phi:@begin->@2] +b2_from_bbegin: + jmp b2 +//SEG4 @2 +b2: +//SEG5 [2] call main [ ] ( ) + jsr main +//SEG6 [3] phi from @2 to @end [phi:@2->@end] +bend_from_b2: + jmp bend +//SEG7 @end +bend: +//SEG8 main +main: { + //SEG9 [4] *((const void()**) KERNEL_IRQ#0) ← &interrupt(KERNEL_MIN)(void()) irq() [ ] ( main:2 [ ] ) -- _deref_pptc1=pprc2 + lda #irq + sta KERNEL_IRQ+1 + jmp b2 + //SEG10 main::@2 + b2: + //SEG11 [5] *((const byte*) FGCOL#0) ← ++ *((const byte*) FGCOL#0) [ ] ( main:2 [ ] ) -- _deref_pbuc1=_inc__deref_pbuc1 + inc FGCOL + jmp b2 +} +//SEG12 irq +irq: { + //SEG13 entry interrupt(KERNEL_MIN) + //SEG14 [6] *((const byte*) BGCOL#0) ← ++ *((const byte*) BGCOL#0) [ ] ( ) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BGCOL + //SEG15 asm { lda$dc0d } + lda $dc0d + jmp breturn + //SEG16 irq::@return + breturn: + //SEG17 [8] return [ ] ( ) - exit interrupt(KERNEL_MIN) + jmp $ea81 +} + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] *((const void()**) KERNEL_IRQ#0) ← &interrupt(KERNEL_MIN)(void()) irq() [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement asm { lda$dc0d } always clobbers reg byte a + +REGISTER UPLIFT SCOPES +Uplift Scope [main] +Uplift Scope [irq] +Uplift Scope [] + +Uplifting [main] best 133 combination +Uplifting [irq] best 133 combination +Uplifting [] best 133 combination + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG1 Global Constants & labels + .label KERNEL_IRQ = $314 + .label BGCOL = $d020 + .label FGCOL = $d021 +//SEG2 @begin +bbegin: +//SEG3 [1] phi from @begin to @2 [phi:@begin->@2] +b2_from_bbegin: + jmp b2 +//SEG4 @2 +b2: +//SEG5 [2] call main [ ] ( ) + jsr main +//SEG6 [3] phi from @2 to @end [phi:@2->@end] +bend_from_b2: + jmp bend +//SEG7 @end +bend: +//SEG8 main +main: { + //SEG9 [4] *((const void()**) KERNEL_IRQ#0) ← &interrupt(KERNEL_MIN)(void()) irq() [ ] ( main:2 [ ] ) -- _deref_pptc1=pprc2 + lda #irq + sta KERNEL_IRQ+1 + jmp b2 + //SEG10 main::@2 + b2: + //SEG11 [5] *((const byte*) FGCOL#0) ← ++ *((const byte*) FGCOL#0) [ ] ( main:2 [ ] ) -- _deref_pbuc1=_inc__deref_pbuc1 + inc FGCOL + jmp b2 +} +//SEG12 irq +irq: { + //SEG13 entry interrupt(KERNEL_MIN) + //SEG14 [6] *((const byte*) BGCOL#0) ← ++ *((const byte*) BGCOL#0) [ ] ( ) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BGCOL + //SEG15 asm { lda$dc0d } + lda $dc0d + jmp breturn + //SEG16 irq::@return + breturn: + //SEG17 [8] return [ ] ( ) - exit interrupt(KERNEL_MIN) + jmp $ea81 +} + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b2 +Removing instruction jmp bend +Removing instruction jmp b2 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction bbegin: +Removing instruction b2_from_bbegin: +Removing instruction bend_from_b2: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction b2: +Removing instruction bend: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @2 +(label) @begin +(label) @end +(byte*) BGCOL +(const byte*) BGCOL#0 BGCOL = ((byte*))(word/dword/signed dword) 53280 +(byte*) FGCOL +(const byte*) FGCOL#0 FGCOL = ((byte*))(word/dword/signed dword) 53281 +(void()**) KERNEL_IRQ +(const void()**) KERNEL_IRQ#0 KERNEL_IRQ = ((void()**))(word/signed word/dword/signed dword) 788 +interrupt(KERNEL_MIN)(void()) irq() +(label) irq::@return +(void()) main() +(label) main::@2 + + + +FINAL ASSEMBLER +Score: 121 + +//SEG0 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG1 Global Constants & labels + .label KERNEL_IRQ = $314 + .label BGCOL = $d020 + .label FGCOL = $d021 +//SEG2 @begin +//SEG3 [1] phi from @begin to @2 [phi:@begin->@2] +//SEG4 @2 +//SEG5 [2] call main [ ] ( ) + jsr main +//SEG6 [3] phi from @2 to @end [phi:@2->@end] +//SEG7 @end +//SEG8 main +main: { + //SEG9 [4] *((const void()**) KERNEL_IRQ#0) ← &interrupt(KERNEL_MIN)(void()) irq() [ ] ( main:2 [ ] ) -- _deref_pptc1=pprc2 + lda #irq + sta KERNEL_IRQ+1 + //SEG10 main::@2 + b2: + //SEG11 [5] *((const byte*) FGCOL#0) ← ++ *((const byte*) FGCOL#0) [ ] ( main:2 [ ] ) -- _deref_pbuc1=_inc__deref_pbuc1 + inc FGCOL + jmp b2 +} +//SEG12 irq +irq: { + //SEG13 entry interrupt(KERNEL_MIN) + //SEG14 [6] *((const byte*) BGCOL#0) ← ++ *((const byte*) BGCOL#0) [ ] ( ) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BGCOL + //SEG15 asm { lda$dc0d } + lda $dc0d + //SEG16 irq::@return + //SEG17 [8] return [ ] ( ) - exit interrupt(KERNEL_MIN) + jmp $ea81 +} + diff --git a/src/test/java/dk/camelot64/kickc/test/ref/test-interrupt-notype.sym b/src/test/java/dk/camelot64/kickc/test/ref/test-interrupt-notype.sym new file mode 100644 index 000000000..536f88f86 --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/ref/test-interrupt-notype.sym @@ -0,0 +1,14 @@ +(label) @2 +(label) @begin +(label) @end +(byte*) BGCOL +(const byte*) BGCOL#0 BGCOL = ((byte*))(word/dword/signed dword) 53280 +(byte*) FGCOL +(const byte*) FGCOL#0 FGCOL = ((byte*))(word/dword/signed dword) 53281 +(void()**) KERNEL_IRQ +(const void()**) KERNEL_IRQ#0 KERNEL_IRQ = ((void()**))(word/signed word/dword/signed dword) 788 +interrupt(KERNEL_MIN)(void()) irq() +(label) irq::@return +(void()) main() +(label) main::@2 +