From c90230d33a6402cb9d894992c384621a14c9b392 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 19 Nov 2018 23:39:52 +0100 Subject: [PATCH] tweaking multiple assignment targets --- compiler/examples/swirl-novm.p8 | 31 +++++ compiler/examples/test.p8 | 12 +- compiler/src/prog8/ast/AstChecker.kt | 38 +++--- compiler/src/prog8/compiler/Compiler.kt | 118 ++++++++++++++++-- .../src/prog8/compiler/intermediate/Opcode.kt | 3 + .../src/prog8/compiler/target/c64/AsmGen.kt | 80 +++++++++--- compiler/src/prog8/stackvm/StackVm.kt | 15 +++ 7 files changed, 252 insertions(+), 45 deletions(-) create mode 100644 compiler/examples/swirl-novm.p8 diff --git a/compiler/examples/swirl-novm.p8 b/compiler/examples/swirl-novm.p8 new file mode 100644 index 000000000..68910587b --- /dev/null +++ b/compiler/examples/swirl-novm.p8 @@ -0,0 +1,31 @@ +%import c64utils +%option enable_floats + +~ main { + + const uword width = 320 + const uword height = 200 + + sub start() { + + ;vm_gfx_clearscr(0) + + float t + ubyte color + + while(1) { + float x = sin(t*1.01) + cos(t*1.1234) + float y = cos(t) + sin(t*0.03456) + ;vm_gfx_pixel(screenx(x), screeny(y), color//16) + t += 0.01 + color++ + } + } + + sub screenx(x: float) -> word { + return floor(x * flt(width)/4.1) + width // 2 + } + sub screeny(y: float) -> word { + return floor(y * flt(height)/4.1) + height // 2 + } +} diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index bc7705967..5efa1fd20 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -16,7 +16,12 @@ sub start() { byte b1 - ;v1=foo() ; @todo fix return type value error see sub + ;v1=foo() + + address =c64.MEMBOT(1, 40000.w) ; ok! + address =c64.MEMBOT(1, address) ; ok! + address =c64.MEMBOT(1, memaddr) ; ok!i + ;address =c64.MEMBOT(1, wordarray[1]) ; @todo nice error about loading X register from stack A, Y =c64.GETADR() ; ok! Y, A =c64.GETADR() ; ok! @@ -24,7 +29,6 @@ sub start() { memaddr = c64flt.GETADRAY() ; ok! wordarray[1] = c64flt.GETADRAY() ; ok! v1, v2 =c64.GETADR() ; ok! - address =c64.MEMBOT(1, 0.w) ; ok ! address =c64.IOBASE() ; ok! A = c64.CHRIN() ; ok ! X = c64.CHRIN() ; ok ! @@ -34,8 +38,8 @@ sub start() { } -sub foo() -> ubyte { - return 1 ; @todo not ubyte but byte (if sub returns byte) +sub foo() -> byte { + return 1 ; @todo fix error: '1' as byte literal (not ubyte) } diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index 905f36189..c1237b7e0 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -304,25 +304,27 @@ class AstChecker(private val namespace: INameScope, override fun process(assignment: Assignment): IStatement { // assigning from a functioncall COULD return multiple values (from an asm subroutine) - val stmt = (assignment.value as FunctionCall).target.targetStatement(namespace) - if(stmt is Subroutine && stmt.returntypes.size>1) { - if(stmt.isAsmSubroutine) { - if(stmt.returntypes.size != assignment.targets.size) - checkResult.add(ExpressionError("number of return values doesn't match number of assignment targets", assignment.value.position)) - else { - if(assignment.targets.all{it.register!=null}) { - val returnRegisters = registerSet(stmt.asmReturnvaluesRegisters) - val targetRegisters = assignment.targets.filter { it.register != null }.map { it.register }.toSet() - if (returnRegisters != targetRegisters) - checkResult.add(ExpressionError("asmsub return registers $returnRegisters don't match assignment target registers", assignment.position)) + if(assignment.value is FunctionCall) { + val stmt = (assignment.value as FunctionCall).target.targetStatement(namespace) + if (stmt is Subroutine && stmt.returntypes.size > 1) { + if (stmt.isAsmSubroutine) { + if (stmt.returntypes.size != assignment.targets.size) + checkResult.add(ExpressionError("number of return values doesn't match number of assignment targets", assignment.value.position)) + else { + if (assignment.targets.all { it.register != null }) { + val returnRegisters = registerSet(stmt.asmReturnvaluesRegisters) + val targetRegisters = assignment.targets.filter { it.register != null }.map { it.register }.toSet() + if (returnRegisters != targetRegisters) + checkResult.add(ExpressionError("asmsub return registers $returnRegisters don't match assignment target registers", assignment.position)) + } + for (thing in stmt.returntypes.zip(assignment.targets)) { + if (thing.second.determineDatatype(namespace, heap, assignment) != thing.first) + checkResult.add(ExpressionError("return type mismatch for target ${thing.second.shortString()}", assignment.value.position)) + } } - for(thing in stmt.returntypes.zip(assignment.targets)) { - if(thing.second.determineDatatype(namespace, heap, assignment)!=thing.first) - checkResult.add(ExpressionError("return type mismatch for target ${thing.second.shortString()}", assignment.value.position)) - } - } - } else - checkResult.add(ExpressionError("only asmsub subroutines can return multiple values", assignment.value.position)) + } else + checkResult.add(ExpressionError("only asmsub subroutines can return multiple values", assignment.value.position)) + } } var resultingAssignment = assignment diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index d34834afd..ba6e64066 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -1,6 +1,8 @@ package prog8.compiler +import com.sun.org.apache.xalan.internal.xsltc.cmdline.Compile import prog8.ast.* +import prog8.ast.RegisterOrPair.* import prog8.compiler.intermediate.IntermediateProgram import prog8.compiler.intermediate.Opcode import prog8.compiler.intermediate.Value @@ -792,11 +794,109 @@ private class StatementTranslator(private val prog: IntermediateProgram, private fun translateSubroutineCall(subroutine: Subroutine, arguments: List, callPosition: Position) { // evaluate the arguments and assign them into the subroutine's argument variables. + prog.line(callPosition) + + if(subroutine.asmParameterRegisters.isNotEmpty()) { + if(subroutine.parameters.size!=subroutine.asmParameterRegisters.size) + throw CompilerException("no support for mix of register and non-register subroutine arguments") + + // only register arguments (or status-flag bits) + var carryParam: Boolean? = null + for(arg in arguments.zip(subroutine.asmParameterRegisters)) { + if(arg.second.statusflag!=null) { + if(arg.second.statusflag==Statusflag.Pc) + carryParam = arg.first.constValue(namespace, heap)!!.asBooleanValue + else + throw CompilerException("no support for status flag parameter: ${arg.second.statusflag}") + } else { + when (arg.second.registerOrPair!!) { + A -> { + val assign = Assignment(listOf(AssignTarget(Register.A, null, null, callPosition)), null, arg.first, callPosition) + assign.linkParents(subroutine.parent) + translate(assign) + } + X -> { + // TODO: save X on stack & restore after call + val assign = Assignment(listOf(AssignTarget(Register.X, null, null, callPosition)), null, arg.first, callPosition) + assign.linkParents(subroutine.parent) + translate(assign) + } + Y -> { + val assign = Assignment(listOf(AssignTarget(Register.Y, null, null, callPosition)), null, arg.first, callPosition) + assign.linkParents(subroutine.parent) + translate(assign) + } + AX -> { + // TODO: save X on stack & restore after call + val valueA: IExpression + val valueX: IExpression + val paramDt = arg.first.resultingDatatype(namespace, heap) + if(paramDt==DataType.UBYTE) { + valueA=arg.first + valueX=LiteralValue.optimalInteger(0, callPosition) + val assignA = Assignment(listOf(AssignTarget(Register.A, null, null, callPosition)), null, valueA, callPosition) + val assignX = Assignment(listOf(AssignTarget(Register.X, null, null, callPosition)), null, valueX, callPosition) + assignA.linkParents(subroutine.parent) + assignX.linkParents(subroutine.parent) + translate(assignA) + translate(assignX) + } else if(paramDt==DataType.UWORD) { + translate(arg.first) + prog.instr(Opcode.POP_REGAX_WORD) + } else + throw CompilerException("don't know how to pass register parameter of type $paramDt") + } + AY -> { + val valueA: IExpression + val valueY: IExpression + val paramDt = arg.first.resultingDatatype(namespace, heap) + if(paramDt==DataType.UBYTE) { + valueA=arg.first + valueY=LiteralValue.optimalInteger(0, callPosition) + val assignA = Assignment(listOf(AssignTarget(Register.A, null, null, callPosition)), null, valueA, callPosition) + val assignY = Assignment(listOf(AssignTarget(Register.Y, null, null, callPosition)), null, valueY, callPosition) + assignA.linkParents(subroutine.parent) + assignY.linkParents(subroutine.parent) + translate(assignA) + translate(assignY) + } else if(paramDt==DataType.UWORD) { + translate(arg.first) + prog.instr(Opcode.POP_REGAY_WORD) + } else + throw CompilerException("don't know how to pass register parameter of type $paramDt") + } + XY -> { + // TODO: save X on stack & restore after call + val valueX: IExpression + val valueY: IExpression + val paramDt = arg.first.resultingDatatype(namespace, heap) + if(paramDt==DataType.UBYTE) { + valueX=arg.first + valueY=LiteralValue.optimalInteger(0, callPosition) + val assignX = Assignment(listOf(AssignTarget(Register.X, null, null, callPosition)), null, valueX, callPosition) + val assignY = Assignment(listOf(AssignTarget(Register.Y, null, null, callPosition)), null, valueY, callPosition) + assignX.linkParents(subroutine.parent) + assignY.linkParents(subroutine.parent) + translate(assignX) + translate(assignY) + } else if(paramDt==DataType.UWORD) { + translate(arg.first) + prog.instr(Opcode.POP_REGXY_WORD) + } else + throw CompilerException("don't know how to pass register parameter of type $paramDt") + } + } + } + } + + // carry is set last, to avoid clobbering it when loading the other parameters + when(carryParam) { + true -> prog.instr(Opcode.SEC) + false -> prog.instr(Opcode.CLC) + } - if(subroutine.asmReturnvaluesRegisters.isNotEmpty()) { - TODO("call asmsub by loading registers instead") } else { - prog.line(callPosition) + // only regular (non-register) arguments for (arg in arguments.zip(subroutine.parameters)) { translate(arg.first) val opcode = opcodePopvar(arg.second.type) @@ -1195,22 +1295,22 @@ private class StatementTranslator(private val prog: IntermediateProgram, if(registerOrStatus.statusflag!=null) return when(registerOrStatus.registerOrPair){ - RegisterOrPair.A -> { + A -> { val assignment = Assignment(listOf(target), null, RegisterExpr(Register.A, target.position), target.position) assignment.linkParents(parent) translate(assignment) } - RegisterOrPair.X -> { + X -> { val assignment = Assignment(listOf(target), null, RegisterExpr(Register.X, target.position), target.position) assignment.linkParents(parent) translate(assignment) } - RegisterOrPair.Y -> { + Y -> { val assignment = Assignment(listOf(target), null, RegisterExpr(Register.Y, target.position), target.position) assignment.linkParents(parent) translate(assignment) } - RegisterOrPair.AX -> { + AX -> { // deal with register pair AX: target = A + X*256 val targetDt = target.determineDatatype(namespace, heap, parent) if(targetDt!=DataType.UWORD) @@ -1218,7 +1318,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, prog.instr(Opcode.PUSH_REGAX_WORD) popValueIntoTarget(target, targetDt) } - RegisterOrPair.AY -> { + AY -> { // deal with register pair AY: target = A + Y*256 val targetDt = target.determineDatatype(namespace, heap, parent) if(targetDt!=DataType.UWORD) @@ -1226,7 +1326,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, prog.instr(Opcode.PUSH_REGAY_WORD) popValueIntoTarget(target, targetDt) } - RegisterOrPair.XY -> { + XY -> { // deal with register pair XY: target = X + Y*256 val targetDt = target.determineDatatype(namespace, heap, parent) if(targetDt!=DataType.UWORD) diff --git a/compiler/src/prog8/compiler/intermediate/Opcode.kt b/compiler/src/prog8/compiler/intermediate/Opcode.kt index d555a1a15..678067d39 100644 --- a/compiler/src/prog8/compiler/intermediate/Opcode.kt +++ b/compiler/src/prog8/compiler/intermediate/Opcode.kt @@ -28,6 +28,9 @@ enum class Opcode { POP_VAR_BYTE, // pop (u)byte value into variable POP_VAR_WORD, // pop (u)word value into variable POP_VAR_FLOAT, // pop float value into variable + POP_REGAX_WORD, // pop uword from stack into A/X registers + POP_REGAY_WORD, // pop uword from stack into A/Y registers + POP_REGXY_WORD, // pop uword from stack into X/Y registers // numeric arithmetic ADD_UB, diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index 3f8ee084c..00d721ddf 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -462,6 +462,11 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, Opcode.PUSH_REGAY_WORD -> { " sta ${ESTACK_LO.toHex()},x | tya | sta ${ESTACK_HI.toHex()},x | dex " } + Opcode.POP_REGAX_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself") + Opcode.POP_REGXY_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself") + Opcode.POP_REGAY_WORD -> { + " inx | lda ${ESTACK_LO.toHex()},x | ldy ${ESTACK_HI.toHex()},x " + } Opcode.READ_INDEXED_VAR_BYTE -> { // @todo is this correct? """ @@ -2440,13 +2445,6 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, // ---------- some special operations ------------------ - // var word = AY register pair - AsmPattern(listOf(Opcode.PUSH_REGAY_WORD, Opcode.POP_VAR_WORD)) { segment -> - """ - sta ${segment[1].callLabel} - sty ${segment[1].callLabel}+1 - """ - }, // var word = AX register pair AsmPattern(listOf(Opcode.PUSH_REGAX_WORD, Opcode.POP_VAR_WORD)) { segment -> """ @@ -2454,6 +2452,13 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, stx ${segment[1].callLabel}+1 """ }, + // var word = AY register pair + AsmPattern(listOf(Opcode.PUSH_REGAY_WORD, Opcode.POP_VAR_WORD)) { segment -> + """ + sta ${segment[1].callLabel} + sty ${segment[1].callLabel}+1 + """ + }, // var word = XY register pair AsmPattern(listOf(Opcode.PUSH_REGXY_WORD, Opcode.POP_VAR_WORD)) { segment -> """ @@ -2461,13 +2466,6 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, sty ${segment[1].callLabel}+1 """ }, - // mem word = AY register pair - AsmPattern(listOf(Opcode.PUSH_REGAY_WORD, Opcode.POP_MEM_WORD)) { segment -> - """ - sta ${hexVal(segment[1])} - sty ${hexValPlusOne(segment[1])} - """ - }, // mem word = AX register pair AsmPattern(listOf(Opcode.PUSH_REGAX_WORD, Opcode.POP_MEM_WORD)) { segment -> """ @@ -2475,16 +2473,70 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, stx ${hexValPlusOne(segment[1])} """ }, + // mem word = AY register pair + AsmPattern(listOf(Opcode.PUSH_REGAY_WORD, Opcode.POP_MEM_WORD)) { segment -> + """ + sta ${hexVal(segment[1])} + sty ${hexValPlusOne(segment[1])} + """ + }, // mem word = XY register pair AsmPattern(listOf(Opcode.PUSH_REGXY_WORD, Opcode.POP_MEM_WORD)) { segment -> """ stx ${hexVal(segment[1])} sty ${hexValPlusOne(segment[1])} """ + }, + + // AX register pair = word value + AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.POP_REGAX_WORD)) { segment -> + val value = hexVal(segment[0]) + " lda #<$value | ldx #>$value" + }, + // AY register pair = word value + AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.POP_REGAY_WORD)) { segment -> + val value = hexVal(segment[0]) + " lda #<$value | ldy #>$value" + }, + // XY register pair = word value + AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.POP_REGXY_WORD)) { segment -> + val value = hexVal(segment[0]) + " ldx #<$value | ldy #>$value" + }, + // AX register pair = word var + AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.POP_REGAX_WORD)) { segment -> + " lda #<${segment[0].callLabel} | ldx #>${segment[0].callLabel}" + }, + // AY register pair = word var + AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.POP_REGAY_WORD)) { segment -> + " lda #<${segment[0].callLabel} | ldy #>${segment[0].callLabel}" + }, + // XY register pair = word var + AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.POP_REGXY_WORD)) { segment -> + " ldx #<${segment[0].callLabel} | ldy #>${segment[0].callLabel}" + }, + // AX register pair = mem word + AsmPattern( + listOf(Opcode.PUSH_MEM_UW, Opcode.POP_REGAX_WORD), + listOf(Opcode.PUSH_MEM_W, Opcode.POP_REGAX_WORD)) { segment -> + " lda ${hexVal(segment[0])} | ldx ${hexValPlusOne(segment[0])}" + }, + // AY register pair = mem word + AsmPattern( + listOf(Opcode.PUSH_MEM_UW, Opcode.POP_REGAY_WORD), + listOf(Opcode.PUSH_MEM_W, Opcode.POP_REGAY_WORD)) { segment -> + " lda ${hexVal(segment[0])} | ldy ${hexValPlusOne(segment[0])}" + }, + // XY register pair = mem word + AsmPattern( + listOf(Opcode.PUSH_MEM_UW, Opcode.POP_REGXY_WORD), + listOf(Opcode.PUSH_MEM_W, Opcode.POP_REGXY_WORD)) { segment -> + " ldx ${hexVal(segment[0])} | ldy ${hexValPlusOne(segment[0])}" } + // // @todo assignment: floatarray[idxbyte] = float // AsmPattern(listOf(Opcode.PUSH_FLOAT, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_FLOAT)) { segment -> // val floatConst = getFloatConst(segment[0].arg!!) diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index 9a9da5c71..2c3d66381 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -777,6 +777,21 @@ class StackVm(private var traceOutputFile: String?) { val y=variables["Y"]!!.integerValue() evalstack.push(Value(DataType.UWORD, y*256+x)) } + Opcode.POP_REGAX_WORD -> { + val value=evalstack.pop().integerValue() + variables["A"] = Value(DataType.UBYTE, value and 255) + variables["X"] = Value(DataType.UBYTE, value shr 8) + } + Opcode.POP_REGAY_WORD -> { + val value=evalstack.pop().integerValue() + variables["A"] = Value(DataType.UBYTE, value and 255) + variables["Y"] = Value(DataType.UBYTE, value shr 8) + } + Opcode.POP_REGXY_WORD -> { + val value=evalstack.pop().integerValue() + variables["X"] = Value(DataType.UBYTE, value and 255) + variables["Y"] = Value(DataType.UBYTE, value shr 8) + } Opcode.POP_VAR_BYTE -> { val value = evalstack.pop() checkDt(value, DataType.UBYTE, DataType.BYTE)