From 83480c69dc3c527a25ed589d03130b7ba8a382d7 Mon Sep 17 00:00:00 2001
From: jespergravgaard <jesper@balmangravgaard.dk>
Date: Tue, 16 Jun 2020 23:30:38 +0200
Subject: [PATCH 1/2] Added strcpy() test.

---
 .../dk/camelot64/kickc/test/TestPrograms.java |   5 +
 src/test/kc/strcpy-0.c                        |  13 +
 src/test/ref/strcpy-0.asm                     |  57 ++
 src/test/ref/strcpy-0.cfg                     |  40 ++
 src/test/ref/strcpy-0.log                     | 598 ++++++++++++++++++
 src/test/ref/strcpy-0.sym                     |  26 +
 6 files changed, 739 insertions(+)
 create mode 100644 src/test/kc/strcpy-0.c
 create mode 100644 src/test/ref/strcpy-0.asm
 create mode 100644 src/test/ref/strcpy-0.cfg
 create mode 100644 src/test/ref/strcpy-0.log
 create mode 100644 src/test/ref/strcpy-0.sym

diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java
index 0a2a00403..30450165b 100644
--- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java
+++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java
@@ -42,6 +42,11 @@ public class TestPrograms {
    public TestPrograms() {
    }
 
+   @Test
+   public void testStrcpy0() throws IOException, URISyntaxException {
+      compileAndCompare("strcpy-0.c");
+   }
+
    @Test
    public void testStaticInitCode() throws IOException, URISyntaxException {
       compileAndCompare("static-init-code.c");
diff --git a/src/test/kc/strcpy-0.c b/src/test/kc/strcpy-0.c
new file mode 100644
index 000000000..ad3a188b5
--- /dev/null
+++ b/src/test/kc/strcpy-0.c
@@ -0,0 +1,13 @@
+
+char* dst1 = (char*)0x0400;
+char* dst2 = (char*)0x0428;
+
+void str_cpy( char *dst, char const *src ) {
+   while ( *dst++ = *src++ ) {}
+}
+
+void main() {
+    str_cpy(dst1, "hello");
+    str_cpy(dst2, "world");
+}
+
diff --git a/src/test/ref/strcpy-0.asm b/src/test/ref/strcpy-0.asm
new file mode 100644
index 000000000..863fe0a95
--- /dev/null
+++ b/src/test/ref/strcpy-0.asm
@@ -0,0 +1,57 @@
+.pc = $801 "Basic"
+:BasicUpstart(main)
+.pc = $80d "Program"
+  .label dst1 = $400
+  .label dst2 = $428
+main: {
+    // str_cpy(dst1, "hello")
+    lda #<dst1
+    sta.z str_cpy.dst
+    lda #>dst1
+    sta.z str_cpy.dst+1
+    lda #<src
+    sta.z str_cpy.src
+    lda #>src
+    sta.z str_cpy.src+1
+    jsr str_cpy
+    // str_cpy(dst2, "world")
+    lda #<dst2
+    sta.z str_cpy.dst
+    lda #>dst2
+    sta.z str_cpy.dst+1
+    lda #<src1
+    sta.z str_cpy.src
+    lda #>src1
+    sta.z str_cpy.src+1
+    jsr str_cpy
+    // }
+    rts
+    src: .text "hello"
+    .byte 0
+    src1: .text "world"
+    .byte 0
+}
+// str_cpy(byte* zp(4) dst, byte* zp(2) src)
+str_cpy: {
+    .label dst = 4
+    .label src = 2
+  __b1:
+    // *dst++ = *src++
+    ldy #0
+    lda (src),y
+    sta (dst),y
+    // while ( *dst++ = *src++ )
+    lda (dst),y
+    inc.z dst
+    bne !+
+    inc.z dst+1
+  !:
+    inc.z src
+    bne !+
+    inc.z src+1
+  !:
+    cmp #0
+    bne __b1
+    // }
+    rts
+}
diff --git a/src/test/ref/strcpy-0.cfg b/src/test/ref/strcpy-0.cfg
new file mode 100644
index 000000000..a34ee0f58
--- /dev/null
+++ b/src/test/ref/strcpy-0.cfg
@@ -0,0 +1,40 @@
+@begin: scope:[]  from
+  [0] phi()
+  to:@1
+@1: scope:[]  from @begin
+  [1] phi()
+  [2] call main 
+  to:@end
+@end: scope:[]  from @1
+  [3] phi()
+
+(void()) main()
+main: scope:[main]  from @1
+  [4] phi()
+  [5] call str_cpy 
+  to:main::@1
+main::@1: scope:[main]  from main
+  [6] phi()
+  [7] call str_cpy 
+  to:main::@return
+main::@return: scope:[main]  from main::@1
+  [8] return 
+  to:@return
+
+(void()) str_cpy((byte*) str_cpy::dst , (to_nomodify byte*) str_cpy::src)
+str_cpy: scope:[str_cpy]  from main main::@1
+  [9] (byte*) str_cpy::dst#4 ← phi( main/(const byte*) dst1 main::@1/(const byte*) dst2 )
+  [9] (to_nomodify byte*) str_cpy::src#4 ← phi( main/(const byte*) main::src main::@1/(const byte*) main::src1 )
+  to:str_cpy::@1
+str_cpy::@1: scope:[str_cpy]  from str_cpy str_cpy::@1
+  [10] (byte*) str_cpy::dst#3 ← phi( str_cpy/(byte*) str_cpy::dst#4 str_cpy::@1/(byte*) str_cpy::dst#0 )
+  [10] (to_nomodify byte*) str_cpy::src#3 ← phi( str_cpy/(to_nomodify byte*) str_cpy::src#4 str_cpy::@1/(to_nomodify byte*) str_cpy::src#0 )
+  [11] *((byte*) str_cpy::dst#3) ← *((to_nomodify byte*) str_cpy::src#3)
+  [12] (byte~) str_cpy::$0 ← *((byte*) str_cpy::dst#3)
+  [13] (byte*) str_cpy::dst#0 ← ++ (byte*) str_cpy::dst#3
+  [14] (to_nomodify byte*) str_cpy::src#0 ← ++ (to_nomodify byte*) str_cpy::src#3
+  [15] if((byte) 0!=(byte~) str_cpy::$0) goto str_cpy::@1
+  to:str_cpy::@return
+str_cpy::@return: scope:[str_cpy]  from str_cpy::@1
+  [16] return 
+  to:@return
diff --git a/src/test/ref/strcpy-0.log b/src/test/ref/strcpy-0.log
new file mode 100644
index 000000000..f326ccc6e
--- /dev/null
+++ b/src/test/ref/strcpy-0.log
@@ -0,0 +1,598 @@
+
+CONTROL FLOW GRAPH SSA
+@begin: scope:[]  from
+  to:@1
+
+(void()) str_cpy((byte*) str_cpy::dst , (to_nomodify byte*) str_cpy::src)
+str_cpy: scope:[str_cpy]  from main main::@1
+  (byte*) str_cpy::dst#4 ← phi( main/(byte*) str_cpy::dst#1 main::@1/(byte*) str_cpy::dst#2 )
+  (to_nomodify byte*) str_cpy::src#4 ← phi( main/(to_nomodify byte*) str_cpy::src#1 main::@1/(to_nomodify byte*) str_cpy::src#2 )
+  to:str_cpy::@1
+str_cpy::@1: scope:[str_cpy]  from str_cpy str_cpy::@1
+  (byte*) str_cpy::dst#3 ← phi( str_cpy/(byte*) str_cpy::dst#4 str_cpy::@1/(byte*) str_cpy::dst#0 )
+  (to_nomodify byte*) str_cpy::src#3 ← phi( str_cpy/(to_nomodify byte*) str_cpy::src#4 str_cpy::@1/(to_nomodify byte*) str_cpy::src#0 )
+  *((byte*) str_cpy::dst#3) ← *((to_nomodify byte*) str_cpy::src#3)
+  (byte~) str_cpy::$0 ← *((byte*) str_cpy::dst#3)
+  (byte*) str_cpy::dst#0 ← ++ (byte*) str_cpy::dst#3
+  (to_nomodify byte*) str_cpy::src#0 ← ++ (to_nomodify byte*) str_cpy::src#3
+  (bool~) str_cpy::$1 ← (number) 0 != (byte~) str_cpy::$0
+  if((bool~) str_cpy::$1) goto str_cpy::@1
+  to:str_cpy::@return
+str_cpy::@return: scope:[str_cpy]  from str_cpy::@1
+  return 
+  to:@return
+
+(void()) main()
+main: scope:[main]  from @1
+  (byte*) str_cpy::dst#1 ← (const byte*) dst1
+  (to_nomodify byte*) str_cpy::src#1 ← (const byte*) main::src
+  call str_cpy 
+  to:main::@1
+main::@1: scope:[main]  from main
+  (byte*) str_cpy::dst#2 ← (const byte*) dst2
+  (to_nomodify byte*) str_cpy::src#2 ← (const byte*) main::src1
+  call str_cpy 
+  to:main::@2
+main::@2: scope:[main]  from main::@1
+  to:main::@return
+main::@return: scope:[main]  from main::@2
+  return 
+  to:@return
+@1: scope:[]  from @begin
+  call main 
+  to:@2
+@2: scope:[]  from @1
+  to:@end
+@end: scope:[]  from @2
+
+SYMBOL TABLE SSA
+(label) @1
+(label) @2
+(label) @begin
+(label) @end
+(const byte*) dst1 = (byte*)(number) $400
+(const byte*) dst2 = (byte*)(number) $428
+(void()) main()
+(label) main::@1
+(label) main::@2
+(label) main::@return
+(const byte*) main::src[(byte) 6]  = (byte*) "hello"
+(const byte*) main::src1[(byte) 6]  = (byte*) "world"
+(void()) str_cpy((byte*) str_cpy::dst , (to_nomodify byte*) str_cpy::src)
+(byte~) str_cpy::$0
+(bool~) str_cpy::$1
+(label) str_cpy::@1
+(label) str_cpy::@return
+(byte*) str_cpy::dst
+(byte*) str_cpy::dst#0
+(byte*) str_cpy::dst#1
+(byte*) str_cpy::dst#2
+(byte*) str_cpy::dst#3
+(byte*) str_cpy::dst#4
+(to_nomodify byte*) str_cpy::src
+(to_nomodify byte*) str_cpy::src#0
+(to_nomodify byte*) str_cpy::src#1
+(to_nomodify byte*) str_cpy::src#2
+(to_nomodify byte*) str_cpy::src#3
+(to_nomodify byte*) str_cpy::src#4
+
+Adding number conversion cast (unumber) 0 in (bool~) str_cpy::$1 ← (number) 0 != (byte~) str_cpy::$0
+Successful SSA optimization PassNAddNumberTypeConversions
+Simplifying constant pointer cast (byte*) 1024
+Simplifying constant pointer cast (byte*) 1064
+Simplifying constant integer cast 0
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (byte) 0
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Simple Condition (bool~) str_cpy::$1 [7] if((byte) 0!=(byte~) str_cpy::$0) goto str_cpy::@1
+Successful SSA optimization Pass2ConditionalJumpSimplification
+Constant (const byte*) str_cpy::dst#1 = dst1
+Constant (const to_nomodify byte*) str_cpy::src#1 = main::src
+Constant (const byte*) str_cpy::dst#2 = dst2
+Constant (const to_nomodify byte*) str_cpy::src#2 = main::src1
+Successful SSA optimization Pass2ConstantIdentification
+Inlining constant with var siblings (const byte*) str_cpy::dst#1
+Inlining constant with var siblings (const to_nomodify byte*) str_cpy::src#1
+Inlining constant with var siblings (const byte*) str_cpy::dst#2
+Inlining constant with var siblings (const to_nomodify byte*) str_cpy::src#2
+Constant inlined str_cpy::src#1 = (const byte*) main::src
+Constant inlined str_cpy::src#2 = (const byte*) main::src1
+Constant inlined str_cpy::dst#1 = (const byte*) dst1
+Constant inlined str_cpy::dst#2 = (const byte*) dst2
+Successful SSA optimization Pass2ConstantInlining
+Added new block during phi lifting str_cpy::@2(between str_cpy::@1 and str_cpy::@1)
+Adding NOP phi() at start of @begin
+Adding NOP phi() at start of @1
+Adding NOP phi() at start of @2
+Adding NOP phi() at start of @end
+Adding NOP phi() at start of main
+Adding NOP phi() at start of main::@1
+Adding NOP phi() at start of main::@2
+CALL GRAPH
+Calls in [] to main:2 
+Calls in [main] to str_cpy:6 str_cpy:8 
+
+Created 4 initial phi equivalence classes
+Coalesced [12] str_cpy::src#5 ← str_cpy::src#4
+Coalesced [13] str_cpy::dst#5 ← str_cpy::dst#4
+Coalesced [21] str_cpy::src#6 ← str_cpy::src#0
+Coalesced [22] str_cpy::dst#6 ← str_cpy::dst#0
+Coalesced down to 2 phi equivalence classes
+Culled Empty Block (label) @2
+Culled Empty Block (label) main::@2
+Culled Empty Block (label) str_cpy::@2
+Adding NOP phi() at start of @begin
+Adding NOP phi() at start of @1
+Adding NOP phi() at start of @end
+Adding NOP phi() at start of main
+Adding NOP phi() at start of main::@1
+
+FINAL CONTROL FLOW GRAPH
+@begin: scope:[]  from
+  [0] phi()
+  to:@1
+@1: scope:[]  from @begin
+  [1] phi()
+  [2] call main 
+  to:@end
+@end: scope:[]  from @1
+  [3] phi()
+
+(void()) main()
+main: scope:[main]  from @1
+  [4] phi()
+  [5] call str_cpy 
+  to:main::@1
+main::@1: scope:[main]  from main
+  [6] phi()
+  [7] call str_cpy 
+  to:main::@return
+main::@return: scope:[main]  from main::@1
+  [8] return 
+  to:@return
+
+(void()) str_cpy((byte*) str_cpy::dst , (to_nomodify byte*) str_cpy::src)
+str_cpy: scope:[str_cpy]  from main main::@1
+  [9] (byte*) str_cpy::dst#4 ← phi( main/(const byte*) dst1 main::@1/(const byte*) dst2 )
+  [9] (to_nomodify byte*) str_cpy::src#4 ← phi( main/(const byte*) main::src main::@1/(const byte*) main::src1 )
+  to:str_cpy::@1
+str_cpy::@1: scope:[str_cpy]  from str_cpy str_cpy::@1
+  [10] (byte*) str_cpy::dst#3 ← phi( str_cpy/(byte*) str_cpy::dst#4 str_cpy::@1/(byte*) str_cpy::dst#0 )
+  [10] (to_nomodify byte*) str_cpy::src#3 ← phi( str_cpy/(to_nomodify byte*) str_cpy::src#4 str_cpy::@1/(to_nomodify byte*) str_cpy::src#0 )
+  [11] *((byte*) str_cpy::dst#3) ← *((to_nomodify byte*) str_cpy::src#3)
+  [12] (byte~) str_cpy::$0 ← *((byte*) str_cpy::dst#3)
+  [13] (byte*) str_cpy::dst#0 ← ++ (byte*) str_cpy::dst#3
+  [14] (to_nomodify byte*) str_cpy::src#0 ← ++ (to_nomodify byte*) str_cpy::src#3
+  [15] if((byte) 0!=(byte~) str_cpy::$0) goto str_cpy::@1
+  to:str_cpy::@return
+str_cpy::@return: scope:[str_cpy]  from str_cpy::@1
+  [16] return 
+  to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+(void()) main()
+(void()) str_cpy((byte*) str_cpy::dst , (to_nomodify byte*) str_cpy::src)
+(byte~) str_cpy::$0 667.3333333333334
+(byte*) str_cpy::dst
+(byte*) str_cpy::dst#0 667.3333333333334
+(byte*) str_cpy::dst#3 1368.3333333333335
+(byte*) str_cpy::dst#4 101.0
+(to_nomodify byte*) str_cpy::src
+(to_nomodify byte*) str_cpy::src#0 1001.0
+(to_nomodify byte*) str_cpy::src#3 776.0
+(to_nomodify byte*) str_cpy::src#4 101.0
+
+Initial phi equivalence classes
+[ str_cpy::src#3 str_cpy::src#4 str_cpy::src#0 ]
+[ str_cpy::dst#3 str_cpy::dst#4 str_cpy::dst#0 ]
+Added variable str_cpy::$0 to live range equivalence class [ str_cpy::$0 ]
+Complete equivalence classes
+[ str_cpy::src#3 str_cpy::src#4 str_cpy::src#0 ]
+[ str_cpy::dst#3 str_cpy::dst#4 str_cpy::dst#0 ]
+[ str_cpy::$0 ]
+Allocated zp[2]:2 [ str_cpy::src#3 str_cpy::src#4 str_cpy::src#0 ]
+Allocated zp[2]:4 [ str_cpy::dst#3 str_cpy::dst#4 str_cpy::dst#0 ]
+Allocated zp[1]:6 [ str_cpy::$0 ]
+
+INITIAL ASM
+Target platform is c64basic / MOS6502X
+  // File Comments
+  // Upstart
+.pc = $801 "Basic"
+:BasicUpstart(main)
+.pc = $80d "Program"
+  // Global Constants & labels
+  .label dst1 = $400
+  .label dst2 = $428
+  // @begin
+__bbegin:
+  // [1] phi from @begin to @1 [phi:@begin->@1]
+__b1_from___bbegin:
+  jmp __b1
+  // @1
+__b1:
+  // [2] call main 
+  // [4] phi from @1 to main [phi:@1->main]
+main_from___b1:
+  jsr main
+  // [3] phi from @1 to @end [phi:@1->@end]
+__bend_from___b1:
+  jmp __bend
+  // @end
+__bend:
+  // main
+main: {
+    // [5] call str_cpy 
+    // [9] phi from main to str_cpy [phi:main->str_cpy]
+  str_cpy_from_main:
+    // [9] phi (byte*) str_cpy::dst#4 = (const byte*) dst1 [phi:main->str_cpy#0] -- pbuz1=pbuc1 
+    lda #<dst1
+    sta.z str_cpy.dst
+    lda #>dst1
+    sta.z str_cpy.dst+1
+    // [9] phi (to_nomodify byte*) str_cpy::src#4 = (const byte*) main::src [phi:main->str_cpy#1] -- pbuz1=pbuc1 
+    lda #<src
+    sta.z str_cpy.src
+    lda #>src
+    sta.z str_cpy.src+1
+    jsr str_cpy
+    // [6] phi from main to main::@1 [phi:main->main::@1]
+  __b1_from_main:
+    jmp __b1
+    // main::@1
+  __b1:
+    // [7] call str_cpy 
+    // [9] phi from main::@1 to str_cpy [phi:main::@1->str_cpy]
+  str_cpy_from___b1:
+    // [9] phi (byte*) str_cpy::dst#4 = (const byte*) dst2 [phi:main::@1->str_cpy#0] -- pbuz1=pbuc1 
+    lda #<dst2
+    sta.z str_cpy.dst
+    lda #>dst2
+    sta.z str_cpy.dst+1
+    // [9] phi (to_nomodify byte*) str_cpy::src#4 = (const byte*) main::src1 [phi:main::@1->str_cpy#1] -- pbuz1=pbuc1 
+    lda #<src1
+    sta.z str_cpy.src
+    lda #>src1
+    sta.z str_cpy.src+1
+    jsr str_cpy
+    jmp __breturn
+    // main::@return
+  __breturn:
+    // [8] return 
+    rts
+    src: .text "hello"
+    .byte 0
+    src1: .text "world"
+    .byte 0
+}
+  // str_cpy
+// str_cpy(byte* zp(4) dst, byte* zp(2) src)
+str_cpy: {
+    .label __0 = 6
+    .label dst = 4
+    .label src = 2
+    // [10] phi from str_cpy str_cpy::@1 to str_cpy::@1 [phi:str_cpy/str_cpy::@1->str_cpy::@1]
+  __b1_from_str_cpy:
+  __b1_from___b1:
+    // [10] phi (byte*) str_cpy::dst#3 = (byte*) str_cpy::dst#4 [phi:str_cpy/str_cpy::@1->str_cpy::@1#0] -- register_copy 
+    // [10] phi (to_nomodify byte*) str_cpy::src#3 = (to_nomodify byte*) str_cpy::src#4 [phi:str_cpy/str_cpy::@1->str_cpy::@1#1] -- register_copy 
+    jmp __b1
+    // str_cpy::@1
+  __b1:
+    // [11] *((byte*) str_cpy::dst#3) ← *((to_nomodify byte*) str_cpy::src#3) -- _deref_pbuz1=_deref_pbuz2 
+    ldy #0
+    lda (src),y
+    ldy #0
+    sta (dst),y
+    // [12] (byte~) str_cpy::$0 ← *((byte*) str_cpy::dst#3) -- vbuz1=_deref_pbuz2 
+    ldy #0
+    lda (dst),y
+    sta.z __0
+    // [13] (byte*) str_cpy::dst#0 ← ++ (byte*) str_cpy::dst#3 -- pbuz1=_inc_pbuz1 
+    inc.z dst
+    bne !+
+    inc.z dst+1
+  !:
+    // [14] (to_nomodify byte*) str_cpy::src#0 ← ++ (to_nomodify byte*) str_cpy::src#3 -- pbuz1=_inc_pbuz1 
+    inc.z src
+    bne !+
+    inc.z src+1
+  !:
+    // [15] if((byte) 0!=(byte~) str_cpy::$0) goto str_cpy::@1 -- vbuc1_neq_vbuz1_then_la1 
+    lda #0
+    cmp.z __0
+    bne __b1_from___b1
+    jmp __breturn
+    // str_cpy::@return
+  __breturn:
+    // [16] return 
+    rts
+}
+  // File Data
+
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [11] *((byte*) str_cpy::dst#3) ← *((to_nomodify byte*) str_cpy::src#3) [ str_cpy::src#3 str_cpy::dst#3 ] ( main:2::str_cpy:5 [ str_cpy::src#3 str_cpy::dst#3 ] { }  main:2::str_cpy:7 [ str_cpy::src#3 str_cpy::dst#3 ] { }  ) always clobbers reg byte a reg byte y 
+Statement [12] (byte~) str_cpy::$0 ← *((byte*) str_cpy::dst#3) [ str_cpy::src#3 str_cpy::dst#3 str_cpy::$0 ] ( main:2::str_cpy:5 [ str_cpy::src#3 str_cpy::dst#3 str_cpy::$0 ] { }  main:2::str_cpy:7 [ str_cpy::src#3 str_cpy::dst#3 str_cpy::$0 ] { }  ) always clobbers reg byte a reg byte y 
+Potential registers zp[2]:2 [ str_cpy::src#3 str_cpy::src#4 str_cpy::src#0 ] : zp[2]:2 , 
+Potential registers zp[2]:4 [ str_cpy::dst#3 str_cpy::dst#4 str_cpy::dst#0 ] : zp[2]:4 , 
+Potential registers zp[1]:6 [ str_cpy::$0 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , 
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [str_cpy] 2,136.67: zp[2]:4 [ str_cpy::dst#3 str_cpy::dst#4 str_cpy::dst#0 ] 1,878: zp[2]:2 [ str_cpy::src#3 str_cpy::src#4 str_cpy::src#0 ] 667.33: zp[1]:6 [ str_cpy::$0 ] 
+Uplift Scope [main] 
+Uplift Scope [] 
+
+Uplifting [str_cpy] best 667 combination zp[2]:4 [ str_cpy::dst#3 str_cpy::dst#4 str_cpy::dst#0 ] zp[2]:2 [ str_cpy::src#3 str_cpy::src#4 str_cpy::src#0 ] reg byte a [ str_cpy::$0 ] 
+Uplifting [main] best 667 combination 
+Uplifting [] best 667 combination 
+
+ASSEMBLER BEFORE OPTIMIZATION
+  // File Comments
+  // Upstart
+.pc = $801 "Basic"
+:BasicUpstart(main)
+.pc = $80d "Program"
+  // Global Constants & labels
+  .label dst1 = $400
+  .label dst2 = $428
+  // @begin
+__bbegin:
+  // [1] phi from @begin to @1 [phi:@begin->@1]
+__b1_from___bbegin:
+  jmp __b1
+  // @1
+__b1:
+  // [2] call main 
+  // [4] phi from @1 to main [phi:@1->main]
+main_from___b1:
+  jsr main
+  // [3] phi from @1 to @end [phi:@1->@end]
+__bend_from___b1:
+  jmp __bend
+  // @end
+__bend:
+  // main
+main: {
+    // [5] call str_cpy 
+    // [9] phi from main to str_cpy [phi:main->str_cpy]
+  str_cpy_from_main:
+    // [9] phi (byte*) str_cpy::dst#4 = (const byte*) dst1 [phi:main->str_cpy#0] -- pbuz1=pbuc1 
+    lda #<dst1
+    sta.z str_cpy.dst
+    lda #>dst1
+    sta.z str_cpy.dst+1
+    // [9] phi (to_nomodify byte*) str_cpy::src#4 = (const byte*) main::src [phi:main->str_cpy#1] -- pbuz1=pbuc1 
+    lda #<src
+    sta.z str_cpy.src
+    lda #>src
+    sta.z str_cpy.src+1
+    jsr str_cpy
+    // [6] phi from main to main::@1 [phi:main->main::@1]
+  __b1_from_main:
+    jmp __b1
+    // main::@1
+  __b1:
+    // [7] call str_cpy 
+    // [9] phi from main::@1 to str_cpy [phi:main::@1->str_cpy]
+  str_cpy_from___b1:
+    // [9] phi (byte*) str_cpy::dst#4 = (const byte*) dst2 [phi:main::@1->str_cpy#0] -- pbuz1=pbuc1 
+    lda #<dst2
+    sta.z str_cpy.dst
+    lda #>dst2
+    sta.z str_cpy.dst+1
+    // [9] phi (to_nomodify byte*) str_cpy::src#4 = (const byte*) main::src1 [phi:main::@1->str_cpy#1] -- pbuz1=pbuc1 
+    lda #<src1
+    sta.z str_cpy.src
+    lda #>src1
+    sta.z str_cpy.src+1
+    jsr str_cpy
+    jmp __breturn
+    // main::@return
+  __breturn:
+    // [8] return 
+    rts
+    src: .text "hello"
+    .byte 0
+    src1: .text "world"
+    .byte 0
+}
+  // str_cpy
+// str_cpy(byte* zp(4) dst, byte* zp(2) src)
+str_cpy: {
+    .label dst = 4
+    .label src = 2
+    // [10] phi from str_cpy str_cpy::@1 to str_cpy::@1 [phi:str_cpy/str_cpy::@1->str_cpy::@1]
+  __b1_from_str_cpy:
+  __b1_from___b1:
+    // [10] phi (byte*) str_cpy::dst#3 = (byte*) str_cpy::dst#4 [phi:str_cpy/str_cpy::@1->str_cpy::@1#0] -- register_copy 
+    // [10] phi (to_nomodify byte*) str_cpy::src#3 = (to_nomodify byte*) str_cpy::src#4 [phi:str_cpy/str_cpy::@1->str_cpy::@1#1] -- register_copy 
+    jmp __b1
+    // str_cpy::@1
+  __b1:
+    // [11] *((byte*) str_cpy::dst#3) ← *((to_nomodify byte*) str_cpy::src#3) -- _deref_pbuz1=_deref_pbuz2 
+    ldy #0
+    lda (src),y
+    ldy #0
+    sta (dst),y
+    // [12] (byte~) str_cpy::$0 ← *((byte*) str_cpy::dst#3) -- vbuaa=_deref_pbuz1 
+    ldy #0
+    lda (dst),y
+    // [13] (byte*) str_cpy::dst#0 ← ++ (byte*) str_cpy::dst#3 -- pbuz1=_inc_pbuz1 
+    inc.z dst
+    bne !+
+    inc.z dst+1
+  !:
+    // [14] (to_nomodify byte*) str_cpy::src#0 ← ++ (to_nomodify byte*) str_cpy::src#3 -- pbuz1=_inc_pbuz1 
+    inc.z src
+    bne !+
+    inc.z src+1
+  !:
+    // [15] if((byte) 0!=(byte~) str_cpy::$0) goto str_cpy::@1 -- vbuc1_neq_vbuaa_then_la1 
+    cmp #0
+    bne __b1_from___b1
+    jmp __breturn
+    // str_cpy::@return
+  __breturn:
+    // [16] return 
+    rts
+}
+  // File Data
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp __b1
+Removing instruction jmp __bend
+Removing instruction jmp __b1
+Removing instruction jmp __breturn
+Removing instruction jmp __b1
+Removing instruction jmp __breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction ldy #0
+Removing instruction ldy #0
+Succesful ASM optimization Pass5UnnecesaryLoadElimination
+Replacing label __b1_from___b1 with __b1
+Removing instruction __b1_from___bbegin:
+Removing instruction __b1:
+Removing instruction main_from___b1:
+Removing instruction __bend_from___b1:
+Removing instruction __b1_from_main:
+Removing instruction str_cpy_from___b1:
+Removing instruction __b1_from_str_cpy:
+Removing instruction __b1_from___b1:
+Succesful ASM optimization Pass5RedundantLabelElimination
+Removing instruction __bbegin:
+Removing instruction __bend:
+Removing instruction str_cpy_from_main:
+Removing instruction __b1:
+Removing instruction __breturn:
+Removing instruction __breturn:
+Succesful ASM optimization Pass5UnusedLabelElimination
+Removing instruction jsr main
+Succesful ASM optimization Pass5SkipBegin
+
+FINAL SYMBOL TABLE
+(label) @1
+(label) @begin
+(label) @end
+(const byte*) dst1 = (byte*) 1024
+(const byte*) dst2 = (byte*) 1064
+(void()) main()
+(label) main::@1
+(label) main::@return
+(const byte*) main::src[(byte) 6]  = (byte*) "hello"
+(const byte*) main::src1[(byte) 6]  = (byte*) "world"
+(void()) str_cpy((byte*) str_cpy::dst , (to_nomodify byte*) str_cpy::src)
+(byte~) str_cpy::$0 reg byte a 667.3333333333334
+(label) str_cpy::@1
+(label) str_cpy::@return
+(byte*) str_cpy::dst
+(byte*) str_cpy::dst#0 dst zp[2]:4 667.3333333333334
+(byte*) str_cpy::dst#3 dst zp[2]:4 1368.3333333333335
+(byte*) str_cpy::dst#4 dst zp[2]:4 101.0
+(to_nomodify byte*) str_cpy::src
+(to_nomodify byte*) str_cpy::src#0 src zp[2]:2 1001.0
+(to_nomodify byte*) str_cpy::src#3 src zp[2]:2 776.0
+(to_nomodify byte*) str_cpy::src#4 src zp[2]:2 101.0
+
+zp[2]:2 [ str_cpy::src#3 str_cpy::src#4 str_cpy::src#0 ]
+zp[2]:4 [ str_cpy::dst#3 str_cpy::dst#4 str_cpy::dst#0 ]
+reg byte a [ str_cpy::$0 ]
+
+
+FINAL ASSEMBLER
+Score: 549
+
+  // File Comments
+  // Upstart
+.pc = $801 "Basic"
+:BasicUpstart(main)
+.pc = $80d "Program"
+  // Global Constants & labels
+  .label dst1 = $400
+  .label dst2 = $428
+  // @begin
+  // [1] phi from @begin to @1 [phi:@begin->@1]
+  // @1
+  // [2] call main 
+  // [4] phi from @1 to main [phi:@1->main]
+  // [3] phi from @1 to @end [phi:@1->@end]
+  // @end
+  // main
+main: {
+    // str_cpy(dst1, "hello")
+    // [5] call str_cpy 
+    // [9] phi from main to str_cpy [phi:main->str_cpy]
+    // [9] phi (byte*) str_cpy::dst#4 = (const byte*) dst1 [phi:main->str_cpy#0] -- pbuz1=pbuc1 
+    lda #<dst1
+    sta.z str_cpy.dst
+    lda #>dst1
+    sta.z str_cpy.dst+1
+    // [9] phi (to_nomodify byte*) str_cpy::src#4 = (const byte*) main::src [phi:main->str_cpy#1] -- pbuz1=pbuc1 
+    lda #<src
+    sta.z str_cpy.src
+    lda #>src
+    sta.z str_cpy.src+1
+    jsr str_cpy
+    // [6] phi from main to main::@1 [phi:main->main::@1]
+    // main::@1
+    // str_cpy(dst2, "world")
+    // [7] call str_cpy 
+    // [9] phi from main::@1 to str_cpy [phi:main::@1->str_cpy]
+    // [9] phi (byte*) str_cpy::dst#4 = (const byte*) dst2 [phi:main::@1->str_cpy#0] -- pbuz1=pbuc1 
+    lda #<dst2
+    sta.z str_cpy.dst
+    lda #>dst2
+    sta.z str_cpy.dst+1
+    // [9] phi (to_nomodify byte*) str_cpy::src#4 = (const byte*) main::src1 [phi:main::@1->str_cpy#1] -- pbuz1=pbuc1 
+    lda #<src1
+    sta.z str_cpy.src
+    lda #>src1
+    sta.z str_cpy.src+1
+    jsr str_cpy
+    // main::@return
+    // }
+    // [8] return 
+    rts
+    src: .text "hello"
+    .byte 0
+    src1: .text "world"
+    .byte 0
+}
+  // str_cpy
+// str_cpy(byte* zp(4) dst, byte* zp(2) src)
+str_cpy: {
+    .label dst = 4
+    .label src = 2
+    // [10] phi from str_cpy str_cpy::@1 to str_cpy::@1 [phi:str_cpy/str_cpy::@1->str_cpy::@1]
+    // [10] phi (byte*) str_cpy::dst#3 = (byte*) str_cpy::dst#4 [phi:str_cpy/str_cpy::@1->str_cpy::@1#0] -- register_copy 
+    // [10] phi (to_nomodify byte*) str_cpy::src#3 = (to_nomodify byte*) str_cpy::src#4 [phi:str_cpy/str_cpy::@1->str_cpy::@1#1] -- register_copy 
+    // str_cpy::@1
+  __b1:
+    // *dst++ = *src++
+    // [11] *((byte*) str_cpy::dst#3) ← *((to_nomodify byte*) str_cpy::src#3) -- _deref_pbuz1=_deref_pbuz2 
+    ldy #0
+    lda (src),y
+    sta (dst),y
+    // while ( *dst++ = *src++ )
+    // [12] (byte~) str_cpy::$0 ← *((byte*) str_cpy::dst#3) -- vbuaa=_deref_pbuz1 
+    lda (dst),y
+    // [13] (byte*) str_cpy::dst#0 ← ++ (byte*) str_cpy::dst#3 -- pbuz1=_inc_pbuz1 
+    inc.z dst
+    bne !+
+    inc.z dst+1
+  !:
+    // [14] (to_nomodify byte*) str_cpy::src#0 ← ++ (to_nomodify byte*) str_cpy::src#3 -- pbuz1=_inc_pbuz1 
+    inc.z src
+    bne !+
+    inc.z src+1
+  !:
+    // [15] if((byte) 0!=(byte~) str_cpy::$0) goto str_cpy::@1 -- vbuc1_neq_vbuaa_then_la1 
+    cmp #0
+    bne __b1
+    // str_cpy::@return
+    // }
+    // [16] return 
+    rts
+}
+  // File Data
+
diff --git a/src/test/ref/strcpy-0.sym b/src/test/ref/strcpy-0.sym
new file mode 100644
index 000000000..906eefdff
--- /dev/null
+++ b/src/test/ref/strcpy-0.sym
@@ -0,0 +1,26 @@
+(label) @1
+(label) @begin
+(label) @end
+(const byte*) dst1 = (byte*) 1024
+(const byte*) dst2 = (byte*) 1064
+(void()) main()
+(label) main::@1
+(label) main::@return
+(const byte*) main::src[(byte) 6]  = (byte*) "hello"
+(const byte*) main::src1[(byte) 6]  = (byte*) "world"
+(void()) str_cpy((byte*) str_cpy::dst , (to_nomodify byte*) str_cpy::src)
+(byte~) str_cpy::$0 reg byte a 667.3333333333334
+(label) str_cpy::@1
+(label) str_cpy::@return
+(byte*) str_cpy::dst
+(byte*) str_cpy::dst#0 dst zp[2]:4 667.3333333333334
+(byte*) str_cpy::dst#3 dst zp[2]:4 1368.3333333333335
+(byte*) str_cpy::dst#4 dst zp[2]:4 101.0
+(to_nomodify byte*) str_cpy::src
+(to_nomodify byte*) str_cpy::src#0 src zp[2]:2 1001.0
+(to_nomodify byte*) str_cpy::src#3 src zp[2]:2 776.0
+(to_nomodify byte*) str_cpy::src#4 src zp[2]:2 101.0
+
+zp[2]:2 [ str_cpy::src#3 str_cpy::src#4 str_cpy::src#0 ]
+zp[2]:4 [ str_cpy::dst#3 str_cpy::dst#4 str_cpy::dst#0 ]
+reg byte a [ str_cpy::$0 ]

From 4bf19bdbece7d748be625cde33f8af13b7c12e01 Mon Sep 17 00:00:00 2001
From: jespergravgaard <jesper@balmangravgaard.dk>
Date: Tue, 16 Jun 2020 23:40:22 +0200
Subject: [PATCH 2/2] Added VS.Code CC65 task.

---
 src/test/kc/.vscode/cc65-x64sc.sh | 9 +++++++++
 src/test/kc/.vscode/tasks.json    | 8 ++++++++
 2 files changed, 17 insertions(+)
 create mode 100755 src/test/kc/.vscode/cc65-x64sc.sh

diff --git a/src/test/kc/.vscode/cc65-x64sc.sh b/src/test/kc/.vscode/cc65-x64sc.sh
new file mode 100755
index 000000000..0f5111496
--- /dev/null
+++ b/src/test/kc/.vscode/cc65-x64sc.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+export C_FILE=$1
+export ASM_FILE=${C_FILE%.*}.s
+export O_FILE=${C_FILE%.*}.o
+export PRG_FILE=${C_FILE%.*}.prg
+cc65 -t c64 -O -o $ASM_FILE $C_FILE
+ca65 -t c64 -o $O_FILE $ASM_FILE
+ld65 -t c64 -o $PRG_FILE $O_FILE c64.lib
+x64sc $PRG_FILE
\ No newline at end of file
diff --git a/src/test/kc/.vscode/tasks.json b/src/test/kc/.vscode/tasks.json
index 8896e66cd..1128741e0 100644
--- a/src/test/kc/.vscode/tasks.json
+++ b/src/test/kc/.vscode/tasks.json
@@ -44,6 +44,14 @@
             "command": ".vscode/kickass-nes.sh",
             "args": [ "${file}" ]  
         },      
+        {
+            "label": "CC65 Build & Run (C64)",
+            "group": "build",
+            "type": "process",
+            "problemMatcher": [],
+            "command": ".vscode/cc65-x64sc.sh",
+            "args": [ "${file}" ]  
+        },      
         {
             "label": "KickAsm Build & Debug (C64)",
             "type": "shell",