diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index 76070bca2..2642257c1 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -1,20 +1,63 @@ %import c64utils -%option enable_floats ~ main { - ubyte[3] balloonsprite = [ %00000000,%01111111,%00000000 ] - sub start() { - ubyte i=0 - A= @($d020) - A= @($d020+i) - @($d020) = 0 - @($d020+i) = 0 - @($d020+i) = 1 - @($d020+i) = 2 - @($d020) = @($d020+i) + 1 - @($d020+i) = @($d020+i) + 1 - c64scr.print_ub(X) + uword uw1 = 0 + uword uw2 = $77ff + uword uw3 = $55aa + word w1 = 0 + word w2 = $22ff + word w3 = $55aa + memory uword muw1 = $2000 + memory uword muw2 = $3000 + memory uword muw3 = $4000 + memory word mw1 = $4100 + memory word mw2 = $4200 + memory word mw3 = $4300 + + uword[3] uwarr = $55aa + word[3] warr = $55aa + memory uword[3] muwarr = $4400 + memory word[3] mwarr = $4500 + + muw3 = $55aa + uwarr[0] = $55aa + uwarr[1] = $55aa + uwarr[2] = $55aa + muwarr[0] = $55aa + muwarr[1] = $55aa + muwarr[2] = $55aa + mwarr[0] = $55aa + mwarr[1] = $55aa + mwarr[2] = $55aa + + uw1 = uw2 + $55aa ;52649 + c64scr.print_uw(uw1) + c64.CHROUT('\n') + + uw1 = uw2 + uw3 ;52649 + c64scr.print_uw(uw1) + c64.CHROUT('\n') + + uw1 = uw2 + muw3 ;52649 + c64scr.print_uw(uw1) + c64.CHROUT('\n') + +; uw1 = uw2 + uwarr[2] ;52649 +; c64scr.print_uw(uw1) +; c64.CHROUT('\n') + + +; w1 = w2 + $55aa ; 30889 +; c64scr.print_w(w1) +; c64.CHROUT('\n') +; w1 = w2 + w3 ; 30889 +; c64scr.print_w(w1) +; c64.CHROUT('\n') +; +; uwarr[2] = uwarr[1] + $55aa +; uwarr[2] = uwarr[1] + uw3 +; uwarr[2] = uwarr[1] + uwarr[1] } } diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index 1178513b0..4ba671d6e 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -524,7 +524,7 @@ class Block(override val name: String, return "Block(name=$name, address=$address, ${statements.size} statements)" } - val options = statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.map {it.name!!}.toSet() + fun options() = statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.map {it.name!!}.toSet() } diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index 09752f586..8d2825db4 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -617,17 +617,17 @@ class AstChecker(private val namespace: INameScope, err("invalid import directive, cannot import itself") } "%breakpoint" -> { - if(directive.parent !is Block) err("this directive may only occur in a block") + if(directive.parent !is INameScope || directive.parent is Module) err("this directive may only occur in a block") if(directive.args.isNotEmpty()) err("invalid breakpoint directive, expected no arguments") } "%asminclude" -> { - if(directive.parent !is Block) err("this directive may only occur in a block") + if(directive.parent !is INameScope || directive.parent is Module) err("this directive may only occur in a block") if(directive.args.size!=2 || directive.args[0].str==null || directive.args[1].name==null) err("invalid asminclude directive, expected arguments: \"filename\", scopelabel") } "%asmbinary" -> { - if(directive.parent !is Block) err("this directive may only occur in a block") + if(directive.parent !is INameScope || directive.parent is Module) err("this directive may only occur in a block") val errormsg = "invalid asmbinary directive, expected arguments: \"filename\" [, offset [, length ] ]" if(directive.args.isEmpty()) err(errormsg) if(directive.args.isNotEmpty() && directive.args[0].str==null) err(errormsg) diff --git a/compiler/src/prog8/ast/StmtReorderer.kt b/compiler/src/prog8/ast/StmtReorderer.kt index 8afdee781..8248c36bf 100644 --- a/compiler/src/prog8/ast/StmtReorderer.kt +++ b/compiler/src/prog8/ast/StmtReorderer.kt @@ -31,6 +31,7 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He val varDecls = module.statements.filterIsInstance() module.statements.removeAll(varDecls) module.statements.addAll(0, varDecls) + val directives = module.statements.filter {it is Directive && it.directive in directivesToMove} module.statements.removeAll(directives) module.statements.addAll(0, directives) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 2eb4db690..d9e2b79b0 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -151,7 +151,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, val continueStmtLabelStack : Stack = Stack() override fun process(block: Block): IStatement { - prog.newBlock(block.scopedname, block.name, block.address, block.options) + prog.newBlock(block.scopedname, block.name, block.address, block.options()) processVariables(block) // @todo optimize initializations with same value: load the value only once (sort on initalization value, datatype ?) prog.label("block."+block.scopedname) prog.line(block.position) @@ -182,18 +182,6 @@ private class StatementTranslator(private val prog: IntermediateProgram, return super.process(subroutine) } - override fun process(directive: Directive): IStatement { - when(directive.directive) { - "%asminclude" -> throw CompilerException("can't use %asminclude in stackvm") - "%asmbinary" -> throw CompilerException("can't use %asmbinary in stackvm") - "%breakpoint" -> { - prog.line(directive.position) - prog.instr(Opcode.BREAKPOINT) - } - } - return super.process(directive) - } - private fun translate(statements: List) { for (stmt: IStatement in statements) { generatedLabelSequenceNumber++ @@ -214,7 +202,17 @@ private class StatementTranslator(private val prog: IntermediateProgram, is AnonymousScope -> translate(stmt) is ReturnFromIrq -> translate(stmt) is Return -> translate(stmt) - is Directive, is VarDecl, is Subroutine -> {} // skip this, already processed these. + is Directive -> { + when(stmt.directive) { + "%asminclude" -> throw CompilerException("can't use %asminclude in stackvm") + "%asmbinary" -> throw CompilerException("can't use %asmbinary in stackvm") + "%breakpoint" -> { + prog.line(stmt.position) + prog.instr(Opcode.BREAKPOINT) + } + } + } + is VarDecl, is Subroutine -> {} // skip this, already processed these. is InlineAssembly -> translate(stmt) else -> TODO("translate statement $stmt to stackvm") } diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index 2514495ed..29857534f 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -502,7 +502,7 @@ _prog8_irq_handler_excl } } Opcode.PUSH_VAR_WORD -> { - " lda ${ins.callLabel} | ldy ${ins.callLabel}+1 | sta ${ESTACK_LO.toHex()},x | pha | tya | sta ${ESTACK_HI.toHex()},x | pla | dex" + " lda ${ins.callLabel} | sta ${ESTACK_LO.toHex()},x | lda ${ins.callLabel}+1 | sta ${ESTACK_HI.toHex()},x | dex" } Opcode.PUSH_VAR_FLOAT -> " lda #<${ins.callLabel} | ldy #>${ins.callLabel}| jsr prog8_lib.push_float" Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB -> { @@ -782,7 +782,7 @@ _prog8_irq_handler_excl Opcode.CAST_B_TO_UW, Opcode.CAST_B_TO_W -> " lda ${(ESTACK_LO+1)},x | ${signExtendA("${(ESTACK_HI+1).toHex()},x")}" // sign extend the lsb Opcode.MSB -> " lda ${(ESTACK_HI+1).toHex()},x | sta ${(ESTACK_LO+1).toHex()},x" - Opcode.ADD_UB, Opcode.ADD_B -> { + Opcode.ADD_UB, Opcode.ADD_B -> { // TODO inline better? """ lda ${(ESTACK_LO + 2).toHex()},x clc @@ -791,7 +791,7 @@ _prog8_irq_handler_excl sta ${(ESTACK_LO + 1).toHex()},x """ } - Opcode.SUB_UB, Opcode.SUB_B -> { + Opcode.SUB_UB, Opcode.SUB_B -> { // TODO inline better? """ lda ${(ESTACK_LO + 2).toHex()},x sec @@ -2921,6 +2921,46 @@ _prog8_irq_handler_excl AsmPattern(listOf(Opcode.PUSH_MEM_B, Opcode.PUSH_BYTE, Opcode.BITXOR_BYTE), listOf(Opcode.PUSH_MEM_UB, Opcode.PUSH_BYTE, Opcode.BITXOR_BYTE)) { segment -> " lda ${hexVal(segment[0])} | eor #${hexVal(segment[1])} | sta ${ESTACK_LO.toHex()},x | dex " + }, + + + // 16 bit addition avoiding excessive stack usage + // @todo optimize this even more with longer asmpatterns (avoid stack use altogether on most common operations) + AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.ADD_UW), + listOf(Opcode.PUSH_VAR_WORD, Opcode.ADD_W)) { segment -> + """ + clc + lda ${segment[0].callLabel} + adc ${(ESTACK_LO+1).toHex()},x + sta ${(ESTACK_LO+1).toHex()},x + lda ${segment[0].callLabel}+1 + adc ${(ESTACK_HI+1).toHex()},x + sta ${(ESTACK_HI+1).toHex()},x + """ + }, + AsmPattern(listOf(Opcode.PUSH_MEM_UW, Opcode.ADD_UW), + listOf(Opcode.PUSH_MEM_W, Opcode.ADD_W)) { segment -> + """ + clc + lda ${hexVal(segment[0])} + adc ${(ESTACK_LO + 1).toHex()},x + sta ${(ESTACK_LO + 1).toHex()},x + lda ${hexValPlusOne(segment[0])} + adc ${(ESTACK_HI + 1).toHex()},x + sta ${(ESTACK_HI + 1).toHex()},x + """ + }, + AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.ADD_UW), + listOf(Opcode.PUSH_WORD, Opcode.ADD_W)) { segment -> + """ + clc + lda #<${hexVal(segment[0])} + adc ${(ESTACK_LO+1).toHex()},x + sta ${(ESTACK_LO+1).toHex()},x + lda #>${hexVal(segment[0])} + adc ${(ESTACK_HI+1).toHex()},x + sta ${(ESTACK_HI+1).toHex()},x + """ } ) diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index b7a87487e..fc06d1271 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -243,8 +243,9 @@ class StackVm(private var traceOutputFile: String?) { val result = variables[name] if(result!=null) return result - if(name in memoryPointers) + if(name in memoryPointers) { throw VmExecutionException("variable is memory-mapped: $name = ${memoryPointers[name]}") + } throw VmExecutionException("unknown variable: $name") } @@ -1205,51 +1206,81 @@ class StackVm(private var traceOutputFile: String?) { Opcode.READ_INDEXED_VAR_BYTE -> { // put the byte value of variable[index] onto the stack val index = evalstack.pop().integerValue() - val variable = getVar(ins.callLabel!!) - if(variable.type==DataType.UWORD) { - // assume the variable is a pointer (address) and get the ubyte value from that memory location - evalstack.push(Value(DataType.UBYTE, mem.getUByte(variable.integerValue()))) - } else { - // get indexed byte element from the arrayspec - val array = heap.get(variable.heapId) - when(array.type) { - DataType.ARRAY_UB-> evalstack.push(Value(DataType.UBYTE, array.array!![index])) - DataType.ARRAY_B -> evalstack.push(Value(DataType.BYTE, array.array!![index])) - DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> evalstack.push(Value(DataType.UBYTE, Petscii.encodePetscii(array.str!![index].toString(), true)[0])) + if(ins.callLabel in memoryPointers) { + val variable = memoryPointers[ins.callLabel]!! + val address = variable.first + index + when(variable.second) { + DataType.ARRAY_UB -> evalstack.push(Value(DataType.UBYTE, mem.getUByte(address))) + DataType.ARRAY_B -> evalstack.push(Value(DataType.BYTE, mem.getSByte(address))) else -> throw VmExecutionException("not a proper array/string variable with byte elements") } + } else { + val variable = getVar(ins.callLabel!!) + if (variable.type == DataType.UWORD) { + // assume the variable is a pointer (address) and get the ubyte value from that memory location + evalstack.push(Value(DataType.UBYTE, mem.getUByte(variable.integerValue()))) + } else { + // get indexed byte element from the arrayspec + val array = heap.get(variable.heapId) + when (array.type) { + DataType.ARRAY_UB -> evalstack.push(Value(DataType.UBYTE, array.array!![index])) + DataType.ARRAY_B -> evalstack.push(Value(DataType.BYTE, array.array!![index])) + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> evalstack.push(Value(DataType.UBYTE, Petscii.encodePetscii(array.str!![index].toString(), true)[0])) + else -> throw VmExecutionException("not a proper array/string variable with byte elements") + } + } } } Opcode.READ_INDEXED_VAR_WORD -> { // put the word value of variable[index] onto the stack val index = evalstack.pop().integerValue() - val variable = getVar(ins.callLabel!!) - if(variable.type==DataType.UWORD) { - // assume the variable is a pointer (address) and get the word value from that memory location - evalstack.push(Value(DataType.UWORD, mem.getUWord(variable.integerValue()))) - } else { - // get indexed word element from the arrayspec - val array = heap.get(variable.heapId) - when(array.type){ - DataType.ARRAY_UW -> evalstack.push(Value(DataType.UWORD, array.array!![index])) - DataType.ARRAY_W -> evalstack.push(Value(DataType.WORD, array.array!![index])) + if(ins.callLabel in memoryPointers) { + val variable = memoryPointers[ins.callLabel]!! + val address = variable.first + index*2 + when(variable.second) { + DataType.ARRAY_UW -> evalstack.push(Value(DataType.UWORD, mem.getUWord(address))) + DataType.ARRAY_W -> evalstack.push(Value(DataType.WORD, mem.getSWord(address))) else -> throw VmExecutionException("not a proper arrayspec var with word elements") } + } else { + // normal variable + val variable = getVar(ins.callLabel!!) + if(variable.type==DataType.UWORD) { + // assume the variable is a pointer (address) and get the word value from that memory location + evalstack.push(Value(DataType.UWORD, mem.getUWord(variable.integerValue()))) + } else { + // get indexed word element from the arrayspec + val array = heap.get(variable.heapId) + when(array.type){ + DataType.ARRAY_UW -> evalstack.push(Value(DataType.UWORD, array.array!![index])) + DataType.ARRAY_W -> evalstack.push(Value(DataType.WORD, array.array!![index])) + else -> throw VmExecutionException("not a proper arrayspec var with word elements") + } + } } } Opcode.READ_INDEXED_VAR_FLOAT -> { - // put the f;pat value of variable[index] onto the stack + // put the float value of variable[index] onto the stack val index = evalstack.pop().integerValue() - val variable = getVar(ins.callLabel!!) - if(variable.type==DataType.UWORD) { - // assume the variable is a pointer (address) and get the float value from that memory location - evalstack.push(Value(DataType.UWORD, mem.getFloat(variable.integerValue()))) - } else { - // get indexed float element from the arrayspec - val array = heap.get(variable.heapId) - if(array.type!=DataType.ARRAY_F) + if(ins.callLabel in memoryPointers) { + val variable = memoryPointers[ins.callLabel]!! + val address = variable.first + index*5 + if(variable.second==DataType.ARRAY_F) + evalstack.push(Value(DataType.FLOAT, mem.getFloat(address))) + else throw VmExecutionException("not a proper arrayspec var with float elements") - evalstack.push(Value(DataType.FLOAT, array.doubleArray!![index])) + } else { + val variable = getVar(ins.callLabel!!) + if (variable.type == DataType.UWORD) { + // assume the variable is a pointer (address) and get the float value from that memory location + evalstack.push(Value(DataType.UWORD, mem.getFloat(variable.integerValue()))) + } else { + // get indexed float element from the arrayspec + val array = heap.get(variable.heapId) + if (array.type != DataType.ARRAY_F) + throw VmExecutionException("not a proper arrayspec var with float elements") + evalstack.push(Value(DataType.FLOAT, array.doubleArray!![index])) + } } } Opcode.WRITE_INDEXED_VAR_BYTE -> { @@ -1310,21 +1341,21 @@ class StackVm(private var traceOutputFile: String?) { if(value.type==DataType.UWORD) { if(memloc.second!=DataType.ARRAY_UW) throw VmExecutionException("invalid memory pointer type $memloc") - mem.setUWord(memloc.first, value.integerValue()) + mem.setUWord(memloc.first+index*2, value.integerValue()) } else { if(memloc.second!=DataType.ARRAY_W) throw VmExecutionException("invalid memory pointer type $memloc") - mem.setSWord(memloc.first, value.integerValue()) + mem.setSWord(memloc.first+index*2, value.integerValue()) } } else { val variable = getVar(varname) if (variable.type == DataType.UWORD) { // assume the variable is a pointer (address) and write the word value to that memory location if(value.type==DataType.UWORD) - mem.setUWord(variable.integerValue(), value.integerValue()) + mem.setUWord(variable.integerValue()+index*2, value.integerValue()) else - mem.setSWord(variable.integerValue(), value.integerValue()) + mem.setSWord(variable.integerValue()+index*2, value.integerValue()) } else { // set indexed word element in the arrayspec val array = heap.get(variable.heapId) @@ -1347,12 +1378,12 @@ class StackVm(private var traceOutputFile: String?) { // variable is the name of a pointer, write the float value to that memory location if(memloc.second!=DataType.ARRAY_F) throw VmExecutionException("invalid memory pointer type $memloc") - mem.setFloat(memloc.first, value.numericValue().toDouble()) + mem.setFloat(memloc.first+index*5, value.numericValue().toDouble()) } else { val variable = getVar(varname) if (variable.type == DataType.UWORD) { // assume the variable is a pointer (address) and write the float value to that memory location - mem.setFloat(variable.integerValue(), value.numericValue().toDouble()) + mem.setFloat(variable.integerValue()+index*5, value.numericValue().toDouble()) } else { // set indexed float element in the arrayspec val array = heap.get(variable.heapId)