package prog8.codegen.cpu6502.assignment

import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.VariableAllocator
import prog8.codegen.cpu6502.returnsWhatWhere


internal class AssignmentAsmGen(private val program: PtProgram,
                                private val asmgen: AsmGen6502Internal,
                                private val anyExprGen: AnyExprAsmGen,
                                private val allocator: VariableAllocator) {
    private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator)

    fun translate(assignment: PtAssignment) {
        val target = AsmAssignTarget.fromAstAssignment(assignment.target, assignment.definingISub(), asmgen)
        val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target)
        val pos = if(assignment.position !== Position.DUMMY) assignment.position else if(assignment.target.position !== Position.DUMMY) assignment.target.position else assignment.value.position
        val assign = AsmAssignment(source, target, program.memsizer, pos)
        translateNormalAssignment(assign, assignment.definingISub())
    }

    fun translate(augmentedAssign: PtAugmentedAssign) {
        val target = AsmAssignTarget.fromAstAssignment(augmentedAssign.target, augmentedAssign.definingISub(), asmgen)
        val source = AsmAssignSource.fromAstSource(augmentedAssign.value, program, asmgen).adjustSignedUnsigned(target)
        val pos = if(augmentedAssign.position !== Position.DUMMY) augmentedAssign.position else if(augmentedAssign.target.position !== Position.DUMMY) augmentedAssign.target.position else augmentedAssign.value.position
        val assign = AsmAugmentedAssignment(source, augmentedAssign.operator, target, program.memsizer, pos)
        augmentableAsmGen.translate(assign, augmentedAssign.definingISub())
    }

    fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) {
        when(assign.source.kind) {
            SourceStorageKind.LITERALNUMBER -> {
                // simple case: assign a constant number
                val num = assign.source.number!!.number
                when (assign.target.datatype) {
                    DataType.UBYTE, DataType.BYTE -> assignConstantByte(assign.target, num.toInt())
                    DataType.UWORD, DataType.WORD -> assignConstantWord(assign.target, num.toInt())
                    DataType.FLOAT -> assignConstantFloat(assign.target, num)
                    else -> throw AssemblyError("weird numval type")
                }
            }
            SourceStorageKind.VARIABLE -> {
                // simple case: assign from another variable
                val variable = assign.source.asmVarname
                when (assign.target.datatype) {
                    DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable)
                    DataType.WORD -> assignVariableWord(assign.target, variable, assign.source.datatype)
                    DataType.UWORD -> {
                        if(assign.source.datatype in PassByReferenceDatatypes)
                            assignAddressOf(assign.target, variable, null, null)
                        else
                            assignVariableWord(assign.target, variable, assign.source.datatype)
                    }
                    DataType.FLOAT -> assignVariableFloat(assign.target, variable)
                    DataType.STR -> assignVariableString(assign.target, variable)
                    else -> throw AssemblyError("unsupported assignment target type ${assign.target.datatype}")
                }
            }
            SourceStorageKind.ARRAY -> {
                val value = assign.source.array!!
                val elementDt = assign.source.datatype
                val arrayVarName = asmgen.asmVariableName(value.variable)

                if(value.usesPointerVariable) {
                    if(elementDt !in ByteDatatypes)
                        throw AssemblyError("non-array var indexing requires bytes dt")
                    if(value.type != DataType.UBYTE)
                        throw AssemblyError("non-array var indexing requires bytes index")
                    if(asmgen.isTargetCpu(CpuType.CPU65c02) && (value.index as? PtNumber)?.number==0.0) {
                        if (asmgen.isZpVar(value.variable)) {
                            asmgen.out("  lda  ($arrayVarName)")
                        } else {
                            asmgen.out("  lda  $arrayVarName |  sta  P8ZP_SCRATCH_W1 |  lda  $arrayVarName+1 |  sta  P8ZP_SCRATCH_W1+1")
                            asmgen.out("  lda  (P8ZP_SCRATCH_W1)")
                        }
                    } else {
                        asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
                        if (asmgen.isZpVar(value.variable)) {
                            asmgen.out("  lda  ($arrayVarName),y")
                        } else {
                            asmgen.out("  lda  $arrayVarName |  sta  P8ZP_SCRATCH_W1 |  lda  $arrayVarName+1 |  sta  P8ZP_SCRATCH_W1+1")
                            asmgen.out("  lda  (P8ZP_SCRATCH_W1),y")
                        }
                    }
                    assignRegisterByte(assign.target, CpuRegister.A, elementDt in SignedDatatypes, false)
                    return
                }

                val constIndex = value.index.asConstInteger()

                if(value.splitWords) {
                    require(elementDt in WordDatatypes)
                    if(constIndex!=null) {
                        asmgen.out("  lda  ${arrayVarName}_lsb+$constIndex |  ldy  ${arrayVarName}_msb+$constIndex")
                        assignRegisterpairWord(assign.target, RegisterOrPair.AY)
                    } else {
                        asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
                        asmgen.out("  lda  ${arrayVarName}_lsb,y |  ldx  ${arrayVarName}_msb,y")
                        assignRegisterpairWord(assign.target, RegisterOrPair.AX)
                    }
                    return
                }

                if (constIndex!=null) {
                    // constant array index value
                    val indexValue = constIndex * program.memsizer.memorySize(elementDt)
                    when (elementDt) {
                        in ByteDatatypes -> {
                            asmgen.out("  lda  $arrayVarName+$indexValue")
                            assignRegisterByte(assign.target, CpuRegister.A, elementDt in SignedDatatypes, false)
                        }
                        in WordDatatypes -> {
                            asmgen.out("  lda  $arrayVarName+$indexValue |  ldy  $arrayVarName+$indexValue+1")
                            assignRegisterpairWord(assign.target, RegisterOrPair.AY)
                        }
                        DataType.FLOAT -> {
                            asmgen.out("  lda  #<($arrayVarName+$indexValue) |  ldy  #>($arrayVarName+$indexValue)")
                            assignFloatFromAY(assign.target)
                        }
                        else ->
                            throw AssemblyError("weird array type")
                    }
                } else {
                    when (elementDt) {
                        in ByteDatatypes -> {
                            asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
                            asmgen.out("  lda  $arrayVarName,y")
                            assignRegisterByte(assign.target, CpuRegister.A, elementDt in SignedDatatypes, true)
                        }
                        in WordDatatypes  -> {
                            asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
                            asmgen.out("  lda  $arrayVarName,y |  ldx  $arrayVarName+1,y")
                            assignRegisterpairWord(assign.target, RegisterOrPair.AX)
                        }
                        DataType.FLOAT -> {
                            asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.A)
                            asmgen.out("""
                                ldy  #>$arrayVarName
                                clc
                                adc  #<$arrayVarName
                                bcc  +
                                iny
+""")
                            assignFloatFromAY(assign.target)
                        }
                        else ->
                            throw AssemblyError("weird array elt type")
                    }
                }
            }
            SourceStorageKind.MEMORY -> {
                fun assignViaExprEval(expression: PtExpression) {
                    assignExpressionToVariable(expression, "P8ZP_SCRATCH_W2", DataType.UWORD)
                    asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
                    assignRegisterByte(assign.target, CpuRegister.A, false, true)
                }

                val value = assign.source.memory!!
                when (value.address) {
                    is PtNumber -> {
                        val address = (value.address as PtNumber).number.toUInt()
                        assignMemoryByte(assign.target, address, null)
                    }
                    is PtIdentifier -> {
                        assignMemoryByte(assign.target, null, value.address as PtIdentifier)
                    }
                    is PtBinaryExpression -> {
                        val addrExpr = value.address as PtBinaryExpression
                        if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
                            assignRegisterByte(assign.target, CpuRegister.A, false, true)
                        } else {
                            assignViaExprEval(value.address)
                        }
                    }
                    else -> assignViaExprEval(value.address)
                }
            }
            SourceStorageKind.EXPRESSION -> {
                assignExpression(assign, scope)
            }
            SourceStorageKind.REGISTER -> {
                asmgen.assignRegister(assign.source.register!!, assign.target)
            }
        }
    }

    private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) {
        when(val value = assign.source.expression!!) {
            is PtAddressOf -> {
                val sourceName = asmgen.asmSymbolName(value.identifier)
                val arrayDt = value.identifier.type
                assignAddressOf(assign.target, sourceName, arrayDt, value.arrayIndexExpr)
            }
            is PtNumber -> throw AssemblyError("source kind should have been literalnumber")
            is PtIdentifier -> throw AssemblyError("source kind should have been variable")
            is PtArrayIndexer -> throw AssemblyError("source kind should have been array")
            is PtMemoryByte -> throw AssemblyError("source kind should have been memory")
            is PtTypeCast -> assignTypeCastedValue(assign.target, value.type, value.value, value)
            is PtFunctionCall -> {
                val symbol = asmgen.symbolTable.lookup(value.name)
                val sub = symbol!!.astNode as IPtSubroutine
                asmgen.translateFunctionCall(value)
                val returnValue = sub.returnsWhatWhere().singleOrNull { it.first.registerOrPair!=null } ?: sub.returnsWhatWhere().single { it.first.statusflag!=null }
                when (returnValue.second) {
                    DataType.STR -> {
                        when(assign.target.datatype) {
                            DataType.UWORD -> {
                                // assign the address of the string result value
                                assignRegisterpairWord(assign.target, RegisterOrPair.AY)
                            }
                            DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
                                throw AssemblyError("stringvalue assignment should have been replaced by a call to strcpy")
                            }
                            else -> throw AssemblyError("weird target dt")
                        }
                    }
                    DataType.FLOAT -> {
                        // float result from function sits in FAC1
                        assignFAC1float(assign.target)
                    }
                    else -> {
                        // do NOT restore X register before assigning the result values first
                        when (returnValue.first.registerOrPair) {
                            RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A, returnValue.second in SignedDatatypes, true)
                            RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X, returnValue.second in SignedDatatypes, true)
                            RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y, returnValue.second in SignedDatatypes, true)
                            RegisterOrPair.AX -> assignVirtualRegister(assign.target, RegisterOrPair.AX)
                            RegisterOrPair.AY -> assignVirtualRegister(assign.target, RegisterOrPair.AY)
                            RegisterOrPair.XY -> assignVirtualRegister(assign.target, RegisterOrPair.XY)
                            RegisterOrPair.R0 -> assignVirtualRegister(assign.target, RegisterOrPair.R0)
                            RegisterOrPair.R1 -> assignVirtualRegister(assign.target, RegisterOrPair.R1)
                            RegisterOrPair.R2 -> assignVirtualRegister(assign.target, RegisterOrPair.R2)
                            RegisterOrPair.R3 -> assignVirtualRegister(assign.target, RegisterOrPair.R3)
                            RegisterOrPair.R4 -> assignVirtualRegister(assign.target, RegisterOrPair.R4)
                            RegisterOrPair.R5 -> assignVirtualRegister(assign.target, RegisterOrPair.R5)
                            RegisterOrPair.R6 -> assignVirtualRegister(assign.target, RegisterOrPair.R6)
                            RegisterOrPair.R7 -> assignVirtualRegister(assign.target, RegisterOrPair.R7)
                            RegisterOrPair.R8 -> assignVirtualRegister(assign.target, RegisterOrPair.R8)
                            RegisterOrPair.R9 -> assignVirtualRegister(assign.target, RegisterOrPair.R9)
                            RegisterOrPair.R10 -> assignVirtualRegister(assign.target, RegisterOrPair.R10)
                            RegisterOrPair.R11 -> assignVirtualRegister(assign.target, RegisterOrPair.R11)
                            RegisterOrPair.R12 -> assignVirtualRegister(assign.target, RegisterOrPair.R12)
                            RegisterOrPair.R13 -> assignVirtualRegister(assign.target, RegisterOrPair.R13)
                            RegisterOrPair.R14 -> assignVirtualRegister(assign.target, RegisterOrPair.R14)
                            RegisterOrPair.R15 -> assignVirtualRegister(assign.target, RegisterOrPair.R15)
                            else -> {
                                val sflag = returnValue.first.statusflag
                                if(sflag!=null)
                                    assignStatusFlagByte(assign.target, sflag)
                                else
                                    throw AssemblyError("should be just one register byte result value")
                            }
                        }
                    }
                }
            }
            is PtBuiltinFunctionCall -> {
                val returnDt = asmgen.translateBuiltinFunctionCallExpression(value, assign.target.register)
                if(assign.target.register==null) {
                    // still need to assign the result to the target variable/etc.
                    when(returnDt) {
                        in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A, returnDt in SignedDatatypes, false)            // function's byte result is in A
                        in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)    // function's word result is in AY
                        DataType.STR -> {
                            when (assign.target.datatype) {
                                DataType.STR -> {
                                    asmgen.out("""
                                        tax
                                        lda  #<${assign.target.asmVarname}
                                        sta  P8ZP_SCRATCH_W1
                                        lda  #>${assign.target.asmVarname}
                                        sta  P8ZP_SCRATCH_W1+1
                                        txa
                                        jsr  prog8_lib.strcpy""")
                                }
                                DataType.UWORD -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
                                else -> throw AssemblyError("str return value type mismatch with target")
                            }
                        }
                        DataType.FLOAT -> {
                            // float result from function sits in FAC1
                            assignFAC1float(assign.target)
                        }
                        else -> throw AssemblyError("weird result type")
                    }
                }
            }
            is PtPrefix -> {
                if(assign.target.array==null) {
                    if(assign.source.datatype==assign.target.datatype) {
                        if(assign.source.datatype in IntegerDatatypes) {
                            val signed = assign.source.datatype in SignedDatatypes
                            if(assign.source.datatype in ByteDatatypes) {
                                assignExpressionToRegister(value.value, RegisterOrPair.A, signed)
                                when(value.operator) {
                                    "+" -> {}
                                    "-" -> {
                                        if(asmgen.isTargetCpu(CpuType.CPU65c02))
                                            asmgen.out("  eor  #255 |  ina")
                                        else
                                            asmgen.out("  eor  #255 |  clc |  adc  #1")
                                    }
                                    "~" -> asmgen.out("  eor  #255")
                                    "not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
                                    else -> throw AssemblyError("invalid prefix operator")
                                }
                                assignRegisterByte(assign.target, CpuRegister.A, signed, false)
                            } else {
                                assignExpressionToRegister(value.value, RegisterOrPair.AY, signed)
                                when(value.operator) {
                                    "+" -> {}
                                    "-" -> {
                                        asmgen.out("""
                                            sec
                                            eor  #255
                                            adc  #0
                                            tax
                                            tya
                                            eor  #255
                                            adc  #0
                                            tay
                                            txa""")
                                    }
                                    "~" -> asmgen.out("  tax |  tya |  eor  #255 |  tay |  txa |  eor  #255")
                                    "not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
                                    else -> throw AssemblyError("invalid prefix operator")
                                }
                                assignRegisterpairWord(assign.target, RegisterOrPair.AY)
                            }
                        } else {
                            // First assign the value to the target then apply the operator in place on the target.
                            // This saves a temporary variable
                            translateNormalAssignment(
                                AsmAssignment(
                                    AsmAssignSource.fromAstSource(value.value, program, asmgen),
                                    assign.target, program.memsizer, assign.position
                                ), scope
                            )
                            when (value.operator) {
                                "+" -> {}
                                "-" -> inplaceNegate(assign, true, scope)
                                "~" -> inplaceInvert(assign, scope)
                                "not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
                                else -> throw AssemblyError("invalid prefix operator")
                            }
                        }
                    } else {
                        // use a temporary variable
                        val tempvar = if(value.type in ByteDatatypes) "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_W1"
                        assignExpressionToVariable(value.value, tempvar, value.type)
                        when (value.operator) {
                            "+" -> {}
                            "-", "~" -> {
                                val assignTempvar = AsmAssignment(
                                    AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = tempvar),
                                    AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, value.type, scope, assign.position, variableAsmName = tempvar),
                                    program.memsizer, assign.position)
                                if(value.operator=="-")
                                    inplaceNegate(assignTempvar, true, scope)
                                else
                                    inplaceInvert(assignTempvar, scope)
                            }
                            "not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
                            else -> throw AssemblyError("invalid prefix operator")
                        }
                        if(value.type in ByteDatatypes)
                            assignVariableByte(assign.target, tempvar)
                        else
                            assignVariableWord(assign.target, tempvar, value.type)
                    }
                } else {
                    assignPrefixedExpressionToArrayElt(assign, scope)
                }
            }
            is PtContainmentCheck -> {
                containmentCheckIntoA(value)
                assignRegisterByte(assign.target, CpuRegister.A, false, true)
            }
            is PtBinaryExpression -> {
                if(!attemptAssignOptimizedBinexpr(value, assign)) {
                    // TOO BAD: the expression was too complex to translate into assembly.
                    val pos = if(value.position!==Position.DUMMY) value.position else assign.position
                    throw AssemblyError("Expression is too complex to translate into assembly. Split it up into several separate statements, introduce a temporary variable, or otherwise rewrite it. Location: $pos")
                }
            }
            else -> throw AssemblyError("weird assignment value type $value")
        }
    }

    private fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment, scope: IPtSubroutine?) {
        require(assign.source.expression is PtPrefix)
        if(assign.source.datatype==DataType.FLOAT) {
            // floatarray[x] = -value   ... just use FAC1 to calculate the expression into and then store that back into the array.
            assignExpressionToRegister(assign.source.expression, RegisterOrPair.FAC1, true)
            assignFAC1float(assign.target)
        } else {
            // array[x] = -value   ... use a tempvar then store that back into the array.
            val tempvar = asmgen.getTempVarName(assign.target.datatype)
            val assignToTempvar = AsmAssignment(assign.source,
                AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, assign.target.datatype, assign.target.scope, assign.target.position,
                    variableAsmName=tempvar, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position)
            asmgen.translateNormalAssignment(assignToTempvar, scope)
            when(assign.target.datatype) {
                in ByteDatatypes -> assignVariableByte(assign.target, tempvar)
                in WordDatatypes -> assignVariableWord(assign.target, tempvar, assign.source.datatype)
                DataType.FLOAT -> assignVariableFloat(assign.target, tempvar)
                else -> throw AssemblyError("weird dt")
            }
        }
    }

    private fun assignVirtualRegister(target: AsmAssignTarget, register: RegisterOrPair) {
        when(target.datatype) {
            in ByteDatatypes -> {
                asmgen.out("  lda  cx16.${register.toString().lowercase()}L")
                assignRegisterByte(target, CpuRegister.A, false, false)
            }
            in WordDatatypes -> assignRegisterpairWord(target, register)
            else -> throw AssemblyError("expected byte or word")
        }
    }

    private fun attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
        val translatedOk = when (expr.operator) {
            in ComparisonOperators -> optimizedComparison(expr, assign)
            in setOf("&", "|", "^", "and", "or", "xor") -> optimizedLogicalOrBitwiseExpr(expr, assign.target)
            "+", "-" -> optimizedPlusMinExpr(expr, assign.target)
            "<<", ">>" -> optimizedBitshiftExpr(expr, assign.target)
            "*" -> optimizedMultiplyExpr(expr, assign.target)
            "/" -> optimizedDivideExpr(expr, assign.target)
            "%" -> optimizedRemainderExpr(expr, assign.target)
            else -> false
        }

        return if(translatedOk)
            true
        else
            anyExprGen.assignAnyExpressionUsingStack(expr, assign)
    }

    private fun optimizedComparison(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
        if(expr.right.asConstInteger() == 0) {
            if(expr.operator == "==" || expr.operator=="!=") {
                when(assign.target.datatype) {
                    in ByteDatatypes -> if(attemptAssignToByteCompareZero(expr, assign)) return true
                    else -> {
                        // do nothing, this is handled by a type cast.
                    }
                }
            }
        }

        if(expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
            if(assignOptimizedComparisonBytes(expr, assign))
                return true
        } else  if(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
            if(assignOptimizedComparisonWords(expr, assign))
                return true
        }

        val origTarget = assign.target.origAstTarget
        if(origTarget!=null) {
            assignConstantByte(assign.target, 0)
            val assignTrue = PtNodeGroup()
            val assignment = PtAssignment(assign.position)
            assignment.add(origTarget)
            assignment.add(PtNumber.fromBoolean(true, assign.position))
            assignTrue.add(assignment)
            val assignFalse = PtNodeGroup()
            val ifelse = PtIfElse(assign.position)
            val exprClone = PtBinaryExpression(expr.operator, expr.type, expr.position)
            expr.children.forEach { exprClone.children.add(it) }        // doesn't seem to need a deep clone
            ifelse.add(exprClone)
            ifelse.add(assignTrue)
            ifelse.add(assignFalse)
            ifelse.parent = expr.parent
            asmgen.translate(ifelse)
            return true
        }

        return false
    }

    private fun optimizedRemainderExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
        when(expr.type) {
            DataType.UBYTE -> {
                assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
                asmgen.out("  pha")
                assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
                asmgen.out("  pla |  jsr  math.divmod_ub_asm")
                if(target.register==RegisterOrPair.A)
                    asmgen.out("  cmp  #0")     // fix the status register
                else
                    assignRegisterByte(target, CpuRegister.A, false, true)
                return true
            }
            DataType.UWORD -> {
                asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
                asmgen.out("  jsr  math.divmod_uw_asm")
                assignVariableWord(target, "P8ZP_SCRATCH_W2", DataType.UWORD)
                return true
            }
            else -> return false
        }
    }

    private fun optimizedDivideExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
        when(expr.type) {
            DataType.UBYTE -> {
                assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
                asmgen.out("  pha")
                assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
                asmgen.out("  pla |  jsr  math.divmod_ub_asm")
                assignRegisterByte(target, CpuRegister.Y, false, true)
                return true
            }
            DataType.BYTE -> {
                assignExpressionToRegister(expr.left, RegisterOrPair.A, true)
                asmgen.out("  pha")
                assignExpressionToRegister(expr.right, RegisterOrPair.Y, true)
                asmgen.out("  pla |  jsr  math.divmod_b_asm")
                assignRegisterByte(target, CpuRegister.Y, true, true)
                return true
            }
            DataType.UWORD -> {
                asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
                asmgen.out("  jsr  math.divmod_uw_asm")
                assignRegisterpairWord(target, RegisterOrPair.AY)
                return true
            }
            DataType.WORD -> {
                asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
                asmgen.out("  jsr  math.divmod_w_asm")
                assignRegisterpairWord(target, RegisterOrPair.AY)
                return true
            }
            else -> return false
        }
    }

    private fun optimizedMultiplyExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
        val value = expr.right.asConstInteger()
        if(value==null) {
            when(expr.type) {
                in ByteDatatypes -> {
                    assignExpressionToRegister(expr.left, RegisterOrPair.A, expr.type in SignedDatatypes)
                    asmgen.out("  pha")
                    assignExpressionToRegister(expr.right, RegisterOrPair.Y, expr.type in SignedDatatypes)
                    asmgen.out("  pla |  jsr  math.multiply_bytes")
                    assignRegisterByte(target, CpuRegister.A, false, true)
                    return true
                }
                in WordDatatypes -> {
                    if(expr.definingBlock()!!.veraFxMuls) {
                        // cx16 verafx hardware mul
                        if(expr.right.isSimple()) {
                            asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.R0, expr.left.type in SignedDatatypes)
                            asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.R1, expr.left.type in SignedDatatypes)
                        } else {
                            asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.left.type in SignedDatatypes)
                            asmgen.saveRegisterStack(CpuRegister.A, false)
                            asmgen.saveRegisterStack(CpuRegister.Y, false)
                            asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.R1, expr.left.type in SignedDatatypes)
                            asmgen.restoreRegisterStack(CpuRegister.Y, false)
                            asmgen.restoreRegisterStack(CpuRegister.A, false)
                            asmgen.out("  sta  cx16.r0 |  sty  cx16.r0+1")
                        }
                        asmgen.out("  jsr  verafx.muls")
                        assignRegisterpairWord(target, RegisterOrPair.AY)
                        return true
                    } else {
                        asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "math.multiply_words.multiplier")
                        asmgen.out("  jsr  math.multiply_words")
                        assignRegisterpairWord(target, RegisterOrPair.AY)
                    }
                    return true
                }
                else -> return false
            }
        } else {
            when (expr.type) {
                in ByteDatatypes -> {
                    assignExpressionToRegister(expr.left, RegisterOrPair.A, expr.type in SignedDatatypes)
                    if (value in asmgen.optimizedByteMultiplications)
                        asmgen.out("  jsr  math.mul_byte_${value}")
                    else
                        asmgen.out("  ldy  #$value |  jsr  math.multiply_bytes")
                    assignRegisterByte(target, CpuRegister.A, false, true)
                    return true
                }
                in WordDatatypes -> {
                    if (value in asmgen.optimizedWordMultiplications) {
                        assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type in SignedDatatypes)
                        asmgen.out("  jsr  math.mul_word_${value}")
                    }
                    else {
                        if(expr.definingBlock()!!.veraFxMuls){
                            // cx16 verafx hardware mul
                            asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "cx16.r1")
                            asmgen.out("""
                                sta  cx16.r0
                                sty  cx16.r0+1
                                jsr  verafx.muls""")
                        } else {
                            asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "math.multiply_words.multiplier")
                            asmgen.out("  jsr  math.multiply_words")
                        }
                    }
                    assignRegisterpairWord(target, RegisterOrPair.AY)
                    return true
                }
                else -> return false
            }
        }
    }

    private fun optimizedBitshiftExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
        val signed = expr.left.type in SignedDatatypes
        val shifts = expr.right.asConstInteger()
        val dt = expr.left.type
        if(shifts==null) {
            // bit shifts with variable shifts
            when(expr.right.type) {
                in ByteDatatypes -> {
                    assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
                }
                in WordDatatypes -> {
                    assignExpressionToRegister(expr.right, RegisterOrPair.AY, false)
                    asmgen.out("""
                        cpy  #0
                        beq  +
                        lda  #127
+""")
                }
                else -> throw AssemblyError("weird shift value type")
            }
            asmgen.out("  pha")
            if(dt in ByteDatatypes) {
                assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
                asmgen.restoreRegisterStack(CpuRegister.Y, true)
                if(expr.operator==">>")
                    if(signed)
                        asmgen.out("  jsr  math.lsr_byte_A")
                    else
                        asmgen.out("  jsr  math.lsr_ubyte_A")
                else
                    asmgen.out("  jsr  math.asl_byte_A")
                assignRegisterByte(target, CpuRegister.A, signed, true)
                return true
            } else {
                assignExpressionToRegister(expr.left, RegisterOrPair.AY, signed)
                asmgen.restoreRegisterStack(CpuRegister.X, true)
                if(expr.operator==">>")
                    if(signed)
                        asmgen.out("  jsr  math.lsr_word_AY")
                    else
                        asmgen.out("  jsr  math.lsr_uword_AY")
                else
                    asmgen.out("  jsr  math.asl_word_AY")
                assignRegisterpairWord(target, RegisterOrPair.AY)
                return true
            }
        }
        else {
            // bit shift with constant value
            if(dt in ByteDatatypes) {
                assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
                when (shifts) {
                    in 0..7 -> {
                        if (expr.operator == "<<") {
                            repeat(shifts) {
                                asmgen.out("  asl  a")
                            }
                        } else {
                            if (signed && shifts > 0) {
                                asmgen.out("  ldy  #$shifts |  jsr  math.lsr_byte_A")
                            } else {
                                repeat(shifts) {
                                    asmgen.out("  lsr  a")
                                }
                            }
                        }
                        assignRegisterByte(target, CpuRegister.A, signed, true)
                        return true
                    }
                    else -> {
                        if(signed && expr.operator==">>") {
                            asmgen.out("  ldy  #$shifts |  jsr  math.lsr_byte_A")
                        } else {
                            asmgen.out("  lda  #0")
                        }
                        assignRegisterByte(target, CpuRegister.A, signed, true)
                        return true
                    }
                }
            } else if(dt in WordDatatypes) {
                assignExpressionToRegister(expr.left, RegisterOrPair.AY, signed)
                when (shifts) {
                    in 0..7 -> {
                        if(expr.operator=="<<") {
                            if(shifts>0) {
                                asmgen.out("  sty  P8ZP_SCRATCH_B1")
                                repeat(shifts) {
                                    asmgen.out("  asl  a |  rol  P8ZP_SCRATCH_B1")
                                }
                                asmgen.out("  ldy  P8ZP_SCRATCH_B1")
                            }
                        } else {
                            if(signed && shifts>0) {
                                asmgen.out("  ldx  #$shifts |  jsr  math.lsr_word_AY")
                            } else {
                                if(shifts>0) {
                                    asmgen.out("  sty  P8ZP_SCRATCH_B1")
                                    repeat(shifts) {
                                        asmgen.out("  lsr  P8ZP_SCRATCH_B1 |  ror  a")
                                    }
                                    asmgen.out("  ldy  P8ZP_SCRATCH_B1")
                                }
                            }
                        }
                        assignRegisterpairWord(target, RegisterOrPair.AY)
                        return true
                    }
                    in 8..15 -> {
                        if(expr.operator == "<<") {
                            // msb = lsb << (shifts-8),   lsb = 0
                            repeat(shifts-8) {
                                asmgen.out("  asl  a")
                            }
                            asmgen.out("  tay |  lda  #0")
                        } else {
                            asmgen.out("  ldx  #$shifts |  jsr  math.lsr_word_AY")
                        }
                        assignRegisterpairWord(target, RegisterOrPair.AY)
                        return true
                    }
                    else -> {
                        if(signed && expr.operator==">>") {
                            asmgen.out("  ldx  #$shifts |  jsr  math.lsr_word_AY")
                        } else {
                            asmgen.out("  lda  #0 |  ldy  #0")
                        }
                        assignRegisterpairWord(target, RegisterOrPair.AY)
                        return true
                    }
                }
            }
        }
        return false
    }

    private fun optimizedPlusMinExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
        val dt = expr.type
        val left = expr.left
        val right = expr.right
        if(dt in ByteDatatypes) {
            when (right) {
                is PtIdentifier -> {
                    assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE)
                    val symname = asmgen.asmVariableName(right)
                    if(expr.operator=="+")
                        asmgen.out("  clc |  adc  $symname")
                    else
                        asmgen.out("  sec |  sbc  $symname")
                    assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
                    return true
                }
                is PtNumber -> {
                    assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE)
                    if(expr.operator=="+")
                        asmgen.out("  clc |  adc  #${right.number.toHex()}")
                    else
                        asmgen.out("  sec |  sbc  #${right.number.toHex()}")
                    assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
                    return true
                }
                else -> {
                    val rightArrayIndexer = expr.right as? PtArrayIndexer
                    if(rightArrayIndexer!=null && rightArrayIndexer.type in ByteDatatypes && left.type in ByteDatatypes) {
                        // special optimization for  bytevalue +/- bytearr[y] :  no need to use a tempvar, just use adc array,y or sbc array,y  or  adc (ptr),y / sbc (ptr),y
                        assignExpressionToRegister(left, RegisterOrPair.A, left.type==DataType.BYTE)
                        if(!rightArrayIndexer.index.isSimple()) asmgen.out("  pha")
                        asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y, false)
                        if(!rightArrayIndexer.index.isSimple()) asmgen.out("  pla")
                        if(rightArrayIndexer.usesPointerVariable && !asmgen.isZpVar(rightArrayIndexer.variable)) {
                            asmgen.out("""
                                ldx  ${rightArrayIndexer.variable.name}
                                stx  P8ZP_SCRATCH_W1
                                ldx  ${rightArrayIndexer.variable.name}+1
                                stx  P8ZP_SCRATCH_W1+1""")
                            if (expr.operator == "+")
                                asmgen.out("  clc |  adc  (P8ZP_SCRATCH_W1),y")
                            else
                                asmgen.out("  sec |  sbc  (P8ZP_SCRATCH_W1),y")
                        } else {
                            val arrayvarname = if (rightArrayIndexer.usesPointerVariable)
                                "(${rightArrayIndexer.variable.name})"
                            else
                                asmgen.asmSymbolName(rightArrayIndexer.variable)
                            if (expr.operator == "+")
                                asmgen.out("  clc |  adc  $arrayvarname,y")
                            else
                                asmgen.out("  sec |  sbc  $arrayvarname,y")
                        }
                        assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
                    } else {
                        assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", right.type)
                        assignExpressionToRegister(left, RegisterOrPair.A, left.type==DataType.BYTE)
                        if (expr.operator == "+")
                            asmgen.out("  clc |  adc  P8ZP_SCRATCH_B1")
                        else
                            asmgen.out("  sec |  sbc  P8ZP_SCRATCH_B1")
                        assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
                    }
                    return true
                }
            }
        } else if(dt in WordDatatypes) {

            fun doAddOrSubWordExpr() {
                asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
                if(expr.operator=="+")
                    asmgen.out("""
                                clc
                                adc  P8ZP_SCRATCH_W1
                                tax
                                tya
                                adc  P8ZP_SCRATCH_W1+1
                                tay
                                txa""")
                else
                    asmgen.out("""
                                sec
                                sbc  P8ZP_SCRATCH_W1
                                tax
                                tya
                                sbc  P8ZP_SCRATCH_W1+1
                                tay
                                txa""")
                assignRegisterpairWord(target, RegisterOrPair.AY)
            }

            when (right) {
                is PtAddressOf -> {
                    val symbol = asmgen.asmVariableName(right.identifier)
                    if(right.isFromArrayElement) {
                        TODO("address-of array element $symbol at ${right.position}")
                    } else {
                        assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
                        if(expr.operator=="+")
                            asmgen.out("""
                                    clc
                                    adc  #<$symbol
                                    tax
                                    tya
                                    adc  #>$symbol
                                    tay
                                    txa""")
                        else
                            asmgen.out("""
                                    sec
                                    sbc  #<$symbol
                                    tax
                                    tya
                                    sbc  #>$symbol
                                    tay
                                    txa""")
                        assignRegisterpairWord(target, RegisterOrPair.AY)
                        return true
                    }
                }
                is PtIdentifier -> {
                    val symname = asmgen.asmVariableName(right)
                    assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
                    if(expr.operator=="+")
                        asmgen.out("""
                                clc
                                adc  $symname
                                tax
                                tya
                                adc  $symname+1
                                tay
                                txa""")
                    else
                        asmgen.out("""
                                sec
                                sbc  $symname
                                tax
                                tya
                                sbc  $symname+1
                                tay
                                txa""")
                    assignRegisterpairWord(target, RegisterOrPair.AY)
                    return true
                }
                is PtNumber -> {
                    assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
                    if(expr.operator=="+") {
                        asmgen.out("""
                                clc
                                adc  #<${right.number.toHex()}
                                tax
                                tya
                                adc  #>${right.number.toHex()}
                                tay
                                txa""")
                    } else if(expr.operator=="-") {
                        asmgen.out("""
                                sec
                                sbc  #<${right.number.toHex()}
                                tax
                                tya
                                sbc  #>${right.number.toHex()}
                                tay
                                txa""")
                    }
                    assignRegisterpairWord(target, RegisterOrPair.AY)
                    return true
                }
                is PtTypeCast -> {
                    val castedValue = right.value
                    if(right.type in WordDatatypes && castedValue.type in ByteDatatypes && castedValue is PtIdentifier) {
                        if(right.type in SignedDatatypes) {
                            // we need to sign extend, do this via temporary word variable
                            asmgen.assignExpressionToVariable(right, "P8ZP_SCRATCH_W1", DataType.WORD)
                            assignExpressionToRegister(left, RegisterOrPair.AY, dt == DataType.WORD)
                            if(expr.operator=="+") {
                                asmgen.out("""
                                clc
                                adc  P8ZP_SCRATCH_W1
                                tax
                                tya
                                adc  P8ZP_SCRATCH_W1+1
                                tay
                                txa""")
                            } else if(expr.operator=="-") {
                                asmgen.out("""
                                sec
                                sbc  P8ZP_SCRATCH_W1
                                tax
                                tya
                                sbc  P8ZP_SCRATCH_W1+1
                                tay
                                txa""")
                            }
                        } else {
                            assignExpressionToRegister(left, RegisterOrPair.AY, dt == DataType.WORD)
                            val castedSymname = asmgen.asmVariableName(castedValue)
                            if (expr.operator == "+")
                                asmgen.out(
                                """
                                    clc
                                    adc  $castedSymname
                                    bcc  +
                                    iny
+"""
                            )
                            else
                                asmgen.out(
                                """
                                    sec
                                    sbc  $castedSymname
                                    bcs  +
                                    dey
+"""
                            )
                        }
                        assignRegisterpairWord(target, RegisterOrPair.AY)
                        return true
                    }
                    doAddOrSubWordExpr()
                    return true
                }
                else -> {
                    doAddOrSubWordExpr()
                    return true
                }
            }
        }
        return false
    }

    private fun optimizedLogicalOrBitwiseExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
        if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
            if (expr.right.isSimple()) {
                if (expr.right is PtNumber || expr.right is PtIdentifier) {
                    assignLogicalWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right)
                    return true
                }
                else if (expr.left is PtNumber || expr.left is PtIdentifier) {
                    assignLogicalWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left)
                    return true
                }
            }

            assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
            asmgen.saveRegisterStack(CpuRegister.A, false)
            assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
            asmgen.restoreRegisterStack(CpuRegister.A, false)
            when (expr.operator) {
                "&", "and" -> asmgen.out("  and  P8ZP_SCRATCH_B1")
                "|", "or" -> asmgen.out("  ora  P8ZP_SCRATCH_B1")
                "^", "xor" -> asmgen.out("  eor  P8ZP_SCRATCH_B1")
                else -> throw AssemblyError("invalid operator")
            }
            assignRegisterByte(target, CpuRegister.A, false, true)
            return true
        }
        else if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
            if (expr.right.isSimple()) {
                if (expr.right is PtNumber || expr.right is PtIdentifier) {
                    assignLogicalWithSimpleRightOperandWord(target, expr.left, expr.operator, expr.right)
                    return true
                }
                else if (expr.left is PtNumber || expr.left is PtIdentifier) {
                    assignLogicalWithSimpleRightOperandWord(target, expr.right, expr.operator, expr.left)
                    return true
                }
            }
            asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
            when (expr.operator) {
                "&", "and" -> asmgen.out("  and  P8ZP_SCRATCH_W1 |  tax |  tya |  and  P8ZP_SCRATCH_W1+1 |  tay |  txa")
                "|", "or" -> asmgen.out("  ora  P8ZP_SCRATCH_W1 |  tax |  tya |  ora  P8ZP_SCRATCH_W1+1 |  tay |  txa")
                "^", "xor" -> asmgen.out("  eor  P8ZP_SCRATCH_W1 |  tax |  tya |  eor  P8ZP_SCRATCH_W1+1 |  tay |  txa")
                else -> throw AssemblyError("invalid operator")
            }
            assignRegisterpairWord(target, RegisterOrPair.AY)
            return true
        }
        return false
    }

    private fun assignOptimizedComparisonBytes(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
        val signed = expr.left.type == DataType.BYTE || expr.right.type ==  DataType.BYTE
        when(expr.operator) {
            "==" -> byteEquals(expr)
            "!=" -> byteNotEquals(expr)
            "<" -> byteLess(expr, signed)
            "<=" -> byteLessEquals(expr, signed)
            ">" -> byteGreater(expr, signed)
            ">=" -> byteGreaterEquals(expr, signed)
            else -> return false
        }

        assignRegisterByte(assign.target, CpuRegister.A, false, true)
        return true
    }

    private fun byteEquals(expr: PtBinaryExpression) {
        when (expr.right) {
            is PtNumber -> {
                val number = (expr.right as PtNumber).number.toInt()
                asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
                asmgen.out("""
                    cmp  #$number
                    beq  +
                    lda  #0
                    beq  ++
+                   lda  #1
+""")
            }
            is PtIdentifier -> {
                val varname = (expr.right as PtIdentifier).name
                asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
                asmgen.out("""
                    cmp  $varname
                    beq  +
                    lda  #0
                    beq  ++
+                   lda  #1
+""")
            }
            else -> {
                asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
                asmgen.out("""
                    cmp  P8ZP_SCRATCH_B1
                    beq  +
                    lda  #0
                    beq  ++
+                   lda  #1
+""")
            }
        }
    }

    private fun byteNotEquals(expr: PtBinaryExpression) {
        when(expr.right) {
            is PtNumber -> {
                val number = (expr.right as PtNumber).number.toInt()
                asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
                asmgen.out("""
                    cmp  #$number
                    bne  +
                    lda  #0
                    beq  ++
+                   lda  #1
+""")
            }
            is PtIdentifier -> {
                val varname = (expr.right as PtIdentifier).name
                asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
                asmgen.out("""
                    cmp  $varname
                    bne  +
                    lda  #0
                    beq  ++
+                   lda  #1
+""")
            }
            else -> {
                asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
                asmgen.out("""
                    cmp  P8ZP_SCRATCH_B1
                    bne  +
                    lda  #0
                    beq  ++
+                   lda  #1
+""")
            }
        }
    }

    private fun byteLess(expr: PtBinaryExpression, signed: Boolean) {
        // note: this is the inverse of byteGreaterEqual
        when(expr.right) {
            is PtNumber -> {
                val number = (expr.right as PtNumber).number.toInt()
                asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
                if(signed) {
                    asmgen.out("""
                        sec
                        sbc  #$number
                        bvs  +
                        eor  #$80
+                       asl  a
                        rol  a
                        and  #1
                        eor  #1""")
                }
                else
                    asmgen.out("""
                        cmp  #$number
                        rol  a
                        and  #1
                        eor  #1""")
            }
            is PtIdentifier -> {
                val varname = (expr.right as PtIdentifier).name
                asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
                if(signed) {
                    asmgen.out("""
                        sec
                        sbc  $varname
                        bvs  +
                        eor  #$80
+                       asl  a
                        rol  a
                        and  #1
                        eor  #1""")
                }
                else
                    asmgen.out("""
                        cmp  $varname
                        rol  a
                        and  #1
                        eor  #1""")
            }
            else -> {
                // note: left and right operands get reversed here to reduce code size
                asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
                if(signed)
                    asmgen.out("""
                        clc
                        sbc  P8ZP_SCRATCH_B1
                        bvs  +
                        eor  #$80
+                       asl  a
                        rol  a
                        and  #1""")
                else
                    asmgen.out("""
                        cmp  P8ZP_SCRATCH_B1
                        bcc  +
                        beq  +
                        lda  #1
                        bne  ++
+                       lda  #0
+""")
            }
        }
    }

    private fun byteLessEquals(expr: PtBinaryExpression, signed: Boolean) {
        // note: this is the inverse of byteGreater
        when(expr.right) {
            is PtNumber -> {
                val number = (expr.right as PtNumber).number.toInt()
                asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
                if(signed)
                    asmgen.out("""
                        sec
                        sbc  #$number
                        bvc  +
                        eor  #$80
+                       bmi  +
                        beq  +
                        lda  #0
                        beq  ++
+                       lda  #1                        
+""")
                else
                    asmgen.out("""
                        cmp  #$number
                        bcc  +
                        beq  +
                        lda  #0
                        beq  ++
+                       lda  #1
+""")
            }
            is PtIdentifier -> {
                val varname = (expr.right as PtIdentifier).name
                asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
                if(signed)
                    asmgen.out("""
                        sec
                        sbc  $varname
                        bvc  +
                        eor  #$80
+                       bmi  +
                        beq  +
                        lda  #0
                        beq  ++
+                       lda  #1                        
+""")
                else
                    asmgen.out("""
                        cmp  $varname
                        bcc  +
                        beq  +
                        lda  #0
                        beq  ++
+                       lda  #1
+""")
            }
            else -> {
                // note: left and right operands get reversed here to reduce code size
                asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
                if(signed)
                    asmgen.out("""
                        sec
                        sbc  P8ZP_SCRATCH_B1
                        bvc  +
                        eor  #$80
+                       bmi  +
                        lda  #1
                        bne  ++
+                       lda  #0                        
+""")
                else
                    asmgen.out("""
                        cmp  P8ZP_SCRATCH_B1
                        lda  #0
                        rol  a""")
            }
        }
    }

    private fun byteGreater(expr: PtBinaryExpression, signed: Boolean) {
        // note: this is the inverse of byteLessEqual
        when(expr.right) {
            is PtNumber -> {
                val number = (expr.right as PtNumber).number.toInt()
                asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
                if(signed)
                    asmgen.out("""
                        sec
                        sbc  #$number
                        beq  +++
                        bvc  +
                        eor  #$80
+                       bmi  +
                        lda  #1
                        bne  ++
+                       lda  #0                        
+""")
                else
                    asmgen.out("""
                        cmp  #$number
                        bcc  +
                        beq  +
                        lda  #1
                        bne  ++
+                       lda  #0
+""")
            }
            is PtIdentifier -> {
                val varname = (expr.right as PtIdentifier).name
                asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
                if(signed)
                    asmgen.out("""
                        sec
                        sbc  $varname
                        beq  +++
                        bvc  +
                        eor  #$80
+                       bmi  +
                        lda  #1
                        bne  ++
+                       lda  #0                        
+""")
                else
                    asmgen.out("""
                        cmp  $varname
                        bcc  +
                        beq  +
                        lda  #1
                        bne  ++
+                       lda  #0
+""")
            }
            else -> {
                // note: left and right operands get reversed here to reduce code size
                asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
                if(signed)
                    asmgen.out("""
                        sec
                        sbc  P8ZP_SCRATCH_B1
                        bvc  +
                        eor  #$80
+                       bmi  +
                        lda  #0
                        beq  ++
+                       lda  #1                        
+""")
                else
                    asmgen.out("""
                        cmp  P8ZP_SCRATCH_B1
                        lda  #0
                        rol  a
                        eor  #1""")
            }
        }
    }

    private fun byteGreaterEquals(expr: PtBinaryExpression, signed: Boolean) {
        // note: this is the inverse of byteLess
        when(expr.right) {
            is PtNumber -> {
                val number = (expr.right as PtNumber).number.toInt()
                asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
                if(signed) {
                    asmgen.out("""
                        sec
                        sbc  #$number
                        bvs  +
                        eor  #$80
+                       asl  a
                        rol  a
                        and  #1""")
                }
                else
                    asmgen.out("""
                        cmp  #$number
                        rol  a
                        and  #1""")
            }
            is PtIdentifier -> {
                val varname = (expr.right as PtIdentifier).name
                asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
                if(signed) {
                    asmgen.out("""
                        sec
                        sbc  $varname
                        bvs  +
                        eor  #$80
+                       asl  a
                        rol  a
                        and  #1""")
                }
                else
                    asmgen.out("""
                        cmp  $varname
                        rol  a
                        and  #1""")
            }
            else -> {
                // note: left and right operands get reversed here to reduce code size
                asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
                if(signed)
                    asmgen.out("""
                        clc
                        sbc  P8ZP_SCRATCH_B1
                        bvs  +
                        eor  #$80
+                       asl  a
                        rol  a
                        and  #1
                        eor  #1""")
                else
                    asmgen.out("""
                        cmp  P8ZP_SCRATCH_B1
                        bcc  +
                        beq  +
                        lda  #0
                        beq  ++
+                       lda  #1
+""")
            }
        }
    }

    private fun assignOptimizedComparisonWords(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
        val signed = expr.left.type == DataType.WORD || expr.right.type ==  DataType.WORD

        when(expr.operator) {
            "==" -> {
                if(expr.left is PtIdentifier) {
                    val varName = asmgen.asmVariableName(expr.left as PtIdentifier)
                    asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.AY)
                    asmgen.out("""
                    cmp  $varName
                    bne  +
                    cpy  $varName+1
                    bne  +
                    lda  #1
                    bne  ++
+                   lda  #0
+""")
                } else {
                    asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
                    asmgen.out("""
                    cmp  P8ZP_SCRATCH_W1
                    bne  +
                    cpy  P8ZP_SCRATCH_W1+1
                    bne  +
                    lda  #1
                    bne  ++
+                   lda  #0
+""")
                }
            }
            "!=" -> {
                if(expr.left is PtIdentifier) {
                    val varName = asmgen.asmVariableName(expr.left as PtIdentifier)
                    asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.AY)
                    asmgen.out("""
                    cmp  $varName
                    bne  +
                    cpy  $varName+1
                    bne  +
                    lda  #0
                    beq  ++
+                   lda  #1
+""")
                } else {
                    asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
                    asmgen.out("""
                    cmp  P8ZP_SCRATCH_W1
                    bne  +
                    cpy  P8ZP_SCRATCH_W1+1
                    bne  +
                    lda  #0
                    beq  ++
+                   lda  #1
+""")
                }
            }
            "<" -> {
                if(expr.right is PtIdentifier) {
                    val varName = asmgen.asmVariableName(expr.right as PtIdentifier)
                    asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.AY)
                    if(signed)
                        asmgen.out("""
		cmp  $varName
        tya
		sbc  $varName+1
		bvc  +
		eor  #${'$'}80
+		bpl  ++
+       lda  #1
        bne  ++
+       lda  #0
+""")
                    else
                        asmgen.out("""
		cpy  $varName+1
		bcc  +
        bne  ++
		cmp  $varName
		bcs  ++
+       lda  #1
        bne  ++
+       lda  #0
+""")
                } else {
                    if(signed) {
                        asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
                        asmgen.out("""
		cmp  P8ZP_SCRATCH_W1
        tya
		sbc  P8ZP_SCRATCH_W1+1
		bvc  +
		eor  #${'$'}80
+		bpl  ++
+       lda  #1
        bne  ++
+       lda  #0
+""")
                    }
                    else {
                        asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
                        asmgen.out("""
		cpy  P8ZP_SCRATCH_W1+1
		bcc  +
        bne  ++
		cmp  P8ZP_SCRATCH_W1
		bcs  ++
+       lda  #1
        bne  ++
+       lda  #0
+""")
                    }
                }
            }
            "<=" -> {
                if(expr.left is PtIdentifier) {
                    val varName = asmgen.asmVariableName(expr.left as PtIdentifier)
                    asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.AY)
                    if(signed)
                        asmgen.out("""
		cmp  $varName
        tya
		sbc  $varName+1
		bvc  +
		eor  #${'$'}80
+		bmi  +
        lda  #1
        bne  ++
+       lda  #0
+""")
                    else
                        asmgen.out("""
		cpy  $varName+1
		bcc  ++
		bne  +
		cmp  $varName
		bcc  ++                        
+       lda  #1
        bne  ++
+       lda  #0
+""")
                } else {
                    if(signed) {
                        asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
                        asmgen.out("""
		cmp  P8ZP_SCRATCH_W1
        tya
		sbc  P8ZP_SCRATCH_W1+1
		bvc  +
		eor  #${'$'}80
+		bmi  +
        lda  #1
        bne  ++
+       lda  #0
+""")
                    }
                    else {
                        asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
                        asmgen.out("""
		cpy  P8ZP_SCRATCH_W1+1
		bcc  ++
		bne  +
		cmp  P8ZP_SCRATCH_W1
		bcc  ++                        
+       lda  #1
        bne  ++
+       lda  #0
+""")
                    }
                }
            }
            ">" -> {
                if(expr.left is PtIdentifier) {
                    val varName = asmgen.asmVariableName(expr.left as PtIdentifier)
                    asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.AY)
                    if(signed)
                        asmgen.out("""
		cmp  $varName
        tya
		sbc  $varName+1
		bvc  +
		eor  #${'$'}80
+		bpl  ++
+       lda  #1
        bne  ++
+       lda  #0
+""")
                    else
                        asmgen.out("""
		cpy  $varName+1
		bcc  +
        bne  ++
		cmp  $varName
		bcs  ++
+       lda  #1
        bne  ++
+       lda  #0
+""")
                } else {
                    if(signed) {
                        asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
                        asmgen.out("""
		cmp  P8ZP_SCRATCH_W1
        tya
		sbc  P8ZP_SCRATCH_W1+1
		bvc  +
		eor  #${'$'}80
+		bpl  ++
+       lda  #1
        bne  ++
+       lda  #0
+""")
                    }
                    else {
                        asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
                        asmgen.out("""
		cpy  P8ZP_SCRATCH_W1+1
		bcc  +
        bne  ++
		cmp  P8ZP_SCRATCH_W1
		bcs  ++
+       lda  #1
        bne  ++
+       lda  #0
+""")
                    }
                }
            }
            ">=" -> {
                if(expr.right is PtIdentifier) {
                    val varName = asmgen.asmVariableName(expr.right as PtIdentifier)
                    asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.AY)
                    if(signed)
                        asmgen.out("""
		cmp  $varName
        tya
		sbc  $varName+1
		bvc  +
		eor  #${'$'}80
+		bmi  +
        lda  #1
        bne  ++
+       lda  #0
+""")
                    else
                        asmgen.out("""
		cpy  $varName+1
		bcc  ++
		bne  +
		cmp  $varName
		bcc  ++                        
+       lda  #1
        bne  ++
+       lda  #0
+""")
                } else {
                    if(signed) {
                        asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
                        asmgen.out("""
		cmp  P8ZP_SCRATCH_W1
        tya
		sbc  P8ZP_SCRATCH_W1+1
		bvc  +
		eor  #${'$'}80
+		bmi  +
        lda  #1
        bne  ++
+       lda  #0
+""")
                    }
                    else {
                        asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
                        asmgen.out("""
		cpy  P8ZP_SCRATCH_W1+1
		bcc  ++
		bne  +
		cmp  P8ZP_SCRATCH_W1
		bcc  ++                        
+       lda  #1
        bne  ++
+       lda  #0
+""")
                    }
                }
            }
            else -> return false
        }

        assignRegisterByte(assign.target, CpuRegister.A, signed, true)
        return true
    }

    private fun assignLogicalWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
        assignExpressionToRegister(left, RegisterOrPair.A, false)
        val operand = when(right) {
            is PtNumber -> "#${right.number.toHex()}"
            is PtIdentifier -> asmgen.asmSymbolName(right)
            else -> throw AssemblyError("wrong right operand type")
        }
        when (operator) {
            "&", "and" -> asmgen.out("  and  $operand")
            "|", "or" -> asmgen.out("  ora  $operand")
            "^", "xor" -> asmgen.out("  eor  $operand")
            else -> throw AssemblyError("invalid operator")
        }
        assignRegisterByte(target, CpuRegister.A, false, true)
    }

    private fun assignLogicalWithSimpleRightOperandWord(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
        assignExpressionToRegister(left, RegisterOrPair.AY, false)
        when(right) {
            is PtNumber -> {
                val number = right.number.toHex()
                when (operator) {
                    "&", "and" -> asmgen.out("  and  #<$number |  tax |  tya |  and  #>$number |  tay |  txa")
                    "|", "or" -> asmgen.out("  ora  #<$number |  tax |  tya |  ora  #>$number |  tay |  txa")
                    "^", "xor" -> asmgen.out("  eor  #<$number |  tax |  tya |  eor  #>$number |  tay |  txa")
                    else -> throw AssemblyError("invalid operator")
                }
            }
            is PtIdentifier -> {
                val name = asmgen.asmSymbolName(right)
                when (operator) {
                    "&", "and" -> asmgen.out("  and  $name |  tax |  tya |  and  $name+1 |  tay |  txa")
                    "|", "or" -> asmgen.out("  ora  $name |  tax |  tya |  ora  $name+1 |  tay |  txa")
                    "^", "xor" -> asmgen.out("  eor  $name |  tax |  tya |  eor  $name+1 |  tay |  txa")
                    else -> throw AssemblyError("invalid operator")
                }
            }
            else -> throw AssemblyError("wrong right operand type")
        }
        assignRegisterpairWord(target, RegisterOrPair.AY)
    }

    private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
        when (expr.operator) {
            "==" -> {
                when(val dt = expr.left.type) {
                    in ByteDatatypes -> {
                        assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
                        asmgen.out("""
                            beq  +
                            lda  #1
+                           eor  #1""")
                        assignRegisterByte(assign.target, CpuRegister.A, false, false)
                        return true
                    }
                    in WordDatatypes -> {
                        assignExpressionToRegister(expr.left, RegisterOrPair.AY, dt==DataType.WORD)
                        asmgen.out("""
                            sty  P8ZP_SCRATCH_B1
                            ora  P8ZP_SCRATCH_B1
                            beq  +
                            lda  #1
+                           eor  #1""")
                        assignRegisterByte(assign.target, CpuRegister.A, false, false)
                        return true
                    }
                    DataType.FLOAT -> {
                        assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
                        asmgen.out("  jsr  floats.SIGN |  and  #1 |  eor  #1")
                        assignRegisterByte(assign.target, CpuRegister.A, false, false)
                        return true
                    }
                    else->{
                        return false
                    }
                }
            }
            "!=" -> {
                when(val dt = expr.left.type) {
                    in ByteDatatypes -> {
                        assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
                        asmgen.out("  beq  + |  lda  #1")
                        asmgen.out("+")
                        assignRegisterByte(assign.target, CpuRegister.A, false, false)
                        return true
                    }
                    in WordDatatypes -> {
                        assignExpressionToRegister(expr.left, RegisterOrPair.AY, dt==DataType.WORD)
                        asmgen.out("  sty  P8ZP_SCRATCH_B1 |  ora  P8ZP_SCRATCH_B1")
                        asmgen.out("  beq  + |  lda  #1")
                        asmgen.out("+")
                        assignRegisterByte(assign.target, CpuRegister.A, false, false)
                        return true
                    }
                    DataType.FLOAT -> {
                        assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
                        asmgen.out("  jsr  floats.SIGN")
                        assignRegisterByte(assign.target, CpuRegister.A, true, false)
                        return true
                    }
                    else->{
                        return false
                    }
                }
            }
            else -> return false
        }
    }

    private fun containmentCheckIntoA(containment: PtContainmentCheck) {
        val elementDt = containment.element.type
        val symbol = asmgen.symbolTable.lookup(containment.iterable.name)
        val variable = symbol!!.astNode as IPtVariable
        val varname = asmgen.asmVariableName(containment.iterable)
        val numElements = when(variable) {
            is PtConstant -> null
            is PtMemMapped -> variable.arraySize
            is PtVariable -> variable.arraySize
        }
        when(variable.type) {
            DataType.STR -> {
                // use subroutine
                assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
                asmgen.saveRegisterStack(CpuRegister.A, true)
                assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position,"P8ZP_SCRATCH_W1"), varname, null, null)
                asmgen.restoreRegisterStack(CpuRegister.A, false)
                val stringVal = (variable as PtVariable).value as PtString
                asmgen.out("  ldy  #${stringVal.value.length}")
                asmgen.out("  jsr  prog8_lib.containment_bytearray")
                return
            }
            DataType.ARRAY_F -> {
                throw AssemblyError("containment check of floats not supported")
            }
            DataType.ARRAY_B, DataType.ARRAY_UB -> {
                assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
                asmgen.saveRegisterStack(CpuRegister.A, true)
                assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W1"), varname, null, null)
                asmgen.restoreRegisterStack(CpuRegister.A, false)
                asmgen.out("  ldy  #$numElements")
                asmgen.out("  jsr  prog8_lib.containment_bytearray")
                return
            }
            DataType.ARRAY_W, DataType.ARRAY_UW -> {
                assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt)
                assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W2"), varname, null, null)
                asmgen.out("  ldy  #$numElements")
                asmgen.out("  jsr  prog8_lib.containment_wordarray")
                return
            }
            else -> throw AssemblyError("invalid dt")
        }
    }

    private fun assignStatusFlagByte(target: AsmAssignTarget, statusflag: Statusflag) {
        when(statusflag) {
            Statusflag.Pc -> {
                asmgen.out("  lda  #0 |  rol  a")
            }
            Statusflag.Pv -> {
                asmgen.out("""
                    bvs  +
                    lda  #0
                    beq  ++
+                   lda  #1
+""")
            }
            else -> throw AssemblyError("can't use Z or N flags as return 'values'")
        }
        assignRegisterByte(target, CpuRegister.A, false, true)
    }

    private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: PtExpression, origTypeCastExpression: PtTypeCast) {
        val valueDt = value.type
        if(valueDt==targetDt)
            throw AssemblyError("type cast to identical dt should have been removed")

        when(value) {
            is PtIdentifier -> {
                if(targetDt in WordDatatypes) {
                    if(valueDt==DataType.UBYTE) {
                        assignVariableUByteIntoWord(target, value)
                        return
                    }
                    if(valueDt==DataType.BYTE) {
                        assignVariableByteIntoWord(target, value)
                        return
                    }
                }
            }
            is PtMemoryByte -> {
                if(targetDt in WordDatatypes) {

                    fun assignViaExprEval(addressExpression: PtExpression) {
                        asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD)
                        asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
                        asmgen.out("  ldy  #0")
                        assignRegisterpairWord(target, RegisterOrPair.AY)
                    }

                    when (value.address) {
                        is PtNumber -> {
                            val address = (value.address as PtNumber).number.toUInt()
                            assignMemoryByteIntoWord(target, address, null)
                        }
                        is PtIdentifier -> {
                            assignMemoryByteIntoWord(target, null, value.address as PtIdentifier)
                        }
                        is PtBinaryExpression -> {
                            val addrExpr = value.address as PtBinaryExpression
                            if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
                                asmgen.out("  ldy  #0")
                                assignRegisterpairWord(target, RegisterOrPair.AY)
                            } else {
                                assignViaExprEval(value.address)
                            }
                        }
                        else -> {
                            assignViaExprEval(value.address)
                        }
                    }
                    return
                }
            }
            is PtNumber -> throw AssemblyError("a cast of a literal value should have been const-folded away")
            is PtArrayIndexer -> {
                if(targetDt in ByteDatatypes && valueDt in WordDatatypes) {
                    // just assign the lsb from the array value
                    return assignCastViaLsbFunc(value, target)
                }
            }
            else -> {}
        }


        // special case optimizations
        if(target.kind == TargetStorageKind.VARIABLE) {
            if(value is PtIdentifier && valueDt != DataType.UNDEFINED)
                return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt)

            when (valueDt) {
                in ByteDatatypes -> {
                    assignExpressionToRegister(value, RegisterOrPair.A, valueDt==DataType.BYTE)
                    assignTypeCastedRegisters(target.asmVarname, targetDt, RegisterOrPair.A, valueDt)
                }
                in WordDatatypes -> {
                    assignExpressionToRegister(value, RegisterOrPair.AY, valueDt==DataType.WORD)
                    assignTypeCastedRegisters(target.asmVarname, targetDt, RegisterOrPair.AY, valueDt)
                }
                DataType.FLOAT -> {
                    assignExpressionToRegister(value, RegisterOrPair.FAC1, true)
                    assignTypeCastedFloatFAC1(target.asmVarname, targetDt)
                }
                in PassByReferenceDatatypes -> {
                    // str/array value cast (most likely to UWORD, take address-of)
                    assignExpressionToVariable(value, target.asmVarname, targetDt)
                }
                else -> throw AssemblyError("strange dt in typecast assign to var: $valueDt  -->  $targetDt")
            }
            return
        }

        if(valueDt in WordDatatypes && origTypeCastExpression.type == DataType.UBYTE) {
            val parentTc = origTypeCastExpression.parent as? PtTypeCast
            if(parentTc!=null && parentTc.type==DataType.UWORD) {
                // typecast a word value to ubyte and directly back to uword
                // generate code for lsb(value) here instead of the ubyte typecast
                return assignCastViaLsbFunc(value, target)
            }
        }

        if(valueDt==DataType.UBYTE || valueDt==DataType.BOOL) {
            when(target.register) {
                RegisterOrPair.A,
                RegisterOrPair.X,
                RegisterOrPair.Y -> {
                    // 'cast' an ubyte value to a byte register; no cast needed at all
                    return assignExpressionToRegister(value, target.register, false)
                }
                RegisterOrPair.AX,
                RegisterOrPair.AY,
                RegisterOrPair.XY,
                in Cx16VirtualRegisters -> {
                    assignExpressionToRegister(value, RegisterOrPair.A, false)
                    assignRegisterByte(target, CpuRegister.A, false, true)
                    return
                }
                else -> {}
            }
        } else if(valueDt==DataType.UWORD) {
            when(target.register) {
                RegisterOrPair.A,
                RegisterOrPair.X,
                RegisterOrPair.Y -> {
                    // cast an uword to a byte register, do this via lsb(value)
                    // generate code for lsb(value) here instead of the ubyte typecast
                    return assignCastViaLsbFunc(value, target)
                }
                RegisterOrPair.AX,
                RegisterOrPair.AY,
                RegisterOrPair.XY,
                in Cx16VirtualRegisters -> {
                    // 'cast' uword into a 16 bits register, just assign it
                    return assignExpressionToRegister(value, target.register!!, false)
                }
                else -> {}
            }
        }

        if(target.kind==TargetStorageKind.REGISTER) {
            if(valueDt==DataType.FLOAT && target.datatype!=DataType.FLOAT) {
                // have to typecast the float number on the fly down to an integer
                assignExpressionToRegister(value, RegisterOrPair.FAC1, target.datatype in SignedDatatypes)
                assignTypeCastedFloatFAC1("P8ZP_SCRATCH_W1", target.datatype)
                assignVariableToRegister("P8ZP_SCRATCH_W1", target.register!!, target.datatype in SignedDatatypes, origTypeCastExpression.definingISub(), target.position)
                return
            } else {
                if(!(valueDt isAssignableTo targetDt)) {
                    return if(valueDt in WordDatatypes && targetDt in ByteDatatypes) {
                        // word to byte, just take the lsb
                        assignCastViaLsbFunc(value, target)
                    } else if(valueDt in WordDatatypes && targetDt in WordDatatypes) {
                        // word to word, just assign
                        assignExpressionToRegister(value, target.register!!, valueDt in SignedDatatypes)
                    } else if(valueDt in ByteDatatypes && targetDt in ByteDatatypes) {
                        // byte to byte, just assign
                        assignExpressionToRegister(value, target.register!!, valueDt in SignedDatatypes)
                    } else if(valueDt in ByteDatatypes && targetDt in WordDatatypes) {
                        // byte to word, just assign
                        assignExpressionToRegister(value, target.register!!, valueDt==DataType.WORD)
                    } else
                        throw AssemblyError("can't cast $valueDt to $targetDt, this should have been checked in the astchecker")
                }
            }
        }

        if(targetDt in IntegerDatatypes && valueDt in IntegerDatatypes && valueDt!=targetDt && valueDt.isAssignableTo(targetDt)) {
            require(targetDt in WordDatatypes && valueDt in ByteDatatypes) {
                "should be byte to word assignment ${origTypeCastExpression.position}"
            }
            when(target.kind) {
//                TargetStorageKind.VARIABLE -> {
//                    This has been handled already earlier on line 961.
//                    // byte to word, just assign to registers first, then assign to variable
//                    assignExpressionToRegister(value, RegisterOrPair.AY, targetDt==DataType.WORD)
//                    assignTypeCastedRegisters(target.asmVarname, targetDt, RegisterOrPair.AY, targetDt)
//                    return
//                }
                TargetStorageKind.ARRAY -> {
                    // byte to word, just assign to registers first, then assign into array
                    assignExpressionToRegister(value, RegisterOrPair.AY, targetDt==DataType.WORD)
                    assignRegisterpairWord(target, RegisterOrPair.AY)
                    return
                }
                TargetStorageKind.REGISTER -> {
                    // byte to word, just assign to registers
                    assignExpressionToRegister(value, target.register!!, targetDt==DataType.WORD)
                    return
                }
                else -> throw AssemblyError("weird target")
            }
        }

        if(targetDt==DataType.FLOAT && (target.register==RegisterOrPair.FAC1 || target.register==RegisterOrPair.FAC2)) {
            if(target.register==RegisterOrPair.FAC2)
                asmgen.pushFAC1()
            when(valueDt) {
                DataType.UBYTE -> {
                    assignExpressionToRegister(value, RegisterOrPair.Y, false)
                    asmgen.out("  jsr  floats.FREADUY")
                }
                DataType.BYTE -> {
                    assignExpressionToRegister(value, RegisterOrPair.A, true)
                    asmgen.out("  jsr  floats.FREADSA")
                }
                DataType.UWORD -> {
                    assignExpressionToRegister(value, RegisterOrPair.AY, false)
                    asmgen.out("  jsr  floats.GIVUAYFAY")
                }
                DataType.WORD -> {
                    assignExpressionToRegister(value, RegisterOrPair.AY, true)
                    asmgen.out("  jsr  floats.GIVAYFAY")
                }
                else -> throw AssemblyError("invalid dt")
            }
            if(target.register==RegisterOrPair.FAC2) {
                asmgen.out("  jsr  floats.MOVEF")
                asmgen.popFAC1()
            }
            return
        }

        // No more special optimized cases yet. Do the rest via more complex evaluation
        // note: cannot use assignTypeCastedValue because that is ourselves :P
        // NOTE: THIS MAY TURN INTO A STACK OVERFLOW ERROR IF IT CAN'T SIMPLIFY THE TYPECAST..... :-/
        asmgen.assignExpressionTo(origTypeCastExpression, target)
    }

    private fun assignCastViaLsbFunc(value: PtExpression, target: AsmAssignTarget) {
        val lsb = PtBuiltinFunctionCall("lsb", false, true, DataType.UBYTE, value.position)
        lsb.parent = value.parent
        lsb.add(value)
        val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb)
        val assign = AsmAssignment(src, target, program.memsizer, value.position)
        translateNormalAssignment(assign, value.definingISub())
    }

    private fun assignTypeCastedFloatFAC1(targetAsmVarName: String, targetDt: DataType) {

        if(targetDt==DataType.FLOAT)
            throw AssemblyError("typecast to identical type")

        when(targetDt) {
            DataType.UBYTE -> asmgen.out("  jsr  floats.cast_FAC1_as_uw_into_ya |  sty  $targetAsmVarName")
            DataType.BYTE -> asmgen.out("  jsr  floats.cast_FAC1_as_w_into_ay |  sta  $targetAsmVarName")
            DataType.UWORD -> asmgen.out("  jsr  floats.cast_FAC1_as_uw_into_ya |  sty  $targetAsmVarName |  sta  $targetAsmVarName+1")
            DataType.WORD -> asmgen.out("  jsr  floats.cast_FAC1_as_w_into_ay |  sta  $targetAsmVarName |  sty  $targetAsmVarName+1")
            else -> throw AssemblyError("weird type")
        }
    }


    private fun assignTypeCastedIdentifier(targetAsmVarName: String, targetDt: DataType,
                                           sourceAsmVarName: String, sourceDt: DataType) {
        if(sourceDt == targetDt)
            throw AssemblyError("typecast to identical type")

        // also see: PtExpressionAsmGen,   fun translateExpression(typecast: PtTypeCast)
        when(sourceDt) {
            DataType.UBYTE -> {
                when(targetDt) {
                    DataType.BYTE -> {
                        asmgen.out("  lda  $sourceAsmVarName |  sta  $targetAsmVarName")
                    }
                    DataType.UWORD, DataType.WORD -> {
                        if(asmgen.isTargetCpu(CpuType.CPU65c02))
                            asmgen.out("  lda  $sourceAsmVarName |  sta  $targetAsmVarName |  stz  $targetAsmVarName+1")
                        else
                            asmgen.out("  lda  $sourceAsmVarName |  sta  $targetAsmVarName |  lda  #0  |  sta  $targetAsmVarName+1")
                    }
                    DataType.FLOAT -> {
                        asmgen.out("""
                            lda  #<$targetAsmVarName
                            ldy  #>$targetAsmVarName
                            sta  P8ZP_SCRATCH_W2
                            sty  P8ZP_SCRATCH_W2+1
                            ldy  $sourceAsmVarName
                            jsr  floats.cast_from_ub""")
                    }
                    else -> throw AssemblyError("weird type")
                }
            }
            DataType.BYTE -> {
                when(targetDt) {
                    DataType.UBYTE -> {
                        asmgen.out("  lda  $sourceAsmVarName |  sta  $targetAsmVarName")
                    }
                    DataType.UWORD -> {
                        if(asmgen.isTargetCpu(CpuType.CPU65c02))
                            asmgen.out("  lda  $sourceAsmVarName |  sta  $targetAsmVarName |  stz  $targetAsmVarName+1")
                        else
                            asmgen.out("  lda  $sourceAsmVarName |  sta  $targetAsmVarName |  lda  #0  |  sta  $targetAsmVarName+1")
                    }
                    DataType.WORD -> {
                        asmgen.out("  lda  $sourceAsmVarName |  sta  $targetAsmVarName")
                        asmgen.signExtendVariableLsb(targetAsmVarName, DataType.BYTE)
                    }
                    DataType.FLOAT -> {
                        asmgen.out("""
                            lda  #<$targetAsmVarName
                            ldy  #>$targetAsmVarName
                            sta  P8ZP_SCRATCH_W2
                            sty  P8ZP_SCRATCH_W2+1
                            lda  $sourceAsmVarName
                            jsr  floats.cast_from_b""")
                    }
                    else -> throw AssemblyError("weird type")
                }
            }
            DataType.UWORD -> {
                when(targetDt) {
                    DataType.BYTE, DataType.UBYTE -> {
                        asmgen.out("  lda  $sourceAsmVarName |  sta  $targetAsmVarName")
                    }
                    DataType.WORD -> {
                        asmgen.out("  lda  $sourceAsmVarName |  sta  $targetAsmVarName |  lda  $sourceAsmVarName+1 |  sta  $targetAsmVarName+1")
                    }
                    DataType.FLOAT -> {
                        asmgen.out("""
                            lda  #<$targetAsmVarName
                            ldy  #>$targetAsmVarName
                            sta  P8ZP_SCRATCH_W2
                            sty  P8ZP_SCRATCH_W2+1
                            lda  $sourceAsmVarName
                            ldy  $sourceAsmVarName+1
                            jsr  floats.cast_from_uw""")
                    }
                    else -> throw AssemblyError("weird type")
                }
            }
            DataType.WORD -> {
                when(targetDt) {
                    DataType.BYTE, DataType.UBYTE -> {
                        asmgen.out("  lda  $sourceAsmVarName |  sta  $targetAsmVarName")
                    }
                    DataType.UWORD -> {
                        asmgen.out("  lda  $sourceAsmVarName |  sta  $targetAsmVarName |  lda  $sourceAsmVarName+1 |  sta  $targetAsmVarName+1")
                    }
                    DataType.FLOAT -> {
                        asmgen.out("""
                            lda  #<$targetAsmVarName
                            ldy  #>$targetAsmVarName
                            sta  P8ZP_SCRATCH_W2
                            sty  P8ZP_SCRATCH_W2+1
                            lda  $sourceAsmVarName
                            ldy  $sourceAsmVarName+1
                            jsr  floats.cast_from_w""")
                    }
                    else -> throw AssemblyError("weird type")
                }
            }
            DataType.FLOAT -> {
                asmgen.out("  lda  #<$sourceAsmVarName |  ldy  #>$sourceAsmVarName")
                when(targetDt) {
                    DataType.UBYTE -> asmgen.out("  jsr  floats.cast_as_uw_into_ya |  sty  $targetAsmVarName")
                    DataType.BYTE -> asmgen.out("  jsr  floats.cast_as_w_into_ay |  sta  $targetAsmVarName")
                    DataType.UWORD -> asmgen.out("  jsr  floats.cast_as_uw_into_ya |  sty  $targetAsmVarName |  sta  $targetAsmVarName+1")
                    DataType.WORD -> asmgen.out("  jsr  floats.cast_as_w_into_ay |  sta  $targetAsmVarName |  sty  $targetAsmVarName+1")
                    else -> throw AssemblyError("weird type")
                }
            }
            DataType.STR -> throw AssemblyError("cannot typecast a string value")
            else -> throw AssemblyError("weird type")
        }
    }


    private fun assignTypeCastedRegisters(targetAsmVarName: String, targetDt: DataType,
                                          regs: RegisterOrPair, sourceDt: DataType) {
        if(sourceDt == targetDt)
            throw AssemblyError("typecast to identical type")

        // also see: PtExpressionAsmGen,   fun translateExpression(typecast: PtTypeCast)
        when(sourceDt) {
            DataType.UBYTE -> {
                when(targetDt) {
                    DataType.BYTE -> {
                        asmgen.out("  st${regs.toString().lowercase()}  $targetAsmVarName")
                    }
                    DataType.UWORD, DataType.WORD -> {
                        if(asmgen.isTargetCpu(CpuType.CPU65c02))
                            asmgen.out(
                                "  st${regs.toString().lowercase()}  $targetAsmVarName |  stz  $targetAsmVarName+1")
                        else
                            asmgen.out(
                                "  st${regs.toString().lowercase()}  $targetAsmVarName |  lda  #0  |  sta  $targetAsmVarName+1")
                    }
                    DataType.FLOAT -> {
                        when(regs) {
                            RegisterOrPair.A -> asmgen.out("  tay")
                            RegisterOrPair.X -> asmgen.out("  txa |  tay")
                            RegisterOrPair.Y -> {}
                            else -> throw AssemblyError("non-byte regs")
                        }
                        asmgen.out("""
                            lda  #<$targetAsmVarName
                            sta  P8ZP_SCRATCH_W2
                            lda  #>$targetAsmVarName
                            sta  P8ZP_SCRATCH_W2+1
                            jsr  floats.cast_from_ub""")
                    }
                    else -> throw AssemblyError("weird type")
                }
            }
            DataType.BYTE -> {
                when(targetDt) {
                    DataType.UBYTE -> {
                        asmgen.out("  st${regs.toString().lowercase()}  $targetAsmVarName")
                    }
                    DataType.UWORD -> {
                        if(asmgen.isTargetCpu(CpuType.CPU65c02))
                            asmgen.out(
                                "  st${
                                    regs.toString().lowercase()
                                }  $targetAsmVarName |  stz  $targetAsmVarName+1")
                        else
                            asmgen.out(
                                "  st${
                                    regs.toString().lowercase()
                                }  $targetAsmVarName |  lda  #0  |  sta  $targetAsmVarName+1")
                    }
                    DataType.WORD -> {
                        when(regs) {
                            RegisterOrPair.A -> {}
                            RegisterOrPair.X -> asmgen.out("  txa")
                            RegisterOrPair.Y -> asmgen.out("  tya")
                            else -> throw AssemblyError("non-byte regs")
                        }
                        asmgen.signExtendAYlsb(sourceDt)
                        asmgen.out("  sta  $targetAsmVarName |  sty  $targetAsmVarName+1")
                    }
                    DataType.FLOAT -> {
                        when(regs) {
                            RegisterOrPair.A -> {}
                            RegisterOrPair.X -> asmgen.out("  txa")
                            RegisterOrPair.Y -> asmgen.out("  tya")
                            else -> throw AssemblyError("non-byte regs")
                        }
                        asmgen.out("""
                            ldy  #<$targetAsmVarName
                            sty  P8ZP_SCRATCH_W2
                            ldy  #>$targetAsmVarName
                            sty  P8ZP_SCRATCH_W2+1
                            jsr  floats.cast_from_b""")
                    }
                    else -> throw AssemblyError("weird type")
                }
            }
            DataType.UWORD -> {
                when(targetDt) {
                    DataType.BYTE, DataType.UBYTE -> {
                        asmgen.out("  st${regs.toString().lowercase().first()}  $targetAsmVarName")
                    }
                    DataType.WORD -> {
                        when(regs) {
                            RegisterOrPair.AX -> asmgen.out("  sta  $targetAsmVarName |  stx  $targetAsmVarName+1")
                            RegisterOrPair.AY -> asmgen.out("  sta  $targetAsmVarName |  sty  $targetAsmVarName+1")
                            RegisterOrPair.XY -> asmgen.out("  stx  $targetAsmVarName |  sty  $targetAsmVarName+1")
                            else -> throw AssemblyError("non-word regs")
                        }
                    }
                    DataType.FLOAT -> {
                        if(regs!=RegisterOrPair.AY)
                            throw AssemblyError("only supports AY here")
                        asmgen.out("""
                            tax
                            lda  #<$targetAsmVarName
                            sta  P8ZP_SCRATCH_W2
                            lda  #>$targetAsmVarName
                            sta  P8ZP_SCRATCH_W2+1
                            txa
                            jsr  floats.cast_from_uw""")
                    }
                    else -> throw AssemblyError("weird type")
                }
            }
            DataType.WORD -> {
                when(targetDt) {
                    DataType.BYTE, DataType.UBYTE -> {
                        asmgen.out("  st${regs.toString().lowercase().first()}  $targetAsmVarName")
                    }
                    DataType.UWORD -> {
                        when(regs) {
                            RegisterOrPair.AX -> asmgen.out("  sta  $targetAsmVarName |  stx  $targetAsmVarName+1")
                            RegisterOrPair.AY -> asmgen.out("  sta  $targetAsmVarName |  sty  $targetAsmVarName+1")
                            RegisterOrPair.XY -> asmgen.out("  stx  $targetAsmVarName |  sty  $targetAsmVarName+1")
                            else -> throw AssemblyError("non-word regs")
                        }
                    }
                    DataType.FLOAT -> {
                        if(regs!=RegisterOrPair.AY)
                            throw AssemblyError("only supports AY here")
                        asmgen.out("""
                            tax
                            lda  #<$targetAsmVarName
                            sta  P8ZP_SCRATCH_W2
                            lda  #>$targetAsmVarName
                            sta  P8ZP_SCRATCH_W2+1
                            txa
                            jsr  floats.cast_from_w""")
                    }
                    else -> throw AssemblyError("weird type")
                }
            }
            DataType.STR -> throw AssemblyError("cannot typecast a string value")
            else -> throw AssemblyError("weird type")
        }
    }

    private fun assignAddressOf(target: AsmAssignTarget, sourceName: String, arrayDt: DataType?, arrayIndexExpr: PtExpression?) {
        val offset = if(arrayIndexExpr!=null) {
            val constIndex = arrayIndexExpr.asConstInteger()
            if(constIndex!=null) {
                if(arrayDt in SplitWordArrayTypes)
                    constIndex
                else
                    program.memsizer.memorySize(arrayDt!!, constIndex)  // add arrayIndexExpr * elementsize  to the address of the array variable.
            } else {
                val eltSize = program.memsizer.memorySize(ArrayToElementTypes.getValue(arrayDt!!))
                assignExpressionToVariable(arrayIndexExpr, "P8ZP_SCRATCH_W1", DataType.UWORD)
                when(eltSize) {
                    1 -> {}
                    2 -> {
                        if(arrayDt !in SplitWordArrayTypes)
                            asmgen.out("  asl  P8ZP_SCRATCH_W1 |  rol  P8ZP_SCRATCH_W1+1")
                    }
                    else -> TODO("address-of array element $sourceName size $eltSize with non-const index at ${target.position}")
                }
                asmgen.out("""
                    lda  #<$sourceName
                    clc
                    adc  P8ZP_SCRATCH_W1
                    tax
                    lda  #>$sourceName
                    adc  P8ZP_SCRATCH_W1+1
                    tay
                    txa""")
                assignRegisterpairWord(target, RegisterOrPair.AY)
                return
            }
        } else 0

        when(target.kind) {
            TargetStorageKind.VARIABLE -> {
                asmgen.out("""
                        lda  #<$sourceName+$offset
                        ldy  #>$sourceName+$offset
                        sta  ${target.asmVarname}
                        sty  ${target.asmVarname}+1
                    """)
            }
            TargetStorageKind.MEMORY -> {
                throw AssemblyError("can't store word into memory byte")
            }
            TargetStorageKind.ARRAY -> {
                asmgen.out("  lda  #<$sourceName+$offset |  ldy #>$sourceName+$offset")
                assignRegisterpairWord(target, RegisterOrPair.AY)
            }
            TargetStorageKind.REGISTER -> {
                when(target.register!!) {
                    RegisterOrPair.AX -> asmgen.out("  ldx  #>$sourceName+$offset |  lda  #<$sourceName+$offset")
                    RegisterOrPair.AY -> asmgen.out("  ldy  #>$sourceName+$offset |  lda  #<$sourceName+$offset")
                    RegisterOrPair.XY -> asmgen.out("  ldy  #>$sourceName+$offset |  ldx  #<$sourceName+$offset")
                    in Cx16VirtualRegisters -> {
                        asmgen.out(
                            """
                            lda  #<$sourceName+$offset
                            sta  cx16.${target.register.toString().lowercase()}
                            lda  #>$sourceName+$offset
                            sta  cx16.${target.register.toString().lowercase()}+1
                        """)
                    }
                    else -> throw AssemblyError("can't load address in a single 8-bit register")
                }
            }
        }
    }

    private fun assignVariableString(target: AsmAssignTarget, sourceName: String) {
        when(target.kind) {
            TargetStorageKind.VARIABLE -> {
                when(target.datatype) {
                    DataType.UWORD -> {
                        asmgen.out("""
                            lda  #<$sourceName
                            ldy  #>$sourceName
                            sta  ${target.asmVarname}
                            sty  ${target.asmVarname}+1
                        """)
                    }
                    DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
                        asmgen.out("""
                            lda  #<${target.asmVarname}
                            ldy  #>${target.asmVarname}
                            sta  P8ZP_SCRATCH_W1
                            sty  P8ZP_SCRATCH_W1+1
                            lda  #<$sourceName
                            ldy  #>$sourceName
                            jsr  prog8_lib.strcpy""")
                    }
                    else -> throw AssemblyError("assign string to incompatible variable type")
                }
            }
            else -> throw AssemblyError("string-assign to weird target")
        }
    }

    private fun assignVariableWord(target: AsmAssignTarget, sourceName: String, sourceDt: DataType) {
        if(sourceDt==DataType.BYTE) {
            // need to sign extend
            asmgen.out("  lda  $sourceName")
            asmgen.signExtendAYlsb(DataType.BYTE)
            assignRegisterpairWord(target, RegisterOrPair.AY)
            return
        }
        require(sourceDt in WordDatatypes || sourceDt==DataType.UBYTE)
        when(target.kind) {
            TargetStorageKind.VARIABLE -> {
                if(sourceDt==DataType.UBYTE) {
                    asmgen.out("  lda  $sourceName |  sta  ${target.asmVarname}")
                    if(asmgen.isTargetCpu(CpuType.CPU65c02))
                        asmgen.out("  stz  ${target.asmVarname}+1")
                    else
                        asmgen.out("  lda  #0 |  sta  ${target.asmVarname}+1")
                }
                else
                    asmgen.out("""
                        lda  $sourceName
                        ldy  $sourceName+1
                        sta  ${target.asmVarname}
                        sty  ${target.asmVarname}+1""")
            }
            TargetStorageKind.MEMORY -> {
                throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast")
            }
            TargetStorageKind.ARRAY -> {
                if(sourceDt==DataType.UBYTE) TODO("assign byte to word array")
                target.array!!
                if(target.constArrayIndexValue!=null) {
                    val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt()
                    when(target.datatype) {
                        in ByteDatatypes -> {
                            asmgen.out(" lda  $sourceName  | sta  ${target.asmVarname}+$scaledIdx")
                        }
                        in WordDatatypes -> {
                            if(target.array.splitWords)
                                asmgen.out("""
                                    lda  $sourceName
                                    sta  ${target.asmVarname}_lsb+${target.constArrayIndexValue}
                                    lda  $sourceName+1
                                    sta  ${target.asmVarname}_msb+${target.constArrayIndexValue}""")
                            else
                                asmgen.out("""
                                    lda  $sourceName
                                    sta  ${target.asmVarname}+$scaledIdx
                                    lda  $sourceName+1
                                    sta  ${target.asmVarname}+$scaledIdx+1""")
                        }
                        else -> throw AssemblyError("weird target variable type ${target.datatype}")
                    }
                }
                else
                {
                    when(target.datatype) {
                        DataType.UBYTE, DataType.BYTE -> {
                            asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
                            asmgen.out(" lda  $sourceName |  sta  ${target.asmVarname},y")
                        }
                        DataType.UWORD, DataType.WORD -> {
                            asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
                            if(target.array.splitWords)
                                asmgen.out("""
                                    lda  $sourceName
                                    sta  ${target.asmVarname}_lsb,y
                                    lda  $sourceName+1
                                    sta  ${target.asmVarname}_msb,y""")
                            else
                                asmgen.out("""
                                    lda  $sourceName
                                    sta  ${target.asmVarname},y
                                    lda  $sourceName+1
                                    sta  ${target.asmVarname}+1,y""")
                        }
                        else -> throw AssemblyError("weird dt")
                    }
                }
            }
            TargetStorageKind.REGISTER -> {
                if(sourceDt==DataType.UBYTE) {
                    when(target.register!!) {
                        RegisterOrPair.AX -> asmgen.out("  ldx  #0 |  lda  $sourceName")
                        RegisterOrPair.AY -> asmgen.out("  ldy  #0 |  lda  $sourceName")
                        RegisterOrPair.XY -> asmgen.out("  ldy  #0 |  ldx  $sourceName")
                        in Cx16VirtualRegisters -> {
                            asmgen.out("  lda  $sourceName |  sta  cx16.${target.register.toString().lowercase()}")
                            if(asmgen.isTargetCpu(CpuType.CPU65c02))
                                asmgen.out("  stz  cx16.${target.register.toString().lowercase()}+1")
                            else
                                asmgen.out("  lda  #0 |  sta  cx16.${target.register.toString().lowercase()}+1")
                        }
                        else -> throw AssemblyError("can't load word in a single 8-bit register")
                    }
                } else {
                    when(target.register!!) {
                        RegisterOrPair.AX -> asmgen.out("  ldx  $sourceName+1 |  lda  $sourceName")
                        RegisterOrPair.AY -> asmgen.out("  ldy  $sourceName+1 |  lda  $sourceName")
                        RegisterOrPair.XY -> asmgen.out("  ldy  $sourceName+1 |  ldx  $sourceName")
                        in Cx16VirtualRegisters -> {
                            asmgen.out(
                                """
                                lda  $sourceName
                                sta  cx16.${target.register.toString().lowercase()}
                                lda  $sourceName+1
                                sta  cx16.${target.register.toString().lowercase()}+1
                            """)
                        }
                        else -> throw AssemblyError("can't load word in a single 8-bit register")
                    }
                }
            }
        }
    }

    internal fun assignFAC2float(target: AsmAssignTarget) {
        asmgen.out("  jsr  floats.MOVFA")
        if(target.register != RegisterOrPair.FAC1)
            assignFAC1float(target)
    }

    internal fun assignFAC1float(target: AsmAssignTarget) {
        when(target.kind) {
            TargetStorageKind.VARIABLE -> {
                asmgen.out("""
                    ldx  #<${target.asmVarname}
                    ldy  #>${target.asmVarname}
                    jsr  floats.MOVMF
                """)
            }
            TargetStorageKind.ARRAY -> {
                asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
                asmgen.out("""
                    ldy  #<${target.asmVarname} 
                    sty  P8ZP_SCRATCH_W1
                    ldy  #>${target.asmVarname}
                    sty  P8ZP_SCRATCH_W1+1
                    jsr  floats.set_array_float_from_fac1""")
            }
            TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
            TargetStorageKind.REGISTER -> {
                if(target.register==RegisterOrPair.FAC2)
                    asmgen.out("  jsr  floats.MOVAF")
                else if (target.register!! != RegisterOrPair.FAC1)
                    throw AssemblyError("can't assign Fac1 float to another register")
            }
        }
    }

    private fun assignFloatFromAY(target: AsmAssignTarget) {
        when(target.kind) {
            TargetStorageKind.VARIABLE -> {
                asmgen.out("""
                    sta  P8ZP_SCRATCH_W1                    
                    sty  P8ZP_SCRATCH_W1+1
                    lda  #<${target.asmVarname} 
                    ldy  #>${target.asmVarname}
                    jsr  floats.copy_float""")
            }
            TargetStorageKind.ARRAY -> {
                asmgen.saveRegisterStack(CpuRegister.A, false)
                asmgen.saveRegisterStack(CpuRegister.Y, false)
                asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
                asmgen.restoreRegisterStack(CpuRegister.Y, false)
                asmgen.restoreRegisterStack(CpuRegister.A, false)
                asmgen.out("""
                    sta  P8ZP_SCRATCH_W1
                    sty  P8ZP_SCRATCH_W1+1
                    lda  #<${target.asmVarname} 
                    ldy  #>${target.asmVarname}
                    sta  P8ZP_SCRATCH_W2
                    sty  P8ZP_SCRATCH_W2+1
                    jsr  floats.set_array_float""")
            }
            TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
            TargetStorageKind.REGISTER -> {
                when(target.register!!) {
                    RegisterOrPair.FAC1 -> asmgen.out("  jsr  floats.MOVFM")
                    RegisterOrPair.FAC2 -> asmgen.out("  jsr  floats.CONUPK")
                    else -> throw AssemblyError("can only assign float to Fac1 or 2")
                }
            }
        }
    }

    private fun assignVariableFloat(target: AsmAssignTarget, sourceName: String) {
        when(target.kind) {
            TargetStorageKind.VARIABLE -> {
                asmgen.out("""
                    lda  #<$sourceName
                    ldy  #>$sourceName
                    sta  P8ZP_SCRATCH_W1
                    sty  P8ZP_SCRATCH_W1+1
                    lda  #<${target.asmVarname}
                    ldy  #>${target.asmVarname}
                    jsr  floats.copy_float""")
            }
            TargetStorageKind.ARRAY -> {
                asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
                asmgen.out("""
                    ldy  #<$sourceName
                    sty  P8ZP_SCRATCH_W1
                    ldy  #>$sourceName
                    sty  P8ZP_SCRATCH_W1+1
                    ldy  #<${target.asmVarname} 
                    sty  P8ZP_SCRATCH_W2
                    ldy  #>${target.asmVarname}
                    sty  P8ZP_SCRATCH_W2+1
                    jsr  floats.set_array_float""")
            }
            TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
            TargetStorageKind.REGISTER -> {
                when(target.register!!) {
                    RegisterOrPair.FAC1 -> asmgen.out("  lda  #<$sourceName  | ldy  #>$sourceName |  jsr  floats.MOVFM")
                    RegisterOrPair.FAC2 -> asmgen.out("  lda  #<$sourceName  | ldy  #>$sourceName |  jsr  floats.CONUPK")
                    else -> throw AssemblyError("can only assign float to Fac1 or 2")
                }
            }
        }
    }

    private fun assignVariableByte(target: AsmAssignTarget, sourceName: String) {
        when(target.kind) {
            TargetStorageKind.VARIABLE -> {
                asmgen.out("""
                    lda  $sourceName
                    sta  ${target.asmVarname}
                    """)
            }
            TargetStorageKind.MEMORY -> {
                asmgen.out("  lda  $sourceName")
                storeRegisterAInMemoryAddress(target.memory!!)
            }
            TargetStorageKind.ARRAY -> {
                if(assignsIndexedPointerVar(target)) {
                    if (target.constArrayIndexValue==0u) {
                        asmgen.out("  lda  $sourceName")
                        asmgen.storeAIntoPointerVar(target.origAstTarget!!.array!!.variable)
                    } else {
                        asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
                        if (asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
                            asmgen.out("  lda  $sourceName |  sta  (${target.asmVarname}),y")
                        } else {
                            asmgen.out("  lda  ${target.asmVarname} |  sta  P8ZP_SCRATCH_W2 |  lda  ${target.asmVarname}+1 |  sta  P8ZP_SCRATCH_W2+1")
                            asmgen.out("  lda  $sourceName |  sta  (P8ZP_SCRATCH_W2),y")
                        }
                    }
                    return
                }
                if(target.array!!.splitWords)
                    TODO("assign into split words ${target.position}")
                if (target.constArrayIndexValue!=null) {
                    val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt()
                    asmgen.out(" lda  $sourceName  | sta  ${target.asmVarname}+$scaledIdx")
                }
                else {
                    asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
                    asmgen.out(" lda  $sourceName |  sta  ${target.asmVarname},y")
                }
            }
            TargetStorageKind.REGISTER -> {
                when(target.register!!) {
                    RegisterOrPair.A -> asmgen.out("  lda  $sourceName")
                    RegisterOrPair.X -> asmgen.out("  ldx  $sourceName")
                    RegisterOrPair.Y -> asmgen.out("  ldy  $sourceName")
                    RegisterOrPair.AX -> asmgen.out("  ldx  #0 |  lda  $sourceName")
                    RegisterOrPair.AY -> asmgen.out("  ldy  #0 |  lda  $sourceName")
                    RegisterOrPair.XY -> asmgen.out("  ldy  #0 |  ldx  $sourceName")
                    RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
                    in Cx16VirtualRegisters -> {
                        asmgen.out(
                            """
                            lda  $sourceName
                            sta  cx16.${target.register.toString().lowercase()}
                            lda  #0
                            sta  cx16.${target.register.toString().lowercase()}+1
                            """)
                    }
                    else -> throw AssemblyError("weird register")
                }
            }
        }
    }

    private fun assignVariableByteIntoWord(wordtarget: AsmAssignTarget, bytevar: PtIdentifier) {
        val sourceName = asmgen.asmVariableName(bytevar)
        when (wordtarget.kind) {
            TargetStorageKind.VARIABLE -> {
                asmgen.out("""
                    lda  $sourceName
                    sta  ${wordtarget.asmVarname}
                    ora  #$7f
                    bmi  +
                    lda  #0
+                   sta  ${wordtarget.asmVarname}+1
                    """)
            }
            TargetStorageKind.ARRAY -> {
                if(wordtarget.array!!.splitWords)
                    TODO("assign byte into split words ${wordtarget.position}")
                if (wordtarget.constArrayIndexValue!=null) {
                    val scaledIdx = wordtarget.constArrayIndexValue!! * 2u
                    asmgen.out("  lda  $sourceName")
                    asmgen.signExtendAYlsb(DataType.BYTE)
                    asmgen.out("  sta  ${wordtarget.asmVarname}+$scaledIdx |  sty  ${wordtarget.asmVarname}+$scaledIdx+1")
                }
                else {
                    asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.X)
                    asmgen.out("  lda  $sourceName")
                    asmgen.signExtendAYlsb(DataType.BYTE)
                    asmgen.out("  sta  ${wordtarget.asmVarname},x |  inx |  tya |  sta  ${wordtarget.asmVarname},x")
                }
            }
            TargetStorageKind.REGISTER -> {
                when(wordtarget.register!!) {
                    RegisterOrPair.AX -> asmgen.out("""
                        lda  $sourceName
                        pha
                        ora  #$7f
                        bmi  +
                        lda  #0
+                       tax
                        pla""")
                    RegisterOrPair.AY -> asmgen.out("""
                        lda  $sourceName
                        tax
                        ora  #$7f
                        bmi  +
                        lda  #0
+                       tay
                        txa""")
                    RegisterOrPair.XY -> asmgen.out("""
                        lda  $sourceName
                        tax 
                        ora  #$7f
                        bmi  +
                        lda  #0
+                       tay""")
                    in Cx16VirtualRegisters -> {
                        val regname = wordtarget.register.name.lowercase()
                        asmgen.out("""
                            lda  $sourceName
                            sta  cx16.$regname
                            ora  #$7f
                            bmi  +
                            lda  #0
+                           sta  cx16.$regname+1""")
                    }
                    else -> throw AssemblyError("only reg pairs allowed as word target ${wordtarget.register}")
                }
            }
            else -> throw AssemblyError("target type isn't word")
        }
    }

    private fun assignVariableUByteIntoWord(wordtarget: AsmAssignTarget, bytevar: PtIdentifier) {
        val sourceName = asmgen.asmVariableName(bytevar)
        when(wordtarget.kind) {
            TargetStorageKind.VARIABLE -> {
                asmgen.out("  lda  $sourceName |  sta  ${wordtarget.asmVarname}")
                if(asmgen.isTargetCpu(CpuType.CPU65c02))
                    asmgen.out("  stz  ${wordtarget.asmVarname}+1")
                else
                    asmgen.out("  lda  #0 |  sta  ${wordtarget.asmVarname}+1")
            }
            TargetStorageKind.ARRAY -> {
                if(wordtarget.array!!.splitWords) {
                    if (wordtarget.constArrayIndexValue!=null) {
                        val scaledIdx = wordtarget.constArrayIndexValue!!
                        asmgen.out("  lda  $sourceName  | sta  ${wordtarget.asmVarname}_lsb+$scaledIdx")
                        if(asmgen.isTargetCpu(CpuType.CPU65c02))
                            asmgen.out("  stz  ${wordtarget.asmVarname}_msb+$scaledIdx")
                        else
                            asmgen.out("  lda  #0  | sta  ${wordtarget.asmVarname}_msb+$scaledIdx")
                    }
                    else {
                        asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.Y)
                        asmgen.out("""
                            lda  $sourceName
                            sta  ${wordtarget.asmVarname}_lsb,y
                            lda  #0
                            sta  ${wordtarget.asmVarname}_msb,y""")
                    }
                } else {
                    if (wordtarget.constArrayIndexValue!=null) {
                        val scaledIdx = wordtarget.constArrayIndexValue!! * 2u
                        asmgen.out("  lda  $sourceName  | sta  ${wordtarget.asmVarname}+$scaledIdx")
                        if(asmgen.isTargetCpu(CpuType.CPU65c02))
                            asmgen.out("  stz  ${wordtarget.asmVarname}+$scaledIdx+1")
                        else
                            asmgen.out("  lda  #0  | sta  ${wordtarget.asmVarname}+$scaledIdx+1")
                    }
                    else {
                        asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.Y)
                        asmgen.out("""
                            lda  $sourceName
                            sta  ${wordtarget.asmVarname},y
                            iny
                            lda  #0
                            sta  ${wordtarget.asmVarname},y""")
                    }
                }
            }
            TargetStorageKind.REGISTER -> {
                when(wordtarget.register!!) {
                    RegisterOrPair.AX -> asmgen.out("  ldx  #0 |  lda  $sourceName")
                    RegisterOrPair.AY -> asmgen.out("  ldy  #0 |  lda  $sourceName")
                    RegisterOrPair.XY -> asmgen.out("  ldy  #0 |  ldx  $sourceName")
                    in Cx16VirtualRegisters -> {
                        val regname = wordtarget.register.name.lowercase()
                        if(asmgen.isTargetCpu(CpuType.CPU65c02))
                            asmgen.out("  lda  $sourceName |  sta  cx16.$regname |  stz  cx16.$regname+1")
                        else
                            asmgen.out("  lda  $sourceName |  sta  cx16.$regname |  lda  #0 |  sta  cx16.$regname+1")
                    }
                    else -> throw AssemblyError("only reg pairs allowed as word target")
                }
            }
            else -> throw AssemblyError("target type isn't word")
        }
    }

    private fun extendToMSBofVirtualReg(cpuRegister: CpuRegister, vreg: String, signed: Boolean) {
        if(signed) {
            when(cpuRegister) {
                CpuRegister.A -> { }
                CpuRegister.X -> asmgen.out("  txa")
                CpuRegister.Y -> asmgen.out("  tya")
            }
            asmgen.out("""
                ora  #$7f
                bmi  +
                lda  #0
+               sta  $vreg+1""")
        } else {
            if(asmgen.isTargetCpu(CpuType.CPU65c02))
                asmgen.out("  stz  $vreg+1")
            else
                asmgen.out("  lda  #0 |  sta  $vreg+1")
        }
    }

    internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister, signed: Boolean, extendWord: Boolean) {
        val assignAsWord = target.datatype in WordDatatypes

        when(target.kind) {
            TargetStorageKind.VARIABLE -> {
                asmgen.out("  st${register.name.lowercase()}  ${target.asmVarname}")
                if(assignAsWord && extendWord) {
                    if(target.datatype in SignedDatatypes) {
                        if(register!=CpuRegister.A)
                            asmgen.out("  t${register.name.lowercase()}a")
                        asmgen.signExtendAYlsb(if(target.datatype in SignedDatatypes) DataType.BYTE else DataType.UBYTE)
                        asmgen.out("  sty  ${target.asmVarname}+1")
                    } else {
                        if(asmgen.isTargetCpu(CpuType.CPU65c02))
                            asmgen.out("  stz  ${target.asmVarname}+1")
                        else
                            asmgen.out("  lda  #0 |  sta  ${target.asmVarname}+1")
                    }
                }
            }
            TargetStorageKind.MEMORY -> {
                when(register) {
                    CpuRegister.A -> {}
                    CpuRegister.X -> asmgen.out(" txa")
                    CpuRegister.Y -> asmgen.out(" tya")
                }
                storeRegisterAInMemoryAddress(target.memory!!)
            }
            TargetStorageKind.ARRAY -> {
                if(assignAsWord) {
                    when(register) {
                        CpuRegister.A -> {}
                        CpuRegister.X -> asmgen.out("  txa")
                        CpuRegister.Y -> asmgen.out("  tya")
                    }
                    if(extendWord) {
                        asmgen.signExtendAYlsb(if(target.datatype in SignedDatatypes) DataType.BYTE else DataType.UBYTE)
                    } else {
                        asmgen.out("  ldy  #0")
                    }
                    assignRegisterpairWord(target, RegisterOrPair.AY)
                } else {
                    assignRegisterByteToByteArray(target, register)
                }
            }
            TargetStorageKind.REGISTER -> {
                when(register) {
                    CpuRegister.A -> when(target.register!!) {
                        RegisterOrPair.A -> {}
                        RegisterOrPair.X -> { asmgen.out("  tax") }
                        RegisterOrPair.Y -> { asmgen.out("  tay") }
                        RegisterOrPair.AY -> {
                            require(extendWord) {
                                "no extend"
                            }
                            if(signed)
                                asmgen.out("""
                ldy  #0
                cmp  #${'$'}80
                bcc  +
                dey
+""")
                            else
                                asmgen.out("  ldy  #0")
                        }
                        RegisterOrPair.AX -> {
                            require(extendWord)
                            if(signed)
                                asmgen.out("""
                ldx  #0
                cmp  #${'$'}80
                bcc  +
                dex
+""")
                            else
                                asmgen.out("  ldx  #0")
                        }
                        RegisterOrPair.XY -> {
                            require(extendWord)
                            if(signed)
                                asmgen.out("""
                tax
                ldy  #0
                cpx  #${'$'}80
                bcc  +
                dey
+""")
                            else
                                asmgen.out("  tax |  ldy  #0")
                        }
                        RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
                        in Cx16VirtualRegisters -> {
                            val reg = "cx16.${target.register.toString().lowercase()}"
                            asmgen.out("  sta  $reg")
                            if(extendWord)
                                extendToMSBofVirtualReg(CpuRegister.A, reg, signed)
                        }
                        else -> throw AssemblyError("weird register")
                    }
                    CpuRegister.X -> when(target.register!!) {
                        RegisterOrPair.A -> { asmgen.out("  txa") }
                        RegisterOrPair.X -> {  }
                        RegisterOrPair.Y -> { asmgen.out("  txy") }
                        RegisterOrPair.AY -> {
                            require(extendWord)
                            if(signed)
                                asmgen.out("""
                txa
                ldy  #0
                cmp  #${'$'}80
                bcc  +
                dey
+""")
                            else
                                asmgen.out("  txa |  ldy  #0")
                        }
                        RegisterOrPair.AX -> {
                            require(extendWord)
                            if(signed)
                                asmgen.out("""
                txa
                ldx  #0
                cmp  #${'$'}80
                bcc  +
                dex
+""")
                            else
                                asmgen.out("  txa |  ldx  #0")
                        }
                        RegisterOrPair.XY -> {
                            require(extendWord)
                            if(signed)
                                asmgen.out("""
                ldy  #0
                cpx  #${'$'}80
                bcc  +
                dey
+""")
                            else
                                asmgen.out("  ldy  #0")
                        }
                        RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
                        in Cx16VirtualRegisters -> {
                            val reg = "cx16.${target.register.toString().lowercase()}"
                            asmgen.out("  stx  $reg")
                            if(extendWord)
                                extendToMSBofVirtualReg(CpuRegister.X, reg, signed)
                        }
                        else -> throw AssemblyError("weird register")
                    }
                    CpuRegister.Y -> when(target.register!!) {
                        RegisterOrPair.A -> { asmgen.out("  tya") }
                        RegisterOrPair.X -> { asmgen.out("  tyx") }
                        RegisterOrPair.Y -> { }
                        RegisterOrPair.AY -> {
                            require(extendWord)
                            if(signed)
                                asmgen.out("""
                tya
                ldy  #0
                cmp  #${'$'}80
                bcc  +
                dey
+""")
                            else
                                asmgen.out("  tya |  ldy  #0")
                        }
                        RegisterOrPair.AX -> {
                            require(extendWord)
                            if(signed)
                                asmgen.out("""
                tya
                ldx  #0
                cmp  #${'$'}80
                bcc  +
                dex
+""")
                            else
                                asmgen.out("  tya |  ldx  #0")
                        }
                        RegisterOrPair.XY -> {
                            require(extendWord)
                            if(signed)
                                asmgen.out("""
                tya
                tax
                ldy  #0
                cpx  #${'$'}80
                bcc  +
                dey
+""")
                            else
                                asmgen.out("  tya |  tax |  ldy  #0")
                        }
                        RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
                        in Cx16VirtualRegisters -> {
                            val reg = "cx16.${target.register.toString().lowercase()}"
                            asmgen.out("  sty  $reg")
                            if(extendWord)
                                extendToMSBofVirtualReg(CpuRegister.Y, reg, signed)
                        }
                        else -> throw AssemblyError("weird register")
                    }
                }
            }
        }
    }

    private fun assignRegisterByteToByteArray(target: AsmAssignTarget, register: CpuRegister) {
        if(target.array!!.splitWords)
            throw AssemblyError("cannot assign byte to split word array here ${target.position}")

        if(assignsIndexedPointerVar(target)) {
            // assign byte to a byte array via an uword pointervariable instead of a regular array variable
            if (target.constArrayIndexValue!=null) {
                when (register) {
                    CpuRegister.A -> {}
                    CpuRegister.X -> asmgen.out(" txa")
                    CpuRegister.Y -> asmgen.out(" tya")
                }
                if(asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
                    asmgen.out("  ldy  #${target.constArrayIndexValue} |  sta  (${target.asmVarname}),y")
                } else {
                    asmgen.out("""
                                ldy  ${target.asmVarname}
                                sty  P8ZP_SCRATCH_W1
                                ldy  ${target.asmVarname}+1
                                sty  P8ZP_SCRATCH_W1+1
                                ldy  #${target.constArrayIndexValue}
                                sta  (P8ZP_SCRATCH_W1),y""")
                }
            }
            else {
                when (register) {
                    CpuRegister.A -> {}
                    CpuRegister.X -> asmgen.out(" txa")
                    CpuRegister.Y -> asmgen.out(" tya")
                }
                val indexVar = target.array.index as? PtIdentifier
                if(indexVar!=null) {
                    if(asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
                        asmgen.out("  ldy  ${asmgen.asmVariableName(indexVar)} |  sta  (${target.asmVarname}),y")
                    } else {
                        asmgen.out("""
                                    ldy  ${target.asmVarname}
                                    sty  P8ZP_SCRATCH_W1
                                    ldy  ${target.asmVarname}+1
                                    sty  P8ZP_SCRATCH_W1+1
                                    ldy  ${asmgen.asmVariableName(indexVar)}
                                    sta  (P8ZP_SCRATCH_W1),y""")
                    }
                } else {
                    asmgen.saveRegisterStack(register, false)
                    asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
                    asmgen.restoreRegisterStack(CpuRegister.A, false)
                    if(asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
                        asmgen.out("  sta  (${target.asmVarname}),y")
                    } else {
                        asmgen.out("""
                            ldx  ${target.asmVarname}
                            stx  P8ZP_SCRATCH_W1
                            ldx  ${target.asmVarname}+1
                            stx  P8ZP_SCRATCH_W1+1
                            sta  (P8ZP_SCRATCH_W1),y""")
                    }
                }
            }
            return
        } else {
            // assign regular array indexing
            if (target.constArrayIndexValue!=null) {
                when (register) {
                    CpuRegister.A -> {}
                    CpuRegister.X -> asmgen.out(" txa")
                    CpuRegister.Y -> asmgen.out(" tya")
                }
                asmgen.out("  sta  ${target.asmVarname}+${target.constArrayIndexValue}")
            }
            else {
                when (register) {
                    CpuRegister.A -> {}
                    CpuRegister.X -> asmgen.out(" txa")
                    CpuRegister.Y -> asmgen.out(" tya")
                }
                val indexVar = target.array.index as? PtIdentifier
                if(indexVar!=null) {
                    asmgen.out("  ldy  ${asmgen.asmVariableName(indexVar)} |  sta  ${target.asmVarname},y")
                } else {
                    require(target.array.index.type in ByteDatatypes)
                    asmgen.saveRegisterStack(register, false)
                    asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
                    asmgen.restoreRegisterStack(CpuRegister.A, false)
                    asmgen.out("  sta  ${target.asmVarname},y")
                }
            }
        }
    }

    internal fun assignRegisterpairWord(target: AsmAssignTarget, regs: RegisterOrPair) {
        require(target.datatype in NumericDatatypes || target.datatype in PassByReferenceDatatypes) {
            "assign target must be word type ${target.position}"
        }
        if(target.datatype==DataType.FLOAT)
            throw AssemblyError("float value should be from FAC1 not from registerpair memory pointer")

        when(target.kind) {
            TargetStorageKind.VARIABLE -> {
                when(regs) {
                    RegisterOrPair.AX -> asmgen.out("  sta  ${target.asmVarname} |  stx  ${target.asmVarname}+1")
                    RegisterOrPair.AY -> asmgen.out("  sta  ${target.asmVarname} |  sty  ${target.asmVarname}+1")
                    RegisterOrPair.XY -> asmgen.out("  stx  ${target.asmVarname} |  sty  ${target.asmVarname}+1")
                    in Cx16VirtualRegisters -> {
                        val srcReg = asmgen.asmSymbolName(regs)
                        asmgen.out("""
                            lda  $srcReg
                            sta  ${target.asmVarname}
                            lda  $srcReg+1
                            sta  ${target.asmVarname}+1""")
                    }
                    else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
                }
            }
            TargetStorageKind.ARRAY -> {
                if(target.array!!.splitWords) {
                    // assign to split lsb/msb word array
                    if (target.constArrayIndexValue!=null) {
                        val idx = target.constArrayIndexValue!!
                        when (regs) {
                            RegisterOrPair.AX -> asmgen.out("  sta  ${target.asmVarname}_lsb+$idx |  stx  ${target.asmVarname}_msb+$idx")
                            RegisterOrPair.AY -> asmgen.out("  sta  ${target.asmVarname}_lsb+$idx |  sty  ${target.asmVarname}_msb+$idx")
                            RegisterOrPair.XY -> asmgen.out("  stx  ${target.asmVarname}_lsb+$idx |  sty  ${target.asmVarname}_msb+$idx")
                            in Cx16VirtualRegisters -> {
                                val srcReg = asmgen.asmSymbolName(regs)
                                asmgen.out("""
                                    lda  $srcReg
                                    sta  ${target.asmVarname}_lsb+$idx
                                    lda  $srcReg+1
                                    sta  ${target.asmVarname}_msb+$idx""")
                            }
                            else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
                        }
                    }
                    else {
                        if (regs !in Cx16VirtualRegisters) {
                            when (regs) {
                                RegisterOrPair.AX -> asmgen.out("  pha |  txa |  pha")
                                RegisterOrPair.AY -> asmgen.out("  pha |  tya |  pha")
                                RegisterOrPair.XY -> asmgen.out("  txa |  pha |  tya |  pha")
                                else -> throw AssemblyError("expected reg pair")
                            }
                            asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
                            asmgen.out("""
                                pla
                                sta  ${target.asmVarname}_msb,y
                                pla
                                sta  ${target.asmVarname}_lsb,y""")
                        } else {
                            val srcReg = asmgen.asmSymbolName(regs)
                            asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
                            asmgen.out("""
                                lda  $srcReg
                                sta  ${target.asmVarname}_lsb,y
                                lda  $srcReg+1
                                sta  ${target.asmVarname}_msb,y""")
                        }
                    }
                } else {
                    // assign to normal word array
                    if (target.constArrayIndexValue!=null) {
                        val idx = target.constArrayIndexValue!! * 2u
                        when (regs) {
                            RegisterOrPair.AX -> asmgen.out("  sta  ${target.asmVarname}+$idx |  stx  ${target.asmVarname}+$idx+1")
                            RegisterOrPair.AY -> asmgen.out("  sta  ${target.asmVarname}+$idx |  sty  ${target.asmVarname}+$idx+1")
                            RegisterOrPair.XY -> asmgen.out("  stx  ${target.asmVarname}+$idx |  sty  ${target.asmVarname}+$idx+1")
                            in Cx16VirtualRegisters -> {
                                val srcReg = asmgen.asmSymbolName(regs)
                                asmgen.out("""
                                    lda  $srcReg
                                    sta  ${target.asmVarname}+$idx
                                    lda  $srcReg+1
                                    sta  ${target.asmVarname}+$idx+1""")
                            }
                            else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
                        }
                    }
                    else {
                        if (regs !in Cx16VirtualRegisters) {
                            when (regs) {
                                RegisterOrPair.AX -> asmgen.out("  pha |  txa |  pha")
                                RegisterOrPair.AY -> asmgen.out("  pha |  tya |  pha")
                                RegisterOrPair.XY -> asmgen.out("  txa |  pha |  tya |  pha")
                                else -> throw AssemblyError("expected reg pair")
                            }
                            asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
                            asmgen.out("""
                                iny
                                pla
                                sta  ${target.asmVarname},y
                                dey
                                pla
                                sta  ${target.asmVarname},y""")
                        } else {
                            val srcReg = asmgen.asmSymbolName(regs)
                            asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
                            asmgen.out("""
                                iny
                                lda  $srcReg+1
                                sta  ${target.asmVarname},y
                                dey
                                lda  $srcReg
                                sta  ${target.asmVarname},y""")
                        }
                    }
                }
            }
            TargetStorageKind.REGISTER -> {
                when(regs) {
                    RegisterOrPair.AX -> when(target.register!!) {
                        RegisterOrPair.AY -> { asmgen.out("  stx  P8ZP_SCRATCH_REG |  ldy  P8ZP_SCRATCH_REG") }
                        RegisterOrPair.AX -> { }
                        RegisterOrPair.XY -> { asmgen.out("  stx  P8ZP_SCRATCH_REG |  ldy  P8ZP_SCRATCH_REG |  tax") }
                        in Cx16VirtualRegisters -> {
                            asmgen.out(
                                """
                                    sta  cx16.${target.register.toString().lowercase()}
                                    stx  cx16.${target.register.toString().lowercase()}+1
                                """)
                        }
                        else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
                    }
                    RegisterOrPair.AY -> when(target.register!!) {
                        RegisterOrPair.AY -> { }
                        RegisterOrPair.AX -> { asmgen.out("  sty  P8ZP_SCRATCH_REG |  ldx  P8ZP_SCRATCH_REG") }
                        RegisterOrPair.XY -> { asmgen.out("  tax") }
                        in Cx16VirtualRegisters -> {
                            asmgen.out(
                                """
                                    sta  cx16.${target.register.toString().lowercase()}
                                    sty  cx16.${target.register.toString().lowercase()}+1
                                """)
                        }
                        else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
                    }
                    RegisterOrPair.XY -> when(target.register!!) {
                        RegisterOrPair.AY -> { asmgen.out("  txa") }
                        RegisterOrPair.AX -> { asmgen.out("  txa |  sty  P8ZP_SCRATCH_REG |  ldx  P8ZP_SCRATCH_REG") }
                        RegisterOrPair.XY -> { }
                        in Cx16VirtualRegisters -> {
                            asmgen.out(
                                """
                                    stx  cx16.${target.register.toString().lowercase()}
                                    sty  cx16.${target.register.toString().lowercase()}+1
                                """)
                        }
                        else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
                    }
                    in Cx16VirtualRegisters -> {
                        val srcReg = asmgen.asmSymbolName(regs)
                        if(regs!=target.register) {
                            when(target.register) {
                                RegisterOrPair.AX -> asmgen.out("  lda  $srcReg |  ldx  $srcReg+1")
                                RegisterOrPair.AY -> asmgen.out("  lda  $srcReg |  ldy  $srcReg+1")
                                RegisterOrPair.XY -> asmgen.out("  ldx  $srcReg |  ldy  $srcReg+1")
                                in Cx16VirtualRegisters -> {
                                    val targetReg = asmgen.asmSymbolName(target.register!!)
                                    asmgen.out("  lda  $srcReg |  sta  $targetReg |  lda  $srcReg+1 |  sta  $targetReg+1")
                                }
                                else -> throw AssemblyError("invalid reg")
                            }
                        }
                    }
                    else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
                }
            }
            TargetStorageKind.MEMORY -> throw AssemblyError("can't store word into memory byte")
        }
    }

    private fun assignConstantWord(target: AsmAssignTarget, word: Int) {
        if(word==0 && asmgen.isTargetCpu(CpuType.CPU65c02)) {
            // optimize setting zero value for this processor
            when(target.kind) {
                TargetStorageKind.VARIABLE -> {
                    asmgen.out("  stz  ${target.asmVarname} |  stz  ${target.asmVarname}+1")
                }
                TargetStorageKind.MEMORY -> {
                    throw AssemblyError("memory is bytes not words")
                }
                TargetStorageKind.ARRAY -> {
                    asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
                    if(target.array.splitWords)
                        asmgen.out("""
                            lda  #0
                            sta  ${target.asmVarname}_lsb,y
                            sta  ${target.asmVarname}_msb,y""")
                    else
                        asmgen.out("""
                            lda  #0
                            sta  ${target.asmVarname},y
                            sta  ${target.asmVarname}+1,y""")
                }
                TargetStorageKind.REGISTER -> {
                    when(target.register!!) {
                        RegisterOrPair.AX -> asmgen.out("  lda  #0 |  tax")
                        RegisterOrPair.AY -> asmgen.out("  lda  #0 |  tay")
                        RegisterOrPair.XY -> asmgen.out("  ldx  #0 |  ldy  #0")
                        in Cx16VirtualRegisters -> {
                            asmgen.out(
                                "  stz  cx16.${
                                    target.register.toString().lowercase()
                                } |  stz  cx16.${target.register.toString().lowercase()}+1")
                        }
                        else -> throw AssemblyError("invalid register for word value")
                    }
                }
            }

            return
        }


        when(target.kind) {
            TargetStorageKind.VARIABLE -> {
                if (word ushr 8 == word and 255) {
                    // lsb=msb
                    asmgen.out("""
                    lda  #${(word and 255).toHex()}
                    sta  ${target.asmVarname}
                    sta  ${target.asmVarname}+1
                """)
                } else {
                    asmgen.out("""
                    lda  #<${word.toHex()}
                    ldy  #>${word.toHex()}
                    sta  ${target.asmVarname}
                    sty  ${target.asmVarname}+1
                """)
                }
            }
            TargetStorageKind.MEMORY -> {
                throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast")
            }
            TargetStorageKind.ARRAY -> {
                asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
                if(target.array.splitWords)
                    asmgen.out("""
                        lda  #<${word.toHex()}
                        sta  ${target.asmVarname}_lsb,y
                        lda  #>${word.toHex()}
                        sta  ${target.asmVarname}_msb,y""")
                else
                    asmgen.out("""
                        lda  #<${word.toHex()}
                        sta  ${target.asmVarname},y
                        lda  #>${word.toHex()}
                        sta  ${target.asmVarname}+1,y""")
            }
            TargetStorageKind.REGISTER -> {
                when(target.register!!) {
                    RegisterOrPair.AX -> asmgen.out("  ldx  #>${word.toHex()} |  lda  #<${word.toHex()}")
                    RegisterOrPair.AY -> asmgen.out("  ldy  #>${word.toHex()} |  lda  #<${word.toHex()}")
                    RegisterOrPair.XY -> asmgen.out("  ldy  #>${word.toHex()} |  ldx  #<${word.toHex()}")
                    in Cx16VirtualRegisters -> {
                        asmgen.out(
                            """
                            lda  #<${word.toHex()}
                            sta  cx16.${target.register.toString().lowercase()}
                            lda  #>${word.toHex()}
                            sta  cx16.${target.register.toString().lowercase()}+1
                            """)
                    }
                    else -> throw AssemblyError("invalid register for word value")
                }
            }
        }
    }

    internal fun assignConstantByte(target: AsmAssignTarget, byte: Int) {
        if(byte==0 && asmgen.isTargetCpu(CpuType.CPU65c02)) {
            // optimize setting zero value for this cpu
            when(target.kind) {
                TargetStorageKind.VARIABLE -> {
                    asmgen.out("  stz  ${target.asmVarname} ")
                }
                TargetStorageKind.MEMORY -> {
                    asmgen.out("  lda  #0")
                    storeRegisterAInMemoryAddress(target.memory!!)
                }
                TargetStorageKind.ARRAY -> {
                    if(assignsIndexedPointerVar(target)) {
                        if (target.constArrayIndexValue==0u) {
                            asmgen.out("  lda  #0")
                            asmgen.storeAIntoPointerVar(target.origAstTarget!!.array!!.variable)
                        } else {
                            asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
                            if (asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
                                asmgen.out("  lda  #0 |  sta  (${target.asmVarname}),y")
                            } else {
                                asmgen.out("  lda  ${target.asmVarname} |  sta  P8ZP_SCRATCH_W2 |  lda  ${target.asmVarname}+1 |  sta  P8ZP_SCRATCH_W2+1")
                                asmgen.out("  lda  #0 |  sta  (P8ZP_SCRATCH_W2),y")
                            }
                        }
                        return
                    }
                    if(target.array!!.splitWords)
                        throw AssemblyError("cannot assign byte to split word array here ${target.position}")
                    if (target.constArrayIndexValue!=null) {
                        val indexValue = target.constArrayIndexValue!!
                        asmgen.out("  stz  ${target.asmVarname}+$indexValue")
                    }
                    else {
                        asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.X, false)
                        asmgen.out("  stz  ${target.asmVarname},x")
                    }
                }
                TargetStorageKind.REGISTER -> when(target.register!!) {
                    RegisterOrPair.A -> asmgen.out("  lda  #0")
                    RegisterOrPair.X -> asmgen.out("  ldx  #0")
                    RegisterOrPair.Y -> asmgen.out("  ldy  #0")
                    RegisterOrPair.AX -> asmgen.out("  lda  #0 |  tax")
                    RegisterOrPair.AY -> asmgen.out("  lda  #0 |  tay")
                    RegisterOrPair.XY -> asmgen.out("  ldx  #0 |  ldy  #0")
                    RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
                    in Cx16VirtualRegisters -> {
                        asmgen.out("  stz  cx16.${target.register.toString().lowercase()} |  stz  cx16.${target.register.toString().lowercase()}+1")
                    }
                    else -> throw AssemblyError("weird register")
                }
            }

            return
        }


        when(target.kind) {
            TargetStorageKind.VARIABLE -> {
                asmgen.out("  lda  #${byte.toHex()} |  sta  ${target.asmVarname} ")
            }
            TargetStorageKind.MEMORY -> {
                asmgen.out("  lda  #${byte.toHex()}")
                storeRegisterAInMemoryAddress(target.memory!!)
            }
            TargetStorageKind.ARRAY -> {
                if(assignsIndexedPointerVar(target)) {
                    if (target.constArrayIndexValue==0u) {
                        asmgen.out("  lda  #${byte.toHex()}")
                        asmgen.storeAIntoPointerVar(target.origAstTarget!!.array!!.variable)
                    } else {
                        asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
                        if (asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
                            asmgen.out("  lda  #${byte.toHex()} |  sta  (${target.asmVarname}),y")
                        } else {
                            asmgen.out("  lda  ${target.asmVarname} |  sta  P8ZP_SCRATCH_W2 |  lda  ${target.asmVarname}+1 |  sta  P8ZP_SCRATCH_W2+1")
                            asmgen.out("  lda  #${byte.toHex()} |  sta  (P8ZP_SCRATCH_W2),y")
                        }
                    }
                    return
                }
                if(target.array!!.splitWords)
                    TODO("assign into split words ${target.position}")
                if (target.constArrayIndexValue!=null) {
                    val indexValue = target.constArrayIndexValue!!
                    asmgen.out("  lda  #${byte.toHex()} |  sta  ${target.asmVarname}+$indexValue")
                }
                else {
                    asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y)
                    asmgen.out("  lda  #${byte.toHex()} |  sta  ${target.asmVarname},y")
                }
            }
            TargetStorageKind.REGISTER -> when(target.register!!) {
                RegisterOrPair.A -> asmgen.out("  lda  #${byte.toHex()}")
                RegisterOrPair.X -> asmgen.out("  ldx  #${byte.toHex()}")
                RegisterOrPair.Y -> asmgen.out("  ldy  #${byte.toHex()}")
                RegisterOrPair.AX -> asmgen.out("  ldx  #0 |  lda  #${byte.toHex()}")
                RegisterOrPair.AY -> asmgen.out("  ldy  #0 |  lda  #${byte.toHex()}")
                RegisterOrPair.XY -> asmgen.out("  ldy  #0 |  ldx  #${byte.toHex()}")
                RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
                in Cx16VirtualRegisters -> {
                    asmgen.out("  lda  #${byte.toHex()} |  sta  cx16.${target.register.toString().lowercase()}")
                    if(asmgen.isTargetCpu(CpuType.CPU65c02))
                        asmgen.out("  stz  cx16.${target.register.toString().lowercase()}+1\n")
                    else
                        asmgen.out("  lda  #0 |  sta  cx16.${target.register.toString().lowercase()}+1\n")
                }
                else -> throw AssemblyError("weird register")
            }
        }
    }

    private fun assignsIndexedPointerVar(target: AsmAssignTarget): Boolean =
        target.origAstTarget?.array?.variable?.type==DataType.UWORD

    internal fun assignConstantFloat(target: AsmAssignTarget, float: Double) {
        if (float == 0.0) {
            // optimized case for float zero
            when(target.kind) {
                TargetStorageKind.VARIABLE -> {
                    if(asmgen.isTargetCpu(CpuType.CPU65c02))
                        asmgen.out("""
                            stz  ${target.asmVarname}
                            stz  ${target.asmVarname}+1
                            stz  ${target.asmVarname}+2
                            stz  ${target.asmVarname}+3
                            stz  ${target.asmVarname}+4
                        """)
                    else
                        asmgen.out("""
                            lda  #0
                            sta  ${target.asmVarname}
                            sta  ${target.asmVarname}+1
                            sta  ${target.asmVarname}+2
                            sta  ${target.asmVarname}+3
                            sta  ${target.asmVarname}+4
                        """)
                }
                TargetStorageKind.ARRAY -> {
                    asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
                    asmgen.out("""
                        ldy  #<${target.asmVarname}
                        sty  P8ZP_SCRATCH_W1
                        ldy  #>${target.asmVarname}
                        sty  P8ZP_SCRATCH_W1+1
                        jsr  floats.set_0_array_float""")
                }
                TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte")
                TargetStorageKind.REGISTER -> {
                    val floatConst = allocator.getFloatAsmConst(float)
                    when(target.register!!) {
                        RegisterOrPair.FAC1 -> asmgen.out("  lda  #<$floatConst  | ldy  #>$floatConst |  jsr  floats.MOVFM")
                        RegisterOrPair.FAC2 -> asmgen.out("  lda  #<$floatConst  | ldy  #>$floatConst |  jsr  floats.CONUPK")
                        else -> throw AssemblyError("can only assign float to Fac1 or 2")
                    }
                }
            }
        } else {
            // non-zero value
            val constFloat = allocator.getFloatAsmConst(float)
            when(target.kind) {
                TargetStorageKind.VARIABLE -> {
                    asmgen.out("""
                        lda  #<$constFloat
                        ldy  #>$constFloat
                        sta  P8ZP_SCRATCH_W1
                        sty  P8ZP_SCRATCH_W1+1
                        lda  #<${target.asmVarname}
                        ldy  #>${target.asmVarname}
                        jsr  floats.copy_float""")
                }
                TargetStorageKind.ARRAY -> {
                    asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
                    asmgen.out("""
                        ldy  #<${constFloat}
                        sty  P8ZP_SCRATCH_W1
                        ldy  #>${constFloat}
                        sty  P8ZP_SCRATCH_W1+1
                        ldy  #<${target.asmVarname}
                        sty  P8ZP_SCRATCH_W2
                        ldy  #>${target.asmVarname}
                        sty  P8ZP_SCRATCH_W2+1
                        jsr  floats.set_array_float
                    """)
                }
                TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte")
                TargetStorageKind.REGISTER -> {
                    val floatConst = allocator.getFloatAsmConst(float)
                    when(target.register!!) {
                        RegisterOrPair.FAC1 -> asmgen.out("  lda  #<$floatConst  | ldy  #>$floatConst |  jsr  floats.MOVFM")
                        RegisterOrPair.FAC2 -> asmgen.out("  lda  #<$floatConst  | ldy  #>$floatConst |  jsr  floats.CONUPK")
                        else -> throw AssemblyError("can only assign float to Fac1 or 2")
                    }
                }
            }
        }
    }

    private fun assignMemoryByte(target: AsmAssignTarget, address: UInt?, identifier: PtIdentifier?) {
        if (address != null) {
            when(target.kind) {
                TargetStorageKind.VARIABLE -> {
                    asmgen.out("""
                        lda  ${address.toHex()}
                        sta  ${target.asmVarname}
                        """)
                }
                TargetStorageKind.MEMORY -> {
                    asmgen.out("  lda  ${address.toHex()}")
                    storeRegisterAInMemoryAddress(target.memory!!)
                }
                TargetStorageKind.ARRAY -> {
                    asmgen.out("  lda  ${address.toHex()}")
                    assignRegisterByte(target, CpuRegister.A, false, true)
                }
                TargetStorageKind.REGISTER -> when(target.register!!) {
                    RegisterOrPair.A -> asmgen.out("  lda  ${address.toHex()}")
                    RegisterOrPair.X -> asmgen.out("  ldx  ${address.toHex()}")
                    RegisterOrPair.Y -> asmgen.out("  ldy  ${address.toHex()}")
                    RegisterOrPair.AX -> asmgen.out("  ldx  #0 |  lda  ${address.toHex()}")
                    RegisterOrPair.AY -> asmgen.out("  ldy  #0 |  lda  ${address.toHex()}")
                    RegisterOrPair.XY -> asmgen.out("  ldy  #0 |  ldy  ${address.toHex()}")
                    RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
                    in Cx16VirtualRegisters -> {
                        asmgen.out(
                            """
                            lda  ${address.toHex()}
                            sta  cx16.${target.register.toString().lowercase()}
                            lda  #0
                            sta  cx16.${target.register.toString().lowercase()}+1
                        """)
                    }
                    else -> throw AssemblyError("weird register")
                }
            }
        } else if (identifier != null) {
            when(target.kind) {
                TargetStorageKind.VARIABLE -> {
                    asmgen.loadByteFromPointerIntoA(identifier)
                    asmgen.out(" sta  ${target.asmVarname}")
                }
                TargetStorageKind.MEMORY -> {
                    asmgen.loadByteFromPointerIntoA(identifier)
                    storeRegisterAInMemoryAddress(target.memory!!)
                }
                TargetStorageKind.ARRAY -> {
                    asmgen.loadByteFromPointerIntoA(identifier)
                    assignRegisterByte(target, CpuRegister.A, false, true)
                }
                TargetStorageKind.REGISTER -> {
                    asmgen.loadByteFromPointerIntoA(identifier)
                    when(target.register!!) {
                        RegisterOrPair.A -> {}
                        RegisterOrPair.X -> asmgen.out("  tax")
                        RegisterOrPair.Y -> asmgen.out("  tay")
                        RegisterOrPair.AX -> asmgen.out("  ldx  #0")
                        RegisterOrPair.AY -> asmgen.out("  ldy  #0")
                        RegisterOrPair.XY -> asmgen.out("  tax |  ldy  #0")
                        RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
                        in Cx16VirtualRegisters -> {
                            asmgen.out(
                                """
                                sta  cx16.${target.register.toString().lowercase()}
                                lda  #0
                                sta  cx16.${target.register.toString().lowercase()}+1
                            """)
                        }
                        else -> throw AssemblyError("weird register")
                    }
                }
            }
        }
    }

    private fun assignMemoryByteIntoWord(wordtarget: AsmAssignTarget, address: UInt?, identifier: PtIdentifier?) {
        if (address != null) {
            when(wordtarget.kind) {
                TargetStorageKind.VARIABLE -> {
                    asmgen.out("  lda  ${address.toHex()} |  sta  ${wordtarget.asmVarname}")
                    if(asmgen.isTargetCpu(CpuType.CPU65c02))
                        asmgen.out("  stz  ${wordtarget.asmVarname}+1")
                    else
                        asmgen.out("  lda  #0 |  sta  ${wordtarget.asmVarname}+1")
                }
                TargetStorageKind.ARRAY -> {
                    asmgen.out("  lda  ${address.toHex()} |  ldy  #0")
                    assignRegisterpairWord(wordtarget, RegisterOrPair.AY)
                }
                TargetStorageKind.REGISTER -> when(wordtarget.register!!) {
                    RegisterOrPair.AX -> asmgen.out("  ldx  #0 |  lda  ${address.toHex()}")
                    RegisterOrPair.AY -> asmgen.out("  ldy  #0 |  lda  ${address.toHex()}")
                    RegisterOrPair.XY -> asmgen.out("  ldy  #0 |  ldy  ${address.toHex()}")
                    in Cx16VirtualRegisters -> {
                        asmgen.out("  lda  ${address.toHex()} |  sta  cx16.${wordtarget.register.toString().lowercase()}")
                        if(asmgen.isTargetCpu(CpuType.CPU65c02))
                            asmgen.out("  stz  cx16.${wordtarget.register.toString().lowercase()}+1")
                        else
                            asmgen.out("  lda  #0 |  sta  cx16.${wordtarget.register.toString().lowercase()}+1")
                    }
                    else -> throw AssemblyError("word regs can only be pair")
                }
                else -> throw AssemblyError("other types aren't word")
            }
        } else if (identifier != null) {
            when(wordtarget.kind) {
                TargetStorageKind.VARIABLE -> {
                    asmgen.loadByteFromPointerIntoA(identifier)
                    asmgen.out(" sta  ${wordtarget.asmVarname}")
                    if(asmgen.isTargetCpu(CpuType.CPU65c02))
                        asmgen.out("  stz  ${wordtarget.asmVarname}+1")
                    else
                        asmgen.out("  lda  #0 |  sta  ${wordtarget.asmVarname}+1")
                }
                TargetStorageKind.ARRAY -> {
                    asmgen.loadByteFromPointerIntoA(identifier)
                    asmgen.out("  ldy  #0")
                    assignRegisterpairWord(wordtarget, RegisterOrPair.AY)
                }
                TargetStorageKind.REGISTER -> {
                    asmgen.loadByteFromPointerIntoA(identifier)
                    when(wordtarget.register!!) {
                        RegisterOrPair.AX -> asmgen.out("  ldx  #0")
                        RegisterOrPair.AY -> asmgen.out("  ldy  #0")
                        RegisterOrPair.XY -> asmgen.out("  tax |  ldy  #0")
                        in Cx16VirtualRegisters -> {
                            asmgen.out("  sta  cx16.${wordtarget.register.toString().lowercase()}")
                            if(asmgen.isTargetCpu(CpuType.CPU65c02))
                                asmgen.out("  stz  cx16.${wordtarget.register.toString().lowercase()}+1")
                            else
                                asmgen.out("  lda  #0 |  sta  cx16.${wordtarget.register.toString().lowercase()}+1")
                        }
                        else -> throw AssemblyError("word regs can only be pair")
                    }
                }
                else -> throw AssemblyError("other types aren't word")
            }
        }
    }

    internal fun storeRegisterAInMemoryAddress(memoryAddress: PtMemoryByte) {
        val addressExpr = memoryAddress.address
        val addressLv = addressExpr as? PtNumber
        val addressOf = addressExpr as? PtAddressOf

        fun storeViaExprEval() {
            when(addressExpr) {
                is PtNumber, is PtIdentifier -> {
                    assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD)
                    asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
                }
                else -> {
                    // same as above but we need to save the A register
                    asmgen.out("  pha")
                    assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD)
                    asmgen.out("  pla")
                    asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
                }
            }
        }

        when {
            addressLv != null -> {
                asmgen.out("  sta  ${addressLv.number.toHex()}")
            }
            addressOf != null -> {
                if(addressOf.isFromArrayElement) {
                    TODO("address-of array element $addressOf")
                } else {
                    asmgen.out("  sta  ${asmgen.asmSymbolName(addressOf.identifier)}")
                }
            }
            addressExpr is PtIdentifier -> {
                asmgen.storeAIntoPointerVar(addressExpr)
            }
            addressExpr is PtBinaryExpression -> {
                if(addressExpr.operator=="+" || addressExpr.operator=="-") {
                    val addrOf = addressExpr.left as? PtAddressOf
                    val offset = (addressExpr.right as? PtNumber)?.number?.toInt()
                    if(addrOf!=null && offset!=null) {
                        if(addrOf.isFromArrayElement) {
                            TODO("address-of array element $addrOf")
                        } else {
                            asmgen.out("  sta  ${asmgen.asmSymbolName(addrOf.identifier)}${addressExpr.operator}${offset}")
                            return
                        }
                    }
                }
                if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true))
                    storeViaExprEval()
            }
            else -> storeViaExprEval()
        }
    }

    internal fun assignExpressionToRegister(expr: PtExpression, register: RegisterOrPair, signed: Boolean) {
        val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
        val tgt = AsmAssignTarget.fromRegisters(register, signed, expr.position, null, asmgen)
        val assign = AsmAssignment(src, tgt, program.memsizer, expr.position)
        translateNormalAssignment(assign, expr.definingISub())
    }

    internal fun assignExpressionToVariable(expr: PtExpression, asmVarName: String, dt: DataType) {
        if(expr.type==DataType.FLOAT && dt!=DataType.FLOAT) {
            throw AssemblyError("can't directly assign a FLOAT expression to an integer variable $expr")
        } else {
            val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
            val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, expr.definingISub(), expr.position, variableAsmName = asmVarName)
            val assign = AsmAssignment(src, tgt, program.memsizer, expr.position)
            translateNormalAssignment(assign, expr.definingISub())
        }
    }

    internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean, scope: IPtSubroutine?, pos: Position) {
        val tgt = AsmAssignTarget.fromRegisters(register, signed, pos, null, asmgen)
        val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName)
        val assign = AsmAssignment(src, tgt, program.memsizer, Position.DUMMY)
        translateNormalAssignment(assign, scope)
    }

    internal fun inplaceInvert(assign: AsmAssignment, scope: IPtSubroutine?) {
        val target = assign.target
        when (assign.target.datatype) {
            DataType.UBYTE -> {
                when (target.kind) {
                    TargetStorageKind.VARIABLE -> {
                        asmgen.out("""
                            lda  ${target.asmVarname}
                            eor  #255
                            sta  ${target.asmVarname}""")
                    }
                    TargetStorageKind.MEMORY -> {
                        val memory = target.memory!!
                        when (memory.address) {
                            is PtNumber -> {
                                val addr = (memory.address as PtNumber).number.toHex()
                                asmgen.out("""
                                    lda  $addr
                                    eor  #255
                                    sta  $addr""")
                            }
                            is PtIdentifier -> {
                                val sourceName = asmgen.loadByteFromPointerIntoA(memory.address as PtIdentifier)
                                asmgen.out("  eor  #255")
                                asmgen.out("  sta  ($sourceName),y")
                            }
                            else -> {
                                asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
                                if(asmgen.isTargetCpu(CpuType.CPU65c02)) {
                                    asmgen.out("""
                                        lda  (P8ZP_SCRATCH_W2)
                                        eor  #255""")
                                } else {
                                    asmgen.out("""
                                        ldy  #0
                                        lda  (P8ZP_SCRATCH_W2),y
                                        eor  #255""")
                                }
                                asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
                            }
                        }
                    }
                    TargetStorageKind.REGISTER -> {
                        when(target.register!!) {
                            RegisterOrPair.A -> asmgen.out("  eor  #255")
                            RegisterOrPair.X -> asmgen.out("  txa |  eor  #255 |  tax")
                            RegisterOrPair.Y -> asmgen.out("  tya |  eor  #255 |  tay")
                            else -> throw AssemblyError("invalid reg dt for byte invert")
                        }
                    }
                    TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign), scope)
                }
            }
            DataType.UWORD -> {
                when (target.kind) {
                    TargetStorageKind.VARIABLE -> {
                        asmgen.out("""
                            lda  ${target.asmVarname}
                            eor  #255
                            sta  ${target.asmVarname}
                            lda  ${target.asmVarname}+1
                            eor  #255
                            sta  ${target.asmVarname}+1""")
                    }
                    TargetStorageKind.REGISTER -> {
                        when(target.register!!) {
                            RegisterOrPair.AX -> asmgen.out("  pha |  txa |  eor  #255 |  tax |  pla |  eor  #255")
                            RegisterOrPair.AY -> asmgen.out("  pha |  tya |  eor  #255 |  tay |  pla |  eor  #255")
                            RegisterOrPair.XY -> asmgen.out("  txa |  eor  #255 |  tax |  tya |  eor  #255 |  tay")
                            in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers")
                            else -> throw AssemblyError("invalid reg dt for word invert")
                        }
                    }
                    TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign), scope)
                    else -> throw AssemblyError("weird target")
                }
            }
            else -> throw AssemblyError("invert of invalid type")
        }
    }

    internal fun inplaceNegate(assign: AsmAssignment, ignoreDatatype: Boolean, scope: IPtSubroutine?) {
        val target = assign.target
        val datatype = if(ignoreDatatype) {
            when(target.datatype) {
                DataType.UBYTE, DataType.BYTE -> DataType.BYTE
                DataType.UWORD, DataType.WORD -> DataType.WORD
                else -> target.datatype
            }
        } else target.datatype
        when (datatype) {
            DataType.BYTE -> {
                when (target.kind) {
                    TargetStorageKind.VARIABLE -> {
                        if(asmgen.isTargetCpu(CpuType.CPU65c02))
                            asmgen.out("""
                                lda  ${target.asmVarname}
                                eor  #255
                                ina
                                sta  ${target.asmVarname}""")
                        else
                            asmgen.out("""
                                lda  #0
                                sec
                                sbc  ${target.asmVarname}
                                sta  ${target.asmVarname}""")
                    }
                    TargetStorageKind.REGISTER -> {
                        when(target.register!!) {
                            RegisterOrPair.A -> {
                                if(asmgen.isTargetCpu(CpuType.CPU65c02))
                                    asmgen.out("  eor  #255 |  ina")
                                else
                                    asmgen.out("  eor  #255 |  clc |  adc  #1")
                            }
                            RegisterOrPair.X -> asmgen.out("  txa |  eor  #255 |  tax |  inx")
                            RegisterOrPair.Y -> asmgen.out("  tya |  eor  #255 |  tay |  iny")
                            else -> throw AssemblyError("invalid reg dt for byte negate")
                        }
                    }
                    TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
                    TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
                }
            }
            DataType.WORD -> {
                when (target.kind) {
                    TargetStorageKind.VARIABLE -> {
                        asmgen.out("""
                            lda  #0
                            sec
                            sbc  ${target.asmVarname}
                            sta  ${target.asmVarname}
                            lda  #0
                            sbc  ${target.asmVarname}+1
                            sta  ${target.asmVarname}+1""")
                    }
                    TargetStorageKind.REGISTER -> {
                        when(target.register!!) { //P8ZP_SCRATCH_REG
                            RegisterOrPair.AX -> {
                                asmgen.out("""
                                    sec
                                    eor  #255
                                    adc  #0
                                    pha
                                    txa
                                    eor  #255
                                    adc  #0
                                    tax
                                    pla""")
                            }
                            RegisterOrPair.AY -> {
                                asmgen.out("""
                                    sec
                                    eor  #255
                                    adc  #0
                                    tax
                                    tya
                                    eor  #255
                                    adc  #0
                                    tay
                                    txa""")
                            }
                            RegisterOrPair.XY -> {
                                asmgen.out("""
                                    sec
                                    txa
                                    eor  #255
                                    adc  #0
                                    tax
                                    tya
                                    eor  #255
                                    adc  #0
                                    tay""")
                            }
                            in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers")
                            else -> throw AssemblyError("invalid reg dt for word neg")
                        }
                    }
                    TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
                    TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
                }
            }
            DataType.FLOAT -> {
                when (target.kind) {
                    TargetStorageKind.REGISTER -> {
                        when(target.register!!) {
                            RegisterOrPair.FAC1 -> asmgen.out("  jsr  floats.NEGOP")
                            RegisterOrPair.FAC2 -> asmgen.out("  jsr  floats.MOVFA |  jsr floats.NEGOP |  jsr  floats.MOVEF")
                            else -> throw AssemblyError("invalid float register")
                        }
                    }
                    TargetStorageKind.VARIABLE -> {
                        // simply flip the sign bit in the float
                        asmgen.out("""
                            lda  ${target.asmVarname}+1
                            eor  #$80
                            sta  ${target.asmVarname}+1
                        """)
                    }
                    TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
                    else -> throw AssemblyError("weird target for in-place float negation")
                }
            }
            else -> throw AssemblyError("negate of invalid type")
        }
    }

    private fun makePrefixedExprFromArrayExprAssign(operator: String, assign: AsmAssignment): AsmAssignment {
        val prefix = PtPrefix(operator, assign.source.datatype, assign.source.array!!.position)
        prefix.add(assign.source.array)
        prefix.parent = assign.target.origAstTarget ?: program
        val prefixSrc = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, assign.source.datatype, expression=prefix)
        return AsmAssignment(prefixSrc, assign.target, assign.memsizer, assign.position)
    }
}
