From a01a9e76f9d85cb1e3849590868dd8b315248f8e Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 7 Feb 2020 01:22:07 +0100 Subject: [PATCH] removed bogus clang target fixed various simulator bugs regarding strings and chars --- compiler/build.gradle | 1 + compiler/src/prog8/CompilerMain.kt | 22 +- .../src/prog8/compiler/target/c64/Petscii.kt | 4 +- .../prog8/compiler/target/clang/ClangGen.kt | 331 ------------------ .../target/clang/ClangMachineDefinition.kt | 23 -- compiler/src/prog8/vm/RuntimeValue.kt | 5 +- compiler/src/prog8/vm/astvm/AstVm.kt | 76 ++-- compiler/src/prog8/vm/astvm/ScreenDialog.kt | 39 +-- examples/hello.p8 | 4 +- examples/testarrays.p8 | 5 + gradle.properties | 2 - 11 files changed, 79 insertions(+), 433 deletions(-) delete mode 100644 compiler/src/prog8/compiler/target/clang/ClangGen.kt delete mode 100644 compiler/src/prog8/compiler/target/clang/ClangMachineDefinition.kt diff --git a/compiler/build.gradle b/compiler/build.gradle index ff2e170f4..aac2adae3 100644 --- a/compiler/build.gradle +++ b/compiler/build.gradle @@ -33,6 +33,7 @@ dependencies { // implementation "org.jetbrains.kotlin:kotlin-reflect" implementation 'org.antlr:antlr4-runtime:4.7.2' implementation 'org.jetbrains.kotlinx:kotlinx-cli-jvm:0.1.0-dev-5' + // implementation 'net.razorvine:ksim65:1.6' implementation project(':parser') testImplementation "org.jetbrains.kotlin:kotlin-test-junit5" diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index 38b97061b..a92001446 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -7,8 +7,6 @@ import prog8.compiler.target.CompilationTarget import prog8.compiler.target.c64.C64MachineDefinition import prog8.compiler.target.c64.Petscii import prog8.compiler.target.c64.codegen.AsmGen -import prog8.compiler.target.clang.ClangGen -import prog8.compiler.target.clang.ClangMachineDefinition import prog8.parser.ParsingFailedError import prog8.vm.astvm.AstVm import java.io.IOException @@ -41,9 +39,9 @@ private fun compileMain(args: Array) { val outputDir by cli.flagValueArgument("-out", "directory", "directory for output files instead of current directory", ".") val dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code") val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations") - val launchSimulator by cli.flagArgument("-sim", "launch the prog8 virtual machine/simulator after compilation") - val watchMode by cli.flagArgument("-watch", "continuous compilation mode (watches for file changes)") - val compilationTarget by cli.flagValueArgument("-target", "compilationtgt", "target output of the compiler, one of: c64, clang. default=c64", "c64") + val launchSimulator by cli.flagArgument("-sim", "launch the builtin execution simulator after compilation") + val watchMode by cli.flagArgument("-watch", "continuous compilation mode (watches for file changes), greatly increases compilation speed") + val compilationTarget by cli.flagValueArgument("-target", "compilertarget", "target output of the compiler, currently only 'c64' (C64 6502 assembly) available", "c64") val moduleFiles by cli.positionalArgumentsList("modules", "main module file(s) to compile", minArgs = 1) try { @@ -61,14 +59,6 @@ private fun compileMain(args: Array) { asmGenerator = ::AsmGen } } - "clang" -> { - with(CompilationTarget) { - name = "clang" - machine = ClangMachineDefinition - encodeString = { str -> str.toByteArray().map { it.toShort()} } - asmGenerator = ::ClangGen - } - } else -> { System.err.println("invalid compilation target") exitProcess(1) @@ -124,6 +114,12 @@ private fun compileMain(args: Array) { } if (launchSimulator) { +// val c64 = razorvine.c64emu.C64Machine("C64 emulator launched from Prog8 compiler") +// c64.cpu.addBreakpoint(0xea31) { cpu, address -> +// println("zz") +// Cpu6502.BreakpointResultAction() +// } +// c64.start() println("\nLaunching AST-based simulator...") val vm = AstVm(compilationResult.programAst, compilationTarget) vm.run() diff --git a/compiler/src/prog8/compiler/target/c64/Petscii.kt b/compiler/src/prog8/compiler/target/c64/Petscii.kt index abdab6399..f569c314f 100644 --- a/compiler/src/prog8/compiler/target/c64/Petscii.kt +++ b/compiler/src/prog8/compiler/target/c64/Petscii.kt @@ -1058,7 +1058,7 @@ object Petscii { 0.toShort() else { val case = if (lowercase) "lower" else "upper" - throw CharConversionException("no ${case}case Petscii character for '$it'") + throw CharConversionException("no ${case}case Petscii character for '$it' (${it.toShort()})") } } } @@ -1076,7 +1076,7 @@ object Petscii { 0.toShort() else { val case = if (lowercase) "lower" else "upper" - throw CharConversionException("no ${case}Screencode character for '$it'") + throw CharConversionException("no ${case}Screencode character for '$it' (${it.toShort()})") } } } diff --git a/compiler/src/prog8/compiler/target/clang/ClangGen.kt b/compiler/src/prog8/compiler/target/clang/ClangGen.kt deleted file mode 100644 index ad94d2878..000000000 --- a/compiler/src/prog8/compiler/target/clang/ClangGen.kt +++ /dev/null @@ -1,331 +0,0 @@ -package prog8.compiler.target.clang - -import prog8.ast.Program -import prog8.ast.base.DataType -import prog8.ast.base.VarDeclType -import prog8.ast.expressions.* -import prog8.ast.statements.* -import prog8.compiler.AssemblyError -import prog8.compiler.CompilationOptions -import prog8.compiler.Zeropage -import prog8.compiler.target.IAssemblyGenerator -import prog8.compiler.target.IAssemblyProgram -import java.io.PrintWriter -import java.nio.file.Path - - -internal class ClangGen(private val program: Program, - private val zeropage: Zeropage, - private val options: CompilationOptions, - private val outputDir: Path) : IAssemblyGenerator { - override fun compileToAssembly(optimize: Boolean): IAssemblyProgram { - - println("Generating C++ language code... [UNFINISHED, EXPERIMENTAL]") - - // TODO manually flatten all scopes. Needed because in C++ even with forward declarations it's not possible to freely access members across all classes - - val outputFile = outputDir.resolve("${program.name}.cpp").toFile() - outputFile.printWriter().use { out -> - header(out) - program.allBlocks().forEach { block(out, it) } - footer(out) - } - - return ClangAssemblyProgram(program.name, outputDir) - } - - private fun header(out: PrintWriter) { - out.println("// c++ transpiled version of Prog8 program ${program.name}\n") - out.println("#include ") - out.println(""" -template -class _prog8_range { - public: - struct iterator { - T value; - iterator (T v) : value(v) {} - operator T () const { return value; } - operator T& () { return value; } - T operator* () const { return value; } - iterator& operator++ () { value += iStep; return *this; } - }; - iterator begin() { return iBegin; } - iterator end() { return iEnd; } -}; - -void _prog8_inlineasm(const char* assembly) { /* nothing */ } -void _prog8_goto_addr(uint16_t addr) { /* nothing */ } -void _prog8_directive_asminclude(const char* module, const char* scopename) { /* nothing */ } - -uint8_t _prog8_register_A = 0; -uint8_t _prog8_register_X = 0; -uint8_t _prog8_register_Y = 0; - -uint8_t _prog8_memory[65536]; - - -""") - } - - private fun footer(out: PrintWriter) { - } - - private fun block(out: PrintWriter, block: Block) { - out.println("// block: $block") - out.println("class ${cname(block.name)} {") - out.println("\tconst char* _prog8_linepos = \"${block.position}\";") - out.print("\tvoid* _prog8_block_address = ") - if (block.address == null) - out.println("nullptr;") - else { - out.println("0x${block.address.toString(16)};") - } - out.println("public:") - block.statements.forEach { statement(out, it) } - out.println("};\n") - } - - private fun statement(out: PrintWriter, stmt: Statement) { - when (stmt) { - is BuiltinFunctionStatementPlaceholder -> TODO("$stmt") - is Directive -> directive(out, stmt) - is Label -> out.println("${cname(stmt.name)}:") - is Return -> { - if (stmt.parent is Subroutine) { - if (stmt.value == null) - out.println("return;") - else { - out.println("return ${expression(stmt.value!!)};") - } - } - } - is Continue -> out.println("continue;") - is Break -> out.println("break;") - is VarDecl -> { - val sub = stmt.definingSubroutine() - if (sub == null || sub.parameters.all { it.name != stmt.name }) - vardecl(out, stmt) - } - is Assignment -> assignment(out, stmt) - is PostIncrDecr -> out.println("${assigntarget(stmt.target)}${stmt.operator};") - is Jump -> jump(out, stmt) - is FunctionCallStatement -> out.println("${stmt.target.nameInSource.joinToString("::")}(${arguments(stmt.arglist)});") - is InlineAssembly -> { - val asmlines = stmt.assembly.lines().map { "\"$it\"" }.joinToString("\n") - out.println("_prog8_inlineasm($asmlines);") - } - is AnonymousScope -> { - out.println("{") - stmt.statements.forEach { statement(out, it) } - out.println("}") - } - is NopStatement -> out.println("// NOP") - is Subroutine -> { - if (stmt.isAsmSubroutine) { - out.println("// TODO ASM-SUBROUTINE: ${stmt.name} @ ${stmt.position}") // TODO - } else { - val returnType = datatype(stmt.returntypes.singleOrNull(), null) - val params = stmt.parameters.map { - val dt = datatype(it.type, null) - "${dt.first} ${cname(it.name)}${dt.second}" - } - val paramstr = params.joinToString(", ") - out.println("\nstatic ${returnType.first} ${cname(stmt.name)}${returnType.second}($paramstr) {") - out.println("\tconst char* _prog8_linepos = \"${stmt.position}\";") - stmt.statements.forEach { statement(out, it) } - out.println("}\n") - } - } - is IfStatement -> ifstatement(out, stmt) - is BranchStatement -> branchstatement(out, stmt) - is ForLoop -> forloop(out, stmt) - is WhileLoop -> whileloop(out, stmt) - is RepeatLoop -> repeatloop(out, stmt) - is WhenStatement -> out.println("// stmt: $stmt") // TODO - is StructDecl -> out.println("// stmt: $stmt") // TODO - else -> throw AssemblyError("c++ translation error: unexpected statement: $stmt") - } - } - - private fun assignment(out: PrintWriter, assign: Assignment) { - val target = assigntarget(assign.target) - val operator = assign.aug_op ?: "=" - out.println("$target $operator ${expression(assign.value)};") - } - - private fun assigntarget(target: AssignTarget): String { - return when { - target.register!=null -> "_prog8_register_${target.register}" - target.identifier!=null -> expression(target.identifier!!) - target.memoryAddress!=null -> "(*(uint8_t*)${expression(target.memoryAddress.addressExpression)}) " - target.arrayindexed!=null -> expression(target.arrayindexed!!) - else -> "?????" - } - } - - private fun jump(out: PrintWriter, jmp: Jump) { - when { - jmp.generatedLabel!=null -> out.println("goto ${jmp.generatedLabel};") - jmp.address!=null -> out.println("_prog8_goto_addr(${jmp.address.toString(16)});") - jmp.identifier!=null -> out.println("goto ${expression(jmp.identifier)};") - } - } - - private fun forloop(out: PrintWriter, forlp: ForLoop) { - out.println("// forloop $forlp") // TODO - } - - private fun repeatloop(out: PrintWriter, repeatLoop: RepeatLoop) { - out.println("do {") - statement(out, repeatLoop.body) - out.println("} while(!(${expression(repeatLoop.untilCondition)}));") - } - - private fun whileloop(out: PrintWriter, whileLoop: WhileLoop) { - out.println("while(${expression(whileLoop.condition)}) {") - statement(out, whileLoop.body) - out.println("}") - } - - private fun ifstatement(out: PrintWriter, ifs: IfStatement) { - out.println("if(${expression(ifs.condition)}) {") - statement(out, ifs.truepart) - if(ifs.elsepart.containsCodeOrVars()) { - out.println("} else {") - statement(out, ifs.elsepart) - } - out.println("}") - } - - private fun branchstatement(out: PrintWriter, branch: BranchStatement) { - out.println("if(BranchFlag.${branch.condition}) {") - statement(out, branch.truepart) - if(branch.elsepart.containsCodeOrVars()) { - out.println("} else {") - statement(out, branch.elsepart) - } - out.println("}") - } - - private fun directive(out: PrintWriter, dr: Directive) { - val args = dr.args.map { - when { - it.int!=null -> it.int.toString() - it.name!=null -> "\"${it.name}\"" - it.str!=null -> "\"${it.str}\"" - else -> "" - } - } - out.println("_prog8_directive_${dr.directive.substring(1)}(${args.joinToString(", ")});") - } - - private fun arguments(args: List): String = args.map{expression(it)}.joinToString(", ") - - private fun vardecl(out: PrintWriter, decl: VarDecl) { - fun cdecl(memory: Boolean) { - val dtype = datatype(decl.datatype, decl.arraysize) - out.print(dtype.first) - if (memory) { - out.print("* ${cname(decl.name)}") - } else { - out.print(" ${cname(decl.name)}${dtype.second}") - } - if (decl.value != null) { - // TODO c++ requires the value initialization to be done outside the class in a static assignment... - if (memory) - out.print(" = static_cast<${dtype.first}*>(${expression(decl.value!!)})") - else - out.print(" = ${expression(decl.value!!)}") - } - // TODO zeropage flag - out.println(";") - } - - out.print("static ") - when (decl.type) { - VarDeclType.VAR -> { - cdecl(false) - } - VarDeclType.CONST -> { - out.print("constexpr const ") - cdecl(false) - } - VarDeclType.MEMORY -> { - out.print("constexpr const ") - cdecl(true) - } - } - } - - - private fun expression(expr: Expression): String { - return when (expr) { - is PrefixExpression -> "${expr.operator}${expression(expr.expression)}" - is BinaryExpression -> "${expression(expr.left)} ${expr.operator} ${expression(expr.right)}" - is ArrayIndexedExpression -> "${expression(expr.identifier)}[${expression(expr.arrayspec.index)}]" - is TypecastExpression -> "static_cast<${datatype(expr.type, null)}>(${expression(expr.expression)}) " - is AddressOf -> "&${expr.identifier.nameInSource.joinToString("::")}" - is DirectMemoryRead -> "_prog8_memory[${expression(expr.addressExpression)}]" - is NumericLiteralValue -> "${expr.number}" - is StructLiteralValue -> TODO("struct literal") - is StringLiteralValue -> "\"${expr.value}\"" - is ArrayLiteralValue -> arrayliteral(expr) - is RangeExpr -> rangeexpr(expr) - is RegisterExpr -> "_prog8_register_${expr.register}" - is IdentifierReference -> expr.nameInSource.joinToString("::") - is FunctionCall -> functioncall(expr) - } - } - - private fun rangeexpr(range: RangeExpr): String { - val constrange = range.toConstantIntegerRange() - return if(constrange!=null) { - if(constrange.first>=0 && constrange.last>=0) - "_prog8_range()" - else - "_prog8_range()" - } else { - TODO("rangeexpr $range") - } - } - - private fun functioncall(fc: FunctionCall): String { - val args = fc.arglist.map { expression(it) }.joinToString(", ") - return "${expression(fc.target)}($args)" - } - - private fun arrayliteral(arraylv: ArrayLiteralValue): String { - val values = arraylv.value.map { expression(it) }.joinToString(", ") - return "{ $values }" - } - - private fun cname(name: String): String { - return if (name in listOf("char", "int", "short", "void")) name + "__c_" else name - } - - private fun datatype(dt: DataType?, arraysize: ArrayIndex?): Pair { - return when (dt) { - DataType.UBYTE -> Pair("uint8_t", "") - DataType.BYTE -> Pair("int8_t", "") - DataType.UWORD -> Pair("uint16_t", "") - DataType.WORD -> Pair("int16_t", "") - DataType.FLOAT -> Pair("float", "") - DataType.STR -> Pair("const char*", "") - DataType.STR_S -> Pair("const char*", "") - DataType.ARRAY_UB -> if (arraysize == null) Pair("uint8_t", "[]") else Pair("uint8_t", "[${expression(arraysize.index)}]") - DataType.ARRAY_B -> if (arraysize == null) Pair("int8_t", "[]") else Pair("int8_t", "[${expression(arraysize.index)}]") - DataType.ARRAY_UW -> if (arraysize == null) Pair("uint16_t", "[]") else Pair("uint16_t", "[${expression(arraysize.index)}]") - DataType.ARRAY_W -> if (arraysize == null) Pair("int16_t", "[]") else Pair("int16_t", "[${expression(arraysize.index)}]") - DataType.ARRAY_F -> if (arraysize == null) Pair("float", "[]") else Pair("float", "[${expression(arraysize.index)}]") - DataType.STRUCT -> TODO("struct") - null -> Pair("void", "") - } - } -} - -class ClangAssemblyProgram(override val name: String, outputDir: Path) : IAssemblyProgram { - override fun assemble(options: CompilationOptions) { - println("TODO C++ COMPILE?") - TODO("not implemented - use g++ or clang to compile the code") - } -} diff --git a/compiler/src/prog8/compiler/target/clang/ClangMachineDefinition.kt b/compiler/src/prog8/compiler/target/clang/ClangMachineDefinition.kt deleted file mode 100644 index 5d02f4ebd..000000000 --- a/compiler/src/prog8/compiler/target/clang/ClangMachineDefinition.kt +++ /dev/null @@ -1,23 +0,0 @@ -package prog8.compiler.target.clang - -import prog8.compiler.CompilationOptions -import prog8.compiler.Zeropage -import prog8.compiler.target.IMachineDefinition -import prog8.compiler.target.c64.C64MachineDefinition - -object ClangMachineDefinition: IMachineDefinition { - override val FLOAT_MAX_NEGATIVE: Double = C64MachineDefinition.FLOAT_MAX_NEGATIVE - override val FLOAT_MAX_POSITIVE: Double = C64MachineDefinition.FLOAT_MAX_POSITIVE - override val FLOAT_MEM_SIZE: Int = C64MachineDefinition.FLOAT_MEM_SIZE - override val opcodeNames: Set = C64MachineDefinition.opcodeNames - - override fun getZeropage(compilerOptions: CompilationOptions): Zeropage = ClangZeropage(compilerOptions) - - class ClangZeropage(options: CompilationOptions) : Zeropage(options) { - override val exitProgramStrategy: ExitProgramStrategy = ExitProgramStrategy.SYSTEM_RESET - init { - for (reserved in options.zpReserved) - reserve(reserved) - } - } -} diff --git a/compiler/src/prog8/vm/RuntimeValue.kt b/compiler/src/prog8/vm/RuntimeValue.kt index 34933f2fe..089861a5e 100644 --- a/compiler/src/prog8/vm/RuntimeValue.kt +++ b/compiler/src/prog8/vm/RuntimeValue.kt @@ -6,7 +6,6 @@ import prog8.ast.base.WordDatatypes import prog8.ast.expressions.ArrayLiteralValue import prog8.ast.expressions.NumericLiteralValue import prog8.ast.expressions.StringLiteralValue -import prog8.compiler.target.CompilationTarget import prog8.vm.astvm.VmExecutionException import java.util.Objects import kotlin.math.abs @@ -594,7 +593,7 @@ class RuntimeValueString(type: DataType, val str: String, val heapId: Int?): Run return type == other.type && str == other.str } - fun iterator(): Iterator = CompilationTarget.encodeString(str).iterator() + fun iterator(): Iterator = str.map { it.toShort() }.iterator() override fun numericValue(): Number { throw VmExecutionException("string is not a number") @@ -619,7 +618,7 @@ open class RuntimeValueArray(type: DataType, val array: Array, val heapI if (elt.value is NumericLiteralValue) resultArray.add((elt.value as NumericLiteralValue).number.toInt()) else { - TODO("ADDRESSOF ${elt.value}") + resultArray.add((elt.hashCode())) // ...poor man's implementation of ADDRESSOF(array), it probably won't work very well } } RuntimeValueArray(array.type, resultArray.toTypedArray(), array.heapId!!) diff --git a/compiler/src/prog8/vm/astvm/AstVm.kt b/compiler/src/prog8/vm/astvm/AstVm.kt index 53e75e1d0..ee26e1fd9 100644 --- a/compiler/src/prog8/vm/astvm/AstVm.kt +++ b/compiler/src/prog8/vm/astvm/AstVm.kt @@ -8,10 +8,8 @@ import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.NumericLiteralValue import prog8.ast.statements.* import prog8.compiler.target.c64.C64MachineDefinition -import prog8.compiler.target.c64.Petscii import prog8.vm.* import java.awt.EventQueue -import java.io.CharConversionException import java.util.ArrayDeque import kotlin.NoSuchElementException import kotlin.concurrent.fixedRateTimer @@ -184,7 +182,7 @@ class AstVm(val program: Program, compilationTarget: String) { if(address in 1024..2023) { // write to the screen matrix val scraddr = address-1024 - dialog.canvas.setChar(scraddr % 40, scraddr / 40, value, 1) + dialog.canvas.setScreenChar(scraddr % 40, scraddr / 40, value, 1) } return value } @@ -240,7 +238,7 @@ class AstVm(val program: Program, compilationTarget: String) { } } } - dialog.canvas.printText("\n", true) + dialog.canvas.printAsciiText("\n") println("PROGRAM EXITED!") dialog.title = "PROGRAM EXITED" } catch (tx: VmTerminationException) { @@ -357,7 +355,7 @@ class AstVm(val program: Program, compilationTarget: String) { // swap cannot be implemented as a function, so inline it here executeSwap(stmt) } else { - val args = evaluate(stmt.arglist).map { it as RuntimeValueNumeric } + val args = evaluate(stmt.arglist) performBuiltinFunction(target.name, args, statusflags) } } @@ -625,7 +623,7 @@ class AstVm(val program: Program, compilationTarget: String) { (array as RuntimeValueArray).array[index.integerValue()] = value.numericValue() else if (array.type in StringDatatypes) { val indexInt = index.integerValue() - val newchr = Petscii.decodePetscii(listOf(value.numericValue().toShort()), true) + val newchr = value.numericValue().toChar().toString() val newstr = (array as RuntimeValueString).str.replaceRange(indexInt, indexInt + 1, newchr) val ident = contextStmt.definingScope().lookup(targetArrayIndexed.identifier.nameInSource, contextStmt) as? VarDecl ?: throw VmExecutionException("can't find assignment target ${target.identifier}") @@ -672,48 +670,48 @@ class AstVm(val program: Program, compilationTarget: String) { "c64scr.print" -> { // if the argument is an UWORD, consider it to be the "address" of the string (=heapId) if (args[0].wordval != null) { - val encodedStr = getEncodedStringFromRuntimeVars(args[0].wordval!!) - dialog.canvas.printText(encodedStr) + val string = getAsciiStringFromRuntimeVars(args[0].wordval!!) + dialog.canvas.printAsciiText(string) } else throw VmExecutionException("print non-heap string") } "c64scr.print_ub" -> { - dialog.canvas.printText(args[0].byteval!!.toString(), true) + dialog.canvas.printAsciiText(args[0].byteval!!.toString()) } "c64scr.print_ub0" -> { - dialog.canvas.printText("%03d".format(args[0].byteval!!), true) + dialog.canvas.printAsciiText("%03d".format(args[0].byteval!!)) } "c64scr.print_b" -> { - dialog.canvas.printText(args[0].byteval!!.toString(), true) + dialog.canvas.printAsciiText(args[0].byteval!!.toString()) } "c64scr.print_uw" -> { - dialog.canvas.printText(args[0].wordval!!.toString(), true) + dialog.canvas.printAsciiText(args[0].wordval!!.toString()) } "c64scr.print_uw0" -> { - dialog.canvas.printText("%05d".format(args[0].wordval!!), true) + dialog.canvas.printAsciiText("%05d".format(args[0].wordval!!)) } "c64scr.print_w" -> { - dialog.canvas.printText(args[0].wordval!!.toString(), true) + dialog.canvas.printAsciiText(args[0].wordval!!.toString()) } "c64scr.print_ubhex" -> { val number = args[0].byteval!! val prefix = if (args[1].asBoolean) "$" else "" - dialog.canvas.printText("$prefix${number.toString(16).padStart(2, '0')}", true) + dialog.canvas.printAsciiText("$prefix${number.toString(16).padStart(2, '0')}") } "c64scr.print_uwhex" -> { val number = args[0].wordval!! val prefix = if (args[1].asBoolean) "$" else "" - dialog.canvas.printText("$prefix${number.toString(16).padStart(4, '0')}", true) + dialog.canvas.printAsciiText("$prefix${number.toString(16).padStart(4, '0')}") } "c64scr.print_uwbin" -> { val number = args[0].wordval!! val prefix = if (args[1].asBoolean) "%" else "" - dialog.canvas.printText("$prefix${number.toString(2).padStart(16, '0')}", true) + dialog.canvas.printAsciiText("$prefix${number.toString(2).padStart(16, '0')}") } "c64scr.print_ubbin" -> { val number = args[0].byteval!! val prefix = if (args[1].asBoolean) "%" else "" - dialog.canvas.printText("$prefix${number.toString(2).padStart(8, '0')}", true) + dialog.canvas.printAsciiText("$prefix${number.toString(2).padStart(8, '0')}") } "c64scr.clear_screenchars" -> { dialog.canvas.clearScreen(6) @@ -722,7 +720,7 @@ class AstVm(val program: Program, compilationTarget: String) { dialog.canvas.clearScreen(args[0].integerValue().toShort()) } "c64scr.setcc" -> { - dialog.canvas.setChar(args[0].integerValue(), args[1].integerValue(), args[2].integerValue().toShort(), args[3].integerValue().toShort()) + dialog.canvas.setScreenChar(args[0].integerValue(), args[1].integerValue(), args[2].integerValue().toShort(), args[3].integerValue().toShort()) } "c64scr.plot" -> { dialog.canvas.setCursorPos(args[0].integerValue(), args[1].integerValue()) @@ -738,27 +736,23 @@ class AstVm(val program: Program, compilationTarget: String) { break else { input.add(char) - val printChar = try { - Petscii.encodePetscii("" + char, true).first() - } catch (cv: CharConversionException) { - 0x3f.toShort() - } - dialog.canvas.printPetscii(printChar) + dialog.canvas.printAscii(char) } } - val inputStr = input.joinToString("") + var inputStr = input.joinToString("") + val inputLength = inputStr.length val heapId = args[0].wordval!! - val origStrLength = getEncodedStringFromRuntimeVars(heapId).size - val encodedStr = Petscii.encodePetscii(inputStr, true).take(origStrLength).toMutableList() - while(encodedStr.size < origStrLength) - encodedStr.add(0) - result = RuntimeValueNumeric(DataType.UBYTE, encodedStr.indexOf(0)) + val origStrLength = getAsciiStringFromRuntimeVars(heapId).length + while(inputStr.length < origStrLength) { + inputStr += '\u0000' + } + result = RuntimeValueNumeric(DataType.UBYTE, inputLength) } "c64flt.print_f" -> { - dialog.canvas.printText(args[0].floatval.toString(), false) + dialog.canvas.printAsciiText(args[0].floatval.toString()) } "c64.CHROUT" -> { - dialog.canvas.printPetscii(args[0].byteval!!) + dialog.canvas.printAscii(args[0].byteval!!.toChar()) } "c64.CLEARSCR" -> { dialog.canvas.clearScreen(6) @@ -770,9 +764,16 @@ class AstVm(val program: Program, compilationTarget: String) { val char=dialog.keyboardBuffer.pop() result = RuntimeValueNumeric(DataType.UBYTE, char.toShort()) } + "c64.GETIN" -> { + Thread.sleep(1) + result = if(dialog.keyboardBuffer.isEmpty()) + RuntimeValueNumeric(DataType.UBYTE, 0) + else + RuntimeValueNumeric(DataType.UBYTE, dialog.keyboardBuffer.pop().toShort()) + } "c64utils.str2uword" -> { val heapId = args[0].wordval!! - val argString = getEncodedStringFromRuntimeVars(heapId) + val argString = getAsciiStringFromRuntimeVars(heapId) val numericpart = argString.takeWhile { it.toChar().isDigit() }.toString() result = RuntimeValueNumeric(DataType.UWORD, numericpart.toInt() and 65535) } @@ -782,11 +783,10 @@ class AstVm(val program: Program, compilationTarget: String) { return result } - private fun getEncodedStringFromRuntimeVars(heapId: Int): List { + private fun getAsciiStringFromRuntimeVars(heapId: Int): String { val stringvar = runtimeVariables.getByHeapId(heapId) as RuntimeValueString - return when { - stringvar.type==DataType.STR -> Petscii.encodePetscii(stringvar.str, true) - stringvar.type==DataType.STR_S -> Petscii.encodeScreencode(stringvar.str, true) + return when (stringvar.type) { + DataType.STR, DataType.STR_S -> stringvar.str else -> throw VmExecutionException("weird string type") } } diff --git a/compiler/src/prog8/vm/astvm/ScreenDialog.kt b/compiler/src/prog8/vm/astvm/ScreenDialog.kt index 0225672c9..b1b042216 100644 --- a/compiler/src/prog8/vm/astvm/ScreenDialog.kt +++ b/compiler/src/prog8/vm/astvm/ScreenDialog.kt @@ -6,6 +6,7 @@ import java.awt.* import java.awt.event.KeyEvent import java.awt.event.KeyListener import java.awt.image.BufferedImage +import java.io.CharConversionException import java.util.ArrayDeque import javax.swing.JFrame import javax.swing.JPanel @@ -63,28 +64,22 @@ class BitmapScreenPanel : KeyListener, JPanel() { g2d.drawLine(x1, y1, x2, y2) } - fun printText(text: String, lowercase: Boolean, inverseVideo: Boolean=false) { + fun printAsciiText(text: String) { val t2 = text.substringBefore(0.toChar()) val lines = t2.split('\n') for(line in lines.withIndex()) { - val petscii = Petscii.encodePetscii(line.value, lowercase) - petscii.forEach { printPetscii(it, inverseVideo) } - if(line.index) { - text.forEach { printPetscii(it, false) } - } - - fun printPetscii(char: Short, inverseVideo: Boolean=false) { - if(char==13.toShort() || char==141.toShort()) { + fun printAscii(char: Char) { + if(char=='\n' || char=='\u008d') { cursorX=0 cursorY++ } else { - setPetscii(cursorX, cursorY, char, 1, inverseVideo) + setAsciiChar(cursorX, cursorY, char, 1) cursorX++ if (cursorX >= (SCREENWIDTH / 8)) { cursorY++ @@ -105,28 +100,32 @@ class BitmapScreenPanel : KeyListener, JPanel() { } } - fun writeTextAt(x: Int, y: Int, text: String, color: Short, lowercase: Boolean, inverseVideo: Boolean=false) { + fun writeAsciiTextAt(x: Int, y: Int, text: String, color: Short) { val colorIdx = (color % C64MachineDefinition.colorPalette.size).toShort() var xx=x for(clearx in xx until xx+text.length) { g2d.clearRect(8*clearx, 8*y, 8, 8) } - for(sc in Petscii.encodePetscii(text, lowercase)) { - if(sc==0.toShort()) + for(c in text) { + if(c=='\u0000') break - setPetscii(xx++, y, sc, colorIdx, inverseVideo) + setAsciiChar(xx++, y, c, colorIdx) } } - fun setPetscii(x: Int, y: Int, petscii: Short, color: Short, inverseVideo: Boolean) { + fun setAsciiChar(x: Int, y: Int, char: Char, color: Short) { g2d.clearRect(8*x, 8*y, 8, 8) val colorIdx = (color % C64MachineDefinition.colorPalette.size).toShort() - val screencode = Petscii.petscii2scr(petscii, inverseVideo) + val screencode = try { + Petscii.encodeScreencode(char.toString(), true)[0] + } catch (x: CharConversionException) { + '?'.toShort() + } val coloredImage = C64MachineDefinition.Charset.getColoredChar(screencode, colorIdx) g2d.drawImage(coloredImage, 8*x, 8*y , null) } - fun setChar(x: Int, y: Int, screencode: Short, color: Short) { + fun setScreenChar(x: Int, y: Int, screencode: Short, color: Short) { g2d.clearRect(8*x, 8*y, 8, 8) val colorIdx = (color % C64MachineDefinition.colorPalette.size).toShort() val coloredImage = C64MachineDefinition.Charset.getColoredChar(screencode, colorIdx) diff --git a/examples/hello.p8 b/examples/hello.p8 index 43c66eb81..d97b1dcd0 100644 --- a/examples/hello.p8 +++ b/examples/hello.p8 @@ -23,7 +23,7 @@ main { ; use indexed loop to write characters str bye = "Goodbye!\n" - for char in 0 to len(bye) + for char in 0 to len(bye)-1 c64.CHROUT(bye[char]) @@ -40,6 +40,8 @@ main { c64.CHROUT(':') c64flt.print_f(clock_seconds) c64.CHROUT('\n') + + c64scr.print("bye!\n") } } diff --git a/examples/testarrays.p8 b/examples/testarrays.p8 index dfb353ff2..d864c26fe 100644 --- a/examples/testarrays.p8 +++ b/examples/testarrays.p8 @@ -1,10 +1,15 @@ +%import c64lib %import c64flt %zeropage basicsafe %option enable_floats main { + ; this is only a parser/compiler test, there's no actual working program + sub start() { + c64scr.print("this is only a parser/compiler test\n") + return str s1 = "irmen" str_s s2 = "hello" diff --git a/gradle.properties b/gradle.properties index 73adb4134..36138d2ae 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,5 +2,3 @@ org.gradle.caching=true org.gradle.console=rich org.gradle.parallel=true org.gradle.daemon=true - -kotlinVersion=1.3.61