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:
Irmen de Jong 2024-03-10 23:55:08 +01:00
commit 80f39e8097
163 changed files with 9143 additions and 5999 deletions

View File

@ -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>

View File

@ -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")
}
}

View File

@ -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

View File

@ -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}:"

View File

@ -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) {

View File

@ -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),

View File

@ -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()

View File

@ -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,

View File

@ -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
}

View File

@ -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!!)
}

View File

@ -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")

View File

@ -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")

View File

@ -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")

View File

@ -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

View File

@ -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>()

View File

@ -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("""

View File

@ -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

View File

@ -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

View File

@ -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)

File diff suppressed because it is too large Load Diff

View File

@ -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 {

View File

@ -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 {

View File

@ -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)

View File

@ -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) {

View File

@ -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 -> {

View File

@ -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

View File

@ -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") }

View File

@ -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"))
}
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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") }

View File

@ -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)

View File

@ -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)
}

View File

@ -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))
}
}
}

View File

@ -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)
}
}

View File

@ -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) {

View File

@ -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()
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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--

View File

@ -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 {

View File

@ -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)
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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) {

View File

@ -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

View File

@ -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) {

View File

@ -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 {{

View File

@ -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

View File

@ -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

View File

@ -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")
}
}

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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++
}

View File

@ -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. Dont 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++

View File

@ -0,0 +1,10 @@
; dummy function on the VM
%import textio
test_stack {
%option no_symbol_prefixing, ignore_unused
sub test() {
}
}

View File

@ -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]
}

View File

@ -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

View File

@ -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")
}
}

View File

@ -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

View File

@ -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 }
}

View File

@ -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

View File

@ -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()

View File

@ -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))

View File

@ -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
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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

View File

@ -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") }

View File

@ -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 {

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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

View File

@ -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
)

View File

@ -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]

View File

@ -40,6 +40,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
breakpointCpuInstruction = null,
printAst1 = false,
printAst2 = false,
strictBool = true,
symbolDefs = emptyMap(),
sourceDirs,
outputDir

View File

@ -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") {

View File

@ -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)

View File

@ -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") {

View File

@ -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).")

View File

@ -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

View File

@ -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()
}
}

View File

@ -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()

View File

@ -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")
}
}

View File

@ -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")
}
}
"""

View File

@ -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
}

View File

@ -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") {

View File

@ -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

View File

@ -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")

View File

@ -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