diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 1a719979c..2a4ef09b5 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -42,6 +42,9 @@ public class Compiler { /** File name of link script to use (from command line parameter). */ private String linkScriptFileName; + /** Variable optimization/memory area configuration to use (from command line parameter). */ + private VariableBuilderConfig variableBuilderConfig; + public Compiler() { this.program = new Program(); } @@ -55,7 +58,11 @@ public class Compiler { } public void setLinkScriptFileName(String linkScript) { - linkScriptFileName = linkScript; + this.linkScriptFileName = linkScript; + } + + public void setVariableBuilderConfig(VariableBuilderConfig variableBuilderConfig) { + this.variableBuilderConfig = variableBuilderConfig; } public void setUpliftCombinations(int upliftCombinations) { @@ -128,7 +135,16 @@ public class Compiler { program.setStatementSequence(new StatementSequence()); CParser cParser = new CParser(program); KickCParser.FileContext cFileContext = cParser.loadAndParseCFile(fileName, currentPath); - Pass0GenerateStatementSequence pass0GenerateStatementSequence = new Pass0GenerateStatementSequence(cParser, cFileContext, program); + + if(variableBuilderConfig == null) { + VariableBuilderConfig config = new VariableBuilderConfig(); + VariableBuilderConfig.defaultPreConfig(config, program.getLog()); + VariableBuilderConfig.defaultPostConfig(config, program.getLog()); + this.variableBuilderConfig = config; + } + + Pass0GenerateStatementSequence pass0GenerateStatementSequence = new Pass0GenerateStatementSequence(cParser, cFileContext, program, variableBuilderConfig); + pass0GenerateStatementSequence.generate(); StatementSequence sequence = program.getStatementSequence(); @@ -298,8 +314,8 @@ public class Compiler { optimizations.add(new Pass2AliasElimination(program)); optimizations.add(new Pass2IdenticalPhiElimination(program)); optimizations.add(new Pass2DuplicateRValueIdentification(program)); - optimizations.add(() -> { program.clearStatementIndices(); return false; }); - optimizations.add(() -> { program.clearVariableReferenceInfos(); return false; }); + optimizations.add(() -> { program.clearStatementIndices(); return false; }); + optimizations.add(() -> { program.clearVariableReferenceInfos();return false; }); optimizations.add(() -> { program.clearStatementInfos(); return false; }); optimizations.add(new PassNStatementIndices(program)); optimizations.add(new Pass2ConditionalJumpSimplification(program)); diff --git a/src/main/java/dk/camelot64/kickc/KickC.java b/src/main/java/dk/camelot64/kickc/KickC.java index 6ba642443..494b08394 100644 --- a/src/main/java/dk/camelot64/kickc/KickC.java +++ b/src/main/java/dk/camelot64/kickc/KickC.java @@ -3,10 +3,8 @@ package dk.camelot64.kickc; import dk.camelot64.kickc.asm.AsmProgram; import dk.camelot64.kickc.fragment.AsmFragmentTemplate; import dk.camelot64.kickc.fragment.AsmFragmentTemplateUsages; -import dk.camelot64.kickc.model.CompileError; -import dk.camelot64.kickc.model.Program; -import dk.camelot64.kickc.model.TargetCpu; -import dk.camelot64.kickc.model.TargetPlatform; +import dk.camelot64.kickc.model.*; +import dk.camelot64.kickc.model.statements.StatementSource; import kickass.KickAssembler; import kickass.nonasm.c64.CharToPetsciiConverter; import picocli.CommandLine; @@ -16,9 +14,11 @@ import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; +import java.util.stream.Collectors; /** KickC Commandline */ @CommandLine.Command( @@ -132,15 +132,18 @@ public class KickC implements Callable { @CommandLine.Option(names = {"-Si"}, description = "Interleave comments with intermediate language code and ASM fragment names in the generated ASM.") private boolean interleaveIclFile = false; - @CommandLine.Option(names = {"-t", "-target"}, description = "The target system. Default is C64 with BASIC upstart. ") + @CommandLine.Option(names = {"-t", "-target"}, description = "The target system. Default is C64 with BASIC upstart. See #pragma target()") private String target = TargetPlatform.C64BASIC.getName(); - @CommandLine.Option(names = {"-cpu"}, description = "The target CPU. Default is 6502 with illegal opcodes. ") + @CommandLine.Option(names = {"-cpu"}, description = "The target CPU. Default is 6502 with illegal opcodes. See #pragma cpu()") private String cpu = TargetCpu.MOS6502X.getName(); - @CommandLine.Option(names = {"-T", "-link"}, description = "Link using a linker script in KickAss segment format.") + @CommandLine.Option(names = {"-T", "-link"}, description = "Link using a linker script in KickAss segment format. See #pragma link()") private String linkScript = null; + @CommandLine.Option(names = {"-var_model"}, description = "Configure variable optimization/memory area. Default is ssa_zp. See #pragma var_model()") + private String varModel = null; + /** Program Exit Code signaling a compile error. */ public static final int COMPILE_ERROR = 1; @@ -268,6 +271,13 @@ public class KickC implements Callable { compiler.setLinkScriptFileName(linkScript); } + if(varModel!=null) { + List settings = Arrays.asList(varModel.split(",")); + settings = settings.stream().map(String::trim).collect(Collectors.toList()); + final VariableBuilderConfig config = VariableBuilderConfig.fromSettings(settings, StatementSource.NONE, compiler.getLog()); + compiler.setVariableBuilderConfig(config); + } + System.out.println("Compiling " + kcFile); Program program = null; try { diff --git a/src/main/java/dk/camelot64/kickc/model/VariableBuilderConfig.java b/src/main/java/dk/camelot64/kickc/model/VariableBuilderConfig.java index 691e52587..bdabed5b3 100644 --- a/src/main/java/dk/camelot64/kickc/model/VariableBuilderConfig.java +++ b/src/main/java/dk/camelot64/kickc/model/VariableBuilderConfig.java @@ -40,6 +40,32 @@ import java.util.*; */ public class VariableBuilderConfig { + /** + * Create variable builder configuration from a number of settings. + * @param settings The settings. + * @param statementSource The statement source (used for error messages) + * @param program The program log (used for error messages) + * @return A variable builder configuration + */ + public static VariableBuilderConfig fromSettings(List settings, StatementSource statementSource, CompileLog log) { + // Detect if the first setting is "full" + boolean full = false; + if(settings.size() > 0 && settings.get(0).equals(SETTING_FULL)) { + full = true; + settings = settings.subList(1, settings.size()); + } + VariableBuilderConfig config = new VariableBuilderConfig(); + if(!full) + defaultPreConfig(config, log); + // Apply all settings + for(String setting : settings) { + config.addSetting(setting, log, statementSource); + } + if(!full) + defaultPostConfig(config, log); + return config; + } + /** Setting specifying that the Variable Builder config is "full" and the default pre/post should not be applied. */ public static final String SETTING_FULL = "full"; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index 8f118bbbb..17b44ae9d 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -28,6 +28,7 @@ import java.nio.file.Path; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * Generates program SSA form by visiting the ANTLR4 parse tree @@ -51,17 +52,14 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor(); this.defaultMemoryArea = Variable.MemoryArea.ZEROPAGE_MEMORY; - VariableBuilderConfig config = new VariableBuilderConfig(); - VariableBuilderConfig.defaultPreConfig(config, program.getLog()); - VariableBuilderConfig.defaultPostConfig(config, program.getLog()); - this.variableBuilderConfig = config; + this.variableBuilderConfig = variableBuilderConfig; scopeStack.push(program.getScope()); } @@ -107,22 +105,9 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor settings = new ArrayList<>(ctx.NAME()); - // Detect if the first setting is "full" - boolean full = false; - if(settings.size() > 0 && settings.get(0).getText().equals(VariableBuilderConfig.SETTING_FULL)) { - full = true; - settings = settings.subList(1, settings.size()); - } - VariableBuilderConfig config = new VariableBuilderConfig(); - if(!full) - VariableBuilderConfig.defaultPreConfig(config, program.getLog()); - for(TerminalNode varModel : settings) { - config.addSetting(varModel.getText(), program.getLog(), new StatementSource(ctx)); - } - if(!full) - VariableBuilderConfig.defaultPostConfig(config, program.getLog()); - this.variableBuilderConfig = config; + List settingNodes = new ArrayList<>(ctx.NAME()); + List settings = settingNodes.stream().map(ParseTree::getText).collect(Collectors.toList()); + this.variableBuilderConfig = VariableBuilderConfig.fromSettings(settings, new StatementSource(ctx), program.getLog()); return null; } diff --git a/src/test/ref/complex/tetris/test-sprites.asm b/src/test/ref/complex/tetris/test-sprites.asm index ff285e9c1..4e376e803 100644 --- a/src/test/ref/complex/tetris/test-sprites.asm +++ b/src/test/ref/complex/tetris/test-sprites.asm @@ -48,7 +48,7 @@ // Screen Sprite pointers on screen 2 .label PLAYFIELD_SPRITE_PTRS_2 = PLAYFIELD_SCREEN_2+SPRITE_PTRS // Address of the sprites covering the playfield - .label PLAYFIELD_SPRITES = $2000 + .label PLAYFIELD_SPRITES = $3000 // Address of the charset .label PLAYFIELD_CHARSET = $2800 // The Y-position of the first sprite row diff --git a/src/test/ref/complex/tetris/test-sprites.log b/src/test/ref/complex/tetris/test-sprites.log index 075c6805c..8ed32f617 100644 --- a/src/test/ref/complex/tetris/test-sprites.log +++ b/src/test/ref/complex/tetris/test-sprites.log @@ -427,7 +427,7 @@ SYMBOL TABLE SSA (const byte*) PLAYFIELD_CHARSET = (byte*)(number) $2800 (const byte*) PLAYFIELD_SCREEN_1 = (byte*)(number) $400 (const byte*) PLAYFIELD_SCREEN_2 = (byte*)(number) $2c00 -(const byte*) PLAYFIELD_SPRITES = (byte*)(number) $2000 +(const byte*) PLAYFIELD_SPRITES = (byte*)(number) $3000 (const byte*) PLAYFIELD_SPRITE_PTRS_1 = (const byte*) PLAYFIELD_SCREEN_1+(const word) SPRITE_PTRS (const byte*) PLAYFIELD_SPRITE_PTRS_2 = (const byte*) PLAYFIELD_SCREEN_2+(const word) SPRITE_PTRS (const byte*) PROCPORT = (byte*)(number) 1 @@ -779,7 +779,7 @@ Simplifying constant pointer cast (byte*) 56578 Simplifying constant pointer cast (void()**) 65534 Simplifying constant pointer cast (byte*) 1024 Simplifying constant pointer cast (byte*) 11264 -Simplifying constant pointer cast (byte*) 8192 +Simplifying constant pointer cast (byte*) 12288 Simplifying constant pointer cast (byte*) 10240 Simplifying constant integer cast $13 Simplifying constant pointer cast (byte*) 10240 @@ -1631,7 +1631,7 @@ Target platform is c64basic / MOS6502X // Screen Sprite pointers on screen 2 .label PLAYFIELD_SPRITE_PTRS_2 = PLAYFIELD_SCREEN_2+SPRITE_PTRS // Address of the sprites covering the playfield - .label PLAYFIELD_SPRITES = $2000 + .label PLAYFIELD_SPRITES = $3000 // Address of the charset .label PLAYFIELD_CHARSET = $2800 // The Y-position of the first sprite row @@ -2501,7 +2501,7 @@ ASSEMBLER BEFORE OPTIMIZATION // Screen Sprite pointers on screen 2 .label PLAYFIELD_SPRITE_PTRS_2 = PLAYFIELD_SCREEN_2+SPRITE_PTRS // Address of the sprites covering the playfield - .label PLAYFIELD_SPRITES = $2000 + .label PLAYFIELD_SPRITES = $3000 // Address of the charset .label PLAYFIELD_CHARSET = $2800 // The Y-position of the first sprite row @@ -3186,7 +3186,7 @@ FINAL SYMBOL TABLE (const byte*) PLAYFIELD_CHARSET = (byte*) 10240 (const byte*) PLAYFIELD_SCREEN_1 = (byte*) 1024 (const byte*) PLAYFIELD_SCREEN_2 = (byte*) 11264 -(const byte*) PLAYFIELD_SPRITES = (byte*) 8192 +(const byte*) PLAYFIELD_SPRITES = (byte*) 12288 (const byte*) PLAYFIELD_SPRITE_PTRS_1 = (const byte*) PLAYFIELD_SCREEN_1+(const word) SPRITE_PTRS (const byte*) PLAYFIELD_SPRITE_PTRS_2 = (const byte*) PLAYFIELD_SCREEN_2+(const word) SPRITE_PTRS (const byte*) PROCPORT = (byte*) 1 @@ -3393,7 +3393,7 @@ Score: 11662 // Screen Sprite pointers on screen 2 .label PLAYFIELD_SPRITE_PTRS_2 = PLAYFIELD_SCREEN_2+SPRITE_PTRS // Address of the sprites covering the playfield - .label PLAYFIELD_SPRITES = $2000 + .label PLAYFIELD_SPRITES = $3000 // Address of the charset .label PLAYFIELD_CHARSET = $2800 // The Y-position of the first sprite row diff --git a/src/test/ref/complex/tetris/test-sprites.sym b/src/test/ref/complex/tetris/test-sprites.sym index 89ffab6a0..0cd166f9c 100644 --- a/src/test/ref/complex/tetris/test-sprites.sym +++ b/src/test/ref/complex/tetris/test-sprites.sym @@ -19,7 +19,7 @@ (const byte*) PLAYFIELD_CHARSET = (byte*) 10240 (const byte*) PLAYFIELD_SCREEN_1 = (byte*) 1024 (const byte*) PLAYFIELD_SCREEN_2 = (byte*) 11264 -(const byte*) PLAYFIELD_SPRITES = (byte*) 8192 +(const byte*) PLAYFIELD_SPRITES = (byte*) 12288 (const byte*) PLAYFIELD_SPRITE_PTRS_1 = (const byte*) PLAYFIELD_SCREEN_1+(const word) SPRITE_PTRS (const byte*) PLAYFIELD_SPRITE_PTRS_2 = (const byte*) PLAYFIELD_SCREEN_2+(const word) SPRITE_PTRS (const byte*) PROCPORT = (byte*) 1