diff --git a/src/main/java/dk/camelot64/kickc/KickC.java b/src/main/java/dk/camelot64/kickc/KickC.java index e1c0f7fd5..3a636e136 100644 --- a/src/main/java/dk/camelot64/kickc/KickC.java +++ b/src/main/java/dk/camelot64/kickc/KickC.java @@ -32,7 +32,7 @@ import java.util.stream.Collectors; descriptionHeading = "%nDescription:%n%n", parameterListHeading = "%nParameters:%n", optionListHeading = "%nOptions:%n", - version = "KickC 0.8.1 BETA" + version = "KickC 0.8.2 BETA" ) public class KickC implements Callable { diff --git a/src/main/kc/target/atari2600.ld b/src/main/kc/target/atari2600.ld new file mode 100644 index 000000000..44a063c52 --- /dev/null +++ b/src/main/kc/target/atari2600.ld @@ -0,0 +1,10 @@ +// Atari 2600 VCS 4K ROM +.file [name="%O.prg", type="bin", segments="Code, Vectors"] +.segmentdef Code [start=$f800,min=$f800,max=$fff9] +.segmentdef Data [start=$80,max=$ff, virtual] +.segmentdef Vectors [start=$fffa,max=$ffff] +.segment Vectors +.word %E // NMI +.word %E // RESET +.word %E // IRQ +.segment Code diff --git a/src/main/kc/target/atari2600.tgt b/src/main/kc/target/atari2600.tgt new file mode 100644 index 000000000..defda335d --- /dev/null +++ b/src/main/kc/target/atari2600.tgt @@ -0,0 +1,8 @@ +{ + "link": "atari2600.ld", + "cpu": "MOS6502X", + "emulator": "stella", + "defines": { + "__ATARI2600__": 1 + } +} \ No newline at end of file diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 976c05a50..8845ddb4c 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -44,12 +44,16 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testAtari2600Min() throws IOException, URISyntaxException { + compileAndCompare("atari2600-min.c"); + } + @Test public void testVic20Raster() throws IOException, URISyntaxException { compileAndCompare("vic20-raster.c"); } - @Test public void testVic20Simple() throws IOException, URISyntaxException { compileAndCompare("vic20-simple.c"); diff --git a/src/test/kc/atari2600-min.c b/src/test/kc/atari2600-min.c new file mode 100644 index 000000000..d3a3ba951 --- /dev/null +++ b/src/test/kc/atari2600-min.c @@ -0,0 +1,51 @@ +// Minimal Atari 2600 VCS Program +#pragma target(atari2600) + +char * const VSYNC = 0x00; +char * const VBLANK = 0x01; +char * const WSYNC = 0x02; +char * const BACKGROUND_COLOR = 0x09; + +char __mem col=0; + +void main() { + while(1) { + + // Vertical Sync + // here we generate the signal that tells the TV to move the beam to the top of + // the screen so we can start the next frame of video. + // The Sync Signal must be on for 3 scanlines. + *WSYNC = 2; // Wait for SYNC (halts CPU until end of scanline) + *VSYNC = 2; // Accumulator D1=1, turns on Vertical Sync signal + *WSYNC = 2; // Wait for Sync - halts CPU until end of 1st scanline of VSYNC + *WSYNC = 2; // wait until end of 2nd scanline of VSYNC + *WSYNC = 0; // wait until end of 3rd scanline of VSYNC + *VSYNC = 0; // Accumulator D1=0, turns off Vertical Sync signal + + // Vertical Blank - game logic + // Since we don't have any yet, just delay + for(char i=0;i<37;i++) { + *WSYNC = 0; // Wait for SYNC (halts CPU until end of scanline) + } + + // Screen - display logic + // Update the registers in TIA (the video chip) in order to generate what the player sees. + // For now we're just going to output 192 colored scanlines lines so we have something to see. + *VBLANK = 0; // D1=1, turns off Vertical Blank signal (image output on) + char c = col++; + for(char i=0;i<192;i++) { + *WSYNC = 0; // Wait for SYNC (halts CPU until end of scanline) + *BACKGROUND_COLOR = c++; // Set background color + } + + // Overscan - game logic + // Since we don't have any yet, just delay + *WSYNC = 0; // Wait for SYNC (halts CPU until end of scanline) + *VBLANK = 2; // // D1=1 turns image output off + *BACKGROUND_COLOR = 0; + for(char i=0;i<07;i++) { + *WSYNC = 0; // Wait for SYNC (halts CPU until end of scanline) + } + + } +} diff --git a/src/test/ref/atari2600-min.asm b/src/test/ref/atari2600-min.asm new file mode 100644 index 000000000..29bbff942 --- /dev/null +++ b/src/test/ref/atari2600-min.asm @@ -0,0 +1,113 @@ +// Minimal Atari 2600 VCS Program + // Atari 2600 VCS 4K ROM +.file [name="atari2600-min.prg", type="bin", segments="Code, Vectors"] +.segmentdef Code [start=$f800,min=$f800,max=$fff9] +.segmentdef Data [start=$80,max=$ff, virtual] +.segmentdef Vectors [start=$fffa,max=$ffff] +.segment Vectors +.word main // NMI +.word main // RESET +.word main // IRQ +.segment Code + + .label VSYNC = 0 + .label VBLANK = 1 + .label WSYNC = 2 + .label BACKGROUND_COLOR = 9 +.segment Code +main: { + lda #0 + sta col + __b2: + // *WSYNC = 2 + // Vertical Sync + // here we generate the signal that tells the TV to move the beam to the top of + // the screen so we can start the next frame of video. + // The Sync Signal must be on for 3 scanlines. + lda #2 + sta WSYNC + // *VSYNC = 2 + // Wait for SYNC (halts CPU until end of scanline) + sta VSYNC + // *WSYNC = 2 + // Accumulator D1=1, turns on Vertical Sync signal + sta WSYNC + // Wait for Sync - halts CPU until end of 1st scanline of VSYNC + sta WSYNC + // *WSYNC = 0 + // wait until end of 2nd scanline of VSYNC + lda #0 + sta WSYNC + // *VSYNC = 0 + // wait until end of 3rd scanline of VSYNC + sta VSYNC + tax + // Vertical Blank - game logic + // Since we don't have any yet, just delay + __b3: + // for(char i=0;i<37;i++) + cpx #$25 + bcc __b4 + // *VBLANK = 0 + // Screen - display logic + // Update the registers in TIA (the video chip) in order to generate what the player sees. + // For now we're just going to output 192 colored scanlines lines so we have something to see. + lda #0 + sta VBLANK + // c = col++ + // D1=1, turns off Vertical Blank signal (image output on) + ldx col + inc col + tay + __b6: + // for(char i=0;i<192;i++) + cpy #$c0 + bcc __b7 + // *WSYNC = 0 + // Overscan - game logic + // Since we don't have any yet, just delay + lda #0 + sta WSYNC + // *VBLANK = 2 + // Wait for SYNC (halts CPU until end of scanline) + lda #2 + sta VBLANK + // *BACKGROUND_COLOR = 0 + // // D1=1 turns image output off + lda #0 + sta BACKGROUND_COLOR + tax + __b9: + // for(char i=0;i<07;i++) + cpx #7 + bcc __b10 + jmp __b2 + __b10: + // *WSYNC = 0 + lda #0 + sta WSYNC + // for(char i=0;i<07;i++) + inx + jmp __b9 + __b7: + // *WSYNC = 0 + lda #0 + sta WSYNC + // *BACKGROUND_COLOR = c++ + // Wait for SYNC (halts CPU until end of scanline) + stx BACKGROUND_COLOR + // *BACKGROUND_COLOR = c++; + inx + // for(char i=0;i<192;i++) + iny + jmp __b6 + __b4: + // *WSYNC = 0 + lda #0 + sta WSYNC + // for(char i=0;i<37;i++) + inx + jmp __b3 +} +.segment Data + col: .byte 0 diff --git a/src/test/ref/atari2600-min.cfg b/src/test/ref/atari2600-min.cfg new file mode 100644 index 000000000..38b5b9dc4 --- /dev/null +++ b/src/test/ref/atari2600-min.cfg @@ -0,0 +1,62 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() + +(void()) main() +main: scope:[main] from @1 + [4] phi() + to:main::@1 +main::@1: scope:[main] from main main::@9 + [5] (byte) col#12 ← phi( main/(byte) 0 main::@9/(byte) col#1 ) + to:main::@2 +main::@2: scope:[main] from main::@1 + [6] *((const nomodify byte*) WSYNC) ← (byte) 2 + [7] *((const nomodify byte*) VSYNC) ← (byte) 2 + [8] *((const nomodify byte*) WSYNC) ← (byte) 2 + [9] *((const nomodify byte*) WSYNC) ← (byte) 2 + [10] *((const nomodify byte*) WSYNC) ← (byte) 0 + [11] *((const nomodify byte*) VSYNC) ← (byte) 0 + to:main::@3 +main::@3: scope:[main] from main::@2 main::@4 + [12] (byte) main::i#2 ← phi( main::@2/(byte) 0 main::@4/(byte) main::i#1 ) + [13] if((byte) main::i#2<(byte) $25) goto main::@4 + to:main::@5 +main::@5: scope:[main] from main::@3 + [14] *((const nomodify byte*) VBLANK) ← (byte) 0 + [15] (byte) main::c#0 ← (byte) col#12 + [16] (byte) col#1 ← ++ (byte) col#12 + to:main::@6 +main::@6: scope:[main] from main::@5 main::@7 + [17] (byte) main::c#2 ← phi( main::@5/(byte) main::c#0 main::@7/(byte) main::c#1 ) + [17] (byte) main::i1#2 ← phi( main::@5/(byte) 0 main::@7/(byte) main::i1#1 ) + [18] if((byte) main::i1#2<(byte) $c0) goto main::@7 + to:main::@8 +main::@8: scope:[main] from main::@6 + [19] *((const nomodify byte*) WSYNC) ← (byte) 0 + [20] *((const nomodify byte*) VBLANK) ← (byte) 2 + [21] *((const nomodify byte*) BACKGROUND_COLOR) ← (byte) 0 + to:main::@9 +main::@9: scope:[main] from main::@10 main::@8 + [22] (byte) main::i2#2 ← phi( main::@10/(byte) main::i2#1 main::@8/(byte) 0 ) + [23] if((byte) main::i2#2<(byte) 7) goto main::@10 + to:main::@1 +main::@10: scope:[main] from main::@9 + [24] *((const nomodify byte*) WSYNC) ← (byte) 0 + [25] (byte) main::i2#1 ← ++ (byte) main::i2#2 + to:main::@9 +main::@7: scope:[main] from main::@6 + [26] *((const nomodify byte*) WSYNC) ← (byte) 0 + [27] *((const nomodify byte*) BACKGROUND_COLOR) ← (byte) main::c#2 + [28] (byte) main::c#1 ← ++ (byte) main::c#2 + [29] (byte) main::i1#1 ← ++ (byte) main::i1#2 + to:main::@6 +main::@4: scope:[main] from main::@3 + [30] *((const nomodify byte*) WSYNC) ← (byte) 0 + [31] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@3 diff --git a/src/test/ref/atari2600-min.log b/src/test/ref/atari2600-min.log new file mode 100644 index 000000000..629d0c230 --- /dev/null +++ b/src/test/ref/atari2600-min.log @@ -0,0 +1,1095 @@ + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte) col#0 ← (byte) 0 + to:@1 + +(void()) main() +main: scope:[main] from @1 + (byte) col#10 ← phi( @1/(byte) col#9 ) + to:main::@1 +main::@1: scope:[main] from main main::@9 + (byte) col#8 ← phi( main/(byte) col#10 main::@9/(byte) col#11 ) + (bool~) main::$3 ← (number) 0 != (number) 1 + if((bool~) main::$3) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + (byte) col#12 ← phi( main::@1/(byte) col#8 ) + *((const nomodify byte*) WSYNC) ← (number) 2 + *((const nomodify byte*) VSYNC) ← (number) 2 + *((const nomodify byte*) WSYNC) ← (number) 2 + *((const nomodify byte*) WSYNC) ← (number) 2 + *((const nomodify byte*) WSYNC) ← (number) 0 + *((const nomodify byte*) VSYNC) ← (number) 0 + (byte) main::i#0 ← (byte) 0 + to:main::@3 +main::@3: scope:[main] from main::@2 main::@4 + (byte) col#7 ← phi( main::@2/(byte) col#12 main::@4/(byte) col#13 ) + (byte) main::i#2 ← phi( main::@2/(byte) main::i#0 main::@4/(byte) main::i#1 ) + (bool~) main::$0 ← (byte) main::i#2 < (number) $25 + if((bool~) main::$0) goto main::@4 + to:main::@5 +main::@4: scope:[main] from main::@3 + (byte) col#13 ← phi( main::@3/(byte) col#7 ) + (byte) main::i#3 ← phi( main::@3/(byte) main::i#2 ) + *((const nomodify byte*) WSYNC) ← (number) 0 + (byte) main::i#1 ← ++ (byte) main::i#3 + to:main::@3 +main::@5: scope:[main] from main::@3 + (byte) col#4 ← phi( main::@3/(byte) col#7 ) + *((const nomodify byte*) VBLANK) ← (number) 0 + (byte) main::c#0 ← (byte) col#4 + (byte) col#1 ← ++ (byte) col#4 + (byte) main::i1#0 ← (byte) 0 + to:main::@6 +main::@6: scope:[main] from main::@5 main::@7 + (byte) col#16 ← phi( main::@5/(byte) col#1 main::@7/(byte) col#17 ) + (byte) main::c#3 ← phi( main::@5/(byte) main::c#0 main::@7/(byte) main::c#1 ) + (byte) main::i1#2 ← phi( main::@5/(byte) main::i1#0 main::@7/(byte) main::i1#1 ) + (bool~) main::$1 ← (byte) main::i1#2 < (number) $c0 + if((bool~) main::$1) goto main::@7 + to:main::@8 +main::@7: scope:[main] from main::@6 + (byte) col#17 ← phi( main::@6/(byte) col#16 ) + (byte) main::i1#3 ← phi( main::@6/(byte) main::i1#2 ) + (byte) main::c#2 ← phi( main::@6/(byte) main::c#3 ) + *((const nomodify byte*) WSYNC) ← (number) 0 + *((const nomodify byte*) BACKGROUND_COLOR) ← (byte) main::c#2 + (byte) main::c#1 ← ++ (byte) main::c#2 + (byte) main::i1#1 ← ++ (byte) main::i1#3 + to:main::@6 +main::@8: scope:[main] from main::@6 + (byte) col#15 ← phi( main::@6/(byte) col#16 ) + *((const nomodify byte*) WSYNC) ← (number) 0 + *((const nomodify byte*) VBLANK) ← (number) 2 + *((const nomodify byte*) BACKGROUND_COLOR) ← (number) 0 + (byte) main::i2#0 ← (byte) 0 + to:main::@9 +main::@9: scope:[main] from main::@10 main::@8 + (byte) col#11 ← phi( main::@10/(byte) col#14 main::@8/(byte) col#15 ) + (byte) main::i2#2 ← phi( main::@10/(byte) main::i2#1 main::@8/(byte) main::i2#0 ) + (bool~) main::$2 ← (byte) main::i2#2 < (number) 7 + if((bool~) main::$2) goto main::@10 + to:main::@1 +main::@10: scope:[main] from main::@9 + (byte) col#14 ← phi( main::@9/(byte) col#11 ) + (byte) main::i2#3 ← phi( main::@9/(byte) main::i2#2 ) + *((const nomodify byte*) WSYNC) ← (number) 0 + (byte) main::i2#1 ← ++ (byte) main::i2#3 + to:main::@9 +main::@return: scope:[main] from main::@1 + (byte) col#5 ← phi( main::@1/(byte) col#8 ) + (byte) col#2 ← (byte) col#5 + return + to:@return +@1: scope:[] from @begin + (byte) col#9 ← phi( @begin/(byte) col#0 ) + call main + to:@2 +@2: scope:[] from @1 + (byte) col#6 ← phi( @1/(byte) col#2 ) + (byte) col#3 ← (byte) col#6 + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(label) @1 +(label) @2 +(label) @begin +(label) @end +(const nomodify byte*) BACKGROUND_COLOR = (byte*)(number) 9 +(const nomodify byte*) VBLANK = (byte*)(number) 1 +(const nomodify byte*) VSYNC = (byte*)(number) 0 +(const nomodify byte*) WSYNC = (byte*)(number) 2 +(byte) col +(byte) col#0 +(byte) col#1 +(byte) col#10 +(byte) col#11 +(byte) col#12 +(byte) col#13 +(byte) col#14 +(byte) col#15 +(byte) col#16 +(byte) col#17 +(byte) col#2 +(byte) col#3 +(byte) col#4 +(byte) col#5 +(byte) col#6 +(byte) col#7 +(byte) col#8 +(byte) col#9 +(void()) main() +(bool~) main::$0 +(bool~) main::$1 +(bool~) main::$2 +(bool~) main::$3 +(label) main::@1 +(label) main::@10 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@5 +(label) main::@6 +(label) main::@7 +(label) main::@8 +(label) main::@9 +(label) main::@return +(byte) main::c +(byte) main::c#0 +(byte) main::c#1 +(byte) main::c#2 +(byte) main::c#3 +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 +(byte) main::i1 +(byte) main::i1#0 +(byte) main::i1#1 +(byte) main::i1#2 +(byte) main::i1#3 +(byte) main::i2 +(byte) main::i2#0 +(byte) main::i2#1 +(byte) main::i2#2 +(byte) main::i2#3 + +Adding number conversion cast (unumber) 2 in *((const nomodify byte*) WSYNC) ← (number) 2 +Adding number conversion cast (unumber) 2 in *((const nomodify byte*) VSYNC) ← (number) 2 +Adding number conversion cast (unumber) 2 in *((const nomodify byte*) WSYNC) ← (number) 2 +Adding number conversion cast (unumber) 2 in *((const nomodify byte*) WSYNC) ← (number) 2 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) WSYNC) ← (number) 0 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) VSYNC) ← (number) 0 +Adding number conversion cast (unumber) $25 in (bool~) main::$0 ← (byte) main::i#2 < (number) $25 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) WSYNC) ← (number) 0 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) VBLANK) ← (number) 0 +Adding number conversion cast (unumber) $c0 in (bool~) main::$1 ← (byte) main::i1#2 < (number) $c0 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) WSYNC) ← (number) 0 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) WSYNC) ← (number) 0 +Adding number conversion cast (unumber) 2 in *((const nomodify byte*) VBLANK) ← (number) 2 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) BACKGROUND_COLOR) ← (number) 0 +Adding number conversion cast (unumber) 7 in (bool~) main::$2 ← (byte) main::i2#2 < (number) 7 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) WSYNC) ← (number) 0 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast *((const nomodify byte*) WSYNC) ← (unumber)(number) 2 +Inlining cast *((const nomodify byte*) VSYNC) ← (unumber)(number) 2 +Inlining cast *((const nomodify byte*) WSYNC) ← (unumber)(number) 2 +Inlining cast *((const nomodify byte*) WSYNC) ← (unumber)(number) 2 +Inlining cast *((const nomodify byte*) WSYNC) ← (unumber)(number) 0 +Inlining cast *((const nomodify byte*) VSYNC) ← (unumber)(number) 0 +Inlining cast *((const nomodify byte*) WSYNC) ← (unumber)(number) 0 +Inlining cast *((const nomodify byte*) VBLANK) ← (unumber)(number) 0 +Inlining cast *((const nomodify byte*) WSYNC) ← (unumber)(number) 0 +Inlining cast *((const nomodify byte*) WSYNC) ← (unumber)(number) 0 +Inlining cast *((const nomodify byte*) VBLANK) ← (unumber)(number) 2 +Inlining cast *((const nomodify byte*) BACKGROUND_COLOR) ← (unumber)(number) 0 +Inlining cast *((const nomodify byte*) WSYNC) ← (unumber)(number) 0 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 0 +Simplifying constant pointer cast (byte*) 1 +Simplifying constant pointer cast (byte*) 2 +Simplifying constant pointer cast (byte*) 9 +Simplifying constant integer cast 2 +Simplifying constant integer cast 2 +Simplifying constant integer cast 2 +Simplifying constant integer cast 2 +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Simplifying constant integer cast $25 +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Simplifying constant integer cast $c0 +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Simplifying constant integer cast 2 +Simplifying constant integer cast 0 +Simplifying constant integer cast 7 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 2 +Finalized unsigned number type (byte) 2 +Finalized unsigned number type (byte) 2 +Finalized unsigned number type (byte) 2 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) $25 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) $c0 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 2 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 7 +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias col#12 = col#8 col#5 col#2 +Alias main::i#2 = main::i#3 +Alias col#13 = col#7 col#4 +Alias main::c#2 = main::c#3 +Alias main::i1#2 = main::i1#3 +Alias col#15 = col#17 col#16 +Alias main::i2#2 = main::i2#3 +Alias col#11 = col#14 +Alias col#0 = col#9 +Alias col#3 = col#6 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values (byte) col#10 (byte) col#0 +Identical Phi Values (byte) col#13 (byte) col#12 +Identical Phi Values (byte) col#15 (byte) col#1 +Identical Phi Values (byte) col#11 (byte) col#15 +Identical Phi Values (byte) col#3 (byte) col#12 +Successful SSA optimization Pass2IdenticalPhiElimination +Simple Condition (bool~) main::$3 [4] if((number) 0!=(number) 1) goto main::@2 +Simple Condition (bool~) main::$0 [14] if((byte) main::i#2<(byte) $25) goto main::@4 +Simple Condition (bool~) main::$1 [23] if((byte) main::i1#2<(byte) $c0) goto main::@7 +Simple Condition (bool~) main::$2 [34] if((byte) main::i2#2<(byte) 7) goto main::@10 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte) col#0 = 0 +Constant (const byte) main::i#0 = 0 +Constant (const byte) main::i1#0 = 0 +Constant (const byte) main::i2#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +if() condition always true - replacing block destination [4] if((number) 0!=(number) 1) goto main::@2 +Successful SSA optimization Pass2ConstantIfs +Removing unused block main::@return +Successful SSA optimization Pass2EliminateUnusedBlocks +Inlining constant with var siblings (const byte) main::i#0 +Inlining constant with var siblings (const byte) main::i1#0 +Inlining constant with var siblings (const byte) main::i2#0 +Inlining constant with var siblings (const byte) col#0 +Constant inlined main::i#0 = (byte) 0 +Constant inlined main::i1#0 = (byte) 0 +Constant inlined main::i2#0 = (byte) 0 +Constant inlined col#0 = (byte) 0 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting main::@11(between main::@9 and main::@1) +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +CALL GRAPH +Calls in [] to main:2 + +Created 5 initial phi equivalence classes +Coalesced [18] main::c#4 ← main::c#0 +Coalesced [26] col#18 ← col#1 +Coalesced [29] main::i2#4 ← main::i2#1 +Coalesced [34] main::i1#4 ← main::i1#1 +Coalesced [35] main::c#5 ← main::c#1 +Coalesced [38] main::i#4 ← main::i#1 +Coalesced down to 5 phi equivalence classes +Culled Empty Block (label) @2 +Culled Empty Block (label) main::@11 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main + +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() + +(void()) main() +main: scope:[main] from @1 + [4] phi() + to:main::@1 +main::@1: scope:[main] from main main::@9 + [5] (byte) col#12 ← phi( main/(byte) 0 main::@9/(byte) col#1 ) + to:main::@2 +main::@2: scope:[main] from main::@1 + [6] *((const nomodify byte*) WSYNC) ← (byte) 2 + [7] *((const nomodify byte*) VSYNC) ← (byte) 2 + [8] *((const nomodify byte*) WSYNC) ← (byte) 2 + [9] *((const nomodify byte*) WSYNC) ← (byte) 2 + [10] *((const nomodify byte*) WSYNC) ← (byte) 0 + [11] *((const nomodify byte*) VSYNC) ← (byte) 0 + to:main::@3 +main::@3: scope:[main] from main::@2 main::@4 + [12] (byte) main::i#2 ← phi( main::@2/(byte) 0 main::@4/(byte) main::i#1 ) + [13] if((byte) main::i#2<(byte) $25) goto main::@4 + to:main::@5 +main::@5: scope:[main] from main::@3 + [14] *((const nomodify byte*) VBLANK) ← (byte) 0 + [15] (byte) main::c#0 ← (byte) col#12 + [16] (byte) col#1 ← ++ (byte) col#12 + to:main::@6 +main::@6: scope:[main] from main::@5 main::@7 + [17] (byte) main::c#2 ← phi( main::@5/(byte) main::c#0 main::@7/(byte) main::c#1 ) + [17] (byte) main::i1#2 ← phi( main::@5/(byte) 0 main::@7/(byte) main::i1#1 ) + [18] if((byte) main::i1#2<(byte) $c0) goto main::@7 + to:main::@8 +main::@8: scope:[main] from main::@6 + [19] *((const nomodify byte*) WSYNC) ← (byte) 0 + [20] *((const nomodify byte*) VBLANK) ← (byte) 2 + [21] *((const nomodify byte*) BACKGROUND_COLOR) ← (byte) 0 + to:main::@9 +main::@9: scope:[main] from main::@10 main::@8 + [22] (byte) main::i2#2 ← phi( main::@10/(byte) main::i2#1 main::@8/(byte) 0 ) + [23] if((byte) main::i2#2<(byte) 7) goto main::@10 + to:main::@1 +main::@10: scope:[main] from main::@9 + [24] *((const nomodify byte*) WSYNC) ← (byte) 0 + [25] (byte) main::i2#1 ← ++ (byte) main::i2#2 + to:main::@9 +main::@7: scope:[main] from main::@6 + [26] *((const nomodify byte*) WSYNC) ← (byte) 0 + [27] *((const nomodify byte*) BACKGROUND_COLOR) ← (byte) main::c#2 + [28] (byte) main::c#1 ← ++ (byte) main::c#2 + [29] (byte) main::i1#1 ← ++ (byte) main::i1#2 + to:main::@6 +main::@4: scope:[main] from main::@3 + [30] *((const nomodify byte*) WSYNC) ← (byte) 0 + [31] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@3 + + +VARIABLE REGISTER WEIGHTS +(byte) col +(byte) col#1 78.71428571428571 +(byte) col#12 92.53846153846155 +(void()) main() +(byte) main::c +(byte) main::c#0 101.0 +(byte) main::c#1 1001.0 +(byte) main::c#2 776.0 +(byte) main::i +(byte) main::i#1 2002.0 +(byte) main::i#2 1001.0 +(byte) main::i1 +(byte) main::i1#1 2002.0 +(byte) main::i1#2 600.5999999999999 +(byte) main::i2 +(byte) main::i2#1 2002.0 +(byte) main::i2#2 1001.0 + +Initial phi equivalence classes +[ col#12 col#1 ] +[ main::i#2 main::i#1 ] +[ main::i1#2 main::i1#1 ] +[ main::c#2 main::c#0 main::c#1 ] +[ main::i2#2 main::i2#1 ] +Complete equivalence classes +[ col#12 col#1 ] +[ main::i#2 main::i#1 ] +[ main::i1#2 main::i1#1 ] +[ main::c#2 main::c#0 main::c#1 ] +[ main::i2#2 main::i2#1 ] +Allocated mem[1] [ col#12 col#1 ] +Allocated zp[1]:2 [ main::i#2 main::i#1 ] +Allocated zp[1]:3 [ main::i1#2 main::i1#1 ] +Allocated zp[1]:4 [ main::c#2 main::c#0 main::c#1 ] +Allocated zp[1]:5 [ main::i2#2 main::i2#1 ] + +INITIAL ASM +Target platform is atari2600 / MOS6502X + // File Comments +// Minimal Atari 2600 VCS Program + // Upstart + // Atari 2600 VCS 4K ROM +.file [name="atari2600-min.prg", type="bin", segments="Code, Vectors"] +.segmentdef Code [start=$f800,min=$f800,max=$fff9] +.segmentdef Data [start=$80,max=$ff, virtual] +.segmentdef Vectors [start=$fffa,max=$ffff] +.segment Vectors +.word main // NMI +.word main // RESET +.word main // IRQ +.segment Code + + // Global Constants & labels + .label VSYNC = 0 + .label VBLANK = 1 + .label WSYNC = 2 + .label BACKGROUND_COLOR = 9 + // @begin +__bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +__b1_from___bbegin: + jmp __b1 + // @1 +__b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from___b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: +.segment Code + // main +main: { + .label i = 2 + .label c = 4 + .label i1 = 3 + .label i2 = 5 + // [5] phi from main to main::@1 [phi:main->main::@1] + __b1_from_main: + // [5] phi (byte) col#12 = (byte) 0 [phi:main->main::@1#0] -- vbum1=vbuc1 + lda #0 + sta col + jmp __b1 + // main::@1 + __b1: + jmp __b2 + // main::@2 + __b2: + // [6] *((const nomodify byte*) WSYNC) ← (byte) 2 -- _deref_pbuc1=vbuc2 + // Vertical Sync + // here we generate the signal that tells the TV to move the beam to the top of + // the screen so we can start the next frame of video. + // The Sync Signal must be on for 3 scanlines. + lda #2 + sta WSYNC + // [7] *((const nomodify byte*) VSYNC) ← (byte) 2 -- _deref_pbuc1=vbuc2 + // Wait for SYNC (halts CPU until end of scanline) + lda #2 + sta VSYNC + // [8] *((const nomodify byte*) WSYNC) ← (byte) 2 -- _deref_pbuc1=vbuc2 + // Accumulator D1=1, turns on Vertical Sync signal + lda #2 + sta WSYNC + // [9] *((const nomodify byte*) WSYNC) ← (byte) 2 -- _deref_pbuc1=vbuc2 + // Wait for Sync - halts CPU until end of 1st scanline of VSYNC + lda #2 + sta WSYNC + // [10] *((const nomodify byte*) WSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // wait until end of 2nd scanline of VSYNC + lda #0 + sta WSYNC + // [11] *((const nomodify byte*) VSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // wait until end of 3rd scanline of VSYNC + lda #0 + sta VSYNC + // [12] phi from main::@2 to main::@3 [phi:main::@2->main::@3] + __b3_from___b2: + // [12] phi (byte) main::i#2 = (byte) 0 [phi:main::@2->main::@3#0] -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp __b3 + // Vertical Blank - game logic + // Since we don't have any yet, just delay + // main::@3 + __b3: + // [13] if((byte) main::i#2<(byte) $25) goto main::@4 -- vbuz1_lt_vbuc1_then_la1 + lda.z i + cmp #$25 + bcc __b4 + jmp __b5 + // main::@5 + __b5: + // [14] *((const nomodify byte*) VBLANK) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // Screen - display logic + // Update the registers in TIA (the video chip) in order to generate what the player sees. + // For now we're just going to output 192 colored scanlines lines so we have something to see. + lda #0 + sta VBLANK + // [15] (byte) main::c#0 ← (byte) col#12 -- vbuz1=vbum2 + // D1=1, turns off Vertical Blank signal (image output on) + lda col + sta.z c + // [16] (byte) col#1 ← ++ (byte) col#12 -- vbum1=_inc_vbum1 + inc col + // [17] phi from main::@5 to main::@6 [phi:main::@5->main::@6] + __b6_from___b5: + // [17] phi (byte) main::c#2 = (byte) main::c#0 [phi:main::@5->main::@6#0] -- register_copy + // [17] phi (byte) main::i1#2 = (byte) 0 [phi:main::@5->main::@6#1] -- vbuz1=vbuc1 + lda #0 + sta.z i1 + jmp __b6 + // main::@6 + __b6: + // [18] if((byte) main::i1#2<(byte) $c0) goto main::@7 -- vbuz1_lt_vbuc1_then_la1 + lda.z i1 + cmp #$c0 + bcc __b7 + jmp __b8 + // main::@8 + __b8: + // [19] *((const nomodify byte*) WSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // Overscan - game logic + // Since we don't have any yet, just delay + lda #0 + sta WSYNC + // [20] *((const nomodify byte*) VBLANK) ← (byte) 2 -- _deref_pbuc1=vbuc2 + // Wait for SYNC (halts CPU until end of scanline) + lda #2 + sta VBLANK + // [21] *((const nomodify byte*) BACKGROUND_COLOR) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // // D1=1 turns image output off + lda #0 + sta BACKGROUND_COLOR + // [22] phi from main::@8 to main::@9 [phi:main::@8->main::@9] + __b9_from___b8: + // [22] phi (byte) main::i2#2 = (byte) 0 [phi:main::@8->main::@9#0] -- vbuz1=vbuc1 + lda #0 + sta.z i2 + jmp __b9 + // main::@9 + __b9: + // [23] if((byte) main::i2#2<(byte) 7) goto main::@10 -- vbuz1_lt_vbuc1_then_la1 + lda.z i2 + cmp #7 + bcc __b10 + // [5] phi from main::@9 to main::@1 [phi:main::@9->main::@1] + __b1_from___b9: + // [5] phi (byte) col#12 = (byte) col#1 [phi:main::@9->main::@1#0] -- register_copy + jmp __b1 + // main::@10 + __b10: + // [24] *((const nomodify byte*) WSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta WSYNC + // [25] (byte) main::i2#1 ← ++ (byte) main::i2#2 -- vbuz1=_inc_vbuz1 + inc.z i2 + // [22] phi from main::@10 to main::@9 [phi:main::@10->main::@9] + __b9_from___b10: + // [22] phi (byte) main::i2#2 = (byte) main::i2#1 [phi:main::@10->main::@9#0] -- register_copy + jmp __b9 + // main::@7 + __b7: + // [26] *((const nomodify byte*) WSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta WSYNC + // [27] *((const nomodify byte*) BACKGROUND_COLOR) ← (byte) main::c#2 -- _deref_pbuc1=vbuz1 + // Wait for SYNC (halts CPU until end of scanline) + lda.z c + sta BACKGROUND_COLOR + // [28] (byte) main::c#1 ← ++ (byte) main::c#2 -- vbuz1=_inc_vbuz1 + inc.z c + // [29] (byte) main::i1#1 ← ++ (byte) main::i1#2 -- vbuz1=_inc_vbuz1 + inc.z i1 + // [17] phi from main::@7 to main::@6 [phi:main::@7->main::@6] + __b6_from___b7: + // [17] phi (byte) main::c#2 = (byte) main::c#1 [phi:main::@7->main::@6#0] -- register_copy + // [17] phi (byte) main::i1#2 = (byte) main::i1#1 [phi:main::@7->main::@6#1] -- register_copy + jmp __b6 + // main::@4 + __b4: + // [30] *((const nomodify byte*) WSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta WSYNC + // [31] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc.z i + // [12] phi from main::@4 to main::@3 [phi:main::@4->main::@3] + __b3_from___b4: + // [12] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@4->main::@3#0] -- register_copy + jmp __b3 +} + // File Data +.segment Data + col: .byte 0 + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [6] *((const nomodify byte*) WSYNC) ← (byte) 2 [ col#12 ] ( main:2 [ col#12 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for mem[1] [ col#12 col#1 ] +Statement [7] *((const nomodify byte*) VSYNC) ← (byte) 2 [ col#12 ] ( main:2 [ col#12 ] { } ) always clobbers reg byte a +Statement [8] *((const nomodify byte*) WSYNC) ← (byte) 2 [ col#12 ] ( main:2 [ col#12 ] { } ) always clobbers reg byte a +Statement [9] *((const nomodify byte*) WSYNC) ← (byte) 2 [ col#12 ] ( main:2 [ col#12 ] { } ) always clobbers reg byte a +Statement [10] *((const nomodify byte*) WSYNC) ← (byte) 0 [ col#12 ] ( main:2 [ col#12 ] { } ) always clobbers reg byte a +Statement [11] *((const nomodify byte*) VSYNC) ← (byte) 0 [ col#12 ] ( main:2 [ col#12 ] { } ) always clobbers reg byte a +Statement [14] *((const nomodify byte*) VBLANK) ← (byte) 0 [ col#12 ] ( main:2 [ col#12 ] { } ) always clobbers reg byte a +Statement [19] *((const nomodify byte*) WSYNC) ← (byte) 0 [ col#1 ] ( main:2 [ col#1 ] { } ) always clobbers reg byte a +Statement [20] *((const nomodify byte*) VBLANK) ← (byte) 2 [ col#1 ] ( main:2 [ col#1 ] { } ) always clobbers reg byte a +Statement [21] *((const nomodify byte*) BACKGROUND_COLOR) ← (byte) 0 [ col#1 ] ( main:2 [ col#1 ] { } ) always clobbers reg byte a +Statement [24] *((const nomodify byte*) WSYNC) ← (byte) 0 [ col#1 main::i2#2 ] ( main:2 [ col#1 main::i2#2 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:5 [ main::i2#2 main::i2#1 ] +Statement [26] *((const nomodify byte*) WSYNC) ← (byte) 0 [ col#1 main::i1#2 main::c#2 ] ( main:2 [ col#1 main::i1#2 main::c#2 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:3 [ main::i1#2 main::i1#1 ] +Removing always clobbered register reg byte a as potential for zp[1]:4 [ main::c#2 main::c#0 main::c#1 ] +Statement [30] *((const nomodify byte*) WSYNC) ← (byte) 0 [ col#12 main::i#2 ] ( main:2 [ col#12 main::i#2 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:2 [ main::i#2 main::i#1 ] +Statement [6] *((const nomodify byte*) WSYNC) ← (byte) 2 [ col#12 ] ( main:2 [ col#12 ] { } ) always clobbers reg byte a +Statement [7] *((const nomodify byte*) VSYNC) ← (byte) 2 [ col#12 ] ( main:2 [ col#12 ] { } ) always clobbers reg byte a +Statement [8] *((const nomodify byte*) WSYNC) ← (byte) 2 [ col#12 ] ( main:2 [ col#12 ] { } ) always clobbers reg byte a +Statement [9] *((const nomodify byte*) WSYNC) ← (byte) 2 [ col#12 ] ( main:2 [ col#12 ] { } ) always clobbers reg byte a +Statement [10] *((const nomodify byte*) WSYNC) ← (byte) 0 [ col#12 ] ( main:2 [ col#12 ] { } ) always clobbers reg byte a +Statement [11] *((const nomodify byte*) VSYNC) ← (byte) 0 [ col#12 ] ( main:2 [ col#12 ] { } ) always clobbers reg byte a +Statement [14] *((const nomodify byte*) VBLANK) ← (byte) 0 [ col#12 ] ( main:2 [ col#12 ] { } ) always clobbers reg byte a +Statement [19] *((const nomodify byte*) WSYNC) ← (byte) 0 [ col#1 ] ( main:2 [ col#1 ] { } ) always clobbers reg byte a +Statement [20] *((const nomodify byte*) VBLANK) ← (byte) 2 [ col#1 ] ( main:2 [ col#1 ] { } ) always clobbers reg byte a +Statement [21] *((const nomodify byte*) BACKGROUND_COLOR) ← (byte) 0 [ col#1 ] ( main:2 [ col#1 ] { } ) always clobbers reg byte a +Statement [24] *((const nomodify byte*) WSYNC) ← (byte) 0 [ col#1 main::i2#2 ] ( main:2 [ col#1 main::i2#2 ] { } ) always clobbers reg byte a +Statement [26] *((const nomodify byte*) WSYNC) ← (byte) 0 [ col#1 main::i1#2 main::c#2 ] ( main:2 [ col#1 main::i1#2 main::c#2 ] { } ) always clobbers reg byte a +Statement [30] *((const nomodify byte*) WSYNC) ← (byte) 0 [ col#12 main::i#2 ] ( main:2 [ col#12 main::i#2 ] { } ) always clobbers reg byte a +Potential registers mem[1] [ col#12 col#1 ] : mem[1] , reg byte x , reg byte y , +Potential registers zp[1]:2 [ main::i#2 main::i#1 ] : zp[1]:2 , reg byte x , reg byte y , +Potential registers zp[1]:3 [ main::i1#2 main::i1#1 ] : zp[1]:3 , reg byte x , reg byte y , +Potential registers zp[1]:4 [ main::c#2 main::c#0 main::c#1 ] : zp[1]:4 , reg byte x , reg byte y , +Potential registers zp[1]:5 [ main::i2#2 main::i2#1 ] : zp[1]:5 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 3,003: zp[1]:2 [ main::i#2 main::i#1 ] 3,003: zp[1]:5 [ main::i2#2 main::i2#1 ] 2,602.6: zp[1]:3 [ main::i1#2 main::i1#1 ] 1,878: zp[1]:4 [ main::c#2 main::c#0 main::c#1 ] +Uplift Scope [] 171.25: mem[1] [ col#12 col#1 ] + +Uplifting [main] best 8212 combination reg byte x [ main::i#2 main::i#1 ] reg byte x [ main::i2#2 main::i2#1 ] reg byte y [ main::i1#2 main::i1#1 ] reg byte x [ main::c#2 main::c#0 main::c#1 ] +Uplifting [] best 8212 combination mem[1] [ col#12 col#1 ] +Attempting to uplift remaining variables inmem[1] [ col#12 col#1 ] +Uplifting [] best 8212 combination mem[1] [ col#12 col#1 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Minimal Atari 2600 VCS Program + // Upstart + // Atari 2600 VCS 4K ROM +.file [name="atari2600-min.prg", type="bin", segments="Code, Vectors"] +.segmentdef Code [start=$f800,min=$f800,max=$fff9] +.segmentdef Data [start=$80,max=$ff, virtual] +.segmentdef Vectors [start=$fffa,max=$ffff] +.segment Vectors +.word main // NMI +.word main // RESET +.word main // IRQ +.segment Code + + // Global Constants & labels + .label VSYNC = 0 + .label VBLANK = 1 + .label WSYNC = 2 + .label BACKGROUND_COLOR = 9 + // @begin +__bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +__b1_from___bbegin: + jmp __b1 + // @1 +__b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from___b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: +.segment Code + // main +main: { + // [5] phi from main to main::@1 [phi:main->main::@1] + __b1_from_main: + // [5] phi (byte) col#12 = (byte) 0 [phi:main->main::@1#0] -- vbum1=vbuc1 + lda #0 + sta col + jmp __b1 + // main::@1 + __b1: + jmp __b2 + // main::@2 + __b2: + // [6] *((const nomodify byte*) WSYNC) ← (byte) 2 -- _deref_pbuc1=vbuc2 + // Vertical Sync + // here we generate the signal that tells the TV to move the beam to the top of + // the screen so we can start the next frame of video. + // The Sync Signal must be on for 3 scanlines. + lda #2 + sta WSYNC + // [7] *((const nomodify byte*) VSYNC) ← (byte) 2 -- _deref_pbuc1=vbuc2 + // Wait for SYNC (halts CPU until end of scanline) + lda #2 + sta VSYNC + // [8] *((const nomodify byte*) WSYNC) ← (byte) 2 -- _deref_pbuc1=vbuc2 + // Accumulator D1=1, turns on Vertical Sync signal + lda #2 + sta WSYNC + // [9] *((const nomodify byte*) WSYNC) ← (byte) 2 -- _deref_pbuc1=vbuc2 + // Wait for Sync - halts CPU until end of 1st scanline of VSYNC + lda #2 + sta WSYNC + // [10] *((const nomodify byte*) WSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // wait until end of 2nd scanline of VSYNC + lda #0 + sta WSYNC + // [11] *((const nomodify byte*) VSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // wait until end of 3rd scanline of VSYNC + lda #0 + sta VSYNC + // [12] phi from main::@2 to main::@3 [phi:main::@2->main::@3] + __b3_from___b2: + // [12] phi (byte) main::i#2 = (byte) 0 [phi:main::@2->main::@3#0] -- vbuxx=vbuc1 + ldx #0 + jmp __b3 + // Vertical Blank - game logic + // Since we don't have any yet, just delay + // main::@3 + __b3: + // [13] if((byte) main::i#2<(byte) $25) goto main::@4 -- vbuxx_lt_vbuc1_then_la1 + cpx #$25 + bcc __b4 + jmp __b5 + // main::@5 + __b5: + // [14] *((const nomodify byte*) VBLANK) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // Screen - display logic + // Update the registers in TIA (the video chip) in order to generate what the player sees. + // For now we're just going to output 192 colored scanlines lines so we have something to see. + lda #0 + sta VBLANK + // [15] (byte) main::c#0 ← (byte) col#12 -- vbuxx=vbum1 + // D1=1, turns off Vertical Blank signal (image output on) + ldx col + // [16] (byte) col#1 ← ++ (byte) col#12 -- vbum1=_inc_vbum1 + inc col + // [17] phi from main::@5 to main::@6 [phi:main::@5->main::@6] + __b6_from___b5: + // [17] phi (byte) main::c#2 = (byte) main::c#0 [phi:main::@5->main::@6#0] -- register_copy + // [17] phi (byte) main::i1#2 = (byte) 0 [phi:main::@5->main::@6#1] -- vbuyy=vbuc1 + ldy #0 + jmp __b6 + // main::@6 + __b6: + // [18] if((byte) main::i1#2<(byte) $c0) goto main::@7 -- vbuyy_lt_vbuc1_then_la1 + cpy #$c0 + bcc __b7 + jmp __b8 + // main::@8 + __b8: + // [19] *((const nomodify byte*) WSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // Overscan - game logic + // Since we don't have any yet, just delay + lda #0 + sta WSYNC + // [20] *((const nomodify byte*) VBLANK) ← (byte) 2 -- _deref_pbuc1=vbuc2 + // Wait for SYNC (halts CPU until end of scanline) + lda #2 + sta VBLANK + // [21] *((const nomodify byte*) BACKGROUND_COLOR) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // // D1=1 turns image output off + lda #0 + sta BACKGROUND_COLOR + // [22] phi from main::@8 to main::@9 [phi:main::@8->main::@9] + __b9_from___b8: + // [22] phi (byte) main::i2#2 = (byte) 0 [phi:main::@8->main::@9#0] -- vbuxx=vbuc1 + ldx #0 + jmp __b9 + // main::@9 + __b9: + // [23] if((byte) main::i2#2<(byte) 7) goto main::@10 -- vbuxx_lt_vbuc1_then_la1 + cpx #7 + bcc __b10 + // [5] phi from main::@9 to main::@1 [phi:main::@9->main::@1] + __b1_from___b9: + // [5] phi (byte) col#12 = (byte) col#1 [phi:main::@9->main::@1#0] -- register_copy + jmp __b1 + // main::@10 + __b10: + // [24] *((const nomodify byte*) WSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta WSYNC + // [25] (byte) main::i2#1 ← ++ (byte) main::i2#2 -- vbuxx=_inc_vbuxx + inx + // [22] phi from main::@10 to main::@9 [phi:main::@10->main::@9] + __b9_from___b10: + // [22] phi (byte) main::i2#2 = (byte) main::i2#1 [phi:main::@10->main::@9#0] -- register_copy + jmp __b9 + // main::@7 + __b7: + // [26] *((const nomodify byte*) WSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta WSYNC + // [27] *((const nomodify byte*) BACKGROUND_COLOR) ← (byte) main::c#2 -- _deref_pbuc1=vbuxx + // Wait for SYNC (halts CPU until end of scanline) + stx BACKGROUND_COLOR + // [28] (byte) main::c#1 ← ++ (byte) main::c#2 -- vbuxx=_inc_vbuxx + inx + // [29] (byte) main::i1#1 ← ++ (byte) main::i1#2 -- vbuyy=_inc_vbuyy + iny + // [17] phi from main::@7 to main::@6 [phi:main::@7->main::@6] + __b6_from___b7: + // [17] phi (byte) main::c#2 = (byte) main::c#1 [phi:main::@7->main::@6#0] -- register_copy + // [17] phi (byte) main::i1#2 = (byte) main::i1#1 [phi:main::@7->main::@6#1] -- register_copy + jmp __b6 + // main::@4 + __b4: + // [30] *((const nomodify byte*) WSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta WSYNC + // [31] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + // [12] phi from main::@4 to main::@3 [phi:main::@4->main::@3] + __b3_from___b4: + // [12] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@4->main::@3#0] -- register_copy + jmp __b3 +} + // File Data +.segment Data + col: .byte 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __bend +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Removing instruction jmp __b3 +Removing instruction jmp __b5 +Removing instruction jmp __b6 +Removing instruction jmp __b8 +Removing instruction jmp __b9 +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction lda #2 +Removing instruction lda #2 +Removing instruction lda #2 +Removing instruction lda #0 +Succesful ASM optimization Pass5UnnecesaryLoadElimination +Replacing label __b1 with __b2 +Removing instruction __b1_from___bbegin: +Removing instruction __b1: +Removing instruction main_from___b1: +Removing instruction __bend_from___b1: +Removing instruction __b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __bbegin: +Removing instruction __bend: +Removing instruction __b1_from_main: +Removing instruction __b3_from___b2: +Removing instruction __b5: +Removing instruction __b6_from___b5: +Removing instruction __b8: +Removing instruction __b9_from___b8: +Removing instruction __b1_from___b9: +Removing instruction __b9_from___b10: +Removing instruction __b6_from___b7: +Removing instruction __b3_from___b4: +Succesful ASM optimization Pass5UnusedLabelElimination +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Replacing instruction ldx #0 with TAX +Replacing instruction ldy #0 with TAY +Replacing instruction ldx #0 with TAX + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(const nomodify byte*) BACKGROUND_COLOR = (byte*) 9 +(const nomodify byte*) VBLANK = (byte*) 1 +(const nomodify byte*) VSYNC = (byte*) 0 +(const nomodify byte*) WSYNC = (byte*) 2 +(byte) col +(byte) col#1 col mem[1] 78.71428571428571 +(byte) col#12 col mem[1] 92.53846153846155 +(void()) main() +(label) main::@1 +(label) main::@10 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@5 +(label) main::@6 +(label) main::@7 +(label) main::@8 +(label) main::@9 +(byte) main::c +(byte) main::c#0 reg byte x 101.0 +(byte) main::c#1 reg byte x 1001.0 +(byte) main::c#2 reg byte x 776.0 +(byte) main::i +(byte) main::i#1 reg byte x 2002.0 +(byte) main::i#2 reg byte x 1001.0 +(byte) main::i1 +(byte) main::i1#1 reg byte y 2002.0 +(byte) main::i1#2 reg byte y 600.5999999999999 +(byte) main::i2 +(byte) main::i2#1 reg byte x 2002.0 +(byte) main::i2#2 reg byte x 1001.0 + +mem[1] [ col#12 col#1 ] +reg byte x [ main::i#2 main::i#1 ] +reg byte y [ main::i1#2 main::i1#1 ] +reg byte x [ main::c#2 main::c#0 main::c#1 ] +reg byte x [ main::i2#2 main::i2#1 ] + + +FINAL ASSEMBLER +Score: 6560 + + // File Comments +// Minimal Atari 2600 VCS Program + // Upstart + // Atari 2600 VCS 4K ROM +.file [name="atari2600-min.prg", type="bin", segments="Code, Vectors"] +.segmentdef Code [start=$f800,min=$f800,max=$fff9] +.segmentdef Data [start=$80,max=$ff, virtual] +.segmentdef Vectors [start=$fffa,max=$ffff] +.segment Vectors +.word main // NMI +.word main // RESET +.word main // IRQ +.segment Code + + // Global Constants & labels + .label VSYNC = 0 + .label VBLANK = 1 + .label WSYNC = 2 + .label BACKGROUND_COLOR = 9 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [4] phi from @1 to main [phi:@1->main] + // [3] phi from @1 to @end [phi:@1->@end] + // @end +.segment Code + // main +main: { + // [5] phi from main to main::@1 [phi:main->main::@1] + // [5] phi (byte) col#12 = (byte) 0 [phi:main->main::@1#0] -- vbum1=vbuc1 + lda #0 + sta col + // main::@1 + // main::@2 + __b2: + // *WSYNC = 2 + // [6] *((const nomodify byte*) WSYNC) ← (byte) 2 -- _deref_pbuc1=vbuc2 + // Vertical Sync + // here we generate the signal that tells the TV to move the beam to the top of + // the screen so we can start the next frame of video. + // The Sync Signal must be on for 3 scanlines. + lda #2 + sta WSYNC + // *VSYNC = 2 + // [7] *((const nomodify byte*) VSYNC) ← (byte) 2 -- _deref_pbuc1=vbuc2 + // Wait for SYNC (halts CPU until end of scanline) + sta VSYNC + // *WSYNC = 2 + // [8] *((const nomodify byte*) WSYNC) ← (byte) 2 -- _deref_pbuc1=vbuc2 + // Accumulator D1=1, turns on Vertical Sync signal + sta WSYNC + // [9] *((const nomodify byte*) WSYNC) ← (byte) 2 -- _deref_pbuc1=vbuc2 + // Wait for Sync - halts CPU until end of 1st scanline of VSYNC + sta WSYNC + // *WSYNC = 0 + // [10] *((const nomodify byte*) WSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // wait until end of 2nd scanline of VSYNC + lda #0 + sta WSYNC + // *VSYNC = 0 + // [11] *((const nomodify byte*) VSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // wait until end of 3rd scanline of VSYNC + sta VSYNC + // [12] phi from main::@2 to main::@3 [phi:main::@2->main::@3] + // [12] phi (byte) main::i#2 = (byte) 0 [phi:main::@2->main::@3#0] -- vbuxx=vbuc1 + tax + // Vertical Blank - game logic + // Since we don't have any yet, just delay + // main::@3 + __b3: + // for(char i=0;i<37;i++) + // [13] if((byte) main::i#2<(byte) $25) goto main::@4 -- vbuxx_lt_vbuc1_then_la1 + cpx #$25 + bcc __b4 + // main::@5 + // *VBLANK = 0 + // [14] *((const nomodify byte*) VBLANK) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // Screen - display logic + // Update the registers in TIA (the video chip) in order to generate what the player sees. + // For now we're just going to output 192 colored scanlines lines so we have something to see. + lda #0 + sta VBLANK + // c = col++ + // [15] (byte) main::c#0 ← (byte) col#12 -- vbuxx=vbum1 + // D1=1, turns off Vertical Blank signal (image output on) + ldx col + // [16] (byte) col#1 ← ++ (byte) col#12 -- vbum1=_inc_vbum1 + inc col + // [17] phi from main::@5 to main::@6 [phi:main::@5->main::@6] + // [17] phi (byte) main::c#2 = (byte) main::c#0 [phi:main::@5->main::@6#0] -- register_copy + // [17] phi (byte) main::i1#2 = (byte) 0 [phi:main::@5->main::@6#1] -- vbuyy=vbuc1 + tay + // main::@6 + __b6: + // for(char i=0;i<192;i++) + // [18] if((byte) main::i1#2<(byte) $c0) goto main::@7 -- vbuyy_lt_vbuc1_then_la1 + cpy #$c0 + bcc __b7 + // main::@8 + // *WSYNC = 0 + // [19] *((const nomodify byte*) WSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // Overscan - game logic + // Since we don't have any yet, just delay + lda #0 + sta WSYNC + // *VBLANK = 2 + // [20] *((const nomodify byte*) VBLANK) ← (byte) 2 -- _deref_pbuc1=vbuc2 + // Wait for SYNC (halts CPU until end of scanline) + lda #2 + sta VBLANK + // *BACKGROUND_COLOR = 0 + // [21] *((const nomodify byte*) BACKGROUND_COLOR) ← (byte) 0 -- _deref_pbuc1=vbuc2 + // // D1=1 turns image output off + lda #0 + sta BACKGROUND_COLOR + // [22] phi from main::@8 to main::@9 [phi:main::@8->main::@9] + // [22] phi (byte) main::i2#2 = (byte) 0 [phi:main::@8->main::@9#0] -- vbuxx=vbuc1 + tax + // main::@9 + __b9: + // for(char i=0;i<07;i++) + // [23] if((byte) main::i2#2<(byte) 7) goto main::@10 -- vbuxx_lt_vbuc1_then_la1 + cpx #7 + bcc __b10 + // [5] phi from main::@9 to main::@1 [phi:main::@9->main::@1] + // [5] phi (byte) col#12 = (byte) col#1 [phi:main::@9->main::@1#0] -- register_copy + jmp __b2 + // main::@10 + __b10: + // *WSYNC = 0 + // [24] *((const nomodify byte*) WSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta WSYNC + // for(char i=0;i<07;i++) + // [25] (byte) main::i2#1 ← ++ (byte) main::i2#2 -- vbuxx=_inc_vbuxx + inx + // [22] phi from main::@10 to main::@9 [phi:main::@10->main::@9] + // [22] phi (byte) main::i2#2 = (byte) main::i2#1 [phi:main::@10->main::@9#0] -- register_copy + jmp __b9 + // main::@7 + __b7: + // *WSYNC = 0 + // [26] *((const nomodify byte*) WSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta WSYNC + // *BACKGROUND_COLOR = c++ + // [27] *((const nomodify byte*) BACKGROUND_COLOR) ← (byte) main::c#2 -- _deref_pbuc1=vbuxx + // Wait for SYNC (halts CPU until end of scanline) + stx BACKGROUND_COLOR + // *BACKGROUND_COLOR = c++; + // [28] (byte) main::c#1 ← ++ (byte) main::c#2 -- vbuxx=_inc_vbuxx + inx + // for(char i=0;i<192;i++) + // [29] (byte) main::i1#1 ← ++ (byte) main::i1#2 -- vbuyy=_inc_vbuyy + iny + // [17] phi from main::@7 to main::@6 [phi:main::@7->main::@6] + // [17] phi (byte) main::c#2 = (byte) main::c#1 [phi:main::@7->main::@6#0] -- register_copy + // [17] phi (byte) main::i1#2 = (byte) main::i1#1 [phi:main::@7->main::@6#1] -- register_copy + jmp __b6 + // main::@4 + __b4: + // *WSYNC = 0 + // [30] *((const nomodify byte*) WSYNC) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta WSYNC + // for(char i=0;i<37;i++) + // [31] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + // [12] phi from main::@4 to main::@3 [phi:main::@4->main::@3] + // [12] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@4->main::@3#0] -- register_copy + jmp __b3 +} + // File Data +.segment Data + col: .byte 0 + diff --git a/src/test/ref/atari2600-min.sym b/src/test/ref/atari2600-min.sym new file mode 100644 index 000000000..cd40de760 --- /dev/null +++ b/src/test/ref/atari2600-min.sym @@ -0,0 +1,40 @@ +(label) @1 +(label) @begin +(label) @end +(const nomodify byte*) BACKGROUND_COLOR = (byte*) 9 +(const nomodify byte*) VBLANK = (byte*) 1 +(const nomodify byte*) VSYNC = (byte*) 0 +(const nomodify byte*) WSYNC = (byte*) 2 +(byte) col +(byte) col#1 col mem[1] 78.71428571428571 +(byte) col#12 col mem[1] 92.53846153846155 +(void()) main() +(label) main::@1 +(label) main::@10 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@5 +(label) main::@6 +(label) main::@7 +(label) main::@8 +(label) main::@9 +(byte) main::c +(byte) main::c#0 reg byte x 101.0 +(byte) main::c#1 reg byte x 1001.0 +(byte) main::c#2 reg byte x 776.0 +(byte) main::i +(byte) main::i#1 reg byte x 2002.0 +(byte) main::i#2 reg byte x 1001.0 +(byte) main::i1 +(byte) main::i1#1 reg byte y 2002.0 +(byte) main::i1#2 reg byte y 600.5999999999999 +(byte) main::i2 +(byte) main::i2#1 reg byte x 2002.0 +(byte) main::i2#2 reg byte x 1001.0 + +mem[1] [ col#12 col#1 ] +reg byte x [ main::i#2 main::i#1 ] +reg byte y [ main::i1#2 main::i1#1 ] +reg byte x [ main::c#2 main::c#0 main::c#1 ] +reg byte x [ main::i2#2 main::i2#1 ]