mirror of
https://github.com/irmen/prog8.git
synced 2025-06-14 11:23:37 +00:00
Compare commits
182 Commits
Author | SHA1 | Date | |
---|---|---|---|
12abafb917 | |||
8dc2e47507 | |||
0be90dedf2 | |||
daf7c3357c | |||
e6bab3ceeb | |||
59387b2ae8 | |||
e8795859c5 | |||
bebe60b687 | |||
ddceec364e | |||
f8f20440d3 | |||
f8722faa4e | |||
d067fa4b73 | |||
26fbbf48a4 | |||
d5cc414221 | |||
b5e51ab937 | |||
552e55c29f | |||
a228908c1a | |||
15fc3b6c04 | |||
0456badd02 | |||
d28f154f1c | |||
399cf5118d | |||
a87f2640d3 | |||
a90ef274d7 | |||
1c02179c5c | |||
77584493fd | |||
a36709e638 | |||
341778ba67 | |||
8d63cce749 | |||
ec50b5a007 | |||
8e7bbcdbe0 | |||
37ecdc47b3 | |||
112ca3cc53 | |||
33b3a1664c | |||
8a0c02e264 | |||
31d84c8921 | |||
34bedbeef1 | |||
3b1b0985c1 | |||
e40ace9dea | |||
4c0e6e2640 | |||
08b314c37d | |||
86da9d3c7e | |||
4e61e25c02 | |||
5097d52d99 | |||
368387e1a7 | |||
09d2185bb1 | |||
5c02e2bd71 | |||
fb01389b3d | |||
aaa81210ce | |||
51269257ea | |||
23a853db1e | |||
9da430ffeb | |||
cc063124cf | |||
3b37b89951 | |||
844b537d1e | |||
caf1d4a22a | |||
d8e244df99 | |||
548e421e27 | |||
322fa7ea69 | |||
db6c887795 | |||
cf7bea0985 | |||
61fe55168a | |||
25d7f8808f | |||
1c4999ec87 | |||
c726d3f937 | |||
f70341df1b | |||
f0b791452e | |||
adf5600a9b | |||
6d4ccc5feb | |||
5f3829d5cc | |||
770ebdcd4a | |||
96f690e749 | |||
eabdd3a8f3 | |||
50650b966b | |||
65e34d4989 | |||
05dad5ab5f | |||
1a69a2f1bc | |||
435faafaad | |||
686b32dc29 | |||
0e64a22910 | |||
4f0839f27e | |||
bb1953267d | |||
acc630972a | |||
6a33be3fd8 | |||
cd8aae4681 | |||
11456496bd | |||
f5fc4e345c | |||
86eef7039f | |||
f4b2264fcf | |||
9b36ae2277 | |||
913ab03963 | |||
38448e471c | |||
67231af623 | |||
e31ef6f06f | |||
09d188106a | |||
d8e2116481 | |||
435dfbb932 | |||
ba93966474 | |||
ea8d17cdb2 | |||
082265fb25 | |||
d138a7a567 | |||
ea27d732ab | |||
9e557ce8ac | |||
924e28e9b3 | |||
e5d9af75de | |||
31c1bf8bc5 | |||
37d4055036 | |||
78b1076110 | |||
0a3c748e41 | |||
ebf79ef9e2 | |||
60a73248cd | |||
abbb7d7ba3 | |||
59c378089e | |||
0b789b5f0b | |||
4382b96a9a | |||
246e4f35a6 | |||
99b9370178 | |||
506062c6b6 | |||
d634061cd9 | |||
8353c689ca | |||
d59d8ff1fe | |||
e98e6f70ac | |||
53e442d509 | |||
134352ed7c | |||
f7cbfdff06 | |||
b28ee0819f | |||
5de626aab8 | |||
7aad5d486e | |||
701f155951 | |||
8c324d7514 | |||
522958e0e9 | |||
97390db5f5 | |||
af920d1427 | |||
779ebc0537 | |||
38949b82c3 | |||
d11386ef26 | |||
0e0377d1f0 | |||
55e0dbab27 | |||
4dc82f2c83 | |||
1ba5587404 | |||
835c4b6da3 | |||
dbd955b61e | |||
d20e2fd88c | |||
e0dea89477 | |||
ccc6b56e35 | |||
6fc2902895 | |||
c96e4b40d4 | |||
37da3e2170 | |||
2661d3c489 | |||
b89bbb9281 | |||
696bf636ed | |||
40952a788a | |||
0162e7a0c1 | |||
6ce099f176 | |||
476a4bac8e | |||
63a410a6df | |||
cca27faa3b | |||
803e6bd81a | |||
88269628a2 | |||
b920d553a0 | |||
5e2d0d0dfc | |||
2ae3bd68eb | |||
9c183f27eb | |||
8046023e82 | |||
e328520588 | |||
7eb079050c | |||
2fdd5543b2 | |||
d04164c0a6 | |||
b047731f82 | |||
4d91f92a2e | |||
98505d27b1 | |||
cd63a58ad9 | |||
170f8dd092 | |||
619dcb6a84 | |||
99ae8ea52e | |||
dc031c30eb | |||
1e702439b7 | |||
8debc42381 | |||
532d719089 | |||
b40860aca4 | |||
2cbe6b5f7f | |||
d2cc7ccdfa | |||
2cb183c6d8 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -7,10 +7,13 @@ build/
|
||||
dist/
|
||||
output/
|
||||
out/
|
||||
out-new/
|
||||
out-old/
|
||||
.*cache/
|
||||
*.directory
|
||||
*.prg
|
||||
*.bin
|
||||
*.p8ir
|
||||
*.labels.txt
|
||||
*.vm.txt
|
||||
*.vice-mon-list
|
||||
|
1276
.idea/inspectionProfiles/Project_Default.xml
generated
1276
.idea/inspectionProfiles/Project_Default.xml
generated
File diff suppressed because it is too large
Load Diff
20
.idea/libraries/KotlinJavaRuntime.xml
generated
20
.idea/libraries/KotlinJavaRuntime.xml
generated
@ -1,23 +1,23 @@
|
||||
<component name="libraryTable">
|
||||
<library name="KotlinJavaRuntime" type="repository">
|
||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.20" />
|
||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.21" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.21/kotlin-stdlib-jdk8-2.1.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.21/kotlin-stdlib-2.1.21.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/2.1.20/kotlin-stdlib-jdk7-2.1.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.21/kotlin-stdlib-jdk7-2.1.21.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.21/kotlin-stdlib-jdk8-2.1.21-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.21/kotlin-stdlib-2.1.21-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/2.1.20/kotlin-stdlib-jdk7-2.1.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.21/kotlin-stdlib-jdk7-2.1.21-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.21/kotlin-stdlib-jdk8-2.1.21-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.21/kotlin-stdlib-2.1.21-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/2.1.20/kotlin-stdlib-jdk7-2.1.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.21/kotlin-stdlib-jdk7-2.1.21-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@ -12,6 +12,7 @@
|
||||
<option name="pkg" value="" />
|
||||
<option name="language" value="Java" />
|
||||
<option name="generateListener" value="false" />
|
||||
<option name="generateVisitor" value="true" />
|
||||
</PerGrammarGenerationSettings>
|
||||
</list>
|
||||
</option>
|
||||
|
@ -71,6 +71,7 @@ What does Prog8 provide?
|
||||
- high-level program optimizations
|
||||
- conditional branches that map 1:1 to cpu status flags
|
||||
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
|
||||
- ``on .. goto`` statement for fast jump tables
|
||||
- ``in`` expression for concise and efficient multi-value/containment check
|
||||
- ``defer`` statement to help write concise and robust subroutine cleanup logic
|
||||
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
|
||||
|
@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
|
||||
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "2.1.20"
|
||||
kotlin("jvm") version "2.1.21"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
@ -101,7 +101,7 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"abs__word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)),
|
||||
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
|
||||
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
|
||||
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *BaseDataType.entries.toTypedArray())),
|
||||
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *(BaseDataType.entries - BaseDataType.STRUCT_INSTANCE).toTypedArray())),
|
||||
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
|
||||
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
|
||||
|
@ -13,6 +13,9 @@ enum class BaseDataType {
|
||||
STR, // pass by reference
|
||||
ARRAY, // pass by reference, subtype is the element type
|
||||
ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types)
|
||||
POINTER, // typed pointer, subtype is whatever type is pointed to
|
||||
STRUCT_INSTANCE, // the actual instance of a struct (not directly supported in the language yet, but we need its type)
|
||||
ARRAY_POINTER, // array of pointers (uwords), subtype is whatever type each element points to
|
||||
UNDEFINED;
|
||||
|
||||
|
||||
@ -26,6 +29,7 @@ enum class BaseDataType {
|
||||
this.isArray && other.isArray -> false
|
||||
this.isArray -> other != FLOAT
|
||||
this == STR -> other != FLOAT
|
||||
this.isPointer -> other.isByteOrBool
|
||||
else -> true
|
||||
}
|
||||
|
||||
@ -34,7 +38,8 @@ enum class BaseDataType {
|
||||
this == other -> true
|
||||
this.isArray && other.isArray -> true
|
||||
this.isByteOrBool -> other.isByteOrBool
|
||||
this.isWord -> other.isWord
|
||||
this.isWord -> other.isWord || other.isPointer
|
||||
this.isPointer -> other.isWord
|
||||
this == STR && other== UWORD || this== UWORD && other== STR -> true
|
||||
this == STR && other.isArray -> true
|
||||
this.isArray && other == STR -> true
|
||||
@ -50,84 +55,163 @@ val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, Bas
|
||||
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
|
||||
val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric
|
||||
val BaseDataType.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW
|
||||
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW
|
||||
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
|
||||
val BaseDataType.isPassByRef get() = this.isIterable
|
||||
val BaseDataType.isPassByValue get() = !this.isIterable
|
||||
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER
|
||||
val BaseDataType.isPointer get() = this == BaseDataType.POINTER
|
||||
val BaseDataType.isStructInstance get() = this == BaseDataType.STRUCT_INSTANCE
|
||||
val BaseDataType.isPointerArray get() = this == BaseDataType.ARRAY_POINTER
|
||||
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER // pointer arrays are also always stored as split uwords
|
||||
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW, BaseDataType.ARRAY_POINTER)
|
||||
val BaseDataType.isPassByRef get() = this.isIterable && !this.isPointer
|
||||
val BaseDataType.isPassByValue get() = !this.isIterable || this.isPointer
|
||||
|
||||
|
||||
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?) {
|
||||
interface ISubType {
|
||||
val scopedNameString: String
|
||||
fun memsize(sizer: IMemSizer): Int
|
||||
}
|
||||
|
||||
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?, var subType: ISubType?, var subTypeFromAntlr: List<String>?=null) {
|
||||
|
||||
init {
|
||||
if(base.isArray) {
|
||||
require(sub != null)
|
||||
if(base.isSplitWordArray)
|
||||
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
when {
|
||||
base.isPointerArray -> {
|
||||
require(sub!=null || subType!=null || subTypeFromAntlr!=null)
|
||||
}
|
||||
base.isArray -> {
|
||||
require(sub != null && subType==null && subTypeFromAntlr==null)
|
||||
if(base.isSplitWordArray)
|
||||
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
}
|
||||
base==BaseDataType.STR -> require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
|
||||
base!=BaseDataType.POINTER -> require(sub == null) { "only string, array and pointer base types can have a subtype"}
|
||||
else -> {
|
||||
require(sub == null || (subType == null && subTypeFromAntlr == null)) {
|
||||
"sub and subtype can't both be set"
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(base==BaseDataType.STR)
|
||||
require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
|
||||
else
|
||||
require(sub == null) { "only string and array base types can have a subtype"}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is DataType) return false
|
||||
return base == other.base && sub == other.sub
|
||||
return base == other.base && sub == other.sub && subType == other.subType
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(base, sub)
|
||||
override fun hashCode(): Int = Objects.hash(base, sub, subType)
|
||||
|
||||
fun setActualSubType(actualSubType: ISubType) {
|
||||
subType = actualSubType
|
||||
subTypeFromAntlr = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val UBYTE = DataType(BaseDataType.UBYTE, null)
|
||||
val BYTE = DataType(BaseDataType.BYTE, null)
|
||||
val UWORD = DataType(BaseDataType.UWORD, null)
|
||||
val WORD = DataType(BaseDataType.WORD, null)
|
||||
val LONG = DataType(BaseDataType.LONG, null)
|
||||
val FLOAT = DataType(BaseDataType.FLOAT, null)
|
||||
val BOOL = DataType(BaseDataType.BOOL, null)
|
||||
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE)
|
||||
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null)
|
||||
val UBYTE = DataType(BaseDataType.UBYTE, null, null)
|
||||
val BYTE = DataType(BaseDataType.BYTE, null, null)
|
||||
val UWORD = DataType(BaseDataType.UWORD, null, null)
|
||||
val WORD = DataType(BaseDataType.WORD, null, null)
|
||||
val LONG = DataType(BaseDataType.LONG, null, null)
|
||||
val FLOAT = DataType(BaseDataType.FLOAT, null, null)
|
||||
val BOOL = DataType(BaseDataType.BOOL, null, null)
|
||||
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE, null)
|
||||
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null, null)
|
||||
|
||||
private val simpletypes = mapOf(
|
||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
|
||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
|
||||
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null),
|
||||
BaseDataType.WORD to DataType(BaseDataType.WORD, null),
|
||||
BaseDataType.LONG to DataType(BaseDataType.LONG, null),
|
||||
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null),
|
||||
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null),
|
||||
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE),
|
||||
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null)
|
||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null, null),
|
||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null, null),
|
||||
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null, null),
|
||||
BaseDataType.WORD to DataType(BaseDataType.WORD, null, null),
|
||||
BaseDataType.LONG to DataType(BaseDataType.LONG, null, null),
|
||||
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null, null),
|
||||
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null, null),
|
||||
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE, null),
|
||||
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null, null)
|
||||
)
|
||||
|
||||
fun forDt(dt: BaseDataType) = simpletypes.getValue(dt)
|
||||
fun forDt(dt: BaseDataType): DataType {
|
||||
if(dt.isStructInstance)
|
||||
TODO("cannot use struct instance as a data type (yet) - use a pointer instead")
|
||||
return simpletypes.getValue(dt)
|
||||
}
|
||||
|
||||
fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
|
||||
require(!elementDt.isPointer) { "use other array constructor for arrays of pointers" }
|
||||
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
|
||||
return if(splitwordarray && actualElementDt.isWord)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt, null)
|
||||
else {
|
||||
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt, null)
|
||||
else
|
||||
throw NoSuchElementException("invalid element dt $elementDt")
|
||||
throw NoSuchElementException("invalid basic element dt $elementDt")
|
||||
}
|
||||
}
|
||||
|
||||
fun arrayOfPointersTo(sub: BaseDataType): DataType = DataType(BaseDataType.ARRAY_POINTER, sub, null)
|
||||
fun arrayOfPointersTo(structType: ISubType?): DataType = DataType(BaseDataType.ARRAY_POINTER, null, structType)
|
||||
fun arrayOfPointersFromAntlrTo(sub: BaseDataType?, identifier: List<String>?): DataType =
|
||||
DataType(BaseDataType.ARRAY_POINTER, sub, null, identifier)
|
||||
|
||||
fun pointer(base: BaseDataType): DataType = DataType(BaseDataType.POINTER, base, null)
|
||||
fun pointer(dt: DataType): DataType {
|
||||
if(dt.isBasic)
|
||||
return DataType(BaseDataType.POINTER, dt.base, null)
|
||||
else
|
||||
return DataType(BaseDataType.POINTER, null, dt.subType, dt.subTypeFromAntlr)
|
||||
}
|
||||
fun pointer(structType: ISubType): DataType = DataType(BaseDataType.POINTER, null, structType, null)
|
||||
fun pointerFromAntlr(identifier: List<String>): DataType = DataType(BaseDataType.POINTER, null, null, identifier)
|
||||
fun structInstance(type: ISubType?): DataType = DataType(BaseDataType.STRUCT_INSTANCE, sub=null, type)
|
||||
fun structInstanceFromAntlr(struct: List<String>): DataType = DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr = struct)
|
||||
}
|
||||
|
||||
|
||||
fun elementToArray(splitwords: Boolean = true): DataType {
|
||||
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords)
|
||||
else arrayFor(base, false)
|
||||
}
|
||||
|
||||
fun elementType(): DataType =
|
||||
if(base.isArray || base==BaseDataType.STR)
|
||||
forDt(sub!!)
|
||||
else
|
||||
throw IllegalArgumentException("not an array")
|
||||
when {
|
||||
isPointerArray -> DataType(BaseDataType.POINTER, sub, subType)
|
||||
base.isArray || base==BaseDataType.STR -> forDt(sub!!)
|
||||
else -> throw IllegalArgumentException("not an array")
|
||||
}
|
||||
|
||||
fun typeForAddressOf(msb: Boolean): DataType {
|
||||
if (isUndefined)
|
||||
return if(msb) pointer(BaseDataType.UBYTE) else UWORD
|
||||
else {
|
||||
if (isBasic)
|
||||
return pointer(base)
|
||||
if (isString)
|
||||
return pointer(BaseDataType.UBYTE)
|
||||
if (isPointer)
|
||||
return UWORD
|
||||
if (isArray) {
|
||||
if (msb || isSplitWordArray)
|
||||
return pointer(BaseDataType.UBYTE)
|
||||
val elementDt = elementType()
|
||||
require(elementDt.isBasic)
|
||||
return pointer(elementDt)
|
||||
}
|
||||
if (subType != null)
|
||||
return pointer(this)
|
||||
return UWORD
|
||||
}
|
||||
}
|
||||
|
||||
fun dereference(): DataType {
|
||||
require(isPointer || isUnsignedWord)
|
||||
return when {
|
||||
isUnsignedWord -> forDt(BaseDataType.UBYTE)
|
||||
sub!=null -> forDt(sub)
|
||||
subType!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, subType)
|
||||
subTypeFromAntlr!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr)
|
||||
else -> throw IllegalArgumentException("cannot dereference this pointer type")
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = when(base) {
|
||||
BaseDataType.ARRAY -> {
|
||||
@ -148,6 +232,15 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.POINTER -> {
|
||||
if(sub!=null) "^^${sub.name.lowercase()}" else if(subType!=null) "^^${subType!!.scopedNameString}" else "^^${subTypeFromAntlr}"
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
if(sub!=null) "^^${sub.name.lowercase()}[] (split)" else if (subType!=null) "^^${subType!!.scopedNameString}[] (split)" else "^^${subTypeFromAntlr}[] (split)"
|
||||
}
|
||||
BaseDataType.STRUCT_INSTANCE -> {
|
||||
sub?.name?.lowercase() ?: if (subType!=null) subType!!.scopedNameString else "$subTypeFromAntlr"
|
||||
}
|
||||
else -> base.name.lowercase()
|
||||
}
|
||||
|
||||
@ -160,6 +253,30 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
BaseDataType.LONG -> "long"
|
||||
BaseDataType.FLOAT -> "float"
|
||||
BaseDataType.STR -> "str"
|
||||
BaseDataType.POINTER -> {
|
||||
when {
|
||||
sub!=null -> "^^${sub.name.lowercase()}"
|
||||
subType!=null -> "^^${subType!!.scopedNameString}"
|
||||
subTypeFromAntlr!=null -> "^^${subTypeFromAntlr!!.joinToString(".")}"
|
||||
else -> "?????"
|
||||
}
|
||||
}
|
||||
BaseDataType.STRUCT_INSTANCE -> {
|
||||
when {
|
||||
sub!=null -> sub.name.lowercase()
|
||||
subType!=null -> subType!!.scopedNameString
|
||||
subTypeFromAntlr!=null -> subTypeFromAntlr!!.joinToString(".")
|
||||
else -> "?????"
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
when {
|
||||
sub!=null -> "^^${sub.name.lowercase()}["
|
||||
subType!=null -> "^^${subType!!.scopedNameString}["
|
||||
subTypeFromAntlr!=null -> "^^${subTypeFromAntlr!!.joinToString(".")}["
|
||||
else -> "????? ["
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY -> {
|
||||
when(sub) {
|
||||
BaseDataType.UBYTE -> "ubyte["
|
||||
@ -187,18 +304,36 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL
|
||||
BaseDataType.UBYTE -> targetType.base in arrayOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.BYTE -> targetType.base in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT, BaseDataType.POINTER, BaseDataType.ARRAY_POINTER)
|
||||
BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
|
||||
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD)
|
||||
BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub
|
||||
BaseDataType.POINTER -> {
|
||||
when {
|
||||
targetType.base == BaseDataType.UWORD || targetType.base == BaseDataType.LONG -> true
|
||||
targetType.isPointer -> this.isUnsignedWord || this==targetType
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
BaseDataType.STRUCT_INSTANCE -> false // we cannot deal with actual struct instances yet in any shape or form (only getting fields from it)
|
||||
BaseDataType.ARRAY_POINTER -> TODO("check assignability of array of pointers")
|
||||
BaseDataType.UNDEFINED -> false
|
||||
}
|
||||
|
||||
fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base)
|
||||
fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base)
|
||||
|
||||
fun size(memsizer: IMemSizer): Int = if(sub!=null) {
|
||||
memsizer.memorySize(sub)
|
||||
} else if(subType!=null) {
|
||||
subType!!.memsize(memsizer)
|
||||
} else {
|
||||
memsizer.memorySize(base)
|
||||
}
|
||||
|
||||
val isBasic = sub==null && subType==null && subTypeFromAntlr==null
|
||||
val isUndefined = base == BaseDataType.UNDEFINED
|
||||
val isByte = base.isByte
|
||||
val isUnsignedByte = base == BaseDataType.UBYTE
|
||||
@ -214,6 +349,9 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
val isSigned = base.isSigned
|
||||
val isUnsigned = !base.isSigned
|
||||
val isArray = base.isArray
|
||||
val isPointer = base.isPointer
|
||||
val isStructInstance = base.isStructInstance
|
||||
val isPointerArray = base.isPointerArray
|
||||
val isBoolArray = base.isArray && sub == BaseDataType.BOOL
|
||||
val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
|
||||
val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE
|
||||
@ -280,7 +418,7 @@ enum class RegisterOrPair {
|
||||
BaseDataType.BYTE -> "sL"
|
||||
BaseDataType.WORD -> "s"
|
||||
BaseDataType.UWORD, null -> ""
|
||||
else -> throw kotlin.IllegalArgumentException("invalid register param type")
|
||||
else -> throw IllegalArgumentException("invalid register param type")
|
||||
}
|
||||
return listOf("cx16", name.lowercase()+suffix)
|
||||
}
|
||||
|
@ -14,5 +14,5 @@ interface IErrorReporter {
|
||||
|
||||
fun noErrorForLine(position: Position): Boolean
|
||||
|
||||
fun print_single_error(errormessage: String)
|
||||
fun printSingleError(errormessage: String)
|
||||
}
|
||||
|
@ -6,7 +6,10 @@ import prog8.code.target.zp.C128Zeropage
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
class C128Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
@ -28,10 +31,10 @@ class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xc000u
|
||||
|
||||
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 val BSSHIGHRAM_START = 0u // TODO address?
|
||||
override val BSSHIGHRAM_END = 0u // TODO address?
|
||||
override val BSSGOLDENRAM_START = 0u // TODO address?
|
||||
override val BSSGOLDENRAM_END = 0u // TODO address?
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
@ -7,7 +7,10 @@ import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
class C64Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
|
@ -37,7 +37,7 @@ class ConfigFileTarget(
|
||||
val zpFullsafe: List<UIntRange>,
|
||||
val zpKernalsafe: List<UIntRange>,
|
||||
val zpBasicsafe: List<UIntRange>
|
||||
): ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(8) {
|
||||
): ICompilationTarget, IStringEncoding by Encoder(true), IMemSizer by NormalMemSizer(8) {
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -6,7 +6,10 @@ import prog8.code.target.zp.CX16Zeropage
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
class Cx16Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
|
@ -7,7 +7,9 @@ import prog8.code.core.IMemSizer
|
||||
internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!! // array of pointers is just array of uwords
|
||||
else if(dt.isArray) {
|
||||
if(numElements==null) return 2 // treat it as a pointer size
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
@ -25,7 +27,9 @@ internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> floatsize * (numElements ?: 1)
|
||||
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
||||
dt.isLong -> 4 * (numElements ?: 1)
|
||||
dt.isPointer -> 2 // pointer is just a uword
|
||||
dt.isStructInstance -> dt.subType!!.memsize(this)
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
|
@ -6,7 +6,10 @@ import prog8.code.target.zp.PETZeropage
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
class PETTarget: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
|
@ -7,7 +7,10 @@ import kotlin.io.path.isReadable
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
|
||||
class VMTarget: ICompilationTarget,
|
||||
IStringEncoding by Encoder(false),
|
||||
IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.ISO
|
||||
override val libraryPath = null
|
||||
@ -88,31 +91,6 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
zeropage = VirtualZeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||
}
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
if(numElements==null) return 2 // treat it as a pointer size
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
||||
BaseDataType.FLOAT-> numElements * FLOAT_MEM_SIZE
|
||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
else if (dt.isString) {
|
||||
return numElements // treat it as the size of the given string with the length
|
||||
?: 2 // treat it as the size to store a string pointer
|
||||
}
|
||||
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> FLOAT_MEM_SIZE * (numElements ?: 1)
|
||||
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -197,6 +197,7 @@ object AtasciiEncoding {
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\r' -> 0x9bu
|
||||
'\u0000' -> 0u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
|
@ -285,6 +285,7 @@ object C64osEncoding {
|
||||
val screencode = encodingC64os[chr]
|
||||
return screencode?.toUByte() ?: when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> 13u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
|
@ -5,19 +5,19 @@ import prog8.code.core.Encoding
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.core.InternalCompilerException
|
||||
|
||||
object Encoder: IStringEncoding {
|
||||
class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding {
|
||||
override val defaultEncoding: Encoding = Encoding.ISO
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||
val coded = when(encoding) {
|
||||
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
|
||||
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
|
||||
Encoding.ISO -> IsoEncoding.encode(str)
|
||||
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.encode(str)
|
||||
Encoding.ISO -> IsoEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.CP437 -> Cp437Encoding.encode(str)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.encode(str)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
||||
Encoding.C64OS -> C64osEncoding.encode(str)
|
||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||
}
|
||||
@ -30,12 +30,12 @@ object Encoder: IStringEncoding {
|
||||
val decoded = when(encoding) {
|
||||
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
|
||||
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
|
||||
Encoding.ISO -> IsoEncoding.decode(bytes)
|
||||
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes)
|
||||
Encoding.ISO -> IsoEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.CP437 -> Cp437Encoding.decode(bytes)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
||||
Encoding.C64OS -> C64osEncoding.decode(bytes)
|
||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||
}
|
||||
|
@ -6,14 +6,16 @@ import com.github.michaelbull.result.Result
|
||||
import java.io.CharConversionException
|
||||
import java.nio.charset.Charset
|
||||
|
||||
|
||||
open class IsoEncodingBase(charsetName: String) {
|
||||
val charset: Charset = Charset.forName(charsetName)
|
||||
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
|
||||
return try {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
@ -27,9 +29,14 @@ open class IsoEncodingBase(charsetName: String) {
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||
fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||
Ok(String(bytes.map {
|
||||
when(it) {
|
||||
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
|
||||
else -> it.toByte()
|
||||
}
|
||||
}.toByteArray(), charset))
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
|
@ -64,10 +64,11 @@ object JapaneseCharacterConverter {
|
||||
object KatakanaEncoding {
|
||||
val charset: Charset = Charset.forName("JIS_X0201")
|
||||
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
|
||||
return try {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||
|
||||
'\u0000' -> 0u
|
||||
'\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
|
||||
@ -112,9 +113,14 @@ object KatakanaEncoding {
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||
fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||
Ok(String(bytes.map {
|
||||
when(it) {
|
||||
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
|
||||
else -> it.toByte()
|
||||
}
|
||||
}.toByteArray(), charset))
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ object PetsciiEncoding {
|
||||
'\ufffe', // 0x07 -> UNDEFINED
|
||||
'\uf118', // 0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
|
||||
'\uf119', // 0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
|
||||
'\ufffe', // 0x0A -> UNDEFINED
|
||||
'\n', // 0x0A -> LINE FEED (RETURN)
|
||||
'\ufffe', // 0x0B -> UNDEFINED
|
||||
'\ufffe', // 0x0C -> UNDEFINED
|
||||
'\n' , // 0x0D -> LINE FEED (RETURN)
|
||||
@ -1117,6 +1117,8 @@ object PetsciiEncoding {
|
||||
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
||||
return screencode?.toUByte() ?: when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> 141u
|
||||
'\r' -> 141u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
|
@ -16,12 +16,6 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("C128 target doesn't yet support floating point routines")
|
||||
// note: in git commit labeled 'c128: remove floats module' the floats.p8 and floats.asm files are removed,
|
||||
// they could be retrieved again at a later time if the compiler somehow *does* store the fp variables in bank1.
|
||||
}
|
||||
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
|
@ -19,7 +19,7 @@ class ConfigurableZeropage(
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
TODO("floats")
|
||||
TODO("floats in configurable target zp")
|
||||
}
|
||||
|
||||
if(SCRATCH_REG!=SCRATCH_B1+1u)
|
||||
@ -30,7 +30,7 @@ class ConfigurableZeropage(
|
||||
ZeropageType.FULL -> fullsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.BASICSAFE -> basicsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.KERNALSAFE -> kernalsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.FLOATSAFE -> TODO("floatsafe")
|
||||
ZeropageType.FLOATSAFE -> TODO("floatsafe in configurable target zp")
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
|
@ -16,10 +16,6 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("PET target doesn't yet support floating point routines")
|
||||
}
|
||||
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
|
@ -37,7 +37,10 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
||||
is PtBlock -> node.name = "p8b_${node.name}"
|
||||
is PtLabel -> if(!node.name.startsWith(GENERATED_LABEL_PREFIX)) node.name = "p8l_${node.name}" // don't prefix autogenerated labels
|
||||
is PtConstant -> node.name = "p8c_${node.name}"
|
||||
is PtVariable, is PtMemMapped, is PtSubroutineParameter -> node.name = "p8v_${node.name}"
|
||||
is PtVariable, is PtMemMapped, is PtSubroutineParameter -> {
|
||||
node.name = "p8v_${node.name}"
|
||||
}
|
||||
is PtStructDecl -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,10 +53,7 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
||||
node.address!!.varbank = node.address!!.varbank!!.prefix(node, st)
|
||||
}
|
||||
}
|
||||
is PtSub -> {
|
||||
prefixNamedNode(node)
|
||||
node.parameters.forEach { prefixNamedNode(it) }
|
||||
}
|
||||
is PtSub -> prefixNamedNode(node)
|
||||
is PtFunctionCall -> {
|
||||
val stNode = st.lookup(node.name)!!
|
||||
if(stNode.astNode!!.definingBlock()?.options?.noSymbolPrefixing!=true) {
|
||||
@ -175,10 +175,10 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
|
||||
if(elt.definingBlock()?.options?.noSymbolPrefixing==true)
|
||||
newValue.add(elt)
|
||||
else {
|
||||
val newAddr = PtAddressOf(elt.position)
|
||||
newAddr.children.add(elt.identifier.prefix(newAddr, st))
|
||||
if(elt.arrayIndexExpr!=null)
|
||||
newAddr.children.add(elt.arrayIndexExpr!!)
|
||||
val newAddr = PtAddressOf(elt.type, elt.position)
|
||||
newAddr.add(elt.identifier!!.prefix(newAddr, st))
|
||||
if (elt.arrayIndexExpr != null)
|
||||
newAddr.add(elt.arrayIndexExpr!!)
|
||||
newAddr.parent = arrayValue
|
||||
newValue.add(newAddr)
|
||||
}
|
||||
@ -186,7 +186,7 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
|
||||
else -> throw AssemblyError("weird array value element $elt")
|
||||
}
|
||||
}
|
||||
val result = PtVariable(name, type, zeropage, align, newValue, arraySize, position)
|
||||
val result = PtVariable(name, type, zeropage, align, dirty, newValue, arraySize, position)
|
||||
result.parent = parent
|
||||
result
|
||||
}
|
||||
@ -227,6 +227,7 @@ private fun PtIdentifier.prefix(parent: PtNode, st: SymbolTable): PtIdentifier {
|
||||
StNodeType.CONSTANT -> 'c'
|
||||
StNodeType.BUILTINFUNC -> 's'
|
||||
StNodeType.MEMORYSLAB -> 'v'
|
||||
StNodeType.STRUCTINSTANCE -> 'i'
|
||||
else -> '?'
|
||||
}
|
||||
val newName = prefixScopedName(name, prefixType)
|
||||
@ -316,9 +317,9 @@ class AsmGen6502Internal (
|
||||
if(symbolTable.allVariables.isNotEmpty()) {
|
||||
println("Static variables (not in ZeroPage):")
|
||||
symbolTable.allVariables
|
||||
.filterNot { allocator.isZpVar(it.scopedName) }
|
||||
.sortedBy { it.scopedName }.forEach {
|
||||
println(" ${it.dt}\t${it.scopedName}\t")
|
||||
.filterNot { allocator.isZpVar(it.scopedNameString) }
|
||||
.sortedBy { it.scopedNameString }.forEach {
|
||||
println(" ${it.dt}\t${it.scopedNameString}\t")
|
||||
}
|
||||
}
|
||||
if(allocator.globalFloatConsts.isNotEmpty()) {
|
||||
@ -330,9 +331,9 @@ class AsmGen6502Internal (
|
||||
if(symbolTable.allMemMappedVariables.isNotEmpty()) {
|
||||
println("Memory mapped:")
|
||||
symbolTable.allMemMappedVariables
|
||||
.sortedWith( compareBy( {it.address}, {it.scopedName} ))
|
||||
.sortedWith( compareBy( {it.address}, {it.scopedNameString} ))
|
||||
.forEach { mvar ->
|
||||
println(" $${mvar.address.toString(16).padStart(4, '0')}\t${mvar.dt}\t${mvar.scopedName}")
|
||||
println(" $${mvar.address.toString(16).padStart(4, '0')}\t${mvar.dt}\t${mvar.scopedNameString}")
|
||||
}
|
||||
}
|
||||
if(symbolTable.allMemorySlabs.isNotEmpty()) {
|
||||
@ -420,7 +421,7 @@ class AsmGen6502Internal (
|
||||
}
|
||||
|
||||
fun asmVariableName(st: StNode, scope: IPtSubroutine?): String {
|
||||
val name = asmVariableName(st.scopedName)
|
||||
val name = asmVariableName(st.scopedNameString)
|
||||
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)
|
||||
@ -432,23 +433,6 @@ class AsmGen6502Internal (
|
||||
}
|
||||
|
||||
|
||||
internal val tempVarsCounters = mutableMapOf(
|
||||
BaseDataType.BOOL to 0,
|
||||
BaseDataType.BYTE to 0,
|
||||
BaseDataType.UBYTE to 0,
|
||||
BaseDataType.WORD to 0,
|
||||
BaseDataType.UWORD to 0,
|
||||
BaseDataType.LONG to 0,
|
||||
BaseDataType.FLOAT to 0
|
||||
)
|
||||
|
||||
internal fun buildTempVarName(dt: BaseDataType, counter: Int): String = "prog8_tmpvar_${dt.toString().lowercase()}_$counter"
|
||||
|
||||
internal fun getTempVarName(dt: BaseDataType): String {
|
||||
tempVarsCounters[dt] = tempVarsCounters.getValue(dt)+1
|
||||
return buildTempVarName(dt, tempVarsCounters.getValue(dt))
|
||||
}
|
||||
|
||||
internal fun loadByteFromPointerIntoA(pointervar: PtIdentifier): String {
|
||||
// returns the source name of the zero page pointervar if it's already in the ZP,
|
||||
// otherwise returns "P8ZP_SCRATCH_W1" which is the intermediary
|
||||
@ -638,7 +622,7 @@ class AsmGen6502Internal (
|
||||
is PtDefer -> throw AssemblyError("defer should have been transformed")
|
||||
is PtNodeGroup -> stmt.children.forEach { translate(it) }
|
||||
is PtJmpTable -> translate(stmt)
|
||||
is PtNop -> {}
|
||||
is PtNop, is PtStructDecl, is PtSubSignature -> {}
|
||||
else -> throw AssemblyError("missing asm translation for $stmt")
|
||||
}
|
||||
}
|
||||
@ -656,16 +640,16 @@ class AsmGen6502Internal (
|
||||
}
|
||||
|
||||
if(expr.splitWords) {
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register), false)
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register))
|
||||
return
|
||||
}
|
||||
|
||||
when {
|
||||
expr.type.isByteOrBool -> {
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register), false)
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register))
|
||||
}
|
||||
expr.type.isWord -> {
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.A, false)
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.A)
|
||||
out(" asl a")
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
@ -674,8 +658,9 @@ class AsmGen6502Internal (
|
||||
}
|
||||
}
|
||||
expr.type.isFloat -> {
|
||||
require(options.compTarget.FLOAT_MEM_SIZE == 5) {"invalid float size ${expr.position}"}
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.A, false)
|
||||
if(options.compTarget.FLOAT_MEM_SIZE != 5)
|
||||
TODO("support float size other than 5 ${expr.position}")
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.A)
|
||||
out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
@ -750,7 +735,7 @@ class AsmGen6502Internal (
|
||||
TargetStorageKind.REGISTER -> {
|
||||
val zero = PtNumber(BaseDataType.UBYTE, 0.0, value.position)
|
||||
zero.parent = value
|
||||
assignExpressionToRegister(zero, target.register!!, false)
|
||||
assignExpressionToRegister(zero, target.register!!)
|
||||
return
|
||||
}
|
||||
else -> { }
|
||||
@ -854,7 +839,7 @@ class AsmGen6502Internal (
|
||||
private fun repeatWordCount(iterations: Int, stmt: PtRepeatLoop) {
|
||||
require(iterations in 257..65536) { "invalid repeat count ${stmt.position}" }
|
||||
val repeatLabel = makeLabel("repeat")
|
||||
val counterVar = createRepeatCounterVar(BaseDataType.UWORD, isTargetCpu(CpuType.CPU65C02), stmt)
|
||||
val counterVar = createTempVarReused(BaseDataType.UWORD, true, stmt)
|
||||
val loopcount = if(iterations==65536) 0 else if(iterations and 0x00ff == 0) iterations else iterations + 0x0100 // so that the loop can simply use a double-dec
|
||||
out("""
|
||||
ldy #>$loopcount
|
||||
@ -874,7 +859,7 @@ $repeatLabel""")
|
||||
// note: A/Y must have been loaded with the number of iterations!
|
||||
// the iny + double dec is microoptimization of the 16 bit loop
|
||||
val repeatLabel = makeLabel("repeat")
|
||||
val counterVar = createRepeatCounterVar(BaseDataType.UWORD, false, stmt)
|
||||
val counterVar = createTempVarReused(BaseDataType.UWORD, true, stmt)
|
||||
out("""
|
||||
cmp #0
|
||||
beq +
|
||||
@ -897,13 +882,13 @@ $repeatLabel""")
|
||||
require(count in 2..256) { "invalid repeat count ${stmt.position}" }
|
||||
val repeatLabel = makeLabel("repeat")
|
||||
if(isTargetCpu(CpuType.CPU65C02)) {
|
||||
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, true, stmt)
|
||||
val counterVar = createTempVarReused(BaseDataType.UBYTE, true, stmt)
|
||||
out(" lda #${count and 255} | sta $counterVar")
|
||||
out(repeatLabel)
|
||||
translate(stmt.statements)
|
||||
out(" dec $counterVar | bne $repeatLabel")
|
||||
} else {
|
||||
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, false, stmt)
|
||||
val counterVar = createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
out(" lda #${count and 255} | sta $counterVar")
|
||||
out(repeatLabel)
|
||||
translate(stmt.statements)
|
||||
@ -915,13 +900,13 @@ $repeatLabel""")
|
||||
val repeatLabel = makeLabel("repeat")
|
||||
out(" cpy #0")
|
||||
if(isTargetCpu(CpuType.CPU65C02)) {
|
||||
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, true, stmt)
|
||||
val counterVar = createTempVarReused(BaseDataType.UBYTE, true, stmt)
|
||||
out(" beq $endLabel | sty $counterVar")
|
||||
out(repeatLabel)
|
||||
translate(stmt.statements)
|
||||
out(" dec $counterVar | bne $repeatLabel")
|
||||
} else {
|
||||
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, false, stmt)
|
||||
val counterVar = createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
out(" beq $endLabel | sty $counterVar")
|
||||
out(repeatLabel)
|
||||
translate(stmt.statements)
|
||||
@ -930,43 +915,9 @@ $repeatLabel""")
|
||||
out(endLabel)
|
||||
}
|
||||
|
||||
private fun createRepeatCounterVar(dt: BaseDataType, preferZeropage: Boolean, stmt: PtRepeatLoop): String {
|
||||
val scope = stmt.definingISub()!!
|
||||
val asmInfo = subroutineExtra(scope)
|
||||
var parent = stmt.parent
|
||||
while(parent !is PtProgram) {
|
||||
if(parent is PtRepeatLoop)
|
||||
break
|
||||
parent = parent.parent
|
||||
}
|
||||
val isNested = parent is PtRepeatLoop
|
||||
|
||||
if(!isNested) {
|
||||
// we can re-use a counter var from the subroutine if it already has one for that datatype
|
||||
val existingVar = asmInfo.extraVars.firstOrNull { it.first==dt && it.second.endsWith("counter") }
|
||||
if(existingVar!=null) {
|
||||
if(!preferZeropage || existingVar.third!=null)
|
||||
return existingVar.second
|
||||
}
|
||||
}
|
||||
|
||||
val counterVar = makeLabel("counter")
|
||||
when(dt) {
|
||||
BaseDataType.UBYTE, BaseDataType.UWORD -> {
|
||||
val result = zeropage.allocate(counterVar, DataType.forDt(dt), null, stmt.position, errors)
|
||||
result.fold(
|
||||
success = { (address, _, _) -> asmInfo.extraVars.add(Triple(dt, counterVar, address)) },
|
||||
failure = { asmInfo.extraVars.add(Triple(dt, counterVar, null)) } // allocate normally
|
||||
)
|
||||
return counterVar
|
||||
}
|
||||
else -> throw AssemblyError("invalidt dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(stmt: PtWhen) {
|
||||
val endLabel = makeLabel("when_end")
|
||||
val choiceBlocks = mutableListOf<Pair<String, PtNodeGroup>>()
|
||||
val choiceBlocks = mutableListOf<Pair<String, PtWhenChoice>>()
|
||||
val conditionDt = stmt.value.type
|
||||
if(conditionDt.isByte)
|
||||
assignExpressionToRegister(stmt.value, RegisterOrPair.A)
|
||||
@ -977,13 +928,20 @@ $repeatLabel""")
|
||||
val choice = choiceNode as PtWhenChoice
|
||||
var choiceLabel = makeLabel("choice")
|
||||
if(choice.isElse) {
|
||||
require(choice.parent.children.last() === choice)
|
||||
translate(choice.statements)
|
||||
// is always the last node so can fall through
|
||||
} else {
|
||||
if(choice.statements.children.isEmpty()) {
|
||||
// no statements for this choice value, jump to the end immediately
|
||||
choiceLabel = endLabel
|
||||
} else {
|
||||
choiceBlocks.add(choiceLabel to choice.statements)
|
||||
val onlyJumpLabel = ((choice.statements.children.singleOrNull() as? PtJump)?.target as? PtIdentifier)?.name
|
||||
if(onlyJumpLabel==null) {
|
||||
choiceBlocks.add(choiceLabel to choice)
|
||||
} else {
|
||||
choiceLabel = onlyJumpLabel
|
||||
}
|
||||
}
|
||||
for (cv in choice.values.children) {
|
||||
val value = (cv as PtNumber).number.toInt()
|
||||
@ -1000,11 +958,14 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
}
|
||||
jmp(endLabel)
|
||||
|
||||
if(choiceBlocks.isNotEmpty())
|
||||
jmp(endLabel)
|
||||
|
||||
for(choiceBlock in choiceBlocks.withIndex()) {
|
||||
out(choiceBlock.value.first)
|
||||
translate(choiceBlock.value.second)
|
||||
if (choiceBlock.index < choiceBlocks.size - 1)
|
||||
translate(choiceBlock.value.second.statements)
|
||||
if (choiceBlock.index < choiceBlocks.size - 1 && !choiceBlock.value.second.isOnlyGotoOrReturn())
|
||||
jmp(endLabel)
|
||||
}
|
||||
out(endLabel)
|
||||
@ -1033,6 +994,7 @@ $repeatLabel""")
|
||||
val target = getJumpTarget(jump)
|
||||
require(!target.needsExpressionEvaluation)
|
||||
if(target.indirect) {
|
||||
require(!target.indexedX)
|
||||
val complementedInstruction = branchInstruction(stmt.condition, true)
|
||||
out("""
|
||||
$complementedInstruction +
|
||||
@ -1084,12 +1046,29 @@ $repeatLabel""")
|
||||
else {
|
||||
if(evaluateAddressExpression) {
|
||||
val arrayIdx = jump.target as? PtArrayIndexer
|
||||
if(arrayIdx!=null && !arrayIdx.splitWords) {
|
||||
// if the jump target is an address in a non-split array (like a jump table of only pointers),
|
||||
// more optimal assembly can be generated using JMP address,X
|
||||
assignExpressionToRegister(arrayIdx.index, RegisterOrPair.A)
|
||||
out(" asl a | tax")
|
||||
return JumpTarget(asmSymbolName(arrayIdx.variable), true, true, false)
|
||||
if (arrayIdx!=null) {
|
||||
val arrayVariable = arrayIdx.variable
|
||||
if(arrayVariable==null)
|
||||
TODO("support for ptr indexing ${arrayIdx.position}")
|
||||
|
||||
if (isTargetCpu(CpuType.CPU65C02)) {
|
||||
if (!arrayIdx.splitWords) {
|
||||
// if the jump target is an address in a non-split array (like a jump table of only pointers),
|
||||
// on the 65c02, more optimal assembly can be generated using JMP (address,X)
|
||||
assignExpressionToRegister(arrayIdx.index, RegisterOrPair.A)
|
||||
out(" asl a | tax")
|
||||
return JumpTarget(asmSymbolName(arrayVariable), true, true, false)
|
||||
} else {
|
||||
// print a message when more optimal code is possible for 65C02 cpu
|
||||
val variable = symbolTable.lookup(arrayVariable.name)!!
|
||||
if(variable is StStaticVariable && variable.length!!<=128u)
|
||||
errors.info("the jump address array is @split, but @nosplit would create more efficient code here", jump.position)
|
||||
}
|
||||
} else {
|
||||
// print a message when more optimal code is possible for 6502 cpu
|
||||
if(!arrayIdx.splitWords)
|
||||
errors.info("the jump address array is @nosplit, but @split would create more efficient code here", jump.position)
|
||||
}
|
||||
}
|
||||
// we can do the address evaluation right now and just use a temporary pointer variable
|
||||
assignExpressionToVariable(jump.target, "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
@ -1101,17 +1080,18 @@ $repeatLabel""")
|
||||
}
|
||||
|
||||
private fun translate(ret: PtReturn) {
|
||||
val returnvalue = ret.children.singleOrNull()
|
||||
val returnvalue = ret.children.singleOrNull() as? PtExpression
|
||||
val sub = ret.definingSub()!!
|
||||
val returnRegs = sub.returnsWhatWhere()
|
||||
|
||||
if(returnvalue!=null) {
|
||||
if (sub.returns.single().isNumericOrBool) {
|
||||
assignExpressionToRegister(returnvalue as PtExpression, returnRegs.single().first.registerOrPair!!)
|
||||
if (sub.signature.returns.single().isNumericOrBool) {
|
||||
assignExpressionToRegister(returnvalue, returnRegs.single().first.registerOrPair!!)
|
||||
}
|
||||
else {
|
||||
// all else take its address and assign that also to AY register pair
|
||||
val addrofValue = PtAddressOf(returnvalue.position)
|
||||
val addrOfDt = returnvalue.type.typeForAddressOf(false)
|
||||
val addrofValue = PtAddressOf(addrOfDt, returnvalue.position)
|
||||
addrofValue.add(returnvalue as PtIdentifier)
|
||||
addrofValue.parent = ret.parent
|
||||
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnRegs.single().first.registerOrPair!!, false)
|
||||
@ -1279,7 +1259,7 @@ $repeatLabel""")
|
||||
}
|
||||
|
||||
if(addressExpr.operator=="+") {
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr, false)
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr)
|
||||
if (ptrAndIndex == null) return false
|
||||
|
||||
if(write) {
|
||||
@ -1291,8 +1271,10 @@ $repeatLabel""")
|
||||
if(addrOf!=null && constOffset!=null) {
|
||||
if(addrOf.isFromArrayElement) {
|
||||
TODO("address-of array element $addrOf")
|
||||
} else if(addrOf.dereference!=null) {
|
||||
throw AssemblyError("write &dereference, makes no sense at ${addrOf.position}")
|
||||
} else {
|
||||
out(" sta ${asmSymbolName(addrOf.identifier)}+${constOffset}")
|
||||
out(" sta ${asmSymbolName(addrOf.identifier!!)}+${constOffset}")
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -1331,8 +1313,10 @@ $repeatLabel""")
|
||||
if(addrOf!=null && constOffset!=null) {
|
||||
if(addrOf.isFromArrayElement) {
|
||||
TODO("address-of array element $addrOf")
|
||||
} else if(addrOf.dereference!=null) {
|
||||
TODO("read &dereference")
|
||||
} else {
|
||||
out(" lda ${asmSymbolName(addrOf.identifier)}+${constOffset}")
|
||||
out(" lda ${asmSymbolName(addrOf.identifier!!)}+${constOffset}")
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -1381,8 +1365,10 @@ $repeatLabel""")
|
||||
if(addrOf!=null && constOffset!=null) {
|
||||
if(addrOf.isFromArrayElement) {
|
||||
TODO("address-of array element $addrOf")
|
||||
} else if(addrOf.dereference!=null) {
|
||||
throw AssemblyError("write &dereference, makes no sense at ${addrOf.position}")
|
||||
} else {
|
||||
out(" sta ${asmSymbolName(addrOf.identifier)}-${constOffset}")
|
||||
out(" sta ${asmSymbolName(addrOf.identifier!!)}-${constOffset}")
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -1412,8 +1398,10 @@ $repeatLabel""")
|
||||
if(addrOf!=null && constOffset!=null) {
|
||||
if(addrOf.isFromArrayElement) {
|
||||
TODO("address-of array element $addrOf")
|
||||
} else if(addrOf.dereference!=null) {
|
||||
TODO("read &dereference")
|
||||
} else {
|
||||
out(" lda ${asmSymbolName(addrOf.identifier)}-${constOffset}")
|
||||
out(" lda ${asmSymbolName(addrOf.identifier!!)}-${constOffset}")
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -1444,7 +1432,15 @@ $repeatLabel""")
|
||||
val node = stScope.astNode
|
||||
if(node is PtSubroutineParameter)
|
||||
return node
|
||||
return node!!.definingSub()?.parameters?.singleOrNull { it.name===name }
|
||||
val params = node!!.definingSub()?.signature?.children
|
||||
if(params!=null) {
|
||||
for(param in params) {
|
||||
param as PtSubroutineParameter
|
||||
if(param.name==name)
|
||||
return param
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) {
|
||||
@ -1549,6 +1545,51 @@ $repeatLabel""")
|
||||
return "$GENERATED_LABEL_PREFIX${generatedLabelSequenceNumber}_$postfix"
|
||||
}
|
||||
|
||||
internal fun createTempVarReused(dt: BaseDataType, preferZeropage: Boolean, stmt: PtNode): String {
|
||||
val scope = stmt.definingISub()!!
|
||||
val asmInfo = subroutineExtra(scope)
|
||||
var parent = stmt.parent
|
||||
while(parent !is PtProgram) {
|
||||
if(parent is PtRepeatLoop || parent is PtForLoop)
|
||||
break
|
||||
parent = parent.parent
|
||||
}
|
||||
val isNested = parent is PtRepeatLoop || parent is PtForLoop
|
||||
|
||||
if(!isNested) {
|
||||
// we can re-use a counter var from the subroutine if it already has one for that datatype
|
||||
val existingVar = asmInfo.extraVars.firstOrNull { it.first==dt && it.second.endsWith("tempv") }
|
||||
if(existingVar!=null) {
|
||||
if(!preferZeropage || existingVar.third!=null) {
|
||||
// println("reuse temp counter var: $dt ${existingVar.second} @${stmt.position}")
|
||||
return existingVar.second
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val counterVar = makeLabel("tempv")
|
||||
// println("new temp counter var: $dt $counterVar @${stmt.position}")
|
||||
when {
|
||||
dt.isIntegerOrBool -> {
|
||||
if(preferZeropage) {
|
||||
val result = zeropage.allocate(counterVar, DataType.forDt(dt), null, stmt.position, errors)
|
||||
result.fold(
|
||||
success = { (address, _, _) -> asmInfo.extraVars.add(Triple(dt, counterVar, address)) },
|
||||
failure = { asmInfo.extraVars.add(Triple(dt, counterVar, null)) } // allocate normally
|
||||
)
|
||||
} else {
|
||||
asmInfo.extraVars.add(Triple(dt, counterVar, null)) // allocate normally
|
||||
}
|
||||
return counterVar
|
||||
}
|
||||
dt == BaseDataType.FLOAT -> {
|
||||
asmInfo.extraVars.add(Triple(dt, counterVar, null)) // allocate normally, floats never on zeropage
|
||||
return counterVar
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignConstFloatToPointerAY(number: PtNumber) {
|
||||
val floatConst = allocator.getFloatAsmConst(number.number)
|
||||
out("""
|
||||
@ -1569,7 +1610,7 @@ $repeatLabel""")
|
||||
val compare = if(useSbc) "sec | sbc" else "cmp"
|
||||
fun cmpViaScratch() {
|
||||
if(assignmentAsmGen.directIntoY(value)) {
|
||||
assignExpressionToRegister(value, RegisterOrPair.Y, false)
|
||||
assignExpressionToRegister(value, RegisterOrPair.Y)
|
||||
out(" sty P8ZP_SCRATCH_REG")
|
||||
} else {
|
||||
out(" pha")
|
||||
@ -1585,7 +1626,9 @@ $repeatLabel""")
|
||||
if(constIndex!=null) {
|
||||
val offset = program.memsizer.memorySize(value.type, constIndex)
|
||||
if(offset<256) {
|
||||
return out(" ldy #$offset | $compare ${asmVariableName(value.variable)},y")
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
return out(" ldy #$offset | $compare ${asmVariableName(value.variable!!)},y")
|
||||
}
|
||||
}
|
||||
cmpViaScratch()
|
||||
@ -1628,7 +1671,7 @@ $repeatLabel""")
|
||||
}
|
||||
|
||||
internal fun assignConditionValueToRegisterAndTest(condition: PtExpression) {
|
||||
assignExpressionToRegister(condition, RegisterOrPair.A, false)
|
||||
assignExpressionToRegister(condition, RegisterOrPair.A)
|
||||
when(condition) {
|
||||
is PtNumber,
|
||||
is PtBool,
|
||||
|
@ -508,9 +508,11 @@ private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): L
|
||||
|
||||
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
// jsr Sub + rts -> jmp Sub
|
||||
// jmp Sub + rts -> jmp Sub
|
||||
// rts + jmp -> remove jmp
|
||||
// rts + bxx -> remove bxx
|
||||
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
|
||||
// bra/jmp + bra/jmp -> remove second bra/jmp (bra bra / jmp jmp are not removed because this is likely a jump table!)
|
||||
// and some other optimizations.
|
||||
|
||||
val mods = mutableListOf<Modification>()
|
||||
@ -520,7 +522,10 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
|
||||
val third = lines[2].value
|
||||
|
||||
if(!haslabel(second)) {
|
||||
if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
if ((" jmp" in first || "\tjmp" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
}
|
||||
else if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
if("floats.pushFAC" !in first && "floats.popFAC" !in first) { // these 2 routines depend on being called with JSR!!
|
||||
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
@ -563,6 +568,15 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only remove bra followed by jmp or jmp followed by bra
|
||||
// bra bra or jmp jmp is likely part of a jump table, which should keep all entries!
|
||||
if((" bra" in first || "\tbra" in first) && (" jmp" in second || "\tjmp" in second)) {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
if((" jmp" in first || "\tjmp" in first) && (" bra" in second || "\tbra" in second)) {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -44,6 +44,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
"setlsb" -> funcSetLsbMsb(fcall, false)
|
||||
"setmsb" -> funcSetLsbMsb(fcall, true)
|
||||
"memory" -> funcMemory(fcall, discardResult, resultRegister)
|
||||
"structalloc" -> funcStructAlloc(fcall, discardResult, resultRegister)
|
||||
"peekw" -> funcPeekW(fcall, resultRegister)
|
||||
"peekf" -> funcPeekF(fcall, resultRegister)
|
||||
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
||||
@ -53,7 +54,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val memread = PtMemoryByte(fcall.position)
|
||||
memread.add(fcall.args[0])
|
||||
memread.parent = fcall
|
||||
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
val memtarget = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.position, memory=memread)
|
||||
asmgen.assignExpressionTo(fcall.args[1], memtarget)
|
||||
@ -380,8 +381,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
||||
val name = (fcall.args[0] as PtString).value
|
||||
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
|
||||
|
||||
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position)
|
||||
val addressOf = PtAddressOf(fcall.position)
|
||||
val addressOf = PtAddressOf(DataType.pointer(BaseDataType.UBYTE), fcall.position)
|
||||
addressOf.add(slabname)
|
||||
addressOf.parent = fcall
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
|
||||
@ -390,6 +392,16 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||
}
|
||||
|
||||
private fun funcStructAlloc(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?) {
|
||||
if(discardResult)
|
||||
throw AssemblyError("should not discard result of struct allocation at $fcall")
|
||||
if(fcall.args.isEmpty())
|
||||
TODO("struct alloc in BSS")
|
||||
else
|
||||
TODO("static struct alloc with values")
|
||||
}
|
||||
|
||||
|
||||
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
translateArguments(fcall, scope)
|
||||
when(fcall.args[0].type.base) {
|
||||
@ -415,8 +427,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
BaseDataType.UBYTE -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
asmgen.out(" lda ${varname},x | lsr a | bcc + | ora #$80 |+ | sta ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
@ -438,8 +453,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
BaseDataType.UWORD -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" lsr ${varname}_msb,x | ror ${varname}_lsb,x | bcc + | lda ${varname}_msb,x | ora #$80 | sta ${varname}_msb,x |+ ")
|
||||
else
|
||||
@ -465,7 +482,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
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.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
asmgen.out(" ror ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
@ -498,7 +518,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
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.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" ror ${varname}_msb,x | ror ${varname}_lsb,x")
|
||||
else
|
||||
@ -521,8 +543,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
BaseDataType.UBYTE -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
asmgen.out(" lda ${varname},x | cmp #$80 | rol a | sta ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
@ -545,7 +569,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb,x |+")
|
||||
else
|
||||
@ -571,7 +597,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
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.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
asmgen.out(" rol ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
@ -604,7 +632,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
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.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" rol ${varname}_lsb,x | rol ${varname}_msb,x")
|
||||
else
|
||||
@ -634,6 +664,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val msbAdd: Int
|
||||
if(indexer.splitWords) {
|
||||
val arrayVariable = indexer.variable
|
||||
if(arrayVariable==null)
|
||||
TODO("support for ptr indexing ${indexer.position}")
|
||||
indexer.children[0] = PtIdentifier(arrayVariable.name + if(msb) "_msb" else "_lsb", DataType.arrayFor(BaseDataType.UBYTE, false), arrayVariable.position)
|
||||
indexer.children[0].parent = indexer
|
||||
elementSize = 1
|
||||
@ -676,7 +708,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(fcall.args[1].asConstInteger() == 0) {
|
||||
assignAsmGen.assignConstantByte(target, 0)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||
assignAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
||||
}
|
||||
}
|
||||
@ -739,7 +771,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.assignConstFloatToPointerAY(number)
|
||||
}
|
||||
else -> {
|
||||
val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT)
|
||||
val tempvar = asmgen.createTempVarReused(BaseDataType.FLOAT, false, fcall)
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.FLOAT)
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
@ -815,7 +847,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr floats.MOVFM")
|
||||
if(resultRegister!=null) {
|
||||
assignAsmGen.assignFAC1float(
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position, null, null, null, resultRegister, null))
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1112,7 +1144,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
|
||||
// just read the msb byte out of the word array
|
||||
if(arg.splitWords) {
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable)+"_msb"
|
||||
if(arg.variable==null)
|
||||
TODO("support for ptr indexing ${arg.position}")
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable!!)+"_msb"
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
@ -1129,7 +1163,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
} else {
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable)
|
||||
if(arg.variable==null)
|
||||
TODO("support for ptr indexing ${arg.position}")
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable!!)
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
@ -1211,7 +1247,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
|
||||
// just read the lsb byte out of the word array
|
||||
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable)+"_lsb" else asmgen.asmVariableName(arg.variable)
|
||||
if(arg.variable==null)
|
||||
TODO("support for ptr indexing ${arg.position}")
|
||||
|
||||
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable!!)+"_lsb" else asmgen.asmVariableName(arg.variable!!)
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
@ -1276,7 +1315,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
fun getSourceForFloat(value: PtExpression): AsmAssignSource {
|
||||
return when (value) {
|
||||
is PtIdentifier -> {
|
||||
val addr = PtAddressOf(value.position)
|
||||
val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), value.position)
|
||||
addr.add(value)
|
||||
addr.parent = call
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
@ -1290,7 +1329,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
|
||||
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
|
||||
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position)
|
||||
val addr = PtAddressOf(value.position)
|
||||
val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), value.position)
|
||||
addr.add(variable)
|
||||
addr.parent = call
|
||||
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
|
||||
@ -1310,7 +1349,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
|
||||
conv.dt.isPassByRef -> {
|
||||
// put the address of the argument in AY
|
||||
val addr = PtAddressOf(value.position)
|
||||
val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), value.position)
|
||||
addr.add(value)
|
||||
addr.parent = call
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
@ -1328,7 +1367,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
|
||||
conv.dt.isPassByRef -> {
|
||||
// put the address of the argument in AY
|
||||
val addr = PtAddressOf(value.position)
|
||||
val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false),value.position)
|
||||
addr.add(value)
|
||||
addr.parent = call
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
|
@ -89,8 +89,8 @@ internal class ForLoopsAsmGen(
|
||||
if(asmgen.options.romable) {
|
||||
// cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
|
||||
// so we need to store the loop end value in a newly allocated temporary variable
|
||||
val toValueVar = asmgen.getTempVarName(iterableDt.elementType().base)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
val toValueVar = asmgen.createTempVarReused(iterableDt.elementType().base, false, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||
asmgen.out(" sta $toValueVar")
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
@ -136,7 +136,7 @@ internal class ForLoopsAsmGen(
|
||||
|
||||
// use self-modifying code to store the loop end comparison value
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
@ -186,7 +186,7 @@ $modifiedLabel cmp #0 ; modified
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
@ -296,7 +296,7 @@ $modifiedLabel cmp #0 ; modified
|
||||
if(asmgen.options.romable) {
|
||||
// cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
|
||||
// so we need to store the loop end value in a newly allocated temporary variable
|
||||
val toValueVar = asmgen.getTempVarName(iterableDt.elementType().base)
|
||||
val toValueVar = asmgen.createTempVarReused(iterableDt.elementType().base, false, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out(" sta $toValueVar")
|
||||
@ -420,6 +420,7 @@ $endLabel""")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.romableError("self-modifying code (forloop over words range)", stmt.position) // TODO fix romable; there is self-modifying code below
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
@ -442,7 +443,6 @@ $modifiedLabel sbc #0 ; modified
|
||||
eor #$80
|
||||
+ bpl $loopLabel
|
||||
$endLabel""")
|
||||
asmgen.romableError("self-modifying code (forloop over words range)", stmt.position) // TODO fix romable
|
||||
}
|
||||
|
||||
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
|
||||
@ -500,15 +500,15 @@ $endLabel""")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val iterableName = asmgen.asmVariableName(ident)
|
||||
val numElements = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
|
||||
val numElements: UInt = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
|
||||
is StStaticVariable -> symbol.length!!
|
||||
is StMemVar -> symbol.length!!
|
||||
else -> 0
|
||||
else -> 0u
|
||||
}
|
||||
when {
|
||||
iterableDt.isString -> {
|
||||
if(asmgen.options.romable) {
|
||||
val indexVar = asmgen.getTempVarName(BaseDataType.UBYTE)
|
||||
val indexVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
sty $indexVar
|
||||
@ -539,14 +539,17 @@ $endLabel""")
|
||||
}
|
||||
}
|
||||
iterableDt.isByteArray || iterableDt.isBoolArray -> {
|
||||
val indexVar = if(asmgen.options.romable) asmgen.getTempVarName(iterableDt.elementType().base) else asmgen.makeLabel("for_index")
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(iterableDt.elementType().base, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
$loopLabel sty $indexVar
|
||||
lda $iterableName,y
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255) {
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@ -562,7 +565,7 @@ $loopLabel sty $indexVar
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16) {
|
||||
if(numElements>=16u) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
@ -576,7 +579,10 @@ $loopLabel sty $indexVar
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
iterableDt.isSplitWordArray -> {
|
||||
val indexVar = if(asmgen.options.romable) asmgen.getTempVarName(BaseDataType.UBYTE) else asmgen.makeLabel("for_index")
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
@ -586,7 +592,7 @@ $loopLabel sty $indexVar
|
||||
lda ${iterableName}_msb,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255) {
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@ -602,7 +608,7 @@ $loopLabel sty $indexVar
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16) {
|
||||
if(numElements>=16u) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
@ -616,8 +622,10 @@ $loopLabel sty $indexVar
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
iterableDt.isWordArray -> {
|
||||
val length = numElements * 2
|
||||
val indexVar = if(asmgen.options.romable) asmgen.getTempVarName(BaseDataType.UBYTE) else asmgen.makeLabel("for_index")
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
@ -627,16 +635,16 @@ $loopLabel sty $indexVar
|
||||
lda $iterableName+1,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(length<=127) {
|
||||
if(numElements<=127u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
iny
|
||||
cpy #$length
|
||||
cpy #${numElements*2u}
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
// length is 128 words, 256 bytes
|
||||
// array size is 128 words, 256 bytes
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@ -645,7 +653,7 @@ $loopLabel sty $indexVar
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(!asmgen.options.romable) {
|
||||
if(length>=16) {
|
||||
if(numElements>=16u) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
|
@ -36,7 +36,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
val symbol = asmgen.symbolTable.lookup(call.name)!!
|
||||
if(symbol.type == StNodeType.LABEL) {
|
||||
require(call.void)
|
||||
asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedName)}")
|
||||
asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedNameString)}")
|
||||
return
|
||||
}
|
||||
|
||||
@ -139,15 +139,16 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
}
|
||||
else if(sub is PtSub) {
|
||||
if(optimizeIntArgsViaCpuRegisters(sub.parameters)) {
|
||||
val parameters = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
|
||||
if(optimizeIntArgsViaCpuRegisters(parameters)) {
|
||||
// Note that if the args fit into cpu registers, we don't concern ourselves here
|
||||
// if they should be put into regular subroutine parameter variables, or the R0-R15 register variables.
|
||||
// That is now up to the subroutine itself.
|
||||
useCpuRegistersForArgs(call.args, sub)
|
||||
} else {
|
||||
// arguments via variables
|
||||
val paramValues = sub.parameters.zip(call.args)
|
||||
val (normalParams, registerParams) = paramValues.partition { it.first.register == null }
|
||||
val paramValues = parameters.zip(call.args)
|
||||
val (normalParams, registerParams) = paramValues.partition { (it.first.register == null) }
|
||||
if (normalParams.isNotEmpty()) {
|
||||
for (p in normalParams)
|
||||
argumentViaVariable(sub, p.first, p.second)
|
||||
@ -166,7 +167,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
|
||||
private fun useCpuRegistersForArgs(args: List<PtExpression>, sub: PtSub) {
|
||||
val params = sub.parameters
|
||||
val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
|
||||
when(params.size) {
|
||||
1 -> {
|
||||
val register = if (params[0].type.isByteOrBool) RegisterOrPair.A else RegisterOrPair.AY
|
||||
@ -234,9 +235,9 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
val param = sub.parameters[it]
|
||||
val arg = call.args[it]
|
||||
registersUsed += if(usesOtherRegistersWhileEvaluating(arg)) {
|
||||
if(!registersUsed.any{it.statusflag!=null || it.registerOrPair in CpuRegisters})
|
||||
if(!registersUsed.any{r -> r.statusflag!=null || r.registerOrPair in CpuRegisters})
|
||||
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
|
||||
else if(registersUsed.any {it.statusflag!=null}) {
|
||||
else if(registersUsed.any { r-> r.statusflag!=null }) {
|
||||
throw AssemblyError("call argument evaluation problem: can't save cpu statusregister parameter ${call.position}")
|
||||
}
|
||||
else {
|
||||
@ -339,7 +340,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
val src = if(value.type.isPassByRef) {
|
||||
if(value is PtIdentifier) {
|
||||
val addr = PtAddressOf(Position.DUMMY)
|
||||
val addr = PtAddressOf(value.type.typeForAddressOf(false),Position.DUMMY)
|
||||
addr.add(value)
|
||||
addr.parent = scope as PtNode
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
|
||||
|
@ -38,7 +38,6 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
// use a BIT instruction to test for bit 7 or 6 set/clear
|
||||
val (testBitSet, variable, bitmask) = useBIT
|
||||
return translateIfBIT(stmt, jumpAfterIf, testBitSet, variable, bitmask)
|
||||
return
|
||||
}
|
||||
|
||||
val rightDt = compareCond.right.type
|
||||
@ -87,6 +86,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
if(testForBitSet) {
|
||||
if(jumpAfterIf!=null) {
|
||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
branch("bmi", target)
|
||||
}
|
||||
else
|
||||
@ -94,6 +94,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
if(jumpAfterIf!=null) {
|
||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
branch("bpl", target)
|
||||
}
|
||||
else
|
||||
@ -107,6 +108,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
if(testForBitSet) {
|
||||
if(jumpAfterIf!=null) {
|
||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
branch("bvs", target)
|
||||
}
|
||||
else
|
||||
@ -114,6 +116,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
if(jumpAfterIf!=null) {
|
||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
branch("bvc", target)
|
||||
}
|
||||
else
|
||||
@ -146,6 +149,93 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
translateIfElseBodies("beq", ifElse)
|
||||
}
|
||||
|
||||
private fun translateIfElseBodiesSignedByte(elseConditional: String, value: PtExpression, stmt: PtIfElse) {
|
||||
fun branchElse(label: String) {
|
||||
when (elseConditional) {
|
||||
"<" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $label""")
|
||||
}
|
||||
">=" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $label""")
|
||||
}
|
||||
else -> throw AssemblyError("wrong conditional $elseConditional")
|
||||
}
|
||||
}
|
||||
val afterIfLabel = asmgen.makeLabel("afterif")
|
||||
asmgen.cmpAwithByteValue(value, true)
|
||||
if(stmt.hasElse()) {
|
||||
// if and else blocks
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
branchElse(elseLabel)
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
// no else block
|
||||
branchElse(afterIfLabel)
|
||||
asmgen.translate(stmt.ifScope)
|
||||
}
|
||||
asmgen.out(afterIfLabel)
|
||||
}
|
||||
|
||||
private fun translateJumpElseBodiesSignedByte(elseConditional: String, value: PtExpression, jump: PtJump, elseBlock: PtNodeGroup) {
|
||||
fun branchTarget(label: String) {
|
||||
when (elseConditional) {
|
||||
"<" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $label""")
|
||||
}
|
||||
">=" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $label""")
|
||||
}
|
||||
else -> throw AssemblyError("wrong conditional $elseConditional")
|
||||
}
|
||||
}
|
||||
fun branchElse(label: String) {
|
||||
when (elseConditional) {
|
||||
"<" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $label""")
|
||||
}
|
||||
">=" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $label""")
|
||||
}
|
||||
else -> throw AssemblyError("wrong conditional $elseConditional")
|
||||
}
|
||||
}
|
||||
|
||||
var target = asmgen.getJumpTarget(jump, false)
|
||||
asmgen.cmpAwithByteValue(value, true)
|
||||
if(target.indirect) {
|
||||
branchElse("+")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.jmp(target.asmLabel, target.indirect, target.indexedX)
|
||||
asmgen.out("+")
|
||||
} else {
|
||||
require(!target.needsExpressionEvaluation)
|
||||
branchTarget(target.asmLabel)
|
||||
}
|
||||
asmgen.translate(elseBlock)
|
||||
}
|
||||
|
||||
private fun translateIfElseBodies(elseBranchInstr: String, stmt: PtIfElse) {
|
||||
// comparison value is already in A
|
||||
val afterIfLabel = asmgen.makeLabel("afterif")
|
||||
@ -154,7 +244,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
asmgen.out(" $elseBranchInstr $elseLabel")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -171,10 +261,9 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
if(target.indirect) {
|
||||
asmgen.out(" $falseBranch +")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.jmp(target.asmLabel, target.indirect, target.indexedX)
|
||||
asmgen.out("+")
|
||||
} else {
|
||||
require(!target.needsExpressionEvaluation)
|
||||
asmgen.out(" $branchInstr ${target.asmLabel}")
|
||||
@ -210,38 +299,9 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
translateIfElseBodies("beq", stmt)
|
||||
}
|
||||
"<" -> translateByteLess(stmt, signed, jumpAfterIf)
|
||||
"<=" -> {
|
||||
// X<=Y -> Y>=X (reverse of >=)
|
||||
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, signed)
|
||||
asmgen.cmpAwithByteValue(condition.left, false)
|
||||
return if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bpl", "bmi", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bmi", stmt)
|
||||
} else {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
}
|
||||
}
|
||||
"<=" -> translateByteLessEqual(stmt, signed, jumpAfterIf)
|
||||
">" -> translateByteGreater(stmt, signed, jumpAfterIf)
|
||||
">=" -> {
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
return if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bpl", "bmi", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bmi", stmt)
|
||||
} else {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
}
|
||||
}
|
||||
">=" -> translateByteGreaterEqual(stmt, signed, jumpAfterIf)
|
||||
in LogicalOperators -> {
|
||||
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.BOOL, stmt.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
|
||||
@ -286,7 +346,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
if(target.indirect) {
|
||||
asmgen.out(" bmi + | beq +")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -306,7 +367,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
asmgen.out(" bmi $elseLabel | beq $elseLabel")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -353,7 +414,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
if(target.indirect) {
|
||||
asmgen.out(" bmi + | bne ++")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -375,7 +437,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
bpl $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -402,13 +464,13 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
private fun translateByteLess(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||
val condition = stmt.condition as PtBinaryExpression
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope)
|
||||
translateJumpElseBodiesSignedByte("<", condition.right, jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bpl", stmt)
|
||||
translateIfElseBodiesSignedByte("<", condition.right, stmt)
|
||||
} else {
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcc", "bcs", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
@ -416,25 +478,43 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateByteLessEqual(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||
// X<=Y -> Y>=X (reverse of >=)
|
||||
val condition = stmt.condition as PtBinaryExpression
|
||||
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, signed)
|
||||
return if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodiesSignedByte(">=", condition.left, jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodiesSignedByte(">=", condition.left, stmt)
|
||||
} else {
|
||||
asmgen.cmpAwithByteValue(condition.left, false)
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateByteGreater(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||
val condition = stmt.condition as PtBinaryExpression
|
||||
if(signed) {
|
||||
// X>Y --> Y<X
|
||||
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, true)
|
||||
asmgen.cmpAwithByteValue(condition.left, true)
|
||||
if (jumpAfterIf != null)
|
||||
translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope)
|
||||
translateJumpElseBodiesSignedByte("<", condition.left, jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bpl", stmt)
|
||||
translateIfElseBodiesSignedByte("<", condition.left, stmt)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A)
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
if(jumpAfterIf!=null) {
|
||||
var target = asmgen.getJumpTarget(jumpAfterIf, false)
|
||||
if(target.indirect) {
|
||||
asmgen.out(" bcc + | beq +")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -453,7 +533,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
asmgen.out(" bcc $elseLabel | beq $elseLabel")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -466,6 +546,23 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateByteGreaterEqual(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||
val condition = stmt.condition as PtBinaryExpression
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
||||
return if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodiesSignedByte(">=", condition.right, jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodiesSignedByte(">=", condition.right, stmt)
|
||||
} else {
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateIfWord(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
|
||||
val signed = condition.left.type.isSigned
|
||||
val constValue = condition.right.asConstInteger()
|
||||
@ -535,7 +632,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
eor #128
|
||||
+ bpl +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -562,7 +660,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
eor #128
|
||||
+ bmi $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -590,7 +688,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
cmp $valueLsb
|
||||
bcs +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
_jump jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -615,7 +714,7 @@ _jump jmp (${target.asmLabel})
|
||||
sbc $valueMsb
|
||||
bcs $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -667,7 +766,8 @@ _jump jmp (${target.asmLabel})
|
||||
eor #128
|
||||
+ bpl +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -694,7 +794,7 @@ _jump jmp (${target.asmLabel})
|
||||
eor #128
|
||||
+ bmi $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -721,7 +821,8 @@ _jump jmp (${target.asmLabel})
|
||||
sbc $valueMsb
|
||||
bcc +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -744,7 +845,7 @@ _jump jmp (${target.asmLabel})
|
||||
sbc $valueMsb
|
||||
bcc $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -784,7 +885,9 @@ _jump jmp (${target.asmLabel})
|
||||
private fun loadAndCmp0MSB(value: PtExpression) {
|
||||
when(value) {
|
||||
is PtArrayIndexer -> {
|
||||
val varname = asmgen.asmVariableName(value.variable)
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varname = asmgen.asmVariableName(value.variable!!)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
if(value.splitWords)
|
||||
asmgen.out(" lda ${varname}_msb,y")
|
||||
@ -800,8 +903,8 @@ _jump jmp (${target.asmLabel})
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
|
||||
asmgen.out(" cpy #0")
|
||||
} else {
|
||||
var varname = asmgen.asmVariableName(value.identifier)
|
||||
if(value.identifier.type.isSplitWordArray) {
|
||||
var varname = asmgen.asmVariableName(value.identifier!!)
|
||||
if(value.identifier!!.type.isSplitWordArray) {
|
||||
varname += if(value.isMsbForSplitArray) "_msb" else "_lsb"
|
||||
}
|
||||
asmgen.out(" lda #>$varname")
|
||||
@ -829,7 +932,8 @@ _jump jmp (${target.asmLabel})
|
||||
lda $valueLsb
|
||||
bne ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -857,7 +961,7 @@ _jump jmp (${target.asmLabel})
|
||||
bne $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -878,8 +982,10 @@ _jump jmp (${target.asmLabel})
|
||||
if(value is PtIdentifier)
|
||||
return compareLsbMsb(value.name, value.name+"+1")
|
||||
if(value is PtArrayIndexer) {
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val constIndex = value.index.asConstInteger()
|
||||
val varname = asmgen.asmVariableName(value.variable)
|
||||
val varname = asmgen.asmVariableName(value.variable!!)
|
||||
if(constIndex!=null) {
|
||||
if(value.splitWords) {
|
||||
return compareLsbMsb("${varname}_lsb+$constIndex", "${varname}_msb+$constIndex")
|
||||
@ -908,7 +1014,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #0
|
||||
bne ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -936,7 +1043,7 @@ _jump jmp (${target.asmLabel})
|
||||
bne $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -973,7 +1080,8 @@ _jump jmp (${target.asmLabel})
|
||||
lda $valueLsb
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -1001,7 +1109,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -1023,8 +1131,10 @@ _jump jmp (${target.asmLabel})
|
||||
if(value is PtIdentifier)
|
||||
return compareLsbMsb(value.name, value.name+"+1")
|
||||
if(value is PtArrayIndexer) {
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val constIndex = value.index.asConstInteger()
|
||||
val varname = asmgen.asmVariableName(value.variable)
|
||||
val varname = asmgen.asmVariableName(value.variable!!)
|
||||
if(constIndex!=null) {
|
||||
if(value.splitWords) {
|
||||
return compareLsbMsb("${varname}_lsb+$constIndex", "${varname}_msb+$constIndex")
|
||||
@ -1053,7 +1163,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #0
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -1081,7 +1192,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -1136,7 +1247,9 @@ _jump jmp (${target.asmLabel})
|
||||
is PtArrayIndexer -> {
|
||||
val constIndex = value.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
val varName = asmgen.asmVariableName(value.variable)
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varName = asmgen.asmVariableName(value.variable!!)
|
||||
if(value.splitWords) {
|
||||
return translateLoadFromVarSplitw(varName, constIndex, "bne", "beq")
|
||||
}
|
||||
@ -1157,7 +1270,9 @@ _jump jmp (${target.asmLabel})
|
||||
is PtArrayIndexer -> {
|
||||
val constIndex = value.index.asConstInteger()
|
||||
if (constIndex != null) {
|
||||
val varName = asmgen.asmVariableName(value.variable)
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varName = asmgen.asmVariableName(value.variable!!)
|
||||
if(value.splitWords) {
|
||||
return translateLoadFromVarSplitw(varName, constIndex, "beq", "bne")
|
||||
}
|
||||
@ -1196,7 +1311,8 @@ _jump jmp (${target.asmLabel})
|
||||
cpy $valueMsb
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -1221,7 +1337,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -1248,7 +1364,8 @@ _jump jmp (${target.asmLabel})
|
||||
cpy $valueMsb
|
||||
bne +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -1273,7 +1390,7 @@ _jump jmp (${target.asmLabel})
|
||||
cpy $valueMsb
|
||||
bne $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -1302,7 +1419,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp ${right.name}+1
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -1331,7 +1449,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -1361,7 +1479,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp ${right.name}+1
|
||||
bne +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -1390,7 +1509,7 @@ _jump jmp (${target.asmLabel})
|
||||
cmp ${right.name}+1
|
||||
bne $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -1423,7 +1542,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #>$value
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -1452,7 +1572,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -1481,7 +1601,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #>$value
|
||||
bne +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@ -1510,7 +1631,7 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #>$value
|
||||
bne $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@ -1532,8 +1653,10 @@ _jump jmp (${target.asmLabel})
|
||||
fun translateEqualsArray(left: PtArrayIndexer, right: PtExpression) {
|
||||
val constIndex = left.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
if(left.variable==null)
|
||||
TODO("support for ptr indexing ${left.position}")
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
val varName = asmgen.asmVariableName(left.variable)
|
||||
val varName = asmgen.asmVariableName(left.variable!!)
|
||||
if(left.splitWords) {
|
||||
return if(notEquals)
|
||||
translateAYNotEquals("${varName}_lsb+$constIndex", "${varName}_msb+$constIndex")
|
||||
@ -1596,13 +1719,13 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
if(left.isFromArrayElement)
|
||||
if(left.isFromArrayElement) {
|
||||
fallbackTranslateForSimpleCondition(stmt)
|
||||
else {
|
||||
val varname = if(left.identifier.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb"
|
||||
} else {
|
||||
val varname = if(left.identifier!!.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
|
||||
} else {
|
||||
left.identifier.name
|
||||
left.identifier!!.name
|
||||
}
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
translateAYNotEquals("#<$varname", "#>$varname")
|
||||
@ -1648,13 +1771,13 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
if(left.isFromArrayElement)
|
||||
if(left.isFromArrayElement) {
|
||||
fallbackTranslateForSimpleCondition(stmt)
|
||||
else {
|
||||
val varname = if(left.identifier.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb"
|
||||
} else {
|
||||
val varname = if(left.identifier!!.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
|
||||
} else {
|
||||
left.identifier.name
|
||||
left.identifier!!.name
|
||||
}
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
translateAYEquals("#<$varname", "#>$varname")
|
||||
|
@ -15,18 +15,18 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
|
||||
when {
|
||||
expr.type.isByteOrBool -> {
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(falseLabel)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
|
||||
asmgen.out(endLabel)
|
||||
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
||||
}
|
||||
expr.type.isWord || expr.type.isString -> {
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(falseLabel)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
|
||||
asmgen.out(endLabel)
|
||||
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
}
|
||||
@ -195,7 +195,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
beq $falseLabel
|
||||
+""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
cmp $varRight
|
||||
bne +
|
||||
@ -219,7 +219,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
cmp $varRight+1
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
cmp $varRight
|
||||
bne $falseLabel
|
||||
@ -242,7 +242,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
beq $falseLabel
|
||||
+""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
cmp #<$number
|
||||
bne +
|
||||
@ -265,7 +265,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
cmp #>$number
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out( """
|
||||
cmp #<$number
|
||||
bne $falseLabel
|
||||
@ -284,7 +284,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
ora $varname+1
|
||||
beq $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel")
|
||||
}
|
||||
}
|
||||
@ -299,7 +299,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
ora $varname+1
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel")
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,6 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
|
||||
memorySlabs()
|
||||
tempVars()
|
||||
footer()
|
||||
}
|
||||
}
|
||||
@ -219,28 +218,6 @@ 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) {
|
||||
BaseDataType.BOOL -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.BYTE -> asmgen.out("$name .char ?")
|
||||
BaseDataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.WORD -> asmgen.out("$name .sint ?")
|
||||
BaseDataType.UWORD -> asmgen.out("$name .word ?")
|
||||
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||
BaseDataType.LONG -> throw AssemblyError("should not have a variable with long dt only constants")
|
||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
asmgen.out(" .send BSS")
|
||||
}
|
||||
|
||||
private fun footer() {
|
||||
var relocateBssVars = false
|
||||
var relocateBssSlabs = false
|
||||
@ -353,7 +330,9 @@ internal class ProgramAndVarsGen(
|
||||
initializers.forEach { assign ->
|
||||
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
|
||||
asmgen.translate(assign)
|
||||
// the other variables that should be set to zero are done so as part of the BSS section.
|
||||
else
|
||||
throw AssemblyError("non-zp variable should not be initialized to zero; it will be zeroed as part of BSS clear")
|
||||
// the other variables that should be set to zero are done so as part of the BSS section clear.
|
||||
}
|
||||
asmgen.out(" rts\n .bend")
|
||||
}
|
||||
@ -370,7 +349,7 @@ internal class ProgramAndVarsGen(
|
||||
val varsInBlock = getVars(scope)
|
||||
|
||||
// Zeropage Variables
|
||||
val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||
val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedNameString }.toSet()
|
||||
zeropagevars2asm(varnames)
|
||||
|
||||
// MemDefs and Consts
|
||||
@ -384,7 +363,7 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
// normal statically allocated variables
|
||||
val variables = varsInBlock
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedNameString) }
|
||||
.map { it.value as StStaticVariable }
|
||||
nonZpVariables2asm(variables)
|
||||
}
|
||||
@ -438,7 +417,7 @@ internal class ProgramAndVarsGen(
|
||||
val varsInSubroutine = getVars(scope)
|
||||
|
||||
// Zeropage Variables
|
||||
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedNameString }.toSet()
|
||||
zeropagevars2asm(varnames)
|
||||
|
||||
// MemDefs and Consts
|
||||
@ -456,7 +435,7 @@ internal class ProgramAndVarsGen(
|
||||
if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main"))
|
||||
entrypointInitialization()
|
||||
|
||||
val params = sub.parameters
|
||||
val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
|
||||
if(functioncallAsmGen.optimizeIntArgsViaCpuRegisters(params)) {
|
||||
asmgen.out("; simple int arg(s) passed via cpu register(s)")
|
||||
|
||||
@ -492,14 +471,14 @@ internal class ProgramAndVarsGen(
|
||||
sub.children.forEach { asmgen.translate(it) }
|
||||
|
||||
asmgen.out("; variables")
|
||||
asmgen.out(" .section BSS")
|
||||
asmgen.out(" .section BSS_NOCLEAR") // these extra vars are initialized before use
|
||||
val asmGenInfo = asmgen.subroutineExtra(sub)
|
||||
for((dt, name, addr) in asmGenInfo.extraVars) {
|
||||
if(addr!=null)
|
||||
asmgen.out("$name = $addr")
|
||||
else when(dt) {
|
||||
BaseDataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.UWORD -> asmgen.out("$name .word ?")
|
||||
BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> asmgen.out("$name .word ?")
|
||||
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||
}
|
||||
@ -508,11 +487,11 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||
if(asmGenInfo.usedFloatEvalResultVar2)
|
||||
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||
asmgen.out(" .send BSS")
|
||||
asmgen.out(" .send BSS_NOCLEAR")
|
||||
|
||||
// normal statically allocated variables
|
||||
val variables = varsInSubroutine
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedNameString) }
|
||||
.map { it.value as StStaticVariable }
|
||||
nonZpVariables2asm(variables)
|
||||
|
||||
@ -567,12 +546,12 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
stringVarsWithInitInZp.forEach {
|
||||
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
||||
outputStringvar(varname, 0, it.value.second, it.value.first)
|
||||
outputStringvar(varname, 0u, it.value.second, it.value.first)
|
||||
}
|
||||
|
||||
arrayVarsWithInitInZp.forEach {
|
||||
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
||||
arrayVariable2asm(varname, it.alloc.dt, 0, it.value, null)
|
||||
arrayVariable2asm(varname, it.alloc.dt, 0u, it.value, null)
|
||||
}
|
||||
|
||||
asmgen.out("+")
|
||||
@ -636,22 +615,37 @@ internal class ProgramAndVarsGen(
|
||||
val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized }
|
||||
if(varsNoInit.isNotEmpty()) {
|
||||
asmgen.out("; non-zeropage variables")
|
||||
asmgen.out(" .section BSS")
|
||||
val (notAligned, aligned) = varsNoInit.partition { it.align==0 }
|
||||
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
|
||||
uninitializedVariable2asm(it)
|
||||
val (dirty, clean) = varsNoInit.partition { it.dirty }
|
||||
|
||||
fun generate(section: String, variables: List<StStaticVariable>) {
|
||||
asmgen.out(" .section $section")
|
||||
val (notAligned, aligned) = variables.partition { it.align == 0u }
|
||||
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
|
||||
uninitializedVariable2asm(it)
|
||||
}
|
||||
aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt.base })
|
||||
.forEach { uninitializedVariable2asm(it) }
|
||||
asmgen.out(" .send $section")
|
||||
}
|
||||
aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt.base }).forEach {
|
||||
uninitializedVariable2asm(it)
|
||||
|
||||
if(clean.isNotEmpty()) {
|
||||
// clean vars end up in BSS so they're at least cleared to 0 at startup
|
||||
generate("BSS", clean)
|
||||
}
|
||||
if(dirty.isNotEmpty()) {
|
||||
// Dirty vars actually are ALSO are put into BSS so they're cleared to 0 at program startup,
|
||||
// but NOT at each entry of the subroutine they're declared in.
|
||||
// This saves the STZ's instructions in the subroutine, while still having deterministic start state.
|
||||
// So there is no actual difference here when compared to the way non-dirty variables are allocated.
|
||||
generate("BSS", dirty)
|
||||
}
|
||||
asmgen.out(" .send BSS")
|
||||
}
|
||||
|
||||
if(varsWithInit.isNotEmpty()) {
|
||||
asmgen.out("; non-zeropage variables with init value")
|
||||
val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt.isString }
|
||||
val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0 }
|
||||
val (notAlignedOther, alignedOther) = othervars.partition { it.align==0 }
|
||||
val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0u }
|
||||
val (notAlignedOther, alignedOther) = othervars.partition { it.align==0u }
|
||||
notAlignedStrings.forEach {
|
||||
outputStringvar(
|
||||
it.name,
|
||||
@ -698,23 +692,27 @@ internal class ProgramAndVarsGen(
|
||||
dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.FLOAT_MEM_SIZE}")
|
||||
dt.isSplitWordArray -> {
|
||||
alignVar(variable.align)
|
||||
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
|
||||
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!.toInt()) / 2
|
||||
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
|
||||
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
|
||||
}
|
||||
dt.isArray -> {
|
||||
alignVar(variable.align)
|
||||
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
|
||||
val numbytes = compTarget.memorySize(variable.dt, variable.length!!.toInt())
|
||||
asmgen.out("${variable.name}\t.fill $numbytes")
|
||||
}
|
||||
dt.isPointer -> asmgen.out("${variable.name}\t.word ?") // a pointer is just an uword address
|
||||
dt.isPointerArray -> {
|
||||
TODO("pointers are not supported yet")
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun alignVar(align: Int) {
|
||||
if(align > 1)
|
||||
private fun alignVar(align: UInt) {
|
||||
if(align > 1u)
|
||||
asmgen.out(" .align ${align.toHex()}")
|
||||
}
|
||||
|
||||
@ -751,7 +749,7 @@ internal class ProgramAndVarsGen(
|
||||
throw AssemblyError("all string vars should have been interned into prog")
|
||||
}
|
||||
dt.isArray -> {
|
||||
arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length)
|
||||
arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length?.toInt())
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
@ -759,7 +757,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun arrayVariable2asm(varname: String, dt: DataType, align: Int, value: StArray?, orNumberOfZeros: Int?) {
|
||||
private fun arrayVariable2asm(varname: String, dt: DataType, align: UInt, value: StArray?, orNumberOfZeros: Int?) {
|
||||
alignVar(align)
|
||||
when {
|
||||
dt.isUnsignedByteArray || dt.isBoolArray -> {
|
||||
@ -861,7 +859,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun outputStringvar(varname: String, align: Int, encoding: Encoding, value: String) {
|
||||
private fun outputStringvar(varname: String, align: UInt, encoding: Encoding, value: String) {
|
||||
alignVar(align)
|
||||
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"", false)
|
||||
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
|
||||
|
@ -52,7 +52,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE }
|
||||
val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE }
|
||||
val varsNotZp = allVariables.filter { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
|
||||
val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0 }
|
||||
val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0u }
|
||||
require(varsDontCareWithAlignment.size + varsDontCareWithoutAlignment.size + varsRequiringZp.size + varsPreferringZp.size + varsNotZp.size == numberOfAllocatableVariables)
|
||||
|
||||
var numVariablesAllocatedInZP = 0
|
||||
@ -60,9 +60,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
|
||||
varsRequiringZp.forEach { variable ->
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedName,
|
||||
variable.scopedNameString,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.length?.toInt(),
|
||||
variable.astNode?.position ?: Position.DUMMY,
|
||||
errors
|
||||
)
|
||||
@ -79,9 +79,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
if(errors.noErrors()) {
|
||||
varsPreferringZp.forEach { variable ->
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedName,
|
||||
variable.scopedNameString,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.length?.toInt(),
|
||||
variable.astNode?.position ?: Position.DUMMY,
|
||||
errors
|
||||
)
|
||||
@ -92,16 +92,16 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
// try to allocate the "don't care" interger variables into the zeropage until it is full.
|
||||
// TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
|
||||
if(errors.noErrors()) {
|
||||
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedName }
|
||||
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedNameString }
|
||||
for (variable in sortedList) {
|
||||
if(variable.dt.isIntegerOrBool) {
|
||||
if(zeropage.free.isEmpty()) {
|
||||
break
|
||||
} else {
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedName,
|
||||
variable.scopedNameString,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.length?.toInt(),
|
||||
variable.astNode?.position ?: Position.DUMMY,
|
||||
errors
|
||||
)
|
||||
|
@ -46,6 +46,12 @@ internal class AnyExprAsmGen(
|
||||
}
|
||||
return assignFloatBinExpr(expr, assign)
|
||||
}
|
||||
expr.type.isPointer -> {
|
||||
require((expr.left.type.isPointer || expr.left.type.isUnsignedWord) && (expr.right.type.isPointer || expr.right.type.isUnsignedWord)) {
|
||||
"both operands must be pointers or uwords"
|
||||
}
|
||||
throw AssemblyError("expression should have been handled otherwise: pointer ${expr.operator} at ${expr.position}")
|
||||
}
|
||||
else -> throw AssemblyError("weird expression type in assignment")
|
||||
}
|
||||
}
|
||||
@ -53,7 +59,7 @@ internal class AnyExprAsmGen(
|
||||
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
|
||||
@ -61,7 +67,7 @@ internal class AnyExprAsmGen(
|
||||
return true
|
||||
}
|
||||
"-" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
|
||||
@ -76,7 +82,7 @@ internal class AnyExprAsmGen(
|
||||
"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.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
|
||||
@ -84,7 +90,7 @@ internal class AnyExprAsmGen(
|
||||
return true
|
||||
}
|
||||
"|" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
|
||||
@ -92,7 +98,7 @@ internal class AnyExprAsmGen(
|
||||
return true
|
||||
}
|
||||
"^", "xor" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
|
||||
|
@ -39,8 +39,11 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
val asmVarname: String by lazy {
|
||||
if (array == null)
|
||||
variableAsmName!!
|
||||
else
|
||||
asmgen.asmVariableName(array.variable)
|
||||
else {
|
||||
if(array.variable==null)
|
||||
TODO("asmVarname for array with pointer")
|
||||
asmgen.asmVariableName(array.variable!!)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
@ -160,8 +163,11 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
val asmVarname: String
|
||||
get() = if(array==null)
|
||||
variableAsmName!!
|
||||
else
|
||||
asmgen.asmVariableName(array.variable)
|
||||
else {
|
||||
if(array.variable==null)
|
||||
TODO("asmVarname for array with pointer")
|
||||
asmgen.asmVariableName(array.variable!!)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromAstSource(value: PtExpression, program: PtProgram, asmgen: AsmGen6502Internal): AsmAssignSource {
|
||||
@ -203,7 +209,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}")
|
||||
val sub = symbol.astNode as IPtSubroutine
|
||||
val returnType =
|
||||
if(sub is PtSub && sub.returns.size>1)
|
||||
if(sub is PtSub && sub.signature.returns.size>1)
|
||||
DataType.UNDEFINED // TODO list of types instead?
|
||||
else
|
||||
sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
|
||||
|
@ -249,7 +249,12 @@ internal class AssignmentAsmGen(
|
||||
SourceStorageKind.ARRAY -> {
|
||||
val value = assign.source.array!!
|
||||
val elementDt = assign.source.datatype
|
||||
val arrayVarName = asmgen.asmVariableName(value.variable)
|
||||
val valueVar = value.variable
|
||||
if(valueVar==null) {
|
||||
TODO("translate assignmenton pointer ${value.position}")
|
||||
return
|
||||
}
|
||||
val arrayVarName = asmgen.asmVariableName(valueVar)
|
||||
val constIndex = value.index.asConstInteger()
|
||||
|
||||
if(value.splitWords) {
|
||||
@ -438,15 +443,21 @@ internal class AssignmentAsmGen(
|
||||
private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) {
|
||||
when(val value = assign.source.expression!!) {
|
||||
is PtAddressOf -> {
|
||||
val arrayDt = value.identifier.type
|
||||
val sourceName =
|
||||
if(value.isMsbForSplitArray)
|
||||
asmgen.asmSymbolName(value.identifier) + "_msb"
|
||||
else if(arrayDt.isSplitWordArray)
|
||||
asmgen.asmSymbolName(value.identifier) + "_lsb" // the _lsb split array comes first in memory
|
||||
else
|
||||
asmgen.asmSymbolName(value.identifier)
|
||||
assignAddressOf(assign.target, sourceName, value.isMsbForSplitArray, arrayDt, value.arrayIndexExpr)
|
||||
val identifier = value.identifier
|
||||
if(identifier==null) TODO("read &dereference")
|
||||
else {
|
||||
val source = asmgen.symbolTable.lookup(identifier.name)
|
||||
require(source !is StConstant) { "addressOf of a constant should have been rewritten to a simple addition expression" }
|
||||
val arrayDt = identifier.type
|
||||
val sourceName =
|
||||
if (value.isMsbForSplitArray)
|
||||
asmgen.asmSymbolName(identifier) + "_msb"
|
||||
else if (arrayDt.isSplitWordArray)
|
||||
asmgen.asmSymbolName(identifier) + "_lsb" // the _lsb split array comes first in memory
|
||||
else
|
||||
asmgen.asmSymbolName(identifier)
|
||||
assignAddressOf(assign.target, sourceName, value.isMsbForSplitArray, arrayDt, value.arrayIndexExpr)
|
||||
}
|
||||
}
|
||||
is PtBool -> throw AssemblyError("source kind should have been literalboolean")
|
||||
is PtNumber -> throw AssemblyError("source kind should have been literalnumber")
|
||||
@ -604,7 +615,7 @@ internal class AssignmentAsmGen(
|
||||
val symbol = asmgen.symbolTable.lookup(value.name)
|
||||
val sub = symbol!!.astNode as IPtSubroutine
|
||||
asmgen.translateFunctionCall(value)
|
||||
if(sub is PtSub && sub.returns.size>1) {
|
||||
if(sub is PtSub && sub.signature.returns.size>1) {
|
||||
// note: multi-value returns are passed throug A or AY (for the first value) then cx16.R15 down to R0
|
||||
// (this allows unencumbered use of many Rx registers if you don't return that many values)
|
||||
val returnRegs = sub.returnsWhatWhere()
|
||||
@ -920,12 +931,18 @@ internal class AssignmentAsmGen(
|
||||
val tgt = PtAssignTarget(false, assign.target.position)
|
||||
val targetarray = assign.target.array!!
|
||||
val array = PtArrayIndexer(assign.target.datatype, targetarray.position)
|
||||
array.add(targetarray.variable)
|
||||
array.add(targetarray.index)
|
||||
tgt.add(array)
|
||||
assignTrue = PtAssignment(assign.position)
|
||||
assignTrue.add(tgt)
|
||||
assignTrue.add(PtNumber.fromBoolean(true, assign.position))
|
||||
|
||||
val targetArrayVar = targetarray.variable
|
||||
if(targetArrayVar==null) {
|
||||
TODO("optimized comparison on pointer ${targetarray.position}")
|
||||
} else {
|
||||
array.add(targetArrayVar)
|
||||
array.add(targetarray.index)
|
||||
tgt.add(array)
|
||||
assignTrue = PtAssignment(assign.position)
|
||||
assignTrue.add(tgt)
|
||||
assignTrue.add(PtNumber.fromBoolean(true, assign.position))
|
||||
}
|
||||
}
|
||||
TargetStorageKind.REGISTER -> { /* handled earlier */ return true }
|
||||
TargetStorageKind.VOID -> { /* do nothing */ return true }
|
||||
@ -971,7 +988,7 @@ internal class AssignmentAsmGen(
|
||||
if(!directIntoY(expr.right)) asmgen.out(" pha")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
|
||||
if(!directIntoY(expr.right)) asmgen.out(" pla")
|
||||
asmgen.out(" jsr prog8_math.divmod_ub_asm")
|
||||
asmgen.out(" jsr prog8_math.remainder_ub_asm")
|
||||
if(target.register==RegisterOrPair.A)
|
||||
asmgen.out(" cmp #0") // fix the status register
|
||||
else
|
||||
@ -1186,19 +1203,18 @@ internal class AssignmentAsmGen(
|
||||
}
|
||||
} else if(dt.isWord) {
|
||||
if(shifts==7 && expr.operator == "<<") {
|
||||
// optimized shift left 7 (*128) by first swapping the lsb/msb and then doing just one final shift
|
||||
// optimized shift left 7 (*128) by swapping the lsb/msb and then doing just one final shift
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.AY, signed)
|
||||
asmgen.out("""
|
||||
; shift left 7
|
||||
sty P8ZP_SCRATCH_REG ; msb
|
||||
sty P8ZP_SCRATCH_REG
|
||||
lsr P8ZP_SCRATCH_REG
|
||||
php ; save carry
|
||||
ror a
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda #0
|
||||
plp ; restore carry
|
||||
ror P8ZP_SCRATCH_REG
|
||||
ror a
|
||||
ldy P8ZP_SCRATCH_REG""")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -1311,25 +1327,35 @@ internal class AssignmentAsmGen(
|
||||
val rightArrayIndexer = expr.right as? PtArrayIndexer
|
||||
if(expr.operator=="+" && leftArrayIndexer!=null && leftArrayIndexer.type.isByte && right.type.isByte) {
|
||||
// special optimization for bytearray[y] + bytevalue : no need to use a tempvar, just use adc array,y
|
||||
assignExpressionToRegister(right, RegisterOrPair.A, right.type.isSigned)
|
||||
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.isSigned, true)
|
||||
val leftArrayVar = leftArrayIndexer.variable
|
||||
if(leftArrayVar==null) {
|
||||
TODO("optimized plusmin pointer ${leftArrayIndexer.position}")
|
||||
} else {
|
||||
assignExpressionToRegister(right, RegisterOrPair.A, right.type.isSigned)
|
||||
if (!leftArrayIndexer.index.isSimple()) asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(leftArrayIndexer.index, RegisterOrPair.Y)
|
||||
if (!leftArrayIndexer.index.isSimple()) asmgen.out(" pla")
|
||||
val arrayvarname = asmgen.asmSymbolName(leftArrayVar)
|
||||
asmgen.out(" clc | adc $arrayvarname,y")
|
||||
assignRegisterByte(target, CpuRegister.A, dt.isSigned, true)
|
||||
}
|
||||
} else if(rightArrayIndexer!=null && rightArrayIndexer.type.isByte && left.type.isByte) {
|
||||
// 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.isSigned)
|
||||
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y, false)
|
||||
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pla")
|
||||
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.isSigned, true)
|
||||
val rightArrayVar = rightArrayIndexer.variable
|
||||
if(rightArrayVar==null) {
|
||||
TODO("optimized plusmin pointer ${rightArrayIndexer.position}")
|
||||
} else {
|
||||
assignExpressionToRegister(left, RegisterOrPair.A, left.type.isSigned)
|
||||
if (!rightArrayIndexer.index.isSimple()) asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y)
|
||||
if (!rightArrayIndexer.index.isSimple()) asmgen.out(" pla")
|
||||
val arrayvarname = asmgen.asmSymbolName(rightArrayVar)
|
||||
if (expr.operator == "+")
|
||||
asmgen.out(" clc | adc $arrayvarname,y")
|
||||
else
|
||||
asmgen.out(" sec | sbc $arrayvarname,y")
|
||||
assignRegisterByte(target, CpuRegister.A, dt.isSigned, true)
|
||||
}
|
||||
} else if(expr.operator=="+" && leftMemByte!=null && right.type.isByte && optimizedPointerIndexPlusMinusByteIntoA(right, "+", leftMemByte)) {
|
||||
assignRegisterByte(target, CpuRegister.A, dt.isSigned, true)
|
||||
return true
|
||||
@ -1355,7 +1381,7 @@ internal class AssignmentAsmGen(
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else if(dt.isWord) {
|
||||
} else if(dt.isWord || dt.isPointer) {
|
||||
|
||||
fun doAddOrSubWordExpr() {
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
|
||||
@ -1382,11 +1408,13 @@ internal class AssignmentAsmGen(
|
||||
|
||||
when (right) {
|
||||
is PtAddressOf -> {
|
||||
var symbol = asmgen.asmVariableName(right.identifier)
|
||||
if(right.isFromArrayElement) {
|
||||
TODO("address-of array element $symbol at ${right.position}")
|
||||
TODO("address-of array element at ${right.position}")
|
||||
} else if(right.dereference!=null) {
|
||||
TODO("read &dereference")
|
||||
} else {
|
||||
if(right.identifier.type.isSplitWordArray) {
|
||||
var symbol = asmgen.asmVariableName(right.identifier!!)
|
||||
if(right.identifier!!.type.isSplitWordArray) {
|
||||
symbol = if(right.isMsbForSplitArray) symbol+"_msb" else symbol+"_lsb"
|
||||
}
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY, dt.isSigned)
|
||||
@ -1567,8 +1595,8 @@ internal class AssignmentAsmGen(
|
||||
if(ptrVar!=null && asmgen.isZpVar(ptrVar)) {
|
||||
assignExpressionToRegister(value, RegisterOrPair.A, false)
|
||||
val pointername = asmgen.asmVariableName(ptrVar)
|
||||
if (constOffset != null && constOffset < 256) {
|
||||
// we have value + @(zpptr + 255), or value - @(zpptr+255)
|
||||
if (constOffset != null) {
|
||||
// we have value + @(zpptr + 255), or value - @(zpptr+255). the offset is always <256.
|
||||
asmgen.out(" ldy #$constOffset")
|
||||
if (operator == "+")
|
||||
asmgen.out(" clc | adc ($pointername),y")
|
||||
@ -1613,10 +1641,16 @@ internal class AssignmentAsmGen(
|
||||
}
|
||||
val rightArray = expr.right as? PtArrayIndexer
|
||||
if(rightArray!=null) {
|
||||
val rightArrayVar = rightArray.variable
|
||||
if(rightArrayVar==null) {
|
||||
TODO("optimized bitwise pointer ${rightArray.position}")
|
||||
return false
|
||||
}
|
||||
|
||||
val constIndex = rightArray.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
val valueVarname = "${asmgen.asmSymbolName(rightArray.variable)} + ${program.memsizer.memorySize(rightArray.type, constIndex)}"
|
||||
val valueVarname = "${asmgen.asmSymbolName(rightArrayVar)} + ${program.memsizer.memorySize(rightArray.type, constIndex)}"
|
||||
when(expr.operator) {
|
||||
"&" -> asmgen.out(" and $valueVarname")
|
||||
"|" -> asmgen.out(" ora $valueVarname")
|
||||
@ -1725,12 +1759,17 @@ internal class AssignmentAsmGen(
|
||||
is PtArrayIndexer -> {
|
||||
val constIndex = right.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
val valueVarname = "${asmgen.asmSymbolName(right.variable)} + ${program.memsizer.memorySize(right.type, constIndex)}"
|
||||
when(operator) {
|
||||
"and" -> asmgen.out(" and $valueVarname")
|
||||
"or" -> asmgen.out(" ora $valueVarname")
|
||||
"xor" -> asmgen.out(" eor $valueVarname")
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
val rightArrayVar = right.variable
|
||||
if(rightArrayVar==null) {
|
||||
TODO("assign result into A pointer ${right.position}")
|
||||
} else {
|
||||
val valueVarname = "${asmgen.asmSymbolName(rightArrayVar)} + ${program.memsizer.memorySize(right.type, constIndex)}"
|
||||
when(operator) {
|
||||
"and" -> asmgen.out(" and $valueVarname")
|
||||
"or" -> asmgen.out(" ora $valueVarname")
|
||||
"xor" -> asmgen.out(" eor $valueVarname")
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
}
|
||||
}
|
||||
}
|
||||
else assignViaScratch()
|
||||
@ -1739,6 +1778,17 @@ internal class AssignmentAsmGen(
|
||||
}
|
||||
}
|
||||
|
||||
fun requiresCmp(expr: PtExpression) =
|
||||
when (expr) {
|
||||
is PtFunctionCall -> {
|
||||
val function = asmgen.symbolTable.lookup(expr.name)
|
||||
function is StExtSub // don't assume the extsub/asmsub has set the cpu flags correctly on exit, add an explicit cmp
|
||||
}
|
||||
is PtBuiltinFunctionCall -> true
|
||||
is PtIfExpression -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
if(!expr.right.isSimple() && expr.operator!="xor") {
|
||||
// shortcircuit evaluation into A
|
||||
val shortcutLabel = asmgen.makeLabel("shortcut")
|
||||
@ -1746,15 +1796,23 @@ internal class AssignmentAsmGen(
|
||||
"and" -> {
|
||||
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
if(requiresCmp(expr.left))
|
||||
asmgen.out(" cmp #0")
|
||||
asmgen.out(" beq $shortcutLabel")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
|
||||
if(requiresCmp(expr.right))
|
||||
asmgen.out(" cmp #0")
|
||||
asmgen.out(shortcutLabel)
|
||||
}
|
||||
"or" -> {
|
||||
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
if(requiresCmp(expr.left))
|
||||
asmgen.out(" cmp #0")
|
||||
asmgen.out(" bne $shortcutLabel")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
|
||||
if(requiresCmp(expr.right))
|
||||
asmgen.out(" cmp #0")
|
||||
asmgen.out(shortcutLabel)
|
||||
}
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
@ -1960,7 +2018,7 @@ $endLabel""")
|
||||
val (dt, numElements) = when(symbol) {
|
||||
is StStaticVariable -> symbol.dt to symbol.length!!
|
||||
is StMemVar -> symbol.dt to symbol.length!!
|
||||
else -> DataType.UNDEFINED to 0
|
||||
else -> DataType.UNDEFINED to 0u
|
||||
}
|
||||
when {
|
||||
dt.isString -> {
|
||||
@ -1968,7 +2026,7 @@ $endLabel""")
|
||||
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, false, null, null)
|
||||
asmgen.out(" pla")
|
||||
asmgen.out(" ldy #${numElements-1}")
|
||||
asmgen.out(" ldy #${numElements-1u}")
|
||||
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||
}
|
||||
dt.isFloatArray -> {
|
||||
@ -2123,7 +2181,7 @@ $endLabel""")
|
||||
assignExpressionToRegister(value, RegisterOrPair.A, valueDt.isSigned)
|
||||
assignTypeCastedRegisters(target.asmVarname, targetDt.base, RegisterOrPair.A, valueDt.base)
|
||||
}
|
||||
valueDt.isWord -> {
|
||||
valueDt.isWord || valueDt.isPointer -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.AY, valueDt.isSigned)
|
||||
assignTypeCastedRegisters(target.asmVarname, targetDt.base, RegisterOrPair.AY, valueDt.base)
|
||||
}
|
||||
@ -2276,6 +2334,12 @@ $endLabel""")
|
||||
return
|
||||
}
|
||||
|
||||
if(targetDt.isUnsignedWord && valueDt.isPointer) {
|
||||
assignExpressionToRegister(value, RegisterOrPair.AY, false)
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return
|
||||
}
|
||||
|
||||
// No more special optimized cases yet. Do the rest via more complex evaluation
|
||||
// note: cannot use assignTypeCastedValue because that is ourselves :P
|
||||
// NOTE: THIS MAY TURN INTO A STACK OVERFLOW ERROR IF IT CAN'T SIMPLIFY THE TYPECAST..... :-/
|
||||
@ -2601,58 +2665,118 @@ $endLabel""")
|
||||
}
|
||||
}
|
||||
BaseDataType.STR -> throw AssemblyError("cannot typecast a string value")
|
||||
BaseDataType.POINTER -> {
|
||||
if(targetDt.isWord || targetDt.isPointer) {
|
||||
when(regs) {
|
||||
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
|
||||
RegisterOrPair.AY -> asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
|
||||
RegisterOrPair.XY -> asmgen.out(" stx $targetAsmVarName | sty $targetAsmVarName+1")
|
||||
else -> throw AssemblyError("non-word regs")
|
||||
}
|
||||
} else {
|
||||
throw AssemblyError("cannot assign pointer to $targetDt")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignAddressOf(target: AsmAssignTarget, sourceName: String, msb: Boolean, arrayDt: DataType?, arrayIndexExpr: PtExpression?) {
|
||||
if(arrayIndexExpr!=null) {
|
||||
val arrayName = if(arrayDt!!.isSplitWordArray) sourceName+"_lsb" else sourceName // the _lsb split array comes first in memory
|
||||
val constIndex = arrayIndexExpr.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
if (arrayDt.isUnsignedWord) {
|
||||
if (arrayDt!!.isUnsignedWord) {
|
||||
// using a UWORD pointer with array indexing, always bytes
|
||||
require(!msb)
|
||||
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
|
||||
if(constIndex>0)
|
||||
if(constIndex in 1..255)
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc #$constIndex
|
||||
bcc +
|
||||
iny
|
||||
+""")
|
||||
else if(constIndex>=256) {
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc #<$constIndex
|
||||
pha
|
||||
tya
|
||||
adc #>$constIndex
|
||||
tay
|
||||
pla""")
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(constIndex>0) {
|
||||
val offset = if(arrayDt.isSplitWordArray) constIndex else program.memsizer.memorySize(arrayDt, constIndex) // add arrayIndexExpr * elementsize to the address of the array variable.
|
||||
asmgen.out(" lda #<($arrayName + $offset) | ldy #>($arrayName + $offset)")
|
||||
asmgen.out(" lda #<($sourceName + $offset) | ldy #>($sourceName + $offset)")
|
||||
} else {
|
||||
asmgen.out(" lda #<$arrayName | ldy #>$arrayName")
|
||||
asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
||||
}
|
||||
}
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return
|
||||
} else {
|
||||
if (arrayDt.isUnsignedWord) {
|
||||
if (arrayDt!!.isUnsignedWord) {
|
||||
// using a UWORD pointer with array indexing, always bytes
|
||||
require(!msb)
|
||||
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
assignExpressionToVariable(arrayIndexExpr, "P8ZP_SCRATCH_REG", DataType.UBYTE)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc P8ZP_SCRATCH_REG
|
||||
bcc +
|
||||
iny
|
||||
if(arrayIndexExpr.type.isWord) {
|
||||
assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.AY, false)
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
tay
|
||||
pla
|
||||
clc
|
||||
adc P8ZP_SCRATCH_W1
|
||||
pha
|
||||
tya
|
||||
adc P8ZP_SCRATCH_W1+1
|
||||
tay
|
||||
pla""")
|
||||
}
|
||||
else {
|
||||
assignExpressionToVariable(arrayIndexExpr, "P8ZP_SCRATCH_REG", DataType.UBYTE)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc P8ZP_SCRATCH_REG
|
||||
bcc +
|
||||
iny
|
||||
+""")
|
||||
}
|
||||
}
|
||||
else {
|
||||
assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.A, false)
|
||||
val subtype = arrayDt.sub!!
|
||||
if(subtype.isByteOrBool) {
|
||||
// elt size 1, we're good
|
||||
} else if(subtype.isWord) {
|
||||
if(!arrayDt.isSplitWordArray) {
|
||||
// elt size 2
|
||||
asmgen.out(" asl a")
|
||||
}
|
||||
} else if(subtype==BaseDataType.FLOAT) {
|
||||
if(asmgen.options.compTarget.FLOAT_MEM_SIZE != 5)
|
||||
TODO("support float size other than 5 ${arrayIndexExpr.position}")
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc P8ZP_SCRATCH_REG"""
|
||||
)
|
||||
} else throw AssemblyError("weird type $subtype")
|
||||
asmgen.out("""
|
||||
ldy #>$arrayName
|
||||
ldy #>$sourceName
|
||||
clc
|
||||
adc #<$arrayName
|
||||
adc #<$sourceName
|
||||
bcc +
|
||||
iny
|
||||
+""")
|
||||
@ -2855,7 +2979,7 @@ $endLabel""")
|
||||
jsr floats.MOVMF""")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
ldy #<${target.asmVarname}
|
||||
sty P8ZP_SCRATCH_W1
|
||||
@ -2887,7 +3011,7 @@ $endLabel""")
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.out(" pha")
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.out(" pla")
|
||||
asmgen.out("""
|
||||
@ -2924,7 +3048,7 @@ $endLabel""")
|
||||
jsr floats.copy_float""")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
ldy #<$sourceName
|
||||
sty P8ZP_SCRATCH_W1
|
||||
@ -3397,7 +3521,7 @@ $endLabel""")
|
||||
} else {
|
||||
require(target.array.index.type.isByteOrBool)
|
||||
asmgen.saveRegisterStack(register, false)
|
||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
|
||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y)
|
||||
asmgen.out(" pla | sta ${target.asmVarname},y")
|
||||
}
|
||||
}
|
||||
@ -3712,7 +3836,7 @@ $endLabel""")
|
||||
asmgen.out(" stz ${target.asmVarname}+$indexValue")
|
||||
}
|
||||
else {
|
||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.X, false)
|
||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.X)
|
||||
asmgen.out(" stz ${target.asmVarname},x")
|
||||
}
|
||||
}
|
||||
@ -3802,7 +3926,7 @@ $endLabel""")
|
||||
sta ${target.asmVarname}+4""")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
ldy #<${target.asmVarname}
|
||||
sty P8ZP_SCRATCH_W1
|
||||
@ -3836,7 +3960,7 @@ $endLabel""")
|
||||
jsr floats.copy_float""")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
ldy #<${constFloat}
|
||||
sty P8ZP_SCRATCH_W1
|
||||
@ -4019,8 +4143,10 @@ $endLabel""")
|
||||
addressOf != null -> {
|
||||
if(addressOf.isFromArrayElement) {
|
||||
TODO("address-of array element $addressOf")
|
||||
} else if(addressOf.dereference!=null) {
|
||||
throw AssemblyError("write &dereference, makes no sense at ${addressOf.position}")
|
||||
} else {
|
||||
asmgen.out(" sta ${asmgen.asmSymbolName(addressOf.identifier)}")
|
||||
asmgen.out(" sta ${asmgen.asmSymbolName(addressOf.identifier!!)}")
|
||||
}
|
||||
}
|
||||
addressExpr is PtIdentifier -> {
|
||||
|
@ -170,7 +170,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
if(memory.address is PtBinaryExpression && tryOptimizedMemoryInplace(memory.address as PtBinaryExpression, operator, value))
|
||||
return
|
||||
// slower method to calculate and use the pointer to access the memory with:
|
||||
asmgen.assignExpressionToRegister(memory.address, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(memory.address, RegisterOrPair.AY)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, true)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
@ -205,7 +205,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" ldx P8ZP_SCRATCH_B1")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
val tempVar = asmgen.getTempVarName(BaseDataType.UBYTE)
|
||||
val tempVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, memory)
|
||||
asmgen.out(" sta $tempVar")
|
||||
if(value.expression is PtTypeCast)
|
||||
inplacemodificationByteWithValue(tempVar, DataType.UBYTE, operator, value.expression)
|
||||
@ -224,7 +224,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
val indexNum = target.array!!.index as? PtNumber
|
||||
val targetArrayVar = target.array!!.variable
|
||||
if(targetArrayVar==null) {
|
||||
TODO("array indexing on pointer ${target.position}")
|
||||
return
|
||||
}
|
||||
val indexNum = target.array.index as? PtNumber
|
||||
if (indexNum!=null) {
|
||||
val index = indexNum.number.toInt()
|
||||
if(target.array.splitWords) {
|
||||
@ -320,7 +325,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
return
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.out(" lda ${target.array.variable.name},y")
|
||||
asmgen.out(" lda ${targetArrayVar.name},y")
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> {
|
||||
inplacemodificationRegisterAwithVariable(operator, "#${value.boolean!!.asInt()}", target.datatype.isSigned)
|
||||
@ -356,7 +361,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
val tempVar = asmgen.getTempVarName(BaseDataType.UBYTE)
|
||||
val tempVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, target.array)
|
||||
asmgen.out(" sta $tempVar")
|
||||
if(value.expression is PtTypeCast)
|
||||
inplacemodificationByteWithValue(tempVar, target.datatype, operator, value.expression)
|
||||
@ -366,7 +371,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" lda $tempVar")
|
||||
}
|
||||
}
|
||||
asmgen.out(" sta ${target.array.variable.name},y")
|
||||
asmgen.out(" sta ${targetArrayVar.name},y")
|
||||
}
|
||||
|
||||
target.datatype.isWord -> {
|
||||
@ -377,11 +382,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
if(target.array.splitWords) {
|
||||
asmgen.out(" lda ${target.array.variable.name}_lsb,y")
|
||||
asmgen.out(" ldx ${target.array.variable.name}_msb,y")
|
||||
asmgen.out(" lda ${targetArrayVar.name}_lsb,y")
|
||||
asmgen.out(" ldx ${targetArrayVar.name}_msb,y")
|
||||
} else {
|
||||
asmgen.out(" lda ${target.array.variable.name},y")
|
||||
asmgen.out(" ldx ${target.array.variable.name}+1,y")
|
||||
asmgen.out(" lda ${targetArrayVar.name},y")
|
||||
asmgen.out(" ldx ${targetArrayVar.name}+1,y")
|
||||
}
|
||||
val block = target.origAstTarget?.definingBlock()
|
||||
when(value.kind) {
|
||||
@ -439,7 +444,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
val tempVar = asmgen.getTempVarName(BaseDataType.UWORD)
|
||||
val tempVar = asmgen.createTempVarReused(BaseDataType.UWORD, false, target.array)
|
||||
asmgen.out(" sta $tempVar | stx $tempVar+1")
|
||||
if(value.expression is PtTypeCast)
|
||||
inplacemodificationWordWithValue(tempVar, target.datatype, operator, value.expression, block)
|
||||
@ -450,14 +455,14 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, true)
|
||||
if(target.array.splitWords)
|
||||
asmgen.out(" sta ${target.array.variable.name}_lsb,y | txa | sta ${target.array.variable.name}_msb,y")
|
||||
asmgen.out(" sta ${targetArrayVar.name}_lsb,y | txa | sta ${targetArrayVar.name}_msb,y")
|
||||
else
|
||||
asmgen.out(" sta ${target.array.variable.name},y | txa | sta ${target.array.variable.name}+1,y")
|
||||
asmgen.out(" sta ${targetArrayVar.name},y | txa | sta ${targetArrayVar.name}+1,y")
|
||||
}
|
||||
|
||||
target.datatype.isFloat -> {
|
||||
// copy array value into tempvar
|
||||
val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT)
|
||||
val tempvar = asmgen.createTempVarReused(BaseDataType.FLOAT, false, target.array)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.A)
|
||||
asmgen.out("""
|
||||
ldy #>${target.asmVarname}
|
||||
@ -515,7 +520,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun tryIndexedIncDec(array: PtArrayIndexer, operator: String): Boolean {
|
||||
val arrayvar = asmgen.asmVariableName(array.variable)
|
||||
val arrayVar = array.variable
|
||||
if(arrayVar==null) {
|
||||
TODO("indexed inc/dec on pointer ${array.position}")
|
||||
return false
|
||||
}
|
||||
val arrayvar = asmgen.asmVariableName(arrayVar)
|
||||
when {
|
||||
array.type.isByte -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(array, CpuRegister.X)
|
||||
@ -583,7 +593,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
if(address.left is PtIdentifier && asmgen.isZpVar(address.left as PtIdentifier)) {
|
||||
return (address.left as PtIdentifier).name
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY)
|
||||
asmgen.out(" sta P8ZP_SCRATCH_W2 | sty P8ZP_SCRATCH_W2+1")
|
||||
return "P8ZP_SCRATCH_W2"
|
||||
}
|
||||
@ -635,9 +645,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
return true
|
||||
}
|
||||
if(rightTc!=null)
|
||||
asmgen.assignExpressionToRegister(rightTc.value, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(rightTc.value, RegisterOrPair.A)
|
||||
else
|
||||
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.A)
|
||||
asmgen.out(" pha") // offset on stack
|
||||
val zpPointerVarName = addrIntoZpPointer()
|
||||
assignValueToA()
|
||||
@ -910,7 +920,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
"-" -> asmgen.out(" sec | sbc $otherName")
|
||||
"*" -> asmgen.out(" ldy $otherName | jsr prog8_math.multiply_bytes")
|
||||
"/" -> asmgen.out(" ldy $otherName | jsr prog8_math.divmod_ub_asm | tya")
|
||||
"%" -> asmgen.out(" ldy $otherName | jsr prog8_math.divmod_ub_asm")
|
||||
"%" -> asmgen.out(" ldy $otherName | jsr prog8_math.remainder_ub_asm")
|
||||
"<<" -> {
|
||||
asmgen.out("""
|
||||
ldy $otherName
|
||||
@ -1019,7 +1029,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(value==0)
|
||||
throw AssemblyError("division by zero")
|
||||
asmgen.out(" ldy #$value | jsr prog8_math.divmod_ub_asm")
|
||||
asmgen.out(" ldy #$value | jsr prog8_math.remainder_ub_asm")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"<<" -> {
|
||||
@ -1125,11 +1135,18 @@ $shortcutLabel:""")
|
||||
}
|
||||
|
||||
if(value is PtArrayIndexer && value.isSimple()) {
|
||||
|
||||
val valueVar = value.variable
|
||||
if(valueVar==null) {
|
||||
TODO("inplace modification on pointer ${value.position}")
|
||||
return
|
||||
}
|
||||
|
||||
// use the already existing optimized codegen for regular assignments x += array[index]
|
||||
val binexpr = PtBinaryExpression(operator, dt, value.position)
|
||||
binexpr.add(PtIdentifier(name, dt, value.position))
|
||||
val arrayValue = PtArrayIndexer(value.type, value.position)
|
||||
arrayValue.add(value.variable)
|
||||
arrayValue.add(valueVar)
|
||||
arrayValue.add(value.index)
|
||||
binexpr.add(arrayValue)
|
||||
binexpr.parent = value
|
||||
@ -1190,7 +1207,7 @@ $shortcutLabel:""")
|
||||
"%" -> {
|
||||
if(signed)
|
||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||
asmgen.out(" ldy $variable | jsr prog8_math.divmod_ub_asm")
|
||||
asmgen.out(" ldy $variable | jsr prog8_math.remainder_ub_asm")
|
||||
}
|
||||
"<<" -> {
|
||||
asmgen.out("""
|
||||
@ -1363,7 +1380,7 @@ $shortcutLabel:""")
|
||||
"%" -> {
|
||||
if(signed)
|
||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||
asmgen.out(" tay | lda $variable | jsr prog8_math.divmod_ub_asm")
|
||||
asmgen.out(" tay | lda $variable | jsr prog8_math.remainder_ub_asm")
|
||||
}
|
||||
"<<" -> {
|
||||
asmgen.out("""
|
||||
@ -1534,7 +1551,7 @@ $shortcutLabel:""")
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
ldy #$value
|
||||
jsr prog8_math.divmod_ub_asm
|
||||
jsr prog8_math.remainder_ub_asm
|
||||
sta $name""")
|
||||
}
|
||||
"<<" -> {
|
||||
@ -1921,18 +1938,16 @@ $shortcutLabel:""")
|
||||
asmgen.out(" lda #0 | sta $lsb")
|
||||
}
|
||||
value==7 -> {
|
||||
// optimized shift left 7 (*128) by first swapping the lsb/msb and then doing just one final shift
|
||||
// optimized shift left 7 (*128) by swapping the lsb/msb and then doing just one final shift
|
||||
asmgen.out("""
|
||||
; shift left 7
|
||||
lsr $msb
|
||||
php ; save carry
|
||||
lda $lsb
|
||||
ror a
|
||||
sta $msb
|
||||
lda #0
|
||||
sta $lsb
|
||||
plp ; restore carry
|
||||
ror $msb
|
||||
ror $lsb""")
|
||||
ror a
|
||||
sta $lsb""")
|
||||
}
|
||||
value>3 -> asmgen.out("""
|
||||
ldy #$value
|
||||
@ -2468,7 +2483,7 @@ $shortcutLabel:""")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
valueDt.isWord -> {
|
||||
valueDt.isWord || valueDt.isPointer -> {
|
||||
// the value is a proper 16-bit word, so use both bytes of it.
|
||||
when (operator) {
|
||||
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
|
||||
@ -2740,7 +2755,7 @@ $shortcutLabel:""")
|
||||
"+" -> {
|
||||
// name += byteexpression
|
||||
if(valueDt.isUnsignedByte) {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc $name
|
||||
@ -2890,7 +2905,7 @@ $shortcutLabel:""")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
valueDt.isWord -> {
|
||||
valueDt.isWord || valueDt.isPointer -> {
|
||||
// the value is a proper 16-bit word, so use both bytes of it.
|
||||
|
||||
if(value is PtArrayIndexer && value.isSimple()) {
|
||||
@ -2905,7 +2920,12 @@ $shortcutLabel:""")
|
||||
"-" -> {
|
||||
if(value.index.type.isByte) {
|
||||
// it's an array indexed by a byte so we can use sbc array,y
|
||||
val arrayname = value.variable.name
|
||||
val valueVar = value.variable
|
||||
if(valueVar==null) {
|
||||
TODO("inplace modification on pointer ${value.position}")
|
||||
return
|
||||
}
|
||||
val arrayname = valueVar.name
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
if(value.splitWords) {
|
||||
asmgen.out("""
|
||||
|
@ -5,7 +5,9 @@ import prog8.code.core.*
|
||||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!!
|
||||
else if(dt.isArray) {
|
||||
require(numElements != null)
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
@ -68,7 +70,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||
override fun print_single_error(errormessage: String) { /* prints nothing in tests */ }
|
||||
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
|
@ -58,6 +58,7 @@ class TestCodegen: FunSpec({
|
||||
DataType.UBYTE,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
@ -67,6 +68,7 @@ class TestCodegen: FunSpec({
|
||||
DataType.arrayFor(BaseDataType.UBYTE),
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
3u,
|
||||
Position.DUMMY
|
||||
@ -76,6 +78,7 @@ class TestCodegen: FunSpec({
|
||||
DataType.arrayFor(BaseDataType.UBYTE),
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
3u,
|
||||
Position.DUMMY
|
||||
@ -85,6 +88,7 @@ class TestCodegen: FunSpec({
|
||||
DataType.WORD,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
|
@ -1,10 +1,10 @@
|
||||
package prog8.codegen.experimental
|
||||
|
||||
import prog8.code.IAssemblyProgram
|
||||
import prog8.code.ICodeGeneratorBackend
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.IAssemblyProgram
|
||||
import prog8.code.ICodeGeneratorBackend
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.codegen.intermediate.IRCodeGen
|
||||
import prog8.intermediate.IRFileWriter
|
||||
@ -26,7 +26,8 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
||||
// this stub only writes the IR program to disk but doesn't generate anything else.
|
||||
IRFileWriter(irProgram, null).write()
|
||||
|
||||
println("** experimental codegen stub: no assembly generated **")
|
||||
if(!options.quiet)
|
||||
println("** experimental codegen stub: no assembly generated **")
|
||||
return EmptyProgram
|
||||
}
|
||||
}
|
||||
@ -34,7 +35,8 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
||||
private object EmptyProgram : IAssemblyProgram {
|
||||
override val name = "<Empty Program>"
|
||||
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||
println("** nothing assembled **")
|
||||
if(!options.quiet)
|
||||
println("** nothing assembled **")
|
||||
return true
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,10 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.DataType
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
@ -45,6 +47,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
||||
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
|
||||
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
||||
"structalloc" -> funcStructAlloc(call)
|
||||
"sizeof" -> throw AssemblyError("sizeof must have been replaced with a constant")
|
||||
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
||||
}
|
||||
}
|
||||
@ -78,7 +82,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val bankTr = exprGen.translateExpression(call.args[0])
|
||||
val addressTr = exprGen.translateExpression(call.args[1])
|
||||
val argumentwordTr = exprGen.translateExpression(call.args[2])
|
||||
@ -91,7 +94,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcCallfar2(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val bankTr = exprGen.translateExpression(call.args[0])
|
||||
val addressTr = exprGen.translateExpression(call.args[1])
|
||||
val argumentA = exprGen.translateExpression(call.args[2])
|
||||
@ -143,7 +145,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val left = exprGen.translateExpression(call.args[0])
|
||||
val right = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, left, left.resultReg, -1)
|
||||
@ -240,11 +241,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
BaseDataType.UWORD -> {
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
val resultReg = codeGen.registers.next(IRDataType.BYTE) // sqrt of a word still produces just a byte result
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
@ -278,7 +279,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val type = irType(call.type)
|
||||
val valueTr = exprGen.translateExpression(call.args[0])
|
||||
val minimumTr = exprGen.translateExpression(call.args[1])
|
||||
@ -497,8 +497,16 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val name = (call.args[0] as PtString).value
|
||||
val code = IRCodeChunk(null, null)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
|
||||
return ExpressionCodeResult(code, IRDataType.BYTE, resultReg, -1)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "$StMemorySlabPrefix.prog8_memoryslab_$name")
|
||||
return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcStructAlloc(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val code = IRCodeChunk(null, null)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
val labelname = SymbolTable.labelnameForStructInstance(call)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = labelname)
|
||||
return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
@ -548,7 +556,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val arr = (arg as? PtArrayIndexer)
|
||||
val index = arr?.index?.asConstInteger()
|
||||
if(arr!=null && index!=null) {
|
||||
val variable = arr.variable.name
|
||||
if(arr.variable==null)
|
||||
TODO("support for ptr indexing ${arr.position}")
|
||||
val variable = arr.variable!!.name
|
||||
if(arr.splitWords) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
when(opcodeMemAndReg.first) {
|
||||
@ -617,7 +627,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
if(target.splitWords) {
|
||||
// lsb/msb in split arrays, element index 'size' is always 1
|
||||
val constIndex = target.index.asConstInteger()
|
||||
val varName = target.variable.name + if(msb) "_msb" else "_lsb"
|
||||
if(target.variable==null)
|
||||
TODO("support for ptr indexing ${target.position}")
|
||||
val varName = target.variable!!.name + if(msb) "_msb" else "_lsb"
|
||||
if(isConstZeroValue) {
|
||||
if(constIndex!=null) {
|
||||
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
@ -651,6 +663,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
}
|
||||
else {
|
||||
val targetVariable = target.variable
|
||||
if(targetVariable==null)
|
||||
TODO("support for ptr indexing ${target.position}")
|
||||
|
||||
val eltSize = codeGen.program.memsizer.memorySize(target.type, null)
|
||||
val constIndex = target.index.asConstInteger()
|
||||
if(isConstZeroValue) {
|
||||
@ -659,17 +675,17 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val offset = eltSize*constIndex + if(msb) 1 else 0
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = target.variable.name)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = targetVariable.name)
|
||||
}
|
||||
} else {
|
||||
val indexTr = exprGen.translateExpression(target.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(eltSize>1)
|
||||
it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
|
||||
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
|
||||
if(msb)
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = target.variable.name)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = targetVariable.name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -680,17 +696,17 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val offset = eltSize*constIndex + if(msb) 1 else 0
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = target.variable.name)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = targetVariable.name)
|
||||
}
|
||||
} else {
|
||||
val indexTr = exprGen.translateExpression(target.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(eltSize>1)
|
||||
it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
|
||||
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
|
||||
if(msb)
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = target.variable.name)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = targetVariable.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,8 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.StExtSub
|
||||
import prog8.code.StNode
|
||||
import prog8.code.StNodeType
|
||||
import prog8.code.StSub
|
||||
import prog8.code.*
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.Cx16VirtualRegisters
|
||||
import prog8.code.core.Statusflag
|
||||
import prog8.code.core.*
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
@ -92,12 +86,107 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
is PtBuiltinFunctionCall -> codeGen.translateBuiltinFunc(expr)
|
||||
is PtFunctionCall -> translate(expr)
|
||||
is PtContainmentCheck -> translate(expr)
|
||||
is PtPointerDeref -> translate(expr)
|
||||
is PtRange,
|
||||
is PtArray,
|
||||
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(deref: PtPointerDeref): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
var pointerReg: Int
|
||||
|
||||
if(deref.startpointer.type.isStructInstance) {
|
||||
TODO("translate structinstance deref???")
|
||||
/*
|
||||
val arrayIndexer = deref.startpointer as? PtArrayIndexer
|
||||
if(arrayIndexer==null)
|
||||
throw AssemblyError("when start pointer is struct instance, array indexer is expected PTR[x]")
|
||||
// first evaluate the adress of POINTER[x].a
|
||||
// which is: value in pointer + x*sizeof(struct) + offsetof(a)
|
||||
// then use traverseDerefChainToCalculateFinalAddress on b.c.d.field
|
||||
val struct = deref.startpointer.type.subType as StStruct
|
||||
val chain = ArrayDeque(deref.chain)
|
||||
val firstField = struct.getField(chain.removeFirst(), codeGen.program.memsizer)
|
||||
val pointerTr = translateExpression(arrayIndexer.variable)
|
||||
result += pointerTr.chunks
|
||||
pointerReg = pointerTr.resultReg
|
||||
val constIndex = arrayIndexer.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
val offset = constIndex * struct.size.toInt()
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = offset), null)
|
||||
} else {
|
||||
val indexTr = translateExpression(arrayIndexer.index)
|
||||
result += indexTr.chunks
|
||||
// multiply the index by the size of the struct and add that to the pointer, then add the offset of the field,
|
||||
// and retrieve the pointer value that is stored there, or the actual value if it's a pointer to simple type
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val indexReg: Int
|
||||
if(arrayIndexer.index.type.isByte) {
|
||||
// extend array index to word
|
||||
indexReg = codeGen.registers.next(IRDataType.WORD)
|
||||
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, indexReg, indexTr.resultReg)
|
||||
} else {
|
||||
indexReg = indexTr.resultReg
|
||||
}
|
||||
it += codeGen.multiplyByConst(DataType.UWORD, indexReg, struct.size.toInt())
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = pointerReg, reg2 = indexReg)
|
||||
}
|
||||
}
|
||||
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = firstField.second.toInt())
|
||||
if (firstField.first.isPointer) {
|
||||
// get the address stored in the pointer and use that for the rest of the chain
|
||||
// LOADI has an exception to allo reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
|
||||
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg)
|
||||
} else {
|
||||
require(chain.isEmpty())
|
||||
// it's a pointer to a simple value, so keep the pointer as-is
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// now use traverseDerefChainToCalculateFinalAddress on b.c.d and finally field or on b.c.d.field if field isn't a field.
|
||||
val derefField = if(deref.type.isPointer) null else chain.removeLastOrNull()
|
||||
actualDeref = PtPointerDeref(deref.type, chain, derefField, deref.position)
|
||||
actualDeref.add(arrayIndexer.variable)
|
||||
*/
|
||||
} else {
|
||||
val tr = translateExpression(deref.startpointer)
|
||||
result += tr.chunks
|
||||
pointerReg = tr.resultReg
|
||||
}
|
||||
|
||||
val (instructions, offset) = traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerReg)
|
||||
result += instructions
|
||||
if(offset==0u) {
|
||||
val irdt = irType(deref.type)
|
||||
return if(deref.type.isFloat) {
|
||||
val resultReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultReg, reg1 = pointerReg), null)
|
||||
ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultReg)
|
||||
} else {
|
||||
val resultReg = codeGen.registers.next(irdt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, irdt, reg1 = resultReg, reg2 = pointerReg), null)
|
||||
ExpressionCodeResult(result, irdt, resultReg, -1)
|
||||
}
|
||||
}
|
||||
|
||||
// load field with offset
|
||||
return if(deref.type.isFloat) {
|
||||
val resultReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADFIELD, IRDataType.FLOAT, fpReg1 = resultReg, reg1 = pointerReg, immediate = offset.toInt()), null)
|
||||
ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultReg)
|
||||
} else {
|
||||
val irdt = irType(deref.type)
|
||||
val resultReg = codeGen.registers.next(irdt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADFIELD, irdt, reg1 = resultReg, reg2 = pointerReg, immediate = offset.toInt()), null)
|
||||
ExpressionCodeResult(result, irdt, resultReg, -1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(ifExpr: PtIfExpression): ExpressionCodeResult {
|
||||
|
||||
if((ifExpr.condition as? PtPrefix)?.operator=="not")
|
||||
@ -166,46 +255,63 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
|
||||
private fun translate(expr: PtAddressOf): ExpressionCodeResult {
|
||||
val vmDt = irType(expr.type)
|
||||
val symbol = expr.identifier.name
|
||||
// note: LOAD <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val resultRegister = codeGen.registers.next(vmDt)
|
||||
val identifier = expr.identifier
|
||||
|
||||
fun loadAddressOfArrayLabel(reg: Int) {
|
||||
if (expr.isMsbForSplitArray) {
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_msb"), null)
|
||||
} else if (expr.identifier.type.isSplitWordArray) {
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier!!.name + "_msb"), null)
|
||||
} else if (identifier!!.type.isSplitWordArray) {
|
||||
// the _lsb split array comes first in memory
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_lsb"), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name + "_lsb"), null)
|
||||
} else
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name), null)
|
||||
}
|
||||
|
||||
if(expr.isFromArrayElement) {
|
||||
val indexTr = translateExpression(expr.arrayIndexExpr!!)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
val indexWordReg = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexWordReg, reg2=indexTr.resultReg), null)
|
||||
if(expr.identifier.type.isUnsignedWord) {
|
||||
val indexWordReg = if(indexTr.dt==IRDataType.BYTE) {
|
||||
val ixWord = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=ixWord, reg2=indexTr.resultReg), null)
|
||||
ixWord
|
||||
} else indexTr.resultReg
|
||||
val resultRegister = codeGen.registers.next(vmDt)
|
||||
if(identifier!!.type.isUnsignedWord) {
|
||||
require(!expr.isMsbForSplitArray)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
|
||||
val ptr = codeGen.symbolTable.lookup(identifier.name)
|
||||
it += if(ptr is StConstant)
|
||||
IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, immediate = ptr.value.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = identifier.name)
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
|
||||
}
|
||||
} else {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(expr.identifier.type, 1)
|
||||
val eltSize = codeGen.program.memsizer.memorySize(identifier.type, 1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
loadAddressOfArrayLabel(resultRegister)
|
||||
if(eltSize>1 && !expr.identifier.type.isSplitWordArray) {
|
||||
it += IRInstruction(Opcode.MUL, IRDataType.WORD, reg1=indexWordReg, immediate = eltSize)
|
||||
if (eltSize > 1 && !identifier.type.isSplitWordArray) {
|
||||
it += IRInstruction(Opcode.MUL, IRDataType.WORD, reg1 = indexWordReg, immediate = eltSize)
|
||||
}
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = resultRegister, reg2 = indexWordReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
|
||||
} else if(expr.identifier!=null ) {
|
||||
val resultRegister = codeGen.registers.next(vmDt)
|
||||
loadAddressOfArrayLabel(resultRegister)
|
||||
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
|
||||
} else {
|
||||
require(vmDt==IRDataType.WORD)
|
||||
val pointerTr = translateExpression(expr.dereference!!.startpointer)
|
||||
result += pointerTr.chunks
|
||||
val (instructions, offset) = traverseRestOfDerefChainToCalculateFinalAddress(expr.dereference!!, pointerTr.resultReg)
|
||||
result += instructions
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerTr.resultReg, immediate = offset.toInt()), null)
|
||||
return ExpressionCodeResult(result, vmDt, pointerTr.resultReg, -1)
|
||||
}
|
||||
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
|
||||
}
|
||||
|
||||
private fun translate(mem: PtMemoryByte): ExpressionCodeResult {
|
||||
@ -219,31 +325,32 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
val ptrWithOffset = mem.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 = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
|
||||
it += IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=offsetReg, labelSymbol = ptrName)
|
||||
if(ptrWithOffset!=null) {
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
|
||||
if(constOffset in 0..255) {
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = pointerReg, labelSymbol = ptrName)
|
||||
it += IRInstruction(Opcode.LOADFIELD, IRDataType.BYTE, reg1=resultRegister, reg2=pointerReg, immediate = constOffset)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
// LOADIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type?.isByte==true)
|
||||
translateExpression(offsetTypecast.value)
|
||||
else
|
||||
translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
|
||||
&& (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
// LOADIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type?.isByte==true)
|
||||
translateExpression(offsetTypecast.value)
|
||||
else
|
||||
translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
}
|
||||
|
||||
val tr = translateExpression(mem.address)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
@ -289,7 +396,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val haystackVar = check.haystackHeapVar!!
|
||||
when {
|
||||
haystackVar.type.isString -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
@ -300,7 +406,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
haystackVar.type.isByteArray -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
@ -314,7 +419,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
haystackVar.type.isWordArray -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
@ -328,7 +432,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
haystackVar.type.isFloatArray -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, -1, elementTr.resultFpReg)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
@ -346,11 +449,30 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
private fun translate(arrayIx: PtArrayIndexer): ExpressionCodeResult {
|
||||
if(arrayIx.type.isStructInstance)
|
||||
throw AssemblyError("cannot translate POINTER[x] resulting in a struct instance; this is likely part of a larger expression POINTER[x].field and that has to be translated earlier as a whole")
|
||||
|
||||
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type, null)
|
||||
val vmDt = irType(arrayIx.type)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val arrayVarSymbol = arrayIx.variable.name
|
||||
val arrayVar = arrayIx.variable
|
||||
if(arrayVar==null) {
|
||||
val pointerTr = translateExpression(arrayIx.pointerderef!!)
|
||||
result += pointerTr.chunks
|
||||
val pointerReg = pointerTr.resultReg
|
||||
return translatePointerIndexing(result, pointerReg, arrayIx.index, eltSize, vmDt)
|
||||
}
|
||||
if(arrayVar.type.isPointer) {
|
||||
val pointerTr = translateExpression(arrayVar)
|
||||
result += pointerTr.chunks
|
||||
val pointerReg = pointerTr.resultReg
|
||||
return translatePointerIndexing(result, pointerReg, arrayIx.index, eltSize, vmDt)
|
||||
}
|
||||
|
||||
require(!arrayVar.type.isPointer) { "only regular array indexing here ${arrayIx.position}" }
|
||||
var resultRegister = -1
|
||||
var resultFpRegister = -1
|
||||
val arrayVarSymbol = arrayVar.name
|
||||
|
||||
if(arrayIx.splitWords) {
|
||||
require(vmDt==IRDataType.WORD)
|
||||
@ -377,9 +499,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, vmDt, finalResultReg, -1)
|
||||
}
|
||||
|
||||
var resultFpRegister = -1
|
||||
if(arrayIx.index is PtNumber) {
|
||||
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize)
|
||||
fun indexByNumber(index: Int) {
|
||||
val memOffset = index * eltSize
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
|
||||
@ -388,23 +509,61 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
resultRegister = codeGen.registers.next(vmDt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
|
||||
}
|
||||
} else {
|
||||
val tr = translateExpression(arrayIx.index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
if(eltSize>1)
|
||||
result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
|
||||
}
|
||||
|
||||
fun indexByExpression() {
|
||||
val (code, indexByteReg) = codeGen.loadIndexReg(arrayIx.index, eltSize, false, arrayIx.splitWords)
|
||||
result += code
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=tr.resultReg, labelSymbol = arrayVarSymbol), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=indexByteReg, labelSymbol = arrayVarSymbol), null)
|
||||
}
|
||||
else {
|
||||
resultRegister = codeGen.registers.next(vmDt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = arrayVarSymbol), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=indexByteReg, labelSymbol = arrayVarSymbol), null)
|
||||
}
|
||||
}
|
||||
|
||||
if(arrayIx.index is PtNumber)
|
||||
indexByNumber((arrayIx.index as PtNumber).number.toInt())
|
||||
else
|
||||
indexByExpression()
|
||||
return ExpressionCodeResult(result, vmDt, resultRegister, resultFpRegister)
|
||||
}
|
||||
|
||||
private fun translatePointerIndexing(
|
||||
result: MutableList<IRCodeChunkBase>,
|
||||
pointerReg: Int,
|
||||
index: PtExpression,
|
||||
eltSize: Int,
|
||||
resultDt: IRDataType
|
||||
): ExpressionCodeResult {
|
||||
var resultRegister = -1
|
||||
var resultFpRegister = -1
|
||||
|
||||
if(index is PtNumber) {
|
||||
val memOffset = eltSize * index.number.toInt()
|
||||
if(memOffset>0)
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1=pointerReg, immediate = memOffset), null)
|
||||
}
|
||||
else {
|
||||
val (code, indexWordReg) = codeGen.loadIndexReg(index, eltSize, true, false)
|
||||
result += code
|
||||
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=pointerReg, reg2=indexWordReg), null)
|
||||
}
|
||||
|
||||
if(resultDt==IRDataType.FLOAT) {
|
||||
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1=resultFpRegister, reg1=pointerReg), null)
|
||||
}
|
||||
else {
|
||||
resultRegister = codeGen.registers.next(resultDt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, resultDt, reg1=resultRegister, reg2=pointerReg), null)
|
||||
}
|
||||
|
||||
return ExpressionCodeResult(result, resultDt, resultRegister, resultFpRegister)
|
||||
}
|
||||
|
||||
private fun translate(expr: PtPrefix): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = translateExpression(expr.value)
|
||||
@ -445,9 +604,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=tr.resultReg, immediate = 0), null)
|
||||
actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
|
||||
}
|
||||
valueDt.isWord -> {
|
||||
valueDt.isWord || valueDt.isPointer -> {
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.WORD, reg1=tr.resultReg, immediate = 0), null)
|
||||
actualResultReg2 =loadStatusAsBooleanResult(Opcode.BSTNE, result)
|
||||
actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
|
||||
}
|
||||
valueDt.isFloat -> {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||
@ -510,6 +669,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.FTOUW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||
}
|
||||
BaseDataType.POINTER -> {
|
||||
actualResultReg2 = tr.resultReg
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
}
|
||||
}
|
||||
@ -553,6 +715,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
}
|
||||
}
|
||||
BaseDataType.POINTER -> {
|
||||
require(valueDt.isUnsignedWord || valueDt.isPointer)
|
||||
actualResultReg2 = tr.resultReg
|
||||
// no further conversion required, pointers are all just uwords
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
TODO("typecast to array of pointers $valueDt -> ${cast.type}")
|
||||
}
|
||||
else -> throw AssemblyError("weird cast type")
|
||||
}
|
||||
|
||||
@ -565,8 +735,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return when(binExpr.operator) {
|
||||
"+" -> operatorPlus(binExpr, vmDt)
|
||||
"-" -> operatorMinus(binExpr, vmDt)
|
||||
"*" -> operatorMultiply(binExpr, vmDt)
|
||||
"/" -> operatorDivide(binExpr, vmDt, signed)
|
||||
"*" -> operatorMultiply(binExpr, binExpr.left.type)
|
||||
"/" -> operatorDivide(binExpr, binExpr.left.type)
|
||||
"%" -> operatorModulo(binExpr, vmDt)
|
||||
"|" -> operatorOr(binExpr, vmDt, true)
|
||||
"&" -> operatorAnd(binExpr, vmDt, true)
|
||||
@ -581,6 +751,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
">" -> operatorGreaterThan(binExpr, vmDt, signed, false)
|
||||
"<=" -> operatorLessThan(binExpr, vmDt, signed, true)
|
||||
">=" -> operatorGreaterThan(binExpr, vmDt, signed, true)
|
||||
"." -> operatorDereference(binExpr, vmDt) // eww, nasty, would rather not have any such expressions anymore
|
||||
else -> throw AssemblyError("weird operator ${binExpr.operator}")
|
||||
}
|
||||
}
|
||||
@ -588,11 +759,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
internal fun translate(fcall: PtFunctionCall): ExpressionCodeResult {
|
||||
val callTarget = codeGen.symbolTable.lookup(fcall.name)!!
|
||||
|
||||
if(callTarget.scopedName in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw", "floats.push", "floats.pop")) {
|
||||
if(callTarget.scopedNameString in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw", "floats.push", "floats.pop")) {
|
||||
// special case, these should be inlined, or even use specialized instructions. Instead of doing a normal subroutine call.
|
||||
return translateStackFunctions(fcall, callTarget)
|
||||
}
|
||||
when(callTarget.scopedName) {
|
||||
when(callTarget.scopedNameString) {
|
||||
"sys.clear_carry" -> {
|
||||
val chunk = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(chunk, IRInstruction(Opcode.CLC), null)
|
||||
@ -618,7 +789,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
when (callTarget) {
|
||||
is StSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
|
||||
// assign the arguments
|
||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
@ -642,7 +812,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
// return value(s)
|
||||
// TODO: for current implemenation of the call convention in case of multiple return values,
|
||||
// TODO: for current implementation of the call convention in case of multiple return values,
|
||||
// a list of Ir virtual registers to hold the results is NOT correct (they're loaded into AY + R15..R0 instead!)
|
||||
// So we use an empty list to avoid confusion here. This may change in a future version.
|
||||
val returnRegSpecs = if(fcall.void || callTarget.returns.size>1) emptyList() else {
|
||||
@ -670,7 +840,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
is StExtSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
|
||||
// assign the arguments
|
||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
@ -709,9 +878,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
Opcode.CALL,
|
||||
address = address.address.toInt(),
|
||||
fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
|
||||
}
|
||||
else {
|
||||
TODO("callfar into another bank is not implemented for the selected compilation target")
|
||||
} else if(address.constbank!=null) {
|
||||
IRInstruction(
|
||||
Opcode.CALLFAR,
|
||||
address = address.address.toInt(),
|
||||
immediate = address.constbank!!.toInt()
|
||||
)
|
||||
} else {
|
||||
val tr = translateExpression(address.varbank!!)
|
||||
require(tr.dt==IRDataType.BYTE)
|
||||
result += tr.chunks
|
||||
IRInstruction(
|
||||
Opcode.CALLFARVB,
|
||||
address = address.address.toInt(),
|
||||
reg1 = tr.resultReg
|
||||
)
|
||||
}
|
||||
}
|
||||
addInstr(result, call, null)
|
||||
@ -762,6 +943,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
else
|
||||
ExpressionCodeResult(result, returnRegSpec!!.dt, finalReturnRegister, -1)
|
||||
}
|
||||
is StStruct -> {
|
||||
throw AssemblyError("stray struct constructor should have been removed (normally it can only occur as initialization expression for a pointer variable)")
|
||||
}
|
||||
else -> {
|
||||
if(callTarget.type == StNodeType.LABEL) {
|
||||
require(fcall.void)
|
||||
@ -771,7 +955,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
else {
|
||||
throw AssemblyError("invalid node type")
|
||||
throw AssemblyError("invalid node type ${callTarget.type} at ${callTarget.astNode?.position}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -804,7 +988,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
|
||||
private fun translateStackFunctions(fcall: PtFunctionCall, callTarget: StNode): ExpressionCodeResult {
|
||||
val chunk = mutableListOf<IRCodeChunkBase>()
|
||||
when(callTarget.scopedName) {
|
||||
when(callTarget.scopedNameString) {
|
||||
"sys.push" -> {
|
||||
// push byte
|
||||
val tr = translateExpression(fcall.args.single())
|
||||
@ -1039,7 +1223,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
private fun loadStatusAsBooleanResult(branchForTrue: Opcode, result: MutableList<IRCodeChunkBase>): Int {
|
||||
// TODO this used to be a single instruction like SCC, SCS, SZ etc but those were problematic
|
||||
// TODO this used to be a single instruction like SCC, SCS, SZ etc
|
||||
val other = codeGen.createLabelName()
|
||||
val after = codeGen.createLabelName()
|
||||
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
@ -1054,7 +1238,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
private fun compareRegisterAsBooleanResult(branchForTrue: Opcode, dt: IRDataType, reg1: Int, reg2: Int, result: MutableList<IRCodeChunkBase>): Int {
|
||||
// TODO this used to be a single instruction like SCC, SCS, SZ etc but those were problematic
|
||||
// TODO this used to be a single instruction like SCC, SCS, SZ etc
|
||||
val other = codeGen.createLabelName()
|
||||
val after = codeGen.createLabelName()
|
||||
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
@ -1214,7 +1398,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun operatorDivide(binExpr: PtBinaryExpression, vmDt: IRDataType, signed: Boolean): ExpressionCodeResult {
|
||||
private fun operatorDivide(binExpr: PtBinaryExpression, dt: DataType): ExpressionCodeResult {
|
||||
val vmDt = irType(dt)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constFactorRight = binExpr.right as? PtNumber
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
@ -1229,11 +1414,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||
addInstr(result, if(signed)
|
||||
IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||
else
|
||||
IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||
, null)
|
||||
addInstr(result, if(dt.isSigned)
|
||||
IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||
else
|
||||
IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||
, null)
|
||||
return ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg)
|
||||
}
|
||||
} else {
|
||||
@ -1241,35 +1426,36 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val factor = constFactorRight.number.toInt()
|
||||
result += codeGen.divideByConst(vmDt, tr.resultReg, factor, signed)
|
||||
result += codeGen.divideByConst(vmDt, tr.resultReg, factor, dt.isSigned)
|
||||
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||
} else {
|
||||
if(binExpr.right is PtNumber) {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
addInstr(result, if (signed)
|
||||
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||
, null)
|
||||
addInstr(result, if (dt.isSigned)
|
||||
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||
, null)
|
||||
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||
} else {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
addInstr(result, if (signed)
|
||||
IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||
else
|
||||
IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||
, null)
|
||||
addInstr(result, if (dt.isSigned)
|
||||
IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||
else
|
||||
IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||
, null)
|
||||
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun operatorMultiply(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
|
||||
private fun operatorMultiply(binExpr: PtBinaryExpression, dt: DataType): ExpressionCodeResult {
|
||||
val vmDt = irType(dt)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constFactorLeft = binExpr.left as? PtNumber
|
||||
val constFactorRight = binExpr.right as? PtNumber
|
||||
@ -1291,7 +1477,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||
addInstr(result, IRInstruction(Opcode.MULR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.MULSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
||||
ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg)
|
||||
}
|
||||
} else {
|
||||
@ -1299,20 +1485,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(binExpr.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val factor = constFactorLeft.number.toInt()
|
||||
result += codeGen.multiplyByConst(vmDt, tr.resultReg, factor)
|
||||
result += codeGen.multiplyByConst(dt, tr.resultReg, factor)
|
||||
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||
} else if(constFactorRight!=null && !constFactorRight.type.isFloat) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val factor = constFactorRight.number.toInt()
|
||||
result += codeGen.multiplyByConst(vmDt, tr.resultReg, factor)
|
||||
result += codeGen.multiplyByConst(dt, tr.resultReg, factor)
|
||||
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||
} else {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.MULR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
|
||||
val opcode = if(dt.isSigned) Opcode.MULSR else Opcode.MULR
|
||||
addInstr(result, IRInstruction(opcode, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
|
||||
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
@ -1428,6 +1615,83 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun operatorDereference(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
|
||||
// the only case we support here is: a.b.c[i] . value
|
||||
val left = binExpr.left as? PtArrayIndexer
|
||||
val right = binExpr.right as? PtIdentifier
|
||||
require(binExpr.operator=="." && left!=null && right!=null) {"invalid dereference expression ${binExpr.position}"}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val indexedTr = translateExpression(left)
|
||||
result += indexedTr.chunks
|
||||
val pointerReg = indexedTr.resultReg
|
||||
val struct = left.type.dereference().subType as? StStruct
|
||||
require(indexedTr.dt== IRDataType.WORD && struct!=null)
|
||||
val field = struct.getField(right.name, this.codeGen.program.memsizer)
|
||||
|
||||
var resultFpReg = -1
|
||||
var resultReg = -1
|
||||
if(vmDt==IRDataType.FLOAT)
|
||||
resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
else
|
||||
resultReg = codeGen.registers.next(vmDt)
|
||||
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = field.second.toInt())
|
||||
if(vmDt==IRDataType.FLOAT)
|
||||
it += IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultFpReg, reg1 = pointerReg)
|
||||
else
|
||||
it += IRInstruction(Opcode.LOADI, vmDt, reg1 = resultReg, reg2 = pointerReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, vmDt, resultReg, resultFpReg)
|
||||
}
|
||||
|
||||
internal fun traverseRestOfDerefChainToCalculateFinalAddress(targetPointerDeref: PtPointerDeref, pointerReg: Int): Pair<IRCodeChunks, UInt> {
|
||||
// returns instructions to calculate the pointer address, and the offset into the struct it points to
|
||||
// so that LOADFIELD and STOREFIELD opcodes can be used instead of having an explicit extra ADD
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
if(targetPointerDeref.chain.isEmpty())
|
||||
return result to 0u // nothing to do; there's no deref chain
|
||||
|
||||
var struct: StStruct? = null
|
||||
if(targetPointerDeref.startpointer.type.subType!=null)
|
||||
struct = targetPointerDeref.startpointer.type.subType as StStruct
|
||||
if(targetPointerDeref.chain.isNotEmpty()) {
|
||||
// traverse deref chain
|
||||
for(deref in targetPointerDeref.chain.dropLast(1)) {
|
||||
val fieldinfo = struct!!.getField(deref, codeGen.program.memsizer)
|
||||
struct = fieldinfo.first.subType as StStruct
|
||||
// get new pointer from field
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldinfo.second.toInt())
|
||||
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
|
||||
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val field = targetPointerDeref.chain.last()
|
||||
val fieldinfo = struct!!.getField(field, codeGen.program.memsizer)
|
||||
if(fieldinfo.second>0u) {
|
||||
if(targetPointerDeref.derefLast) {
|
||||
require(fieldinfo.first.isPointer)
|
||||
// add the field offset
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldinfo.second.toInt()), null)
|
||||
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg), null)
|
||||
return result to 0u
|
||||
} else {
|
||||
return result to fieldinfo.second
|
||||
}
|
||||
}
|
||||
if(targetPointerDeref.derefLast) {
|
||||
require(fieldinfo.first.isPointer)
|
||||
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg), null)
|
||||
}
|
||||
return result to 0u
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -48,6 +48,7 @@ class IRCodeGen(
|
||||
irProg.linkChunks()
|
||||
irProg.convertAsmChunks()
|
||||
|
||||
// the optimizer also does 1 essential step regardless of optimizations: joining adjacent chunks.
|
||||
val optimizer = IRPeepholeOptimizer(irProg)
|
||||
optimizer.optimize(options.optimize, errors)
|
||||
irProg.validate()
|
||||
@ -55,6 +56,8 @@ class IRCodeGen(
|
||||
return irProg
|
||||
}
|
||||
|
||||
fun registerTypes(): Map<Int, IRDataType> = registers.getTypes()
|
||||
|
||||
private fun changeGlobalVarInits(symbolTable: SymbolTable) {
|
||||
// Normally, block level (global) variables that have a numeric initialization value
|
||||
// are initialized via an assignment statement.
|
||||
@ -64,15 +67,17 @@ class IRCodeGen(
|
||||
if(variable.uninitialized && variable.parent.type==StNodeType.BLOCK) {
|
||||
val block = variable.parent.astNode as PtBlock
|
||||
val initialization = (block.children.firstOrNull {
|
||||
it is PtAssignment && it.target.identifier?.name==variable.scopedName
|
||||
it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedNameString
|
||||
} as PtAssignment?)
|
||||
val initValue = initialization?.value
|
||||
when(initValue){
|
||||
is PtBool -> {
|
||||
require(initValue.asInt()!=0) { "boolean var should not be initialized with false, it wil be set to false as part of BSS clear, initializer=$initialization" }
|
||||
variable.setOnetimeInitNumeric(initValue.asInt().toDouble())
|
||||
initsToRemove += block to initialization
|
||||
}
|
||||
is PtNumber -> {
|
||||
require(initValue.number!=0.0) { "variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
|
||||
variable.setOnetimeInitNumeric(initValue.number)
|
||||
initsToRemove += block to initialization
|
||||
}
|
||||
@ -101,6 +106,15 @@ class IRCodeGen(
|
||||
is PtVariable -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
is PtProgram -> require('.' !in node.name) { "program name should not be scoped: ${node.name}" }
|
||||
is PtSubroutineParameter -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
is PtPointerDeref -> require('.' in node.startpointer.name) { "node $node name is not scoped: ${node.startpointer.name}" }
|
||||
is PtIdentifier -> {
|
||||
if('.' !in node.name) {
|
||||
// there is 1 case where the identifier is not scoped: if it's the value field name after a pointer array indexing.
|
||||
val expr = node.parent as? PtBinaryExpression
|
||||
if (expr?.operator != "." || expr.right !== node || expr.left !is PtArrayIndexer || !expr.left.type.isPointer)
|
||||
require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
}
|
||||
}
|
||||
else -> { /* node has no name or is ok to have no dots in the name */ }
|
||||
}
|
||||
node.children.forEach { verifyPtNode(it) }
|
||||
@ -194,10 +208,7 @@ class IRCodeGen(
|
||||
old.fpReg1,
|
||||
old.fpReg2,
|
||||
immediate = immediateValue,
|
||||
null,
|
||||
address = addressValue,
|
||||
null,
|
||||
null
|
||||
address = addressValue
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -256,9 +267,11 @@ class IRCodeGen(
|
||||
is PtBool,
|
||||
is PtArray,
|
||||
is PtBlock,
|
||||
is PtDefer -> throw AssemblyError("should have been transformed")
|
||||
is PtString -> throw AssemblyError("should not occur as separate statement node ${node.position}")
|
||||
is PtDefer -> throw AssemblyError("defer should have been transformed")
|
||||
is PtString -> throw AssemblyError("string should not occur as separate statement node ${node.position}")
|
||||
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
|
||||
is PtStructDecl -> emptyList()
|
||||
is PtSubSignature -> emptyList()
|
||||
else -> TODO("missing codegen for $node")
|
||||
}
|
||||
|
||||
@ -420,35 +433,44 @@ class IRCodeGen(
|
||||
whenStmt.choices.children.forEach {
|
||||
val choice = it as PtWhenChoice
|
||||
if(choice.isElse) {
|
||||
require(choice.parent.children.last() === choice)
|
||||
result += translateNode(choice.statements)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
// is always the last node so can fall through
|
||||
} else {
|
||||
if(choice.statements.children.isEmpty()) {
|
||||
// no statements for this choice value, jump to the end immediately
|
||||
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
||||
choice.values.children.map { v -> v as PtNumber }.sortedBy { v -> v.number }.forEach { value ->
|
||||
result += IRCodeChunk(null, null).also { chunk ->
|
||||
chunk += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val choiceLabel = createLabelName()
|
||||
choices.add(choiceLabel to choice)
|
||||
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = choiceLabel)
|
||||
val onlyJumpLabel = ((choice.statements.children.singleOrNull() as? PtJump)?.target as? PtIdentifier)?.name
|
||||
val branchLabel: String
|
||||
if(onlyJumpLabel==null) {
|
||||
choices.add(choiceLabel to choice)
|
||||
branchLabel = choiceLabel
|
||||
} else {
|
||||
branchLabel = onlyJumpLabel
|
||||
}
|
||||
choice.values.children.map { v -> v as PtNumber }.sortedBy { v -> v.number }.forEach { value ->
|
||||
result += IRCodeChunk(null, null).also { chunk ->
|
||||
chunk += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = branchLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
|
||||
if(choices.isNotEmpty())
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
|
||||
choices.forEach { (label, choice) ->
|
||||
result += labelFirstChunk(translateNode(choice.statements), label)
|
||||
val lastStatement = choice.statements.children.last()
|
||||
if(lastStatement !is PtReturn && lastStatement !is PtJump)
|
||||
if(!choice.isOnlyGotoOrReturn())
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
}
|
||||
|
||||
@ -468,11 +490,12 @@ class IRCodeGen(
|
||||
translateForInNonConstantRange(forLoop, loopvar)
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
require(forLoop.variable.name == loopvar.scopedNameString)
|
||||
val elementDt = irType(iterable.type.elementType())
|
||||
val iterableLength = symbolTable.getLength(iterable.name)
|
||||
val loopvarSymbol = forLoop.variable.name
|
||||
val indexReg = registers.next(IRDataType.BYTE)
|
||||
val tmpReg = registers.next(IRDataType.BYTE)
|
||||
val tmpReg = registers.next(elementDt)
|
||||
val loopLabel = createLabelName()
|
||||
val endLabel = createLabelName()
|
||||
when {
|
||||
@ -480,9 +503,9 @@ class IRCodeGen(
|
||||
// iterate over a zero-terminated string
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
|
||||
result += IRCodeChunk(loopLabel, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name)
|
||||
it += IRInstruction(Opcode.LOADX, elementDt, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name)
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tmpReg, labelSymbol = loopvarSymbol)
|
||||
it += IRInstruction(Opcode.STOREM, elementDt, reg1 = tmpReg, labelSymbol = loopvarSymbol)
|
||||
}
|
||||
result += translateNode(forLoop.statements)
|
||||
val jumpChunk = IRCodeChunk(null, null)
|
||||
@ -491,10 +514,9 @@ class IRCodeGen(
|
||||
result += jumpChunk
|
||||
result += IRCodeChunk(endLabel, null)
|
||||
}
|
||||
iterable.type.isSplitWordArray -> {
|
||||
iterable.type.isSplitWordArray || iterable.type.isPointerArray -> {
|
||||
// iterate over lsb/msb split word array
|
||||
val elementDt = iterable.type.elementType()
|
||||
if(!elementDt.isWord)
|
||||
if(elementDt!=IRDataType.WORD)
|
||||
throw AssemblyError("weird dt")
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
|
||||
result += IRCodeChunk(loopLabel, null).also {
|
||||
@ -504,7 +526,7 @@ class IRCodeGen(
|
||||
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)
|
||||
it += IRInstruction(Opcode.STOREM, elementDt, reg1=concatReg, labelSymbol = loopvarSymbol)
|
||||
}
|
||||
result += translateNode(forLoop.statements)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
@ -548,7 +570,7 @@ class IRCodeGen(
|
||||
val step = iterable.step.number.toInt()
|
||||
if (step==0)
|
||||
throw AssemblyError("step 0")
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
require(forLoop.variable.name == loopvar.scopedNameString)
|
||||
val loopvarSymbol = forLoop.variable.name
|
||||
val loopvarDt = when(loopvar) {
|
||||
is StMemVar -> loopvar.dt
|
||||
@ -634,7 +656,7 @@ class IRCodeGen(
|
||||
|
||||
private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StNode): IRCodeChunks {
|
||||
val loopLabel = createLabelName()
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
require(forLoop.variable.name == loopvar.scopedNameString)
|
||||
val loopvarSymbol = forLoop.variable.name
|
||||
val loopvarDt = when(loopvar) {
|
||||
is StMemVar -> loopvar.dt
|
||||
@ -771,7 +793,7 @@ class IRCodeGen(
|
||||
code += if(factor==0.0) {
|
||||
IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = 0.0)
|
||||
} else {
|
||||
IRInstruction(Opcode.MUL, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
|
||||
IRInstruction(Opcode.MULS, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@ -789,38 +811,40 @@ class IRCodeGen(
|
||||
val factorReg = registers.next(IRDataType.FLOAT)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, immediateFp = factor)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
||||
IRInstruction(Opcode.MULSM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.MULSM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
internal fun multiplyByConst(dt: IRDataType, reg: Int, factor: Int): IRCodeChunk {
|
||||
internal fun multiplyByConst(dt: DataType, reg: Int, factor: Int): IRCodeChunk {
|
||||
val irdt = irType(dt)
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(factor==1)
|
||||
return code
|
||||
val pow2 = powersOfTwoInt.indexOf(factor)
|
||||
if(pow2==1) {
|
||||
// just shift 1 bit
|
||||
code += IRInstruction(Opcode.LSL, dt, reg1=reg)
|
||||
code += IRInstruction(Opcode.LSL, irdt, reg1=reg)
|
||||
}
|
||||
else if(pow2>=1) {
|
||||
// just shift multiple bits
|
||||
val pow2reg = registers.next(IRDataType.BYTE)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=pow2reg, immediate = pow2)
|
||||
code += IRInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg)
|
||||
code += IRInstruction(Opcode.LSLN, irdt, reg1=reg, reg2=pow2reg)
|
||||
} else {
|
||||
code += if (factor == 0) {
|
||||
IRInstruction(Opcode.LOAD, dt, reg1=reg, immediate = 0)
|
||||
IRInstruction(Opcode.LOAD, irdt, reg1=reg, immediate = 0)
|
||||
} else {
|
||||
IRInstruction(Opcode.MUL, dt, reg1=reg, immediate = factor)
|
||||
val opcode = if(dt.isSigned) Opcode.MULS else Opcode.MUL
|
||||
IRInstruction(opcode, irdt, reg1=reg, immediate = factor)
|
||||
}
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
internal fun multiplyByConstInplace(dt: IRDataType, knownAddress: Int?, symbol: String?, factor: Int): IRCodeChunk {
|
||||
internal fun multiplyByConstInplace(dt: IRDataType, signed: Boolean, knownAddress: Int?, symbol: String?, factor: Int): IRCodeChunk {
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(factor==1)
|
||||
return code
|
||||
@ -850,10 +874,11 @@ class IRCodeGen(
|
||||
else {
|
||||
val factorReg = registers.next(dt)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, immediate = factor)
|
||||
val opcode = if(signed) Opcode.MULSM else Opcode.MULM
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, address = knownAddress)
|
||||
IRInstruction(opcode, dt, reg1=factorReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, labelSymbol = symbol)
|
||||
IRInstruction(opcode, dt, reg1=factorReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@ -1070,7 +1095,7 @@ class IRCodeGen(
|
||||
}
|
||||
// evaluate jump address expression into a register and jump indirectly to it
|
||||
val tr = expressionEval.translateExpression(goto.target)
|
||||
for(i in tr.chunks.flatMap { it.instructions }) {
|
||||
for(i in tr.chunks.flatMap { c -> c.instructions }) {
|
||||
it += i
|
||||
}
|
||||
it += IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg)
|
||||
@ -1653,7 +1678,7 @@ class IRCodeGen(
|
||||
translateSimple(cond, Opcode.BSTEQ, false)
|
||||
}
|
||||
is PtTypeCast -> {
|
||||
require(cond.type.isBool && cond.value.type.isNumeric)
|
||||
require(cond.type.isBool && (cond.value.type.isNumeric || cond.value.type.isPointer))
|
||||
translateSimple(cond, Opcode.BSTEQ, false)
|
||||
}
|
||||
is PtIdentifier, is PtArrayIndexer, is PtContainmentCheck -> {
|
||||
@ -1836,7 +1861,7 @@ class IRCodeGen(
|
||||
is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ }
|
||||
is PtAlign -> TODO("ir support for inline %align")
|
||||
is PtSub -> {
|
||||
val sub = IRSubroutine(child.name, translate(child.parameters), child.returns, child.position)
|
||||
val sub = IRSubroutine(child.name, translateParameters(child.signature.children), child.signature.returns, child.position)
|
||||
for (subchild in child.children) {
|
||||
translateNode(subchild).forEach { sub += it }
|
||||
}
|
||||
@ -1885,21 +1910,23 @@ class IRCodeGen(
|
||||
}
|
||||
}
|
||||
}
|
||||
is PtStructDecl -> { /* do nothing, should be found in the symbol table */ }
|
||||
else -> TODO("weird block child node $child")
|
||||
}
|
||||
}
|
||||
return irBlock
|
||||
}
|
||||
|
||||
private fun translate(parameters: List<PtSubroutineParameter>): List<IRSubroutine.IRParam> {
|
||||
private fun translateParameters(parameters: List<PtNode>): List<IRSubroutine.IRParam> {
|
||||
val result = mutableListOf<IRSubroutine.IRParam>()
|
||||
parameters.forEach {
|
||||
it as PtSubroutineParameter
|
||||
if(it.register==null) {
|
||||
val flattenedName = it.definingISub()!!.name + "." + it.name
|
||||
if (symbolTable.lookup(flattenedName) == null)
|
||||
TODO("fix missing lookup for: $flattenedName parameter")
|
||||
val orig = symbolTable.lookup(flattenedName) as StStaticVariable
|
||||
result += IRSubroutine.IRParam(flattenedName, orig.dt)
|
||||
require('.' in it.name) { "even parameter names should have been made fully scoped by now" }
|
||||
val orig = symbolTable.lookup(it.name) as? StStaticVariable
|
||||
if (orig == null)
|
||||
TODO("fix missing lookup for: ${it.name} parameter")
|
||||
result += IRSubroutine.IRParam(it.name, orig.dt)
|
||||
} else {
|
||||
val reg = it.register
|
||||
require(reg in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" }
|
||||
@ -1924,7 +1951,7 @@ class IRCodeGen(
|
||||
|
||||
internal fun isOne(expression: PtExpression): Boolean = (expression as? PtNumber)?.number==1.0 || (expression as? PtBool)?.value==true
|
||||
|
||||
fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
|
||||
internal fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
|
||||
return IRCodeChunk(label, null).also {
|
||||
val args = params.map { (dt, reg)->
|
||||
FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(dt, reg, null))
|
||||
@ -1935,9 +1962,7 @@ class IRCodeGen(
|
||||
}
|
||||
}
|
||||
|
||||
fun registerTypes(): Map<Int, IRDataType> = registers.getTypes()
|
||||
|
||||
fun setCpuRegister(registerOrFlag: RegisterOrStatusflag, paramDt: IRDataType, resultReg: Int, resultFpReg: Int): IRCodeChunk {
|
||||
internal fun setCpuRegister(registerOrFlag: RegisterOrStatusflag, paramDt: IRDataType, resultReg: Int, resultFpReg: Int): IRCodeChunk {
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
when(registerOrFlag.registerOrPair) {
|
||||
RegisterOrPair.A -> chunk += IRInstruction(Opcode.STOREHA, IRDataType.BYTE, reg1=resultReg)
|
||||
@ -1954,10 +1979,83 @@ class IRCodeGen(
|
||||
null -> when(registerOrFlag.statusflag) {
|
||||
// TODO: do the statusflag argument as last
|
||||
Statusflag.Pc -> chunk += IRInstruction(Opcode.LSR, paramDt, reg1=resultReg)
|
||||
else -> throw AssemblyError("weird statusflag as param")
|
||||
else -> throw AssemblyError("unsupported statusflag as param")
|
||||
}
|
||||
else -> throw AssemblyError("unsupported register arg")
|
||||
}
|
||||
return chunk
|
||||
}
|
||||
|
||||
internal fun evaluatePointerAddressIntoReg(result: MutableList<IRCodeChunkBase>, deref: PtPointerDeref): Pair<Int, UInt> {
|
||||
// calculates the pointer address and returns the register it's in + remaining offset into the struct (so that LOADFIELD/STOREFIELD instructions can be used)
|
||||
val pointerTr = expressionEval.translateExpression(deref.startpointer)
|
||||
result += pointerTr.chunks
|
||||
val (instructions, offset) = expressionEval.traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerTr.resultReg)
|
||||
result += instructions
|
||||
return pointerTr.resultReg to offset
|
||||
}
|
||||
|
||||
internal fun storeValueAtPointersLocation(result: MutableList<IRCodeChunkBase>, addressReg: Int, offset: UInt, type: DataType, valueIsZero: Boolean, existingValueRegister: Int) {
|
||||
if(offset==0u) {
|
||||
val irdt = irType(type)
|
||||
val instr = if(type.isFloat) {
|
||||
if (valueIsZero) IRInstruction(Opcode.STOREZI, IRDataType.FLOAT, reg1 = addressReg)
|
||||
else IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 = existingValueRegister, reg1 = addressReg)
|
||||
} else {
|
||||
if (valueIsZero) IRInstruction(Opcode.STOREZI, irdt, reg1 = addressReg)
|
||||
else IRInstruction(Opcode.STOREI, irdt, reg1 = existingValueRegister, reg2 = addressReg)
|
||||
}
|
||||
addInstr(result, instr, null)
|
||||
return
|
||||
}
|
||||
|
||||
// store with field offset
|
||||
var valueRegister = existingValueRegister
|
||||
val irdt = irType(type)
|
||||
if(valueIsZero && valueRegister<0) {
|
||||
if(type.isFloat) {
|
||||
valueRegister = registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = valueRegister, immediateFp = 0.0), null)
|
||||
} else {
|
||||
valueRegister = registers.next(irdt)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, irdt, reg1 = valueRegister, immediate = 0), null)
|
||||
}
|
||||
}
|
||||
val instr = if (type.isFloat)
|
||||
IRInstruction(Opcode.STOREFIELD, IRDataType.FLOAT, fpReg1 = valueRegister, reg1 = addressReg, immediate = offset.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.STOREFIELD, irdt, reg1 = valueRegister, reg2 = addressReg, immediate = offset.toInt())
|
||||
addInstr(result, instr, null)
|
||||
}
|
||||
|
||||
internal fun loadIndexReg(index: PtExpression, itemsize: Int, wordIndex: Boolean, arrayIsSplitWords: Boolean): Pair<IRCodeChunks, Int> {
|
||||
// returns the code to load the Index into the register, which is also returned.
|
||||
|
||||
require(index !is PtNumber) { "index should not be a constant number here, calling code should handle that in a more efficient way" }
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
if(wordIndex) {
|
||||
val tr = expressionEval.translateExpression(index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
var indexReg = tr.resultReg
|
||||
if(tr.dt==IRDataType.BYTE) {
|
||||
indexReg = registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexReg, reg2=tr.resultReg), null)
|
||||
}
|
||||
result += multiplyByConst(DataType.UWORD, indexReg, itemsize)
|
||||
return Pair(result, indexReg)
|
||||
}
|
||||
|
||||
// regular byte size index value.
|
||||
val byteIndexTr = expressionEval.translateExpression(index)
|
||||
addToResult(result, byteIndexTr, byteIndexTr.resultReg, -1)
|
||||
|
||||
if(itemsize==1 || arrayIsSplitWords)
|
||||
return Pair(result, byteIndexTr.resultReg)
|
||||
|
||||
result += multiplyByConst(DataType.UBYTE, byteIndexTr.resultReg, itemsize)
|
||||
return Pair(result, byteIndexTr.resultReg)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
|
||||
private fun optimizeOnlyJoinChunks() {
|
||||
// this chunk-joining is REQUIRED (optimization or no) to end up with a structurally sound chunk list
|
||||
irprog.foreachSub { sub ->
|
||||
joinChunks(sub)
|
||||
removeEmptyChunks(sub)
|
||||
@ -84,7 +85,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
/*
|
||||
Empty Code chunk with label ->
|
||||
If next chunk has no label -> move label to next chunk, remove original
|
||||
If next chunk has label -> label name should be the same, remove original, otherwise merge both labels into 1.
|
||||
If next chunk has label -> label name should be the same, in which case remove original, otherwise leave everything untouched.
|
||||
If is last chunk -> keep chunk in place because of the label.
|
||||
Empty Code chunk without label ->
|
||||
should not have been generated! ERROR.
|
||||
@ -111,14 +112,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
if(index>0) {
|
||||
if (chunk.label == nextchunk.label)
|
||||
removeChunks += index
|
||||
else {
|
||||
removeChunks += index
|
||||
replaceLabels[chunk.label!!] = nextchunk.label!!
|
||||
replaceLabels.entries.forEach { (key, value) ->
|
||||
if (value == chunk.label)
|
||||
replaceLabels[key] = nextchunk.label!!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -364,7 +357,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
var changed = false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
when (ins.opcode) {
|
||||
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> {
|
||||
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MULS, Opcode.MOD -> {
|
||||
if (ins.immediate == 1) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
|
@ -27,6 +27,9 @@ class IRUnusedCodeRemover(
|
||||
// we could clean up the SymbolTable as well, but ONLY if these symbols aren't referenced somewhere still in an instruction or variable initializer value
|
||||
val prefix = "$blockLabel."
|
||||
val blockVars = irprog.st.allVariables().filter { it.name.startsWith(prefix) }
|
||||
|
||||
// check if there are symbols referenced elsewhere that we should not prune (even though the rest of the block is empty)
|
||||
|
||||
blockVars.forEach { stVar ->
|
||||
irprog.allSubs().flatMap { it.chunks }.forEach { chunk ->
|
||||
chunk.instructions.forEach { ins ->
|
||||
@ -47,7 +50,50 @@ class IRUnusedCodeRemover(
|
||||
}
|
||||
}
|
||||
|
||||
val blockStructs = irprog.st.allStructDefs().filter { it.name.startsWith(prefix) }
|
||||
blockStructs.forEach { struct ->
|
||||
irprog.st.allStructInstances().forEach { instance ->
|
||||
if(instance.structName == struct.name)
|
||||
return // a struct instance is declared using this struct type
|
||||
}
|
||||
irprog.st.allVariables().forEach { variable ->
|
||||
if(variable.dt.isPointer || variable.dt.isStructInstance)
|
||||
if(struct.name == variable.dt.subType!!.scopedNameString)
|
||||
return // a variable exists with the struct as (pointer) type
|
||||
}
|
||||
}
|
||||
|
||||
irprog.st.removeTree(blockLabel)
|
||||
removeBlockInits(irprog, blockLabel)
|
||||
}
|
||||
|
||||
private fun removeBlockInits(code: IRProgram, blockLabel: String) {
|
||||
val instructions = code.globalInits.instructions
|
||||
instructions.toTypedArray().forEach {ins ->
|
||||
if(ins.labelSymbol?.startsWith(blockLabel)==true) {
|
||||
instructions.remove(ins)
|
||||
}
|
||||
}
|
||||
|
||||
// remove stray loads
|
||||
instructions.toTypedArray().forEach { ins ->
|
||||
if(ins.opcode in arrayOf(Opcode.LOAD, Opcode.LOADR, Opcode.LOADM)) {
|
||||
if(ins.reg1!=0) {
|
||||
if(instructions.count { it.reg1==ins.reg1 || it.reg2==ins.reg1 } <2) {
|
||||
if(ins.labelSymbol!=null)
|
||||
code.st.removeIfExists(ins.labelSymbol!!)
|
||||
instructions.remove(ins)
|
||||
}
|
||||
}
|
||||
else if(ins.fpReg1!=0) {
|
||||
if (instructions.count { it.fpReg1 == ins.fpReg1 || it.fpReg2 == ins.fpReg1 } < 2) {
|
||||
if(ins.labelSymbol!=null)
|
||||
code.st.removeIfExists(ins.labelSymbol!!)
|
||||
instructions.remove(ins)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeUnusedSubroutines(): Int {
|
||||
|
@ -14,6 +14,12 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
||||
StNodeType.MEMVAR -> st.add(convert(it.value as StMemVar))
|
||||
StNodeType.CONSTANT -> st.add(convert(it.value as StConstant))
|
||||
StNodeType.MEMORYSLAB -> st.add(convert(it.value as StMemorySlab))
|
||||
StNodeType.STRUCTINSTANCE -> {
|
||||
val instance = it.value as StStructInstance
|
||||
val struct = sourceSt.lookup(instance.structName) as StStruct
|
||||
st.add(convert(instance, struct.fields))
|
||||
}
|
||||
StNodeType.STRUCT -> st.add(convert(it.value as StStruct))
|
||||
else -> { }
|
||||
}
|
||||
}
|
||||
@ -37,12 +43,17 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
||||
}
|
||||
|
||||
|
||||
private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
private fun convert(struct: StStruct): IRStStructDef =
|
||||
IRStStructDef(struct.scopedNameString, struct.fields, struct.size)
|
||||
|
||||
fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
|
||||
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
|
||||
else
|
||||
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
|
||||
|
||||
private fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
|
||||
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
|
||||
else
|
||||
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
|
||||
|
||||
|
||||
private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
|
||||
if('.' in variable.name) {
|
||||
return IRStStaticVariable(variable.name,
|
||||
@ -52,7 +63,8 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
variable.initializationArrayValue?.map { convertArrayElt(it) },
|
||||
variable.length,
|
||||
variable.zpwish,
|
||||
variable.align)
|
||||
variable.align,
|
||||
variable.dirty)
|
||||
} else {
|
||||
fun fixupAddressOfInArray(array: List<StArrayElement>?): List<IRStArrayElement>? {
|
||||
if(array==null)
|
||||
@ -61,14 +73,14 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
array.forEach {
|
||||
if(it.addressOfSymbol!=null) {
|
||||
val target = variable.lookup(it.addressOfSymbol!!) ?: throw NoSuchElementException("can't find variable ${it.addressOfSymbol}")
|
||||
newArray.add(IRStArrayElement(null, null, target.scopedName))
|
||||
newArray.add(IRStArrayElement(null, null, target.scopedNameString))
|
||||
} else {
|
||||
newArray.add(convertArrayElt(it))
|
||||
}
|
||||
}
|
||||
return newArray
|
||||
}
|
||||
val scopedName = variable.scopedName
|
||||
val scopedName = variable.scopedNameString
|
||||
return IRStStaticVariable(scopedName,
|
||||
variable.dt,
|
||||
variable.initializationNumericValue,
|
||||
@ -76,7 +88,8 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
fixupAddressOfInArray(variable.initializationArrayValue),
|
||||
variable.length,
|
||||
variable.zpwish,
|
||||
variable.align
|
||||
variable.align,
|
||||
variable.dirty
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -92,7 +105,7 @@ private fun convert(variable: StMemVar): IRStMemVar {
|
||||
)
|
||||
} else {
|
||||
val scopedName = try {
|
||||
variable.scopedName
|
||||
variable.scopedNameString
|
||||
} catch (_: UninitializedPropertyAccessException) {
|
||||
variable.name
|
||||
}
|
||||
@ -107,7 +120,7 @@ private fun convert(constant: StConstant): IRStConstant {
|
||||
constant.name
|
||||
} else {
|
||||
try {
|
||||
constant.scopedName
|
||||
constant.scopedNameString
|
||||
} catch (_: UninitializedPropertyAccessException) {
|
||||
constant.name
|
||||
}
|
||||
@ -120,12 +133,17 @@ private fun convert(variable: StMemorySlab): IRStMemorySlab {
|
||||
return if('.' in variable.name)
|
||||
IRStMemorySlab(variable.name, variable.size, variable.align)
|
||||
else
|
||||
IRStMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align)
|
||||
IRStMemorySlab("$StMemorySlabPrefix.${variable.name}", variable.size, variable.align)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
private fun convert(instance: StStructInstance, fields: List<Pair<DataType, String>>): IRStStructInstance {
|
||||
val values = fields.zip(instance.initialValues).map { (field, value) ->
|
||||
val elt = convertArrayElt(value)
|
||||
IRStructInitValue(field.first.base, elt)
|
||||
}
|
||||
return IRStStructInstance(instance.name, instance.structName, values, instance.size)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
internal const val StMemorySlabPrefix = "prog8_slabs" // TODO also add ".prog8_memoryslab_" ?
|
||||
|
@ -3,7 +3,9 @@ import prog8.code.core.*
|
||||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray || dt.isSplitWordArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!!
|
||||
else if(dt.isArray || dt.isSplitWordArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
@ -66,7 +68,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||
override fun print_single_error(errormessage: String) { /* prints nothing in tests */ }
|
||||
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
|
@ -53,7 +53,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
irProg.chunks().single().instructions.size shouldBe 1
|
||||
}
|
||||
|
||||
test("remove jmp to label below") {
|
||||
test("remove jmp to label below but keep labels") {
|
||||
val c1 = IRCodeChunk("main.start", null)
|
||||
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label")
|
||||
val c2 = IRCodeChunk("label", null)
|
||||
@ -69,13 +69,16 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
irProg.chunks().size shouldBe 3
|
||||
irProg.chunks()[0].label shouldBe "main.start"
|
||||
irProg.chunks()[1].label shouldBe "label2"
|
||||
irProg.chunks()[2].label shouldBe "label3"
|
||||
irProg.chunks()[0].isEmpty() shouldBe true
|
||||
irProg.chunks()[1].isEmpty() shouldBe false
|
||||
irProg.chunks()[2].isEmpty() shouldBe true
|
||||
val chunks = irProg.chunks()
|
||||
chunks.size shouldBe 4
|
||||
chunks[0].label shouldBe "main.start"
|
||||
chunks[1].label shouldBe "label"
|
||||
chunks[2].label shouldBe "label2"
|
||||
chunks[3].label shouldBe "label3"
|
||||
chunks[0].isEmpty() shouldBe true
|
||||
chunks[1].isEmpty() shouldBe true
|
||||
chunks[2].isEmpty() shouldBe false
|
||||
chunks[3].isEmpty() shouldBe true
|
||||
val instr = irProg.chunks().flatMap { it.instructions }
|
||||
instr.size shouldBe 2
|
||||
instr[0].opcode shouldBe Opcode.JUMP
|
||||
|
@ -52,6 +52,7 @@ class TestVmCodeGen: FunSpec({
|
||||
DataType.UBYTE,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
@ -61,6 +62,7 @@ class TestVmCodeGen: FunSpec({
|
||||
DataType.arrayFor(BaseDataType.UBYTE),
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
3u,
|
||||
Position.DUMMY
|
||||
@ -70,6 +72,7 @@ class TestVmCodeGen: FunSpec({
|
||||
DataType.arrayFor(BaseDataType.UBYTE),
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
3u,
|
||||
Position.DUMMY
|
||||
@ -79,6 +82,7 @@ class TestVmCodeGen: FunSpec({
|
||||
DataType.WORD,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
@ -167,6 +171,7 @@ class TestVmCodeGen: FunSpec({
|
||||
DataType.FLOAT,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
@ -238,6 +243,7 @@ class TestVmCodeGen: FunSpec({
|
||||
DataType.FLOAT,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
@ -305,6 +311,7 @@ class TestVmCodeGen: FunSpec({
|
||||
DataType.FLOAT,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
@ -360,6 +367,7 @@ class TestVmCodeGen: FunSpec({
|
||||
DataType.BYTE,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
@ -431,6 +439,7 @@ class TestVmCodeGen: FunSpec({
|
||||
DataType.BYTE,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
@ -498,6 +507,7 @@ class TestVmCodeGen: FunSpec({
|
||||
DataType.BYTE,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
@ -554,11 +564,8 @@ class TestVmCodeGen: FunSpec({
|
||||
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||
irChunks.size shouldBe 1
|
||||
irChunks[0].instructions.size shouldBe 2
|
||||
val preparecallInstr = irChunks[0].instructions[0]
|
||||
preparecallInstr.opcode shouldBe Opcode.PREPARECALL
|
||||
preparecallInstr.immediate shouldBe 0
|
||||
val callInstr = irChunks[0].instructions[1]
|
||||
irChunks[0].instructions.size shouldBe 1
|
||||
val callInstr = irChunks[0].instructions[0]
|
||||
callInstr.opcode shouldBe Opcode.CALL
|
||||
callInstr.address shouldBe 0x5000
|
||||
}
|
||||
|
@ -5,10 +5,7 @@ import prog8.ast.FatalAstException
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.FunctionCallExpression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.isInteger
|
||||
import prog8.code.core.isIntegerOrBool
|
||||
import prog8.code.core.*
|
||||
import kotlin.math.*
|
||||
|
||||
|
||||
@ -70,52 +67,45 @@ class ConstExprEvaluator {
|
||||
}
|
||||
|
||||
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
||||
if(right.type.isIntegerOrBool) {
|
||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.UWORD) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.LONG) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
|
||||
if(right.type.isIntegerOrBool) {
|
||||
val leftDt = left.type
|
||||
if(leftDt.isByteOrBool)
|
||||
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() xor right.number.toInt()) and 255).toDouble(), left.position)
|
||||
else if(leftDt.isInteger) {
|
||||
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() xor right.number.toInt()) and 65535).toDouble(), left.position)
|
||||
else if (leftDt == BaseDataType.LONG)
|
||||
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 {
|
||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
||||
if(right.type.isIntegerOrBool) {
|
||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.UWORD) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.LONG) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
|
||||
if(right.type.isIntegerOrBool) {
|
||||
val leftDt = left.type
|
||||
if(leftDt.isByteOrBool)
|
||||
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() or right.number.toInt()) and 255).toDouble(), left.position)
|
||||
else if(leftDt.isInteger) {
|
||||
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() or right.number.toInt()) and 65535).toDouble(), left.position)
|
||||
else if (leftDt == BaseDataType.LONG)
|
||||
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 {
|
||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
||||
if(right.type.isIntegerOrBool) {
|
||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.UWORD) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.LONG) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
|
||||
if(right.type.isIntegerOrBool) {
|
||||
val leftDt = left.type
|
||||
if(leftDt.isByteOrBool)
|
||||
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() and right.number.toInt()) and 255).toDouble(), left.position)
|
||||
else if(leftDt.isInteger) {
|
||||
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() and right.number.toInt()) and 65535).toDouble(), left.position)
|
||||
else if (leftDt == BaseDataType.LONG)
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left & $right", left.position)
|
||||
|
@ -27,8 +27,8 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
// @( &thing ) --> thing (but only if thing is a byte type!)
|
||||
val addrOf = memread.addressExpression as? AddressOf
|
||||
if(addrOf!=null) {
|
||||
if(addrOf.identifier.inferType(program).isBytes)
|
||||
return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier, parent))
|
||||
if(addrOf.identifier?.inferType(program)?.isBytes==true)
|
||||
return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier!!, parent))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
@ -129,7 +129,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
return listOf(IAstModification.ReplaceNode(expr, newArray, parent))
|
||||
}
|
||||
else {
|
||||
val leftTarget = (expr.left as? IdentifierReference)?.targetVarDecl(program)
|
||||
val leftTarget = (expr.left as? IdentifierReference)?.targetVarDecl()
|
||||
if(leftTarget!=null && leftTarget.origin==VarDeclOrigin.ARRAYLITERAL)
|
||||
throw FatalAstException("shouldn't see an array literal converted to an autovar here")
|
||||
}
|
||||
@ -332,15 +332,19 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
|
||||
val constIndex = arrayIndexedExpression.indexer.constIndex()
|
||||
if (constIndex != null) {
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
if(arrayVar!=null) {
|
||||
val array =arrayVar.value as? ArrayLiteral
|
||||
if(array!=null) {
|
||||
val value = array.value[constIndex].constValue(program)
|
||||
if(value!=null) {
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, value, parent))
|
||||
if(arrayIndexedExpression.plainarrayvar!=null) {
|
||||
val arrayVar = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
|
||||
if(arrayVar!=null) {
|
||||
val array =arrayVar.value as? ArrayLiteral
|
||||
if(array!=null) {
|
||||
val value = array.value[constIndex].constValue(program)
|
||||
if(value!=null) {
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, value, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(arrayIndexedExpression.pointerderef!=null) {
|
||||
TODO("constant fold pointer[i]")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -387,10 +391,10 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
val rangeTo = iterableRange.to as? NumericLiteral
|
||||
if(rangeFrom==null || rangeTo==null) return noModifications
|
||||
|
||||
val loopvar = forLoop.loopVar.targetVarDecl(program) ?: return noModifications
|
||||
val loopvar = forLoop.loopVar.targetVarDecl() ?: return noModifications
|
||||
|
||||
val stepLiteral = iterableRange.step as? NumericLiteral
|
||||
require(loopvar.datatype.sub == null)
|
||||
require(loopvar.datatype.isBasic)
|
||||
val loopvarSimpleDt = loopvar.datatype.base
|
||||
when(loopvarSimpleDt) {
|
||||
BaseDataType.UBYTE -> {
|
||||
|
@ -323,7 +323,7 @@ internal class ConstantIdentifierReplacer(
|
||||
val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position)
|
||||
return if(arrayIdx.parent is AssignTarget) {
|
||||
val memwrite = DirectMemoryWrite(add, identifier.position)
|
||||
val assignTarget = AssignTarget(null, null, memwrite, null, false, identifier.position)
|
||||
val assignTarget = AssignTarget(null, null, memwrite, null, false, position = identifier.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent))
|
||||
} else {
|
||||
val memread = DirectMemoryRead(add, identifier.position)
|
||||
|
@ -18,8 +18,8 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
|
||||
// try to statically convert a literal value into one of the desired type
|
||||
val literal = typecast.expression as? NumericLiteral
|
||||
if (literal != null) {
|
||||
val newLiteral = literal.cast(typecast.type, typecast.implicit)
|
||||
if (literal != null && typecast.type.isBasic) {
|
||||
val newLiteral = literal.cast(typecast.type.base, typecast.implicit)
|
||||
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
|
||||
mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
|
||||
}
|
||||
@ -33,7 +33,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
|
||||
}
|
||||
} else {
|
||||
if (typecast.expression.inferType(program) issimpletype typecast.type) {
|
||||
if (typecast.expression.inferType(program) istype typecast.type) {
|
||||
// remove duplicate cast
|
||||
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
|
||||
}
|
||||
@ -49,7 +49,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
if(truepart.statements.singleOrNull() is Jump) {
|
||||
return listOf(
|
||||
IAstModification.InsertAfter(ifElse, elsepart, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse)
|
||||
IAstModification.ReplaceNode(elsepart, AnonymousScope.empty(), ifElse)
|
||||
)
|
||||
}
|
||||
if(elsepart.statements.singleOrNull() is Jump) {
|
||||
@ -57,7 +57,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
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(elsepart, AnonymousScope.empty(), ifElse),
|
||||
IAstModification.ReplaceNode(truepart, elsepart, ifElse)
|
||||
)
|
||||
}
|
||||
@ -423,6 +423,29 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(arrayIndexedExpression.indexer.constIndex()==0) {
|
||||
if(arrayIndexedExpression.plainarrayvar!=null) {
|
||||
val dt = arrayIndexedExpression.plainarrayvar!!.inferType(program).getOrUndef()
|
||||
if(dt.isPointer) {
|
||||
// pointer[0] --> pointer^^
|
||||
val deref = PtrDereference(arrayIndexedExpression.plainarrayvar!!.nameInSource, true, arrayIndexedExpression.plainarrayvar!!.position)
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression,deref, parent))
|
||||
}
|
||||
}
|
||||
val ptrDeref = arrayIndexedExpression.pointerderef
|
||||
if(ptrDeref!=null) {
|
||||
val dt = ptrDeref.inferType(program).getOrUndef()
|
||||
if(dt.isPointer) {
|
||||
// ptr1.ptr2[0] --> ptr1.ptr2^^
|
||||
val deref = PtrDereference(ptrDeref.chain, true, ptrDeref.position)
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, deref, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun applyAbsorptionLaws(expr: BinaryExpression): Expression? {
|
||||
// NOTE: only when the terms are not function calls!!!
|
||||
if(expr.left is IFunctionCall || expr.right is IFunctionCall)
|
||||
@ -499,7 +522,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
else if (valueDt issimpletype BaseDataType.BYTE) {
|
||||
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
|
||||
val cast = TypecastExpression(arg.expression, BaseDataType.UBYTE, true, arg.position)
|
||||
val cast = TypecastExpression(arg.expression, DataType.UBYTE, true, arg.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
} else {
|
||||
@ -516,7 +539,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
else if (argDt issimpletype BaseDataType.BYTE) {
|
||||
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
|
||||
val cast = TypecastExpression(arg, BaseDataType.UBYTE, true, arg.position)
|
||||
val cast = TypecastExpression(arg, DataType.UBYTE, true, arg.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
}
|
||||
@ -555,12 +578,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
if(functionCallExpr.target.nameInSource == listOf("mkword")) {
|
||||
if(functionCallExpr.args[0].constValue(program)?.number==0.0) {
|
||||
// just cast the lsb to uword
|
||||
val cast = TypecastExpression(functionCallExpr.args[1], BaseDataType.UWORD, true, functionCallExpr.position)
|
||||
val cast = TypecastExpression(functionCallExpr.args[1], DataType.UWORD, true, functionCallExpr.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
}
|
||||
else if(functionCallExpr.target.nameInSource == listOf("strings", "contains")) {
|
||||
val target = (functionCallExpr.args[0] as? IdentifierReference)?.targetVarDecl(program)
|
||||
val target = (functionCallExpr.args[0] as? IdentifierReference)?.targetVarDecl()
|
||||
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)
|
||||
@ -711,9 +734,9 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
// just use: msb(value) as type
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return if(leftDt.isSignedWord)
|
||||
TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
|
||||
TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
else
|
||||
TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
|
||||
TypecastExpression(msb, DataType.UWORD, true, expr.position)
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
@ -839,14 +862,14 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
// shift left by 8 bits is just a byte operation: mkword(lsb(X), 0)
|
||||
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)), expr.position)
|
||||
return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position)
|
||||
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
|
||||
}
|
||||
else if (amount > 8) {
|
||||
// same as above but with residual shifts.
|
||||
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
val shifted = BinaryExpression(lsb, "<<", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
|
||||
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position)
|
||||
return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position)
|
||||
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
@ -887,12 +910,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
else if(amount==8) {
|
||||
// shift right by 8 bits is just a byte operation: msb(X) as uword
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
|
||||
return TypecastExpression(msb, DataType.UWORD, true, expr.position)
|
||||
}
|
||||
else if (amount > 8) {
|
||||
// same as above but with residual shifts.
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), BaseDataType.UWORD, true, expr.position)
|
||||
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position)
|
||||
}
|
||||
}
|
||||
BaseDataType.WORD -> {
|
||||
@ -903,12 +926,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
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, BaseDataType.BYTE, true, 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, BaseDataType.BYTE, true, expr.position)
|
||||
val signed = TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
return BinaryExpression(signed, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import prog8.code.target.VMTarget
|
||||
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.values.isEmpty()
|
||||
|
||||
|
||||
// inliner potentially enables *ONE LINED* subroutines, wihtout to be inlined.
|
||||
// inliner potentially enables *ONE LINED* subroutines, without to be inlined.
|
||||
|
||||
class Inliner(private val program: Program, private val options: CompilationOptions): AstWalker() {
|
||||
|
||||
@ -105,7 +105,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
|
||||
if (subroutine.inline && subroutine.statements.size > 1) {
|
||||
require(subroutine.statements.size == 2 && isEmptyReturn(subroutine.statements[1]))
|
||||
subroutine.statements.removeLast() // get rid of the Return, to be able to inline the (single) statement preceding it.
|
||||
subroutine.statements.removeLastOrNull() // get rid of the Return, to be able to inline the (single) statement preceding it.
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,7 +113,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
}
|
||||
|
||||
private fun makeFullyScoped(identifier: IdentifierReference) {
|
||||
identifier.targetStatement(program)?.let { target ->
|
||||
identifier.targetStatement()?.let { target ->
|
||||
val scoped = (target as INamedStatement).scopedName
|
||||
val scopedIdent = IdentifierReference(scoped, identifier.position)
|
||||
modifications += IAstModification.ReplaceNode(identifier, scopedIdent, identifier.parent)
|
||||
@ -122,7 +122,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
|
||||
private fun makeFullyScoped(call: FunctionCallStatement) {
|
||||
makeFullyScoped(call.target)
|
||||
call.target.targetSubroutine(program)?.let { sub ->
|
||||
call.target.targetSubroutine()?.let { sub ->
|
||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||
val scopedArgs = makeScopedArgs(call.args)
|
||||
if(scopedArgs.any()) {
|
||||
@ -134,7 +134,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
|
||||
private fun makeFullyScoped(call: FunctionCallExpression) {
|
||||
makeFullyScoped(call.target)
|
||||
call.target.targetSubroutine(program)?.let { sub ->
|
||||
call.target.targetSubroutine()?.let { sub ->
|
||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||
val scopedArgs = makeScopedArgs(call.args)
|
||||
if(scopedArgs.any()) {
|
||||
@ -149,7 +149,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
when (it) {
|
||||
is NumericLiteral -> it.copy()
|
||||
is IdentifierReference -> {
|
||||
val target = it.targetStatement(program) ?: return emptyList()
|
||||
val target = it.targetStatement() ?: return emptyList()
|
||||
val scoped = (target as INamedStatement).scopedName
|
||||
IdentifierReference(scoped, it.position)
|
||||
}
|
||||
@ -205,7 +205,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
}
|
||||
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
|
||||
val sub = functionCallStatement.target.targetStatement(program.builtinFunctions) as? Subroutine
|
||||
return if(sub==null || !canInline(sub, functionCallStatement))
|
||||
noModifications
|
||||
else
|
||||
@ -213,7 +213,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
}
|
||||
|
||||
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||
val sub = functionCallExpr.target.targetStatement(program) as? Subroutine
|
||||
val sub = functionCallExpr.target.targetStatement(program.builtinFunctions) as? Subroutine
|
||||
if(sub!=null && sub.inline && sub.parameters.isEmpty() && canInline(sub, functionCallExpr)) {
|
||||
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) {
|
||||
"invalid inline sub at ${sub.position}"
|
||||
@ -249,7 +249,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
val stmt = sub.statements.single()
|
||||
if (stmt is IFunctionCall) {
|
||||
val existing = (fcall as Node).definingScope.lookup(stmt.target.nameInSource.take(1))
|
||||
return existing !is VarDecl
|
||||
return existing !is VarDecl && existing !is StructFieldRef
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
@ -29,12 +29,19 @@ class StatementOptimizer(private val program: Program,
|
||||
if(functionCallStatement.target.nameInSource==listOf("txt", "print")) {
|
||||
val arg = functionCallStatement.args.single()
|
||||
val stringVar: IdentifierReference? = if(arg is AddressOf) {
|
||||
if(arg.arrayIndex==null) arg.identifier else null
|
||||
if(arg.arrayIndex==null) {
|
||||
if(arg.identifier!=null)
|
||||
arg.identifier
|
||||
else
|
||||
null // struct can't have string fields so nothing to look at here
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
arg as? IdentifierReference
|
||||
}
|
||||
if(stringVar!=null && stringVar.wasStringLiteral(program)) {
|
||||
val string = stringVar.targetVarDecl(program)?.value as? StringLiteral
|
||||
if(stringVar!=null && stringVar.wasStringLiteral()) {
|
||||
val string = stringVar.targetVarDecl()?.value as? StringLiteral
|
||||
if(string!=null) {
|
||||
val pos = functionCallStatement.position
|
||||
if (string.value.length == 1) {
|
||||
@ -82,12 +89,11 @@ class StatementOptimizer(private val program: Program,
|
||||
// empty true part? switch with the else part
|
||||
if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) {
|
||||
val invertedCondition = invertCondition(ifElse.condition, program)
|
||||
val emptyscope = AnonymousScope(mutableListOf(), ifElse.elsepart.position)
|
||||
val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.truepart, truepart, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, emptyscope, ifElse)
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope.empty(), ifElse)
|
||||
)
|
||||
}
|
||||
|
||||
@ -106,7 +112,7 @@ class StatementOptimizer(private val program: Program,
|
||||
if(ifElse.truepart.statements.singleOrNull() is Return) {
|
||||
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope.empty(), ifElse),
|
||||
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
@ -146,13 +152,13 @@ class StatementOptimizer(private val program: Program,
|
||||
if (range.size() == 1) {
|
||||
// for loop over a (constant) range of just a single value-- optimize the loop away
|
||||
// loopvar/reg = range value , follow by block
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
val scope = AnonymousScope.empty(forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, position=forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
}
|
||||
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl(program)
|
||||
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl()
|
||||
if(iterable!=null) {
|
||||
if(iterable.datatype.isString) {
|
||||
val sv = iterable.value as StringLiteral
|
||||
@ -161,8 +167,8 @@ class StatementOptimizer(private val program: Program,
|
||||
// loop over string of length 1 -> just assign the single character
|
||||
val character = options.compTarget.encodeString(sv.value, sv.encoding)[0]
|
||||
val byte = NumericLiteral(BaseDataType.UBYTE, character.toDouble(), iterable.position)
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
val scope = AnonymousScope.empty(forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, position=forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
@ -173,9 +179,9 @@ class StatementOptimizer(private val program: Program,
|
||||
// loop over array of length 1 -> just assign the single value
|
||||
val av = (iterable.value as ArrayLiteral).value[0].constValue(program)?.number
|
||||
if(av!=null) {
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
val scope = AnonymousScope.empty(forLoop.position)
|
||||
scope.statements.add(Assignment(
|
||||
AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
|
||||
AssignTarget(forLoop.loopVar, null, null, null, false, position = forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
|
||||
AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
@ -466,7 +472,7 @@ class StatementOptimizer(private val program: Program,
|
||||
return IfElse(
|
||||
compare,
|
||||
AnonymousScope(mutableListOf(assign), position),
|
||||
AnonymousScope(mutableListOf(), position),
|
||||
AnonymousScope.empty(),
|
||||
position
|
||||
)
|
||||
}
|
||||
@ -501,6 +507,27 @@ class StatementOptimizer(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(whenStmt.betterAsOnGoto(program, options)) {
|
||||
// rewrite when into a on..goto , which is faster and also smaller for ~5+ cases
|
||||
var elseJump: Jump? = null
|
||||
val jumps = mutableListOf<Pair<Int, Jump>>()
|
||||
whenStmt.choices.forEach { choice ->
|
||||
if(choice.values==null) {
|
||||
elseJump = choice.statements.statements.single() as Jump
|
||||
} else {
|
||||
choice.values!!.forEach { value ->
|
||||
jumps.add(value.constValue(program)!!.number.toInt() to choice.statements.statements.single() as Jump)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val jumpLabels = jumps.sortedBy { it.first }.map { it.second.target as IdentifierReference }
|
||||
|
||||
val elsePart = if(elseJump==null) null else AnonymousScope(mutableListOf(elseJump), elseJump.position)
|
||||
val onGoto = OnGoto(false, whenStmt.condition, jumpLabels, elsePart, whenStmt.position)
|
||||
return listOf(IAstModification.ReplaceNode(whenStmt, onGoto, parent))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
@ -8,9 +8,9 @@ import prog8.ast.expressions.TypecastExpression
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.compiler.CallGraph
|
||||
|
||||
|
||||
@ -27,10 +27,10 @@ class UnusedCodeRemover(private val program: Program,
|
||||
|
||||
program.allBlocks.singleOrNull { it.name=="sys" } ?.let {
|
||||
val subroutines = it.statements.filterIsInstance<Subroutine>()
|
||||
val push = subroutines.single { it.name == "push" }
|
||||
val pushw = subroutines.single { it.name == "pushw" }
|
||||
val pop = subroutines.single { it.name == "pop" }
|
||||
val popw = subroutines.single { it.name == "popw" }
|
||||
val push = subroutines.single { s -> s.name == "push" }
|
||||
val pushw = subroutines.single { s -> s.name == "pushw" }
|
||||
val pop = subroutines.single { s -> s.name == "pop" }
|
||||
val popw = subroutines.single { s -> s.name == "popw" }
|
||||
neverRemoveSubroutines.add(push)
|
||||
neverRemoveSubroutines.add(pushw)
|
||||
neverRemoveSubroutines.add(pop)
|
||||
@ -39,8 +39,8 @@ class UnusedCodeRemover(private val program: Program,
|
||||
|
||||
program.allBlocks.singleOrNull { it.name=="floats" } ?.let {
|
||||
val subroutines = it.statements.filterIsInstance<Subroutine>()
|
||||
val push = subroutines.single { it.name == "push" }
|
||||
val pop = subroutines.single { it.name == "pop" }
|
||||
val push = subroutines.single { s -> s.name == "push" }
|
||||
val pop = subroutines.single { s -> s.name == "pop" }
|
||||
neverRemoveSubroutines.add(push)
|
||||
neverRemoveSubroutines.add(pop)
|
||||
}
|
||||
@ -181,14 +181,20 @@ 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) {
|
||||
if("ignore_unused" !in decl.definingBlock.options())
|
||||
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(
|
||||
IAstModification.ReplaceNode(decl, voidCall, parent),
|
||||
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
|
||||
)
|
||||
val callStruct = (assignment.value as IFunctionCall).target.targetStructDecl()
|
||||
if(callStruct!=null) {
|
||||
// don't turn a struct instance allocation call to a void call, instead, remove everything
|
||||
return listOf(IAstModification.Remove(assignment, assignment.parent as IStatementContainer))
|
||||
} else {
|
||||
if("ignore_unused" !in decl.definingBlock.options())
|
||||
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(
|
||||
IAstModification.ReplaceNode(decl, voidCall, parent),
|
||||
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1,5 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
id("application")
|
||||
kotlin("jvm")
|
||||
@ -18,7 +16,6 @@ dependencies {
|
||||
implementation(project(":codeGenExperimental"))
|
||||
implementation(project(":virtualmachine"))
|
||||
// implementation(project(":beanshell"))
|
||||
implementation("org.antlr:antlr4-runtime:4.13.2")
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.6")
|
||||
|
@ -25,6 +25,5 @@
|
||||
<orderEntry type="module" module-name="codeGenIntermediate" />
|
||||
<orderEntry type="module" module-name="virtualmachine" />
|
||||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="library" name="antlr.antlr4" level="project" />
|
||||
</component>
|
||||
</module>
|
@ -419,12 +419,14 @@ sys {
|
||||
|
||||
const ubyte target = 128 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
|
||||
|
||||
const ubyte SIZEOF_BOOL = 1
|
||||
const ubyte SIZEOF_BYTE = 1
|
||||
const ubyte SIZEOF_UBYTE = 1
|
||||
const ubyte SIZEOF_WORD = 2
|
||||
const ubyte SIZEOF_UWORD = 2
|
||||
const ubyte SIZEOF_FLOAT = 5
|
||||
const ubyte SIZEOF_BOOL = sizeof(bool)
|
||||
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@ -946,6 +948,18 @@ _no_msb_size
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
|
||||
%asm {{
|
||||
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
@ -439,12 +439,14 @@ sys {
|
||||
|
||||
const ubyte target = 64 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
|
||||
|
||||
const ubyte SIZEOF_BOOL = 1
|
||||
const ubyte SIZEOF_BYTE = 1
|
||||
const ubyte SIZEOF_UBYTE = 1
|
||||
const ubyte SIZEOF_WORD = 2
|
||||
const ubyte SIZEOF_UWORD = 2
|
||||
const ubyte SIZEOF_FLOAT = 5
|
||||
const ubyte SIZEOF_BOOL = sizeof(bool)
|
||||
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@ -968,6 +970,18 @@ _no_msb_size
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
|
||||
%asm {{
|
||||
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
@ -28,12 +28,20 @@
|
||||
; that routine can for instance call current() (or just look at the active_task variable) to get the id of the next task to execute.
|
||||
; It has then to return a boolean: true=next task is to be executed, false=skip the task this time.
|
||||
; - in tasks: call yield() to pass control to the next task. Use the returned userdata value to do different things.
|
||||
; For now, you MUST call yield() only from the actual subroutine that has been registered as a task!
|
||||
; (this is because otherwise the cpu call stack gets messed up and an RTS in task1 could suddenly pop a return address belonging to another tasks' call frame)
|
||||
; - call current() to get the current task id.
|
||||
; - call kill(taskid) to kill a task by id.
|
||||
; - call killall() to kill all tasks.
|
||||
; - IMPORTANT: if you add the same subroutine multiple times, IT CANNOT DEPEND ON ANY LOCAL VARIABLES OR R0-R15 TO KEEP STATE. NOT EVEN REPEAT LOOP COUNTERS.
|
||||
; Those are all shared in the different tasks! You HAVE to use a mechanism around the userdata value (pointer?) to keep separate state elsewhere!
|
||||
; - IMPORTANT: ``defer`` cannot be used inside a coroutine that is reused for multiple tasks!!!
|
||||
;
|
||||
; TIP: HOW TO WAIT without BLOCKING other coroutines?
|
||||
; Make sure you call yield() in the waiting loop, for example:
|
||||
; uword timer = cbm.RDTIM16() + 60
|
||||
; while cbm.RDTIM16() != timer
|
||||
; void coroutines.yield()
|
||||
|
||||
coroutines {
|
||||
%option ignore_unused
|
||||
@ -41,19 +49,17 @@ coroutines {
|
||||
const ubyte MAX_TASKS = 64
|
||||
uword[MAX_TASKS] tasklist
|
||||
uword[MAX_TASKS] userdatas
|
||||
uword[MAX_TASKS] returnaddresses
|
||||
ubyte active_task
|
||||
uword supervisor
|
||||
|
||||
sub add(uword taskaddress, uword userdata) -> ubyte {
|
||||
sub add(uword @nozp taskaddress, uword @nozp userdata) -> ubyte {
|
||||
; find the next empty slot in the tasklist and stick it there
|
||||
; returns the task id of the new task, or 255 if there was no space for more tasks. 0 is a valid task id!
|
||||
; also returns the success in the Carry flag (carry set=success, carry clear = task was not added)
|
||||
for cx16.r0L in 0 to len(tasklist)-1 {
|
||||
if tasklist[cx16.r0L] == 0 {
|
||||
tasklist[cx16.r0L] = taskaddress
|
||||
tasklist[cx16.r0L] = sys.get_as_returnaddress(taskaddress)
|
||||
userdatas[cx16.r0L] = userdata
|
||||
returnaddresses[cx16.r0L] = 0
|
||||
sys.set_carry()
|
||||
return cx16.r0L
|
||||
}
|
||||
@ -70,57 +76,48 @@ coroutines {
|
||||
}
|
||||
}
|
||||
|
||||
sub run(uword supervisor_routine) {
|
||||
sub run(uword @nozp supervisor_routine) {
|
||||
supervisor = supervisor_routine
|
||||
for active_task in 0 to len(tasklist)-1 {
|
||||
if tasklist[active_task]!=0 {
|
||||
; activate the termination handler and start the first task
|
||||
; note: cannot use pushw() because JSR doesn't push the return address in the same way
|
||||
sys.push_returnaddress(&termination)
|
||||
goto tasklist[active_task]
|
||||
sys.pushw(tasklist[active_task])
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub yield() -> uword {
|
||||
; Store the return address of the yielding task,
|
||||
; and continue with the next one instead (round-robin)
|
||||
; Returns the associated userdata value
|
||||
uword task_start, task_continue
|
||||
returnaddresses[active_task] = sys.popw()
|
||||
; Store the return address of the yielding task, and continue with the next one instead (round-robin)
|
||||
; Returns the associated userdata value.
|
||||
; NOTE: CAN ONLY BE CALLED FROM THE SCOPE OF THE SUBROUTINE THAT HAS BEEN REGISTERED AS THE TASK!
|
||||
uword task_return_address
|
||||
tasklist[active_task] = sys.popw()
|
||||
|
||||
resume_with_next_task:
|
||||
skip_task:
|
||||
if not next_task() {
|
||||
void sys.popw() ; remove return to the termination handler
|
||||
return 0 ; exiting here will now actually return from the start() call back to the calling program :)
|
||||
return 0 ; exiting here will now actually return back to the calling program that called run()
|
||||
}
|
||||
|
||||
if supervisor!=0 {
|
||||
if supervisor!=0
|
||||
if lsb(call(supervisor))==0
|
||||
goto resume_with_next_task
|
||||
}
|
||||
goto skip_task
|
||||
|
||||
if task_continue==0 {
|
||||
; fetch start address of next task.
|
||||
; address on the stack must be pushed in reverse byte order
|
||||
; also, subtract 1 from the start address because JSR pushes returnaddress minus 1
|
||||
; note: cannot use pushw() because JSR doesn't push the return address in the same way
|
||||
sys.push_returnaddress(task_start)
|
||||
} else
|
||||
sys.pushw(task_continue)
|
||||
|
||||
; returning from yield then continues with the next coroutine
|
||||
; returning from yield then continues with the next coroutine:
|
||||
sys.pushw(task_return_address)
|
||||
return userdatas[active_task]
|
||||
|
||||
sub next_task() -> bool {
|
||||
; search through the task list for the next active task
|
||||
repeat len(tasklist) {
|
||||
active_task++
|
||||
if active_task==len(returnaddresses)
|
||||
if active_task==len(tasklist)
|
||||
active_task=0
|
||||
task_start = tasklist[active_task]
|
||||
if task_start!=0 {
|
||||
task_continue = returnaddresses[active_task]
|
||||
task_return_address = tasklist[active_task]
|
||||
if task_return_address!=0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -128,9 +125,8 @@ resume_with_next_task:
|
||||
}
|
||||
}
|
||||
|
||||
sub kill(ubyte taskid) {
|
||||
sub kill(ubyte @nozp taskid) {
|
||||
tasklist[taskid] = 0
|
||||
returnaddresses[taskid] = 0
|
||||
}
|
||||
|
||||
sub current() -> ubyte {
|
||||
@ -138,12 +134,10 @@ resume_with_next_task:
|
||||
}
|
||||
|
||||
sub termination() {
|
||||
; a task has terminated. wipe it from the list.
|
||||
; this is an internal routine
|
||||
; internal routine: a task has terminated. wipe it from the list.
|
||||
kill(active_task)
|
||||
; reactivate this termination handler
|
||||
; note: cannot use pushw() because JSR doesn't push the return address in the same way
|
||||
; reactivate this termination handler and go to the next task
|
||||
sys.push_returnaddress(&termination)
|
||||
goto coroutines.yield.resume_with_next_task
|
||||
goto coroutines.yield.skip_task
|
||||
}
|
||||
}
|
||||
|
@ -22,18 +22,23 @@ monogfx {
|
||||
const ubyte MODE_STIPPLE = %00000001
|
||||
const ubyte MODE_INVERT = %00000010
|
||||
|
||||
uword buffer_visible, buffer_back
|
||||
|
||||
|
||||
sub lores() {
|
||||
; enable 320*240 bitmap mode
|
||||
buffer_visible = buffer_back = $0000
|
||||
cx16.VERA_CTRL=0
|
||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||
cx16.VERA_DC_HSCALE = 64
|
||||
cx16.VERA_DC_VSCALE = 64
|
||||
cx16.VERA_L1_CONFIG = %00000100
|
||||
cx16.VERA_L1_MAPBASE = 0
|
||||
cx16.VERA_L1_TILEBASE = 0
|
||||
cx16.VERA_L1_TILEBASE = 0 ; lores
|
||||
width = 320
|
||||
height = 240
|
||||
lores_mode = true
|
||||
buffer_visible = buffer_back = $0000
|
||||
mode = MODE_NORMAL
|
||||
clear_screen(false)
|
||||
}
|
||||
@ -46,14 +51,40 @@ monogfx {
|
||||
cx16.VERA_DC_VSCALE = 128
|
||||
cx16.VERA_L1_CONFIG = %00000100
|
||||
cx16.VERA_L1_MAPBASE = 0
|
||||
cx16.VERA_L1_TILEBASE = %00000001
|
||||
cx16.VERA_L1_TILEBASE = %00000001 ; hires
|
||||
width = 640
|
||||
height = 480
|
||||
lores_mode = false
|
||||
buffer_visible = buffer_back = $0000
|
||||
mode = MODE_NORMAL
|
||||
clear_screen(false)
|
||||
}
|
||||
|
||||
sub enable_doublebuffer() {
|
||||
; enable double buffering mode
|
||||
if lores_mode {
|
||||
buffer_visible = $0000
|
||||
buffer_back = $2800
|
||||
} else {
|
||||
buffer_visible = $0000
|
||||
buffer_back = $9800
|
||||
}
|
||||
}
|
||||
|
||||
sub swap_buffers(bool wait_for_vsync) {
|
||||
; flip the buffers: make the back buffer visible and the other one now the backbuffer.
|
||||
; to avoid any screen tearing it is advised to call this during the vertical blank (pass true)
|
||||
if wait_for_vsync
|
||||
sys.waitvsync()
|
||||
cx16.r0 = buffer_back
|
||||
buffer_back = buffer_visible
|
||||
buffer_visible = cx16.r0
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.r0 &= %1111110000000000
|
||||
cx16.VERA_L1_TILEBASE = cx16.VERA_L1_TILEBASE & 1 | (cx16.r0H >>1 )
|
||||
}
|
||||
|
||||
|
||||
sub textmode() {
|
||||
; back to normal text mode
|
||||
cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode
|
||||
@ -559,6 +590,7 @@ drawmode: ora cx16.r15L
|
||||
sub disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) {
|
||||
; Warning: NO BOUNDS CHECKS. Make sure circle fits in the screen.
|
||||
; Midpoint algorithm, filled
|
||||
; Note: has problems with INVERT draw mode because of horizontal span overdrawing. Horizontal lines may occur.
|
||||
if radius==0
|
||||
return
|
||||
ubyte @zp yy = 0
|
||||
@ -597,6 +629,7 @@ drawmode: ora cx16.r15L
|
||||
sub safe_disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) {
|
||||
; Does bounds checking and clipping.
|
||||
; Midpoint algorithm, filled
|
||||
; Note: has problems with INVERT draw mode because of horizontal span overdrawing. Horizontal lines may occur.
|
||||
if radius==0
|
||||
return
|
||||
ubyte @zp yy = 0
|
||||
@ -672,8 +705,7 @@ nostipple:
|
||||
invert:
|
||||
prepare()
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
eor p8v_maskbits,y
|
||||
eor cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
}}
|
||||
return
|
||||
@ -696,7 +728,7 @@ invert:
|
||||
adc p8v_times40_lsb,y
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda p8v_times40_msb,y
|
||||
adc #0
|
||||
adc p8v_buffer_back+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
|
||||
lda p8v_xx
|
||||
@ -708,18 +740,29 @@ invert:
|
||||
; width=640 (hires)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda p8v_xx
|
||||
and #7
|
||||
pha ; xbits
|
||||
|
||||
; xx /= 8
|
||||
lsr p8v_xx+1
|
||||
ror p8v_xx
|
||||
lsr p8v_xx+1
|
||||
ror p8v_xx
|
||||
lsr p8v_xx
|
||||
}}
|
||||
xx /= 8
|
||||
;xx /= 8
|
||||
xx += yy*(640/8)
|
||||
%asm {{
|
||||
lda p8v_xx+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda p8v_xx
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda p8v_xx+1
|
||||
clc
|
||||
adc p8v_buffer_back+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda #0
|
||||
rol a ; hi bit carry also needed when double-buffering
|
||||
sta cx16.VERA_ADDR_H
|
||||
plx ; xbits
|
||||
lda p8v_maskbits,x
|
||||
}}
|
||||
@ -761,11 +804,15 @@ invert:
|
||||
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda p8v_xx+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda p8v_xx
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda p8v_xx+1
|
||||
clc
|
||||
adc p8v_buffer_back+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda #0
|
||||
rol a ; hi bit carry also needed when double-buffering
|
||||
sta cx16.VERA_ADDR_H
|
||||
ply ; xbits
|
||||
lda p8s_plot.p8v_maskbits,y
|
||||
and cx16.VERA_DATA0
|
||||
@ -848,8 +895,8 @@ skip:
|
||||
}
|
||||
|
||||
sub fill_scanline_right() {
|
||||
; TODO maybe this could use vera auto increment, but that requires some clever masking calculations
|
||||
cx16.r9s = xx
|
||||
; TODO maybe this could use vera auto increment, but that requires some clever masking calculations
|
||||
cx16.r9s = xx
|
||||
while xx <= width-1 {
|
||||
if pgetset()
|
||||
break
|
||||
@ -884,11 +931,15 @@ skip:
|
||||
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda p8v_xpos+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda p8v_xpos
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda p8v_xpos+1
|
||||
clc
|
||||
adc p8v_buffer_back+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda #0
|
||||
rol a ; hi bit carry also needed when double-buffering
|
||||
sta cx16.VERA_ADDR_H
|
||||
ply ; xbits
|
||||
lda p8s_plot.p8v_maskbits,y
|
||||
and cx16.VERA_DATA0
|
||||
@ -935,12 +986,12 @@ _doplot beq +
|
||||
ror a
|
||||
lsr a
|
||||
lsr a
|
||||
clc
|
||||
ldy p8v_yy
|
||||
clc
|
||||
adc p8v_times40_lsb,y
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda p8v_times40_msb,y
|
||||
adc #0
|
||||
adc p8v_buffer_back+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda #%00010000 ; autoincr
|
||||
sta cx16.VERA_ADDR_H
|
||||
@ -948,7 +999,33 @@ _doplot beq +
|
||||
}
|
||||
else {
|
||||
cx16.r0 = yy*(640/8)
|
||||
cx16.vaddr(0, cx16.r0+(xx/8), 0, 1)
|
||||
;cx16.r0 += xx/8
|
||||
%asm {{
|
||||
ldy p8v_xx+1
|
||||
lda p8v_xx
|
||||
sty P8ZP_SCRATCH_B1
|
||||
lsr P8ZP_SCRATCH_B1
|
||||
ror a
|
||||
lsr P8ZP_SCRATCH_B1
|
||||
ror a
|
||||
lsr a
|
||||
clc
|
||||
adc cx16.r0
|
||||
sta cx16.r0
|
||||
bcc +
|
||||
inc cx16.r0+1
|
||||
+
|
||||
stz cx16.VERA_CTRL
|
||||
lda cx16.r0L
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.r0H
|
||||
clc
|
||||
adc p8v_buffer_back+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda #%00001000 ; autoincr (1 bit shifted)
|
||||
rol a ; hi bit carry also needed when double-buffering
|
||||
sta cx16.VERA_ADDR_H
|
||||
}}
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -1116,15 +1193,11 @@ cdraw_mod2 ora cx16.VERA_DATA1
|
||||
cmp #0
|
||||
beq +
|
||||
lda #255
|
||||
+ ldy #80
|
||||
- sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
+ ldy #40
|
||||
-
|
||||
.rept 16
|
||||
sta cx16.VERA_DATA0
|
||||
.endrept
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
|
@ -474,7 +474,7 @@ extsub $ff68 = mouse_config(byte shape @A, ubyte resX @X, ubyte resY @Y) clobbe
|
||||
extsub $ff6b = mouse_get(ubyte zdataptr @X) -> ubyte @A, byte @X ; use mouse_pos() instead
|
||||
extsub $ff71 = mouse_scan() clobbers(A, X, Y)
|
||||
extsub $ff53 = joystick_scan() clobbers(A, X, Y)
|
||||
extsub $ff56 = joystick_get(ubyte joynr @A) -> uword @AX, bool @Y ; note: everything is inverted
|
||||
extsub $ff56 = joystick_get(ubyte joynr @A) -> uword @AX, bool @Y ; note: everything is inverted even the boolean present flag. Also see detect_joysticks() and get_all_joysticks()
|
||||
|
||||
; X16Edit (rom bank 13/14 but you ideally should use the routine search_x16edit() to search for the correct bank)
|
||||
extsub $C000 = x16edit_default() clobbers(A,X,Y)
|
||||
@ -1168,8 +1168,39 @@ asmsub restore_vera_context() clobbers(A) {
|
||||
}}
|
||||
}
|
||||
|
||||
sub joysticks_detect() -> ubyte {
|
||||
; returns bits 0-4, set to 1 if that joystick is present.
|
||||
; bit 0 = keyboard joystick, bit 1 - 4 = joypads 1 to 4
|
||||
cx16.r0H = 255
|
||||
for cx16.r0L in 4 downto 0 {
|
||||
void cx16.joystick_get(cx16.r0L)
|
||||
%asm {{
|
||||
cpy #1 ; present?
|
||||
}}
|
||||
rol(cx16.r0H)
|
||||
}
|
||||
return ~cx16.r0H
|
||||
}
|
||||
|
||||
; Commander X16 IRQ dispatcher routines
|
||||
sub joysticks_getall(bool also_keyboard_js) -> uword {
|
||||
; returns combined pressed buttons from all connected joysticks
|
||||
; note: returns the 'normal' not inverted status bits for the buttons (1 = button pressed.)
|
||||
cx16.r0H = 1
|
||||
if also_keyboard_js
|
||||
cx16.r0H = 0
|
||||
cx16.r1 = $ffff
|
||||
for cx16.r0L in cx16.r0H to 4 {
|
||||
bool notpresent
|
||||
cx16.r2, notpresent = cx16.joystick_get(cx16.r0L)
|
||||
if not notpresent {
|
||||
cx16.r1 &= cx16.r2
|
||||
}
|
||||
}
|
||||
return ~cx16.r1
|
||||
}
|
||||
|
||||
|
||||
; Commander X16 IRQ dispatcher routines
|
||||
|
||||
inline asmsub disable_irqs() clobbers(A) {
|
||||
; Disable all Vera IRQ sources. Note that it does NOT set the CPU IRQ disabled status bit!
|
||||
@ -1216,6 +1247,7 @@ _vsync_vec .word ?
|
||||
_line_vec .word ?
|
||||
_aflow_vec .word ?
|
||||
_sprcol_vec .word ?
|
||||
_continue_with_system_handler .byte ?
|
||||
.send BSS
|
||||
|
||||
_irq_dispatcher
|
||||
@ -1224,44 +1256,42 @@ _irq_dispatcher
|
||||
cld
|
||||
lda cx16.VERA_ISR
|
||||
and cx16.VERA_IEN ; only consider the bits for sources that can actually raise the IRQ
|
||||
sta cx16.VERA_ISR ; note: AFLOW can only be cleared by filling the audio FIFO for at least 1/4. Not via the ISR bit.
|
||||
|
||||
bit #2
|
||||
stz _continue_with_system_handler
|
||||
|
||||
bit #2 ; make sure to test for LINE IRQ first to handle that as soon as we can
|
||||
beq +
|
||||
pha
|
||||
jsr _line_handler
|
||||
ldy #2
|
||||
sty cx16.VERA_ISR
|
||||
bra _dispatch_end
|
||||
+
|
||||
bit #4
|
||||
beq +
|
||||
jsr _sprcol_handler
|
||||
ldy #4
|
||||
sty cx16.VERA_ISR
|
||||
bra _dispatch_end
|
||||
+
|
||||
bit #8
|
||||
beq +
|
||||
jsr _aflow_handler
|
||||
; note: AFLOW can only be cleared by filling the audio FIFO for at least 1/4. Not via the ISR bit.
|
||||
bra _dispatch_end
|
||||
+
|
||||
bit #1
|
||||
beq +
|
||||
tsb _continue_with_system_handler
|
||||
pla
|
||||
|
||||
+ lsr a
|
||||
bcc +
|
||||
pha
|
||||
jsr _vsync_handler
|
||||
cmp #0
|
||||
bne _dispatch_end
|
||||
ldy #1
|
||||
sty cx16.VERA_ISR
|
||||
bra _return_irq
|
||||
+
|
||||
lda #0
|
||||
_dispatch_end
|
||||
cmp #0
|
||||
beq _return_irq
|
||||
jsr sys.restore_prog8_internals
|
||||
tsb _continue_with_system_handler
|
||||
pla
|
||||
|
||||
+ lsr a
|
||||
lsr a
|
||||
bcc +
|
||||
pha
|
||||
jsr _sprcol_handler
|
||||
tsb _continue_with_system_handler
|
||||
pla
|
||||
|
||||
+ lsr a
|
||||
bcc +
|
||||
jsr _aflow_handler
|
||||
tsb _continue_with_system_handler
|
||||
|
||||
+ jsr sys.restore_prog8_internals
|
||||
lda _continue_with_system_handler
|
||||
beq _no_sys_handler
|
||||
jmp (sys.restore_irq._orig_irqvec) ; continue with normal kernal irq routine
|
||||
_return_irq
|
||||
jsr sys.restore_prog8_internals
|
||||
_no_sys_handler
|
||||
ply
|
||||
plx
|
||||
pla
|
||||
@ -1485,12 +1515,14 @@ sys {
|
||||
|
||||
const ubyte target = 16 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
|
||||
|
||||
const ubyte SIZEOF_BOOL = 1
|
||||
const ubyte SIZEOF_BYTE = 1
|
||||
const ubyte SIZEOF_UBYTE = 1
|
||||
const ubyte SIZEOF_WORD = 2
|
||||
const ubyte SIZEOF_UWORD = 2
|
||||
const ubyte SIZEOF_FLOAT = 5
|
||||
const ubyte SIZEOF_BOOL = sizeof(bool)
|
||||
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@ -1997,6 +2029,18 @@ save_SCRATCH_ZPWORD2 .word ?
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
|
||||
%asm {{
|
||||
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
@ -119,6 +119,7 @@ verafx {
|
||||
; Returns the 16 bits unsigned result of R0*R1 in AY.
|
||||
; Note: only the lower 16 bits! (the upper 16 bits are not valid for unsigned word multiplications, only for signed)
|
||||
; Verafx doesn't support unsigned values like this for full 32 bit result.
|
||||
; Note: clobbers VRAM $1f9bc - $1f9bf (inclusive)
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
sta P8ZP_SCRATCH_W1
|
||||
@ -136,6 +137,7 @@ verafx {
|
||||
asmsub muls(word value1 @R0, word value2 @R1) clobbers(X) -> word @AY, word @R0 {
|
||||
; Returns the 32 bits signed result in AY and R0 (lower word, upper word).
|
||||
; Vera Fx multiplication support only works on signed values!
|
||||
; Note: clobbers VRAM $1f9bc - $1f9bf (inclusive)
|
||||
%asm {{
|
||||
lda #(2 << 1)
|
||||
sta cx16.VERA_CTRL ; $9F25
|
||||
|
@ -241,6 +241,28 @@ divmod_ub_asm .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
remainder_ub_asm .proc
|
||||
; -- divide A by Y, returns remainder in A (unsigned)
|
||||
; division by zero will result in just the original number.
|
||||
; This routine specialcases 0,1,2 and otherwise is just a repeated subtraction.
|
||||
cpy #0
|
||||
beq _zero
|
||||
cpy #1
|
||||
bne +
|
||||
lda #0
|
||||
rts
|
||||
+ cpy #2
|
||||
bne +
|
||||
and #1
|
||||
rts
|
||||
+ sty P8ZP_SCRATCH_REG
|
||||
sec
|
||||
- sbc P8ZP_SCRATCH_REG
|
||||
bcs -
|
||||
adc P8ZP_SCRATCH_REG
|
||||
_zero rts
|
||||
.pend
|
||||
|
||||
divmod_w_asm .proc
|
||||
; signed word division: make everything positive and fix sign afterwards
|
||||
sta P8ZP_SCRATCH_W2
|
||||
|
@ -102,43 +102,35 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
|
||||
}
|
||||
|
||||
sub randrange(ubyte n) -> ubyte {
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias).
|
||||
; NOTE: does not work for code in ROM
|
||||
cx16.r0H = 255 / n * n
|
||||
do {
|
||||
cx16.r0L = math.rnd()
|
||||
} until cx16.r0L < cx16.r0H
|
||||
return cx16.r0L % n
|
||||
; -- return random number uniformly distributed from 0 to n-1
|
||||
; NOTE: does not work for code in ROM, use randrange_rom instead for that
|
||||
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
|
||||
cx16.r0 = math.rnd() * (n as uword)
|
||||
return cx16.r0H
|
||||
}
|
||||
|
||||
sub randrange_rom(ubyte n) -> ubyte {
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias).
|
||||
; NOTE: works for code in ROM, make sure to initialize seed using rndseed_rom
|
||||
cx16.r0H = 255 / n * n
|
||||
do {
|
||||
cx16.r0L = math.rnd_rom()
|
||||
} until cx16.r0L < cx16.r0H
|
||||
return cx16.r0L % n
|
||||
; -- return random number uniformly distributed from 0 to n-1
|
||||
; NOTE: works for code in ROM, make sure you have initialized the seed using rndseed_rom
|
||||
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
|
||||
cx16.r0 = math.rnd_rom() * (n as uword)
|
||||
return cx16.r0H
|
||||
}
|
||||
|
||||
sub randrangew(uword n) -> uword {
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
|
||||
; -- return random number uniformly distributed from 0 to n-1
|
||||
; NOTE: does not work for code in ROM
|
||||
cx16.r1 = 65535 / n * n
|
||||
do {
|
||||
cx16.r0 = math.rndw()
|
||||
} until cx16.r0 < cx16.r1
|
||||
return cx16.r0 % n
|
||||
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
|
||||
cx16.r0 = math.rndw() * n
|
||||
return math.mul16_last_upper()
|
||||
}
|
||||
|
||||
sub randrangew_rom(uword n) -> uword {
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
|
||||
; NOTE: works for code in ROM, make sure to initialize seed using rndseed_rom
|
||||
cx16.r1 = 65535 / n * n
|
||||
do {
|
||||
cx16.r0 = math.rndw_rom()
|
||||
} until cx16.r0 < cx16.r1
|
||||
return cx16.r0 % n
|
||||
; -- return random number uniformly distributed from 0 to n-1
|
||||
; NOTE: works for code in ROM, make sure you have initialized the seed using rndseed_rom
|
||||
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
|
||||
cx16.r0 = math.rndw_rom() * n
|
||||
return math.mul16_last_upper()
|
||||
}
|
||||
|
||||
asmsub rndseed(uword seed1 @AY, uword seed2 @R0) clobbers(A,Y) {
|
||||
@ -705,25 +697,36 @@ log2_tab
|
||||
; Linear interpolation (LERP)
|
||||
; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 255]
|
||||
; guarantees v = v1 when t = 255
|
||||
return v0 + msb(t as uword * (v1 - v0) + 255)
|
||||
if v1<v0
|
||||
return v0 - msb(t as uword * (v0 - v1) + 255)
|
||||
else
|
||||
return v0 + msb(t as uword * (v1 - v0) + 255)
|
||||
}
|
||||
|
||||
sub lerpw(uword v0, uword v1, uword t) -> uword {
|
||||
; Linear interpolation (LERP) on word values
|
||||
; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 65535]
|
||||
; guarantees v = v1 when t = 65535
|
||||
; guarantees v = v1 when t = 65535. Clobbers R15.
|
||||
if v1<v0 {
|
||||
t *= v0-v1
|
||||
cx16.r15 = math.mul16_last_upper()
|
||||
if t!=0
|
||||
cx16.r15++
|
||||
return v0 - cx16.r15
|
||||
}
|
||||
t *= v1-v0
|
||||
cx16.r0 = math.mul16_last_upper()
|
||||
cx16.r15 = math.mul16_last_upper()
|
||||
if t!=0
|
||||
cx16.r0++
|
||||
return v0 + cx16.r0
|
||||
cx16.r15++
|
||||
return v0 + cx16.r15
|
||||
}
|
||||
|
||||
sub interpolate(ubyte v, ubyte inputMin, ubyte inputMax, ubyte outputMin, ubyte outputMax) -> ubyte {
|
||||
; Interpolate a value v in interval [inputMin, inputMax] to output interval [outputMin, outputMax]
|
||||
; There is no version for words because of lack of precision in the fixed point calculation there.
|
||||
cx16.r0 = ((v-inputMin)*256+inputMax) / (inputMax-inputMin)
|
||||
cx16.r0 *= (outputMax-outputMin)
|
||||
return cx16.r0H + outputMin
|
||||
; Clobbers R15.
|
||||
; (There is no version for words because of lack of precision in the fixed point calculation there)
|
||||
cx16.r15 = ((v-inputMin)*256+inputMax) / (inputMax-inputMin)
|
||||
cx16.r15 *= (outputMax-outputMin)
|
||||
return cx16.r15H + outputMin
|
||||
}
|
||||
}
|
||||
|
@ -98,12 +98,14 @@ sys {
|
||||
|
||||
const ubyte target = 32 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
|
||||
|
||||
const ubyte SIZEOF_BOOL = 1
|
||||
const ubyte SIZEOF_BYTE = 1
|
||||
const ubyte SIZEOF_UBYTE = 1
|
||||
const ubyte SIZEOF_WORD = 2
|
||||
const ubyte SIZEOF_UWORD = 2
|
||||
const ubyte SIZEOF_FLOAT = 0 ; undefined, no floats supported
|
||||
const ubyte SIZEOF_BOOL = sizeof(bool)
|
||||
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@ -481,6 +483,18 @@ save_SCRATCH_ZPWORD2 .word ?
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
|
||||
%asm {{
|
||||
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
@ -43,46 +43,72 @@ _done
|
||||
}}
|
||||
}
|
||||
|
||||
/*
|
||||
prog8 source code for the above routine:
|
||||
|
||||
sub gnomesort_ub(uword @requirezp values, ubyte num_elements) {
|
||||
sub gnomesort_by_ub(uword @requirezp uw_keys, uword values, ubyte num_elements) {
|
||||
; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse).
|
||||
ubyte @zp pos=1
|
||||
while pos != num_elements {
|
||||
if values[pos]>=values[pos-1]
|
||||
if uw_keys[pos]>=uw_keys[pos-1]
|
||||
pos++
|
||||
else {
|
||||
; swap elements
|
||||
cx16.r0L = values[pos-1]
|
||||
values[pos-1] = values[pos]
|
||||
values[pos] = cx16.r0L
|
||||
cx16.r0L = uw_keys[pos-1]
|
||||
uw_keys[pos-1] = uw_keys[pos]
|
||||
uw_keys[pos] = cx16.r0L
|
||||
uword @requirezp vptr = values + pos*$0002 -2
|
||||
cx16.r0 = peekw(vptr)
|
||||
pokew(vptr, peekw(vptr+2))
|
||||
pokew(vptr+2, cx16.r0)
|
||||
|
||||
pos--
|
||||
if_z
|
||||
pos++
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
sub gnomesort_uw(uword values, ubyte num_elements) {
|
||||
; TODO optimize this more, rewrite in asm?
|
||||
ubyte @zp pos = 1
|
||||
uword @requirezp ptr = values+2
|
||||
sub gnomesort_uw(uword @requirezp values, ubyte num_elements) {
|
||||
; Sorts the values array (no-split unsigned words).
|
||||
; Max number of elements is 128. Clobbers R0 and R1.
|
||||
ubyte @zp pos=2
|
||||
num_elements *= 2
|
||||
while pos != num_elements {
|
||||
cx16.r0 = peekw(ptr-2)
|
||||
cx16.r1 = peekw(ptr)
|
||||
if cx16.r0<=cx16.r1 {
|
||||
pos++
|
||||
ptr+=2
|
||||
}
|
||||
cx16.r1L = pos-2
|
||||
if peekw(values+pos) >= peekw(values + cx16.r1L)
|
||||
pos += 2
|
||||
else {
|
||||
; swap elements
|
||||
pokew(ptr-2, cx16.r1)
|
||||
pokew(ptr, cx16.r0)
|
||||
if pos>1 {
|
||||
pos--
|
||||
ptr-=2
|
||||
}
|
||||
cx16.r0 = peekw(values + cx16.r1L)
|
||||
pokew(values + cx16.r1L, peekw(values + pos))
|
||||
pokew(values + pos, cx16.r0)
|
||||
pos-=2
|
||||
if_z
|
||||
pos+=2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub gnomesort_by_uw(uword @requirezp uw_keys, uword wordvalues, ubyte num_elements) {
|
||||
; Sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse).
|
||||
; both arrays should be no-split array of words. uw_keys are unsigned.
|
||||
; Max number of elements is 128. Clobbers R0 and R1.
|
||||
ubyte @zp pos=2
|
||||
num_elements *= 2
|
||||
while pos != num_elements {
|
||||
cx16.r1L = pos-2
|
||||
if peekw(uw_keys+pos) >= peekw(uw_keys + cx16.r1L)
|
||||
pos += 2
|
||||
else {
|
||||
; swap elements
|
||||
cx16.r0 = peekw(uw_keys + cx16.r1L)
|
||||
pokew(uw_keys + cx16.r1L, peekw(uw_keys+ pos))
|
||||
pokew(uw_keys + pos, cx16.r0)
|
||||
cx16.r0 = peekw(wordvalues + cx16.r1L)
|
||||
pokew(wordvalues + cx16.r1L, peekw(wordvalues + pos))
|
||||
pokew(wordvalues + pos, cx16.r0)
|
||||
|
||||
pos-=2
|
||||
if_z
|
||||
pos+=2
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -90,6 +116,7 @@ _done
|
||||
; gnomesort_pointers is not worth it over shellshort_pointers.
|
||||
|
||||
sub shellsort_ub(uword @requirezp values, ubyte num_elements) {
|
||||
; sorts the values array (unsigned bytes).
|
||||
num_elements--
|
||||
ubyte @zp gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
@ -112,6 +139,7 @@ _done
|
||||
}
|
||||
|
||||
sub shellsort_uw(uword @requirezp values, ubyte num_elements) {
|
||||
; sorts the values array (no-split unsigned words).
|
||||
num_elements--
|
||||
ubyte gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
@ -121,13 +149,65 @@ _done
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
while j>=gap {
|
||||
uword @zp v = peekw(values+k*2)
|
||||
uword @zp v = peekw(values+k*$0002)
|
||||
if v <= temp break
|
||||
pokew(values+j*2, v)
|
||||
pokew(values+j*$0002, v)
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
pokew(values+j*2, temp)
|
||||
pokew(values+j*$0002, temp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub shellsort_by_ub(uword @requirezp ub_keys, uword @requirezp wordvalues, ubyte num_elements) {
|
||||
; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse).
|
||||
num_elements--
|
||||
ubyte @zp gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
ubyte i
|
||||
for i in gap to num_elements {
|
||||
ubyte @zp temp = ub_keys[i]
|
||||
uword temp_wv = peekw(wordvalues + i*$0002)
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
repeat {
|
||||
ubyte @zp v = ub_keys[k]
|
||||
if v <= temp break
|
||||
if j < gap break
|
||||
ub_keys[j] = v
|
||||
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
ub_keys[j] = temp
|
||||
pokew(wordvalues + j*$0002, temp_wv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub shellsort_by_uw(uword @requirezp uw_keys, uword @requirezp wordvalues, ubyte num_elements) {
|
||||
; sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse).
|
||||
; both arrays should be no-split array of words. uw_keys are unsigned.
|
||||
num_elements--
|
||||
ubyte gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
ubyte i
|
||||
for i in gap to num_elements {
|
||||
uword @zp temp = peekw(uw_keys+i*$0002)
|
||||
uword temp_wv = peekw(wordvalues + i*$0002)
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
while j>=gap {
|
||||
uword @zp v = peekw(uw_keys+k*2)
|
||||
if v <= temp break
|
||||
pokew(uw_keys+j*2, v)
|
||||
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
pokew(uw_keys+j*2, temp)
|
||||
pokew(wordvalues + j*$0002, temp_wv)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -144,14 +224,14 @@ _done
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
while j>=gap {
|
||||
cx16.r0 = peekw(pointers+k*2)
|
||||
cx16.r0 = peekw(pointers+k*$0002)
|
||||
void call(comparefunc)
|
||||
if_cs break
|
||||
pokew(pointers+j*2, cx16.r0)
|
||||
pokew(pointers+j*$0002, cx16.r0)
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
pokew(pointers+j*2, cx16.r1)
|
||||
pokew(pointers+j*$0002, cx16.r1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -151,6 +151,33 @@ _found tya
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub find_eol(uword string @AY) -> ubyte @A, bool @Pc {
|
||||
; Locates the position of the first End Of Line character in the string.
|
||||
; This is a convenience function that looks for both a CR or LF (byte 13 or byte 10) as being a possible Line Ending.
|
||||
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
|
||||
%asm {{
|
||||
; 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
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
beq _notfound
|
||||
cmp #13
|
||||
beq _found
|
||||
cmp #10
|
||||
beq _found
|
||||
iny
|
||||
bne -
|
||||
_notfound lda #255
|
||||
clc
|
||||
rts
|
||||
_found tya
|
||||
sec
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub rfind(uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc {
|
||||
; Locates the first position of the given character in the string, starting from the right.
|
||||
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
|
||||
@ -391,16 +418,18 @@ fail clc ; yes, no match found, return with c=0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub hash(str string @R0) -> ubyte @A {
|
||||
asmsub hash(str string @AY) -> ubyte @A {
|
||||
; experimental 8 bit hashing function.
|
||||
; hash(-1)=179; clear carry; hash(i) = ROL hash(i-1) XOR string[i]
|
||||
; On the English word list in /usr/share/dict/words it seems to have a pretty even distribution
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #179
|
||||
sta P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
clc
|
||||
- lda (cx16.r0),y
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
beq +
|
||||
rol P8ZP_SCRATCH_REG
|
||||
eor P8ZP_SCRATCH_REG
|
||||
|
@ -177,21 +177,17 @@ math {
|
||||
}
|
||||
|
||||
sub randrange(ubyte n) -> ubyte {
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
|
||||
cx16.r0H = 255 / n * n
|
||||
do {
|
||||
cx16.r0L = math.rnd()
|
||||
} until cx16.r0L < cx16.r0H
|
||||
return cx16.r0L % n
|
||||
; -- return random number uniformly distributed from 0 to n-1
|
||||
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
|
||||
cx16.r0 = math.rnd() * (n as uword)
|
||||
return cx16.r0H
|
||||
}
|
||||
|
||||
sub randrangew(uword n) -> uword {
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
|
||||
cx16.r1 = 65535 / n * n
|
||||
do {
|
||||
cx16.r0 = math.rndw()
|
||||
} until cx16.r0 < cx16.r1
|
||||
return cx16.r0 % n
|
||||
; -- return random number uniformly distributed from 0 to n-1
|
||||
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
|
||||
cx16.r0 = math.rndw() * n
|
||||
return math.mul16_last_upper()
|
||||
}
|
||||
|
||||
sub rndseed(uword seed1, uword seed2) {
|
||||
@ -408,25 +404,36 @@ math {
|
||||
; Linear interpolation (LERP)
|
||||
; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 255]
|
||||
; guarantees v = v1 when t = 255
|
||||
return v0 + msb(t as uword * (v1 - v0) + 255)
|
||||
if v1<v0
|
||||
return v0 - msb(t as uword * (v0 - v1) + 255)
|
||||
else
|
||||
return v0 + msb(t as uword * (v1 - v0) + 255)
|
||||
}
|
||||
|
||||
sub lerpw(uword v0, uword v1, uword t) -> uword {
|
||||
; Linear interpolation (LERP) on word values
|
||||
; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 65535]
|
||||
; guarantees v = v1 when t = 65535
|
||||
; guarantees v = v1 when t = 65535. Clobbers R15.
|
||||
if v1<v0 {
|
||||
t *= v0-v1
|
||||
cx16.r15 = math.mul16_last_upper()
|
||||
if t!=0
|
||||
cx16.r15++
|
||||
return v0 - cx16.r15
|
||||
}
|
||||
t *= v1-v0
|
||||
cx16.r0 = math.mul16_last_upper()
|
||||
cx16.r15 = math.mul16_last_upper()
|
||||
if t!=0
|
||||
cx16.r0++
|
||||
return v0 + cx16.r0
|
||||
cx16.r15++
|
||||
return v0 + cx16.r15
|
||||
}
|
||||
|
||||
sub interpolate(ubyte v, ubyte inputMin, ubyte inputMax, ubyte outputMin, ubyte outputMax) -> ubyte {
|
||||
; Interpolate a value v in interval [inputMin, inputMax] to output interval [outputMin, outputMax]
|
||||
; There is no version for words because of lack of precision in the fixed point calculation there.
|
||||
cx16.r0 = ((v-inputMin)*256+inputMax) / (inputMax-inputMin)
|
||||
cx16.r0 *= (outputMax-outputMin)
|
||||
return cx16.r0H + outputMin
|
||||
; Clobbers R15.
|
||||
; (There is no version for words because of lack of precision in the fixed point calculation there)
|
||||
cx16.r15 = ((v-inputMin)*256+inputMax) / (inputMax-inputMin)
|
||||
cx16.r15 *= (outputMax-outputMin)
|
||||
return cx16.r15H + outputMin
|
||||
}
|
||||
}
|
||||
|
103
compiler/res/prog8lib/virtual/sorting.p8
Normal file
103
compiler/res/prog8lib/virtual/sorting.p8
Normal file
@ -0,0 +1,103 @@
|
||||
; **experimental** data sorting routines, API subject to change!!
|
||||
|
||||
; NOTE: gnomesort is not implemented here, just use shellshort.
|
||||
|
||||
sorting {
|
||||
%option ignore_unused
|
||||
|
||||
sub shellsort_ub(uword @requirezp values, ubyte num_elements) {
|
||||
num_elements--
|
||||
ubyte @zp gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
ubyte i
|
||||
for i in gap to num_elements {
|
||||
ubyte @zp temp = values[i]
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
repeat {
|
||||
ubyte @zp v = values[k]
|
||||
if v <= temp break
|
||||
if j < gap break
|
||||
values[j] = v
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
values[j] = temp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub shellsort_uw(uword @requirezp values, ubyte num_elements) {
|
||||
num_elements--
|
||||
ubyte gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
ubyte i
|
||||
for i in gap to num_elements {
|
||||
uword @zp temp = peekw(values+i*$0002)
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
while j>=gap {
|
||||
uword @zp v = peekw(values+k*2)
|
||||
if v <= temp break
|
||||
pokew(values+j*2, v)
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
pokew(values+j*2, temp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub shellsort_by_ub(uword @requirezp ub_keys, uword @requirezp wordvalues, ubyte num_elements) {
|
||||
; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse).
|
||||
num_elements--
|
||||
ubyte @zp gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
ubyte i
|
||||
for i in gap to num_elements {
|
||||
ubyte @zp temp = ub_keys[i]
|
||||
uword temp_wv = peekw(wordvalues + i*$0002)
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
repeat {
|
||||
ubyte @zp v = ub_keys[k]
|
||||
if v <= temp break
|
||||
if j < gap break
|
||||
ub_keys[j] = v
|
||||
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
ub_keys[j] = temp
|
||||
pokew(wordvalues + j*$0002, temp_wv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub shellsort_by_uw(uword @requirezp uw_keys, uword @requirezp wordvalues, ubyte num_elements) {
|
||||
; sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse).
|
||||
; both arrays should be no-split array of words. uw_keys are unsigned.
|
||||
num_elements--
|
||||
ubyte gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
ubyte i
|
||||
for i in gap to num_elements {
|
||||
uword @zp temp = peekw(uw_keys+i*$0002)
|
||||
uword temp_wv = peekw(wordvalues + i*$0002)
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
while j>=gap {
|
||||
uword @zp v = peekw(uw_keys+k*2)
|
||||
if v <= temp break
|
||||
pokew(uw_keys+j*2, v)
|
||||
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
pokew(uw_keys+j*2, temp)
|
||||
pokew(wordvalues + j*$0002, temp_wv)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -53,34 +53,47 @@ strings {
|
||||
target[ix]=0
|
||||
}
|
||||
|
||||
sub find(str st, ubyte character) -> ubyte {
|
||||
sub find(str st, ubyte character) -> ubyte, bool {
|
||||
; Locates the first position of the given character in the string,
|
||||
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
|
||||
; NOTE: because this isn't an asmsub, there's only a SINGLE return value here. On the c64/cx16 targets etc there are 2 return values.
|
||||
; returns index in A, and boolean if found or not. (when false A will also be 255, an invalid index).
|
||||
ubyte ix
|
||||
for ix in 0 to length(st)-1 {
|
||||
if st[ix]==character {
|
||||
sys.set_carry()
|
||||
return ix
|
||||
return ix, true
|
||||
}
|
||||
}
|
||||
sys.clear_carry()
|
||||
return 255
|
||||
return 255, false
|
||||
}
|
||||
|
||||
sub rfind(uword stringptr, ubyte character) -> ubyte {
|
||||
sub find_eol(str st) -> ubyte, bool {
|
||||
; Locates the position of the first End Of Line character in the string.
|
||||
; This is a convenience function that looks for both a CR or LF (byte 13 or byte 10) as being a possible Line Ending.
|
||||
; returns index in A, and boolean if found or not. (when false A will also be 255, an invalid index).
|
||||
ubyte ix
|
||||
for ix in 0 to length(st)-1 {
|
||||
if st[ix] in "\x0a\x0d" {
|
||||
sys.set_carry()
|
||||
return ix, true
|
||||
}
|
||||
}
|
||||
sys.clear_carry()
|
||||
return 255, false
|
||||
}
|
||||
|
||||
sub rfind(uword stringptr, ubyte character) -> ubyte, bool {
|
||||
; Locates the first position of the given character in the string, starting from the right.
|
||||
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
|
||||
; NOTE: because this isn't an asmsub, there's only a SINGLE return value here. On the c64/cx16 targets etc there are 2 return values.
|
||||
; returns index in A, and boolean if found or not. (when false A will also be 255, an invalid index).
|
||||
ubyte ix
|
||||
for ix in length(stringptr)-1 downto 0 {
|
||||
if stringptr[ix]==character {
|
||||
sys.set_carry()
|
||||
return ix
|
||||
return ix, true
|
||||
}
|
||||
}
|
||||
sys.clear_carry()
|
||||
return 255
|
||||
return 255, false
|
||||
}
|
||||
|
||||
sub contains(str st, ubyte character) -> bool {
|
||||
@ -179,7 +192,7 @@ strings {
|
||||
sub hash(str st) -> ubyte {
|
||||
; experimental 8 bit hashing function.
|
||||
; hash(-1)=179; hash(i) = ROL hash(i-1) XOR string[i]
|
||||
; (experimental because the quality of the resulting hash value still has to be determined)
|
||||
; On the English word list in /usr/share/dict/words it seems to have a pretty even distribution
|
||||
ubyte hashcode = 179
|
||||
ubyte ix
|
||||
sys.clear_carry()
|
||||
|
@ -7,12 +7,14 @@ sys {
|
||||
|
||||
const ubyte target = 255 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
|
||||
|
||||
const ubyte SIZEOF_BOOL = 1
|
||||
const ubyte SIZEOF_BYTE = 1
|
||||
const ubyte SIZEOF_UBYTE = 1
|
||||
const ubyte SIZEOF_WORD = 2
|
||||
const ubyte SIZEOF_UWORD = 2
|
||||
const ubyte SIZEOF_FLOAT = 8
|
||||
const ubyte SIZEOF_BOOL = sizeof(bool)
|
||||
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@ -198,6 +200,12 @@ sys {
|
||||
}}
|
||||
}
|
||||
|
||||
sub get_as_returnaddress(uword address) -> uword {
|
||||
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
|
||||
address--
|
||||
return mkword(lsb(address), msb(address))
|
||||
}
|
||||
|
||||
sub pop() -> ubyte {
|
||||
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
|
||||
%ir {{
|
||||
@ -231,9 +239,8 @@ sys {
|
||||
|
||||
if_cs
|
||||
cx16.r0L |= 1
|
||||
; TODO: overflow flag not yet supported
|
||||
; if_vs
|
||||
; cx16.r0L |= %01000000
|
||||
if_vs
|
||||
cx16.r0L |= %01000000
|
||||
|
||||
return cx16.r0L
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val plainText by cli.option(ArgType.Boolean, fullName = "plaintext", description = "output only plain text, no colors or fancy symbols")
|
||||
val printAst1 by cli.option(ArgType.Boolean, fullName = "printast1", description = "print out the internal compiler AST")
|
||||
val printAst2 by cli.option(ArgType.Boolean, fullName = "printast2", description = "print out the simplified AST that is used for code generation")
|
||||
val quietAll by cli.option(ArgType.Boolean, fullName = "quiet", description = "don't print compiler and assembler messages")
|
||||
val quietAll by cli.option(ArgType.Boolean, fullName = "quiet", description = "don't print compiler and assembler messages, except warnings and errors")
|
||||
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler messages")
|
||||
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.")
|
||||
@ -65,12 +65,14 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val dontSplitWordArrays by cli.option(ArgType.Boolean, fullName = "dontsplitarrays", description = "don't store any word array as split lsb/msb in memory, as if all of those have @nosplit")
|
||||
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 compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of ${CompilationTargets.joinToString(",")} or a custom target properties file) (required)")
|
||||
val showTimings by cli.option(ArgType.Boolean, fullName = "timings", description = "show internal compiler timings (for performance analysis)")
|
||||
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 startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "run a .p8ir IR source file in the embedded VM")
|
||||
val warnSymbolShadowing by cli.option(ArgType.Boolean, fullName = "warnshadow", description="show assembler warnings about symbol shadowing")
|
||||
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
|
||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
||||
val version by cli.option(ArgType.Boolean, fullName = "version", description = "print compiler version and exit")
|
||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").optional().multiple(999)
|
||||
|
||||
try {
|
||||
cli.parse(args)
|
||||
@ -80,6 +82,11 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
if(version==true) {
|
||||
banner()
|
||||
return true
|
||||
}
|
||||
|
||||
if(quietAll!=true)
|
||||
banner()
|
||||
|
||||
@ -123,6 +130,11 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
if(moduleFiles.isEmpty()) {
|
||||
System.err.println("No module file(s) specified")
|
||||
return false
|
||||
}
|
||||
|
||||
if(varsHighBank==0 && compilationTarget==Cx16Target.NAME) {
|
||||
System.err.println("On the Commander X16, HiRAM bank 0 is used by the kernal and can't be used.")
|
||||
return false
|
||||
@ -170,6 +182,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
warnSymbolShadowing == true,
|
||||
quietAll == true,
|
||||
quietAll == true || quietAssembler == true,
|
||||
showTimings == true,
|
||||
asmListfile == true,
|
||||
dontIncludeSourcelines != true,
|
||||
experimentalCodegen == true,
|
||||
@ -254,6 +267,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
warnSymbolShadowing == true,
|
||||
quietAll == true,
|
||||
quietAll == true || quietAssembler == true,
|
||||
showTimings == true,
|
||||
asmListfile == true,
|
||||
dontIncludeSourcelines != true,
|
||||
experimentalCodegen == true,
|
||||
|
@ -1,8 +1,8 @@
|
||||
package prog8.compiler
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.AstException
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.SyntaxError
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.VarDecl
|
||||
@ -94,16 +94,16 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
||||
// 1 arg, type = anything, result type = ubyte or uword
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("sizeof requires one argument", position)
|
||||
if(args[0] !is IdentifierReference && args[0] !is NumericLiteral)
|
||||
throw SyntaxError("sizeof argument should be an identifier or number", position)
|
||||
if(args[0] !is IdentifierReference && args[0] !is NumericLiteral && args[0] !is AddressOf)
|
||||
throw CannotEvaluateException("sizeof","argument should be an identifier, number, or type name")
|
||||
|
||||
val dt = args[0].inferType(program)
|
||||
if(dt.isKnown) {
|
||||
if(args[0] is NumericLiteral)
|
||||
if(args[0] is NumericLiteral || args[0] is AddressOf)
|
||||
return NumericLiteral.optimalInteger(program.memsizer.memorySize(dt.getOrUndef(), null), position)
|
||||
|
||||
val target = (args[0] as IdentifierReference).targetStatement(program)
|
||||
?: throw CannotEvaluateException("sizeof", "no target")
|
||||
val target = (args[0] as? IdentifierReference)?.targetStatement()
|
||||
?: throw SyntaxError("wrong argument type", position)
|
||||
|
||||
return when {
|
||||
dt.isArray -> {
|
||||
@ -112,19 +112,40 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
||||
NumericLiteral.optimalInteger(program.memsizer.memorySize(elementDt, length), position)
|
||||
}
|
||||
dt.isString -> throw SyntaxError("sizeof(str) is undefined, did you mean len, or perhaps strings.length?", position)
|
||||
else -> NumericLiteral(BaseDataType.UBYTE, program.memsizer.memorySize(dt.getOrUndef(), null).toDouble(), position)
|
||||
else -> NumericLiteral.optimalInteger( program.memsizer.memorySize(dt.getOrUndef(), null), position)
|
||||
}
|
||||
} else {
|
||||
throw SyntaxError("sizeof invalid argument type", position)
|
||||
val identifier = args[0] as? IdentifierReference
|
||||
if(identifier?.nameInSource?.size==1) {
|
||||
when(identifier.nameInSource[0]) {
|
||||
"ubyte" -> return NumericLiteral.optimalInteger(program.memsizer.memorySize(BaseDataType.UBYTE), position)
|
||||
"byte" -> return NumericLiteral.optimalInteger(program.memsizer.memorySize(BaseDataType.BYTE), position)
|
||||
"uword" -> return NumericLiteral.optimalInteger(program.memsizer.memorySize(BaseDataType.UWORD), position)
|
||||
"word" -> return NumericLiteral.optimalInteger(program.memsizer.memorySize(BaseDataType.WORD), position)
|
||||
"long" -> return NumericLiteral.optimalInteger(program.memsizer.memorySize(BaseDataType.LONG), position)
|
||||
"float" -> return NumericLiteral.optimalInteger(program.memsizer.memorySize(BaseDataType.FLOAT), position)
|
||||
"bool" -> return NumericLiteral.optimalInteger(program.memsizer.memorySize(BaseDataType.BOOL), position)
|
||||
}
|
||||
}
|
||||
|
||||
// the argument could refer to a struct declaration
|
||||
val struct = (args[0] as? IdentifierReference)?.targetStructDecl()
|
||||
if(struct!=null) {
|
||||
val size = struct.memsize(program.memsizer)
|
||||
return NumericLiteral(BaseDataType.UBYTE, size.toDouble(), position)
|
||||
}
|
||||
|
||||
throw SyntaxError("sizeof argument should be an identifier, number, or type name", position)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
// note: in some cases the length is > 255, and then we have to return a UWORD type instead of a UBYTE.
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("len requires one argument", position)
|
||||
|
||||
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program)
|
||||
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl()
|
||||
var arraySize = directMemVar?.arraysize?.constIndex()
|
||||
if(arraySize != null)
|
||||
return NumericLiteral.optimalInteger(arraySize, position)
|
||||
@ -134,7 +155,7 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
||||
return NumericLiteral.optimalInteger((args[0] as StringLiteral).value.length, position)
|
||||
if(args[0] !is IdentifierReference)
|
||||
throw SyntaxError("len argument should be an identifier", position)
|
||||
val target = (args[0] as IdentifierReference).targetVarDecl(program)
|
||||
val target = (args[0] as IdentifierReference).targetVarDecl()
|
||||
?: throw CannotEvaluateException("len", "no target vardecl")
|
||||
|
||||
return when {
|
||||
|
@ -24,9 +24,11 @@ import java.nio.file.Path
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.isRegularFile
|
||||
import kotlin.io.path.nameWithoutExtension
|
||||
import kotlin.math.round
|
||||
import kotlin.system.exitProcess
|
||||
import kotlin.system.measureTimeMillis
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.DurationUnit
|
||||
import kotlin.time.measureTime
|
||||
import kotlin.time.measureTimedValue
|
||||
|
||||
|
||||
class CompilationResult(val compilerAst: Program, // deprecated, use codegenAst instead
|
||||
@ -40,6 +42,7 @@ class CompilerArguments(val filepath: Path,
|
||||
val warnSymbolShadowing: Boolean,
|
||||
val quietAll: Boolean,
|
||||
val quietAssembler: Boolean,
|
||||
val showTimings: Boolean,
|
||||
val asmListfile: Boolean,
|
||||
val includeSourcelines: Boolean,
|
||||
val experimentalCodegen: Boolean,
|
||||
@ -83,9 +86,20 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
}
|
||||
|
||||
try {
|
||||
val totalTime = measureTimeMillis {
|
||||
val totalTime = measureTime {
|
||||
val libraryDirs = if(compTarget.libraryPath!=null) listOf(compTarget.libraryPath.toString()) else emptyList()
|
||||
val (program, options, imported) = parseMainModule(args.filepath, args.errors, compTarget, args.sourceDirs, libraryDirs, args.quietAll)
|
||||
val (parseresult, parseDuration) = measureTimedValue {
|
||||
parseMainModule(
|
||||
args.filepath,
|
||||
args.errors,
|
||||
compTarget,
|
||||
args.sourceDirs,
|
||||
libraryDirs,
|
||||
args.quietAll
|
||||
)
|
||||
}
|
||||
|
||||
val (program, options, imported) = parseresult
|
||||
compilationOptions = options
|
||||
|
||||
with(compilationOptions) {
|
||||
@ -120,87 +134,119 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
}
|
||||
|
||||
|
||||
processAst(program, args.errors, compilationOptions)
|
||||
val processDuration = measureTime {
|
||||
processAst(program, args.errors, compilationOptions)
|
||||
}
|
||||
|
||||
// println("*********** COMPILER AST RIGHT BEFORE OPTIMIZING *************")
|
||||
// printProgram(program)
|
||||
|
||||
if (compilationOptions.optimize) {
|
||||
optimizeAst(
|
||||
program,
|
||||
compilationOptions,
|
||||
args.errors,
|
||||
BuiltinFunctionsFacade(BuiltinFunctions),
|
||||
)
|
||||
val optimizeDuration = measureTime {
|
||||
if (compilationOptions.optimize) {
|
||||
optimizeAst(
|
||||
program,
|
||||
compilationOptions,
|
||||
args.errors,
|
||||
BuiltinFunctionsFacade(BuiltinFunctions),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
determineProgramLoadAddress(program, compilationOptions, args.errors)
|
||||
args.errors.report()
|
||||
postprocessAst(program, args.errors, compilationOptions)
|
||||
args.errors.report()
|
||||
val postprocessDuration = measureTime {
|
||||
determineProgramLoadAddress(program, compilationOptions, args.errors)
|
||||
args.errors.report()
|
||||
postprocessAst(program, args.errors, compilationOptions)
|
||||
args.errors.report()
|
||||
}
|
||||
|
||||
// println("*********** COMPILER AST BEFORE ASSEMBLYGEN *************")
|
||||
// printProgram(program)
|
||||
|
||||
var createAssemblyDuration = Duration.ZERO
|
||||
var simplifiedAstDuration = Duration.ZERO
|
||||
|
||||
if (args.writeAssembly) {
|
||||
|
||||
// re-initialize memory areas with final compilationOptions
|
||||
compilationOptions.compTarget.initializeMemoryAreas(compilationOptions)
|
||||
program.processAstBeforeAsmGeneration(compilationOptions, args.errors)
|
||||
args.errors.report()
|
||||
|
||||
if(args.printAst1) {
|
||||
if (args.printAst1) {
|
||||
println("\n*********** COMPILER AST *************")
|
||||
printProgram(program)
|
||||
println("*********** COMPILER AST END *************\n")
|
||||
}
|
||||
|
||||
val intermediateAst = SimplifiedAstMaker(program, args.errors).transform()
|
||||
val stMaker = SymbolTableMaker(intermediateAst, compilationOptions)
|
||||
val symbolTable = stMaker.make()
|
||||
val (intermediateAst, simplifiedAstDuration2) = measureTimedValue {
|
||||
val intermediateAst = SimplifiedAstMaker(program, args.errors).transform()
|
||||
val stMaker = SymbolTableMaker(intermediateAst, compilationOptions)
|
||||
val symbolTable = stMaker.make()
|
||||
|
||||
postprocessSimplifiedAst(intermediateAst, symbolTable, args.errors)
|
||||
postprocessSimplifiedAst(intermediateAst, symbolTable, compilationOptions, args.errors)
|
||||
args.errors.report()
|
||||
|
||||
if(compilationOptions.optimize) {
|
||||
optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable, args.errors)
|
||||
if (compilationOptions.optimize) {
|
||||
optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable, args.errors)
|
||||
args.errors.report()
|
||||
}
|
||||
|
||||
if (args.printAst2) {
|
||||
println("\n*********** SIMPLIFIED AST *************")
|
||||
printAst(intermediateAst, true, ::println)
|
||||
println("*********** SIMPLIFIED AST END *************\n")
|
||||
}
|
||||
|
||||
verifyFinalAstBeforeAsmGen(intermediateAst, compilationOptions, symbolTable, args.errors)
|
||||
args.errors.report()
|
||||
intermediateAst
|
||||
}
|
||||
simplifiedAstDuration =simplifiedAstDuration2
|
||||
|
||||
if(args.printAst2) {
|
||||
println("\n*********** SIMPLIFIED AST *************")
|
||||
printAst(intermediateAst, true, ::println)
|
||||
println("*********** SIMPLIFIED AST END *************\n")
|
||||
}
|
||||
|
||||
verifyFinalAstBeforeAsmGen(intermediateAst, compilationOptions, symbolTable, args.errors)
|
||||
args.errors.report()
|
||||
|
||||
if(!createAssemblyAndAssemble(intermediateAst, args.errors, compilationOptions, program.generatedLabelSequenceNumber)) {
|
||||
System.err.println("Error in codegeneration or assembler")
|
||||
return null
|
||||
createAssemblyDuration = measureTime {
|
||||
if (!createAssemblyAndAssemble(
|
||||
intermediateAst,
|
||||
args.errors,
|
||||
compilationOptions,
|
||||
program.generatedLabelSequenceNumber
|
||||
)
|
||||
) {
|
||||
System.err.println("Error in codegeneration or assembler")
|
||||
return null
|
||||
}
|
||||
}
|
||||
ast = intermediateAst
|
||||
} else {
|
||||
if(args.printAst1) {
|
||||
if (args.printAst1) {
|
||||
println("\n*********** COMPILER AST *************")
|
||||
printProgram(program)
|
||||
println("*********** COMPILER AST END *************\n")
|
||||
}
|
||||
if(args.printAst2) {
|
||||
if (args.printAst2) {
|
||||
System.err.println("There is no simplified Ast available if assembly generation is disabled.")
|
||||
}
|
||||
}
|
||||
|
||||
System.out.flush()
|
||||
System.err.flush()
|
||||
|
||||
if(!args.quietAll && args.showTimings) {
|
||||
println("\n**** TIMINGS ****")
|
||||
println("source parsing : ${parseDuration.toString(DurationUnit.SECONDS, 3)}")
|
||||
println("ast processing : ${processDuration.toString(DurationUnit.SECONDS, 3)}")
|
||||
println("ast optimizing : ${optimizeDuration.toString(DurationUnit.SECONDS, 3)}")
|
||||
println("ast postprocess : ${postprocessDuration.toString(DurationUnit.SECONDS, 3)}")
|
||||
println("code prepare : ${simplifiedAstDuration.toString(DurationUnit.SECONDS, 3)}")
|
||||
println("code generation : ${createAssemblyDuration.toString(DurationUnit.SECONDS, 3)}")
|
||||
val totalDuration = parseDuration + processDuration + optimizeDuration + postprocessDuration + simplifiedAstDuration + createAssemblyDuration
|
||||
println(" total : ${totalDuration.toString(DurationUnit.SECONDS, 3)}")
|
||||
}
|
||||
}
|
||||
|
||||
System.out.flush()
|
||||
System.err.flush()
|
||||
if(!args.quietAll) {
|
||||
val seconds = totalTime / 1000.0
|
||||
println("\nTotal compilation+assemble time: ${round(seconds * 100.0) / 100.0} sec.")
|
||||
println("\nTotal compilation+assemble time: ${totalTime.toString(DurationUnit.SECONDS, 3)}.")
|
||||
}
|
||||
return CompilationResult(resultingProgram!!, ast, compilationOptions, importedFiles)
|
||||
} catch (px: ParseError) {
|
||||
args.errors.print_single_error("${px.position.toClickableStr()} parse error: ${px.message}".trim())
|
||||
args.errors.printSingleError("${px.position.toClickableStr()} parse error: ${px.message}".trim())
|
||||
} catch (ac: ErrorsReportedException) {
|
||||
if(args.printAst1 && resultingProgram!=null) {
|
||||
println("\n*********** COMPILER AST *************")
|
||||
@ -217,17 +263,17 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
}
|
||||
}
|
||||
if(!ac.message.isNullOrEmpty()) {
|
||||
args.errors.print_single_error(ac.message!!)
|
||||
args.errors.printSingleError(ac.message!!)
|
||||
}
|
||||
} catch (nsf: NoSuchFileException) {
|
||||
args.errors.print_single_error("File not found: ${nsf.message}")
|
||||
args.errors.printSingleError("File not found: ${nsf.message}")
|
||||
} catch (ax: AstException) {
|
||||
args.errors.print_single_error(ax.toString())
|
||||
args.errors.printSingleError(ax.toString())
|
||||
} catch (x: Exception) {
|
||||
args.errors.print_single_error("\ninternal error")
|
||||
args.errors.printSingleError("\ninternal error")
|
||||
throw x
|
||||
} catch (x: NotImplementedError) {
|
||||
args.errors.print_single_error("\ninternal error: missing feature/code")
|
||||
args.errors.printSingleError("\ninternal error: missing feature/code")
|
||||
throw x
|
||||
}
|
||||
|
||||
@ -410,7 +456,6 @@ internal fun determineCompilationOptions(program: Program, compTarget: ICompilat
|
||||
|
||||
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||
program.preprocessAst(errors, compilerOptions)
|
||||
|
||||
if(errors.noErrors() && compilerOptions.dumpSymbols) {
|
||||
printSymbols(program)
|
||||
exitProcess(0)
|
||||
@ -424,7 +469,7 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions
|
||||
errors.report()
|
||||
program.reorderStatements(errors)
|
||||
errors.report()
|
||||
program.desugaring(errors)
|
||||
program.desugaring(errors, compilerOptions)
|
||||
errors.report()
|
||||
program.changeNotExpressionAndIfComparisonExpr(errors, compilerOptions.compTarget)
|
||||
errors.report()
|
||||
@ -486,7 +531,7 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
|
||||
}
|
||||
|
||||
private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||
program.desugaring(errors)
|
||||
program.desugaring(errors, compilerOptions)
|
||||
program.addTypecasts(errors, compilerOptions)
|
||||
errors.report()
|
||||
program.variousCleanups(errors, compilerOptions)
|
||||
@ -495,8 +540,21 @@ private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOpt
|
||||
program.verifyFunctionArgTypes(errors, compilerOptions)
|
||||
errors.report()
|
||||
program.moveMainBlockAsFirst(compilerOptions.compTarget)
|
||||
|
||||
val fixer = BeforeAsmAstChanger(program, compilerOptions, errors)
|
||||
fixer.visit(program)
|
||||
while (errors.noErrors() && fixer.applyModifications() > 0) {
|
||||
fixer.visit(program)
|
||||
}
|
||||
|
||||
program.checkValid(errors, compilerOptions) // check if final tree is still valid
|
||||
errors.report()
|
||||
|
||||
val cleaner = BeforeAsmTypecastCleaner(program, errors)
|
||||
cleaner.visit(program)
|
||||
while (errors.noErrors() && cleaner.applyModifications() > 0) {
|
||||
cleaner.visit(program)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAssemblyAndAssemble(program: PtProgram,
|
||||
|
@ -95,7 +95,7 @@ internal class ErrorReporter(val colors: IConsoleColors): IErrorReporter {
|
||||
override fun noErrors() = messages.none { it.severity==MessageSeverity.ERROR }
|
||||
override fun noErrorForLine(position: Position) = !messages.any { it.position.line==position.line && it.severity!=MessageSeverity.INFO }
|
||||
|
||||
override fun print_single_error(errormessage: String) {
|
||||
override fun printSingleError(errormessage: String) {
|
||||
System.out.flush()
|
||||
colors.error(System.err)
|
||||
System.err.println(errormessage)
|
||||
|
@ -39,7 +39,7 @@ class ModuleImporter(private val program: Program,
|
||||
println("Compiling program ${Path("").absolute().relativize(programPath)}")
|
||||
println("Compiler target: $compilationTargetName")
|
||||
}
|
||||
val source = ImportFileSystem.getFile(programPath, false)
|
||||
val source = ImportFileSystem.getFile(programPath)
|
||||
return Ok(importModule(source))
|
||||
}
|
||||
}
|
||||
@ -158,7 +158,7 @@ class ModuleImporter(private val program: Program,
|
||||
|
||||
normalLocations.forEach {
|
||||
try {
|
||||
return Ok(ImportFileSystem.getFile(it.resolve(fileName), false))
|
||||
return Ok(ImportFileSystem.getFile(it.resolve(fileName)))
|
||||
} catch (_: NoSuchFileException) {
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,11 +3,7 @@ package prog8.compiler.astprocessing
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.CharLiteral
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.expressions.StringLiteral
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
@ -24,19 +20,6 @@ internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: Compila
|
||||
checker.visit(this)
|
||||
}
|
||||
|
||||
internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationOptions, errors: IErrorReporter) {
|
||||
val fixer = BeforeAsmAstChanger(this, compilerOptions, errors)
|
||||
fixer.visit(this)
|
||||
while (errors.noErrors() && fixer.applyModifications() > 0) {
|
||||
fixer.visit(this)
|
||||
}
|
||||
val cleaner = BeforeAsmTypecastCleaner(this, errors)
|
||||
cleaner.visit(this)
|
||||
while (errors.noErrors() && cleaner.applyModifications() > 0) {
|
||||
cleaner.visit(this)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Program.reorderStatements(errors: IErrorReporter) {
|
||||
val reorder = StatementReorderer(this, errors)
|
||||
reorder.visit(this)
|
||||
@ -107,8 +90,8 @@ internal fun Program.addTypecasts(errors: IErrorReporter, options: CompilationOp
|
||||
caster.applyModifications()
|
||||
}
|
||||
|
||||
fun Program.desugaring(errors: IErrorReporter) {
|
||||
val desugar = CodeDesugarer(this, errors)
|
||||
fun Program.desugaring(errors: IErrorReporter, options: CompilationOptions) {
|
||||
val desugar = CodeDesugarer(this, options.compTarget, errors)
|
||||
desugar.visit(this)
|
||||
while(errors.noErrors() && desugar.applyModifications()>0)
|
||||
desugar.visit(this)
|
||||
@ -136,7 +119,7 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
|
||||
checker2.visit(this)
|
||||
|
||||
if(errors.noErrors()) {
|
||||
val lit2decl = LiteralsToAutoVars(this, errors)
|
||||
val lit2decl = LiteralsToAutoVarsAndRecombineIdentifiers(this, errors)
|
||||
lit2decl.visit(this)
|
||||
while(errors.noErrors() && lit2decl.applyModifications()>0)
|
||||
lit2decl.visit(this)
|
||||
@ -185,8 +168,8 @@ internal fun Program.moveMainBlockAsFirst(target: ICompilationTarget) {
|
||||
}
|
||||
}
|
||||
|
||||
internal fun IdentifierReference.isSubroutineParameter(program: Program): Boolean {
|
||||
val vardecl = this.targetVarDecl(program)
|
||||
internal fun IdentifierReference.isSubroutineParameter(): Boolean {
|
||||
val vardecl = this.targetVarDecl()
|
||||
if(vardecl!=null && vardecl.origin==VarDeclOrigin.SUBROUTINEPARAM) {
|
||||
return vardecl.definingSubroutine?.parameters?.any { it.name==vardecl.name } == true
|
||||
}
|
||||
@ -219,7 +202,7 @@ internal fun Subroutine.hasRtsInAsm(checkOnlyLastInstruction: Boolean): Boolean
|
||||
}
|
||||
|
||||
internal fun IdentifierReference.checkFunctionOrLabelExists(program: Program, statement: Statement, errors: IErrorReporter): Statement? {
|
||||
when (val targetStatement = this.targetStatement(program)) {
|
||||
when (val targetStatement = targetStatement(program.builtinFunctions)) {
|
||||
is Label, is Subroutine, is BuiltinFunctionPlaceholder -> return targetStatement
|
||||
is VarDecl -> {
|
||||
if(statement is Jump) {
|
||||
@ -231,7 +214,7 @@ internal fun IdentifierReference.checkFunctionOrLabelExists(program: Program, st
|
||||
else
|
||||
errors.err("cannot call that: ${this.nameInSource.joinToString(".")}", this.position)
|
||||
}
|
||||
is Alias -> {
|
||||
is Alias, is StructDecl, is StructFieldRef -> {
|
||||
return targetStatement
|
||||
}
|
||||
null -> {
|
||||
|
@ -40,7 +40,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
}
|
||||
|
||||
override fun visit(alias: Alias) {
|
||||
if(alias.target.targetStatement(program)==null)
|
||||
if(alias.target.targetStatement(program.builtinFunctions)==null)
|
||||
errors.err("undefined symbol: ${alias.target.nameInSource.joinToString(".") }", alias.target.position)
|
||||
}
|
||||
|
||||
@ -94,7 +94,6 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
super.visit(decl)
|
||||
}
|
||||
|
||||
|
||||
override fun visit(subroutine: Subroutine) {
|
||||
if(subroutine.name in BuiltinFunctions) {
|
||||
// the builtin functions can't be redefined
|
||||
@ -169,13 +168,13 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
|
||||
private fun visitFunctionCall(call: IFunctionCall) {
|
||||
if(call.target.nameInSource==listOf("rnd") || call.target.nameInSource==listOf("rndw")) {
|
||||
val target = call.target.targetStatement(program)
|
||||
val target = call.target.targetStatement(program.builtinFunctions)
|
||||
if(target==null) {
|
||||
errors.err("rnd() and rndw() builtin functions have been moved into the math module", call.position)
|
||||
return
|
||||
}
|
||||
}
|
||||
when (val target = call.target.targetStatement(program)) {
|
||||
when (val target = call.target.targetStatement(program.builtinFunctions)) {
|
||||
is Subroutine -> {
|
||||
val expectedNumberOfArgs: Int = target.parameters.size
|
||||
if(call.args.size != expectedNumberOfArgs) {
|
||||
@ -215,7 +214,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
if(target.type!=VarDeclType.VAR || !target.datatype.isUnsignedWord)
|
||||
errors.err("wrong address variable datatype, expected uword", call.target.position)
|
||||
}
|
||||
is Alias -> {}
|
||||
is Alias, is StructDecl, is StructFieldRef -> {}
|
||||
null -> {}
|
||||
else -> errors.err("cannot call this as a subroutine or function", call.target.position)
|
||||
}
|
||||
|
@ -1,12 +1,7 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.SyntaxError
|
||||
import prog8.ast.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.findParentNode
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
@ -129,7 +124,14 @@ class AstPreprocessor(val program: Program,
|
||||
// we need to handle multi-decl here too, the desugarer maybe has not processed it here yet...
|
||||
if(decl.value!=null) {
|
||||
decl.names.forEach { name ->
|
||||
val target = AssignTarget(IdentifierReference(listOf(name), decl.position), null, null, null, false, decl.position)
|
||||
val target = AssignTarget(
|
||||
IdentifierReference(listOf(name), decl.position),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
position = decl.position
|
||||
)
|
||||
val assign = Assignment(target.copy(), decl.value!!.copy(), AssignmentOrigin.VARINIT, decl.position)
|
||||
replacements.add(IAstModification.InsertAfter(decl, assign, scope))
|
||||
}
|
||||
@ -145,7 +147,14 @@ class AstPreprocessor(val program: Program,
|
||||
} else {
|
||||
// handle declaration of a single variable
|
||||
if(decl.value!=null && decl.datatype.isNumericOrBool) {
|
||||
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, null, false, decl.position)
|
||||
val target = AssignTarget(
|
||||
IdentifierReference(listOf(decl.name), decl.position),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
position = decl.position
|
||||
)
|
||||
val assign = Assignment(target, decl.value!!, AssignmentOrigin.VARINIT, decl.position)
|
||||
replacements.add(IAstModification.ReplaceNode(decl, assign, scope))
|
||||
decl.value = null
|
||||
@ -181,7 +190,7 @@ class AstPreprocessor(val program: Program,
|
||||
val nextAssignment = decl.nextSibling() as? Assignment
|
||||
if(nextAssignment!=null && nextAssignment.origin!=AssignmentOrigin.VARINIT) {
|
||||
// check if the following assignment initializes the variable
|
||||
if(decl.value==null && nextAssignment.target.identifier?.targetVarDecl(program)===decl) {
|
||||
if(decl.value==null && nextAssignment.target.identifier?.targetVarDecl()===decl) {
|
||||
if(!nextAssignment.value.referencesIdentifier(nextAssignment.target.identifier!!.nameInSource))
|
||||
nextAssignment.origin = AssignmentOrigin.VARINIT
|
||||
}
|
||||
@ -191,6 +200,29 @@ class AstPreprocessor(val program: Program,
|
||||
return makeUnSplitArray(decl)
|
||||
}
|
||||
|
||||
// convert all antlr names to structs
|
||||
val antlrTypeName = decl.datatype.subTypeFromAntlr
|
||||
if(antlrTypeName!=null) {
|
||||
val node = decl.definingScope.lookup(antlrTypeName)
|
||||
if(node==null) {
|
||||
errors.err("cannot find struct type ${antlrTypeName.joinToString(".")}", decl.position)
|
||||
} else {
|
||||
decl.datatype.setActualSubType(node as StructDecl)
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(struct: StructDecl, parent: Node): Iterable<IAstModification> {
|
||||
// convert all antlr names to structs
|
||||
struct.fields.forEach {
|
||||
if(it.first.subTypeFromAntlr!=null) {
|
||||
val struct = struct.definingScope.lookup(it.first.subTypeFromAntlr!!) as? ISubType
|
||||
if(struct!=null)
|
||||
it.first.setActualSubType(struct)
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
@ -241,7 +273,23 @@ class AstPreprocessor(val program: Program,
|
||||
errors.err("can only use R0-R15 as register param for normal subroutines", it.position)
|
||||
}
|
||||
}
|
||||
return mods
|
||||
if(mods.isNotEmpty())
|
||||
return mods
|
||||
}
|
||||
}
|
||||
|
||||
subroutine.returntypes.forEach {
|
||||
if(it.subTypeFromAntlr!=null) {
|
||||
val struct = subroutine.lookup(it.subTypeFromAntlr!!) as? ISubType
|
||||
if(struct!=null)
|
||||
it.setActualSubType(struct)
|
||||
}
|
||||
}
|
||||
subroutine.parameters.forEach {
|
||||
if(it.type.subTypeFromAntlr!=null) {
|
||||
val struct = subroutine.lookup(it.type.subTypeFromAntlr!!) as? ISubType
|
||||
if(struct!=null)
|
||||
it.type.setActualSubType(struct)
|
||||
}
|
||||
}
|
||||
|
||||
@ -253,6 +301,19 @@ class AstPreprocessor(val program: Program,
|
||||
?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCallExpr.position}")
|
||||
|
||||
checkStringParam(functionCallExpr as IFunctionCall, stmtOfExpression)
|
||||
|
||||
if(functionCallExpr.target.nameInSource==listOf("sizeof")) {
|
||||
val arg = functionCallExpr.args.firstOrNull()
|
||||
if (arg is PtrDereference) {
|
||||
// replace sizeof(ptr^^) with sizeof(type that ptr points to)
|
||||
val dt = arg.inferType(program)
|
||||
if(dt.isKnown) {
|
||||
val dtName = dt.getOrUndef().toString()
|
||||
val newArg = IdentifierReference(dtName.split("."), arg.position)
|
||||
return listOf(IAstModification.ReplaceNode(arg, newArg, functionCallExpr))
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
@ -262,13 +323,34 @@ class AstPreprocessor(val program: Program,
|
||||
}
|
||||
|
||||
override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
|
||||
val tgt = alias.target.targetStatement(program)
|
||||
val tgt = alias.target.targetStatement(program.builtinFunctions)
|
||||
if(tgt is Block) {
|
||||
errors.err("cannot alias blocks", alias.target.position)
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
// convert all antlr names to structs
|
||||
if(typecast.type.subTypeFromAntlr!=null) {
|
||||
val struct = typecast.definingScope.lookup(typecast.type.subTypeFromAntlr!!) as? ISubType
|
||||
if(struct!=null)
|
||||
typecast.type.setActualSubType(struct)
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(field: StructFieldRef, parent: Node): Iterable<IAstModification> {
|
||||
if(field.type.subTypeFromAntlr!=null) {
|
||||
val struct = field.definingScope.lookup(field.type.subTypeFromAntlr!!) as? ISubType
|
||||
if(struct!=null)
|
||||
field.type.setActualSubType(struct)
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
||||
private fun checkStringParam(call: IFunctionCall, stmt: Statement) {
|
||||
val targetStatement = call.target.checkFunctionOrLabelExists(program, stmt, errors)
|
||||
if(targetStatement!=null) {
|
||||
|
@ -3,6 +3,7 @@ package prog8.compiler.astprocessing
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.defaultZero
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.statements.*
|
||||
@ -48,18 +49,27 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
|
||||
val mods = mutableListOf<IAstModification>()
|
||||
|
||||
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine.
|
||||
// and if an assembly block doesn't contain a rts/rti.
|
||||
// and if an assembly block doesn't contain a rts/rti. AND if there's no return value(s) because we can't make one up!
|
||||
if (!subroutine.isAsmSubroutine) {
|
||||
if(subroutine.isEmpty()) {
|
||||
val returnStmt = Return(arrayOf(), subroutine.position)
|
||||
mods += IAstModification.InsertLast(returnStmt, subroutine)
|
||||
if(subroutine.returntypes.isNotEmpty())
|
||||
errors.err("subroutine is missing a return statement with value(s)", subroutine.position)
|
||||
else {
|
||||
val returnStmt = Return(arrayOf(), subroutine.position)
|
||||
mods += IAstModification.InsertLast(returnStmt, subroutine)
|
||||
}
|
||||
} else {
|
||||
val last = subroutine.statements.last()
|
||||
if((last !is InlineAssembly || !last.hasReturnOrRts()) && last !is Return) {
|
||||
val lastStatement = subroutine.statements.reversed().firstOrNull { it !is Subroutine }
|
||||
if(lastStatement !is Return) {
|
||||
val returnStmt = Return(arrayOf(), subroutine.position)
|
||||
mods += IAstModification.InsertLast(returnStmt, subroutine)
|
||||
if(subroutine.returntypes.isNotEmpty()) {
|
||||
// .... we cannot return this as an error, because that also breaks legitimate cases where the return is done from within a nested scope somewhere
|
||||
// errors.err("subroutine is missing a return statement with value(s)", subroutine.position)
|
||||
} else {
|
||||
val returnStmt = Return(arrayOf(), subroutine.position)
|
||||
mods += IAstModification.InsertLast(returnStmt, subroutine)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -76,8 +86,20 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
|
||||
&& prevStmt !is Subroutine
|
||||
&& prevStmt !is Return
|
||||
) {
|
||||
val returnStmt = Return(arrayOf(), subroutine.position)
|
||||
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope)
|
||||
if(!subroutine.inline) {
|
||||
if(outerScope is Subroutine && outerScope.returntypes.isNotEmpty()) {
|
||||
if(outerScope.returntypes.size>1 || !outerScope.returntypes[0].isNumericOrBool) {
|
||||
errors.err("subroutine is missing a return statement to avoid falling through into nested subroutine", outerStatements[subroutineStmtIdx-1].position)
|
||||
} else {
|
||||
val zero = defaultZero(outerScope.returntypes[0].base, Position.DUMMY)
|
||||
val returnStmt = Return(arrayOf(zero), outerStatements[subroutineStmtIdx - 1].position)
|
||||
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope)
|
||||
}
|
||||
} else {
|
||||
val returnStmt = Return(arrayOf(), outerStatements[subroutineStmtIdx - 1].position)
|
||||
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@ import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.isByte
|
||||
import prog8.code.core.isWord
|
||||
|
||||
|
||||
@ -29,14 +28,14 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(typecast.type==sourceDt.base)
|
||||
if(typecast.type==sourceDt)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||
|
||||
if(sourceDt.isPassByRef) {
|
||||
if(typecast.type == BaseDataType.UWORD) {
|
||||
if(typecast.type.isUnsignedWord) {
|
||||
val identifier = typecast.expression as? IdentifierReference
|
||||
if(identifier!=null) {
|
||||
return if(identifier.isSubroutineParameter(program)) {
|
||||
return if(identifier.isSubroutineParameter()) {
|
||||
listOf(
|
||||
IAstModification.ReplaceNode(
|
||||
typecast,
|
||||
@ -48,7 +47,7 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
||||
listOf(
|
||||
IAstModification.ReplaceNode(
|
||||
typecast,
|
||||
AddressOf(identifier, null, false, typecast.position),
|
||||
AddressOf(identifier, null, null, false, typecast.position),
|
||||
parent
|
||||
)
|
||||
)
|
||||
|
@ -5,13 +5,10 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.ComparisonOperators
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal class CodeDesugarer(val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
internal class CodeDesugarer(val program: Program, private val target: ICompilationTarget, private val errors: IErrorReporter) : AstWalker() {
|
||||
|
||||
// Some more code shuffling to simplify the Ast that the codegenerator has to process.
|
||||
// Several changes have already been done by the StatementReorderer !
|
||||
@ -27,11 +24,24 @@ internal class CodeDesugarer(val program: Program, private val errors: IErrorRep
|
||||
// - @(&var) and @(&var+1) replaced by lsb(var) and msb(var) if var is a word
|
||||
// - flatten chained assignments
|
||||
// - remove alias nodes
|
||||
// - convert on..goto/call to jumpaddr array and separate goto/call
|
||||
// - replace implicit pointer dereference chains (a.b.c.d) with explicit ones (a^^.b^^.c^^.d)
|
||||
// - replace ptr^^ by @(ptr) if ptr is just an uword.
|
||||
// - replace p1^^ = p2^^ by memcopy.
|
||||
|
||||
override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
|
||||
return listOf(IAstModification.Remove(alias, parent as IStatementContainer))
|
||||
}
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
if(decl.datatype.isPointer && decl.datatype.sub==BaseDataType.STR) {
|
||||
errors.info("^^str replaced by ^^ubyte", decl.position)
|
||||
val decl2 = decl.copy(DataType.pointer(BaseDataType.UBYTE))
|
||||
return listOf(IAstModification.ReplaceNode(decl, decl2, parent))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
|
||||
fun jumpAfter(stmt: Statement): Iterable<IAstModification> {
|
||||
val label = program.makeLabel("after", breakStmt.position)
|
||||
@ -106,7 +116,7 @@ if not CONDITION
|
||||
untilLoop.body,
|
||||
IfElse(invertCondition(untilLoop.condition, program),
|
||||
AnonymousScope(mutableListOf(program.jumpLabel(loopLabel)), pos),
|
||||
AnonymousScope(mutableListOf(), pos),
|
||||
AnonymousScope.empty(),
|
||||
pos)
|
||||
), pos)
|
||||
return listOf(IAstModification.ReplaceNode(untilLoop, replacement, parent))
|
||||
@ -114,10 +124,6 @@ if not CONDITION
|
||||
|
||||
override fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
if(!whileLoop.condition.inferType(program).isBool) {
|
||||
errors.err("condition should be a boolean", whileLoop.condition.position)
|
||||
}
|
||||
|
||||
/*
|
||||
while true -> repeat
|
||||
while false -> discard
|
||||
@ -150,7 +156,7 @@ _after:
|
||||
loopLabel,
|
||||
IfElse(invertCondition(whileLoop.condition, program),
|
||||
AnonymousScope(mutableListOf(program.jumpLabel(afterLabel)), pos),
|
||||
AnonymousScope(mutableListOf(), pos),
|
||||
AnonymousScope.empty(),
|
||||
pos),
|
||||
whileLoop.body,
|
||||
program.jumpLabel(loopLabel),
|
||||
@ -173,7 +179,14 @@ _after:
|
||||
}
|
||||
if(functionCall.target.nameInSource==listOf("poke")) {
|
||||
// poke(a, v) is synonymous with @(a) = v
|
||||
val tgt = AssignTarget(null, null, DirectMemoryWrite(functionCall.args[0], position), null, false, position)
|
||||
val tgt = AssignTarget(
|
||||
null,
|
||||
null,
|
||||
DirectMemoryWrite(functionCall.args[0], position),
|
||||
null,
|
||||
false,
|
||||
position = position
|
||||
)
|
||||
val assign = Assignment(tgt, functionCall.args[1], AssignmentOrigin.OPTIMIZER, position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCall as Node, assign, parent))
|
||||
}
|
||||
@ -204,21 +217,56 @@ _after:
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
// replace pointervar[word] by @(pointervar+word) to avoid the
|
||||
// "array indexing is limited to byte size 0..255" error for pointervariables.
|
||||
|
||||
if(arrayIndexedExpression.pointerderef!=null) {
|
||||
return noModifications
|
||||
}
|
||||
|
||||
val indexExpr = arrayIndexedExpression.indexer.indexExpr
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
if(arrayVar!=null && arrayVar.datatype.isUnsignedWord) {
|
||||
val wordIndex = TypecastExpression(indexExpr, BaseDataType.UWORD, true, indexExpr.position)
|
||||
val address = BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", wordIndex, arrayIndexedExpression.position)
|
||||
return if(parent is AssignTarget) {
|
||||
val arrayVar = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
|
||||
if(arrayVar!=null && (arrayVar.datatype.isUnsignedWord || (arrayVar.datatype.isPointer && arrayVar.datatype.sub==BaseDataType.UBYTE))) {
|
||||
val wordIndex = TypecastExpression(indexExpr, DataType.UWORD, true, indexExpr.position)
|
||||
val address = BinaryExpression(
|
||||
arrayIndexedExpression.plainarrayvar!!.copy(),
|
||||
"+",
|
||||
wordIndex,
|
||||
arrayIndexedExpression.position
|
||||
)
|
||||
return if (parent is AssignTarget) {
|
||||
// assignment to array
|
||||
val memwrite = DirectMemoryWrite(address, arrayIndexedExpression.position)
|
||||
val newtarget = AssignTarget(null, null, memwrite, null, false, arrayIndexedExpression.position)
|
||||
val newtarget = AssignTarget(
|
||||
null,
|
||||
null,
|
||||
memwrite,
|
||||
null,
|
||||
false,
|
||||
position = arrayIndexedExpression.position
|
||||
)
|
||||
listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent))
|
||||
} else {
|
||||
// read from array
|
||||
val memread = DirectMemoryRead(address, arrayIndexedExpression.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
|
||||
}
|
||||
} else if(arrayVar!=null && (arrayVar.type==VarDeclType.MEMORY || arrayVar.datatype.isString || arrayVar.datatype.isPointer || arrayVar.datatype.isArray)) {
|
||||
return noModifications
|
||||
} else if(arrayVar!=null) {
|
||||
// it could be a pointer dereference instead of a simple array variable
|
||||
TODO("deref[word] rewrite ???? $arrayIndexedExpression")
|
||||
// val dt = arrayIndexedExpression.plainarrayvar!!.traverseDerefChainForDt(null)
|
||||
// if(dt.isUnsignedWord) {
|
||||
// // ptr.field[index] --> @(ptr.field + index)
|
||||
// val index = arrayIndexedExpression.indexer.indexExpr
|
||||
// val address = BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", index, arrayIndexedExpression.position)
|
||||
// if(parent is AssignTarget) {
|
||||
// val memwrite = DirectMemoryWrite(address, arrayIndexedExpression.position)
|
||||
// return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memwrite, parent))
|
||||
// } else {
|
||||
// val memread = DirectMemoryRead(address, arrayIndexedExpression.position)
|
||||
// return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
|
||||
// }
|
||||
// }
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
@ -259,6 +307,63 @@ _after:
|
||||
return listOf(IAstModification.ReplaceNode(expr, squareCall, parent))
|
||||
}
|
||||
|
||||
if(expr.operator==".") {
|
||||
val left = expr.left as? ArrayIndexedExpression
|
||||
val right = expr.right as? PtrDereference
|
||||
if(left!=null && right!=null) {
|
||||
if(parent is BinaryExpression && parent.operator=="." && parent.right===expr) {
|
||||
val parentLeft = parent.left as? IdentifierReference
|
||||
if(parentLeft!=null) {
|
||||
// parent is:
|
||||
// BinaryExpression "."
|
||||
// / \
|
||||
// IdRef (this BinExpr)
|
||||
// x.y / \
|
||||
// ArrayIdx PtrDeref
|
||||
// z[i] field
|
||||
//
|
||||
// transform this into this so it can be processed further:
|
||||
//
|
||||
// BinaryExpression "."
|
||||
// / \
|
||||
// ArrayIdx IdentifierRef
|
||||
// x.y.z[i] field
|
||||
|
||||
val combinedIdentifier = IdentifierReference(parentLeft.nameInSource+left.plainarrayvar!!.nameInSource, parentLeft.position)
|
||||
val newleft = ArrayIndexedExpression(combinedIdentifier, null, left.indexer, left.position)
|
||||
val newright = IdentifierReference(listOf(right.chain.single()), right.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(parent.left, newleft, parent),
|
||||
IAstModification.ReplaceNode(parent.right, newright, parent)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(expr.left is ArrayIndexedExpression && right!=null) {
|
||||
// replace replace x.y.listarray[2]^^.value with just x.y.listarray[2] . value
|
||||
// this will be further modified elsewhere
|
||||
val ident = IdentifierReference(right.chain, right.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, ident, expr))
|
||||
|
||||
// TODO hmmm , replace cx16.r1 = listarray[2]^^.value with a temp pointer var to contain the indexed value
|
||||
// val assign = expr.parent as? Assignment
|
||||
// if(assign!=null) {
|
||||
// val ptrDt = expr.left.inferType(program).getOrUndef()
|
||||
// val pointerVar = VarDecl.createAuto(ptrDt)
|
||||
// val pointerIdent = IdentifierReference(pointerVar.name.split("."), expr.position)
|
||||
// val tgt = AssignTarget(pointerIdent, null, null, null, false, null, position = expr.position)
|
||||
// val assignPtr = Assignment(tgt, expr.left, AssignmentOrigin.USERCODE, expr.position)
|
||||
// val derefValue = PtrDereference(pointerIdent.nameInSource + right.chain, false, expr.position)
|
||||
// return listOf(
|
||||
// IAstModification.InsertBefore(assign, assignPtr, assign.parent as IStatementContainer),
|
||||
// IAstModification.ReplaceNode(assign.value, derefValue, assign),
|
||||
// IAstModification.InsertFirst(pointerVar, expr.definingSubroutine!!)
|
||||
// )
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
@ -270,8 +375,8 @@ _after:
|
||||
val addrOf = memread.addressExpression as? AddressOf
|
||||
if(addrOf?.arrayIndex!=null)
|
||||
return noModifications
|
||||
if(addrOf!=null && addrOf.identifier.inferType(program).isWords) {
|
||||
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), memread.position), mutableListOf(addrOf.identifier), memread.position)
|
||||
if(addrOf!=null && addrOf.identifier?.inferType(program)?.isWords==true) {
|
||||
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), memread.position), mutableListOf(addrOf.identifier!!), memread.position)
|
||||
return listOf(IAstModification.ReplaceNode(memread, lsb, parent))
|
||||
}
|
||||
val expr = memread.addressExpression as? BinaryExpression
|
||||
@ -279,9 +384,11 @@ _after:
|
||||
val addressOf = expr.left as? AddressOf
|
||||
val offset = (expr.right as? NumericLiteral)?.number?.toInt()
|
||||
if(addressOf!=null && offset==1) {
|
||||
val variable = addressOf.identifier.targetVarDecl(program)
|
||||
val variable = addressOf.identifier?.targetVarDecl()
|
||||
if(variable!=null && variable.datatype.isWord) {
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), memread.position), mutableListOf(addressOf.identifier), memread.position)
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), memread.position), mutableListOf(
|
||||
addressOf.identifier!!
|
||||
), memread.position)
|
||||
return listOf(IAstModification.ReplaceNode(memread, msb, parent))
|
||||
}
|
||||
}
|
||||
@ -350,4 +457,195 @@ _after:
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
if (identifier.nameInSource.size>1) {
|
||||
val firstTarget = (identifier.firstTarget() as? VarDecl)
|
||||
val firstDt = firstTarget?.datatype
|
||||
if (firstDt?.isPointer == true) {
|
||||
// the a.b.c.d can be a pointer dereference chain ending in a struct field; a^^.b^^.c^^.d
|
||||
|
||||
val chain = mutableListOf(identifier.nameInSource[0])
|
||||
var struct = firstDt.subType as? StructDecl
|
||||
for(name in identifier.nameInSource.drop(1)) {
|
||||
if(struct==null) {
|
||||
errors.err("unknown field '${name}", position = identifier.position)
|
||||
return noModifications
|
||||
}
|
||||
val fieldDt = struct.getFieldType(name)
|
||||
if(fieldDt==null) {
|
||||
errors.err("unknown field '${name}' in struct '${struct.name}'", identifier.position)
|
||||
return noModifications
|
||||
}
|
||||
if(fieldDt.isPointer) {
|
||||
chain.add(name)
|
||||
struct = fieldDt.subType as? StructDecl
|
||||
} else {
|
||||
chain.add(name)
|
||||
struct = null
|
||||
}
|
||||
}
|
||||
val deref = PtrDereference(chain, false, identifier.position)
|
||||
return listOf(IAstModification.ReplaceNode(identifier, deref, parent))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(ongoto: OnGoto, parent: Node): Iterable<IAstModification> {
|
||||
val indexDt = ongoto.index.inferType(program).getOrUndef()
|
||||
if(!indexDt.isUnsignedByte)
|
||||
return noModifications
|
||||
|
||||
val numlabels = ongoto.labels.size
|
||||
val split = if(ongoto.isCall)
|
||||
true // for calls (indirect JSR), split array is always the optimal choice
|
||||
else
|
||||
target.cpu==CpuType.CPU6502 // for goto (indirect JMP), split array is optimal for 6502, but NOT for the 65C02 (it has a different JMP addressing mode available)
|
||||
val arrayDt = DataType.arrayFor(BaseDataType.UWORD, split)
|
||||
val labelArray = ArrayLiteral(InferredTypes.knownFor(arrayDt), ongoto.labels.toTypedArray(), ongoto.position)
|
||||
val jumplistArray = VarDecl.createAutoOptionalSplit(labelArray)
|
||||
|
||||
val indexValue: Expression
|
||||
val conditionVar: VarDecl?
|
||||
val assignIndex: Assignment?
|
||||
|
||||
// put condition in temp var, if it is not simple; to avoid evaluating expression multiple times
|
||||
if (ongoto.index.isSimple) {
|
||||
indexValue = ongoto.index
|
||||
assignIndex = null
|
||||
conditionVar = null
|
||||
} else {
|
||||
conditionVar = VarDecl.createAuto(indexDt)
|
||||
indexValue = IdentifierReference(listOf(conditionVar.name), conditionVar.position)
|
||||
val varTarget = AssignTarget(indexValue, null, null, null, false, position=conditionVar.position)
|
||||
assignIndex = Assignment(varTarget, ongoto.index, AssignmentOrigin.USERCODE, ongoto.position)
|
||||
}
|
||||
|
||||
val callTarget = ArrayIndexedExpression(IdentifierReference(listOf(jumplistArray.name), jumplistArray.position), null, ArrayIndex(indexValue.copy(), indexValue.position), ongoto.position)
|
||||
val callIndexed = AnonymousScope.empty(ongoto.position)
|
||||
if(ongoto.isCall) {
|
||||
callIndexed.statements.add(FunctionCallStatement(IdentifierReference(listOf("call"), ongoto.position), mutableListOf(callTarget), true, ongoto.position))
|
||||
} else {
|
||||
callIndexed.statements.add(Jump(callTarget, ongoto.position))
|
||||
}
|
||||
|
||||
val ifSt = if(ongoto.elsepart==null || ongoto.elsepart!!.isEmpty()) {
|
||||
// if index<numlabels call(labels[index])
|
||||
val compare = BinaryExpression(indexValue.copy(), "<", NumericLiteral.optimalInteger(numlabels, ongoto.position), ongoto.position)
|
||||
IfElse(compare, callIndexed, AnonymousScope.empty(), ongoto.position)
|
||||
} else {
|
||||
// if index>=numlabels elselabel() else call(labels[index])
|
||||
val compare = BinaryExpression(indexValue.copy(), ">=", NumericLiteral.optimalInteger(numlabels, ongoto.position), ongoto.position)
|
||||
IfElse(compare, ongoto.elsepart!!, callIndexed, ongoto.position)
|
||||
}
|
||||
|
||||
val replacementScope = AnonymousScope(if(conditionVar==null)
|
||||
mutableListOf(ifSt, jumplistArray)
|
||||
else
|
||||
mutableListOf(conditionVar, assignIndex!!, ifSt, jumplistArray)
|
||||
, ongoto.position)
|
||||
return listOf(IAstModification.ReplaceNode(ongoto, replacementScope, parent))
|
||||
}
|
||||
|
||||
override fun after(deref: PtrDereference, parent: Node): Iterable<IAstModification> {
|
||||
val isLHS = parent is AssignTarget
|
||||
val varDt = (deref.firstTarget() as? VarDecl)?.datatype
|
||||
if(varDt?.isUnsignedWord==true || (varDt?.isPointer==true && varDt.sub?.isByte==true)) {
|
||||
// replace ptr^^ by @(ptr) when ptr is uword or ^^byte
|
||||
val identifier = IdentifierReference(deref.chain, deref.position)
|
||||
if(isLHS && varDt.sub==BaseDataType.UBYTE) {
|
||||
val memwrite = DirectMemoryWrite(identifier, deref.position)
|
||||
return listOf(IAstModification.ReplaceNode(deref, memwrite, parent))
|
||||
} else if(!isLHS) {
|
||||
val memread = DirectMemoryRead(identifier, deref.position)
|
||||
val replacement = if (varDt.sub == BaseDataType.BYTE)
|
||||
TypecastExpression(memread, DataType.BYTE, true, memread.position)
|
||||
else
|
||||
memread
|
||||
return listOf(IAstModification.ReplaceNode(deref, replacement, parent))
|
||||
}
|
||||
}
|
||||
|
||||
val expr = deref.parent as? BinaryExpression
|
||||
if (expr != null && expr.operator == ".") {
|
||||
if (expr.left is IdentifierReference && expr.right === deref) {
|
||||
// replace (a) . (b^^) by (a.b)^^
|
||||
val name = (expr.left as IdentifierReference).nameInSource + deref.chain
|
||||
val replacement = PtrDereference(name, deref.derefLast, deref.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, replacement, expr.parent))
|
||||
} else if(expr.left===deref && expr.right is ArrayIndexedExpression) {
|
||||
// replace (a^^) . ( s[b] ) by (a^^.s^^)[b]
|
||||
val idx = expr.right as ArrayIndexedExpression
|
||||
if(idx.plainarrayvar!=null) {
|
||||
val name = deref.chain + idx.plainarrayvar!!.nameInSource
|
||||
val ptrDeref = PtrDereference(name, false, deref.position)
|
||||
val indexer = ArrayIndexedExpression(null, ptrDeref, idx.indexer, idx.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, indexer, expr.parent))
|
||||
} else {
|
||||
TODO("convert ptr.p[idx]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(deref: ArrayIndexedPtrDereference, parent: Node): Iterable<IAstModification> {
|
||||
// get rid of the ArrayIndexedPtrDereference AST node, replace it with other AST nodes that are equivalent
|
||||
// z[i]^^.field --> (z[i]) . (field)
|
||||
val firstIndexed = deref.chain.indexOfFirst { it.second!=null }
|
||||
require(firstIndexed==0) {"array dereference expected on first element in the chain, anything before it should be part of the parent binexpression"}
|
||||
val index = deref.chain.first()
|
||||
val tail = deref.chain.drop(1)
|
||||
|
||||
if(tail.any { it.second!=null }) {
|
||||
TODO("support multiple array indexed dereferencings ${deref.position}")
|
||||
} else {
|
||||
val pointer = IdentifierReference(listOf(index.first), deref.position)
|
||||
val left = ArrayIndexedExpression(pointer, null, index.second!!, deref.position)
|
||||
val right = PtrDereference(tail.map { it.first }, deref.derefLast, deref.position)
|
||||
val derefExpr = BinaryExpression(left, ".", right, deref.position)
|
||||
return listOf(IAstModification.ReplaceNode(deref, derefExpr, parent))
|
||||
}
|
||||
}
|
||||
|
||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||
subroutine.returntypes.withIndex().forEach { (idx, rt) ->
|
||||
if(rt.isPointer && rt.sub==BaseDataType.STR) {
|
||||
errors.info("^^str replaced by ^^ubyte in return type(s)", subroutine.position)
|
||||
subroutine.returntypes[idx] = DataType.pointer(BaseDataType.UBYTE)
|
||||
}
|
||||
}
|
||||
|
||||
subroutine.parameters.withIndex().forEach { (idx, param) ->
|
||||
if(param.type.isPointer && param.type.sub==BaseDataType.STR) {
|
||||
errors.info("^^str replaced by ^^ubyte", param.position)
|
||||
subroutine.parameters[idx] = SubroutineParameter(param.name, DataType.pointer(BaseDataType.UBYTE), param.zp, param.registerOrPair, param.position)
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
val targetDt = assignment.target.inferType(program)
|
||||
val sourceDt = assignment.value.inferType(program)
|
||||
if(targetDt.isStructInstance && sourceDt.isStructInstance) {
|
||||
if(targetDt == sourceDt) {
|
||||
val size = program.memsizer.memorySize(sourceDt.getOrUndef(), null)
|
||||
require(program.memsizer.memorySize(targetDt.getOrUndef(), null)==size)
|
||||
val sourcePtr = IdentifierReference((assignment.value as PtrDereference).chain, assignment.position)
|
||||
val targetPtr = IdentifierReference(assignment.target.pointerDereference!!.chain, assignment.position)
|
||||
val numBytes = NumericLiteral.optimalInteger(size, assignment.position)
|
||||
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), assignment.position),
|
||||
mutableListOf(sourcePtr, targetPtr, numBytes),
|
||||
false, assignment.position)
|
||||
return listOf(IAstModification.ReplaceNode(assignment, memcopy, parent))
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,15 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.ast.PtContainmentCheck
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.IErrorReporter
|
||||
|
||||
|
||||
internal class LiteralsToAutoVars(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
internal class LiteralsToAutoVarsAndRecombineIdentifiers(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
|
||||
override fun after(string: StringLiteral, parent: Node): Iterable<IAstModification> {
|
||||
if(string.parent !is VarDecl && string.parent !is WhenChoice) {
|
||||
@ -83,14 +80,28 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
if(decl.names.size>1) {
|
||||
|
||||
val fcallTarget = (decl.value as? IFunctionCall)?.target?.targetSubroutine(program)
|
||||
val fcallTarget = (decl.value as? IFunctionCall)?.target?.targetSubroutine()
|
||||
if(fcallTarget!=null) {
|
||||
// ubyte a,b,c = multi() --> ubyte a,b,c / a,b,c = multi()
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
val variables = decl.names.map {
|
||||
AssignTarget(IdentifierReference(listOf(it), decl.position), null, null, null, false, decl.position)
|
||||
AssignTarget(
|
||||
IdentifierReference(listOf(it), decl.position),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
position = decl.position
|
||||
)
|
||||
}
|
||||
val multiassignFuncCall = Assignment(AssignTarget(null, null, null, variables, false, decl.position), decl.value!!, AssignmentOrigin.VARINIT, decl.position)
|
||||
val multiassignFuncCall = Assignment(AssignTarget(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
variables,
|
||||
false,
|
||||
position = decl.position
|
||||
), decl.value!!, AssignmentOrigin.VARINIT, decl.position)
|
||||
decl.value = null
|
||||
modifications += IAstModification.InsertAfter(decl, multiassignFuncCall, parent as IStatementContainer)
|
||||
return modifications
|
||||
@ -98,8 +109,8 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
|
||||
|
||||
// note: the desugaring of a multi-variable vardecl has to be done here
|
||||
// and not in CodeDesugarer, that one is too late (identifiers can't be found otherwise)
|
||||
if(!decl.datatype.isNumericOrBool)
|
||||
errors.err("can only multi declare numeric and boolean variables", decl.position)
|
||||
if(!decl.datatype.isNumericOrBool && !decl.datatype.isPointer && !decl.datatype.isStructInstance)
|
||||
errors.err("cannot multi declare variables of this type", decl.position)
|
||||
if(decl.alignment != 0u) {
|
||||
errors.err("only single variable declarations can have alignment", decl.position)
|
||||
return noModifications
|
||||
@ -116,7 +127,7 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
|
||||
}
|
||||
|
||||
override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
|
||||
val target = alias.target.targetStatement(program)
|
||||
val target = alias.target.targetStatement()
|
||||
if(target is Alias) {
|
||||
val newAlias = Alias(alias.alias, target.target, alias.position)
|
||||
return listOf(IAstModification.ReplaceNode(alias, newAlias, parent))
|
||||
@ -125,11 +136,11 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
|
||||
}
|
||||
|
||||
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||
val target = identifier.targetStatement(program)
|
||||
val target = identifier.targetStatement()
|
||||
|
||||
// don't replace an identifier in an Alias or when the alias points to another alias (that will be resolved first elsewhere)
|
||||
if(target is Alias && parent !is Alias) {
|
||||
if(target.target.targetStatement(program) !is Alias)
|
||||
if(target.target.targetStatement() !is Alias)
|
||||
return listOf(IAstModification.ReplaceNode(identifier, target.target.copy(position = identifier.position), parent))
|
||||
}
|
||||
|
||||
@ -146,4 +157,26 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
|
||||
// }
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator==".") {
|
||||
val leftIdent = expr.left as? IdentifierReference
|
||||
val rightIndex = expr.right as? ArrayIndexedExpression
|
||||
if (leftIdent != null && rightIndex != null) {
|
||||
// maybe recombine IDENTIFIER . ARRAY[IDX] --> COMBINEDIDENTIFIER[IDX]
|
||||
val leftTarget = leftIdent.targetStatement()
|
||||
if(leftTarget==null || leftTarget !is StructDecl) {
|
||||
if(rightIndex.plainarrayvar!=null) {
|
||||
val combinedName = leftIdent.nameInSource + rightIndex.plainarrayvar!!.nameInSource
|
||||
val combined = IdentifierReference(combinedName, leftIdent.position)
|
||||
val indexer = ArrayIndexedExpression(combined, null, rightIndex.indexer, leftIdent.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, indexer, parent))
|
||||
} else {
|
||||
throw FatalAstException("didn't expect pointer[idx] in this phase already")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
}
|
@ -74,8 +74,11 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
}
|
||||
is UntilLoop -> throw FatalAstException("until loops must have been converted to jumps")
|
||||
is VarDecl -> transform(statement)
|
||||
is StructDecl -> transform(statement)
|
||||
is When -> transform(statement)
|
||||
is WhileLoop -> throw FatalAstException("while loops must have been converted to jumps")
|
||||
is OnGoto -> throw FatalAstException("ongoto must have been converted to array and separate call/goto")
|
||||
is StructFieldRef -> throw FatalAstException("should not occur as part of the actual AST")
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,9 +99,36 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
is StringLiteral -> transform(expr)
|
||||
is TypecastExpression -> transform(expr)
|
||||
is IfExpression -> transform(expr)
|
||||
is PtrDereference -> transform(expr)
|
||||
is ArrayIndexedPtrDereference -> throw FatalAstException("this should have been converted to some other ast nodes")
|
||||
}
|
||||
}
|
||||
|
||||
private fun transform(deref: PtrDereference): PtPointerDeref {
|
||||
val type = deref.inferType(program).getOrElse {
|
||||
throw FatalAstException("unknown dt")
|
||||
}
|
||||
|
||||
val startpointer: PtIdentifier
|
||||
val chain: List<String>
|
||||
var targetVar = deref.definingScope.lookup(deref.chain) as? VarDecl
|
||||
if(targetVar!=null) {
|
||||
startpointer = PtIdentifier(targetVar.scopedName.joinToString("."), targetVar.datatype, deref.position)
|
||||
chain = emptyList()
|
||||
} else {
|
||||
targetVar = deref.definingScope.lookup(deref.chain.take(1)) as? VarDecl
|
||||
if(targetVar!=null) {
|
||||
startpointer = PtIdentifier(targetVar.scopedName.joinToString("."), targetVar.datatype, deref.position)
|
||||
chain = deref.chain.drop(1)
|
||||
} else {
|
||||
TODO("find startpointer from ${deref.chain} ${deref.position}")
|
||||
}
|
||||
}
|
||||
val result = PtPointerDeref(type, chain, deref.derefLast, deref.position)
|
||||
result.add(startpointer)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun transform(ifExpr: IfExpression): PtIfExpression {
|
||||
val type = ifExpr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||
val ifexpr = PtIfExpression(type, ifExpr.position)
|
||||
@ -157,6 +187,38 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
else -> Pair("", null)
|
||||
}
|
||||
if(augmentedValue!=null) {
|
||||
|
||||
if(srcAssign.target.inferType(program).isPointer) {
|
||||
val expr = srcExpr as BinaryExpression
|
||||
if(expr.operator=="+") {
|
||||
// pointer arithmetic: add the size of the struct times the argument
|
||||
val leftDt = expr.left.inferType(program).getOrUndef()
|
||||
require(leftDt.isPointer && !expr.right.inferType(program).isPointer)
|
||||
val structSize = leftDt.size(program.memsizer)
|
||||
val constValue = augmentedValue.constValue(program)
|
||||
if(constValue!=null) {
|
||||
val total = constValue.number*structSize
|
||||
if (total == 0.0)
|
||||
return PtNop(srcAssign.position)
|
||||
else {
|
||||
val assign = PtAugmentedAssign(operator, srcAssign.position)
|
||||
assign.add(transform(srcAssign.target))
|
||||
assign.add(PtNumber(BaseDataType.UWORD, total, srcAssign.position))
|
||||
return assign
|
||||
}
|
||||
} else {
|
||||
val multiplication = PtBinaryExpression("*", DataType.UWORD, srcAssign.position)
|
||||
multiplication.add(transformExpression(augmentedValue))
|
||||
multiplication.add(PtNumber(BaseDataType.UWORD, structSize.toDouble(), srcAssign.position))
|
||||
val assign = PtAugmentedAssign(operator, srcAssign.position)
|
||||
assign.add(transform(srcAssign.target))
|
||||
assign.add(multiplication)
|
||||
return assign
|
||||
}
|
||||
} else
|
||||
throw FatalAstException("unexpected augmented assignment operator on pointer ${expr.operator}")
|
||||
}
|
||||
|
||||
val assign = PtAugmentedAssign(operator, srcAssign.position)
|
||||
assign.add(transform(srcAssign.target))
|
||||
assign.add(transformExpression(augmentedValue))
|
||||
@ -164,6 +226,9 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
}
|
||||
}
|
||||
|
||||
if(srcAssign.origin == AssignmentOrigin.VARINIT && srcAssign.parent is Block && srcAssign.value.constValue(program)?.number==0.0)
|
||||
throw FatalAstException("should not have a redundant block-level variable=0 assignment; it will be zeroed as part of BSS clear")
|
||||
|
||||
val assign = PtAssignment(srcAssign.position, srcAssign.origin==AssignmentOrigin.VARINIT)
|
||||
val multi = srcAssign.target.multi
|
||||
if(multi==null) {
|
||||
@ -177,18 +242,17 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
|
||||
private fun transform(srcTarget: AssignTarget): PtAssignTarget {
|
||||
val target = PtAssignTarget(srcTarget.void, srcTarget.position)
|
||||
if(srcTarget.identifier!=null)
|
||||
target.add(transform(srcTarget.identifier!!))
|
||||
else if(srcTarget.arrayindexed!=null)
|
||||
target.add(transform(srcTarget.arrayindexed!!))
|
||||
else if(srcTarget.memoryAddress!=null)
|
||||
target.add(transform(srcTarget.memoryAddress!!))
|
||||
else if(!srcTarget.void)
|
||||
throw FatalAstException("invalid AssignTarget")
|
||||
when {
|
||||
srcTarget.identifier!=null -> target.add(transform(srcTarget.identifier!!))
|
||||
srcTarget.arrayindexed!=null -> target.add(transform(srcTarget.arrayindexed!!))
|
||||
srcTarget.memoryAddress!=null -> target.add(transform(srcTarget.memoryAddress!!))
|
||||
srcTarget.pointerDereference !=null -> target.add(transform(srcTarget.pointerDereference!!))
|
||||
!srcTarget.void -> throw FatalAstException("invalid AssignTarget")
|
||||
}
|
||||
return target
|
||||
}
|
||||
|
||||
private fun transform(identifier: IdentifierReference): PtIdentifier {
|
||||
private fun transform(identifier: IdentifierReference): PtExpression {
|
||||
val (target, type) = identifier.targetNameAndType(program)
|
||||
return PtIdentifier(target, type, identifier.position)
|
||||
}
|
||||
@ -336,9 +400,18 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
}
|
||||
}
|
||||
|
||||
val (target, _) = srcCall.target.targetNameAndType(program)
|
||||
val iType = srcCall.inferType(program)
|
||||
val call = PtFunctionCall(target, iType.isUnknown && srcCall.parent !is Assignment, iType.getOrElse { DataType.UNDEFINED }, srcCall.position)
|
||||
val targetStruct = srcCall.target.targetStructDecl()
|
||||
val call =
|
||||
if(targetStruct!=null) {
|
||||
// a call to a struct yields a pointer to a struct instance and means: allocate a statically initialized struct instance of that type
|
||||
PtBuiltinFunctionCall("structalloc", false, true, DataType.pointer(targetStruct), srcCall.position)
|
||||
} else {
|
||||
// regular function call
|
||||
val (target, _) = srcCall.target.targetNameAndType(program)
|
||||
val iType = srcCall.inferType(program)
|
||||
PtFunctionCall(target, iType.isUnknown && srcCall.parent !is Assignment, iType.getOrElse { DataType.UNDEFINED }, srcCall.position)
|
||||
}
|
||||
|
||||
for (arg in srcCall.args)
|
||||
call.add(transformExpression(arg))
|
||||
return call
|
||||
@ -417,7 +490,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
if(binexpr.operator=="==" || binexpr.operator=="!=") {
|
||||
val fcall = binexpr.left as? FunctionCallExpression
|
||||
if(fcall!=null) {
|
||||
val returnRegs = fcall.target.targetSubroutine(program)?.asmReturnvaluesRegisters
|
||||
val returnRegs = fcall.target.targetSubroutine()?.asmReturnvaluesRegisters
|
||||
if(returnRegs!=null && returnRegs.size==1 && returnRegs[0].statusflag!=null) {
|
||||
return codeForStatusflag(fcall, returnRegs[0].statusflag!!, binexpr.operator == "==")
|
||||
}
|
||||
@ -426,7 +499,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
} else {
|
||||
val fcall = srcIf.condition as? FunctionCallExpression
|
||||
if (fcall != null) {
|
||||
val returnRegs = fcall.target.targetSubroutine(program)?.asmReturnvaluesRegisters
|
||||
val returnRegs = fcall.target.targetSubroutine()?.asmReturnvaluesRegisters
|
||||
if(returnRegs!=null && returnRegs.size==1 && returnRegs[0].statusflag!=null) {
|
||||
return codeForStatusflag(fcall, returnRegs[0].statusflag!!, false)
|
||||
}
|
||||
@ -436,7 +509,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
if(prefix!=null && prefix.operator=="not") {
|
||||
val prefixedFcall = prefix.expression as? FunctionCallExpression
|
||||
if (prefixedFcall != null) {
|
||||
val returnRegs = prefixedFcall.target.targetSubroutine(program)?.asmReturnvaluesRegisters
|
||||
val returnRegs = prefixedFcall.target.targetSubroutine()?.asmReturnvaluesRegisters
|
||||
if (returnRegs != null && returnRegs.size == 1 && returnRegs[0].statusflag != null) {
|
||||
return codeForStatusflag(prefixedFcall, returnRegs[0].statusflag!!, true)
|
||||
}
|
||||
@ -502,6 +575,8 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
private fun transformAsmSub(srcSub: Subroutine): PtAsmSub {
|
||||
val params = srcSub.asmParameterRegisters.zip(srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.registerOrPair, it.position) })
|
||||
val varbank = if(srcSub.asmAddress?.varbank==null) null else transform(srcSub.asmAddress!!.varbank!!)
|
||||
if(varbank!=null && varbank !is PtIdentifier)
|
||||
throw FatalAstException("varbank must be a regular variable")
|
||||
val asmAddr = if(srcSub.asmAddress==null) null else {
|
||||
val constAddr = srcSub.asmAddress!!.address.constValue(program)
|
||||
if(constAddr==null) throw FatalAstException("extsub address should be a constant")
|
||||
@ -551,11 +626,10 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
if(it.isString) DataType.UWORD else it
|
||||
}
|
||||
// do not bother about the 'inline' hint of the source subroutine.
|
||||
val sub = PtSub(srcSub.name,
|
||||
srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.registerOrPair, it.position) },
|
||||
returnTypes,
|
||||
srcSub.position)
|
||||
sub.parameters.forEach { it.parent=sub }
|
||||
val sub = PtSub(srcSub.name,srcSub.position)
|
||||
val signature = PtSubSignature( returnTypes, srcSub.position)
|
||||
srcSub.parameters.forEach { signature.add(PtSubroutineParameter(it.name, it.type, it.registerOrPair, it.position)) }
|
||||
sub.add(signature)
|
||||
makeScopeVarsDecls(vardecls).forEach { sub.add(it) }
|
||||
for (statement in statements)
|
||||
sub.add(transformStatement(statement))
|
||||
@ -575,6 +649,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
srcVar.datatype,
|
||||
srcVar.zeropage,
|
||||
srcVar.alignment,
|
||||
srcVar.dirty,
|
||||
value,
|
||||
srcVar.arraysize?.constIndex()?.toUInt(),
|
||||
srcVar.position
|
||||
@ -585,6 +660,10 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
}
|
||||
}
|
||||
|
||||
private fun transform(struct: StructDecl): PtStructDecl {
|
||||
return PtStructDecl(struct.name, struct.fields, struct.position)
|
||||
}
|
||||
|
||||
private fun transform(srcWhen: When): PtWhen {
|
||||
val w = PtWhen(srcWhen.position)
|
||||
w.add(transformExpression(srcWhen.condition))
|
||||
@ -611,22 +690,44 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
}
|
||||
|
||||
private fun transform(src: AddressOf): PtAddressOf {
|
||||
val addr = PtAddressOf(src.position, src.msb)
|
||||
addr.add(transform(src.identifier))
|
||||
if(src.arrayIndex!=null)
|
||||
val addr = PtAddressOf(src.inferType(program).getOrUndef(), src.position, src.msb)
|
||||
if(src.identifier!=null)
|
||||
addr.add(transform(src.identifier!!))
|
||||
if (src.arrayIndex != null)
|
||||
addr.add(transformExpression(src.arrayIndex!!.indexExpr))
|
||||
if (src.dereference!=null)
|
||||
addr.add(transformExpression(src.dereference!!))
|
||||
return addr
|
||||
}
|
||||
|
||||
private fun transform(srcArr: ArrayIndexedExpression): PtArrayIndexer {
|
||||
val dt = srcArr.arrayvar.targetVarDecl(program)!!.datatype
|
||||
if(!dt.isArray && !dt.isString)
|
||||
throw FatalAstException("array indexing can only be used on array or string variables ${srcArr.position}")
|
||||
val eltType = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||
val array = PtArrayIndexer(eltType, srcArr.position)
|
||||
array.add(transform(srcArr.arrayvar))
|
||||
array.add(transformExpression(srcArr.indexer.indexExpr))
|
||||
return array
|
||||
if(srcArr.plainarrayvar!=null) {
|
||||
val dt = srcArr.plainarrayvar!!.inferType(program)
|
||||
if (!dt.isArray && !dt.isString && !dt.isPointer)
|
||||
throw FatalAstException("array indexing can only be used on array, string or pointer variables ${srcArr.position}")
|
||||
val eltType = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||
val array = PtArrayIndexer(eltType, srcArr.position)
|
||||
array.add(transform(srcArr.plainarrayvar!!))
|
||||
array.add(transformExpression(srcArr.indexer.indexExpr))
|
||||
return array
|
||||
}
|
||||
if(srcArr.pointerderef!=null) {
|
||||
val dt = srcArr.pointerderef!!.inferType(program)
|
||||
if(dt.isUnsignedWord) {
|
||||
val array = PtArrayIndexer(DataType.UBYTE, srcArr.position)
|
||||
array.add(transform(srcArr.pointerderef!!))
|
||||
array.add(transformExpression(srcArr.indexer.indexExpr))
|
||||
return array
|
||||
} else if(dt.isPointer) {
|
||||
val eltType = dt.getOrUndef().dereference()
|
||||
val array = PtArrayIndexer(eltType, srcArr.position)
|
||||
array.add(transform(srcArr.pointerderef!!))
|
||||
array.add(transformExpression(srcArr.indexer.indexExpr))
|
||||
return array
|
||||
} else
|
||||
TODO("transform pointer index $dt ${srcArr.position}")
|
||||
}
|
||||
throw FatalAstException("expected plain array variable or pointer dereference")
|
||||
}
|
||||
|
||||
private fun transform(srcArr: ArrayLiteral): PtArray {
|
||||
@ -639,12 +740,139 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
return arr
|
||||
}
|
||||
|
||||
private fun transform(srcExpr: BinaryExpression): PtBinaryExpression {
|
||||
val type = srcExpr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||
val expr = PtBinaryExpression(srcExpr.operator, type, srcExpr.position)
|
||||
expr.add(transformExpression(srcExpr.left))
|
||||
expr.add(transformExpression(srcExpr.right))
|
||||
return expr
|
||||
private fun transform(srcExpr: BinaryExpression): PtExpression {
|
||||
val type = srcExpr.inferType(program).getOrElse {
|
||||
throw FatalAstException("unknown dt $srcExpr")
|
||||
}
|
||||
if(srcExpr.operator==".") {
|
||||
when (srcExpr.right) {
|
||||
is IdentifierReference -> {
|
||||
val arrayIndexer = srcExpr.left as? ArrayIndexedExpression
|
||||
val valueName = srcExpr.right as? IdentifierReference
|
||||
if (arrayIndexer == null) {
|
||||
TODO("unexpected deref expression ${srcExpr.position}")
|
||||
} else if(valueName?.nameInSource?.size!=1) {
|
||||
TODO("unexpected deref expression ${srcExpr.position}")
|
||||
} else {
|
||||
// the expression is: a.b.c[i] . value
|
||||
val result = PtBinaryExpression(".", type, srcExpr.position)
|
||||
result.add(transformExpression(arrayIndexer))
|
||||
result.add(PtIdentifier(valueName.nameInSource[0], type, srcExpr.position))
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
is ArrayIndexedExpression -> {
|
||||
errors.err("at the moment it is not possible to chain array syntax on pointers like ...p1[x].p2[y]... use separate expressions for the time being", srcExpr.right.position) // TODO add support for chained array syntax on pointers (rewrite ast?)
|
||||
return PtIdentifier("<dummy>", DataType.UNDEFINED, Position.DUMMY)
|
||||
}
|
||||
|
||||
else -> throw FatalAstException("unknown deref at ${srcExpr.position}")
|
||||
}
|
||||
} else {
|
||||
if(srcExpr.left.inferType(program).isPointer || srcExpr.right.inferType(program).isPointer) {
|
||||
return when (srcExpr.operator) {
|
||||
"+", "-" -> transformWithPointerArithmetic(srcExpr)
|
||||
in ComparisonOperators -> transformWithPointerComparison(srcExpr)
|
||||
else -> throw FatalAstException("unsupported operator on pointer: ${srcExpr.operator} at ${srcExpr.position}")
|
||||
}
|
||||
} else {
|
||||
val expr = PtBinaryExpression(srcExpr.operator, type, srcExpr.position)
|
||||
expr.add(transformExpression(srcExpr.left))
|
||||
expr.add(transformExpression(srcExpr.right))
|
||||
return expr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformWithPointerComparison(expr: BinaryExpression): PtBinaryExpression {
|
||||
val leftDt = expr.left.inferType(program)
|
||||
val rightDt = expr.right.inferType(program)
|
||||
require(leftDt.isPointer || leftDt.isWords)
|
||||
require(rightDt.isPointer || rightDt.isWords)
|
||||
val comparison = PtBinaryExpression(expr.operator, DataType.BOOL, expr.position)
|
||||
comparison.add(transformExpression(expr.left))
|
||||
comparison.add(transformExpression(expr.right))
|
||||
return comparison
|
||||
}
|
||||
|
||||
private fun transformWithPointerArithmetic(expr: BinaryExpression): PtExpression {
|
||||
val operator = expr.operator
|
||||
require(operator=="+" || operator=="-")
|
||||
// below where '+' is used, you can substitute '-'.
|
||||
// pointer arithmetic: ptr + value
|
||||
val leftDt = expr.left.inferType(program).getOrUndef()
|
||||
val rightDt = expr.right.inferType(program).getOrUndef()
|
||||
|
||||
if(leftDt.isPointer && !rightDt.isPointer) {
|
||||
val resultDt = leftDt
|
||||
val structSize = leftDt.size(program.memsizer)
|
||||
val constValue = expr.right.constValue(program)
|
||||
if(constValue!=null) {
|
||||
// ptr + constvalue * structsize
|
||||
val total = constValue.number*structSize
|
||||
if (total == 0.0)
|
||||
return transformExpression(expr.left)
|
||||
else {
|
||||
val plusorminus = PtBinaryExpression(operator, resultDt, expr.position)
|
||||
plusorminus.add(transformExpression(expr.left))
|
||||
plusorminus.add(PtNumber(BaseDataType.UWORD, total, expr.position))
|
||||
return plusorminus
|
||||
}
|
||||
} else {
|
||||
if(structSize==1) {
|
||||
// ptr + right, just keep it as it is
|
||||
val plus = PtBinaryExpression("+", DataType.UWORD, expr.position)
|
||||
plus.add(transformExpression(expr.left))
|
||||
plus.add(transformExpression(expr.right))
|
||||
return plus
|
||||
} else {
|
||||
// ptr + right * structSize
|
||||
val total = PtBinaryExpression("*", DataType.UWORD, expr.position)
|
||||
total.add(transformExpression(expr.right))
|
||||
total.add(PtNumber(BaseDataType.UWORD, structSize.toDouble(), expr.position))
|
||||
val plusorminus = PtBinaryExpression(operator, resultDt, expr.position)
|
||||
plusorminus.add(transformExpression(expr.left))
|
||||
plusorminus.add(total)
|
||||
return plusorminus
|
||||
}
|
||||
}
|
||||
} else if(!leftDt.isPointer && rightDt.isPointer) {
|
||||
val resultDt = rightDt
|
||||
val structSize = rightDt.size(program.memsizer)
|
||||
val constValue = expr.left.constValue(program)
|
||||
if(constValue!=null) {
|
||||
// ptr + constvalue * structsize
|
||||
val total = constValue.number*structSize
|
||||
if (total == 0.0)
|
||||
return transformExpression(expr.right)
|
||||
else {
|
||||
val plusorminus = PtBinaryExpression(operator, resultDt, expr.position)
|
||||
plusorminus.add(transformExpression(expr.right))
|
||||
plusorminus.add(PtNumber(BaseDataType.UWORD, total, expr.position))
|
||||
return plusorminus
|
||||
}
|
||||
} else {
|
||||
if(structSize==1) {
|
||||
// ptr + left, just keep it as it is
|
||||
val plus = PtBinaryExpression("+", DataType.UWORD, expr.position)
|
||||
plus.add(transformExpression(expr.left))
|
||||
plus.add(transformExpression(expr.right))
|
||||
return plus
|
||||
} else {
|
||||
// ptr + left * structSize
|
||||
val total = PtBinaryExpression("*", DataType.UWORD, expr.position)
|
||||
total.add(transformExpression(expr.left))
|
||||
total.add(PtNumber(BaseDataType.UWORD, structSize.toDouble(), expr.position))
|
||||
val plusorminus = PtBinaryExpression(operator, resultDt, expr.position)
|
||||
plusorminus.add(transformExpression(expr.right))
|
||||
plusorminus.add(total)
|
||||
return plusorminus
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw FatalAstException("weird pointer arithmetic ${expr.position}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun transform(srcCheck: ContainmentCheck): PtExpression {
|
||||
@ -666,14 +894,14 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
expr.add(high)
|
||||
} else {
|
||||
val low = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
|
||||
val lowFloat = PtTypeCast(BaseDataType.FLOAT, range.from.position)
|
||||
val lowFloat = PtTypeCast(DataType.FLOAT, range.from.position)
|
||||
lowFloat.add(transformExpression(range.from))
|
||||
low.add(lowFloat)
|
||||
low.add(x1)
|
||||
expr.add(low)
|
||||
val high = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
|
||||
high.add(x2)
|
||||
val highFLoat = PtTypeCast(BaseDataType.FLOAT, range.to.position)
|
||||
val highFLoat = PtTypeCast(DataType.FLOAT, range.to.position)
|
||||
highFLoat.add(transformExpression(range.to))
|
||||
high.add(highFLoat)
|
||||
expr.add(high)
|
||||
@ -764,11 +992,11 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
private fun transform(srcCast: TypecastExpression): PtTypeCast {
|
||||
val cast = PtTypeCast(srcCast.type, srcCast.position)
|
||||
cast.add(transformExpression(srcCast.expression))
|
||||
require(cast.type!=cast.value.type)
|
||||
require(cast.type!=cast.value.type) {
|
||||
"bogus typecast shouldn't occur at ${srcCast.position}" }
|
||||
return cast
|
||||
}
|
||||
|
||||
|
||||
private fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
|
||||
return if (SourceCode.isLibraryResource(filename)) {
|
||||
return com.github.michaelbull.result.runCatching {
|
||||
|
@ -1,12 +1,76 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.code.StStruct
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal fun postprocessSimplifiedAst(program: PtProgram, st: SymbolTable, errors: IErrorReporter) {
|
||||
internal fun postprocessSimplifiedAst(
|
||||
program: PtProgram,
|
||||
st: SymbolTable,
|
||||
option: CompilationOptions,
|
||||
errors: IErrorReporter
|
||||
) {
|
||||
processDefers(program, st, errors)
|
||||
processSubtypesIntoStReferences(program, st)
|
||||
|
||||
if(option.compTarget.cpu!=CpuType.VIRTUAL)
|
||||
checkForPointerTypesOn6502(program, errors) // TODO remove this once the 6502 codegen can deal with pointer types and structs
|
||||
}
|
||||
|
||||
private fun checkForPointerTypesOn6502(program: PtProgram, errors: IErrorReporter) {
|
||||
fun check(node: PtNode) {
|
||||
when(node) {
|
||||
//is PtAddressOf -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
is PtAssignTarget -> if(!node.void && node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
//is PtBinaryExpression -> if(node.left.type.isPointer || node.right.type.isPointer) errors.err("cannot do pointer arithmetic yet on 6502 target $node", node.position)
|
||||
is PtIdentifier -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
is PtPointerDeref -> errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
is PtPrefix -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
is PtTypeCast -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
is PtStructDecl -> errors.err("cannot use struct type yet on 6502 target $node", node.position)
|
||||
is PtSubroutineParameter -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
is PtVariable -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
is PtSubSignature -> if(node.returns.any{it.isPointer}) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
walkAst(program) { node, _ -> check(node) }
|
||||
}
|
||||
|
||||
|
||||
private fun processSubtypesIntoStReferences(program: PtProgram, st: SymbolTable) {
|
||||
|
||||
fun getStStruct(subType: ISubType): StStruct {
|
||||
val stNode = st.lookup(subType.scopedNameString) as? StStruct
|
||||
if(stNode != null)
|
||||
return stNode
|
||||
else
|
||||
throw FatalAstException("cannot find in ST: ${subType.scopedNameString} $subType")
|
||||
}
|
||||
|
||||
fun fixSubtype(type: DataType) {
|
||||
if(type.subType!=null && type.subType !is StStruct) {
|
||||
type.subType = getStStruct(type.subType!!)
|
||||
}
|
||||
}
|
||||
|
||||
fun fixSubtypes(node: PtNode) {
|
||||
when(node) {
|
||||
is IPtVariable -> fixSubtype(node.type)
|
||||
is PtPointerDeref -> fixSubtype(node.type)
|
||||
is PtStructDecl -> node.fields.forEach { fixSubtype(it.first) }
|
||||
is PtAsmSub -> node.returns.forEach { fixSubtype(it.second) }
|
||||
is PtExpression -> fixSubtype(node.type)
|
||||
is PtSubSignature -> node.returns.forEach { fixSubtype(it) }
|
||||
is PtSubroutineParameter -> fixSubtype(node.type)
|
||||
else -> { /* has no datatype */ }
|
||||
}
|
||||
}
|
||||
|
||||
walkAst(program) { node, _ -> fixSubtypes(node) }
|
||||
}
|
||||
|
||||
|
||||
@ -46,6 +110,7 @@ private fun setDeferMasks(program: PtProgram, errors: IErrorReporter): Map<PtSub
|
||||
DataType.UBYTE,
|
||||
ZeropageWish.NOT_IN_ZEROPAGE,
|
||||
0u,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
sub.position
|
||||
@ -55,8 +120,9 @@ private fun setDeferMasks(program: PtProgram, errors: IErrorReporter): Map<PtSub
|
||||
it.add(PtIdentifier(sub.scopedName+"."+maskVarName, DataType.UBYTE, sub.position))
|
||||
})
|
||||
assignZero.add(PtNumber(BaseDataType.UBYTE, 0.0, sub.position))
|
||||
sub.add(0, assignZero)
|
||||
sub.add(0, deferVariable)
|
||||
val firstIndex = sub.children.indexOfFirst { it !is PtSubSignature } // first child node is the sub's signature so add below that one
|
||||
sub.add(firstIndex, assignZero)
|
||||
sub.add(firstIndex, deferVariable)
|
||||
|
||||
for((deferIndex, defer) in defers.withIndex()) {
|
||||
// replace the defer statement with one that enables the bit in the mask for this defer
|
||||
@ -159,9 +225,7 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
|
||||
popCalls.forEach { newRet.add(it) }
|
||||
group.add(PtFunctionCall(ret.definingSub()!!.scopedName+"."+invokeDefersRoutineName, true,DataType.UNDEFINED, ret.position))
|
||||
group.add(newRet)
|
||||
group.parent = ret.parent
|
||||
val idx = ret.parent.children.indexOf(ret)
|
||||
ret.parent.children[idx] = group
|
||||
replaceNode(ret, group)
|
||||
}
|
||||
|
||||
// subroutine ends
|
||||
@ -197,9 +261,7 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
|
||||
})
|
||||
branchcc.add(PtNodeGroup())
|
||||
defersRoutine.add(branchcc)
|
||||
for(c in defer.children) {
|
||||
defersRoutine.add(c)
|
||||
}
|
||||
transferChildren(defer, defersRoutine, true)
|
||||
defersRoutine.add(PtLabel(skiplabel, Position.DUMMY))
|
||||
}
|
||||
// val printMask = PtFunctionCall("txt.print_ubbin", true, DataType.UNDEFINED, Position.DUMMY)
|
||||
@ -211,3 +273,18 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
|
||||
sub.add(defersRoutine)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun transferChildren(source: PtNode, target: PtNode, keepExisting: Boolean = false) {
|
||||
if(!keepExisting)
|
||||
target.children.clear()
|
||||
for(c in source.children)
|
||||
target.add(c)
|
||||
}
|
||||
|
||||
|
||||
private fun replaceNode(oldNode: PtNode, newNode: PtNode) {
|
||||
newNode.parent = oldNode.parent
|
||||
val idx = oldNode.parent.children.indexOf(oldNode)
|
||||
oldNode.parent.children[idx] = newNode
|
||||
}
|
||||
|
@ -49,10 +49,11 @@ internal class StatementReorderer(
|
||||
if(decl.dirty && decl.value!=null)
|
||||
errors.err("dirty variable can't have initialization value", decl.position)
|
||||
|
||||
if (decl.datatype.isNumericOrBool) {
|
||||
if (decl.datatype.isNumericOrBool || decl.datatype.isPointer) {
|
||||
if(decl !in declsProcessedWithInitAssignment) {
|
||||
declsProcessedWithInitAssignment.add(decl)
|
||||
if (decl.value == null) {
|
||||
if (decl.value == null || decl.value?.constValue(program)?.number==0.0) {
|
||||
decl.value = null
|
||||
if (decl.origin==VarDeclOrigin.USERCODE && decl.allowInitializeWithZero) {
|
||||
if(decl.dirty) {
|
||||
// no initialization at all!
|
||||
@ -62,13 +63,18 @@ internal class StatementReorderer(
|
||||
// unless there's already an assignment below it, that initializes the value (or a for loop that uses it as loopvar).
|
||||
// This allows you to restart the program and have the same starting values of the variables
|
||||
// So basically consider 'ubyte xx' as a short form for 'ubyte xx; xx=0'
|
||||
decl.value = null
|
||||
val canskip = canSkipInitializationWith0(decl)
|
||||
if (!canskip) {
|
||||
// (this explicit initialization assignment to 0 is not required for global variables in the block scope, these are zeroed as part of the BSS section clear)
|
||||
if (!canSkipInitializationWith0(decl)) {
|
||||
// Add assignment to initialize with zero
|
||||
// Note: for block-level vars, this will introduce assignments in the block scope. These have to be dealt with correctly later.
|
||||
val identifier = IdentifierReference(listOf(decl.name), decl.position)
|
||||
val assignzero = Assignment(AssignTarget(identifier, null, null, null, false, decl.position),
|
||||
val assignzero = Assignment(AssignTarget(
|
||||
identifier,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
position = decl.position
|
||||
),
|
||||
decl.zeroElementValue(), AssignmentOrigin.VARINIT, decl.position)
|
||||
return listOf(IAstModification.InsertAfter(
|
||||
decl, assignzero, parent as IStatementContainer
|
||||
@ -81,7 +87,7 @@ internal class StatementReorderer(
|
||||
// So basically consider 'ubyte xx=99' as a short form for 'ubyte xx; xx=99'
|
||||
val pos = decl.value!!.position
|
||||
val identifier = IdentifierReference(listOf(decl.name), pos)
|
||||
val assign = Assignment(AssignTarget(identifier, null, null, null, false, pos),
|
||||
val assign = Assignment(AssignTarget(identifier, null, null, null, false, position = pos),
|
||||
decl.value!!, AssignmentOrigin.VARINIT, pos)
|
||||
decl.value = null
|
||||
return listOf(IAstModification.InsertAfter(
|
||||
@ -96,11 +102,11 @@ internal class StatementReorderer(
|
||||
// (that code only triggers on regular assignment, not on variable initializers)
|
||||
val ident = decl.value as? IdentifierReference
|
||||
if(ident!=null) {
|
||||
val target = ident.targetVarDecl(program)
|
||||
val target = ident.targetVarDecl()
|
||||
if(target!=null && target.isArray) {
|
||||
val pos = decl.value!!.position
|
||||
val identifier = IdentifierReference(listOf(decl.name), pos)
|
||||
val assign = Assignment(AssignTarget(identifier, null, null, null, false, pos),
|
||||
val assign = Assignment(AssignTarget(identifier, null, null, null, false, position = pos),
|
||||
decl.value!!, AssignmentOrigin.VARINIT, pos)
|
||||
decl.value = null
|
||||
return listOf(IAstModification.InsertAfter(
|
||||
@ -115,6 +121,11 @@ internal class StatementReorderer(
|
||||
}
|
||||
|
||||
private fun canSkipInitializationWith0(decl: VarDecl): Boolean {
|
||||
// if the variable is declared in a block, we can omit the init with 0 because
|
||||
// the variable will be initialized to zero when the BSS section is cleared as a whole.
|
||||
if(decl.parent is Block)
|
||||
return true
|
||||
|
||||
// if there is an assignment to the variable below it (regular assign, or For loop),
|
||||
// and there is nothing important in between, we can skip the initialization.
|
||||
val statements = (decl.parent as? IStatementContainer)?.statements ?: return false
|
||||
@ -123,14 +134,14 @@ internal class StatementReorderer(
|
||||
when(stmt) {
|
||||
is Assignment -> {
|
||||
if (!stmt.isAugmentable) {
|
||||
val assignTargets = stmt.target.multi?.mapNotNull { it.identifier?.targetVarDecl(program) }
|
||||
val assignTargets = stmt.target.multi?.mapNotNull { it.identifier?.targetVarDecl() }
|
||||
if(assignTargets!=null) {
|
||||
if(decl in assignTargets) {
|
||||
stmt.origin = AssignmentOrigin.VARINIT
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
val assignTgt = stmt.target.identifier?.targetVarDecl(program)
|
||||
val assignTgt = stmt.target.identifier?.targetVarDecl()
|
||||
if (assignTgt == decl) {
|
||||
stmt.origin = AssignmentOrigin.VARINIT
|
||||
return true
|
||||
@ -143,11 +154,11 @@ internal class StatementReorderer(
|
||||
is ChainedAssignment -> {
|
||||
var chained: ChainedAssignment? = stmt
|
||||
while(chained!=null) {
|
||||
val assignTgt = chained.target.identifier?.targetVarDecl(program)
|
||||
val assignTgt = chained.target.identifier?.targetVarDecl()
|
||||
if (assignTgt == decl)
|
||||
return true
|
||||
if(chained.nested is Assignment) {
|
||||
if ((chained.nested as Assignment).target.identifier?.targetVarDecl(program) == decl) {
|
||||
if ((chained.nested as Assignment).target.identifier?.targetVarDecl() == decl) {
|
||||
(chained.nested as Assignment).origin = AssignmentOrigin.VARINIT
|
||||
return true
|
||||
}
|
||||
@ -325,7 +336,7 @@ internal class StatementReorderer(
|
||||
|
||||
private fun checkCopyArrayValue(assign: Assignment) {
|
||||
val identifier = assign.target.identifier!!
|
||||
val targetVar = identifier.targetVarDecl(program)!!
|
||||
val targetVar = identifier.targetVarDecl()!!
|
||||
|
||||
if(targetVar.arraysize==null) {
|
||||
errors.err("array has no defined size", assign.position)
|
||||
@ -340,7 +351,7 @@ internal class StatementReorderer(
|
||||
}
|
||||
|
||||
val sourceIdent = assign.value as IdentifierReference
|
||||
val sourceVar = sourceIdent.targetVarDecl(program)!!
|
||||
val sourceVar = sourceIdent.targetVarDecl()!!
|
||||
if(!sourceVar.isArray) {
|
||||
errors.err("value must be an array", sourceIdent.position)
|
||||
} else {
|
||||
|
@ -66,8 +66,12 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
if(valueDt isNotAssignableTo decl.datatype)
|
||||
return noModifications
|
||||
|
||||
// uwords are allowed to be assigned to pointers as initialization value without a cast
|
||||
if(decl.datatype.isPointer && valueDt.isUnsignedWord)
|
||||
return noModifications
|
||||
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
addTypecastOrCastedValueModification(modifications, declValue, decl.datatype.base, decl)
|
||||
addTypecastOrCastedValueModification(modifications, declValue, decl.datatype, decl)
|
||||
return modifications
|
||||
}
|
||||
}
|
||||
@ -98,7 +102,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
// if(rightDt.isBytes)
|
||||
// modifications += IAstModification.ReplaceNode(expr.right, TypecastExpression(expr.right, leftConstAsWord.type, true, expr.right.position), expr)
|
||||
}
|
||||
} else if (parent is TypecastExpression && parent.type == BaseDataType.UWORD && parent.parent is Assignment) {
|
||||
} else if (parent is TypecastExpression && parent.type.isUnsignedWord && parent.parent is Assignment) {
|
||||
val assign = parent.parent as Assignment
|
||||
if (assign.target.inferType(program).isWords) {
|
||||
modifications += IAstModification.ReplaceNode(expr.left, leftConstAsWord, expr)
|
||||
@ -131,34 +135,34 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
|
||||
if(leftDt issimpletype BaseDataType.BYTE && (rightDt issimpletype BaseDataType.UBYTE || rightDt issimpletype BaseDataType.UWORD)) {
|
||||
// cast left to unsigned
|
||||
val cast = TypecastExpression(expr.left, rightDt.getOrUndef().base, true, expr.left.position)
|
||||
val cast = TypecastExpression(expr.left, rightDt.getOrUndef(), true, expr.left.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||
}
|
||||
if(leftDt issimpletype BaseDataType.WORD && (rightDt issimpletype BaseDataType.UBYTE || rightDt issimpletype BaseDataType.UWORD)) {
|
||||
// cast left to unsigned word. Cast right to unsigned word if it is ubyte
|
||||
val mods = mutableListOf<IAstModification>()
|
||||
val cast = TypecastExpression(expr.left, BaseDataType.UWORD, true, expr.left.position)
|
||||
val cast = TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position)
|
||||
mods += IAstModification.ReplaceNode(expr.left, cast, expr)
|
||||
if(rightDt issimpletype BaseDataType.UBYTE) {
|
||||
mods += IAstModification.ReplaceNode(expr.right,
|
||||
TypecastExpression(expr.right, BaseDataType.UWORD, true, expr.right.position),
|
||||
TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position),
|
||||
expr)
|
||||
}
|
||||
return mods
|
||||
}
|
||||
if(rightDt issimpletype BaseDataType.BYTE && (leftDt issimpletype BaseDataType.UBYTE || leftDt issimpletype BaseDataType.UWORD)) {
|
||||
// cast right to unsigned
|
||||
val cast = TypecastExpression(expr.right, leftDt.getOrUndef().base, true, expr.right.position)
|
||||
val cast = TypecastExpression(expr.right, leftDt.getOrUndef(), true, expr.right.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, cast, expr))
|
||||
}
|
||||
if(rightDt issimpletype BaseDataType.WORD && (leftDt issimpletype BaseDataType.UBYTE || leftDt issimpletype BaseDataType.UWORD)) {
|
||||
// cast right to unsigned word. Cast left to unsigned word if it is ubyte
|
||||
val mods = mutableListOf<IAstModification>()
|
||||
val cast = TypecastExpression(expr.right, BaseDataType.UWORD, true, expr.right.position)
|
||||
val cast = TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position)
|
||||
mods += IAstModification.ReplaceNode(expr.right, cast, expr)
|
||||
if(leftDt issimpletype BaseDataType.UBYTE) {
|
||||
mods += IAstModification.ReplaceNode(expr.left,
|
||||
TypecastExpression(expr.left, BaseDataType.UWORD, true, expr.left.position),
|
||||
TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position),
|
||||
expr)
|
||||
}
|
||||
return mods
|
||||
@ -176,14 +180,57 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
} else {
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
when {
|
||||
toFix===expr.left -> addTypecastOrCastedValueModification(modifications, expr.left, commonDt.base, expr)
|
||||
toFix===expr.right -> addTypecastOrCastedValueModification(modifications, expr.right, commonDt.base, expr)
|
||||
toFix===expr.left -> addTypecastOrCastedValueModification(modifications, expr.left, commonDt, expr)
|
||||
toFix===expr.right -> addTypecastOrCastedValueModification(modifications, expr.right, commonDt, expr)
|
||||
else -> throw FatalAstException("confused binary expression side")
|
||||
}
|
||||
return modifications
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// comparison of a pointer with a number will simply treat the pointer as the uword that it is
|
||||
// this may require casting the other operand to uword as well
|
||||
if(expr.operator in ComparisonOperators) {
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
if(leftDt.isNumeric && rightDt.isPointer) {
|
||||
val cast = TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position)
|
||||
modifications += IAstModification.ReplaceNode(expr.right, cast, expr)
|
||||
if(!leftDt.isUnsignedWord && leftDt isAssignableTo InferredTypes.knownFor(BaseDataType.UWORD)) {
|
||||
val cast2 = TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position)
|
||||
modifications += IAstModification.ReplaceNode(expr.left, cast2, expr)
|
||||
}
|
||||
}
|
||||
else if(leftDt.isPointer && rightDt.isNumeric) {
|
||||
val cast = TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position)
|
||||
modifications += IAstModification.ReplaceNode(expr.left, cast, expr)
|
||||
if(!rightDt.isUnsignedWord && rightDt isAssignableTo InferredTypes.knownFor(BaseDataType.UWORD)) {
|
||||
val cast2 = TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position)
|
||||
modifications += IAstModification.ReplaceNode(expr.right, cast2, expr)
|
||||
}
|
||||
}
|
||||
return modifications
|
||||
}
|
||||
|
||||
|
||||
// pointer arithmetic
|
||||
if(leftDt.isPointer) {
|
||||
val cast = TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, cast, expr))
|
||||
} else if(rightDt.isPointer) {
|
||||
val cast = TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||
}
|
||||
}
|
||||
|
||||
// check if shifts have a positive integer shift type
|
||||
if(expr.operator=="<<" || expr.operator==">>") {
|
||||
if(rightDt.isInteger) {
|
||||
val rconst = expr.right.constValue(program)
|
||||
if(rconst!=null && rconst.number<0)
|
||||
errors.err("can only shift by a positive amount", expr.right.position)
|
||||
} else
|
||||
errors.err("right operand of bit shift must be an integer", expr.right.position)
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,7 +250,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
// special case, don't typecast STR/arrays to UWORD, we support those assignments "directly"
|
||||
return noModifications
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
addTypecastOrCastedValueModification(modifications, assignment.value, targettype.base, assignment)
|
||||
addTypecastOrCastedValueModification(modifications, assignment.value, targettype, assignment)
|
||||
return modifications
|
||||
} else {
|
||||
fun castLiteral(cvalue2: NumericLiteral): List<IAstModification.ReplaceNode> {
|
||||
@ -233,49 +280,63 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
|
||||
private fun afterFunctionCallArgs(call: IFunctionCall): Iterable<IAstModification> {
|
||||
// see if a typecast is needed to convert the arguments into the required parameter type
|
||||
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
val params = when(val sub = call.target.targetStatement(program)) {
|
||||
is BuiltinFunctionPlaceholder -> BuiltinFunctions.getValue(sub.name).parameters.toList()
|
||||
is Subroutine -> sub.parameters.map { FParam(it.name, it.type.base) }
|
||||
val paramsPossibleDatatypes = when(val sub = call.target.targetStatement(program.builtinFunctions)) {
|
||||
is BuiltinFunctionPlaceholder -> {
|
||||
BuiltinFunctions.getValue(sub.name).parameters.map {
|
||||
it.possibleDatatypes.map { dt ->
|
||||
if(dt.isArray)
|
||||
DataType.arrayFor(BaseDataType.BOOL, false) // the builtin function signature doesn't tell us the element type....
|
||||
else if(dt.isPointer)
|
||||
DataType.pointer(BaseDataType.UBYTE)
|
||||
else
|
||||
DataType.forDt(dt)
|
||||
}
|
||||
}
|
||||
}
|
||||
is Subroutine -> sub.parameters.map { listOf(it.type) }
|
||||
is StructDecl -> sub.fields.map { listOf(it.first) }
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
params.zip(call.args).forEach {
|
||||
val targetDt = it.first.possibleDatatypes.first()
|
||||
paramsPossibleDatatypes.zip(call.args).forEach {
|
||||
val possibleTargetDt = it.first.first()
|
||||
val targetDt = if(possibleTargetDt.isPointer) DataType.UWORD else possibleTargetDt // use UWORD instead of a pointer type (using words for pointers is allowed without further casting)
|
||||
val argIdt = it.second.inferType(program)
|
||||
if (argIdt.isKnown) {
|
||||
if (argIdt.isKnown && !targetDt.isStructInstance) {
|
||||
val argDt = argIdt.getOrUndef()
|
||||
if (argDt.base !in it.first.possibleDatatypes) {
|
||||
if (argDt !in it.first) {
|
||||
val identifier = it.second as? IdentifierReference
|
||||
val number = it.second as? NumericLiteral
|
||||
if(number!=null) {
|
||||
addTypecastOrCastedValueModification(modifications, it.second, targetDt, call as Node)
|
||||
} else if(identifier!=null && targetDt==BaseDataType.UWORD && argDt.isPassByRef) {
|
||||
if(!identifier.isSubroutineParameter(program)) {
|
||||
} else if(identifier!=null && targetDt.isUnsignedWord && argDt.isPassByRef) {
|
||||
if(!identifier.isSubroutineParameter()) {
|
||||
// We allow STR/ARRAY values for UWORD parameters.
|
||||
// If it's an array (not STR), take the address.
|
||||
if(!argDt.isString) {
|
||||
modifications += IAstModification.ReplaceNode(
|
||||
identifier,
|
||||
AddressOf(identifier, null, false, it.second.position),
|
||||
AddressOf(identifier, null, null, false, it.second.position),
|
||||
call as Node
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if(targetDt==BaseDataType.BOOL) {
|
||||
addTypecastOrCastedValueModification(modifications, it.second, BaseDataType.BOOL, call as Node)
|
||||
} else if(!targetDt.isIterable && argDt isAssignableTo DataType.forDt(targetDt)) {
|
||||
if(!argDt.isString || targetDt!=BaseDataType.UWORD)
|
||||
} else if(targetDt.isBool) {
|
||||
addTypecastOrCastedValueModification(modifications, it.second, DataType.BOOL, call as Node)
|
||||
} else if(!targetDt.isIterable && argDt isAssignableTo targetDt) {
|
||||
if(!argDt.isString || !targetDt.isUnsignedWord)
|
||||
addTypecastOrCastedValueModification(modifications, it.second, targetDt, call as Node)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val identifier = it.second as? IdentifierReference
|
||||
if(identifier!=null && targetDt==BaseDataType.UWORD) {
|
||||
if(identifier!=null && targetDt.isUnsignedWord) {
|
||||
// take the address of the identifier
|
||||
modifications += IAstModification.ReplaceNode(
|
||||
identifier,
|
||||
AddressOf(identifier, null, false, it.second.position),
|
||||
AddressOf(identifier, null, null, false, it.second.position),
|
||||
call as Node
|
||||
)
|
||||
}
|
||||
@ -286,7 +347,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
|
||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
// warn about any implicit type casts to Float, because that may not be intended
|
||||
if(typecast.implicit && typecast.type==BaseDataType.FLOAT) {
|
||||
if(typecast.implicit && typecast.type.isFloat) {
|
||||
if(options.floats)
|
||||
errors.info("integer implicitly converted to float. Suggestion: use float literals, add an explicit cast, or revert to integer arithmetic", typecast.position)
|
||||
else
|
||||
@ -297,30 +358,32 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
|
||||
override fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||
// make sure the memory address is an uword
|
||||
// make sure the memory address is an uword or a pointer (to whatever type), otherwise cast
|
||||
val dt = memread.addressExpression.inferType(program).getOr(DataType.UWORD)
|
||||
if(dt.isUndefined || dt.isUnsignedWord || dt.isPointer)
|
||||
return noModifications
|
||||
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
val dt = memread.addressExpression.inferType(program)
|
||||
if(dt.isKnown && !dt.getOr(DataType.UWORD).isUnsignedWord) {
|
||||
val castedValue = (memread.addressExpression as? NumericLiteral)?.cast(BaseDataType.UWORD, true)?.valueOrZero()
|
||||
if(castedValue!=null)
|
||||
modifications += IAstModification.ReplaceNode(memread.addressExpression, castedValue, memread)
|
||||
else
|
||||
addTypecastOrCastedValueModification(modifications, memread.addressExpression, BaseDataType.UWORD, memread)
|
||||
}
|
||||
val castedValue = (memread.addressExpression as? NumericLiteral)?.cast(BaseDataType.UWORD, true)?.valueOrZero()
|
||||
if(castedValue!=null)
|
||||
modifications += IAstModification.ReplaceNode(memread.addressExpression, castedValue, memread)
|
||||
else
|
||||
addTypecastOrCastedValueModification(modifications, memread.addressExpression, DataType.UWORD, memread)
|
||||
return modifications
|
||||
}
|
||||
|
||||
override fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> {
|
||||
// make sure the memory address is an uword
|
||||
// make sure the memory address is an uword or a pointer (to whatever type), otherwise cast
|
||||
val dt = memwrite.addressExpression.inferType(program).getOr(DataType.UWORD)
|
||||
if(dt.isUndefined || dt.isUnsignedWord || dt.isPointer)
|
||||
return noModifications
|
||||
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
val dt = memwrite.addressExpression.inferType(program)
|
||||
if(dt.isKnown && !dt.getOr(DataType.UWORD).isUnsignedWord) {
|
||||
val castedValue = (memwrite.addressExpression as? NumericLiteral)?.cast(BaseDataType.UWORD, true)?.valueOrZero()
|
||||
if(castedValue!=null)
|
||||
modifications += IAstModification.ReplaceNode(memwrite.addressExpression, castedValue, memwrite)
|
||||
else
|
||||
addTypecastOrCastedValueModification(modifications, memwrite.addressExpression, BaseDataType.UWORD, memwrite)
|
||||
}
|
||||
val castedValue = (memwrite.addressExpression as? NumericLiteral)?.cast(BaseDataType.UWORD, true)?.valueOrZero()
|
||||
if(castedValue!=null)
|
||||
modifications += IAstModification.ReplaceNode(memwrite.addressExpression, castedValue, memwrite)
|
||||
else
|
||||
addTypecastOrCastedValueModification(modifications, memwrite.addressExpression, DataType.UWORD, memwrite)
|
||||
return modifications
|
||||
}
|
||||
|
||||
@ -348,12 +411,18 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
if (returnDt istype subReturnType or returnDt.isNotAssignableTo(subReturnType))
|
||||
continue
|
||||
if (returnValue is NumericLiteral) {
|
||||
val cast = returnValue.cast(subReturnType.base, true)
|
||||
if(cast.isValid) {
|
||||
returnStmt.values[index] = cast.valueOrZero()
|
||||
if((returnValue.type == BaseDataType.UWORD || returnValue.type == BaseDataType.UBYTE) && subReturnType.isPointer) {
|
||||
// cast unsigned integer to the number type
|
||||
val cast = TypecastExpression(returnValue, subReturnType, true, returnValue.position)
|
||||
modifications += IAstModification.ReplaceNode(returnValue, cast, returnStmt)
|
||||
} else {
|
||||
val cast = returnValue.cast(subReturnType.base, true)
|
||||
if (cast.isValid) {
|
||||
returnStmt.values[index] = cast.valueOrZero()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addTypecastOrCastedValueModification(modifications, returnValue, subReturnType.base, returnStmt)
|
||||
addTypecastOrCastedValueModification(modifications, returnValue, subReturnType, returnStmt)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -386,7 +455,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
val varDt = when (parent) {
|
||||
is ContainmentCheck -> parent.element.inferType(program)
|
||||
is ForLoop -> parent.loopVarDt(program)
|
||||
else -> InferredTypes.InferredType.unknown()
|
||||
else -> InferredTypes.unknown()
|
||||
}
|
||||
return adjustRangeDts(range, fromConst, fromDt, toConst, toDt, varDt.getOrUndef(), parent)
|
||||
}
|
||||
@ -398,9 +467,9 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
for((index, elt) in array.value.withIndex()) {
|
||||
if (elt is IdentifierReference) {
|
||||
val eltType = elt.inferType(program)
|
||||
val tgt = elt.targetStatement(program)
|
||||
val tgt = elt.targetStatement()
|
||||
if(eltType.isIterable || tgt is Subroutine || tgt is Label || tgt is Block) {
|
||||
val addressof = AddressOf(elt, null, false, elt.position)
|
||||
val addressof = AddressOf(elt, null, null, false, elt.position)
|
||||
addressof.linkParents(array)
|
||||
array.value[index] = addressof
|
||||
}
|
||||
@ -411,6 +480,12 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
|
||||
override fun after(ifExpr: IfExpression, parent: Node): Iterable<IAstModification> {
|
||||
val ct = ifExpr.condition.inferType(program)
|
||||
if(!ct.isBool && (ct.isNumeric || ct.isPointer)) {
|
||||
val cast = TypecastExpression(ifExpr.condition, DataType.BOOL, true, ifExpr.condition.position)
|
||||
return listOf(IAstModification.ReplaceNode(ifExpr.condition, cast, ifExpr))
|
||||
}
|
||||
|
||||
val trueDt = ifExpr.truevalue.inferType(program)
|
||||
val falseDt = ifExpr.falsevalue.inferType(program)
|
||||
if (trueDt != falseDt) {
|
||||
@ -422,13 +497,40 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
)
|
||||
if (toFix != null) {
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
addTypecastOrCastedValueModification(modifications, toFix, commonDt.base, ifExpr)
|
||||
addTypecastOrCastedValueModification(modifications, toFix, commonDt, ifExpr)
|
||||
return modifications
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
|
||||
val ct = whileLoop.condition.inferType(program)
|
||||
if(!ct.isBool && (ct.isNumeric || ct.isPointer)) {
|
||||
val cast = TypecastExpression(whileLoop.condition, DataType.BOOL, true, whileLoop.condition.position)
|
||||
return listOf(IAstModification.ReplaceNode(whileLoop.condition, cast, whileLoop))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(ifElse: IfElse, parent: Node): Iterable<IAstModification> {
|
||||
val ct = ifElse.condition.inferType(program)
|
||||
if(!ct.isBool && (ct.isNumeric || ct.isPointer)) {
|
||||
val cast = TypecastExpression(ifElse.condition, DataType.BOOL, true, ifElse.condition.position)
|
||||
return listOf(IAstModification.ReplaceNode(ifElse.condition, cast, ifElse))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
|
||||
val ct = untilLoop.condition.inferType(program)
|
||||
if(!ct.isBool && (ct.isNumeric || ct.isPointer)) {
|
||||
val cast = TypecastExpression(untilLoop.condition, DataType.BOOL, true, untilLoop.condition.position)
|
||||
return listOf(IAstModification.ReplaceNode(untilLoop.condition, cast, untilLoop))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun adjustRangeDts(
|
||||
range: RangeExpression,
|
||||
fromConst: NumericLiteral?,
|
||||
@ -524,7 +626,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
|
||||
val (commonDt, toChange) = BinaryExpression.commonDatatype(fromDt, toDt, range.from, range.to)
|
||||
if(toChange!=null)
|
||||
addTypecastOrCastedValueModification(modifications, toChange, commonDt.base, range)
|
||||
addTypecastOrCastedValueModification(modifications, toChange, commonDt, range)
|
||||
|
||||
return modifications
|
||||
}
|
||||
@ -536,7 +638,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
val idxDt = arrayIndexedExpression.indexer.indexExpr.inferType(program).getOrUndef()
|
||||
if(idxDt.base.largerSizeThan(smaller.type)) {
|
||||
val newIdx = ArrayIndex(smaller, smaller.position)
|
||||
val newIndexer = ArrayIndexedExpression(arrayIndexedExpression.arrayvar, newIdx, arrayIndexedExpression.position)
|
||||
val newIndexer = ArrayIndexedExpression(arrayIndexedExpression.plainarrayvar, arrayIndexedExpression.pointerderef, newIdx, arrayIndexedExpression.position)
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, newIndexer, parent))
|
||||
}
|
||||
}
|
||||
@ -546,16 +648,33 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
private fun addTypecastOrCastedValueModification(
|
||||
modifications: MutableList<IAstModification>,
|
||||
expressionToCast: Expression,
|
||||
requiredType: BaseDataType,
|
||||
requiredType: DataType,
|
||||
parent: Node
|
||||
) {
|
||||
val sourceDt = expressionToCast.inferType(program).getOrUndef()
|
||||
if(sourceDt.base == requiredType)
|
||||
if(sourceDt.base == requiredType.base)
|
||||
return
|
||||
if(requiredType == BaseDataType.BOOL)
|
||||
if(requiredType.isBool) {
|
||||
if(sourceDt.isNumeric || sourceDt.isPointer) {
|
||||
// only allow numerics and pointers to be implicitly cast to bool
|
||||
val cast = TypecastExpression(expressionToCast, DataType.BOOL, true, expressionToCast.position)
|
||||
modifications += IAstModification.ReplaceNode(expressionToCast, cast, parent)
|
||||
}
|
||||
return
|
||||
if(expressionToCast is NumericLiteral && expressionToCast.type!=BaseDataType.FLOAT) { // refuse to automatically truncate floats
|
||||
val castedValue = expressionToCast.cast(requiredType, true)
|
||||
}
|
||||
|
||||
// uwords are allowed to be assigned to pointers without a cast
|
||||
if(requiredType.isPointer && sourceDt.isUnsignedWord)
|
||||
return
|
||||
if (requiredType.isUnsignedWord) {
|
||||
// & (address-of) is allowed to be assigned to an uword without a cast
|
||||
if (expressionToCast is AddressOf) return
|
||||
// casting a pointer to an uword is not needed
|
||||
if (expressionToCast.inferType(program).isPointer) return
|
||||
}
|
||||
|
||||
if(expressionToCast is NumericLiteral && expressionToCast.type!=BaseDataType.FLOAT && requiredType.isNumericOrBool) { // refuse to automatically truncate floats
|
||||
val castedValue = expressionToCast.cast(requiredType.base, true)
|
||||
if (castedValue.isValid) {
|
||||
val signOriginal = sign(expressionToCast.number)
|
||||
val signCasted = sign(castedValue.valueOrZero().number)
|
||||
@ -565,6 +684,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val cast = TypecastExpression(expressionToCast, requiredType, true, expressionToCast.position)
|
||||
modifications += IAstModification.ReplaceNode(expressionToCast, cast, parent)
|
||||
}
|
||||
|
@ -4,29 +4,28 @@ import prog8.ast.FatalAstException
|
||||
import prog8.code.ast.PtExpression
|
||||
import prog8.code.ast.PtFunctionCall
|
||||
import prog8.code.ast.PtTypeCast
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.DataType
|
||||
|
||||
|
||||
internal fun makePushPopFunctionCalls(value: PtExpression): Pair<PtFunctionCall, PtExpression> {
|
||||
var popTypecast: BaseDataType? = null
|
||||
var pushTypecast: BaseDataType? = null
|
||||
var popTypecast: DataType? = null
|
||||
var pushTypecast: DataType? = null
|
||||
var pushWord = false
|
||||
var pushFloat = false
|
||||
|
||||
when {
|
||||
value.type.isBool -> {
|
||||
pushTypecast = BaseDataType.UBYTE
|
||||
popTypecast = BaseDataType.BOOL
|
||||
pushTypecast = DataType.UBYTE
|
||||
popTypecast = DataType.BOOL
|
||||
}
|
||||
value.type.isSignedByte -> {
|
||||
pushTypecast = BaseDataType.UBYTE
|
||||
popTypecast = BaseDataType.BYTE
|
||||
pushTypecast = DataType.UBYTE
|
||||
popTypecast = DataType.BYTE
|
||||
}
|
||||
value.type.isSignedWord -> {
|
||||
pushWord = true
|
||||
pushTypecast = BaseDataType.UWORD
|
||||
popTypecast = BaseDataType.WORD
|
||||
pushTypecast = DataType.UWORD
|
||||
popTypecast = DataType.WORD
|
||||
}
|
||||
value.type.isUnsignedByte -> {}
|
||||
value.type.isUnsignedWord -> pushWord = true
|
||||
|
@ -24,10 +24,8 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
// check and possibly adjust value datatype vs decl datatype
|
||||
val valueType = decl.value?.inferType(program)
|
||||
if(valueType!=null && !valueType.istype(decl.datatype)) {
|
||||
if(valueType.isUnknown) {
|
||||
errors.err("value has incompatible type for ${decl.datatype}", decl.value!!.position)
|
||||
if(valueType.isUnknown)
|
||||
return noModifications
|
||||
}
|
||||
val valueDt = valueType.getOrUndef()
|
||||
when(decl.type) {
|
||||
VarDeclType.VAR -> {
|
||||
@ -41,8 +39,6 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
errors.err("value '$constValue' out of range for ${decl.datatype}", constValue.position)
|
||||
else
|
||||
errors.err("value out of range for ${decl.datatype}", decl.value!!.position)
|
||||
} else {
|
||||
throw FatalAstException("value dt differs from decl dt ${decl.position}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -70,11 +66,12 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
}
|
||||
|
||||
// check splitting of word arrays
|
||||
if(!decl.datatype.isWordArray && decl.splitwordarray != SplitWish.DONTCARE) {
|
||||
if(decl.splitwordarray != SplitWish.DONTCARE && !decl.datatype.isWordArray && !decl.datatype.isPointerArray) {
|
||||
if(decl.origin != VarDeclOrigin.ARRAYLITERAL)
|
||||
errors.err("@split and @nosplit are for word arrays only", decl.position)
|
||||
errors.err("@split and @nosplit are for word or pointer arrays only", decl.position)
|
||||
}
|
||||
else if(decl.datatype.isWordArray) {
|
||||
|
||||
if(decl.datatype.isWordArray) {
|
||||
var changeDataType: DataType?
|
||||
var changeSplit: SplitWish = decl.splitwordarray
|
||||
when(decl.splitwordarray) {
|
||||
@ -84,15 +81,29 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
changeSplit = SplitWish.NOSPLIT
|
||||
}
|
||||
else {
|
||||
changeDataType = if(decl.datatype.isSplitWordArray) null else DataType.arrayFor(decl.datatype.elementType().base, true)
|
||||
changeDataType = if(decl.datatype.isSplitWordArray) null else {
|
||||
val eltDt = decl.datatype.elementType()
|
||||
if(eltDt.isPointer)
|
||||
TODO("convert array of pointers to split words array type")
|
||||
else
|
||||
DataType.arrayFor(eltDt.base)
|
||||
}
|
||||
changeSplit = SplitWish.SPLIT
|
||||
}
|
||||
}
|
||||
SplitWish.SPLIT -> {
|
||||
changeDataType = if(decl.datatype.isSplitWordArray) null else DataType.arrayFor(decl.datatype.elementType().base, true)
|
||||
changeDataType = if(decl.datatype.isSplitWordArray) null else {
|
||||
val eltDt = decl.datatype.elementType()
|
||||
if(eltDt.isPointer)
|
||||
TODO("convert array of pointers to split words array type")
|
||||
else
|
||||
DataType.arrayFor(eltDt.base)
|
||||
}
|
||||
}
|
||||
SplitWish.NOSPLIT -> {
|
||||
changeDataType = if(decl.datatype.isSplitWordArray) DataType.arrayFor(decl.datatype.elementType().base, false) else null
|
||||
changeDataType = if(decl.datatype.isSplitWordArray && !decl.datatype.elementType().isPointer)
|
||||
DataType.arrayFor(decl.datatype.elementType().base, false)
|
||||
else null
|
||||
}
|
||||
}
|
||||
if(changeDataType!=null) {
|
||||
@ -132,14 +143,17 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
if(constValue!=null)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, constValue, parent))
|
||||
|
||||
if(typecast.expression is NumericLiteral) {
|
||||
val value = (typecast.expression as NumericLiteral).cast(typecast.type, typecast.implicit)
|
||||
if(value.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, value.valueOrZero(), parent))
|
||||
val number = typecast.expression as? NumericLiteral
|
||||
if(number!=null) {
|
||||
if(typecast.type.isBasic) {
|
||||
val value = number.cast(typecast.type.base, typecast.implicit)
|
||||
if (value.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, value.valueOrZero(), parent))
|
||||
}
|
||||
}
|
||||
|
||||
val sourceDt = typecast.expression.inferType(program)
|
||||
if(sourceDt issimpletype typecast.type)
|
||||
if(sourceDt istype typecast.type)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||
|
||||
if(parent is Assignment) {
|
||||
@ -150,6 +164,26 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
}
|
||||
}
|
||||
|
||||
// number cast to bool -> number!=0
|
||||
if (typecast.type.isBool) {
|
||||
val et = typecast.expression.inferType(program)
|
||||
if (et.isNumeric) {
|
||||
if(typecast.expression is NumericLiteral) {
|
||||
val boolean = NumericLiteral.fromBoolean((typecast.expression as NumericLiteral).asBooleanValue, typecast.expression.position)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, boolean, parent))
|
||||
} else {
|
||||
val zero = defaultZero(et.getOrUndef().base, typecast.position)
|
||||
val cmp = BinaryExpression(typecast.expression, "!=", zero, typecast.position)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, cmp, parent))
|
||||
}
|
||||
}
|
||||
else if (et.isPointer) {
|
||||
val ptrAsUword = TypecastExpression(typecast.expression, DataType.UWORD, true, typecast.position)
|
||||
val cmp = BinaryExpression(ptrAsUword, "!=", NumericLiteral.optimalNumeric(BaseDataType.UWORD, null, 0.0, typecast.position), typecast.position)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, cmp, parent))
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
@ -285,7 +319,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
|
||||
// replace x==1 or x==2 or x==3 with a containment check x in [1,2,3]
|
||||
val valueCopies = values.sortedBy { it.number }.map { it.copy() }
|
||||
val arrayType = DataType.arrayFor(elementType.base, true)
|
||||
val arrayType = DataType.arrayFor(elementType.base)
|
||||
val valuesArray = ArrayLiteral(InferredTypes.InferredType.known(arrayType), valueCopies.toTypedArray(), expr.position)
|
||||
val containment = ContainmentCheck(needle, valuesArray, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, containment, parent))
|
||||
@ -394,17 +428,21 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
val index = arrayIndexedExpression.indexer.constIndex()
|
||||
if(index!=null && index<0) {
|
||||
val target = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
val arraysize = target?.arraysize?.constIndex()
|
||||
if(arraysize!=null) {
|
||||
if(arraysize+index < 0) {
|
||||
errors.err("index out of bounds", arrayIndexedExpression.position)
|
||||
return noModifications
|
||||
if(arrayIndexedExpression.plainarrayvar!=null) {
|
||||
val target = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
|
||||
val arraysize = target?.arraysize?.constIndex()
|
||||
if(arraysize!=null) {
|
||||
if(arraysize+index < 0) {
|
||||
errors.err("index out of bounds", arrayIndexedExpression.position)
|
||||
return noModifications
|
||||
}
|
||||
// replace the negative index by the normal index
|
||||
val newIndex = NumericLiteral.optimalNumeric(arraysize+index, arrayIndexedExpression.indexer.position)
|
||||
arrayIndexedExpression.indexer.indexExpr = newIndex
|
||||
newIndex.linkParents(arrayIndexedExpression.indexer)
|
||||
}
|
||||
// replace the negative index by the normal index
|
||||
val newIndex = NumericLiteral.optimalNumeric(arraysize+index, arrayIndexedExpression.indexer.position)
|
||||
arrayIndexedExpression.indexer.indexExpr = newIndex
|
||||
newIndex.linkParents(arrayIndexedExpression.indexer)
|
||||
} else if(arrayIndexedExpression.pointerderef!=null) {
|
||||
TODO("cleanup pointer indexing")
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
@ -414,20 +452,46 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
val name = functionCallExpr.target.nameInSource
|
||||
if(name==listOf("msw")) {
|
||||
val valueDt = functionCallExpr.args[0].inferType(program)
|
||||
if(valueDt.isWords || valueDt.isBytes) {
|
||||
if(valueDt.isWords || valueDt.isBytes || valueDt.isPointer) {
|
||||
val zero = NumericLiteral(BaseDataType.UWORD, 0.0, functionCallExpr.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, zero, parent))
|
||||
}
|
||||
} else if(name==listOf("lsw")) {
|
||||
val valueDt = functionCallExpr.args[0].inferType(program)
|
||||
if(valueDt.isWords)
|
||||
if(valueDt.isWords || valueDt.isPointer)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
|
||||
if(valueDt.isBytes) {
|
||||
val cast = TypecastExpression(functionCallExpr.args[0], BaseDataType.UWORD, true, functionCallExpr.position)
|
||||
val cast = TypecastExpression(functionCallExpr.args[0], DataType.UWORD, true, functionCallExpr.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
}
|
||||
|
||||
if(parent is IStatementContainer) {
|
||||
val targetStruct = functionCallExpr.target.targetStructDecl()
|
||||
if (targetStruct != null) {
|
||||
// static struct instance allocation can only occur as an initializer for a pointer variable
|
||||
return listOf(IAstModification.Remove(functionCallExpr, parent as IStatementContainer))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
|
||||
if(addressOf.arrayIndex!=null) {
|
||||
val tgt = addressOf.identifier?.constValue(program)
|
||||
if (tgt != null && tgt.type.isWord) {
|
||||
// &constant[idx] --> constant + idx
|
||||
val indexExpr = addressOf.arrayIndex!!.indexExpr
|
||||
val right = if(indexExpr.inferType(program) issimpletype tgt.type)
|
||||
indexExpr
|
||||
else
|
||||
TypecastExpression(indexExpr, DataType.forDt(tgt.type), true, indexExpr.position)
|
||||
val add = BinaryExpression(tgt, "+", right, addressOf.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(addressOf, add, parent)
|
||||
)
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,8 @@ import prog8.ast.Program
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.core.*
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.code.core.*
|
||||
|
||||
internal class VerifyFunctionArgTypes(val program: Program, val options: CompilationOptions, val errors: IErrorReporter) : IAstVisitor {
|
||||
|
||||
@ -31,7 +31,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
|
||||
}
|
||||
|
||||
override fun visit(identifier: IdentifierReference) {
|
||||
if(identifier.wasStringLiteral(program)) {
|
||||
if(identifier.wasStringLiteral()) {
|
||||
allStringRefs.add(identifier.nameInSource)
|
||||
}
|
||||
}
|
||||
@ -83,11 +83,24 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
|
||||
if(argDt==paramDt)
|
||||
return true
|
||||
|
||||
// there are some exceptions that are considered compatible, such as STR <> UWORD
|
||||
if(argDt.isString && paramDt.isUnsignedWord ||
|
||||
argDt.isUnsignedWord && paramDt.isString ||
|
||||
argDt.isUnsignedWord && paramDt.isUnsignedByteArray ||
|
||||
argDt.isString && paramDt.isUnsignedByteArray)
|
||||
// there are some exceptions that are considered compatible, such as STR <> UWORD, UWORD <> pointer
|
||||
if(argDt.isUnsignedWord)
|
||||
if((paramDt.isString || paramDt.isUnsignedByteArray || paramDt.isPointer))
|
||||
return true
|
||||
if(argDt.isString) {
|
||||
if(paramDt.isUnsignedWord || paramDt.isUnsignedByteArray)
|
||||
return true
|
||||
if(paramDt.isPointer && paramDt.sub==BaseDataType.UBYTE)
|
||||
return true
|
||||
}
|
||||
|
||||
// if uword is passed, check if the parameter type is pointer to array element type
|
||||
if(argDt.isArray && paramDt.isPointer) {
|
||||
TODO("array vs element pointer check")
|
||||
}
|
||||
|
||||
// if expected is UWORD and actual is any pointer, we allow it (uword is untyped pointer, for backwards compatibility)
|
||||
if(paramDt.isUnsignedWord && argDt.isPointer)
|
||||
return true
|
||||
|
||||
return false
|
||||
@ -97,14 +110,15 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
|
||||
val argITypes = call.args.map { it.inferType(program) }
|
||||
val firstUnknownDt = argITypes.indexOfFirst { it.isUnknown }
|
||||
if(firstUnknownDt>=0) {
|
||||
// if an uword is expected but a pointer is provided, that is okay without a cast
|
||||
val identifier = call.args[0] as? IdentifierReference
|
||||
return if(identifier==null || identifier.targetStatement(program)!=null)
|
||||
return if(identifier==null || identifier.targetStatement(program.builtinFunctions)!=null)
|
||||
Pair("argument ${firstUnknownDt + 1} invalid argument type", call.args[firstUnknownDt].position)
|
||||
else
|
||||
null
|
||||
}
|
||||
val argtypes = argITypes.map { it.getOrUndef() }
|
||||
val target = call.target.targetStatement(program)
|
||||
val target = call.target.targetStatement(program.builtinFunctions)
|
||||
if (target is Subroutine) {
|
||||
val consideredParamTypes: List<DataType> = target.parameters.map { it.type }
|
||||
if(argtypes.size != consideredParamTypes.size)
|
||||
@ -113,6 +127,14 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
|
||||
if(mismatch>=0) {
|
||||
val actual = argtypes[mismatch]
|
||||
val expected = consideredParamTypes[mismatch]
|
||||
if(expected.isPointer && expected.sub!!.isWord) {
|
||||
val arg = call.args[mismatch]
|
||||
val argArray = if(arg is AddressOf) arg.identifier else arg
|
||||
if(argArray?.inferType(program)?.getOrUndef()?.isSplitWordArray==true)
|
||||
return Pair("argument ${mismatch + 1} cannot pass address to a split words array where a word pointer argument is expected, use a @nosplit word array instead", call.args[mismatch].position)
|
||||
else
|
||||
return Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
|
||||
}
|
||||
return Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
|
||||
}
|
||||
if(target.isAsmSubroutine) {
|
||||
|
@ -77,7 +77,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), false, src, outputDir, writeAssembly = false)
|
||||
val statements = result!!.compilerAst.entrypoint.statements
|
||||
statements.size shouldBe 7
|
||||
statements.size shouldBe 8
|
||||
val a1 = statements[2] as Assignment
|
||||
val a2 = statements[3] as Assignment
|
||||
val a3 = statements[4] as Assignment
|
||||
|
@ -7,6 +7,7 @@ import io.kotest.matchers.collections.shouldContain
|
||||
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
|
||||
import io.kotest.matchers.maps.shouldNotContainKey
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.statements.Block
|
||||
@ -312,4 +313,20 @@ xyz {
|
||||
val blocks2 = result2.codegenAst!!.allBlocks().toList()
|
||||
blocks2.any { it.name=="xyz" } shouldBe false
|
||||
}
|
||||
|
||||
test("symbol lookup of pointer fields should mark variable as used in callgraph") {
|
||||
val src = """
|
||||
main {
|
||||
struct List {
|
||||
^^uword s
|
||||
ubyte n
|
||||
}
|
||||
sub start() {
|
||||
^^List l1 = List()
|
||||
l1.s^^ = 2
|
||||
}
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), true, src, outputDir, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
})
|
||||
|
@ -8,7 +8,6 @@ import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.types.instanceOf
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.statements.Assignment
|
||||
@ -31,11 +30,11 @@ class TestCompilerOnCharLit: FunSpec({
|
||||
|
||||
val outputDir = tempdir().toPath()
|
||||
|
||||
fun findInitializer(vardecl: VarDecl, program: Program): Assignment? =
|
||||
fun findInitializer(vardecl: VarDecl): Assignment? =
|
||||
(vardecl.parent as IStatementContainer).statements
|
||||
.asSequence()
|
||||
.filterIsInstance<Assignment>()
|
||||
.singleOrNull { it.origin== AssignmentOrigin.VARINIT && it.target.identifier?.targetVarDecl(program) === vardecl }
|
||||
.singleOrNull { it.origin== AssignmentOrigin.VARINIT && it.target.identifier?.targetVarDecl() === vardecl }
|
||||
|
||||
|
||||
test("testCharLitAsExtsubArg") {
|
||||
@ -79,14 +78,14 @@ class TestCompilerOnCharLit: FunSpec({
|
||||
|
||||
funCall.args[0] shouldBe instanceOf<IdentifierReference>()
|
||||
val arg = funCall.args[0] as IdentifierReference
|
||||
val decl = arg.targetVarDecl(program)!!
|
||||
val decl = arg.targetVarDecl()!!
|
||||
decl.type shouldBe VarDeclType.VAR
|
||||
decl.datatype shouldBe DataType.UBYTE
|
||||
|
||||
withClue("initializer value should have been moved to separate assignment"){
|
||||
decl.value shouldBe null
|
||||
}
|
||||
val assignInitialValue = findInitializer(decl, program)!!
|
||||
val assignInitialValue = findInitializer(decl)!!
|
||||
assignInitialValue.target.identifier!!.nameInSource shouldBe listOf("ch")
|
||||
withClue("char literal should have been replaced by ubyte literal") {
|
||||
assignInitialValue.value shouldBe instanceOf<NumericLiteral>()
|
||||
@ -115,7 +114,7 @@ class TestCompilerOnCharLit: FunSpec({
|
||||
// Now, both is ok for the arg: a) still the IdRef or b) replaced by numeric literal
|
||||
when (val arg = funCall.args[0]) {
|
||||
is IdentifierReference -> {
|
||||
val decl = arg.targetVarDecl(program)!!
|
||||
val decl = arg.targetVarDecl()!!
|
||||
decl.type shouldBe VarDeclType.CONST
|
||||
decl.datatype shouldBe DataType.UBYTE
|
||||
(decl.value as NumericLiteral).number shouldBe platform.encodeString("\n", Encoding.PETSCII)[0]
|
||||
|
@ -36,6 +36,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
|
||||
warnSymbolShadowing = false,
|
||||
quietAll = true,
|
||||
quietAssembler = true,
|
||||
showTimings = false,
|
||||
asmListfile = false,
|
||||
includeSourcelines = false,
|
||||
experimentalCodegen = false,
|
||||
@ -156,6 +157,7 @@ class TestCompilerOnExamplesCx16: FunSpec({
|
||||
"interpolation",
|
||||
"kefrenbars",
|
||||
"keyboardhandler",
|
||||
"landscape",
|
||||
"life",
|
||||
"mandelbrot",
|
||||
"multi-irq-old",
|
||||
@ -239,7 +241,8 @@ class TestCompilerOnExamplesVirtual: FunSpec({
|
||||
"bouncegfx",
|
||||
"bsieve",
|
||||
"pixelshader",
|
||||
"sincos"
|
||||
"sincos",
|
||||
"pointers/sortedlist" // TODO add to "c64" later as well
|
||||
),
|
||||
listOf(false, true)
|
||||
)
|
||||
|
@ -34,7 +34,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
|
||||
val strLits = startSub.statements
|
||||
.filterIsInstance<FunctionCallStatement>()
|
||||
.map { it.args[0] as IdentifierReference }
|
||||
.map { it.targetVarDecl(program)!!.value as StringLiteral }
|
||||
.map { it.targetVarDecl()!!.value as StringLiteral }
|
||||
|
||||
strLits[0].value shouldBe "main.bar"
|
||||
strLits[1].value shouldBe "foo.bar"
|
||||
@ -57,12 +57,12 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
|
||||
.filterIsInstance<FunctionCallStatement>()
|
||||
.map { it.args[0] }
|
||||
|
||||
val str0 = (args[0] as IdentifierReference).targetVarDecl(program)!!.value as StringLiteral
|
||||
val str0 = (args[0] as IdentifierReference).targetVarDecl()!!.value as StringLiteral
|
||||
str0.value shouldBe "main.bar"
|
||||
str0.definingScope.name shouldBe "main"
|
||||
|
||||
val id1 = (args[1] as AddressOf).identifier
|
||||
val lbl1 = id1.targetStatement(program) as Label
|
||||
val id1 = (args[1] as AddressOf).identifier!!
|
||||
val lbl1 = id1.targetStatement() as Label
|
||||
lbl1.name shouldBe "foo_bar"
|
||||
lbl1.definingScope.name shouldBe "main"
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user