mirror of
https://github.com/irmen/prog8.git
synced 2025-06-16 09:23:43 +00:00
Compare commits
134 Commits
v11.2
...
languageSe
Author | SHA1 | Date | |
---|---|---|---|
837e88d61d | |||
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 | |||
84026b105f | |||
a4d0589f10 | |||
e375f6afce | |||
5a7bc04816 | |||
bd1894580e | |||
9e694c0337 | |||
c82586db28 | |||
dd2d466350 | |||
830da8de0a | |||
4e5ee333c8 | |||
9df899eb63 | |||
ca7491a702 | |||
1a07129865 | |||
4fbd67ff99 | |||
5bc6c50f42 | |||
063de3801d | |||
ae65266a4a | |||
8ed2401e0b | |||
d2e8ee8269 | |||
1f996e3b8b | |||
7108b74105 | |||
801fe1b604 | |||
fb44c87597 | |||
6b9cdbd482 | |||
0ab98033b5 | |||
14a2b96609 | |||
f829b689db | |||
dfda8b7ed5 | |||
4388466451 | |||
5c2f509a52 | |||
59582f5210 | |||
e2a8bdbdfb | |||
0916b943da | |||
9c7ebc883c | |||
0ee42b9aa0 | |||
37b3868ca3 | |||
a6835ce3f0 | |||
69c96ad99b | |||
b72877d59d | |||
05eb15d4f7 | |||
f1fec37c79 | |||
73f6880ff8 | |||
8a53742f31 | |||
9be40e85ff | |||
61079c1eb7 | |||
1075ee8fc3 | |||
a28b265197 | |||
20e534c468 | |||
da7aa5dc49 | |||
8f2a43ca0a | |||
d0909d7810 | |||
1641999d20 | |||
e16452037c | |||
344d79684a | |||
573a1d9b7b | |||
25ab57580c | |||
a332e0e3d1 | |||
376f1cb139 | |||
90f80558d7 | |||
e281994898 | |||
29fac122e1 | |||
1dc412eb90 | |||
3770a4fe0c | |||
79cda544c8 | |||
f04b97d890 | |||
3e9b4ccc45 | |||
2c3d838dd8 | |||
7668a3c660 | |||
5dd45b714a | |||
8b08895d0f |
1458
.idea/inspectionProfiles/Project_Default.xml
generated
1458
.idea/inspectionProfiles/Project_Default.xml
generated
File diff suppressed because it is too large
Load Diff
20
.idea/libraries/KotlinJavaRuntime.xml
generated
20
.idea/libraries/KotlinJavaRuntime.xml
generated
@ -1,23 +1,23 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="KotlinJavaRuntime" type="repository">
|
<library name="KotlinJavaRuntime" type="repository">
|
||||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.10" />
|
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.20" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10.jar!/" />
|
<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.10/kotlin-stdlib-2.1.10.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/annotations/13.0/annotations-13.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.10/kotlin-stdlib-jdk7-2.1.10.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC>
|
<JAVADOC>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10-javadoc.jar!/" />
|
<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.10/kotlin-stdlib-2.1.10-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/annotations/13.0/annotations-13.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.10/kotlin-stdlib-jdk7-2.1.10-javadoc.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-javadoc.jar!/" />
|
||||||
</JAVADOC>
|
</JAVADOC>
|
||||||
<SOURCES>
|
<SOURCES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10-sources.jar!/" />
|
<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.10/kotlin-stdlib-2.1.10-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/annotations/13.0/annotations-13.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.10/kotlin-stdlib-jdk7-2.1.10-sources.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-sources.jar!/" />
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
</component>
|
</component>
|
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -22,7 +22,7 @@
|
|||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
<type id="Python" />
|
<type id="Python" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="openjdk-17" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="openjdk-21" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
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$/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$/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$/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$/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$/.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" />
|
<module fileurl="file://$PROJECT_DIR$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" />
|
||||||
|
@ -65,11 +65,13 @@ What does Prog8 provide?
|
|||||||
- access to most Kernal ROM routines as external subroutine definitions you can call normally
|
- access to most Kernal ROM routines as external subroutine definitions you can call normally
|
||||||
- tight control over Zeropage usage
|
- tight control over Zeropage usage
|
||||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||||
|
- programs can be configured to execute in ROM
|
||||||
- strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents.
|
- strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents.
|
||||||
- automatic static variable allocations, automatic string and array variables and string sharing
|
- automatic static variable allocations, automatic string and array variables and string sharing
|
||||||
- high-level program optimizations
|
- high-level program optimizations
|
||||||
- conditional branches that map 1:1 to cpu status flags
|
- conditional branches that map 1:1 to cpu status flags
|
||||||
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
|
- ``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
|
- ``in`` expression for concise and efficient multi-value/containment check
|
||||||
- ``defer`` statement to help write concise and robust subroutine cleanup logic
|
- ``defer`` statement to help write concise and robust subroutine cleanup logic
|
||||||
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
|
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
|
||||||
|
@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
|
|||||||
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "2.1.10"
|
kotlin("jvm") version "2.1.20"
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
|
@ -1,8 +1,31 @@
|
|||||||
package prog8.code
|
package prog8.code
|
||||||
|
|
||||||
|
import java.io.IOException
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.absolute
|
||||||
|
|
||||||
|
|
||||||
// the automatically generated module where all string literals are interned to:
|
// the automatically generated module where all string literals are interned to:
|
||||||
const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings"
|
const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings"
|
||||||
|
|
||||||
// all automatically generated labels everywhere need to have the same label name prefix:
|
// all automatically generated labels everywhere need to have the same label name prefix:
|
||||||
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
|
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the absolute path of the given path,
|
||||||
|
* where links are replaced by the actual directories,
|
||||||
|
* and containing no redundant path elements.
|
||||||
|
* If the path doesn't refer to an existing directory or file on the file system,
|
||||||
|
* it is returned unchanged.
|
||||||
|
*/
|
||||||
|
fun Path.sanitize(): Path {
|
||||||
|
return try {
|
||||||
|
this.toRealPath().normalize()
|
||||||
|
} catch (_: java.nio.file.NoSuchFileException) {
|
||||||
|
this.absolute().normalize()
|
||||||
|
//throw NoSuchFileException(this.toFile(), null, nx.reason).also { it.initCause(nx) }
|
||||||
|
} catch (iox: IOException) {
|
||||||
|
throw FileSystemException(this.toFile()).also { it.initCause(iox) }
|
||||||
|
}
|
||||||
|
}
|
@ -32,6 +32,7 @@ class CompilationOptions(val output: OutputType,
|
|||||||
var breakpointCpuInstruction: String? = null,
|
var breakpointCpuInstruction: String? = null,
|
||||||
var ignoreFootguns: Boolean = false,
|
var ignoreFootguns: Boolean = false,
|
||||||
var outputDir: Path = Path(""),
|
var outputDir: Path = Path(""),
|
||||||
|
var quiet: Boolean = false,
|
||||||
var symbolDefs: Map<String, String> = emptyMap()
|
var symbolDefs: Map<String, String> = emptyMap()
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package prog8.code.core
|
package prog8.code.core
|
||||||
|
|
||||||
import java.util.Objects
|
import java.util.*
|
||||||
|
|
||||||
enum class BaseDataType {
|
enum class BaseDataType {
|
||||||
UBYTE, // pass by value 8 bits unsigned
|
UBYTE, // pass by value 8 bits unsigned
|
||||||
@ -80,6 +80,17 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
|||||||
override fun hashCode(): Int = Objects.hash(base, sub)
|
override fun hashCode(): Int = Objects.hash(base, sub)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
val UBYTE = DataType(BaseDataType.UBYTE, null)
|
||||||
|
val BYTE = DataType(BaseDataType.BYTE, null)
|
||||||
|
val UWORD = DataType(BaseDataType.UWORD, null)
|
||||||
|
val WORD = DataType(BaseDataType.WORD, null)
|
||||||
|
val LONG = DataType(BaseDataType.LONG, null)
|
||||||
|
val FLOAT = DataType(BaseDataType.FLOAT, null)
|
||||||
|
val BOOL = DataType(BaseDataType.BOOL, null)
|
||||||
|
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE)
|
||||||
|
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null)
|
||||||
|
|
||||||
private val simpletypes = mapOf(
|
private val simpletypes = mapOf(
|
||||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
|
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
|
||||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
|
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
|
||||||
|
@ -28,6 +28,7 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
|
|||||||
val libraryPath: Path?
|
val libraryPath: Path?
|
||||||
val customLauncher: List<String>
|
val customLauncher: List<String>
|
||||||
val additionalAssemblerOptions: String?
|
val additionalAssemblerOptions: String?
|
||||||
|
val defaultOutputType: OutputType
|
||||||
|
|
||||||
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
|
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
|
||||||
fun getFloatAsmBytes(num: Number): String
|
fun getFloatAsmBytes(num: Number): String
|
||||||
@ -35,7 +36,7 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
|
|||||||
fun convertFloatToBytes(num: Double): List<UByte>
|
fun convertFloatToBytes(num: Double): List<UByte>
|
||||||
fun convertBytesToFloat(bytes: List<UByte>): Double
|
fun convertBytesToFloat(bytes: List<UByte>): Double
|
||||||
|
|
||||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
|
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean)
|
||||||
fun isIOAddress(address: UInt): Boolean
|
fun isIOAddress(address: UInt): Boolean
|
||||||
|
|
||||||
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||||
|
@ -13,4 +13,6 @@ interface IErrorReporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun noErrorForLine(position: Position): Boolean
|
fun noErrorForLine(position: Position): Boolean
|
||||||
|
|
||||||
|
fun printSingleError(errormessage: String)
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ interface IMemSizer {
|
|||||||
|
|
||||||
fun memorySize(dt: BaseDataType): Int {
|
fun memorySize(dt: BaseDataType): Int {
|
||||||
if(dt.isPassByRef)
|
if(dt.isPassByRef)
|
||||||
return memorySize(DataType.forDt(BaseDataType.UWORD), null) // a pointer size
|
return memorySize(DataType.UWORD, null) // a pointer size
|
||||||
try {
|
try {
|
||||||
return memorySize(DataType.forDt(dt), null)
|
return memorySize(DataType.forDt(dt), null)
|
||||||
} catch (x: NoSuchElementException) {
|
} catch (x: NoSuchElementException) {
|
||||||
|
@ -82,7 +82,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||||||
}
|
}
|
||||||
datatype.isFloat -> {
|
datatype.isFloat -> {
|
||||||
if (options.floats) {
|
if (options.floats) {
|
||||||
val memsize = options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
|
val memsize = options.compTarget.memorySize(DataType.FLOAT, null)
|
||||||
if(position!=null)
|
if(position!=null)
|
||||||
errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
|
errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
|
||||||
else
|
else
|
||||||
@ -154,7 +154,7 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
|
|||||||
datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!)
|
datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!)
|
||||||
datatype.isFloat -> {
|
datatype.isFloat -> {
|
||||||
if (options.floats) {
|
if (options.floats) {
|
||||||
options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
|
options.compTarget.memorySize(DataType.FLOAT, null)
|
||||||
} else return Err(MemAllocationError("floating point option not enabled"))
|
} else return Err(MemAllocationError("floating point option not enabled"))
|
||||||
}
|
}
|
||||||
else -> throw MemAllocationError("weird dt")
|
else -> throw MemAllocationError("weird dt")
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package prog8.code.core
|
package prog8.code.core
|
||||||
|
|
||||||
|
import prog8.code.sanitize
|
||||||
import prog8.code.source.SourceCode
|
import prog8.code.source.SourceCode
|
||||||
import java.nio.file.InvalidPathException
|
import java.nio.file.InvalidPathException
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
import kotlin.io.path.absolute
|
|
||||||
|
|
||||||
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
||||||
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
||||||
@ -13,7 +13,7 @@ data class Position(val file: String, val line: Int, val startCol: Int, val endC
|
|||||||
if(SourceCode.isLibraryResource(file))
|
if(SourceCode.isLibraryResource(file))
|
||||||
return "$file:$line:$startCol:"
|
return "$file:$line:$startCol:"
|
||||||
return try {
|
return try {
|
||||||
val path = Path(file).absolute().normalize().toString()
|
val path = Path(file).sanitize().toString()
|
||||||
"file://$path:$line:$startCol:"
|
"file://$path:$line:$startCol:"
|
||||||
} catch(_: InvalidPathException) {
|
} catch(_: InvalidPathException) {
|
||||||
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
|
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package prog8.code.source
|
package prog8.code.source
|
||||||
|
|
||||||
import prog8.code.core.Position
|
import prog8.code.core.Position
|
||||||
|
import prog8.code.sanitize
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.TreeMap
|
import java.util.*
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
import kotlin.io.path.absolute
|
|
||||||
|
|
||||||
|
|
||||||
// Resource caching "filesystem".
|
// Resource caching "filesystem".
|
||||||
@ -22,7 +22,7 @@ object ImportFileSystem {
|
|||||||
fun expandTilde(path: Path): Path = Path(expandTilde(path.toString()))
|
fun expandTilde(path: Path): Path = Path(expandTilde(path.toString()))
|
||||||
|
|
||||||
fun getFile(path: Path, isLibrary: Boolean=false): SourceCode {
|
fun getFile(path: Path, isLibrary: Boolean=false): SourceCode {
|
||||||
val normalized = path.absolute().normalize()
|
val normalized = path.sanitize()
|
||||||
val cached = cache[normalized.toString()]
|
val cached = cache[normalized.toString()]
|
||||||
if (cached != null)
|
if (cached != null)
|
||||||
return cached
|
return cached
|
||||||
@ -48,7 +48,7 @@ object ImportFileSystem {
|
|||||||
val cached = cache[position.file]
|
val cached = cache[position.file]
|
||||||
if(cached != null)
|
if(cached != null)
|
||||||
return getLine(cached, position.line)
|
return getLine(cached, position.line)
|
||||||
val path = Path(position.file).absolute().normalize()
|
val path = Path(position.file).sanitize()
|
||||||
val cached2 = cache[path.toString()]
|
val cached2 = cache[path.toString()]
|
||||||
if(cached2 != null)
|
if(cached2 != null)
|
||||||
return getLine(cached2, position.line)
|
return getLine(cached2, position.line)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package prog8.code.source
|
package prog8.code.source
|
||||||
|
|
||||||
|
import prog8.code.sanitize
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.text.Normalizer
|
import java.text.Normalizer
|
||||||
@ -59,7 +60,7 @@ sealed class SourceCode {
|
|||||||
private const val LIBRARYFILEPREFIX = "library:"
|
private const val LIBRARYFILEPREFIX = "library:"
|
||||||
private const val STRINGSOURCEPREFIX = "string:"
|
private const val STRINGSOURCEPREFIX = "string:"
|
||||||
val curdir: Path = Path(".").absolute()
|
val curdir: Path = Path(".").absolute()
|
||||||
fun relative(path: Path): Path = curdir.relativize(path.absolute())
|
fun relative(path: Path): Path = curdir.relativize(path.sanitize())
|
||||||
fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString)
|
fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString)
|
||||||
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
|
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
|
||||||
fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX)
|
fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX)
|
||||||
|
@ -6,12 +6,16 @@ import prog8.code.target.zp.C128Zeropage
|
|||||||
import java.nio.file.Path
|
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 name = NAME
|
||||||
override val defaultEncoding = Encoding.PETSCII
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
override val libraryPath = null
|
override val libraryPath = null
|
||||||
override val customLauncher: List<String> = emptyList()
|
override val customLauncher: List<String> = emptyList()
|
||||||
override val additionalAssemblerOptions = null
|
override val additionalAssemblerOptions = null
|
||||||
|
override val defaultOutputType = OutputType.PRG
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "c128"
|
const val NAME = "c128"
|
||||||
@ -48,17 +52,21 @@ class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
|||||||
return m5.toDouble()
|
return m5.toDouble()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||||
if(selectedEmulator!=1) {
|
if(selectedEmulator!=1) {
|
||||||
System.err.println("The c128 target only supports the main emulator (Vice).")
|
System.err.println("The c128 target only supports the main emulator (Vice).")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
println("\nStarting C-128 emulator x128...")
|
if(!quiet)
|
||||||
|
println("\nStarting C-128 emulator x128...")
|
||||||
|
|
||||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||||
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
val processb = ProcessBuilder(cmdline)
|
||||||
|
if(!quiet)
|
||||||
|
processb.inheritIO()
|
||||||
val process: Process = processb.start()
|
val process: Process = processb.start()
|
||||||
process.waitFor()
|
process.waitFor()
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,16 @@ import java.io.IOException
|
|||||||
import java.nio.file.Path
|
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 name = NAME
|
||||||
override val defaultEncoding = Encoding.PETSCII
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
override val libraryPath = null
|
override val libraryPath = null
|
||||||
override val customLauncher: List<String> = emptyList()
|
override val customLauncher: List<String> = emptyList()
|
||||||
override val additionalAssemblerOptions = null
|
override val additionalAssemblerOptions = null
|
||||||
|
override val defaultOutputType = OutputType.PRG
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "c64"
|
const val NAME = "c64"
|
||||||
@ -52,18 +56,22 @@ class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
|
|||||||
return m5.toDouble()
|
return m5.toDouble()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||||
if(selectedEmulator!=1) {
|
if(selectedEmulator!=1) {
|
||||||
System.err.println("The c64 target only supports the main emulator (Vice).")
|
System.err.println("The c64 target only supports the main emulator (Vice).")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for(emulator in listOf("x64sc", "x64")) {
|
for(emulator in listOf("x64sc", "x64")) {
|
||||||
println("\nStarting C-64 emulator $emulator...")
|
if(!quiet)
|
||||||
|
println("\nStarting C-64 emulator $emulator...")
|
||||||
|
|
||||||
val viceMonlist = viceMonListName(programNameWithPath.toString())
|
val viceMonlist = viceMonListName(programNameWithPath.toString())
|
||||||
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
val processb = ProcessBuilder(cmdline)
|
||||||
|
if(!quiet)
|
||||||
|
processb.inheritIO()
|
||||||
val process: Process
|
val process: Process
|
||||||
try {
|
try {
|
||||||
process=processb.start()
|
process=processb.start()
|
||||||
|
@ -24,6 +24,7 @@ class ConfigFileTarget(
|
|||||||
override val BSSHIGHRAM_END: UInt,
|
override val BSSHIGHRAM_END: UInt,
|
||||||
override val BSSGOLDENRAM_START: UInt,
|
override val BSSGOLDENRAM_START: UInt,
|
||||||
override val BSSGOLDENRAM_END: UInt,
|
override val BSSGOLDENRAM_END: UInt,
|
||||||
|
override val defaultOutputType: OutputType,
|
||||||
override val libraryPath: Path,
|
override val libraryPath: Path,
|
||||||
override val customLauncher: List<String>,
|
override val customLauncher: List<String>,
|
||||||
override val additionalAssemblerOptions: String?,
|
override val additionalAssemblerOptions: String?,
|
||||||
@ -36,7 +37,7 @@ class ConfigFileTarget(
|
|||||||
val zpFullsafe: List<UIntRange>,
|
val zpFullsafe: List<UIntRange>,
|
||||||
val zpKernalsafe: List<UIntRange>,
|
val zpKernalsafe: List<UIntRange>,
|
||||||
val zpBasicsafe: 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 {
|
companion object {
|
||||||
|
|
||||||
@ -108,7 +109,10 @@ class ConfigFileTarget(
|
|||||||
(customLauncherStr+"\n").lines().map { it.trimEnd() }
|
(customLauncherStr+"\n").lines().map { it.trimEnd() }
|
||||||
else emptyList()
|
else emptyList()
|
||||||
val assemblerOptionsStr = props.getProperty("assembler_options", "").trim()
|
val assemblerOptionsStr = props.getProperty("assembler_options", "").trim()
|
||||||
val assemblerOptions = if(assemblerOptionsStr.isBlank()) null else assemblerOptionsStr
|
val assemblerOptions = assemblerOptionsStr.ifBlank { null }
|
||||||
|
|
||||||
|
val outputTypeString = props.getProperty("output_type", "PRG")
|
||||||
|
val defaultOutputType = OutputType.valueOf(outputTypeString.uppercase())
|
||||||
|
|
||||||
return ConfigFileTarget(
|
return ConfigFileTarget(
|
||||||
configfile.nameWithoutExtension,
|
configfile.nameWithoutExtension,
|
||||||
@ -121,6 +125,7 @@ class ConfigFileTarget(
|
|||||||
props.getInteger("bss_highram_end"),
|
props.getInteger("bss_highram_end"),
|
||||||
props.getInteger("bss_goldenram_start"),
|
props.getInteger("bss_goldenram_start"),
|
||||||
props.getInteger("bss_goldenram_end"),
|
props.getInteger("bss_goldenram_end"),
|
||||||
|
defaultOutputType,
|
||||||
libraryPath,
|
libraryPath,
|
||||||
customLauncher,
|
customLauncher,
|
||||||
assemblerOptions,
|
assemblerOptions,
|
||||||
@ -148,7 +153,7 @@ class ConfigFileTarget(
|
|||||||
override fun getFloatAsmBytes(num: Number) = TODO("floats")
|
override fun getFloatAsmBytes(num: Number) = TODO("floats")
|
||||||
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("floats")
|
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("floats")
|
||||||
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("floats")
|
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("floats")
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||||
throw IllegalArgumentException("Custom compiler target cannot automatically launch an emulator. Do this manually.")
|
throw IllegalArgumentException("Custom compiler target cannot automatically launch an emulator. Do this manually.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,12 +6,16 @@ import prog8.code.target.zp.CX16Zeropage
|
|||||||
import java.nio.file.Path
|
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 name = NAME
|
||||||
override val defaultEncoding = Encoding.PETSCII
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
override val libraryPath = null
|
override val libraryPath = null
|
||||||
override val customLauncher: List<String> = emptyList()
|
override val customLauncher: List<String> = emptyList()
|
||||||
override val additionalAssemblerOptions = null
|
override val additionalAssemblerOptions = null
|
||||||
|
override val defaultOutputType = OutputType.PRG
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "cx16"
|
const val NAME = "cx16"
|
||||||
@ -48,7 +52,7 @@ class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
|||||||
return m5.toDouble()
|
return m5.toDouble()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||||
val emulator: String
|
val emulator: String
|
||||||
val extraArgs: List<String>
|
val extraArgs: List<String>
|
||||||
|
|
||||||
@ -67,9 +71,13 @@ class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println("\nStarting Commander X16 emulator $emulator...")
|
if(!quiet)
|
||||||
|
println("\nStarting Commander X16 emulator $emulator...")
|
||||||
|
|
||||||
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
val processb = ProcessBuilder(cmdline)
|
||||||
|
if(!quiet)
|
||||||
|
processb.inheritIO()
|
||||||
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
|
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
|
||||||
val process: Process = processb.start()
|
val process: Process = processb.start()
|
||||||
process.waitFor()
|
process.waitFor()
|
||||||
|
@ -18,14 +18,14 @@ internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (dt.isString) {
|
else if (dt.isString) {
|
||||||
if(numElements!=null) return numElements // treat it as the size of the given string with the length
|
return numElements // treat it as the size of the given string with the length
|
||||||
else return 2 // treat it as the size to store a string pointer
|
?: 2 // treat it as the size to store a string pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||||
dt.isFloat -> floatsize * (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")
|
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||||
else -> 2 * (numElements ?: 1)
|
else -> 2 * (numElements ?: 1)
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,16 @@ import prog8.code.target.zp.PETZeropage
|
|||||||
import java.nio.file.Path
|
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 name = NAME
|
||||||
override val defaultEncoding = Encoding.PETSCII
|
override val defaultEncoding = Encoding.PETSCII
|
||||||
override val libraryPath = null
|
override val libraryPath = null
|
||||||
override val customLauncher: List<String> = emptyList()
|
override val customLauncher: List<String> = emptyList()
|
||||||
override val additionalAssemblerOptions = null
|
override val additionalAssemblerOptions = null
|
||||||
|
override val defaultOutputType = OutputType.PRG
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "pet32"
|
const val NAME = "pet32"
|
||||||
@ -47,17 +51,21 @@ class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
|
|||||||
return m5.toDouble()
|
return m5.toDouble()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||||
if(selectedEmulator!=1) {
|
if(selectedEmulator!=1) {
|
||||||
System.err.println("The pet target only supports the main emulator (Vice).")
|
System.err.println("The pet target only supports the main emulator (Vice).")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
println("\nStarting PET emulator...")
|
if(!quiet)
|
||||||
|
println("\nStarting PET emulator...")
|
||||||
|
|
||||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||||
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
|
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
val processb = ProcessBuilder(cmdline)
|
||||||
|
if(!quiet)
|
||||||
|
processb.inheritIO()
|
||||||
val process=processb.start()
|
val process=processb.start()
|
||||||
process.waitFor()
|
process.waitFor()
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,16 @@ import kotlin.io.path.isReadable
|
|||||||
import kotlin.io.path.name
|
import kotlin.io.path.name
|
||||||
import kotlin.io.path.readText
|
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 name = NAME
|
||||||
override val defaultEncoding = Encoding.ISO
|
override val defaultEncoding = Encoding.ISO
|
||||||
override val libraryPath = null
|
override val libraryPath = null
|
||||||
override val customLauncher: List<String> = emptyList()
|
override val customLauncher: List<String> = emptyList()
|
||||||
override val additionalAssemblerOptions = null
|
override val additionalAssemblerOptions = null
|
||||||
|
override val defaultOutputType = OutputType.PRG
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "virtual"
|
const val NAME = "virtual"
|
||||||
@ -21,8 +25,8 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
|||||||
|
|
||||||
override val cpu = CpuType.VIRTUAL
|
override val cpu = CpuType.VIRTUAL
|
||||||
|
|
||||||
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE.toDouble()
|
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE
|
||||||
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE.toDouble()
|
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE
|
||||||
override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE
|
override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE
|
||||||
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
|
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||||
@ -39,7 +43,7 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
|||||||
// little endian binary representation
|
// little endian binary representation
|
||||||
val bits = num.toDouble().toBits().toULong()
|
val bits = num.toDouble().toBits().toULong()
|
||||||
val hexStr = bits.toString(16).padStart(16, '0')
|
val hexStr = bits.toString(16).padStart(16, '0')
|
||||||
val parts = hexStr.chunked(2).map { "\$" + it }
|
val parts = hexStr.chunked(2).map { "$$it" }
|
||||||
return parts.joinToString(", ")
|
return parts.joinToString(", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,19 +67,21 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
|||||||
return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7)
|
return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||||
println("\nStarting Virtual Machine...")
|
if(!quiet)
|
||||||
|
println("\nStarting Virtual Machine...")
|
||||||
|
|
||||||
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
|
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
|
||||||
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
||||||
val filename = programNameWithPath.name
|
val filename = programNameWithPath.name
|
||||||
if(programNameWithPath.isReadable()) {
|
if(programNameWithPath.isReadable()) {
|
||||||
vm.runProgram(programNameWithPath.readText())
|
vm.runProgram(programNameWithPath.readText(), quiet)
|
||||||
} else {
|
} else {
|
||||||
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
|
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
|
||||||
if(withExt.isReadable())
|
if(withExt.isReadable())
|
||||||
vm.runProgram(withExt.readText())
|
vm.runProgram(withExt.readText(), quiet)
|
||||||
else
|
else
|
||||||
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file")
|
throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,37 +91,12 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
|||||||
zeropage = VirtualZeropage(compilerOptions)
|
zeropage = VirtualZeropage(compilerOptions)
|
||||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
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) {
|
|
||||||
if(numElements!=null) return numElements // treat it as the size of the given string with the length
|
|
||||||
else return 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface IVirtualMachineRunner {
|
interface IVirtualMachineRunner {
|
||||||
fun runProgram(irSource: String)
|
fun runProgram(irSource: String, quiet: Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
||||||
|
@ -26,7 +26,7 @@ object AtasciiEncoding {
|
|||||||
'▖',
|
'▖',
|
||||||
|
|
||||||
// $10
|
// $10
|
||||||
'♣',
|
'♣',
|
||||||
'┌',
|
'┌',
|
||||||
'─',
|
'─',
|
||||||
'┼',
|
'┼',
|
||||||
@ -62,7 +62,7 @@ object AtasciiEncoding {
|
|||||||
'/',
|
'/',
|
||||||
|
|
||||||
// $30
|
// $30
|
||||||
'0',
|
'0',
|
||||||
'1',
|
'1',
|
||||||
'2',
|
'2',
|
||||||
'3',
|
'3',
|
||||||
@ -80,7 +80,7 @@ object AtasciiEncoding {
|
|||||||
'?',
|
'?',
|
||||||
|
|
||||||
// $40
|
// $40
|
||||||
'@',
|
'@',
|
||||||
'A',
|
'A',
|
||||||
'B',
|
'B',
|
||||||
'C',
|
'C',
|
||||||
@ -197,6 +197,7 @@ object AtasciiEncoding {
|
|||||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||||
val mapped = str.map { chr ->
|
val mapped = str.map { chr ->
|
||||||
when (chr) {
|
when (chr) {
|
||||||
|
'\r' -> 0x9bu
|
||||||
'\u0000' -> 0u
|
'\u0000' -> 0u
|
||||||
in '\u8000'..'\u80ff' -> {
|
in '\u8000'..'\u80ff' -> {
|
||||||
// special case: take the lower 8 bit hex value directly
|
// special case: take the lower 8 bit hex value directly
|
||||||
|
@ -285,6 +285,7 @@ object C64osEncoding {
|
|||||||
val screencode = encodingC64os[chr]
|
val screencode = encodingC64os[chr]
|
||||||
return screencode?.toUByte() ?: when (chr) {
|
return screencode?.toUByte() ?: when (chr) {
|
||||||
'\u0000' -> 0u
|
'\u0000' -> 0u
|
||||||
|
'\n' -> 13u
|
||||||
in '\u8000'..'\u80ff' -> {
|
in '\u8000'..'\u80ff' -> {
|
||||||
// special case: take the lower 8 bit hex value directly
|
// special case: take the lower 8 bit hex value directly
|
||||||
(chr.code - 0x8000).toUByte()
|
(chr.code - 0x8000).toUByte()
|
||||||
|
@ -5,19 +5,19 @@ import prog8.code.core.Encoding
|
|||||||
import prog8.code.core.IStringEncoding
|
import prog8.code.core.IStringEncoding
|
||||||
import prog8.code.core.InternalCompilerException
|
import prog8.code.core.InternalCompilerException
|
||||||
|
|
||||||
object Encoder: IStringEncoding {
|
class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding {
|
||||||
override val defaultEncoding: Encoding = Encoding.ISO
|
override val defaultEncoding: Encoding = Encoding.ISO
|
||||||
|
|
||||||
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||||
val coded = when(encoding) {
|
val coded = when(encoding) {
|
||||||
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
|
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
|
||||||
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
|
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
|
||||||
Encoding.ISO -> IsoEncoding.encode(str)
|
Encoding.ISO -> IsoEncoding.encode(str, newlineToCarriageReturn)
|
||||||
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str, newlineToCarriageReturn)
|
||||||
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str)
|
Encoding.ISO16 -> IsoEasternEncoding.encode(str, newlineToCarriageReturn)
|
||||||
Encoding.ISO16 -> IsoEasternEncoding.encode(str)
|
|
||||||
Encoding.CP437 -> Cp437Encoding.encode(str)
|
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)
|
Encoding.C64OS -> C64osEncoding.encode(str)
|
||||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||||
}
|
}
|
||||||
@ -30,12 +30,12 @@ object Encoder: IStringEncoding {
|
|||||||
val decoded = when(encoding) {
|
val decoded = when(encoding) {
|
||||||
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
|
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
|
||||||
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
|
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
|
||||||
Encoding.ISO -> IsoEncoding.decode(bytes)
|
Encoding.ISO -> IsoEncoding.decode(bytes, newlineToCarriageReturn)
|
||||||
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes, newlineToCarriageReturn)
|
||||||
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes)
|
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes, newlineToCarriageReturn)
|
||||||
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes)
|
|
||||||
Encoding.CP437 -> Cp437Encoding.decode(bytes)
|
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)
|
Encoding.C64OS -> C64osEncoding.decode(bytes)
|
||||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,16 @@ import com.github.michaelbull.result.Result
|
|||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
|
||||||
open class IsoEncodingBase(charsetName: String) {
|
open class IsoEncodingBase(charsetName: String) {
|
||||||
val charset: Charset = Charset.forName(charsetName)
|
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 {
|
return try {
|
||||||
val mapped = str.map { chr ->
|
val mapped = str.map { chr ->
|
||||||
when (chr) {
|
when (chr) {
|
||||||
'\u0000' -> 0u
|
'\u0000' -> 0u
|
||||||
|
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||||
in '\u8000'..'\u80ff' -> {
|
in '\u8000'..'\u80ff' -> {
|
||||||
// special case: take the lower 8 bit hex value directly
|
// special case: take the lower 8 bit hex value directly
|
||||||
(chr.code - 0x8000).toUByte()
|
(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 {
|
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) {
|
} catch (ce: CharConversionException) {
|
||||||
Err(ce)
|
Err(ce)
|
||||||
}
|
}
|
||||||
|
@ -64,10 +64,11 @@ object JapaneseCharacterConverter {
|
|||||||
object KatakanaEncoding {
|
object KatakanaEncoding {
|
||||||
val charset: Charset = Charset.forName("JIS_X0201")
|
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 {
|
return try {
|
||||||
val mapped = str.map { chr ->
|
val mapped = str.map { chr ->
|
||||||
when (chr) {
|
when (chr) {
|
||||||
|
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||||
|
|
||||||
'\u0000' -> 0u
|
'\u0000' -> 0u
|
||||||
'\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
|
'\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 {
|
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) {
|
} catch (ce: CharConversionException) {
|
||||||
Err(ce)
|
Err(ce)
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ object PetsciiEncoding {
|
|||||||
'\ufffe', // 0x07 -> UNDEFINED
|
'\ufffe', // 0x07 -> UNDEFINED
|
||||||
'\uf118', // 0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
|
'\uf118', // 0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
|
||||||
'\uf119', // 0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
|
'\uf119', // 0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
|
||||||
'\ufffe', // 0x0A -> UNDEFINED
|
'\n', // 0x0A -> LINE FEED (RETURN)
|
||||||
'\ufffe', // 0x0B -> UNDEFINED
|
'\ufffe', // 0x0B -> UNDEFINED
|
||||||
'\ufffe', // 0x0C -> UNDEFINED
|
'\ufffe', // 0x0C -> UNDEFINED
|
||||||
'\n' , // 0x0D -> LINE FEED (RETURN)
|
'\n' , // 0x0D -> LINE FEED (RETURN)
|
||||||
@ -1117,6 +1117,8 @@ object PetsciiEncoding {
|
|||||||
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
||||||
return screencode?.toUByte() ?: when (chr) {
|
return screencode?.toUByte() ?: when (chr) {
|
||||||
'\u0000' -> 0u
|
'\u0000' -> 0u
|
||||||
|
'\n' -> 141u
|
||||||
|
'\r' -> 141u
|
||||||
in '\u8000'..'\u80ff' -> {
|
in '\u8000'..'\u80ff' -> {
|
||||||
// special case: take the lower 8 bit hex value directly
|
// special case: take the lower 8 bit hex value directly
|
||||||
(chr.code - 0x8000).toUByte()
|
(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
|
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||||
|
|
||||||
init {
|
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(
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
ZeropageType.FLOATSAFE,
|
ZeropageType.FLOATSAFE,
|
||||||
ZeropageType.BASICSAFE,
|
ZeropageType.BASICSAFE,
|
||||||
|
@ -85,12 +85,12 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||||
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
||||||
for(reg in 0..15) {
|
for(reg in 0..15) {
|
||||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
allocatedVariables["cx16.r${reg}s"] = VarAllocation((4+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
allocatedVariables["cx16.r${reg}L"] = VarAllocation((4+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((5+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
allocatedVariables["cx16.r${reg}H"] = VarAllocation((5+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||||
free.remove((4+reg*2).toUInt())
|
free.remove((4+reg*2).toUInt())
|
||||||
free.remove((5+reg*2).toUInt())
|
free.remove((5+reg*2).toUInt())
|
||||||
}
|
}
|
||||||
|
@ -57,12 +57,12 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
||||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||||
for(reg in 0..15) {
|
for(reg in 0..15) {
|
||||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,9 @@
|
|||||||
package prog8.code.target.zp
|
package prog8.code.target.zp
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.CompilationOptions
|
||||||
|
import prog8.code.core.DataType
|
||||||
|
import prog8.code.core.Zeropage
|
||||||
|
import prog8.code.core.ZeropageType
|
||||||
|
|
||||||
class ConfigurableZeropage(
|
class ConfigurableZeropage(
|
||||||
override val SCRATCH_B1: UInt, // temp storage for a single byte
|
override val SCRATCH_B1: UInt, // temp storage for a single byte
|
||||||
@ -16,7 +19,7 @@ class ConfigurableZeropage(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
if (options.floats) {
|
if (options.floats) {
|
||||||
TODO("floats")
|
TODO("floats in configurable target zp")
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SCRATCH_REG!=SCRATCH_B1+1u)
|
if(SCRATCH_REG!=SCRATCH_B1+1u)
|
||||||
@ -27,7 +30,7 @@ class ConfigurableZeropage(
|
|||||||
ZeropageType.FULL -> fullsafe.forEach { free.addAll(it) }
|
ZeropageType.FULL -> fullsafe.forEach { free.addAll(it) }
|
||||||
ZeropageType.BASICSAFE -> basicsafe.forEach { free.addAll(it) }
|
ZeropageType.BASICSAFE -> basicsafe.forEach { free.addAll(it) }
|
||||||
ZeropageType.KERNALSAFE -> kernalsafe.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()
|
val distinctFree = free.distinct()
|
||||||
@ -46,12 +49,12 @@ class ConfigurableZeropage(
|
|||||||
for(reg in 0..15) {
|
for(reg in 0..15) {
|
||||||
val address = virtualRegistersStart + (2*reg).toUInt()
|
val address = virtualRegistersStart + (2*reg).toUInt()
|
||||||
if(address<=0xffu) {
|
if(address<=0xffu) {
|
||||||
allocatedVariables["cx16.r${reg}"] = VarAllocation(address, DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
allocatedVariables["cx16.r${reg}"] = VarAllocation(address, DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,6 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (options.floats) {
|
|
||||||
throw InternalCompilerException("PET target doesn't yet support floating point routines")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.floats && options.zeropage !in arrayOf(
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
ZeropageType.FLOATSAFE,
|
ZeropageType.FLOATSAFE,
|
||||||
ZeropageType.BASICSAFE,
|
ZeropageType.BASICSAFE,
|
||||||
|
@ -1,13 +1,7 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import com.github.michaelbull.result.fold
|
import com.github.michaelbull.result.fold
|
||||||
import prog8.code.GENERATED_LABEL_PREFIX
|
import prog8.code.*
|
||||||
import prog8.code.IAssemblyProgram
|
|
||||||
import prog8.code.ICodeGeneratorBackend
|
|
||||||
import prog8.code.StNode
|
|
||||||
import prog8.code.StNodeType
|
|
||||||
import prog8.code.SymbolTable
|
|
||||||
import prog8.code.SymbolTableMaker
|
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.code.source.ImportFileSystem
|
import prog8.code.source.ImportFileSystem
|
||||||
@ -15,7 +9,6 @@ import prog8.code.source.SourceCode
|
|||||||
import prog8.code.target.Cx16Target
|
import prog8.code.target.Cx16Target
|
||||||
import prog8.codegen.cpu6502.assignment.*
|
import prog8.codegen.cpu6502.assignment.*
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
import kotlin.io.path.absolute
|
|
||||||
import kotlin.io.path.writeLines
|
import kotlin.io.path.writeLines
|
||||||
|
|
||||||
|
|
||||||
@ -138,7 +131,7 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
|||||||
functionCallsToPrefix.reversed().forEach { (parent, index) ->
|
functionCallsToPrefix.reversed().forEach { (parent, index) ->
|
||||||
val node = parent.children[index]
|
val node = parent.children[index]
|
||||||
if(node is PtFunctionCall) {
|
if(node is PtFunctionCall) {
|
||||||
val prefixedName = PtIdentifier(node.name, DataType.forDt(BaseDataType.UNDEFINED), Position.DUMMY).prefix(parent, st)
|
val prefixedName = PtIdentifier(node.name, DataType.UNDEFINED, Position.DUMMY).prefix(parent, st)
|
||||||
val prefixedNode = node.withNewName(prefixedName.name)
|
val prefixedNode = node.withNewName(prefixedName.name)
|
||||||
parent.children[index] = prefixedNode
|
parent.children[index] = prefixedNode
|
||||||
} else {
|
} else {
|
||||||
@ -193,7 +186,7 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
|
|||||||
else -> throw AssemblyError("weird array value element $elt")
|
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.parent = parent
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@ -272,7 +265,9 @@ class AsmGen6502Internal (
|
|||||||
assembly.clear()
|
assembly.clear()
|
||||||
loopEndLabels.clear()
|
loopEndLabels.clear()
|
||||||
|
|
||||||
println("Generating assembly code... ")
|
if(!options.quiet)
|
||||||
|
println("Generating assembly code... ")
|
||||||
|
|
||||||
programGen.generate()
|
programGen.generate()
|
||||||
|
|
||||||
if(errors.noErrors()) {
|
if(errors.noErrors()) {
|
||||||
@ -437,22 +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.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 {
|
internal fun loadByteFromPointerIntoA(pointervar: PtIdentifier): String {
|
||||||
// returns the source name of the zero page pointervar if it's already in the ZP,
|
// 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
|
// otherwise returns "P8ZP_SCRATCH_W1" which is the intermediary
|
||||||
@ -627,7 +606,7 @@ class AsmGen6502Internal (
|
|||||||
is PtJump -> {
|
is PtJump -> {
|
||||||
val target = getJumpTarget(stmt)
|
val target = getJumpTarget(stmt)
|
||||||
require(!target.needsExpressionEvaluation)
|
require(!target.needsExpressionEvaluation)
|
||||||
jmp(target.asmLabel, target.indirect)
|
jmp(target.asmLabel, target.indirect, target.indexedX)
|
||||||
}
|
}
|
||||||
is PtLabel -> translate(stmt)
|
is PtLabel -> translate(stmt)
|
||||||
is PtConditionalBranch -> translate(stmt)
|
is PtConditionalBranch -> translate(stmt)
|
||||||
@ -660,16 +639,16 @@ class AsmGen6502Internal (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(expr.splitWords) {
|
if(expr.splitWords) {
|
||||||
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register), false)
|
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
when {
|
when {
|
||||||
expr.type.isByteOrBool -> {
|
expr.type.isByteOrBool -> {
|
||||||
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register), false)
|
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register))
|
||||||
}
|
}
|
||||||
expr.type.isWord -> {
|
expr.type.isWord -> {
|
||||||
assignExpressionToRegister(expr.index, RegisterOrPair.A, false)
|
assignExpressionToRegister(expr.index, RegisterOrPair.A)
|
||||||
out(" asl a")
|
out(" asl a")
|
||||||
when (register) {
|
when (register) {
|
||||||
CpuRegister.A -> {}
|
CpuRegister.A -> {}
|
||||||
@ -679,7 +658,7 @@ class AsmGen6502Internal (
|
|||||||
}
|
}
|
||||||
expr.type.isFloat -> {
|
expr.type.isFloat -> {
|
||||||
require(options.compTarget.FLOAT_MEM_SIZE == 5) {"invalid float size ${expr.position}"}
|
require(options.compTarget.FLOAT_MEM_SIZE == 5) {"invalid float size ${expr.position}"}
|
||||||
assignExpressionToRegister(expr.index, RegisterOrPair.A, false)
|
assignExpressionToRegister(expr.index, RegisterOrPair.A)
|
||||||
out("""
|
out("""
|
||||||
sta P8ZP_SCRATCH_REG
|
sta P8ZP_SCRATCH_REG
|
||||||
asl a
|
asl a
|
||||||
@ -754,7 +733,7 @@ class AsmGen6502Internal (
|
|||||||
TargetStorageKind.REGISTER -> {
|
TargetStorageKind.REGISTER -> {
|
||||||
val zero = PtNumber(BaseDataType.UBYTE, 0.0, value.position)
|
val zero = PtNumber(BaseDataType.UBYTE, 0.0, value.position)
|
||||||
zero.parent = value
|
zero.parent = value
|
||||||
assignExpressionToRegister(zero, target.register!!, false)
|
assignExpressionToRegister(zero, target.register!!)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
else -> { }
|
else -> { }
|
||||||
@ -858,7 +837,7 @@ class AsmGen6502Internal (
|
|||||||
private fun repeatWordCount(iterations: Int, stmt: PtRepeatLoop) {
|
private fun repeatWordCount(iterations: Int, stmt: PtRepeatLoop) {
|
||||||
require(iterations in 257..65536) { "invalid repeat count ${stmt.position}" }
|
require(iterations in 257..65536) { "invalid repeat count ${stmt.position}" }
|
||||||
val repeatLabel = makeLabel("repeat")
|
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
|
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("""
|
out("""
|
||||||
ldy #>$loopcount
|
ldy #>$loopcount
|
||||||
@ -878,7 +857,7 @@ $repeatLabel""")
|
|||||||
// note: A/Y must have been loaded with the number of iterations!
|
// note: A/Y must have been loaded with the number of iterations!
|
||||||
// the iny + double dec is microoptimization of the 16 bit loop
|
// the iny + double dec is microoptimization of the 16 bit loop
|
||||||
val repeatLabel = makeLabel("repeat")
|
val repeatLabel = makeLabel("repeat")
|
||||||
val counterVar = createRepeatCounterVar(BaseDataType.UWORD, false, stmt)
|
val counterVar = createTempVarReused(BaseDataType.UWORD, false, stmt)
|
||||||
out("""
|
out("""
|
||||||
cmp #0
|
cmp #0
|
||||||
beq +
|
beq +
|
||||||
@ -901,13 +880,13 @@ $repeatLabel""")
|
|||||||
require(count in 2..256) { "invalid repeat count ${stmt.position}" }
|
require(count in 2..256) { "invalid repeat count ${stmt.position}" }
|
||||||
val repeatLabel = makeLabel("repeat")
|
val repeatLabel = makeLabel("repeat")
|
||||||
if(isTargetCpu(CpuType.CPU65C02)) {
|
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(" lda #${count and 255} | sta $counterVar")
|
||||||
out(repeatLabel)
|
out(repeatLabel)
|
||||||
translate(stmt.statements)
|
translate(stmt.statements)
|
||||||
out(" dec $counterVar | bne $repeatLabel")
|
out(" dec $counterVar | bne $repeatLabel")
|
||||||
} else {
|
} else {
|
||||||
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, false, stmt)
|
val counterVar = createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||||
out(" lda #${count and 255} | sta $counterVar")
|
out(" lda #${count and 255} | sta $counterVar")
|
||||||
out(repeatLabel)
|
out(repeatLabel)
|
||||||
translate(stmt.statements)
|
translate(stmt.statements)
|
||||||
@ -919,13 +898,13 @@ $repeatLabel""")
|
|||||||
val repeatLabel = makeLabel("repeat")
|
val repeatLabel = makeLabel("repeat")
|
||||||
out(" cpy #0")
|
out(" cpy #0")
|
||||||
if(isTargetCpu(CpuType.CPU65C02)) {
|
if(isTargetCpu(CpuType.CPU65C02)) {
|
||||||
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, true, stmt)
|
val counterVar = createTempVarReused(BaseDataType.UBYTE, true, stmt)
|
||||||
out(" beq $endLabel | sty $counterVar")
|
out(" beq $endLabel | sty $counterVar")
|
||||||
out(repeatLabel)
|
out(repeatLabel)
|
||||||
translate(stmt.statements)
|
translate(stmt.statements)
|
||||||
out(" dec $counterVar | bne $repeatLabel")
|
out(" dec $counterVar | bne $repeatLabel")
|
||||||
} else {
|
} else {
|
||||||
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, false, stmt)
|
val counterVar = createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||||
out(" beq $endLabel | sty $counterVar")
|
out(" beq $endLabel | sty $counterVar")
|
||||||
out(repeatLabel)
|
out(repeatLabel)
|
||||||
translate(stmt.statements)
|
translate(stmt.statements)
|
||||||
@ -934,43 +913,9 @@ $repeatLabel""")
|
|||||||
out(endLabel)
|
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) {
|
private fun translate(stmt: PtWhen) {
|
||||||
val endLabel = makeLabel("when_end")
|
val endLabel = makeLabel("when_end")
|
||||||
val choiceBlocks = mutableListOf<Pair<String, PtNodeGroup>>()
|
val choiceBlocks = mutableListOf<Pair<String, PtWhenChoice>>()
|
||||||
val conditionDt = stmt.value.type
|
val conditionDt = stmt.value.type
|
||||||
if(conditionDt.isByte)
|
if(conditionDt.isByte)
|
||||||
assignExpressionToRegister(stmt.value, RegisterOrPair.A)
|
assignExpressionToRegister(stmt.value, RegisterOrPair.A)
|
||||||
@ -981,13 +926,20 @@ $repeatLabel""")
|
|||||||
val choice = choiceNode as PtWhenChoice
|
val choice = choiceNode as PtWhenChoice
|
||||||
var choiceLabel = makeLabel("choice")
|
var choiceLabel = makeLabel("choice")
|
||||||
if(choice.isElse) {
|
if(choice.isElse) {
|
||||||
|
require(choice.parent.children.last() === choice)
|
||||||
translate(choice.statements)
|
translate(choice.statements)
|
||||||
|
// is always the last node so can fall through
|
||||||
} else {
|
} else {
|
||||||
if(choice.statements.children.isEmpty()) {
|
if(choice.statements.children.isEmpty()) {
|
||||||
// no statements for this choice value, jump to the end immediately
|
// no statements for this choice value, jump to the end immediately
|
||||||
choiceLabel = endLabel
|
choiceLabel = endLabel
|
||||||
} else {
|
} 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) {
|
for (cv in choice.values.children) {
|
||||||
val value = (cv as PtNumber).number.toInt()
|
val value = (cv as PtNumber).number.toInt()
|
||||||
@ -1004,11 +956,14 @@ $repeatLabel""")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jmp(endLabel)
|
|
||||||
|
if(choiceBlocks.isNotEmpty())
|
||||||
|
jmp(endLabel)
|
||||||
|
|
||||||
for(choiceBlock in choiceBlocks.withIndex()) {
|
for(choiceBlock in choiceBlocks.withIndex()) {
|
||||||
out(choiceBlock.value.first)
|
out(choiceBlock.value.first)
|
||||||
translate(choiceBlock.value.second)
|
translate(choiceBlock.value.second.statements)
|
||||||
if (choiceBlock.index < choiceBlocks.size - 1)
|
if (choiceBlock.index < choiceBlocks.size - 1 && !choiceBlock.value.second.isOnlyGotoOrReturn())
|
||||||
jmp(endLabel)
|
jmp(endLabel)
|
||||||
}
|
}
|
||||||
out(endLabel)
|
out(endLabel)
|
||||||
@ -1037,6 +992,7 @@ $repeatLabel""")
|
|||||||
val target = getJumpTarget(jump)
|
val target = getJumpTarget(jump)
|
||||||
require(!target.needsExpressionEvaluation)
|
require(!target.needsExpressionEvaluation)
|
||||||
if(target.indirect) {
|
if(target.indirect) {
|
||||||
|
require(!target.indexedX)
|
||||||
val complementedInstruction = branchInstruction(stmt.condition, true)
|
val complementedInstruction = branchInstruction(stmt.condition, true)
|
||||||
out("""
|
out("""
|
||||||
$complementedInstruction +
|
$complementedInstruction +
|
||||||
@ -1070,7 +1026,7 @@ $repeatLabel""")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class JumpTarget(val asmLabel: String, val indirect: Boolean, val needsExpressionEvaluation: Boolean)
|
class JumpTarget(val asmLabel: String, val indirect: Boolean, val indexedX: Boolean, val needsExpressionEvaluation: Boolean)
|
||||||
|
|
||||||
internal fun getJumpTarget(jump: PtJump, evaluateAddressExpression: Boolean = true): JumpTarget {
|
internal fun getJumpTarget(jump: PtJump, evaluateAddressExpression: Boolean = true): JumpTarget {
|
||||||
val ident = jump.target as? PtIdentifier
|
val ident = jump.target as? PtIdentifier
|
||||||
@ -1078,20 +1034,41 @@ $repeatLabel""")
|
|||||||
// can be a label, or a pointer variable
|
// can be a label, or a pointer variable
|
||||||
val symbol = symbolTable.lookup(ident.name)
|
val symbol = symbolTable.lookup(ident.name)
|
||||||
return if(symbol?.type in arrayOf(StNodeType.STATICVAR, StNodeType.MEMVAR, StNodeType.CONSTANT))
|
return if(symbol?.type in arrayOf(StNodeType.STATICVAR, StNodeType.MEMVAR, StNodeType.CONSTANT))
|
||||||
JumpTarget(asmSymbolName(ident), true, false) // indirect jump if the jump symbol is a variable
|
JumpTarget(asmSymbolName(ident), true, false,false) // indirect jump if the jump symbol is a variable
|
||||||
else
|
else
|
||||||
JumpTarget(asmSymbolName(ident), false, false)
|
JumpTarget(asmSymbolName(ident), false, false,false)
|
||||||
}
|
}
|
||||||
val addr = jump.target.asConstInteger()
|
val addr = jump.target.asConstInteger()
|
||||||
if(addr!=null)
|
if(addr!=null)
|
||||||
return JumpTarget(addr.toHex(), false, false)
|
return JumpTarget(addr.toHex(), false, false,false)
|
||||||
else {
|
else {
|
||||||
if(evaluateAddressExpression) {
|
if(evaluateAddressExpression) {
|
||||||
|
val arrayIdx = jump.target as? PtArrayIndexer
|
||||||
|
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
|
// we can do the address evaluation right now and just use a temporary pointer variable
|
||||||
assignExpressionToVariable(jump.target, "P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UWORD))
|
assignExpressionToVariable(jump.target, "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||||
return JumpTarget("P8ZP_SCRATCH_W1", true, false)
|
return JumpTarget("P8ZP_SCRATCH_W1", true, false,false)
|
||||||
} else {
|
} else {
|
||||||
return JumpTarget("PROG8_JUMP_TARGET_IS_UNEVALUATED_ADDRESS_EXPRESSION", true, true)
|
return JumpTarget("PROG8_JUMP_TARGET_IS_UNEVALUATED_ADDRESS_EXPRESSION", true, false,true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1102,7 +1079,7 @@ $repeatLabel""")
|
|||||||
val returnRegs = sub.returnsWhatWhere()
|
val returnRegs = sub.returnsWhatWhere()
|
||||||
|
|
||||||
if(returnvalue!=null) {
|
if(returnvalue!=null) {
|
||||||
if (sub.returns.single().isNumericOrBool==true) {
|
if (sub.returns.single().isNumericOrBool) {
|
||||||
assignExpressionToRegister(returnvalue as PtExpression, returnRegs.single().first.registerOrPair!!)
|
assignExpressionToRegister(returnvalue as PtExpression, returnRegs.single().first.registerOrPair!!)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1144,8 +1121,8 @@ $repeatLabel""")
|
|||||||
val sourcePath = Path(incbin.definingBlock()!!.source.origin)
|
val sourcePath = Path(incbin.definingBlock()!!.source.origin)
|
||||||
val includedPath = sourcePath.resolveSibling(incbin.file)
|
val includedPath = sourcePath.resolveSibling(incbin.file)
|
||||||
val pathForAssembler = options.outputDir // #54: 64tass needs the path *relative to the .asm file*
|
val pathForAssembler = options.outputDir // #54: 64tass needs the path *relative to the .asm file*
|
||||||
.absolute()
|
.sanitize()
|
||||||
.relativize(includedPath.absolute())
|
.relativize(includedPath.sanitize())
|
||||||
.normalize() // avoid assembler warnings (-Wportable; only some, not all)
|
.normalize() // avoid assembler warnings (-Wportable; only some, not all)
|
||||||
.toString().replace('\\', '/')
|
.toString().replace('\\', '/')
|
||||||
out(" .binary \"$pathForAssembler\" $offset $length")
|
out(" .binary \"$pathForAssembler\" $offset $length")
|
||||||
@ -1211,10 +1188,14 @@ $repeatLabel""")
|
|||||||
|
|
||||||
internal fun isZpVar(variable: PtIdentifier): Boolean = allocator.isZpVar(variable.name)
|
internal fun isZpVar(variable: PtIdentifier): Boolean = allocator.isZpVar(variable.name)
|
||||||
|
|
||||||
internal fun jmp(asmLabel: String, indirect: Boolean=false) {
|
internal fun jmp(asmLabel: String, indirect: Boolean=false, indexedX: Boolean=false) {
|
||||||
if(indirect) {
|
if(indirect) {
|
||||||
out(" jmp ($asmLabel)")
|
if(indexedX)
|
||||||
|
out(" jmp ($asmLabel,x)")
|
||||||
|
else
|
||||||
|
out(" jmp ($asmLabel)")
|
||||||
} else {
|
} else {
|
||||||
|
require(!indexedX) { "indexedX only allowed for indirect jumps" }
|
||||||
if (isTargetCpu(CpuType.CPU65C02))
|
if (isTargetCpu(CpuType.CPU65C02))
|
||||||
out(" bra $asmLabel") // note: 64tass will convert this automatically to a jmp if the relative distance is too large
|
out(" bra $asmLabel") // note: 64tass will convert this automatically to a jmp if the relative distance is too large
|
||||||
else
|
else
|
||||||
@ -1271,7 +1252,7 @@ $repeatLabel""")
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(addressExpr.operator=="+") {
|
if(addressExpr.operator=="+") {
|
||||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr, false)
|
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr)
|
||||||
if (ptrAndIndex == null) return false
|
if (ptrAndIndex == null) return false
|
||||||
|
|
||||||
if(write) {
|
if(write) {
|
||||||
@ -1301,13 +1282,13 @@ $repeatLabel""")
|
|||||||
val saveA = evalBytevalueWillClobberA(ptrAndIndex.first) || evalBytevalueWillClobberA(ptrAndIndex.second)
|
val saveA = evalBytevalueWillClobberA(ptrAndIndex.first) || evalBytevalueWillClobberA(ptrAndIndex.second)
|
||||||
if(saveA) out(" pha")
|
if(saveA) out(" pha")
|
||||||
if(ptrAndIndex.second.isSimple()) {
|
if(ptrAndIndex.second.isSimple()) {
|
||||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||||
if(saveA) out(" pla")
|
if(saveA) out(" pla")
|
||||||
out(" sta (P8ZP_SCRATCH_W2),y")
|
out(" sta (P8ZP_SCRATCH_W2),y")
|
||||||
} else {
|
} else {
|
||||||
pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second)
|
pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second)
|
||||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
restoreRegisterStack(CpuRegister.Y, true)
|
restoreRegisterStack(CpuRegister.Y, true)
|
||||||
if(saveA) out(" pla")
|
if(saveA) out(" pla")
|
||||||
out(" sta (P8ZP_SCRATCH_W2),y")
|
out(" sta (P8ZP_SCRATCH_W2),y")
|
||||||
@ -1344,12 +1325,12 @@ $repeatLabel""")
|
|||||||
} else {
|
} else {
|
||||||
// copy the pointer var to zp first
|
// copy the pointer var to zp first
|
||||||
if(ptrAndIndex.second.isSimple()) {
|
if(ptrAndIndex.second.isSimple()) {
|
||||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||||
out(" lda (P8ZP_SCRATCH_W2),y")
|
out(" lda (P8ZP_SCRATCH_W2),y")
|
||||||
} else {
|
} else {
|
||||||
pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second)
|
pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second)
|
||||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
restoreRegisterStack(CpuRegister.Y, false)
|
restoreRegisterStack(CpuRegister.Y, false)
|
||||||
out(" lda (P8ZP_SCRATCH_W2),y")
|
out(" lda (P8ZP_SCRATCH_W2),y")
|
||||||
}
|
}
|
||||||
@ -1389,7 +1370,7 @@ $repeatLabel""")
|
|||||||
} else {
|
} else {
|
||||||
// copy the pointer var to zp first
|
// copy the pointer var to zp first
|
||||||
out(" pha")
|
out(" pha")
|
||||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
out(" ldy #${256-constOffset} ; negative offset $constOffset")
|
out(" ldy #${256-constOffset} ; negative offset $constOffset")
|
||||||
out(" dec P8ZP_SCRATCH_W2+1 | pla | sta (P8ZP_SCRATCH_W2),y") // temporarily make MSB 1 less to be able to use the negative Y offset
|
out(" dec P8ZP_SCRATCH_W2+1 | pla | sta (P8ZP_SCRATCH_W2),y") // temporarily make MSB 1 less to be able to use the negative Y offset
|
||||||
return true
|
return true
|
||||||
@ -1419,7 +1400,7 @@ $repeatLabel""")
|
|||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
// copy the pointer var to zp first
|
// copy the pointer var to zp first
|
||||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
out(" ldy #${256-constOffset} ; negative offset $constOffset")
|
out(" ldy #${256-constOffset} ; negative offset $constOffset")
|
||||||
out(" dec P8ZP_SCRATCH_W2+1 | lda (P8ZP_SCRATCH_W2),y") // temporarily make MSB 1 less to be able to use the negative Y offset
|
out(" dec P8ZP_SCRATCH_W2+1 | lda (P8ZP_SCRATCH_W2),y") // temporarily make MSB 1 less to be able to use the negative Y offset
|
||||||
return true
|
return true
|
||||||
@ -1441,22 +1422,22 @@ $repeatLabel""")
|
|||||||
|
|
||||||
internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) {
|
internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) {
|
||||||
if(left.isSimple()) {
|
if(left.isSimple()) {
|
||||||
assignExpressionToVariable(right, rightVarName, DataType.forDt(BaseDataType.UBYTE))
|
assignExpressionToVariable(right, rightVarName, DataType.UBYTE)
|
||||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||||
} else {
|
} else {
|
||||||
pushCpuStack(BaseDataType.UBYTE, left)
|
pushCpuStack(BaseDataType.UBYTE, left)
|
||||||
assignExpressionToVariable(right, rightVarName, DataType.forDt(BaseDataType.UBYTE))
|
assignExpressionToVariable(right, rightVarName, DataType.UBYTE)
|
||||||
out(" pla")
|
out(" pla")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun assignWordOperandsToAYAndVar(left: PtExpression, right: PtExpression, rightVarname: String) {
|
internal fun assignWordOperandsToAYAndVar(left: PtExpression, right: PtExpression, rightVarname: String) {
|
||||||
if(left.isSimple()) {
|
if(left.isSimple()) {
|
||||||
assignExpressionToVariable(right, rightVarname, DataType.forDt(BaseDataType.UWORD))
|
assignExpressionToVariable(right, rightVarname, DataType.UWORD)
|
||||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||||
} else {
|
} else {
|
||||||
pushCpuStack(BaseDataType.UWORD, left)
|
pushCpuStack(BaseDataType.UWORD, left)
|
||||||
assignExpressionToVariable(right, rightVarname, DataType.forDt(BaseDataType.UWORD))
|
assignExpressionToVariable(right, rightVarname, DataType.UWORD)
|
||||||
restoreRegisterStack(CpuRegister.Y, false)
|
restoreRegisterStack(CpuRegister.Y, false)
|
||||||
restoreRegisterStack(CpuRegister.A, false)
|
restoreRegisterStack(CpuRegister.A, false)
|
||||||
}
|
}
|
||||||
@ -1465,7 +1446,7 @@ $repeatLabel""")
|
|||||||
internal fun translateDirectMemReadExpressionToRegA(expr: PtMemoryByte) {
|
internal fun translateDirectMemReadExpressionToRegA(expr: PtMemoryByte) {
|
||||||
|
|
||||||
fun assignViaExprEval() {
|
fun assignViaExprEval() {
|
||||||
assignExpressionToVariable(expr.address, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
assignExpressionToVariable(expr.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
if (isTargetCpu(CpuType.CPU65C02)) {
|
if (isTargetCpu(CpuType.CPU65C02)) {
|
||||||
out(" lda (P8ZP_SCRATCH_W2)")
|
out(" lda (P8ZP_SCRATCH_W2)")
|
||||||
} else {
|
} else {
|
||||||
@ -1541,6 +1522,51 @@ $repeatLabel""")
|
|||||||
return "$GENERATED_LABEL_PREFIX${generatedLabelSequenceNumber}_$postfix"
|
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) {
|
internal fun assignConstFloatToPointerAY(number: PtNumber) {
|
||||||
val floatConst = allocator.getFloatAsmConst(number.number)
|
val floatConst = allocator.getFloatAsmConst(number.number)
|
||||||
out("""
|
out("""
|
||||||
@ -1561,7 +1587,7 @@ $repeatLabel""")
|
|||||||
val compare = if(useSbc) "sec | sbc" else "cmp"
|
val compare = if(useSbc) "sec | sbc" else "cmp"
|
||||||
fun cmpViaScratch() {
|
fun cmpViaScratch() {
|
||||||
if(assignmentAsmGen.directIntoY(value)) {
|
if(assignmentAsmGen.directIntoY(value)) {
|
||||||
assignExpressionToRegister(value, RegisterOrPair.Y, false)
|
assignExpressionToRegister(value, RegisterOrPair.Y)
|
||||||
out(" sty P8ZP_SCRATCH_REG")
|
out(" sty P8ZP_SCRATCH_REG")
|
||||||
} else {
|
} else {
|
||||||
out(" pha")
|
out(" pha")
|
||||||
@ -1620,7 +1646,7 @@ $repeatLabel""")
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun assignConditionValueToRegisterAndTest(condition: PtExpression) {
|
internal fun assignConditionValueToRegisterAndTest(condition: PtExpression) {
|
||||||
assignExpressionToRegister(condition, RegisterOrPair.A, false)
|
assignExpressionToRegister(condition, RegisterOrPair.A)
|
||||||
when(condition) {
|
when(condition) {
|
||||||
is PtNumber,
|
is PtNumber,
|
||||||
is PtBool,
|
is PtBool,
|
||||||
@ -1661,7 +1687,7 @@ $repeatLabel""")
|
|||||||
is PtIdentifier -> equalf(asmVariableName(left), asmVariableName(right))
|
is PtIdentifier -> equalf(asmVariableName(left), asmVariableName(right))
|
||||||
is PtNumber -> equalf(asmVariableName(left), allocator.getFloatAsmConst(right.number))
|
is PtNumber -> equalf(asmVariableName(left), allocator.getFloatAsmConst(right.number))
|
||||||
else -> {
|
else -> {
|
||||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT))
|
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
|
||||||
equalf(asmVariableName(left), subroutineFloatEvalResultVar1)
|
equalf(asmVariableName(left), subroutineFloatEvalResultVar1)
|
||||||
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
|
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
|
||||||
}
|
}
|
||||||
@ -1671,7 +1697,7 @@ $repeatLabel""")
|
|||||||
is PtIdentifier -> equalf(left, asmVariableName(right))
|
is PtIdentifier -> equalf(left, asmVariableName(right))
|
||||||
is PtNumber -> equalf(left, allocator.getFloatAsmConst(right.number))
|
is PtNumber -> equalf(left, allocator.getFloatAsmConst(right.number))
|
||||||
else -> {
|
else -> {
|
||||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT))
|
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
|
||||||
equalf(left, subroutineFloatEvalResultVar1)
|
equalf(left, subroutineFloatEvalResultVar1)
|
||||||
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
|
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
|
||||||
}
|
}
|
||||||
@ -1706,7 +1732,7 @@ $repeatLabel""")
|
|||||||
is PtIdentifier -> lessf(asmVariableName(left), asmVariableName(right))
|
is PtIdentifier -> lessf(asmVariableName(left), asmVariableName(right))
|
||||||
is PtNumber -> lessf(asmVariableName(left), allocator.getFloatAsmConst(right.number))
|
is PtNumber -> lessf(asmVariableName(left), allocator.getFloatAsmConst(right.number))
|
||||||
else -> {
|
else -> {
|
||||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT))
|
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
|
||||||
lessf(asmVariableName(left), subroutineFloatEvalResultVar1)
|
lessf(asmVariableName(left), subroutineFloatEvalResultVar1)
|
||||||
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
|
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
|
||||||
}
|
}
|
||||||
@ -1716,7 +1742,7 @@ $repeatLabel""")
|
|||||||
is PtIdentifier -> lessf(left, asmVariableName(right))
|
is PtIdentifier -> lessf(left, asmVariableName(right))
|
||||||
is PtNumber -> lessf(left, allocator.getFloatAsmConst(right.number))
|
is PtNumber -> lessf(left, allocator.getFloatAsmConst(right.number))
|
||||||
else -> {
|
else -> {
|
||||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT))
|
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
|
||||||
lessf(left, subroutineFloatEvalResultVar1)
|
lessf(left, subroutineFloatEvalResultVar1)
|
||||||
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
|
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
|
||||||
}
|
}
|
||||||
@ -1748,11 +1774,13 @@ $repeatLabel""")
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun romableWarning(problem: String, pos: Position) {
|
fun romableError(problem: String, pos: Position, assemblerShouldFail: Boolean = true) {
|
||||||
if(options.romable) {
|
if(options.romable) {
|
||||||
// until the code generation can provide an alternative, we have to report about code generated that is incompatible with ROMable code mode...
|
// until the code generation can provide an alternative, we have to report about code generated that is incompatible with ROMable code mode...
|
||||||
errors.warn("problem for ROMable code: $problem", pos)
|
errors.warn("problem for ROMable code: $problem", pos)
|
||||||
out(" .error \"ROMable code selected but incompatible code was generated: $problem\"")
|
if(assemblerShouldFail) {
|
||||||
|
out(" .error \"ROMable code selected but incompatible code was generated: $problem $pos\"")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -459,7 +459,7 @@ private fun optimizeStoreLoadSame(
|
|||||||
return mods
|
return mods
|
||||||
}
|
}
|
||||||
|
|
||||||
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""")
|
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_.$]*)""")
|
||||||
|
|
||||||
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
||||||
// try to get the constant value address, could return null if it's a symbol instead
|
// try to get the constant value address, could return null if it's a symbol instead
|
||||||
@ -508,9 +508,11 @@ private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): L
|
|||||||
|
|
||||||
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||||
// jsr Sub + rts -> jmp Sub
|
// jsr Sub + rts -> jmp Sub
|
||||||
|
// jmp Sub + rts -> jmp Sub
|
||||||
// rts + jmp -> remove jmp
|
// rts + jmp -> remove jmp
|
||||||
// rts + bxx -> remove bxx
|
// rts + bxx -> remove bxx
|
||||||
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
|
// 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.
|
// and some other optimizations.
|
||||||
|
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
@ -520,7 +522,10 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
|
|||||||
val third = lines[2].value
|
val third = lines[2].value
|
||||||
|
|
||||||
if(!haslabel(second)) {
|
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!!
|
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[0].index, false, lines[0].value.replace("jsr", "jmp"))
|
||||||
mods += Modification(lines[1].index, true, null)
|
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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -687,18 +701,20 @@ private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<
|
|||||||
// phy + ldy + pla -> tya + ldy
|
// phy + ldy + pla -> tya + ldy
|
||||||
// phx + ldx + pla -> txa + ldx
|
// phx + ldx + pla -> txa + ldx
|
||||||
// pha + lda + pla -> nop
|
// pha + lda + pla -> nop
|
||||||
if(first=="phy" && second.startsWith("ldy ") && third=="pla") {
|
when (first) {
|
||||||
mods.add(Modification(lines[3].index, true, null))
|
"phy" if second.startsWith("ldy ") && third=="pla" -> {
|
||||||
mods.add(Modification(lines[1].index, false, " tya"))
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
}
|
mods.add(Modification(lines[1].index, false, " tya"))
|
||||||
else if(first=="phx" && second.startsWith("ldx ") && third=="pla") {
|
}
|
||||||
mods.add(Modification(lines[3].index, true, null))
|
"phx" if second.startsWith("ldx ") && third=="pla" -> {
|
||||||
mods.add(Modification(lines[1].index, false, " txa"))
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
}
|
mods.add(Modification(lines[1].index, false, " txa"))
|
||||||
else if(first=="pha" && second.startsWith("lda ") && third=="pla") {
|
}
|
||||||
mods.add(Modification(lines[1].index, true, null))
|
"pha" if second.startsWith("lda ") && third=="pla" -> {
|
||||||
mods.add(Modification(lines[2].index, true, null))
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
mods.add(Modification(lines[3].index, true, null))
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,10 @@ package prog8.codegen.cpu6502
|
|||||||
|
|
||||||
import prog8.code.GENERATED_LABEL_PREFIX
|
import prog8.code.GENERATED_LABEL_PREFIX
|
||||||
import prog8.code.IAssemblyProgram
|
import prog8.code.IAssemblyProgram
|
||||||
import prog8.code.core.*
|
import prog8.code.core.CompilationOptions
|
||||||
|
import prog8.code.core.ICompilationTarget
|
||||||
|
import prog8.code.core.IErrorReporter
|
||||||
|
import prog8.code.core.OutputType
|
||||||
import prog8.code.target.C128Target
|
import prog8.code.target.C128Target
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
import prog8.code.target.PETTarget
|
import prog8.code.target.PETTarget
|
||||||
@ -46,7 +49,8 @@ internal class AssemblyProgram(
|
|||||||
|
|
||||||
command.addAll(listOf("--output", prgFile.toString(), assemblyFile.toString()))
|
command.addAll(listOf("--output", prgFile.toString(), assemblyFile.toString()))
|
||||||
assemblerCommand = command
|
assemblerCommand = command
|
||||||
println("\nCreating prg for target ${compTarget.name}.")
|
if(!options.quiet)
|
||||||
|
println("\nCreating prg for target ${compTarget.name}.")
|
||||||
}
|
}
|
||||||
OutputType.XEX -> {
|
OutputType.XEX -> {
|
||||||
// Atari800XL .xex generation.
|
// Atari800XL .xex generation.
|
||||||
@ -67,7 +71,8 @@ internal class AssemblyProgram(
|
|||||||
|
|
||||||
command.addAll(listOf("--output", xexFile.toString(), assemblyFile.toString()))
|
command.addAll(listOf("--output", xexFile.toString(), assemblyFile.toString()))
|
||||||
assemblerCommand = command
|
assemblerCommand = command
|
||||||
println("\nCreating xex for target ${compTarget.name}.")
|
if(!options.quiet)
|
||||||
|
println("\nCreating xex for target ${compTarget.name}.")
|
||||||
}
|
}
|
||||||
OutputType.RAW -> {
|
OutputType.RAW -> {
|
||||||
// Neo6502/headerless raw program generation.
|
// Neo6502/headerless raw program generation.
|
||||||
@ -87,7 +92,8 @@ internal class AssemblyProgram(
|
|||||||
|
|
||||||
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
||||||
assemblerCommand = command
|
assemblerCommand = command
|
||||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
if(!options.quiet)
|
||||||
|
println("\nCreating raw binary for target ${compTarget.name}.")
|
||||||
}
|
}
|
||||||
OutputType.LIBRARY -> {
|
OutputType.LIBRARY -> {
|
||||||
// CBM machines library (.bin) generation (with or without 2 byte load address header depending on the compilation target machine)
|
// CBM machines library (.bin) generation (with or without 2 byte load address header depending on the compilation target machine)
|
||||||
@ -107,10 +113,12 @@ internal class AssemblyProgram(
|
|||||||
command.add("--list=$listFile")
|
command.add("--list=$listFile")
|
||||||
|
|
||||||
if(compTarget.name in listOf(C64Target.NAME, C128Target.NAME, PETTarget.NAME)) {
|
if(compTarget.name in listOf(C64Target.NAME, C128Target.NAME, PETTarget.NAME)) {
|
||||||
println("\nCreating binary library file with header for target ${compTarget.name}.")
|
if(!options.quiet)
|
||||||
|
println("\nCreating binary library file with header for target ${compTarget.name}.")
|
||||||
command.add("--cbm-prg")
|
command.add("--cbm-prg")
|
||||||
} else {
|
} else {
|
||||||
println("\nCreating binary library file without header for target ${compTarget.name}.")
|
if(!options.quiet)
|
||||||
|
println("\nCreating binary library file without header for target ${compTarget.name}.")
|
||||||
command.add("--nostart") // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD
|
command.add("--nostart") // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,8 +130,11 @@ internal class AssemblyProgram(
|
|||||||
if(options.compTarget.additionalAssemblerOptions!=null)
|
if(options.compTarget.additionalAssemblerOptions!=null)
|
||||||
assemblerCommand.add(options.compTarget.additionalAssemblerOptions!!)
|
assemblerCommand.add(options.compTarget.additionalAssemblerOptions!!)
|
||||||
|
|
||||||
val proc = ProcessBuilder(assemblerCommand).inheritIO().start()
|
val proc = ProcessBuilder(assemblerCommand)
|
||||||
val result = proc.waitFor()
|
if(!options.quiet)
|
||||||
|
proc.inheritIO()
|
||||||
|
val process = proc.start()
|
||||||
|
val result = process.waitFor()
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
removeGeneratedLabelsFromMonlist()
|
removeGeneratedLabelsFromMonlist()
|
||||||
generateBreakpointList()
|
generateBreakpointList()
|
||||||
@ -149,7 +160,7 @@ internal class AssemblyProgram(
|
|||||||
for (line in viceMonListFile.toFile().readLines()) {
|
for (line in viceMonListFile.toFile().readLines()) {
|
||||||
val match = pattern.matchEntire(line)
|
val match = pattern.matchEntire(line)
|
||||||
if (match != null)
|
if (match != null)
|
||||||
breakpoints.add("break \$" + match.groupValues[1])
|
breakpoints.add("break $" + match.groupValues[1])
|
||||||
}
|
}
|
||||||
val num = breakpoints.size
|
val num = breakpoints.size
|
||||||
breakpoints.add(0, "; breakpoint list now follows")
|
breakpoints.add(0, "; breakpoint list now follows")
|
||||||
|
@ -53,9 +53,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
val memread = PtMemoryByte(fcall.position)
|
val memread = PtMemoryByte(fcall.position)
|
||||||
memread.add(fcall.args[0])
|
memread.add(fcall.args[0])
|
||||||
memread.parent = fcall
|
memread.parent = fcall
|
||||||
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A)
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
val memtarget = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.position, memory=memread)
|
val memtarget = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.position, memory=memread)
|
||||||
asmgen.assignExpressionTo(fcall.args[1], memtarget)
|
asmgen.assignExpressionTo(fcall.args[1], memtarget)
|
||||||
asmgen.out(" pla")
|
asmgen.out(" pla")
|
||||||
}
|
}
|
||||||
@ -107,8 +107,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" jsr prog8_math.divmod_ub_asm")
|
asmgen.out(" jsr prog8_math.divmod_ub_asm")
|
||||||
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
||||||
val var3name = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
|
val var3name = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
|
||||||
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[2].position, var2name)
|
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
|
||||||
val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[3].position, var3name)
|
val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[3].position, var3name)
|
||||||
assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A, false, false)
|
assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A, false, false)
|
||||||
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y, false, false)
|
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y, false, false)
|
||||||
}
|
}
|
||||||
@ -120,7 +120,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
// output: P8ZP_SCRATCH_W2 in ZP: 16-bit remainder, A/Y: 16 bit division result
|
// output: P8ZP_SCRATCH_W2 in ZP: 16-bit remainder, A/Y: 16 bit division result
|
||||||
asmgen.out(" jsr prog8_math.divmod_uw_asm")
|
asmgen.out(" jsr prog8_math.divmod_uw_asm")
|
||||||
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
||||||
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[2].position, var2name)
|
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
|
||||||
val remainderVar = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
|
val remainderVar = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
|
||||||
assignAsmGen.assignRegisterpairWord(divisionTarget, RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(divisionTarget, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -196,7 +196,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
asmgen.assignExpressionToVariable(fcall.args[0], asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.forDt(BaseDataType.UWORD)) // jump address
|
asmgen.assignExpressionToVariable(fcall.args[0], asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD) // jump address
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
; push a return address so the jmp becomes indirect jsr
|
; push a return address so the jmp becomes indirect jsr
|
||||||
lda #>((+)-1)
|
lda #>((+)-1)
|
||||||
@ -227,6 +227,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
.word ${constAddress.toHex()}
|
.word ${constAddress.toHex()}
|
||||||
.byte $constBank""")
|
.byte $constBank""")
|
||||||
} else {
|
} else {
|
||||||
|
if(asmgen.options.romable)
|
||||||
|
TODO("no code for non-const callfar (jsrfar) yet that's usable in ROM ${fcall.position}")
|
||||||
|
// self-modifying code: set jsrfar arguments
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
|
||||||
asmgen.out(" sta (++)+0")
|
asmgen.out(" sta (++)+0")
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
|
||||||
@ -236,7 +239,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
jsr $jsrfar
|
jsr $jsrfar
|
||||||
+ .word 0
|
+ .word 0
|
||||||
+ .byte 0""")
|
+ .byte 0""")
|
||||||
asmgen.romableWarning("self-modifying code for jsrfar", fcall.position) // TODO
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// note that by convention the values in A+Y registers are now the return value of the call.
|
// note that by convention the values in A+Y registers are now the return value of the call.
|
||||||
@ -284,6 +286,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
.word ${constAddress.toHex()}
|
.word ${constAddress.toHex()}
|
||||||
.byte $constBank""")
|
.byte $constBank""")
|
||||||
} else {
|
} else {
|
||||||
|
if(asmgen.options.romable)
|
||||||
|
TODO("no code for non-const callfar2 (jsrfar) yet that's usable in ROM ${fcall.position}")
|
||||||
|
// self-modifying code: set jsrfar arguments
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
|
||||||
asmgen.out(" sta (++)+0")
|
asmgen.out(" sta (++)+0")
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
|
||||||
@ -293,7 +298,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
jsr $jsrfar
|
jsr $jsrfar
|
||||||
+ .word 0
|
+ .word 0
|
||||||
+ .byte 0""")
|
+ .byte 0""")
|
||||||
asmgen.romableWarning("self-modifying code for jsrfar", fcall.position) // TODO
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// note that by convention the values in A+Y registers are now the return value of the call.
|
// note that by convention the values in A+Y registers are now the return value of the call.
|
||||||
@ -376,11 +380,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
||||||
val name = (fcall.args[0] as PtString).value
|
val name = (fcall.args[0] as PtString).value
|
||||||
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
|
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
|
||||||
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.forDt(BaseDataType.UWORD), fcall.position)
|
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position)
|
||||||
val addressOf = PtAddressOf(fcall.position)
|
val addressOf = PtAddressOf(fcall.position)
|
||||||
addressOf.add(slabname)
|
addressOf.add(slabname)
|
||||||
addressOf.parent = fcall
|
addressOf.parent = fcall
|
||||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.forDt(BaseDataType.UWORD), expression = addressOf)
|
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
|
||||||
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||||
val assign = AsmAssignment(src, listOf(target), program.memsizer, fcall.position)
|
val assign = AsmAssignment(src, listOf(target), program.memsizer, fcall.position)
|
||||||
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||||
@ -465,31 +469,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" ror ${varname},x")
|
asmgen.out(" ror ${varname},x")
|
||||||
}
|
}
|
||||||
is PtMemoryByte -> {
|
is PtMemoryByte -> {
|
||||||
if (what.address is PtNumber) {
|
when {
|
||||||
val number = (what.address as PtNumber).number
|
what.address is PtNumber -> {
|
||||||
asmgen.out(" ror ${number.toHex()}")
|
val number = (what.address as PtNumber).number
|
||||||
} else {
|
asmgen.out(" ror ${number.toHex()}")
|
||||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
}
|
||||||
if(ptrAndIndex!=null) {
|
what.address is PtIdentifier -> {
|
||||||
asmgen.out(" php")
|
asmgen.out(" php") // save Carry
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
val sourceName = asmgen.loadByteFromPointerIntoA(what.address as PtIdentifier)
|
||||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
asmgen.out(" plp | ror a")
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
}
|
||||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
else -> {
|
||||||
asmgen.out("""
|
TODO("ror ptr-expression ${what.position}")
|
||||||
plp
|
|
||||||
+ ror ${'$'}ffff,x ; modified""")
|
|
||||||
asmgen.romableWarning("self-modifying code (ror)", fcall.position) // TODO
|
|
||||||
} else {
|
|
||||||
if(!what.address.isSimple()) asmgen.out(" php") // save Carry
|
|
||||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
|
||||||
if(!what.address.isSimple()) asmgen.out(" plp")
|
|
||||||
asmgen.out("""
|
|
||||||
sta (+) + 1
|
|
||||||
sty (+) + 2
|
|
||||||
+ ror ${'$'}ffff ; modified""")
|
|
||||||
asmgen.romableWarning("self-modifying code (ror)", fcall.position) // TODO
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -555,7 +547,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||||
val varname = asmgen.asmVariableName(what.variable)
|
val varname = asmgen.asmVariableName(what.variable)
|
||||||
if(what.splitWords)
|
if(what.splitWords)
|
||||||
asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb |+")
|
asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb,x |+")
|
||||||
else
|
else
|
||||||
asmgen.out(" asl ${varname},x | rol ${varname}+1,x | bcc + | inc ${varname},x |+ ")
|
asmgen.out(" asl ${varname},x | rol ${varname}+1,x | bcc + | inc ${varname},x |+ ")
|
||||||
}
|
}
|
||||||
@ -583,31 +575,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" rol ${varname},x")
|
asmgen.out(" rol ${varname},x")
|
||||||
}
|
}
|
||||||
is PtMemoryByte -> {
|
is PtMemoryByte -> {
|
||||||
if (what.address is PtNumber) {
|
when {
|
||||||
val number = (what.address as PtNumber).number
|
what.address is PtNumber -> {
|
||||||
asmgen.out(" rol ${number.toHex()}")
|
val number = (what.address as PtNumber).number
|
||||||
} else {
|
asmgen.out(" rol ${number.toHex()}")
|
||||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
}
|
||||||
if(ptrAndIndex!=null) {
|
what.address is PtIdentifier -> {
|
||||||
asmgen.out(" php")
|
asmgen.out(" php") // save Carry
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
val sourceName = asmgen.loadByteFromPointerIntoA(what.address as PtIdentifier)
|
||||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
asmgen.out(" plp | rol a")
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
}
|
||||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
else -> {
|
||||||
asmgen.out("""
|
TODO("rol ptr-expression ${what.position}")
|
||||||
plp
|
|
||||||
+ rol ${'$'}ffff,x ; modified""")
|
|
||||||
asmgen.romableWarning("self-modifying code (rol)", fcall.position) // TODO
|
|
||||||
} else {
|
|
||||||
if(!what.address.isSimple()) asmgen.out(" php") // save Carry
|
|
||||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
|
||||||
if(!what.address.isSimple()) asmgen.out(" plp")
|
|
||||||
asmgen.out("""
|
|
||||||
sta (+) + 1
|
|
||||||
sty (+) + 2
|
|
||||||
+ rol ${'$'}ffff ; modified""")
|
|
||||||
asmgen.romableWarning("self-modifying code (rol)", fcall.position) // TODO
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -646,7 +626,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
when(fcall.args[0]) {
|
when(fcall.args[0]) {
|
||||||
is PtIdentifier -> {
|
is PtIdentifier -> {
|
||||||
val varname = asmgen.asmVariableName(fcall.args[0] as PtIdentifier) + if(msb) "+1" else ""
|
val varname = asmgen.asmVariableName(fcall.args[0] as PtIdentifier) + if(msb) "+1" else ""
|
||||||
target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, variableAsmName = varname)
|
target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, variableAsmName = varname)
|
||||||
}
|
}
|
||||||
is PtArrayIndexer -> {
|
is PtArrayIndexer -> {
|
||||||
val indexer = fcall.args[0] as PtArrayIndexer
|
val indexer = fcall.args[0] as PtArrayIndexer
|
||||||
@ -688,7 +668,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
multipliedIndex.parent=indexer
|
multipliedIndex.parent=indexer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
target = AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, array = indexer)
|
target = AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, array = indexer)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("setlsb/setmsb on weird target ${fcall.args[0]}")
|
else -> throw AssemblyError("setlsb/setmsb on weird target ${fcall.args[0]}")
|
||||||
}
|
}
|
||||||
@ -696,7 +676,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
if(fcall.args[1].asConstInteger() == 0) {
|
if(fcall.args[1].asConstInteger() == 0) {
|
||||||
assignAsmGen.assignConstantByte(target, 0)
|
assignAsmGen.assignConstantByte(target, 0)
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||||
assignAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
assignAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -759,8 +739,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.assignConstFloatToPointerAY(number)
|
asmgen.assignConstFloatToPointerAY(number)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT)
|
val tempvar = asmgen.createTempVarReused(BaseDataType.FLOAT, false, fcall)
|
||||||
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.forDt(BaseDataType.FLOAT))
|
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.FLOAT)
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
pha
|
pha
|
||||||
@ -835,7 +815,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" jsr floats.MOVFM")
|
asmgen.out(" jsr floats.MOVFM")
|
||||||
if(resultRegister!=null) {
|
if(resultRegister!=null) {
|
||||||
assignAsmGen.assignFAC1float(
|
assignAsmGen.assignFAC1float(
|
||||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.FLOAT), fcall.definingISub(), fcall.position, null, null, null, resultRegister, null))
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1309,11 +1289,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
throw AssemblyError("cannot use float arguments outside of a subroutine scope")
|
throw AssemblyError("cannot use float arguments outside of a subroutine scope")
|
||||||
|
|
||||||
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
|
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
|
||||||
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.forDt(BaseDataType.FLOAT), value.position)
|
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position)
|
||||||
val addr = PtAddressOf(value.position)
|
val addr = PtAddressOf(value.position)
|
||||||
addr.add(variable)
|
addr.add(variable)
|
||||||
addr.parent = call
|
addr.parent = call
|
||||||
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.forDt(BaseDataType.FLOAT))
|
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
|
||||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,228 +33,351 @@ internal class ForLoopsAsmGen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForOverNonconstRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
private fun translateForOverNonconstRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
if(range.step.asConstInteger()!! < -1) {
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
|
||||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
|
||||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
|
||||||
asmgen.loopEndLabels.add(endLabel)
|
|
||||||
val stepsize=range.step.asConstInteger()!!
|
|
||||||
|
|
||||||
if(stepsize < -1) {
|
|
||||||
val limit = range.to.asConstInteger()
|
val limit = range.to.asConstInteger()
|
||||||
if(limit==0)
|
if(limit==0)
|
||||||
throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping")
|
throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping")
|
||||||
}
|
}
|
||||||
|
|
||||||
when {
|
when {
|
||||||
iterableDt.isByteArray -> {
|
iterableDt.isByteArray -> forOverNonconstByteRange(stmt, iterableDt, range)
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> forOverNonconstWordRange(stmt, iterableDt, range)
|
||||||
asmgen.assignExpressionToVariable(range.from, varname, iterableDt.elementType())
|
else -> throw AssemblyError("range expression can only be byte or word")
|
||||||
if (stepsize==-1 && range.to.asConstInteger()==0) {
|
}
|
||||||
// simple loop downto 0 step -1
|
|
||||||
asmgen.out(loopLabel)
|
asmgen.loopEndLabels.removeLast()
|
||||||
asmgen.translate(stmt.statements)
|
}
|
||||||
|
|
||||||
|
private fun forOverNonconstByteRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||||
|
val stepsize = range.step.asConstInteger()!!
|
||||||
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
|
asmgen.loopEndLabels.add(endLabel)
|
||||||
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
|
|
||||||
|
asmgen.assignExpressionToVariable(range.from, varname, iterableDt.elementType())
|
||||||
|
when (stepsize) {
|
||||||
|
-1 if range.to.asConstInteger()==0 -> {
|
||||||
|
// simple loop downto 0 step -1
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(stmt.statements)
|
||||||
|
asmgen.out("""
|
||||||
|
dec $varname
|
||||||
|
lda $varname
|
||||||
|
cmp #255
|
||||||
|
bne $loopLabel""")
|
||||||
|
}
|
||||||
|
-1 if range.to.asConstInteger()==1 -> {
|
||||||
|
// simple loop downto 1 step -1
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(stmt.statements)
|
||||||
|
asmgen.out("""
|
||||||
|
dec $varname
|
||||||
|
bne $loopLabel""")
|
||||||
|
}
|
||||||
|
1, -1 -> forOverBytesRangeStepOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||||
|
else -> forOverBytesRangeStepGreaterOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun forOverBytesRangeStepOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
|
||||||
|
// bytes range, step 1 or -1
|
||||||
|
|
||||||
|
val stepsize = range.step.asConstInteger()!!
|
||||||
|
val incdec = if(stepsize==1) "inc" else "dec"
|
||||||
|
|
||||||
|
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.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) {
|
||||||
|
if(stepsize<0) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
dec $varname
|
clc
|
||||||
lda $varname
|
sbc $varname
|
||||||
cmp #255
|
bvc +
|
||||||
bne $loopLabel""")
|
eor #$80
|
||||||
|
+ bpl $endLabel""")
|
||||||
}
|
}
|
||||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
else {
|
||||||
// simple loop downto 1 step -1
|
|
||||||
asmgen.out(loopLabel)
|
|
||||||
asmgen.translate(stmt.statements)
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
dec $varname
|
sec
|
||||||
bne $loopLabel""")
|
sbc $varname
|
||||||
|
bvc +
|
||||||
|
eor #$80
|
||||||
|
+ bmi $endLabel""")
|
||||||
}
|
}
|
||||||
else if (stepsize==1 || stepsize==-1) {
|
} else {
|
||||||
// bytes array, step 1 or -1
|
if(stepsize<0) {
|
||||||
|
|
||||||
val incdec = if(stepsize==1) "inc" else "dec"
|
|
||||||
// loop over byte range via loopvar
|
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
|
||||||
// pre-check for end already reached
|
|
||||||
if(iterableDt.isSignedByteArray) {
|
|
||||||
asmgen.out(" sta $modifiedLabel+1")
|
|
||||||
if(stepsize<0) {
|
|
||||||
asmgen.out("""
|
|
||||||
clc
|
|
||||||
sbc $varname
|
|
||||||
bvc +
|
|
||||||
eor #${'$'}80
|
|
||||||
+ bpl $endLabel""")
|
|
||||||
}
|
|
||||||
else
|
|
||||||
asmgen.out("""
|
|
||||||
sec
|
|
||||||
sbc $varname
|
|
||||||
bvc +
|
|
||||||
eor #${'$'}80
|
|
||||||
+ bmi $endLabel""")
|
|
||||||
} else {
|
|
||||||
if(stepsize<0) {
|
|
||||||
asmgen.out("""
|
|
||||||
cmp $varname
|
|
||||||
beq +
|
|
||||||
bcs $endLabel
|
|
||||||
+""")
|
|
||||||
}
|
|
||||||
else
|
|
||||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
|
||||||
asmgen.out(" sta $modifiedLabel+1")
|
|
||||||
}
|
|
||||||
asmgen.out(loopLabel)
|
|
||||||
asmgen.translate(stmt.statements)
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
cmp $varname
|
||||||
$modifiedLabel cmp #0 ; modified
|
beq +
|
||||||
beq $endLabel
|
bcs $endLabel
|
||||||
$incdec $varname""")
|
|
||||||
asmgen.romableWarning("self-modifying code (forloop over range)", stmt.position) // TODO
|
|
||||||
asmgen.jmp(loopLabel)
|
|
||||||
asmgen.out(endLabel)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// bytes, step >= 2 or <= -2
|
|
||||||
|
|
||||||
// loop over byte range via loopvar
|
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
|
||||||
// pre-check for end already reached
|
|
||||||
if(iterableDt.isSignedByteArray) {
|
|
||||||
asmgen.out(" sta $modifiedLabel+1")
|
|
||||||
if(stepsize<0)
|
|
||||||
asmgen.out("""
|
|
||||||
clc
|
|
||||||
sbc $varname
|
|
||||||
bvc +
|
|
||||||
eor #${'$'}80
|
|
||||||
+ bpl $endLabel""")
|
|
||||||
else
|
|
||||||
asmgen.out("""
|
|
||||||
sec
|
|
||||||
sbc $varname
|
|
||||||
bvc +
|
|
||||||
eor #${'$'}80
|
|
||||||
+ bmi $endLabel""")
|
|
||||||
} else {
|
|
||||||
if(stepsize<0)
|
|
||||||
asmgen.out("""
|
|
||||||
cmp $varname
|
|
||||||
beq +
|
|
||||||
bcs $endLabel
|
|
||||||
+""")
|
+""")
|
||||||
else
|
}
|
||||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
else {
|
||||||
asmgen.out(" sta $modifiedLabel+1")
|
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||||
}
|
|
||||||
asmgen.out(loopLabel)
|
|
||||||
asmgen.translate(stmt.statements)
|
|
||||||
if(stepsize>0) {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $varname
|
|
||||||
clc
|
|
||||||
adc #$stepsize
|
|
||||||
sta $varname
|
|
||||||
$modifiedLabel cmp #0 ; modified
|
|
||||||
bmi $loopLabel
|
|
||||||
beq $loopLabel""")
|
|
||||||
asmgen.romableWarning("self-modifying code (forloop over range)", stmt.position) // TODO
|
|
||||||
} else {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $varname
|
|
||||||
sec
|
|
||||||
sbc #${stepsize.absoluteValue}
|
|
||||||
sta $varname
|
|
||||||
$modifiedLabel cmp #0 ; modified
|
|
||||||
bpl $loopLabel""")
|
|
||||||
asmgen.romableWarning("self-modifying code (forloop over range)", stmt.position) // TODO
|
|
||||||
}
|
|
||||||
asmgen.out(endLabel)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> {
|
asmgen.out(loopLabel)
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
asmgen.translate(forloop.statements)
|
||||||
assignLoopvarWord(stmt, range)
|
asmgen.out("""
|
||||||
if(stepsize==-1 && range.to.asConstInteger()==0) {
|
lda $varname
|
||||||
// simple loop downto 0 step -1 (words)
|
cmp $toValueVar
|
||||||
asmgen.out(loopLabel)
|
beq $endLabel
|
||||||
asmgen.translate(stmt.statements)
|
$incdec $varname""")
|
||||||
|
asmgen.jmp(loopLabel)
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// use self-modifying code to store the loop end comparison value
|
||||||
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||||
|
// pre-check for end already reached
|
||||||
|
if(iterableDt.isSignedByteArray) {
|
||||||
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
|
if(stepsize<0) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
clc
|
||||||
bne ++
|
sbc $varname
|
||||||
lda $varname+1
|
bvc +
|
||||||
beq $endLabel
|
eor #$80
|
||||||
+ lda $varname
|
+ bpl $endLabel""")
|
||||||
bne +
|
|
||||||
dec $varname+1
|
|
||||||
+ dec $varname""")
|
|
||||||
asmgen.jmp(loopLabel)
|
|
||||||
asmgen.out(endLabel)
|
|
||||||
}
|
}
|
||||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
else
|
||||||
// simple loop downto 1 step -1 (words)
|
|
||||||
asmgen.out(loopLabel)
|
|
||||||
asmgen.translate(stmt.statements)
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
sec
|
||||||
cmp #1
|
sbc $varname
|
||||||
bne +
|
bvc +
|
||||||
lda $varname+1
|
eor #$80
|
||||||
beq $endLabel
|
+ bmi $endLabel""")
|
||||||
+ lda $varname
|
} else {
|
||||||
bne +
|
if(stepsize<0) {
|
||||||
dec $varname+1
|
asmgen.out("""
|
||||||
+ dec $varname""")
|
cmp $varname
|
||||||
asmgen.jmp(loopLabel)
|
beq +
|
||||||
asmgen.out(endLabel)
|
bcs $endLabel
|
||||||
|
+""")
|
||||||
}
|
}
|
||||||
else if (stepsize == 1 || stepsize == -1) {
|
else {
|
||||||
// words, step 1 or -1
|
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
}
|
||||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
asmgen.out("""
|
}
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(forloop.statements)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
$modifiedLabel cmp #0 ; modified
|
||||||
|
beq $endLabel
|
||||||
|
$incdec $varname""")
|
||||||
|
asmgen.jmp(loopLabel)
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun forOverBytesRangeStepGreaterOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
|
||||||
|
// bytes range, step >= 2 or <= -2
|
||||||
|
|
||||||
|
val stepsize = range.step.asConstInteger()!!
|
||||||
|
|
||||||
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||||
|
// pre-check for end already reached
|
||||||
|
if(iterableDt.isSignedByteArray) {
|
||||||
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
|
if(stepsize<0)
|
||||||
|
asmgen.out("""
|
||||||
|
clc
|
||||||
|
sbc $varname
|
||||||
|
bvc +
|
||||||
|
eor #$80
|
||||||
|
+ bpl $endLabel""")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
sec
|
||||||
|
sbc $varname
|
||||||
|
bvc +
|
||||||
|
eor #$80
|
||||||
|
+ bmi $endLabel""")
|
||||||
|
} else {
|
||||||
|
if(stepsize<0)
|
||||||
|
asmgen.out("""
|
||||||
|
cmp $varname
|
||||||
|
beq +
|
||||||
|
bcs $endLabel
|
||||||
|
+""")
|
||||||
|
else {
|
||||||
|
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||||
|
}
|
||||||
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
|
}
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(forloop.statements)
|
||||||
|
|
||||||
|
asmgen.romableError("self-modifying code (forloop over bytes range)", forloop.position) // TODO fix romable; there is self-modifying code below
|
||||||
|
if(stepsize>0) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
clc
|
||||||
|
adc #$stepsize
|
||||||
|
sta $varname
|
||||||
|
$modifiedLabel cmp #0 ; modified
|
||||||
|
bmi $loopLabel
|
||||||
|
beq $loopLabel""")
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
sec
|
||||||
|
sbc #${stepsize.absoluteValue}
|
||||||
|
sta $varname
|
||||||
|
$modifiedLabel cmp #0 ; modified
|
||||||
|
bpl $loopLabel""")
|
||||||
|
}
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun forOverNonconstWordRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||||
|
val stepsize = range.step.asConstInteger()!!
|
||||||
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
|
asmgen.loopEndLabels.add(endLabel)
|
||||||
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
|
assignLoopvarWord(stmt, range)
|
||||||
|
if(stepsize==-1 && range.to.asConstInteger()==0) {
|
||||||
|
// simple loop downto 0 step -1 (words)
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(stmt.statements)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
bne ++
|
||||||
|
lda $varname+1
|
||||||
|
beq $endLabel
|
||||||
|
+ lda $varname
|
||||||
|
bne +
|
||||||
|
dec $varname+1
|
||||||
|
+ dec $varname""")
|
||||||
|
asmgen.jmp(loopLabel)
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
}
|
||||||
|
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
||||||
|
// simple loop downto 1 step -1 (words)
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(stmt.statements)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
cmp #1
|
||||||
|
bne +
|
||||||
|
lda $varname+1
|
||||||
|
beq $endLabel
|
||||||
|
+ lda $varname
|
||||||
|
bne +
|
||||||
|
dec $varname+1
|
||||||
|
+ dec $varname""")
|
||||||
|
asmgen.jmp(loopLabel)
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
}
|
||||||
|
else if (stepsize == 1 || stepsize == -1)
|
||||||
|
forOverWordsRangeStepOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||||
|
else if (stepsize > 0)
|
||||||
|
forOverWordsRangeStepGreaterOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||||
|
else
|
||||||
|
forOverWordsRangeStepGreaterOneDescending(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun forOverWordsRangeStepOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
|
||||||
|
// words range, step 1 or -1
|
||||||
|
val stepsize = range.step.asConstInteger()!!
|
||||||
|
|
||||||
|
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.createTempVarReused(iterableDt.elementType().base, false, range)
|
||||||
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
|
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||||
|
asmgen.out(" sta $toValueVar")
|
||||||
|
asmgen.out(" sty $toValueVar+1")
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(forloop.statements)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname+1
|
||||||
|
cmp $toValueVar+1
|
||||||
|
bne +
|
||||||
|
lda $varname
|
||||||
|
cmp $toValueVar
|
||||||
|
beq $endLabel""")
|
||||||
|
if(stepsize==1) {
|
||||||
|
asmgen.out("""
|
||||||
|
+ inc $varname
|
||||||
|
bne $loopLabel
|
||||||
|
inc $varname+1""")
|
||||||
|
asmgen.jmp(loopLabel)
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
+ lda $varname
|
||||||
|
bne +
|
||||||
|
dec $varname+1
|
||||||
|
+ dec $varname""")
|
||||||
|
asmgen.jmp(loopLabel)
|
||||||
|
}
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
|
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||||
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
|
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||||
|
asmgen.out("""
|
||||||
sty $modifiedLabel+1
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(forloop.statements)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname+1
|
lda $varname+1
|
||||||
$modifiedLabel cmp #0 ; modified
|
$modifiedLabel cmp #0 ; modified
|
||||||
bne +
|
bne +
|
||||||
lda $varname
|
lda $varname
|
||||||
$modifiedLabel2 cmp #0 ; modified
|
$modifiedLabel2 cmp #0 ; modified
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
asmgen.romableWarning("self-modifying code (forloop over range)", stmt.position) // TODO
|
if(stepsize==1) {
|
||||||
if(stepsize==1) {
|
asmgen.out("""
|
||||||
asmgen.out("""
|
+ inc $varname
|
||||||
+ inc $varname
|
bne $loopLabel
|
||||||
bne $loopLabel
|
inc $varname+1""")
|
||||||
inc $varname+1""")
|
asmgen.jmp(loopLabel)
|
||||||
asmgen.jmp(loopLabel)
|
} else {
|
||||||
} else {
|
asmgen.out("""
|
||||||
asmgen.out("""
|
+ lda $varname
|
||||||
+ lda $varname
|
bne +
|
||||||
bne +
|
dec $varname+1
|
||||||
dec $varname+1
|
+ dec $varname""")
|
||||||
+ dec $varname""")
|
asmgen.jmp(loopLabel)
|
||||||
asmgen.jmp(loopLabel)
|
}
|
||||||
}
|
asmgen.out(endLabel)
|
||||||
asmgen.out(endLabel)
|
}
|
||||||
}
|
}
|
||||||
else if (stepsize > 0) {
|
|
||||||
// (u)words, step >= 2
|
private fun forOverWordsRangeStepGreaterOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, stmt: PtForLoop) {
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
// (u)words, step >= 2
|
||||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
val stepsize = range.step.asConstInteger()!!
|
||||||
asmgen.out("""
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
|
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||||
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
|
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||||
|
asmgen.out("""
|
||||||
sty $modifiedLabel+1
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
|
|
||||||
if (iterableDt.isUnsignedWordArray) {
|
asmgen.romableError("self-modifying code (forloop over word range)", stmt.position) // TODO fix romable; there is self-modifying code below
|
||||||
asmgen.out("""
|
if (iterableDt.isUnsignedWordArray) {
|
||||||
|
asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
clc
|
clc
|
||||||
adc #<$stepsize
|
adc #<$stepsize
|
||||||
@ -270,9 +393,8 @@ $modifiedLabel2 lda #0 ; modified
|
|||||||
bcc $endLabel
|
bcc $endLabel
|
||||||
bcs $loopLabel
|
bcs $loopLabel
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
asmgen.romableWarning("self-modifying code (forloop over range)", stmt.position) // TODO
|
} else {
|
||||||
} else {
|
asmgen.out("""
|
||||||
asmgen.out("""
|
|
||||||
lda $varname
|
lda $varname
|
||||||
clc
|
clc
|
||||||
adc #<$stepsize
|
adc #<$stepsize
|
||||||
@ -288,21 +410,22 @@ $modifiedLabel lda #0 ; modified
|
|||||||
eor #$80
|
eor #$80
|
||||||
+ bpl $loopLabel
|
+ bpl $loopLabel
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
asmgen.romableWarning("self-modifying code (forloop over range)", stmt.position) // TODO
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
// (u)words, step <= -2
|
private fun forOverWordsRangeStepGreaterOneDescending(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, stmt: PtForLoop) {
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
// (u)words, step <= -2
|
||||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
val stepsize = range.step.asConstInteger()!!
|
||||||
asmgen.out("""
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
|
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||||
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
|
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||||
|
asmgen.out("""
|
||||||
sty $modifiedLabel+1
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
|
asmgen.out("""
|
||||||
asmgen.out("""
|
|
||||||
lda $varname
|
lda $varname
|
||||||
sec
|
sec
|
||||||
sbc #<${stepsize.absoluteValue}
|
sbc #<${stepsize.absoluteValue}
|
||||||
@ -319,13 +442,7 @@ $modifiedLabel sbc #0 ; modified
|
|||||||
eor #$80
|
eor #$80
|
||||||
+ bpl $loopLabel
|
+ bpl $loopLabel
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
asmgen.romableWarning("self-modifying code (forloop over range)", stmt.position) // TODO
|
asmgen.romableError("self-modifying code (forloop over words range)", stmt.position) // TODO fix romable
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("range expression can only be byte or word")
|
|
||||||
}
|
|
||||||
|
|
||||||
asmgen.loopEndLabels.removeLast()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
|
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
|
||||||
@ -337,23 +454,23 @@ $endLabel""")
|
|||||||
sta P8ZP_SCRATCH_W2 ; to
|
sta P8ZP_SCRATCH_W2 ; to
|
||||||
sty P8ZP_SCRATCH_W2+1 ; to
|
sty P8ZP_SCRATCH_W2+1 ; to
|
||||||
lda $fromVar
|
lda $fromVar
|
||||||
cmp P8ZP_SCRATCH_W2
|
cmp P8ZP_SCRATCH_W2
|
||||||
lda $fromVar+1
|
lda $fromVar+1
|
||||||
sbc P8ZP_SCRATCH_W2+1
|
sbc P8ZP_SCRATCH_W2+1
|
||||||
bvc +
|
bvc +
|
||||||
eor #${'$'}80
|
eor #$80
|
||||||
+ bmi $endLabel
|
+ bmi $endLabel
|
||||||
lda P8ZP_SCRATCH_W2
|
lda P8ZP_SCRATCH_W2
|
||||||
ldy P8ZP_SCRATCH_W2+1""")
|
ldy P8ZP_SCRATCH_W2+1""")
|
||||||
else
|
else
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sta P8ZP_SCRATCH_REG
|
sta P8ZP_SCRATCH_REG
|
||||||
cmp $fromVar
|
cmp $fromVar
|
||||||
tya
|
tya
|
||||||
sbc $fromVar+1
|
sbc $fromVar+1
|
||||||
bvc +
|
bvc +
|
||||||
eor #${'$'}80
|
eor #$80
|
||||||
+ bmi $endLabel
|
+ bmi $endLabel
|
||||||
lda P8ZP_SCRATCH_REG""")
|
lda P8ZP_SCRATCH_REG""")
|
||||||
} else {
|
} else {
|
||||||
if(stepsize<0)
|
if(stepsize<0)
|
||||||
@ -369,11 +486,11 @@ $endLabel""")
|
|||||||
+""")
|
+""")
|
||||||
else
|
else
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
cpy $fromVar+1
|
cpy $fromVar+1
|
||||||
bcc $endLabel
|
bcc $endLabel
|
||||||
bne +
|
bne +
|
||||||
cmp $fromVar
|
cmp $fromVar
|
||||||
bcc $endLabel
|
bcc $endLabel
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -390,25 +507,42 @@ $endLabel""")
|
|||||||
}
|
}
|
||||||
when {
|
when {
|
||||||
iterableDt.isString -> {
|
iterableDt.isString -> {
|
||||||
asmgen.out("""
|
if(asmgen.options.romable) {
|
||||||
lda #<$iterableName
|
val indexVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||||
ldy #>$iterableName
|
asmgen.out("""
|
||||||
sta $loopLabel+1
|
ldy #0
|
||||||
sty $loopLabel+2
|
sty $indexVar
|
||||||
$loopLabel lda ${65535.toHex()} ; modified
|
$loopLabel lda $iterableName,y
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||||
asmgen.romableWarning("self-modifying code (forloop over iterable)", stmt.position) // TODO
|
asmgen.translate(stmt.statements)
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.out("""
|
||||||
asmgen.out("""
|
inc $indexVar
|
||||||
inc $loopLabel+1
|
ldy $indexVar
|
||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
inc $loopLabel+2
|
|
||||||
bne $loopLabel
|
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
|
} else {
|
||||||
|
val indexVar = asmgen.makeLabel("for_index")
|
||||||
|
asmgen.out("""
|
||||||
|
ldy #0
|
||||||
|
sty $indexVar
|
||||||
|
$loopLabel lda $iterableName,y
|
||||||
|
beq $endLabel
|
||||||
|
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||||
|
asmgen.translate(stmt.statements)
|
||||||
|
asmgen.out("""
|
||||||
|
inc $indexVar
|
||||||
|
ldy $indexVar
|
||||||
|
bne $loopLabel
|
||||||
|
$indexVar .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
iterableDt.isByteArray || iterableDt.isBoolArray -> {
|
iterableDt.isByteArray || iterableDt.isBoolArray -> {
|
||||||
val indexVar = asmgen.makeLabel("for_index")
|
val indexVar = if(asmgen.options.romable)
|
||||||
|
asmgen.createTempVarReused(iterableDt.elementType().base, false, stmt)
|
||||||
|
else
|
||||||
|
asmgen.makeLabel("for_index")
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
$loopLabel sty $indexVar
|
$loopLabel sty $indexVar
|
||||||
@ -430,20 +564,25 @@ $loopLabel sty $indexVar
|
|||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
}
|
}
|
||||||
if(numElements>=16) {
|
if(!asmgen.options.romable) {
|
||||||
// allocate index var on ZP if possible
|
if(numElements>=16) {
|
||||||
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors)
|
// allocate index var on ZP if possible, otherwise inline
|
||||||
result.fold(
|
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
result.fold(
|
||||||
failure = { asmgen.out("$indexVar .byte 0") }
|
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
)
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
} else {
|
)
|
||||||
asmgen.out("$indexVar .byte 0")
|
} else {
|
||||||
|
asmgen.out("$indexVar .byte 0")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
iterableDt.isSplitWordArray -> {
|
iterableDt.isSplitWordArray -> {
|
||||||
val indexVar = 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)
|
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
@ -468,21 +607,25 @@ $loopLabel sty $indexVar
|
|||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
}
|
}
|
||||||
if(numElements>=16) {
|
if(!asmgen.options.romable) {
|
||||||
// allocate index var on ZP if possible
|
if(numElements>=16) {
|
||||||
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors)
|
// allocate index var on ZP if possible, otherwise inline
|
||||||
result.fold(
|
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||||
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
result.fold(
|
||||||
failure = { asmgen.out("$indexVar .byte 0") }
|
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
)
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
} else {
|
)
|
||||||
asmgen.out("$indexVar .byte 0")
|
} else {
|
||||||
|
asmgen.out("$indexVar .byte 0")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
iterableDt.isWordArray -> {
|
iterableDt.isWordArray -> {
|
||||||
val length = numElements * 2
|
val indexVar = if(asmgen.options.romable)
|
||||||
val indexVar = asmgen.makeLabel("for_index")
|
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||||
|
else
|
||||||
|
asmgen.makeLabel("for_index")
|
||||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
@ -492,16 +635,16 @@ $loopLabel sty $indexVar
|
|||||||
lda $iterableName+1,y
|
lda $iterableName+1,y
|
||||||
sta $loopvarName+1""")
|
sta $loopvarName+1""")
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
if(length<=127) {
|
if(numElements<=127) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $indexVar
|
ldy $indexVar
|
||||||
iny
|
iny
|
||||||
iny
|
iny
|
||||||
cpy #$length
|
cpy #${numElements*2}
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
bne $loopLabel""")
|
bne $loopLabel""")
|
||||||
} else {
|
} else {
|
||||||
// length is 128 words, 256 bytes
|
// array size is 128 words, 256 bytes
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $indexVar
|
ldy $indexVar
|
||||||
iny
|
iny
|
||||||
@ -509,15 +652,17 @@ $loopLabel sty $indexVar
|
|||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
}
|
}
|
||||||
if(length>=16) {
|
if(!asmgen.options.romable) {
|
||||||
// allocate index var on ZP if possible
|
if(numElements>=16) {
|
||||||
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors)
|
// allocate index var on ZP if possible, otherwise inline
|
||||||
result.fold(
|
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||||
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
result.fold(
|
||||||
failure = { asmgen.out("$indexVar .byte 0") }
|
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
)
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
} else {
|
)
|
||||||
asmgen.out("$indexVar .byte 0")
|
} else {
|
||||||
|
asmgen.out("$indexVar .byte 0")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,10 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
if(bank==null) {
|
if(bank==null) {
|
||||||
val varbank = if(sub.address?.varbank==null) null else asmgen.asmVariableName(sub.address!!.varbank!!)
|
val varbank = if(sub.address?.varbank==null) null else asmgen.asmVariableName(sub.address!!.varbank!!)
|
||||||
if(varbank!=null) {
|
if(varbank!=null) {
|
||||||
|
if(asmgen.options.romable)
|
||||||
|
TODO("no codegen yet for non-const bank in subroutine call that's usable in ROM ${call.position}")
|
||||||
|
|
||||||
|
// self-modifying code: set jsrfar bank argument
|
||||||
when(asmgen.options.compTarget.name) {
|
when(asmgen.options.compTarget.name) {
|
||||||
"cx16" -> {
|
"cx16" -> {
|
||||||
// JSRFAR can jump to a banked RAM address as well!
|
// JSRFAR can jump to a banked RAM address as well!
|
||||||
@ -72,7 +76,6 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
||||||
+ .byte 0 ; modified"""
|
+ .byte 0 ; modified"""
|
||||||
)
|
)
|
||||||
asmgen.romableWarning("self-modifying code for cx16 banked jsr", call.position) // TODO
|
|
||||||
}
|
}
|
||||||
"c64" -> {
|
"c64" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -86,7 +89,6 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
||||||
+ .byte 0 ; modified"""
|
+ .byte 0 ; modified"""
|
||||||
)
|
)
|
||||||
asmgen.romableWarning("self-modifying code for c64 banked jsr", call.position) // TODO
|
|
||||||
}
|
}
|
||||||
"c128" -> {
|
"c128" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -100,7 +102,6 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
||||||
+ .byte 0 ; modified"""
|
+ .byte 0 ; modified"""
|
||||||
)
|
)
|
||||||
asmgen.romableWarning("self-modifying code for c128 banked jsr", call.position) // TODO
|
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("callfar is not supported on the selected compilation target")
|
else -> throw AssemblyError("callfar is not supported on the selected compilation target")
|
||||||
}
|
}
|
||||||
@ -233,9 +234,9 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
val param = sub.parameters[it]
|
val param = sub.parameters[it]
|
||||||
val arg = call.args[it]
|
val arg = call.args[it]
|
||||||
registersUsed += if(usesOtherRegistersWhileEvaluating(arg)) {
|
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)
|
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}")
|
throw AssemblyError("call argument evaluation problem: can't save cpu statusregister parameter ${call.position}")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -325,7 +326,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
register!!
|
register!!
|
||||||
if(requiredDt.largerSizeThan(value.type)) {
|
if(requiredDt.largerSizeThan(value.type)) {
|
||||||
// we need to sign extend the source, do this via temporary word variable
|
// we need to sign extend the source, do this via temporary word variable
|
||||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UBYTE))
|
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE)
|
||||||
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type.base)
|
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type.base)
|
||||||
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
|
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
|
||||||
} else {
|
} 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
|
// use a BIT instruction to test for bit 7 or 6 set/clear
|
||||||
val (testBitSet, variable, bitmask) = useBIT
|
val (testBitSet, variable, bitmask) = useBIT
|
||||||
return translateIfBIT(stmt, jumpAfterIf, testBitSet, variable, bitmask)
|
return translateIfBIT(stmt, jumpAfterIf, testBitSet, variable, bitmask)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val rightDt = compareCond.right.type
|
val rightDt = compareCond.right.type
|
||||||
@ -87,6 +86,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
if(testForBitSet) {
|
if(testForBitSet) {
|
||||||
if(jumpAfterIf!=null) {
|
if(jumpAfterIf!=null) {
|
||||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
require(!target.indexedX)
|
||||||
branch("bmi", target)
|
branch("bmi", target)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -94,6 +94,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
} else {
|
} else {
|
||||||
if(jumpAfterIf!=null) {
|
if(jumpAfterIf!=null) {
|
||||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
require(!target.indexedX)
|
||||||
branch("bpl", target)
|
branch("bpl", target)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -107,6 +108,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
if(testForBitSet) {
|
if(testForBitSet) {
|
||||||
if(jumpAfterIf!=null) {
|
if(jumpAfterIf!=null) {
|
||||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
require(!target.indexedX)
|
||||||
branch("bvs", target)
|
branch("bvs", target)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -114,6 +116,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
} else {
|
} else {
|
||||||
if(jumpAfterIf!=null) {
|
if(jumpAfterIf!=null) {
|
||||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
require(!target.indexedX)
|
||||||
branch("bvc", target)
|
branch("bvc", target)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -146,6 +149,93 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
translateIfElseBodies("beq", ifElse)
|
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) {
|
private fun translateIfElseBodies(elseBranchInstr: String, stmt: PtIfElse) {
|
||||||
// comparison value is already in A
|
// comparison value is already in A
|
||||||
val afterIfLabel = asmgen.makeLabel("afterif")
|
val afterIfLabel = asmgen.makeLabel("afterif")
|
||||||
@ -154,7 +244,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
val elseLabel = asmgen.makeLabel("else")
|
val elseLabel = asmgen.makeLabel("else")
|
||||||
asmgen.out(" $elseBranchInstr $elseLabel")
|
asmgen.out(" $elseBranchInstr $elseLabel")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -171,10 +261,9 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
if(target.indirect) {
|
if(target.indirect) {
|
||||||
asmgen.out(" $falseBranch +")
|
asmgen.out(" $falseBranch +")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
asmgen.out("""
|
asmgen.jmp(target.asmLabel, target.indirect, target.indexedX)
|
||||||
jmp (${target.asmLabel})
|
asmgen.out("+")
|
||||||
+""")
|
|
||||||
} else {
|
} else {
|
||||||
require(!target.needsExpressionEvaluation)
|
require(!target.needsExpressionEvaluation)
|
||||||
asmgen.out(" $branchInstr ${target.asmLabel}")
|
asmgen.out(" $branchInstr ${target.asmLabel}")
|
||||||
@ -210,40 +299,11 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
translateIfElseBodies("beq", stmt)
|
translateIfElseBodies("beq", stmt)
|
||||||
}
|
}
|
||||||
"<" -> translateByteLess(stmt, signed, jumpAfterIf)
|
"<" -> translateByteLess(stmt, signed, jumpAfterIf)
|
||||||
"<=" -> {
|
"<=" -> translateByteLessEqual(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
">" -> translateByteGreater(stmt, signed, jumpAfterIf)
|
">" -> translateByteGreater(stmt, signed, jumpAfterIf)
|
||||||
">=" -> {
|
">=" -> translateByteGreaterEqual(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
in LogicalOperators -> {
|
in LogicalOperators -> {
|
||||||
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.BOOL), stmt.definingISub(), condition.position, register=RegisterOrPair.A)
|
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.BOOL, stmt.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||||
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
|
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
|
||||||
if (jumpAfterIf != null)
|
if (jumpAfterIf != null)
|
||||||
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
|
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
|
||||||
@ -286,7 +346,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
if(target.indirect) {
|
if(target.indirect) {
|
||||||
asmgen.out(" bmi + | beq +")
|
asmgen.out(" bmi + | beq +")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jmp (${target.asmLabel})
|
jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -306,7 +367,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
val elseLabel = asmgen.makeLabel("else")
|
val elseLabel = asmgen.makeLabel("else")
|
||||||
asmgen.out(" bmi $elseLabel | beq $elseLabel")
|
asmgen.out(" bmi $elseLabel | beq $elseLabel")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -353,7 +414,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
if(target.indirect) {
|
if(target.indirect) {
|
||||||
asmgen.out(" bmi + | bne ++")
|
asmgen.out(" bmi + | bne ++")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ jmp (${target.asmLabel})
|
+ jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -375,7 +437,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
bpl $elseLabel
|
bpl $elseLabel
|
||||||
+""")
|
+""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -402,13 +464,13 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
private fun translateByteLess(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
private fun translateByteLess(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||||
val condition = stmt.condition as PtBinaryExpression
|
val condition = stmt.condition as PtBinaryExpression
|
||||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
||||||
asmgen.cmpAwithByteValue(condition.right, false)
|
|
||||||
if(signed) {
|
if(signed) {
|
||||||
if(jumpAfterIf!=null)
|
if(jumpAfterIf!=null)
|
||||||
translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope)
|
translateJumpElseBodiesSignedByte("<", condition.right, jumpAfterIf, stmt.elseScope)
|
||||||
else
|
else
|
||||||
translateIfElseBodies("bpl", stmt)
|
translateIfElseBodiesSignedByte("<", condition.right, stmt)
|
||||||
} else {
|
} else {
|
||||||
|
asmgen.cmpAwithByteValue(condition.right, false)
|
||||||
if(jumpAfterIf!=null)
|
if(jumpAfterIf!=null)
|
||||||
translateJumpElseBodies("bcc", "bcs", jumpAfterIf, stmt.elseScope)
|
translateJumpElseBodies("bcc", "bcs", jumpAfterIf, stmt.elseScope)
|
||||||
else
|
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?) {
|
private fun translateByteGreater(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||||
val condition = stmt.condition as PtBinaryExpression
|
val condition = stmt.condition as PtBinaryExpression
|
||||||
if(signed) {
|
if(signed) {
|
||||||
// X>Y --> Y<X
|
// X>Y --> Y<X
|
||||||
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, true)
|
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, true)
|
||||||
asmgen.cmpAwithByteValue(condition.left, true)
|
|
||||||
if (jumpAfterIf != null)
|
if (jumpAfterIf != null)
|
||||||
translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope)
|
translateJumpElseBodiesSignedByte("<", condition.left, jumpAfterIf, stmt.elseScope)
|
||||||
else
|
else
|
||||||
translateIfElseBodies("bpl", stmt)
|
translateIfElseBodiesSignedByte("<", condition.left, stmt)
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A)
|
||||||
asmgen.cmpAwithByteValue(condition.right, false)
|
asmgen.cmpAwithByteValue(condition.right, false)
|
||||||
if(jumpAfterIf!=null) {
|
if(jumpAfterIf!=null) {
|
||||||
var target = asmgen.getJumpTarget(jumpAfterIf, false)
|
var target = asmgen.getJumpTarget(jumpAfterIf, false)
|
||||||
if(target.indirect) {
|
if(target.indirect) {
|
||||||
asmgen.out(" bcc + | beq +")
|
asmgen.out(" bcc + | beq +")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jmp (${target.asmLabel})
|
jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -453,7 +533,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
val elseLabel = asmgen.makeLabel("else")
|
val elseLabel = asmgen.makeLabel("else")
|
||||||
asmgen.out(" bcc $elseLabel | beq $elseLabel")
|
asmgen.out(" bcc $elseLabel | beq $elseLabel")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} 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?) {
|
private fun translateIfWord(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
|
||||||
val signed = condition.left.type.isSigned
|
val signed = condition.left.type.isSigned
|
||||||
val constValue = condition.right.asConstInteger()
|
val constValue = condition.right.asConstInteger()
|
||||||
@ -535,7 +632,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
eor #128
|
eor #128
|
||||||
+ bpl +""")
|
+ bpl +""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jmp (${target.asmLabel})
|
jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -562,7 +660,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
eor #128
|
eor #128
|
||||||
+ bmi $elseLabel""")
|
+ bmi $elseLabel""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -590,7 +688,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
|||||||
cmp $valueLsb
|
cmp $valueLsb
|
||||||
bcs +""")
|
bcs +""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
_jump jmp (${target.asmLabel})
|
_jump jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -615,7 +714,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
sbc $valueMsb
|
sbc $valueMsb
|
||||||
bcs $elseLabel""")
|
bcs $elseLabel""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -667,7 +766,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
eor #128
|
eor #128
|
||||||
+ bpl +""")
|
+ bpl +""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jmp (${target.asmLabel})
|
jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -694,7 +794,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
eor #128
|
eor #128
|
||||||
+ bmi $elseLabel""")
|
+ bmi $elseLabel""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -721,7 +821,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
sbc $valueMsb
|
sbc $valueMsb
|
||||||
bcc +""")
|
bcc +""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jmp (${target.asmLabel})
|
jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -744,7 +845,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
sbc $valueMsb
|
sbc $valueMsb
|
||||||
bcc $elseLabel""")
|
bcc $elseLabel""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -829,7 +930,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
lda $valueLsb
|
lda $valueLsb
|
||||||
bne ++""")
|
bne ++""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ jmp (${target.asmLabel})
|
+ jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -857,7 +959,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
bne $elseLabel
|
bne $elseLabel
|
||||||
+""")
|
+""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -908,7 +1010,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
cmp #0
|
cmp #0
|
||||||
bne ++""")
|
bne ++""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ jmp (${target.asmLabel})
|
+ jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -936,7 +1039,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
bne $elseLabel
|
bne $elseLabel
|
||||||
+""")
|
+""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -973,7 +1076,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
lda $valueLsb
|
lda $valueLsb
|
||||||
beq ++""")
|
beq ++""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ jmp (${target.asmLabel})
|
+ jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -1001,7 +1105,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
beq $elseLabel
|
beq $elseLabel
|
||||||
+""")
|
+""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -1053,7 +1157,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
cmp #0
|
cmp #0
|
||||||
beq ++""")
|
beq ++""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ jmp (${target.asmLabel})
|
+ jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -1081,7 +1186,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
beq $elseLabel
|
beq $elseLabel
|
||||||
+""")
|
+""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -1196,7 +1301,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
cpy $valueMsb
|
cpy $valueMsb
|
||||||
beq ++""")
|
beq ++""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ jmp (${target.asmLabel})
|
+ jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -1221,7 +1327,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
beq $elseLabel
|
beq $elseLabel
|
||||||
+""")
|
+""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -1248,7 +1354,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
cpy $valueMsb
|
cpy $valueMsb
|
||||||
bne +""")
|
bne +""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jmp (${target.asmLabel})
|
jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -1273,7 +1380,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
cpy $valueMsb
|
cpy $valueMsb
|
||||||
bne $elseLabel""")
|
bne $elseLabel""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -1302,7 +1409,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
cmp ${right.name}+1
|
cmp ${right.name}+1
|
||||||
beq ++""")
|
beq ++""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ jmp (${target.asmLabel})
|
+ jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -1331,7 +1439,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
beq $elseLabel
|
beq $elseLabel
|
||||||
+""")
|
+""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -1361,7 +1469,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
cmp ${right.name}+1
|
cmp ${right.name}+1
|
||||||
bne +""")
|
bne +""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jmp (${target.asmLabel})
|
jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -1390,7 +1499,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
cmp ${right.name}+1
|
cmp ${right.name}+1
|
||||||
bne $elseLabel""")
|
bne $elseLabel""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -1423,7 +1532,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
cmp #>$value
|
cmp #>$value
|
||||||
beq ++""")
|
beq ++""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ jmp (${target.asmLabel})
|
+ jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -1452,7 +1562,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
beq $elseLabel
|
beq $elseLabel
|
||||||
+""")
|
+""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
@ -1481,7 +1591,8 @@ _jump jmp (${target.asmLabel})
|
|||||||
cmp #>$value
|
cmp #>$value
|
||||||
bne +""")
|
bne +""")
|
||||||
if(target.needsExpressionEvaluation)
|
if(target.needsExpressionEvaluation)
|
||||||
target = asmgen.getJumpTarget(jump, true)
|
target = asmgen.getJumpTarget(jump)
|
||||||
|
require(!target.indexedX)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jmp (${target.asmLabel})
|
jmp (${target.asmLabel})
|
||||||
+""")
|
+""")
|
||||||
@ -1510,7 +1621,7 @@ _jump jmp (${target.asmLabel})
|
|||||||
cmp #>$value
|
cmp #>$value
|
||||||
bne $elseLabel""")
|
bne $elseLabel""")
|
||||||
asmgen.translate(stmt.ifScope)
|
asmgen.translate(stmt.ifScope)
|
||||||
asmgen.jmp(afterIfLabel, false)
|
asmgen.jmp(afterIfLabel)
|
||||||
asmgen.out(elseLabel)
|
asmgen.out(elseLabel)
|
||||||
asmgen.translate(stmt.elseScope)
|
asmgen.translate(stmt.elseScope)
|
||||||
} else {
|
} else {
|
||||||
|
@ -15,18 +15,18 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
|
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
|
||||||
when {
|
when {
|
||||||
expr.type.isByteOrBool -> {
|
expr.type.isByteOrBool -> {
|
||||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
|
||||||
asmgen.jmp(endLabel)
|
asmgen.jmp(endLabel)
|
||||||
asmgen.out(falseLabel)
|
asmgen.out(falseLabel)
|
||||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
||||||
}
|
}
|
||||||
expr.type.isWord || expr.type.isString -> {
|
expr.type.isWord || expr.type.isString -> {
|
||||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY, false)
|
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
|
||||||
asmgen.jmp(endLabel)
|
asmgen.jmp(endLabel)
|
||||||
asmgen.out(falseLabel)
|
asmgen.out(falseLabel)
|
||||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY, false)
|
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
@ -43,21 +43,26 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) {
|
private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) {
|
||||||
if (condition is PtBinaryExpression) {
|
when (condition) {
|
||||||
val rightDt = condition.right.type
|
is PtBinaryExpression -> {
|
||||||
return when {
|
val rightDt = condition.right.type
|
||||||
rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
|
return when {
|
||||||
rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
|
rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
|
||||||
rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel)
|
rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
|
||||||
else -> throw AssemblyError("weird dt")
|
rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel)
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is PtPrefix if condition.operator=="not" -> {
|
||||||
|
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
|
||||||
|
asmgen.assignConditionValueToRegisterAndTest(condition)
|
||||||
|
asmgen.out(" beq $falseLabel")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if(condition is PtPrefix && condition.operator=="not") {
|
|
||||||
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
|
|
||||||
} else {
|
|
||||||
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
|
|
||||||
asmgen.assignConditionValueToRegisterAndTest(condition)
|
|
||||||
asmgen.out(" beq $falseLabel")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +87,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
asmgen.out(" beq $falseLabel")
|
asmgen.out(" beq $falseLabel")
|
||||||
}
|
}
|
||||||
in LogicalOperators -> {
|
in LogicalOperators -> {
|
||||||
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.BOOL), condition.definingISub(), condition.position, register=RegisterOrPair.A)
|
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.BOOL, condition.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||||
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
|
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
|
||||||
asmgen.out(" beq $falseLabel")
|
asmgen.out(" beq $falseLabel")
|
||||||
} else {
|
} else {
|
||||||
@ -190,7 +195,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
beq $falseLabel
|
beq $falseLabel
|
||||||
+""")
|
+""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
cmp $varRight
|
cmp $varRight
|
||||||
bne +
|
bne +
|
||||||
@ -214,7 +219,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
cmp $varRight+1
|
cmp $varRight+1
|
||||||
bne $falseLabel""")
|
bne $falseLabel""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
cmp $varRight
|
cmp $varRight
|
||||||
bne $falseLabel
|
bne $falseLabel
|
||||||
@ -237,7 +242,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
beq $falseLabel
|
beq $falseLabel
|
||||||
+""")
|
+""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
cmp #<$number
|
cmp #<$number
|
||||||
bne +
|
bne +
|
||||||
@ -260,7 +265,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
cmp #>$number
|
cmp #>$number
|
||||||
bne $falseLabel""")
|
bne $falseLabel""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
asmgen.out( """
|
asmgen.out( """
|
||||||
cmp #<$number
|
cmp #<$number
|
||||||
bne $falseLabel
|
bne $falseLabel
|
||||||
@ -279,7 +284,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
ora $varname+1
|
ora $varname+1
|
||||||
beq $falseLabel""")
|
beq $falseLabel""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel")
|
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -294,7 +299,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
|||||||
ora $varname+1
|
ora $varname+1
|
||||||
bne $falseLabel""")
|
bne $falseLabel""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||||
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel")
|
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,6 @@ internal class ProgramAndVarsGen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
memorySlabs()
|
memorySlabs()
|
||||||
tempVars()
|
|
||||||
footer()
|
footer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,27 +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}")
|
|
||||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
asmgen.out(" .send BSS")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun footer() {
|
private fun footer() {
|
||||||
var relocateBssVars = false
|
var relocateBssVars = false
|
||||||
var relocateBssSlabs = false
|
var relocateBssSlabs = false
|
||||||
@ -299,6 +277,7 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" .dsection slabs_BSS")
|
asmgen.out(" .dsection slabs_BSS")
|
||||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||||
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
||||||
|
asmgen.out(" .dsection BSS_NOCLEAR")
|
||||||
asmgen.out("prog8_bss_section_start")
|
asmgen.out("prog8_bss_section_start")
|
||||||
asmgen.out(" .dsection BSS")
|
asmgen.out(" .dsection BSS")
|
||||||
if(relocateBssSlabs)
|
if(relocateBssSlabs)
|
||||||
@ -306,6 +285,7 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
|
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
|
||||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||||
} else {
|
} else {
|
||||||
|
asmgen.out(" .dsection BSS_NOCLEAR")
|
||||||
asmgen.out("prog8_bss_section_start")
|
asmgen.out("prog8_bss_section_start")
|
||||||
asmgen.out(" .dsection BSS")
|
asmgen.out(" .dsection BSS")
|
||||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||||
@ -318,6 +298,10 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
|
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(relocatedBssEnd >= options.memtopAddress)
|
||||||
|
options.memtopAddress = relocatedBssEnd+1u
|
||||||
|
|
||||||
asmgen.out(" ; memtop check")
|
asmgen.out(" ; memtop check")
|
||||||
asmgen.out(" .cerror * >= ${options.memtopAddress.toHex()}, \"Program too long by \", * - ${(options.memtopAddress-1u).toHex()}, \" bytes, memtop=${options.memtopAddress.toHex()}\"")
|
asmgen.out(" .cerror * >= ${options.memtopAddress.toHex()}, \"Program too long by \", * - ${(options.memtopAddress-1u).toHex()}, \" bytes, memtop=${options.memtopAddress.toHex()}\"")
|
||||||
}
|
}
|
||||||
@ -346,7 +330,9 @@ internal class ProgramAndVarsGen(
|
|||||||
initializers.forEach { assign ->
|
initializers.forEach { assign ->
|
||||||
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
|
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
|
||||||
asmgen.translate(assign)
|
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")
|
asmgen.out(" rts\n .bend")
|
||||||
}
|
}
|
||||||
@ -485,14 +471,14 @@ internal class ProgramAndVarsGen(
|
|||||||
sub.children.forEach { asmgen.translate(it) }
|
sub.children.forEach { asmgen.translate(it) }
|
||||||
|
|
||||||
asmgen.out("; variables")
|
asmgen.out("; variables")
|
||||||
asmgen.out(" .section BSS")
|
asmgen.out(" .section BSS_NOCLEAR") // these extra vars are initialized before use
|
||||||
val asmGenInfo = asmgen.subroutineExtra(sub)
|
val asmGenInfo = asmgen.subroutineExtra(sub)
|
||||||
for((dt, name, addr) in asmGenInfo.extraVars) {
|
for((dt, name, addr) in asmGenInfo.extraVars) {
|
||||||
if(addr!=null)
|
if(addr!=null)
|
||||||
asmgen.out("$name = $addr")
|
asmgen.out("$name = $addr")
|
||||||
else when(dt) {
|
else when(dt) {
|
||||||
BaseDataType.UBYTE -> asmgen.out("$name .byte ?")
|
BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL -> asmgen.out("$name .byte ?")
|
||||||
BaseDataType.UWORD -> asmgen.out("$name .word ?")
|
BaseDataType.UWORD, BaseDataType.WORD -> asmgen.out("$name .word ?")
|
||||||
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||||
}
|
}
|
||||||
@ -501,7 +487,7 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||||
if(asmGenInfo.usedFloatEvalResultVar2)
|
if(asmGenInfo.usedFloatEvalResultVar2)
|
||||||
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||||
asmgen.out(" .send BSS")
|
asmgen.out(" .send BSS_NOCLEAR")
|
||||||
|
|
||||||
// normal statically allocated variables
|
// normal statically allocated variables
|
||||||
val variables = varsInSubroutine
|
val variables = varsInSubroutine
|
||||||
@ -629,15 +615,30 @@ internal class ProgramAndVarsGen(
|
|||||||
val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized }
|
val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized }
|
||||||
if(varsNoInit.isNotEmpty()) {
|
if(varsNoInit.isNotEmpty()) {
|
||||||
asmgen.out("; non-zeropage variables")
|
asmgen.out("; non-zeropage variables")
|
||||||
asmgen.out(" .section BSS")
|
val (dirty, clean) = varsNoInit.partition { it.dirty }
|
||||||
val (notAligned, aligned) = varsNoInit.partition { it.align==0 }
|
|
||||||
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
|
fun generate(section: String, variables: List<StStaticVariable>) {
|
||||||
uninitializedVariable2asm(it)
|
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()) {
|
if(varsWithInit.isNotEmpty()) {
|
||||||
@ -652,7 +653,7 @@ internal class ProgramAndVarsGen(
|
|||||||
it.initializationStringValue!!.second,
|
it.initializationStringValue!!.second,
|
||||||
it.initializationStringValue!!.first
|
it.initializationStringValue!!.first
|
||||||
)
|
)
|
||||||
asmgen.romableWarning("inlined variable (${it.dt}, ${it.name})", Position.DUMMY) // TODO
|
asmgen.romableError("string (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||||
}
|
}
|
||||||
alignedStrings.sortedBy { it.align }.forEach {
|
alignedStrings.sortedBy { it.align }.forEach {
|
||||||
outputStringvar(
|
outputStringvar(
|
||||||
@ -661,16 +662,22 @@ internal class ProgramAndVarsGen(
|
|||||||
it.initializationStringValue!!.second,
|
it.initializationStringValue!!.second,
|
||||||
it.initializationStringValue!!.first
|
it.initializationStringValue!!.first
|
||||||
)
|
)
|
||||||
asmgen.romableWarning("inlined variable (${it.dt}, ${it.name})", Position.DUMMY) // TODO
|
asmgen.romableError("string (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||||
}
|
}
|
||||||
|
|
||||||
notAlignedOther.sortedBy { it.type }.forEach {
|
notAlignedOther.sortedBy { it.type }.forEach {
|
||||||
staticVariable2asm(it)
|
staticVariable2asm(it)
|
||||||
asmgen.romableWarning("inlined variable (${it.dt}, ${it.name})", Position.DUMMY) // TODO
|
if(it.dt.isArray)
|
||||||
|
asmgen.romableError("array (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||||
|
else
|
||||||
|
asmgen.romableError("inlined variable (${it.dt} ${it.name})", Position.DUMMY) // TODO print warning with position of the var
|
||||||
}
|
}
|
||||||
alignedOther.sortedBy { it.align }.sortedBy { it.type }.forEach {
|
alignedOther.sortedBy { it.align }.sortedBy { it.type }.forEach {
|
||||||
staticVariable2asm(it)
|
staticVariable2asm(it)
|
||||||
asmgen.romableWarning("inlined variable (${it.dt}, ${it.name})", Position.DUMMY) // TODO
|
if(it.dt.isArray)
|
||||||
|
asmgen.romableError("array (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||||
|
else
|
||||||
|
asmgen.romableError("inlined variable (${it.dt} ${it.name})", Position.DUMMY) // TODO print warning with position of the var
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,7 @@ package prog8.codegen.cpu6502
|
|||||||
|
|
||||||
import com.github.michaelbull.result.fold
|
import com.github.michaelbull.result.fold
|
||||||
import com.github.michaelbull.result.onSuccess
|
import com.github.michaelbull.result.onSuccess
|
||||||
import prog8.code.StNode
|
import prog8.code.*
|
||||||
import prog8.code.StNodeType
|
|
||||||
import prog8.code.StStaticVariable
|
|
||||||
import prog8.code.SymbolTable
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
@ -23,7 +20,13 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
zeropageVars = zeropage.allocatedVariables
|
zeropageVars = zeropage.allocatedVariables
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun isZpVar(scopedName: String) = scopedName in zeropageVars
|
internal fun isZpVar(scopedName: String): Boolean {
|
||||||
|
if(scopedName in zeropageVars)
|
||||||
|
return true
|
||||||
|
|
||||||
|
val v = symboltable.lookup(scopedName)
|
||||||
|
return if(v is StMemVar) v.address <= 255u else false
|
||||||
|
}
|
||||||
|
|
||||||
internal fun getFloatAsmConst(number: Double): String {
|
internal fun getFloatAsmConst(number: Double): String {
|
||||||
val asmName = globalFloatConsts[number]
|
val asmName = globalFloatConsts[number]
|
||||||
|
@ -2,7 +2,10 @@ package prog8.codegen.cpu6502.assignment
|
|||||||
|
|
||||||
import prog8.code.ast.PtBinaryExpression
|
import prog8.code.ast.PtBinaryExpression
|
||||||
import prog8.code.ast.PtExpression
|
import prog8.code.ast.PtExpression
|
||||||
import prog8.code.core.*
|
import prog8.code.core.AssemblyError
|
||||||
|
import prog8.code.core.ComparisonOperators
|
||||||
|
import prog8.code.core.DataType
|
||||||
|
import prog8.code.core.RegisterOrPair
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
import prog8.code.target.Cx16Target
|
import prog8.code.target.Cx16Target
|
||||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||||
@ -50,17 +53,17 @@ internal class AnyExprAsmGen(
|
|||||||
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||||
when(expr.operator) {
|
when(expr.operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
|
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
|
||||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
|
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
|
||||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||||
return true
|
return true
|
||||||
@ -73,25 +76,25 @@ internal class AnyExprAsmGen(
|
|||||||
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
|
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
|
||||||
"or" -> TODO("logical or (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.out(" pha")
|
||||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
|
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
|
||||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
"|" -> {
|
"|" -> {
|
||||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
|
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
|
||||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
"^", "xor" -> {
|
"^", "xor" -> {
|
||||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
|
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
|
||||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||||
return true
|
return true
|
||||||
@ -197,7 +200,7 @@ internal class AnyExprAsmGen(
|
|||||||
private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) {
|
private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) {
|
||||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
||||||
if(!expr.right.isSimple()) asmgen.pushFAC1()
|
if(!expr.right.isSimple()) asmgen.pushFAC1()
|
||||||
asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.forDt(BaseDataType.FLOAT))
|
asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.FLOAT)
|
||||||
if(!expr.right.isSimple()) asmgen.popFAC1()
|
if(!expr.right.isSimple()) asmgen.popFAC1()
|
||||||
asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var")
|
asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var")
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
fun fromAstAssignmentMulti(targets: List<PtAssignTarget>, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): List<AsmAssignTarget> {
|
fun fromAstAssignmentMulti(targets: List<PtAssignTarget>, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): List<AsmAssignTarget> {
|
||||||
return targets.map {
|
return targets.map {
|
||||||
if(it.void)
|
if(it.void)
|
||||||
AsmAssignTarget(TargetStorageKind.VOID, asmgen, DataType.forDt(BaseDataType.UNDEFINED), null, it.position)
|
AsmAssignTarget(TargetStorageKind.VOID, asmgen, DataType.UNDEFINED, null, it.position)
|
||||||
else
|
else
|
||||||
fromAstAssignment(it, definingSub, asmgen)
|
fromAstAssignment(it, definingSub, asmgen)
|
||||||
}
|
}
|
||||||
@ -87,18 +87,18 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
RegisterOrPair.A,
|
RegisterOrPair.A,
|
||||||
RegisterOrPair.X,
|
RegisterOrPair.X,
|
||||||
RegisterOrPair.Y -> {
|
RegisterOrPair.Y -> {
|
||||||
val dt = DataType.forDt(if(signed) BaseDataType.BYTE else BaseDataType.UBYTE)
|
val dt = if(signed) DataType.BYTE else DataType.UBYTE
|
||||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||||
}
|
}
|
||||||
RegisterOrPair.AX,
|
RegisterOrPair.AX,
|
||||||
RegisterOrPair.AY,
|
RegisterOrPair.AY,
|
||||||
RegisterOrPair.XY -> {
|
RegisterOrPair.XY -> {
|
||||||
val dt = DataType.forDt(if(signed) BaseDataType.WORD else BaseDataType.UWORD)
|
val dt = if(signed) DataType.WORD else DataType.UWORD
|
||||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||||
}
|
}
|
||||||
RegisterOrPair.FAC1,
|
RegisterOrPair.FAC1,
|
||||||
RegisterOrPair.FAC2 -> {
|
RegisterOrPair.FAC2 -> {
|
||||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.FLOAT), scope, pos, register = registers)
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
|
||||||
}
|
}
|
||||||
RegisterOrPair.R0,
|
RegisterOrPair.R0,
|
||||||
RegisterOrPair.R1,
|
RegisterOrPair.R1,
|
||||||
@ -116,7 +116,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
RegisterOrPair.R13,
|
RegisterOrPair.R13,
|
||||||
RegisterOrPair.R14,
|
RegisterOrPair.R14,
|
||||||
RegisterOrPair.R15 -> {
|
RegisterOrPair.R15 -> {
|
||||||
val dt = DataType.forDt(if(signed) BaseDataType.WORD else BaseDataType.UWORD)
|
val dt = if(signed) DataType.WORD else DataType.UWORD
|
||||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,7 +170,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
||||||
val bv = value as? PtBool
|
val bv = value as? PtBool
|
||||||
if(bv!=null)
|
if(bv!=null)
|
||||||
return AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.forDt(BaseDataType.BOOL), boolean = bv)
|
return AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.BOOL, boolean = bv)
|
||||||
|
|
||||||
return when(value) {
|
return when(value) {
|
||||||
// checked above: is PtNumber -> throw AssemblyError("should have been constant value")
|
// checked above: is PtNumber -> throw AssemblyError("should have been constant value")
|
||||||
@ -191,7 +191,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is PtMemoryByte -> {
|
is PtMemoryByte -> {
|
||||||
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.forDt(BaseDataType.UBYTE), memory = value)
|
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
||||||
}
|
}
|
||||||
is PtArrayIndexer -> {
|
is PtArrayIndexer -> {
|
||||||
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value)
|
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value)
|
||||||
@ -204,7 +204,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
val sub = symbol.astNode as IPtSubroutine
|
val sub = symbol.astNode as IPtSubroutine
|
||||||
val returnType =
|
val returnType =
|
||||||
if(sub is PtSub && sub.returns.size>1)
|
if(sub is PtSub && sub.returns.size>1)
|
||||||
DataType.forDt(BaseDataType.UNDEFINED) // TODO list of types instead?
|
DataType.UNDEFINED // TODO list of types instead?
|
||||||
else
|
else
|
||||||
sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
|
sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
|
||||||
?: throw AssemblyError("can't translate zero return values in assignment")
|
?: throw AssemblyError("can't translate zero return values in assignment")
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
package prog8.codegen.cpu6502.assignment
|
package prog8.codegen.cpu6502.assignment
|
||||||
|
|
||||||
import prog8.code.StMemVar
|
import prog8.code.*
|
||||||
import prog8.code.StExtSub
|
|
||||||
import prog8.code.StExtSubParameter
|
|
||||||
import prog8.code.StStaticVariable
|
|
||||||
import prog8.code.StSub
|
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||||
@ -382,7 +378,7 @@ internal class AssignmentAsmGen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fallback assignmen through temporary pointer var
|
// fallback assignmen through temporary pointer var
|
||||||
assignExpressionToVariable(address, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
assignExpressionToVariable(address, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2", false)
|
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2", false)
|
||||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||||
}
|
}
|
||||||
@ -433,7 +429,7 @@ internal class AssignmentAsmGen(
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
if(saveA) asmgen.out(" pha")
|
if(saveA) asmgen.out(" pha")
|
||||||
assignExpressionToVariable(address, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
assignExpressionToVariable(address, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
if(saveA) asmgen.out(" pla")
|
if(saveA) asmgen.out(" pla")
|
||||||
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2", false)
|
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2", false)
|
||||||
}
|
}
|
||||||
@ -442,6 +438,8 @@ internal class AssignmentAsmGen(
|
|||||||
private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) {
|
private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) {
|
||||||
when(val value = assign.source.expression!!) {
|
when(val value = assign.source.expression!!) {
|
||||||
is PtAddressOf -> {
|
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 arrayDt = value.identifier.type
|
||||||
val sourceName =
|
val sourceName =
|
||||||
if(value.isMsbForSplitArray)
|
if(value.isMsbForSplitArray)
|
||||||
@ -975,7 +973,7 @@ internal class AssignmentAsmGen(
|
|||||||
if(!directIntoY(expr.right)) asmgen.out(" pha")
|
if(!directIntoY(expr.right)) asmgen.out(" pha")
|
||||||
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
|
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
|
||||||
if(!directIntoY(expr.right)) asmgen.out(" pla")
|
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)
|
if(target.register==RegisterOrPair.A)
|
||||||
asmgen.out(" cmp #0") // fix the status register
|
asmgen.out(" cmp #0") // fix the status register
|
||||||
else
|
else
|
||||||
@ -985,7 +983,7 @@ internal class AssignmentAsmGen(
|
|||||||
expr.type.isUnsignedWord -> {
|
expr.type.isUnsignedWord -> {
|
||||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
|
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
|
||||||
asmgen.out(" jsr prog8_math.divmod_uw_asm")
|
asmgen.out(" jsr prog8_math.divmod_uw_asm")
|
||||||
assignVariableWord(target, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
assignVariableWord(target, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
else -> return false
|
else -> return false
|
||||||
@ -1190,19 +1188,18 @@ internal class AssignmentAsmGen(
|
|||||||
}
|
}
|
||||||
} else if(dt.isWord) {
|
} else if(dt.isWord) {
|
||||||
if(shifts==7 && expr.operator == "<<") {
|
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)
|
assignExpressionToRegister(expr.left, RegisterOrPair.AY, signed)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
; shift left 7
|
; shift left 7
|
||||||
sty P8ZP_SCRATCH_REG ; msb
|
sty P8ZP_SCRATCH_REG
|
||||||
lsr P8ZP_SCRATCH_REG
|
lsr P8ZP_SCRATCH_REG
|
||||||
php ; save carry
|
ror a
|
||||||
sta P8ZP_SCRATCH_REG
|
sta P8ZP_SCRATCH_REG
|
||||||
lda #0
|
lda #0
|
||||||
plp ; restore carry
|
|
||||||
ror P8ZP_SCRATCH_REG
|
|
||||||
ror a
|
ror a
|
||||||
ldy P8ZP_SCRATCH_REG""")
|
ldy P8ZP_SCRATCH_REG""")
|
||||||
|
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1317,7 +1314,7 @@ internal class AssignmentAsmGen(
|
|||||||
// special optimization for bytearray[y] + bytevalue : no need to use a tempvar, just use adc array,y
|
// special optimization for bytearray[y] + bytevalue : no need to use a tempvar, just use adc array,y
|
||||||
assignExpressionToRegister(right, RegisterOrPair.A, right.type.isSigned)
|
assignExpressionToRegister(right, RegisterOrPair.A, right.type.isSigned)
|
||||||
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pha")
|
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")
|
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pla")
|
||||||
val arrayvarname = asmgen.asmSymbolName(leftArrayIndexer.variable)
|
val arrayvarname = asmgen.asmSymbolName(leftArrayIndexer.variable)
|
||||||
asmgen.out(" clc | adc $arrayvarname,y")
|
asmgen.out(" clc | adc $arrayvarname,y")
|
||||||
@ -1326,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
|
// 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)
|
assignExpressionToRegister(left, RegisterOrPair.A, left.type.isSigned)
|
||||||
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pha")
|
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")
|
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pla")
|
||||||
val arrayvarname = asmgen.asmSymbolName(rightArrayIndexer.variable)
|
val arrayvarname = asmgen.asmSymbolName(rightArrayIndexer.variable)
|
||||||
if (expr.operator == "+")
|
if (expr.operator == "+")
|
||||||
@ -1501,7 +1498,7 @@ internal class AssignmentAsmGen(
|
|||||||
if(right.type.isWord && castedValue.type.isByte && castedValue is PtIdentifier) {
|
if(right.type.isWord && castedValue.type.isByte && castedValue is PtIdentifier) {
|
||||||
if(right.type.isSigned) {
|
if(right.type.isSigned) {
|
||||||
// we need to sign extend, do this via temporary word variable
|
// we need to sign extend, do this via temporary word variable
|
||||||
asmgen.assignExpressionToVariable(right, "P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.WORD))
|
asmgen.assignExpressionToVariable(right, "P8ZP_SCRATCH_W1", DataType.WORD)
|
||||||
assignExpressionToRegister(left, RegisterOrPair.AY, dt.isSigned)
|
assignExpressionToRegister(left, RegisterOrPair.AY, dt.isSigned)
|
||||||
if(expr.operator=="+") {
|
if(expr.operator=="+") {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -1571,8 +1568,8 @@ internal class AssignmentAsmGen(
|
|||||||
if(ptrVar!=null && asmgen.isZpVar(ptrVar)) {
|
if(ptrVar!=null && asmgen.isZpVar(ptrVar)) {
|
||||||
assignExpressionToRegister(value, RegisterOrPair.A, false)
|
assignExpressionToRegister(value, RegisterOrPair.A, false)
|
||||||
val pointername = asmgen.asmVariableName(ptrVar)
|
val pointername = asmgen.asmVariableName(ptrVar)
|
||||||
if (constOffset != null && constOffset < 256) {
|
if (constOffset != null) {
|
||||||
// we have value + @(zpptr + 255), or value - @(zpptr+255)
|
// we have value + @(zpptr + 255), or value - @(zpptr+255). the offset is always <256.
|
||||||
asmgen.out(" ldy #$constOffset")
|
asmgen.out(" ldy #$constOffset")
|
||||||
if (operator == "+")
|
if (operator == "+")
|
||||||
asmgen.out(" clc | adc ($pointername),y")
|
asmgen.out(" clc | adc ($pointername),y")
|
||||||
@ -1638,7 +1635,7 @@ internal class AssignmentAsmGen(
|
|||||||
asmgen.out(" sty P8ZP_SCRATCH_B1")
|
asmgen.out(" sty P8ZP_SCRATCH_B1")
|
||||||
} else {
|
} else {
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.out(" pla")
|
asmgen.out(" pla")
|
||||||
}
|
}
|
||||||
when (expr.operator) {
|
when (expr.operator) {
|
||||||
@ -1691,7 +1688,7 @@ internal class AssignmentAsmGen(
|
|||||||
asmgen.out(" sty P8ZP_SCRATCH_B1")
|
asmgen.out(" sty P8ZP_SCRATCH_B1")
|
||||||
} else {
|
} else {
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
asmgen.out(" pla")
|
asmgen.out(" pla")
|
||||||
}
|
}
|
||||||
when (operator) {
|
when (operator) {
|
||||||
@ -1964,27 +1961,27 @@ $endLabel""")
|
|||||||
val (dt, numElements) = when(symbol) {
|
val (dt, numElements) = when(symbol) {
|
||||||
is StStaticVariable -> symbol.dt to symbol.length!!
|
is StStaticVariable -> symbol.dt to symbol.length!!
|
||||||
is StMemVar -> symbol.dt to symbol.length!!
|
is StMemVar -> symbol.dt to symbol.length!!
|
||||||
else -> DataType.forDt(BaseDataType.UNDEFINED) to 0
|
else -> DataType.UNDEFINED to 0
|
||||||
}
|
}
|
||||||
when {
|
when {
|
||||||
dt.isString -> {
|
dt.isString -> {
|
||||||
assignExpressionToRegister(containment.needle, RegisterOrPair.A, elementDt.isSigned)
|
assignExpressionToRegister(containment.needle, RegisterOrPair.A, elementDt.isSigned)
|
||||||
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
|
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
|
||||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UWORD), containment.definingISub(), containment.position,"P8ZP_SCRATCH_W1"), symbolName, false, null, null)
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position,"P8ZP_SCRATCH_W1"), symbolName, false, null, null)
|
||||||
asmgen.out(" pla")
|
asmgen.out(" pla")
|
||||||
asmgen.out(" ldy #${numElements-1}")
|
asmgen.out(" ldy #${numElements-1}")
|
||||||
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||||
}
|
}
|
||||||
dt.isFloatArray -> {
|
dt.isFloatArray -> {
|
||||||
assignExpressionToRegister(containment.needle, RegisterOrPair.FAC1, true)
|
assignExpressionToRegister(containment.needle, RegisterOrPair.FAC1, true)
|
||||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UWORD), containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, false, null, null)
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, false, null, null)
|
||||||
asmgen.out(" ldy #$numElements")
|
asmgen.out(" ldy #$numElements")
|
||||||
asmgen.out(" jsr floats.containment_floatarray")
|
asmgen.out(" jsr floats.containment_floatarray")
|
||||||
}
|
}
|
||||||
dt.isByteArray -> {
|
dt.isByteArray -> {
|
||||||
assignExpressionToRegister(containment.needle, RegisterOrPair.A, elementDt.isSigned)
|
assignExpressionToRegister(containment.needle, RegisterOrPair.A, elementDt.isSigned)
|
||||||
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
|
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
|
||||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UWORD), containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, false, null, null)
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, false, null, null)
|
||||||
asmgen.out(" pla")
|
asmgen.out(" pla")
|
||||||
asmgen.out(" ldy #$numElements")
|
asmgen.out(" ldy #$numElements")
|
||||||
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||||
@ -1992,11 +1989,11 @@ $endLabel""")
|
|||||||
dt.isWordArray -> {
|
dt.isWordArray -> {
|
||||||
assignExpressionToVariable(containment.needle, "P8ZP_SCRATCH_W1", elementDt)
|
assignExpressionToVariable(containment.needle, "P8ZP_SCRATCH_W1", elementDt)
|
||||||
if(dt.isSplitWordArray) {
|
if(dt.isSplitWordArray) {
|
||||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UWORD), containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName+"_lsb", false, null, null)
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName+"_lsb", false, null, null)
|
||||||
asmgen.out(" ldy #$numElements")
|
asmgen.out(" ldy #$numElements")
|
||||||
asmgen.out(" jsr prog8_lib.containment_splitwordarray")
|
asmgen.out(" jsr prog8_lib.containment_splitwordarray")
|
||||||
} else {
|
} else {
|
||||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UWORD), containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName, false, null, null)
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName, false, null, null)
|
||||||
asmgen.out(" ldy #$numElements")
|
asmgen.out(" ldy #$numElements")
|
||||||
asmgen.out(" jsr prog8_lib.containment_linearwordarray")
|
asmgen.out(" jsr prog8_lib.containment_linearwordarray")
|
||||||
}
|
}
|
||||||
@ -2060,7 +2057,7 @@ $endLabel""")
|
|||||||
if(targetDt.isWord) {
|
if(targetDt.isWord) {
|
||||||
|
|
||||||
fun assignViaExprEval(addressExpression: PtExpression) {
|
fun assignViaExprEval(addressExpression: PtExpression) {
|
||||||
asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2", false)
|
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2", false)
|
||||||
asmgen.out(" ldy #0")
|
asmgen.out(" ldy #0")
|
||||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||||
@ -2287,10 +2284,10 @@ $endLabel""")
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun assignCastViaLsbFunc(value: PtExpression, target: AsmAssignTarget) {
|
private fun assignCastViaLsbFunc(value: PtExpression, target: AsmAssignTarget) {
|
||||||
val lsb = PtBuiltinFunctionCall("lsb", false, true, DataType.forDt(BaseDataType.UBYTE), value.position)
|
val lsb = PtBuiltinFunctionCall("lsb", false, true, DataType.UBYTE, value.position)
|
||||||
lsb.parent = value.parent
|
lsb.parent = value.parent
|
||||||
lsb.add(value)
|
lsb.add(value)
|
||||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.forDt(BaseDataType.UBYTE), expression = lsb)
|
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb)
|
||||||
val assign = AsmAssignment(src, listOf(target), program.memsizer, value.position)
|
val assign = AsmAssignment(src, listOf(target), program.memsizer, value.position)
|
||||||
translateNormalAssignment(assign, value.definingISub())
|
translateNormalAssignment(assign, value.definingISub())
|
||||||
}
|
}
|
||||||
@ -2614,16 +2611,26 @@ $endLabel""")
|
|||||||
val arrayName = if(arrayDt!!.isSplitWordArray) sourceName+"_lsb" else sourceName // the _lsb split array comes first in memory
|
val arrayName = if(arrayDt!!.isSplitWordArray) sourceName+"_lsb" else sourceName // the _lsb split array comes first in memory
|
||||||
val constIndex = arrayIndexExpr.asConstInteger()
|
val constIndex = arrayIndexExpr.asConstInteger()
|
||||||
if(constIndex!=null) {
|
if(constIndex!=null) {
|
||||||
if (arrayDt?.isUnsignedWord==true) {
|
if (arrayDt.isUnsignedWord) {
|
||||||
require(!msb)
|
require(!msb)
|
||||||
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
|
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
|
||||||
if(constIndex>0)
|
if(constIndex in 1..255)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
clc
|
clc
|
||||||
adc #$constIndex
|
adc #$constIndex
|
||||||
bcc +
|
bcc +
|
||||||
iny
|
iny
|
||||||
+""")
|
+""")
|
||||||
|
else if(constIndex>=256) {
|
||||||
|
asmgen.out("""
|
||||||
|
clc
|
||||||
|
adc #<$constIndex
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
adc #>$constIndex
|
||||||
|
tay
|
||||||
|
pla""")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(constIndex>0) {
|
if(constIndex>0) {
|
||||||
@ -2636,20 +2643,38 @@ $endLabel""")
|
|||||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
if (arrayDt?.isUnsignedWord==true) {
|
if (arrayDt.isUnsignedWord) {
|
||||||
require(!msb)
|
require(!msb)
|
||||||
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
|
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
|
||||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||||
assignExpressionToVariable(arrayIndexExpr, "P8ZP_SCRATCH_REG", DataType.forDt(BaseDataType.UBYTE))
|
if(arrayIndexExpr.type.isWord) {
|
||||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.AY, false)
|
||||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
asmgen.out("""
|
||||||
asmgen.out("""
|
sta P8ZP_SCRATCH_W1
|
||||||
clc
|
sty P8ZP_SCRATCH_W1+1
|
||||||
adc P8ZP_SCRATCH_REG
|
pla
|
||||||
bcc +
|
tay
|
||||||
iny
|
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 {
|
else {
|
||||||
assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.A, false)
|
assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.A, false)
|
||||||
@ -2859,7 +2884,7 @@ $endLabel""")
|
|||||||
jsr floats.MOVMF""")
|
jsr floats.MOVMF""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #<${target.asmVarname}
|
ldy #<${target.asmVarname}
|
||||||
sty P8ZP_SCRATCH_W1
|
sty P8ZP_SCRATCH_W1
|
||||||
@ -2891,7 +2916,7 @@ $endLabel""")
|
|||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
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.restoreRegisterStack(CpuRegister.Y, false)
|
||||||
asmgen.out(" pla")
|
asmgen.out(" pla")
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -2928,7 +2953,7 @@ $endLabel""")
|
|||||||
jsr floats.copy_float""")
|
jsr floats.copy_float""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #<$sourceName
|
ldy #<$sourceName
|
||||||
sty P8ZP_SCRATCH_W1
|
sty P8ZP_SCRATCH_W1
|
||||||
@ -3225,7 +3250,7 @@ $endLabel""")
|
|||||||
if(signed)
|
if(signed)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
cmp #${'$'}80
|
cmp #$80
|
||||||
bcc +
|
bcc +
|
||||||
dey
|
dey
|
||||||
+""")
|
+""")
|
||||||
@ -3237,7 +3262,7 @@ $endLabel""")
|
|||||||
if(signed)
|
if(signed)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldx #0
|
ldx #0
|
||||||
cmp #${'$'}80
|
cmp #$80
|
||||||
bcc +
|
bcc +
|
||||||
dex
|
dex
|
||||||
+""")
|
+""")
|
||||||
@ -3250,7 +3275,7 @@ $endLabel""")
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
tax
|
tax
|
||||||
ldy #0
|
ldy #0
|
||||||
cpx #${'$'}80
|
cpx #$80
|
||||||
bcc +
|
bcc +
|
||||||
dey
|
dey
|
||||||
+""")
|
+""")
|
||||||
@ -3276,7 +3301,7 @@ $endLabel""")
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
txa
|
txa
|
||||||
ldy #0
|
ldy #0
|
||||||
cmp #${'$'}80
|
cmp #$80
|
||||||
bcc +
|
bcc +
|
||||||
dey
|
dey
|
||||||
+""")
|
+""")
|
||||||
@ -3289,7 +3314,7 @@ $endLabel""")
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
txa
|
txa
|
||||||
ldx #0
|
ldx #0
|
||||||
cmp #${'$'}80
|
cmp #$80
|
||||||
bcc +
|
bcc +
|
||||||
dex
|
dex
|
||||||
+""")
|
+""")
|
||||||
@ -3301,7 +3326,7 @@ $endLabel""")
|
|||||||
if(signed)
|
if(signed)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
cpx #${'$'}80
|
cpx #$80
|
||||||
bcc +
|
bcc +
|
||||||
dey
|
dey
|
||||||
+""")
|
+""")
|
||||||
@ -3327,7 +3352,7 @@ $endLabel""")
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
tya
|
tya
|
||||||
ldy #0
|
ldy #0
|
||||||
cmp #${'$'}80
|
cmp #$80
|
||||||
bcc +
|
bcc +
|
||||||
dey
|
dey
|
||||||
+""")
|
+""")
|
||||||
@ -3340,7 +3365,7 @@ $endLabel""")
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
tya
|
tya
|
||||||
ldx #0
|
ldx #0
|
||||||
cmp #${'$'}80
|
cmp #$80
|
||||||
bcc +
|
bcc +
|
||||||
dex
|
dex
|
||||||
+""")
|
+""")
|
||||||
@ -3354,7 +3379,7 @@ $endLabel""")
|
|||||||
tya
|
tya
|
||||||
tax
|
tax
|
||||||
ldy #0
|
ldy #0
|
||||||
cpx #${'$'}80
|
cpx #$80
|
||||||
bcc +
|
bcc +
|
||||||
dey
|
dey
|
||||||
+""")
|
+""")
|
||||||
@ -3401,7 +3426,7 @@ $endLabel""")
|
|||||||
} else {
|
} else {
|
||||||
require(target.array.index.type.isByteOrBool)
|
require(target.array.index.type.isByteOrBool)
|
||||||
asmgen.saveRegisterStack(register, false)
|
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")
|
asmgen.out(" pla | sta ${target.asmVarname},y")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3716,7 +3741,7 @@ $endLabel""")
|
|||||||
asmgen.out(" stz ${target.asmVarname}+$indexValue")
|
asmgen.out(" stz ${target.asmVarname}+$indexValue")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.X, false)
|
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.X)
|
||||||
asmgen.out(" stz ${target.asmVarname},x")
|
asmgen.out(" stz ${target.asmVarname},x")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3806,7 +3831,7 @@ $endLabel""")
|
|||||||
sta ${target.asmVarname}+4""")
|
sta ${target.asmVarname}+4""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #<${target.asmVarname}
|
ldy #<${target.asmVarname}
|
||||||
sty P8ZP_SCRATCH_W1
|
sty P8ZP_SCRATCH_W1
|
||||||
@ -3840,7 +3865,7 @@ $endLabel""")
|
|||||||
jsr floats.copy_float""")
|
jsr floats.copy_float""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #<${constFloat}
|
ldy #<${constFloat}
|
||||||
sty P8ZP_SCRATCH_W1
|
sty P8ZP_SCRATCH_W1
|
||||||
@ -4092,7 +4117,7 @@ $endLabel""")
|
|||||||
asmgen.storeAIntoPointerVar(memory.address as PtIdentifier)
|
asmgen.storeAIntoPointerVar(memory.address as PtIdentifier)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||||
if(asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
if(asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda (P8ZP_SCRATCH_W2)
|
lda (P8ZP_SCRATCH_W2)
|
||||||
@ -4154,8 +4179,8 @@ $endLabel""")
|
|||||||
val target = assign.target
|
val target = assign.target
|
||||||
val datatype = if(ignoreDatatype) {
|
val datatype = if(ignoreDatatype) {
|
||||||
when {
|
when {
|
||||||
target.datatype.isByte -> DataType.forDt(BaseDataType.BYTE)
|
target.datatype.isByte -> DataType.BYTE
|
||||||
target.datatype.isWord -> DataType.forDt(BaseDataType.WORD)
|
target.datatype.isWord -> DataType.WORD
|
||||||
else -> target.datatype
|
else -> target.datatype
|
||||||
}
|
}
|
||||||
} else target.datatype
|
} else target.datatype
|
||||||
|
@ -131,18 +131,18 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
is PtNumber -> {
|
is PtNumber -> {
|
||||||
val addr = (memory.address as PtNumber).number.toInt()
|
val addr = (memory.address as PtNumber).number.toInt()
|
||||||
when(value.kind) {
|
when(value.kind) {
|
||||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteWithLiteralval(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.boolean!!.asInt())
|
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteWithLiteralval(addr.toHex(), DataType.UBYTE, operator, value.boolean!!.asInt())
|
||||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationByteWithLiteralval(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.number!!.number.toInt())
|
SourceStorageKind.LITERALNUMBER -> inplacemodificationByteWithLiteralval(addr.toHex(), DataType.UBYTE, operator, value.number!!.number.toInt())
|
||||||
SourceStorageKind.VARIABLE -> inplacemodificationByteWithVariable(addr.toHex(), false, operator, value.asmVarname)
|
SourceStorageKind.VARIABLE -> inplacemodificationByteWithVariable(addr.toHex(), false, operator, value.asmVarname)
|
||||||
SourceStorageKind.REGISTER -> inplacemodificationByteWithVariable(addr.toHex(), false, operator, regName(value))
|
SourceStorageKind.REGISTER -> inplacemodificationByteWithVariable(addr.toHex(), false, operator, regName(value))
|
||||||
SourceStorageKind.MEMORY -> inplacemodificationByteWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.memory!!)
|
SourceStorageKind.MEMORY -> inplacemodificationByteWithValue(addr.toHex(), DataType.UBYTE, operator, value.memory!!)
|
||||||
SourceStorageKind.ARRAY -> inplacemodificationByteWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.array!!)
|
SourceStorageKind.ARRAY -> inplacemodificationByteWithValue(addr.toHex(), DataType.UBYTE, operator, value.array!!)
|
||||||
SourceStorageKind.EXPRESSION -> {
|
SourceStorageKind.EXPRESSION -> {
|
||||||
if(value.expression is PtTypeCast) {
|
if(value.expression is PtTypeCast) {
|
||||||
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
|
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
|
||||||
inplacemodificationByteWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.expression)
|
inplacemodificationByteWithValue(addr.toHex(), DataType.UBYTE, operator, value.expression)
|
||||||
} else {
|
} else {
|
||||||
inplacemodificationByteWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.expression!!)
|
inplacemodificationByteWithValue(addr.toHex(), DataType.UBYTE, operator, value.expression!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,7 +170,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
if(memory.address is PtBinaryExpression && tryOptimizedMemoryInplace(memory.address as PtBinaryExpression, operator, value))
|
if(memory.address is PtBinaryExpression && tryOptimizedMemoryInplace(memory.address as PtBinaryExpression, operator, value))
|
||||||
return
|
return
|
||||||
// slower method to calculate and use the pointer to access the memory with:
|
// 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.A, true)
|
||||||
asmgen.saveRegisterStack(CpuRegister.Y, true)
|
asmgen.saveRegisterStack(CpuRegister.Y, true)
|
||||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||||
@ -196,21 +196,21 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
SourceStorageKind.MEMORY -> {
|
SourceStorageKind.MEMORY -> {
|
||||||
asmgen.out(" sta P8ZP_SCRATCH_B1")
|
asmgen.out(" sta P8ZP_SCRATCH_B1")
|
||||||
inplacemodificationByteWithValue("P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE), operator, value.memory!!)
|
inplacemodificationByteWithValue("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.memory!!)
|
||||||
asmgen.out(" ldx P8ZP_SCRATCH_B1")
|
asmgen.out(" ldx P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
SourceStorageKind.ARRAY -> {
|
SourceStorageKind.ARRAY -> {
|
||||||
asmgen.out(" sta P8ZP_SCRATCH_B1")
|
asmgen.out(" sta P8ZP_SCRATCH_B1")
|
||||||
inplacemodificationByteWithValue("P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE), operator, value.array!!)
|
inplacemodificationByteWithValue("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.array!!)
|
||||||
asmgen.out(" ldx P8ZP_SCRATCH_B1")
|
asmgen.out(" ldx P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
SourceStorageKind.EXPRESSION -> {
|
SourceStorageKind.EXPRESSION -> {
|
||||||
val tempVar = asmgen.getTempVarName(BaseDataType.UBYTE)
|
val tempVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, memory)
|
||||||
asmgen.out(" sta $tempVar")
|
asmgen.out(" sta $tempVar")
|
||||||
if(value.expression is PtTypeCast)
|
if(value.expression is PtTypeCast)
|
||||||
inplacemodificationByteWithValue(tempVar, DataType.forDt(BaseDataType.UBYTE), operator, value.expression)
|
inplacemodificationByteWithValue(tempVar, DataType.UBYTE, operator, value.expression)
|
||||||
else
|
else
|
||||||
inplacemodificationByteWithValue(tempVar, DataType.forDt(BaseDataType.UBYTE), operator, value.expression!!)
|
inplacemodificationByteWithValue(tempVar, DataType.UBYTE, operator, value.expression!!)
|
||||||
asmgen.out(" ldx $tempVar")
|
asmgen.out(" ldx $tempVar")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -356,7 +356,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
SourceStorageKind.EXPRESSION -> {
|
SourceStorageKind.EXPRESSION -> {
|
||||||
val tempVar = asmgen.getTempVarName(BaseDataType.UBYTE)
|
val tempVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, target.array)
|
||||||
asmgen.out(" sta $tempVar")
|
asmgen.out(" sta $tempVar")
|
||||||
if(value.expression is PtTypeCast)
|
if(value.expression is PtTypeCast)
|
||||||
inplacemodificationByteWithValue(tempVar, target.datatype, operator, value.expression)
|
inplacemodificationByteWithValue(tempVar, target.datatype, operator, value.expression)
|
||||||
@ -439,7 +439,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
SourceStorageKind.EXPRESSION -> {
|
SourceStorageKind.EXPRESSION -> {
|
||||||
val tempVar = asmgen.getTempVarName(BaseDataType.UWORD)
|
val tempVar = asmgen.createTempVarReused(BaseDataType.UWORD, false, target.array)
|
||||||
asmgen.out(" sta $tempVar | stx $tempVar+1")
|
asmgen.out(" sta $tempVar | stx $tempVar+1")
|
||||||
if(value.expression is PtTypeCast)
|
if(value.expression is PtTypeCast)
|
||||||
inplacemodificationWordWithValue(tempVar, target.datatype, operator, value.expression, block)
|
inplacemodificationWordWithValue(tempVar, target.datatype, operator, value.expression, block)
|
||||||
@ -457,7 +457,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
|
|
||||||
target.datatype.isFloat -> {
|
target.datatype.isFloat -> {
|
||||||
// copy array value into tempvar
|
// 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.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.A)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #>${target.asmVarname}
|
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)) {
|
if(address.left is PtIdentifier && asmgen.isZpVar(address.left as PtIdentifier)) {
|
||||||
return (address.left as PtIdentifier).name
|
return (address.left as PtIdentifier).name
|
||||||
} else {
|
} 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")
|
asmgen.out(" sta P8ZP_SCRATCH_W2 | sty P8ZP_SCRATCH_W2+1")
|
||||||
return "P8ZP_SCRATCH_W2"
|
return "P8ZP_SCRATCH_W2"
|
||||||
}
|
}
|
||||||
@ -592,7 +592,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
fun assignValueToA() {
|
fun assignValueToA() {
|
||||||
val assignValue = AsmAssignment(value,
|
val assignValue = AsmAssignment(value,
|
||||||
listOf(
|
listOf(
|
||||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.UBYTE),
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE,
|
||||||
address.definingISub(), Position.DUMMY, register = RegisterOrPair.A)
|
address.definingISub(), Position.DUMMY, register = RegisterOrPair.A)
|
||||||
),
|
),
|
||||||
program.memsizer, Position.DUMMY)
|
program.memsizer, Position.DUMMY)
|
||||||
@ -635,9 +635,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if(rightTc!=null)
|
if(rightTc!=null)
|
||||||
asmgen.assignExpressionToRegister(rightTc.value, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(rightTc.value, RegisterOrPair.A)
|
||||||
else
|
else
|
||||||
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.A)
|
||||||
asmgen.out(" pha") // offset on stack
|
asmgen.out(" pha") // offset on stack
|
||||||
val zpPointerVarName = addrIntoZpPointer()
|
val zpPointerVarName = addrIntoZpPointer()
|
||||||
assignValueToA()
|
assignValueToA()
|
||||||
@ -898,7 +898,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun inplacemodificationBytePointerWithValue(pointervar: PtIdentifier, operator: String, value: PtExpression) {
|
private fun inplacemodificationBytePointerWithValue(pointervar: PtIdentifier, operator: String, value: PtExpression) {
|
||||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
inplacemodificationBytePointerWithVariable(pointervar, operator, "P8ZP_SCRATCH_B1")
|
inplacemodificationBytePointerWithVariable(pointervar, operator, "P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -910,7 +910,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
"-" -> asmgen.out(" sec | sbc $otherName")
|
"-" -> asmgen.out(" sec | sbc $otherName")
|
||||||
"*" -> asmgen.out(" ldy $otherName | jsr prog8_math.multiply_bytes")
|
"*" -> 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 | tya")
|
||||||
"%" -> asmgen.out(" ldy $otherName | jsr prog8_math.divmod_ub_asm")
|
"%" -> asmgen.out(" ldy $otherName | jsr prog8_math.remainder_ub_asm")
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $otherName
|
ldy $otherName
|
||||||
@ -962,10 +962,18 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
when (operator) {
|
when (operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
if(value==1) {
|
if(value==1) {
|
||||||
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
if(asmgen.options.romable) {
|
||||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||||
asmgen.romableWarning("self-modifying code (access pointer)", pointervar.position) // TODO
|
asmgen.out(" inc a")
|
||||||
|
else
|
||||||
|
asmgen.out(" clc | adc #1")
|
||||||
|
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||||
|
} else {
|
||||||
|
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
||||||
|
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||||
|
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
asmgen.out(" clc | adc #$value")
|
asmgen.out(" clc | adc #$value")
|
||||||
@ -974,10 +982,18 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
if(value==1) {
|
if(value==1) {
|
||||||
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
if(asmgen.options.romable) {
|
||||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||||
asmgen.romableWarning("self-modifying code (access pointer)", pointervar.position) // TODO
|
asmgen.out(" dec a")
|
||||||
|
else
|
||||||
|
asmgen.out(" sec | sbc #1")
|
||||||
|
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||||
|
} else {
|
||||||
|
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
||||||
|
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||||
|
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
asmgen.out(" sec | sbc #$value")
|
asmgen.out(" sec | sbc #$value")
|
||||||
@ -1003,7 +1019,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
if(value==0)
|
if(value==0)
|
||||||
throw AssemblyError("division by zero")
|
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)
|
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||||
}
|
}
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
@ -1174,7 +1190,7 @@ $shortcutLabel:""")
|
|||||||
"%" -> {
|
"%" -> {
|
||||||
if(signed)
|
if(signed)
|
||||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
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("""
|
asmgen.out("""
|
||||||
@ -1347,7 +1363,7 @@ $shortcutLabel:""")
|
|||||||
"%" -> {
|
"%" -> {
|
||||||
if(signed)
|
if(signed)
|
||||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
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("""
|
asmgen.out("""
|
||||||
@ -1518,7 +1534,7 @@ $shortcutLabel:""")
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $name
|
lda $name
|
||||||
ldy #$value
|
ldy #$value
|
||||||
jsr prog8_math.divmod_ub_asm
|
jsr prog8_math.remainder_ub_asm
|
||||||
sta $name""")
|
sta $name""")
|
||||||
}
|
}
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
@ -1905,18 +1921,16 @@ $shortcutLabel:""")
|
|||||||
asmgen.out(" lda #0 | sta $lsb")
|
asmgen.out(" lda #0 | sta $lsb")
|
||||||
}
|
}
|
||||||
value==7 -> {
|
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("""
|
asmgen.out("""
|
||||||
; shift left 7
|
; shift left 7
|
||||||
lsr $msb
|
lsr $msb
|
||||||
php ; save carry
|
|
||||||
lda $lsb
|
lda $lsb
|
||||||
|
ror a
|
||||||
sta $msb
|
sta $msb
|
||||||
lda #0
|
lda #0
|
||||||
sta $lsb
|
ror a
|
||||||
plp ; restore carry
|
sta $lsb""")
|
||||||
ror $msb
|
|
||||||
ror $lsb""")
|
|
||||||
}
|
}
|
||||||
value>3 -> asmgen.out("""
|
value>3 -> asmgen.out("""
|
||||||
ldy #$value
|
ldy #$value
|
||||||
@ -2724,7 +2738,7 @@ $shortcutLabel:""")
|
|||||||
"+" -> {
|
"+" -> {
|
||||||
// name += byteexpression
|
// name += byteexpression
|
||||||
if(valueDt.isUnsignedByte) {
|
if(valueDt.isUnsignedByte) {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
clc
|
clc
|
||||||
adc $name
|
adc $name
|
||||||
|
@ -68,6 +68,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
|||||||
|
|
||||||
override fun noErrors(): Boolean = errors.isEmpty()
|
override fun noErrors(): Boolean = errors.isEmpty()
|
||||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||||
|
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
|
||||||
|
|
||||||
override fun report() {
|
override fun report() {
|
||||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||||
|
@ -5,12 +5,15 @@ import io.kotest.assertions.withClue
|
|||||||
import io.kotest.core.spec.style.FunSpec
|
import io.kotest.core.spec.style.FunSpec
|
||||||
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
|
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
|
||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
|
import prog8.code.StMemVar
|
||||||
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.SymbolTableMaker
|
import prog8.code.SymbolTableMaker
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.code.source.SourceCode
|
import prog8.code.source.SourceCode
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
import prog8.codegen.cpu6502.AsmGen6502
|
import prog8.codegen.cpu6502.AsmGen6502
|
||||||
|
import prog8.codegen.cpu6502.VariableAllocator
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
|
|
||||||
@ -52,9 +55,10 @@ class TestCodegen: FunSpec({
|
|||||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"pi",
|
"pi",
|
||||||
DataType.forDt(BaseDataType.UBYTE),
|
DataType.UBYTE,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
@ -64,6 +68,7 @@ class TestCodegen: FunSpec({
|
|||||||
DataType.arrayFor(BaseDataType.UBYTE),
|
DataType.arrayFor(BaseDataType.UBYTE),
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
3u,
|
3u,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
@ -73,15 +78,17 @@ class TestCodegen: FunSpec({
|
|||||||
DataType.arrayFor(BaseDataType.UBYTE),
|
DataType.arrayFor(BaseDataType.UBYTE),
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
3u,
|
3u,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"xx",
|
"xx",
|
||||||
DataType.forDt(BaseDataType.WORD),
|
DataType.WORD,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
@ -89,13 +96,13 @@ class TestCodegen: FunSpec({
|
|||||||
|
|
||||||
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
val target = PtAssignTarget(false, Position.DUMMY).also {
|
val target = PtAssignTarget(false, Position.DUMMY).also {
|
||||||
val targetIdx = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY).also { idx ->
|
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
|
||||||
idx.add(PtIdentifier("main.start.particleX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
|
idx.add(PtIdentifier("main.start.particleX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
|
||||||
idx.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
|
idx.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
|
||||||
}
|
}
|
||||||
it.add(targetIdx)
|
it.add(targetIdx)
|
||||||
}
|
}
|
||||||
val value = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)
|
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
|
||||||
value.add(PtIdentifier("main.start.particleDX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
|
value.add(PtIdentifier("main.start.particleDX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
|
||||||
value.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
|
value.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
|
||||||
assign.add(target)
|
assign.add(target)
|
||||||
@ -104,15 +111,15 @@ class TestCodegen: FunSpec({
|
|||||||
|
|
||||||
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
||||||
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
|
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
}
|
}
|
||||||
prefixAssign.add(prefixTarget)
|
prefixAssign.add(prefixTarget)
|
||||||
prefixAssign.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
sub.add(prefixAssign)
|
sub.add(prefixAssign)
|
||||||
|
|
||||||
val numberAssign = PtAugmentedAssign("-=", Position.DUMMY)
|
val numberAssign = PtAugmentedAssign("-=", Position.DUMMY)
|
||||||
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
}
|
}
|
||||||
numberAssign.add(numberAssignTarget)
|
numberAssign.add(numberAssignTarget)
|
||||||
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
|
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
|
||||||
@ -120,10 +127,10 @@ class TestCodegen: FunSpec({
|
|||||||
|
|
||||||
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
}
|
}
|
||||||
cxregAssign.add(cxregAssignTarget)
|
cxregAssign.add(cxregAssignTarget)
|
||||||
cxregAssign.add(PtIdentifier("cx16.r0", DataType.forDt(BaseDataType.UWORD), Position.DUMMY))
|
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
|
||||||
sub.add(cxregAssign)
|
sub.add(cxregAssign)
|
||||||
|
|
||||||
block.add(sub)
|
block.add(sub)
|
||||||
@ -131,7 +138,7 @@ class TestCodegen: FunSpec({
|
|||||||
|
|
||||||
// define the "cx16.r0" virtual register
|
// define the "cx16.r0" virtual register
|
||||||
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||||
cx16block.add(PtMemMapped("r0", DataType.forDt(BaseDataType.UWORD), 100u, null, Position.DUMMY))
|
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||||
program.add(cx16block)
|
program.add(cx16block)
|
||||||
|
|
||||||
val options = getTestOptions()
|
val options = getTestOptions()
|
||||||
@ -160,5 +167,15 @@ class TestCodegen: FunSpec({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("memory mapped zp var is correctly considered to be zp var") {
|
||||||
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
|
val st = SymbolTable(program)
|
||||||
|
st.add(StMemVar("zpmemvar", DataType.WORD, 0x20u, null, null))
|
||||||
|
st.add(StMemVar("normalmemvar", DataType.WORD, 0x9000u, null, null))
|
||||||
|
val allocator = VariableAllocator(st, getTestOptions(), ErrorReporterForTests())
|
||||||
|
allocator.isZpVar("zpmemvar") shouldBe true
|
||||||
|
allocator.isZpVar("normalmemvar") shouldBe false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package prog8.codegen.experimental
|
package prog8.codegen.experimental
|
||||||
|
|
||||||
|
import prog8.code.IAssemblyProgram
|
||||||
|
import prog8.code.ICodeGeneratorBackend
|
||||||
import prog8.code.SymbolTable
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.ast.PtProgram
|
import prog8.code.ast.PtProgram
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.CompilationOptions
|
||||||
import prog8.code.IAssemblyProgram
|
|
||||||
import prog8.code.ICodeGeneratorBackend
|
|
||||||
import prog8.code.core.IErrorReporter
|
import prog8.code.core.IErrorReporter
|
||||||
import prog8.codegen.intermediate.IRCodeGen
|
import prog8.codegen.intermediate.IRCodeGen
|
||||||
import prog8.intermediate.IRFileWriter
|
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.
|
// this stub only writes the IR program to disk but doesn't generate anything else.
|
||||||
IRFileWriter(irProgram, null).write()
|
IRFileWriter(irProgram, null).write()
|
||||||
|
|
||||||
println("** experimental codegen stub: no assembly generated **")
|
if(!options.quiet)
|
||||||
|
println("** experimental codegen stub: no assembly generated **")
|
||||||
return EmptyProgram
|
return EmptyProgram
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -34,7 +35,8 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
|||||||
private object EmptyProgram : IAssemblyProgram {
|
private object EmptyProgram : IAssemblyProgram {
|
||||||
override val name = "<Empty Program>"
|
override val name = "<Empty Program>"
|
||||||
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||||
println("** nothing assembled **")
|
if(!options.quiet)
|
||||||
|
println("** nothing assembled **")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,20 +68,22 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
|
|
||||||
private fun assignCpuRegister(returns: StExtSubParameter, regNum: Int, target: PtAssignTarget): IRCodeChunks {
|
private fun assignCpuRegister(returns: StExtSubParameter, regNum: Int, target: PtAssignTarget): IRCodeChunks {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val loadCpuRegInstr = when(returns.register.registerOrPair) {
|
when(returns.register.registerOrPair) {
|
||||||
RegisterOrPair.A -> IRInstruction(Opcode.LOADHA, IRDataType.BYTE, reg1=regNum)
|
RegisterOrPair.A -> addInstr(result, IRInstruction(Opcode.LOADHA, IRDataType.BYTE, reg1=regNum), null)
|
||||||
RegisterOrPair.X -> IRInstruction(Opcode.LOADHX, IRDataType.BYTE, reg1=regNum)
|
RegisterOrPair.X -> addInstr(result, IRInstruction(Opcode.LOADHX, IRDataType.BYTE, reg1=regNum), null)
|
||||||
RegisterOrPair.Y -> IRInstruction(Opcode.LOADHY, IRDataType.BYTE, reg1=regNum)
|
RegisterOrPair.Y -> addInstr(result, IRInstruction(Opcode.LOADHY, IRDataType.BYTE, reg1=regNum), null)
|
||||||
RegisterOrPair.AX -> IRInstruction(Opcode.LOADHAX, IRDataType.WORD, reg1=regNum)
|
RegisterOrPair.AX -> addInstr(result, IRInstruction(Opcode.LOADHAX, IRDataType.WORD, reg1=regNum), null)
|
||||||
RegisterOrPair.AY -> IRInstruction(Opcode.LOADHAY, IRDataType.WORD, reg1=regNum)
|
RegisterOrPair.AY -> addInstr(result, IRInstruction(Opcode.LOADHAY, IRDataType.WORD, reg1=regNum), null)
|
||||||
RegisterOrPair.XY -> IRInstruction(Opcode.LOADHXY, IRDataType.WORD, reg1=regNum)
|
RegisterOrPair.XY -> addInstr(result, IRInstruction(Opcode.LOADHXY, IRDataType.WORD, reg1=regNum), null)
|
||||||
in Cx16VirtualRegisters -> IRInstruction(Opcode.LOADM, irType(returns.type), reg1=regNum, labelSymbol = "cx16.${returns.register.registerOrPair.toString().lowercase()}")
|
in Cx16VirtualRegisters -> addInstr(result, IRInstruction(Opcode.LOADM, irType(returns.type), reg1=regNum, labelSymbol = "cx16.${returns.register.registerOrPair.toString().lowercase()}"), null)
|
||||||
null -> {
|
RegisterOrPair.FAC1 -> addInstr(result, IRInstruction(Opcode.LOADHFACZERO, IRDataType.FLOAT, fpReg1 = regNum), null)
|
||||||
TODO("assign CPU status flag ${returns.register.statusflag!!}")
|
RegisterOrPair.FAC2 -> addInstr(result, IRInstruction(Opcode.LOADHFACONE, IRDataType.FLOAT, fpReg1 = regNum), null)
|
||||||
}
|
null -> if(returns.register.statusflag!=null)
|
||||||
else -> throw AssemblyError("cannot load register")
|
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.
|
// build an assignment to store the value in the actual target.
|
||||||
val assign = PtAssignment(target.position)
|
val assign = PtAssignment(target.position)
|
||||||
@ -91,6 +93,30 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
return result
|
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 {
|
internal fun translate(augAssign: PtAugmentedAssign): IRCodeChunks {
|
||||||
// augmented assignment always has just a single target
|
// augmented assignment always has just a single target
|
||||||
if (augAssign.target.children.single() is PtIrRegister)
|
if (augAssign.target.children.single() is PtIrRegister)
|
||||||
@ -108,7 +134,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val chunks = when (augAssign.operator) {
|
val chunks = when (augAssign.operator) {
|
||||||
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||||
"-=" -> operatorMinusInplace(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)
|
"/=" -> operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||||
"|=" -> operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
"|=" -> operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||||
"or=" -> operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
"or=" -> operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||||
@ -205,7 +231,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val tr = expressionEval.translateExpression(array.index)
|
val tr = expressionEval.translateExpression(array.index)
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
if(!array.splitWords && eltSize>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
|
return tr.resultReg
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,10 +387,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val instruction = if(zero) {
|
val instruction = if(zero) {
|
||||||
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = targetIdent.name)
|
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = targetIdent.name)
|
||||||
} else {
|
} else {
|
||||||
if (targetDt == IRDataType.FLOAT)
|
if (targetDt == IRDataType.FLOAT) {
|
||||||
|
require(valueFpRegister>=0)
|
||||||
IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
|
IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
|
require(valueRegister>=0)
|
||||||
IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
|
IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
result += IRCodeChunk(null, null).also { it += instruction }
|
result += IRCodeChunk(null, null).also { it += instruction }
|
||||||
return result
|
return result
|
||||||
@ -514,7 +544,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
return Pair(result, tr.resultReg)
|
return Pair(result, tr.resultReg)
|
||||||
}
|
}
|
||||||
val mult: PtExpression = PtBinaryExpression("*", DataType.forDt(BaseDataType.UBYTE), array.position)
|
val mult: PtExpression = PtBinaryExpression("*", DataType.UBYTE, array.position)
|
||||||
mult.children += array.index
|
mult.children += array.index
|
||||||
mult.children += PtNumber(BaseDataType.UBYTE, itemsize.toDouble(), array.position)
|
mult.children += PtNumber(BaseDataType.UBYTE, itemsize.toDouble(), array.position)
|
||||||
val tr = expressionEval.translateExpression(mult)
|
val tr = expressionEval.translateExpression(mult)
|
||||||
@ -763,7 +793,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
return result
|
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) {
|
if(array!=null) {
|
||||||
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
@ -777,7 +807,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val valueReg=codeGen.registers.next(eltDt)
|
val valueReg=codeGen.registers.next(eltDt)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOAD, eltDt, reg1=valueReg, immediate = constValue)
|
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
|
return result
|
||||||
@ -797,22 +828,23 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
val tr = expressionEval.translateExpression(operand)
|
val tr = expressionEval.translateExpression(operand)
|
||||||
addToResult(result, tr, -1, tr.resultFpReg)
|
addToResult(result, tr, -1, tr.resultFpReg)
|
||||||
addInstr(result, if(constAddress!=null)
|
addInstr(result, if(constAddress!=null)
|
||||||
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress)
|
IRInstruction(Opcode.MULSM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
|
IRInstruction(Opcode.MULSM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
|
||||||
, null)
|
, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(constFactorRight!=null && !constFactorRight.type.isFloat) {
|
if(constFactorRight!=null && !constFactorRight.type.isFloat) {
|
||||||
val factor = constFactorRight.number.toInt()
|
val factor = constFactorRight.number.toInt()
|
||||||
result += codeGen.multiplyByConstInplace(vmDt, constAddress, symbol, factor)
|
result += codeGen.multiplyByConstInplace(vmDt, signed, constAddress, symbol, factor)
|
||||||
} else {
|
} else {
|
||||||
val tr = expressionEval.translateExpression(operand)
|
val tr = expressionEval.translateExpression(operand)
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
val opcode = if(signed) Opcode.MULSM else Opcode.MULM
|
||||||
addInstr(result, if(constAddress!=null)
|
addInstr(result, if(constAddress!=null)
|
||||||
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, address = constAddress)
|
IRInstruction(opcode, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
IRInstruction(opcode, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||||
, null)
|
, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package prog8.codegen.intermediate
|
|||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.AssemblyError
|
import prog8.code.core.AssemblyError
|
||||||
import prog8.code.core.BaseDataType
|
import prog8.code.core.BaseDataType
|
||||||
|
import prog8.code.core.DataType
|
||||||
import prog8.intermediate.*
|
import prog8.intermediate.*
|
||||||
|
|
||||||
|
|
||||||
@ -78,7 +79,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
|
|
||||||
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
|
||||||
val bankTr = exprGen.translateExpression(call.args[0])
|
val bankTr = exprGen.translateExpression(call.args[0])
|
||||||
val addressTr = exprGen.translateExpression(call.args[1])
|
val addressTr = exprGen.translateExpression(call.args[1])
|
||||||
val argumentwordTr = exprGen.translateExpression(call.args[2])
|
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 {
|
private fun funcCallfar2(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
|
||||||
val bankTr = exprGen.translateExpression(call.args[0])
|
val bankTr = exprGen.translateExpression(call.args[0])
|
||||||
val addressTr = exprGen.translateExpression(call.args[1])
|
val addressTr = exprGen.translateExpression(call.args[1])
|
||||||
val argumentA = exprGen.translateExpression(call.args[2])
|
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 {
|
private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
|
||||||
val left = exprGen.translateExpression(call.args[0])
|
val left = exprGen.translateExpression(call.args[0])
|
||||||
val right = exprGen.translateExpression(call.args[1])
|
val right = exprGen.translateExpression(call.args[1])
|
||||||
addToResult(result, left, left.resultReg, -1)
|
addToResult(result, left, left.resultReg, -1)
|
||||||
@ -240,11 +238,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
}
|
}
|
||||||
BaseDataType.UWORD -> {
|
BaseDataType.UWORD -> {
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
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 {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
|
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 -> {
|
BaseDataType.FLOAT -> {
|
||||||
addToResult(result, tr, -1, tr.resultFpReg)
|
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 {
|
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
|
||||||
val type = irType(call.type)
|
val type = irType(call.type)
|
||||||
val valueTr = exprGen.translateExpression(call.args[0])
|
val valueTr = exprGen.translateExpression(call.args[0])
|
||||||
val minimumTr = exprGen.translateExpression(call.args[1])
|
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)
|
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
if(eltSize>1)
|
if(eltSize>1)
|
||||||
it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
|
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
|
||||||
if(msb)
|
if(msb)
|
||||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
||||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = target.variable.name)
|
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = target.variable.name)
|
||||||
@ -687,7 +684,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
if(eltSize>1)
|
if(eltSize>1)
|
||||||
it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
|
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
|
||||||
if(msb)
|
if(msb)
|
||||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
||||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = target.variable.name)
|
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = target.variable.name)
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package prog8.codegen.intermediate
|
package prog8.codegen.intermediate
|
||||||
|
|
||||||
import prog8.code.StNode
|
import prog8.code.*
|
||||||
import prog8.code.StExtSub
|
|
||||||
import prog8.code.StNodeType
|
|
||||||
import prog8.code.StSub
|
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.intermediate.*
|
import prog8.intermediate.*
|
||||||
@ -30,7 +27,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
fun translateExpression(expr: PtExpression): ExpressionCodeResult {
|
fun translateExpression(expr: PtExpression): ExpressionCodeResult {
|
||||||
return when (expr) {
|
return when (expr) {
|
||||||
is PtIrRegister -> {
|
is PtIrRegister -> {
|
||||||
ExpressionCodeResult(emptyList(), irType(expr.type), expr.register, -1)
|
if(expr.type.isFloat)
|
||||||
|
ExpressionCodeResult(emptyList(), IRDataType.FLOAT, -1, expr.register)
|
||||||
|
else
|
||||||
|
ExpressionCodeResult(emptyList(), irType(expr.type), expr.register, -1)
|
||||||
}
|
}
|
||||||
is PtBool -> {
|
is PtBool -> {
|
||||||
val code = IRCodeChunk(null, null)
|
val code = IRCodeChunk(null, null)
|
||||||
@ -178,12 +178,19 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
if(expr.isFromArrayElement) {
|
if(expr.isFromArrayElement) {
|
||||||
val indexTr = translateExpression(expr.arrayIndexExpr!!)
|
val indexTr = translateExpression(expr.arrayIndexExpr!!)
|
||||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||||
val indexWordReg = codeGen.registers.next(IRDataType.WORD)
|
val indexWordReg = if(indexTr.dt==IRDataType.BYTE) {
|
||||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexWordReg, reg2=indexTr.resultReg), null)
|
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) {
|
if(expr.identifier.type.isUnsignedWord) {
|
||||||
require(!expr.isMsbForSplitArray)
|
require(!expr.isMsbForSplitArray)
|
||||||
result += IRCodeChunk(null, null).also {
|
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)
|
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -283,7 +290,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
val haystackVar = check.haystackHeapVar!!
|
val haystackVar = check.haystackHeapVar!!
|
||||||
when {
|
when {
|
||||||
haystackVar.type.isString -> {
|
haystackVar.type.isString -> {
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
|
||||||
val elementTr = translateExpression(check.needle)
|
val elementTr = translateExpression(check.needle)
|
||||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||||
val iterableTr = translateExpression(haystackVar)
|
val iterableTr = translateExpression(haystackVar)
|
||||||
@ -294,7 +300,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
haystackVar.type.isByteArray -> {
|
haystackVar.type.isByteArray -> {
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
|
||||||
val elementTr = translateExpression(check.needle)
|
val elementTr = translateExpression(check.needle)
|
||||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||||
val iterableTr = translateExpression(haystackVar)
|
val iterableTr = translateExpression(haystackVar)
|
||||||
@ -308,7 +313,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
haystackVar.type.isWordArray -> {
|
haystackVar.type.isWordArray -> {
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
|
||||||
val elementTr = translateExpression(check.needle)
|
val elementTr = translateExpression(check.needle)
|
||||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||||
val iterableTr = translateExpression(haystackVar)
|
val iterableTr = translateExpression(haystackVar)
|
||||||
@ -322,7 +326,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
haystackVar.type.isFloatArray -> {
|
haystackVar.type.isFloatArray -> {
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
|
||||||
val elementTr = translateExpression(check.needle)
|
val elementTr = translateExpression(check.needle)
|
||||||
addToResult(result, elementTr, -1, elementTr.resultFpReg)
|
addToResult(result, elementTr, -1, elementTr.resultFpReg)
|
||||||
val iterableTr = translateExpression(haystackVar)
|
val iterableTr = translateExpression(haystackVar)
|
||||||
@ -386,7 +389,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
val tr = translateExpression(arrayIx.index)
|
val tr = translateExpression(arrayIx.index)
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
if(eltSize>1)
|
if(eltSize>1)
|
||||||
result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
|
result += codeGen.multiplyByConst(DataType.UBYTE, tr.resultReg, eltSize)
|
||||||
if(vmDt==IRDataType.FLOAT) {
|
if(vmDt==IRDataType.FLOAT) {
|
||||||
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||||
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=tr.resultReg, labelSymbol = arrayVarSymbol), null)
|
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=tr.resultReg, labelSymbol = arrayVarSymbol), null)
|
||||||
@ -559,8 +562,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
return when(binExpr.operator) {
|
return when(binExpr.operator) {
|
||||||
"+" -> operatorPlus(binExpr, vmDt)
|
"+" -> operatorPlus(binExpr, vmDt)
|
||||||
"-" -> operatorMinus(binExpr, vmDt)
|
"-" -> operatorMinus(binExpr, vmDt)
|
||||||
"*" -> operatorMultiply(binExpr, vmDt)
|
"*" -> operatorMultiply(binExpr, binExpr.left.type)
|
||||||
"/" -> operatorDivide(binExpr, vmDt, signed)
|
"/" -> operatorDivide(binExpr, binExpr.left.type)
|
||||||
"%" -> operatorModulo(binExpr, vmDt)
|
"%" -> operatorModulo(binExpr, vmDt)
|
||||||
"|" -> operatorOr(binExpr, vmDt, true)
|
"|" -> operatorOr(binExpr, vmDt, true)
|
||||||
"&" -> operatorAnd(binExpr, vmDt, true)
|
"&" -> operatorAnd(binExpr, vmDt, true)
|
||||||
@ -612,7 +615,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
when (callTarget) {
|
when (callTarget) {
|
||||||
is StSub -> {
|
is StSub -> {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
|
|
||||||
// assign the arguments
|
// assign the arguments
|
||||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||||
@ -664,7 +666,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
}
|
}
|
||||||
is StExtSub -> {
|
is StExtSub -> {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
|
|
||||||
// assign the arguments
|
// assign the arguments
|
||||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||||
@ -703,9 +704,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
Opcode.CALL,
|
Opcode.CALL,
|
||||||
address = address.address.toInt(),
|
address = address.address.toInt(),
|
||||||
fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
|
fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
|
||||||
}
|
} else if(address.constbank!=null) {
|
||||||
else {
|
IRInstruction(
|
||||||
TODO("callfar into another bank is not implemented for the selected compilation target")
|
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)
|
addInstr(result, call, null)
|
||||||
@ -1033,7 +1046,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun loadStatusAsBooleanResult(branchForTrue: Opcode, result: MutableList<IRCodeChunkBase>): Int {
|
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 other = codeGen.createLabelName()
|
||||||
val after = codeGen.createLabelName()
|
val after = codeGen.createLabelName()
|
||||||
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
@ -1048,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 {
|
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 other = codeGen.createLabelName()
|
||||||
val after = codeGen.createLabelName()
|
val after = codeGen.createLabelName()
|
||||||
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||||
@ -1208,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 result = mutableListOf<IRCodeChunkBase>()
|
||||||
val constFactorRight = binExpr.right as? PtNumber
|
val constFactorRight = binExpr.right as? PtNumber
|
||||||
if(vmDt==IRDataType.FLOAT) {
|
if(vmDt==IRDataType.FLOAT) {
|
||||||
@ -1223,7 +1237,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||||
val rightTr = translateExpression(binExpr.right)
|
val rightTr = translateExpression(binExpr.right)
|
||||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
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)
|
IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||||
@ -1235,13 +1249,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
val tr = translateExpression(binExpr.left)
|
val tr = translateExpression(binExpr.left)
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
val factor = constFactorRight.number.toInt()
|
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)
|
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||||
} else {
|
} else {
|
||||||
if(binExpr.right is PtNumber) {
|
if(binExpr.right is PtNumber) {
|
||||||
val leftTr = translateExpression(binExpr.left)
|
val leftTr = translateExpression(binExpr.left)
|
||||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
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())
|
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||||
@ -1252,7 +1266,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
val rightTr = translateExpression(binExpr.right)
|
val rightTr = translateExpression(binExpr.right)
|
||||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
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)
|
IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||||
@ -1263,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 result = mutableListOf<IRCodeChunkBase>()
|
||||||
val constFactorLeft = binExpr.left as? PtNumber
|
val constFactorLeft = binExpr.left as? PtNumber
|
||||||
val constFactorRight = binExpr.right as? PtNumber
|
val constFactorRight = binExpr.right as? PtNumber
|
||||||
@ -1285,7 +1300,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||||
val rightTr = translateExpression(binExpr.right)
|
val rightTr = translateExpression(binExpr.right)
|
||||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
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)
|
ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1293,20 +1308,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
val tr = translateExpression(binExpr.right)
|
val tr = translateExpression(binExpr.right)
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
val factor = constFactorLeft.number.toInt()
|
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)
|
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||||
} else if(constFactorRight!=null && !constFactorRight.type.isFloat) {
|
} else if(constFactorRight!=null && !constFactorRight.type.isFloat) {
|
||||||
val tr = translateExpression(binExpr.left)
|
val tr = translateExpression(binExpr.left)
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
val factor = constFactorRight.number.toInt()
|
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)
|
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||||
} else {
|
} else {
|
||||||
val leftTr = translateExpression(binExpr.left)
|
val leftTr = translateExpression(binExpr.left)
|
||||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
val rightTr = translateExpression(binExpr.right)
|
val rightTr = translateExpression(binExpr.right)
|
||||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
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)
|
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ class IRCodeGen(
|
|||||||
irProg.linkChunks()
|
irProg.linkChunks()
|
||||||
irProg.convertAsmChunks()
|
irProg.convertAsmChunks()
|
||||||
|
|
||||||
|
// the optimizer also does 1 essential step regardless of optimizations: joining adjacent chunks.
|
||||||
val optimizer = IRPeepholeOptimizer(irProg)
|
val optimizer = IRPeepholeOptimizer(irProg)
|
||||||
optimizer.optimize(options.optimize, errors)
|
optimizer.optimize(options.optimize, errors)
|
||||||
irProg.validate()
|
irProg.validate()
|
||||||
@ -64,15 +65,17 @@ class IRCodeGen(
|
|||||||
if(variable.uninitialized && variable.parent.type==StNodeType.BLOCK) {
|
if(variable.uninitialized && variable.parent.type==StNodeType.BLOCK) {
|
||||||
val block = variable.parent.astNode as PtBlock
|
val block = variable.parent.astNode as PtBlock
|
||||||
val initialization = (block.children.firstOrNull {
|
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?)
|
} as PtAssignment?)
|
||||||
val initValue = initialization?.value
|
val initValue = initialization?.value
|
||||||
when(initValue){
|
when(initValue){
|
||||||
is PtBool -> {
|
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())
|
variable.setOnetimeInitNumeric(initValue.asInt().toDouble())
|
||||||
initsToRemove += block to initialization
|
initsToRemove += block to initialization
|
||||||
}
|
}
|
||||||
is PtNumber -> {
|
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)
|
variable.setOnetimeInitNumeric(initValue.number)
|
||||||
initsToRemove += block to initialization
|
initsToRemove += block to initialization
|
||||||
}
|
}
|
||||||
@ -194,10 +197,7 @@ class IRCodeGen(
|
|||||||
old.fpReg1,
|
old.fpReg1,
|
||||||
old.fpReg2,
|
old.fpReg2,
|
||||||
immediate = immediateValue,
|
immediate = immediateValue,
|
||||||
null,
|
address = addressValue
|
||||||
address = addressValue,
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -256,8 +256,8 @@ class IRCodeGen(
|
|||||||
is PtBool,
|
is PtBool,
|
||||||
is PtArray,
|
is PtArray,
|
||||||
is PtBlock,
|
is PtBlock,
|
||||||
is PtDefer -> throw AssemblyError("should have been transformed")
|
is PtDefer -> throw AssemblyError("defer should have been transformed")
|
||||||
is PtString -> throw AssemblyError("should not occur as separate statement node ${node.position}")
|
is PtString -> throw AssemblyError("string should not occur as separate statement node ${node.position}")
|
||||||
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
|
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
|
||||||
else -> TODO("missing codegen for $node")
|
else -> TODO("missing codegen for $node")
|
||||||
}
|
}
|
||||||
@ -420,35 +420,44 @@ class IRCodeGen(
|
|||||||
whenStmt.choices.children.forEach {
|
whenStmt.choices.children.forEach {
|
||||||
val choice = it as PtWhenChoice
|
val choice = it as PtWhenChoice
|
||||||
if(choice.isElse) {
|
if(choice.isElse) {
|
||||||
|
require(choice.parent.children.last() === choice)
|
||||||
result += translateNode(choice.statements)
|
result += translateNode(choice.statements)
|
||||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
// is always the last node so can fall through
|
||||||
} else {
|
} else {
|
||||||
if(choice.statements.children.isEmpty()) {
|
if(choice.statements.children.isEmpty()) {
|
||||||
// no statements for this choice value, jump to the end immediately
|
// no statements for this choice value, jump to the end immediately
|
||||||
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
|
choice.values.children.map { v -> v as PtNumber }.sortedBy { v -> v.number }.forEach { value ->
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also { chunk ->
|
||||||
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
chunk += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val choiceLabel = createLabelName()
|
val choiceLabel = createLabelName()
|
||||||
choices.add(choiceLabel to choice)
|
val onlyJumpLabel = ((choice.statements.children.singleOrNull() as? PtJump)?.target as? PtIdentifier)?.name
|
||||||
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
|
val branchLabel: String
|
||||||
result += IRCodeChunk(null, null).also {
|
if(onlyJumpLabel==null) {
|
||||||
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
choices.add(choiceLabel to choice)
|
||||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = choiceLabel)
|
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) ->
|
choices.forEach { (label, choice) ->
|
||||||
result += labelFirstChunk(translateNode(choice.statements), label)
|
result += labelFirstChunk(translateNode(choice.statements), label)
|
||||||
val lastStatement = choice.statements.children.last()
|
if(!choice.isOnlyGotoOrReturn())
|
||||||
if(lastStatement !is PtReturn && lastStatement !is PtJump)
|
|
||||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,10 +478,11 @@ class IRCodeGen(
|
|||||||
}
|
}
|
||||||
is PtIdentifier -> {
|
is PtIdentifier -> {
|
||||||
require(forLoop.variable.name == loopvar.scopedName)
|
require(forLoop.variable.name == loopvar.scopedName)
|
||||||
|
val elementDt = irType(iterable.type.elementType())
|
||||||
val iterableLength = symbolTable.getLength(iterable.name)
|
val iterableLength = symbolTable.getLength(iterable.name)
|
||||||
val loopvarSymbol = forLoop.variable.name
|
val loopvarSymbol = forLoop.variable.name
|
||||||
val indexReg = registers.next(IRDataType.BYTE)
|
val indexReg = registers.next(IRDataType.BYTE)
|
||||||
val tmpReg = registers.next(IRDataType.BYTE)
|
val tmpReg = registers.next(elementDt)
|
||||||
val loopLabel = createLabelName()
|
val loopLabel = createLabelName()
|
||||||
val endLabel = createLabelName()
|
val endLabel = createLabelName()
|
||||||
when {
|
when {
|
||||||
@ -480,9 +490,9 @@ class IRCodeGen(
|
|||||||
// iterate over a zero-terminated string
|
// iterate over a zero-terminated string
|
||||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
|
||||||
result += IRCodeChunk(loopLabel, null).also {
|
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.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)
|
result += translateNode(forLoop.statements)
|
||||||
val jumpChunk = IRCodeChunk(null, null)
|
val jumpChunk = IRCodeChunk(null, null)
|
||||||
@ -493,8 +503,7 @@ class IRCodeGen(
|
|||||||
}
|
}
|
||||||
iterable.type.isSplitWordArray -> {
|
iterable.type.isSplitWordArray -> {
|
||||||
// iterate over lsb/msb split word array
|
// iterate over lsb/msb split word array
|
||||||
val elementDt = iterable.type.elementType()
|
if(elementDt!=IRDataType.WORD)
|
||||||
if(!elementDt.isWord)
|
|
||||||
throw AssemblyError("weird dt")
|
throw AssemblyError("weird dt")
|
||||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
|
||||||
result += IRCodeChunk(loopLabel, null).also {
|
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=tmpRegMsb, reg2=indexReg, labelSymbol=iterable.name+"_msb")
|
||||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, labelSymbol=iterable.name+"_lsb")
|
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.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 += translateNode(forLoop.statements)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
@ -771,7 +780,7 @@ class IRCodeGen(
|
|||||||
code += if(factor==0.0) {
|
code += if(factor==0.0) {
|
||||||
IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = 0.0)
|
IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = 0.0)
|
||||||
} else {
|
} else {
|
||||||
IRInstruction(Opcode.MUL, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
|
IRInstruction(Opcode.MULS, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
@ -789,38 +798,40 @@ class IRCodeGen(
|
|||||||
val factorReg = registers.next(IRDataType.FLOAT)
|
val factorReg = registers.next(IRDataType.FLOAT)
|
||||||
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, immediateFp = factor)
|
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, immediateFp = factor)
|
||||||
code += if(knownAddress!=null)
|
code += if(knownAddress!=null)
|
||||||
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
IRInstruction(Opcode.MULSM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
IRInstruction(Opcode.MULSM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
||||||
}
|
}
|
||||||
return code
|
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)
|
val code = IRCodeChunk(null, null)
|
||||||
if(factor==1)
|
if(factor==1)
|
||||||
return code
|
return code
|
||||||
val pow2 = powersOfTwoInt.indexOf(factor)
|
val pow2 = powersOfTwoInt.indexOf(factor)
|
||||||
if(pow2==1) {
|
if(pow2==1) {
|
||||||
// just shift 1 bit
|
// just shift 1 bit
|
||||||
code += IRInstruction(Opcode.LSL, dt, reg1=reg)
|
code += IRInstruction(Opcode.LSL, irdt, reg1=reg)
|
||||||
}
|
}
|
||||||
else if(pow2>=1) {
|
else if(pow2>=1) {
|
||||||
// just shift multiple bits
|
// just shift multiple bits
|
||||||
val pow2reg = registers.next(IRDataType.BYTE)
|
val pow2reg = registers.next(IRDataType.BYTE)
|
||||||
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=pow2reg, immediate = pow2)
|
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 {
|
} else {
|
||||||
code += if (factor == 0) {
|
code += if (factor == 0) {
|
||||||
IRInstruction(Opcode.LOAD, dt, reg1=reg, immediate = 0)
|
IRInstruction(Opcode.LOAD, irdt, reg1=reg, immediate = 0)
|
||||||
} else {
|
} 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
|
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)
|
val code = IRCodeChunk(null, null)
|
||||||
if(factor==1)
|
if(factor==1)
|
||||||
return code
|
return code
|
||||||
@ -850,10 +861,11 @@ class IRCodeGen(
|
|||||||
else {
|
else {
|
||||||
val factorReg = registers.next(dt)
|
val factorReg = registers.next(dt)
|
||||||
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, immediate = factor)
|
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, immediate = factor)
|
||||||
|
val opcode = if(signed) Opcode.MULSM else Opcode.MULM
|
||||||
code += if(knownAddress!=null)
|
code += if(knownAddress!=null)
|
||||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, address = knownAddress)
|
IRInstruction(opcode, dt, reg1=factorReg, address = knownAddress)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, labelSymbol = symbol)
|
IRInstruction(opcode, dt, reg1=factorReg, labelSymbol = symbol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
@ -1070,7 +1082,7 @@ class IRCodeGen(
|
|||||||
}
|
}
|
||||||
// evaluate jump address expression into a register and jump indirectly to it
|
// evaluate jump address expression into a register and jump indirectly to it
|
||||||
val tr = expressionEval.translateExpression(goto.target)
|
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 += i
|
||||||
}
|
}
|
||||||
it += IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg)
|
it += IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg)
|
||||||
@ -1761,18 +1773,33 @@ class IRCodeGen(
|
|||||||
if(ret.children.size>1) {
|
if(ret.children.size>1) {
|
||||||
// note: multi-value returns are passed throug A or AY (for the first value) then cx16.R15 down to R0
|
// note: multi-value returns are passed throug A or AY (for the first value) then cx16.R15 down to R0
|
||||||
// (this allows unencumbered use of many Rx registers if you don't return that many values)
|
// (this allows unencumbered use of many Rx registers if you don't return that many values)
|
||||||
// make sure to assign the first value as the last in the sequence, to avoid clobbering the AY registers afterwards
|
// a floating point value is passed via FAC (just one fp value is possible)
|
||||||
|
|
||||||
val returnRegs = ret.definingISub()!!.returnsWhatWhere()
|
val returnRegs = ret.definingISub()!!.returnsWhatWhere()
|
||||||
val values = ret.children.zip(returnRegs)
|
val values = ret.children.zip(returnRegs)
|
||||||
|
// first all but the first return values
|
||||||
for ((value, register) in values.drop(1)) {
|
for ((value, register) in values.drop(1)) {
|
||||||
val tr = expressionEval.translateExpression(value as PtExpression)
|
val tr = expressionEval.translateExpression(value as PtExpression)
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
if(register.second.isFloat) {
|
||||||
result += setCpuRegister(register.first, irType(register.second), tr.resultReg, -1)
|
addToResult(result, tr, -1, tr.resultFpReg)
|
||||||
|
result += setCpuRegister(register.first, IRDataType.FLOAT, -1, tr.resultFpReg)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
result += setCpuRegister(register.first, irType(register.second), tr.resultReg, -1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// finally do the first of the return values (this avoids clobbering of its value in AY)
|
||||||
values.first().also { (value, register) ->
|
values.first().also { (value, register) ->
|
||||||
val tr = expressionEval.translateExpression(value as PtExpression)
|
val tr = expressionEval.translateExpression(value as PtExpression)
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
if(register.second.isFloat) {
|
||||||
result += setCpuRegister(register.first, irType(register.second), tr.resultReg, -1)
|
addToResult(result, tr, -1, tr.resultFpReg)
|
||||||
|
result += setCpuRegister(register.first, IRDataType.FLOAT, -1, tr.resultFpReg)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
result += setCpuRegister(register.first, irType(register.second), tr.resultReg, -1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
addInstr(result, IRInstruction(Opcode.RETURN), null)
|
addInstr(result, IRInstruction(Opcode.RETURN), null)
|
||||||
return result
|
return result
|
||||||
@ -1939,7 +1966,7 @@ class IRCodeGen(
|
|||||||
null -> when(registerOrFlag.statusflag) {
|
null -> when(registerOrFlag.statusflag) {
|
||||||
// TODO: do the statusflag argument as last
|
// TODO: do the statusflag argument as last
|
||||||
Statusflag.Pc -> chunk += IRInstruction(Opcode.LSR, paramDt, reg1=resultReg)
|
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")
|
else -> throw AssemblyError("unsupported register arg")
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeOnlyJoinChunks() {
|
private fun optimizeOnlyJoinChunks() {
|
||||||
|
// this chunk-joining is REQUIRED (optimization or no) to end up with a structurally sound chunk list
|
||||||
irprog.foreachSub { sub ->
|
irprog.foreachSub { sub ->
|
||||||
joinChunks(sub)
|
joinChunks(sub)
|
||||||
removeEmptyChunks(sub)
|
removeEmptyChunks(sub)
|
||||||
@ -84,7 +85,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
/*
|
/*
|
||||||
Empty Code chunk with label ->
|
Empty Code chunk with label ->
|
||||||
If next chunk has no label -> move label to next chunk, remove original
|
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.
|
If is last chunk -> keep chunk in place because of the label.
|
||||||
Empty Code chunk without label ->
|
Empty Code chunk without label ->
|
||||||
should not have been generated! ERROR.
|
should not have been generated! ERROR.
|
||||||
@ -111,14 +112,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
if(index>0) {
|
if(index>0) {
|
||||||
if (chunk.label == nextchunk.label)
|
if (chunk.label == nextchunk.label)
|
||||||
removeChunks += index
|
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
|
var changed = false
|
||||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||||
when (ins.opcode) {
|
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) {
|
if (ins.immediate == 1) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
changed = true
|
changed = true
|
||||||
@ -384,15 +377,19 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Opcode.AND -> {
|
Opcode.AND -> {
|
||||||
if (ins.immediate == 0) {
|
when (ins.immediate) {
|
||||||
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
|
0 -> {
|
||||||
changed = true
|
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
|
||||||
} else if (ins.immediate == 255 && ins.type == IRDataType.BYTE) {
|
changed = true
|
||||||
chunk.instructions.removeAt(idx)
|
}
|
||||||
changed = true
|
255 if ins.type == IRDataType.BYTE -> {
|
||||||
} else if (ins.immediate == 65535 && ins.type == IRDataType.WORD) {
|
chunk.instructions.removeAt(idx)
|
||||||
chunk.instructions.removeAt(idx)
|
changed = true
|
||||||
changed = true
|
}
|
||||||
|
65535 if ins.type == IRDataType.WORD -> {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Opcode.OR -> {
|
Opcode.OR -> {
|
||||||
|
@ -173,7 +173,7 @@ class IRUnusedCodeRemover(
|
|||||||
if(chunk!=null)
|
if(chunk!=null)
|
||||||
new+=chunk
|
new+=chunk
|
||||||
else
|
else
|
||||||
allLabeledChunks[label]?.let { new += it }
|
allLabeledChunks[label]?.let { c -> new += c }
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
new += instr.branchTarget!!
|
new += instr.branchTarget!!
|
||||||
@ -226,7 +226,7 @@ class IRUnusedCodeRemover(
|
|||||||
chunk.instructions.forEach {
|
chunk.instructions.forEach {
|
||||||
if(it.labelSymbol!=null) {
|
if(it.labelSymbol!=null) {
|
||||||
val chunkName = it.labelSymbol!!.substringBeforeLast('.')
|
val chunkName = it.labelSymbol!!.substringBeforeLast('.')
|
||||||
allLabeledChunks[chunkName]?.let { linkedChunks+=it }
|
allLabeledChunks[chunkName]?.let { c -> linkedChunks += c }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,8 @@
|
|||||||
package prog8.codegen.intermediate
|
package prog8.codegen.intermediate
|
||||||
|
|
||||||
import prog8.code.StArrayElement
|
import prog8.code.*
|
||||||
import prog8.code.StConstant
|
|
||||||
import prog8.code.StMemVar
|
|
||||||
import prog8.code.StMemorySlab
|
|
||||||
import prog8.code.StNodeType
|
|
||||||
import prog8.code.StStaticVariable
|
|
||||||
import prog8.code.SymbolTable
|
|
||||||
import prog8.code.core.DataType
|
import prog8.code.core.DataType
|
||||||
import prog8.intermediate.IRStArrayElement
|
import prog8.intermediate.*
|
||||||
import prog8.intermediate.IRStConstant
|
|
||||||
import prog8.intermediate.IRStMemVar
|
|
||||||
import prog8.intermediate.IRStMemorySlab
|
|
||||||
import prog8.intermediate.IRStStaticVariable
|
|
||||||
import prog8.intermediate.IRSymbolTable
|
|
||||||
|
|
||||||
|
|
||||||
fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
||||||
@ -37,7 +26,7 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
|||||||
val addrOfSymbol = arrayElt.addressOfSymbol
|
val addrOfSymbol = arrayElt.addressOfSymbol
|
||||||
if (addrOfSymbol != null) {
|
if (addrOfSymbol != null) {
|
||||||
require(addrOfSymbol.contains('.')) {
|
require(addrOfSymbol.contains('.')) {
|
||||||
"pointer var in array should be properly scoped: ${addrOfSymbol} in ${variable.name}"
|
"pointer var in array should be properly scoped: $addrOfSymbol in ${variable.name}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,9 +44,7 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
|||||||
else
|
else
|
||||||
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
|
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
|
||||||
|
|
||||||
val scopedName: String
|
|
||||||
if('.' in variable.name) {
|
if('.' in variable.name) {
|
||||||
scopedName = variable.name
|
|
||||||
return IRStStaticVariable(variable.name,
|
return IRStStaticVariable(variable.name,
|
||||||
variable.dt,
|
variable.dt,
|
||||||
variable.initializationNumericValue,
|
variable.initializationNumericValue,
|
||||||
@ -65,7 +52,8 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
|||||||
variable.initializationArrayValue?.map { convertArrayElt(it) },
|
variable.initializationArrayValue?.map { convertArrayElt(it) },
|
||||||
variable.length,
|
variable.length,
|
||||||
variable.zpwish,
|
variable.zpwish,
|
||||||
variable.align)
|
variable.align,
|
||||||
|
variable.dirty)
|
||||||
} else {
|
} else {
|
||||||
fun fixupAddressOfInArray(array: List<StArrayElement>?): List<IRStArrayElement>? {
|
fun fixupAddressOfInArray(array: List<StArrayElement>?): List<IRStArrayElement>? {
|
||||||
if(array==null)
|
if(array==null)
|
||||||
@ -81,7 +69,7 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
|||||||
}
|
}
|
||||||
return newArray
|
return newArray
|
||||||
}
|
}
|
||||||
scopedName = variable.scopedName
|
val scopedName = variable.scopedName
|
||||||
return IRStStaticVariable(scopedName,
|
return IRStStaticVariable(scopedName,
|
||||||
variable.dt,
|
variable.dt,
|
||||||
variable.initializationNumericValue,
|
variable.initializationNumericValue,
|
||||||
@ -89,16 +77,15 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
|||||||
fixupAddressOfInArray(variable.initializationArrayValue),
|
fixupAddressOfInArray(variable.initializationArrayValue),
|
||||||
variable.length,
|
variable.length,
|
||||||
variable.zpwish,
|
variable.zpwish,
|
||||||
variable.align
|
variable.align,
|
||||||
|
variable.dirty
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun convert(variable: StMemVar): IRStMemVar {
|
private fun convert(variable: StMemVar): IRStMemVar {
|
||||||
val scopedName: String
|
|
||||||
if('.' in variable.name) {
|
if('.' in variable.name) {
|
||||||
scopedName = variable.name
|
|
||||||
return IRStMemVar(
|
return IRStMemVar(
|
||||||
variable.name,
|
variable.name,
|
||||||
variable.dt,
|
variable.dt,
|
||||||
@ -106,7 +93,7 @@ private fun convert(variable: StMemVar): IRStMemVar {
|
|||||||
variable.length
|
variable.length
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
scopedName = try {
|
val scopedName = try {
|
||||||
variable.scopedName
|
variable.scopedName
|
||||||
} catch (_: UninitializedPropertyAccessException) {
|
} catch (_: UninitializedPropertyAccessException) {
|
||||||
variable.name
|
variable.name
|
||||||
|
@ -66,6 +66,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
|||||||
|
|
||||||
override fun noErrors(): Boolean = errors.isEmpty()
|
override fun noErrors(): Boolean = errors.isEmpty()
|
||||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||||
|
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
|
||||||
|
|
||||||
override fun report() {
|
override fun report() {
|
||||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||||
|
@ -53,7 +53,7 @@ class TestIRPeepholeOpt: FunSpec({
|
|||||||
irProg.chunks().single().instructions.size shouldBe 1
|
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)
|
val c1 = IRCodeChunk("main.start", null)
|
||||||
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label")
|
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label")
|
||||||
val c2 = IRCodeChunk("label", null)
|
val c2 = IRCodeChunk("label", null)
|
||||||
@ -69,13 +69,16 @@ class TestIRPeepholeOpt: FunSpec({
|
|||||||
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
|
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
|
||||||
val opt = IRPeepholeOptimizer(irProg)
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
opt.optimize(true, ErrorReporterForTests())
|
opt.optimize(true, ErrorReporterForTests())
|
||||||
irProg.chunks().size shouldBe 3
|
val chunks = irProg.chunks()
|
||||||
irProg.chunks()[0].label shouldBe "main.start"
|
chunks.size shouldBe 4
|
||||||
irProg.chunks()[1].label shouldBe "label2"
|
chunks[0].label shouldBe "main.start"
|
||||||
irProg.chunks()[2].label shouldBe "label3"
|
chunks[1].label shouldBe "label"
|
||||||
irProg.chunks()[0].isEmpty() shouldBe true
|
chunks[2].label shouldBe "label2"
|
||||||
irProg.chunks()[1].isEmpty() shouldBe false
|
chunks[3].label shouldBe "label3"
|
||||||
irProg.chunks()[2].isEmpty() shouldBe true
|
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 }
|
val instr = irProg.chunks().flatMap { it.instructions }
|
||||||
instr.size shouldBe 2
|
instr.size shouldBe 2
|
||||||
instr[0].opcode shouldBe Opcode.JUMP
|
instr[0].opcode shouldBe Opcode.JUMP
|
||||||
|
@ -49,9 +49,10 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"pi",
|
"pi",
|
||||||
DataType.forDt(BaseDataType.UBYTE),
|
DataType.UBYTE,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
@ -61,6 +62,7 @@ class TestVmCodeGen: FunSpec({
|
|||||||
DataType.arrayFor(BaseDataType.UBYTE),
|
DataType.arrayFor(BaseDataType.UBYTE),
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
3u,
|
3u,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
@ -70,15 +72,17 @@ class TestVmCodeGen: FunSpec({
|
|||||||
DataType.arrayFor(BaseDataType.UBYTE),
|
DataType.arrayFor(BaseDataType.UBYTE),
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
3u,
|
3u,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"xx",
|
"xx",
|
||||||
DataType.forDt(BaseDataType.WORD),
|
DataType.WORD,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
@ -86,7 +90,7 @@ class TestVmCodeGen: FunSpec({
|
|||||||
|
|
||||||
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
val target = PtAssignTarget(false, Position.DUMMY).also {
|
val target = PtAssignTarget(false, Position.DUMMY).also {
|
||||||
val targetIdx = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY).also { idx ->
|
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
|
||||||
idx.add(PtIdentifier("main.start.particleX",
|
idx.add(PtIdentifier("main.start.particleX",
|
||||||
DataType.arrayFor(BaseDataType.UBYTE),
|
DataType.arrayFor(BaseDataType.UBYTE),
|
||||||
Position.DUMMY))
|
Position.DUMMY))
|
||||||
@ -94,7 +98,7 @@ class TestVmCodeGen: FunSpec({
|
|||||||
}
|
}
|
||||||
it.add(targetIdx)
|
it.add(targetIdx)
|
||||||
}
|
}
|
||||||
val value = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)
|
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
|
||||||
value.add(PtIdentifier("main.start.particleDX",
|
value.add(PtIdentifier("main.start.particleDX",
|
||||||
DataType.arrayFor(BaseDataType.UBYTE),
|
DataType.arrayFor(BaseDataType.UBYTE),
|
||||||
Position.DUMMY))
|
Position.DUMMY))
|
||||||
@ -105,15 +109,15 @@ class TestVmCodeGen: FunSpec({
|
|||||||
|
|
||||||
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
||||||
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
|
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
}
|
}
|
||||||
prefixAssign.add(prefixTarget)
|
prefixAssign.add(prefixTarget)
|
||||||
prefixAssign.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
sub.add(prefixAssign)
|
sub.add(prefixAssign)
|
||||||
|
|
||||||
val numberAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
val numberAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
}
|
}
|
||||||
numberAssign.add(numberAssignTarget)
|
numberAssign.add(numberAssignTarget)
|
||||||
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
|
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
|
||||||
@ -121,10 +125,10 @@ class TestVmCodeGen: FunSpec({
|
|||||||
|
|
||||||
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
}
|
}
|
||||||
cxregAssign.add(cxregAssignTarget)
|
cxregAssign.add(cxregAssignTarget)
|
||||||
cxregAssign.add(PtIdentifier("cx16.r0", DataType.forDt(BaseDataType.UWORD), Position.DUMMY))
|
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
|
||||||
sub.add(cxregAssign)
|
sub.add(cxregAssign)
|
||||||
|
|
||||||
block.add(sub)
|
block.add(sub)
|
||||||
@ -132,7 +136,7 @@ class TestVmCodeGen: FunSpec({
|
|||||||
|
|
||||||
// define the "cx16.r0" virtual register
|
// define the "cx16.r0" virtual register
|
||||||
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||||
cx16block.add(PtMemMapped("r0", DataType.forDt(BaseDataType.UWORD), 100u, null, Position.DUMMY))
|
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||||
program.add(cx16block)
|
program.add(cx16block)
|
||||||
|
|
||||||
val options = getTestOptions()
|
val options = getTestOptions()
|
||||||
@ -164,40 +168,41 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"f1",
|
"f1",
|
||||||
DataType.forDt(BaseDataType.FLOAT),
|
DataType.FLOAT,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
val if1 = PtIfElse(Position.DUMMY)
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||||
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp1.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
cmp1.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
||||||
if1.add(cmp1)
|
if1.add(cmp1)
|
||||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if1)
|
sub.add(if1)
|
||||||
val if2 = PtIfElse(Position.DUMMY)
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||||
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp2.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
cmp2.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
||||||
if2.add(cmp2)
|
if2.add(cmp2)
|
||||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if2)
|
sub.add(if2)
|
||||||
val if3 = PtIfElse(Position.DUMMY)
|
val if3 = PtIfElse(Position.DUMMY)
|
||||||
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||||
cmp3.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp3.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
cmp3.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
||||||
if3.add(cmp3)
|
if3.add(cmp3)
|
||||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if3)
|
sub.add(if3)
|
||||||
val if4 = PtIfElse(Position.DUMMY)
|
val if4 = PtIfElse(Position.DUMMY)
|
||||||
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||||
cmp4.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp4.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
cmp4.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
||||||
if4.add(cmp4)
|
if4.add(cmp4)
|
||||||
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
@ -235,40 +240,41 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"f1",
|
"f1",
|
||||||
DataType.forDt(BaseDataType.FLOAT),
|
DataType.FLOAT,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
val if1 = PtIfElse(Position.DUMMY)
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||||
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
if1.add(cmp1)
|
if1.add(cmp1)
|
||||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if1)
|
sub.add(if1)
|
||||||
val if2 = PtIfElse(Position.DUMMY)
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||||
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
if2.add(cmp2)
|
if2.add(cmp2)
|
||||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if2)
|
sub.add(if2)
|
||||||
val if3 = PtIfElse(Position.DUMMY)
|
val if3 = PtIfElse(Position.DUMMY)
|
||||||
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||||
cmp3.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp3.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
cmp3.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
if3.add(cmp3)
|
if3.add(cmp3)
|
||||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if3)
|
sub.add(if3)
|
||||||
val if4 = PtIfElse(Position.DUMMY)
|
val if4 = PtIfElse(Position.DUMMY)
|
||||||
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||||
cmp4.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp4.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
cmp4.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
if4.add(cmp4)
|
if4.add(cmp4)
|
||||||
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
@ -302,24 +308,25 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"f1",
|
"f1",
|
||||||
DataType.forDt(BaseDataType.FLOAT),
|
DataType.FLOAT,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
val if1 = PtIfElse(Position.DUMMY)
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||||
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
if1.add(cmp1)
|
if1.add(cmp1)
|
||||||
if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
||||||
if1.add(PtNodeGroup())
|
if1.add(PtNodeGroup())
|
||||||
sub.add(if1)
|
sub.add(if1)
|
||||||
val if2 = PtIfElse(Position.DUMMY)
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
val cmp2 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||||
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
if2.add(cmp2)
|
if2.add(cmp2)
|
||||||
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
||||||
@ -357,40 +364,41 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"sb1",
|
"sb1",
|
||||||
DataType.forDt(BaseDataType.BYTE),
|
DataType.BYTE,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
val if1 = PtIfElse(Position.DUMMY)
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||||
cmp1.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
cmp1.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
cmp1.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
||||||
if1.add(cmp1)
|
if1.add(cmp1)
|
||||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if1)
|
sub.add(if1)
|
||||||
val if2 = PtIfElse(Position.DUMMY)
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||||
cmp2.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
cmp2.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
cmp2.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
||||||
if2.add(cmp2)
|
if2.add(cmp2)
|
||||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if2)
|
sub.add(if2)
|
||||||
val if3 = PtIfElse(Position.DUMMY)
|
val if3 = PtIfElse(Position.DUMMY)
|
||||||
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||||
cmp3.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
cmp3.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
cmp3.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
||||||
if3.add(cmp3)
|
if3.add(cmp3)
|
||||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if3)
|
sub.add(if3)
|
||||||
val if4 = PtIfElse(Position.DUMMY)
|
val if4 = PtIfElse(Position.DUMMY)
|
||||||
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||||
cmp4.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
cmp4.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
cmp4.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
||||||
if4.add(cmp4)
|
if4.add(cmp4)
|
||||||
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
@ -428,40 +436,41 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"sb1",
|
"sb1",
|
||||||
DataType.forDt(BaseDataType.BYTE),
|
DataType.BYTE,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
val if1 = PtIfElse(Position.DUMMY)
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||||
cmp1.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
cmp1.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
cmp1.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
||||||
if1.add(cmp1)
|
if1.add(cmp1)
|
||||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if1)
|
sub.add(if1)
|
||||||
val if2 = PtIfElse(Position.DUMMY)
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||||
cmp2.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
cmp2.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
cmp2.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
||||||
if2.add(cmp2)
|
if2.add(cmp2)
|
||||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if2)
|
sub.add(if2)
|
||||||
val if3 = PtIfElse(Position.DUMMY)
|
val if3 = PtIfElse(Position.DUMMY)
|
||||||
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||||
cmp3.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
cmp3.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
cmp3.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
||||||
if3.add(cmp3)
|
if3.add(cmp3)
|
||||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
sub.add(if3)
|
sub.add(if3)
|
||||||
val if4 = PtIfElse(Position.DUMMY)
|
val if4 = PtIfElse(Position.DUMMY)
|
||||||
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||||
cmp4.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
cmp4.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
cmp4.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
||||||
if4.add(cmp4)
|
if4.add(cmp4)
|
||||||
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
@ -495,24 +504,25 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
sub.add(PtVariable(
|
sub.add(PtVariable(
|
||||||
"ub1",
|
"ub1",
|
||||||
DataType.forDt(BaseDataType.BYTE),
|
DataType.BYTE,
|
||||||
ZeropageWish.DONTCARE,
|
ZeropageWish.DONTCARE,
|
||||||
0u,
|
0u,
|
||||||
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Position.DUMMY
|
Position.DUMMY
|
||||||
))
|
))
|
||||||
val if1 = PtIfElse(Position.DUMMY)
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||||
cmp1.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
|
cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||||
cmp1.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
|
cmp1.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
|
||||||
if1.add(cmp1)
|
if1.add(cmp1)
|
||||||
if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
||||||
if1.add(PtNodeGroup())
|
if1.add(PtNodeGroup())
|
||||||
sub.add(if1)
|
sub.add(if1)
|
||||||
val if2 = PtIfElse(Position.DUMMY)
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
val cmp2 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||||
cmp2.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
|
cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||||
cmp2.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
|
cmp2.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
|
||||||
if2.add(cmp2)
|
if2.add(cmp2)
|
||||||
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also {it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also {it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
||||||
@ -543,7 +553,7 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val extsub = PtAsmSub("routine", PtAsmSub.Address(null, null, 0x5000u), setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
|
val extsub = PtAsmSub("routine", PtAsmSub.Address(null, null, 0x5000u), setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
|
||||||
block.add(extsub)
|
block.add(extsub)
|
||||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||||
val call = PtFunctionCall("main.routine", true, DataType.forDt(BaseDataType.UNDEFINED), Position.DUMMY)
|
val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY)
|
||||||
sub.add(call)
|
sub.add(call)
|
||||||
block.add(sub)
|
block.add(sub)
|
||||||
program.add(block)
|
program.add(block)
|
||||||
@ -554,11 +564,8 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||||
irChunks.size shouldBe 1
|
irChunks.size shouldBe 1
|
||||||
irChunks[0].instructions.size shouldBe 2
|
irChunks[0].instructions.size shouldBe 1
|
||||||
val preparecallInstr = irChunks[0].instructions[0]
|
val callInstr = irChunks[0].instructions[0]
|
||||||
preparecallInstr.opcode shouldBe Opcode.PREPARECALL
|
|
||||||
preparecallInstr.immediate shouldBe 0
|
|
||||||
val callInstr = irChunks[0].instructions[1]
|
|
||||||
callInstr.opcode shouldBe Opcode.CALL
|
callInstr.opcode shouldBe Opcode.CALL
|
||||||
callInstr.address shouldBe 0x5000
|
callInstr.address shouldBe 0x5000
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,7 @@ import prog8.ast.FatalAstException
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.expressions.FunctionCallExpression
|
import prog8.ast.expressions.FunctionCallExpression
|
||||||
import prog8.ast.expressions.NumericLiteral
|
import prog8.ast.expressions.NumericLiteral
|
||||||
import prog8.code.core.BaseDataType
|
import prog8.code.core.*
|
||||||
import prog8.code.core.Position
|
|
||||||
import prog8.code.core.isInteger
|
|
||||||
import prog8.code.core.isIntegerOrBool
|
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
|
|
||||||
@ -70,52 +67,45 @@ class ConstExprEvaluator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
if(right.type.isIntegerOrBool) {
|
||||||
if(right.type.isIntegerOrBool) {
|
val leftDt = left.type
|
||||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
|
if(leftDt.isByteOrBool)
|
||||||
}
|
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() xor right.number.toInt()) and 255).toDouble(), left.position)
|
||||||
} else if(left.type==BaseDataType.UWORD) {
|
else if(leftDt.isInteger) {
|
||||||
if(right.type.isInteger) {
|
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position)
|
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() xor right.number.toInt()) and 65535).toDouble(), left.position)
|
||||||
}
|
else if (leftDt == BaseDataType.LONG)
|
||||||
} else if(left.type==BaseDataType.LONG) {
|
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
|
||||||
if(right.type.isInteger) {
|
|
||||||
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw ExpressionError("cannot calculate $left ^ $right", left.position)
|
throw ExpressionError("cannot calculate $left ^ $right", left.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
if(right.type.isIntegerOrBool) {
|
||||||
if(right.type.isIntegerOrBool) {
|
val leftDt = left.type
|
||||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
|
if(leftDt.isByteOrBool)
|
||||||
}
|
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() or right.number.toInt()) and 255).toDouble(), left.position)
|
||||||
} else if(left.type==BaseDataType.UWORD) {
|
else if(leftDt.isInteger) {
|
||||||
if(right.type.isInteger) {
|
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position)
|
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() or right.number.toInt()) and 65535).toDouble(), left.position)
|
||||||
}
|
else if (leftDt == BaseDataType.LONG)
|
||||||
} else if(left.type==BaseDataType.LONG) {
|
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
|
||||||
if(right.type.isInteger) {
|
|
||||||
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw ExpressionError("cannot calculate $left | $right", left.position)
|
throw ExpressionError("cannot calculate $left | $right", left.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
if(right.type.isIntegerOrBool) {
|
||||||
if(right.type.isIntegerOrBool) {
|
val leftDt = left.type
|
||||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
|
if(leftDt.isByteOrBool)
|
||||||
}
|
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() and right.number.toInt()) and 255).toDouble(), left.position)
|
||||||
} else if(left.type==BaseDataType.UWORD) {
|
else if(leftDt.isInteger) {
|
||||||
if(right.type.isInteger) {
|
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position)
|
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() and right.number.toInt()) and 65535).toDouble(), left.position)
|
||||||
}
|
else if (leftDt == BaseDataType.LONG)
|
||||||
} else if(left.type==BaseDataType.LONG) {
|
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
|
||||||
if(right.type.isInteger) {
|
|
||||||
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw ExpressionError("cannot calculate $left & $right", 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))
|
return listOf(IAstModification.ReplaceNode(expr, newArray, parent))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val leftTarget = (expr.left as? IdentifierReference)?.targetVarDecl(program)
|
val leftTarget = (expr.left as? IdentifierReference)?.targetVarDecl()
|
||||||
if(leftTarget!=null && leftTarget.origin==VarDeclOrigin.ARRAYLITERAL)
|
if(leftTarget!=null && leftTarget.origin==VarDeclOrigin.ARRAYLITERAL)
|
||||||
throw FatalAstException("shouldn't see an array literal converted to an autovar here")
|
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()
|
val constIndex = arrayIndexedExpression.indexer.constIndex()
|
||||||
if (constIndex != null) {
|
if (constIndex != null) {
|
||||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl()
|
||||||
if(arrayVar!=null) {
|
if(arrayVar!=null) {
|
||||||
val array =arrayVar.value as? ArrayLiteral
|
val array =arrayVar.value as? ArrayLiteral
|
||||||
if(array!=null) {
|
if(array!=null) {
|
||||||
@ -387,7 +387,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
|||||||
val rangeTo = iterableRange.to as? NumericLiteral
|
val rangeTo = iterableRange.to as? NumericLiteral
|
||||||
if(rangeFrom==null || rangeTo==null) return noModifications
|
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
|
val stepLiteral = iterableRange.step as? NumericLiteral
|
||||||
require(loopvar.datatype.sub == null)
|
require(loopvar.datatype.sub == null)
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.FatalAstException
|
import prog8.ast.*
|
||||||
import prog8.ast.IFunctionCall
|
|
||||||
import prog8.ast.IStatementContainer
|
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.UndefinedSymbolError
|
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
@ -457,7 +452,7 @@ internal class ConstantIdentifierReplacer(
|
|||||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||||
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
|
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
|
||||||
if(constRange!=null) {
|
if(constRange!=null) {
|
||||||
val rangeType = rangeExpr.inferType(program).getOr(DataType.forDt(BaseDataType.UBYTE))
|
val rangeType = rangeExpr.inferType(program).getOr(DataType.UBYTE)
|
||||||
return if(rangeType.isByte) {
|
return if(rangeType.isByte) {
|
||||||
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
|
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
|
||||||
constRange.map { NumericLiteral(rangeType.base, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
constRange.map { NumericLiteral(rangeType.base, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||||
|
@ -531,7 +531,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
// useless msb() of byte value that was typecasted to word, replace with 0
|
// useless msb() of byte value that was typecasted to word, replace with 0
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(
|
||||||
functionCallExpr,
|
functionCallExpr,
|
||||||
NumericLiteral(valueDt.getOr(DataType.forDt(BaseDataType.UBYTE)).base, 0.0, arg.expression.position),
|
NumericLiteral(valueDt.getOr(DataType.UBYTE).base, 0.0, arg.expression.position),
|
||||||
parent))
|
parent))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -546,7 +546,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
// useless msb() of byte value, replace with 0
|
// useless msb() of byte value, replace with 0
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(
|
||||||
functionCallExpr,
|
functionCallExpr,
|
||||||
NumericLiteral(argDt.getOr(DataType.forDt(BaseDataType.UBYTE)).base, 0.0, arg.position),
|
NumericLiteral(argDt.getOr(DataType.UBYTE).base, 0.0, arg.position),
|
||||||
parent))
|
parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -560,7 +560,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(functionCallExpr.target.nameInSource == listOf("strings", "contains")) {
|
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) {
|
if(target?.value is StringLiteral) {
|
||||||
errors.info("for actual strings, use a regular containment check instead: 'char in string'", functionCallExpr.position)
|
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)
|
val contains = ContainmentCheck(functionCallExpr.args[1], functionCallExpr.args[0], functionCallExpr.position)
|
||||||
|
@ -13,10 +13,10 @@ import prog8.code.core.InternalCompilerException
|
|||||||
import prog8.code.target.VMTarget
|
import prog8.code.target.VMTarget
|
||||||
|
|
||||||
|
|
||||||
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.values.size==0
|
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() {
|
class Inliner(private val program: Program, private val options: CompilationOptions): AstWalker() {
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
|||||||
else if (stmt.values[0] is IdentifierReference) {
|
else if (stmt.values[0] is IdentifierReference) {
|
||||||
makeFullyScoped(stmt.values[0] as IdentifierReference)
|
makeFullyScoped(stmt.values[0] as IdentifierReference)
|
||||||
true
|
true
|
||||||
} else if (stmt.values[0]!! is IFunctionCall && (stmt.values[0] as IFunctionCall).args.size <= 1 && (stmt.values[0] as IFunctionCall).args.all { it is NumericLiteral || it is IdentifierReference }) {
|
} else if (stmt.values[0] is IFunctionCall && (stmt.values[0] as IFunctionCall).args.size <= 1 && (stmt.values[0] as IFunctionCall).args.all { it is NumericLiteral || it is IdentifierReference }) {
|
||||||
if (stmt.values[0] is FunctionCallExpression) {
|
if (stmt.values[0] is FunctionCallExpression) {
|
||||||
makeFullyScoped(stmt.values[0] as FunctionCallExpression)
|
makeFullyScoped(stmt.values[0] as FunctionCallExpression)
|
||||||
true
|
true
|
||||||
@ -105,7 +105,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
|||||||
|
|
||||||
if (subroutine.inline && subroutine.statements.size > 1) {
|
if (subroutine.inline && subroutine.statements.size > 1) {
|
||||||
require(subroutine.statements.size == 2 && isEmptyReturn(subroutine.statements[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) {
|
private fun makeFullyScoped(call: FunctionCallStatement) {
|
||||||
makeFullyScoped(call.target)
|
makeFullyScoped(call.target)
|
||||||
call.target.targetSubroutine(program)?.let { sub ->
|
call.target.targetSubroutine()?.let { sub ->
|
||||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||||
val scopedArgs = makeScopedArgs(call.args)
|
val scopedArgs = makeScopedArgs(call.args)
|
||||||
if(scopedArgs.any()) {
|
if(scopedArgs.any()) {
|
||||||
@ -134,7 +134,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
|||||||
|
|
||||||
private fun makeFullyScoped(call: FunctionCallExpression) {
|
private fun makeFullyScoped(call: FunctionCallExpression) {
|
||||||
makeFullyScoped(call.target)
|
makeFullyScoped(call.target)
|
||||||
call.target.targetSubroutine(program)?.let { sub ->
|
call.target.targetSubroutine()?.let { sub ->
|
||||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||||
val scopedArgs = makeScopedArgs(call.args)
|
val scopedArgs = makeScopedArgs(call.args)
|
||||||
if(scopedArgs.any()) {
|
if(scopedArgs.any()) {
|
||||||
|
@ -5,11 +5,7 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.code.core.AssociativeOperators
|
import prog8.code.core.*
|
||||||
import prog8.code.core.BaseDataType
|
|
||||||
import prog8.code.core.CompilationOptions
|
|
||||||
import prog8.code.core.IErrorReporter
|
|
||||||
import prog8.code.core.Position
|
|
||||||
|
|
||||||
|
|
||||||
class StatementOptimizer(private val program: Program,
|
class StatementOptimizer(private val program: Program,
|
||||||
@ -37,8 +33,8 @@ class StatementOptimizer(private val program: Program,
|
|||||||
} else {
|
} else {
|
||||||
arg as? IdentifierReference
|
arg as? IdentifierReference
|
||||||
}
|
}
|
||||||
if(stringVar!=null && stringVar.wasStringLiteral(program)) {
|
if(stringVar!=null && stringVar.wasStringLiteral()) {
|
||||||
val string = stringVar.targetVarDecl(program)?.value as? StringLiteral
|
val string = stringVar.targetVarDecl()?.value as? StringLiteral
|
||||||
if(string!=null) {
|
if(string!=null) {
|
||||||
val pos = functionCallStatement.position
|
val pos = functionCallStatement.position
|
||||||
if (string.value.length == 1) {
|
if (string.value.length == 1) {
|
||||||
@ -156,7 +152,7 @@ class StatementOptimizer(private val program: Program,
|
|||||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
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!=null) {
|
||||||
if(iterable.datatype.isString) {
|
if(iterable.datatype.isString) {
|
||||||
val sv = iterable.value as StringLiteral
|
val sv = iterable.value as StringLiteral
|
||||||
@ -315,17 +311,6 @@ class StatementOptimizer(private val program: Program,
|
|||||||
val bexpr=assignment.value as? BinaryExpression
|
val bexpr=assignment.value as? BinaryExpression
|
||||||
if(bexpr!=null) {
|
if(bexpr!=null) {
|
||||||
val rightCv = bexpr.right.constValue(program)?.number
|
val rightCv = bexpr.right.constValue(program)?.number
|
||||||
if(bexpr.operator=="-" && rightCv==null && targetIDt.isInteger) {
|
|
||||||
if(bexpr.right.isSimple && bexpr.right isSameAs assignment.target) {
|
|
||||||
// X = value - X --> X = -X ; X += value (to avoid need of stack-evaluation, for integers)
|
|
||||||
val negation = PrefixExpression("-", bexpr.right.copy(), bexpr.position)
|
|
||||||
val addValue = Assignment(assignment.target.copy(), BinaryExpression(bexpr.right, "+", bexpr.left, bexpr.position), AssignmentOrigin.OPTIMIZER, assignment.position)
|
|
||||||
return listOf(
|
|
||||||
IAstModification.ReplaceNode(bexpr, negation, assignment),
|
|
||||||
IAstModification.InsertAfter(assignment, addValue, parent as IStatementContainer)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rightCv != null && assignment.target isSameAs bexpr.left) {
|
if (rightCv != null && assignment.target isSameAs bexpr.left) {
|
||||||
// assignments of the form: X = X <operator> <expr>
|
// assignments of the form: X = X <operator> <expr>
|
||||||
@ -516,6 +501,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
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,9 +8,9 @@ import prog8.ast.expressions.TypecastExpression
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
|
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||||
import prog8.code.core.ICompilationTarget
|
import prog8.code.core.ICompilationTarget
|
||||||
import prog8.code.core.IErrorReporter
|
import prog8.code.core.IErrorReporter
|
||||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
|
||||||
import prog8.compiler.CallGraph
|
import prog8.compiler.CallGraph
|
||||||
|
|
||||||
|
|
||||||
@ -27,10 +27,10 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
|
|
||||||
program.allBlocks.singleOrNull { it.name=="sys" } ?.let {
|
program.allBlocks.singleOrNull { it.name=="sys" } ?.let {
|
||||||
val subroutines = it.statements.filterIsInstance<Subroutine>()
|
val subroutines = it.statements.filterIsInstance<Subroutine>()
|
||||||
val push = subroutines.single { it.name == "push" }
|
val push = subroutines.single { s -> s.name == "push" }
|
||||||
val pushw = subroutines.single { it.name == "pushw" }
|
val pushw = subroutines.single { s -> s.name == "pushw" }
|
||||||
val pop = subroutines.single { it.name == "pop" }
|
val pop = subroutines.single { s -> s.name == "pop" }
|
||||||
val popw = subroutines.single { it.name == "popw" }
|
val popw = subroutines.single { s -> s.name == "popw" }
|
||||||
neverRemoveSubroutines.add(push)
|
neverRemoveSubroutines.add(push)
|
||||||
neverRemoveSubroutines.add(pushw)
|
neverRemoveSubroutines.add(pushw)
|
||||||
neverRemoveSubroutines.add(pop)
|
neverRemoveSubroutines.add(pop)
|
||||||
@ -39,8 +39,8 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
|
|
||||||
program.allBlocks.singleOrNull { it.name=="floats" } ?.let {
|
program.allBlocks.singleOrNull { it.name=="floats" } ?.let {
|
||||||
val subroutines = it.statements.filterIsInstance<Subroutine>()
|
val subroutines = it.statements.filterIsInstance<Subroutine>()
|
||||||
val push = subroutines.single { it.name == "push" }
|
val push = subroutines.single { s -> s.name == "push" }
|
||||||
val pop = subroutines.single { it.name == "pop" }
|
val pop = subroutines.single { s -> s.name == "pop" }
|
||||||
neverRemoveSubroutines.add(push)
|
neverRemoveSubroutines.add(push)
|
||||||
neverRemoveSubroutines.add(pop)
|
neverRemoveSubroutines.add(pop)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
%import shared_cbm_diskio
|
%import shared_cbm_diskio
|
||||||
|
|
||||||
; No alterations here; everything is taken from the shared module.
|
diskio {
|
||||||
|
%option no_symbol_prefixing, ignore_unused
|
||||||
|
|
||||||
|
; No alterations here; everything is taken from the shared module.
|
||||||
|
}
|
||||||
|
@ -419,12 +419,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 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_BOOL = sizeof(bool)
|
||||||
const ubyte SIZEOF_BYTE = 1
|
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||||
const ubyte SIZEOF_UBYTE = 1
|
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||||
const ubyte SIZEOF_WORD = 2
|
const ubyte SIZEOF_WORD = sizeof(word)
|
||||||
const ubyte SIZEOF_UWORD = 2
|
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||||
const ubyte SIZEOF_FLOAT = 5
|
const ubyte SIZEOF_LONG = sizeof(long)
|
||||||
|
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||||
const byte MIN_BYTE = -128
|
const byte MIN_BYTE = -128
|
||||||
const byte MAX_BYTE = 127
|
const byte MAX_BYTE = 127
|
||||||
const ubyte MIN_UBYTE = 0
|
const ubyte MIN_UBYTE = 0
|
||||||
@ -445,7 +446,6 @@ sys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
asmsub save_prog8_internals() {
|
asmsub save_prog8_internals() {
|
||||||
; TODO: Romable
|
|
||||||
%asm {{
|
%asm {{
|
||||||
lda P8ZP_SCRATCH_B1
|
lda P8ZP_SCRATCH_B1
|
||||||
sta save_SCRATCH_ZPB1
|
sta save_SCRATCH_ZPB1
|
||||||
@ -460,10 +460,12 @@ sys {
|
|||||||
lda P8ZP_SCRATCH_W2+1
|
lda P8ZP_SCRATCH_W2+1
|
||||||
sta save_SCRATCH_ZPWORD2+1
|
sta save_SCRATCH_ZPWORD2+1
|
||||||
rts
|
rts
|
||||||
save_SCRATCH_ZPB1 .byte 0
|
.section BSS
|
||||||
save_SCRATCH_ZPREG .byte 0
|
save_SCRATCH_ZPB1 .byte ?
|
||||||
save_SCRATCH_ZPWORD1 .word 0
|
save_SCRATCH_ZPREG .byte ?
|
||||||
save_SCRATCH_ZPWORD2 .word 0
|
save_SCRATCH_ZPWORD1 .word ?
|
||||||
|
save_SCRATCH_ZPWORD2 .word ?
|
||||||
|
.send BSS
|
||||||
; !notreached!
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -489,8 +491,8 @@ save_SCRATCH_ZPWORD2 .word 0
|
|||||||
asmsub set_irq(uword handler @AY) clobbers(A) {
|
asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
sta _modified+1
|
sta _vector
|
||||||
sty _modified+2
|
sty _vector+1
|
||||||
lda #<_irq_handler
|
lda #<_irq_handler
|
||||||
sta cbm.CINV
|
sta cbm.CINV
|
||||||
lda #>_irq_handler
|
lda #>_irq_handler
|
||||||
@ -500,8 +502,8 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
|
|||||||
_irq_handler
|
_irq_handler
|
||||||
jsr sys.save_prog8_internals
|
jsr sys.save_prog8_internals
|
||||||
cld
|
cld
|
||||||
_modified
|
|
||||||
jsr $ffff ; modified
|
jsr _run_custom
|
||||||
pha
|
pha
|
||||||
jsr sys.restore_prog8_internals
|
jsr sys.restore_prog8_internals
|
||||||
pla
|
pla
|
||||||
@ -516,6 +518,12 @@ _modified
|
|||||||
tax
|
tax
|
||||||
pla
|
pla
|
||||||
rti
|
rti
|
||||||
|
_run_custom
|
||||||
|
jmp (_vector)
|
||||||
|
.section BSS
|
||||||
|
_vector .word ?
|
||||||
|
.send BSS
|
||||||
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,8 +534,8 @@ asmsub restore_irq() clobbers(A) {
|
|||||||
sta cbm.CINV
|
sta cbm.CINV
|
||||||
lda #>cbm.IRQDFRT
|
lda #>cbm.IRQDFRT
|
||||||
sta cbm.CINV+1
|
sta cbm.CINV+1
|
||||||
lda #0
|
lda #1
|
||||||
sta c64.IREQMASK ; disable raster irq
|
sta c64.IREQMASK ; enable raster irq
|
||||||
lda #%10000001
|
lda #%10000001
|
||||||
sta c64.CIA1ICR ; restore CIA1 irq
|
sta c64.CIA1ICR ; restore CIA1 irq
|
||||||
cli
|
cli
|
||||||
@ -538,8 +546,8 @@ asmsub restore_irq() clobbers(A) {
|
|||||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
sta _modified+1
|
sta _vector
|
||||||
sty _modified+2
|
sty _vector+1
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
ldy cx16.r0+1
|
ldy cx16.r0+1
|
||||||
jsr _setup_raster_irq
|
jsr _setup_raster_irq
|
||||||
@ -553,8 +561,8 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
|||||||
_raster_irq_handler
|
_raster_irq_handler
|
||||||
jsr sys.save_prog8_internals
|
jsr sys.save_prog8_internals
|
||||||
cld
|
cld
|
||||||
_modified
|
|
||||||
jsr $ffff ; modified
|
jsr _run_custom
|
||||||
pha
|
pha
|
||||||
jsr sys.restore_prog8_internals
|
jsr sys.restore_prog8_internals
|
||||||
lda #$ff
|
lda #$ff
|
||||||
@ -568,6 +576,12 @@ _modified
|
|||||||
tax
|
tax
|
||||||
pla
|
pla
|
||||||
rti
|
rti
|
||||||
|
_run_custom
|
||||||
|
jmp (_vector)
|
||||||
|
.section BSS
|
||||||
|
_vector .word ?
|
||||||
|
.send BSS
|
||||||
|
|
||||||
|
|
||||||
_setup_raster_irq
|
_setup_raster_irq
|
||||||
pha
|
pha
|
||||||
@ -882,7 +896,7 @@ _no_msb_size
|
|||||||
sta p8_sys_startup.cleanup_at_exit._exitcode
|
sta p8_sys_startup.cleanup_at_exit._exitcode
|
||||||
lda #0
|
lda #0
|
||||||
rol a
|
rol a
|
||||||
sta p8_sys_startup.cleanup_at_exit._exitcodeCarry
|
sta p8_sys_startup.cleanup_at_exit._exitcarry
|
||||||
stx p8_sys_startup.cleanup_at_exit._exitcodeX
|
stx p8_sys_startup.cleanup_at_exit._exitcodeX
|
||||||
sty p8_sys_startup.cleanup_at_exit._exitcodeY
|
sty p8_sys_startup.cleanup_at_exit._exitcodeY
|
||||||
ldx prog8_lib.orig_stackpointer
|
ldx prog8_lib.orig_stackpointer
|
||||||
@ -1057,7 +1071,6 @@ cx16 {
|
|||||||
&byte r15sH = $1bff
|
&byte r15sH = $1bff
|
||||||
|
|
||||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||||
; TODO: Romable
|
|
||||||
%asm {{
|
%asm {{
|
||||||
ldy #31
|
ldy #31
|
||||||
- lda cx16.r0,y
|
- lda cx16.r0,y
|
||||||
@ -1066,9 +1079,11 @@ cx16 {
|
|||||||
bpl -
|
bpl -
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
.section BSS
|
||||||
_cx16_vreg_storage
|
_cx16_vreg_storage
|
||||||
.word 0,0,0,0,0,0,0,0
|
.word ?,?,?,?,?,?,?,?
|
||||||
.word 0,0,0,0,0,0,0,0
|
.word ?,?,?,?,?,?,?,?
|
||||||
|
.send BSS
|
||||||
; !notreached!
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -1138,16 +1153,21 @@ asmsub cleanup_at_exit() {
|
|||||||
sta $ff00 ; default bank 15
|
sta $ff00 ; default bank 15
|
||||||
jsr cbm.CLRCHN ; reset i/o channels
|
jsr cbm.CLRCHN ; reset i/o channels
|
||||||
jsr enable_runstop_and_charsetswitch
|
jsr enable_runstop_and_charsetswitch
|
||||||
_exitcodeCarry = *+1
|
lda _exitcarry
|
||||||
lda #0
|
|
||||||
lsr a
|
lsr a
|
||||||
_exitcode = *+1
|
lda _exitcode
|
||||||
lda #0 ; exit code possibly modified in exit()
|
ldx _exitcodeX
|
||||||
_exitcodeX = *+1
|
ldy _exitcodeY
|
||||||
ldx #0
|
|
||||||
_exitcodeY = *+1
|
|
||||||
ldy #0
|
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
.section BSS
|
||||||
|
_exitcarry .byte ?
|
||||||
|
_exitcode .byte ?
|
||||||
|
_exitcodeX .byte ?
|
||||||
|
_exitcodeY .byte ?
|
||||||
|
.send BSS
|
||||||
|
|
||||||
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,15 +302,16 @@ asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y)
|
|||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda _screenrows+1,y
|
lda _screenrows+1,y
|
||||||
sta _mod+2
|
sta P8ZP_SCRATCH_W1+1
|
||||||
txa
|
txa
|
||||||
clc
|
clc
|
||||||
adc _screenrows,y
|
adc _screenrows,y
|
||||||
sta _mod+1
|
sta P8ZP_SCRATCH_W1
|
||||||
bcc +
|
bcc +
|
||||||
inc _mod+2
|
inc P8ZP_SCRATCH_W1+1
|
||||||
+ pla
|
+ pla
|
||||||
_mod sta $ffff ; modified
|
ldy #0
|
||||||
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
rts
|
rts
|
||||||
|
|
||||||
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
||||||
@ -326,14 +327,15 @@ asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
|||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda setchr._screenrows+1,y
|
lda setchr._screenrows+1,y
|
||||||
sta _mod+2
|
sta P8ZP_SCRATCH_W1+1
|
||||||
pla
|
pla
|
||||||
clc
|
clc
|
||||||
adc setchr._screenrows,y
|
adc setchr._screenrows,y
|
||||||
sta _mod+1
|
sta P8ZP_SCRATCH_W1
|
||||||
bcc _mod
|
bcc +
|
||||||
inc _mod+2
|
inc P8ZP_SCRATCH_W1+1
|
||||||
_mod lda $ffff ; modified
|
+ ldy #0
|
||||||
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -346,15 +348,16 @@ asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A, Y) {
|
|||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda _colorrows+1,y
|
lda _colorrows+1,y
|
||||||
sta _mod+2
|
sta P8ZP_SCRATCH_W1+1
|
||||||
txa
|
txa
|
||||||
clc
|
clc
|
||||||
adc _colorrows,y
|
adc _colorrows,y
|
||||||
sta _mod+1
|
sta P8ZP_SCRATCH_W1
|
||||||
bcc +
|
bcc +
|
||||||
inc _mod+2
|
inc P8ZP_SCRATCH_W1+1
|
||||||
+ pla
|
+ pla
|
||||||
_mod sta $ffff ; modified
|
ldy #0
|
||||||
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
rts
|
rts
|
||||||
|
|
||||||
_colorrows .word $d800 + range(0, 1000, 40)
|
_colorrows .word $d800 + range(0, 1000, 40)
|
||||||
@ -370,14 +373,15 @@ asmsub getclr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
|||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda setclr._colorrows+1,y
|
lda setclr._colorrows+1,y
|
||||||
sta _mod+2
|
sta P8ZP_SCRATCH_W1+1
|
||||||
pla
|
pla
|
||||||
clc
|
clc
|
||||||
adc setclr._colorrows,y
|
adc setclr._colorrows,y
|
||||||
sta _mod+1
|
sta P8ZP_SCRATCH_W1
|
||||||
bcc _mod
|
bcc +
|
||||||
inc _mod+2
|
inc P8ZP_SCRATCH_W1+1
|
||||||
_mod lda $ffff ; modified
|
+ ldy #0
|
||||||
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -385,25 +389,28 @@ _mod lda $ffff ; modified
|
|||||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||||
; ---- set char+color at the given position on the screen
|
; ---- set char+color at the given position on the screen
|
||||||
%asm {{
|
%asm {{
|
||||||
|
_charptr = P8ZP_SCRATCH_W1
|
||||||
|
_colorptr = P8ZP_SCRATCH_W2
|
||||||
lda row
|
lda row
|
||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda setchr._screenrows+1,y
|
lda setchr._screenrows+1,y
|
||||||
sta _charmod+2
|
sta _charptr+1
|
||||||
adc #$d4
|
adc #$d4
|
||||||
sta _colormod+2
|
sta _colorptr+1
|
||||||
lda setchr._screenrows,y
|
lda setchr._screenrows,y
|
||||||
clc
|
clc
|
||||||
adc col
|
adc col
|
||||||
sta _charmod+1
|
sta _charptr
|
||||||
sta _colormod+1
|
sta _colorptr
|
||||||
bcc +
|
bcc +
|
||||||
inc _charmod+2
|
inc _charptr+1
|
||||||
inc _colormod+2
|
inc _colorptr+1
|
||||||
+ lda character
|
+ lda character
|
||||||
_charmod sta $ffff ; modified
|
ldy #0
|
||||||
|
sta (_charptr),y
|
||||||
lda charcolor
|
lda charcolor
|
||||||
_colormod sta $ffff ; modified
|
sta (_colorptr),y
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
%import shared_cbm_diskio
|
%import shared_cbm_diskio
|
||||||
|
|
||||||
; No alterations here; everything is taken from the shared module.
|
diskio {
|
||||||
|
%option no_symbol_prefixing, ignore_unused
|
||||||
|
|
||||||
|
; No alterations here; everything is taken from the shared module.
|
||||||
|
}
|
||||||
|
@ -4,8 +4,9 @@ FL_ONE_const .byte 129 ; 1.0
|
|||||||
FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
|
FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
|
||||||
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
|
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
|
||||||
|
|
||||||
|
.section BSS
|
||||||
floats_temp_var .byte 0,0,0,0,0 ; temporary storage for a float
|
floats_temp_var .byte ?,?,?,?,? ; temporary storage for a float
|
||||||
|
.send BSS
|
||||||
|
|
||||||
ub2float .proc
|
ub2float .proc
|
||||||
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
||||||
@ -178,10 +179,10 @@ dec_var_f .proc
|
|||||||
jmp MOVMF
|
jmp MOVMF
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
.section BSS
|
||||||
fmath_float1 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
fmath_float1 .byte ?,?,?,?,? ; storage for a mflpt5 value
|
||||||
fmath_float2 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
fmath_float2 .byte ?,?,?,?,? ; storage for a mflpt5 value
|
||||||
|
.send BSS
|
||||||
|
|
||||||
var_fac1_less_f .proc
|
var_fac1_less_f .proc
|
||||||
; -- is the float in FAC1 < the variable AY? Result in A. Clobbers X.
|
; -- is the float in FAC1 < the variable AY? Result in A. Clobbers X.
|
||||||
|
@ -193,6 +193,7 @@ graphics {
|
|||||||
|
|
||||||
if length!=0 {
|
if length!=0 {
|
||||||
%asm {{
|
%asm {{
|
||||||
|
_pixeladdr = P8ZP_SCRATCH_W1
|
||||||
lda p8v_length
|
lda p8v_length
|
||||||
and #7
|
and #7
|
||||||
sta p8v_separate_pixels
|
sta p8v_separate_pixels
|
||||||
@ -203,37 +204,35 @@ graphics {
|
|||||||
lsr p8v_length+1
|
lsr p8v_length+1
|
||||||
ror p8v_length
|
ror p8v_length
|
||||||
lda p8v_pixaddr
|
lda p8v_pixaddr
|
||||||
sta _modified+1
|
sta _pixeladdr
|
||||||
lda p8v_pixaddr+1
|
lda p8v_pixaddr+1
|
||||||
sta _modified+2
|
sta _pixeladdr+1
|
||||||
lda p8v_length
|
lda p8v_length
|
||||||
ora p8v_length+1
|
ora p8v_length+1
|
||||||
beq _zero
|
beq _zero
|
||||||
ldy p8v_length
|
ldy p8v_length
|
||||||
|
sty P8ZP_SCRATCH_B1 ; length
|
||||||
ldx #$ff
|
ldx #$ff
|
||||||
_modified stx $ffff ; modified
|
_more txa
|
||||||
lda _modified+1
|
ldy #0
|
||||||
|
sta (_pixeladdr),y
|
||||||
|
lda _pixeladdr
|
||||||
clc
|
clc
|
||||||
adc #8
|
adc #8
|
||||||
sta _modified+1
|
sta _pixeladdr
|
||||||
bcc +
|
bcc +
|
||||||
inc _modified+2
|
inc _pixeladdr+1
|
||||||
+ dey
|
+ dec P8ZP_SCRATCH_B1 ; length
|
||||||
bne _modified
|
bne _more
|
||||||
_zero
|
_zero
|
||||||
ldy p8v_separate_pixels
|
ldy p8v_separate_pixels
|
||||||
beq hline_zero2
|
beq _end
|
||||||
lda _modified+1
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
lda _modified+2
|
|
||||||
sta P8ZP_SCRATCH_W1+1
|
|
||||||
lda hline_filled_right,y
|
lda hline_filled_right,y
|
||||||
ldy #0
|
ldy #0
|
||||||
ora (P8ZP_SCRATCH_W1),y
|
ora (_pixeladdr),y
|
||||||
sta (P8ZP_SCRATCH_W1),y
|
sta (_pixeladdr),y
|
||||||
jmp hline_zero2
|
_end rts
|
||||||
hline_filled_right .byte 0, %10000000, %11000000, %11100000, %11110000, %11111000, %11111100, %11111110
|
hline_filled_right .byte 0, %10000000, %11000000, %11100000, %11110000, %11111000, %11111100, %11111110
|
||||||
hline_zero2
|
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -349,10 +349,10 @@ inline asmsub getbanks() -> ubyte @A {
|
|||||||
; retrieve arguments
|
; retrieve arguments
|
||||||
ldy #$01
|
ldy #$01
|
||||||
lda (P8ZP_SCRATCH_W1),y ; grab low byte of target address
|
lda (P8ZP_SCRATCH_W1),y ; grab low byte of target address
|
||||||
sta _jmpfar+1
|
sta _jmpfar_vec
|
||||||
iny
|
iny
|
||||||
lda (P8ZP_SCRATCH_W1),y ; now the high byte
|
lda (P8ZP_SCRATCH_W1),y ; now the high byte
|
||||||
sta _jmpfar+2
|
sta _jmpfar_vec+1
|
||||||
iny
|
iny
|
||||||
lda (P8ZP_SCRATCH_W1),y ; then the target bank
|
lda (P8ZP_SCRATCH_W1),y ; then the target bank
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
@ -377,7 +377,7 @@ inline asmsub getbanks() -> ubyte @A {
|
|||||||
lda P8ZP_SCRATCH_W2
|
lda P8ZP_SCRATCH_W2
|
||||||
ldy P8ZP_SCRATCH_W2+1
|
ldy P8ZP_SCRATCH_W2+1
|
||||||
plp
|
plp
|
||||||
jsr _jmpfar ; do the actual call
|
jsr _jsrfar ; do the actual call
|
||||||
; restore bank without clobbering status flags and A register
|
; restore bank without clobbering status flags and A register
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
php
|
php
|
||||||
@ -390,8 +390,13 @@ inline asmsub getbanks() -> ubyte @A {
|
|||||||
lda P8ZP_SCRATCH_W1
|
lda P8ZP_SCRATCH_W1
|
||||||
plp
|
plp
|
||||||
rts
|
rts
|
||||||
|
_jsrfar jmp (_jmpfar_vec)
|
||||||
|
|
||||||
_jmpfar jmp $0000 ; modified
|
.section BSS
|
||||||
|
_jmpfar_vec .word ?
|
||||||
|
.send BSS
|
||||||
|
|
||||||
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -434,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 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_BOOL = sizeof(bool)
|
||||||
const ubyte SIZEOF_BYTE = 1
|
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||||
const ubyte SIZEOF_UBYTE = 1
|
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||||
const ubyte SIZEOF_WORD = 2
|
const ubyte SIZEOF_WORD = sizeof(word)
|
||||||
const ubyte SIZEOF_UWORD = 2
|
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||||
const ubyte SIZEOF_FLOAT = 5
|
const ubyte SIZEOF_LONG = sizeof(long)
|
||||||
|
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||||
const byte MIN_BYTE = -128
|
const byte MIN_BYTE = -128
|
||||||
const byte MAX_BYTE = 127
|
const byte MAX_BYTE = 127
|
||||||
const ubyte MIN_UBYTE = 0
|
const ubyte MIN_UBYTE = 0
|
||||||
@ -460,7 +466,6 @@ sys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
asmsub save_prog8_internals() {
|
asmsub save_prog8_internals() {
|
||||||
; TODO: Romable
|
|
||||||
%asm {{
|
%asm {{
|
||||||
lda P8ZP_SCRATCH_B1
|
lda P8ZP_SCRATCH_B1
|
||||||
sta save_SCRATCH_ZPB1
|
sta save_SCRATCH_ZPB1
|
||||||
@ -475,10 +480,12 @@ sys {
|
|||||||
lda P8ZP_SCRATCH_W2+1
|
lda P8ZP_SCRATCH_W2+1
|
||||||
sta save_SCRATCH_ZPWORD2+1
|
sta save_SCRATCH_ZPWORD2+1
|
||||||
rts
|
rts
|
||||||
save_SCRATCH_ZPB1 .byte 0
|
.section BSS
|
||||||
save_SCRATCH_ZPREG .byte 0
|
save_SCRATCH_ZPB1 .byte ?
|
||||||
save_SCRATCH_ZPWORD1 .word 0
|
save_SCRATCH_ZPREG .byte ?
|
||||||
save_SCRATCH_ZPWORD2 .word 0
|
save_SCRATCH_ZPWORD1 .word ?
|
||||||
|
save_SCRATCH_ZPWORD2 .word ?
|
||||||
|
.send BSS
|
||||||
; !notreached!
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -504,8 +511,8 @@ save_SCRATCH_ZPWORD2 .word 0
|
|||||||
asmsub set_irq(uword handler @AY) clobbers(A) {
|
asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
sta _modified+1
|
sta _vector
|
||||||
sty _modified+2
|
sty _vector+1
|
||||||
lda #<_irq_handler
|
lda #<_irq_handler
|
||||||
sta cbm.CINV
|
sta cbm.CINV
|
||||||
lda #>_irq_handler
|
lda #>_irq_handler
|
||||||
@ -515,8 +522,8 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
|
|||||||
_irq_handler
|
_irq_handler
|
||||||
jsr sys.save_prog8_internals
|
jsr sys.save_prog8_internals
|
||||||
cld
|
cld
|
||||||
_modified
|
|
||||||
jsr $ffff ; modified
|
jsr _run_custom
|
||||||
pha
|
pha
|
||||||
jsr sys.restore_prog8_internals
|
jsr sys.restore_prog8_internals
|
||||||
pla
|
pla
|
||||||
@ -531,6 +538,13 @@ _modified
|
|||||||
tax
|
tax
|
||||||
pla
|
pla
|
||||||
rti
|
rti
|
||||||
|
|
||||||
|
_run_custom
|
||||||
|
jmp (_vector)
|
||||||
|
.section BSS
|
||||||
|
_vector .word ?
|
||||||
|
.send BSS
|
||||||
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -553,8 +567,8 @@ asmsub restore_irq() clobbers(A) {
|
|||||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
sta _modified+1
|
sta _vector
|
||||||
sty _modified+2
|
sty _vector+1
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
ldy cx16.r0+1
|
ldy cx16.r0+1
|
||||||
jsr _setup_raster_irq
|
jsr _setup_raster_irq
|
||||||
@ -568,8 +582,8 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
|||||||
_raster_irq_handler
|
_raster_irq_handler
|
||||||
jsr sys.save_prog8_internals
|
jsr sys.save_prog8_internals
|
||||||
cld
|
cld
|
||||||
_modified
|
|
||||||
jsr $ffff ; modified
|
jsr _run_custom
|
||||||
pha
|
pha
|
||||||
jsr sys.restore_prog8_internals
|
jsr sys.restore_prog8_internals
|
||||||
lda #$ff
|
lda #$ff
|
||||||
@ -584,6 +598,12 @@ _modified
|
|||||||
pla
|
pla
|
||||||
rti
|
rti
|
||||||
|
|
||||||
|
_run_custom
|
||||||
|
jmp (_vector)
|
||||||
|
.section BSS
|
||||||
|
_vector .word ?
|
||||||
|
.send BSS
|
||||||
|
|
||||||
_setup_raster_irq
|
_setup_raster_irq
|
||||||
pha
|
pha
|
||||||
lda #%01111111
|
lda #%01111111
|
||||||
@ -898,7 +918,7 @@ _no_msb_size
|
|||||||
sta p8_sys_startup.cleanup_at_exit._exitcode
|
sta p8_sys_startup.cleanup_at_exit._exitcode
|
||||||
lda #0
|
lda #0
|
||||||
rol a
|
rol a
|
||||||
sta p8_sys_startup.cleanup_at_exit._exitcodeCarry
|
sta p8_sys_startup.cleanup_at_exit._exitcarry
|
||||||
stx p8_sys_startup.cleanup_at_exit._exitcodeX
|
stx p8_sys_startup.cleanup_at_exit._exitcodeX
|
||||||
sty p8_sys_startup.cleanup_at_exit._exitcodeY
|
sty p8_sys_startup.cleanup_at_exit._exitcodeY
|
||||||
ldx prog8_lib.orig_stackpointer
|
ldx prog8_lib.orig_stackpointer
|
||||||
@ -1073,7 +1093,6 @@ cx16 {
|
|||||||
&byte r15sH = $cfff
|
&byte r15sH = $cfff
|
||||||
|
|
||||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||||
; TODO: Romable
|
|
||||||
%asm {{
|
%asm {{
|
||||||
ldy #31
|
ldy #31
|
||||||
- lda cx16.r0,y
|
- lda cx16.r0,y
|
||||||
@ -1082,9 +1101,11 @@ cx16 {
|
|||||||
bpl -
|
bpl -
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
.section BSS
|
||||||
_cx16_vreg_storage
|
_cx16_vreg_storage
|
||||||
.word 0,0,0,0,0,0,0,0
|
.word ?,?,?,?,?,?,?,?
|
||||||
.word 0,0,0,0,0,0,0,0
|
.word ?,?,?,?,?,?,?,?
|
||||||
|
.send BSS
|
||||||
; !notreached!
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -1170,16 +1191,21 @@ asmsub cleanup_at_exit() {
|
|||||||
jsr cbm.MEMTOP ; adjust MEMTOP down again
|
jsr cbm.MEMTOP ; adjust MEMTOP down again
|
||||||
jsr cbm.CLRCHN ; reset i/o channels
|
jsr cbm.CLRCHN ; reset i/o channels
|
||||||
jsr enable_runstop_and_charsetswitch
|
jsr enable_runstop_and_charsetswitch
|
||||||
_exitcodeCarry = *+1
|
lda _exitcarry
|
||||||
lda #0
|
|
||||||
lsr a
|
lsr a
|
||||||
_exitcode = *+1
|
lda _exitcode
|
||||||
lda #0 ; exit code possibly modified in exit()
|
ldx _exitcodeX
|
||||||
_exitcodeX = *+1
|
ldy _exitcodeY
|
||||||
ldx #0
|
|
||||||
_exitcodeY = *+1
|
|
||||||
ldy #0
|
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
.section BSS
|
||||||
|
_exitcarry .byte ?
|
||||||
|
_exitcode .byte ?
|
||||||
|
_exitcodeX .byte ?
|
||||||
|
_exitcodeY .byte ?
|
||||||
|
.send BSS
|
||||||
|
|
||||||
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,15 +305,16 @@ asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y)
|
|||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda _screenrows+1,y
|
lda _screenrows+1,y
|
||||||
sta _mod+2
|
sta P8ZP_SCRATCH_W1+1
|
||||||
txa
|
txa
|
||||||
clc
|
clc
|
||||||
adc _screenrows,y
|
adc _screenrows,y
|
||||||
sta _mod+1
|
sta P8ZP_SCRATCH_W1
|
||||||
bcc +
|
bcc +
|
||||||
inc _mod+2
|
inc P8ZP_SCRATCH_W1+1
|
||||||
+ pla
|
+ pla
|
||||||
_mod sta $ffff ; modified
|
ldy #0
|
||||||
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
rts
|
rts
|
||||||
|
|
||||||
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
||||||
@ -329,14 +330,15 @@ asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
|||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda setchr._screenrows+1,y
|
lda setchr._screenrows+1,y
|
||||||
sta _mod+2
|
sta P8ZP_SCRATCH_W1+1
|
||||||
pla
|
pla
|
||||||
clc
|
clc
|
||||||
adc setchr._screenrows,y
|
adc setchr._screenrows,y
|
||||||
sta _mod+1
|
sta P8ZP_SCRATCH_W1
|
||||||
bcc _mod
|
bcc +
|
||||||
inc _mod+2
|
inc P8ZP_SCRATCH_W1+1
|
||||||
_mod lda $ffff ; modified
|
+ ldy #0
|
||||||
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -349,15 +351,16 @@ asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A, Y) {
|
|||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda _colorrows+1,y
|
lda _colorrows+1,y
|
||||||
sta _mod+2
|
sta P8ZP_SCRATCH_W1+1
|
||||||
txa
|
txa
|
||||||
clc
|
clc
|
||||||
adc _colorrows,y
|
adc _colorrows,y
|
||||||
sta _mod+1
|
sta P8ZP_SCRATCH_W1
|
||||||
bcc +
|
bcc +
|
||||||
inc _mod+2
|
inc P8ZP_SCRATCH_W1+1
|
||||||
+ pla
|
+ pla
|
||||||
_mod sta $ffff ; modified
|
ldy #0
|
||||||
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
rts
|
rts
|
||||||
|
|
||||||
_colorrows .word $d800 + range(0, 1000, 40)
|
_colorrows .word $d800 + range(0, 1000, 40)
|
||||||
@ -373,14 +376,15 @@ asmsub getclr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
|||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda setclr._colorrows+1,y
|
lda setclr._colorrows+1,y
|
||||||
sta _mod+2
|
sta P8ZP_SCRATCH_W1+1
|
||||||
pla
|
pla
|
||||||
clc
|
clc
|
||||||
adc setclr._colorrows,y
|
adc setclr._colorrows,y
|
||||||
sta _mod+1
|
sta P8ZP_SCRATCH_W1
|
||||||
bcc _mod
|
bcc +
|
||||||
inc _mod+2
|
inc P8ZP_SCRATCH_W1+1
|
||||||
_mod lda $ffff ; modified
|
+ ldy #0
|
||||||
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -388,25 +392,28 @@ _mod lda $ffff ; modified
|
|||||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||||
; ---- set char+color at the given position on the screen
|
; ---- set char+color at the given position on the screen
|
||||||
%asm {{
|
%asm {{
|
||||||
|
_charptr = P8ZP_SCRATCH_W1
|
||||||
|
_colorptr = P8ZP_SCRATCH_W2
|
||||||
lda row
|
lda row
|
||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda setchr._screenrows+1,y
|
lda setchr._screenrows+1,y
|
||||||
sta _charmod+2
|
sta _charptr+1
|
||||||
adc #$d4
|
adc #$d4
|
||||||
sta _colormod+2
|
sta _colorptr+1
|
||||||
lda setchr._screenrows,y
|
lda setchr._screenrows,y
|
||||||
clc
|
clc
|
||||||
adc col
|
adc col
|
||||||
sta _charmod+1
|
sta _charptr
|
||||||
sta _colormod+1
|
sta _colorptr
|
||||||
bcc +
|
bcc +
|
||||||
inc _charmod+2
|
inc _charptr+1
|
||||||
inc _colormod+2
|
inc _colorptr+1
|
||||||
+ lda character
|
+ lda character
|
||||||
_charmod sta $ffff ; modified
|
ldy #0
|
||||||
|
sta (_charptr),y
|
||||||
lda charcolor
|
lda charcolor
|
||||||
_colormod sta $ffff ; modified
|
sta (_colorptr),y
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ conv {
|
|||||||
|
|
||||||
; ----- number conversions to decimal strings ----
|
; ----- number conversions to decimal strings ----
|
||||||
|
|
||||||
str @shared string_out = "????????????????" ; result buffer for the string conversion routines
|
ubyte[16] @shared string_out ; result buffer for the string conversion routines (note: uses uninitialized ARRAY instead of STR, to force it to be allocated in BSS area so it's ROM-compatible)
|
||||||
|
|
||||||
asmsub str_ub0(ubyte value @A) clobbers(X) -> str @AY {
|
asmsub str_ub0(ubyte value @A) clobbers(X) -> str @AY {
|
||||||
; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total)
|
; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total)
|
||||||
@ -352,7 +352,6 @@ asmsub str2word(str string @AY) -> word @AY {
|
|||||||
; the number may be preceded by a + or - sign but may NOT contain spaces
|
; the number may be preceded by a + or - sign but may NOT contain spaces
|
||||||
; (any non-digit character will terminate the number string that is parsed)
|
; (any non-digit character will terminate the number string that is parsed)
|
||||||
; result in AY, number of characters processed also remains in cx16.r15 if you want to use it!! (0 = error)
|
; result in AY, number of characters processed also remains in cx16.r15 if you want to use it!! (0 = error)
|
||||||
; TODO: Romable
|
|
||||||
%asm {{
|
%asm {{
|
||||||
_result = P8ZP_SCRATCH_W1
|
_result = P8ZP_SCRATCH_W1
|
||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
@ -403,7 +402,9 @@ _digit
|
|||||||
+ iny
|
+ iny
|
||||||
bne _parse
|
bne _parse
|
||||||
; never reached
|
; never reached
|
||||||
_negative .byte 0
|
.section BSS
|
||||||
|
_negative .byte ?
|
||||||
|
.send BSS
|
||||||
; !notreached!
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -540,7 +541,6 @@ asmsub internal_uword2decimal (uword value @AY) -> ubyte @Y, ubyte @A, ubyte @
|
|||||||
; output in internal_uword2decimal.decTenThousands, decThousands, decHundreds, decTens, decOnes
|
; output in internal_uword2decimal.decTenThousands, decThousands, decHundreds, decTens, decOnes
|
||||||
; (these are terminated by a zero byte so they can be easily printed)
|
; (these are terminated by a zero byte so they can be easily printed)
|
||||||
; also returns Y = 100's, A = 10's, X = 1's
|
; also returns Y = 100's, A = 10's, X = 1's
|
||||||
; TODO: Romable
|
|
||||||
|
|
||||||
%asm {{
|
%asm {{
|
||||||
|
|
||||||
@ -701,12 +701,14 @@ ShiftedBcdTab
|
|||||||
.byte $30,$31,$32,$33,$34,$38,$39,$3A,$3B,$3C
|
.byte $30,$31,$32,$33,$34,$38,$39,$3A,$3B,$3C
|
||||||
.byte $40,$41,$42,$43,$44,$48,$49,$4A,$4B,$4C
|
.byte $40,$41,$42,$43,$44,$48,$49,$4A,$4B,$4C
|
||||||
|
|
||||||
decTenThousands .byte 0
|
.section BSS
|
||||||
decThousands .byte 0
|
decTenThousands .byte ?
|
||||||
decHundreds .byte 0
|
decThousands .byte ?
|
||||||
decTens .byte 0
|
decHundreds .byte ?
|
||||||
decOnes .byte 0
|
decTens .byte ?
|
||||||
.byte 0 ; zero-terminate the decimal output string
|
decOnes .byte ?
|
||||||
|
.byte ? ; zero-terminate the decimal output string, set to 0 by bss init mechanisms
|
||||||
|
.send BSS
|
||||||
; !notreached!
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -758,7 +760,9 @@ asmsub internal_uword2hex (uword value @AY) clobbers(A,Y) {
|
|||||||
sta output+2
|
sta output+2
|
||||||
sty output+3
|
sty output+3
|
||||||
rts
|
rts
|
||||||
output .text "0000", $00 ; 0-terminated output buffer (to make printing easier)
|
.section BSS
|
||||||
|
output .fill 5 ; 0-terminated output buffer (to make printing easier)
|
||||||
|
.send BSS
|
||||||
; !notreached!
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,16 @@
|
|||||||
; NOTE: If you experience weird behavior with these routines and you are using them
|
; NOTE: If you experience weird behavior with these routines and you are using them
|
||||||
; in the X16 emulator using HostFs, please try again with an SD-card image instead first.
|
; in the X16 emulator using HostFs, please try again with an SD-card image instead first.
|
||||||
; It is possible that there are still small differences between HostFS and actual CBM DOS in the emulator.
|
; It is possible that there are still small differences between HostFS and actual CBM DOS in the emulator.
|
||||||
;
|
|
||||||
; About the secondary addresses:
|
; About the secondary addresses:
|
||||||
; for writes (new files or overwrites), you can use 1 (without the mode string) or 2-14 (with the mode string)
|
; for writes (new files or overwrites), you can use 1 (without the mode string) or 2-14 (with the mode string)
|
||||||
; for reads (existing files) you can use 0 or 2-14 (mode string is optional)
|
; for reads (existing files) you can use 0 or 2-14 (mode string is optional)
|
||||||
; for modify mode (new files* or existing files), you must use 2-14, and the mode string ,s,m is required
|
; for modify mode (new files* or existing files), you must use 2-14, and the mode string ,s,m is required
|
||||||
|
|
||||||
|
; About the Dos/Drive error status message:
|
||||||
|
; The routines don't usually read/clear the dos/drive error status and message. ("blinking red led")
|
||||||
|
; In case of an error, you usually have to read/clear that yourself (with status() for example).
|
||||||
|
|
||||||
|
|
||||||
%import textio
|
%import textio
|
||||||
%import conv
|
%import conv
|
||||||
@ -21,6 +25,7 @@ diskio {
|
|||||||
|
|
||||||
const ubyte READ_IO_CHANNEL=12
|
const ubyte READ_IO_CHANNEL=12
|
||||||
const ubyte WRITE_IO_CHANNEL=13
|
const ubyte WRITE_IO_CHANNEL=13
|
||||||
|
const ubyte STATUS_EOF=$40
|
||||||
|
|
||||||
ubyte @shared drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
|
ubyte @shared drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
|
||||||
|
|
||||||
@ -94,7 +99,7 @@ io_error:
|
|||||||
cbm.CLRCHN() ; restore default i/o devices
|
cbm.CLRCHN() ; restore default i/o devices
|
||||||
cbm.CLOSE(READ_IO_CHANNEL)
|
cbm.CLOSE(READ_IO_CHANNEL)
|
||||||
|
|
||||||
if status!=0 and status & $40 == 0 { ; bit 6=end of file
|
if status!=0 and status & STATUS_EOF == 0 { ; bit 6=end of file
|
||||||
txt.print("\ni/o error, status: ")
|
txt.print("\ni/o error, status: ")
|
||||||
txt.print_ub(status)
|
txt.print_ub(status)
|
||||||
txt.nl()
|
txt.nl()
|
||||||
@ -346,7 +351,7 @@ close_end:
|
|||||||
if cbm.READST()==0 {
|
if cbm.READST()==0 {
|
||||||
iteration_in_progress = true
|
iteration_in_progress = true
|
||||||
void cbm.CHRIN() ; read first byte to test for file not found
|
void cbm.CHRIN() ; read first byte to test for file not found
|
||||||
if cbm.READST()==0 {
|
if cbm.READST() & ~STATUS_EOF == 0 {
|
||||||
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
|
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
|
||||||
void cbm.OPEN() ; re-open the file
|
void cbm.OPEN() ; re-open the file
|
||||||
cbm.CLRCHN() ; reset default i/o channels
|
cbm.CLRCHN() ; reset default i/o channels
|
||||||
@ -375,7 +380,7 @@ close_end:
|
|||||||
cbm.CLRCHN() ; reset default i/o channels
|
cbm.CLRCHN() ; reset default i/o channels
|
||||||
if cx16.r0L!=0 {
|
if cx16.r0L!=0 {
|
||||||
f_close()
|
f_close()
|
||||||
if cx16.r0L & $40 == 0
|
if cx16.r0L & STATUS_EOF == 0
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return 1
|
return 1
|
||||||
@ -395,7 +400,7 @@ close_end:
|
|||||||
if msb(bufferpointer) == $c0
|
if msb(bufferpointer) == $c0
|
||||||
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
|
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
|
||||||
num_bytes -= readsize
|
num_bytes -= readsize
|
||||||
if cbm.READST() & $40 !=0 {
|
if cbm.READST() & STATUS_EOF !=0 {
|
||||||
f_close() ; end of file, close it
|
f_close() ; end of file, close it
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -406,39 +411,39 @@ close_end:
|
|||||||
byte_read_loop: ; fallback if MACPTR isn't supported on the device
|
byte_read_loop: ; fallback if MACPTR isn't supported on the device
|
||||||
%asm {{
|
%asm {{
|
||||||
lda bufferpointer
|
lda bufferpointer
|
||||||
sta m_in_buffer+1
|
sta P8ZP_SCRATCH_W1
|
||||||
lda bufferpointer+1
|
lda bufferpointer+1
|
||||||
sta m_in_buffer+2
|
sta P8ZP_SCRATCH_W1+1
|
||||||
}}
|
}}
|
||||||
while num_bytes!=0 {
|
while num_bytes!=0 {
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr cbm.CHRIN
|
jsr cbm.CHRIN
|
||||||
m_in_buffer sta $ffff
|
sta (P8ZP_SCRATCH_W1)
|
||||||
inc m_in_buffer+1
|
inc P8ZP_SCRATCH_W1
|
||||||
bne +
|
bne +
|
||||||
inc m_in_buffer+2
|
inc P8ZP_SCRATCH_W1+1
|
||||||
+
|
+
|
||||||
}}
|
}}
|
||||||
|
list_blocks++
|
||||||
cx16.r0L = cbm.READST()
|
cx16.r0L = cbm.READST()
|
||||||
if_nz {
|
if_nz {
|
||||||
f_close()
|
f_close()
|
||||||
cbm.CLRCHN() ; reset default i/o channels
|
cbm.CLRCHN() ; reset default i/o channels
|
||||||
if cx16.r0L & $40 !=0 ; eof?
|
if cx16.r0L & STATUS_EOF !=0 ; eof?
|
||||||
return list_blocks ; number of bytes read
|
return list_blocks ; number of bytes read
|
||||||
return 0 ; error.
|
return 0 ; error.
|
||||||
}
|
}
|
||||||
list_blocks++
|
|
||||||
num_bytes--
|
num_bytes--
|
||||||
}
|
}
|
||||||
cbm.CLRCHN() ; reset default i/o channels
|
cbm.CLRCHN() ; reset default i/o channels
|
||||||
return list_blocks ; number of bytes read
|
return list_blocks ; number of bytes read
|
||||||
}
|
}
|
||||||
|
|
||||||
; optimized for Commander X16 to use MACPTR block read kernal call
|
|
||||||
sub f_read_all(uword bufferpointer) -> uword {
|
sub f_read_all(uword bufferpointer) -> uword {
|
||||||
; -- read the full contents of the file, returns number of bytes read.
|
; -- read the full rest of the file, returns number of bytes read.
|
||||||
; It is assumed the file size is less than 64 K.
|
; It is assumed the file size is less than 64 K.
|
||||||
; NOTE: cannot be used to load into VRAM. Use vload() or call cx16.MACPTR() yourself with the vera data register as address.
|
; NOTE: cannot be used to load into VRAM. Use vload() or call cx16.MACPTR() yourself with the vera data register as address.
|
||||||
|
; Usually you will just be using load() / load_raw() or vload() / vload_raw() to read entire files!
|
||||||
if not iteration_in_progress
|
if not iteration_in_progress
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@ -446,6 +451,8 @@ m_in_buffer sta $ffff
|
|||||||
uword total_read = 0
|
uword total_read = 0
|
||||||
while cbm.READST()==0 {
|
while cbm.READST()==0 {
|
||||||
cx16.r0 = f_read(bufferpointer, 256)
|
cx16.r0 = f_read(bufferpointer, 256)
|
||||||
|
if cx16.r0==0
|
||||||
|
break
|
||||||
total_read += cx16.r0
|
total_read += cx16.r0
|
||||||
bufferpointer += cx16.r0
|
bufferpointer += cx16.r0
|
||||||
}
|
}
|
||||||
@ -724,6 +731,12 @@ io_error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; Load a prog8 compiled library binary blob at the given location into memory.
|
||||||
|
sub loadlib(uword libnameptr, uword libaddress) -> uword {
|
||||||
|
return internal_load_routine(libnameptr, libaddress, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sub internal_load_routine(uword filenameptr, uword address_override, bool headerless) -> uword {
|
sub internal_load_routine(uword filenameptr, uword address_override, bool headerless) -> uword {
|
||||||
cbm.SETNAM(strings.length(filenameptr), filenameptr)
|
cbm.SETNAM(strings.length(filenameptr), filenameptr)
|
||||||
ubyte secondary = 1
|
ubyte secondary = 1
|
||||||
@ -941,7 +954,7 @@ internal_vload:
|
|||||||
io_error:
|
io_error:
|
||||||
cbm.CLRCHN()
|
cbm.CLRCHN()
|
||||||
cbm.CLOSE(READ_IO_CHANNEL)
|
cbm.CLOSE(READ_IO_CHANNEL)
|
||||||
if status!=0 and status & $40 == 0
|
if status!=0 and status & STATUS_EOF == 0
|
||||||
return 0
|
return 0
|
||||||
if @(cx16.r12)==0 {
|
if @(cx16.r12)==0 {
|
||||||
cx16.r12--
|
cx16.r12--
|
||||||
@ -973,6 +986,7 @@ io_error:
|
|||||||
sub exists(str filename) -> bool {
|
sub exists(str filename) -> bool {
|
||||||
; -- returns true if the given file exists on the disk, otherwise false
|
; -- returns true if the given file exists on the disk, otherwise false
|
||||||
; DON'T use this if you already have a file open with f_open!
|
; DON'T use this if you already have a file open with f_open!
|
||||||
|
; NOTE: doesn't clear the dos error status and message, you'll have to read/clear that yourself (with status() for example)
|
||||||
if f_open(filename) {
|
if f_open(filename) {
|
||||||
f_close()
|
f_close()
|
||||||
return true
|
return true
|
||||||
|
@ -154,9 +154,9 @@ gfx_hires {
|
|||||||
inc cx16.VERA_ADDR_L
|
inc cx16.VERA_ADDR_L
|
||||||
bne ++
|
bne ++
|
||||||
inc cx16.VERA_ADDR_M
|
inc cx16.VERA_ADDR_M
|
||||||
+ bne +
|
+ bne +
|
||||||
inc cx16.VERA_ADDR_H
|
inc cx16.VERA_ADDR_H
|
||||||
+ inx ; next pixel
|
+ inx ; next pixel
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,18 +22,23 @@ monogfx {
|
|||||||
const ubyte MODE_STIPPLE = %00000001
|
const ubyte MODE_STIPPLE = %00000001
|
||||||
const ubyte MODE_INVERT = %00000010
|
const ubyte MODE_INVERT = %00000010
|
||||||
|
|
||||||
|
uword buffer_visible, buffer_back
|
||||||
|
|
||||||
|
|
||||||
sub lores() {
|
sub lores() {
|
||||||
; enable 320*240 bitmap mode
|
; enable 320*240 bitmap mode
|
||||||
|
buffer_visible = buffer_back = $0000
|
||||||
cx16.VERA_CTRL=0
|
cx16.VERA_CTRL=0
|
||||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||||
cx16.VERA_DC_HSCALE = 64
|
cx16.VERA_DC_HSCALE = 64
|
||||||
cx16.VERA_DC_VSCALE = 64
|
cx16.VERA_DC_VSCALE = 64
|
||||||
cx16.VERA_L1_CONFIG = %00000100
|
cx16.VERA_L1_CONFIG = %00000100
|
||||||
cx16.VERA_L1_MAPBASE = 0
|
cx16.VERA_L1_MAPBASE = 0
|
||||||
cx16.VERA_L1_TILEBASE = 0
|
cx16.VERA_L1_TILEBASE = 0 ; lores
|
||||||
width = 320
|
width = 320
|
||||||
height = 240
|
height = 240
|
||||||
lores_mode = true
|
lores_mode = true
|
||||||
|
buffer_visible = buffer_back = $0000
|
||||||
mode = MODE_NORMAL
|
mode = MODE_NORMAL
|
||||||
clear_screen(false)
|
clear_screen(false)
|
||||||
}
|
}
|
||||||
@ -46,14 +51,40 @@ monogfx {
|
|||||||
cx16.VERA_DC_VSCALE = 128
|
cx16.VERA_DC_VSCALE = 128
|
||||||
cx16.VERA_L1_CONFIG = %00000100
|
cx16.VERA_L1_CONFIG = %00000100
|
||||||
cx16.VERA_L1_MAPBASE = 0
|
cx16.VERA_L1_MAPBASE = 0
|
||||||
cx16.VERA_L1_TILEBASE = %00000001
|
cx16.VERA_L1_TILEBASE = %00000001 ; hires
|
||||||
width = 640
|
width = 640
|
||||||
height = 480
|
height = 480
|
||||||
lores_mode = false
|
lores_mode = false
|
||||||
|
buffer_visible = buffer_back = $0000
|
||||||
mode = MODE_NORMAL
|
mode = MODE_NORMAL
|
||||||
clear_screen(false)
|
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() {
|
sub textmode() {
|
||||||
; back to normal text mode
|
; back to normal text mode
|
||||||
cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output 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) {
|
sub disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) {
|
||||||
; Warning: NO BOUNDS CHECKS. Make sure circle fits in the screen.
|
; Warning: NO BOUNDS CHECKS. Make sure circle fits in the screen.
|
||||||
; Midpoint algorithm, filled
|
; Midpoint algorithm, filled
|
||||||
|
; Note: has problems with INVERT draw mode because of horizontal span overdrawing. Horizontal lines may occur.
|
||||||
if radius==0
|
if radius==0
|
||||||
return
|
return
|
||||||
ubyte @zp yy = 0
|
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) {
|
sub safe_disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) {
|
||||||
; Does bounds checking and clipping.
|
; Does bounds checking and clipping.
|
||||||
; Midpoint algorithm, filled
|
; Midpoint algorithm, filled
|
||||||
|
; Note: has problems with INVERT draw mode because of horizontal span overdrawing. Horizontal lines may occur.
|
||||||
if radius==0
|
if radius==0
|
||||||
return
|
return
|
||||||
ubyte @zp yy = 0
|
ubyte @zp yy = 0
|
||||||
@ -672,8 +705,7 @@ nostipple:
|
|||||||
invert:
|
invert:
|
||||||
prepare()
|
prepare()
|
||||||
%asm {{
|
%asm {{
|
||||||
lda cx16.VERA_DATA0
|
eor cx16.VERA_DATA0
|
||||||
eor p8v_maskbits,y
|
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
}}
|
}}
|
||||||
return
|
return
|
||||||
@ -696,7 +728,7 @@ invert:
|
|||||||
adc p8v_times40_lsb,y
|
adc p8v_times40_lsb,y
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
lda p8v_times40_msb,y
|
lda p8v_times40_msb,y
|
||||||
adc #0
|
adc p8v_buffer_back+1
|
||||||
sta cx16.VERA_ADDR_M
|
sta cx16.VERA_ADDR_M
|
||||||
|
|
||||||
lda p8v_xx
|
lda p8v_xx
|
||||||
@ -708,18 +740,29 @@ invert:
|
|||||||
; width=640 (hires)
|
; width=640 (hires)
|
||||||
%asm {{
|
%asm {{
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
stz cx16.VERA_ADDR_H
|
|
||||||
lda p8v_xx
|
lda p8v_xx
|
||||||
and #7
|
and #7
|
||||||
pha ; xbits
|
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)
|
xx += yy*(640/8)
|
||||||
%asm {{
|
%asm {{
|
||||||
lda p8v_xx+1
|
|
||||||
sta cx16.VERA_ADDR_M
|
|
||||||
lda p8v_xx
|
lda p8v_xx
|
||||||
sta cx16.VERA_ADDR_L
|
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
|
plx ; xbits
|
||||||
lda p8v_maskbits,x
|
lda p8v_maskbits,x
|
||||||
}}
|
}}
|
||||||
@ -761,11 +804,15 @@ invert:
|
|||||||
|
|
||||||
%asm {{
|
%asm {{
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
stz cx16.VERA_ADDR_H
|
|
||||||
lda p8v_xx+1
|
|
||||||
sta cx16.VERA_ADDR_M
|
|
||||||
lda p8v_xx
|
lda p8v_xx
|
||||||
sta cx16.VERA_ADDR_L
|
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
|
ply ; xbits
|
||||||
lda p8s_plot.p8v_maskbits,y
|
lda p8s_plot.p8v_maskbits,y
|
||||||
and cx16.VERA_DATA0
|
and cx16.VERA_DATA0
|
||||||
@ -848,8 +895,8 @@ skip:
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub fill_scanline_right() {
|
sub fill_scanline_right() {
|
||||||
; TODO maybe this could use vera auto increment, but that requires some clever masking calculations
|
; TODO maybe this could use vera auto increment, but that requires some clever masking calculations
|
||||||
cx16.r9s = xx
|
cx16.r9s = xx
|
||||||
while xx <= width-1 {
|
while xx <= width-1 {
|
||||||
if pgetset()
|
if pgetset()
|
||||||
break
|
break
|
||||||
@ -884,11 +931,15 @@ skip:
|
|||||||
|
|
||||||
%asm {{
|
%asm {{
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
stz cx16.VERA_ADDR_H
|
|
||||||
lda p8v_xpos+1
|
|
||||||
sta cx16.VERA_ADDR_M
|
|
||||||
lda p8v_xpos
|
lda p8v_xpos
|
||||||
sta cx16.VERA_ADDR_L
|
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
|
ply ; xbits
|
||||||
lda p8s_plot.p8v_maskbits,y
|
lda p8s_plot.p8v_maskbits,y
|
||||||
and cx16.VERA_DATA0
|
and cx16.VERA_DATA0
|
||||||
@ -935,12 +986,12 @@ _doplot beq +
|
|||||||
ror a
|
ror a
|
||||||
lsr a
|
lsr a
|
||||||
lsr a
|
lsr a
|
||||||
clc
|
|
||||||
ldy p8v_yy
|
ldy p8v_yy
|
||||||
|
clc
|
||||||
adc p8v_times40_lsb,y
|
adc p8v_times40_lsb,y
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
lda p8v_times40_msb,y
|
lda p8v_times40_msb,y
|
||||||
adc #0
|
adc p8v_buffer_back+1
|
||||||
sta cx16.VERA_ADDR_M
|
sta cx16.VERA_ADDR_M
|
||||||
lda #%00010000 ; autoincr
|
lda #%00010000 ; autoincr
|
||||||
sta cx16.VERA_ADDR_H
|
sta cx16.VERA_ADDR_H
|
||||||
@ -948,7 +999,33 @@ _doplot beq +
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cx16.r0 = yy*(640/8)
|
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
|
return
|
||||||
}
|
}
|
||||||
@ -1116,15 +1193,11 @@ cdraw_mod2 ora cx16.VERA_DATA1
|
|||||||
cmp #0
|
cmp #0
|
||||||
beq +
|
beq +
|
||||||
lda #255
|
lda #255
|
||||||
+ ldy #80
|
+ ldy #40
|
||||||
- sta cx16.VERA_DATA0
|
-
|
||||||
sta cx16.VERA_DATA0
|
.rept 16
|
||||||
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
|
||||||
|
.endrept
|
||||||
dey
|
dey
|
||||||
bne -
|
bne -
|
||||||
rts
|
rts
|
||||||
|
@ -257,6 +257,7 @@ palette {
|
|||||||
|
|
||||||
sub set_default16() {
|
sub set_default16() {
|
||||||
; set first 16 colors to the defaults on the X16
|
; set first 16 colors to the defaults on the X16
|
||||||
|
; (doesn't use the rom table so this works on roms older than 49 as well)
|
||||||
uword[] @nosplit colors = [
|
uword[] @nosplit colors = [
|
||||||
$000, ; 0 = black
|
$000, ; 0 = black
|
||||||
$fff, ; 1 = white
|
$fff, ; 1 = white
|
||||||
@ -277,4 +278,12 @@ palette {
|
|||||||
]
|
]
|
||||||
set_rgb_nosplit(colors, len(colors), 0)
|
set_rgb_nosplit(colors, len(colors), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; set the full 256 colors in the palette back to its default values on the X16
|
||||||
|
; NOTE: this routine requires rom version 49+
|
||||||
|
alias set_default = cx16.set_default_palette
|
||||||
|
|
||||||
|
; get the bank and address of the word-array containing the 256 default palette colors
|
||||||
|
; NOTE: this routine requires rom version 49+
|
||||||
|
alias get_default = cx16.get_default_palette
|
||||||
}
|
}
|
||||||
|
@ -15,14 +15,22 @@ psg {
|
|||||||
const ubyte NOISE = %11000000
|
const ubyte NOISE = %11000000
|
||||||
const ubyte LEFT = %01000000
|
const ubyte LEFT = %01000000
|
||||||
const ubyte RIGHT = %10000000
|
const ubyte RIGHT = %10000000
|
||||||
|
const ubyte DISABLED = %11111111
|
||||||
|
|
||||||
|
sub init() {
|
||||||
|
; -- initializes the psg module (all 16 voices set to disabled)
|
||||||
|
for cx16.r1L in 0 to 15
|
||||||
|
voice_enabled[cx16.r1L] = false
|
||||||
|
}
|
||||||
|
|
||||||
sub voice(ubyte voice_num, ubyte channel, ubyte vol, ubyte waveform, ubyte pulsewidth) {
|
sub voice(ubyte voice_num, ubyte channel, ubyte vol, ubyte waveform, ubyte pulsewidth) {
|
||||||
; -- Enables a 'voice' on the PSG.
|
; -- Enables a 'voice' on the PSG.
|
||||||
; voice_num = 0-15, the voice number.
|
; voice_num = 0-15, the voice number.
|
||||||
; channel = either LEFT or RIGHT or (LEFT|RIGHT). Specifies the stereo channel(s) to use.
|
; channel = either LEFT or RIGHT or (LEFT|RIGHT). Specifies the stereo channel(s) to use. DISABLED=disable the voice.
|
||||||
; vol = 0-63, the starting volume for the voice
|
; vol = 0-63, the starting volume for the voice
|
||||||
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
|
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
|
||||||
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
|
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
|
||||||
|
voice_enabled[voice_num] = false
|
||||||
envelope_states[voice_num] = 255
|
envelope_states[voice_num] = 255
|
||||||
sys.irqsafe_set_irqd()
|
sys.irqsafe_set_irqd()
|
||||||
cx16.r0 = $f9c2 + voice_num * 4
|
cx16.r0 = $f9c2 + voice_num * 4
|
||||||
@ -30,11 +38,19 @@ psg {
|
|||||||
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
||||||
cx16.VERA_ADDR_M = msb(cx16.r0)
|
cx16.VERA_ADDR_M = msb(cx16.r0)
|
||||||
cx16.VERA_ADDR_H = 1
|
cx16.VERA_ADDR_H = 1
|
||||||
cx16.VERA_DATA0 = channel | vol
|
if channel!=DISABLED {
|
||||||
cx16.VERA_ADDR_L++
|
cx16.VERA_DATA0 = channel | vol
|
||||||
cx16.VERA_DATA0 = waveform | pulsewidth
|
cx16.VERA_ADDR_L++
|
||||||
envelope_volumes[voice_num] = mkword(vol, 0)
|
cx16.VERA_DATA0 = waveform | pulsewidth
|
||||||
envelope_maxvolumes[voice_num] = vol
|
envelope_volumes[voice_num] = mkword(vol, 0)
|
||||||
|
envelope_maxvolumes[voice_num] = vol
|
||||||
|
voice_enabled[voice_num] = true
|
||||||
|
} else {
|
||||||
|
cx16.VERA_DATA0 = 0
|
||||||
|
envelope_volumes[voice_num] = 0
|
||||||
|
envelope_maxvolumes[voice_num] = 0
|
||||||
|
voice_enabled[voice_num] = false
|
||||||
|
}
|
||||||
sys.irqsafe_clear_irqd()
|
sys.irqsafe_clear_irqd()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,10 +115,12 @@ psg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub silent() {
|
sub silent() {
|
||||||
; -- Shut down all PSG voices.
|
; -- Silence all active PSG voices.
|
||||||
for cx16.r1L in 0 to 15 {
|
for cx16.r1L in 0 to 15 {
|
||||||
envelope_states[cx16.r1L] = 255
|
if voice_enabled[cx16.r1L] {
|
||||||
volume(cx16.r1L, 0)
|
envelope_states[cx16.r1L] = 255
|
||||||
|
volume(cx16.r1L, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,6 +140,8 @@ psg {
|
|||||||
sys.pushw(cx16.r9)
|
sys.pushw(cx16.r9)
|
||||||
; calculate new volumes
|
; calculate new volumes
|
||||||
for cx16.r1L in 0 to 15 {
|
for cx16.r1L in 0 to 15 {
|
||||||
|
if not voice_enabled[cx16.r1L]
|
||||||
|
continue
|
||||||
when envelope_states[cx16.r1L] {
|
when envelope_states[cx16.r1L] {
|
||||||
0 -> {
|
0 -> {
|
||||||
; attack
|
; attack
|
||||||
@ -165,7 +185,10 @@ psg {
|
|||||||
cx16.VERA_ADDR_M = $f9
|
cx16.VERA_ADDR_M = $f9
|
||||||
cx16.VERA_ADDR_H = 1 | %00110000
|
cx16.VERA_ADDR_H = 1 | %00110000
|
||||||
for cx16.r1L in 0 to 15 {
|
for cx16.r1L in 0 to 15 {
|
||||||
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
|
if voice_enabled[cx16.r1L]
|
||||||
|
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
|
||||||
|
else
|
||||||
|
cx16.VERA_DATA0 = cx16.VERA_DATA1
|
||||||
}
|
}
|
||||||
cx16.restore_vera_context()
|
cx16.restore_vera_context()
|
||||||
cx16.r9 = sys.popw()
|
cx16.r9 = sys.popw()
|
||||||
@ -175,6 +198,7 @@ psg {
|
|||||||
return true ; run the system IRQ handler afterwards
|
return true ; run the system IRQ handler afterwards
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool[16] voice_enabled
|
||||||
ubyte[16] envelope_states
|
ubyte[16] envelope_states
|
||||||
uword[16] envelope_volumes ; scaled by 256
|
uword[16] envelope_volumes ; scaled by 256
|
||||||
ubyte[16] envelope_attacks
|
ubyte[16] envelope_attacks
|
||||||
|
@ -474,7 +474,7 @@ extsub $ff68 = mouse_config(byte shape @A, ubyte resX @X, ubyte resY @Y) clobbe
|
|||||||
extsub $ff6b = mouse_get(ubyte zdataptr @X) -> ubyte @A, byte @X ; use mouse_pos() instead
|
extsub $ff6b = mouse_get(ubyte zdataptr @X) -> ubyte @A, byte @X ; use mouse_pos() instead
|
||||||
extsub $ff71 = mouse_scan() clobbers(A, X, Y)
|
extsub $ff71 = mouse_scan() clobbers(A, X, Y)
|
||||||
extsub $ff53 = joystick_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)
|
; 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)
|
extsub $C000 = x16edit_default() clobbers(A,X,Y)
|
||||||
@ -558,6 +558,7 @@ const ubyte EXTAPI_mouse_set_position = $0C
|
|||||||
const ubyte EXTAPI_scnsiz = $0D
|
const ubyte EXTAPI_scnsiz = $0D
|
||||||
const ubyte EXTAPI_kbd_leds = $0E
|
const ubyte EXTAPI_kbd_leds = $0E
|
||||||
const ubyte EXTAPI_memory_decompress_from_func = $0F
|
const ubyte EXTAPI_memory_decompress_from_func = $0F
|
||||||
|
const ubyte EXTAPI_default_palette = $10
|
||||||
|
|
||||||
; extapi16 call numbers
|
; extapi16 call numbers
|
||||||
const ubyte EXTAPI16_test = $00
|
const ubyte EXTAPI16_test = $00
|
||||||
@ -668,6 +669,34 @@ asmsub scnsiz(ubyte width @X, ubyte heigth @Y) clobbers(A,X,Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub memory_decompress_from_func(uword datafunction @R4, uword output @R1) clobbers(A,X,Y) -> uword @R1 {
|
||||||
|
; requires rom v49+
|
||||||
|
%asm {{
|
||||||
|
lda #EXTAPI_memory_decompress_from_func
|
||||||
|
jmp cx16.extapi
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub get_default_palette() -> ubyte @A, uword @XY {
|
||||||
|
; returns memory address of default palette; bank in A and address in XY
|
||||||
|
; requires rom v49+
|
||||||
|
%asm {{
|
||||||
|
sec
|
||||||
|
lda #EXTAPI_default_palette
|
||||||
|
jmp cx16.extapi
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub set_default_palette() {
|
||||||
|
; sets default palette in to the Vera
|
||||||
|
; requires rom v49+
|
||||||
|
%asm {{
|
||||||
|
clc
|
||||||
|
lda #EXTAPI_default_palette
|
||||||
|
jmp cx16.extapi
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub get_charset() -> ubyte @A {
|
asmsub get_charset() -> ubyte @A {
|
||||||
;Get charset mode. result: 0=unknown, 1=ISO, 2=PETSCII upper case/gfx, 3=PETSCII lowercase.
|
;Get charset mode. result: 0=unknown, 1=ISO, 2=PETSCII upper case/gfx, 3=PETSCII lowercase.
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -728,6 +757,42 @@ inline asmsub getrambank() -> ubyte @A {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline asmsub push_rombank(ubyte newbank @A) clobbers(Y) {
|
||||||
|
; push the current rombank on the stack and makes the given rom bank active
|
||||||
|
; combined with pop_rombank() makes for easy temporary rom bank switch
|
||||||
|
%asm {{
|
||||||
|
ldy $01
|
||||||
|
phy
|
||||||
|
sta $01
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline asmsub pop_rombank() {
|
||||||
|
; sets the current rom bank back to what was stored previously on the stack
|
||||||
|
%asm {{
|
||||||
|
pla
|
||||||
|
sta $01
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline asmsub push_rambank(ubyte newbank @A) clobbers(Y) {
|
||||||
|
; push the current hiram bank on the stack and makes the given hiram bank active
|
||||||
|
; combined with pop_rombank() makes for easy temporary hiram bank switch
|
||||||
|
%asm {{
|
||||||
|
ldy $00
|
||||||
|
phy
|
||||||
|
sta $00
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline asmsub pop_rambank() {
|
||||||
|
; sets the current hiram bank back to what was stored previously on the stack
|
||||||
|
%asm {{
|
||||||
|
pla
|
||||||
|
sta $00
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub numbanks() clobbers(X) -> uword @AY {
|
asmsub numbanks() clobbers(X) -> uword @AY {
|
||||||
; -- Returns the number of available RAM banks according to the kernal (each bank is 8 Kb).
|
; -- Returns the number of available RAM banks according to the kernal (each bank is 8 Kb).
|
||||||
; Note that the number of such banks can be 256 so a word is returned.
|
; Note that the number of such banks can be 256 so a word is returned.
|
||||||
@ -981,7 +1046,6 @@ asmsub vpoke_mask(ubyte bank @A, uword address @R0, ubyte mask @X, ubyte value @
|
|||||||
}
|
}
|
||||||
|
|
||||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||||
; TODO: Romable
|
|
||||||
%asm {{
|
%asm {{
|
||||||
ldy #31
|
ldy #31
|
||||||
- lda cx16.r0,y
|
- lda cx16.r0,y
|
||||||
@ -990,9 +1054,11 @@ asmsub save_virtual_registers() clobbers(A,Y) {
|
|||||||
bpl -
|
bpl -
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
.section BSS
|
||||||
_cx16_vreg_storage
|
_cx16_vreg_storage
|
||||||
.word 0,0,0,0,0,0,0,0
|
.word ?,?,?,?,?,?,?,?
|
||||||
.word 0,0,0,0,0,0,0,0
|
.word ?,?,?,?,?,?,?,?
|
||||||
|
.send BSS
|
||||||
; !notreached!
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -1011,7 +1077,6 @@ asmsub restore_virtual_registers() clobbers(A,Y) {
|
|||||||
|
|
||||||
asmsub save_vera_context() clobbers(A) {
|
asmsub save_vera_context() clobbers(A) {
|
||||||
; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
|
; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
|
||||||
; TODO: Romable
|
|
||||||
%asm {{
|
%asm {{
|
||||||
; note cannot store this on cpu hardware stack because this gets called as a subroutine
|
; note cannot store this on cpu hardware stack because this gets called as a subroutine
|
||||||
lda cx16.VERA_ADDR_L
|
lda cx16.VERA_ADDR_L
|
||||||
@ -1032,7 +1097,9 @@ asmsub save_vera_context() clobbers(A) {
|
|||||||
lda cx16.VERA_ADDR_H
|
lda cx16.VERA_ADDR_H
|
||||||
sta _vera_storage+6
|
sta _vera_storage+6
|
||||||
rts
|
rts
|
||||||
_vera_storage: .byte 0,0,0,0,0,0,0,0
|
.section BSS
|
||||||
|
_vera_storage: .byte ?,?,?,?,?,?,?,?
|
||||||
|
.send BSS
|
||||||
; !notreached!
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -1101,8 +1168,39 @@ asmsub restore_vera_context() clobbers(A) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub joysticks_detect() -> ubyte {
|
||||||
|
; returns bits 0-4, set to 1 if that joystick is present.
|
||||||
|
; bit 0 = keyboard joystick, bit 1 - 4 = joypads 1 to 4
|
||||||
|
cx16.r0H = 255
|
||||||
|
for cx16.r0L in 4 downto 0 {
|
||||||
|
void cx16.joystick_get(cx16.r0L)
|
||||||
|
%asm {{
|
||||||
|
cpy #1 ; present?
|
||||||
|
}}
|
||||||
|
rol(cx16.r0H)
|
||||||
|
}
|
||||||
|
return ~cx16.r0H
|
||||||
|
}
|
||||||
|
|
||||||
; Commander X16 IRQ dispatcher routines
|
sub joysticks_getall(bool also_keyboard_js) -> uword {
|
||||||
|
; returns combined pressed buttons from all connected joysticks
|
||||||
|
; note: returns the 'normal' not inverted status bits for the buttons (1 = button pressed.)
|
||||||
|
cx16.r0H = 1
|
||||||
|
if also_keyboard_js
|
||||||
|
cx16.r0H = 0
|
||||||
|
cx16.r1 = $ffff
|
||||||
|
for cx16.r0L in cx16.r0H to 4 {
|
||||||
|
bool notpresent
|
||||||
|
cx16.r2, notpresent = cx16.joystick_get(cx16.r0L)
|
||||||
|
if not notpresent {
|
||||||
|
cx16.r1 &= cx16.r2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ~cx16.r1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; Commander X16 IRQ dispatcher routines
|
||||||
|
|
||||||
inline asmsub disable_irqs() clobbers(A) {
|
inline asmsub disable_irqs() clobbers(A) {
|
||||||
; Disable all Vera IRQ sources. Note that it does NOT set the CPU IRQ disabled status bit!
|
; Disable all Vera IRQ sources. Note that it does NOT set the CPU IRQ disabled status bit!
|
||||||
@ -1127,74 +1225,93 @@ asmsub enable_irq_handlers(bool disable_all_irq_sources @Pc) clobbers(A,Y) {
|
|||||||
ldy #>_irq_dispatcher
|
ldy #>_irq_dispatcher
|
||||||
sta cbm.CINV
|
sta cbm.CINV
|
||||||
sty cbm.CINV+1
|
sty cbm.CINV+1
|
||||||
|
|
||||||
|
lda #<_default_1_handler
|
||||||
|
ldy #>_default_1_handler
|
||||||
|
sta _vsync_vec
|
||||||
|
sty _vsync_vec+1
|
||||||
|
lda #<_default_0_handler
|
||||||
|
ldy #>_default_0_handler
|
||||||
|
sta _line_vec
|
||||||
|
sty _line_vec+1
|
||||||
|
sta _aflow_vec
|
||||||
|
sty _aflow_vec+1
|
||||||
|
sta _sprcol_vec
|
||||||
|
sty _sprcol_vec+1
|
||||||
|
|
||||||
plp
|
plp
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
.section BSS
|
||||||
|
_vsync_vec .word ?
|
||||||
|
_line_vec .word ?
|
||||||
|
_aflow_vec .word ?
|
||||||
|
_sprcol_vec .word ?
|
||||||
|
_continue_with_system_handler .byte ?
|
||||||
|
.send BSS
|
||||||
|
|
||||||
_irq_dispatcher
|
_irq_dispatcher
|
||||||
; order of handling: LINE, SPRCOL, AFLOW, VSYNC.
|
; order of handling: LINE, SPRCOL, AFLOW, VSYNC.
|
||||||
jsr sys.save_prog8_internals
|
jsr sys.save_prog8_internals
|
||||||
cld
|
cld
|
||||||
lda cx16.VERA_ISR
|
lda cx16.VERA_ISR
|
||||||
and cx16.VERA_IEN ; only consider the bits for sources that can actually raise the IRQ
|
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 +
|
beq +
|
||||||
_mod_line_jump
|
pha
|
||||||
jsr _default_line_handler ; modified
|
jsr _line_handler
|
||||||
ldy #2
|
tsb _continue_with_system_handler
|
||||||
sty cx16.VERA_ISR
|
pla
|
||||||
bra _dispatch_end
|
|
||||||
+
|
+ lsr a
|
||||||
bit #4
|
bcc +
|
||||||
beq +
|
pha
|
||||||
_mod_sprcol_jump
|
jsr _vsync_handler
|
||||||
jsr _default_sprcol_handler ; modified
|
tsb _continue_with_system_handler
|
||||||
ldy #4
|
pla
|
||||||
sty cx16.VERA_ISR
|
|
||||||
bra _dispatch_end
|
+ lsr a
|
||||||
+
|
lsr a
|
||||||
bit #8
|
bcc +
|
||||||
beq +
|
pha
|
||||||
_mod_aflow_jump
|
jsr _sprcol_handler
|
||||||
jsr _default_aflow_handler ; modified
|
tsb _continue_with_system_handler
|
||||||
; note: AFLOW can only be cleared by filling the audio FIFO for at least 1/4. Not via the ISR bit.
|
pla
|
||||||
bra _dispatch_end
|
|
||||||
+
|
+ lsr a
|
||||||
bit #1
|
bcc +
|
||||||
beq +
|
jsr _aflow_handler
|
||||||
_mod_vsync_jump
|
tsb _continue_with_system_handler
|
||||||
jsr _default_vsync_handler ; modified
|
|
||||||
cmp #0
|
+ jsr sys.restore_prog8_internals
|
||||||
bne _dispatch_end
|
lda _continue_with_system_handler
|
||||||
ldy #1
|
beq _no_sys_handler
|
||||||
sty cx16.VERA_ISR
|
|
||||||
bra _return_irq
|
|
||||||
+
|
|
||||||
lda #0
|
|
||||||
_dispatch_end
|
|
||||||
cmp #0
|
|
||||||
beq _return_irq
|
|
||||||
jsr sys.restore_prog8_internals
|
|
||||||
jmp (sys.restore_irq._orig_irqvec) ; continue with normal kernal irq routine
|
jmp (sys.restore_irq._orig_irqvec) ; continue with normal kernal irq routine
|
||||||
_return_irq
|
_no_sys_handler
|
||||||
jsr sys.restore_prog8_internals
|
|
||||||
ply
|
ply
|
||||||
plx
|
plx
|
||||||
pla
|
pla
|
||||||
rti
|
rti
|
||||||
|
|
||||||
_default_vsync_handler
|
_default_0_handler
|
||||||
|
lda #0
|
||||||
|
rts
|
||||||
|
_default_1_handler
|
||||||
lda #1
|
lda #1
|
||||||
rts
|
rts
|
||||||
_default_line_handler
|
|
||||||
lda #0
|
_vsync_handler
|
||||||
rts
|
jmp (_vsync_vec)
|
||||||
_default_sprcol_handler
|
_line_handler
|
||||||
lda #0
|
jmp (_line_vec)
|
||||||
rts
|
_sprcol_handler
|
||||||
_default_aflow_handler
|
jmp (_sprcol_vec)
|
||||||
lda #0
|
_aflow_handler
|
||||||
rts
|
jmp (_aflow_vec)
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1203,8 +1320,8 @@ asmsub set_vsync_irq_handler(uword address @AY) clobbers(A) {
|
|||||||
%asm {{
|
%asm {{
|
||||||
php
|
php
|
||||||
sei
|
sei
|
||||||
sta enable_irq_handlers._mod_vsync_jump+1
|
sta enable_irq_handlers._vsync_vec
|
||||||
sty enable_irq_handlers._mod_vsync_jump+2
|
sty enable_irq_handlers._vsync_vec+1
|
||||||
lda #1
|
lda #1
|
||||||
tsb cx16.VERA_IEN
|
tsb cx16.VERA_IEN
|
||||||
plp
|
plp
|
||||||
@ -1218,8 +1335,8 @@ asmsub set_line_irq_handler(uword rasterline @R0, uword address @AY) clobbers(A,
|
|||||||
%asm {{
|
%asm {{
|
||||||
php
|
php
|
||||||
sei
|
sei
|
||||||
sta enable_irq_handlers._mod_line_jump+1
|
sta enable_irq_handlers._line_vec
|
||||||
sty enable_irq_handlers._mod_line_jump+2
|
sty enable_irq_handlers._line_vec+1
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
ldy cx16.r0+1
|
ldy cx16.r0+1
|
||||||
jsr sys.set_rasterline
|
jsr sys.set_rasterline
|
||||||
@ -1235,8 +1352,8 @@ asmsub set_sprcol_irq_handler(uword address @AY) clobbers(A) {
|
|||||||
%asm {{
|
%asm {{
|
||||||
php
|
php
|
||||||
sei
|
sei
|
||||||
sta enable_irq_handlers._mod_sprcol_jump+1
|
sta enable_irq_handlers._sprcol_vec
|
||||||
sty enable_irq_handlers._mod_sprcol_jump+2
|
sty enable_irq_handlers._sprcol_vec+1
|
||||||
lda #4
|
lda #4
|
||||||
tsb cx16.VERA_IEN
|
tsb cx16.VERA_IEN
|
||||||
plp
|
plp
|
||||||
@ -1250,8 +1367,8 @@ asmsub set_aflow_irq_handler(uword address @AY) clobbers(A) {
|
|||||||
%asm {{
|
%asm {{
|
||||||
php
|
php
|
||||||
sei
|
sei
|
||||||
sta enable_irq_handlers._mod_aflow_jump+1
|
sta enable_irq_handlers._aflow_vec
|
||||||
sty enable_irq_handlers._mod_aflow_jump+2
|
sty enable_irq_handlers._aflow_vec+1
|
||||||
lda #8
|
lda #8
|
||||||
tsb cx16.VERA_IEN
|
tsb cx16.VERA_IEN
|
||||||
plp
|
plp
|
||||||
@ -1398,12 +1515,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 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_BOOL = sizeof(bool)
|
||||||
const ubyte SIZEOF_BYTE = 1
|
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||||
const ubyte SIZEOF_UBYTE = 1
|
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||||
const ubyte SIZEOF_WORD = 2
|
const ubyte SIZEOF_WORD = sizeof(word)
|
||||||
const ubyte SIZEOF_UWORD = 2
|
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||||
const ubyte SIZEOF_FLOAT = 5
|
const ubyte SIZEOF_LONG = sizeof(long)
|
||||||
|
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||||
const byte MIN_BYTE = -128
|
const byte MIN_BYTE = -128
|
||||||
const byte MAX_BYTE = 127
|
const byte MAX_BYTE = 127
|
||||||
const ubyte MIN_UBYTE = 0
|
const ubyte MIN_UBYTE = 0
|
||||||
@ -1419,8 +1537,8 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
|
|||||||
; Sets the handler for the VSYNC interrupt, and enable that interrupt.
|
; Sets the handler for the VSYNC interrupt, and enable that interrupt.
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
sta _modified+1
|
sta _vector
|
||||||
sty _modified+2
|
sty _vector+1
|
||||||
lda #<_irq_handler
|
lda #<_irq_handler
|
||||||
sta cbm.CINV
|
sta cbm.CINV
|
||||||
lda #>_irq_handler
|
lda #>_irq_handler
|
||||||
@ -1433,8 +1551,8 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
|
|||||||
_irq_handler
|
_irq_handler
|
||||||
jsr sys.save_prog8_internals
|
jsr sys.save_prog8_internals
|
||||||
cld
|
cld
|
||||||
_modified
|
|
||||||
jsr $ffff ; modified
|
jsr _run_custom
|
||||||
pha
|
pha
|
||||||
jsr sys.restore_prog8_internals
|
jsr sys.restore_prog8_internals
|
||||||
pla
|
pla
|
||||||
@ -1446,11 +1564,16 @@ _modified
|
|||||||
plx
|
plx
|
||||||
pla
|
pla
|
||||||
rti
|
rti
|
||||||
|
_run_custom
|
||||||
|
jmp (_vector)
|
||||||
|
.section BSS
|
||||||
|
_vector .word ?
|
||||||
|
.send BSS
|
||||||
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub restore_irq() clobbers(A) {
|
asmsub restore_irq() clobbers(A) {
|
||||||
; TODO: Romable
|
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
lda _orig_irqvec
|
lda _orig_irqvec
|
||||||
@ -1463,7 +1586,9 @@ asmsub restore_irq() clobbers(A) {
|
|||||||
sta cx16.VERA_IEN
|
sta cx16.VERA_IEN
|
||||||
cli
|
cli
|
||||||
rts
|
rts
|
||||||
_orig_irqvec .word 0
|
.section BSS_NOCLEAR
|
||||||
|
_orig_irqvec .word ?
|
||||||
|
.send BSS_NOCLEAR
|
||||||
; !notreached!
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -1472,8 +1597,8 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
|||||||
; Sets the handler for the LINE interrupt, and enable (only) that interrupt.
|
; Sets the handler for the LINE interrupt, and enable (only) that interrupt.
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
sta _modified+1
|
sta _vector
|
||||||
sty _modified+2
|
sty _vector+1
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
ldy cx16.r0+1
|
ldy cx16.r0+1
|
||||||
lda cx16.VERA_IEN
|
lda cx16.VERA_IEN
|
||||||
@ -1493,7 +1618,7 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
|||||||
_raster_irq_handler
|
_raster_irq_handler
|
||||||
jsr sys.save_prog8_internals
|
jsr sys.save_prog8_internals
|
||||||
cld
|
cld
|
||||||
_modified jsr $ffff ; modified
|
jsr _run_custom
|
||||||
jsr sys.restore_prog8_internals
|
jsr sys.restore_prog8_internals
|
||||||
; end irq processing - don't use kernal's irq handling
|
; end irq processing - don't use kernal's irq handling
|
||||||
lda #2
|
lda #2
|
||||||
@ -1502,6 +1627,13 @@ _modified jsr $ffff ; modified
|
|||||||
plx
|
plx
|
||||||
pla
|
pla
|
||||||
rti
|
rti
|
||||||
|
_run_custom
|
||||||
|
jmp (_vector)
|
||||||
|
.section BSS
|
||||||
|
_vector .word ?
|
||||||
|
.send BSS
|
||||||
|
|
||||||
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1778,7 +1910,6 @@ _no_msb_size
|
|||||||
}
|
}
|
||||||
|
|
||||||
asmsub save_prog8_internals() {
|
asmsub save_prog8_internals() {
|
||||||
; TODO: Romable
|
|
||||||
%asm {{
|
%asm {{
|
||||||
lda P8ZP_SCRATCH_B1
|
lda P8ZP_SCRATCH_B1
|
||||||
sta save_SCRATCH_ZPB1
|
sta save_SCRATCH_ZPB1
|
||||||
@ -1793,10 +1924,12 @@ _no_msb_size
|
|||||||
lda P8ZP_SCRATCH_W2+1
|
lda P8ZP_SCRATCH_W2+1
|
||||||
sta save_SCRATCH_ZPWORD2+1
|
sta save_SCRATCH_ZPWORD2+1
|
||||||
rts
|
rts
|
||||||
save_SCRATCH_ZPB1 .byte 0
|
.section BSS
|
||||||
save_SCRATCH_ZPREG .byte 0
|
save_SCRATCH_ZPB1 .byte ?
|
||||||
save_SCRATCH_ZPWORD1 .word 0
|
save_SCRATCH_ZPREG .byte ?
|
||||||
save_SCRATCH_ZPWORD2 .word 0
|
save_SCRATCH_ZPWORD1 .word ?
|
||||||
|
save_SCRATCH_ZPWORD2 .word ?
|
||||||
|
.send BSS
|
||||||
; !notreached!
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -1847,7 +1980,7 @@ save_SCRATCH_ZPWORD2 .word 0
|
|||||||
sta p8_sys_startup.cleanup_at_exit._exitcode
|
sta p8_sys_startup.cleanup_at_exit._exitcode
|
||||||
lda #0
|
lda #0
|
||||||
rol a
|
rol a
|
||||||
sta p8_sys_startup.cleanup_at_exit._exitcodeCarry
|
sta p8_sys_startup.cleanup_at_exit._exitcarry
|
||||||
stx p8_sys_startup.cleanup_at_exit._exitcodeX
|
stx p8_sys_startup.cleanup_at_exit._exitcodeX
|
||||||
sty p8_sys_startup.cleanup_at_exit._exitcodeY
|
sty p8_sys_startup.cleanup_at_exit._exitcodeY
|
||||||
ldx prog8_lib.orig_stackpointer
|
ldx prog8_lib.orig_stackpointer
|
||||||
@ -1985,16 +2118,21 @@ asmsub cleanup_at_exit() {
|
|||||||
jsr cbm.CLRCHN ; reset i/o channels
|
jsr cbm.CLRCHN ; reset i/o channels
|
||||||
lda #9
|
lda #9
|
||||||
jsr cbm.CHROUT ; enable charset switch
|
jsr cbm.CHROUT ; enable charset switch
|
||||||
_exitcodeCarry = *+1
|
lda _exitcarry
|
||||||
lda #0
|
|
||||||
lsr a
|
lsr a
|
||||||
_exitcode = *+1
|
lda _exitcode
|
||||||
lda #0 ; exit code possibly modified in exit()
|
ldx _exitcodeX
|
||||||
_exitcodeX = *+1
|
ldy _exitcodeY
|
||||||
ldx #0
|
|
||||||
_exitcodeY = *+1
|
|
||||||
ldy #0
|
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
.section BSS
|
||||||
|
_exitcarry .byte ?
|
||||||
|
_exitcode .byte ?
|
||||||
|
_exitcodeX .byte ?
|
||||||
|
_exitcodeY .byte ?
|
||||||
|
.send BSS
|
||||||
|
|
||||||
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,19 +96,21 @@ asmsub get_cursor() -> ubyte @X, ubyte @Y {
|
|||||||
asmsub fill_screen (ubyte character @ A, ubyte color @ Y) clobbers(A, X) {
|
asmsub fill_screen (ubyte character @ A, ubyte color @ Y) clobbers(A, X) {
|
||||||
; ---- fill the character screen with the given fill character and character color.
|
; ---- fill the character screen with the given fill character and character color.
|
||||||
%asm {{
|
%asm {{
|
||||||
sty _ly+1
|
_color = P8ZP_SCRATCH_B1
|
||||||
|
_numrows = P8ZP_SCRATCH_REG
|
||||||
|
sty _color
|
||||||
pha
|
pha
|
||||||
jsr cbm.SCREEN ; get dimensions in X/Y
|
jsr cbm.SCREEN ; get dimensions in X/Y
|
||||||
txa
|
txa
|
||||||
lsr a
|
lsr a
|
||||||
lsr a
|
lsr a
|
||||||
sta _lx+1
|
sta _numrows
|
||||||
lda #%00010000
|
lda #%00010000
|
||||||
jsr set_vera_textmatrix_addresses
|
jsr set_vera_textmatrix_addresses
|
||||||
pla
|
pla
|
||||||
_lx ldx #0 ; modified
|
_more ldx _numrows
|
||||||
phy
|
phy
|
||||||
_ly ldy #1 ; modified
|
ldy _color
|
||||||
- sta cx16.VERA_DATA0
|
- sta cx16.VERA_DATA0
|
||||||
sty cx16.VERA_DATA0
|
sty cx16.VERA_DATA0
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
@ -124,7 +126,7 @@ _ly ldy #1 ; modified
|
|||||||
beq +
|
beq +
|
||||||
stz cx16.VERA_ADDR_L
|
stz cx16.VERA_ADDR_L
|
||||||
inc cx16.VERA_ADDR_M ; next line
|
inc cx16.VERA_ADDR_M ; next line
|
||||||
bra _lx
|
bra _more
|
||||||
+ rts
|
+ rts
|
||||||
|
|
||||||
set_vera_textmatrix_addresses:
|
set_vera_textmatrix_addresses:
|
||||||
@ -148,11 +150,11 @@ asmsub clear_screenchars (ubyte character @ A) clobbers(X, Y) {
|
|||||||
txa
|
txa
|
||||||
lsr a
|
lsr a
|
||||||
lsr a
|
lsr a
|
||||||
sta _lx+1
|
sta P8ZP_SCRATCH_REG
|
||||||
lda #%00100000
|
lda #%00100000
|
||||||
jsr fill_screen.set_vera_textmatrix_addresses
|
jsr fill_screen.set_vera_textmatrix_addresses
|
||||||
pla
|
pla
|
||||||
_lx ldx #0 ; modified
|
_more ldx P8ZP_SCRATCH_REG
|
||||||
- sta cx16.VERA_DATA0
|
- sta cx16.VERA_DATA0
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
@ -163,7 +165,7 @@ _lx ldx #0 ; modified
|
|||||||
beq +
|
beq +
|
||||||
stz cx16.VERA_ADDR_L
|
stz cx16.VERA_ADDR_L
|
||||||
inc cx16.VERA_ADDR_M ; next line
|
inc cx16.VERA_ADDR_M ; next line
|
||||||
bra _lx
|
bra _more
|
||||||
+ rts
|
+ rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -172,18 +174,20 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(X, Y) {
|
|||||||
; ---- clear the character screen colors with the given color (leaves characters).
|
; ---- clear the character screen colors with the given color (leaves characters).
|
||||||
; (assumes color matrix is at the default address)
|
; (assumes color matrix is at the default address)
|
||||||
%asm {{
|
%asm {{
|
||||||
sta _la+1
|
_color = P8ZP_SCRATCH_B1
|
||||||
|
_numrows = P8ZP_SCRATCH_REG
|
||||||
|
sta _color
|
||||||
jsr cbm.SCREEN ; get dimensions in X/Y
|
jsr cbm.SCREEN ; get dimensions in X/Y
|
||||||
txa
|
txa
|
||||||
lsr a
|
lsr a
|
||||||
lsr a
|
lsr a
|
||||||
sta _lx+1
|
sta _numrows
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
lda #%00100000
|
lda #%00100000
|
||||||
jsr fill_screen.set_vera_textmatrix_addresses
|
jsr fill_screen.set_vera_textmatrix_addresses
|
||||||
inc cx16.VERA_ADDR_L ; start at (1,0) - the color attribute byte
|
inc cx16.VERA_ADDR_L ; start at (1,0) - the color attribute byte
|
||||||
_lx ldx #0 ; modified
|
_more ldx _numrows
|
||||||
_la lda #0 ; modified
|
lda _color
|
||||||
- sta cx16.VERA_DATA0
|
- sta cx16.VERA_DATA0
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
@ -195,7 +199,7 @@ _la lda #0 ; modified
|
|||||||
lda #1
|
lda #1
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
inc cx16.VERA_ADDR_M ; next line
|
inc cx16.VERA_ADDR_M ; next line
|
||||||
bra _lx
|
bra _more
|
||||||
+ rts
|
+ rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -272,7 +276,7 @@ asmsub scroll_left() clobbers(A, X, Y) {
|
|||||||
%asm {{
|
%asm {{
|
||||||
jsr cbm.SCREEN
|
jsr cbm.SCREEN
|
||||||
dex
|
dex
|
||||||
stx _lx+1
|
stx P8ZP_SCRATCH_REG ; columns
|
||||||
dey
|
dey
|
||||||
sty P8ZP_SCRATCH_B1 ; number of rows to scroll
|
sty P8ZP_SCRATCH_B1 ; number of rows to scroll
|
||||||
|
|
||||||
@ -294,7 +298,7 @@ _nextline
|
|||||||
stz cx16.VERA_ADDR_L
|
stz cx16.VERA_ADDR_L
|
||||||
sty cx16.VERA_ADDR_M
|
sty cx16.VERA_ADDR_M
|
||||||
|
|
||||||
_lx ldx #0 ; modified
|
ldx P8ZP_SCRATCH_REG ; columns
|
||||||
- lda cx16.VERA_DATA0
|
- lda cx16.VERA_DATA0
|
||||||
sta cx16.VERA_DATA1 ; copy char
|
sta cx16.VERA_DATA1 ; copy char
|
||||||
lda cx16.VERA_DATA0
|
lda cx16.VERA_DATA0
|
||||||
@ -314,16 +318,19 @@ asmsub scroll_right() clobbers(A,X,Y) {
|
|||||||
; ---- scroll the whole screen 1 character to the right
|
; ---- scroll the whole screen 1 character to the right
|
||||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||||
%asm {{
|
%asm {{
|
||||||
|
_columns = P8ZP_SCRATCH_REG
|
||||||
|
_rcolv1 = P8ZP_SCRATCH_W1
|
||||||
|
_rcolv2 = P8ZP_SCRATCH_W1+1
|
||||||
jsr cbm.SCREEN
|
jsr cbm.SCREEN
|
||||||
dex
|
dex
|
||||||
stx _lx+1
|
stx _columns
|
||||||
txa
|
txa
|
||||||
asl a
|
asl a
|
||||||
dea
|
dea
|
||||||
sta _rcol+1
|
sta _rcolv1
|
||||||
ina
|
ina
|
||||||
ina
|
ina
|
||||||
sta _rcol2+1
|
sta _rcolv2
|
||||||
dey
|
dey
|
||||||
sty P8ZP_SCRATCH_B1 ; number of rows to scroll
|
sty P8ZP_SCRATCH_B1 ; number of rows to scroll
|
||||||
|
|
||||||
@ -331,7 +338,7 @@ _nextline
|
|||||||
stz cx16.VERA_CTRL ; data port 0: source column
|
stz cx16.VERA_CTRL ; data port 0: source column
|
||||||
lda #%00011000 | VERA_TEXTMATRIX>>16 ; auto decrement 1
|
lda #%00011000 | VERA_TEXTMATRIX>>16 ; auto decrement 1
|
||||||
sta cx16.VERA_ADDR_H
|
sta cx16.VERA_ADDR_H
|
||||||
_rcol lda #79*2-1 ; modified
|
lda _rcolv1
|
||||||
sta cx16.VERA_ADDR_L ; begin in rightmost column minus one
|
sta cx16.VERA_ADDR_L ; begin in rightmost column minus one
|
||||||
lda P8ZP_SCRATCH_B1
|
lda P8ZP_SCRATCH_B1
|
||||||
clc
|
clc
|
||||||
@ -342,11 +349,11 @@ _rcol lda #79*2-1 ; modified
|
|||||||
sta cx16.VERA_CTRL ; data port 1: destination column
|
sta cx16.VERA_CTRL ; data port 1: destination column
|
||||||
lda #%00011000 | VERA_TEXTMATRIX>>16 ; auto decrement 1
|
lda #%00011000 | VERA_TEXTMATRIX>>16 ; auto decrement 1
|
||||||
sta cx16.VERA_ADDR_H
|
sta cx16.VERA_ADDR_H
|
||||||
_rcol2 lda #79*2+1 ; modified
|
lda _rcolv2
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
sty cx16.VERA_ADDR_M
|
sty cx16.VERA_ADDR_M
|
||||||
|
|
||||||
_lx ldx #0 ; modified
|
ldx _columns
|
||||||
- lda cx16.VERA_DATA0
|
- lda cx16.VERA_DATA0
|
||||||
sta cx16.VERA_DATA1 ; copy char
|
sta cx16.VERA_DATA1 ; copy char
|
||||||
lda cx16.VERA_DATA0
|
lda cx16.VERA_DATA0
|
||||||
@ -367,7 +374,7 @@ asmsub scroll_up() clobbers(A, X, Y) {
|
|||||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr cbm.SCREEN
|
jsr cbm.SCREEN
|
||||||
stx _nextline+1
|
stx P8ZP_SCRATCH_REG ; columns
|
||||||
dey
|
dey
|
||||||
sty P8ZP_SCRATCH_B1
|
sty P8ZP_SCRATCH_B1
|
||||||
stz cx16.VERA_CTRL ; data port 0 is source
|
stz cx16.VERA_CTRL ; data port 0 is source
|
||||||
@ -386,7 +393,7 @@ asmsub scroll_up() clobbers(A, X, Y) {
|
|||||||
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||||
|
|
||||||
_nextline
|
_nextline
|
||||||
ldx #80 ; modified
|
ldx P8ZP_SCRATCH_REG ; columns
|
||||||
- lda cx16.VERA_DATA0
|
- lda cx16.VERA_DATA0
|
||||||
sta cx16.VERA_DATA1 ; copy char
|
sta cx16.VERA_DATA1 ; copy char
|
||||||
lda cx16.VERA_DATA0
|
lda cx16.VERA_DATA0
|
||||||
@ -415,7 +422,7 @@ asmsub scroll_down() clobbers(A, X, Y) {
|
|||||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr cbm.SCREEN
|
jsr cbm.SCREEN
|
||||||
stx _nextline+1
|
stx P8ZP_SCRATCH_REG ; columns
|
||||||
dey
|
dey
|
||||||
sty P8ZP_SCRATCH_B1
|
sty P8ZP_SCRATCH_B1
|
||||||
stz cx16.VERA_CTRL ; data port 0 is source
|
stz cx16.VERA_CTRL ; data port 0 is source
|
||||||
@ -440,7 +447,7 @@ asmsub scroll_down() clobbers(A, X, Y) {
|
|||||||
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||||
|
|
||||||
_nextline
|
_nextline
|
||||||
ldx #80 ; modified
|
ldx P8ZP_SCRATCH_REG ; columns
|
||||||
- lda cx16.VERA_DATA0
|
- lda cx16.VERA_DATA0
|
||||||
sta cx16.VERA_DATA1 ; copy char
|
sta cx16.VERA_DATA1 ; copy char
|
||||||
lda cx16.VERA_DATA0
|
lda cx16.VERA_DATA0
|
||||||
|
@ -64,8 +64,6 @@ multiply_words .proc
|
|||||||
; The routine also works for NEGATIVE (signed) word values, but ONLY the lower 16 bits of the result are correct then!
|
; The routine also works for NEGATIVE (signed) word values, but ONLY the lower 16 bits of the result are correct then!
|
||||||
; Prog8 only uses those so that's not an issue, but math.mul16_last_upper() no longer gives the correct result here.
|
; Prog8 only uses those so that's not an issue, but math.mul16_last_upper() no longer gives the correct result here.
|
||||||
|
|
||||||
; TODO: Romable
|
|
||||||
|
|
||||||
; mult62.a
|
; mult62.a
|
||||||
; from: https://github.com/TobyLobster/multiply_test/blob/main/tests/mult62.a
|
; from: https://github.com/TobyLobster/multiply_test/blob/main/tests/mult62.a
|
||||||
; based on Dr Jefyll, http://forum.6502.org/viewtopic.php?f=9&t=689&start=0#p19958
|
; based on Dr Jefyll, http://forum.6502.org/viewtopic.php?f=9&t=689&start=0#p19958
|
||||||
@ -182,14 +180,14 @@ _inner_loop2
|
|||||||
ldy result+1
|
ldy result+1
|
||||||
rts
|
rts
|
||||||
|
|
||||||
result .byte 0,0,0,0 ; routine could be faster if this were in Zeropage...
|
.section BSS
|
||||||
|
result .byte ?,?,?,? ; routine could be faster if this were in Zeropage...
|
||||||
|
.send BSS
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
divmod_b_asm .proc
|
divmod_b_asm .proc
|
||||||
; signed byte division: make everything positive and fix sign afterwards
|
; signed byte division: make everything positive and fix sign afterwards
|
||||||
; TODO: Romable
|
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
tya
|
tya
|
||||||
eor P8ZP_SCRATCH_B1
|
eor P8ZP_SCRATCH_B1
|
||||||
@ -217,7 +215,9 @@ divmod_b_asm .proc
|
|||||||
adc #0 ; negate result
|
adc #0 ; negate result
|
||||||
tay
|
tay
|
||||||
+ rts
|
+ rts
|
||||||
_remainder .byte 0
|
.section BSS
|
||||||
|
_remainder .byte ?
|
||||||
|
.send BSS
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
@ -241,6 +241,28 @@ divmod_ub_asm .proc
|
|||||||
rts
|
rts
|
||||||
.pend
|
.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
|
divmod_w_asm .proc
|
||||||
; signed word division: make everything positive and fix sign afterwards
|
; signed word division: make everything positive and fix sign afterwards
|
||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
@ -289,8 +311,6 @@ divmod_uw_asm .proc
|
|||||||
; input: P8ZP_SCRATCH_W1 in ZP: 16 bit number, A/Y: 16 bit divisor
|
; input: P8ZP_SCRATCH_W1 in ZP: 16 bit number, A/Y: 16 bit divisor
|
||||||
; output: P8ZP_SCRATCH_W2 in ZP: 16 bit remainder, A/Y: 16 bit division result
|
; output: P8ZP_SCRATCH_W2 in ZP: 16 bit remainder, A/Y: 16 bit division result
|
||||||
; division by zero will result in quotient = 65535 and remainder = divident
|
; division by zero will result in quotient = 65535 and remainder = divident
|
||||||
; TODO: Romable
|
|
||||||
|
|
||||||
|
|
||||||
dividend = P8ZP_SCRATCH_W1
|
dividend = P8ZP_SCRATCH_W1
|
||||||
remainder = P8ZP_SCRATCH_W2
|
remainder = P8ZP_SCRATCH_W2
|
||||||
@ -325,15 +345,15 @@ result = dividend ;save memory by reusing divident to store the result
|
|||||||
lda result
|
lda result
|
||||||
ldy result+1
|
ldy result+1
|
||||||
rts
|
rts
|
||||||
_divisor .word 0
|
.section BSS
|
||||||
|
_divisor .word ?
|
||||||
|
.send BSS
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
randword .proc
|
randword .proc
|
||||||
; -- 16 bit pseudo random number generator into AY
|
; -- 16 bit pseudo random number generator into AY
|
||||||
; default seed = $00c2 $1137
|
; default seed = $00c2 $1137. NOTE: uses self-modifying code so won't work in ROM (use randword_rom instead)
|
||||||
; routine from https://codebase64.org/doku.php?id=base:x_abc_random_number_generator_8_16_bit
|
; routine from https://codebase64.org/doku.php?id=base:x_abc_random_number_generator_8_16_bit
|
||||||
; TODO: Romable
|
|
||||||
inc x1
|
inc x1
|
||||||
clc
|
clc
|
||||||
x1=*+1
|
x1=*+1
|
||||||
@ -354,7 +374,34 @@ b1=*+1
|
|||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
randbyte = randword ; -- 8 bit pseudo random number generator into A (by just reusing randword)
|
randword_rom .proc
|
||||||
|
; -- 16 bit pseudo random number generator into AY. Can run from ROM.
|
||||||
|
; NOTE: you have to set the initial seed using randseed_rom! (a good default seed = $00c2 $1137)
|
||||||
|
; routine from https://codebase64.org/doku.php?id=base:x_abc_random_number_generator_8_16_bit
|
||||||
|
inc _x1
|
||||||
|
clc
|
||||||
|
lda _x1
|
||||||
|
eor _c1
|
||||||
|
eor _a1
|
||||||
|
sta _a1
|
||||||
|
adc _b1
|
||||||
|
sta _b1
|
||||||
|
lsr a
|
||||||
|
eor _a1
|
||||||
|
adc _c1
|
||||||
|
sta _c1
|
||||||
|
ldy _b1
|
||||||
|
rts
|
||||||
|
.section BSS
|
||||||
|
_x1 .byte ?
|
||||||
|
_c1 .byte ?
|
||||||
|
_a1 .byte ?
|
||||||
|
_b1 .byte ?
|
||||||
|
.send BSS
|
||||||
|
.pend
|
||||||
|
|
||||||
|
randbyte = randword ; -- 8 bit pseudo random number generator into A (by just reusing randword) NOTE: can not run from ROM
|
||||||
|
randbyte_rom = randword_rom ; -- 8 bit pseudo random number generator into A (by just reusing randword). Can run from ROM
|
||||||
|
|
||||||
|
|
||||||
; ----------- optimized multiplications (in-place A (byte) and ?? (word)) : ---------
|
; ----------- optimized multiplications (in-place A (byte) and ?? (word)) : ---------
|
||||||
|
@ -83,32 +83,58 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub rnd_rom() clobbers(Y) -> ubyte @A {
|
||||||
|
%asm {{
|
||||||
|
jmp prog8_math.randbyte_rom
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub rndw() -> uword @AY {
|
asmsub rndw() -> uword @AY {
|
||||||
%asm {{
|
%asm {{
|
||||||
jmp prog8_math.randword
|
jmp prog8_math.randword
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub rndw_rom() -> uword @AY {
|
||||||
|
%asm {{
|
||||||
|
jmp prog8_math.randword_rom
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
sub randrange(ubyte n) -> ubyte {
|
sub randrange(ubyte n) -> ubyte {
|
||||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
|
; -- return random number uniformly distributed from 0 to n-1
|
||||||
cx16.r0H = 255 / n * n
|
; NOTE: does not work for code in ROM, use randrange_rom instead for that
|
||||||
do {
|
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
|
||||||
cx16.r0L = math.rnd()
|
cx16.r0 = math.rnd() * (n as uword)
|
||||||
} until cx16.r0L < cx16.r0H
|
return cx16.r0H
|
||||||
return cx16.r0L % n
|
}
|
||||||
|
|
||||||
|
sub randrange_rom(ubyte n) -> ubyte {
|
||||||
|
; -- 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 {
|
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
|
||||||
cx16.r1 = 65535 / n * n
|
; NOTE: does not work for code in ROM
|
||||||
do {
|
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
|
||||||
cx16.r0 = math.rndw()
|
cx16.r0 = math.rndw() * n
|
||||||
} until cx16.r0 < cx16.r1
|
return math.mul16_last_upper()
|
||||||
return cx16.r0 % n
|
}
|
||||||
|
|
||||||
|
sub randrangew_rom(uword n) -> uword {
|
||||||
|
; -- 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) {
|
asmsub rndseed(uword seed1 @AY, uword seed2 @R0) clobbers(A,Y) {
|
||||||
; -- set new pseudo RNG's seed values. Defaults are: $00c2, $1137
|
; -- set new pseudo RNG's seed values. Defaults are: $00c2, $1137. NOTE: does not work for code in ROM, use rndseed_rom instead
|
||||||
%asm {{
|
%asm {{
|
||||||
sta prog8_math.randword.x1
|
sta prog8_math.randword.x1
|
||||||
sty prog8_math.randword.c1
|
sty prog8_math.randword.c1
|
||||||
@ -120,6 +146,19 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub rndseed_rom(uword seed1 @AY, uword seed2 @R0) clobbers(A,Y) {
|
||||||
|
; -- set new pseudo RNG's seed values (for the ROM-version of the RNG). Good defaults are: $00c2, $1137.
|
||||||
|
%asm {{
|
||||||
|
sta prog8_math.randword_rom._x1
|
||||||
|
sty prog8_math.randword_rom._c1
|
||||||
|
lda cx16.r0L
|
||||||
|
sta prog8_math.randword_rom._a1
|
||||||
|
lda cx16.r0H
|
||||||
|
sta prog8_math.randword_rom._b1
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub log2(ubyte value @A) -> ubyte @Y {
|
asmsub log2(ubyte value @A) -> ubyte @Y {
|
||||||
%asm {{
|
%asm {{
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
@ -658,25 +697,36 @@ log2_tab
|
|||||||
; Linear interpolation (LERP)
|
; Linear interpolation (LERP)
|
||||||
; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 255]
|
; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 255]
|
||||||
; guarantees v = v1 when t = 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 {
|
sub lerpw(uword v0, uword v1, uword t) -> uword {
|
||||||
; Linear interpolation (LERP) on word values
|
; Linear interpolation (LERP) on word values
|
||||||
; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 65535]
|
; 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
|
t *= v1-v0
|
||||||
cx16.r0 = math.mul16_last_upper()
|
cx16.r15 = math.mul16_last_upper()
|
||||||
if t!=0
|
if t!=0
|
||||||
cx16.r0++
|
cx16.r15++
|
||||||
return v0 + cx16.r0
|
return v0 + cx16.r15
|
||||||
}
|
}
|
||||||
|
|
||||||
sub interpolate(ubyte v, ubyte inputMin, ubyte inputMax, ubyte outputMin, ubyte outputMax) -> ubyte {
|
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]
|
; 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.
|
; Clobbers R15.
|
||||||
cx16.r0 = ((v-inputMin)*256+inputMax) / (inputMax-inputMin)
|
; (There is no version for words because of lack of precision in the fixed point calculation there)
|
||||||
cx16.r0 *= (outputMax-outputMin)
|
cx16.r15 = ((v-inputMin)*256+inputMax) / (inputMax-inputMin)
|
||||||
return cx16.r0H + outputMin
|
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 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_BOOL = sizeof(bool)
|
||||||
const ubyte SIZEOF_BYTE = 1
|
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||||
const ubyte SIZEOF_UBYTE = 1
|
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||||
const ubyte SIZEOF_WORD = 2
|
const ubyte SIZEOF_WORD = sizeof(word)
|
||||||
const ubyte SIZEOF_UWORD = 2
|
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||||
const ubyte SIZEOF_FLOAT = 0 ; undefined, no floats supported
|
const ubyte SIZEOF_LONG = sizeof(long)
|
||||||
|
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||||
const byte MIN_BYTE = -128
|
const byte MIN_BYTE = -128
|
||||||
const byte MAX_BYTE = 127
|
const byte MAX_BYTE = 127
|
||||||
const ubyte MIN_UBYTE = 0
|
const ubyte MIN_UBYTE = 0
|
||||||
@ -360,7 +361,6 @@ _no_msb_size
|
|||||||
}
|
}
|
||||||
|
|
||||||
asmsub save_prog8_internals() {
|
asmsub save_prog8_internals() {
|
||||||
; TODO: Romable
|
|
||||||
%asm {{
|
%asm {{
|
||||||
lda P8ZP_SCRATCH_B1
|
lda P8ZP_SCRATCH_B1
|
||||||
sta save_SCRATCH_ZPB1
|
sta save_SCRATCH_ZPB1
|
||||||
@ -375,10 +375,12 @@ _no_msb_size
|
|||||||
lda P8ZP_SCRATCH_W2+1
|
lda P8ZP_SCRATCH_W2+1
|
||||||
sta save_SCRATCH_ZPWORD2+1
|
sta save_SCRATCH_ZPWORD2+1
|
||||||
rts
|
rts
|
||||||
save_SCRATCH_ZPB1 .byte 0
|
.section BSS
|
||||||
save_SCRATCH_ZPREG .byte 0
|
save_SCRATCH_ZPB1 .byte ?
|
||||||
save_SCRATCH_ZPWORD1 .word 0
|
save_SCRATCH_ZPREG .byte ?
|
||||||
save_SCRATCH_ZPWORD2 .word 0
|
save_SCRATCH_ZPWORD1 .word ?
|
||||||
|
save_SCRATCH_ZPWORD2 .word ?
|
||||||
|
.send BSS
|
||||||
; !notreached!
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -429,7 +431,7 @@ save_SCRATCH_ZPWORD2 .word 0
|
|||||||
sta p8_sys_startup.cleanup_at_exit._exitcode
|
sta p8_sys_startup.cleanup_at_exit._exitcode
|
||||||
lda #0
|
lda #0
|
||||||
rol a
|
rol a
|
||||||
sta p8_sys_startup.cleanup_at_exit._exitcodeCarry
|
sta p8_sys_startup.cleanup_at_exit._exitcarry
|
||||||
stx p8_sys_startup.cleanup_at_exit._exitcodeX
|
stx p8_sys_startup.cleanup_at_exit._exitcodeX
|
||||||
sty p8_sys_startup.cleanup_at_exit._exitcodeY
|
sty p8_sys_startup.cleanup_at_exit._exitcodeY
|
||||||
ldx prog8_lib.orig_stackpointer
|
ldx prog8_lib.orig_stackpointer
|
||||||
@ -604,7 +606,6 @@ cx16 {
|
|||||||
&byte r15sH = $7fff
|
&byte r15sH = $7fff
|
||||||
|
|
||||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||||
; TODO: Romable
|
|
||||||
%asm {{
|
%asm {{
|
||||||
ldy #31
|
ldy #31
|
||||||
- lda cx16.r0,y
|
- lda cx16.r0,y
|
||||||
@ -613,9 +614,11 @@ cx16 {
|
|||||||
bpl -
|
bpl -
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
.section BSS
|
||||||
_cx16_vreg_storage
|
_cx16_vreg_storage
|
||||||
.word 0,0,0,0,0,0,0,0
|
.word ?,?,?,?,?,?,?,?
|
||||||
.word 0,0,0,0,0,0,0,0
|
.word ?,?,?,?,?,?,?,?
|
||||||
|
.send BSS
|
||||||
; !notreached!
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -668,16 +671,21 @@ asmsub init_system_phase2() {
|
|||||||
asmsub cleanup_at_exit() {
|
asmsub cleanup_at_exit() {
|
||||||
; executed when the main subroutine does rts
|
; executed when the main subroutine does rts
|
||||||
%asm {{
|
%asm {{
|
||||||
_exitcodeCarry = *+1
|
lda _exitcarry
|
||||||
lda #0
|
|
||||||
lsr a
|
lsr a
|
||||||
_exitcode = *+1
|
lda _exitcode
|
||||||
lda #0 ; exit code possibly modified in exit()
|
ldx _exitcodeX
|
||||||
_exitcodeX = *+1
|
ldy _exitcodeY
|
||||||
ldx #0
|
|
||||||
_exitcodeY = *+1
|
|
||||||
ldy #0
|
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
.section BSS
|
||||||
|
_exitcarry .byte ?
|
||||||
|
_exitcode .byte ?
|
||||||
|
_exitcodeX .byte ?
|
||||||
|
_exitcodeY .byte ?
|
||||||
|
.send BSS
|
||||||
|
|
||||||
|
; !notreached!
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,15 +159,16 @@ asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y)
|
|||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda _screenrows+1,y
|
lda _screenrows+1,y
|
||||||
sta _mod+2
|
sta P8ZP_SCRATCH_W1+1
|
||||||
txa
|
txa
|
||||||
clc
|
clc
|
||||||
adc _screenrows,y
|
adc _screenrows,y
|
||||||
sta _mod+1
|
sta P8ZP_SCRATCH_W1
|
||||||
bcc +
|
bcc +
|
||||||
inc _mod+2
|
inc P8ZP_SCRATCH_W1+1
|
||||||
+ pla
|
+ pla
|
||||||
_mod sta $ffff ; modified
|
ldy #0
|
||||||
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
rts
|
rts
|
||||||
|
|
||||||
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
||||||
@ -183,14 +184,15 @@ asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
|||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda setchr._screenrows+1,y
|
lda setchr._screenrows+1,y
|
||||||
sta _mod+2
|
sta P8ZP_SCRATCH_W1+1
|
||||||
pla
|
pla
|
||||||
clc
|
clc
|
||||||
adc setchr._screenrows,y
|
adc setchr._screenrows,y
|
||||||
sta _mod+1
|
sta P8ZP_SCRATCH_W1
|
||||||
bcc _mod
|
bcc +
|
||||||
inc _mod+2
|
inc P8ZP_SCRATCH_W1+1
|
||||||
_mod lda $ffff ; modified
|
+ ldy #0
|
||||||
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -200,22 +202,23 @@ sub setclr (ubyte col, ubyte row, ubyte color) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor_ignored) {
|
||||||
; ---- set char at the given position on the screen. charcolor is ignored on PET
|
; ---- set char at the given position on the screen. charcolor is ignored on PET
|
||||||
%asm {{
|
%asm {{
|
||||||
lda row
|
lda row
|
||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda setchr._screenrows+1,y
|
lda setchr._screenrows+1,y
|
||||||
sta _charmod+2
|
sta P8ZP_SCRATCH_W1+1
|
||||||
lda setchr._screenrows,y
|
lda setchr._screenrows,y
|
||||||
clc
|
clc
|
||||||
adc col
|
adc col
|
||||||
sta _charmod+1
|
sta P8ZP_SCRATCH_W1
|
||||||
bcc +
|
bcc +
|
||||||
inc _charmod+2
|
inc P8ZP_SCRATCH_W1+1
|
||||||
+ lda character
|
+ lda character
|
||||||
_charmod sta $ffff ; modified
|
ldy #0
|
||||||
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -184,7 +184,6 @@ func_sort_uw .proc
|
|||||||
; sorting subroutine coded by mats rosengren (mats.rosengren@esa.int)
|
; sorting subroutine coded by mats rosengren (mats.rosengren@esa.int)
|
||||||
; input: address of array to sort in P8ZP_SCRATCH_W1, length in A
|
; input: address of array to sort in P8ZP_SCRATCH_W1, length in A
|
||||||
; first: subtract 2 of the pointer
|
; first: subtract 2 of the pointer
|
||||||
; TODO: Romable
|
|
||||||
asl a
|
asl a
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
lda P8ZP_SCRATCH_W1
|
lda P8ZP_SCRATCH_W1
|
||||||
@ -237,8 +236,10 @@ _l3 ldy P8ZP_SCRATCH_B1 ;where the largest value shall be put
|
|||||||
dec P8ZP_SCRATCH_B1
|
dec P8ZP_SCRATCH_B1
|
||||||
bne _sort_loop ;start working with the shorter sequence
|
bne _sort_loop ;start working with the shorter sequence
|
||||||
rts
|
rts
|
||||||
_work1 .byte 0
|
.section BSS
|
||||||
_work3 .word 0
|
_work1 .byte ?
|
||||||
|
_work3 .word ?
|
||||||
|
.send BSS
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
@ -247,7 +248,6 @@ func_sort_w .proc
|
|||||||
; sorting subroutine coded by mats rosengren (mats.rosengren@esa.int)
|
; sorting subroutine coded by mats rosengren (mats.rosengren@esa.int)
|
||||||
; input: address of array to sort in P8ZP_SCRATCH_W1, length in A
|
; input: address of array to sort in P8ZP_SCRATCH_W1, length in A
|
||||||
; first: subtract 2 of the pointer
|
; first: subtract 2 of the pointer
|
||||||
; TODO: Romable
|
|
||||||
asl a
|
asl a
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
lda P8ZP_SCRATCH_W1
|
lda P8ZP_SCRATCH_W1
|
||||||
@ -301,8 +301,10 @@ _l3 ldy P8ZP_SCRATCH_B1 ;where the largest value shall be put
|
|||||||
dec P8ZP_SCRATCH_B1
|
dec P8ZP_SCRATCH_B1
|
||||||
bne _sort_loop ;start working with the shorter sequence
|
bne _sort_loop ;start working with the shorter sequence
|
||||||
rts
|
rts
|
||||||
_work1 .byte 0
|
.section BSS
|
||||||
_work3 .word 0
|
_work1 .byte ?
|
||||||
|
_work3 .word ?
|
||||||
|
.send BSS
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
; Internal library routines - always included by the compiler
|
; Internal library routines - always included by the compiler
|
||||||
; Generic machine independent 6502 code.
|
; Generic machine independent 6502 code.
|
||||||
|
|
||||||
|
.section BSS_NOCLEAR
|
||||||
orig_stackpointer .byte 0 ; stores the Stack pointer register at program start
|
orig_stackpointer .byte ? ; stores the Stack pointer register at program start
|
||||||
|
.send BSS_NOCLEAR
|
||||||
|
|
||||||
program_startup_clear_bss .proc
|
program_startup_clear_bss .proc
|
||||||
; this is always ran first thing from the start routine to clear out the BSS area
|
; this is always ran first thing from the start routine to clear out the BSS area
|
||||||
@ -146,11 +146,10 @@ memcopy16_up .proc
|
|||||||
memset .proc
|
memset .proc
|
||||||
; -- fill memory from (P8ZP_SCRATCH_W1), length XY, with value in A.
|
; -- fill memory from (P8ZP_SCRATCH_W1), length XY, with value in A.
|
||||||
; clobbers X, Y
|
; clobbers X, Y
|
||||||
; TODO: Romable
|
|
||||||
stx P8ZP_SCRATCH_B1
|
stx P8ZP_SCRATCH_B1
|
||||||
sty _save_reg
|
sty P8ZP_SCRATCH_REG
|
||||||
ldy #0
|
ldy #0
|
||||||
ldx _save_reg
|
ldx P8ZP_SCRATCH_REG
|
||||||
beq _lastpage
|
beq _lastpage
|
||||||
|
|
||||||
_fullpage sta (P8ZP_SCRATCH_W1),y
|
_fullpage sta (P8ZP_SCRATCH_W1),y
|
||||||
@ -167,17 +166,14 @@ _lastpage ldy P8ZP_SCRATCH_B1
|
|||||||
bne -
|
bne -
|
||||||
|
|
||||||
+ rts
|
+ rts
|
||||||
_save_reg .byte 0
|
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
memsetw .proc
|
memsetw .proc
|
||||||
; -- fill memory from (P8ZP_SCRATCH_W1) number of words in P8ZP_SCRATCH_W2, with word value in AY.
|
; -- fill memory from (P8ZP_SCRATCH_W1) number of words in P8ZP_SCRATCH_W2, with word value in AY.
|
||||||
; clobbers A, X, Y
|
; clobbers A, X, Y
|
||||||
sta _mod1+1 ; self-modify
|
sta _val
|
||||||
sty _mod1b+1 ; self-modify
|
sty _val+1
|
||||||
sta _mod2+1 ; self-modify
|
|
||||||
sty _mod2b+1 ; self-modify
|
|
||||||
ldx P8ZP_SCRATCH_W1
|
ldx P8ZP_SCRATCH_W1
|
||||||
stx P8ZP_SCRATCH_B1
|
stx P8ZP_SCRATCH_B1
|
||||||
ldx P8ZP_SCRATCH_W1+1
|
ldx P8ZP_SCRATCH_W1+1
|
||||||
@ -189,11 +185,11 @@ memsetw .proc
|
|||||||
beq _lastpage
|
beq _lastpage
|
||||||
|
|
||||||
_fullpage
|
_fullpage
|
||||||
_mod1 lda #0 ; self-modified
|
lda _val
|
||||||
sta (P8ZP_SCRATCH_W1),y ; first page
|
sta (P8ZP_SCRATCH_W1),y ; first page
|
||||||
sta (P8ZP_SCRATCH_B1),y ; second page
|
sta (P8ZP_SCRATCH_B1),y ; second page
|
||||||
iny
|
iny
|
||||||
_mod1b lda #0 ; self-modified
|
lda _val+1
|
||||||
sta (P8ZP_SCRATCH_W1),y ; first page
|
sta (P8ZP_SCRATCH_W1),y ; first page
|
||||||
sta (P8ZP_SCRATCH_B1),y ; second page
|
sta (P8ZP_SCRATCH_B1),y ; second page
|
||||||
iny
|
iny
|
||||||
@ -210,12 +206,12 @@ _lastpage ldx P8ZP_SCRATCH_W2
|
|||||||
|
|
||||||
ldy #0
|
ldy #0
|
||||||
-
|
-
|
||||||
_mod2 lda #0 ; self-modified
|
lda _val
|
||||||
sta (P8ZP_SCRATCH_W1), y
|
sta (P8ZP_SCRATCH_W1), y
|
||||||
inc P8ZP_SCRATCH_W1
|
inc P8ZP_SCRATCH_W1
|
||||||
bne _mod2b
|
bne +
|
||||||
inc P8ZP_SCRATCH_W1+1
|
inc P8ZP_SCRATCH_W1+1
|
||||||
_mod2b lda #0 ; self-modified
|
+ lda _val+1
|
||||||
sta (P8ZP_SCRATCH_W1), y
|
sta (P8ZP_SCRATCH_W1), y
|
||||||
inc P8ZP_SCRATCH_W1
|
inc P8ZP_SCRATCH_W1
|
||||||
bne +
|
bne +
|
||||||
@ -223,6 +219,9 @@ _mod2b lda #0 ; self-modified
|
|||||||
+ dex
|
+ dex
|
||||||
bne -
|
bne -
|
||||||
_done rts
|
_done rts
|
||||||
|
.section BSS
|
||||||
|
_val .word ?
|
||||||
|
.send BSS
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
@ -269,7 +268,6 @@ strcpy .proc
|
|||||||
|
|
||||||
strcmp_expression .proc
|
strcmp_expression .proc
|
||||||
; -- compare strings, result in A
|
; -- compare strings, result in A
|
||||||
; TODO: Romable
|
|
||||||
lda _arg_s2
|
lda _arg_s2
|
||||||
ldy _arg_s2+1
|
ldy _arg_s2+1
|
||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
@ -277,8 +275,10 @@ strcmp_expression .proc
|
|||||||
lda _arg_s1
|
lda _arg_s1
|
||||||
ldy _arg_s1+1
|
ldy _arg_s1+1
|
||||||
jmp strcmp_mem
|
jmp strcmp_mem
|
||||||
_arg_s1 .word 0
|
.section BSS
|
||||||
_arg_s2 .word 0
|
_arg_s1 .word ?
|
||||||
|
_arg_s2 .word ?
|
||||||
|
.send BSS
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
strcmp_mem .proc
|
strcmp_mem .proc
|
||||||
@ -426,80 +426,6 @@ _found lda #1
|
|||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
arraycopy_split_to_normal_words .proc
|
|
||||||
; P8ZP_SCRATCH_W1 = start of lsb array
|
|
||||||
; P8ZP_SCRATCH_W2 = start of msb array
|
|
||||||
; AY = start of normal word target array
|
|
||||||
; X = number of elements to copy
|
|
||||||
sta _modlsb+1
|
|
||||||
sty _modlsb+2
|
|
||||||
clc
|
|
||||||
adc #1
|
|
||||||
bne +
|
|
||||||
iny
|
|
||||||
+ sta _modmsb+1
|
|
||||||
sty _modmsb+2
|
|
||||||
ldy #0
|
|
||||||
- lda (P8ZP_SCRATCH_W1),y
|
|
||||||
_modlsb sta $ffff ; modified lsb store
|
|
||||||
lda _modlsb+1
|
|
||||||
clc
|
|
||||||
adc #2
|
|
||||||
sta _modlsb+1
|
|
||||||
bcc +
|
|
||||||
inc _modlsb+2
|
|
||||||
+ lda (P8ZP_SCRATCH_W2),y
|
|
||||||
_modmsb sta $ffff ; modified msb store
|
|
||||||
lda _modmsb+1
|
|
||||||
clc
|
|
||||||
adc #2
|
|
||||||
sta _modmsb+1
|
|
||||||
bcc +
|
|
||||||
inc _modmsb+2
|
|
||||||
+ iny
|
|
||||||
dex
|
|
||||||
bne -
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
||||||
arraycopy_normal_to_split_words .proc
|
|
||||||
; P8ZP_SCRATCH_W1 = start of target lsb array
|
|
||||||
; P8ZP_SCRATCH_W2 = start of target msb array
|
|
||||||
; AY = start of normal word source array
|
|
||||||
; X = number of elements to copy
|
|
||||||
sta _modsrclsb+1
|
|
||||||
sty _modsrclsb+2
|
|
||||||
clc
|
|
||||||
adc #1
|
|
||||||
bne +
|
|
||||||
iny
|
|
||||||
+ sta _modsrcmsb+1
|
|
||||||
sty _modsrcmsb+2
|
|
||||||
ldy #0
|
|
||||||
_modsrclsb lda $ffff ; modified lsb read
|
|
||||||
sta (P8ZP_SCRATCH_W1),y
|
|
||||||
lda _modsrclsb+1
|
|
||||||
clc
|
|
||||||
adc #2
|
|
||||||
sta _modsrclsb+1
|
|
||||||
bcc +
|
|
||||||
inc _modsrclsb+2
|
|
||||||
+
|
|
||||||
_modsrcmsb lda $ffff ; modnfied msb read
|
|
||||||
sta (P8ZP_SCRATCH_W2),y
|
|
||||||
lda _modsrcmsb+1
|
|
||||||
clc
|
|
||||||
adc #2
|
|
||||||
sta _modsrcmsb+1
|
|
||||||
bcc +
|
|
||||||
inc _modsrcmsb+2
|
|
||||||
+ iny
|
|
||||||
dex
|
|
||||||
bne _modsrclsb
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
memcopy_small .proc
|
memcopy_small .proc
|
||||||
; copy up to a single page (256 bytes) of memory.
|
; copy up to a single page (256 bytes) of memory.
|
||||||
; note: only works for NON-OVERLAPPING memory regions!
|
; note: only works for NON-OVERLAPPING memory regions!
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
; shared CBM (C64/C128) disk drive I/O routines.
|
; shared CBM (C64/C128) disk drive I/O routines.
|
||||||
|
|
||||||
|
; About the Dos/Drive error status message:
|
||||||
|
; The routines don't usually read/clear the dos/drive error status and message. ("blinking red led")
|
||||||
|
; In case of an error, you usually have to read/clear that yourself (with status() for example).
|
||||||
|
|
||||||
|
|
||||||
%import textio
|
%import textio
|
||||||
%import conv
|
%import conv
|
||||||
%import strings
|
%import strings
|
||||||
@ -10,6 +15,7 @@ diskio {
|
|||||||
|
|
||||||
const ubyte READ_IO_CHANNEL=12
|
const ubyte READ_IO_CHANNEL=12
|
||||||
const ubyte WRITE_IO_CHANNEL=13
|
const ubyte WRITE_IO_CHANNEL=13
|
||||||
|
const ubyte STATUS_EOF=$40
|
||||||
|
|
||||||
ubyte @shared drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
|
ubyte @shared drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
|
||||||
|
|
||||||
@ -72,7 +78,7 @@ io_error:
|
|||||||
cbm.CLRCHN() ; restore default i/o devices
|
cbm.CLRCHN() ; restore default i/o devices
|
||||||
cbm.CLOSE(READ_IO_CHANNEL)
|
cbm.CLOSE(READ_IO_CHANNEL)
|
||||||
|
|
||||||
if status!=0 and status & $40 == 0 { ; bit 6=end of file
|
if status!=0 and status & STATUS_EOF == 0 {
|
||||||
txt.print("\ni/o error, status: ")
|
txt.print("\ni/o error, status: ")
|
||||||
txt.print_ub(status)
|
txt.print_ub(status)
|
||||||
txt.nl()
|
txt.nl()
|
||||||
@ -322,7 +328,7 @@ close_end:
|
|||||||
if cbm.READST()==0 {
|
if cbm.READST()==0 {
|
||||||
iteration_in_progress = true
|
iteration_in_progress = true
|
||||||
void cbm.CHRIN() ; read first byte to test for file not found
|
void cbm.CHRIN() ; read first byte to test for file not found
|
||||||
if cbm.READST()==0 {
|
if cbm.READST() & ~STATUS_EOF == 0 {
|
||||||
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
|
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
|
||||||
void cbm.OPEN() ; re-open the file
|
void cbm.OPEN() ; re-open the file
|
||||||
cbm.CLRCHN() ; reset default i/o channels
|
cbm.CLRCHN() ; reset default i/o channels
|
||||||
@ -349,7 +355,7 @@ close_end:
|
|||||||
cbm.CLRCHN() ; reset default i/o channels
|
cbm.CLRCHN() ; reset default i/o channels
|
||||||
if cx16.r0L!=0 {
|
if cx16.r0L!=0 {
|
||||||
f_close()
|
f_close()
|
||||||
if cx16.r0L & $40 == 0
|
if cx16.r0L & STATUS_EOF == 0
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return 1
|
return 1
|
||||||
@ -358,28 +364,29 @@ close_end:
|
|||||||
list_blocks = 0 ; we reuse this variable for the total number of bytes read
|
list_blocks = 0 ; we reuse this variable for the total number of bytes read
|
||||||
%asm {{
|
%asm {{
|
||||||
lda bufferpointer
|
lda bufferpointer
|
||||||
sta m_in_buffer+1
|
sta P8ZP_SCRATCH_W1
|
||||||
lda bufferpointer+1
|
lda bufferpointer+1
|
||||||
sta m_in_buffer+2
|
sta P8ZP_SCRATCH_W1+1
|
||||||
}}
|
}}
|
||||||
while num_bytes!=0 {
|
while num_bytes!=0 {
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr cbm.CHRIN
|
jsr cbm.CHRIN
|
||||||
m_in_buffer sta $ffff
|
ldy #0
|
||||||
inc m_in_buffer+1
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
|
inc P8ZP_SCRATCH_W1
|
||||||
bne +
|
bne +
|
||||||
inc m_in_buffer+2
|
inc P8ZP_SCRATCH_W1+1
|
||||||
+
|
+
|
||||||
}}
|
}}
|
||||||
|
list_blocks++
|
||||||
cx16.r0L = cbm.READST()
|
cx16.r0L = cbm.READST()
|
||||||
if_nz {
|
if_nz {
|
||||||
f_close()
|
f_close()
|
||||||
cbm.CLRCHN() ; reset default i/o channels
|
cbm.CLRCHN() ; reset default i/o channels
|
||||||
if cx16.r0L & $40 !=0 ; eof?
|
if cx16.r0L & STATUS_EOF !=0 ; eof?
|
||||||
return list_blocks ; number of bytes read
|
return list_blocks ; number of bytes read
|
||||||
return 0 ; error.
|
return 0 ; error.
|
||||||
}
|
}
|
||||||
list_blocks++
|
|
||||||
num_bytes--
|
num_bytes--
|
||||||
}
|
}
|
||||||
cbm.CLRCHN() ; reset default i/o channels
|
cbm.CLRCHN() ; reset default i/o channels
|
||||||
@ -387,8 +394,9 @@ m_in_buffer sta $ffff
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub f_read_all(uword bufferpointer) -> uword {
|
sub f_read_all(uword bufferpointer) -> uword {
|
||||||
; -- read the full contents of the file, returns number of bytes read.
|
; -- read the full rest of the file, returns number of bytes read.
|
||||||
; It is assumed the file size is less than 64 K.
|
; It is assumed the file size is less than 64 K.
|
||||||
|
; Usually you will just be using load() / load_raw() to read entire files!
|
||||||
if not iteration_in_progress
|
if not iteration_in_progress
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@ -396,6 +404,8 @@ m_in_buffer sta $ffff
|
|||||||
uword total_read = 0
|
uword total_read = 0
|
||||||
while cbm.READST()==0 {
|
while cbm.READST()==0 {
|
||||||
cx16.r0 = f_read(bufferpointer, 256)
|
cx16.r0 = f_read(bufferpointer, 256)
|
||||||
|
if cx16.r0==0
|
||||||
|
break
|
||||||
total_read += cx16.r0
|
total_read += cx16.r0
|
||||||
bufferpointer += cx16.r0
|
bufferpointer += cx16.r0
|
||||||
}
|
}
|
||||||
@ -643,6 +653,12 @@ io_error:
|
|||||||
return load(filenameptr, start_address)
|
return load(filenameptr, start_address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Load a prog8 compiled library binary blob at the given location into memory.
|
||||||
|
sub loadlib(uword libnameptr, uword libaddress) -> uword {
|
||||||
|
return load(libnameptr, libaddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sub delete(uword filenameptr) {
|
sub delete(uword filenameptr) {
|
||||||
; -- delete a file on the drive
|
; -- delete a file on the drive
|
||||||
list_filename[0] = 's'
|
list_filename[0] = 's'
|
||||||
@ -672,6 +688,7 @@ io_error:
|
|||||||
sub exists(str filename) -> bool {
|
sub exists(str filename) -> bool {
|
||||||
; -- returns true if the given file exists on the disk, otherwise false
|
; -- returns true if the given file exists on the disk, otherwise false
|
||||||
; DON'T use this if you already have a file open with f_open!
|
; DON'T use this if you already have a file open with f_open!
|
||||||
|
; NOTE: doesn't clear the dos error status and message, you'll have to read/clear that yourself (with status() for example)
|
||||||
if f_open(filename) {
|
if f_open(filename) {
|
||||||
f_close()
|
f_close()
|
||||||
return true
|
return true
|
||||||
|
@ -139,8 +139,8 @@ compression {
|
|||||||
asmsub decode_rle_srcfunc(uword source_function @AY, uword target @R0, uword maxsize @R1) clobbers(X) -> uword @AY {
|
asmsub decode_rle_srcfunc(uword source_function @AY, uword target @R0, uword maxsize @R1) clobbers(X) -> uword @AY {
|
||||||
; -- Decodes "ByteRun1" (aka PackBits) RLE compressed data. Control byte value 128 ends the decoding.
|
; -- Decodes "ByteRun1" (aka PackBits) RLE compressed data. Control byte value 128 ends the decoding.
|
||||||
; Also stops decompressing when the maxsize has been reached. Returns the size of the decompressed data.
|
; Also stops decompressing when the maxsize has been reached. Returns the size of the decompressed data.
|
||||||
; Instead of a source buffer, you provide a callback function that must return the next byte to compress in A.
|
; Instead of a source buffer, you provide a callback function that must return the next byte to decompress in A.
|
||||||
; TODO: Romable
|
; Note: the callback routine MUST NOT MODIFY the prog8 scratch variables such as P8ZP_SCRATCH_W1 etc!
|
||||||
%asm {{
|
%asm {{
|
||||||
sta _cb_mod1+1
|
sta _cb_mod1+1
|
||||||
sty _cb_mod1+2
|
sty _cb_mod1+2
|
||||||
@ -173,6 +173,7 @@ _loop
|
|||||||
|
|
||||||
; check control byte
|
; check control byte
|
||||||
_cb_mod1 jsr $ffff ; modified
|
_cb_mod1 jsr $ffff ; modified
|
||||||
|
cmp #0
|
||||||
bpl _literals
|
bpl _literals
|
||||||
cmp #128
|
cmp #128
|
||||||
beq _end
|
beq _end
|
||||||
@ -219,7 +220,10 @@ _cb_mod3 jsr $ffff ; modified
|
|||||||
inc P8ZP_SCRATCH_W2+1
|
inc P8ZP_SCRATCH_W2+1
|
||||||
bcs _loop
|
bcs _loop
|
||||||
|
|
||||||
_orig_target .word 0
|
.section BSS
|
||||||
|
_orig_target .word ?
|
||||||
|
.send BSS
|
||||||
|
|
||||||
_end
|
_end
|
||||||
; return w2-orig_target, the size of the decompressed data
|
; return w2-orig_target, the size of the decompressed data
|
||||||
lda P8ZP_SCRATCH_W2
|
lda P8ZP_SCRATCH_W2
|
||||||
@ -331,7 +335,10 @@ _literals
|
|||||||
inc P8ZP_SCRATCH_W2+1
|
inc P8ZP_SCRATCH_W2+1
|
||||||
bcs _loop
|
bcs _loop
|
||||||
|
|
||||||
_orig_target .word 0
|
.section BSS
|
||||||
|
_orig_target .word ?
|
||||||
|
.send BSS
|
||||||
|
|
||||||
_end
|
_end
|
||||||
; return w2-orig_target, the size of the decompressed data
|
; return w2-orig_target, the size of the decompressed data
|
||||||
lda P8ZP_SCRATCH_W2
|
lda P8ZP_SCRATCH_W2
|
||||||
|
@ -8,10 +8,11 @@ sorting {
|
|||||||
|
|
||||||
; NOTE: all word arrays are assumed to be @nosplit!!
|
; NOTE: all word arrays are assumed to be @nosplit!!
|
||||||
; NOTE: sorting is done in ascending order!!!
|
; NOTE: sorting is done in ascending order!!!
|
||||||
|
; Note: could be made slightly faster by using modifying dcode for the CPY after _loop but that compromises romability
|
||||||
|
|
||||||
asmsub gnomesort_ub(uword bytearray @AY, ubyte num_elements @X) {
|
asmsub gnomesort_ub(uword bytearray @AY, ubyte num_elements @X) {
|
||||||
%asm {{
|
%asm {{
|
||||||
stx _loop+1 ; modifying
|
stx P8ZP_SCRATCH_REG
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
@ -22,7 +23,7 @@ sorting {
|
|||||||
sty P8ZP_SCRATCH_W2+1
|
sty P8ZP_SCRATCH_W2+1
|
||||||
ldy #1 ; pos
|
ldy #1 ; pos
|
||||||
_loop
|
_loop
|
||||||
cpy #0 ; modified
|
cpy P8ZP_SCRATCH_REG
|
||||||
beq _done
|
beq _done
|
||||||
lda (P8ZP_SCRATCH_W1),y
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
cmp (P8ZP_SCRATCH_W2),y
|
cmp (P8ZP_SCRATCH_W2),y
|
||||||
@ -64,7 +65,10 @@ _done
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
sub gnomesort_uw(uword values, ubyte num_elements) {
|
sub gnomesort_uw(uword values, ubyte num_elements) {
|
||||||
; TODO optimize this more, rewrite in asm?
|
; When written in asm this is 10-20% faster, but unreadable. Not worth it.
|
||||||
|
; Also, sorting just an array of word numbers is very seldomly used, most often you
|
||||||
|
; need to sort other things associated with it as well and that is not done here anyway,
|
||||||
|
; so requires a custom user coded sorting routine anyway.
|
||||||
ubyte @zp pos = 1
|
ubyte @zp pos = 1
|
||||||
uword @requirezp ptr = values+2
|
uword @requirezp ptr = values+2
|
||||||
while pos != num_elements {
|
while pos != num_elements {
|
||||||
|
@ -10,6 +10,7 @@ strings {
|
|||||||
; This value is determined during runtime and counts upto the first terminating 0 byte in the string,
|
; This value is determined during runtime and counts upto the first terminating 0 byte in the string,
|
||||||
; regardless of the size of the string during compilation time. Don’t confuse this with len and sizeof!
|
; regardless of the size of the string during compilation time. Don’t confuse this with len and sizeof!
|
||||||
|
|
||||||
|
; uses P8ZP_SCRATCH_W1 to store the string address, do not change this, other routines here may depend on it (as optimization implementation detail)
|
||||||
%asm {{
|
%asm {{
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
@ -47,7 +48,6 @@ _loop dey
|
|||||||
bne _loop
|
bne _loop
|
||||||
+ rts
|
+ rts
|
||||||
}}
|
}}
|
||||||
; asmgen.out(" jsr prog8_lib.func_leftstr")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub right(uword source @AY, ubyte length @X, uword target @R1) clobbers(A,Y) {
|
asmsub right(uword source @AY, ubyte length @X, uword target @R1) clobbers(A,Y) {
|
||||||
@ -56,16 +56,16 @@ _loop dey
|
|||||||
; Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
|
; Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
|
||||||
; Modifies in-place, doesn’t return a value (so can’t be used in an expression).
|
; Modifies in-place, doesn’t return a value (so can’t be used in an expression).
|
||||||
%asm {{
|
%asm {{
|
||||||
; need to copy the the cx16 virtual registers to zeropage to be compatible with C64...
|
; need to copy the the cx16 virtual registers to zeropage to be compatible with C64...
|
||||||
stx P8ZP_SCRATCH_B1
|
stx P8ZP_SCRATCH_B1
|
||||||
sta cx16.r0
|
sta cx16.r0
|
||||||
sty cx16.r0+1
|
sty cx16.r0+1
|
||||||
jsr length
|
jsr length
|
||||||
tya
|
tya
|
||||||
sec
|
sec
|
||||||
sbc P8ZP_SCRATCH_B1
|
sbc P8ZP_SCRATCH_B1
|
||||||
clc
|
clc
|
||||||
adc cx16.r0
|
adc cx16.r0
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
lda cx16.r0+1
|
lda cx16.r0+1
|
||||||
adc #0
|
adc #0
|
||||||
@ -96,7 +96,7 @@ _loop dey
|
|||||||
; Also, you have to make sure yourself that start and length are within bounds of the strings.
|
; Also, you have to make sure yourself that start and length are within bounds of the strings.
|
||||||
; Modifies in-place, doesn’t return a value (so can’t be used in an expression).
|
; Modifies in-place, doesn’t return a value (so can’t be used in an expression).
|
||||||
%asm {{
|
%asm {{
|
||||||
; need to copy the the cx16 virtual registers to zeropage to be compatible with C64...
|
; need to copy the the cx16 virtual registers to zeropage to be compatible with C64...
|
||||||
; substr(source, target, start, length)
|
; substr(source, target, start, length)
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
@ -154,17 +154,13 @@ _found tya
|
|||||||
asmsub rfind(uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc {
|
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.
|
; 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).
|
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
|
||||||
; TODO: Romable
|
|
||||||
%asm {{
|
%asm {{
|
||||||
stx P8ZP_SCRATCH_B1
|
stx P8ZP_SCRATCH_B1
|
||||||
sta _str
|
; note: we make use of the fact that length() stores the string address AY in P8ZP_SCRATCH_W1 for us! we need that later
|
||||||
sty _str+1
|
|
||||||
jsr length
|
jsr length
|
||||||
|
cpy #0
|
||||||
|
beq _notfound
|
||||||
dey
|
dey
|
||||||
lda _str
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
lda _str+1
|
|
||||||
sta P8ZP_SCRATCH_W1+1
|
|
||||||
- lda (P8ZP_SCRATCH_W1),y
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
cmp P8ZP_SCRATCH_B1
|
cmp P8ZP_SCRATCH_B1
|
||||||
beq _found
|
beq _found
|
||||||
@ -177,9 +173,6 @@ _notfound lda #255
|
|||||||
_found tya
|
_found tya
|
||||||
sec
|
sec
|
||||||
rts
|
rts
|
||||||
|
|
||||||
_str .word 0
|
|
||||||
; !notreached!
|
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,16 +316,23 @@ _done rts
|
|||||||
}
|
}
|
||||||
|
|
||||||
asmsub pattern_match(str string @AY, str pattern @R0) clobbers(Y) -> bool @A {
|
asmsub pattern_match(str string @AY, str pattern @R0) clobbers(Y) -> bool @A {
|
||||||
|
; This routine matches a string against a pattern and returns with the carry bit set if they match, or clear if they don't.
|
||||||
|
; The two characters ? and * have a special meaning when they appear in the pattern. All other characters match themselves.
|
||||||
|
; ? matches any one character. For example, F?? matches FOO but not FU, and ?? matches all two-character strings.
|
||||||
|
; * matches any string, including the empty string.
|
||||||
|
; For example, F* matches all strings starting with F. *O*O* matches all strings with at least two Os. Finally, ?* matches all non-empty strings.
|
||||||
|
; Both the pattern and the string must be NUL-terminated (that it, followed with a 00 byte) and at most 255 characters long (excluding the NUL).
|
||||||
|
; Code taken from http://6502.org/source/strings/patmatch.htm
|
||||||
|
;
|
||||||
|
; Input: cx16.r0: A NUL-terminated, <255-length pattern
|
||||||
|
; AY: A NUL-terminated, <255-length string
|
||||||
|
;
|
||||||
|
; Output: A = 1 if the string matches the pattern, A = 0 if not.
|
||||||
|
;
|
||||||
|
; Notes: Clobbers A, X, Y. Each * in the pattern uses 4 bytes of stack.
|
||||||
|
; Does not work in ROM due to self-modifying code.
|
||||||
|
|
||||||
%asm {{
|
%asm {{
|
||||||
; pattern matching of a string.
|
|
||||||
; Input: cx16.r0: A NUL-terminated, <255-length pattern
|
|
||||||
; AY: A NUL-terminated, <255-length string
|
|
||||||
;
|
|
||||||
; Output: A = 1 if the string matches the pattern, A = 0 if not.
|
|
||||||
;
|
|
||||||
; Notes: Clobbers A, X, Y. Each * in the pattern uses 4 bytes of stack.
|
|
||||||
;
|
|
||||||
; see http://6502.org/source/strings/patmatch.htm
|
|
||||||
|
|
||||||
strptr = P8ZP_SCRATCH_W1
|
strptr = P8ZP_SCRATCH_W1
|
||||||
|
|
||||||
@ -391,16 +391,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.
|
; experimental 8 bit hashing function.
|
||||||
; hash(-1)=179; clear carry; hash(i) = ROL hash(i-1) XOR string[i]
|
; 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
|
; On the English word list in /usr/share/dict/words it seems to have a pretty even distribution
|
||||||
%asm {{
|
%asm {{
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda #179
|
lda #179
|
||||||
sta P8ZP_SCRATCH_REG
|
sta P8ZP_SCRATCH_REG
|
||||||
ldy #0
|
ldy #0
|
||||||
clc
|
clc
|
||||||
- lda (cx16.r0),y
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
beq +
|
beq +
|
||||||
rol P8ZP_SCRATCH_REG
|
rol P8ZP_SCRATCH_REG
|
||||||
eor P8ZP_SCRATCH_REG
|
eor P8ZP_SCRATCH_REG
|
||||||
|
@ -18,44 +18,8 @@ diskio {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub list_filenames(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
|
; the VM version of diskio has no facility to iterate over filenames.
|
||||||
; -- fill the provided buffer with the names of the files on the disk (until buffer is full).
|
; (list_filenames, lf_start_list, lf_next_entry, lf_end_list)
|
||||||
; Files in the buffer are separated by a 0 byte. You can provide an optional pattern to match against.
|
|
||||||
; After the last filename one additional 0 byte is placed to indicate the end of the list.
|
|
||||||
; Returns number of files (it skips 'dir' entries i.e. subdirectories).
|
|
||||||
; Also sets carry on exit: Carry clear = all files returned, Carry set = directory has more files that didn't fit in the buffer.
|
|
||||||
; Note that no list of pointers of some form is returned, the names are just squashed together.
|
|
||||||
; If you really need a list of pointers to the names, that is pretty straightforward to construct by iterating over the names
|
|
||||||
; and registering when the next one starts after the 0-byte separator.
|
|
||||||
; TODO IMPLEMENT THIS
|
|
||||||
txt.print("@TODO: list_filenames\n")
|
|
||||||
sys.clear_carry()
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
; ----- iterative file lister functions (uses the read io channel) -----
|
|
||||||
|
|
||||||
sub lf_start_list(uword pattern_ptr) -> bool {
|
|
||||||
; -- start an iterative file listing with optional pattern matching.
|
|
||||||
; note: only a single iteration loop can be active at a time!
|
|
||||||
; TODO IMPLEMENT THIS
|
|
||||||
txt.print("@TODO: lf_start_list\n")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
sub lf_next_entry() -> bool {
|
|
||||||
; -- retrieve the next entry from an iterative file listing session.
|
|
||||||
; results will be found in list_blocks, list_filename, and list_filetype.
|
|
||||||
; if it returns false though, there are no more entries (or an error occurred).
|
|
||||||
; TODO IMPLEMENT THIS
|
|
||||||
txt.print("@TODO: lf_next_entry\n")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
sub lf_end_list() {
|
|
||||||
; TODO IMPLEMENT THIS
|
|
||||||
txt.print("@TODO: lf_end_list\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
; ----- iterative file loader functions (uses the input io channel) -----
|
; ----- iterative file loader functions (uses the input io channel) -----
|
||||||
@ -94,8 +58,9 @@ diskio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub f_read_all(uword bufferpointer) -> uword {
|
sub f_read_all(uword bufferpointer) -> uword {
|
||||||
; -- read the full contents of the file, returns number of bytes read.
|
; -- read the full rest of the file, returns number of bytes read.
|
||||||
; It is assumed the file size is less than 64 K.
|
; It is assumed the file size is less than 64 K.
|
||||||
|
; Usually you will just be using load() / load_raw() to read entire files!
|
||||||
uword actual
|
uword actual
|
||||||
repeat {
|
repeat {
|
||||||
%ir {{
|
%ir {{
|
||||||
|
@ -177,21 +177,17 @@ math {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub randrange(ubyte n) -> ubyte {
|
sub randrange(ubyte n) -> ubyte {
|
||||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
|
; -- return random number uniformly distributed from 0 to n-1
|
||||||
cx16.r0H = 255 / n * n
|
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
|
||||||
do {
|
cx16.r0 = math.rnd() * (n as uword)
|
||||||
cx16.r0L = math.rnd()
|
return cx16.r0H
|
||||||
} until cx16.r0L < cx16.r0H
|
|
||||||
return cx16.r0L % n
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub randrangew(uword n) -> uword {
|
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
|
||||||
cx16.r1 = 65535 / n * n
|
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
|
||||||
do {
|
cx16.r0 = math.rndw() * n
|
||||||
cx16.r0 = math.rndw()
|
return math.mul16_last_upper()
|
||||||
} until cx16.r0 < cx16.r1
|
|
||||||
return cx16.r0 % n
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub rndseed(uword seed1, uword seed2) {
|
sub rndseed(uword seed1, uword seed2) {
|
||||||
@ -204,6 +200,13 @@ math {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; there should be zero need to use these variants in the virtual target, but whatever:
|
||||||
|
alias rndseed_rom = math.rndseed
|
||||||
|
alias rnd_rom = math.rnd
|
||||||
|
alias rndw_rom = math.rndw
|
||||||
|
alias randrange_rom = math.randrange
|
||||||
|
alias randrangew_rom = math.randrangew
|
||||||
|
|
||||||
sub log2(ubyte value) -> ubyte {
|
sub log2(ubyte value) -> ubyte {
|
||||||
ubyte result = 7
|
ubyte result = 7
|
||||||
ubyte compare = $80
|
ubyte compare = $80
|
||||||
@ -401,25 +404,36 @@ math {
|
|||||||
; Linear interpolation (LERP)
|
; Linear interpolation (LERP)
|
||||||
; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 255]
|
; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 255]
|
||||||
; guarantees v = v1 when t = 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 {
|
sub lerpw(uword v0, uword v1, uword t) -> uword {
|
||||||
; Linear interpolation (LERP) on word values
|
; Linear interpolation (LERP) on word values
|
||||||
; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 65535]
|
; 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
|
t *= v1-v0
|
||||||
cx16.r0 = math.mul16_last_upper()
|
cx16.r15 = math.mul16_last_upper()
|
||||||
if t!=0
|
if t!=0
|
||||||
cx16.r0++
|
cx16.r15++
|
||||||
return v0 + cx16.r0
|
return v0 + cx16.r15
|
||||||
}
|
}
|
||||||
|
|
||||||
sub interpolate(ubyte v, ubyte inputMin, ubyte inputMax, ubyte outputMin, ubyte outputMax) -> ubyte {
|
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]
|
; 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.
|
; Clobbers R15.
|
||||||
cx16.r0 = ((v-inputMin)*256+inputMax) / (inputMax-inputMin)
|
; (There is no version for words because of lack of precision in the fixed point calculation there)
|
||||||
cx16.r0 *= (outputMax-outputMin)
|
cx16.r15 = ((v-inputMin)*256+inputMax) / (inputMax-inputMin)
|
||||||
return cx16.r0H + outputMin
|
cx16.r15 *= (outputMax-outputMin)
|
||||||
|
return cx16.r15H + outputMin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,6 +123,19 @@ strings {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub ncompare(str st1, str st2, ubyte length) -> byte {
|
||||||
|
; Compares two strings for sorting.
|
||||||
|
; Returns -1 (255), 0 or 1, meaning: string1 sorts before, equal or after string2.
|
||||||
|
; Only compares the strings from index 0 up to the length argument.
|
||||||
|
%ir {{
|
||||||
|
loadm.w r99000,strings.ncompare.st1
|
||||||
|
loadm.w r99001,strings.ncompare.st2
|
||||||
|
loadm.b r99100,strings.ncompare.length
|
||||||
|
syscall 58 (r99000.w, r99001.w, r99100.b) : r99100.b
|
||||||
|
returnr.b r99100
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
sub lower(str st) -> ubyte {
|
sub lower(str st) -> ubyte {
|
||||||
; Lowercases the petscii string in-place. Returns length of the string.
|
; Lowercases the petscii string in-place. Returns length of the string.
|
||||||
; (for efficiency, non-letter characters > 128 will also not be left intact,
|
; (for efficiency, non-letter characters > 128 will also not be left intact,
|
||||||
@ -166,7 +179,7 @@ strings {
|
|||||||
sub hash(str st) -> ubyte {
|
sub hash(str st) -> ubyte {
|
||||||
; experimental 8 bit hashing function.
|
; experimental 8 bit hashing function.
|
||||||
; hash(-1)=179; hash(i) = ROL hash(i-1) XOR string[i]
|
; 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 hashcode = 179
|
||||||
ubyte ix
|
ubyte ix
|
||||||
sys.clear_carry()
|
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 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_BOOL = sizeof(bool)
|
||||||
const ubyte SIZEOF_BYTE = 1
|
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||||
const ubyte SIZEOF_UBYTE = 1
|
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||||
const ubyte SIZEOF_WORD = 2
|
const ubyte SIZEOF_WORD = sizeof(word)
|
||||||
const ubyte SIZEOF_UWORD = 2
|
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||||
const ubyte SIZEOF_FLOAT = 8
|
const ubyte SIZEOF_LONG = sizeof(long)
|
||||||
|
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||||
const byte MIN_BYTE = -128
|
const byte MIN_BYTE = -128
|
||||||
const byte MAX_BYTE = 127
|
const byte MAX_BYTE = 127
|
||||||
const ubyte MIN_UBYTE = 0
|
const ubyte MIN_UBYTE = 0
|
||||||
@ -231,9 +232,8 @@ sys {
|
|||||||
|
|
||||||
if_cs
|
if_cs
|
||||||
cx16.r0L |= 1
|
cx16.r0L |= 1
|
||||||
; TODO: overflow flag not yet supported
|
if_vs
|
||||||
; if_vs
|
cx16.r0L |= %01000000
|
||||||
; cx16.r0L |= %01000000
|
|
||||||
|
|
||||||
return cx16.r0L
|
return cx16.r0L
|
||||||
}
|
}
|
||||||
|
@ -28,12 +28,6 @@ fun main(args: Array<String>) {
|
|||||||
// it means that you have to run the gradle task once to generate this file.
|
// it means that you have to run the gradle task once to generate this file.
|
||||||
// Do that with this command: gradle createVersionFile
|
// Do that with this command: gradle createVersionFile
|
||||||
|
|
||||||
println("\nProg8 compiler v${prog8.buildversion.VERSION} by Irmen de Jong (irmen@razorvine.net)")
|
|
||||||
if('-' in prog8.buildversion.VERSION) {
|
|
||||||
println("Prerelease version from git commit ${prog8.buildversion.GIT_SHA.take(8)} in branch ${prog8.buildversion.GIT_BRANCH}")
|
|
||||||
}
|
|
||||||
println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
|
|
||||||
|
|
||||||
val succes = compileMain(args)
|
val succes = compileMain(args)
|
||||||
if(!succes)
|
if(!succes)
|
||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
@ -63,27 +57,39 @@ 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 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 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 printAst2 by cli.option(ArgType.Boolean, fullName = "printast2", description = "print out the simplified AST that is used for code generation")
|
||||||
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
|
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 slabsGolden by cli.option(ArgType.Boolean, fullName = "slabsgolden", description = "put memory() slabs in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
|
||||||
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
|
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
|
||||||
val dontIncludeSourcelines by cli.option(ArgType.Boolean, fullName = "nosourcelines", description = "do not include original Prog8 source lines in generated asm code")
|
val dontIncludeSourcelines by cli.option(ArgType.Boolean, fullName = "nosourcelines", description = "do not include original Prog8 source lines in generated asm code")
|
||||||
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 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 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 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 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 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 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 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 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 {
|
try {
|
||||||
cli.parse(args)
|
cli.parse(args)
|
||||||
} catch (e: IllegalStateException) {
|
} catch (e: IllegalStateException) {
|
||||||
|
banner()
|
||||||
System.err.println(e.message)
|
System.err.println(e.message)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(version==true) {
|
||||||
|
banner()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if(quietAll!=true)
|
||||||
|
banner()
|
||||||
|
|
||||||
val outputPath = pathFrom(outputDir)
|
val outputPath = pathFrom(outputDir)
|
||||||
if(!outputPath.toFile().isDirectory) {
|
if(!outputPath.toFile().isDirectory) {
|
||||||
System.err.println("Output path doesn't exist")
|
System.err.println("Output path doesn't exist")
|
||||||
@ -124,6 +130,11 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(moduleFiles.isEmpty()) {
|
||||||
|
System.err.println("No module file(s) specified")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if(varsHighBank==0 && compilationTarget==Cx16Target.NAME) {
|
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.")
|
System.err.println("On the Commander X16, HiRAM bank 0 is used by the kernal and can't be used.")
|
||||||
return false
|
return false
|
||||||
@ -148,7 +159,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(startVm==true) {
|
if(startVm==true) {
|
||||||
runVm(moduleFiles.first())
|
runVm(moduleFiles.first(), quietAll==true)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +180,9 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
if(checkSource==true) false else dontOptimize != true,
|
if(checkSource==true) false else dontOptimize != true,
|
||||||
if(checkSource==true) false else dontWriteAssembly != true,
|
if(checkSource==true) false else dontWriteAssembly != true,
|
||||||
warnSymbolShadowing == true,
|
warnSymbolShadowing == true,
|
||||||
quietAssembler == true,
|
quietAll == true,
|
||||||
|
quietAll == true || quietAssembler == true,
|
||||||
|
showTimings == true,
|
||||||
asmListfile == true,
|
asmListfile == true,
|
||||||
dontIncludeSourcelines != true,
|
dontIncludeSourcelines != true,
|
||||||
experimentalCodegen == true,
|
experimentalCodegen == true,
|
||||||
@ -252,7 +265,9 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
if(checkSource==true) false else dontOptimize != true,
|
if(checkSource==true) false else dontOptimize != true,
|
||||||
if(checkSource==true) false else dontWriteAssembly != true,
|
if(checkSource==true) false else dontWriteAssembly != true,
|
||||||
warnSymbolShadowing == true,
|
warnSymbolShadowing == true,
|
||||||
quietAssembler == true,
|
quietAll == true,
|
||||||
|
quietAll == true || quietAssembler == true,
|
||||||
|
showTimings == true,
|
||||||
asmListfile == true,
|
asmListfile == true,
|
||||||
dontIncludeSourcelines != true,
|
dontIncludeSourcelines != true,
|
||||||
experimentalCodegen == true,
|
experimentalCodegen == true,
|
||||||
@ -296,15 +311,23 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
val programNameInPath = outputPath.resolve(compilationResult.compilerAst.name)
|
val programNameInPath = outputPath.resolve(compilationResult.compilerAst.name)
|
||||||
|
|
||||||
if (startEmulator1 == true)
|
if (startEmulator1 == true)
|
||||||
compilationResult.compilationOptions.compTarget.launchEmulator(1, programNameInPath)
|
compilationResult.compilationOptions.compTarget.launchEmulator(1, programNameInPath, quietAll==true)
|
||||||
else if (startEmulator2 == true)
|
else if (startEmulator2 == true)
|
||||||
compilationResult.compilationOptions.compTarget.launchEmulator(2, programNameInPath)
|
compilationResult.compilationOptions.compTarget.launchEmulator(2, programNameInPath, quietAll==true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun banner() {
|
||||||
|
println("\nProg8 compiler v${prog8.buildversion.VERSION} by Irmen de Jong (irmen@razorvine.net)")
|
||||||
|
if('-' in prog8.buildversion.VERSION) {
|
||||||
|
println("Prerelease version from git commit ${prog8.buildversion.GIT_SHA.take(8)} in branch ${prog8.buildversion.GIT_BRANCH}")
|
||||||
|
}
|
||||||
|
println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
|
||||||
|
}
|
||||||
|
|
||||||
fun convertFloatToBytes(number: String, target: String) {
|
fun convertFloatToBytes(number: String, target: String) {
|
||||||
val tgt = getCompilationTargetByName(target)
|
val tgt = getCompilationTargetByName(target)
|
||||||
val dbl = number.toDouble()
|
val dbl = number.toDouble()
|
||||||
@ -335,8 +358,8 @@ private fun processSymbolDefs(symbolDefs: List<String>): Map<String, String>? {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun runVm(irFilename: String) {
|
fun runVm(irFilename: String, quiet: Boolean) {
|
||||||
val irFile = Path(irFilename)
|
val irFile = Path(irFilename)
|
||||||
val vmdef = VMTarget()
|
val vmdef = VMTarget()
|
||||||
vmdef.launchEmulator(0, irFile)
|
vmdef.launchEmulator(0, irFile, quiet)
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package prog8.compiler
|
package prog8.compiler
|
||||||
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.AstException
|
import prog8.ast.AstException
|
||||||
import prog8.ast.FatalAstException
|
import prog8.ast.FatalAstException
|
||||||
|
import prog8.ast.Program
|
||||||
import prog8.ast.SyntaxError
|
import prog8.ast.SyntaxError
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.VarDecl
|
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 -> NumericLiteral(BaseDataType.UBYTE, program.memsizer.memorySize(dt.getOrUndef(), null).toDouble(), position)
|
||||||
}
|
}
|
||||||
} else {
|
} 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)
|
throw SyntaxError("sizeof invalid argument type", position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
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.
|
// 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)
|
if(args.size!=1)
|
||||||
throw SyntaxError("len requires one argument", position)
|
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()
|
var arraySize = directMemVar?.arraysize?.constIndex()
|
||||||
if(arraySize != null)
|
if(arraySize != null)
|
||||||
return NumericLiteral.optimalInteger(arraySize, position)
|
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)
|
return NumericLiteral.optimalInteger((args[0] as StringLiteral).value.length, position)
|
||||||
if(args[0] !is IdentifierReference)
|
if(args[0] !is IdentifierReference)
|
||||||
throw SyntaxError("len argument should be an identifier", position)
|
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")
|
?: throw CannotEvaluateException("len", "no target vardecl")
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user