prog8/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt
2023-01-22 17:10:52 +01:00

208 lines
10 KiB
Kotlin

package prog8.codegen.cpu6502.assignment
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen
import prog8.codegen.cpu6502.asConstInteger
import prog8.codegen.cpu6502.returnsWhatWhere
import prog8.codegen.cpu6502.targetSubroutine
internal enum class TargetStorageKind {
VARIABLE,
ARRAY,
MEMORY,
REGISTER,
STACK
}
internal enum class SourceStorageKind {
LITERALNUMBER,
VARIABLE,
ARRAY,
MEMORY,
REGISTER,
STACK, // value is already present on stack
EXPRESSION, // expression in ast-form, still to be evaluated
}
internal class AsmAssignTarget(val kind: TargetStorageKind,
private val asmgen: AsmGen,
val datatype: DataType,
val scope: IPtSubroutine?,
private val variableAsmName: String? = null,
val array: PtArrayIndexer? = null,
val memory: PtMemoryByte? = null,
val register: RegisterOrPair? = null,
val origAstTarget: PtAssignTarget? = null
)
{
val constArrayIndexValue by lazy { array?.index?.asConstInteger()?.toUInt() }
val asmVarname: String by lazy {
if (array == null)
variableAsmName!!
else
asmgen.asmVariableName(array.variable)
}
lateinit var origAssign: AsmAssignment
init {
if(register!=null && datatype !in NumericDatatypes)
throw AssemblyError("register must be integer or float type")
}
companion object {
fun fromAstAssignment(assign: PtAssignment, program: PtProgram, asmgen: AsmGen): AsmAssignTarget {
with(assign.target) {
when {
identifier != null -> {
val parameter: PtSubroutineParameter = TODO("search subroutine parameter that it may refer to ${assign.target.identifier!!.name}") // identifier!!.targetVarDecl(program)?.subroutineParameter
if (parameter!=null) {
val sub = parameter.definingAsmSub()
if (sub!=null) {
val reg = sub.parameters.single { it.first===parameter }.second
if(reg.statusflag!=null)
throw AssemblyError("can't assign value to processor statusflag directly")
else
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, assign.definingISub(), register=reg.registerOrPair, origAstTarget = this)
}
}
return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, type, assign.definingISub(), variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
}
array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, assign.definingISub(), array = array, origAstTarget = this)
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, assign.definingISub(), memory = memory, origAstTarget = this)
else -> throw AssemblyError("weird target")
}
}
}
fun fromRegisters(registers: RegisterOrPair, signed: Boolean, scope: IPtSubroutine?, asmgen: AsmGen): AsmAssignTarget =
when(registers) {
RegisterOrPair.A,
RegisterOrPair.X,
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, register = registers)
RegisterOrPair.AX,
RegisterOrPair.AY,
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers)
RegisterOrPair.FAC1,
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, register = registers)
RegisterOrPair.R0,
RegisterOrPair.R1,
RegisterOrPair.R2,
RegisterOrPair.R3,
RegisterOrPair.R4,
RegisterOrPair.R5,
RegisterOrPair.R6,
RegisterOrPair.R7,
RegisterOrPair.R8,
RegisterOrPair.R9,
RegisterOrPair.R10,
RegisterOrPair.R11,
RegisterOrPair.R12,
RegisterOrPair.R13,
RegisterOrPair.R14,
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers)
}
}
}
internal class AsmAssignSource(val kind: SourceStorageKind,
private val program: PtProgram,
private val asmgen: AsmGen,
val datatype: DataType,
private val variableAsmName: String? = null,
val array: PtArrayIndexer? = null,
val memory: PtMemoryByte? = null,
val register: RegisterOrPair? = null,
val number: PtNumber? = null,
val expression: PtExpression? = null
)
{
val asmVarname: String
get() = if(array==null)
variableAsmName!!
else
asmgen.asmVariableName(array.variable)
companion object {
fun fromAstSource(value: PtExpression, program: PtProgram, asmgen: AsmGen): AsmAssignSource {
val cv = value as? PtNumber
if(cv!=null)
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
return when(value) {
is PtNumber -> throw AssemblyError("should have been constant value")
is PtString -> throw AssemblyError("string literal value should not occur anymore for asm generation")
is PtArray -> throw AssemblyError("array literal value should not occur anymore for asm generation")
is PtIdentifier -> {
val parameter: PtSubroutineParameter = TODO("search subroutine parameter that it may refer to ${value.name}") // value.targetVarDecl(program)?.subroutineParameter
if(parameter?.definingAsmSub() != null)
throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}")
val varName=asmgen.asmVariableName(value)
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
if(value.type == DataType.UWORD && varName.lowercase().startsWith("cx16.r")) {
val regStr = varName.lowercase().substring(5)
val reg = RegisterOrPair.valueOf(regStr.uppercase())
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, value.type, register = reg)
} else {
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = varName)
}
}
is PtMemoryByte -> {
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
}
is PtArrayIndexer -> {
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value)
}
is PtBuiltinFunctionCall -> {
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
}
is PtFunctionCall -> {
val sub = value.targetSubroutine(program)!!
val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first
?: throw AssemblyError("can't translate zero return values in assignment")
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
}
else -> {
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
}
}
}
}
fun adjustSignedUnsigned(target: AsmAssignTarget): AsmAssignSource {
// allow some signed/unsigned relaxations
fun withAdjustedDt(newType: DataType) =
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, expression)
if(target.datatype!=datatype) {
if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) {
return withAdjustedDt(target.datatype)
} else if(target.datatype in WordDatatypes && datatype in WordDatatypes) {
return withAdjustedDt(target.datatype)
}
}
return this
}
}
internal class AsmAssignment(val source: AsmAssignSource,
val target: AsmAssignTarget,
val isAugmentable: Boolean,
memsizer: IMemSizer,
val position: Position) {
init {
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype at $position" }
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
"source dt size must be less or equal to target dt size at $position"
}
}
}