mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f874942075 | ||
|
|
2cadb546d5 | ||
|
|
344a1b9eb8 | ||
|
|
3c77f8a020 | ||
|
|
8e00408e3e | ||
|
|
abcdfd8e28 | ||
|
|
b0f5b6925d | ||
|
|
ef79d0c43e | ||
|
|
78b4288005 | ||
|
|
680f5d21ee | ||
|
|
c71aa0895f | ||
|
|
9f8e61789a | ||
|
|
932035cdc5 | ||
|
|
ef198f1493 | ||
|
|
48ef856c0b | ||
|
|
9aea2b22c4 | ||
|
|
e0055bc431 | ||
|
|
9553248ed6 | ||
|
|
39d2194d8f | ||
|
|
0800033b47 | ||
|
|
64d8943b7d | ||
|
|
444e97b00b | ||
|
|
1816bda7ea | ||
|
|
d4a2031c07 | ||
|
|
8cf0b6cf51 | ||
|
|
f2010bf7a5 | ||
|
|
8f56a7fe69 | ||
|
|
64c132ee0a | ||
|
|
84a7e86fe3 | ||
|
|
a8c09d6144 | ||
|
|
87c46ba730 | ||
|
|
0f83dc6491 | ||
|
|
cc22861719 | ||
|
|
a14c192ea3 | ||
|
|
b3d98be862 | ||
|
|
43027a4728 | ||
|
|
03831a7394 | ||
|
|
fdbbd181ea | ||
|
|
69075376dc | ||
|
|
504d1440cc | ||
|
|
9e33b8b8da | ||
|
|
0cfcc5cd29 | ||
|
|
e0de662f8e | ||
|
|
66a836d094 | ||
|
|
80095f4962 | ||
|
|
828d83dbef | ||
|
|
7de665d1e4 | ||
|
|
0a356ba73a | ||
|
|
41de8caa13 | ||
|
|
968609d06d | ||
|
|
3b199a2a87 | ||
|
|
0c1018ec61 | ||
|
|
bc3f2db3de | ||
|
|
06bedb7adb | ||
|
|
45a9751217 | ||
|
|
e8da62aa29 | ||
|
|
ddb2ff4216 | ||
|
|
f27e3478b9 | ||
|
|
38dc7fb7bd | ||
|
|
aa4cd13c31 | ||
|
|
4a4b6c04a1 | ||
|
|
37fa3b34a2 | ||
|
|
f8084e7955 | ||
|
|
4d5119ce3e | ||
|
|
d85c347a6c | ||
|
|
7dd758a753 | ||
|
|
806654fc44 | ||
|
|
8e6b91cb9e | ||
|
|
334e6dca28 | ||
|
|
f2daa17b92 | ||
|
|
6d9fccacb1 | ||
|
|
37638e7ed0 | ||
|
|
8a0e650511 | ||
|
|
8ba5a0d90c | ||
|
|
bfd3edb617 | ||
|
|
56ba24962c | ||
|
|
19a2110ba2 | ||
|
|
242a3eec63 | ||
|
|
fee46f2e54 | ||
|
|
6aed7e429a | ||
|
|
517ea82b99 | ||
|
|
99c29343de | ||
|
|
892fa76883 | ||
|
|
d446b57d05 | ||
|
|
0e086d788b | ||
|
|
498841d45d | ||
|
|
d1f8ee1e56 | ||
|
|
07feb5c925 | ||
|
|
75fd263e85 | ||
|
|
1e1f444cab | ||
|
|
89cc7e5fa9 | ||
|
|
265e7aefbf | ||
|
|
1c55a6c6dc | ||
|
|
8f18b5b8a7 | ||
|
|
f790182f0b | ||
|
|
813007a5d8 | ||
|
|
d03ff1e4d0 | ||
|
|
932bbd0381 | ||
|
|
01bd648cb2 | ||
|
|
779a5606a7 | ||
|
|
ccc11e49d2 | ||
|
|
d28c994ecd | ||
|
|
5d88717f32 | ||
|
|
e35cfd4971 | ||
|
|
bcc4bf5c2b | ||
|
|
a0594cbce3 | ||
|
|
078bfefe41 | ||
|
|
9c1b11d605 | ||
|
|
44d82f9190 | ||
|
|
37fcde30d6 | ||
|
|
09c6cb4d6b | ||
|
|
b428343c2a | ||
|
|
dfce292294 | ||
|
|
2b8f613a00 | ||
|
|
2eb137618e | ||
|
|
4bb2b8ca9b | ||
|
|
5179562fb2 | ||
|
|
0a4de45453 | ||
|
|
ffdc658dc8 | ||
|
|
7530f4407b | ||
|
|
73864c8101 | ||
|
|
f948917124 | ||
|
|
0d44492086 | ||
|
|
38a22fbc99 | ||
|
|
8ae435549d | ||
|
|
9b113c0cbb | ||
|
|
0e0fac8c4b | ||
|
|
4cd9bb8f99 | ||
|
|
ad9eaeafeb | ||
|
|
6cd392909c | ||
|
|
49ec430592 | ||
|
|
09f3fbeb38 | ||
|
|
e7698686fa | ||
|
|
66d939df0d | ||
|
|
6bc079c7b7 | ||
|
|
299419917e | ||
|
|
69f6afe420 | ||
|
|
b7279a3d9e | ||
|
|
e14b854d7b | ||
|
|
8bd7c601c0 | ||
|
|
997288fa03 | ||
|
|
0f26b39997 | ||
|
|
ae66fcac1e | ||
|
|
43944a94eb | ||
|
|
eba0bde6f3 | ||
|
|
4544af441b | ||
|
|
a8be94de6b | ||
|
|
b24df31c2b | ||
|
|
332ba8ed7e | ||
|
|
58400f53bc | ||
|
|
01c2112881 | ||
|
|
a546c2247d | ||
|
|
0da9142009 | ||
|
|
796add0ee2 | ||
|
|
00b32f64e6 | ||
|
|
f97b3f23e2 | ||
|
|
08a079a96e |
No files matched your search
@@ -0,0 +1,13 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
#patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: irmen
|
||||
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
#liberapay: # Replace with a single Liberapay username
|
||||
#issuehunt: # Replace with a single IssueHunt username
|
||||
#otechie: # Replace with a single Otechie username
|
||||
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
Generated
+21
-24
@@ -1,26 +1,23 @@
|
||||
<component name="libraryTable">
|
||||
<library name="KotlinJavaRuntime" type="repository">
|
||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.0/kotlin-stdlib-jdk8-1.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.0/kotlin-stdlib-1.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.0/kotlin-stdlib-common-1.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.0/kotlin-stdlib-jdk7-1.9.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.0/kotlin-stdlib-jdk8-1.9.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.0/kotlin-stdlib-1.9.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.0/kotlin-stdlib-common-1.9.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.0/kotlin-stdlib-jdk7-1.9.0-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.0/kotlin-stdlib-jdk8-1.9.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.0/kotlin-stdlib-1.9.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.0/kotlin-stdlib-common-1.9.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.0/kotlin-stdlib-jdk7-1.9.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="KotlinJavaRuntime" type="repository">
|
||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.22" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.22/kotlin-stdlib-jdk7-1.9.22.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.22/kotlin-stdlib-jdk7-1.9.22-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.22/kotlin-stdlib-jdk7-1.9.22-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
Generated
+3
-3
@@ -1,9 +1,9 @@
|
||||
<component name="libraryTable">
|
||||
<library name="slf4j.simple" type="repository">
|
||||
<properties maven-id="org.slf4j:slf4j-simple:2.0.9" />
|
||||
<properties maven-id="org.slf4j:slf4j-simple:2.0.11" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/2.0.9/slf4j-simple-2.0.9.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/2.0.9/slf4j-api-2.0.9.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/2.0.11/slf4j-simple-2.0.11.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/2.0.11/slf4j-api-2.0.11.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
[](https://ko-fi.com/H2H6S0FFF)
|
||||
[](https://prog8.readthedocs.io/)
|
||||
|
||||
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
||||
@@ -9,6 +10,13 @@ This is a structured programming language for the 8-bit 6502/6510/65c02 micropro
|
||||
as used in many home computers from that era. It is a medium to low level programming language,
|
||||
which aims to provide many conveniences over raw assembly code (even when using a macro assembler).
|
||||
|
||||
**Want to buy me a coffee or a pizza perhaps?**
|
||||
|
||||
This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can.
|
||||
If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,
|
||||
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen).
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
|
||||
|
||||
@@ -174,7 +174,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
// }
|
||||
// VarDeclType.MEMORY -> {
|
||||
// val numElements =
|
||||
// if(decl.datatype in ArrayDatatypes)
|
||||
// if(decl.isArray)
|
||||
// decl.arraysize!!.constIndex()
|
||||
// else null
|
||||
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
|
||||
|
||||
@@ -31,7 +31,10 @@ sealed class PtNode(val position: Position) {
|
||||
}
|
||||
|
||||
|
||||
class PtNodeGroup : PtNode(Position.DUMMY)
|
||||
sealed interface IPtStatementContainer
|
||||
|
||||
|
||||
class PtNodeGroup : PtNode(Position.DUMMY), IPtStatementContainer
|
||||
|
||||
|
||||
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
|
||||
@@ -64,27 +67,30 @@ class PtProgram(
|
||||
children.asSequence().filterIsInstance<PtBlock>()
|
||||
|
||||
fun entrypoint(): PtSub? =
|
||||
allBlocks().firstOrNull { it.name == "main" || it.name=="p8_main" }
|
||||
allBlocks().firstOrNull { it.name == "main" || it.name=="p8b_main" }
|
||||
?.children
|
||||
?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8_start" || it.name=="p8_main.p8_start") } as PtSub?
|
||||
?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8s_start" || it.name=="p8b_main.p8s_start") } as PtSub?
|
||||
}
|
||||
|
||||
|
||||
class PtBlock(name: String,
|
||||
val address: UInt?,
|
||||
val library: Boolean,
|
||||
val forceOutput: Boolean,
|
||||
val noSymbolPrefixing: Boolean,
|
||||
val veraFxMuls: Boolean,
|
||||
val alignment: BlockAlignment,
|
||||
val source: SourceCode, // taken from the module the block is defined in.
|
||||
val options: Options,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position) {
|
||||
) : PtNamedNode(name, position), IPtStatementContainer {
|
||||
enum class BlockAlignment {
|
||||
NONE,
|
||||
WORD,
|
||||
PAGE
|
||||
}
|
||||
|
||||
class Options(val address: UInt? = null,
|
||||
val forceOutput: Boolean = false,
|
||||
val noSymbolPrefixing: Boolean = false,
|
||||
val veraFxMuls: Boolean = false,
|
||||
val ignoreUnused: Boolean = false,
|
||||
val alignment: BlockAlignment = BlockAlignment.NONE)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package prog8.code.ast
|
||||
import prog8.code.core.*
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.round
|
||||
import kotlin.math.truncate
|
||||
|
||||
|
||||
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
|
||||
@@ -36,7 +36,14 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
||||
return arrayIndexExpr!! isSameAs other.arrayIndexExpr!!
|
||||
}
|
||||
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index && other.splitWords==splitWords
|
||||
is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
|
||||
is PtBinaryExpression -> {
|
||||
if(other !is PtBinaryExpression || other.operator!=operator)
|
||||
false
|
||||
else if(operator in AssociativeOperators)
|
||||
(other.left isSameAs left && other.right isSameAs right) || (other.left isSameAs right && other.right isSameAs left)
|
||||
else
|
||||
other.left isSameAs left && other.right isSameAs right
|
||||
}
|
||||
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
|
||||
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
|
||||
is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register
|
||||
@@ -72,7 +79,12 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
||||
is PtArray -> true
|
||||
is PtArrayIndexer -> index is PtNumber || index is PtIdentifier
|
||||
is PtBinaryExpression -> false
|
||||
is PtBuiltinFunctionCall -> name in arrayOf("msb", "lsb", "peek", "peekw", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd")
|
||||
is PtBuiltinFunctionCall -> {
|
||||
when (name) {
|
||||
in arrayOf("msb", "lsb", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> this.args.all { it.isSimple() }
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
is PtContainmentCheck -> false
|
||||
is PtFunctionCall -> false
|
||||
is PtIdentifier -> true
|
||||
@@ -126,16 +138,16 @@ class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
|
||||
|
||||
class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(elementType, position) {
|
||||
val variable: PtIdentifier
|
||||
get() = children[0] as PtIdentifier
|
||||
get() {
|
||||
require((children[0] as? PtIdentifier)?.type in ArrayDatatypes+DataType.STR) // TODO remove
|
||||
return children[0] as PtIdentifier
|
||||
}
|
||||
val index: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
|
||||
val splitWords: Boolean
|
||||
get() = variable.type in SplitWordArrayTypes
|
||||
|
||||
val usesPointerVariable: Boolean
|
||||
get() = variable.type==DataType.UWORD
|
||||
|
||||
init {
|
||||
require(elementType in NumericDatatypes)
|
||||
}
|
||||
@@ -227,9 +239,9 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
|
||||
if(type==DataType.BOOL)
|
||||
throw IllegalArgumentException("bool should have become ubyte @$position")
|
||||
if(type!=DataType.FLOAT) {
|
||||
val rounded = round(number)
|
||||
if (rounded != number)
|
||||
throw IllegalArgumentException("refused rounding of float to avoid loss of precision @$position")
|
||||
val trunc = truncate(number)
|
||||
if (trunc != number)
|
||||
throw IllegalArgumentException("refused truncating of float to avoid loss of precision @$position")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +250,10 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if(other==null || other !is PtNumber)
|
||||
return false
|
||||
return number==other.number
|
||||
else if(type!=DataType.BOOL && other.type!=DataType.BOOL)
|
||||
return number==other.number
|
||||
else
|
||||
return type==other.type && number==other.number
|
||||
}
|
||||
|
||||
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
|
||||
|
||||
@@ -53,15 +53,24 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
"goto ${node.identifier.name}"
|
||||
else if(node.address!=null)
|
||||
"goto ${node.address.toHex()}"
|
||||
else if(node.generatedLabel!=null)
|
||||
"goto ${node.generatedLabel}"
|
||||
else
|
||||
"???"
|
||||
}
|
||||
is PtAsmSub -> {
|
||||
val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..."
|
||||
val params = node.parameters.map {
|
||||
val register = it.first.registerOrPair
|
||||
val statusflag = it.first.statusflag
|
||||
"${it.second.type} ${it.second.name} @${register ?: statusflag}"
|
||||
}.joinToString(", ")
|
||||
val clobbers = if (node.clobbers.isEmpty()) "" else "clobbers ${node.clobbers}"
|
||||
val returns = if (node.returns.isEmpty()) "" else (if (node.returns.size == 1) "-> ${node.returns[0].second.name.lowercase()}" else "-> ${node.returns.map { it.second.name.lowercase() }}")
|
||||
val returns = if (node.returns.isEmpty()) "" else {
|
||||
"-> ${node.returns.map {
|
||||
val register = it.first.registerOrPair
|
||||
val statusflag = it.first.statusflag
|
||||
"${it.second} @${register ?: statusflag}"}
|
||||
.joinToString(", ")
|
||||
}"
|
||||
}
|
||||
val str = if (node.inline) "inline " else ""
|
||||
if(node.address==null) {
|
||||
str + "asmsub ${node.name}($params) $clobbers $returns"
|
||||
@@ -70,8 +79,8 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
}
|
||||
}
|
||||
is PtBlock -> {
|
||||
val addr = if(node.address==null) "" else "@${node.address.toHex()}"
|
||||
val align = if(node.alignment==PtBlock.BlockAlignment.NONE) "" else "align=${node.alignment}"
|
||||
val addr = if(node.options.address==null) "" else "@${node.options.address.toHex()}"
|
||||
val align = if(node.options.alignment==PtBlock.BlockAlignment.NONE) "" else "align=${node.options.alignment}"
|
||||
"\nblock '${node.name}' $addr $align"
|
||||
}
|
||||
is PtConstant -> {
|
||||
@@ -89,7 +98,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
}
|
||||
}
|
||||
is PtSub -> {
|
||||
val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..."
|
||||
val params = node.parameters.map { "${it.type} ${it.name}" }.joinToString(", ")
|
||||
var str = "sub ${node.name}($params) "
|
||||
if(node.returntype!=null)
|
||||
str += "-> ${node.returntype.name.lowercase()}"
|
||||
|
||||
@@ -23,7 +23,7 @@ class PtSub(
|
||||
val parameters: List<PtSubroutineParameter>,
|
||||
val returntype: DataType?,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position), IPtSubroutine {
|
||||
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer {
|
||||
init {
|
||||
// params and return value should not be str
|
||||
if(parameters.any{ it.type !in NumericDatatypes })
|
||||
@@ -101,9 +101,8 @@ class PtIfElse(position: Position) : PtNode(position) {
|
||||
}
|
||||
|
||||
|
||||
class PtJump(val identifier: PtIdentifier?,
|
||||
class PtJump(val identifier: PtIdentifier?, // note: even ad-hoc labels are wrapped as an Identifier to simplify code. Just use dummy type and position.
|
||||
val address: UInt?,
|
||||
val generatedLabel: String?,
|
||||
position: Position) : PtNode(position) {
|
||||
init {
|
||||
identifier?.let {it.parent = this }
|
||||
|
||||
@@ -125,14 +125,11 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"pokew" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
|
||||
"pokef" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.FLOAT))), null),
|
||||
"pokemon" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
||||
"pop" to FSignature(false, listOf(FParam("target", ByteDatatypes)), null),
|
||||
"popw" to FSignature(false, listOf(FParam("target", WordDatatypes)), null),
|
||||
"push" to FSignature(false, listOf(FParam("value", ByteDatatypes)), null),
|
||||
"pushw" to FSignature(false, listOf(FParam("value", WordDatatypes)), null),
|
||||
"rsave" to FSignature(false, emptyList(), null),
|
||||
"rrestore" to FSignature(false, emptyList(), null),
|
||||
"memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"call" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
)
|
||||
|
||||
val InplaceModifyingBuiltinFunctions = setOf("setlsb", "setmsb", "rol", "ror", "rol2", "ror2", "sort", "reverse")
|
||||
@@ -21,8 +21,11 @@ class CompilationOptions(val output: OutputType,
|
||||
var includeSourcelines: Boolean = false,
|
||||
var experimentalCodegen: Boolean = false,
|
||||
var varsHighBank: Int? = null,
|
||||
var varsGolden: Boolean = false,
|
||||
var slabsHighBank: Int? = null,
|
||||
var slabsGolden: Boolean = false,
|
||||
var splitWordArrays: Boolean = false,
|
||||
var breakpointCpuInstruction: Boolean = false,
|
||||
var breakpointCpuInstruction: String? = null,
|
||||
var outputDir: Path = Path(""),
|
||||
var symbolDefs: Map<String, String> = emptyMap()
|
||||
) {
|
||||
|
||||
@@ -120,7 +120,7 @@ enum class BranchCondition {
|
||||
PL, // PL == POS
|
||||
POS,
|
||||
VS,
|
||||
VC,
|
||||
VC
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,8 @@ val ElementToArrayTypes = mapOf(
|
||||
DataType.WORD to DataType.ARRAY_W,
|
||||
DataType.UWORD to DataType.ARRAY_UW,
|
||||
DataType.FLOAT to DataType.ARRAY_F,
|
||||
DataType.BOOL to DataType.ARRAY_BOOL
|
||||
DataType.BOOL to DataType.ARRAY_BOOL,
|
||||
DataType.STR to DataType.ARRAY_UW // array of str is just an array of pointers
|
||||
)
|
||||
|
||||
val Cx16VirtualRegisters = arrayOf(
|
||||
|
||||
@@ -3,11 +3,12 @@ package prog8.code.core
|
||||
interface IErrorReporter {
|
||||
fun err(msg: String, position: Position)
|
||||
fun warn(msg: String, position: Position)
|
||||
fun info(msg: String, position: Position)
|
||||
fun undefined(symbol: List<String>, position: Position)
|
||||
fun noErrors(): Boolean
|
||||
fun report()
|
||||
fun finalizeNumErrors(numErrors: Int, numWarnings: Int) {
|
||||
fun finalizeNumErrors(numErrors: Int, numWarnings: Int, numInfos: Int) {
|
||||
if(numErrors>0)
|
||||
throw ErrorsReportedException("There are $numErrors errors and $numWarnings warnings.")
|
||||
throw ErrorsReportedException("There are $numErrors errors, $numWarnings warnings, and $numInfos infos.")
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@ interface IMachineDefinition {
|
||||
val PROGRAM_LOAD_ADDRESS : UInt
|
||||
val BSSHIGHRAM_START: UInt
|
||||
val BSSHIGHRAM_END: UInt
|
||||
val BSSGOLDENRAM_START: UInt
|
||||
val BSSGOLDENRAM_END: UInt
|
||||
|
||||
val cpu: CpuType
|
||||
var zeropage: Zeropage
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package prog8.code.core
|
||||
|
||||
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=")
|
||||
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "and", "or", "xor")
|
||||
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||
val LogicalOperators = setOf("and", "or", "xor", "not")
|
||||
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
|
||||
val BitwiseOperators = setOf("&", "|", "^", "~")
|
||||
val PrefixOperators = setOf("+", "-", "~", "not")
|
||||
// val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators
|
||||
|
||||
fun invertedComparisonOperator(operator: String) =
|
||||
when (operator) {
|
||||
|
||||
@@ -69,7 +69,8 @@ sealed class SourceCode {
|
||||
* Turn a plain String into a [SourceCode] object.
|
||||
* [origin] will be something like `string:44c56085`.
|
||||
*/
|
||||
class Text(override val text: String): SourceCode() {
|
||||
class Text(origText: String): SourceCode() {
|
||||
override val text = origText.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||
override val isFromResources = false
|
||||
override val isFromFilesystem = false
|
||||
override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}"
|
||||
@@ -95,7 +96,8 @@ sealed class SourceCode {
|
||||
val normalized = path.normalize()
|
||||
origin = relative(normalized).toString()
|
||||
try {
|
||||
text = Normalizer.normalize(normalized.readText(), Normalizer.Form.NFC)
|
||||
val contents = Normalizer.normalize(normalized.readText(), Normalizer.Form.NFC)
|
||||
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||
name = normalized.toFile().nameWithoutExtension
|
||||
} catch (nfx: java.nio.file.NoSuchFileException) {
|
||||
throw NoSuchFileException(normalized.toFile()).also { it.initCause(nfx) }
|
||||
@@ -127,7 +129,8 @@ sealed class SourceCode {
|
||||
)
|
||||
}
|
||||
val stream = object {}.javaClass.getResourceAsStream(normalized)
|
||||
text = stream!!.reader().use { Normalizer.normalize(it.readText(), Normalizer.Form.NFC) }
|
||||
val contents = stream!!.reader().use { Normalizer.normalize(it.readText(), Normalizer.Form.NFC) }
|
||||
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||
name = Path(pathString).toFile().nameWithoutExtension
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package prog8.code.optimize
|
||||
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
fun optimizeIntermediateAst(program: PtProgram, options: CompilationOptions, errors: IErrorReporter) {
|
||||
if (!options.optimize)
|
||||
return
|
||||
while(errors.noErrors() && optimizeCommonSubExpressions(program, errors)>0) {
|
||||
// keep rolling
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Boolean) {
|
||||
fun recurse(node: PtNode, depth: Int) {
|
||||
if(act(node, depth))
|
||||
node.children.forEach { recurse(it, depth+1) }
|
||||
}
|
||||
recurse(root, 0)
|
||||
}
|
||||
|
||||
|
||||
private var tempVarCounter = 0
|
||||
|
||||
private fun optimizeCommonSubExpressions(program: PtProgram, errors: IErrorReporter): Int {
|
||||
|
||||
fun extractableSubExpr(expr: PtExpression): Boolean {
|
||||
val result = if(expr is PtBinaryExpression)
|
||||
expr.type !in ByteDatatypes ||
|
||||
!expr.left.isSimple() ||
|
||||
!expr.right.isSimple() ||
|
||||
(expr.operator !in LogicalOperators && expr.operator !in BitwiseOperators)
|
||||
else if (expr is PtArrayIndexer && expr.type !in ByteDatatypes)
|
||||
true
|
||||
else
|
||||
!expr.isSimple()
|
||||
return result
|
||||
}
|
||||
|
||||
// for each Binaryexpression, recurse to find a common subexpression pair therein.
|
||||
val commons = mutableMapOf<PtBinaryExpression, Pair<PtExpression, PtExpression>>()
|
||||
walkAst(program) { node: PtNode, depth: Int ->
|
||||
if(node is PtBinaryExpression) {
|
||||
val subExpressions = mutableListOf<PtExpression>()
|
||||
walkAst(node.left) { subNode: PtNode, subDepth: Int ->
|
||||
if (subNode is PtExpression) {
|
||||
if(extractableSubExpr(subNode)) subExpressions.add(subNode)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
walkAst(node.right) { subNode: PtNode, subDepth: Int ->
|
||||
if (subNode is PtExpression) {
|
||||
if(extractableSubExpr(subNode)) subExpressions.add(subNode)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
|
||||
outer@for (first in subExpressions) {
|
||||
for (second in subExpressions) {
|
||||
if (first!==second && first isSameAs second) {
|
||||
commons[node] = first to second
|
||||
break@outer // do only 1 replacement at a time per binaryexpression
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
} else true
|
||||
}
|
||||
|
||||
// replace common subexpressions by a temp variable that is assigned only once.
|
||||
// TODO: check for commonalities across multiple separate expressions in the current scope, not only inside a single line
|
||||
commons.forEach { binexpr, (occurrence1, occurrence2) ->
|
||||
val (stmtContainer, stmt) = findContainingStatements(binexpr)
|
||||
val occurrence1idx = occurrence1.parent.children.indexOf(occurrence1)
|
||||
val occurrence2idx = occurrence2.parent.children.indexOf(occurrence2)
|
||||
val containerScopedName = findScopeName(stmtContainer)
|
||||
tempVarCounter++
|
||||
val tempvarName = "prog8_subexprvar_$tempVarCounter"
|
||||
// TODO: some tempvars could be reused, if they are from different lines
|
||||
|
||||
val datatype = occurrence1.type
|
||||
val singleReplacement1 = PtIdentifier("$containerScopedName.$tempvarName", datatype, occurrence1.position)
|
||||
val singleReplacement2 = PtIdentifier("$containerScopedName.$tempvarName", datatype, occurrence2.position)
|
||||
occurrence1.parent.children[occurrence1idx] = singleReplacement1
|
||||
singleReplacement1.parent = occurrence1.parent
|
||||
occurrence2.parent.children[occurrence2idx] = singleReplacement2
|
||||
singleReplacement2.parent = occurrence2.parent
|
||||
|
||||
val tempassign = PtAssignment(binexpr.position).also { assign ->
|
||||
assign.add(PtAssignTarget(binexpr.position).also { tgt->
|
||||
tgt.add(PtIdentifier("$containerScopedName.$tempvarName", datatype, binexpr.position))
|
||||
})
|
||||
assign.add(occurrence1)
|
||||
occurrence1.parent = assign
|
||||
}
|
||||
stmtContainer.children.add(stmtContainer.children.indexOf(stmt), tempassign)
|
||||
tempassign.parent = stmtContainer
|
||||
|
||||
val tempvar = PtVariable(tempvarName, datatype, ZeropageWish.NOT_IN_ZEROPAGE, null, null, binexpr.position)
|
||||
stmtContainer.add(0, tempvar)
|
||||
tempvar.parent = stmtContainer
|
||||
|
||||
// errors.info("common subexpressions replaced by a tempvar, maybe simplify the expression manually", binexpr.position)
|
||||
}
|
||||
|
||||
return commons.size
|
||||
}
|
||||
|
||||
|
||||
internal fun findScopeName(node: PtNode): String {
|
||||
var parent=node
|
||||
while(parent !is PtNamedNode)
|
||||
parent = parent.parent
|
||||
return parent.scopedName
|
||||
}
|
||||
|
||||
|
||||
internal fun findContainingStatements(node: PtNode): Pair<PtNode, PtNode> { // returns (parentstatementcontainer, childstatement)
|
||||
var parent = node.parent
|
||||
var child = node
|
||||
while(true) {
|
||||
if(parent is IPtStatementContainer) {
|
||||
return parent to child
|
||||
}
|
||||
child=parent
|
||||
parent=parent.parent
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||
else -> Int.MIN_VALUE
|
||||
else -> throw IllegalArgumentException("invalid datatype")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||
else -> Int.MIN_VALUE
|
||||
else -> throw IllegalArgumentException("invalid datatype")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ class AtariMachineDefinition: IMachineDefinition {
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
override val BSSGOLDENRAM_START = 0u // TODO
|
||||
override val BSSGOLDENRAM_END = 0u // TODO
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
@@ -17,6 +17,8 @@ class C128MachineDefinition: IMachineDefinition {
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
override val BSSGOLDENRAM_START = 0u // TODO
|
||||
override val BSSGOLDENRAM_END = 0u // TODO
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
@@ -17,7 +17,9 @@ class C64MachineDefinition: IMachineDefinition {
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
|
||||
override val BSSHIGHRAM_START = 0xc000u
|
||||
override val BSSHIGHRAM_END = 0xd000u
|
||||
override val BSSHIGHRAM_END = 0xcfffu
|
||||
override val BSSGOLDENRAM_START = 0u
|
||||
override val BSSGOLDENRAM_END = 0u
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
@@ -9,7 +9,7 @@ internal object CbmMemorySizer: IMemSizer {
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE
|
||||
else -> Int.MIN_VALUE
|
||||
else -> throw IllegalArgumentException("invalid datatype")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,9 @@ class CX16MachineDefinition: IMachineDefinition {
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
|
||||
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
|
||||
override val BSSHIGHRAM_END = 0xc000u // rom starts here.
|
||||
override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000
|
||||
override val BSSGOLDENRAM_START = 0x0400u
|
||||
override val BSSGOLDENRAM_END = 0x07ffu
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
@@ -36,7 +38,7 @@ class CX16MachineDefinition: IMachineDefinition {
|
||||
when(selectedEmulator) {
|
||||
1 -> {
|
||||
emulator = "x16emu"
|
||||
extraArgs = emptyList()
|
||||
extraArgs = listOf("-debug")
|
||||
}
|
||||
2 -> {
|
||||
emulator = "box16"
|
||||
@@ -49,7 +51,7 @@ class CX16MachineDefinition: IMachineDefinition {
|
||||
}
|
||||
|
||||
println("\nStarting Commander X16 emulator $emulator...")
|
||||
val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
|
||||
val process: Process = processb.start()
|
||||
|
||||
@@ -15,8 +15,10 @@ class PETMachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0401u
|
||||
|
||||
override val BSSHIGHRAM_START = 0xffffu
|
||||
override val BSSHIGHRAM_END = 0xffffu
|
||||
override val BSSHIGHRAM_START = 0u
|
||||
override val BSSHIGHRAM_END = 0u
|
||||
override val BSSGOLDENRAM_START = 0u
|
||||
override val BSSGOLDENRAM_END = 0u
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
@@ -17,6 +17,8 @@ class VirtualMachineDefinition: IMachineDefinition {
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // not actually used
|
||||
override val BSSHIGHRAM_END = 0u // not actually used
|
||||
override val BSSGOLDENRAM_START = 0u // not actually used
|
||||
override val BSSGOLDENRAM_END = 0u // not actually used
|
||||
override lateinit var zeropage: Zeropage // not actually used
|
||||
override lateinit var golden: GoldenRam // not actually used
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import com.github.michaelbull.result.fold
|
||||
import prog8.code.StNode
|
||||
import prog8.code.StNodeType
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.SymbolTableMaker
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.codegen.cpu6502.assignment.*
|
||||
import java.util.*
|
||||
import kotlin.io.path.Path
|
||||
@@ -32,7 +34,14 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
|
||||
val functionCallsToPrefix = mutableListOf<Pair<PtNode, Int>>() // parent + index
|
||||
|
||||
fun prefixNamedNode(node: PtNamedNode) {
|
||||
node.name = "p8_${node.name}"
|
||||
when(node) {
|
||||
is PtAsmSub, is PtSub -> node.name = "p8s_${node.name}"
|
||||
is PtBlock -> node.name = "p8b_${node.name}"
|
||||
is PtLabel -> node.name = "p8l_${node.name}"
|
||||
is PtConstant -> node.name = "p8c_${node.name}"
|
||||
is PtVariable, is PtMemMapped, is PtSubroutineParameter -> node.name = "p8v_${node.name}"
|
||||
else -> node.name = "p8_${node.name}"
|
||||
}
|
||||
}
|
||||
|
||||
fun prefixSymbols(node: PtNode) {
|
||||
@@ -47,7 +56,7 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
|
||||
}
|
||||
is PtFunctionCall -> {
|
||||
val stNode = st.lookup(node.name)!!
|
||||
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
|
||||
if(stNode.astNode.definingBlock()?.options?.noSymbolPrefixing!=true) {
|
||||
val index = node.parent.children.indexOf(node)
|
||||
functionCallsToPrefix += node.parent to index
|
||||
}
|
||||
@@ -58,25 +67,16 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
|
||||
lookupName = lookupName.dropLast(4)
|
||||
}
|
||||
val stNode = st.lookup(lookupName)!!
|
||||
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
|
||||
if(stNode.astNode.definingBlock()?.options?.noSymbolPrefixing!=true) {
|
||||
val index = node.parent.children.indexOf(node)
|
||||
nodesToPrefix += node.parent to index
|
||||
}
|
||||
}
|
||||
is PtJump -> {
|
||||
if(node.identifier!=null) {
|
||||
val stNode = st.lookup(node.identifier!!.name)!!
|
||||
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
|
||||
val index = node.parent.children.indexOf(node)
|
||||
nodesToPrefix += node.parent to index
|
||||
}
|
||||
}
|
||||
else if(node.generatedLabel!=null) {
|
||||
val stNode = st.lookup(node.generatedLabel!!)!!
|
||||
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
|
||||
val index = node.parent.children.indexOf(node)
|
||||
nodesToPrefix += node.parent to index
|
||||
}
|
||||
val stNode = st.lookup(node.identifier!!.name) ?: throw AssemblyError("name not found ${node.identifier}")
|
||||
if(stNode.astNode.definingBlock()?.options?.noSymbolPrefixing!=true) {
|
||||
val index = node.parent.children.indexOf(node)
|
||||
nodesToPrefix += node.parent to index
|
||||
}
|
||||
}
|
||||
is PtBlock -> prefixNamedNode(node)
|
||||
@@ -94,7 +94,7 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
|
||||
}
|
||||
|
||||
program.allBlocks().forEach { block ->
|
||||
if (!block.noSymbolPrefixing) {
|
||||
if (!block.options.noSymbolPrefixing) {
|
||||
prefixSymbols(block)
|
||||
}
|
||||
}
|
||||
@@ -124,8 +124,20 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
|
||||
}
|
||||
}
|
||||
|
||||
private fun prefixScopedName(name: String, type: Char): String {
|
||||
if('.' !in name)
|
||||
return "p8${type}_$name"
|
||||
val parts = name.split('.')
|
||||
val firstPrefixed = "p8b_${parts[0]}"
|
||||
val lastPrefixed = "p8${type}_${parts.last()}"
|
||||
// the parts in between are assumed to be subroutine scopes.
|
||||
val inbetweenPrefixed = parts.drop(1).dropLast(1).map{ "p8s_$it" }
|
||||
val prefixed = listOf(firstPrefixed) + inbetweenPrefixed + listOf(lastPrefixed)
|
||||
return prefixed.joinToString(".")
|
||||
}
|
||||
|
||||
private fun PtVariable.prefix(st: SymbolTable): PtVariable {
|
||||
name = name.split('.').map {"p8_$it" }.joinToString(".")
|
||||
name = prefixScopedName(name, 'v')
|
||||
if(value==null)
|
||||
return this
|
||||
|
||||
@@ -137,7 +149,7 @@ private fun PtVariable.prefix(st: SymbolTable): PtVariable {
|
||||
is PtIdentifier -> newValue.add(elt.prefix(arrayValue, st))
|
||||
is PtNumber -> newValue.add(elt)
|
||||
is PtAddressOf -> {
|
||||
if(elt.definingBlock()?.noSymbolPrefixing==true)
|
||||
if(elt.definingBlock()?.options?.noSymbolPrefixing==true)
|
||||
newValue.add(elt)
|
||||
else {
|
||||
val newAddr = PtAddressOf(elt.position)
|
||||
@@ -155,19 +167,14 @@ private fun PtVariable.prefix(st: SymbolTable): PtVariable {
|
||||
}
|
||||
|
||||
private fun PtJump.prefix(parent: PtNode, st: SymbolTable): PtJump {
|
||||
val jump = if(identifier!=null) {
|
||||
val prefixedIdent = identifier!!.prefix(this, st)
|
||||
PtJump(prefixedIdent, address, generatedLabel, position)
|
||||
} else {
|
||||
val prefixedLabel = generatedLabel!!.split('.').map {"p8_$it" }.joinToString(".")
|
||||
PtJump(null, address, prefixedLabel, position)
|
||||
}
|
||||
val prefixedIdent = identifier!!.prefix(this, st)
|
||||
val jump = PtJump(prefixedIdent, address, position)
|
||||
jump.parent = parent
|
||||
return jump
|
||||
}
|
||||
|
||||
private fun PtFunctionCall.prefix(parent: PtNode): PtFunctionCall {
|
||||
val newName = name.split('.').map {"p8_$it" }.joinToString(".")
|
||||
val newName = prefixScopedName(name, 's')
|
||||
val call = PtFunctionCall(newName, void, type, position)
|
||||
call.children.addAll(children)
|
||||
call.children.forEach { it.parent = call }
|
||||
@@ -176,11 +183,29 @@ private fun PtFunctionCall.prefix(parent: PtNode): PtFunctionCall {
|
||||
}
|
||||
|
||||
private fun PtIdentifier.prefix(parent: PtNode, st: SymbolTable): PtIdentifier {
|
||||
val target = st.lookup(name)
|
||||
if(target?.astNode?.definingBlock()?.noSymbolPrefixing==true)
|
||||
var target = st.lookup(name)
|
||||
if(target?.astNode?.definingBlock()?.options?.noSymbolPrefixing==true)
|
||||
return this
|
||||
|
||||
val newName = name.split('.').map { "p8_$it" }.joinToString(".")
|
||||
if(target==null) {
|
||||
if(name.endsWith("_lsb") || name.endsWith("_msb")) {
|
||||
target = st.lookup(name.dropLast(4))
|
||||
if(target?.astNode?.definingBlock()?.options?.noSymbolPrefixing==true)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
val prefixType = when(target!!.type) {
|
||||
StNodeType.BLOCK -> 'b'
|
||||
StNodeType.SUBROUTINE, StNodeType.ROMSUB -> 's'
|
||||
StNodeType.LABEL -> 'l'
|
||||
StNodeType.STATICVAR, StNodeType.MEMVAR -> 'v'
|
||||
StNodeType.CONSTANT -> 'c'
|
||||
StNodeType.BUILTINFUNC -> 's'
|
||||
StNodeType.MEMORYSLAB -> 'v'
|
||||
else -> '?'
|
||||
}
|
||||
val newName = prefixScopedName(name, prefixType)
|
||||
val node = PtIdentifier(newName, type, position)
|
||||
node.parent = parent
|
||||
return node
|
||||
@@ -199,7 +224,7 @@ class AsmGen6502Internal (
|
||||
internal val loopEndLabels = ArrayDeque<String>()
|
||||
private val zeropage = options.compTarget.machine.zeropage
|
||||
private val allocator = VariableAllocator(symbolTable, options, errors)
|
||||
private val assemblyLines = mutableListOf<String>()
|
||||
private val assembly = mutableListOf<String>()
|
||||
private val breakpointLabels = mutableListOf<String>()
|
||||
private val forloopsAsmGen = ForLoopsAsmGen(this, zeropage)
|
||||
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
|
||||
@@ -211,7 +236,7 @@ class AsmGen6502Internal (
|
||||
|
||||
fun compileToAssembly(): IAssemblyProgram? {
|
||||
|
||||
assemblyLines.clear()
|
||||
assembly.clear()
|
||||
loopEndLabels.clear()
|
||||
|
||||
println("Generating assembly code... ")
|
||||
@@ -219,15 +244,22 @@ class AsmGen6502Internal (
|
||||
|
||||
if(errors.noErrors()) {
|
||||
val output = options.outputDir.resolve("${program.name}.asm")
|
||||
val asmLines = assembly.asSequence().flatMapTo(mutableListOf()) { it.split('\n') }
|
||||
if(options.compTarget.name==Cx16Target.NAME) {
|
||||
scanInvalid65816instructions(asmLines)
|
||||
if(!errors.noErrors()) {
|
||||
errors.report()
|
||||
return null
|
||||
}
|
||||
}
|
||||
if(options.optimize) {
|
||||
val separateLines = assemblyLines.flatMapTo(mutableListOf()) { it.split('\n') }
|
||||
assemblyLines.clear()
|
||||
while(optimizeAssembly(separateLines, options.compTarget.machine, symbolTable)>0) {
|
||||
while(optimizeAssembly(asmLines, options.compTarget.machine, symbolTable)>0) {
|
||||
// optimize the assembly source code
|
||||
}
|
||||
output.writeLines(separateLines)
|
||||
output.writeLines(asmLines)
|
||||
} else {
|
||||
output.writeLines(assemblyLines)
|
||||
// write the unmodified code
|
||||
output.writeLines(assembly)
|
||||
}
|
||||
return AssemblyProgram(program.name, options.outputDir, options.compTarget)
|
||||
} else {
|
||||
@@ -236,6 +268,22 @@ class AsmGen6502Internal (
|
||||
}
|
||||
}
|
||||
|
||||
private fun scanInvalid65816instructions(asmLines: MutableList<String>) {
|
||||
// The CommanderX16 ships with a WDC 65C02 CPU or a WDC 65816 CPU
|
||||
// The latter is compatible with the 65C02 except for 4 instructions: RMB, SMB, BBS, BBR.
|
||||
// We cannot set a different 6502 CPU target for the 64tass assembler, because we still need to support the STP and WAI instructions...
|
||||
// so we have to scan for these instructions ourselves.
|
||||
val invalid = Regex("""\s*((rmb\s|smb\s|bbs\s|bbr\s)|(rmb[0-7]|smb[0-7]|bbs[0-7]|bbr[0-7]))""", RegexOption.IGNORE_CASE)
|
||||
for((index, line) in asmLines.withIndex()) {
|
||||
if(line.length>=4 && invalid.matchesAt(line, 0)) {
|
||||
errors.err(
|
||||
"invalid assembly instruction used (not compatible with the 65816 CPU): ${line.trim()}",
|
||||
Position("<output-assemblycode>", index, 1, 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun isTargetCpu(cpu: CpuType) = options.compTarget.machine.cpu == cpu
|
||||
|
||||
private var lastSourceLineNumber: Int = -1
|
||||
@@ -259,9 +307,9 @@ class AsmGen6502Internal (
|
||||
for (line in fragment.splitToSequence('\n')) {
|
||||
val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line
|
||||
// trimmed = trimmed.replace(Regex("^\\+\\s+"), "+\t") // sanitize local label indentation
|
||||
assemblyLines.add(trimmed)
|
||||
assembly.add(trimmed)
|
||||
}
|
||||
} else assemblyLines.add(fragment)
|
||||
} else assembly.add(fragment)
|
||||
}
|
||||
|
||||
fun asmSymbolName(regs: RegisterOrPair): String =
|
||||
@@ -296,15 +344,32 @@ class AsmGen6502Internal (
|
||||
name
|
||||
}
|
||||
|
||||
fun asmVariableName(st: StNode, scope: PtSub?): String {
|
||||
val name = asmVariableName(st.scopedName)
|
||||
if(scope==null)
|
||||
return name
|
||||
// remove the whole prefix and just make the variable name locally scoped (64tass scopes it to the proper .proc block)
|
||||
val subName = scope.scopedName
|
||||
return if (name.length>subName.length && name.startsWith(subName) && name[subName.length] == '.')
|
||||
name.drop(subName.length+1)
|
||||
else
|
||||
name
|
||||
}
|
||||
|
||||
|
||||
internal val tempVarsCounters = mutableMapOf(
|
||||
DataType.BYTE to 0,
|
||||
DataType.UBYTE to 0,
|
||||
DataType.WORD to 0,
|
||||
DataType.UWORD to 0,
|
||||
DataType.FLOAT to 0
|
||||
)
|
||||
|
||||
internal fun buildTempVarName(dt: DataType, counter: Int): String = "prog8_tmpvar_${dt.toString().lowercase()}_$counter"
|
||||
|
||||
internal fun getTempVarName(dt: DataType): String {
|
||||
return when(dt) {
|
||||
DataType.UBYTE -> "cx16.r9L"
|
||||
DataType.BYTE -> "cx16.r9sL"
|
||||
DataType.UWORD -> "cx16.r9"
|
||||
DataType.WORD -> "cx16.r9s"
|
||||
DataType.FLOAT -> "floats.floats_temp_var"
|
||||
else -> throw AssemblyError("invalid dt $dt")
|
||||
}
|
||||
tempVarsCounters[dt] = tempVarsCounters.getValue(dt)+1
|
||||
return buildTempVarName(dt, tempVarsCounters.getValue(dt))
|
||||
}
|
||||
|
||||
internal fun loadByteFromPointerIntoA(pointervar: PtIdentifier): String {
|
||||
@@ -487,18 +552,14 @@ class AsmGen6502Internal (
|
||||
}
|
||||
}
|
||||
|
||||
internal fun loadScaledArrayIndexIntoRegister(
|
||||
expr: PtArrayIndexer,
|
||||
elementDt: DataType,
|
||||
register: CpuRegister
|
||||
) {
|
||||
internal fun loadScaledArrayIndexIntoRegister(expr: PtArrayIndexer, register: CpuRegister) {
|
||||
val reg = register.toString().lowercase()
|
||||
val indexnum = expr.index.asConstInteger()
|
||||
if (indexnum != null) {
|
||||
val indexValue = if(expr.splitWords)
|
||||
indexnum
|
||||
else
|
||||
indexnum * options.compTarget.memorySize(elementDt)
|
||||
indexnum * options.compTarget.memorySize(expr.type)
|
||||
out(" ld$reg #$indexValue")
|
||||
return
|
||||
}
|
||||
@@ -508,7 +569,7 @@ class AsmGen6502Internal (
|
||||
return
|
||||
}
|
||||
|
||||
when (elementDt) {
|
||||
when (expr.type) {
|
||||
in ByteDatatypes -> {
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register), false)
|
||||
}
|
||||
@@ -684,7 +745,6 @@ class AsmGen6502Internal (
|
||||
if (jump is PtJump) {
|
||||
// jump somewhere if X!=0
|
||||
val label = when {
|
||||
jump.generatedLabel!=null -> jump.generatedLabel!!
|
||||
jump.identifier!=null -> asmSymbolName(jump.identifier!!)
|
||||
jump.address!=null -> jump.address!!.toHex()
|
||||
else -> throw AssemblyError("weird jump")
|
||||
@@ -984,7 +1044,6 @@ $repeatLabel""")
|
||||
|
||||
private fun getJumpTarget(jump: PtJump): Pair<String, Boolean> {
|
||||
val ident = jump.identifier
|
||||
val label = jump.generatedLabel
|
||||
val addr = jump.address
|
||||
return when {
|
||||
ident!=null -> {
|
||||
@@ -995,7 +1054,6 @@ $repeatLabel""")
|
||||
else
|
||||
Pair(asmSymbolName(ident), false)
|
||||
}
|
||||
label!=null -> Pair(label, false)
|
||||
addr!=null -> Pair(addr.toHex(), false)
|
||||
else -> Pair("????", false)
|
||||
}
|
||||
@@ -1027,7 +1085,7 @@ $repeatLabel""")
|
||||
if(asm.isIR)
|
||||
throw AssemblyError("%asm containing IR code cannot be translated to 6502 assembly")
|
||||
else
|
||||
assemblyLines.add(asm.assembly.trimEnd().trimStart('\r', '\n'))
|
||||
assembly.add(asm.assembly.trimEnd().trimStart('\r', '\n'))
|
||||
}
|
||||
|
||||
private fun translate(incbin: PtIncludeBinary) {
|
||||
@@ -1049,14 +1107,8 @@ $repeatLabel""")
|
||||
val label = "_prog8_breakpoint_${breakpointLabels.size+1}"
|
||||
breakpointLabels.add(label)
|
||||
out(label)
|
||||
if(options.breakpointCpuInstruction) {
|
||||
val instruction =
|
||||
when(options.compTarget.machine.cpu) {
|
||||
CpuType.CPU6502 -> "brk"
|
||||
CpuType.CPU65c02 -> "stp"
|
||||
else -> throw AssemblyError("invalid cpu type")
|
||||
}
|
||||
out(" $instruction")
|
||||
if(options.breakpointCpuInstruction!=null) {
|
||||
out(" ${options.breakpointCpuInstruction}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1140,7 +1192,7 @@ $repeatLabel""")
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun tryOptimizedPointerAccessWithA(addressExpr: PtExpression, operator: String, write: Boolean): Boolean {
|
||||
internal fun tryOptimizedPointerAccessWithA(addressExpr: PtBinaryExpression, write: Boolean): Boolean {
|
||||
// optimize pointer,indexregister if possible
|
||||
|
||||
fun evalBytevalueWillClobberA(expr: PtExpression): Boolean {
|
||||
@@ -1156,7 +1208,7 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
if(operator=="+") {
|
||||
if(addressExpr.operator=="+") {
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr)
|
||||
if(ptrAndIndex!=null) {
|
||||
val pointervar = ptrAndIndex.first as? PtIdentifier
|
||||
@@ -1245,7 +1297,6 @@ $repeatLabel""")
|
||||
val rightConstVal = right as? PtNumber
|
||||
|
||||
val label = when {
|
||||
jump.generatedLabel!=null -> jump.generatedLabel!!
|
||||
jump.identifier!=null -> asmSymbolName(jump.identifier!!)
|
||||
jump.address!=null -> jump.address!!.toHex()
|
||||
else -> throw AssemblyError("weird jump")
|
||||
@@ -2868,7 +2919,7 @@ $repeatLabel""")
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val addrExpr = expr.address as PtBinaryExpression
|
||||
if(!tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||
if(!tryOptimizedPointerAccessWithA(addrExpr, false)) {
|
||||
assignViaExprEval()
|
||||
}
|
||||
}
|
||||
@@ -3029,30 +3080,16 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun popCpuStack(dt: DataType, target: IPtVariable, scope: IPtSubroutine?) {
|
||||
// note: because A is pushed first so popped last, saving A is often not required here.
|
||||
val targetAsmSub = (target as PtNode).definingAsmSub()
|
||||
if(targetAsmSub != null) {
|
||||
val parameter = targetAsmSub.parameters.first { it.second.name==target.name }
|
||||
popCpuStack(targetAsmSub, parameter.second, parameter.first)
|
||||
return
|
||||
}
|
||||
val scopedName = when(target) {
|
||||
is PtConstant -> target.scopedName
|
||||
is PtMemMapped -> target.scopedName
|
||||
is PtVariable -> target.scopedName
|
||||
else -> throw AssemblyError("weird target var")
|
||||
}
|
||||
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, this, target.type, scope, target.position, variableAsmName = asmVariableName(scopedName))
|
||||
internal fun popCpuStack(dt: DataType) {
|
||||
if (dt in ByteDatatypes) {
|
||||
out(" pla")
|
||||
assignRegister(RegisterOrPair.A, tgt)
|
||||
} else {
|
||||
} else if (dt in WordDatatypes) {
|
||||
if (isTargetCpu(CpuType.CPU65c02))
|
||||
out(" ply | pla")
|
||||
else
|
||||
out(" pla | tay | pla")
|
||||
assignRegister(RegisterOrPair.AY, tgt)
|
||||
} else {
|
||||
throw AssemblyError("can't pop type $dt")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3104,7 +3141,7 @@ $repeatLabel""")
|
||||
|
||||
internal fun makeLabel(postfix: String): String {
|
||||
generatedLabelSequenceNumber++
|
||||
return "prog8_label_asm_${generatedLabelSequenceNumber}_$postfix"
|
||||
return "label_asm_${generatedLabelSequenceNumber}_$postfix"
|
||||
}
|
||||
|
||||
fun assignConstFloatToPointerAY(number: PtNumber) {
|
||||
|
||||
@@ -381,15 +381,9 @@ private fun optimizeStoreLoadSame(
|
||||
} else if(first=="phx" && second=="pla") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " txa"))
|
||||
} else if(first=="phx" && second=="ply") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " txy"))
|
||||
} else if(first=="phy" && second=="pla") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " tya"))
|
||||
} else if(first=="phy" && second=="plx") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " tyx"))
|
||||
}
|
||||
}
|
||||
return mods
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
@@ -62,28 +63,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
"poke" -> throw AssemblyError("poke() should have been replaced by @()")
|
||||
"push" -> asmgen.pushCpuStack(DataType.UBYTE, fcall.args[0])
|
||||
"pushw" -> asmgen.pushCpuStack(DataType.UWORD, fcall.args[0])
|
||||
"pop" -> {
|
||||
require(fcall.args[0] is PtIdentifier) {
|
||||
"attempt to pop a value into a differently typed variable, or in something else that isn't supported ${fcall.position}"
|
||||
}
|
||||
val symbol = asmgen.symbolTable.lookup((fcall.args[0] as PtIdentifier).name)
|
||||
val target = symbol!!.astNode as IPtVariable
|
||||
asmgen.popCpuStack(DataType.UBYTE, target, fcall.definingISub())
|
||||
}
|
||||
"popw" -> {
|
||||
require(fcall.args[0] is PtIdentifier) {
|
||||
"attempt to pop a value into a differently typed variable, or in something else that isn't supported ${fcall.position}"
|
||||
}
|
||||
val symbol = asmgen.symbolTable.lookup((fcall.args[0] as PtIdentifier).name)
|
||||
val target = symbol!!.astNode as IPtVariable
|
||||
asmgen.popCpuStack(DataType.UWORD, target, fcall.definingISub())
|
||||
}
|
||||
"rsave" -> funcRsave()
|
||||
"rrestore" -> funcRrestore()
|
||||
"cmp" -> funcCmp(fcall)
|
||||
"callfar" -> funcCallFar(fcall, resultRegister)
|
||||
"call" -> funcCall(fcall)
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
|
||||
"prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE, resultRegister)
|
||||
"prog8_lib_square_word" -> funcSquare(fcall, DataType.UWORD, resultRegister)
|
||||
@@ -193,6 +177,21 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
plp""")
|
||||
}
|
||||
|
||||
private fun funcCall(fcall: PtBuiltinFunctionCall) {
|
||||
val constAddr = fcall.args[0].asConstInteger()
|
||||
if(constAddr!=null) {
|
||||
asmgen.out(" jsr ${constAddr.toHex()}")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY) // jump address
|
||||
asmgen.out("""
|
||||
sta (+)+1
|
||||
sty (+)+2
|
||||
+ jsr 0 ; modified""")
|
||||
}
|
||||
|
||||
// note: the routine can return a word value (in AY)
|
||||
}
|
||||
|
||||
private fun funcCallFar(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
if(asmgen.options.compTarget.name != "cx16")
|
||||
throw AssemblyError("callfar only works on cx16 target at this time")
|
||||
@@ -311,106 +310,112 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun funcReverse(fcall: PtBuiltinFunctionCall) {
|
||||
val variable = fcall.args.single()
|
||||
if (variable is PtIdentifier) {
|
||||
val symbol = asmgen.symbolTable.lookup(variable.name)
|
||||
val decl = symbol!!.astNode as IPtVariable
|
||||
val numElements = when(decl) {
|
||||
is PtConstant -> throw AssemblyError("cannot reverse a constant")
|
||||
is PtMemMapped -> decl.arraySize
|
||||
is PtVariable -> decl.arraySize
|
||||
val variable = fcall.args.single() as PtIdentifier
|
||||
val symbol = asmgen.symbolTable.lookup(variable.name)
|
||||
val (dt, numElements) = when(symbol) {
|
||||
is StStaticVariable -> symbol.dt to symbol.length!!
|
||||
is StMemVar -> symbol.dt to symbol.length!!
|
||||
else -> DataType.UNDEFINED to 0
|
||||
}
|
||||
val varName = asmgen.asmVariableName(variable)
|
||||
when (dt) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr prog8_lib.func_reverse_b""")
|
||||
}
|
||||
val varName = asmgen.asmVariableName(variable)
|
||||
when (decl.type) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr prog8_lib.func_reverse_b""")
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr prog8_lib.func_reverse_w""")
|
||||
}
|
||||
DataType.STR -> {
|
||||
val stringLength = (symbol as StStaticVariable).length!!-1
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$stringLength
|
||||
jsr prog8_lib.func_reverse_b""")
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr floats.func_reverse_f""")
|
||||
}
|
||||
in SplitWordArrayTypes -> TODO("split word reverse")
|
||||
else -> throw AssemblyError("weird type")
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr prog8_lib.func_reverse_w""")
|
||||
}
|
||||
DataType.STR -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #${numElements-1}
|
||||
jsr prog8_lib.func_reverse_b""")
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr floats.func_reverse_f""")
|
||||
}
|
||||
in SplitWordArrayTypes -> {
|
||||
// reverse the lsb and msb arrays both, independently
|
||||
asmgen.out("""
|
||||
lda #<${varName}_lsb
|
||||
ldy #>${varName}_lsb
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr prog8_lib.func_reverse_b
|
||||
lda #<${varName}_msb
|
||||
ldy #>${varName}_msb
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr prog8_lib.func_reverse_b""")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcSort(fcall: PtBuiltinFunctionCall) {
|
||||
val variable = fcall.args.single()
|
||||
if (variable is PtIdentifier) {
|
||||
val symbol = asmgen.symbolTable.lookup(variable.name)
|
||||
val decl = symbol!!.astNode as IPtVariable
|
||||
val varName = asmgen.asmVariableName(variable)
|
||||
val numElements = when(decl) {
|
||||
is PtConstant -> throw AssemblyError("cannot sort a constant")
|
||||
is PtMemMapped -> decl.arraySize
|
||||
is PtVariable -> decl.arraySize
|
||||
val variable = fcall.args.single() as PtIdentifier
|
||||
val symbol = asmgen.symbolTable.lookup(variable.name)
|
||||
val varName = asmgen.asmVariableName(variable)
|
||||
val (dt, numElements) = when(symbol) {
|
||||
is StStaticVariable -> symbol.dt to symbol.length!!
|
||||
is StMemVar -> symbol.dt to symbol.length!!
|
||||
else -> DataType.UNDEFINED to 0
|
||||
}
|
||||
when (dt) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements""")
|
||||
asmgen.out(if (dt == DataType.ARRAY_UB) " jsr prog8_lib.func_sort_ub" else " jsr prog8_lib.func_sort_b")
|
||||
}
|
||||
when (decl.type) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements""")
|
||||
asmgen.out(if (decl.type == DataType.ARRAY_UB) " jsr prog8_lib.func_sort_ub" else " jsr prog8_lib.func_sort_b")
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements""")
|
||||
asmgen.out(if (decl.type == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w")
|
||||
}
|
||||
DataType.STR -> {
|
||||
val stringLength = (symbol as StStaticVariable).length!!-1
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$stringLength
|
||||
jsr prog8_lib.func_sort_ub""")
|
||||
}
|
||||
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
|
||||
in SplitWordArrayTypes -> TODO("split word sort")
|
||||
else -> throw AssemblyError("weird type")
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements""")
|
||||
asmgen.out(if (dt == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w")
|
||||
}
|
||||
} else
|
||||
throw AssemblyError("weird type")
|
||||
DataType.STR -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #${numElements-1}
|
||||
jsr prog8_lib.func_sort_ub""")
|
||||
}
|
||||
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
|
||||
in SplitWordArrayTypes -> TODO("split words sort")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcRor2(fcall: PtBuiltinFunctionCall) {
|
||||
@@ -419,8 +424,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
DataType.UBYTE -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
translateRolRorArrayArgs(what.variable, what, "ror2", 'b')
|
||||
asmgen.out(" jsr prog8_lib.ror2_array_ub")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
asmgen.out(" lda ${varname},x | lsr a | bcc + | ora #\$80 |+ | sta ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
@@ -441,10 +447,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
DataType.UWORD -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.splitWords)
|
||||
TODO("ror2 split words ${what.position}")
|
||||
translateRolRorArrayArgs(what.variable, what, "ror2", 'w')
|
||||
asmgen.out(" jsr prog8_lib.ror2_array_uw")
|
||||
asmgen.out(" lsr ${varname}_msb,x | ror ${varname}_lsb,x | bcc + | lda ${varname}_msb,x | ora #\$80 | sta ${varname}_msb,x |+ ")
|
||||
else
|
||||
asmgen.out(" lsr ${varname}+1,x | ror ${varname},x | bcc + | lda ${varname}+1,x | ora #\$80 | sta ${varname}+1,x |+ ")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
@@ -463,8 +471,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
DataType.UBYTE -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
translateRolRorArrayArgs(what.variable, what, "ror", 'b')
|
||||
asmgen.out(" jsr prog8_lib.ror_array_ub")
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
asmgen.out(" ror ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
@@ -473,15 +484,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
||||
if(ptrAndIndex!=null) {
|
||||
asmgen.out(" php")
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
||||
asmgen.out("""
|
||||
plp
|
||||
+ ror ${'$'}ffff,x ; modified""")
|
||||
} else {
|
||||
if(!what.address.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||
if(!what.address.isSimple()) asmgen.out(" plp")
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
@@ -499,10 +514,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
DataType.UWORD -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.splitWords)
|
||||
TODO("ror split words ${what.position}")
|
||||
translateRolRorArrayArgs(what.variable, what, "ror", 'w')
|
||||
asmgen.out(" jsr prog8_lib.ror_array_uw")
|
||||
asmgen.out(" ror ${varname}_msb,x | ror ${varname}_lsb,x")
|
||||
else
|
||||
asmgen.out(" ror ${varname}+1,x | ror ${varname},x")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
@@ -521,8 +540,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
DataType.UBYTE -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
translateRolRorArrayArgs(what.variable, what, "rol2", 'b')
|
||||
asmgen.out(" jsr prog8_lib.rol2_array_ub")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
asmgen.out(" lda ${varname},x | cmp #\$80 | rol a | sta ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
@@ -543,10 +563,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
DataType.UWORD -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.splitWords)
|
||||
TODO("rol2 split words ${what.position}")
|
||||
translateRolRorArrayArgs(what.variable, what, "rol2", 'w')
|
||||
asmgen.out(" jsr prog8_lib.rol2_array_uw")
|
||||
asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb |+")
|
||||
else
|
||||
asmgen.out(" asl ${varname},x | rol ${varname}+1,x | bcc + | inc ${varname},x |+ ")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
@@ -565,8 +587,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
DataType.UBYTE -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
translateRolRorArrayArgs(what.variable, what, "rol", 'b')
|
||||
asmgen.out(" jsr prog8_lib.rol_array_ub")
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
asmgen.out(" rol ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
@@ -575,15 +600,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
||||
if(ptrAndIndex!=null) {
|
||||
asmgen.out(" php")
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
||||
asmgen.out("""
|
||||
plp
|
||||
+ rol ${'$'}ffff,x ; modified""")
|
||||
} else {
|
||||
if(!what.address.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||
if(!what.address.isSimple()) asmgen.out(" plp")
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
@@ -601,10 +630,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
DataType.UWORD -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.splitWords)
|
||||
TODO("rol split words ${what.position}")
|
||||
translateRolRorArrayArgs(what.variable, what, "rol", 'w')
|
||||
asmgen.out(" jsr prog8_lib.rol_array_uw")
|
||||
asmgen.out(" rol ${varname}_lsb,x | rol ${varname}_msb,x")
|
||||
else
|
||||
asmgen.out(" rol ${varname},x | rol ${varname}+1,x")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
@@ -617,23 +650,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateRolRorArrayArgs(arrayvar: PtIdentifier, indexer: PtArrayIndexer, operation: String, dt: Char) {
|
||||
if(indexer.splitWords)
|
||||
TODO("rol/ror split words access ${indexer.position}")
|
||||
if(arrayvar.type==DataType.UWORD) {
|
||||
if(dt!='b')
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD)
|
||||
} else {
|
||||
val p = arrayvar.parent
|
||||
val addressOf = PtAddressOf(arrayvar.position)
|
||||
addressOf.add(arrayvar)
|
||||
addressOf.parent = p
|
||||
asmgen.assignExpressionToVariable(addressOf, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD)
|
||||
}
|
||||
asmgen.assignExpressionToVariable(indexer.index, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE)
|
||||
}
|
||||
|
||||
private fun funcSetLsbMsb(fcall: PtBuiltinFunctionCall, msb: Boolean) {
|
||||
val target: AsmAssignTarget
|
||||
when(fcall.args[0]) {
|
||||
@@ -661,7 +677,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
val indexer = fcall.args[0] as PtArrayIndexer
|
||||
require(!indexer.usesPointerVariable)
|
||||
val elementSize: Int
|
||||
val msbAdd: Int
|
||||
if(indexer.splitWords) {
|
||||
@@ -728,13 +743,57 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun funcAnyAll(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
outputAddressAndLenghtOfArray(fcall.args[0])
|
||||
val dt = fcall.args.single().type
|
||||
val array = fcall.args[0] as PtIdentifier
|
||||
when (dt) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A | ldy #0")
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A | ldy #0")
|
||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
|
||||
in SplitWordArrayTypes -> TODO("split word any/all")
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> {
|
||||
outputAddressAndLengthOfArray(array)
|
||||
asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A")
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
outputAddressAndLengthOfArray(array)
|
||||
asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A")
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
outputAddressAndLengthOfArray(array)
|
||||
asmgen.out(" jsr floats.func_${fcall.name}_f_into_A")
|
||||
}
|
||||
in SplitWordArrayTypes -> {
|
||||
val numElements = (asmgen.symbolTable.lookup(array.name) as StStaticVariable).length
|
||||
when(fcall.name) {
|
||||
"any" -> {
|
||||
// any(lsb-array) or any(msb-array)
|
||||
val arrayName = asmgen.asmVariableName(array)
|
||||
asmgen.out("""
|
||||
lda #<${arrayName}_lsb
|
||||
ldy #>${arrayName}_lsb
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
""")
|
||||
asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A")
|
||||
asmgen.out(" bne +") // shortcircuit
|
||||
asmgen.out("""
|
||||
pha
|
||||
lda #<${arrayName}_msb
|
||||
ldy #>${arrayName}_msb
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
""")
|
||||
asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A")
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
pla
|
||||
ora P8ZP_SCRATCH_REG
|
||||
+""")
|
||||
}
|
||||
"all" -> {
|
||||
TODO("split words all")
|
||||
}
|
||||
else -> throw AssemblyError("weird call")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes, true)
|
||||
@@ -786,8 +845,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
else -> {
|
||||
val tempvar = asmgen.getTempVarName(DataType.FLOAT)
|
||||
asmgen.assignExpressionTo(fcall.args[1],
|
||||
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position, tempvar, null, null, null, null))
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.FLOAT)
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
pha
|
||||
@@ -1187,15 +1245,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable)+"_lsb" else asmgen.asmVariableName(arg.variable)
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, arg.type, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVar,y")
|
||||
}
|
||||
RegisterOrPair.Y -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, arg.type, CpuRegister.X)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.X)
|
||||
asmgen.out(" lda $arrayVar,x")
|
||||
}
|
||||
RegisterOrPair.X -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, arg.type, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
asmgen.out(" ldx $arrayVar,y")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
@@ -1241,16 +1299,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun outputAddressAndLenghtOfArray(arg: PtExpression) {
|
||||
// address in P8ZP_SCRATCH_W1, number of elements in A
|
||||
arg as PtIdentifier
|
||||
private fun outputAddressAndLengthOfArray(arg: PtIdentifier) {
|
||||
// address goes in P8ZP_SCRATCH_W1, number of elements in A
|
||||
val symbol = asmgen.symbolTable.lookup(arg.name)
|
||||
val arrayVar = symbol!!.astNode as IPtVariable
|
||||
val numElements = when(arrayVar) {
|
||||
is PtConstant -> null
|
||||
is PtMemMapped -> arrayVar.arraySize
|
||||
is PtVariable -> arrayVar.arraySize
|
||||
} ?: throw AssemblyError("length of non-array requested")
|
||||
val numElements = when(symbol) {
|
||||
is StStaticVariable -> symbol.length!!
|
||||
is StMemVar -> symbol.length!!
|
||||
else -> 0
|
||||
}
|
||||
val identifierName = asmgen.asmVariableName(arg)
|
||||
asmgen.out("""
|
||||
lda #<$identifierName
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import com.github.michaelbull.result.fold
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.ast.PtForLoop
|
||||
import prog8.code.ast.PtIdentifier
|
||||
import prog8.code.ast.PtRange
|
||||
import prog8.code.core.*
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
@@ -332,11 +336,10 @@ $endLabel""")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val iterableName = asmgen.asmVariableName(ident)
|
||||
val symbol = asmgen.symbolTable.lookup(ident.name)
|
||||
val decl = symbol!!.astNode as IPtVariable
|
||||
val numElements = when(decl) {
|
||||
is PtConstant -> throw AssemblyError("length of non-array requested")
|
||||
is PtMemMapped -> decl.arraySize
|
||||
is PtVariable -> decl.arraySize
|
||||
val numElements = when(symbol) {
|
||||
is StStaticVariable -> symbol.length!!
|
||||
is StMemVar -> symbol.length!!
|
||||
else -> 0
|
||||
}
|
||||
when(iterableDt) {
|
||||
DataType.STR -> {
|
||||
@@ -364,7 +367,7 @@ $loopLabel sty $indexVar
|
||||
lda $iterableName,y
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements!!<=255u) {
|
||||
if(numElements<=255) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@@ -379,7 +382,7 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(numElements>=16u) {
|
||||
if(numElements>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
@@ -392,7 +395,7 @@ $loopLabel sty $indexVar
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||
val length = numElements!! * 2u
|
||||
val length = numElements * 2
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
@@ -403,7 +406,7 @@ $loopLabel sty $indexVar
|
||||
lda $iterableName+1,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(length<=127u) {
|
||||
if(length<=127) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@@ -420,7 +423,7 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(length>=16u) {
|
||||
if(length>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
@@ -433,7 +436,6 @@ $loopLabel sty $indexVar
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT -> {
|
||||
numElements!!
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
@@ -444,7 +446,7 @@ $loopLabel sty $indexVar
|
||||
lda ${iterableName}_msb,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255u) {
|
||||
if(numElements<=255) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@@ -459,7 +461,7 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(numElements>=16u) {
|
||||
if(numElements>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
|
||||
@@ -26,7 +26,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
|
||||
|
||||
val symbol = asmgen.symbolTable.lookup(call.name)
|
||||
val sub = symbol!!.astNode as IPtSubroutine
|
||||
val sub = symbol?.astNode as IPtSubroutine
|
||||
val subAsmName = asmgen.asmSymbolName(call.name)
|
||||
|
||||
if(sub is PtAsmSub) {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.ast.PtIdentifier
|
||||
import prog8.code.ast.PtNumber
|
||||
import prog8.code.ast.PtPostIncrDecr
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
@@ -37,6 +34,55 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
}
|
||||
targetMemory!=null -> {
|
||||
fun incDecViaExprEval() {
|
||||
asmgen.assignExpressionToRegister(targetMemory.address, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
}
|
||||
|
||||
fun tryOptimizedPointerIncDec(address: PtBinaryExpression): Boolean {
|
||||
if(address.operator=="+") {
|
||||
val offset = address.right.asConstInteger()
|
||||
if(offset!=null && offset<256) {
|
||||
// we have @(ptr + 255) ++ , or @(ptr+255)--
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
ldx #$offset""")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff,x\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff,x\t; modified")
|
||||
return true
|
||||
} else if(address.right.type in ByteDatatypes) {
|
||||
// we have @(ptr + bytevar) ++ , or @(ptr+bytevar)--
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.X, false)
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff,x\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff,x\t; modified")
|
||||
return true
|
||||
} else if((address.right as? PtTypeCast)?.value?.type in ByteDatatypes) {
|
||||
// we have @(ptr + bytevar) ++ , or @(ptr+bytevar)--
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.assignExpressionToRegister((address.right as PtTypeCast).value, RegisterOrPair.X, false)
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff,x\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff,x\t; modified")
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
when (val addressExpr = targetMemory.address) {
|
||||
is PtNumber -> {
|
||||
val what = addressExpr.number.toHex()
|
||||
@@ -50,13 +96,13 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
if(!tryOptimizedPointerIncDec(addressExpr)) {
|
||||
incDecViaExprEval()
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
asmgen.assignExpressionToRegister(addressExpr, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
incDecViaExprEval()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,7 +121,7 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
dec ${asmArrayvarname}_msb+$constIndex
|
||||
+ dec ${asmArrayvarname}_lsb+$constIndex""")
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, CpuRegister.X)
|
||||
if(incr)
|
||||
asmgen.out(" inc ${asmArrayvarname}_lsb,x | bne + | inc ${asmArrayvarname}_msb,x |+")
|
||||
else
|
||||
@@ -91,22 +137,7 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
if(targetArrayIdx.usesPointerVariable) {
|
||||
asmgen.out("""
|
||||
lda $asmArrayvarname
|
||||
clc
|
||||
adc #$indexValue
|
||||
sta (+) +1
|
||||
lda $asmArrayvarname+1
|
||||
adc #0
|
||||
sta (+) +2""")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
} else {
|
||||
asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
||||
}
|
||||
asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
@@ -127,25 +158,10 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
else
|
||||
{
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, CpuRegister.X)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
if(targetArrayIdx.usesPointerVariable) {
|
||||
asmgen.out("""
|
||||
txa
|
||||
clc
|
||||
adc $asmArrayvarname
|
||||
sta (+) +1
|
||||
lda $asmArrayvarname+1
|
||||
adc #0
|
||||
sta (+) +2""")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
} else {
|
||||
asmgen.out(if (incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
|
||||
}
|
||||
asmgen.out(if (incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
|
||||
@@ -34,8 +34,8 @@ internal class ProgramAndVarsGen(
|
||||
header()
|
||||
val allBlocks = program.allBlocks()
|
||||
|
||||
if(allBlocks.first().name != "p8_main" && allBlocks.first().name != "main")
|
||||
throw AssemblyError("first block should be 'main' or 'p8_main'")
|
||||
if(allBlocks.first().name != "p8b_main" && allBlocks.first().name != "main")
|
||||
throw AssemblyError("first block should be 'main' or 'p8b_main'")
|
||||
|
||||
if(errors.noErrors()) {
|
||||
program.allBlocks().forEach { block2asm(it) }
|
||||
@@ -49,6 +49,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
|
||||
memorySlabs()
|
||||
tempVars()
|
||||
footer()
|
||||
}
|
||||
}
|
||||
@@ -133,29 +134,22 @@ internal class ProgramAndVarsGen(
|
||||
pha""")
|
||||
}
|
||||
|
||||
// make sure that on the cx16 and c64, basic rom is banked in again when we exit the program
|
||||
when(compTarget.name) {
|
||||
"cx16" -> {
|
||||
if(options.floats)
|
||||
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
|
||||
asmgen.out(" jsr p8_main.p8_start")
|
||||
asmgen.out(" jsr p8b_main.p8s_start")
|
||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||
}
|
||||
"c64" -> {
|
||||
asmgen.out(" jsr p8_main.p8_start | lda #31 | sta $01")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||
else
|
||||
asmgen.out(" rts")
|
||||
asmgen.out(" jsr p8b_main.p8s_start | lda #31 | sta $01")
|
||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||
}
|
||||
"c128" -> {
|
||||
asmgen.out(" jsr p8_main.p8_start | lda #0 | sta ${"$"}ff00")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||
else
|
||||
asmgen.out(" rts")
|
||||
asmgen.out(" jsr p8b_main.p8s_start | lda #0 | sta ${"$"}ff00")
|
||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||
}
|
||||
else -> asmgen.jmp("p8_main.p8_start")
|
||||
else -> asmgen.jmp("p8b_main.p8s_start")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,48 +166,119 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun tempVars() {
|
||||
asmgen.out("; expression temp vars\n .section BSS")
|
||||
for((dt, count) in asmgen.tempVarsCounters) {
|
||||
if(count>0) {
|
||||
for(num in 1..count) {
|
||||
val name = asmgen.buildTempVarName(dt, num)
|
||||
when (dt) {
|
||||
DataType.BYTE -> asmgen.out("$name .char ?")
|
||||
DataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||
DataType.WORD -> asmgen.out("$name .sint ?")
|
||||
DataType.UWORD -> asmgen.out("$name .word ?")
|
||||
DataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
asmgen.out(" .send BSS")
|
||||
}
|
||||
|
||||
private fun footer() {
|
||||
var relocateBssVars = false
|
||||
var relocateBssSlabs = false
|
||||
var relocatedBssStart = 0u
|
||||
var relocatedBssEnd = 0u
|
||||
|
||||
if(options.varsGolden) {
|
||||
if(options.compTarget.machine.BSSGOLDENRAM_START == 0u ||
|
||||
options.compTarget.machine.BSSGOLDENRAM_END == 0u ||
|
||||
options.compTarget.machine.BSSGOLDENRAM_END <= options.compTarget.machine.BSSGOLDENRAM_START) {
|
||||
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
|
||||
}
|
||||
relocateBssVars = true
|
||||
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START
|
||||
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END
|
||||
}
|
||||
else if(options.varsHighBank!=null) {
|
||||
if(options.compTarget.machine.BSSHIGHRAM_START == 0u ||
|
||||
options.compTarget.machine.BSSHIGHRAM_END == 0u ||
|
||||
options.compTarget.machine.BSSHIGHRAM_END <= options.compTarget.machine.BSSHIGHRAM_START) {
|
||||
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
|
||||
}
|
||||
if(options.slabsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
|
||||
throw AssemblyError("slabs and vars high bank must be the same")
|
||||
relocateBssVars = true
|
||||
relocatedBssStart = options.compTarget.machine.BSSHIGHRAM_START
|
||||
relocatedBssEnd = options.compTarget.machine.BSSHIGHRAM_END
|
||||
}
|
||||
|
||||
if(options.slabsGolden) {
|
||||
if(options.compTarget.machine.BSSGOLDENRAM_START == 0u ||
|
||||
options.compTarget.machine.BSSGOLDENRAM_END == 0u ||
|
||||
options.compTarget.machine.BSSGOLDENRAM_END <= options.compTarget.machine.BSSGOLDENRAM_START) {
|
||||
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
|
||||
}
|
||||
relocateBssSlabs = true
|
||||
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START
|
||||
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END
|
||||
}
|
||||
else if(options.slabsHighBank!=null) {
|
||||
if(options.compTarget.machine.BSSHIGHRAM_START == 0u ||
|
||||
options.compTarget.machine.BSSHIGHRAM_END == 0u ||
|
||||
options.compTarget.machine.BSSHIGHRAM_END <= options.compTarget.machine.BSSHIGHRAM_START) {
|
||||
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
|
||||
}
|
||||
if(options.varsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
|
||||
throw AssemblyError("slabs and vars high bank must be the same")
|
||||
relocateBssSlabs = true
|
||||
relocatedBssStart = options.compTarget.machine.BSSHIGHRAM_START
|
||||
relocatedBssEnd = options.compTarget.machine.BSSHIGHRAM_END
|
||||
}
|
||||
|
||||
asmgen.out("; bss sections")
|
||||
asmgen.out("PROG8_VARSHIGH_RAMBANK = ${options.varsHighBank ?: 1}")
|
||||
if(options.varsHighBank!=null) {
|
||||
if(options.compTarget.machine.BSSHIGHRAM_START == 0u || options.compTarget.machine.BSSHIGHRAM_END==0u) {
|
||||
throw AssemblyError("current compilation target hasn't got the high ram area properly defined")
|
||||
}
|
||||
// BSS vars in high ram area, memory() slabs just concatenated at the end of the program.
|
||||
if(symboltable.allMemorySlabs.isNotEmpty()) {
|
||||
if(relocateBssVars) {
|
||||
if(!relocateBssSlabs)
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
}
|
||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||
asmgen.out(" * = ${options.compTarget.machine.BSSHIGHRAM_START.toHex()}")
|
||||
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
||||
asmgen.out("prog8_bss_section_start")
|
||||
asmgen.out(" .dsection BSS")
|
||||
asmgen.out(" .cerror * >= ${options.compTarget.machine.BSSHIGHRAM_END.toHex()}, \"too many variables for BSS section\"")
|
||||
if(relocateBssSlabs)
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
|
||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||
} else {
|
||||
// BSS vars followed by memory() slabs, concatenated at the end of the program.
|
||||
asmgen.out("prog8_bss_section_start")
|
||||
asmgen.out(" .dsection BSS")
|
||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||
if(symboltable.allMemorySlabs.isNotEmpty()) {
|
||||
if(!relocateBssSlabs)
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
}
|
||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||
if(relocateBssSlabs) {
|
||||
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun block2asm(block: PtBlock) {
|
||||
asmgen.out("")
|
||||
asmgen.out("; ---- block: '${block.name}' ----")
|
||||
if(block.address!=null)
|
||||
asmgen.out("* = ${block.address!!.toHex()}")
|
||||
if(block.options.address!=null)
|
||||
asmgen.out("* = ${block.options.address!!.toHex()}")
|
||||
else {
|
||||
if(block.alignment==PtBlock.BlockAlignment.WORD)
|
||||
if(block.options.alignment==PtBlock.BlockAlignment.WORD)
|
||||
asmgen.out("\t.align 2")
|
||||
else if(block.alignment==PtBlock.BlockAlignment.PAGE)
|
||||
else if(block.options.alignment==PtBlock.BlockAlignment.PAGE)
|
||||
asmgen.out("\t.align $100")
|
||||
}
|
||||
|
||||
asmgen.out("${block.name}\t" + (if(block.forceOutput) ".block" else ".proc"))
|
||||
asmgen.out("${block.name}\t" + (if(block.options.forceOutput) ".block" else ".proc"))
|
||||
asmgen.outputSourceLine(block)
|
||||
|
||||
createBlockVariables(block)
|
||||
@@ -236,7 +301,7 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out(" rts\n .bend")
|
||||
}
|
||||
|
||||
asmgen.out(if(block.forceOutput) "\n\t.bend" else "\n\t.pend")
|
||||
asmgen.out(if(block.options.forceOutput) "\n\t.bend" else "\n\t.pend")
|
||||
}
|
||||
|
||||
private fun getVars(scope: StNode): Map<String, StNode> =
|
||||
@@ -276,7 +341,7 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
val asmStartScope: String
|
||||
val asmEndScope: String
|
||||
if(sub.definingBlock()!!.forceOutput) {
|
||||
if(sub.definingBlock()!!.options.forceOutput) {
|
||||
asmStartScope = ".block"
|
||||
asmEndScope = ".bend"
|
||||
} else {
|
||||
@@ -299,7 +364,7 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
val asmStartScope: String
|
||||
val asmEndScope: String
|
||||
if(sub.definingBlock()!!.forceOutput) {
|
||||
if(sub.definingBlock()!!.options.forceOutput) {
|
||||
asmStartScope = ".block"
|
||||
asmEndScope = ".bend"
|
||||
} else {
|
||||
@@ -331,7 +396,7 @@ internal class ProgramAndVarsGen(
|
||||
asmsubs2asm(sub.children)
|
||||
|
||||
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
|
||||
if((sub.name=="start" || sub.name=="p8_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8_main"))
|
||||
if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main"))
|
||||
entrypointInitialization()
|
||||
|
||||
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
|
||||
|
||||
@@ -29,7 +29,7 @@ internal class AnyExprAsmGen(
|
||||
require(expr.operator in ComparisonOperators)
|
||||
return assignFloatBinExpr(expr, assign)
|
||||
}
|
||||
throw AssemblyError("weird expr operand types: ${expr.left.type} and {${expr.right.type}")
|
||||
throw AssemblyError("weird expr operand types: ${expr.left.type} and ${expr.right.type}")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
require(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
||||
@@ -49,54 +49,24 @@ internal class AnyExprAsmGen(
|
||||
|
||||
private fun assignWordBinExpr(expr: PtBinaryExpression): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
TODO("word + at ${expr.position}")
|
||||
}
|
||||
"-" -> {
|
||||
TODO("word - at ${expr.position}")
|
||||
}
|
||||
"*" -> {
|
||||
TODO("word * at ${expr.position}")
|
||||
}
|
||||
"/" -> {
|
||||
TODO("word / at ${expr.position}")
|
||||
}
|
||||
"<<" -> {
|
||||
TODO("word << at ${expr.position}")
|
||||
}
|
||||
">>" -> {
|
||||
TODO("word >> at ${expr.position}")
|
||||
}
|
||||
"%" -> {
|
||||
TODO("word % at ${expr.position}")
|
||||
}
|
||||
"&", "and" -> {
|
||||
TODO("word and at ${expr.position}")
|
||||
}
|
||||
"|", "or" -> {
|
||||
TODO("word or at ${expr.position}")
|
||||
}
|
||||
"^", "xor" -> {
|
||||
TODO("word xor at ${expr.position}")
|
||||
}
|
||||
"==" -> {
|
||||
TODO("word == at ${expr.position}")
|
||||
}
|
||||
"!=" -> {
|
||||
TODO("word != at ${expr.position}")
|
||||
}
|
||||
"<" -> {
|
||||
TODO("word < at ${expr.position}")
|
||||
}
|
||||
"<=" -> {
|
||||
TODO("word <= at ${expr.position}")
|
||||
}
|
||||
">" -> {
|
||||
TODO("word > at ${expr.position}")
|
||||
}
|
||||
">=" -> {
|
||||
TODO("word >= at ${expr.position}")
|
||||
}
|
||||
"+" -> TODO("word + at ${expr.position}")
|
||||
"-" -> TODO("word - at ${expr.position}")
|
||||
"*" -> TODO("word * at ${expr.position}")
|
||||
"/" -> TODO("word / at ${expr.position}")
|
||||
"<<" -> TODO("word << at ${expr.position}")
|
||||
">>" -> TODO("word >> at ${expr.position}")
|
||||
"%" -> TODO("word % at ${expr.position}")
|
||||
"and" -> TODO("word logical and (with optional shortcircuit) ${expr.position}")
|
||||
"or" -> TODO("word logical or (with optional shortcircuit) ${expr.position}")
|
||||
"&" -> TODO("word and at ${expr.position}")
|
||||
"|" -> TODO("word or at ${expr.position}")
|
||||
"^", "xor" -> TODO("word xor at ${expr.position}")
|
||||
"==" -> TODO("word == at ${expr.position}")
|
||||
"!=" -> TODO("word != at ${expr.position}")
|
||||
"<" -> TODO("word < at ${expr.position}")
|
||||
"<=" -> TODO("word <= at ${expr.position}")
|
||||
">" -> TODO("word > at ${expr.position}")
|
||||
">=" -> TODO("word >= at ${expr.position}")
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
@@ -134,7 +104,9 @@ internal class AnyExprAsmGen(
|
||||
"%" -> {
|
||||
TODO("byte % at ${expr.position}")
|
||||
}
|
||||
"&", "and" -> {
|
||||
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
|
||||
"or" -> TODO("logical or (with optional shortcircuit) ${expr.position}")
|
||||
"&" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
@@ -142,7 +114,7 @@ internal class AnyExprAsmGen(
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"|", "or" -> {
|
||||
"|" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package prog8.codegen.cpu6502.assignment
|
||||
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
@@ -62,32 +64,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
val value = assign.source.array!!
|
||||
val elementDt = assign.source.datatype
|
||||
val arrayVarName = asmgen.asmVariableName(value.variable)
|
||||
|
||||
if(value.usesPointerVariable) {
|
||||
if(elementDt !in ByteDatatypes)
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
if(value.type != DataType.UBYTE)
|
||||
throw AssemblyError("non-array var indexing requires bytes index")
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02) && (value.index as? PtNumber)?.number==0.0) {
|
||||
if (asmgen.isZpVar(value.variable)) {
|
||||
asmgen.out(" lda ($arrayVarName)")
|
||||
} else {
|
||||
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
|
||||
asmgen.out(" lda (P8ZP_SCRATCH_W1)")
|
||||
}
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
|
||||
if (asmgen.isZpVar(value.variable)) {
|
||||
asmgen.out(" lda ($arrayVarName),y")
|
||||
} else {
|
||||
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
|
||||
asmgen.out(" lda (P8ZP_SCRATCH_W1),y")
|
||||
}
|
||||
}
|
||||
assignRegisterByte(assign.target, CpuRegister.A, elementDt in SignedDatatypes, false)
|
||||
return
|
||||
}
|
||||
|
||||
val constIndex = value.index.asConstInteger()
|
||||
|
||||
if(value.splitWords) {
|
||||
@@ -96,7 +72,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" lda ${arrayVarName}_lsb+$constIndex | ldy ${arrayVarName}_msb+$constIndex")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
asmgen.out(" lda ${arrayVarName}_lsb,y | ldx ${arrayVarName}_msb,y")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AX)
|
||||
}
|
||||
@@ -125,17 +101,17 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
when (elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVarName,y")
|
||||
assignRegisterByte(assign.target, CpuRegister.A, elementDt in SignedDatatypes, true)
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVarName,y | ldx $arrayVarName+1,y")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AX)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.A)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.A)
|
||||
asmgen.out("""
|
||||
ldy #>$arrayVarName
|
||||
clc
|
||||
@@ -168,7 +144,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val addrExpr = value.address as PtBinaryExpression
|
||||
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, false)) {
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false, true)
|
||||
} else {
|
||||
assignViaExprEval(value.address)
|
||||
@@ -425,7 +401,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("&", "|", "^", "and", "or", "xor") -> optimizedLogicalOrBitwiseExpr(expr, assign.target)
|
||||
in setOf("&", "|", "^", "xor") -> optimizedBitwiseExpr(expr, assign.target)
|
||||
in setOf("and", "or") -> optimizedLogicalAndOrExpr(expr, assign.target)
|
||||
"+", "-" -> optimizedPlusMinExpr(expr, assign.target)
|
||||
"<<", ">>" -> optimizedBitshiftExpr(expr, assign.target)
|
||||
"*" -> optimizedMultiplyExpr(expr, assign.target)
|
||||
@@ -483,13 +460,24 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return false
|
||||
}
|
||||
|
||||
private fun directIntoY(expr: PtExpression): Boolean {
|
||||
return when(expr) {
|
||||
is PtIdentifier -> true
|
||||
is PtMachineRegister -> true
|
||||
is PtNumber -> true
|
||||
is PtBuiltinFunctionCall -> expr.name in arrayOf("lsb", "msb")
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun optimizedRemainderExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
|
||||
when(expr.type) {
|
||||
DataType.UBYTE -> {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
if(!directIntoY(expr.right)) asmgen.out(" pha")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
|
||||
asmgen.out(" pla | jsr math.divmod_ub_asm")
|
||||
if(!directIntoY(expr.right)) asmgen.out(" pla")
|
||||
asmgen.out(" jsr math.divmod_ub_asm")
|
||||
if(target.register==RegisterOrPair.A)
|
||||
asmgen.out(" cmp #0") // fix the status register
|
||||
else
|
||||
@@ -510,17 +498,19 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
when(expr.type) {
|
||||
DataType.UBYTE -> {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
if(!directIntoY(expr.right)) asmgen.out(" pha")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
|
||||
asmgen.out(" pla | jsr math.divmod_ub_asm")
|
||||
if(!directIntoY(expr.right)) asmgen.out(" pla")
|
||||
asmgen.out(" jsr math.divmod_ub_asm")
|
||||
assignRegisterByte(target, CpuRegister.Y, false, true)
|
||||
return true
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, true)
|
||||
asmgen.out(" pha")
|
||||
if(!directIntoY(expr.right)) asmgen.out(" pha")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.Y, true)
|
||||
asmgen.out(" pla | jsr math.divmod_b_asm")
|
||||
if(!directIntoY(expr.right)) asmgen.out(" pla")
|
||||
asmgen.out(" jsr math.divmod_b_asm")
|
||||
assignRegisterByte(target, CpuRegister.Y, true, true)
|
||||
return true
|
||||
}
|
||||
@@ -546,25 +536,26 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
when(expr.type) {
|
||||
in ByteDatatypes -> {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, expr.type in SignedDatatypes)
|
||||
asmgen.out(" pha")
|
||||
if(!directIntoY(expr.right)) asmgen.out(" pha")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.Y, expr.type in SignedDatatypes)
|
||||
asmgen.out(" pla | jsr math.multiply_bytes")
|
||||
if(!directIntoY(expr.right)) asmgen.out(" pla")
|
||||
asmgen.out(" jsr math.multiply_bytes")
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
return true
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
if(expr.definingBlock()!!.veraFxMuls) {
|
||||
if(expr.definingBlock()!!.options.veraFxMuls) {
|
||||
// cx16 verafx hardware mul
|
||||
if(expr.right.isSimple()) {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.R0, expr.left.type in SignedDatatypes)
|
||||
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.R1, expr.left.type in SignedDatatypes)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.left.type in SignedDatatypes)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.R1, expr.left.type in SignedDatatypes)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
asmgen.out(" pla")
|
||||
asmgen.out(" sta cx16.r0 | sty cx16.r0+1")
|
||||
}
|
||||
asmgen.out(" jsr verafx.muls")
|
||||
@@ -596,7 +587,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr math.mul_word_${value}")
|
||||
}
|
||||
else {
|
||||
if(expr.definingBlock()!!.veraFxMuls){
|
||||
if(expr.definingBlock()!!.options.veraFxMuls){
|
||||
// cx16 verafx hardware mul
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "cx16.r1")
|
||||
asmgen.out("""
|
||||
@@ -731,7 +722,11 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
asmgen.out(" tay | lda #0")
|
||||
} else {
|
||||
asmgen.out(" ldx #$shifts | jsr math.lsr_word_AY")
|
||||
asmgen.out(" ldx #$shifts")
|
||||
if(signed)
|
||||
asmgen.out(" jsr math.lsr_word_AY")
|
||||
else
|
||||
asmgen.out(" jsr math.lsr_uword_AY")
|
||||
}
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return true
|
||||
@@ -777,37 +772,47 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return true
|
||||
}
|
||||
else -> {
|
||||
val leftMemByte = expr.left as? PtMemoryByte
|
||||
val rightMemByte = expr.right as? PtMemoryByte
|
||||
val leftArrayIndexer = expr.left as? PtArrayIndexer
|
||||
val rightArrayIndexer = expr.right as? PtArrayIndexer
|
||||
if(rightArrayIndexer!=null && rightArrayIndexer.type in ByteDatatypes && left.type in ByteDatatypes) {
|
||||
// special optimization for bytevalue +/- bytearr[y] : no need to use a tempvar, just use adc array,y or sbc array,y or adc (ptr),y / sbc (ptr),y
|
||||
if(expr.operator=="+" && leftArrayIndexer!=null && leftArrayIndexer.type in ByteDatatypes && right.type in ByteDatatypes) {
|
||||
// special optimization for bytearray[y] + bytevalue : no need to use a tempvar, just use adc array,y
|
||||
assignExpressionToRegister(right, RegisterOrPair.A, right.type==DataType.BYTE)
|
||||
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(leftArrayIndexer.index, RegisterOrPair.Y, false)
|
||||
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pla")
|
||||
val arrayvarname = asmgen.asmSymbolName(leftArrayIndexer.variable)
|
||||
asmgen.out(" clc | adc $arrayvarname,y")
|
||||
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
|
||||
} else if(rightArrayIndexer!=null && rightArrayIndexer.type in ByteDatatypes && left.type in ByteDatatypes) {
|
||||
// special optimization for bytevalue +/- bytearray[y] : no need to use a tempvar, just use adc array,y or sbc array,y
|
||||
assignExpressionToRegister(left, RegisterOrPair.A, left.type==DataType.BYTE)
|
||||
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y, false)
|
||||
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pla")
|
||||
if(rightArrayIndexer.usesPointerVariable && !asmgen.isZpVar(rightArrayIndexer.variable)) {
|
||||
asmgen.out("""
|
||||
ldx ${rightArrayIndexer.variable.name}
|
||||
stx P8ZP_SCRATCH_W1
|
||||
ldx ${rightArrayIndexer.variable.name}+1
|
||||
stx P8ZP_SCRATCH_W1+1""")
|
||||
if (expr.operator == "+")
|
||||
asmgen.out(" clc | adc (P8ZP_SCRATCH_W1),y")
|
||||
else
|
||||
asmgen.out(" sec | sbc (P8ZP_SCRATCH_W1),y")
|
||||
} else {
|
||||
val arrayvarname = if (rightArrayIndexer.usesPointerVariable)
|
||||
"(${rightArrayIndexer.variable.name})"
|
||||
else
|
||||
asmgen.asmSymbolName(rightArrayIndexer.variable)
|
||||
if (expr.operator == "+")
|
||||
asmgen.out(" clc | adc $arrayvarname,y")
|
||||
else
|
||||
asmgen.out(" sec | sbc $arrayvarname,y")
|
||||
}
|
||||
val arrayvarname = asmgen.asmSymbolName(rightArrayIndexer.variable)
|
||||
if (expr.operator == "+")
|
||||
asmgen.out(" clc | adc $arrayvarname,y")
|
||||
else
|
||||
asmgen.out(" sec | sbc $arrayvarname,y")
|
||||
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
|
||||
} else if(expr.operator=="+" && leftMemByte!=null && right.type in ByteDatatypes && optimizedPointerIndexPlusMinusByteIntoA(right, "+", leftMemByte)) {
|
||||
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
|
||||
return true
|
||||
} else if(rightMemByte!=null && left.type in ByteDatatypes && optimizedPointerIndexPlusMinusByteIntoA(left, expr.operator, rightMemByte)) {
|
||||
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
|
||||
return true
|
||||
} else {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", right.type)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A, left.type==DataType.BYTE)
|
||||
if(directIntoY(right)) {
|
||||
assignExpressionToRegister(right, RegisterOrPair.Y, left.type==DataType.BYTE)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_B1")
|
||||
} else {
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", right.type)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
if (expr.operator == "+")
|
||||
asmgen.out(" clc | adc P8ZP_SCRATCH_B1")
|
||||
else
|
||||
@@ -982,28 +987,74 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return false
|
||||
}
|
||||
|
||||
private fun optimizedLogicalOrBitwiseExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
|
||||
private fun optimizedPointerIndexPlusMinusByteIntoA(value: PtExpression, operator: String, mem: PtMemoryByte): Boolean {
|
||||
// special optimization for bytevalue +/- pointervar[y] (actually: bytevalue +/- @(address) )
|
||||
val address = mem.address as? PtBinaryExpression
|
||||
if(address is PtBinaryExpression) {
|
||||
val ptrVar = address.left as? PtIdentifier
|
||||
if(ptrVar!=null && asmgen.isZpVar(ptrVar)) {
|
||||
assignExpressionToRegister(value, RegisterOrPair.A, false)
|
||||
val pointername = asmgen.asmVariableName(ptrVar)
|
||||
val constOffset = address.right.asConstInteger()
|
||||
if (constOffset != null && constOffset < 256) {
|
||||
// we have value + @(zpptr + 255), or value - @(zpptr+255)
|
||||
asmgen.out(" ldy #$constOffset")
|
||||
if (operator == "+")
|
||||
asmgen.out(" clc | adc ($pointername),y")
|
||||
else
|
||||
asmgen.out(" sec | sbc ($pointername),y")
|
||||
} else if (address.right.type in ByteDatatypes) {
|
||||
// we have @(ptr + bytevar) ++ , or @(ptr+bytevar)--
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToRegister(address.right, RegisterOrPair.Y, false)
|
||||
asmgen.out(" pla")
|
||||
if (operator == "+")
|
||||
asmgen.out(" clc | adc ($pointername),y")
|
||||
else
|
||||
asmgen.out(" sec | sbc ($pointername),y")
|
||||
} else if ((address.right as? PtTypeCast)?.value?.type in ByteDatatypes) {
|
||||
// we have @(ptr + bytevar as uword) ++ , or @(ptr+bytevar as uword)--
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToRegister((address.right as PtTypeCast).value, RegisterOrPair.Y, false)
|
||||
asmgen.out(" pla")
|
||||
if (operator == "+")
|
||||
asmgen.out(" clc | adc ($pointername),y")
|
||||
else
|
||||
asmgen.out(" sec | sbc ($pointername),y")
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun optimizedBitwiseExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
|
||||
if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
|
||||
if (expr.right.isSimple()) {
|
||||
if (expr.right is PtNumber || expr.right is PtIdentifier) {
|
||||
assignLogicalWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right)
|
||||
assignBitwiseWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right)
|
||||
return true
|
||||
}
|
||||
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
|
||||
assignLogicalWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left)
|
||||
assignBitwiseWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
if(directIntoY(expr.right)) {
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_B1")
|
||||
} else {
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
when (expr.operator) {
|
||||
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
||||
"&" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||
"|" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
||||
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
||||
else -> throw AssemblyError("invalid operator")
|
||||
else -> throw AssemblyError("invalid bitwise operator")
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
return true
|
||||
@@ -1011,20 +1062,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
else if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
||||
if (expr.right.isSimple()) {
|
||||
if (expr.right is PtNumber || expr.right is PtIdentifier) {
|
||||
assignLogicalWithSimpleRightOperandWord(target, expr.left, expr.operator, expr.right)
|
||||
assignBitwiseWithSimpleRightOperandWord(target, expr.left, expr.operator, expr.right)
|
||||
return true
|
||||
}
|
||||
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
|
||||
assignLogicalWithSimpleRightOperandWord(target, expr.right, expr.operator, expr.left)
|
||||
assignBitwiseWithSimpleRightOperandWord(target, expr.right, expr.operator, expr.left)
|
||||
return true
|
||||
}
|
||||
}
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
|
||||
when (expr.operator) {
|
||||
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_W1 | tax | tya | and P8ZP_SCRATCH_W1+1 | tay | txa")
|
||||
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_W1 | tax | tya | ora P8ZP_SCRATCH_W1+1 | tay | txa")
|
||||
"&" -> 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")
|
||||
else -> throw AssemblyError("invalid operator")
|
||||
else -> throw AssemblyError("invalid bitwise operator")
|
||||
}
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return true
|
||||
@@ -1032,6 +1083,63 @@ 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
|
||||
}
|
||||
|
||||
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)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_B1")
|
||||
} else {
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
when (expr.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)
|
||||
return true
|
||||
}
|
||||
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")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun assignOptimizedComparisonBytes(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
val signed = expr.left.type == DataType.BYTE || expr.right.type == DataType.BYTE
|
||||
when(expr.operator) {
|
||||
@@ -1703,7 +1811,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return true
|
||||
}
|
||||
|
||||
private fun assignLogicalWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
|
||||
private fun assignBitwiseWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
|
||||
assignExpressionToRegister(left, RegisterOrPair.A, false)
|
||||
val operand = when(right) {
|
||||
is PtNumber -> "#${right.number.toHex()}"
|
||||
@@ -1711,33 +1819,52 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
else -> throw AssemblyError("wrong right operand type")
|
||||
}
|
||||
when (operator) {
|
||||
"&", "and" -> asmgen.out(" and $operand")
|
||||
"|", "or" -> asmgen.out(" ora $operand")
|
||||
"&" -> asmgen.out(" and $operand")
|
||||
"|" -> asmgen.out(" ora $operand")
|
||||
"^", "xor" -> asmgen.out(" eor $operand")
|
||||
else -> throw AssemblyError("invalid operator")
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
}
|
||||
|
||||
private fun assignLogicalWithSimpleRightOperandWord(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
|
||||
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) {
|
||||
is PtNumber -> {
|
||||
val number = right.number.toHex()
|
||||
when (operator) {
|
||||
"&", "and" -> asmgen.out(" and #<$number | tax | tya | and #>$number | tay | txa")
|
||||
"|", "or" -> asmgen.out(" ora #<$number | tax | tya | ora #>$number | tay | txa")
|
||||
"&" -> 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")
|
||||
else -> throw AssemblyError("invalid operator")
|
||||
else -> throw AssemblyError("invalid bitwise operator")
|
||||
}
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val name = asmgen.asmSymbolName(right)
|
||||
when (operator) {
|
||||
"&", "and" -> asmgen.out(" and $name | tax | tya | and $name+1 | tay | txa")
|
||||
"|", "or" -> asmgen.out(" ora $name | tax | tya | ora $name+1 | tay | txa")
|
||||
"&" -> 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")
|
||||
else -> throw AssemblyError("invalid operator")
|
||||
else -> throw AssemblyError("invalid bitwise operator")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("wrong right operand type")
|
||||
@@ -1814,44 +1941,41 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
|
||||
private fun containmentCheckIntoA(containment: PtContainmentCheck) {
|
||||
val elementDt = containment.element.type
|
||||
val symbol = asmgen.symbolTable.lookup(containment.iterable.name)
|
||||
val variable = symbol!!.astNode as IPtVariable
|
||||
val varname = asmgen.asmVariableName(containment.iterable)
|
||||
val numElements = when(variable) {
|
||||
is PtConstant -> null
|
||||
is PtMemMapped -> variable.arraySize
|
||||
is PtVariable -> variable.arraySize
|
||||
val symbol = asmgen.symbolTable.lookup(containment.iterable.name)!!
|
||||
val symbolName = asmgen.asmVariableName(symbol, containment.definingSub())
|
||||
val (dt, numElements) = when(symbol) {
|
||||
is StStaticVariable -> symbol.dt to symbol.length!!
|
||||
is StMemVar -> symbol.dt to symbol.length!!
|
||||
else -> DataType.UNDEFINED to 0
|
||||
}
|
||||
when(variable.type) {
|
||||
when(dt) {
|
||||
DataType.STR -> {
|
||||
// use subroutine
|
||||
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position,"P8ZP_SCRATCH_W1"), varname, null, null)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
val stringVal = (variable as PtVariable).value as PtString
|
||||
asmgen.out(" ldy #${stringVal.value.length}")
|
||||
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position,"P8ZP_SCRATCH_W1"), symbolName, null, null)
|
||||
asmgen.out(" pla")
|
||||
asmgen.out(" ldy #${numElements-1}")
|
||||
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||
return
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
throw AssemblyError("containment check of floats not supported")
|
||||
assignExpressionToRegister(containment.element, RegisterOrPair.FAC1, true)
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, null, null)
|
||||
asmgen.out(" ldy #$numElements")
|
||||
asmgen.out(" jsr floats.containment_floatarray")
|
||||
}
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W1"), varname, null, null)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, null, null)
|
||||
asmgen.out(" pla")
|
||||
asmgen.out(" ldy #$numElements")
|
||||
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||
return
|
||||
}
|
||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt)
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W2"), varname, null, null)
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName, null, null)
|
||||
asmgen.out(" ldy #$numElements")
|
||||
asmgen.out(" jsr prog8_lib.containment_wordarray")
|
||||
return
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
@@ -1870,7 +1994,22 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
else -> throw AssemblyError("can't use Z or N flags as return 'values'")
|
||||
Statusflag.Pz -> {
|
||||
asmgen.out("""
|
||||
beq +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
Statusflag.Pn -> {
|
||||
asmgen.out("""
|
||||
bmi +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
}
|
||||
@@ -1913,7 +2052,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val addrExpr = value.address as PtBinaryExpression
|
||||
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, false)) {
|
||||
asmgen.out(" ldy #0")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
} else {
|
||||
@@ -1974,20 +2113,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
if(valueDt==DataType.UBYTE || valueDt==DataType.BOOL) {
|
||||
if(valueDt in ByteDatatypes) {
|
||||
when(target.register) {
|
||||
RegisterOrPair.A,
|
||||
RegisterOrPair.X,
|
||||
RegisterOrPair.Y -> {
|
||||
// 'cast' an ubyte value to a byte register; no cast needed at all
|
||||
return assignExpressionToRegister(value, target.register, false)
|
||||
return assignExpressionToRegister(value, target.register, valueDt in SignedDatatypes)
|
||||
}
|
||||
RegisterOrPair.AX,
|
||||
RegisterOrPair.AY,
|
||||
RegisterOrPair.XY,
|
||||
in Cx16VirtualRegisters -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.A, false)
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
assignRegisterByte(target, CpuRegister.A, valueDt in SignedDatatypes, true)
|
||||
return
|
||||
}
|
||||
else -> {}
|
||||
@@ -2006,7 +2145,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
RegisterOrPair.XY,
|
||||
in Cx16VirtualRegisters -> {
|
||||
// 'cast' uword into a 16 bits register, just assign it
|
||||
return assignExpressionToRegister(value, target.register!!, false)
|
||||
return assignExpressionToRegister(value, target.register!!, targetDt in SignedDatatypes)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
@@ -2391,7 +2530,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
else
|
||||
program.memsizer.memorySize(arrayDt!!, constIndex) // add arrayIndexExpr * elementsize to the address of the array variable.
|
||||
} else {
|
||||
val eltSize = program.memsizer.memorySize(ArrayToElementTypes.getValue(arrayDt!!))
|
||||
val eltSize = if(arrayDt==DataType.UWORD) 1 else program.memsizer.memorySize(ArrayToElementTypes.getValue(arrayDt!!))
|
||||
assignExpressionToVariable(arrayIndexExpr, "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
when(eltSize) {
|
||||
1 -> {}
|
||||
@@ -2538,11 +2677,11 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
{
|
||||
when(target.datatype) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname},y")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
if(target.array.splitWords)
|
||||
asmgen.out("""
|
||||
lda $sourceName
|
||||
@@ -2641,11 +2780,11 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
jsr floats.copy_float""")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
asmgen.out(" pla")
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
@@ -2715,21 +2854,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if(assignsIndexedPointerVar(target)) {
|
||||
if (target.constArrayIndexValue==0u) {
|
||||
asmgen.out(" lda $sourceName")
|
||||
asmgen.storeAIntoPointerVar(target.origAstTarget!!.array!!.variable)
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
|
||||
if (asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
|
||||
asmgen.out(" lda $sourceName | sta (${target.asmVarname}),y")
|
||||
} else {
|
||||
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
|
||||
asmgen.out(" lda $sourceName | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if(target.array!!.splitWords)
|
||||
TODO("assign into split words ${target.position}")
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
@@ -2737,7 +2861,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
||||
}
|
||||
else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname},y")
|
||||
}
|
||||
}
|
||||
@@ -2788,7 +2912,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" sta ${wordtarget.asmVarname}+$scaledIdx | sty ${wordtarget.asmVarname}+$scaledIdx+1")
|
||||
}
|
||||
else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.X)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, CpuRegister.X)
|
||||
asmgen.out(" lda $sourceName")
|
||||
asmgen.signExtendAYlsb(DataType.BYTE)
|
||||
asmgen.out(" sta ${wordtarget.asmVarname},x | inx | tya | sta ${wordtarget.asmVarname},x")
|
||||
@@ -2857,7 +2981,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}_msb+$scaledIdx")
|
||||
}
|
||||
else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, CpuRegister.Y)
|
||||
asmgen.out("""
|
||||
lda $sourceName
|
||||
sta ${wordtarget.asmVarname}_lsb,y
|
||||
@@ -2874,7 +2998,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
|
||||
}
|
||||
else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, CpuRegister.Y)
|
||||
asmgen.out("""
|
||||
lda $sourceName
|
||||
sta ${wordtarget.asmVarname},y
|
||||
@@ -3025,7 +3149,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
CpuRegister.X -> when(target.register!!) {
|
||||
RegisterOrPair.A -> { asmgen.out(" txa") }
|
||||
RegisterOrPair.X -> { }
|
||||
RegisterOrPair.Y -> { asmgen.out(" txy") }
|
||||
RegisterOrPair.Y -> { asmgen.out(" stx P8ZP_SCRATCH_REG | ldy P8ZP_SCRATCH_REG") }
|
||||
RegisterOrPair.AY -> {
|
||||
require(extendWord)
|
||||
if(signed)
|
||||
@@ -3075,7 +3199,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
CpuRegister.Y -> when(target.register!!) {
|
||||
RegisterOrPair.A -> { asmgen.out(" tya") }
|
||||
RegisterOrPair.X -> { asmgen.out(" tyx") }
|
||||
RegisterOrPair.X -> { asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG") }
|
||||
RegisterOrPair.Y -> { }
|
||||
RegisterOrPair.AY -> {
|
||||
require(extendWord)
|
||||
@@ -3135,88 +3259,29 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
if(target.array!!.splitWords)
|
||||
throw AssemblyError("cannot assign byte to split word array here ${target.position}")
|
||||
|
||||
if(assignsIndexedPointerVar(target)) {
|
||||
// assign byte to a byte array via an uword pointervariable instead of a regular array variable
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> asmgen.out(" txa")
|
||||
CpuRegister.Y -> asmgen.out(" tya")
|
||||
}
|
||||
if(asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
|
||||
asmgen.out(" ldy #${target.constArrayIndexValue} | sta (${target.asmVarname}),y")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy ${target.asmVarname}
|
||||
sty P8ZP_SCRATCH_W1
|
||||
ldy ${target.asmVarname}+1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #${target.constArrayIndexValue}
|
||||
sta (P8ZP_SCRATCH_W1),y""")
|
||||
}
|
||||
// assign regular array indexing
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> asmgen.out(" txa")
|
||||
CpuRegister.Y -> asmgen.out(" tya")
|
||||
}
|
||||
else {
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> asmgen.out(" txa")
|
||||
CpuRegister.Y -> asmgen.out(" tya")
|
||||
}
|
||||
val indexVar = target.array.index as? PtIdentifier
|
||||
if(indexVar!=null) {
|
||||
if(asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
|
||||
asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta (${target.asmVarname}),y")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy ${target.asmVarname}
|
||||
sty P8ZP_SCRATCH_W1
|
||||
ldy ${target.asmVarname}+1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy ${asmgen.asmVariableName(indexVar)}
|
||||
sta (P8ZP_SCRATCH_W1),y""")
|
||||
}
|
||||
} else {
|
||||
asmgen.saveRegisterStack(register, false)
|
||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
if(asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
|
||||
asmgen.out(" sta (${target.asmVarname}),y")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldx ${target.asmVarname}
|
||||
stx P8ZP_SCRATCH_W1
|
||||
ldx ${target.asmVarname}+1
|
||||
stx P8ZP_SCRATCH_W1+1
|
||||
sta (P8ZP_SCRATCH_W1),y""")
|
||||
}
|
||||
}
|
||||
asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}")
|
||||
}
|
||||
else {
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> asmgen.out(" txa")
|
||||
CpuRegister.Y -> asmgen.out(" tya")
|
||||
}
|
||||
return
|
||||
} else {
|
||||
// assign regular array indexing
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> asmgen.out(" txa")
|
||||
CpuRegister.Y -> asmgen.out(" tya")
|
||||
}
|
||||
asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}")
|
||||
}
|
||||
else {
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> asmgen.out(" txa")
|
||||
CpuRegister.Y -> asmgen.out(" tya")
|
||||
}
|
||||
val indexVar = target.array.index as? PtIdentifier
|
||||
if(indexVar!=null) {
|
||||
asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta ${target.asmVarname},y")
|
||||
} else {
|
||||
require(target.array.index.type in ByteDatatypes)
|
||||
asmgen.saveRegisterStack(register, false)
|
||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
asmgen.out(" sta ${target.asmVarname},y")
|
||||
}
|
||||
val indexVar = target.array.index as? PtIdentifier
|
||||
if(indexVar!=null) {
|
||||
asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta ${target.asmVarname},y")
|
||||
} else {
|
||||
require(target.array.index.type in ByteDatatypes)
|
||||
asmgen.saveRegisterStack(register, false)
|
||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
|
||||
asmgen.out(" pla | sta ${target.asmVarname},y")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3273,7 +3338,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
|
||||
else -> throw AssemblyError("expected reg pair")
|
||||
}
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.out("""
|
||||
pla
|
||||
sta ${target.asmVarname}_msb,y
|
||||
@@ -3281,7 +3346,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
sta ${target.asmVarname}_lsb,y""")
|
||||
} else {
|
||||
val srcReg = asmgen.asmSymbolName(regs)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.out("""
|
||||
lda $srcReg
|
||||
sta ${target.asmVarname}_lsb,y
|
||||
@@ -3316,7 +3381,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
|
||||
else -> throw AssemblyError("expected reg pair")
|
||||
}
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.out("""
|
||||
iny
|
||||
pla
|
||||
@@ -3326,7 +3391,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
sta ${target.asmVarname},y""")
|
||||
} else {
|
||||
val srcReg = asmgen.asmSymbolName(regs)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.out("""
|
||||
iny
|
||||
lda $srcReg+1
|
||||
@@ -3412,7 +3477,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
throw AssemblyError("memory is bytes not words")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, CpuRegister.Y)
|
||||
if(target.array.splitWords)
|
||||
asmgen.out("""
|
||||
lda #0
|
||||
@@ -3466,7 +3531,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, CpuRegister.Y)
|
||||
if(target.array.splitWords)
|
||||
asmgen.out("""
|
||||
lda #<${word.toHex()}
|
||||
@@ -3512,21 +3577,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if(assignsIndexedPointerVar(target)) {
|
||||
if (target.constArrayIndexValue==0u) {
|
||||
asmgen.out(" lda #0")
|
||||
asmgen.storeAIntoPointerVar(target.origAstTarget!!.array!!.variable)
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
|
||||
if (asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
|
||||
asmgen.out(" lda #0 | sta (${target.asmVarname}),y")
|
||||
} else {
|
||||
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
|
||||
asmgen.out(" lda #0 | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if(target.array!!.splitWords)
|
||||
throw AssemblyError("cannot assign byte to split word array here ${target.position}")
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
@@ -3566,21 +3616,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if(assignsIndexedPointerVar(target)) {
|
||||
if (target.constArrayIndexValue==0u) {
|
||||
asmgen.out(" lda #${byte.toHex()}")
|
||||
asmgen.storeAIntoPointerVar(target.origAstTarget!!.array!!.variable)
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
|
||||
if (asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
|
||||
asmgen.out(" lda #${byte.toHex()} | sta (${target.asmVarname}),y")
|
||||
} else {
|
||||
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
|
||||
asmgen.out(" lda #${byte.toHex()} | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if(target.array!!.splitWords)
|
||||
TODO("assign into split words ${target.position}")
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
@@ -3588,7 +3623,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue")
|
||||
}
|
||||
else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname},y")
|
||||
}
|
||||
}
|
||||
@@ -3612,9 +3647,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignsIndexedPointerVar(target: AsmAssignTarget): Boolean =
|
||||
target.origAstTarget?.array?.variable?.type==DataType.UWORD
|
||||
|
||||
internal fun assignConstantFloat(target: AsmAssignTarget, float: Double) {
|
||||
if (float == 0.0) {
|
||||
// optimized case for float zero
|
||||
@@ -3887,7 +3919,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true))
|
||||
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true))
|
||||
storeViaExprEval()
|
||||
}
|
||||
else -> storeViaExprEval()
|
||||
@@ -3941,9 +3973,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
sta $addr""")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(memory.address as PtIdentifier)
|
||||
asmgen.loadByteFromPointerIntoA(memory.address as PtIdentifier)
|
||||
asmgen.out(" eor #255")
|
||||
asmgen.out(" sta ($sourceName),y")
|
||||
asmgen.storeAIntoPointerVar(memory.address as PtIdentifier)
|
||||
}
|
||||
else -> {
|
||||
asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
|
||||
+205
-31
@@ -37,8 +37,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
"*=" -> inplaceModification(assign.target, "*", assign.source)
|
||||
"/=" -> inplaceModification(assign.target, "/", assign.source)
|
||||
"|=" -> inplaceModification(assign.target, "|", assign.source)
|
||||
"or=" -> inplaceModification(assign.target, "or", assign.source)
|
||||
"&=" -> inplaceModification(assign.target, "&", assign.source)
|
||||
"^=" -> inplaceModification(assign.target, "^", assign.source)
|
||||
"and=" -> inplaceModification(assign.target, "and", assign.source)
|
||||
"^=", "xor=" -> inplaceModification(assign.target, "^", assign.source)
|
||||
"<<=" -> inplaceModification(assign.target, "<<", assign.source)
|
||||
">>=" -> inplaceModification(assign.target, ">>", assign.source)
|
||||
"%=" -> inplaceModification(assign.target, "%", assign.source)
|
||||
@@ -161,7 +163,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
asmgen.assignExpressionTo(memory.address, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UWORD, memory.definingISub(), target.position, register = RegisterOrPair.AY))
|
||||
if(memory.address is PtBinaryExpression && tryOptimizedMemoryInplace(memory.address as PtBinaryExpression, operator, value))
|
||||
return
|
||||
asmgen.assignExpressionToRegister(memory.address, RegisterOrPair.AY, false)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, true)
|
||||
asmgen.out(" jsr prog8_lib.read_byte_from_address_in_AY_into_A")
|
||||
@@ -189,12 +193,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" ldx P8ZP_SCRATCH_B1")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
asmgen.out(" sta P8ZP_SCRATCH_B1")
|
||||
val tempVar = asmgen.getTempVarName(DataType.UBYTE)
|
||||
asmgen.out(" sta $tempVar")
|
||||
if(value.expression is PtTypeCast)
|
||||
inplacemodificationByteVariableWithValue("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.expression)
|
||||
inplacemodificationByteVariableWithValue(tempVar, DataType.UBYTE, operator, value.expression)
|
||||
else
|
||||
inplacemodificationByteVariableWithValue("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.expression!!)
|
||||
asmgen.out(" ldx P8ZP_SCRATCH_B1")
|
||||
inplacemodificationByteVariableWithValue(tempVar, DataType.UBYTE, operator, value.expression!!)
|
||||
asmgen.out(" ldx $tempVar")
|
||||
}
|
||||
}
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
@@ -206,10 +211,23 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
TargetStorageKind.ARRAY -> {
|
||||
val indexNum = target.array!!.index as? PtNumber
|
||||
if (indexNum!=null) {
|
||||
val targetVarName = if(target.array.splitWords)
|
||||
"${target.asmVarname} + ${indexNum.number.toInt()}"
|
||||
else
|
||||
"${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
|
||||
val index = indexNum.number.toInt()
|
||||
if(target.array.splitWords) {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationSplitWordWithLiteralval(target.asmVarname, index, operator, value.number!!.number.toInt())
|
||||
else -> {
|
||||
// TODO: more optimized code for VARIABLE, REGISTER, MEMORY, ARRAY, EXPRESSION in the case of split-word arrays
|
||||
val scope = target.origAstTarget?.definingSub()
|
||||
val regTarget = AsmAssignTarget.fromRegisters(RegisterOrPair.R0, false, target.position, scope, asmgen)
|
||||
val assignToReg = AsmAssignment(value, regTarget, program.memsizer, target.position)
|
||||
assignmentAsmGen.translateNormalAssignment(assignToReg, scope)
|
||||
inplacemodificationSplitWordWithR0(target.asmVarname, index, operator)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// normal array
|
||||
val targetVarName = "${target.asmVarname} + ${index*program.memsizer.memorySize(target.datatype)}"
|
||||
when (target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
when(value.kind) {
|
||||
@@ -276,7 +294,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
&& value.expression is PtTypeCast
|
||||
&& tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
|
||||
return
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.out(" lda ${target.array.variable.name},y")
|
||||
when(value.kind) {
|
||||
@@ -310,13 +328,14 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
asmgen.out(" sta P8ZP_SCRATCH_B1")
|
||||
val tempVar = asmgen.getTempVarName(DataType.UBYTE)
|
||||
asmgen.out(" sta $tempVar")
|
||||
if(value.expression is PtTypeCast)
|
||||
inplacemodificationByteVariableWithValue("P8ZP_SCRATCH_B1", target.datatype, operator, value.expression)
|
||||
inplacemodificationByteVariableWithValue(tempVar, target.datatype, operator, value.expression)
|
||||
else
|
||||
inplacemodificationByteVariableWithValue("P8ZP_SCRATCH_B1", target.datatype, operator, value.expression!!)
|
||||
inplacemodificationByteVariableWithValue(tempVar, target.datatype, operator, value.expression!!)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.out(" lda P8ZP_SCRATCH_B1")
|
||||
asmgen.out(" lda $tempVar")
|
||||
}
|
||||
}
|
||||
asmgen.out(" sta ${target.array.variable.name},y")
|
||||
@@ -327,7 +346,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
&& value.expression is PtTypeCast
|
||||
&& tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
|
||||
return
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
if(target.array.splitWords) {
|
||||
asmgen.out(" lda ${target.array.variable.name}_lsb,y")
|
||||
@@ -384,12 +403,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1")
|
||||
val tempVar = asmgen.getTempVarName(DataType.UWORD)
|
||||
asmgen.out(" sta $tempVar | stx $tempVar+1")
|
||||
if(value.expression is PtTypeCast)
|
||||
inplacemodificationWordWithValue("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression, block)
|
||||
inplacemodificationWordWithValue(tempVar, target.datatype, operator, value.expression, block)
|
||||
else
|
||||
inplacemodificationWordWithValue("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression!!, block)
|
||||
asmgen.out(" lda P8ZP_SCRATCH_W1 | ldx P8ZP_SCRATCH_W1+1")
|
||||
inplacemodificationWordWithValue(tempVar, target.datatype, operator, value.expression!!, block)
|
||||
asmgen.out(" lda $tempVar | ldx $tempVar+1")
|
||||
}
|
||||
}
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, true)
|
||||
@@ -402,7 +422,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
DataType.FLOAT -> {
|
||||
// copy array value into tempvar
|
||||
val tempvar = asmgen.getTempVarName(DataType.FLOAT)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.FLOAT, CpuRegister.A)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.A)
|
||||
asmgen.out("""
|
||||
ldy #>${target.asmVarname}
|
||||
clc
|
||||
@@ -456,6 +476,133 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryOptimizedMemoryInplace(address: PtBinaryExpression, operator: String, value: AsmAssignSource): Boolean {
|
||||
if(value.datatype !in ByteDatatypes || operator !in "|&^+-")
|
||||
return false
|
||||
val rightTc = address.right as? PtTypeCast
|
||||
val constOffset = (address.right as? PtNumber)?.number?.toInt()
|
||||
if(address.operator=="+" && (address.right.type in ByteDatatypes || (rightTc!=null && rightTc.value.type in ByteDatatypes) || (constOffset!=null && constOffset<256)) ) {
|
||||
if(rightTc!=null)
|
||||
asmgen.assignExpressionToRegister(rightTc.value, RegisterOrPair.A, false)
|
||||
else if(constOffset!=null)
|
||||
asmgen.out(" lda #${constOffset}")
|
||||
else
|
||||
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha") // offset on stack
|
||||
val zpPointerVarName: String
|
||||
if(address.left is PtIdentifier && asmgen.isZpVar(address.left as PtIdentifier)) {
|
||||
zpPointerVarName = (address.left as PtIdentifier).name
|
||||
} else {
|
||||
zpPointerVarName = "P8ZP_SCRATCH_W2"
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
|
||||
asmgen.out(" sta $zpPointerVarName | sty $zpPointerVarName+1")
|
||||
}
|
||||
// calculate value into A
|
||||
val assignValue = AsmAssignment(value,
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE,
|
||||
address.definingISub(), Position.DUMMY, register = RegisterOrPair.A),
|
||||
program.memsizer, Position.DUMMY)
|
||||
assignmentAsmGen.translateNormalAssignment(assignValue, address.definingISub())
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false) // offset into Y
|
||||
when(operator) {
|
||||
"|" -> asmgen.out(" ora ($zpPointerVarName),y")
|
||||
"&" -> asmgen.out(" and ($zpPointerVarName),y")
|
||||
"^" -> asmgen.out(" eor ($zpPointerVarName),y")
|
||||
"+" -> asmgen.out(" clc | adc ($zpPointerVarName),y")
|
||||
"-" -> asmgen.out(" sta P8ZP_SCRATCH_REG | lda ($zpPointerVarName),y | sec | sbc P8ZP_SCRATCH_REG")
|
||||
else -> throw AssemblyError("invalid op")
|
||||
}
|
||||
asmgen.out(" sta ($zpPointerVarName),y")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun inplacemodificationSplitWordWithR0(arrayVar: String, index: Int, operator: String) {
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
asmgen.out("""
|
||||
lda ${arrayVar}_lsb+$index
|
||||
clc
|
||||
adc cx16.r0L
|
||||
sta ${arrayVar}_lsb+$index
|
||||
lda ${arrayVar}_msb+$index
|
||||
adc cx16.r0H
|
||||
sta ${arrayVar}_msb+$index""")
|
||||
}
|
||||
"-" -> {
|
||||
asmgen.out("""
|
||||
lda ${arrayVar}_lsb+$index
|
||||
sec
|
||||
sbc cx16.r0L
|
||||
sta ${arrayVar}_lsb+$index
|
||||
lda ${arrayVar}_msb+$index
|
||||
sbc cx16.r0H
|
||||
sta ${arrayVar}_msb+$index""")
|
||||
}
|
||||
else -> TODO("in-place modify split-words array value for operator $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplacemodificationSplitWordWithLiteralval(arrayVar: String, index: Int, operator: String, value: Int) {
|
||||
// note: this contains special optimized cases because we know the exact value. Don't replace this with another routine.
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
when {
|
||||
value==0 -> {}
|
||||
value in 1..0xff -> asmgen.out("""
|
||||
lda ${arrayVar}_lsb+$index
|
||||
clc
|
||||
adc #$value
|
||||
sta ${arrayVar}_lsb+$index
|
||||
bcc +
|
||||
inc ${arrayVar}_msb+$index
|
||||
+""")
|
||||
value==0x0100 -> asmgen.out(" inc ${arrayVar}_msb+$index")
|
||||
value==0x0200 -> asmgen.out(" inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index")
|
||||
value==0x0300 -> asmgen.out(" inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index")
|
||||
value==0x0400 -> asmgen.out(" inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index")
|
||||
value and 255==0 -> asmgen.out(" lda ${arrayVar}_msb+$index | clc | adc #>$value | sta ${arrayVar}_msb+$index")
|
||||
else -> asmgen.out("""
|
||||
lda ${arrayVar}_lsb+$index
|
||||
clc
|
||||
adc #<$value
|
||||
sta ${arrayVar}_lsb+$index
|
||||
lda ${arrayVar}_msb+$index
|
||||
adc #>$value
|
||||
sta ${arrayVar}_msb+$index""")
|
||||
}
|
||||
}
|
||||
"-" -> {
|
||||
when {
|
||||
value==0 -> {}
|
||||
value in 1..0xff -> asmgen.out("""
|
||||
lda ${arrayVar}_lsb+$index
|
||||
sec
|
||||
sbc #$value
|
||||
sta ${arrayVar}_lsb+$index
|
||||
bcs +
|
||||
dec ${arrayVar}_msb+$index
|
||||
+""")
|
||||
value==0x0100 -> asmgen.out(" dec ${arrayVar}_msb+$index")
|
||||
value==0x0200 -> asmgen.out(" dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index")
|
||||
value==0x0300 -> asmgen.out(" dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index")
|
||||
value==0x0400 -> asmgen.out(" dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index")
|
||||
value and 255==0 -> asmgen.out(" lda ${arrayVar}_msb+$index | sec | sbc #>$value | sta ${arrayVar}_msb+$index")
|
||||
else -> asmgen.out("""
|
||||
lda ${arrayVar}_lsb+$index
|
||||
sec
|
||||
sbc #<$value
|
||||
sta ${arrayVar}_lsb+$index
|
||||
lda ${arrayVar}_msb+$index
|
||||
sbc #>$value
|
||||
sta ${arrayVar}_msb+$index""")
|
||||
}
|
||||
}
|
||||
else -> TODO("in-place modify split-words array value for operator $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplacemodificationRegisterAXwithVariable(operator: String, variable: String, varDt: DataType): Boolean {
|
||||
when(operator) {
|
||||
"+" -> {
|
||||
@@ -791,12 +938,38 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
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
|
||||
$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
|
||||
$shortcutLabel:""")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// normal evaluation
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
|
||||
inplacemodificationRegisterAwithVariableWithSwappedOperands(operator, name, dt in SignedDatatypes)
|
||||
asmgen.out(" sta $name")
|
||||
}
|
||||
|
||||
private fun inplacemodificationByteVariableWithVariable(name: String, dt: DataType, operator: String, otherName: String) {
|
||||
// note: no logical and/or shortcut here, not worth it due to simple right operand
|
||||
asmgen.out(" lda $name")
|
||||
inplacemodificationRegisterAwithVariable(operator, otherName, dt in SignedDatatypes)
|
||||
asmgen.out(" sta $name")
|
||||
@@ -850,9 +1023,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
+""")
|
||||
}
|
||||
}
|
||||
"&" -> asmgen.out(" and $variable")
|
||||
"|" -> asmgen.out(" ora $variable")
|
||||
"^" -> asmgen.out(" eor $variable")
|
||||
"&", "and" -> asmgen.out(" and $variable")
|
||||
"|", "or" -> asmgen.out(" ora $variable")
|
||||
"^","xor" -> asmgen.out(" eor $variable")
|
||||
"==" -> {
|
||||
asmgen.out("""
|
||||
cmp $variable
|
||||
@@ -1130,6 +1303,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
|
||||
private fun inplacemodificationByteVariableWithLiteralval(name: String, dt: DataType, operator: String, value: Int) {
|
||||
// note: this contains special optimized cases because we know the exact value. Don't replace this with another routine.
|
||||
// note: no logical and/or shortcut here, not worth it due to simple right operand
|
||||
when (operator) {
|
||||
"+" -> asmgen.out(" lda $name | clc | adc #$value | sta $name")
|
||||
"-" -> asmgen.out(" lda $name | sec | sbc #$value | sta $name")
|
||||
@@ -1192,9 +1366,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
}
|
||||
"&" -> immediateAndInplace(name, value)
|
||||
"|" -> immediateOrInplace(name, value)
|
||||
"^" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
||||
"&", "and" -> immediateAndInplace(name, value)
|
||||
"|", "or" -> immediateOrInplace(name, value)
|
||||
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
||||
"==" -> {
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
@@ -1440,7 +1614,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
if(value in asmgen.optimizedWordMultiplications) {
|
||||
asmgen.out(" lda $name | ldy $name+1 | jsr math.mul_word_$value | sta $name | sty $name+1")
|
||||
} else {
|
||||
if(block?.veraFxMuls==true)
|
||||
if(block?.options?.veraFxMuls==true)
|
||||
// cx16 verafx hardware mul
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
@@ -1907,7 +2081,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
sta $name+1""")
|
||||
}
|
||||
"*" -> {
|
||||
if(block?.veraFxMuls==true) {
|
||||
if(block?.options?.veraFxMuls==true) {
|
||||
// cx16 verafx hardware muls
|
||||
if(valueDt==DataType.UBYTE) {
|
||||
asmgen.out(" lda $otherName | sta cx16.r1")
|
||||
@@ -2075,7 +2249,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
|
||||
"-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name | lda $name+1 | sbc $otherName+1 | sta $name+1")
|
||||
"*" -> {
|
||||
if(block?.veraFxMuls==true)
|
||||
if(block?.options?.veraFxMuls==true)
|
||||
// cx16 verafx hardware muls
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
@@ -2279,7 +2453,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
|
||||
private fun inplacemodificationWordWithValue(name: String, dt: DataType, operator: String, value: PtExpression, block: PtBlock?) {
|
||||
fun multiplyVarByWordInAY() {
|
||||
if(block?.veraFxMuls==true)
|
||||
if(block?.options?.veraFxMuls==true)
|
||||
// cx16 verafx hardware muls
|
||||
asmgen.out("""
|
||||
sta cx16.r1
|
||||
|
||||
@@ -29,11 +29,11 @@ internal object DummyStringEncoder : IStringEncoding {
|
||||
}
|
||||
}
|
||||
|
||||
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false):
|
||||
IErrorReporter {
|
||||
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false): IErrorReporter {
|
||||
|
||||
val errors = mutableListOf<String>()
|
||||
val warnings = mutableListOf<String>()
|
||||
val infos = mutableListOf<String>()
|
||||
|
||||
override fun err(msg: String, position: Position) {
|
||||
val text = "${position.toClickableStr()} $msg"
|
||||
@@ -47,6 +47,12 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
warnings.add(text)
|
||||
}
|
||||
|
||||
override fun info(msg: String, position: Position) {
|
||||
val text = "${position.toClickableStr()} $msg"
|
||||
if(text !in infos)
|
||||
infos.add(text)
|
||||
}
|
||||
|
||||
override fun undefined(symbol: List<String>, position: Position) {
|
||||
err("undefined symbol: ${symbol.joinToString(".")}", position)
|
||||
}
|
||||
@@ -54,10 +60,11 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") }
|
||||
errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") }
|
||||
if(throwExceptionAtReportIfErrors)
|
||||
finalizeNumErrors(errors.size, warnings.size)
|
||||
finalizeNumErrors(errors.size, warnings.size, infos.size)
|
||||
if(!keepMessagesAfterReporting) {
|
||||
clear()
|
||||
}
|
||||
@@ -66,5 +73,6 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
fun clear() {
|
||||
errors.clear()
|
||||
warnings.clear()
|
||||
infos.clear()
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ class TestCodegen: FunSpec({
|
||||
//}
|
||||
val codegen = AsmGen6502(prefixSymbols = false)
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main",false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
|
||||
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
|
||||
@@ -95,7 +95,7 @@ class TestCodegen: FunSpec({
|
||||
program.add(block)
|
||||
|
||||
// define the "cx16.r0" virtual register
|
||||
val cx16block = PtBlock("cx16", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||
program.add(cx16block)
|
||||
|
||||
|
||||
@@ -14,5 +14,6 @@
|
||||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,12 +1,13 @@
|
||||
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.AssemblyError
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.PrefixOperators
|
||||
import prog8.code.core.SignedDatatypes
|
||||
import prog8.code.core.*
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
internal class AssignmentGen(private val codeGen: IRCodeGen, private val expressionEval: ExpressionGen) {
|
||||
|
||||
internal fun translate(assignment: PtAssignment): IRCodeChunks {
|
||||
@@ -22,91 +23,57 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(augAssign.target.children.single() is PtMachineRegister)
|
||||
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
|
||||
|
||||
val ident = augAssign.target.identifier
|
||||
val memory = augAssign.target.memory
|
||||
val array = augAssign.target.array
|
||||
val target = augAssign.target
|
||||
val targetDt = irType(target.type)
|
||||
val memTarget = target.memory
|
||||
val constAddress = (memTarget?.address as? PtNumber)?.number?.toInt()
|
||||
val symbol = target.identifier?.name
|
||||
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)
|
||||
in PrefixOperators -> inplacePrefix(augAssign.operator, symbol, array, constAddress, memTarget, targetDt)
|
||||
|
||||
val chunks = if(ident!=null) {
|
||||
assignVarAugmented(ident.name, augAssign)
|
||||
} else if(memory != null) {
|
||||
if(memory.address is PtNumber)
|
||||
assignMemoryAugmented((memory.address as PtNumber).number.toInt(), augAssign)
|
||||
else
|
||||
fallbackAssign(augAssign)
|
||||
} else if(array!=null) {
|
||||
// NOTE: naive fallback assignment here will sometimes generate code that loads the index value multiple times
|
||||
// in a register. It's way too much work to optimize that here - instead, we trust that the generated IL assembly
|
||||
// will be optimized later and have the double assignments removed.
|
||||
fallbackAssign(augAssign)
|
||||
} else {
|
||||
fallbackAssign(augAssign)
|
||||
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
|
||||
}
|
||||
|
||||
val chunks = result.getOrElse { fallbackAssign(augAssign) }
|
||||
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(augAssign.position)
|
||||
return chunks
|
||||
}
|
||||
|
||||
private fun assignMemoryAugmented(
|
||||
address: Int,
|
||||
assignment: PtAugmentedAssign
|
||||
): IRCodeChunks {
|
||||
val value = assignment.value
|
||||
val vmDt = irType(value.type)
|
||||
return when(assignment.operator) {
|
||||
"+=" -> expressionEval.operatorPlusInplace(address, null, vmDt, value)
|
||||
"-=" -> expressionEval.operatorMinusInplace(address, null, vmDt, value)
|
||||
"*=" -> expressionEval.operatorMultiplyInplace(address, null, vmDt, value)
|
||||
"/=" -> expressionEval.operatorDivideInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||
"|=" -> expressionEval.operatorOrInplace(address, null, vmDt, value)
|
||||
"&=" -> expressionEval.operatorAndInplace(address, null, vmDt, value)
|
||||
"^=" -> expressionEval.operatorXorInplace(address, null, vmDt, value)
|
||||
"<<=" -> expressionEval.operatorShiftLeftInplace(address, null, vmDt, value)
|
||||
">>=" -> expressionEval.operatorShiftRightInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||
"%=" -> expressionEval.operatorModuloInplace(address, null, vmDt, value)
|
||||
"==" -> expressionEval.operatorEqualsInplace(address, null, vmDt, value)
|
||||
"!=" -> expressionEval.operatorNotEqualsInplace(address, null, vmDt, value)
|
||||
"<" -> expressionEval.operatorLessInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||
">" -> expressionEval.operatorGreaterInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||
"<=" -> expressionEval.operatorLessEqualInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||
">=" -> expressionEval.operatorGreaterEqualInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||
in PrefixOperators -> inplacePrefix(assignment.operator, vmDt, address, null)
|
||||
|
||||
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks {
|
||||
val value = assignment.value
|
||||
val targetDt = irType(assignment.target.type)
|
||||
return when (assignment.operator) {
|
||||
"+=" -> expressionEval.operatorPlusInplace(null, symbol, targetDt, value)
|
||||
"-=" -> expressionEval.operatorMinusInplace(null, symbol, targetDt, value)
|
||||
"*=" -> expressionEval.operatorMultiplyInplace(null, symbol, targetDt, value)
|
||||
"/=" -> expressionEval.operatorDivideInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||
"|=" -> expressionEval.operatorOrInplace(null, symbol, targetDt, value)
|
||||
"&=" -> expressionEval.operatorAndInplace(null, symbol, targetDt, value)
|
||||
"^=" -> expressionEval.operatorXorInplace(null, symbol, targetDt, value)
|
||||
"<<=" -> expressionEval.operatorShiftLeftInplace(null, symbol, targetDt, value)
|
||||
">>=" -> expressionEval.operatorShiftRightInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||
"%=" -> expressionEval.operatorModuloInplace(null, symbol, targetDt, value)
|
||||
"==" -> expressionEval.operatorEqualsInplace(null, symbol, targetDt, value)
|
||||
"!=" -> expressionEval.operatorNotEqualsInplace(null, symbol, targetDt, value)
|
||||
"<" -> expressionEval.operatorLessInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||
">" -> expressionEval.operatorGreaterInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||
"<=" -> expressionEval.operatorLessEqualInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||
">=" -> expressionEval.operatorGreaterEqualInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||
in PrefixOperators -> inplacePrefix(assignment.operator, targetDt, null, symbol)
|
||||
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
|
||||
val value: PtExpression
|
||||
if(origAssign.operator in PrefixOperators) {
|
||||
value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position)
|
||||
value.add(origAssign.value)
|
||||
} else {
|
||||
require(origAssign.operator.endsWith('='))
|
||||
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
|
||||
val operator = when(origAssign.operator) {
|
||||
in ComparisonOperators -> origAssign.operator
|
||||
else -> {
|
||||
require(origAssign.operator.endsWith('='))
|
||||
origAssign.operator.dropLast(1)
|
||||
}
|
||||
}
|
||||
value = PtBinaryExpression(operator, origAssign.value.type, origAssign.value.position)
|
||||
val left: PtExpression = origAssign.target.children.single() as PtExpression
|
||||
value.add(left)
|
||||
value.add(origAssign.value)
|
||||
@@ -117,28 +84,161 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return translateRegularAssign(normalAssign)
|
||||
}
|
||||
|
||||
private fun inplacePrefix(operator: String, vmDt: IRDataType, address: Int?, symbol: String?): IRCodeChunks {
|
||||
val code= IRCodeChunk(null, null)
|
||||
private fun inplacePrefix(operator: String, symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType): Result<IRCodeChunks, NotImplementedError> {
|
||||
if(operator=="+")
|
||||
return Ok(emptyList())
|
||||
|
||||
if(array!=null)
|
||||
return Ok(inplacePrefixArray(operator, array))
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(constAddress==null && memory!=null) {
|
||||
val register = codeGen.registers.nextFree()
|
||||
val tr = expressionEval.translateExpression(memory.address)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
when(operator) {
|
||||
"-" -> {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADI, vmDt, reg1=register, reg2=tr.resultReg)
|
||||
it += IRInstruction(Opcode.NEG, vmDt, reg1=register)
|
||||
it += IRInstruction(Opcode.STOREI, vmDt, reg1=register, reg2=tr.resultReg)
|
||||
}
|
||||
}
|
||||
"~" -> {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADI, vmDt, reg1=register, reg2=tr.resultReg)
|
||||
it += IRInstruction(Opcode.INV, vmDt, reg1=register)
|
||||
it += IRInstruction(Opcode.STOREI, vmDt, reg1=register, reg2=tr.resultReg)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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?
|
||||
else -> throw AssemblyError("weird prefix operator")
|
||||
}
|
||||
}
|
||||
return Ok(result)
|
||||
}
|
||||
|
||||
private fun inplacePrefixArray(operator: String, array: PtArrayIndexer): IRCodeChunks {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val vmDt = irType(array.type)
|
||||
val constIndex = array.index.asConstInteger()
|
||||
|
||||
fun loadIndex(): Int {
|
||||
val tr = expressionEval.translateExpression(array.index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
if(!array.splitWords && eltSize>1)
|
||||
result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
|
||||
return tr.resultReg
|
||||
}
|
||||
|
||||
if(array.splitWords) {
|
||||
// handle split LSB/MSB arrays
|
||||
when(operator) {
|
||||
"+" -> { }
|
||||
"-" -> {
|
||||
val skipCarryLabel = codeGen.createLabelName()
|
||||
if(constIndex!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex), null)
|
||||
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel), null)
|
||||
addInstr(result, IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), null)
|
||||
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), skipCarryLabel)
|
||||
} else {
|
||||
val indexReg = loadIndex()
|
||||
val registerLsb = codeGen.registers.nextFree()
|
||||
val registerMsb = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerLsb)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
|
||||
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerMsb)
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = registerLsb, immediate = 0)
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel)
|
||||
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1 = registerMsb)
|
||||
}
|
||||
result += IRCodeChunk(skipCarryLabel, null).also {
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
|
||||
}
|
||||
}
|
||||
}
|
||||
"~" -> {
|
||||
if(constIndex!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex), null)
|
||||
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), null)
|
||||
} else {
|
||||
val indexReg = loadIndex()
|
||||
val register = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
|
||||
it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird prefix operator")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// normal array.
|
||||
|
||||
when(operator) {
|
||||
"+" -> { }
|
||||
"-" -> {
|
||||
code += if(address!=null)
|
||||
IRInstruction(Opcode.NEGM, vmDt, address = address)
|
||||
else
|
||||
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = symbol)
|
||||
if(constIndex!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.NEGM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||
} else {
|
||||
val indexReg = loadIndex()
|
||||
val register = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
it += IRInstruction(Opcode.NEG, vmDt, reg1 = register)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
"~" -> {
|
||||
val regMask = codeGen.registers.nextFree()
|
||||
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff
|
||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, immediate = mask)
|
||||
code += if(address!=null)
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, address = address)
|
||||
else
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = symbol)
|
||||
if(constIndex!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.INVM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||
} else {
|
||||
val indexReg = loadIndex()
|
||||
val register = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
it += IRInstruction(Opcode.INV, vmDt, reg1 = register)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
"not" -> {
|
||||
// TODO: in boolean branch, is 'not' handled ok like this?
|
||||
val register = codeGen.registers.nextFree()
|
||||
if(constIndex!=null) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=register, immediate = 1)
|
||||
it += IRInstruction(Opcode.XORM, vmDt, reg1=register, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
} else {
|
||||
val indexReg = loadIndex()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
it += IRInstruction(Opcode.XOR, vmDt, reg1 = register, immediate = 1)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird prefix operator")
|
||||
}
|
||||
return listOf(code)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
|
||||
@@ -202,36 +302,17 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val variable = targetArray.variable.name
|
||||
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type)
|
||||
|
||||
if(targetArray.usesPointerVariable) {
|
||||
if(itemsize!=1)
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
if(targetArray.index.type!=DataType.UBYTE)
|
||||
throw AssemblyError("non-array var indexing requires bytes index")
|
||||
val tr = expressionEval.translateExpression(targetArray.index)
|
||||
val idxReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(zero) {
|
||||
// there's no STOREZIX instruction
|
||||
valueRegister = codeGen.registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, targetDt, reg1=valueRegister, immediate = 0)
|
||||
}
|
||||
code += IRInstruction(Opcode.STOREIX, targetDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
|
||||
result += code
|
||||
return result
|
||||
}
|
||||
|
||||
val fixedIndex = constIntValue(targetArray.index)
|
||||
val arrayLength = codeGen.symbolTable.getLength(targetArray.variable.name)
|
||||
if(zero) {
|
||||
if(fixedIndex!=null) {
|
||||
val chunk = IRCodeChunk(null, null).also {
|
||||
if(targetArray.splitWords) {
|
||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb+$fixedIndex")
|
||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb+$fixedIndex")
|
||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
|
||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
|
||||
}
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = "$variable+${fixedIndex*itemsize}")
|
||||
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
|
||||
}
|
||||
result += chunk
|
||||
} else {
|
||||
@@ -251,7 +332,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(fixedIndex!=null) {
|
||||
val offset = fixedIndex*itemsize
|
||||
val chunk = IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset")
|
||||
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = variable, symbolOffset = offset)
|
||||
}
|
||||
result += chunk
|
||||
} else {
|
||||
@@ -266,12 +347,12 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val chunk = IRCodeChunk(null, null).also {
|
||||
if(targetArray.splitWords) {
|
||||
val msbReg = codeGen.registers.nextFree()
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueRegister, immediate = arrayLength, labelSymbol = "${variable}_lsb+$fixedIndex")
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueRegister, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
|
||||
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = msbReg, immediate = arrayLength, labelSymbol = "${variable}_msb+$fixedIndex")
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = msbReg, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
|
||||
}
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = "$variable+${fixedIndex*itemsize}")
|
||||
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
|
||||
}
|
||||
result += chunk
|
||||
} else {
|
||||
@@ -302,18 +383,47 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg) }
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(targetMemory.address is PtNumber) {
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, reg1=valueRegister, address=(targetMemory.address as PtNumber).number.toInt()) }
|
||||
result += chunk
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg) }
|
||||
val constAddress = targetMemory.address as? PtNumber
|
||||
if(constAddress!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, targetDt, reg1=valueRegister, address=constAddress.number.toInt()), null)
|
||||
return result
|
||||
}
|
||||
val ptrWithOffset = targetMemory.address as? PtBinaryExpression
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
|
||||
// STOREIX only works with byte index.
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val offsetReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
|
||||
it += IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=offsetReg, labelSymbol = ptrName)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
|
||||
&& (ptrWithOffset.right.type in ByteDatatypes || offsetTypecast?.value?.type in ByteDatatypes)) {
|
||||
// STOREIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type in ByteDatatypes)
|
||||
expressionEval.translateExpression(offsetTypecast!!.value)
|
||||
else
|
||||
expressionEval.translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
return result
|
||||
}
|
||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg), null)
|
||||
return result
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
@@ -20,12 +20,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call)
|
||||
"divmod__ubyte" -> funcDivmod(call, IRDataType.BYTE)
|
||||
"divmod__uword" -> funcDivmod(call, IRDataType.WORD)
|
||||
"pop" -> funcPop(call)
|
||||
"popw" -> funcPopw(call)
|
||||
"push" -> funcPush(call)
|
||||
"pushw" -> funcPushw(call)
|
||||
"rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
|
||||
"callfar" -> funcCallfar(call)
|
||||
"call" -> funcCall(call)
|
||||
"msb" -> funcMsb(call)
|
||||
"lsb" -> funcLsb(call)
|
||||
"memory" -> funcMemory(call)
|
||||
@@ -44,10 +41,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"reverse" -> funcReverse(call)
|
||||
"setlsb" -> funcSetLsbMsb(call, false)
|
||||
"setmsb" -> funcSetLsbMsb(call, true)
|
||||
"rol" -> funcRolRor(Opcode.ROXL, call)
|
||||
"ror" -> funcRolRor(Opcode.ROXR, call)
|
||||
"rol2" -> funcRolRor(Opcode.ROL, call)
|
||||
"ror2" -> funcRolRor(Opcode.ROR, call)
|
||||
"rol" -> funcRolRor(call)
|
||||
"ror" -> funcRolRor(call)
|
||||
"rol2" -> funcRolRor(call)
|
||||
"ror2" -> funcRolRor(call)
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
||||
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
|
||||
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
||||
@@ -71,6 +68,17 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcCall(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val addressTr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, addressTr, addressTr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.CALLI, reg1 = addressTr.resultReg), null)
|
||||
if(call.void)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
else
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, codeGen.registers.nextFree(), -1) // TODO actually the result is returned in CPU registers AY...
|
||||
}
|
||||
|
||||
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
@@ -140,6 +148,31 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
private fun funcAny(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val arrayName = call.args[0] as PtIdentifier
|
||||
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
|
||||
if(arrayName.type in SplitWordArrayTypes) {
|
||||
// any(lsb-array) or any(msb-array)
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val trLsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_lsb", DataType.ARRAY_UB, call.position))
|
||||
addToResult(result, trLsb, trLsb.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
|
||||
result += codeGen.makeSyscall(IMSyscall.ANY_BYTE, listOf(IRDataType.WORD to trLsb.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to trLsb.resultReg)
|
||||
val shortcircuitLabel = codeGen.createLabelName()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = trLsb.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = shortcircuitLabel)
|
||||
it += IRInstruction(Opcode.PREPARECALL, immediate = 2)
|
||||
}
|
||||
val trMsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_msb", DataType.ARRAY_UB, call.position))
|
||||
addToResult(result, trMsb, trMsb.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
|
||||
result += codeGen.makeSyscall(IMSyscall.ANY_BYTE, listOf(IRDataType.WORD to trMsb.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to trMsb.resultReg)
|
||||
addInstr(result, IRInstruction(Opcode.ORR, IRDataType.BYTE, reg1=trLsb.resultReg, reg2=trMsb.resultReg), null)
|
||||
result += IRCodeChunk(shortcircuitLabel, null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, trLsb.resultReg, -1)
|
||||
}
|
||||
|
||||
val syscall =
|
||||
when (arrayName.type) {
|
||||
DataType.ARRAY_UB,
|
||||
@@ -149,11 +182,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
DataType.ARRAY_F -> IMSyscall.ANY_FLOAT
|
||||
else -> throw IllegalArgumentException("weird type")
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
val tr = exprGen.translateExpression(arrayName)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
|
||||
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||
@@ -162,6 +193,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
private fun funcAll(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val arrayName = call.args[0] as PtIdentifier
|
||||
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
|
||||
|
||||
if(arrayName.type in SplitWordArrayTypes) {
|
||||
// this is a bit complicated to calculate.... have to check all recombined (lsb,msb) words for $0000
|
||||
TODO("all(split words $arrayName)")
|
||||
}
|
||||
|
||||
val syscall =
|
||||
when(arrayName.type) {
|
||||
DataType.ARRAY_UB,
|
||||
@@ -173,7 +210,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
val tr = exprGen.translateExpression(arrayName)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
|
||||
@@ -223,14 +260,21 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val vmDt = irType(call.type)
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SGN, vmDt, reg1 = resultReg, reg2 = tr.resultReg)
|
||||
|
||||
if(tr.dt==IRDataType.FLOAT) {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SGN, tr.dt, reg1 = resultReg, fpReg1 = tr.resultFpReg)
|
||||
}
|
||||
} else {
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SGN, tr.dt, reg1 = resultReg, reg2 = tr.resultReg)
|
||||
}
|
||||
}
|
||||
return ExpressionCodeResult(result, vmDt, resultReg, -1)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcSqrt(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
@@ -262,64 +306,40 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt for sqrt")
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcPop(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val code = IRCodeChunk(null, null)
|
||||
val reg = codeGen.registers.nextFree()
|
||||
code += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=reg)
|
||||
val result = mutableListOf<IRCodeChunkBase>(code)
|
||||
result += assignRegisterTo(call.args.single(), reg)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, reg, -1)
|
||||
}
|
||||
|
||||
private fun funcPopw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val code = IRCodeChunk(null, null)
|
||||
val reg = codeGen.registers.nextFree()
|
||||
code += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=reg)
|
||||
val result = mutableListOf<IRCodeChunkBase>(code)
|
||||
result += assignRegisterTo(call.args.single(), reg)
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, reg, -1)
|
||||
}
|
||||
|
||||
private fun funcPush(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=tr.resultReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
|
||||
private fun funcPushw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1 = tr.resultReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
|
||||
private fun funcReverse(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val arrayName = call.args[0] as PtIdentifier
|
||||
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
if(arrayName.type in SplitWordArrayTypes) {
|
||||
// reverse the lsb and msb arrays both, independently
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val trLsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_lsb", DataType.ARRAY_UB, call.position))
|
||||
addToResult(result, trLsb, trLsb.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
|
||||
result += codeGen.makeSyscall(IMSyscall.REVERSE_BYTES, listOf(IRDataType.WORD to trLsb.resultReg, IRDataType.BYTE to lengthReg), null)
|
||||
val trMsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_msb", DataType.ARRAY_UB, call.position))
|
||||
addToResult(result, trMsb, trMsb.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
|
||||
result += codeGen.makeSyscall(IMSyscall.REVERSE_BYTES, listOf(IRDataType.WORD to trMsb.resultReg, IRDataType.BYTE to lengthReg), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
|
||||
val syscall =
|
||||
when(arrayName.type) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS
|
||||
DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS
|
||||
in SplitWordArrayTypes -> TODO("split word reverse")
|
||||
else -> throw IllegalArgumentException("weird type to reverse")
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
val tr = exprGen.translateExpression(arrayName)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
|
||||
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
@@ -341,7 +361,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
val tr = exprGen.translateExpression(arrayName)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
|
||||
@@ -595,7 +615,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
return exprGen.translateExpression(call.args.single())
|
||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||
// TODO to be more strict, maybe we *should* introduce a new result register that is of type .b?
|
||||
// ....To be more strict, maybe we should introduce a new result register that is of type .b?
|
||||
}
|
||||
|
||||
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
@@ -603,22 +623,58 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg)
|
||||
}
|
||||
addInstr(result, IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null)
|
||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val vmDt = irType(call.args[0].type)
|
||||
private fun funcRolRor(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(opcode, vmDt, reg1 = tr.resultReg)
|
||||
val arg = call.args[0]
|
||||
val vmDt = irType(arg.type)
|
||||
val opcodeMemAndReg = when(call.name) {
|
||||
"rol" -> Opcode.ROXLM to Opcode.ROXL
|
||||
"ror" -> Opcode.ROXRM to Opcode.ROXR
|
||||
"rol2" -> Opcode.ROLM to Opcode.ROL
|
||||
"ror2" -> Opcode.RORM to Opcode.ROR
|
||||
else -> throw AssemblyError("wrong func")
|
||||
}
|
||||
result += assignRegisterTo(call.args[0], tr.resultReg)
|
||||
|
||||
val ident = arg as? PtIdentifier
|
||||
if(ident!=null) {
|
||||
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = ident.name), null)
|
||||
return ExpressionCodeResult(result, vmDt, -1, -1)
|
||||
}
|
||||
|
||||
val memAddr = (arg as? PtMemoryByte)?.address?.asConstInteger()
|
||||
if(memAddr!=null) {
|
||||
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, address = memAddr), null)
|
||||
return ExpressionCodeResult(result, vmDt, -1, -1)
|
||||
}
|
||||
|
||||
val arr = (arg as? PtArrayIndexer)
|
||||
val index = arr?.index?.asConstInteger()
|
||||
if(arr!=null && index!=null) {
|
||||
val variable = arr.variable.name
|
||||
val itemsize = codeGen.program.memsizer.memorySize(arr.type)
|
||||
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = index*itemsize), null)
|
||||
return ExpressionCodeResult(result, vmDt, -1, -1)
|
||||
}
|
||||
|
||||
val opcode = opcodeMemAndReg.second
|
||||
val saveCarry = opcode in OpcodesThatDependOnCarry && !arg.isSimple()
|
||||
if(saveCarry)
|
||||
addInstr(result, IRInstruction(Opcode.PUSHST), null) // save Carry
|
||||
val tr = exprGen.translateExpression(arg)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
if(saveCarry)
|
||||
addInstr(result, IRInstruction(Opcode.POPST), null)
|
||||
addInstr(result, IRInstruction(opcode, vmDt, reg1 = tr.resultReg), null)
|
||||
if(saveCarry)
|
||||
addInstr(result, IRInstruction(Opcode.PUSHST), null) // save Carry
|
||||
result += assignRegisterTo(arg, tr.resultReg)
|
||||
if(saveCarry)
|
||||
addInstr(result, IRInstruction(Opcode.POPST), null)
|
||||
return ExpressionCodeResult(result, vmDt, -1, -1)
|
||||
}
|
||||
|
||||
@@ -649,7 +705,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
require(!target.usesPointerVariable)
|
||||
if(target.splitWords) {
|
||||
// lsb/msb in split arrays, element index 'size' is always 1
|
||||
val constIndex = target.index.asConstInteger()
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -18,7 +18,6 @@ class IRCodeGen(
|
||||
private val expressionEval = ExpressionGen(this)
|
||||
private val builtinFuncGen = BuiltinFuncGen(this, expressionEval)
|
||||
private val assignmentGen = AssignmentGen(this, expressionEval)
|
||||
private var irSymbolTable: IRSymbolTable = IRSymbolTable(null)
|
||||
internal val registers = RegisterPool()
|
||||
|
||||
fun generate(): IRProgram {
|
||||
@@ -26,7 +25,7 @@ class IRCodeGen(
|
||||
moveAllNestedSubroutinesToBlockScope()
|
||||
verifyNameScoping(program, symbolTable)
|
||||
|
||||
irSymbolTable = IRSymbolTable(symbolTable)
|
||||
val irSymbolTable = IRSymbolTable.fromStDuringCodegen(symbolTable)
|
||||
val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding)
|
||||
|
||||
// collect global variables initializers
|
||||
@@ -144,19 +143,10 @@ class IRCodeGen(
|
||||
(idx, instr) ->
|
||||
val symbolExpr = instr.labelSymbol
|
||||
if(symbolExpr!=null) {
|
||||
val symbol: String
|
||||
val index: UInt
|
||||
if('+' in symbolExpr) {
|
||||
val operands = symbolExpr.split('+', )
|
||||
symbol = operands[0]
|
||||
index = operands[1].toUInt()
|
||||
} else {
|
||||
symbol = symbolExpr
|
||||
index = 0u
|
||||
}
|
||||
val target = symbolTable.flat[symbol]
|
||||
val index = instr.labelSymbolOffset ?: 0
|
||||
val target = symbolTable.flat[symbolExpr]
|
||||
if (target is StMemVar) {
|
||||
replacements.add(Triple(chunk, idx, target.address+index))
|
||||
replacements.add(Triple(chunk, idx, target.address+index.toUInt()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -316,7 +306,7 @@ class IRCodeGen(
|
||||
}
|
||||
addInstr(result, branchIns, null)
|
||||
} else {
|
||||
val label = if(goto.generatedLabel!=null) goto.generatedLabel else goto.identifier!!.name
|
||||
val label = goto.identifier!!.name
|
||||
val branchIns = when(branch.condition) {
|
||||
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
|
||||
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, labelSymbol = label)
|
||||
@@ -485,8 +475,8 @@ class IRCodeGen(
|
||||
val tmpRegLsb = registers.nextFree()
|
||||
val tmpRegMsb = registers.nextFree()
|
||||
val concatReg = registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, immediate = iterableLength, labelSymbol=iterable.name+"_msb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, immediate = iterableLength, labelSymbol=iterable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, labelSymbol=iterable.name+"_msb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, labelSymbol=iterable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=concatReg, reg2=tmpRegMsb, reg3=tmpRegLsb)
|
||||
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=concatReg, labelSymbol = loopvarSymbol)
|
||||
}
|
||||
@@ -994,8 +984,6 @@ class IRCodeGen(
|
||||
}
|
||||
it += if (goto.address != null)
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, address = goto.address?.toInt())
|
||||
else if (goto.generatedLabel != null)
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = goto.generatedLabel)
|
||||
else
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = goto.identifier!!.name)
|
||||
}
|
||||
@@ -1016,8 +1004,6 @@ class IRCodeGen(
|
||||
|
||||
private fun branchInstr(goto: PtJump, branchOpcode: Opcode) = if (goto.address != null)
|
||||
IRInstruction(branchOpcode, address = goto.address?.toInt())
|
||||
else if (goto.generatedLabel != null)
|
||||
IRInstruction(branchOpcode, labelSymbol = goto.generatedLabel)
|
||||
else
|
||||
IRInstruction(branchOpcode, labelSymbol = goto.identifier!!.name)
|
||||
|
||||
@@ -1031,7 +1017,8 @@ class IRCodeGen(
|
||||
val condition = ifElse.condition as PtBinaryExpression
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val requireCompareZero = leftTr.lastInstruction().opcode !in OpcodesThatSetStatusbits
|
||||
val lastInstruction = leftTr.chunks.last().instructions.lastOrNull()
|
||||
val requireCompareZero = lastInstruction?.opcode !in OpcodesThatSetStatusbits
|
||||
when(condition.operator) {
|
||||
"==" -> {
|
||||
if(requireCompareZero)
|
||||
@@ -1053,8 +1040,6 @@ class IRCodeGen(
|
||||
}
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, address = goto.address?.toInt()), null)
|
||||
else if (goto.generatedLabel != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, labelSymbol = goto.generatedLabel), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, labelSymbol = goto.identifier!!.name), null)
|
||||
}
|
||||
@@ -1102,8 +1087,6 @@ class IRCodeGen(
|
||||
}
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, address = goto.address?.toInt()), null)
|
||||
else if (goto.generatedLabel != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, labelSymbol = goto.generatedLabel), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, labelSymbol = goto.identifier!!.name), null)
|
||||
}
|
||||
@@ -1161,8 +1144,6 @@ class IRCodeGen(
|
||||
} else {
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, address = goto.address?.toInt()), null)
|
||||
else if (goto.generatedLabel != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.generatedLabel), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null)
|
||||
}
|
||||
@@ -1461,27 +1442,26 @@ class IRCodeGen(
|
||||
when(postIncrDecr.operator) {
|
||||
"++" -> {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_lsb+$fixedIndex")
|
||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
|
||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_msb+$fixedIndex")
|
||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
|
||||
}
|
||||
result += IRCodeChunk(skipLabel, null)
|
||||
}
|
||||
"--" -> {
|
||||
val valueReg=registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=valueReg, labelSymbol = "${variable}_lsb+$fixedIndex")
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=valueReg, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
|
||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_msb+$fixedIndex")
|
||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
|
||||
}
|
||||
result += IRCodeChunk(skipLabel, null).also {
|
||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_lsb+$fixedIndex")
|
||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
} else {
|
||||
val arrayLength = symbolTable.getLength(array.variable.name)
|
||||
val indexTr = expressionEval.translateExpression(array.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
val incReg = registers.nextFree()
|
||||
@@ -1489,28 +1469,28 @@ class IRCodeGen(
|
||||
when(postIncrDecr.operator) {
|
||||
"++" -> {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_msb")
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_msb")
|
||||
}
|
||||
result += IRCodeChunk(skipLabel, null)
|
||||
}
|
||||
"--" -> {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_msb")
|
||||
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_msb")
|
||||
}
|
||||
result += IRCodeChunk(skipLabel, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird operator")
|
||||
@@ -1536,38 +1516,60 @@ class IRCodeGen(
|
||||
if(ident!=null) {
|
||||
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = ident.name), null)
|
||||
} else if(memory!=null) {
|
||||
if(memory.address is PtNumber) {
|
||||
val address = (memory.address as PtNumber).number.toInt()
|
||||
addInstr(result, IRInstruction(operationMem, irDt, address = address), null)
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(memory.address)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val constAddress = memory.address as? PtNumber
|
||||
if(constAddress!=null) {
|
||||
addInstr(result, IRInstruction(operationMem, irDt, address = constAddress.number.toInt()), null)
|
||||
return result
|
||||
}
|
||||
val ptrWithOffset = memory.address as? PtBinaryExpression
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
|
||||
// LOADIX only works with byte index.
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val offsetReg = registers.nextFree()
|
||||
val incReg = registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = tr.resultReg)
|
||||
it += IRInstruction(operationRegister, irDt, reg1 = incReg)
|
||||
it += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = tr.resultReg)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
|
||||
it += IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=incReg, reg2=offsetReg, labelSymbol = ptrName)
|
||||
it += IRInstruction(operationRegister, irDt, reg1 = incReg)
|
||||
it += IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=incReg, reg2=offsetReg, labelSymbol = ptrName)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
|
||||
&& (ptrWithOffset.right.type in ByteDatatypes || offsetTypecast?.value?.type in ByteDatatypes)) {
|
||||
// LOADIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type in ByteDatatypes)
|
||||
expressionEval.translateExpression(offsetTypecast!!.value)
|
||||
else
|
||||
expressionEval.translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val incReg = registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=incReg, reg2=tr.resultReg, labelSymbol = ptrName)
|
||||
it += IRInstruction(operationRegister, irDt, reg1 = incReg)
|
||||
it += IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=incReg, reg2=tr.resultReg, labelSymbol = ptrName)
|
||||
}
|
||||
return result
|
||||
}
|
||||
val tr = expressionEval.translateExpression(memory.address)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val incReg = registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = tr.resultReg)
|
||||
it += IRInstruction(operationRegister, irDt, reg1 = incReg)
|
||||
it += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = tr.resultReg)
|
||||
}
|
||||
return result
|
||||
} else if (array!=null) {
|
||||
val variable = array.variable.name
|
||||
val itemsize = program.memsizer.memorySize(array.type)
|
||||
val fixedIndex = constIntValue(array.index)
|
||||
if(fixedIndex!=null) {
|
||||
val offset = fixedIndex*itemsize
|
||||
val indexReg = registers.nextFree()
|
||||
val dataReg = registers.nextFree()
|
||||
if(array.usesPointerVariable) {
|
||||
// we don't have an indirect dec/inc so do it via an intermediate register
|
||||
result += IRCodeChunk(null,null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = offset)
|
||||
it += IRInstruction(Opcode.LOADIX, irDt, reg1 = dataReg, reg2 = indexReg, labelSymbol = variable)
|
||||
it += IRInstruction(operationRegister, irDt, reg1=dataReg)
|
||||
it += IRInstruction(Opcode.STOREIX, irDt, reg1 = dataReg, reg2 = indexReg, labelSymbol = variable)
|
||||
}
|
||||
} else {
|
||||
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = "$variable+$offset"), null)
|
||||
}
|
||||
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = variable, symbolOffset = fixedIndex*itemsize), null)
|
||||
} else {
|
||||
val indexTr = expressionEval.translateExpression(array.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
@@ -1575,15 +1577,9 @@ class IRCodeGen(
|
||||
result += multiplyByConst(IRDataType.BYTE, indexTr.resultReg, itemsize)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val incReg = registers.nextFree()
|
||||
if(array.usesPointerVariable) {
|
||||
it += IRInstruction(Opcode.LOADIX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
it += IRInstruction(operationRegister, irDt, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREIX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
} else {
|
||||
it += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
it += IRInstruction(operationRegister, irDt, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
}
|
||||
it += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
it += IRInstruction(operationRegister, irDt, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
}
|
||||
}
|
||||
} else
|
||||
@@ -1628,9 +1624,7 @@ class IRCodeGen(
|
||||
if(jump.address!=null) {
|
||||
chunk += IRInstruction(Opcode.JUMP, address = jump.address!!.toInt())
|
||||
} else {
|
||||
if (jump.generatedLabel != null)
|
||||
chunk += IRInstruction(Opcode.JUMP, labelSymbol = jump.generatedLabel!!)
|
||||
else if (jump.identifier != null) {
|
||||
if (jump.identifier != null) {
|
||||
val symbol = symbolTable.lookup(jump.identifier!!.name)
|
||||
if(symbol?.type==StNodeType.MEMVAR || symbol?.type==StNodeType.STATICVAR) {
|
||||
val jumpReg = registers.nextFree()
|
||||
@@ -1674,7 +1668,15 @@ class IRCodeGen(
|
||||
}
|
||||
|
||||
private fun translate(block: PtBlock): IRBlock {
|
||||
val irBlock = IRBlock(block.name, block.address, block.library, block.forceOutput, translate(block.alignment), block.position) // no use for other attributes yet?
|
||||
val irBlock = IRBlock(block.name, block.library,
|
||||
IRBlock.Options(
|
||||
block.options.address,
|
||||
block.options.forceOutput,
|
||||
block.options.noSymbolPrefixing,
|
||||
block.options.veraFxMuls,
|
||||
block.options.ignoreUnused,
|
||||
translate(block.options.alignment)
|
||||
), block.position)
|
||||
for (child in block.children) {
|
||||
when(child) {
|
||||
is PtNop -> { /* nothing */ }
|
||||
@@ -1722,7 +1724,7 @@ class IRCodeGen(
|
||||
is PtLabel -> {
|
||||
irBlock += IRCodeChunk(child.name, null)
|
||||
}
|
||||
else -> TODO("weird child node $child")
|
||||
else -> TODO("weird block child node $child")
|
||||
}
|
||||
}
|
||||
return irBlock
|
||||
@@ -1746,7 +1748,7 @@ class IRCodeGen(
|
||||
private var labelSequenceNumber = 0
|
||||
internal fun createLabelName(): String {
|
||||
labelSequenceNumber++
|
||||
return "prog8_label_gen_$labelSequenceNumber"
|
||||
return "label_gen_$labelSequenceNumber"
|
||||
}
|
||||
|
||||
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall): ExpressionCodeResult
|
||||
|
||||
@@ -46,7 +46,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
.map { IndexedValue(it.index, it.value) }
|
||||
val changed = removeNops(chunk1, indexedInstructions)
|
||||
|| replaceConcatZeroMsbWithExt(chunk1, indexedInstructions)
|
||||
|| removeDoubleLoadsAndStores(chunk1, indexedInstructions) // TODO not yet implemented
|
||||
|| removeDoubleLoadsAndStores(chunk1, indexedInstructions)
|
||||
|| removeUselessArithmetic(chunk1, indexedInstructions)
|
||||
|| removeNeedlessCompares(chunk1, indexedInstructions)
|
||||
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|
||||
@@ -324,14 +324,16 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
if(idx>0 && idx<(indexedInstructions.size-1) && ins.opcode==Opcode.CMPI && ins.immediate==0) {
|
||||
val previous = indexedInstructions[idx-1].value
|
||||
if(previous.opcode in OpcodesThatSetStatusbitsIncludingCarry) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
} else if(previous.opcode in OpcodesThatSetStatusbitsButNotCarry) {
|
||||
val next = indexedInstructions[idx+1].value
|
||||
if(next.opcode !in arrayOf(Opcode.BSTCC, Opcode.BSTCS, Opcode.BSTPOS, Opcode.BSTNEG)) {
|
||||
if(previous.reg1==ins.reg1) {
|
||||
if (previous.opcode in OpcodesThatSetStatusbitsIncludingCarry) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
} else if (previous.opcode in OpcodesThatSetStatusbitsButNotCarry) {
|
||||
val next = indexedInstructions[idx + 1].value
|
||||
if (next.opcode !in OpcodesThatDependOnCarry) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -417,7 +419,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
// TODO: detect multiple loads to the same target registers, only keep first (if source is not I/O memory)
|
||||
// TODO: detect multiple stores to the same target, only keep first (if target is not I/O memory)
|
||||
// TODO: detect multiple float ffrom/fto to the same target, only keep first
|
||||
// TODO: detect multiple sequential rnd with same reg1, only keep one
|
||||
// TODO: detect subsequent same xors/nots/negs, remove the pairs completely as they cancel out
|
||||
// TODO: detect multiple same ands, ors; only keep first
|
||||
// TODO: (hard) detect multiple registers being assigned the same value (and not changed) - use only 1 of them
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
@@ -61,6 +60,9 @@ class IRUnusedCodeRemover(
|
||||
irprog.blocks.forEach { block ->
|
||||
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
|
||||
if(sub.isEmpty()) {
|
||||
if(!block.options.ignoreUnused) {
|
||||
errors.info("unused subroutine '${sub.label}'", sub.position)
|
||||
}
|
||||
block.children.remove(sub)
|
||||
irprog.st.removeTree(sub.label)
|
||||
numRemoved++
|
||||
@@ -79,8 +81,8 @@ class IRUnusedCodeRemover(
|
||||
irprog.blocks.forEach { block ->
|
||||
block.children.filterIsInstance<IRAsmSubroutine>().reversed().forEach { sub ->
|
||||
if(sub.isEmpty()) {
|
||||
if(!sub.position.file.startsWith(LIBRARYFILEPREFIX)) {
|
||||
errors.warn("unused asmsubroutine ${sub.label}", sub.position)
|
||||
if(!block.options.ignoreUnused) {
|
||||
errors.info("unused subroutine '${sub.label}'", sub.position)
|
||||
}
|
||||
block.children.remove(sub)
|
||||
irprog.st.removeTree(sub.label)
|
||||
@@ -100,7 +102,7 @@ class IRUnusedCodeRemover(
|
||||
// check if asmsub is called from another asmsub
|
||||
irprog.blocks.asSequence().forEach { block ->
|
||||
block.children.filterIsInstance<IRAsmSubroutine>().forEach { sub ->
|
||||
if (block.forceOutput || block.library)
|
||||
if (block.options.forceOutput || block.library)
|
||||
linkedAsmSubs += sub
|
||||
if (sub.asmChunk.isNotEmpty()) {
|
||||
allSubs.forEach { (label, asmsub) ->
|
||||
@@ -165,7 +167,13 @@ class IRUnusedCodeRemover(
|
||||
it.next?.let { next -> new += next }
|
||||
it.instructions.forEach { instr ->
|
||||
if (instr.branchTarget == null)
|
||||
instr.labelSymbol?.let { label -> allLabeledChunks[label]?.let { chunk -> new += chunk } }
|
||||
instr.labelSymbol?.let { label ->
|
||||
val chunk = allLabeledChunks[label.substringBeforeLast('.')]
|
||||
if(chunk!=null)
|
||||
new+=chunk
|
||||
else
|
||||
allLabeledChunks[label]?.let { new += it }
|
||||
}
|
||||
else
|
||||
new += instr.branchTarget!!
|
||||
}
|
||||
@@ -212,6 +220,16 @@ class IRUnusedCodeRemover(
|
||||
linkedChunks += chunk
|
||||
}
|
||||
|
||||
// make sure that chunks that are only used as a prefix of a label, are also marked as linked
|
||||
linkedChunks.toList().forEach { chunk ->
|
||||
chunk.instructions.forEach {
|
||||
if(it.labelSymbol!=null) {
|
||||
val chunkName = it.labelSymbol!!.substringBeforeLast('.')
|
||||
allLabeledChunks[chunkName]?.let { linkedChunks+=it }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return removeUnlinkedChunks(linkedChunks)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,11 +27,11 @@ internal object DummyStringEncoder : IStringEncoding {
|
||||
}
|
||||
}
|
||||
|
||||
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false):
|
||||
IErrorReporter {
|
||||
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false): IErrorReporter {
|
||||
|
||||
val errors = mutableListOf<String>()
|
||||
val warnings = mutableListOf<String>()
|
||||
val infos = mutableListOf<String>()
|
||||
|
||||
override fun err(msg: String, position: Position) {
|
||||
val text = "${position.toClickableStr()} $msg"
|
||||
@@ -45,6 +45,12 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
warnings.add(text)
|
||||
}
|
||||
|
||||
override fun info(msg: String, position: Position) {
|
||||
val text = "${position.toClickableStr()} $msg"
|
||||
if(text !in infos)
|
||||
infos.add(text)
|
||||
}
|
||||
|
||||
override fun undefined(symbol: List<String>, position: Position) {
|
||||
err("undefined symbol: ${symbol.joinToString(".")}", position)
|
||||
}
|
||||
@@ -52,10 +58,11 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") }
|
||||
errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") }
|
||||
if(throwExceptionAtReportIfErrors)
|
||||
finalizeNumErrors(errors.size, warnings.size)
|
||||
finalizeNumErrors(errors.size, warnings.size, infos.size)
|
||||
if(!keepMessagesAfterReporting) {
|
||||
clear()
|
||||
}
|
||||
@@ -64,5 +71,6 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
fun clear() {
|
||||
errors.clear()
|
||||
warnings.clear()
|
||||
infos.clear()
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import prog8.intermediate.*
|
||||
class TestIRPeepholeOpt: FunSpec({
|
||||
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
|
||||
require(chunks.first().label=="main.start")
|
||||
val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
|
||||
val block = IRBlock("main", false, IRBlock.Options(), Position.DUMMY)
|
||||
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
|
||||
chunks.forEach { sub += it }
|
||||
block += sub
|
||||
@@ -24,7 +24,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
compTarget = target,
|
||||
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
|
||||
)
|
||||
val prog = IRProgram("test", IRSymbolTable(null), options, target)
|
||||
val prog = IRProgram("test", IRSymbolTable(), options, target)
|
||||
prog.addBlock(block)
|
||||
prog.linkChunks()
|
||||
prog.validate()
|
||||
|
||||
@@ -42,7 +42,7 @@ class TestVmCodeGen: FunSpec({
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
|
||||
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
|
||||
@@ -92,7 +92,7 @@ class TestVmCodeGen: FunSpec({
|
||||
program.add(block)
|
||||
|
||||
// define the "cx16.r0" virtual register
|
||||
val cx16block = PtBlock("cx16", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||
program.add(cx16block)
|
||||
|
||||
@@ -121,7 +121,7 @@ class TestVmCodeGen: FunSpec({
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
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)
|
||||
@@ -184,7 +184,7 @@ class TestVmCodeGen: FunSpec({
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
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)
|
||||
@@ -243,7 +243,7 @@ class TestVmCodeGen: FunSpec({
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
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)
|
||||
@@ -251,7 +251,7 @@ class TestVmCodeGen: FunSpec({
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
|
||||
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
|
||||
if1.add(PtNodeGroup())
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
@@ -259,7 +259,7 @@ class TestVmCodeGen: FunSpec({
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
|
||||
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
|
||||
if2.add(PtNodeGroup())
|
||||
sub.add(if2)
|
||||
block.add(sub)
|
||||
@@ -290,7 +290,7 @@ class TestVmCodeGen: FunSpec({
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
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)
|
||||
@@ -353,7 +353,7 @@ class TestVmCodeGen: FunSpec({
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
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)
|
||||
@@ -412,7 +412,7 @@ class TestVmCodeGen: FunSpec({
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
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)
|
||||
@@ -420,7 +420,7 @@ class TestVmCodeGen: FunSpec({
|
||||
cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||
cmp1.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
|
||||
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
|
||||
if1.add(PtNodeGroup())
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
@@ -428,7 +428,7 @@ class TestVmCodeGen: FunSpec({
|
||||
cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||
cmp2.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
|
||||
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
|
||||
if2.add(PtNodeGroup())
|
||||
sub.add(if2)
|
||||
block.add(sub)
|
||||
@@ -452,7 +452,7 @@ class TestVmCodeGen: FunSpec({
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val romsub = PtAsmSub("routine", 0x5000u, setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
|
||||
block.add(romsub)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
|
||||
@@ -27,6 +27,7 @@ dependencies {
|
||||
implementation project(':codeCore')
|
||||
implementation project(':compilerAst')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
}
|
||||
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="compilerAst" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,17 +1,19 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.ExpressionError
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.FunctionCallExpression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IntegerDatatypes
|
||||
import prog8.code.core.Position
|
||||
import kotlin.math.*
|
||||
|
||||
|
||||
class ConstExprEvaluator {
|
||||
|
||||
fun evaluate(left: NumericLiteral, operator: String, right: NumericLiteral): Expression {
|
||||
fun evaluate(left: NumericLiteral, operator: String, right: NumericLiteral): NumericLiteral {
|
||||
try {
|
||||
return when(operator) {
|
||||
"+" -> plus(left, right)
|
||||
@@ -19,9 +21,9 @@ class ConstExprEvaluator {
|
||||
"*" -> multiply(left, right)
|
||||
"/" -> divide(left, right)
|
||||
"%" -> remainder(left, right)
|
||||
"&" -> bitwiseand(left, right)
|
||||
"|" -> bitwiseor(left, right)
|
||||
"^" -> bitwisexor(left, right)
|
||||
"&" -> bitwiseAnd(left, right)
|
||||
"|" -> bitwiseOr(left, right)
|
||||
"^" -> bitwiseXor(left, right)
|
||||
"<" -> NumericLiteral.fromBoolean(left < right, left.position)
|
||||
">" -> NumericLiteral.fromBoolean(left > right, left.position)
|
||||
"<=" -> NumericLiteral.fromBoolean(left <= right, left.position)
|
||||
@@ -30,6 +32,9 @@ class ConstExprEvaluator {
|
||||
"!=" -> NumericLiteral.fromBoolean(left != right, left.position)
|
||||
"<<" -> shiftedleft(left, right)
|
||||
">>" -> shiftedright(left, right)
|
||||
"and" -> logicalAnd(left, right)
|
||||
"or" -> logicalOr(left, right)
|
||||
"xor" -> logicalXor(left, right)
|
||||
else -> throw FatalAstException("const evaluation for invalid operator $operator")
|
||||
}
|
||||
} catch (ax: FatalAstException) {
|
||||
@@ -37,7 +42,7 @@ class ConstExprEvaluator {
|
||||
}
|
||||
}
|
||||
|
||||
private fun shiftedright(left: NumericLiteral, amount: NumericLiteral): Expression {
|
||||
private fun shiftedright(left: NumericLiteral, amount: NumericLiteral): NumericLiteral {
|
||||
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
|
||||
throw ExpressionError("cannot compute $left >> $amount", left.position)
|
||||
val result =
|
||||
@@ -48,52 +53,81 @@ class ConstExprEvaluator {
|
||||
return NumericLiteral(left.type, result.toDouble(), left.position)
|
||||
}
|
||||
|
||||
private fun shiftedleft(left: NumericLiteral, amount: NumericLiteral): Expression {
|
||||
private fun shiftedleft(left: NumericLiteral, amount: NumericLiteral): NumericLiteral {
|
||||
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
|
||||
throw ExpressionError("cannot compute $left << $amount", left.position)
|
||||
val result = left.number.toInt().shl(amount.number.toInt())
|
||||
return NumericLiteral(left.type, result.toDouble(), left.position)
|
||||
// when(left.type) {
|
||||
// DataType.BOOL -> result = result and 1
|
||||
// DataType.UBYTE -> result = result and 255
|
||||
// DataType.BYTE -> result = result.toByte().toInt()
|
||||
// DataType.UWORD -> result = result and 65535
|
||||
// DataType.WORD -> result = result.toShort().toInt()
|
||||
// else -> { /* keep as it is */ }
|
||||
// }
|
||||
return NumericLiteral.optimalNumeric(result.toDouble(), left.position)
|
||||
}
|
||||
|
||||
private fun bitwisexor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type== DataType.UBYTE) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral(DataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.UWORD) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral(DataType.UWORD, (left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
|
||||
return NumericLiteral(DataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.LONG) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left ^ $right", left.position)
|
||||
}
|
||||
|
||||
private fun bitwiseor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type== DataType.UBYTE) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.UWORD) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral(DataType.UWORD, (left.number.toInt() or right.number.toInt()).toDouble(), left.position)
|
||||
return NumericLiteral(DataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.LONG) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left | $right", left.position)
|
||||
}
|
||||
|
||||
private fun bitwiseand(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type== DataType.UBYTE) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral(DataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.UWORD) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral(DataType.UWORD, (left.number.toInt() and right.number.toInt()).toDouble(), left.position)
|
||||
return NumericLiteral(DataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.LONG) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left & $right", left.position)
|
||||
}
|
||||
|
||||
private fun logicalAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral =
|
||||
NumericLiteral.fromBoolean(left.asBooleanValue and right.asBooleanValue, left.position)
|
||||
|
||||
private fun logicalOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral =
|
||||
NumericLiteral.fromBoolean(left.asBooleanValue or right.asBooleanValue, left.position)
|
||||
|
||||
private fun logicalXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral =
|
||||
NumericLiteral.fromBoolean(left.asBooleanValue xor right.asBooleanValue, left.position)
|
||||
|
||||
private fun plus(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
val error = "cannot add $left and $right"
|
||||
return when (left.type) {
|
||||
@@ -206,4 +240,154 @@ class ConstExprEvaluator {
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
}
|
||||
|
||||
fun evaluate(call: FunctionCallExpression, program: Program): NumericLiteral? {
|
||||
if(call.target.nameInSource.size!=2)
|
||||
return null // likely a builtin function, or user function, these get evaluated elsewhere
|
||||
val constArgs = call.args.mapNotNull { it.constValue(program) }
|
||||
if(constArgs.size!=call.args.size)
|
||||
return null
|
||||
|
||||
return when(call.target.nameInSource[0]) {
|
||||
"math" -> evalMath(call, constArgs)
|
||||
"floats" -> evalFloats(call, constArgs)
|
||||
"string" -> evalString(call, constArgs)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun evalFloats(func: FunctionCallExpression, args: List<NumericLiteral>): NumericLiteral? {
|
||||
val result= when(func.target.nameInSource[1]) {
|
||||
"pow" -> args[0].number.pow(args[1].number)
|
||||
"sin" -> sin(args[0].number)
|
||||
"cos" -> cos(args[0].number)
|
||||
"tan" -> tan(args[0].number)
|
||||
"atan" -> atan(args[0].number)
|
||||
"ln" -> ln(args[0].number)
|
||||
"log2" -> log2(args[0].number)
|
||||
"rad" -> args[0].number/360.0 * 2 * PI
|
||||
"deg" -> args[0].number/ 2 / PI * 360.0
|
||||
"round" -> round(args[0].number)
|
||||
"floor" -> floor(args[0].number)
|
||||
"ceil" -> ceil(args[0].number)
|
||||
"minf", "min" -> min(args[0].number, args[1].number)
|
||||
"maxf", "max" -> max(args[0].number, args[1].number)
|
||||
"clampf", "clamp" -> {
|
||||
var value = args[0].number
|
||||
val minimum = args[1].number
|
||||
val maximum = args[2].number
|
||||
if(value<minimum)
|
||||
value=minimum
|
||||
if(value<maximum)
|
||||
value
|
||||
else
|
||||
maximum
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
return if(result==null)
|
||||
null
|
||||
else
|
||||
NumericLiteral(DataType.FLOAT, result, func.position)
|
||||
}
|
||||
|
||||
private fun evalMath(func: FunctionCallExpression, args: List<NumericLiteral>): NumericLiteral? {
|
||||
return when(func.target.nameInSource[1]) {
|
||||
"sin8u" -> {
|
||||
val value = truncate(128.0 + 127.5 * sin(args.single().number / 256.0 * 2 * PI))
|
||||
NumericLiteral(DataType.UBYTE, value, func.position)
|
||||
}
|
||||
"cos8u" -> {
|
||||
val value = truncate(128.0 + 127.5 * cos(args.single().number / 256.0 * 2 * PI))
|
||||
NumericLiteral(DataType.UBYTE, value, func.position)
|
||||
}
|
||||
"sin8" -> {
|
||||
val value = truncate(127.0 * sin(args.single().number / 256.0 * 2 * PI))
|
||||
NumericLiteral(DataType.BYTE, value, func.position)
|
||||
}
|
||||
"cos8" -> {
|
||||
val value = truncate(127.0 * cos(args.single().number / 256.0 * 2 * PI))
|
||||
NumericLiteral(DataType.BYTE, value, func.position)
|
||||
}
|
||||
"sinr8u" -> {
|
||||
val value = truncate(128.0 + 127.5 * sin(args.single().number / 180.0 * 2 * PI))
|
||||
NumericLiteral(DataType.UBYTE, value, func.position)
|
||||
}
|
||||
"cosr8u" -> {
|
||||
val value = truncate(128.0 + 127.5 * cos(args.single().number / 180.0 * 2 * PI))
|
||||
NumericLiteral(DataType.UBYTE, value, func.position)
|
||||
}
|
||||
"sinr8" -> {
|
||||
val value = truncate(127.0 * sin(args.single().number / 180.0 * 2 * PI))
|
||||
NumericLiteral(DataType.BYTE, value, func.position)
|
||||
}
|
||||
"cosr8" -> {
|
||||
val value = truncate(127.0 * cos(args.single().number / 180.0 * 2 * PI))
|
||||
NumericLiteral(DataType.BYTE, value, func.position)
|
||||
}
|
||||
"log2" -> {
|
||||
val value = truncate(log2(args.single().number))
|
||||
NumericLiteral(DataType.UBYTE, value, func.position)
|
||||
}
|
||||
"log2w" -> {
|
||||
val value = truncate(log2(args.single().number))
|
||||
NumericLiteral(DataType.UWORD, value, func.position)
|
||||
}
|
||||
"atan2" -> {
|
||||
val x1f = args[0].number
|
||||
val y1f = args[1].number
|
||||
val x2f = args[2].number
|
||||
val y2f = args[3].number
|
||||
var radians = atan2(y2f-y1f, x2f-x1f)
|
||||
if(radians<0)
|
||||
radians+=2*PI
|
||||
NumericLiteral(DataType.UWORD, floor(radians/2.0/PI*256.0), func.position)
|
||||
}
|
||||
"diff" -> {
|
||||
val n1 = args[0].number
|
||||
val n2 = args[1].number
|
||||
val value = if(n1>n2) n1-n2 else n2-n1
|
||||
NumericLiteral(DataType.UBYTE, value, func.position)
|
||||
}
|
||||
"diffw" -> {
|
||||
val n1 = args[0].number
|
||||
val n2 = args[1].number
|
||||
val value = if(n1>n2) n1-n2 else n2-n1
|
||||
NumericLiteral(DataType.UWORD, value, func.position)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun evalString(func: FunctionCallExpression, args: List<NumericLiteral>): NumericLiteral? {
|
||||
return when(func.target.nameInSource[1]) {
|
||||
"isdigit" -> {
|
||||
val char = args[0].number.toInt()
|
||||
NumericLiteral.fromBoolean(char in 48..57, func.position)
|
||||
}
|
||||
"isupper" -> {
|
||||
// shifted petscii has 2 ranges that contain the upper case letters... 97-122 and 193-218
|
||||
val char = args[0].number.toInt()
|
||||
NumericLiteral.fromBoolean(char in 97..122 || char in 193..218, func.position)
|
||||
}
|
||||
"islower" -> {
|
||||
val char = args[0].number.toInt()
|
||||
NumericLiteral.fromBoolean(char in 65..90, func.position)
|
||||
}
|
||||
"isletter" -> {
|
||||
val char = args[0].number.toInt()
|
||||
NumericLiteral.fromBoolean(char in 65..90 || char in 97..122 || char in 193..218, func.position)
|
||||
}
|
||||
"isspace" -> {
|
||||
val char = args[0].number.toInt()
|
||||
NumericLiteral.fromBoolean(char in arrayOf(32, 13, 9, 10, 141, 160), func.position)
|
||||
}
|
||||
"isprint" -> {
|
||||
val char = args[0].number.toInt()
|
||||
NumericLiteral.fromBoolean(char in 32..127 || char>=160, func.position)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,18 +4,23 @@ import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.maySwapOperandOrder
|
||||
import prog8.ast.statements.ForLoop
|
||||
import prog8.ast.statements.RepeatLoop
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.VarDeclType
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.AssociativeOperators
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IErrorReporter
|
||||
import kotlin.math.floor
|
||||
|
||||
|
||||
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
class ConstantFoldingOptimizer(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
|
||||
private val evaluator = ConstExprEvaluator()
|
||||
|
||||
override fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
|
||||
val constAddr = addressOf.constValue(program) ?: return noModifications
|
||||
return listOf(IAstModification.ReplaceNode(addressOf, constAddr, parent))
|
||||
}
|
||||
|
||||
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||
// @( &thing ) --> thing (but only if thing is a byte type!)
|
||||
@@ -27,6 +32,19 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(numLiteral: NumericLiteral, parent: Node): Iterable<IAstModification> {
|
||||
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))
|
||||
if(casted.isValid) {
|
||||
return listOf(IAstModification.ReplaceNode(numLiteral, casted.value!!, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> {
|
||||
val result = containment.constValue(program)
|
||||
if(result!=null)
|
||||
@@ -79,6 +97,8 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
else if(expr.operator=="*" && rightconst!=null && expr.left is StringLiteral) {
|
||||
// mutiply a string.
|
||||
val part = expr.left as StringLiteral
|
||||
if(part.value.isEmpty())
|
||||
errors.warn("resulting string has length zero", part.position)
|
||||
val newStr = StringLiteral(part.value.repeat(rightconst.number.toInt()), part.encoding, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newStr, parent))
|
||||
}
|
||||
@@ -134,9 +154,8 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
}
|
||||
}
|
||||
|
||||
val evaluator = ConstExprEvaluator()
|
||||
|
||||
// const fold when both operands are a const
|
||||
// const fold when both operands are a const.
|
||||
// if in a chained comparison, that one has to be desugared first though.
|
||||
if(leftconst != null && rightconst != null) {
|
||||
val result = evaluator.evaluate(leftconst, expr.operator, rightconst)
|
||||
modifications += IAstModification.ReplaceNode(expr, result, parent)
|
||||
@@ -229,51 +248,13 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
}
|
||||
else if(leftBinExpr.operator=="*" && rightBinExpr.operator=="*"){
|
||||
if (c1 != null && c2 != null && c1==c2) {
|
||||
//(X * C) <plusmin> (Y * C) => (X <plusmin> Y) * C
|
||||
val xwithy = BinaryExpression(leftBinExpr.left, expr.operator, rightBinExpr.left, expr.position)
|
||||
val newExpr = BinaryExpression(xwithy, "*", c1, expr.position)
|
||||
modifications += IAstModification.ReplaceNode(expr, newExpr, parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(rightconst!=null && (expr.operator=="<<" || expr.operator==">>")) {
|
||||
val dt = expr.left.inferType(program)
|
||||
if(dt.isBytes && rightconst.number>=8) {
|
||||
if(dt.istype(DataType.UBYTE)) {
|
||||
val zeroUB = NumericLiteral(DataType.UBYTE, 0.0, expr.position)
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroUB, parent))
|
||||
} else {
|
||||
if(leftconst!=null) {
|
||||
val zeroB = NumericLiteral(DataType.BYTE, 0.0, expr.position)
|
||||
val minusoneB = NumericLiteral(DataType.BYTE, -1.0, expr.position)
|
||||
if(leftconst.number<0.0) {
|
||||
if(expr.operator=="<<")
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroB, parent))
|
||||
else
|
||||
modifications.add(IAstModification.ReplaceNode(expr, minusoneB, parent))
|
||||
} else {
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroB, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(dt.isWords && rightconst.number>=16) {
|
||||
if(dt.istype(DataType.UWORD)) {
|
||||
val zeroUW = NumericLiteral(DataType.UWORD, 0.0, expr.position)
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroUW, parent))
|
||||
} else {
|
||||
if(leftconst!=null) {
|
||||
val zeroW = NumericLiteral(DataType.WORD, 0.0, expr.position)
|
||||
val minusoneW = NumericLiteral(DataType.WORD, -1.0, expr.position)
|
||||
if(leftconst.number<0.0) {
|
||||
if(expr.operator=="<<")
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroW, parent))
|
||||
else
|
||||
modifications.add(IAstModification.ReplaceNode(expr, minusoneW, parent))
|
||||
} else {
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroW, parent))
|
||||
//(X * C) <plusmin> (Y * C) => (X <plusmin> Y) * C (only if types of X and Y are the same!)
|
||||
val xDt = leftBinExpr.left.inferType(program)
|
||||
val yDt = rightBinExpr.left.inferType(program)
|
||||
if(xDt==yDt) {
|
||||
val xwithy = BinaryExpression(leftBinExpr.left, expr.operator, rightBinExpr.left, expr.position)
|
||||
val newExpr = BinaryExpression(xwithy, "*", c1, expr.position)
|
||||
modifications += IAstModification.ReplaceNode(expr, newExpr, parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -313,8 +294,13 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
val constvalue = functionCallExpr.constValue(program)
|
||||
return if(constvalue!=null)
|
||||
listOf(IAstModification.ReplaceNode(functionCallExpr, constvalue, parent))
|
||||
else
|
||||
noModifications
|
||||
else {
|
||||
val const2 = evaluator.evaluate(functionCallExpr, program)
|
||||
return if(const2!=null)
|
||||
listOf(IAstModification.ReplaceNode(functionCallExpr, const2, parent))
|
||||
else
|
||||
noModifications
|
||||
}
|
||||
}
|
||||
|
||||
override fun after(bfc: BuiltinFunctionCall, parent: Node): Iterable<IAstModification> {
|
||||
@@ -336,14 +322,14 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
if(stepLiteral!=null) {
|
||||
val stepCast = stepLiteral.cast(targetDt)
|
||||
if(stepCast.isValid)
|
||||
stepCast.valueOrZero()
|
||||
stepCast.value!!
|
||||
else
|
||||
range.step
|
||||
} else {
|
||||
range.step
|
||||
}
|
||||
|
||||
return RangeExpression(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, range.position)
|
||||
return RangeExpression(fromCast.value!!, toCast.value!!, newStep, range.position)
|
||||
}
|
||||
|
||||
// adjust the datatype of a range expression in for loops to the loop variable.
|
||||
@@ -402,7 +388,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
if(decl.datatype!=DataType.BOOL || valueDt.isnot(DataType.UBYTE)) {
|
||||
val cast = numval.cast(decl.datatype)
|
||||
if (cast.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
|
||||
return listOf(IAstModification.ReplaceNode(numval, cast.value!!, decl))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -419,6 +405,11 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
val constValue = typecast.constValue(program) ?: return noModifications
|
||||
return listOf(IAstModification.ReplaceNode(typecast, constValue, parent))
|
||||
}
|
||||
|
||||
private class ShuffleOperands(val expr: BinaryExpression,
|
||||
val exprOperator: String?,
|
||||
val subExpr: BinaryExpression,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.FatalAstException
|
||||
@@ -9,10 +11,17 @@ import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
import prog8.compiler.CallGraph
|
||||
|
||||
// Fix up the literal value's type to match that of the vardecl
|
||||
// (also check range literal operands types before they get expanded into arrays for instance)
|
||||
class VarConstantValueTypeAdjuster(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
class VarConstantValueTypeAdjuster(
|
||||
private val program: Program,
|
||||
private val options: CompilationOptions,
|
||||
private val errors: IErrorReporter
|
||||
) : AstWalker() {
|
||||
|
||||
private val callGraph by lazy { CallGraph(program) }
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
@@ -25,19 +34,86 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
|
||||
&& declConstValue.type != decl.datatype) {
|
||||
// avoid silent float roundings
|
||||
if(decl.datatype in IntegerDatatypes && declConstValue.type == DataType.FLOAT) {
|
||||
errors.err("refused rounding of float to avoid loss of precision", decl.value!!.position)
|
||||
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
|
||||
declConstValue.linkParents(decl)
|
||||
val cast = declConstValue.cast(decl.datatype)
|
||||
if (cast.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.valueOrZero(), decl))
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.value!!, decl))
|
||||
}
|
||||
}
|
||||
} catch (x: UndefinedSymbolError) {
|
||||
errors.err(x.message, x.position)
|
||||
}
|
||||
|
||||
// replace variables by constants, if possible
|
||||
if(options.optimize) {
|
||||
if (decl.sharedWithAsm || decl.type != VarDeclType.VAR || decl.origin != VarDeclOrigin.USERCODE || decl.datatype !in NumericDatatypes)
|
||||
return noModifications
|
||||
if (decl.value != null && decl.value!!.constValue(program) == null)
|
||||
return noModifications
|
||||
val usages = callGraph.usages(decl)
|
||||
val (writes, reads) = usages
|
||||
.partition {
|
||||
it is InlineAssembly // can't really tell if it's written to or only read, assume the worst
|
||||
|| it.parent is AssignTarget
|
||||
|| it.parent is ForLoop
|
||||
|| it.parent is AddressOf
|
||||
|| (it.parent as? IFunctionCall)?.target?.nameInSource?.singleOrNull() in InplaceModifyingBuiltinFunctions
|
||||
}
|
||||
val singleAssignment =
|
||||
writes.singleOrNull()?.parent?.parent as? Assignment ?: writes.singleOrNull()?.parent as? Assignment
|
||||
if (singleAssignment == null) {
|
||||
if (writes.isEmpty()) {
|
||||
if(reads.isEmpty()) {
|
||||
// variable is never used AT ALL so we just remove it altogether
|
||||
if("ignore_unused" !in decl.definingBlock.options())
|
||||
errors.info("removing unused variable '${decl.name}'", decl.position)
|
||||
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
|
||||
}
|
||||
val declValue = decl.value?.constValue(program)
|
||||
if (declValue != null) {
|
||||
// variable is never written to, so it can be replaced with a constant, IF the value is a constant
|
||||
errors.info("variable is never written to and was replaced by a constant", decl.position)
|
||||
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.splitArray, decl.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(decl, const, parent),
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (singleAssignment.origin == AssignmentOrigin.VARINIT && singleAssignment.value.constValue(program) != null) {
|
||||
if(reads.isEmpty()) {
|
||||
// variable is never used AT ALL so we just remove it altogether, including the single assignment
|
||||
if("ignore_unused" !in decl.definingBlock.options())
|
||||
errors.info("removing unused variable '${decl.name}'", decl.position)
|
||||
return listOf(
|
||||
IAstModification.Remove(decl, parent as IStatementContainer),
|
||||
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
// variable only has a single write and it is the initialization value, so it can be replaced with a constant, IF the value is a constant
|
||||
errors.info("variable is never written to and was replaced by a constant", decl.position)
|
||||
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(decl, const, parent),
|
||||
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
}
|
||||
/*
|
||||
TODO: need to check if there are no variable usages between the declaration and the assignment (because these rely on the original initialization value)
|
||||
if(writes.size==2) {
|
||||
val firstAssignment = writes[0].parent as? Assignment
|
||||
val secondAssignment = writes[1].parent as? Assignment
|
||||
if(firstAssignment?.origin==AssignmentOrigin.VARINIT && secondAssignment?.value?.constValue(program)!=null) {
|
||||
errors.warn("variable is only assigned once here, consider using this as the initialization value in the declaration instead", secondAssignment.position)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
@@ -196,6 +272,14 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
|
||||
// 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() {
|
||||
|
||||
override fun before(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
|
||||
val constValue = addressOf.constValue(program)
|
||||
if(constValue!=null) {
|
||||
return listOf(IAstModification.ReplaceNode(addressOf, constValue, parent))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||
// replace identifiers that refer to const value, with the value itself
|
||||
// if it's a simple type and if it's not a left hand side variable
|
||||
@@ -228,16 +312,20 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
listOf(IAstModification.ReplaceNode(arrayIdx, memread, arrayIdx.parent))
|
||||
}
|
||||
}
|
||||
return when (cval.type) {
|
||||
in NumericDatatypes -> listOf(
|
||||
IAstModification.ReplaceNode(
|
||||
identifier,
|
||||
NumericLiteral(cval.type, cval.number, identifier.position),
|
||||
identifier.parent
|
||||
when (cval.type) {
|
||||
in NumericDatatypes -> {
|
||||
if(parent is AddressOf)
|
||||
return noModifications // cannot replace the identifier INSIDE the addr-of here, let's do it later.
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(
|
||||
identifier,
|
||||
NumericLiteral(cval.type, cval.number, identifier.position),
|
||||
identifier.parent
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
in PassByReferenceDatatypes -> throw InternalCompilerException("pass-by-reference type should not be considered a constant")
|
||||
else -> noModifications
|
||||
else -> return noModifications
|
||||
}
|
||||
} catch (x: UndefinedSymbolError) {
|
||||
errors.err(x.message, x.position)
|
||||
@@ -245,14 +333,14 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
}
|
||||
}
|
||||
|
||||
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
// the initializer value can't refer to the variable itself (recursive definition)
|
||||
if(decl.value?.referencesIdentifier(listOf(decl.name)) == true || decl.arraysize?.indexExpr?.referencesIdentifier(listOf(decl.name)) == true) {
|
||||
errors.err("recursive var declaration", decl.position)
|
||||
return noModifications
|
||||
}
|
||||
|
||||
if(decl.isArray && decl.type==VarDeclType.MEMORY) {
|
||||
if(decl.isArray && decl.type==VarDeclType.MEMORY && decl.value !is IdentifierReference) {
|
||||
val memaddr = decl.value?.constValue(program)
|
||||
if(memaddr!=null && memaddr !== decl.value) {
|
||||
return listOf(IAstModification.SetExpression(
|
||||
|
||||
@@ -13,7 +13,6 @@ import prog8.ast.statements.Jump
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.VMTarget
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.log2
|
||||
import kotlin.math.pow
|
||||
@@ -33,8 +32,8 @@ class ExpressionSimplifier(private val program: Program,
|
||||
val literal = typecast.expression as? NumericLiteral
|
||||
if (literal != null) {
|
||||
val newLiteral = literal.cast(typecast.type)
|
||||
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
|
||||
mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
|
||||
if (newLiteral.isValid && newLiteral.value!! !== literal) {
|
||||
mods += IAstModification.ReplaceNode(typecast, newLiteral.value!!, parent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,30 +65,36 @@ class ExpressionSimplifier(private val program: Program,
|
||||
)
|
||||
}
|
||||
if(elsepart.statements.singleOrNull() is Jump) {
|
||||
val invertedCondition = invertCondition(ifElse.condition)
|
||||
if(invertedCondition!=null) {
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
|
||||
IAstModification.InsertAfter(ifElse, truepart, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse),
|
||||
IAstModification.ReplaceNode(truepart, elsepart, ifElse)
|
||||
)
|
||||
}
|
||||
val invertedCondition = invertCondition(ifElse.condition, program)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
|
||||
IAstModification.InsertAfter(ifElse, truepart, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse),
|
||||
IAstModification.ReplaceNode(truepart, elsepart, ifElse)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if(compTarget.name!=VMTarget.NAME) {
|
||||
val booleanCondition = ifElse.condition as? BinaryExpression
|
||||
if(booleanCondition!=null && booleanCondition.operator=="&") {
|
||||
// special optimization of WORD & $ff00 -> just and the msb of WORD with $ff
|
||||
val rightNum = booleanCondition.right as? NumericLiteral
|
||||
if(rightNum!=null && rightNum.type==DataType.UWORD && (rightNum.number.toInt() and 0x00ff)==0) {
|
||||
val booleanCondition = ifElse.condition as? BinaryExpression
|
||||
if(booleanCondition!=null && booleanCondition.operator=="&") {
|
||||
val rightNum = booleanCondition.right as? NumericLiteral
|
||||
if (rightNum!=null && rightNum.type==DataType.UWORD) {
|
||||
if ((rightNum.number.toInt() and 0x00ff) == 0) {
|
||||
// if WORD & $xx00 -> if msb(WORD) & $xx
|
||||
val msb = BuiltinFunctionCall(IdentifierReference(listOf("msb"), booleanCondition.left.position), mutableListOf(booleanCondition.left), booleanCondition.left.position)
|
||||
val bytevalue = NumericLiteral(DataType.UBYTE, (rightNum.number.toInt() shr 8).toDouble(), booleanCondition.right.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(booleanCondition.left, msb, booleanCondition),
|
||||
IAstModification.ReplaceNode(booleanCondition.right, bytevalue, booleanCondition))
|
||||
}
|
||||
else if ((rightNum.number.toInt() and 0xff00) == 0) {
|
||||
// if WORD & $00ff -> if lsb(WORD) & $ff
|
||||
val lsb = BuiltinFunctionCall(IdentifierReference(listOf("lsb"), booleanCondition.left.position), mutableListOf(booleanCondition.left), booleanCondition.left.position)
|
||||
val bytevalue = NumericLiteral(DataType.UBYTE, rightNum.number, booleanCondition.right.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(booleanCondition.left, lsb, booleanCondition),
|
||||
IAstModification.ReplaceNode(booleanCondition.right, bytevalue, booleanCondition))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +102,10 @@ class ExpressionSimplifier(private val program: Program,
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
val newExpr = applyAbsorptionLaws(expr)
|
||||
if(newExpr!=null)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
|
||||
val leftVal = expr.left.constValue(program)
|
||||
val rightVal = expr.right.constValue(program)
|
||||
|
||||
@@ -158,8 +167,8 @@ class ExpressionSimplifier(private val program: Program,
|
||||
val y = determineY(x, leftBinExpr)
|
||||
if (y != null) {
|
||||
val yPlus1 = BinaryExpression(y, "+", NumericLiteral(leftDt, 1.0, y.position), y.position)
|
||||
val newExpr = BinaryExpression(x, "*", yPlus1, x.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
val replacement = BinaryExpression(x, "*", yPlus1, x.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, replacement, parent))
|
||||
}
|
||||
} else {
|
||||
// Y*X - X -> X*(Y - 1)
|
||||
@@ -168,8 +177,8 @@ class ExpressionSimplifier(private val program: Program,
|
||||
val y = determineY(x, leftBinExpr)
|
||||
if (y != null) {
|
||||
val yMinus1 = BinaryExpression(y, "-", NumericLiteral(leftDt, 1.0, y.position), y.position)
|
||||
val newExpr = BinaryExpression(x, "*", yMinus1, x.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
val replacement = BinaryExpression(x, "*", yMinus1, x.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, replacement, parent))
|
||||
}
|
||||
}
|
||||
} else if (rightBinExpr?.operator == "*") {
|
||||
@@ -180,8 +189,8 @@ class ExpressionSimplifier(private val program: Program,
|
||||
val y = determineY(x, rightBinExpr)
|
||||
if (y != null) {
|
||||
val yPlus1 = BinaryExpression(y, "+", NumericLiteral.optimalInteger(1, y.position), y.position)
|
||||
val newExpr = BinaryExpression(x, "*", yPlus1, x.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
val replacement = BinaryExpression(x, "*", yPlus1, x.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, replacement, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,7 +279,7 @@ class ExpressionSimplifier(private val program: Program,
|
||||
|
||||
// simplify when a term is constant and directly determines the outcome
|
||||
val constFalse = NumericLiteral.fromBoolean(false, expr.position)
|
||||
val newExpr: Expression? = when (expr.operator) {
|
||||
val newExpr2 = when (expr.operator) {
|
||||
"|" -> {
|
||||
when {
|
||||
leftVal?.number==0.0 -> expr.right
|
||||
@@ -315,33 +324,102 @@ class ExpressionSimplifier(private val program: Program,
|
||||
}
|
||||
|
||||
if(rightVal!=null && leftDt==DataType.BOOL) {
|
||||
// see if we can replace comparison against true/1 with simpler comparison against zero
|
||||
if (expr.operator == "==") {
|
||||
if (rightVal.number == 1.0) {
|
||||
val zero = NumericLiteral(DataType.UBYTE, 0.0, expr.right.position)
|
||||
return listOf(
|
||||
IAstModification.SetExpression({expr.operator="!="}, expr, parent),
|
||||
IAstModification.ReplaceNode(expr.right, zero, expr)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (expr.operator == "!=") {
|
||||
if (rightVal.number == 1.0) {
|
||||
val zero = NumericLiteral(DataType.UBYTE, 0.0, expr.right.position)
|
||||
return listOf(
|
||||
IAstModification.SetExpression({expr.operator="=="}, expr, parent),
|
||||
IAstModification.ReplaceNode(expr.right, zero, expr)
|
||||
)
|
||||
// boolean compare against a number -> just keep the boolean, no compare
|
||||
if(expr.operator=="==" || expr.operator=="!=") {
|
||||
val test = if (expr.operator == "==") rightVal.asBooleanValue else !rightVal.asBooleanValue
|
||||
return if (test) {
|
||||
listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
} else {
|
||||
listOf(IAstModification.ReplaceNode(expr, invertCondition(expr.left, program), parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(newExpr != null)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
if(newExpr2 != null)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr2, parent))
|
||||
|
||||
if (rightVal!=null && (expr.operator == "==" || expr.operator == "!=")) {
|
||||
val bitwise = expr.left as? BinaryExpression
|
||||
if(bitwise!=null && bitwise.operator=="&" && bitwise.inferType(program).isWords) {
|
||||
val andNum = (bitwise.right as? NumericLiteral)?.number?.toInt()
|
||||
if (andNum!=null) {
|
||||
if ((andNum and 0x00ff) == 0) {
|
||||
// (WORD & $xx00)==y -> (msb(WORD) & $xx)==y
|
||||
val msb = BuiltinFunctionCall(IdentifierReference(listOf("msb"), bitwise.left.position), mutableListOf(bitwise.left), bitwise.left.position)
|
||||
val bytevalue = NumericLiteral(DataType.UBYTE, (andNum shr 8).toDouble(), bitwise.right.position)
|
||||
val rightvalByte = NumericLiteral(DataType.UBYTE, (rightVal.number.toInt() shr 8).toDouble(), rightVal.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(bitwise.left, msb, bitwise),
|
||||
IAstModification.ReplaceNode(bitwise.right, bytevalue, bitwise),
|
||||
IAstModification.ReplaceNode(expr.right, rightvalByte, expr)
|
||||
)
|
||||
}
|
||||
else if((andNum and 0xff00) == 0) {
|
||||
// (WORD & $00xx)==y -> (lsb(WORD) & $xx)==y
|
||||
val lsb = BuiltinFunctionCall(IdentifierReference(listOf("lsb"), bitwise.left.position), mutableListOf(bitwise.left), bitwise.left.position)
|
||||
val bytevalue = NumericLiteral(DataType.UBYTE, andNum.toDouble(), bitwise.right.position)
|
||||
val rightvalByte = NumericLiteral(DataType.UBYTE, (rightVal.number.toInt() and 255).toDouble(), rightVal.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(bitwise.left, lsb, bitwise),
|
||||
IAstModification.ReplaceNode(bitwise.right, bytevalue, bitwise),
|
||||
IAstModification.ReplaceNode(expr.right, rightvalByte, expr)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator=="not") {
|
||||
// not X <compare> Y -> X <invertedcompare> Y
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
||||
private fun applyAbsorptionLaws(expr: BinaryExpression): Expression? {
|
||||
val rightB = expr.right as? BinaryExpression
|
||||
if(rightB!=null) {
|
||||
// absorption laws: a or (a and b) --> a, a and (a or b) --> a
|
||||
if(expr.operator=="or" && rightB.operator=="and") {
|
||||
if(expr.left isSameAs rightB.left || expr.left isSameAs rightB.right) {
|
||||
return expr.left
|
||||
}
|
||||
}
|
||||
else if(expr.operator=="and" && rightB.operator=="or") {
|
||||
if(expr.left isSameAs rightB.left || expr.left isSameAs rightB.right) {
|
||||
return expr.left
|
||||
}
|
||||
}
|
||||
}
|
||||
val leftB = expr.left as? BinaryExpression
|
||||
if(leftB!=null) {
|
||||
// absorption laws: (a and b) or a --> a, (a or b) and a --> a
|
||||
if(expr.operator=="or" && leftB.operator=="and") {
|
||||
if(expr.right isSameAs leftB.left || expr.left isSameAs leftB.right) {
|
||||
return expr.right
|
||||
}
|
||||
}
|
||||
else if(expr.operator=="and" && leftB.operator=="or") {
|
||||
if(expr.right isSameAs leftB.left || expr.right isSameAs leftB.right) {
|
||||
return expr.right
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(functionCallExpr.target.nameInSource == listOf("lsb")) {
|
||||
if(functionCallExpr.args.isEmpty())
|
||||
@@ -349,10 +427,15 @@ class ExpressionSimplifier(private val program: Program,
|
||||
val arg = functionCallExpr.args[0]
|
||||
if(arg is TypecastExpression) {
|
||||
val valueDt = arg.expression.inferType(program)
|
||||
if (valueDt istype DataType.BYTE || valueDt istype DataType.UBYTE) {
|
||||
// useless lsb() of byte value that was typecasted to word
|
||||
if (valueDt istype DataType.UBYTE) {
|
||||
// useless lsb() of ubyte value
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, arg.expression, parent))
|
||||
}
|
||||
else if (valueDt istype DataType.BYTE) {
|
||||
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
|
||||
val cast = TypecastExpression(arg.expression, DataType.UBYTE, true, arg.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
} else {
|
||||
if(arg is IdentifierReference && arg.nameInSource.size==2
|
||||
&& arg.nameInSource[0]=="cx16" && arg.nameInSource[1].uppercase() in RegisterOrPair.names) {
|
||||
@@ -361,10 +444,15 @@ class ExpressionSimplifier(private val program: Program,
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, highReg, parent))
|
||||
}
|
||||
val argDt = arg.inferType(program)
|
||||
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
|
||||
if (argDt istype DataType.UBYTE) {
|
||||
// useless lsb() of byte value
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, arg, parent))
|
||||
}
|
||||
else if (argDt istype DataType.BYTE) {
|
||||
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
|
||||
val cast = TypecastExpression(arg, DataType.UBYTE, true, arg.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(functionCallExpr.target.nameInSource == listOf("msb")) {
|
||||
@@ -405,6 +493,14 @@ class ExpressionSimplifier(private val program: Program,
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
}
|
||||
else if(functionCallExpr.target.nameInSource == listOf("string", "contains")) {
|
||||
val target = (functionCallExpr.args[0] as? IdentifierReference)?.targetVarDecl(program)
|
||||
if(target?.value is StringLiteral) {
|
||||
errors.info("for actual strings, use a regular containment check instead: 'char in string'", functionCallExpr.position)
|
||||
val contains = ContainmentCheck(functionCallExpr.args[1], functionCallExpr.args[0], functionCallExpr.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr as Node, contains, parent))
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
@@ -540,10 +636,25 @@ class ExpressionSimplifier(private val program: Program,
|
||||
return expr.left
|
||||
}
|
||||
}
|
||||
256.0 -> {
|
||||
when(leftDt) {
|
||||
DataType.UBYTE -> return NumericLiteral(DataType.UBYTE, 0.0, expr.position)
|
||||
DataType.BYTE -> return null // is either 0 or -1 we cannot tell here
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
// just use: msb(value) as type
|
||||
val msb = BuiltinFunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return if(leftDt==DataType.WORD)
|
||||
TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
else
|
||||
TypecastExpression(msb, DataType.UWORD, true, expr.position)
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
in powersOfTwo -> {
|
||||
if (leftDt==DataType.UBYTE || leftDt==DataType.UWORD) {
|
||||
// Unsigned number divided by a power of two => shift right
|
||||
// Signed number can't simply be bitshifted in this case (due to rounding issues for negative values),
|
||||
// Signed number can't simply be bitshifted in this case (due to rounding issues for negative values), TODO is this correct???
|
||||
// so we leave that as is and let the code generator deal with it.
|
||||
val numshifts = log2(cv).toInt()
|
||||
return BinaryExpression(expr.left, ">>", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)
|
||||
@@ -705,8 +816,8 @@ class ExpressionSimplifier(private val program: Program,
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if (amount >= 16) {
|
||||
errors.warn("shift always results in 0", expr.position)
|
||||
return NumericLiteral.optimalInteger(0, expr.position)
|
||||
errors.err("useless to shift by more than 15 bits", expr.position)
|
||||
return null
|
||||
}
|
||||
else if(amount==8) {
|
||||
// shift right by 8 bits is just a byte operation: msb(X) as uword
|
||||
@@ -720,11 +831,21 @@ class ExpressionSimplifier(private val program: Program,
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
// bit-shifting a signed value shouldn't be allowed by the compiler but here we go...
|
||||
if (amount > 16) {
|
||||
expr.right = NumericLiteral.optimalInteger(16, expr.right.position)
|
||||
if (amount >= 16) {
|
||||
errors.err("useless to shift by more than 15 bits", expr.position)
|
||||
return null
|
||||
}
|
||||
else if(amount == 8) {
|
||||
// shift right by 8 bits is just a byte operation: msb(X) as byte (will get converted to word later)
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
}
|
||||
else if(amount > 8) {
|
||||
// same as above but with residual shifts. Take care to do signed shift.
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
val signed = TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
return BinaryExpression(signed, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
|
||||
@@ -7,13 +7,13 @@ import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
|
||||
|
||||
fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget) {
|
||||
val valuetypefixer = VarConstantValueTypeAdjuster(this, errors)
|
||||
fun Program.constantFold(errors: IErrorReporter, options: CompilationOptions) {
|
||||
val valuetypefixer = VarConstantValueTypeAdjuster(this, options, errors)
|
||||
valuetypefixer.visit(this)
|
||||
if(errors.noErrors()) {
|
||||
valuetypefixer.applyModifications()
|
||||
|
||||
val replacer = ConstantIdentifierReplacer(this, errors, compTarget)
|
||||
val replacer = ConstantIdentifierReplacer(this, errors, options.compTarget)
|
||||
replacer.visit(this)
|
||||
if (errors.noErrors()) {
|
||||
replacer.applyModifications()
|
||||
@@ -22,7 +22,7 @@ fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget)
|
||||
if(errors.noErrors()) {
|
||||
valuetypefixer.applyModifications()
|
||||
|
||||
val optimizer = ConstantFoldingOptimizer(this)
|
||||
val optimizer = ConstantFoldingOptimizer(this, errors)
|
||||
optimizer.visit(this)
|
||||
while (errors.noErrors() && optimizer.applyModifications() > 0) {
|
||||
optimizer.visit(this)
|
||||
|
||||
@@ -110,12 +110,27 @@ class StatementOptimizer(private val program: Program,
|
||||
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)
|
||||
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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
||||
if(forLoop.body.isEmpty()) {
|
||||
errors.warn("removing empty for loop", forLoop.position)
|
||||
errors.info("removing empty for loop", forLoop.position)
|
||||
return listOf(IAstModification.Remove(forLoop, parent as IStatementContainer))
|
||||
} else if(forLoop.body.statements.size==1) {
|
||||
val loopvar = forLoop.body.statements[0] as? VarDecl
|
||||
@@ -151,7 +166,7 @@ class StatementOptimizer(private val program: Program,
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
}
|
||||
else if(iterable.datatype in ArrayDatatypes) {
|
||||
else if(iterable.isArray) {
|
||||
val size = iterable.arraysize!!.constIndex()
|
||||
if(size==1) {
|
||||
// loop over array of length 1 -> just assign the single value
|
||||
@@ -271,7 +286,7 @@ class StatementOptimizer(private val program: Program,
|
||||
val iter = repeatLoop.iterations
|
||||
if(iter!=null) {
|
||||
if(repeatLoop.body.isEmpty()) {
|
||||
errors.warn("empty loop removed", repeatLoop.position)
|
||||
errors.info("empty loop removed", repeatLoop.position)
|
||||
return listOf(IAstModification.Remove(repeatLoop, parent as IStatementContainer))
|
||||
}
|
||||
val iterations = iter.constValue(program)?.number?.toInt()
|
||||
@@ -453,14 +468,14 @@ class StatementOptimizer(private val program: Program,
|
||||
|
||||
fun replaceWithIf(condition: Expression, trueBlock: AnonymousScope, elseBlock: AnonymousScope?): List<IAstModification> {
|
||||
val ifStmt = IfElse(condition, trueBlock, elseBlock ?: AnonymousScope(mutableListOf(), whenStmt.position), whenStmt.position)
|
||||
errors.warn("for boolean condition a normal if statement is preferred", whenStmt.position)
|
||||
errors.info("for boolean condition a normal if statement is preferred", whenStmt.position)
|
||||
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.
|
||||
// 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)
|
||||
|
||||
@@ -66,14 +66,14 @@ class UnusedCodeRemover(private val program: Program,
|
||||
if (block.containsNoCodeNorVars) {
|
||||
if(block.name != internedStringsModuleName) {
|
||||
if(!block.statements.any { it is Subroutine && it.hasBeenInlined })
|
||||
errors.warn("removing unused block '${block.name}'", block.position)
|
||||
errors.info("removing unused block '${block.name}'", block.position)
|
||||
}
|
||||
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
|
||||
}
|
||||
if(callgraph.unused(block)) {
|
||||
if(block.statements.any{ it !is VarDecl || it.type== VarDeclType.VAR}) {
|
||||
if(!block.statements.any { it is Subroutine && it.hasBeenInlined })
|
||||
errors.warn("removing unused block '${block.name}'", block.position)
|
||||
errors.info("removing unused block '${block.name}'", block.position)
|
||||
}
|
||||
if(!block.statements.any { it is Subroutine && it.hasBeenInlined }) {
|
||||
program.removeInternedStringsFromRemovedBlock(block)
|
||||
@@ -90,8 +90,8 @@ class UnusedCodeRemover(private val program: Program,
|
||||
if (subroutine !== program.entrypoint && !forceOutput && !subroutine.isAsmSubroutine) {
|
||||
if(callgraph.unused(subroutine)) {
|
||||
if(subroutine.containsNoCodeNorVars) {
|
||||
if(!subroutine.definingModule.isLibrary)
|
||||
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
||||
if("ignore_unused" !in subroutine.definingBlock.options())
|
||||
errors.info("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
||||
val removals = mutableListOf(IAstModification.Remove(subroutine, parent as IStatementContainer))
|
||||
callgraph.calledBy[subroutine]?.let {
|
||||
for(node in it)
|
||||
@@ -99,8 +99,8 @@ class UnusedCodeRemover(private val program: Program,
|
||||
}
|
||||
return removals
|
||||
}
|
||||
if(!subroutine.hasBeenInlined && subroutine.definingModule===program.toplevelModule) {
|
||||
errors.warn("unused subroutine '${subroutine.name}'", subroutine.position)
|
||||
if(!subroutine.hasBeenInlined && "ignore_unused" !in subroutine.definingBlock.options()) {
|
||||
errors.info("unused subroutine '${subroutine.name}'", subroutine.position)
|
||||
}
|
||||
if(!subroutine.inline) {
|
||||
program.removeInternedStringsFromRemovedSubroutine(subroutine)
|
||||
@@ -119,8 +119,8 @@ class UnusedCodeRemover(private val program: Program,
|
||||
if (!forceOutput && decl.origin==VarDeclOrigin.USERCODE && !decl.sharedWithAsm) {
|
||||
val usages = callgraph.usages(decl)
|
||||
if (usages.isEmpty()) {
|
||||
if(!decl.definingModule.isLibrary)
|
||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||
if("ignore_unused" !in decl.definingBlock.options())
|
||||
errors.info("removing unused variable '${decl.name}'", decl.position)
|
||||
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
|
||||
}
|
||||
else {
|
||||
@@ -131,8 +131,8 @@ class UnusedCodeRemover(private val program: Program,
|
||||
if(assignment!=null && assignment.origin==AssignmentOrigin.VARINIT) {
|
||||
if(assignment.value.isSimple) {
|
||||
// remove the vardecl
|
||||
if(!decl.definingModule.isLibrary)
|
||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||
if("ignore_unused" !in decl.definingBlock.options())
|
||||
errors.info("removing unused variable '${decl.name}'", decl.position)
|
||||
return listOf(
|
||||
IAstModification.Remove(decl, parent as IStatementContainer),
|
||||
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
|
||||
@@ -144,7 +144,7 @@ class UnusedCodeRemover(private val program: Program,
|
||||
val declIndex = (parent as IStatementContainer).statements.indexOf(decl)
|
||||
val singleUseIndex = (parent as IStatementContainer).statements.indexOf(singleUse.parent)
|
||||
if(declIndex==singleUseIndex-1) {
|
||||
errors.warn("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position)
|
||||
errors.info("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position)
|
||||
val fcall = assignment.value as IFunctionCall
|
||||
val voidCall = FunctionCallStatement(fcall.target, fcall.args, true, fcall.position)
|
||||
return listOf(
|
||||
@@ -154,7 +154,7 @@ class UnusedCodeRemover(private val program: Program,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errors.warn("variable '${decl.name}' is unused but has non-trivial initialization assignment. Leaving this in but maybe it can be removed altogether", decl.position)
|
||||
errors.info("variable '${decl.name}' is unused but has non-trivial initialization assignment. Leaving this in but maybe it can be removed altogether", decl.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
; Prog8 definitions for the Atari800XL
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
atari {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
||||
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
||||
@@ -20,8 +21,6 @@ atari {
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const ubyte target = 8 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 8 = atari800XL
|
||||
|
||||
asmsub init_system() {
|
||||
@@ -253,9 +252,9 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub exit(ubyte returnvalue @A) {
|
||||
asmsub exit(ubyte returnvalue @A) {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
; TODO
|
||||
; TODO where to store A as exit code?
|
||||
%asm {{
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
txs
|
||||
@@ -270,11 +269,37 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push(ubyte value @A) {
|
||||
%asm {{
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pushw(uword value @AY) {
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub popw() -> uword @AY {
|
||||
%asm {{
|
||||
pla
|
||||
tay
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cx16 {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||
; they are simulated on the Atari as well but their location in memory is different
|
||||
; TODO
|
||||
@@ -406,4 +431,9 @@ cx16 {
|
||||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
txt {
|
||||
|
||||
%option no_symbol_prefixing
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
const ubyte DEFAULT_WIDTH = 40
|
||||
const ubyte DEFAULT_HEIGHT = 24
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
; Prog8 definitions for the Commodore-128
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
cbm {
|
||||
; Commodore (CBM) common variables, vectors and kernal routines
|
||||
%option no_symbol_prefixing
|
||||
|
||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||
&ubyte TIME_MID = $a1 ; .. mid byte
|
||||
@@ -78,7 +79,7 @@ romsub $FFAB = UNTLK() clobbers(A) ; command serial
|
||||
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
||||
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word (use CLEARST to reset it to 0)
|
||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
|
||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||
@@ -128,12 +129,19 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
}}
|
||||
}
|
||||
|
||||
sub CLEARST() {
|
||||
; -- Set the ST status variable back to 0. (there's no direct kernal call for this)
|
||||
; Note: a drive error state (blinking led) isn't cleared! You can use diskio.status() to clear that.
|
||||
SETNAM(0, $0000)
|
||||
SETLFS(15, 3, 15)
|
||||
void OPEN()
|
||||
CLOSE(15)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
c64 {
|
||||
; C64 I/O registers (VIC, SID, CIA)
|
||||
%option no_symbol_prefixing
|
||||
|
||||
; the default locations of the 8 sprite pointers (store address of sprite / 64)
|
||||
&ubyte SPRPTR0 = 2040
|
||||
@@ -288,7 +296,6 @@ c64 {
|
||||
|
||||
c128 {
|
||||
; ---- C128 specific registers ----
|
||||
%option no_symbol_prefixing
|
||||
|
||||
&ubyte VM1 = $0A2C ; shadow for VUC $d018 in text mode
|
||||
&ubyte VM2 = $0A2D ; shadow for VIC $d018 in bitmap screen mode
|
||||
@@ -325,7 +332,6 @@ asmsub disable_basic() clobbers(A) {
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const ubyte target = 128 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
|
||||
|
||||
@@ -367,7 +373,13 @@ asmsub init_system_phase2() {
|
||||
asmsub cleanup_at_exit() {
|
||||
; executed when the main subroutine does rts
|
||||
%asm {{
|
||||
jmp sys.enable_runstop_and_charsetswitch
|
||||
lda #0
|
||||
sta $ff00 ; default bank 15
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
jsr enable_runstop_and_charsetswitch
|
||||
_exitcode = *+1
|
||||
lda #0 ; exit code possibly modified in exit()
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -748,16 +760,13 @@ _longcopy
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub exit(ubyte returnvalue @A) {
|
||||
asmsub exit(ubyte returnvalue @A) {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
%asm {{
|
||||
lda #0
|
||||
sta $ff00 ; default bank 15
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
jsr sys.enable_runstop_and_charsetswitch
|
||||
sta cleanup_at_exit._exitcode
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
txs
|
||||
rts ; return to original caller
|
||||
jmp cleanup_at_exit
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -768,11 +777,37 @@ _longcopy
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push(ubyte value @A) {
|
||||
%asm {{
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pushw(uword value @AY) {
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub popw() -> uword @AY {
|
||||
%asm {{
|
||||
pla
|
||||
tay
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cx16 {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||
; they are simulated on the C128 as well but their location in memory is different
|
||||
; (because there's no room for them in the zeropage)
|
||||
@@ -904,4 +939,10 @@ cx16 {
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
|
||||
txt {
|
||||
%option no_symbol_prefixing
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
const ubyte DEFAULT_WIDTH = 40
|
||||
const ubyte DEFAULT_HEIGHT = 25
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
; Prog8 definitions for floating point handling on the Commodore-64
|
||||
|
||||
%option enable_floats
|
||||
%option enable_floats, no_symbol_prefixing, ignore_unused
|
||||
%import floats_functions
|
||||
|
||||
floats {
|
||||
; ---- this block contains C-64 floating point related functions ----
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const float π = 3.141592653589793
|
||||
const float PI = π
|
||||
const float TWOPI = 2*π
|
||||
@@ -47,7 +45,7 @@ romsub $b391 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||
|
||||
romsub $b3a2 = FREADUY(ubyte value @ Y) clobbers(A,X,Y) ; 8 bit unsigned Y -> float in fac1
|
||||
romsub $bc3c = FREADSA(byte value @ A) clobbers(A,X,Y) ; 8 bit signed A -> float in fac1
|
||||
romsub $b7b5 = FREADSTR(ubyte length @ A) clobbers(A,X,Y) ; str -> fac1, $22/23 must point to string, A=string length. Also see parse_f()
|
||||
romsub $b7b5 = FREADSTR(ubyte length @ A) clobbers(A,X,Y) ; str -> fac1, $22/23 must point to string, A=string length. Also see parse()
|
||||
romsub $aabc = FPRINTLN() clobbers(A,X,Y) ; print string of fac1, on one line (= with newline) destroys fac1. (consider FOUT + STROUT as well)
|
||||
romsub $bddd = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY ($0100)
|
||||
|
||||
@@ -159,7 +157,7 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
||||
|
||||
&uword AYINT_facmo = $64 ; $64/$65 contain result of AYINT
|
||||
|
||||
sub rndf() -> float {
|
||||
sub rnd() -> float {
|
||||
%asm {{
|
||||
lda #1
|
||||
jsr FREADSA
|
||||
@@ -167,7 +165,7 @@ sub rndf() -> float {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub parse_f(str value @AY) -> float @FAC1 {
|
||||
asmsub parse(str value @AY) -> float @FAC1 {
|
||||
%asm {{
|
||||
sta $22
|
||||
sty $23
|
||||
|
||||
@@ -147,3 +147,54 @@ func_sqrt_into_FAC1 .proc
|
||||
jsr MOVFM
|
||||
jmp SQR
|
||||
.pend
|
||||
|
||||
|
||||
|
||||
containment_floatarray .proc
|
||||
; -- check if a value exists in a float array.
|
||||
; parameters: FAC1: value to check, P8ZP_SCRATCH_W1: address of the word array, Y = length of array (>=1).
|
||||
; returns boolean 0/1 in A.
|
||||
sty P8ZP_SCRATCH_REG
|
||||
ldx #<floats.floats_temp_var
|
||||
ldy #>floats.floats_temp_var
|
||||
jsr floats.MOVMF
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda floats.floats_temp_var
|
||||
cmp (P8ZP_SCRATCH_W1),y
|
||||
bne _firstmiss
|
||||
iny
|
||||
lda floats.floats_temp_var+1
|
||||
cmp (P8ZP_SCRATCH_W1),y
|
||||
bne _secondmiss
|
||||
iny
|
||||
lda floats.floats_temp_var+2
|
||||
cmp (P8ZP_SCRATCH_W1),y
|
||||
bne _thirdmiss
|
||||
iny
|
||||
lda floats.floats_temp_var+3
|
||||
cmp (P8ZP_SCRATCH_W1),y
|
||||
bne _fourthmiss
|
||||
iny
|
||||
lda floats.floats_temp_var+4
|
||||
cmp (P8ZP_SCRATCH_W1),y
|
||||
bne _fifthmiss
|
||||
lda #1
|
||||
rts
|
||||
|
||||
_firstmiss
|
||||
iny
|
||||
_secondmiss
|
||||
iny
|
||||
_thirdmiss
|
||||
iny
|
||||
_fourthmiss
|
||||
iny
|
||||
_fifthmiss
|
||||
iny
|
||||
dex
|
||||
bne -
|
||||
lda #0
|
||||
rts
|
||||
|
||||
.pend
|
||||
@@ -8,7 +8,7 @@
|
||||
; so that the program itself can be larger without starting to overwrite the graphics memory.
|
||||
|
||||
graphics {
|
||||
%option no_symbol_prefixing
|
||||
%option ignore_unused
|
||||
|
||||
const uword WIDTH = 320
|
||||
const ubyte HEIGHT = 200
|
||||
@@ -175,11 +175,11 @@ graphics {
|
||||
|
||||
if separate_pixels {
|
||||
%asm {{
|
||||
lda pixaddr
|
||||
lda p8v_pixaddr
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda pixaddr+1
|
||||
lda p8v_pixaddr+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
ldy separate_pixels
|
||||
ldy p8v_separate_pixels
|
||||
lda hline_filled_right,y
|
||||
eor #255
|
||||
ldy #0
|
||||
@@ -193,23 +193,23 @@ graphics {
|
||||
|
||||
if length {
|
||||
%asm {{
|
||||
lda length
|
||||
lda p8v_length
|
||||
and #7
|
||||
sta separate_pixels
|
||||
lsr length+1
|
||||
ror length
|
||||
lsr length+1
|
||||
ror length
|
||||
lsr length+1
|
||||
ror length
|
||||
lda pixaddr
|
||||
sta p8v_separate_pixels
|
||||
lsr p8v_length+1
|
||||
ror p8v_length
|
||||
lsr p8v_length+1
|
||||
ror p8v_length
|
||||
lsr p8v_length+1
|
||||
ror p8v_length
|
||||
lda p8v_pixaddr
|
||||
sta _modified+1
|
||||
lda pixaddr+1
|
||||
lda p8v_pixaddr+1
|
||||
sta _modified+2
|
||||
lda length
|
||||
ora length+1
|
||||
lda p8v_length
|
||||
ora p8v_length+1
|
||||
beq _zero
|
||||
ldy length
|
||||
ldy p8v_length
|
||||
ldx #$ff
|
||||
_modified stx $ffff ; modified
|
||||
lda _modified+1
|
||||
@@ -221,7 +221,7 @@ _modified stx $ffff ; modified
|
||||
+ dey
|
||||
bne _modified
|
||||
_zero
|
||||
ldy separate_pixels
|
||||
ldy p8v_separate_pixels
|
||||
beq hline_zero2
|
||||
lda _modified+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
@@ -319,9 +319,9 @@ hline_zero2
|
||||
|
||||
inline asmsub plot(uword plotx @XY, ubyte ploty @A) clobbers (A, X, Y) {
|
||||
%asm {{
|
||||
stx graphics.internal_plotx
|
||||
sty graphics.internal_plotx+1
|
||||
jsr graphics.internal_plot
|
||||
stx p8b_graphics.p8v_internal_plotx
|
||||
sty p8b_graphics.p8v_internal_plotx+1
|
||||
jsr p8b_graphics.p8s_internal_plot
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -333,11 +333,11 @@ hline_zero2
|
||||
asmsub internal_plot(ubyte ploty @A) clobbers (A, X, Y) { ; internal_plotx is 16 bits 0 to 319... doesn't fit in a register
|
||||
%asm {{
|
||||
tay
|
||||
lda internal_plotx+1
|
||||
lda p8v_internal_plotx+1
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
lsr a ; 0
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda internal_plotx
|
||||
lda p8v_internal_plotx
|
||||
pha
|
||||
and #7
|
||||
tax
|
||||
@@ -365,7 +365,7 @@ _ormask .byte 128, 64, 32, 16, 8, 4, 2, 1
|
||||
; the y lookup tables encodes this formula: BITMAP_ADDRESS + 320*(py>>3) + (py & 7) (y from 0..199)
|
||||
; We use the 64tass syntax for range expressions to calculate this table on assembly time.
|
||||
|
||||
_plot_y_values := BITMAP_ADDRESS + 320*(range(200)>>3) + (range(200) & 7)
|
||||
_plot_y_values := p8c_BITMAP_ADDRESS + 320*(range(200)>>3) + (range(200) & 7)
|
||||
|
||||
_y_lookup_lo .byte <_plot_y_values
|
||||
_y_lookup_hi .byte >_plot_y_values
|
||||
@@ -375,9 +375,9 @@ _y_lookup_hi .byte >_plot_y_values
|
||||
|
||||
asmsub get_y_lookup(ubyte yy @Y) -> uword @AY {
|
||||
%asm {{
|
||||
lda internal_plot._y_lookup_lo,y
|
||||
lda p8s_internal_plot._y_lookup_lo,y
|
||||
pha
|
||||
lda internal_plot._y_lookup_hi,y
|
||||
lda p8s_internal_plot._y_lookup_hi,y
|
||||
tay
|
||||
pla
|
||||
rts
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
; Prog8 definitions for the Commodore-64
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
cbm {
|
||||
|
||||
; Commodore (CBM) common variables, vectors and kernal routines
|
||||
|
||||
%option no_symbol_prefixing
|
||||
|
||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||
&ubyte TIME_MID = $a1 ; .. mid byte
|
||||
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||
@@ -82,7 +80,7 @@ romsub $FFAB = UNTLK() clobbers(A) ; command serial
|
||||
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
||||
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word (use CLEARST to reset it to 0)
|
||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
|
||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||
@@ -128,13 +126,20 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
}}
|
||||
}
|
||||
|
||||
sub CLEARST() {
|
||||
; -- Set the ST status variable back to 0. (there's no direct kernal call for this)
|
||||
; Note: a drive error state (blinking led) isn't cleared! You can use diskio.status() to clear that.
|
||||
SETNAM(0, $0000)
|
||||
SETLFS(15, 3, 15)
|
||||
void OPEN()
|
||||
CLOSE(15)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
c64 {
|
||||
; C64 I/O registers (VIC, SID, CIA)
|
||||
|
||||
%option no_symbol_prefixing
|
||||
|
||||
; the default locations of the 8 sprite pointers (store address of sprite / 64)
|
||||
; (depending on the VIC bank and screen ram address selection these can be shifted around though,
|
||||
; see the two routines after this for a dynamic way of determining the correct memory location)
|
||||
@@ -324,8 +329,6 @@ c64 {
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const ubyte target = 64 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
|
||||
|
||||
|
||||
@@ -369,7 +372,13 @@ asmsub init_system_phase2() {
|
||||
asmsub cleanup_at_exit() {
|
||||
; executed when the main subroutine does rts
|
||||
%asm {{
|
||||
jmp sys.enable_runstop_and_charsetswitch
|
||||
lda #31
|
||||
sta $01 ; bank the kernal in
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
jsr enable_runstop_and_charsetswitch
|
||||
_exitcode = *+1
|
||||
lda #0 ; exit code possibly modified in exit()
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -749,16 +758,13 @@ _longcopy
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub exit(ubyte returnvalue @A) {
|
||||
asmsub exit(ubyte returnvalue @A) {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
%asm {{
|
||||
lda #31
|
||||
sta $01 ; bank the kernal in
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
jsr sys.enable_runstop_and_charsetswitch
|
||||
sta cleanup_at_exit._exitcode
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
txs
|
||||
rts ; return to original caller
|
||||
jmp cleanup_at_exit
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -769,12 +775,36 @@ _longcopy
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push(ubyte value @A) {
|
||||
%asm {{
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pushw(uword value @AY) {
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub popw() -> uword @AY {
|
||||
%asm {{
|
||||
pla
|
||||
tay
|
||||
pla
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
cx16 {
|
||||
|
||||
%option no_symbol_prefixing
|
||||
|
||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||
; they are simulated on the C64 as well but their location in memory is different
|
||||
; (because there's no room for them in the zeropage in the default configuration)
|
||||
@@ -908,4 +938,9 @@ cx16 {
|
||||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
txt {
|
||||
|
||||
%option no_symbol_prefixing
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
const ubyte DEFAULT_WIDTH = 40
|
||||
const ubyte DEFAULT_HEIGHT = 25
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
conv {
|
||||
|
||||
%option no_symbol_prefixing
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
; ----- number conversions to decimal strings ----
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
; BMX Specification: https://cx16forum.com/forum/viewtopic.php?t=6945
|
||||
|
||||
%import diskio
|
||||
%option ignore_unused
|
||||
|
||||
bmx {
|
||||
|
||||
@@ -17,7 +18,7 @@ bmx {
|
||||
uword palette_entries ; 1-256
|
||||
ubyte palette_start
|
||||
ubyte compression
|
||||
uword palette_buffer_ptr = 0 ; should you want to load or save the palette into main memory instead of directly into vram
|
||||
uword @shared palette_buffer_ptr = 0 ; should you want to load or save the palette into main memory instead of directly into vram
|
||||
|
||||
uword error_message ; pointer to error message, or 0 if all ok
|
||||
ubyte old_drivenumber
|
||||
|
||||
@@ -12,16 +12,17 @@
|
||||
|
||||
|
||||
%import textio
|
||||
%import conv
|
||||
%import string
|
||||
%import syslib
|
||||
|
||||
diskio {
|
||||
%option no_symbol_prefixing
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
const ubyte READ_IO_CHANNEL=12
|
||||
const ubyte WRITE_IO_CHANNEL=13
|
||||
|
||||
ubyte drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
|
||||
ubyte @shared drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
|
||||
|
||||
sub reset_read_channel() {
|
||||
void cbm.CHKIN(READ_IO_CHANNEL)
|
||||
@@ -41,8 +42,6 @@ diskio {
|
||||
if_cs
|
||||
goto io_error
|
||||
reset_read_channel()
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
repeat 4 {
|
||||
void cbm.CHRIN() ; skip the 4 prologue bytes
|
||||
@@ -99,8 +98,6 @@ io_error:
|
||||
if_cs
|
||||
goto io_error
|
||||
reset_read_channel()
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
while cbm.CHRIN()!='"' {
|
||||
; skip up to entry name
|
||||
@@ -184,8 +181,6 @@ io_error:
|
||||
if_cs
|
||||
goto io_error
|
||||
reset_read_channel()
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
repeat 4 {
|
||||
void cbm.CHRIN() ; skip the 4 prologue bytes
|
||||
@@ -195,6 +190,7 @@ io_error:
|
||||
return true
|
||||
|
||||
io_error:
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
lf_end_list()
|
||||
return false
|
||||
}
|
||||
@@ -290,21 +286,20 @@ close_end:
|
||||
cbm.SETLFS(READ_IO_CHANNEL, drivenumber, READ_IO_CHANNEL) ; note: has to be Channel,x,Channel because otherwise f_seek doesn't work
|
||||
void cbm.OPEN() ; open 12,8,12,"filename"
|
||||
if_cc {
|
||||
reset_read_channel()
|
||||
if cbm.READST()==0 {
|
||||
iteration_in_progress = true
|
||||
reset_read_channel()
|
||||
if_cc {
|
||||
void cbm.CHRIN() ; read first byte to test for file not found
|
||||
if not cbm.READST() {
|
||||
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
|
||||
void cbm.OPEN() ; re-open the file
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
return true
|
||||
}
|
||||
void cbm.CHRIN() ; read first byte to test for file not found
|
||||
if cbm.READST()==0 {
|
||||
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
|
||||
void cbm.OPEN() ; re-open the file
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
f_close()
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -451,6 +446,7 @@ _end rts
|
||||
if_cc {
|
||||
return not cbm.READST()
|
||||
}
|
||||
cbm.CLOSE(WRITE_IO_CHANNEL)
|
||||
f_close_w()
|
||||
return false
|
||||
}
|
||||
@@ -494,6 +490,14 @@ no_mciout:
|
||||
|
||||
sub status() -> uword {
|
||||
; -- retrieve the disk drive's current status message
|
||||
str device_not_present_error = "device not present #xx"
|
||||
if cbm.READST()==128 {
|
||||
device_not_present_error[len(device_not_present_error)-2] = 0
|
||||
conv.str_ub(drivenumber)
|
||||
void string.copy(conv.string_out, &device_not_present_error+len(device_not_present_error)-2)
|
||||
return device_not_present_error
|
||||
}
|
||||
|
||||
uword messageptr = &list_filename
|
||||
cbm.SETNAM(0, list_filename)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
@@ -501,8 +505,6 @@ no_mciout:
|
||||
if_cs
|
||||
goto io_error
|
||||
void cbm.CHKIN(15) ; use #15 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
while not cbm.READST() {
|
||||
cx16.r5L = cbm.CHRIN()
|
||||
@@ -519,7 +521,8 @@ done:
|
||||
return list_filename
|
||||
|
||||
io_error:
|
||||
list_filename = "?disk error"
|
||||
cbm.CLOSE(15)
|
||||
list_filename = "io error"
|
||||
goto done
|
||||
}
|
||||
|
||||
@@ -748,8 +751,6 @@ internal_vload:
|
||||
if_cs
|
||||
goto io_error
|
||||
reset_read_channel()
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
repeat 6 {
|
||||
void cbm.CHRIN()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
; Docs: https://github.com/X16Community/x16-emulator#emulator-io-registers
|
||||
|
||||
emudbg {
|
||||
%option ignore_unused
|
||||
|
||||
const uword EMU_BASE = $9fb0
|
||||
|
||||
@@ -13,10 +14,14 @@ emudbg {
|
||||
&ubyte EMU_RECORD_GIF = EMU_BASE + 5
|
||||
&ubyte EMU_RECORD_WAV = EMU_BASE + 6
|
||||
&ubyte EMU_CMDKEYS_DISABLED = EMU_BASE + 7
|
||||
&ubyte EMU_CPUCLK_L = EMU_BASE + 8 ; write: reset cpu clock to 0
|
||||
&ubyte EMU_CPUCLK_M = EMU_BASE + 9 ; write: outputs "User debug 1: $xx"
|
||||
&ubyte EMU_CPUCLK_H = EMU_BASE + 10 ; write: outputs "User debug 2: $xx"
|
||||
&ubyte EMU_CPUCLK_U = EMU_BASE + 11 ; write: outputs as character to console
|
||||
&ubyte EMU_CPUCLK_L = EMU_BASE + 8
|
||||
&ubyte EMU_CPUCLK_M = EMU_BASE + 9
|
||||
&ubyte EMU_CPUCLK_H = EMU_BASE + 10
|
||||
&ubyte EMU_CPUCLK_U = EMU_BASE + 11
|
||||
&ubyte EMU_CPUCLK_RESET = EMU_BASE + 8 ; write: reset cpu clock to 0
|
||||
&ubyte EMU_DBGOUT1 = EMU_BASE + 9 ; write: outputs "User debug 1: $xx"
|
||||
&ubyte EMU_DBGOUT2 = EMU_BASE + 10 ; write: outputs "User debug 2: $xx"
|
||||
&ubyte EMU_CHROUT = EMU_BASE + 11 ; write: outputs as character to console
|
||||
; 12 is unused for now
|
||||
&ubyte EMU_KEYMAP = EMU_BASE + 13
|
||||
&ubyte EMU_EMU_DETECT1 = EMU_BASE + 14
|
||||
@@ -45,16 +50,16 @@ emudbg {
|
||||
sub console_chrout(ubyte char) {
|
||||
; note: make sure the character is in Iso encoding.
|
||||
if is_emulator()
|
||||
EMU_CPUCLK_U = char
|
||||
EMU_CHROUT = char
|
||||
}
|
||||
|
||||
sub console_value1(ubyte value) {
|
||||
if is_emulator()
|
||||
EMU_CPUCLK_M = value
|
||||
EMU_DBGOUT1 = value
|
||||
}
|
||||
|
||||
sub console_value2(ubyte value) {
|
||||
if is_emulator()
|
||||
EMU_CPUCLK_H = value
|
||||
EMU_DBGOUT2 = value
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
; Prog8 definitions for floating point handling on the CommanderX16
|
||||
|
||||
%option enable_floats
|
||||
%option enable_floats, no_symbol_prefixing, ignore_unused
|
||||
%import floats_functions
|
||||
|
||||
floats {
|
||||
; ---- this block contains C-64 compatible floating point related functions ----
|
||||
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const float π = 3.141592653589793
|
||||
const float PI = π
|
||||
const float TWOPI = 2*π
|
||||
@@ -29,7 +27,7 @@ romsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101
|
||||
romsub $fe03 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||
|
||||
romsub $fe06 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
|
||||
romsub $fe09 = VAL_1(uword string @XY, ubyte length @A) clobbers(A,X,Y) -> float @FAC1 ; convert ASCII string in XY and length in A, to floating point in FAC1. WARNING: not implemented in the ROM yet!
|
||||
romsub $fe09 = VAL_1(uword string @XY, ubyte length @A) clobbers(A,X,Y) -> float @FAC1 ; convert ASCII string in XY and length in A, to floating point in FAC1. WARNING: only implemented in ROM 47+. Safer to use floats.parse() instead.
|
||||
|
||||
; GETADR: fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
||||
; (tip: use GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||
@@ -137,11 +135,11 @@ asmsub FREADUY (ubyte value @Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub parse_f(str value @AY) -> float @FAC1 {
|
||||
asmsub parse(str value @AY) -> float @FAC1 {
|
||||
; -- parse a string value of a number to float in FAC1
|
||||
; warning: on older <R47 kernals it uses an internal BASIC routine that is ROM version dependent,
|
||||
; ($deb6 is inside the routine for VAL at $deb3) See basic.sym from x16-rom
|
||||
; TODO once VAL_1 is merged into the kernal properly, remove all the workarounds here
|
||||
; TODO once ROM v47 is released, all the workarounds here can be removed. But probably keep the kernal VAL_1 existance check
|
||||
%asm {{
|
||||
ldx VAL_1
|
||||
cpx #$4c ; is there an implementation in VAL_1? (test for JMP)
|
||||
@@ -177,7 +175,7 @@ _msg .text 13,"?val kaputt",13,0
|
||||
|
||||
&uword AYINT_facmo = $c6 ; $c6/$c7 contain result of AYINT
|
||||
|
||||
sub rndf() -> float {
|
||||
sub rnd() -> float {
|
||||
%asm {{
|
||||
lda #1
|
||||
jmp RND_0
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
gfx2 {
|
||||
|
||||
%option no_symbol_prefixing
|
||||
%option ignore_unused
|
||||
|
||||
; read-only control variables:
|
||||
ubyte active_mode = 0
|
||||
@@ -141,8 +141,8 @@ gfx2 {
|
||||
; lores 256c
|
||||
position(xx, yy)
|
||||
%asm {{
|
||||
lda color
|
||||
ldx length+1
|
||||
lda p8v_color
|
||||
ldx p8v_length+1
|
||||
beq +
|
||||
ldy #0
|
||||
- sta cx16.VERA_DATA0
|
||||
@@ -150,7 +150,7 @@ gfx2 {
|
||||
bne -
|
||||
dex
|
||||
bne -
|
||||
+ ldy length ; remaining
|
||||
+ ldy p8v_length ; remaining
|
||||
beq +
|
||||
- sta cx16.VERA_DATA0
|
||||
dey
|
||||
@@ -180,7 +180,7 @@ gfx2 {
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.r0+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
ldx xx
|
||||
ldx p8v_xx
|
||||
}}
|
||||
|
||||
repeat length {
|
||||
@@ -189,8 +189,8 @@ gfx2 {
|
||||
and #3
|
||||
tay
|
||||
lda cx16.VERA_DATA0
|
||||
and gfx2.plot.mask4c,y
|
||||
ora colorbits,y
|
||||
and p8b_gfx2.p8s_plot.p8v_mask4c,y
|
||||
ora p8v_colorbits,y
|
||||
sta cx16.VERA_DATA0
|
||||
cpy #%00000011 ; next vera byte?
|
||||
bne ++
|
||||
@@ -232,9 +232,9 @@ gfx2 {
|
||||
position(xx,yy)
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (14<<4)
|
||||
%asm {{
|
||||
ldy lheight
|
||||
ldy p8v_lheight
|
||||
beq +
|
||||
lda color
|
||||
lda p8v_color
|
||||
- sta cx16.VERA_DATA0
|
||||
dey
|
||||
bne -
|
||||
@@ -260,8 +260,8 @@ gfx2 {
|
||||
repeat lheight {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
and mask
|
||||
ora color
|
||||
and p8v_mask
|
||||
ora p8v_color
|
||||
sta cx16.VERA_DATA1
|
||||
}}
|
||||
}
|
||||
@@ -534,7 +534,7 @@ gfx2 {
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda cx16.r0
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda color
|
||||
lda p8v_color
|
||||
sta cx16.VERA_DATA0
|
||||
}}
|
||||
}
|
||||
@@ -558,9 +558,9 @@ gfx2 {
|
||||
lda cx16.r0L
|
||||
sta cx16.VERA_ADDR_L
|
||||
ldy cx16.r2L ; xbits
|
||||
lda mask4c,y
|
||||
lda p8v_mask4c,y
|
||||
and cx16.VERA_DATA0
|
||||
ora color
|
||||
ora p8v_color
|
||||
sta cx16.VERA_DATA0
|
||||
}}
|
||||
}
|
||||
@@ -605,10 +605,10 @@ gfx2 {
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.VERA_DATA0
|
||||
pha
|
||||
lda xx
|
||||
lda p8v_xx
|
||||
and #3
|
||||
tay
|
||||
lda gfx2.plot.shift4c,y
|
||||
lda p8b_gfx2.p8s_plot.p8v_shift4c,y
|
||||
tay
|
||||
pla
|
||||
cpy #0
|
||||
@@ -651,21 +651,21 @@ gfx2 {
|
||||
;; cx16.r12L++
|
||||
%asm {{
|
||||
ldy cx16.r12L
|
||||
lda sxl
|
||||
sta stack_xl_lsb,y
|
||||
lda sxl+1
|
||||
sta stack_xl_msb,y
|
||||
lda sxr
|
||||
sta stack_xr_lsb,y
|
||||
lda sxr+1
|
||||
sta stack_xr_msb,y
|
||||
lda sy
|
||||
sta stack_y_lsb,y
|
||||
lda sy+1
|
||||
sta stack_y_msb,y
|
||||
lda p8v_sxl
|
||||
sta p8v_stack_xl_lsb,y
|
||||
lda p8v_sxl+1
|
||||
sta p8v_stack_xl_msb,y
|
||||
lda p8v_sxr
|
||||
sta p8v_stack_xr_lsb,y
|
||||
lda p8v_sxr+1
|
||||
sta p8v_stack_xr_msb,y
|
||||
lda p8v_sy
|
||||
sta p8v_stack_y_lsb,y
|
||||
lda p8v_sy+1
|
||||
sta p8v_stack_y_msb,y
|
||||
ldy cx16.r12L
|
||||
lda sdy
|
||||
sta stack_dy,y
|
||||
lda p8v_sdy
|
||||
sta p8v_stack_dy,y
|
||||
inc cx16.r12L
|
||||
}}
|
||||
}
|
||||
@@ -679,21 +679,21 @@ gfx2 {
|
||||
%asm {{
|
||||
dec cx16.r12L
|
||||
ldy cx16.r12L
|
||||
lda stack_xl_lsb,y
|
||||
sta x1
|
||||
lda stack_xl_msb,y
|
||||
sta x1+1
|
||||
lda stack_xr_lsb,y
|
||||
sta x2
|
||||
lda stack_xr_msb,y
|
||||
sta x2+1
|
||||
lda stack_y_lsb,y
|
||||
sta yy
|
||||
lda stack_y_msb,y
|
||||
sta yy+1
|
||||
lda p8v_stack_xl_lsb,y
|
||||
sta p8v_x1
|
||||
lda p8v_stack_xl_msb,y
|
||||
sta p8v_x1+1
|
||||
lda p8v_stack_xr_lsb,y
|
||||
sta p8v_x2
|
||||
lda p8v_stack_xr_msb,y
|
||||
sta p8v_x2+1
|
||||
lda p8v_stack_y_lsb,y
|
||||
sta p8v_yy
|
||||
lda p8v_stack_y_msb,y
|
||||
sta p8v_yy+1
|
||||
ldy cx16.r12L
|
||||
lda stack_dy,y
|
||||
sta dy
|
||||
lda p8v_stack_dy,y
|
||||
sta p8v_dy
|
||||
}}
|
||||
yy+=dy
|
||||
}
|
||||
@@ -708,7 +708,7 @@ gfx2 {
|
||||
while cx16.r12L {
|
||||
pop_stack()
|
||||
xx = x1
|
||||
; TODO: if mode==1 (256c) use vera autodecrement instead of pget(), but code bloat not worth it?
|
||||
; possible speed optimization: if mode==1 (256c) use vera autodecrement instead of pget(), but code bloat not worth it?
|
||||
while xx >= 0 {
|
||||
if pget(xx as uword, yy as uword) != cx16.r11L
|
||||
break
|
||||
@@ -726,7 +726,7 @@ gfx2 {
|
||||
|
||||
do {
|
||||
cx16.r9 = xx
|
||||
; TODO: if mode==1 (256c) use vera autoincrement instead of pget(), but code bloat not worth it?
|
||||
; 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
|
||||
@@ -852,7 +852,7 @@ skip:
|
||||
position(xx,yy)
|
||||
yy++
|
||||
%asm {{
|
||||
ldx color
|
||||
ldx p8v_color
|
||||
lda cx16.VERA_DATA1
|
||||
sta P8ZP_SCRATCH_B1
|
||||
ldy #8
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
|
||||
graphics {
|
||||
%option no_symbol_prefixing
|
||||
%option ignore_unused
|
||||
|
||||
romsub $feff = FB_cursor_position2() clobbers(A,X,Y) ; alias for the normal FB_cursor_position() call but reuses existing r0 and r1
|
||||
|
||||
@@ -158,7 +158,7 @@ graphics {
|
||||
inline asmsub plot(uword plotx @R0, uword ploty @R1) clobbers(A, X, Y) {
|
||||
%asm {{
|
||||
jsr cx16.FB_cursor_position
|
||||
lda graphics.stroke_color
|
||||
lda p8b_graphics.p8v_stroke_color
|
||||
jsr cx16.FB_set_pixel
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
monogfx {
|
||||
|
||||
%option no_symbol_prefixing
|
||||
%option ignore_unused
|
||||
|
||||
; read-only control variables:
|
||||
uword width = 0
|
||||
@@ -107,11 +107,11 @@ monogfx {
|
||||
; just use 2 byte writes with shifted mask
|
||||
position2(xx,yy,true)
|
||||
%asm {{
|
||||
ldy length
|
||||
lda masked_ends,y
|
||||
ldy p8v_length
|
||||
lda p8v_masked_ends,y
|
||||
sta cx16.r0L ; save left byte
|
||||
stz P8ZP_SCRATCH_B1
|
||||
lda xx
|
||||
lda p8v_xx
|
||||
and #7
|
||||
beq +
|
||||
tay
|
||||
@@ -122,10 +122,10 @@ monogfx {
|
||||
bne -
|
||||
sta cx16.r0L ; new left byte
|
||||
+
|
||||
lda dont_stipple_flag
|
||||
lda p8v_dont_stipple_flag
|
||||
bne _dontstipple
|
||||
; determine stipple pattern
|
||||
lda yy
|
||||
lda p8v_yy
|
||||
and #1
|
||||
beq +
|
||||
lda #%10101010
|
||||
@@ -139,7 +139,7 @@ monogfx {
|
||||
and P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_B1
|
||||
_dontstipple
|
||||
lda draw
|
||||
lda p8v_draw
|
||||
beq _clear
|
||||
lda cx16.r0L ; left byte
|
||||
ora cx16.VERA_DATA1
|
||||
@@ -184,34 +184,34 @@ _clear
|
||||
separate_pixels = lsb(length) & 7
|
||||
xx += length & $fff8
|
||||
%asm {{
|
||||
lsr length+1
|
||||
ror length
|
||||
lsr length+1
|
||||
ror length
|
||||
lsr length+1
|
||||
ror length
|
||||
lda draw
|
||||
lsr p8v_length+1
|
||||
ror p8v_length
|
||||
lsr p8v_length+1
|
||||
ror p8v_length
|
||||
lsr p8v_length+1
|
||||
ror p8v_length
|
||||
lda p8v_draw
|
||||
bne +
|
||||
ldy #0 ; black
|
||||
bra _loop
|
||||
+ lda dont_stipple_flag
|
||||
+ lda p8v_dont_stipple_flag
|
||||
beq _stipple
|
||||
ldy #255 ; don't stipple
|
||||
bra _loop
|
||||
_stipple lda yy
|
||||
_stipple lda p8v_yy
|
||||
and #1 ; determine stipple pattern to use
|
||||
bne +
|
||||
ldy #%01010101
|
||||
bra _loop
|
||||
+ ldy #%10101010
|
||||
_loop lda length
|
||||
ora length+1
|
||||
_loop lda p8v_length
|
||||
ora p8v_length+1
|
||||
beq _done
|
||||
sty cx16.VERA_DATA0
|
||||
lda length
|
||||
lda p8v_length
|
||||
bne +
|
||||
dec length+1
|
||||
+ dec length
|
||||
dec p8v_length+1
|
||||
+ dec p8v_length
|
||||
bra _loop
|
||||
_done
|
||||
}}
|
||||
@@ -554,9 +554,9 @@ _done
|
||||
if draw {
|
||||
; solid color or perhaps stipple
|
||||
%asm {{
|
||||
lda xx
|
||||
eor yy
|
||||
ora dont_stipple_flag
|
||||
lda p8v_xx
|
||||
eor p8v_yy
|
||||
ora p8v_dont_stipple_flag
|
||||
and #1
|
||||
}}
|
||||
if_nz {
|
||||
@@ -575,7 +575,7 @@ _done
|
||||
|
||||
sub prepare() {
|
||||
%asm {{
|
||||
lda xx
|
||||
lda p8v_xx
|
||||
and #7
|
||||
pha ; xbits
|
||||
}}
|
||||
@@ -587,12 +587,12 @@ _done
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda xx+1
|
||||
lda p8v_xx+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda xx
|
||||
lda p8v_xx
|
||||
sta cx16.VERA_ADDR_L
|
||||
ply ; xbits
|
||||
lda maskbits,y
|
||||
lda p8v_maskbits,y
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -608,7 +608,7 @@ _done
|
||||
|
||||
sub pget(uword @zp xx, uword yy) -> ubyte {
|
||||
%asm {{
|
||||
lda xx
|
||||
lda p8v_xx
|
||||
and #7
|
||||
pha ; xbits
|
||||
}}
|
||||
@@ -621,12 +621,12 @@ _done
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda xx+1
|
||||
lda p8v_xx+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda xx
|
||||
lda p8v_xx
|
||||
sta cx16.VERA_ADDR_L
|
||||
ply ; xbits
|
||||
lda plot.maskbits,y
|
||||
lda p8s_plot.p8v_maskbits,y
|
||||
and cx16.VERA_DATA0
|
||||
beq +
|
||||
lda #1
|
||||
@@ -662,21 +662,21 @@ _done
|
||||
;; cx16.r12L++
|
||||
%asm {{
|
||||
ldy cx16.r12L
|
||||
lda sxl
|
||||
sta stack_xl_lsb,y
|
||||
lda sxl+1
|
||||
sta stack_xl_msb,y
|
||||
lda sxr
|
||||
sta stack_xr_lsb,y
|
||||
lda sxr+1
|
||||
sta stack_xr_msb,y
|
||||
lda sy
|
||||
sta stack_y_lsb,y
|
||||
lda sy+1
|
||||
sta stack_y_msb,y
|
||||
lda p8v_sxl
|
||||
sta p8v_stack_xl_lsb,y
|
||||
lda p8v_sxl+1
|
||||
sta p8v_stack_xl_msb,y
|
||||
lda p8v_sxr
|
||||
sta p8v_stack_xr_lsb,y
|
||||
lda p8v_sxr+1
|
||||
sta p8v_stack_xr_msb,y
|
||||
lda p8v_sy
|
||||
sta p8v_stack_y_lsb,y
|
||||
lda p8v_sy+1
|
||||
sta p8v_stack_y_msb,y
|
||||
ldy cx16.r12L
|
||||
lda sdy
|
||||
sta stack_dy,y
|
||||
lda p8v_sdy
|
||||
sta p8v_stack_dy,y
|
||||
inc cx16.r12L
|
||||
}}
|
||||
}
|
||||
@@ -690,21 +690,21 @@ _done
|
||||
%asm {{
|
||||
dec cx16.r12L
|
||||
ldy cx16.r12L
|
||||
lda stack_xl_lsb,y
|
||||
sta x1
|
||||
lda stack_xl_msb,y
|
||||
sta x1+1
|
||||
lda stack_xr_lsb,y
|
||||
sta x2
|
||||
lda stack_xr_msb,y
|
||||
sta x2+1
|
||||
lda stack_y_lsb,y
|
||||
sta yy
|
||||
lda stack_y_msb,y
|
||||
sta yy+1
|
||||
lda p8v_stack_xl_lsb,y
|
||||
sta p8v_x1
|
||||
lda p8v_stack_xl_msb,y
|
||||
sta p8v_x1+1
|
||||
lda p8v_stack_xr_lsb,y
|
||||
sta p8v_x2
|
||||
lda p8v_stack_xr_msb,y
|
||||
sta p8v_x2+1
|
||||
lda p8v_stack_y_lsb,y
|
||||
sta p8v_yy
|
||||
lda p8v_stack_y_msb,y
|
||||
sta p8v_yy+1
|
||||
ldy cx16.r12L
|
||||
lda stack_dy,y
|
||||
sta dy
|
||||
lda p8v_stack_dy,y
|
||||
sta p8v_dy
|
||||
}}
|
||||
yy+=dy
|
||||
}
|
||||
@@ -797,7 +797,7 @@ skip:
|
||||
cx16.vaddr_autoincr(charset_bank, chardataptr, 0, 1)
|
||||
%asm {{
|
||||
; pre-shift the bits
|
||||
lda text.xx
|
||||
lda p8s_text.p8v_xx
|
||||
and #7
|
||||
sta P8ZP_SCRATCH_B1
|
||||
ldy #0
|
||||
@@ -810,9 +810,9 @@ skip:
|
||||
ror P8ZP_SCRATCH_REG
|
||||
dex
|
||||
bne -
|
||||
+ sta char_bitmap_bytes_left,y
|
||||
+ sta p8v_char_bitmap_bytes_left,y
|
||||
lda P8ZP_SCRATCH_REG
|
||||
sta char_bitmap_bytes_right,y
|
||||
sta p8v_char_bitmap_bytes_right,y
|
||||
iny
|
||||
cpy #8
|
||||
bne --
|
||||
@@ -823,7 +823,7 @@ skip:
|
||||
if draw {
|
||||
%asm {{
|
||||
ldy #0
|
||||
- lda char_bitmap_bytes_left,y
|
||||
- lda p8v_char_bitmap_bytes_left,y
|
||||
ora cx16.VERA_DATA1
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
@@ -833,7 +833,7 @@ skip:
|
||||
} else {
|
||||
%asm {{
|
||||
ldy #0
|
||||
- lda char_bitmap_bytes_left,y
|
||||
- lda p8v_char_bitmap_bytes_left,y
|
||||
eor #255
|
||||
and cx16.VERA_DATA1
|
||||
sta cx16.VERA_DATA0
|
||||
@@ -849,7 +849,7 @@ skip:
|
||||
if draw {
|
||||
%asm {{
|
||||
ldy #0
|
||||
- lda char_bitmap_bytes_right,y
|
||||
- lda p8v_char_bitmap_bytes_right,y
|
||||
ora cx16.VERA_DATA1
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
@@ -859,7 +859,7 @@ skip:
|
||||
} else {
|
||||
%asm {{
|
||||
ldy #0
|
||||
- lda char_bitmap_bytes_right,y
|
||||
- lda p8v_char_bitmap_bytes_right,y
|
||||
eor #255
|
||||
and cx16.VERA_DATA1
|
||||
sta cx16.VERA_DATA0
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
; The first 16 colors can be restored to their default with set_default16()
|
||||
|
||||
palette {
|
||||
%option no_symbol_prefixing
|
||||
%option ignore_unused
|
||||
|
||||
uword vera_palette_ptr
|
||||
|
||||
@@ -14,6 +14,11 @@ palette {
|
||||
cx16.vpoke(1, vera_palette_ptr, msb(color))
|
||||
}
|
||||
|
||||
sub get_color(ubyte index) -> uword {
|
||||
vera_palette_ptr = $fa00+(index as uword * 2)
|
||||
return mkword(cx16.vpeek(1, vera_palette_ptr+1), cx16.vpeek(1, vera_palette_ptr))
|
||||
}
|
||||
|
||||
sub set_rgb_be(uword palette_ptr, uword num_colors) {
|
||||
; 1 word per color entry, $0rgb in big endian format
|
||||
vera_palette_ptr = $fa00
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
%import syslib
|
||||
|
||||
psg {
|
||||
%option no_symbol_prefixing
|
||||
%option ignore_unused
|
||||
|
||||
; $1F9C0 - $1F9FF 16 blocks of 4 PSG registers (16 voices)
|
||||
; 00 frequency word LSB
|
||||
@@ -114,10 +114,10 @@ psg {
|
||||
; cx16.r0 = the volume word (volume scaled by 256)
|
||||
; cx16.r1L = the voice number
|
||||
; cx16.r2L = attack value
|
||||
pushw(cx16.r0)
|
||||
push(cx16.r1L)
|
||||
push(cx16.r2L)
|
||||
pushw(cx16.r9)
|
||||
sys.pushw(cx16.r0)
|
||||
sys.push(cx16.r1L)
|
||||
sys.push(cx16.r2L)
|
||||
sys.pushw(cx16.r9)
|
||||
; calculate new volumes
|
||||
for cx16.r1L in 0 to 15 {
|
||||
when envelope_states[cx16.r1L] {
|
||||
@@ -166,10 +166,10 @@ psg {
|
||||
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
|
||||
}
|
||||
cx16.restore_vera_context()
|
||||
popw(cx16.r9)
|
||||
pop(cx16.r2L)
|
||||
pop(cx16.r1L)
|
||||
popw(cx16.r0)
|
||||
cx16.r9 = sys.popw()
|
||||
cx16.r2L = sys.pop()
|
||||
cx16.r1L = sys.pop()
|
||||
cx16.r0 = sys.popw()
|
||||
return true ; run the system IRQ handler afterwards
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
; note: collision mask is not supported here yet.
|
||||
|
||||
sprites {
|
||||
%option ignore_unused
|
||||
|
||||
const ubyte SIZE_8 = 0
|
||||
const ubyte SIZE_16 = 1
|
||||
const ubyte SIZE_32 = 2
|
||||
@@ -39,6 +41,16 @@ sprites {
|
||||
cx16.vpoke_mask(1, sprite_reg+1, %11110000, msb(addr)) ; address 16:13
|
||||
}
|
||||
|
||||
sub get_data_ptr(ubyte spritenum) {
|
||||
; -- returns the VRAM address where the sprite's bitmap data is stored
|
||||
; R1 (byte) = the vera bank (0 or 1), R0 (word) = the address.
|
||||
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.r0 <<= 5 ; address
|
||||
}
|
||||
|
||||
sub pos(ubyte spritenum, word xpos, word ypos) {
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + spritenum*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(xpos))
|
||||
@@ -133,4 +145,47 @@ sprites {
|
||||
sub set_palette_offset(ubyte spritenum, ubyte offset) {
|
||||
cx16.vpoke_mask(1, VERA_SPRITEREGS + 7 + spritenum*$0008, %11110000, offset>>4)
|
||||
}
|
||||
|
||||
sub set_mousepointer_hand() {
|
||||
; the array below is the compressed form of this sprite image:
|
||||
; 00, 16, 16, 00, 00, 00, 00, 00, 16, 16, 16, 00, 00, 00, 00, 00,
|
||||
; 16, 01, 01, 16, 00, 00, 16, 16, 15, 01, 01, 16, 00, 00, 00, 00,
|
||||
; 16, 01, 01, 01, 16, 16, 15, 01, 16, 15, 01, 01, 16, 00, 00, 00,
|
||||
; 16, 15, 01, 01, 01, 01, 16, 15, 01, 01, 01, 01, 16, 00, 00, 00,
|
||||
; 00, 16, 15, 01, 01, 01, 01, 01, 01, 01, 11, 01, 01, 16, 00, 00,
|
||||
; 00, 00, 16, 15, 01, 01, 01, 01, 11, 01, 01, 11, 01, 16, 00, 00,
|
||||
; 00, 00, 16, 16, 15, 01, 01, 01, 01, 11, 01, 01, 01, 16, 00, 00,
|
||||
; 00, 00, 16, 12, 12, 15, 01, 01, 01, 01, 01, 01, 01, 16, 16, 00,
|
||||
; 00, 00, 16, 12, 15, 15, 01, 01, 01, 01, 01, 01, 16, 01, 01, 16,
|
||||
; 00, 00, 00, 16, 15, 15, 01, 01, 01, 01, 01, 01, 01, 01, 01, 16,
|
||||
; 00, 00, 00, 00, 16, 15, 15, 15, 15, 15, 16, 01, 01, 15, 16, 00,
|
||||
; 00, 00, 00, 00, 00, 16, 16, 16, 16, 16, 15, 01, 15, 16, 00, 00,
|
||||
; 00, 00, 00, 00, 00, 00, 00, 00, 16, 12, 15, 15, 16, 00, 00, 00,
|
||||
; 00, 00, 00, 00, 00, 00, 00, 00, 16, 12, 12, 16, 00, 00, 00, 00,
|
||||
; 00, 00, 00, 00, 00, 00, 00, 00, 00, 16, 16, 00, 00, 00, 00, 00,
|
||||
; 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
|
||||
ubyte[] hand_image_lzsa = [
|
||||
26, 31, 0, 16, 16, 0, 46, 16, 192, 58, 1, 1, 16, 156, 41, 15, 37,
|
||||
137, 42, 1, 6, 115, 2, 43, 1, 152, 34, 4, 127, 4, 15, 11, 116, 233,
|
||||
11, 37, 135, 6, 38, 7, 19, 12, 12, 67, 167, 35, 136, 237, 15, 77,
|
||||
16, 157, 37, 71, 157, 47, 2, 33, 24, 9, 15, 69, 83, 66, 94, 6, 136,
|
||||
77, 0, 186, 6, 69, 154, 6, 143, 70, 204, 15, 0, 191, 231, 232]
|
||||
|
||||
set_mousepointer_image(hand_image_lzsa, true)
|
||||
}
|
||||
|
||||
sub set_mousepointer_image(uword data, bool compressed) {
|
||||
get_data_ptr(0) ; the mouse cursor is sprite 0
|
||||
if cx16.r1L==0 and cx16.r0==0
|
||||
return ; mouse cursor not enabled
|
||||
ubyte vbank = cx16.r1L
|
||||
cx16.vaddr(vbank, cx16.r0, 0, 1)
|
||||
if compressed {
|
||||
void cx16.memory_decompress(data, &cx16.VERA_DATA0) ; decompress directly into vram
|
||||
} else {
|
||||
for cx16.r0L in 0 to 255 {
|
||||
cx16.VERA_DATA0 = data[cx16.r0L]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
; Prog8 definitions for the CommanderX16
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
|
||||
cbm {
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
cbm {
|
||||
; Commodore (CBM) common variables, vectors and kernal routines
|
||||
|
||||
%option no_symbol_prefixing
|
||||
|
||||
; STROUT --> use txt.print
|
||||
; CLEARSCR -> use txt.clear_screen
|
||||
; HOMECRSR -> use txt.home or txt.plot
|
||||
|
||||
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip, including resetting to the default color palette
|
||||
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip, including resetting to the default color palette. Note: also sets the video mode back to VGA
|
||||
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, IRQ, ...)
|
||||
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, screen
|
||||
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
||||
@@ -29,7 +29,7 @@ romsub $FFAB = UNTLK() clobbers(A) ; command serial
|
||||
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
||||
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word (use CLEARST to reset it to 0)
|
||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
|
||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||
@@ -81,12 +81,19 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
}}
|
||||
}
|
||||
|
||||
sub CLEARST() {
|
||||
; -- Set the ST status variable back to 0. (there's no direct kernal call for this)
|
||||
; Note: a drive error state (blinking led) isn't cleared! You can use diskio.status() to clear that.
|
||||
SETNAM(0, $0000)
|
||||
SETLFS(15, 3, 15)
|
||||
void OPEN()
|
||||
CLOSE(15)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cx16 {
|
||||
|
||||
%option no_symbol_prefixing
|
||||
|
||||
; irq, system and hardware vectors:
|
||||
&uword IERROR = $0300
|
||||
&uword IMAIN = $0302
|
||||
@@ -232,6 +239,7 @@ cx16 {
|
||||
const uword VERA_BASE = $9F20
|
||||
&ubyte VERA_ADDR_L = VERA_BASE + $0000
|
||||
&ubyte VERA_ADDR_M = VERA_BASE + $0001
|
||||
&uword VERA_ADDR = VERA_BASE + $0000 ; still need to do the _H separately
|
||||
&ubyte VERA_ADDR_H = VERA_BASE + $0002
|
||||
&ubyte VERA_DATA0 = VERA_BASE + $0003
|
||||
&ubyte VERA_DATA1 = VERA_BASE + $0004
|
||||
@@ -257,15 +265,19 @@ cx16 {
|
||||
&ubyte VERA_L0_TILEBASE = VERA_BASE + $000F
|
||||
&ubyte VERA_L0_HSCROLL_L = VERA_BASE + $0010
|
||||
&ubyte VERA_L0_HSCROLL_H = VERA_BASE + $0011
|
||||
&uword VERA_L0_HSCROLL = VERA_BASE + $0010
|
||||
&ubyte VERA_L0_VSCROLL_L = VERA_BASE + $0012
|
||||
&ubyte VERA_L0_VSCROLL_H = VERA_BASE + $0013
|
||||
&uword VERA_L0_VSCROLL = VERA_BASE + $0012
|
||||
&ubyte VERA_L1_CONFIG = VERA_BASE + $0014
|
||||
&ubyte VERA_L1_MAPBASE = VERA_BASE + $0015
|
||||
&ubyte VERA_L1_TILEBASE = VERA_BASE + $0016
|
||||
&ubyte VERA_L1_HSCROLL_L = VERA_BASE + $0017
|
||||
&ubyte VERA_L1_HSCROLL_H = VERA_BASE + $0018
|
||||
&uword VERA_L1_HSCROLL = VERA_BASE + $0017
|
||||
&ubyte VERA_L1_VSCROLL_L = VERA_BASE + $0019
|
||||
&ubyte VERA_L1_VSCROLL_H = VERA_BASE + $001A
|
||||
&uword VERA_L1_VSCROLL = VERA_BASE + $0019
|
||||
&ubyte VERA_AUDIO_CTRL = VERA_BASE + $001B
|
||||
&ubyte VERA_AUDIO_RATE = VERA_BASE + $001C
|
||||
&ubyte VERA_AUDIO_DATA = VERA_BASE + $001D
|
||||
@@ -279,16 +291,21 @@ cx16 {
|
||||
&ubyte VERA_FX_MULT = VERA_BASE + $000c
|
||||
&ubyte VERA_FX_X_INCR_L = VERA_BASE + $0009
|
||||
&ubyte VERA_FX_X_INCR_H = VERA_BASE + $000a
|
||||
&uword VERA_FX_X_INCR = VERA_BASE + $0009
|
||||
&ubyte VERA_FX_Y_INCR_L = VERA_BASE + $000b
|
||||
&ubyte VERA_FX_Y_INCR_H = VERA_BASE + $000c
|
||||
&uword VERA_FX_Y_INCR = VERA_BASE + $000b
|
||||
&ubyte VERA_FX_X_POS_L = VERA_BASE + $0009
|
||||
&ubyte VERA_FX_X_POS_H = VERA_BASE + $000a
|
||||
&uword VERA_FX_X_POS = VERA_BASE + $0009
|
||||
&ubyte VERA_FX_Y_POS_L = VERA_BASE + $000b
|
||||
&ubyte VERA_FX_Y_POS_H = VERA_BASE + $000c
|
||||
&uword VERA_FX_Y_POS = VERA_BASE + $000b
|
||||
&ubyte VERA_FX_X_POS_S = VERA_BASE + $0009
|
||||
&ubyte VERA_FX_Y_POS_S = VERA_BASE + $000a
|
||||
&ubyte VERA_FX_POLY_FILL_L = VERA_BASE + $000b
|
||||
&ubyte VERA_FX_POLY_FILL_H = VERA_BASE + $000c
|
||||
&uword VERA_FX_POLY_FILL = VERA_BASE + $000b
|
||||
&ubyte VERA_FX_CACHE_L = VERA_BASE + $0009
|
||||
&ubyte VERA_FX_CACHE_M = VERA_BASE + $000a
|
||||
&ubyte VERA_FX_CACHE_H = VERA_BASE + $000b
|
||||
@@ -432,12 +449,70 @@ romsub $ff53 = joystick_scan() clobbers(A, X, Y)
|
||||
romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
|
||||
romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values
|
||||
|
||||
; Audio (rom bank 10)
|
||||
romsub $C04B = psg_init() clobbers(A,X,Y) ; (re)init Vera PSG
|
||||
romsub $C063 = ym_init() clobbers(A,X,Y) -> bool @Pc ; (re)init YM chip
|
||||
romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> bool @Pc ; load default YM patches
|
||||
romsub $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc ; (re)initialize both vera PSG and YM audio chips
|
||||
; TODO: add more of the audio routines?
|
||||
; X16Edit (rom bank 13/14 but you ideally should use the routine search_x16edit() to search for the correct bank)
|
||||
romsub $C000 = x16edit_default() clobbers(A,X,Y)
|
||||
romsub $C003 = x16edit_loadfile(ubyte firstbank @X, ubyte lastbank @Y, str filename @R0, ubyte filenameLength @R1) clobbers(A,X,Y)
|
||||
romsub $C006 = x16edit_loadfile_options(ubyte firstbank @X, ubyte lastbank @Y, str filename @R0,
|
||||
uword filenameLengthAndOptions @R1, uword tabstopAndWordwrap @R2,
|
||||
uword disknumberAndColors @R3, uword headerAndStatusColors @R4) clobbers(A,X,Y)
|
||||
|
||||
; Audio (rom bank 10 - you have to activate this bank manually first! Or use callfar/JSRFAR.)
|
||||
romsub $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc ; (re)initialize both vera PSG and YM audio chips
|
||||
romsub $C000 = bas_fmfreq(ubyte channel @A, uword freq @XY, bool noretrigger @Pc) clobbers(A,X,Y) -> bool @Pc
|
||||
romsub $C003 = bas_fmnote(ubyte channel @A, ubyte note @X, ubyte fracsemitone @Y, bool noretrigger @Pc) clobbers(A,X,Y) -> bool @Pc
|
||||
romsub $C006 = bas_fmplaystring(ubyte length @A, str string @XY) clobbers(A,X,Y)
|
||||
romsub $C009 = bas_fmvib(ubyte speed @A, ubyte depth @X) clobbers(A,X,Y) -> bool @Pc
|
||||
romsub $C00C = bas_playstringvoice(ubyte channel @A) clobbers(Y)
|
||||
romsub $C00F = bas_psgfreq(ubyte voice @A, uword freq @XY) clobbers(A,X,Y) -> bool @Pc
|
||||
romsub $C012 = bas_psgnote(ubyte voice @A, ubyte note @X, ubyte fracsemitone @Y) clobbers(A,X,Y) -> bool @Pc
|
||||
romsub $C015 = bas_psgwav(ubyte voice @A, ubyte waveform @X) clobbers(A,X,Y) -> bool @Pc
|
||||
romsub $C018 = bas_psgplaystring(ubyte length @A, str string @XY) clobbers(A,X,Y)
|
||||
romsub $C08D = bas_fmchordstring(ubyte length @A, str string @XY) clobbers(A,X,Y)
|
||||
romsub $C090 = bas_psgchordstring(ubyte length @A, str string @XY) clobbers(A,X,Y)
|
||||
romsub $C01B = notecon_bas2fm(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
|
||||
romsub $C01E = notecon_bas2midi(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
|
||||
romsub $C021 = notecon_bas2psg(ubyte note @X, ubyte fracsemitone @Y) clobbers(A) -> uword @XY, bool @Pc
|
||||
romsub $C024 = notecon_fm2bas(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
|
||||
romsub $C027 = notecon_fm2midi(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
|
||||
romsub $C02A = notecon_fm2psg(ubyte note @X, ubyte fracsemitone @Y) clobbers(A) -> uword @XY, bool @Pc
|
||||
romsub $C02D = notecon_freq2bas(uword freqHz @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
|
||||
romsub $C030 = notecon_freq2fm(uword freqHz @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
|
||||
romsub $C033 = notecon_freq2midi(uword freqHz @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
|
||||
romsub $C036 = notecon_freq2psg(uword freqHz @XY) clobbers(A) -> uword @XY, bool @Pc
|
||||
romsub $C039 = notecon_midi2bas(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
|
||||
romsub $C03C = notecon_midi2fm(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
|
||||
romsub $C03F = notecon_midi2psg(ubyte note @X, ubyte fracsemitone @Y) clobbers(A) -> uword @XY, bool @Pc
|
||||
romsub $C042 = notecon_psg2bas(uword freq @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
|
||||
romsub $C045 = notecon_psg2fm(uword freq @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
|
||||
romsub $C048 = notecon_psg2midi(uword freq @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
|
||||
romsub $C04B = psg_init() clobbers(A,X,Y) ; (re)init Vera PSG
|
||||
romsub $C04E = psg_playfreq(ubyte voice @A, uword freq @XY) clobbers(A,X,Y)
|
||||
romsub $C051 = psg_read(ubyte offset @X, bool cookedVol @Pc) clobbers(Y) -> ubyte @A
|
||||
romsub $C054 = psg_setatten(ubyte voice @A, ubyte attenuation @X) clobbers(A,X,Y)
|
||||
romsub $C057 = psg_setfreq(ubyte voice @A, uword freq @XY) clobbers(A,X,Y)
|
||||
romsub $C05A = psg_setpan(ubyte voice @A, ubyte panning @X) clobbers(A,X,Y)
|
||||
romsub $C05D = psg_setvol(ubyte voice @A, ubyte volume @X) clobbers(A,X,Y)
|
||||
romsub $C060 = psg_write(ubyte value @A, ubyte offset @X) clobbers(Y)
|
||||
romsub $C0A2 = psg_write_fast(ubyte value @A, ubyte offset @X) clobbers(Y)
|
||||
romsub $C093 = psg_getatten(ubyte voice @A) clobbers(Y) -> ubyte @X
|
||||
romsub $C096 = psg_getpan(ubyte voice @A) clobbers(Y) -> ubyte @X
|
||||
romsub $C063 = ym_init() clobbers(A,X,Y) -> bool @Pc ; (re)init YM chip
|
||||
romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> bool @Pc ; load default YM patches
|
||||
romsub $C069 = ym_loadpatch(ubyte channel @A, uword patchOrAddress @XY, bool what @Pc) clobbers(A,X,Y)
|
||||
romsub $C06C = ym_loadpatchlfn(ubyte channel @A, ubyte lfn @X) clobbers(X,Y) -> ubyte @A, bool @Pc
|
||||
romsub $C06F = ym_playdrum(ubyte channel @A, ubyte note @X) clobbers(A,X,Y) -> bool @Pc
|
||||
romsub $C072 = ym_playnote(ubyte channel @A, ubyte kc @X, ubyte kf @Y, bool notrigger @Pc) clobbers(A,X,Y) -> bool @Pc
|
||||
romsub $C075 = ym_setatten(ubyte channel @A, ubyte attenuation @X) clobbers(Y) -> bool @Pc
|
||||
romsub $C078 = ym_setdrum(ubyte channel @A, ubyte note @X) clobbers(A,X,Y) -> bool @Pc
|
||||
romsub $C07B = ym_setnote(ubyte channel @A, ubyte kc @X, ubyte kf @Y) clobbers(A,X,Y) -> bool @Pc
|
||||
romsub $C07E = ym_setpan(ubyte channel @A, ubyte panning @X) clobbers(A,X,Y) -> bool @Pc
|
||||
romsub $C081 = ym_read(ubyte register @X, bool cooked @Pc) clobbers(Y) -> ubyte @A, bool @Pc
|
||||
romsub $C084 = ym_release(ubyte channel @A) clobbers(A,X,Y) -> bool @Pc
|
||||
romsub $C087 = ym_trigger(ubyte channel @A, bool noRelease @Pc) clobbers(A,X,Y) -> bool @Pc
|
||||
romsub $C08A = ym_write(ubyte value @A, ubyte register @X) clobbers(Y) -> bool @Pc
|
||||
romsub $C099 = ym_getatten(ubyte channel @A) clobbers(Y) -> ubyte @X
|
||||
romsub $C09C = ym_getpan(ubyte channel @A) clobbers(Y) -> ubyte @X
|
||||
romsub $C0A5 = ym_get_chip_type() clobbers(X) -> ubyte @A
|
||||
|
||||
|
||||
asmsub set_screen_mode(ubyte mode @A) clobbers(A,X,Y) -> bool @Pc {
|
||||
@@ -545,7 +620,6 @@ asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
|
||||
; note: inefficient when reading multiple sequential bytes!
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
and #1
|
||||
sta cx16.VERA_ADDR_H
|
||||
sty cx16.VERA_ADDR_M
|
||||
stx cx16.VERA_ADDR_L
|
||||
@@ -559,7 +633,6 @@ asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrO
|
||||
; with optional auto increment or decrement of 1.
|
||||
; Note that the vaddr_autoincr() and vaddr_autodecr() routines allow to set all possible strides, not just 1.
|
||||
%asm {{
|
||||
and #1
|
||||
pha
|
||||
lda cx16.r1
|
||||
and #1
|
||||
@@ -615,8 +688,7 @@ asmsub vaddr_autoincr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, uword
|
||||
sta cx16.VERA_ADDR_H
|
||||
rts
|
||||
|
||||
_setup and #1
|
||||
sta P8ZP_SCRATCH_REG
|
||||
_setup sta P8ZP_SCRATCH_REG
|
||||
lda cx16.r1
|
||||
and #1
|
||||
sta cx16.VERA_CTRL
|
||||
@@ -686,7 +758,6 @@ asmsub vpoke(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
|
||||
; note: inefficient when writing multiple sequential bytes!
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
and #1
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.r0
|
||||
sta cx16.VERA_ADDR_L
|
||||
@@ -702,7 +773,6 @@ asmsub vpoke_or(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A) {
|
||||
; note: inefficient when writing multiple sequential bytes!
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
and #1
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.r0
|
||||
sta cx16.VERA_ADDR_L
|
||||
@@ -720,7 +790,6 @@ asmsub vpoke_and(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
|
||||
; note: inefficient when writing multiple sequential bytes!
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
and #1
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.r0
|
||||
sta cx16.VERA_ADDR_L
|
||||
@@ -738,7 +807,6 @@ asmsub vpoke_xor(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A)
|
||||
; note: inefficient when writing multiple sequential bytes!
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
and #1
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.r0
|
||||
sta cx16.VERA_ADDR_L
|
||||
@@ -757,7 +825,6 @@ asmsub vpoke_mask(ubyte bank @A, uword address @R0, ubyte mask @X, ubyte value @
|
||||
%asm {{
|
||||
sty P8ZP_SCRATCH_B1
|
||||
stz cx16.VERA_CTRL
|
||||
and #1
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.r0
|
||||
sta cx16.VERA_ADDR_L
|
||||
@@ -1057,14 +1124,100 @@ inline asmsub disable_irq_handlers() {
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
sub search_x16edit() -> ubyte {
|
||||
; -- Search the rom bank that contains x16edit. Returns bank number, or 255 if not found.
|
||||
cx16.r0L = cx16.getrombank()
|
||||
sys.set_irqd()
|
||||
str @shared signature = petscii:"x16edit"
|
||||
for cx16.r1L in 31 downto 0 {
|
||||
cx16.rombank(cx16.r1L)
|
||||
cx16.r2 = $fff0
|
||||
%asm {{
|
||||
ldy #0
|
||||
- lda signature,y
|
||||
cmp (cx16.r2),y
|
||||
bne +
|
||||
iny
|
||||
cpy #7
|
||||
bne -
|
||||
sec
|
||||
bcs ++
|
||||
+ clc
|
||||
+
|
||||
}}
|
||||
if_cs {
|
||||
cx16.rombank(cx16.r0L)
|
||||
sys.clear_irqd()
|
||||
return cx16.r1L
|
||||
}
|
||||
}
|
||||
sys.clear_irqd()
|
||||
return 255
|
||||
}
|
||||
|
||||
asmsub cpu_is_65816() -> bool @A {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
%asm {{
|
||||
php
|
||||
clv
|
||||
.byte $e2, $ea ; SEP #$ea, should be interpreted as 2 NOPs by 6502. 65c816 will set the Overflow flag.
|
||||
bvc +
|
||||
lda #1
|
||||
plp
|
||||
rts
|
||||
+ lda #0
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub set_program_args(uword args_ptr, ubyte args_size) {
|
||||
; Set the inter-program arguments.
|
||||
; standardized way to pass arguments between programs is in ram bank 0, address $bf00-$bfff.
|
||||
; see https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2007%20-%20Memory%20Map.md#bank-0
|
||||
sys.push(getrambank())
|
||||
rambank(0)
|
||||
sys.memcopy(args_ptr, $bf00, args_size)
|
||||
if args_size<255
|
||||
@($bf00+args_size) = 0
|
||||
rambank(sys.pop())
|
||||
}
|
||||
|
||||
asmsub get_program_args(uword buffer @R0, ubyte buf_size @R1, bool binary @Pc) {
|
||||
; Retrieve the inter-program arguments. If binary=false, it treats them as a string (stops copying at first zero).
|
||||
; standardized way to pass arguments between programs is in ram bank 0, address $bf00-$bfff.
|
||||
; see https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2007%20-%20Memory%20Map.md#bank-0
|
||||
%asm {{
|
||||
lda #0
|
||||
rol a
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda $00
|
||||
pha
|
||||
stz $00
|
||||
stz P8ZP_SCRATCH_W1
|
||||
lda #$bf
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
sta (cx16.r0),y
|
||||
beq +
|
||||
_continue iny
|
||||
cpy cx16.r1L ; max size?
|
||||
bne -
|
||||
beq ++
|
||||
+ lda P8ZP_SCRATCH_REG ; binary?
|
||||
bne _continue
|
||||
+ pla
|
||||
sta $00
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const ubyte target = 16 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
|
||||
|
||||
asmsub init_system() {
|
||||
@@ -1135,6 +1288,9 @@ asmsub cleanup_at_exit() {
|
||||
lda #4
|
||||
sta $01 ; rom bank 4 (basic)
|
||||
stz $2d ; hack to reset machine code monitor bank to 0
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
_exitcode = *+1
|
||||
lda #0 ; exit code possibly modified in exit()
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -1498,10 +1654,10 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
asmsub exit(ubyte returnvalue @A) {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
%asm {{
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
sta cleanup_at_exit._exitcode
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
txs
|
||||
rts ; return to original caller
|
||||
jmp cleanup_at_exit
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -1511,4 +1667,31 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
ldy #>prog8_program_end
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push(ubyte value @A) {
|
||||
%asm {{
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pushw(uword value @AY) {
|
||||
%asm {{
|
||||
pha
|
||||
phy
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub popw() -> uword @AY {
|
||||
%asm {{
|
||||
ply
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
txt {
|
||||
|
||||
%option no_symbol_prefixing
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
const ubyte DEFAULT_WIDTH = 80
|
||||
const ubyte DEFAULT_HEIGHT = 60
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
; Experimental Vera FX support.
|
||||
; Somewhat experimental Vera FX support.
|
||||
; Docs:
|
||||
; https://github.com/X16Community/x16-docs/blob/master/VERA%20FX%20Reference.md
|
||||
; https://docs.google.com/document/d/1q34uWOiM3Be2pnaHRVgSdHySI-qsiQWPTo_gfE54PTg/edit
|
||||
|
||||
verafx {
|
||||
%option no_symbol_prefixing
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
sub available() -> bool {
|
||||
; returns true if Vera FX is available (Vera V0.3.1 or later), false if not.
|
||||
@@ -21,7 +21,7 @@ verafx {
|
||||
return cx16.r1L
|
||||
}
|
||||
|
||||
sub clear(ubyte vbank, uword vaddr, ubyte data, uword amountof32bits) {
|
||||
sub clear(ubyte vbank, uword vaddr, ubyte data, uword num_longwords) {
|
||||
; use cached 4-byte write to quickly clear a portion of the video memory to a given byte value
|
||||
; this routine is around 3 times faster as gfx2.clear_screen()
|
||||
cx16.VERA_CTRL = 0
|
||||
@@ -37,24 +37,24 @@ verafx {
|
||||
cx16.VERA_FX_MULT = 0
|
||||
cx16.VERA_FX_CTRL = %01000000 ; cache write enable
|
||||
|
||||
if (amountof32bits & %1111110000000011) == 0 {
|
||||
repeat lsb(amountof32bits >> 2)
|
||||
if (num_longwords & %1111110000000011) == 0 {
|
||||
repeat lsb(num_longwords >> 2)
|
||||
unroll 4 cx16.VERA_DATA0=0 ; write 4*4 bytes at a time, unrolled
|
||||
}
|
||||
else if (amountof32bits & %1111111000000001) == 0 {
|
||||
repeat lsb(amountof32bits >> 1)
|
||||
else if (num_longwords & %1111111000000001) == 0 {
|
||||
repeat lsb(num_longwords >> 1)
|
||||
unroll 2 cx16.VERA_DATA0=0 ; write 2*4 bytes at a time, unrolled
|
||||
}
|
||||
else if (lsb(amountof32bits) & 3) == 0 {
|
||||
repeat amountof32bits >> 2
|
||||
else if (lsb(num_longwords) & 3) == 0 {
|
||||
repeat num_longwords >> 2
|
||||
unroll 4 cx16.VERA_DATA0=0 ; write 4*4 bytes at a time, unrolled
|
||||
}
|
||||
else if (lsb(amountof32bits) & 1) == 0 {
|
||||
repeat amountof32bits >> 1
|
||||
else if (lsb(num_longwords) & 1) == 0 {
|
||||
repeat num_longwords >> 1
|
||||
unroll 2 cx16.VERA_DATA0=0 ; write 2*4 bytes at a time, unrolled
|
||||
}
|
||||
else {
|
||||
repeat amountof32bits
|
||||
repeat num_longwords
|
||||
cx16.VERA_DATA0=0 ; write 4 bytes at a time
|
||||
}
|
||||
|
||||
@@ -62,6 +62,53 @@ verafx {
|
||||
cx16.VERA_CTRL = 0
|
||||
}
|
||||
|
||||
sub copy(ubyte srcbank, uword srcaddr, ubyte tgtbank, uword tgtaddr, uword num_longwords) {
|
||||
; use cached 4-byte writes to quickly copy a portion of the video memory to somewhere else
|
||||
; this routine is about 50% faster as a plain byte-by-byte copy
|
||||
cx16.VERA_CTRL = 1
|
||||
cx16.VERA_ADDR_H = srcbank | %00010000 ; source: 1-byte increment
|
||||
cx16.VERA_ADDR_M = msb(srcaddr)
|
||||
cx16.VERA_ADDR_L = lsb(srcaddr)
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_H = tgtbank | %00110000 ; target: 4-byte increment
|
||||
cx16.VERA_ADDR_M = msb(tgtaddr)
|
||||
cx16.VERA_ADDR_L = lsb(tgtaddr)
|
||||
cx16.VERA_CTRL = 2<<1 ; dcsel = 2
|
||||
cx16.VERA_FX_MULT = 0
|
||||
cx16.VERA_FX_CTRL = %01100000 ; cache write enable + cache fill enable
|
||||
cx16.r0 = num_longwords
|
||||
|
||||
if (cx16.r0L & 1) == 0 {
|
||||
repeat cx16.r0>>1 {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA1 ; fill cache with 4 source bytes...
|
||||
lda cx16.VERA_DATA1
|
||||
lda cx16.VERA_DATA1
|
||||
lda cx16.VERA_DATA1
|
||||
stz cx16.VERA_DATA0 ; write 4 bytes at once.
|
||||
lda cx16.VERA_DATA1 ; fill cache with 4 source bytes...
|
||||
lda cx16.VERA_DATA1
|
||||
lda cx16.VERA_DATA1
|
||||
lda cx16.VERA_DATA1
|
||||
stz cx16.VERA_DATA0 ; write 4 bytes at once.
|
||||
}}
|
||||
}
|
||||
} else {
|
||||
repeat cx16.r0 {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA1 ; fill cache with 4 source bytes...
|
||||
lda cx16.VERA_DATA1
|
||||
lda cx16.VERA_DATA1
|
||||
lda cx16.VERA_DATA1
|
||||
stz cx16.VERA_DATA0 ; write 4 bytes at once.
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
cx16.VERA_FX_CTRL = 0 ; cache write disable
|
||||
cx16.VERA_CTRL = 0
|
||||
}
|
||||
|
||||
; unsigned multiplication just passes the values as signed to muls
|
||||
; if you do this yourself in your call to muls, it will save a few instructions.
|
||||
sub mult(uword value1, uword value2) -> uword {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
%import textio
|
||||
|
||||
cx16logo {
|
||||
%option no_symbol_prefixing
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
sub logo_at(ubyte column, ubyte row) {
|
||||
uword strptr
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
; C64/C128 disk drive I/O routines.
|
||||
|
||||
%import textio
|
||||
%import conv
|
||||
%import string
|
||||
%import syslib
|
||||
|
||||
diskio {
|
||||
%option no_symbol_prefixing
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
const ubyte READ_IO_CHANNEL=12
|
||||
const ubyte WRITE_IO_CHANNEL=13
|
||||
|
||||
ubyte drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
|
||||
ubyte @shared drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
|
||||
|
||||
sub reset_read_channel() {
|
||||
void cbm.CHKIN(READ_IO_CHANNEL)
|
||||
@@ -30,8 +31,6 @@ diskio {
|
||||
if_cs
|
||||
goto io_error
|
||||
reset_read_channel()
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
repeat 4 {
|
||||
void cbm.CHRIN() ; skip the 4 prologue bytes
|
||||
@@ -89,8 +88,6 @@ io_error:
|
||||
if_cs
|
||||
goto io_error
|
||||
reset_read_channel()
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
while cbm.CHRIN()!='"' {
|
||||
; skip up to entry name
|
||||
@@ -175,8 +172,6 @@ io_error:
|
||||
if_cs
|
||||
goto io_error
|
||||
reset_read_channel()
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
repeat 4 {
|
||||
void cbm.CHRIN() ; skip the 4 prologue bytes
|
||||
@@ -186,6 +181,7 @@ io_error:
|
||||
return true
|
||||
|
||||
io_error:
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
lf_end_list()
|
||||
return false
|
||||
}
|
||||
@@ -281,21 +277,20 @@ close_end:
|
||||
cbm.SETLFS(READ_IO_CHANNEL, drivenumber, READ_IO_CHANNEL) ; note: has to be Channel,x,Channel because otherwise f_seek doesn't work
|
||||
void cbm.OPEN() ; open 12,8,12,"filename"
|
||||
if_cc {
|
||||
reset_read_channel()
|
||||
if cbm.READST()==0 {
|
||||
iteration_in_progress = true
|
||||
reset_read_channel()
|
||||
if_cc {
|
||||
void cbm.CHRIN() ; read first byte to test for file not found
|
||||
if not cbm.READST() {
|
||||
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
|
||||
void cbm.OPEN() ; re-open the file
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
return true
|
||||
}
|
||||
void cbm.CHRIN() ; read first byte to test for file not found
|
||||
if cbm.READST()==0 {
|
||||
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
|
||||
void cbm.OPEN() ; re-open the file
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
f_close()
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -402,6 +397,7 @@ _end rts
|
||||
void cbm.OPEN() ; open 13,8,1,"filename"
|
||||
if_cc
|
||||
return not cbm.READST()
|
||||
cbm.CLOSE(WRITE_IO_CHANNEL)
|
||||
f_close_w()
|
||||
return false
|
||||
}
|
||||
@@ -430,6 +426,13 @@ _end rts
|
||||
|
||||
sub status() -> uword {
|
||||
; -- retrieve the disk drive's current status message
|
||||
str device_not_present_error = "device not present #xx"
|
||||
if cbm.READST()==128 {
|
||||
device_not_present_error[len(device_not_present_error)-2] = 0
|
||||
conv.str_ub(drivenumber)
|
||||
void string.copy(conv.string_out, &device_not_present_error+len(device_not_present_error)-2)
|
||||
return device_not_present_error
|
||||
}
|
||||
uword messageptr = &list_filename
|
||||
cbm.SETNAM(0, list_filename)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
@@ -437,8 +440,6 @@ _end rts
|
||||
if_cs
|
||||
goto io_error
|
||||
void cbm.CHKIN(15) ; use #15 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
while not cbm.READST() {
|
||||
cx16.r5L = cbm.CHRIN()
|
||||
@@ -455,7 +456,8 @@ done:
|
||||
return list_filename
|
||||
|
||||
io_error:
|
||||
list_filename = "?disk error"
|
||||
cbm.CLOSE(15)
|
||||
list_filename = "io error"
|
||||
goto done
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
floats {
|
||||
; the floating point functions shared across compiler targets
|
||||
%option merge, no_symbol_prefixing
|
||||
%option merge, no_symbol_prefixing, ignore_unused
|
||||
|
||||
asmsub print_f(float value @FAC1) clobbers(A,X,Y) {
|
||||
asmsub print(float value @FAC1) clobbers(A,X,Y) {
|
||||
; ---- prints the floating point value (without a newline). No leading space (unlike BASIC)!
|
||||
%asm {{
|
||||
jsr str_f
|
||||
jsr tostr
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
beq +
|
||||
@@ -16,7 +16,7 @@ asmsub print_f(float value @FAC1) clobbers(A,X,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_f(float value @FAC1) clobbers(X) -> str @AY {
|
||||
asmsub tostr(float value @FAC1) clobbers(X) -> str @AY {
|
||||
; ---- converts the floating point value to a string. No leading space!
|
||||
%asm {{
|
||||
jsr FOUT
|
||||
@@ -179,7 +179,7 @@ sub ceil(float value) -> float {
|
||||
}}
|
||||
}
|
||||
|
||||
sub rndseedf(float seed) {
|
||||
sub rndseed(float seed) {
|
||||
if seed>0
|
||||
seed = -seed ; make sure fp seed is always negative
|
||||
|
||||
@@ -215,4 +215,17 @@ sub clampf(float value, float minimum, float maximum) -> float {
|
||||
return minimum
|
||||
}
|
||||
|
||||
inline asmsub push(float value @FAC1) {
|
||||
%asm {{
|
||||
jsr floats.pushFAC1
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> float @FAC1 {
|
||||
%asm {{
|
||||
clc
|
||||
jsr floats.popFAC
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
; such as abs, sqrt, clamp, min, max for example.
|
||||
|
||||
math {
|
||||
%option no_symbol_prefixing
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
%asminclude "library:math.asm"
|
||||
|
||||
@@ -484,4 +484,49 @@ log2_tab
|
||||
}}
|
||||
}
|
||||
|
||||
sub crc16(uword data, uword length) -> uword {
|
||||
; calculates the CRC16 (XMODEM) checksum of the buffer.
|
||||
cx16.r1 = data ; make sure pointer is in zp (on cx16)
|
||||
cx16.r0 = 0 ; the crc value
|
||||
repeat length {
|
||||
cx16.r0H ^= @(cx16.r1)
|
||||
repeat 8 {
|
||||
if cx16.r0H & $80 {
|
||||
cx16.r0 <<= 1
|
||||
cx16.r0 ^= $1021
|
||||
}
|
||||
else
|
||||
cx16.r0<<=1
|
||||
}
|
||||
cx16.r1++
|
||||
}
|
||||
return cx16.r0
|
||||
}
|
||||
|
||||
sub crc32(uword data, uword length) {
|
||||
; Calculates the CRC-32 (POSIX) checksum of the buffer.
|
||||
; because prog8 doesn't have 32 bits integers, we have to split up the calculation over 2 words.
|
||||
; result stored in cx16.r0 (low word) and cx16.r1 (high word)
|
||||
cx16.r2 = data ; make sure pointer is in zp (on cx16)
|
||||
cx16.r1 = 0
|
||||
cx16.r0 = 0
|
||||
repeat length {
|
||||
cx16.r1H ^= @(cx16.r2)
|
||||
repeat 8 {
|
||||
if cx16.r1H & $80 {
|
||||
cx16.r0 <<= 1
|
||||
rol(cx16.r1)
|
||||
cx16.r1 ^= $04c1
|
||||
cx16.r0 ^= $1db7
|
||||
}
|
||||
else {
|
||||
cx16.r0 <<= 1
|
||||
rol(cx16.r1)
|
||||
}
|
||||
}
|
||||
cx16.r2++
|
||||
}
|
||||
cx16.r1 ^= $ffff
|
||||
cx16.r0 ^= $ffff
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,10 @@
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
; see: https://www.pagetable.com/?p=926 , http://www.zimmers.net/cbmpics/cbm/PETx/petmem.txt
|
||||
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
cbm {
|
||||
; Commodore (CBM) common variables, vectors and kernal routines
|
||||
%option no_symbol_prefixing
|
||||
|
||||
&ubyte TIME_HI = $8d ; software jiffy clock, hi byte
|
||||
&ubyte TIME_MID = $8e ; .. mid byte
|
||||
@@ -79,7 +80,6 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const ubyte target = 32 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 32=PET
|
||||
|
||||
@@ -110,6 +110,8 @@ asmsub init_system_phase2() {
|
||||
asmsub cleanup_at_exit() {
|
||||
; executed when the main subroutine does rts
|
||||
%asm {{
|
||||
_exitcode = *+1
|
||||
lda #0 ; exit code possibly modified in exit()
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -345,12 +347,13 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub exit(ubyte returnvalue @A) {
|
||||
asmsub exit(ubyte returnvalue @A) {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
%asm {{
|
||||
sta cleanup_at_exit._exitcode
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
txs
|
||||
rts ; return to original caller
|
||||
jmp cleanup_at_exit
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -361,11 +364,37 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push(ubyte value @A) {
|
||||
%asm {{
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pushw(uword value @AY) {
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub popw() -> uword @AY {
|
||||
%asm {{
|
||||
pla
|
||||
tay
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cx16 {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||
; they are simulated on the PET as well but their location in memory is different
|
||||
; (because there's no room for them in the zeropage)
|
||||
@@ -497,4 +526,10 @@ cx16 {
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
|
||||
txt {
|
||||
%option no_symbol_prefixing
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
const ubyte DEFAULT_WIDTH = 40
|
||||
const ubyte DEFAULT_HEIGHT = 25
|
||||
|
||||
@@ -218,170 +218,6 @@ rol2_mem_ub .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
rol_array_ub .proc
|
||||
; -- rol a ubyte in an array
|
||||
lda _arg_target
|
||||
ldy _arg_target+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy _arg_index
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rol a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
_arg_target .word 0
|
||||
_arg_index .byte 0
|
||||
.pend
|
||||
|
||||
|
||||
ror_array_ub .proc
|
||||
; -- ror a ubyte in an array
|
||||
lda _arg_target
|
||||
ldy _arg_target+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy _arg_index
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ror a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
_arg_target .word 0
|
||||
_arg_index .byte 0
|
||||
.pend
|
||||
|
||||
ror2_array_ub .proc
|
||||
; -- ror2 (8-bit ror) a ubyte in an array
|
||||
lda _arg_target
|
||||
ldy _arg_target+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy _arg_index
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
lsr a
|
||||
bcc +
|
||||
ora #$80
|
||||
+ sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
_arg_target .word 0
|
||||
_arg_index .byte 0
|
||||
.pend
|
||||
|
||||
rol2_array_ub .proc
|
||||
; -- rol2 (8-bit rol) a ubyte in an array
|
||||
lda _arg_target
|
||||
ldy _arg_target+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy _arg_index
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
cmp #$80
|
||||
rol a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
_arg_target .word 0
|
||||
_arg_index .byte 0
|
||||
.pend
|
||||
|
||||
ror_array_uw .proc
|
||||
; -- ror a uword in an array
|
||||
php
|
||||
lda _arg_target
|
||||
ldy _arg_target+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda _arg_index
|
||||
asl a
|
||||
tay
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
plp
|
||||
ror a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ror a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
_arg_target .word 0
|
||||
_arg_index .byte 0
|
||||
.pend
|
||||
|
||||
rol_array_uw .proc
|
||||
; -- rol a uword in an array
|
||||
php
|
||||
lda _arg_target
|
||||
ldy _arg_target+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda _arg_index
|
||||
asl a
|
||||
tay
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
plp
|
||||
rol a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rol a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
_arg_target .word 0
|
||||
_arg_index .byte 0
|
||||
.pend
|
||||
|
||||
rol2_array_uw .proc
|
||||
; -- rol2 (16-bit rol) a uword in an array
|
||||
lda _arg_target
|
||||
ldy _arg_target+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda _arg_index
|
||||
asl a
|
||||
tay
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
asl a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rol a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
bcc +
|
||||
dey
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
adc #0
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
+ rts
|
||||
_arg_target .word 0
|
||||
_arg_index .byte 0
|
||||
.pend
|
||||
|
||||
ror2_array_uw .proc
|
||||
; -- ror2 (16-bit ror) a uword in an array
|
||||
lda _arg_target
|
||||
ldy _arg_target+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda _arg_index
|
||||
asl a
|
||||
tay
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
lsr a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ror a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
bcc +
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ora #$80
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
+ rts
|
||||
_arg_target .word 0
|
||||
_arg_index .byte 0
|
||||
.pend
|
||||
|
||||
|
||||
strcpy .proc
|
||||
; copy a string (must be 0-terminated) from A/Y to (P8ZP_SCRATCH_W1)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
; Internal library routines - always included by the compiler
|
||||
|
||||
prog8_lib {
|
||||
%option no_symbol_prefixing
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
%asminclude "library:prog8_lib.asm"
|
||||
%asminclude "library:prog8_funcs.asm"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
; 0-terminated string manipulation routines.
|
||||
|
||||
string {
|
||||
%option no_symbol_prefixing
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
asmsub length(uword string @AY) clobbers(A) -> ubyte @Y {
|
||||
; Returns the number of bytes in the string.
|
||||
@@ -20,22 +20,19 @@ string {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub left(uword source @R0, ubyte length @A, uword target @R1) clobbers(A, Y) {
|
||||
asmsub left(uword source @AX, ubyte length @Y, uword target @R1) clobbers(A, Y) {
|
||||
; Copies the left side of the source string of the given length to target string.
|
||||
; It is assumed the target string buffer is large enough to contain the result.
|
||||
; Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
|
||||
; Modifies in-place, doesn’t return a value (so can’t be used in an expression).
|
||||
%asm {{
|
||||
; need to copy the the cx16 virtual registers to zeropage to be compatible with C64...
|
||||
ldy cx16.r0
|
||||
sty P8ZP_SCRATCH_W1
|
||||
ldy cx16.r0+1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy cx16.r1
|
||||
sty P8ZP_SCRATCH_W2
|
||||
ldy cx16.r1+1
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
tay
|
||||
; need to copy the the cx16 virtual registers to zeropage to be compatible with C64...
|
||||
sta P8ZP_SCRATCH_W1
|
||||
stx P8ZP_SCRATCH_W1+1
|
||||
lda cx16.r1
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda cx16.r1+1
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
lda #0
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
cpy #0
|
||||
@@ -51,16 +48,16 @@ _loop dey
|
||||
; asmgen.out(" jsr prog8_lib.func_leftstr")
|
||||
}
|
||||
|
||||
asmsub right(uword source @R0, ubyte length @A, uword target @R1) clobbers(A,Y) {
|
||||
asmsub right(uword source @AY, ubyte length @X, uword target @R1) clobbers(A,Y) {
|
||||
; Copies the right side of the source string of the given length to target string.
|
||||
; It is assumed the target string buffer is large enough to contain the result.
|
||||
; Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
|
||||
; Modifies in-place, doesn’t return a value (so can’t be used in an expression).
|
||||
%asm {{
|
||||
; need to copy the the cx16 virtual registers to zeropage to be compatible with C64...
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
stx P8ZP_SCRATCH_B1
|
||||
sta cx16.r0
|
||||
sty cx16.r0+1
|
||||
jsr string.length
|
||||
tya
|
||||
sec
|
||||
@@ -128,16 +125,14 @@ _startloop dey
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub find(uword string @R0, ubyte character @A) -> ubyte @A, bool @Pc {
|
||||
asmsub find(uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc {
|
||||
; Locates the first position of the given character in the string,
|
||||
; returns Carry set if found + index in A, or A=0 + Carry clear if not found.
|
||||
%asm {{
|
||||
; need to copy the the cx16 virtual registers to zeropage to make this run on C64...
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
; need to copy the the cx16 virtual registers to zeropage to make this run on C64...
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_B1
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
beq _notfound
|
||||
@@ -154,6 +149,13 @@ _found tya
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub contains(uword string @AY, ubyte character @X) -> bool @Pc {
|
||||
; Just return true/false if the character is in the given string or not.
|
||||
%asm {{
|
||||
jmp find
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub copy(uword source @R0, uword target @AY) clobbers(A) -> ubyte @Y {
|
||||
; Copy a string to another, overwriting that one.
|
||||
; Returns the length of the string that was copied.
|
||||
@@ -384,7 +386,7 @@ fail clc ; yes, no match found, return with c=0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub isdigit(ubyte character @A) -> bool @Pc {
|
||||
asmsub isdigit(ubyte petsciichar @A) -> bool @Pc {
|
||||
%asm {{
|
||||
cmp #'0'
|
||||
bcs +
|
||||
@@ -398,8 +400,8 @@ fail clc ; yes, no match found, return with c=0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub isupper(ubyte character @A) -> bool @Pc {
|
||||
; shifted petscii has 2 ranges that contain the upper case letters...
|
||||
asmsub isupper(ubyte petsciichar @A) -> bool @Pc {
|
||||
; shifted petscii has 2 ranges that contain the upper case letters... 97-122 and 193-218
|
||||
%asm {{
|
||||
cmp #97
|
||||
bcs +
|
||||
@@ -418,7 +420,7 @@ _yes sec
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub islower(ubyte character @A) -> bool @Pc {
|
||||
asmsub islower(ubyte petsciichar @A) -> bool @Pc {
|
||||
%asm {{
|
||||
cmp #'a'
|
||||
bcs +
|
||||
@@ -432,7 +434,7 @@ _yes sec
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub isletter(ubyte character @A) -> bool @Pc {
|
||||
asmsub isletter(ubyte petsciichar @A) -> bool @Pc {
|
||||
%asm {{
|
||||
jsr islower
|
||||
bcs +
|
||||
@@ -440,4 +442,42 @@ _yes sec
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub isspace(ubyte petsciichar @A) -> bool @Pc {
|
||||
%asm {{
|
||||
cmp #32
|
||||
beq +
|
||||
cmp #13
|
||||
beq +
|
||||
cmp #9
|
||||
beq +
|
||||
cmp #10
|
||||
beq +
|
||||
cmp #141
|
||||
beq +
|
||||
cmp #160
|
||||
beq +
|
||||
clc
|
||||
rts
|
||||
+ sec
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub isprint(ubyte petsciichar @A) -> bool @Pc {
|
||||
%asm {{
|
||||
cmp #160
|
||||
bcc +
|
||||
rts
|
||||
+ cmp #32
|
||||
bcs +
|
||||
rts
|
||||
+ cmp #128
|
||||
bcc +
|
||||
clc
|
||||
rts
|
||||
+ sec
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
%import textio
|
||||
|
||||
test_stack {
|
||||
%option no_symbol_prefixing
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
asmsub test() {
|
||||
%asm {{
|
||||
|
||||
@@ -4,6 +4,8 @@ conv {
|
||||
|
||||
; ----- number conversions to decimal strings ----
|
||||
|
||||
%option ignore_unused
|
||||
|
||||
str string_out = "????????????????" ; result buffer for the string conversion routines
|
||||
|
||||
sub str_ub0(ubyte value) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
%import textio
|
||||
|
||||
emudbg {
|
||||
%option ignore_unused
|
||||
|
||||
sub is_emulator() -> bool {
|
||||
; Test for emulator presence.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
; Prog8 definitions for floating point handling on the VirtualMachine
|
||||
|
||||
%option enable_floats
|
||||
%option enable_floats, ignore_unused
|
||||
|
||||
floats {
|
||||
|
||||
@@ -9,31 +9,31 @@ floats {
|
||||
const float TWOPI = 2*π
|
||||
|
||||
|
||||
sub print_f(float value) {
|
||||
sub print(float value) {
|
||||
; ---- prints the floating point value (without a newline and no leading spaces).
|
||||
%ir {{
|
||||
loadm.f fr65535,floats.print_f.value
|
||||
loadm.f fr65535,floats.print.value
|
||||
syscall 25 (fr65535.f)
|
||||
return
|
||||
}}
|
||||
}
|
||||
|
||||
sub str_f(float value) -> str {
|
||||
sub tostr(float value) -> str {
|
||||
; ---- converts the floating point value to a string (no leading spaces)
|
||||
str @shared buffer=" "*20
|
||||
%ir {{
|
||||
load.w r65535,floats.str_f.buffer
|
||||
loadm.f fr65535,floats.str_f.value
|
||||
load.w r65535,floats.tostr.buffer
|
||||
loadm.f fr65535,floats.tostr.value
|
||||
syscall 47 (r65535.w, fr65535.f)
|
||||
load.w r0,floats.str_f.buffer
|
||||
load.w r0,floats.tostr.buffer
|
||||
returnr.w r0
|
||||
}}
|
||||
}
|
||||
|
||||
sub parse_f(str value) -> float {
|
||||
sub parse(str value) -> float {
|
||||
; -- parse a string value of a number to float
|
||||
%ir {{
|
||||
loadm.w r65535,floats.parse_f.value
|
||||
loadm.w r65535,floats.parse.value
|
||||
syscall 45 (r65535.w): fr0.f
|
||||
returnr.f fr0
|
||||
}}
|
||||
@@ -131,16 +131,16 @@ sub ceil(float value) -> float {
|
||||
}}
|
||||
}
|
||||
|
||||
sub rndf() -> float {
|
||||
sub rnd() -> float {
|
||||
%ir {{
|
||||
syscall 35 () : fr0.f
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
sub rndseedf(float seed) {
|
||||
sub rndseed(float seed) {
|
||||
%ir {{
|
||||
loadm.f fr65535,floats.rndseedf.seed
|
||||
loadm.f fr65535,floats.rndseed.seed
|
||||
syscall 32 (fr65535.f)
|
||||
return
|
||||
}}
|
||||
@@ -173,4 +173,21 @@ sub normalize(float value) -> float {
|
||||
return value
|
||||
}
|
||||
|
||||
sub push(float value) {
|
||||
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
|
||||
%ir {{
|
||||
loadm.f fr65535,floats.push.value
|
||||
push.f fr65535
|
||||
return
|
||||
}}
|
||||
}
|
||||
|
||||
sub pop() -> float {
|
||||
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
|
||||
%ir {{
|
||||
pop.f fr65535
|
||||
returnr.f fr65535
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
; such as abs, sqrt, clamp, min, max for example.
|
||||
|
||||
math {
|
||||
%option ignore_unused
|
||||
|
||||
sub sin8u(ubyte angle) -> ubyte {
|
||||
ubyte[256] sintab = [$80, $83, $86, $89, $8c, $8f, $92, $95, $98, $9b, $9e, $a2, $a5, $a7, $aa, $ad, $b0, $b3, $b6, $b9,
|
||||
@@ -292,4 +293,45 @@ math {
|
||||
return w2-w1
|
||||
}
|
||||
|
||||
sub crc16(uword data, uword length) -> uword {
|
||||
; calculates the CRC16 (XMODEM) checksum of the buffer.
|
||||
cx16.r0 = 0 ; the crc value
|
||||
repeat length {
|
||||
cx16.r0H ^= @(data)
|
||||
repeat 8 {
|
||||
if cx16.r0H & $80
|
||||
cx16.r0 = (cx16.r0<<1)^$1021
|
||||
else
|
||||
cx16.r0<<=1
|
||||
}
|
||||
data++
|
||||
}
|
||||
return cx16.r0
|
||||
}
|
||||
|
||||
sub crc32(uword data, uword length) {
|
||||
; Calculates the CRC-32 (POSIX) checksum of the buffer.
|
||||
; because prog8 doesn't have 32 bits integers, we have to split up the calculation over 2 words.
|
||||
; result stored in cx16.r0 (low word) and cx16.r1 (high word)
|
||||
cx16.r1 = 0
|
||||
cx16.r0 = 0
|
||||
repeat length {
|
||||
cx16.r1H ^= @(data)
|
||||
repeat 8 {
|
||||
if cx16.r1H & $80 {
|
||||
cx16.r0 <<= 1
|
||||
rol(cx16.r1)
|
||||
cx16.r1 ^= $04c1
|
||||
cx16.r0 ^= $1db7
|
||||
}
|
||||
else {
|
||||
cx16.r0 <<= 1
|
||||
rol(cx16.r1)
|
||||
}
|
||||
}
|
||||
data++
|
||||
}
|
||||
cx16.r1 ^= $ffff
|
||||
cx16.r0 ^= $ffff
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
monogfx {
|
||||
|
||||
%option no_symbol_prefixing
|
||||
%option ignore_unused
|
||||
|
||||
; read-only control variables:
|
||||
uword width = 0
|
||||
@@ -70,12 +70,9 @@ monogfx {
|
||||
}
|
||||
|
||||
sub horizontal_line(uword xx, uword yy, uword length, bool draw) {
|
||||
ubyte color = 0
|
||||
if draw
|
||||
color = 255
|
||||
uword xpos
|
||||
for xpos in xx to xx+length-1
|
||||
plot(xpos, yy, color)
|
||||
plot(xpos, yy, draw)
|
||||
}
|
||||
|
||||
sub safe_horizontal_line(uword xx, uword yy, uword length, bool draw) {
|
||||
@@ -97,12 +94,9 @@ monogfx {
|
||||
}
|
||||
|
||||
sub vertical_line(uword xx, uword yy, uword lheight, bool draw) {
|
||||
ubyte color = 0
|
||||
if draw
|
||||
color = 255
|
||||
uword ypos
|
||||
for ypos in yy to yy+lheight-1
|
||||
plot(xx, ypos, color)
|
||||
plot(xx, ypos, draw)
|
||||
}
|
||||
|
||||
sub line(uword @zp x1, uword @zp y1, uword @zp x2, uword @zp y2, bool draw) {
|
||||
@@ -460,12 +454,12 @@ skip:
|
||||
; -- select the text charset to use with the text() routine
|
||||
; the charset number is the same as for the cx16.screen_set_charset() ROM function.
|
||||
; 1 = ISO charset, 2 = PETSCII uppercase+graphs, 3= PETSCII uppercase+lowercase.
|
||||
; TODO vm has no bitmap charset
|
||||
; TODO vm bitmap charset
|
||||
}
|
||||
|
||||
sub text(uword @zp xx, uword yy, bool draw, uword sctextptr) {
|
||||
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
|
||||
; You must also have called text_charset() first to select and prepare the character set to use.
|
||||
; TODO vm has no bitmap charset
|
||||
; TODO vm bitmap charset
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
; Internal library routines - always included by the compiler
|
||||
|
||||
prog8_lib {
|
||||
%option force_output
|
||||
%option force_output, ignore_unused
|
||||
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
; 0-terminated string manipulation routines. For the Virtual Machine target.
|
||||
|
||||
string {
|
||||
%option ignore_unused
|
||||
|
||||
sub length(str st) -> ubyte {
|
||||
; Returns the number of bytes in the string.
|
||||
; This value is determined during runtime and counts upto the first terminating 0 byte in the string,
|
||||
@@ -63,6 +65,13 @@ string {
|
||||
return 0
|
||||
}
|
||||
|
||||
sub contains(str st, ubyte character) -> bool {
|
||||
void find(st, character)
|
||||
if_cs
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
sub copy(str source, str target) -> ubyte {
|
||||
; Copy a string to another, overwriting that one.
|
||||
; Returns the length of the string that was copied.
|
||||
@@ -190,4 +199,12 @@ string {
|
||||
sub isletter(ubyte character) -> bool {
|
||||
return islower(character) or isupper(character)
|
||||
}
|
||||
}
|
||||
|
||||
sub isspace(ubyte character) -> bool {
|
||||
return character in [32, 13, 9, 10, 141, 160]
|
||||
}
|
||||
|
||||
sub isprint(ubyte character) -> bool {
|
||||
return character>=32 and character<=127 or character>=160
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
; Prog8 definitions for the Virtual Machine
|
||||
|
||||
%option ignore_unused
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
|
||||
@@ -126,6 +128,62 @@ sys {
|
||||
returnr.b r0
|
||||
}}
|
||||
}
|
||||
|
||||
sub push(ubyte b) {
|
||||
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
|
||||
%ir {{
|
||||
loadm.b r65535,sys.push.b
|
||||
push.b r65535
|
||||
}}
|
||||
}
|
||||
|
||||
sub pushw(uword w) {
|
||||
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
|
||||
%ir {{
|
||||
loadm.w r65535,sys.pushw.w
|
||||
push.w r65535
|
||||
}}
|
||||
}
|
||||
|
||||
sub pop() -> ubyte {
|
||||
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
|
||||
%ir {{
|
||||
pop.b r65535
|
||||
returnr.b r65535
|
||||
}}
|
||||
}
|
||||
|
||||
sub popw() -> uword {
|
||||
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
|
||||
%ir {{
|
||||
pop.w r65535
|
||||
returnr.w r65535
|
||||
}}
|
||||
}
|
||||
|
||||
sub read_flags() -> ubyte {
|
||||
; "simulate" the 6502 status register a little bit
|
||||
if_neg {
|
||||
if_z
|
||||
cx16.r0L = %10000010
|
||||
else
|
||||
cx16.r0L = %10000000
|
||||
}
|
||||
else {
|
||||
if_z
|
||||
cx16.r0L = %00000010
|
||||
else
|
||||
cx16.r0L = %00000000
|
||||
}
|
||||
|
||||
if_cs
|
||||
cx16.r0L |= 1
|
||||
; TODO: overflow flag not yet supported
|
||||
; if_vs
|
||||
; cx16.r0L |= %01000000
|
||||
|
||||
return cx16.r0L
|
||||
}
|
||||
}
|
||||
|
||||
cx16 {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
; Prog8 definitions for the Text I/O console routines for the Virtual Machine
|
||||
|
||||
%import conv
|
||||
%option ignore_unused
|
||||
|
||||
txt {
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDe
|
||||
private fun compileMain(args: Array<String>): Boolean {
|
||||
val cli = ArgParser("prog8compiler", prefixStyle = ArgParser.OptionPrefixStyle.JVM)
|
||||
val asmListfile by cli.option(ArgType.Boolean, fullName = "asmlist", description = "make the assembler produce a listing file as well")
|
||||
val checkSource by cli.option(ArgType.Boolean, fullName = "check", description = "quickly check program for errors, no output will be produced")
|
||||
val symbolDefs by cli.option(ArgType.String, fullName = "D", description = "define assembly symbol(s) with -D SYMBOL=VALUE").multiple()
|
||||
val startEmulator1 by cli.option(ArgType.Boolean, fullName = "emu", description = "auto-start emulator after successful compilation")
|
||||
val startEmulator2 by cli.option(ArgType.Boolean, fullName = "emu2", description = "auto-start alternative emulator after successful compilation")
|
||||
@@ -49,11 +50,16 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||
val includeSourcelines by cli.option(ArgType.Boolean, fullName = "sourcelines", description = "include original Prog8 source lines in generated asm code")
|
||||
val splitWordArrays by cli.option(ArgType.Boolean, fullName = "splitarrays", description = "treat all word arrays as tagged with @split to make them lsb/msb split in memory")
|
||||
val breakpointCpuInstruction by cli.option(ArgType.Boolean, fullName = "breakinstr", description = "also use a CPU instruction for %breakpoint")
|
||||
val printAst1 by cli.option(ArgType.Boolean, fullName = "printast1", description = "print out the compiler AST")
|
||||
val printAst2 by cli.option(ArgType.Boolean, fullName = "printast2", description = "print out the intermediate AST that is used for code generation")
|
||||
val breakpointCpuInstruction by cli.option(ArgType.Choice(listOf("brk", "stp"), { it }), fullName = "breakinstr", description = "the CPU instruction to use as well for %breakpoint")
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${PETTarget.NAME}', '${VMTarget.NAME}') (required)")
|
||||
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
|
||||
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
|
||||
val varsGolden by cli.option(ArgType.Boolean, fullName = "varsgolden", description = "put uninitialized variables in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
|
||||
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
|
||||
val slabsGolden by cli.option(ArgType.Boolean, fullName = "slabsgolden", description = "put memory() slabs in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
|
||||
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
|
||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
||||
|
||||
try {
|
||||
@@ -96,6 +102,32 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
if (varsGolden==true) {
|
||||
if (compilationTarget != Cx16Target.NAME) {
|
||||
System.err.println("Golden Ram is only available on the Commander X16 target.")
|
||||
return false
|
||||
}
|
||||
if (varsHighBank!=null || slabsHighBank!=null) {
|
||||
System.err.println("Either use varsgolden or varshigh (and slabsgolden or slabshigh), not both or mixed.")
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (slabsGolden==true) {
|
||||
if (compilationTarget != Cx16Target.NAME) {
|
||||
System.err.println("Golden Ram is only available on the Commander X16 target.")
|
||||
return false
|
||||
}
|
||||
if (varsHighBank!=null || slabsHighBank!=null) {
|
||||
System.err.println("Either use golden or high ram, not both.")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if(varsHighBank!=null && slabsHighBank!=null && varsHighBank!=slabsHighBank) {
|
||||
System.err.println("Vars and slabs high memory bank must be the same.")
|
||||
return false
|
||||
}
|
||||
|
||||
if(startVm==true) {
|
||||
return runVm(moduleFiles.first())
|
||||
}
|
||||
@@ -113,22 +145,31 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val filepath = pathFrom(filepathRaw).normalize()
|
||||
val compilerArgs = CompilerArguments(
|
||||
filepath,
|
||||
dontOptimize != true,
|
||||
dontWriteAssembly != true,
|
||||
if(checkSource==true) false else dontOptimize != true,
|
||||
if(checkSource==true) false else dontWriteAssembly != true,
|
||||
warnSymbolShadowing == true,
|
||||
quietAssembler == true,
|
||||
asmListfile == true,
|
||||
includeSourcelines == true,
|
||||
experimentalCodegen == true,
|
||||
varsHighBank,
|
||||
varsGolden == true,
|
||||
slabsHighBank,
|
||||
slabsGolden == true,
|
||||
compilationTarget!!,
|
||||
splitWordArrays == true,
|
||||
breakpointCpuInstruction = false,
|
||||
breakpointCpuInstruction,
|
||||
printAst1 == true,
|
||||
printAst2 == true,
|
||||
processedSymbols,
|
||||
srcdirs,
|
||||
outputPath
|
||||
)
|
||||
val compilationResult = compileProgram(compilerArgs)
|
||||
|
||||
if(checkSource==true)
|
||||
println("No output produced.")
|
||||
|
||||
if(compilationResult!=null)
|
||||
results.add(compilationResult)
|
||||
}
|
||||
@@ -182,22 +223,31 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
try {
|
||||
val compilerArgs = CompilerArguments(
|
||||
filepath,
|
||||
dontOptimize != true,
|
||||
dontWriteAssembly != true,
|
||||
if(checkSource==true) false else dontOptimize != true,
|
||||
if(checkSource==true) false else dontWriteAssembly != true,
|
||||
warnSymbolShadowing == true,
|
||||
quietAssembler == true,
|
||||
asmListfile == true,
|
||||
includeSourcelines == true,
|
||||
experimentalCodegen == true,
|
||||
varsHighBank,
|
||||
varsGolden == true,
|
||||
slabsHighBank,
|
||||
slabsGolden == true,
|
||||
compilationTarget!!,
|
||||
splitWordArrays == true,
|
||||
breakpointCpuInstruction == true,
|
||||
breakpointCpuInstruction,
|
||||
printAst1 == true,
|
||||
printAst2 == true,
|
||||
processedSymbols,
|
||||
srcdirs,
|
||||
outputPath
|
||||
)
|
||||
val result = compileProgram(compilerArgs)
|
||||
|
||||
if(checkSource==true)
|
||||
println("No output produced.")
|
||||
|
||||
if(result==null)
|
||||
return false
|
||||
else
|
||||
|
||||
Loaded 100 of 218 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user