Merge branch 'rpn-expressions'

This commit is contained in:
Irmen de Jong 2023-03-19 17:36:20 +01:00
commit c50c9ca545
34 changed files with 1582 additions and 595 deletions

View File

@ -15,7 +15,12 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
* This gives the fastest lookup possible (no need to traverse tree nodes)
*/
val flat: Map<String, StNode> by lazy {
private var cachedFlat: Map<String, StNode>? = null
val flat: Map<String, StNode> get() {
if(cachedFlat!=null)
return cachedFlat!!
val result = mutableMapOf<String, StNode>()
fun collect(node: StNode) {
for(child in node.children) {
@ -24,7 +29,12 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
}
}
collect(this)
result
cachedFlat = result
return result
}
fun resetCachedFlat() {
cachedFlat = null
}
val allVariables: Collection<StStaticVariable> by lazy {

View File

@ -53,7 +53,8 @@ sealed class PtNamedNode(var name: String, position: Position): PtNode(position)
class PtProgram(
val name: String,
val memsizer: IMemSizer,
val encoding: IStringEncoding
val encoding: IStringEncoding,
var binaryExpressionsAreRPN: Boolean = false
) : PtNode(Position.DUMMY) {
// fun allModuleDirectives(): Sequence<PtDirective> =
@ -64,6 +65,45 @@ class PtProgram(
fun entrypoint(): PtSub? =
allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && it.name == "start" } as PtSub?
// If the code generator wants, it can transform binary expression nodes into flat RPN nodes.
// This will destroy the original binaryexpression nodes!
fun transformBinExprToRPN() {
if(binaryExpressionsAreRPN)
return
fun transformToRPN(originalExpr: PtBinaryExpression): PtRpn {
fun makeRpn(expr: PtExpression): PtRpn {
val rpn = PtRpn(expr.type, expr.position)
if(expr is PtBinaryExpression)
rpn.addRpnNode(transformToRPN(expr))
else
rpn.addRpnNode(expr)
return rpn
}
val rpn = PtRpn(originalExpr.type, originalExpr.position)
rpn.addRpnNode(makeRpn(originalExpr.left))
rpn.addRpnNode(makeRpn(originalExpr.right))
rpn.addRpnNode(PtRpnOperator(originalExpr.operator, originalExpr.type, originalExpr.left.type, originalExpr.right.type, originalExpr.position))
return rpn
}
fun transformBinExprToRPN(node: PtNode, parent: PtNode) {
if(node is PtBinaryExpression) {
val rpn = transformToRPN(node)
val idx = parent.children.indexOf(node)
rpn.parent = parent
parent.children[idx] = rpn
}
node.children.forEach {child ->
transformBinExprToRPN(child, node)
}
}
children.forEach { transformBinExprToRPN(it, this) }
binaryExpressionsAreRPN = true
}
}

View File

@ -3,6 +3,7 @@ package prog8.code.ast
import prog8.code.core.*
import java.util.*
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.round
@ -27,6 +28,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index
is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
is PtRpn -> other is PtRpn && this.isSame(other)
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register
@ -62,6 +64,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
is PtArray -> true
is PtArrayIndexer -> index is PtNumber || index is PtIdentifier
is PtBinaryExpression -> false
is PtRpn -> false
is PtBuiltinFunctionCall -> name in arrayOf("msb", "lsb", "peek", "peekw", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd")
is PtContainmentCheck -> false
is PtFunctionCall -> false
@ -158,6 +161,129 @@ class PtBinaryExpression(val operator: String, type: DataType, position: Positio
}
class PtRpn(type: DataType, position: Position): PtExpression(type, position) {
// contains only PtExpression (not PtRpn!) and PtRpnOperator nodes
// not created directly by the compiler for now, if code generators prefer this over PtBinaryExpression,
// they have to transform the ast themselves first using the utility routine on PtProgram for it.
fun addRpnNode(node: PtNode) {
require(node is PtRpnOperator || node is PtExpression)
if(node is PtRpn) {
node.children.forEach {
children.add(it)
it.parent = this
}
}
else {
require(node !is PtBinaryExpression)
children.add(node)
node.parent = this
}
}
fun print() {
children.forEach {
when(it) {
is PtRpnOperator -> println(it.operator)
is PtExpression -> println("expr $it ${it.position}")
else -> {}
}
}
}
fun isSame(other: PtRpn): Boolean {
if(other.children.size==children.size) {
return other.children.zip(this.children).all { (first, second) ->
when (first) {
is PtRpnOperator -> second is PtRpnOperator && first.operator==second.operator
is PtExpression -> second is PtExpression && first isSameAs second
else -> false
}
}
}
return false
}
fun maxDepth(): Map<DataType, Int> {
val depths = mutableMapOf(
DataType.UBYTE to 0,
DataType.UWORD to 0,
DataType.FLOAT to 0
)
val maxDepths = mutableMapOf(
DataType.UBYTE to 0,
DataType.UWORD to 0,
DataType.FLOAT to 0
)
var numPushes = 0
var numPops = 0
fun push(type: DataType) {
when (type) {
in ByteDatatypes -> {
val depth = depths.getValue(DataType.UBYTE) + 1
depths[DataType.UBYTE] = depth
maxDepths[DataType.UBYTE] = max(maxDepths.getValue(DataType.UBYTE), depth)
}
in WordDatatypes, in PassByReferenceDatatypes -> {
val depth = depths.getValue(DataType.UWORD) + 1
depths[DataType.UWORD] = depth
maxDepths[DataType.UWORD] = max(maxDepths.getValue(DataType.UWORD), depth)
}
DataType.FLOAT -> {
val depth = depths.getValue(DataType.FLOAT) + 1
depths[DataType.FLOAT] = depth
maxDepths[DataType.FLOAT] = max(maxDepths.getValue(DataType.FLOAT), depth)
}
else -> throw IllegalArgumentException("invalid dt")
}
numPushes++
}
fun pop(type: DataType) {
when (type) {
in ByteDatatypes -> depths[DataType.UBYTE]=depths.getValue(DataType.UBYTE) - 1
in WordDatatypes, in PassByReferenceDatatypes -> depths[DataType.UWORD]=depths.getValue(DataType.UWORD) - 1
DataType.FLOAT -> depths[DataType.FLOAT]=depths.getValue(DataType.FLOAT) - 1
else -> throw IllegalArgumentException("invalid dt")
}
numPops++
}
children.withIndex().forEach { (index, node) ->
if (node is PtRpnOperator) {
pop(node.leftType)
pop(node.rightType)
push(node.type)
}
else {
push((node as PtExpression).type)
}
}
require(numPushes==numPops+1 && numPushes==children.size) {
"RPN not balanced, pushes=$numPushes pops=$numPops childs=${children.size}"
}
return maxDepths
}
fun finalOperator() = children.last() as PtRpnOperator
fun finalLeftOperand() = children[children.size-3]
fun finalRightOperand() = children[children.size-2]
fun finalOperation() = Triple(finalLeftOperand(), finalOperator(), finalRightOperand())
}
class PtRpnOperator(val operator: String, val type: DataType, val leftType: DataType, val rightType: DataType, position: Position): PtNode(position) {
init {
// NOTE: For now, we require that the types of the operands are the same size as the output type of the operator node.
if(operator !in ComparisonOperators) {
require(type equalsSize leftType && type equalsSize rightType) {
"operand type size(s) differ from operator result type $type: $leftType $rightType oper: $operator"
}
}
}
}
class PtContainmentCheck(position: Position): PtExpression(DataType.UBYTE, position) {
val element: PtExpression
get() = children[0] as PtExpression
@ -215,6 +341,8 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
}
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
override fun toString() = "PtNumber:$type:$number"
}

View File

@ -6,7 +6,7 @@ import prog8.code.core.*
* Produces readable text from a [PtNode] (AST node, usually starting with PtProgram as root),
* passing it as a String to the specified receiver function.
*/
fun printAst(root: PtNode, output: (text: String) -> Unit) {
fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Unit) {
fun type(dt: DataType) = "!${dt.name.lowercase()}!"
fun txt(node: PtNode): String {
return when(node) {
@ -19,6 +19,8 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
is PtArray -> "array len=${node.children.size} ${type(node.type)}"
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)}"
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
is PtRpn -> "<rpnexpr>"
is PtRpnOperator -> node.operator
is PtBuiltinFunctionCall -> {
val str = if(node.void) "void " else ""
str + node.name + "()"
@ -134,16 +136,22 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
root.children.forEach {
walkAst(it) { node, depth ->
val txt = txt(node)
if(txt.isNotEmpty())
output(" ".repeat(depth) + txt(node))
val library = if(node is PtBlock) node.library else node.definingBlock()?.library==true
if(!library || !skipLibraries) {
if (txt.isNotEmpty())
output(" ".repeat(depth) + txt(node))
}
}
}
println()
} else {
walkAst(root) { node, depth ->
val txt = txt(node)
if(txt.isNotEmpty())
output(" ".repeat(depth) + txt(node))
val library = if(node is PtBlock) node.library else node.definingBlock()?.library==true
if(!library || !skipLibraries) {
if (txt.isNotEmpty())
output(" ".repeat(depth) + txt(node))
}
}
}
}

View File

@ -100,8 +100,8 @@ class PtForLoop(position: Position) : PtNode(position) {
class PtIfElse(position: Position) : PtNode(position) {
val condition: PtBinaryExpression
get() = children[0] as PtBinaryExpression
val condition: PtExpression // either PtRpn or PtBinaryExpression
get() = children[0] as PtExpression
val ifScope: PtNodeGroup
get() = children[1] as PtNodeGroup
val elseScope: PtNodeGroup

View File

@ -20,6 +20,7 @@ class CompilationOptions(val output: OutputType,
var asmListfile: Boolean = false,
var experimentalCodegen: Boolean = false,
var varsHigh: Boolean = false,
var useRPN: Boolean = false,
var evalStackBaseAddress: UInt? = null,
var outputDir: Path = Path(""),
var symbolDefs: Map<String, String> = emptyMap()

View File

@ -21,6 +21,13 @@ class AsmGen6502: ICodeGeneratorBackend {
options: CompilationOptions,
errors: IErrorReporter
): IAssemblyProgram? {
if(options.useRPN) {
program.transformBinExprToRPN()
errors.warn("EXPERIMENTAL RPN EXPRESSION NODES ARE USED. CODE SIZE+SPEED WILL SUFFER.", Position.DUMMY)
}
// printAst(program, true) { println(it) }
val asmgen = AsmGen6502Internal(program, symbolTable, options, errors)
return asmgen.compileToAssembly()
}
@ -43,9 +50,9 @@ class AsmGen6502Internal (
private val forloopsAsmGen = ForLoopsAsmGen(program, this, zeropage)
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator)
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
private val assignmentAsmGen = AssignmentAsmGen(program, this, allocator)
private val assignmentAsmGen = AssignmentAsmGen(program, symbolTable, this, allocator)
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
fun compileToAssembly(): IAssemblyProgram? {
@ -467,17 +474,17 @@ class AsmGen6502Internal (
internal fun restoreXafterCall(functionCall: PtFunctionCall) =
functioncallAsmGen.restoreXafterCall(functionCall)
internal fun translateNormalAssignment(assign: AsmAssignment) =
assignmentAsmGen.translateNormalAssignment(assign)
internal fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) =
assignmentAsmGen.translateNormalAssignment(assign, scope)
internal fun assignExpressionToRegister(expr: PtExpression, register: RegisterOrPair, signed: Boolean=false) =
assignmentAsmGen.assignExpressionToRegister(expr, register, signed)
internal fun assignExpressionToVariable(expr: PtExpression, asmVarName: String, dt: DataType, scope: IPtSubroutine?) =
assignmentAsmGen.assignExpressionToVariable(expr, asmVarName, dt, scope)
internal fun assignExpressionToVariable(expr: PtExpression, asmVarName: String, dt: DataType) =
assignmentAsmGen.assignExpressionToVariable(expr, asmVarName, dt)
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, pos: Position, signed: Boolean=false) =
assignmentAsmGen.assignVariableToRegister(asmVarName, register, signed, pos)
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, scope: IPtSubroutine?, pos: Position, signed: Boolean=false) =
assignmentAsmGen.assignVariableToRegister(asmVarName, register, signed, scope, pos)
internal fun assignRegister(reg: RegisterOrPair, target: AsmAssignTarget) {
when(reg) {
@ -507,7 +514,7 @@ class AsmGen6502Internal (
AsmAssignment(
AsmAssignSource(SourceStorageKind.REGISTER, program, this, target.datatype, register=RegisterOrPair.AY),
target, program.memsizer, value.position
)
), value.definingISub()
)
}
DataType.FLOAT -> {
@ -544,35 +551,59 @@ class AsmGen6502Internal (
}
private fun translate(stmt: PtIfElse) {
requireComparisonExpression(stmt.condition) // IfStatement: condition must be of form 'x <comparison> <value>'
val booleanCondition = stmt.condition
if (stmt.elseScope.children.isEmpty()) {
val jump = stmt.ifScope.children.singleOrNull()
if(jump is PtJump) {
translateCompareAndJumpIfTrue(booleanCondition, jump)
if(stmt.condition is PtRpn) {
val condition = stmt.condition as PtRpn
requireComparisonExpression(condition) // IfStatement: condition must be of form 'x <comparison> <value>'
if (stmt.elseScope.children.isEmpty()) {
val jump = stmt.ifScope.children.singleOrNull()
if (jump is PtJump) {
translateCompareAndJumpIfTrueRPN(condition, jump)
} else {
val endLabel = makeLabel("if_end")
translateCompareAndJumpIfFalseRPN(condition, endLabel)
translate(stmt.ifScope)
out(endLabel)
}
} else {
// both true and else parts
val elseLabel = makeLabel("if_else")
val endLabel = makeLabel("if_end")
translateCompareAndJumpIfFalse(booleanCondition, endLabel)
translateCompareAndJumpIfFalseRPN(condition, elseLabel)
translate(stmt.ifScope)
jmp(endLabel)
out(elseLabel)
translate(stmt.elseScope)
out(endLabel)
}
} else {
val condition = stmt.condition as PtBinaryExpression
requireComparisonExpression(condition) // IfStatement: condition must be of form 'x <comparison> <value>'
if (stmt.elseScope.children.isEmpty()) {
val jump = stmt.ifScope.children.singleOrNull()
if (jump is PtJump) {
translateCompareAndJumpIfTrue(condition, jump)
} else {
val endLabel = makeLabel("if_end")
translateCompareAndJumpIfFalse(condition, endLabel)
translate(stmt.ifScope)
out(endLabel)
}
} else {
// both true and else parts
val elseLabel = makeLabel("if_else")
val endLabel = makeLabel("if_end")
translateCompareAndJumpIfFalse(condition, elseLabel)
translate(stmt.ifScope)
jmp(endLabel)
out(elseLabel)
translate(stmt.elseScope)
out(endLabel)
}
}
else {
// both true and else parts
val elseLabel = makeLabel("if_else")
val endLabel = makeLabel("if_end")
translateCompareAndJumpIfFalse(booleanCondition, elseLabel)
translate(stmt.ifScope)
jmp(endLabel)
out(elseLabel)
translate(stmt.elseScope)
out(endLabel)
}
}
private fun requireComparisonExpression(condition: PtExpression) {
if(condition !is PtBinaryExpression || condition.operator !in ComparisonOperators)
if (!(condition is PtRpn && condition.finalOperator().operator in ComparisonOperators) && !(condition is PtBinaryExpression && condition.operator in ComparisonOperators))
throw AssemblyError("expected boolean comparison expression $condition")
}
@ -597,11 +628,11 @@ class AsmGen6502Internal (
val name = asmVariableName(stmt.count as PtIdentifier)
when(vardecl.type) {
DataType.UBYTE, DataType.BYTE -> {
assignVariableToRegister(name, RegisterOrPair.Y, stmt.count.position)
assignVariableToRegister(name, RegisterOrPair.Y, stmt.definingISub(), stmt.count.position)
repeatCountInY(stmt, endLabel)
}
DataType.UWORD, DataType.WORD -> {
assignVariableToRegister(name, RegisterOrPair.AY, stmt.count.position)
assignVariableToRegister(name, RegisterOrPair.AY, stmt.definingISub(), stmt.count.position)
repeatWordCountInAY(endLabel, stmt)
}
else -> throw AssemblyError("invalid loop variable datatype $vardecl")
@ -977,33 +1008,78 @@ $repeatLabel lda $counterVar
}
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression): Pair<PtExpression, PtExpression>? {
if(pointerOffsetExpr is PtBinaryExpression && pointerOffsetExpr.operator=="+") {
val leftDt = pointerOffsetExpr.left.type
val rightDt = pointerOffsetExpr.right.type
if(leftDt == DataType.UWORD && rightDt == DataType.UBYTE)
return Pair(pointerOffsetExpr.left, pointerOffsetExpr.right)
if(leftDt == DataType.UBYTE && rightDt == DataType.UWORD)
return Pair(pointerOffsetExpr.right, pointerOffsetExpr.left)
if(leftDt == DataType.UWORD && rightDt == DataType.UWORD) {
// could be that the index was a constant numeric byte but converted to word, check that
val constIdx = pointerOffsetExpr.right as? PtNumber
if(constIdx!=null && constIdx.number.toInt()>=0 && constIdx.number.toInt()<=255) {
return Pair(pointerOffsetExpr.left, PtNumber(DataType.UBYTE, constIdx.number, constIdx.position))
}
// could be that the index was typecasted into uword, check that
val rightTc = pointerOffsetExpr.right as? PtTypeCast
if(rightTc!=null && rightTc.value.type == DataType.UBYTE)
return Pair(pointerOffsetExpr.left, rightTc.value)
val leftTc = pointerOffsetExpr.left as? PtTypeCast
if(leftTc!=null && leftTc.value.type == DataType.UBYTE)
return Pair(pointerOffsetExpr.right, leftTc.value)
}
val left: PtExpression
val right: PtExpression
val operator: String
when (pointerOffsetExpr) {
is PtRpn -> {
if(pointerOffsetExpr.children.size>3) {
val rightmostOperator = pointerOffsetExpr.finalOperator()
if(rightmostOperator.operator=="+") {
val rightmostOperand = pointerOffsetExpr.finalRightOperand()
if ((rightmostOperand is PtNumber && rightmostOperand.type in IntegerDatatypes && rightmostOperand.number.toInt() in 0..255)
|| (rightmostOperand is PtExpression && rightmostOperand.type == DataType.UBYTE)
|| (rightmostOperand is PtTypeCast && rightmostOperand.value.type == DataType.UBYTE)
) {
// split up the big expression in 2 parts so that we CAN use ZP,Y indexing after all
pointerOffsetExpr.children.removeLast()
pointerOffsetExpr.children.removeLast()
val tempvar = getTempVarName(DataType.UWORD)
assignExpressionToVariable(pointerOffsetExpr, tempvar, DataType.UWORD)
val smallExpr = PtRpn(DataType.UWORD, pointerOffsetExpr.position)
smallExpr.addRpnNode(PtIdentifier(tempvar, DataType.UWORD, pointerOffsetExpr.position))
smallExpr.addRpnNode(rightmostOperand)
smallExpr.addRpnNode(rightmostOperator)
smallExpr.parent = pointerOffsetExpr.parent
val result = pointerViaIndexRegisterPossible(smallExpr)
require(result != null)
return result
}
}
return null // expression is too complex
}
val (leftNode, oper, rightNode) = pointerOffsetExpr.finalOperation()
operator=oper.operator
if (leftNode !is PtExpression || rightNode !is PtExpression) return null
left = leftNode
right = rightNode
}
is PtBinaryExpression -> {
operator = pointerOffsetExpr.operator
left = pointerOffsetExpr.left
right = pointerOffsetExpr.right
}
else -> return null
}
if (operator != "+") return null
val leftDt = left.type
val rightDt = right.type
if(leftDt == DataType.UWORD && rightDt == DataType.UBYTE)
return Pair(left, right)
if(leftDt == DataType.UBYTE && rightDt == DataType.UWORD)
return Pair(right, left)
if(leftDt == DataType.UWORD && rightDt == DataType.UWORD) {
// could be that the index was a constant numeric byte but converted to word, check that
val constIdx = right as? PtNumber
if(constIdx!=null && constIdx.number.toInt()>=0 && constIdx.number.toInt()<=255) {
val num = PtNumber(DataType.UBYTE, constIdx.number, constIdx.position)
num.parent = right.parent
return Pair(left, num)
}
// could be that the index was typecasted into uword, check that
val rightTc = right as? PtTypeCast
if(rightTc!=null && rightTc.value.type == DataType.UBYTE)
return Pair(left, rightTc.value)
val leftTc = left as? PtTypeCast
if(leftTc!=null && leftTc.value.type == DataType.UBYTE)
return Pair(right, leftTc.value)
}
return null
}
internal fun tryOptimizedPointerAccessWithA(expr: PtBinaryExpression, write: Boolean): Boolean {
internal fun tryOptimizedPointerAccessWithA(addressExpr: PtExpression, operator: String, write: Boolean): Boolean {
// optimize pointer,indexregister if possible
fun evalBytevalueWillClobberA(expr: PtExpression): Boolean {
@ -1019,9 +1095,8 @@ $repeatLabel lda $counterVar
}
}
if(expr.operator=="+") {
val ptrAndIndex = pointerViaIndexRegisterPossible(expr)
if(operator=="+") {
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr)
if(ptrAndIndex!=null) {
val pointervar = ptrAndIndex.first as? PtIdentifier
val target = if(pointervar==null) null else symbolTable.lookup(pointervar.name)!!.astNode
@ -1047,14 +1122,14 @@ $repeatLabel lda $counterVar
if(saveA)
out(" pha")
if(ptrAndIndex.second.isSimple()) {
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
if(saveA)
out(" pla")
out(" sta (P8ZP_SCRATCH_W2),y")
} else {
pushCpuStack(DataType.UBYTE, ptrAndIndex.second)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
restoreRegisterStack(CpuRegister.Y, true)
if(saveA)
out(" pla")
@ -1068,12 +1143,12 @@ $repeatLabel lda $counterVar
} else {
// copy the pointer var to zp first
if(ptrAndIndex.second.isSimple()) {
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" lda (P8ZP_SCRATCH_W2),y")
} else {
pushCpuStack(DataType.UBYTE, ptrAndIndex.second)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
restoreRegisterStack(CpuRegister.Y, false)
out(" lda (P8ZP_SCRATCH_W2),y")
}
@ -1089,12 +1164,67 @@ $repeatLabel lda $counterVar
}
internal fun findSubroutineParameter(name: String, asmgen: AsmGen6502Internal): PtSubroutineParameter? {
val node = asmgen.symbolTable.lookup(name)!!.astNode
val stScope = asmgen.symbolTable.lookup(name)
require(stScope!=null) {
"invalid name lookup $name"
}
val node = stScope.astNode
if(node is PtSubroutineParameter)
return node
return node.definingSub()?.parameters?.singleOrNull { it.name===name }
}
private fun translateCompareAndJumpIfTrueRPN(expr: PtRpn, jump: PtJump) {
val (leftRpn, oper, right) = expr.finalOperation()
if(oper.operator !in ComparisonOperators)
throw AssemblyError("must be comparison expression")
val left: PtExpression = if(expr.children.size>3 || leftRpn !is PtExpression) {
expr.children.removeLast()
expr.children.removeLast()
expr
} else
leftRpn
// invert the comparison, so we can reuse the JumpIfFalse code generation routines
val invertedComparisonOperator = invertedComparisonOperator(oper.operator)
?: throw AssemblyError("can't invert comparison $expr")
val label = when {
jump.generatedLabel!=null -> jump.generatedLabel!!
jump.identifier!=null -> asmSymbolName(jump.identifier!!)
jump.address!=null -> jump.address!!.toHex()
else -> throw AssemblyError("weird jump")
}
val rightConstVal = right as? PtNumber
if (rightConstVal!=null && rightConstVal.number == 0.0) {
testZeroAndJump(left, invertedComparisonOperator, label)
}
else {
require(right is PtExpression)
val leftConstVal = left as? PtNumber
testNonzeroComparisonAndJump(left, invertedComparisonOperator, right, label, leftConstVal, rightConstVal)
}
}
private fun translateCompareAndJumpIfFalseRPN(expr: PtRpn, jumpIfFalseLabel: String) {
val (leftRpn, oper, right) = expr.finalOperation()
val left: PtExpression = if(expr.children.size>3 || leftRpn !is PtExpression) {
expr.children.removeLast()
expr.children.removeLast()
expr
} else
leftRpn
require(right is PtExpression)
val leftConstVal = left as? PtNumber
val rightConstVal = right as? PtNumber
if (rightConstVal!=null && rightConstVal.number == 0.0)
testZeroAndJump(left, oper.operator, jumpIfFalseLabel)
else
testNonzeroComparisonAndJump(left, oper.operator, right, jumpIfFalseLabel, leftConstVal, rightConstVal)
}
private fun translateCompareAndJumpIfTrue(expr: PtBinaryExpression, jump: PtJump) {
if(expr.operator !in ComparisonOperators)
throw AssemblyError("must be comparison expression")
@ -1287,7 +1417,7 @@ $repeatLabel lda $counterVar
}
}
private fun testNonzeroComparisonAndJump(
internal fun testNonzeroComparisonAndJump(
left: PtExpression,
operator: String,
right: PtExpression,
@ -1398,7 +1528,7 @@ $repeatLabel lda $counterVar
} else {
val subroutine = left.definingSub()!!
subroutineExtra(subroutine).usedFloatEvalResultVar1 = true
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
assignExpressionToRegister(left, RegisterOrPair.FAC1)
out("""
lda #<$subroutineFloatEvalResultVar1
@ -1443,7 +1573,7 @@ $repeatLabel lda $counterVar
} else {
val subroutine = left.definingSub()!!
subroutineExtra(subroutine).usedFloatEvalResultVar1 = true
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
assignExpressionToRegister(left, RegisterOrPair.FAC1)
out("""
lda #<$subroutineFloatEvalResultVar1
@ -1488,7 +1618,7 @@ $repeatLabel lda $counterVar
} else {
val subroutine = left.definingSub()!!
subroutineExtra(subroutine).usedFloatEvalResultVar1 = true
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
assignExpressionToRegister(left, RegisterOrPair.FAC1)
out("""
lda #<$subroutineFloatEvalResultVar1
@ -1533,7 +1663,7 @@ $repeatLabel lda $counterVar
} else {
val subroutine = left.definingSub()!!
subroutineExtra(subroutine).usedFloatEvalResultVar1 = true
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
assignExpressionToRegister(left, RegisterOrPair.FAC1)
out("""
lda #<$subroutineFloatEvalResultVar1
@ -1578,11 +1708,11 @@ $repeatLabel lda $counterVar
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.UBYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
out(" pla")
}
return code("P8ZP_SCRATCH_B1")
@ -1619,11 +1749,11 @@ $repeatLabel lda $counterVar
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.UBYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
out(" pla")
}
return code("P8ZP_SCRATCH_B1")
@ -1662,11 +1792,11 @@ $repeatLabel lda $counterVar
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToRegister(left, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.UWORD, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
@ -1708,11 +1838,11 @@ $repeatLabel lda $counterVar
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD)
assignExpressionToRegister(left, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.WORD, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
@ -1755,11 +1885,11 @@ $repeatLabel lda $counterVar
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.UBYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
out(" pla")
}
return code("P8ZP_SCRATCH_B1")
@ -1798,11 +1928,11 @@ $repeatLabel lda $counterVar
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.BYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
out(" pla")
}
return code("P8ZP_SCRATCH_B1")
@ -1847,11 +1977,11 @@ $repeatLabel lda $counterVar
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToRegister(left, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.UWORD, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
@ -1898,11 +2028,11 @@ $repeatLabel lda $counterVar
return
if(right.isSimple()) {
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD, null)
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD)
assignExpressionToRegister(right, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.WORD, right)
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD, null)
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
@ -1946,11 +2076,11 @@ $repeatLabel lda $counterVar
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.UBYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
out(" pla")
}
return code("P8ZP_SCRATCH_B1")
@ -1989,11 +2119,11 @@ $repeatLabel lda $counterVar
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.BYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
out(" pla")
}
return code("P8ZP_SCRATCH_B1")
@ -2040,11 +2170,11 @@ $repeatLabel lda $counterVar
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToRegister(left, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.UWORD, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
@ -2095,11 +2225,11 @@ $repeatLabel lda $counterVar
}
if(right.isSimple()) {
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD, null)
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD)
assignExpressionToRegister(right, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.WORD, right)
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD, null)
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
@ -2139,11 +2269,11 @@ $repeatLabel lda $counterVar
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.UBYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
out(" pla")
}
return code("P8ZP_SCRATCH_B1")
@ -2179,11 +2309,11 @@ $repeatLabel lda $counterVar
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.BYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
out(" pla")
}
return code("P8ZP_SCRATCH_B1")
@ -2221,11 +2351,11 @@ $repeatLabel lda $counterVar
return
if(right.isSimple()) {
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToRegister(right, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.UWORD, right)
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
@ -2267,11 +2397,11 @@ $repeatLabel lda $counterVar
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD)
assignExpressionToRegister(left, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.WORD, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
@ -2310,14 +2440,14 @@ $repeatLabel lda $counterVar
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else if(right.isSimple()) {
assignExpressionToVariable(left, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
assignExpressionToVariable(left, "P8ZP_SCRATCH_B1", DataType.UBYTE)
assignExpressionToRegister(right, RegisterOrPair.A)
} else {
pushCpuStack(DataType.UBYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
restoreRegisterStack(CpuRegister.A, false)
}
out(" cmp P8ZP_SCRATCH_B1 | bne $jumpIfFalseLabel")
@ -2356,14 +2486,14 @@ $repeatLabel lda $counterVar
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else if(right.isSimple()) {
assignExpressionToVariable(left, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
assignExpressionToVariable(left, "P8ZP_SCRATCH_B1", DataType.UBYTE)
assignExpressionToRegister(right, RegisterOrPair.A)
} else {
pushCpuStack(DataType.UBYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
restoreRegisterStack(CpuRegister.A, false)
}
return code("P8ZP_SCRATCH_B1")
@ -2432,14 +2562,14 @@ $repeatLabel lda $counterVar
}
else -> {
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToRegister(left, RegisterOrPair.AY)
} else if(right.isSimple()) {
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToRegister(right, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.UWORD, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
@ -2518,14 +2648,14 @@ $repeatLabel lda $counterVar
}
else -> {
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToRegister(left, RegisterOrPair.AY)
} else if (right.isSimple()) {
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToRegister(right, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.UWORD, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
@ -2615,7 +2745,7 @@ $repeatLabel lda $counterVar
} else {
val subroutine = left.definingSub()!!
subroutineExtra(subroutine).usedFloatEvalResultVar1 = true
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
assignExpressionToRegister(left, RegisterOrPair.FAC1)
out("""
lda #<$subroutineFloatEvalResultVar1
@ -2700,7 +2830,7 @@ $repeatLabel lda $counterVar
} else {
val subroutine = left.definingSub()!!
subroutineExtra(subroutine).usedFloatEvalResultVar1 = true
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
assignExpressionToRegister(left, RegisterOrPair.FAC1)
out("""
lda #<$subroutineFloatEvalResultVar1
@ -2804,7 +2934,7 @@ $repeatLabel lda $counterVar
internal fun translateDirectMemReadExpressionToRegAorStack(expr: PtMemoryByte, pushResultOnEstack: Boolean) {
fun assignViaExprEval() {
assignExpressionToVariable(expr.address, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(expr.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
if (isTargetCpu(CpuType.CPU65c02)) {
if (pushResultOnEstack) {
out(" lda (P8ZP_SCRATCH_W2) | dex | sta P8ESTACK_LO+1,x")
@ -2834,7 +2964,17 @@ $repeatLabel lda $counterVar
out(" sta P8ESTACK_LO,x | dex")
}
is PtBinaryExpression -> {
if(tryOptimizedPointerAccessWithA(expr.address as PtBinaryExpression, false)) {
val addrExpr = expr.address as PtBinaryExpression
if(tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
if(pushResultOnEstack)
out(" sta P8ESTACK_LO,x | dex")
} else {
assignViaExprEval()
}
}
is PtRpn -> {
val addrExpr = expr.address as PtRpn
if(tryOptimizedPointerAccessWithA(addrExpr, addrExpr.finalOperator().operator, false)) {
if(pushResultOnEstack)
out(" sta P8ESTACK_LO,x | dex")
} else {

View File

@ -163,24 +163,24 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" cmp ${arg2.address.asConstInteger()!!.toHex()}")
} else {
if(arg1.isSimple()) {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp P8ZP_SCRATCH_B1")
} else {
asmgen.pushCpuStack(DataType.UBYTE, arg1)
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
}
}
}
else -> {
if(arg1.isSimple()) {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp P8ZP_SCRATCH_B1")
} else {
asmgen.pushCpuStack(DataType.UBYTE, arg1)
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
}
}
@ -209,7 +209,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
else -> {
if(arg1.isSimple()) {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, fcall.definingISub())
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD)
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
cpy P8ZP_SCRATCH_W1+1
@ -218,7 +218,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
+""")
} else {
asmgen.pushCpuStack(DataType.UWORD, arg1)
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, fcall.definingISub())
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out("""
@ -250,7 +250,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
val assign = AsmAssignment(src, target, program.memsizer, fcall.position)
asmgen.translateNormalAssignment(assign)
asmgen.translateNormalAssignment(assign, fcall.definingISub())
}
private fun funcSqrt16(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
@ -550,14 +550,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(arrayvar.type==DataType.UWORD) {
if(dt!='b')
throw AssemblyError("non-array var indexing requires bytes dt")
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD)
} else {
val addressOf = PtAddressOf(arrayvar.position)
addressOf.add(arrayvar)
addressOf.parent = arrayvar.parent.parent
asmgen.assignExpressionToVariable(addressOf, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
asmgen.assignExpressionToVariable(addressOf, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD)
}
asmgen.assignExpressionToVariable(indexer.index, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
asmgen.assignExpressionToVariable(indexer.index, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE)
}
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
@ -661,35 +661,41 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
return
}
}
is PtBinaryExpression -> {
if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) {
val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
if(asmgen.isZpVar(addrExpr.left as PtIdentifier)) {
// pointervar is already in the zero page, no need to copy
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
val index = (addrExpr.right as PtNumber).number.toHex()
asmgen.out("""
ldy #$index
is PtRpn, is PtBinaryExpression -> {
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
val pointer = result?.first as? PtIdentifier
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
// can do ZP,Y indexing
val varname = asmgen.asmVariableName(pointer)
val scope = fcall.definingISub()!!
asmgen.saveRegisterLocal(CpuRegister.X, scope)
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
asmgen.saveRegisterLocal(CpuRegister.Y, scope)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
asmgen.restoreRegisterLocal(CpuRegister.Y)
asmgen.out("""
sta ($varname),y
txa
iny
sta ($varname),y""")
asmgen.restoreRegisterLocal(CpuRegister.X)
return
}
asmgen.restoreRegisterLocal(CpuRegister.X)
return
}
}
else -> { /* fall through */ }
}
// fall through method:
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD, null)
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_pokew")
}
private fun funcPeekW(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
fun fallback() {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
}
when(val addrExpr = fcall.args[0]) {
is PtNumber -> {
val addr = addrExpr.number.toHex()
@ -715,38 +721,25 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
tay
pla""")
}
} else {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
}
} else fallback()
}
is PtBinaryExpression -> {
if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) {
val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
if(asmgen.isZpVar(addrExpr.left as PtIdentifier)) {
// pointervar is already in the zero page, no need to copy
val index = (addrExpr.right as PtNumber).number.toHex()
asmgen.out("""
ldy #$index
lda ($varname),y
pha
iny
lda ($varname),y
tay
pla""")
} else {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
}
} else {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
}
}
else -> {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
is PtRpn, is PtBinaryExpression -> {
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
val pointer = result?.first as? PtIdentifier
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
// can do ZP,Y indexing
val varname = asmgen.asmVariableName(pointer)
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
asmgen.out("""
lda ($varname),y
pha
iny
lda ($varname),y
tay
pla""")
} else fallback()
}
else -> fallback()
}
if(resultToStack){
@ -977,7 +970,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val addr = PtAddressOf(value.position)
addr.add(variable)
addr.parent = call
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT, scope)
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
AsmAssignSource.fromAstSource(addr, program, asmgen)
}
}
@ -1005,7 +998,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, conv.dt, null, value.position, variableAsmName = varname)
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
asmgen.translateNormalAssignment(assign)
asmgen.translateNormalAssignment(assign, scope)
}
conv.reg != null -> {
val src = when (conv.dt) {
@ -1023,7 +1016,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, value.position, null, asmgen)
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
asmgen.translateNormalAssignment(assign)
asmgen.translateNormalAssignment(assign, scope)
}
else -> throw AssemblyError("callconv")
}

View File

@ -25,6 +25,7 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
when(expression) {
is PtPrefix -> translateExpression(expression)
is PtBinaryExpression -> translateExpression(expression)
is PtRpn -> translateRpnExpression(expression)
is PtArrayIndexer -> translateExpression(expression)
is PtTypeCast -> translateExpression(expression)
is PtAddressOf -> translateExpression(expression)
@ -33,7 +34,7 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
is PtIdentifier -> translateExpression(expression)
is PtFunctionCall -> translateFunctionCallResultOntoStack(expression)
is PtBuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
is PtContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
is PtContainmentCheck -> translateContainmentCheck(expression)
is PtArray, is PtString -> throw AssemblyError("string/array literal value assignment should have been replaced by a variable")
is PtRange -> throw AssemblyError("range expression should have been changed into array values")
is PtMachineRegister -> throw AssemblyError("machine register ast node should not occur in 6502 codegen it is for IR code")
@ -41,6 +42,11 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
}
}
private fun translateContainmentCheck(check: PtContainmentCheck) {
asmgen.assignExpressionToRegister(check, RegisterOrPair.A)
asmgen.out(" sta P8ESTACK_LO,x | dex")
}
private fun translateFunctionCallResultOntoStack(call: PtFunctionCall) {
// only for use in nested expression evaluation
@ -239,18 +245,114 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
}
}
private fun translateRpnExpression(expr: PtRpn) {
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
val (left, oper, right) = expr.finalOperation()
if(left is PtExpression && right is PtExpression && translateSomewhatOptimized(left, oper.operator, right))
return
val leftDt = oper.leftType
val rightDt = oper.rightType
// comparison with zero
if(oper.operator in ComparisonOperators && leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
val rightVal = (expr.finalRightOperand() as PtExpression).asConstInteger()
if (rightVal == 0) {
when (left) {
is PtExpression -> {
translateComparisonWithZero(left, leftDt, oper.operator)
}
is PtRpnOperator -> {
expr.children.removeLast()
expr.children.removeLast()
translateComparisonWithZero(expr, leftDt, oper.operator)
}
else -> throw AssemblyError("weird rpn node")
}
return
}
}
// string compare
if(left is PtExpression && leftDt==DataType.STR && rightDt==DataType.STR && oper.operator in ComparisonOperators)
return translateCompareStrings(left, oper.operator, expr.finalRightOperand() as PtExpression)
// any other expression
if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes)
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))
throw AssemblyError("operator ${oper.operator} left/right dt not identical: $leftDt $rightDt right=${expr.finalRightOperand()}")
var depth=0
expr.children.forEach {
if(it is PtRpnOperator) {
when(it.leftType) {
in ByteDatatypes -> translateBinaryOperatorBytes(it.operator, it.leftType)
in WordDatatypes -> translateBinaryOperatorWords(it.operator, it.leftType)
DataType.FLOAT -> translateBinaryOperatorFloats(it.operator)
DataType.STR -> {
if(it.operator !in ComparisonOperators)
throw AssemblyError("expected only comparison operators on string, not ${oper.operator}")
asmgen.out(" jsr prog8_lib.strcmp_stack")
compareStringsProcessResultInA(it.operator)
}
else -> throw AssemblyError("non-numerical datatype ${it.leftType}")
}
depth--
} else {
translateExpressionInternal(it as PtExpression)
depth++
}
}
require(depth==1) { "unbalanced RPN: $depth ${expr.position}" }
}
private fun translateExpression(expr: PtBinaryExpression) {
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
require(!program.binaryExpressionsAreRPN)
if(translateSomewhatOptimized(expr.left, expr.operator, expr.right))
return
val leftDt = expr.left.type
val rightDt = expr.right.type
// see if we can apply some optimized routines still
when(expr.operator) {
// compare with zero
if(expr.operator in ComparisonOperators) {
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
val rightVal = expr.right.asConstInteger()
if(rightVal==0)
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
}
}
if(leftDt==DataType.STR && rightDt==DataType.STR && expr.operator in ComparisonOperators)
return translateCompareStrings(expr.left, expr.operator, expr.right)
if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes)
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical")
// the general, non-optimized cases
// TODO optimize more cases.... (or one day just don't use the evalstack at all anymore)
translateExpressionInternal(expr.left)
translateExpressionInternal(expr.right)
when (leftDt) {
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
DataType.FLOAT -> translateBinaryOperatorFloats(expr.operator)
else -> throw AssemblyError("non-numerical datatype")
}
}
private fun translateSomewhatOptimized(left: PtExpression, operator: String, right: PtExpression): Boolean {
val leftDt = left.type
val rightDt = right.type
when(operator) {
"+" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val leftVal = expr.left.asConstInteger()
val rightVal = expr.right.asConstInteger()
val leftVal = left.asConstInteger()
val rightVal = right.asConstInteger()
if (leftVal!=null && leftVal in -4..4) {
translateExpressionInternal(expr.right)
translateExpressionInternal(right)
if(rightDt in ByteDatatypes) {
val incdec = if(leftVal<0) "dec" else "inc"
repeat(leftVal.absoluteValue) {
@ -276,11 +378,11 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
}
}
}
return
return true
}
else if (rightVal!=null && rightVal in -4..4)
{
translateExpressionInternal(expr.left)
translateExpressionInternal(left)
if(leftDt in ByteDatatypes) {
val incdec = if(rightVal<0) "dec" else "inc"
repeat(rightVal.absoluteValue) {
@ -306,16 +408,16 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
}
}
}
return
return true
}
}
}
"-" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val rightVal = expr.right.asConstInteger()
val rightVal = right.asConstInteger()
if (rightVal!=null && rightVal in -4..4)
{
translateExpressionInternal(expr.left)
translateExpressionInternal(left)
if(leftDt in ByteDatatypes) {
val incdec = if(rightVal<0) "inc" else "dec"
repeat(rightVal.absoluteValue) {
@ -341,14 +443,14 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
}
}
}
return
return true
}
}
}
">>" -> {
val amount = expr.right.asConstInteger()
val amount = right.asConstInteger()
if(amount!=null) {
translateExpressionInternal(expr.left)
translateExpressionInternal(left)
when (leftDt) {
DataType.UBYTE -> {
if (amount <= 2)
@ -374,17 +476,17 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x")
return
return true
}
var left = amount
while (left >= 7) {
var amountLeft = amount
while (amountLeft >= 7) {
asmgen.out(" jsr math.shift_right_uw_7")
left -= 7
amountLeft -= 7
}
if (left in 0..2)
repeat(left) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
if (amountLeft in 0..2)
repeat(amountLeft) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
else
asmgen.out(" jsr math.shift_right_uw_$left")
asmgen.out(" jsr math.shift_right_uw_$amountLeft")
}
DataType.WORD -> {
if(amount>=16) {
@ -399,27 +501,27 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
sta P8ESTACK_LO+1,x
sta P8ESTACK_HI+1,x
+""")
return
return true
}
var left = amount
while (left >= 7) {
var amountLeft = amount
while (amountLeft >= 7) {
asmgen.out(" jsr math.shift_right_w_7")
left -= 7
amountLeft -= 7
}
if (left in 0..2)
repeat(left) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
if (amountLeft in 0..2)
repeat(amountLeft) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
else
asmgen.out(" jsr math.shift_right_w_$left")
asmgen.out(" jsr math.shift_right_w_$amountLeft")
}
else -> throw AssemblyError("weird type")
}
return
return true
}
}
"<<" -> {
val amount = expr.right.asConstInteger()
val amount = right.asConstInteger()
if(amount!=null) {
translateExpressionInternal(expr.left)
translateExpressionInternal(left)
if (leftDt in ByteDatatypes) {
if (amount <= 2)
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
@ -429,78 +531,80 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
asmgen.out(" sta P8ESTACK_LO+1,x")
}
} else {
var left = amount
while (left >= 7) {
var amountLeft = amount
while (amountLeft >= 7) {
asmgen.out(" jsr math.shift_left_w_7")
left -= 7
amountLeft -= 7
}
if (left in 0..2)
repeat(left) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") }
if (amountLeft in 0..2)
repeat(amountLeft) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") }
else
asmgen.out(" jsr math.shift_left_w_$left")
asmgen.out(" jsr math.shift_left_w_$amountLeft")
}
return
return true
}
}
"*" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val leftVar = expr.left as? PtIdentifier
val rightVar = expr.right as? PtIdentifier
if(leftVar!=null && rightVar!=null && leftVar==rightVar)
return translateSquared(leftVar, leftDt)
val leftVar = left as? PtIdentifier
val rightVar = right as? PtIdentifier
if(leftVar!=null && rightVar!=null && leftVar==rightVar) {
translateSquared(leftVar, leftDt)
return true
}
}
val value = expr.right as? PtNumber
val value = right as? PtNumber
if(value!=null) {
if(rightDt in IntegerDatatypes) {
val amount = value.number.toInt()
if(amount==2) {
// optimize x*2 common case
translateExpressionInternal(expr.left)
translateExpressionInternal(left)
if(leftDt in ByteDatatypes) {
asmgen.out(" asl P8ESTACK_LO+1,x")
} else {
asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x")
}
return
return true
}
when(rightDt) {
DataType.UBYTE -> {
if(amount in asmgen.optimizedByteMultiplications) {
translateExpressionInternal(expr.left)
translateExpressionInternal(left)
asmgen.out(" jsr math.stack_mul_byte_$amount")
return
return true
}
}
DataType.BYTE -> {
if(amount in asmgen.optimizedByteMultiplications) {
translateExpressionInternal(expr.left)
translateExpressionInternal(left)
asmgen.out(" jsr math.stack_mul_byte_$amount")
return
return true
}
if(amount.absoluteValue in asmgen.optimizedByteMultiplications) {
translateExpressionInternal(expr.left)
translateExpressionInternal(left)
asmgen.out(" jsr prog8_lib.neg_b | jsr math.stack_mul_byte_${amount.absoluteValue}")
return
return true
}
}
DataType.UWORD -> {
if(amount in asmgen.optimizedWordMultiplications) {
translateExpressionInternal(expr.left)
translateExpressionInternal(left)
asmgen.out(" jsr math.stack_mul_word_$amount")
return
return true
}
}
DataType.WORD -> {
if(amount in asmgen.optimizedWordMultiplications) {
translateExpressionInternal(expr.left)
translateExpressionInternal(left)
asmgen.out(" jsr math.stack_mul_word_$amount")
return
return true
}
if(amount.absoluteValue in asmgen.optimizedWordMultiplications) {
translateExpressionInternal(expr.left)
translateExpressionInternal(left)
asmgen.out(" jsr prog8_lib.neg_w | jsr math.stack_mul_word_${amount.absoluteValue}")
return
return true
}
}
else -> {}
@ -510,9 +614,9 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
}
"/" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val rightVal = expr.right.asConstInteger()
val rightVal = right.asConstInteger()
if(rightVal!=null && rightVal==2) {
translateExpressionInternal(expr.left)
translateExpressionInternal(left)
when (leftDt) {
DataType.UBYTE -> {
asmgen.out(" lsr P8ESTACK_LO+1,x")
@ -545,38 +649,13 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
}
else -> throw AssemblyError("weird dt")
}
return
return true
}
}
}
in ComparisonOperators -> {
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
val rightVal = expr.right.asConstInteger()
if(rightVal==0)
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
}
}
}
if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes)
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical")
if(leftDt==DataType.STR && rightDt==DataType.STR && expr.operator in ComparisonOperators) {
translateCompareStrings(expr.left, expr.operator, expr.right)
}
else {
// the general, non-optimized cases
// TODO optimize more cases.... (or one day just don't use the evalstack at all anymore)
translateExpressionInternal(expr.left)
translateExpressionInternal(expr.right)
when (leftDt) {
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
DataType.FLOAT -> translateBinaryOperatorFloats(expr.operator)
else -> throw AssemblyError("non-numerical datatype")
}
}
return false
}
private fun translateComparisonWithZero(expr: PtExpression, dt: DataType, operator: String) {
@ -871,9 +950,13 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
}
private fun translateCompareStrings(s1: PtExpression, operator: String, s2: PtExpression) {
asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD, null)
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD, null)
asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD)
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD)
asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A
compareStringsProcessResultInA(operator)
}
private fun compareStringsProcessResultInA(operator: String) {
when(operator) {
"==" -> asmgen.out(" and #1 | eor #1 | sta P8ESTACK_LO,x")
"!=" -> asmgen.out(" and #1 | sta P8ESTACK_LO,x")

View File

@ -50,8 +50,8 @@ internal class ForLoopsAsmGen(private val program: PtProgram,
val incdec = if(stepsize==1) "inc" else "dec"
// loop over byte range via loopvar
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt))
asmgen.out(loopLabel)
asmgen.translate(stmt.statements)
asmgen.out("""
@ -68,8 +68,8 @@ $modifiedLabel cmp #0 ; modified
// loop over byte range via loopvar
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt))
asmgen.out(loopLabel)
asmgen.translate(stmt.statements)
if(stepsize>0) {
@ -594,6 +594,5 @@ $loopLabel""")
asmgen.assignExpressionToVariable(
range.from,
asmgen.asmVariableName(stmt.variable),
stmt.variable.type,
stmt.definingISub())
stmt.variable.type)
}

View File

@ -141,7 +141,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
throw AssemblyError("argument type incompatible")
val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name)
asmgen.assignExpressionToVariable(value, varName, parameter.type, sub)
asmgen.assignExpressionToVariable(value, varName, parameter.type)
}
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null) {
@ -198,16 +198,17 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
register!!
if(requiredDt largerThan value.type) {
// we need to sign extend the source, do this via temporary word variable
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE, sub)
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE)
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type)
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, Position.DUMMY)
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
} else {
val scope = value.definingISub()
val target: AsmAssignTarget =
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, sub, value.position, register = register)
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register)
else {
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD
AsmAssignTarget.fromRegisters(register, signed, value.position, sub, asmgen)
AsmAssignTarget.fromRegisters(register, signed, value.position, scope, asmgen)
}
val src = if(value.type in PassByReferenceDatatypes) {
if(value is PtIdentifier) {
@ -221,7 +222,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} else {
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
}
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY))
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY), scope)
}
}
}

View File

@ -359,13 +359,18 @@ internal class ProgramAndVarsGen(
asmgen.out("; variables")
val asmGenInfo = asmgen.subroutineExtra(sub)
// TODO move these to BSS as well
for((dt, name, addr) in asmGenInfo.extraVars) {
if(addr!=null)
asmgen.out("$name = $addr")
else when(dt) {
DataType.UBYTE -> asmgen.out("$name .byte 0")
DataType.UWORD -> asmgen.out("$name .word 0")
else -> throw AssemblyError("weird dt")
DataType.FLOAT -> {
require(options.compTarget.machine.FLOAT_MEM_SIZE==5)
asmgen.out("$name .byte 0,0,0,0,0")
}
else -> throw AssemblyError("weird dt for extravar $dt")
}
}
if(asmGenInfo.usedRegsaveA) // will probably never occur

View File

@ -102,6 +102,28 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers)
}
}
fun isSameAs(left: PtExpression): Boolean =
when(kind) {
TargetStorageKind.VARIABLE -> {
val scopedName: String = if('.' in asmVarname)
asmVarname
else {
val scopeName = (scope as? PtNamedNode)?.scopedName
if (scopeName == null) asmVarname else "$scopeName.$asmVarname"
}
left is PtIdentifier && left.name==scopedName
}
TargetStorageKind.ARRAY -> {
left is PtArrayIndexer && left isSameAs array!!
}
TargetStorageKind.MEMORY -> {
left isSameAs memory!!
}
TargetStorageKind.REGISTER, TargetStorageKind.STACK -> {
false
}
}
}
internal class AsmAssignSource(val kind: SourceStorageKind,

View File

@ -1,13 +1,17 @@
package prog8.codegen.cpu6502.assignment
import prog8.code.StStaticVariable
import prog8.code.SymbolTable
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.VariableAllocator
import prog8.codegen.cpu6502.returnsWhatWhere
import java.util.*
internal class AssignmentAsmGen(private val program: PtProgram,
private val symbolTable: SymbolTable,
private val asmgen: AsmGen6502Internal,
private val allocator: VariableAllocator) {
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator)
@ -16,17 +20,17 @@ internal class AssignmentAsmGen(private val program: PtProgram,
val target = AsmAssignTarget.fromAstAssignment(assignment.target, assignment.definingISub(), asmgen)
val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target)
val assign = AsmAssignment(source, target, program.memsizer, assignment.position)
translateNormalAssignment(assign)
translateNormalAssignment(assign, assignment.definingISub())
}
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)
augmentableAsmGen.translate(assign)
augmentableAsmGen.translate(assign, augmentedAssign.definingISub())
}
fun translateNormalAssignment(assign: AsmAssignment) {
fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) {
when(assign.source.kind) {
SourceStorageKind.LITERALNUMBER -> {
// simple case: assign a constant number
@ -127,7 +131,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
SourceStorageKind.MEMORY -> {
fun assignViaExprEval(expression: PtExpression) {
assignExpressionToVariable(expression, "P8ZP_SCRATCH_W2", DataType.UWORD, assign.target.scope)
assignExpressionToVariable(expression, "P8ZP_SCRATCH_W2", DataType.UWORD)
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
assignRegisterByte(assign.target, CpuRegister.A)
}
@ -141,8 +145,17 @@ internal class AssignmentAsmGen(private val program: PtProgram,
is PtIdentifier -> {
assignMemoryByte(assign.target, null, value.address as PtIdentifier)
}
is PtRpn -> {
val addrExpr = value.address as PtRpn
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.finalOperator().operator, false)) {
assignRegisterByte(assign.target, CpuRegister.A)
} else {
assignViaExprEval(value.address)
}
}
is PtBinaryExpression -> {
if(asmgen.tryOptimizedPointerAccessWithA(value.address as PtBinaryExpression, false)) {
val addrExpr = value.address as PtBinaryExpression
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
assignRegisterByte(assign.target, CpuRegister.A)
} else {
assignViaExprEval(value.address)
@ -152,7 +165,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
SourceStorageKind.EXPRESSION -> {
assignExpression(assign)
assignExpression(assign, scope)
}
SourceStorageKind.REGISTER -> {
asmgen.assignRegister(assign.source.register!!, assign.target)
@ -164,7 +177,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
private fun assignExpression(assign: AsmAssignment) {
private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) {
when(val value = assign.source.expression!!) {
is PtAddressOf -> {
val sourceName = asmgen.asmSymbolName(value.identifier)
@ -277,17 +290,17 @@ internal class AssignmentAsmGen(private val program: PtProgram,
AsmAssignment(
AsmAssignSource.fromAstSource(value.value, program, asmgen),
assign.target, program.memsizer, assign.position
)
), scope
)
when (value.operator) {
"+" -> {}
"-" -> inplaceNegate(assign, true)
"~" -> inplaceInvert(assign)
"-" -> inplaceNegate(assign, true, scope)
"~" -> inplaceInvert(assign, scope)
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
else -> throw AssemblyError("invalid prefix operator")
}
} else {
assignPrefixedExpressionToArrayElt(assign)
assignPrefixedExpressionToArrayElt(assign, scope)
}
}
is PtContainmentCheck -> {
@ -295,6 +308,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignRegisterByte(assign.target, CpuRegister.A)
}
is PtBinaryExpression -> {
require(!program.binaryExpressionsAreRPN)
if(!attemptAssignOptimizedBinexpr(value, assign)) {
// All remaining binary expressions just evaluate via the stack for now.
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
@ -302,11 +316,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
fallbackToStackEval(assign)
}
}
is PtRpn -> {
if(!attemptAssignOptimizedExprRPN(assign, scope!!)) {
// All remaining binary expressions just evaluate via the stack for now.
// TODO: For RPN expressions this should never occur anymore and the eval stack should be removed when we achieve this
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
// because the code here is the implementation of exactly that...)
fallbackToStackEval(assign)
}
}
else -> throw AssemblyError("weird assignment value type $value")
}
}
private fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment) {
private fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment, scope: IPtSubroutine?) {
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,7 +341,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
val assignToTempvar = AsmAssignment(assign.source,
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, assign.target.datatype, assign.target.scope, assign.target.position,
variableAsmName=tempvar, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position)
asmgen.translateNormalAssignment(assignToTempvar)
asmgen.translateNormalAssignment(assignToTempvar, scope)
when(assign.target.datatype) {
in ByteDatatypes -> assignVariableByte(assign.target, tempvar)
in WordDatatypes -> assignVariableWord(assign.target, tempvar)
@ -339,6 +362,253 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
private fun attemptAssignOptimizedExprRPN(assign: AsmAssignment, scope: IPtSubroutine): Boolean {
val value = assign.source.expression as PtRpn
val (left, oper, right) = value.finalOperation()
if(oper.operator in ComparisonOperators) {
assignRPNComparison(assign, value)
return true
}
if(value.children.size==3 && left is PtExpression && right is PtExpression)
if(simpleLogicalExprRPN(left, oper.operator, right, assign.target))
return true
// TODO RPN add +,-,<<,>> and perhaps even == and != special behaviors of BinExpr
val asmExtra = asmgen.subroutineExtra(scope)
val evalVars = mutableMapOf (
DataType.UBYTE to Stack<String>(),
DataType.UWORD to Stack<String>(),
DataType.FLOAT to Stack<String>()
)
fun getVarDt(dt: DataType) =
when(dt) {
in ByteDatatypes -> DataType.UBYTE
in WordDatatypes, in PassByReferenceDatatypes -> DataType.UWORD
else -> dt
}
fun evalVarName(dt: DataType, depth: Int): String {
val name: String
val varDt: DataType
when(dt) {
in ByteDatatypes -> {
name = "p8_rpn_eval_byte_$depth"
varDt = DataType.UBYTE
}
in WordDatatypes, in PassByReferenceDatatypes -> {
name = "p8_rpn_eval_word_$depth"
varDt = DataType.UWORD
}
DataType.FLOAT -> {
name = "p8_rpn_eval_float_$depth"
varDt = DataType.FLOAT
}
else -> throw AssemblyError("weird dt")
}
evalVars.getValue(varDt).push(name)
if(!asmExtra.extraVars.any { it.second==name }) {
val stScope = symbolTable.lookup((scope as PtNamedNode).scopedName)!!
val dummyNode = PtVariable(name, varDt, ZeropageWish.DONTCARE, null, null, Position.DUMMY)
dummyNode.parent = scope
stScope.add(StStaticVariable(name, varDt, null, null, null, null, ZeropageWish.DONTCARE, dummyNode))
asmExtra.extraVars.add(Triple(varDt, name, null))
}
return name
}
var depth=0
value.children.forEach {
when (it) {
is PtRpnOperator -> {
val rightvar = evalVars.getValue(getVarDt(it.rightType)).pop()
val leftvar = evalVars.getValue(getVarDt(it.leftType)).pop()
depth-=2
val resultVarname = evalVarName(it.type, depth)
depth++
symbolTable.resetCachedFlat()
if(it.operator in ComparisonOperators) {
require(it.type == DataType.UBYTE)
val scopeName = (scope as PtNamedNode).scopedName
val comparison = PtRpn(DataType.UBYTE, assign.position)
comparison.addRpnNode(PtIdentifier("$scopeName.$leftvar", it.type, value.position))
comparison.addRpnNode(PtIdentifier("$scopeName.$rightvar", it.rightType, value.position))
comparison.addRpnNode(PtRpnOperator(it.operator, it.type, it.leftType, it.rightType, it.position))
comparison.parent = scope
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = comparison)
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, scope, assign.position, variableAsmName = resultVarname)
val normalAssign = AsmAssignment(src, target, program.memsizer, assign.position)
assignRPNComparison(normalAssign, comparison)
} else {
require(resultVarname==leftvar) {
"expected result $resultVarname == leftvar $leftvar"
}
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, it.rightType, variableAsmName = rightvar)
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, it.type, scope, assign.position, variableAsmName = resultVarname)
val augAssign = AsmAugmentedAssignment(src, it.operator+"=", target, program.memsizer, assign.position)
augmentableAsmGen.translate(augAssign, scope)
}
}
is PtExpression -> {
val varname = evalVarName(it.type, depth)
assignExpressionToVariable(it, varname, it.type)
depth++
}
else -> throw AssemblyError("weird rpn node")
}
}
require(depth==1) { "unbalanced RPN: $depth ${value.position}" }
val resultVariable = evalVars.getValue(getVarDt(value.type)).pop()
when(assign.target.datatype) {
in ByteDatatypes -> assignVariableByte(assign.target, resultVariable)
in WordDatatypes -> assignVariableWord(assign.target, resultVariable)
DataType.FLOAT -> assignVariableFloat(assign.target, resultVariable)
else -> throw AssemblyError("weird dt")
}
require(evalVars.all { it.value.isEmpty() }) { "invalid rpn evaluation" }
return true
}
private fun simpleLogicalExprRPN(left: PtExpression, operator: String, right: PtExpression, target: AsmAssignTarget): Boolean {
fun simpleLogicalBytesExpr() {
// both left and right expression operands are simple.
if (right is PtNumber || right is PtIdentifier)
assignLogicalWithSimpleRightOperandByte(target, left, operator, right)
else if (left is PtNumber || left is PtIdentifier)
assignLogicalWithSimpleRightOperandByte(target, right, operator, left)
else {
assignExpressionToRegister(left, RegisterOrPair.A, false)
asmgen.saveRegisterStack(CpuRegister.A, false)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.restoreRegisterStack(CpuRegister.A, false)
when (operator) {
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
else -> throw AssemblyError("invalid operator")
}
assignRegisterByte(target, CpuRegister.A)
}
}
fun simpleLogicalWordsExpr() {
// both left and right expression operands are simple.
if (right is PtNumber || right is PtIdentifier)
assignLogicalWithSimpleRightOperandWord(target, left, operator, right)
else if (left is PtNumber || left is PtIdentifier)
assignLogicalWithSimpleRightOperandWord(target, right, operator, left)
else {
assignExpressionToRegister(left, RegisterOrPair.AY, false)
asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, false)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W1", DataType.UWORD)
when (operator) {
"&", "and" -> asmgen.out(" pla | and P8ZP_SCRATCH_W1+1 | tay | pla | and P8ZP_SCRATCH_W1")
"|", "or" -> asmgen.out(" pla | ora P8ZP_SCRATCH_W1+1 | tay | pla | ora P8ZP_SCRATCH_W1")
"^", "xor" -> asmgen.out(" pla | eor P8ZP_SCRATCH_W1+1 | tay | pla | eor P8ZP_SCRATCH_W1")
else -> throw AssemblyError("invalid operator")
}
assignRegisterpairWord(target, RegisterOrPair.AY)
}
}
if(operator in setOf("&", "|", "^", "and", "or", "xor")) {
if (left.type in ByteDatatypes && right.type in ByteDatatypes) {
if (right.isSimple()) {
simpleLogicalBytesExpr()
return true
}
}
if (left.type in WordDatatypes && right.type in WordDatatypes) {
if (right.isSimple()) {
simpleLogicalWordsExpr()
return true
}
}
}
return false
}
private fun assignRPNComparison(assign: AsmAssignment, comparison: PtRpn) {
val (leftRpn, oper, right) = comparison.finalOperation()
val constRight = (right as PtExpression).asConstInteger()
if(constRight == 0) {
if(oper.operator == "==" || oper.operator == "!=") {
when(assign.target.datatype) {
in ByteDatatypes -> {
if(attemptAssignToByteCompareZeroRPN(comparison, assign))
return
}
in WordDatatypes -> {
assignConstantWord(assign.target, 0)
if(attemptAssignToByteCompareZeroRPN(comparison, assign))
return
}
else -> {
// do nothing, this is handled by a type cast.
}
}
}
}
val left: PtExpression = if(comparison.children.size>3 || leftRpn !is PtExpression) {
comparison.children.removeLast()
comparison.children.removeLast()
comparison
} else
leftRpn
val leftNum = left as? PtNumber
val rightNum = right as? PtNumber
val jumpIfFalseLabel = asmgen.makeLabel("cmp")
if(assign.target.isSameAs(left)) {
// In-place comparison Target = Target <compare> Right
val targetDt = assign.target.datatype
val tempVar = asmgen.getTempVarName(assign.target.datatype)
val tempTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, targetDt, comparison.definingISub(), comparison.position, variableAsmName = tempVar)
when (assign.target.datatype) {
in ByteDatatypes -> assignConstantByte(tempTarget, 0)
in WordDatatypes -> assignConstantWord(tempTarget, 0)
DataType.FLOAT -> assignConstantFloat(tempTarget, 0.0)
else -> throw AssemblyError("invalid dt")
}
asmgen.testNonzeroComparisonAndJump(left, oper.operator, right, jumpIfFalseLabel, leftNum, rightNum)
when (assign.target.datatype) {
in ByteDatatypes -> assignConstantByte(tempTarget, 1)
in WordDatatypes -> assignConstantWord(tempTarget, 1)
DataType.FLOAT -> assignConstantFloat(tempTarget, 1.0)
else -> throw AssemblyError("invalid dt")
}
asmgen.out(jumpIfFalseLabel)
val tempLeft = PtIdentifier(tempVar, targetDt, comparison.position)
tempLeft.parent=comparison
asmgen.assignExpressionTo(tempLeft, assign.target)
} else {
// Normal comparison Target = Left <compare> Right
when (assign.target.datatype) {
in ByteDatatypes -> assignConstantByte(assign.target, 0)
in WordDatatypes -> assignConstantWord(assign.target, 0)
DataType.FLOAT -> assignConstantFloat(assign.target, 0.0)
else -> throw AssemblyError("invalid dt")
}
asmgen.testNonzeroComparisonAndJump(left, oper.operator, right, jumpIfFalseLabel, leftNum, rightNum)
when (assign.target.datatype) {
in ByteDatatypes -> assignConstantByte(assign.target, 1)
in WordDatatypes -> assignConstantWord(assign.target, 1)
DataType.FLOAT -> assignConstantFloat(assign.target, 1.0)
else -> throw AssemblyError("invalid dt")
}
asmgen.out(jumpIfFalseLabel)
}
}
private fun attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
if(expr.operator in ComparisonOperators) {
if(expr.right.asConstInteger() == 0) {
@ -384,7 +654,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
else {
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.saveRegisterStack(CpuRegister.A, false)
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE, expr.definingISub())
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.restoreRegisterStack(CpuRegister.A, false)
when (expr.operator) {
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
@ -406,7 +676,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignExpressionToRegister(expr.left, RegisterOrPair.AY, false)
asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, false)
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD, expr.definingISub())
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD)
when (expr.operator) {
"&", "and" -> asmgen.out(" pla | and P8ZP_SCRATCH_W1+1 | tay | pla | and P8ZP_SCRATCH_W1")
"|", "or" -> asmgen.out(" pla | ora P8ZP_SCRATCH_W1+1 | tay | pla | ora P8ZP_SCRATCH_W1")
@ -439,7 +709,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
expr.left.isSimple() && expr.right.isSimple()) {
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.saveRegisterStack(CpuRegister.A, false)
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE, expr.definingISub())
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.restoreRegisterStack(CpuRegister.A, false)
if(expr.operator=="==") {
asmgen.out("""
@ -465,7 +735,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignExpressionToRegister(expr.left, RegisterOrPair.AY, false)
asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, false)
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD, expr.definingISub())
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false)
if(expr.operator=="==") {
@ -721,6 +991,84 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignRegisterpairWord(target, RegisterOrPair.AY)
}
private fun attemptAssignToByteCompareZeroRPN(expr: PtRpn, assign: AsmAssignment): Boolean {
val (leftRpn, oper, right) = expr.finalOperation()
val left = if(expr.children.size!=3 || leftRpn !is PtExpression) {
expr.children.removeLast()
expr.children.removeLast()
expr
} else
leftRpn
when (oper.operator) {
"==" -> {
when(val dt = left.type) {
in ByteDatatypes -> {
assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE)
asmgen.out("""
beq +
lda #0
beq ++
+ lda #1
+""")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
in WordDatatypes -> {
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
asmgen.out("""
sty P8ZP_SCRATCH_B1
ora P8ZP_SCRATCH_B1
beq +
lda #0
beq ++
+ lda #1
+""")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
DataType.FLOAT -> {
assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN | and #1 | eor #1")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
else->{
return false
}
}
}
"!=" -> {
when(val dt = left.type) {
in ByteDatatypes -> {
assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE)
asmgen.out(" beq + | lda #1")
asmgen.out("+")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
in WordDatatypes -> {
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1")
asmgen.out(" beq + | lda #1")
asmgen.out("+")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
DataType.FLOAT -> {
assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
else->{
return false
}
}
}
else -> return false
}
}
private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when (expr.operator) {
"==" -> {
@ -836,7 +1184,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
return
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt, containment.definingISub())
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W2"), varname)
asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_wordarray")
@ -886,7 +1234,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
if(targetDt in WordDatatypes) {
fun assignViaExprEval(addressExpression: PtExpression) {
asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD)
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
asmgen.out(" ldy #0")
assignRegisterpairWord(target, RegisterOrPair.AY)
@ -900,8 +1248,18 @@ internal class AssignmentAsmGen(private val program: PtProgram,
is PtIdentifier -> {
assignMemoryByteIntoWord(target, null, value.address as PtIdentifier)
}
is PtRpn -> {
val addrExpr = value.address as PtRpn
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.finalOperator().operator, false)) {
asmgen.out(" ldy #0")
assignRegisterpairWord(target, RegisterOrPair.AY)
} else {
assignViaExprEval(value.address)
}
}
is PtBinaryExpression -> {
if(asmgen.tryOptimizedPointerAccessWithA(value.address as PtBinaryExpression, false)) {
val addrExpr = value.address as PtBinaryExpression
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
asmgen.out(" ldy #0")
assignRegisterpairWord(target, RegisterOrPair.AY)
} else {
@ -940,7 +1298,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
in PassByReferenceDatatypes -> {
// str/array value cast (most likely to UWORD, take address-of)
assignExpressionToVariable(value, target.asmVarname, targetDt, null)
assignExpressionToVariable(value, target.asmVarname, targetDt)
}
else -> throw AssemblyError("strange dt in typecast assign to var: $valueDt --> $targetDt")
}
@ -998,7 +1356,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
// have to typecast the float number on the fly down to an integer
assignExpressionToRegister(value, RegisterOrPair.FAC1, target.datatype in SignedDatatypes)
assignTypeCastedFloatFAC1("P8ZP_SCRATCH_W1", target.datatype)
assignVariableToRegister("P8ZP_SCRATCH_W1", target.register!!, target.datatype in SignedDatatypes, target.position)
assignVariableToRegister("P8ZP_SCRATCH_W1", target.register!!, target.datatype in SignedDatatypes, origTypeCastExpression.definingISub(), target.position)
return
} else {
if(!(valueDt isAssignableTo targetDt)) {
@ -1103,7 +1461,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
lsb.add(value)
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb)
val assign = AsmAssignment(src, target, program.memsizer, value.position)
translateNormalAssignment(assign)
translateNormalAssignment(assign, value.definingISub())
}
private fun assignTypeCastedFloatFAC1(targetAsmVarName: String, targetDt: DataType) {
@ -2801,13 +3159,13 @@ internal class AssignmentAsmGen(private val program: PtProgram,
fun storeViaExprEval() {
when(addressExpr) {
is PtNumber, is PtIdentifier -> {
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD)
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
}
else -> {
// same as above but we need to save the A register
asmgen.out(" pha")
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD)
asmgen.out(" pla")
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
}
@ -2821,8 +3179,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
addressExpr is PtIdentifier -> {
asmgen.storeAIntoPointerVar(addressExpr)
}
addressExpr is PtRpn -> {
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.finalOperator().operator, true))
storeViaExprEval()
}
addressExpr is PtBinaryExpression -> {
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true))
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true))
storeViaExprEval()
}
else -> storeViaExprEval()
@ -2833,28 +3195,28 @@ internal class AssignmentAsmGen(private val program: PtProgram,
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
val tgt = AsmAssignTarget.fromRegisters(register, signed, expr.position, null, asmgen)
val assign = AsmAssignment(src, tgt, program.memsizer, expr.position)
translateNormalAssignment(assign)
translateNormalAssignment(assign, expr.definingISub())
}
internal fun assignExpressionToVariable(expr: PtExpression, asmVarName: String, dt: DataType, scope: IPtSubroutine?) {
internal fun assignExpressionToVariable(expr: PtExpression, asmVarName: String, dt: DataType) {
if(expr.type==DataType.FLOAT && dt!=DataType.FLOAT) {
throw AssemblyError("can't directly assign a FLOAT expression to an integer variable $expr")
} else {
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, scope, expr.position, variableAsmName = asmVarName)
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, expr.definingISub(), expr.position, variableAsmName = asmVarName)
val assign = AsmAssignment(src, tgt, program.memsizer, expr.position)
translateNormalAssignment(assign)
translateNormalAssignment(assign, expr.definingISub())
}
}
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean, pos: Position) {
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean, scope: IPtSubroutine?, pos: Position) {
val tgt = AsmAssignTarget.fromRegisters(register, signed, pos, null, asmgen)
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName)
val assign = AsmAssignment(src, tgt, program.memsizer, Position.DUMMY)
translateNormalAssignment(assign)
translateNormalAssignment(assign, scope)
}
internal fun inplaceInvert(assign: AsmAssignment) {
internal fun inplaceInvert(assign: AsmAssignment, scope: IPtSubroutine?) {
val target = assign.target
when (assign.target.datatype) {
DataType.UBYTE -> {
@ -2881,7 +3243,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" sta ($sourceName),y")
}
else -> {
asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD, target.scope)
asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
asmgen.out("""
ldy #0
lda (P8ZP_SCRATCH_W2),y
@ -2899,7 +3261,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
TargetStorageKind.STACK -> TODO("no asm gen for byte stack invert")
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign))
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign), scope)
else -> throw AssemblyError("weird target")
}
}
@ -2924,7 +3286,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
TargetStorageKind.STACK -> TODO("no asm gen for word stack invert")
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign))
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign), scope)
else -> throw AssemblyError("weird target")
}
}
@ -2932,7 +3294,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
internal fun inplaceNegate(assign: AsmAssignment, ignoreDatatype: Boolean) {
internal fun inplaceNegate(assign: AsmAssignment, ignoreDatatype: Boolean, scope: IPtSubroutine?) {
val target = assign.target
val datatype = if(ignoreDatatype) {
when(target.datatype) {
@ -2967,7 +3329,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
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))
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
else -> throw AssemblyError("weird target")
}
}
@ -3027,7 +3389,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
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))
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
else -> throw AssemblyError("weird target")
}
}
@ -3049,7 +3411,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
""")
}
TargetStorageKind.STACK -> TODO("no asm gen for float stack negate")
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign))
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
else -> throw AssemblyError("weird target for in-place float negation")
}
}

View File

@ -11,16 +11,16 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
private val asmgen: AsmGen6502Internal,
private val allocator: VariableAllocator
) {
fun translate(assign: AsmAugmentedAssignment) {
fun translate(assign: AsmAugmentedAssignment, scope: IPtSubroutine?) {
when(assign.operator) {
"-" -> {
val a2 = AsmAssignment(assign.source, assign.target, assign.memsizer, assign.position)
assignmentAsmGen.inplaceNegate(a2, false)
assignmentAsmGen.inplaceNegate(a2, false, scope)
}
"~" -> {
val a2 = AsmAssignment(assign.source, assign.target, assign.memsizer, assign.position)
assignmentAsmGen.inplaceInvert(a2)
assignmentAsmGen.inplaceInvert(a2, scope)
}
"+" -> { /* is a nop */ }
else -> {
@ -44,6 +44,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"^=" -> 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}")
}
}
@ -303,7 +304,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
private fun inplaceModification_byte_value_to_pointer(pointervar: PtIdentifier, operator: String, value: PtExpression) {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE)
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
when (operator) {
// note: ** (power) operator requires floats.
@ -453,7 +454,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(" clc | adc $name | sta $name")
}
"-" -> {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", dt, null)
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", dt)
asmgen.out(" lda $name | sec | sbc P8ZP_SCRATCH_B1 | sta $name")
}
"*" -> {
@ -1358,7 +1359,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
when (operator) {
// note: ** (power) operator requires floats.
"+" -> {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", valueDt, null)
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", valueDt)
if(valueDt==DataType.UBYTE)
asmgen.out("""
lda $name
@ -1382,7 +1383,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
sta $name+1""")
}
"-" -> {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", valueDt, null)
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", valueDt)
if(valueDt==DataType.UBYTE)
asmgen.out("""
lda $name
@ -1488,7 +1489,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(" clc | adc $name | sta $name | tya | adc $name+1 | sta $name+1")
}
"-" -> {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", valueDt, null)
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", valueDt)
asmgen.out(" lda $name | sec | sbc P8ZP_SCRATCH_W1 | sta $name | lda $name+1 | sbc P8ZP_SCRATCH_W1+1 | sta $name+1")
}
"*" -> {

View File

@ -2,10 +2,7 @@ package prog8.codegen.experimental
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram
import prog8.code.core.ICodeGeneratorBackend
import prog8.code.core.IErrorReporter
import prog8.code.core.*
import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileWriter
@ -16,6 +13,12 @@ class ExperiCodeGen: ICodeGeneratorBackend {
options: CompilationOptions,
errors: IErrorReporter
): IAssemblyProgram? {
if(options.useRPN) {
program.transformBinExprToRPN()
errors.warn("EXPERIMENTAL RPN EXPRESSION NODES ARE USED. CODE SIZE+SPEED WILL SUFFER.", Position.DUMMY)
}
// you could write a code generator directly on the PtProgram AST,
// but you can also use the Intermediate Representation to build a codegen on:
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)

View File

@ -92,10 +92,19 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
value.add(origAssign.value)
} 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)
if(codeGen.program.binaryExpressionsAreRPN) {
value = PtRpn(origAssign.value.type, origAssign.value.position)
val left = origAssign.target.children.single() as PtExpression
val right = origAssign.value
value.add(left)
value.add(right)
value.add(PtRpnOperator(origAssign.operator.dropLast(1), origAssign.target.type, left.type, right.type, origAssign.position))
} else {
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)
}
}
normalAssign.add(value)
return translateRegularAssign(normalAssign)
@ -262,9 +271,19 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val tr = if(itemsize==1) {
expressionEval.translateExpression(array.index)
} else {
val mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
val mult : PtExpression
if(codeGen.program.binaryExpressionsAreRPN) {
mult = PtRpn(DataType.UBYTE, array.position)
val left = array.index
val right = PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
mult.add(left)
mult.add(right)
mult.add(PtRpnOperator("*", DataType.UBYTE, left.type, right.type, array.position))
} else {
mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
}
expressionEval.translateExpression(mult)
}
addToResult(result, tr, tr.resultReg, -1)

View File

@ -93,6 +93,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
is PtPrefix -> translate(expr)
is PtArrayIndexer -> translate(expr)
is PtBinaryExpression -> translate(expr)
is PtRpn -> translate(expr)
is PtBuiltinFunctionCall -> codeGen.translateBuiltinFunc(expr)
is PtFunctionCall -> translate(expr)
is PtContainmentCheck -> translate(expr)
@ -315,6 +316,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, codeGen.irType(cast.type), actualResultReg2, actualResultFpReg2)
}
private fun translate(rpn: PtRpn): ExpressionCodeResult {
TODO("RPN expression (intermediate codegen) $rpn")
}
private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
val vmDt = codeGen.irType(binExpr.left.type)
val signed = binExpr.left.type in SignedDatatypes
@ -615,28 +620,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
internal fun operatorShiftRightInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, signed: Boolean, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
if(codeGen.isOne(operand)) {
val opc = if (signed) Opcode.ASRM else Opcode.LSRM
val ins = if(knownAddress!=null)
IRInstruction(opc, vmDt, value=knownAddress)
else
IRInstruction(opc, vmDt, labelSymbol = symbol)
addInstr(result, ins, null)
} else {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
val ins = if(knownAddress!=null)
IRInstruction(opc, vmDt, reg1 = tr.resultReg, value=knownAddress)
else
IRInstruction(opc, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
addInstr(result, ins, null)
}
return ExpressionCodeResult(result, vmDt, -1, -1)
}
private fun operatorShiftLeft(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
return if(codeGen.isOne(binExpr.right)){
@ -654,26 +637,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
internal fun operatorShiftLeftInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
if(codeGen.isOne(operand)){
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.LSLM, vmDt, value=knownAddress)
else
IRInstruction(Opcode.LSLM, vmDt, labelSymbol = symbol)
, null)
} else {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, value=knownAddress)
else
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
,null)
}
return ExpressionCodeResult(result, vmDt, -1, -1)
}
private fun operatorXor(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
return if(binExpr.right is PtNumber) {
@ -691,18 +654,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
internal fun operatorXorInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.XORM, vmDt, reg1=tr.resultReg, value = knownAddress)
else
IRInstruction(Opcode.XORM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
,null)
return ExpressionCodeResult(result, vmDt, -1, -1)
}
private fun operatorAnd(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
return if(binExpr.right is PtNumber) {
@ -720,18 +671,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
internal fun operatorAndInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, value=knownAddress)
else
IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
,null)
return ExpressionCodeResult(result, vmDt, -1, -1)
}
private fun operatorOr(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
return if(binExpr.right is PtNumber) {
@ -749,18 +688,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
internal fun operatorOrInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, value = knownAddress)
else
IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
, null)
return ExpressionCodeResult(result, vmDt, -1, -1)
}
private fun operatorModulo(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
require(vmDt!=IRDataType.FLOAT) {"floating-point modulo not supported ${binExpr.position}"}
val result = mutableListOf<IRCodeChunkBase>()
@ -834,55 +761,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
internal fun operatorDivideInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, signed: Boolean, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val constFactorRight = operand as? PtNumber
if(vmDt==IRDataType.FLOAT) {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
val factor = constFactorRight.number.toFloat()
result += codeGen.divideByConstFloatInplace(knownAddress, symbol, factor)
} else {
val tr = translateExpression(operand)
addToResult(result, tr, -1, tr.resultFpReg)
val ins = if(signed) {
if(knownAddress!=null)
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = tr.resultFpReg, value = knownAddress)
else
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
}
else {
if(knownAddress!=null)
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = tr.resultFpReg, value = knownAddress)
else
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
}
addInstr(result, ins, null)
}
} else {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
val factor = constFactorRight.number.toInt()
result += codeGen.divideByConstInplace(vmDt, knownAddress, symbol, factor, signed)
} else {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
val ins = if(signed) {
if(knownAddress!=null)
IRInstruction(Opcode.DIVSM, vmDt, reg1 = tr.resultReg, value = knownAddress)
else
IRInstruction(Opcode.DIVSM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
}
else {
if(knownAddress!=null)
IRInstruction(Opcode.DIVM, vmDt, reg1 = tr.resultReg, value = knownAddress)
else
IRInstruction(Opcode.DIVM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
}
addInstr(result, ins, null)
}
}
return ExpressionCodeResult(result, vmDt, -1, -1)
}
private fun operatorMultiply(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val constFactorLeft = binExpr.left as? PtNumber
@ -932,39 +810,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
internal fun operatorMultiplyInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val constFactorRight = operand as? PtNumber
if(vmDt==IRDataType.FLOAT) {
if(constFactorRight!=null) {
val factor = constFactorRight.number.toFloat()
result += codeGen.multiplyByConstFloatInplace(knownAddress, symbol, factor)
} else {
val tr = translateExpression(operand)
addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, value = knownAddress)
else
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
, null)
}
} else {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
val factor = constFactorRight.number.toInt()
result += codeGen.multiplyByConstInplace(vmDt, knownAddress, symbol, factor)
} else {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, value = knownAddress)
else
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
, null)
}
}
return ExpressionCodeResult(result, vmDt, -1, -1)
}
private fun operatorMinus(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
if(vmDt==IRDataType.FLOAT) {
@ -1014,46 +859,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
internal fun operatorMinusInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
if(vmDt==IRDataType.FLOAT) {
if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.DECM, vmDt, value=knownAddress)
else
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
, null)
}
else {
val tr = translateExpression(operand)
addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, value=knownAddress)
else
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol)
, null)
}
} else {
if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.DECM, vmDt, value=knownAddress)
else
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
, null)
}
else {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, value = knownAddress)
else
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
, null)
}
}
return ExpressionCodeResult(result, vmDt, -1, -1)
}
private fun operatorPlus(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
if(vmDt==IRDataType.FLOAT) {
@ -1115,6 +920,153 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
internal fun operatorAndInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, value=knownAddress)
else
IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
,null)
return ExpressionCodeResult(result, vmDt, -1, -1)
}
internal fun operatorOrInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, value = knownAddress)
else
IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
, null)
return ExpressionCodeResult(result, vmDt, -1, -1)
}
internal fun operatorDivideInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, signed: Boolean, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val constFactorRight = operand as? PtNumber
if(vmDt==IRDataType.FLOAT) {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
val factor = constFactorRight.number.toFloat()
result += codeGen.divideByConstFloatInplace(knownAddress, symbol, factor)
} else {
val tr = translateExpression(operand)
addToResult(result, tr, -1, tr.resultFpReg)
val ins = if(signed) {
if(knownAddress!=null)
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = tr.resultFpReg, value = knownAddress)
else
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
}
else {
if(knownAddress!=null)
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = tr.resultFpReg, value = knownAddress)
else
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
}
addInstr(result, ins, null)
}
} else {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
val factor = constFactorRight.number.toInt()
result += codeGen.divideByConstInplace(vmDt, knownAddress, symbol, factor, signed)
} else {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
val ins = if(signed) {
if(knownAddress!=null)
IRInstruction(Opcode.DIVSM, vmDt, reg1 = tr.resultReg, value = knownAddress)
else
IRInstruction(Opcode.DIVSM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
}
else {
if(knownAddress!=null)
IRInstruction(Opcode.DIVM, vmDt, reg1 = tr.resultReg, value = knownAddress)
else
IRInstruction(Opcode.DIVM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
}
addInstr(result, ins, null)
}
}
return ExpressionCodeResult(result, vmDt, -1, -1)
}
internal fun operatorMultiplyInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val constFactorRight = operand as? PtNumber
if(vmDt==IRDataType.FLOAT) {
if(constFactorRight!=null) {
val factor = constFactorRight.number.toFloat()
result += codeGen.multiplyByConstFloatInplace(knownAddress, symbol, factor)
} else {
val tr = translateExpression(operand)
addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, value = knownAddress)
else
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
, null)
}
} else {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
val factor = constFactorRight.number.toInt()
result += codeGen.multiplyByConstInplace(vmDt, knownAddress, symbol, factor)
} else {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, value = knownAddress)
else
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
, null)
}
}
return ExpressionCodeResult(result, vmDt, -1, -1)
}
internal fun operatorMinusInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
if(vmDt==IRDataType.FLOAT) {
if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.DECM, vmDt, value=knownAddress)
else
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
, null)
}
else {
val tr = translateExpression(operand)
addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, value=knownAddress)
else
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol)
, null)
}
} else {
if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.DECM, vmDt, value=knownAddress)
else
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
, null)
}
else {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, value = knownAddress)
else
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
, null)
}
}
return ExpressionCodeResult(result, vmDt, -1, -1)
}
internal fun operatorPlusInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
if(vmDt==IRDataType.FLOAT) {
@ -1155,6 +1107,60 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, vmDt, -1, -1)
}
internal fun operatorShiftRightInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, signed: Boolean, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
if(codeGen.isOne(operand)) {
val opc = if (signed) Opcode.ASRM else Opcode.LSRM
val ins = if(knownAddress!=null)
IRInstruction(opc, vmDt, value=knownAddress)
else
IRInstruction(opc, vmDt, labelSymbol = symbol)
addInstr(result, ins, null)
} else {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
val ins = if(knownAddress!=null)
IRInstruction(opc, vmDt, reg1 = tr.resultReg, value=knownAddress)
else
IRInstruction(opc, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
addInstr(result, ins, null)
}
return ExpressionCodeResult(result, vmDt, -1, -1)
}
internal fun operatorShiftLeftInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
if(codeGen.isOne(operand)){
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.LSLM, vmDt, value=knownAddress)
else
IRInstruction(Opcode.LSLM, vmDt, labelSymbol = symbol)
, null)
} else {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, value=knownAddress)
else
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
,null)
}
return ExpressionCodeResult(result, vmDt, -1, -1)
}
internal fun operatorXorInplace(knownAddress: Int?, symbol: String?, vmDt: IRDataType, operand: PtExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.XORM, vmDt, reg1=tr.resultReg, value = knownAddress)
else
IRInstruction(Opcode.XORM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
,null)
return ExpressionCodeResult(result, vmDt, -1, -1)
}
}

View File

@ -269,6 +269,8 @@ class IRCodeGen(
is PtProgram,
is PtArrayIndexer,
is PtBinaryExpression,
is PtRpn,
is PtRpnOperator,
is PtIdentifier,
is PtWhenChoice,
is PtPrefix,
@ -896,30 +898,70 @@ class IRCodeGen(
private fun translate(ifElse: PtIfElse): IRCodeChunks {
val condition = ifElse.condition
if(condition.operator !in ComparisonOperators)
throw AssemblyError("if condition should only be a binary comparison expression")
val signed = condition.left.type in SignedDatatypes
val irDtLeft = irType(condition.left.type)
val goto = ifElse.ifScope.children.firstOrNull() as? PtJump
return when {
goto!=null && ifElse.elseScope.children.isEmpty() -> translateIfFollowedByJustGoto(ifElse, goto, irDtLeft, signed)
constValue(condition.right) == 0.0 -> translateIfElseZeroComparison(ifElse, irDtLeft, signed)
else -> translateIfElseNonZeroComparison(ifElse, irDtLeft, signed)
when (condition) {
is PtBinaryExpression -> {
if(condition.operator !in ComparisonOperators)
throw AssemblyError("if condition should only be a binary comparison expression")
val signed = condition.left.type in SignedDatatypes
val irDtLeft = irType(condition.left.type)
return when {
goto!=null && ifElse.elseScope.children.isEmpty() -> translateIfFollowedByJustGoto(ifElse, goto, irDtLeft, signed)
constValue(condition.right) == 0.0 -> translateIfElseZeroComparison(ifElse, irDtLeft, signed)
else -> translateIfElseNonZeroComparison(ifElse, irDtLeft, signed)
}
}
is PtRpn -> {
val (left, oper, right) = condition.finalOperation()
if(oper.operator !in ComparisonOperators)
throw AssemblyError("if condition should only be a binary comparison expression")
val signed = oper.leftType in SignedDatatypes
val irDtLeft = irType(oper.leftType)
return when {
goto!=null && ifElse.elseScope.children.isEmpty() -> translateIfFollowedByJustGotoRPN(ifElse, goto, irDtLeft, signed)
constValue(right as PtExpression) == 0.0 -> translateIfElseZeroComparisonRPN(ifElse, irDtLeft, signed)
else -> translateIfElseNonZeroComparisonRPN(ifElse, irDtLeft, signed)
}
}
else -> {
throw AssemblyError("weird condition node: $condition")
}
}
}
private fun translateIfFollowedByJustGotoRPN(ifElse: PtIfElse, goto: PtJump, irDtLeft: IRDataType, signed: Boolean): MutableList<IRCodeChunkBase> {
TODO("RPN translateIfFollowedByJustGotoRPN")
}
private fun translateIfElseZeroComparisonRPN(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks {
TODO("RPN translateIfElseZeroComparisonRPN")
}
private fun translateIfElseNonZeroComparisonRPN(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks {
TODO("RPN translateIfElseNonZeroComparisonRPN")
}
private fun translateIfFollowedByJustGoto(ifElse: PtIfElse, goto: PtJump, irDtLeft: IRDataType, signed: Boolean): MutableList<IRCodeChunkBase> {
require(!program.binaryExpressionsAreRPN)
val condition = ifElse.condition as PtBinaryExpression
val result = mutableListOf<IRCodeChunkBase>()
if(irDtLeft==IRDataType.FLOAT) {
val leftTr = expressionEval.translateExpression(ifElse.condition.left)
if (irDtLeft == IRDataType.FLOAT) {
val leftTr = expressionEval.translateExpression(condition.left)
addToResult(result, leftTr, -1, leftTr.resultFpReg)
val rightTr = expressionEval.translateExpression(ifElse.condition.right)
val rightTr = expressionEval.translateExpression(condition.right)
addToResult(result, rightTr, -1, rightTr.resultFpReg)
result += IRCodeChunk(null,null).also {
result += IRCodeChunk(null, null).also {
val compResultReg = registers.nextFree()
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=compResultReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg)
val gotoOpcode = when (ifElse.condition.operator) {
it += IRInstruction(
Opcode.FCOMP,
IRDataType.FLOAT,
reg1 = compResultReg,
fpReg1 = leftTr.resultFpReg,
fpReg2 = rightTr.resultFpReg
)
val gotoOpcode = when (condition.operator) {
"==" -> Opcode.BZ
"!=" -> Opcode.BNZ
"<" -> Opcode.BLEZS
@ -929,16 +971,31 @@ class IRCodeGen(
else -> throw AssemblyError("weird operator")
}
it += if (goto.address != null)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, value = goto.address?.toInt())
IRInstruction(
gotoOpcode,
IRDataType.BYTE,
reg1 = compResultReg,
value = goto.address?.toInt()
)
else if (goto.generatedLabel != null)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = goto.generatedLabel)
IRInstruction(
gotoOpcode,
IRDataType.BYTE,
reg1 = compResultReg,
labelSymbol = goto.generatedLabel
)
else
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = goto.identifier!!.name)
IRInstruction(
gotoOpcode,
IRDataType.BYTE,
reg1 = compResultReg,
labelSymbol = goto.identifier!!.name
)
}
return result
} else {
val rightConst = ifElse.condition.right.asConstInteger()
return if(rightConst==0)
val rightConst = condition.right.asConstInteger()
return if (rightConst == 0)
ifZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
else {
ifNonZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
@ -953,9 +1010,11 @@ class IRCodeGen(
irDtLeft: IRDataType,
goto: PtJump
): MutableList<IRCodeChunkBase> {
val leftTr = expressionEval.translateExpression(ifElse.condition.left)
require(!program.binaryExpressionsAreRPN)
val condition = ifElse.condition as PtBinaryExpression
val leftTr = expressionEval.translateExpression(condition.left)
addToResult(result, leftTr, leftTr.resultReg, -1)
val opcode = when (ifElse.condition.operator) {
val opcode = when (condition.operator) {
"==" -> Opcode.BZ
"!=" -> Opcode.BNZ
"<" -> if (signed) Opcode.BLZS else throw AssemblyError("unsigned < 0 shouldn't occur in codegen")
@ -980,14 +1039,16 @@ class IRCodeGen(
irDtLeft: IRDataType,
goto: PtJump
): MutableList<IRCodeChunkBase> {
val leftTr = expressionEval.translateExpression(ifElse.condition.left)
require(!program.binaryExpressionsAreRPN)
val condition = ifElse.condition as PtBinaryExpression
val leftTr = expressionEval.translateExpression(condition.left)
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = expressionEval.translateExpression(ifElse.condition.right)
val rightTr = expressionEval.translateExpression(condition.right)
addToResult(result, rightTr, rightTr.resultReg, -1)
val opcode: Opcode
val firstReg: Int
val secondReg: Int
when (ifElse.condition.operator) {
when (condition.operator) {
"==" -> {
opcode = Opcode.BEQ
firstReg = leftTr.resultReg
@ -1032,21 +1093,23 @@ class IRCodeGen(
}
private fun translateIfElseZeroComparison(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks {
require(!program.binaryExpressionsAreRPN)
val result = mutableListOf<IRCodeChunkBase>()
val elseBranch: Opcode
val compResultReg: Int
val branchDt: IRDataType
val condition = ifElse.condition as PtBinaryExpression
if(irDtLeft==IRDataType.FLOAT) {
branchDt = IRDataType.BYTE
compResultReg = registers.nextFree()
val leftTr = expressionEval.translateExpression(ifElse.condition.left)
val leftTr = expressionEval.translateExpression(condition.left)
addToResult(result, leftTr, -1, leftTr.resultFpReg)
result += IRCodeChunk(null, null).also {
val rightFpReg = registers.nextFreeFloat()
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = rightFpReg, fpValue = 0f)
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=compResultReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightFpReg)
}
elseBranch = when (ifElse.condition.operator) {
elseBranch = when (condition.operator) {
"==" -> Opcode.BNZ
"!=" -> Opcode.BZ
"<" -> Opcode.BGEZS
@ -1058,10 +1121,10 @@ class IRCodeGen(
} else {
// integer comparisons
branchDt = irDtLeft
val tr = expressionEval.translateExpression(ifElse.condition.left)
val tr = expressionEval.translateExpression(condition.left)
compResultReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1)
elseBranch = when (ifElse.condition.operator) {
elseBranch = when (condition.operator) {
"==" -> Opcode.BNZ
"!=" -> Opcode.BZ
"<" -> if (signed) Opcode.BGEZS else throw AssemblyError("unsigned < 0 shouldn't occur in codegen")
@ -1092,20 +1155,21 @@ class IRCodeGen(
}
private fun translateIfElseNonZeroComparison(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks {
require(!program.binaryExpressionsAreRPN)
val result = mutableListOf<IRCodeChunkBase>()
val elseBranchOpcode: Opcode
val elseBranchFirstReg: Int
val elseBranchSecondReg: Int
val branchDt: IRDataType
val condition = ifElse.condition as PtBinaryExpression
if(irDtLeft==IRDataType.FLOAT) {
val leftTr = expressionEval.translateExpression(ifElse.condition.left)
val leftTr = expressionEval.translateExpression(condition.left)
addToResult(result, leftTr, -1, leftTr.resultFpReg)
val rightTr = expressionEval.translateExpression(ifElse.condition.right)
val rightTr = expressionEval.translateExpression(condition.right)
addToResult(result, rightTr, -1, rightTr.resultFpReg)
val compResultReg = registers.nextFree()
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=compResultReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
val elseBranch = when (ifElse.condition.operator) {
val elseBranch = when (condition.operator) {
"==" -> Opcode.BNZ
"!=" -> Opcode.BZ
"<" -> Opcode.BGEZS
@ -1133,11 +1197,11 @@ class IRCodeGen(
} else {
// integer comparisons
branchDt = irDtLeft
val leftTr = expressionEval.translateExpression(ifElse.condition.left)
val leftTr = expressionEval.translateExpression(condition.left)
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = expressionEval.translateExpression(ifElse.condition.right)
val rightTr = expressionEval.translateExpression(condition.right)
addToResult(result, rightTr, rightTr.resultReg, -1)
when (ifElse.condition.operator) {
when (condition.operator) {
"==" -> {
elseBranchOpcode = Opcode.BNE
elseBranchFirstReg = leftTr.resultReg

View File

@ -2,10 +2,7 @@ package prog8.codegen.vm
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram
import prog8.code.core.ICodeGeneratorBackend
import prog8.code.core.IErrorReporter
import prog8.code.core.*
import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileWriter
import prog8.intermediate.IRProgram
@ -17,6 +14,13 @@ class VmCodeGen: ICodeGeneratorBackend {
options: CompilationOptions,
errors: IErrorReporter
): IAssemblyProgram? {
if(options.useRPN) {
program.transformBinExprToRPN()
errors.warn("EXPERIMENTAL RPN EXPRESSION NODES ARE USED. CODE SIZE+SPEED WILL SUFFER.", Position.DUMMY)
}
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
val irProgram = irCodeGen.generate()
return VmAssemblyProgram(irProgram.name, irProgram)

View File

@ -21,6 +21,8 @@ class BinExprSplitter(private val program: Program, private val options: Compila
if(options.compTarget.name == VMTarget.NAME)
return noModifications // don't split expressions when targeting the vm codegen, it handles nested expressions well
if(options.useRPN) // TODO RPN does this make a difference?
return noModifications
if(assignment.value.inferType(program) istype DataType.FLOAT && !options.optimizeFloatExpressions)
return noModifications

View File

@ -997,6 +997,20 @@ _arg_s1 .word 0
_arg_s2 .word 0
.pend
strcmp_stack .proc
; -- compare strings, both on stack.
; Returns -1,0,1 in A, depeding on the ordering. Clobbers Y.
inx
lda P8ESTACK_LO,x
ldy P8ESTACK_HI,x
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
inx
lda P8ESTACK_LO,x
ldy P8ESTACK_HI,x
jmp strcmp_mem
.pend
strcmp_mem .proc
; -- compares strings in s1 (AY) and s2 (P8ZP_SCRATCH_W2).

View File

@ -52,6 +52,7 @@ private fun compileMain(args: Array<String>): Boolean {
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
val varsHigh by cli.option(ArgType.Boolean, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program")
val useRPN by cli.option(ArgType.Boolean, fullName = "rpn", description = "use RPN for expression code-generation (experimental)")
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
try {
@ -126,6 +127,7 @@ private fun compileMain(args: Array<String>): Boolean {
asmListfile == true,
experimentalCodegen == true,
varsHigh == true,
useRPN == true,
compilationTarget,
evalStackAddr,
processedSymbols,
@ -190,6 +192,7 @@ private fun compileMain(args: Array<String>): Boolean {
asmListfile == true,
experimentalCodegen == true,
varsHigh == true,
useRPN == true,
compilationTarget,
evalStackAddr,
processedSymbols,

View File

@ -36,6 +36,7 @@ class CompilerArguments(val filepath: Path,
val asmListfile: Boolean,
val experimentalCodegen: Boolean,
val varsHigh: Boolean,
val useRPN: Boolean,
val compilationTarget: String,
val evalStackBaseAddress: UInt?,
val symbolDefs: Map<String, String>,
@ -76,6 +77,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
asmListfile = args.asmListfile
experimentalCodegen = args.experimentalCodegen
varsHigh = args.varsHigh
useRPN = args.useRPN
evalStackBaseAddress = args.evalStackBaseAddress
outputDir = args.outputDir.normalize()
symbolDefs = args.symbolDefs
@ -118,7 +120,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
// println("*********** COMPILER AST RIGHT BEFORE ASM GENERATION *************")
// printProgram(program)
// println("*********** AST RIGHT BEFORE ASM GENERATION *************")
// printAst(intermediateAst, ::println)
// printAst(intermediateAst, true, ::println)
if(!createAssemblyAndAssemble(intermediateAst, args.errors, compilationOptions)) {
System.err.println("Error in codegeneration or assembler")

View File

@ -457,6 +457,8 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
}
private fun transform(srcExpr: BinaryExpression): PtBinaryExpression {
// NOTE: the Ast maker here will NOT create RPN-expression nodes for the code generator.
// If the code generator prefers RPN expressions, it has to transform the PtBinaryExpression itself into PtRpn.
val type = srcExpr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val actualType = if(type==DataType.BOOL) DataType.UBYTE else type
val expr = PtBinaryExpression(srcExpr.operator, actualType, srcExpr.position)

View File

@ -33,6 +33,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
asmListfile = false,
experimentalCodegen = false,
varsHigh = false,
useRPN = false,
compilationTarget = target.name,
evalStackBaseAddress = null,
symbolDefs = emptyMap(),

View File

@ -50,6 +50,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
asmListfile = false,
experimentalCodegen = false,
varsHigh = false,
useRPN = false,
compilationTarget = Cx16Target.NAME,
evalStackBaseAddress = null,
symbolDefs = emptyMap(),

View File

@ -66,7 +66,7 @@ class TestIntermediateAst: FunSpec({
val fcall = (entry.children[4] as PtAssignment).value as PtFunctionCall
fcall.void shouldBe false
fcall.type shouldBe DataType.UBYTE
printAst(ast, ::println)
printAst(ast, false, ::println)
}
})

View File

@ -0,0 +1,45 @@
package prog8tests.codegeneration
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import prog8.code.ast.PtAssignment
import prog8.code.ast.PtBuiltinFunctionCall
import prog8.code.ast.PtRpn
import prog8.code.core.DataType
import prog8.code.target.C64Target
import prog8tests.helpers.compileText
class TestRPNCodeGen: FunSpec({
test("rpn 6502") {
val text="""
main {
sub start() {
uword pointer = 4000
uword a = 11
uword b = 22
uword c = 33
cx16.r0 = peekw(pointer+a+b*c+42)
pokew(pointer+a+b*c+42, 4242)
}
}"""
val result = compileText(C64Target(), false, text, writeAssembly = true, useRPN = true)!!
val ast = result.codegenAst!!
val statements = ast.entrypoint()!!.children
statements.size shouldBe 11
val peekw = (statements[8] as PtAssignment).value as PtBuiltinFunctionCall
val pokew = (statements[9] as PtBuiltinFunctionCall)
val rpn1 = peekw.args.first() as PtRpn
val rpn2 = pokew.args.first() as PtRpn
rpn1.children.size shouldBe 7
val depth1 = rpn1.maxDepth()
depth1.getValue(DataType.UBYTE) shouldBe 0
depth1.getValue(DataType.UWORD) shouldBe 3
depth1.getValue(DataType.FLOAT) shouldBe 0
rpn2.children.size shouldBe 7
val depth2 = rpn2.maxDepth()
depth2.getValue(DataType.UBYTE) shouldBe 0
depth2.getValue(DataType.UWORD) shouldBe 3
depth2.getValue(DataType.FLOAT) shouldBe 0
}
})

View File

@ -17,7 +17,8 @@ internal fun compileFile(
outputDir: Path = prog8tests.helpers.outputDir,
errors: IErrorReporter? = null,
writeAssembly: Boolean = true,
optFloatExpr: Boolean = true
optFloatExpr: Boolean = true,
useRPN: Boolean = false
) : CompilationResult? {
val filepath = fileDir.resolve(fileName)
assumeReadableFile(filepath)
@ -31,6 +32,7 @@ internal fun compileFile(
asmListfile = false,
experimentalCodegen = false,
varsHigh = false,
useRPN = useRPN,
platform.name,
evalStackBaseAddress = null,
symbolDefs = emptyMap(),
@ -51,11 +53,12 @@ internal fun compileText(
sourceText: String,
errors: IErrorReporter? = null,
writeAssembly: Boolean = true,
optFloatExpr: Boolean = true
optFloatExpr: Boolean = true,
useRPN: Boolean = false
) : CompilationResult? {
val filePath = outputDir.resolve("on_the_fly_test_" + sourceText.hashCode().toUInt().toString(16) + ".p8")
// we don't assumeNotExists(filePath) - should be ok to just overwrite it
filePath.toFile().writeText(sourceText)
return compileFile(platform, optimize, filePath.parent, filePath.name,
errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr)
errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr, useRPN=useRPN)
}

View File

@ -1,5 +1,19 @@
TODO
====
RPN: examples/line-circle-txt crashes
RPN: examples/turtlegfx crashes
RPN: examples/maze crashes
RPN: examples/bsieve,charset compilation crash (bit shift expression)
RPN: cube3d-float is massive and slow
RPN: mandelbrot is big, but seems faster
RPN: swirl is MUCH slower, wizzine is slower
RPN: Fix the TODO RPN routines to be optimized assembly in RpnExpressionAsmGen.kt
RPN: check BinExprSplitter disablement any effect for RPN?
RPN: Implement RPN codegen for IR.
- Move asmExtra vars into BSS as well, now are .byte 0 allocated
For next minor release
^^^^^^^^^^^^^^^^^^^^^^

View File

@ -43,7 +43,7 @@ main {
DX[lp] = true
else
BX[lp]=BX[lp]-1
} else if DX[lp] {
} else {
if (BX[lp] == txt.DEFAULT_WIDTH-1) {
BX[lp] = txt.DEFAULT_WIDTH-2
DX[lp] = false
@ -56,7 +56,7 @@ main {
DY[lp] = true
else
BY[lp]=BY[lp]-1
} else if DY[lp] == 1 {
} else {
if (BY[lp] == txt.DEFAULT_HEIGHT-1) {
BY[lp] = txt.DEFAULT_HEIGHT-2
DY[lp] = false

View File

@ -1,11 +1,21 @@
%import math
%import textio
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
main {
uword[10] answers_animals
sub start() {
ubyte current_question = 1
uword previous_animals = 33
current_question = msb(answers_animals[current_question]) ; TODO takes 1 more vm registers than 8.10
answers_animals[current_question] = mkword(msb(previous_animals), 0) ; TODO takes 1 more vm registers than 8.10
; TODO expected result: 7 registers in 8.10, now takes 9 instead
bool x
ubyte y
repeat 20 {
x = math.rnd() & 1
y = ((math.rnd()&1)!=0)
txt.print_ub(x)
txt.spc()
txt.print_ub(y)
txt.nl()
}
}
}

View File

@ -42,7 +42,8 @@ class RequestParser : Take {
quietAssembler = false,
asmListfile = false,
experimentalCodegen = false,
varsHigh = false
varsHigh = false,
useRPN = false
)
val compilationResult = compileProgram(args)
return RsJson(Jsonding())