diff --git a/compiler/examples/numbergame.p8 b/compiler/examples/numbergame.p8 index bd2a92c72..b5f5e5ece 100644 --- a/compiler/examples/numbergame.p8 +++ b/compiler/examples/numbergame.p8 @@ -1,81 +1,46 @@ %output prg ~ main { - sub start() -> () { + sub start() { str name = " " - str guess = "0000000000" - byte secretnumber = 0 - byte attempts_left = 10 + str guess = "000000" + byte guessednumber + byte secretnumber + byte attempts_left - ; greeting _vm_write_str("Let's play a number guessing game!\n") _vm_write_str("Enter your name: ") _vm_input_str(name) - _vm_write_char($8d) - _vm_write_char($8d) - _vm_write_str("Hello, ") + _vm_write_str("\nHello, ") _vm_write_str(name) - _vm_write_char($2e) - _vm_write_char($8d) + _vm_write_str(".\nI am thinking of a number from 1 to 100! You'll have to guess it!\n") - secretnumber = make_number() + secretnumber = rnd() % 100 + + for attempts_left in 10 to 1 step -1 { + _vm_write_str("\nYou have ") + _vm_write_num(attempts_left) + _vm_write_str(" guess") + if attempts_left>1 _vm_write_str("es") + _vm_write_str(" left. What is your next guess? ") + _vm_input_str(guess) + guessednumber = str2byte(guess) + if guessednumber==secretnumber { + _vm_write_str("\nYou guessed it, impressive!\n") + _vm_write_str("Thanks for playing.\n") + return + } else { + _vm_write_str("That is too ") + if guessednumber (X) { - byte number - number = rnd() - return rnd() - return number - } -; ; create a secret random number from 1-100 -; c64.RNDA(0) ; fac = rnd(0) -; c64.MUL10() ; fac *= 10 -; c64.MUL10() ; .. and now *100 -; c64.FADDH() ; add 0.5.. -; c64.FADDH() ; and again, so +1 total -; AY = c64flt.GETADRAY() -; secretnumber = A -; ;A=math.randbyte() -; ;A+=c64.RASTER -; ;A-=c64.TIME_LO -; ;X,secretnumber=math.divmod_bytes(A, 99) -; -; c64scr.print_string("I am thinking of a number from 1 to 100!You'll have to guess it!\n") -; -;ask_guess: -; c64scr.print_string("\nYou have ") -; c64scr.print_byte_decimal(attempts_left) -; c64scr.print_string(" guess") -; if(attempts_left>0) c64scr.print_string("es") -; -; c64scr.print_string(" left.\nWhat is your next guess? ") -; Y = c64scr.input_chars(guess) -; c64.CHROUT("\n") -; freadstr_arg = guess -; c64.FREADSTR(A) -; AY = c64flt.GETADRAY() -; if(A==secretnumber) { -; c64scr.print_string("\nThat's my number, impressive!\n") -; goto goodbye -; } -; c64scr.print_string("That is too ") -; if(A > secretnumber) -; c64scr.print_string("low!\n") -; else -; c64scr.print_string("high!\n") -; -; attempts_left-- -; if(attempts_left>0) goto ask_guess -; ; more efficient: if_nz goto ask_guess -; -; ; game over. -; c64scr.print_string("\nToo bad! It was: ") -; c64scr.print_byte_decimal(secretnumber) -; c64.CHROUT("\n") -; -;goodbye: -; c64scr.print_string("\nThanks for playing. Bye!\n") -; return } } diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index ae4df5aa1..eefef9202 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -12,13 +12,19 @@ sub start() { byte[2,3] matrixvar byte[5] barrayvar word[5] warrayvar + ; byte eol = "\n" ;; todo: convert string of len 1 to byte - fvar=test(34455) + fvar=test(15, 222.22) + _vm_write_num(fvar) + _vm_write_char($8d) + fvar=test(17, 333.33) + _vm_write_num(fvar) + _vm_write_char($8d) return -sub test(arg: byte) -> float { - return 44.54 +sub test(arg: byte, f: float) -> float { + return f/arg } } diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index 37f618452..a6618afc3 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -37,13 +37,6 @@ enum class Register { XY } -enum class Statusflag { - Pc, - Pz, - Pv, - Pn -} - enum class BranchCondition { CS, CC, diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index f5e78b211..793babfe2 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -513,6 +513,8 @@ class AstChecker(private val namespace: INameScope, val targetStatement = checkFunctionOrLabelExists(functionCall.target, functionCall) if(targetStatement!=null) checkFunctionCall(targetStatement, functionCall.arglist, functionCall.position) + if(targetStatement is Subroutine && targetStatement.returnvalues.isNotEmpty()) + printWarning("result value of subroutine call is discarded", functionCall.position) return super.process(functionCall) } @@ -524,7 +526,7 @@ class AstChecker(private val namespace: INameScope, // it's a call to a builtin function. val func = BuiltinFunctions[target.name]!! if(args.size!=func.parameters.size) - checkResult.add(SyntaxError("invalid number of parameters", position)) + checkResult.add(SyntaxError("invalid number of arguments", position)) else { for (arg in args.withIndex().zip(func.parameters)) { if(arg.first.value.resultingDatatype(namespace, heap) !in arg.second.possibleDatatypes) @@ -533,7 +535,7 @@ class AstChecker(private val namespace: INameScope, } } else if(target is Subroutine) { if(args.size!=target.parameters.size) - checkResult.add(SyntaxError("invalid number of parameters", position)) + checkResult.add(SyntaxError("invalid number of arguments", position)) else { for (arg in args.withIndex().zip(target.parameters)) { if(arg.first.value.resultingDatatype(namespace, heap) != arg.second.type) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index af2821810..bd9b73784 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -220,6 +220,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, override fun process(subroutine: Subroutine): IStatement { stackvmProg.label(subroutine.scopedname) + // note: the caller has already written the arguments into the subroutine's parameter variables. translate(subroutine.statements) return super.process(subroutine) } @@ -403,7 +404,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, translateFunctionCall(funcname, expr.arglist) } else { when(target) { - is Subroutine -> translateSubroutineCall(target, expr.arglist, expr.parent) + is Subroutine -> translateSubroutineCall(target, expr.arglist) else -> TODO("non-builtin-function call to $target") } } @@ -468,8 +469,12 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, when(targetStmt) { is Label -> stackvmProg.instr(Opcode.CALL, callLabel = targetStmt.scopedname) - is Subroutine -> - translateSubroutineCall(targetStmt, stmt.arglist, stmt) + is Subroutine -> { + translateSubroutineCall(targetStmt, stmt.arglist) + // make sure we clean up the unused result values from the stack. + for(rv in targetStmt.returnvalues) + stackvmProg.instr(Opcode.DISCARD) + } else -> throw AstException("invalid call target node type: ${targetStmt::class}") } @@ -504,9 +509,11 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } } - fun translateSubroutineCall(subroutine: Subroutine, arguments: List, parent: Node) { - for(param in arguments.zip(subroutine.parameters)) { - // @todo push params onto stack in correct order + private fun translateSubroutineCall(subroutine: Subroutine, arguments: List) { + // evaluate the arguments and assign them into the subroutine's argument variables. + for(arg in arguments.zip(subroutine.parameters)) { + translate(arg.first) + stackvmProg.instr(Opcode.POP_VAR, callLabel = subroutine.scopedname+"."+arg.second.name) } stackvmProg.instr(Opcode.CALL, callLabel=subroutine.scopedname) } @@ -666,9 +673,9 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } private fun translate(stmt: Return) { - val returnvalues = (stmt.definingScope() as? Subroutine)?.returnvalues ?: emptyList() - for(value in stmt.values.zip(returnvalues)) { - // @todo assign the return values to the proper return variables + // put the return values on the stack, in reversed order. The caller will process them. + for(value in stmt.values.reversed()) { + translate(value) } stackvmProg.line(stmt.position) stackvmProg.instr(Opcode.RETURN) diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index d2d0432aa..bdda131c4 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -54,15 +54,26 @@ val BuiltinFunctions = mapOf( "clear_carry" to FunctionSignature(false, emptyList(), null), "set_irqd" to FunctionSignature(false, emptyList(), null), "clear_irqd" to FunctionSignature(false, emptyList(), null), - "_vm_write_memchr" to FunctionSignature(false, emptyList(), null), - "_vm_write_memstr" to FunctionSignature(false, emptyList(), null), - "_vm_write_num" to FunctionSignature(false, emptyList(), null), - "_vm_write_char" to FunctionSignature(false, emptyList(), null), - "_vm_write_str" to FunctionSignature(false, emptyList(), null), - "_vm_input_str" to FunctionSignature(false, emptyList(), null), - "_vm_gfx_clearscr" to FunctionSignature(false, emptyList(), null), - "_vm_gfx_pixel" to FunctionSignature(false, emptyList(), null), - "_vm_gfx_text" to FunctionSignature(false, emptyList(), null) + "str2byte" to FunctionSignature(true, listOf(BuiltinFunctionParam("string", listOf(DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))), DataType.BYTE), + "str2word" to FunctionSignature(true, listOf(BuiltinFunctionParam("string", listOf(DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))), DataType.WORD), + "str2float" to FunctionSignature(true, listOf(BuiltinFunctionParam("string", listOf(DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))), DataType.FLOAT), + "_vm_write_memchr" to FunctionSignature(false, listOf(BuiltinFunctionParam("address", listOf(DataType.WORD))), null), + "_vm_write_memstr" to FunctionSignature(false, listOf(BuiltinFunctionParam("address", listOf(DataType.WORD))), null), + "_vm_write_num" to FunctionSignature(false, listOf(BuiltinFunctionParam("number", listOf(DataType.BYTE, DataType.WORD, DataType.FLOAT))), null), + "_vm_write_char" to FunctionSignature(false, listOf(BuiltinFunctionParam("char", listOf(DataType.BYTE))), null), + "_vm_write_str" to FunctionSignature(false, listOf(BuiltinFunctionParam("string", listOf(DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))), null), + "_vm_input_str" to FunctionSignature(false, listOf(BuiltinFunctionParam("intovar", listOf(DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))), null), + "_vm_gfx_clearscr" to FunctionSignature(false, listOf(BuiltinFunctionParam("color", listOf(DataType.BYTE))), null), + "_vm_gfx_pixel" to FunctionSignature(false, listOf( + BuiltinFunctionParam("x", listOf(DataType.BYTE, DataType.WORD)), + BuiltinFunctionParam("y", listOf(DataType.BYTE, DataType.WORD)), + BuiltinFunctionParam("color", listOf(DataType.BYTE))), null), + "_vm_gfx_text" to FunctionSignature(false, listOf( + BuiltinFunctionParam("x", listOf(DataType.BYTE, DataType.WORD)), + BuiltinFunctionParam("y", listOf(DataType.BYTE, DataType.WORD)), + BuiltinFunctionParam("color", listOf(DataType.BYTE)), + BuiltinFunctionParam("text", listOf(DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))), + null) ) diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index 8386e1b60..2338fc231 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -158,6 +158,9 @@ enum class Syscall(val callNr: Short) { FUNC_RND(89), // push a random byte on the stack FUNC_RNDW(90), // push a random word on the stack FUNC_RNDF(91), // push a random float on the stack (between 0.0 and 1.0) + FUNC_STR2BYTE(92), + FUNC_STR2WORD(93), + FUNC_STR2FLOAT(94) // note: not all builtin functions of the Prog8 language are present as functions: // some of them are straight opcodes (such as MSB, LSB, LSL, LSR, ROL, ROR, ROL2, ROR2, and FLT)! @@ -618,7 +621,24 @@ class StackVm(private var traceOutputFile: String?) { else evalstack.push(Value(DataType.BYTE, if (value.array!!.all{v->v!=0}) 1 else 0)) } - else -> throw VmExecutionException("unimplemented syscall $syscall") + Syscall.FUNC_STR2BYTE -> { + val strvar = evalstack.pop() + val str = heap.get(strvar.heapId) + val y = str.str!!.trim().trimEnd('\u0000') + evalstack.push(Value(DataType.BYTE, y.toShort())) + } + Syscall.FUNC_STR2WORD -> { + val strvar = evalstack.pop() + val str = heap.get(strvar.heapId) + val y = str.str!!.trim().trimEnd('\u0000') + evalstack.push(Value(DataType.BYTE, y.toInt())) + } + Syscall.FUNC_STR2FLOAT -> { + val strvar = evalstack.pop() + val str = heap.get(strvar.heapId) + val y = str.str!!.trim().trimEnd('\u0000') + evalstack.push(Value(DataType.BYTE, y.toDouble())) + } } } diff --git a/docs/source/programming.rst b/docs/source/programming.rst index f67f32c85..b77301e12 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -561,6 +561,15 @@ rndw() rndf() returns a pseudo-random float between 0.0 and 1.0 +str2byte(s) + converts string s into the numeric value that s represents (byte). + +str2word(s) + converts string s into the numeric value that s represents (word). + +str2float(s) + converts string s into the numeric value that s represents (float). + lsl(x) Shift the bits in x (byte or word) one position to the left. Bit 0 is set to 0 (and the highest bit is shifted into the status register's Carry flag)