From 30b3172e7f60987cce3ba964567fa28e125220a3 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Fri, 31 Jul 2020 23:16:54 +0200 Subject: [PATCH] Added Z register support to register allocation, synthesis, fragment system, clobber and more for 65CE02 and 45GS02. --- .../cache/fragment-cache-csg65ce02.asm | 96 ++++++- .../cache/fragment-cache-mega45gs02.asm | 2 +- .../fragment/cache/fragment-cache-mos6502.asm | 2 +- .../cache/fragment-cache-mos6502x.asm | 98 +++---- .../cache/fragment-cache-wdc65c02.asm | 2 +- .../csg65ce02-common/vbuzz=_inc_vbuzz.asm | 1 + .../fragment/csg65ce02-common/vbuzz=vbuc1.asm | 1 + .../vbuzz_lt_vbuc1_then_la1.asm | 2 + .../dk/camelot64/cpufamily6502/Cpu65xx.java | 32 ++- .../camelot64/cpufamily6502/CpuClobber.java | 2 +- .../cpufamily6502/cpus/Cpu45GS02.java | 2 +- .../cpufamily6502/cpus/Cpu6502Illegal.java | 2 +- .../cpufamily6502/cpus/Cpu6502Official.java | 2 +- .../cpufamily6502/cpus/Cpu65C02.java | 2 +- .../cpufamily6502/cpus/Cpu65CE02.java | 2 +- .../kickc/fragment/AsmFragmentClobber.java | 35 ++- .../AsmFragmentInstanceSpecFactory.java | 2 + .../AsmFragmentTemplateSynthesisRule.java | 47 ++- .../dk/camelot64/kickc/model/Registers.java | 20 ++ .../dk/camelot64/kickc/model/TargetCpu.java | 2 +- .../kickc/passes/Pass4AssertNoCpuClobber.java | 3 + .../kickc/passes/Pass4CodeGeneration.java | 2 + ...ass4RegisterUpliftPotentialInitialize.java | 2 + ...gisterUpliftPotentialRegisterAnalysis.java | 6 +- .../dk/camelot64/kickc/test/TestPrograms.java | 5 + src/test/kc/cpu-65ce02-b.c | 11 + src/test/kc/register-1.c | 2 +- src/test/ref/cpu-45gs02-addressing-modes.log | 2 +- src/test/ref/cpu-45gs02.log | 9 +- src/test/ref/cpu-65ce02-b.asm | 24 ++ src/test/ref/cpu-65ce02-b.cfg | 16 ++ src/test/ref/cpu-65ce02-b.log | 271 ++++++++++++++++++ src/test/ref/cpu-65ce02-b.sym | 10 + src/test/ref/cpu-65ce02.log | 7 +- 34 files changed, 620 insertions(+), 104 deletions(-) create mode 100644 src/main/fragment/csg65ce02-common/vbuzz=_inc_vbuzz.asm create mode 100644 src/main/fragment/csg65ce02-common/vbuzz=vbuc1.asm create mode 100644 src/main/fragment/csg65ce02-common/vbuzz_lt_vbuc1_then_la1.asm create mode 100644 src/test/kc/cpu-65ce02-b.c create mode 100644 src/test/ref/cpu-65ce02-b.asm create mode 100644 src/test/ref/cpu-65ce02-b.cfg create mode 100644 src/test/ref/cpu-65ce02-b.log create mode 100644 src/test/ref/cpu-65ce02-b.sym diff --git a/src/main/fragment/cache/fragment-cache-csg65ce02.asm b/src/main/fragment/cache/fragment-cache-csg65ce02.asm index c4971bdbb..9cbd1f563 100644 --- a/src/main/fragment/cache/fragment-cache-csg65ce02.asm +++ b/src/main/fragment/cache/fragment-cache-csg65ce02.asm @@ -1,4 +1,15 @@ -//KICKC FRAGMENT CACHE 15bc71afc6 +//KICKC FRAGMENT CACHE 1368a0c981 +//FRAGMENT vbuzz=vbuc1 +ldz #{c1} +//FRAGMENT vbuzz_lt_vbuc1_then_la1 +cpz #{c1} +bcc {la1} +//FRAGMENT pbuc1_derefidx_vbuzz=vbuzz +tza +tax +sta {c1},x +//FRAGMENT vbuzz=_inc_vbuzz +inz //FRAGMENT vbsz1=_deref_pbsc1 lda {c1} sta {z1} @@ -29,6 +40,10 @@ sta {z1} tya neg sta {z1} +//FRAGMENT vbsz1=_neg_vbszz +tza +neg +sta {z1} //FRAGMENT vbsaa=_neg_vbsz1 lda {z1} neg @@ -40,6 +55,9 @@ neg //FRAGMENT vbsaa=_neg_vbsyy tya neg +//FRAGMENT vbsaa=_neg_vbszz +tza +neg //FRAGMENT vbsxx=_neg_vbsz1 lda {z1} neg @@ -55,6 +73,10 @@ tax tya neg tax +//FRAGMENT vbsxx=_neg_vbszz +tza +neg +tax //FRAGMENT vbsyy=_neg_vbsz1 lda {z1} neg @@ -70,6 +92,29 @@ tay tya neg tay +//FRAGMENT vbsyy=_neg_vbszz +tza +neg +tay +//FRAGMENT vbszz=_neg_vbsz1 +lda {z1} +neg +taz +//FRAGMENT vbszz=_neg_vbsaa +neg +taz +//FRAGMENT vbszz=_neg_vbsxx +txa +neg +taz +//FRAGMENT vbszz=_neg_vbsyy +tya +neg +taz +//FRAGMENT vbszz=_neg_vbszz +tza +neg +taz //FRAGMENT _deref_pbsc1=vbsaa sta {c1} //FRAGMENT vbsz1=vbsaa_ror_2 @@ -86,6 +131,11 @@ tya asr asr sta {z1} +//FRAGMENT vbsz1=vbszz_ror_2 +tza +asr +asr +sta {z1} //FRAGMENT vbsaa=vbsz1_ror_2 lda {z1} asr @@ -101,6 +151,10 @@ asr tya asr asr +//FRAGMENT vbsaa=vbszz_ror_2 +tza +asr +asr //FRAGMENT vbsxx=vbsz1_ror_2 lda {z1} asr @@ -120,6 +174,11 @@ tya asr asr tax +//FRAGMENT vbsxx=vbszz_ror_2 +tza +asr +asr +tax //FRAGMENT vbsyy=vbsz1_ror_2 lda {z1} asr @@ -139,11 +198,46 @@ tya asr asr tay +//FRAGMENT vbsyy=vbszz_ror_2 +tza +asr +asr +tay +//FRAGMENT vbszz=vbsz1_ror_2 +lda {z1} +asr +asr +taz +//FRAGMENT vbszz=vbsaa_ror_2 +asr +asr +taz +//FRAGMENT vbszz=vbsxx_ror_2 +txa +asr +asr +taz +//FRAGMENT vbszz=vbsyy_ror_2 +tya +asr +asr +taz +//FRAGMENT vbszz=vbszz_ror_2 +tza +asr +asr +taz //FRAGMENT vbsyy=_deref_pbsc1 ldy {c1} +//FRAGMENT vbszz=_deref_pbsc1 +lda {c1} +taz //FRAGMENT _deref_pbsc1=vbsxx txa sta {c1} //FRAGMENT _deref_pbsc1=vbsyy tya sta {c1} +//FRAGMENT _deref_pbsc1=vbszz +tza +sta {c1} diff --git a/src/main/fragment/cache/fragment-cache-mega45gs02.asm b/src/main/fragment/cache/fragment-cache-mega45gs02.asm index 6d8e6b76f..b2d06b3ad 100644 --- a/src/main/fragment/cache/fragment-cache-mega45gs02.asm +++ b/src/main/fragment/cache/fragment-cache-mega45gs02.asm @@ -1,4 +1,4 @@ -//KICKC FRAGMENT CACHE 15bc71afc6 +//KICKC FRAGMENT CACHE 1368a0c981 //FRAGMENT vduz1=vduc1 lda #<{c1} sta {z1} diff --git a/src/main/fragment/cache/fragment-cache-mos6502.asm b/src/main/fragment/cache/fragment-cache-mos6502.asm index a2160abfc..38df9ce51 100644 --- a/src/main/fragment/cache/fragment-cache-mos6502.asm +++ b/src/main/fragment/cache/fragment-cache-mos6502.asm @@ -1,4 +1,4 @@ -//KICKC FRAGMENT CACHE 15bc71afc6 +//KICKC FRAGMENT CACHE 1368a0c981 //FRAGMENT vbuz1=vbuc1 lda #{c1} sta {z1} diff --git a/src/main/fragment/cache/fragment-cache-mos6502x.asm b/src/main/fragment/cache/fragment-cache-mos6502x.asm index b6ae9844d..8a381848e 100644 --- a/src/main/fragment/cache/fragment-cache-mos6502x.asm +++ b/src/main/fragment/cache/fragment-cache-mos6502x.asm @@ -1,12 +1,57 @@ -//KICKC FRAGMENT CACHE 15bc71afc6 -//FRAGMENT vbuz1=vbuc1 -lda #{c1} -sta {z1} +//KICKC FRAGMENT CACHE 1368a0c981 //FRAGMENT pbuz1=pbuc1 lda #<{c1} sta {z1} lda #>{c1} sta {z1}+1 +//FRAGMENT vbuz1=vbuc1 +lda #{c1} +sta {z1} +//FRAGMENT _deref_pbuz1=pbuc1_derefidx_vbuz2 +ldy {z2} +lda {c1},y +ldy #0 +sta ({z1}),y +//FRAGMENT vbuz1=_inc_vbuz1 +inc {z1} +//FRAGMENT vbuz1_neq_vbuc1_then_la1 +lda #{c1} +cmp {z1} +bne {la1} +//FRAGMENT pbuz1=_inc_pbuz1 +inc {z1} +bne !+ +inc {z1}+1 +!: +//FRAGMENT pbuz1_lt_pbuc1_then_la1 +lda {z1}+1 +cmp #>{c1} +bcc {la1} +bne !+ +lda {z1} +cmp #<{c1} +bcc {la1} +!: +//FRAGMENT _deref_pbuz1=pbuc1_derefidx_vbuaa +tay +lda {c1},y +ldy #0 +sta ({z1}),y +//FRAGMENT _deref_pbuz1=pbuc1_derefidx_vbuxx +lda {c1},x +ldy #0 +sta ({z1}),y +//FRAGMENT _deref_pbuz1=pbuc1_derefidx_vbuyy +lda {c1},y +ldy #0 +sta ({z1}),y +//FRAGMENT vbuxx_neq_vbuc1_then_la1 +cpx #{c1} +bne {la1} +//FRAGMENT vbuxx=vbuc1 +ldx #{c1} +//FRAGMENT vbuxx=_inc_vbuxx +inx //FRAGMENT vwuz1=vwuc1 lda #<{c1} sta {z1} @@ -43,12 +88,6 @@ lda {z2} sta {z1} lda {z2}+1 sta {z1}+1 -//FRAGMENT vbuz1=_inc_vbuz1 -inc {z1} -//FRAGMENT vbuz1_neq_vbuc1_then_la1 -lda #{c1} -cmp {z1} -bne {la1} //FRAGMENT pbuz1=pbuz2 lda {z2} sta {z1} @@ -69,11 +108,6 @@ sta {z1}+1 lda {z2} ldy #0 sta ({z1}),y -//FRAGMENT pbuz1=_inc_pbuz1 -inc {z1} -bne !+ -inc {z1}+1 -!: //FRAGMENT vbuz1=_hi_vwuz2 lda {z2}+1 sta {z1} @@ -112,9 +146,6 @@ sta {z1} lda #{c1} and {z2} sta {z1} -//FRAGMENT vbuxx_neq_vbuc1_then_la1 -cpx #{c1} -bne {la1} //FRAGMENT _deref_pbuz1=vbuaa ldy #0 sta ({z1}),y @@ -255,8 +286,6 @@ and #{c1} tay //FRAGMENT vbuaa=vbuc1 lda #{c1} -//FRAGMENT vbuxx=vbuc1 -ldx #{c1} //FRAGMENT vbuyy=vbuc1 ldy #{c1} //FRAGMENT vbuaa=pbuc1_derefidx_vbuxx @@ -1278,8 +1307,6 @@ ldy {z1}+1 //FRAGMENT vbuyy=vbuaa_band_vbuc1 and #{c1} tay -//FRAGMENT vbuxx=_inc_vbuxx -inx //FRAGMENT vbuyy=_inc_vbuyy iny //FRAGMENT vbuyy_neq_vbuc1_then_la1 @@ -1659,11 +1686,6 @@ bne {la1} //FRAGMENT _deref_pbuc1=_deref_pbuc2 lda {c2} sta {c1} -//FRAGMENT _deref_pbuz1=pbuc1_derefidx_vbuz2 -ldy {z2} -lda {c1},y -ldy #0 -sta ({z1}),y //FRAGMENT vbuz1=vbuz1_plus_vbuz2 lda {z1} clc @@ -1886,14 +1908,6 @@ tay //FRAGMENT vbuc1_neq_vbuaa_then_la1 cmp #{c1} bne {la1} -//FRAGMENT _deref_pbuz1=pbuc1_derefidx_vbuxx -lda {c1},x -ldy #0 -sta ({z1}),y -//FRAGMENT _deref_pbuz1=pbuc1_derefidx_vbuyy -lda {c1},y -ldy #0 -sta ({z1}),y //FRAGMENT vbuz1=vbuz1_plus_vbuxx txa clc @@ -2889,15 +2903,6 @@ inc {m1} lda #{c1} cmp {m1} bne {la1} -//FRAGMENT pbuz1_lt_pbuc1_then_la1 -lda {z1}+1 -cmp #>{c1} -bcc {la1} -bne !+ -lda {z1} -cmp #<{c1} -bcc {la1} -!: //FRAGMENT vboz1=vbuz2_eq_vbuc1 lda {z2} eor #{c1} @@ -3735,11 +3740,6 @@ tax //FRAGMENT vbuyy=_byte_vwuz1 lda {z1} tay -//FRAGMENT _deref_pbuz1=pbuc1_derefidx_vbuaa -tay -lda {c1},y -ldy #0 -sta ({z1}),y //FRAGMENT pwsz1=pwsc1_plus_vwsz1 clc lda {z1} diff --git a/src/main/fragment/cache/fragment-cache-wdc65c02.asm b/src/main/fragment/cache/fragment-cache-wdc65c02.asm index 9fc783b6d..bf4d3aabf 100644 --- a/src/main/fragment/cache/fragment-cache-wdc65c02.asm +++ b/src/main/fragment/cache/fragment-cache-wdc65c02.asm @@ -1,4 +1,4 @@ -//KICKC FRAGMENT CACHE 15bc71afc6 +//KICKC FRAGMENT CACHE 1368a0c981 //FRAGMENT vbuz1=_deref_pbuc1 lda {c1} sta {z1} diff --git a/src/main/fragment/csg65ce02-common/vbuzz=_inc_vbuzz.asm b/src/main/fragment/csg65ce02-common/vbuzz=_inc_vbuzz.asm new file mode 100644 index 000000000..ea2a13222 --- /dev/null +++ b/src/main/fragment/csg65ce02-common/vbuzz=_inc_vbuzz.asm @@ -0,0 +1 @@ +inz \ No newline at end of file diff --git a/src/main/fragment/csg65ce02-common/vbuzz=vbuc1.asm b/src/main/fragment/csg65ce02-common/vbuzz=vbuc1.asm new file mode 100644 index 000000000..476927492 --- /dev/null +++ b/src/main/fragment/csg65ce02-common/vbuzz=vbuc1.asm @@ -0,0 +1 @@ +ldz #{c1} \ No newline at end of file diff --git a/src/main/fragment/csg65ce02-common/vbuzz_lt_vbuc1_then_la1.asm b/src/main/fragment/csg65ce02-common/vbuzz_lt_vbuc1_then_la1.asm new file mode 100644 index 000000000..201f1582d --- /dev/null +++ b/src/main/fragment/csg65ce02-common/vbuzz_lt_vbuc1_then_la1.asm @@ -0,0 +1,2 @@ +cpz #{c1} +bcc {la1} \ No newline at end of file diff --git a/src/main/java/dk/camelot64/cpufamily6502/Cpu65xx.java b/src/main/java/dk/camelot64/cpufamily6502/Cpu65xx.java index 1da9cab8c..7660a4777 100644 --- a/src/main/java/dk/camelot64/cpufamily6502/Cpu65xx.java +++ b/src/main/java/dk/camelot64/cpufamily6502/Cpu65xx.java @@ -13,19 +13,21 @@ public class Cpu65xx { /** All opcodes in the instruction set. */ private final List allOpcodes; + /** true if the CPU has a Z register. */ + private boolean registerZ; + /** Maps mnemonic_addressingMode to the instruction opcode */ private final Map opcodesByMnemonicAddrMode; - public Cpu65xx(String name) { + public Cpu65xx(String name, boolean registerZ) { this.name = name; + this.registerZ = registerZ; this.allOpcodes = new ArrayList<>(); this.opcodesByMnemonicAddrMode = new LinkedHashMap<>(); } - public Cpu65xx(String name, Cpu65xx basedOn) { - this.name = name; - this.allOpcodes = new ArrayList<>(); - this.opcodesByMnemonicAddrMode = new LinkedHashMap<>(); + public Cpu65xx(String name, Cpu65xx basedOn, boolean registerZ) { + this(name, registerZ); for(CpuOpcode opcode : basedOn.allOpcodes) { addOpcode(opcode); } @@ -33,12 +35,22 @@ public class Cpu65xx { /** * Get the CPU name. + * * @return The name */ public String getName() { return name; } + /** + * Does the CPU have an extra Z-register. (only the 65ce02 and 45gs02) have this. + * + * @return true if the CPU has a Z register + */ + public boolean hasRegisterZ() { + return registerZ; + } + /** * Add an instruction opcode to the instruction set. * @@ -48,7 +60,7 @@ public class Cpu65xx { * @param cycles The number of cycles */ protected void addOpcode(int opcode, String mnemonic, CpuAddressingMode addressingMode, double cycles, String clobberString) { - addOpcode(new int[] { opcode }, mnemonic, addressingMode, cycles, clobberString); + addOpcode(new int[]{opcode}, mnemonic, addressingMode, cycles, clobberString); } /** @@ -75,19 +87,18 @@ public class Cpu65xx { /** * Remove an opcode from the instruction set. (should only be done during initialization) + * * @param mnemonic The lower case mnemonic * @param addressingMode The addressing mode */ protected void removeOpcode(String mnemonic, CpuAddressingMode addressingMode) { final CpuOpcode opcode = getOpcode(mnemonic, addressingMode); - if(opcode==null) - throw new RuntimeException("Opcode not found "+mnemonic+" "+addressingMode.getName()); + if(opcode == null) + throw new RuntimeException("Opcode not found " + mnemonic + " " + addressingMode.getName()); allOpcodes.remove(opcode); opcodesByMnemonicAddrMode.remove(opcode.getMnemonic() + "_" + opcode.getAddressingMode().getName()); } - - /** * Get a specific instruction opcode form the instruction set * @@ -140,6 +151,7 @@ public class Cpu65xx { /** * Get all the opcodes of the CPU + * * @return The opcodes */ public Collection getAllOpcodes() { diff --git a/src/main/java/dk/camelot64/cpufamily6502/CpuClobber.java b/src/main/java/dk/camelot64/cpufamily6502/CpuClobber.java index eff204301..0a83c7f3d 100644 --- a/src/main/java/dk/camelot64/cpufamily6502/CpuClobber.java +++ b/src/main/java/dk/camelot64/cpufamily6502/CpuClobber.java @@ -155,7 +155,7 @@ public class CpuClobber implements Serializable { } public boolean isRegisterZ() { - return registerY; + return registerZ; } public boolean isFlagC() { diff --git a/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu45GS02.java b/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu45GS02.java index c2ba0e238..17fd09731 100644 --- a/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu45GS02.java +++ b/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu45GS02.java @@ -16,7 +16,7 @@ public class Cpu45GS02 extends Cpu65xx { public final static Cpu45GS02 INSTANCE = new Cpu45GS02(); public Cpu45GS02() { - super(NAME, Cpu65CE02.INSTANCE); + super(NAME, Cpu65CE02.INSTANCE, true); addOpcode( new int[] {0x42, 0x42, 0x5},"orq",CpuAddressingMode.ZP,8,"AXYZnz"); addOpcode( new int[] {0x42, 0x42, 0x6},"aslq",CpuAddressingMode.ZP,12,"cnz"); diff --git a/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu6502Illegal.java b/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu6502Illegal.java index 82d3a95e7..872d7bbf4 100644 --- a/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu6502Illegal.java +++ b/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu6502Illegal.java @@ -16,7 +16,7 @@ public class Cpu6502Illegal extends Cpu65xx { public final static Cpu6502Illegal INSTANCE = new Cpu6502Illegal(); public Cpu6502Illegal() { - super(NAME, Cpu6502Official.INSTANCE); + super(NAME, Cpu6502Official.INSTANCE, false); addOpcode(0x03, "slo", CpuAddressingMode.IZX, 8.0, "Acnz"); addOpcode(0x07, "slo", CpuAddressingMode.ZP, 5.0, "Acnz"); addOpcode(0x0b, "anc", CpuAddressingMode.IMM, 2.0, "Acnz"); diff --git a/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu6502Official.java b/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu6502Official.java index 72ba034e2..b247a52f2 100644 --- a/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu6502Official.java +++ b/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu6502Official.java @@ -17,7 +17,7 @@ public class Cpu6502Official extends Cpu65xx { public final static Cpu6502Official INSTANCE = new Cpu6502Official(); public Cpu6502Official() { - super(NAME); + super(NAME, false); addOpcode(0x00, "brk", CpuAddressingMode.NON, 7.0, ""); addOpcode(0x01, "ora", CpuAddressingMode.IZX, 6.0, "Anz"); addOpcode(0x05, "ora", CpuAddressingMode.ZP, 3.0, "Anz"); diff --git a/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu65C02.java b/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu65C02.java index 5d315e97f..2625e480c 100644 --- a/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu65C02.java +++ b/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu65C02.java @@ -16,7 +16,7 @@ public class Cpu65C02 extends Cpu65xx { public final static Cpu65C02 INSTANCE = new Cpu65C02(); public Cpu65C02() { - super(NAME, Cpu6502Official.INSTANCE); + super(NAME, Cpu6502Official.INSTANCE, false); addOpcode(0x4,"tsb", CpuAddressingMode.ZP,5,"z"); addOpcode(0x7,"rmb0", CpuAddressingMode.ZP,5,""); addOpcode(0xC,"tsb", CpuAddressingMode.ABS,6,"z"); diff --git a/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu65CE02.java b/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu65CE02.java index 56b17dba8..745cd897f 100644 --- a/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu65CE02.java +++ b/src/main/java/dk/camelot64/cpufamily6502/cpus/Cpu65CE02.java @@ -16,7 +16,7 @@ public class Cpu65CE02 extends Cpu65xx { public final static Cpu65CE02 INSTANCE = new Cpu65CE02(); public Cpu65CE02() { - super(NAME, Cpu65C02.INSTANCE); + super(NAME, Cpu65C02.INSTANCE, true); // Remove all opcodes not present on 65CE02 - eg. (zp), which is changed to (zp),y removeOpcode("ora", CpuAddressingMode.INZ); diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentClobber.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentClobber.java index 0bb2981bf..97318f9cf 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentClobber.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentClobber.java @@ -2,21 +2,25 @@ package dk.camelot64.kickc.fragment; import dk.camelot64.cpufamily6502.CpuClobber; +import java.util.Objects; + /** The clobber profile for a fragment template. Only distinguishes the 3 registers A/X/Y and not the flags. */ public class AsmFragmentClobber implements Comparable { private boolean clobberA; private boolean clobberX; private boolean clobberY; + private boolean clobberZ; - public AsmFragmentClobber(boolean clobberA, boolean clobberX, boolean clobberY) { + public AsmFragmentClobber(boolean clobberA, boolean clobberX, boolean clobberY, boolean clobberZ) { this.clobberA = clobberA; this.clobberX = clobberX; this.clobberY = clobberY; + this.clobberZ = clobberZ; } public AsmFragmentClobber(CpuClobber clobber) { - this(clobber.isRegisterA(), clobber.isRegisterX(), clobber.isRegisterY()); + this(clobber.isRegisterA(), clobber.isRegisterX(), clobber.isRegisterY(), clobber.isRegisterZ()); } public boolean isClobberA() { @@ -31,22 +35,24 @@ public class AsmFragmentClobber implements Comparable { return clobberY; } + public boolean isClobberZ() { + return clobberY; + } + @Override public boolean equals(Object o) { if(this == o) return true; if(o == null || getClass() != o.getClass()) return false; AsmFragmentClobber that = (AsmFragmentClobber) o; - if(clobberA != that.clobberA) return false; - if(clobberX != that.clobberX) return false; - return clobberY == that.clobberY; + return clobberA == that.clobberA && + clobberX == that.clobberX && + clobberY == that.clobberY && + clobberZ == that.clobberZ; } @Override public int hashCode() { - int result = (clobberA ? 1 : 0); - result = 31 * result + (clobberX ? 1 : 0); - result = 31 * result + (clobberY ? 1 : 0); - return result; + return Objects.hash(clobberA, clobberX, clobberY, clobberZ); } /** @@ -62,11 +68,15 @@ public class AsmFragmentClobber implements Comparable { return false; } if(!other.isClobberX() && this.isClobberX()) { - // This clobber clobbers A, while the other does not - not a subset + // This clobber clobbers X, while the other does not - not a subset return false; } if(!other.isClobberY() && this.isClobberY()) { - // This clobber clobbers A, while the other does not - not a subset + // This clobber clobbers Y, while the other does not - not a subset + return false; + } + if(!other.isClobberZ() && this.isClobberZ()) { + // This clobber clobbers Z, while the other does not - not a subset return false; } // This is a subset @@ -86,11 +96,12 @@ public class AsmFragmentClobber implements Comparable { @Override public String toString() { - return (clobberA?"A ":"")+(clobberX?"X ":"")+(clobberY?"Y ":" "); + return (clobberA?"A ":"")+(clobberX?"X ":"")+(clobberY?"Y ":" ")+(clobberZ?"Z ":" "); } @Override public int compareTo(AsmFragmentClobber o) { return toString().compareTo(o.toString()); } + } diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java index 80c753825..17ea5d95c 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java @@ -536,6 +536,8 @@ public class AsmFragmentInstanceSpecFactory { return "xx"; } else if(Registers.RegisterType.REG_Y.equals(register.getType())) { return "yy"; + } else if(Registers.RegisterType.REG_Z.equals(register.getType())) { + return "zz"; } else if(Registers.RegisterType.REG_ALU.equals(register.getType())) { throw new AsmFragmentInstance.AluNotApplicableException(); } else { diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesisRule.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesisRule.java index 0be365714..f4260cdd7 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesisRule.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesisRule.java @@ -116,7 +116,7 @@ class AsmFragmentTemplateSynthesisRule { if(subDontClobber.contains("aa") && subTemplate.getClobber().isClobberA()) return null; if(subDontClobber.contains("xx") && subTemplate.getClobber().isClobberX()) return null; if(subDontClobber.contains("yy") && subTemplate.getClobber().isClobberY()) return null; - // TODO Z register: if(subDontClobber.contains("zz") && subTemplate.getClobber().isClobberZ()) return null; + if(subDontClobber.contains("zz") && subTemplate.getClobber().isClobberZ()) return null; } StringBuilder newFragment = new StringBuilder(); @@ -280,6 +280,7 @@ class AsmFragmentTemplateSynthesisRule { mapSToU.put("vbsaa", "vbuaa"); mapSToU.put("vbsxx", "vbuxx"); mapSToU.put("vbsyy", "vbuyy"); + mapSToU.put("vbszz", "vbuzz"); mapSToU.put("vwsz1", "vwuz1"); mapSToU.put("vwsz2", "vwuz2"); mapSToU.put("vwsz3", "vwuz3"); @@ -350,6 +351,7 @@ class AsmFragmentTemplateSynthesisRule { String rvalAa = ".*=.*aa.*|.*_.*aa.*|...aa_(lt|gt|le|ge|eq|neq)_.*"; String rvalXx = ".*=.*xx.*|.*_.*xx.*|...xx_(lt|gt|le|ge|eq|neq)_.*"; String rvalYy = ".*=.*yy.*|.*_.*yy.*|...yy_(lt|gt|le|ge|eq|neq)_.*"; + String rvalZz = ".*=.*zz.*|.*_.*zz.*|...zz_(lt|gt|le|ge|eq|neq)_.*"; String rvalZ1 = ".*=.*z1.*|.*_.*z1.*|...z1_(lt|gt|le|ge|eq|neq)_.*"; String rvalZ2 = ".*=.*z2.*|.*_.*z2.*|...z2_(lt|gt|le|ge|eq|neq)_.*"; String lvalC1 = ".*c1.*=.*"; @@ -369,6 +371,7 @@ class AsmFragmentTemplateSynthesisRule { String lvalAa = "...aa=.*"; String lvalXx = "...xx=.*"; String lvalYy = "...yy=.*"; + String lvalZz = "...zz=.*"; String lvalZM1 = "...[zm]1=.*"; String lvalZM2 = "...[zm]2=.*"; String lvalZM3 = "...[zm]3=.*"; @@ -445,12 +448,29 @@ class AsmFragmentTemplateSynthesisRule { // Replace two YYs with AA (not assigned) synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*vb.)yy(.*vb.)yy(.*)", rvalAa, "tya", "$1=$2aa$3aa$4", null, null)); + // Replace first ZZ with AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)zz(.*)", lvalZz+"|"+rvalAa, "tza", "$1aa$2", null, null)); + // Replace two ZZs with AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)zz(.*vb.)zz(.*)", lvalZz+"|"+rvalAa, "tza", "$1aa$2aa$3", null, null)); + // Replace second (not first) ZZ with AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)zz(.*vb.)zz(.*)", lvalZz+"|"+rvalAa, "tza", "$1zz$2aa$3", null, null)); + // Replace ZZ with AA (not assigned) + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vb(.)zz(.*)", rvalAa, "tza", "$1=$2vb$3aa$4", null, null)); + // Replace two ZZs with AA (not assigned) + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*vb.)zz(.*vb.)zz(.*)", rvalAa, "tza", "$1=$2aa$3aa$4", null, null)); + // Replace XX with YY synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vb(.)xx(.*)", rvalYy, "stx $ff\nldy $ff", "$1=$2vb$3yy$4", null, null)); // Replace YY with XX synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vb(.)yy(.*)", rvalXx, "sty $ff\nldx $ff", "$1=$2vb$3xx$4", null, null)); + // Replace ZZ with YY + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vb(.)zz(.*)", rvalYy, "stz $ff\nldy $ff", "$1=$2vb$3yy$4", null, null)); + // Replace ZZ with XX + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vb(.)zz(.*)", rvalXx, "stz $ff\nldx $ff", "$1=$2vb$3xx$4", null, null)); + + // Replace Z1 with M1 synths.add(new AsmFragmentTemplateSynthesisRule("(.*)z1(.*)", twoZM1, null, "$1m1$2", null, mapZ1M1)); // Replace two Z1 with M1 @@ -636,6 +656,9 @@ class AsmFragmentTemplateSynthesisRule { synths.add(new AsmFragmentTemplateSynthesisRule("vb(.)xx=(.*)", null, null, "vb$1aa=$2", "tax", null)); // Rewrite Assignments to Y from A synths.add(new AsmFragmentTemplateSynthesisRule("vb(.)yy=(.*)", null, null, "vb$1aa=$2", "tay", null)); + // Rewrite Assignments to Z from A + synths.add(new AsmFragmentTemplateSynthesisRule("vb(.)zz=(.*)", null, null, "vb$1aa=$2", "taz", null)); + // Rewrite Assignments to Z1 from A synths.add(new AsmFragmentTemplateSynthesisRule("vb(.)m1=(.*)", twoZM1, null, "vb$1aa=$2", "sta {m1}", mapZM1)); // Rewrite Assignments to Z1 from A @@ -731,14 +754,14 @@ class AsmFragmentTemplateSynthesisRule { synths.add(new AsmFragmentTemplateSynthesisRule("(.*)q[^v][^o]([czm][1-9])(.*[pq].*)", null, null, "$1qvo$2$3", null, null)); // Synthesize some constant pointers as constant words (remove when the above section can be included) - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_(lt|gt|le|ge|eq|neq)_p..([czm].)_then_(.*)", null, null, "$1_$2_vwu$3_then_$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("p..([czm].)_(lt|gt|le|ge|eq|neq)_(.*)", null, null, "vwu$1_$2_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=p..([czm].)", null, null, "$1=vwu$2", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_(plus|minus|bor|bxor)_p..([czm].)", null, null, "$1=$2_$3_vwu$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=p..([czm].)_(plus|minus|bor|bxor)_(.*)", null, null, "$1=vwu$2_$3_$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("p..([czm].)=(.*)_(sethi|setlo|plus|minus)_(.*)", null, null, "vwu$1=$2_$3_$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=p..([czm].)_(sethi|setlo|plus|minus)_(.*)", null, null, "$1=vwu$2_$3_$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("p..([czm].)=_(inc|dec)_p..([czm].)", null, null, "vwu$1=_$2_vwu$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_(lt|gt|le|ge|eq|neq)_p..([czm][0-9])_then_(.*)", null, null, "$1_$2_vwu$3_then_$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("p..([czm][0-9])_(lt|gt|le|ge|eq|neq)_(.*)", null, null, "vwu$1_$2_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=p..([czm][0-9])", null, null, "$1=vwu$2", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_(plus|minus|bor|bxor)_p..([czm][0-9])", null, null, "$1=$2_$3_vwu$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=p..([czm][0-9])_(plus|minus|bor|bxor)_(.*)", null, null, "$1=vwu$2_$3_$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("p..([czm][0-9])=(.*)_(sethi|setlo|plus|minus)_(.*)", null, null, "vwu$1=$2_$3_$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=p..([czm][0-9])_(sethi|setlo|plus|minus)_(.*)", null, null, "$1=vwu$2_$3_$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("p..([czm][0-9])=_(inc|dec)_p..([czm][0-9])", null, null, "vwu$1=_$2_vwu$3", null, null)); // Synthesize constants using AA/XX/YY synths.add(new AsmFragmentTemplateSynthesisRule("(.*)vb(.)c1(.*)", rvalAa+"|"+ derefC1, "lda #{c1}", "$1vb$2aa$3", null, null)); @@ -809,7 +832,7 @@ class AsmFragmentTemplateSynthesisRule { synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c2_derefidx_vbuyy(.*c2.*)", rvalAa, "lda {c2},y", "$1=$2vb$3aa$4", null, null)); // Remove any parenthesis ending up around values - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)\\(([vp][bwd][us][zcaxy][123456axy])\\)(.*)", null, null, "$1$2$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)\\(([vp][bwd][us][mzcaxy][123456axyz])\\)(.*)", null, null, "$1$2$3", null, null)); synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuz1_(.*)", rvalYy+"|"+twoZM1, "ldy {z1}", "$1_derefidx_vbuyy_$2", null, mapZM1)); synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuz1_(lt|gt|le|ge|eq|neq)_(.*)", rvalXx+"|"+twoZM1, "ldx {z1}", "$1_derefidx_vbuxx_$2_$3", null, mapZM1)); @@ -837,7 +860,7 @@ class AsmFragmentTemplateSynthesisRule { synths.add(new AsmFragmentTemplateSynthesisRule("(vb[su]..)=(vb[su]..)_(plus|minus)_(vb[su]..)", null, null, "$1=$2_$3_$4", null, mapSToU)); synths.add(new AsmFragmentTemplateSynthesisRule("(vw[su]..)=(vw[su]..)_(plus|minus)_(vw[su]..)", null, null, "$1=$2_$3_$4", null, mapSToU)); synths.add(new AsmFragmentTemplateSynthesisRule("(vd[su]..)=(vd[su]..)_(plus|minus)_(vd[su]..)", null, null, "$1=$2_$3_$4", null, mapSToU)); - synths.add(new AsmFragmentTemplateSynthesisRule("(vbu..)=_(lo|hi)_vws(z.|c.|m.)", null, null, "$1=_$2_vwu$3", null, mapSToU)); + synths.add(new AsmFragmentTemplateSynthesisRule("(vbu..)=_(lo|hi)_vws(z[0-9]|c[0-9]|m[0-9])", null, null, "$1=_$2_vwu$3", null, mapSToU)); // Use Z1/Z2 ASM to synthesize Z1-only code ( ...z1...z1... -> ...z1...z2... ) synths.add(new AsmFragmentTemplateSynthesisRule("(v..[zm])1=(v..[zm])1_(plus|minus|band|bxor|bor)_(.*)", oneZM2, null, "$11=$22_$3_$4", null, mapZM1, false)); @@ -849,7 +872,7 @@ class AsmFragmentTemplateSynthesisRule { // Convert INC/DEC to +1/-1 ( ..._inc_xxx... -> ...xxx_plus_1_... / ..._dec_xxx... -> ...xxx_minus_1_... ) synths.add(new AsmFragmentTemplateSynthesisRule("vb(.)aa=_inc_(.*)", null, null, "vb$1aa=$2_plus_1", null, null)); synths.add(new AsmFragmentTemplateSynthesisRule("vb(.)aa=_dec_(.*)", null, null, "vb$1aa=$2_minus_1", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(v..[zm].)=_inc_(v..[zm].)", null, null, "$1=$2_plus_1", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(v..[zm][0-9])=_inc_(v..[zm][0-9])", null, null, "$1=$2_plus_1", null, null)); return synths; } diff --git a/src/main/java/dk/camelot64/kickc/model/Registers.java b/src/main/java/dk/camelot64/kickc/model/Registers.java index a3e6bf227..7af6783fd 100644 --- a/src/main/java/dk/camelot64/kickc/model/Registers.java +++ b/src/main/java/dk/camelot64/kickc/model/Registers.java @@ -19,6 +19,10 @@ public class Registers { return new RegisterYByte(); } + public static Register getRegisterZ() { + return new RegisterZByte(); + } + public static Register getRegisterA() { return new RegisterAByte(); } @@ -35,6 +39,8 @@ public class Registers { return getRegisterX(); case "Y": return getRegisterY(); + case "Z": + return getRegisterZ(); default: return null; } @@ -45,6 +51,7 @@ public class Registers { REG_A, REG_Y, REG_X, + REG_Z, REG_ALU, ZP_MEM, MAIN_MEM, @@ -250,6 +257,19 @@ public class Registers { } + /** The Z register. */ + public static class RegisterZByte extends RegisterCpuByte { + @Override + public RegisterType getType() { + return RegisterType.REG_Z; + } + + @Override + public String toString() { + return "reg byte z"; + } + + } /** The X register. */ public static class RegisterXByte extends RegisterCpuByte { diff --git a/src/main/java/dk/camelot64/kickc/model/TargetCpu.java b/src/main/java/dk/camelot64/kickc/model/TargetCpu.java index a4b1c2db2..97523f96e 100644 --- a/src/main/java/dk/camelot64/kickc/model/TargetCpu.java +++ b/src/main/java/dk/camelot64/kickc/model/TargetCpu.java @@ -70,7 +70,7 @@ public enum TargetCpu { /** The CPU name used by KickAsm */ private final String asmName; - /** The SM CPU knowing the instruction set. */ + /** The CPU instruction set. */ private final Cpu65xx cpu65xx; /** Features of the CPU */ diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4AssertNoCpuClobber.java b/src/main/java/dk/camelot64/kickc/passes/Pass4AssertNoCpuClobber.java index 3b3a0e8f5..172242e21 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4AssertNoCpuClobber.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4AssertNoCpuClobber.java @@ -40,6 +40,9 @@ public class Pass4AssertNoCpuClobber extends Pass2Base { if(clobber.isRegisterY()) { clobberRegisters.add(Registers.getRegisterY()); } + if(clobber.isRegisterZ()) { + clobberRegisters.add(Registers.getRegisterZ()); + } return clobberRegisters; } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 872a9aa71..ec58b4e33 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -272,6 +272,8 @@ public class Pass4CodeGeneration { signature.append("register(X)"); } else if(allocation instanceof Registers.RegisterYByte) { signature.append("register(Y)"); + } else if(allocation instanceof Registers.RegisterZByte) { + signature.append("register(Z)"); } signature.append(" "); signature.append(parameter.getLocalName()); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialInitialize.java b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialInitialize.java index 6926e6427..df8cf4f39 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialInitialize.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialInitialize.java @@ -62,6 +62,8 @@ public class Pass4RegisterUpliftPotentialInitialize extends Pass2Base { potentials.add(Registers.getRegisterA()); potentials.add(Registers.getRegisterX()); potentials.add(Registers.getRegisterY()); + if(getProgram().getTargetCpu().getCpu65xx().hasRegisterZ()) + potentials.add(Registers.getRegisterZ()); } registerPotentials.setPotentialRegisters(equivalenceClass, potentials); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialRegisterAnalysis.java b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialRegisterAnalysis.java index b977eb71f..2a97e5186 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialRegisterAnalysis.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialRegisterAnalysis.java @@ -5,7 +5,9 @@ import dk.camelot64.kickc.asm.AsmProgram; import dk.camelot64.kickc.fragment.AsmFragmentInstance; import dk.camelot64.kickc.fragment.AsmFragmentTemplateSynthesizer; import dk.camelot64.kickc.model.*; -import dk.camelot64.kickc.model.statements.*; +import dk.camelot64.kickc.model.statements.Statement; +import dk.camelot64.kickc.model.statements.StatementLValue; +import dk.camelot64.kickc.model.statements.StatementPhiBlock; import dk.camelot64.kickc.model.values.LValue; import dk.camelot64.kickc.model.values.RValue; import dk.camelot64.kickc.model.values.ValueList; @@ -157,6 +159,8 @@ public class Pass4RegisterUpliftPotentialRegisterAnalysis extends Pass2Base { alwaysClobbered.add(Registers.getRegisterA()); alwaysClobbered.add(Registers.getRegisterX()); alwaysClobbered.add(Registers.getRegisterY()); + if(getProgram().getTargetCpu().getCpu65xx().hasRegisterZ()) + alwaysClobbered.add(Registers.getRegisterZ()); Set unknownFragments = new LinkedHashSet<>(); diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 3081e31a2..9fbe3ad41 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -67,6 +67,11 @@ public class TestPrograms { compileAndCompare("cpu-45gs02.c"); } + @Test + public void testCpu65CE02b() throws IOException, URISyntaxException { + compileAndCompare("cpu-65ce02-b.c"); + } + @Test public void testCpu65CE02() throws IOException, URISyntaxException { compileAndCompare("cpu-65ce02.c"); diff --git a/src/test/kc/cpu-65ce02-b.c b/src/test/kc/cpu-65ce02-b.c new file mode 100644 index 000000000..4ddaafb32 --- /dev/null +++ b/src/test/kc/cpu-65ce02-b.c @@ -0,0 +1,11 @@ +// Test the 65CE02 CPU +// A program that uses 65CE02 instructions + +#pragma cpu(CSG65CE02) + +char* const SCREEN = 0x0400; + +void main() { + for(register(Z) char i=0;i<100;i++) + SCREEN[i] = i; +} \ No newline at end of file diff --git a/src/test/kc/register-1.c b/src/test/kc/register-1.c index 04c4a64cf..56704318f 100644 --- a/src/test/kc/register-1.c +++ b/src/test/kc/register-1.c @@ -3,7 +3,7 @@ void main() { char* const SCREEN = 0x0400; - char register(Z) idx = 3; + char register(H) idx = 3; while(idx++<7) SCREEN[idx] = 'a'; } diff --git a/src/test/ref/cpu-45gs02-addressing-modes.log b/src/test/ref/cpu-45gs02-addressing-modes.log index c37177e76..2d4382169 100644 --- a/src/test/ref/cpu-45gs02-addressing-modes.log +++ b/src/test/ref/cpu-45gs02-addressing-modes.log @@ -102,7 +102,7 @@ main: { far: .byte $60 REGISTER UPLIFT POTENTIAL REGISTERS -Statement asm { inx lda#$12 phw#$1234 lda$12 lda$12,x ldx$12,y lda($12,x) lda($12),y ora($12),z lda($12,sp),y lda$1234 lda$1234,x lda$1234,y beqlbl1 lbeqfar bbr0$12,lbl2 lbl1: jmp($1234) lbl2: jmp($1234,x) lbl3: lda(($12)),z ldq(($12)) } always clobbers reg byte a reg byte x reg byte y +Statement asm { inx lda#$12 phw#$1234 lda$12 lda$12,x ldx$12,y lda($12,x) lda($12),y ora($12),z lda($12,sp),y lda$1234 lda$1234,x lda$1234,y beqlbl1 lbeqfar bbr0$12,lbl2 lbl1: jmp($1234) lbl2: jmp($1234,x) lbl3: lda(($12)),z ldq(($12)) } always clobbers reg byte a reg byte x reg byte y reg byte z REGISTER UPLIFT SCOPES Uplift Scope [main] diff --git a/src/test/ref/cpu-45gs02.log b/src/test/ref/cpu-45gs02.log index 5e5404bb1..7f834d35a 100644 --- a/src/test/ref/cpu-45gs02.log +++ b/src/test/ref/cpu-45gs02.log @@ -257,15 +257,16 @@ main: { // File Data REGISTER UPLIFT POTENTIAL REGISTERS -Statement [3] *((const dword*) SCREEN) ← (dword) main::sum#2 [ ] ( [ ] { } ) always clobbers reg byte a reg byte x reg byte y -Statement [5] (dword) main::sum#1 ← (dword) main::sum#2 + (dword) main::addend#2 [ main::i#2 main::addend#2 main::sum#1 ] ( [ main::i#2 main::addend#2 main::sum#1 ] { } ) always clobbers reg byte a reg byte x reg byte y +Statement [3] *((const dword*) SCREEN) ← (dword) main::sum#2 [ ] ( [ ] { } ) always clobbers reg byte a reg byte x reg byte y reg byte z +Statement [5] (dword) main::sum#1 ← (dword) main::sum#2 + (dword) main::addend#2 [ main::i#2 main::addend#2 main::sum#1 ] ( [ main::i#2 main::addend#2 main::sum#1 ] { } ) always clobbers reg byte a reg byte x reg byte y reg byte z Removing always clobbered register reg byte a as potential for zp[1]:2 [ main::i#2 main::i#1 ] Removing always clobbered register reg byte x as potential for zp[1]:2 [ main::i#2 main::i#1 ] Removing always clobbered register reg byte y as potential for zp[1]:2 [ main::i#2 main::i#1 ] +Removing always clobbered register reg byte z as potential for zp[1]:2 [ main::i#2 main::i#1 ] Statement [6] (dword) main::addend#1 ← (dword) main::addend#2 + (byte) main::i#2 [ main::i#2 main::sum#1 main::addend#1 ] ( [ main::i#2 main::sum#1 main::addend#1 ] { } ) always clobbers reg byte a Statement [2] if((byte) main::i#2<(byte) $64) goto main::@2 [ main::i#2 main::sum#2 main::addend#2 ] ( [ main::i#2 main::sum#2 main::addend#2 ] { } ) always clobbers reg byte a -Statement [3] *((const dword*) SCREEN) ← (dword) main::sum#2 [ ] ( [ ] { } ) always clobbers reg byte a reg byte x reg byte y -Statement [5] (dword) main::sum#1 ← (dword) main::sum#2 + (dword) main::addend#2 [ main::i#2 main::addend#2 main::sum#1 ] ( [ main::i#2 main::addend#2 main::sum#1 ] { } ) always clobbers reg byte a reg byte x reg byte y +Statement [3] *((const dword*) SCREEN) ← (dword) main::sum#2 [ ] ( [ ] { } ) always clobbers reg byte a reg byte x reg byte y reg byte z +Statement [5] (dword) main::sum#1 ← (dword) main::sum#2 + (dword) main::addend#2 [ main::i#2 main::addend#2 main::sum#1 ] ( [ main::i#2 main::addend#2 main::sum#1 ] { } ) always clobbers reg byte a reg byte x reg byte y reg byte z Statement [6] (dword) main::addend#1 ← (dword) main::addend#2 + (byte) main::i#2 [ main::i#2 main::sum#1 main::addend#1 ] ( [ main::i#2 main::sum#1 main::addend#1 ] { } ) always clobbers reg byte a Potential registers zp[1]:2 [ main::i#2 main::i#1 ] : zp[1]:2 , Potential registers zp[4]:3 [ main::sum#2 main::sum#1 ] : zp[4]:3 , diff --git a/src/test/ref/cpu-65ce02-b.asm b/src/test/ref/cpu-65ce02-b.asm new file mode 100644 index 000000000..c6d79f5d1 --- /dev/null +++ b/src/test/ref/cpu-65ce02-b.asm @@ -0,0 +1,24 @@ +// Test the 65CE02 CPU +// A program that uses 65CE02 instructions +.cpu _65ce02 +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 +main: { + ldz #0 + __b1: + // for(register(Z) char i=0;i<100;i++) + cpz #$64 + bcc __b2 + // } + rts + __b2: + // SCREEN[i] = i + tza + tax + sta SCREEN,x + // for(register(Z) char i=0;i<100;i++) + inz + jmp __b1 +} diff --git a/src/test/ref/cpu-65ce02-b.cfg b/src/test/ref/cpu-65ce02-b.cfg new file mode 100644 index 000000000..18aa79339 --- /dev/null +++ b/src/test/ref/cpu-65ce02-b.cfg @@ -0,0 +1,16 @@ + +(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) main::i#2<(byte) $64) 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 nomodify byte*) SCREEN + (byte) main::i#2) ← (byte) main::i#2 + [5] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 diff --git a/src/test/ref/cpu-65ce02-b.log b/src/test/ref/cpu-65ce02-b.log new file mode 100644 index 000000000..ace474b01 --- /dev/null +++ b/src/test/ref/cpu-65ce02-b.log @@ -0,0 +1,271 @@ + +CONTROL FLOW GRAPH SSA + +(void()) main() +main: scope:[main] from __start + (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 ← (byte) main::i#2 < (number) $64 + 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 nomodify byte*) SCREEN + (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 + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +(const nomodify byte*) SCREEN = (byte*)(number) $400 +(void()) __start() +(label) __start::@1 +(label) __start::@return +(void()) main() +(bool~) main::$0 +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i !reg byte z +(byte) main::i#0 !reg byte z +(byte) main::i#1 !reg byte z +(byte) main::i#2 !reg byte z +(byte) main::i#3 !reg byte z + +Adding number conversion cast (unumber) $64 in (bool~) main::$0 ← (byte) main::i#2 < (number) $64 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast $64 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) $64 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias main::i#2 = main::i#3 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$0 [3] if((byte) main::i#2<(byte) $64) 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::@1 +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 [6] 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) main::i#2<(byte) $64) 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 nomodify byte*) SCREEN + (byte) main::i#2) ← (byte) main::i#2 + [5] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 + + +VARIABLE REGISTER WEIGHTS +(void()) main() +(byte) main::i !reg byte z +(byte) main::i#1 !reg byte z 22.0 +(byte) main::i#2 !reg byte z 18.333333333333332 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] + +INITIAL ASM +Target platform is c64basic / CSG65CE02 + // File Comments +// Test the 65CE02 CPU +// A program that uses 65CE02 instructions + // Upstart +.cpu _65ce02 +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // 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] -- vbuzz=vbuc1 + ldz #0 + jmp __b1 + // main::@1 + __b1: + // [2] if((byte) main::i#2<(byte) $64) goto main::@2 -- vbuzz_lt_vbuc1_then_la1 + cpz #$64 + bcc __b2 + jmp __breturn + // main::@return + __breturn: + // [3] return + rts + // main::@2 + __b2: + // [4] *((const nomodify byte*) SCREEN + (byte) main::i#2) ← (byte) main::i#2 -- pbuc1_derefidx_vbuzz=vbuzz + tza + tax + sta SCREEN,x + // [5] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuzz=_inc_vbuzz + inz + // [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 + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] *((const nomodify byte*) SCREEN + (byte) main::i#2) ← (byte) main::i#2 [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a reg byte x +Statement [5] (byte) main::i#1 ← ++ (byte) main::i#2 [ main::i#1 ] ( [ main::i#1 ] { } ) always clobbers reg byte z +Potential registers reg byte z [ main::i#2 main::i#1 ] : reg byte z , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 40.33: reg byte z [ main::i#2 main::i#1 ] +Uplift Scope [] + +Uplifting [main] best 251 combination reg byte z [ main::i#2 main::i#1 ] +Uplifting [] best 251 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test the 65CE02 CPU +// A program that uses 65CE02 instructions + // Upstart +.cpu _65ce02 +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // 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] -- vbuzz=vbuc1 + ldz #0 + jmp __b1 + // main::@1 + __b1: + // [2] if((byte) main::i#2<(byte) $64) goto main::@2 -- vbuzz_lt_vbuc1_then_la1 + cpz #$64 + bcc __b2 + jmp __breturn + // main::@return + __breturn: + // [3] return + rts + // main::@2 + __b2: + // [4] *((const nomodify byte*) SCREEN + (byte) main::i#2) ← (byte) main::i#2 -- pbuc1_derefidx_vbuzz=vbuzz + tza + tax + sta SCREEN,x + // [5] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuzz=_inc_vbuzz + inz + // [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 + +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 nomodify byte*) SCREEN = (byte*) 1024 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i !reg byte z +(byte) main::i#1 !reg byte z 22.0 +(byte) main::i#2 !reg byte z 18.333333333333332 + +reg byte z [ main::i#2 main::i#1 ] + + +FINAL ASSEMBLER +Score: 191 + + // File Comments +// Test the 65CE02 CPU +// A program that uses 65CE02 instructions + // Upstart +.cpu _65ce02 +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // 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] -- vbuzz=vbuc1 + ldz #0 + // main::@1 + __b1: + // for(register(Z) char i=0;i<100;i++) + // [2] if((byte) main::i#2<(byte) $64) goto main::@2 -- vbuzz_lt_vbuc1_then_la1 + cpz #$64 + bcc __b2 + // main::@return + // } + // [3] return + rts + // main::@2 + __b2: + // SCREEN[i] = i + // [4] *((const nomodify byte*) SCREEN + (byte) main::i#2) ← (byte) main::i#2 -- pbuc1_derefidx_vbuzz=vbuzz + tza + tax + sta SCREEN,x + // for(register(Z) char i=0;i<100;i++) + // [5] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuzz=_inc_vbuzz + inz + // [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 + diff --git a/src/test/ref/cpu-65ce02-b.sym b/src/test/ref/cpu-65ce02-b.sym new file mode 100644 index 000000000..34b97dff0 --- /dev/null +++ b/src/test/ref/cpu-65ce02-b.sym @@ -0,0 +1,10 @@ +(const nomodify byte*) SCREEN = (byte*) 1024 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i !reg byte z +(byte) main::i#1 !reg byte z 22.0 +(byte) main::i#2 !reg byte z 18.333333333333332 + +reg byte z [ main::i#2 main::i#1 ] diff --git a/src/test/ref/cpu-65ce02.log b/src/test/ref/cpu-65ce02.log index c4dd2463e..d2cad335c 100644 --- a/src/test/ref/cpu-65ce02.log +++ b/src/test/ref/cpu-65ce02.log @@ -155,15 +155,16 @@ main: { REGISTER UPLIFT POTENTIAL REGISTERS Statement [1] (signed byte) main::a#1 ← - (signed byte) main::a#0 [ main::a#1 ] ( [ main::a#1 ] { } ) always clobbers reg byte a Statement [3] (signed byte~) main::$1 ← (signed byte) main::a#1 >> (byte) 2 [ main::$1 ] ( [ main::$1 ] { } ) always clobbers reg byte a -Potential registers zp[1]:2 [ main::a#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , -Potential registers zp[1]:3 [ main::a#1 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , -Potential registers zp[1]:4 [ main::$1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:2 [ main::a#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , reg byte z , +Potential registers zp[1]:3 [ main::a#1 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , reg byte z , +Potential registers zp[1]:4 [ main::$1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , reg byte z , REGISTER UPLIFT SCOPES Uplift Scope [main] 4: zp[1]:2 [ main::a#0 ] 4: zp[1]:4 [ main::$1 ] 3: zp[1]:3 [ main::a#1 ] Uplift Scope [] Uplifting [main] best 27 combination reg byte a [ main::a#0 ] reg byte a [ main::$1 ] reg byte a [ main::a#1 ] +Limited combination testing to 100 combinations of 125 possible. Uplifting [] best 27 combination ASSEMBLER BEFORE OPTIMIZATION