mirror of
https://github.com/irmen/prog8.git
synced 2025-07-24 20:24:09 +00:00
Compare commits
90 Commits
v11.3
...
languageSe
Author | SHA1 | Date | |
---|---|---|---|
|
9daa116148 | ||
|
054c98da7c | ||
|
a4a1b563aa | ||
|
7dd64b4f13 | ||
|
edc353cc24 | ||
|
0a16dcafc0 | ||
|
1b420f7fe7 | ||
|
6a9a82ff9d | ||
|
aa36e6b19f | ||
|
51cb6aad50 | ||
|
b5ce409592 | ||
|
1efdfe8ea1 | ||
|
67d4180825 | ||
|
be31e190d2 | ||
|
b5d1575823 | ||
|
e3e395836d | ||
|
8dc2e47507 | ||
|
daf7c3357c | ||
|
e8795859c5 | ||
|
bebe60b687 | ||
|
ddceec364e | ||
|
d067fa4b73 | ||
|
b5e51ab937 | ||
|
552e55c29f | ||
|
a228908c1a | ||
|
15fc3b6c04 | ||
|
0456badd02 | ||
|
399cf5118d | ||
|
a87f2640d3 | ||
|
a90ef274d7 | ||
|
341778ba67 | ||
|
ec50b5a007 | ||
|
31d84c8921 | ||
|
34bedbeef1 | ||
|
3b1b0985c1 | ||
|
368387e1a7 | ||
|
9da430ffeb | ||
|
cc063124cf | ||
|
3b37b89951 | ||
|
844b537d1e | ||
|
caf1d4a22a | ||
|
d8e244df99 | ||
|
548e421e27 | ||
|
322fa7ea69 | ||
|
cf7bea0985 | ||
|
25d7f8808f | ||
|
acc630972a | ||
|
6a33be3fd8 | ||
|
f5fc4e345c | ||
|
67231af623 | ||
|
e31ef6f06f | ||
|
09d188106a | ||
|
d8e2116481 | ||
|
435dfbb932 | ||
|
ba93966474 | ||
|
ea8d17cdb2 | ||
|
082265fb25 | ||
|
9e557ce8ac | ||
|
e5d9af75de | ||
|
31c1bf8bc5 | ||
|
37d4055036 | ||
|
78b1076110 | ||
|
0a3c748e41 | ||
|
ebf79ef9e2 | ||
|
99b9370178 | ||
|
d634061cd9 | ||
|
d59d8ff1fe | ||
|
53e442d509 | ||
|
f7cbfdff06 | ||
|
b28ee0819f | ||
|
522958e0e9 | ||
|
ccc6b56e35 | ||
|
7eb079050c | ||
|
2fdd5543b2 | ||
|
d04164c0a6 | ||
|
b047731f82 | ||
|
4d91f92a2e | ||
|
98505d27b1 | ||
|
cd63a58ad9 | ||
|
170f8dd092 | ||
|
619dcb6a84 | ||
|
99ae8ea52e | ||
|
dc031c30eb | ||
|
1e702439b7 | ||
|
8debc42381 | ||
|
532d719089 | ||
|
b40860aca4 | ||
|
2cbe6b5f7f | ||
|
d2cc7ccdfa | ||
|
2cb183c6d8 |
5
.idea/inspectionProfiles/Project_Default.xml
generated
5
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -68,6 +68,7 @@
|
||||
<inspection_tool class="BlockingMethodInNonBlockingContext" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="BooleanConstructor" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="BooleanExpressionMayBeConditional" enabled="false" level="INFORMATION" enabled_by_default="false" />
|
||||
<inspection_tool class="BooleanLiteralArgument" enabled="true" level="TEXT ATTRIBUTES" enabled_by_default="true" editorAttributes="CONSIDERATION_ATTRIBUTES" />
|
||||
<inspection_tool class="BooleanMethodIsAlwaysInverted" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="BoxingBoxedValue" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="BusyWait" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
@@ -765,7 +766,6 @@
|
||||
<inspection_tool class="MathRoundingWithIntArgument" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="MavenCoroutinesDeprecation" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="MeaninglessRecordAnnotationInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="MemberVisibilityCanBePrivate" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="MetaAnnotationWithoutRuntimeRetention" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="MethodCanBeVariableArityMethod" enabled="false" level="INFORMATION" enabled_by_default="false" />
|
||||
<inspection_tool class="MethodNameSameAsClassName" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
@@ -917,7 +917,6 @@
|
||||
<inspection_tool class="PrimitiveArrayArgumentToVariableArgMethod" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WEAK WARNING" enabled_by_default="true" editorAttributes="INFO_ATTRIBUTES" />
|
||||
<inspection_tool class="ProtectedMemberInFinalClass" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PublicApiImplicitType" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PublicField" enabled="false" level="INFORMATION" enabled_by_default="false">
|
||||
<option name="ignoreEnums" value="false" />
|
||||
<option name="ignorableAnnotations">
|
||||
@@ -995,7 +994,7 @@
|
||||
<option name="ignoreEmptySuperMethods" value="false" />
|
||||
<option name="onlyReportWhenAnnotated" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="RegExpAnonymousGroup" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="RegExpAnonymousGroup" enabled="true" level="WEAK WARNING" enabled_by_default="true" editorAttributes="INFO_ATTRIBUTES" />
|
||||
<inspection_tool class="RemoveLiteralUnderscores" enabled="false" level="INFORMATION" enabled_by_default="false" />
|
||||
<inspection_tool class="ReplaceAllDot" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="ReplaceAssignmentWithOperatorAssignment" enabled="false" level="INFORMATION" enabled_by_default="false">
|
||||
|
8
.idea/kotlinc.xml
generated
8
.idea/kotlinc.xml
generated
@@ -7,13 +7,13 @@
|
||||
<option name="jvmTarget" value="11" />
|
||||
</component>
|
||||
<component name="KotlinCommonCompilerArguments">
|
||||
<option name="apiVersion" value="2.1" />
|
||||
<option name="languageVersion" value="2.1" />
|
||||
<option name="apiVersion" value="2.2" />
|
||||
<option name="languageVersion" value="2.2" />
|
||||
</component>
|
||||
<component name="KotlinCompilerSettings">
|
||||
<option name="additionalArguments" value="-Xwhen-guards" />
|
||||
<option name="additionalArguments" value="-Xwhen-guards -jvm-default=no-compatibility" />
|
||||
</component>
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.1.10" />
|
||||
<option name="version" value="2.2.0" />
|
||||
</component>
|
||||
</project>
|
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.2.0" />
|
||||
<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.2.0/kotlin-stdlib-jdk8-2.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.0/kotlin-stdlib-jdk7-2.2.0.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.2.0/kotlin-stdlib-jdk8-2.2.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.0/kotlin-stdlib-jdk7-2.2.0-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.2.0/kotlin-stdlib-jdk8-2.2.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.0/kotlin-stdlib-jdk7-2.2.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
4
.idea/libraries/eclipse_lsp4j.xml
generated
4
.idea/libraries/eclipse_lsp4j.xml
generated
@@ -4,8 +4,8 @@
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j/0.24.0/org.eclipse.lsp4j-0.24.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j.jsonrpc/0.24.0/org.eclipse.lsp4j.jsonrpc-0.24.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.12.1/gson-2.12.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.36.0/error_prone_annotations-2.36.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.13.1/gson-2.13.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.38.0/error_prone_annotations-2.38.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
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>
|
||||
|
1
.idea/modules.xml
generated
1
.idea/modules.xml
generated
@@ -15,6 +15,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/languageServer/languageServer.iml" filepath="$PROJECT_DIR$/languageServer/languageServer.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" />
|
||||
|
@@ -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``
|
||||
@@ -95,7 +96,7 @@ What does Prog8 provide?
|
||||
- "c64": Commodore-64 (6502 like CPU)
|
||||
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
|
||||
- "pet32": Commodore PET (limited support)
|
||||
- via external configurable targets: Atari 800 XL, Neo6502, NES, C64OS, ...
|
||||
- via external configurable targets: Atari 800 XL, Neo6502, NES, C64 OS, Foenix F256, ...
|
||||
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag)
|
||||
|
||||
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode
|
||||
import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
|
||||
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "2.1.20"
|
||||
kotlin("jvm") version "2.2.0"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@@ -18,6 +19,7 @@ allprojects {
|
||||
compilerOptions {
|
||||
freeCompilerArgs = listOf("-Xwhen-guards")
|
||||
jvmTarget = JvmTarget.JVM_11
|
||||
jvmDefault = JvmDefaultMode.NO_COMPATIBILITY
|
||||
}
|
||||
sourceSets.all {
|
||||
languageSettings {
|
||||
|
@@ -27,7 +27,7 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||
var golden: GoldenRam
|
||||
val libraryPath: Path?
|
||||
val customLauncher: List<String>
|
||||
val additionalAssemblerOptions: String?
|
||||
val additionalAssemblerOptions: List<String>
|
||||
val defaultOutputType: OutputType
|
||||
|
||||
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
|
||||
|
@@ -14,5 +14,5 @@ interface IErrorReporter {
|
||||
|
||||
fun noErrorForLine(position: Position): Boolean
|
||||
|
||||
fun print_single_error(errormessage: String)
|
||||
fun printSingleError(errormessage: String)
|
||||
}
|
||||
|
@@ -6,12 +6,15 @@ 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
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
|
@@ -7,12 +7,15 @@ 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
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
|
@@ -27,7 +27,7 @@ class ConfigFileTarget(
|
||||
override val defaultOutputType: OutputType,
|
||||
override val libraryPath: Path,
|
||||
override val customLauncher: List<String>,
|
||||
override val additionalAssemblerOptions: String?,
|
||||
override val additionalAssemblerOptions: List<String>,
|
||||
val ioAddresses: List<UIntRange>,
|
||||
val zpScratchB1: UInt,
|
||||
val zpScratchReg: UInt,
|
||||
@@ -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 {
|
||||
|
||||
@@ -109,8 +109,6 @@ class ConfigFileTarget(
|
||||
(customLauncherStr+"\n").lines().map { it.trimEnd() }
|
||||
else emptyList()
|
||||
val assemblerOptionsStr = props.getProperty("assembler_options", "").trim()
|
||||
val assemblerOptions = assemblerOptionsStr.ifBlank { null }
|
||||
|
||||
val outputTypeString = props.getProperty("output_type", "PRG")
|
||||
val defaultOutputType = OutputType.valueOf(outputTypeString.uppercase())
|
||||
|
||||
@@ -128,7 +126,7 @@ class ConfigFileTarget(
|
||||
defaultOutputType,
|
||||
libraryPath,
|
||||
customLauncher,
|
||||
assemblerOptions,
|
||||
if(assemblerOptionsStr=="") emptyList() else assemblerOptionsStr.split(" "),
|
||||
ioAddresses,
|
||||
props.getInteger("zp_scratch_b1"),
|
||||
props.getInteger("zp_scratch_reg"),
|
||||
|
@@ -6,12 +6,15 @@ 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
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
|
@@ -25,7 +25,7 @@ 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.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
|
@@ -6,12 +6,15 @@ 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
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
|
@@ -7,12 +7,15 @@ 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
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
@@ -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,
|
||||
|
@@ -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
|
||||
}
|
||||
@@ -432,23 +432,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
|
||||
@@ -656,16 +639,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 +657,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 +734,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 +838,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, isTargetCpu(CpuType.CPU65C02), 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 +858,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, false, stmt)
|
||||
out("""
|
||||
cmp #0
|
||||
beq +
|
||||
@@ -897,13 +881,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 +899,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 +914,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 +927,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 +957,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 +993,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 +1045,25 @@ $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) {
|
||||
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(arrayIdx.variable), true, true, false)
|
||||
} else {
|
||||
// print a message when more optimal code is possible for 65C02 cpu
|
||||
val variable = symbolTable.lookup(arrayIdx.variable.name)!!
|
||||
if(variable is StStaticVariable && variable.length!!<=128)
|
||||
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)
|
||||
@@ -1279,7 +1253,7 @@ $repeatLabel""")
|
||||
}
|
||||
|
||||
if(addressExpr.operator=="+") {
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr, false)
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr)
|
||||
if (ptrAndIndex == null) return false
|
||||
|
||||
if(write) {
|
||||
@@ -1549,6 +1523,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 +1588,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")
|
||||
@@ -1628,7 +1647,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))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -28,6 +28,14 @@ internal class AssemblyProgram(
|
||||
|
||||
val assemblerCommand: List<String>
|
||||
|
||||
fun addRemainingOptions(command: MutableList<String>, program: Path, assembly: Path): List<String> {
|
||||
if(options.compTarget.additionalAssemblerOptions.isNotEmpty())
|
||||
command.addAll(options.compTarget.additionalAssemblerOptions)
|
||||
|
||||
command.addAll(listOf("--output", program.toString(), assembly.toString()))
|
||||
return command
|
||||
}
|
||||
|
||||
when(options.output) {
|
||||
OutputType.PRG -> {
|
||||
// CBM machines .prg generation.
|
||||
@@ -47,8 +55,7 @@ internal class AssemblyProgram(
|
||||
command.add("--list=$listFile")
|
||||
}
|
||||
|
||||
command.addAll(listOf("--output", prgFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
assemblerCommand = addRemainingOptions(command, prgFile, assemblyFile)
|
||||
if(!options.quiet)
|
||||
println("\nCreating prg for target ${compTarget.name}.")
|
||||
}
|
||||
@@ -69,8 +76,7 @@ internal class AssemblyProgram(
|
||||
if(options.asmListfile)
|
||||
command.add("--list=$listFile")
|
||||
|
||||
command.addAll(listOf("--output", xexFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
assemblerCommand = addRemainingOptions(command,xexFile, assemblyFile)
|
||||
if(!options.quiet)
|
||||
println("\nCreating xex for target ${compTarget.name}.")
|
||||
}
|
||||
@@ -90,8 +96,7 @@ internal class AssemblyProgram(
|
||||
if(options.asmListfile)
|
||||
command.add("--list=$listFile")
|
||||
|
||||
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
assemblerCommand = addRemainingOptions(command, binFile, assemblyFile)
|
||||
if(!options.quiet)
|
||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
||||
}
|
||||
@@ -122,14 +127,10 @@ internal class AssemblyProgram(
|
||||
command.add("--nostart") // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD
|
||||
}
|
||||
|
||||
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
assemblerCommand = addRemainingOptions(command, binFile, assemblyFile)
|
||||
}
|
||||
}
|
||||
|
||||
if(options.compTarget.additionalAssemblerOptions!=null)
|
||||
assemblerCommand.add(options.compTarget.additionalAssemblerOptions!!)
|
||||
|
||||
val proc = ProcessBuilder(assemblerCommand)
|
||||
if(!options.quiet)
|
||||
proc.inheritIO()
|
||||
|
@@ -53,7 +53,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)
|
||||
@@ -676,7 +676,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 +739,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 +815,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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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) {
|
||||
@@ -508,7 +508,7 @@ $endLabel""")
|
||||
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,7 +539,10 @@ $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
|
||||
@@ -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
|
||||
@@ -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<=127) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
iny
|
||||
cpy #$length
|
||||
cpy #${numElements*2}
|
||||
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>=16) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
|
@@ -234,9 +234,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 {
|
||||
|
@@ -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 {
|
||||
@@ -829,7 +930,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 +959,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 {
|
||||
@@ -908,7 +1010,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 +1039,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 +1076,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 +1105,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 {
|
||||
@@ -1053,7 +1157,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 +1186,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 {
|
||||
@@ -1196,7 +1301,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 +1327,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 +1354,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 +1380,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 +1409,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 +1439,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 +1469,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 +1499,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 +1532,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 +1562,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 +1591,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 +1621,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 {
|
||||
|
@@ -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")
|
||||
}
|
||||
@@ -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,7 +487,7 @@ 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
|
||||
@@ -636,15 +615,30 @@ 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 == 0 }
|
||||
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()) {
|
||||
|
@@ -53,7 +53,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 +61,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 +76,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 +84,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 +92,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")
|
||||
|
@@ -438,6 +438,8 @@ internal class AssignmentAsmGen(
|
||||
private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) {
|
||||
when(val value = assign.source.expression!!) {
|
||||
is PtAddressOf -> {
|
||||
val source = asmgen.symbolTable.lookup(value.identifier.name)
|
||||
require(source !is StConstant) { "addressOf of a constant should have been rewritten to a simple addition expression" }
|
||||
val arrayDt = value.identifier.type
|
||||
val sourceName =
|
||||
if(value.isMsbForSplitArray)
|
||||
@@ -971,7 +973,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 +1188,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
|
||||
}
|
||||
|
||||
@@ -1313,7 +1314,7 @@ internal class AssignmentAsmGen(
|
||||
// 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)
|
||||
asmgen.assignExpressionToRegister(leftArrayIndexer.index, RegisterOrPair.Y)
|
||||
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pla")
|
||||
val arrayvarname = asmgen.asmSymbolName(leftArrayIndexer.variable)
|
||||
asmgen.out(" clc | adc $arrayvarname,y")
|
||||
@@ -1322,7 +1323,7 @@ internal class AssignmentAsmGen(
|
||||
// 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)
|
||||
asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y)
|
||||
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pla")
|
||||
val arrayvarname = asmgen.asmSymbolName(rightArrayIndexer.variable)
|
||||
if (expr.operator == "+")
|
||||
@@ -1567,8 +1568,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")
|
||||
@@ -1739,6 +1740,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 +1758,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")
|
||||
@@ -2471,8 +2491,9 @@ $endLabel""")
|
||||
BaseDataType.UBYTE -> {
|
||||
when(targetDt) {
|
||||
BaseDataType.BOOL -> {
|
||||
val compare = if(regs==RegisterOrPair.A) "cmp" else "cp${regs.toString().lowercase()}"
|
||||
asmgen.out("""
|
||||
cp${regs.toString().lowercase()} #0
|
||||
$compare #0
|
||||
beq +
|
||||
ld${regs.toString().lowercase()} #1
|
||||
+ st${regs.toString().lowercase()} $targetAsmVarName""")
|
||||
@@ -2607,52 +2628,100 @@ $endLabel""")
|
||||
|
||||
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 +2924,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 +2956,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 +2993,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 +3466,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 +3781,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 +3871,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 +3905,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
|
||||
|
@@ -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)
|
||||
@@ -356,7 +356,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)
|
||||
@@ -439,7 +439,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)
|
||||
@@ -457,7 +457,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
|
||||
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}
|
||||
@@ -583,7 +583,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 +635,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 +910,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 +1019,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)
|
||||
}
|
||||
"<<" -> {
|
||||
@@ -1190,7 +1190,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 +1363,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 +1534,7 @@ $shortcutLabel:""")
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
ldy #$value
|
||||
jsr prog8_math.divmod_ub_asm
|
||||
jsr prog8_math.remainder_ub_asm
|
||||
sta $name""")
|
||||
}
|
||||
"<<" -> {
|
||||
@@ -1921,18 +1921,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
|
||||
@@ -2740,7 +2738,7 @@ $shortcutLabel:""")
|
||||
"+" -> {
|
||||
// name += byteexpression
|
||||
if(valueDt.isUnsignedByte) {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc $name
|
||||
|
@@ -68,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") }
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -68,22 +68,22 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
|
||||
private fun assignCpuRegister(returns: StExtSubParameter, regNum: Int, target: PtAssignTarget): IRCodeChunks {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val loadCpuRegInstr = when(returns.register.registerOrPair) {
|
||||
RegisterOrPair.A -> IRInstruction(Opcode.LOADHA, IRDataType.BYTE, reg1=regNum)
|
||||
RegisterOrPair.X -> IRInstruction(Opcode.LOADHX, IRDataType.BYTE, reg1=regNum)
|
||||
RegisterOrPair.Y -> IRInstruction(Opcode.LOADHY, IRDataType.BYTE, reg1=regNum)
|
||||
RegisterOrPair.AX -> IRInstruction(Opcode.LOADHAX, IRDataType.WORD, reg1=regNum)
|
||||
RegisterOrPair.AY -> IRInstruction(Opcode.LOADHAY, IRDataType.WORD, reg1=regNum)
|
||||
RegisterOrPair.XY -> IRInstruction(Opcode.LOADHXY, IRDataType.WORD, reg1=regNum)
|
||||
in Cx16VirtualRegisters -> IRInstruction(Opcode.LOADM, irType(returns.type), reg1=regNum, labelSymbol = "cx16.${returns.register.registerOrPair.toString().lowercase()}")
|
||||
RegisterOrPair.FAC1 -> IRInstruction(Opcode.LOADHFACZERO, IRDataType.FLOAT, fpReg1 = regNum)
|
||||
RegisterOrPair.FAC2 -> IRInstruction(Opcode.LOADHFACONE, IRDataType.FLOAT, fpReg1 = regNum)
|
||||
null -> {
|
||||
TODO("assign CPU status flag ${returns.register.statusflag!!}")
|
||||
}
|
||||
else -> throw AssemblyError("cannot load register")
|
||||
when(returns.register.registerOrPair) {
|
||||
RegisterOrPair.A -> addInstr(result, IRInstruction(Opcode.LOADHA, IRDataType.BYTE, reg1=regNum), null)
|
||||
RegisterOrPair.X -> addInstr(result, IRInstruction(Opcode.LOADHX, IRDataType.BYTE, reg1=regNum), null)
|
||||
RegisterOrPair.Y -> addInstr(result, IRInstruction(Opcode.LOADHY, IRDataType.BYTE, reg1=regNum), null)
|
||||
RegisterOrPair.AX -> addInstr(result, IRInstruction(Opcode.LOADHAX, IRDataType.WORD, reg1=regNum), null)
|
||||
RegisterOrPair.AY -> addInstr(result, IRInstruction(Opcode.LOADHAY, IRDataType.WORD, reg1=regNum), null)
|
||||
RegisterOrPair.XY -> addInstr(result, IRInstruction(Opcode.LOADHXY, IRDataType.WORD, reg1=regNum), null)
|
||||
in Cx16VirtualRegisters -> addInstr(result, IRInstruction(Opcode.LOADM, irType(returns.type), reg1=regNum, labelSymbol = "cx16.${returns.register.registerOrPair.toString().lowercase()}"), null)
|
||||
RegisterOrPair.FAC1 -> addInstr(result, IRInstruction(Opcode.LOADHFACZERO, IRDataType.FLOAT, fpReg1 = regNum), null)
|
||||
RegisterOrPair.FAC2 -> addInstr(result, IRInstruction(Opcode.LOADHFACONE, IRDataType.FLOAT, fpReg1 = regNum), null)
|
||||
null -> if(returns.register.statusflag!=null)
|
||||
result += assignCpuStatusFlagReturnvalue(returns.register.statusflag!!, regNum)
|
||||
else
|
||||
throw AssemblyError("weird CPU register")
|
||||
else -> throw AssemblyError("weird CPU register")
|
||||
}
|
||||
addInstr(result, loadCpuRegInstr, null)
|
||||
|
||||
// build an assignment to store the value in the actual target.
|
||||
val assign = PtAssignment(target.position)
|
||||
@@ -93,6 +93,30 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return result
|
||||
}
|
||||
|
||||
private fun assignCpuStatusFlagReturnvalue(statusflag: Statusflag, regNum: Int): IRCodeChunks {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
when(statusflag) {
|
||||
Statusflag.Pc -> {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=regNum, immediate = 0)
|
||||
it += IRInstruction(Opcode.ROXL, IRDataType.BYTE, reg1=regNum)
|
||||
}
|
||||
}
|
||||
Statusflag.Pz -> TODO("find a way to assign cpu Z status bit to reg $regNum but it can already be clobbered by other return values")
|
||||
Statusflag.Pn -> TODO("find a way to assign cpu Z status bit to reg $regNum but it can already be clobbered by other return values")
|
||||
Statusflag.Pv -> {
|
||||
val skipLabel = codeGen.createLabelName()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=regNum, immediate = 0)
|
||||
it += IRInstruction(Opcode.BSTVC, labelSymbol = skipLabel)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=regNum, immediate = 1)
|
||||
}
|
||||
result += IRCodeChunk(skipLabel, null)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
internal fun translate(augAssign: PtAugmentedAssign): IRCodeChunks {
|
||||
// augmented assignment always has just a single target
|
||||
if (augAssign.target.children.single() is PtIrRegister)
|
||||
@@ -110,7 +134,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val chunks = when (augAssign.operator) {
|
||||
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"-=" -> operatorMinusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"*=" -> operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"*=" -> operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"/=" -> operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"|=" -> operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"or=" -> operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
@@ -207,7 +231,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val tr = expressionEval.translateExpression(array.index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
if(!array.splitWords && eltSize>1)
|
||||
result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
|
||||
result += codeGen.multiplyByConst(DataType.UBYTE, tr.resultReg, eltSize)
|
||||
return tr.resultReg
|
||||
}
|
||||
|
||||
@@ -769,7 +793,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return result
|
||||
}
|
||||
|
||||
private fun operatorMultiplyInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
private fun operatorMultiplyInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
@@ -783,7 +807,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val valueReg=codeGen.registers.next(eltDt)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, eltDt, reg1=valueReg, immediate = constValue)
|
||||
it += IRInstruction(Opcode.MULM, eltDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
val opcode = if(signed) Opcode.MULSM else Opcode.MULM
|
||||
it += IRInstruction(opcode, eltDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -803,22 +828,23 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress)
|
||||
IRInstruction(Opcode.MULSM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.MULSM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
} else {
|
||||
if(constFactorRight!=null && !constFactorRight.type.isFloat) {
|
||||
val factor = constFactorRight.number.toInt()
|
||||
result += codeGen.multiplyByConstInplace(vmDt, constAddress, symbol, factor)
|
||||
result += codeGen.multiplyByConstInplace(vmDt, signed, constAddress, symbol, factor)
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val opcode = if(signed) Opcode.MULSM else Opcode.MULM
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
IRInstruction(opcode, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
IRInstruction(opcode, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package prog8.codegen.intermediate
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.DataType
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
@@ -78,7 +79,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 +91,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 +142,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 +238,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 +276,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])
|
||||
@@ -666,7 +663,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
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)
|
||||
@@ -687,7 +684,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
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)
|
||||
|
@@ -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.*
|
||||
|
||||
|
||||
@@ -184,12 +178,19 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
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)
|
||||
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
|
||||
if(expr.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(expr.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 = symbol)
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
|
||||
}
|
||||
} else {
|
||||
@@ -289,7 +290,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 +300,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 +313,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 +326,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)
|
||||
@@ -392,7 +389,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(arrayIx.index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
if(eltSize>1)
|
||||
result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
|
||||
result += codeGen.multiplyByConst(DataType.UBYTE, tr.resultReg, eltSize)
|
||||
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)
|
||||
@@ -565,8 +562,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)
|
||||
@@ -618,7 +615,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)) {
|
||||
@@ -670,7 +666,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 +704,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)
|
||||
@@ -1039,7 +1046,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 +1061,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 +1221,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,7 +1237,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, if(signed)
|
||||
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)
|
||||
@@ -1241,13 +1249,13 @@ 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)
|
||||
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())
|
||||
@@ -1258,7 +1266,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
addInstr(result, if (signed)
|
||||
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)
|
||||
@@ -1269,7 +1277,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
|
||||
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 +1300,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 +1308,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)
|
||||
}
|
||||
}
|
||||
|
@@ -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()
|
||||
@@ -64,15 +65,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.scopedName
|
||||
} 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
|
||||
}
|
||||
@@ -194,10 +197,7 @@ class IRCodeGen(
|
||||
old.fpReg1,
|
||||
old.fpReg2,
|
||||
immediate = immediateValue,
|
||||
null,
|
||||
address = addressValue,
|
||||
null,
|
||||
null
|
||||
address = addressValue
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -256,8 +256,8 @@ 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}")
|
||||
else -> TODO("missing codegen for $node")
|
||||
}
|
||||
@@ -420,35 +420,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)
|
||||
}
|
||||
|
||||
@@ -469,10 +478,11 @@ class IRCodeGen(
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
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 +490,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)
|
||||
@@ -493,8 +503,7 @@ class IRCodeGen(
|
||||
}
|
||||
iterable.type.isSplitWordArray -> {
|
||||
// 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 +513,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 {
|
||||
@@ -771,7 +780,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 +798,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 +861,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 +1082,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)
|
||||
@@ -1954,7 +1966,7 @@ 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")
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -48,6 +48,36 @@ class IRUnusedCodeRemover(
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@@ -52,7 +52,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)
|
||||
@@ -76,7 +77,8 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
fixupAddressOfInArray(variable.initializationArrayValue),
|
||||
variable.length,
|
||||
variable.zpwish,
|
||||
variable.align
|
||||
variable.align,
|
||||
variable.dirty
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -66,7 +66,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)
|
||||
|
@@ -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,7 +332,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
|
||||
val constIndex = arrayIndexedExpression.indexer.constIndex()
|
||||
if (constIndex != null) {
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl()
|
||||
if(arrayVar!=null) {
|
||||
val array =arrayVar.value as? ArrayLiteral
|
||||
if(array!=null) {
|
||||
@@ -387,7 +387,7 @@ 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)
|
||||
|
@@ -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)
|
||||
)
|
||||
}
|
||||
@@ -560,7 +560,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
}
|
||||
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)
|
||||
|
@@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()) {
|
||||
|
@@ -33,8 +33,8 @@ class StatementOptimizer(private val program: Program,
|
||||
} 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 +82,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 +105,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 +145,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)
|
||||
val scope = AnonymousScope.empty(forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, 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,7 +160,7 @@ 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)
|
||||
val scope = AnonymousScope.empty(forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
@@ -173,7 +172,7 @@ 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),
|
||||
AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
@@ -466,7 +465,7 @@ class StatementOptimizer(private val program: Program,
|
||||
return IfElse(
|
||||
compare,
|
||||
AnonymousScope(mutableListOf(assign), position),
|
||||
AnonymousScope(mutableListOf(), position),
|
||||
AnonymousScope.empty(),
|
||||
position
|
||||
)
|
||||
}
|
||||
@@ -501,6 +500,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)
|
||||
}
|
||||
|
@@ -317,10 +317,25 @@ c128 {
|
||||
&ubyte VM4 = $0A2F ; starting page for VDC attribute mem
|
||||
|
||||
|
||||
; TODO c128 a bunch of kernal routines are missing here that are specific to the c128
|
||||
|
||||
extsub $FF6E = JSRFAR()
|
||||
extsub $FF68 = SETBNK(ubyte databank @A, ubyte filenamebank @X)
|
||||
extsub $FF47 = SPIN_SPOUT() clobbers(A) ; set up serial bus for fast communications mode
|
||||
extsub $FF4A = CLOSE_ALL(ubyte device @X) clobbers(X) ; close all channels to specific device
|
||||
extsub $FF4D = C64_MODE() ; restart machine in C64 mode (does not return)
|
||||
extsub $FF50 = DMA_CALL(ubyte bank @X, ubyte command @Y) clobbers(A,X) ; send a command to a DMA device
|
||||
extsub $FF53 = BOOT_CALL(ubyte device @X, ubyte drive @A) clobbers(A,X,Y) ; try to autoboot the given disk
|
||||
extsub $FF56 = PHOENIX() clobbers(A,X,Y) ; search for and autostart ROMs, cartridges, then default disk
|
||||
extsub $FF59 = LKUPLA(ubyte lfn @A) -> bool @Pc, ubyte @X ; look up logical file number to see if it's open; returns device
|
||||
extsub $FF5C = LKUPSA(ubyte sa @Y) -> bool @Pc, ubyte @A, ubyte @X ; look up secondary address to see if it's in use; returns lfn and device
|
||||
extsub $FF5F = SWAPPER() clobbers(A,X,Y) ; swap active screen (between 40- and 80-column)
|
||||
extsub $FF62 = DLCHR() clobbers(A,X,Y) ; copy character ROM into VDC video RAM
|
||||
extsub $FF65 = PFKEY(ubyte zpaddr @A, ubyte key @X, ubyte length @Y) ; redefine programmable function key (string descriptor in zp, addr in A)
|
||||
extsub $FF68 = SETBNK(ubyte data_bank @A, ubyte filename_bank @X) ; set memory bank for load/save
|
||||
extsub $FF6B = GETCFG(ubyte bank @X) -> ubyte @A ; translate bank number to MMU configuration register value
|
||||
extsub $FF6E = JSRFAR() clobbers(A,X) ; call routine in another bank (parameters set in zero page addresses 2-8)
|
||||
extsub $FF71 = JMPFAR() clobbers(A,X) ; jump without return to another bank (parameters set as for JSRFAR)
|
||||
extsub $FF74 = INDFET(ubyte zpaddr @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> ubyte @A ; fetch byte from another bank (address in zp, ptr in A)
|
||||
extsub $FF77 = INDSTA(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) ; store byte to another bank (address in zp, ptr in $02b9)
|
||||
extsub $FF7A = INDCMP(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> bool @Pz, bool @Pc, bool @Pv; compare byte in another bank (address in zp, ptr in $02c8)
|
||||
extsub $FF7D = PRIMM() ; print immediate string
|
||||
|
||||
|
||||
; ---- C128 specific system utility routines: ----
|
||||
@@ -419,12 +434,13 @@ 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_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@@ -946,6 +962,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
|
||||
@@ -984,6 +1012,7 @@ cx16 {
|
||||
&uword r14 = $1bfc
|
||||
&uword r15 = $1bfe
|
||||
|
||||
; signed word versions
|
||||
&word r0s = $1be0
|
||||
&word r1s = $1be2
|
||||
&word r2s = $1be4
|
||||
@@ -1001,6 +1030,7 @@ cx16 {
|
||||
&word r14s = $1bfc
|
||||
&word r15s = $1bfe
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $1be0
|
||||
&ubyte r1L = $1be2
|
||||
&ubyte r2L = $1be4
|
||||
@@ -1035,6 +1065,7 @@ cx16 {
|
||||
&ubyte r14H = $1bfd
|
||||
&ubyte r15H = $1bff
|
||||
|
||||
; signed byte versions (low and high bytes)
|
||||
&byte r0sL = $1be0
|
||||
&byte r1sL = $1be2
|
||||
&byte r2sL = $1be4
|
||||
@@ -1069,6 +1100,42 @@ cx16 {
|
||||
&byte r14sH = $1bfd
|
||||
&byte r15sH = $1bff
|
||||
|
||||
; boolean versions (low and high bytes)
|
||||
&bool r0bL = $1be0
|
||||
&bool r1bL = $1be2
|
||||
&bool r2bL = $1be4
|
||||
&bool r3bL = $1be6
|
||||
&bool r4bL = $1be8
|
||||
&bool r5bL = $1bea
|
||||
&bool r6bL = $1bec
|
||||
&bool r7bL = $1bee
|
||||
&bool r8bL = $1bf0
|
||||
&bool r9bL = $1bf2
|
||||
&bool r10bL = $1bf4
|
||||
&bool r11bL = $1bf6
|
||||
&bool r12bL = $1bf8
|
||||
&bool r13bL = $1bfa
|
||||
&bool r14bL = $1bfc
|
||||
&bool r15bL = $1bfe
|
||||
|
||||
&bool r0bH = $1be1
|
||||
&bool r1bH = $1be3
|
||||
&bool r2bH = $1be5
|
||||
&bool r3bH = $1be7
|
||||
&bool r4bH = $1be9
|
||||
&bool r5bH = $1beb
|
||||
&bool r6bH = $1bed
|
||||
&bool r7bH = $1bef
|
||||
&bool r8bH = $1bf1
|
||||
&bool r9bH = $1bf3
|
||||
&bool r10bH = $1bf5
|
||||
&bool r11bH = $1bf7
|
||||
&bool r12bH = $1bf9
|
||||
&bool r13bH = $1bfb
|
||||
&bool r14bH = $1bfd
|
||||
&bool r15bH = $1bff
|
||||
|
||||
|
||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
%asm {{
|
||||
ldy #31
|
||||
|
@@ -439,12 +439,13 @@ 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_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@@ -968,6 +969,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
|
||||
@@ -1006,6 +1019,7 @@ cx16 {
|
||||
&uword r14 = $cffc
|
||||
&uword r15 = $cffe
|
||||
|
||||
; signed word versions
|
||||
&word r0s = $cfe0
|
||||
&word r1s = $cfe2
|
||||
&word r2s = $cfe4
|
||||
@@ -1023,6 +1037,7 @@ cx16 {
|
||||
&word r14s = $cffc
|
||||
&word r15s = $cffe
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $cfe0
|
||||
&ubyte r1L = $cfe2
|
||||
&ubyte r2L = $cfe4
|
||||
@@ -1057,6 +1072,7 @@ cx16 {
|
||||
&ubyte r14H = $cffd
|
||||
&ubyte r15H = $cfff
|
||||
|
||||
; signed byte versions (low and high bytes)
|
||||
&byte r0sL = $cfe0
|
||||
&byte r1sL = $cfe2
|
||||
&byte r2sL = $cfe4
|
||||
@@ -1091,6 +1107,42 @@ cx16 {
|
||||
&byte r14sH = $cffd
|
||||
&byte r15sH = $cfff
|
||||
|
||||
; boolean versions
|
||||
&bool r0bL = $cfe0
|
||||
&bool r1bL = $cfe2
|
||||
&bool r2bL = $cfe4
|
||||
&bool r3bL = $cfe6
|
||||
&bool r4bL = $cfe8
|
||||
&bool r5bL = $cfea
|
||||
&bool r6bL = $cfec
|
||||
&bool r7bL = $cfee
|
||||
&bool r8bL = $cff0
|
||||
&bool r9bL = $cff2
|
||||
&bool r10bL = $cff4
|
||||
&bool r11bL = $cff6
|
||||
&bool r12bL = $cff8
|
||||
&bool r13bL = $cffa
|
||||
&bool r14bL = $cffc
|
||||
&bool r15bL = $cffe
|
||||
|
||||
&bool r0bH = $cfe1
|
||||
&bool r1bH = $cfe3
|
||||
&bool r2bH = $cfe5
|
||||
&bool r3bH = $cfe7
|
||||
&bool r4bH = $cfe9
|
||||
&bool r5bH = $cfeb
|
||||
&bool r6bH = $cfed
|
||||
&bool r7bH = $cfef
|
||||
&bool r8bH = $cff1
|
||||
&bool r9bH = $cff3
|
||||
&bool r10bH = $cff5
|
||||
&bool r11bH = $cff7
|
||||
&bool r12bH = $cff9
|
||||
&bool r13bH = $cffb
|
||||
&bool r14bH = $cffd
|
||||
&bool r15bH = $cfff
|
||||
|
||||
|
||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
%asm {{
|
||||
ldy #31
|
||||
|
@@ -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
|
||||
|
@@ -177,6 +177,7 @@ cx16 {
|
||||
&uword r14 = $001e
|
||||
&uword r15 = $0020
|
||||
|
||||
; signed word versions
|
||||
&word r0s = $0002
|
||||
&word r1s = $0004
|
||||
&word r2s = $0006
|
||||
@@ -194,6 +195,7 @@ cx16 {
|
||||
&word r14s = $001e
|
||||
&word r15s = $0020
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $0002
|
||||
&ubyte r1L = $0004
|
||||
&ubyte r2L = $0006
|
||||
@@ -228,6 +230,7 @@ cx16 {
|
||||
&ubyte r14H = $001f
|
||||
&ubyte r15H = $0021
|
||||
|
||||
; signed byte versions (low and high bytes)
|
||||
&byte r0sL = $0002
|
||||
&byte r1sL = $0004
|
||||
&byte r2sL = $0006
|
||||
@@ -262,6 +265,42 @@ cx16 {
|
||||
&byte r14sH = $001f
|
||||
&byte r15sH = $0021
|
||||
|
||||
; boolean versions
|
||||
&bool r0bL = $0002
|
||||
&bool r1bL = $0004
|
||||
&bool r2bL = $0006
|
||||
&bool r3bL = $0008
|
||||
&bool r4bL = $000a
|
||||
&bool r5bL = $000c
|
||||
&bool r6bL = $000e
|
||||
&bool r7bL = $0010
|
||||
&bool r8bL = $0012
|
||||
&bool r9bL = $0014
|
||||
&bool r10bL = $0016
|
||||
&bool r11bL = $0018
|
||||
&bool r12bL = $001a
|
||||
&bool r13bL = $001c
|
||||
&bool r14bL = $001e
|
||||
&bool r15bL = $0020
|
||||
|
||||
&bool r0bH = $0003
|
||||
&bool r1bH = $0005
|
||||
&bool r2bH = $0007
|
||||
&bool r3bH = $0009
|
||||
&bool r4bH = $000b
|
||||
&bool r5bH = $000d
|
||||
&bool r6bH = $000f
|
||||
&bool r7bH = $0011
|
||||
&bool r8bH = $0013
|
||||
&bool r9bH = $0015
|
||||
&bool r10bH = $0017
|
||||
&bool r11bH = $0019
|
||||
&bool r12bH = $001b
|
||||
&bool r13bH = $001d
|
||||
&bool r14bH = $001f
|
||||
&bool r15bH = $0021
|
||||
|
||||
|
||||
; VERA registers
|
||||
|
||||
const uword VERA_BASE = $9F20
|
||||
@@ -474,7 +513,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 +1207,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 +1286,7 @@ _vsync_vec .word ?
|
||||
_line_vec .word ?
|
||||
_aflow_vec .word ?
|
||||
_sprcol_vec .word ?
|
||||
_continue_with_system_handler .byte ?
|
||||
.send BSS
|
||||
|
||||
_irq_dispatcher
|
||||
@@ -1224,44 +1295,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 +1554,13 @@ 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_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@@ -1997,6 +2067,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
|
||||
|
@@ -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,13 @@ 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_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@@ -481,6 +482,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
|
||||
@@ -519,6 +532,7 @@ cx16 {
|
||||
&uword r14 = $7ffc
|
||||
&uword r15 = $7ffe
|
||||
|
||||
; signed word versions
|
||||
&word r0s = $7fe0
|
||||
&word r1s = $7fe2
|
||||
&word r2s = $7fe4
|
||||
@@ -536,6 +550,7 @@ cx16 {
|
||||
&word r14s = $7ffc
|
||||
&word r15s = $7ffe
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $7fe0
|
||||
&ubyte r1L = $7fe2
|
||||
&ubyte r2L = $7fe4
|
||||
@@ -570,6 +585,7 @@ cx16 {
|
||||
&ubyte r14H = $7ffd
|
||||
&ubyte r15H = $7fff
|
||||
|
||||
; signed byte versions (low and high bytes)
|
||||
&byte r0sL = $7fe0
|
||||
&byte r1sL = $7fe2
|
||||
&byte r2sL = $7fe4
|
||||
@@ -604,6 +620,42 @@ cx16 {
|
||||
&byte r14sH = $7ffd
|
||||
&byte r15sH = $7fff
|
||||
|
||||
; boolean versions
|
||||
&bool r0bL = $7fe0
|
||||
&bool r1bL = $7fe2
|
||||
&bool r2bL = $7fe4
|
||||
&bool r3bL = $7fe6
|
||||
&bool r4bL = $7fe8
|
||||
&bool r5bL = $7fea
|
||||
&bool r6bL = $7fec
|
||||
&bool r7bL = $7fee
|
||||
&bool r8bL = $7ff0
|
||||
&bool r9bL = $7ff2
|
||||
&bool r10bL = $7ff4
|
||||
&bool r11bL = $7ff6
|
||||
&bool r12bL = $7ff8
|
||||
&bool r13bL = $7ffa
|
||||
&bool r14bL = $7ffc
|
||||
&bool r15bL = $7ffe
|
||||
|
||||
&bool r0bH = $7fe1
|
||||
&bool r1bH = $7fe3
|
||||
&bool r2bH = $7fe5
|
||||
&bool r3bH = $7fe7
|
||||
&bool r4bH = $7fe9
|
||||
&bool r5bH = $7feb
|
||||
&bool r6bH = $7fed
|
||||
&bool r7bH = $7fef
|
||||
&bool r8bH = $7ff1
|
||||
&bool r9bH = $7ff3
|
||||
&bool r10bH = $7ff5
|
||||
&bool r11bH = $7ff7
|
||||
&bool r12bH = $7ff9
|
||||
&bool r13bH = $7ffb
|
||||
&bool r14bH = $7ffd
|
||||
&bool r15bH = $7fff
|
||||
|
||||
|
||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
%asm {{
|
||||
ldy #31
|
||||
|
@@ -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,13 @@ 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_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@@ -198,6 +199,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 +238,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
|
||||
}
|
||||
@@ -260,6 +266,7 @@ cx16 {
|
||||
&uword r14 = $ff1e
|
||||
&uword r15 = $ff20
|
||||
|
||||
; signed word versions
|
||||
&word r0s = $ff02
|
||||
&word r1s = $ff04
|
||||
&word r2s = $ff06
|
||||
@@ -277,6 +284,7 @@ cx16 {
|
||||
&word r14s = $ff1e
|
||||
&word r15s = $ff20
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $ff02
|
||||
&ubyte r1L = $ff04
|
||||
&ubyte r2L = $ff06
|
||||
@@ -311,6 +319,7 @@ cx16 {
|
||||
&ubyte r14H = $ff1f
|
||||
&ubyte r15H = $ff21
|
||||
|
||||
; signed byte versions (low and high bytes)
|
||||
&byte r0sL = $ff02
|
||||
&byte r1sL = $ff04
|
||||
&byte r2sL = $ff06
|
||||
@@ -345,6 +354,42 @@ cx16 {
|
||||
&byte r14sH = $ff1f
|
||||
&byte r15sH = $ff21
|
||||
|
||||
; boolean versions
|
||||
&bool r0bL = $ff02
|
||||
&bool r1bL = $ff04
|
||||
&bool r2bL = $ff06
|
||||
&bool r3bL = $ff08
|
||||
&bool r4bL = $ff0a
|
||||
&bool r5bL = $ff0c
|
||||
&bool r6bL = $ff0e
|
||||
&bool r7bL = $ff10
|
||||
&bool r8bL = $ff12
|
||||
&bool r9bL = $ff14
|
||||
&bool r10bL = $ff16
|
||||
&bool r11bL = $ff18
|
||||
&bool r12bL = $ff1a
|
||||
&bool r13bL = $ff1c
|
||||
&bool r14bL = $ff1e
|
||||
&bool r15bL = $ff20
|
||||
|
||||
&bool r0bH = $ff03
|
||||
&bool r1bH = $ff05
|
||||
&bool r2bH = $ff07
|
||||
&bool r3bH = $ff09
|
||||
&bool r4bH = $ff0b
|
||||
&bool r5bH = $ff0d
|
||||
&bool r6bH = $ff0f
|
||||
&bool r7bH = $ff11
|
||||
&bool r8bH = $ff13
|
||||
&bool r9bH = $ff15
|
||||
&bool r10bH = $ff17
|
||||
&bool r11bH = $ff19
|
||||
&bool r12bH = $ff1b
|
||||
&bool r13bH = $ff1d
|
||||
&bool r14bH = $ff1f
|
||||
&bool r15bH = $ff21
|
||||
|
||||
|
||||
sub save_virtual_registers() {
|
||||
uword[32] storage
|
||||
storage[0] = r0
|
||||
|
@@ -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
|
||||
@@ -115,16 +115,30 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
||||
else -> NumericLiteral(BaseDataType.UBYTE, program.memsizer.memorySize(dt.getOrUndef(), null).toDouble(), position)
|
||||
}
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
throw SyntaxError("sizeof invalid argument type", 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 +148,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)
|
||||
args.errors.report()
|
||||
|
||||
if(compilationOptions.optimize) {
|
||||
optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable, args.errors)
|
||||
postprocessSimplifiedAst(intermediateAst, symbolTable, args.errors)
|
||||
args.errors.report()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -424,7 +470,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 +532,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 +541,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) {
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,9 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C128Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.PETTarget
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.compiler.builtinFunctionReturnType
|
||||
import java.io.CharConversionException
|
||||
@@ -182,21 +184,19 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
val iterableDt = forLoop.iterable.inferType(program).getOrUndef()
|
||||
|
||||
if(iterableDt.isNumeric) TODO("iterable type should not be simple numeric!? "+forLoop.position)
|
||||
|
||||
if(forLoop.iterable is IFunctionCall) {
|
||||
errors.err("can not loop over function call return value", forLoop.position)
|
||||
} else if(!(iterableDt.isIterable) && forLoop.iterable !is RangeExpression) {
|
||||
errors.err("can only loop over an iterable type", forLoop.position)
|
||||
} else {
|
||||
val loopvar = forLoop.loopVar.targetVarDecl(program)
|
||||
val loopvar = forLoop.loopVar.targetVarDecl()
|
||||
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
|
||||
errors.err("for loop requires a variable to loop with", forLoop.position)
|
||||
} else {
|
||||
require(loopvar.datatype.isNumericOrBool)
|
||||
when (loopvar.datatype.base) {
|
||||
BaseDataType.UBYTE -> {
|
||||
if(!iterableDt.isUnsignedByte && !iterableDt.isUnsignedByteArray && !iterableDt.isString) // TODO remove ubyte check?
|
||||
if(!iterableDt.isUnsignedByte && !iterableDt.isUnsignedByteArray && !iterableDt.isString)
|
||||
errors.err("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position)
|
||||
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression)
|
||||
}
|
||||
@@ -205,7 +205,7 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("bool loop variable can only loop over boolean array", forLoop.position)
|
||||
}
|
||||
BaseDataType.UWORD -> {
|
||||
if(!iterableDt.isUnsignedByte && !iterableDt.isUnsignedWord && !iterableDt.isString && // TODO remove byte and word check?
|
||||
if(!iterableDt.isUnsignedByte && !iterableDt.isUnsignedWord && !iterableDt.isString &&
|
||||
!iterableDt.isUnsignedByteArray && !iterableDt.isUnsignedWordArray &&
|
||||
!iterableDt.isSplitWordArray)
|
||||
errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position)
|
||||
@@ -213,7 +213,7 @@ internal class AstChecker(private val program: Program,
|
||||
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression)
|
||||
}
|
||||
BaseDataType.BYTE -> {
|
||||
if(!iterableDt.isSignedByte && !iterableDt.isSignedByteArray) // TODO remove byte check?
|
||||
if(!iterableDt.isSignedByte && !iterableDt.isSignedByteArray)
|
||||
errors.err("byte loop variable can only loop over bytes", forLoop.position)
|
||||
}
|
||||
BaseDataType.WORD -> {
|
||||
@@ -392,7 +392,7 @@ internal class AstChecker(private val program: Program,
|
||||
err("variable bank extsub has no romable code-generation for the required jsrfar call, stick to constant bank, or create a system-ram trampoline")
|
||||
}
|
||||
|
||||
if(varbank.targetVarDecl(program)?.datatype?.isUnsignedByte!=true)
|
||||
if(varbank.targetVarDecl()?.datatype?.isUnsignedByte!=true)
|
||||
err("bank variable must be ubyte")
|
||||
}
|
||||
if(subroutine.inline && subroutine.asmAddress!=null)
|
||||
@@ -406,11 +406,42 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
// subroutine must contain at least one 'return' or 'goto'
|
||||
// (or if it has an asm block, that must contain a 'rts' or 'jmp' or 'bra')
|
||||
var haveReturnError = false
|
||||
if(!hasReturnOrExternalJumpOrRts(subroutine)) {
|
||||
if (subroutine.returntypes.isNotEmpty()) {
|
||||
// for asm subroutines with an address, no statement check is possible.
|
||||
if (subroutine.asmAddress == null && !subroutine.inline)
|
||||
if (subroutine.asmAddress == null && !subroutine.inline) {
|
||||
err("non-inline subroutine has result value(s) and thus must have at least one 'return' or external 'goto' in it (or the assembler equivalent in case of %asm)")
|
||||
haveReturnError = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val lastStatement = subroutine.statements.reversed().dropWhile { it is Subroutine || it is VarDecl || it is Directive || (it is Assignment && it.origin== AssignmentOrigin.VARINIT) }.firstOrNull()
|
||||
if(!haveReturnError && !subroutine.isAsmSubroutine && !subroutine.inline && subroutine.returntypes.isNotEmpty() && lastStatement !is Return) {
|
||||
if(lastStatement==null)
|
||||
err("subroutine '${subroutine.name}' has result value(s) but doesn't end with a return statement")
|
||||
else {
|
||||
val returnError = when (lastStatement) {
|
||||
is Jump, is OnGoto -> false
|
||||
is IStatementContainer -> !hasReturnOrExternalJumpOrRts(lastStatement as IStatementContainer)
|
||||
is InlineAssembly -> !lastStatement.hasReturnOrRts()
|
||||
is ForLoop -> hasReturnOrExternalJumpOrRts(lastStatement.body)
|
||||
is IfElse -> !hasReturnOrExternalJumpOrRts(lastStatement.truepart) && !hasReturnOrExternalJumpOrRts(lastStatement.elsepart)
|
||||
is ConditionalBranch -> !hasReturnOrExternalJumpOrRts(lastStatement.truepart) && !hasReturnOrExternalJumpOrRts(lastStatement.elsepart)
|
||||
is RepeatLoop -> {
|
||||
lastStatement.iterations!=null || !hasReturnOrExternalJumpOrRts(lastStatement.body)
|
||||
}
|
||||
is UntilLoop -> !hasReturnOrExternalJumpOrRts(lastStatement.body)
|
||||
is WhileLoop -> !hasReturnOrExternalJumpOrRts(lastStatement.body)
|
||||
is When -> lastStatement.choices.all { !hasReturnOrExternalJumpOrRts(it.statements) }
|
||||
else -> true
|
||||
}
|
||||
|
||||
if(returnError) {
|
||||
val pos = if(lastStatement is Subroutine) subroutine.position else lastStatement.position
|
||||
errors.err("subroutine '${subroutine.name}' has result value(s) but doesn't end with a return statement", pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,7 +589,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
val ident = repeatLoop.iterations as? IdentifierReference
|
||||
if(ident!=null) {
|
||||
val targetVar = ident.targetVarDecl(program)
|
||||
val targetVar = ident.targetVarDecl()
|
||||
if(targetVar==null)
|
||||
errors.err("invalid assignment value, maybe forgot '&' (address-of)", ident.position)
|
||||
}
|
||||
@@ -598,7 +629,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
val fcall = assignment.value as? IFunctionCall
|
||||
val fcallTarget = fcall?.target?.targetSubroutine(program)
|
||||
val fcallTarget = fcall?.target?.targetSubroutine()
|
||||
if(assignment.target.multi!=null) {
|
||||
checkMultiAssignment(assignment, fcall, fcallTarget)
|
||||
} else if(fcallTarget!=null) {
|
||||
@@ -681,8 +712,7 @@ internal class AstChecker(private val program: Program,
|
||||
if (assignment.value !is BinaryExpression && assignment.value !is PrefixExpression && assignment.value !is ContainmentCheck && assignment.value !is IfExpression)
|
||||
errors.err("invalid assignment value, maybe forgot '&' (address-of)", assignment.value.position)
|
||||
} else {
|
||||
checkAssignmentCompatible(assignTarget, targetDatatype.getOrUndef(),
|
||||
sourceDatatype.getOrUndef(), assignment.value)
|
||||
checkAssignmentCompatible(targetDatatype.getOrUndef(),sourceDatatype.getOrUndef(), assignment.value, assignment.value.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -691,7 +721,7 @@ internal class AstChecker(private val program: Program,
|
||||
fun checkRomTarget(target: AssignTarget) {
|
||||
val idx=target.arrayindexed
|
||||
if(idx!=null) {
|
||||
val decl = idx.arrayvar.targetVarDecl(program)!!
|
||||
val decl = idx.arrayvar.targetVarDecl()!!
|
||||
if(decl.type!=VarDeclType.MEMORY && decl.zeropage!=ZeropageWish.REQUIRE_ZEROPAGE) {
|
||||
// memory mapped arrays are assumed to be in RAM. If they're not.... well, POOF
|
||||
errors.err("cannot assign to an array or string that is located in ROM (option romable is enabled)", assignTarget.position)
|
||||
@@ -709,7 +739,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
override fun visit(addressOf: AddressOf) {
|
||||
checkLongType(addressOf)
|
||||
val variable=addressOf.identifier.targetVarDecl(program)
|
||||
val variable=addressOf.identifier.targetVarDecl()
|
||||
if (variable!=null) {
|
||||
if (variable.type == VarDeclType.CONST && addressOf.arrayIndex == null)
|
||||
errors.err("invalid pointer-of operand type", addressOf.position)
|
||||
@@ -764,6 +794,9 @@ internal class AstChecker(private val program: Program,
|
||||
// FLOATS enabled?
|
||||
if(!compilerOptions.floats && (decl.datatype.isFloat || decl.datatype.isFloatArray) && decl.type != VarDeclType.MEMORY)
|
||||
err("floating point used, but that is not enabled via options")
|
||||
else if(compilerOptions.compTarget.name in arrayOf(PETTarget.NAME, C128Target.NAME) && decl.type != VarDeclType.CONST && (decl.datatype.isFloat || decl.datatype.isFloatArray)) {
|
||||
err("pet32 and c128 target do not support floating point numbers yet")
|
||||
}
|
||||
|
||||
// ARRAY without size specifier MUST have an iterable initializer value
|
||||
if(decl.isArray && decl.arraysize==null) {
|
||||
@@ -1124,14 +1157,6 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("array literal for iteration must contain constants. Try using a separate array variable instead?", array.position)
|
||||
}
|
||||
|
||||
if(array.parent is Assignment) {
|
||||
val arraydt = array.inferType(program)
|
||||
val assignTarget = (array.parent as Assignment).target
|
||||
val targetDt = assignTarget.inferType(program)
|
||||
if(arraydt!=targetDt)
|
||||
errors.err("value has incompatible type ($arraydt) for the variable ($targetDt)", array.position)
|
||||
}
|
||||
|
||||
super.visit(array)
|
||||
}
|
||||
|
||||
@@ -1162,7 +1187,7 @@ internal class AstChecker(private val program: Program,
|
||||
override fun visit(expr: PrefixExpression) {
|
||||
|
||||
if(expr.expression is IFunctionCall) {
|
||||
val targetStatement = (expr.expression as IFunctionCall).target.targetSubroutine(program)
|
||||
val targetStatement = (expr.expression as IFunctionCall).target.targetSubroutine()
|
||||
if(targetStatement?.returntypes?.isEmpty()==true) {
|
||||
errors.err("subroutine doesn't return a value", expr.expression.position)
|
||||
}
|
||||
@@ -1610,7 +1635,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
private fun checkPointer(pointervar: IdentifierReference) {
|
||||
val vardecl = pointervar.targetVarDecl(program)
|
||||
val vardecl = pointervar.targetVarDecl()
|
||||
if(vardecl?.zeropage == ZeropageWish.NOT_IN_ZEROPAGE)
|
||||
errors.info("pointer variable should preferrably be in zeropage but is marked nozp", vardecl.position)
|
||||
}
|
||||
@@ -1623,7 +1648,7 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("indexing requires an iterable or address uword variable", arrayIndexedExpression.position)
|
||||
val indexVariable = arrayIndexedExpression.indexer.indexExpr as? IdentifierReference
|
||||
if(indexVariable!=null) {
|
||||
if(indexVariable.targetVarDecl(program)?.datatype?.isSigned==true) {
|
||||
if(indexVariable.targetVarDecl()?.datatype?.isSigned==true) {
|
||||
errors.err("variable array indexing can't be performed with signed variables", indexVariable.position)
|
||||
return
|
||||
}
|
||||
@@ -1678,6 +1703,9 @@ internal class AstChecker(private val program: Program,
|
||||
if(whenStmt.condition.constValue(program)!=null)
|
||||
errors.warn("when-value is a constant and will always result in the same choice", whenStmt.condition.position)
|
||||
|
||||
if(whenStmt.betterAsOnGoto(program, compilerOptions))
|
||||
errors.info("when statement can be replaced with on..goto", whenStmt.position)
|
||||
|
||||
super.visit(whenStmt)
|
||||
}
|
||||
|
||||
@@ -2015,25 +2043,23 @@ internal class AstChecker(private val program: Program,
|
||||
return correct
|
||||
}
|
||||
|
||||
private fun checkAssignmentCompatible(target: AssignTarget,
|
||||
targetDatatype: DataType,
|
||||
private fun checkAssignmentCompatible(targetDatatype: DataType,
|
||||
sourceDatatype: DataType,
|
||||
sourceValue: Expression) : Boolean {
|
||||
val position = sourceValue.position
|
||||
sourceValue: Expression,
|
||||
position: Position) : Boolean {
|
||||
|
||||
if (targetDatatype.isArray) {
|
||||
if(sourceValue.inferType(program).isArray)
|
||||
errors.err("cannot assign arrays directly. Maybe use sys.memcopy instead.", target.position)
|
||||
if(sourceDatatype.isArray)
|
||||
errors.err("cannot assign arrays directly. Maybe use sys.memcopy instead.", position)
|
||||
else
|
||||
errors.err("cannot assign value to array. Maybe use sys.memset/memsetw instead.", target.position)
|
||||
errors.err("cannot assign value to array. Maybe use sys.memset/memsetw instead.", position)
|
||||
return false
|
||||
}
|
||||
if (sourceValue is ArrayLiteral) {
|
||||
errors.err("cannot assign array", target.position)
|
||||
return false
|
||||
}
|
||||
if(sourceValue is RangeExpression) {
|
||||
errors.err("can't assign a range value to something else", position)
|
||||
if (sourceDatatype.isArray) {
|
||||
if(targetDatatype.isUnsignedWord)
|
||||
errors.err("invalid assignment value, maybe forgot '&' (address-of)", position)
|
||||
else
|
||||
errors.err("cannot assign array", position) // also includes range expressions
|
||||
return false
|
||||
}
|
||||
if(sourceDatatype.isUndefined) {
|
||||
@@ -2056,41 +2082,30 @@ internal class AstChecker(private val program: Program,
|
||||
if(result)
|
||||
return true
|
||||
|
||||
if(sourceDatatype.isWord && targetDatatype.isByte) {
|
||||
errors.err("cannot assign word to byte, use msb() or lsb()?", position)
|
||||
}
|
||||
else if(sourceDatatype.isIterable && targetDatatype.isByte) {
|
||||
errors.err("cannot assign string or array to a byte", position)
|
||||
}
|
||||
val sourceIsBitwiseOperatorExpression = (sourceValue as? BinaryExpression)?.operator in BitwiseOperators
|
||||
if(sourceDatatype.isWord && targetDatatype.isByte)
|
||||
errors.err("cannot assign word to byte, maybe use msb() or lsb()", position)
|
||||
else if(sourceDatatype.isFloat&& targetDatatype.isInteger)
|
||||
errors.err("cannot assign float to ${targetDatatype}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
|
||||
else if((sourceValue as? BinaryExpression)?.operator in BitwiseOperators && targetDatatype.equalsSize(sourceDatatype)) {
|
||||
// this is allowed: bitwise operation between different types as long as they're the same size.
|
||||
}
|
||||
else if(targetDatatype.isUnsignedWord && sourceDatatype.isPassByRef) {
|
||||
// this is allowed: a pass-by-reference datatype into an uword (pointer value).
|
||||
}
|
||||
else if(sourceDatatype.isArray && targetDatatype.isArray) {
|
||||
// this is allowed (assigning array to array)
|
||||
else if(sourceIsBitwiseOperatorExpression && targetDatatype.equalsSize(sourceDatatype)) {
|
||||
// this is allowed: bitwise operation between different types as long as they're the same size.
|
||||
}
|
||||
else if(sourceDatatype.isBool && !targetDatatype.isBool) {
|
||||
else if(targetDatatype.isString && sourceDatatype.isUnsignedWord)
|
||||
errors.err("can't assign uword to str. If the source is a string pointer and you actually want to overwrite the target string, use an explicit strings.copy(src,tgt) instead.", position)
|
||||
else
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
else if(targetDatatype.isBool && !sourceDatatype.isBool) {
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
else if(targetDatatype.isString) {
|
||||
if(sourceDatatype.isUnsignedWord)
|
||||
errors.err("can't assign UWORD to STR. If the source is a string and you actually want to overwrite the target string, use an explicit strings.copy(src,tgt) instead.", position)
|
||||
else
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
else {
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun visit(onGoto: OnGoto) {
|
||||
if(!onGoto.index.inferType(program).getOrUndef().isUnsignedByte) {
|
||||
errors.err("on..goto index must be an unsigned byte", onGoto.index.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, errors: IErrorReporter) {
|
||||
|
@@ -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)
|
||||
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
@@ -144,7 +139,7 @@ class AstPreprocessor(val program: Program,
|
||||
}
|
||||
} else {
|
||||
// handle declaration of a single variable
|
||||
if(decl.value!=null && decl.datatype.isNumericOrBool) {
|
||||
if(decl.value!=null && !decl.datatype.isIterable) {
|
||||
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, null, false, decl.position)
|
||||
val assign = Assignment(target, decl.value!!, AssignmentOrigin.VARINIT, decl.position)
|
||||
replacements.add(IAstModification.ReplaceNode(decl, assign, scope))
|
||||
@@ -181,7 +176,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
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -36,7 +36,7 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
||||
if(typecast.type == BaseDataType.UWORD) {
|
||||
val identifier = typecast.expression as? IdentifierReference
|
||||
if(identifier!=null) {
|
||||
return if(identifier.isSubroutineParameter(program)) {
|
||||
return if(identifier.isSubroutineParameter()) {
|
||||
listOf(
|
||||
IAstModification.ReplaceNode(
|
||||
typecast,
|
||||
|
@@ -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,6 +24,7 @@ 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
|
||||
|
||||
override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
|
||||
return listOf(IAstModification.Remove(alias, parent as IStatementContainer))
|
||||
@@ -106,7 +104,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))
|
||||
@@ -150,7 +148,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),
|
||||
@@ -205,7 +203,7 @@ _after:
|
||||
// replace pointervar[word] by @(pointervar+word) to avoid the
|
||||
// "array indexing is limited to byte size 0..255" error for pointervariables.
|
||||
val indexExpr = arrayIndexedExpression.indexer.indexExpr
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl()
|
||||
if(arrayVar!=null && arrayVar.datatype.isUnsignedWord) {
|
||||
val wordIndex = TypecastExpression(indexExpr, BaseDataType.UWORD, true, indexExpr.position)
|
||||
val address = BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", wordIndex, arrayIndexedExpression.position)
|
||||
@@ -279,7 +277,7 @@ _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)
|
||||
return listOf(IAstModification.ReplaceNode(memread, msb, parent))
|
||||
@@ -350,4 +348,60 @@ _after:
|
||||
|
||||
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, conditionVar.position)
|
||||
assignIndex = Assignment(varTarget, ongoto.index, AssignmentOrigin.USERCODE, ongoto.position)
|
||||
}
|
||||
|
||||
val callTarget = ArrayIndexedExpression(IdentifierReference(listOf(jumplistArray.name), jumplistArray.position), 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))
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ 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() {
|
||||
@@ -83,7 +83,7 @@ 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>()
|
||||
|
@@ -76,6 +76,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
is VarDecl -> 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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +165,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) {
|
||||
@@ -417,7 +421,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 +430,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 +440,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)
|
||||
}
|
||||
@@ -575,6 +579,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
|
||||
@@ -619,7 +624,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
}
|
||||
|
||||
private fun transform(srcArr: ArrayIndexedExpression): PtArrayIndexer {
|
||||
val dt = srcArr.arrayvar.targetVarDecl(program)!!.datatype
|
||||
val dt = srcArr.arrayvar.targetVarDecl()!!.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") }
|
||||
@@ -768,7 +773,6 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
return cast
|
||||
}
|
||||
|
||||
|
||||
private fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
|
||||
return if (SourceCode.isLibraryResource(filename)) {
|
||||
return com.github.michaelbull.result.runCatching {
|
||||
|
@@ -46,6 +46,7 @@ private fun setDeferMasks(program: PtProgram, errors: IErrorReporter): Map<PtSub
|
||||
DataType.UBYTE,
|
||||
ZeropageWish.NOT_IN_ZEROPAGE,
|
||||
0u,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
sub.position
|
||||
|
@@ -52,7 +52,8 @@ internal class StatementReorderer(
|
||||
if (decl.datatype.isNumericOrBool) {
|
||||
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,11 +63,9 @@ 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),
|
||||
decl.zeroElementValue(), AssignmentOrigin.VARINIT, decl.position)
|
||||
@@ -96,7 +95,7 @@ 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)
|
||||
@@ -115,6 +114,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 +127,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 +147,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 +329,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 +344,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 {
|
||||
|
@@ -185,6 +185,16 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
@@ -251,7 +261,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
if(number!=null) {
|
||||
addTypecastOrCastedValueModification(modifications, it.second, targetDt, call as Node)
|
||||
} else if(identifier!=null && targetDt==BaseDataType.UWORD && argDt.isPassByRef) {
|
||||
if(!identifier.isSubroutineParameter(program)) {
|
||||
if(!identifier.isSubroutineParameter()) {
|
||||
// We allow STR/ARRAY values for UWORD parameters.
|
||||
// If it's an array (not STR), take the address.
|
||||
if(!argDt.isString) {
|
||||
|
@@ -84,12 +84,12 @@ 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 DataType.arrayFor(decl.datatype.elementType().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 DataType.arrayFor(decl.datatype.elementType().base)
|
||||
}
|
||||
SplitWish.NOSPLIT -> {
|
||||
changeDataType = if(decl.datatype.isSplitWordArray) DataType.arrayFor(decl.datatype.elementType().base, false) else null
|
||||
@@ -285,7 +285,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,7 +394,7 @@ 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 target = arrayIndexedExpression.arrayvar.targetVarDecl()
|
||||
val arraysize = target?.arraysize?.constIndex()
|
||||
if(arraysize!=null) {
|
||||
if(arraysize+index < 0) {
|
||||
@@ -429,5 +429,24 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
}
|
||||
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, 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)
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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",
|
||||
|
@@ -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,7 +57,7 @@ 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"
|
||||
|
||||
|
@@ -28,6 +28,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
|
||||
warnSymbolShadowing = false,
|
||||
quietAll = true,
|
||||
quietAssembler = true,
|
||||
showTimings = false,
|
||||
asmListfile = false,
|
||||
includeSourcelines = false,
|
||||
experimentalCodegen = false,
|
||||
|
@@ -21,6 +21,8 @@ class TestLaunchEmu: FunSpec({
|
||||
|
||||
<VARIABLESNOINIT>
|
||||
</VARIABLESNOINIT>
|
||||
<VARIABLESNOINITDIRTY>
|
||||
</VARIABLESNOINITDIRTY>
|
||||
<VARIABLESWITHINIT>
|
||||
</VARIABLESWITHINIT>
|
||||
<CONSTANTS>
|
||||
|
@@ -262,12 +262,13 @@ class TestMemory: FunSpec({
|
||||
shouldThrow<IllegalArgumentException> {
|
||||
target.memorySize(BaseDataType.UNDEFINED)
|
||||
}
|
||||
shouldThrow<IllegalArgumentException> {
|
||||
target.memorySize(BaseDataType.LONG)
|
||||
shouldThrow<NoSuchElementException> {
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.LONG), 10)
|
||||
}
|
||||
target.memorySize(BaseDataType.BOOL) shouldBe 1
|
||||
target.memorySize(BaseDataType.BYTE) shouldBe 1
|
||||
target.memorySize(BaseDataType.WORD) shouldBe 2
|
||||
target.memorySize(BaseDataType.LONG) shouldBe 4
|
||||
target.memorySize(BaseDataType.FLOAT) shouldBe target.FLOAT_MEM_SIZE
|
||||
|
||||
target.memorySize(DataType.BOOL, null) shouldBe 1
|
||||
@@ -283,13 +284,14 @@ class TestMemory: FunSpec({
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.BOOL), 10) shouldBe 10
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.BYTE), 10) shouldBe 10
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.WORD), 10) shouldBe 20
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.WORD), 10) shouldBe 20
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.UWORD), 10) shouldBe 20
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.FLOAT), 10) shouldBe 10*target.FLOAT_MEM_SIZE
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.WORD, true), 10) shouldBe 20
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.UWORD, true), 10) shouldBe 20
|
||||
|
||||
target.memorySize(DataType.BOOL, 10) shouldBe 10
|
||||
target.memorySize(DataType.UWORD, 10) shouldBe 20
|
||||
target.memorySize(DataType.LONG, 10) shouldBe 40
|
||||
target.memorySize(DataType.FLOAT, 10) shouldBe 10*target.FLOAT_MEM_SIZE
|
||||
}
|
||||
}
|
||||
|
@@ -37,16 +37,16 @@ class TestNumericLiteral: FunSpec({
|
||||
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos), NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos)) shouldBe true
|
||||
}
|
||||
|
||||
test("test truncating") {
|
||||
test("test truncating avoidance") {
|
||||
shouldThrow<ExpressionError> {
|
||||
NumericLiteral(BaseDataType.BYTE, -2.345, dummyPos)
|
||||
}.message shouldContain "refused truncating"
|
||||
}.message shouldContain "float value given for integer"
|
||||
shouldThrow<ExpressionError> {
|
||||
NumericLiteral(BaseDataType.BYTE, -2.6, dummyPos)
|
||||
}.message shouldContain "refused truncating"
|
||||
}.message shouldContain "float value given for integer"
|
||||
shouldThrow<ExpressionError> {
|
||||
NumericLiteral(BaseDataType.UWORD, 2222.345, dummyPos)
|
||||
}.message shouldContain "refused truncating"
|
||||
}.message shouldContain "float value given for integer"
|
||||
NumericLiteral(BaseDataType.UBYTE, 2.0, dummyPos).number shouldBe 2.0
|
||||
NumericLiteral(BaseDataType.BYTE, -2.0, dummyPos).number shouldBe -2.0
|
||||
NumericLiteral(BaseDataType.UWORD, 2222.0, dummyPos).number shouldBe 2222.0
|
||||
@@ -213,7 +213,7 @@ class TestNumericLiteral: FunSpec({
|
||||
test("cast can change value") {
|
||||
fun num(dt: BaseDataType, num: Double): NumericLiteral {
|
||||
val n = NumericLiteral(dt, num, Position.DUMMY)
|
||||
n.linkParents(AnonymousScope(mutableListOf(), Position.DUMMY))
|
||||
n.linkParents(AnonymousScope.empty())
|
||||
return n
|
||||
}
|
||||
val cast1 = num(BaseDataType.UBYTE, 200.0).cast(BaseDataType.BYTE, false)
|
||||
@@ -233,7 +233,7 @@ class TestNumericLiteral: FunSpec({
|
||||
test("convert cannot change value") {
|
||||
fun num(dt: BaseDataType, num: Double): NumericLiteral {
|
||||
val n = NumericLiteral(dt, num, Position.DUMMY)
|
||||
n.linkParents(AnonymousScope(mutableListOf(), Position.DUMMY))
|
||||
n.linkParents(AnonymousScope.empty())
|
||||
return n
|
||||
}
|
||||
num(BaseDataType.UBYTE, 200.0).convertTypeKeepValue(BaseDataType.BYTE).isValid shouldBe false
|
||||
|
@@ -22,7 +22,9 @@ import prog8.code.core.Position
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.vm.VmRunner
|
||||
import prog8tests.helpers.*
|
||||
import kotlin.io.path.readText
|
||||
|
||||
|
||||
class TestOptimization: FunSpec({
|
||||
@@ -225,7 +227,7 @@ other {
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target(), optimize=false, src, outputDir, writeAssembly = false)!!
|
||||
val assignFF = result.compilerAst.entrypoint.statements.last() as Assignment
|
||||
val assignFF = result.compilerAst.entrypoint.statements.dropLast(1).last() as Assignment
|
||||
assignFF.isAugmentable shouldBe true
|
||||
assignFF.target.identifier!!.nameInSource shouldBe listOf("ff")
|
||||
val value = assignFF.value as BinaryExpression
|
||||
@@ -252,7 +254,7 @@ other {
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
result.compilerAst.entrypoint.statements.size shouldBe 7
|
||||
result.compilerAst.entrypoint.statements.size shouldBe 8
|
||||
val alldecls = result.compilerAst.entrypoint.allDefinedSymbols.toList()
|
||||
alldecls.map { it.first } shouldBe listOf("unused_but_shared", "usedvar_only_written", "usedvar")
|
||||
}
|
||||
@@ -276,12 +278,12 @@ other {
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
result.compilerAst.entrypoint.statements.size shouldBe 3
|
||||
result.compilerAst.entrypoint.statements.size shouldBe 4
|
||||
val ifstmt = result.compilerAst.entrypoint.statements[0] as IfElse
|
||||
ifstmt.truepart.statements.size shouldBe 1
|
||||
(ifstmt.truepart.statements[0] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "r0")
|
||||
val func2 = result.compilerAst.entrypoint.statements[2] as Subroutine
|
||||
func2.statements.size shouldBe 2
|
||||
val func2 = result.compilerAst.entrypoint.statements.last() as Subroutine
|
||||
func2.statements.size shouldBe 3
|
||||
(func2.statements[0] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "r0")
|
||||
}
|
||||
|
||||
@@ -312,7 +314,7 @@ main {
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
result.compilerAst.entrypoint.statements.size shouldBe 0
|
||||
result.compilerAst.entrypoint.statements.size shouldBe 1
|
||||
result.compilerAst.entrypoint.definingScope.statements.size shouldBe 1
|
||||
}
|
||||
|
||||
@@ -350,7 +352,7 @@ main {
|
||||
z6 = z1 - 5
|
||||
*/
|
||||
val statements = result.compilerAst.entrypoint.statements
|
||||
statements.size shouldBe 12
|
||||
statements.size shouldBe 13
|
||||
val z1decl = statements[0] as VarDecl
|
||||
val z1init = statements[1] as Assignment
|
||||
val z2decl = statements[2] as VarDecl
|
||||
@@ -395,8 +397,8 @@ main {
|
||||
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 5
|
||||
val assign=stmts.last() as Assignment
|
||||
stmts.size shouldBe 6
|
||||
val assign=stmts[4] as Assignment
|
||||
(assign.target.memoryAddress?.addressExpression as IdentifierReference).nameInSource shouldBe listOf("aa")
|
||||
}
|
||||
|
||||
@@ -412,8 +414,8 @@ main {
|
||||
"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 5
|
||||
val assign=stmts.last() as Assignment
|
||||
stmts.size shouldBe 6
|
||||
val assign=stmts[4] as Assignment
|
||||
(assign.target.memoryAddress?.addressExpression as IdentifierReference).nameInSource shouldBe listOf("aa")
|
||||
}
|
||||
|
||||
@@ -431,7 +433,7 @@ main {
|
||||
"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 10
|
||||
stmts.size shouldBe 11
|
||||
stmts.filterIsInstance<VarDecl>().size shouldBe 5
|
||||
stmts.filterIsInstance<Assignment>().size shouldBe 5
|
||||
}
|
||||
@@ -508,7 +510,7 @@ main {
|
||||
xx += 6
|
||||
*/
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 6
|
||||
stmts.size shouldBe 7
|
||||
stmts.filterIsInstance<VarDecl>().size shouldBe 3
|
||||
stmts.filterIsInstance<Assignment>().size shouldBe 3
|
||||
}
|
||||
@@ -537,13 +539,13 @@ main {
|
||||
xx += 10
|
||||
*/
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 7
|
||||
stmts.size shouldBe 8
|
||||
stmts.filterIsInstance<VarDecl>().size shouldBe 2
|
||||
stmts.filterIsInstance<Assignment>().size shouldBe 5
|
||||
val assignXX1 = stmts[1] as Assignment
|
||||
assignXX1.target.identifier!!.nameInSource shouldBe listOf("xx")
|
||||
assignXX1.value shouldBe NumericLiteral(BaseDataType.UWORD, 20.0, Position.DUMMY)
|
||||
val assignXX2 = stmts.last() as Assignment
|
||||
val assignXX2 = stmts[6] as Assignment
|
||||
assignXX2.target.identifier!!.nameInSource shouldBe listOf("xx")
|
||||
val xxValue = assignXX2.value as BinaryExpression
|
||||
xxValue.operator shouldBe "+"
|
||||
@@ -577,7 +579,7 @@ main {
|
||||
thingy++
|
||||
*/
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 6
|
||||
stmts.size shouldBe 7
|
||||
val ifStmt = stmts[5] as IfElse
|
||||
val containment = ifStmt.condition as ContainmentCheck
|
||||
(containment.element as IdentifierReference).nameInSource shouldBe listOf("source")
|
||||
@@ -612,7 +614,7 @@ main {
|
||||
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 5
|
||||
stmts.size shouldBe 6
|
||||
val ifStmt = stmts[4] as IfElse
|
||||
val containment = ifStmt.condition as ContainmentCheck
|
||||
(containment.element as IdentifierReference).nameInSource shouldBe listOf("source")
|
||||
@@ -634,7 +636,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 5
|
||||
stmts.size shouldBe 6
|
||||
val ifStmt = stmts[4] as IfElse
|
||||
ifStmt.condition shouldBe instanceOf<BinaryExpression>()
|
||||
}
|
||||
@@ -652,7 +654,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 5
|
||||
stmts.size shouldBe 6
|
||||
val ifStmt = stmts[4] as IfElse
|
||||
ifStmt.condition shouldBe instanceOf<BinaryExpression>()
|
||||
}
|
||||
@@ -670,7 +672,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 5
|
||||
stmts.size shouldBe 6
|
||||
val ifStmt = stmts[4] as IfElse
|
||||
ifStmt.condition shouldBe instanceOf<BinaryExpression>()
|
||||
}
|
||||
@@ -687,7 +689,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 3
|
||||
stmts.size shouldBe 4
|
||||
}
|
||||
|
||||
test("repeated assignments to IO register should remain") {
|
||||
@@ -828,7 +830,7 @@ main {
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = false, errors = errors)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 4
|
||||
st.size shouldBe 5
|
||||
val xxConst = st[0] as VarDecl
|
||||
xxConst.type shouldBe VarDeclType.CONST
|
||||
xxConst.name shouldBe "xx"
|
||||
@@ -872,7 +874,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 8
|
||||
st.size shouldBe 9
|
||||
val if1c = (st[4] as IfElse).condition as PrefixExpression
|
||||
val if2c = (st[5] as IfElse).condition as PrefixExpression
|
||||
val if3c = (st[6] as IfElse).condition as PrefixExpression
|
||||
@@ -915,7 +917,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 12
|
||||
st.size shouldBe 13
|
||||
val if1 = st[4] as IfElse
|
||||
val if2 = st[5] as IfElse
|
||||
val if3 = st[6] as IfElse
|
||||
@@ -970,7 +972,7 @@ main {
|
||||
|
||||
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 17
|
||||
st.size shouldBe 18
|
||||
|
||||
val answerValue = (st[3] as Assignment).value
|
||||
answerValue shouldBe NumericLiteral(BaseDataType.UWORD, 0.0, Position.DUMMY)
|
||||
@@ -1019,7 +1021,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 3
|
||||
st.size shouldBe 4
|
||||
val ifCond1 = (st[0] as IfElse).condition as BinaryExpression
|
||||
val ifCond2 = (st[1] as IfElse).condition as BinaryExpression
|
||||
val ifCond3 = (st[2] as IfElse).condition as BinaryExpression
|
||||
@@ -1178,4 +1180,54 @@ main {
|
||||
}"""
|
||||
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("correct unused block removal for virtual target") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0++
|
||||
}
|
||||
}
|
||||
|
||||
some_block {
|
||||
uword buffer = memory("arena", 2000, 0)
|
||||
}
|
||||
|
||||
|
||||
other_block {
|
||||
sub redherring (uword buffer) {
|
||||
%ir {{
|
||||
loadm.w r99000,other_block.redherring.buffer
|
||||
}}
|
||||
}
|
||||
}
|
||||
"""
|
||||
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = true)!!
|
||||
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
|
||||
VmRunner().runProgram(virtfile.readText(), false)
|
||||
}
|
||||
|
||||
test("correct unused block removal for c64 target") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0++
|
||||
}
|
||||
}
|
||||
|
||||
some_block {
|
||||
uword buffer = memory("arena", 2000, 0)
|
||||
}
|
||||
|
||||
|
||||
other_block {
|
||||
sub redherring (uword buffer) {
|
||||
%asm {{
|
||||
lda #<p8b_other_block.p8s_redherring.p8v_buffer
|
||||
ldy #>p8b_other_block.p8s_redherring.p8v_buffer
|
||||
}}
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), true, src, outputDir) shouldNotBe null
|
||||
}
|
||||
})
|
||||
|
@@ -19,11 +19,7 @@ import prog8.code.core.Position
|
||||
import prog8.code.core.unescape
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.encodings.Encoder
|
||||
import prog8.code.target.encodings.AtasciiEncoding
|
||||
import prog8.code.target.encodings.C64osEncoding
|
||||
import prog8.code.target.encodings.IsoEncoding
|
||||
import prog8.code.target.encodings.PetsciiEncoding
|
||||
import prog8.code.target.encodings.*
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.compileText
|
||||
import java.io.CharConversionException
|
||||
@@ -227,7 +223,7 @@ class TestStringEncodings: FunSpec({
|
||||
|
||||
context("iso") {
|
||||
test("iso accepts iso-characters") {
|
||||
val result = IsoEncoding.encode("a_~ëç")
|
||||
val result = IsoEncoding.encode("a_~ëç", false)
|
||||
result.getOrElse { throw it }.map {it.toInt()} shouldBe listOf(97, 95, 126, 235, 231)
|
||||
}
|
||||
|
||||
@@ -276,13 +272,13 @@ class TestStringEncodings: FunSpec({
|
||||
passthrough[1] shouldBe '\u801b'
|
||||
passthrough[2] shouldBe '\u8099'
|
||||
passthrough[3] shouldBe '\u80ff'
|
||||
var encoded = Encoder.encodeString(passthrough, Encoding.PETSCII)
|
||||
var encoded = Encoder(false).encodeString(passthrough, Encoding.PETSCII)
|
||||
encoded shouldBe listOf<UByte>(0u, 0x1bu, 0x99u, 0xffu)
|
||||
encoded = Encoder.encodeString(passthrough, Encoding.ATASCII)
|
||||
encoded = Encoder(false).encodeString(passthrough, Encoding.ATASCII)
|
||||
encoded shouldBe listOf<UByte>(0u, 0x1bu, 0x99u, 0xffu)
|
||||
encoded = Encoder.encodeString(passthrough, Encoding.SCREENCODES)
|
||||
encoded = Encoder(false).encodeString(passthrough, Encoding.SCREENCODES)
|
||||
encoded shouldBe listOf<UByte>(0u, 0x1bu, 0x99u, 0xffu)
|
||||
encoded = Encoder.encodeString(passthrough, Encoding.ISO)
|
||||
encoded = Encoder(false).encodeString(passthrough, Encoding.ISO)
|
||||
encoded shouldBe listOf<UByte>(0u, 0x1bu, 0x99u, 0xffu)
|
||||
}
|
||||
|
||||
@@ -413,5 +409,39 @@ class TestStringEncodings: FunSpec({
|
||||
val char2 = (main.statements[5] as Assignment).value as NumericLiteral
|
||||
char2.number shouldBe 80.0
|
||||
}
|
||||
|
||||
test("with newline conversion") {
|
||||
val encoder = Encoder(true)
|
||||
encoder.encodeString("\n\r", Encoding.PETSCII) shouldBe listOf<UByte>(13u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.SCREENCODES) shouldBe listOf<UByte>(141u, 141u)
|
||||
encoder.encodeString("\n\r", Encoding.ATASCII) shouldBe listOf<UByte>(155u, 155u)
|
||||
encoder.encodeString("\n\r", Encoding.ISO) shouldBe listOf<UByte>(13u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.ISO5) shouldBe listOf<UByte>(13u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.ISO16) shouldBe listOf<UByte>(13u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.CP437) shouldBe listOf<UByte>(10u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.KATAKANA) shouldBe listOf<UByte>(13u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.C64OS) shouldBe listOf<UByte>(13u, 13u)
|
||||
|
||||
encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.PETSCII).map { it.code } shouldBe listOf(10, 10)
|
||||
encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.ISO).map { it.code } shouldBe listOf(10, 10)
|
||||
encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.CP437).map { it.code } shouldBe listOf(10, 13)
|
||||
}
|
||||
|
||||
test("without newline conversion") {
|
||||
val encoder = Encoder(false)
|
||||
encoder.encodeString("\n\r", Encoding.PETSCII) shouldBe listOf<UByte>(13u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.SCREENCODES) shouldBe listOf<UByte>(141u, 141u)
|
||||
encoder.encodeString("\n\r", Encoding.ATASCII) shouldBe listOf<UByte>(155u, 155u)
|
||||
encoder.encodeString("\n\r", Encoding.ISO) shouldBe listOf<UByte>(10u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.ISO5) shouldBe listOf<UByte>(10u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.ISO16) shouldBe listOf<UByte>(10u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.CP437) shouldBe listOf<UByte>(10u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.KATAKANA) shouldBe listOf<UByte>(10u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.C64OS) shouldBe listOf<UByte>(13u, 13u)
|
||||
|
||||
encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.PETSCII).map { it.code } shouldBe listOf(10, 10)
|
||||
encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.ISO).map { it.code } shouldBe listOf(10, 13)
|
||||
encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.CP437).map { it.code } shouldBe listOf(10, 13)
|
||||
}
|
||||
})
|
||||
|
||||
|
@@ -54,6 +54,9 @@ class TestSubroutines: FunSpec({
|
||||
}
|
||||
|
||||
asmsub asmfunc(str thing @AY) {
|
||||
%asm {{
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub func(str thing) {
|
||||
@@ -67,12 +70,12 @@ class TestSubroutines: FunSpec({
|
||||
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
||||
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
||||
asmfunc.isAsmSubroutine shouldBe true
|
||||
asmfunc.statements.isEmpty() shouldBe true
|
||||
asmfunc.statements.size shouldBe 1
|
||||
func.isAsmSubroutine shouldBe false
|
||||
withClue("str param for subroutines should be changed into UWORD") {
|
||||
asmfunc.parameters.single().type shouldBe DataType.UWORD
|
||||
func.parameters.single().type shouldBe DataType.UWORD
|
||||
func.statements.size shouldBe 4
|
||||
func.statements.size shouldBe 5
|
||||
val paramvar = func.statements[0] as VarDecl
|
||||
paramvar.name shouldBe "thing"
|
||||
paramvar.datatype shouldBe DataType.UWORD
|
||||
@@ -174,6 +177,9 @@ class TestSubroutines: FunSpec({
|
||||
}
|
||||
|
||||
asmsub asmfunc(ubyte[] thing @AY) {
|
||||
%asm {{
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub func(ubyte[] thing) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user