diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index 549e4a170..6d4c440a4 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -111,7 +111,12 @@ val IntegerDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, Data val NumericDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT) val StringDatatypes = setOf(DataType.STR, DataType.STR_S) val ArrayDatatypes = setOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F) - +val ArrayElementTypes = mapOf( + DataType.ARRAY_B to DataType.BYTE, + DataType.ARRAY_UB to DataType.UBYTE, + DataType.ARRAY_W to DataType.WORD, + DataType.ARRAY_UW to DataType.UWORD, + DataType.ARRAY_F to DataType.FLOAT) class FatalAstException (override var message: String) : Exception(message) diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index 6fe735155..0df1d7f83 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -1132,6 +1132,31 @@ private class AstChecker(private val program: Program, } private fun checkArrayValues(value: LiteralValue, type: DataType): Boolean { + if(value.isArray && value.heapId==null) { + // TODO weird, array literal that hasn't been moved to the heap yet? + val array = value.arrayvalue!!.map { it.constValue(program)!! } + val correct: Boolean + when(type) { + DataType.ARRAY_UB -> { + correct=array.all { it.bytevalue!=null && it.bytevalue in 0..255 } + } + DataType.ARRAY_B -> { + correct=array.all { it.bytevalue!=null && it.bytevalue in -128..127 } + } + DataType.ARRAY_UW -> { + correct=array.all { it.wordvalue!=null && it.wordvalue in 0..65535 } + } + DataType.ARRAY_W -> { + correct=array.all { it.wordvalue!=null && it.wordvalue in -32768..32767} + } + DataType.ARRAY_F -> correct = true + else -> throw AstException("invalid array type $type") + } + if(!correct) + checkResult.add(ExpressionError("array value out of range for type $type", value.position)) + return correct + } + val array = program.heap.get(value.heapId!!) val correct: Boolean when(type) { diff --git a/compiler/src/prog8/ast/StmtReorderer.kt b/compiler/src/prog8/ast/StmtReorderer.kt index 8aa64e882..94103b432 100644 --- a/compiler/src/prog8/ast/StmtReorderer.kt +++ b/compiler/src/prog8/ast/StmtReorderer.kt @@ -267,18 +267,22 @@ private class StatementReorderer(private val program: Program): IAstProcessor { } } is BuiltinFunctionStatementPlaceholder -> { + // if(sub.name in setOf("lsl", "lsr", "rol", "ror", "rol2", "ror2", "memset", "memcopy", "memsetw", "swap")) val func = BuiltinFunctions.getValue(sub.name) - for(arg in func.parameters.zip(call.arglist.withIndex())) { - val argtype = arg.second.value.inferType(program) - if(argtype!=null) { - if(arg.first.possibleDatatypes.any{ argtype == it}) - continue - for(possibleType in arg.first.possibleDatatypes) { - if(argtype isAssignableTo possibleType) { - val typecasted = TypecastExpression(arg.second.value, possibleType, arg.second.value.position) - typecasted.linkParents(arg.second.value.parent) - call.arglist[arg.second.index] = typecasted - break + if(func.pure) { + // non-pure functions don't get automatic typecasts because sometimes they act directly on their parameters + for (arg in func.parameters.zip(call.arglist.withIndex())) { + val argtype = arg.second.value.inferType(program) + if (argtype != null) { + if (arg.first.possibleDatatypes.any { argtype == it }) + continue + for (possibleType in arg.first.possibleDatatypes) { + if (argtype isAssignableTo possibleType) { + val typecasted = TypecastExpression(arg.second.value, possibleType, arg.second.value.position) + typecasted.linkParents(arg.second.value.parent) + call.arglist[arg.second.index] = typecasted + break + } } } } diff --git a/compiler/src/prog8/astvm/AstVm.kt b/compiler/src/prog8/astvm/AstVm.kt index c00566087..8016a2bf0 100644 --- a/compiler/src/prog8/astvm/AstVm.kt +++ b/compiler/src/prog8/astvm/AstVm.kt @@ -96,11 +96,13 @@ class RuntimeVariables { return address } - fun swap(a1: VarDecl, a2: VarDecl) { - val v1 = get(a1.definingScope(), a1.name) - val v2 = get(a2.definingScope(), a2.name) - set(a1.definingScope(), a1.name, v2) - set(a2.definingScope(), a2.name, v1) + fun swap(a1: VarDecl, a2: VarDecl) = swap(a1.definingScope(), a1.name, a2.definingScope(), a2.name) + + fun swap(scope1: INameScope, name1: String, scope2: INameScope, name2: String) { + val v1 = get(scope1, name1) + val v2 = get(scope2, name2) + set(scope1, name1, v2) + set(scope2, name2, v1) } private val vars = mutableMapOf>().withDefault { mutableMapOf() } @@ -160,7 +162,7 @@ class AstVm(val program: Program) { when { jx.generatedLabel != null -> { val label = entrypoint.getLabelOrVariable(jx.generatedLabel) as Label - TODO("$label") + TODO("generatedlabel $label") } jx.identifier != null -> { when (val jumptarget = entrypoint.lookup(jx.identifier.nameInSource, jx.identifier.parent)) { @@ -188,6 +190,7 @@ class AstVm(val program: Program) { private val runtimeVariables = RuntimeVariables() private val functions = BuiltinFunctions() + private val evalCtx = EvalContext(program, mem, statusflags, runtimeVariables, functions, ::executeSubroutine) class LoopControlBreak : Exception() class LoopControlContinue : Exception() @@ -205,8 +208,7 @@ class AstVm(val program: Program) { for (arg in sub.parameters.zip(arguments)) { val idref = IdentifierReference(listOf(arg.first.name), sub.position) performAssignment(AssignTarget(null, idref, null, null, idref.position), - arg.second, sub.statements.first(), - EvalContext(program, mem, statusflags, runtimeVariables, functions, ::executeSubroutine)) + arg.second, sub.statements.first(), evalCtx) } val statements = sub.statements.iterator() @@ -239,8 +241,8 @@ class AstVm(val program: Program) { } } + private fun executeStatement(sub: INameScope, stmt: IStatement) { - val evalCtx = EvalContext(program, mem, statusflags, runtimeVariables, functions, ::executeSubroutine) instructionCounter++ if (instructionCounter % 100 == 0) Thread.sleep(1) @@ -396,11 +398,44 @@ class AstVm(val program: Program) { private fun executeSwap(sub: INameScope, swap: FunctionCallStatement) { // TODO: can swap many different parameters.... in all combinations... - println("TODO SWAP ${swap.arglist}") -// val a1 = (swap.arglist[0] as IdentifierReference).targetVarDecl(program.namespace)!! -// val a2 = (swap.arglist[1] as IdentifierReference).targetVarDecl(program.namespace)!! -// runtimeVariables.swap(a1, a2) -// TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + val v1 = swap.arglist[0] + val v2 = swap.arglist[1] + if(v1 is IdentifierReference && v2 is IdentifierReference) { + val decl1 = v1.targetVarDecl(program.namespace)!! + val decl2 = v2.targetVarDecl(program.namespace)!! + runtimeVariables.swap(decl1, decl2) + return + } + else if (v1 is RegisterExpr && v2 is RegisterExpr) { + runtimeVariables.swap(program.namespace, v1.register.name, program.namespace, v2.register.name) + return + } + else if(v1 is ArrayIndexedExpression && v2 is ArrayIndexedExpression) { + val decl1 = v1.identifier.targetVarDecl(program.namespace)!! + val decl2 = v2.identifier.targetVarDecl(program.namespace)!! + val index1 = evaluate(v1.arrayspec.index, evalCtx) + val index2 = evaluate(v2.arrayspec.index, evalCtx) + val rvar1 = runtimeVariables.get(decl1.definingScope(), decl1.name) + val rvar2 = runtimeVariables.get(decl2.definingScope(), decl2.name) + val val1 = rvar1.array!![index1.integerValue()] + val val2 = rvar2.array!![index2.integerValue()] + val rval1 = RuntimeValue(ArrayElementTypes.getValue(rvar1.type), val1) + val rval2 = RuntimeValue(ArrayElementTypes.getValue(rvar2.type), val2) + performAssignment(AssignTarget(null, null, v1, null, v1.position), rval2, swap, evalCtx) + performAssignment(AssignTarget(null, null, v2, null, v2.position), rval1, swap, evalCtx) + return + } + else if(v1 is DirectMemoryRead && v2 is DirectMemoryRead) { + val address1 = evaluate(v1.addressExpression, evalCtx).wordval!! + val address2 = evaluate(v2.addressExpression, evalCtx).wordval!! + val value1 = evalCtx.mem.getUByte(address1) + val value2 = evalCtx.mem.getUByte(address2) + evalCtx.mem.setUByte(address1, value2) + evalCtx.mem.setUByte(address2, value1) + return + } + + TODO("not implemented swap $v1 $v2") } fun performAssignment(target: AssignTarget, value: RuntimeValue, contextStmt: IStatement, evalCtx: EvalContext) { @@ -424,7 +459,8 @@ class AstVm(val program: Program) { runtimeVariables.set(decl.definingScope(), decl.name, value) } target.memoryAddress != null -> { - TODO("assign memory") + val address = evaluate(target.memoryAddress!!.addressExpression, evalCtx).wordval!! + evalCtx.mem.setUByte(address, value.byteval!!) } target.arrayindexed != null -> { val array = evaluate(target.arrayindexed.identifier, evalCtx) @@ -479,15 +515,11 @@ class AstVm(val program: Program) { private fun oneForCycle(stmt: ForLoop, loopvarDt: DataType, loopValue: Number, loopVar: IdentifierReference) { // assign the new loop value to the loopvar, and run the code performAssignment(AssignTarget(null, loopVar, null, null, loopVar.position), - RuntimeValue(loopvarDt, loopValue), stmt.body.statements.first(), - EvalContext(program, mem, statusflags, runtimeVariables, functions, ::executeSubroutine)) + RuntimeValue(loopvarDt, loopValue), stmt.body.statements.first(), evalCtx) executeAnonymousScope(stmt.body) } - private fun evaluate(args: List): List { - val ctx = EvalContext(program, mem, statusflags, runtimeVariables, functions, ::executeSubroutine) - return args.map { evaluate(it, ctx) } - } + private fun evaluate(args: List) = args.map { evaluate(it, evalCtx) } private fun performSyscall(sub: Subroutine, args: List) { assert(sub.isAsmSubroutine) diff --git a/compiler/src/prog8/astvm/Expressions.kt b/compiler/src/prog8/astvm/Expressions.kt index 9a6c54bd9..1e627ac73 100644 --- a/compiler/src/prog8/astvm/Expressions.kt +++ b/compiler/src/prog8/astvm/Expressions.kt @@ -66,14 +66,7 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue { val array = evaluate(expr.identifier, ctx) val index = evaluate(expr.arrayspec.index, ctx) val value = array.array!![index.integerValue()] - return when(array.type) { - DataType.ARRAY_UB -> RuntimeValue(DataType.UBYTE, num = value) - DataType.ARRAY_B -> RuntimeValue(DataType.BYTE, num = value) - DataType.ARRAY_UW -> RuntimeValue(DataType.UWORD, num = value) - DataType.ARRAY_W -> RuntimeValue(DataType.WORD, num = value) - DataType.ARRAY_F -> RuntimeValue(DataType.FLOAT, num = value) - else -> throw VmExecutionException("strange array type ${array.type}") - } + return RuntimeValue(ArrayElementTypes.getValue(array.type), value) } is TypecastExpression -> { return evaluate(expr.expression, ctx).cast(expr.type) @@ -84,7 +77,8 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue { return RuntimeValue(DataType.UWORD, heapId) } is DirectMemoryRead -> { - TODO("memoryread $expr") + val address = evaluate(expr.addressExpression, ctx).wordval!! + return RuntimeValue(DataType.UBYTE, ctx.mem.getUByte(address)) } is DirectMemoryWrite -> { TODO("memorywrite $expr") diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 10bdbb3e6..4ffcadf45 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -843,12 +843,12 @@ internal class Compiler(private val program: Program): IAstProcessor { val arg = args.single() val dt = arg.inferType(program) when (dt) { - DataType.UBYTE -> prog.instr(Opcode.ROL_BYTE) - DataType.UWORD -> prog.instr(Opcode.ROL_WORD) + in ByteDatatypes -> prog.instr(Opcode.ROL_BYTE) + in WordDatatypes -> prog.instr(Opcode.ROL_WORD) else -> throw CompilerException("wrong datatype") } // this function doesn't return a value on the stack so we pop it directly into the argument register/variable again - popValueIntoTarget(AssignTarget.fromExpr(arg), dt) + popValueIntoTarget(AssignTarget.fromExpr(arg), dt!!) } "ror" -> { val arg = args.single() diff --git a/compiler/src/prog8/compiler/RuntimeValue.kt b/compiler/src/prog8/compiler/RuntimeValue.kt index 12a300743..8b0d45203 100644 --- a/compiler/src/prog8/compiler/RuntimeValue.kt +++ b/compiler/src/prog8/compiler/RuntimeValue.kt @@ -288,13 +288,13 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?= fun rol(carry: Boolean): Pair { // 9 or 17 bit rotate left (with carry)) return when(type) { - DataType.UBYTE -> { + DataType.UBYTE, DataType.BYTE -> { val v = byteval!!.toInt() val newCarry = (v and 0x80) != 0 val newval = (v and 0x7f shl 1) or (if(carry) 1 else 0) Pair(RuntimeValue(DataType.UBYTE, newval), newCarry) } - DataType.UWORD -> { + DataType.UWORD, DataType.WORD -> { val v = wordval!! val newCarry = (v and 0x8000) != 0 val newval = (v and 0x7fff shl 1) or (if(carry) 1 else 0) @@ -307,13 +307,13 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?= fun ror(carry: Boolean): Pair { // 9 or 17 bit rotate right (with carry) return when(type) { - DataType.UBYTE -> { + DataType.UBYTE, DataType.BYTE -> { val v = byteval!!.toInt() val newCarry = v and 1 != 0 val newval = (v ushr 1) or (if(carry) 0x80 else 0) Pair(RuntimeValue(DataType.UBYTE, newval), newCarry) } - DataType.UWORD -> { + DataType.UWORD, DataType.WORD -> { val v = wordval!! val newCarry = v and 1 != 0 val newval = (v ushr 1) or (if(carry) 0x8000 else 0) @@ -326,13 +326,13 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?= fun rol2(): RuntimeValue { // 8 or 16 bit rotate left return when(type) { - DataType.UBYTE -> { + DataType.UBYTE, DataType.BYTE -> { val v = byteval!!.toInt() val carry = (v and 0x80) ushr 7 val newval = (v and 0x7f shl 1) or carry RuntimeValue(DataType.UBYTE, newval) } - DataType.UWORD -> { + DataType.UWORD, DataType.WORD -> { val v = wordval!! val carry = (v and 0x8000) ushr 15 val newval = (v and 0x7fff shl 1) or carry @@ -345,13 +345,13 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?= fun ror2(): RuntimeValue { // 8 or 16 bit rotate right return when(type) { - DataType.UBYTE -> { + DataType.UBYTE, DataType.BYTE -> { val v = byteval!!.toInt() val carry = v and 1 shl 7 val newval = (v ushr 1) or carry RuntimeValue(DataType.UBYTE, newval) } - DataType.UWORD -> { + DataType.UWORD, DataType.WORD -> { val v = wordval!! val carry = v and 1 shl 15 val newval = (v ushr 1) or carry diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index 599d6d182..9d423346d 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -126,11 +126,7 @@ fun builtinFunctionReturnType(function: String, args: List, program return when(dt) { in NumericDatatypes -> dt!! in StringDatatypes -> dt!! - DataType.ARRAY_UB -> DataType.UBYTE - DataType.ARRAY_B -> DataType.BYTE - DataType.ARRAY_UW -> DataType.UWORD - DataType.ARRAY_W -> DataType.WORD - DataType.ARRAY_F -> DataType.FLOAT + in ArrayDatatypes -> ArrayElementTypes.getValue(dt!!) else -> throw FatalAstException("function '$function' requires one argument which is an iterable") } } @@ -157,11 +153,7 @@ fun builtinFunctionReturnType(function: String, args: List, program when(dt) { in NumericDatatypes -> dt in StringDatatypes -> DataType.UBYTE - DataType.ARRAY_UB -> DataType.UBYTE - DataType.ARRAY_B -> DataType.BYTE - DataType.ARRAY_UW -> DataType.UWORD - DataType.ARRAY_W -> DataType.WORD - DataType.ARRAY_F -> DataType.FLOAT + in ArrayDatatypes -> ArrayElementTypes.getValue(dt) else -> null } } diff --git a/examples/test.p8 b/examples/test.p8 index ec368763b..9013dd23f 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -6,17 +6,37 @@ ~ main { sub start() { - ubyte[100] arr1 - ubyte[100] arr2 + ; TODO array to heap ubyte[100] arr1 = 1 to 100 + ; TODO array to heap ubyte[100] arr2 = 101 to 200 + + + &ubyte m1 = $d020 + &uword mw1 = $c000 + + ubyte[] arr1 = [1,2,3,4,5,6,7,8,9,10,11] + ubyte[] arr2 = [11,22,33,44,55,66,77,88,99,100,101] word w1 = 1111 word w2 = 2222 + m1 = 0 + mw1 = 65535 + + Y = @($d020) + @($d020) = A + + ror(w1) + ror2(w1) + rol(w1) + rol2(w1) + lsr(w1) + lsl(w1) + swap(w1, w2) swap(A, Y) - swap(arr1[10], arr2[20]) - swap(arr1[10], Y) - swap(Y, arr2[10]) + swap(arr1[4], arr2[9]) + ; TODO swap(arr1[4], Y) + ; TODO swap(Y, arr2[9]) swap(@($d020), @($d021)) } }