diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index 50c46e5ba..c0ce3c0aa 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -45,6 +45,46 @@ sub start() { memory float mfloat = $c006 memory float mfloat2 = $d006 +;label: +; +; while A>99 { +; X=22 +; } +; +; repeat { +; X=22 +; } until A>99 +; +; for X in 0 to 99 { +; Y=33 +; } +; +; for ubyte derp in 2 to 44 { +; X=44 +; } +; +; if A<22 goto label +; +; if X<22 { +; A=99 +; } else { +; Y=42 +; } + + Y=42 + AY=42 + AY=42555 + Y = ub + AY= ub + AY= uw + + Y = mubyte + AY = mubyte + AY = muword + + Y = ubarr1[2] + AY = ubarr1[2] + AY = uwarr1[2] barr1[2]=42 ubarr1[2]=42 @@ -52,6 +92,10 @@ sub start() { uwarr1[2]=42555 farr1[2]=42.5678 + ubarr1[2]=X + uwarr1[2]=XY + ; farr1[2]=XY ; @todo + barr1[2] = b ubarr1[2] = ub warr1[2] = w @@ -68,62 +112,88 @@ sub start() { ub = ubarr1[2] w = warr1[2] uw = uwarr1[2] - fl1 = farr1[2] + ; fl1 = farr1[2] ; @todo mbyte= barr1[2] mubyte = ubarr1[2] mword = warr1[2] muword = uwarr1[2] - mfloat = farr1[2] + ; mfloat = farr1[2] ; @todo barr1[2] = barr2[3] ubarr1[2] = ubarr2[3] warr1[2] = warr2[3] uwarr1[2] = uwarr2[3] - farr1[2] = farr2[3] + ; farr1[2] = farr2[3] ; @todo + + + XY[2]=42 + XY[2] = ub + XY[2] = mubyte + ub = XY[2] + uw = XY[2] + ;fl1 = XY[2] ; @todo + mubyte = XY[2] + muword = XY[2] + ;mfloat = XY[2] ; @todo + XY[2] = AY[3] ; @todo wat is de output hiervan??? -; b = 1 -; ub = 1 -; w = 1 -; uw = 1 -; fl1 = 2.345 -; -; b = b2 -; ub = pixely -; w = w2 -; uw = uw2 -; fl1 = fl2 -; -; b = mbyte -; ub = mubyte -; w = mword -; uw = muword -; fl1 = mfloat -; -; mbyte = 1 -; mubyte = 1 -; mword = 1 -; muword = 1 -; mfloat = 3.456 + b = 1 + ub = 1 + w = 1 + uw = 1 + fl1 = 2.345 + + b = b2 + ub = pixely + w = b2 + w = w2 + w = ub + uw = ub + uw = uw2 + ;fl1 = ub ; @todo + ;fl1 = b2 ; @todo + ;fl1 = uw2 ; @todo + ;fl1 = w2 ; @todo + fl1 = fl2 + + b = mbyte + ub = mubyte + w = mword + w = mbyte + w = mubyte + uw = mubyte + uw = muword + fl1 = mfloat + ;fl1 = mbyte ; @todo + ;fl1 = mword ; @todo + ;fl1 = mubyte ; @todo + ;fl1 = muword ; @todo + + mbyte = 1 + mubyte = 1 + mword = 1 + muword = 1 + mfloat = 3.456 + + %breakpoint + + mbyte = b + mubyte = ub + mword = w + muword = uw + mfloat = fl2 + + %breakpoint + + mbyte = mbyte2 + mubyte = mubyte2 + mword = mword2 + muword = muword2 + mfloat = mfloat2 -; %breakpoint -; -; mbyte = b -; mubyte = ub -; mword = w -; muword = uw -; mfloat = fl2 -; -; %breakpoint -; -; mbyte = mbyte2 -; mubyte = mubyte2 -; mword = mword2 -; muword = muword2 -; mfloat = mfloat2 -; return } diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index b3571d7fc..e139cd718 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -931,7 +931,7 @@ class AstChecker(private val namespace: INameScope, val result = when(targetDatatype) { DataType.BYTE -> sourceDatatype==DataType.BYTE DataType.UBYTE -> sourceDatatype==DataType.UBYTE - DataType.WORD -> sourceDatatype==DataType.BYTE || sourceDatatype==DataType.WORD + DataType.WORD -> sourceDatatype==DataType.BYTE || sourceDatatype==DataType.UBYTE || sourceDatatype==DataType.WORD DataType.UWORD -> sourceDatatype==DataType.UBYTE || sourceDatatype==DataType.UWORD DataType.FLOAT -> sourceDatatype in NumericDatatypes DataType.STR -> sourceDatatype==DataType.STR diff --git a/compiler/src/prog8/ast/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/AstIdentifiersChecker.kt index d136554a3..f48107438 100644 --- a/compiler/src/prog8/ast/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/AstIdentifiersChecker.kt @@ -120,18 +120,20 @@ class AstIdentifiersChecker : IAstProcessor { } override fun process(forLoop: ForLoop): IStatement { - // if the for loop as a decltype, it means to declare the loopvar inside the loop body + // if the for loop has a decltype, it means to declare the loopvar inside the loop body // rather than reusing an already declared loopvar from an outer scope. - if(forLoop.loopRegister!=null && forLoop.decltype!=null) { - checkResult.add(SyntaxError("register loop variables cannot be explicitly declared with a datatype", forLoop.position)) - } else { - val loopVar = forLoop.loopVar!! - val varName = loopVar.nameInSource.last() + if(forLoop.loopRegister!=null) { + if(forLoop.decltype!=null) + checkResult.add(SyntaxError("register loop variables cannot be explicitly declared with a datatype", forLoop.position)) + if(forLoop.loopRegister == Register.X || forLoop.loopRegister==Register.XY || forLoop.loopRegister==Register.AX) + checkResult.add(SyntaxError("it's not possible to write to the X register because it's used as an internal pointer", forLoop.position)) + } else if(forLoop.loopVar!=null) { + val varName = forLoop.loopVar.nameInSource.last() when (forLoop.decltype) { DataType.UBYTE, DataType.UWORD -> { - val existing = if(forLoop.body.isEmpty()) null else forLoop.body.lookup(loopVar.nameInSource, forLoop.body.statements.first()) + val existing = if(forLoop.body.isEmpty()) null else forLoop.body.lookup(forLoop.loopVar.nameInSource, forLoop.body.statements.first()) if(existing==null) { - val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, null, varName, null, loopVar.position) + val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, null, varName, null, forLoop.loopVar.position) vardecl.linkParents(forLoop.body) forLoop.body.statements.add(0, vardecl) forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body' @@ -143,4 +145,10 @@ class AstIdentifiersChecker : IAstProcessor { } return super.process(forLoop) } + + override fun process(assignTarget: AssignTarget): AssignTarget { + if(assignTarget.register==Register.X || assignTarget.register==Register.AX || assignTarget.register==Register.XY) + checkResult.add(SyntaxError("it's not possible to write to the X register because it's used as an internal pointer", assignTarget.position)) + return super.process(assignTarget) + } } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index db04bdc1e..65b45f82f 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -440,8 +440,8 @@ private class StatementTranslator(private val prog: IntermediateProgram, BranchCondition.CC -> Opcode.BCS BranchCondition.EQ, BranchCondition.Z -> Opcode.BNZ BranchCondition.NE, BranchCondition.NZ -> Opcode.BZ - BranchCondition.VS -> TODO("Opcode.BVC") - BranchCondition.VC -> TODO("Opcode.BVS") + BranchCondition.VS -> Opcode.BVC + BranchCondition.VC -> Opcode.BVS BranchCondition.MI, BranchCondition.NEG -> Opcode.BPOS BranchCondition.PL, BranchCondition.POS -> Opcode.BNEG } @@ -470,6 +470,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, * An IF statement: IF (condition-expression) { stuff } else { other_stuff } * Which is translated into: * + * TEST * BZ _stmt_999_else * stuff * JUMP _stmt_999_end @@ -480,6 +481,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, * * or when there is no else block: * + * TEST * BZ _stmt_999_end * stuff * _stmt_999_end: @@ -491,11 +493,13 @@ private class StatementTranslator(private val prog: IntermediateProgram, translate(stmt.condition) val labelEnd = makeLabel("end") if(stmt.elsepart.isEmpty()) { + prog.instr(Opcode.TEST) prog.instr(Opcode.BZ, callLabel = labelEnd) translate(stmt.truepart) prog.label(labelEnd) } else { val labelElse = makeLabel("else") + prog.instr(Opcode.TEST) prog.instr(Opcode.BZ, callLabel = labelElse) translate(stmt.truepart) prog.instr(Opcode.JUMP, callLabel = labelEnd) @@ -1473,6 +1477,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, prog.instr(opcodePush(zero.type), Value(zero.type, numElements)) prog.instr(opcodePushvar(zero.type), callLabel = indexVar) prog.instr(opcodeSub(zero.type)) + prog.instr(Opcode.TEST) prog.instr(Opcode.BNZ, callLabel = loopLabel) prog.label(breakLabel) @@ -1535,6 +1540,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, prog.instr(opcodePush(varDt), Value(varDt, range.last + range.step)) prog.instr(opcodePushvar(varDt), callLabel = varname) prog.instr(opcodeSub(varDt)) + prog.instr(Opcode.TEST) prog.instr(Opcode.BNZ, callLabel = loopLabel) prog.label(breakLabel) @@ -1692,6 +1698,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, * continue -> goto condition * continue: * + * test * bnz loop * break: * nop @@ -1707,6 +1714,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, translate(stmt.body) prog.label(continueLabel) translate(stmt.condition) + prog.instr(Opcode.TEST) prog.instr(Opcode.BNZ, callLabel = loopLabel) prog.label(breakLabel) prog.instr(Opcode.NOP) @@ -1726,6 +1734,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, * continue -> goto condition * condition: * + * test * bz goto loop * break: * nop @@ -1740,6 +1749,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, translate(stmt.body) prog.label(continueLabel) translate(stmt.untilCondition) + prog.instr(Opcode.TEST) prog.instr(Opcode.BZ, callLabel = loopLabel) prog.label(breakLabel) prog.instr(Opcode.NOP) diff --git a/compiler/src/prog8/compiler/intermediate/Opcode.kt b/compiler/src/prog8/compiler/intermediate/Opcode.kt index 638895a35..f9ab137fb 100644 --- a/compiler/src/prog8/compiler/intermediate/Opcode.kt +++ b/compiler/src/prog8/compiler/intermediate/Opcode.kt @@ -158,6 +158,8 @@ enum class Opcode { DEC_VAR_F, // comparisons + // @todo the comparisons now push the result back on the stack. Optimize this to work with processor flags exclusively. This does mean you can no longer use a logical boolean result as a byte 0/1 value ? + TEST, // pop top value from stack and test it. Sets cpu flags (zero, negative, overflow) accordingly. LESS_B, LESS_UB, LESS_W, @@ -193,16 +195,16 @@ enum class Opcode { WRITE_INDEXED_VAR_WORD, WRITE_INDEXED_VAR_FLOAT, - // branching + // branching, without consuming a value from the stack JUMP, - BCS, - BCC, - BZ, // branch if value on top of stack is zero - BNZ, // branch if value on top of stack is not zero - BNEG, // branch if value on top of stack < 0 - BPOS, // branch if value on top of stack >= 0 - // BVS, // status flag V (overflow) not implemented - // BVC, // status flag V (overflow) not implemented + BCS, // branch if carry set + BCC, // branch if carry clear + BZ, // branch if zero flag + BNZ, // branch if not zero flag + BNEG, // branch if negative flag + BPOS, // branch if not negative flag + BVS, // branch if overflow flag + BVC, // branch if not overflow flag // subroutine calling CALL, diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index 664ec9153..eb9ccf55f 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -400,50 +400,50 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, "_prog8_breakpoint_$breakpointCounter\tnop" } - Opcode.PUSH_BYTE -> { - " lda #${ins.arg!!.integerValue().toHex()} | sta ${ESTACK_LO.toHex()},x | dex" - } - Opcode.PUSH_WORD -> { - val value = ins.arg!!.integerValue().toHex() - " lda #<$value | sta ${ESTACK_LO.toHex()},x | lda #>$value | sta ${ESTACK_HI.toHex()},x | dex" - } - Opcode.PUSH_FLOAT -> { - val floatConst = getFloatConst(ins.arg!!) - " lda #<$floatConst | ldy #>$floatConst | jsr prog8_lib.push_float" - } - Opcode.PUSH_VAR_BYTE -> { - when(ins.callLabel) { - "X" -> throw CompilerException("makes no sense to push X, it's used as a stack pointer itself") - "A" -> " sta ${ESTACK_LO.toHex()},x | dex" - "Y" -> " tya | sta ${ESTACK_LO.toHex()},x | dex" - else -> " lda ${ins.callLabel} | sta ${ESTACK_LO.toHex()},x | dex" - } - } - Opcode.PUSH_VAR_WORD -> { - when (ins.callLabel) { - "AX" -> throw CompilerException("makes no sense to push X, it's used as a stack pointer itself") - "XY" -> throw CompilerException("makes no sense to push X, it's used as a stack pointer itself") - "AY" -> " sta ${ESTACK_LO.toHex()},x | pha | tya | sta ${ESTACK_HI.toHex()},x | pla | dex" - else -> " lda ${ins.callLabel} | ldy ${ins.callLabel}+1 | sta ${ESTACK_LO.toHex()},x | pha | tya | sta ${ESTACK_HI.toHex()},x | pla | dex" - } - } - Opcode.PUSH_VAR_FLOAT -> " lda #<${ins.callLabel} | ldy #>${ins.callLabel}| jsr prog8_lib.push_float" - Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB -> { - """ - lda ${ins.arg!!.integerValue().toHex()} - sta ${ESTACK_LO.toHex()},x - dex - """ - } - Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW -> { - """ - lda ${ins.arg!!.integerValue().toHex()} - sta ${ESTACK_LO.toHex()},x - lda ${(ins.arg.integerValue()+1).toHex()} - sta ${ESTACK_HI.toHex()},x - dex - """ - } +// Opcode.PUSH_BYTE -> { +// " lda #${ins.arg!!.integerValue().toHex()} | sta ${ESTACK_LO.toHex()},x | dex" +// } +// Opcode.PUSH_WORD -> { +// val value = ins.arg!!.integerValue().toHex() +// " lda #<$value | sta ${ESTACK_LO.toHex()},x | lda #>$value | sta ${ESTACK_HI.toHex()},x | dex" +// } +// Opcode.PUSH_FLOAT -> { +// val floatConst = getFloatConst(ins.arg!!) +// " lda #<$floatConst | ldy #>$floatConst | jsr prog8_lib.push_float" +// } +// Opcode.PUSH_VAR_BYTE -> { +// when(ins.callLabel) { +// "X" -> throw CompilerException("makes no sense to push X, it's used as a stack pointer itself") +// "A" -> " sta ${ESTACK_LO.toHex()},x | dex" +// "Y" -> " tya | sta ${ESTACK_LO.toHex()},x | dex" +// else -> " lda ${ins.callLabel} | sta ${ESTACK_LO.toHex()},x | dex" +// } +// } +// Opcode.PUSH_VAR_WORD -> { +// when (ins.callLabel) { +// "AX" -> throw CompilerException("makes no sense to push X, it's used as a stack pointer itself") +// "XY" -> throw CompilerException("makes no sense to push X, it's used as a stack pointer itself") +// "AY" -> " sta ${ESTACK_LO.toHex()},x | pha | tya | sta ${ESTACK_HI.toHex()},x | pla | dex" +// else -> " lda ${ins.callLabel} | ldy ${ins.callLabel}+1 | sta ${ESTACK_LO.toHex()},x | pha | tya | sta ${ESTACK_HI.toHex()},x | pla | dex" +// } +// } +// Opcode.PUSH_VAR_FLOAT -> " lda #<${ins.callLabel} | ldy #>${ins.callLabel}| jsr prog8_lib.push_float" +// Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB -> { +// """ +// lda ${ins.arg!!.integerValue().toHex()} +// sta ${ESTACK_LO.toHex()},x +// dex +// """ +// } +// Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW -> { +// """ +// lda ${ins.arg!!.integerValue().toHex()} +// sta ${ESTACK_LO.toHex()},x +// lda ${(ins.arg.integerValue()+1).toHex()} +// sta ${ESTACK_HI.toHex()},x +// dex +// """ +// } Opcode.POP_MEM_B, Opcode.POP_MEM_UB -> { """ @@ -678,10 +678,12 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, Opcode.BCS -> " bcs ${ins.callLabel}" Opcode.BCC -> " bcc ${ins.callLabel}" - Opcode.BZ -> " beq ${ins.callLabel}" - Opcode.BNZ -> " bne ${ins.callLabel}" Opcode.BNEG -> " bmi ${ins.callLabel}" Opcode.BPOS -> " bpl ${ins.callLabel}" + Opcode.BVC -> " bvc ${ins.callLabel}" + Opcode.BVS -> " bvs ${ins.callLabel}" + Opcode.BZ -> " beq ${ins.callLabel}" + Opcode.BNZ -> " bne ${ins.callLabel}" Opcode.UB2FLOAT -> " jsr prog8_lib.ub2float" Opcode.B2FLOAT -> " jsr prog8_lib.b2float" Opcode.UW2FLOAT -> " jsr prog8_lib.uw2float" @@ -1035,10 +1037,16 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, // assignment: mem = bytevar/ubytevar AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.POP_MEM_B)) { segment -> - " lda ${segment[0].callLabel} | sta ${segment[1].arg!!.integerValue().toHex()}" + when(segment[0].callLabel) { + "A", "X", "Y" -> TODO("$segment") + else -> " lda ${segment[0].callLabel} | sta ${segment[1].arg!!.integerValue().toHex()}" + } }, AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.POP_MEM_UB)) { segment -> - " lda ${segment[0].callLabel} | sta ${segment[1].arg!!.integerValue().toHex()}" + when(segment[0].callLabel) { + "A", "X", "Y" -> TODO("$segment") + else -> " lda ${segment[0].callLabel} | sta ${segment[1].arg!!.integerValue().toHex()}" + } }, // assignment: mem = byte/ubyte @@ -1051,10 +1059,16 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, // assignment: (u)bytevar = membyte AsmPattern(listOf(Opcode.PUSH_MEM_B, Opcode.POP_VAR_BYTE)) { segment -> - " lda ${segment[0].arg!!.integerValue().toHex()} | sta ${segment[1].callLabel}" + when(segment[1].callLabel) { + "A", "X", "Y" -> " ld${segment[1].callLabel!!.toLowerCase()} ${segment[0].arg!!.integerValue().toHex()}" + else -> " lda ${segment[0].arg!!.integerValue().toHex()} | sta ${segment[1].callLabel}" + } }, AsmPattern(listOf(Opcode.PUSH_MEM_UB, Opcode.POP_VAR_BYTE)) { segment -> - " lda ${segment[0].arg!!.integerValue().toHex()} | sta ${segment[1].callLabel}" + when(segment[1].callLabel) { + "A", "X", "Y" -> " ld${segment[1].callLabel!!.toLowerCase()} ${segment[0].arg!!.integerValue().toHex()}" + else -> " lda ${segment[0].arg!!.integerValue().toHex()} | sta ${segment[1].callLabel}" + } }, @@ -1077,20 +1091,32 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, // assignment: mem = wordvar/uwordvar AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.POP_MEM_W)) { segment -> - """ - lda ${segment[0].callLabel} - sta ${segment[1].arg!!.integerValue().toHex()} - lda ${segment[0].callLabel}+1 - sta ${(segment[1].arg!!.integerValue()+1).toHex()} - """ + when(segment[0].callLabel) { + "AX" -> TODO("$segment") + "AY" -> TODO("$segment") + "XY" -> TODO("$segment") + else -> + """ + lda ${segment[0].callLabel} + sta ${segment[1].arg!!.integerValue().toHex()} + lda ${segment[0].callLabel}+1 + sta ${(segment[1].arg!!.integerValue()+1).toHex()} + """ + } }, AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.POP_MEM_UW)) { segment -> - """ - lda ${segment[0].callLabel} - sta ${segment[1].arg!!.integerValue().toHex()} - lda ${segment[0].callLabel}+1 - sta ${(segment[1].arg!!.integerValue()+1).toHex()} - """ + when(segment[0].callLabel) { + "AX" -> TODO("$segment") + "AY" -> TODO("$segment") + "XY" -> TODO("$segment") + else -> + """ + lda ${segment[0].callLabel} + sta ${segment[1].arg!!.integerValue().toHex()} + lda ${segment[0].callLabel}+1 + sta ${(segment[1].arg!!.integerValue()+1).toHex()} + """ + } }, // assignment: mem = word/uword @@ -1113,20 +1139,32 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, // assignment: (u)wordvar = memword AsmPattern(listOf(Opcode.PUSH_MEM_W, Opcode.POP_VAR_WORD)) { segment -> - """ - lda ${segment[0].arg!!.integerValue().toHex()} - sta ${segment[1].callLabel} - lda ${(segment[0].arg!!.integerValue()+1).toHex()} - sta ${segment[1].callLabel}+1 - """ + when(segment[1].callLabel) { + "AX" -> " lda ${segment[0].arg!!.integerValue().toHex()} | ldx ${(segment[0].arg!!.integerValue()+1).toHex()}" + "AY" -> " lda ${segment[0].arg!!.integerValue().toHex()} | ldy ${(segment[0].arg!!.integerValue()+1).toHex()}" + "XY" -> " ldx ${segment[0].arg!!.integerValue().toHex()} | ldy ${(segment[0].arg!!.integerValue()+1).toHex()}" + else -> + """ + lda ${segment[0].arg!!.integerValue().toHex()} + sta ${segment[1].callLabel} + lda ${(segment[0].arg!!.integerValue()+1).toHex()} + sta ${segment[1].callLabel}+1 + """ + } }, AsmPattern(listOf(Opcode.PUSH_MEM_UW, Opcode.POP_VAR_WORD)) { segment -> - """ - lda ${segment[0].arg!!.integerValue().toHex()} - sta ${segment[1].callLabel} - lda ${(segment[0].arg!!.integerValue()+1).toHex()} - sta ${segment[1].callLabel}+1 - """ + when(segment[1].callLabel) { + "AX" -> " lda ${segment[0].arg!!.integerValue().toHex()} | ldx ${(segment[0].arg!!.integerValue()+1).toHex()}" + "AY" -> " lda ${segment[0].arg!!.integerValue().toHex()} | ldy ${(segment[0].arg!!.integerValue()+1).toHex()}" + "XY" -> " ldx ${segment[0].arg!!.integerValue().toHex()} | ldy ${(segment[0].arg!!.integerValue()+1).toHex()}" + else -> + """ + lda ${segment[0].arg!!.integerValue().toHex()} + sta ${segment[1].callLabel} + lda ${(segment[0].arg!!.integerValue()+1).toHex()} + sta ${segment[1].callLabel}+1 + """ + } }, // assignment: var = float @@ -1191,17 +1229,396 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, """ }, - // @todo add all indexed assignment forms + // assignment: uwordvar = ubytevar + AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.UB2UWORD, Opcode.POP_VAR_WORD)) { segment -> + when(segment[0].callLabel) { + "A", "X", "Y" -> TODO("$segment") + else -> + when(segment[2].callLabel) { + "AX" -> " lda ${segment[0].callLabel} | ldx #0" + "AY" -> " lda ${segment[0].callLabel} | ldy #0" + "XY" -> " ldx ${segment[0].callLabel} | ldy #0" + else -> " lda ${segment[0].callLabel} | sta ${segment[2].callLabel} | lda #0 | sta ${segment[2].callLabel}+1" + } + } + }, + // assignment: wordvar = bytevar (sign extended) + AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.B2WORD, Opcode.POP_VAR_WORD)) { segment -> + when(segment[0].callLabel) { + "A", "X", "Y" -> TODO("$segment") + else -> + when(segment[2].callLabel) { + "AX" -> TODO(" $segment") + "AY" -> TODO(" $segment") + "XY" -> TODO(" $segment") + else -> + """ + lda ${segment[0].callLabel} + sta ${segment[2].callLabel} + ora #$7f + bmi + + lda #0 ++ sta ${segment[2].callLabel}+1 + """ + } + } + }, + // assignment: wordvar = membyte (sign extended) + AsmPattern(listOf(Opcode.PUSH_MEM_B, Opcode.B2WORD, Opcode.POP_VAR_WORD)) { segment -> + when(segment[2].callLabel) { + "AX" -> TODO("$segment") + "AY" -> TODO("$segment") + "XY" -> TODO("$segment") + else -> + """ + lda ${segment[0].arg!!.integerValue().toHex()} + sta ${segment[2].callLabel} + ora #$7f + bmi + + lda #0 ++ sta ${segment[2].callLabel}+1 + """ + } + }, + // assignment: uwordvar = mem ubyte + AsmPattern(listOf(Opcode.PUSH_MEM_UB, Opcode.UB2UWORD, Opcode.POP_VAR_WORD)) { segment -> + when(segment[2].callLabel) { + "AX" -> " lda ${segment[0].arg!!.integerValue().toHex()} | ldx #0" + "AY" -> " lda ${segment[0].arg!!.integerValue().toHex()} | ldy #0" + "XY" -> " ldx ${segment[0].arg!!.integerValue().toHex()} | ldy #0" + else -> " lda ${segment[0].arg!!.integerValue().toHex()} | sta ${segment[2].callLabel} | lda #0 | sta ${segment[2].callLabel}+1" + } + }, + // assignment: uwordvar = ubytearray[index_byte] + AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_BYTE, Opcode.UB2UWORD, Opcode.POP_VAR_WORD)) { segment -> + val index = segment[0].arg!!.integerValue().toHex() + when(segment[1].callLabel) { + "AX" -> TODO("$segment") + "AY" -> TODO("$segment") + "XY" -> TODO("$segment") + else -> + when(segment[3].callLabel) { + "AX" -> " lda ${segment[1].callLabel}+$index | ldx #0" + "AY" -> " lda ${segment[1].callLabel}+$index | ldy #0" + "XY" -> " ldx ${segment[1].callLabel}+$index | ldy #0" + else -> " lda ${segment[1].callLabel}+$index | sta ${segment[3].callLabel} | lda #0 | sta ${segment[3].callLabel}+1" + } + } + }, + // assignment: mem uword = ubytearray[index_byte] + AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_BYTE, Opcode.UB2UWORD, Opcode.POP_MEM_UW)) { segment -> + val index = segment[0].arg!!.integerValue().toHex() + """ + lda ${segment[1].callLabel}+$index + ldy ${segment[1].callLabel}+$index+1 + sta ${segment[3].arg!!.integerValue().toHex()} + sty ${(segment[3].arg!!.integerValue()+1).toHex()} + """ + }, + // assignment: (u)wordvar = (u)wordarray[index_byte] + AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_WORD, Opcode.POP_VAR_WORD)) { segment -> + val index = segment[0].arg!!.integerValue()*2 + when(segment[1].callLabel) { + "AX" -> TODO("$segment") + "AY" -> TODO("$segment") + "XY" -> TODO("$segment") + else -> + when(segment[2].callLabel) { + "AX" -> " lda ${segment[1].callLabel}+$index | ldx ${segment[1].callLabel}+$index+1" + "AY" -> " lda ${segment[1].callLabel}+$index | ldy ${segment[1].callLabel}+$index+1" + "XY" -> " ldx ${segment[1].callLabel}+$index | ldy ${segment[1].callLabel}+$index+1" + else -> " lda ${segment[1].callLabel}+$index | sta ${segment[2].callLabel} | lda ${segment[1].callLabel}+$index+1,x | sta ${segment[2].callLabel}+1" + } + } + }, + + // assignment: bytearray[idxbyte] = byte + AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_BYTE)) { segment -> + val index = segment[1].arg!!.integerValue() + val value = segment[0].arg!!.integerValue().toHex() + when(segment[2].callLabel) { + "AX" -> " sta ${C64Zeropage.SCRATCH_W1} | stx ${C64Zeropage.SCRATCH_W1+1} | ldy #$index | lda #$value | sta (${C64Zeropage.SCRATCH_W1}),y" + "AY" -> " sta ${C64Zeropage.SCRATCH_W1} | sty ${C64Zeropage.SCRATCH_W1+1} | ldy #$index | lda #$value | sta (${C64Zeropage.SCRATCH_W1}),y" + "XY" -> " stx ${C64Zeropage.SCRATCH_W1} | sty ${C64Zeropage.SCRATCH_W1+1} | ldy #$index | lda #$value | sta (${C64Zeropage.SCRATCH_W1}),y" + else -> " lda #$value | sta ${segment[2].callLabel}+$index" + } + }, + // assignment: bytearray[idxbyte] = bytevar + AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_BYTE)) { segment -> + val index = segment[1].arg!!.integerValue() + val saveValue: String + val loadValue: String + when(segment[0].callLabel) { + "A" -> { + saveValue = "" + loadValue = "" + } + "X" -> { + saveValue = "" + loadValue = "txa" + } + "Y" -> { + saveValue = "sty ${C64Zeropage.SCRATCH_B1}" + loadValue = "lda ${C64Zeropage.SCRATCH_B1}" + } + else -> { + saveValue = "" + loadValue = "lda ${segment[0].callLabel}" + } + } + when(segment[2].callLabel) { + "AX" -> " $saveValue | sta ${C64Zeropage.SCRATCH_W1} | stx ${C64Zeropage.SCRATCH_W1+1} | ldy #$index | $loadValue | sta (${C64Zeropage.SCRATCH_W1}),y" + "AY" -> " $saveValue | sta ${C64Zeropage.SCRATCH_W1} | sty ${C64Zeropage.SCRATCH_W1+1} | ldy #$index | $loadValue | sta (${C64Zeropage.SCRATCH_W1}),y" + "XY" -> " $saveValue | stx ${C64Zeropage.SCRATCH_W1} | sty ${C64Zeropage.SCRATCH_W1+1} | ldy #$index | $loadValue | sta (${C64Zeropage.SCRATCH_W1}),y" + else -> " $loadValue | sta ${segment[2].callLabel}+$index" + } + }, + // assignment: bytearray[idxbyte] = membyte + AsmPattern(listOf(Opcode.PUSH_MEM_B, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_BYTE)) { segment -> + val index = segment[1].arg!!.integerValue() + when(segment[2].callLabel) { + "AX" -> TODO("$segment") + "AY" -> TODO("$segment") + "XY" -> TODO("$segment") + else -> + """ + lda ${segment[0].arg!!.integerValue().toHex()} + sta ${segment[2].callLabel}+$index + """ + } + }, + // assignment: bytearray[idxbyte] = memubyte + AsmPattern(listOf(Opcode.PUSH_MEM_UB, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_BYTE)) { segment -> + val index = segment[1].arg!!.integerValue() + when(segment[2].callLabel) { + "AX" -> " sta ${C64Zeropage.SCRATCH_W1} | stx ${C64Zeropage.SCRATCH_W1+1} | ldy #$index | lda ${segment[0].arg!!.integerValue().toHex()} | sta (${C64Zeropage.SCRATCH_W1}),y" + "AY" -> " sta ${C64Zeropage.SCRATCH_W1} | sty ${C64Zeropage.SCRATCH_W1+1} | ldy #$index | lda ${segment[0].arg!!.integerValue().toHex()} | sta (${C64Zeropage.SCRATCH_W1}),y" + "XY" -> " stx ${C64Zeropage.SCRATCH_W1} | sty ${C64Zeropage.SCRATCH_W1+1} | ldy #$index | lda ${segment[0].arg!!.integerValue().toHex()} | sta (${C64Zeropage.SCRATCH_W1}),y" + else -> " lda ${segment[0].arg!!.integerValue().toHex()} | sta ${segment[2].callLabel}+$index" + } + }, + // assignment: wordarray[idxbyte] = word + AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment -> + val index = segment[1].arg!!.integerValue()*2 + when(segment[2].callLabel) { + "AX" -> TODO("$segment") + "AY" -> TODO("$segment") + "XY" -> TODO("$segment") + else -> + """ + lda #<${segment[0].arg!!.integerValue().toHex()} + ldy #>${segment[0].arg!!.integerValue().toHex()} + sta ${segment[2].callLabel}+$index + sty ${segment[2].callLabel}+$index+1 + """ + } + }, + // assignment: wordarray[idxbyte] = wordvar + AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment -> + val index = segment[1].arg!!.integerValue()*2 + when(segment[0].callLabel) { + "AX" -> " sta ${segment[2].callLabel}+$index | stx ${segment[2].callLabel}+$index+1" + "AY" -> " sta ${segment[2].callLabel}+$index | sty ${segment[2].callLabel}+$index+1" + "XY" -> " stx ${segment[2].callLabel}+$index | sty ${segment[2].callLabel}+$index+1" + else -> + """ + lda ${segment[0].callLabel} + ldy ${segment[0].callLabel}+1 + sta ${segment[2].callLabel}+$index + sty ${segment[2].callLabel}+$index+1 + """ + } + }, + // assignment: wordarray[idxbyte] = memword + AsmPattern(listOf(Opcode.PUSH_MEM_W, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment -> + val index = segment[1].arg!!.integerValue()*2 + when(segment[2].callLabel) { + "AX" -> TODO("$segment") + "AY" -> TODO("$segment") + "XY" -> TODO("$segment") + else -> + """ + lda ${segment[0].arg!!.integerValue().toHex()} + ldy ${segment[0].arg!!.integerValue().toHex()}+1 + sta ${segment[2].callLabel}+$index + sty ${segment[2].callLabel}+$index+1 + """ + } + }, + // assignment: wordarray[idxbyte] = memuword (=same as above) + AsmPattern(listOf(Opcode.PUSH_MEM_UW, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment -> + val index = segment[1].arg!!.integerValue()*2 + when(segment[2].callLabel) { + "AX" -> TODO("$segment") + "AY" -> TODO("$segment") + "XY" -> TODO("$segment") + else -> + """ + lda ${segment[0].arg!!.integerValue().toHex()} + ldy ${segment[0].arg!!.integerValue().toHex()}+1 + sta ${segment[2].callLabel}+$index + sty ${segment[2].callLabel}+$index+1 + """ + } + }, + // assignment: floatarray[idxbyte] = float + AsmPattern(listOf(Opcode.PUSH_FLOAT, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_FLOAT)) { segment -> + val floatConst = getFloatConst(segment[0].arg!!) + val index = segment[1].arg!!.integerValue() * Mflpt5.MemorySize + """ + lda #<$floatConst + ldy #>$floatConst + sta ${C64Zeropage.SCRATCH_W1} + sty ${C64Zeropage.SCRATCH_W1+1} + lda #<(${segment[2].callLabel}+$index) + ldy #>(${segment[2].callLabel}+$index+1) + sta ${C64Zeropage.SCRATCH_W2} + sty ${C64Zeropage.SCRATCH_W2+1} + jsr prog8_lib.copy_float + """ + }, + // assignment: floatarray[idxbyte] = floatvar + AsmPattern(listOf(Opcode.PUSH_VAR_FLOAT, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_FLOAT)) { segment -> + val index = segment[1].arg!!.integerValue() * Mflpt5.MemorySize + """ + lda #<${segment[0].callLabel} + ldy #>${segment[0].callLabel} + sta ${C64Zeropage.SCRATCH_W1} + sty ${C64Zeropage.SCRATCH_W1+1} + lda #<(${segment[2].callLabel}+$index) + ldy #>(${segment[2].callLabel}+$index+1) + sta ${C64Zeropage.SCRATCH_W2} + sty ${C64Zeropage.SCRATCH_W2+1} + jsr prog8_lib.copy_float + """ + }, + // assignment: floatarray[idxbyte] = memfloat + AsmPattern(listOf(Opcode.PUSH_MEM_FLOAT, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_FLOAT)) { segment -> + val index = segment[1].arg!!.integerValue() * Mflpt5.MemorySize + """ + lda #<${segment[0].arg!!.integerValue().toHex()} + ldy #>${segment[0].arg!!.integerValue().toHex()} + sta ${C64Zeropage.SCRATCH_W1} + sty ${C64Zeropage.SCRATCH_W1+1} + lda #<(${segment[2].callLabel}+$index) + ldy #>(${segment[2].callLabel}+$index+1) + sta ${C64Zeropage.SCRATCH_W2} + sty ${C64Zeropage.SCRATCH_W2+1} + jsr prog8_lib.copy_float + """ + }, // assignment: var = bytearray[index] AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_BYTE, Opcode.POP_VAR_BYTE)) { segment -> val index = segment[0].arg!!.integerValue() - when (segment[2].callLabel) { - "A", "X", "Y" -> - " ld${segment[2].callLabel!!.toLowerCase()} ${segment[1].callLabel}+$index" + when(segment[1].callLabel) { + "AX" -> TODO("$segment") + "AY" -> TODO("$segment") + "XY" -> TODO("$segment") else -> - TODO("assign byte to array indexed") + when (segment[2].callLabel) { + "A", "X", "Y" -> + " ld${segment[2].callLabel!!.toLowerCase()} ${segment[1].callLabel}+$index" + else -> + " lda ${segment[1].callLabel}+$index | sta ${segment[2].callLabel}" + } + } + }, + + // assignment: mem(u)byte = (u)bytearray[index] + AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_BYTE, Opcode.POP_MEM_B)) { segment -> + val address = segment[2].arg!!.integerValue().toHex() + val index = segment[0].arg!!.integerValue() + when(segment[1].callLabel) { + "AX" -> " sta ${C64Zeropage.SCRATCH_W1} | stx ${C64Zeropage.SCRATCH_W1+1} | ldy #$index | lda (${C64Zeropage.SCRATCH_W1}),y | sta $address" + "AY" -> " sta ${C64Zeropage.SCRATCH_W1} | sty ${C64Zeropage.SCRATCH_W1+1} | ldy #$index | lda (${C64Zeropage.SCRATCH_W1}),y | sta $address" + "XY" -> " stx ${C64Zeropage.SCRATCH_W1} | sty ${C64Zeropage.SCRATCH_W1+1} | ldy #$index | lda (${C64Zeropage.SCRATCH_W1}),y | sta $address" + else -> " lda ${segment[1].callLabel}+$index | sta $address" + } + }, + AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_BYTE, Opcode.POP_MEM_UB)) { segment -> + val address = segment[2].arg!!.integerValue().toHex() + val index = segment[0].arg!!.integerValue() + when(segment[1].callLabel) { + "AX" -> " sta ${C64Zeropage.SCRATCH_W1} | stx ${C64Zeropage.SCRATCH_W1+1} | ldy #$index | lda (${C64Zeropage.SCRATCH_W1}),y | sta $address" + "AY" -> " sta ${C64Zeropage.SCRATCH_W1} | sty ${C64Zeropage.SCRATCH_W1+1} | ldy #$index | lda (${C64Zeropage.SCRATCH_W1}),y | sta $address" + "XY" -> " stx ${C64Zeropage.SCRATCH_W1} | sty ${C64Zeropage.SCRATCH_W1+1} | ldy #$index | lda (${C64Zeropage.SCRATCH_W1}),y | sta $address" + else -> " lda ${segment[1].callLabel}+$index | sta $address" + } + }, + + // assignment: mem(u)word = (u)wordarray[index] + AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_WORD, Opcode.POP_MEM_W)) { segment -> + val index = segment[0].arg!!.integerValue()*2 + when(segment[1].callLabel) { + "AX" -> TODO("$segment") + "AY" -> TODO("$segment") + "XY" -> TODO("$segment") + else -> + """ + lda ${segment[1].callLabel}+$index + ldy ${segment[1].callLabel}+1+$index + sta ${segment[2].arg!!.integerValue().toHex()} + sty ${(segment[2].arg!!.integerValue()+1).toHex()} + """ + } + }, + AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_WORD, Opcode.POP_MEM_UW)) { segment -> + val index = segment[0].arg!!.integerValue()*2 + when(segment[1].callLabel) { + "AX" -> TODO("$segment") + "AY" -> TODO("$segment") + "XY" -> TODO("$segment") + else -> + """ + lda ${segment[1].callLabel}+$index + ldy ${segment[1].callLabel}+1+$index + sta ${segment[2].arg!!.integerValue().toHex()} + sty ${(segment[2].arg!!.integerValue()+1).toHex()} + """ + } + }, + + // assignment: bytearray2[index2] = bytearray1[index1] + AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_BYTE, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_BYTE)) { segment-> + val index1 = segment[0].arg!!.integerValue() + val index2 = segment[2].arg!!.integerValue() + when(segment[1].callLabel) { + "AX" -> TODO("$segment") + "AY" -> TODO("$segment") + "XY" -> TODO("$segment") + else -> + when(segment[3].callLabel) { + "AX" -> " sta ${C64Zeropage.SCRATCH_W1} | stx ${C64Zeropage.SCRATCH_W1+1} | lda ${segment[1].callLabel}+$index1 | ldy #$index2 | sta (${C64Zeropage.SCRATCH_W1}),y" + "AY" -> " sta ${C64Zeropage.SCRATCH_W1} | sty ${C64Zeropage.SCRATCH_W1+1} | lda ${segment[1].callLabel}+$index1 | ldy #$index2 | sta (${C64Zeropage.SCRATCH_W1}),y" + "XY" -> " stx ${C64Zeropage.SCRATCH_W1} | sty ${C64Zeropage.SCRATCH_W1+1} | lda ${segment[1].callLabel}+$index1 | ldy #$index2 | sta (${C64Zeropage.SCRATCH_W1}),y" + else -> " lda ${segment[1].callLabel}+$index1 | sta ${segment[3].callLabel}+$index2" + } + } + }, + // assignment: wordarrayw[index2] = wordarray1[index1] + AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_WORD, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment-> + val index1 = segment[0].arg!!.integerValue()*2 + val index2 = segment[2].arg!!.integerValue()*2 + when(segment[1].callLabel) { + "AX" -> TODO("$segment") + "AY" -> TODO("$segment") + "XY" -> TODO("$segment") + else -> + when(segment[3].callLabel) { + "AX" -> TODO("$segment") + "AY" -> TODO("$segment") + "XY" -> TODO("$segment") + else -> + """ + lda ${segment[1].callLabel}+$index1 + ldy ${segment[1].callLabel}+$index1+1 + sta ${segment[3].callLabel}+$index2 + sty ${segment[3].callLabel}+$index2+1 + """ + } } } + ) } diff --git a/compiler/src/prog8/optimizing/StatementOptimizer.kt b/compiler/src/prog8/optimizing/StatementOptimizer.kt index 1dd4f4fab..7df179546 100644 --- a/compiler/src/prog8/optimizing/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizing/StatementOptimizer.kt @@ -18,10 +18,9 @@ import prog8.functions.BuiltinFunctions todo optimize addition with self into shift 1 (A+=A -> A<<=1) todo assignment optimization: optimize some simple multiplications and divisions into shifts (A*=2 -> lsl(A), X=X/2 -> lsr(X) ) todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to) - todo merge sequence of assignments into one (as long as the value is a constant and the target not a MEMORY type!) + todo merge sequence of assignments into one to avoid repeated value loads (as long as the value is a constant and the target not a MEMORY type!) todo report more always true/always false conditions - todo inline subroutines that are only called once - todo inline subroutines that are "sufficiently small" + todo (optionally?) inline subroutines that are "sufficiently small" (=VERY small, say 0-3 statements, otherwise code size will explode and short branches will suffer) */ class StatementOptimizer(private val namespace: INameScope, private val heap: HeapValues) : IAstProcessor { diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index ff0443be4..1204ea948 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -100,6 +100,10 @@ class StackVm(private var traceOutputFile: String?) { val mem = Memory() var P_carry: Boolean = false private set + var P_negative: Boolean = false + private set + var P_zero: Boolean = false + private set var P_irqd: Boolean = false private set var variables = mutableMapOf() // all variables (set of all vars used by all blocks/subroutines) key = their fully scoped name @@ -739,13 +743,18 @@ class StackVm(private var traceOutputFile: String?) { Opcode.BCC -> return if(P_carry) ins.nextAlt!! else ins.next Opcode.BZ -> - return if(evalstack.pop().numericValue().toDouble()==0.0) ins.next else ins.nextAlt!! + return if(P_zero) ins.next else ins.nextAlt!! Opcode.BNZ -> - return if(evalstack.pop().numericValue().toDouble()!=0.0) ins.next else ins.nextAlt!! + return if(P_zero) ins.nextAlt!! else ins.next Opcode.BNEG -> - return if(evalstack.pop().numericValue().toDouble()<0.0) ins.next else ins.nextAlt!! - Opcode.BPOS -> { - return if (evalstack.pop().numericValue().toDouble() >= 0.0) ins.next else ins.nextAlt!! + return if(P_negative) ins.next else ins.nextAlt!! + Opcode.BPOS -> + return if(P_negative) ins.nextAlt!! else ins.next + Opcode.BVS, Opcode.BVC -> throw VmExecutionException("stackVM doesn't support the overflow flag") + Opcode.TEST -> { + val value=evalstack.pop().numericValue().toDouble() + P_zero = value == 0.0 + P_negative = value < 0.0 } Opcode.CALL -> callstack.push(ins.nextAlt) @@ -1329,6 +1338,8 @@ class StackVm(private var traceOutputFile: String?) { Opcode.RSAVE -> { evalstack.push(Value(DataType.UBYTE, if(P_irqd) 1 else 0)) evalstack.push(Value(DataType.UBYTE, if(P_carry) 1 else 0)) + evalstack.push(Value(DataType.UBYTE, if(P_negative) 1 else 0)) + evalstack.push(Value(DataType.UBYTE, if(P_zero) 1 else 0)) evalstack.push(variables["X"]) evalstack.push(variables["Y"]) evalstack.push(variables["A"]) @@ -1337,6 +1348,8 @@ class StackVm(private var traceOutputFile: String?) { variables["A"] = evalstack.pop() variables["X"] = evalstack.pop() variables["Y"] = evalstack.pop() + P_zero = evalstack.pop().asBooleanValue + P_negative = evalstack.pop().asBooleanValue P_carry = evalstack.pop().asBooleanValue P_irqd = evalstack.pop().asBooleanValue } diff --git a/compiler/test/StackVMOpcodeTests.kt b/compiler/test/StackVMOpcodeTests.kt index 5387774fe..e67a20102 100644 --- a/compiler/test/StackVMOpcodeTests.kt +++ b/compiler/test/StackVMOpcodeTests.kt @@ -98,6 +98,8 @@ class TestStackVmOpcodes { vm.load(makeProg(ins), null) assertFalse(vm.P_carry) assertFalse(vm.P_irqd) + assertFalse(vm.P_negative) + assertFalse(vm.P_zero) vm.step(1) assertTrue(vm.P_carry) assertFalse(vm.P_irqd) @@ -862,17 +864,19 @@ class TestStackVmOpcodes { fun testBZ() { val ins = mutableListOf( Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 1)), + Instruction(Opcode.TEST), Instruction(Opcode.BZ, callLabel = "label"), Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 0)), + Instruction(Opcode.TEST), Instruction(Opcode.BZ, callLabel = "label"), Instruction(Opcode.LINE, callLabel = "string1"), Instruction(Opcode.TERMINATE), Instruction(Opcode.LINE, callLabel = "string2")) val labels = mapOf("label" to ins.last()) // points to the second LINE instruction vm.load(makeProg(ins, labels=labels), null) - vm.step(2) - assertEquals("", vm.sourceLine) vm.step(3) + assertEquals("", vm.sourceLine) + vm.step(4) assertEquals("string2", vm.sourceLine) assertEquals(0, vm.callstack.size) assertEquals(0, vm.evalstack.size) @@ -882,17 +886,19 @@ class TestStackVmOpcodes { fun testBNZ() { val ins = mutableListOf( Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 0)), + Instruction(Opcode.TEST), Instruction(Opcode.BNZ, callLabel = "label"), Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 1)), + Instruction(Opcode.TEST), Instruction(Opcode.BNZ, callLabel = "label"), Instruction(Opcode.LINE, callLabel = "string1"), Instruction(Opcode.TERMINATE), Instruction(Opcode.LINE, callLabel = "string2")) val labels = mapOf("label" to ins.last()) // points to the second LINE instruction vm.load(makeProg(ins, labels=labels), null) - vm.step(2) - assertEquals("", vm.sourceLine) vm.step(3) + assertEquals("", vm.sourceLine) + vm.step(4) assertEquals("string2", vm.sourceLine) assertEquals(0, vm.callstack.size) assertEquals(0, vm.evalstack.size) @@ -902,21 +908,24 @@ class TestStackVmOpcodes { fun testBNEG() { val ins = mutableListOf( Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 0)), + Instruction(Opcode.TEST), Instruction(Opcode.BNEG, callLabel = "label"), Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 1)), + Instruction(Opcode.TEST), Instruction(Opcode.BNEG, callLabel = "label"), Instruction(Opcode.PUSH_FLOAT, Value(DataType.FLOAT, -99)), + Instruction(Opcode.TEST), Instruction(Opcode.BNEG, callLabel = "label"), Instruction(Opcode.LINE, callLabel = "string1"), Instruction(Opcode.TERMINATE), Instruction(Opcode.LINE, callLabel = "string2")) val labels = mapOf("label" to ins.last()) // points to the second LINE instruction vm.load(makeProg(ins, labels=labels), null) - vm.step(2) - assertEquals("", vm.sourceLine) - vm.step(2) + vm.step(3) assertEquals("", vm.sourceLine) vm.step(3) + assertEquals("", vm.sourceLine) + vm.step(4) assertEquals("string2", vm.sourceLine) assertEquals(0, vm.callstack.size) assertEquals(0, vm.evalstack.size) @@ -926,17 +935,19 @@ class TestStackVmOpcodes { fun testBPOS() { val ins = mutableListOf( Instruction(Opcode.PUSH_FLOAT, Value(DataType.FLOAT, -99)), + Instruction(Opcode.TEST), Instruction(Opcode.BPOS, callLabel = "label"), Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 0)), + Instruction(Opcode.TEST), Instruction(Opcode.BPOS, callLabel = "label"), Instruction(Opcode.LINE, callLabel = "string1"), Instruction(Opcode.TERMINATE), Instruction(Opcode.LINE, callLabel = "string2")) val labels = mapOf("label" to ins.last()) // points to the second LINE instruction vm.load(makeProg(ins, labels=labels), null) - vm.step(2) - assertEquals("", vm.sourceLine) vm.step(3) + assertEquals("", vm.sourceLine) + vm.step(4) assertEquals("string2", vm.sourceLine) assertEquals(0, vm.callstack.size) assertEquals(0, vm.evalstack.size) diff --git a/prog8lib/prog8lib.p8 b/prog8lib/prog8lib.p8 index d32fca148..c20460865 100644 --- a/prog8lib/prog8lib.p8 +++ b/prog8lib/prog8lib.p8 @@ -29,6 +29,7 @@ ror2_word ; @todo: stubs for now +; @todo: move float operations to their own library (only included when floats are enabled) ub2float rts @@ -63,6 +64,9 @@ sub_f mul_f rts +neg_f + rts + sub_uw rts @@ -72,5 +76,73 @@ less_ub less_f rts + +func_sin + rts +func_cos + rts +func_abs + rts +func_acos + rts +func_asin + rts +func_tan + rts +func_atan + rts +func_ln + rts +func_log2 + rts +func_log10 + rts +func_sqrt + rts +func_rad + rts +func_deg + rts +func_round + rts +func_floor + rts +func_ceil + rts +func_max + rts +func_min + rts +func_avg + rts +func_sum + rts +func_len + rts +func_any + rts +func_all + rts +func_rnd + rts +func_rndw + rts +func_rndf + rts +func_wrd + rts +func_uwrd + rts +func_str2byte + rts +func_str2ubyte + rts +func_str2word + rts +func_str2uword + rts +func_str2float + rts + }} }