removed bogus clang target

fixed various simulator bugs regarding strings and chars
This commit is contained in:
Irmen de Jong 2020-02-07 01:22:07 +01:00
parent 357d704aec
commit a01a9e76f9
11 changed files with 79 additions and 433 deletions

View File

@ -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"

View File

@ -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<String>) {
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<String>) {
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<String>) {
}
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()

View File

@ -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()})")
}
}
}

View File

@ -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 <cstdint>")
out.println("""
template <typename T,T iBegin,T iEnd,T iStep=1>
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<Expression>): 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<uint16_t, ${constrange.first}, ${constrange.last}, ${constrange.step}>()"
else
"_prog8_range<int16_t, ${constrange.first}, ${constrange.last}, ${constrange.step}>()"
} 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<String, String> {
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")
}
}

View File

@ -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<String> = 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)
}
}
}

View File

@ -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<Number> = CompilationTarget.encodeString(str).iterator()
fun iterator(): Iterator<Number> = 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<Number>, 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!!)

View File

@ -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<program ended>", true)
dialog.canvas.printAsciiText("\n<program ended>")
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<Short> {
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")
}
}

View File

@ -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<lines.size-1) {
printPetscii(13) // newline
}
line.value.forEach { printAscii(it) }
if(line.index<lines.size-1)
printAscii('\n')
}
}
fun printText(text: Iterable<Short>) {
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)

View File

@ -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")
}
}

View File

@ -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"

View File

@ -2,5 +2,3 @@ org.gradle.caching=true
org.gradle.console=rich
org.gradle.parallel=true
org.gradle.daemon=true
kotlinVersion=1.3.61