diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 13901b4c8..5b4009e48 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -357,6 +357,9 @@ public class Compiler { new Pass4CodeGeneration(program, true).generate(); new Pass4AssertNoCpuClobber(program).check(); + // Remove unnecessary register savings from interrupts {@link InterruptType#HARDWARE_NOCLOBBER} + new Pass4InterruptClobberFix(program).fix(); + getLog().append("\nASSEMBLER BEFORE OPTIMIZATION"); getLog().append(program.getAsm().toString()); diff --git a/src/main/java/dk/camelot64/kickc/asm/AsmProgram.java b/src/main/java/dk/camelot64/kickc/asm/AsmProgram.java index 13cb75b53..b85a6733e 100644 --- a/src/main/java/dk/camelot64/kickc/asm/AsmProgram.java +++ b/src/main/java/dk/camelot64/kickc/asm/AsmProgram.java @@ -1,6 +1,7 @@ package dk.camelot64.kickc.asm; import dk.camelot64.kickc.fragment.AsmFormat; +import dk.camelot64.kickc.model.values.ScopeRef; import java.util.ArrayList; import java.util.Collection; @@ -36,8 +37,8 @@ public class AsmProgram { return segments; } - public AsmSegment startSegment(Integer statementIndex, String source) { - AsmSegment segment = new AsmSegment(nextSegmentIndex++, statementIndex, source); + public AsmSegment startSegment(ScopeRef scopeRef, Integer statementIndex, String source) { + AsmSegment segment = new AsmSegment(nextSegmentIndex++, scopeRef, statementIndex, source); segments.add(segment); return segment; } diff --git a/src/main/java/dk/camelot64/kickc/asm/AsmSegment.java b/src/main/java/dk/camelot64/kickc/asm/AsmSegment.java index 5f1f303d5..d0c1fdbf3 100644 --- a/src/main/java/dk/camelot64/kickc/asm/AsmSegment.java +++ b/src/main/java/dk/camelot64/kickc/asm/AsmSegment.java @@ -1,6 +1,7 @@ package dk.camelot64.kickc.asm; import dk.camelot64.kickc.model.PhiTransitions; +import dk.camelot64.kickc.model.values.ScopeRef; import java.util.ArrayList; import java.util.List; @@ -44,8 +45,12 @@ public class AsmSegment { /** If the segment is an assignment in a PHI transition this contains the index of the assignment within the transition. */ private Integer phiTransitionAssignmentIdx; - public AsmSegment(int index, Integer statementIdx, String source) { + /** The full name of the containing scope (procedure). */ + private String scopeLabel; + + public AsmSegment(int index, ScopeRef scope, Integer statementIdx, String source) { this.lines = new ArrayList<>(); + this.scopeLabel = scope.getFullName(); this.index = index; this.statementIdx = statementIdx; this.source = source; @@ -108,6 +113,10 @@ public class AsmSegment { this.phiTransitionAssignmentIdx = phiTransitionAssignmentIdx; } + public String getScopeLabel() { + return scopeLabel; + } + /** * Get the number of bytes the segment occupies in memory. * Per default calculated by adding up the bytes of each ASM line in the segment. @@ -219,4 +228,7 @@ public class AsmSegment { return toString(new AsmProgram.AsmPrintState(true, false)); } + public void setSource(String source) { + this.source = source; + } } diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java index ce39b0441..1b2514a12 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java @@ -109,7 +109,7 @@ public class AsmFragmentTemplate { AsmFragmentInstance fragmentInstance = new AsmFragmentInstance(new Program(), signature, ScopeRef.ROOT, this, bindings); AsmProgram asm = new AsmProgram(); - asm.startSegment(null, signature); + asm.startSegment( ScopeRef.ROOT, null, signature); fragmentInstance.generate(asm); AsmClobber asmClobber = asm.getClobber(); this.clobber = new AsmFragmentClobber(asmClobber); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index ea642f0be..63e40c1f8 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -45,13 +45,13 @@ public class Pass4CodeGeneration { AsmProgram asm = new AsmProgram(); ScopeRef currentScope = ScopeRef.ROOT; - asm.startSegment(null, "Basic Upstart"); + asm.startSegment( currentScope, null, "Basic Upstart"); asm.addLine(new AsmSetPc("Basic", AsmFormat.getAsmNumber(0x0801))); asm.addLine(new AsmBasicUpstart("main")); asm.addLine(new AsmSetPc("Program", AsmFormat.getAsmNumber(0x080d))); // Generate global ZP labels - asm.startSegment(null, "Global Constants & labels"); + asm.startSegment( currentScope, null, "Global Constants & labels"); addConstants(asm, currentScope); addZpLabels(asm, currentScope); for(ControlFlowBlock block : getGraph().getAllBlocks()) { @@ -61,7 +61,7 @@ public class Pass4CodeGeneration { asm.addScopeEnd(); } currentScope = block.getScope(); - asm.startSegment(null, block.getLabel().getFullName()); + asm.startSegment(currentScope, null, block.getLabel().getFullName()); asm.addScopeBegin(block.getLabel().getFullName().replace('@', 'b').replace(':', '_')); // Add all ZP labels for the scope addConstants(asm, currentScope); @@ -75,11 +75,11 @@ public class Pass4CodeGeneration { // Generate interrupt entry if needed Procedure procedure = block.getProcedure(program); if(procedure!=null && procedure.getInterruptType()!=null) { - generateInterruptEntry(asm, procedure.getInterruptType()); + generateInterruptEntry(asm, procedure); } } else { // Generate label for block inside procedure - asm.startSegment(null, block.getLabel().getFullName()); + asm.startSegment(currentScope, null, block.getLabel().getFullName()); asm.addLabel(block.getLabel().getLocalName().replace('@', 'b').replace(':', '_')); } // Generate statements @@ -376,7 +376,7 @@ public class Pass4CodeGeneration { */ public void generateStatementAsm(AsmProgram asm, ControlFlowBlock block, Statement statement, AsmCodegenAluState aluState, boolean genCallPhiEntry) { - asm.startSegment(statement.getIndex(), statement.toString(program, verboseAliveInfo)); + asm.startSegment(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo)); // IF the previous statement was added to the ALU register - generate the composite ASM fragment if(aluState.hasAluAssignment()) { @@ -469,10 +469,11 @@ public class Pass4CodeGeneration { /** * Generate exit-code for entering an interrupt procedure based on the interrupt type * @param asm The assembler to generate code into - * @param interruptType The type of interrupt to generate + * @param procedure The interrupt procedure */ - private void generateInterruptEntry(AsmProgram asm, Procedure.InterruptType interruptType) { - asm.startSegment(null, "interrupt "+interruptType.name()); + private void generateInterruptEntry(AsmProgram asm, Procedure procedure) { + Procedure.InterruptType interruptType = procedure.getInterruptType(); + asm.startSegment( procedure.getRef(), null, "entry interrupt("+interruptType.name()+")"); //asm.getCurrentSegment().setXXX(); if(Procedure.InterruptType.KERNEL_MIN.equals(interruptType)) { // No entry ASM needed @@ -485,8 +486,9 @@ public class Pass4CodeGeneration { } else if(Procedure.InterruptType.HARDWARE_NONE.equals(interruptType)) { // No entry ASM needed } else if(Procedure.InterruptType.HARDWARE_CLOBBER.equals(interruptType)) { - throw new CompileError("Not implemented! "+interruptType.name()); - } else { + 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 { throw new RuntimeException("Interrupt Type not supported " + interruptType.name()); } } @@ -498,6 +500,7 @@ public class Pass4CodeGeneration { * @param interruptType The type of interrupt to generate */ private void generateInterruptExit(AsmProgram asm, Statement statement, Procedure.InterruptType interruptType) { + asm.getCurrentSegment().setSource(asm.getCurrentSegment().getSource() + " - exit interrupt("+interruptType.name()+")"); if(Procedure.InterruptType.KERNEL_MIN.equals(interruptType)) { asm.addInstruction("jmp", AsmAddressingMode.ABS, "$ea81", false); } else if(Procedure.InterruptType.KERNEL_KEYBOARD.equals(interruptType)) { @@ -513,7 +516,13 @@ public class Pass4CodeGeneration { } else if(Procedure.InterruptType.HARDWARE_NONE.equals(interruptType)) { asm.addInstruction("rti", AsmAddressingMode.NON, null, false); } else if(Procedure.InterruptType.HARDWARE_CLOBBER.equals(interruptType)) { - throw new CompileError("Not implemented! "+interruptType.name()); + asm.addLabel("rega").setDontOptimize(true); + asm.addInstruction("lda", AsmAddressingMode.IMM, "00", false).setDontOptimize(true); + asm.addLabel("regx").setDontOptimize(true); + asm.addInstruction("ldx", AsmAddressingMode.IMM, "00", false).setDontOptimize(true); + asm.addLabel("regy").setDontOptimize(true); + asm.addInstruction("ldy", AsmAddressingMode.IMM, "00", false).setDontOptimize(true); + asm.addInstruction("rti", AsmAddressingMode.NON, null, false); } else { throw new RuntimeException("Interrupt Type not supported " + statement); } @@ -574,7 +583,7 @@ public class Pass4CodeGeneration { segmentSrc += fBlock.getLabel().getFullName() + " "; } segmentSrc += "to " + toBlock.getLabel().getFullName(); - asm.startSegment(toFirstStatement.getIndex(), segmentSrc); + asm.startSegment( scope, toFirstStatement.getIndex(), segmentSrc); asm.getCurrentSegment().setPhiTransitionId(transition.getTransitionId()); for(ControlFlowBlock fBlock : transition.getFromBlocks()) { asm.addLabel((toBlock.getLabel().getLocalName() + "_from_" + fBlock.getLabel().getLocalName()).replace('@', 'b').replace(':', '_')); @@ -585,7 +594,7 @@ public class Pass4CodeGeneration { RValue rValue = assignment.getrValue(); Statement statement = assignment.getPhiBlock(); // Generate an ASM move fragment - asm.startSegment(statement.getIndex(), "[" + statement.getIndex() + "] phi " + lValue.toString(program) + " = " + rValue.toString(program)); + asm.startSegment(scope, statement.getIndex(), "[" + statement.getIndex() + "] phi " + lValue.toString(program) + " = " + rValue.toString(program)); asm.getCurrentSegment().setPhiTransitionId(transition.getTransitionId()); asm.getCurrentSegment().setPhiTransitionAssignmentIdx(assignment.getAssignmentIdx()); if(isRegisterCopy(lValue, rValue)) { diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4InterruptClobberFix.java b/src/main/java/dk/camelot64/kickc/passes/Pass4InterruptClobberFix.java new file mode 100644 index 000000000..55e69f477 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4InterruptClobberFix.java @@ -0,0 +1,101 @@ +package dk.camelot64.kickc.passes; + +import dk.camelot64.kickc.asm.AsmClobber; +import dk.camelot64.kickc.asm.AsmLine; +import dk.camelot64.kickc.asm.AsmProgram; +import dk.camelot64.kickc.asm.AsmSegment; +import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.Registers; +import dk.camelot64.kickc.model.symbols.Procedure; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.ListIterator; + +import static dk.camelot64.kickc.passes.Pass4AssertNoCpuClobber.getClobberRegisters; + +/*** Ensure that all interrupt procedures with type {@link Procedure.InterruptType#HARDWARE_CLOBBER } only saves the necessary registers. */ +public class Pass4InterruptClobberFix extends Pass2Base { + + public Pass4InterruptClobberFix(Program program) { + super(program); + } + + /** + * Check that no statement clobbers a CPU register used by an alive variable + */ + public void fix() { + Collection procedures = getProgram().getScope().getAllProcedures(true); + for(Procedure procedure : procedures) { + if(Procedure.InterruptType.HARDWARE_CLOBBER.equals(procedure.getInterruptType())) { + + AsmSegment interruptEntry = null; + AsmSegment interruptExit = null; + + // Iterate all procedure segments to find the interrupt routine clobber + AsmClobber procClobber = new AsmClobber(); + AsmProgram asm = getProgram().getAsm(); + for(AsmSegment asmSegment : asm.getSegments()) { + if(procedure.getFullName().equals(asmSegment.getScopeLabel())) { + if(asmSegment.getSource().contains(Procedure.InterruptType.HARDWARE_CLOBBER.name())) { + if(asmSegment.getSource().contains("entry interrupt")) { + interruptEntry = asmSegment; + } else if(asmSegment.getSource().contains("exit interrupt")) { + interruptExit = asmSegment; + } else { + throw new RuntimeException("Unknown interrupt ASM segment "+asmSegment.getSource()); + } + continue; + } + AsmClobber asmSegmentClobber = asmSegment.getClobber(); + procClobber.add(asmSegmentClobber); + } + } + getLog().append("Interrupt procedure "+procedure.getFullName()+" clobbers "+procClobber.toString()); + if(interruptEntry==null || interruptExit==null) { + throw new RuntimeException("Cannot find interrupt entry/exit for interrupt "+procedure.getFullName()); + } + List notClobberedRegisters = getNonClobberedRegisterNames(procClobber); + if(notClobberedRegisters.isEmpty()) { + // All registers clobbered - no need to fix anything + continue; + } + // Remove all lines saving/restoring non-clobbered registers in entry + pruneNonClobberedInterruptLines(interruptEntry, notClobberedRegisters); + // Remove all lines saving/restoring non-clobbered registers in entry + pruneNonClobberedInterruptLines(interruptExit, notClobberedRegisters); + + } + } + } + + private List getNonClobberedRegisterNames(AsmClobber procClobber) { + List notClobberedRegisters = new ArrayList<>(); + if(!procClobber.isClobberA()) { + notClobberedRegisters.add("a"); + } + if(!procClobber.isClobberX()) { + notClobberedRegisters.add("x"); + } + if(!procClobber.isClobberY()) { + notClobberedRegisters.add("y"); + } + return notClobberedRegisters; + } + + private void pruneNonClobberedInterruptLines(AsmSegment interruptEntryExit, List notClobberedRegisters) { + ListIterator entryLines = interruptEntryExit.getLines().listIterator(); + while(entryLines.hasNext()) { + AsmLine line = entryLines.next(); + for(String notClobberedReg : notClobberedRegisters) { + if(line.getAsm().contains(notClobberedReg)) { + // Found an A/X/Y in the asm where A/X/Y is not clobbered - remove the line + getLog().append("Removing interrupt register storage "+line.toString()+" in SEG"+interruptEntryExit.getIndex()+" "+interruptEntryExit.getSource()); + entryLines.remove(); + } + } + } + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialRegisterAnalysis.java b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialRegisterAnalysis.java index c84e5bd33..ffdfe0de5 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialRegisterAnalysis.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialRegisterAnalysis.java @@ -160,7 +160,7 @@ public class Pass4RegisterUpliftPotentialRegisterAnalysis extends Pass2Base { combination.allocate(getProgram()); // Generate ASM AsmProgram asm = new AsmProgram(); - asm.startSegment(statement.getIndex(), statement.toString(getProgram(), true)); + asm.startSegment(block.getScope(), statement.getIndex(), statement.toString(getProgram(), true)); Pass4CodeGeneration.AsmCodegenAluState aluState = new Pass4CodeGeneration.AsmCodegenAluState(); try { (new Pass4CodeGeneration(getProgram(), false)).generateStatementAsm(asm, block, statement, aluState, false); diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 558e0ff03..868c04ea9 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -46,10 +46,10 @@ public class TestPrograms { AsmFragmentTemplateUsages.logUsages(log, false, false, false, false, false, false); } - //@Test - //public void testIrqHardwareClobber() throws IOException, URISyntaxException { - // compileAndCompare("irq-hardware-clobber"); - //} + @Test + public void testIrqHardwareClobber() throws IOException, URISyntaxException { + compileAndCompare("irq-hardware-clobber"); + } @Test public void testIrqHardware() throws IOException, URISyntaxException { diff --git a/src/test/java/dk/camelot64/kickc/test/ref/irq-hardware-clobber.asm b/src/test/java/dk/camelot64/kickc/test/ref/irq-hardware-clobber.asm new file mode 100644 index 000000000..a0a5bd168 --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/ref/irq-hardware-clobber.asm @@ -0,0 +1,56 @@ +.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 + .label PROCPORT_DDR = 0 + .const PROCPORT_DDR_MEMORY_MASK = 7 + .label PROCPORT = 1 + .const PROCPORT_RAM_IO = $35 + jsr main +main: { + sei + lda #PROCPORT_DDR_MEMORY_MASK + sta PROCPORT_DDR + lda #PROCPORT_RAM_IO + sta PROCPORT + lda #CIA_INTERRUPT_CLEAR + sta CIA1_INTERRUPT + lda VIC_CONTROL + ora #$80 + sta VIC_CONTROL + lda #0 + sta RASTER + lda #IRQ_RASTER + sta IRQ_ENABLE + lda #irq + sta HARDWARE_IRQ+1 + cli + b2: + inc FGCOL + jmp b2 +} +irq: { + sta rega+1 + lda #WHITE + sta BGCOL + lda #BLACK + sta BGCOL + lda #IRQ_RASTER + sta IRQ_STATUS + rega: + lda #00 + rti +} diff --git a/src/test/java/dk/camelot64/kickc/test/ref/irq-hardware-clobber.cfg b/src/test/java/dk/camelot64/kickc/test/ref/irq-hardware-clobber.cfg new file mode 100644 index 000000000..488912a67 --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/ref/irq-hardware-clobber.cfg @@ -0,0 +1,31 @@ +@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 + asm { sei } + [5] *((const byte*) PROCPORT_DDR#0) ← (const byte) PROCPORT_DDR_MEMORY_MASK#0 [ ] ( main:2 [ ] ) + [6] *((const byte*) PROCPORT#0) ← (const byte) PROCPORT_RAM_IO#0 [ ] ( main:2 [ ] ) + [7] *((const byte*) CIA1_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 [ ] ( main:2 [ ] ) + [8] *((const byte*) VIC_CONTROL#0) ← *((const byte*) VIC_CONTROL#0) | (byte/word/signed word/dword/signed dword) 128 [ ] ( main:2 [ ] ) + [9] *((const byte*) RASTER#0) ← (byte/signed byte/word/signed word/dword/signed dword) 0 [ ] ( main:2 [ ] ) + [10] *((const byte*) IRQ_ENABLE#0) ← (const byte) IRQ_RASTER#0 [ ] ( main:2 [ ] ) + [11] *((const void()**) HARDWARE_IRQ#0) ← &interrupt(HARDWARE_CLOBBER)(void()) irq() [ ] ( main:2 [ ] ) + asm { cli } + to:main::@2 +main::@2: scope:[main] from main main::@2 + [13] *((const byte*) FGCOL#0) ← ++ *((const byte*) FGCOL#0) [ ] ( main:2 [ ] ) + to:main::@2 +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/java/dk/camelot64/kickc/test/ref/irq-hardware-clobber.log b/src/test/java/dk/camelot64/kickc/test/ref/irq-hardware-clobber.log new file mode 100644 index 000000000..11ca4e422 --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/ref/irq-hardware-clobber.log @@ -0,0 +1,789 @@ +PARSING src/test/java/dk/camelot64/kickc/test/kc/irq-hardware-clobber.kc +// A minimal working raster hardware IRQ with clobber-based register savings + +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_clobber) void irq() { + *BGCOL = WHITE; + *BGCOL = BLACK; + // Acknowledge the IRQ + *IRQ_STATUS = IRQ_RASTER; +} +Adding pre/post-modifier *((byte*) FGCOL) ← ++ *((byte*) FGCOL) +Resolved forward reference irq to interrupt(HARDWARE_CLOBBER)(void()) irq() +SYMBOLS +(label) @1 +(label) @2 +(label) @begin +(label) @end +(byte*) BGCOL +(byte) BLACK +(byte*) CIA1_INTERRUPT +(byte) CIA_INTERRUPT_CLEAR +(byte*) FGCOL +(void()**) HARDWARE_IRQ +(byte) IRQ_COLLISION_BG +(byte) IRQ_COLLISION_SPRITE +(byte*) IRQ_ENABLE +(byte) IRQ_LIGHTPEN +(byte) IRQ_RASTER +(byte*) IRQ_STATUS +(void()**) KERNEL_IRQ +(byte*) PROCPORT +(byte*) PROCPORT_DDR +(byte) PROCPORT_DDR_MEMORY_MASK +(byte) PROCPORT_RAM_IO +(byte*) RASTER +(byte*) VIC_CONTROL +(byte) WHITE +interrupt(HARDWARE_CLOBBER)(void()) irq() +(label) irq::@return +(void()) main() +(void()*~) main::$0 +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@5 +(label) main::@6 +(label) main::@return + +Promoting word/signed word/dword/signed dword to void()** in KERNEL_IRQ ← ((void()**)) 788 +Promoting word/dword/signed dword to void()** in HARDWARE_IRQ ← ((void()**)) 65534 +Promoting word/dword/signed dword to byte* in RASTER ← ((byte*)) 53266 +Promoting word/dword/signed dword to byte* in VIC_CONTROL ← ((byte*)) 53265 +Promoting word/dword/signed dword to byte* in IRQ_STATUS ← ((byte*)) 53273 +Promoting word/dword/signed dword to byte* in IRQ_ENABLE ← ((byte*)) 53274 +Promoting word/dword/signed dword to byte* in BGCOL ← ((byte*)) 53280 +Promoting word/dword/signed dword to byte* in FGCOL ← ((byte*)) 53281 +Promoting word/dword/signed dword to byte* in CIA1_INTERRUPT ← ((byte*)) 56333 +Promoting byte/signed byte/word/signed word/dword/signed dword to byte* in PROCPORT_DDR ← ((byte*)) 0 +Promoting byte/signed byte/word/signed word/dword/signed dword to byte* in PROCPORT ← ((byte*)) 1 +INITIAL CONTROL FLOW GRAPH +@begin: scope:[] from + (void()**) KERNEL_IRQ ← ((void()**)) (word/signed word/dword/signed dword) 788 + (void()**) HARDWARE_IRQ ← ((void()**)) (word/dword/signed dword) 65534 + (byte*) RASTER ← ((byte*)) (word/dword/signed dword) 53266 + (byte*) VIC_CONTROL ← ((byte*)) (word/dword/signed dword) 53265 + (byte*) IRQ_STATUS ← ((byte*)) (word/dword/signed dword) 53273 + (byte*) IRQ_ENABLE ← ((byte*)) (word/dword/signed dword) 53274 + (byte) IRQ_RASTER ← (byte/signed byte/word/signed word/dword/signed dword) 1 + (byte) IRQ_COLLISION_BG ← (byte/signed byte/word/signed word/dword/signed dword) 2 + (byte) IRQ_COLLISION_SPRITE ← (byte/signed byte/word/signed word/dword/signed dword) 4 + (byte) IRQ_LIGHTPEN ← (byte/signed byte/word/signed word/dword/signed dword) 8 + (byte*) BGCOL ← ((byte*)) (word/dword/signed dword) 53280 + (byte*) FGCOL ← ((byte*)) (word/dword/signed dword) 53281 + (byte) WHITE ← (byte/signed byte/word/signed word/dword/signed dword) 1 + (byte) BLACK ← (byte/signed byte/word/signed word/dword/signed dword) 0 + (byte*) CIA1_INTERRUPT ← ((byte*)) (word/dword/signed dword) 56333 + (byte) CIA_INTERRUPT_CLEAR ← (byte/signed byte/word/signed word/dword/signed dword) 127 + (byte*) PROCPORT_DDR ← ((byte*)) (byte/signed byte/word/signed word/dword/signed dword) 0 + (byte) PROCPORT_DDR_MEMORY_MASK ← (byte/signed byte/word/signed word/dword/signed dword) 7 + (byte*) PROCPORT ← ((byte*)) (byte/signed byte/word/signed word/dword/signed dword) 1 + (byte) PROCPORT_RAM_IO ← (byte/signed byte/word/signed word/dword/signed dword) 53 + to:@1 +main: scope:[main] from + asm { sei } + *((byte*) PROCPORT_DDR) ← (byte) PROCPORT_DDR_MEMORY_MASK + *((byte*) PROCPORT) ← (byte) PROCPORT_RAM_IO + *((byte*) CIA1_INTERRUPT) ← (byte) CIA_INTERRUPT_CLEAR + *((byte*) VIC_CONTROL) ← *((byte*) VIC_CONTROL) | (byte/word/signed word/dword/signed dword) 128 + *((byte*) RASTER) ← (byte/signed byte/word/signed word/dword/signed dword) 0 + *((byte*) IRQ_ENABLE) ← (byte) IRQ_RASTER + (void()*~) main::$0 ← & interrupt(HARDWARE_CLOBBER)(void()) irq() + *((void()**) HARDWARE_IRQ) ← (void()*~) main::$0 + asm { cli } + to:main::@1 +main::@1: scope:[main] from main main::@2 + if(true) goto main::@2 + to:main::@4 +main::@2: scope:[main] from main::@1 main::@5 + *((byte*) FGCOL) ← ++ *((byte*) FGCOL) + to:main::@1 +main::@4: scope:[main] from main::@1 + to:main::@3 +main::@3: scope:[main] from main::@4 main::@6 + to:main::@return +main::@5: scope:[main] from + to:main::@2 +main::@6: scope:[main] from + to:main::@3 +main::@return: scope:[main] from main::@3 + return + to:@return +@1: scope:[] from @begin + to:@2 +irq: scope:[irq] from + *((byte*) BGCOL) ← (byte) WHITE + *((byte*) BGCOL) ← (byte) BLACK + *((byte*) IRQ_STATUS) ← (byte) IRQ_RASTER + to:irq::@return +irq::@return: scope:[irq] from irq + return + to:@return +@2: scope:[] from @1 + call main + to:@end +@end: scope:[] from @2 + +Eliminating unused variable (void()**) KERNEL_IRQ and assignment [0] (void()**) KERNEL_IRQ ← ((void()**)) (word/signed word/dword/signed dword) 788 +Eliminating unused variable (byte) IRQ_COLLISION_BG and assignment [7] (byte) IRQ_COLLISION_BG ← (byte/signed byte/word/signed word/dword/signed dword) 2 +Eliminating unused variable (byte) IRQ_COLLISION_SPRITE and assignment [8] (byte) IRQ_COLLISION_SPRITE ← (byte/signed byte/word/signed word/dword/signed dword) 4 +Eliminating unused variable (byte) IRQ_LIGHTPEN and assignment [9] (byte) IRQ_LIGHTPEN ← (byte/signed byte/word/signed word/dword/signed dword) 8 +Removing empty block main::@4 +Removing empty block main::@3 +Removing empty block main::@5 +Removing empty block main::@6 +Removing empty block @1 +PROCEDURE MODIFY VARIABLE ANALYSIS + +Completing Phi functions... + +CONTROL FLOW GRAPH SSA WITH ASSIGNMENT CALL & RETURN +@begin: scope:[] from + (void()**) HARDWARE_IRQ#0 ← ((void()**)) (word/dword/signed dword) 65534 + (byte*) RASTER#0 ← ((byte*)) (word/dword/signed dword) 53266 + (byte*) VIC_CONTROL#0 ← ((byte*)) (word/dword/signed dword) 53265 + (byte*) IRQ_STATUS#0 ← ((byte*)) (word/dword/signed dword) 53273 + (byte*) IRQ_ENABLE#0 ← ((byte*)) (word/dword/signed dword) 53274 + (byte) IRQ_RASTER#0 ← (byte/signed byte/word/signed word/dword/signed dword) 1 + (byte*) BGCOL#0 ← ((byte*)) (word/dword/signed dword) 53280 + (byte*) FGCOL#0 ← ((byte*)) (word/dword/signed dword) 53281 + (byte) WHITE#0 ← (byte/signed byte/word/signed word/dword/signed dword) 1 + (byte) BLACK#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0 + (byte*) CIA1_INTERRUPT#0 ← ((byte*)) (word/dword/signed dword) 56333 + (byte) CIA_INTERRUPT_CLEAR#0 ← (byte/signed byte/word/signed word/dword/signed dword) 127 + (byte*) PROCPORT_DDR#0 ← ((byte*)) (byte/signed byte/word/signed word/dword/signed dword) 0 + (byte) PROCPORT_DDR_MEMORY_MASK#0 ← (byte/signed byte/word/signed word/dword/signed dword) 7 + (byte*) PROCPORT#0 ← ((byte*)) (byte/signed byte/word/signed word/dword/signed dword) 1 + (byte) PROCPORT_RAM_IO#0 ← (byte/signed byte/word/signed word/dword/signed dword) 53 + 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) | (byte/word/signed word/dword/signed dword) 128 + *((byte*) RASTER#0) ← (byte/signed byte/word/signed word/dword/signed dword) 0 + *((byte*) IRQ_ENABLE#0) ← (byte) IRQ_RASTER#0 + (void()*~) main::$0 ← & interrupt(HARDWARE_CLOBBER)(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_CLOBBER)(void()) irq() +(label) irq::@return +(void()) main() +(void()*~) main::$0 +(label) main::@1 +(label) main::@2 +(label) main::@return + +OPTIMIZING CONTROL FLOW GRAPH +Culled Empty Block (label) @3 +Succesful SSA optimization Pass2CullEmptyBlocks +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 = 127 +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 = 53 +Constant (const void()*) main::$0 = &irq +Succesful SSA optimization Pass2ConstantIdentification +if() condition always true - replacing block destination if(true) goto main::@2 +Succesful SSA optimization Pass2ConstantIfs +Removing unused block main::@return +Succesful SSA optimization Pass2EliminateUnusedBlocks +Culled Empty Block (label) main::@1 +Succesful SSA optimization Pass2CullEmptyBlocks +OPTIMIZING CONTROL FLOW GRAPH +Constant inlined main::$0 = &interrupt(HARDWARE_CLOBBER)(void()) irq() +Succesful SSA optimization Pass2ConstantInlining +Block Sequence Planned @begin @2 @end main main::@2 irq irq::@return +Block Sequence Planned @begin @2 @end main main::@2 irq irq::@return +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 + +Propagating live ranges... +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Block Sequence Planned @begin @2 @end main main::@2 irq irq::@return +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +Propagating live ranges... + +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 + asm { sei } + [5] *((const byte*) PROCPORT_DDR#0) ← (const byte) PROCPORT_DDR_MEMORY_MASK#0 [ ] ( main:2 [ ] ) + [6] *((const byte*) PROCPORT#0) ← (const byte) PROCPORT_RAM_IO#0 [ ] ( main:2 [ ] ) + [7] *((const byte*) CIA1_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 [ ] ( main:2 [ ] ) + [8] *((const byte*) VIC_CONTROL#0) ← *((const byte*) VIC_CONTROL#0) | (byte/word/signed word/dword/signed dword) 128 [ ] ( main:2 [ ] ) + [9] *((const byte*) RASTER#0) ← (byte/signed byte/word/signed word/dword/signed dword) 0 [ ] ( main:2 [ ] ) + [10] *((const byte*) IRQ_ENABLE#0) ← (const byte) IRQ_RASTER#0 [ ] ( main:2 [ ] ) + [11] *((const void()**) HARDWARE_IRQ#0) ← &interrupt(HARDWARE_CLOBBER)(void()) irq() [ ] ( main:2 [ ] ) + asm { cli } + to:main::@2 +main::@2: scope:[main] from main main::@2 + [13] *((const byte*) FGCOL#0) ← ++ *((const byte*) FGCOL#0) [ ] ( main:2 [ ] ) + to:main::@2 +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 + +DOMINATORS +@begin dominated by @begin +@2 dominated by @2 @begin +@end dominated by @2 @begin @end +main dominated by @2 @begin main +main::@2 dominated by @2 @begin main::@2 main +irq dominated by @2 @begin @end main::@2 irq irq::@return main +irq::@return dominated by @2 @begin @end main::@2 irq irq::@return main + +NATURAL LOOPS +Found back edge: Loop head: main::@2 tails: main::@2 blocks: null +Found back edge: Loop head: irq::@return tails: irq blocks: null +Populated: Loop head: main::@2 tails: main::@2 blocks: main::@2 +Populated: Loop head: irq::@return tails: irq blocks: irq +Loop head: main::@2 tails: main::@2 blocks: main::@2 +Loop head: irq::@return tails: irq blocks: irq + +NATURAL LOOPS WITH DEPTH +Found 1 loops in scope [irq] + Loop head: irq::@return tails: irq blocks: irq +Found 0 loops in scope [] +Found 1 loops in scope [main] + Loop head: main::@2 tails: main::@2 blocks: main::@2 +Loop head: main::@2 tails: main::@2 blocks: main::@2 depth: 1 +Loop head: irq::@return tails: irq blocks: irq depth: 1 + + +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_CLOBBER)(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 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 + .label PROCPORT_DDR = 0 + .const PROCPORT_DDR_MEMORY_MASK = 7 + .label PROCPORT = 1 + .const PROCPORT_RAM_IO = $35 +//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 asm { sei } + sei + //SEG10 [5] *((const byte*) PROCPORT_DDR#0) ← (const byte) PROCPORT_DDR_MEMORY_MASK#0 [ ] ( main:2 [ ] ) -- _deref_pbuc1=vbuc2 + lda #PROCPORT_DDR_MEMORY_MASK + sta PROCPORT_DDR + //SEG11 [6] *((const byte*) PROCPORT#0) ← (const byte) PROCPORT_RAM_IO#0 [ ] ( main:2 [ ] ) -- _deref_pbuc1=vbuc2 + lda #PROCPORT_RAM_IO + sta PROCPORT + //SEG12 [7] *((const byte*) CIA1_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 [ ] ( main:2 [ ] ) -- _deref_pbuc1=vbuc2 + lda #CIA_INTERRUPT_CLEAR + sta CIA1_INTERRUPT + //SEG13 [8] *((const byte*) VIC_CONTROL#0) ← *((const byte*) VIC_CONTROL#0) | (byte/word/signed word/dword/signed dword) 128 [ ] ( main:2 [ ] ) -- _deref_pbuc1=_deref_pbuc1_bor_vbuc2 + lda VIC_CONTROL + ora #$80 + sta VIC_CONTROL + //SEG14 [9] *((const byte*) RASTER#0) ← (byte/signed byte/word/signed word/dword/signed dword) 0 [ ] ( main:2 [ ] ) -- _deref_pbuc1=vbuc2 + lda #0 + sta RASTER + //SEG15 [10] *((const byte*) IRQ_ENABLE#0) ← (const byte) IRQ_RASTER#0 [ ] ( main:2 [ ] ) -- _deref_pbuc1=vbuc2 + lda #IRQ_RASTER + sta IRQ_ENABLE + //SEG16 [11] *((const void()**) HARDWARE_IRQ#0) ← &interrupt(HARDWARE_CLOBBER)(void()) irq() [ ] ( main:2 [ ] ) -- _deref_pptc1=pprc2 + lda #irq + sta HARDWARE_IRQ+1 + //SEG17 asm { cli } + cli + jmp b2 + //SEG18 main::@2 + b2: + //SEG19 [13] *((const byte*) FGCOL#0) ← ++ *((const byte*) FGCOL#0) [ ] ( main:2 [ ] ) -- _deref_pbuc1=_inc__deref_pbuc1 + inc FGCOL + jmp b2 +} +//SEG20 irq +irq: { + //SEG21 entry interrupt(HARDWARE_CLOBBER) + sta rega+1 + stx regx+1 + sty regy+1 + //SEG22 [14] *((const byte*) BGCOL#0) ← (const byte) WHITE#0 [ ] ( ) -- _deref_pbuc1=vbuc2 + lda #WHITE + sta BGCOL + //SEG23 [15] *((const byte*) BGCOL#0) ← (const byte) BLACK#0 [ ] ( ) -- _deref_pbuc1=vbuc2 + lda #BLACK + sta BGCOL + //SEG24 [16] *((const byte*) IRQ_STATUS#0) ← (const byte) IRQ_RASTER#0 [ ] ( ) -- _deref_pbuc1=vbuc2 + lda #IRQ_RASTER + sta IRQ_STATUS + jmp breturn + //SEG25 irq::@return + breturn: + //SEG26 [17] return [ ] ( ) - exit interrupt(HARDWARE_CLOBBER) + rega: + lda #00 + regx: + ldx #00 + regy: + ldy #00 + rti +} + +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/word/signed word/dword/signed dword) 128 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [9] *((const byte*) RASTER#0) ← (byte/signed byte/word/signed word/dword/signed dword) 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_CLOBBER)(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 503 combination +Uplifting [irq] best 503 combination +Uplifting [] best 503 combination +Interrupt procedure irq clobbers ANZ +Removing interrupt register storage stx regx+1 in SEG21 entry interrupt(HARDWARE_CLOBBER) +Removing interrupt register storage sty regy+1 in SEG21 entry interrupt(HARDWARE_CLOBBER) +Removing interrupt register storage regx: in SEG26 [17] return [ ] ( ) - exit interrupt(HARDWARE_CLOBBER) +Removing interrupt register storage ldx #00 in SEG26 [17] return [ ] ( ) - exit interrupt(HARDWARE_CLOBBER) +Removing interrupt register storage regy: in SEG26 [17] return [ ] ( ) - exit interrupt(HARDWARE_CLOBBER) +Removing interrupt register storage ldy #00 in SEG26 [17] return [ ] ( ) - exit interrupt(HARDWARE_CLOBBER) + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG1 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 + .label PROCPORT_DDR = 0 + .const PROCPORT_DDR_MEMORY_MASK = 7 + .label PROCPORT = 1 + .const PROCPORT_RAM_IO = $35 +//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 asm { sei } + sei + //SEG10 [5] *((const byte*) PROCPORT_DDR#0) ← (const byte) PROCPORT_DDR_MEMORY_MASK#0 [ ] ( main:2 [ ] ) -- _deref_pbuc1=vbuc2 + lda #PROCPORT_DDR_MEMORY_MASK + sta PROCPORT_DDR + //SEG11 [6] *((const byte*) PROCPORT#0) ← (const byte) PROCPORT_RAM_IO#0 [ ] ( main:2 [ ] ) -- _deref_pbuc1=vbuc2 + lda #PROCPORT_RAM_IO + sta PROCPORT + //SEG12 [7] *((const byte*) CIA1_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 [ ] ( main:2 [ ] ) -- _deref_pbuc1=vbuc2 + lda #CIA_INTERRUPT_CLEAR + sta CIA1_INTERRUPT + //SEG13 [8] *((const byte*) VIC_CONTROL#0) ← *((const byte*) VIC_CONTROL#0) | (byte/word/signed word/dword/signed dword) 128 [ ] ( main:2 [ ] ) -- _deref_pbuc1=_deref_pbuc1_bor_vbuc2 + lda VIC_CONTROL + ora #$80 + sta VIC_CONTROL + //SEG14 [9] *((const byte*) RASTER#0) ← (byte/signed byte/word/signed word/dword/signed dword) 0 [ ] ( main:2 [ ] ) -- _deref_pbuc1=vbuc2 + lda #0 + sta RASTER + //SEG15 [10] *((const byte*) IRQ_ENABLE#0) ← (const byte) IRQ_RASTER#0 [ ] ( main:2 [ ] ) -- _deref_pbuc1=vbuc2 + lda #IRQ_RASTER + sta IRQ_ENABLE + //SEG16 [11] *((const void()**) HARDWARE_IRQ#0) ← &interrupt(HARDWARE_CLOBBER)(void()) irq() [ ] ( main:2 [ ] ) -- _deref_pptc1=pprc2 + lda #irq + sta HARDWARE_IRQ+1 + //SEG17 asm { cli } + cli + jmp b2 + //SEG18 main::@2 + b2: + //SEG19 [13] *((const byte*) FGCOL#0) ← ++ *((const byte*) FGCOL#0) [ ] ( main:2 [ ] ) -- _deref_pbuc1=_inc__deref_pbuc1 + inc FGCOL + jmp b2 +} +//SEG20 irq +irq: { + //SEG21 entry interrupt(HARDWARE_CLOBBER) + sta rega+1 + //SEG22 [14] *((const byte*) BGCOL#0) ← (const byte) WHITE#0 [ ] ( ) -- _deref_pbuc1=vbuc2 + lda #WHITE + sta BGCOL + //SEG23 [15] *((const byte*) BGCOL#0) ← (const byte) BLACK#0 [ ] ( ) -- _deref_pbuc1=vbuc2 + lda #BLACK + sta BGCOL + //SEG24 [16] *((const byte*) IRQ_STATUS#0) ← (const byte) IRQ_RASTER#0 [ ] ( ) -- _deref_pbuc1=vbuc2 + lda #IRQ_RASTER + sta IRQ_STATUS + jmp breturn + //SEG25 irq::@return + breturn: + //SEG26 [17] return [ ] ( ) - exit interrupt(HARDWARE_CLOBBER) + rega: + lda #00 + rti +} + +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: +Removing instruction breturn: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction b2: +Removing instruction bend: +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) BLACK +(const byte) BLACK#0 BLACK = (byte/signed byte/word/signed word/dword/signed dword) 0 +(byte*) CIA1_INTERRUPT +(const byte*) CIA1_INTERRUPT#0 CIA1_INTERRUPT = ((byte*))(word/dword/signed dword) 56333 +(byte) CIA_INTERRUPT_CLEAR +(const byte) CIA_INTERRUPT_CLEAR#0 CIA_INTERRUPT_CLEAR = (byte/signed byte/word/signed word/dword/signed dword) 127 +(byte*) FGCOL +(const byte*) FGCOL#0 FGCOL = ((byte*))(word/dword/signed dword) 53281 +(void()**) HARDWARE_IRQ +(const void()**) HARDWARE_IRQ#0 HARDWARE_IRQ = ((void()**))(word/dword/signed dword) 65534 +(byte*) IRQ_ENABLE +(const byte*) IRQ_ENABLE#0 IRQ_ENABLE = ((byte*))(word/dword/signed dword) 53274 +(byte) IRQ_RASTER +(const byte) IRQ_RASTER#0 IRQ_RASTER = (byte/signed byte/word/signed word/dword/signed dword) 1 +(byte*) IRQ_STATUS +(const byte*) IRQ_STATUS#0 IRQ_STATUS = ((byte*))(word/dword/signed dword) 53273 +(byte*) PROCPORT +(const byte*) PROCPORT#0 PROCPORT = ((byte*))(byte/signed byte/word/signed word/dword/signed dword) 1 +(byte*) PROCPORT_DDR +(const byte*) PROCPORT_DDR#0 PROCPORT_DDR = ((byte*))(byte/signed byte/word/signed word/dword/signed dword) 0 +(byte) PROCPORT_DDR_MEMORY_MASK +(const byte) PROCPORT_DDR_MEMORY_MASK#0 PROCPORT_DDR_MEMORY_MASK = (byte/signed byte/word/signed word/dword/signed dword) 7 +(byte) PROCPORT_RAM_IO +(const byte) PROCPORT_RAM_IO#0 PROCPORT_RAM_IO = (byte/signed byte/word/signed word/dword/signed dword) 53 +(byte*) RASTER +(const byte*) RASTER#0 RASTER = ((byte*))(word/dword/signed dword) 53266 +(byte*) VIC_CONTROL +(const byte*) VIC_CONTROL#0 VIC_CONTROL = ((byte*))(word/dword/signed dword) 53265 +(byte) WHITE +(const byte) WHITE#0 WHITE = (byte/signed byte/word/signed word/dword/signed dword) 1 +interrupt(HARDWARE_CLOBBER)(void()) irq() +(label) irq::@return +(void()) main() +(label) main::@2 + + + +FINAL ASSEMBLER +Score: 380 + +//SEG0 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG1 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 + .label PROCPORT_DDR = 0 + .const PROCPORT_DDR_MEMORY_MASK = 7 + .label PROCPORT = 1 + .const PROCPORT_RAM_IO = $35 +//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 asm { sei } + sei + //SEG10 [5] *((const byte*) PROCPORT_DDR#0) ← (const byte) PROCPORT_DDR_MEMORY_MASK#0 [ ] ( main:2 [ ] ) -- _deref_pbuc1=vbuc2 + lda #PROCPORT_DDR_MEMORY_MASK + sta PROCPORT_DDR + //SEG11 [6] *((const byte*) PROCPORT#0) ← (const byte) PROCPORT_RAM_IO#0 [ ] ( main:2 [ ] ) -- _deref_pbuc1=vbuc2 + lda #PROCPORT_RAM_IO + sta PROCPORT + //SEG12 [7] *((const byte*) CIA1_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 [ ] ( main:2 [ ] ) -- _deref_pbuc1=vbuc2 + lda #CIA_INTERRUPT_CLEAR + sta CIA1_INTERRUPT + //SEG13 [8] *((const byte*) VIC_CONTROL#0) ← *((const byte*) VIC_CONTROL#0) | (byte/word/signed word/dword/signed dword) 128 [ ] ( main:2 [ ] ) -- _deref_pbuc1=_deref_pbuc1_bor_vbuc2 + lda VIC_CONTROL + ora #$80 + sta VIC_CONTROL + //SEG14 [9] *((const byte*) RASTER#0) ← (byte/signed byte/word/signed word/dword/signed dword) 0 [ ] ( main:2 [ ] ) -- _deref_pbuc1=vbuc2 + lda #0 + sta RASTER + //SEG15 [10] *((const byte*) IRQ_ENABLE#0) ← (const byte) IRQ_RASTER#0 [ ] ( main:2 [ ] ) -- _deref_pbuc1=vbuc2 + lda #IRQ_RASTER + sta IRQ_ENABLE + //SEG16 [11] *((const void()**) HARDWARE_IRQ#0) ← &interrupt(HARDWARE_CLOBBER)(void()) irq() [ ] ( main:2 [ ] ) -- _deref_pptc1=pprc2 + lda #irq + sta HARDWARE_IRQ+1 + //SEG17 asm { cli } + cli + //SEG18 main::@2 + b2: + //SEG19 [13] *((const byte*) FGCOL#0) ← ++ *((const byte*) FGCOL#0) [ ] ( main:2 [ ] ) -- _deref_pbuc1=_inc__deref_pbuc1 + inc FGCOL + jmp b2 +} +//SEG20 irq +irq: { + //SEG21 entry interrupt(HARDWARE_CLOBBER) + sta rega+1 + //SEG22 [14] *((const byte*) BGCOL#0) ← (const byte) WHITE#0 [ ] ( ) -- _deref_pbuc1=vbuc2 + lda #WHITE + sta BGCOL + //SEG23 [15] *((const byte*) BGCOL#0) ← (const byte) BLACK#0 [ ] ( ) -- _deref_pbuc1=vbuc2 + lda #BLACK + sta BGCOL + //SEG24 [16] *((const byte*) IRQ_STATUS#0) ← (const byte) IRQ_RASTER#0 [ ] ( ) -- _deref_pbuc1=vbuc2 + lda #IRQ_RASTER + sta IRQ_STATUS + //SEG25 irq::@return + //SEG26 [17] return [ ] ( ) - exit interrupt(HARDWARE_CLOBBER) + rega: + lda #00 + rti +} + diff --git a/src/test/java/dk/camelot64/kickc/test/ref/irq-hardware-clobber.sym b/src/test/java/dk/camelot64/kickc/test/ref/irq-hardware-clobber.sym new file mode 100644 index 000000000..407379094 --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/ref/irq-hardware-clobber.sym @@ -0,0 +1,40 @@ +(label) @2 +(label) @begin +(label) @end +(byte*) BGCOL +(const byte*) BGCOL#0 BGCOL = ((byte*))(word/dword/signed dword) 53280 +(byte) BLACK +(const byte) BLACK#0 BLACK = (byte/signed byte/word/signed word/dword/signed dword) 0 +(byte*) CIA1_INTERRUPT +(const byte*) CIA1_INTERRUPT#0 CIA1_INTERRUPT = ((byte*))(word/dword/signed dword) 56333 +(byte) CIA_INTERRUPT_CLEAR +(const byte) CIA_INTERRUPT_CLEAR#0 CIA_INTERRUPT_CLEAR = (byte/signed byte/word/signed word/dword/signed dword) 127 +(byte*) FGCOL +(const byte*) FGCOL#0 FGCOL = ((byte*))(word/dword/signed dword) 53281 +(void()**) HARDWARE_IRQ +(const void()**) HARDWARE_IRQ#0 HARDWARE_IRQ = ((void()**))(word/dword/signed dword) 65534 +(byte*) IRQ_ENABLE +(const byte*) IRQ_ENABLE#0 IRQ_ENABLE = ((byte*))(word/dword/signed dword) 53274 +(byte) IRQ_RASTER +(const byte) IRQ_RASTER#0 IRQ_RASTER = (byte/signed byte/word/signed word/dword/signed dword) 1 +(byte*) IRQ_STATUS +(const byte*) IRQ_STATUS#0 IRQ_STATUS = ((byte*))(word/dword/signed dword) 53273 +(byte*) PROCPORT +(const byte*) PROCPORT#0 PROCPORT = ((byte*))(byte/signed byte/word/signed word/dword/signed dword) 1 +(byte*) PROCPORT_DDR +(const byte*) PROCPORT_DDR#0 PROCPORT_DDR = ((byte*))(byte/signed byte/word/signed word/dword/signed dword) 0 +(byte) PROCPORT_DDR_MEMORY_MASK +(const byte) PROCPORT_DDR_MEMORY_MASK#0 PROCPORT_DDR_MEMORY_MASK = (byte/signed byte/word/signed word/dword/signed dword) 7 +(byte) PROCPORT_RAM_IO +(const byte) PROCPORT_RAM_IO#0 PROCPORT_RAM_IO = (byte/signed byte/word/signed word/dword/signed dword) 53 +(byte*) RASTER +(const byte*) RASTER#0 RASTER = ((byte*))(word/dword/signed dword) 53266 +(byte*) VIC_CONTROL +(const byte*) VIC_CONTROL#0 VIC_CONTROL = ((byte*))(word/dword/signed dword) 53265 +(byte) WHITE +(const byte) WHITE#0 WHITE = (byte/signed byte/word/signed word/dword/signed dword) 1 +interrupt(HARDWARE_CLOBBER)(void()) irq() +(label) irq::@return +(void()) main() +(label) main::@2 +