From 5ff79073f452083c1b894d150de30064b5a0040b Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 9 Jul 2019 04:09:29 +0200 Subject: [PATCH] added DUP opcodes --- .../src/prog8/ast/processing/AstChecker.kt | 2 +- .../VarInitValueAndAddressOfCreator.kt | 1 + compiler/src/prog8/compiler/Compiler.kt | 45 +++++++++++++++---- .../src/prog8/compiler/intermediate/Opcode.kt | 2 + .../src/prog8/compiler/target/c64/AsmGen.kt | 6 +++ compiler/src/prog8/vm/stackvm/StackVm.kt | 10 +++++ examples/test.p8 | 21 ++++++++- parser/antlr/prog8.g4 | 2 +- parser/src/prog8/parser/prog8Lexer.java | 2 +- parser/src/prog8/parser/prog8Parser.java | 2 +- 10 files changed, 80 insertions(+), 13 deletions(-) diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index d860351dd..9d34b24f9 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -931,7 +931,7 @@ internal class AstChecker(private val program: Program, if (constvalue == null) checkResult.add(SyntaxError("value of a when choice must be a constant", whenChoice.position)) else if (constvalue.type !in IntegerDatatypes) - checkResult.add(SyntaxError("value of a when choice must be an integer", whenChoice.position)) + checkResult.add(SyntaxError("value of a when choice must be a byte or word", whenChoice.position)) } else { if(whenChoice !== (whenChoice.parent as WhenStatement).choices.last()) checkResult.add(SyntaxError("else choice must be the last one", whenChoice.position)) diff --git a/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt b/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt index c76ef5aff..58a52c6bf 100644 --- a/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt +++ b/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt @@ -110,6 +110,7 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope val variable = VarDecl(VarDeclType.VAR, strvalue.type, false, null, autoVarName, strvalue, isArray = false, autoGenerated = false, position = strvalue.position) addVarDecl(strvalue.definingScope(), variable) + println("MADE ANONVAR $variable") // XXX } } } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 831a7498b..c5e528cd5 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -2083,26 +2083,55 @@ internal class Compiler(private val program: Program) { private fun translate(whenstmt: WhenStatement) { val conditionDt = whenstmt.condition.inferType(program) + if(conditionDt !in IntegerDatatypes) + throw CompilerException("when condition must be integer") translate(whenstmt.condition) if(whenstmt.choices.isEmpty()) { - when(conditionDt) { - in ByteDatatypes -> prog.instr(Opcode.DISCARD_BYTE) - in WordDatatypes -> prog.instr(Opcode.DISCARD_WORD) - else -> throw CompilerException("when condition must be integer") - } + if (conditionDt in ByteDatatypes) prog.instr(Opcode.DISCARD_BYTE) + else prog.instr(Opcode.DISCARD_WORD) return } - // TODO compare + val endOfWhenLabel = makeLabel(whenstmt, "when_end") + val choiceLabels = mutableListOf() + var previousValue = 0 for(choice in whenstmt.choiceValues(program)) { - if(choice.first==null) { + val choiceVal = choice.first + if(choiceVal==null) { // the else clause translate(choice.second.statements) + } else { + val subtract = choiceVal-previousValue + previousValue = choiceVal + if (conditionDt in ByteDatatypes) { + prog.instr(Opcode.DUP_B) + prog.instr(Opcode.PUSH_BYTE, RuntimeValue(conditionDt!!, subtract)) + prog.instr(opcodeCompare(conditionDt)) + } + else { + prog.instr(Opcode.DUP_W) + prog.instr(Opcode.PUSH_WORD, RuntimeValue(conditionDt!!, subtract)) + prog.instr(opcodeCompare(conditionDt)) + } + val choiceLabel = makeLabel(whenstmt, "choice_$choiceVal") + choiceLabels.add(choiceLabel) + prog.instr(Opcode.BZ, callLabel = choiceLabel) } } + prog.instr(Opcode.JUMP, callLabel = endOfWhenLabel) - TODO("whenstmt $whenstmt with choice values ${whenstmt.choiceValues(program).map{it.first}}") + for(choice in whenstmt.choices.zip(choiceLabels)) { + // TODO the various code blocks here, don't forget to jump to the end label at their eind + prog.label(choice.second) + prog.instr(Opcode.NOP) + prog.instr(Opcode.JUMP, callLabel = endOfWhenLabel) + } + + prog.label(endOfWhenLabel) + + if (conditionDt in ByteDatatypes) prog.instr(Opcode.DISCARD_BYTE) + else prog.instr(Opcode.DISCARD_WORD) } private fun translateAsmInclude(args: List, source: Path) { diff --git a/compiler/src/prog8/compiler/intermediate/Opcode.kt b/compiler/src/prog8/compiler/intermediate/Opcode.kt index 3d7719d4a..b66d5d043 100644 --- a/compiler/src/prog8/compiler/intermediate/Opcode.kt +++ b/compiler/src/prog8/compiler/intermediate/Opcode.kt @@ -19,6 +19,8 @@ enum class Opcode { PUSH_REGAY_WORD, // push registers A/Y as a 16-bit word PUSH_REGXY_WORD, // push registers X/Y as a 16-bit word PUSH_ADDR_HEAPVAR, // push the address of the variable that's on the heap (string or array) + DUP_B, // duplicate the top byte on the stack + DUP_W, // duplicate the top word on the stack // popping values off the (evaluation) stack, possibly storing them in another location DISCARD_BYTE, // discard top byte value diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index 33880322c..5ff3a164d 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -475,6 +475,12 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter Opcode.RRESTOREX -> " sta ${C64Zeropage.SCRATCH_REG} | pla | tax | lda ${C64Zeropage.SCRATCH_REG}" Opcode.DISCARD_BYTE -> " inx" Opcode.DISCARD_WORD -> " inx" + Opcode.DUP_B -> { + " dex | lda ${(ESTACK_LO+1).toHex()},x | sta ${ESTACK_LO.toHex()},x" + } + Opcode.DUP_W -> { + " dex | lda ${(ESTACK_LO+1).toHex()},x | sta ${ESTACK_LO.toHex()},x | lda ${(ESTACK_HI+1).toHex()},x | sta ${ESTACK_HI.toHex()},x " + } Opcode.DISCARD_FLOAT -> " inx | inx | inx" Opcode.INLINE_ASSEMBLY -> "@inline@" + (ins.callLabel2 ?: "") // All of the inline assembly is stored in the calllabel2 property. the '@inline@' is a special marker to accept it. Opcode.INCLUDE_FILE -> { diff --git a/compiler/src/prog8/vm/stackvm/StackVm.kt b/compiler/src/prog8/vm/stackvm/StackVm.kt index 044bcf37b..e02d9d8de 100644 --- a/compiler/src/prog8/vm/stackvm/StackVm.kt +++ b/compiler/src/prog8/vm/stackvm/StackVm.kt @@ -376,6 +376,16 @@ class StackVm(private var traceOutputFile: String?) { val value = evalstack.pop() checkDt(value, DataType.FLOAT) } + Opcode.DUP_B -> { + val value = evalstack.peek() + checkDt(value, DataType.BYTE, DataType.UBYTE) + evalstack.push(value) + } + Opcode.DUP_W -> { + val value = evalstack.peek() + checkDt(value, DataType.WORD, DataType.UWORD) + evalstack.push(value) + } Opcode.POP_MEM_BYTE -> { val value = evalstack.pop() checkDt(value, DataType.BYTE, DataType.UBYTE) diff --git a/examples/test.p8 b/examples/test.p8 index df6a0de4b..bf3b93dba 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,6 +7,22 @@ sub start() { A=10 Y=22 + uword uw = A*Y + + when uw { + 12345 -> { + A=44 + } + 12346 -> { + A=44 + } + 12347 -> { + A=44 + } + else -> { + A=0 + } + } when 4+A+Y { 10 -> { @@ -14,6 +30,9 @@ } 5 -> c64scr.print("five") 30 -> c64scr.print("thirty") + 31 -> c64scr.print("thirty1") + 32 -> c64scr.print("thirty2") + 33 -> c64scr.print("thirty3") 99 -> c64scr.print("nn") 55 -> { ; should be optimized away @@ -21,7 +40,7 @@ 56 -> { ; should be optimized away } - 57 -> { + 57243 -> { ; should be optimized away } else -> { diff --git a/parser/antlr/prog8.g4 b/parser/antlr/prog8.g4 index 190bea2bf..d37aa6c90 100644 --- a/parser/antlr/prog8.g4 +++ b/parser/antlr/prog8.g4 @@ -197,7 +197,7 @@ scoped_identifier : NAME ('.' NAME)* ; register : 'A' | 'X' | 'Y' ; -registerorpair : 'A' | 'X' | 'Y' | 'AX' | 'AY' | 'XY' ; // only used in subroutine params and returnvalues +registerorpair : 'A' | 'X' | 'Y' | 'AX' | 'AY' | 'XY' ; // pairs can only be used in subroutine params and returnvalues statusregister : 'Pc' | 'Pz' | 'Pn' | 'Pv' ; diff --git a/parser/src/prog8/parser/prog8Lexer.java b/parser/src/prog8/parser/prog8Lexer.java index 8e9b994cb..efe8c9c15 100644 --- a/parser/src/prog8/parser/prog8Lexer.java +++ b/parser/src/prog8/parser/prog8Lexer.java @@ -1,4 +1,4 @@ -// Generated from /home/irmen/Projects/prog8/parser/antlr/prog8.g4 by ANTLR 4.7.2 +// Generated from prog8.g4 by ANTLR 4.7.2 package prog8.parser; diff --git a/parser/src/prog8/parser/prog8Parser.java b/parser/src/prog8/parser/prog8Parser.java index b0edb3910..3199f8247 100644 --- a/parser/src/prog8/parser/prog8Parser.java +++ b/parser/src/prog8/parser/prog8Parser.java @@ -1,4 +1,4 @@ -// Generated from /home/irmen/Projects/prog8/parser/antlr/prog8.g4 by ANTLR 4.7.2 +// Generated from prog8.g4 by ANTLR 4.7.2 package prog8.parser;