mirror of
https://github.com/irmen/prog8.git
synced 2025-01-10 20:30:23 +00:00
reintroduce explicit PtAugmentedAssign ast node
This commit is contained in:
parent
cf3523f49f
commit
5c3f41f64d
@ -12,6 +12,7 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
|
||||
return when(node) {
|
||||
is PtAssignTarget -> ""
|
||||
is PtAssignment -> "<assign>"
|
||||
is PtAugmentedAssign -> "<inplace-assign> ${node.operator}"
|
||||
is PtBreakpoint -> "%breakpoint"
|
||||
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
|
||||
is PtAddressOf -> "&"
|
||||
@ -30,7 +31,10 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
|
||||
is PtIdentifier -> "${node.name} ${type(node.type)}"
|
||||
is PtMachineRegister -> "VMREG#${node.register} ${type(node.type)}"
|
||||
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 PtRange -> "<range>"
|
||||
is PtString -> "\"${node.value.escape()}\""
|
||||
|
@ -43,36 +43,22 @@ class PtAssignment(position: Position) : PtNode(position) {
|
||||
get() = children[0] as PtAssignTarget
|
||||
val value: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
}
|
||||
|
||||
val isInplaceAssign: Boolean by lazy {
|
||||
val target = target.children.single() as PtExpression
|
||||
when(val source = value) {
|
||||
is PtArrayIndexer -> {
|
||||
if(target is PtArrayIndexer && source.type==target.type) {
|
||||
if(target.variable isSameAs source.variable) {
|
||||
target.index isSameAs source.index
|
||||
}
|
||||
}
|
||||
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 PtAugmentedAssign(val operator: String, position: Position) : PtNode(position) {
|
||||
val target: PtAssignTarget
|
||||
get() = children[0] as PtAssignTarget
|
||||
val value: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
init {
|
||||
require(operator.endsWith('=') || operator in PrefixOperators) {
|
||||
"invalid augmented assign operator $operator"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtAssignTarget(position: Position) : PtNode(position) {
|
||||
val identifier: PtIdentifier?
|
||||
get() = children.single() as? PtIdentifier
|
||||
|
@ -5,6 +5,7 @@ val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||
val LogicalOperators = setOf("and", "or", "xor", "not")
|
||||
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
|
||||
val BitwiseOperators = setOf("&", "|", "^", "~")
|
||||
val PrefixOperators = setOf("+", "-", "~", "not")
|
||||
// val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators
|
||||
|
||||
fun invertedComparisonOperator(operator: String) =
|
||||
|
@ -148,7 +148,7 @@ class AsmGen6502Internal (
|
||||
is PtVariable, is PtMemMapped -> {
|
||||
val sourceName = asmVariableName(pointervar)
|
||||
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
|
||||
out(" lda ($sourceName)")
|
||||
sourceName
|
||||
@ -162,7 +162,7 @@ class AsmGen6502Internal (
|
||||
"P8ZP_SCRATCH_W1"
|
||||
}
|
||||
} 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
|
||||
out(" ldy #0 | lda ($sourceName),y")
|
||||
sourceName
|
||||
@ -185,7 +185,7 @@ class AsmGen6502Internal (
|
||||
internal fun storeAIntoPointerVar(pointervar: PtIdentifier) {
|
||||
val sourceName = asmVariableName(pointervar)
|
||||
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
|
||||
out(" sta ($sourceName)")
|
||||
} else {
|
||||
@ -197,7 +197,7 @@ class AsmGen6502Internal (
|
||||
sta (P8ZP_SCRATCH_W2)""")
|
||||
}
|
||||
} 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
|
||||
out(" ldy #0 | sta ($sourceName),y")
|
||||
} else {
|
||||
@ -337,6 +337,7 @@ class AsmGen6502Internal (
|
||||
is PtBuiltinFunctionCall -> builtinFunctionsAsmGen.translateFunctioncallStatement(stmt)
|
||||
is PtFunctionCall -> functioncallAsmGen.translateFunctionCallStatement(stmt)
|
||||
is PtAssignment -> assignmentAsmGen.translate(stmt)
|
||||
is PtAugmentedAssign -> assignmentAsmGen.translate(stmt)
|
||||
is PtJump -> {
|
||||
val (asmLabel, indirect) = getJumpTarget(stmt)
|
||||
jmp(asmLabel, indirect)
|
||||
@ -505,7 +506,7 @@ class AsmGen6502Internal (
|
||||
translateNormalAssignment(
|
||||
AsmAssignment(
|
||||
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) {
|
||||
if(indirect) {
|
||||
|
@ -335,7 +335,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null)
|
||||
else
|
||||
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)
|
||||
}
|
||||
|
||||
@ -1089,7 +1089,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
conv.reg != null -> {
|
||||
@ -1107,7 +1107,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
else -> throw AssemblyError("callconv")
|
||||
|
@ -221,7 +221,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
} else {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
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 {
|
||||
val asmName = globalFloatConsts[number]
|
||||
|
@ -44,7 +44,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
asmgen.asmVariableName(array.variable)
|
||||
}
|
||||
|
||||
lateinit var origAssign: AsmAssignment
|
||||
lateinit var origAssign: AsmAssignmentBase
|
||||
|
||||
init {
|
||||
if(register!=null && datatype !in NumericDatatypes)
|
||||
@ -52,8 +52,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromAstAssignment(assign: PtAssignment, asmgen: AsmGen6502Internal): AsmAssignTarget {
|
||||
with(assign.target) {
|
||||
fun fromAstAssignment(target: PtAssignTarget, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget {
|
||||
with(target) {
|
||||
when {
|
||||
identifier != null -> {
|
||||
val parameter = asmgen.findSubroutineParameter(identifier!!.name, asmgen)
|
||||
@ -64,13 +64,13 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
if(reg.statusflag!=null)
|
||||
throw AssemblyError("can't assign value to processor statusflag directly")
|
||||
else
|
||||
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, assign.definingISub(), register=reg.registerOrPair, origAstTarget = this)
|
||||
return AsmAssignTarget(TargetStorageKind.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)
|
||||
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, assign.definingISub(), memory = memory, origAstTarget = this)
|
||||
array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, array = array, origAstTarget = this)
|
||||
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, memory = memory, origAstTarget = this)
|
||||
else -> throw AssemblyError("weird target")
|
||||
}
|
||||
}
|
||||
@ -191,12 +191,10 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
}
|
||||
|
||||
|
||||
internal class AsmAssignment(val source: AsmAssignSource,
|
||||
val target: AsmAssignTarget,
|
||||
val isAugmentable: Boolean,
|
||||
memsizer: IMemSizer,
|
||||
val position: Position) {
|
||||
|
||||
internal sealed class AsmAssignmentBase(val source: AsmAssignSource,
|
||||
val target: AsmAssignTarget,
|
||||
val memsizer: IMemSizer,
|
||||
val position: Position) {
|
||||
init {
|
||||
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
||||
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype at $position" }
|
||||
@ -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)
|
||||
|
||||
|
@ -11,23 +11,22 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator)
|
||||
|
||||
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 assign = AsmAssignment(source, target, assignment.isInplaceAssign, program.memsizer, assignment.position)
|
||||
val assign = AsmAssignment(source, target, program.memsizer, assignment.position)
|
||||
target.origAssign = assign
|
||||
translateNormalAssignment(assign)
|
||||
}
|
||||
|
||||
if(assign.isAugmentable)
|
||||
augmentableAsmGen.translate(assign)
|
||||
else
|
||||
translateNormalAssignment(assign)
|
||||
fun translate(augmentedAssign: PtAugmentedAssign) {
|
||||
val target = AsmAssignTarget.fromAstAssignment(augmentedAssign.target, augmentedAssign.definingISub(), asmgen)
|
||||
val source = AsmAssignSource.fromAstSource(augmentedAssign.value, program, asmgen).adjustSignedUnsigned(target)
|
||||
val assign = AsmAugmentedAssignment(source, augmentedAssign.operator, target, program.memsizer, augmentedAssign.position)
|
||||
target.origAssign = assign
|
||||
augmentableAsmGen.translate(assign)
|
||||
}
|
||||
|
||||
fun translateNormalAssignment(assign: AsmAssignment) {
|
||||
if(assign.isAugmentable) {
|
||||
augmentableAsmGen.translate(assign)
|
||||
return
|
||||
}
|
||||
|
||||
when(assign.source.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
// simple case: assign a constant number
|
||||
@ -277,14 +276,13 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
translateNormalAssignment(
|
||||
AsmAssignment(
|
||||
AsmAssignSource.fromAstSource(value.value, program, asmgen),
|
||||
assign.target,
|
||||
false, program.memsizer, assign.position
|
||||
assign.target, program.memsizer, assign.position
|
||||
)
|
||||
)
|
||||
when (value.operator) {
|
||||
"+" -> {}
|
||||
"-" -> augmentableAsmGen.inplaceNegate(assign, true)
|
||||
"~" -> augmentableAsmGen.inplaceInvert(assign)
|
||||
"-" -> inplaceNegate(assign, true)
|
||||
"~" -> inplaceInvert(assign)
|
||||
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
|
||||
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)
|
||||
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.
|
||||
@ -318,8 +316,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
// array[x] = -value ... use a tempvar then store that back into the array.
|
||||
val tempvar = asmgen.getTempVarName(assign.target.datatype)
|
||||
val assignToTempvar = AsmAssignment(assign.source,
|
||||
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, assign.target.datatype, assign.target.scope, variableAsmName=tempvar, origAstTarget = assign.target.origAstTarget),
|
||||
false, program.memsizer, assign.position)
|
||||
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, assign.target.datatype, assign.target.scope,
|
||||
variableAsmName=tempvar, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position)
|
||||
asmgen.translateNormalAssignment(assignToTempvar)
|
||||
when(assign.target.datatype) {
|
||||
in ByteDatatypes -> assignVariableByte(assign.target, tempvar)
|
||||
@ -1106,7 +1104,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
lsb.parent = value.parent
|
||||
lsb.add(value)
|
||||
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)
|
||||
}
|
||||
|
||||
@ -2831,7 +2829,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
internal fun assignExpressionToRegister(expr: PtExpression, register: RegisterOrPair, signed: Boolean) {
|
||||
val src = AsmAssignSource.fromAstSource(expr, program, 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)
|
||||
}
|
||||
|
||||
@ -2841,7 +2839,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -2849,7 +2847,218 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean) {
|
||||
val tgt = AsmAssignTarget.fromRegisters(register, signed, null, asmgen)
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -11,134 +11,41 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val allocator: VariableAllocator
|
||||
) {
|
||||
fun translate(assign: AsmAssignment) {
|
||||
require(assign.isAugmentable)
|
||||
require(assign.source.kind == SourceStorageKind.EXPRESSION) {
|
||||
"non-expression assign value should be handled elsewhere ${assign.position}"
|
||||
}
|
||||
fun translate(assign: AsmAugmentedAssignment) {
|
||||
|
||||
when (val value = assign.source.expression!!) {
|
||||
is PtPrefix -> {
|
||||
// A = -A , A = +A, A = ~A, A = not A
|
||||
when (value.operator) {
|
||||
"+" -> {}
|
||||
"-" -> inplaceNegate(assign, false)
|
||||
"~" -> inplaceInvert(assign)
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
when(assign.operator) {
|
||||
"-" -> {
|
||||
val a2 = AsmAssignment(assign.source, assign.target, assign.memsizer, assign.position)
|
||||
assignmentAsmGen.inplaceNegate(a2, false)
|
||||
}
|
||||
"~" -> {
|
||||
val a2 = AsmAssignment(assign.source, assign.target, assign.memsizer, assign.position)
|
||||
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) {
|
||||
val astTarget = target.origAstTarget!!
|
||||
if (binExpr.left isSameAs astTarget) {
|
||||
// A = A <operator> Something
|
||||
return inplaceModification(target, binExpr.operator, binExpr.right)
|
||||
private fun augmentedAssignExpr(assign: AsmAugmentedAssignment) {
|
||||
val srcValue = assign.source.toAstExpression(assign.target.scope as PtNamedNode)
|
||||
when (assign.operator) {
|
||||
"+=" -> 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)
|
||||
">>=" -> 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) {
|
||||
@ -303,7 +210,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
target.datatype == DataType.BYTE, null,
|
||||
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.assignRegisterByte(target, CpuRegister.A)
|
||||
}
|
||||
@ -314,7 +221,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
target.datatype == DataType.WORD, null,
|
||||
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.assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
}
|
||||
@ -325,7 +232,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
true, null,
|
||||
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.assignFAC1float(target)
|
||||
}
|
||||
@ -1736,260 +1643,50 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
""")
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
}
|
||||
|
||||
private fun inplaceCast(target: AsmAssignTarget, cast: PtTypeCast, position: Position) {
|
||||
val outerCastDt = cast.type
|
||||
val innerCastDt = (cast.value as? PtTypeCast)?.type
|
||||
if (innerCastDt == null) {
|
||||
// simple typecast where the value is the target
|
||||
when (target.datatype) {
|
||||
DataType.UBYTE, DataType.BYTE -> { /* byte target can't be typecasted to anything else at all */ }
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
when (outerCastDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz ${target.asmVarname}+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta ${target.asmVarname}+1")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, target.datatype, CpuRegister.Y, true)
|
||||
asmgen.out(" lda #0 | sta ${target.asmVarname},y")
|
||||
}
|
||||
TargetStorageKind.STACK -> {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz P8ESTACK_HI+1,x")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
|
||||
}
|
||||
else -> throw AssemblyError("weird target")
|
||||
}
|
||||
}
|
||||
DataType.UWORD, DataType.WORD, in IterableDatatypes -> {}
|
||||
DataType.FLOAT -> throw AssemblyError("can't cast float in-place")
|
||||
else -> throw AssemblyError("weird cast type")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if (outerCastDt != DataType.FLOAT)
|
||||
throw AssemblyError("in-place cast of a float makes no sense")
|
||||
}
|
||||
else -> throw AssemblyError("invalid cast target type")
|
||||
}
|
||||
} else {
|
||||
// typecast with nested typecast, that has the target as a value
|
||||
// calculate singular cast that is required
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun AsmAssignSource.toAstExpression(scope: PtNamedNode): PtExpression {
|
||||
return when(kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> this.number!!
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
val ident = PtIdentifier(scope.scopedName + '.' + asmVarname, datatype, Position.DUMMY)
|
||||
ident.parent = scope
|
||||
ident
|
||||
}
|
||||
SourceStorageKind.ARRAY -> this.array!!
|
||||
SourceStorageKind.MEMORY -> this.memory!!
|
||||
SourceStorageKind.EXPRESSION -> this.expression!!
|
||||
SourceStorageKind.REGISTER -> {
|
||||
if(register in Cx16VirtualRegisters) {
|
||||
val ident = PtIdentifier("cx16.${register!!.name.lowercase()}", DataType.UWORD, position = scope.position)
|
||||
ident.parent = scope
|
||||
ident
|
||||
} else {
|
||||
throw AssemblyError("no ast expr possible for source register $register")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("invalid assign source kind $kind")
|
||||
}
|
||||
}
|
||||
|
||||
private fun AsmAssignTarget.toAstExpression(): PtExpression {
|
||||
return when(kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
val ident = PtIdentifier((this.scope as PtNamedNode).scopedName + '.' + asmVarname, datatype, origAstTarget?.position ?: Position.DUMMY)
|
||||
ident.parent = this.scope
|
||||
ident
|
||||
}
|
||||
TargetStorageKind.ARRAY -> this.array!!
|
||||
TargetStorageKind.MEMORY -> this.memory!!
|
||||
TargetStorageKind.REGISTER -> {
|
||||
if(register in Cx16VirtualRegisters) {
|
||||
val ident = PtIdentifier("cx16.${register!!.name.lowercase()}", DataType.UWORD, position = this.origAssign.position)
|
||||
ident.parent = (this.scope as? PtNamedNode) ?: this.origAstTarget!!
|
||||
ident
|
||||
} else {
|
||||
throw AssemblyError("no ast expr possible for target register $register")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("invalid assign target kind $kind")
|
||||
}
|
||||
}
|
||||
|
60
codeGenCpu6502/test/Dummies.kt
Normal file
60
codeGenCpu6502/test/Dummies.kt
Normal 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()
|
||||
}
|
||||
}
|
@ -1,10 +1,102 @@
|
||||
package prog8tests.codegencpu6502
|
||||
|
||||
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({
|
||||
|
||||
// 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
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -3,6 +3,7 @@ package prog8.codegen.intermediate
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.PrefixOperators
|
||||
import prog8.code.core.SignedDatatypes
|
||||
import prog8.intermediate.*
|
||||
|
||||
@ -12,22 +13,26 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(assignment.target.children.single() is PtMachineRegister)
|
||||
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
|
||||
|
||||
return if (assignment.isInplaceAssign)
|
||||
translateInplaceAssign(assignment)
|
||||
else
|
||||
translateRegularAssign(assignment)
|
||||
return 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 memory = assignment.target.memory
|
||||
val array = assignment.target.array
|
||||
|
||||
return if(ident!=null) {
|
||||
assignSelfInMemory(ident.name, assignment.value, assignment)
|
||||
assignVarAugmented(ident.name, assignment)
|
||||
} else if(memory != null) {
|
||||
if(memory.address is PtNumber)
|
||||
assignSelfInMemoryKnownAddress((memory.address as PtNumber).number.toInt(), assignment.value, assignment)
|
||||
assignMemoryAugmented((memory.address as PtNumber).number.toInt(), assignment)
|
||||
else
|
||||
fallbackAssign(assignment)
|
||||
} 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,
|
||||
value: PtExpression,
|
||||
origAssign: PtAssignment
|
||||
assignment: PtAugmentedAssign
|
||||
): IRCodeChunks {
|
||||
val value = assignment.value
|
||||
val vmDt = codeGen.irType(value.type)
|
||||
when(value) {
|
||||
is PtIdentifier -> return emptyList() // do nothing, x=x null assignment.
|
||||
is PtMachineRegister -> return emptyList() // do nothing, reg=reg null assignment
|
||||
is PtPrefix -> return inplacePrefix(value.operator, vmDt, address, null)
|
||||
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, address, null, origAssign)
|
||||
is PtMemoryByte -> {
|
||||
return if (!codeGen.options.compTarget.machine.isIOAddress(address.toUInt()))
|
||||
emptyList() // do nothing, mem=mem null assignment.
|
||||
else {
|
||||
// read and write a (i/o) memory location to itself.
|
||||
val tempReg = codeGen.registers.nextFree()
|
||||
val code = IRCodeChunk(null, null)
|
||||
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address)
|
||||
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address)
|
||||
listOf(code)
|
||||
}
|
||||
}
|
||||
else -> return fallbackAssign(origAssign)
|
||||
return when(assignment.operator) {
|
||||
"+" -> expressionEval.operatorPlusInplace(address, null, vmDt, value)
|
||||
"-" -> expressionEval.operatorMinusInplace(address, null, vmDt, value)
|
||||
"*" -> expressionEval.operatorMultiplyInplace(address, null, vmDt, value)
|
||||
"/" -> expressionEval.operatorDivideInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||
"|" -> expressionEval.operatorOrInplace(address, null, vmDt, value)
|
||||
"&" -> expressionEval.operatorAndInplace(address, null, vmDt, value)
|
||||
"^" -> expressionEval.operatorXorInplace(address, null, vmDt, value)
|
||||
"<<" -> expressionEval.operatorShiftLeftInplace(address, null, vmDt, value)
|
||||
">>" -> expressionEval.operatorShiftRightInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||
in PrefixOperators -> inplacePrefix(assignment.operator, vmDt, address, null)
|
||||
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}") // TODO fallbackAssign?
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignSelfInMemory(
|
||||
symbol: String,
|
||||
value: PtExpression,
|
||||
origAssign: PtAssignment
|
||||
): IRCodeChunks {
|
||||
val vmDt = codeGen.irType(value.type)
|
||||
return when(value) {
|
||||
is PtIdentifier -> emptyList() // do nothing, x=x null assignment.
|
||||
is PtMachineRegister -> emptyList() // do nothing, reg=reg null assignment
|
||||
is PtPrefix -> inplacePrefix(value.operator, vmDt, null, symbol)
|
||||
is PtBinaryExpression -> inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, null, symbol, origAssign)
|
||||
is PtMemoryByte -> {
|
||||
val code = IRCodeChunk(null, null)
|
||||
val tempReg = codeGen.registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, labelSymbol = symbol)
|
||||
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, labelSymbol = symbol)
|
||||
listOf(code)
|
||||
}
|
||||
|
||||
else -> fallbackAssign(origAssign)
|
||||
private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks {
|
||||
val value = assignment.value
|
||||
val valueVmDt = codeGen.irType(value.type)
|
||||
return when (assignment.operator) {
|
||||
"+=" -> expressionEval.operatorPlusInplace(null, symbol, valueVmDt, value)
|
||||
"-=" -> expressionEval.operatorMinusInplace(null, symbol, valueVmDt, value)
|
||||
"*=" -> expressionEval.operatorMultiplyInplace(null, symbol, valueVmDt, value)
|
||||
"/=" -> expressionEval.operatorDivideInplace(null, symbol, valueVmDt, value.type in SignedDatatypes, value)
|
||||
"|=" -> expressionEval.operatorOrInplace(null, symbol, valueVmDt, value)
|
||||
"&=" -> expressionEval.operatorAndInplace(null, symbol, valueVmDt, value)
|
||||
"^=" -> expressionEval.operatorXorInplace(null, symbol, valueVmDt, value)
|
||||
"<<=" -> expressionEval.operatorShiftLeftInplace(null, symbol, valueVmDt, value)
|
||||
">>=" -> expressionEval.operatorShiftRightInplace(null, symbol, valueVmDt, value.type in SignedDatatypes, value)
|
||||
in PrefixOperators -> inplacePrefix(assignment.operator, valueVmDt, null, symbol)
|
||||
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}") // TODO fallbackAssign?
|
||||
}
|
||||
}
|
||||
|
||||
private fun fallbackAssign(origAssign: PtAssignment): IRCodeChunks {
|
||||
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
|
||||
if (codeGen.options.slowCodegenWarnings)
|
||||
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
|
||||
return translateRegularAssign(origAssign)
|
||||
}
|
||||
|
||||
private fun inplaceBinexpr(
|
||||
operator: String,
|
||||
operand: PtExpression,
|
||||
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 -> {}
|
||||
}
|
||||
val normalAssign = PtAssignment(origAssign.position)
|
||||
normalAssign.add(origAssign.target)
|
||||
val value: PtExpression
|
||||
if(origAssign.operator in PrefixOperators) {
|
||||
value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position)
|
||||
value.add(origAssign.value)
|
||||
} else {
|
||||
symbol!!
|
||||
when (operator) {
|
||||
"+" -> return expressionEval.operatorPlusInplace(null, symbol, vmDt, operand)
|
||||
"-" -> return expressionEval.operatorMinusInplace(null, symbol, vmDt, operand)
|
||||
"*" -> return expressionEval.operatorMultiplyInplace(null, symbol, vmDt, operand)
|
||||
"/" -> 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 -> {}
|
||||
}
|
||||
require(origAssign.operator.endsWith('='))
|
||||
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
|
||||
val left: PtExpression = origAssign.target.children.single() as PtExpression
|
||||
value.add(left)
|
||||
value.add(origAssign.value)
|
||||
}
|
||||
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)
|
||||
when(operator) {
|
||||
"+" -> { }
|
||||
"-" -> {
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.NEGM, vmDt, value = knownAddress)
|
||||
code += if(address!=null)
|
||||
IRInstruction(Opcode.NEGM, vmDt, value = address)
|
||||
else
|
||||
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = addressSymbol)
|
||||
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = symbol)
|
||||
}
|
||||
"~" -> {
|
||||
val regMask = codeGen.registers.nextFree()
|
||||
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff
|
||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = knownAddress)
|
||||
code += if(address!=null)
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = address)
|
||||
else
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = addressSymbol)
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = symbol)
|
||||
}
|
||||
else -> throw AssemblyError("weird prefix operator")
|
||||
}
|
||||
|
@ -238,6 +238,7 @@ class IRCodeGen(
|
||||
is PtMemMapped -> emptyList() // memmapped var should be looked up via symbol table
|
||||
is PtConstant -> emptyList() // constants have all been folded into the code
|
||||
is PtAssignment -> assignmentGen.translate(node)
|
||||
is PtAugmentedAssign -> assignmentGen.translate(node)
|
||||
is PtNodeGroup -> translateGroup(node.children)
|
||||
is PtBuiltinFunctionCall -> translateBuiltinFunc(node, 0)
|
||||
is PtFunctionCall -> expressionEval.translate(node, 0, 0)
|
||||
@ -1178,7 +1179,7 @@ class IRCodeGen(
|
||||
for (child in block.children) {
|
||||
when(child) {
|
||||
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 PtSub -> {
|
||||
val sub = IRSubroutine(child.name, translate(child.parameters), child.returntype, child.position)
|
||||
|
@ -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 {
|
||||
// the VM reads the IR file from disk.
|
||||
|
@ -1,12 +1,18 @@
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.IMemSizer
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType) = 0
|
||||
override fun memorySize(arrayDt: DataType, numElements: Int) = 0
|
||||
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 {
|
||||
@ -18,3 +24,35 @@ internal object DummyStringEncoder : IStringEncoding {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
103
codeGenIntermediate/test/TestVmCodeGen.kt
Normal file
103
codeGenIntermediate/test/TestVmCodeGen.kt
Normal 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
|
||||
}
|
||||
})
|
@ -6,9 +6,11 @@ import prog8.ast.Program
|
||||
import prog8.ast.base.AstException
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.printProgram
|
||||
import prog8.ast.statements.Directive
|
||||
import prog8.code.SymbolTableMaker
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.ast.printAst
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.*
|
||||
import prog8.codegen.vm.VmCodeGen
|
||||
|
@ -90,6 +90,54 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
|
||||
}
|
||||
|
||||
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)
|
||||
assign.add(transform(srcAssign.target))
|
||||
assign.add(transformExpression(srcAssign.value))
|
||||
|
@ -21,8 +21,17 @@ internal object DummyFunctions : IBuiltinFunctions {
|
||||
}
|
||||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType) = 0
|
||||
override fun memorySize(arrayDt: DataType, numElements: Int) = 0
|
||||
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 {
|
||||
|
@ -1,7 +1,6 @@
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.ints.shouldBeGreaterThan
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.types.instanceOf
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.core.CbmPrgLauncherType
|
||||
import prog8.code.core.CompilationOptions
|
||||
|
Loading…
x
Reference in New Issue
Block a user