mirror of
https://github.com/irmen/prog8.git
synced 2024-08-17 17:29:01 +00:00
split codegen
This commit is contained in:
parent
a983a896f2
commit
248e7b808c
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,745 @@
|
|||||||
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
|
import prog8.ast.statements.DirectMemoryWrite
|
||||||
|
import prog8.ast.statements.VarDecl
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
|
import prog8.compiler.toHex
|
||||||
|
|
||||||
|
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
|
||||||
|
internal fun translate(assign: Assignment) {
|
||||||
|
if(assign.aug_op!=null)
|
||||||
|
throw AssemblyError("aug-op assignments should have been transformed to normal ones")
|
||||||
|
|
||||||
|
when(assign.value) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val numVal = assign.value as NumericLiteralValue
|
||||||
|
when(numVal.type) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> assignFromByteConstant(assign.target, numVal.number.toShort())
|
||||||
|
DataType.UWORD, DataType.WORD -> assignFromWordConstant(assign.target, numVal.number.toInt())
|
||||||
|
DataType.FLOAT -> assignFromFloatConstant(assign.target, numVal.number.toDouble())
|
||||||
|
else -> throw AssemblyError("weird numval type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RegisterExpr -> {
|
||||||
|
assignFromRegister(assign.target, (assign.value as RegisterExpr).register)
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val type = assign.target.inferType(program, assign).typeOrElse(DataType.STRUCT)
|
||||||
|
when(type) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> assignFromByteVariable(assign.target, assign.value as IdentifierReference)
|
||||||
|
DataType.UWORD, DataType.WORD -> assignFromWordVariable(assign.target, assign.value as IdentifierReference)
|
||||||
|
DataType.FLOAT -> assignFromFloatVariable(assign.target, assign.value as IdentifierReference)
|
||||||
|
else -> throw AssemblyError("unsupported assignment target type $type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is AddressOf -> {
|
||||||
|
val identifier = (assign.value as AddressOf).identifier
|
||||||
|
assignFromAddressOf(assign.target, identifier)
|
||||||
|
}
|
||||||
|
is DirectMemoryRead -> {
|
||||||
|
val read = (assign.value as DirectMemoryRead)
|
||||||
|
when(read.addressExpression) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val address = (read.addressExpression as NumericLiteralValue).number.toInt()
|
||||||
|
assignFromMemoryByte(assign.target, address, null)
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
assignFromMemoryByte(assign.target, null, read.addressExpression as IdentifierReference)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(read.addressExpression)
|
||||||
|
TODO("read memory byte from result and put that in ${assign.target}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PrefixExpression -> {
|
||||||
|
// TODO optimize common cases
|
||||||
|
asmgen.translateExpression(assign.value as PrefixExpression)
|
||||||
|
assignFromEvalResult(assign.target)
|
||||||
|
}
|
||||||
|
is BinaryExpression -> {
|
||||||
|
// TODO optimize common cases
|
||||||
|
asmgen.translateExpression(assign.value as BinaryExpression)
|
||||||
|
assignFromEvalResult(assign.target)
|
||||||
|
}
|
||||||
|
is ArrayIndexedExpression -> {
|
||||||
|
// TODO optimize common cases
|
||||||
|
val arrayExpr = assign.value as ArrayIndexedExpression
|
||||||
|
val arrayDt = arrayExpr.identifier.targetVarDecl(program.namespace)!!.datatype
|
||||||
|
val index = arrayExpr.arrayspec.index
|
||||||
|
if(index is NumericLiteralValue) {
|
||||||
|
// constant array index value
|
||||||
|
val arrayVarName = asmgen.asmIdentifierName(arrayExpr.identifier)
|
||||||
|
val indexValue = index.number.toInt() * ArrayElementTypes.getValue(arrayDt).memorySize()
|
||||||
|
when (arrayDt) {
|
||||||
|
DataType.STR, DataType.STR_S, DataType.ARRAY_UB, DataType.ARRAY_B ->
|
||||||
|
asmgen.out(" lda $arrayVarName+$indexValue | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W ->
|
||||||
|
asmgen.out(" lda $arrayVarName+$indexValue | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda $arrayVarName+$indexValue+1 | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
|
||||||
|
DataType.ARRAY_F ->
|
||||||
|
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr c64flt.push_float")
|
||||||
|
else ->
|
||||||
|
throw AssemblyError("weird array type")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
asmgen.translateArrayIndexIntoA(arrayExpr)
|
||||||
|
asmgen.readAndPushArrayvalueWithIndexA(arrayDt, arrayExpr.identifier)
|
||||||
|
}
|
||||||
|
assignFromEvalResult(assign.target)
|
||||||
|
}
|
||||||
|
is TypecastExpression -> {
|
||||||
|
val cast = assign.value as TypecastExpression
|
||||||
|
val sourceType = cast.expression.inferType(program)
|
||||||
|
val targetType = assign.target.inferType(program, assign)
|
||||||
|
if(sourceType.isKnown && targetType.isKnown &&
|
||||||
|
(sourceType.typeOrElse(DataType.STRUCT) in ByteDatatypes && targetType.typeOrElse(DataType.STRUCT) in ByteDatatypes) ||
|
||||||
|
(sourceType.typeOrElse(DataType.STRUCT) in WordDatatypes && targetType.typeOrElse(DataType.STRUCT) in WordDatatypes)) {
|
||||||
|
// no need for a type cast
|
||||||
|
assign.value = cast.expression
|
||||||
|
translate(assign)
|
||||||
|
} else {
|
||||||
|
asmgen.translateExpression(assign.value as TypecastExpression)
|
||||||
|
assignFromEvalResult(assign.target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is FunctionCall -> {
|
||||||
|
asmgen.translateExpression(assign.value as FunctionCall)
|
||||||
|
assignFromEvalResult(assign.target)
|
||||||
|
}
|
||||||
|
is ArrayLiteralValue, is StringLiteralValue -> TODO("string/array/struct assignment?")
|
||||||
|
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened")
|
||||||
|
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromEvalResult(target: AssignTarget) {
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
when {
|
||||||
|
target.register!=null -> {
|
||||||
|
if(target.register== Register.X)
|
||||||
|
throw AssemblyError("can't pop into X register - use variable instead")
|
||||||
|
asmgen.out(" inx | ld${target.register.name.toLowerCase()} ${MachineDefinition.ESTACK_LO_HEX},x ")
|
||||||
|
}
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
val targetDt = targetIdent.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
when(targetDt) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x | sta $targetName")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
sta $targetName
|
||||||
|
lda ${MachineDefinition.ESTACK_HI_HEX},x
|
||||||
|
sta $targetName+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$targetName
|
||||||
|
ldy #>$targetName
|
||||||
|
jsr c64flt.pop_float
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird target variable type $targetDt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
asmgen.out(" inx | ldy ${MachineDefinition.ESTACK_LO_HEX},x")
|
||||||
|
storeRegisterInMemoryAddress(Register.Y, target.memoryAddress)
|
||||||
|
}
|
||||||
|
target.arrayindexed!=null -> {
|
||||||
|
val arrayDt = target.arrayindexed!!.identifier.targetVarDecl(program.namespace)!!.datatype
|
||||||
|
val arrayVarName = asmgen.asmIdentifierName(target.arrayindexed!!.identifier)
|
||||||
|
asmgen.translateExpression(target.arrayindexed!!.arrayspec.index)
|
||||||
|
asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x")
|
||||||
|
popAndWriteArrayvalueWithIndexA(arrayDt, arrayVarName)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird assignment target $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromAddressOf(target: AssignTarget, name: IdentifierReference) {
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
val struct = name.memberOfStruct(program.namespace)
|
||||||
|
val sourceName = if(struct!=null) {
|
||||||
|
// take the address of the first struct member instead
|
||||||
|
val decl = name.targetVarDecl(program.namespace)!!
|
||||||
|
val firstStructMember = struct.nameOfFirstMember()
|
||||||
|
// find the flattened var that belongs to this first struct member
|
||||||
|
val firstVarName = listOf(decl.name, firstStructMember)
|
||||||
|
val firstVar = name.definingScope().lookup(firstVarName, name) as VarDecl
|
||||||
|
firstVar.name
|
||||||
|
} else {
|
||||||
|
asmgen.fixNameSymbols(name.nameInSource.joinToString ("."))
|
||||||
|
}
|
||||||
|
|
||||||
|
when {
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$sourceName
|
||||||
|
ldy #>$sourceName
|
||||||
|
sta $targetName
|
||||||
|
sty $targetName+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
TODO("assign address $sourceName to memory word $target")
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
TODO("assign address $sourceName to array $targetName [ $index ]")
|
||||||
|
}
|
||||||
|
else -> TODO("assign address $sourceName to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromWordVariable(target: AssignTarget, variable: IdentifierReference) {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(variable)
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
when {
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
ldy $sourceName+1
|
||||||
|
sta $targetName
|
||||||
|
sty $targetName+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
TODO("assign wordvar $sourceName to memory ${target.memoryAddress}")
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
asmgen.out(" lda $sourceName | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda $sourceName+1 | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x")
|
||||||
|
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
popAndWriteArrayvalueWithIndexA(arrayDt, targetName)
|
||||||
|
}
|
||||||
|
else -> TODO("assign wordvar to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(variable)
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
when {
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
sta $targetName
|
||||||
|
lda $sourceName+1
|
||||||
|
sta $targetName+1
|
||||||
|
lda $sourceName+2
|
||||||
|
sta $targetName+2
|
||||||
|
lda $sourceName+3
|
||||||
|
sta $targetName+3
|
||||||
|
lda $sourceName+4
|
||||||
|
sta $targetName+4
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr c64flt.push_float")
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out(" lda #<$targetName | ldy #>$targetName | jsr c64flt.pop_float_to_indexed_var")
|
||||||
|
}
|
||||||
|
else -> TODO("assign floatvar to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromByteVariable(target: AssignTarget, variable: IdentifierReference) {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(variable)
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
when {
|
||||||
|
target.register!=null -> {
|
||||||
|
asmgen.out(" ld${target.register.name.toLowerCase()} $sourceName")
|
||||||
|
}
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
sta $targetName
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
asmgen.out(" lda $sourceName | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x")
|
||||||
|
popAndWriteArrayvalueWithIndexA(arrayDt, targetName)
|
||||||
|
}
|
||||||
|
target.memoryAddress != null -> {
|
||||||
|
val addressExpr = target.memoryAddress.addressExpression
|
||||||
|
val addressLv = addressExpr as? NumericLiteralValue
|
||||||
|
when {
|
||||||
|
addressLv != null -> asmgen.out(" lda $sourceName | sta ${addressLv.number.toHex()}")
|
||||||
|
addressExpr is IdentifierReference -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(addressExpr)
|
||||||
|
asmgen.out(" lda $sourceName | sta $targetName")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(addressExpr)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
ldy ${MachineDefinition.ESTACK_HI_HEX},x
|
||||||
|
sta (+) +1
|
||||||
|
sty (+) +2
|
||||||
|
lda $sourceName
|
||||||
|
+ sta ${65535.toHex()} ; modified
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> TODO("assign bytevar to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromRegister(target: AssignTarget, register: Register) {
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
when {
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out(" st${register.name.toLowerCase()} $targetName")
|
||||||
|
}
|
||||||
|
target.register!=null -> {
|
||||||
|
when(register) {
|
||||||
|
Register.A -> when(target.register) {
|
||||||
|
Register.A -> {}
|
||||||
|
Register.X -> asmgen.out(" tax")
|
||||||
|
Register.Y -> asmgen.out(" tay")
|
||||||
|
}
|
||||||
|
Register.X -> when(target.register) {
|
||||||
|
Register.A -> asmgen.out(" txa")
|
||||||
|
Register.X -> {}
|
||||||
|
Register.Y -> asmgen.out(" txy")
|
||||||
|
}
|
||||||
|
Register.Y -> when(target.register) {
|
||||||
|
Register.A -> asmgen.out(" tya")
|
||||||
|
Register.X -> asmgen.out(" tyx")
|
||||||
|
Register.Y -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
storeRegisterInMemoryAddress(register, target.memoryAddress)
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
when (index) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val memindex = index.number.toInt()
|
||||||
|
when(register) {
|
||||||
|
Register.A -> asmgen.out(" sta $targetName+$memindex")
|
||||||
|
Register.X -> asmgen.out(" stx $targetName+$memindex")
|
||||||
|
Register.Y -> asmgen.out(" sty $targetName+$memindex")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RegisterExpr -> {
|
||||||
|
when(register) {
|
||||||
|
Register.A -> asmgen.out(" sta ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
Register.X -> asmgen.out(" stx ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
Register.Y -> asmgen.out(" sty ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
}
|
||||||
|
when(index.register) {
|
||||||
|
Register.A -> {}
|
||||||
|
Register.X -> asmgen.out(" txa")
|
||||||
|
Register.Y -> asmgen.out(" tya")
|
||||||
|
}
|
||||||
|
asmgen.out("""
|
||||||
|
tay
|
||||||
|
lda ${MachineDefinition.C64Zeropage.SCRATCH_B1}
|
||||||
|
sta $targetName,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
when(register) {
|
||||||
|
Register.A -> asmgen.out(" sta ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
Register.X -> asmgen.out(" stx ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
Register.Y -> asmgen.out(" sty ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
}
|
||||||
|
asmgen.out("""
|
||||||
|
lda ${asmgen.asmIdentifierName(index)}
|
||||||
|
tay
|
||||||
|
lda ${MachineDefinition.C64Zeropage.SCRATCH_B1}
|
||||||
|
sta $targetName,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.saveRegister(register)
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.restoreRegister(register)
|
||||||
|
when(register) {
|
||||||
|
Register.A -> asmgen.out(" sta ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
Register.X -> asmgen.out(" stx ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
Register.Y -> asmgen.out(" sty ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
}
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
tay
|
||||||
|
lda ${MachineDefinition.C64Zeropage.SCRATCH_B1}
|
||||||
|
sta $targetName,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> TODO("assign register $register to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun storeRegisterInMemoryAddress(register: Register, memoryAddress: DirectMemoryWrite) {
|
||||||
|
val addressExpr = memoryAddress.addressExpression
|
||||||
|
val addressLv = addressExpr as? NumericLiteralValue
|
||||||
|
val registerName = register.name.toLowerCase()
|
||||||
|
when {
|
||||||
|
addressLv != null -> asmgen.out(" st$registerName ${addressLv.number.toHex()}")
|
||||||
|
addressExpr is IdentifierReference -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(addressExpr)
|
||||||
|
when(register) {
|
||||||
|
Register.A -> asmgen.out("""
|
||||||
|
ldy $targetName
|
||||||
|
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1}
|
||||||
|
ldy $targetName+1
|
||||||
|
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1+1}
|
||||||
|
ldy #0
|
||||||
|
sta (${MachineDefinition.C64Zeropage.SCRATCH_W1}),y
|
||||||
|
""")
|
||||||
|
Register.X -> asmgen.out("""
|
||||||
|
txa
|
||||||
|
ldy $targetName
|
||||||
|
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1}
|
||||||
|
ldy $targetName+1
|
||||||
|
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1+1}
|
||||||
|
ldy #0
|
||||||
|
sta (${MachineDefinition.C64Zeropage.SCRATCH_W1}),y
|
||||||
|
""")
|
||||||
|
Register.Y -> asmgen.out("""
|
||||||
|
tya
|
||||||
|
ldy $targetName
|
||||||
|
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1}
|
||||||
|
ldy $targetName+1
|
||||||
|
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1+1}
|
||||||
|
ldy #0
|
||||||
|
sta (${MachineDefinition.C64Zeropage.SCRATCH_W1}),y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.saveRegister(register)
|
||||||
|
asmgen.translateExpression(addressExpr)
|
||||||
|
asmgen.restoreRegister(register)
|
||||||
|
when (register) {
|
||||||
|
Register.A -> asmgen.out(" tay")
|
||||||
|
Register.X -> throw AssemblyError("can't use X register here")
|
||||||
|
Register.Y -> {}
|
||||||
|
}
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
sta (+) +1
|
||||||
|
lda ${MachineDefinition.ESTACK_HI_HEX},x
|
||||||
|
sta (+) +2
|
||||||
|
+ sty ${65535.toHex()} ; modified
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromWordConstant(target: AssignTarget, word: Int) {
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
when {
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
if(word ushr 8 == word and 255) {
|
||||||
|
// lsb=msb
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${(word and 255).toHex()}
|
||||||
|
sta $targetName
|
||||||
|
sta $targetName+1
|
||||||
|
""")
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${word.toHex()}
|
||||||
|
ldy #>${word.toHex()}
|
||||||
|
sta $targetName
|
||||||
|
sty $targetName+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
TODO("assign word $word to memory ${target.memoryAddress}")
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
// TODO optimize common cases
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
lda #<${word.toHex()}
|
||||||
|
sta $targetName,y
|
||||||
|
lda #>${word.toHex()}
|
||||||
|
sta $targetName+1,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> TODO("assign word $word to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromByteConstant(target: AssignTarget, byte: Short) {
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
when {
|
||||||
|
target.register!=null -> {
|
||||||
|
asmgen.out(" ld${target.register.name.toLowerCase()} #${byte.toHex()}")
|
||||||
|
}
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out(" lda #${byte.toHex()} | sta $targetName ")
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
asmgen.out(" ldy #${byte.toHex()}")
|
||||||
|
storeRegisterInMemoryAddress(Register.Y, target.memoryAddress)
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
// TODO optimize common cases
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
ldy ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
lda #${byte.toHex()}
|
||||||
|
sta $targetName,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> TODO("assign byte $byte to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromFloatConstant(target: AssignTarget, float: Double) {
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
if(float==0.0) {
|
||||||
|
// optimized case for float zero
|
||||||
|
when {
|
||||||
|
targetIdent != null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #0
|
||||||
|
sta $targetName
|
||||||
|
sta $targetName+1
|
||||||
|
sta $targetName+2
|
||||||
|
sta $targetName+3
|
||||||
|
sta $targetName+4
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
if(index is NumericLiteralValue) {
|
||||||
|
val indexValue = index.number.toInt() * MachineDefinition.Mflpt5.MemorySize
|
||||||
|
asmgen.out("""
|
||||||
|
lda #0
|
||||||
|
sta $targetName+$indexValue
|
||||||
|
sta $targetName+$indexValue+1
|
||||||
|
sta $targetName+$indexValue+2
|
||||||
|
sta $targetName+$indexValue+3
|
||||||
|
sta $targetName+$indexValue+4
|
||||||
|
""")
|
||||||
|
} else {
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
tay
|
||||||
|
lda #0
|
||||||
|
sta $targetName,y
|
||||||
|
sta $targetName+1,y
|
||||||
|
sta $targetName+2,y
|
||||||
|
sta $targetName+3,y
|
||||||
|
sta $targetName+4,y
|
||||||
|
""") // TODO use a subroutine for this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> TODO("assign float 0.0 to $target")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// non-zero value
|
||||||
|
val constFloat = asmgen.getFloatConst(float)
|
||||||
|
when {
|
||||||
|
targetIdent != null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $constFloat
|
||||||
|
sta $targetName
|
||||||
|
lda $constFloat+1
|
||||||
|
sta $targetName+1
|
||||||
|
lda $constFloat+2
|
||||||
|
sta $targetName+2
|
||||||
|
lda $constFloat+3
|
||||||
|
sta $targetName+3
|
||||||
|
lda $constFloat+4
|
||||||
|
sta $targetName+4
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val arrayVarName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
if(index is NumericLiteralValue) {
|
||||||
|
val indexValue = index.number.toInt() * MachineDefinition.Mflpt5.MemorySize
|
||||||
|
asmgen.out("""
|
||||||
|
lda $constFloat
|
||||||
|
sta $arrayVarName+$indexValue
|
||||||
|
lda $constFloat+1
|
||||||
|
sta $arrayVarName+$indexValue+1
|
||||||
|
lda $constFloat+2
|
||||||
|
sta $arrayVarName+$indexValue+2
|
||||||
|
lda $constFloat+3
|
||||||
|
sta $arrayVarName+$indexValue+3
|
||||||
|
lda $constFloat+4
|
||||||
|
sta $arrayVarName+$indexValue+4
|
||||||
|
""")
|
||||||
|
} else {
|
||||||
|
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||||
|
asmgen.out("""
|
||||||
|
sta ${MachineDefinition.C64Zeropage.SCRATCH_REG}
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc ${MachineDefinition.C64Zeropage.SCRATCH_REG}
|
||||||
|
tay
|
||||||
|
lda $constFloat
|
||||||
|
sta $arrayVarName,y
|
||||||
|
lda $constFloat+1
|
||||||
|
sta $arrayVarName+1,y
|
||||||
|
lda $constFloat+2
|
||||||
|
sta $arrayVarName+2,y
|
||||||
|
lda $constFloat+3
|
||||||
|
sta $arrayVarName+3,y
|
||||||
|
lda $constFloat+4
|
||||||
|
sta $arrayVarName+4,y
|
||||||
|
""") // TODO use a subroutine for this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> TODO("assign float $float to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) {
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
if(address!=null) {
|
||||||
|
when {
|
||||||
|
target.register!=null -> {
|
||||||
|
asmgen.out(" ld${target.register.name.toLowerCase()} ${address.toHex()}")
|
||||||
|
}
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out("""
|
||||||
|
lda ${address.toHex()}
|
||||||
|
sta $targetName
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
asmgen.out(" ldy ${address.toHex()}")
|
||||||
|
storeRegisterInMemoryAddress(Register.Y, target.memoryAddress)
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
TODO("assign memory byte at $address to array $targetName [ $index ]")
|
||||||
|
}
|
||||||
|
else -> TODO("assign memory byte $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(identifier!=null) {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(identifier)
|
||||||
|
when {
|
||||||
|
target.register!=null -> {
|
||||||
|
asmgen.out("""
|
||||||
|
ldy #0
|
||||||
|
lda ($sourceName),y
|
||||||
|
""")
|
||||||
|
when(target.register){
|
||||||
|
Register.A -> {}
|
||||||
|
Register.X -> asmgen.out(" tax")
|
||||||
|
Register.Y -> asmgen.out(" tay")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out("""
|
||||||
|
ldy #0
|
||||||
|
lda ($sourceName),y
|
||||||
|
sta $targetName
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
asmgen.out(" ldy $sourceName")
|
||||||
|
storeRegisterInMemoryAddress(Register.Y, target.memoryAddress)
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
TODO("assign memory byte $sourceName to array $targetName [ $index ]")
|
||||||
|
}
|
||||||
|
else -> TODO("assign memory byte $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun popAndWriteArrayvalueWithIndexA(arrayDt: DataType, variablename: String) {
|
||||||
|
when (arrayDt) {
|
||||||
|
DataType.STR, DataType.STR_S, DataType.ARRAY_UB, DataType.ARRAY_B ->
|
||||||
|
asmgen.out(" tay | inx | lda ${MachineDefinition.ESTACK_LO_HEX},x | sta $variablename,y")
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W ->
|
||||||
|
asmgen.out(" asl a | tay | inx | lda ${MachineDefinition.ESTACK_LO_HEX},x | sta $variablename,y | lda ${MachineDefinition.ESTACK_HI_HEX},x | sta $variablename+1,y")
|
||||||
|
DataType.ARRAY_F ->
|
||||||
|
// index * 5 is done in the subroutine that's called
|
||||||
|
asmgen.out("""
|
||||||
|
sta ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
dex
|
||||||
|
lda #<$variablename
|
||||||
|
ldy #>$variablename
|
||||||
|
jsr c64flt.pop_float_to_indexed_var
|
||||||
|
""")
|
||||||
|
else ->
|
||||||
|
throw AssemblyError("weird array type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,8 +9,6 @@ import prog8.ast.base.WordDatatypes
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
import prog8.compiler.CompilationOptions
|
|
||||||
import prog8.compiler.Zeropage
|
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
||||||
@ -18,10 +16,7 @@ import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
|
|||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
import prog8.functions.FunctionSignature
|
import prog8.functions.FunctionSignature
|
||||||
|
|
||||||
internal class BuiltinFunctionsAsmGen(private val program: Program,
|
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
private val options: CompilationOptions,
|
|
||||||
private val zeropage: Zeropage,
|
|
||||||
private val asmgen: AsmGen) {
|
|
||||||
|
|
||||||
internal fun translateFunctioncallExpression(fcall: FunctionCall, func: FunctionSignature) {
|
internal fun translateFunctioncallExpression(fcall: FunctionCall, func: FunctionSignature) {
|
||||||
translateFunctioncall(fcall, func, false)
|
translateFunctioncall(fcall, func, false)
|
||||||
@ -33,21 +28,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
|
|
||||||
private fun translateFunctioncall(fcall: IFunctionCall, func: FunctionSignature, discardResult: Boolean) {
|
private fun translateFunctioncall(fcall: IFunctionCall, func: FunctionSignature, discardResult: Boolean) {
|
||||||
val functionName = fcall.target.nameInSource.last()
|
val functionName = fcall.target.nameInSource.last()
|
||||||
if(discardResult) {
|
if (discardResult) {
|
||||||
if(func.pure)
|
if (func.pure)
|
||||||
return // can just ignore the whole function call altogether
|
return // can just ignore the whole function call altogether
|
||||||
else if(func.returntype!=null)
|
else if (func.returntype != null)
|
||||||
throw AssemblyError("discarding result of non-pure function $fcall")
|
throw AssemblyError("discarding result of non-pure function $fcall")
|
||||||
}
|
}
|
||||||
|
|
||||||
when(functionName) {
|
when (functionName) {
|
||||||
"msb" -> {
|
"msb" -> {
|
||||||
val arg = fcall.arglist.single()
|
val arg = fcall.arglist.single()
|
||||||
if(arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
||||||
throw AssemblyError("msb required word argument")
|
throw AssemblyError("msb required word argument")
|
||||||
if(arg is NumericLiteralValue)
|
if (arg is NumericLiteralValue)
|
||||||
throw AssemblyError("should have been const-folded")
|
throw AssemblyError("should have been const-folded")
|
||||||
if(arg is IdentifierReference) {
|
if (arg is IdentifierReference) {
|
||||||
val sourceName = asmgen.asmIdentifierName(arg)
|
val sourceName = asmgen.asmIdentifierName(arg)
|
||||||
asmgen.out(" lda $sourceName+1 | sta $ESTACK_LO_HEX,x | dex")
|
asmgen.out(" lda $sourceName+1 | sta $ESTACK_LO_HEX,x | dex")
|
||||||
} else {
|
} else {
|
||||||
@ -87,7 +82,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
"min", "max", "sum" -> {
|
"min", "max", "sum" -> {
|
||||||
outputPushAddressAndLenghtOfArray(fcall.arglist[0])
|
outputPushAddressAndLenghtOfArray(fcall.arglist[0])
|
||||||
val dt = fcall.arglist.single().inferType(program)
|
val dt = fcall.arglist.single().inferType(program)
|
||||||
when(dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.ARRAY_UB, DataType.STR_S, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_ub")
|
DataType.ARRAY_UB, DataType.STR_S, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_ub")
|
||||||
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
||||||
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${functionName}_uw")
|
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${functionName}_uw")
|
||||||
@ -99,7 +94,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
"any", "all" -> {
|
"any", "all" -> {
|
||||||
outputPushAddressAndLenghtOfArray(fcall.arglist[0])
|
outputPushAddressAndLenghtOfArray(fcall.arglist[0])
|
||||||
val dt = fcall.arglist.single().inferType(program)
|
val dt = fcall.arglist.single().inferType(program)
|
||||||
when(dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR_S, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR_S, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr c64flt.func_${functionName}_f")
|
DataType.ARRAY_F -> asmgen.out(" jsr c64flt.func_${functionName}_f")
|
||||||
@ -135,11 +130,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.arglist.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when(dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
when(what) {
|
when (what) {
|
||||||
is RegisterExpr -> {
|
is RegisterExpr -> {
|
||||||
when(what.register) {
|
when (what.register) {
|
||||||
Register.A -> asmgen.out(" asl a")
|
Register.A -> asmgen.out(" asl a")
|
||||||
Register.X -> asmgen.out(" txa | asl a | tax")
|
Register.X -> asmgen.out(" txa | asl a | tax")
|
||||||
Register.Y -> asmgen.out(" tya | asl a | tay")
|
Register.Y -> asmgen.out(" tya | asl a | tay")
|
||||||
@ -147,7 +142,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
is IdentifierReference -> asmgen.out(" asl ${asmgen.asmIdentifierName(what)}")
|
is IdentifierReference -> asmgen.out(" asl ${asmgen.asmIdentifierName(what)}")
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
if(what.addressExpression is NumericLiteralValue) {
|
if (what.addressExpression is NumericLiteralValue) {
|
||||||
asmgen.out(" asl ${(what.addressExpression as NumericLiteralValue).number.toHex()}")
|
asmgen.out(" asl ${(what.addressExpression as NumericLiteralValue).number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
TODO("lsl memory byte $what")
|
TODO("lsl memory byte $what")
|
||||||
@ -160,7 +155,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
when(what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> TODO("lsl sbyte $what")
|
is ArrayIndexedExpression -> TODO("lsl sbyte $what")
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
@ -176,11 +171,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.arglist.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when(dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when(what) {
|
when (what) {
|
||||||
is RegisterExpr -> {
|
is RegisterExpr -> {
|
||||||
when(what.register) {
|
when (what.register) {
|
||||||
Register.A -> asmgen.out(" lsr a")
|
Register.A -> asmgen.out(" lsr a")
|
||||||
Register.X -> asmgen.out(" txa | lsr a | tax")
|
Register.X -> asmgen.out(" txa | lsr a | tax")
|
||||||
Register.Y -> asmgen.out(" tya | lsr a | tay")
|
Register.Y -> asmgen.out(" tya | lsr a | tay")
|
||||||
@ -188,7 +183,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
is IdentifierReference -> asmgen.out(" lsr ${asmgen.asmIdentifierName(what)}")
|
is IdentifierReference -> asmgen.out(" lsr ${asmgen.asmIdentifierName(what)}")
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
if(what.addressExpression is NumericLiteralValue) {
|
if (what.addressExpression is NumericLiteralValue) {
|
||||||
asmgen.out(" lsr ${(what.addressExpression as NumericLiteralValue).number.toHex()}")
|
asmgen.out(" lsr ${(what.addressExpression as NumericLiteralValue).number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
TODO("lsr memory byte $what")
|
TODO("lsr memory byte $what")
|
||||||
@ -201,7 +196,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
when(what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> TODO("lsr sbyte $what")
|
is ArrayIndexedExpression -> TODO("lsr sbyte $what")
|
||||||
is DirectMemoryRead -> TODO("lsr sbyte $what")
|
is DirectMemoryRead -> TODO("lsr sbyte $what")
|
||||||
is RegisterExpr -> TODO("lsr sbyte $what")
|
is RegisterExpr -> TODO("lsr sbyte $what")
|
||||||
@ -210,7 +205,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when(what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> TODO("lsr uword $what")
|
is ArrayIndexedExpression -> TODO("lsr uword $what")
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
@ -220,7 +215,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
when(what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> TODO("lsr sword $what")
|
is ArrayIndexedExpression -> TODO("lsr sword $what")
|
||||||
is IdentifierReference -> TODO("lsr sword $what")
|
is IdentifierReference -> TODO("lsr sword $what")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -233,7 +228,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.arglist.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when(dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
TODO("rol ubyte")
|
TODO("rol ubyte")
|
||||||
}
|
}
|
||||||
@ -247,7 +242,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.arglist.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when(dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
TODO("rol2 ubyte")
|
TODO("rol2 ubyte")
|
||||||
}
|
}
|
||||||
@ -261,7 +256,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.arglist.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when(dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
TODO("ror ubyte")
|
TODO("ror ubyte")
|
||||||
}
|
}
|
||||||
@ -275,7 +270,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.arglist.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when(dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
TODO("ror2 ubyte")
|
TODO("ror2 ubyte")
|
||||||
}
|
}
|
||||||
@ -326,4 +321,3 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,430 @@
|
|||||||
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
|
import prog8.compiler.toHex
|
||||||
|
import prog8.functions.BuiltinFunctions
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
internal class ExpressionsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
|
||||||
|
internal fun translateExpression(expression: Expression) {
|
||||||
|
when(expression) {
|
||||||
|
is PrefixExpression -> translateExpression(expression)
|
||||||
|
is BinaryExpression -> translateExpression(expression)
|
||||||
|
is ArrayIndexedExpression -> translatePushFromArray(expression)
|
||||||
|
is TypecastExpression -> translateExpression(expression)
|
||||||
|
is AddressOf -> translateExpression(expression)
|
||||||
|
is DirectMemoryRead -> translateExpression(expression)
|
||||||
|
is NumericLiteralValue -> translateExpression(expression)
|
||||||
|
is RegisterExpr -> translateExpression(expression)
|
||||||
|
is IdentifierReference -> translateExpression(expression)
|
||||||
|
is FunctionCall -> {
|
||||||
|
val functionName = expression.target.nameInSource.last()
|
||||||
|
val builtinFunc = BuiltinFunctions[functionName]
|
||||||
|
if(builtinFunc!=null) {
|
||||||
|
asmgen.translateFunctioncallExpression(expression, builtinFunc)
|
||||||
|
} else {
|
||||||
|
asmgen.translateFunctionCall(expression)
|
||||||
|
val sub = expression.target.targetSubroutine(program.namespace)!!
|
||||||
|
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
|
||||||
|
for((_, reg) in returns) {
|
||||||
|
if(!reg.stack) {
|
||||||
|
// result value in cpu or status registers, put it on the stack
|
||||||
|
if(reg.registerOrPair!=null) {
|
||||||
|
when(reg.registerOrPair) {
|
||||||
|
RegisterOrPair.A -> asmgen.out(" sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
RegisterOrPair.Y -> asmgen.out(" tya | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" sta ${MachineDefinition.ESTACK_LO_HEX},x | tya | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
|
||||||
|
RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY -> throw AssemblyError("can't push X register - use a variable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// return value from a statusregister is not put on the stack, it should be acted on via a conditional branch such as if_cc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is ArrayLiteralValue, is StringLiteralValue -> TODO("string/array/struct assignment?")
|
||||||
|
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened")
|
||||||
|
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: TypecastExpression) {
|
||||||
|
translateExpression(expr.expression)
|
||||||
|
when(expr.expression.inferType(program).typeOrElse(DataType.STRUCT)) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {}
|
||||||
|
DataType.UWORD, DataType.WORD -> asmgen.out(" lda #0 | sta ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x")
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_ub2float")
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {}
|
||||||
|
DataType.UWORD, DataType.WORD -> asmgen.out(" lda ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x | ${asmgen.signExtendAtoMsb("${MachineDefinition.ESTACK_HI_PLUS1_HEX},x")}")
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_b2float")
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.BYTE, DataType.UBYTE -> {}
|
||||||
|
DataType.WORD, DataType.UWORD -> {}
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_uw2float")
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.BYTE, DataType.UBYTE -> {}
|
||||||
|
DataType.WORD, DataType.UWORD -> {}
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_w2float")
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.UBYTE -> asmgen.out(" jsr c64flt.stack_float2uw")
|
||||||
|
DataType.BYTE -> asmgen.out(" jsr c64flt.stack_float2w")
|
||||||
|
DataType.UWORD -> asmgen.out(" jsr c64flt.stack_float2uw")
|
||||||
|
DataType.WORD -> asmgen.out(" jsr c64flt.stack_float2w")
|
||||||
|
DataType.FLOAT -> {}
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot case a pass-by-reference datatypes into something else")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: AddressOf) {
|
||||||
|
val name = asmgen.asmIdentifierName(expr.identifier)
|
||||||
|
asmgen.out(" lda #<$name | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda #>$name | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: DirectMemoryRead) {
|
||||||
|
when(expr.addressExpression) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val address = (expr.addressExpression as NumericLiteralValue).number.toInt()
|
||||||
|
asmgen.out(" lda ${address.toHex()} | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(expr.addressExpression as IdentifierReference)
|
||||||
|
asmgen.out(" lda $sourceName | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
translateExpression(expr.addressExpression)
|
||||||
|
asmgen.out(" jsr prog8_lib.read_byte_from_address")
|
||||||
|
asmgen.out(" sta ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: NumericLiteralValue) {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
DataType.UWORD, DataType.WORD -> asmgen.out("""
|
||||||
|
lda #<${expr.number.toHex()}
|
||||||
|
sta ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
lda #>${expr.number.toHex()}
|
||||||
|
sta ${MachineDefinition.ESTACK_HI_HEX},x
|
||||||
|
dex
|
||||||
|
""")
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
val floatConst = asmgen.getFloatConst(expr.number.toDouble())
|
||||||
|
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr c64flt.push_float")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: RegisterExpr) {
|
||||||
|
when(expr.register) {
|
||||||
|
Register.A -> asmgen.out(" sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
Register.X -> throw AssemblyError("cannot push X - use a variable instead of the X register")
|
||||||
|
Register.Y -> asmgen.out(" tya | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: IdentifierReference) {
|
||||||
|
val varname = asmgen.asmIdentifierName(expr)
|
||||||
|
when(expr.inferType(program).typeOrElse(DataType.STRUCT)) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
asmgen.out(" lda $varname | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD, in ArrayDatatypes, in StringDatatypes -> {
|
||||||
|
// (for arrays and strings, push their address)
|
||||||
|
asmgen.out(" lda $varname | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda $varname+1 | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out(" lda #<$varname | ldy #>$varname| jsr c64flt.push_float")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("stack push weird variable type $expr")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40)
|
||||||
|
private val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40)
|
||||||
|
private val powerOfTwos = setOf(0,1,2,4,8,16,32,64,128,256)
|
||||||
|
|
||||||
|
private fun translateExpression(expr: BinaryExpression) {
|
||||||
|
val leftIDt = expr.left.inferType(program)
|
||||||
|
val rightIDt = expr.right.inferType(program)
|
||||||
|
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
||||||
|
throw AssemblyError("can't infer type of both expression operands")
|
||||||
|
|
||||||
|
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
val rightDt = rightIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
// see if we can apply some optimized routines
|
||||||
|
when(expr.operator) {
|
||||||
|
">>" -> {
|
||||||
|
// bit-shifts are always by a constant number (for now)
|
||||||
|
translateExpression(expr.left)
|
||||||
|
val amount = expr.right.constValue(program)!!.number.toInt()
|
||||||
|
when (leftDt) {
|
||||||
|
DataType.UBYTE -> repeat(amount) { asmgen.out(" lsr ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x") }
|
||||||
|
DataType.BYTE -> repeat(amount) { asmgen.out(" lda ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x | asl a | ror ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x") }
|
||||||
|
DataType.UWORD -> repeat(amount) { asmgen.out(" lsr ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x | ror ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x") }
|
||||||
|
DataType.WORD -> repeat(amount) { asmgen.out(" lda ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x | asl a | ror ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x | ror ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x") }
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
"<<" -> {
|
||||||
|
// bit-shifts are always by a constant number (for now)
|
||||||
|
translateExpression(expr.left)
|
||||||
|
val amount = expr.right.constValue(program)!!.number.toInt()
|
||||||
|
if (leftDt in ByteDatatypes)
|
||||||
|
repeat(amount) { asmgen.out(" asl ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x") }
|
||||||
|
else
|
||||||
|
repeat(amount) { asmgen.out(" asl ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x | rol ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x") }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
"*" -> {
|
||||||
|
val value = expr.right.constValue(program)
|
||||||
|
if(value!=null) {
|
||||||
|
if(rightDt in IntegerDatatypes) {
|
||||||
|
val amount = value.number.toInt()
|
||||||
|
if(amount in powerOfTwos)
|
||||||
|
printWarning("${expr.right.position} multiplication by power of 2 should have been optimized into a left shift instruction: $amount")
|
||||||
|
when(rightDt) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
if(amount in optimizedByteMultiplications) {
|
||||||
|
translateExpression(expr.left)
|
||||||
|
asmgen.out(" jsr math.mul_byte_$amount")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
if(amount in optimizedByteMultiplications) {
|
||||||
|
translateExpression(expr.left)
|
||||||
|
asmgen.out(" jsr math.mul_byte_$amount")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(amount.absoluteValue in optimizedByteMultiplications) {
|
||||||
|
translateExpression(expr.left)
|
||||||
|
asmgen.out(" jsr prog8_lib.neg_b | jsr math.mul_byte_${amount.absoluteValue}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
if(amount in optimizedWordMultiplications) {
|
||||||
|
translateExpression(expr.left)
|
||||||
|
asmgen.out(" jsr math.mul_word_$amount")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
if(amount in optimizedWordMultiplications) {
|
||||||
|
translateExpression(expr.left)
|
||||||
|
asmgen.out(" jsr math.mul_word_$amount")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(amount.absoluteValue in optimizedWordMultiplications) {
|
||||||
|
translateExpression(expr.left)
|
||||||
|
asmgen.out(" jsr prog8_lib.neg_w | jsr math.mul_word_${amount.absoluteValue}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the general, non-optimized cases
|
||||||
|
translateExpression(expr.left)
|
||||||
|
translateExpression(expr.right)
|
||||||
|
if(leftDt!=rightDt)
|
||||||
|
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical") // is this strictly required always?
|
||||||
|
when (leftDt) {
|
||||||
|
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
|
||||||
|
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
|
||||||
|
DataType.FLOAT -> translateBinaryOperatorFloats(expr.operator)
|
||||||
|
else -> throw AssemblyError("non-numerical datatype")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: PrefixExpression) {
|
||||||
|
translateExpression(expr.expression)
|
||||||
|
val type = expr.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
when(expr.operator) {
|
||||||
|
"+" -> {}
|
||||||
|
"-" -> {
|
||||||
|
when(type) {
|
||||||
|
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b")
|
||||||
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w")
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.neg_f")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"~" -> {
|
||||||
|
when(type) {
|
||||||
|
in ByteDatatypes ->
|
||||||
|
asmgen.out("""
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
||||||
|
eor #255
|
||||||
|
sta ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
||||||
|
""")
|
||||||
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.inv_word")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"not" -> {
|
||||||
|
when(type) {
|
||||||
|
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.not_byte")
|
||||||
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.not_word")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid prefix operator ${expr.operator}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun translatePushFromArray(arrayExpr: ArrayIndexedExpression) {
|
||||||
|
// assume *reading* from an array
|
||||||
|
val index = arrayExpr.arrayspec.index
|
||||||
|
val arrayDt = arrayExpr.identifier.targetVarDecl(program.namespace)!!.datatype
|
||||||
|
val arrayVarName = asmgen.asmIdentifierName(arrayExpr.identifier)
|
||||||
|
if(index is NumericLiteralValue) {
|
||||||
|
val elementDt = ArrayElementTypes.getValue(arrayDt)
|
||||||
|
val indexValue = index.number.toInt() * elementDt.memorySize()
|
||||||
|
when(elementDt) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
asmgen.out(" lda $arrayVarName+$indexValue | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
asmgen.out(" lda $arrayVarName+$indexValue | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda $arrayVarName+$indexValue+1 | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr c64flt.push_float")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
asmgen.translateArrayIndexIntoA(arrayExpr)
|
||||||
|
asmgen.readAndPushArrayvalueWithIndexA(arrayDt, arrayExpr.identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateBinaryOperatorBytes(operator: String, types: DataType) {
|
||||||
|
when(operator) {
|
||||||
|
"**" -> throw AssemblyError("** operator requires floats")
|
||||||
|
"*" -> asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier
|
||||||
|
"/" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
|
||||||
|
"%" -> {
|
||||||
|
if(types==DataType.BYTE)
|
||||||
|
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||||
|
asmgen.out(" jsr prog8_lib.remainder_ub")
|
||||||
|
}
|
||||||
|
"+" -> asmgen.out("""
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_PLUS2_HEX},x
|
||||||
|
clc
|
||||||
|
adc ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
||||||
|
inx
|
||||||
|
sta ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
||||||
|
""")
|
||||||
|
"-" -> asmgen.out("""
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_PLUS2_HEX},x
|
||||||
|
sec
|
||||||
|
sbc ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
||||||
|
inx
|
||||||
|
sta ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
||||||
|
""")
|
||||||
|
"<<", ">>" -> throw AssemblyError("bit-shifts not via stack")
|
||||||
|
"<" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.less_ub" else " jsr prog8_lib.less_b")
|
||||||
|
">" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greater_ub" else " jsr prog8_lib.greater_b")
|
||||||
|
"<=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.lesseq_ub" else " jsr prog8_lib.lesseq_b")
|
||||||
|
">=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greatereq_ub" else " jsr prog8_lib.greatereq_b")
|
||||||
|
"==" -> asmgen.out(" jsr prog8_lib.equal_b")
|
||||||
|
"!=" -> asmgen.out(" jsr prog8_lib.notequal_b")
|
||||||
|
"&" -> asmgen.out(" jsr prog8_lib.bitand_b")
|
||||||
|
"^" -> asmgen.out(" jsr prog8_lib.bitxor_b")
|
||||||
|
"|" -> asmgen.out(" jsr prog8_lib.bitor_b")
|
||||||
|
"and" -> asmgen.out(" jsr prog8_lib.and_b")
|
||||||
|
"or" -> asmgen.out(" jsr prog8_lib.or_b")
|
||||||
|
"xor" -> asmgen.out(" jsr prog8_lib.xor_b")
|
||||||
|
else -> throw AssemblyError("invalid operator $operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateBinaryOperatorWords(operator: String, types: DataType) {
|
||||||
|
when(operator) {
|
||||||
|
"**" -> throw AssemblyError("** operator requires floats")
|
||||||
|
"*" -> asmgen.out(" jsr prog8_lib.mul_word")
|
||||||
|
"/" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w")
|
||||||
|
"%" -> {
|
||||||
|
if(types==DataType.WORD)
|
||||||
|
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||||
|
asmgen.out(" jsr prog8_lib.remainder_uw")
|
||||||
|
}
|
||||||
|
"+" -> asmgen.out(" jsr prog8_lib.add_w")
|
||||||
|
"-" -> asmgen.out(" jsr prog8_lib.sub_w")
|
||||||
|
"<<" -> throw AssemblyError("<< should not operate via stack")
|
||||||
|
">>" -> throw AssemblyError(">> should not operate via stack")
|
||||||
|
"<" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
|
||||||
|
">" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
|
||||||
|
"<=" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
|
||||||
|
">=" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
|
||||||
|
"==" -> asmgen.out(" jsr prog8_lib.equal_w")
|
||||||
|
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w")
|
||||||
|
"&" -> asmgen.out(" jsr prog8_lib.bitand_w")
|
||||||
|
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
|
||||||
|
"|" -> asmgen.out(" jsr prog8_lib.bitor_w")
|
||||||
|
"and" -> asmgen.out(" jsr prog8_lib.and_w")
|
||||||
|
"or" -> asmgen.out(" jsr prog8_lib.or_w")
|
||||||
|
"xor" -> asmgen.out(" jsr prog8_lib.xor_w")
|
||||||
|
else -> throw AssemblyError("invalid operator $operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateBinaryOperatorFloats(operator: String) {
|
||||||
|
when(operator) {
|
||||||
|
"**" -> asmgen.out(" jsr c64flt.pow_f")
|
||||||
|
"*" -> asmgen.out(" jsr c64flt.mul_f")
|
||||||
|
"/" -> asmgen.out(" jsr c64flt.div_f")
|
||||||
|
"+" -> asmgen.out(" jsr c64flt.add_f")
|
||||||
|
"-" -> asmgen.out(" jsr c64flt.sub_f")
|
||||||
|
"<" -> asmgen.out(" jsr c64flt.less_f")
|
||||||
|
">" -> asmgen.out(" jsr c64flt.greater_f")
|
||||||
|
"<=" -> asmgen.out(" jsr c64flt.lesseq_f")
|
||||||
|
">=" -> asmgen.out(" jsr c64flt.greatereq_f")
|
||||||
|
"==" -> asmgen.out(" jsr c64flt.equal_f")
|
||||||
|
"!=" -> asmgen.out(" jsr c64flt.notequal_f")
|
||||||
|
"%", "<<", ">>", "&", "^", "|", "and", "or", "xor" -> throw AssemblyError("requires integer datatype")
|
||||||
|
else -> throw AssemblyError("invalid operator $operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
559
compiler/src/prog8/compiler/target/c64/codegen/ForLoopsAsmGen.kt
Normal file
559
compiler/src/prog8/compiler/target/c64/codegen/ForLoopsAsmGen.kt
Normal file
@ -0,0 +1,559 @@
|
|||||||
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.Register
|
||||||
|
import prog8.ast.expressions.IdentifierReference
|
||||||
|
import prog8.ast.expressions.RangeExpr
|
||||||
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
|
import prog8.ast.statements.ForLoop
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
|
import prog8.compiler.toHex
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
|
||||||
|
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
|
||||||
|
internal fun translate(stmt: ForLoop) {
|
||||||
|
val iterableDt = stmt.iterable.inferType(program)
|
||||||
|
if(!iterableDt.isKnown)
|
||||||
|
throw AssemblyError("can't determine iterable dt")
|
||||||
|
when(stmt.iterable) {
|
||||||
|
is RangeExpr -> {
|
||||||
|
val range = (stmt.iterable as RangeExpr).toConstantIntegerRange()
|
||||||
|
if(range==null) {
|
||||||
|
translateForOverNonconstRange(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as RangeExpr)
|
||||||
|
} else {
|
||||||
|
if (range.isEmpty())
|
||||||
|
throw AssemblyError("empty range")
|
||||||
|
translateForOverConstRange(stmt, iterableDt.typeOrElse(DataType.STRUCT), range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
translateForOverIterableVar(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as IdentifierReference)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("can't iterate over ${stmt.iterable}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpr) {
|
||||||
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
|
val continueLabel = asmgen.makeLabel("for_continue")
|
||||||
|
val counterLabel = asmgen.makeLabel("for_counter")
|
||||||
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
|
asmgen.loopContinueLabels.push(continueLabel)
|
||||||
|
val stepsize=range.step.constValue(program)?.number
|
||||||
|
when (stepsize) {
|
||||||
|
1 -> {
|
||||||
|
when(iterableDt) {
|
||||||
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
|
if (stmt.loopRegister != null) {
|
||||||
|
// loop register over range
|
||||||
|
if(stmt.loopRegister!= Register.A)
|
||||||
|
throw AssemblyError("can only use A")
|
||||||
|
asmgen.translateExpression(range.to)
|
||||||
|
asmgen.translateExpression(range.from)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
sta $loopLabel+1
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
sec
|
||||||
|
sbc $loopLabel+1
|
||||||
|
adc #0
|
||||||
|
sta $counterLabel
|
||||||
|
$loopLabel lda #0 ; modified""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
inc $loopLabel+1
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
} else {
|
||||||
|
// loop over byte range via loopvar
|
||||||
|
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
||||||
|
asmgen.translateExpression(range.to)
|
||||||
|
asmgen.translateExpression(range.from)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
sta $varname
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
sec
|
||||||
|
sbc $varname
|
||||||
|
adc #0
|
||||||
|
sta $counterLabel
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
inc $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
|
asmgen.translateExpression(range.to)
|
||||||
|
asmgen.out(" inc ${MachineDefinition.ESTACK_LO_HEX}+1,x | bne + | inc ${MachineDefinition.ESTACK_HI_HEX}+1,x |+ ")
|
||||||
|
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
||||||
|
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position),
|
||||||
|
null, range.from, range.position)
|
||||||
|
assignLoopvar.linkParents(stmt)
|
||||||
|
asmgen.translate(assignLoopvar)
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
inc $varname
|
||||||
|
bne +
|
||||||
|
inc $varname+1
|
||||||
|
+ lda ${MachineDefinition.ESTACK_HI_HEX}+1,x
|
||||||
|
cmp $varname+1
|
||||||
|
bne +
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX}+1,x
|
||||||
|
cmp $varname
|
||||||
|
beq $endLabel
|
||||||
|
+ jmp $loopLabel
|
||||||
|
$endLabel inx""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("range expression can only be byte or word")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-1 -> {
|
||||||
|
when(iterableDt){
|
||||||
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
|
if (stmt.loopRegister != null) {
|
||||||
|
// loop register over range
|
||||||
|
if(stmt.loopRegister!= Register.A)
|
||||||
|
throw AssemblyError("can only use A")
|
||||||
|
asmgen.translateExpression(range.from)
|
||||||
|
asmgen.translateExpression(range.to)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX}+1,x
|
||||||
|
sta $loopLabel+1
|
||||||
|
sec
|
||||||
|
sbc ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
adc #0
|
||||||
|
sta $counterLabel
|
||||||
|
inx
|
||||||
|
$loopLabel lda #0 ; modified""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
dec $loopLabel+1
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
} else {
|
||||||
|
// loop over byte range via loopvar
|
||||||
|
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
||||||
|
asmgen.translateExpression(range.from)
|
||||||
|
asmgen.translateExpression(range.to)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX}+1,x
|
||||||
|
sta $varname
|
||||||
|
sec
|
||||||
|
sbc ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
adc #0
|
||||||
|
sta $counterLabel
|
||||||
|
inx
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
dec $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
|
asmgen.translateExpression(range.to)
|
||||||
|
asmgen.out("""
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX}+1,x
|
||||||
|
bne +
|
||||||
|
dec ${MachineDefinition.ESTACK_HI_HEX}+1,x
|
||||||
|
+ dec ${MachineDefinition.ESTACK_LO_HEX}+1,x
|
||||||
|
""")
|
||||||
|
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
||||||
|
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position),
|
||||||
|
null, range.from, range.position)
|
||||||
|
assignLoopvar.linkParents(stmt)
|
||||||
|
asmgen.translate(assignLoopvar)
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
bne +
|
||||||
|
dec $varname+1
|
||||||
|
+ dec $varname
|
||||||
|
lda ${MachineDefinition.ESTACK_HI_HEX}+1,x
|
||||||
|
cmp $varname+1
|
||||||
|
bne +
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX}+1,x
|
||||||
|
cmp $varname
|
||||||
|
beq $endLabel
|
||||||
|
+ jmp $loopLabel
|
||||||
|
$endLabel inx""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("range expression can only be byte or word")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> when (iterableDt) {
|
||||||
|
DataType.ARRAY_UB, DataType.ARRAY_B -> TODO("non-const forloop bytes, step >1: $stepsize")
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W -> TODO("non-const forloop words, step >1: $stepsize")
|
||||||
|
else -> throw AssemblyError("range expression can only be byte or word")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
asmgen.loopEndLabels.pop()
|
||||||
|
asmgen.loopContinueLabels.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) {
|
||||||
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
|
val continueLabel = asmgen.makeLabel("for_continue")
|
||||||
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
|
asmgen.loopContinueLabels.push(continueLabel)
|
||||||
|
val iterableName = asmgen.asmIdentifierName(ident)
|
||||||
|
val decl = ident.targetVarDecl(program.namespace)!!
|
||||||
|
when(iterableDt) {
|
||||||
|
DataType.STR, DataType.STR_S -> {
|
||||||
|
if(stmt.loopRegister!=null && stmt.loopRegister!= Register.A)
|
||||||
|
throw AssemblyError("can only use A")
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$iterableName
|
||||||
|
ldy #>$iterableName
|
||||||
|
sta $loopLabel+1
|
||||||
|
sty $loopLabel+2
|
||||||
|
$loopLabel lda ${65535.toHex()} ; modified
|
||||||
|
beq $endLabel""")
|
||||||
|
if(stmt.loopVar!=null)
|
||||||
|
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar!!)}")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel inc $loopLabel+1
|
||||||
|
bne $loopLabel
|
||||||
|
inc $loopLabel+2
|
||||||
|
bne $loopLabel
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
|
val length = decl.arraysize!!.size()!!
|
||||||
|
if(stmt.loopRegister!=null && stmt.loopRegister!= Register.A)
|
||||||
|
throw AssemblyError("can only use A")
|
||||||
|
val counterLabel = asmgen.makeLabel("for_counter")
|
||||||
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$iterableName
|
||||||
|
ldy #>$iterableName
|
||||||
|
sta $modifiedLabel+1
|
||||||
|
sty $modifiedLabel+2
|
||||||
|
ldy #0
|
||||||
|
$loopLabel sty $counterLabel
|
||||||
|
$modifiedLabel lda ${65535.toHex()},y ; modified""")
|
||||||
|
if(stmt.loopVar!=null)
|
||||||
|
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar!!)}")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel ldy $counterLabel
|
||||||
|
iny
|
||||||
|
cpy #${length and 255}
|
||||||
|
beq $endLabel
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
|
val length = decl.arraysize!!.size()!! * 2
|
||||||
|
if(stmt.loopRegister!=null)
|
||||||
|
throw AssemblyError("can't use register to loop over words")
|
||||||
|
val counterLabel = asmgen.makeLabel("for_counter")
|
||||||
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
|
val modifiedLabel2 = asmgen.makeLabel("for_modified2")
|
||||||
|
val loopvarName = asmgen.asmIdentifierName(stmt.loopVar!!)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$iterableName
|
||||||
|
ldy #>$iterableName
|
||||||
|
sta $modifiedLabel+1
|
||||||
|
sty $modifiedLabel+2
|
||||||
|
lda #<$iterableName+1
|
||||||
|
ldy #>$iterableName+1
|
||||||
|
sta $modifiedLabel2+1
|
||||||
|
sty $modifiedLabel2+2
|
||||||
|
ldy #0
|
||||||
|
$loopLabel sty $counterLabel
|
||||||
|
$modifiedLabel lda ${65535.toHex()},y ; modified
|
||||||
|
sta $loopvarName
|
||||||
|
$modifiedLabel2 lda ${65535.toHex()},y ; modified
|
||||||
|
sta $loopvarName+1""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel ldy $counterLabel
|
||||||
|
iny
|
||||||
|
iny
|
||||||
|
cpy #${length and 255}
|
||||||
|
beq $endLabel
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> {
|
||||||
|
throw AssemblyError("for loop with floating point variables is not supported")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("can't iterate over $iterableDt")
|
||||||
|
}
|
||||||
|
asmgen.loopEndLabels.pop()
|
||||||
|
asmgen.loopContinueLabels.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) {
|
||||||
|
// TODO: optimize loop code when the range is < 256 iterations, don't need a separate counter in such cases
|
||||||
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
|
val continueLabel = asmgen.makeLabel("for_continue")
|
||||||
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
|
asmgen.loopContinueLabels.push(continueLabel)
|
||||||
|
when(iterableDt) {
|
||||||
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
|
val counterLabel = asmgen.makeLabel("for_counter")
|
||||||
|
if(stmt.loopRegister!=null) {
|
||||||
|
|
||||||
|
// loop register over range
|
||||||
|
|
||||||
|
if(stmt.loopRegister!= Register.A)
|
||||||
|
throw AssemblyError("can only use A")
|
||||||
|
when {
|
||||||
|
range.step==1 -> {
|
||||||
|
// step = 1
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${range.first}
|
||||||
|
sta $loopLabel+1
|
||||||
|
lda #${range.last-range.first+1 and 255}
|
||||||
|
sta $counterLabel
|
||||||
|
$loopLabel lda #0 ; modified""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
inc $loopLabel+1
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
range.step==-1 -> {
|
||||||
|
// step = -1
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${range.first}
|
||||||
|
sta $loopLabel+1
|
||||||
|
lda #${range.first-range.last+1 and 255}
|
||||||
|
sta $counterLabel
|
||||||
|
$loopLabel lda #0 ; modified """)
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
dec $loopLabel+1
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
range.step >= 2 -> {
|
||||||
|
// step >= 2
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${(range.last-range.first) / range.step + 1}
|
||||||
|
sta $counterLabel
|
||||||
|
lda #${range.first}
|
||||||
|
$loopLabel pha""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel pla
|
||||||
|
dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
clc
|
||||||
|
adc #${range.step}
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// step <= -2
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${(range.first-range.last) / range.step.absoluteValue + 1}
|
||||||
|
sta $counterLabel
|
||||||
|
lda #${range.first}
|
||||||
|
$loopLabel pha""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel pla
|
||||||
|
dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
sec
|
||||||
|
sbc #${range.step.absoluteValue}
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// loop over byte range via loopvar
|
||||||
|
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
||||||
|
when {
|
||||||
|
range.step==1 -> {
|
||||||
|
// step = 1
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${range.first}
|
||||||
|
sta $varname
|
||||||
|
lda #${range.last-range.first+1 and 255}
|
||||||
|
sta $counterLabel
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
inc $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
range.step==-1 -> {
|
||||||
|
// step = -1
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${range.first}
|
||||||
|
sta $varname
|
||||||
|
lda #${range.first-range.last+1 and 255}
|
||||||
|
sta $counterLabel
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
dec $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
range.step >= 2 -> {
|
||||||
|
// step >= 2
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${(range.last-range.first) / range.step + 1}
|
||||||
|
sta $counterLabel
|
||||||
|
lda #${range.first}
|
||||||
|
sta $varname
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
lda $varname
|
||||||
|
clc
|
||||||
|
adc #${range.step}
|
||||||
|
sta $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// step <= -2
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${(range.first-range.last) / range.step.absoluteValue + 1}
|
||||||
|
sta $counterLabel
|
||||||
|
lda #${range.first}
|
||||||
|
sta $varname
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
lda $varname
|
||||||
|
sec
|
||||||
|
sbc #${range.step.absoluteValue}
|
||||||
|
sta $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
|
// loop over word range via loopvar
|
||||||
|
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
||||||
|
when {
|
||||||
|
range.step == 1 -> {
|
||||||
|
// word, step = 1
|
||||||
|
val lastValue = range.last+1
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${range.first}
|
||||||
|
ldy #>${range.first}
|
||||||
|
sta $varname
|
||||||
|
sty $varname+1
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel inc $varname
|
||||||
|
bne +
|
||||||
|
inc $varname+1
|
||||||
|
+ lda $varname
|
||||||
|
cmp #<$lastValue
|
||||||
|
bne +
|
||||||
|
lda $varname+1
|
||||||
|
cmp #>$lastValue
|
||||||
|
beq $endLabel
|
||||||
|
+ jmp $loopLabel
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
range.step == -1 -> {
|
||||||
|
// word, step = 1
|
||||||
|
val lastValue = range.last-1
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${range.first}
|
||||||
|
ldy #>${range.first}
|
||||||
|
sta $varname
|
||||||
|
sty $varname+1
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel lda $varname
|
||||||
|
bne +
|
||||||
|
dec $varname+1
|
||||||
|
+ dec $varname
|
||||||
|
lda $varname
|
||||||
|
cmp #<$lastValue
|
||||||
|
bne +
|
||||||
|
lda $varname+1
|
||||||
|
cmp #>$lastValue
|
||||||
|
beq $endLabel
|
||||||
|
+ jmp $loopLabel
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
range.step >= 2 -> {
|
||||||
|
// word, step >= 2
|
||||||
|
TODO("for, word, step>=2")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// step <= -2
|
||||||
|
TODO("for, word, step<=-2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("range expression can only be byte or word")
|
||||||
|
}
|
||||||
|
asmgen.loopEndLabels.pop()
|
||||||
|
asmgen.loopContinueLabels.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,235 @@
|
|||||||
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
|
import prog8.ast.statements.SubroutineParameter
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
|
import prog8.compiler.toHex
|
||||||
|
|
||||||
|
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
|
||||||
|
internal fun translateFunctionCall(stmt: IFunctionCall) {
|
||||||
|
// output the code to setup the parameters and perform the actual call
|
||||||
|
// does NOT output the code to deal with the result values!
|
||||||
|
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||||
|
if(Register.X in sub.asmClobbers)
|
||||||
|
asmgen.out(" stx c64.SCRATCH_ZPREGX") // we only save X for now (required! is the eval stack pointer), screw A and Y...
|
||||||
|
|
||||||
|
val subName = asmgen.asmIdentifierName(stmt.target)
|
||||||
|
if(stmt.arglist.isNotEmpty()) {
|
||||||
|
for(arg in sub.parameters.withIndex().zip(stmt.arglist)) {
|
||||||
|
translateFuncArguments(arg.first, arg.second, sub)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
asmgen.out(" jsr $subName")
|
||||||
|
|
||||||
|
if(Register.X in sub.asmClobbers)
|
||||||
|
asmgen.out(" ldx c64.SCRATCH_ZPREGX") // restore X again
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateFuncArguments(parameter: IndexedValue<SubroutineParameter>, value: Expression, sub: Subroutine) {
|
||||||
|
val sourceIDt = value.inferType(program)
|
||||||
|
if(!sourceIDt.isKnown)
|
||||||
|
throw AssemblyError("arg type unknown")
|
||||||
|
val sourceDt = sourceIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
if(!argumentTypeCompatible(sourceDt, parameter.value.type))
|
||||||
|
throw AssemblyError("argument type incompatible")
|
||||||
|
if(sub.asmParameterRegisters.isEmpty()) {
|
||||||
|
// pass parameter via a variable
|
||||||
|
val paramVar = parameter.value
|
||||||
|
val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
|
||||||
|
val target = AssignTarget(null, IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
|
||||||
|
target.linkParents(value.parent)
|
||||||
|
when (value) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
// optimize when the argument is a constant literal
|
||||||
|
when(parameter.value.type) {
|
||||||
|
in ByteDatatypes -> asmgen.assignFromByteConstant(target, value.number.toShort())
|
||||||
|
in WordDatatypes -> asmgen.assignFromWordConstant(target, value.number.toInt())
|
||||||
|
DataType.FLOAT -> asmgen.assignFromFloatConstant(target, value.number.toDouble())
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("can't pass string/array as arguments?")
|
||||||
|
else -> throw AssemblyError("weird parameter datatype")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
// optimize when the argument is a variable
|
||||||
|
when (parameter.value.type) {
|
||||||
|
in ByteDatatypes -> asmgen.assignFromByteVariable(target, value)
|
||||||
|
in WordDatatypes -> asmgen.assignFromWordVariable(target, value)
|
||||||
|
DataType.FLOAT -> asmgen.assignFromFloatVariable(target, value)
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("can't pass string/array as arguments?")
|
||||||
|
else -> throw AssemblyError("weird parameter datatype")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RegisterExpr -> {
|
||||||
|
asmgen.assignFromRegister(target, value.register)
|
||||||
|
}
|
||||||
|
is DirectMemoryRead -> {
|
||||||
|
when(value.addressExpression) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
|
||||||
|
asmgen.assignFromMemoryByte(target, address, null)
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
asmgen.assignFromMemoryByte(target, null, value.addressExpression as IdentifierReference)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(value.addressExpression)
|
||||||
|
asmgen.out(" jsr prog8_lib.read_byte_from_address | inx")
|
||||||
|
asmgen.assignFromRegister(target, Register.A)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(value)
|
||||||
|
asmgen.assignFromEvalResult(target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// pass parameter via a register parameter
|
||||||
|
val paramRegister = sub.asmParameterRegisters[parameter.index]
|
||||||
|
val statusflag = paramRegister.statusflag
|
||||||
|
val register = paramRegister.registerOrPair
|
||||||
|
val stack = paramRegister.stack
|
||||||
|
when {
|
||||||
|
stack -> {
|
||||||
|
// push arg onto the stack
|
||||||
|
// note: argument order is reversed (first argument will be deepest on the stack)
|
||||||
|
asmgen.translateExpression(value)
|
||||||
|
}
|
||||||
|
statusflag!=null -> {
|
||||||
|
if (statusflag == Statusflag.Pc) {
|
||||||
|
// this param needs to be set last, right before the jsr
|
||||||
|
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
||||||
|
when(value) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val carrySet = value.number.toInt() != 0
|
||||||
|
asmgen.out(if(carrySet) " sec" else " clc")
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(value)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
beq +
|
||||||
|
sec
|
||||||
|
bcs ++
|
||||||
|
+ clc
|
||||||
|
+
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
is RegisterExpr -> {
|
||||||
|
when(value.register) {
|
||||||
|
Register.A -> asmgen.out(" cmp #0")
|
||||||
|
Register.X -> asmgen.out(" txa")
|
||||||
|
Register.Y -> asmgen.out(" tya")
|
||||||
|
}
|
||||||
|
asmgen.out("""
|
||||||
|
beq +
|
||||||
|
sec
|
||||||
|
bcs ++
|
||||||
|
+ clc
|
||||||
|
+
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(value)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
beq +
|
||||||
|
sec
|
||||||
|
bcs ++
|
||||||
|
+ clc
|
||||||
|
+
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else throw AssemblyError("can only use Carry as status flag parameter")
|
||||||
|
}
|
||||||
|
register!=null && register.name.length==1 -> {
|
||||||
|
when (value) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position)
|
||||||
|
target.linkParents(value.parent)
|
||||||
|
asmgen.assignFromByteConstant(target, value.number.toShort())
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position)
|
||||||
|
target.linkParents(value.parent)
|
||||||
|
asmgen.assignFromByteVariable(target, value)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(value)
|
||||||
|
when(register) {
|
||||||
|
RegisterOrPair.A -> asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x")
|
||||||
|
RegisterOrPair.X -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||||
|
RegisterOrPair.Y -> asmgen.out(" inx | ldy ${MachineDefinition.ESTACK_LO_HEX},x")
|
||||||
|
else -> throw AssemblyError("cannot assign to register pair")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
register!=null && register.name.length==2 -> {
|
||||||
|
// register pair as a 16-bit value (only possible for subroutine parameters)
|
||||||
|
when (value) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
// optimize when the argument is a constant literal
|
||||||
|
val hex = value.number.toHex()
|
||||||
|
when (register) {
|
||||||
|
RegisterOrPair.AX -> asmgen.out(" lda #<$hex | ldx #>$hex")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" lda #<$hex | ldy #>$hex")
|
||||||
|
RegisterOrPair.XY -> asmgen.out(" ldx #<$hex | ldy #>$hex")
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is AddressOf -> {
|
||||||
|
// optimize when the argument is an address of something
|
||||||
|
val sourceName = asmgen.asmIdentifierName(value.identifier)
|
||||||
|
when (register) {
|
||||||
|
RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
||||||
|
RegisterOrPair.XY -> asmgen.out(" ldx #<$sourceName | ldy #>$sourceName")
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(value)
|
||||||
|
when (register) {
|
||||||
|
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx $sourceName+1")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy $sourceName+1")
|
||||||
|
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy $sourceName+1")
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(value)
|
||||||
|
if (register == RegisterOrPair.AX || register == RegisterOrPair.XY)
|
||||||
|
throw AssemblyError("can't use X register here - use a variable")
|
||||||
|
else if (register == RegisterOrPair.AY)
|
||||||
|
asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x | ldy ${MachineDefinition.ESTACK_HI_HEX},x")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun argumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
||||||
|
if(argType isAssignableTo paramType)
|
||||||
|
return true
|
||||||
|
|
||||||
|
// we have a special rule for some types.
|
||||||
|
// strings are assignable to UWORD, for example, and vice versa
|
||||||
|
if(argType in StringDatatypes && paramType==DataType.UWORD)
|
||||||
|
return true
|
||||||
|
if(argType==DataType.UWORD && paramType in StringDatatypes)
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,149 @@
|
|||||||
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.IdentifierReference
|
||||||
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
import prog8.ast.expressions.RegisterExpr
|
||||||
|
import prog8.ast.statements.PostIncrDecr
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
|
import prog8.compiler.toHex
|
||||||
|
|
||||||
|
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
internal fun translate(stmt: PostIncrDecr) {
|
||||||
|
val incr = stmt.operator=="++"
|
||||||
|
val targetIdent = stmt.target.identifier
|
||||||
|
val targetMemory = stmt.target.memoryAddress
|
||||||
|
val targetArrayIdx = stmt.target.arrayindexed
|
||||||
|
val targetRegister = stmt.target.register
|
||||||
|
when {
|
||||||
|
targetRegister!=null -> {
|
||||||
|
when(targetRegister) {
|
||||||
|
Register.A -> {
|
||||||
|
if(incr)
|
||||||
|
asmgen.out(" clc | adc #1 ")
|
||||||
|
else
|
||||||
|
asmgen.out(" sec | sbc #1 ")
|
||||||
|
}
|
||||||
|
Register.X -> {
|
||||||
|
if(incr) asmgen.out(" inx") else asmgen.out(" dex")
|
||||||
|
}
|
||||||
|
Register.Y -> {
|
||||||
|
if(incr) asmgen.out(" iny") else asmgen.out(" dey")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val what = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
val dt = stmt.target.inferType(program, stmt).typeOrElse(DataType.STRUCT)
|
||||||
|
when (dt) {
|
||||||
|
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
||||||
|
in WordDatatypes -> {
|
||||||
|
if(incr)
|
||||||
|
asmgen.out(" inc $what | bne + | inc $what+1 |+")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
lda $what
|
||||||
|
bne +
|
||||||
|
dec $what+1
|
||||||
|
+ dec $what
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out(" lda #<$what | ldy #>$what")
|
||||||
|
asmgen.out(if(incr) " jsr c64flt.inc_var_f" else " jsr c64flt.dec_var_f")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("need numeric type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetMemory!=null -> {
|
||||||
|
val addressExpr = targetMemory.addressExpression
|
||||||
|
when (addressExpr) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val what = addressExpr.number.toHex()
|
||||||
|
asmgen.out(if(incr) " inc $what" else " dec $what")
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val what = asmgen.asmIdentifierName(addressExpr)
|
||||||
|
asmgen.out(if(incr) " inc $what" else " dec $what")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird target type $targetMemory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val what = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
val elementDt = ArrayElementTypes.getValue(arrayDt)
|
||||||
|
when(index) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val indexValue = index.number.toInt() * elementDt.memorySize()
|
||||||
|
when(elementDt) {
|
||||||
|
in ByteDatatypes -> asmgen.out(if (incr) " inc $what+$indexValue" else " dec $what+$indexValue")
|
||||||
|
in WordDatatypes -> {
|
||||||
|
if(incr)
|
||||||
|
asmgen.out(" inc $what+$indexValue | bne + | inc $what+$indexValue+1 |+")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
lda $what+$indexValue
|
||||||
|
bne +
|
||||||
|
dec $what+$indexValue+1
|
||||||
|
+ dec $what+$indexValue
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out(" lda #<$what+$indexValue | ldy #>$what+$indexValue")
|
||||||
|
asmgen.out(if(incr) " jsr c64flt.inc_var_f" else " jsr c64flt.dec_var_f")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("need numeric type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RegisterExpr -> {
|
||||||
|
// TODO optimize common cases
|
||||||
|
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||||
|
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
// TODO optimize common cases
|
||||||
|
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||||
|
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// TODO optimize common cases
|
||||||
|
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||||
|
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird target type ${stmt.target}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun incrDecrArrayvalueWithIndexA(incr: Boolean, arrayDt: DataType, arrayVarName: String) {
|
||||||
|
asmgen.out(" stx ${MachineDefinition.C64Zeropage.SCRATCH_REG_X} | tax")
|
||||||
|
when(arrayDt) {
|
||||||
|
DataType.STR, DataType.STR_S,
|
||||||
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
|
asmgen.out(if(incr) " inc $arrayVarName,x" else " dec $arrayVarName,x")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
|
if(incr)
|
||||||
|
asmgen.out(" inc $arrayVarName,x | bne + | inc $arrayVarName+1,x |+")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
lda $arrayVarName,x
|
||||||
|
bne +
|
||||||
|
dec $arrayVarName+1,x
|
||||||
|
+ dec $arrayVarName
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> {
|
||||||
|
asmgen.out(" lda #<$arrayVarName | ldy #>$arrayVarName")
|
||||||
|
asmgen.out(if(incr) " jsr c64flt.inc_indexed_var_f" else " jsr c64flt.dec_indexed_var_f")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird array dt")
|
||||||
|
}
|
||||||
|
asmgen.out(" ldx ${MachineDefinition.C64Zeropage.SCRATCH_REG_X}")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
%import c64lib
|
%import c64lib
|
||||||
%import c64utils
|
%import c64utils
|
||||||
|
|
||||||
|
; TODO: some optimizer breaks this.. the 3d sorting seems broken? or maybe more? runs fine without optimization
|
||||||
|
|
||||||
spritedata $2000 {
|
spritedata $2000 {
|
||||||
; this memory block contains the sprite data
|
; this memory block contains the sprite data
|
||||||
; it must start on an address aligned to 64 bytes.
|
; it must start on an address aligned to 64 bytes.
|
||||||
|
Loading…
Reference in New Issue
Block a user