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 9228d1acd..8fb164811 100644 --- a/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java +++ b/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java @@ -143,6 +143,8 @@ public class Procedure extends Scope { HARDWARE_NONE, /** Interrupt served directly from hardware through $fffe-f. Will exit through RTI and will save ALL registers. */ HARDWARE_ALL, + /** Interrupt served directly from hardware through $fffe-f. Will exit through RTI and will save ALL registers using the stack. */ + HARDWARE_STACK, /** Interrupt served directly from hardware through $fffe-f. Will exit through RTI and will save necessary registers based on clobber. */ HARDWARE_CLOBBER; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index ba6ca4b50..69067d434 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -853,7 +853,6 @@ public class Pass4CodeGeneration { private void generateInterruptEntry(AsmProgram asm, Procedure procedure) { Procedure.InterruptType interruptType = procedure.getInterruptType(); asm.startChunk(procedure.getRef(), null, "entry interrupt(" + interruptType.name() + ")"); - //asm.getCurrentChunk().setXXX(); if(Procedure.InterruptType.KERNEL_MIN.equals(interruptType)) { // No entry ASM needed } else if(Procedure.InterruptType.KERNEL_KEYBOARD.equals(interruptType)) { @@ -862,6 +861,12 @@ public class Pass4CodeGeneration { asm.addInstruction("sta", AsmAddressingMode.ABS, "rega+1", false).setDontOptimize(true); asm.addInstruction("stx", AsmAddressingMode.ABS, "regx+1", false).setDontOptimize(true); asm.addInstruction("sty", AsmAddressingMode.ABS, "regy+1", false).setDontOptimize(true); + } else if(Procedure.InterruptType.HARDWARE_STACK.equals(interruptType)) { + asm.addInstruction("pha", AsmAddressingMode.NON, null, false).setDontOptimize(true); + asm.addInstruction("txa", AsmAddressingMode.NON, null, false).setDontOptimize(true); + asm.addInstruction("pha", AsmAddressingMode.NON, null, false).setDontOptimize(true); + asm.addInstruction("tya", AsmAddressingMode.NON, null, false).setDontOptimize(true); + asm.addInstruction("pha", AsmAddressingMode.NON, null, false).setDontOptimize(true); } else if(Procedure.InterruptType.HARDWARE_NONE.equals(interruptType)) { // No entry ASM needed } else if(Procedure.InterruptType.HARDWARE_CLOBBER.equals(interruptType)) { @@ -894,6 +899,13 @@ public class Pass4CodeGeneration { asm.addLabel("regy").setDontOptimize(true); asm.addInstruction("ldy", AsmAddressingMode.IMM, "00", false).setDontOptimize(true); asm.addInstruction("rti", AsmAddressingMode.NON, null, false); + } else if(Procedure.InterruptType.HARDWARE_STACK.equals(interruptType)) { + asm.addInstruction("pla", AsmAddressingMode.NON, null, false).setDontOptimize(true); + asm.addInstruction("tay", AsmAddressingMode.NON, null, false).setDontOptimize(true); + asm.addInstruction("pla", AsmAddressingMode.NON, null, false).setDontOptimize(true); + asm.addInstruction("tax", AsmAddressingMode.NON, null, false).setDontOptimize(true); + asm.addInstruction("pla", AsmAddressingMode.NON, null, false).setDontOptimize(true); + asm.addInstruction("rti", AsmAddressingMode.NON, null, false); } else if(Procedure.InterruptType.HARDWARE_NONE.equals(interruptType)) { asm.addInstruction("rti", AsmAddressingMode.NON, null, false); } else if(Procedure.InterruptType.HARDWARE_CLOBBER.equals(interruptType)) { diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index ebe2ff5c9..3d97d336b 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -1884,6 +1884,11 @@ public class TestPrograms { compileAndCompare("irq-hardware-clobber"); } + @Test + public void testIrqHardwareStack() throws IOException, URISyntaxException { + compileAndCompare("irq-hardware-stack"); + } + @Test public void testIrqHardware() throws IOException, URISyntaxException { compileAndCompare("irq-hardware"); diff --git a/src/test/kc/irq-hardware-stack.kc b/src/test/kc/irq-hardware-stack.kc new file mode 100644 index 000000000..62e945e87 --- /dev/null +++ b/src/test/kc/irq-hardware-stack.kc @@ -0,0 +1,58 @@ +// A minimal working raster IRQ + +const void()** KERNEL_IRQ = $0314; +const void()** HARDWARE_IRQ = $fffe; +const byte* RASTER = $d012; +const byte* VIC_CONTROL = $d011; +const byte* IRQ_STATUS = $d019; +const byte* IRQ_ENABLE = $d01a; +const byte IRQ_RASTER = %00000001; +const byte IRQ_COLLISION_BG = %00000010; +const byte IRQ_COLLISION_SPRITE = %00000100; +const byte IRQ_LIGHTPEN = %00001000; +const byte* BGCOL = $d020; +const byte* FGCOL = $d021; +const byte WHITE = 1; +const byte BLACK = 0; + +const byte* CIA1_INTERRUPT = $dc0d; +const byte CIA_INTERRUPT_CLEAR = $7f; + +// Processor port data direction register +const byte* PROCPORT_DDR = $00; +// Mask for PROCESSOR_PORT_DDR which allows only memory configuration to be written +const byte PROCPORT_DDR_MEMORY_MASK = %00000111; + +// Processor Port Register controlling RAM/ROM configuration and the datasette +const byte* PROCPORT = $01; +// RAM in $A000, $E000 I/O in $D000 +const byte PROCPORT_RAM_IO = %00110101; +// RAM in $A000, $E000 CHAR ROM in $D000 + +void main() { + asm { sei } + // Disable kernal & basic + *PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK; + *PROCPORT = PROCPORT_RAM_IO; + // Disable CIA 1 Timer IRQ + *CIA1_INTERRUPT = CIA_INTERRUPT_CLEAR; + // Set raster line to $100 + *VIC_CONTROL |=$80; + *RASTER = $00; + // Enable Raster Interrupt + *IRQ_ENABLE = IRQ_RASTER; + // Set the IRQ routine + *HARDWARE_IRQ = &irq; + asm { cli } + while(true) { + (*FGCOL)++; + } +} + +// Interrupt Routine +interrupt(hardware_stack) void irq() { + *BGCOL = WHITE; + *BGCOL = BLACK; + // Acknowledge the IRQ + *IRQ_STATUS = IRQ_RASTER; +} \ No newline at end of file diff --git a/src/test/ref/irq-hardware-stack.asm b/src/test/ref/irq-hardware-stack.asm new file mode 100644 index 000000000..29c2fbd44 --- /dev/null +++ b/src/test/ref/irq-hardware-stack.asm @@ -0,0 +1,75 @@ +// A minimal working raster IRQ +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label HARDWARE_IRQ = $fffe + .label RASTER = $d012 + .label VIC_CONTROL = $d011 + .label IRQ_STATUS = $d019 + .label IRQ_ENABLE = $d01a + .const IRQ_RASTER = 1 + .label BGCOL = $d020 + .label FGCOL = $d021 + .const WHITE = 1 + .const BLACK = 0 + .label CIA1_INTERRUPT = $dc0d + .const CIA_INTERRUPT_CLEAR = $7f + // Processor port data direction register + .label PROCPORT_DDR = 0 + // Mask for PROCESSOR_PORT_DDR which allows only memory configuration to be written + .const PROCPORT_DDR_MEMORY_MASK = 7 + // Processor Port Register controlling RAM/ROM configuration and the datasette + .label PROCPORT = 1 + // RAM in $A000, $E000 I/O in $D000 + .const PROCPORT_RAM_IO = $35 +// RAM in $A000, $E000 CHAR ROM in $D000 +main: { + sei + // Disable kernal & basic + lda #PROCPORT_DDR_MEMORY_MASK + sta PROCPORT_DDR + lda #PROCPORT_RAM_IO + sta PROCPORT + // Disable CIA 1 Timer IRQ + lda #CIA_INTERRUPT_CLEAR + sta CIA1_INTERRUPT + // Set raster line to $100 + lda #$80 + ora VIC_CONTROL + sta VIC_CONTROL + lda #0 + sta RASTER + // Enable Raster Interrupt + lda #IRQ_RASTER + sta IRQ_ENABLE + // Set the IRQ routine + lda #irq + sta HARDWARE_IRQ+1 + cli + b1: + inc FGCOL + jmp b1 +} +// Interrupt Routine +irq: { + pha + txa + pha + tya + pha + lda #WHITE + sta BGCOL + lda #BLACK + sta BGCOL + // Acknowledge the IRQ + lda #IRQ_RASTER + sta IRQ_STATUS + pla + tay + pla + tax + pla + rti +} diff --git a/src/test/ref/irq-hardware-stack.cfg b/src/test/ref/irq-hardware-stack.cfg new file mode 100644 index 000000000..dd5bef1b5 --- /dev/null +++ b/src/test/ref/irq-hardware-stack.cfg @@ -0,0 +1,31 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + asm { sei } + [5] *((const byte*) PROCPORT_DDR#0) ← (const byte) PROCPORT_DDR_MEMORY_MASK#0 + [6] *((const byte*) PROCPORT#0) ← (const byte) PROCPORT_RAM_IO#0 + [7] *((const byte*) CIA1_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 + [8] *((const byte*) VIC_CONTROL#0) ← *((const byte*) VIC_CONTROL#0) | (byte) $80 + [9] *((const byte*) RASTER#0) ← (byte) 0 + [10] *((const byte*) IRQ_ENABLE#0) ← (const byte) IRQ_RASTER#0 + [11] *((const void()**) HARDWARE_IRQ#0) ← &interrupt(HARDWARE_STACK)(void()) irq() + asm { cli } + to:main::@1 +main::@1: scope:[main] from main main::@1 + [13] *((const byte*) FGCOL#0) ← ++ *((const byte*) FGCOL#0) + to:main::@1 +irq: scope:[irq] from + [14] *((const byte*) BGCOL#0) ← (const byte) WHITE#0 + [15] *((const byte*) BGCOL#0) ← (const byte) BLACK#0 + [16] *((const byte*) IRQ_STATUS#0) ← (const byte) IRQ_RASTER#0 + to:irq::@return +irq::@return: scope:[irq] from irq + [17] return + to:@return diff --git a/src/test/ref/irq-hardware-stack.log b/src/test/ref/irq-hardware-stack.log new file mode 100644 index 000000000..1012424bd --- /dev/null +++ b/src/test/ref/irq-hardware-stack.log @@ -0,0 +1,704 @@ +Resolved forward reference irq to interrupt(HARDWARE_STACK)(void()) irq() +Culled Empty Block (label) main::@4 +Culled Empty Block (label) main::@3 +Culled Empty Block (label) main::@5 +Culled Empty Block (label) main::@6 +Culled Empty Block (label) @1 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (void()**) HARDWARE_IRQ#0 ← ((void()**)) (number) $fffe + (byte*) RASTER#0 ← ((byte*)) (number) $d012 + (byte*) VIC_CONTROL#0 ← ((byte*)) (number) $d011 + (byte*) IRQ_STATUS#0 ← ((byte*)) (number) $d019 + (byte*) IRQ_ENABLE#0 ← ((byte*)) (number) $d01a + (byte) IRQ_RASTER#0 ← (number) 1 + (byte*) BGCOL#0 ← ((byte*)) (number) $d020 + (byte*) FGCOL#0 ← ((byte*)) (number) $d021 + (byte) WHITE#0 ← (number) 1 + (byte) BLACK#0 ← (number) 0 + (byte*) CIA1_INTERRUPT#0 ← ((byte*)) (number) $dc0d + (byte) CIA_INTERRUPT_CLEAR#0 ← (number) $7f + (byte*) PROCPORT_DDR#0 ← ((byte*)) (number) 0 + (byte) PROCPORT_DDR_MEMORY_MASK#0 ← (number) 7 + (byte*) PROCPORT#0 ← ((byte*)) (number) 1 + (byte) PROCPORT_RAM_IO#0 ← (number) $35 + to:@2 +main: scope:[main] from @2 + asm { sei } + *((byte*) PROCPORT_DDR#0) ← (byte) PROCPORT_DDR_MEMORY_MASK#0 + *((byte*) PROCPORT#0) ← (byte) PROCPORT_RAM_IO#0 + *((byte*) CIA1_INTERRUPT#0) ← (byte) CIA_INTERRUPT_CLEAR#0 + *((byte*) VIC_CONTROL#0) ← *((byte*) VIC_CONTROL#0) | (number) $80 + *((byte*) RASTER#0) ← (number) 0 + *((byte*) IRQ_ENABLE#0) ← (byte) IRQ_RASTER#0 + (void()*~) main::$0 ← & interrupt(HARDWARE_STACK)(void()) irq() + *((void()**) HARDWARE_IRQ#0) ← (void()*~) main::$0 + asm { cli } + 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) WHITE#0 + *((byte*) BGCOL#0) ← (byte) BLACK#0 + *((byte*) IRQ_STATUS#0) ← (byte) IRQ_RASTER#0 + 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) BLACK +(byte) BLACK#0 +(byte*) CIA1_INTERRUPT +(byte*) CIA1_INTERRUPT#0 +(byte) CIA_INTERRUPT_CLEAR +(byte) CIA_INTERRUPT_CLEAR#0 +(byte*) FGCOL +(byte*) FGCOL#0 +(void()**) HARDWARE_IRQ +(void()**) HARDWARE_IRQ#0 +(byte*) IRQ_ENABLE +(byte*) IRQ_ENABLE#0 +(byte) IRQ_RASTER +(byte) IRQ_RASTER#0 +(byte*) IRQ_STATUS +(byte*) IRQ_STATUS#0 +(byte*) PROCPORT +(byte*) PROCPORT#0 +(byte*) PROCPORT_DDR +(byte*) PROCPORT_DDR#0 +(byte) PROCPORT_DDR_MEMORY_MASK +(byte) PROCPORT_DDR_MEMORY_MASK#0 +(byte) PROCPORT_RAM_IO +(byte) PROCPORT_RAM_IO#0 +(byte*) RASTER +(byte*) RASTER#0 +(byte*) VIC_CONTROL +(byte*) VIC_CONTROL#0 +(byte) WHITE +(byte) WHITE#0 +interrupt(HARDWARE_STACK)(void()) irq() +(label) irq::@return +(void()) main() +(void()*~) main::$0 +(label) main::@1 +(label) main::@2 +(label) main::@return + +Adding number conversion cast (unumber) 1 in (byte) IRQ_RASTER#0 ← (number) 1 +Adding number conversion cast (unumber) 1 in (byte) WHITE#0 ← (number) 1 +Adding number conversion cast (unumber) 0 in (byte) BLACK#0 ← (number) 0 +Adding number conversion cast (unumber) $7f in (byte) CIA_INTERRUPT_CLEAR#0 ← (number) $7f +Adding number conversion cast (unumber) 7 in (byte) PROCPORT_DDR_MEMORY_MASK#0 ← (number) 7 +Adding number conversion cast (unumber) $35 in (byte) PROCPORT_RAM_IO#0 ← (number) $35 +Adding number conversion cast (unumber) $80 in *((byte*) VIC_CONTROL#0) ← *((byte*) VIC_CONTROL#0) | (number) $80 +Adding number conversion cast (unumber) 0 in *((byte*) RASTER#0) ← (number) 0 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (void()**) HARDWARE_IRQ#0 ← (void()**)(number) $fffe +Inlining cast (byte*) RASTER#0 ← (byte*)(number) $d012 +Inlining cast (byte*) VIC_CONTROL#0 ← (byte*)(number) $d011 +Inlining cast (byte*) IRQ_STATUS#0 ← (byte*)(number) $d019 +Inlining cast (byte*) IRQ_ENABLE#0 ← (byte*)(number) $d01a +Inlining cast (byte) IRQ_RASTER#0 ← (unumber)(number) 1 +Inlining cast (byte*) BGCOL#0 ← (byte*)(number) $d020 +Inlining cast (byte*) FGCOL#0 ← (byte*)(number) $d021 +Inlining cast (byte) WHITE#0 ← (unumber)(number) 1 +Inlining cast (byte) BLACK#0 ← (unumber)(number) 0 +Inlining cast (byte*) CIA1_INTERRUPT#0 ← (byte*)(number) $dc0d +Inlining cast (byte) CIA_INTERRUPT_CLEAR#0 ← (unumber)(number) $7f +Inlining cast (byte*) PROCPORT_DDR#0 ← (byte*)(number) 0 +Inlining cast (byte) PROCPORT_DDR_MEMORY_MASK#0 ← (unumber)(number) 7 +Inlining cast (byte*) PROCPORT#0 ← (byte*)(number) 1 +Inlining cast (byte) PROCPORT_RAM_IO#0 ← (unumber)(number) $35 +Inlining cast *((byte*) RASTER#0) ← (unumber)(number) 0 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (void()**) 65534 +Simplifying constant pointer cast (byte*) 53266 +Simplifying constant pointer cast (byte*) 53265 +Simplifying constant pointer cast (byte*) 53273 +Simplifying constant pointer cast (byte*) 53274 +Simplifying constant integer cast 1 +Simplifying constant pointer cast (byte*) 53280 +Simplifying constant pointer cast (byte*) 53281 +Simplifying constant integer cast 1 +Simplifying constant integer cast 0 +Simplifying constant pointer cast (byte*) 56333 +Simplifying constant integer cast $7f +Simplifying constant pointer cast (byte*) 0 +Simplifying constant integer cast 7 +Simplifying constant pointer cast (byte*) 1 +Simplifying constant integer cast $35 +Simplifying constant integer cast $80 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 1 +Finalized unsigned number type (byte) 1 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) $7f +Finalized unsigned number type (byte) 7 +Finalized unsigned number type (byte) $35 +Finalized unsigned number type (byte) $80 +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Constant right-side identified [23] (void()*~) main::$0 ← & interrupt(HARDWARE_STACK)(void()) irq() +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const void()**) HARDWARE_IRQ#0 = (void()**) 65534 +Constant (const byte*) RASTER#0 = (byte*) 53266 +Constant (const byte*) VIC_CONTROL#0 = (byte*) 53265 +Constant (const byte*) IRQ_STATUS#0 = (byte*) 53273 +Constant (const byte*) IRQ_ENABLE#0 = (byte*) 53274 +Constant (const byte) IRQ_RASTER#0 = 1 +Constant (const byte*) BGCOL#0 = (byte*) 53280 +Constant (const byte*) FGCOL#0 = (byte*) 53281 +Constant (const byte) WHITE#0 = 1 +Constant (const byte) BLACK#0 = 0 +Constant (const byte*) CIA1_INTERRUPT#0 = (byte*) 56333 +Constant (const byte) CIA_INTERRUPT_CLEAR#0 = $7f +Constant (const byte*) PROCPORT_DDR#0 = (byte*) 0 +Constant (const byte) PROCPORT_DDR_MEMORY_MASK#0 = 7 +Constant (const byte*) PROCPORT#0 = (byte*) 1 +Constant (const byte) PROCPORT_RAM_IO#0 = $35 +Constant (const void()*) main::$0 = &irq +Successful SSA optimization Pass2ConstantIdentification +if() condition always true - replacing block destination [26] if(true) goto main::@2 +Successful SSA optimization Pass2ConstantIfs +Removing unused block main::@return +Successful SSA optimization Pass2EliminateUnusedBlocks +Constant inlined main::$0 = &interrupt(HARDWARE_STACK)(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 @3 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main::@1 +CALL GRAPH +Calls in [] to main:2 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Culled Empty Block (label) @3 +Culled Empty Block (label) main::@1 +Renumbering block @2 to @1 +Renumbering block main::@2 to main::@1 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + asm { sei } + [5] *((const byte*) PROCPORT_DDR#0) ← (const byte) PROCPORT_DDR_MEMORY_MASK#0 + [6] *((const byte*) PROCPORT#0) ← (const byte) PROCPORT_RAM_IO#0 + [7] *((const byte*) CIA1_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 + [8] *((const byte*) VIC_CONTROL#0) ← *((const byte*) VIC_CONTROL#0) | (byte) $80 + [9] *((const byte*) RASTER#0) ← (byte) 0 + [10] *((const byte*) IRQ_ENABLE#0) ← (const byte) IRQ_RASTER#0 + [11] *((const void()**) HARDWARE_IRQ#0) ← &interrupt(HARDWARE_STACK)(void()) irq() + asm { cli } + to:main::@1 +main::@1: scope:[main] from main main::@1 + [13] *((const byte*) FGCOL#0) ← ++ *((const byte*) FGCOL#0) + to:main::@1 +irq: scope:[irq] from + [14] *((const byte*) BGCOL#0) ← (const byte) WHITE#0 + [15] *((const byte*) BGCOL#0) ← (const byte) BLACK#0 + [16] *((const byte*) IRQ_STATUS#0) ← (const byte) IRQ_RASTER#0 + to:irq::@return +irq::@return: scope:[irq] from irq + [17] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte*) BGCOL +(byte) BLACK +(byte*) CIA1_INTERRUPT +(byte) CIA_INTERRUPT_CLEAR +(byte*) FGCOL +(void()**) HARDWARE_IRQ +(byte*) IRQ_ENABLE +(byte) IRQ_RASTER +(byte*) IRQ_STATUS +(byte*) PROCPORT +(byte*) PROCPORT_DDR +(byte) PROCPORT_DDR_MEMORY_MASK +(byte) PROCPORT_RAM_IO +(byte*) RASTER +(byte*) VIC_CONTROL +(byte) WHITE +interrupt(HARDWARE_STACK)(void()) irq() +(void()) main() + +Initial phi equivalence classes +Complete equivalence classes + +INITIAL ASM +Target platform is c64basic + // File Comments +// A minimal working raster IRQ + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label HARDWARE_IRQ = $fffe + .label RASTER = $d012 + .label VIC_CONTROL = $d011 + .label IRQ_STATUS = $d019 + .label IRQ_ENABLE = $d01a + .const IRQ_RASTER = 1 + .label BGCOL = $d020 + .label FGCOL = $d021 + .const WHITE = 1 + .const BLACK = 0 + .label CIA1_INTERRUPT = $dc0d + .const CIA_INTERRUPT_CLEAR = $7f + // Processor port data direction register + .label PROCPORT_DDR = 0 + // Mask for PROCESSOR_PORT_DDR which allows only memory configuration to be written + .const PROCPORT_DDR_MEMORY_MASK = 7 + // Processor Port Register controlling RAM/ROM configuration and the datasette + .label PROCPORT = 1 + // RAM in $A000, $E000 I/O in $D000 + .const PROCPORT_RAM_IO = $35 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +// RAM in $A000, $E000 CHAR ROM in $D000 +main: { + // asm { sei } + sei + // [5] *((const byte*) PROCPORT_DDR#0) ← (const byte) PROCPORT_DDR_MEMORY_MASK#0 -- _deref_pbuc1=vbuc2 + // Disable kernal & basic + lda #PROCPORT_DDR_MEMORY_MASK + sta PROCPORT_DDR + // [6] *((const byte*) PROCPORT#0) ← (const byte) PROCPORT_RAM_IO#0 -- _deref_pbuc1=vbuc2 + lda #PROCPORT_RAM_IO + sta PROCPORT + // [7] *((const byte*) CIA1_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 -- _deref_pbuc1=vbuc2 + // Disable CIA 1 Timer IRQ + lda #CIA_INTERRUPT_CLEAR + sta CIA1_INTERRUPT + // [8] *((const byte*) VIC_CONTROL#0) ← *((const byte*) VIC_CONTROL#0) | (byte) $80 -- _deref_pbuc1=_deref_pbuc1_bor_vbuc2 + // Set raster line to $100 + lda #$80 + ora VIC_CONTROL + sta VIC_CONTROL + // [9] *((const byte*) RASTER#0) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta RASTER + // [10] *((const byte*) IRQ_ENABLE#0) ← (const byte) IRQ_RASTER#0 -- _deref_pbuc1=vbuc2 + // Enable Raster Interrupt + lda #IRQ_RASTER + sta IRQ_ENABLE + // [11] *((const void()**) HARDWARE_IRQ#0) ← &interrupt(HARDWARE_STACK)(void()) irq() -- _deref_pptc1=pprc2 + // Set the IRQ routine + lda #irq + sta HARDWARE_IRQ+1 + // asm { cli } + cli + jmp b1 + // main::@1 + b1: + // [13] *((const byte*) FGCOL#0) ← ++ *((const byte*) FGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc FGCOL + jmp b1 +} + // irq +// Interrupt Routine +irq: { + // entry interrupt(HARDWARE_STACK) + pha + txa + pha + tya + pha + // [14] *((const byte*) BGCOL#0) ← (const byte) WHITE#0 -- _deref_pbuc1=vbuc2 + lda #WHITE + sta BGCOL + // [15] *((const byte*) BGCOL#0) ← (const byte) BLACK#0 -- _deref_pbuc1=vbuc2 + lda #BLACK + sta BGCOL + // [16] *((const byte*) IRQ_STATUS#0) ← (const byte) IRQ_RASTER#0 -- _deref_pbuc1=vbuc2 + // Acknowledge the IRQ + lda #IRQ_RASTER + sta IRQ_STATUS + jmp breturn + // irq::@return + breturn: + // [17] return - exit interrupt(HARDWARE_STACK) + pla + tay + pla + tax + pla + rti +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [5] *((const byte*) PROCPORT_DDR#0) ← (const byte) PROCPORT_DDR_MEMORY_MASK#0 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [6] *((const byte*) PROCPORT#0) ← (const byte) PROCPORT_RAM_IO#0 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [7] *((const byte*) CIA1_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [8] *((const byte*) VIC_CONTROL#0) ← *((const byte*) VIC_CONTROL#0) | (byte) $80 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [9] *((const byte*) RASTER#0) ← (byte) 0 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [10] *((const byte*) IRQ_ENABLE#0) ← (const byte) IRQ_RASTER#0 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [11] *((const void()**) HARDWARE_IRQ#0) ← &interrupt(HARDWARE_STACK)(void()) irq() [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [14] *((const byte*) BGCOL#0) ← (const byte) WHITE#0 [ ] ( [ ] ) always clobbers reg byte a +Statement [15] *((const byte*) BGCOL#0) ← (const byte) BLACK#0 [ ] ( [ ] ) always clobbers reg byte a +Statement [16] *((const byte*) IRQ_STATUS#0) ← (const byte) IRQ_RASTER#0 [ ] ( [ ] ) always clobbers reg byte a +Statement [17] return [ ] ( [ ] ) always clobbers reg byte a reg byte x reg byte y + +REGISTER UPLIFT SCOPES +Uplift Scope [main] +Uplift Scope [irq] +Uplift Scope [] + +Uplifting [main] best 334 combination +Uplifting [irq] best 334 combination +Uplifting [] best 334 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// A minimal working raster IRQ + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label HARDWARE_IRQ = $fffe + .label RASTER = $d012 + .label VIC_CONTROL = $d011 + .label IRQ_STATUS = $d019 + .label IRQ_ENABLE = $d01a + .const IRQ_RASTER = 1 + .label BGCOL = $d020 + .label FGCOL = $d021 + .const WHITE = 1 + .const BLACK = 0 + .label CIA1_INTERRUPT = $dc0d + .const CIA_INTERRUPT_CLEAR = $7f + // Processor port data direction register + .label PROCPORT_DDR = 0 + // Mask for PROCESSOR_PORT_DDR which allows only memory configuration to be written + .const PROCPORT_DDR_MEMORY_MASK = 7 + // Processor Port Register controlling RAM/ROM configuration and the datasette + .label PROCPORT = 1 + // RAM in $A000, $E000 I/O in $D000 + .const PROCPORT_RAM_IO = $35 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +// RAM in $A000, $E000 CHAR ROM in $D000 +main: { + // asm { sei } + sei + // [5] *((const byte*) PROCPORT_DDR#0) ← (const byte) PROCPORT_DDR_MEMORY_MASK#0 -- _deref_pbuc1=vbuc2 + // Disable kernal & basic + lda #PROCPORT_DDR_MEMORY_MASK + sta PROCPORT_DDR + // [6] *((const byte*) PROCPORT#0) ← (const byte) PROCPORT_RAM_IO#0 -- _deref_pbuc1=vbuc2 + lda #PROCPORT_RAM_IO + sta PROCPORT + // [7] *((const byte*) CIA1_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 -- _deref_pbuc1=vbuc2 + // Disable CIA 1 Timer IRQ + lda #CIA_INTERRUPT_CLEAR + sta CIA1_INTERRUPT + // [8] *((const byte*) VIC_CONTROL#0) ← *((const byte*) VIC_CONTROL#0) | (byte) $80 -- _deref_pbuc1=_deref_pbuc1_bor_vbuc2 + // Set raster line to $100 + lda #$80 + ora VIC_CONTROL + sta VIC_CONTROL + // [9] *((const byte*) RASTER#0) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta RASTER + // [10] *((const byte*) IRQ_ENABLE#0) ← (const byte) IRQ_RASTER#0 -- _deref_pbuc1=vbuc2 + // Enable Raster Interrupt + lda #IRQ_RASTER + sta IRQ_ENABLE + // [11] *((const void()**) HARDWARE_IRQ#0) ← &interrupt(HARDWARE_STACK)(void()) irq() -- _deref_pptc1=pprc2 + // Set the IRQ routine + lda #irq + sta HARDWARE_IRQ+1 + // asm { cli } + cli + jmp b1 + // main::@1 + b1: + // [13] *((const byte*) FGCOL#0) ← ++ *((const byte*) FGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc FGCOL + jmp b1 +} + // irq +// Interrupt Routine +irq: { + // entry interrupt(HARDWARE_STACK) + pha + txa + pha + tya + pha + // [14] *((const byte*) BGCOL#0) ← (const byte) WHITE#0 -- _deref_pbuc1=vbuc2 + lda #WHITE + sta BGCOL + // [15] *((const byte*) BGCOL#0) ← (const byte) BLACK#0 -- _deref_pbuc1=vbuc2 + lda #BLACK + sta BGCOL + // [16] *((const byte*) IRQ_STATUS#0) ← (const byte) IRQ_RASTER#0 -- _deref_pbuc1=vbuc2 + // Acknowledge the IRQ + lda #IRQ_RASTER + sta IRQ_STATUS + jmp breturn + // irq::@return + breturn: + // [17] return - exit interrupt(HARDWARE_STACK) + pla + tay + pla + tax + pla + rti +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction bend_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(byte*) BGCOL +(const byte*) BGCOL#0 BGCOL = (byte*) 53280 +(byte) BLACK +(const byte) BLACK#0 BLACK = (byte) 0 +(byte*) CIA1_INTERRUPT +(const byte*) CIA1_INTERRUPT#0 CIA1_INTERRUPT = (byte*) 56333 +(byte) CIA_INTERRUPT_CLEAR +(const byte) CIA_INTERRUPT_CLEAR#0 CIA_INTERRUPT_CLEAR = (byte) $7f +(byte*) FGCOL +(const byte*) FGCOL#0 FGCOL = (byte*) 53281 +(void()**) HARDWARE_IRQ +(const void()**) HARDWARE_IRQ#0 HARDWARE_IRQ = (void()**) 65534 +(byte*) IRQ_ENABLE +(const byte*) IRQ_ENABLE#0 IRQ_ENABLE = (byte*) 53274 +(byte) IRQ_RASTER +(const byte) IRQ_RASTER#0 IRQ_RASTER = (byte) 1 +(byte*) IRQ_STATUS +(const byte*) IRQ_STATUS#0 IRQ_STATUS = (byte*) 53273 +(byte*) PROCPORT +(const byte*) PROCPORT#0 PROCPORT = (byte*) 1 +(byte*) PROCPORT_DDR +(const byte*) PROCPORT_DDR#0 PROCPORT_DDR = (byte*) 0 +(byte) PROCPORT_DDR_MEMORY_MASK +(const byte) PROCPORT_DDR_MEMORY_MASK#0 PROCPORT_DDR_MEMORY_MASK = (byte) 7 +(byte) PROCPORT_RAM_IO +(const byte) PROCPORT_RAM_IO#0 PROCPORT_RAM_IO = (byte) $35 +(byte*) RASTER +(const byte*) RASTER#0 RASTER = (byte*) 53266 +(byte*) VIC_CONTROL +(const byte*) VIC_CONTROL#0 VIC_CONTROL = (byte*) 53265 +(byte) WHITE +(const byte) WHITE#0 WHITE = (byte) 1 +interrupt(HARDWARE_STACK)(void()) irq() +(label) irq::@return +(void()) main() +(label) main::@1 + + + +FINAL ASSEMBLER +Score: 316 + + // File Comments +// A minimal working raster IRQ + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label HARDWARE_IRQ = $fffe + .label RASTER = $d012 + .label VIC_CONTROL = $d011 + .label IRQ_STATUS = $d019 + .label IRQ_ENABLE = $d01a + .const IRQ_RASTER = 1 + .label BGCOL = $d020 + .label FGCOL = $d021 + .const WHITE = 1 + .const BLACK = 0 + .label CIA1_INTERRUPT = $dc0d + .const CIA_INTERRUPT_CLEAR = $7f + // Processor port data direction register + .label PROCPORT_DDR = 0 + // Mask for PROCESSOR_PORT_DDR which allows only memory configuration to be written + .const PROCPORT_DDR_MEMORY_MASK = 7 + // Processor Port Register controlling RAM/ROM configuration and the datasette + .label PROCPORT = 1 + // RAM in $A000, $E000 I/O in $D000 + .const PROCPORT_RAM_IO = $35 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +// RAM in $A000, $E000 CHAR ROM in $D000 +main: { + // asm + // asm { sei } + sei + // *PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK + // [5] *((const byte*) PROCPORT_DDR#0) ← (const byte) PROCPORT_DDR_MEMORY_MASK#0 -- _deref_pbuc1=vbuc2 + // Disable kernal & basic + lda #PROCPORT_DDR_MEMORY_MASK + sta PROCPORT_DDR + // *PROCPORT = PROCPORT_RAM_IO + // [6] *((const byte*) PROCPORT#0) ← (const byte) PROCPORT_RAM_IO#0 -- _deref_pbuc1=vbuc2 + lda #PROCPORT_RAM_IO + sta PROCPORT + // *CIA1_INTERRUPT = CIA_INTERRUPT_CLEAR + // [7] *((const byte*) CIA1_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 -- _deref_pbuc1=vbuc2 + // Disable CIA 1 Timer IRQ + lda #CIA_INTERRUPT_CLEAR + sta CIA1_INTERRUPT + // *VIC_CONTROL |=$80 + // [8] *((const byte*) VIC_CONTROL#0) ← *((const byte*) VIC_CONTROL#0) | (byte) $80 -- _deref_pbuc1=_deref_pbuc1_bor_vbuc2 + // Set raster line to $100 + lda #$80 + ora VIC_CONTROL + sta VIC_CONTROL + // *RASTER = $00 + // [9] *((const byte*) RASTER#0) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta RASTER + // *IRQ_ENABLE = IRQ_RASTER + // [10] *((const byte*) IRQ_ENABLE#0) ← (const byte) IRQ_RASTER#0 -- _deref_pbuc1=vbuc2 + // Enable Raster Interrupt + lda #IRQ_RASTER + sta IRQ_ENABLE + // *HARDWARE_IRQ = &irq + // [11] *((const void()**) HARDWARE_IRQ#0) ← &interrupt(HARDWARE_STACK)(void()) irq() -- _deref_pptc1=pprc2 + // Set the IRQ routine + lda #irq + sta HARDWARE_IRQ+1 + // asm + // asm { cli } + cli + // main::@1 + b1: + // (*FGCOL)++; + // [13] *((const byte*) FGCOL#0) ← ++ *((const byte*) FGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc FGCOL + jmp b1 +} + // irq +// Interrupt Routine +irq: { + // entry interrupt(HARDWARE_STACK) + pha + txa + pha + tya + pha + // *BGCOL = WHITE + // [14] *((const byte*) BGCOL#0) ← (const byte) WHITE#0 -- _deref_pbuc1=vbuc2 + lda #WHITE + sta BGCOL + // *BGCOL = BLACK + // [15] *((const byte*) BGCOL#0) ← (const byte) BLACK#0 -- _deref_pbuc1=vbuc2 + lda #BLACK + sta BGCOL + // *IRQ_STATUS = IRQ_RASTER + // [16] *((const byte*) IRQ_STATUS#0) ← (const byte) IRQ_RASTER#0 -- _deref_pbuc1=vbuc2 + // Acknowledge the IRQ + lda #IRQ_RASTER + sta IRQ_STATUS + // irq::@return + // } + // [17] return - exit interrupt(HARDWARE_STACK) + pla + tay + pla + tax + pla + rti +} + // File Data + diff --git a/src/test/ref/irq-hardware-stack.sym b/src/test/ref/irq-hardware-stack.sym new file mode 100644 index 000000000..fc69ce64a --- /dev/null +++ b/src/test/ref/irq-hardware-stack.sym @@ -0,0 +1,40 @@ +(label) @1 +(label) @begin +(label) @end +(byte*) BGCOL +(const byte*) BGCOL#0 BGCOL = (byte*) 53280 +(byte) BLACK +(const byte) BLACK#0 BLACK = (byte) 0 +(byte*) CIA1_INTERRUPT +(const byte*) CIA1_INTERRUPT#0 CIA1_INTERRUPT = (byte*) 56333 +(byte) CIA_INTERRUPT_CLEAR +(const byte) CIA_INTERRUPT_CLEAR#0 CIA_INTERRUPT_CLEAR = (byte) $7f +(byte*) FGCOL +(const byte*) FGCOL#0 FGCOL = (byte*) 53281 +(void()**) HARDWARE_IRQ +(const void()**) HARDWARE_IRQ#0 HARDWARE_IRQ = (void()**) 65534 +(byte*) IRQ_ENABLE +(const byte*) IRQ_ENABLE#0 IRQ_ENABLE = (byte*) 53274 +(byte) IRQ_RASTER +(const byte) IRQ_RASTER#0 IRQ_RASTER = (byte) 1 +(byte*) IRQ_STATUS +(const byte*) IRQ_STATUS#0 IRQ_STATUS = (byte*) 53273 +(byte*) PROCPORT +(const byte*) PROCPORT#0 PROCPORT = (byte*) 1 +(byte*) PROCPORT_DDR +(const byte*) PROCPORT_DDR#0 PROCPORT_DDR = (byte*) 0 +(byte) PROCPORT_DDR_MEMORY_MASK +(const byte) PROCPORT_DDR_MEMORY_MASK#0 PROCPORT_DDR_MEMORY_MASK = (byte) 7 +(byte) PROCPORT_RAM_IO +(const byte) PROCPORT_RAM_IO#0 PROCPORT_RAM_IO = (byte) $35 +(byte*) RASTER +(const byte*) RASTER#0 RASTER = (byte*) 53266 +(byte*) VIC_CONTROL +(const byte*) VIC_CONTROL#0 VIC_CONTROL = (byte*) 53265 +(byte) WHITE +(const byte) WHITE#0 WHITE = (byte) 1 +interrupt(HARDWARE_STACK)(void()) irq() +(label) irq::@return +(void()) main() +(label) main::@1 +