diff --git a/src/main/kc/target/mega65.ld b/src/main/kc/target/mega65.ld new file mode 100644 index 000000000..a896e33d1 --- /dev/null +++ b/src/main/kc/target/mega65.ld @@ -0,0 +1,10 @@ +.file [name="%O", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$2001] +.segmentdef Code [start=$2017] +.segmentdef Data [startAfter="Code"] +.segment Basic +.byte $0a, $20, $0a, $00, $fe, $02, $20, $30, $00 // 10 BANK 0 +.byte $15, $20, $14, $00, $9e, $20 // 20 SYS +.text toIntString(%E) // NNNN +.byte $00, $00, $00 // \ No newline at end of file diff --git a/src/main/kc/target/mega65.tgt b/src/main/kc/target/mega65.tgt new file mode 100644 index 000000000..ae9537c3a --- /dev/null +++ b/src/main/kc/target/mega65.tgt @@ -0,0 +1,9 @@ +{ + "extension": "prg", + "link": "mega65.ld", + "cpu": "MEGA45GS02", + "emulator": "xmega65 -prg", + "defines": { + "__MEGA65__": 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 1291e1025..861c6a101 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -192,6 +192,11 @@ public class TestPrograms { compileAndCompare("examples/nes-demo/nes-demo.c"); } + @Test + public void testMega65Hello() throws IOException, URISyntaxException { + compileAndCompare("examples/mega65/hello-mega65.c"); + } + @Test public void testAtariXlHello() throws IOException, URISyntaxException { compileAndCompare("examples/atarixl/helloxl.c"); diff --git a/src/test/kc/examples/mega65/hello-mega65.c b/src/test/kc/examples/mega65/hello-mega65.c new file mode 100644 index 000000000..abbda3c4d --- /dev/null +++ b/src/test/kc/examples/mega65/hello-mega65.c @@ -0,0 +1,15 @@ +// Hello World for MEGA 65 + +#pragma target(mega65) + +char * SCREEN = 0x0800; +char * COLORS = 0xd800; + +char MSG[] = "hello world!"; + +void main() { + for(char i=0;MSG[i];i++) { + SCREEN[i] = MSG[i]; + COLORS[i] = i; + } +} \ No newline at end of file diff --git a/src/test/ref/examples/mega65/hello-mega65.asm b/src/test/ref/examples/mega65/hello-mega65.asm new file mode 100644 index 000000000..77d2ee721 --- /dev/null +++ b/src/test/ref/examples/mega65/hello-mega65.asm @@ -0,0 +1,38 @@ +// Hello World for MEGA 65 +.cpu _45gs02 + .file [name="hello-mega65.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$2001] +.segmentdef Code [start=$2017] +.segmentdef Data [startAfter="Code"] +.segment Basic +.byte $0a, $20, $0a, $00, $fe, $02, $20, $30, $00 // 10 BANK 0 +.byte $15, $20, $14, $00, $9e, $20 // 20 SYS +.text toIntString(main) // NNNN +.byte $00, $00, $00 // + .label SCREEN = $800 + .label COLORS = $d800 +.segment Code +main: { + ldx #0 + __b1: + // for(char i=0;MSG[i];i++) + lda MSG,x + cmp #0 + bne __b2 + // } + rts + __b2: + // SCREEN[i] = MSG[i] + lda MSG,x + sta SCREEN,x + // COLORS[i] = i + txa + sta COLORS,x + // for(char i=0;MSG[i];i++) + inx + jmp __b1 +} +.segment Data + MSG: .text "hello world!" + .byte 0 diff --git a/src/test/ref/examples/mega65/hello-mega65.cfg b/src/test/ref/examples/mega65/hello-mega65.cfg new file mode 100644 index 000000000..535dfefdd --- /dev/null +++ b/src/test/ref/examples/mega65/hello-mega65.cfg @@ -0,0 +1,17 @@ + +(void()) main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@2 + [1] (byte) main::i#2 ← phi( main/(byte) 0 main::@2/(byte) main::i#1 ) + [2] if((byte) 0!=*((const byte*) MSG + (byte) main::i#2)) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [3] return + to:@return +main::@2: scope:[main] from main::@1 + [4] *((const byte*) SCREEN + (byte) main::i#2) ← *((const byte*) MSG + (byte) main::i#2) + [5] *((const byte*) COLORS + (byte) main::i#2) ← (byte) main::i#2 + [6] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 diff --git a/src/test/ref/examples/mega65/hello-mega65.log b/src/test/ref/examples/mega65/hello-mega65.log new file mode 100644 index 000000000..3d1102e08 --- /dev/null +++ b/src/test/ref/examples/mega65/hello-mega65.log @@ -0,0 +1,341 @@ +Inlined call call __init + +CONTROL FLOW GRAPH SSA + +(void()) main() +main: scope:[main] from __start::@1 + (byte) main::i#0 ← (byte) 0 + to:main::@1 +main::@1: scope:[main] from main main::@2 + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@2/(byte) main::i#1 ) + (bool~) main::$0 ← (number) 0 != *((const byte*) MSG + (byte) main::i#2) + if((bool~) main::$0) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + (byte) main::i#3 ← phi( main::@1/(byte) main::i#2 ) + *((const byte*) SCREEN + (byte) main::i#3) ← *((const byte*) MSG + (byte) main::i#3) + *((const byte*) COLORS + (byte) main::i#3) ← (byte) main::i#3 + (byte) main::i#1 ← ++ (byte) main::i#3 + to:main::@1 +main::@return: scope:[main] from main::@1 + return + to:@return + +(void()) __start() +__start: scope:[__start] from + to:__start::__init1 +__start::__init1: scope:[__start] from __start + to:__start::@1 +__start::@1: scope:[__start] from __start::__init1 + call main + to:__start::@2 +__start::@2: scope:[__start] from __start::@1 + to:__start::@return +__start::@return: scope:[__start] from __start::@2 + return + to:@return + +SYMBOL TABLE SSA +(const byte*) COLORS = (byte*)(number) $d800 +(const byte*) MSG[] = (byte*) "hello world!" +(const byte*) SCREEN = (byte*)(number) $800 +(void()) __start() +(label) __start::@1 +(label) __start::@2 +(label) __start::@return +(label) __start::__init1 +(void()) main() +(bool~) main::$0 +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 + +Adding number conversion cast (unumber) 0 in (bool~) main::$0 ← (number) 0 != *((const byte*) MSG + (byte) main::i#2) +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (byte*) 2048 +Simplifying constant pointer cast (byte*) 55296 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias main::i#2 = main::i#3 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$0 [3] if((byte) 0!=*((const byte*) MSG + (byte) main::i#2)) goto main::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte) main::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::__init1 +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@2 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Inlining constant with var siblings (const byte) main::i#0 +Constant inlined main::i#0 = (byte) 0 +Successful SSA optimization Pass2ConstantInlining +Adding NOP phi() at start of main +CALL GRAPH + +Created 1 initial phi equivalence classes +Coalesced [7] main::i#4 ← main::i#1 +Coalesced down to 1 phi equivalence classes +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +(void()) main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@2 + [1] (byte) main::i#2 ← phi( main/(byte) 0 main::@2/(byte) main::i#1 ) + [2] if((byte) 0!=*((const byte*) MSG + (byte) main::i#2)) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [3] return + to:@return +main::@2: scope:[main] from main::@1 + [4] *((const byte*) SCREEN + (byte) main::i#2) ← *((const byte*) MSG + (byte) main::i#2) + [5] *((const byte*) COLORS + (byte) main::i#2) ← (byte) main::i#2 + [6] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 + + +VARIABLE REGISTER WEIGHTS +(void()) main() +(byte) main::i +(byte) main::i#1 22.0 +(byte) main::i#2 19.25 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +Allocated zp[1]:2 [ main::i#2 main::i#1 ] + +INITIAL ASM +Target platform is mega65 / MEGA45GS02 + // File Comments +// Hello World for MEGA 65 + // Upstart +.cpu _45gs02 + .file [name="hello-mega65.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$2001] +.segmentdef Code [start=$2017] +.segmentdef Data [startAfter="Code"] +.segment Basic +.byte $0a, $20, $0a, $00, $fe, $02, $20, $30, $00 // 10 BANK 0 +.byte $15, $20, $14, $00, $9e, $20 // 20 SYS +.text toIntString(main) // NNNN +.byte $00, $00, $00 // + // Global Constants & labels + .label SCREEN = $800 + .label COLORS = $d800 +.segment Code + // main +main: { + .label i = 2 + // [1] phi from main to main::@1 [phi:main->main::@1] + __b1_from_main: + // [1] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp __b1 + // main::@1 + __b1: + // [2] if((byte) 0!=*((const byte*) MSG + (byte) main::i#2)) goto main::@2 -- vbuc1_neq_pbuc2_derefidx_vbuz1_then_la1 + lda #0 + ldy.z i + cmp MSG,y + bne __b2 + jmp __breturn + // main::@return + __breturn: + // [3] return + rts + // main::@2 + __b2: + // [4] *((const byte*) SCREEN + (byte) main::i#2) ← *((const byte*) MSG + (byte) main::i#2) -- pbuc1_derefidx_vbuz1=pbuc2_derefidx_vbuz1 + ldy.z i + lda MSG,y + sta SCREEN,y + // [5] *((const byte*) COLORS + (byte) main::i#2) ← (byte) main::i#2 -- pbuc1_derefidx_vbuz1=vbuz1 + ldy.z i + tya + sta COLORS,y + // [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc.z i + // [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + __b1_from___b2: + // [1] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp __b1 +} + // File Data +.segment Data + MSG: .text "hello world!" + .byte 0 + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [2] if((byte) 0!=*((const byte*) MSG + (byte) main::i#2)) goto main::@2 [ main::i#2 ] ( [ 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 [4] *((const byte*) SCREEN + (byte) main::i#2) ← *((const byte*) MSG + (byte) main::i#2) [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a +Statement [5] *((const byte*) COLORS + (byte) main::i#2) ← (byte) main::i#2 [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a +Statement [2] if((byte) 0!=*((const byte*) MSG + (byte) main::i#2)) goto main::@2 [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a +Statement [4] *((const byte*) SCREEN + (byte) main::i#2) ← *((const byte*) MSG + (byte) main::i#2) [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a +Statement [5] *((const byte*) COLORS + (byte) main::i#2) ← (byte) main::i#2 [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a +Potential registers zp[1]:2 [ main::i#2 main::i#1 ] : zp[1]:2 , reg byte x , reg byte y , reg byte z , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 41.25: zp[1]:2 [ main::i#2 main::i#1 ] +Uplift Scope [] + +Uplifting [main] best 391 combination reg byte x [ main::i#2 main::i#1 ] +Uplifting [] best 391 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Hello World for MEGA 65 + // Upstart +.cpu _45gs02 + .file [name="hello-mega65.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$2001] +.segmentdef Code [start=$2017] +.segmentdef Data [startAfter="Code"] +.segment Basic +.byte $0a, $20, $0a, $00, $fe, $02, $20, $30, $00 // 10 BANK 0 +.byte $15, $20, $14, $00, $9e, $20 // 20 SYS +.text toIntString(main) // NNNN +.byte $00, $00, $00 // + // Global Constants & labels + .label SCREEN = $800 + .label COLORS = $d800 +.segment Code + // main +main: { + // [1] phi from main to main::@1 [phi:main->main::@1] + __b1_from_main: + // [1] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp __b1 + // main::@1 + __b1: + // [2] if((byte) 0!=*((const byte*) MSG + (byte) main::i#2)) goto main::@2 -- vbuc1_neq_pbuc2_derefidx_vbuxx_then_la1 + lda MSG,x + cmp #0 + bne __b2 + jmp __breturn + // main::@return + __breturn: + // [3] return + rts + // main::@2 + __b2: + // [4] *((const byte*) SCREEN + (byte) main::i#2) ← *((const byte*) MSG + (byte) main::i#2) -- pbuc1_derefidx_vbuxx=pbuc2_derefidx_vbuxx + lda MSG,x + sta SCREEN,x + // [5] *((const byte*) COLORS + (byte) main::i#2) ← (byte) main::i#2 -- pbuc1_derefidx_vbuxx=vbuxx + txa + sta COLORS,x + // [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + // [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + __b1_from___b2: + // [1] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp __b1 +} + // File Data +.segment Data + MSG: .text "hello world!" + .byte 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __b1_from_main: +Removing instruction __breturn: +Removing instruction __b1_from___b2: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(const byte*) COLORS = (byte*) 55296 +(const byte*) MSG[] = (byte*) "hello world!" +(const byte*) SCREEN = (byte*) 2048 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 22.0 +(byte) main::i#2 reg byte x 19.25 + +reg byte x [ main::i#2 main::i#1 ] + + +FINAL ASSEMBLER +Score: 331 + + // File Comments +// Hello World for MEGA 65 + // Upstart +.cpu _45gs02 + .file [name="hello-mega65.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$2001] +.segmentdef Code [start=$2017] +.segmentdef Data [startAfter="Code"] +.segment Basic +.byte $0a, $20, $0a, $00, $fe, $02, $20, $30, $00 // 10 BANK 0 +.byte $15, $20, $14, $00, $9e, $20 // 20 SYS +.text toIntString(main) // NNNN +.byte $00, $00, $00 // + // Global Constants & labels + .label SCREEN = $800 + .label COLORS = $d800 +.segment Code + // main +main: { + // [1] phi from main to main::@1 [phi:main->main::@1] + // [1] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + // main::@1 + __b1: + // for(char i=0;MSG[i];i++) + // [2] if((byte) 0!=*((const byte*) MSG + (byte) main::i#2)) goto main::@2 -- vbuc1_neq_pbuc2_derefidx_vbuxx_then_la1 + lda MSG,x + cmp #0 + bne __b2 + // main::@return + // } + // [3] return + rts + // main::@2 + __b2: + // SCREEN[i] = MSG[i] + // [4] *((const byte*) SCREEN + (byte) main::i#2) ← *((const byte*) MSG + (byte) main::i#2) -- pbuc1_derefidx_vbuxx=pbuc2_derefidx_vbuxx + lda MSG,x + sta SCREEN,x + // COLORS[i] = i + // [5] *((const byte*) COLORS + (byte) main::i#2) ← (byte) main::i#2 -- pbuc1_derefidx_vbuxx=vbuxx + txa + sta COLORS,x + // for(char i=0;MSG[i];i++) + // [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + // [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + // [1] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp __b1 +} + // File Data +.segment Data + MSG: .text "hello world!" + .byte 0 + diff --git a/src/test/ref/examples/mega65/hello-mega65.sym b/src/test/ref/examples/mega65/hello-mega65.sym new file mode 100644 index 000000000..fabdff22a --- /dev/null +++ b/src/test/ref/examples/mega65/hello-mega65.sym @@ -0,0 +1,12 @@ +(const byte*) COLORS = (byte*) 55296 +(const byte*) MSG[] = (byte*) "hello world!" +(const byte*) SCREEN = (byte*) 2048 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 22.0 +(byte) main::i#2 reg byte x 19.25 + +reg byte x [ main::i#2 main::i#1 ]