diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index d10d436d7..95b598c6e 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -39,11 +39,10 @@ sub start() -> () { float time = 0.0 _vm_gfx_clearscr(0) - for X in 3 to 100 step 3/3 { - A=44 + for X in 3 to 100 { + A=X continue break - A=99 } loop: diff --git a/compiler/src/prog8/Main.kt b/compiler/src/prog8/Main.kt index 0fd36e394..eca10572d 100644 --- a/compiler/src/prog8/Main.kt +++ b/compiler/src/prog8/Main.kt @@ -1,13 +1,15 @@ package prog8 -import java.nio.file.Paths import prog8.ast.* -import prog8.parser.* import prog8.compiler.* import prog8.optimizing.constantFold import prog8.optimizing.optimizeStatements import prog8.optimizing.simplifyExpressions +import prog8.parser.ParsingFailedError +import prog8.parser.importModule import java.io.File +import java.io.PrintStream +import java.nio.file.Paths import kotlin.system.exitProcess @@ -74,10 +76,10 @@ fun main(args: Array) { intermediate.optimize() val stackVmFilename = intermediate.name + "_stackvm.txt" - val stackvmFile = File(stackVmFilename).printWriter() - intermediate.toTextLines().forEach { stackvmFile.println(it) } + val stackvmFile = PrintStream(File(stackVmFilename), "utf-8") + intermediate.writeAsText(stackvmFile) stackvmFile.close() - println("StackVM intermediary code written to $stackVmFilename") + println("StackVM program code written to '$stackVmFilename'") // val assembly = stackvmProg.compileToAssembly() // diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index d1a6489c3..4fbaac253 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -1,10 +1,10 @@ package prog8.ast -import prog8.functions.* -import prog8.parser.prog8Parser import org.antlr.v4.runtime.ParserRuleContext import org.antlr.v4.runtime.tree.TerminalNode -import prog8.compiler.Petscii +import prog8.compiler.target.c64.Petscii +import prog8.functions.* +import prog8.parser.prog8Parser import java.nio.file.Paths import kotlin.math.abs import kotlin.math.floor diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index 21644281c..05e963fae 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -74,6 +74,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions: } else { val iterableDt = forLoop.iterable.resultingDatatype(namespace) if (forLoop.loopRegister != null) { + printWarning("using a register as loop variable is risky (it could get clobbered in the body)", forLoop.position) // loop register when (forLoop.loopRegister) { Register.A, Register.X, Register.Y -> { diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 9f4717869..aa9b5c9b2 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -1,18 +1,12 @@ package prog8.compiler import prog8.ast.* -import kotlin.experimental.and -import kotlin.math.absoluteValue -import kotlin.math.pow -import kotlin.system.exitProcess +import prog8.stackvm.* +import java.io.PrintStream class CompilerException(message: String?) : Exception(message) -// 5-byte cbm MFLPT format limitations: -const val FLOAT_MAX_POSITIVE = 1.7014118345e+38 -const val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 - fun Number.toHex(): String { // 0..15 -> "0".."15" @@ -28,82 +22,91 @@ fun Number.toHex(): String { } -data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short) { - companion object { - val zero = Mflpt5(0, 0,0,0,0) - fun fromNumber(num: Number): Mflpt5 { - // see https://en.wikipedia.org/wiki/Microsoft_Binary_Format - // and https://sourceforge.net/p/acme-crossass/code-0/62/tree/trunk/ACME_Lib/cbm/mflpt.a - // and https://en.wikipedia.org/wiki/IEEE_754-1985 +class StackVmProgram(val name: String) { + private val instructions = mutableListOf() + private val variables = mutableMapOf() + private val memory = mutableMapOf>() + private val labels = mutableMapOf() + val numVariables: Int + get() {return variables.size} + val numInstructions: Int + get() {return instructions.size} - val flt = num.toDouble() - if(flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE) - throw CompilerException("floating point number out of 5-byte mflpt range: $this") - if(flt==0.0) - return zero + fun optimize() { + println("\nOptimizing stackVM code...") - val sign = if(flt<0.0) 0x80L else 0x00L - var exponent = 128 + 32 // 128 is cbm's bias, 32 is this algo's bias - var mantissa = flt.absoluteValue - - // if mantissa is too large, shift right and adjust exponent - while(mantissa >= 0x100000000) { - mantissa /= 2.0 - exponent ++ - } - // if mantissa is too small, shift left and adjust exponent - while(mantissa < 0x80000000) { - mantissa *= 2.0 - exponent -- - } - - return when { - exponent<0 -> zero // underflow, use zero instead - exponent>255 -> throw CompilerException("floating point overflow: $this") - exponent==0 -> zero - else -> { - val mantLong = mantissa.toLong() - Mflpt5( - exponent.toShort(), - (mantLong.and(0x7f000000L) ushr 24).or(sign).toShort(), - (mantLong.and(0x00ff0000L) ushr 16).toShort(), - (mantLong.and(0x0000ff00L) ushr 8).toShort(), - (mantLong.and(0x000000ffL)).toShort()) - } - } - } + this.instructions.removeIf { it.opcode==Opcode.NOP && it !is LabelInstr } // remove nops (that are not a label) + // todo optimize stackvm code } - fun toDouble(): Double { - if(this == zero) return 0.0 - val exp = b0 - 128 - val sign = (b1.and(0x80)) > 0 - val number = 0x80000000L.or(b1.toLong() shl 24).or(b2.toLong() shl 16).or(b3.toLong() shl 8).or(b4.toLong()) - val result = number.toDouble() * (2.0).pow(exp) / 0x100000000 - return if(sign) -result else result + fun blockvar(scopedname: String, decl: VarDecl) { + val value = when(decl.datatype) { + DataType.BYTE, DataType.WORD, DataType.FLOAT -> Value(decl.datatype, (decl.value as LiteralValue).asNumericValue) + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> Value(decl.datatype, null, (decl.value as LiteralValue).strvalue) + DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX -> TODO("array/matrix variable values") + } + variables[scopedname] = value + } + + fun writeAsText(out: PrintStream) { + Program(name, instructions, labels, variables, memory).print(out) + } + + fun instr(opcode: Opcode, arg: Value? = null, callLabel: String? = null) { + instructions.add(Instruction(opcode, arg, callLabel)) + } + + fun label(labelname: String) { + val instr = LabelInstr(labelname) + instructions.add(instr) + labels[labelname] = instr + } + + fun line(position: Position) { + instructions.add(Instruction(Opcode.LINE, Value(DataType.STR, null, "${position.line} ${position.file}"))) } } +enum class OutputType { + RAW, + PRG +} + +enum class LauncherType { + BASIC, + NONE +} + +enum class ZeropageType { + BASICSAFE, + KERNALSAFE, + FULL +} + + +data class CompilationOptions(val output: OutputType, + val launcher: LauncherType, + val zeropage: ZeropageType, + val floats: Boolean) + class Compiler(private val options: CompilationOptions) { fun compile(module: Module) : StackVmProgram { println("\nCreating stackVM code...") val namespace = module.definingScope() - val intermediate = StackVmProgram(module.name) - namespace.debugPrint() // create the pool of all variables used in all blocks and scopes val varGather = VarGatherer(intermediate) varGather.process(module) - println("Number of allocated variables and constants: ${intermediate.variables.size} (${intermediate.variablesMemSize} bytes)") + println("Number of allocated variables and constants: ${intermediate.numVariables}") val translator = StatementTranslator(intermediate, namespace) translator.process(module) println("Number of source statements: ${translator.stmtUniqueSequenceNr}") - println("Number of vm instructions: ${intermediate.instructions.size}") + println("Number of vm instructions: ${intermediate.numInstructions}") return intermediate } @@ -146,7 +149,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva "%asmbinary" -> throw CompilerException("can't use %asmbinary in stackvm") "%breakpoint" -> { stackvmProg.line(directive.position) - stackvmProg.instruction("break") + stackvmProg.instr(Opcode.BREAKPOINT) } } return super.process(directive) @@ -178,12 +181,12 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva private fun translate(stmt: Continue) { stackvmProg.line(stmt.position) - stackvmProg.instruction("continue") + TODO("translate CONTINUE") } private fun translate(stmt: Break) { stackvmProg.line(stmt.position) - stackvmProg.instruction("break") + TODO("translate BREAK") } private fun translate(branch: BranchStatement) { @@ -202,23 +205,23 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva val labelElse = makeLabel("else") val labelContinue = makeLabel("continue") val opcode = when(branch.condition) { - BranchCondition.CS -> "bcc" - BranchCondition.CC -> "bcs" - BranchCondition.EQ -> "bne" - BranchCondition.NE -> "beq" - BranchCondition.VS -> "bvc" - BranchCondition.VC -> "bvs" - BranchCondition.MI -> "bpl" - BranchCondition.PL -> "bmi" + BranchCondition.CS -> Opcode.BCC + BranchCondition.CC -> Opcode.BCS + BranchCondition.EQ -> Opcode.BNE + BranchCondition.NE -> Opcode.BEQ + BranchCondition.VS -> TODO("Opcode.BVC") + BranchCondition.VC -> TODO("Opcode.BVS") + BranchCondition.MI -> Opcode.BPL + BranchCondition.PL -> Opcode.BMI } if(branch.elsepart.isEmpty()) { - stackvmProg.instruction("$opcode $labelContinue") + stackvmProg.instr(opcode, callLabel = labelContinue) translate(branch.statements) stackvmProg.label(labelContinue) } else { - stackvmProg.instruction("$opcode $labelElse") + stackvmProg.instr(opcode, callLabel = labelElse) translate(branch.statements) - stackvmProg.instruction("jump $labelContinue") + stackvmProg.instr(Opcode.JUMP, callLabel = labelContinue) stackvmProg.label(labelElse) translate(branch.elsepart) stackvmProg.label(labelContinue) @@ -245,13 +248,13 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva val labelElse = makeLabel("else") val labelContinue = makeLabel("continue") if(stmt.elsepart.isEmpty()) { - stackvmProg.instruction("beq $labelContinue") + stackvmProg.instr(Opcode.BEQ, callLabel = labelContinue) translate(stmt.statements) stackvmProg.label(labelContinue) } else { - stackvmProg.instruction("beq $labelElse") + stackvmProg.instr(Opcode.BEQ, callLabel = labelElse) translate(stmt.statements) - stackvmProg.instruction("jump $labelContinue") + stackvmProg.instr(Opcode.JUMP, callLabel = labelContinue) stackvmProg.label(labelElse) translate(stmt.elsepart) stackvmProg.label(labelContinue) @@ -261,7 +264,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva private fun translate(expr: IExpression) { when(expr) { is RegisterExpr -> { - stackvmProg.instruction("push_var ${expr.register}") + stackvmProg.instr(Opcode.PUSH_VAR, Value(DataType.STR, null, expr.register.toString())) } is BinaryExpression -> { translate(expr.left) @@ -278,7 +281,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva } else { when(target) { is Subroutine -> { - stackvmProg.instruction("call ${target.scopedname}") + stackvmProg.instr(Opcode.CALL, callLabel = target.scopedname) } else -> TODO("non-builtin-function call to $target") } @@ -288,24 +291,24 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva val target = expr.targetStatement(namespace) when(target) { is VarDecl -> { - stackvmProg.instruction("push_var ${target.scopedname}") + stackvmProg.instr(Opcode.PUSH_VAR, Value(DataType.STR, null, target.scopedname)) } else -> throw CompilerException("expression identifierref should be a vardef, not $target") } } is RangeExpr -> { - TODO("TRANSLATE $expr") // todo + TODO("TRANSLATE range $expr") // todo } else -> { val lv = expr.constValue(namespace) ?: throw CompilerException("constant expression required, not $expr") when(lv.type) { - DataType.BYTE -> stackvmProg.instruction("push b:%02x".format(lv.bytevalue!!)) - DataType.WORD -> stackvmProg.instruction("push w:%04x".format(lv.wordvalue!!)) - DataType.FLOAT -> stackvmProg.instruction("push f:${lv.floatvalue}") - DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> stackvmProg.instruction("push \"${lv.strvalue}\"") + DataType.BYTE -> stackvmProg.instr(Opcode.PUSH, Value(DataType.BYTE, lv.bytevalue)) + DataType.WORD -> stackvmProg.instr(Opcode.PUSH, Value(DataType.WORD, lv.wordvalue)) + DataType.FLOAT -> stackvmProg.instr(Opcode.PUSH, Value(DataType.FLOAT, lv.floatvalue)) + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> stackvmProg.instr(Opcode.PUSH, Value(DataType.STR,null, lv.strvalue)) DataType.ARRAY, DataType.ARRAY_W -> { lv.arrayvalue?.forEach { translate(it) } - stackvmProg.instruction("array w:${lv.arrayvalue!!.size.toString(16)}") + stackvmProg.instr(Opcode.ARRAY, Value(DataType.WORD, lv.arrayvalue!!.size)) } DataType.MATRIX -> TODO("matrix type") } @@ -314,28 +317,28 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva } private fun translateBinaryOperator(operator: String) { - val instruction = when(operator) { - "+" -> "add" - "-" -> "sub" - "*" -> "mul" - "/" -> "div" - "%" -> "remainder" - "**" -> "pow" - "&" -> "bitand" - "|" -> "bitor" - "^" -> "bitxor" - "and" -> "and" - "or" -> "or" - "xor" -> "xor" - "<" -> "less" - ">" -> "greater" - "<=" -> "lesseq" - ">=" -> "greatereq" - "==" -> "equal" - "!=" -> "notequal" + val opcode = when(operator) { + "+" -> Opcode.ADD + "-" -> Opcode.SUB + "*" -> Opcode.MUL + "/" -> Opcode.DIV + "%" -> Opcode.REMAINDER + "**" -> Opcode.POW + "&" -> Opcode.BITAND + "|" -> Opcode.BITOR + "^" -> Opcode.BITXOR + "and" -> Opcode.AND + "or" -> Opcode.OR + "xor" -> Opcode.XOR + "<" -> Opcode.LESS + ">" -> Opcode.GREATER + "<=" -> Opcode.LESSEQ + ">=" -> Opcode.GREATEREQ + "==" -> Opcode.EQUAL + "!=" -> Opcode.NOTEQUAL else -> throw FatalAstException("const evaluation for invalid operator $operator") } - stackvmProg.instruction(instruction) + stackvmProg.instr(opcode) } private fun translate(stmt: FunctionCallStatement) { @@ -354,44 +357,50 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva else -> throw AstException("invalid call target node type: ${targetStmt::class}") } stmt.arglist.forEach { translate(it) } - stackvmProg.instruction("call $targetname") + stackvmProg.instr(Opcode.CALL, callLabel = targetname) } private fun createFunctionCall(funcname: String) { - if (funcname.startsWith("_VM_")) - stackvmProg.instruction("syscall ${funcname.substring(4)}") // call builtin function - else - stackvmProg.instruction("syscall FUNC_$funcname") + val function = ( + if (funcname.startsWith("_VM_")) + funcname.substring(4) + else + "FUNC_$funcname" + ).toUpperCase() + val callNr = Syscall.valueOf(function).callNr + stackvmProg.instr(Opcode.SYSCALL, Value(DataType.BYTE, callNr)) } private fun translate(stmt: Jump) { - val instr = - if(stmt.address!=null) { - "jump \$${stmt.address.toString(16)}" - } else { - val target = stmt.identifier!!.targetStatement(namespace)!! - when(target) { - is Label -> "jump ${target.scopedname}" - is Subroutine -> "jump ${target.scopedname}" - else -> throw CompilerException("invalid jump target type ${target::class}") - } - } + var jumpAddress: Value? = null + var jumpLabel: String? = null + + if(stmt.address!=null) { + jumpAddress = Value(DataType.WORD, stmt.address) + } else { + val target = stmt.identifier!!.targetStatement(namespace)!! + jumpLabel = when(target) { + is Label -> target.scopedname + is Subroutine -> target.scopedname + else -> throw CompilerException("invalid jump target type ${target::class}") + } + } stackvmProg.line(stmt.position) - stackvmProg.instruction(instr) + stackvmProg.instr(Opcode.JUMP, jumpAddress, jumpLabel) } private fun translate(stmt: PostIncrDecr) { stackvmProg.line(stmt.position) if(stmt.target.register!=null) { when(stmt.operator) { - "++" -> stackvmProg.instruction("inc_var ${stmt.target.register}") - "--" -> stackvmProg.instruction("dec_var ${stmt.target.register}") + "++" -> stackvmProg.instr(Opcode.INC_VAR, Value(DataType.STR, null, stmt.target.register.toString())) + "--" -> stackvmProg.instr(Opcode.DEC_VAR, Value(DataType.STR, null, stmt.target.register.toString())) } } else { val targetStatement = stmt.target.identifier!!.targetStatement(namespace) as VarDecl when(stmt.operator) { - "++" -> stackvmProg.instruction("inc_var ${targetStatement.scopedname}") - "--" -> stackvmProg.instruction("dec_var ${targetStatement.scopedname}") + "++" -> stackvmProg.instr(Opcode.INC_VAR, Value(DataType.STR, null, targetStatement.scopedname)) + "--" -> stackvmProg.instr(Opcode.DEC_VAR, Value(DataType.STR, null, targetStatement.scopedname)) } } } @@ -407,14 +416,14 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva DataType.BYTE -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") DataType.WORD -> { if(valueDt==DataType.BYTE) - stackvmProg.instruction("b2word") + stackvmProg.instr(Opcode.B2WORD) else throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") } DataType.FLOAT -> { when (valueDt) { - DataType.BYTE -> stackvmProg.instruction("b2float") - DataType.WORD -> stackvmProg.instruction("w2float") + DataType.BYTE -> stackvmProg.instr(Opcode.B2FLOAT) + DataType.WORD -> stackvmProg.instr(Opcode.W2FLOAT) else -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") } } @@ -429,11 +438,11 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva if(stmt.target.identifier!=null) { val target = stmt.target.identifier!!.targetStatement(namespace)!! when(target) { - is VarDecl -> stackvmProg.instruction("push_var ${target.scopedname}") + is VarDecl -> stackvmProg.instr(Opcode.PUSH_VAR, Value(DataType.STR, null, target.scopedname)) else -> throw CompilerException("invalid assignment target type ${target::class}") } } else if(stmt.target.register!=null) { - stackvmProg.instruction("push_var ${stmt.target.register}") + stackvmProg.instr(Opcode.PUSH_VAR, Value(DataType.STR, null, stmt.target.register.toString())) } translateAugAssignOperator(stmt.aug_op) } @@ -442,27 +451,27 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva if(stmt.target.identifier!=null) { val target = stmt.target.identifier!!.targetStatement(namespace)!! when(target) { - is VarDecl -> stackvmProg.instruction("pop_var ${target.scopedname}") + is VarDecl -> stackvmProg.instr(Opcode.POP_VAR, Value(DataType.STR, null, target.scopedname)) else -> throw CompilerException("invalid assignment target type ${target::class}") } } else if(stmt.target.register!=null) { - stackvmProg.instruction("pop_var ${stmt.target.register}") + stackvmProg.instr(Opcode.POP_VAR, Value(DataType.STR, null, stmt.target.register.toString())) } } private fun translateAugAssignOperator(aug_op: String) { - val instruction = when(aug_op) { - "+=" -> "add" - "-=" -> "sub" - "/=" -> "div" - "*=" -> "mul" - "**=" -> "pow" - "&=" -> "bitand" - "|=" -> "bitor" - "^=" -> "bitxor" + val opcode = when(aug_op) { + "+=" -> Opcode.ADD + "-=" -> Opcode.SUB + "/=" -> Opcode.DIV + "*=" -> Opcode.MUL + "**=" -> Opcode.POW + "&=" -> Opcode.BITAND + "|=" -> Opcode.BITOR + "^=" -> Opcode.BITXOR else -> throw CompilerException("invalid aug assignment operator $aug_op") } - stackvmProg.instruction(instruction) + stackvmProg.instr(opcode) } private fun translate(stmt: Return) { @@ -470,161 +479,32 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva TODO("return with value(s) not yet supported: $stmt") } stackvmProg.line(stmt.position) - stackvmProg.instruction("return") + stackvmProg.instr(Opcode.RETURN) } private fun translate(stmt: Label) { stackvmProg.label(stmt.scopedname) } - private fun translate(loop: ForLoop) { stackvmProg.line(loop.position) if(loop.loopRegister!=null) { val reg = loop.loopRegister - println("@TODO translate for loop (register $reg)") // TODO + if(loop.iterable is RangeExpr) { + val range = (loop.iterable as RangeExpr).toKotlinRange() + if(range.isEmpty()) + throw CompilerException("loop over empty range") + if(range.step==1) { + println("@todo for loop, register, range, step 1") // todo + } else { + TODO("loop over range with step != 1") + } + } else { + TODO("loop over something else as a Range: ${loop.iterable}") + } } else { val loopvar = (loop.loopVar!!.targetStatement(namespace) as VarDecl) println("@TODO translate for loop (variable $loopvar)") // TODO } } } - - -class StackVmProgram(val name: String) { - - val variables = mutableMapOf() - val instructions = mutableListOf() - val variablesMemSize: Int - get() { - return variables.values.fold(0) { acc, vardecl -> acc+vardecl.memorySize} - } - - fun optimize() { - println("\nOptimizing stackVM code...") - // todo optimize stackvm code - } - - fun compileToAssembly(): AssemblyResult { - println("\nGenerating assembly code from stackvmProg code... ") - // todo generate 6502 assembly - return AssemblyResult(name) - } - - fun blockvar(scopedname: String, decl: VarDecl) { - variables[scopedname] = decl - } - - fun toTextLines() : List { - val result = mutableListOf("; stackvm program code for: $name") - result.add("%memory") - // todo memory lines for initial memory initialization - result.add("%end_memory") - result.add("%variables") - for(v in variables) { - if (!(v.value.type == VarDeclType.VAR || v.value.type == VarDeclType.CONST)) { - throw AssertionError("Should be only VAR or CONST variables") - } - val litval = v.value.value as LiteralValue - val litvalStr = when(litval.type) { - DataType.BYTE -> litval.bytevalue!!.toString(16) - DataType.WORD -> litval.wordvalue!!.toString(16) - DataType.FLOAT -> litval.floatvalue.toString() - DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> "\"${litval.strvalue}\"" - else -> TODO("non-scalar value") - } - val line = "${v.key} ${v.value.datatype.toString().toLowerCase()} $litvalStr" - result.add(line) - } - result.add("%end_variables") - result.add("%instructions") - result.addAll(instructions) - result.add("%end_instructions") - return result - } - - fun instruction(s: String) { - instructions.add(" $s") - } - - fun label(name: String) { - instructions.add("$name:") - } - - fun line(position: Position) { - instructions.add(" _line ${position.line} ${position.file}") - } -} - -enum class OutputType { - RAW, - PRG -} - -enum class LauncherType { - BASIC, - NONE -} - -enum class ZeropageType { - BASICSAFE, - KERNALSAFE, - FULL -} - - -data class CompilationOptions(val output: OutputType, - val launcher: LauncherType, - val zeropage: ZeropageType, - val floats: Boolean) - - -class AssemblyResult(val name: String) { - fun assemble(options: CompilationOptions, inputfilename: String, outputfilename: String) { - println("\nGenerating machine code program...") - - val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "-Wall", "-Wno-strict-bool", - "--dump-labels", "--vice-labels", "-l", "$outputfilename.vice-mon-list", - "--no-monitor", "--output", outputfilename, inputfilename) - - when(options.output) { - OutputType.PRG -> { - command.add("--cbm-prg") - println("\nCreating C-64 prg.") - } - OutputType.RAW -> { - command.add("--nostart") - println("\nCreating raw binary.") - } - } - - val proc = ProcessBuilder(command).inheritIO().start() - val result = proc.waitFor() - if(result!=0) { - System.err.println("assembler failed with returncode $result") - exitProcess(result) - } - } - - fun generateBreakpointList(): String { - // todo build breakpoint list -/* - def generate_breakpoint_list(self, program_filename: str) -> str: - breakpoints = [] - vice_mon_file = program_filename + ".vice-mon-list" - with open(vice_mon_file, "rU") as f: - for line in f: - match = re.fullmatch(r"al (?P
\w+) \S+_prog8_breakpoint_\d+.?", line, re.DOTALL) - if match: - breakpoints.append("$" + match.group("address")) - with open(vice_mon_file, "at") as f: - print("; vice monitor breakpoint list now follows", file=f) - print("; {:d} breakpoints have been defined here".format(len(breakpoints)), file=f) - print("del", file=f) - for b in breakpoints: - print("break", b, file=f) - return vice_mon_file - */ - return "monitorfile.txt" - } -} \ No newline at end of file diff --git a/compiler/src/prog8/compiler/Zeropage.kt b/compiler/src/prog8/compiler/Zeropage.kt index 1b1f1ba46..8b4602df6 100644 --- a/compiler/src/prog8/compiler/Zeropage.kt +++ b/compiler/src/prog8/compiler/Zeropage.kt @@ -3,50 +3,16 @@ package prog8.compiler import prog8.ast.* -class Zeropage(private val options: CompilationOptions) { - - companion object { - const val SCRATCH_B1 = 0x02 - const val SCRATCH_B2 = 0x03 - const val SCRATCH_W1 = 0xfb // $fb/$fc - const val SCRATCH_W2 = 0xfd // $fd/$fe - } +abstract class Zeropage(protected val options: CompilationOptions) { private val allocations = mutableMapOf>() - val free = mutableListOf() - - init { - if(options.zeropage==ZeropageType.FULL) { - free.addAll(0x04 .. 0xfa) - free.add(0xff) - free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ - } else { - if(options.zeropage==ZeropageType.KERNALSAFE) { - // add the Zp addresses that are just used by BASIC routines to the free list - free.addAll(listOf(0x09, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, - 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, - 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, - 0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x6f, 0x70)) - } - // add the Zp addresses not even used by BASIC - // these are valid for the C-64 (when no RS232 I/O is performed): - // ($02, $03, $fb-$fc, $fd-$fe are reserved as scratch addresses for various routines) - // KNOWN WORKING FREE: 0x04, 0x05, 0x06, 0x2a, 0x52, 0xf7, 0xf8, 0xf9, 0xfa)) - free.addAll(listOf(0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0d, 0x0e, - 0x12, 0x2a, 0x52, 0x94, 0x95, 0xa7, 0xa8, 0xa9, 0xaa, - 0xb5, 0xb6, 0xf7, 0xf8, 0xf9, 0xfa)) - } - assert(!free.contains(Zeropage.SCRATCH_B1)) - assert(!free.contains(Zeropage.SCRATCH_B2)) - assert(!free.contains(Zeropage.SCRATCH_W1)) - assert(!free.contains(Zeropage.SCRATCH_W2)) - } + val free = mutableListOf() // subclasses must set this to the appropriate free locations. fun available() = free.size fun allocate(vardecl: VarDecl) : Int { assert(vardecl.name.isEmpty() || !allocations.values.any { it.first==vardecl.name } ) {"same name can't be allocated twice"} - assert(vardecl.type==VarDeclType.VAR) {"can only allocate VAR type"} + assert(vardecl.type== VarDeclType.VAR) {"can only allocate VAR type"} val size = if(vardecl.arrayspec!=null) { diff --git a/compiler/src/prog8/compiler/target/c64/Commodore64.kt b/compiler/src/prog8/compiler/target/c64/Commodore64.kt new file mode 100644 index 000000000..74d1d6e69 --- /dev/null +++ b/compiler/src/prog8/compiler/target/c64/Commodore64.kt @@ -0,0 +1,167 @@ +package prog8.compiler.target.c64 + +import prog8.compiler.* +import prog8.stackvm.Program +import kotlin.math.absoluteValue +import kotlin.math.pow +import kotlin.system.exitProcess + + +// 5-byte cbm MFLPT format limitations: +const val FLOAT_MAX_POSITIVE = 1.7014118345e+38 +const val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 + + + +class C64Zeropage(options: CompilationOptions) : Zeropage(options) { + + companion object { + const val SCRATCH_B1 = 0x02 + const val SCRATCH_B2 = 0x03 + const val SCRATCH_W1 = 0xfb // $fb/$fc + const val SCRATCH_W2 = 0xfd // $fd/$fe + } + + init { + if(options.zeropage== ZeropageType.FULL) { + free.addAll(0x04 .. 0xfa) + free.add(0xff) + free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ + } else { + if(options.zeropage== ZeropageType.KERNALSAFE) { + // add the Zp addresses that are just used by BASIC routines to the free list + free.addAll(listOf(0x09, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, + 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, + 0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x6f, 0x70)) + } + // add the Zp addresses not even used by BASIC + // these are valid for the C-64 (when no RS232 I/O is performed): + // ($02, $03, $fb-$fc, $fd-$fe are reserved as scratch addresses for various routines) + // KNOWN WORKING FREE: 0x04, 0x05, 0x06, 0x2a, 0x52, 0xf7, 0xf8, 0xf9, 0xfa)) + free.addAll(listOf(0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0d, 0x0e, + 0x12, 0x2a, 0x52, 0x94, 0x95, 0xa7, 0xa8, 0xa9, 0xaa, + 0xb5, 0xb6, 0xf7, 0xf8, 0xf9, 0xfa)) + } + assert(!free.contains(SCRATCH_B1)) + assert(!free.contains(SCRATCH_B2)) + assert(!free.contains(SCRATCH_W1)) + assert(!free.contains(SCRATCH_W2)) + } +} + + +data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short) { + + companion object { + val zero = Mflpt5(0, 0,0,0,0) + fun fromNumber(num: Number): Mflpt5 { + // see https://en.wikipedia.org/wiki/Microsoft_Binary_Format + // and https://sourceforge.net/p/acme-crossass/code-0/62/tree/trunk/ACME_Lib/cbm/mflpt.a + // and https://en.wikipedia.org/wiki/IEEE_754-1985 + + val flt = num.toDouble() + if(flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE) + throw CompilerException("floating point number out of 5-byte mflpt range: $this") + if(flt==0.0) + return zero + + val sign = if(flt<0.0) 0x80L else 0x00L + var exponent = 128 + 32 // 128 is cbm's bias, 32 is this algo's bias + var mantissa = flt.absoluteValue + + // if mantissa is too large, shift right and adjust exponent + while(mantissa >= 0x100000000) { + mantissa /= 2.0 + exponent ++ + } + // if mantissa is too small, shift left and adjust exponent + while(mantissa < 0x80000000) { + mantissa *= 2.0 + exponent -- + } + + return when { + exponent<0 -> zero // underflow, use zero instead + exponent>255 -> throw CompilerException("floating point overflow: $this") + exponent==0 -> zero + else -> { + val mantLong = mantissa.toLong() + Mflpt5( + exponent.toShort(), + (mantLong.and(0x7f000000L) ushr 24).or(sign).toShort(), + (mantLong.and(0x00ff0000L) ushr 16).toShort(), + (mantLong.and(0x0000ff00L) ushr 8).toShort(), + (mantLong.and(0x000000ffL)).toShort()) + } + } + } + } + + fun toDouble(): Double { + if(this == zero) return 0.0 + val exp = b0 - 128 + val sign = (b1.toInt() and 0x80) > 0 + val number = 0x80000000L.or(b1.toLong() shl 24).or(b2.toLong() shl 16).or(b3.toLong() shl 8).or(b4.toLong()) + val result = number.toDouble() * (2.0).pow(exp) / 0x100000000 + return if(sign) -result else result + } +} + + +fun compileToAssembly(program: Program): AssemblyResult { + println("\nGenerating assembly code from stackvmProg code... ") + // todo generate 6502 assembly + return AssemblyResult(program.name) +} + + +class AssemblyResult(val name: String) { + fun assemble(options: CompilationOptions, inputfilename: String, outputfilename: String) { + println("\nGenerating machine code program...") + + val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "-Wall", "-Wno-strict-bool", + "--dump-labels", "--vice-labels", "-l", "$outputfilename.vice-mon-list", + "--no-monitor", "--output", outputfilename, inputfilename) + + when(options.output) { + OutputType.PRG -> { + command.add("--cbm-prg") + println("\nCreating C-64 prg.") + } + OutputType.RAW -> { + command.add("--nostart") + println("\nCreating raw binary.") + } + } + + val proc = ProcessBuilder(command).inheritIO().start() + val result = proc.waitFor() + if(result!=0) { + System.err.println("assembler failed with returncode $result") + exitProcess(result) + } + } + + fun generateBreakpointList(): String { + // todo build breakpoint list +/* + def generate_breakpoint_list(self, program_filename: str) -> str: + breakpoints = [] + vice_mon_file = program_filename + ".vice-mon-list" + with open(vice_mon_file, "rU") as f: + for line in f: + match = re.fullmatch(r"al (?P
\w+) \S+_prog8_breakpoint_\d+.?", line, re.DOTALL) + if match: + breakpoints.append("$" + match.group("address")) + with open(vice_mon_file, "at") as f: + print("; vice monitor breakpoint list now follows", file=f) + print("; {:d} breakpoints have been defined here".format(len(breakpoints)), file=f) + print("del", file=f) + for b in breakpoints: + print("break", b, file=f) + return vice_mon_file + */ + return "monitorfile.txt" + } +} \ No newline at end of file diff --git a/compiler/src/prog8/compiler/Petscii.kt b/compiler/src/prog8/compiler/target/c64/Petscii.kt similarity index 99% rename from compiler/src/prog8/compiler/Petscii.kt rename to compiler/src/prog8/compiler/target/c64/Petscii.kt index 5df631d4f..95686eaad 100644 --- a/compiler/src/prog8/compiler/Petscii.kt +++ b/compiler/src/prog8/compiler/target/c64/Petscii.kt @@ -1,4 +1,6 @@ -package prog8.compiler +package prog8.compiler.target.c64 + +import prog8.compiler.CompilerException class Petscii { companion object { diff --git a/compiler/src/prog8/optimizing/ConstantFolding.kt b/compiler/src/prog8/optimizing/ConstantFolding.kt index 1504d1c23..3fa3d23ef 100644 --- a/compiler/src/prog8/optimizing/ConstantFolding.kt +++ b/compiler/src/prog8/optimizing/ConstantFolding.kt @@ -1,7 +1,7 @@ package prog8.optimizing import prog8.ast.* -import prog8.compiler.Petscii +import prog8.compiler.target.c64.Petscii class ConstantFolding(private val namespace: INameScope) : IAstProcessor { diff --git a/compiler/src/prog8/parser/ModuleParsing.kt b/compiler/src/prog8/parser/ModuleParsing.kt index 19c63710a..51400ebc3 100644 --- a/compiler/src/prog8/parser/ModuleParsing.kt +++ b/compiler/src/prog8/parser/ModuleParsing.kt @@ -1,10 +1,10 @@ package prog8.parser import org.antlr.v4.runtime.CharStreams +import prog8.ast.* import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths -import prog8.ast.* class ParsingFailedError(override var message: String) : Exception(message) diff --git a/compiler/src/prog8/stackvm/ScreenDialog.kt b/compiler/src/prog8/stackvm/ScreenDialog.kt index b934baeb5..dc9f54bff 100644 --- a/compiler/src/prog8/stackvm/ScreenDialog.kt +++ b/compiler/src/prog8/stackvm/ScreenDialog.kt @@ -1,8 +1,10 @@ package prog8.stackvm -import javax.swing.* import java.awt.* import java.awt.image.BufferedImage +import javax.swing.JButton +import javax.swing.JFrame +import javax.swing.JPanel import javax.swing.Timer diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index 129d05734..537e0affe 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -1,12 +1,11 @@ package prog8.stackvm import prog8.ast.DataType -import prog8.compiler.Mflpt5 -import prog8.compiler.Petscii +import prog8.compiler.target.c64.Petscii +import prog8.compiler.target.c64.Mflpt5 import java.awt.EventQueue import java.io.File import java.io.PrintStream -import java.io.PrintWriter import java.util.* import java.util.regex.Pattern import javax.swing.Timer @@ -118,7 +117,7 @@ enum class Opcode { SEC, // set carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations CLC, // clear carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations NOP, - BREAK, // breakpoint + BREAKPOINT, // breakpoint TERMINATE, // end the program LINE // record source file line number } @@ -228,7 +227,7 @@ class Memory { } -class Value(val type: DataType, private val numericvalue: Number?, val stringvalue: String?=null, val arrayvalue: IntArray?=null) { +class Value(val type: DataType, numericvalue: Number?, val stringvalue: String?=null, val arrayvalue: IntArray?=null) { private var byteval: Short? = null private var wordval: Int? = null private var floatval: Double? = null @@ -251,7 +250,7 @@ class Value(val type: DataType, private val numericvalue: Number?, val stringval DataType.ARRAY -> { asBooleanValue = arrayvalue!!.isNotEmpty() } - DataType.STR -> { + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> { if(stringvalue==null) throw VmExecutionException("expect stringvalue for STR type") asBooleanValue = stringvalue.isNotEmpty() } @@ -261,9 +260,9 @@ class Value(val type: DataType, private val numericvalue: Number?, val stringval override fun toString(): String { return when(type) { - DataType.BYTE -> "%02x".format(byteval) - DataType.WORD -> "%04x".format(wordval) - DataType.FLOAT -> floatval.toString() + DataType.BYTE -> "b:%02x".format(byteval) + DataType.WORD -> "w:%04x".format(wordval) + DataType.FLOAT -> "f:$floatval" DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> "\"$stringvalue\"" DataType.ARRAY -> TODO("array") DataType.ARRAY_W -> TODO("word array") @@ -513,6 +512,7 @@ open class Instruction(val opcode: Opcode, val argStr = arg?.toString() ?: "" val result = when { + opcode==Opcode.LINE -> "_line ${arg!!.stringvalue}" opcode==Opcode.SYSCALL -> { val syscall = Syscall.values().find { it.callNr==arg!!.numericValue() } "syscall $syscall" @@ -522,13 +522,11 @@ open class Instruction(val opcode: Opcode, } .trimEnd() - if(opcode==Opcode.LINE) - return " _$result" return " $result" } } -class Label(val name: String) : Instruction(opcode = Opcode.NOP) { +class LabelInstr(val name: String) : Instruction(opcode = Opcode.NOP) { override fun toString(): String { return "$name:" } @@ -547,7 +545,7 @@ private class MyStack : Stack() { fun pop2() : Pair = Pair(pop(), pop()) - fun printTop(amount: Int, output: PrintWriter) { + fun printTop(amount: Int, output: PrintStream) { peek(amount).reversed().forEach { output.println(" $it") } } } @@ -659,10 +657,12 @@ class Program (val name: String, if(line=="%end_variables") return vars val (name, type, valueStr) = line.split(splitpattern, limit = 3) + if(valueStr[0] !='"' && valueStr[1]!=':') + throw VmExecutionException("missing value type character") val value = when(type) { - "byte" -> Value(DataType.BYTE, valueStr.toShort(16)) - "word" -> Value(DataType.WORD, valueStr.toInt(16)) - "float" -> Value(DataType.FLOAT, valueStr.toDouble()) + "byte" -> Value(DataType.BYTE, valueStr.substring(2).toShort(16)) + "word" -> Value(DataType.WORD, valueStr.substring(2).toInt(16)) + "float" -> Value(DataType.FLOAT, valueStr.substring(2).toDouble()) "str", "str_p", "str_s", "str_ps" -> { if(valueStr.startsWith('"') && valueStr.endsWith('"')) Value(DataType.STR, null, unescape(valueStr.substring(1, valueStr.length-1))) @@ -780,7 +780,7 @@ class Program (val name: String, } } - fun print(out: PrintStream) { + fun print(out: PrintStream, embeddedLabels: Boolean=true) { out.println("; stackVM program code for '$name'") out.println("%memory") if(memory.isNotEmpty()) { @@ -796,10 +796,13 @@ class Program (val name: String, out.println("%instructions") val labels = this.labels.entries.associateBy({it.value}) {it.key} for(instr in this.program) { - val label = labels[instr] - if(label!=null) - out.println("$label:") - out.println(instr) + if(!embeddedLabels) { + val label = labels[instr] + if (label != null) + out.println("$label:") + } else { + out.println(instr) + } } out.println("%end_instructions") } @@ -813,7 +816,7 @@ class StackVm(val traceOutputFile: String?) { private var variables = mutableMapOf() // all variables (set of all vars used by all blocks/subroutines) key = their fully scoped name private var carry: Boolean = false private var program = listOf() - private var traceOutput = if(traceOutputFile!=null) File(traceOutputFile).printWriter() else null + private var traceOutput = if(traceOutputFile!=null) PrintStream(File(traceOutputFile), "utf-8") else null private lateinit var currentIns: Instruction private lateinit var canvas: BitmapScreenPanel private val rnd = Random() @@ -1105,7 +1108,7 @@ class StackVm(val traceOutputFile: String?) { Opcode.SEC -> carry = true Opcode.CLC -> carry = false Opcode.TERMINATE -> throw VmTerminationException("terminate instruction") - Opcode.BREAK -> throw VmBreakpointException() + Opcode.BREAKPOINT -> throw VmBreakpointException() Opcode.INC_MEM -> { val addr = ins.arg!!.integerValue() @@ -1374,8 +1377,6 @@ fun main(args: Array) { } val program = Program.load(args.first()) - program.print(System.out) - val vm = StackVm(traceOutputFile = null) val dialog = ScreenDialog() vm.load(program, dialog.canvas) diff --git a/compiler/test/UnitTests.kt b/compiler/test/UnitTests.kt index b4c228484..0500fe390 100644 --- a/compiler/test/UnitTests.kt +++ b/compiler/test/UnitTests.kt @@ -5,6 +5,7 @@ import prog8.ast.Position import prog8.ast.VarDecl import prog8.ast.VarDeclType import prog8.compiler.* +import prog8.compiler.target.c64.* import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.closeTo import org.hamcrest.Matchers.equalTo @@ -114,7 +115,7 @@ private val dummypos = Position("test", 0, 0, 0) class TestZeropage { @Test fun testNames() { - val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, false)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, false)) assertFailsWith { zp.allocate(VarDecl(VarDeclType.MEMORY, DataType.BYTE, null, "", null, dummypos)) @@ -131,27 +132,27 @@ class TestZeropage { @Test fun testZpFloatEnable() { - val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, false)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, false)) assertFailsWith { zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos)) } - val zp2 = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, true)) + val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, true)) zp2.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos)) } @Test fun testFreeSpaces() { - val zp1 = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, true)) + val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, true)) assertEquals(23, zp1.available()) - val zp2 = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, true)) + val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, true)) assertEquals(71, zp2.available()) - val zp3 = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, true)) + val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, true)) assertEquals(239, zp3.available()) } @Test fun testBasicsafeAllocation() { - val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, true)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, true)) assertEquals(23, zp.available()) zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos)) @@ -175,7 +176,7 @@ class TestZeropage { @Test fun testFullAllocation() { - val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, true)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, true)) assertEquals(239, zp.available()) val loc = zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos)) assertTrue(loc > 3) @@ -212,7 +213,7 @@ class TestZeropage { // free = (0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0d, 0x0e, // 0x12, 0x2a, 0x52, 0x94, 0x95, 0xa7, 0xa8, 0xa9, 0xaa, // 0xb5, 0xb6, 0xf7, 0xf8, 0xf9, 0xfa)) - val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, true)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, true)) assertEquals(23, zp.available()) assertEquals(0x04, zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos))) assertEquals(0x09, zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null, dummypos)))