reintroduce explicit PtAugmentedAssign ast node

This commit is contained in:
Irmen de Jong 2023-02-15 22:50:35 +01:00
parent cf3523f49f
commit 5c3f41f64d
21 changed files with 788 additions and 560 deletions

View File

@ -12,6 +12,7 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
return when(node) { return when(node) {
is PtAssignTarget -> "" is PtAssignTarget -> ""
is PtAssignment -> "<assign>" is PtAssignment -> "<assign>"
is PtAugmentedAssign -> "<inplace-assign> ${node.operator}"
is PtBreakpoint -> "%breakpoint" is PtBreakpoint -> "%breakpoint"
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}" is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
is PtAddressOf -> "&" is PtAddressOf -> "&"
@ -30,7 +31,10 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
is PtIdentifier -> "${node.name} ${type(node.type)}" is PtIdentifier -> "${node.name} ${type(node.type)}"
is PtMachineRegister -> "VMREG#${node.register} ${type(node.type)}" is PtMachineRegister -> "VMREG#${node.register} ${type(node.type)}"
is PtMemoryByte -> "@()" is PtMemoryByte -> "@()"
is PtNumber -> "${node.number.toHex()} ${type(node.type)}" is PtNumber -> {
val numstr = if(node.type == DataType.FLOAT) node.number.toString() else node.number.toHex()
"$numstr ${type(node.type)}"
}
is PtPrefix -> node.operator is PtPrefix -> node.operator
is PtRange -> "<range>" is PtRange -> "<range>"
is PtString -> "\"${node.value.escape()}\"" is PtString -> "\"${node.value.escape()}\""

View File

@ -43,36 +43,22 @@ class PtAssignment(position: Position) : PtNode(position) {
get() = children[0] as PtAssignTarget get() = children[0] as PtAssignTarget
val value: PtExpression val value: PtExpression
get() = children[1] as PtExpression get() = children[1] as PtExpression
}
val isInplaceAssign: Boolean by lazy {
val target = target.children.single() as PtExpression class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position) {
when(val source = value) { val target: PtAssignTarget
is PtArrayIndexer -> { get() = children[0] as PtAssignTarget
if(target is PtArrayIndexer && source.type==target.type) { val value: PtExpression
if(target.variable isSameAs source.variable) { get() = children[1] as PtExpression
target.index isSameAs source.index init {
} require(operator.endsWith('=') || operator in PrefixOperators) {
} "invalid augmented assign operator $operator"
false
}
is PtIdentifier -> target is PtIdentifier && target.type==source.type && target.name==source.name
is PtMachineRegister -> target is PtMachineRegister && target.register==source.register
is PtMemoryByte -> target is PtMemoryByte && target.address isSameAs source.address
is PtNumber -> target is PtNumber && target.type == source.type && target.number==source.number
is PtAddressOf -> target is PtAddressOf && target.identifier isSameAs source.identifier
is PtPrefix -> {
(target is PtPrefix && target.operator==source.operator && target.value isSameAs source.value)
||
(target is PtIdentifier && (source.value as? PtIdentifier)?.name==target.name)
}
is PtTypeCast -> target is PtTypeCast && target.type==source.type && target.value isSameAs source.value
is PtBinaryExpression ->
target isSameAs source.left
else -> false
} }
} }
} }
class PtAssignTarget(position: Position) : PtNode(position) { class PtAssignTarget(position: Position) : PtNode(position) {
val identifier: PtIdentifier? val identifier: PtIdentifier?
get() = children.single() as? PtIdentifier get() = children.single() as? PtIdentifier

View File

@ -5,6 +5,7 @@ val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
val LogicalOperators = setOf("and", "or", "xor", "not") val LogicalOperators = setOf("and", "or", "xor", "not")
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor") val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
val BitwiseOperators = setOf("&", "|", "^", "~") val BitwiseOperators = setOf("&", "|", "^", "~")
val PrefixOperators = setOf("+", "-", "~", "not")
// val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators // val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators
fun invertedComparisonOperator(operator: String) = fun invertedComparisonOperator(operator: String) =

View File

@ -148,7 +148,7 @@ class AsmGen6502Internal (
is PtVariable, is PtMemMapped -> { is PtVariable, is PtMemMapped -> {
val sourceName = asmVariableName(pointervar) val sourceName = asmVariableName(pointervar)
if (isTargetCpu(CpuType.CPU65c02)) { if (isTargetCpu(CpuType.CPU65c02)) {
return if (allocator.isZpVar((target as PtNamedNode).scopedName.split('.'))) { // TODO dotted string return if (allocator.isZpVar((target as PtNamedNode).scopedName.split('.'))) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
out(" lda ($sourceName)") out(" lda ($sourceName)")
sourceName sourceName
@ -162,7 +162,7 @@ class AsmGen6502Internal (
"P8ZP_SCRATCH_W1" "P8ZP_SCRATCH_W1"
} }
} else { } else {
return if (allocator.isZpVar((target as PtNamedNode).scopedName.split('.'))) { // TODO dotted string return if (allocator.isZpVar((target as PtNamedNode).scopedName.split('.'))) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
out(" ldy #0 | lda ($sourceName),y") out(" ldy #0 | lda ($sourceName),y")
sourceName sourceName
@ -185,7 +185,7 @@ class AsmGen6502Internal (
internal fun storeAIntoPointerVar(pointervar: PtIdentifier) { internal fun storeAIntoPointerVar(pointervar: PtIdentifier) {
val sourceName = asmVariableName(pointervar) val sourceName = asmVariableName(pointervar)
if (isTargetCpu(CpuType.CPU65c02)) { if (isTargetCpu(CpuType.CPU65c02)) {
if (allocator.isZpVar(pointervar.name.split('.'))) { // TODO dotted string if (allocator.isZpVar(pointervar.name.split('.'))) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
out(" sta ($sourceName)") out(" sta ($sourceName)")
} else { } else {
@ -197,7 +197,7 @@ class AsmGen6502Internal (
sta (P8ZP_SCRATCH_W2)""") sta (P8ZP_SCRATCH_W2)""")
} }
} else { } else {
if (allocator.isZpVar(pointervar.name.split('.'))) { // TODO dotted string if (allocator.isZpVar(pointervar.name.split('.'))) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
out(" ldy #0 | sta ($sourceName),y") out(" ldy #0 | sta ($sourceName),y")
} else { } else {
@ -337,6 +337,7 @@ class AsmGen6502Internal (
is PtBuiltinFunctionCall -> builtinFunctionsAsmGen.translateFunctioncallStatement(stmt) is PtBuiltinFunctionCall -> builtinFunctionsAsmGen.translateFunctioncallStatement(stmt)
is PtFunctionCall -> functioncallAsmGen.translateFunctionCallStatement(stmt) is PtFunctionCall -> functioncallAsmGen.translateFunctionCallStatement(stmt)
is PtAssignment -> assignmentAsmGen.translate(stmt) is PtAssignment -> assignmentAsmGen.translate(stmt)
is PtAugmentedAssign -> assignmentAsmGen.translate(stmt)
is PtJump -> { is PtJump -> {
val (asmLabel, indirect) = getJumpTarget(stmt) val (asmLabel, indirect) = getJumpTarget(stmt)
jmp(asmLabel, indirect) jmp(asmLabel, indirect)
@ -505,7 +506,7 @@ class AsmGen6502Internal (
translateNormalAssignment( translateNormalAssignment(
AsmAssignment( AsmAssignment(
AsmAssignSource(SourceStorageKind.REGISTER, program, this, target.datatype, register=RegisterOrPair.AY), AsmAssignSource(SourceStorageKind.REGISTER, program, this, target.datatype, register=RegisterOrPair.AY),
target, false, program.memsizer, value.position target, program.memsizer, value.position
) )
) )
} }
@ -962,7 +963,7 @@ $repeatLabel lda $counterVar
} }
} }
internal fun isZpVar(variable: PtIdentifier): Boolean = allocator.isZpVar(variable.name.split('.')) // TODO dotted string internal fun isZpVar(variable: PtIdentifier): Boolean = allocator.isZpVar(variable.name.split('.'))
internal fun jmp(asmLabel: String, indirect: Boolean=false) { internal fun jmp(asmLabel: String, indirect: Boolean=false) {
if(indirect) { if(indirect) {

View File

@ -335,7 +335,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null) AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null)
else else
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, asmgen) AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, asmgen)
val assign = AsmAssignment(src, target, false, program.memsizer, fcall.position) val assign = AsmAssignment(src, target, program.memsizer, fcall.position)
asmgen.translateNormalAssignment(assign) asmgen.translateNormalAssignment(assign)
} }
@ -1089,7 +1089,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
} }
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, conv.dt, null, variableAsmName = varname) val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, conv.dt, null, variableAsmName = varname)
val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position) val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
asmgen.translateNormalAssignment(assign) asmgen.translateNormalAssignment(assign)
} }
conv.reg != null -> { conv.reg != null -> {
@ -1107,7 +1107,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
} }
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, null, asmgen) val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, null, asmgen)
val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position) val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
asmgen.translateNormalAssignment(assign) asmgen.translateNormalAssignment(assign)
} }
else -> throw AssemblyError("callconv") else -> throw AssemblyError("callconv")

View File

@ -221,7 +221,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} else { } else {
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target) AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
} }
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, program.memsizer, Position.DUMMY)) asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY))
} }
} }
} }

View File

@ -22,7 +22,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
allocateZeropageVariables() allocateZeropageVariables()
} }
internal fun isZpVar(scopedName: List<String>) = scopedName in zeropageVars // TODO as dotted string instead of list? internal fun isZpVar(scopedName: List<String>) = scopedName in zeropageVars // TODO as dotted string instead of list
internal fun getFloatAsmConst(number: Double): String { internal fun getFloatAsmConst(number: Double): String {
val asmName = globalFloatConsts[number] val asmName = globalFloatConsts[number]

View File

@ -44,7 +44,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
asmgen.asmVariableName(array.variable) asmgen.asmVariableName(array.variable)
} }
lateinit var origAssign: AsmAssignment lateinit var origAssign: AsmAssignmentBase
init { init {
if(register!=null && datatype !in NumericDatatypes) if(register!=null && datatype !in NumericDatatypes)
@ -52,8 +52,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
} }
companion object { companion object {
fun fromAstAssignment(assign: PtAssignment, asmgen: AsmGen6502Internal): AsmAssignTarget { fun fromAstAssignment(target: PtAssignTarget, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget {
with(assign.target) { with(target) {
when { when {
identifier != null -> { identifier != null -> {
val parameter = asmgen.findSubroutineParameter(identifier!!.name, asmgen) val parameter = asmgen.findSubroutineParameter(identifier!!.name, asmgen)
@ -64,13 +64,13 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
if(reg.statusflag!=null) if(reg.statusflag!=null)
throw AssemblyError("can't assign value to processor statusflag directly") throw AssemblyError("can't assign value to processor statusflag directly")
else else
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, assign.definingISub(), register=reg.registerOrPair, origAstTarget = this) return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, definingSub, register=reg.registerOrPair, origAstTarget = this)
} }
} }
return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, type, assign.definingISub(), variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this) return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, type, definingSub, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
} }
array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, assign.definingISub(), array = array, origAstTarget = this) array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, array = array, origAstTarget = this)
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, assign.definingISub(), memory = memory, origAstTarget = this) memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, memory = memory, origAstTarget = this)
else -> throw AssemblyError("weird target") else -> throw AssemblyError("weird target")
} }
} }
@ -191,12 +191,10 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
} }
internal class AsmAssignment(val source: AsmAssignSource, internal sealed class AsmAssignmentBase(val source: AsmAssignSource,
val target: AsmAssignTarget, val target: AsmAssignTarget,
val isAugmentable: Boolean, val memsizer: IMemSizer,
memsizer: IMemSizer, val position: Position) {
val position: Position) {
init { init {
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY)) if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype at $position" } require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype at $position" }
@ -205,3 +203,15 @@ internal class AsmAssignment(val source: AsmAssignSource,
} }
} }
} }
internal class AsmAssignment(source: AsmAssignSource,
target: AsmAssignTarget,
memsizer: IMemSizer,
position: Position): AsmAssignmentBase(source, target, memsizer, position)
internal class AsmAugmentedAssignment(source: AsmAssignSource,
val operator: String,
target: AsmAssignTarget,
memsizer: IMemSizer,
position: Position): AsmAssignmentBase(source, target, memsizer, position)

View File

@ -11,23 +11,22 @@ internal class AssignmentAsmGen(private val program: PtProgram,
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator) private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator)
fun translate(assignment: PtAssignment) { fun translate(assignment: PtAssignment) {
val target = AsmAssignTarget.fromAstAssignment(assignment, asmgen) val target = AsmAssignTarget.fromAstAssignment(assignment.target, assignment.definingISub(), asmgen)
val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target) val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target)
val assign = AsmAssignment(source, target, assignment.isInplaceAssign, program.memsizer, assignment.position) val assign = AsmAssignment(source, target, program.memsizer, assignment.position)
target.origAssign = assign target.origAssign = assign
translateNormalAssignment(assign)
}
if(assign.isAugmentable) fun translate(augmentedAssign: PtAugmentedAssign) {
augmentableAsmGen.translate(assign) val target = AsmAssignTarget.fromAstAssignment(augmentedAssign.target, augmentedAssign.definingISub(), asmgen)
else val source = AsmAssignSource.fromAstSource(augmentedAssign.value, program, asmgen).adjustSignedUnsigned(target)
translateNormalAssignment(assign) val assign = AsmAugmentedAssignment(source, augmentedAssign.operator, target, program.memsizer, augmentedAssign.position)
target.origAssign = assign
augmentableAsmGen.translate(assign)
} }
fun translateNormalAssignment(assign: AsmAssignment) { fun translateNormalAssignment(assign: AsmAssignment) {
if(assign.isAugmentable) {
augmentableAsmGen.translate(assign)
return
}
when(assign.source.kind) { when(assign.source.kind) {
SourceStorageKind.LITERALNUMBER -> { SourceStorageKind.LITERALNUMBER -> {
// simple case: assign a constant number // simple case: assign a constant number
@ -277,14 +276,13 @@ internal class AssignmentAsmGen(private val program: PtProgram,
translateNormalAssignment( translateNormalAssignment(
AsmAssignment( AsmAssignment(
AsmAssignSource.fromAstSource(value.value, program, asmgen), AsmAssignSource.fromAstSource(value.value, program, asmgen),
assign.target, assign.target, program.memsizer, assign.position
false, program.memsizer, assign.position
) )
) )
when (value.operator) { when (value.operator) {
"+" -> {} "+" -> {}
"-" -> augmentableAsmGen.inplaceNegate(assign, true) "-" -> inplaceNegate(assign, true)
"~" -> augmentableAsmGen.inplaceInvert(assign) "~" -> inplaceInvert(assign)
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0") "not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
else -> throw AssemblyError("invalid prefix operator") else -> throw AssemblyError("invalid prefix operator")
} }
@ -308,7 +306,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
} }
internal fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment) { private fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment) {
require(assign.source.expression is PtPrefix) require(assign.source.expression is PtPrefix)
if(assign.source.datatype==DataType.FLOAT) { if(assign.source.datatype==DataType.FLOAT) {
// floatarray[x] = -value ... just use FAC1 to calculate the expression into and then store that back into the array. // floatarray[x] = -value ... just use FAC1 to calculate the expression into and then store that back into the array.
@ -318,8 +316,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
// array[x] = -value ... use a tempvar then store that back into the array. // array[x] = -value ... use a tempvar then store that back into the array.
val tempvar = asmgen.getTempVarName(assign.target.datatype) val tempvar = asmgen.getTempVarName(assign.target.datatype)
val assignToTempvar = AsmAssignment(assign.source, val assignToTempvar = AsmAssignment(assign.source,
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, assign.target.datatype, assign.target.scope, variableAsmName=tempvar, origAstTarget = assign.target.origAstTarget), AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, assign.target.datatype, assign.target.scope,
false, program.memsizer, assign.position) variableAsmName=tempvar, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position)
asmgen.translateNormalAssignment(assignToTempvar) asmgen.translateNormalAssignment(assignToTempvar)
when(assign.target.datatype) { when(assign.target.datatype) {
in ByteDatatypes -> assignVariableByte(assign.target, tempvar) in ByteDatatypes -> assignVariableByte(assign.target, tempvar)
@ -1106,7 +1104,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
lsb.parent = value.parent lsb.parent = value.parent
lsb.add(value) lsb.add(value)
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb) val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb)
val assign = AsmAssignment(src, target, false, program.memsizer, value.position) val assign = AsmAssignment(src, target, program.memsizer, value.position)
translateNormalAssignment(assign) translateNormalAssignment(assign)
} }
@ -2831,7 +2829,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
internal fun assignExpressionToRegister(expr: PtExpression, register: RegisterOrPair, signed: Boolean) { internal fun assignExpressionToRegister(expr: PtExpression, register: RegisterOrPair, signed: Boolean) {
val src = AsmAssignSource.fromAstSource(expr, program, asmgen) val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
val tgt = AsmAssignTarget.fromRegisters(register, signed, null, asmgen) val tgt = AsmAssignTarget.fromRegisters(register, signed, null, asmgen)
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position) val assign = AsmAssignment(src, tgt, program.memsizer, expr.position)
translateNormalAssignment(assign) translateNormalAssignment(assign)
} }
@ -2841,7 +2839,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} else { } else {
val src = AsmAssignSource.fromAstSource(expr, program, asmgen) val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, scope, variableAsmName = asmVarName) val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, scope, variableAsmName = asmVarName)
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position) val assign = AsmAssignment(src, tgt, program.memsizer, expr.position)
translateNormalAssignment(assign) translateNormalAssignment(assign)
} }
} }
@ -2849,7 +2847,218 @@ internal class AssignmentAsmGen(private val program: PtProgram,
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean) { internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean) {
val tgt = AsmAssignTarget.fromRegisters(register, signed, null, asmgen) val tgt = AsmAssignTarget.fromRegisters(register, signed, null, asmgen)
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName) val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName)
val assign = AsmAssignment(src, tgt, false, program.memsizer, Position.DUMMY) val assign = AsmAssignment(src, tgt, program.memsizer, Position.DUMMY)
translateNormalAssignment(assign) translateNormalAssignment(assign)
} }
internal fun inplaceInvert(assign: AsmAssignment) {
val target = assign.target
when (assign.target.datatype) {
DataType.UBYTE -> {
when (target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda ${target.asmVarname}
eor #255
sta ${target.asmVarname}""")
}
TargetStorageKind.MEMORY -> {
val memory = target.memory!!
when (memory.address) {
is PtNumber -> {
val addr = (memory.address as PtNumber).number.toHex()
asmgen.out("""
lda $addr
eor #255
sta $addr""")
}
is PtIdentifier -> {
val sourceName = asmgen.loadByteFromPointerIntoA(memory.address as PtIdentifier)
asmgen.out(" eor #255")
asmgen.out(" sta ($sourceName),y")
}
else -> {
asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD, target.scope)
asmgen.out("""
ldy #0
lda (P8ZP_SCRATCH_W2),y
eor #255""")
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
}
}
}
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.A -> asmgen.out(" eor #255")
RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax")
RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay")
else -> throw AssemblyError("invalid reg dt for byte invert")
}
}
TargetStorageKind.STACK -> TODO("no asm gen for byte stack invert")
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign))
else -> throw AssemblyError("weird target")
}
}
DataType.UWORD -> {
when (target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda ${target.asmVarname}
eor #255
sta ${target.asmVarname}
lda ${target.asmVarname}+1
eor #255
sta ${target.asmVarname}+1""")
}
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.AX -> asmgen.out(" pha | txa | eor #255 | tax | pla | eor #255")
RegisterOrPair.AY -> asmgen.out(" pha | tya | eor #255 | tay | pla | eor #255")
RegisterOrPair.XY -> asmgen.out(" txa | eor #255 | tax | tya | eor #255 | tay")
in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers")
else -> throw AssemblyError("invalid reg dt for word invert")
}
}
TargetStorageKind.STACK -> TODO("no asm gen for word stack invert")
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign))
else -> throw AssemblyError("weird target")
}
}
else -> throw AssemblyError("invert of invalid type")
}
}
internal fun inplaceNegate(assign: AsmAssignment, ignoreDatatype: Boolean) {
val target = assign.target
val datatype = if(ignoreDatatype) {
when(target.datatype) {
DataType.UBYTE, DataType.BYTE -> DataType.BYTE
DataType.UWORD, DataType.WORD -> DataType.WORD
else -> target.datatype
}
} else target.datatype
when (datatype) {
DataType.BYTE -> {
when (target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda #0
sec
sbc ${target.asmVarname}
sta ${target.asmVarname}""")
}
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.A -> {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" eor #255 | ina")
else
asmgen.out(" eor #255 | clc | adc #1")
}
RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax | inx")
RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay | iny")
else -> throw AssemblyError("invalid reg dt for byte negate")
}
}
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
TargetStorageKind.STACK -> TODO("no asm gen for byte stack negate")
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign))
else -> throw AssemblyError("weird target")
}
}
DataType.WORD -> {
when (target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda #0
sec
sbc ${target.asmVarname}
sta ${target.asmVarname}
lda #0
sbc ${target.asmVarname}+1
sta ${target.asmVarname}+1""")
}
TargetStorageKind.REGISTER -> {
when(target.register!!) { //P8ZP_SCRATCH_REG
RegisterOrPair.AX -> {
asmgen.out("""
sec
eor #255
adc #0
pha
txa
eor #255
adc #0
tax
pla""")
}
RegisterOrPair.AY -> {
asmgen.out("""
sec
eor #255
adc #0
pha
tya
eor #255
adc #0
tay
pla""")
}
RegisterOrPair.XY -> {
asmgen.out("""
sec
txa
eor #255
adc #0
tax
tya
eor #255
adc #0
tay""")
}
in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers")
else -> throw AssemblyError("invalid reg dt for word neg")
}
}
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
TargetStorageKind.STACK -> TODO("no asm gen for word stack negate")
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign))
else -> throw AssemblyError("weird target")
}
}
DataType.FLOAT -> {
when (target.kind) {
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.NEGOP")
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.MOVFA | jsr floats.NEGOP | jsr floats.MOVEF")
else -> throw AssemblyError("invalid float register")
}
}
TargetStorageKind.VARIABLE -> {
// simply flip the sign bit in the float
asmgen.out("""
lda ${target.asmVarname}+1
eor #$80
sta ${target.asmVarname}+1
""")
}
TargetStorageKind.STACK -> TODO("no asm gen for float stack negate")
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign))
else -> throw AssemblyError("weird target for in-place float negation")
}
}
else -> throw AssemblyError("negate of invalid type")
}
}
private fun makePrefixedExprFromArrayExprAssign(operator: String, assign: AsmAssignment): AsmAssignment {
val prefix = PtPrefix(operator, assign.source.datatype, assign.source.array!!.position)
prefix.add(assign.source.array)
prefix.parent = assign.target.origAstTarget ?: program
val prefixSrc = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, assign.source.datatype, expression=prefix)
return AsmAssignment(prefixSrc, assign.target, assign.memsizer, assign.position)
}
} }

View File

@ -11,134 +11,41 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
private val asmgen: AsmGen6502Internal, private val asmgen: AsmGen6502Internal,
private val allocator: VariableAllocator private val allocator: VariableAllocator
) { ) {
fun translate(assign: AsmAssignment) { fun translate(assign: AsmAugmentedAssignment) {
require(assign.isAugmentable)
require(assign.source.kind == SourceStorageKind.EXPRESSION) {
"non-expression assign value should be handled elsewhere ${assign.position}"
}
when (val value = assign.source.expression!!) { when(assign.operator) {
is PtPrefix -> { "-" -> {
// A = -A , A = +A, A = ~A, A = not A val a2 = AsmAssignment(assign.source, assign.target, assign.memsizer, assign.position)
when (value.operator) { assignmentAsmGen.inplaceNegate(a2, false)
"+" -> {} }
"-" -> inplaceNegate(assign, false) "~" -> {
"~" -> inplaceInvert(assign) val a2 = AsmAssignment(assign.source, assign.target, assign.memsizer, assign.position)
else -> throw AssemblyError("invalid prefix operator") assignmentAsmGen.inplaceInvert(a2)
} }
"+" -> { /* is a nop */ }
else -> {
if(assign.operator.endsWith('='))
augmentedAssignExpr(assign)
else
throw AssemblyError("invalid augmented assign operator ${assign.operator}")
} }
is PtTypeCast -> inplaceCast(assign.target, value, assign.position)
is PtBinaryExpression -> inplaceBinary(assign.target, value)
else -> throw AssemblyError("invalid aug assign value type")
} }
} }
private fun inplaceBinary(target: AsmAssignTarget, binExpr: PtBinaryExpression) { private fun augmentedAssignExpr(assign: AsmAugmentedAssignment) {
val astTarget = target.origAstTarget!! val srcValue = assign.source.toAstExpression(assign.target.scope as PtNamedNode)
if (binExpr.left isSameAs astTarget) { when (assign.operator) {
// A = A <operator> Something "+=" -> inplaceModification(assign.target, "+", srcValue)
return inplaceModification(target, binExpr.operator, binExpr.right) "-=" -> inplaceModification(assign.target, "-", srcValue)
"*=" -> inplaceModification(assign.target, "*", srcValue)
"/=" -> inplaceModification(assign.target, "/", srcValue)
"|=" -> inplaceModification(assign.target, "|", srcValue)
"&=" -> inplaceModification(assign.target, "&", srcValue)
"^=" -> inplaceModification(assign.target, "^", srcValue)
"<<=" -> inplaceModification(assign.target, "<<", srcValue)
">>=" -> inplaceModification(assign.target, ">>", srcValue)
else -> throw AssemblyError("invalid augmented assign operator ${assign.operator}") // TODO fallback to non-augmented Assign?
} }
if (binExpr.operator in AssociativeOperators) {
if (binExpr.right isSameAs astTarget) {
// A = 5 <operator> A
return inplaceModification(target, binExpr.operator, binExpr.left)
}
val leftBinExpr = binExpr.left as? PtBinaryExpression
if (leftBinExpr?.operator == binExpr.operator) {
// TODO better optimize the chained asm to avoid intermediate stores/loads?
when {
binExpr.right isSameAs astTarget -> {
// A = (x <associative-operator> y) <same-operator> A
inplaceModification(target, binExpr.operator, leftBinExpr.left)
inplaceModification(target, binExpr.operator, leftBinExpr.right)
return
}
leftBinExpr.left isSameAs astTarget -> {
// A = (A <associative-operator> x) <same-operator> y
inplaceModification(target, binExpr.operator, leftBinExpr.right)
inplaceModification(target, binExpr.operator, binExpr.right)
return
}
leftBinExpr.right isSameAs astTarget -> {
// A = (x <associative-operator> A) <same-operator> y
inplaceModification(target, binExpr.operator, leftBinExpr.left)
inplaceModification(target, binExpr.operator, binExpr.right)
return
}
}
}
val rightBinExpr = binExpr.right as? PtBinaryExpression
if (rightBinExpr?.operator == binExpr.operator) {
when {
binExpr.left isSameAs astTarget -> {
// A = A <associative-operator> (x <same-operator> y)
inplaceModification(target, binExpr.operator, rightBinExpr.left)
inplaceModification(target, binExpr.operator, rightBinExpr.right)
return
}
rightBinExpr.left isSameAs astTarget -> {
// A = y <associative-operator> (A <same-operator> x)
inplaceModification(target, binExpr.operator, binExpr.left)
inplaceModification(target, binExpr.operator, rightBinExpr.right)
return
}
rightBinExpr.right isSameAs astTarget -> {
// A = y <associative-operator> (x <same-operator> y)
inplaceModification(target, binExpr.operator, binExpr.left)
inplaceModification(target, binExpr.operator, rightBinExpr.left)
return
}
}
}
}
val leftBinExpr = binExpr.left as? PtBinaryExpression
val rightBinExpr = binExpr.right as? PtBinaryExpression
if(leftBinExpr!=null && rightBinExpr==null) {
if(leftBinExpr.left isSameAs astTarget) {
// X = (X <oper> Right) <oper> Something
inplaceModification(target, leftBinExpr.operator, leftBinExpr.right)
inplaceModification(target, binExpr.operator, binExpr.right)
return
}
if(leftBinExpr.right isSameAs astTarget) {
// X = (Left <oper> X) <oper> Something
if(leftBinExpr.operator in AssociativeOperators) {
inplaceModification(target, leftBinExpr.operator, leftBinExpr.left)
inplaceModification(target, binExpr.operator, binExpr.right)
return
} else {
throw AssemblyError("operands in wrong order for non-associative operator")
}
}
}
if(leftBinExpr==null && rightBinExpr!=null) {
if(rightBinExpr.left isSameAs astTarget) {
// X = Something <oper> (X <oper> Right)
if(binExpr.operator in AssociativeOperators) {
inplaceModification(target, rightBinExpr.operator, rightBinExpr.right)
inplaceModification(target, binExpr.operator, binExpr.left)
return
} else {
throw AssemblyError("operands in wrong order for non-associative operator")
}
}
if(rightBinExpr.right isSameAs astTarget) {
// X = Something <oper> (Left <oper> X)
if(binExpr.operator in AssociativeOperators && rightBinExpr.operator in AssociativeOperators) {
inplaceModification(target, rightBinExpr.operator, rightBinExpr.left)
inplaceModification(target, binExpr.operator, binExpr.left)
return
} else {
throw AssemblyError("operands in wrong order for non-associative operator")
}
}
}
throw AssemblyError("assignment should follow augmentable rules $binExpr")
} }
private fun inplaceModification(target: AsmAssignTarget, operator: String, origValue: PtExpression) { private fun inplaceModification(target: AsmAssignTarget, operator: String, origValue: PtExpression) {
@ -303,7 +210,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
target.datatype == DataType.BYTE, null, target.datatype == DataType.BYTE, null,
asmgen asmgen
) )
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position) val assign = AsmAssignment(target.origAssign.source, tgt, program.memsizer, value.position)
assignmentAsmGen.translateNormalAssignment(assign) assignmentAsmGen.translateNormalAssignment(assign)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A) assignmentAsmGen.assignRegisterByte(target, CpuRegister.A)
} }
@ -314,7 +221,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
target.datatype == DataType.WORD, null, target.datatype == DataType.WORD, null,
asmgen asmgen
) )
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position) val assign = AsmAssignment(target.origAssign.source, tgt, program.memsizer, value.position)
assignmentAsmGen.translateNormalAssignment(assign) assignmentAsmGen.translateNormalAssignment(assign)
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY) assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
} }
@ -325,7 +232,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
true, null, true, null,
asmgen asmgen
) )
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position) val assign = AsmAssignment(target.origAssign.source, tgt, program.memsizer, value.position)
assignmentAsmGen.translateNormalAssignment(assign) assignmentAsmGen.translateNormalAssignment(assign)
assignmentAsmGen.assignFAC1float(target) assignmentAsmGen.assignFAC1float(target)
} }
@ -1736,260 +1643,50 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
""") """)
asmgen.restoreRegisterLocal(CpuRegister.X) asmgen.restoreRegisterLocal(CpuRegister.X)
} }
}
private fun inplaceCast(target: AsmAssignTarget, cast: PtTypeCast, position: Position) {
val outerCastDt = cast.type private fun AsmAssignSource.toAstExpression(scope: PtNamedNode): PtExpression {
val innerCastDt = (cast.value as? PtTypeCast)?.type return when(kind) {
if (innerCastDt == null) { SourceStorageKind.LITERALNUMBER -> this.number!!
// simple typecast where the value is the target SourceStorageKind.VARIABLE -> {
when (target.datatype) { val ident = PtIdentifier(scope.scopedName + '.' + asmVarname, datatype, Position.DUMMY)
DataType.UBYTE, DataType.BYTE -> { /* byte target can't be typecasted to anything else at all */ } ident.parent = scope
DataType.UWORD, DataType.WORD -> { ident
when (outerCastDt) { }
DataType.UBYTE, DataType.BYTE -> { SourceStorageKind.ARRAY -> this.array!!
when (target.kind) { SourceStorageKind.MEMORY -> this.memory!!
TargetStorageKind.VARIABLE -> { SourceStorageKind.EXPRESSION -> this.expression!!
if(asmgen.isTargetCpu(CpuType.CPU65c02)) SourceStorageKind.REGISTER -> {
asmgen.out(" stz ${target.asmVarname}+1") if(register in Cx16VirtualRegisters) {
else val ident = PtIdentifier("cx16.${register!!.name.lowercase()}", DataType.UWORD, position = scope.position)
asmgen.out(" lda #0 | sta ${target.asmVarname}+1") ident.parent = scope
} ident
TargetStorageKind.ARRAY -> { } else {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, target.datatype, CpuRegister.Y, true) throw AssemblyError("no ast expr possible for source register $register")
asmgen.out(" lda #0 | sta ${target.asmVarname},y") }
} }
TargetStorageKind.STACK -> { else -> throw AssemblyError("invalid assign source kind $kind")
if(asmgen.isTargetCpu(CpuType.CPU65c02)) }
asmgen.out(" stz P8ESTACK_HI+1,x") }
else
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x") private fun AsmAssignTarget.toAstExpression(): PtExpression {
} return when(kind) {
else -> throw AssemblyError("weird target") TargetStorageKind.VARIABLE -> {
} val ident = PtIdentifier((this.scope as PtNamedNode).scopedName + '.' + asmVarname, datatype, origAstTarget?.position ?: Position.DUMMY)
} ident.parent = this.scope
DataType.UWORD, DataType.WORD, in IterableDatatypes -> {} ident
DataType.FLOAT -> throw AssemblyError("can't cast float in-place") }
else -> throw AssemblyError("weird cast type") TargetStorageKind.ARRAY -> this.array!!
} TargetStorageKind.MEMORY -> this.memory!!
} TargetStorageKind.REGISTER -> {
DataType.FLOAT -> { if(register in Cx16VirtualRegisters) {
if (outerCastDt != DataType.FLOAT) val ident = PtIdentifier("cx16.${register!!.name.lowercase()}", DataType.UWORD, position = this.origAssign.position)
throw AssemblyError("in-place cast of a float makes no sense") ident.parent = (this.scope as? PtNamedNode) ?: this.origAstTarget!!
} ident
else -> throw AssemblyError("invalid cast target type") } else {
} throw AssemblyError("no ast expr possible for target register $register")
} else { }
// typecast with nested typecast, that has the target as a value }
// calculate singular cast that is required else -> throw AssemblyError("invalid assign target kind $kind")
val castDt = if (outerCastDt largerThan innerCastDt) innerCastDt else outerCastDt }
val resultingCast = PtTypeCast(castDt, position)
resultingCast.add((cast.value as PtTypeCast).value)
require(castDt!=resultingCast.value.type)
inplaceCast(target, resultingCast, position)
}
}
internal fun inplaceInvert(assign: AsmAssignment) {
val target = assign.target
when (assign.target.datatype) {
DataType.UBYTE -> {
when (target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda ${target.asmVarname}
eor #255
sta ${target.asmVarname}""")
}
TargetStorageKind.MEMORY -> {
val memory = target.memory!!
when (memory.address) {
is PtNumber -> {
val addr = (memory.address as PtNumber).number.toHex()
asmgen.out("""
lda $addr
eor #255
sta $addr""")
}
is PtIdentifier -> {
val sourceName = asmgen.loadByteFromPointerIntoA(memory.address as PtIdentifier)
asmgen.out(" eor #255")
asmgen.out(" sta ($sourceName),y")
}
else -> {
asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD, target.scope)
asmgen.out("""
ldy #0
lda (P8ZP_SCRATCH_W2),y
eor #255""")
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
}
}
}
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.A -> asmgen.out(" eor #255")
RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax")
RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay")
else -> throw AssemblyError("invalid reg dt for byte invert")
}
}
TargetStorageKind.STACK -> TODO("no asm gen for byte stack invert")
TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
else -> throw AssemblyError("weird target")
}
}
DataType.UWORD -> {
when (target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda ${target.asmVarname}
eor #255
sta ${target.asmVarname}
lda ${target.asmVarname}+1
eor #255
sta ${target.asmVarname}+1""")
}
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.AX -> asmgen.out(" pha | txa | eor #255 | tax | pla | eor #255")
RegisterOrPair.AY -> asmgen.out(" pha | tya | eor #255 | tay | pla | eor #255")
RegisterOrPair.XY -> asmgen.out(" txa | eor #255 | tax | tya | eor #255 | tay")
in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers")
else -> throw AssemblyError("invalid reg dt for word invert")
}
}
TargetStorageKind.STACK -> TODO("no asm gen for word stack invert")
TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
else -> throw AssemblyError("weird target")
}
}
else -> throw AssemblyError("invert of invalid type")
}
}
internal fun inplaceNegate(assign: AsmAssignment, ignoreDatatype: Boolean) {
val target = assign.target
val datatype = if(ignoreDatatype) {
when(target.datatype) {
DataType.UBYTE, DataType.BYTE -> DataType.BYTE
DataType.UWORD, DataType.WORD -> DataType.WORD
else -> target.datatype
}
} else target.datatype
when (datatype) {
DataType.BYTE -> {
when (target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda #0
sec
sbc ${target.asmVarname}
sta ${target.asmVarname}""")
}
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.A -> {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" eor #255 | ina")
else
asmgen.out(" eor #255 | clc | adc #1")
}
RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax | inx")
RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay | iny")
else -> throw AssemblyError("invalid reg dt for byte negate")
}
}
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
TargetStorageKind.STACK -> TODO("no asm gen for byte stack negate")
TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
else -> throw AssemblyError("weird target")
}
}
DataType.WORD -> {
when (target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda #0
sec
sbc ${target.asmVarname}
sta ${target.asmVarname}
lda #0
sbc ${target.asmVarname}+1
sta ${target.asmVarname}+1""")
}
TargetStorageKind.REGISTER -> {
when(target.register!!) { //P8ZP_SCRATCH_REG
RegisterOrPair.AX -> {
asmgen.out("""
sec
eor #255
adc #0
pha
txa
eor #255
adc #0
tax
pla""")
}
RegisterOrPair.AY -> {
asmgen.out("""
sec
eor #255
adc #0
pha
tya
eor #255
adc #0
tay
pla""")
}
RegisterOrPair.XY -> {
asmgen.out("""
sec
txa
eor #255
adc #0
tax
tya
eor #255
adc #0
tay""")
}
in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers")
else -> throw AssemblyError("invalid reg dt for word neg")
}
}
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
TargetStorageKind.STACK -> TODO("no asm gen for word stack negate")
TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
else -> throw AssemblyError("weird target")
}
}
DataType.FLOAT -> {
when (target.kind) {
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.NEGOP")
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.MOVFA | jsr floats.NEGOP | jsr floats.MOVEF")
else -> throw AssemblyError("invalid float register")
}
}
TargetStorageKind.VARIABLE -> {
// simply flip the sign bit in the float
asmgen.out("""
lda ${target.asmVarname}+1
eor #$80
sta ${target.asmVarname}+1
""")
}
TargetStorageKind.STACK -> TODO("no asm gen for float stack negate")
TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
else -> throw AssemblyError("weird target for in-place float negation")
}
}
else -> throw AssemblyError("negate of invalid type")
}
}
} }

View File

@ -0,0 +1,60 @@
package prog8tests.codegencpu6502
import prog8.code.core.*
internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType) = when(dt) {
in ByteDatatypes -> 1
DataType.FLOAT -> 5
else -> 2
}
override fun memorySize(arrayDt: DataType, numElements: Int) = when(arrayDt) {
DataType.ARRAY_UW -> numElements*2
DataType.ARRAY_W -> numElements*2
DataType.ARRAY_F -> numElements*5
else -> numElements
}
}
internal object DummyStringEncoder : IStringEncoding {
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
return emptyList()
}
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String {
return ""
}
}
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false):
IErrorReporter {
val errors = mutableListOf<String>()
val warnings = mutableListOf<String>()
override fun err(msg: String, position: Position) {
errors.add("${position.toClickableStr()} $msg")
}
override fun warn(msg: String, position: Position) {
warnings.add("${position.toClickableStr()} $msg")
}
override fun noErrors(): Boolean = errors.isEmpty()
override fun report() {
warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") }
errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") }
if(throwExceptionAtReportIfErrors)
finalizeNumErrors(errors.size, warnings.size)
if(!keepMessagesAfterReporting) {
clear()
}
}
fun clear() {
errors.clear()
warnings.clear()
}
}

View File

@ -1,10 +1,102 @@
package prog8tests.codegencpu6502 package prog8tests.codegencpu6502
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldNotBe
import prog8.code.SymbolTableMaker
import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.codegen.cpu6502.AsmGen6502
class TestCodegen: FunSpec({ class TestCodegen: FunSpec({
// TODO there are no 6502 specific codegen tests yet fun getTestOptions(): CompilationOptions {
val target = C64Target()
return CompilationOptions(
OutputType.RAW,
CbmPrgLauncherType.NONE,
ZeropageType.DONTUSE,
zpReserved = emptyList(),
floats = true,
noSysInit = false,
compTarget = target,
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
)
}
test("augmented assign on arrays") {
//main {
// sub start() {
// ubyte[] particleX = [1,2,3]
// ubyte[] particleDX = [1,2,3]
// particleX[2] += particleDX[2]
//
// word @shared xx = 1
// xx = -xx
// xx += 42
// xx += cx16.r0
// }
//}
val codegen = AsmGen6502()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
sub.add(PtVariable("particleDX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY))
val assign = PtAugmentedAssign("+=", Position.DUMMY)
val target = PtAssignTarget(Position.DUMMY).also {
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY))
idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
}
it.add(targetIdx)
}
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
value.add(PtIdentifier("main.start.particleDX", DataType.ARRAY_UB, Position.DUMMY))
value.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
assign.add(target)
assign.add(value)
sub.add(assign)
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
val prefixTarget = PtAssignTarget(Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
}
prefixAssign.add(prefixTarget)
prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
sub.add(prefixAssign)
val numberAssign = PtAugmentedAssign("-=", Position.DUMMY)
val numberAssignTarget = PtAssignTarget(Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
}
numberAssign.add(numberAssignTarget)
numberAssign.add(PtNumber(DataType.WORD, 42.0, Position.DUMMY))
sub.add(numberAssign)
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
val cxregAssignTarget = PtAssignTarget(Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
}
cxregAssign.add(cxregAssignTarget)
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
sub.add(cxregAssign)
block.add(sub)
program.add(block)
// define the "cx16.r0" virtual register
val cx16block = PtBlock("cx16", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
program.add(cx16block)
val options = getTestOptions()
val st = SymbolTableMaker(program, options).make()
val errors = ErrorReporterForTests()
codegen.generate(program, st, options, errors) shouldNotBe null
}
}) })

View File

@ -3,6 +3,7 @@ package prog8.codegen.intermediate
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.AssemblyError import prog8.code.core.AssemblyError
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.PrefixOperators
import prog8.code.core.SignedDatatypes import prog8.code.core.SignedDatatypes
import prog8.intermediate.* import prog8.intermediate.*
@ -12,22 +13,26 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(assignment.target.children.single() is PtMachineRegister) if(assignment.target.children.single() is PtMachineRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister") throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
return if (assignment.isInplaceAssign) return translateRegularAssign(assignment)
translateInplaceAssign(assignment)
else
translateRegularAssign(assignment)
} }
private fun translateInplaceAssign(assignment: PtAssignment): IRCodeChunks { internal fun translate(augmentedAssign: PtAugmentedAssign): IRCodeChunks {
if(augmentedAssign.target.children.single() is PtMachineRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
return translateInplaceAssign(augmentedAssign)
}
private fun translateInplaceAssign(assignment: PtAugmentedAssign): IRCodeChunks {
val ident = assignment.target.identifier val ident = assignment.target.identifier
val memory = assignment.target.memory val memory = assignment.target.memory
val array = assignment.target.array val array = assignment.target.array
return if(ident!=null) { return if(ident!=null) {
assignSelfInMemory(ident.name, assignment.value, assignment) assignVarAugmented(ident.name, assignment)
} else if(memory != null) { } else if(memory != null) {
if(memory.address is PtNumber) if(memory.address is PtNumber)
assignSelfInMemoryKnownAddress((memory.address as PtNumber).number.toInt(), assignment.value, assignment) assignMemoryAugmented((memory.address as PtNumber).number.toInt(), assignment)
else else
fallbackAssign(assignment) fallbackAssign(assignment)
} else if(array!=null) { } else if(array!=null) {
@ -40,120 +45,83 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
} }
} }
private fun assignSelfInMemoryKnownAddress( private fun assignMemoryAugmented(
address: Int, address: Int,
value: PtExpression, assignment: PtAugmentedAssign
origAssign: PtAssignment
): IRCodeChunks { ): IRCodeChunks {
val value = assignment.value
val vmDt = codeGen.irType(value.type) val vmDt = codeGen.irType(value.type)
when(value) { return when(assignment.operator) {
is PtIdentifier -> return emptyList() // do nothing, x=x null assignment. "+" -> expressionEval.operatorPlusInplace(address, null, vmDt, value)
is PtMachineRegister -> return emptyList() // do nothing, reg=reg null assignment "-" -> expressionEval.operatorMinusInplace(address, null, vmDt, value)
is PtPrefix -> return inplacePrefix(value.operator, vmDt, address, null) "*" -> expressionEval.operatorMultiplyInplace(address, null, vmDt, value)
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, address, null, origAssign) "/" -> expressionEval.operatorDivideInplace(address, null, vmDt, value.type in SignedDatatypes, value)
is PtMemoryByte -> { "|" -> expressionEval.operatorOrInplace(address, null, vmDt, value)
return if (!codeGen.options.compTarget.machine.isIOAddress(address.toUInt())) "&" -> expressionEval.operatorAndInplace(address, null, vmDt, value)
emptyList() // do nothing, mem=mem null assignment. "^" -> expressionEval.operatorXorInplace(address, null, vmDt, value)
else { "<<" -> expressionEval.operatorShiftLeftInplace(address, null, vmDt, value)
// read and write a (i/o) memory location to itself. ">>" -> expressionEval.operatorShiftRightInplace(address, null, vmDt, value.type in SignedDatatypes, value)
val tempReg = codeGen.registers.nextFree() in PrefixOperators -> inplacePrefix(assignment.operator, vmDt, address, null)
val code = IRCodeChunk(null, null) else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}") // TODO fallbackAssign?
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address)
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address)
listOf(code)
}
}
else -> return fallbackAssign(origAssign)
} }
} }
private fun assignSelfInMemory( private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks {
symbol: String, val value = assignment.value
value: PtExpression, val valueVmDt = codeGen.irType(value.type)
origAssign: PtAssignment return when (assignment.operator) {
): IRCodeChunks { "+=" -> expressionEval.operatorPlusInplace(null, symbol, valueVmDt, value)
val vmDt = codeGen.irType(value.type) "-=" -> expressionEval.operatorMinusInplace(null, symbol, valueVmDt, value)
return when(value) { "*=" -> expressionEval.operatorMultiplyInplace(null, symbol, valueVmDt, value)
is PtIdentifier -> emptyList() // do nothing, x=x null assignment. "/=" -> expressionEval.operatorDivideInplace(null, symbol, valueVmDt, value.type in SignedDatatypes, value)
is PtMachineRegister -> emptyList() // do nothing, reg=reg null assignment "|=" -> expressionEval.operatorOrInplace(null, symbol, valueVmDt, value)
is PtPrefix -> inplacePrefix(value.operator, vmDt, null, symbol) "&=" -> expressionEval.operatorAndInplace(null, symbol, valueVmDt, value)
is PtBinaryExpression -> inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, null, symbol, origAssign) "^=" -> expressionEval.operatorXorInplace(null, symbol, valueVmDt, value)
is PtMemoryByte -> { "<<=" -> expressionEval.operatorShiftLeftInplace(null, symbol, valueVmDt, value)
val code = IRCodeChunk(null, null) ">>=" -> expressionEval.operatorShiftRightInplace(null, symbol, valueVmDt, value.type in SignedDatatypes, value)
val tempReg = codeGen.registers.nextFree() in PrefixOperators -> inplacePrefix(assignment.operator, valueVmDt, null, symbol)
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, labelSymbol = symbol) else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}") // TODO fallbackAssign?
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, labelSymbol = symbol)
listOf(code)
}
else -> fallbackAssign(origAssign)
} }
} }
private fun fallbackAssign(origAssign: PtAssignment): IRCodeChunks { private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
if (codeGen.options.slowCodegenWarnings) if (codeGen.options.slowCodegenWarnings)
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position) codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
return translateRegularAssign(origAssign) val normalAssign = PtAssignment(origAssign.position)
} normalAssign.add(origAssign.target)
val value: PtExpression
private fun inplaceBinexpr( if(origAssign.operator in PrefixOperators) {
operator: String, value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position)
operand: PtExpression, value.add(origAssign.value)
vmDt: IRDataType,
signed: Boolean,
knownAddress: Int?,
symbol: String?,
origAssign: PtAssignment
): IRCodeChunks {
if(knownAddress!=null) {
when (operator) {
"+" -> return expressionEval.operatorPlusInplace(knownAddress, null, vmDt, operand)
"-" -> return expressionEval.operatorMinusInplace(knownAddress, null, vmDt, operand)
"*" -> return expressionEval.operatorMultiplyInplace(knownAddress, null, vmDt, operand)
"/" -> return expressionEval.operatorDivideInplace(knownAddress, null, vmDt, signed, operand)
"|" -> return expressionEval.operatorOrInplace(knownAddress, null, vmDt, operand)
"&" -> return expressionEval.operatorAndInplace(knownAddress, null, vmDt, operand)
"^" -> return expressionEval.operatorXorInplace(knownAddress, null, vmDt, operand)
"<<" -> return expressionEval.operatorShiftLeftInplace(knownAddress, null, vmDt, operand)
">>" -> return expressionEval.operatorShiftRightInplace(knownAddress, null, vmDt, signed, operand)
else -> {}
}
} else { } else {
symbol!! require(origAssign.operator.endsWith('='))
when (operator) { value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
"+" -> return expressionEval.operatorPlusInplace(null, symbol, vmDt, operand) val left: PtExpression = origAssign.target.children.single() as PtExpression
"-" -> return expressionEval.operatorMinusInplace(null, symbol, vmDt, operand) value.add(left)
"*" -> return expressionEval.operatorMultiplyInplace(null, symbol, vmDt, operand) value.add(origAssign.value)
"/" -> return expressionEval.operatorDivideInplace(null, symbol, vmDt, signed, operand)
"|" -> return expressionEval.operatorOrInplace(null, symbol, vmDt, operand)
"&" -> return expressionEval.operatorAndInplace(null, symbol, vmDt, operand)
"^" -> return expressionEval.operatorXorInplace(null, symbol, vmDt, operand)
"<<" -> return expressionEval.operatorShiftLeftInplace(null, symbol, vmDt, operand)
">>" -> return expressionEval.operatorShiftRightInplace(null, symbol, vmDt, signed, operand)
else -> {}
}
} }
return fallbackAssign(origAssign) normalAssign.add(value)
return translateRegularAssign(normalAssign)
} }
private fun inplacePrefix(operator: String, vmDt: IRDataType, knownAddress: Int?, addressSymbol: String?): IRCodeChunks { private fun inplacePrefix(operator: String, vmDt: IRDataType, address: Int?, symbol: String?): IRCodeChunks {
val code= IRCodeChunk(null, null) val code= IRCodeChunk(null, null)
when(operator) { when(operator) {
"+" -> { } "+" -> { }
"-" -> { "-" -> {
code += if(knownAddress!=null) code += if(address!=null)
IRInstruction(Opcode.NEGM, vmDt, value = knownAddress) IRInstruction(Opcode.NEGM, vmDt, value = address)
else else
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = addressSymbol) IRInstruction(Opcode.NEGM, vmDt, labelSymbol = symbol)
} }
"~" -> { "~" -> {
val regMask = codeGen.registers.nextFree() val regMask = codeGen.registers.nextFree()
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff
code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask) code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
code += if(knownAddress!=null) code += if(address!=null)
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = knownAddress) IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = address)
else else
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = addressSymbol) IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = symbol)
} }
else -> throw AssemblyError("weird prefix operator") else -> throw AssemblyError("weird prefix operator")
} }

View File

@ -238,6 +238,7 @@ class IRCodeGen(
is PtMemMapped -> emptyList() // memmapped var should be looked up via symbol table is PtMemMapped -> emptyList() // memmapped var should be looked up via symbol table
is PtConstant -> emptyList() // constants have all been folded into the code is PtConstant -> emptyList() // constants have all been folded into the code
is PtAssignment -> assignmentGen.translate(node) is PtAssignment -> assignmentGen.translate(node)
is PtAugmentedAssign -> assignmentGen.translate(node)
is PtNodeGroup -> translateGroup(node.children) is PtNodeGroup -> translateGroup(node.children)
is PtBuiltinFunctionCall -> translateBuiltinFunc(node, 0) is PtBuiltinFunctionCall -> translateBuiltinFunc(node, 0)
is PtFunctionCall -> expressionEval.translate(node, 0, 0) is PtFunctionCall -> expressionEval.translate(node, 0, 0)
@ -1178,7 +1179,7 @@ class IRCodeGen(
for (child in block.children) { for (child in block.children) {
when(child) { when(child) {
is PtNop -> { /* nothing */ } is PtNop -> { /* nothing */ }
is PtAssignment -> { /* global variable initialization is done elsewhere */ } is PtAssignment, is PtAugmentedAssign -> { /* global variable initialization is done elsewhere */ }
is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ } is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ }
is PtSub -> { is PtSub -> {
val sub = IRSubroutine(child.name, translate(child.parameters), child.returntype, child.position) val sub = IRSubroutine(child.name, translate(child.parameters), child.returntype, child.position)

View File

@ -24,7 +24,7 @@ class VmCodeGen: ICodeGeneratorBackend {
} }
internal class VmAssemblyProgram(override val name: String, private val irProgram: IRProgram): IAssemblyProgram { internal class VmAssemblyProgram(override val name: String, internal val irProgram: IRProgram): IAssemblyProgram {
override fun assemble(options: CompilationOptions): Boolean { override fun assemble(options: CompilationOptions): Boolean {
// the VM reads the IR file from disk. // the VM reads the IR file from disk.

View File

@ -1,12 +1,18 @@
import prog8.code.core.DataType import prog8.code.core.*
import prog8.code.core.Encoding
import prog8.code.core.IMemSizer
import prog8.code.core.IStringEncoding
internal object DummyMemsizer : IMemSizer { internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType) = 0 override fun memorySize(dt: DataType) = when(dt) {
override fun memorySize(arrayDt: DataType, numElements: Int) = 0 in ByteDatatypes -> 1
DataType.FLOAT -> 5
else -> 2
}
override fun memorySize(arrayDt: DataType, numElements: Int) = when(arrayDt) {
DataType.ARRAY_UW -> numElements*2
DataType.ARRAY_W -> numElements*2
DataType.ARRAY_F -> numElements*5
else -> numElements
}
} }
internal object DummyStringEncoder : IStringEncoding { internal object DummyStringEncoder : IStringEncoding {
@ -18,3 +24,35 @@ internal object DummyStringEncoder : IStringEncoding {
return "" return ""
} }
} }
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false):
IErrorReporter {
val errors = mutableListOf<String>()
val warnings = mutableListOf<String>()
override fun err(msg: String, position: Position) {
errors.add("${position.toClickableStr()} $msg")
}
override fun warn(msg: String, position: Position) {
warnings.add("${position.toClickableStr()} $msg")
}
override fun noErrors(): Boolean = errors.isEmpty()
override fun report() {
warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") }
errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") }
if(throwExceptionAtReportIfErrors)
finalizeNumErrors(errors.size, warnings.size)
if(!keepMessagesAfterReporting) {
clear()
}
}
fun clear() {
errors.clear()
warnings.clear()
}
}

View File

@ -0,0 +1,103 @@
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.ints.shouldBeGreaterThan
import prog8.code.SymbolTableMaker
import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.codegen.vm.VmAssemblyProgram
import prog8.codegen.vm.VmCodeGen
import prog8.intermediate.IRSubroutine
class TestVmCodeGen: FunSpec({
fun getTestOptions(): CompilationOptions {
val target = VMTarget()
return CompilationOptions(
OutputType.RAW,
CbmPrgLauncherType.NONE,
ZeropageType.DONTUSE,
zpReserved = emptyList(),
floats = true,
noSysInit = false,
compTarget = target,
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
)
}
test("augmented assigns") {
//main {
// sub start() {
// ubyte[] particleX = [1,2,3]
// ubyte[] particleDX = [1,2,3]
// particleX[2] += particleDX[2]
//
// word @shared xx = 1
// xx = -xx
// xx += 42
// xx += cx16.r0
// }
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
sub.add(PtVariable("particleDX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY))
val assign = PtAugmentedAssign("+=", Position.DUMMY)
val target = PtAssignTarget(Position.DUMMY).also {
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY))
idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
}
it.add(targetIdx)
}
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
value.add(PtIdentifier("main.start.particleDX", DataType.ARRAY_UB, Position.DUMMY))
value.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
assign.add(target)
assign.add(value)
sub.add(assign)
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
val prefixTarget = PtAssignTarget(Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
}
prefixAssign.add(prefixTarget)
prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
sub.add(prefixAssign)
val numberAssign = PtAugmentedAssign("+=", Position.DUMMY)
val numberAssignTarget = PtAssignTarget(Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
}
numberAssign.add(numberAssignTarget)
numberAssign.add(PtNumber(DataType.WORD, 42.0, Position.DUMMY))
sub.add(numberAssign)
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
val cxregAssignTarget = PtAssignTarget(Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
}
cxregAssign.add(cxregAssignTarget)
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
sub.add(cxregAssign)
block.add(sub)
program.add(block)
// define the "cx16.r0" virtual register
val cx16block = PtBlock("cx16", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
program.add(cx16block)
val options = getTestOptions()
val st = SymbolTableMaker(program, options).make()
val errors = ErrorReporterForTests()
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
irChunks.size shouldBeGreaterThan 4
}
})

View File

@ -6,9 +6,11 @@ import prog8.ast.Program
import prog8.ast.base.AstException import prog8.ast.base.AstException
import prog8.ast.expressions.Expression import prog8.ast.expressions.Expression
import prog8.ast.expressions.NumericLiteral import prog8.ast.expressions.NumericLiteral
import prog8.ast.printProgram
import prog8.ast.statements.Directive import prog8.ast.statements.Directive
import prog8.code.SymbolTableMaker import prog8.code.SymbolTableMaker
import prog8.code.ast.PtProgram import prog8.code.ast.PtProgram
import prog8.code.ast.printAst
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.* import prog8.code.target.*
import prog8.codegen.vm.VmCodeGen import prog8.codegen.vm.VmCodeGen

View File

@ -90,6 +90,54 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
} }
private fun transform(srcAssign: Assignment): PtNode { private fun transform(srcAssign: Assignment): PtNode {
if(srcAssign.isAugmentable) {
val srcExpr = srcAssign.value
val (operator: String, augmentedValue: Expression?) = when(srcExpr) {
is BinaryExpression -> {
if(srcExpr.operator=="==" || srcExpr.operator=="%") {
// no special code possible for 'in-place comparison and result as boolean' or 'remainder'
Pair("", null)
}
else if(srcExpr.left isSameAs srcAssign.target) {
Pair(srcExpr.operator+'=', srcExpr.right)
} else if(srcExpr.right isSameAs srcAssign.target) {
Pair(srcExpr.operator+'=', srcExpr.left)
} else {
// either left or right is same as target, other combinations are not supported here
Pair("", null)
}
}
is PrefixExpression -> {
require(srcExpr.expression isSameAs srcAssign.target)
Pair(srcExpr.operator, srcExpr.expression)
}
is TypecastExpression -> {
// At this time, there are no special optimized instructions to do an in-place type conversion.
// so we simply revert to a regular type converting assignment.
// Also, an in-place type cast is very uncommon so probably not worth optimizing anyway.
Pair("", null)
// the following is what *could* be used here if such instructions *were* available:
// if(srcExpr.expression isSameAs srcAssign.target)
// Pair("cast", srcExpr.expression)
// else {
// val subTypeCast = srcExpr.expression as? TypecastExpression
// val targetDt = srcAssign.target.inferType(program).getOrElse { DataType.UNDEFINED }
// if (subTypeCast!=null && srcExpr.type==targetDt && subTypeCast.expression isSameAs srcAssign.target) {
// Pair("cast", subTypeCast)
// } else
// Pair("", null)
// }
}
else -> Pair("", null)
}
if(augmentedValue!=null) {
val assign = PtAugmentedAssign(operator, srcAssign.position)
assign.add(transform(srcAssign.target))
assign.add(transformExpression(augmentedValue))
return assign
}
}
val assign = PtAssignment(srcAssign.position) val assign = PtAssignment(srcAssign.position)
assign.add(transform(srcAssign.target)) assign.add(transform(srcAssign.target))
assign.add(transformExpression(srcAssign.value)) assign.add(transformExpression(srcAssign.value))

View File

@ -21,8 +21,17 @@ internal object DummyFunctions : IBuiltinFunctions {
} }
internal object DummyMemsizer : IMemSizer { internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType) = 0 override fun memorySize(dt: DataType) = when(dt) {
override fun memorySize(arrayDt: DataType, numElements: Int) = 0 in ByteDatatypes -> 1
DataType.FLOAT -> 5
else -> 2
}
override fun memorySize(arrayDt: DataType, numElements: Int) = when(arrayDt) {
DataType.ARRAY_UW -> numElements*2
DataType.ARRAY_W -> numElements*2
DataType.ARRAY_F -> numElements*5
else -> numElements
}
} }
internal object DummyStringEncoder : IStringEncoding { internal object DummyStringEncoder : IStringEncoding {

View File

@ -1,7 +1,6 @@
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.ints.shouldBeGreaterThan import io.kotest.matchers.ints.shouldBeGreaterThan
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.instanceOf
import prog8.code.StStaticVariable import prog8.code.StStaticVariable
import prog8.code.core.CbmPrgLauncherType import prog8.code.core.CbmPrgLauncherType
import prog8.code.core.CompilationOptions import prog8.code.core.CompilationOptions