diff --git a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmsubHelpers.kt b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmsubHelpers.kt index 25f4819d4..3480282fb 100644 --- a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmsubHelpers.kt +++ b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmsubHelpers.kt @@ -1,18 +1,31 @@ package prog8.compiler.target.cpu6502.codegen import prog8.ast.base.Cx16VirtualRegisters +import prog8.ast.base.RegisterOrPair import prog8.ast.expressions.* import prog8.ast.statements.Subroutine internal fun asmsub6502ArgsEvalOrder(sub: Subroutine): List { val order = mutableListOf() - // order is: 1) cx16 virtual word registers, 2) actual CPU registers, 3) CPU Carry status flag + // order is: + // 1) cx16 virtual word registers, + // 2) paired CPU registers, + // 3) single CPU registers (X last), + // 4) CPU Carry status flag val args = sub.parameters.zip(sub.asmParameterRegisters).withIndex() val (cx16regs, args2) = args.partition { it.value.second.registerOrPair in Cx16VirtualRegisters } - val (regs, rest) = args2.partition { it.value.second.registerOrPair != null } + val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY) + val (pairedRegs , args3) = args2.partition { it.value.second.registerOrPair in pairedRegisters } + val (regs, rest) = args3.partition { it.value.second.registerOrPair != null } + cx16regs.forEach { order += it.index } - regs.forEach { order += it.index } + pairedRegs.forEach { order += it.index } + regs.forEach { + if(it.value.second.registerOrPair != RegisterOrPair.X) + order += it.index + } + regs.firstOrNull { it.value.second.registerOrPair==RegisterOrPair.X } ?.let { order += it.index} rest.forEach { order += it.index } require(order.size==sub.parameters.size) return order diff --git a/compiler/res/prog8lib/c64/syslib.p8 b/compiler/res/prog8lib/c64/syslib.p8 index 15b70b98a..5fbb9bc0d 100644 --- a/compiler/res/prog8lib/c64/syslib.p8 +++ b/compiler/res/prog8lib/c64/syslib.p8 @@ -625,6 +625,22 @@ _longcopy }} } + inline asmsub rsavex() { + %asm {{ + txa + pha + }} + } + + inline asmsub rrestorex() { + %asm {{ + sta P8ZP_SCRATCH_REG + pla + tax + lda P8ZP_SCRATCH_REG + }} + } + inline asmsub read_flags() -> ubyte @A { %asm {{ php @@ -644,6 +660,15 @@ _longcopy }} } + inline asmsub popx() -> ubyte @X { + %asm {{ + sta P8ZP_SCRATCH_REG + pla + tax + lda P8ZP_SCRATCH_REG + }} + } + inline asmsub pushw(uword value @AY) { %asm {{ pha diff --git a/compiler/res/prog8lib/cx16/syslib.p8 b/compiler/res/prog8lib/cx16/syslib.p8 index c59fe5852..479ed806e 100644 --- a/compiler/res/prog8lib/cx16/syslib.p8 +++ b/compiler/res/prog8lib/cx16/syslib.p8 @@ -880,6 +880,18 @@ sys { }} } + inline asmsub rsavex() { + %asm {{ + phx + }} + } + + inline asmsub rrestorex() { + %asm {{ + plx + }} + } + inline asmsub read_flags() -> ubyte @A { %asm {{ php @@ -899,6 +911,12 @@ sys { }} } + inline asmsub popx() -> ubyte @X { + %asm {{ + plx + }} + } + inline asmsub pushw(uword value @AY) { %asm {{ pha diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 7b6d95065..b70114205 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -265,7 +265,7 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions program.charLiteralsToUByteLiterals(compilerOptions.compTarget) program.constantFold(errors, compilerOptions.compTarget) errors.report() - program.reorderStatements(errors, compilerOptions.compTarget) + program.reorderStatements(errors, compilerOptions) errors.report() program.addTypecasts(errors) errors.report() @@ -327,8 +327,8 @@ private fun writeAssembly(program: Program, program.processAstBeforeAsmGeneration(compilerOptions, errors) errors.report() -// println("*********** AST RIGHT BEFORE ASM GENERATION *************") -// printProgram(program) + println("*********** AST RIGHT BEFORE ASM GENERATION *************") + printProgram(program) compilerOptions.compTarget.machine.initializeZeropage(compilerOptions) val assembly = asmGeneratorFor(compilerOptions.compTarget, diff --git a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt index 05450c92f..14e5f0481 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt @@ -29,8 +29,8 @@ internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationO } } -internal fun Program.reorderStatements(errors: IErrorReporter, target: ICompilationTarget) { - val reorder = StatementReorderer(this, errors, target) +internal fun Program.reorderStatements(errors: IErrorReporter, options: CompilationOptions) { + val reorder = StatementReorderer(this, errors, options) reorder.visit(this) if(errors.noErrors()) { reorder.applyModifications() diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index 71fc90025..13de4ba5b 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -7,11 +7,14 @@ import prog8.ast.statements.* import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification import prog8.compilerinterface.BuiltinFunctions +import prog8.compilerinterface.CompilationOptions import prog8.compilerinterface.ICompilationTarget import prog8.compilerinterface.IErrorReporter -internal class StatementReorderer(val program: Program, val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() { +internal class StatementReorderer(val program: Program, + val errors: IErrorReporter, + private val options: CompilationOptions) : AstWalker() { // Reorders the statements in a way the compiler needs. // - 'main' block must be the very first statement UNLESS it has an address set. // - library blocks are put last. @@ -397,30 +400,91 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport if(function.parameters.isEmpty()) { // 0 params -> just GoSub return listOf(IAstModification.ReplaceNode(call, GoSub(null, call.target, null, call.position), parent)) - } else if(!compTarget.asmsubArgsHaveRegisterClobberRisk(call.args)) { - // no register clobber risk, let the asmgen assign values to registers directly. + } else if(!options.compTarget.asmsubArgsHaveRegisterClobberRisk(call.args)) { + // no register clobber risk, let the asmgen assign values to the registers directly. return noModifications } else { - // TODO new logic for passing arguments to asmsub with clobber risk... - val argEvalOrder = compTarget.asmsubArgsEvalOrder(function) - println("ARGS ORDER OF $call: ${argEvalOrder.toList()}") + // clobber risk; evaluate the arguments on the CPU stack first (in reverse order)... + if (options.slowCodegenWarnings) + errors.warn("slow argument passing used to avoid register clobbering", call.position) + val argOrder = options.compTarget.asmsubArgsEvalOrder(function) + val modifications = mutableListOf() + if(function.shouldSaveX()) + modifications += IAstModification.InsertBefore( + call, + FunctionCallStatement(IdentifierReference(listOf("sys", "rsavex"), call.position), mutableListOf(), true, call.position), + parent as IStatementContainer + ) + argOrder.reversed().forEach { + val arg = call.args[it] + val param = function.parameters[it] + val push = pushCall(arg, param.type, arg.position) + modifications += IAstModification.InsertBefore(call, push, parent as IStatementContainer) + } + // ... and pop them off again into the registers. + argOrder.forEach { + val param = function.parameters[it] + val targetName = function.scopedName + param.name + val popassign = + if(function.asmParameterRegisters[it].registerOrPair == RegisterOrPair.X) + popCallAssignX(targetName, param.type, call.position) + else + popCallAssign(targetName, param.type, call.position) + modifications += IAstModification.InsertBefore(call, popassign, parent as IStatementContainer) + } + if(function.shouldSaveX()) + modifications += IAstModification.InsertAfter( + call, + FunctionCallStatement(IdentifierReference(listOf("sys", "rrestorex"), call.position), mutableListOf(), true, call.position), + parent as IStatementContainer + ) -// function.asmsubArgsEvalOrder().forEach { -// val arg = call.args[it] -// val param = function.parameters[it] -// val paramReg = function.asmParameterRegisters[it] -// when(param.type) { -// DataType.UBYTE -> TODO() -// DataType.BYTE -> TODO() -// DataType.UWORD -> TODO() -// DataType.WORD -> TODO() -// else -> throw FatalAstException("invalidt dt for asmsub param") -// } -// } -// - return noModifications + return modifications + IAstModification.ReplaceNode(call, GoSub(null, call.target, null, call.position), parent) } } + private fun popCallAssignX(targetName: List, dt: DataType, position: Position): Assignment { + val func = IdentifierReference(listOf("sys", "popx"), position) + val popcall = when(dt) { + DataType.UBYTE -> FunctionCall(func, mutableListOf(), position) + DataType.BYTE -> TypecastExpression(FunctionCall(func, mutableListOf(), position), DataType.UBYTE, true, position) + else -> throw FatalAstException("invalid dt $dt") + } + return Assignment( + AssignTarget(IdentifierReference(targetName, position), null, null, position), + popcall, position + ) + } + + private fun popCallAssign(targetName: List, dt: DataType, position: Position): Assignment { + val func = IdentifierReference(listOf("sys", if(dt in ByteDatatypes) "pop" else "popw"), position) + val popcall = when(dt) { + DataType.UBYTE, DataType.UWORD -> FunctionCall(func, mutableListOf(), position) + in PassByReferenceDatatypes -> FunctionCall(func, mutableListOf(), position) + DataType.BYTE -> TypecastExpression(FunctionCall(func, mutableListOf(), position), DataType.UBYTE, true, position) + DataType.WORD -> TypecastExpression(FunctionCall(func, mutableListOf(), position), DataType.UWORD, true, position) + else -> throw FatalAstException("invalid dt $dt") + } + return Assignment( + AssignTarget(IdentifierReference(targetName, position), null, null, position), + popcall, position + ) + } + + private fun pushCall(value: Expression, dt: DataType, position: Position): FunctionCallStatement { + val pushvalue = when(dt) { + DataType.UBYTE, DataType.UWORD -> value + in PassByReferenceDatatypes -> value + DataType.BYTE -> TypecastExpression(value, DataType.UBYTE, true, position) + DataType.WORD -> TypecastExpression(value, DataType.UWORD, true, position) + else -> throw FatalAstException("invalid dt $dt $value") + } + + return FunctionCallStatement( + IdentifierReference(listOf("sys", if(dt in ByteDatatypes) "push" else "pushw"), position), + mutableListOf(pushvalue), + true, position + ) + } } diff --git a/examples/test.p8 b/examples/test.p8 index a4c55d542..ec3f464d0 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,3 +1,4 @@ +%import textio %import test_stack main { @@ -6,28 +7,9 @@ main { test_stack.test() uword @shared uw - -; sys.push(-22 as ubyte) -; sys.push(44) -; sys.pushw(-11234 as uword) -; sys.pushw(12345) -; sys.push(1) -; sys.push(2) -; ubyte @shared ub = sys.pop() -; byte @shared bb = sys.pop() as byte -; uw = sys.popw() -; word @shared ww = sys.popw() as word -; void sys.pop() -; void sys.pop() - - routine2(uw, 11,22, true, 33) - routine2(uw, 11,22, true, 33) - routine2(uw, 11,22, true, 33) - routine2(uw, 11,22, true, 33) - blerp(22) - blerp(22) - blerp(22) - blerp(22) + uw= 0 + routine(uw+11, 22,33, true, 44) + routine2(uw+11, 22,33, true, 44) test_stack.test() @@ -36,14 +18,32 @@ main { } - sub blerp(uword z) { - z++ + sub routine(uword num, ubyte a1, ubyte a2, ubyte switch, byte a3) { + txt.print_uw(num) + txt.spc() + txt.print_ub(a1) + txt.spc() + txt.print_ub(a2) + txt.spc() + txt.print_ub(switch) + txt.spc() + txt.print_b(a3) + txt.nl() } - asmsub routine2(uword num @AY, ubyte a1 @R1, ubyte a2 @R2, ubyte switch @Pc, ubyte a3 @X) { + ; TODO make switch R3 use Pc instead and make that work ! + asmsub routine2(uword num @AY, ubyte a1 @R1, ubyte a2 @R2, ubyte switch @R3, ubyte a3 @X) { %asm {{ - adc #20 - rts + sta routine.num + sty routine.num+1 + lda cx16.r1 + sta routine.a1 + lda cx16.r2 + sta routine.a2 + lda cx16.r3 + sta routine.switch + stx routine.a3 + jmp routine }} }