diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index 6fb7ef9ae..14837de17 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -1,51 +1,53 @@ %option enable_floats - ~ main { sub start() { - byte bvar = -88 - ubyte ubvar = 222 - word wvar = -12345 - uword uwvar = 55555 + memory ubyte mub = $c400 + memory ubyte mub2 = $c401 + memory uword muw = $c500 + memory uword muw2 = $c502 + memory byte mb = $c000 + memory word mw = $c002 + + byte b + ubyte ub = $c4 + ubyte ub2 = $c4 + uword uw = $c500 + uword uw2 = $c502 + word ww - byte bv2 - ubyte ubv2 - word wv2 - uword uwv2 + mub=5 + muw=4444 + mb=-100 + mw=-23333 - X=X - X=X - Y=Y - X=A - A=Y - A=ubvar - AX=XY - XY=XY - AY=uwvar - XY=uwvar - bv2 = ub2b(ubvar) - ubv2 = b2ub(bvar) - wv2 = wrd(uwvar) - uwv2 = uwrd(wvar) - wv2 = wrd(ubvar) - wv2 = wrd(bvar) - uwv2 = uwrd(ubvar) - uwv2 = uwrd(bvar) +; b++ +; A++ +; XY++ +; mub++ +; ub++ +; uw++ +; ww++ +; mb++ +; mw++ +; +; b-- +; A-- +; XY-- +; mub-- +; ub-- +; uw-- +; ww-- +; mb-- +; mw-- + return } } - - -~ block2 $c000 { - - str derp="hello" - byte v =44 - return -} diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index a364741d4..47d003a63 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -1078,7 +1078,7 @@ class LiteralValue(val type: DataType, throw ExpressionError("cannot compare type $type with ${other.type}", other.position) } - fun intoDatatype(targettype: DataType): LiteralValue { + fun intoDatatype(targettype: DataType): LiteralValue? { if(type==targettype) return this when(type) { @@ -1139,7 +1139,7 @@ class LiteralValue(val type: DataType, } else -> {} } - throw FatalAstException("invalid type conversion from $this to $targettype") + return null // invalid type conversion from $this to $targettype } } diff --git a/compiler/src/prog8/ast/StmtReorderer.kt b/compiler/src/prog8/ast/StmtReorderer.kt index f03f1824f..b69d6dd13 100644 --- a/compiler/src/prog8/ast/StmtReorderer.kt +++ b/compiler/src/prog8/ast/StmtReorderer.kt @@ -107,8 +107,10 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He vardeclsToAdd[scope]!!.add(decl.asDefaultValueDecl()) val declvalue = decl.value!! val value = - if(declvalue is LiteralValue) - declvalue.intoDatatype(decl.datatype) + if(declvalue is LiteralValue) { + val converted = declvalue.intoDatatype(decl.datatype) + converted ?: declvalue + } else declvalue return VariableInitializationAssignment( diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 71d6d90ed..c651660c2 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -187,7 +187,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, } private fun processVariables(scope: INameScope) { - for(variable in scope.statements.asSequence().filter {it is VarDecl}.map { it as VarDecl }) + for(variable in scope.statements.asSequence().filter {it is VarDecl && it.type==VarDeclType.VAR}.map { it as VarDecl }) prog.variable(variable.scopedname, variable) for(subscope in scope.subScopes()) processVariables(subscope.value) @@ -343,6 +343,19 @@ private class StatementTranslator(private val prog: IntermediateProgram, } } + private fun opcodePopmem(dt: DataType): Opcode { + return when (dt) { + DataType.UBYTE -> Opcode.POP_MEM_UB + DataType.BYTE -> Opcode.POP_MEM_B + DataType.UWORD -> Opcode.POP_MEM_UW + DataType.WORD -> Opcode.POP_MEM_W + DataType.FLOAT -> Opcode.POP_MEM_FLOAT + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, + DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F, DataType.MATRIX_UB, + DataType.ARRAY_B, DataType.ARRAY_W, DataType.MATRIX_B -> Opcode.POP_MEM_UW + } + } + private fun opcodeDecvar(reg: Register): Opcode { return when(reg) { Register.A, Register.X, Register.Y -> Opcode.DEC_VAR_UB @@ -1109,7 +1122,8 @@ private class StatementTranslator(private val prog: IntermediateProgram, if(valueDt!=targetDt) { // convert value to target datatype if possible when(targetDt) { - DataType.UBYTE, DataType.BYTE -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") + DataType.UBYTE, DataType.BYTE -> + throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") DataType.UWORD, DataType.WORD -> { when (valueDt) { DataType.UBYTE -> prog.instr(Opcode.UB2UWORD) @@ -1161,13 +1175,21 @@ private class StatementTranslator(private val prog: IntermediateProgram, when { stmt.target.identifier!=null -> { val target = stmt.target.identifier!!.targetStatement(namespace)!! - when(target) { - is VarDecl -> { - val opcode = opcodePopvar(stmt.target.determineDatatype(namespace, heap, stmt)!!) - prog.instr(opcode, callLabel = target.scopedname) + if (target is VarDecl) { + when(target.type) { + VarDeclType.VAR -> { + val opcode = opcodePopvar(stmt.target.determineDatatype(namespace, heap, stmt)!!) + prog.instr(opcode, callLabel = target.scopedname) + } + VarDeclType.MEMORY -> { + val opcode = opcodePopmem(stmt.target.determineDatatype(namespace, heap, stmt)!!) + val address = target.value?.constValue(namespace, heap)!!.asIntegerValue!! + prog.instr(opcode, Value(DataType.UWORD, address)) + } + VarDeclType.CONST -> throw CompilerException("cannot assign to const") } - else -> throw CompilerException("invalid assignment target type ${target::class}") } + else throw CompilerException("invalid assignment target type ${target::class}") } stmt.target.register!=null -> { val opcode=opcodePopvar(stmt.target.register!!) diff --git a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt index dc3e22c6c..13b388a06 100644 --- a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt +++ b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt @@ -1,9 +1,6 @@ package prog8.compiler.intermediate -import prog8.ast.DataType -import prog8.ast.LiteralValue -import prog8.ast.Position -import prog8.ast.VarDecl +import prog8.ast.* import prog8.compiler.CompilerException import prog8.compiler.HeapValues import java.io.PrintStream @@ -246,6 +243,8 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap } fun variable(scopedname: String, decl: VarDecl) { + if(decl.type!=VarDeclType.VAR) + return // const and memory variables don't require storage val value = when(decl.datatype) { DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT -> Value(decl.datatype, (decl.value as LiteralValue).asNumericValue!!) DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> { diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index 8b59979a6..6518e63ad 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -1,13 +1,10 @@ package prog8.compiler.target.c64 -import prog8.compiler.CompilationOptions -import prog8.compiler.LauncherType -import prog8.compiler.OutputType +import prog8.compiler.* import prog8.compiler.intermediate.Instruction import prog8.compiler.intermediate.IntermediateProgram import prog8.compiler.intermediate.LabelInstr import prog8.compiler.intermediate.Opcode -import prog8.compiler.toHex import java.io.File import java.util.* @@ -67,6 +64,11 @@ class AsmGen(val options: CompilationOptions) { // todo zeropage if it's there for(block in program.blocks) out("\tjsr ${block.scopedname}._prog8_init") + out("\tlda #0") + out("\ttay") + out("\ttax") + out("\tdex\t; init estack pointer to \$ff") + out("\tclc") out("\tjmp main.start\t; jump to program entrypoint") } @@ -94,6 +96,63 @@ class AsmGen(val options: CompilationOptions) { private val registerStrings = setOf("A", "X", "Y", "AX", "AY", "XY") + + // note: to put stuff on the stack, we use Absolute,X addressing mode which is 3 bytes / 4 cycles + // possible space optimization is to use zeropage (indirect),Y which is 2 bytes, but 5 cycles + + private fun pushByte(byte: Int, out: (String) -> Unit) { + out("\tlda #${byte.toHex()}") + out("\tsta ${ESTACK_LO.toHex()},x") + out("\tdex") + } + + private fun pushMemByte(address: Int, out: (String) -> Unit) { + out("\tlda ${address.toHex()}") + out("\tsta ${ESTACK_LO.toHex()},x") + out("\tdex") + } + + private fun pushVarByte(name: String, out: (String) -> Unit) { + out("\tlda $name") + out("\tsta ${ESTACK_LO.toHex()},x") + out("\tdex") + } + + private fun pushWord(word: Int, out: (String) -> Unit) { + out("\tlda #<${word.toHex()}") + out("\tsta ${ESTACK_LO.toHex()},x") + out("\tlda #>${word.toHex()}") + out("\tsta ${ESTACK_HI.toHex()},x") + out("\tdex") + } + + private fun pushMemWord(address: Int, out: (String) -> Unit) { + out("\tlda ${address.toHex()}") + out("\tsta ${ESTACK_LO.toHex()},x") + out("\tlda ${(address+1).toHex()}") + out("\tsta ${ESTACK_HI.toHex()},x") + out("\tdex") + } + + private fun pushVarWord(name: String, out: (String) -> Unit) { + out("\tlda $name") + out("\tsta ${ESTACK_LO.toHex()},x") + out("\tlda $name+1") + out("\tsta ${ESTACK_HI.toHex()},x") + out("\tdex") + } + + private fun popByteA(out: (String) -> Unit) { + out("\tinx") + out("\tlda ${ESTACK_LO.toHex()},x") + } + + private fun popWordAY(out: (String) -> Unit) { + out("\tinx") + out("\tlda ${ESTACK_LO.toHex()},x") + out("\tldy ${ESTACK_HI.toHex()},x") + } + private fun ins2asm(out: (String) -> Unit, insIdx: Int, ins: Instruction, block: IntermediateProgram.ProgramBlock): Int { if(ins is LabelInstr) { if(ins.name==block.shortname) @@ -102,7 +161,6 @@ class AsmGen(val options: CompilationOptions) { out(ins.name.substring(block.shortname.length+1)) else out(ins.name) - out("\trts") // todo weg return 0 } when(ins.opcode) { @@ -113,87 +171,246 @@ class AsmGen(val options: CompilationOptions) { Opcode.CLC -> out("\tclc") Opcode.SEI -> out("\tsei") Opcode.CLI -> out("\tcli") + Opcode.RETURN -> out("\trts") // todo is return really this simple? Opcode.B2UB -> {} // is a no-op, just carry on with the byte as-is Opcode.UB2B -> {} // is a no-op, just carry on with the byte as-is Opcode.RSAVE -> out("\tphp\n\tpha\n\ttxa\n\tpha\n\ttya\n\tpha") Opcode.RRESTORE -> out("\tpla\n\ttay\n\tpla\n\ttax\n\tpla\n\tplp") - Opcode.PUSH_BYTE -> { - // check if we load a register (X, Y) with constant value - val nextIns = block.getIns(insIdx+1) - if(nextIns.opcode==Opcode.POP_VAR_BYTE && nextIns.callLabel in registerStrings) { - out("\tld${nextIns.callLabel!!.toLowerCase()} #${ins.arg!!.integerValue().toHex()}") - return 1 // skip 1 - } - // todo push_byte - } - Opcode.PUSH_WORD -> { - // check if we load a register (AX, AY, XY) with constant value - val nextIns = block.getIns(insIdx+1) - if(nextIns.opcode==Opcode.POP_VAR_WORD && nextIns.callLabel in registerStrings) { - val regs = nextIns.callLabel!!.toLowerCase() - val value = ins.arg!!.integerValue().toHex() - out("\tld${regs[0]} #<$value") - out("\tld${regs[1]} #>$value") - return 1 // skip 1 - } - // todo push_word - } + Opcode.DISCARD_BYTE -> out("\tinx") // remove 1 (2) bytes from stack + Opcode.DISCARD_WORD -> out("\tinx") // remove 2 bytes from stack + Opcode.DISCARD_FLOAT -> out("\tinx\n\tinx\n\tinx") // remove 5 (6) bytes from stack Opcode.COPY_VAR_BYTE -> { - if(ins.callLabel2 in registerStrings) { - if(ins.callLabel in registerStrings) { - // copying register -> register - when { - ins.callLabel == "A" -> out("\tta${ins.callLabel2!!.toLowerCase()}") - ins.callLabel == "X" -> if (ins.callLabel2 == "Y") { - // 6502 doesn't have txy - out("\ttxa\n\ttay") - } else out("\ttxa") - ins.callLabel == "Y" -> if (ins.callLabel2 == "X") { - // 6502 doesn't have tyx - out("\ttya\n\ttax") - } else out("\ttya") + when { + ins.callLabel2 in registerStrings -> { + if(ins.callLabel in registerStrings) { + // copying register -> register + when { + ins.callLabel == "A" -> out("\tta${ins.callLabel2!!.toLowerCase()}") + ins.callLabel == "X" -> + if (ins.callLabel2 == "Y") + out("\ttxa\n\ttay") // 6502 doesn't have txy + else + out("\ttxa") + ins.callLabel == "Y" -> + if (ins.callLabel2 == "X") + out("\ttya\n\ttax") // 6502 doesn't have tyx + else + out("\ttya") + } + return 0 } - return 0 + // var -> reg + out("\tld${ins.callLabel2!!.toLowerCase()} ${ins.callLabel}") } + ins.callLabel in registerStrings -> + // reg -> var + out("\tst${ins.callLabel!!.toLowerCase()} ${ins.callLabel2}") + else -> + // var -> var + out("\tlda ${ins.callLabel}\n\tsta ${ins.callLabel2}") } - // todo copy_var_byte } Opcode.COPY_VAR_WORD -> { - if(ins.callLabel2 in registerStrings) { - if(ins.callLabel in registerStrings) { - // copying registerpair -> registerpair - when { - ins.callLabel == "AX" -> when (ins.callLabel2) { - "AY" -> out("\ttxy") - "XY" -> out("\tpha\n\ttxa\n\ttay\n\tpla\n\ttax") - } - ins.callLabel == "AY" -> when (ins.callLabel2) { - "AX" -> out("\tpha\n\ttya\n\ttax\n\tpla") - "XY" -> out("\ttax") - } - ins.callLabel == "XY" -> when (ins.callLabel2) { - "AX" -> out("\ttxa\n\tpha\n\ttya\n\ttax\n\tpla") - "AY" -> out("\ttxa") + when { + ins.callLabel2 in registerStrings -> { + if(ins.callLabel in registerStrings) { + // copying registerpair -> registerpair + when { + ins.callLabel == "AX" -> when (ins.callLabel2) { + "AY" -> out("\ttxy") + "XY" -> out("\tpha\n\ttxa\n\ttay\n\tpla\n\ttax") + } + ins.callLabel == "AY" -> when (ins.callLabel2) { + "AX" -> out("\tpha\n\ttya\n\ttax\n\tpla") + "XY" -> out("\ttax") + } + ins.callLabel == "XY" -> when (ins.callLabel2) { + "AX" -> out("\ttxa\n\tpha\n\ttya\n\ttax\n\tpla") + "AY" -> out("\ttxa") + } } + return 0 } - return 0 + // wvar -> regpair + val regpair = ins.callLabel2!!.toLowerCase() + out("\tld${regpair[0]} ${ins.callLabel}") + out("\tld${regpair[1]} ${ins.callLabel}+1") + } + ins.callLabel in registerStrings -> { + // regpair->wvar + val regpair = ins.callLabel!!.toLowerCase() + out("\tst${regpair[0]} ${ins.callLabel2}") + out("\tst${regpair[1]} ${ins.callLabel2}+1") + } + else -> { + // wvar->wvar + out("\tlda ${ins.callLabel}\n\tsta ${ins.callLabel2}") + out("\tlda ${ins.callLabel}+1\n\tsta ${ins.callLabel2}+1") } } - // todo copy_var_byte } - else-> {} -// Opcode.PUSH_FLOAT -> TODO() -// Opcode.PUSH_MEM_B -> TODO() -// Opcode.PUSH_MEM_UB -> TODO() -// Opcode.PUSH_MEM_W -> TODO() -// Opcode.PUSH_MEM_UW -> TODO() -// Opcode.PUSH_MEM_FLOAT -> TODO() -// Opcode.PUSH_VAR_BYTE -> TODO() -// Opcode.PUSH_VAR_WORD -> TODO() -// Opcode.PUSH_VAR_FLOAT -> TODO() -// Opcode.DISCARD_BYTE -> TODO() -// Opcode.DISCARD_WORD -> TODO() -// Opcode.DISCARD_FLOAT -> TODO() + Opcode.PUSH_BYTE -> { + val nextIns = block.getIns(insIdx+1) + if(nextIns==Opcode.DISCARD_BYTE) + throw CompilerException("discard after push should have been removed") + if(nextIns.opcode==Opcode.POP_VAR_BYTE) { + if(nextIns.callLabel in registerStrings) { + // load a register with constant value + out("\tld${nextIns.callLabel!!.toLowerCase()} #${ins.arg!!.integerValue().toHex()}") + return 1 // skip 1 + } + // load a variable with a constant value + out("\tlda #${ins.arg!!.integerValue().toHex()}") + out("\tsta ${nextIns.callLabel}") + return 1 // skip 1 + } + if(nextIns.opcode==Opcode.POP_MEM_B || nextIns.opcode==Opcode.POP_MEM_UB) { + // memory location = constant value + out("\tlda #${ins.arg!!.integerValue().toHex()}") + out("\tsta ${nextIns.arg!!.integerValue().toHex()}") + return 1 // skip 1 + } + // byte onto stack + pushByte(ins.arg!!.integerValue(), out) + } + Opcode.PUSH_MEM_UB, Opcode.PUSH_MEM_B -> { + val nextIns = block.getIns(insIdx+1) + if(nextIns==Opcode.DISCARD_BYTE) + throw CompilerException("discard after push should have been removed") + if(nextIns.opcode==Opcode.POP_VAR_BYTE) { + if(nextIns.callLabel in registerStrings) { + // load a register with memory location + out("\tld${nextIns.callLabel!!.toLowerCase()} ${ins.arg!!.integerValue().toHex()}") + return 1 // skip 1 + } + // load var with mem b + out("\tlda ${ins.arg!!.integerValue().toHex()}\n\tsta ${nextIns.callLabel}") + return 1 // skip 1 + } + if(nextIns.opcode==Opcode.POP_MEM_B || nextIns.opcode==Opcode.POP_MEM_UB) { + // copy byte from mem -> mem + out("\tlda ${ins.arg!!.integerValue().toHex()}\n\tsta ${nextIns.arg!!.integerValue().toHex()}") + return 1 // skip 1 + } + // byte from memory onto stack + pushMemByte(ins.arg!!.integerValue(), out) + } + Opcode.PUSH_VAR_BYTE -> { + val nextIns = block.getIns(insIdx+1) + if(nextIns==Opcode.DISCARD_BYTE) + throw CompilerException("discard after push should have been removed") + if(nextIns.opcode==Opcode.POP_VAR_BYTE) + throw CompilerException("push var+pop var should have been replaced by copy var") + if(nextIns.opcode==Opcode.POP_MEM_B || nextIns.opcode==Opcode.POP_MEM_UB) { + // copy byte from var -> mem + out("\tlda ${ins.callLabel}\n\tsta ${nextIns.arg!!.integerValue().toHex()}") + return 1 // skip 1 + } + // byte from variable onto stack + pushVarByte(ins.callLabel!!, out) + } + Opcode.PUSH_WORD -> { + val nextIns = block.getIns(insIdx+1) + if(nextIns==Opcode.DISCARD_WORD) + throw CompilerException("discard after push should have been removed") + if(nextIns.opcode==Opcode.POP_VAR_WORD) { + val value = ins.arg!!.integerValue() + if(nextIns.callLabel in registerStrings) { + // we load a register (AX, AY, XY) with constant value + val regs = nextIns.callLabel!!.toLowerCase() + out("\tld${regs[0]} #<${value.toHex()}") + out("\tld${regs[1]} #>${value.toHex()}") + return 1 // skip 1 + } + // load a word variable with a constant value + out("\tlda #<${value.toHex()}") + out("\tsta ${nextIns.callLabel}") + out("\tlda #>${value.toHex()}") + out("\tsta ${nextIns.callLabel}+1") + return 1 // skip 1 + } + if(nextIns.opcode==Opcode.POP_MEM_W || nextIns.opcode==Opcode.POP_MEM_UW) { + // we're loading a word into memory + out("\tlda #<${ins.arg!!.integerValue().toHex()}") + out("\tsta ${nextIns.arg!!.integerValue().toHex()}") + out("\tlda #>${ins.arg.integerValue().toHex()}") + out("\tsta ${(nextIns.arg.integerValue()+1).toHex()}") + return 1 // skip 1 + } + pushWord(ins.arg!!.integerValue(), out) + } + Opcode.PUSH_MEM_UW, Opcode.PUSH_MEM_W -> { + val nextIns = block.getIns(insIdx+1) + if(nextIns==Opcode.DISCARD_WORD) + throw CompilerException("discard after push should have been removed") + if(nextIns.opcode==Opcode.POP_VAR_WORD) { + if(nextIns.callLabel in registerStrings) { + // load a register (AX, AY, XY) with word from memory + val regs = nextIns.callLabel!!.toLowerCase() + val value = ins.arg!!.integerValue() + out("\tld${regs[0]} ${value.toHex()}") + out("\tld${regs[1]} ${(value + 1).toHex()}") + return 1 // skip 1 + } + // load var with mem word + out("\tlda ${ins.arg!!.integerValue().toHex()}") + out("\tsta ${nextIns.callLabel}") + out("\tlda ${(ins.arg.integerValue()+1).toHex()}") + out("\tsta ${nextIns.callLabel}+1") + return 1 // skip 1 + } + if(nextIns.opcode==Opcode.POP_MEM_W || nextIns.opcode==Opcode.POP_MEM_UW) { + // copy word mem->mem + out("\tlda ${ins.arg!!.integerValue().toHex()}") + out("\tsta ${nextIns.arg!!.integerValue().toHex()}") + out("\tlda ${(ins.arg.integerValue()+1).toHex()}") + out("\tsta ${(nextIns.arg.integerValue()+1).toHex()}") + return 1 // skip 1 + } + // word from memory onto stack + pushMemWord(ins.arg!!.integerValue(), out) + } + Opcode.PUSH_VAR_WORD -> { + val nextIns = block.getIns(insIdx+1) + if(nextIns==Opcode.DISCARD_FLOAT) + throw CompilerException("discard after push should have been removed") + if(nextIns.opcode==Opcode.POP_VAR_WORD) + throw CompilerException("push var+pop var should have been replaced by copy var") + if(nextIns.opcode==Opcode.POP_MEM_W || nextIns.opcode==Opcode.POP_MEM_UW) { + // copy word from var -> mem + out("\tlda ${ins.callLabel}\n\tsta ${nextIns.arg!!.integerValue().toHex()}") + return 1 // skip 1 + } + // word from memory onto stack + pushVarWord(ins.callLabel!!, out) + } + Opcode.PUSH_FLOAT -> { + val nextIns = block.getIns(insIdx+1) + if(nextIns==Opcode.DISCARD_FLOAT) + throw CompilerException("discard after push should have been removed") + if(!options.floats) + throw CompilerException("floats not enabled") + TODO("push float") + } + Opcode.PUSH_VAR_FLOAT -> { + val nextIns = block.getIns(insIdx+1) + if(nextIns==Opcode.DISCARD_FLOAT) + throw CompilerException("discard after push should have been removed") + if(nextIns.opcode==Opcode.POP_VAR_FLOAT) + throw CompilerException("push var+pop var should have been replaced by copy var") + if(!options.floats) + throw CompilerException("floats not enabled") + TODO("push var float") + } + Opcode.PUSH_MEM_FLOAT -> { + val nextIns = block.getIns(insIdx+1) + if(nextIns==Opcode.DISCARD_FLOAT) + throw CompilerException("discard after push should have been removed") + if(!options.floats) + throw CompilerException("floats not enabled") + TODO("push mem float") + } + else-> TODO("asm for $ins") // Opcode.POP_MEM_B -> TODO() // Opcode.POP_MEM_UB -> TODO() // Opcode.POP_MEM_W -> TODO() @@ -202,9 +419,27 @@ class AsmGen(val options: CompilationOptions) { // Opcode.POP_VAR_BYTE -> TODO() // Opcode.POP_VAR_WORD -> TODO() // Opcode.POP_VAR_FLOAT -> TODO() -// Opcode.COPY_VAR_BYTE -> TODO() -// Opcode.COPY_VAR_WORD -> TODO() // Opcode.COPY_VAR_FLOAT -> TODO() +// Opcode.INC_B -> TODO() +// Opcode.INC_UB -> TODO() +// Opcode.INC_W -> TODO() +// Opcode.INC_UW -> TODO() +// Opcode.INC_F -> TODO() +// Opcode.INC_VAR_B -> TODO() +// Opcode.INC_VAR_UB -> TODO() +// Opcode.INC_VAR_W -> TODO() +// Opcode.INC_VAR_UW -> TODO() +// Opcode.INC_VAR_F -> TODO() +// Opcode.DEC_B -> TODO() +// Opcode.DEC_UB -> TODO() +// Opcode.DEC_W -> TODO() +// Opcode.DEC_UW -> TODO() +// Opcode.DEC_F -> TODO() +// Opcode.DEC_VAR_B -> TODO() +// Opcode.DEC_VAR_UB -> TODO() +// Opcode.DEC_VAR_W -> TODO() +// Opcode.DEC_VAR_UW -> TODO() +// Opcode.DEC_VAR_F -> TODO() // Opcode.ADD_UB -> TODO() // Opcode.ADD_B -> TODO() // Opcode.ADD_UW -> TODO() @@ -304,26 +539,6 @@ class AsmGen(val options: CompilationOptions) { // Opcode.XOR_WORD -> TODO() // Opcode.NOT_BYTE -> TODO() // Opcode.NOT_WORD -> TODO() -// Opcode.INC_B -> TODO() -// Opcode.INC_UB -> TODO() -// Opcode.INC_W -> TODO() -// Opcode.INC_UW -> TODO() -// Opcode.INC_F -> TODO() -// Opcode.INC_VAR_B -> TODO() -// Opcode.INC_VAR_UB -> TODO() -// Opcode.INC_VAR_W -> TODO() -// Opcode.INC_VAR_UW -> TODO() -// Opcode.INC_VAR_F -> TODO() -// Opcode.DEC_B -> TODO() -// Opcode.DEC_UB -> TODO() -// Opcode.DEC_W -> TODO() -// Opcode.DEC_UW -> TODO() -// Opcode.DEC_F -> TODO() -// Opcode.DEC_VAR_B -> TODO() -// Opcode.DEC_VAR_UB -> TODO() -// Opcode.DEC_VAR_W -> TODO() -// Opcode.DEC_VAR_UW -> TODO() -// Opcode.DEC_VAR_F -> TODO() // Opcode.LESS_B -> TODO() // Opcode.LESS_UB -> TODO() // Opcode.LESS_W -> TODO() @@ -364,7 +579,6 @@ class AsmGen(val options: CompilationOptions) { // Opcode.BNEG -> TODO() // Opcode.BPOS -> TODO() // Opcode.CALL -> TODO() -// Opcode.RETURN -> TODO() // Opcode.SYSCALL -> TODO() // Opcode.BREAKPOINT -> TODO() } diff --git a/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt b/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt index 60d20e06b..1322edd9c 100644 --- a/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt +++ b/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt @@ -52,6 +52,6 @@ class AssemblyProgram(val name: String) { breakpoints.add(0, "; vice monitor breakpoint list now follows") breakpoints.add(1, "; $num breakpoints have been defined") breakpoints.add(2, "del") - File(viceMonListFile).appendText(breakpoints.joinToString("\n")) + File(viceMonListFile).appendText(breakpoints.joinToString("\n")+"\n") } } \ No newline at end of file diff --git a/compiler/src/prog8/compiler/target/c64/Commodore64.kt b/compiler/src/prog8/compiler/target/c64/Commodore64.kt index bc1e6e1d1..8c94adf0a 100644 --- a/compiler/src/prog8/compiler/target/c64/Commodore64.kt +++ b/compiler/src/prog8/compiler/target/c64/Commodore64.kt @@ -15,6 +15,10 @@ const val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 const val BASIC_LOAD_ADDRESS = 0x0801 const val RAW_LOAD_ADDRESS = 0xc000 +// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations) +const val ESTACK_LO = 0xce00 // $ce00-$ceff inclusive +const val ESTACK_HI = 0xcf00 // $cf00-$cfff inclusive + class C64Zeropage(options: CompilationOptions) : Zeropage(options) { diff --git a/compiler/src/prog8/optimizing/ConstantFolding.kt b/compiler/src/prog8/optimizing/ConstantFolding.kt index d2ff602c3..170ee80c6 100644 --- a/compiler/src/prog8/optimizing/ConstantFolding.kt +++ b/compiler/src/prog8/optimizing/ConstantFolding.kt @@ -553,29 +553,59 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV // see if we can promote/convert a literal value to the required datatype when(targetDt) { DataType.UWORD -> { + // we can convert to UWORD: any UBYTE, BYTE/WORD that are >=0, FLOAT that's an integer 0..65535 if(lv.type==DataType.UBYTE) assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position=lv.position) + else if(lv.type==DataType.BYTE && lv.bytevalue!!>=0) + assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position=lv.position) + else if(lv.type==DataType.WORD && lv.bytevalue!!>=0) + assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position=lv.position) else if(lv.type==DataType.FLOAT) { val d = lv.floatvalue!! - if(floor(d)==d && d in 0..65535) { + if(floor(d)==d && d in 0..65535) assignment.value = LiteralValue(DataType.UWORD, wordvalue=floor(d).toInt(), position=lv.position) - } + } + } + DataType.UBYTE -> { + // we can convert to UBYTE: UWORD <=255, BYTE >=0, FLOAT that's an integer 0..255 + if(lv.type==DataType.UWORD && lv.wordvalue!! <= 255) + assignment.value = LiteralValue(DataType.UBYTE, lv.wordvalue.toShort(), position=lv.position) + else if(lv.type==DataType.BYTE && lv.bytevalue!! >=0) + assignment.value = LiteralValue(DataType.UBYTE, lv.bytevalue.toShort(), position=lv.position) + else if(lv.type==DataType.FLOAT) { + val d = lv.floatvalue!! + if(floor(d)==d && d in 0..255) + assignment.value = LiteralValue(DataType.UBYTE, floor(d).toShort(), position=lv.position) + } + } + DataType.BYTE -> { + // we can convert to BYTE: UWORD/UBYTE <= 127, FLOAT that's an integer 0..127 + if(lv.type==DataType.UWORD && lv.wordvalue!! <= 127) + assignment.value = LiteralValue(DataType.BYTE, lv.wordvalue.toShort(), position=lv.position) + else if(lv.type==DataType.UBYTE && lv.bytevalue!! <= 127) + assignment.value = LiteralValue(DataType.BYTE, lv.bytevalue, position=lv.position) + else if(lv.type==DataType.FLOAT) { + val d = lv.floatvalue!! + if(floor(d)==d && d in 0..127) + assignment.value = LiteralValue(DataType.BYTE, floor(d).toShort(), position=lv.position) + } + } + DataType.WORD -> { + // we can convert to WORD: any UBYTE/BYTE, UWORD <= 32767, FLOAT that's an integer -32768..32767 + if(lv.type==DataType.UBYTE || lv.type==DataType.BYTE) + assignment.value = LiteralValue(DataType.WORD, lv.bytevalue!!, position=lv.position) + else if(lv.type==DataType.UWORD && lv.wordvalue!! <= 32767) + assignment.value = LiteralValue(DataType.WORD, wordvalue=lv.wordvalue, position=lv.position) + else if(lv.type==DataType.FLOAT) { + val d = lv.floatvalue!! + if(floor(d)==d && d in -32768..32767) + assignment.value = LiteralValue(DataType.BYTE, floor(d).toShort(), position=lv.position) } } DataType.FLOAT -> { if(lv.isNumeric) assignment.value = LiteralValue(DataType.FLOAT, floatvalue= lv.asNumericValue?.toDouble(), position=lv.position) } - DataType.UBYTE -> { - if(lv.type==DataType.UWORD && lv.asIntegerValue in 0..255) { - assignment.value = LiteralValue(DataType.UBYTE, lv.asIntegerValue?.toShort(), position=lv.position) - } else if(lv.type==DataType.FLOAT) { - val d = lv.floatvalue!! - if(floor(d)==d && d in 0..255) { - assignment.value = LiteralValue(DataType.UBYTE, floor(d).toShort(), position=lv.position) - } - } - } else -> {} } }