mirror of https://github.com/irmen/prog8.git
3984 lines
196 KiB
Kotlin
3984 lines
196 KiB
Kotlin
package prog8.codegen.cpu6502.assignment
|
|
|
|
import prog8.code.StMemVar
|
|
import prog8.code.StRomSub
|
|
import prog8.code.StRomSubParameter
|
|
import prog8.code.StStaticVariable
|
|
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 options: CompilationOptions,
|
|
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 translateMultiAssign(assignment: PtAssignment) {
|
|
val values = assignment.value as? PtFunctionCall
|
|
?: throw AssemblyError("only function calls can return multiple values in a multi-assign")
|
|
|
|
val sub = asmgen.symbolTable.lookup(values.name) as? StRomSub
|
|
?: throw AssemblyError("only asmsubs can return multiple values")
|
|
|
|
require(sub.returns.size>=2)
|
|
if(sub.returns.any { it.type==DataType.FLOAT })
|
|
TODO("deal with (multiple?) FP return registers")
|
|
|
|
asmgen.translate(values)
|
|
|
|
fun needsToSaveA(registersResults: List<Pair<StRomSubParameter, PtNode>>): Boolean =
|
|
if(registersResults.isEmpty())
|
|
false
|
|
else if(registersResults.all { (it.second as PtAssignTarget).identifier!=null})
|
|
false
|
|
else
|
|
true
|
|
|
|
fun assignCarryFlagResult(target: PtAssignTarget, saveA: Boolean) {
|
|
if(saveA) asmgen.out(" pha")
|
|
asmgen.out(" lda #0 | rol a")
|
|
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
|
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
|
if(saveA) asmgen.out(" pla")
|
|
}
|
|
|
|
fun assignZeroFlagResult(target: PtAssignTarget, saveA: Boolean) {
|
|
if(saveA) asmgen.out(" pha")
|
|
asmgen.out("""
|
|
beq +
|
|
lda #0
|
|
beq ++
|
|
+ lda #1
|
|
+""")
|
|
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
|
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
|
if(saveA) asmgen.out(" pla")
|
|
}
|
|
|
|
fun assignNegativeFlagResult(target: PtAssignTarget, saveA: Boolean) {
|
|
if(saveA) asmgen.out(" pha")
|
|
asmgen.out("""
|
|
bmi +
|
|
lda #0
|
|
beq ++
|
|
+ lda #1
|
|
+""")
|
|
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
|
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
|
if(saveA) asmgen.out(" pla")
|
|
}
|
|
|
|
fun assignOverflowFlagResult(target: PtAssignTarget, saveA: Boolean) {
|
|
if(saveA) asmgen.out(" pha")
|
|
asmgen.out("""
|
|
bvs +
|
|
lda #0
|
|
beq ++
|
|
+ lda #1
|
|
+""")
|
|
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
|
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
|
if(saveA) asmgen.out(" pla")
|
|
}
|
|
|
|
fun assignRegisterResults(registersResults: List<Pair<StRomSubParameter, PtNode>>) {
|
|
registersResults.forEach { (returns, target) ->
|
|
target as PtAssignTarget
|
|
if(!target.void) {
|
|
val targetIdent = target.identifier
|
|
val targetMem = target.memory
|
|
if(targetIdent!=null || targetMem!=null) {
|
|
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
|
when(returns.type) {
|
|
in ByteDatatypesWithBoolean -> {
|
|
if(returns.register.registerOrPair in Cx16VirtualRegisters) {
|
|
assignVirtualRegister(tgt, returns.register.registerOrPair!!)
|
|
} else {
|
|
assignRegisterByte(tgt, returns.register.registerOrPair!!.asCpuRegister(), false, false)
|
|
}
|
|
}
|
|
in WordDatatypes -> {
|
|
assignRegisterpairWord(tgt, returns.register.registerOrPair!!)
|
|
}
|
|
else -> throw AssemblyError("weird dt")
|
|
}
|
|
}
|
|
else TODO("array target for multi-value assignment") // Not done yet due to result register clobbering complexity
|
|
}
|
|
}
|
|
}
|
|
|
|
val assignmentTargets = assignment.children.dropLast(1)
|
|
if(sub.returns.size==assignmentTargets.size) {
|
|
// because we can only handle integer results right now we can just zip() it all up
|
|
val (statusFlagResults, registersResults) = sub.returns.zip(assignmentTargets).partition { it.first.register.statusflag!=null }
|
|
if(statusFlagResults.isNotEmpty()) {
|
|
if(statusFlagResults.size>1)
|
|
TODO("handle multiple status flag results")
|
|
val (returns, target) = statusFlagResults.single()
|
|
target as PtAssignTarget
|
|
if(target.void) {
|
|
// forget about the Carry status flag, only assign the normal return values
|
|
assignRegisterResults(registersResults)
|
|
return
|
|
}
|
|
if(registersResults.all {
|
|
val tgt = it.second as PtAssignTarget
|
|
tgt.void || tgt.identifier!=null})
|
|
{
|
|
// all other results are just stored into identifiers directly so first handle those
|
|
// (simple store instructions that don't modify the carry flag)
|
|
assignRegisterResults(registersResults)
|
|
return when(returns.register.statusflag!!) {
|
|
Statusflag.Pc -> assignCarryFlagResult(target, false)
|
|
Statusflag.Pz -> assignZeroFlagResult(target, false)
|
|
Statusflag.Pv -> assignOverflowFlagResult(target, false)
|
|
Statusflag.Pn -> assignNegativeFlagResult(target, false)
|
|
}
|
|
}
|
|
|
|
val saveA = needsToSaveA(registersResults)
|
|
when(returns.register.statusflag!!) {
|
|
Statusflag.Pc -> assignCarryFlagResult(target, saveA)
|
|
Statusflag.Pz -> assignZeroFlagResult(target, saveA)
|
|
Statusflag.Pv -> assignOverflowFlagResult(target, saveA)
|
|
Statusflag.Pn -> assignNegativeFlagResult(target, saveA)
|
|
}
|
|
}
|
|
assignRegisterResults(registersResults)
|
|
} else {
|
|
throw AssemblyError("number of values and targets don't match")
|
|
}
|
|
}
|
|
|
|
|
|
fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) {
|
|
when(assign.source.kind) {
|
|
SourceStorageKind.LITERALBOOLEAN -> {
|
|
// simple case: assign a constant boolean (0 or 1)
|
|
val num = assign.source.boolean!!.asInt()
|
|
when (assign.target.datatype) {
|
|
DataType.BOOL, DataType.UBYTE, DataType.BYTE -> assignConstantByte(assign.target, num)
|
|
DataType.UWORD, DataType.WORD -> assignConstantWord(assign.target, num)
|
|
DataType.FLOAT -> assignConstantFloat(assign.target, num.toDouble())
|
|
else -> throw AssemblyError("weird numval type")
|
|
}
|
|
}
|
|
SourceStorageKind.LITERALNUMBER -> {
|
|
// simple case: assign a constant number
|
|
val num = assign.source.number!!.number
|
|
when (assign.target.datatype) {
|
|
DataType.BOOL -> assignConstantByte(assign.target, if(num==0.0) 0 else 1)
|
|
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.BOOL -> {
|
|
if (assign.source.datatype == DataType.BOOL || !options.strictBool) assignVariableByte(assign.target, variable)
|
|
else throw AssemblyError("assigning non-bool variable to boolean, should have been typecasted")
|
|
}
|
|
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)
|
|
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, 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 ByteDatatypesWithBoolean -> {
|
|
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 ByteDatatypesWithBoolean -> {
|
|
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
|
asmgen.out(" lda $arrayVarName,y")
|
|
assignRegisterByte(assign.target, CpuRegister.A, elementDt in SignedDatatypes, true)
|
|
}
|
|
in WordDatatypes -> {
|
|
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
|
asmgen.out(" lda $arrayVarName,y | ldx $arrayVarName+1,y")
|
|
assignRegisterpairWord(assign.target, RegisterOrPair.AX)
|
|
}
|
|
DataType.FLOAT -> {
|
|
asmgen.loadScaledArrayIndexIntoRegister(value, 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", false)
|
|
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, 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 PtBool -> throw AssemblyError("source kind should have been literalboolean")
|
|
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 ByteDatatypesWithBoolean -> 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 isAssignableTo assign.target.datatype || (assign.source.datatype==DataType.BOOL && assign.target.datatype in ByteDatatypes)) {
|
|
if(assign.source.datatype in IntegerDatatypesWithBoolean) {
|
|
val signed = assign.source.datatype in SignedDatatypes
|
|
if(assign.source.datatype in ByteDatatypesWithBoolean) {
|
|
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" -> asmgen.out(" eor #1")
|
|
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 shouldn't exist for an integer")
|
|
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" -> inplaceInvert(assign, scope)
|
|
else -> throw AssemblyError("invalid prefix operator")
|
|
}
|
|
}
|
|
} else {
|
|
// use a temporary variable
|
|
val tempvar = if(value.type in ByteDatatypesWithBoolean) "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" -> {
|
|
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)
|
|
inplaceInvert(assignTempvar, scope)
|
|
}
|
|
else -> throw AssemblyError("invalid prefix operator")
|
|
}
|
|
if(value.type in ByteDatatypesWithBoolean)
|
|
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 {
|
|
val register = if(assign.source.datatype in ByteDatatypesWithBoolean) RegisterOrPair.A else RegisterOrPair.AY
|
|
val assignToRegister = AsmAssignment(assign.source,
|
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, assign.target.datatype, assign.target.scope, assign.target.position,
|
|
register = register, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position)
|
|
asmgen.translateNormalAssignment(assignToRegister, scope)
|
|
val signed = assign.target.datatype in SignedDatatypes
|
|
when(assign.target.datatype) {
|
|
in ByteDatatypesWithBoolean -> assignRegisterByte(assign.target, CpuRegister.A, signed, false)
|
|
in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
|
else -> throw AssemblyError("weird dt")
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun assignVirtualRegister(target: AsmAssignTarget, register: RegisterOrPair) {
|
|
when(target.datatype) {
|
|
in ByteDatatypesWithBoolean -> {
|
|
if(register in Cx16VirtualRegisters) {
|
|
asmgen.out(" lda cx16.${register.toString().lowercase()}L")
|
|
} else {
|
|
TODO("LDA byte from $register")
|
|
}
|
|
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 BitwiseOperators -> optimizedBitwiseExpr(expr, assign.target)
|
|
in LogicalOperators -> optimizedLogicalExpr(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 ByteDatatypesWithBoolean -> if(attemptAssignToByteCompareZero(expr, assign)) return true
|
|
else -> {
|
|
// do nothing, this is handled by a type cast.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(expr.left.type==DataType.UBYTE) {
|
|
if(expr.operator=="<") {
|
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
|
when(val right = expr.right) {
|
|
is PtIdentifier -> {
|
|
asmgen.out("""
|
|
cmp ${right.name}
|
|
rol a
|
|
and #1
|
|
eor #1""")
|
|
assignRegisterByte(assign.target, CpuRegister.A, false, false)
|
|
return true
|
|
}
|
|
is PtMemoryByte -> {
|
|
val addr = right.address as? PtNumber
|
|
if(addr!=null) {
|
|
asmgen.out("""
|
|
cmp ${addr.number.toHex()}
|
|
rol a
|
|
and #1
|
|
eor #1""")
|
|
assignRegisterByte(assign.target, CpuRegister.A, false, false)
|
|
return true
|
|
}
|
|
if(asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
|
val ptrvar = right.address as? PtIdentifier
|
|
if (ptrvar != null && asmgen.isZpVar(ptrvar)) {
|
|
asmgen.out("""
|
|
cmp (${ptrvar.name})
|
|
rol a
|
|
and #1
|
|
eor #1""")
|
|
assignRegisterByte(assign.target, CpuRegister.A, false, false)
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
is PtNumber -> {
|
|
asmgen.out("""
|
|
cmp #${right.number.toInt()}
|
|
rol a
|
|
and #1
|
|
eor #1""")
|
|
assignRegisterByte(assign.target, CpuRegister.A, false, false)
|
|
return true
|
|
}
|
|
else -> { /* not optimizable */ }
|
|
}
|
|
}
|
|
else if(expr.operator==">=") {
|
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
|
when(val right = expr.right) {
|
|
is PtIdentifier -> {
|
|
asmgen.out("""
|
|
cmp ${right.name}
|
|
rol a
|
|
and #1""")
|
|
assignRegisterByte(assign.target, CpuRegister.A, false, false)
|
|
return true
|
|
}
|
|
is PtMemoryByte -> {
|
|
val addr = right.address as? PtNumber
|
|
if(addr!=null) {
|
|
asmgen.out("""
|
|
cmp ${addr.number.toHex()}
|
|
rol a
|
|
and #1""")
|
|
assignRegisterByte(assign.target, CpuRegister.A, false, false)
|
|
return true
|
|
}
|
|
if(asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
|
val ptrvar = right.address as? PtIdentifier
|
|
if (ptrvar != null && asmgen.isZpVar(ptrvar)) {
|
|
asmgen.out("""
|
|
cmp (${ptrvar.name})
|
|
rol a
|
|
and #1""")
|
|
assignRegisterByte(assign.target, CpuRegister.A, false, false)
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
is PtNumber -> {
|
|
asmgen.out("""
|
|
cmp #${right.number.toInt()}
|
|
rol a
|
|
and #1""")
|
|
assignRegisterByte(assign.target, CpuRegister.A, false, false)
|
|
return true
|
|
}
|
|
else -> { /* not optimizable */ }
|
|
}
|
|
}
|
|
}
|
|
|
|
// b = v > 99 --> b=false , if v>99 b=true
|
|
val targetReg=assign.target.register
|
|
if(targetReg!=null) {
|
|
// a register as target should be handled slightly differently to avoid overwriting the value
|
|
val ifPart = PtNodeGroup()
|
|
val elsePart = PtNodeGroup()
|
|
val reg = when(targetReg) {
|
|
RegisterOrPair.A -> "a"
|
|
RegisterOrPair.X -> "x"
|
|
RegisterOrPair.Y -> "y"
|
|
else -> TODO("comparison to word register")
|
|
}
|
|
val assignTrue = PtInlineAssembly(" ld${reg} #1", false, assign.target.position)
|
|
val assignFalse = PtInlineAssembly(" ld${reg} #0", false, assign.target.position)
|
|
ifPart.add(assignTrue)
|
|
elsePart.add(assignFalse)
|
|
val ifelse = PtIfElse(assign.position)
|
|
val exprClone: PtBinaryExpression
|
|
if(!asmgen.isTargetCpu(CpuType.VIRTUAL)
|
|
&& (expr.operator==">" || expr.operator=="<=")
|
|
&& expr.right.type in WordDatatypes) {
|
|
// word X>Y -> X<Y, X<=Y -> Y>=X , easier to do in 6502 (codegen also expects these to no longe exist!)
|
|
exprClone = PtBinaryExpression(if(expr.operator==">") "<" else ">=", expr.type, expr.position)
|
|
exprClone.children.add(expr.children[1]) // doesn't seem to need a deep clone
|
|
exprClone.children.add(expr.children[0]) // doesn't seem to need a deep clone
|
|
} else {
|
|
exprClone = PtBinaryExpression(expr.operator, expr.type, expr.position)
|
|
exprClone.children.add(expr.children[0]) // doesn't seem to need a deep clone
|
|
exprClone.children.add(expr.children[1]) // doesn't seem to need a deep clone
|
|
}
|
|
ifelse.add(exprClone)
|
|
ifelse.add(ifPart)
|
|
ifelse.add(elsePart)
|
|
ifelse.parent = expr.parent
|
|
asmgen.translate(ifelse)
|
|
return true
|
|
}
|
|
val target = assign.target.origAstTarget
|
|
assignConstantByte(assign.target, 0)
|
|
val ifPart = PtNodeGroup()
|
|
val assignTrue: PtNode
|
|
if(target!=null) {
|
|
// set target to true
|
|
assignTrue = PtAssignment(assign.position)
|
|
assignTrue.add(target)
|
|
assignTrue.add(PtNumber.fromBoolean(true, assign.position))
|
|
} else {
|
|
when(assign.target.kind) {
|
|
TargetStorageKind.VARIABLE -> {
|
|
if(assign.target.datatype in WordDatatypes) {
|
|
assignTrue = if(asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
|
PtInlineAssembly("""
|
|
lda #1
|
|
sta ${assign.target.asmVarname}
|
|
stz ${assign.target.asmVarname}+1""", false, assign.target.position)
|
|
} else {
|
|
PtInlineAssembly("""
|
|
lda #1
|
|
sta ${assign.target.asmVarname}
|
|
lda #0
|
|
sta ${assign.target.asmVarname}+1""", false, assign.target.position)
|
|
}
|
|
} else {
|
|
assignTrue = PtInlineAssembly(" lda #1\n sta ${assign.target.asmVarname}", false, assign.target.position)
|
|
}
|
|
}
|
|
TargetStorageKind.MEMORY -> {
|
|
val tgt = PtAssignTarget(false, assign.target.position)
|
|
val targetmem = assign.target.memory!!
|
|
val mem = PtMemoryByte(targetmem.position)
|
|
mem.add(targetmem.address)
|
|
tgt.add(mem)
|
|
assignTrue = PtAssignment(assign.position)
|
|
assignTrue.add(tgt)
|
|
assignTrue.add(PtNumber.fromBoolean(true, assign.position))
|
|
}
|
|
TargetStorageKind.ARRAY -> {
|
|
val tgt = PtAssignTarget(false, assign.target.position)
|
|
val targetarray = assign.target.array!!
|
|
val array = PtArrayIndexer(assign.target.datatype, targetarray.position)
|
|
array.add(targetarray.variable)
|
|
array.add(targetarray.index)
|
|
tgt.add(array)
|
|
assignTrue = PtAssignment(assign.position)
|
|
assignTrue.add(tgt)
|
|
assignTrue.add(PtNumber.fromBoolean(true, assign.position))
|
|
}
|
|
TargetStorageKind.REGISTER -> { /* handled earlier */ return true }
|
|
}
|
|
}
|
|
ifPart.add(assignTrue)
|
|
val ifelse = PtIfElse(assign.position)
|
|
val exprClone: PtBinaryExpression
|
|
if(!asmgen.isTargetCpu(CpuType.VIRTUAL)
|
|
&& (expr.operator==">" || expr.operator=="<=")
|
|
&& expr.right.type in WordDatatypes) {
|
|
// word X>Y -> X<Y, X<=Y -> Y>=X , easier to do in 6502 (codegen also expects these to no longe exist!)
|
|
exprClone = PtBinaryExpression(if(expr.operator==">") "<" else ">=", expr.type, expr.position)
|
|
exprClone.children.add(expr.children[1]) // doesn't seem to need a deep clone
|
|
exprClone.children.add(expr.children[0]) // doesn't seem to need a deep clone
|
|
} else {
|
|
exprClone = PtBinaryExpression(expr.operator, expr.type, expr.position)
|
|
exprClone.children.add(expr.children[0]) // doesn't seem to need a deep clone
|
|
exprClone.children.add(expr.children[1]) // doesn't seem to need a deep clone
|
|
}
|
|
ifelse.add(exprClone)
|
|
ifelse.add(ifPart)
|
|
ifelse.add(PtNodeGroup())
|
|
ifelse.parent = expr.parent
|
|
asmgen.translate(ifelse)
|
|
return true
|
|
}
|
|
|
|
internal fun directIntoY(expr: PtExpression): Boolean {
|
|
return when(expr) {
|
|
is PtIdentifier -> true
|
|
is PtIrRegister -> true
|
|
is PtNumber -> true
|
|
is PtBuiltinFunctionCall -> expr.name in arrayOf("lsb", "msb")
|
|
else -> false
|
|
}
|
|
}
|
|
|
|
private fun optimizedRemainderExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
|
|
when(expr.type) {
|
|
DataType.UBYTE -> {
|
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
|
if(!directIntoY(expr.right)) asmgen.out(" pha")
|
|
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
|
|
if(!directIntoY(expr.right)) asmgen.out(" pla")
|
|
asmgen.out(" 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)
|
|
if(!directIntoY(expr.right)) asmgen.out(" pha")
|
|
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
|
|
if(!directIntoY(expr.right)) asmgen.out(" pla")
|
|
asmgen.out(" jsr math.divmod_ub_asm")
|
|
assignRegisterByte(target, CpuRegister.Y, false, true)
|
|
return true
|
|
}
|
|
DataType.BYTE -> {
|
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, true)
|
|
if(!directIntoY(expr.right)) asmgen.out(" pha")
|
|
assignExpressionToRegister(expr.right, RegisterOrPair.Y, true)
|
|
if(!directIntoY(expr.right)) asmgen.out(" pla")
|
|
asmgen.out(" 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)
|
|
if(!directIntoY(expr.right)) asmgen.out(" pha")
|
|
assignExpressionToRegister(expr.right, RegisterOrPair.Y, expr.type in SignedDatatypes)
|
|
if(!directIntoY(expr.right)) asmgen.out(" pla")
|
|
asmgen.out(" jsr math.multiply_bytes")
|
|
assignRegisterByte(target, CpuRegister.A, false, true)
|
|
return true
|
|
}
|
|
in WordDatatypes -> {
|
|
if(expr.definingBlock()!!.options.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.out(" pha")
|
|
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
|
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.R1, expr.left.type in SignedDatatypes)
|
|
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
|
asmgen.out(" pla")
|
|
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()!!.options.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")
|
|
if(signed)
|
|
asmgen.out(" jsr math.lsr_word_AY")
|
|
else
|
|
asmgen.out(" jsr math.lsr_uword_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 leftMemByte = expr.left as? PtMemoryByte
|
|
val rightMemByte = expr.right as? PtMemoryByte
|
|
val leftArrayIndexer = expr.left as? PtArrayIndexer
|
|
val rightArrayIndexer = expr.right as? PtArrayIndexer
|
|
if(expr.operator=="+" && leftArrayIndexer!=null && leftArrayIndexer.type in ByteDatatypes && right.type in ByteDatatypes) {
|
|
// special optimization for bytearray[y] + bytevalue : no need to use a tempvar, just use adc array,y
|
|
assignExpressionToRegister(right, RegisterOrPair.A, right.type==DataType.BYTE)
|
|
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pha")
|
|
asmgen.assignExpressionToRegister(leftArrayIndexer.index, RegisterOrPair.Y, false)
|
|
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pla")
|
|
val arrayvarname = asmgen.asmSymbolName(leftArrayIndexer.variable)
|
|
asmgen.out(" clc | adc $arrayvarname,y")
|
|
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
|
|
} else if(rightArrayIndexer!=null && rightArrayIndexer.type in ByteDatatypes && left.type in ByteDatatypes) {
|
|
// special optimization for bytevalue +/- bytearray[y] : no need to use a tempvar, just use adc array,y or sbc array,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")
|
|
val arrayvarname = 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 if(expr.operator=="+" && leftMemByte!=null && right.type in ByteDatatypes && optimizedPointerIndexPlusMinusByteIntoA(right, "+", leftMemByte)) {
|
|
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
|
|
return true
|
|
} else if(rightMemByte!=null && left.type in ByteDatatypes && optimizedPointerIndexPlusMinusByteIntoA(left, expr.operator, rightMemByte)) {
|
|
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
|
|
return true
|
|
} else {
|
|
assignExpressionToRegister(left, RegisterOrPair.A, left.type==DataType.BYTE)
|
|
if(directIntoY(right)) {
|
|
assignExpressionToRegister(right, RegisterOrPair.Y, left.type==DataType.BYTE)
|
|
asmgen.out(" sty P8ZP_SCRATCH_B1")
|
|
} else {
|
|
asmgen.out(" pha")
|
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", right.type)
|
|
asmgen.out(" pla")
|
|
}
|
|
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 optimizedPointerIndexPlusMinusByteIntoA(value: PtExpression, operator: String, mem: PtMemoryByte): Boolean {
|
|
// special optimization for bytevalue +/- pointervar[y] (actually: bytevalue +/- @(address) )
|
|
val address = mem.address as? PtBinaryExpression
|
|
if(address is PtBinaryExpression) {
|
|
val ptrVar = address.left as? PtIdentifier
|
|
if(ptrVar!=null && asmgen.isZpVar(ptrVar)) {
|
|
assignExpressionToRegister(value, RegisterOrPair.A, false)
|
|
val pointername = asmgen.asmVariableName(ptrVar)
|
|
val constOffset = address.right.asConstInteger()
|
|
if (constOffset != null && constOffset < 256) {
|
|
// we have value + @(zpptr + 255), or value - @(zpptr+255)
|
|
asmgen.out(" ldy #$constOffset")
|
|
if (operator == "+")
|
|
asmgen.out(" clc | adc ($pointername),y")
|
|
else
|
|
asmgen.out(" sec | sbc ($pointername),y")
|
|
} else if (address.right.type in ByteDatatypes) {
|
|
// we have @(ptr + bytevar) ++ , or @(ptr+bytevar)--
|
|
asmgen.out(" pha")
|
|
assignExpressionToRegister(address.right, RegisterOrPair.Y, false)
|
|
asmgen.out(" pla")
|
|
if (operator == "+")
|
|
asmgen.out(" clc | adc ($pointername),y")
|
|
else
|
|
asmgen.out(" sec | sbc ($pointername),y")
|
|
} else if ((address.right as? PtTypeCast)?.value?.type in ByteDatatypes) {
|
|
// we have @(ptr + bytevar as uword) ++ , or @(ptr+bytevar as uword)--
|
|
asmgen.out(" pha")
|
|
assignExpressionToRegister((address.right as PtTypeCast).value, RegisterOrPair.Y, false)
|
|
asmgen.out(" pla")
|
|
if (operator == "+")
|
|
asmgen.out(" clc | adc ($pointername),y")
|
|
else
|
|
asmgen.out(" sec | sbc ($pointername),y")
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
private fun optimizedBitwiseExpr(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) {
|
|
assignBitwiseWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right)
|
|
return true
|
|
}
|
|
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
|
|
assignBitwiseWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left)
|
|
return true
|
|
}
|
|
}
|
|
val rightArray = expr.right as? PtArrayIndexer
|
|
if(rightArray!=null) {
|
|
val constIndex = rightArray.index.asConstInteger()
|
|
if(constIndex!=null) {
|
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
|
val valueVarname = "${asmgen.asmSymbolName(rightArray.variable)} + ${constIndex*program.memsizer.memorySize(rightArray.type)}"
|
|
when(expr.operator) {
|
|
"&" -> asmgen.out(" and $valueVarname")
|
|
"|" -> asmgen.out(" ora $valueVarname")
|
|
"^" -> asmgen.out(" eor $valueVarname")
|
|
else -> throw AssemblyError("invalid logical operator")
|
|
}
|
|
assignRegisterByte(target, CpuRegister.A, false, true)
|
|
return true
|
|
}
|
|
}
|
|
|
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
|
if(directIntoY(expr.right)) {
|
|
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
|
|
asmgen.out(" sty P8ZP_SCRATCH_B1")
|
|
} else {
|
|
asmgen.out(" pha")
|
|
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
|
asmgen.out(" pla")
|
|
}
|
|
when (expr.operator) {
|
|
"&" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
|
"|" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
|
"^" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
|
else -> throw AssemblyError("invalid bitwise 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) {
|
|
assignBitwiseWithSimpleRightOperandWord(target, expr.left, expr.operator, expr.right)
|
|
return true
|
|
}
|
|
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
|
|
assignBitwiseWithSimpleRightOperandWord(target, expr.right, expr.operator, expr.left)
|
|
return true
|
|
}
|
|
}
|
|
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
|
|
when (expr.operator) {
|
|
"&" -> asmgen.out(" and P8ZP_SCRATCH_W1 | tax | tya | and P8ZP_SCRATCH_W1+1 | tay | txa")
|
|
"|" -> asmgen.out(" ora P8ZP_SCRATCH_W1 | tax | tya | ora P8ZP_SCRATCH_W1+1 | tay | txa")
|
|
"^" -> asmgen.out(" eor P8ZP_SCRATCH_W1 | tax | tya | eor P8ZP_SCRATCH_W1+1 | tay | txa")
|
|
else -> throw AssemblyError("invalid bitwise operator")
|
|
}
|
|
assignRegisterpairWord(target, RegisterOrPair.AY)
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
internal fun optimizedLogicalExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
|
|
|
|
fun swapOperands(): Boolean =
|
|
if(expr.right is PtIdentifier || expr.right is PtMemoryByte)
|
|
false
|
|
else
|
|
expr.left is PtIdentifier || expr.left is PtMemoryByte
|
|
|
|
fun assignResultIntoA(left: PtExpression, operator: String, right: PtExpression) {
|
|
// non short-circuit evaluation it is *likely* shorter and faster because of the simple operands.
|
|
|
|
fun assignViaScratch() {
|
|
if(directIntoY(right)) {
|
|
assignExpressionToRegister(right, RegisterOrPair.Y, false)
|
|
asmgen.out(" sty P8ZP_SCRATCH_B1")
|
|
} else {
|
|
asmgen.out(" pha")
|
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
|
asmgen.out(" pla")
|
|
}
|
|
when (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 logical operator")
|
|
}
|
|
}
|
|
|
|
assignExpressionToRegister(left, RegisterOrPair.A, false)
|
|
when(right) {
|
|
is PtBool -> throw AssemblyError("bool literal in logical expr should have been optimized away")
|
|
is PtIdentifier -> {
|
|
val varname = asmgen.asmVariableName(right)
|
|
when (operator) {
|
|
"and" -> asmgen.out(" and $varname")
|
|
"or" -> asmgen.out(" ora $varname")
|
|
"xor" -> asmgen.out(" eor $varname")
|
|
else -> throw AssemblyError("invalid logical operator")
|
|
}
|
|
}
|
|
is PtMemoryByte -> {
|
|
val constAddress = right.address.asConstInteger()
|
|
if(constAddress!=null) {
|
|
when (operator) {
|
|
"and" -> asmgen.out(" and ${constAddress.toHex()}")
|
|
"or" -> asmgen.out(" ora ${constAddress.toHex()}")
|
|
"xor" -> asmgen.out(" eor ${constAddress.toHex()}")
|
|
else -> throw AssemblyError("invalid logical operator")
|
|
}
|
|
}
|
|
else assignViaScratch()
|
|
}
|
|
is PtArrayIndexer -> {
|
|
val constIndex = right.index.asConstInteger()
|
|
if(constIndex!=null) {
|
|
val valueVarname = "${asmgen.asmSymbolName(right.variable)} + ${constIndex*program.memsizer.memorySize(right.type)}"
|
|
when(operator) {
|
|
"and" -> asmgen.out(" and $valueVarname")
|
|
"or" -> asmgen.out(" ora $valueVarname")
|
|
"xor" -> asmgen.out(" eor $valueVarname")
|
|
else -> throw AssemblyError("invalid logical operator")
|
|
}
|
|
}
|
|
else assignViaScratch()
|
|
}
|
|
else -> assignViaScratch()
|
|
}
|
|
}
|
|
|
|
if(!expr.right.isSimple() && expr.operator!="xor") {
|
|
// shortcircuit evaluation into A
|
|
val shortcutLabel = asmgen.makeLabel("shortcut")
|
|
when (expr.operator) {
|
|
"and" -> {
|
|
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
|
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
|
asmgen.out(" beq $shortcutLabel")
|
|
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
|
|
asmgen.out(shortcutLabel)
|
|
}
|
|
"or" -> {
|
|
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
|
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
|
asmgen.out(" bne $shortcutLabel")
|
|
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
|
|
asmgen.out(shortcutLabel)
|
|
}
|
|
else -> throw AssemblyError("invalid logical operator")
|
|
}
|
|
} else if(swapOperands()) {
|
|
// non short-circuit evaluation is *likely* shorter and faster because of the simple operands.
|
|
assignResultIntoA(expr.right, expr.operator, expr.left)
|
|
} else {
|
|
// non short-circuit evaluation is *likely* shorter and faster because of the simple operands.
|
|
assignResultIntoA(expr.left, expr.operator, expr.right)
|
|
}
|
|
|
|
assignRegisterByte(target, CpuRegister.A, false, true)
|
|
return true
|
|
}
|
|
|
|
private fun assignBitwiseWithSimpleRightOperandByte(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) {
|
|
"&" -> asmgen.out(" and $operand")
|
|
"|" -> asmgen.out(" ora $operand")
|
|
"^" -> asmgen.out(" eor $operand")
|
|
else -> throw AssemblyError("invalid operator")
|
|
}
|
|
assignRegisterByte(target, CpuRegister.A, false, true)
|
|
}
|
|
|
|
private fun assignBitwiseWithSimpleRightOperandWord(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
|
|
assignExpressionToRegister(left, RegisterOrPair.AY, false)
|
|
when(right) {
|
|
is PtNumber -> {
|
|
val number = right.number.toHex()
|
|
when (operator) {
|
|
"&" -> asmgen.out(" and #<$number | tax | tya | and #>$number | tay | txa")
|
|
"|" -> asmgen.out(" ora #<$number | tax | tya | ora #>$number | tay | txa")
|
|
"^" -> asmgen.out(" eor #<$number | tax | tya | eor #>$number | tay | txa")
|
|
else -> throw AssemblyError("invalid bitwise operator")
|
|
}
|
|
}
|
|
is PtIdentifier -> {
|
|
val name = asmgen.asmSymbolName(right)
|
|
when (operator) {
|
|
"&" -> asmgen.out(" and $name | tax | tya | and $name+1 | tay | txa")
|
|
"|" -> asmgen.out(" ora $name | tax | tya | ora $name+1 | tay | txa")
|
|
"^" -> asmgen.out(" eor $name | tax | tya | eor $name+1 | tay | txa")
|
|
else -> throw AssemblyError("invalid bitwise 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) {
|
|
DataType.BOOL -> TODO("compare bool to 0")
|
|
in ByteDatatypes -> {
|
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
|
|
asmgen.out("""
|
|
cmp #0
|
|
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) {
|
|
DataType.BOOL -> TODO("compare bool to 0")
|
|
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 symbolName = asmgen.asmVariableName(symbol, containment.definingSub())
|
|
val (dt, numElements) = when(symbol) {
|
|
is StStaticVariable -> symbol.dt to symbol.length!!
|
|
is StMemVar -> symbol.dt to symbol.length!!
|
|
else -> DataType.UNDEFINED to 0
|
|
}
|
|
when(dt) {
|
|
DataType.STR -> {
|
|
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
|
|
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
|
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position,"P8ZP_SCRATCH_W1"), symbolName, null, null)
|
|
asmgen.out(" pla")
|
|
asmgen.out(" ldy #${numElements-1}")
|
|
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
|
}
|
|
DataType.ARRAY_F -> {
|
|
assignExpressionToRegister(containment.element, RegisterOrPair.FAC1, true)
|
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, null, null)
|
|
asmgen.out(" ldy #$numElements")
|
|
asmgen.out(" jsr floats.containment_floatarray")
|
|
}
|
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
|
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
|
|
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
|
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, null, null)
|
|
asmgen.out(" pla")
|
|
asmgen.out(" ldy #$numElements")
|
|
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
|
}
|
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
|
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt)
|
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName, null, null)
|
|
asmgen.out(" ldy #$numElements")
|
|
asmgen.out(" jsr prog8_lib.containment_wordarray")
|
|
}
|
|
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
|
|
+""")
|
|
}
|
|
Statusflag.Pz -> {
|
|
asmgen.out("""
|
|
beq +
|
|
lda #0
|
|
beq ++
|
|
+ lda #1
|
|
+""")
|
|
}
|
|
Statusflag.Pn -> {
|
|
asmgen.out("""
|
|
bmi +
|
|
lda #0
|
|
beq ++
|
|
+ lda #1
|
|
+""")
|
|
}
|
|
}
|
|
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 || valueDt==DataType.BOOL) {
|
|
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", false)
|
|
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, false)) {
|
|
asmgen.out(" ldy #0")
|
|
assignRegisterpairWord(target, RegisterOrPair.AY)
|
|
} else {
|
|
assignViaExprEval(value.address)
|
|
}
|
|
}
|
|
else -> {
|
|
assignViaExprEval(value.address)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
}
|
|
is PtNumber, is PtBool -> 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 if(targetDt==DataType.BOOL && valueDt in WordDatatypes) {
|
|
return assignWordToBool(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) {
|
|
DataType.BOOL -> {
|
|
if(targetDt in IntegerDatatypes) {
|
|
// optimization to assign boolean expression to integer target (just assign the 0 or 1 directly)
|
|
val assignDirect = AsmAssignment(
|
|
AsmAssignSource.fromAstSource(value, program, asmgen),
|
|
target,
|
|
program.memsizer,
|
|
target.position
|
|
)
|
|
assignExpression(assignDirect, target.scope)
|
|
} else {
|
|
TODO("assign bool to non-integer type $targetDt")
|
|
}
|
|
}
|
|
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 in ByteDatatypesWithBoolean) {
|
|
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, valueDt in SignedDatatypes)
|
|
}
|
|
RegisterOrPair.AX,
|
|
RegisterOrPair.AY,
|
|
RegisterOrPair.XY,
|
|
in Cx16VirtualRegisters -> {
|
|
assignExpressionToRegister(value, RegisterOrPair.A, false)
|
|
assignRegisterByte(target, CpuRegister.A, valueDt in SignedDatatypes, true)
|
|
return
|
|
}
|
|
else -> {}
|
|
}
|
|
} else if(valueDt==DataType.UWORD) {
|
|
when(target.register) {
|
|
RegisterOrPair.A,
|
|
RegisterOrPair.X,
|
|
RegisterOrPair.Y -> {
|
|
return if(targetDt==DataType.BOOL)
|
|
assignWordToBool(value, target)
|
|
else
|
|
// cast an uword to a byte register, do this via lsb(value)
|
|
// generate code for lsb(value) here instead of the ubyte typecast
|
|
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!!, targetDt in SignedDatatypes)
|
|
}
|
|
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, targetDt in SignedDatatypes)
|
|
assignTypeCastedFloatFAC1("P8ZP_SCRATCH_W1", targetDt)
|
|
assignVariableToRegister("P8ZP_SCRATCH_W1", target.register!!, targetDt 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==DataType.BOOL) {
|
|
// word to bool
|
|
assignWordToBool(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 ByteDatatypesWithBoolean && targetDt in ByteDatatypes) {
|
|
// byte to byte, just assign
|
|
assignExpressionToRegister(value, target.register!!, valueDt in SignedDatatypes)
|
|
} else if(valueDt in ByteDatatypesWithBoolean && 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 IntegerDatatypesWithBoolean && valueDt.isAssignableTo(targetDt)) {
|
|
require(targetDt in WordDatatypes && valueDt in ByteDatatypesWithBoolean) {
|
|
"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 assignWordToBool(value: PtExpression, target: AsmAssignTarget) {
|
|
assignExpressionToRegister(value, RegisterOrPair.AY, false)
|
|
asmgen.out("""
|
|
sty P8ZP_SCRATCH_REG
|
|
ora P8ZP_SCRATCH_REG
|
|
beq +
|
|
lda #1
|
|
+""")
|
|
assignRegisterByte(target, CpuRegister.A, false, false)
|
|
}
|
|
|
|
private fun assignTypeCastedFloatFAC1(targetAsmVarName: String, targetDt: DataType) {
|
|
|
|
if(targetDt==DataType.FLOAT)
|
|
throw AssemblyError("typecast to identical type")
|
|
|
|
when(targetDt) {
|
|
DataType.BOOL -> asmgen.out(" jsr floats.cast_FAC1_as_bool_into_a | sta $targetAsmVarName")
|
|
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, DataType.BOOL -> {
|
|
when(targetDt) {
|
|
DataType.BOOL -> {
|
|
asmgen.out("""
|
|
lda $sourceAsmVarName
|
|
beq +
|
|
lda #1
|
|
+ sta $targetAsmVarName""")
|
|
}
|
|
DataType.UBYTE, 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 $targetDt")
|
|
}
|
|
}
|
|
DataType.BYTE -> {
|
|
when(targetDt) {
|
|
DataType.UBYTE, DataType.BOOL -> {
|
|
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.BOOL -> {
|
|
asmgen.out("""
|
|
lda $sourceAsmVarName
|
|
ora $sourceAsmVarName+1
|
|
beq +
|
|
lda #1
|
|
+ sta $targetAsmVarName""")
|
|
}
|
|
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.BOOL -> {
|
|
asmgen.out("""
|
|
lda $sourceAsmVarName
|
|
ora $sourceAsmVarName+1
|
|
beq +
|
|
lda #1
|
|
+ sta $targetAsmVarName""")
|
|
}
|
|
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.BOOL -> asmgen.out(" jsr floats.cast_as_bool_into_a | sta $targetAsmVarName")
|
|
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.BOOL -> {
|
|
when (targetDt) {
|
|
in ByteDatatypesWithBoolean -> asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
|
|
else -> throw AssemblyError("assign bool to non-byte variable")
|
|
}
|
|
}
|
|
DataType.UBYTE -> {
|
|
when(targetDt) {
|
|
DataType.BOOL -> {
|
|
asmgen.out("""
|
|
cp${regs.toString().lowercase()} #0
|
|
beq +
|
|
ld${regs.toString().lowercase()} #1
|
|
+ st${regs.toString().lowercase()} $targetAsmVarName""")
|
|
}
|
|
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.BOOL -> TODO("assign byte to bool")
|
|
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.BOOL -> TODO("assign uword to bool")
|
|
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.BOOL -> TODO("assign word to bool")
|
|
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?) {
|
|
if(arrayIndexExpr!=null) {
|
|
require(arrayDt !in SplitWordArrayTypes)
|
|
val constIndex = arrayIndexExpr.asConstInteger()
|
|
if(constIndex!=null) {
|
|
if (arrayDt == DataType.UWORD) {
|
|
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
|
|
if(constIndex>0)
|
|
asmgen.out("""
|
|
clc
|
|
adc #$constIndex
|
|
bne +
|
|
iny
|
|
+""")
|
|
}
|
|
else {
|
|
if(constIndex>0) {
|
|
val offset = program.memsizer.memorySize(arrayDt!!, constIndex) // add arrayIndexExpr * elementsize to the address of the array variable.
|
|
asmgen.out(" lda #<($sourceName + $offset) | ldy #>($sourceName + $offset)")
|
|
} else {
|
|
asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
|
}
|
|
}
|
|
assignRegisterpairWord(target, RegisterOrPair.AY)
|
|
return
|
|
} else {
|
|
if (arrayDt == DataType.UWORD) {
|
|
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
|
|
asmgen.saveRegisterStack(CpuRegister.A, false)
|
|
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
|
assignExpressionToVariable(arrayIndexExpr, "P8ZP_SCRATCH_REG", DataType.UBYTE)
|
|
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
|
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
|
asmgen.out("""
|
|
clc
|
|
adc P8ZP_SCRATCH_REG
|
|
bne +
|
|
iny
|
|
+""")
|
|
}
|
|
else {
|
|
assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.A, false)
|
|
asmgen.out("""
|
|
ldy #>$sourceName
|
|
clc
|
|
adc #<$sourceName
|
|
bne +
|
|
iny
|
|
+""")
|
|
}
|
|
assignRegisterpairWord(target, RegisterOrPair.AY)
|
|
return
|
|
}
|
|
}
|
|
|
|
// address of a normal variable
|
|
when(target.kind) {
|
|
TargetStorageKind.VARIABLE -> {
|
|
asmgen.out("""
|
|
lda #<$sourceName
|
|
ldy #>$sourceName
|
|
sta ${target.asmVarname}
|
|
sty ${target.asmVarname}+1""")
|
|
}
|
|
TargetStorageKind.MEMORY -> {
|
|
throw AssemblyError("can't store word into memory byte")
|
|
}
|
|
TargetStorageKind.ARRAY -> {
|
|
asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
|
assignRegisterpairWord(target, RegisterOrPair.AY)
|
|
}
|
|
TargetStorageKind.REGISTER -> {
|
|
when(target.register!!) {
|
|
RegisterOrPair.AX -> asmgen.out(" ldx #>$sourceName | lda #<$sourceName")
|
|
RegisterOrPair.AY -> asmgen.out(" ldy #>$sourceName | lda #<$sourceName")
|
|
RegisterOrPair.XY -> asmgen.out(" ldy #>$sourceName | ldx #<$sourceName")
|
|
in Cx16VirtualRegisters -> {
|
|
asmgen.out("""
|
|
lda #<$sourceName
|
|
ldy #>$sourceName
|
|
sta cx16.${target.register.toString().lowercase()}
|
|
sty 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 || sourceDt==DataType.BOOL) { "weird source dt for word variable" }
|
|
when(target.kind) {
|
|
TargetStorageKind.VARIABLE -> {
|
|
if(sourceDt==DataType.UBYTE || sourceDt==DataType.BOOL) {
|
|
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, CpuRegister.Y)
|
|
asmgen.out(" lda $sourceName | sta ${target.asmVarname},y")
|
|
}
|
|
DataType.UWORD, DataType.WORD -> {
|
|
asmgen.loadScaledArrayIndexIntoRegister(target.array, 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.out(" pha")
|
|
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
|
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
|
|
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
|
asmgen.out(" pla")
|
|
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(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, 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, 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, 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, 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(" stx P8ZP_SCRATCH_REG | ldy P8ZP_SCRATCH_REG") }
|
|
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(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG") }
|
|
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}")
|
|
|
|
// 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 ByteDatatypesWithBoolean)
|
|
asmgen.saveRegisterStack(register, false)
|
|
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
|
|
asmgen.out(" pla | 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, 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, 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, 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, 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!!, 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!!, 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(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(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, 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")
|
|
}
|
|
}
|
|
}
|
|
|
|
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", false)
|
|
}
|
|
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", false)
|
|
}
|
|
}
|
|
}
|
|
|
|
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, 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, DataType.BOOL -> {
|
|
val eorValue = if(assign.target.datatype==DataType.BOOL) 1 else 255
|
|
when (target.kind) {
|
|
TargetStorageKind.VARIABLE -> {
|
|
asmgen.out("""
|
|
lda ${target.asmVarname}
|
|
eor #$eorValue
|
|
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 #$eorValue
|
|
sta $addr""")
|
|
}
|
|
is PtIdentifier -> {
|
|
asmgen.loadByteFromPointerIntoA(memory.address as PtIdentifier)
|
|
asmgen.out(" eor #$eorValue")
|
|
asmgen.storeAIntoPointerVar(memory.address as PtIdentifier)
|
|
}
|
|
else -> {
|
|
asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
|
if(asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
|
asmgen.out("""
|
|
lda (P8ZP_SCRATCH_W2)
|
|
eor #$eorValue""")
|
|
} else {
|
|
asmgen.out("""
|
|
ldy #0
|
|
lda (P8ZP_SCRATCH_W2),y
|
|
eor #$eorValue""")
|
|
}
|
|
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2", false)
|
|
}
|
|
}
|
|
}
|
|
TargetStorageKind.REGISTER -> {
|
|
when(target.register!!) {
|
|
RegisterOrPair.A -> asmgen.out(" eor #$eorValue")
|
|
RegisterOrPair.X -> asmgen.out(" txa | eor #$eorValue | tax")
|
|
RegisterOrPair.Y -> asmgen.out(" tya | eor #$eorValue | tay")
|
|
else -> throw AssemblyError("invalid reg dt for byte invert")
|
|
}
|
|
}
|
|
TargetStorageKind.ARRAY -> {
|
|
val invertOperator = if(assign.target.datatype==DataType.BOOL) "not" else "~"
|
|
assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign(invertOperator, 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)
|
|
}
|
|
}
|