From 504d1440cc1506f7b0e40207189d5d10b8687df4 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 16 Jan 2024 20:41:53 +0100 Subject: [PATCH] fixed rol(),rol2(),ror(),ror2() --- .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 16 + .../codegen/intermediate/AssignmentGen.kt | 56 +- .../codegen/intermediate/BuiltinFuncGen.kt | 18 +- compiler/test/arithmetic/bitshift2.p8 | 865 ++++++++++++++++++ compiler/test/ast/TestConst.kt | 18 + .../test/codegeneration/TestArrayThings.kt | 14 + docs/source/todo.rst | 5 +- examples/test.p8 | 88 -- gradle.properties | 2 +- .../src/prog8/intermediate/IRInstructions.kt | 6 + virtualmachine/src/prog8/vm/VirtualMachine.kt | 24 + 11 files changed, 988 insertions(+), 124 deletions(-) create mode 100644 compiler/test/arithmetic/bitshift2.p8 diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 75b406783..26a56983c 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -471,7 +471,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, DataType.UBYTE -> { when (what) { is PtArrayIndexer -> { + if(!what.index.isSimple()) asmgen.out(" php") // save Carry asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X) + if(!what.index.isSimple()) asmgen.out(" plp") val varname = asmgen.asmVariableName(what.variable) asmgen.out(" ror ${varname},x") } @@ -482,15 +484,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, } else { val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address) if(ptrAndIndex!=null) { + asmgen.out(" php") asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A) asmgen.saveRegisterStack(CpuRegister.A, true) asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY) asmgen.out(" sta (+) + 1 | sty (+) + 2") asmgen.restoreRegisterStack(CpuRegister.X, false) asmgen.out(""" + plp + ror ${'$'}ffff,x ; modified""") } else { + if(!what.address.isSimple()) asmgen.out(" php") // save Carry asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY) + if(!what.address.isSimple()) asmgen.out(" plp") asmgen.out(""" sta (+) + 1 sty (+) + 2 @@ -508,7 +514,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, DataType.UWORD -> { when (what) { is PtArrayIndexer -> { + if(!what.index.isSimple()) asmgen.out(" php") // save Carry asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X) + if(!what.index.isSimple()) asmgen.out(" plp") val varname = asmgen.asmVariableName(what.variable) if(what.splitWords) asmgen.out(" ror ${varname}_msb,x | ror ${varname}_lsb,x") @@ -579,7 +587,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, DataType.UBYTE -> { when (what) { is PtArrayIndexer -> { + if(!what.index.isSimple()) asmgen.out(" php") // save Carry asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X) + if(!what.index.isSimple()) asmgen.out(" plp") val varname = asmgen.asmVariableName(what.variable) asmgen.out(" rol ${varname},x") } @@ -590,15 +600,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, } else { val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address) if(ptrAndIndex!=null) { + asmgen.out(" php") asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A) asmgen.saveRegisterStack(CpuRegister.A, true) asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY) asmgen.out(" sta (+) + 1 | sty (+) + 2") asmgen.restoreRegisterStack(CpuRegister.X, false) asmgen.out(""" + plp + rol ${'$'}ffff,x ; modified""") } else { + if(!what.address.isSimple()) asmgen.out(" php") // save Carry asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY) + if(!what.address.isSimple()) asmgen.out(" plp") asmgen.out(""" sta (+) + 1 sty (+) + 2 @@ -616,7 +630,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, DataType.UWORD -> { when (what) { is PtArrayIndexer -> { + if(!what.index.isSimple()) asmgen.out(" php") // save Carry asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X) + if(!what.index.isSimple()) asmgen.out(" plp") val varname = asmgen.asmVariableName(what.variable) if(what.splitWords) asmgen.out(" rol ${varname}_lsb,x | rol ${varname}_msb,x") diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt index 8b5a0dd93..aa37315b2 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt @@ -50,25 +50,26 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express assignment: PtAugmentedAssign ): IRCodeChunks { val value = assignment.value - val vmDt = irType(value.type) + val targetDt = irType(assignment.target.type) + val signed = assignment.target.type in SignedDatatypes return when(assignment.operator) { - "+=" -> expressionEval.operatorPlusInplace(address, null, vmDt, value) - "-=" -> expressionEval.operatorMinusInplace(address, null, vmDt, value) - "*=" -> expressionEval.operatorMultiplyInplace(address, null, vmDt, value) - "/=" -> expressionEval.operatorDivideInplace(address, null, vmDt, value.type in SignedDatatypes, value) - "|=" -> expressionEval.operatorOrInplace(address, null, vmDt, value) - "&=" -> expressionEval.operatorAndInplace(address, null, vmDt, value) - "^=" -> expressionEval.operatorXorInplace(address, null, vmDt, value) - "<<=" -> expressionEval.operatorShiftLeftInplace(address, null, vmDt, value) - ">>=" -> expressionEval.operatorShiftRightInplace(address, null, vmDt, value.type in SignedDatatypes, value) - "%=" -> expressionEval.operatorModuloInplace(address, null, vmDt, value) - "==" -> expressionEval.operatorEqualsInplace(address, null, vmDt, value) - "!=" -> expressionEval.operatorNotEqualsInplace(address, null, vmDt, value) - "<" -> expressionEval.operatorLessInplace(address, null, vmDt, value.type in SignedDatatypes, value) - ">" -> expressionEval.operatorGreaterInplace(address, null, vmDt, value.type in SignedDatatypes, value) - "<=" -> expressionEval.operatorLessEqualInplace(address, null, vmDt, value.type in SignedDatatypes, value) - ">=" -> expressionEval.operatorGreaterEqualInplace(address, null, vmDt, value.type in SignedDatatypes, value) - in PrefixOperators -> inplacePrefix(assignment.operator, vmDt, address, null) + "+=" -> expressionEval.operatorPlusInplace(address, null, targetDt, value) + "-=" -> expressionEval.operatorMinusInplace(address, null, targetDt, value) + "*=" -> expressionEval.operatorMultiplyInplace(address, null, targetDt, value) + "/=" -> expressionEval.operatorDivideInplace(address, null, targetDt, signed, value) + "|=" -> expressionEval.operatorOrInplace(address, null, targetDt, value) + "&=" -> expressionEval.operatorAndInplace(address, null, targetDt, value) + "^=" -> expressionEval.operatorXorInplace(address, null, targetDt, value) + "<<=" -> expressionEval.operatorShiftLeftInplace(address, null, targetDt, value) + ">>=" -> expressionEval.operatorShiftRightInplace(address, null, targetDt, signed, value) + "%=" -> expressionEval.operatorModuloInplace(address, null, targetDt, value) + "==" -> expressionEval.operatorEqualsInplace(address, null, targetDt, value) + "!=" -> expressionEval.operatorNotEqualsInplace(address, null, targetDt, value) + "<" -> expressionEval.operatorLessInplace(address, null, targetDt, signed, value) + ">" -> expressionEval.operatorGreaterInplace(address, null, targetDt, signed, value) + "<=" -> expressionEval.operatorLessEqualInplace(address, null, targetDt, signed, value) + ">=" -> expressionEval.operatorGreaterEqualInplace(address, null, targetDt, signed, value) + in PrefixOperators -> inplacePrefix(assignment.operator, targetDt, address, null) else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}") } @@ -76,26 +77,27 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks { val value = assignment.value + val signed = assignment.target.type in SignedDatatypes val targetDt = irType(assignment.target.type) - return when (assignment.operator) { + return when(assignment.operator) { "+=" -> expressionEval.operatorPlusInplace(null, symbol, targetDt, value) "-=" -> expressionEval.operatorMinusInplace(null, symbol, targetDt, value) "*=" -> expressionEval.operatorMultiplyInplace(null, symbol, targetDt, value) - "/=" -> expressionEval.operatorDivideInplace(null, symbol, targetDt, value.type in SignedDatatypes, value) + "/=" -> expressionEval.operatorDivideInplace(null, symbol, targetDt, signed, value) "|=" -> expressionEval.operatorOrInplace(null, symbol, targetDt, value) "or=" -> expressionEval.operatorLogicalOrInplace(null, symbol, targetDt, value) "&=" -> expressionEval.operatorAndInplace(null, symbol, targetDt, value) "and=" -> expressionEval.operatorLogicalAndInplace(null, symbol, targetDt, value) "^=", "xor=" -> expressionEval.operatorXorInplace(null, symbol, targetDt, value) "<<=" -> expressionEval.operatorShiftLeftInplace(null, symbol, targetDt, value) - ">>=" -> expressionEval.operatorShiftRightInplace(null, symbol, targetDt, value.type in SignedDatatypes, value) + ">>=" -> expressionEval.operatorShiftRightInplace(null, symbol, targetDt, signed, value) "%=" -> expressionEval.operatorModuloInplace(null, symbol, targetDt, value) "==" -> expressionEval.operatorEqualsInplace(null, symbol, targetDt, value) "!=" -> expressionEval.operatorNotEqualsInplace(null, symbol, targetDt, value) - "<" -> expressionEval.operatorLessInplace(null, symbol, targetDt, value.type in SignedDatatypes, value) - ">" -> expressionEval.operatorGreaterInplace(null, symbol, targetDt, value.type in SignedDatatypes, value) - "<=" -> expressionEval.operatorLessEqualInplace(null, symbol, targetDt, value.type in SignedDatatypes, value) - ">=" -> expressionEval.operatorGreaterEqualInplace(null, symbol, targetDt, value.type in SignedDatatypes, value) + "<" -> expressionEval.operatorLessInplace(null, symbol, targetDt, signed, value) + ">" -> expressionEval.operatorGreaterInplace(null, symbol, targetDt, signed, value) + "<=" -> expressionEval.operatorLessEqualInplace(null, symbol, targetDt, signed, value) + ">=" -> expressionEval.operatorGreaterEqualInplace(null, symbol, targetDt, signed, value) in PrefixOperators -> inplacePrefix(assignment.operator, targetDt, null, symbol) else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}") } @@ -104,11 +106,11 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks { val value: PtExpression if(origAssign.operator in PrefixOperators) { - value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position) + value = PtPrefix(origAssign.operator, origAssign.target.type, origAssign.value.position) value.add(origAssign.value) } else { require(origAssign.operator.endsWith('=')) - value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position) + value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.target.type, origAssign.value.position) val left: PtExpression = origAssign.target.children.single() as PtExpression value.add(left) value.add(origAssign.value) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index 71558aef1..208acc90b 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -623,22 +623,28 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe val tr = exprGen.translateExpression(call.args.single()) addToResult(result, tr, tr.resultReg, -1) val resultReg = codeGen.registers.nextFree() - result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg) - } + addInstr(result, IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null) // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) } private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall): ExpressionCodeResult { + // TODO optimize this to use the other ROL/ROR instructions too to always load into a temp reg val vmDt = irType(call.args[0].type) val result = mutableListOf() + val saveCarry = opcode in OpcodesThatDependOnCarry && !call.args[0].isSimple() + if(saveCarry) + addInstr(result, IRInstruction(Opcode.PUSHST), null) // save Carry val tr = exprGen.translateExpression(call.args[0]) addToResult(result, tr, tr.resultReg, -1) - result += IRCodeChunk(null, null).also { - it += IRInstruction(opcode, vmDt, reg1 = tr.resultReg) - } + if(saveCarry) + addInstr(result, IRInstruction(Opcode.POPST), null) + addInstr(result, IRInstruction(opcode, vmDt, reg1 = tr.resultReg), null) + if(saveCarry) + addInstr(result, IRInstruction(Opcode.PUSHST), null) // save Carry result += assignRegisterTo(call.args[0], tr.resultReg) + if(saveCarry) + addInstr(result, IRInstruction(Opcode.POPST), null) return ExpressionCodeResult(result, vmDt, -1, -1) } diff --git a/compiler/test/arithmetic/bitshift2.p8 b/compiler/test/arithmetic/bitshift2.p8 new file mode 100644 index 000000000..95bbd844a --- /dev/null +++ b/compiler/test/arithmetic/bitshift2.p8 @@ -0,0 +1,865 @@ +%import textio +%zeropage basicsafe +%option no_sysinit + +main { + sub start() { + unsigned() + signed() + } + + ubyte[2] ubarray + uword[2] uwarray + byte[2] barray + word[2] warray + + sub value_and_carry(ubyte value) -> ubyte { + sys.set_carry() + return value + } + + sub unsigned() { + txt.print("rol_ub\n") + test_rol_ub(%00000000, false, %00000000, false) + test_rol_ub(%00000000, true, %00000001, false) + test_rol_ub(%01000000, false, %10000000, false) + test_rol_ub(%01000000, true, %10000001, false) + test_rol_ub(%10000000, false, %00000000, true) + test_rol_ub(%10000000, true, %00000001, true) + + txt.print("ror_ub\n") + test_ror_ub(%00000000, false, %00000000, false) + test_ror_ub(%00000000, true, %10000000, false) + test_ror_ub(%01000000, false, %00100000, false) + test_ror_ub(%01000000, true, %10100000, false) + test_ror_ub(%00000001, false, %00000000, true) + test_ror_ub(%00000001, true, %10000000, true) + + txt.print("rol2_ub\n") + test_rol2_ub(%00000000, %00000000) + test_rol2_ub(%01000001, %10000010) + test_rol2_ub(%10000010, %00000101) + test_rol2_ub(%11111110, %11111101) + + txt.print("ror2_ub\n") + test_ror2_ub(%00000000, %00000000) + test_ror2_ub(%01000001, %10100000) + test_ror2_ub(%10000010, %01000001) + test_ror2_ub(%11111110, %01111111) + + txt.print("rol_uw\n") + test_rol_uw(%0000000010000000, false, %0000000100000000, false) + test_rol_uw(%0000000010000000, true, %0000000100000001, false) + test_rol_uw(%0100000010000000, false, %1000000100000000, false) + test_rol_uw(%0100000010000000, true, %1000000100000001, false) + test_rol_uw(%1000000010000000, false, %0000000100000000, true) + test_rol_uw(%1000000010000000, true, %0000000100000001, true) + + txt.print("ror_uw\n") + test_ror_uw(%0000000100000000, false, %0000000010000000, false) + test_ror_uw(%0000000100000000, true, %1000000010000000, false) + test_ror_uw(%0100000100000000, false, %0010000010000000, false) + test_ror_uw(%0100000100000000, true, %1010000010000000, false) + test_ror_uw(%0000000100000001, false, %0000000010000000, true) + test_ror_uw(%0000000100000001, true, %1000000010000000, true) + + txt.print("rol2_uw\n") + test_rol2_uw(%0000000010000000, %0000000100000000) + test_rol2_uw(%0100000110000000, %1000001100000000) + test_rol2_uw(%1000001010000000, %0000010100000001) + test_rol2_uw(%1111111010000000, %1111110100000001) + + txt.print("ror2_uw\n") + test_ror2_uw(%0000000100000000, %0000000010000000) + test_ror2_uw(%0100000100000000, %0010000010000000) + test_ror2_uw(%1000001100000001, %1100000110000000) + test_ror2_uw(%1111111100000011, %1111111110000001) + + txt.print("<< ub\n") + test_shiftl_ub(%00000000, %00000000, false) + test_shiftl_ub(%00000001, %00000010, false) + test_shiftl_ub(%01000000, %10000000, false) + test_shiftl_ub(%10000000, %00000000, true) + + txt.print(">> ub\n") + test_shiftr_ub(%00000000, %00000000, false) + test_shiftr_ub(%00000001, %00000000, true) + test_shiftr_ub(%10000000, %01000000, false) + test_shiftr_ub(%10000001, %01000000, true) + + txt.print("<< uw\n") + test_shiftl_uw(%0000000000000000, %0000000000000000, false) + test_shiftl_uw(%0000000000000001, %0000000000000010, false) + test_shiftl_uw(%0000000010000001, %0000000100000010, false) + test_shiftl_uw(%0100000010000000, %1000000100000000, false) + test_shiftl_uw(%1100000010000000, %1000000100000000, true) + test_shiftl_uw(%1000000000000000, %0000000000000000, true) + + txt.print(">> uw\n") + test_shiftr_uw(%0000000000000000, %0000000000000000, false) + test_shiftr_uw(%0000000000000001, %0000000000000000, true) + test_shiftr_uw(%0000001100000010, %0000000110000001, false) + test_shiftr_uw(%0000001100000011, %0000000110000001, true) + test_shiftr_uw(%1000000000000010, %0100000000000001, false) + } + + sub signed() { + txt.print("<< b\n") + test_shiftl_b(%00000000 as byte, %00000000 as byte, false) + test_shiftl_b(%00000001 as byte, %00000010 as byte, false) + test_shiftl_b(%01000000 as byte, %10000000 as byte, false) + test_shiftl_b(%10000000 as byte, %00000000 as byte, true) + + txt.print(">> b\n") + test_shiftr_b(%00000000 as byte, %00000000 as byte, false) + test_shiftr_b(%00000001 as byte, %00000000 as byte, true) + test_shiftr_b(%10000000 as byte, %11000000 as byte, false) + test_shiftr_b(%10000010 as byte, %11000001 as byte, false) + test_shiftr_b(%10000001 as byte, %11000000 as byte, true) + + txt.print("<< w\n") + test_shiftl_w(%0000000000000000 as word, %0000000000000000 as word, false) + test_shiftl_w(%0000000000000001 as word, %0000000000000010 as word, false) + test_shiftl_w(%0000000010000001 as word, %0000000100000010 as word, false) + test_shiftl_w(%0100000010000000 as word, %1000000100000000 as word, false) + test_shiftl_w(%1100000010000000 as word, %1000000100000000 as word, true) + test_shiftl_w(%1000000000000000 as word, %0000000000000000 as word, true) + + txt.print(">> w\n") + test_shiftr_w(%0000000000000000 as word, %0000000000000000 as word, false) + test_shiftr_w(%0000000000000001 as word, %0000000000000000 as word, true) + test_shiftr_w(%0000001100000010 as word, %0000000110000001 as word, false) + test_shiftr_w(%0000001100000011 as word, %0000000110000001 as word, true) + test_shiftr_w(%1000000000000010 as word, %1100000000000001 as word, false) + test_shiftr_w(%1000000000000001 as word, %1100000000000000 as word, true) + } + + + sub test_rol2_ub(ubyte value, ubyte test) { + ubyte original = value + sys.set_carry() + rol2(value) + if value!=test { + txt.print("rol2_ub error ") + txt.print_ub(original) + txt.spc() + txt.print_ub(value) + txt.print(" exp: ") + txt.print_ub(test) + txt.nl() + } + ubarray[1]=original + sys.set_carry() + rol2(ubarray[1]) + if ubarray[1]!=test { + txt.print("rol2_ub array error ") + txt.print_ub(original) + txt.spc() + txt.print_ub(ubarray[1]) + txt.print(" exp: ") + txt.print_ub(test) + txt.nl() + } + @($8000)=original + sys.set_carry() + rol2(@($8000)) + if @($8000)!=test { + txt.print("rol2_ub mem error ") + txt.print_ub(original) + txt.spc() + txt.print_ub(@($8000)) + txt.print(" exp: ") + txt.print_ub(test) + txt.nl() + } + } + + sub test_ror2_ub(ubyte value, ubyte test) { + ubyte original = value + sys.set_carry() + ror2(value) + if value!=test { + txt.print("ror2_ub error ") + txt.print_ub(original) + txt.spc() + txt.print_ub(value) + txt.print(" exp: ") + txt.print_ub(test) + txt.nl() + } + ubarray[1] = original + sys.set_carry() + ror2(ubarray[1]) + if ubarray[1]!=test { + txt.print("ror2_ub array error ") + txt.print_ub(original) + txt.spc() + txt.print_ub(ubarray[1]) + txt.print(" exp: ") + txt.print_ub(test) + txt.nl() + } + @($8000) = original + sys.set_carry() + ror2(@($8000)) + if @($8000)!=test { + txt.print("ror2_ub mem error ") + txt.print_ub(original) + txt.spc() + txt.print_ub(@($8000)) + txt.print(" exp: ") + txt.print_ub(test) + txt.nl() + } + } + + sub test_rol_ub(ubyte value, bool carry, ubyte test, bool newcarry) { + bool carrycheck = false + ubyte original = value + if carry + sys.set_carry() + else + sys.clear_carry() + rol(value) + if_cs + carrycheck=true + if value!=test or carrycheck!=newcarry{ + txt.print("rol_ub error ") + txt.print_ub(original) + txt.spc() + txt.print_ub(carry) + txt.spc() + txt.print_ub(value) + txt.spc() + txt.print_ub(carrycheck) + txt.print(" exp: ") + txt.print_ub(test) + txt.spc() + txt.print_ub(newcarry) + txt.nl() + } + + ubarray[1] = original + carrycheck = false + if carry + sys.set_carry() + else + sys.clear_carry() + rol(ubarray[value_and_carry(1)]) + if_cs + carrycheck=true + if ubarray[1]!=test or carrycheck!=newcarry{ + txt.print("rol_ub array error ") + txt.print_ub(original) + txt.spc() + txt.print_ub(carry) + txt.spc() + txt.print_ub(ubarray[1]) + txt.spc() + txt.print_ub(carrycheck) + txt.print(" exp: ") + txt.print_ub(test) + txt.spc() + txt.print_ub(newcarry) + txt.nl() + } + + @($8001)=original + carrycheck = false + if carry + sys.set_carry() + else + sys.clear_carry() + rol(@($8000+value_and_carry(1))) + if_cs + carrycheck=true + if @($8001)!=test or carrycheck!=newcarry { + txt.print("rol_ub mem error ") + txt.print_ub(original) + txt.spc() + txt.print_ub(carry) + txt.spc() + txt.print_ub(@($8001)) + txt.spc() + txt.print_ub(carrycheck) + txt.print(" exp: ") + txt.print_ub(test) + txt.spc() + txt.print_ub(newcarry) + txt.nl() + } + } + + sub test_ror_ub(ubyte value, bool carry, ubyte test, bool newcarry) { + bool carrycheck = false + ubyte original = value + if carry + sys.set_carry() + else + sys.clear_carry() + ror(value) + if_cs + carrycheck=true + if value!=test or carrycheck!=newcarry{ + txt.print("ror_ub error ") + txt.print_ub(original) + txt.spc() + txt.print_ub(carry) + txt.spc() + txt.print_ub(value) + txt.spc() + txt.print_ub(carrycheck) + txt.print(" exp: ") + txt.print_ub(test) + txt.spc() + txt.print_ub(newcarry) + txt.nl() + } + + ubarray[1] = original + carrycheck = false + if carry + sys.set_carry() + else + sys.clear_carry() + ror(ubarray[value_and_carry(1)]) + if_cs + carrycheck=true + if ubarray[1]!=test or carrycheck!=newcarry { + txt.print("ror_ub array error ") + txt.print_ub(original) + txt.spc() + txt.print_ub(carry) + txt.spc() + txt.print_ub(ubarray[1]) + txt.spc() + txt.print_ub(carrycheck) + txt.print(" exp: ") + txt.print_ub(test) + txt.spc() + txt.print_ub(newcarry) + txt.nl() + } + + @($8001) = original + carrycheck = false + if carry + sys.set_carry() + else + sys.clear_carry() + ror(@($8000+value_and_carry(1))) + if_cs + carrycheck=true + if @($8001)!=test or carrycheck!=newcarry { + txt.print("ror_ub mem error ") + txt.print_ub(original) + txt.spc() + txt.print_ub(carry) + txt.spc() + txt.print_ub(@($8001)) + txt.spc() + txt.print_ub(carrycheck) + txt.print(" exp: ") + txt.print_ub(test) + txt.spc() + txt.print_ub(newcarry) + txt.nl() + } + } + + sub test_rol2_uw(uword value, uword test) { + uword original = value + sys.set_carry() + rol2(value) + if value!=test { + txt.print("rol2_uw error ") + txt.print_uw(original) + txt.spc() + txt.print_uw(value) + txt.print(" exp: ") + txt.print_uw(test) + txt.nl() + } + + uwarray[1] = original + sys.set_carry() + rol2(uwarray[1]) + if uwarray[1]!=test { + txt.print("rol2_uw array error ") + txt.print_uw(original) + txt.spc() + txt.print_uw(uwarray[1]) + txt.print(" exp: ") + txt.print_uw(test) + txt.nl() + } + } + + sub test_ror2_uw(uword value, uword test) { + uword original = value + sys.set_carry() + ror2(value) + if value!=test { + txt.print("ror2_uw error ") + txt.print_uw(original) + txt.spc() + txt.print_uw(value) + txt.print(" exp: ") + txt.print_uw(test) + txt.nl() + } + + uwarray[1] = original + sys.set_carry() + ror2(uwarray[1]) + if uwarray[1]!=test { + txt.print("ror2_uw array error ") + txt.print_uw(original) + txt.spc() + txt.print_uw(uwarray[1]) + txt.print(" exp: ") + txt.print_uw(test) + txt.nl() + } + } + + sub test_rol_uw(uword value, bool carry, uword test, bool newcarry) { + bool carrycheck = false + uword original = value + if carry + sys.set_carry() + else + sys.clear_carry() + rol(value) + if_cs + carrycheck=true + if value!=test or carrycheck!=newcarry{ + txt.print("rol_uw error ") + txt.print_uw(original) + txt.spc() + txt.print_uw(carry) + txt.spc() + txt.print_uw(value) + txt.spc() + txt.print_uw(carrycheck) + txt.print(" exp: ") + txt.print_uw(test) + txt.spc() + txt.print_uw(carrycheck) + txt.nl() + } + + uwarray[1] = original + carrycheck = false + if carry + sys.set_carry() + else + sys.clear_carry() + rol(uwarray[value_and_carry(1)]) + if_cs + carrycheck=true + if uwarray[1]!=test or carrycheck!=newcarry{ + txt.print("rol_uw array error ") + txt.print_uw(original) + txt.spc() + txt.print_uw(carry) + txt.spc() + txt.print_uw(uwarray[1]) + txt.spc() + txt.print_uw(carrycheck) + txt.print(" exp: ") + txt.print_uw(test) + txt.spc() + txt.print_uw(carrycheck) + txt.nl() + } + } + + sub test_ror_uw(uword value, bool carry, uword test, bool newcarry) { + bool carrycheck = false + uword original = value + if carry + sys.set_carry() + else + sys.clear_carry() + ror(value) + if_cs + carrycheck=true + if value!=test or carrycheck!=newcarry{ + txt.print("ror_uw error ") + txt.print_uw(original) + txt.spc() + txt.print_uw(carry) + txt.spc() + txt.print_uw(value) + txt.spc() + txt.print_uw(carrycheck) + txt.print(" exp: ") + txt.print_uw(test) + txt.spc() + txt.print_uw(carrycheck) + txt.nl() + } + + uwarray[1] = original + carrycheck = false + if carry + sys.set_carry() + else + sys.clear_carry() + ror(uwarray[value_and_carry(1)]) + if_cs + carrycheck=true + if uwarray[1]!=test or carrycheck!=newcarry{ + txt.print("ror_uw array error ") + txt.print_uw(original) + txt.spc() + txt.print_uw(carry) + txt.spc() + txt.print_uw(uwarray[1]) + txt.spc() + txt.print_uw(carrycheck) + txt.print(" exp: ") + txt.print_uw(test) + txt.spc() + txt.print_uw(carrycheck) + txt.nl() + } + } + + sub test_shiftl_ub(ubyte value, ubyte test, bool newcarry) { + bool carrycheck = false + ubyte original = value + sys.set_carry() + value <<= 1 + if_cs + carrycheck=true + if value!=test or carrycheck!=newcarry { + txt.print("<< ub error ") + txt.print_ub(original) + txt.spc() + txt.print_ub(value) + txt.spc() + txt.print_ub(carrycheck) + txt.print(" exp: ") + txt.print_ub(test) + txt.spc() + txt.print_ub(newcarry) + txt.nl() + } + + ubarray[1] = original + sys.set_carry() + carrycheck = false + ubarray[1] <<= 1 + if_cs + carrycheck=true + if ubarray[1]!=test or carrycheck!=newcarry { + txt.print("<< ub array error ") + txt.print_ub(original) + txt.spc() + txt.print_ub(ubarray[1]) + txt.spc() + txt.print_ub(carrycheck) + txt.print(" exp: ") + txt.print_ub(test) + txt.spc() + txt.print_ub(newcarry) + txt.nl() + } + } + + sub test_shiftr_ub(ubyte value, ubyte test, bool newcarry) { + bool carrycheck = false + ubyte original = value + sys.set_carry() + value >>= 1 + if_cs + carrycheck=true + if value!=test or carrycheck!=newcarry { + txt.print(">> ub error ") + txt.print_ub(original) + txt.spc() + txt.print_ub(value) + txt.spc() + txt.print_ub(carrycheck) + txt.print(" exp: ") + txt.print_ub(test) + txt.spc() + txt.print_ub(newcarry) + txt.nl() + } + + ubarray[1] = original + sys.set_carry() + carrycheck = false + ubarray[1] >>= 1 + if_cs + carrycheck=true + if ubarray[1]!=test or carrycheck!=newcarry { + txt.print(">> ub array error ") + txt.print_ub(original) + txt.spc() + txt.print_ub(ubarray[1]) + txt.spc() + txt.print_ub(carrycheck) + txt.print(" exp: ") + txt.print_ub(test) + txt.spc() + txt.print_ub(newcarry) + txt.nl() + } + } + + sub test_shiftl_uw(uword value, uword test, bool newcarry) { + bool carrycheck = false + uword original = value + sys.set_carry() + value <<= 1 + if_cs + carrycheck=true + if value!=test or carrycheck!=newcarry { + txt.print("<< uw error ") + txt.print_uw(original) + txt.spc() + txt.print_uw(value) + txt.spc() + txt.print_uw(carrycheck) + txt.print(" exp: ") + txt.print_uw(test) + txt.spc() + txt.print_uw(carrycheck) + txt.nl() + } + + uwarray[1] = original + sys.set_carry() + carrycheck = false + uwarray[1] <<= 1 + if_cs + carrycheck=true + if uwarray[1]!=test or carrycheck!=newcarry { + txt.print("<< uw array error ") + txt.print_uw(original) + txt.spc() + txt.print_uw(uwarray[1]) + txt.spc() + txt.print_uw(carrycheck) + txt.print(" exp: ") + txt.print_uw(test) + txt.spc() + txt.print_uw(carrycheck) + txt.nl() + } + } + + sub test_shiftr_uw(uword value, uword test, bool newcarry) { + bool carrycheck = false + uword original = value + sys.set_carry() + value >>= 1 + if_cs + carrycheck=true + if value!=test or carrycheck!=newcarry { + txt.print(">> uw error ") + txt.print_uw(original) + txt.spc() + txt.print_uw(value) + txt.spc() + txt.print_uw(carrycheck) + txt.print(" exp: ") + txt.print_uw(test) + txt.spc() + txt.print_uw(carrycheck) + txt.nl() + } + + uwarray[1] = original + sys.set_carry() + carrycheck = false + uwarray[1] >>= 1 + if_cs + carrycheck=true + if uwarray[1]!=test or carrycheck!=newcarry { + txt.print(">> uw array error ") + txt.print_uw(original) + txt.spc() + txt.print_uw(uwarray[1]) + txt.spc() + txt.print_uw(carrycheck) + txt.print(" exp: ") + txt.print_uw(test) + txt.spc() + txt.print_uw(carrycheck) + txt.nl() + } + } + + sub test_shiftl_b(byte value, byte test, bool newcarry) { + bool carrycheck = false + byte original = value + sys.set_carry() + value <<= 1 + if_cs + carrycheck=true + if value!=test or carrycheck!=newcarry { + txt.print("<< b error ") + txt.print_b(original) + txt.spc() + txt.print_b(value) + txt.spc() + txt.print_b(carrycheck) + txt.print(" exp: ") + txt.print_b(test) + txt.spc() + txt.print_b(carrycheck) + txt.nl() + } + + barray[1] = original + sys.set_carry() + carrycheck = false + barray[1] <<= 1 + if_cs + carrycheck=true + if barray[1]!=test or carrycheck!=newcarry { + txt.print("<< b array error ") + txt.print_b(original) + txt.spc() + txt.print_b(barray[1]) + txt.spc() + txt.print_b(carrycheck) + txt.print(" exp: ") + txt.print_b(test) + txt.spc() + txt.print_b(carrycheck) + txt.nl() + } + } + + sub test_shiftr_b(byte value, byte test, bool newcarry) { + bool carrycheck = false + byte original = value + sys.set_carry() + value >>= 1 + if_cs + carrycheck=true + if value!=test or carrycheck!=newcarry { + txt.print(">> b error ") + txt.print_b(original) + txt.spc() + txt.print_b(value) + txt.spc() + txt.print_b(carrycheck) + txt.print(" exp: ") + txt.print_b(test) + txt.spc() + txt.print_b(carrycheck) + txt.nl() + } + + barray[1] = original + sys.set_carry() + carrycheck = false + barray[1] >>= 1 + if_cs + carrycheck=true + if barray[1]!=test or carrycheck!=newcarry { + txt.print(">> b array error ") + txt.print_b(original) + txt.spc() + txt.print_b(barray[1]) + txt.spc() + txt.print_b(carrycheck) + txt.print(" exp: ") + txt.print_b(test) + txt.spc() + txt.print_b(carrycheck) + txt.nl() + } + } + + sub test_shiftl_w(word value, word test, bool newcarry) { + bool carrycheck = false + word original = value + sys.set_carry() + value <<= 1 + if_cs + carrycheck=true + if value!=test or carrycheck!=newcarry { + txt.print("<< w error ") + txt.print_w(original) + txt.spc() + txt.print_w(value) + txt.spc() + txt.print_w(carrycheck) + txt.print(" exp: ") + txt.print_w(test) + txt.spc() + txt.print_w(carrycheck) + txt.nl() + } + + warray[1] = original + sys.set_carry() + carrycheck = false + warray[1] <<= 1 + if_cs + carrycheck=true + if warray[1]!=test or carrycheck!=newcarry { + txt.print("<< w array error ") + txt.print_w(original) + txt.spc() + txt.print_w(warray[1]) + txt.spc() + txt.print_w(carrycheck) + txt.print(" exp: ") + txt.print_w(test) + txt.spc() + txt.print_w(carrycheck) + txt.nl() + } + } + + sub test_shiftr_w(word value, word test, bool newcarry) { + bool carrycheck = false + word original = value + sys.set_carry() + value >>= 1 + if_cs + carrycheck=true + if value!=test or carrycheck!=newcarry { + txt.print(">> w error ") + txt.print_w(original) + txt.spc() + txt.print_w(value) + txt.spc() + txt.print_w(carrycheck) + txt.print(" exp: ") + txt.print_w(test) + txt.spc() + txt.print_w(carrycheck) + txt.nl() + } + + warray[1] = original + sys.set_carry() + carrycheck = false + warray[1] >>= 1 + if_cs + carrycheck=true + if warray[1]!=test or carrycheck!=newcarry { + txt.print(">> w array error ") + txt.print_w(original) + txt.spc() + txt.print_w(warray[1]) + txt.spc() + txt.print_w(carrycheck) + txt.print(" exp: ") + txt.print_w(test) + txt.spc() + txt.print_w(carrycheck) + txt.nl() + } + } +} diff --git a/compiler/test/ast/TestConst.kt b/compiler/test/ast/TestConst.kt index aa5a06e83..006cc9475 100644 --- a/compiler/test/ast/TestConst.kt +++ b/compiler/test/ast/TestConst.kt @@ -294,4 +294,22 @@ main { ((st[4] as Assignment).value as NumericLiteral).number shouldBe 0x9e00+2*30 ((st[5] as Assignment).value as NumericLiteral).number shouldBe 0x9e00+2*30 } + + test("address of a const uword pointer array expression") { + val src=""" +main { + sub start() { + const uword buffer = ${'$'}2000 + uword addr = &buffer[2] + + const ubyte width = 100 + ubyte @shared i + ubyte @shared j + uword addr2 = &buffer[i * width + j] + } +}""" + val result = compileText(Cx16Target(), true, src, writeAssembly = false)!! + val st = result.compilerAst.entrypoint.statements + st.size shouldBe 9999 + } }) diff --git a/compiler/test/codegeneration/TestArrayThings.kt b/compiler/test/codegeneration/TestArrayThings.kt index 4f1204bb7..9081e5b4c 100644 --- a/compiler/test/codegeneration/TestArrayThings.kt +++ b/compiler/test/codegeneration/TestArrayThings.kt @@ -203,5 +203,19 @@ main { }""" compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null } + + test("address of a uword pointer array expression") { + val src=""" +main { + sub start() { + set_state(12345, 1) + } + sub set_state(uword buffer, ubyte i) { + uword addr = &buffer[i] + addr++ + } +}""" + compileText(C64Target(), false, src, writeAssembly = true) shouldNotBe null + } }) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 7f8e58873..8237d23e6 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,9 +1,10 @@ TODO ==== -funcRor()/funcRol(): save carry flag before calculating array index otherwise it gets clobbered +2 unit tests that are failing. +Mark had a compiler crash FatalAstException: invalid dt. -Mark had a compiler crash FatalAstException: invalid dt +IR: optimize funcRolRor() ... diff --git a/examples/test.p8 b/examples/test.p8 index 1b728782b..4e5252bf2 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,93 +4,5 @@ main { sub start() { - word @shared q = -12345 - txt.print_w(q) - txt.nl() - txt.print_uwbin(q as uword, true) - txt.nl() - q >>=9 - txt.print_w(q) - txt.nl() - txt.print_uwbin(q as uword, true) - txt.nl() - -; mem() -; bytes() -; words() - } - - sub mem() { - @($2000) = $7a - rol(@($2000)) - txt.print_ubbin(@($2000), true) - txt.nl() - rol2(@($2000)) - txt.print_ubbin(@($2000), true) - txt.nl() - ror(@($2000)) - txt.print_ubbin(@($2000), true) - txt.nl() - ror2(@($2000)) - txt.print_ubbin(@($2000), true) - txt.nl() - txt.nl() - } - - sub bytes() { - ubyte[] wa = [$1a, $2b, $3c] - - txt.print_ubbin(wa[2], true) - txt.nl() - rol(wa[2]) - txt.print_ubbin(wa[2], true) - txt.nl() - rol2(wa[2]) - txt.print_ubbin(wa[2], true) - txt.nl() - ror(wa[2]) - txt.print_ubbin(wa[2], true) - txt.nl() - ror2(wa[2]) - txt.print_ubbin(wa[2], true) - txt.nl() - txt.nl() - } - - sub words() { - uword[] wa = [$11aa, $22bb, $33cc] - uword[] @split swa = [$11aa, $22bb, $33cc] - - txt.print_uwbin(wa[2], true) - txt.nl() - rol(wa[2]) - txt.print_uwbin(wa[2], true) - txt.nl() - rol2(wa[2]) - txt.print_uwbin(wa[2], true) - txt.nl() - ror(wa[2]) - txt.print_uwbin(wa[2], true) - txt.nl() - ror2(wa[2]) - txt.print_uwbin(wa[2], true) - txt.nl() - txt.nl() - - txt.print_uwbin(swa[2], true) - txt.nl() - rol(swa[2]) - txt.print_uwbin(swa[2], true) - txt.nl() - rol2(swa[2]) - txt.print_uwbin(swa[2], true) - txt.nl() - ror(swa[2]) - txt.print_uwbin(swa[2], true) - txt.nl() - ror2(swa[2]) - txt.print_uwbin(swa[2], true) - txt.nl() - txt.nl() } } diff --git a/gradle.properties b/gradle.properties index 1c23b3de8..d8e1ba651 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ org.gradle.daemon=true kotlin.code.style=official javaVersion=11 kotlinVersion=1.9.22 -version=9.8-SNAPSHOT +version=10.0-SNAPSHOT diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index 709a13c88..2665af183 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -235,6 +235,8 @@ msig [b, w] reg1, reg2 - reg1 becomes the most significant by concat [b, w] reg1, reg2, reg3 - reg1.w = 'concatenate' two registers: lsb/lsw of reg2 (as msb) and lsb/lsw of reg3 (as lsb) into word or int (int not yet implemented; requires 32bits regs) push [b, w, f] reg1 - push value in reg1 on the stack pop [b, w, f] reg1 - pop value from stack into reg1 +pushst - push status register bits to stack +popst - pop status register bits from stack */ enum class Opcode { @@ -387,6 +389,8 @@ enum class Opcode { SEC, PUSH, POP, + PUSHST, + POPST, MSIG, CONCAT, BREAKPOINT @@ -699,6 +703,8 @@ val instructionFormats = mutableMapOf( Opcode.MSIG to InstructionFormat.from("BW,>r1,r1 | F,>fr1"), + Opcode.PUSHST to InstructionFormat.from("N"), + Opcode.POPST to InstructionFormat.from("N"), Opcode.CONCAT to InstructionFormat.from("BW,<>r1, InsCONCAT(ins) Opcode.PUSH -> InsPUSH(ins) Opcode.POP -> InsPOP(ins) + Opcode.PUSHST -> InsPUSHST() + Opcode.POPST -> InsPOPST() Opcode.BREAKPOINT -> InsBREAKPOINT() Opcode.CLC -> { statusCarry = false; nextPc() } Opcode.SEC -> { statusCarry = true; nextPc() } @@ -360,6 +362,28 @@ class VirtualMachine(irProgram: IRProgram) { nextPc() } + private fun InsPUSHST() { + var status: UByte = 0u + if(statusNegative) + status = status or 0b10000000u + if(statusZero) + status = status or 0b00000010u + if(statusCarry) + status = status or 0b00000001u + // TODO overflow not yet supported + valueStack.push(status) + nextPc() + } + + private fun InsPOPST() { + val status = valueStack.pop().toInt() + statusNegative = status and 0b10000000 != 0 + statusZero = status and 0b00000010 != 0 + statusCarry = status and 0b00000001 != 0 + // TODO overflow not yet supported + nextPc() + } + private fun InsSYSCALL(i: IRInstruction) { // put the syscall's arguments that were prepared onto the stack for(value in syscallParams) {