mirror of
https://github.com/irmen/prog8.git
synced 2024-06-26 07:29:32 +00:00
Merge branch 'booleans'
# Conflicts: # compiler/res/prog8lib/cx16/monogfx.p8 # compiler/res/prog8lib/virtual/monogfx.p8 # compiler/src/prog8/compiler/astprocessing/BoolRemover.kt # compiler/test/TestTypecasts.kt # docs/source/todo.rst # examples/cx16/highresbitmap.p8 # examples/test.p8 # httpCompilerService/src/prog8/http/TestHttp.kt
This commit is contained in:
commit
80f39e8097
|
@ -195,7 +195,7 @@ class StStaticVariable(name: String,
|
|||
require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length)
|
||||
}
|
||||
if(onetimeInitializationNumericValue!=null) {
|
||||
require(dt in NumericDatatypes)
|
||||
require(dt in NumericDatatypes || dt==DataType.BOOL)
|
||||
}
|
||||
if(onetimeInitializationArrayValue!=null) {
|
||||
require(dt in ArrayDatatypes)
|
||||
|
@ -221,6 +221,7 @@ class StMemVar(name: String,
|
|||
StNode(name, StNodeType.MEMVAR, astNode) {
|
||||
|
||||
init{
|
||||
require(dt!=DataType.BOOL && dt!=DataType.ARRAY_BOOL)
|
||||
if(dt in ArrayDatatypes || dt == DataType.STR)
|
||||
require(length!=null) { "memory mapped array or string must have known length" }
|
||||
}
|
||||
|
@ -250,7 +251,7 @@ class StRomSub(name: String,
|
|||
|
||||
class StSubroutineParameter(val name: String, val type: DataType)
|
||||
class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType)
|
||||
class StArrayElement(val number: Double?, val addressOfSymbol: String?)
|
||||
class StArrayElement(val number: Double?, val addressOfSymbol: String?, val boolean: Boolean?)
|
||||
|
||||
typealias StString = Pair<String, Encoding>
|
||||
typealias StArray = List<StArrayElement>
|
||||
|
|
|
@ -128,10 +128,11 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
|||
is PtAddressOf -> {
|
||||
if(it.isFromArrayElement)
|
||||
TODO("address-of array element $it in initial array value")
|
||||
StArrayElement(null, it.identifier.name)
|
||||
StArrayElement(null, it.identifier.name, null)
|
||||
}
|
||||
is PtIdentifier -> StArrayElement(null, it.name)
|
||||
is PtNumber -> StArrayElement(it.number, null)
|
||||
is PtIdentifier -> StArrayElement(null, it.name, null)
|
||||
is PtNumber -> StArrayElement(it.number, null, null)
|
||||
is PtBool -> StArrayElement(null, null, it.value)
|
||||
else -> throw AssemblyError("invalid array element $it")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@ import kotlin.math.truncate
|
|||
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
|
||||
|
||||
init {
|
||||
if(type==DataType.BOOL)
|
||||
throw IllegalArgumentException("bool should have become ubyte @$position")
|
||||
if(type==DataType.UNDEFINED) {
|
||||
@Suppress("LeakingThis")
|
||||
when(this) {
|
||||
|
@ -49,6 +47,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
|||
is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register
|
||||
is PtMemoryByte -> other is PtMemoryByte && other.address isSameAs address
|
||||
is PtNumber -> other is PtNumber && other.type==type && other.number==number
|
||||
is PtBool -> other is PtBool && other.value==value
|
||||
is PtPrefix -> other is PtPrefix && other.type==type && other.operator==operator && other.value isSameAs value
|
||||
is PtRange -> other is PtRange && other.type==type && other.from==from && other.to==to && other.step==step
|
||||
is PtTypeCast -> other is PtTypeCast && other.type==type && other.value isSameAs value
|
||||
|
@ -71,7 +70,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
|||
}
|
||||
}
|
||||
|
||||
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt()
|
||||
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt() ?: (this as? PtBool)?.asInt()
|
||||
|
||||
fun isSimple(): Boolean {
|
||||
return when(this) {
|
||||
|
@ -90,6 +89,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
|||
is PtIdentifier -> true
|
||||
is PtMachineRegister -> true
|
||||
is PtMemoryByte -> address is PtNumber || address is PtIdentifier
|
||||
is PtBool -> true
|
||||
is PtNumber -> true
|
||||
is PtPrefix -> value.isSimple()
|
||||
is PtRange -> true
|
||||
|
@ -116,6 +116,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
|||
is PtMachineRegister -> return withClonedChildrenFrom(this, PtMachineRegister(register, type, position))
|
||||
is PtMemoryByte -> return withClonedChildrenFrom(this, PtMemoryByte(position))
|
||||
is PtNumber -> return withClonedChildrenFrom(this, PtNumber(type, number, position))
|
||||
is PtBool -> return withClonedChildrenFrom(this, PtBool(value, position))
|
||||
is PtPrefix -> return withClonedChildrenFrom(this, PtPrefix(operator, type, position))
|
||||
is PtRange -> return withClonedChildrenFrom(this, PtRange(type, position))
|
||||
is PtString -> return withClonedChildrenFrom(this, PtString(value, encoding, position))
|
||||
|
@ -149,7 +150,7 @@ class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(el
|
|||
get() = variable.type in SplitWordArrayTypes
|
||||
|
||||
init {
|
||||
require(elementType in NumericDatatypes)
|
||||
require(elementType in NumericDatatypesWithBoolean)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,15 +184,21 @@ class PtBuiltinFunctionCall(val name: String,
|
|||
|
||||
|
||||
class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
||||
// note: "and", "or", "xor" do not occur anymore as operators. They've been replaced int the ast by their bitwise versions &, |, ^.
|
||||
val left: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val right: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
|
||||
init {
|
||||
if(operator in ComparisonOperators + LogicalOperators)
|
||||
require(type==DataType.BOOL)
|
||||
else
|
||||
require(type!=DataType.BOOL) { "no bool allowed for this operator $operator"}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtContainmentCheck(position: Position): PtExpression(DataType.UBYTE, position) {
|
||||
class PtContainmentCheck(position: Position): PtExpression(DataType.BOOL, position) {
|
||||
val element: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val iterable: PtIdentifier
|
||||
|
@ -228,6 +235,27 @@ class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position)
|
|||
}
|
||||
|
||||
|
||||
class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.BOOL, position) {
|
||||
|
||||
companion object {
|
||||
fun fromNumber(number: Number, position: Position): PtBool =
|
||||
PtBool(if(number==0.0) false else true, position)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(type, value)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if(other==null || other !is PtBool)
|
||||
return false
|
||||
return value==other.value
|
||||
}
|
||||
|
||||
override fun toString() = "PtBool:$value"
|
||||
|
||||
fun asInt(): Int = if(value) 1 else 0
|
||||
}
|
||||
|
||||
|
||||
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
|
||||
|
||||
companion object {
|
||||
|
@ -237,7 +265,7 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
|
|||
|
||||
init {
|
||||
if(type==DataType.BOOL)
|
||||
throw IllegalArgumentException("bool should have become ubyte @$position")
|
||||
throw IllegalArgumentException("use PtBool instead")
|
||||
if(type!=DataType.FLOAT) {
|
||||
val trunc = truncate(number)
|
||||
if (trunc != number)
|
||||
|
@ -267,8 +295,7 @@ class PtPrefix(val operator: String, type: DataType, position: Position): PtExpr
|
|||
get() = children.single() as PtExpression
|
||||
|
||||
init {
|
||||
// note: the "not" operator may no longer occur in the ast; not x should have been replaced with x==0
|
||||
require(operator in setOf("+", "-", "~")) { "invalid prefix operator: $operator" }
|
||||
require(operator in setOf("+", "-", "~", "not")) { "invalid prefix operator: $operator" }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -330,5 +357,5 @@ class PtTypeCast(type: DataType, position: Position) : PtExpression(type, positi
|
|||
class PtMachineRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position)
|
||||
|
||||
|
||||
fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else null
|
||||
fun constIntValue(expr: PtExpression): Int? = if(expr is PtNumber) expr.number.toInt() else null
|
||||
fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else if(expr is PtBool) expr.asInt().toDouble() else null
|
||||
fun constIntValue(expr: PtExpression): Int? = if(expr is PtNumber) expr.number.toInt() else if(expr is PtBool) expr.asInt() else null
|
||||
|
|
|
@ -40,6 +40,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
|||
val numstr = if(node.type == DataType.FLOAT) node.number.toString() else node.number.toHex()
|
||||
"$numstr ${type(node.type)}"
|
||||
}
|
||||
is PtBool -> node.value.toString()
|
||||
is PtPrefix -> node.operator
|
||||
is PtRange -> "<range>"
|
||||
is PtString -> "\"${node.value.escape()}\""
|
||||
|
@ -89,7 +90,11 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
|||
"\nblock '${node.name}' $addr $align"
|
||||
}
|
||||
is PtConstant -> {
|
||||
val value = if(node.type in IntegerDatatypes) node.value.toInt().toString() else node.value.toString()
|
||||
val value = when(node.type) {
|
||||
DataType.BOOL -> if(node.value==0.0) "false" else "true"
|
||||
in IntegerDatatypes -> node.value.toInt().toString()
|
||||
else -> node.value.toString()
|
||||
}
|
||||
"const ${node.type.name.lowercase()} ${node.name} = $value"
|
||||
}
|
||||
is PtLabel -> "${node.name}:"
|
||||
|
|
|
@ -26,10 +26,10 @@ class PtSub(
|
|||
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer {
|
||||
init {
|
||||
// params and return value should not be str
|
||||
if(parameters.any{ it.type !in NumericDatatypes })
|
||||
throw AssemblyError("non-numeric parameter")
|
||||
if(returntype!=null && returntype !in NumericDatatypes)
|
||||
throw AssemblyError("non-numeric returntype $returntype")
|
||||
if(parameters.any{ it.type !in NumericDatatypes && it.type!=DataType.BOOL })
|
||||
throw AssemblyError("non-numeric/non-bool parameter")
|
||||
if(returntype!=null && returntype !in NumericDatatypes && returntype!=DataType.BOOL)
|
||||
throw AssemblyError("non-numeric/non-bool returntype $returntype")
|
||||
parameters.forEach { it.parent=this }
|
||||
}
|
||||
}
|
||||
|
@ -98,6 +98,8 @@ class PtIfElse(position: Position) : PtNode(position) {
|
|||
get() = children[1] as PtNodeGroup
|
||||
val elseScope: PtNodeGroup
|
||||
get() = children[2] as PtNodeGroup
|
||||
|
||||
fun hasElse(): Boolean = children.size==3 && elseScope.children.isNotEmpty()
|
||||
}
|
||||
|
||||
|
||||
|
@ -146,7 +148,11 @@ class PtVariable(name: String, override val type: DataType, val zeropage: Zeropa
|
|||
class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable
|
||||
|
||||
|
||||
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable
|
||||
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
|
||||
init {
|
||||
require(type!=DataType.BOOL && type!=DataType.ARRAY_BOOL)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtWhen(position: Position) : PtNode(position) {
|
||||
|
|
|
@ -79,28 +79,28 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
|||
"sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
// cmp returns a status in the carry flag, but not a proper return value
|
||||
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
|
||||
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null),
|
||||
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
|
||||
"prog8_lib_arraycopy" to FSignature(false, listOf(FParam("source", ArrayDatatypes), FParam("target", ArrayDatatypes)), null),
|
||||
"prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE),
|
||||
"prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD),
|
||||
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null),
|
||||
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null),
|
||||
"abs__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE),
|
||||
"abs__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD))), DataType.WORD),
|
||||
"abs__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
|
||||
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
|
||||
// normal functions follow:
|
||||
"sizeof" to FSignature(true, listOf(FParam("object", DataType.entries.toTypedArray())), DataType.UBYTE),
|
||||
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
|
||||
"sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null),
|
||||
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE),
|
||||
"sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null),
|
||||
"sqrt__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
||||
"sqrt__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||
"sqrt__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
|
||||
"divmod" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divident", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("division", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"divmod__ubyte" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE)), FParam("divident", arrayOf(DataType.UBYTE)), FParam("division", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null),
|
||||
"divmod__uword" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UWORD)), FParam("divident", arrayOf(DataType.UWORD)), FParam("division", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null),
|
||||
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.BOOL),
|
||||
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.BOOL),
|
||||
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
||||
"msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
||||
"mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),
|
||||
|
|
|
@ -26,6 +26,7 @@ class CompilationOptions(val output: OutputType,
|
|||
var slabsHighBank: Int? = null,
|
||||
var slabsGolden: Boolean = false,
|
||||
var splitWordArrays: Boolean = false,
|
||||
var strictBool: Boolean = true,
|
||||
var breakpointCpuInstruction: String? = null,
|
||||
var outputDir: Path = Path(""),
|
||||
var symbolDefs: Map<String, String> = emptyMap()
|
||||
|
|
|
@ -24,8 +24,8 @@ enum class DataType {
|
|||
*/
|
||||
infix fun isAssignableTo(targetType: DataType) =
|
||||
when(this) {
|
||||
BOOL -> targetType.oneOf(BOOL, BYTE, UBYTE, WORD, UWORD, LONG, FLOAT)
|
||||
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, LONG, FLOAT, BOOL)
|
||||
BOOL -> targetType == BOOL
|
||||
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, LONG, FLOAT)
|
||||
BYTE -> targetType.oneOf(BYTE, WORD, LONG, FLOAT)
|
||||
UWORD -> targetType.oneOf(UWORD, LONG, FLOAT)
|
||||
WORD -> targetType.oneOf(WORD, LONG, FLOAT)
|
||||
|
@ -41,9 +41,9 @@ enum class DataType {
|
|||
infix fun largerThan(other: DataType) =
|
||||
when {
|
||||
this == other -> false
|
||||
this in ByteDatatypes -> false
|
||||
this in WordDatatypes -> other in ByteDatatypes
|
||||
this == LONG -> other in ByteDatatypes+WordDatatypes
|
||||
this in ByteDatatypesWithBoolean -> false
|
||||
this in WordDatatypes -> other in ByteDatatypesWithBoolean
|
||||
this == LONG -> other in ByteDatatypesWithBoolean+WordDatatypes
|
||||
this == STR && other == UWORD || this == UWORD && other == STR -> false
|
||||
else -> true
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ enum class DataType {
|
|||
infix fun equalsSize(other: DataType) =
|
||||
when {
|
||||
this == other -> true
|
||||
this in ByteDatatypes -> other in ByteDatatypes
|
||||
this in ByteDatatypesWithBoolean -> other in ByteDatatypesWithBoolean
|
||||
this in WordDatatypes -> other in WordDatatypes
|
||||
this== STR && other== UWORD || this== UWORD && other== STR -> true
|
||||
else -> false
|
||||
|
@ -124,12 +124,13 @@ enum class BranchCondition {
|
|||
}
|
||||
|
||||
|
||||
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.BOOL)
|
||||
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE)
|
||||
val ByteDatatypesWithBoolean = ByteDatatypes + DataType.BOOL
|
||||
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
|
||||
val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG)
|
||||
val IntegerDatatypes = IntegerDatatypesNoBool + DataType.BOOL
|
||||
val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG, DataType.FLOAT)
|
||||
val NumericDatatypes = NumericDatatypesNoBool + DataType.BOOL
|
||||
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG)
|
||||
val IntegerDatatypesWithBoolean = IntegerDatatypes + DataType.BOOL
|
||||
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG, DataType.FLOAT)
|
||||
val NumericDatatypesWithBoolean = NumericDatatypes + DataType.BOOL
|
||||
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.LONG, DataType.FLOAT)
|
||||
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_F, DataType.ARRAY_BOOL)
|
||||
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
|
||||
|
@ -141,7 +142,7 @@ val IterableDatatypes = arrayOf(
|
|||
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT,
|
||||
DataType.ARRAY_F, DataType.ARRAY_BOOL
|
||||
)
|
||||
val PassByValueDatatypes = NumericDatatypes
|
||||
val PassByValueDatatypes = NumericDatatypesWithBoolean
|
||||
val PassByReferenceDatatypes = IterableDatatypes
|
||||
val ArrayToElementTypes = mapOf(
|
||||
DataType.STR to DataType.UBYTE,
|
||||
|
|
|
@ -11,4 +11,6 @@ interface IErrorReporter {
|
|||
if(numErrors>0)
|
||||
throw ErrorsReportedException("There are $numErrors errors, $numWarnings warnings, and $numInfos infos.")
|
||||
}
|
||||
|
||||
fun noErrorForLine(position: Position): Boolean
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||
|
||||
val size: Int =
|
||||
when (datatype) {
|
||||
in IntegerDatatypes -> options.compTarget.memorySize(datatype)
|
||||
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype)
|
||||
DataType.STR, in ArrayDatatypes -> {
|
||||
val memsize = options.compTarget.memorySize(datatype, numElements!!)
|
||||
if(position!=null)
|
||||
|
@ -119,7 +119,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||
free.removeAll(address until address+size.toUInt())
|
||||
if(name.isNotEmpty()) {
|
||||
allocatedVariables[name] = when(datatype) {
|
||||
in NumericDatatypes -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
|
||||
in NumericDatatypes, DataType.BOOL -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
|
||||
DataType.STR -> VarAllocation(address, datatype, size)
|
||||
in ArrayDatatypes -> VarAllocation(address, datatype, size)
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
|
@ -151,7 +151,7 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
|
|||
|
||||
val size: Int =
|
||||
when (datatype) {
|
||||
in IntegerDatatypes -> options.compTarget.memorySize(datatype)
|
||||
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype)
|
||||
DataType.STR, in ArrayDatatypes -> {
|
||||
options.compTarget.memorySize(datatype, numElements!!)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package prog8.code.core
|
|||
|
||||
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "and", "or", "xor")
|
||||
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||
val LogicalOperators = setOf("and", "or", "xor", "not")
|
||||
val LogicalOperators = setOf("and", "or", "xor", "not", "in")
|
||||
val BitwiseOperators = setOf("&", "|", "^", "~")
|
||||
val PrefixOperators = setOf("+", "-", "~", "not")
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
|||
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
return when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in ByteDatatypesWithBoolean -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||
else -> throw IllegalArgumentException("invalid datatype")
|
||||
|
|
|
@ -14,7 +14,7 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
|||
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
return when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in ByteDatatypesWithBoolean -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||
else -> throw IllegalArgumentException("invalid datatype")
|
||||
|
|
|
@ -6,7 +6,7 @@ import prog8.code.core.*
|
|||
internal object CbmMemorySizer: IMemSizer {
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
return when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in ByteDatatypesWithBoolean -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE
|
||||
else -> throw IllegalArgumentException("invalid datatype")
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -58,7 +58,7 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
|
|||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeSamePointerIndexing(linesByFourteen)
|
||||
mods = optimizeSamePointerIndexingAndUselessBeq(linesByFourteen)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFourteen = getLinesBy(lines, 14)
|
||||
|
@ -75,23 +75,48 @@ private fun String.isStoreReg() = this.startsWith("sta") || this.startsWith("sty
|
|||
private fun String.isStoreRegOrZero() = this.isStoreReg() || this.startsWith("stz")
|
||||
private fun String.isLoadReg() = this.startsWith("lda") || this.startsWith("ldy") || this.startsWith("ldx")
|
||||
|
||||
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?)
|
||||
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?, val removeLabel: Boolean=false)
|
||||
|
||||
private fun apply(modifications: List<Modification>, lines: MutableList<String>) {
|
||||
for (modification in modifications.sortedBy { it.lineIndex }.reversed()) {
|
||||
if(modification.remove)
|
||||
lines.removeAt(modification.lineIndex)
|
||||
if(modification.remove) {
|
||||
if(modification.removeLabel)
|
||||
lines.removeAt(modification.lineIndex)
|
||||
else {
|
||||
val line = lines[modification.lineIndex]
|
||||
if (line.length < 2 || line[0] == ';' || line.trimStart()[0] == ';')
|
||||
lines.removeAt(modification.lineIndex)
|
||||
else if (haslabel(line)) {
|
||||
val label = keeplabel(line)
|
||||
if (label.isNotEmpty())
|
||||
lines[modification.lineIndex] = label
|
||||
else
|
||||
lines.removeAt(modification.lineIndex)
|
||||
} else lines.removeAt(modification.lineIndex)
|
||||
}
|
||||
}
|
||||
else
|
||||
lines[modification.lineIndex] = modification.replacement!!
|
||||
}
|
||||
}
|
||||
|
||||
private fun haslabel(line: String): Boolean {
|
||||
return line.length>1 && line[0]!=';' && (!line[0].isWhitespace() || ':' in line)
|
||||
}
|
||||
|
||||
private fun keeplabel(line: String): String {
|
||||
if(':' in line)
|
||||
return line.substringBefore(':') + ':'
|
||||
val splits = line.split('\t', ' ', limit=2)
|
||||
return if(splits.size>1) splits[0] + ':' else ""
|
||||
}
|
||||
|
||||
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
||||
// all lines (that aren't empty or comments) in sliding windows of certain size
|
||||
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
||||
lines.asSequence().withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
||||
|
||||
private fun optimizeSameAssignments(
|
||||
linesByFourteen: List<List<IndexedValue<String>>>,
|
||||
linesByFourteen: Sequence<List<IndexedValue<String>>>,
|
||||
machine: IMachineDefinition,
|
||||
symbolTable: SymbolTable
|
||||
): List<Modification> {
|
||||
|
@ -281,7 +306,7 @@ private fun optimizeSameAssignments(
|
|||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
private fun optimizeSamePointerIndexingAndUselessBeq(linesByFourteen: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
|
||||
// Optimize same pointer indexing where for instance we load and store to the same ptr index in Y
|
||||
// if Y isn't modified in between we can omit the second LDY:
|
||||
|
@ -318,13 +343,42 @@ private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<
|
|||
mods.add(Modification(lines[4].index, true, null))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
beq +
|
||||
lda #1
|
||||
+
|
||||
[ optional: label_xxxx_shortcut line here]
|
||||
beq label_xxxx_shortcut / bne label_xxxx_shortcut
|
||||
or *_afterif labels.
|
||||
|
||||
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
|
||||
*/
|
||||
|
||||
if(first=="beq +" && second=="lda #1" && third=="+") {
|
||||
if((fourth.startsWith("beq label_") || fourth.startsWith("bne label_")) &&
|
||||
(fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) {
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
else if(fourth.startsWith("label_") && (fourth.endsWith("_shortcut") || fourth.endsWith("_shortcut:"))) {
|
||||
if((fifth.startsWith("beq label_") || fifth.startsWith("bne label_")) &&
|
||||
(fifth.endsWith("_shortcut") || fifth.endsWith("_afterif") || fifth.endsWith("_shortcut:") || fifth.endsWith("_afterif:"))) {
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeStoreLoadSame(
|
||||
linesByFour: List<List<IndexedValue<String>>>,
|
||||
linesByFour: Sequence<List<IndexedValue<String>>>,
|
||||
machine: IMachineDefinition,
|
||||
symbolTable: SymbolTable
|
||||
): List<Modification> {
|
||||
|
@ -414,7 +468,7 @@ private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
|||
}
|
||||
}
|
||||
|
||||
private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
|
||||
val mods = mutableListOf<Modification>()
|
||||
for (lines in linesByFour) {
|
||||
|
@ -435,10 +489,11 @@ private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<
|
|||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
// jsr Sub + rts -> jmp Sub
|
||||
// rts + jmp -> remove jmp
|
||||
// rts + bxx -> remove bxx
|
||||
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
|
||||
// and some other optimizations.
|
||||
|
||||
val mods = mutableListOf<Modification>()
|
||||
|
@ -472,6 +527,23 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: List<List<IndexedVal
|
|||
mods += Modification(lines[1].index, true, null)
|
||||
}
|
||||
|
||||
if (!haslabel(second)) {
|
||||
if ((" lda" in first || "\tlda" in first) && (" cmp #0" in second || "\tcmp #0" in second) ||
|
||||
(" ldx" in first || "\tldx" in first) && (" cpx #0" in second || "\tcpx #0" in second) ||
|
||||
(" ldy" in first || "\tldy" in first) && (" cpy #0" in second || "\tcpy #0" in second)
|
||||
) {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
else if(" cmp #0" in second || "\tcmp #0" in second) {
|
||||
// there are many instructions that modify A and set the bits...
|
||||
for(instr in arrayOf("lda", "ora", "and", "eor", "adc", "sbc", "asl", "cmp", "inc a", "lsr", "pla", "rol", "ror", "txa", "tya")) {
|
||||
if(" $instr" in first || "\t$instr" in first) {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
LDA NUM1
|
||||
CMP NUM2
|
||||
|
@ -501,7 +573,7 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: List<List<IndexedVal
|
|||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeUselessPushPopStack(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
val mods = mutableListOf<Modification>()
|
||||
|
||||
fun optimize(register: Char, lines: List<IndexedValue<String>>) {
|
||||
|
@ -538,7 +610,7 @@ private fun optimizeUselessPushPopStack(linesByFour: List<List<IndexedValue<Stri
|
|||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeUnneededTempvarInAdd(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
private fun optimizeUnneededTempvarInAdd(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
// sequence: sta P8ZP_SCRATCH_XX / lda something / clc / adc P8ZP_SCRATCH_XX
|
||||
// this can be performed without the scratch variable: clc / adc something
|
||||
val mods = mutableListOf<Modification>()
|
||||
|
|
|
@ -315,6 +315,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||
asmgen.out(" cmp #${arg2.number.toInt()}")
|
||||
}
|
||||
is PtBool -> {
|
||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||
asmgen.out(" cmp #${arg2.asInt()}")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if(arg2.address is PtNumber) {
|
||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||
|
@ -343,6 +347,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||
cmp ${asmgen.asmVariableName(arg2)}
|
||||
+""")
|
||||
}
|
||||
is PtBool -> TODO("word compare against bool")
|
||||
is PtNumber -> {
|
||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
|
|
|
@ -17,7 +17,7 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
|
|||
emptyList()
|
||||
else {
|
||||
val register = when (returntype!!) {
|
||||
in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
||||
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
|
@ -31,7 +31,7 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
|
|||
|
||||
internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
|
||||
return when(returntype) {
|
||||
in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
||||
null -> null
|
||||
|
|
|
@ -359,7 +359,7 @@ $loopLabel lda ${65535.toHex()} ; modified
|
|||
bne $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_BOOL -> {
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
|
|
|
@ -16,8 +16,8 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||
}
|
||||
|
||||
internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
|
||||
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|
||||
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
|
||||
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypesWithBoolean)
|
||||
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypesWithBoolean && sub.parameters[1].type in ByteDatatypesWithBoolean)
|
||||
|
||||
internal fun translateFunctionCall(call: PtFunctionCall) {
|
||||
// Output only the code to set up the parameters and perform the actual call
|
||||
|
@ -46,7 +46,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||
else if(sub is PtSub) {
|
||||
if(optimizeIntArgsViaRegisters(sub)) {
|
||||
if(sub.parameters.size==1) {
|
||||
val register = if (sub.parameters[0].type in ByteDatatypes) RegisterOrPair.A else RegisterOrPair.AY
|
||||
val register = if (sub.parameters[0].type in ByteDatatypesWithBoolean) RegisterOrPair.A else RegisterOrPair.AY
|
||||
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register)
|
||||
} else {
|
||||
// 2 byte params, second in Y, first in A
|
||||
|
@ -84,6 +84,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||
is PtMachineRegister -> false
|
||||
is PtMemoryByte -> return usesOtherRegistersWhileEvaluating(arg.address)
|
||||
is PtNumber -> false
|
||||
is PtBool -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +153,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||
}
|
||||
if (statusflag!=null) {
|
||||
if(requiredDt!=value.type)
|
||||
throw AssemblyError("for statusflag, byte value is required")
|
||||
throw AssemblyError("for statusflag, byte or bool value is required")
|
||||
if (statusflag == Statusflag.Pc) {
|
||||
// this boolean param needs to be set last, right before the jsr
|
||||
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
||||
|
@ -161,6 +162,9 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||
val carrySet = value.number.toInt() != 0
|
||||
asmgen.out(if(carrySet) " sec" else " clc")
|
||||
}
|
||||
is PtBool -> {
|
||||
asmgen.out(if(value.value) " sec" else " clc")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val sourceName = asmgen.asmVariableName(value)
|
||||
// note: cannot use X register here to store A because it might be used for other arguments
|
||||
|
@ -218,6 +222,8 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
||||
if(argType isAssignableTo paramType)
|
||||
return true
|
||||
if(argType==DataType.BOOL && paramType==DataType.BOOL)
|
||||
return true
|
||||
if(argType in ByteDatatypes && paramType in ByteDatatypes)
|
||||
return true
|
||||
if(argType in WordDatatypes && paramType in WordDatatypes)
|
||||
|
|
1759
codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt
Normal file
1759
codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -173,6 +173,7 @@ internal class ProgramAndVarsGen(
|
|||
for(num in 1..count) {
|
||||
val name = asmgen.buildTempVarName(dt, num)
|
||||
when (dt) {
|
||||
DataType.BOOL -> asmgen.out("$name .byte ?")
|
||||
DataType.BYTE -> asmgen.out("$name .char ?")
|
||||
DataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||
DataType.WORD -> asmgen.out("$name .sint ?")
|
||||
|
@ -404,7 +405,7 @@ internal class ProgramAndVarsGen(
|
|||
if(sub.parameters.size==1) {
|
||||
val dt = sub.parameters[0].type
|
||||
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
|
||||
if(dt in ByteDatatypes)
|
||||
if(dt in ByteDatatypesWithBoolean)
|
||||
asmgen.assignRegister(RegisterOrPair.A, target)
|
||||
else
|
||||
asmgen.assignRegister(RegisterOrPair.AY, target)
|
||||
|
@ -604,7 +605,7 @@ internal class ProgramAndVarsGen(
|
|||
|
||||
private fun uninitializedVariable2asm(variable: StStaticVariable) {
|
||||
when (variable.dt) {
|
||||
DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ?")
|
||||
DataType.BOOL, DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ?")
|
||||
DataType.BYTE -> asmgen.out("${variable.name}\t.char ?")
|
||||
DataType.UWORD -> asmgen.out("${variable.name}\t.word ?")
|
||||
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?")
|
||||
|
@ -634,6 +635,7 @@ internal class ProgramAndVarsGen(
|
|||
} else 0
|
||||
|
||||
when (variable.dt) {
|
||||
DataType.BOOL -> TODO("bool var to asm")
|
||||
DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
|
||||
DataType.BYTE -> asmgen.out("${variable.name}\t.char $initialValue")
|
||||
DataType.UWORD -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
|
||||
|
@ -658,7 +660,7 @@ internal class ProgramAndVarsGen(
|
|||
|
||||
private fun arrayVariable2asm(varname: String, dt: DataType, value: StArray?, orNumberOfZeros: Int?) {
|
||||
when(dt) {
|
||||
DataType.ARRAY_UB -> {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_BOOL -> {
|
||||
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
||||
if (data.size <= 16)
|
||||
asmgen.out("$varname\t.byte ${data.joinToString()}")
|
||||
|
@ -726,7 +728,7 @@ internal class ProgramAndVarsGen(
|
|||
private fun zeroFilledArray(numElts: Int): StArray {
|
||||
val values = mutableListOf<StArrayElement>()
|
||||
repeat(numElts) {
|
||||
values.add(StArrayElement(0.0, null))
|
||||
values.add(StArrayElement(0.0, null, null))
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
@ -763,6 +765,16 @@ internal class ProgramAndVarsGen(
|
|||
private fun makeArrayFillDataUnsigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
|
||||
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||
return when (dt) {
|
||||
DataType.ARRAY_BOOL ->
|
||||
// byte array can never contain pointer-to types, so treat values as all integers
|
||||
array.map {
|
||||
if(it.boolean!=null)
|
||||
if(it.boolean==true) "1" else "0"
|
||||
else {
|
||||
val number = it.number!!
|
||||
if(number==0.0) "0" else "1"
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_UB ->
|
||||
// byte array can never contain pointer-to types, so treat values as all integers
|
||||
array.map {
|
||||
|
|
|
@ -91,7 +91,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||
if(errors.noErrors()) {
|
||||
val sortedList = varsDontCare.sortedByDescending { it.scopedName }
|
||||
for (variable in sortedList) {
|
||||
if(variable.dt in IntegerDatatypes) {
|
||||
if(variable.dt in IntegerDatatypesWithBoolean) {
|
||||
if(zeropage.free.isEmpty()) {
|
||||
break
|
||||
} else {
|
||||
|
|
|
@ -18,8 +18,8 @@ internal class AnyExprAsmGen(
|
|||
) {
|
||||
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.type) {
|
||||
in ByteDatatypes -> {
|
||||
if(expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes)
|
||||
in ByteDatatypesWithBoolean -> {
|
||||
if(expr.left.type in ByteDatatypesWithBoolean && expr.right.type in ByteDatatypesWithBoolean)
|
||||
return assignByteBinExpr(expr, assign)
|
||||
if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
||||
require(expr.operator in ComparisonOperators)
|
||||
|
|
|
@ -14,6 +14,7 @@ internal enum class TargetStorageKind {
|
|||
}
|
||||
|
||||
internal enum class SourceStorageKind {
|
||||
LITERALBOOLEAN,
|
||||
LITERALNUMBER,
|
||||
VARIABLE,
|
||||
ARRAY,
|
||||
|
@ -43,8 +44,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||
}
|
||||
|
||||
init {
|
||||
if(register!=null && datatype !in NumericDatatypes)
|
||||
throw AssemblyError("register must be integer or float type")
|
||||
if(register!=null && datatype !in NumericDatatypesWithBoolean)
|
||||
throw AssemblyError("must be numeric type")
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -133,6 +134,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||
val memory: PtMemoryByte? = null,
|
||||
val register: RegisterOrPair? = null,
|
||||
val number: PtNumber? = null,
|
||||
val boolean: PtBool? = null,
|
||||
val expression: PtExpression? = null
|
||||
)
|
||||
{
|
||||
|
@ -147,6 +149,9 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||
val cv = value as? PtNumber
|
||||
if(cv!=null)
|
||||
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
||||
val bv = value as? PtBool
|
||||
if(bv!=null)
|
||||
return AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.BOOL, boolean = bv)
|
||||
|
||||
return when(value) {
|
||||
// checked above: is PtNumber -> throw AssemblyError("should have been constant value")
|
||||
|
@ -194,7 +199,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||
// allow some signed/unsigned relaxations
|
||||
|
||||
fun withAdjustedDt(newType: DataType) =
|
||||
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, expression)
|
||||
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, boolean, expression)
|
||||
|
||||
if(target.datatype!=datatype) {
|
||||
if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import prog8.codegen.cpu6502.returnsWhatWhere
|
|||
|
||||
internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val options: CompilationOptions,
|
||||
private val anyExprGen: AnyExprAsmGen,
|
||||
private val allocator: VariableAllocator) {
|
||||
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator)
|
||||
|
@ -33,10 +34,21 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
|
||||
fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) {
|
||||
when(assign.source.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> {
|
||||
// simple case: assign a constant boolean (0 or 1)
|
||||
val num = assign.source.boolean!!.asInt()
|
||||
when (assign.target.datatype) {
|
||||
DataType.BOOL, DataType.UBYTE, DataType.BYTE -> assignConstantByte(assign.target, num)
|
||||
DataType.UWORD, DataType.WORD -> assignConstantWord(assign.target, num)
|
||||
DataType.FLOAT -> assignConstantFloat(assign.target, num.toDouble())
|
||||
else -> throw AssemblyError("weird numval type")
|
||||
}
|
||||
}
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
// simple case: assign a constant number
|
||||
val num = assign.source.number!!.number
|
||||
when (assign.target.datatype) {
|
||||
DataType.BOOL -> assignConstantByte(assign.target, if(num==0.0) 0 else 1)
|
||||
DataType.UBYTE, DataType.BYTE -> assignConstantByte(assign.target, num.toInt())
|
||||
DataType.UWORD, DataType.WORD -> assignConstantWord(assign.target, num.toInt())
|
||||
DataType.FLOAT -> assignConstantFloat(assign.target, num)
|
||||
|
@ -47,6 +59,10 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
// simple case: assign from another variable
|
||||
val variable = assign.source.asmVarname
|
||||
when (assign.target.datatype) {
|
||||
DataType.BOOL -> {
|
||||
if (assign.source.datatype == DataType.BOOL || !options.strictBool) assignVariableByte(assign.target, variable)
|
||||
else throw AssemblyError("assigning non-bool variable to boolean, should have been typecasted")
|
||||
}
|
||||
DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable)
|
||||
DataType.WORD -> assignVariableWord(assign.target, variable, assign.source.datatype)
|
||||
DataType.UWORD -> {
|
||||
|
@ -83,7 +99,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
// constant array index value
|
||||
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
|
||||
when (elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
in ByteDatatypesWithBoolean -> {
|
||||
asmgen.out(" lda $arrayVarName+$indexValue")
|
||||
assignRegisterByte(assign.target, CpuRegister.A, elementDt in SignedDatatypes, false)
|
||||
}
|
||||
|
@ -100,7 +116,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
}
|
||||
} else {
|
||||
when (elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
in ByteDatatypesWithBoolean -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVarName,y")
|
||||
assignRegisterByte(assign.target, CpuRegister.A, elementDt in SignedDatatypes, true)
|
||||
|
@ -169,6 +185,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
val arrayDt = value.identifier.type
|
||||
assignAddressOf(assign.target, sourceName, arrayDt, value.arrayIndexExpr)
|
||||
}
|
||||
is PtBool -> throw AssemblyError("source kind should have been literalboolean")
|
||||
is PtNumber -> throw AssemblyError("source kind should have been literalnumber")
|
||||
is PtIdentifier -> throw AssemblyError("source kind should have been variable")
|
||||
is PtArrayIndexer -> throw AssemblyError("source kind should have been array")
|
||||
|
@ -237,7 +254,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
if(assign.target.register==null) {
|
||||
// still need to assign the result to the target variable/etc.
|
||||
when(returnDt) {
|
||||
in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A, returnDt in SignedDatatypes, false) // function's byte result is in A
|
||||
in ByteDatatypesWithBoolean -> assignRegisterByte(assign.target, CpuRegister.A, returnDt in SignedDatatypes, false) // function's byte result is in A
|
||||
in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY
|
||||
DataType.STR -> {
|
||||
when (assign.target.datatype) {
|
||||
|
@ -265,10 +282,10 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
}
|
||||
is PtPrefix -> {
|
||||
if(assign.target.array==null) {
|
||||
if(assign.source.datatype==assign.target.datatype) {
|
||||
if(assign.source.datatype in IntegerDatatypes) {
|
||||
if(assign.source.datatype isAssignableTo assign.target.datatype || (assign.source.datatype==DataType.BOOL && assign.target.datatype in ByteDatatypes)) {
|
||||
if(assign.source.datatype in IntegerDatatypesWithBoolean) {
|
||||
val signed = assign.source.datatype in SignedDatatypes
|
||||
if(assign.source.datatype in ByteDatatypes) {
|
||||
if(assign.source.datatype in ByteDatatypesWithBoolean) {
|
||||
assignExpressionToRegister(value.value, RegisterOrPair.A, signed)
|
||||
when(value.operator) {
|
||||
"+" -> {}
|
||||
|
@ -279,7 +296,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
asmgen.out(" eor #255 | clc | adc #1")
|
||||
}
|
||||
"~" -> asmgen.out(" eor #255")
|
||||
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
|
||||
"not" -> asmgen.out(" eor #1")
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
assignRegisterByte(assign.target, CpuRegister.A, signed, false)
|
||||
|
@ -300,7 +317,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
txa""")
|
||||
}
|
||||
"~" -> asmgen.out(" tax | tya | eor #255 | tay | txa | eor #255")
|
||||
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
|
||||
"not" -> throw AssemblyError("not shouldn't exist for an integer")
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
|
@ -318,13 +335,13 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
"+" -> {}
|
||||
"-" -> inplaceNegate(assign, true, scope)
|
||||
"~" -> inplaceInvert(assign, scope)
|
||||
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
|
||||
"not" -> inplaceInvert(assign, scope)
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// use a temporary variable
|
||||
val tempvar = if(value.type in ByteDatatypes) "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_W1"
|
||||
val tempvar = if(value.type in ByteDatatypesWithBoolean) "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_W1"
|
||||
assignExpressionToVariable(value.value, tempvar, value.type)
|
||||
when (value.operator) {
|
||||
"+" -> {}
|
||||
|
@ -338,10 +355,16 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
else
|
||||
inplaceInvert(assignTempvar, scope)
|
||||
}
|
||||
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
|
||||
"not" -> {
|
||||
val assignTempvar = AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = tempvar),
|
||||
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, value.type, scope, assign.position, variableAsmName = tempvar),
|
||||
program.memsizer, assign.position)
|
||||
inplaceInvert(assignTempvar, scope)
|
||||
}
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
if(value.type in ByteDatatypes)
|
||||
if(value.type in ByteDatatypesWithBoolean)
|
||||
assignVariableByte(assign.target, tempvar)
|
||||
else
|
||||
assignVariableWord(assign.target, tempvar, value.type)
|
||||
|
@ -372,16 +395,15 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
assignExpressionToRegister(assign.source.expression, RegisterOrPair.FAC1, true)
|
||||
assignFAC1float(assign.target)
|
||||
} else {
|
||||
// array[x] = -value ... use a tempvar then store that back into the array.
|
||||
val tempvar = asmgen.getTempVarName(assign.target.datatype)
|
||||
val assignToTempvar = AsmAssignment(assign.source,
|
||||
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, assign.target.datatype, assign.target.scope, assign.target.position,
|
||||
variableAsmName=tempvar, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position)
|
||||
asmgen.translateNormalAssignment(assignToTempvar, scope)
|
||||
val register = if(assign.source.datatype in ByteDatatypesWithBoolean) RegisterOrPair.A else RegisterOrPair.AY
|
||||
val assignToRegister = AsmAssignment(assign.source,
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, assign.target.datatype, assign.target.scope, assign.target.position,
|
||||
register = register, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position)
|
||||
asmgen.translateNormalAssignment(assignToRegister, scope)
|
||||
val signed = assign.target.datatype in SignedDatatypes
|
||||
when(assign.target.datatype) {
|
||||
in ByteDatatypes -> assignVariableByte(assign.target, tempvar)
|
||||
in WordDatatypes -> assignVariableWord(assign.target, tempvar, assign.source.datatype)
|
||||
DataType.FLOAT -> assignVariableFloat(assign.target, tempvar)
|
||||
in ByteDatatypesWithBoolean -> assignRegisterByte(assign.target, CpuRegister.A, signed, false)
|
||||
in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
|
@ -389,8 +411,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
|
||||
private fun assignVirtualRegister(target: AsmAssignTarget, register: RegisterOrPair) {
|
||||
when(target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(" lda cx16.${register.toString().lowercase()}L")
|
||||
in ByteDatatypesWithBoolean -> {
|
||||
if(register in Cx16VirtualRegisters) {
|
||||
asmgen.out(" lda cx16.${register.toString().lowercase()}L")
|
||||
} else {
|
||||
TODO("LDA byte from $register")
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false, false)
|
||||
}
|
||||
in WordDatatypes -> assignRegisterpairWord(target, register)
|
||||
|
@ -401,8 +427,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
private fun attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
val translatedOk = when (expr.operator) {
|
||||
in ComparisonOperators -> optimizedComparison(expr, assign)
|
||||
in setOf("&", "|", "^", "xor") -> optimizedBitwiseExpr(expr, assign.target)
|
||||
in setOf("and", "or") -> optimizedLogicalAndOrExpr(expr, assign.target)
|
||||
in BitwiseOperators -> optimizedBitwiseExpr(expr, assign.target)
|
||||
in LogicalOperators -> optimizedLogicalExpr(expr, assign.target)
|
||||
"+", "-" -> optimizedPlusMinExpr(expr, assign.target)
|
||||
"<<", ">>" -> optimizedBitshiftExpr(expr, assign.target)
|
||||
"*" -> optimizedMultiplyExpr(expr, assign.target)
|
||||
|
@ -421,7 +447,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
if(expr.right.asConstInteger() == 0) {
|
||||
if(expr.operator == "==" || expr.operator=="!=") {
|
||||
when(assign.target.datatype) {
|
||||
in ByteDatatypes -> if(attemptAssignToByteCompareZero(expr, assign)) return true
|
||||
in ByteDatatypesWithBoolean -> if(attemptAssignToByteCompareZero(expr, assign)) return true
|
||||
else -> {
|
||||
// do nothing, this is handled by a type cast.
|
||||
}
|
||||
|
@ -429,6 +455,101 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
}
|
||||
}
|
||||
|
||||
if(expr.left.type==DataType.UBYTE) {
|
||||
if(expr.operator=="<") {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
when(val right = expr.right) {
|
||||
is PtIdentifier -> {
|
||||
asmgen.out("""
|
||||
cmp ${right.name}
|
||||
rol a
|
||||
and #1
|
||||
eor #1""")
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false, false)
|
||||
return true
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
val addr = right.address as? PtNumber
|
||||
if(addr!=null) {
|
||||
asmgen.out("""
|
||||
cmp ${addr.number.toHex()}
|
||||
rol a
|
||||
and #1
|
||||
eor #1""")
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false, false)
|
||||
return true
|
||||
}
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||
val ptrvar = right.address as? PtIdentifier
|
||||
if (ptrvar != null && asmgen.isZpVar(ptrvar)) {
|
||||
asmgen.out("""
|
||||
cmp (${ptrvar.name})
|
||||
rol a
|
||||
and #1
|
||||
eor #1""")
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false, false)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
is PtNumber -> {
|
||||
asmgen.out("""
|
||||
cmp #${right.number.toInt()}
|
||||
rol a
|
||||
and #1
|
||||
eor #1""")
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false, false)
|
||||
return true
|
||||
}
|
||||
else -> { /* not optimizable */ }
|
||||
}
|
||||
}
|
||||
else if(expr.operator==">=") {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
when(val right = expr.right) {
|
||||
is PtIdentifier -> {
|
||||
asmgen.out("""
|
||||
cmp ${right.name}
|
||||
rol a
|
||||
and #1""")
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false, false)
|
||||
return true
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
val addr = right.address as? PtNumber
|
||||
if(addr!=null) {
|
||||
asmgen.out("""
|
||||
cmp ${addr.number.toHex()}
|
||||
rol a
|
||||
and #1""")
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false, false)
|
||||
return true
|
||||
}
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||
val ptrvar = right.address as? PtIdentifier
|
||||
if (ptrvar != null && asmgen.isZpVar(ptrvar)) {
|
||||
asmgen.out("""
|
||||
cmp (${ptrvar.name})
|
||||
rol a
|
||||
and #1""")
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false, false)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
is PtNumber -> {
|
||||
asmgen.out("""
|
||||
cmp #${right.number.toInt()}
|
||||
rol a
|
||||
and #1""")
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false, false)
|
||||
return true
|
||||
}
|
||||
else -> { /* not optimizable */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// b = v > 99 --> b=false , if v>99 b=true
|
||||
val targetReg=assign.target.register
|
||||
if(targetReg!=null) {
|
||||
|
@ -543,7 +664,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
return true
|
||||
}
|
||||
|
||||
private fun directIntoY(expr: PtExpression): Boolean {
|
||||
internal fun directIntoY(expr: PtExpression): Boolean {
|
||||
return when(expr) {
|
||||
is PtIdentifier -> true
|
||||
is PtMachineRegister -> true
|
||||
|
@ -1123,6 +1244,22 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
return true
|
||||
}
|
||||
}
|
||||
val rightArray = expr.right as? PtArrayIndexer
|
||||
if(rightArray!=null) {
|
||||
val constIndex = rightArray.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
val valueVarname = "${asmgen.asmSymbolName(rightArray.variable)} + ${constIndex*program.memsizer.memorySize(rightArray.type)}"
|
||||
when(expr.operator) {
|
||||
"&" -> asmgen.out(" and $valueVarname")
|
||||
"|" -> asmgen.out(" ora $valueVarname")
|
||||
"^" -> asmgen.out(" eor $valueVarname")
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
if(directIntoY(expr.right)) {
|
||||
|
@ -1136,7 +1273,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
when (expr.operator) {
|
||||
"&" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||
"|" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
||||
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
||||
"^" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
||||
else -> throw AssemblyError("invalid bitwise operator")
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
|
@ -1157,7 +1294,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
when (expr.operator) {
|
||||
"&" -> asmgen.out(" and P8ZP_SCRATCH_W1 | tax | tya | and P8ZP_SCRATCH_W1+1 | tay | txa")
|
||||
"|" -> asmgen.out(" ora P8ZP_SCRATCH_W1 | tax | tya | ora P8ZP_SCRATCH_W1+1 | tay | txa")
|
||||
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_W1 | tax | tya | eor P8ZP_SCRATCH_W1+1 | tay | txa")
|
||||
"^" -> asmgen.out(" eor P8ZP_SCRATCH_W1 | tax | tya | eor P8ZP_SCRATCH_W1+1 | tay | txa")
|
||||
else -> throw AssemblyError("invalid bitwise operator")
|
||||
}
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
|
@ -1166,61 +1303,105 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
return false
|
||||
}
|
||||
|
||||
private fun optimizedLogicalAndOrExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
|
||||
if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
|
||||
if (expr.right is PtNumber || expr.right is PtIdentifier) {
|
||||
assignLogicalAndOrWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right)
|
||||
return true
|
||||
}
|
||||
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
|
||||
assignLogicalAndOrWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left)
|
||||
return true
|
||||
}
|
||||
internal fun optimizedLogicalExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
|
||||
|
||||
if(!expr.right.isSimple()) {
|
||||
// shortcircuit evaluation into A
|
||||
val shortcutLabel = asmgen.makeLabel("shortcut")
|
||||
when (expr.operator) {
|
||||
"and" -> {
|
||||
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" beq $shortcutLabel")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
|
||||
asmgen.out(shortcutLabel)
|
||||
}
|
||||
"or" -> {
|
||||
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" bne $shortcutLabel")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
|
||||
asmgen.out(shortcutLabel)
|
||||
}
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
}
|
||||
} else {
|
||||
// normal evaluation into A, it is *likely* shorter and faster because of the simple operands.
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
if(directIntoY(expr.right)) {
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
|
||||
fun swapOperands(): Boolean =
|
||||
if(expr.right is PtIdentifier || expr.right is PtMemoryByte)
|
||||
false
|
||||
else
|
||||
expr.left is PtIdentifier || expr.left is PtMemoryByte
|
||||
|
||||
fun assignResultIntoA(left: PtExpression, operator: String, right: PtExpression) {
|
||||
// non short-circuit evaluation it is *likely* shorter and faster because of the simple operands.
|
||||
|
||||
fun assignViaScratch() {
|
||||
if(directIntoY(right)) {
|
||||
assignExpressionToRegister(right, RegisterOrPair.Y, false)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_B1")
|
||||
} else {
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
when (expr.operator) {
|
||||
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 logical operator")
|
||||
}
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
return true
|
||||
|
||||
assignExpressionToRegister(left, RegisterOrPair.A, false)
|
||||
when(right) {
|
||||
is PtBool -> throw AssemblyError("bool literal in logical expr should have been optimized away")
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(right)
|
||||
when (operator) {
|
||||
"and" -> asmgen.out(" and $varname")
|
||||
"or" -> asmgen.out(" ora $varname")
|
||||
"xor" -> asmgen.out(" eor $varname")
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
}
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
val constAddress = right.address.asConstInteger()
|
||||
if(constAddress!=null) {
|
||||
when (operator) {
|
||||
"and" -> asmgen.out(" and ${constAddress.toHex()}")
|
||||
"or" -> asmgen.out(" ora ${constAddress.toHex()}")
|
||||
"xor" -> asmgen.out(" eor ${constAddress.toHex()}")
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
}
|
||||
}
|
||||
else assignViaScratch()
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
val constIndex = right.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
val valueVarname = "${asmgen.asmSymbolName(right.variable)} + ${constIndex*program.memsizer.memorySize(right.type)}"
|
||||
when(operator) {
|
||||
"and" -> asmgen.out(" and $valueVarname")
|
||||
"or" -> asmgen.out(" ora $valueVarname")
|
||||
"xor" -> asmgen.out(" eor $valueVarname")
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
}
|
||||
}
|
||||
else assignViaScratch()
|
||||
}
|
||||
else -> assignViaScratch()
|
||||
}
|
||||
}
|
||||
else if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
||||
throw AssemblyError("logical and/or/xor on words should be done on typecast to bytes instead")
|
||||
|
||||
if(!expr.right.isSimple() && expr.operator!="xor") {
|
||||
// shortcircuit evaluation into A
|
||||
val shortcutLabel = asmgen.makeLabel("shortcut")
|
||||
when (expr.operator) {
|
||||
"and" -> {
|
||||
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" beq $shortcutLabel")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
|
||||
asmgen.out(shortcutLabel)
|
||||
}
|
||||
"or" -> {
|
||||
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" bne $shortcutLabel")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
|
||||
asmgen.out(shortcutLabel)
|
||||
}
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
}
|
||||
} else if(swapOperands()) {
|
||||
// non short-circuit evaluation is *likely* shorter and faster because of the simple operands.
|
||||
assignResultIntoA(expr.right, expr.operator, expr.left)
|
||||
} else {
|
||||
// non short-circuit evaluation is *likely* shorter and faster because of the simple operands.
|
||||
assignResultIntoA(expr.left, expr.operator, expr.right)
|
||||
}
|
||||
return false
|
||||
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun assignBitwiseWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
|
||||
|
@ -1233,31 +1414,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
when (operator) {
|
||||
"&" -> asmgen.out(" and $operand")
|
||||
"|" -> asmgen.out(" ora $operand")
|
||||
"^", "xor" -> asmgen.out(" eor $operand")
|
||||
"^" -> asmgen.out(" eor $operand")
|
||||
else -> throw AssemblyError("invalid operator")
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
}
|
||||
|
||||
private fun assignLogicalAndOrWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
|
||||
// normal evaluation, not worth to shortcircuit the simple right operand
|
||||
assignExpressionToRegister(left, RegisterOrPair.A, false)
|
||||
if(directIntoY(right)) {
|
||||
assignExpressionToRegister(right, RegisterOrPair.Y, false)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_B1")
|
||||
} else {
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
when (operator) {
|
||||
"and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||
"or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
}
|
||||
|
||||
private fun assignBitwiseWithSimpleRightOperandWord(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY, false)
|
||||
when(right) {
|
||||
|
@ -1266,7 +1428,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
when (operator) {
|
||||
"&" -> asmgen.out(" and #<$number | tax | tya | and #>$number | tay | txa")
|
||||
"|" -> asmgen.out(" ora #<$number | tax | tya | ora #>$number | tay | txa")
|
||||
"^", "xor" -> asmgen.out(" eor #<$number | tax | tya | eor #>$number | tay | txa")
|
||||
"^" -> asmgen.out(" eor #<$number | tax | tya | eor #>$number | tay | txa")
|
||||
else -> throw AssemblyError("invalid bitwise operator")
|
||||
}
|
||||
}
|
||||
|
@ -1275,7 +1437,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
when (operator) {
|
||||
"&" -> asmgen.out(" and $name | tax | tya | and $name+1 | tay | txa")
|
||||
"|" -> asmgen.out(" ora $name | tax | tya | ora $name+1 | tay | txa")
|
||||
"^", "xor" -> asmgen.out(" eor $name | tax | tya | eor $name+1 | tay | txa")
|
||||
"^" -> asmgen.out(" eor $name | tax | tya | eor $name+1 | tay | txa")
|
||||
else -> throw AssemblyError("invalid bitwise operator")
|
||||
}
|
||||
}
|
||||
|
@ -1288,6 +1450,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
when (expr.operator) {
|
||||
"==" -> {
|
||||
when(val dt = expr.left.type) {
|
||||
DataType.BOOL -> TODO("compare bool to 0")
|
||||
in ByteDatatypes -> {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
|
||||
asmgen.out("""
|
||||
|
@ -1321,6 +1484,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
}
|
||||
"!=" -> {
|
||||
when(val dt = expr.left.type) {
|
||||
DataType.BOOL -> TODO("compare bool to 0")
|
||||
in ByteDatatypes -> {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
|
||||
asmgen.out(" beq + | lda #1")
|
||||
|
@ -1434,7 +1598,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
when(value) {
|
||||
is PtIdentifier -> {
|
||||
if(targetDt in WordDatatypes) {
|
||||
if(valueDt==DataType.UBYTE) {
|
||||
if(valueDt==DataType.UBYTE || valueDt==DataType.BOOL) {
|
||||
assignVariableUByteIntoWord(target, value)
|
||||
return
|
||||
}
|
||||
|
@ -1478,11 +1642,13 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
return
|
||||
}
|
||||
}
|
||||
is PtNumber -> throw AssemblyError("a cast of a literal value should have been const-folded away")
|
||||
is PtNumber, is PtBool -> throw AssemblyError("a cast of a literal value should have been const-folded away")
|
||||
is PtArrayIndexer -> {
|
||||
if(targetDt in ByteDatatypes && valueDt in WordDatatypes) {
|
||||
// just assign the lsb from the array value
|
||||
return assignCastViaLsbFunc(value, target)
|
||||
} else if(targetDt==DataType.BOOL && valueDt in WordDatatypes) {
|
||||
return assignWordToBool(value, target)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
|
@ -1495,6 +1661,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt)
|
||||
|
||||
when (valueDt) {
|
||||
DataType.BOOL -> {
|
||||
if(targetDt in ByteDatatypes) {
|
||||
// optimization to assign boolean expression to byte target (just assign the 0 or 1 directly, no cast needed)
|
||||
val assignDirect = AsmAssignment(
|
||||
AsmAssignSource.fromAstSource(value, program, asmgen),
|
||||
target,
|
||||
program.memsizer,
|
||||
target.position
|
||||
)
|
||||
assignExpression(assignDirect, target.scope)
|
||||
} else {
|
||||
throw AssemblyError("expected bool or byte target type")
|
||||
}
|
||||
}
|
||||
in ByteDatatypes -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.A, valueDt==DataType.BYTE)
|
||||
assignTypeCastedRegisters(target.asmVarname, targetDt, RegisterOrPair.A, valueDt)
|
||||
|
@ -1525,7 +1705,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
}
|
||||
}
|
||||
|
||||
if(valueDt in ByteDatatypes) {
|
||||
if(valueDt in ByteDatatypesWithBoolean) {
|
||||
when(target.register) {
|
||||
RegisterOrPair.A,
|
||||
RegisterOrPair.X,
|
||||
|
@ -1548,9 +1728,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
RegisterOrPair.A,
|
||||
RegisterOrPair.X,
|
||||
RegisterOrPair.Y -> {
|
||||
// cast an uword to a byte register, do this via lsb(value)
|
||||
// generate code for lsb(value) here instead of the ubyte typecast
|
||||
return assignCastViaLsbFunc(value, target)
|
||||
return if(targetDt==DataType.BOOL)
|
||||
assignWordToBool(value, target)
|
||||
else
|
||||
// cast an uword to a byte register, do this via lsb(value)
|
||||
// generate code for lsb(value) here instead of the ubyte typecast
|
||||
assignCastViaLsbFunc(value, target)
|
||||
}
|
||||
RegisterOrPair.AX,
|
||||
RegisterOrPair.AY,
|
||||
|
@ -1566,22 +1749,25 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
if(target.kind==TargetStorageKind.REGISTER) {
|
||||
if(valueDt==DataType.FLOAT && target.datatype!=DataType.FLOAT) {
|
||||
// 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, origTypeCastExpression.definingISub(), target.position)
|
||||
assignExpressionToRegister(value, RegisterOrPair.FAC1, targetDt in SignedDatatypes)
|
||||
assignTypeCastedFloatFAC1("P8ZP_SCRATCH_W1", targetDt)
|
||||
assignVariableToRegister("P8ZP_SCRATCH_W1", target.register!!, targetDt in SignedDatatypes, origTypeCastExpression.definingISub(), target.position)
|
||||
return
|
||||
} else {
|
||||
if(!(valueDt isAssignableTo targetDt)) {
|
||||
return if(valueDt in WordDatatypes && targetDt in ByteDatatypes) {
|
||||
// word to byte, just take the lsb
|
||||
assignCastViaLsbFunc(value, target)
|
||||
} else if(valueDt in WordDatatypes && targetDt==DataType.BOOL) {
|
||||
// word to bool
|
||||
assignWordToBool(value, target)
|
||||
} else if(valueDt in WordDatatypes && targetDt in WordDatatypes) {
|
||||
// word to word, just assign
|
||||
assignExpressionToRegister(value, target.register!!, valueDt in SignedDatatypes)
|
||||
} else if(valueDt in ByteDatatypes && targetDt in ByteDatatypes) {
|
||||
} else if(valueDt in ByteDatatypesWithBoolean && targetDt in ByteDatatypes) {
|
||||
// byte to byte, just assign
|
||||
assignExpressionToRegister(value, target.register!!, valueDt in SignedDatatypes)
|
||||
} else if(valueDt in ByteDatatypes && targetDt in WordDatatypes) {
|
||||
} else if(valueDt in ByteDatatypesWithBoolean && targetDt in WordDatatypes) {
|
||||
// byte to word, just assign
|
||||
assignExpressionToRegister(value, target.register!!, valueDt==DataType.WORD)
|
||||
} else
|
||||
|
@ -1590,8 +1776,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
}
|
||||
}
|
||||
|
||||
if(targetDt in IntegerDatatypes && valueDt in IntegerDatatypes && valueDt!=targetDt && valueDt.isAssignableTo(targetDt)) {
|
||||
require(targetDt in WordDatatypes && valueDt in ByteDatatypes) {
|
||||
if(targetDt in IntegerDatatypes && valueDt in IntegerDatatypesWithBoolean && valueDt.isAssignableTo(targetDt)) {
|
||||
require(targetDt in WordDatatypes && valueDt in ByteDatatypesWithBoolean) {
|
||||
"should be byte to word assignment ${origTypeCastExpression.position}"
|
||||
}
|
||||
when(target.kind) {
|
||||
|
@ -1661,12 +1847,24 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
translateNormalAssignment(assign, value.definingISub())
|
||||
}
|
||||
|
||||
private fun assignWordToBool(value: PtExpression, target: AsmAssignTarget) {
|
||||
assignExpressionToRegister(value, RegisterOrPair.AY, false)
|
||||
asmgen.out("""
|
||||
sty P8ZP_SCRATCH_REG
|
||||
ora P8ZP_SCRATCH_REG
|
||||
beq +
|
||||
lda #1
|
||||
+""")
|
||||
assignRegisterByte(target, CpuRegister.A, false, false)
|
||||
}
|
||||
|
||||
private fun assignTypeCastedFloatFAC1(targetAsmVarName: String, targetDt: DataType) {
|
||||
|
||||
if(targetDt==DataType.FLOAT)
|
||||
throw AssemblyError("typecast to identical type")
|
||||
|
||||
when(targetDt) {
|
||||
DataType.BOOL -> asmgen.out(" jsr floats.cast_FAC1_as_bool_into_a | sta $targetAsmVarName")
|
||||
DataType.UBYTE -> asmgen.out(" jsr floats.cast_FAC1_as_uw_into_ya | sty $targetAsmVarName")
|
||||
DataType.BYTE -> asmgen.out(" jsr floats.cast_FAC1_as_w_into_ay | sta $targetAsmVarName")
|
||||
DataType.UWORD -> asmgen.out(" jsr floats.cast_FAC1_as_uw_into_ya | sty $targetAsmVarName | sta $targetAsmVarName+1")
|
||||
|
@ -1683,9 +1881,16 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
|
||||
// also see: PtExpressionAsmGen, fun translateExpression(typecast: PtTypeCast)
|
||||
when(sourceDt) {
|
||||
DataType.UBYTE -> {
|
||||
DataType.UBYTE, DataType.BOOL -> {
|
||||
when(targetDt) {
|
||||
DataType.BYTE -> {
|
||||
DataType.BOOL -> {
|
||||
asmgen.out("""
|
||||
lda $sourceAsmVarName
|
||||
beq +
|
||||
lda #1
|
||||
+ sta $targetAsmVarName""")
|
||||
}
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
|
@ -1703,12 +1908,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
ldy $sourceAsmVarName
|
||||
jsr floats.cast_from_ub""")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
else -> throw AssemblyError("weird type $targetDt")
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
when(targetDt) {
|
||||
DataType.UBYTE -> {
|
||||
DataType.UBYTE, DataType.BOOL -> {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
|
@ -1735,6 +1940,14 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
}
|
||||
DataType.UWORD -> {
|
||||
when(targetDt) {
|
||||
DataType.BOOL -> {
|
||||
asmgen.out("""
|
||||
lda $sourceAsmVarName
|
||||
ora $sourceAsmVarName+1
|
||||
beq +
|
||||
lda #1
|
||||
+ sta $targetAsmVarName""")
|
||||
}
|
||||
DataType.BYTE, DataType.UBYTE -> {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||
}
|
||||
|
@ -1756,6 +1969,14 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
}
|
||||
DataType.WORD -> {
|
||||
when(targetDt) {
|
||||
DataType.BOOL -> {
|
||||
asmgen.out("""
|
||||
lda $sourceAsmVarName
|
||||
ora $sourceAsmVarName+1
|
||||
beq +
|
||||
lda #1
|
||||
+ sta $targetAsmVarName""")
|
||||
}
|
||||
DataType.BYTE, DataType.UBYTE -> {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||
}
|
||||
|
@ -1778,6 +1999,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<$sourceAsmVarName | ldy #>$sourceAsmVarName")
|
||||
when(targetDt) {
|
||||
DataType.BOOL -> asmgen.out(" jsr floats.cast_as_bool_into_a | sta $targetAsmVarName")
|
||||
DataType.UBYTE -> asmgen.out(" jsr floats.cast_as_uw_into_ya | sty $targetAsmVarName")
|
||||
DataType.BYTE -> asmgen.out(" jsr floats.cast_as_w_into_ay | sta $targetAsmVarName")
|
||||
DataType.UWORD -> asmgen.out(" jsr floats.cast_as_uw_into_ya | sty $targetAsmVarName | sta $targetAsmVarName+1")
|
||||
|
@ -1798,8 +2020,21 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
|
||||
// also see: PtExpressionAsmGen, fun translateExpression(typecast: PtTypeCast)
|
||||
when(sourceDt) {
|
||||
DataType.BOOL -> {
|
||||
when (targetDt) {
|
||||
in ByteDatatypesWithBoolean -> asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
|
||||
else -> throw AssemblyError("assign bool to non-byte variable")
|
||||
}
|
||||
}
|
||||
DataType.UBYTE -> {
|
||||
when(targetDt) {
|
||||
DataType.BOOL -> {
|
||||
asmgen.out("""
|
||||
cp${regs.toString().lowercase()} #0
|
||||
beq +
|
||||
ld${regs.toString().lowercase()} #1
|
||||
+ st${regs.toString().lowercase()} $targetAsmVarName""")
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
|
||||
}
|
||||
|
@ -1830,6 +2065,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
}
|
||||
DataType.BYTE -> {
|
||||
when(targetDt) {
|
||||
DataType.BOOL -> TODO("assign byte to bool")
|
||||
DataType.UBYTE -> {
|
||||
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
|
||||
}
|
||||
|
@ -1874,6 +2110,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
}
|
||||
DataType.UWORD -> {
|
||||
when(targetDt) {
|
||||
DataType.BOOL -> TODO("assign uword to bool")
|
||||
DataType.BYTE, DataType.UBYTE -> {
|
||||
asmgen.out(" st${regs.toString().lowercase().first()} $targetAsmVarName")
|
||||
}
|
||||
|
@ -1902,6 +2139,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
}
|
||||
DataType.WORD -> {
|
||||
when(targetDt) {
|
||||
DataType.BOOL -> TODO("assign word to bool")
|
||||
DataType.BYTE, DataType.UBYTE -> {
|
||||
asmgen.out(" st${regs.toString().lowercase().first()} $targetAsmVarName")
|
||||
}
|
||||
|
@ -2001,7 +2239,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
throw AssemblyError("can't store word into memory byte")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
||||
asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
}
|
||||
TargetStorageKind.REGISTER -> {
|
||||
|
@ -2059,10 +2297,10 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return
|
||||
}
|
||||
require(sourceDt in WordDatatypes || sourceDt==DataType.UBYTE)
|
||||
require(sourceDt in WordDatatypes || sourceDt==DataType.UBYTE || sourceDt==DataType.BOOL) { "weird source dt for word variable" }
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
if(sourceDt==DataType.UBYTE) {
|
||||
if(sourceDt==DataType.UBYTE || sourceDt==DataType.BOOL) {
|
||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname}")
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz ${target.asmVarname}+1")
|
||||
|
@ -2710,7 +2948,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
if(indexVar!=null) {
|
||||
asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta ${target.asmVarname},y")
|
||||
} else {
|
||||
require(target.array.index.type in ByteDatatypes)
|
||||
require(target.array.index.type in ByteDatatypesWithBoolean)
|
||||
asmgen.saveRegisterStack(register, false)
|
||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
|
||||
asmgen.out(" pla | sta ${target.asmVarname},y")
|
||||
|
@ -3386,12 +3624,13 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
internal fun inplaceInvert(assign: AsmAssignment, scope: IPtSubroutine?) {
|
||||
val target = assign.target
|
||||
when (assign.target.datatype) {
|
||||
DataType.UBYTE -> {
|
||||
DataType.UBYTE, DataType.BOOL -> {
|
||||
val eorValue = if(assign.target.datatype==DataType.BOOL) 1 else 255
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out("""
|
||||
lda ${target.asmVarname}
|
||||
eor #255
|
||||
eor #$eorValue
|
||||
sta ${target.asmVarname}""")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
|
@ -3401,12 +3640,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
val addr = (memory.address as PtNumber).number.toHex()
|
||||
asmgen.out("""
|
||||
lda $addr
|
||||
eor #255
|
||||
eor #$eorValue
|
||||
sta $addr""")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
asmgen.loadByteFromPointerIntoA(memory.address as PtIdentifier)
|
||||
asmgen.out(" eor #255")
|
||||
asmgen.out(" eor #$eorValue")
|
||||
asmgen.storeAIntoPointerVar(memory.address as PtIdentifier)
|
||||
}
|
||||
else -> {
|
||||
|
@ -3414,12 +3653,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
if(asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||
asmgen.out("""
|
||||
lda (P8ZP_SCRATCH_W2)
|
||||
eor #255""")
|
||||
eor #$eorValue""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
eor #255""")
|
||||
eor #$eorValue""")
|
||||
}
|
||||
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2", false)
|
||||
}
|
||||
|
@ -3427,13 +3666,16 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||
}
|
||||
TargetStorageKind.REGISTER -> {
|
||||
when(target.register!!) {
|
||||
RegisterOrPair.A -> asmgen.out(" eor #255")
|
||||
RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax")
|
||||
RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay")
|
||||
RegisterOrPair.A -> asmgen.out(" eor #$eorValue")
|
||||
RegisterOrPair.X -> asmgen.out(" txa | eor #$eorValue | tax")
|
||||
RegisterOrPair.Y -> asmgen.out(" tya | eor #$eorValue | tay")
|
||||
else -> throw AssemblyError("invalid reg dt for byte invert")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign), scope)
|
||||
TargetStorageKind.ARRAY -> {
|
||||
val invertOperator = if(assign.target.datatype==DataType.BOOL) "not" else "~"
|
||||
assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign(invertOperator, assign), scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
|
|
|
@ -18,13 +18,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||
val a2 = AsmAssignment(assign.source, assign.target, assign.memsizer, assign.position)
|
||||
assignmentAsmGen.inplaceNegate(a2, false, scope)
|
||||
}
|
||||
"~" -> {
|
||||
"~", "not" -> {
|
||||
val a2 = AsmAssignment(assign.source, assign.target, assign.memsizer, assign.position)
|
||||
assignmentAsmGen.inplaceInvert(a2, scope)
|
||||
}
|
||||
"+" -> { /* is a nop */ }
|
||||
else -> {
|
||||
require(assign.operator in ComparisonOperators || assign.operator.length>=2) { "invalid aug assign operator ${assign.operator}" }
|
||||
augmentedAssignExpr(assign)
|
||||
}
|
||||
}
|
||||
|
@ -67,8 +66,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
when (target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
in ByteDatatypesWithBoolean -> {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteVariableWithLiteralval(target.asmVarname, target.datatype, operator, value.boolean!!.asInt())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationByteVariableWithLiteralval(target.asmVarname, target.datatype, operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationByteVariableWithVariable(target.asmVarname, target.datatype, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationByteVariableWithVariable(target.asmVarname, target.datatype, operator, regName(value))
|
||||
|
@ -87,6 +87,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||
in WordDatatypes -> {
|
||||
val block = target.origAstTarget?.definingBlock()
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationWordWithLiteralval(target.asmVarname, target.datatype, operator, value.boolean!!.asInt(), block)
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationWordWithLiteralval(target.asmVarname, target.datatype, operator, value.number!!.number.toInt(), block)
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationWordWithVariable(target.asmVarname, target.datatype, operator, value.asmVarname, value.datatype, block)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationWordWithVariable(target.asmVarname, target.datatype, operator, regName(value), value.datatype, block)
|
||||
|
@ -105,6 +106,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||
}
|
||||
DataType.FLOAT -> {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationFloatWithLiteralval(target.asmVarname, operator, value.boolean!!.asInt().toDouble())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(target.asmVarname, operator, value.number!!.number)
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(target.asmVarname, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(target.asmVarname, operator, regName(value))
|
||||
|
@ -129,6 +131,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||
is PtNumber -> {
|
||||
val addr = (memory.address as PtNumber).number.toInt()
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteVariableWithLiteralval(addr.toHex(), DataType.UBYTE, operator, value.boolean!!.asInt())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationByteVariableWithLiteralval(addr.toHex(), DataType.UBYTE, operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationByteVariableWithVariable(addr.toHex(), DataType.UBYTE, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationByteVariableWithVariable(addr.toHex(), DataType.UBYTE, operator, regName(value))
|
||||
|
@ -147,6 +150,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||
is PtIdentifier -> {
|
||||
val pointer = memory.address as PtIdentifier
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationBytePointerWithLiteralval(pointer, operator, value.boolean!!.asInt())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationBytePointerWithLiteralval(pointer, operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationBytePointerWithVariable(pointer, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationBytePointerWithVariable(pointer, operator, regName(value))
|
||||
|
@ -170,6 +174,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||
asmgen.saveRegisterStack(CpuRegister.Y, true)
|
||||
asmgen.out(" jsr prog8_lib.read_byte_from_address_in_AY_into_A")
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> {
|
||||
inplacemodificationRegisterAwithVariable(operator, "#${value.boolean!!.asInt()}", false)
|
||||
asmgen.out(" tax")
|
||||
}
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
inplacemodificationRegisterAwithVariable(operator, "#${value.number!!.number.toInt()}", false)
|
||||
asmgen.out(" tax")
|
||||
|
@ -229,8 +237,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||
// normal array
|
||||
val targetVarName = "${target.asmVarname} + ${index*program.memsizer.memorySize(target.datatype)}"
|
||||
when (target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
in ByteDatatypesWithBoolean -> {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteVariableWithLiteralval(targetVarName, target.datatype, operator, value.boolean!!.asInt())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationByteVariableWithLiteralval(targetVarName, target.datatype, operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationByteVariableWithVariable(targetVarName, target.datatype, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationByteVariableWithVariable(targetVarName, target.datatype, operator, regName(value))
|
||||
|
@ -250,6 +259,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||
in WordDatatypes -> {
|
||||
val block = target.origAstTarget?.definingBlock()
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationWordWithLiteralval(targetVarName, target.datatype, operator, value.boolean!!.asInt(), block)
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationWordWithLiteralval(targetVarName, target.datatype, operator, value.number!!.number.toInt(), block)
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationWordWithVariable(targetVarName, target.datatype, operator, value.asmVarname, value.datatype, block)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationWordWithVariable(targetVarName, target.datatype, operator, regName(value), value.datatype, block)
|
||||
|
@ -268,6 +278,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||
|
||||
DataType.FLOAT -> {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationFloatWithLiteralval(targetVarName, operator, value.boolean!!.asInt().toDouble())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(targetVarName, operator, value.number!!.number)
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(targetVarName, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(targetVarName, operator, regName(value))
|
||||
|
@ -295,7 +306,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||
return
|
||||
|
||||
when (target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
in ByteDatatypesWithBoolean -> {
|
||||
if(value.kind==SourceStorageKind.EXPRESSION
|
||||
&& value.expression is PtTypeCast
|
||||
&& tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
|
||||
|
@ -304,6 +315,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.out(" lda ${target.array.variable.name},y")
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> {
|
||||
inplacemodificationRegisterAwithVariable(operator, "#${value.boolean!!.asInt()}", target.datatype in SignedDatatypes)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, true)
|
||||
}
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
inplacemodificationRegisterAwithVariable(operator, "#${value.number!!.number.toInt()}", target.datatype in SignedDatatypes)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, true)
|
||||
|
@ -363,6 +378,14 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||
}
|
||||
val block = target.origAstTarget?.definingBlock()
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> {
|
||||
val number = value.boolean!!.asInt()
|
||||
if(!inplacemodificationRegisterAXwithLiteralval(operator, number)) {
|
||||
asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1")
|
||||
inplacemodificationWordWithLiteralval("P8ZP_SCRATCH_W1", target.datatype, operator, number, block)
|
||||
asmgen.out(" lda P8ZP_SCRATCH_W1 | ldx P8ZP_SCRATCH_W1+1")
|
||||
}
|
||||
}
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(!inplacemodificationRegisterAXwithLiteralval(operator, number)) {
|
||||
|
@ -446,6 +469,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||
|
||||
// calculate on tempvar
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationFloatWithLiteralval(tempvar, operator, value.boolean!!.asInt().toDouble())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(tempvar, operator, value.number!!.number)
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(tempvar, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(tempvar, operator, regName(value))
|
||||
|
@ -953,7 +977,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
// pretty uncommon, who's going to assign a comparison boolean expresion to a pointer?
|
||||
// pretty uncommon, who's going to assign a comparison boolean expression to a pointer?
|
||||
"<", "<=", ">", ">=" -> TODO("byte-var-to-pointer comparisons")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
|
@ -1058,34 +1082,38 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||
+""")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
// pretty uncommon, who's going to assign a comparison boolean expresion to a pointer?:
|
||||
// pretty uncommon, who's going to assign a comparison boolean expression to a pointer?:
|
||||
"<", "<=", ">", ">=" -> TODO("byte-litval-to-pointer comparisons")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplacemodificationByteVariableWithValue(name: String, dt: DataType, operator: String, value: PtExpression) {
|
||||
val shortcutLabel = asmgen.makeLabel("shortcut")
|
||||
when (operator) {
|
||||
"and" -> {
|
||||
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
|
||||
asmgen.out(" lda $name | beq $shortcutLabel")
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
|
||||
asmgen.out("""
|
||||
and $name
|
||||
sta $name
|
||||
if(!value.isSimple()) {
|
||||
// attempt short-circuit (McCarthy) evaluation
|
||||
when (operator) {
|
||||
"and" -> {
|
||||
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
|
||||
val shortcutLabel = asmgen.makeLabel("shortcut")
|
||||
asmgen.out(" lda $name | beq $shortcutLabel")
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
|
||||
asmgen.out("""
|
||||
and $name
|
||||
sta $name
|
||||
$shortcutLabel:""")
|
||||
return
|
||||
}
|
||||
"or" -> {
|
||||
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
|
||||
asmgen.out(" lda $name | bne $shortcutLabel")
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
|
||||
asmgen.out("""
|
||||
ora $name
|
||||
sta $name
|
||||
return
|
||||
}
|
||||
"or" -> {
|
||||
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
|
||||
val shortcutLabel = asmgen.makeLabel("shortcut")
|
||||
asmgen.out(" lda $name | bne $shortcutLabel")
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
|
||||
asmgen.out("""
|
||||
ora $name
|
||||
sta $name
|
||||
$shortcutLabel:""")
|
||||
return
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2400,7 +2428,7 @@ $shortcutLabel:""")
|
|||
lda #0
|
||||
sta $name+1""")
|
||||
}
|
||||
// pretty uncommon, who's going to assign a comparison boolean expresion to a word var?:
|
||||
// pretty uncommon, who's going to assign a comparison boolean expression to a word var?:
|
||||
"<", "<=", ">", ">=" -> TODO("word-bytevar-to-var comparisons")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
|
@ -2827,7 +2855,7 @@ $shortcutLabel:""")
|
|||
lda #0
|
||||
sta $name+1""")
|
||||
}
|
||||
// pretty uncommon, who's going to assign a comparison boolean expresion to a word var?:
|
||||
// pretty uncommon, who's going to assign a comparison boolean expression to a word var?:
|
||||
"<", "<=", ">", ">=" -> TODO("word-bytevalue-to-var comparisons")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
|
@ -2907,7 +2935,7 @@ $shortcutLabel:""")
|
|||
lda #0
|
||||
sta $name+1""")
|
||||
}
|
||||
// pretty uncommon, who's going to assign a comparison boolean expresion to a word var?:
|
||||
// pretty uncommon, who's going to assign a comparison boolean expression to a word var?:
|
||||
"<", "<=", ">", ">=" -> TODO("word-value-to-var comparisons")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
|
@ -2947,7 +2975,7 @@ $shortcutLabel:""")
|
|||
jsr floats.FDIV
|
||||
""")
|
||||
}
|
||||
// pretty uncommon, who's going to assign a comparison boolean expresion to a float var:
|
||||
// pretty uncommon, who's going to assign a comparison boolean expression to a float var:
|
||||
"==" -> TODO("float-value-to-var comparison ==")
|
||||
"!=" -> TODO("float-value-to-var comparison !=")
|
||||
"<", "<=", ">", ">=" -> TODO("float-value-to-var comparisons")
|
||||
|
@ -3003,7 +3031,7 @@ $shortcutLabel:""")
|
|||
jsr floats.FDIV
|
||||
""")
|
||||
}
|
||||
// pretty uncommon, who's going to assign a comparison boolean expresion to a float var:
|
||||
// pretty uncommon, who's going to assign a comparison boolean expression to a float var:
|
||||
"==" -> {
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
|
|
|
@ -5,7 +5,7 @@ import prog8.code.core.*
|
|||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType) = when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in ByteDatatypesWithBoolean -> 1
|
||||
DataType.FLOAT -> 5
|
||||
else -> 2
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
|||
}
|
||||
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
package prog8.codegen.intermediate
|
||||
|
||||
import com.github.michaelbull.result.Ok
|
||||
import com.github.michaelbull.result.Result
|
||||
import com.github.michaelbull.result.getOrElse
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.intermediate.*
|
||||
|
@ -31,31 +28,31 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||
val array = target.array
|
||||
val value = augAssign.value
|
||||
val signed = target.type in SignedDatatypes
|
||||
val result: Result<IRCodeChunks, NotImplementedError> = when(augAssign.operator) {
|
||||
"+=" -> expressionEval.operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"-=" -> expressionEval.operatorMinusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"*=" -> expressionEval.operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"/=" -> expressionEval.operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"|=" -> expressionEval.operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"or=" -> expressionEval.operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"&=" -> expressionEval.operatorAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"and=" -> expressionEval.operatorLogicalAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"^=", "xor=" -> expressionEval.operatorXorInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"<<=" -> expressionEval.operatorShiftLeftInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
">>=" -> expressionEval.operatorShiftRightInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"%=" -> expressionEval.operatorModuloInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"==" -> expressionEval.operatorEqualsInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"!=" -> expressionEval.operatorNotEqualsInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"<" -> expressionEval.operatorLessInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
">" -> expressionEval.operatorGreaterInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"<=" -> expressionEval.operatorLessEqualInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
">=" -> expressionEval.operatorGreaterEqualInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
val result = when(augAssign.operator) {
|
||||
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"-=" -> operatorMinusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"*=" -> operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"/=" -> operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"|=" -> operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"or=" -> operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"&=" -> operatorAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"and=" -> operatorLogicalAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"^=", "xor=" -> operatorXorInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"<<=" -> operatorShiftLeftInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
">>=" -> operatorShiftRightInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"%=" -> operatorModuloInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"==" -> operatorEqualsInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"!=" -> operatorNotEqualsInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"<" -> operatorLessInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
">" -> operatorGreaterInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"<=" -> operatorLessEqualInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
">=" -> operatorGreaterEqualInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
in PrefixOperators -> inplacePrefix(augAssign.operator, symbol, array, constAddress, memTarget, targetDt)
|
||||
|
||||
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
|
||||
}
|
||||
|
||||
val chunks = result.getOrElse { fallbackAssign(augAssign) }
|
||||
val chunks = if(result!=null) result else fallbackAssign(augAssign)
|
||||
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(augAssign.position)
|
||||
return chunks
|
||||
}
|
||||
|
@ -84,12 +81,12 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||
return translateRegularAssign(normalAssign)
|
||||
}
|
||||
|
||||
private fun inplacePrefix(operator: String, symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType): Result<IRCodeChunks, NotImplementedError> {
|
||||
private fun inplacePrefix(operator: String, symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType): IRCodeChunks? {
|
||||
if(operator=="+")
|
||||
return Ok(emptyList())
|
||||
return emptyList()
|
||||
|
||||
if(array!=null)
|
||||
return Ok(inplacePrefixArray(operator, array))
|
||||
return inplacePrefixArray(operator, array)
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(constAddress==null && memory!=null) {
|
||||
|
@ -116,11 +113,17 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||
when (operator) {
|
||||
"-" -> addInstr(result, IRInstruction(Opcode.NEGM, vmDt, address = constAddress, labelSymbol = symbol), null)
|
||||
"~" -> addInstr(result, IRInstruction(Opcode.INVM, vmDt, address = constAddress, labelSymbol = symbol), null)
|
||||
// TODO: in boolean branch, how is 'not' handled here?
|
||||
"not" -> {
|
||||
val regMask = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, immediate = 1)
|
||||
it += IRInstruction(Opcode.XORM, vmDt, reg1=regMask, address = constAddress, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird prefix operator")
|
||||
}
|
||||
}
|
||||
return Ok(result)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun inplacePrefixArray(operator: String, array: PtArrayIndexer): IRCodeChunks {
|
||||
|
@ -450,4 +453,905 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||
addToResult(result, tr, tr.resultReg, -1)
|
||||
return Pair(result, tr.resultReg)
|
||||
}
|
||||
|
||||
private fun operatorAndInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constIndex = array.index.asConstInteger()
|
||||
val constValue = operand.asConstInteger()
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type)
|
||||
if(constIndex!=null && constValue!=null) {
|
||||
if(array.splitWords) {
|
||||
val valueRegLsb = codeGen.registers.nextFree()
|
||||
val valueRegMsb = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegLsb, immediate=constValue and 255)
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegMsb, immediate=constValue shr 8)
|
||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
}
|
||||
} else {
|
||||
val valueReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
return null // TODO("inplace word array &")
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place &")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol),null)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun operatorLogicalAndInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constIndex = array.index.asConstInteger()
|
||||
val constValue = operand.asConstInteger()
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type)
|
||||
if(constIndex!=null && constValue!=null) {
|
||||
if(array.splitWords) {
|
||||
val valueRegLsb = codeGen.registers.nextFree()
|
||||
val valueRegMsb = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegLsb, immediate=constValue and 255)
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegMsb, immediate=constValue shr 8)
|
||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
}
|
||||
} else {
|
||||
val valueReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
||||
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
return null // TODO("inplace word array and")
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place and")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
if(!operand.isSimple()) {
|
||||
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
|
||||
val inplaceReg = codeGen.registers.nextFree()
|
||||
val shortcutLabel = codeGen.createLabelName()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += if(constAddress!=null)
|
||||
IRInstruction(Opcode.LOADM, vmDt, reg1=inplaceReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.LOADM, vmDt, reg1=inplaceReg, labelSymbol = symbol)
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = shortcutLabel)
|
||||
}
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.STOREM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.STOREM, vmDt, reg1=tr.resultReg, labelSymbol = symbol), null)
|
||||
result += IRCodeChunk(shortcutLabel, null)
|
||||
} else {
|
||||
// normal evaluation, it is *likely* shorter and faster because of the simple operands.
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol),null)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun operatorOrInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constIndex = array.index.asConstInteger()
|
||||
val constValue = operand.asConstInteger()
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type)
|
||||
if(constIndex!=null && constValue!=null) {
|
||||
if(array.splitWords) {
|
||||
val valueRegLsb = codeGen.registers.nextFree()
|
||||
val valueRegMsb = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegLsb, immediate=constValue and 255)
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegMsb, immediate=constValue shr 8)
|
||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
}
|
||||
} else {
|
||||
val valueReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
return null // TODO("inplace word array |")
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place |")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, labelSymbol = symbol), null)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun operatorLogicalOrInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constIndex = array.index.asConstInteger()
|
||||
val constValue = operand.asConstInteger()
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type)
|
||||
if(constIndex!=null && constValue!=null) {
|
||||
if(array.splitWords) {
|
||||
val valueRegLsb = codeGen.registers.nextFree()
|
||||
val valueRegMsb = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegLsb, immediate=constValue and 255)
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegMsb, immediate=constValue shr 8)
|
||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
}
|
||||
} else {
|
||||
val valueReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
||||
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
return null // TODO("inplace word array or")
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place or"")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
if(!operand.isSimple()) {
|
||||
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
|
||||
val inplaceReg = codeGen.registers.nextFree()
|
||||
val shortcutLabel = codeGen.createLabelName()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += if(constAddress!=null)
|
||||
IRInstruction(Opcode.LOADM, vmDt, reg1=inplaceReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.LOADM, vmDt, reg1=inplaceReg, labelSymbol = symbol)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = shortcutLabel)
|
||||
}
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.STOREM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.STOREM, vmDt, reg1=tr.resultReg, labelSymbol = symbol), null)
|
||||
result += IRCodeChunk(shortcutLabel, null)
|
||||
} else {
|
||||
// normal evaluation, it is *likely* shorter and faster because of the simple operands.
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, labelSymbol = symbol), null)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun operatorDivideInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
TODO("/ in array")
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place /"")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constFactorRight = operand as? PtNumber
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
|
||||
val factor = constFactorRight.number
|
||||
result += codeGen.divideByConstFloatInplace(constAddress, symbol, factor)
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
val ins = if(signed) {
|
||||
if(constAddress!=null)
|
||||
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
if(constAddress!=null)
|
||||
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress)
|
||||
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, constAddress, symbol, factor, signed)
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ins = if(signed) {
|
||||
if(constAddress!=null)
|
||||
IRInstruction(Opcode.DIVSM, vmDt, reg1 = tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DIVSM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
if(constAddress!=null)
|
||||
IRInstruction(Opcode.DIVM, vmDt, reg1 = tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DIVM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
|
||||
}
|
||||
addInstr(result, ins, null)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun operatorMultiplyInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
TODO("* in array")
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place *"")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constFactorRight = operand as? PtNumber
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
if(constFactorRight!=null) {
|
||||
val factor = constFactorRight.number
|
||||
result += codeGen.multiplyByConstFloatInplace(constAddress, symbol, factor)
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress)
|
||||
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, constAddress, symbol, factor)
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun operatorMinusInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(array.splitWords)
|
||||
return operatorMinusInplaceSplitArray(array, operand)
|
||||
val eltDt = irType(array.type)
|
||||
val constIndex = array.index.asConstInteger()
|
||||
val constValue = operand.asConstInteger()
|
||||
if(constIndex!=null && constValue!=null) {
|
||||
if(constValue==1) {
|
||||
addInstr(result, IRInstruction(Opcode.DECM, eltDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||
} else {
|
||||
val valueReg=codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, eltDt, reg1=valueReg, immediate = constValue)
|
||||
it += IRInstruction(Opcode.SUBM, eltDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
return null // TODO("inplace array -")
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place -"")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.DECM, vmDt, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
} else {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.DECM, vmDt, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun operatorMinusInplaceSplitArray(array: PtArrayIndexer, operand: PtExpression): IRCodeChunks? {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constIndex = array.index.asConstInteger()
|
||||
val constValue = operand.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
val skip = codeGen.createLabelName()
|
||||
if(constValue==1) {
|
||||
val lsbReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lsbReg, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skip)
|
||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
}
|
||||
result += IRCodeChunk(skip, null).also {
|
||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
}
|
||||
return result
|
||||
} else {
|
||||
return null // TODO("inplace split word array +")
|
||||
}
|
||||
}
|
||||
return null // TODO("inplace split word array +")
|
||||
}
|
||||
|
||||
private fun operatorPlusInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(array.splitWords)
|
||||
return operatorPlusInplaceSplitArray(array, operand)
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type)
|
||||
val elementDt = irType(array.type)
|
||||
val constIndex = array.index.asConstInteger()
|
||||
val constValue = operand.asConstInteger()
|
||||
if(constIndex!=null && constValue!=null) {
|
||||
if(constValue==1) {
|
||||
addInstr(result, IRInstruction(Opcode.INCM, elementDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||
} else {
|
||||
val valueReg=codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, elementDt, reg1=valueReg, immediate = constValue)
|
||||
it += IRInstruction(Opcode.ADDM, elementDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
return null // TODO("inplace array +")
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place +"")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.INCM, vmDt, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
} else {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.INCM, vmDt, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun operatorPlusInplaceSplitArray(array: PtArrayIndexer, operand: PtExpression): IRCodeChunks? {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constIndex = array.index.asConstInteger()
|
||||
val constValue = operand.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
val skip = codeGen.createLabelName()
|
||||
if(constValue==1) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skip)
|
||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
}
|
||||
result += IRCodeChunk(skip, null)
|
||||
return result
|
||||
} else {
|
||||
return null // TODO("inplace split word array +")
|
||||
}
|
||||
}
|
||||
return null // TODO("inplace split word array +")
|
||||
}
|
||||
|
||||
private fun operatorShiftRightInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
TODO(">> in array")
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place >>"")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(codeGen.isOne(operand)) {
|
||||
val opc = if (signed) Opcode.ASRM else Opcode.LSRM
|
||||
val ins = if(constAddress!=null)
|
||||
IRInstruction(opc, vmDt, address = constAddress)
|
||||
else
|
||||
IRInstruction(opc, vmDt, labelSymbol = symbol)
|
||||
addInstr(result, ins, null)
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
|
||||
val ins = if(constAddress!=null)
|
||||
IRInstruction(opc, vmDt, reg1 = tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(opc, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
|
||||
addInstr(result, ins, null)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun operatorShiftLeftInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
TODO("<< in array")
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place <<"")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(codeGen.isOne(operand)){
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.LSLM, vmDt, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.LSLM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
,null)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun operatorXorInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constIndex = array.index.asConstInteger()
|
||||
val constValue = operand.asConstInteger()
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type)
|
||||
if(constIndex!=null && constValue!=null) {
|
||||
if(array.splitWords) {
|
||||
val valueRegLsb = codeGen.registers.nextFree()
|
||||
val valueRegMsb = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegLsb, immediate=constValue and 255)
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegMsb, immediate=constValue shr 8)
|
||||
it += IRInstruction(Opcode.XORM, vmDt, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
|
||||
it += IRInstruction(Opcode.XORM, vmDt, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
|
||||
}
|
||||
} else {
|
||||
val valueReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
|
||||
it += IRInstruction(Opcode.XORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
return null // TODO("inplace word array xor")
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place xor"")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
,null)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun operatorModuloInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
TODO("% in array")
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place %"")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
if(operand is PtNumber) {
|
||||
val number = operand.number.toInt()
|
||||
if (constAddress != null) {
|
||||
// @(address) = @(address) %= operand
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultReg, address = constAddress)
|
||||
it += IRInstruction(Opcode.MOD, vmDt, reg1 = resultReg, immediate = number)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, address = constAddress)
|
||||
}
|
||||
} else {
|
||||
// symbol = symbol %= operand
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultReg, labelSymbol = symbol)
|
||||
it += IRInstruction(Opcode.MOD, vmDt, reg1 = resultReg, immediate = number)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
result += tr.chunks
|
||||
if (constAddress != null) {
|
||||
// @(address) = @(address) %= operand
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultReg, address = constAddress)
|
||||
it += IRInstruction(Opcode.MODR, vmDt, reg1 = resultReg, reg2 = tr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, address = constAddress)
|
||||
}
|
||||
} else {
|
||||
// symbol = symbol %= operand
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultReg, labelSymbol = symbol)
|
||||
it += IRInstruction(Opcode.MODR, vmDt, reg1 = resultReg, reg2 = tr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun operatorEqualsInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
if(array!=null)
|
||||
return createInplaceArrayComparison(array, operand, "==")
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place compare"")
|
||||
|
||||
val chunks = if(vmDt==IRDataType.FLOAT) {
|
||||
createInplaceFloatComparison(constAddress, symbol, operand, Opcode.SEQ)
|
||||
} else {
|
||||
createInplaceComparison(constAddress, symbol, vmDt, operand, Opcode.SEQ)
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
|
||||
private fun operatorNotEqualsInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
if(array!=null)
|
||||
return createInplaceArrayComparison(array, operand, "!=")
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place compare"")
|
||||
|
||||
val chunks = if(vmDt==IRDataType.FLOAT) {
|
||||
createInplaceFloatComparison(constAddress, symbol, operand, Opcode.SNE)
|
||||
} else {
|
||||
createInplaceComparison(constAddress, symbol, vmDt, operand, Opcode.SNE)
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
|
||||
private fun operatorGreaterInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
||||
if(array!=null)
|
||||
return createInplaceArrayComparison(array, operand, ">")
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place compare"")
|
||||
|
||||
val opcode = if(signed) Opcode.SGTS else Opcode.SGT
|
||||
val chunks = if(vmDt==IRDataType.FLOAT) {
|
||||
createInplaceFloatComparison(constAddress, symbol, operand, opcode)
|
||||
} else {
|
||||
createInplaceComparison(constAddress, symbol, vmDt, operand, opcode)
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
|
||||
private fun operatorLessInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
||||
if(array!=null)
|
||||
return createInplaceArrayComparison(array, operand, "<")
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place compare"")
|
||||
|
||||
val opcode = if(signed) Opcode.SLTS else Opcode.SLT
|
||||
val chunks = if(vmDt==IRDataType.FLOAT) {
|
||||
createInplaceFloatComparison(constAddress, symbol, operand, opcode)
|
||||
} else {
|
||||
createInplaceComparison(constAddress, symbol, vmDt, operand, opcode)
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
|
||||
private fun operatorGreaterEqualInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
||||
if(array!=null)
|
||||
return createInplaceArrayComparison(array, operand, ">=")
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place compare"")
|
||||
|
||||
val opcode = if(signed) Opcode.SGES else Opcode.SGE
|
||||
val chunks = if(vmDt==IRDataType.FLOAT) {
|
||||
createInplaceFloatComparison(constAddress, symbol, operand, opcode)
|
||||
} else {
|
||||
createInplaceComparison(constAddress, symbol, vmDt, operand, opcode)
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
|
||||
private fun operatorLessEqualInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
||||
if(array!=null)
|
||||
return createInplaceArrayComparison(array, operand, "<=")
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place compare"")
|
||||
|
||||
val opcode = if(signed) Opcode.SLES else Opcode.SLE
|
||||
val chunks = if(vmDt==IRDataType.FLOAT) {
|
||||
createInplaceFloatComparison(constAddress, symbol, operand, opcode)
|
||||
} else {
|
||||
createInplaceComparison(constAddress, symbol, vmDt, operand, opcode)
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
|
||||
private fun createInplaceComparison(
|
||||
constAddress: Int?,
|
||||
symbol: String?,
|
||||
vmDt: IRDataType,
|
||||
operand: PtExpression,
|
||||
compareAndSetOpcode: Opcode
|
||||
): MutableList<IRCodeChunkBase> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val valueReg = codeGen.registers.nextFree()
|
||||
val cmpResultReg = codeGen.registers.nextFree()
|
||||
if(operand is PtNumber || operand is PtBool) {
|
||||
|
||||
if(operand is PtNumber && operand.number==0.0 && compareAndSetOpcode in arrayOf(Opcode.SEQ, Opcode.SNE)) {
|
||||
// ==0 or !=0 optimized case
|
||||
val compareAndSetOpcodeZero = if(compareAndSetOpcode==Opcode.SEQ) Opcode.SZ else Opcode.SNZ
|
||||
if (constAddress != null) {
|
||||
// in-place modify a memory location
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = constAddress)
|
||||
it += IRInstruction(compareAndSetOpcodeZero, vmDt, reg1 = cmpResultReg, reg2 = valueReg)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = constAddress)
|
||||
}
|
||||
} else {
|
||||
// in-place modify a symbol (variable)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
|
||||
it += IRInstruction(compareAndSetOpcodeZero, vmDt, reg1=cmpResultReg, reg2 = valueReg)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// compare against number that is not 0
|
||||
val numberReg = codeGen.registers.nextFree()
|
||||
val value = if(operand is PtNumber) operand.number.toInt() else if(operand is PtBool) operand.asInt() else throw AssemblyError("wrong operand type")
|
||||
if (constAddress != null) {
|
||||
// in-place modify a memory location
|
||||
val innervalue = if(operand is PtNumber) operand.number.toInt() else if(operand is PtBool) operand.asInt() else throw AssemblyError("wrong operand type")
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = constAddress)
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = innervalue)
|
||||
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = cmpResultReg, reg2 = valueReg, reg3 = numberReg)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = constAddress)
|
||||
}
|
||||
} else {
|
||||
// in-place modify a symbol (variable)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = value)
|
||||
it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = numberReg)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
if (constAddress != null) {
|
||||
// in-place modify a memory location
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = constAddress)
|
||||
it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = tr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = constAddress)
|
||||
}
|
||||
} else {
|
||||
// in-place modify a symbol (variable)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
|
||||
it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = tr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun createInplaceFloatComparison(
|
||||
constAddress: Int?,
|
||||
symbol: String?,
|
||||
operand: PtExpression,
|
||||
compareAndSetOpcode: Opcode
|
||||
): MutableList<IRCodeChunkBase> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val valueReg = codeGen.registers.nextFreeFloat()
|
||||
val cmpReg = codeGen.registers.nextFree()
|
||||
val zeroReg = codeGen.registers.nextFree()
|
||||
if(operand is PtNumber) {
|
||||
val numberReg = codeGen.registers.nextFreeFloat()
|
||||
val cmpResultReg = codeGen.registers.nextFree()
|
||||
if (constAddress != null) {
|
||||
// in-place modify a memory location
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = constAddress)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number)
|
||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
|
||||
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
|
||||
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = constAddress)
|
||||
}
|
||||
} else {
|
||||
// in-place modify a symbol (variable)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number)
|
||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
|
||||
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
|
||||
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
val cmpResultReg = codeGen.registers.nextFree()
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
if (constAddress != null) {
|
||||
// in-place modify a memory location
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = constAddress)
|
||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
|
||||
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
|
||||
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = constAddress)
|
||||
}
|
||||
} else {
|
||||
// in-place modify a symbol (variable)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
|
||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
|
||||
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
|
||||
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun createInplaceArrayComparison(array: PtArrayIndexer, value: PtExpression, comparisonOperator: String): IRCodeChunks? {
|
||||
if(array.type==DataType.FLOAT)
|
||||
return null // TODO("optimized in-place compare on float arrays")) // TODO?
|
||||
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(array.splitWords)
|
||||
TODO("inplace compare for split word array")
|
||||
val vmDt = irType(array.type)
|
||||
val constIndex = array.index.asConstInteger()
|
||||
val constValue = value.asConstInteger()
|
||||
val cmpResultReg = codeGen.registers.nextFree()
|
||||
if(constIndex!=null) {
|
||||
if(constValue==0) {
|
||||
// comparison against zero.
|
||||
val valueReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
when(comparisonOperator) {
|
||||
"==" -> it += IRInstruction(Opcode.SZ, vmDt, reg1=cmpResultReg, reg2=valueReg)
|
||||
"!=" -> it += IRInstruction(Opcode.SNZ, vmDt, reg1=cmpResultReg, reg2=valueReg)
|
||||
"<" -> return null // TODO("array <0 inplace")) // TODO?
|
||||
"<=" -> return null // TODO("array <=0 inplace")) // TODO?
|
||||
">" -> return null // TODO("array >0 inplace")) // TODO?
|
||||
">=" -> return null // TODO("array >=0 inplace")) // TODO?
|
||||
else -> throw AssemblyError("invalid operator")
|
||||
}
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
return result
|
||||
} else {
|
||||
return null // TODO("compare against non-zero value"))
|
||||
}
|
||||
} else {
|
||||
if(constValue==0) {
|
||||
// comparison against zero.
|
||||
val valueReg = codeGen.registers.nextFree()
|
||||
val indexTr = expressionEval.translateExpression(array.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1=valueReg, reg2=indexTr.resultReg, labelSymbol = array.variable.name)
|
||||
when(comparisonOperator) {
|
||||
"==" -> it += IRInstruction(Opcode.SZ, vmDt, reg1=cmpResultReg, reg2=valueReg)
|
||||
"!=" -> it += IRInstruction(Opcode.SNZ, vmDt, reg1=cmpResultReg, reg2=valueReg)
|
||||
"<" -> return null // TODO("array <0 inplace")) // TODO?
|
||||
"<=" -> return null // TODO("array <=0 inplace")) // TODO?
|
||||
">" -> return null // TODO("array >0 inplace")) // TODO?
|
||||
">=" -> return null // TODO("array >=0 inplace")) // TODO?
|
||||
else -> throw AssemblyError("invalid operator")
|
||||
}
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1=valueReg, reg2=indexTr.resultReg, labelSymbol = array.variable.name)
|
||||
}
|
||||
return result
|
||||
}
|
||||
else
|
||||
return null // TODO("compare against non-zero value"))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -222,6 +222,7 @@ class IRCodeGen(
|
|||
is PtTypeCast,
|
||||
is PtSubroutineParameter,
|
||||
is PtNumber,
|
||||
is PtBool,
|
||||
is PtArray,
|
||||
is PtBlock,
|
||||
is PtString -> throw AssemblyError("should not occur as separate statement node ${node.position}")
|
||||
|
@ -246,8 +247,8 @@ class IRCodeGen(
|
|||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
val goto = branch.trueScope.children.firstOrNull() as? PtJump
|
||||
if(goto is PtJump && branch.falseScope.children.isEmpty()) {
|
||||
// special case the form: if_cc <condition> goto <place>
|
||||
if (goto is PtJump) {
|
||||
// special case the form: if_cc goto <place> (with optional else)
|
||||
val address = goto.address?.toInt()
|
||||
if(address!=null) {
|
||||
val branchIns = when(branch.condition) {
|
||||
|
@ -275,10 +276,11 @@ class IRCodeGen(
|
|||
}
|
||||
addInstr(result, branchIns, null)
|
||||
}
|
||||
if(branch.falseScope.children.isNotEmpty())
|
||||
result += translateNode(branch.falseScope)
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
val elseLabel = createLabelName()
|
||||
// note that the branch opcode used is the opposite as the branch condition, because the generated code jumps to the 'else' part
|
||||
val branchIns = when(branch.condition) {
|
||||
|
@ -869,96 +871,94 @@ class IRCodeGen(
|
|||
}
|
||||
|
||||
private fun translate(ifElse: PtIfElse): IRCodeChunks {
|
||||
val condition = ifElse.condition
|
||||
val goto = ifElse.ifScope.children.firstOrNull() as? PtJump
|
||||
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)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// if X --> meaning: if X!=0
|
||||
val irDt = irType(condition.type)
|
||||
val signed = condition.type in SignedDatatypes
|
||||
return if(goto!=null && ifElse.elseScope.children.isEmpty()) {
|
||||
translateIfFollowedByJustGoto(ifElse, goto, irDt, signed)
|
||||
} else {
|
||||
translateIfElseNonZeroComparison(ifElse, irDt, signed)
|
||||
}
|
||||
}
|
||||
return if(goto!=null && ifElse.elseScope.children.isEmpty()) {
|
||||
translateIfFollowedByJustGoto(ifElse, goto)
|
||||
} else {
|
||||
translateIfElse(ifElse)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun translateIfFollowedByJustGoto(ifElse: PtIfElse, goto: PtJump, irDtLeft: IRDataType, signed: Boolean): MutableList<IRCodeChunkBase> {
|
||||
if(isIndirectJump(goto))
|
||||
TODO("indirect jump after if ${ifElse.position}")
|
||||
|
||||
private fun translateIfFollowedByJustGoto(ifElse: PtIfElse, goto: PtJump): MutableList<IRCodeChunkBase> {
|
||||
val condition = ifElse.condition as? PtBinaryExpression
|
||||
if(condition==null || condition.left.type!=DataType.FLOAT) {
|
||||
return if(isIndirectJump(goto))
|
||||
ifWithOnlyIndirectJump_IntegerCond(ifElse, goto)
|
||||
else
|
||||
ifWithOnlyNormalJump_IntegerCond(ifElse, goto)
|
||||
}
|
||||
|
||||
// we assume only a binary expression can contain a floating point.
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(condition==null) {
|
||||
if(irDtLeft==IRDataType.FLOAT)
|
||||
throw AssemblyError("condition value should not be float")
|
||||
ifNonZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
|
||||
return result
|
||||
} else {
|
||||
if (irDtLeft == IRDataType.FLOAT) {
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||
val rightTr = expressionEval.translateExpression(condition.right)
|
||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val compResultReg = registers.nextFree()
|
||||
it += IRInstruction(
|
||||
Opcode.FCOMP,
|
||||
IRDataType.FLOAT,
|
||||
reg1 = compResultReg,
|
||||
fpReg1 = leftTr.resultFpReg,
|
||||
fpReg2 = rightTr.resultFpReg
|
||||
)
|
||||
when(condition.operator) {
|
||||
"==" -> {
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = compResultReg, immediate = 0)
|
||||
it += branchInstr(goto, Opcode.BSTEQ)
|
||||
}
|
||||
"!=" -> {
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = compResultReg, immediate = 0)
|
||||
it += branchInstr(goto, Opcode.BSTNE)
|
||||
}
|
||||
else -> {
|
||||
val gotoOpcode = when (condition.operator) {
|
||||
"<" -> Opcode.BLTS
|
||||
">" -> Opcode.BGTS
|
||||
"<=" -> Opcode.BLES
|
||||
">=" -> Opcode.BGES
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
it += if (goto.address != null)
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, address = goto.address?.toInt())
|
||||
else
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = goto.identifier!!.name)
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||
val rightTr = expressionEval.translateExpression(condition.right)
|
||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||
var afterIfLabel = ""
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val compResultReg = registers.nextFree()
|
||||
it += IRInstruction(
|
||||
Opcode.FCOMP,
|
||||
IRDataType.FLOAT,
|
||||
reg1 = compResultReg,
|
||||
fpReg1 = leftTr.resultFpReg,
|
||||
fpReg2 = rightTr.resultFpReg
|
||||
)
|
||||
|
||||
if(isIndirectJump(goto)) {
|
||||
// indirect jump to target so the if has to jump past it instead
|
||||
afterIfLabel = createLabelName()
|
||||
when(condition.operator) {
|
||||
"==" -> {
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = compResultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = afterIfLabel)
|
||||
}
|
||||
"!=" -> {
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = compResultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = afterIfLabel)
|
||||
}
|
||||
else -> {
|
||||
val gotoOpcode = when (condition.operator) {
|
||||
"<" -> Opcode.BGES
|
||||
">" -> Opcode.BLES
|
||||
"<=" -> Opcode.BGTS
|
||||
">=" -> Opcode.BLTS
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
it += IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = afterIfLabel)
|
||||
}
|
||||
}
|
||||
return result
|
||||
it += IRInstruction(Opcode.JUMPI, labelSymbol = goto.identifier!!.name)
|
||||
} else {
|
||||
val rightConst = condition.right.asConstInteger()
|
||||
if (rightConst == 0)
|
||||
ifZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
|
||||
else {
|
||||
ifNonZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
|
||||
// normal jump, directly to target with branch opcode
|
||||
when(condition.operator) {
|
||||
"==" -> {
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = compResultReg, immediate = 0)
|
||||
it += branchInstr(goto, Opcode.BSTEQ)
|
||||
}
|
||||
"!=" -> {
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = compResultReg, immediate = 0)
|
||||
it += branchInstr(goto, Opcode.BSTNE)
|
||||
}
|
||||
else -> {
|
||||
val gotoOpcode = when (condition.operator) {
|
||||
"<" -> Opcode.BLTS
|
||||
">" -> Opcode.BGTS
|
||||
"<=" -> Opcode.BLES
|
||||
">=" -> Opcode.BGES
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
it += if (goto.address != null)
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, address = goto.address?.toInt())
|
||||
else
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = goto.identifier!!.name)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
if(afterIfLabel.isNotEmpty())
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun branchInstr(goto: PtJump, branchOpcode: Opcode): IRInstruction {
|
||||
|
@ -970,74 +970,164 @@ class IRCodeGen(
|
|||
}
|
||||
}
|
||||
|
||||
private fun ifZeroIntThenJump(
|
||||
result: MutableList<IRCodeChunkBase>,
|
||||
ifElse: PtIfElse,
|
||||
signed: Boolean,
|
||||
irDtLeft: IRDataType,
|
||||
goto: PtJump
|
||||
) {
|
||||
val condition = ifElse.condition as PtBinaryExpression
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val lastInstruction = leftTr.chunks.last().instructions.lastOrNull()
|
||||
val requireCompareZero = lastInstruction?.opcode !in OpcodesThatSetStatusbits
|
||||
when(condition.operator) {
|
||||
"==" -> {
|
||||
if(requireCompareZero)
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, irDtLeft, reg1 = leftTr.resultReg, immediate = 0), null)
|
||||
addInstr(result, branchInstr(goto, Opcode.BSTEQ), null)
|
||||
}
|
||||
"!=" -> {
|
||||
if(requireCompareZero)
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, irDtLeft, reg1 = leftTr.resultReg, immediate = 0), null)
|
||||
addInstr(result, branchInstr(goto, Opcode.BSTNE), null)
|
||||
}
|
||||
else -> {
|
||||
val opcode = when (condition.operator) {
|
||||
"<" -> if (signed) Opcode.BLTS else Opcode.BLT
|
||||
">" -> if (signed) Opcode.BGTS else Opcode.BGT
|
||||
"<=" -> if (signed) Opcode.BLES else Opcode.BLE
|
||||
">=" -> if (signed) Opcode.BGES else Opcode.BGE
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, address = goto.address?.toInt()), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, labelSymbol = goto.identifier!!.name), null)
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun ifWithOnlyIndirectJump_IntegerCond(ifElse: PtIfElse, goto: PtJump): MutableList<IRCodeChunkBase> {
|
||||
// indirect jump to target so the if has to jump past it instead
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val afterIfLabel = createLabelName()
|
||||
val gotoSymbol = goto.identifier!!.name
|
||||
|
||||
fun ifNonZeroIntThenJump_BinExpr(condition: PtBinaryExpression) {
|
||||
if(condition.operator in LogicalOperators) {
|
||||
val trCond = expressionEval.translateExpression(condition)
|
||||
result += trCond.chunks
|
||||
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = afterIfLabel), null)
|
||||
return
|
||||
}
|
||||
|
||||
private fun ifNonZeroIntThenJump(
|
||||
result: MutableList<IRCodeChunkBase>,
|
||||
ifElse: PtIfElse,
|
||||
signed: Boolean,
|
||||
irDtLeft: IRDataType,
|
||||
goto: PtJump
|
||||
) {
|
||||
val condition = ifElse.condition as? PtBinaryExpression
|
||||
if(condition==null) {
|
||||
throw AssemblyError("expected condition")
|
||||
// val tr = expressionEval.translateExpression(ifElse.condition)
|
||||
// result += tr.chunks
|
||||
// result += IRCodeChunk(null, null).also {
|
||||
// it += IRInstruction(Opcode.CMPI, irDtLeft, reg1 = tr.resultReg, immediate = 0) // was redundant CMP most likely
|
||||
// it += branchInstr(goto, Opcode.BSTNE)
|
||||
// }
|
||||
} else {
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
val irDt = leftTr.dt
|
||||
val signed = condition.left.type in SignedDatatypes
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val number = (condition.right as? PtNumber)?.number?.toInt()
|
||||
if(number!=null) {
|
||||
val firstReg = leftTr.resultReg
|
||||
when(condition.operator) {
|
||||
"==" -> {
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, irDtLeft, reg1 = firstReg, immediate = number), null)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, irDt, reg1 = firstReg, immediate = number)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = afterIfLabel)
|
||||
}
|
||||
}
|
||||
"!=" -> {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, irDt, reg1 = firstReg, immediate = number)
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = afterIfLabel)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val opcode = when (condition.operator) {
|
||||
"<" -> if(signed) Opcode.BGES else Opcode.BGE
|
||||
">" -> if(signed) Opcode.BLES else Opcode.BLE
|
||||
"<=" -> if(signed) Opcode.BGTS else Opcode.BGT
|
||||
">=" -> if(signed) Opcode.BLTS else Opcode.BLT
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, labelSymbol = afterIfLabel), null)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
val rightTr = expressionEval.translateExpression(condition.right)
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
val firstReg: Int
|
||||
val secondReg: Int
|
||||
val opcode: Opcode
|
||||
var useCmp = false
|
||||
when (condition.operator) {
|
||||
"==" -> {
|
||||
useCmp = true
|
||||
opcode = Opcode.BSTNE
|
||||
firstReg = leftTr.resultReg
|
||||
secondReg = rightTr.resultReg
|
||||
}
|
||||
"!=" -> {
|
||||
useCmp = true
|
||||
opcode = Opcode.BSTEQ
|
||||
firstReg = leftTr.resultReg
|
||||
secondReg = rightTr.resultReg
|
||||
}
|
||||
"<" -> {
|
||||
opcode = if (signed) Opcode.BGESR else Opcode.BGER
|
||||
firstReg = leftTr.resultReg
|
||||
secondReg = rightTr.resultReg
|
||||
}
|
||||
">" -> {
|
||||
// swapped operands
|
||||
opcode = if (signed) Opcode.BGESR else Opcode.BGER
|
||||
firstReg = rightTr.resultReg
|
||||
secondReg = leftTr.resultReg
|
||||
}
|
||||
"<=" -> {
|
||||
opcode = if (signed) Opcode.BGTSR else Opcode.BGTR
|
||||
firstReg = leftTr.resultReg
|
||||
secondReg = rightTr.resultReg
|
||||
}
|
||||
">=" -> {
|
||||
// swapped operands
|
||||
opcode = if (signed) Opcode.BGTSR else Opcode.BGTR
|
||||
firstReg = rightTr.resultReg
|
||||
secondReg = leftTr.resultReg
|
||||
}
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
|
||||
if(useCmp) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMP, irDt, reg1 = firstReg, reg2 = secondReg)
|
||||
it += IRInstruction(opcode, labelSymbol = afterIfLabel)
|
||||
}
|
||||
} else {
|
||||
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, labelSymbol = afterIfLabel), null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when(val cond = ifElse.condition) {
|
||||
is PtTypeCast -> {
|
||||
require(cond.type==DataType.BOOL && cond.value.type in NumericDatatypes)
|
||||
val tr = expressionEval.translateExpression(cond)
|
||||
result += tr.chunks
|
||||
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = afterIfLabel), null)
|
||||
}
|
||||
is PtIdentifier, is PtArrayIndexer, is PtBuiltinFunctionCall, is PtFunctionCall, is PtContainmentCheck -> {
|
||||
val tr = expressionEval.translateExpression(cond)
|
||||
result += tr.chunks
|
||||
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = afterIfLabel), null)
|
||||
}
|
||||
is PtPrefix -> {
|
||||
require(cond.operator=="not")
|
||||
val tr = expressionEval.translateExpression(cond.value)
|
||||
result += tr.chunks
|
||||
addInstr(result, IRInstruction(Opcode.BSTNE, labelSymbol = afterIfLabel), null)
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
ifNonZeroIntThenJump_BinExpr(cond)
|
||||
}
|
||||
else -> throw AssemblyError("weird if condition ${ifElse.condition}")
|
||||
}
|
||||
|
||||
addInstr(result, IRInstruction(Opcode.JUMPI, labelSymbol = gotoSymbol), null)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun ifWithOnlyNormalJump_IntegerCond(ifElse: PtIfElse, goto: PtJump): MutableList<IRCodeChunkBase> {
|
||||
// normal goto after if, using branch instructions
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
fun ifNonZeroIntThenJump_BinExpr(condition: PtBinaryExpression) {
|
||||
if(condition.operator in LogicalOperators) {
|
||||
val trCond = expressionEval.translateExpression(condition)
|
||||
result += trCond.chunks
|
||||
addInstr(result, branchInstr(goto, Opcode.BSTNE), null)
|
||||
return
|
||||
}
|
||||
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
val irDt = leftTr.dt
|
||||
val signed = condition.left.type in SignedDatatypes
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val number = (condition.right as? PtNumber)?.number?.toInt()
|
||||
if(number!=null) {
|
||||
val firstReg = leftTr.resultReg
|
||||
when(condition.operator) {
|
||||
"==" -> {
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, irDt, reg1 = firstReg, immediate = number), null)
|
||||
addInstr(result, branchInstr(goto, Opcode.BSTEQ), null)
|
||||
}
|
||||
"!=" -> {
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, irDtLeft, reg1 = firstReg, immediate = number), null)
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, irDt, reg1 = firstReg, immediate = number), null)
|
||||
addInstr(result, branchInstr(goto, Opcode.BSTNE), null)
|
||||
}
|
||||
else -> {
|
||||
|
@ -1049,9 +1139,9 @@ class IRCodeGen(
|
|||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, address = goto.address?.toInt()), null)
|
||||
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, address = goto.address?.toInt()), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, labelSymbol = goto.identifier!!.name), null)
|
||||
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, labelSymbol = goto.identifier!!.name), null)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1101,121 +1191,140 @@ class IRCodeGen(
|
|||
|
||||
if(useCmp) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMP, irDtLeft, reg1 = firstReg, reg2 = secondReg)
|
||||
it += IRInstruction(Opcode.CMP, irDt, reg1 = firstReg, reg2 = secondReg)
|
||||
it += branchInstr(goto, opcode)
|
||||
}
|
||||
} else {
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, address = goto.address?.toInt()), null)
|
||||
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, address = goto.address?.toInt()), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null)
|
||||
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when(val cond = ifElse.condition) {
|
||||
is PtTypeCast -> {
|
||||
require(cond.type==DataType.BOOL && cond.value.type in NumericDatatypes)
|
||||
val tr = expressionEval.translateExpression(cond)
|
||||
result += tr.chunks
|
||||
addInstr(result, branchInstr(goto, Opcode.BSTNE), null)
|
||||
}
|
||||
is PtIdentifier, is PtArrayIndexer, is PtBuiltinFunctionCall, is PtFunctionCall, is PtContainmentCheck -> {
|
||||
val tr = expressionEval.translateExpression(cond)
|
||||
result += tr.chunks
|
||||
addInstr(result, branchInstr(goto, Opcode.BSTNE), null)
|
||||
}
|
||||
is PtPrefix -> {
|
||||
require(cond.operator=="not")
|
||||
val tr = expressionEval.translateExpression(cond.value)
|
||||
result += tr.chunks
|
||||
addInstr(result, branchInstr(goto, Opcode.BSTEQ), null)
|
||||
}
|
||||
is PtBinaryExpression -> ifNonZeroIntThenJump_BinExpr(cond)
|
||||
else -> throw AssemblyError("weird if condition ${ifElse.condition}")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun translateIfElseZeroComparison(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val elseBranch: Opcode
|
||||
var useCmpi = false // for the branch opcodes that have been converted to CMPI + BSTxx form already
|
||||
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(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, immediateFp = 0.0)
|
||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=compResultReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightFpReg)
|
||||
}
|
||||
when (condition.operator) {
|
||||
"==" -> {
|
||||
elseBranch = Opcode.BSTNE
|
||||
useCmpi = true
|
||||
}
|
||||
"!=" -> {
|
||||
elseBranch = Opcode.BSTEQ
|
||||
useCmpi = true
|
||||
}
|
||||
"<" -> elseBranch = Opcode.BGES
|
||||
">" -> elseBranch = Opcode.BLES
|
||||
"<=" -> elseBranch = Opcode.BGTS
|
||||
">=" -> elseBranch = Opcode.BLTS
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
} else {
|
||||
// integer comparisons
|
||||
branchDt = irDtLeft
|
||||
val tr = expressionEval.translateExpression(condition.left)
|
||||
compResultReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
when (condition.operator) {
|
||||
"==" -> {
|
||||
elseBranch = Opcode.BSTNE
|
||||
useCmpi = true
|
||||
}
|
||||
"!=" -> {
|
||||
elseBranch = Opcode.BSTEQ
|
||||
useCmpi = true
|
||||
}
|
||||
"<" -> elseBranch = if (signed) Opcode.BGES else Opcode.BGE
|
||||
">" -> elseBranch = if (signed) Opcode.BLES else Opcode.BLE
|
||||
"<=" -> elseBranch = if (signed) Opcode.BGTS else Opcode.BGT
|
||||
">=" -> elseBranch = if (signed) Opcode.BLTS else Opcode.BLT
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
private fun translateIfElse(ifElse: PtIfElse): IRCodeChunks {
|
||||
val condition = ifElse.condition as? PtBinaryExpression
|
||||
if(condition==null || condition.left.type != DataType.FLOAT) {
|
||||
return ifWithElse_IntegerCond(ifElse)
|
||||
}
|
||||
|
||||
if(ifElse.elseScope.children.isEmpty()) {
|
||||
// just if
|
||||
val afterIfLabel = createLabelName()
|
||||
if(useCmpi) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, branchDt, reg1=compResultReg, immediate = 0)
|
||||
it += IRInstruction(elseBranch, labelSymbol = afterIfLabel)
|
||||
}
|
||||
} else
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1=compResultReg, immediate = 0, labelSymbol = afterIfLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
} else {
|
||||
// if and else
|
||||
// we assume only a binary expression can contain a floating point.
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||
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: Opcode
|
||||
var useCmpi = false // for the branch opcodes that have been converted to CMPI + BSTxx form already
|
||||
when (condition.operator) {
|
||||
"==" -> {
|
||||
elseBranch = Opcode.BSTNE
|
||||
useCmpi = true
|
||||
}
|
||||
"!=" -> {
|
||||
elseBranch = Opcode.BSTEQ
|
||||
useCmpi = true
|
||||
}
|
||||
"<" -> elseBranch = Opcode.BGES
|
||||
">" -> elseBranch = Opcode.BLES
|
||||
"<=" -> elseBranch = Opcode.BGTS
|
||||
">=" -> elseBranch = Opcode.BLTS
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
|
||||
if (ifElse.hasElse()) {
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
if(useCmpi) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, branchDt, reg1=compResultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = compResultReg, immediate = 0)
|
||||
it += IRInstruction(elseBranch, labelSymbol = elseLabel)
|
||||
}
|
||||
} else
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1=compResultReg, immediate = 0, labelSymbol = elseLabel), null)
|
||||
addInstr(result, IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = elseLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
} else {
|
||||
// only if part
|
||||
val afterIfLabel = createLabelName()
|
||||
if(useCmpi) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = compResultReg, immediate = 0)
|
||||
it += IRInstruction(elseBranch, labelSymbol = afterIfLabel)
|
||||
}
|
||||
} else
|
||||
addInstr(result, IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = afterIfLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun translateIfElseNonZeroComparison(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks {
|
||||
private fun ifWithElse_IntegerCond(ifElse: PtIfElse): List<IRCodeChunkBase> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val elseBranchFirstReg: Int
|
||||
val elseBranchSecondReg: Int
|
||||
val branchDt: IRDataType
|
||||
val condition = ifElse.condition as? PtBinaryExpression
|
||||
if(condition==null) {
|
||||
throw AssemblyError("if-else condition is not a binaryexpression, should have been converted?")
|
||||
} else {
|
||||
if (irDtLeft == IRDataType.FLOAT) {
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||
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)
|
||||
|
||||
fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode) {
|
||||
val tr = expressionEval.translateExpression(condition)
|
||||
result += tr.chunks
|
||||
if(ifElse.hasElse()) {
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(result, IRInstruction(jumpFalseOpcode, labelSymbol = elseLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
} else {
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(result, IRInstruction(jumpFalseOpcode, labelSymbol = afterIfLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
}
|
||||
}
|
||||
|
||||
fun translateBinExpr(condition: PtBinaryExpression) {
|
||||
if(condition.operator in LogicalOperators)
|
||||
return translateSimple(condition, Opcode.BSTEQ)
|
||||
|
||||
val signed = condition.left.type in SignedDatatypes
|
||||
val elseBranchFirstReg: Int
|
||||
val elseBranchSecondReg: Int
|
||||
val number = (condition.right as? PtNumber)?.number?.toInt()
|
||||
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
val branchDt = leftTr.dt
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
if (number!=null) {
|
||||
val elseBranch: Opcode
|
||||
var useCmpi = false // for the branch opcodes that have been converted to CMPI + BSTxx form already
|
||||
when (condition.operator) {
|
||||
|
@ -1227,23 +1336,25 @@ class IRCodeGen(
|
|||
elseBranch = Opcode.BSTEQ
|
||||
useCmpi = true
|
||||
}
|
||||
"<" -> elseBranch = Opcode.BGES
|
||||
">" -> elseBranch = Opcode.BLES
|
||||
"<=" -> elseBranch = Opcode.BGTS
|
||||
">=" -> elseBranch = Opcode.BLTS
|
||||
else -> throw AssemblyError("weird operator")
|
||||
"<" -> elseBranch = if(signed) Opcode.BGES else Opcode.BGE
|
||||
">" -> elseBranch = if(signed) Opcode.BLES else Opcode.BLE
|
||||
"<=" -> elseBranch = if(signed) Opcode.BGTS else Opcode.BGT
|
||||
">=" -> elseBranch = if(signed) Opcode.BLTS else Opcode.BLT
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
if (ifElse.elseScope.children.isNotEmpty()) {
|
||||
|
||||
if (ifElse.hasElse()) {
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
if(useCmpi) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = compResultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.CMPI, branchDt, reg1 = leftTr.resultReg, immediate = number)
|
||||
it += IRInstruction(elseBranch, labelSymbol = elseLabel)
|
||||
}
|
||||
} else
|
||||
addInstr(result, IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = elseLabel), null)
|
||||
} else {
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1 = leftTr.resultReg, immediate = number, labelSymbol = elseLabel), null)
|
||||
}
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
|
@ -1253,145 +1364,110 @@ class IRCodeGen(
|
|||
val afterIfLabel = createLabelName()
|
||||
if(useCmpi) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = compResultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.CMPI, branchDt, reg1 = leftTr.resultReg, immediate = number)
|
||||
it += IRInstruction(elseBranch, labelSymbol = afterIfLabel)
|
||||
}
|
||||
} else
|
||||
addInstr(result, IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = afterIfLabel), null)
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1 = leftTr.resultReg, immediate = number, labelSymbol = afterIfLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
}
|
||||
} else {
|
||||
// integer comparisons
|
||||
branchDt = irDtLeft
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val number = (condition.right as? PtNumber)?.number?.toInt()
|
||||
if (number!=null) {
|
||||
val elseBranch: Opcode
|
||||
var useCmpi = false // for the branch opcodes that have been converted to CMPI + BSTxx form already
|
||||
when (condition.operator) {
|
||||
"==" -> {
|
||||
elseBranch = Opcode.BSTNE
|
||||
useCmpi = true
|
||||
}
|
||||
"!=" -> {
|
||||
elseBranch = Opcode.BSTEQ
|
||||
useCmpi = true
|
||||
}
|
||||
"<" -> elseBranch = if(signed) Opcode.BGES else Opcode.BGE
|
||||
">" -> elseBranch = if(signed) Opcode.BLES else Opcode.BLE
|
||||
"<=" -> elseBranch = if(signed) Opcode.BGTS else Opcode.BGT
|
||||
">=" -> elseBranch = if(signed) Opcode.BLTS else Opcode.BLT
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
val rightTr = expressionEval.translateExpression(condition.right)
|
||||
val elseBranch: Opcode
|
||||
var useCmp = false
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
when (condition.operator) {
|
||||
"==" -> {
|
||||
useCmp = true
|
||||
elseBranch = Opcode.BSTNE
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
if (ifElse.elseScope.children.isNotEmpty()) {
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
if(useCmpi) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, branchDt, reg1 = leftTr.resultReg, immediate = number)
|
||||
it += IRInstruction(elseBranch, labelSymbol = elseLabel)
|
||||
}
|
||||
} else
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1 = leftTr.resultReg, immediate = number, labelSymbol = elseLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
} else {
|
||||
// only if part
|
||||
val afterIfLabel = createLabelName()
|
||||
if(useCmpi) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, branchDt, reg1 = leftTr.resultReg, immediate = number)
|
||||
it += IRInstruction(elseBranch, labelSymbol = afterIfLabel)
|
||||
}
|
||||
} else
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1 = leftTr.resultReg, immediate = number, labelSymbol = afterIfLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
"!=" -> {
|
||||
useCmp = true
|
||||
elseBranch = Opcode.BSTEQ
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
} else {
|
||||
val rightTr = expressionEval.translateExpression(condition.right)
|
||||
val elseBranch: Opcode
|
||||
var useCmp = false
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
when (condition.operator) {
|
||||
"==" -> {
|
||||
useCmp = true
|
||||
elseBranch = Opcode.BSTNE
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
"!=" -> {
|
||||
useCmp = true
|
||||
elseBranch = Opcode.BSTEQ
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
"<" -> {
|
||||
// else part when left >= right
|
||||
elseBranch = if (signed) Opcode.BGESR else Opcode.BGER
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
">" -> {
|
||||
// else part when left <= right --> right >= left
|
||||
elseBranch = if (signed) Opcode.BGESR else Opcode.BGER
|
||||
elseBranchFirstReg = rightTr.resultReg
|
||||
elseBranchSecondReg = leftTr.resultReg
|
||||
}
|
||||
"<=" -> {
|
||||
// else part when left > right
|
||||
elseBranch = if (signed) Opcode.BGTSR else Opcode.BGTR
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
"<" -> {
|
||||
// else part when left >= right
|
||||
elseBranch = if (signed) Opcode.BGESR else Opcode.BGER
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
">" -> {
|
||||
// else part when left <= right --> right >= left
|
||||
elseBranch = if (signed) Opcode.BGESR else Opcode.BGER
|
||||
elseBranchFirstReg = rightTr.resultReg
|
||||
elseBranchSecondReg = leftTr.resultReg
|
||||
}
|
||||
"<=" -> {
|
||||
// else part when left > right
|
||||
elseBranch = if (signed) Opcode.BGTSR else Opcode.BGTR
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
">=" -> {
|
||||
// else part when left < right --> right > left
|
||||
elseBranch = if (signed) Opcode.BGTSR else Opcode.BGTR
|
||||
elseBranchFirstReg = rightTr.resultReg
|
||||
elseBranchSecondReg = leftTr.resultReg
|
||||
}
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
|
||||
">=" -> {
|
||||
// else part when left < right --> right > left
|
||||
elseBranch = if (signed) Opcode.BGTSR else Opcode.BGTR
|
||||
elseBranchFirstReg = rightTr.resultReg
|
||||
elseBranchSecondReg = leftTr.resultReg
|
||||
if (ifElse.hasElse()) {
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
if(useCmp) {
|
||||
result += IRCodeChunk(null,null).also {
|
||||
it += IRInstruction(Opcode.CMP, branchDt, reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg)
|
||||
it += IRInstruction(elseBranch, labelSymbol = elseLabel)
|
||||
}
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
if (ifElse.elseScope.children.isNotEmpty()) {
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
if(useCmp) {
|
||||
result += IRCodeChunk(null,null).also {
|
||||
it += IRInstruction(Opcode.CMP, branchDt, reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg)
|
||||
it += IRInstruction(elseBranch, labelSymbol = elseLabel)
|
||||
}
|
||||
} else {
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg, labelSymbol = elseLabel), null)
|
||||
}
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
} else {
|
||||
// only if part
|
||||
val afterIfLabel = createLabelName()
|
||||
if(useCmp) {
|
||||
result += IRCodeChunk(null,null).also {
|
||||
it += IRInstruction(Opcode.CMP, branchDt, reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg)
|
||||
it += IRInstruction(elseBranch, labelSymbol = afterIfLabel)
|
||||
}
|
||||
} else {
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg, labelSymbol = afterIfLabel), null)
|
||||
}
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg, labelSymbol = elseLabel), null)
|
||||
}
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
} else {
|
||||
// only if part
|
||||
val afterIfLabel = createLabelName()
|
||||
if(useCmp) {
|
||||
result += IRCodeChunk(null,null).also {
|
||||
it += IRInstruction(Opcode.CMP, branchDt, reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg)
|
||||
it += IRInstruction(elseBranch, labelSymbol = afterIfLabel)
|
||||
}
|
||||
} else {
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg, labelSymbol = afterIfLabel), null)
|
||||
}
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
when(val cond=ifElse.condition) {
|
||||
is PtTypeCast -> {
|
||||
require(cond.type==DataType.BOOL && cond.value.type in NumericDatatypes)
|
||||
translateSimple(cond, Opcode.BSTEQ)
|
||||
}
|
||||
is PtIdentifier, is PtArrayIndexer, is PtBuiltinFunctionCall, is PtFunctionCall, is PtContainmentCheck -> {
|
||||
translateSimple(cond, Opcode.BSTEQ)
|
||||
}
|
||||
is PtPrefix -> {
|
||||
require(cond.operator=="not")
|
||||
translateSimple(cond.value, Opcode.BSTNE)
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
translateBinExpr(cond)
|
||||
}
|
||||
else -> throw AssemblyError("weird if condition ${ifElse.condition}")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun translate(repeat: PtRepeatLoop): IRCodeChunks {
|
||||
|
@ -1542,6 +1618,8 @@ class IRCodeGen(
|
|||
private fun translate(parameters: List<PtSubroutineParameter>) =
|
||||
parameters.map {
|
||||
val flattenedName = it.definingSub()!!.name + "." + it.name
|
||||
if(symbolTable.lookup(flattenedName)==null)
|
||||
TODO("fix missing lookup for: $flattenedName parameter")
|
||||
val orig = symbolTable.lookup(flattenedName) as StStaticVariable
|
||||
IRSubroutine.IRParam(flattenedName, orig.dt)
|
||||
}
|
||||
|
@ -1563,9 +1641,9 @@ class IRCodeGen(
|
|||
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall): ExpressionCodeResult
|
||||
= builtinFuncGen.translate(call)
|
||||
|
||||
internal fun isZero(expression: PtExpression): Boolean = expression is PtNumber && expression.number==0.0
|
||||
internal fun isZero(expression: PtExpression): Boolean = (expression as? PtNumber)?.number==0.0 || (expression as? PtBool)?.value==false
|
||||
|
||||
internal fun isOne(expression: PtExpression): Boolean = expression is PtNumber && expression.number==1.0
|
||||
internal fun isOne(expression: PtExpression): Boolean = (expression as? PtNumber)?.number==1.0 || (expression as? PtBool)?.value==true
|
||||
|
||||
fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
|
||||
return IRCodeChunk(label, null).also {
|
||||
|
|
|
@ -337,10 +337,33 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// a SNZ etc. whose target register is not used can be removed altogether
|
||||
if(ins.opcode in OpcodesThatSetRegFromStatusbits) {
|
||||
val usages = regUsages(ins.reg1!!)
|
||||
if(usages.toList().sumOf { it.second } <= 1) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun regUsages(register: Int): Map<IRCodeChunkBase, Int> {
|
||||
val chunks = mutableMapOf<IRCodeChunkBase, Int>()
|
||||
irprog.foreachSub { sub ->
|
||||
sub.chunks.forEach { chunk ->
|
||||
val used = chunk.usedRegisters()
|
||||
val numUsages = used.readRegs.getOrDefault(register, 0) + used.writeRegs.getOrDefault(register, 0)
|
||||
if(numUsages>0) {
|
||||
chunks[chunk] = numUsages
|
||||
}
|
||||
}
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
|
||||
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
|
||||
var changed = false
|
||||
|
|
|
@ -3,7 +3,7 @@ import prog8.code.core.*
|
|||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType) = when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in ByteDatatypesWithBoolean -> 1
|
||||
DataType.FLOAT -> 5
|
||||
else -> 2
|
||||
}
|
||||
|
@ -56,6 +56,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
|||
}
|
||||
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
|
|
|
@ -125,7 +125,7 @@ class TestVmCodeGen: FunSpec({
|
|||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp1.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
|
@ -133,7 +133,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp2.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
|
@ -141,7 +141,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if2)
|
||||
val if3 = PtIfElse(Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp3.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
|
||||
if3.add(cmp3)
|
||||
|
@ -149,7 +149,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if3)
|
||||
val if4 = PtIfElse(Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp4.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
|
||||
if4.add(cmp4)
|
||||
|
@ -188,7 +188,7 @@ class TestVmCodeGen: FunSpec({
|
|||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
|
@ -196,7 +196,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
|
@ -204,7 +204,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if2)
|
||||
val if3 = PtIfElse(Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp3.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if3.add(cmp3)
|
||||
|
@ -212,7 +212,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if3)
|
||||
val if4 = PtIfElse(Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp4.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if4.add(cmp4)
|
||||
|
@ -247,7 +247,7 @@ class TestVmCodeGen: FunSpec({
|
|||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
|
@ -255,7 +255,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if1.add(PtNodeGroup())
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
|
@ -294,7 +294,7 @@ class TestVmCodeGen: FunSpec({
|
|||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BYTE, Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp1.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
|
@ -302,7 +302,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BYTE, Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp2.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
|
@ -310,7 +310,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if2)
|
||||
val if3 = PtIfElse(Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BYTE, Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp3.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
|
||||
if3.add(cmp3)
|
||||
|
@ -318,7 +318,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if3)
|
||||
val if4 = PtIfElse(Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BYTE, Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp4.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
|
||||
if4.add(cmp4)
|
||||
|
@ -357,7 +357,7 @@ class TestVmCodeGen: FunSpec({
|
|||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BYTE, Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp1.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
|
@ -365,7 +365,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BYTE, Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp2.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
|
@ -373,7 +373,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if2)
|
||||
val if3 = PtIfElse(Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BYTE, Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp3.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
|
||||
if3.add(cmp3)
|
||||
|
@ -381,7 +381,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if3)
|
||||
val if4 = PtIfElse(Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BYTE, Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp4.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
|
||||
if4.add(cmp4)
|
||||
|
@ -416,7 +416,7 @@ class TestVmCodeGen: FunSpec({
|
|||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||
cmp1.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
|
@ -424,7 +424,7 @@ class TestVmCodeGen: FunSpec({
|
|||
if1.add(PtNodeGroup())
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||
cmp2.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
|
|
|
@ -7,6 +7,7 @@ import prog8.ast.expressions.FunctionCallExpression
|
|||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IntegerDatatypes
|
||||
import prog8.code.core.IntegerDatatypesWithBoolean
|
||||
import prog8.code.core.Position
|
||||
import kotlin.math.*
|
||||
|
||||
|
@ -69,15 +70,15 @@ class ConstExprEvaluator {
|
|||
}
|
||||
|
||||
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type== DataType.UBYTE) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
if(left.type==DataType.UBYTE || left.type==DataType.BOOL) {
|
||||
if(right.type in IntegerDatatypesWithBoolean) {
|
||||
return NumericLiteral(DataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.UWORD) {
|
||||
} else if(left.type==DataType.UWORD) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral(DataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.LONG) {
|
||||
} else if(left.type==DataType.LONG) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
|
@ -86,15 +87,15 @@ class ConstExprEvaluator {
|
|||
}
|
||||
|
||||
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type== DataType.UBYTE) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
if(left.type==DataType.UBYTE || left.type==DataType.BOOL) {
|
||||
if(right.type in IntegerDatatypesWithBoolean) {
|
||||
return NumericLiteral(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.UWORD) {
|
||||
} else if(left.type==DataType.UWORD) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral(DataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.LONG) {
|
||||
} else if(left.type==DataType.LONG) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
|
@ -103,11 +104,11 @@ class ConstExprEvaluator {
|
|||
}
|
||||
|
||||
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type== DataType.UBYTE) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
if(left.type==DataType.UBYTE || left.type==DataType.BOOL) {
|
||||
if(right.type in IntegerDatatypesWithBoolean) {
|
||||
return NumericLiteral(DataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.UWORD) {
|
||||
} else if(left.type==DataType.UWORD) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral(DataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
|
|
|
@ -36,9 +36,9 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
|||
if(parent is Assignment) {
|
||||
val iDt = parent.target.inferType(program)
|
||||
if(iDt.isKnown && !iDt.isBool && !iDt.istype(numLiteral.type)) {
|
||||
val casted = numLiteral.cast(iDt.getOr(DataType.UNDEFINED))
|
||||
val casted = numLiteral.cast(iDt.getOr(DataType.UNDEFINED), true)
|
||||
if(casted.isValid) {
|
||||
return listOf(IAstModification.ReplaceNode(numLiteral, casted.value!!, parent))
|
||||
return listOf(IAstModification.ReplaceNode(numLiteral, casted.valueOrZero(), parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -313,23 +313,23 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
|||
|
||||
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
||||
fun adjustRangeDt(rangeFrom: NumericLiteral, targetDt: DataType, rangeTo: NumericLiteral, stepLiteral: NumericLiteral?, range: RangeExpression): RangeExpression? {
|
||||
val fromCast = rangeFrom.cast(targetDt)
|
||||
val toCast = rangeTo.cast(targetDt)
|
||||
val fromCast = rangeFrom.cast(targetDt, true)
|
||||
val toCast = rangeTo.cast(targetDt, true)
|
||||
if(!fromCast.isValid || !toCast.isValid)
|
||||
return null
|
||||
|
||||
val newStep =
|
||||
if(stepLiteral!=null) {
|
||||
val stepCast = stepLiteral.cast(targetDt)
|
||||
val stepCast = stepLiteral.cast(targetDt, true)
|
||||
if(stepCast.isValid)
|
||||
stepCast.value!!
|
||||
stepCast.valueOrZero()
|
||||
else
|
||||
range.step
|
||||
} else {
|
||||
range.step
|
||||
}
|
||||
|
||||
return RangeExpression(fromCast.value!!, toCast.value!!, newStep, range.position)
|
||||
return RangeExpression(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, range.position)
|
||||
}
|
||||
|
||||
// adjust the datatype of a range expression in for loops to the loop variable.
|
||||
|
@ -386,9 +386,9 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
|||
val valueDt = numval.inferType(program)
|
||||
if(valueDt isnot decl.datatype) {
|
||||
if(decl.datatype!=DataType.BOOL || valueDt.isnot(DataType.UBYTE)) {
|
||||
val cast = numval.cast(decl.datatype)
|
||||
val cast = numval.cast(decl.datatype, true)
|
||||
if (cast.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(numval, cast.value!!, decl))
|
||||
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,12 +35,12 @@ class VarConstantValueTypeAdjuster(
|
|||
// avoid silent float roundings
|
||||
if(decl.datatype in IntegerDatatypes && declConstValue.type == DataType.FLOAT) {
|
||||
errors.err("refused truncating of float to avoid loss of precision", decl.value!!.position)
|
||||
} else {
|
||||
// cast the numeric literal to the appropriate datatype of the variable
|
||||
} else if(decl.datatype!=DataType.BOOL) {
|
||||
// cast the numeric literal to the appropriate datatype of the variable if it's not boolean
|
||||
declConstValue.linkParents(decl)
|
||||
val cast = declConstValue.cast(decl.datatype)
|
||||
val cast = declConstValue.cast(decl.datatype, true)
|
||||
if (cast.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.value!!, decl))
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.valueOrZero(), decl))
|
||||
}
|
||||
}
|
||||
} catch (x: UndefinedSymbolError) {
|
||||
|
@ -270,7 +270,11 @@ class VarConstantValueTypeAdjuster(
|
|||
// Replace all constant identifiers with their actual value,
|
||||
// and the array var initializer values and sizes.
|
||||
// This is needed because further constant optimizations depend on those.
|
||||
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||
internal class ConstantIdentifierReplacer(
|
||||
private val program: Program,
|
||||
private val options: CompilationOptions,
|
||||
private val errors: IErrorReporter
|
||||
) : AstWalker() {
|
||||
|
||||
override fun before(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
|
||||
val constValue = addressOf.constValue(program)
|
||||
|
@ -292,7 +296,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||
return noModifications
|
||||
|
||||
val dt = identifier.inferType(program)
|
||||
if(!dt.isKnown || !dt.isNumeric)
|
||||
if(!dt.isKnown || !dt.isNumeric && !dt.isBool)
|
||||
return noModifications
|
||||
|
||||
try {
|
||||
|
@ -313,7 +317,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||
}
|
||||
}
|
||||
when (cval.type) {
|
||||
in NumericDatatypes -> {
|
||||
in NumericDatatypesWithBoolean -> {
|
||||
if(parent is AddressOf)
|
||||
return noModifications // cannot replace the identifier INSIDE the addr-of here, let's do it later.
|
||||
return listOf(
|
||||
|
@ -369,7 +373,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||
DataType.FLOAT -> {
|
||||
// vardecl: for scalar float vars, promote constant integer initialization values to floats
|
||||
val litval = decl.value as? NumericLiteral
|
||||
if (litval!=null && litval.type in IntegerDatatypes) {
|
||||
if (litval!=null && litval.type in IntegerDatatypesWithBoolean) {
|
||||
val newValue = NumericLiteral(DataType.FLOAT, litval.number, litval.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||
}
|
||||
|
@ -470,12 +474,11 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||
val numericLv = decl.value as? NumericLiteral
|
||||
val size = decl.arraysize?.constIndex() ?: return null
|
||||
if(rangeExpr==null && numericLv!=null) {
|
||||
// arraysize initializer is a single int, and we know the size.
|
||||
// arraysize initializer is a single int, and we know the array size.
|
||||
val fillvalue = numericLv.number
|
||||
if (fillvalue < compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > compTarget.machine.FLOAT_MAX_POSITIVE)
|
||||
if (fillvalue < options.compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > options.compTarget.machine.FLOAT_MAX_POSITIVE)
|
||||
errors.err("float value overflow", numericLv.position)
|
||||
else {
|
||||
// create the array itself, filled with the fillvalue.
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.FLOAT, it, numericLv.position) }.toTypedArray<Expression>()
|
||||
return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = numericLv.position)
|
||||
}
|
||||
|
@ -485,9 +488,13 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||
val numericLv = decl.value as? NumericLiteral
|
||||
val size = decl.arraysize?.constIndex() ?: return null
|
||||
if(numericLv!=null) {
|
||||
// arraysize initializer is a single int, and we know the size.
|
||||
val fillvalue = if(numericLv.number==0.0) 0.0 else 1.0
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.UBYTE, fillvalue, numericLv.position) }.toTypedArray<Expression>()
|
||||
// arraysize initializer is a single value, and we know the array size.
|
||||
if(numericLv.type!=DataType.BOOL) {
|
||||
if(options.strictBool || numericLv.type !in ByteDatatypes)
|
||||
errors.err("initializer value is not a boolean", numericLv.position)
|
||||
return null
|
||||
}
|
||||
val array = Array(size) {numericLv.number}.map { NumericLiteral(DataType.BOOL, it, numericLv.position) }.toTypedArray<Expression>()
|
||||
return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_BOOL), array, position = numericLv.position)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,7 @@ import kotlin.math.pow
|
|||
|
||||
// TODO add more peephole expression optimizations? Investigate what optimizations binaryen has?
|
||||
|
||||
class ExpressionSimplifier(private val program: Program,
|
||||
private val errors: IErrorReporter,
|
||||
private val compTarget: ICompilationTarget) : AstWalker() {
|
||||
class ExpressionSimplifier(private val program: Program, private val options: CompilationOptions, private val errors: IErrorReporter) : AstWalker() {
|
||||
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
|
||||
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
||||
|
||||
|
@ -31,9 +29,9 @@ class ExpressionSimplifier(private val program: Program,
|
|||
// try to statically convert a literal value into one of the desired type
|
||||
val literal = typecast.expression as? NumericLiteral
|
||||
if (literal != null) {
|
||||
val newLiteral = literal.cast(typecast.type)
|
||||
if (newLiteral.isValid && newLiteral.value!! !== literal) {
|
||||
mods += IAstModification.ReplaceNode(typecast, newLiteral.value!!, parent)
|
||||
val newLiteral = literal.cast(typecast.type, typecast.implicit)
|
||||
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
|
||||
mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,10 +112,6 @@ class ExpressionSimplifier(private val program: Program,
|
|||
if (!leftIDt.isKnown || !rightIDt.isKnown)
|
||||
throw FatalAstException("can't determine datatype of both expression operands $expr")
|
||||
|
||||
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
||||
if (leftVal != null && expr.operator in AssociativeOperators && rightVal == null && maySwapOperandOrder(expr))
|
||||
return listOf(IAstModification.SwapOperands(expr))
|
||||
|
||||
// NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> NonBinaryExpression
|
||||
if (expr.operator in AssociativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) {
|
||||
if(parent !is Assignment || !(expr.left isSameAs parent.target) && maySwapOperandOrder(expr))
|
||||
|
@ -255,26 +249,95 @@ class ExpressionSimplifier(private val program: Program,
|
|||
}
|
||||
}
|
||||
|
||||
// boolvar & 1 --> boolvar
|
||||
// boolvar & 2 --> false
|
||||
if(expr.operator=="&" && rightDt in IntegerDatatypes && (leftDt == DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
|
||||
if(rightVal?.number==1.0) {
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
} else if(rightVal?.number!=null && (rightVal.number.toInt() and 1)==0) {
|
||||
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(false, expr.position), parent))
|
||||
// optimize boolean constant comparisons
|
||||
if(expr.operator=="==") {
|
||||
if(rightDt==DataType.BOOL && leftDt==DataType.BOOL) {
|
||||
if(rightVal?.asBooleanValue==true)
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
else
|
||||
return listOf(IAstModification.ReplaceNode(expr, PrefixExpression("not", expr.left, expr.position), parent))
|
||||
}
|
||||
if (rightVal?.number == 1.0) {
|
||||
if (options.strictBool) {
|
||||
if (rightDt != leftDt) {
|
||||
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
} else
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
}
|
||||
else if (rightVal?.number == 0.0) {
|
||||
if (options.strictBool) {
|
||||
if (rightDt != leftDt) {
|
||||
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (expr.operator=="!=") {
|
||||
if(rightDt==DataType.BOOL && leftDt==DataType.BOOL) {
|
||||
if(rightVal?.asBooleanValue==false)
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
else
|
||||
return listOf(IAstModification.ReplaceNode(expr, PrefixExpression("not", expr.left, expr.position), parent))
|
||||
}
|
||||
if (rightVal?.number == 1.0) {
|
||||
if(options.strictBool) {
|
||||
if(rightDt!=leftDt) {
|
||||
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rightVal?.number == 0.0) {
|
||||
if(options.strictBool) {
|
||||
if(rightDt!=leftDt) {
|
||||
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
} else
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
}
|
||||
}
|
||||
|
||||
if(leftDt==DataType.BOOL) {
|
||||
// optimize boolean constant comparisons
|
||||
// if(expr.operator=="==" && rightVal?.number==0.0)
|
||||
// return listOf(IAstModification.ReplaceNode(expr, PrefixExpression("not", expr.left, expr.position), parent))
|
||||
// if(expr.operator=="!=" && rightVal?.number==1.0)
|
||||
// return listOf(IAstModification.ReplaceNode(expr, PrefixExpression("not", expr.left, expr.position), parent))
|
||||
if(expr.operator=="==" && rightVal?.number==1.0)
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
if(expr.operator=="!=" && rightVal?.number==0.0)
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
if(expr.operator in arrayOf("and", "or", "xor")) {
|
||||
if(leftVal!=null) {
|
||||
val result = if(leftVal.asBooleanValue) {
|
||||
when(expr.operator) {
|
||||
"and" -> expr.right
|
||||
"or" -> NumericLiteral.fromBoolean(true, expr.position)
|
||||
"xor" -> PrefixExpression("not", expr.right, expr.position)
|
||||
else -> throw FatalAstException("weird op")
|
||||
}
|
||||
} else {
|
||||
when(expr.operator) {
|
||||
"and" -> NumericLiteral.fromBoolean(false, expr.position)
|
||||
"or" -> expr.right
|
||||
"xor" -> expr.right
|
||||
else -> throw FatalAstException("weird op")
|
||||
}
|
||||
}
|
||||
return listOf(IAstModification.ReplaceNode(expr, result, parent))
|
||||
}
|
||||
else if(rightVal!=null) {
|
||||
val result = if(rightVal.asBooleanValue) {
|
||||
when(expr.operator) {
|
||||
"and" -> expr.left
|
||||
"or" -> NumericLiteral.fromBoolean(true, expr.position)
|
||||
"xor" -> PrefixExpression("not", expr.left, expr.position)
|
||||
else -> throw FatalAstException("weird op")
|
||||
}
|
||||
} else {
|
||||
when(expr.operator) {
|
||||
"and" -> NumericLiteral.fromBoolean(false, expr.position)
|
||||
"or" -> expr.left
|
||||
"xor" -> expr.left
|
||||
else -> throw FatalAstException("weird op")
|
||||
}
|
||||
}
|
||||
return listOf(IAstModification.ReplaceNode(expr, result, parent))
|
||||
}
|
||||
}
|
||||
|
||||
// simplify when a term is constant and directly determines the outcome
|
||||
|
@ -387,7 +450,6 @@ class ExpressionSimplifier(private val program: Program,
|
|||
return noModifications
|
||||
}
|
||||
|
||||
|
||||
private fun applyAbsorptionLaws(expr: BinaryExpression): Expression? {
|
||||
val rightB = expr.right as? BinaryExpression
|
||||
if(rightB!=null) {
|
||||
|
|
|
@ -3,7 +3,6 @@ package prog8.optimizer
|
|||
import prog8.ast.IBuiltinFunctions
|
||||
import prog8.ast.Program
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
|
||||
|
||||
|
@ -13,7 +12,7 @@ fun Program.constantFold(errors: IErrorReporter, options: CompilationOptions) {
|
|||
if(errors.noErrors()) {
|
||||
valuetypefixer.applyModifications()
|
||||
|
||||
val replacer = ConstantIdentifierReplacer(this, errors, options.compTarget)
|
||||
val replacer = ConstantIdentifierReplacer(this, options, errors)
|
||||
replacer.visit(this)
|
||||
if (errors.noErrors()) {
|
||||
replacer.applyModifications()
|
||||
|
@ -60,8 +59,8 @@ fun Program.inlineSubroutines(options: CompilationOptions): Int {
|
|||
return inliner.applyModifications()
|
||||
}
|
||||
|
||||
fun Program.simplifyExpressions(errors: IErrorReporter, target: ICompilationTarget) : Int {
|
||||
val opti = ExpressionSimplifier(this, errors, target)
|
||||
fun Program.simplifyExpressions(errors: IErrorReporter, options: CompilationOptions) : Int {
|
||||
val opti = ExpressionSimplifier(this, options, errors)
|
||||
opti.visit(this)
|
||||
return opti.applyModifications()
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ class StatementOptimizer(private val program: Program,
|
|||
|
||||
// empty true part? switch with the else part
|
||||
if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) {
|
||||
val invertedCondition = BinaryExpression(ifElse.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, ifElse.condition.position), ifElse.condition.position)
|
||||
val invertedCondition = invertCondition(ifElse.condition, program)
|
||||
val emptyscope = AnonymousScope(mutableListOf(), ifElse.elsepart.position)
|
||||
val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position)
|
||||
return listOf(
|
||||
|
@ -103,25 +103,40 @@ class StatementOptimizer(private val program: Program,
|
|||
}
|
||||
}
|
||||
|
||||
// remove obvious dangling elses (else after a return)
|
||||
if(ifElse.elsepart.isNotEmpty() && ifElse.truepart.statements.singleOrNull() is Return) {
|
||||
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
|
||||
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
if(ifElse.elsepart.isNotEmpty()) {
|
||||
// remove obvious dangling elses (else after a return)
|
||||
if(ifElse.truepart.statements.singleOrNull() is Return) {
|
||||
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
|
||||
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
|
||||
// switch if/else around if the else is just a jump or branch
|
||||
if(ifElse.elsepart.isNotEmpty() && ifElse.elsepart.statements.size==1) {
|
||||
val jump = ifElse.elsepart.statements[0]
|
||||
if(jump is Jump) {
|
||||
val newTruePart = AnonymousScope(mutableListOf(jump), ifElse.elsepart.position)
|
||||
// switch if/else around if the else is just a jump or branch
|
||||
if(ifElse.elsepart.statements.size==1) {
|
||||
val jump = ifElse.elsepart.statements[0]
|
||||
if(jump is Jump) {
|
||||
val newTruePart = AnonymousScope(mutableListOf(jump), ifElse.elsepart.position)
|
||||
val newElsePart = AnonymousScope(ifElse.truepart.statements, ifElse.truepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, newElsePart, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.truepart, newTruePart, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.condition, invertCondition(ifElse.condition, program), ifElse)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// switch if/else around if the condition is a not
|
||||
val prefixCond = ifElse.condition as? PrefixExpression
|
||||
if(prefixCond?.operator=="not") {
|
||||
errors.info("invert conditon and swap if/else blocks", ifElse.condition.position)
|
||||
val newTruePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
|
||||
val newElsePart = AnonymousScope(ifElse.truepart.statements, ifElse.truepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, newElsePart, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.truepart, newTruePart, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.condition, invertCondition(ifElse.condition, program), ifElse)
|
||||
IAstModification.ReplaceNode(ifElse.condition, prefixCond.expression, ifElse)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -445,38 +460,6 @@ class StatementOptimizer(private val program: Program,
|
|||
return listOf(IAstModification.ReplaceNode(whenStmt, ifStmt, parent))
|
||||
}
|
||||
|
||||
if(whenStmt.condition.inferType(program).isBool) {
|
||||
if(whenStmt.choices.all { it.values?.size==1 }) {
|
||||
if (whenStmt.choices.all { it.values!!.single().constValue(program)!!.number in arrayOf(0.0, 1.0) }) {
|
||||
// it's a when statement on booleans that can just be replaced by an if or if-else.
|
||||
if (whenStmt.choices.size == 1) {
|
||||
return if(whenStmt.choices[0].values!![0].constValue(program)!!.number==1.0) {
|
||||
replaceWithIf(whenStmt.condition, whenStmt.choices[0].statements, null)
|
||||
} else {
|
||||
val notCondition = BinaryExpression(whenStmt.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, whenStmt.condition.position), whenStmt.condition.position)
|
||||
replaceWithIf(notCondition, whenStmt.choices[0].statements, null)
|
||||
}
|
||||
} else if (whenStmt.choices.size == 2) {
|
||||
var trueBlock: AnonymousScope? = null
|
||||
var elseBlock: AnonymousScope? = null
|
||||
if(whenStmt.choices[0].values!![0].constValue(program)!!.number==1.0) {
|
||||
trueBlock = whenStmt.choices[0].statements
|
||||
} else {
|
||||
elseBlock = whenStmt.choices[0].statements
|
||||
}
|
||||
if(whenStmt.choices[1].values!![0].constValue(program)!!.number==1.0) {
|
||||
trueBlock = whenStmt.choices[1].statements
|
||||
} else {
|
||||
elseBlock = whenStmt.choices[1].statements
|
||||
}
|
||||
if(trueBlock!=null && elseBlock!=null) {
|
||||
return replaceWithIf(whenStmt.condition, trueBlock, elseBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val constantValue = whenStmt.condition.constValue(program)?.number
|
||||
if(constantValue!=null) {
|
||||
// when condition is a constant
|
||||
|
|
|
@ -105,7 +105,7 @@ romsub $FFF3 = IOBASE() -> uword @ XY ; read base addr
|
|||
|
||||
; ---- utilities -----
|
||||
|
||||
asmsub STOP2() clobbers(X) -> ubyte @A {
|
||||
asmsub STOP2() clobbers(X) -> bool @A {
|
||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||
%asm {{
|
||||
jsr cbm.STOP
|
||||
|
|
|
@ -98,6 +98,21 @@ cast_as_w_into_ay .proc ; also used for float 2 b
|
|||
jmp cast_FAC1_as_w_into_ay
|
||||
.pend
|
||||
|
||||
cast_as_bool_into_a .proc
|
||||
; -- cast float at A/Y to bool into A
|
||||
; clobbers X
|
||||
jsr MOVFM
|
||||
jmp cast_FAC1_as_bool_into_a
|
||||
.pend
|
||||
|
||||
cast_FAC1_as_bool_into_a .proc
|
||||
; -- cast fac1 to bool into A
|
||||
; clobbers X
|
||||
jsr SIGN
|
||||
and #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
cast_FAC1_as_uw_into_ya .proc ; also used for float 2 ub
|
||||
; -- cast fac1 to uword into Y/A
|
||||
; clobbers X
|
||||
|
@ -221,7 +236,7 @@ var_fac1_greatereq_f .proc
|
|||
.pend
|
||||
|
||||
var_fac1_equal_f .proc
|
||||
; -- are the floats numbers in FAC1 and the variable AY *not* identical? Result in A. Clobbers X.
|
||||
; -- are the floats numbers in FAC1 and the variable AY identical? Result in A. Clobbers X.
|
||||
jsr FCOMP
|
||||
and #1
|
||||
eor #1
|
||||
|
|
|
@ -73,7 +73,7 @@ graphics {
|
|||
}
|
||||
|
||||
word @zp d = 0
|
||||
ubyte positive_ix = true
|
||||
bool positive_ix = true
|
||||
if dx < 0 {
|
||||
dx = -dx
|
||||
positive_ix = false
|
||||
|
@ -173,7 +173,7 @@ graphics {
|
|||
ubyte separate_pixels = lsb(xx) & 7
|
||||
uword pixaddr = get_y_lookup(yy) + (xx&$fff8)
|
||||
|
||||
if separate_pixels {
|
||||
if separate_pixels!=0 {
|
||||
%asm {{
|
||||
lda p8v_pixaddr
|
||||
sta P8ZP_SCRATCH_W1
|
||||
|
@ -191,7 +191,7 @@ graphics {
|
|||
length -= 8
|
||||
}
|
||||
|
||||
if length {
|
||||
if length!=0 {
|
||||
%asm {{
|
||||
lda p8v_length
|
||||
and #7
|
||||
|
|
|
@ -102,7 +102,7 @@ romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of
|
|||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||
|
||||
asmsub STOP2() clobbers(X) -> ubyte @A {
|
||||
asmsub STOP2() clobbers(X) -> bool @A {
|
||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||
%asm {{
|
||||
jsr cbm.STOP
|
||||
|
|
|
@ -126,7 +126,7 @@ bmx {
|
|||
; If you're saving the whole screen width, you can leave screenwidth at 0.
|
||||
; Returns: success status. If false, error_message points to the error message string.
|
||||
error_message = 0
|
||||
if compression {
|
||||
if compression!=0 {
|
||||
error_message = "compression not supported"
|
||||
return false
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ save_end:
|
|||
for cx16.r0L in 0 to sizeof(header)-1 {
|
||||
header[cx16.r0L] = cbm.CHRIN()
|
||||
}
|
||||
return not cbm.READST()
|
||||
return cbm.READST()==0
|
||||
}
|
||||
|
||||
sub read_palette() -> bool {
|
||||
|
@ -197,7 +197,7 @@ save_end:
|
|||
do {
|
||||
cx16.r4L = cbm.CHRIN()
|
||||
cx16.r4H = cbm.CHRIN()
|
||||
if cx16.r3 {
|
||||
if cx16.r3!=0 {
|
||||
pokew(cx16.r3, cx16.r4) ; into memory
|
||||
cx16.r3+=2
|
||||
} else {
|
||||
|
@ -206,7 +206,7 @@ save_end:
|
|||
}
|
||||
cx16.r2L--
|
||||
} until cx16.r2L==0
|
||||
return cbm.READST()==0 or cbm.READST()&$40 ; no error or eof?
|
||||
return cbm.READST()==0 or cbm.READST()&$40!=0 ; no error or eof?
|
||||
}
|
||||
|
||||
sub read_bitmap_padded(ubyte vbank, uword vaddr, uword screenwidth) -> bool {
|
||||
|
@ -220,7 +220,7 @@ save_end:
|
|||
if_cs
|
||||
vbank++
|
||||
}
|
||||
return cbm.READST()==0 or cbm.READST()&$40 ; no error or eof?
|
||||
return cbm.READST()==0 or cbm.READST()&$40!=0 ; no error or eof?
|
||||
}
|
||||
|
||||
sub read_bitmap(ubyte vbank, uword vaddr) -> bool {
|
||||
|
@ -229,11 +229,11 @@ save_end:
|
|||
cx16.vaddr(vbank, vaddr, 0, 1)
|
||||
repeat height
|
||||
read_scanline(cx16.r3)
|
||||
return cbm.READST()==0 or cbm.READST()&$40 ; no error or eof?
|
||||
return cbm.READST()==0 or cbm.READST()&$40!=0 ; no error or eof?
|
||||
}
|
||||
|
||||
sub read_scanline(uword size) {
|
||||
while size {
|
||||
while size!=0 {
|
||||
cx16.r0 = cx16.MACPTR(min(255, size) as ubyte, &cx16.VERA_DATA0, true)
|
||||
if_cs {
|
||||
; no MACPTR support
|
||||
|
@ -251,7 +251,7 @@ save_end:
|
|||
for cx16.r0L in 0 to sizeof(header)-1 {
|
||||
cbm.CHROUT(header[cx16.r0L])
|
||||
}
|
||||
return not cbm.READST()
|
||||
return cbm.READST()==0
|
||||
}
|
||||
|
||||
sub write_palette() -> bool {
|
||||
|
@ -262,7 +262,7 @@ save_end:
|
|||
cx16.r2L = lsb(palette_entries)
|
||||
cx16.vaddr(1, $fa00+palette_start*2, 0, 1)
|
||||
do {
|
||||
if cx16.r3 {
|
||||
if cx16.r3!=0 {
|
||||
cbm.CHROUT(@(cx16.r3)) ; from memory
|
||||
cx16.r3++
|
||||
cbm.CHROUT(@(cx16.r3))
|
||||
|
@ -273,7 +273,7 @@ save_end:
|
|||
}
|
||||
cx16.r2L--
|
||||
} until cx16.r2L==0
|
||||
return not cbm.READST()
|
||||
return cbm.READST()==0
|
||||
}
|
||||
|
||||
sub write_bitmap(ubyte vbank, uword vaddr, uword screenwidth) -> bool {
|
||||
|
@ -282,19 +282,19 @@ save_end:
|
|||
cx16.vaddr(vbank, vaddr, 0, 1)
|
||||
cx16.r3 = bytes_per_scanline(width) ; num bytes per image scanline
|
||||
cx16.r2 = 0
|
||||
if screenwidth
|
||||
if screenwidth!=0
|
||||
cx16.r2 = bytes_per_scanline(screenwidth-width) ; num bytes padding per screen scanline
|
||||
repeat height {
|
||||
write_scanline(cx16.r3)
|
||||
repeat cx16.r2
|
||||
cx16.r0L = cx16.VERA_DATA0
|
||||
}
|
||||
return not cbm.READST()
|
||||
return cbm.READST()==0
|
||||
|
||||
sub write_scanline(uword size) {
|
||||
while size {
|
||||
while size!=0 {
|
||||
cx16.r0L = lsb(size)
|
||||
if msb(size)
|
||||
if msb(size)!=0
|
||||
cx16.r0L = 0 ; 256 bytes
|
||||
cx16.r0 = cx16.MCIOUT(cx16.r0L, &cx16.VERA_DATA0, true)
|
||||
if_cs {
|
||||
|
|
|
@ -79,7 +79,7 @@ io_error:
|
|||
cbm.CLRCHN() ; restore default i/o devices
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
|
||||
if status and status & $40 == 0 { ; bit 6=end of file
|
||||
if status!=0 and status & $40 == 0 { ; bit 6=end of file
|
||||
txt.print("\ni/o error, status: ")
|
||||
txt.print_ub(status)
|
||||
txt.nl()
|
||||
|
@ -121,7 +121,7 @@ io_error:
|
|||
io_error:
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
if status and status & $40 == 0
|
||||
if status!=0 and status & $40 == 0
|
||||
return 0
|
||||
return list_filename
|
||||
}
|
||||
|
@ -210,14 +210,14 @@ io_error:
|
|||
ubyte blocks_lsb = cbm.CHRIN()
|
||||
ubyte blocks_msb = cbm.CHRIN()
|
||||
|
||||
if cbm.READST()
|
||||
if cbm.READST()!=0
|
||||
goto close_end
|
||||
|
||||
list_blocks = mkword(blocks_msb, blocks_lsb)
|
||||
|
||||
; read until the filename starts after the first "
|
||||
while cbm.CHRIN()!='\"' {
|
||||
if cbm.READST()
|
||||
if cbm.READST()!=0
|
||||
goto close_end
|
||||
}
|
||||
|
||||
|
@ -240,7 +240,7 @@ io_error:
|
|||
list_filetype[0] = cx16.r15L
|
||||
list_filetype[1] = cbm.CHRIN()
|
||||
list_filetype[2] = cbm.CHRIN()
|
||||
while cbm.CHRIN() {
|
||||
while cbm.CHRIN()!=0 {
|
||||
; read the rest of the entry until the end
|
||||
}
|
||||
|
||||
|
@ -248,7 +248,7 @@ io_error:
|
|||
void cbm.CHRIN()
|
||||
|
||||
if not list_skip_disk_name {
|
||||
if not list_pattern
|
||||
if list_pattern==0
|
||||
return true
|
||||
if string.pattern_match(list_filename, list_pattern)
|
||||
return true
|
||||
|
@ -308,14 +308,14 @@ close_end:
|
|||
; -- read from the currently open file, up to the given number of bytes.
|
||||
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
||||
; NOTE: cannot be used to load into VRAM. Use vload() or call cx16.MACPTR() yourself with the vera data register as address.
|
||||
if not iteration_in_progress or not num_bytes
|
||||
if not iteration_in_progress or num_bytes==0
|
||||
return 0
|
||||
|
||||
reset_read_channel()
|
||||
list_blocks = 0 ; we reuse this variable for the total number of bytes read
|
||||
|
||||
uword readsize
|
||||
while num_bytes {
|
||||
while num_bytes!=0 {
|
||||
readsize = 255
|
||||
if num_bytes<readsize
|
||||
readsize = num_bytes
|
||||
|
@ -327,7 +327,7 @@ close_end:
|
|||
if msb(bufferpointer) == $c0
|
||||
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
|
||||
num_bytes -= readsize
|
||||
if cbm.READST() & $40 {
|
||||
if cbm.READST() & $40 !=0 {
|
||||
f_close() ; end of file, close it
|
||||
break
|
||||
}
|
||||
|
@ -341,10 +341,10 @@ byte_read_loop: ; fallback if MACPTR isn't supported on the device
|
|||
lda bufferpointer+1
|
||||
sta m_in_buffer+2
|
||||
}}
|
||||
while num_bytes {
|
||||
if cbm.READST() {
|
||||
while num_bytes!=0 {
|
||||
if cbm.READST()!=0 {
|
||||
f_close()
|
||||
if cbm.READST() & $40 ; eof?
|
||||
if cbm.READST() & $40 !=0 ; eof?
|
||||
return list_blocks ; number of bytes read
|
||||
return 0 ; error.
|
||||
}
|
||||
|
@ -371,7 +371,7 @@ m_in_buffer sta $ffff
|
|||
|
||||
reset_read_channel()
|
||||
uword total_read = 0
|
||||
while not cbm.READST() {
|
||||
while cbm.READST()==0 {
|
||||
cx16.r0 = f_read(bufferpointer, 256)
|
||||
total_read += cx16.r0
|
||||
bufferpointer += cx16.r0
|
||||
|
@ -444,7 +444,7 @@ _end rts
|
|||
cbm.SETLFS(WRITE_IO_CHANNEL, drivenumber, WRITE_IO_CHANNEL)
|
||||
void cbm.OPEN() ; open 13,8,13,"filename"
|
||||
if_cc {
|
||||
return not cbm.READST()
|
||||
return cbm.READST()==0
|
||||
}
|
||||
cbm.CLOSE(WRITE_IO_CHANNEL)
|
||||
f_close_w()
|
||||
|
@ -466,7 +466,7 @@ _end rts
|
|||
if cbm.READST()!=0
|
||||
return false
|
||||
} until num_bytes==0
|
||||
return not cbm.READST()
|
||||
return cbm.READST()==0
|
||||
|
||||
no_mciout:
|
||||
; the device doesn't support MCIOUT, use a normal per-byte write loop
|
||||
|
@ -474,7 +474,7 @@ no_mciout:
|
|||
cbm.CHROUT(@(bufferpointer))
|
||||
bufferpointer++
|
||||
}
|
||||
return not cbm.READST()
|
||||
return cbm.READST()==0
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -505,7 +505,7 @@ no_mciout:
|
|||
goto io_error
|
||||
void cbm.CHKIN(15) ; use #15 as input channel
|
||||
|
||||
while not cbm.READST() {
|
||||
while cbm.READST()==0 {
|
||||
cx16.r5L = cbm.CHRIN()
|
||||
if cx16.r5L=='\r' or cx16.r5L=='\n'
|
||||
break
|
||||
|
@ -561,9 +561,9 @@ io_error:
|
|||
}}
|
||||
|
||||
if_cc
|
||||
cx16.r0L = cbm.READST()==0
|
||||
cx16.r0L = cbm.READST()==0 as ubyte
|
||||
|
||||
return cx16.r0L
|
||||
return cx16.r0L as bool
|
||||
}
|
||||
|
||||
; Use kernal LOAD routine to load the given program file in memory.
|
||||
|
@ -595,7 +595,7 @@ io_error:
|
|||
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||
ubyte secondary = 1
|
||||
cx16.r1 = 0
|
||||
if address_override
|
||||
if address_override!=0
|
||||
secondary = 0
|
||||
if headerless
|
||||
secondary |= %00000010 ; activate cx16 kernal headerless load support
|
||||
|
@ -658,7 +658,7 @@ io_error:
|
|||
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
|
||||
}
|
||||
|
||||
asmsub vload(str name @R0, ubyte bank @A, uword startaddress @R1) clobbers(X, Y) -> ubyte @A {
|
||||
asmsub vload(str name @R0, ubyte bank @A, uword startaddress @R1) clobbers(X, Y) -> bool @A {
|
||||
; -- like the basic command VLOAD "filename",drivenumber,bank,address
|
||||
; loads a file into Vera's video memory in the given bank:address, returns success in A
|
||||
; the file has to have the usual 2 byte header (which will be skipped)
|
||||
|
@ -697,7 +697,7 @@ internal_vload:
|
|||
}}
|
||||
}
|
||||
|
||||
asmsub vload_raw(str name @R0, ubyte bank @A, uword startaddress @R1) clobbers(X, Y) -> ubyte @A {
|
||||
asmsub vload_raw(str name @R0, ubyte bank @A, uword startaddress @R1) clobbers(X, Y) -> bool @A {
|
||||
; -- like the basic command BVLOAD "filename",drivenumber,bank,address
|
||||
; loads a file into Vera's video memory in the given bank:address, returns success in A
|
||||
; the file is read fully including the first two bytes.
|
||||
|
@ -754,7 +754,7 @@ internal_vload:
|
|||
repeat 6 {
|
||||
void cbm.CHRIN()
|
||||
}
|
||||
while cbm.CHRIN() {
|
||||
while cbm.CHRIN()!=0 {
|
||||
; skip first line (drive label)
|
||||
}
|
||||
while cbm.CHRIN()!='"' {
|
||||
|
@ -785,7 +785,7 @@ internal_vload:
|
|||
io_error:
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
if status and status & $40 == 0
|
||||
if status!=0 and status & $40 == 0
|
||||
return 0
|
||||
if @(cx16.r12)==0 {
|
||||
cx16.r12--
|
||||
|
|
|
@ -62,7 +62,7 @@ gfx2 {
|
|||
}
|
||||
|
||||
init_mode(mode)
|
||||
if active_mode
|
||||
if active_mode!=0
|
||||
clear_screen(0)
|
||||
}
|
||||
|
||||
|
@ -305,17 +305,17 @@ gfx2 {
|
|||
}
|
||||
|
||||
word @zp d = 0
|
||||
cx16.r1L = true ; 'positive_ix'
|
||||
cx16.r1L = 1 ;; true ; 'positive_ix'
|
||||
if dx < 0 {
|
||||
dx = -dx
|
||||
cx16.r1L = false
|
||||
cx16.r1L = 0 ;; false
|
||||
}
|
||||
word @zp dx2 = dx*2
|
||||
word @zp dy2 = dy*2
|
||||
cx16.r14 = x1 ; internal plot X
|
||||
|
||||
if dx >= dy {
|
||||
if cx16.r1L {
|
||||
if cx16.r1L!=0 {
|
||||
repeat {
|
||||
plot(cx16.r14, y1, color)
|
||||
if cx16.r14==x2
|
||||
|
@ -342,7 +342,7 @@ gfx2 {
|
|||
}
|
||||
}
|
||||
else {
|
||||
if cx16.r1L {
|
||||
if cx16.r1L!=0 {
|
||||
repeat {
|
||||
plot(cx16.r14, y1, color)
|
||||
if y1 == y2
|
||||
|
@ -705,7 +705,7 @@ gfx2 {
|
|||
push_stack(xx, xx, yy, 1)
|
||||
push_stack(xx, xx, yy + 1, -1)
|
||||
word left = 0
|
||||
while cx16.r12L {
|
||||
while cx16.r12L!=0 {
|
||||
pop_stack()
|
||||
xx = x1
|
||||
; possible speed optimization: if mode==1 (256c) use vera autodecrement instead of pget(), but code bloat not worth it?
|
||||
|
@ -725,15 +725,15 @@ gfx2 {
|
|||
xx = x1 + 1
|
||||
|
||||
do {
|
||||
cx16.r9 = xx
|
||||
cx16.r9s = xx
|
||||
; possible speed optimization: if mode==1 (256c) use vera autoincrement instead of pget(), but code bloat not worth it?
|
||||
while xx <= width-1 {
|
||||
if pget(xx as uword, yy as uword) != cx16.r11L
|
||||
break
|
||||
xx++
|
||||
}
|
||||
if cx16.r9!=xx
|
||||
horizontal_line(cx16.r9, yy as uword, (xx as uword)-cx16.r9, cx16.r10L)
|
||||
if cx16.r9s!=xx
|
||||
horizontal_line(cx16.r9, yy as uword, xx-cx16.r9s as uword, cx16.r10L)
|
||||
|
||||
push_stack(left, xx - 1, yy, dy)
|
||||
if xx > x2 + 1
|
||||
|
@ -845,7 +845,7 @@ skip:
|
|||
when active_mode {
|
||||
1 -> {
|
||||
; lores 256c
|
||||
while @(sctextptr) {
|
||||
while @(sctextptr)!=0 {
|
||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||
cx16.vaddr(charset_bank, chardataptr, 1, 1)
|
||||
repeat 8 {
|
||||
|
@ -874,9 +874,9 @@ skip:
|
|||
; hires 4c
|
||||
; we're going to use a few cx16 registers to make sure every variable is in zeropage in the inner loop.
|
||||
cx16.r11L = color
|
||||
while @(sctextptr) {
|
||||
while @(sctextptr)!=0 {
|
||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||
cx16.vaddr(charset_bank, chardataptr, 1, true) ; for reading the chardata from Vera data channel 1
|
||||
cx16.vaddr(charset_bank, chardataptr, 1, 1) ; for reading the chardata from Vera data channel 1
|
||||
position(xx, yy) ; only calculated once, we update vera address in the loop instead
|
||||
cx16.VERA_ADDR_H &= $0f ; no auto increment
|
||||
repeat 8 {
|
||||
|
|
|
@ -57,20 +57,20 @@ graphics {
|
|||
}
|
||||
|
||||
sub fillrect(uword xx, uword yy, uword width, uword height) {
|
||||
cx16.GRAPH_draw_rect(xx, yy, width, height, 0, 1)
|
||||
cx16.GRAPH_draw_rect(xx, yy, width, height, 0, true)
|
||||
}
|
||||
|
||||
sub rect(uword xx, uword yy, uword width, uword height) {
|
||||
cx16.GRAPH_draw_rect(xx, yy, width, height, 0, 0)
|
||||
cx16.GRAPH_draw_rect(xx, yy, width, height, 0, false)
|
||||
}
|
||||
|
||||
sub horizontal_line(uword xx, uword yy, uword length) {
|
||||
if length
|
||||
if length!=0
|
||||
cx16.GRAPH_draw_line(xx, yy, xx+length-1, yy)
|
||||
}
|
||||
|
||||
sub vertical_line(uword xx, uword yy, uword height) {
|
||||
if height
|
||||
if height!=0
|
||||
cx16.GRAPH_draw_line(xx, yy, xx, yy+height-1)
|
||||
}
|
||||
|
||||
|
|
|
@ -168,7 +168,7 @@ _clear
|
|||
}
|
||||
|
||||
ubyte separate_pixels = (8-lsb(xx)) & 7
|
||||
if separate_pixels {
|
||||
if separate_pixels!=0 {
|
||||
when mode {
|
||||
MODE_NORMAL -> {
|
||||
position(xx,yy)
|
||||
|
@ -197,7 +197,7 @@ _clear
|
|||
}
|
||||
length -= separate_pixels
|
||||
}
|
||||
if length {
|
||||
if length!=0 {
|
||||
position(xx, yy)
|
||||
separate_pixels = lsb(length) & 7
|
||||
xx += length & $fff8
|
||||
|
@ -395,17 +395,17 @@ drawmode: ora cx16.r15L
|
|||
}
|
||||
|
||||
word @zp d = 0
|
||||
cx16.r1L = true ; 'positive_ix'
|
||||
cx16.r1L = 1 ;; true ; 'positive_ix'
|
||||
if dx < 0 {
|
||||
dx = -dx
|
||||
cx16.r1L = false
|
||||
cx16.r1L = 0 ;; false
|
||||
}
|
||||
word @zp dx2 = dx*2
|
||||
word @zp dy2 = dy*2
|
||||
cx16.r14 = x1 ; internal plot X
|
||||
|
||||
if dx >= dy {
|
||||
if cx16.r1L {
|
||||
if cx16.r1L!=0 {
|
||||
repeat {
|
||||
plot(cx16.r14, y1, draw)
|
||||
if cx16.r14==x2
|
||||
|
@ -432,7 +432,7 @@ drawmode: ora cx16.r15L
|
|||
}
|
||||
}
|
||||
else {
|
||||
if cx16.r1L {
|
||||
if cx16.r1L!=0 {
|
||||
repeat {
|
||||
plot(cx16.r14, y1, draw)
|
||||
if y1 == y2
|
||||
|
@ -678,7 +678,7 @@ invert:
|
|||
plot(xx, yy, draw)
|
||||
}
|
||||
|
||||
sub pget(uword @zp xx, uword yy) -> ubyte {
|
||||
sub pget(uword @zp xx, uword yy) -> bool {
|
||||
%asm {{
|
||||
lda p8v_xx
|
||||
and #7
|
||||
|
@ -780,7 +780,7 @@ invert:
|
|||
}}
|
||||
yy+=dy
|
||||
}
|
||||
cx16.r11L = pget(xx as uword, yy as uword) ; old_color
|
||||
cx16.r11L = pget(xx as uword, yy as uword) as ubyte ; old_color
|
||||
if cx16.r11L == cx16.r10L
|
||||
return
|
||||
if xx<0 or xx>width-1 or yy<0 or yy>height-1
|
||||
|
@ -788,16 +788,16 @@ invert:
|
|||
push_stack(xx, xx, yy, 1)
|
||||
push_stack(xx, xx, yy + 1, -1)
|
||||
word left = 0
|
||||
while cx16.r12L {
|
||||
while cx16.r12L!=0 {
|
||||
pop_stack()
|
||||
xx = x1
|
||||
while xx >= 0 {
|
||||
if pget(xx as uword, yy as uword) != cx16.r11L
|
||||
if pget(xx as uword, yy as uword) as ubyte != cx16.r11L
|
||||
break
|
||||
xx--
|
||||
}
|
||||
if x1!=xx
|
||||
horizontal_line(xx as uword+1, yy as uword, x1-xx as uword, cx16.r10L)
|
||||
horizontal_line(xx as uword+1, yy as uword, x1-xx as uword, cx16.r10L as bool)
|
||||
else
|
||||
goto skip
|
||||
|
||||
|
@ -807,14 +807,14 @@ invert:
|
|||
xx = x1 + 1
|
||||
|
||||
do {
|
||||
cx16.r9 = xx
|
||||
cx16.r9s = xx
|
||||
while xx <= width-1 {
|
||||
if pget(xx as uword, yy as uword) != cx16.r11L
|
||||
if pget(xx as uword, yy as uword) as ubyte != cx16.r11L
|
||||
break
|
||||
xx++
|
||||
}
|
||||
if cx16.r9!=xx
|
||||
horizontal_line(cx16.r9, yy as uword, (xx as uword)-cx16.r9, cx16.r10L)
|
||||
if cx16.r9s!=xx
|
||||
horizontal_line(cx16.r9, yy as uword, xx-cx16.r9s as uword, cx16.r10L as bool)
|
||||
|
||||
push_stack(left, xx - 1, yy, dy)
|
||||
if xx > x2 + 1
|
||||
|
@ -822,7 +822,7 @@ invert:
|
|||
skip:
|
||||
xx++
|
||||
while xx <= x2 {
|
||||
if pget(xx as uword, yy as uword) == cx16.r11L
|
||||
if pget(xx as uword, yy as uword) as ubyte == cx16.r11L
|
||||
break
|
||||
xx++
|
||||
}
|
||||
|
@ -874,7 +874,7 @@ skip:
|
|||
sta cdraw_mod2
|
||||
}}
|
||||
|
||||
while @(cx16.r3) {
|
||||
while @(cx16.r3)!=0 {
|
||||
chardataptr = charset_addr + @(cx16.r3) * $0008
|
||||
; copy the character bitmap into RAM
|
||||
cx16.vaddr_autoincr(charset_bank, chardataptr, 0, 1)
|
||||
|
@ -926,7 +926,7 @@ cdraw_mod1 ora cx16.VERA_DATA1
|
|||
}}
|
||||
}
|
||||
; right part of shifted char
|
||||
if lsb(xx) & 7 {
|
||||
if lsb(xx) & 7 !=0 {
|
||||
position2(xx+8, yy, true)
|
||||
set_autoincrs()
|
||||
if draw {
|
||||
|
|
|
@ -134,7 +134,7 @@ psg {
|
|||
}
|
||||
1 -> {
|
||||
; sustain
|
||||
if envelope_sustains[cx16.r1L] {
|
||||
if envelope_sustains[cx16.r1L]!=0 {
|
||||
envelope_sustains[cx16.r1L]--
|
||||
} else {
|
||||
envelope_states[cx16.r1L] = 2 ; start release
|
||||
|
@ -143,7 +143,7 @@ psg {
|
|||
2 -> {
|
||||
; release
|
||||
cx16.r0 = envelope_volumes[cx16.r1L] - envelope_releases[cx16.r1L] * $0040
|
||||
if msb(cx16.r0) & %11000000 {
|
||||
if msb(cx16.r0) & %11000000 !=0 {
|
||||
cx16.r0 = 0
|
||||
envelope_releases[cx16.r1L] = 0
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ sprites {
|
|||
sprite_reg = VERA_SPRITEREGS + spritenum*$0008
|
||||
cx16.r0L = cx16.vpeek(1, sprite_reg)
|
||||
cx16.r0H = cx16.vpeek(1, sprite_reg+1)
|
||||
cx16.r1L = cx16.r0H & %00001000 !=0 ; bank
|
||||
cx16.r1L = cx16.r0H & %00001000 !=0 as ubyte ; bank
|
||||
cx16.r0 <<= 5 ; address
|
||||
}
|
||||
|
||||
|
@ -96,8 +96,8 @@ sprites {
|
|||
sub move(ubyte spritenum, word dx, word dy) {
|
||||
; move a sprite based on its current position
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + spritenum*$0008
|
||||
cx16.r1 = mkword(cx16.vpeek(1, sprite_reg+1), cx16.vpeek(1, sprite_reg)) as word + dx
|
||||
cx16.r2 = mkword(cx16.vpeek(1, sprite_reg+3), cx16.vpeek(1, sprite_reg+2)) as word + dy
|
||||
cx16.r1s = mkword(cx16.vpeek(1, sprite_reg+1), cx16.vpeek(1, sprite_reg)) as word + dx
|
||||
cx16.r2s = mkword(cx16.vpeek(1, sprite_reg+3), cx16.vpeek(1, sprite_reg+2)) as word + dy
|
||||
cx16.vpoke(1, sprite_reg, cx16.r1L)
|
||||
cx16.vpoke(1, sprite_reg+1, cx16.r1H)
|
||||
cx16.vpoke(1, sprite_reg+2, cx16.r2L)
|
||||
|
@ -107,7 +107,7 @@ sprites {
|
|||
sub movex(ubyte spritenum, word dx) {
|
||||
; move a sprite horizontally based on its current position
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + spritenum*$0008
|
||||
cx16.r1 = mkword(cx16.vpeek(1, sprite_reg+1), cx16.vpeek(1, sprite_reg)) as word + dx
|
||||
cx16.r1s = mkword(cx16.vpeek(1, sprite_reg+1), cx16.vpeek(1, sprite_reg)) as word + dx
|
||||
cx16.vpoke(1, sprite_reg, cx16.r1L)
|
||||
cx16.vpoke(1, sprite_reg+1, cx16.r1H)
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ sprites {
|
|||
sub movey(ubyte spritenum, word dy) {
|
||||
; move a sprite vertically based on its current position
|
||||
sprite_reg = VERA_SPRITEREGS + 4 + spritenum*$0008
|
||||
cx16.r1 = mkword(cx16.vpeek(1, sprite_reg+1), cx16.vpeek(1, sprite_reg)) as word + dy
|
||||
cx16.r1s = mkword(cx16.vpeek(1, sprite_reg+1), cx16.vpeek(1, sprite_reg)) as word + dy
|
||||
cx16.vpoke(1, sprite_reg, cx16.r1L)
|
||||
cx16.vpoke(1, sprite_reg+1, cx16.r1H)
|
||||
}
|
||||
|
@ -135,11 +135,11 @@ sprites {
|
|||
}
|
||||
|
||||
sub flipx(ubyte spritenum, bool flipped) {
|
||||
cx16.vpoke_mask(1, VERA_SPRITEREGS + 6 + spritenum*$0008, %11111110, flipped)
|
||||
cx16.vpoke_mask(1, VERA_SPRITEREGS + 6 + spritenum*$0008, %11111110, flipped as ubyte)
|
||||
}
|
||||
|
||||
sub flipy(ubyte spritenum, bool flipped) {
|
||||
cx16.vpoke_mask(1, VERA_SPRITEREGS + 6 + spritenum*$0008, %11111101, flipped<<1)
|
||||
cx16.vpoke_mask(1, VERA_SPRITEREGS + 6 + spritenum*$0008, %11111101, (flipped as ubyte)<<1)
|
||||
}
|
||||
|
||||
sub set_palette_offset(ubyte spritenum, ubyte offset) {
|
||||
|
|
|
@ -53,8 +53,8 @@ romsub $FFF3 = IOBASE() -> uword @ XY ; read base addr
|
|||
|
||||
; ---- utility
|
||||
|
||||
asmsub STOP2() clobbers(X) -> ubyte @A {
|
||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||
asmsub STOP2() clobbers(X) -> bool @A {
|
||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the zero status flag.
|
||||
%asm {{
|
||||
jsr cbm.STOP
|
||||
beq +
|
||||
|
@ -556,7 +556,7 @@ asmsub mouse_config2(byte shape @A) clobbers (A, X, Y) {
|
|||
|
||||
asmsub mouse_pos() clobbers(X) -> ubyte @A {
|
||||
; -- short wrapper around mouse_get() kernal routine:
|
||||
; -- gets the position of the mouse cursor in cx16.r0 and cx16.r1 (x/y coordinate), returns mouse button status.
|
||||
; -- gets the position of the mouse cursor in cx16.r0 and cx16.r1 (x/y coordinate), returns mouse button status in A.
|
||||
%asm {{
|
||||
ldx #cx16.r0
|
||||
jmp cx16.mouse_get
|
||||
|
|
|
@ -15,10 +15,10 @@ verafx {
|
|||
; Vera version number is valid.
|
||||
; Vera fx is available on Vera version 0.3.1 and later,
|
||||
; so no need to even check VERA_DC_VER1, which contains 0 (or higher)
|
||||
cx16.r1L = mkword(cx16.VERA_DC_VER2, cx16.VERA_DC_VER3) >= $0301
|
||||
cx16.r1L = mkword(cx16.VERA_DC_VER2, cx16.VERA_DC_VER3) >= $0301 as ubyte
|
||||
}
|
||||
cx16.VERA_CTRL = cx16.r0L
|
||||
return cx16.r1L
|
||||
return cx16.r1L as bool
|
||||
}
|
||||
|
||||
sub clear(ubyte vbank, uword vaddr, ubyte data, uword num_longwords) {
|
||||
|
|
|
@ -68,7 +68,7 @@ io_error:
|
|||
cbm.CLRCHN() ; restore default i/o devices
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
|
||||
if status and status & $40 == 0 { ; bit 6=end of file
|
||||
if status!=0 and status & $40 == 0 { ; bit 6=end of file
|
||||
txt.print("\ni/o error, status: ")
|
||||
txt.print_ub(status)
|
||||
txt.nl()
|
||||
|
@ -83,7 +83,7 @@ io_error:
|
|||
|
||||
cbm.SETNAM(1, "$")
|
||||
cbm.SETLFS(READ_IO_CHANNEL, drivenumber, 0)
|
||||
ubyte okay = false
|
||||
bool okay = false
|
||||
void cbm.OPEN() ; open 12,8,0,"$"
|
||||
if_cs
|
||||
goto io_error
|
||||
|
@ -201,14 +201,14 @@ io_error:
|
|||
ubyte blocks_lsb = cbm.CHRIN()
|
||||
ubyte blocks_msb = cbm.CHRIN()
|
||||
|
||||
if cbm.READST()
|
||||
if cbm.READST()!=0
|
||||
goto close_end
|
||||
|
||||
list_blocks = mkword(blocks_msb, blocks_lsb)
|
||||
|
||||
; read until the filename starts after the first "
|
||||
while cbm.CHRIN()!='\"' {
|
||||
if cbm.READST()
|
||||
if cbm.READST()!=0
|
||||
goto close_end
|
||||
}
|
||||
|
||||
|
@ -231,7 +231,7 @@ io_error:
|
|||
list_filetype[0] = cx16.r15L
|
||||
list_filetype[1] = cbm.CHRIN()
|
||||
list_filetype[2] = cbm.CHRIN()
|
||||
while cbm.CHRIN() {
|
||||
while cbm.CHRIN()!=0 {
|
||||
; read the rest of the entry until the end
|
||||
}
|
||||
|
||||
|
@ -239,7 +239,7 @@ io_error:
|
|||
void cbm.CHRIN()
|
||||
|
||||
if not list_skip_disk_name {
|
||||
if not list_pattern
|
||||
if list_pattern==0
|
||||
return true
|
||||
if string.pattern_match(list_filename, list_pattern)
|
||||
return true
|
||||
|
@ -297,7 +297,7 @@ close_end:
|
|||
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
|
||||
; -- read from the currently open file, up to the given number of bytes.
|
||||
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
||||
if not iteration_in_progress or not num_bytes
|
||||
if not iteration_in_progress or num_bytes==0
|
||||
return 0
|
||||
|
||||
reset_read_channel()
|
||||
|
@ -309,10 +309,10 @@ close_end:
|
|||
lda bufferpointer+1
|
||||
sta m_in_buffer+2
|
||||
}}
|
||||
while num_bytes {
|
||||
if cbm.READST() {
|
||||
while num_bytes!=0 {
|
||||
if cbm.READST()!=0 {
|
||||
f_close()
|
||||
if cbm.READST() & $40 ; eof?
|
||||
if cbm.READST() & $40 !=0 ; eof?
|
||||
return list_blocks ; number of bytes read
|
||||
return 0 ; error.
|
||||
}
|
||||
|
@ -337,7 +337,7 @@ m_in_buffer sta $ffff
|
|||
|
||||
reset_read_channel()
|
||||
uword total_read = 0
|
||||
while not cbm.READST() {
|
||||
while cbm.READST()==0 {
|
||||
cx16.r0 = f_read(bufferpointer, 256)
|
||||
total_read += cx16.r0
|
||||
bufferpointer += cx16.r0
|
||||
|
@ -396,7 +396,7 @@ _end rts
|
|||
cbm.SETLFS(WRITE_IO_CHANNEL, drivenumber, 1)
|
||||
void cbm.OPEN() ; open 13,8,1,"filename"
|
||||
if_cc
|
||||
return not cbm.READST()
|
||||
return cbm.READST()==0
|
||||
cbm.CLOSE(WRITE_IO_CHANNEL)
|
||||
f_close_w()
|
||||
return false
|
||||
|
@ -410,7 +410,7 @@ _end rts
|
|||
cbm.CHROUT(@(bufferpointer))
|
||||
bufferpointer++
|
||||
}
|
||||
return not cbm.READST()
|
||||
return cbm.READST()==0
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -440,7 +440,7 @@ _end rts
|
|||
goto io_error
|
||||
void cbm.CHKIN(15) ; use #15 as input channel
|
||||
|
||||
while not cbm.READST() {
|
||||
while cbm.READST()==0 {
|
||||
cx16.r5L = cbm.CHRIN()
|
||||
if cx16.r5L=='\r' or cx16.r5L=='\n'
|
||||
break
|
||||
|
@ -480,9 +480,9 @@ io_error:
|
|||
}}
|
||||
|
||||
if_cc
|
||||
cx16.r0L = cbm.READST()==0
|
||||
cx16.r0L = cbm.READST()==0 as ubyte
|
||||
|
||||
return cx16.r0L
|
||||
return cx16.r0L as bool
|
||||
}
|
||||
|
||||
; Use kernal LOAD routine to load the given program file in memory.
|
||||
|
@ -495,7 +495,7 @@ io_error:
|
|||
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||
ubyte secondary = 1
|
||||
cx16.r1 = 0
|
||||
if address_override
|
||||
if address_override!=0
|
||||
secondary = 0
|
||||
cbm.SETLFS(1, drivenumber, secondary)
|
||||
%asm {{
|
||||
|
|
|
@ -509,7 +509,7 @@ log2_tab
|
|||
repeat length {
|
||||
cx16.r0H ^= @(cx16.r1)
|
||||
repeat 8 {
|
||||
if cx16.r0H & $80 {
|
||||
if cx16.r0H & $80 !=0 {
|
||||
cx16.r0 <<= 1
|
||||
cx16.r0 ^= $1021
|
||||
}
|
||||
|
@ -531,7 +531,7 @@ log2_tab
|
|||
repeat length {
|
||||
cx16.r1H ^= @(cx16.r2)
|
||||
repeat 8 {
|
||||
if cx16.r1H & $80 {
|
||||
if cx16.r1H & $80 !=0 {
|
||||
cx16.r0 <<= 1
|
||||
rol(cx16.r1)
|
||||
cx16.r1 ^= $04c1
|
||||
|
|
|
@ -34,7 +34,7 @@ romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; get a characte
|
|||
romsub $FFE7 = CLALL() clobbers(A,X) ; close all files
|
||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||
|
||||
asmsub STOP2() clobbers(X) -> ubyte @A {
|
||||
asmsub STOP2() clobbers(X) -> bool @A {
|
||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||
%asm {{
|
||||
jsr cbm.STOP
|
||||
|
|
|
@ -36,4 +36,11 @@ _offsets .byte 128, 0, 64, 32, 64, 192, 128, 128
|
|||
}
|
||||
|
||||
|
||||
sub print_bool(bool value) {
|
||||
if value
|
||||
txt.print("true")
|
||||
else
|
||||
txt.print("false")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -297,7 +297,7 @@ _done rts
|
|||
return compare(st + str_len - suffix_len, suffix) == 0
|
||||
}
|
||||
|
||||
asmsub pattern_match(str string @AY, str pattern @R0) clobbers(Y) -> ubyte @A {
|
||||
asmsub pattern_match(str string @AY, str pattern @R0) clobbers(Y) -> bool @A {
|
||||
%asm {{
|
||||
; pattern matching of a string.
|
||||
; Input: cx16.r0: A NUL-terminated, <255-length pattern
|
||||
|
|
|
@ -44,9 +44,9 @@ sub internal_str_ub(ubyte value, uword out_ptr) {
|
|||
value -= hundreds*100
|
||||
ubyte tens = value / 10
|
||||
value -= tens*10
|
||||
if hundreds
|
||||
if hundreds!=0
|
||||
goto output_hundreds
|
||||
if tens
|
||||
if tens!=0
|
||||
goto output_tens
|
||||
goto output_ones
|
||||
output_hundreds:
|
||||
|
@ -163,13 +163,13 @@ sub internal_str_uw(uword value, uword out_ptr) {
|
|||
ubyte thousands = value4-value5*10 as ubyte
|
||||
uword value6 = value5/10
|
||||
ubyte tenthousands = value5-value6*10 as ubyte
|
||||
if tenthousands
|
||||
if tenthousands!=0
|
||||
goto output_tenthousands
|
||||
if thousands
|
||||
if thousands!=0
|
||||
goto output_thousands
|
||||
if hundreds
|
||||
if hundreds!=0
|
||||
goto output_hundreds
|
||||
if tens
|
||||
if tens!=0
|
||||
goto output_tens
|
||||
goto output_ones
|
||||
output_tenthousands:
|
||||
|
|
|
@ -208,7 +208,7 @@ math {
|
|||
ubyte result = 7
|
||||
ubyte compare = $80
|
||||
repeat {
|
||||
if value&compare
|
||||
if value&compare!=0
|
||||
return result
|
||||
result--
|
||||
if_z
|
||||
|
@ -221,7 +221,7 @@ math {
|
|||
ubyte result = 15
|
||||
uword compare = $8000
|
||||
repeat {
|
||||
if value&compare
|
||||
if value&compare!=0
|
||||
return result
|
||||
result--
|
||||
if_z
|
||||
|
@ -317,7 +317,7 @@ math {
|
|||
repeat length {
|
||||
cx16.r0H ^= @(data)
|
||||
repeat 8 {
|
||||
if cx16.r0H & $80
|
||||
if cx16.r0H & $80 !=0
|
||||
cx16.r0 = (cx16.r0<<1)^$1021
|
||||
else
|
||||
cx16.r0<<=1
|
||||
|
@ -336,7 +336,7 @@ math {
|
|||
repeat length {
|
||||
cx16.r1H ^= @(data)
|
||||
repeat 8 {
|
||||
if cx16.r1H & $80 {
|
||||
if cx16.r1H & $80 !=0 {
|
||||
cx16.r0 <<= 1
|
||||
rol(cx16.r1)
|
||||
cx16.r1 ^= $04c1
|
||||
|
|
|
@ -44,7 +44,7 @@ monogfx {
|
|||
}
|
||||
|
||||
sub clear_screen(ubyte color) {
|
||||
if color
|
||||
if color!=0
|
||||
color=255
|
||||
sys.gfx_clear(color)
|
||||
}
|
||||
|
@ -130,17 +130,17 @@ monogfx {
|
|||
}
|
||||
|
||||
word @zp d = 0
|
||||
cx16.r1L = true ; 'positive_ix'
|
||||
cx16.r1L = 1 ; true ; 'positive_ix'
|
||||
if dx < 0 {
|
||||
dx = -dx
|
||||
cx16.r1L = false
|
||||
cx16.r1L = 0 ; false
|
||||
}
|
||||
word @zp dx2 = dx*2
|
||||
word @zp dy2 = dy*2
|
||||
cx16.r14 = x1 ; internal plot X
|
||||
|
||||
if dx >= dy {
|
||||
if cx16.r1L {
|
||||
if cx16.r1L!=0 {
|
||||
repeat {
|
||||
plot(cx16.r14, y1, draw)
|
||||
if cx16.r14==x2
|
||||
|
@ -167,7 +167,7 @@ monogfx {
|
|||
}
|
||||
}
|
||||
else {
|
||||
if cx16.r1L {
|
||||
if cx16.r1L!=0 {
|
||||
repeat {
|
||||
plot(cx16.r14, y1, draw)
|
||||
if y1 == y2
|
||||
|
@ -346,7 +346,7 @@ monogfx {
|
|||
sys.gfx_plot(xx, yy, 255)
|
||||
}
|
||||
MODE_STIPPLE -> {
|
||||
if (xx ^ yy)&1
|
||||
if (xx ^ yy)&1 !=0
|
||||
sys.gfx_plot(xx, yy, 255)
|
||||
else
|
||||
sys.gfx_plot(xx, yy, 0)
|
||||
|
@ -369,8 +369,8 @@ monogfx {
|
|||
plot(xx, yy, draw)
|
||||
}
|
||||
|
||||
sub pget(uword @zp xx, uword yy) -> ubyte {
|
||||
return sys.gfx_getpixel(xx, yy)
|
||||
sub pget(uword @zp xx, uword yy) -> bool {
|
||||
return sys.gfx_getpixel(xx, yy) as bool
|
||||
}
|
||||
|
||||
sub fill(uword x, uword y, bool draw) {
|
||||
|
@ -417,16 +417,16 @@ monogfx {
|
|||
push_stack(xx, xx, yy, 1)
|
||||
push_stack(xx, xx, yy + 1, -1)
|
||||
word left = 0
|
||||
while cx16.r12L {
|
||||
while cx16.r12L!=0 {
|
||||
pop_stack()
|
||||
xx = x1
|
||||
while xx >= 0 {
|
||||
if pget(xx as uword, yy as uword) != cx16.r11L
|
||||
if pget(xx as uword, yy as uword) as ubyte != cx16.r11L
|
||||
break
|
||||
xx--
|
||||
}
|
||||
if x1!=xx
|
||||
horizontal_line(xx as uword+1, yy as uword, x1-xx as uword, cx16.r10L)
|
||||
horizontal_line(xx as uword+1, yy as uword, x1-xx as uword, cx16.r10L as bool)
|
||||
else
|
||||
goto skip
|
||||
|
||||
|
@ -436,14 +436,14 @@ monogfx {
|
|||
xx = x1 + 1
|
||||
|
||||
do {
|
||||
cx16.r9 = xx
|
||||
cx16.r9 = xx as uword
|
||||
while xx <= width-1 {
|
||||
if pget(xx as uword, yy as uword) != cx16.r11L
|
||||
if pget(xx as uword, yy as uword) as ubyte != cx16.r11L
|
||||
break
|
||||
xx++
|
||||
}
|
||||
if cx16.r9!=xx
|
||||
horizontal_line(cx16.r9, yy as uword, (xx as uword)-cx16.r9, cx16.r10L)
|
||||
horizontal_line(cx16.r9, yy as uword, (xx as uword)-cx16.r9, cx16.r10L as bool)
|
||||
|
||||
push_stack(left, xx - 1, yy, dy)
|
||||
if xx > x2 + 1
|
||||
|
@ -451,7 +451,7 @@ monogfx {
|
|||
skip:
|
||||
xx++
|
||||
while xx <= x2 {
|
||||
if pget(xx as uword, yy as uword) == cx16.r11L
|
||||
if pget(xx as uword, yy as uword) as ubyte == cx16.r11L
|
||||
break
|
||||
xx++
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ string {
|
|||
; This value is determined during runtime and counts upto the first terminating 0 byte in the string,
|
||||
; regardless of the size of the string during compilation time. Don’t confuse this with len and sizeof!
|
||||
ubyte count = 0
|
||||
while st[count]
|
||||
while st[count]!=0
|
||||
count++
|
||||
return count
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ string {
|
|||
ubyte ix
|
||||
repeat {
|
||||
ubyte char=st[ix]
|
||||
if not char
|
||||
if char==0
|
||||
return ix
|
||||
if char >= 'A' and char <= 'Z'
|
||||
st[ix] = char | %00100000
|
||||
|
@ -127,7 +127,7 @@ string {
|
|||
ubyte ix
|
||||
repeat {
|
||||
ubyte char=st[ix]
|
||||
if not char
|
||||
if char==0
|
||||
return ix
|
||||
if char >= 97 and char <= 122
|
||||
st[ix] = char & %11011111
|
||||
|
@ -175,7 +175,7 @@ string {
|
|||
ubyte ix
|
||||
sys.clear_carry()
|
||||
repeat {
|
||||
if st[ix] {
|
||||
if st[ix]!=0 {
|
||||
rol(hashcode)
|
||||
hashcode ^= st[ix]
|
||||
ix++
|
||||
|
|
10
compiler/res/prog8lib/virtual/test_stack.p8
Normal file
10
compiler/res/prog8lib/virtual/test_stack.p8
Normal file
|
@ -0,0 +1,10 @@
|
|||
; dummy function on the VM
|
||||
|
||||
%import textio
|
||||
|
||||
test_stack {
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
sub test() {
|
||||
}
|
||||
}
|
|
@ -74,30 +74,37 @@ sub print_b (byte value) {
|
|||
print(conv.str_b(value))
|
||||
}
|
||||
|
||||
sub print_bool(bool value) {
|
||||
if value
|
||||
print("true")
|
||||
else
|
||||
print("false")
|
||||
}
|
||||
|
||||
sub print_ubhex (ubyte value, ubyte prefix) {
|
||||
; ---- print the ubyte in hex form
|
||||
if prefix
|
||||
if prefix!=0
|
||||
chrout('$')
|
||||
print(conv.str_ubhex(value))
|
||||
}
|
||||
|
||||
sub print_ubbin (ubyte value, ubyte prefix) {
|
||||
; ---- print the ubyte in binary form
|
||||
if prefix
|
||||
if prefix!=0
|
||||
chrout('%')
|
||||
print(conv.str_ubbin(value))
|
||||
}
|
||||
|
||||
sub print_uwbin (uword value, ubyte prefix) {
|
||||
; ---- print the uword in binary form
|
||||
if prefix
|
||||
if prefix!=0
|
||||
chrout('%')
|
||||
print(conv.str_uwbin(value))
|
||||
}
|
||||
|
||||
sub print_uwhex (uword value, ubyte prefix) {
|
||||
; ---- print the uword in hexadecimal form (4 digits)
|
||||
if prefix
|
||||
if prefix!=0
|
||||
chrout('$')
|
||||
print(conv.str_uwhex(value))
|
||||
}
|
||||
|
@ -153,7 +160,7 @@ sub setchr (ubyte col, ubyte row, ubyte char) {
|
|||
|
||||
sub petscii2scr(ubyte petscii_char) -> ubyte {
|
||||
; -- convert petscii character to screencode
|
||||
byte[8] offsets = [128, 0, 64, 32, 64, 192, 128, 128]
|
||||
ubyte[8] offsets = [128, 0, 64, 32, 64, 192, 128, 128]
|
||||
return petscii_char ^ offsets[petscii_char>>5]
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
|
||||
val dumpVariables by cli.option(ArgType.Boolean, fullName = "dumpvars", description = "print a dump of the variables in the program")
|
||||
val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code")
|
||||
val noStrictBool by cli.option(ArgType.Boolean, fullName = "nostrictbool", description = "allow implicit conversions between bool and bytes")
|
||||
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform code optimizations")
|
||||
val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".")
|
||||
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
|
||||
|
@ -163,6 +164,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||
breakpointCpuInstruction,
|
||||
printAst1 == true,
|
||||
printAst2 == true,
|
||||
noStrictBool != true,
|
||||
processedSymbols,
|
||||
srcdirs,
|
||||
outputPath
|
||||
|
@ -242,6 +244,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||
breakpointCpuInstruction,
|
||||
printAst1 == true,
|
||||
printAst2 == true,
|
||||
noStrictBool != true,
|
||||
processedSymbols,
|
||||
srcdirs,
|
||||
outputPath
|
||||
|
|
|
@ -19,8 +19,8 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
|
|||
"sqrt__ubyte" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
|
||||
"sqrt__uword" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
|
||||
"sqrt__float" to { a, p, prg -> oneFloatArgOutputFloat(a, p, prg) { sqrt(it) } },
|
||||
"any" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) },
|
||||
"all" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
|
||||
"any" to { a, p, prg -> collectionArgBoolResult(a, p, prg) { array->array.any { it!=0.0 } } },
|
||||
"all" to { a, p, prg -> collectionArgBoolResult(a, p, prg) { array->array.all { it!=0.0 } } },
|
||||
"lsb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x and 255).toDouble() } },
|
||||
"msb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x ushr 8 and 255).toDouble()} },
|
||||
"mkword" to ::builtinMkword,
|
||||
|
@ -38,10 +38,6 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
|
|||
"max__word" to ::builtinMaxWord
|
||||
)
|
||||
|
||||
private fun builtinAny(array: List<Double>): Double = if(array.any { it!=0.0 }) 1.0 else 0.0
|
||||
|
||||
private fun builtinAll(array: List<Double>): Double = if(array.all { it!=0.0 }) 1.0 else 0.0
|
||||
|
||||
internal fun builtinFunctionReturnType(function: String): InferredTypes.InferredType {
|
||||
if(function in arrayOf("set_carry", "set_irqd", "clear_carry", "clear_irqd"))
|
||||
return InferredTypes.InferredType.void()
|
||||
|
@ -63,7 +59,7 @@ private fun oneIntArgOutputInt(args: List<Expression>, position: Position, progr
|
|||
if(args.size!=1)
|
||||
throw SyntaxError("built-in function requires one integer argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val allowedDt = if(signed) IntegerDatatypesNoBool else arrayOf(DataType.UBYTE, DataType.UWORD)
|
||||
val allowedDt = if(signed) IntegerDatatypes else arrayOf(DataType.UBYTE, DataType.UWORD)
|
||||
if(constval.type !in allowedDt)
|
||||
throw SyntaxError("built-in function requires one integer argument", position)
|
||||
|
||||
|
@ -81,7 +77,7 @@ private fun oneFloatArgOutputFloat(args: List<Expression>, position: Position, p
|
|||
return NumericLiteral(DataType.FLOAT, function(constval.number), args[0].position)
|
||||
}
|
||||
|
||||
private fun collectionArg(args: List<Expression>, position: Position, program: Program, function: (arg: List<Double>)->Double): NumericLiteral {
|
||||
private fun collectionArgBoolResult(args: List<Expression>, position: Position, program: Program, function: (arg: List<Double>)->Boolean): NumericLiteral {
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
||||
|
||||
|
@ -89,8 +85,7 @@ private fun collectionArg(args: List<Expression>, position: Position, program: P
|
|||
val constElements = array.value.map{it.constValue(program)?.number}
|
||||
if(constElements.contains(null))
|
||||
throw NotConstArgumentException()
|
||||
|
||||
return NumericLiteral.optimalNumeric(function(constElements.mapNotNull { it }), args[0].position)
|
||||
return NumericLiteral.fromBoolean(function(constElements.mapNotNull { it }), args[0].position)
|
||||
}
|
||||
|
||||
private fun builtinAbs(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
|
@ -100,7 +95,7 @@ private fun builtinAbs(args: List<Expression>, position: Position, program: Prog
|
|||
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
return when (constval.type) {
|
||||
in IntegerDatatypesNoBool -> NumericLiteral.optimalInteger(abs(constval.number.toInt()), args[0].position)
|
||||
in IntegerDatatypes -> NumericLiteral.optimalInteger(abs(constval.number.toInt()), args[0].position)
|
||||
else -> throw SyntaxError("abs requires one integer argument", position)
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +156,7 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
|||
val refLv = target.value as? StringLiteral ?: throw CannotEvaluateException("len", "stringsize unknown")
|
||||
NumericLiteral.optimalInteger(refLv.value.length, args[0].position)
|
||||
}
|
||||
in NumericDatatypes -> throw SyntaxError("cannot use len on numeric value, did you mean sizeof?", args[0].position)
|
||||
in NumericDatatypes, DataType.BOOL -> throw SyntaxError("cannot use len on numeric value, did you mean sizeof?", args[0].position)
|
||||
else -> throw InternalCompilerException("weird datatype")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ class CompilerArguments(val filepath: Path,
|
|||
val breakpointCpuInstruction: String?,
|
||||
val printAst1: Boolean,
|
||||
val printAst2: Boolean,
|
||||
val strictBool: Boolean,
|
||||
val symbolDefs: Map<String, String>,
|
||||
val sourceDirs: List<String> = emptyList(),
|
||||
val outputDir: Path = Path(""),
|
||||
|
@ -93,13 +94,14 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
|||
splitWordArrays = args.splitWordArrays
|
||||
outputDir = args.outputDir.normalize()
|
||||
symbolDefs = args.symbolDefs
|
||||
strictBool = args.strictBool
|
||||
}
|
||||
program = programresult
|
||||
importedFiles = imported
|
||||
|
||||
processAst(program, args.errors, compilationOptions)
|
||||
// println("*********** COMPILER AST RIGHT BEFORE OPTIMIZING *************")
|
||||
// printProgram(program)
|
||||
// println("*********** COMPILER AST RIGHT BEFORE OPTIMIZING *************")
|
||||
// printProgram(program)
|
||||
|
||||
if (compilationOptions.optimize) {
|
||||
optimizeAst(
|
||||
|
@ -404,7 +406,7 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions
|
|||
errors.report()
|
||||
program.reorderStatements(errors)
|
||||
errors.report()
|
||||
program.desugaring(errors)
|
||||
program.desugaring(errors, compilerOptions)
|
||||
errors.report()
|
||||
program.changeNotExpressionAndIfComparisonExpr(errors, compilerOptions.compTarget)
|
||||
errors.report()
|
||||
|
@ -430,7 +432,7 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
|
|||
removeUnusedCode(program, errors,compilerOptions)
|
||||
while (true) {
|
||||
// keep optimizing expressions and statements until no more steps remain
|
||||
val optsDone1 = program.simplifyExpressions(errors, compilerOptions.compTarget)
|
||||
val optsDone1 = program.simplifyExpressions(errors, compilerOptions)
|
||||
val optsDone2 = program.optimizeStatements(errors, functions, compilerOptions)
|
||||
val optsDone3 = program.inlineSubroutines(compilerOptions)
|
||||
program.constantFold(errors, compilerOptions) // because simplified statements and expressions can result in more constants that can be folded away
|
||||
|
@ -448,13 +450,13 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
|
|||
}
|
||||
|
||||
private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||
program.desugaring(errors)
|
||||
program.desugaring(errors, compilerOptions)
|
||||
program.addTypecasts(errors, compilerOptions)
|
||||
errors.report()
|
||||
program.variousCleanups(errors, compilerOptions)
|
||||
val callGraph = CallGraph(program)
|
||||
callGraph.checkRecursiveCalls(errors)
|
||||
program.verifyFunctionArgTypes(errors)
|
||||
program.verifyFunctionArgTypes(errors, compilerOptions)
|
||||
errors.report()
|
||||
program.moveMainBlockAsFirst()
|
||||
program.checkValid(errors, compilerOptions) // check if final tree is still valid
|
||||
|
|
|
@ -43,6 +43,7 @@ internal class ErrorReporter: IErrorReporter {
|
|||
if(msg !in alreadyReportedMessages) {
|
||||
when(it.severity) {
|
||||
MessageSeverity.ERROR -> {
|
||||
System.out.flush()
|
||||
printer.print("\u001b[91mERROR\u001B[0m ") // bright red
|
||||
numErrors++
|
||||
}
|
||||
|
@ -66,4 +67,6 @@ internal class ErrorReporter: IErrorReporter {
|
|||
}
|
||||
|
||||
override fun noErrors() = messages.none { it.severity==MessageSeverity.ERROR }
|
||||
override fun noErrorForLine(position: Position) = !messages.any { it.position.line==position.line && it.severity!=MessageSeverity.INFO }
|
||||
|
||||
}
|
||||
|
|
|
@ -123,12 +123,6 @@ internal class AstChecker(private val program: Program,
|
|||
if(valueDt istype DataType.BOOL && expectedReturnValues[0] == DataType.UBYTE) {
|
||||
// if the return value is a bool and the return type is ubyte, allow this. But give a warning.
|
||||
errors.info("return type of the subroutine should probably be bool instead of ubyte", returnStmt.position)
|
||||
} else if(valueDt istype DataType.UBYTE && expectedReturnValues[0] == DataType.BOOL) {
|
||||
// if the return value is ubyte and the return type is bool, allow this only if value is 0 or 1
|
||||
val returnValue = returnStmt.value as? NumericLiteral
|
||||
if (returnValue == null || returnValue.type != DataType.UBYTE || (returnValue.number!=0.0 && returnValue.number!=1.0)) {
|
||||
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position)
|
||||
}
|
||||
} else if(valueDt.isIterable && expectedReturnValues[0]==DataType.UWORD) {
|
||||
// you can return a string or array when an uword (pointer) is returned
|
||||
} else if(valueDt istype DataType.UWORD && expectedReturnValues[0]==DataType.STR) {
|
||||
|
@ -156,12 +150,17 @@ internal class AstChecker(private val program: Program,
|
|||
}
|
||||
|
||||
override fun visit(ifElse: IfElse) {
|
||||
val dt = ifElse.condition.inferType(program)
|
||||
if(!dt.isInteger && !dt.istype(DataType.BOOL)) {
|
||||
val identifier = ifElse.condition as? IdentifierReference
|
||||
if(identifier==null || identifier.targetStatement(program)!=null)
|
||||
errors.err("condition value should be an integer type or bool", ifElse.condition.position)
|
||||
if(!ifElse.condition.inferType(program).isBool) {
|
||||
val ctype = ifElse.condition.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(compilerOptions.strictBool || ctype !in ByteDatatypes)
|
||||
errors.err("condition should be a boolean", ifElse.condition.position)
|
||||
}
|
||||
|
||||
val constvalue = ifElse.condition.constValue(program)
|
||||
if(constvalue!=null) {
|
||||
errors.warn("condition is always ${constvalue.asBooleanValue}", ifElse.condition.position)
|
||||
}
|
||||
|
||||
super.visit(ifElse)
|
||||
}
|
||||
|
||||
|
@ -482,22 +481,22 @@ internal class AstChecker(private val program: Program,
|
|||
}
|
||||
|
||||
override fun visit(untilLoop: UntilLoop) {
|
||||
val dt = untilLoop.condition.inferType(program)
|
||||
if(!dt.isInteger && !dt.istype(DataType.BOOL)) {
|
||||
val identifier = untilLoop.condition as? IdentifierReference
|
||||
if(identifier==null || identifier.targetStatement(program)!=null)
|
||||
errors.err("condition value should be an integer type or bool", untilLoop.condition.position)
|
||||
if(!untilLoop.condition.inferType(program).isBool) {
|
||||
val ctype = untilLoop.condition.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(compilerOptions.strictBool || ctype !in ByteDatatypes)
|
||||
errors.err("condition should be a boolean", untilLoop.condition.position)
|
||||
}
|
||||
|
||||
super.visit(untilLoop)
|
||||
}
|
||||
|
||||
override fun visit(whileLoop: WhileLoop) {
|
||||
val dt = whileLoop.condition.inferType(program)
|
||||
if(!dt.isInteger && !dt.istype(DataType.BOOL)) {
|
||||
val identifier = whileLoop.condition as? IdentifierReference
|
||||
if(identifier==null || identifier.targetStatement(program)!=null)
|
||||
errors.err("condition value should be an integer type or bool", whileLoop.condition.position)
|
||||
if(!whileLoop.condition.inferType(program).isBool) {
|
||||
val ctype = whileLoop.condition.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(compilerOptions.strictBool || ctype !in ByteDatatypes)
|
||||
errors.err("condition should be a boolean", whileLoop.condition.position)
|
||||
}
|
||||
|
||||
super.visit(whileLoop)
|
||||
}
|
||||
|
||||
|
@ -541,20 +540,10 @@ internal class AstChecker(private val program: Program,
|
|||
if(numvalue!=null && targetDt.isKnown)
|
||||
checkValueTypeAndRange(targetDt.getOr(DataType.UNDEFINED), numvalue)
|
||||
|
||||
// for now, don't enforce bool type with only logical operators...
|
||||
// if(assignment.isAugmentable && targetDt istype DataType.BOOL) {
|
||||
// val operator = (assignment.value as? BinaryExpression)?.operator
|
||||
// if(operator in InvalidOperatorsForBoolean)
|
||||
// errors.err("can't use boolean operand with this operator $operator", assignment.position)
|
||||
// }
|
||||
|
||||
super.visit(assignment)
|
||||
}
|
||||
|
||||
override fun visit(assignTarget: AssignTarget) {
|
||||
if(assignTarget.inferType(program).istype(DataType.LONG))
|
||||
errors.err("integer overflow", assignTarget.position)
|
||||
|
||||
super.visit(assignTarget)
|
||||
|
||||
val memAddr = assignTarget.memoryAddress?.addressExpression?.constValue(program)?.number?.toInt()
|
||||
|
@ -588,16 +577,13 @@ internal class AstChecker(private val program: Program,
|
|||
if (assignment is Assignment) {
|
||||
val targetDatatype = assignTarget.inferType(program)
|
||||
if (targetDatatype.isKnown) {
|
||||
val constVal = assignment.value.constValue(program)
|
||||
if(constVal==null) {
|
||||
val sourceDatatype = assignment.value.inferType(program)
|
||||
if (sourceDatatype.isUnknown) {
|
||||
if (assignment.value !is FunctionCallExpression)
|
||||
errors.err("invalid assignment value, maybe forgot '&' (address-of)", assignment.value.position)
|
||||
} else {
|
||||
checkAssignmentCompatible(targetDatatype.getOr(DataType.UNDEFINED),
|
||||
sourceDatatype.getOr(DataType.UNDEFINED), assignment.value)
|
||||
}
|
||||
val sourceDatatype = assignment.value.inferType(program)
|
||||
if (sourceDatatype.isUnknown) {
|
||||
if (assignment.value !is BinaryExpression && assignment.value !is PrefixExpression && assignment.value !is ContainmentCheck)
|
||||
errors.err("invalid assignment value, maybe forgot '&' (address-of)", assignment.value.position)
|
||||
} else {
|
||||
checkAssignmentCompatible(targetDatatype.getOr(DataType.UNDEFINED),
|
||||
sourceDatatype.getOr(DataType.UNDEFINED), assignment.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -621,6 +607,10 @@ internal class AstChecker(private val program: Program,
|
|||
|
||||
if(decl.datatype==DataType.LONG)
|
||||
errors.err("integer overflow", decl.position)
|
||||
if(decl.type==VarDeclType.MEMORY) {
|
||||
if (decl.datatype == DataType.BOOL || decl.datatype == DataType.ARRAY_BOOL)
|
||||
errors.err("variables mapped in memory should be numeric", decl.position)
|
||||
}
|
||||
|
||||
fun err(msg: String) = errors.err(msg, decl.position)
|
||||
|
||||
|
@ -630,8 +620,8 @@ internal class AstChecker(private val program: Program,
|
|||
|
||||
// CONST can only occur on simple types (byte, word, float)
|
||||
if(decl.type== VarDeclType.CONST) {
|
||||
if (decl.datatype !in NumericDatatypes)
|
||||
err("const can only be used on numeric types (byte, word, float)")
|
||||
if (decl.datatype !in NumericDatatypesWithBoolean)
|
||||
err("const can only be used on numeric types or booleans")
|
||||
}
|
||||
|
||||
// FLOATS enabled?
|
||||
|
@ -718,12 +708,14 @@ internal class AstChecker(private val program: Program,
|
|||
if(decl.isArray) {
|
||||
val eltDt = ArrayToElementTypes.getValue(decl.datatype)
|
||||
if(iDt isnot eltDt) {
|
||||
if(!(iDt.isBool && eltDt==DataType.UBYTE || iDt.istype(DataType.UBYTE) && eltDt==DataType.BOOL))
|
||||
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})")
|
||||
if(compilerOptions.strictBool)
|
||||
err("initialisation value has incompatible type ($iDt) for the variable (${decl.datatype})")
|
||||
else if(!(iDt.isBool && eltDt==DataType.UBYTE || iDt.istype(DataType.UBYTE) && eltDt==DataType.BOOL))
|
||||
err("initialisation value has incompatible type ($iDt) for the variable (${decl.datatype})")
|
||||
}
|
||||
} else {
|
||||
if(!(iDt.isBool && decl.datatype==DataType.UBYTE || iDt.istype(DataType.UBYTE) && decl.datatype==DataType.BOOL))
|
||||
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})")
|
||||
err("initialisation value has incompatible type ($iDt) for the variable (${decl.datatype})")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -973,9 +965,15 @@ internal class AstChecker(private val program: Program,
|
|||
else if(expr.operator == "~") {
|
||||
if(dt !in IntegerDatatypes)
|
||||
errors.err("can only use bitwise invert on integer types", expr.position)
|
||||
if(dt==DataType.BOOL)
|
||||
else if(dt==DataType.BOOL)
|
||||
errors.err("bitwise invert is for integer types, use 'not' on booleans", expr.position)
|
||||
}
|
||||
else if(expr.operator == "not") {
|
||||
if(dt!=DataType.BOOL) {
|
||||
if(compilerOptions.strictBool || dt !in ByteDatatypes)
|
||||
errors.err("logical not is for booleans", expr.position)
|
||||
}
|
||||
}
|
||||
super.visit(expr)
|
||||
}
|
||||
|
||||
|
@ -1016,11 +1014,6 @@ internal class AstChecker(private val program: Program,
|
|||
errors.err("remainder can only be used on unsigned integer operands", expr.right.position)
|
||||
}
|
||||
}
|
||||
"&", "|", "^" -> {
|
||||
// only integer numeric operands accepted
|
||||
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
|
||||
errors.err("bitwise operator can only be used on integer operands", expr.right.position)
|
||||
}
|
||||
"in" -> throw FatalAstException("in expression should have been replaced by containmentcheck")
|
||||
"<<", ">>" -> {
|
||||
if(rightDt in WordDatatypes) {
|
||||
|
@ -1045,10 +1038,12 @@ internal class AstChecker(private val program: Program,
|
|||
// expression with one side BOOL other side (U)BYTE is allowed; bool==byte
|
||||
} else if((expr.operator == "<<" || expr.operator == ">>") && (leftDt in WordDatatypes && rightDt in ByteDatatypes)) {
|
||||
// exception allowed: shifting a word by a byte
|
||||
} else if((expr.operator in BitwiseOperators) && (leftDt in IntegerDatatypes && rightDt in IntegerDatatypes)) {
|
||||
// exception allowed: bitwise operations with any integers
|
||||
} else if((leftDt==DataType.UWORD && rightDt==DataType.STR) || (leftDt==DataType.STR && rightDt==DataType.UWORD)) {
|
||||
// exception allowed: comparing uword (pointer) with string
|
||||
} else {
|
||||
errors.err("left and right operands aren't the same type", expr.left.position)
|
||||
errors.err("left and right operands aren't the same type: $leftDt vs $rightDt", expr.position)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1060,12 +1055,12 @@ internal class AstChecker(private val program: Program,
|
|||
} else {
|
||||
if(expr.left is TypecastExpression && expr.right is NumericLiteral && !expr.right.inferType(program).istype(DataType.FLOAT)) {
|
||||
val origLeftDt = (expr.left as TypecastExpression).expression.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(rightDt.largerThan(origLeftDt) && !(expr.right as NumericLiteral).cast(origLeftDt).isValid)
|
||||
if(rightDt.largerThan(origLeftDt) && !(expr.right as NumericLiteral).cast(origLeftDt, true).isValid)
|
||||
errors.err("operands are not the same type", expr.right.position)
|
||||
}
|
||||
if(expr.right is TypecastExpression && expr.left is NumericLiteral && !expr.left.inferType(program).istype(DataType.FLOAT)) {
|
||||
val origRightDt = (expr.right as TypecastExpression).expression.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(leftDt.largerThan(origRightDt) && !(expr.left as NumericLiteral).cast(origRightDt).isValid)
|
||||
if(leftDt.largerThan(origRightDt) && !(expr.left as NumericLiteral).cast(origRightDt, true).isValid)
|
||||
errors.err("operands are not the same type", expr.right.position)
|
||||
}
|
||||
}
|
||||
|
@ -1091,6 +1086,20 @@ internal class AstChecker(private val program: Program,
|
|||
errors.err("can't use boolean operand with this operator ${expr.operator}", expr.right.position)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(expr.operator in LogicalOperators) {
|
||||
if (leftDt != DataType.BOOL || rightDt != DataType.BOOL) {
|
||||
if(compilerOptions.strictBool || leftDt !in ByteDatatypes || rightDt !in ByteDatatypes)
|
||||
errors.err("logical operator requires boolean operands", expr.right.position)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (leftDt == DataType.BOOL || rightDt == DataType.BOOL) {
|
||||
if(expr.operator!="==" && expr.operator!="!=")
|
||||
errors.err("operator requires numeric operands", expr.right.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(typecast: TypecastExpression) {
|
||||
|
@ -1102,7 +1111,7 @@ internal class AstChecker(private val program: Program,
|
|||
errors.err("this expression doesn't return a value", typecast.expression.position)
|
||||
|
||||
if(typecast.expression is NumericLiteral) {
|
||||
val castResult = (typecast.expression as NumericLiteral).cast(typecast.type)
|
||||
val castResult = (typecast.expression as NumericLiteral).cast(typecast.type, typecast.implicit)
|
||||
if(castResult.isValid)
|
||||
throw FatalAstException("cast should have been performed in const eval already")
|
||||
errors.err(castResult.whyFailed!!, typecast.expression.position)
|
||||
|
@ -1161,7 +1170,7 @@ internal class AstChecker(private val program: Program,
|
|||
errors.warn("sgn() of unsigned type is always 0 or 1, this is perhaps not what was intended", functionCallExpr.args.first().position)
|
||||
}
|
||||
|
||||
val error = VerifyFunctionArgTypes.checkTypes(functionCallExpr, program)
|
||||
val error = VerifyFunctionArgTypes.checkTypes(functionCallExpr, program, compilerOptions)
|
||||
if(error!=null)
|
||||
errors.err(error.first, error.second)
|
||||
|
||||
|
@ -1264,7 +1273,7 @@ internal class AstChecker(private val program: Program,
|
|||
|
||||
}
|
||||
|
||||
val error = VerifyFunctionArgTypes.checkTypes(functionCallStatement, program)
|
||||
val error = VerifyFunctionArgTypes.checkTypes(functionCallStatement, program, compilerOptions)
|
||||
if(error!=null)
|
||||
errors.err(error.first, error.second)
|
||||
|
||||
|
@ -1291,14 +1300,7 @@ internal class AstChecker(private val program: Program,
|
|||
if(target is Label && args.isNotEmpty())
|
||||
errors.err("cannot use arguments when calling a label", position)
|
||||
|
||||
if(target is BuiltinFunctionPlaceholder) {
|
||||
if(target.name=="all" || target.name=="any") {
|
||||
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program)?.datatype == DataType.STR
|
||||
|| args[0].inferType(program).getOr(DataType.STR) == DataType.STR) {
|
||||
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
|
||||
}
|
||||
}
|
||||
} else if(target is Subroutine) {
|
||||
if(target is Subroutine) {
|
||||
if(target.isAsmSubroutine) {
|
||||
for (arg in args.zip(target.parameters)) {
|
||||
val argIDt = arg.first.inferType(program)
|
||||
|
@ -1377,7 +1379,10 @@ internal class AstChecker(private val program: Program,
|
|||
}
|
||||
|
||||
override fun visit(whenStmt: When) {
|
||||
if(!whenStmt.condition.inferType(program).isInteger)
|
||||
val conditionDt = whenStmt.condition.inferType(program)
|
||||
if(conditionDt.isBool)
|
||||
errors.err("condition is boolean, use if statement instead", whenStmt.position)
|
||||
else if(!conditionDt.isInteger)
|
||||
errors.err("when condition must be an integer value", whenStmt.position)
|
||||
val tally = mutableSetOf<Int>()
|
||||
for((choices, choiceNode) in whenStmt.choiceValues(program)) {
|
||||
|
@ -1408,7 +1413,7 @@ internal class AstChecker(private val program: Program,
|
|||
for((constvalue, pos) in constvalues) {
|
||||
when {
|
||||
constvalue == null -> errors.err("choice value must be a constant", pos)
|
||||
constvalue.type !in IntegerDatatypes -> errors.err("choice value must be a byte or word", pos)
|
||||
constvalue.type !in IntegerDatatypesWithBoolean -> errors.err("choice value must be a byte or word", pos)
|
||||
conditionType isnot constvalue.type -> {
|
||||
if(conditionType.isKnown) {
|
||||
if(conditionType.istype(DataType.BOOL)) {
|
||||
|
@ -1494,7 +1499,8 @@ internal class AstChecker(private val program: Program,
|
|||
|
||||
private fun checkLongType(expression: Expression) {
|
||||
if(expression.inferType(program).istype(DataType.LONG)) {
|
||||
errors.err("integer overflow", expression.position)
|
||||
if(errors.noErrorForLine(expression.position))
|
||||
errors.err("integer overflow", expression.position)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1656,13 +1662,16 @@ internal class AstChecker(private val program: Program,
|
|||
return err("value '$number' out of range for word")
|
||||
}
|
||||
DataType.BOOL -> {
|
||||
return true
|
||||
if (value.type!=DataType.BOOL) {
|
||||
if (compilerOptions.strictBool || value.type !in ByteDatatypes)
|
||||
err("type of value ${value.type} doesn't match target $targetDt")
|
||||
}
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
val eltDt = ArrayToElementTypes.getValue(targetDt)
|
||||
return checkValueTypeAndRange(eltDt, value)
|
||||
}
|
||||
else -> return err("value of type ${value.type} not compatible with $targetDt")
|
||||
else -> return err("type of value ${value.type} doesn't match target $targetDt")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -1675,11 +1684,11 @@ internal class AstChecker(private val program: Program,
|
|||
is IdentifierReference -> it.nameInSource.hashCode() and 0xffff
|
||||
is TypecastExpression -> {
|
||||
val constVal = it.expression.constValue(program)
|
||||
val cast = constVal?.cast(it.type)
|
||||
val cast = constVal?.cast(it.type, true)
|
||||
if(cast==null || !cast.isValid)
|
||||
-9999999
|
||||
else
|
||||
cast.value!!.number.toInt()
|
||||
cast.valueOrZero().number.toInt()
|
||||
}
|
||||
else -> -9999999
|
||||
}
|
||||
|
@ -1725,11 +1734,11 @@ internal class AstChecker(private val program: Program,
|
|||
}
|
||||
|
||||
val result = when(targetDatatype) {
|
||||
DataType.BOOL -> sourceDatatype in NumericDatatypes
|
||||
DataType.BYTE -> sourceDatatype == DataType.BYTE || sourceDatatype == DataType.BOOL
|
||||
DataType.UBYTE -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.BOOL
|
||||
DataType.WORD -> sourceDatatype in setOf(DataType.BYTE, DataType.UBYTE, DataType.WORD, DataType.BOOL)
|
||||
DataType.UWORD -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.UWORD || sourceDatatype == DataType.BOOL
|
||||
DataType.BOOL -> sourceDatatype==DataType.BOOL
|
||||
DataType.BYTE -> sourceDatatype == DataType.BYTE
|
||||
DataType.UBYTE -> sourceDatatype == DataType.UBYTE
|
||||
DataType.WORD -> sourceDatatype in setOf(DataType.BYTE, DataType.UBYTE, DataType.WORD)
|
||||
DataType.UWORD -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.UWORD
|
||||
DataType.FLOAT -> sourceDatatype in NumericDatatypes
|
||||
DataType.STR -> sourceDatatype == DataType.STR
|
||||
else -> {
|
||||
|
@ -1749,12 +1758,22 @@ internal class AstChecker(private val program: Program,
|
|||
}
|
||||
else if(sourceDatatype== DataType.FLOAT && targetDatatype in IntegerDatatypes)
|
||||
errors.err("cannot assign float to ${targetDatatype.name.lowercase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
|
||||
else if((sourceValue as? BinaryExpression)?.operator in BitwiseOperators && targetDatatype.equalsSize(sourceDatatype)) {
|
||||
// this is allowed: bitwise operation between different types as long as they're the same size.
|
||||
}
|
||||
else if(targetDatatype==DataType.UWORD && sourceDatatype in PassByReferenceDatatypes) {
|
||||
// this is allowed: a pass-by-reference datatype into a uword (pointer value).
|
||||
}
|
||||
else if(sourceDatatype==DataType.BOOL && targetDatatype!=DataType.BOOL) {
|
||||
if(compilerOptions.strictBool || targetDatatype !in ByteDatatypes)
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
else if(targetDatatype==DataType.BOOL && sourceDatatype!=DataType.BOOL) {
|
||||
if(compilerOptions.strictBool || sourceDatatype !in ByteDatatypes)
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
else {
|
||||
if(targetDatatype!=DataType.UWORD && sourceDatatype !in PassByReferenceDatatypes) {
|
||||
// allow bitwise operations on different types as long as the size is the same
|
||||
if (!((sourceValue as? BinaryExpression)?.operator in BitwiseOperators && targetDatatype.equalsSize(sourceDatatype)))
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
|
||||
return false
|
||||
|
|
|
@ -22,10 +22,6 @@ internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: Compila
|
|||
}
|
||||
|
||||
internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationOptions, errors: IErrorReporter) {
|
||||
val boolRemover = BoolRemover(this)
|
||||
boolRemover.visit(this)
|
||||
boolRemover.applyModifications()
|
||||
|
||||
val fixer = BeforeAsmAstChanger(this, compilerOptions)
|
||||
fixer.visit(this)
|
||||
while (errors.noErrors() && fixer.applyModifications() > 0) {
|
||||
|
@ -96,15 +92,15 @@ internal fun Program.addTypecasts(errors: IErrorReporter, options: CompilationOp
|
|||
caster.applyModifications()
|
||||
}
|
||||
|
||||
fun Program.desugaring(errors: IErrorReporter) {
|
||||
val desugar = CodeDesugarer(this, errors)
|
||||
fun Program.desugaring(errors: IErrorReporter, options: CompilationOptions) {
|
||||
val desugar = CodeDesugarer(this, options, errors)
|
||||
desugar.visit(this)
|
||||
while(errors.noErrors() && desugar.applyModifications()>0)
|
||||
desugar.visit(this)
|
||||
}
|
||||
|
||||
internal fun Program.verifyFunctionArgTypes(errors: IErrorReporter) {
|
||||
val fixer = VerifyFunctionArgTypes(this, errors)
|
||||
internal fun Program.verifyFunctionArgTypes(errors: IErrorReporter, options: CompilationOptions) {
|
||||
val fixer = VerifyFunctionArgTypes(this, options, errors)
|
||||
fixer.visit(this)
|
||||
}
|
||||
|
||||
|
@ -121,7 +117,7 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
|
|||
checker2.visit(this)
|
||||
|
||||
if(errors.noErrors()) {
|
||||
val lit2decl = LiteralsToAutoVars(this, options.compTarget, errors)
|
||||
val lit2decl = LiteralsToAutoVars(this, errors)
|
||||
lit2decl.visit(this)
|
||||
if(errors.noErrors())
|
||||
lit2decl.applyModifications()
|
||||
|
|
|
@ -136,7 +136,7 @@ class AstPreprocessor(val program: Program,
|
|||
}
|
||||
} else {
|
||||
// handle declaration of a single variable
|
||||
if(decl.value!=null && decl.datatype in NumericDatatypes) {
|
||||
if(decl.value!=null && (decl.datatype in NumericDatatypes || decl.datatype==DataType.BOOL)) {
|
||||
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
||||
val assign = Assignment(target, decl.value!!, AssignmentOrigin.VARINIT, decl.position)
|
||||
replacements.add(IAstModification.ReplaceNode(decl, assign, scope))
|
||||
|
|
|
@ -26,7 +26,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
|
|||
}
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
if (decl.type == VarDeclType.VAR && decl.value != null && decl.datatype in NumericDatatypes)
|
||||
if (decl.type == VarDeclType.VAR && decl.value != null && (decl.datatype in NumericDatatypes || decl.datatype==DataType.BOOL))
|
||||
throw InternalCompilerException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
|
||||
|
||||
return noModifications
|
||||
|
@ -103,18 +103,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
|
|||
}
|
||||
|
||||
override fun after(ifElse: IfElse, parent: Node): Iterable<IAstModification> {
|
||||
val binExpr = ifElse.condition as? BinaryExpression
|
||||
if(binExpr==null) {
|
||||
// if x -> if x!=0
|
||||
val booleanExpr = BinaryExpression(
|
||||
ifElse.condition,
|
||||
"!=",
|
||||
NumericLiteral.optimalInteger(0, ifElse.condition.position),
|
||||
ifElse.condition.position
|
||||
)
|
||||
return listOf(IAstModification.ReplaceNode(ifElse.condition, booleanExpr, ifElse))
|
||||
}
|
||||
|
||||
val binExpr = ifElse.condition as? BinaryExpression ?: return noModifications
|
||||
if(binExpr.operator !in ComparisonOperators) {
|
||||
val constRight = binExpr.right.constValue(program)
|
||||
if(constRight!=null) {
|
||||
|
@ -131,20 +120,67 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
|
|||
return listOf(IAstModification.ReplaceNode(ifElse.condition, booleanExpr, ifElse))
|
||||
}
|
||||
}
|
||||
|
||||
// if x*5 -> if x*5 != 0
|
||||
val booleanExpr = BinaryExpression(
|
||||
ifElse.condition,
|
||||
"!=",
|
||||
NumericLiteral.optimalInteger(0, ifElse.condition.position),
|
||||
ifElse.condition.position
|
||||
)
|
||||
return listOf(IAstModification.ReplaceNode(ifElse.condition, booleanExpr, ifElse))
|
||||
}
|
||||
|
||||
if((binExpr.left as? NumericLiteral)?.number==0.0 &&
|
||||
if(binExpr.operator=="==" &&
|
||||
(binExpr.left as? NumericLiteral)?.number==0.0 &&
|
||||
(binExpr.right as? NumericLiteral)?.number!=0.0)
|
||||
throw InternalCompilerException("0==X should have been swapped to if X==0")
|
||||
throw InternalCompilerException("0==X should be just X ${binExpr.position}")
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
if (options.compTarget.name == VMTarget.NAME)
|
||||
return noModifications
|
||||
|
||||
val rightDt = expr.right.inferType(program)
|
||||
val rightNum = expr.right.constValue(program)
|
||||
|
||||
if(rightDt.isWords && (rightNum==null || rightNum.number!=0.0)) {
|
||||
when (expr.operator) {
|
||||
">" -> {
|
||||
// X>Y -> Y<X , easier to do in 6502
|
||||
expr.operator = "<"
|
||||
val left = expr.left
|
||||
expr.left = expr.right
|
||||
expr.right = left
|
||||
return noModifications
|
||||
}
|
||||
|
||||
"<=" -> {
|
||||
// X<=Y -> Y>=X , easier to do in 6502
|
||||
expr.operator = ">="
|
||||
val left = expr.left
|
||||
expr.left = expr.right
|
||||
expr.right = left
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(rightNum!=null && rightNum.type in IntegerDatatypes && rightNum.number!=0.0) {
|
||||
when(expr.operator) {
|
||||
">" -> {
|
||||
// X>N -> X>=N+1, easier to do in 6502
|
||||
val maximum = if(rightNum.type in ByteDatatypes) 255 else 65535
|
||||
if(rightNum.number<maximum) {
|
||||
val numPlusOne = rightNum.number.toInt()+1
|
||||
val newExpr = BinaryExpression(expr.left, ">=", NumericLiteral(rightNum.type, numPlusOne.toDouble(), rightNum.position), expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
}
|
||||
}
|
||||
"<=" -> {
|
||||
// X<=N -> X<N+1, easier to do in 6502
|
||||
val maximum = if(rightNum.type in ByteDatatypes) 255 else 65535
|
||||
if(rightNum.number<maximum) {
|
||||
val numPlusOne = rightNum.number.toInt()+1
|
||||
val newExpr = BinaryExpression(expr.left, "<", NumericLiteral(rightNum.type, numPlusOne.toDouble(), rightNum.position), expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
|
|
@ -15,14 +15,6 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
|||
private val errors: IErrorReporter
|
||||
) : AstWalker() {
|
||||
|
||||
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(typecast.type == DataType.BOOL) {
|
||||
val notZero = BinaryExpression(typecast.expression, "!=", NumericLiteral(DataType.UBYTE, 0.0, typecast.position), typecast.position)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, notZero, parent))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
// see if we can remove redundant typecasts (outside of expressions)
|
||||
// such as casting byte<->ubyte, word<->uword or even redundant casts (sourcetype = target type).
|
||||
|
@ -111,7 +103,9 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
|||
val arg2 = bfcs.args[1]
|
||||
val dt1 = arg1.inferType(program).getOr(DataType.UNDEFINED)
|
||||
val dt2 = arg2.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(dt1 in ByteDatatypes) {
|
||||
if(dt1==DataType.BOOL && dt2==DataType.BOOL)
|
||||
return noModifications
|
||||
else if(dt1 in ByteDatatypes) {
|
||||
if(dt2 in ByteDatatypes)
|
||||
return noModifications
|
||||
val (replaced, cast) = arg1.typecastTo(if(dt1== DataType.UBYTE) DataType.UWORD else DataType.WORD, dt1, true)
|
||||
|
|
|
@ -1,133 +0,0 @@
|
|||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.SubroutineParameter
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
|
||||
internal class BoolRemover(val program: Program) : AstWalker() {
|
||||
|
||||
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(typecast.type == DataType.BOOL) {
|
||||
val valueDt = typecast.expression.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||
val notZero = BinaryExpression(typecast.expression, "!=", NumericLiteral(valueDt, 0.0, typecast.position), typecast.position)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, notZero, parent))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
if(decl.datatype==DataType.BOOL) {
|
||||
var newvalue = decl.value
|
||||
if(newvalue is NumericLiteral) {
|
||||
if(newvalue.number!=0.0)
|
||||
newvalue = NumericLiteral(DataType.UBYTE, 1.0, newvalue.position)
|
||||
}
|
||||
val ubyteDecl = VarDecl(decl.type, decl.origin, DataType.UBYTE, decl.zeropage, null, decl.name, emptyList(),
|
||||
newvalue, decl.sharedWithAsm, false, decl.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl, ubyteDecl, parent))
|
||||
}
|
||||
|
||||
if(decl.datatype==DataType.ARRAY_BOOL) {
|
||||
var newarray = decl.value
|
||||
if(decl.value is ArrayLiteral) {
|
||||
val oldArray = (decl.value as ArrayLiteral).value
|
||||
val convertedArray = oldArray.map {
|
||||
var number: Expression = it
|
||||
if (it is NumericLiteral && it.type == DataType.BOOL)
|
||||
number = NumericLiteral(DataType.UBYTE, if (it.number == 0.0) 0.0 else 1.0, number.position)
|
||||
number
|
||||
}.toTypedArray()
|
||||
newarray = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_UB), convertedArray, decl.position)
|
||||
}
|
||||
val ubyteArrayDecl = VarDecl(decl.type, decl.origin, DataType.ARRAY_UB, decl.zeropage, decl.arraysize, decl.name, emptyList(),
|
||||
newarray, decl.sharedWithAsm, decl.splitArray, decl.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl, ubyteArrayDecl, parent))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||
// replace BOOL return type and parameters by UBYTE
|
||||
if(subroutine.returntypes.any { it==DataType.BOOL } || subroutine.parameters.any {it.type==DataType.BOOL}) {
|
||||
val newReturnTypes = subroutine.returntypes.map {
|
||||
if(it==DataType.BOOL) DataType.UBYTE else it
|
||||
}.toMutableList()
|
||||
val newParams = subroutine.parameters.map {
|
||||
if(it.type==DataType.BOOL) SubroutineParameter(it.name, DataType.UBYTE, it.position) else it
|
||||
}.toMutableList()
|
||||
val newSubroutine = Subroutine(subroutine.name, newParams, newReturnTypes,
|
||||
subroutine.asmParameterRegisters, subroutine.asmReturnvaluesRegisters, subroutine.asmClobbers,
|
||||
subroutine.asmAddress, subroutine.isAsmSubroutine, subroutine.inline, false, subroutine.statements,
|
||||
subroutine.position)
|
||||
return listOf(IAstModification.ReplaceNode(subroutine, newSubroutine, parent))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator in setOf("and", "or", "xor")) {
|
||||
// see if any of the arguments to a logical boolean expression need type casting to bool
|
||||
val mods = mutableListOf<IAstModification>()
|
||||
val newLeft = wrapWithBooleanCastIfNeeded(expr.left, program)
|
||||
val newRight = wrapWithBooleanCastIfNeeded(expr.right, program)
|
||||
if(newLeft!=null)
|
||||
mods += IAstModification.ReplaceNodeSafe(expr.left, newLeft, expr)
|
||||
if(newRight!=null)
|
||||
mods += IAstModification.ReplaceNodeSafe(expr.right, newRight, expr)
|
||||
return mods
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator=="not") {
|
||||
val binExpr = expr.expression as? BinaryExpression
|
||||
if(binExpr!=null) {
|
||||
val invertedOperator = invertedComparisonOperator(binExpr.operator)
|
||||
if(invertedOperator!=null) {
|
||||
val inverted = BinaryExpression(binExpr.left, invertedOperator, binExpr.right, binExpr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, inverted, parent))
|
||||
}
|
||||
}
|
||||
val exprDt = expr.expression.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||
val nonBoolDt = if(exprDt==DataType.BOOL) DataType.UBYTE else exprDt
|
||||
val equalZero = BinaryExpression(expr.expression, "==", NumericLiteral(nonBoolDt, 0.0, expr.expression.position), expr.expression.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, equalZero, parent))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
||||
internal fun wrapWithBooleanCastIfNeeded(expr: Expression, program: Program): Expression? {
|
||||
fun isBoolean(expr: Expression): Boolean {
|
||||
return if(expr.inferType(program) istype DataType.BOOL)
|
||||
true
|
||||
else if(expr is NumericLiteral && expr.type in IntegerDatatypes && (expr.number==0.0 || expr.number==1.0))
|
||||
true
|
||||
else if(expr is BinaryExpression && expr.operator in ComparisonOperators + LogicalOperators)
|
||||
true
|
||||
else if(expr is PrefixExpression && expr.operator == "not")
|
||||
true
|
||||
else if(expr is BinaryExpression && expr.operator in BitwiseOperators) {
|
||||
if(isBoolean(expr.left) && isBoolean(expr.right))
|
||||
true
|
||||
else expr.operator=="&" && expr.right.constValue(program)?.number==1.0 // x & 1 is also a boolean result
|
||||
}
|
||||
else
|
||||
false
|
||||
}
|
||||
|
||||
return if(isBoolean(expr))
|
||||
null
|
||||
else
|
||||
TypecastExpression(expr, DataType.BOOL, true, expr.position)
|
||||
}
|
|
@ -5,13 +5,10 @@ import prog8.ast.expressions.*
|
|||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.ComparisonOperators
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal class CodeDesugarer(val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
internal class CodeDesugarer(val program: Program, private val options: CompilationOptions, private val errors: IErrorReporter) : AstWalker() {
|
||||
|
||||
// Some more code shuffling to simplify the Ast that the codegenerator has to process.
|
||||
// Several changes have already been done by the StatementReorderer !
|
||||
|
@ -124,6 +121,13 @@ if not CONDITION
|
|||
}
|
||||
|
||||
override fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
if(!whileLoop.condition.inferType(program).isBool) {
|
||||
val ctype = whileLoop.condition.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(options.strictBool || ctype !in ByteDatatypes)
|
||||
errors.err("condition should be a boolean", whileLoop.condition.position)
|
||||
}
|
||||
|
||||
/*
|
||||
while true -> repeat
|
||||
while false -> discard
|
||||
|
|
|
@ -349,6 +349,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
|||
}
|
||||
}
|
||||
|
||||
// if something_returning_Pc() -> if_cc
|
||||
val binexpr = srcIf.condition as? BinaryExpression
|
||||
if(binexpr!=null && binexpr.right.constValue(program)?.number==0.0) {
|
||||
if(binexpr.operator=="==" || binexpr.operator=="!=") {
|
||||
|
@ -368,6 +369,17 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
|||
return codeForStatusflag(fcall, returnRegs[0].statusflag!!, false)
|
||||
}
|
||||
}
|
||||
|
||||
val prefix = srcIf.condition as? PrefixExpression
|
||||
if(prefix!=null && prefix.operator=="not") {
|
||||
val prefixedFcall = prefix.expression as? FunctionCallExpression
|
||||
if (prefixedFcall != null) {
|
||||
val returnRegs = prefixedFcall.target.targetSubroutine(program)?.asmReturnvaluesRegisters
|
||||
if(returnRegs!=null && returnRegs.size==1 && returnRegs[0].statusflag!=null) {
|
||||
return codeForStatusflag(prefixedFcall, returnRegs[0].statusflag!!, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val ifelse = PtIfElse(srcIf.position)
|
||||
|
@ -558,8 +570,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
|||
|
||||
private fun transform(srcExpr: BinaryExpression): PtBinaryExpression {
|
||||
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)
|
||||
val expr = PtBinaryExpression(srcExpr.operator, type, srcExpr.position)
|
||||
expr.add(transformExpression(srcExpr.left))
|
||||
expr.add(transformExpression(srcExpr.right))
|
||||
return expr
|
||||
|
@ -578,27 +589,27 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
|||
|
||||
fun desugar(range: RangeExpression): PtExpression {
|
||||
require(range.from.inferType(program)==range.to.inferType(program))
|
||||
val expr = PtBinaryExpression("and", DataType.UBYTE, srcCheck.position)
|
||||
val expr = PtBinaryExpression("and", DataType.BOOL, srcCheck.position)
|
||||
val x1 = transformExpression(srcCheck.element)
|
||||
val x2 = transformExpression(srcCheck.element)
|
||||
val eltDt = srcCheck.element.inferType(program)
|
||||
if(eltDt.isInteger) {
|
||||
val low = PtBinaryExpression("<=", DataType.UBYTE, srcCheck.position)
|
||||
val low = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
|
||||
low.add(transformExpression(range.from))
|
||||
low.add(x1)
|
||||
expr.add(low)
|
||||
val high = PtBinaryExpression("<=", DataType.UBYTE, srcCheck.position)
|
||||
val high = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
|
||||
high.add(x2)
|
||||
high.add(transformExpression(range.to))
|
||||
expr.add(high)
|
||||
} else {
|
||||
val low = PtBinaryExpression("<=", DataType.UBYTE, srcCheck.position)
|
||||
val low = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
|
||||
val lowFloat = PtTypeCast(DataType.FLOAT, range.from.position)
|
||||
lowFloat.add(transformExpression(range.from))
|
||||
low.add(lowFloat)
|
||||
low.add(x1)
|
||||
expr.add(low)
|
||||
val high = PtBinaryExpression("<=", DataType.UBYTE, srcCheck.position)
|
||||
val high = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
|
||||
high.add(x2)
|
||||
val highFLoat = PtTypeCast(DataType.FLOAT, range.to.position)
|
||||
highFLoat.add(transformExpression(range.to))
|
||||
|
@ -655,8 +666,12 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
|||
return mem
|
||||
}
|
||||
|
||||
private fun transform(number: NumericLiteral): PtNumber =
|
||||
PtNumber(number.type, number.number, number.position)
|
||||
private fun transform(number: NumericLiteral): PtExpression {
|
||||
return if(number.type==DataType.BOOL)
|
||||
PtBool(number.asBooleanValue, number.position)
|
||||
else
|
||||
PtNumber(number.type, number.number, number.position)
|
||||
}
|
||||
|
||||
private fun transform(srcPrefix: PrefixExpression): PtPrefix {
|
||||
val type = srcPrefix.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||
|
|
|
@ -13,13 +13,13 @@ import prog8.ast.statements.VarDecl
|
|||
import prog8.ast.statements.WhenChoice
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.NumericDatatypesWithBoolean
|
||||
import prog8.code.core.SplitWordArrayTypes
|
||||
|
||||
|
||||
internal class LiteralsToAutoVars(private val program: Program,
|
||||
private val target: ICompilationTarget,
|
||||
private val errors: IErrorReporter
|
||||
) : AstWalker() {
|
||||
internal class LiteralsToAutoVars(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
|
||||
override fun after(string: StringLiteral, parent: Node): Iterable<IAstModification> {
|
||||
if(string.parent !is VarDecl && string.parent !is WhenChoice) {
|
||||
|
@ -76,8 +76,8 @@ internal class LiteralsToAutoVars(private val program: Program,
|
|||
if(decl.names.size>1) {
|
||||
// note: the desugaring of a multi-variable vardecl has to be done here
|
||||
// and not in CodeDesugarer, that one is too late (identifiers can't be found otherwise)
|
||||
if(decl.datatype !in NumericDatatypes)
|
||||
errors.err("can only multi declare numeric variables", decl.position)
|
||||
if(decl.datatype !in NumericDatatypesWithBoolean)
|
||||
errors.err("can only multi declare numeric and boolean variables", decl.position)
|
||||
if(errors.noErrors()) {
|
||||
// desugar into individual vardecl per name.
|
||||
return decl.desugarMultiDecl().map {
|
||||
|
|
|
@ -9,10 +9,7 @@ import prog8.ast.expressions.PrefixExpression
|
|||
import prog8.ast.expressions.invertCondition
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.IntegerDatatypes
|
||||
import prog8.code.core.*
|
||||
|
||||
internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val errors: IErrorReporter, val compTarget: ICompilationTarget) : AstWalker() {
|
||||
|
||||
|
@ -21,7 +18,7 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
|
|||
val left = expr.left as? BinaryExpression
|
||||
if (left != null) {
|
||||
val rightValue = expr.right.constValue(program)
|
||||
if (rightValue?.number == 0.0 && rightValue.type in IntegerDatatypes) {
|
||||
if (rightValue?.number == 0.0 && rightValue.type in IntegerDatatypesWithBoolean) {
|
||||
if (left.operator == "==" && expr.operator == "==") {
|
||||
// (x==something)==0 --> x!=something
|
||||
left.operator = "!="
|
||||
|
@ -43,15 +40,11 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
|
|||
}
|
||||
}
|
||||
|
||||
if(expr.operator=="^" && expr.left.inferType(program) istype DataType.BOOL && expr.right.constValue(program)?.number == 1.0) {
|
||||
// boolean ^ 1 --> not boolean
|
||||
return listOf(IAstModification.ReplaceNode(expr, invertCondition(expr.left, program), parent))
|
||||
}
|
||||
|
||||
|
||||
// applying De Morgan's laws proved beneficial for the code generator,
|
||||
// when the code has one outer 'not' instead of two inner ones.
|
||||
if(expr.operator=="or" || expr.operator=="and") {
|
||||
|
||||
// boolean case
|
||||
val newOper = if(expr.operator=="or") "and" else "or"
|
||||
val leftP = expr.left as? PrefixExpression
|
||||
val rightP = expr.right as? PrefixExpression
|
||||
|
@ -62,16 +55,40 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
|
|||
val notExpr = PrefixExpression("not", inner, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, notExpr, parent))
|
||||
}
|
||||
val leftB = expr.left as? BinaryExpression
|
||||
val rightB = expr.right as? BinaryExpression
|
||||
if(leftB!=null && leftB.operator=="==" && (leftB.right as? NumericLiteral)?.number==0.0
|
||||
&& rightB!=null && rightB.operator=="==" && (rightB.right as? NumericLiteral)?.number==0.0) {
|
||||
// a==0 or b==0 --> (a!=0 and b!=0)==0
|
||||
// a==0 and b==0 --> (a!=0 or b!=0)==0
|
||||
leftB.operator = "!="
|
||||
rightB.operator = "!="
|
||||
val inner = BinaryExpression(leftB, newOper, rightB, expr.position)
|
||||
val notExpr = BinaryExpression(inner, "==", NumericLiteral.optimalInteger(0, expr.position), expr.position)
|
||||
|
||||
// integer case (only if both are the same type)
|
||||
val leftC = expr.left as? BinaryExpression
|
||||
val rightC = expr.right as? BinaryExpression
|
||||
if(leftC!=null && rightC!=null && leftC.operator=="==" && rightC.operator=="==") {
|
||||
if (leftC.right.constValue(program)?.number == 0.0 && rightC.right.constValue(program)?.number == 0.0) {
|
||||
val leftDt = leftC.left.inferType(program).getOr(DataType.UNDEFINED)
|
||||
val rightDt = rightC.left.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(leftDt==rightDt && leftDt in IntegerDatatypes) {
|
||||
if (rightC.left.isSimple) {
|
||||
// x==0 or y==0 -> (x & y)==0
|
||||
// x==0 and y==0 -> (x | y)==0
|
||||
val newOperator = if(expr.operator=="or") "&" else "|"
|
||||
val inner = BinaryExpression(leftC.left, newOperator, rightC.left, expr.position)
|
||||
val compare = BinaryExpression(inner, "==", NumericLiteral(leftDt, 0.0, expr.position), expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, compare, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mixed cases
|
||||
if(leftC!=null && rightP!=null && leftC.operator=="==" && rightP.operator=="not") {
|
||||
// mixed case 1: x==V or not y -> not(x!=V and y)
|
||||
val invertedLeftExpression = invertCondition(leftC, program)
|
||||
val inner = BinaryExpression(invertedLeftExpression, newOper, rightP.expression, expr.position)
|
||||
val notExpr = PrefixExpression("not", inner, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, notExpr, parent))
|
||||
}
|
||||
else if(rightC!=null && leftP!=null && rightC.operator=="==" && leftP.operator=="not") {
|
||||
// mixed case 1: not x or y==V -> not(x and y!=V)
|
||||
val invertedRightExpression = invertCondition(rightC, program)
|
||||
val inner = BinaryExpression(leftP.expression, newOper, invertedRightExpression, expr.position)
|
||||
val notExpr = PrefixExpression("not", inner, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, notExpr, parent))
|
||||
}
|
||||
}
|
||||
|
@ -82,17 +99,50 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
|
|||
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator == "not") {
|
||||
|
||||
// first check if we're already part of a "boolean" expresion (i.e. comparing against 0)
|
||||
// first check if we're already part of a "boolean" expression (i.e. comparing against 0 or 1)
|
||||
// if so, simplify THAT whole expression rather than making it more complicated
|
||||
if(parent is BinaryExpression && parent.right.constValue(program)?.number==0.0) {
|
||||
if(parent.operator=="==") {
|
||||
// (NOT X)==0 --> X!=0
|
||||
val replacement = BinaryExpression(expr.expression, "!=", NumericLiteral.optimalInteger(0, expr.position), expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(parent, replacement, parent.parent))
|
||||
} else if(parent.operator=="!=") {
|
||||
// (NOT X)!=0 --> X==0
|
||||
val replacement = BinaryExpression(expr.expression, "==", NumericLiteral.optimalInteger(0, expr.position), expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(parent, replacement, parent.parent))
|
||||
if (parent is BinaryExpression) {
|
||||
if (parent.right.constValue(program)?.number == 0.0) {
|
||||
if(parent.right.inferType(program).isBool) {
|
||||
if(parent.operator=="==") {
|
||||
// (NOT X)==false --> X==true --> X
|
||||
return listOf(IAstModification.ReplaceNode(parent, expr.expression, parent.parent))
|
||||
} else if(parent.operator=="!=") {
|
||||
// (NOT X)!=false --> X!=true -> not X
|
||||
return listOf(IAstModification.ReplaceNode(parent, expr, parent.parent))
|
||||
}
|
||||
} else {
|
||||
if(parent.operator=="==") {
|
||||
// (NOT X)==0 --> X!=0
|
||||
val replacement = BinaryExpression(expr.expression, "!=", NumericLiteral.optimalInteger(0, expr.position), expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(parent, replacement, parent.parent))
|
||||
} else if(parent.operator=="!=") {
|
||||
// (NOT X)!=0 --> X==0
|
||||
val replacement = BinaryExpression(expr.expression, "==", NumericLiteral.optimalInteger(0, expr.position), expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(parent, replacement, parent.parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (parent.right.constValue(program)?.number == 1.0) {
|
||||
if(parent.right.inferType(program).isBool) {
|
||||
if(parent.operator=="==") {
|
||||
// (NOT X)==true --> X==false --> not X
|
||||
return listOf(IAstModification.ReplaceNode(parent, expr, parent.parent))
|
||||
} else if(parent.operator=="!=") {
|
||||
// (NOT X)!=true --> X!=false -> X
|
||||
return listOf(IAstModification.ReplaceNode(parent, expr.expression, parent.parent))
|
||||
}
|
||||
} else {
|
||||
if(parent.operator=="==") {
|
||||
// (NOT X)==1 --> X==0
|
||||
val replacement = BinaryExpression(expr.expression, "==", NumericLiteral.optimalInteger(0, expr.position), expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(parent, replacement, parent.parent))
|
||||
} else if(parent.operator=="!=") {
|
||||
// (NOT X)!=1 --> X!=0
|
||||
val replacement = BinaryExpression(expr.expression, "!=", NumericLiteral.optimalInteger(0, expr.position), expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(parent, replacement, parent.parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,13 +170,8 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
|
|||
return listOf(IAstModification.ReplaceNode(expr, subBinExpr, parent))
|
||||
}
|
||||
}
|
||||
|
||||
// not simpleX -> simpleX==0
|
||||
if(expr.expression.isSimple) {
|
||||
val replacement = BinaryExpression(expr.expression.copy(),"==", NumericLiteral(DataType.UBYTE, 0.0, expr.position), expr.position)
|
||||
return listOf(IAstModification.ReplaceNodeSafe(expr, replacement, parent))
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.expressions.ArrayLiteral
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
|
@ -44,7 +42,7 @@ internal class StatementReorderer(
|
|||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
if (decl.type == VarDeclType.VAR) {
|
||||
if (decl.datatype in NumericDatatypes) {
|
||||
if (decl.datatype in NumericDatatypes || decl.datatype==DataType.BOOL) {
|
||||
if(decl !in declsProcessedWithInitAssignment) {
|
||||
declsProcessedWithInitAssignment.add(decl)
|
||||
if (decl.value == null) {
|
||||
|
@ -178,15 +176,18 @@ internal class StatementReorderer(
|
|||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
||||
// simplething <associative> X -> X <associative> simplething
|
||||
// (this should be done by the ExpressionSimplifier when optimizing is enabled,
|
||||
// but the current assembly code generator for IF statements now also depends on it, so we do it here regardless of optimization.)
|
||||
if (expr.left.constValue(program) != null
|
||||
&& expr.operator in AssociativeOperators
|
||||
&& expr.right.constValue(program) == null
|
||||
&& maySwapOperandOrder(expr))
|
||||
return listOf(IAstModification.SwapOperands(expr))
|
||||
|
||||
if(expr.operator in AssociativeOperators) {
|
||||
if(expr.left is IdentifierReference || expr.left is NumericLiteral || expr.left is DirectMemoryRead || (expr.left as? ArrayIndexedExpression)?.indexer?.constIndex()!=null) {
|
||||
if(expr.right !is IdentifierReference && expr.right !is NumericLiteral && expr.right !is DirectMemoryRead) {
|
||||
if(maySwapOperandOrder(expr)) {
|
||||
return listOf(IAstModification.SwapOperands(expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
|
|
@ -24,12 +24,16 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||
val valueDt = declValue.inferType(program)
|
||||
if(valueDt isnot decl.datatype) {
|
||||
|
||||
// don't add a typecast on an array initializer value, unless booleans
|
||||
if(decl.isArray && !options.strictBool) {
|
||||
if(tryConvertBooleanArrays(decl, declValue, parent))
|
||||
return noModifications
|
||||
}
|
||||
|
||||
if(valueDt.isInteger && decl.isArray) {
|
||||
if(decl.datatype == DataType.ARRAY_BOOL) {
|
||||
val integer = declValue.constValue(program)?.number
|
||||
if(integer!=null) {
|
||||
val num = NumericLiteral(DataType.UBYTE, if(integer==0.0) 0.0 else 1.0, declValue.position)
|
||||
val num = NumericLiteral(DataType.BOOL, if(integer==0.0) 0.0 else 1.0, declValue.position)
|
||||
num.parent = decl
|
||||
decl.value = num
|
||||
}
|
||||
|
@ -50,6 +54,69 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||
return noModifications
|
||||
}
|
||||
|
||||
private fun tryConvertBooleanArrays(decl: VarDecl, declValue: Expression, parent: Node): Boolean {
|
||||
val valueNumber = declValue.constValue(program)
|
||||
val valueArray = declValue as? ArrayLiteral
|
||||
when (decl.datatype) {
|
||||
DataType.ARRAY_BOOL -> {
|
||||
if(valueNumber!=null) {
|
||||
decl.value = NumericLiteral.fromBoolean(valueNumber.number!=0.0, declValue.position)
|
||||
decl.linkParents(parent)
|
||||
return true
|
||||
} else if(valueArray!=null) {
|
||||
val newArray = valueArray.value.map {
|
||||
if(it.inferType(program).isBytes) {
|
||||
TypecastExpression(it, DataType.BOOL, false, it.position)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
decl.value = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_BOOL), newArray.toTypedArray(), valueArray.position)
|
||||
decl.linkParents(parent)
|
||||
return true
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_B -> {
|
||||
if(valueNumber!=null) {
|
||||
decl.value = NumericLiteral(DataType.BYTE, if(valueNumber.asBooleanValue) 1.0 else 0.0, declValue.position)
|
||||
decl.linkParents(parent)
|
||||
return true
|
||||
} else if(valueArray!=null) {
|
||||
val newArray = valueArray.value.map {
|
||||
if(it.inferType(program).isBool) {
|
||||
TypecastExpression(it, DataType.BYTE, false, it.position)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
decl.value = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_B), newArray.toTypedArray(), valueArray.position)
|
||||
decl.linkParents(parent)
|
||||
return true
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_UB -> {
|
||||
if(valueNumber!=null) {
|
||||
decl.value = NumericLiteral(DataType.UBYTE, if(valueNumber.asBooleanValue) 1.0 else 0.0, declValue.position)
|
||||
decl.linkParents(parent)
|
||||
return true
|
||||
} else if(valueArray!=null) {
|
||||
val newArray = valueArray.value.map {
|
||||
if(it.inferType(program).isBool) {
|
||||
TypecastExpression(it, DataType.UBYTE, false, it.position)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
decl.value = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_UB), newArray.toTypedArray(), valueArray.position)
|
||||
decl.linkParents(parent)
|
||||
return true
|
||||
}
|
||||
}
|
||||
else -> { /* no casting possible */ }
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
val leftDt = expr.left.inferType(program)
|
||||
val rightDt = expr.right.inferType(program)
|
||||
|
@ -87,28 +154,25 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||
}
|
||||
}
|
||||
|
||||
if(expr.operator in LogicalOperators && leftDt.isInteger && rightDt.isInteger) {
|
||||
// see if any of the operands needs conversion to bool
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
val newLeft = wrapWithBooleanCastIfNeeded(expr.left, program)
|
||||
val newRight = wrapWithBooleanCastIfNeeded(expr.right, program)
|
||||
if(newLeft!=null)
|
||||
modifications += IAstModification.ReplaceNode(expr.left, newLeft, expr)
|
||||
if(newRight!=null)
|
||||
modifications += IAstModification.ReplaceNode(expr.right, newRight, expr)
|
||||
if(modifications.isNotEmpty())
|
||||
return modifications
|
||||
}
|
||||
if(leftDt!=rightDt) {
|
||||
// convert bool type to byte if needed
|
||||
if(leftDt istype DataType.BOOL && rightDt.isBytes && !rightDt.istype(DataType.BOOL)) {
|
||||
if(rightCv==null || (rightCv.number!=1.0 && rightCv.number!=0.0))
|
||||
return listOf(IAstModification.ReplaceNode(expr.left,
|
||||
TypecastExpression(expr.left, rightDt.getOr(DataType.UNDEFINED),true, expr.left.position), expr))
|
||||
} else if(leftDt.isBytes && !leftDt.istype(DataType.BOOL) && rightDt istype DataType.BOOL) {
|
||||
if(leftCv==null || (leftCv.number!=1.0 && leftCv.number!=0.0))
|
||||
return listOf(IAstModification.ReplaceNode(expr.right,
|
||||
TypecastExpression(expr.right, leftDt.getOr(DataType.UNDEFINED),true, expr.right.position), expr))
|
||||
if(!options.strictBool) {
|
||||
if (expr.operator in LogicalOperators) {
|
||||
if (leftDt.isBool) {
|
||||
val cast = TypecastExpression(expr.right, DataType.BOOL, false, expr.right.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, cast, expr))
|
||||
} else {
|
||||
val cast = TypecastExpression(expr.left, DataType.BOOL, false, expr.left.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||
}
|
||||
} else {
|
||||
if(leftDt.isBool && rightDt.isBytes) {
|
||||
val cast = TypecastExpression(expr.left, rightDt.getOr(DataType.UNDEFINED), false, expr.left.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||
} else if(rightDt.isBool && leftDt.isBytes) {
|
||||
val cast = TypecastExpression(expr.right, leftDt.getOr(DataType.UNDEFINED), false, expr.right.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, cast, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert a negative operand for bitwise operator to the 2's complement positive number instead
|
||||
|
@ -169,13 +233,18 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||
// determine common datatype and add typecast as required to make left and right equal types
|
||||
val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.getOr(DataType.UNDEFINED), rightDt.getOr(DataType.UNDEFINED), expr.left, expr.right)
|
||||
if(toFix!=null) {
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
when {
|
||||
toFix===expr.left -> addTypecastOrCastedValueModification(modifications, expr.left, commonDt, expr)
|
||||
toFix===expr.right -> addTypecastOrCastedValueModification(modifications, expr.right, commonDt, expr)
|
||||
else -> throw FatalAstException("confused binary expression side")
|
||||
if(commonDt==DataType.BOOL) {
|
||||
// don't automatically cast to bool
|
||||
errors.err("left and right operands aren't the same type: $leftDt vs $rightDt", expr.position)
|
||||
} else {
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
when {
|
||||
toFix===expr.left -> addTypecastOrCastedValueModification(modifications, expr.left, commonDt, expr)
|
||||
toFix===expr.right -> addTypecastOrCastedValueModification(modifications, expr.right, commonDt, expr)
|
||||
else -> throw FatalAstException("confused binary expression side")
|
||||
}
|
||||
return modifications
|
||||
}
|
||||
return modifications
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -196,18 +265,14 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||
if(valuetype in IterableDatatypes && targettype==DataType.UWORD)
|
||||
// special case, don't typecast STR/arrays to UWORD, we support those assignments "directly"
|
||||
return noModifications
|
||||
if((assignment.value as? BinaryExpression)?.operator in ComparisonOperators && targettype in IntegerDatatypes) {
|
||||
// special case, treat a boolean comparison result as the same type as the target value to avoid needless casts later
|
||||
return noModifications
|
||||
}
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
addTypecastOrCastedValueModification(modifications, assignment.value, targettype, assignment)
|
||||
return modifications
|
||||
} else {
|
||||
fun castLiteral(cvalue2: NumericLiteral): List<IAstModification.ReplaceNode> {
|
||||
val cast = cvalue2.cast(targettype)
|
||||
val cast = cvalue2.cast(targettype, true)
|
||||
return if(cast.isValid)
|
||||
listOf(IAstModification.ReplaceNode(assignment.value, cast.value!!, assignment))
|
||||
listOf(IAstModification.ReplaceNode(assignment.value, cast.valueOrZero(), assignment))
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
|
@ -215,6 +280,16 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||
if(cvalue!=null) {
|
||||
val number = cvalue.number
|
||||
// more complex comparisons if the type is different, but the constant value is compatible
|
||||
if(!options.strictBool) {
|
||||
if (targettype == DataType.BOOL && valuetype in ByteDatatypes) {
|
||||
val cast = NumericLiteral.fromBoolean(number!=0.0, cvalue.position)
|
||||
return listOf(IAstModification.ReplaceNode(assignment.value, cast, assignment))
|
||||
}
|
||||
if (targettype in ByteDatatypes && valuetype == DataType.BOOL) {
|
||||
val cast = NumericLiteral(targettype, if(cvalue.asBooleanValue) 1.0 else 0.0, cvalue.position)
|
||||
return listOf(IAstModification.ReplaceNode(assignment.value, cast, assignment))
|
||||
}
|
||||
}
|
||||
if (valuetype == DataType.BYTE && targettype == DataType.UBYTE) {
|
||||
if(number>0)
|
||||
return castLiteral(cvalue)
|
||||
|
@ -228,6 +303,17 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||
if(number<0x8000)
|
||||
return castLiteral(cvalue)
|
||||
}
|
||||
} else {
|
||||
if(!options.strictBool) {
|
||||
if (targettype == DataType.BOOL && valuetype in ByteDatatypes) {
|
||||
val cast = TypecastExpression(assignment.value, targettype, false, assignment.value.position)
|
||||
return listOf(IAstModification.ReplaceNode(assignment.value, cast, assignment))
|
||||
}
|
||||
if (targettype in ByteDatatypes && valuetype == DataType.BOOL) {
|
||||
val cast = TypecastExpression(assignment.value, targettype, false, assignment.value.position)
|
||||
return listOf(IAstModification.ReplaceNode(assignment.value, cast, assignment))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -244,7 +330,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||
}
|
||||
|
||||
private fun afterFunctionCallArgs(call: IFunctionCall): Iterable<IAstModification> {
|
||||
// see if a typecast is needed to convert the arguments into the required parameter's type
|
||||
// see if a typecast is needed to convert the arguments into the required parameter type
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
val sub = call.target.targetStatement(program)
|
||||
val params = when(sub) {
|
||||
|
@ -280,7 +366,8 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||
} else if(argDt isAssignableTo targetDt) {
|
||||
if(argDt!=DataType.STR || targetDt!=DataType.UWORD)
|
||||
addTypecastOrCastedValueModification(modifications, it.second, targetDt, call as Node)
|
||||
}
|
||||
} else if(!options.strictBool && targetDt in ByteDatatypes && argDt==DataType.BOOL)
|
||||
addTypecastOrCastedValueModification(modifications, it.second, targetDt, call as Node)
|
||||
}
|
||||
} else {
|
||||
val identifier = it.second as? IdentifierReference
|
||||
|
@ -314,7 +401,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||
val modifications = mutableListOf<IAstModification>()
|
||||
val dt = memread.addressExpression.inferType(program)
|
||||
if(dt.isKnown && dt.getOr(DataType.UWORD)!=DataType.UWORD) {
|
||||
val castedValue = (memread.addressExpression as? NumericLiteral)?.cast(DataType.UWORD)?.value
|
||||
val castedValue = (memread.addressExpression as? NumericLiteral)?.cast(DataType.UWORD, true)?.valueOrZero()
|
||||
if(castedValue!=null)
|
||||
modifications += IAstModification.ReplaceNode(memread.addressExpression, castedValue, memread)
|
||||
else
|
||||
|
@ -328,7 +415,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||
val modifications = mutableListOf<IAstModification>()
|
||||
val dt = memwrite.addressExpression.inferType(program)
|
||||
if(dt.isKnown && dt.getOr(DataType.UWORD)!=DataType.UWORD) {
|
||||
val castedValue = (memwrite.addressExpression as? NumericLiteral)?.cast(DataType.UWORD)?.value
|
||||
val castedValue = (memwrite.addressExpression as? NumericLiteral)?.cast(DataType.UWORD, true)?.valueOrZero()
|
||||
if(castedValue!=null)
|
||||
modifications += IAstModification.ReplaceNode(memwrite.addressExpression, castedValue, memwrite)
|
||||
else
|
||||
|
@ -346,12 +433,24 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||
if(subroutine.returntypes.size==1) {
|
||||
val subReturnType = subroutine.returntypes.first()
|
||||
val returnDt = returnValue.inferType(program)
|
||||
|
||||
if(!options.strictBool) {
|
||||
if(subReturnType==DataType.BOOL && returnDt.isBytes) {
|
||||
val cast = TypecastExpression(returnValue, DataType.BOOL, false, returnValue.position)
|
||||
return listOf(IAstModification.ReplaceNode(returnValue, cast, returnStmt))
|
||||
}
|
||||
if(subReturnType in ByteDatatypes && returnDt.isBool) {
|
||||
val cast = TypecastExpression(returnValue, subReturnType, false, returnValue.position)
|
||||
return listOf(IAstModification.ReplaceNode(returnValue, cast, returnStmt))
|
||||
}
|
||||
}
|
||||
|
||||
if (returnDt istype subReturnType or returnDt.isNotAssignableTo(subReturnType))
|
||||
return noModifications
|
||||
if (returnValue is NumericLiteral) {
|
||||
val cast = returnValue.cast(subReturnType)
|
||||
val cast = returnValue.cast(subReturnType, true)
|
||||
if(cast.isValid)
|
||||
returnStmt.value = cast.value
|
||||
returnStmt.value = cast.valueOrZero()
|
||||
} else {
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
addTypecastOrCastedValueModification(modifications, returnValue, subReturnType, returnStmt)
|
||||
|
@ -388,6 +487,13 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||
return modifications
|
||||
}
|
||||
|
||||
override fun after(ifElse: IfElse, parent: Node): Iterable<IAstModification> {
|
||||
if(!options.strictBool && ifElse.condition.inferType(program).isBytes) {
|
||||
val cast = TypecastExpression(ifElse.condition, DataType.BOOL, false, ifElse.condition.position)
|
||||
return listOf(IAstModification.ReplaceNode(ifElse.condition, cast, ifElse))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun addTypecastOrCastedValueModification(
|
||||
modifications: MutableList<IAstModification>,
|
||||
|
@ -398,13 +504,28 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||
val sourceDt = expressionToCast.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(sourceDt == requiredType)
|
||||
return
|
||||
if(!options.strictBool) {
|
||||
if(requiredType==DataType.BOOL && sourceDt in ByteDatatypes) {
|
||||
val cast = TypecastExpression(expressionToCast, DataType.BOOL, false, expressionToCast.position)
|
||||
modifications.add(IAstModification.ReplaceNode(expressionToCast, cast, parent))
|
||||
return
|
||||
}
|
||||
if(requiredType in ByteDatatypes && sourceDt==DataType.BOOL) {
|
||||
val cast = TypecastExpression(expressionToCast, requiredType, false, expressionToCast.position)
|
||||
modifications.add(IAstModification.ReplaceNode(expressionToCast, cast, parent))
|
||||
return
|
||||
}
|
||||
}
|
||||
if(requiredType==DataType.BOOL) {
|
||||
return
|
||||
}
|
||||
if(expressionToCast is NumericLiteral && expressionToCast.type!=DataType.FLOAT) { // refuse to automatically truncate floats
|
||||
val castedValue = expressionToCast.cast(requiredType)
|
||||
val castedValue = expressionToCast.cast(requiredType, true)
|
||||
if (castedValue.isValid) {
|
||||
val signOriginal = sign(expressionToCast.number)
|
||||
val signCasted = sign(castedValue.value!!.number)
|
||||
val signCasted = sign(castedValue.valueOrZero().number)
|
||||
if(signOriginal==signCasted) {
|
||||
modifications += IAstModification.ReplaceNode(expressionToCast, castedValue.value!!, parent)
|
||||
modifications += IAstModification.ReplaceNode(expressionToCast, castedValue.valueOrZero(), parent)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -48,13 +48,13 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
|||
return listOf(IAstModification.ReplaceNode(typecast, constValue, parent))
|
||||
|
||||
if(typecast.expression is NumericLiteral) {
|
||||
val value = (typecast.expression as NumericLiteral).cast(typecast.type)
|
||||
val value = (typecast.expression as NumericLiteral).cast(typecast.type, typecast.implicit)
|
||||
if(value.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, value.value!!, parent))
|
||||
return listOf(IAstModification.ReplaceNode(typecast, value.valueOrZero(), parent))
|
||||
}
|
||||
|
||||
val sourceDt = typecast.expression.inferType(program)
|
||||
if(sourceDt istype typecast.type || (sourceDt istype DataType.BOOL && typecast.type==DataType.UBYTE))
|
||||
if(sourceDt istype typecast.type)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||
|
||||
if(parent is Assignment) {
|
||||
|
@ -65,13 +65,6 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
|||
}
|
||||
}
|
||||
|
||||
// if the expression is a comparison expression, or a logical expression, it produces the
|
||||
// correct 'boolean' byte result so the cast can be removed. Only if target is Integer.
|
||||
val binExpr = typecast.expression as? BinaryExpression
|
||||
if(binExpr!=null && binExpr.operator in ComparisonOperators + LogicalOperators && typecast.type in IntegerDatatypesNoBool) {
|
||||
return listOf(IAstModification.ReplaceNode(typecast, binExpr, parent))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
@ -103,6 +96,17 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
|||
// +X --> X
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.expression, parent))
|
||||
}
|
||||
|
||||
if(!options.strictBool && expr.operator=="not") {
|
||||
if(expr.expression.inferType(program).isBytes) {
|
||||
// not bytevalue --> bytevalue==0
|
||||
val cmp = BinaryExpression(expr.expression, "==",
|
||||
NumericLiteral(expr.expression.inferType(program).getOr(DataType.UNDEFINED), 0.0, expr.expression.position),
|
||||
expr.expression.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, cmp, parent))
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
@ -152,6 +156,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
|||
}
|
||||
}
|
||||
|
||||
/* TODO: is this really no longer needed in boolean branch?
|
||||
if(expr.operator in LogicalOperators) {
|
||||
// remove redundant !=0 comparisons from logical expressions such as: a!=0 xor b --> a xor b (only for byte operands!)
|
||||
val leftExpr = expr.left as? BinaryExpression
|
||||
|
@ -171,6 +176,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
|||
return listOf(IAstModification.ReplaceNode(rightExpr, rightExpr.left, expr))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import prog8.ast.statements.*
|
|||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.core.*
|
||||
|
||||
internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorReporter) : IAstVisitor {
|
||||
internal class VerifyFunctionArgTypes(val program: Program, val options: CompilationOptions, val errors: IErrorReporter) : IAstVisitor {
|
||||
|
||||
override fun visit(program: Program) {
|
||||
super.visit(program)
|
||||
|
@ -41,7 +41,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
|
|||
private val memorySlabs = mutableListOf<Slab>()
|
||||
|
||||
override fun visit(functionCallExpr: FunctionCallExpression) {
|
||||
val error = checkTypes(functionCallExpr as IFunctionCall, program)
|
||||
val error = checkTypes(functionCallExpr as IFunctionCall, program, options)
|
||||
if(error!=null)
|
||||
errors.err(error.first, error.second)
|
||||
else {
|
||||
|
@ -62,7 +62,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
|
|||
}
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||
val error = checkTypes(functionCallStatement as IFunctionCall, program)
|
||||
val error = checkTypes(functionCallStatement as IFunctionCall, program, options)
|
||||
if(error!=null)
|
||||
errors.err(error.first, error.second)
|
||||
|
||||
|
@ -85,7 +85,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
|
|||
return false
|
||||
}
|
||||
|
||||
fun checkTypes(call: IFunctionCall, program: Program): Pair<String, Position>? {
|
||||
fun checkTypes(call: IFunctionCall, program: Program, options: CompilationOptions): Pair<String, Position>? {
|
||||
val argITypes = call.args.map { it.inferType(program) }
|
||||
val firstUnknownDt = argITypes.indexOfFirst { it.isUnknown }
|
||||
if(firstUnknownDt>=0) {
|
||||
|
@ -105,10 +105,10 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
|
|||
if(mismatch>=0) {
|
||||
val actual = argtypes[mismatch]
|
||||
val expected = consideredParamTypes[mismatch]
|
||||
return if(actual==DataType.BOOL && expected in ByteDatatypes)
|
||||
null // a bool is just 1 or 0.
|
||||
else if(expected==DataType.BOOL && actual==DataType.UBYTE && call.args[mismatch].constValue(program)?.number in setOf(0.0, 1.0))
|
||||
null // specifying a 1 or 0 as a BOOL is okay
|
||||
return if(expected==DataType.BOOL && !options.strictBool && actual in ByteDatatypes)
|
||||
null
|
||||
else if(expected in ByteDatatypes && !options.strictBool && actual==DataType.BOOL)
|
||||
null
|
||||
else
|
||||
Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import prog8.ast.statements.Assignment
|
|||
import prog8.ast.statements.FunctionCallStatement
|
||||
import prog8.code.core.BuiltinFunctions
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.NumericDatatypesNoBool
|
||||
import prog8.code.core.NumericDatatypes
|
||||
import prog8.code.core.RegisterOrPair
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8tests.helpers.compileText
|
||||
|
@ -18,7 +18,7 @@ class TestBuiltinFunctions: FunSpec({
|
|||
val func = BuiltinFunctions.getValue("sgn")
|
||||
func.parameters.size shouldBe 1
|
||||
func.parameters[0].name shouldBe "value"
|
||||
func.parameters[0].possibleDatatypes shouldBe NumericDatatypesNoBool
|
||||
func.parameters[0].possibleDatatypes shouldBe NumericDatatypes
|
||||
func.pure shouldBe true
|
||||
func.returnType shouldBe DataType.BYTE
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
|
|||
breakpointCpuInstruction = null,
|
||||
printAst1 = false,
|
||||
printAst2 = false,
|
||||
strictBool = true,
|
||||
symbolDefs = emptyMap(),
|
||||
outputDir = outputDir
|
||||
)
|
||||
|
|
|
@ -164,32 +164,6 @@ class TestCompilerOnRanges: FunSpec({
|
|||
}
|
||||
}
|
||||
|
||||
test("testForLoopWithRange_bool_to_bool") {
|
||||
val platform = Cx16Target()
|
||||
val result = compileText(platform, optimize = true, """
|
||||
main {
|
||||
sub start() {
|
||||
ubyte i
|
||||
for i in false to true {
|
||||
i += i ; keep optimizer from removing it
|
||||
}
|
||||
}
|
||||
}
|
||||
""")!!
|
||||
|
||||
val program = result.compilerAst
|
||||
val startSub = program.entrypoint
|
||||
val rangeExpr = startSub
|
||||
.statements.filterIsInstance<ForLoop>()
|
||||
.map { it.iterable }
|
||||
.filterIsInstance<RangeExpression>()[0]
|
||||
|
||||
rangeExpr.size() shouldBe 2
|
||||
val intProgression = rangeExpr.toConstantIntegerRange()
|
||||
intProgression?.first shouldBe 0
|
||||
intProgression?.last shouldBe 1
|
||||
}
|
||||
|
||||
test("testForLoopWithRange_ubyte_to_ubyte") {
|
||||
val platform = Cx16Target()
|
||||
val result = compileText(platform, optimize = true, """
|
||||
|
@ -421,7 +395,7 @@ class TestCompilerOnRanges: FunSpec({
|
|||
C64Target(), false, """
|
||||
main {
|
||||
sub start() {
|
||||
ubyte xx
|
||||
bool xx
|
||||
uword ww
|
||||
str name = "irmen"
|
||||
ubyte[] values = [1,2,3,4,5,6,7]
|
||||
|
|
|
@ -40,6 +40,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
|
|||
breakpointCpuInstruction = null,
|
||||
printAst1 = false,
|
||||
printAst2 = false,
|
||||
strictBool = true,
|
||||
symbolDefs = emptyMap(),
|
||||
sourceDirs,
|
||||
outputDir
|
||||
|
|
|
@ -105,11 +105,11 @@ class TestOptimization: FunSpec({
|
|||
sub start() {
|
||||
bool @shared a1
|
||||
bool @shared a2
|
||||
a1 = not a1 ; a1 = a1==0
|
||||
a1 = not a1 ; a1 = not a1
|
||||
a1 = not not a1 ; a1 = a1, so removed totally
|
||||
a1 = not not not a1 ; a1 = a1==0
|
||||
a1 = not a1 or not a2 ; a1 = (a1 and a2)==0
|
||||
a1 = not a1 and not a2 ; a1 = (a1 or a2)==0
|
||||
a1 = not not not a1 ; a1 = not a1
|
||||
a1 = not a1 or not a2 ; a1 = not (a1 and a2)
|
||||
a1 = not a1 and not a2 ; a1 = not (a1 or a2)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
@ -117,20 +117,18 @@ class TestOptimization: FunSpec({
|
|||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 9
|
||||
|
||||
val value1 = (stmts[4] as Assignment).value as BinaryExpression
|
||||
val value2 = (stmts[5] as Assignment).value as BinaryExpression
|
||||
val value3 = (stmts[6] as Assignment).value as BinaryExpression
|
||||
val value4 = (stmts[7] as Assignment).value as BinaryExpression
|
||||
value1.operator shouldBe "=="
|
||||
value1.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
value2.operator shouldBe "=="
|
||||
value2.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
value3.operator shouldBe "=="
|
||||
value3.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
(value3.left as BinaryExpression).operator shouldBe "and"
|
||||
value4.operator shouldBe "=="
|
||||
value4.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
(value4.left as BinaryExpression).operator shouldBe "or"
|
||||
val value1 = (stmts[4] as Assignment).value as PrefixExpression
|
||||
val value2 = (stmts[5] as Assignment).value as PrefixExpression
|
||||
val value3 = (stmts[6] as Assignment).value as PrefixExpression
|
||||
val value4 = (stmts[7] as Assignment).value as PrefixExpression
|
||||
value1.operator shouldBe "not"
|
||||
value2.operator shouldBe "not"
|
||||
value3.operator shouldBe "not"
|
||||
value4.operator shouldBe "not"
|
||||
value1.expression shouldBe instanceOf<IdentifierReference>()
|
||||
value2.expression shouldBe instanceOf<IdentifierReference>()
|
||||
(value3.expression as BinaryExpression).operator shouldBe "and"
|
||||
(value4.expression as BinaryExpression).operator shouldBe "or"
|
||||
}
|
||||
|
||||
test("various 'not' operator rewrites with optimizations") {
|
||||
|
@ -139,11 +137,11 @@ class TestOptimization: FunSpec({
|
|||
sub start() {
|
||||
bool @shared a1
|
||||
bool @shared a2
|
||||
a1 = not a1 ; a1 = a1==0
|
||||
a1 = not not a1 ; a1 = a1, so removed totally
|
||||
a1 = not not not a1 ; a1 = a1==0
|
||||
a1 = not a1 or not a2 ; a1 = (a1 and a2)==0
|
||||
a1 = not a1 and not a2 ; a1 = (a1 or a2)==0
|
||||
a1 = not a1 ; a1 = not a1
|
||||
a1 = not not a1 ; a1 = a1, so removed totally
|
||||
a1 = not not not a1 ; a1 = not a1
|
||||
a1 = not a1 or not a2 ; a1 = not (a1 and a2)
|
||||
a1 = not a1 and not a2 ; a1 = not (a1 or a2)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
@ -151,20 +149,18 @@ class TestOptimization: FunSpec({
|
|||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 9
|
||||
|
||||
val value1 = (stmts[4] as Assignment).value as BinaryExpression
|
||||
val value2 = (stmts[5] as Assignment).value as BinaryExpression
|
||||
val value3 = (stmts[6] as Assignment).value as BinaryExpression
|
||||
val value4 = (stmts[7] as Assignment).value as BinaryExpression
|
||||
value1.operator shouldBe "=="
|
||||
value1.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
value2.operator shouldBe "=="
|
||||
value2.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
value3.operator shouldBe "=="
|
||||
value3.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
(value3.left as BinaryExpression).operator shouldBe "and"
|
||||
value4.operator shouldBe "=="
|
||||
value4.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
(value4.left as BinaryExpression).operator shouldBe "or"
|
||||
val value1 = (stmts[4] as Assignment).value as PrefixExpression
|
||||
val value2 = (stmts[5] as Assignment).value as PrefixExpression
|
||||
val value3 = (stmts[6] as Assignment).value as PrefixExpression
|
||||
val value4 = (stmts[7] as Assignment).value as PrefixExpression
|
||||
value1.operator shouldBe "not"
|
||||
value2.operator shouldBe "not"
|
||||
value3.operator shouldBe "not"
|
||||
value4.operator shouldBe "not"
|
||||
value1.expression shouldBe instanceOf<IdentifierReference>()
|
||||
value2.expression shouldBe instanceOf<IdentifierReference>()
|
||||
(value3.expression as BinaryExpression).operator shouldBe "and"
|
||||
(value4.expression as BinaryExpression).operator shouldBe "or"
|
||||
}
|
||||
|
||||
test("asmgen correctly deals with float typecasting in augmented assignment") {
|
||||
|
@ -215,7 +211,7 @@ class TestOptimization: FunSpec({
|
|||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
if cx16.r0 {
|
||||
if cx16.r0!=0 {
|
||||
uword xx ; to be removed
|
||||
cx16.r0 = 0
|
||||
}
|
||||
|
@ -278,7 +274,7 @@ main {
|
|||
z1 = 10
|
||||
ubyte @shared z2
|
||||
z2 = ~z2
|
||||
ubyte @shared z3
|
||||
bool @shared z3
|
||||
z3 = not z3
|
||||
uword @shared z4
|
||||
z4 = (z4 as ubyte)
|
||||
|
@ -294,8 +290,8 @@ main {
|
|||
z1 = 10
|
||||
ubyte z2
|
||||
z2 = 255
|
||||
ubyte z3
|
||||
z3 = 1
|
||||
bool z3
|
||||
z3 = true
|
||||
uword z4
|
||||
z4 = 0
|
||||
ubyte z5
|
||||
|
@ -323,9 +319,9 @@ main {
|
|||
z2decl.name shouldBe "z2"
|
||||
z2init.value shouldBe NumericLiteral(DataType.UBYTE, 255.0, Position.DUMMY)
|
||||
z3decl.name shouldBe "z3"
|
||||
z3init.value shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
|
||||
z3init.value shouldBe NumericLiteral(DataType.BOOL, 1.0, Position.DUMMY)
|
||||
z4decl.name shouldBe "z4"
|
||||
z4init.value shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
z4init.value shouldBe NumericLiteral(DataType.UWORD, 0.0, Position.DUMMY)
|
||||
z5decl.name shouldBe "z5"
|
||||
(z5init.value as BinaryExpression).operator shouldBe "+"
|
||||
(z5init.value as BinaryExpression).right shouldBe NumericLiteral(DataType.UBYTE, 5.0, Position.DUMMY)
|
||||
|
@ -378,7 +374,7 @@ main {
|
|||
ubyte @shared z1 = 1
|
||||
ubyte @shared z2 = + 1
|
||||
ubyte @shared z3 = ~ 1
|
||||
ubyte @shared z4 = not 1
|
||||
bool @shared z4 = not 1
|
||||
byte @shared z5 = - 1
|
||||
}
|
||||
}
|
||||
|
@ -400,8 +396,9 @@ main {
|
|||
"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "out of range"
|
||||
errors.errors[1] shouldContain "cannot assign word to byte"
|
||||
}
|
||||
|
||||
test("out of range cast should give error") {
|
||||
|
@ -495,13 +492,13 @@ main {
|
|||
stmts.filterIsInstance<Assignment>().size shouldBe 5
|
||||
val assignXX1 = stmts[1] as Assignment
|
||||
assignXX1.target.identifier!!.nameInSource shouldBe listOf("xx")
|
||||
assignXX1.value shouldBe NumericLiteral(DataType.UBYTE, 20.0, Position.DUMMY)
|
||||
assignXX1.value shouldBe NumericLiteral(DataType.UWORD, 20.0, Position.DUMMY)
|
||||
val assignXX2 = stmts.last() as Assignment
|
||||
assignXX2.target.identifier!!.nameInSource shouldBe listOf("xx")
|
||||
val xxValue = assignXX2.value as BinaryExpression
|
||||
xxValue.operator shouldBe "+"
|
||||
(xxValue.left as? IdentifierReference)?.nameInSource shouldBe listOf("xx")
|
||||
xxValue.right shouldBe NumericLiteral(DataType.UBYTE, 10.0, Position.DUMMY)
|
||||
xxValue.right shouldBe NumericLiteral(DataType.UWORD, 10.0, Position.DUMMY)
|
||||
}
|
||||
|
||||
test("multi-comparison replaced by containment check") {
|
||||
|
@ -603,7 +600,7 @@ main {
|
|||
main{
|
||||
sub start () {
|
||||
uword @shared eRef
|
||||
if eRef[3] and 10 {
|
||||
if eRef[3] & 10 ==0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -780,16 +777,17 @@ main {
|
|||
sub start() {
|
||||
bool @shared a1
|
||||
bool @shared a2
|
||||
|
||||
if a1==0 and a2==0
|
||||
cx16.r0++
|
||||
if a1==0 or a2==0
|
||||
cx16.r0++
|
||||
|
||||
if not a1 or not a2
|
||||
cx16.r0++
|
||||
if not a1 and not a2
|
||||
cx16.r0++
|
||||
|
||||
if cx16.r0L==42 or not a2
|
||||
cx16.r0L++
|
||||
if not a2 or cx16.r0L==42
|
||||
cx16.r0L++
|
||||
|
||||
}
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), true, src, writeAssembly = false)!!
|
||||
|
@ -803,10 +801,10 @@ main {
|
|||
if2c.operator shouldBe "not"
|
||||
if3c.operator shouldBe "not"
|
||||
if4c.operator shouldBe "not"
|
||||
(if1c.expression as BinaryExpression).operator shouldBe "or"
|
||||
(if2c.expression as BinaryExpression).operator shouldBe "and"
|
||||
(if1c.expression as BinaryExpression).operator shouldBe "and"
|
||||
(if2c.expression as BinaryExpression).operator shouldBe "or"
|
||||
(if3c.expression as BinaryExpression).operator shouldBe "and"
|
||||
(if4c.expression as BinaryExpression).operator shouldBe "or"
|
||||
(if4c.expression as BinaryExpression).operator shouldBe "and"
|
||||
}
|
||||
|
||||
test("absorption laws") {
|
||||
|
|
|
@ -87,8 +87,8 @@ class TestSymbolTable: FunSpec({
|
|||
val node = PtIdentifier("dummy", DataType.UBYTE, Position.DUMMY)
|
||||
val stVar1 = StStaticVariable("initialized", DataType.UBYTE, 99.0, null, null, null, ZeropageWish.DONTCARE, node)
|
||||
val stVar2 = StStaticVariable("uninitialized", DataType.UBYTE, null, null, null, null, ZeropageWish.DONTCARE, node)
|
||||
val arrayInitNonzero = listOf(StArrayElement(1.1, null), StArrayElement(2.2, null), StArrayElement(3.3, null))
|
||||
val arrayInitAllzero = listOf(StArrayElement(0.0, null), StArrayElement(0.0, null), StArrayElement(0.0, null))
|
||||
val arrayInitNonzero = listOf(StArrayElement(1.1, null, null), StArrayElement(2.2, null, null), StArrayElement(3.3, null, null))
|
||||
val arrayInitAllzero = listOf(StArrayElement(0.0, null, null), StArrayElement(0.0, null, null), StArrayElement(0.0, null, null))
|
||||
val stVar3 = StStaticVariable("initialized", DataType.ARRAY_UW, null, null, arrayInitNonzero, 3, ZeropageWish.DONTCARE, node)
|
||||
val stVar4 = StStaticVariable("initialized", DataType.ARRAY_UW, null, null, arrayInitAllzero, 3, ZeropageWish.DONTCARE, node)
|
||||
val stVar5 = StStaticVariable("uninitialized", DataType.ARRAY_UW, null, null, null, 3, ZeropageWish.DONTCARE, node)
|
||||
|
|
|
@ -8,6 +8,7 @@ import io.kotest.matchers.string.shouldContain
|
|||
import io.kotest.matchers.types.instanceOf
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.printProgram
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.IfElse
|
||||
import prog8.ast.statements.VarDecl
|
||||
|
@ -41,29 +42,28 @@ class TestTypecasts: FunSpec({
|
|||
|
||||
test("not casting bool operands to logical operators") {
|
||||
val text="""
|
||||
%import textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
bool @shared bb2=true
|
||||
bool @shared bb = bb2 and true
|
||||
bool @shared bb3=false
|
||||
bool @shared bb = bb2 and bb3
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 4
|
||||
val expr = (stmts[3] as Assignment).value as BinaryExpression
|
||||
stmts.size shouldBe 6
|
||||
val expr = (stmts[5] as Assignment).value as BinaryExpression
|
||||
expr.operator shouldBe "and"
|
||||
expr.right shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
|
||||
(expr.left as IdentifierReference).nameInSource shouldBe listOf("bb2") // no cast
|
||||
(expr.right as IdentifierReference).nameInSource shouldBe listOf("bb3") // no cast
|
||||
|
||||
val result2 = compileText(C64Target(), true, text, writeAssembly = true)!!
|
||||
val stmts2 = result2.compilerAst.entrypoint.statements
|
||||
stmts2.size shouldBe 5
|
||||
val expr2 = (stmts2[3] as Assignment).value as BinaryExpression
|
||||
stmts2.size shouldBe 7
|
||||
val expr2 = (stmts2[5] as Assignment).value as BinaryExpression
|
||||
expr2.operator shouldBe "and"
|
||||
expr2.right shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
|
||||
(expr2.left as IdentifierReference).nameInSource shouldBe listOf("bb2") // no cast
|
||||
(expr2.right as IdentifierReference).nameInSource shouldBe listOf("bb3") // no cast
|
||||
}
|
||||
|
||||
test("bool expressions with functioncalls") {
|
||||
|
@ -86,23 +86,24 @@ main {
|
|||
bool @shared bvalue
|
||||
|
||||
bvalue = ub1 xor ub2 xor ub3 xor true
|
||||
bvalue = ub1 xor ub2 xor ub3 xor ftrue(99)
|
||||
bvalue = ub1 and ub2 and ftrue(99)
|
||||
bvalue = ub1 xor ub2 xor ub3 xor ftrue(99)!=0
|
||||
bvalue = ub1 and ub2 and ftrue(99)!=0
|
||||
bvalue = ub1 xor ub2 xor ub3 xor btrue(99)
|
||||
bvalue = ub1 and ub2 and btrue(99)
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
printProgram(result.compilerAst)
|
||||
/*
|
||||
ubyte @shared ub1
|
||||
ub1 = 1
|
||||
ubyte @shared ub2
|
||||
ub2 = 1
|
||||
ubyte @shared ub3
|
||||
ub3 = 1
|
||||
ubyte @shared bvalue
|
||||
bvalue = (((ub1 xor ub2) xor ub3) xor 1)
|
||||
bool @shared ub1
|
||||
ub1 = true
|
||||
bool @shared ub2
|
||||
ub2 = true
|
||||
bool @shared ub3
|
||||
ub3 = true
|
||||
bool @shared bvalue
|
||||
bvalue = not ((ub1 xor ub2) xor ub3)
|
||||
bvalue = (((ub1 xor ub2) xor ub3) xor (ftrue(99)!=0))
|
||||
bvalue = ((ub1 and ub2) and (ftrue(99)!=0))
|
||||
bvalue = (((ub1 xor ub2) xor ub3) xor btrue(99))
|
||||
|
@ -110,12 +111,12 @@ main {
|
|||
return
|
||||
*/
|
||||
stmts.size shouldBe 13
|
||||
val assignValue1 = (stmts[7] as Assignment).value as BinaryExpression
|
||||
val assignValue1 = (stmts[7] as Assignment).value as PrefixExpression
|
||||
val assignValue2 = (stmts[8] as Assignment).value as BinaryExpression
|
||||
val assignValue3 = (stmts[9] as Assignment).value as BinaryExpression
|
||||
val assignValue4 = (stmts[10] as Assignment).value as BinaryExpression
|
||||
val assignValue5 = (stmts[11] as Assignment).value as BinaryExpression
|
||||
assignValue1.operator shouldBe "xor"
|
||||
assignValue1.operator shouldBe "not"
|
||||
assignValue2.operator shouldBe "xor"
|
||||
assignValue3.operator shouldBe "and"
|
||||
assignValue4.operator shouldBe "xor"
|
||||
|
@ -132,478 +133,6 @@ main {
|
|||
assignValue5.right shouldBe instanceOf<IFunctionCall>()
|
||||
}
|
||||
|
||||
test("simple logical with byte instead of bool ok with typecasting") {
|
||||
val text="""
|
||||
main {
|
||||
ubyte ubb
|
||||
|
||||
sub start() {
|
||||
ubb = ubb and 123
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 2
|
||||
val assignValue = (stmts[0] as Assignment).value as BinaryExpression
|
||||
assignValue.left shouldBe instanceOf<BinaryExpression>() // as a result of the cast to boolean
|
||||
assignValue.operator shouldBe "and"
|
||||
(assignValue.right as NumericLiteral).number shouldBe 1.0
|
||||
}
|
||||
|
||||
test("logical with byte instead of bool") {
|
||||
val text="""
|
||||
%import textio
|
||||
|
||||
main {
|
||||
|
||||
sub ftrue(ubyte arg) -> ubyte {
|
||||
arg++
|
||||
return 128
|
||||
}
|
||||
|
||||
sub ffalse(ubyte arg) -> ubyte {
|
||||
arg++
|
||||
return 0
|
||||
}
|
||||
|
||||
sub start() {
|
||||
ubyte ub1 = 2
|
||||
ubyte ub2 = 4
|
||||
ubyte ub3 = 8
|
||||
ubyte ub4 = 0
|
||||
ubyte bvalue
|
||||
|
||||
txt.print("const not 0: ")
|
||||
txt.print_ub(not 129)
|
||||
txt.nl()
|
||||
txt.print("const not 1: ")
|
||||
txt.print_ub(not 0)
|
||||
txt.nl()
|
||||
txt.print("const inv 126: ")
|
||||
txt.print_ub(~ 129)
|
||||
txt.nl()
|
||||
txt.print("const inv 255: ")
|
||||
txt.print_ub(~ 0)
|
||||
txt.nl()
|
||||
bvalue = 129
|
||||
txt.print("bitwise inv 126: ")
|
||||
bvalue = ~ bvalue
|
||||
txt.print_ub(bvalue)
|
||||
txt.nl()
|
||||
bvalue = 0
|
||||
txt.print("bitwise inv 255: ")
|
||||
bvalue = ~ bvalue
|
||||
txt.print_ub(bvalue)
|
||||
txt.nl()
|
||||
|
||||
txt.print("bitwise or 14: ")
|
||||
txt.print_ub(ub1 | ub2 | ub3 | ub4)
|
||||
txt.nl()
|
||||
txt.print("bitwise or 142: ")
|
||||
txt.print_ub(ub1 | ub2 | ub3 | ub4 | 128)
|
||||
txt.nl()
|
||||
txt.print("bitwise and 0: ")
|
||||
txt.print_ub(ub1 & ub2 & ub3 & ub4)
|
||||
txt.nl()
|
||||
txt.print("bitwise and 8: ")
|
||||
txt.print_ub(ub3 & ub3 & 127)
|
||||
txt.nl()
|
||||
txt.print("bitwise xor 14: ")
|
||||
txt.print_ub(ub1 ^ ub2 ^ ub3 ^ ub4)
|
||||
txt.nl()
|
||||
txt.print("bitwise xor 6: ")
|
||||
txt.print_ub(ub1 ^ ub2 ^ ub3 ^ 8)
|
||||
txt.nl()
|
||||
txt.print("bitwise not 247: ")
|
||||
txt.print_ub(~ub3)
|
||||
txt.nl()
|
||||
txt.print("bitwise not 255: ")
|
||||
txt.print_ub(~ub4)
|
||||
txt.nl()
|
||||
|
||||
txt.print("not 0: ")
|
||||
bvalue = 3 * (ub4 | not (ub3 | ub3 | ub3))
|
||||
txt.print_ub(bvalue)
|
||||
if 3*(ub4 | not (ub1 | ub1 | ub1))
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
txt.nl()
|
||||
|
||||
txt.print("not 0: ")
|
||||
bvalue = not ub3
|
||||
txt.print_ub(bvalue)
|
||||
if not ub1
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
txt.nl()
|
||||
|
||||
txt.print("not 1: ")
|
||||
bvalue = not ub4
|
||||
txt.print_ub(bvalue)
|
||||
if not ub4
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
|
||||
bvalue = bvalue and 128
|
||||
txt.print("bvl 1: ")
|
||||
txt.print_ub(bvalue)
|
||||
if bvalue and 128
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
|
||||
txt.print("and 1: ")
|
||||
bvalue = ub1 and ub2 and ub3
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 and ub2 and ub3
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print("and 1: ")
|
||||
bvalue = ub1 and ub2 and ub3 and 64
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 and ub2 and ub3 and 64
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print("and 1: ")
|
||||
bvalue = ub1 and ub2 and ub3 and ftrue(99)
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 and ub2 and ub3 and ftrue(99)
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print("and 0: ")
|
||||
bvalue = ub1 and ub2 and ub3 and ub4
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 and ub2 and ub3 and ub4
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
txt.nl()
|
||||
txt.print("and 0: ")
|
||||
bvalue = ub1 and ub2 and ub3 and ffalse(99)
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 and ub2 and ub3 and ffalse(99)
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
txt.nl()
|
||||
|
||||
txt.print(" or 1: ")
|
||||
bvalue = ub1 or ub2 or ub3 or ub4
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 or ub2 or ub3 or ub4
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print(" or 1: ")
|
||||
bvalue = ub4 or ub4 or ub1
|
||||
txt.print_ub(bvalue)
|
||||
if ub4 or ub4 or ub1
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print(" or 1: ")
|
||||
bvalue = ub1 or ub2 or ub3 or ftrue(99)
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 or ub2 or ub3 or ftrue(99)
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
|
||||
txt.print("xor 1: ")
|
||||
bvalue = ub1 xor ub2 xor ub3 xor ub4
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 xor ub2 xor ub3 xor ub4
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print("xor 1: ")
|
||||
bvalue = ub1 xor ub2 xor ub3 xor ffalse(99)
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 xor ub2 xor ub3 xor ffalse(99)
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
|
||||
txt.print("xor 0: ")
|
||||
bvalue = ub1 xor ub2 xor ub3 xor ub4 xor true
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 xor ub2 xor ub3 xor ub4 xor true
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
txt.nl()
|
||||
txt.print("xor 0: ")
|
||||
bvalue = ub1 xor ub2 xor ub3 xor ftrue(99)
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 xor ub2 xor ub3 xor ftrue(99)
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
}
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBeGreaterThan 10
|
||||
}
|
||||
|
||||
test("logical with bools") {
|
||||
val text="""
|
||||
%import textio
|
||||
|
||||
main {
|
||||
|
||||
sub ftrue(ubyte arg) -> ubyte {
|
||||
arg++
|
||||
return 128
|
||||
}
|
||||
|
||||
sub ffalse(ubyte arg) -> ubyte {
|
||||
arg++
|
||||
return 0
|
||||
}
|
||||
|
||||
sub start() {
|
||||
bool ub1 = 2
|
||||
bool ub2 = 4
|
||||
bool ub3 = 8
|
||||
bool ub4 = 0
|
||||
bool bvalue
|
||||
|
||||
txt.print("const not 0: ")
|
||||
txt.print_ub(not 129)
|
||||
txt.nl()
|
||||
txt.print("const not 1: ")
|
||||
txt.print_ub(not 0)
|
||||
txt.nl()
|
||||
txt.print("const inv 126: ")
|
||||
txt.print_ub(~ 129)
|
||||
txt.nl()
|
||||
txt.print("const inv 255: ")
|
||||
txt.print_ub(~ 0)
|
||||
txt.nl()
|
||||
; bvalue = 129
|
||||
; txt.print("bitwise inv 126: ")
|
||||
; bvalue = ~ bvalue
|
||||
; txt.print_ub(bvalue)
|
||||
; txt.nl()
|
||||
; bvalue = 0
|
||||
; txt.print("bitwise inv 255: ")
|
||||
; bvalue = ~ bvalue
|
||||
; txt.print_ub(bvalue)
|
||||
; txt.nl()
|
||||
|
||||
txt.print("bitwise or 14: ")
|
||||
txt.print_ub(ub1 | ub2 | ub3 | ub4)
|
||||
txt.nl()
|
||||
txt.print("bitwise or 142: ")
|
||||
txt.print_ub(ub1 | ub2 | ub3 | ub4 | 128)
|
||||
txt.nl()
|
||||
txt.print("bitwise and 0: ")
|
||||
txt.print_ub(ub1 & ub2 & ub3 & ub4)
|
||||
txt.nl()
|
||||
txt.print("bitwise and 8: ")
|
||||
txt.print_ub(ub3 & ub3 & 127)
|
||||
txt.nl()
|
||||
txt.print("bitwise xor 14: ")
|
||||
txt.print_ub(ub1 ^ ub2 ^ ub3 ^ ub4)
|
||||
txt.nl()
|
||||
txt.print("bitwise xor 6: ")
|
||||
txt.print_ub(ub1 ^ ub2 ^ ub3 ^ 8)
|
||||
txt.nl()
|
||||
;txt.print("bitwise not 247: ")
|
||||
;txt.print_ub(~ub3)
|
||||
;txt.nl()
|
||||
;txt.print("bitwise not 255: ")
|
||||
;txt.print_ub(~ub4)
|
||||
;txt.nl()
|
||||
|
||||
txt.print("not 0: ")
|
||||
bvalue = 3 * (ub4 | not (ub3 | ub3 | ub3))
|
||||
txt.print_ub(bvalue)
|
||||
if 3*(ub4 | not (ub1 | ub1 | ub1))
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
txt.nl()
|
||||
|
||||
txt.print("not 0: ")
|
||||
bvalue = not ub3
|
||||
txt.print_ub(bvalue)
|
||||
if not ub1
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
txt.nl()
|
||||
|
||||
txt.print("not 1: ")
|
||||
bvalue = not ub4
|
||||
txt.print_ub(bvalue)
|
||||
if not ub4
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
|
||||
bvalue = bvalue and 128
|
||||
txt.print("bvl 1: ")
|
||||
txt.print_ub(bvalue)
|
||||
if bvalue and 128
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
|
||||
txt.print("and 1: ")
|
||||
bvalue = ub1 and ub2 and ub3
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 and ub2 and ub3
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print("and 1: ")
|
||||
bvalue = ub1 and ub2 and ub3 and 64
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 and ub2 and ub3 and 64
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print("and 1: ")
|
||||
bvalue = ub1 and ub2 and ub3 and ftrue(99)
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 and ub2 and ub3 and ftrue(99)
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print("and 0: ")
|
||||
bvalue = ub1 and ub2 and ub3 and ub4
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 and ub2 and ub3 and ub4
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
txt.nl()
|
||||
txt.print("and 0: ")
|
||||
bvalue = ub1 and ub2 and ub3 and ffalse(99)
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 and ub2 and ub3 and ffalse(99)
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
txt.nl()
|
||||
|
||||
txt.print(" or 1: ")
|
||||
bvalue = ub1 or ub2 or ub3 or ub4
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 or ub2 or ub3 or ub4
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print(" or 1: ")
|
||||
bvalue = ub4 or ub4 or ub1
|
||||
txt.print_ub(bvalue)
|
||||
if ub4 or ub4 or ub1
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print(" or 1: ")
|
||||
bvalue = ub1 or ub2 or ub3 or ftrue(99)
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 or ub2 or ub3 or ftrue(99)
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
|
||||
txt.print("xor 1: ")
|
||||
bvalue = ub1 xor ub2 xor ub3 xor ub4
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 xor ub2 xor ub3 xor ub4
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print("xor 1: ")
|
||||
bvalue = ub1 xor ub2 xor ub3 xor ffalse(99)
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 xor ub2 xor ub3 xor ffalse(99)
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
|
||||
txt.print("xor 0: ")
|
||||
bvalue = ub1 xor ub2 xor ub3 xor ub4 xor true
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 xor ub2 xor ub3 xor ub4 xor true
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
txt.nl()
|
||||
txt.print("xor 0: ")
|
||||
bvalue = ub1 xor ub2 xor ub3 xor ftrue(99)
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 xor ub2 xor ub3 xor ftrue(99)
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
}
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBeGreaterThan 10
|
||||
}
|
||||
|
||||
test("bool arrays") {
|
||||
val text="""
|
||||
main {
|
||||
sub start() {
|
||||
bool[] barray = [true, false, 1, 0, 222]
|
||||
bool bb
|
||||
ubyte xx
|
||||
|
||||
for bb in barray {
|
||||
if bb
|
||||
xx++
|
||||
}
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 6
|
||||
val arraydecl = stmts[0] as VarDecl
|
||||
arraydecl.datatype shouldBe DataType.ARRAY_BOOL
|
||||
val values = (arraydecl.value as ArrayLiteral).value
|
||||
values.size shouldBe 5
|
||||
values[0] shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
|
||||
values[1] shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
values[2] shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
|
||||
values[3] shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
values[4] shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
|
||||
}
|
||||
|
||||
test("correct handling of bool parameters") {
|
||||
val text="""
|
||||
main {
|
||||
|
@ -615,10 +144,9 @@ main {
|
|||
sub start() {
|
||||
bool boolvalue1 = true
|
||||
bool boolvalue2 = false
|
||||
uword xx
|
||||
|
||||
boolvalue1 = thing(true, false)
|
||||
boolvalue2 = thing(xx, xx)
|
||||
boolvalue2 = thing(false, true)
|
||||
|
||||
if boolvalue1 and boolvalue2
|
||||
boolvalue1=false
|
||||
|
@ -626,14 +154,14 @@ main {
|
|||
}"""
|
||||
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 9
|
||||
val fcall1 = ((stmts[6] as Assignment).value as IFunctionCall)
|
||||
fcall1.args[0] shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
|
||||
fcall1.args[1] shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
val fcall2 = ((stmts[7] as Assignment).value as IFunctionCall)
|
||||
(fcall2.args[0] as TypecastExpression).type shouldBe DataType.BOOL
|
||||
(fcall2.args[1] as TypecastExpression).type shouldBe DataType.BOOL
|
||||
val ifCond = (stmts[8] as IfElse).condition as BinaryExpression
|
||||
stmts.size shouldBe 7
|
||||
val fcall1 = ((stmts[4] as Assignment).value as IFunctionCall)
|
||||
fcall1.args[0] shouldBe NumericLiteral(DataType.BOOL, 1.0, Position.DUMMY)
|
||||
fcall1.args[1] shouldBe NumericLiteral(DataType.BOOL, 0.0, Position.DUMMY)
|
||||
val fcall2 = ((stmts[5] as Assignment).value as IFunctionCall)
|
||||
fcall2.args[0] shouldBe NumericLiteral(DataType.BOOL, 0.0, Position.DUMMY)
|
||||
fcall2.args[1] shouldBe NumericLiteral(DataType.BOOL, 1.0, Position.DUMMY)
|
||||
val ifCond = (stmts[6] as IfElse).condition as BinaryExpression
|
||||
ifCond.operator shouldBe "and" // no asm writing so logical expressions haven't been replaced with bitwise equivalents yet
|
||||
(ifCond.left as IdentifierReference).nameInSource shouldBe listOf("boolvalue1")
|
||||
(ifCond.right as IdentifierReference).nameInSource shouldBe listOf("boolvalue2")
|
||||
|
@ -644,17 +172,17 @@ main {
|
|||
main {
|
||||
sub start() {
|
||||
uword camg
|
||||
ubyte @shared interlaced
|
||||
bool @shared interlaced
|
||||
interlaced = (camg & ${'$'}0004) != 0
|
||||
interlaced++
|
||||
cx16.r0L++
|
||||
interlaced = (${'$'}0004 & camg) != 0
|
||||
interlaced++
|
||||
cx16.r0L++
|
||||
uword @shared ww
|
||||
ww = (camg & ${'$'}0004)
|
||||
ww++
|
||||
ww = (${'$'}0004 & camg)
|
||||
|
||||
ubyte @shared wordNr2 = (interlaced >= ${'$'}33) + (interlaced >= ${'$'}66) + (interlaced >= ${'$'}99) + (interlaced >= ${'$'}CC)
|
||||
ubyte @shared value
|
||||
bool @shared collected = (value >= ${'$'}33) or (value >= ${'$'}66) or (value >= ${'$'}99) or (value >= ${'$'}CC)
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
||||
|
@ -842,8 +370,8 @@ main {
|
|||
|
||||
main {
|
||||
sub start() {
|
||||
byte bb = -10
|
||||
word ww = -1000
|
||||
byte @shared bb = -10
|
||||
word @shared ww = -1000
|
||||
|
||||
if bb>0 {
|
||||
bb++
|
||||
|
@ -851,13 +379,13 @@ main {
|
|||
if bb < 0 {
|
||||
bb ++
|
||||
}
|
||||
if bb & 1 {
|
||||
if bb & 1 !=0 {
|
||||
bb++
|
||||
}
|
||||
if bb & 128 {
|
||||
if bb & 128 !=0 {
|
||||
bb++
|
||||
}
|
||||
if bb & 255 {
|
||||
if bb & 255 !=0 {
|
||||
bb++
|
||||
}
|
||||
|
||||
|
@ -867,13 +395,13 @@ main {
|
|||
if ww < 0 {
|
||||
ww ++
|
||||
}
|
||||
if ww & 1 {
|
||||
if ww & 1 !=0 {
|
||||
ww++
|
||||
}
|
||||
if ww & 32768 {
|
||||
if ww & 32768 != 0 {
|
||||
ww++
|
||||
}
|
||||
if ww & 65535 {
|
||||
if ww & 65535 != 0 {
|
||||
ww++
|
||||
}
|
||||
}
|
||||
|
@ -888,20 +416,20 @@ main {
|
|||
val text = """
|
||||
main {
|
||||
sub start() {
|
||||
byte bb
|
||||
word ww
|
||||
byte @shared bb
|
||||
word @shared ww
|
||||
|
||||
ubyte iteration_in_progress
|
||||
uword num_bytes
|
||||
bool @shared iteration_in_progress
|
||||
uword @shared num_bytes
|
||||
|
||||
if not iteration_in_progress or not num_bytes {
|
||||
if not iteration_in_progress or num_bytes==0 {
|
||||
num_bytes++
|
||||
}
|
||||
|
||||
if bb as ubyte {
|
||||
if bb as ubyte !=0 {
|
||||
bb++
|
||||
}
|
||||
if ww as uword {
|
||||
if ww as uword !=0 {
|
||||
ww++
|
||||
}
|
||||
}
|
||||
|
@ -930,10 +458,10 @@ main {
|
|||
val text = """
|
||||
main {
|
||||
sub start() {
|
||||
byte ub1 = -50
|
||||
byte ub2 = -51
|
||||
byte ub3 = -52
|
||||
byte ub4 = 100
|
||||
byte @shared ub1 = -50
|
||||
byte @shared ub2 = -51
|
||||
byte @shared ub3 = -52
|
||||
byte @shared ub4 = 100
|
||||
word @shared ww = func(ub1, ub2, ub3, ub4)
|
||||
ww = func(ub4, ub2, ub3, ub1)
|
||||
ww=afunc(ub1, ub2, ub3, ub4)
|
||||
|
@ -1073,8 +601,8 @@ main {
|
|||
thing(large)
|
||||
thing(320*240/8/8)
|
||||
thing(320*HEIGHT/8/8)
|
||||
thing(320*HEIGHT) ; overflow
|
||||
large = 12345678 ; overflow
|
||||
thing(320*HEIGHT) ; overflow
|
||||
large = 12345678 ; overflow
|
||||
}
|
||||
|
||||
sub thing(uword value) {
|
||||
|
@ -1083,32 +611,170 @@ main {
|
|||
}"""
|
||||
val errors=ErrorReporterForTests()
|
||||
compileText(C64Target(), false, src, writeAssembly = false, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 4
|
||||
errors.errors.size shouldBe 3
|
||||
errors.errors[0] shouldContain ":9:"
|
||||
errors.errors[0] shouldContain "no cast"
|
||||
errors.errors[1] shouldContain "overflow"
|
||||
errors.errors[2] shouldContain "out of range"
|
||||
errors.errors[3] shouldContain "overflow"
|
||||
errors.errors[1] shouldContain ":10:"
|
||||
errors.errors[1] shouldContain "out of range"
|
||||
errors.errors[2] shouldContain ":10:"
|
||||
errors.errors[2] shouldContain "doesn't match"
|
||||
}
|
||||
|
||||
test("type fitting of const assignment values") {
|
||||
test("various bool typecasts and type mismatches") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
|
||||
float @shared fl
|
||||
ubyte @shared flags
|
||||
byte @shared flagss
|
||||
uword @shared flagsw
|
||||
word @shared flagssw
|
||||
bool @shared bflags = 123
|
||||
cx16.r0++
|
||||
bflags = 123
|
||||
cx16.r0++
|
||||
bflags = 123 as bool
|
||||
|
||||
flags = bflags
|
||||
flagss = bflags
|
||||
flagsw = bflags
|
||||
flagssw = bflags
|
||||
fl = bflags
|
||||
bflags = flags
|
||||
bflags = flagss
|
||||
bflags = flagsw
|
||||
bflags = flagssw
|
||||
bflags = fl
|
||||
|
||||
flags = bflags as ubyte
|
||||
flagss = bflags as byte
|
||||
flagsw = bflags as uword
|
||||
flagssw = bflags as word
|
||||
fl = bflags as float
|
||||
bflags = flags as bool
|
||||
bflags = flagss as bool
|
||||
bflags = flagsw as bool
|
||||
bflags = flagssw as bool
|
||||
bflags = fl as bool
|
||||
}
|
||||
}"""
|
||||
val errors=ErrorReporterForTests()
|
||||
compileText(VMTarget(), false, src, writeAssembly = false, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 12
|
||||
errors.errors.all { "type of value" in it } shouldBe true
|
||||
errors.errors.all { "doesn't match" in it } shouldBe true
|
||||
}
|
||||
|
||||
test("bool to byte cast in expression is not allowed") {
|
||||
val text="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte[3] values
|
||||
func1(22 in values)
|
||||
func2(22 in values)
|
||||
ubyte @shared qq = 22 in values
|
||||
byte @shared ww = 22 in values
|
||||
}
|
||||
sub func1(ubyte arg) {
|
||||
arg++
|
||||
}
|
||||
sub func2(byte arg) {
|
||||
arg++
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, text, writeAssembly = true, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 4
|
||||
errors.errors[0] shouldContain("argument 1 type mismatch")
|
||||
errors.errors[1] shouldContain("argument 1 type mismatch")
|
||||
errors.errors[2] shouldContain("type of value BOOL doesn't match target")
|
||||
errors.errors[3] shouldContain("type of value BOOL doesn't match target")
|
||||
}
|
||||
|
||||
test("bool function parameters correct typing") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
bool bb = func(true)
|
||||
void func(true)
|
||||
; all these should fail:
|
||||
void func(0)
|
||||
void func(1)
|
||||
void func(42)
|
||||
void func(65535)
|
||||
void func(655.444)
|
||||
cx16.r0L = func(true)
|
||||
}
|
||||
|
||||
sub func(bool draw) -> bool {
|
||||
cx16.r0++
|
||||
return true
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, src, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 6
|
||||
errors.errors[0] shouldContain("type mismatch")
|
||||
errors.errors[1] shouldContain("type mismatch")
|
||||
errors.errors[2] shouldContain("type mismatch")
|
||||
errors.errors[3] shouldContain("type mismatch")
|
||||
errors.errors[4] shouldContain("type mismatch")
|
||||
errors.errors[5] shouldContain("type of value BOOL doesn't match target")
|
||||
}
|
||||
|
||||
test("no implicit bool-to-int cast") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
&ubyte mapped = 8000
|
||||
mapped = 6144 >> 9
|
||||
ubyte @shared ubb = 6144 >> 9
|
||||
bool @shared bb = 6144
|
||||
func(true)
|
||||
func(true as ubyte)
|
||||
cx16.r0L = true
|
||||
cx16.r0L = true as ubyte
|
||||
}
|
||||
|
||||
sub func(bool b) {
|
||||
cx16.r0++
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), true, src, writeAssembly = false) shouldNotBe null
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, src, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain(":5:14: argument 1 type mismatch")
|
||||
errors.errors[1] shouldContain(":6:20: type of value BOOL doesn't match target")
|
||||
}
|
||||
|
||||
val src2="""
|
||||
test("no implicit int-to-bool cast") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte @shared ubb = 6144 >> 2 ; should still be type error
|
||||
func1(true)
|
||||
func2(true)
|
||||
func1(true as ubyte)
|
||||
func2(true as uword)
|
||||
bool @shared bb1 = 1
|
||||
bool @shared bb2 = 12345
|
||||
bool @shared bb3 = 1 as bool
|
||||
bool @shared bb4 = 12345 as bool
|
||||
}
|
||||
|
||||
sub func1(ubyte ub) {
|
||||
cx16.r0++
|
||||
}
|
||||
|
||||
sub func2(uword uw) {
|
||||
cx16.r0++
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), true, src2, writeAssembly = false) shouldBe null
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, src, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 4
|
||||
errors.errors[0] shouldContain(":4:15: no implicit cast")
|
||||
errors.errors[1] shouldContain(":5:15: no implicit cast")
|
||||
errors.errors[2] shouldContain(":8:28: type of value UBYTE doesn't match target")
|
||||
errors.errors[3] shouldContain(":9:28: type of value UWORD doesn't match target")
|
||||
}
|
||||
|
||||
test("str replaced with uword in subroutine params and return types") {
|
||||
|
|
|
@ -30,42 +30,42 @@ main {
|
|||
if length!=3 txt.print("error strlen2\n")
|
||||
|
||||
; ANY
|
||||
ub = any(ubarr)
|
||||
ub = any(ubarr) as ubyte
|
||||
if ub==0 txt.print("error any1\n")
|
||||
ub = any(barr)
|
||||
ub = any(barr) as ubyte
|
||||
if ub==0 txt.print("error any2\n")
|
||||
ub = any(uwarr)
|
||||
ub = any(uwarr) as ubyte
|
||||
if ub==0 txt.print("error any3\n")
|
||||
ub = any(warr)
|
||||
ub = any(warr) as ubyte
|
||||
if ub==0 txt.print("error any4\n")
|
||||
ub = any(farr)
|
||||
ub = any(farr) as ubyte
|
||||
if ub==0 txt.print("error any5\n")
|
||||
|
||||
; ALL
|
||||
ub = all(ubarr)
|
||||
ub = all(ubarr) as ubyte
|
||||
if ub==1 txt.print("error all1\n")
|
||||
ub = all(barr)
|
||||
ub = all(barr) as ubyte
|
||||
if ub==1 txt.print("error all2\n")
|
||||
ub = all(uwarr)
|
||||
ub = all(uwarr) as ubyte
|
||||
if ub==1 txt.print("error all3\n")
|
||||
ub = all(warr)
|
||||
ub = all(warr) as ubyte
|
||||
if ub==1 txt.print("error all4\n")
|
||||
ub = all(farr)
|
||||
ub = all(farr) as ubyte
|
||||
if ub==1 txt.print("error all5\n")
|
||||
ubarr[1]=$40
|
||||
barr[1]=$40
|
||||
uwarr[1]=$4000
|
||||
warr[1]=$4000
|
||||
farr[1]=1.1
|
||||
ub = all(ubarr)
|
||||
ub = all(ubarr) as ubyte
|
||||
if ub==0 txt.print("error all6\n")
|
||||
ub = all(barr)
|
||||
ub = all(barr) as ubyte
|
||||
if ub==0 txt.print("error all7\n")
|
||||
ub = all(uwarr)
|
||||
ub = all(uwarr) as ubyte
|
||||
if ub==0 txt.print("error all8\n")
|
||||
ub = all(warr)
|
||||
ub = all(warr) as ubyte
|
||||
if ub==0 txt.print("error all9\n")
|
||||
ub = all(farr)
|
||||
ub = all(farr) as ubyte
|
||||
if ub==0 txt.print("error all10\n")
|
||||
|
||||
txt.print("\nyou should see no errors printed above (only at first run).")
|
||||
|
|
|
@ -259,12 +259,6 @@ main {
|
|||
uw = shiftruw15()
|
||||
txt.print_uwbin(uw, true)
|
||||
txt.chrout('\n')
|
||||
uw = shiftruw16()
|
||||
txt.print_uwbin(uw, true)
|
||||
txt.chrout('\n')
|
||||
uw = shiftruw17()
|
||||
txt.print_uwbin(uw, true)
|
||||
txt.chrout('\n')
|
||||
txt.print("enter to continue:\n")
|
||||
void txt.input_chars(inputbuffer)
|
||||
|
||||
|
@ -459,16 +453,6 @@ main {
|
|||
return (q >> 15)
|
||||
}
|
||||
|
||||
sub shiftruw16() -> uword {
|
||||
uword @shared q = $a49f
|
||||
return (q >> 16)
|
||||
}
|
||||
|
||||
sub shiftruw17() -> uword {
|
||||
uword @shared q = $a49f
|
||||
return (q >> 17)
|
||||
}
|
||||
|
||||
sub shiftrsw0() -> word {
|
||||
word @shared q = -12345
|
||||
return q >> 0
|
||||
|
|
|
@ -227,15 +227,15 @@ main {
|
|||
txt.print("rol_ub error ")
|
||||
txt.print_ub(original)
|
||||
txt.spc()
|
||||
txt.print_ub(carry)
|
||||
txt.print_bool(carry)
|
||||
txt.spc()
|
||||
txt.print_ub(value)
|
||||
txt.spc()
|
||||
txt.print_ub(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_ub(test)
|
||||
txt.spc()
|
||||
txt.print_ub(newcarry)
|
||||
txt.print_bool(newcarry)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
|
@ -252,15 +252,15 @@ main {
|
|||
txt.print("rol_ub array error ")
|
||||
txt.print_ub(original)
|
||||
txt.spc()
|
||||
txt.print_ub(carry)
|
||||
txt.print_bool(carry)
|
||||
txt.spc()
|
||||
txt.print_ub(ubarray[1])
|
||||
txt.spc()
|
||||
txt.print_ub(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_ub(test)
|
||||
txt.spc()
|
||||
txt.print_ub(newcarry)
|
||||
txt.print_bool(newcarry)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
|
@ -277,15 +277,15 @@ main {
|
|||
txt.print("rol_ub mem error ")
|
||||
txt.print_ub(original)
|
||||
txt.spc()
|
||||
txt.print_ub(carry)
|
||||
txt.print_bool(carry)
|
||||
txt.spc()
|
||||
txt.print_ub(@($8001))
|
||||
txt.spc()
|
||||
txt.print_ub(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_ub(test)
|
||||
txt.spc()
|
||||
txt.print_ub(newcarry)
|
||||
txt.print_bool(newcarry)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
@ -304,15 +304,15 @@ main {
|
|||
txt.print("ror_ub error ")
|
||||
txt.print_ub(original)
|
||||
txt.spc()
|
||||
txt.print_ub(carry)
|
||||
txt.print_bool(carry)
|
||||
txt.spc()
|
||||
txt.print_ub(value)
|
||||
txt.spc()
|
||||
txt.print_ub(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_ub(test)
|
||||
txt.spc()
|
||||
txt.print_ub(newcarry)
|
||||
txt.print_bool(newcarry)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
|
@ -329,15 +329,15 @@ main {
|
|||
txt.print("ror_ub array error ")
|
||||
txt.print_ub(original)
|
||||
txt.spc()
|
||||
txt.print_ub(carry)
|
||||
txt.print_bool(carry)
|
||||
txt.spc()
|
||||
txt.print_ub(ubarray[1])
|
||||
txt.spc()
|
||||
txt.print_ub(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_ub(test)
|
||||
txt.spc()
|
||||
txt.print_ub(newcarry)
|
||||
txt.print_bool(newcarry)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
|
@ -354,15 +354,15 @@ main {
|
|||
txt.print("ror_ub mem error ")
|
||||
txt.print_ub(original)
|
||||
txt.spc()
|
||||
txt.print_ub(carry)
|
||||
txt.print_bool(carry)
|
||||
txt.spc()
|
||||
txt.print_ub(@($8001))
|
||||
txt.spc()
|
||||
txt.print_ub(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_ub(test)
|
||||
txt.spc()
|
||||
txt.print_ub(newcarry)
|
||||
txt.print_bool(newcarry)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
@ -437,15 +437,15 @@ main {
|
|||
txt.print("rol_uw error ")
|
||||
txt.print_uw(original)
|
||||
txt.spc()
|
||||
txt.print_uw(carry)
|
||||
txt.print_bool(carry)
|
||||
txt.spc()
|
||||
txt.print_uw(value)
|
||||
txt.spc()
|
||||
txt.print_uw(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_uw(test)
|
||||
txt.spc()
|
||||
txt.print_uw(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
|
@ -462,15 +462,15 @@ main {
|
|||
txt.print("rol_uw array error ")
|
||||
txt.print_uw(original)
|
||||
txt.spc()
|
||||
txt.print_uw(carry)
|
||||
txt.print_bool(carry)
|
||||
txt.spc()
|
||||
txt.print_uw(uwarray[1])
|
||||
txt.spc()
|
||||
txt.print_uw(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_uw(test)
|
||||
txt.spc()
|
||||
txt.print_uw(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
@ -489,15 +489,15 @@ main {
|
|||
txt.print("ror_uw error ")
|
||||
txt.print_uw(original)
|
||||
txt.spc()
|
||||
txt.print_uw(carry)
|
||||
txt.print_bool(carry)
|
||||
txt.spc()
|
||||
txt.print_uw(value)
|
||||
txt.spc()
|
||||
txt.print_uw(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_uw(test)
|
||||
txt.spc()
|
||||
txt.print_uw(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
|
@ -514,15 +514,15 @@ main {
|
|||
txt.print("ror_uw array error ")
|
||||
txt.print_uw(original)
|
||||
txt.spc()
|
||||
txt.print_uw(carry)
|
||||
txt.print_bool(carry)
|
||||
txt.spc()
|
||||
txt.print_uw(uwarray[1])
|
||||
txt.spc()
|
||||
txt.print_uw(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_uw(test)
|
||||
txt.spc()
|
||||
txt.print_uw(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
@ -540,11 +540,11 @@ main {
|
|||
txt.spc()
|
||||
txt.print_ub(value)
|
||||
txt.spc()
|
||||
txt.print_ub(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_ub(test)
|
||||
txt.spc()
|
||||
txt.print_ub(newcarry)
|
||||
txt.print_bool(newcarry)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
|
@ -560,11 +560,11 @@ main {
|
|||
txt.spc()
|
||||
txt.print_ub(ubarray[1])
|
||||
txt.spc()
|
||||
txt.print_ub(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_ub(test)
|
||||
txt.spc()
|
||||
txt.print_ub(newcarry)
|
||||
txt.print_bool(newcarry)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
@ -582,11 +582,11 @@ main {
|
|||
txt.spc()
|
||||
txt.print_ub(value)
|
||||
txt.spc()
|
||||
txt.print_ub(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_ub(test)
|
||||
txt.spc()
|
||||
txt.print_ub(newcarry)
|
||||
txt.print_bool(newcarry)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
|
@ -602,11 +602,11 @@ main {
|
|||
txt.spc()
|
||||
txt.print_ub(ubarray[1])
|
||||
txt.spc()
|
||||
txt.print_ub(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_ub(test)
|
||||
txt.spc()
|
||||
txt.print_ub(newcarry)
|
||||
txt.print_bool(newcarry)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
@ -624,11 +624,11 @@ main {
|
|||
txt.spc()
|
||||
txt.print_uw(value)
|
||||
txt.spc()
|
||||
txt.print_uw(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_uw(test)
|
||||
txt.spc()
|
||||
txt.print_uw(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
|
@ -644,11 +644,11 @@ main {
|
|||
txt.spc()
|
||||
txt.print_uw(uwarray[1])
|
||||
txt.spc()
|
||||
txt.print_uw(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_uw(test)
|
||||
txt.spc()
|
||||
txt.print_uw(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
@ -666,11 +666,11 @@ main {
|
|||
txt.spc()
|
||||
txt.print_uw(value)
|
||||
txt.spc()
|
||||
txt.print_uw(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_uw(test)
|
||||
txt.spc()
|
||||
txt.print_uw(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
|
@ -686,11 +686,11 @@ main {
|
|||
txt.spc()
|
||||
txt.print_uw(uwarray[1])
|
||||
txt.spc()
|
||||
txt.print_uw(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_uw(test)
|
||||
txt.spc()
|
||||
txt.print_uw(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
@ -708,11 +708,11 @@ main {
|
|||
txt.spc()
|
||||
txt.print_b(value)
|
||||
txt.spc()
|
||||
txt.print_b(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_b(test)
|
||||
txt.spc()
|
||||
txt.print_b(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
|
@ -728,11 +728,11 @@ main {
|
|||
txt.spc()
|
||||
txt.print_b(barray[1])
|
||||
txt.spc()
|
||||
txt.print_b(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_b(test)
|
||||
txt.spc()
|
||||
txt.print_b(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
@ -750,11 +750,11 @@ main {
|
|||
txt.spc()
|
||||
txt.print_b(value)
|
||||
txt.spc()
|
||||
txt.print_b(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_b(test)
|
||||
txt.spc()
|
||||
txt.print_b(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
|
@ -770,11 +770,11 @@ main {
|
|||
txt.spc()
|
||||
txt.print_b(barray[1])
|
||||
txt.spc()
|
||||
txt.print_b(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_b(test)
|
||||
txt.spc()
|
||||
txt.print_b(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
@ -792,11 +792,11 @@ main {
|
|||
txt.spc()
|
||||
txt.print_w(value)
|
||||
txt.spc()
|
||||
txt.print_w(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_w(test)
|
||||
txt.spc()
|
||||
txt.print_w(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
|
@ -812,11 +812,11 @@ main {
|
|||
txt.spc()
|
||||
txt.print_w(warray[1])
|
||||
txt.spc()
|
||||
txt.print_w(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_w(test)
|
||||
txt.spc()
|
||||
txt.print_w(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
@ -834,11 +834,11 @@ main {
|
|||
txt.spc()
|
||||
txt.print_w(value)
|
||||
txt.spc()
|
||||
txt.print_w(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_w(test)
|
||||
txt.spc()
|
||||
txt.print_w(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
|
@ -854,11 +854,11 @@ main {
|
|||
txt.spc()
|
||||
txt.print_w(warray[1])
|
||||
txt.spc()
|
||||
txt.print_w(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.print(" exp: ")
|
||||
txt.print_w(test)
|
||||
txt.spc()
|
||||
txt.print_w(carrycheck)
|
||||
txt.print_bool(carrycheck)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -351,59 +351,59 @@ main {
|
|||
txt.print_w(ww)
|
||||
txt.nl()
|
||||
|
||||
ub = any(ubarr)
|
||||
ub = any(ubarr) as ubyte
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
ub = zero+any(ubarr)*1+zero
|
||||
ub = zero+(any(ubarr) as ubyte)*1+zero
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
|
||||
ub = any(barr)
|
||||
ub = any(barr) as ubyte
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
ub = zero+any(barr)*1+zero
|
||||
ub = zero+(any(barr) as ubyte)*1+zero
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
|
||||
ub = any(uwarr)
|
||||
ub = any(uwarr) as ubyte
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
ub = zero+any(uwarr)*1+zero
|
||||
ub = zero+(any(uwarr) as ubyte)*1+zero
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
|
||||
ub = any(warr)
|
||||
ub = any(warr) as ubyte
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
ub = zero+any(warr)*1+zero
|
||||
ub = zero+(any(warr) as ubyte)*1+zero
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
|
||||
ub = all(ubarr)
|
||||
ub = all(ubarr) as ubyte
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
ub = zero+all(ubarr)*1+zero
|
||||
ub = zero+(all(ubarr) as ubyte)*1+zero
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
|
||||
ub = all(barr)
|
||||
ub = all(barr) as ubyte
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
ub = zero+all(barr)*1+zero
|
||||
ub = zero+(all(barr) as ubyte)*1+zero
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
|
||||
ub = all(uwarr)
|
||||
ub = all(uwarr) as ubyte
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
ub = zero+all(uwarr)*1+zero
|
||||
ub = zero+(all(uwarr) as ubyte)*1+zero
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
|
||||
ub = all(warr)
|
||||
ub = all(warr) as ubyte
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
ub = zero+all(warr)*1+zero
|
||||
ub = zero+(all(warr) as ubyte)*1+zero
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
|
||||
|
@ -436,16 +436,16 @@ main {
|
|||
txt.print_b(bb)
|
||||
txt.nl()
|
||||
|
||||
ub = any(flarr)
|
||||
ub = any(flarr) as ubyte
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
ub = zero+any(flarr)*1+zero
|
||||
ub = zero+(any(flarr) as ubyte)*1+zero
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
ub = all(flarr)
|
||||
ub = all(flarr) as ubyte
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
ub = zero+all(flarr)*1+zero
|
||||
ub = zero+(all(flarr) as ubyte)*1+zero
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
|
||||
|
|
|
@ -1,206 +0,0 @@
|
|||
%import textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
|
||||
sub ftrue(ubyte arg) -> ubyte {
|
||||
arg++
|
||||
return 128
|
||||
}
|
||||
|
||||
sub ffalse(ubyte arg) -> ubyte {
|
||||
arg++
|
||||
return 0
|
||||
}
|
||||
|
||||
sub start() {
|
||||
ubyte ub1 = 2
|
||||
ubyte ub2 = 4
|
||||
ubyte ub3 = 8
|
||||
ubyte ub4 = 0
|
||||
ubyte bvalue
|
||||
|
||||
txt.print("const not 0: ")
|
||||
txt.print_ub(not 129)
|
||||
txt.nl()
|
||||
txt.print("const not 1: ")
|
||||
txt.print_ub(not 0)
|
||||
txt.nl()
|
||||
txt.print("const inv 126: ")
|
||||
txt.print_ub(~ 129)
|
||||
txt.nl()
|
||||
txt.print("const inv 255: ")
|
||||
txt.print_ub(~ 0)
|
||||
txt.nl()
|
||||
bvalue = 129
|
||||
txt.print("bitwise inv 126: ")
|
||||
bvalue = ~ bvalue
|
||||
txt.print_ub(bvalue)
|
||||
txt.nl()
|
||||
bvalue = 0
|
||||
txt.print("bitwise inv 255: ")
|
||||
bvalue = ~ bvalue
|
||||
txt.print_ub(bvalue)
|
||||
txt.nl()
|
||||
|
||||
txt.print("bitwise or 14: ")
|
||||
txt.print_ub(ub1 | ub2 | ub3 | ub4)
|
||||
txt.nl()
|
||||
txt.print("bitwise or 142: ")
|
||||
txt.print_ub(ub1 | ub2 | ub3 | ub4 | 128)
|
||||
txt.nl()
|
||||
txt.print("bitwise and 0: ")
|
||||
txt.print_ub(ub1 & ub2 & ub3 & ub4)
|
||||
txt.nl()
|
||||
txt.print("bitwise and 8: ")
|
||||
txt.print_ub(ub3 & ub3 & 127)
|
||||
txt.nl()
|
||||
txt.print("bitwise xor 14: ")
|
||||
txt.print_ub(ub1 ^ ub2 ^ ub3 ^ ub4)
|
||||
txt.nl()
|
||||
txt.print("bitwise xor 6: ")
|
||||
txt.print_ub(ub1 ^ ub2 ^ ub3 ^ 8)
|
||||
txt.nl()
|
||||
txt.print("bitwise not 247: ")
|
||||
txt.print_ub(~ub3)
|
||||
txt.nl()
|
||||
txt.print("bitwise not 255: ")
|
||||
txt.print_ub(~ub4)
|
||||
txt.nl()
|
||||
|
||||
txt.print("not 0: ")
|
||||
bvalue = 3 * (ub4 | not (ub3 | ub3 | ub3))
|
||||
txt.print_ub(bvalue)
|
||||
if 3*(ub4 | not (ub1 | ub1 | ub1))
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
txt.nl()
|
||||
|
||||
txt.print("not 0: ")
|
||||
bvalue = not ub3
|
||||
txt.print_ub(bvalue)
|
||||
if not ub1
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
txt.nl()
|
||||
|
||||
txt.print("not 1: ")
|
||||
bvalue = not ub4
|
||||
txt.print_ub(bvalue)
|
||||
if not ub4
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
|
||||
bvalue = bvalue and 128
|
||||
txt.print("bvl 1: ")
|
||||
txt.print_ub(bvalue)
|
||||
if bvalue and 128
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
|
||||
txt.print("and 1: ")
|
||||
bvalue = ub1 and ub2 and ub3
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 and ub2 and ub3
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print("and 1: ")
|
||||
bvalue = ub1 and ub2 and ub3 and 64
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 and ub2 and ub3 and 64
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print("and 1: ")
|
||||
bvalue = ub1 and ub2 and ub3 and ftrue(99)
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 and ub2 and ub3 and ftrue(99)
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print("and 0: ")
|
||||
bvalue = ub1 and ub2 and ub3 and ub4
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 and ub2 and ub3 and ub4
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
txt.nl()
|
||||
txt.print("and 0: ")
|
||||
bvalue = ub1 and ub2 and ub3 and ffalse(99)
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 and ub2 and ub3 and ffalse(99)
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
txt.nl()
|
||||
|
||||
txt.print(" or 1: ")
|
||||
bvalue = ub1 or ub2 or ub3 or ub4
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 or ub2 or ub3 or ub4
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print(" or 1: ")
|
||||
bvalue = ub4 or ub4 or ub1
|
||||
txt.print_ub(bvalue)
|
||||
if ub4 or ub4 or ub1
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print(" or 1: ")
|
||||
bvalue = ub1 or ub2 or ub3 or ftrue(99)
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 or ub2 or ub3 or ftrue(99)
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
|
||||
txt.print("xor 1: ")
|
||||
bvalue = ub1 xor ub2 xor ub3 xor ub4
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 xor ub2 xor ub3 xor ub4
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
txt.print("xor 1: ")
|
||||
bvalue = ub1 xor ub2 xor ub3 xor ffalse(99)
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 xor ub2 xor ub3 xor ffalse(99)
|
||||
txt.print(" / ok")
|
||||
else
|
||||
txt.print(" / fail")
|
||||
txt.nl()
|
||||
|
||||
txt.print("xor 0: ")
|
||||
bvalue = ub1 xor ub2 xor ub3 xor ub4 xor true
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 xor ub2 xor ub3 xor ub4 xor true
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
txt.nl()
|
||||
txt.print("xor 0: ")
|
||||
bvalue = ub1 xor ub2 xor ub3 xor ftrue(99)
|
||||
txt.print_ub(bvalue)
|
||||
if ub1 xor ub2 xor ub3 xor ftrue(99)
|
||||
txt.print(" / fail")
|
||||
else
|
||||
txt.print(" / ok")
|
||||
}
|
||||
}
|
|
@ -85,14 +85,15 @@ class TestAstChecks: FunSpec({
|
|||
sub start() {
|
||||
ubyte[] array = [1,2,3,4]
|
||||
str s1 = "test"
|
||||
bool bb1, bb2
|
||||
ubyte ff = 1
|
||||
txt.print(&s1+ff)
|
||||
txt.print(&array+ff)
|
||||
txt.print_uwhex(&s1+ff, true)
|
||||
txt.print_uwhex(&array+ff, true)
|
||||
; also good:
|
||||
ff = (s1 == "derp")
|
||||
ff = (s1 != "derp")
|
||||
bb1 = (s1 == "derp")
|
||||
bb2 = (s1 != "derp")
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
|
|
@ -65,11 +65,11 @@ class TestConst: FunSpec({
|
|||
val subR5value = (stmts[7] as Assignment).value
|
||||
val binexpr5 = subR5value as BinaryExpression
|
||||
binexpr5.operator shouldBe "-"
|
||||
binexpr5.right shouldBe NumericLiteral(DataType.UWORD, 1899.0, Position.DUMMY)
|
||||
binexpr5.right shouldBe NumericLiteral(DataType.WORD, 1899.0, Position.DUMMY)
|
||||
val subR7value = (stmts[8] as Assignment).value
|
||||
val binexpr7 = subR7value as BinaryExpression
|
||||
binexpr7.operator shouldBe "+"
|
||||
binexpr7.right shouldBe NumericLiteral(DataType.UWORD, 99.0, Position.DUMMY)
|
||||
binexpr7.right shouldBe NumericLiteral(DataType.WORD, 99.0, Position.DUMMY)
|
||||
}
|
||||
|
||||
test("const folding multiple scenarios * and / (floats)") {
|
||||
|
@ -158,24 +158,24 @@ class TestConst: FunSpec({
|
|||
val mulR0Value = (stmts[2] as Assignment).value
|
||||
val binexpr0 = mulR0Value as BinaryExpression
|
||||
binexpr0.operator shouldBe "*"
|
||||
binexpr0.right shouldBe NumericLiteral(DataType.UWORD, 180.0, Position.DUMMY)
|
||||
binexpr0.right shouldBe NumericLiteral(DataType.WORD, 180.0, Position.DUMMY)
|
||||
val mulR1Value = (stmts[3] as Assignment).value
|
||||
val binexpr1 = mulR1Value as BinaryExpression
|
||||
binexpr1.operator shouldBe "*"
|
||||
binexpr1.right shouldBe NumericLiteral(DataType.UWORD, 180.0, Position.DUMMY)
|
||||
binexpr1.right shouldBe NumericLiteral(DataType.WORD, 180.0, Position.DUMMY)
|
||||
val divR2Value = (stmts[4] as Assignment).value
|
||||
val binexpr2 = divR2Value as BinaryExpression
|
||||
binexpr2.operator shouldBe "/"
|
||||
binexpr2.right shouldBe NumericLiteral(DataType.UWORD, 90.0, Position.DUMMY)
|
||||
binexpr2.right shouldBe NumericLiteral(DataType.WORD, 90.0, Position.DUMMY)
|
||||
val mulR3Value = (stmts[5] as Assignment).value
|
||||
val binexpr3 = mulR3Value as BinaryExpression
|
||||
binexpr3.operator shouldBe "*"
|
||||
binexpr3.right shouldBe NumericLiteral(DataType.UWORD, 10.0, Position.DUMMY)
|
||||
binexpr3.right shouldBe NumericLiteral(DataType.WORD, 10.0, Position.DUMMY)
|
||||
binexpr3.left shouldBe instanceOf<BinaryExpression>()
|
||||
val mulR4Value = (stmts[6] as Assignment).value
|
||||
val binexpr4 = mulR4Value as BinaryExpression
|
||||
binexpr4.operator shouldBe "/"
|
||||
binexpr4.right shouldBe NumericLiteral(DataType.UWORD, 5.0, Position.DUMMY)
|
||||
binexpr4.right shouldBe NumericLiteral(DataType.WORD, 5.0, Position.DUMMY)
|
||||
binexpr4.left shouldBe instanceOf<BinaryExpression>()
|
||||
}
|
||||
|
||||
|
@ -241,16 +241,16 @@ main {
|
|||
main {
|
||||
sub start() {
|
||||
float fl = 1.2 ; no other assignments
|
||||
cx16.r0L = string.isdigit(math.diff(119, floats.floor(floats.deg(fl)) as ubyte))
|
||||
cx16.r1L = string.isletter(math.diff(119, floats.floor(floats.deg(1.2)) as ubyte))
|
||||
bool @shared result1 = string.isdigit(math.diff(119, floats.floor(floats.deg(fl)) as ubyte))
|
||||
bool @shared result2 = string.isletter(math.diff(119, floats.floor(floats.deg(1.2)) as ubyte))
|
||||
}
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), true, src, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 3
|
||||
st.size shouldBe 5
|
||||
(st[0] as VarDecl).type shouldBe VarDeclType.CONST
|
||||
val assignv1 = (st[1] as Assignment).value
|
||||
val assignv2 = (st[2] as Assignment).value
|
||||
val assignv1 = (st[2] as Assignment).value
|
||||
val assignv2 = (st[4] as Assignment).value
|
||||
(assignv1 as NumericLiteral).number shouldBe 1.0
|
||||
(assignv2 as NumericLiteral).number shouldBe 0.0
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.kotest.assertions.fail
|
|||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.ints.shouldBeGreaterThan
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.types.instanceOf
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Position
|
||||
|
@ -20,10 +21,11 @@ class TestIntermediateAst: FunSpec({
|
|||
%import graphics
|
||||
main {
|
||||
sub start() {
|
||||
ubyte cc
|
||||
bool cc
|
||||
ubyte dd
|
||||
ubyte[] array = [1,2,3]
|
||||
cc = 11 in array
|
||||
cc = sqrt(lsb(cc))
|
||||
dd = sqrt(lsb(dd))
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
@ -34,7 +36,7 @@ class TestIntermediateAst: FunSpec({
|
|||
ast.name shouldBe result.compilerAst.name
|
||||
ast.allBlocks().any() shouldBe true
|
||||
val entry = ast.entrypoint() ?: fail("no main.start() found")
|
||||
entry.children.size shouldBe 5
|
||||
entry.children.size shouldBe 7
|
||||
entry.name shouldBe "start"
|
||||
entry.scopedName shouldBe "main.start"
|
||||
val blocks = ast.allBlocks().toList()
|
||||
|
@ -42,23 +44,30 @@ class TestIntermediateAst: FunSpec({
|
|||
blocks[0].name shouldBe "main"
|
||||
blocks[0].scopedName shouldBe "main"
|
||||
|
||||
val ccInit = entry.children[2] as PtAssignment
|
||||
val ccInit = entry.children[3] as PtAssignment
|
||||
ccInit.target.identifier?.name shouldBe "main.start.cc"
|
||||
(ccInit.value as PtNumber).number shouldBe 0.0
|
||||
(ccInit.value as PtBool).value shouldBe false
|
||||
val ddInit = entry.children[4] as PtAssignment
|
||||
ddInit.target.identifier?.name shouldBe "main.start.dd"
|
||||
(ddInit.value as PtNumber).number shouldBe 0.0
|
||||
|
||||
val ccdecl = entry.children[0] as PtVariable
|
||||
ccdecl.name shouldBe "cc"
|
||||
ccdecl.scopedName shouldBe "main.start.cc"
|
||||
ccdecl.type shouldBe DataType.UBYTE
|
||||
val arraydecl = entry.children[1] as IPtVariable
|
||||
ccdecl.type shouldBe DataType.BOOL
|
||||
val dddecl = entry.children[1] as PtVariable
|
||||
dddecl.name shouldBe "dd"
|
||||
dddecl.scopedName shouldBe "main.start.dd"
|
||||
dddecl.type shouldBe DataType.UBYTE
|
||||
|
||||
val arraydecl = entry.children[2] as IPtVariable
|
||||
arraydecl.name shouldBe "array"
|
||||
arraydecl.type shouldBe DataType.ARRAY_UB
|
||||
|
||||
val containment = (entry.children[3] as PtAssignment).value as PtContainmentCheck
|
||||
(containment.element as PtNumber).number shouldBe 11.0
|
||||
val fcall = (entry.children[4] as PtAssignment).value as PtFunctionCall
|
||||
fcall.void shouldBe false
|
||||
fcall.type shouldBe DataType.UBYTE
|
||||
val ccAssignV = (entry.children[5] as PtAssignment).value
|
||||
ccAssignV shouldBe instanceOf<PtContainmentCheck>()
|
||||
val ddAssignV = (entry.children[6] as PtAssignment).value
|
||||
ddAssignV shouldBe instanceOf<PtFunctionCall>()
|
||||
}
|
||||
|
||||
test("isSame on binaryExpressions") {
|
||||
|
|
|
@ -914,7 +914,7 @@ class TestProg8Parser: FunSpec( {
|
|||
val text="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte @shared cc
|
||||
bool @shared cc
|
||||
ubyte[] array = [1,2,3]
|
||||
cc = 99 in 12345
|
||||
cc = 9999 in array
|
||||
|
|
|
@ -92,16 +92,17 @@ main {
|
|||
sub start() {
|
||||
str name = "name"
|
||||
uword nameptr = &name
|
||||
bool result
|
||||
|
||||
cx16.r0L= name=="foo"
|
||||
cx16.r1L= name!="foo"
|
||||
cx16.r2L= name<"foo"
|
||||
cx16.r3L= name>"foo"
|
||||
result = name=="foo"
|
||||
result = name!="foo"
|
||||
result = name<"foo"
|
||||
result = name>"foo"
|
||||
|
||||
cx16.r0L= nameptr=="foo"
|
||||
cx16.r1L= nameptr!="foo"
|
||||
cx16.r2L= nameptr<"foo"
|
||||
cx16.r3L= nameptr>"foo"
|
||||
result = nameptr=="foo"
|
||||
result = nameptr!="foo"
|
||||
result = nameptr<"foo"
|
||||
result = nameptr>"foo"
|
||||
|
||||
void compare(name, "foo")
|
||||
void compare(name, "name")
|
||||
|
@ -117,10 +118,10 @@ main {
|
|||
}"""
|
||||
val result = compileText(C64Target(), optimize=false, src, writeAssembly=true)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 16
|
||||
stmts.size shouldBe 17
|
||||
val result2 = compileText(VMTarget(), optimize=false, src, writeAssembly=true)!!
|
||||
val stmts2 = result2.compilerAst.entrypoint.statements
|
||||
stmts2.size shouldBe 16
|
||||
stmts2.size shouldBe 17
|
||||
}
|
||||
|
||||
test("string concatenation and repeats") {
|
||||
|
@ -202,8 +203,8 @@ main {
|
|||
uword[128] YY
|
||||
ubyte[] ARRAY = [1, 5, 2]
|
||||
repeat {
|
||||
ubyte pixel_side1 = pget(2, YY[2]+1) in ARRAY
|
||||
ubyte pixel_side2 = pget(2, 2) in ARRAY
|
||||
bool pixel_side1 = pget(2, YY[2]+1) in ARRAY
|
||||
bool pixel_side2 = pget(2, 2) in ARRAY
|
||||
ubyte[] array2 = [1,2,3]
|
||||
}
|
||||
}
|
||||
|
@ -218,16 +219,17 @@ main {
|
|||
main {
|
||||
sub start() {
|
||||
ubyte[] array=[1,2,3]
|
||||
cx16.r0L = not (3 in array)
|
||||
cx16.r1L = 3 not in array
|
||||
bool result
|
||||
result = not (3 in array)
|
||||
result = 3 not in array
|
||||
}
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target(), optimize=false, src, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 3
|
||||
val value1 = (stmts[1] as Assignment).value as PrefixExpression
|
||||
val value2 = (stmts[2] as Assignment).value as PrefixExpression
|
||||
stmts.size shouldBe 4
|
||||
val value1 = (stmts[2] as Assignment).value as PrefixExpression
|
||||
val value2 = (stmts[3] as Assignment).value as PrefixExpression
|
||||
value1.operator shouldBe "not"
|
||||
value2.operator shouldBe "not"
|
||||
value1.expression shouldBe instanceOf<ContainmentCheck>()
|
||||
|
@ -281,23 +283,23 @@ main
|
|||
{
|
||||
sub start()
|
||||
{
|
||||
ubyte variable=55
|
||||
ubyte @shared variable=55
|
||||
when variable
|
||||
{
|
||||
33 -> cx16.r0++
|
||||
else -> cx16.r1++
|
||||
}
|
||||
|
||||
if variable {
|
||||
if variable!=0 {
|
||||
cx16.r0++
|
||||
} else {
|
||||
cx16.r1++
|
||||
}
|
||||
|
||||
if variable { cx16.r0++ }
|
||||
if variable!=0 { cx16.r0++ }
|
||||
else { cx16.r1++ }
|
||||
|
||||
if variable
|
||||
if variable!=0
|
||||
{
|
||||
cx16.r0++
|
||||
}
|
||||
|
@ -348,7 +350,10 @@ main
|
|||
}
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), optimize=false, src, writeAssembly=false) shouldNotBe null
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(VMTarget(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "use if"
|
||||
}
|
||||
|
||||
test("char as str param is error") {
|
||||
|
@ -447,26 +452,27 @@ main {
|
|||
sub start() {
|
||||
ubyte @shared n=20
|
||||
ubyte @shared x=10
|
||||
bool @shared result1, result2
|
||||
|
||||
if n < x {
|
||||
; nothing here, conditional gets inverted
|
||||
} else {
|
||||
cx16.r0++
|
||||
}
|
||||
cx16.r0L = n<x == 0
|
||||
cx16.r1L = not n<x
|
||||
result1 = n<x == false
|
||||
result2 = not n<x
|
||||
}
|
||||
}"""
|
||||
val result=compileText(VMTarget(), optimize=true, src, writeAssembly=false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 7
|
||||
st.size shouldBe 11
|
||||
|
||||
val ifCond = (st[4] as IfElse).condition as BinaryExpression
|
||||
val ifCond = (st[8] as IfElse).condition as BinaryExpression
|
||||
ifCond.operator shouldBe ">="
|
||||
(ifCond.left as IdentifierReference).nameInSource shouldBe listOf("n")
|
||||
(ifCond.right as IdentifierReference).nameInSource shouldBe listOf("x")
|
||||
val assign1 = (st[5] as Assignment).value as BinaryExpression
|
||||
val assign2 = (st[6] as Assignment).value as BinaryExpression
|
||||
val assign1 = (st[9] as Assignment).value as BinaryExpression
|
||||
val assign2 = (st[10] as Assignment).value as BinaryExpression
|
||||
assign1.operator shouldBe ">="
|
||||
(assign1.left as IdentifierReference).nameInSource shouldBe listOf("n")
|
||||
(assign1.right as IdentifierReference).nameInSource shouldBe listOf("x")
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package prog8tests.codegeneration
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.compileText
|
||||
|
||||
|
||||
|
@ -84,19 +87,58 @@ class TestVariables: FunSpec({
|
|||
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("initialization of boolean array with array") {
|
||||
val text = """
|
||||
main {
|
||||
sub start() {
|
||||
bool[3] sieve0 = [true, false, true]
|
||||
}
|
||||
}
|
||||
"""
|
||||
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("initialization of boolean array with wrong array type should fail") {
|
||||
val text = """
|
||||
main {
|
||||
sub start() {
|
||||
bool[] sieve0 = [true, false, 1]
|
||||
bool[] sieve1 = [true, false, 42]
|
||||
}
|
||||
}
|
||||
"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, text, writeAssembly = true, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "initialisation value has incompatible type"
|
||||
errors.errors[1] shouldContain "initialisation value has incompatible type"
|
||||
}
|
||||
|
||||
test("initialization of boolean array with single value") {
|
||||
val text = """
|
||||
main {
|
||||
sub start() {
|
||||
bool[10] sieve0 = false
|
||||
bool[10] sieve1 = true
|
||||
bool[10] sieve2 = 42
|
||||
sieve0[0] = true
|
||||
sieve1[0] = true
|
||||
sieve2[0] = true
|
||||
}
|
||||
}
|
||||
"""
|
||||
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("initialization of boolean array with single value of wrong type fails") {
|
||||
val text = """
|
||||
main {
|
||||
sub start() {
|
||||
bool[10] sieve2 = 42
|
||||
}
|
||||
}
|
||||
"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, text, writeAssembly = true, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "initializer value is not a boolean"
|
||||
}
|
||||
})
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user