diff --git a/compiler/res/prog8lib/c64lib.p8 b/compiler/res/prog8lib/c64lib.p8 index ac9ee9a22..59830b768 100644 --- a/compiler/res/prog8lib/c64lib.p8 +++ b/compiler/res/prog8lib/c64lib.p8 @@ -64,6 +64,7 @@ memory ubyte SP7X = $d00e memory ubyte SP7Y = $d00f memory ubyte[16] SPXY = $d000 ; the 8 sprite X and Y registers as an array. + memory uword[8] SPXYW = $d000 ; the 8 sprite X and Y registers as a combined xy word array. memory ubyte MSIGX = $d010 memory ubyte SCROLY = $d011 diff --git a/compiler/src/prog8/ast/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/AstIdentifiersChecker.kt index 76c00fe75..21fa3fc50 100644 --- a/compiler/src/prog8/ast/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/AstIdentifiersChecker.kt @@ -66,9 +66,9 @@ class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor { // lsb(...) is just an alias for type cast to ubyte, so replace with "... as ubyte" val typecast = TypecastExpression(functionCall.arglist.single(), DataType.UBYTE, functionCall.position) typecast.linkParents(functionCall.parent) - return typecast + return super.process(typecast) } - return functionCall + return super.process(functionCall) } override fun process(decl: VarDecl): IStatement { diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 41ac18c0c..006de5618 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -832,7 +832,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, else -> throw CompilerException("wrong datatype for $funcname()") } } - "msb" -> prog.instr(Opcode.MSB) // note: LSB is not a function, it's just an alias for the cast "... as ubyte" + "msb" -> prog.instr(Opcode.MSB) // note: "lsb" is not a function at all, it's just an alias for the cast "... as ubyte" "mkword" -> prog.instr(Opcode.MKWORD) "lsl" -> { val arg = args.single() diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index 3fd200eb5..57d3c76a5 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -72,7 +72,10 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, for(b in program.blocks) block2asm(b) - optimizeAssembly(assemblyLines) + var optimizationsDone=1 + while(optimizationsDone>0) { + optimizationsDone = optimizeAssembly(assemblyLines) + } File("${program.name}.asm").printWriter().use { for (line in assemblyLines) { it.println(line) } @@ -81,9 +84,10 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, return AssemblyProgram(program.name) } - private fun optimizeAssembly(lines: MutableList) { + private fun optimizeAssembly(lines: MutableList): Int { // sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated. val removeLines = mutableListOf() + var numberOfOptimizations = 0 for(pair in lines.withIndex().windowed(2)) { val first = pair[0].value val second = pair[1].value @@ -97,14 +101,53 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, { removeLines.add(pair[0].index) removeLines.add(pair[1].index) + numberOfOptimizations++ } } for(i in removeLines.reversed()) lines.removeAt(i) + removeLines.clear() + + // sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can be eliminated + for(pair in lines.withIndex().windowed(2)) { + val first = pair[0].value.trim() + val second = pair[1].value.trim() + if(first.startsWith(';') || second.startsWith(';')) + continue // skip over asm comments + + if((first.startsWith("sta ") && second.startsWith("lda ")) || + (first.startsWith("stx ") && second.startsWith("ldx ")) || + (first.startsWith("sty ") && second.startsWith("ldy ")) || + (first.startsWith("lda ") && second.startsWith("lda ")) || + (first.startsWith("ldy ") && second.startsWith("ldy ")) || + (first.startsWith("ldx ") && second.startsWith("ldx ")) || + (first.startsWith("sta ") && second.startsWith("lda ")) || + (first.startsWith("sty ") && second.startsWith("ldy ")) || + (first.startsWith("stx ") && second.startsWith("ldx ")) + ) { + val firstLoc = first.substring(4) + val secondLoc = second.substring(4) + if(firstLoc==secondLoc) { + removeLines.add(pair[1].index) + numberOfOptimizations++ + } + } + } + + + for(i in removeLines.reversed()) + lines.removeAt(i) + removeLines.clear() + + return numberOfOptimizations + } + + private fun out(str: String) { + // TODO: line splitting should be done here instead of at outputFragment + assemblyLines.add(str) } - private fun out(str: String) = assemblyLines.add(str) private fun symname(scoped: String, block: IntermediateProgram.ProgramBlock): String { if(' ' in scoped) @@ -375,6 +418,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, else { val withNewlines = singleAsm.replace('|', '\n') for (line in withNewlines.split('\n')) { + // TODO move line splitting to out() function if (line.isNotEmpty()) { var trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line.trim() trimmed = trimmed.replace(Regex("^\\+\\s+"), "+\t") // sanitize local label indentation @@ -783,7 +827,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, Opcode.CAST_UB_TO_UW, Opcode.CAST_UB_TO_W -> " lda #0 | sta ${(ESTACK_HI+1).toHex()},x" // clear the msb Opcode.CAST_B_TO_UW, Opcode.CAST_B_TO_W -> " lda ${(ESTACK_LO+1).toHex()},x | ${signExtendA("${(ESTACK_HI+1).toHex()},x")}" // sign extend the lsb Opcode.MSB -> " lda ${(ESTACK_HI+1).toHex()},x | sta ${(ESTACK_LO+1).toHex()},x" - // TODO: Opcode.MKWORD -> " inx | lda ${ESTACK_LO.toHex()},x | sta ${(ESTACK_HI+1).toHex()},x " + Opcode.MKWORD -> " inx | lda ${ESTACK_LO.toHex()},x | sta ${(ESTACK_HI+1).toHex()},x " Opcode.ADD_UB, Opcode.ADD_B -> { // TODO inline better? """ diff --git a/examples/wizzine.p8 b/examples/wizzine.p8 index 141fc1f26..d9531f935 100644 --- a/examples/wizzine.p8 +++ b/examples/wizzine.p8 @@ -44,25 +44,24 @@ ~ irq { + sub irq() { + ubyte angle ; no initialization value so it keeps the previous one. -sub irq() { - ubyte angle ; no initialization value so it keeps the previous one. + c64.EXTCOL-- - c64.EXTCOL-- + angle++ + c64.MSIGX=0 - angle++ - c64.MSIGX=0 + for ubyte i in 7 to 0 step -1 { + uword x = sin8u(angle*2-i*8) as uword + 50 + ubyte y = cos8u(angle*3-i*8) / 2 + 70 + c64.SPXYW[i] = mkword(lsb(x), y) + lsl(c64.MSIGX) + if msb(x) c64.MSIGX++ + c64.EXTCOL++ + } + c64.EXTCOL-=7 ; @todo for memory vars, this should not become more than 2 * dec but normal sbc instead - for ubyte i in 14 to 0 step -2 { - uword x = sin8u(angle*2-i*8) as uword + 50 - ubyte y = cos8u(angle*3-i*8) / 2 + 70 - c64.SPXY[i] = lsb(x) - c64.SPXY[i+1] = y - lsl(c64.MSIGX) - if msb(x) c64.MSIGX++ } - c64.EXTCOL++ -} - }