diff --git a/codeGenIntermediate/codeGenIntermediate.iml b/codeGenIntermediate/codeGenIntermediate.iml index 893f7df14..9626d27b3 100644 --- a/codeGenIntermediate/codeGenIntermediate.iml +++ b/codeGenIntermediate/codeGenIntermediate.iml @@ -14,5 +14,6 @@ + \ No newline at end of file diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt index f2e9905f6..1f0728680 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt @@ -1,5 +1,8 @@ package prog8.codegen.intermediate +import com.github.michaelbull.result.Ok +import com.github.michaelbull.result.Result +import com.github.michaelbull.result.getOrElse import prog8.code.ast.* import prog8.code.core.AssemblyError import prog8.code.core.DataType @@ -7,6 +10,7 @@ import prog8.code.core.PrefixOperators import prog8.code.core.SignedDatatypes import prog8.intermediate.* + internal class AssignmentGen(private val codeGen: IRCodeGen, private val expressionEval: ExpressionGen) { internal fun translate(assignment: PtAssignment): IRCodeChunks { @@ -34,10 +38,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express else fallbackAssign(augAssign) } else if(array!=null) { - // NOTE: naive fallback assignment here will sometimes generate code that loads the index value multiple times - // in a register. It's way too much work to optimize that here - instead, we trust that the generated IL assembly - // will be optimized later and have the double assignments removed. - fallbackAssign(augAssign) + assignArrayAugmented(array, augAssign) } else { fallbackAssign(augAssign) } @@ -45,10 +46,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express return chunks } - private fun assignMemoryAugmented( - address: Int, - assignment: PtAugmentedAssign - ): IRCodeChunks { + private fun assignMemoryAugmented(address: Int, assignment: PtAugmentedAssign): IRCodeChunks { val value = assignment.value val targetDt = irType(assignment.target.type) val signed = assignment.target.type in SignedDatatypes @@ -63,7 +61,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express "<<=" -> 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.operatorEqualsNotEqualsInplace(address, null, targetDt, value) "!=" -> expressionEval.operatorNotEqualsInplace(address, null, targetDt, value) "<" -> expressionEval.operatorLessInplace(address, null, targetDt, signed, value) ">" -> expressionEval.operatorGreaterInplace(address, null, targetDt, signed, value) @@ -92,17 +90,49 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express "<<=" -> expressionEval.operatorShiftLeftInplace(null, symbol, targetDt, value) ">>=" -> expressionEval.operatorShiftRightInplace(null, symbol, targetDt, signed, value) "%=" -> expressionEval.operatorModuloInplace(null, symbol, targetDt, value) - "==" -> expressionEval.operatorEqualsInplace(null, symbol, targetDt, value) + "==" -> expressionEval.operatorEqualsNotEqualsInplace(null, symbol, targetDt, value) "!=" -> expressionEval.operatorNotEqualsInplace(null, symbol, targetDt, 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}") } } + private fun assignArrayAugmented(array: PtArrayIndexer, assignment: PtAugmentedAssign): IRCodeChunks { + val eltSize = codeGen.program.memsizer.memorySize(array.type) + val value = assignment.value + val signed = assignment.target.type in SignedDatatypes + val result = when(assignment.operator) { + // TODO implement all of these: +// "+=" -> expressionEval.operatorPlusInplace(array, eltSize, value) +// "-=" -> expressionEval.operatorMinusInplace(array, eltSize, value) +// "*=" -> expressionEval.operatorMultiplyInplace(array, eltSize, value) +// "/=" -> expressionEval.operatorDivideInplace(array, eltSize, signed, value) +// "|=" -> expressionEval.operatorOrInplace(array, eltSize, value) +// "&=" -> expressionEval.operatorAndInplace(array, eltSize, value) +// "^=" -> expressionEval.operatorXorInplace(array, eltSize, value) +// "<<=" -> expressionEval.operatorShiftLeftInplace(array, eltSize, value) +// ">>=" -> expressionEval.operatorShiftRightInplace(array, eltSize, signed, value) +// "%=" -> expressionEval.operatorModuloInplace(array, eltSize, value) + "==" -> expressionEval.operatorEqualsNotEqualsInplace(array, eltSize, value, true) + "!=" -> expressionEval.operatorEqualsNotEqualsInplace(array, eltSize, value, false) +// "<" -> expressionEval.operatorLessInplace(array, eltSize, signed, value) // TODO reuse code from == +// ">" -> expressionEval.operatorGreaterInplace(array, eltSize, signed, value) // TODO reuse code from == +// "<=" -> expressionEval.operatorLessEqualInplace(array, eltSize, signed, value) // TODO reuse code from == +// ">=" -> expressionEval.operatorGreaterEqualInplace(array, eltSize, signed, value) // TODO reuse code from == + in PrefixOperators -> inplacePrefix(assignment.operator, array, eltSize) + + // else -> Err(NotImplementedError("invalid augmented assign operator ${assignment.operator}")) + else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}") + } + + return result.getOrElse { fallbackAssign(assignment) } + } + private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks { val value: PtExpression if(origAssign.operator in PrefixOperators) { @@ -122,12 +152,79 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express return translateRegularAssign(normalAssign) } + private fun inplacePrefix(operator: String, array: PtArrayIndexer, eltSize: Int): Result { + if(array.splitWords) + TODO("inplace prefix for split word array") + + val result = mutableListOf() + val vmDt = irType(array.type) + val constIndex = array.index.asConstInteger() + when(operator) { + "+" -> { } + "-" -> { + if(constIndex!=null) { + addInstr(result, IRInstruction(Opcode.NEGM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null) + } else { + val register = codeGen.registers.nextFree() + val tr = expressionEval.translateExpression(array.index) + addToResult(result, tr, tr.resultReg, -1) + if(eltSize>1) + result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize) + result += IRCodeChunk(null, null).also { + it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name) + it += IRInstruction(Opcode.NEG, vmDt, reg1 = register) + it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name) + } + } + } + "~" -> { + if(constIndex!=null) { + addInstr(result, IRInstruction(Opcode.INVM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null) + } else { + val register = codeGen.registers.nextFree() + val tr = expressionEval.translateExpression(array.index) + addToResult(result, tr, tr.resultReg, -1) + if(eltSize>1) + result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize) + result += IRCodeChunk(null, null).also { + it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name) + it += IRInstruction(Opcode.INV, vmDt, reg1 = register) + it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name) + } + } + } + "not" -> { + val register = codeGen.registers.nextFree() + if(constIndex!=null) { + // TODO: in boolean branch, is 'not' handled ok like this? + result += IRCodeChunk(null, null).also { + it += IRInstruction(Opcode.LOAD, vmDt, reg1=register, immediate = 1) + it += IRInstruction(Opcode.XORM, vmDt, reg1=register, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) + } + } else { + val tr = expressionEval.translateExpression(array.index) + addToResult(result, tr, tr.resultReg, -1) + if(eltSize>1) + result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize) + result += IRCodeChunk(null, null).also { + it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name) + it += IRInstruction(Opcode.XOR, vmDt, reg1 = register, immediate = 1) + it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = tr.resultReg, labelSymbol = array.variable.name) + } + } + } + else -> throw AssemblyError("weird prefix operator") + } + return Ok(result) + } + private fun inplacePrefix(operator: String, vmDt: IRDataType, address: Int?, symbol: String?): IRCodeChunks { val code= IRCodeChunk(null, null) when(operator) { "+" -> { } "-" -> code += IRInstruction(Opcode.NEGM, vmDt, address = address, labelSymbol = symbol) "~" -> code += IRInstruction(Opcode.INVM, vmDt, address = address, labelSymbol = symbol) + // TODO: in boolean branch, how is 'not' handled here? else -> throw AssemblyError("weird prefix operator") } return listOf(code) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index ad0e30be5..0e97b6ccb 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -1,11 +1,15 @@ package prog8.codegen.intermediate +import com.github.michaelbull.result.Err +import com.github.michaelbull.result.Ok +import com.github.michaelbull.result.Result import prog8.code.StRomSub import prog8.code.StSub import prog8.code.ast.* import prog8.code.core.* import prog8.intermediate.* + internal class ExpressionCodeResult(val chunks: IRCodeChunks, val dt: IRDataType, val resultReg: Int, val resultFpReg: Int) { constructor(chunks: IRCodeChunk, dt: IRDataType, resultReg: Int, resultFpReg: Int) : this(listOf(chunks), dt, resultReg, resultFpReg) @@ -193,15 +197,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { if(arrayIx.splitWords) { require(vmDt==IRDataType.WORD) - val arrayLength = codeGen.symbolTable.getLength(arrayIx.variable.name) resultRegister = codeGen.registers.nextFree() val finalResultReg = codeGen.registers.nextFree() if(arrayIx.index is PtNumber) { val memOffset = (arrayIx.index as PtNumber).number.toInt() result += IRCodeChunk(null, null).also { val tmpRegMsb = codeGen.registers.nextFree() - it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=tmpRegMsb, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_msb", symbolOffset = memOffset) - it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_lsb", symbolOffset = memOffset) + it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=tmpRegMsb, labelSymbol= "${arrayVarSymbol}_msb", symbolOffset = memOffset) + it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, labelSymbol= "${arrayVarSymbol}_lsb", symbolOffset = memOffset) it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=finalResultReg, reg2=tmpRegMsb, reg3=resultRegister) } } else { @@ -209,8 +212,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { addToResult(result, tr, tr.resultReg, -1) result += IRCodeChunk(null, null).also { val tmpRegMsb = codeGen.registers.nextFree() - it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2 = tr.resultReg, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_msb") - it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=resultRegister, reg2 = tr.resultReg, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_lsb") + it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2 = tr.resultReg, labelSymbol= "${arrayVarSymbol}_msb") + it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=resultRegister, reg2 = tr.resultReg, labelSymbol= "${arrayVarSymbol}_lsb") it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=finalResultReg, reg2=tmpRegMsb, reg3=resultRegister) } } @@ -1338,7 +1341,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { return result } - fun operatorEqualsInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks { + fun operatorEqualsNotEqualsInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks { return if(vmDt==IRDataType.FLOAT) { createInplaceFloatComparison(knownAddress, symbol, operand, Opcode.SEQ) } else { @@ -1391,7 +1394,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { } } - private fun createInplaceComparison( + private fun createInplaceComparison( knownAddress: Int?, symbol: String?, vmDt: IRDataType, @@ -1402,6 +1405,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { val valueReg = codeGen.registers.nextFree() val cmpResultReg = codeGen.registers.nextFree() if(operand is PtNumber) { + // TODO optimize if operand is 0 val numberReg = codeGen.registers.nextFree() if (knownAddress != null) { // in-place modify a memory location @@ -1453,6 +1457,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { val cmpReg = codeGen.registers.nextFree() val zeroReg = codeGen.registers.nextFree() if(operand is PtNumber) { + // TODO optimize if operand is 0 ? val numberReg = codeGen.registers.nextFreeFloat() val cmpResultReg = codeGen.registers.nextFree() if (knownAddress != null) { @@ -1507,6 +1512,38 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { return result } + fun operatorEqualsNotEqualsInplace(array: PtArrayIndexer, eltSize: Int, value: PtExpression, equals: Boolean): Result { + val result = mutableListOf() + if(array.splitWords) + TODO("inplace == for split word array") + if(array.usesPointerVariable) + TODO("inplace == for pointer variable") + val vmDt = irType(array.type) + val constIndex = array.index.asConstInteger() + val constValue = value.asConstInteger() + val cmpResultReg = codeGen.registers.nextFree() + if(constIndex!=null) { + if(constValue!=null) { + val valueReg = codeGen.registers.nextFree() + if(constValue==0) { + val compareAndSetOpcode = if(equals) Opcode.SZ else Opcode.SNZ + result += IRCodeChunk(null, null).also { + it += IRInstruction(Opcode.LOADM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) + it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2=valueReg) + it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) + } + } else { + return Err(NotImplementedError("compare against non-zero value")) + } + } else { + TODO("non const value") + } + } else { + TODO("non const index") + } + return Ok(result) + } + } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 38d4ac7ca..f29d8766b 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -475,8 +475,8 @@ class IRCodeGen( val tmpRegLsb = registers.nextFree() val tmpRegMsb = registers.nextFree() val concatReg = registers.nextFree() - it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, immediate = iterableLength, labelSymbol=iterable.name+"_msb") - it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, immediate = iterableLength, labelSymbol=iterable.name+"_lsb") + it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, labelSymbol=iterable.name+"_msb") + it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, labelSymbol=iterable.name+"_lsb") it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=concatReg, reg2=tmpRegMsb, reg3=tmpRegLsb) it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=concatReg, labelSymbol = loopvarSymbol) } @@ -1462,7 +1462,6 @@ class IRCodeGen( else -> throw AssemblyError("weird operator") } } else { - val arrayLength = symbolTable.getLength(array.variable.name) val indexTr = expressionEval.translateExpression(array.index) addToResult(result, indexTr, indexTr.resultReg, -1) val incReg = registers.nextFree() @@ -1470,28 +1469,28 @@ class IRCodeGen( when(postIncrDecr.operator) { "++" -> { result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb") + it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb") it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=incReg) - it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb") + it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb") it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel) - it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb") + it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_msb") it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=incReg) - it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb") + it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_msb") } result += IRCodeChunk(skipLabel, null) } "--" -> { result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb") + it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb") it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel) - it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb") + it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_msb") it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1=incReg) - it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb") + it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_msb") } result += IRCodeChunk(skipLabel, null).also { - it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb") + it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb") it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1=incReg) - it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb") + it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb") } } else -> throw AssemblyError("weird operator") diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 6fc84a0a0..a821e1569 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,6 +1,10 @@ TODO ==== +IR assignVarAugmented(): implement all operators. +IR expressionGen.kt: optimize various stuff if the operand is const value 0 + + maze: if cell & UP!=0 and @(celladdr(cx,cy-1)) & (WALKED|BACKTRACKED) ==0 ^^ adding this !=0 caused a weird beq + / lda #1 / + to appear in front of the shortcircuit beq... @@ -65,6 +69,7 @@ Optimizations: those checks should probably be removed, or be made permanent - optimizeCommonSubExpressions: currently only looks in expressions on a single line, could search across multiple expressions + STRUCTS again? -------------- diff --git a/examples/test.p8 b/examples/test.p8 index edc297234..aa89810c0 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,15 +1,32 @@ %import textio -%zeropage dontuse +%zeropage basicsafe +%option no_sysinit main { sub start() { - ubyte @shared ubb = 99 - uword @shared uww = 12345 - ubyte[200] @shared barr - uword @shared ptr = memory("data", $2000, 0) + uword[3] ubarr + bool[3] barr + bool @shared bb - %breakpoint +; ubarr[1] = ubarr[1] + 2 +; ubarr[1] = ubarr[1] * 3 - txt.print_uwhex(sys.progend(), true) +; barr[1] = barr[0] and barr[2] +; barr[1] = barr[0] or barr[2] +; barr[1] = barr[0] xor barr[2] +; barr[1] = not barr[0] + + ubarr[1] = 999 + ubarr[1] = ubarr[1]==999 + txt.print_uw(ubarr[1]) + +; barr[1] = barr[1] and bb +; barr[1] = barr[1] or bb +; barr[1] = barr[1] xor bb + +; bb = bb and barr[1] +; bb = bb or barr[1] +; bb = bb xor barr[1] +; bb = not bb } }