mirror of
https://github.com/irmen/prog8.git
synced 2024-09-29 08:57:51 +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) {
|
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()}\""
|
||||||
|
@ -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
|
||||||
|
@ -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) =
|
||||||
|
@ -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) {
|
||||||
|
@ -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")
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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]
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
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
|
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
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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.
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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.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
|
||||||
|
@ -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))
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user