diff --git a/src/main/fragment/pbuz1_derefidx__deref_pbuz2=_inc_pbuz1_derefidx__deref_pbuz2.asm b/src/main/fragment/pbuz1_derefidx__deref_pbuz2=_inc_pbuz1_derefidx__deref_pbuz2.asm new file mode 100644 index 000000000..fb2587274 --- /dev/null +++ b/src/main/fragment/pbuz1_derefidx__deref_pbuz2=_inc_pbuz1_derefidx__deref_pbuz2.asm @@ -0,0 +1,7 @@ +ldy #0 +lda ({z2}),y +tay +lda ({z1}),y +clc +adc #1 +sta ({z1}),y \ No newline at end of file diff --git a/src/main/fragment/pwuz1_derefidx_vbuyy=_inc_pwuz1_derefidx_vbuyy.asm b/src/main/fragment/pwuz1_derefidx_vbuyy=_inc_pwuz1_derefidx_vbuyy.asm new file mode 100644 index 000000000..12d14c3fa --- /dev/null +++ b/src/main/fragment/pwuz1_derefidx_vbuyy=_inc_pwuz1_derefidx_vbuyy.asm @@ -0,0 +1,10 @@ +lda ({z1}),y +clc +adc #1 +sta ({z1}),y +bne !+ +iny +lda ({z1}),y +adc #0 +sta ({z1}),y +!: \ No newline at end of file diff --git a/src/main/fragment/pwuz1_derefidx_vbuyy=vwuc1.asm b/src/main/fragment/pwuz1_derefidx_vbuyy=vwuc1.asm new file mode 100644 index 000000000..1fca3f46d --- /dev/null +++ b/src/main/fragment/pwuz1_derefidx_vbuyy=vwuc1.asm @@ -0,0 +1,5 @@ +lda #<{c1} +sta ({z1}),y +iny +lda #>{c1} +sta ({z1}),y diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index c87706025..592952848 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -242,6 +242,7 @@ public class Compiler { private void pass2AssertSSA() { List<Pass2SsaAssertion> assertions = new ArrayList<>(); + //assertions.add(new Pass2AssertNoLValueObjectEquality(program)); assertions.add(new Pass2AssertTypeMatch(program)); assertions.add(new Pass2AssertSymbols(program)); assertions.add(new Pass2AssertBlocks(program)); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index 7f7a2225f..3791e55a1 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -1429,7 +1429,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> { return lValue; } - private LValue copyLValue(LValue lValue) { + private static LValue copyLValue(LValue lValue) { if(lValue instanceof VariableRef) { return new VariableRef(((VariableRef) lValue).getFullName()); } else if(lValue instanceof LvalueIntermediate) { @@ -1929,7 +1929,8 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> { List<PrePostModifier> modifiers, StatementSource source) { for(PrePostModifier mod : modifiers) { - Statement stmt = new StatementAssignment((LValue) mod.child, mod.operator, mod.child, source, Comment.NO_COMMENTS); + + Statement stmt = new StatementAssignment((LValue) mod.child, mod.operator, copyLValue((LValue) mod.child), source, Comment.NO_COMMENTS); parser.sequence.addStatement(stmt); if(parser.program.getLog().isVerboseParse()) { parser.program.getLog().append("Adding pre/post-modifier " + stmt.toString(parser.program, false)); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoLValueObjectEquality.java b/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoLValueObjectEquality.java new file mode 100644 index 000000000..6531a5053 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoLValueObjectEquality.java @@ -0,0 +1,47 @@ +package dk.camelot64.kickc.passes; + +import dk.camelot64.kickc.model.InternalError; +import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.iterator.ProgramValue; +import dk.camelot64.kickc.model.iterator.ProgramValueIterator; +import dk.camelot64.kickc.model.values.ConstantValue; +import dk.camelot64.kickc.model.values.SymbolRef; +import dk.camelot64.kickc.model.values.Value; + +import java.util.HashMap; + +/** Asserts that the program does not contain calls with lValues */ +public class Pass2AssertNoLValueObjectEquality extends Pass2SsaAssertion { + + public Pass2AssertNoLValueObjectEquality(Program program) { + super(program); + } + + @Override + public void check() throws AssertionFailed { + HashMap<Value, ValueInProgram> allValues = new HashMap<>(); + ProgramValueIterator.execute(getGraph(), (programValue, currentStmt, stmtIt, currentBlock) -> { + Value value = programValue.get(); + if(value!=null && !(value instanceof SymbolRef) && !(value instanceof ConstantValue)) { + ValueInProgram other = allValues.get(value); + if(other != null) { + if(other.value == value) + throw new InternalError("Error! Value object equality in program " + value.toString(getProgram()), currentStmt.getSource()); + } else { + allValues.put(value, new ValueInProgram(value, programValue)); + } + } + }); + } + + private static class ValueInProgram { + Value value; + ProgramValue programValue; + + public ValueInProgram(Value value, ProgramValue programValue) { + this.value = value; + this.programValue = programValue; + } + } + +} diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index f935e9183..cf5354e37 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -46,6 +46,11 @@ public class TestPrograms { compileAndCompare("plasma-center"); } + @Test + public void testScreenShowSpiralBuckets() throws IOException, URISyntaxException { + compileAndCompare("screen-show-spiral-buckets", log()); + } + @Test public void testScreenShowSpiral() throws IOException, URISyntaxException { compileAndCompare("screen-show-spiral"); diff --git a/src/test/kc/screen-show-spiral-buckets.kc b/src/test/kc/screen-show-spiral-buckets.kc new file mode 100644 index 000000000..5ef52c628 --- /dev/null +++ b/src/test/kc/screen-show-spiral-buckets.kc @@ -0,0 +1,117 @@ +// Fill screen using a spiral based on distance-to-center / angle-to-center +// Utilizes a bucket sort for identifying the minimum angle/distance + +import "stdlib" +import "sqr" +import "atan2" + + +// Screen containing distance to center +const byte* SCREEN_DIST = malloc(1000); +// Screen containing angle to center +const byte* SCREEN_ANGLE = malloc(1000); +// Screen containing angle to center +const byte* SCREEN_FILL = 0x0400; + +// Char to fill with +const byte FILL_CHAR = '@'; + +void main() { + init_dist_screen(SCREEN_DIST); + init_angle_screen(SCREEN_ANGLE); + init_buckets(); +/* + while(true) { + // Find the minimum dist/angle that is not already filled + byte* dist = SCREEN_DIST; + byte* angle = SCREEN_ANGLE; + byte* fill = SCREEN_FILL; + word min_dist_angle = 0xffff; + byte* min_fill = SCREEN_FILL; + do { + if(*fill!=FILL_CHAR) { + word dist_angle = { *dist, *angle }; + if(dist_angle<min_dist_angle) { + min_fill = fill; + min_dist_angle = dist_angle; + } + } + dist++; + angle++; + fill++; + } while (fill<SCREEN_FILL+1000); + // Break if not found (means we are done) + if(min_dist_angle==0xffff) + break; + // Fill the found location + *min_fill = FILL_CHAR; + } + */ +} + +// Array containing the bucket size for each of the 256 buckets +const word* BUCKET_SIZES = malloc(0x80*sizeof(word)); + +void init_buckets() { + // Init bucket sizes to 0 + for(byte i:0..0x7f) BUCKET_SIZES[i]=0; + // first find bucket sizes - by counting number of chars with each distance value + byte* dist = SCREEN_DIST; + for( word i:0..999 ) { + BUCKET_SIZES[*dist]++; + dist++; + } +} + + +// Populates 1000 bytes (a screen) with values representing the angle to the center. +// Utilizes symmetry around the center +void init_angle_screen(byte* screen) { +/* + byte* screen_topline = screen+40*12; + byte *screen_bottomline = screen+40*12; + for(byte y: 0..12) { + for( byte x=0,xb=39; x<=19; x++, xb--) { + signed word xw = (signed word)(word){ 39-x*2, 0 }; + signed word yw = (signed word)(word){ y*2, 0 }; + word angle_w = atan2_16(xw, yw); + byte ang_w = >(angle_w+0x0080); + screen_bottomline[xb] = ang_w; + screen_topline[xb] = -ang_w; + screen_topline[x] = 0x80+ang_w; + screen_bottomline[x] = 0x80-ang_w; + } + screen_topline -= 40; + screen_bottomline += 40; + } + */ +} + +// Populates 1000 bytes (a screen) with values representing the distance to the center. +// The actual value stored is distance*2 to increase precision +void init_dist_screen(byte* screen) { +/* + NUM_SQUARES = 0x30; + init_squares(); + byte* screen_topline = screen; + byte *screen_bottomline = screen+40*24; + for(byte y: 0..12) { + byte y2 = y*2; + byte yd = (y2>=24)?(y2-24):(24-y2); + word yds = sqr(yd); + for( byte x=0,xb=39; x<=19; x++, xb--) { + byte x2 = x*2; + byte xd = (x2>=39)?(x2-39):(39-x2); + word xds = sqr(xd); + word ds = xds+yds; + byte d = sqrt(ds); + screen_topline[x] = d; + screen_bottomline[x] = d; + screen_topline[xb] = d; + screen_bottomline[xb] = d; + } + screen_topline += 40; + screen_bottomline -= 40; + } + */ +} \ No newline at end of file diff --git a/src/test/ref/screen-show-spiral-buckets.asm b/src/test/ref/screen-show-spiral-buckets.asm new file mode 100644 index 000000000..a09b4a2dd --- /dev/null +++ b/src/test/ref/screen-show-spiral-buckets.asm @@ -0,0 +1,124 @@ +// Fill screen using a spiral based on distance-to-center / angle-to-center +// Utilizes a bucket sort for identifying the minimum angle/distance +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + .const SIZEOF_WORD = 2 + // Start of the heap used by malloc() + .label HEAP_START = $c000 + .label heap_head = 6 + // Screen containing distance to center + .label SCREEN_DIST = 2 + // Array containing the bucket size for each of the 256 buckets + .label BUCKET_SIZES = $a +bbegin: + lda #<$3e8 + sta malloc.size + lda #>$3e8 + sta malloc.size+1 + lda #<HEAP_START + sta heap_head + lda #>HEAP_START + sta heap_head+1 + jsr malloc + lda malloc.mem + sta SCREEN_DIST + lda malloc.mem+1 + sta SCREEN_DIST+1 + lda #<$3e8 + sta malloc.size + lda #>$3e8 + sta malloc.size+1 + jsr malloc + lda #$80*SIZEOF_WORD + sta malloc.size + lda #0 + sta malloc.size+1 + jsr malloc + jsr main + rts +main: { + jsr init_dist_screen + jsr init_angle_screen + jsr init_buckets + rts +} +init_buckets: { + .label dist = 2 + .label i1 = 4 + ldx #0 + // Init bucket sizes to 0 + b1: + txa + asl + tay + lda #<0 + sta (BUCKET_SIZES),y + iny + sta (BUCKET_SIZES),y + inx + cpx #$80 + bne b1 + // first find bucket sizes - by counting number of chars with each distance value + sta i1 + sta i1+1 + b3: + ldy #0 + lda (dist),y + asl + tay + lda (BUCKET_SIZES),y + clc + adc #1 + sta (BUCKET_SIZES),y + bne !+ + iny + lda (BUCKET_SIZES),y + adc #0 + sta (BUCKET_SIZES),y + !: + inc dist + bne !+ + inc dist+1 + !: + inc i1 + bne !+ + inc i1+1 + !: + lda i1+1 + cmp #>$3e8 + bne b3 + lda i1 + cmp #<$3e8 + bne b3 + rts +} +// Populates 1000 bytes (a screen) with values representing the angle to the center. +// Utilizes symmetry around the center +init_angle_screen: { + rts +} +// Populates 1000 bytes (a screen) with values representing the distance to the center. +// The actual value stored is distance*2 to increase precision +init_dist_screen: { + rts +} +// Allocates a block of size bytes of memory, returning a pointer to the beginning of the block. +// The content of the newly allocated block of memory is not initialized, remaining with indeterminate values. +// malloc(word zeropage(8) size) +malloc: { + .label mem = $a + .label size = 8 + lda heap_head + sta mem + lda heap_head+1 + sta mem+1 + lda heap_head + clc + adc size + sta heap_head + lda heap_head+1 + adc size+1 + sta heap_head+1 + rts +} diff --git a/src/test/ref/screen-show-spiral-buckets.cfg b/src/test/ref/screen-show-spiral-buckets.cfg new file mode 100644 index 000000000..47961c2ca --- /dev/null +++ b/src/test/ref/screen-show-spiral-buckets.cfg @@ -0,0 +1,85 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call malloc + to:@4 +@4: scope:[] from @1 + [3] (void*) SCREEN_DIST#0 ← (void*)(byte*) malloc::mem#0 + [4] call malloc + to:@2 +@2: scope:[] from @4 + [5] phi() + [6] call malloc + to:@5 +@5: scope:[] from @2 + [7] (void*) BUCKET_SIZES#0 ← (void*)(byte*) malloc::mem#0 + to:@3 +@3: scope:[] from @5 + [8] phi() + [9] call main + to:@end +@end: scope:[] from @3 + [10] phi() +main: scope:[main] from @3 + [11] phi() + [12] call init_dist_screen + to:main::@1 +main::@1: scope:[main] from main + [13] phi() + [14] call init_angle_screen + to:main::@2 +main::@2: scope:[main] from main::@1 + [15] phi() + [16] call init_buckets + to:main::@return +main::@return: scope:[main] from main::@2 + [17] return + to:@return +init_buckets: scope:[init_buckets] from main::@2 + [18] phi() + to:init_buckets::@1 +init_buckets::@1: scope:[init_buckets] from init_buckets init_buckets::@1 + [19] (byte) init_buckets::i#2 ← phi( init_buckets/(byte) 0 init_buckets::@1/(byte) init_buckets::i#1 ) + [20] (byte~) init_buckets::$2 ← (byte) init_buckets::i#2 << (byte) 1 + [21] *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$2) ← (byte) 0 + [22] (byte) init_buckets::i#1 ← ++ (byte) init_buckets::i#2 + [23] if((byte) init_buckets::i#1!=(byte) $80) goto init_buckets::@1 + to:init_buckets::@2 +init_buckets::@2: scope:[init_buckets] from init_buckets::@1 + [24] (byte*) init_buckets::dist#0 ← (byte*)(void*) SCREEN_DIST#0 + to:init_buckets::@3 +init_buckets::@3: scope:[init_buckets] from init_buckets::@2 init_buckets::@3 + [25] (word) init_buckets::i1#2 ← phi( init_buckets::@2/(word) 0 init_buckets::@3/(word) init_buckets::i1#1 ) + [25] (byte*) init_buckets::dist#2 ← phi( init_buckets::@2/(byte*) init_buckets::dist#0 init_buckets::@3/(byte*) init_buckets::dist#1 ) + [26] (byte~) init_buckets::$3 ← *((byte*) init_buckets::dist#2) << (byte) 1 + [27] *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$3) ← ++ *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$3) + [28] (byte*) init_buckets::dist#1 ← ++ (byte*) init_buckets::dist#2 + [29] (word) init_buckets::i1#1 ← ++ (word) init_buckets::i1#2 + [30] if((word) init_buckets::i1#1!=(word) $3e8) goto init_buckets::@3 + to:init_buckets::@return +init_buckets::@return: scope:[init_buckets] from init_buckets::@3 + [31] return + to:@return +init_angle_screen: scope:[init_angle_screen] from main::@1 + [32] phi() + to:init_angle_screen::@return +init_angle_screen::@return: scope:[init_angle_screen] from init_angle_screen + [33] return + to:@return +init_dist_screen: scope:[init_dist_screen] from main + [34] phi() + to:init_dist_screen::@return +init_dist_screen::@return: scope:[init_dist_screen] from init_dist_screen + [35] return + to:@return +malloc: scope:[malloc] from @1 @2 @4 + [36] (word) malloc::size#3 ← phi( @4/(word) $3e8 @1/(word) $3e8 @2/(byte) $80*(const byte) SIZEOF_WORD ) + [36] (byte*) heap_head#6 ← phi( @4/(byte*) heap_head#1 @1/(const byte*) HEAP_START#0 @2/(byte*) heap_head#1 ) + [37] (byte*) malloc::mem#0 ← (byte*) heap_head#6 + [38] (byte*) heap_head#1 ← (byte*) heap_head#6 + (word) malloc::size#3 + to:malloc::@return +malloc::@return: scope:[malloc] from malloc + [39] return + to:@return diff --git a/src/test/ref/screen-show-spiral-buckets.log b/src/test/ref/screen-show-spiral-buckets.log new file mode 100644 index 000000000..49c3c2287 --- /dev/null +++ b/src/test/ref/screen-show-spiral-buckets.log @@ -0,0 +1,1476 @@ +Fixing pointer addition (word*~) bsearch16u::$7 ← (word*) bsearch16u::items + (byte~) bsearch16u::$6 +Fixing pointer addition (word*~) bsearch16u::$15 ← (word*) bsearch16u::pivot + (number) 1 +Fixing pointer addition (word*~) bsearch16u::$1 ← (word*) bsearch16u::items - (number) 1 +Fixing pointer increment (word*) init_squares::squares ← ++ (word*) init_squares::squares +Fixing pointer addition (word~) sqrt::$1 ← (word*) sqrt::found - (word*) SQUARES +Fixing pointer array-indexing *((word*) SQUARES + (byte) sqr::val) +Fixing pointer array-indexing *((word[CORDIC_ITERATIONS_16]) CORDIC_ATAN2_ANGLES_16 + (byte) atan2_16::i) +Fixing pointer array-indexing *((word[CORDIC_ITERATIONS_16]) CORDIC_ATAN2_ANGLES_16 + (byte) atan2_16::i) +Fixing pointer array-indexing *((word*) BUCKET_SIZES + (byte) init_buckets::i) +Fixing pointer array-indexing *((word*) BUCKET_SIZES + *((byte*) init_buckets::dist)) +Fixing pointer array-indexing *((word*) BUCKET_SIZES + *((byte*) init_buckets::dist)) +Warning! Adding boolean cast to non-boolean sub-expression (byte) atan2_16::shift +Identified constant variable (byte*) HEAP_START +Identified constant variable (byte) NUM_SQUARES +Culled Empty Block (label) malloc::@1 +Culled Empty Block (label) @1 +Culled Empty Block (label) @2 +Culled Empty Block (label) @3 +Culled Empty Block (label) @4 +Culled Empty Block (label) @5 +Culled Empty Block (label) @6 +Culled Empty Block (label) @7 +Culled Empty Block (label) init_buckets::@4 +Culled Empty Block (label) @10 +Culled Empty Block (label) @11 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte*) HEAP_START#0 ← ((byte*)) (number) $c000 + (byte*) heap_head#0 ← (byte*) HEAP_START#0 + to:@8 +malloc: scope:[malloc] from @13 @8 @9 + (word) malloc::size#3 ← phi( @13/(word) malloc::size#1 @8/(word) malloc::size#0 @9/(word) malloc::size#2 ) + (byte*) heap_head#6 ← phi( @13/(byte*) heap_head#3 @8/(byte*) heap_head#11 @9/(byte*) heap_head#12 ) + (byte*) malloc::mem#0 ← (byte*) heap_head#6 + (byte*) heap_head#1 ← (byte*) heap_head#6 + (word) malloc::size#3 + (void*) malloc::return#0 ← ((void*)) (byte*) malloc::mem#0 + to:malloc::@return +malloc::@return: scope:[malloc] from malloc + (byte*) heap_head#7 ← phi( malloc/(byte*) heap_head#1 ) + (void*) malloc::return#5 ← phi( malloc/(void*) malloc::return#0 ) + (void*) malloc::return#1 ← (void*) malloc::return#5 + (byte*) heap_head#2 ← (byte*) heap_head#7 + return + to:@return +@8: scope:[] from @begin + (byte*) heap_head#11 ← phi( @begin/(byte*) heap_head#0 ) + (word) malloc::size#0 ← (number) $3e8 + call malloc + (void*) malloc::return#2 ← (void*) malloc::return#1 + to:@13 +@13: scope:[] from @8 + (byte*) heap_head#8 ← phi( @8/(byte*) heap_head#2 ) + (void*) malloc::return#6 ← phi( @8/(void*) malloc::return#2 ) + (void*~) $0 ← (void*) malloc::return#6 + (byte*) heap_head#3 ← (byte*) heap_head#8 + (byte*) SCREEN_DIST#0 ← ((byte*)) (void*~) $0 + (word) malloc::size#1 ← (number) $3e8 + call malloc + (void*) malloc::return#3 ← (void*) malloc::return#1 + to:@14 +@14: scope:[] from @13 + (byte*) heap_head#9 ← phi( @13/(byte*) heap_head#2 ) + (void*) malloc::return#7 ← phi( @13/(void*) malloc::return#3 ) + (void*~) $1 ← (void*) malloc::return#7 + (byte*) heap_head#4 ← (byte*) heap_head#9 + (byte*) SCREEN_ANGLE#0 ← ((byte*)) (void*~) $1 + to:@9 +main: scope:[main] from @12 + (byte*) init_dist_screen::screen#0 ← (byte*) SCREEN_DIST#0 + call init_dist_screen + to:main::@1 +main::@1: scope:[main] from main + (byte*) init_angle_screen::screen#0 ← (byte*) SCREEN_ANGLE#0 + call init_angle_screen + to:main::@2 +main::@2: scope:[main] from main::@1 + call init_buckets + to:main::@3 +main::@3: scope:[main] from main::@2 + to:main::@return +main::@return: scope:[main] from main::@3 + return + to:@return +@9: scope:[] from @14 + (byte*) heap_head#12 ← phi( @14/(byte*) heap_head#4 ) + (word) malloc::size#2 ← (number) $80*(const byte) SIZEOF_WORD + call malloc + (void*) malloc::return#4 ← (void*) malloc::return#1 + to:@15 +@15: scope:[] from @9 + (byte*) heap_head#10 ← phi( @9/(byte*) heap_head#2 ) + (void*) malloc::return#8 ← phi( @9/(void*) malloc::return#4 ) + (void*~) $2 ← (void*) malloc::return#8 + (byte*) heap_head#5 ← (byte*) heap_head#10 + (word*) BUCKET_SIZES#0 ← ((word*)) (void*~) $2 + to:@12 +init_buckets: scope:[init_buckets] from main::@2 + (byte) init_buckets::i#0 ← (byte) 0 + to:init_buckets::@1 +init_buckets::@1: scope:[init_buckets] from init_buckets init_buckets::@1 + (byte) init_buckets::i#2 ← phi( init_buckets/(byte) init_buckets::i#0 init_buckets::@1/(byte) init_buckets::i#1 ) + (byte~) init_buckets::$2 ← (byte) init_buckets::i#2 * (const byte) SIZEOF_WORD + *((word*) BUCKET_SIZES#0 + (byte~) init_buckets::$2) ← (number) 0 + (byte) init_buckets::i#1 ← (byte) init_buckets::i#2 + rangenext(0,$7f) + (bool~) init_buckets::$0 ← (byte) init_buckets::i#1 != rangelast(0,$7f) + if((bool~) init_buckets::$0) goto init_buckets::@1 + to:init_buckets::@2 +init_buckets::@2: scope:[init_buckets] from init_buckets::@1 + (byte*) init_buckets::dist#0 ← (byte*) SCREEN_DIST#0 + (word) init_buckets::i1#0 ← (word) 0 + to:init_buckets::@3 +init_buckets::@3: scope:[init_buckets] from init_buckets::@2 init_buckets::@3 + (word) init_buckets::i1#2 ← phi( init_buckets::@2/(word) init_buckets::i1#0 init_buckets::@3/(word) init_buckets::i1#1 ) + (byte*) init_buckets::dist#2 ← phi( init_buckets::@2/(byte*) init_buckets::dist#0 init_buckets::@3/(byte*) init_buckets::dist#1 ) + (byte~) init_buckets::$3 ← *((byte*) init_buckets::dist#2) * (const byte) SIZEOF_WORD + *((word*) BUCKET_SIZES#0 + (byte~) init_buckets::$3) ← ++ *((word*) BUCKET_SIZES#0 + (byte~) init_buckets::$3) + (byte*) init_buckets::dist#1 ← ++ (byte*) init_buckets::dist#2 + (word) init_buckets::i1#1 ← (word) init_buckets::i1#2 + rangenext(0,$3e7) + (bool~) init_buckets::$1 ← (word) init_buckets::i1#1 != rangelast(0,$3e7) + if((bool~) init_buckets::$1) goto init_buckets::@3 + to:init_buckets::@return +init_buckets::@return: scope:[init_buckets] from init_buckets::@3 + return + to:@return +init_angle_screen: scope:[init_angle_screen] from main::@1 + to:init_angle_screen::@return +init_angle_screen::@return: scope:[init_angle_screen] from init_angle_screen + return + to:@return +init_dist_screen: scope:[init_dist_screen] from main + to:init_dist_screen::@return +init_dist_screen::@return: scope:[init_dist_screen] from init_dist_screen + return + to:@return +@12: scope:[] from @15 + call main + to:@16 +@16: scope:[] from @12 + to:@end +@end: scope:[] from @16 + +SYMBOL TABLE SSA +(void*~) $0 +(void*~) $1 +(void*~) $2 +(label) @12 +(label) @13 +(label) @14 +(label) @15 +(label) @16 +(label) @8 +(label) @9 +(label) @begin +(label) @end +(word*) BUCKET_SIZES +(word*) BUCKET_SIZES#0 +(byte*) HEAP_START +(byte*) HEAP_START#0 +(byte*) SCREEN_ANGLE +(byte*) SCREEN_ANGLE#0 +(byte*) SCREEN_DIST +(byte*) SCREEN_DIST#0 +(const byte) SIZEOF_WORD = (byte) 2 +(byte*) heap_head +(byte*) heap_head#0 +(byte*) heap_head#1 +(byte*) heap_head#10 +(byte*) heap_head#11 +(byte*) heap_head#12 +(byte*) heap_head#2 +(byte*) heap_head#3 +(byte*) heap_head#4 +(byte*) heap_head#5 +(byte*) heap_head#6 +(byte*) heap_head#7 +(byte*) heap_head#8 +(byte*) heap_head#9 +(void()) init_angle_screen((byte*) init_angle_screen::screen) +(label) init_angle_screen::@return +(byte*) init_angle_screen::screen +(byte*) init_angle_screen::screen#0 +(void()) init_buckets() +(bool~) init_buckets::$0 +(bool~) init_buckets::$1 +(byte~) init_buckets::$2 +(byte~) init_buckets::$3 +(label) init_buckets::@1 +(label) init_buckets::@2 +(label) init_buckets::@3 +(label) init_buckets::@return +(byte*) init_buckets::dist +(byte*) init_buckets::dist#0 +(byte*) init_buckets::dist#1 +(byte*) init_buckets::dist#2 +(byte) init_buckets::i +(byte) init_buckets::i#0 +(byte) init_buckets::i#1 +(byte) init_buckets::i#2 +(word) init_buckets::i1 +(word) init_buckets::i1#0 +(word) init_buckets::i1#1 +(word) init_buckets::i1#2 +(void()) init_dist_screen((byte*) init_dist_screen::screen) +(label) init_dist_screen::@return +(byte*) init_dist_screen::screen +(byte*) init_dist_screen::screen#0 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(void*()) malloc((word) malloc::size) +(label) malloc::@return +(byte*) malloc::mem +(byte*) malloc::mem#0 +(void*) malloc::return +(void*) malloc::return#0 +(void*) malloc::return#1 +(void*) malloc::return#2 +(void*) malloc::return#3 +(void*) malloc::return#4 +(void*) malloc::return#5 +(void*) malloc::return#6 +(void*) malloc::return#7 +(void*) malloc::return#8 +(word) malloc::size +(word) malloc::size#0 +(word) malloc::size#1 +(word) malloc::size#2 +(word) malloc::size#3 + +Adding number conversion cast (unumber) $3e8 in (word) malloc::size#0 ← (number) $3e8 +Adding number conversion cast (unumber) $3e8 in (word) malloc::size#1 ← (number) $3e8 +Adding number conversion cast (unumber) $80*SIZEOF_WORD in (word) malloc::size#2 ← (number) $80*(const byte) SIZEOF_WORD +Adding number conversion cast (unumber) $80 in (word) malloc::size#2 ← ((unumber)) (number) $80*(const byte) SIZEOF_WORD +Adding number conversion cast (unumber) 0 in *((word*) BUCKET_SIZES#0 + (byte~) init_buckets::$2) ← (number) 0 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte*) HEAP_START#0 ← (byte*)(number) $c000 +Inlining cast (void*) malloc::return#0 ← (void*)(byte*) malloc::mem#0 +Inlining cast (word) malloc::size#0 ← (unumber)(number) $3e8 +Inlining cast (byte*) SCREEN_DIST#0 ← (byte*)(void*~) $0 +Inlining cast (word) malloc::size#1 ← (unumber)(number) $3e8 +Inlining cast (byte*) SCREEN_ANGLE#0 ← (byte*)(void*~) $1 +Inlining cast (word) malloc::size#2 ← (unumber)(unumber)(number) $80*(const byte) SIZEOF_WORD +Inlining cast (word*) BUCKET_SIZES#0 ← (word*)(void*~) $2 +Inlining cast *((word*) BUCKET_SIZES#0 + (byte~) init_buckets::$2) ← (unumber)(number) 0 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 49152 +Simplifying constant integer cast $3e8 +Simplifying constant integer cast $3e8 +Simplifying constant integer cast (unumber)(number) $80*(const byte) SIZEOF_WORD +Simplifying constant integer cast $80 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (word) $3e8 +Finalized unsigned number type (word) $3e8 +Finalized unsigned number type (byte) $80 +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (byte*) HEAP_START#0 = (byte*) heap_head#0 (byte*) heap_head#11 +Alias (void*) malloc::return#0 = (void*) malloc::return#5 (void*) malloc::return#1 +Alias (byte*) heap_head#1 = (byte*) heap_head#7 (byte*) heap_head#2 +Alias (void*) malloc::return#2 = (void*) malloc::return#6 +Alias (byte*) heap_head#3 = (byte*) heap_head#8 +Alias (void*) malloc::return#3 = (void*) malloc::return#7 +Alias (byte*) heap_head#12 = (byte*) heap_head#4 (byte*) heap_head#9 +Alias (void*) malloc::return#4 = (void*) malloc::return#8 +Alias (byte*) heap_head#10 = (byte*) heap_head#5 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values (byte*) heap_head#3 (byte*) heap_head#1 +Identical Phi Values (byte*) heap_head#12 (byte*) heap_head#1 +Identical Phi Values (byte*) heap_head#10 (byte*) heap_head#1 +Successful SSA optimization Pass2IdenticalPhiElimination +Simple Condition (bool~) init_buckets::$0 [45] if((byte) init_buckets::i#1!=rangelast(0,$7f)) goto init_buckets::@1 +Simple Condition (bool~) init_buckets::$1 [54] if((word) init_buckets::i1#1!=rangelast(0,$3e7)) goto init_buckets::@3 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte*) HEAP_START#0 = (byte*) 49152 +Constant (const word) malloc::size#0 = $3e8 +Constant (const word) malloc::size#1 = $3e8 +Constant (const word) malloc::size#2 = $80*SIZEOF_WORD +Constant (const byte) init_buckets::i#0 = 0 +Constant (const word) init_buckets::i1#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Resolved ranged next value [43] init_buckets::i#1 ← ++ init_buckets::i#2 to ++ +Resolved ranged comparison value [45] if(init_buckets::i#1!=rangelast(0,$7f)) goto init_buckets::@1 to (number) $80 +Resolved ranged next value [52] init_buckets::i1#1 ← ++ init_buckets::i1#2 to ++ +Resolved ranged comparison value [54] if(init_buckets::i1#1!=rangelast(0,$3e7)) goto init_buckets::@3 to (number) $3e8 +Eliminating unused variable (byte*) init_dist_screen::screen#0 and assignment [13] (byte*) init_dist_screen::screen#0 ← (byte*) SCREEN_DIST#0 +Eliminating unused variable (byte*) init_angle_screen::screen#0 and assignment [15] (byte*) init_angle_screen::screen#0 ← (byte*) SCREEN_ANGLE#0 +Successful SSA optimization PassNEliminateUnusedVars +Eliminating unused variable (byte*) SCREEN_ANGLE#0 and assignment [12] (byte*) SCREEN_ANGLE#0 ← (byte*)(void*~) $1 +Successful SSA optimization PassNEliminateUnusedVars +Eliminating unused variable (void*~) $1 and assignment [11] (void*~) $1 ← (void*) malloc::return#3 +Successful SSA optimization PassNEliminateUnusedVars +Eliminating unused variable (void*) malloc::return#3 and assignment [10] (void*) malloc::return#3 ← (void*) malloc::return#0 +Successful SSA optimization PassNEliminateUnusedVars +Adding number conversion cast (unumber) $80 in if((byte) init_buckets::i#1!=(number) $80) goto init_buckets::@1 +Adding number conversion cast (unumber) $3e8 in if((word) init_buckets::i1#1!=(number) $3e8) goto init_buckets::@3 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant integer cast $80 +Simplifying constant integer cast $3e8 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) $80 +Finalized unsigned number type (word) $3e8 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Inlining Noop Cast [3] (void*) malloc::return#0 ← (void*)(byte*) malloc::mem#0 keeping malloc::mem#0 +Successful SSA optimization Pass2NopCastInlining +Inlining Noop Cast [6] (void*) malloc::return#2 ← (void*)(byte*) malloc::mem#0 keeping malloc::mem#0 +Inlining Noop Cast [15] (void*) malloc::return#4 ← (void*)(byte*) malloc::mem#0 keeping malloc::mem#0 +Successful SSA optimization Pass2NopCastInlining +Inlining Noop Cast [8] (byte*) SCREEN_DIST#0 ← (byte*)(void*~) $0 keeping SCREEN_DIST#0 +Inlining Noop Cast [17] (word*) BUCKET_SIZES#0 ← (word*)(void*~) $2 keeping BUCKET_SIZES#0 +Successful SSA optimization Pass2NopCastInlining +Rewriting multiplication to use shift [19] (byte~) init_buckets::$2 ← (byte) init_buckets::i#2 * (const byte) SIZEOF_WORD +Rewriting multiplication to use shift [25] (byte~) init_buckets::$3 ← *((byte*) init_buckets::dist#2) * (const byte) SIZEOF_WORD +Successful SSA optimization Pass2MultiplyToShiftRewriting +Inlining constant with var siblings (const word) malloc::size#0 +Inlining constant with var siblings (const word) malloc::size#1 +Inlining constant with var siblings (const word) malloc::size#2 +Inlining constant with var siblings (const byte) init_buckets::i#0 +Inlining constant with var siblings (const word) init_buckets::i1#0 +Constant inlined init_buckets::i1#0 = (word) 0 +Constant inlined malloc::size#2 = (byte) $80*(const byte) SIZEOF_WORD +Constant inlined malloc::size#1 = (word) $3e8 +Constant inlined malloc::size#0 = (word) $3e8 +Constant inlined init_buckets::i#0 = (byte) 0 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting init_buckets::@5(between init_buckets::@1 and init_buckets::@1) +Added new block during phi lifting init_buckets::@6(between init_buckets::@3 and init_buckets::@3) +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @8 +Adding NOP phi() at start of @14 +Adding NOP phi() at start of @12 +Adding NOP phi() at start of @16 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@1 +Adding NOP phi() at start of main::@2 +Adding NOP phi() at start of main::@3 +Adding NOP phi() at start of init_buckets +Adding NOP phi() at start of init_angle_screen +Adding NOP phi() at start of init_dist_screen +CALL GRAPH +Calls in [] to malloc:2 malloc:5 malloc:8 main:11 +Calls in [main] to init_dist_screen:15 init_angle_screen:17 init_buckets:19 + +Created 5 initial phi equivalence classes +Coalesced [4] heap_head#13 ← heap_head#1 +Coalesced (already) [7] heap_head#14 ← heap_head#1 +Coalesced [29] init_buckets::dist#3 ← init_buckets::dist#0 +Coalesced [37] init_buckets::dist#4 ← init_buckets::dist#1 +Coalesced [38] init_buckets::i1#3 ← init_buckets::i1#1 +Coalesced [39] init_buckets::i#3 ← init_buckets::i#1 +Coalesced down to 5 phi equivalence classes +Culled Empty Block (label) @14 +Culled Empty Block (label) @16 +Culled Empty Block (label) main::@3 +Culled Empty Block (label) init_buckets::@6 +Culled Empty Block (label) init_buckets::@5 +Renumbering block @8 to @1 +Renumbering block @9 to @2 +Renumbering block @12 to @3 +Renumbering block @13 to @4 +Renumbering block @15 to @5 +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 @3 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@1 +Adding NOP phi() at start of main::@2 +Adding NOP phi() at start of init_buckets +Adding NOP phi() at start of init_angle_screen +Adding NOP phi() at start of init_dist_screen + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call malloc + to:@4 +@4: scope:[] from @1 + [3] (void*) SCREEN_DIST#0 ← (void*)(byte*) malloc::mem#0 + [4] call malloc + to:@2 +@2: scope:[] from @4 + [5] phi() + [6] call malloc + to:@5 +@5: scope:[] from @2 + [7] (void*) BUCKET_SIZES#0 ← (void*)(byte*) malloc::mem#0 + to:@3 +@3: scope:[] from @5 + [8] phi() + [9] call main + to:@end +@end: scope:[] from @3 + [10] phi() +main: scope:[main] from @3 + [11] phi() + [12] call init_dist_screen + to:main::@1 +main::@1: scope:[main] from main + [13] phi() + [14] call init_angle_screen + to:main::@2 +main::@2: scope:[main] from main::@1 + [15] phi() + [16] call init_buckets + to:main::@return +main::@return: scope:[main] from main::@2 + [17] return + to:@return +init_buckets: scope:[init_buckets] from main::@2 + [18] phi() + to:init_buckets::@1 +init_buckets::@1: scope:[init_buckets] from init_buckets init_buckets::@1 + [19] (byte) init_buckets::i#2 ← phi( init_buckets/(byte) 0 init_buckets::@1/(byte) init_buckets::i#1 ) + [20] (byte~) init_buckets::$2 ← (byte) init_buckets::i#2 << (byte) 1 + [21] *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$2) ← (byte) 0 + [22] (byte) init_buckets::i#1 ← ++ (byte) init_buckets::i#2 + [23] if((byte) init_buckets::i#1!=(byte) $80) goto init_buckets::@1 + to:init_buckets::@2 +init_buckets::@2: scope:[init_buckets] from init_buckets::@1 + [24] (byte*) init_buckets::dist#0 ← (byte*)(void*) SCREEN_DIST#0 + to:init_buckets::@3 +init_buckets::@3: scope:[init_buckets] from init_buckets::@2 init_buckets::@3 + [25] (word) init_buckets::i1#2 ← phi( init_buckets::@2/(word) 0 init_buckets::@3/(word) init_buckets::i1#1 ) + [25] (byte*) init_buckets::dist#2 ← phi( init_buckets::@2/(byte*) init_buckets::dist#0 init_buckets::@3/(byte*) init_buckets::dist#1 ) + [26] (byte~) init_buckets::$3 ← *((byte*) init_buckets::dist#2) << (byte) 1 + [27] *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$3) ← ++ *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$3) + [28] (byte*) init_buckets::dist#1 ← ++ (byte*) init_buckets::dist#2 + [29] (word) init_buckets::i1#1 ← ++ (word) init_buckets::i1#2 + [30] if((word) init_buckets::i1#1!=(word) $3e8) goto init_buckets::@3 + to:init_buckets::@return +init_buckets::@return: scope:[init_buckets] from init_buckets::@3 + [31] return + to:@return +init_angle_screen: scope:[init_angle_screen] from main::@1 + [32] phi() + to:init_angle_screen::@return +init_angle_screen::@return: scope:[init_angle_screen] from init_angle_screen + [33] return + to:@return +init_dist_screen: scope:[init_dist_screen] from main + [34] phi() + to:init_dist_screen::@return +init_dist_screen::@return: scope:[init_dist_screen] from init_dist_screen + [35] return + to:@return +malloc: scope:[malloc] from @1 @2 @4 + [36] (word) malloc::size#3 ← phi( @4/(word) $3e8 @1/(word) $3e8 @2/(byte) $80*(const byte) SIZEOF_WORD ) + [36] (byte*) heap_head#6 ← phi( @4/(byte*) heap_head#1 @1/(const byte*) HEAP_START#0 @2/(byte*) heap_head#1 ) + [37] (byte*) malloc::mem#0 ← (byte*) heap_head#6 + [38] (byte*) heap_head#1 ← (byte*) heap_head#6 + (word) malloc::size#3 + to:malloc::@return +malloc::@return: scope:[malloc] from malloc + [39] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(word*) BUCKET_SIZES +(void*) BUCKET_SIZES#0 0.1 +(byte*) HEAP_START +(byte*) SCREEN_ANGLE +(byte*) SCREEN_DIST +(void*) SCREEN_DIST#0 0.11764705882352941 +(byte*) heap_head +(byte*) heap_head#1 1.0 +(byte*) heap_head#6 4.0 +(void()) init_angle_screen((byte*) init_angle_screen::screen) +(byte*) init_angle_screen::screen +(void()) init_buckets() +(byte~) init_buckets::$2 22.0 +(byte~) init_buckets::$3 33.0 +(byte*) init_buckets::dist +(byte*) init_buckets::dist#0 4.0 +(byte*) init_buckets::dist#1 7.333333333333333 +(byte*) init_buckets::dist#2 11.666666666666666 +(byte) init_buckets::i +(byte) init_buckets::i#1 16.5 +(byte) init_buckets::i#2 11.0 +(word) init_buckets::i1 +(word) init_buckets::i1#1 16.5 +(word) init_buckets::i1#2 5.5 +(void()) init_dist_screen((byte*) init_dist_screen::screen) +(byte*) init_dist_screen::screen +(void()) main() +(void*()) malloc((word) malloc::size) +(byte*) malloc::mem +(byte*) malloc::mem#0 0.4 +(void*) malloc::return +(word) malloc::size +(word) malloc::size#3 1.0 + +Initial phi equivalence classes +[ init_buckets::i#2 init_buckets::i#1 ] +[ init_buckets::dist#2 init_buckets::dist#0 init_buckets::dist#1 ] +[ init_buckets::i1#2 init_buckets::i1#1 ] +[ heap_head#6 heap_head#1 ] +[ malloc::size#3 ] +Added variable SCREEN_DIST#0 to zero page equivalence class [ SCREEN_DIST#0 ] +Added variable BUCKET_SIZES#0 to zero page equivalence class [ BUCKET_SIZES#0 ] +Added variable init_buckets::$2 to zero page equivalence class [ init_buckets::$2 ] +Added variable init_buckets::$3 to zero page equivalence class [ init_buckets::$3 ] +Added variable malloc::mem#0 to zero page equivalence class [ malloc::mem#0 ] +Complete equivalence classes +[ init_buckets::i#2 init_buckets::i#1 ] +[ init_buckets::dist#2 init_buckets::dist#0 init_buckets::dist#1 ] +[ init_buckets::i1#2 init_buckets::i1#1 ] +[ heap_head#6 heap_head#1 ] +[ malloc::size#3 ] +[ SCREEN_DIST#0 ] +[ BUCKET_SIZES#0 ] +[ init_buckets::$2 ] +[ init_buckets::$3 ] +[ malloc::mem#0 ] +Allocated zp ZP_BYTE:2 [ init_buckets::i#2 init_buckets::i#1 ] +Allocated zp ZP_WORD:3 [ init_buckets::dist#2 init_buckets::dist#0 init_buckets::dist#1 ] +Allocated zp ZP_WORD:5 [ init_buckets::i1#2 init_buckets::i1#1 ] +Allocated zp ZP_WORD:7 [ heap_head#6 heap_head#1 ] +Allocated zp ZP_WORD:9 [ malloc::size#3 ] +Allocated zp ZP_WORD:11 [ SCREEN_DIST#0 ] +Allocated zp ZP_WORD:13 [ BUCKET_SIZES#0 ] +Allocated zp ZP_BYTE:15 [ init_buckets::$2 ] +Allocated zp ZP_BYTE:16 [ init_buckets::$3 ] +Allocated zp ZP_WORD:17 [ malloc::mem#0 ] + +INITIAL ASM + // File Comments +// Fill screen using a spiral based on distance-to-center / angle-to-center +// Utilizes a bucket sort for identifying the minimum angle/distance + // Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .const SIZEOF_WORD = 2 + // Start of the heap used by malloc() + .label HEAP_START = $c000 + .label heap_head = 7 + // Screen containing distance to center + .label SCREEN_DIST = $b + // Array containing the bucket size for each of the 256 buckets + .label BUCKET_SIZES = $d + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call malloc + // [36] phi from @1 to malloc [phi:@1->malloc] +malloc_from_b1: + // [36] phi (word) malloc::size#3 = (word) $3e8 [phi:@1->malloc#0] -- vwuz1=vwuc1 + lda #<$3e8 + sta malloc.size + lda #>$3e8 + sta malloc.size+1 + // [36] phi (byte*) heap_head#6 = (const byte*) HEAP_START#0 [phi:@1->malloc#1] -- pbuz1=pbuc1 + lda #<HEAP_START + sta heap_head + lda #>HEAP_START + sta heap_head+1 + jsr malloc + jmp b4 + // @4 +b4: + // [3] (void*) SCREEN_DIST#0 ← (void*)(byte*) malloc::mem#0 -- pvoz1=pvoz2 + lda malloc.mem + sta SCREEN_DIST + lda malloc.mem+1 + sta SCREEN_DIST+1 + // [4] call malloc + // [36] phi from @4 to malloc [phi:@4->malloc] +malloc_from_b4: + // [36] phi (word) malloc::size#3 = (word) $3e8 [phi:@4->malloc#0] -- vwuz1=vwuc1 + lda #<$3e8 + sta malloc.size + lda #>$3e8 + sta malloc.size+1 + // [36] phi (byte*) heap_head#6 = (byte*) heap_head#1 [phi:@4->malloc#1] -- register_copy + jsr malloc + // [5] phi from @4 to @2 [phi:@4->@2] +b2_from_b4: + jmp b2 + // @2 +b2: + // [6] call malloc + // [36] phi from @2 to malloc [phi:@2->malloc] +malloc_from_b2: + // [36] phi (word) malloc::size#3 = (byte) $80*(const byte) SIZEOF_WORD [phi:@2->malloc#0] -- vwuz1=vbuc1 + lda #$80*SIZEOF_WORD + sta malloc.size + lda #0 + sta malloc.size+1 + // [36] phi (byte*) heap_head#6 = (byte*) heap_head#1 [phi:@2->malloc#1] -- register_copy + jsr malloc + jmp b5 + // @5 +b5: + // [7] (void*) BUCKET_SIZES#0 ← (void*)(byte*) malloc::mem#0 -- pvoz1=pvoz2 + lda malloc.mem + sta BUCKET_SIZES + lda malloc.mem+1 + sta BUCKET_SIZES+1 + // [8] phi from @5 to @3 [phi:@5->@3] +b3_from_b5: + jmp b3 + // @3 +b3: + // [9] call main + // [11] phi from @3 to main [phi:@3->main] +main_from_b3: + jsr main + // [10] phi from @3 to @end [phi:@3->@end] +bend_from_b3: + jmp bend + // @end +bend: + // main +main: { + // [12] call init_dist_screen + // [34] phi from main to init_dist_screen [phi:main->init_dist_screen] + init_dist_screen_from_main: + jsr init_dist_screen + // [13] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + jmp b1 + // main::@1 + b1: + // [14] call init_angle_screen + // [32] phi from main::@1 to init_angle_screen [phi:main::@1->init_angle_screen] + init_angle_screen_from_b1: + jsr init_angle_screen + // [15] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + b2_from_b1: + jmp b2 + // main::@2 + b2: + // [16] call init_buckets + // [18] phi from main::@2 to init_buckets [phi:main::@2->init_buckets] + init_buckets_from_b2: + jsr init_buckets + jmp breturn + // main::@return + breturn: + // [17] return + rts +} + // init_buckets +init_buckets: { + .label _2 = $f + .label _3 = $10 + .label i = 2 + .label dist = 3 + .label i1 = 5 + // [19] phi from init_buckets to init_buckets::@1 [phi:init_buckets->init_buckets::@1] + b1_from_init_buckets: + // [19] phi (byte) init_buckets::i#2 = (byte) 0 [phi:init_buckets->init_buckets::@1#0] -- vbuz1=vbuc1 + lda #0 + sta i + jmp b1 + // Init bucket sizes to 0 + // [19] phi from init_buckets::@1 to init_buckets::@1 [phi:init_buckets::@1->init_buckets::@1] + b1_from_b1: + // [19] phi (byte) init_buckets::i#2 = (byte) init_buckets::i#1 [phi:init_buckets::@1->init_buckets::@1#0] -- register_copy + jmp b1 + // init_buckets::@1 + b1: + // [20] (byte~) init_buckets::$2 ← (byte) init_buckets::i#2 << (byte) 1 -- vbuz1=vbuz2_rol_1 + lda i + asl + sta _2 + // [21] *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$2) ← (byte) 0 -- pwuz1_derefidx_vbuz2=vwuc1 + ldy _2 + lda #<0 + sta (BUCKET_SIZES),y + iny + lda #>0 + sta (BUCKET_SIZES),y + // [22] (byte) init_buckets::i#1 ← ++ (byte) init_buckets::i#2 -- vbuz1=_inc_vbuz1 + inc i + // [23] if((byte) init_buckets::i#1!=(byte) $80) goto init_buckets::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #$80 + cmp i + bne b1_from_b1 + jmp b2 + // init_buckets::@2 + b2: + // [24] (byte*) init_buckets::dist#0 ← (byte*)(void*) SCREEN_DIST#0 -- pbuz1=pbuz2 + // first find bucket sizes - by counting number of chars with each distance value + lda SCREEN_DIST + sta dist + lda SCREEN_DIST+1 + sta dist+1 + // [25] phi from init_buckets::@2 to init_buckets::@3 [phi:init_buckets::@2->init_buckets::@3] + b3_from_b2: + // [25] phi (word) init_buckets::i1#2 = (word) 0 [phi:init_buckets::@2->init_buckets::@3#0] -- vwuz1=vwuc1 + lda #<0 + sta i1 + lda #>0 + sta i1+1 + // [25] phi (byte*) init_buckets::dist#2 = (byte*) init_buckets::dist#0 [phi:init_buckets::@2->init_buckets::@3#1] -- register_copy + jmp b3 + // [25] phi from init_buckets::@3 to init_buckets::@3 [phi:init_buckets::@3->init_buckets::@3] + b3_from_b3: + // [25] phi (word) init_buckets::i1#2 = (word) init_buckets::i1#1 [phi:init_buckets::@3->init_buckets::@3#0] -- register_copy + // [25] phi (byte*) init_buckets::dist#2 = (byte*) init_buckets::dist#1 [phi:init_buckets::@3->init_buckets::@3#1] -- register_copy + jmp b3 + // init_buckets::@3 + b3: + // [26] (byte~) init_buckets::$3 ← *((byte*) init_buckets::dist#2) << (byte) 1 -- vbuz1=_deref_pbuz2_rol_1 + ldy #0 + lda (dist),y + asl + sta _3 + // [27] *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$3) ← ++ *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$3) -- pwuz1_derefidx_vbuz2=_inc_pwuz1_derefidx_vbuz2 + ldy _3 + lda (BUCKET_SIZES),y + clc + adc #1 + sta (BUCKET_SIZES),y + bne !+ + iny + lda (BUCKET_SIZES),y + adc #0 + sta (BUCKET_SIZES),y + !: + // [28] (byte*) init_buckets::dist#1 ← ++ (byte*) init_buckets::dist#2 -- pbuz1=_inc_pbuz1 + inc dist + bne !+ + inc dist+1 + !: + // [29] (word) init_buckets::i1#1 ← ++ (word) init_buckets::i1#2 -- vwuz1=_inc_vwuz1 + inc i1 + bne !+ + inc i1+1 + !: + // [30] if((word) init_buckets::i1#1!=(word) $3e8) goto init_buckets::@3 -- vwuz1_neq_vwuc1_then_la1 + lda i1+1 + cmp #>$3e8 + bne b3_from_b3 + lda i1 + cmp #<$3e8 + bne b3_from_b3 + jmp breturn + // init_buckets::@return + breturn: + // [31] return + rts +} + // init_angle_screen +// Populates 1000 bytes (a screen) with values representing the angle to the center. +// Utilizes symmetry around the center +init_angle_screen: { + jmp breturn + // init_angle_screen::@return + breturn: + // [33] return + rts +} + // init_dist_screen +// Populates 1000 bytes (a screen) with values representing the distance to the center. +// The actual value stored is distance*2 to increase precision +init_dist_screen: { + jmp breturn + // init_dist_screen::@return + breturn: + // [35] return + rts +} + // malloc +// Allocates a block of size bytes of memory, returning a pointer to the beginning of the block. +// The content of the newly allocated block of memory is not initialized, remaining with indeterminate values. +// malloc(word zeropage(9) size) +malloc: { + .label mem = $11 + .label size = 9 + // [37] (byte*) malloc::mem#0 ← (byte*) heap_head#6 -- pbuz1=pbuz2 + lda heap_head + sta mem + lda heap_head+1 + sta mem+1 + // [38] (byte*) heap_head#1 ← (byte*) heap_head#6 + (word) malloc::size#3 -- pbuz1=pbuz1_plus_vwuz2 + lda heap_head + clc + adc size + sta heap_head + lda heap_head+1 + adc size+1 + sta heap_head+1 + jmp breturn + // malloc::@return + breturn: + // [39] return + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [3] (void*) SCREEN_DIST#0 ← (void*)(byte*) malloc::mem#0 [ SCREEN_DIST#0 heap_head#1 ] ( [ SCREEN_DIST#0 heap_head#1 ] ) always clobbers reg byte a +Statement [7] (void*) BUCKET_SIZES#0 ← (void*)(byte*) malloc::mem#0 [ SCREEN_DIST#0 BUCKET_SIZES#0 ] ( [ SCREEN_DIST#0 BUCKET_SIZES#0 ] ) always clobbers reg byte a +Statement [20] (byte~) init_buckets::$2 ← (byte) init_buckets::i#2 << (byte) 1 [ SCREEN_DIST#0 BUCKET_SIZES#0 init_buckets::i#2 init_buckets::$2 ] ( main:9::init_buckets:16 [ SCREEN_DIST#0 BUCKET_SIZES#0 init_buckets::i#2 init_buckets::$2 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ init_buckets::i#2 init_buckets::i#1 ] +Statement [21] *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$2) ← (byte) 0 [ SCREEN_DIST#0 BUCKET_SIZES#0 init_buckets::i#2 ] ( main:9::init_buckets:16 [ SCREEN_DIST#0 BUCKET_SIZES#0 init_buckets::i#2 ] ) always clobbers reg byte a reg byte y +Removing always clobbered register reg byte y as potential for zp ZP_BYTE:2 [ init_buckets::i#2 init_buckets::i#1 ] +Statement [24] (byte*) init_buckets::dist#0 ← (byte*)(void*) SCREEN_DIST#0 [ BUCKET_SIZES#0 init_buckets::dist#0 ] ( main:9::init_buckets:16 [ BUCKET_SIZES#0 init_buckets::dist#0 ] ) always clobbers reg byte a +Statement [26] (byte~) init_buckets::$3 ← *((byte*) init_buckets::dist#2) << (byte) 1 [ BUCKET_SIZES#0 init_buckets::dist#2 init_buckets::i1#2 init_buckets::$3 ] ( main:9::init_buckets:16 [ BUCKET_SIZES#0 init_buckets::dist#2 init_buckets::i1#2 init_buckets::$3 ] ) always clobbers reg byte a reg byte y +Statement [27] *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$3) ← ++ *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$3) [ BUCKET_SIZES#0 init_buckets::dist#2 init_buckets::i1#2 ] ( main:9::init_buckets:16 [ BUCKET_SIZES#0 init_buckets::dist#2 init_buckets::i1#2 ] ) always clobbers reg byte a reg byte y +Statement [30] if((word) init_buckets::i1#1!=(word) $3e8) goto init_buckets::@3 [ BUCKET_SIZES#0 init_buckets::dist#1 init_buckets::i1#1 ] ( main:9::init_buckets:16 [ BUCKET_SIZES#0 init_buckets::dist#1 init_buckets::i1#1 ] ) always clobbers reg byte a +Statement [37] (byte*) malloc::mem#0 ← (byte*) heap_head#6 [ malloc::mem#0 heap_head#6 malloc::size#3 ] ( malloc:2 [ malloc::mem#0 heap_head#6 malloc::size#3 ] malloc:4 [ malloc::mem#0 heap_head#6 malloc::size#3 ] malloc:6 [ malloc::mem#0 heap_head#6 malloc::size#3 ] ) always clobbers reg byte a +Statement [38] (byte*) heap_head#1 ← (byte*) heap_head#6 + (word) malloc::size#3 [ malloc::mem#0 heap_head#1 ] ( malloc:2 [ malloc::mem#0 heap_head#1 ] malloc:4 [ malloc::mem#0 heap_head#1 ] malloc:6 [ malloc::mem#0 heap_head#1 ] ) always clobbers reg byte a +Statement [3] (void*) SCREEN_DIST#0 ← (void*)(byte*) malloc::mem#0 [ SCREEN_DIST#0 heap_head#1 ] ( [ SCREEN_DIST#0 heap_head#1 ] ) always clobbers reg byte a +Statement [7] (void*) BUCKET_SIZES#0 ← (void*)(byte*) malloc::mem#0 [ SCREEN_DIST#0 BUCKET_SIZES#0 ] ( [ SCREEN_DIST#0 BUCKET_SIZES#0 ] ) always clobbers reg byte a +Statement [20] (byte~) init_buckets::$2 ← (byte) init_buckets::i#2 << (byte) 1 [ SCREEN_DIST#0 BUCKET_SIZES#0 init_buckets::i#2 init_buckets::$2 ] ( main:9::init_buckets:16 [ SCREEN_DIST#0 BUCKET_SIZES#0 init_buckets::i#2 init_buckets::$2 ] ) always clobbers reg byte a +Statement [21] *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$2) ← (byte) 0 [ SCREEN_DIST#0 BUCKET_SIZES#0 init_buckets::i#2 ] ( main:9::init_buckets:16 [ SCREEN_DIST#0 BUCKET_SIZES#0 init_buckets::i#2 ] ) always clobbers reg byte a reg byte y +Statement [24] (byte*) init_buckets::dist#0 ← (byte*)(void*) SCREEN_DIST#0 [ BUCKET_SIZES#0 init_buckets::dist#0 ] ( main:9::init_buckets:16 [ BUCKET_SIZES#0 init_buckets::dist#0 ] ) always clobbers reg byte a +Statement [26] (byte~) init_buckets::$3 ← *((byte*) init_buckets::dist#2) << (byte) 1 [ BUCKET_SIZES#0 init_buckets::dist#2 init_buckets::i1#2 init_buckets::$3 ] ( main:9::init_buckets:16 [ BUCKET_SIZES#0 init_buckets::dist#2 init_buckets::i1#2 init_buckets::$3 ] ) always clobbers reg byte a reg byte y +Statement [27] *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$3) ← ++ *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$3) [ BUCKET_SIZES#0 init_buckets::dist#2 init_buckets::i1#2 ] ( main:9::init_buckets:16 [ BUCKET_SIZES#0 init_buckets::dist#2 init_buckets::i1#2 ] ) always clobbers reg byte a reg byte y +Statement [30] if((word) init_buckets::i1#1!=(word) $3e8) goto init_buckets::@3 [ BUCKET_SIZES#0 init_buckets::dist#1 init_buckets::i1#1 ] ( main:9::init_buckets:16 [ BUCKET_SIZES#0 init_buckets::dist#1 init_buckets::i1#1 ] ) always clobbers reg byte a +Statement [37] (byte*) malloc::mem#0 ← (byte*) heap_head#6 [ malloc::mem#0 heap_head#6 malloc::size#3 ] ( malloc:2 [ malloc::mem#0 heap_head#6 malloc::size#3 ] malloc:4 [ malloc::mem#0 heap_head#6 malloc::size#3 ] malloc:6 [ malloc::mem#0 heap_head#6 malloc::size#3 ] ) always clobbers reg byte a +Statement [38] (byte*) heap_head#1 ← (byte*) heap_head#6 + (word) malloc::size#3 [ malloc::mem#0 heap_head#1 ] ( malloc:2 [ malloc::mem#0 heap_head#1 ] malloc:4 [ malloc::mem#0 heap_head#1 ] malloc:6 [ malloc::mem#0 heap_head#1 ] ) always clobbers reg byte a +Potential registers zp ZP_BYTE:2 [ init_buckets::i#2 init_buckets::i#1 ] : zp ZP_BYTE:2 , reg byte x , +Potential registers zp ZP_WORD:3 [ init_buckets::dist#2 init_buckets::dist#0 init_buckets::dist#1 ] : zp ZP_WORD:3 , +Potential registers zp ZP_WORD:5 [ init_buckets::i1#2 init_buckets::i1#1 ] : zp ZP_WORD:5 , +Potential registers zp ZP_WORD:7 [ heap_head#6 heap_head#1 ] : zp ZP_WORD:7 , +Potential registers zp ZP_WORD:9 [ malloc::size#3 ] : zp ZP_WORD:9 , +Potential registers zp ZP_WORD:11 [ SCREEN_DIST#0 ] : zp ZP_WORD:11 , +Potential registers zp ZP_WORD:13 [ BUCKET_SIZES#0 ] : zp ZP_WORD:13 , +Potential registers zp ZP_BYTE:15 [ init_buckets::$2 ] : zp ZP_BYTE:15 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:16 [ init_buckets::$3 ] : zp ZP_BYTE:16 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_WORD:17 [ malloc::mem#0 ] : zp ZP_WORD:17 , + +REGISTER UPLIFT SCOPES +Uplift Scope [init_buckets] 33: zp ZP_BYTE:16 [ init_buckets::$3 ] 27.5: zp ZP_BYTE:2 [ init_buckets::i#2 init_buckets::i#1 ] 23: zp ZP_WORD:3 [ init_buckets::dist#2 init_buckets::dist#0 init_buckets::dist#1 ] 22: zp ZP_WORD:5 [ init_buckets::i1#2 init_buckets::i1#1 ] 22: zp ZP_BYTE:15 [ init_buckets::$2 ] +Uplift Scope [] 5: zp ZP_WORD:7 [ heap_head#6 heap_head#1 ] 0.12: zp ZP_WORD:11 [ SCREEN_DIST#0 ] 0.1: zp ZP_WORD:13 [ BUCKET_SIZES#0 ] +Uplift Scope [malloc] 1: zp ZP_WORD:9 [ malloc::size#3 ] 0.4: zp ZP_WORD:17 [ malloc::mem#0 ] +Uplift Scope [main] +Uplift Scope [init_angle_screen] +Uplift Scope [init_dist_screen] + +Uplifting [init_buckets] best 1725 combination reg byte a [ init_buckets::$3 ] reg byte x [ init_buckets::i#2 init_buckets::i#1 ] zp ZP_WORD:3 [ init_buckets::dist#2 init_buckets::dist#0 init_buckets::dist#1 ] zp ZP_WORD:5 [ init_buckets::i1#2 init_buckets::i1#1 ] reg byte a [ init_buckets::$2 ] +Uplifting [] best 1725 combination zp ZP_WORD:7 [ heap_head#6 heap_head#1 ] zp ZP_WORD:11 [ SCREEN_DIST#0 ] zp ZP_WORD:13 [ BUCKET_SIZES#0 ] +Uplifting [malloc] best 1725 combination zp ZP_WORD:9 [ malloc::size#3 ] zp ZP_WORD:17 [ malloc::mem#0 ] +Uplifting [main] best 1725 combination +Uplifting [init_angle_screen] best 1725 combination +Uplifting [init_dist_screen] best 1725 combination +Coalescing zero page register with common assignment [ zp ZP_WORD:3 [ init_buckets::dist#2 init_buckets::dist#0 init_buckets::dist#1 ] ] with [ zp ZP_WORD:11 [ SCREEN_DIST#0 ] ] - score: 1 +Coalescing zero page register with common assignment [ zp ZP_WORD:13 [ BUCKET_SIZES#0 ] ] with [ zp ZP_WORD:17 [ malloc::mem#0 ] ] - score: 1 +Allocated (was zp ZP_WORD:3) zp ZP_WORD:2 [ init_buckets::dist#2 init_buckets::dist#0 init_buckets::dist#1 SCREEN_DIST#0 ] +Allocated (was zp ZP_WORD:5) zp ZP_WORD:4 [ init_buckets::i1#2 init_buckets::i1#1 ] +Allocated (was zp ZP_WORD:7) zp ZP_WORD:6 [ heap_head#6 heap_head#1 ] +Allocated (was zp ZP_WORD:9) zp ZP_WORD:8 [ malloc::size#3 ] +Allocated (was zp ZP_WORD:13) zp ZP_WORD:10 [ BUCKET_SIZES#0 malloc::mem#0 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Fill screen using a spiral based on distance-to-center / angle-to-center +// Utilizes a bucket sort for identifying the minimum angle/distance + // Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .const SIZEOF_WORD = 2 + // Start of the heap used by malloc() + .label HEAP_START = $c000 + .label heap_head = 6 + // Screen containing distance to center + .label SCREEN_DIST = 2 + // Array containing the bucket size for each of the 256 buckets + .label BUCKET_SIZES = $a + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call malloc + // [36] phi from @1 to malloc [phi:@1->malloc] +malloc_from_b1: + // [36] phi (word) malloc::size#3 = (word) $3e8 [phi:@1->malloc#0] -- vwuz1=vwuc1 + lda #<$3e8 + sta malloc.size + lda #>$3e8 + sta malloc.size+1 + // [36] phi (byte*) heap_head#6 = (const byte*) HEAP_START#0 [phi:@1->malloc#1] -- pbuz1=pbuc1 + lda #<HEAP_START + sta heap_head + lda #>HEAP_START + sta heap_head+1 + jsr malloc + jmp b4 + // @4 +b4: + // [3] (void*) SCREEN_DIST#0 ← (void*)(byte*) malloc::mem#0 -- pvoz1=pvoz2 + lda malloc.mem + sta SCREEN_DIST + lda malloc.mem+1 + sta SCREEN_DIST+1 + // [4] call malloc + // [36] phi from @4 to malloc [phi:@4->malloc] +malloc_from_b4: + // [36] phi (word) malloc::size#3 = (word) $3e8 [phi:@4->malloc#0] -- vwuz1=vwuc1 + lda #<$3e8 + sta malloc.size + lda #>$3e8 + sta malloc.size+1 + // [36] phi (byte*) heap_head#6 = (byte*) heap_head#1 [phi:@4->malloc#1] -- register_copy + jsr malloc + // [5] phi from @4 to @2 [phi:@4->@2] +b2_from_b4: + jmp b2 + // @2 +b2: + // [6] call malloc + // [36] phi from @2 to malloc [phi:@2->malloc] +malloc_from_b2: + // [36] phi (word) malloc::size#3 = (byte) $80*(const byte) SIZEOF_WORD [phi:@2->malloc#0] -- vwuz1=vbuc1 + lda #$80*SIZEOF_WORD + sta malloc.size + lda #0 + sta malloc.size+1 + // [36] phi (byte*) heap_head#6 = (byte*) heap_head#1 [phi:@2->malloc#1] -- register_copy + jsr malloc + jmp b5 + // @5 +b5: + // [7] (void*) BUCKET_SIZES#0 ← (void*)(byte*) malloc::mem#0 + // [8] phi from @5 to @3 [phi:@5->@3] +b3_from_b5: + jmp b3 + // @3 +b3: + // [9] call main + // [11] phi from @3 to main [phi:@3->main] +main_from_b3: + jsr main + // [10] phi from @3 to @end [phi:@3->@end] +bend_from_b3: + jmp bend + // @end +bend: + // main +main: { + // [12] call init_dist_screen + // [34] phi from main to init_dist_screen [phi:main->init_dist_screen] + init_dist_screen_from_main: + jsr init_dist_screen + // [13] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + jmp b1 + // main::@1 + b1: + // [14] call init_angle_screen + // [32] phi from main::@1 to init_angle_screen [phi:main::@1->init_angle_screen] + init_angle_screen_from_b1: + jsr init_angle_screen + // [15] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + b2_from_b1: + jmp b2 + // main::@2 + b2: + // [16] call init_buckets + // [18] phi from main::@2 to init_buckets [phi:main::@2->init_buckets] + init_buckets_from_b2: + jsr init_buckets + jmp breturn + // main::@return + breturn: + // [17] return + rts +} + // init_buckets +init_buckets: { + .label dist = 2 + .label i1 = 4 + // [19] phi from init_buckets to init_buckets::@1 [phi:init_buckets->init_buckets::@1] + b1_from_init_buckets: + // [19] phi (byte) init_buckets::i#2 = (byte) 0 [phi:init_buckets->init_buckets::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp b1 + // Init bucket sizes to 0 + // [19] phi from init_buckets::@1 to init_buckets::@1 [phi:init_buckets::@1->init_buckets::@1] + b1_from_b1: + // [19] phi (byte) init_buckets::i#2 = (byte) init_buckets::i#1 [phi:init_buckets::@1->init_buckets::@1#0] -- register_copy + jmp b1 + // init_buckets::@1 + b1: + // [20] (byte~) init_buckets::$2 ← (byte) init_buckets::i#2 << (byte) 1 -- vbuaa=vbuxx_rol_1 + txa + asl + // [21] *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$2) ← (byte) 0 -- pwuz1_derefidx_vbuaa=vwuc1 + tay + lda #<0 + sta (BUCKET_SIZES),y + iny + lda #>0 + sta (BUCKET_SIZES),y + // [22] (byte) init_buckets::i#1 ← ++ (byte) init_buckets::i#2 -- vbuxx=_inc_vbuxx + inx + // [23] if((byte) init_buckets::i#1!=(byte) $80) goto init_buckets::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #$80 + bne b1_from_b1 + jmp b2 + // init_buckets::@2 + b2: + // [24] (byte*) init_buckets::dist#0 ← (byte*)(void*) SCREEN_DIST#0 + // first find bucket sizes - by counting number of chars with each distance value + // [25] phi from init_buckets::@2 to init_buckets::@3 [phi:init_buckets::@2->init_buckets::@3] + b3_from_b2: + // [25] phi (word) init_buckets::i1#2 = (word) 0 [phi:init_buckets::@2->init_buckets::@3#0] -- vwuz1=vwuc1 + lda #<0 + sta i1 + lda #>0 + sta i1+1 + // [25] phi (byte*) init_buckets::dist#2 = (byte*) init_buckets::dist#0 [phi:init_buckets::@2->init_buckets::@3#1] -- register_copy + jmp b3 + // [25] phi from init_buckets::@3 to init_buckets::@3 [phi:init_buckets::@3->init_buckets::@3] + b3_from_b3: + // [25] phi (word) init_buckets::i1#2 = (word) init_buckets::i1#1 [phi:init_buckets::@3->init_buckets::@3#0] -- register_copy + // [25] phi (byte*) init_buckets::dist#2 = (byte*) init_buckets::dist#1 [phi:init_buckets::@3->init_buckets::@3#1] -- register_copy + jmp b3 + // init_buckets::@3 + b3: + // [26] (byte~) init_buckets::$3 ← *((byte*) init_buckets::dist#2) << (byte) 1 -- vbuaa=_deref_pbuz1_rol_1 + ldy #0 + lda (dist),y + asl + // [27] *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$3) ← ++ *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$3) -- pwuz1_derefidx_vbuaa=_inc_pwuz1_derefidx_vbuaa + tay + lda (BUCKET_SIZES),y + clc + adc #1 + sta (BUCKET_SIZES),y + bne !+ + iny + lda (BUCKET_SIZES),y + adc #0 + sta (BUCKET_SIZES),y + !: + // [28] (byte*) init_buckets::dist#1 ← ++ (byte*) init_buckets::dist#2 -- pbuz1=_inc_pbuz1 + inc dist + bne !+ + inc dist+1 + !: + // [29] (word) init_buckets::i1#1 ← ++ (word) init_buckets::i1#2 -- vwuz1=_inc_vwuz1 + inc i1 + bne !+ + inc i1+1 + !: + // [30] if((word) init_buckets::i1#1!=(word) $3e8) goto init_buckets::@3 -- vwuz1_neq_vwuc1_then_la1 + lda i1+1 + cmp #>$3e8 + bne b3_from_b3 + lda i1 + cmp #<$3e8 + bne b3_from_b3 + jmp breturn + // init_buckets::@return + breturn: + // [31] return + rts +} + // init_angle_screen +// Populates 1000 bytes (a screen) with values representing the angle to the center. +// Utilizes symmetry around the center +init_angle_screen: { + jmp breturn + // init_angle_screen::@return + breturn: + // [33] return + rts +} + // init_dist_screen +// Populates 1000 bytes (a screen) with values representing the distance to the center. +// The actual value stored is distance*2 to increase precision +init_dist_screen: { + jmp breturn + // init_dist_screen::@return + breturn: + // [35] return + rts +} + // malloc +// Allocates a block of size bytes of memory, returning a pointer to the beginning of the block. +// The content of the newly allocated block of memory is not initialized, remaining with indeterminate values. +// malloc(word zeropage(8) size) +malloc: { + .label mem = $a + .label size = 8 + // [37] (byte*) malloc::mem#0 ← (byte*) heap_head#6 -- pbuz1=pbuz2 + lda heap_head + sta mem + lda heap_head+1 + sta mem+1 + // [38] (byte*) heap_head#1 ← (byte*) heap_head#6 + (word) malloc::size#3 -- pbuz1=pbuz1_plus_vwuz2 + lda heap_head + clc + adc size + sta heap_head + lda heap_head+1 + adc size+1 + sta heap_head+1 + jmp breturn + // malloc::@return + breturn: + // [39] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp b4 +Removing instruction jmp b2 +Removing instruction jmp b5 +Removing instruction jmp b3 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp b2 +Removing instruction jmp breturn +Removing instruction jmp b1 +Removing instruction jmp b2 +Removing instruction jmp b3 +Removing instruction jmp breturn +Removing instruction jmp breturn +Removing instruction jmp breturn +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction lda #>0 +Removing instruction lda #>0 +Succesful ASM optimization Pass5UnnecesaryLoadElimination +Replacing label b1_from_b1 with b1 +Replacing label b3_from_b3 with b3 +Replacing label b3_from_b3 with b3 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction malloc_from_b1: +Removing instruction b2_from_b4: +Removing instruction malloc_from_b2: +Removing instruction b5: +Removing instruction b3_from_b5: +Removing instruction main_from_b3: +Removing instruction bend_from_b3: +Removing instruction b1_from_main: +Removing instruction init_angle_screen_from_b1: +Removing instruction b2_from_b1: +Removing instruction init_buckets_from_b2: +Removing instruction b1_from_b1: +Removing instruction b3_from_b3: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction b4: +Removing instruction malloc_from_b4: +Removing instruction b2: +Removing instruction b3: +Removing instruction bend: +Removing instruction init_dist_screen_from_main: +Removing instruction b1: +Removing instruction b2: +Removing instruction breturn: +Removing instruction b1_from_init_buckets: +Removing instruction b2: +Removing instruction b3_from_b2: +Removing instruction breturn: +Removing instruction breturn: +Removing instruction breturn: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Adding RTS to root block +Succesful ASM optimization Pass5AddMainRts +Removing instruction jmp b1 +Removing instruction jmp b3 +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction lda #<0 +Succesful ASM optimization Pass5UnnecesaryLoadElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @2 +(label) @3 +(label) @4 +(label) @5 +(label) @begin +(label) @end +(word*) BUCKET_SIZES +(void*) BUCKET_SIZES#0 BUCKET_SIZES zp ZP_WORD:10 0.1 +(byte*) HEAP_START +(const byte*) HEAP_START#0 HEAP_START = (byte*) 49152 +(byte*) SCREEN_ANGLE +(byte*) SCREEN_DIST +(void*) SCREEN_DIST#0 SCREEN_DIST zp ZP_WORD:2 0.11764705882352941 +(const byte) SIZEOF_WORD SIZEOF_WORD = (byte) 2 +(byte*) heap_head +(byte*) heap_head#1 heap_head zp ZP_WORD:6 1.0 +(byte*) heap_head#6 heap_head zp ZP_WORD:6 4.0 +(void()) init_angle_screen((byte*) init_angle_screen::screen) +(label) init_angle_screen::@return +(byte*) init_angle_screen::screen +(void()) init_buckets() +(byte~) init_buckets::$2 reg byte a 22.0 +(byte~) init_buckets::$3 reg byte a 33.0 +(label) init_buckets::@1 +(label) init_buckets::@2 +(label) init_buckets::@3 +(label) init_buckets::@return +(byte*) init_buckets::dist +(byte*) init_buckets::dist#0 dist zp ZP_WORD:2 4.0 +(byte*) init_buckets::dist#1 dist zp ZP_WORD:2 7.333333333333333 +(byte*) init_buckets::dist#2 dist zp ZP_WORD:2 11.666666666666666 +(byte) init_buckets::i +(byte) init_buckets::i#1 reg byte x 16.5 +(byte) init_buckets::i#2 reg byte x 11.0 +(word) init_buckets::i1 +(word) init_buckets::i1#1 i1 zp ZP_WORD:4 16.5 +(word) init_buckets::i1#2 i1 zp ZP_WORD:4 5.5 +(void()) init_dist_screen((byte*) init_dist_screen::screen) +(label) init_dist_screen::@return +(byte*) init_dist_screen::screen +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(void*()) malloc((word) malloc::size) +(label) malloc::@return +(byte*) malloc::mem +(byte*) malloc::mem#0 mem zp ZP_WORD:10 0.4 +(void*) malloc::return +(word) malloc::size +(word) malloc::size#3 size zp ZP_WORD:8 1.0 + +reg byte x [ init_buckets::i#2 init_buckets::i#1 ] +zp ZP_WORD:2 [ init_buckets::dist#2 init_buckets::dist#0 init_buckets::dist#1 SCREEN_DIST#0 ] +zp ZP_WORD:4 [ init_buckets::i1#2 init_buckets::i1#1 ] +zp ZP_WORD:6 [ heap_head#6 heap_head#1 ] +zp ZP_WORD:8 [ malloc::size#3 ] +zp ZP_WORD:10 [ BUCKET_SIZES#0 malloc::mem#0 ] +reg byte a [ init_buckets::$2 ] +reg byte a [ init_buckets::$3 ] + + +FINAL ASSEMBLER +Score: 1377 + + // File Comments +// Fill screen using a spiral based on distance-to-center / angle-to-center +// Utilizes a bucket sort for identifying the minimum angle/distance + // Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .const SIZEOF_WORD = 2 + // Start of the heap used by malloc() + .label HEAP_START = $c000 + .label heap_head = 6 + // Screen containing distance to center + .label SCREEN_DIST = 2 + // Array containing the bucket size for each of the 256 buckets + .label BUCKET_SIZES = $a + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // malloc(1000) + // [2] call malloc + // [36] phi from @1 to malloc [phi:@1->malloc] + // [36] phi (word) malloc::size#3 = (word) $3e8 [phi:@1->malloc#0] -- vwuz1=vwuc1 + lda #<$3e8 + sta malloc.size + lda #>$3e8 + sta malloc.size+1 + // [36] phi (byte*) heap_head#6 = (const byte*) HEAP_START#0 [phi:@1->malloc#1] -- pbuz1=pbuc1 + lda #<HEAP_START + sta heap_head + lda #>HEAP_START + sta heap_head+1 + jsr malloc + // @4 + // malloc(1000) + // [3] (void*) SCREEN_DIST#0 ← (void*)(byte*) malloc::mem#0 -- pvoz1=pvoz2 + lda malloc.mem + sta SCREEN_DIST + lda malloc.mem+1 + sta SCREEN_DIST+1 + // [4] call malloc + // [36] phi from @4 to malloc [phi:@4->malloc] + // [36] phi (word) malloc::size#3 = (word) $3e8 [phi:@4->malloc#0] -- vwuz1=vwuc1 + lda #<$3e8 + sta malloc.size + lda #>$3e8 + sta malloc.size+1 + // [36] phi (byte*) heap_head#6 = (byte*) heap_head#1 [phi:@4->malloc#1] -- register_copy + jsr malloc + // [5] phi from @4 to @2 [phi:@4->@2] + // @2 + // malloc(0x80*sizeof(word)) + // [6] call malloc + // [36] phi from @2 to malloc [phi:@2->malloc] + // [36] phi (word) malloc::size#3 = (byte) $80*(const byte) SIZEOF_WORD [phi:@2->malloc#0] -- vwuz1=vbuc1 + lda #$80*SIZEOF_WORD + sta malloc.size + lda #0 + sta malloc.size+1 + // [36] phi (byte*) heap_head#6 = (byte*) heap_head#1 [phi:@2->malloc#1] -- register_copy + jsr malloc + // @5 + // malloc(0x80*sizeof(word)) + // [7] (void*) BUCKET_SIZES#0 ← (void*)(byte*) malloc::mem#0 + // [8] phi from @5 to @3 [phi:@5->@3] + // @3 + // [9] call main + // [11] phi from @3 to main [phi:@3->main] + jsr main + rts + // [10] phi from @3 to @end [phi:@3->@end] + // @end + // main +main: { + // init_dist_screen(SCREEN_DIST) + // [12] call init_dist_screen + // [34] phi from main to init_dist_screen [phi:main->init_dist_screen] + jsr init_dist_screen + // [13] phi from main to main::@1 [phi:main->main::@1] + // main::@1 + // init_angle_screen(SCREEN_ANGLE) + // [14] call init_angle_screen + // [32] phi from main::@1 to init_angle_screen [phi:main::@1->init_angle_screen] + jsr init_angle_screen + // [15] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + // main::@2 + // init_buckets() + // [16] call init_buckets + // [18] phi from main::@2 to init_buckets [phi:main::@2->init_buckets] + jsr init_buckets + // main::@return + // } + // [17] return + rts +} + // init_buckets +init_buckets: { + .label dist = 2 + .label i1 = 4 + // [19] phi from init_buckets to init_buckets::@1 [phi:init_buckets->init_buckets::@1] + // [19] phi (byte) init_buckets::i#2 = (byte) 0 [phi:init_buckets->init_buckets::@1#0] -- vbuxx=vbuc1 + ldx #0 + // Init bucket sizes to 0 + // [19] phi from init_buckets::@1 to init_buckets::@1 [phi:init_buckets::@1->init_buckets::@1] + // [19] phi (byte) init_buckets::i#2 = (byte) init_buckets::i#1 [phi:init_buckets::@1->init_buckets::@1#0] -- register_copy + // init_buckets::@1 + b1: + // BUCKET_SIZES[i]=0 + // [20] (byte~) init_buckets::$2 ← (byte) init_buckets::i#2 << (byte) 1 -- vbuaa=vbuxx_rol_1 + txa + asl + // [21] *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$2) ← (byte) 0 -- pwuz1_derefidx_vbuaa=vwuc1 + tay + lda #<0 + sta (BUCKET_SIZES),y + iny + sta (BUCKET_SIZES),y + // for(byte i:0..0x7f) + // [22] (byte) init_buckets::i#1 ← ++ (byte) init_buckets::i#2 -- vbuxx=_inc_vbuxx + inx + // [23] if((byte) init_buckets::i#1!=(byte) $80) goto init_buckets::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #$80 + bne b1 + // init_buckets::@2 + // dist = SCREEN_DIST + // [24] (byte*) init_buckets::dist#0 ← (byte*)(void*) SCREEN_DIST#0 + // first find bucket sizes - by counting number of chars with each distance value + // [25] phi from init_buckets::@2 to init_buckets::@3 [phi:init_buckets::@2->init_buckets::@3] + // [25] phi (word) init_buckets::i1#2 = (word) 0 [phi:init_buckets::@2->init_buckets::@3#0] -- vwuz1=vwuc1 + sta i1 + sta i1+1 + // [25] phi (byte*) init_buckets::dist#2 = (byte*) init_buckets::dist#0 [phi:init_buckets::@2->init_buckets::@3#1] -- register_copy + // [25] phi from init_buckets::@3 to init_buckets::@3 [phi:init_buckets::@3->init_buckets::@3] + // [25] phi (word) init_buckets::i1#2 = (word) init_buckets::i1#1 [phi:init_buckets::@3->init_buckets::@3#0] -- register_copy + // [25] phi (byte*) init_buckets::dist#2 = (byte*) init_buckets::dist#1 [phi:init_buckets::@3->init_buckets::@3#1] -- register_copy + // init_buckets::@3 + b3: + // BUCKET_SIZES[*dist]++; + // [26] (byte~) init_buckets::$3 ← *((byte*) init_buckets::dist#2) << (byte) 1 -- vbuaa=_deref_pbuz1_rol_1 + ldy #0 + lda (dist),y + asl + // [27] *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$3) ← ++ *((word*)(void*) BUCKET_SIZES#0 + (byte~) init_buckets::$3) -- pwuz1_derefidx_vbuaa=_inc_pwuz1_derefidx_vbuaa + tay + lda (BUCKET_SIZES),y + clc + adc #1 + sta (BUCKET_SIZES),y + bne !+ + iny + lda (BUCKET_SIZES),y + adc #0 + sta (BUCKET_SIZES),y + !: + // dist++; + // [28] (byte*) init_buckets::dist#1 ← ++ (byte*) init_buckets::dist#2 -- pbuz1=_inc_pbuz1 + inc dist + bne !+ + inc dist+1 + !: + // for( word i:0..999 ) + // [29] (word) init_buckets::i1#1 ← ++ (word) init_buckets::i1#2 -- vwuz1=_inc_vwuz1 + inc i1 + bne !+ + inc i1+1 + !: + // [30] if((word) init_buckets::i1#1!=(word) $3e8) goto init_buckets::@3 -- vwuz1_neq_vwuc1_then_la1 + lda i1+1 + cmp #>$3e8 + bne b3 + lda i1 + cmp #<$3e8 + bne b3 + // init_buckets::@return + // } + // [31] return + rts +} + // init_angle_screen +// Populates 1000 bytes (a screen) with values representing the angle to the center. +// Utilizes symmetry around the center +init_angle_screen: { + // init_angle_screen::@return + // [33] return + rts +} + // init_dist_screen +// Populates 1000 bytes (a screen) with values representing the distance to the center. +// The actual value stored is distance*2 to increase precision +init_dist_screen: { + // init_dist_screen::@return + // [35] return + rts +} + // malloc +// Allocates a block of size bytes of memory, returning a pointer to the beginning of the block. +// The content of the newly allocated block of memory is not initialized, remaining with indeterminate values. +// malloc(word zeropage(8) size) +malloc: { + .label mem = $a + .label size = 8 + // mem = heap_head + // [37] (byte*) malloc::mem#0 ← (byte*) heap_head#6 -- pbuz1=pbuz2 + lda heap_head + sta mem + lda heap_head+1 + sta mem+1 + // heap_head+= size + // [38] (byte*) heap_head#1 ← (byte*) heap_head#6 + (word) malloc::size#3 -- pbuz1=pbuz1_plus_vwuz2 + lda heap_head + clc + adc size + sta heap_head + lda heap_head+1 + adc size+1 + sta heap_head+1 + // malloc::@return + // } + // [39] return + rts +} + // File Data + diff --git a/src/test/ref/screen-show-spiral-buckets.sym b/src/test/ref/screen-show-spiral-buckets.sym new file mode 100644 index 000000000..b2fea696a --- /dev/null +++ b/src/test/ref/screen-show-spiral-buckets.sym @@ -0,0 +1,61 @@ +(label) @1 +(label) @2 +(label) @3 +(label) @4 +(label) @5 +(label) @begin +(label) @end +(word*) BUCKET_SIZES +(void*) BUCKET_SIZES#0 BUCKET_SIZES zp ZP_WORD:10 0.1 +(byte*) HEAP_START +(const byte*) HEAP_START#0 HEAP_START = (byte*) 49152 +(byte*) SCREEN_ANGLE +(byte*) SCREEN_DIST +(void*) SCREEN_DIST#0 SCREEN_DIST zp ZP_WORD:2 0.11764705882352941 +(const byte) SIZEOF_WORD SIZEOF_WORD = (byte) 2 +(byte*) heap_head +(byte*) heap_head#1 heap_head zp ZP_WORD:6 1.0 +(byte*) heap_head#6 heap_head zp ZP_WORD:6 4.0 +(void()) init_angle_screen((byte*) init_angle_screen::screen) +(label) init_angle_screen::@return +(byte*) init_angle_screen::screen +(void()) init_buckets() +(byte~) init_buckets::$2 reg byte a 22.0 +(byte~) init_buckets::$3 reg byte a 33.0 +(label) init_buckets::@1 +(label) init_buckets::@2 +(label) init_buckets::@3 +(label) init_buckets::@return +(byte*) init_buckets::dist +(byte*) init_buckets::dist#0 dist zp ZP_WORD:2 4.0 +(byte*) init_buckets::dist#1 dist zp ZP_WORD:2 7.333333333333333 +(byte*) init_buckets::dist#2 dist zp ZP_WORD:2 11.666666666666666 +(byte) init_buckets::i +(byte) init_buckets::i#1 reg byte x 16.5 +(byte) init_buckets::i#2 reg byte x 11.0 +(word) init_buckets::i1 +(word) init_buckets::i1#1 i1 zp ZP_WORD:4 16.5 +(word) init_buckets::i1#2 i1 zp ZP_WORD:4 5.5 +(void()) init_dist_screen((byte*) init_dist_screen::screen) +(label) init_dist_screen::@return +(byte*) init_dist_screen::screen +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(void*()) malloc((word) malloc::size) +(label) malloc::@return +(byte*) malloc::mem +(byte*) malloc::mem#0 mem zp ZP_WORD:10 0.4 +(void*) malloc::return +(word) malloc::size +(word) malloc::size#3 size zp ZP_WORD:8 1.0 + +reg byte x [ init_buckets::i#2 init_buckets::i#1 ] +zp ZP_WORD:2 [ init_buckets::dist#2 init_buckets::dist#0 init_buckets::dist#1 SCREEN_DIST#0 ] +zp ZP_WORD:4 [ init_buckets::i1#2 init_buckets::i1#1 ] +zp ZP_WORD:6 [ heap_head#6 heap_head#1 ] +zp ZP_WORD:8 [ malloc::size#3 ] +zp ZP_WORD:10 [ BUCKET_SIZES#0 malloc::mem#0 ] +reg byte a [ init_buckets::$2 ] +reg byte a [ init_buckets::$3 ]