mirror of
https://github.com/irmen/prog8.git
synced 2025-07-06 06:23:59 +00:00
Compare commits
51 Commits
v11.3.2
...
languageSe
Author | SHA1 | Date | |
---|---|---|---|
1f3c19169b | |||
1efdfe8ea1 | |||
67d4180825 | |||
be31e190d2 | |||
b5d1575823 | |||
e3e395836d | |||
8dc2e47507 | |||
daf7c3357c | |||
e8795859c5 | |||
bebe60b687 | |||
ddceec364e | |||
d067fa4b73 | |||
b5e51ab937 | |||
552e55c29f | |||
a228908c1a | |||
15fc3b6c04 | |||
0456badd02 | |||
399cf5118d | |||
a87f2640d3 | |||
a90ef274d7 | |||
341778ba67 | |||
ec50b5a007 | |||
31d84c8921 | |||
34bedbeef1 | |||
3b1b0985c1 | |||
368387e1a7 | |||
9da430ffeb | |||
cc063124cf | |||
3b37b89951 | |||
844b537d1e | |||
caf1d4a22a | |||
d8e244df99 | |||
548e421e27 | |||
322fa7ea69 | |||
cf7bea0985 | |||
25d7f8808f | |||
acc630972a | |||
6a33be3fd8 | |||
f5fc4e345c | |||
67231af623 | |||
e31ef6f06f | |||
09d188106a | |||
d8e2116481 | |||
435dfbb932 | |||
ba93966474 | |||
ea8d17cdb2 | |||
082265fb25 | |||
9e557ce8ac | |||
e5d9af75de | |||
31c1bf8bc5 | |||
37d4055036 |
4
.idea/libraries/eclipse_lsp4j.xml
generated
4
.idea/libraries/eclipse_lsp4j.xml
generated
@ -4,8 +4,8 @@
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j/0.24.0/org.eclipse.lsp4j-0.24.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j.jsonrpc/0.24.0/org.eclipse.lsp4j.jsonrpc-0.24.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.12.1/gson-2.12.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.36.0/error_prone_annotations-2.36.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.13.1/gson-2.13.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.38.0/error_prone_annotations-2.38.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@ -12,6 +12,7 @@
|
||||
<option name="pkg" value="" />
|
||||
<option name="language" value="Java" />
|
||||
<option name="generateListener" value="false" />
|
||||
<option name="generateVisitor" value="true" />
|
||||
</PerGrammarGenerationSettings>
|
||||
</list>
|
||||
</option>
|
||||
|
1
.idea/modules.xml
generated
1
.idea/modules.xml
generated
@ -15,6 +15,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/languageServer/languageServer.iml" filepath="$PROJECT_DIR$/languageServer/languageServer.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" />
|
||||
|
@ -71,6 +71,7 @@ What does Prog8 provide?
|
||||
- high-level program optimizations
|
||||
- conditional branches that map 1:1 to cpu status flags
|
||||
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
|
||||
- ``on .. goto`` statement for fast jump tables
|
||||
- ``in`` expression for concise and efficient multi-value/containment check
|
||||
- ``defer`` statement to help write concise and robust subroutine cleanup logic
|
||||
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
|
||||
|
@ -27,7 +27,7 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||
var golden: GoldenRam
|
||||
val libraryPath: Path?
|
||||
val customLauncher: List<String>
|
||||
val additionalAssemblerOptions: String?
|
||||
val additionalAssemblerOptions: List<String>
|
||||
val defaultOutputType: OutputType
|
||||
|
||||
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
|
||||
|
@ -6,12 +6,15 @@ import prog8.code.target.zp.C128Zeropage
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
class C128Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
|
@ -7,12 +7,15 @@ import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
class C64Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
|
@ -27,7 +27,7 @@ class ConfigFileTarget(
|
||||
override val defaultOutputType: OutputType,
|
||||
override val libraryPath: Path,
|
||||
override val customLauncher: List<String>,
|
||||
override val additionalAssemblerOptions: String?,
|
||||
override val additionalAssemblerOptions: List<String>,
|
||||
val ioAddresses: List<UIntRange>,
|
||||
val zpScratchB1: UInt,
|
||||
val zpScratchReg: UInt,
|
||||
@ -37,7 +37,7 @@ class ConfigFileTarget(
|
||||
val zpFullsafe: List<UIntRange>,
|
||||
val zpKernalsafe: List<UIntRange>,
|
||||
val zpBasicsafe: List<UIntRange>
|
||||
): ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(8) {
|
||||
): ICompilationTarget, IStringEncoding by Encoder(true), IMemSizer by NormalMemSizer(8) {
|
||||
|
||||
companion object {
|
||||
|
||||
@ -109,8 +109,6 @@ class ConfigFileTarget(
|
||||
(customLauncherStr+"\n").lines().map { it.trimEnd() }
|
||||
else emptyList()
|
||||
val assemblerOptionsStr = props.getProperty("assembler_options", "").trim()
|
||||
val assemblerOptions = assemblerOptionsStr.ifBlank { null }
|
||||
|
||||
val outputTypeString = props.getProperty("output_type", "PRG")
|
||||
val defaultOutputType = OutputType.valueOf(outputTypeString.uppercase())
|
||||
|
||||
@ -128,7 +126,7 @@ class ConfigFileTarget(
|
||||
defaultOutputType,
|
||||
libraryPath,
|
||||
customLauncher,
|
||||
assemblerOptions,
|
||||
if(assemblerOptionsStr=="") emptyList() else assemblerOptionsStr.split(" "),
|
||||
ioAddresses,
|
||||
props.getInteger("zp_scratch_b1"),
|
||||
props.getInteger("zp_scratch_reg"),
|
||||
|
@ -6,12 +6,15 @@ import prog8.code.target.zp.CX16Zeropage
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
class Cx16Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
|
@ -25,7 +25,7 @@ internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> floatsize * (numElements ?: 1)
|
||||
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
||||
dt.isLong -> 4 * (numElements ?: 1)
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
|
@ -6,12 +6,15 @@ import prog8.code.target.zp.PETZeropage
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
class PETTarget: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
|
@ -7,12 +7,15 @@ import kotlin.io.path.isReadable
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
|
||||
class VMTarget: ICompilationTarget,
|
||||
IStringEncoding by Encoder(false),
|
||||
IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val defaultEncoding = Encoding.ISO
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
@ -88,31 +91,6 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
zeropage = VirtualZeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||
}
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
if(numElements==null) return 2 // treat it as a pointer size
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
||||
BaseDataType.FLOAT-> numElements * FLOAT_MEM_SIZE
|
||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
else if (dt.isString) {
|
||||
return numElements // treat it as the size of the given string with the length
|
||||
?: 2 // treat it as the size to store a string pointer
|
||||
}
|
||||
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> FLOAT_MEM_SIZE * (numElements ?: 1)
|
||||
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -197,6 +197,7 @@ object AtasciiEncoding {
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\r' -> 0x9bu
|
||||
'\u0000' -> 0u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
|
@ -285,6 +285,7 @@ object C64osEncoding {
|
||||
val screencode = encodingC64os[chr]
|
||||
return screencode?.toUByte() ?: when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> 13u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
|
@ -5,19 +5,19 @@ import prog8.code.core.Encoding
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.core.InternalCompilerException
|
||||
|
||||
object Encoder: IStringEncoding {
|
||||
class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding {
|
||||
override val defaultEncoding: Encoding = Encoding.ISO
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||
val coded = when(encoding) {
|
||||
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
|
||||
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
|
||||
Encoding.ISO -> IsoEncoding.encode(str)
|
||||
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.encode(str)
|
||||
Encoding.ISO -> IsoEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.CP437 -> Cp437Encoding.encode(str)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.encode(str)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
||||
Encoding.C64OS -> C64osEncoding.encode(str)
|
||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||
}
|
||||
@ -30,12 +30,12 @@ object Encoder: IStringEncoding {
|
||||
val decoded = when(encoding) {
|
||||
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
|
||||
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
|
||||
Encoding.ISO -> IsoEncoding.decode(bytes)
|
||||
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes)
|
||||
Encoding.ISO -> IsoEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.CP437 -> Cp437Encoding.decode(bytes)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
||||
Encoding.C64OS -> C64osEncoding.decode(bytes)
|
||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||
}
|
||||
|
@ -6,14 +6,16 @@ import com.github.michaelbull.result.Result
|
||||
import java.io.CharConversionException
|
||||
import java.nio.charset.Charset
|
||||
|
||||
|
||||
open class IsoEncodingBase(charsetName: String) {
|
||||
val charset: Charset = Charset.forName(charsetName)
|
||||
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
|
||||
return try {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
@ -27,9 +29,14 @@ open class IsoEncodingBase(charsetName: String) {
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||
fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||
Ok(String(bytes.map {
|
||||
when(it) {
|
||||
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
|
||||
else -> it.toByte()
|
||||
}
|
||||
}.toByteArray(), charset))
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
|
@ -64,10 +64,11 @@ object JapaneseCharacterConverter {
|
||||
object KatakanaEncoding {
|
||||
val charset: Charset = Charset.forName("JIS_X0201")
|
||||
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
|
||||
return try {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||
|
||||
'\u0000' -> 0u
|
||||
'\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
|
||||
@ -112,9 +113,14 @@ object KatakanaEncoding {
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||
fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||
Ok(String(bytes.map {
|
||||
when(it) {
|
||||
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
|
||||
else -> it.toByte()
|
||||
}
|
||||
}.toByteArray(), charset))
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ object PetsciiEncoding {
|
||||
'\ufffe', // 0x07 -> UNDEFINED
|
||||
'\uf118', // 0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
|
||||
'\uf119', // 0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
|
||||
'\ufffe', // 0x0A -> UNDEFINED
|
||||
'\n', // 0x0A -> LINE FEED (RETURN)
|
||||
'\ufffe', // 0x0B -> UNDEFINED
|
||||
'\ufffe', // 0x0C -> UNDEFINED
|
||||
'\n' , // 0x0D -> LINE FEED (RETURN)
|
||||
@ -1117,6 +1117,8 @@ object PetsciiEncoding {
|
||||
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
||||
return screencode?.toUByte() ?: when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> 141u
|
||||
'\r' -> 141u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
|
@ -16,12 +16,6 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("C128 target doesn't yet support floating point routines")
|
||||
// note: in git commit labeled 'c128: remove floats module' the floats.p8 and floats.asm files are removed,
|
||||
// they could be retrieved again at a later time if the compiler somehow *does* store the fp variables in bank1.
|
||||
}
|
||||
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
|
@ -19,7 +19,7 @@ class ConfigurableZeropage(
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
TODO("floats")
|
||||
TODO("floats in configurable target zp")
|
||||
}
|
||||
|
||||
if(SCRATCH_REG!=SCRATCH_B1+1u)
|
||||
@ -30,7 +30,7 @@ class ConfigurableZeropage(
|
||||
ZeropageType.FULL -> fullsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.BASICSAFE -> basicsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.KERNALSAFE -> kernalsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.FLOATSAFE -> TODO("floatsafe")
|
||||
ZeropageType.FLOATSAFE -> TODO("floatsafe in configurable target zp")
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
|
@ -16,10 +16,6 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("PET target doesn't yet support floating point routines")
|
||||
}
|
||||
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
|
@ -432,23 +432,6 @@ class AsmGen6502Internal (
|
||||
}
|
||||
|
||||
|
||||
internal val tempVarsCounters = mutableMapOf(
|
||||
BaseDataType.BOOL to 0,
|
||||
BaseDataType.BYTE to 0,
|
||||
BaseDataType.UBYTE to 0,
|
||||
BaseDataType.WORD to 0,
|
||||
BaseDataType.UWORD to 0,
|
||||
BaseDataType.LONG to 0,
|
||||
BaseDataType.FLOAT to 0
|
||||
)
|
||||
|
||||
internal fun buildTempVarName(dt: BaseDataType, counter: Int): String = "prog8_tmpvar_${dt.toString().lowercase()}_$counter"
|
||||
|
||||
internal fun getTempVarName(dt: BaseDataType): String {
|
||||
tempVarsCounters[dt] = tempVarsCounters.getValue(dt)+1
|
||||
return buildTempVarName(dt, tempVarsCounters.getValue(dt))
|
||||
}
|
||||
|
||||
internal fun loadByteFromPointerIntoA(pointervar: PtIdentifier): String {
|
||||
// returns the source name of the zero page pointervar if it's already in the ZP,
|
||||
// otherwise returns "P8ZP_SCRATCH_W1" which is the intermediary
|
||||
@ -674,7 +657,8 @@ class AsmGen6502Internal (
|
||||
}
|
||||
}
|
||||
expr.type.isFloat -> {
|
||||
require(options.compTarget.FLOAT_MEM_SIZE == 5) {"invalid float size ${expr.position}"}
|
||||
if(options.compTarget.FLOAT_MEM_SIZE != 5)
|
||||
TODO("support float size other than 5 ${expr.position}")
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.A)
|
||||
out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
@ -854,7 +838,7 @@ class AsmGen6502Internal (
|
||||
private fun repeatWordCount(iterations: Int, stmt: PtRepeatLoop) {
|
||||
require(iterations in 257..65536) { "invalid repeat count ${stmt.position}" }
|
||||
val repeatLabel = makeLabel("repeat")
|
||||
val counterVar = createRepeatCounterVar(BaseDataType.UWORD, isTargetCpu(CpuType.CPU65C02), stmt)
|
||||
val counterVar = createTempVarReused(BaseDataType.UWORD, isTargetCpu(CpuType.CPU65C02), stmt)
|
||||
val loopcount = if(iterations==65536) 0 else if(iterations and 0x00ff == 0) iterations else iterations + 0x0100 // so that the loop can simply use a double-dec
|
||||
out("""
|
||||
ldy #>$loopcount
|
||||
@ -874,7 +858,7 @@ $repeatLabel""")
|
||||
// note: A/Y must have been loaded with the number of iterations!
|
||||
// the iny + double dec is microoptimization of the 16 bit loop
|
||||
val repeatLabel = makeLabel("repeat")
|
||||
val counterVar = createRepeatCounterVar(BaseDataType.UWORD, false, stmt)
|
||||
val counterVar = createTempVarReused(BaseDataType.UWORD, false, stmt)
|
||||
out("""
|
||||
cmp #0
|
||||
beq +
|
||||
@ -897,13 +881,13 @@ $repeatLabel""")
|
||||
require(count in 2..256) { "invalid repeat count ${stmt.position}" }
|
||||
val repeatLabel = makeLabel("repeat")
|
||||
if(isTargetCpu(CpuType.CPU65C02)) {
|
||||
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, true, stmt)
|
||||
val counterVar = createTempVarReused(BaseDataType.UBYTE, true, stmt)
|
||||
out(" lda #${count and 255} | sta $counterVar")
|
||||
out(repeatLabel)
|
||||
translate(stmt.statements)
|
||||
out(" dec $counterVar | bne $repeatLabel")
|
||||
} else {
|
||||
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, false, stmt)
|
||||
val counterVar = createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
out(" lda #${count and 255} | sta $counterVar")
|
||||
out(repeatLabel)
|
||||
translate(stmt.statements)
|
||||
@ -915,13 +899,13 @@ $repeatLabel""")
|
||||
val repeatLabel = makeLabel("repeat")
|
||||
out(" cpy #0")
|
||||
if(isTargetCpu(CpuType.CPU65C02)) {
|
||||
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, true, stmt)
|
||||
val counterVar = createTempVarReused(BaseDataType.UBYTE, true, stmt)
|
||||
out(" beq $endLabel | sty $counterVar")
|
||||
out(repeatLabel)
|
||||
translate(stmt.statements)
|
||||
out(" dec $counterVar | bne $repeatLabel")
|
||||
} else {
|
||||
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, false, stmt)
|
||||
val counterVar = createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
out(" beq $endLabel | sty $counterVar")
|
||||
out(repeatLabel)
|
||||
translate(stmt.statements)
|
||||
@ -930,43 +914,9 @@ $repeatLabel""")
|
||||
out(endLabel)
|
||||
}
|
||||
|
||||
private fun createRepeatCounterVar(dt: BaseDataType, preferZeropage: Boolean, stmt: PtRepeatLoop): String {
|
||||
val scope = stmt.definingISub()!!
|
||||
val asmInfo = subroutineExtra(scope)
|
||||
var parent = stmt.parent
|
||||
while(parent !is PtProgram) {
|
||||
if(parent is PtRepeatLoop)
|
||||
break
|
||||
parent = parent.parent
|
||||
}
|
||||
val isNested = parent is PtRepeatLoop
|
||||
|
||||
if(!isNested) {
|
||||
// we can re-use a counter var from the subroutine if it already has one for that datatype
|
||||
val existingVar = asmInfo.extraVars.firstOrNull { it.first==dt && it.second.endsWith("counter") }
|
||||
if(existingVar!=null) {
|
||||
if(!preferZeropage || existingVar.third!=null)
|
||||
return existingVar.second
|
||||
}
|
||||
}
|
||||
|
||||
val counterVar = makeLabel("counter")
|
||||
when(dt) {
|
||||
BaseDataType.UBYTE, BaseDataType.UWORD -> {
|
||||
val result = zeropage.allocate(counterVar, DataType.forDt(dt), null, stmt.position, errors)
|
||||
result.fold(
|
||||
success = { (address, _, _) -> asmInfo.extraVars.add(Triple(dt, counterVar, address)) },
|
||||
failure = { asmInfo.extraVars.add(Triple(dt, counterVar, null)) } // allocate normally
|
||||
)
|
||||
return counterVar
|
||||
}
|
||||
else -> throw AssemblyError("invalidt dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(stmt: PtWhen) {
|
||||
val endLabel = makeLabel("when_end")
|
||||
val choiceBlocks = mutableListOf<Pair<String, PtNodeGroup>>()
|
||||
val choiceBlocks = mutableListOf<Pair<String, PtWhenChoice>>()
|
||||
val conditionDt = stmt.value.type
|
||||
if(conditionDt.isByte)
|
||||
assignExpressionToRegister(stmt.value, RegisterOrPair.A)
|
||||
@ -977,13 +927,20 @@ $repeatLabel""")
|
||||
val choice = choiceNode as PtWhenChoice
|
||||
var choiceLabel = makeLabel("choice")
|
||||
if(choice.isElse) {
|
||||
require(choice.parent.children.last() === choice)
|
||||
translate(choice.statements)
|
||||
// is always the last node so can fall through
|
||||
} else {
|
||||
if(choice.statements.children.isEmpty()) {
|
||||
// no statements for this choice value, jump to the end immediately
|
||||
choiceLabel = endLabel
|
||||
} else {
|
||||
choiceBlocks.add(choiceLabel to choice.statements)
|
||||
val onlyJumpLabel = ((choice.statements.children.singleOrNull() as? PtJump)?.target as? PtIdentifier)?.name
|
||||
if(onlyJumpLabel==null) {
|
||||
choiceBlocks.add(choiceLabel to choice)
|
||||
} else {
|
||||
choiceLabel = onlyJumpLabel
|
||||
}
|
||||
}
|
||||
for (cv in choice.values.children) {
|
||||
val value = (cv as PtNumber).number.toInt()
|
||||
@ -1000,11 +957,14 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
}
|
||||
jmp(endLabel)
|
||||
|
||||
if(choiceBlocks.isNotEmpty())
|
||||
jmp(endLabel)
|
||||
|
||||
for(choiceBlock in choiceBlocks.withIndex()) {
|
||||
out(choiceBlock.value.first)
|
||||
translate(choiceBlock.value.second)
|
||||
if (choiceBlock.index < choiceBlocks.size - 1)
|
||||
translate(choiceBlock.value.second.statements)
|
||||
if (choiceBlock.index < choiceBlocks.size - 1 && !choiceBlock.value.second.isOnlyGotoOrReturn())
|
||||
jmp(endLabel)
|
||||
}
|
||||
out(endLabel)
|
||||
@ -1085,18 +1045,24 @@ $repeatLabel""")
|
||||
else {
|
||||
if(evaluateAddressExpression) {
|
||||
val arrayIdx = jump.target as? PtArrayIndexer
|
||||
if (isTargetCpu(CpuType.CPU65C02) && arrayIdx!=null) {
|
||||
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)
|
||||
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
|
||||
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)
|
||||
// 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
|
||||
@ -1557,6 +1523,51 @@ $repeatLabel""")
|
||||
return "$GENERATED_LABEL_PREFIX${generatedLabelSequenceNumber}_$postfix"
|
||||
}
|
||||
|
||||
internal fun createTempVarReused(dt: BaseDataType, preferZeropage: Boolean, stmt: PtNode): String {
|
||||
val scope = stmt.definingISub()!!
|
||||
val asmInfo = subroutineExtra(scope)
|
||||
var parent = stmt.parent
|
||||
while(parent !is PtProgram) {
|
||||
if(parent is PtRepeatLoop || parent is PtForLoop)
|
||||
break
|
||||
parent = parent.parent
|
||||
}
|
||||
val isNested = parent is PtRepeatLoop || parent is PtForLoop
|
||||
|
||||
if(!isNested) {
|
||||
// we can re-use a counter var from the subroutine if it already has one for that datatype
|
||||
val existingVar = asmInfo.extraVars.firstOrNull { it.first==dt && it.second.endsWith("tempv") }
|
||||
if(existingVar!=null) {
|
||||
if(!preferZeropage || existingVar.third!=null) {
|
||||
// println("reuse temp counter var: $dt ${existingVar.second} @${stmt.position}")
|
||||
return existingVar.second
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val counterVar = makeLabel("tempv")
|
||||
// println("new temp counter var: $dt $counterVar @${stmt.position}")
|
||||
when {
|
||||
dt.isIntegerOrBool -> {
|
||||
if(preferZeropage) {
|
||||
val result = zeropage.allocate(counterVar, DataType.forDt(dt), null, stmt.position, errors)
|
||||
result.fold(
|
||||
success = { (address, _, _) -> asmInfo.extraVars.add(Triple(dt, counterVar, address)) },
|
||||
failure = { asmInfo.extraVars.add(Triple(dt, counterVar, null)) } // allocate normally
|
||||
)
|
||||
} else {
|
||||
asmInfo.extraVars.add(Triple(dt, counterVar, null)) // allocate normally
|
||||
}
|
||||
return counterVar
|
||||
}
|
||||
dt == BaseDataType.FLOAT -> {
|
||||
asmInfo.extraVars.add(Triple(dt, counterVar, null)) // allocate normally, floats never on zeropage
|
||||
return counterVar
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignConstFloatToPointerAY(number: PtNumber) {
|
||||
val floatConst = allocator.getFloatAsmConst(number.number)
|
||||
out("""
|
||||
|
@ -512,6 +512,7 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
|
||||
// rts + jmp -> remove jmp
|
||||
// rts + bxx -> remove bxx
|
||||
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
|
||||
// bra/jmp + bra/jmp -> remove second bra/jmp (bra bra / jmp jmp are not removed because this is likely a jump table!)
|
||||
// and some other optimizations.
|
||||
|
||||
val mods = mutableListOf<Modification>()
|
||||
@ -567,6 +568,15 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only remove bra followed by jmp or jmp followed by bra
|
||||
// bra bra or jmp jmp is likely part of a jump table, which should keep all entries!
|
||||
if((" bra" in first || "\tbra" in first) && (" jmp" in second || "\tjmp" in second)) {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
if((" jmp" in first || "\tjmp" in first) && (" bra" in second || "\tbra" in second)) {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -28,6 +28,14 @@ internal class AssemblyProgram(
|
||||
|
||||
val assemblerCommand: List<String>
|
||||
|
||||
fun addRemainingOptions(command: MutableList<String>, program: Path, assembly: Path): List<String> {
|
||||
if(options.compTarget.additionalAssemblerOptions.isNotEmpty())
|
||||
command.addAll(options.compTarget.additionalAssemblerOptions)
|
||||
|
||||
command.addAll(listOf("--output", program.toString(), assembly.toString()))
|
||||
return command
|
||||
}
|
||||
|
||||
when(options.output) {
|
||||
OutputType.PRG -> {
|
||||
// CBM machines .prg generation.
|
||||
@ -47,8 +55,7 @@ internal class AssemblyProgram(
|
||||
command.add("--list=$listFile")
|
||||
}
|
||||
|
||||
command.addAll(listOf("--output", prgFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
assemblerCommand = addRemainingOptions(command, prgFile, assemblyFile)
|
||||
if(!options.quiet)
|
||||
println("\nCreating prg for target ${compTarget.name}.")
|
||||
}
|
||||
@ -69,8 +76,7 @@ internal class AssemblyProgram(
|
||||
if(options.asmListfile)
|
||||
command.add("--list=$listFile")
|
||||
|
||||
command.addAll(listOf("--output", xexFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
assemblerCommand = addRemainingOptions(command,xexFile, assemblyFile)
|
||||
if(!options.quiet)
|
||||
println("\nCreating xex for target ${compTarget.name}.")
|
||||
}
|
||||
@ -90,8 +96,7 @@ internal class AssemblyProgram(
|
||||
if(options.asmListfile)
|
||||
command.add("--list=$listFile")
|
||||
|
||||
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
assemblerCommand = addRemainingOptions(command, binFile, assemblyFile)
|
||||
if(!options.quiet)
|
||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
||||
}
|
||||
@ -122,14 +127,10 @@ internal class AssemblyProgram(
|
||||
command.add("--nostart") // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD
|
||||
}
|
||||
|
||||
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
assemblerCommand = addRemainingOptions(command, binFile, assemblyFile)
|
||||
}
|
||||
}
|
||||
|
||||
if(options.compTarget.additionalAssemblerOptions!=null)
|
||||
assemblerCommand.add(options.compTarget.additionalAssemblerOptions!!)
|
||||
|
||||
val proc = ProcessBuilder(assemblerCommand)
|
||||
if(!options.quiet)
|
||||
proc.inheritIO()
|
||||
|
@ -739,7 +739,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.assignConstFloatToPointerAY(number)
|
||||
}
|
||||
else -> {
|
||||
val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT)
|
||||
val tempvar = asmgen.createTempVarReused(BaseDataType.FLOAT, false, fcall)
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.FLOAT)
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
|
@ -89,7 +89,7 @@ internal class ForLoopsAsmGen(
|
||||
if(asmgen.options.romable) {
|
||||
// cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
|
||||
// so we need to store the loop end value in a newly allocated temporary variable
|
||||
val toValueVar = asmgen.getTempVarName(iterableDt.elementType().base)
|
||||
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
|
||||
@ -296,7 +296,7 @@ $modifiedLabel cmp #0 ; modified
|
||||
if(asmgen.options.romable) {
|
||||
// cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
|
||||
// so we need to store the loop end value in a newly allocated temporary variable
|
||||
val toValueVar = asmgen.getTempVarName(iterableDt.elementType().base)
|
||||
val toValueVar = asmgen.createTempVarReused(iterableDt.elementType().base, false, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out(" sta $toValueVar")
|
||||
@ -420,6 +420,7 @@ $endLabel""")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.romableError("self-modifying code (forloop over words range)", stmt.position) // TODO fix romable; there is self-modifying code below
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
@ -442,7 +443,6 @@ $modifiedLabel sbc #0 ; modified
|
||||
eor #$80
|
||||
+ bpl $loopLabel
|
||||
$endLabel""")
|
||||
asmgen.romableError("self-modifying code (forloop over words range)", stmt.position) // TODO fix romable
|
||||
}
|
||||
|
||||
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
|
||||
@ -508,7 +508,7 @@ $endLabel""")
|
||||
when {
|
||||
iterableDt.isString -> {
|
||||
if(asmgen.options.romable) {
|
||||
val indexVar = asmgen.getTempVarName(BaseDataType.UBYTE)
|
||||
val indexVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
sty $indexVar
|
||||
@ -539,7 +539,10 @@ $endLabel""")
|
||||
}
|
||||
}
|
||||
iterableDt.isByteArray || iterableDt.isBoolArray -> {
|
||||
val indexVar = if(asmgen.options.romable) asmgen.getTempVarName(iterableDt.elementType().base) else asmgen.makeLabel("for_index")
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(iterableDt.elementType().base, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
$loopLabel sty $indexVar
|
||||
@ -576,7 +579,10 @@ $loopLabel sty $indexVar
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
iterableDt.isSplitWordArray -> {
|
||||
val indexVar = if(asmgen.options.romable) asmgen.getTempVarName(BaseDataType.UBYTE) else asmgen.makeLabel("for_index")
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
@ -616,8 +622,10 @@ $loopLabel sty $indexVar
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
iterableDt.isWordArray -> {
|
||||
val length = numElements * 2
|
||||
val indexVar = if(asmgen.options.romable) asmgen.getTempVarName(BaseDataType.UBYTE) else asmgen.makeLabel("for_index")
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
@ -627,16 +635,16 @@ $loopLabel sty $indexVar
|
||||
lda $iterableName+1,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(length<=127) {
|
||||
if(numElements<=127) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
iny
|
||||
cpy #$length
|
||||
cpy #${numElements*2}
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
// length is 128 words, 256 bytes
|
||||
// array size is 128 words, 256 bytes
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@ -645,7 +653,7 @@ $loopLabel sty $indexVar
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(!asmgen.options.romable) {
|
||||
if(length>=16) {
|
||||
if(numElements>=16) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
|
@ -149,6 +149,93 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
translateIfElseBodies("beq", ifElse)
|
||||
}
|
||||
|
||||
private fun translateIfElseBodiesSignedByte(elseConditional: String, value: PtExpression, stmt: PtIfElse) {
|
||||
fun branchElse(label: String) {
|
||||
when (elseConditional) {
|
||||
"<" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $label""")
|
||||
}
|
||||
">=" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $label""")
|
||||
}
|
||||
else -> throw AssemblyError("wrong conditional $elseConditional")
|
||||
}
|
||||
}
|
||||
val afterIfLabel = asmgen.makeLabel("afterif")
|
||||
asmgen.cmpAwithByteValue(value, true)
|
||||
if(stmt.hasElse()) {
|
||||
// if and else blocks
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
branchElse(elseLabel)
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
// no else block
|
||||
branchElse(afterIfLabel)
|
||||
asmgen.translate(stmt.ifScope)
|
||||
}
|
||||
asmgen.out(afterIfLabel)
|
||||
}
|
||||
|
||||
private fun translateJumpElseBodiesSignedByte(elseConditional: String, value: PtExpression, jump: PtJump, elseBlock: PtNodeGroup) {
|
||||
fun branchTarget(label: String) {
|
||||
when (elseConditional) {
|
||||
"<" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $label""")
|
||||
}
|
||||
">=" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $label""")
|
||||
}
|
||||
else -> throw AssemblyError("wrong conditional $elseConditional")
|
||||
}
|
||||
}
|
||||
fun branchElse(label: String) {
|
||||
when (elseConditional) {
|
||||
"<" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $label""")
|
||||
}
|
||||
">=" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $label""")
|
||||
}
|
||||
else -> throw AssemblyError("wrong conditional $elseConditional")
|
||||
}
|
||||
}
|
||||
|
||||
var target = asmgen.getJumpTarget(jump, false)
|
||||
asmgen.cmpAwithByteValue(value, true)
|
||||
if(target.indirect) {
|
||||
branchElse("+")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.jmp(target.asmLabel, target.indirect, target.indexedX)
|
||||
asmgen.out("+")
|
||||
} else {
|
||||
require(!target.needsExpressionEvaluation)
|
||||
branchTarget(target.asmLabel)
|
||||
}
|
||||
asmgen.translate(elseBlock)
|
||||
}
|
||||
|
||||
private fun translateIfElseBodies(elseBranchInstr: String, stmt: PtIfElse) {
|
||||
// comparison value is already in A
|
||||
val afterIfLabel = asmgen.makeLabel("afterif")
|
||||
@ -212,38 +299,9 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
translateIfElseBodies("beq", stmt)
|
||||
}
|
||||
"<" -> translateByteLess(stmt, signed, jumpAfterIf)
|
||||
"<=" -> {
|
||||
// X<=Y -> Y>=X (reverse of >=)
|
||||
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, signed)
|
||||
asmgen.cmpAwithByteValue(condition.left, false)
|
||||
return if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bpl", "bmi", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bmi", stmt)
|
||||
} else {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
}
|
||||
}
|
||||
"<=" -> translateByteLessEqual(stmt, signed, jumpAfterIf)
|
||||
">" -> translateByteGreater(stmt, signed, jumpAfterIf)
|
||||
">=" -> {
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
return if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bpl", "bmi", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bmi", stmt)
|
||||
} else {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
}
|
||||
}
|
||||
">=" -> translateByteGreaterEqual(stmt, signed, jumpAfterIf)
|
||||
in LogicalOperators -> {
|
||||
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.BOOL, stmt.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
|
||||
@ -406,13 +464,13 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
private fun translateByteLess(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||
val condition = stmt.condition as PtBinaryExpression
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope)
|
||||
translateJumpElseBodiesSignedByte("<", condition.right, jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bpl", stmt)
|
||||
translateIfElseBodiesSignedByte("<", condition.right, stmt)
|
||||
} else {
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcc", "bcs", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
@ -420,16 +478,33 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateByteLessEqual(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||
// X<=Y -> Y>=X (reverse of >=)
|
||||
val condition = stmt.condition as PtBinaryExpression
|
||||
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, signed)
|
||||
return if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodiesSignedByte(">=", condition.left, jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodiesSignedByte(">=", condition.left, stmt)
|
||||
} else {
|
||||
asmgen.cmpAwithByteValue(condition.left, false)
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateByteGreater(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||
val condition = stmt.condition as PtBinaryExpression
|
||||
if(signed) {
|
||||
// X>Y --> Y<X
|
||||
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, true)
|
||||
asmgen.cmpAwithByteValue(condition.left, true)
|
||||
if (jumpAfterIf != null)
|
||||
translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope)
|
||||
translateJumpElseBodiesSignedByte("<", condition.left, jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bpl", stmt)
|
||||
translateIfElseBodiesSignedByte("<", condition.left, stmt)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A)
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
@ -471,6 +546,23 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateByteGreaterEqual(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||
val condition = stmt.condition as PtBinaryExpression
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
||||
return if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodiesSignedByte(">=", condition.right, jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodiesSignedByte(">=", condition.right, stmt)
|
||||
} else {
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateIfWord(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
|
||||
val signed = condition.left.type.isSigned
|
||||
val constValue = condition.right.asConstInteger()
|
||||
|
@ -45,7 +45,6 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
|
||||
memorySlabs()
|
||||
tempVars()
|
||||
footer()
|
||||
}
|
||||
}
|
||||
@ -219,29 +218,6 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun tempVars() {
|
||||
// these can be in the no clear section because they'll always get assigned a new value
|
||||
asmgen.out("; expression temp vars\n .section BSS_NOCLEAR")
|
||||
for((dt, count) in asmgen.tempVarsCounters) {
|
||||
if(count>0) {
|
||||
for(num in 1..count) {
|
||||
val name = asmgen.buildTempVarName(dt, num)
|
||||
when (dt) {
|
||||
BaseDataType.BOOL -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.BYTE -> asmgen.out("$name .char ?")
|
||||
BaseDataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.WORD -> asmgen.out("$name .sint ?")
|
||||
BaseDataType.UWORD -> asmgen.out("$name .word ?")
|
||||
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||
BaseDataType.LONG -> throw AssemblyError("should not have a variable with long dt only constants")
|
||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
asmgen.out(" .send BSS_NOCLEAR")
|
||||
}
|
||||
|
||||
private fun footer() {
|
||||
var relocateBssVars = false
|
||||
var relocateBssSlabs = false
|
||||
@ -501,8 +477,8 @@ internal class ProgramAndVarsGen(
|
||||
if(addr!=null)
|
||||
asmgen.out("$name = $addr")
|
||||
else when(dt) {
|
||||
BaseDataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.UWORD -> asmgen.out("$name .word ?")
|
||||
BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> asmgen.out("$name .word ?")
|
||||
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||
}
|
||||
|
@ -973,7 +973,7 @@ internal class AssignmentAsmGen(
|
||||
if(!directIntoY(expr.right)) asmgen.out(" pha")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
|
||||
if(!directIntoY(expr.right)) asmgen.out(" pla")
|
||||
asmgen.out(" jsr prog8_math.divmod_ub_asm")
|
||||
asmgen.out(" jsr prog8_math.remainder_ub_asm")
|
||||
if(target.register==RegisterOrPair.A)
|
||||
asmgen.out(" cmp #0") // fix the status register
|
||||
else
|
||||
@ -1188,19 +1188,18 @@ internal class AssignmentAsmGen(
|
||||
}
|
||||
} else if(dt.isWord) {
|
||||
if(shifts==7 && expr.operator == "<<") {
|
||||
// optimized shift left 7 (*128) by first swapping the lsb/msb and then doing just one final shift
|
||||
// optimized shift left 7 (*128) by swapping the lsb/msb and then doing just one final shift
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.AY, signed)
|
||||
asmgen.out("""
|
||||
; shift left 7
|
||||
sty P8ZP_SCRATCH_REG ; msb
|
||||
sty P8ZP_SCRATCH_REG
|
||||
lsr P8ZP_SCRATCH_REG
|
||||
php ; save carry
|
||||
ror a
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda #0
|
||||
plp ; restore carry
|
||||
ror P8ZP_SCRATCH_REG
|
||||
ror a
|
||||
ldy P8ZP_SCRATCH_REG""")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -1741,6 +1740,17 @@ internal class AssignmentAsmGen(
|
||||
}
|
||||
}
|
||||
|
||||
fun requiresCmp(expr: PtExpression) =
|
||||
when (expr) {
|
||||
is PtFunctionCall -> {
|
||||
val function = asmgen.symbolTable.lookup(expr.name)
|
||||
function is StExtSub // don't assume the extsub/asmsub has set the cpu flags correctly on exit, add an explicit cmp
|
||||
}
|
||||
is PtBuiltinFunctionCall -> true
|
||||
is PtIfExpression -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
if(!expr.right.isSimple() && expr.operator!="xor") {
|
||||
// shortcircuit evaluation into A
|
||||
val shortcutLabel = asmgen.makeLabel("shortcut")
|
||||
@ -1748,15 +1758,23 @@ internal class AssignmentAsmGen(
|
||||
"and" -> {
|
||||
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
if(requiresCmp(expr.left))
|
||||
asmgen.out(" cmp #0")
|
||||
asmgen.out(" beq $shortcutLabel")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
|
||||
if(requiresCmp(expr.right))
|
||||
asmgen.out(" cmp #0")
|
||||
asmgen.out(shortcutLabel)
|
||||
}
|
||||
"or" -> {
|
||||
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
if(requiresCmp(expr.left))
|
||||
asmgen.out(" cmp #0")
|
||||
asmgen.out(" bne $shortcutLabel")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
|
||||
if(requiresCmp(expr.right))
|
||||
asmgen.out(" cmp #0")
|
||||
asmgen.out(shortcutLabel)
|
||||
}
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
@ -2609,10 +2627,10 @@ $endLabel""")
|
||||
|
||||
private fun assignAddressOf(target: AsmAssignTarget, sourceName: String, msb: Boolean, arrayDt: DataType?, arrayIndexExpr: PtExpression?) {
|
||||
if(arrayIndexExpr!=null) {
|
||||
val arrayName = if(arrayDt!!.isSplitWordArray) sourceName+"_lsb" else sourceName // the _lsb split array comes first in memory
|
||||
val constIndex = arrayIndexExpr.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
if (arrayDt.isUnsignedWord) {
|
||||
if (arrayDt!!.isUnsignedWord) {
|
||||
// using a UWORD pointer with array indexing, always bytes
|
||||
require(!msb)
|
||||
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
|
||||
if(constIndex in 1..255)
|
||||
@ -2636,15 +2654,16 @@ $endLabel""")
|
||||
else {
|
||||
if(constIndex>0) {
|
||||
val offset = if(arrayDt.isSplitWordArray) constIndex else program.memsizer.memorySize(arrayDt, constIndex) // add arrayIndexExpr * elementsize to the address of the array variable.
|
||||
asmgen.out(" lda #<($arrayName + $offset) | ldy #>($arrayName + $offset)")
|
||||
asmgen.out(" lda #<($sourceName + $offset) | ldy #>($sourceName + $offset)")
|
||||
} else {
|
||||
asmgen.out(" lda #<$arrayName | ldy #>$arrayName")
|
||||
asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
||||
}
|
||||
}
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return
|
||||
} else {
|
||||
if (arrayDt.isUnsignedWord) {
|
||||
if (arrayDt!!.isUnsignedWord) {
|
||||
// using a UWORD pointer with array indexing, always bytes
|
||||
require(!msb)
|
||||
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||
@ -2679,10 +2698,29 @@ $endLabel""")
|
||||
}
|
||||
else {
|
||||
assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.A, false)
|
||||
val subtype = arrayDt.sub!!
|
||||
if(subtype.isByteOrBool) {
|
||||
// elt size 1, we're good
|
||||
} else if(subtype.isWord) {
|
||||
if(!arrayDt.isSplitWordArray) {
|
||||
// elt size 2
|
||||
asmgen.out(" asl a")
|
||||
}
|
||||
} else if(subtype==BaseDataType.FLOAT) {
|
||||
if(asmgen.options.compTarget.FLOAT_MEM_SIZE != 5)
|
||||
TODO("support float size other than 5 ${arrayIndexExpr.position}")
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc P8ZP_SCRATCH_REG"""
|
||||
)
|
||||
} else throw AssemblyError("weird type $subtype")
|
||||
asmgen.out("""
|
||||
ldy #>$arrayName
|
||||
ldy #>$sourceName
|
||||
clc
|
||||
adc #<$arrayName
|
||||
adc #<$sourceName
|
||||
bcc +
|
||||
iny
|
||||
+""")
|
||||
|
@ -205,7 +205,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" ldx P8ZP_SCRATCH_B1")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
val tempVar = asmgen.getTempVarName(BaseDataType.UBYTE)
|
||||
val tempVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, memory)
|
||||
asmgen.out(" sta $tempVar")
|
||||
if(value.expression is PtTypeCast)
|
||||
inplacemodificationByteWithValue(tempVar, DataType.UBYTE, operator, value.expression)
|
||||
@ -356,7 +356,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
val tempVar = asmgen.getTempVarName(BaseDataType.UBYTE)
|
||||
val tempVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, target.array)
|
||||
asmgen.out(" sta $tempVar")
|
||||
if(value.expression is PtTypeCast)
|
||||
inplacemodificationByteWithValue(tempVar, target.datatype, operator, value.expression)
|
||||
@ -439,7 +439,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
val tempVar = asmgen.getTempVarName(BaseDataType.UWORD)
|
||||
val tempVar = asmgen.createTempVarReused(BaseDataType.UWORD, false, target.array)
|
||||
asmgen.out(" sta $tempVar | stx $tempVar+1")
|
||||
if(value.expression is PtTypeCast)
|
||||
inplacemodificationWordWithValue(tempVar, target.datatype, operator, value.expression, block)
|
||||
@ -457,7 +457,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
|
||||
target.datatype.isFloat -> {
|
||||
// copy array value into tempvar
|
||||
val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT)
|
||||
val tempvar = asmgen.createTempVarReused(BaseDataType.FLOAT, false, target.array)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.A)
|
||||
asmgen.out("""
|
||||
ldy #>${target.asmVarname}
|
||||
@ -910,7 +910,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
"-" -> asmgen.out(" sec | sbc $otherName")
|
||||
"*" -> asmgen.out(" ldy $otherName | jsr prog8_math.multiply_bytes")
|
||||
"/" -> asmgen.out(" ldy $otherName | jsr prog8_math.divmod_ub_asm | tya")
|
||||
"%" -> asmgen.out(" ldy $otherName | jsr prog8_math.divmod_ub_asm")
|
||||
"%" -> asmgen.out(" ldy $otherName | jsr prog8_math.remainder_ub_asm")
|
||||
"<<" -> {
|
||||
asmgen.out("""
|
||||
ldy $otherName
|
||||
@ -1019,7 +1019,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(value==0)
|
||||
throw AssemblyError("division by zero")
|
||||
asmgen.out(" ldy #$value | jsr prog8_math.divmod_ub_asm")
|
||||
asmgen.out(" ldy #$value | jsr prog8_math.remainder_ub_asm")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"<<" -> {
|
||||
@ -1190,7 +1190,7 @@ $shortcutLabel:""")
|
||||
"%" -> {
|
||||
if(signed)
|
||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||
asmgen.out(" ldy $variable | jsr prog8_math.divmod_ub_asm")
|
||||
asmgen.out(" ldy $variable | jsr prog8_math.remainder_ub_asm")
|
||||
}
|
||||
"<<" -> {
|
||||
asmgen.out("""
|
||||
@ -1363,7 +1363,7 @@ $shortcutLabel:""")
|
||||
"%" -> {
|
||||
if(signed)
|
||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||
asmgen.out(" tay | lda $variable | jsr prog8_math.divmod_ub_asm")
|
||||
asmgen.out(" tay | lda $variable | jsr prog8_math.remainder_ub_asm")
|
||||
}
|
||||
"<<" -> {
|
||||
asmgen.out("""
|
||||
@ -1534,7 +1534,7 @@ $shortcutLabel:""")
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
ldy #$value
|
||||
jsr prog8_math.divmod_ub_asm
|
||||
jsr prog8_math.remainder_ub_asm
|
||||
sta $name""")
|
||||
}
|
||||
"<<" -> {
|
||||
@ -1921,18 +1921,16 @@ $shortcutLabel:""")
|
||||
asmgen.out(" lda #0 | sta $lsb")
|
||||
}
|
||||
value==7 -> {
|
||||
// optimized shift left 7 (*128) by first swapping the lsb/msb and then doing just one final shift
|
||||
// optimized shift left 7 (*128) by swapping the lsb/msb and then doing just one final shift
|
||||
asmgen.out("""
|
||||
; shift left 7
|
||||
lsr $msb
|
||||
php ; save carry
|
||||
lda $lsb
|
||||
ror a
|
||||
sta $msb
|
||||
lda #0
|
||||
sta $lsb
|
||||
plp ; restore carry
|
||||
ror $msb
|
||||
ror $lsb""")
|
||||
ror a
|
||||
sta $lsb""")
|
||||
}
|
||||
value>3 -> asmgen.out("""
|
||||
ldy #$value
|
||||
|
@ -68,22 +68,22 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
|
||||
private fun assignCpuRegister(returns: StExtSubParameter, regNum: Int, target: PtAssignTarget): IRCodeChunks {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val loadCpuRegInstr = when(returns.register.registerOrPair) {
|
||||
RegisterOrPair.A -> IRInstruction(Opcode.LOADHA, IRDataType.BYTE, reg1=regNum)
|
||||
RegisterOrPair.X -> IRInstruction(Opcode.LOADHX, IRDataType.BYTE, reg1=regNum)
|
||||
RegisterOrPair.Y -> IRInstruction(Opcode.LOADHY, IRDataType.BYTE, reg1=regNum)
|
||||
RegisterOrPair.AX -> IRInstruction(Opcode.LOADHAX, IRDataType.WORD, reg1=regNum)
|
||||
RegisterOrPair.AY -> IRInstruction(Opcode.LOADHAY, IRDataType.WORD, reg1=regNum)
|
||||
RegisterOrPair.XY -> IRInstruction(Opcode.LOADHXY, IRDataType.WORD, reg1=regNum)
|
||||
in Cx16VirtualRegisters -> IRInstruction(Opcode.LOADM, irType(returns.type), reg1=regNum, labelSymbol = "cx16.${returns.register.registerOrPair.toString().lowercase()}")
|
||||
RegisterOrPair.FAC1 -> IRInstruction(Opcode.LOADHFACZERO, IRDataType.FLOAT, fpReg1 = regNum)
|
||||
RegisterOrPair.FAC2 -> IRInstruction(Opcode.LOADHFACONE, IRDataType.FLOAT, fpReg1 = regNum)
|
||||
null -> {
|
||||
TODO("assign CPU status flag ${returns.register.statusflag!!}")
|
||||
}
|
||||
else -> throw AssemblyError("cannot load register")
|
||||
when(returns.register.registerOrPair) {
|
||||
RegisterOrPair.A -> addInstr(result, IRInstruction(Opcode.LOADHA, IRDataType.BYTE, reg1=regNum), null)
|
||||
RegisterOrPair.X -> addInstr(result, IRInstruction(Opcode.LOADHX, IRDataType.BYTE, reg1=regNum), null)
|
||||
RegisterOrPair.Y -> addInstr(result, IRInstruction(Opcode.LOADHY, IRDataType.BYTE, reg1=regNum), null)
|
||||
RegisterOrPair.AX -> addInstr(result, IRInstruction(Opcode.LOADHAX, IRDataType.WORD, reg1=regNum), null)
|
||||
RegisterOrPair.AY -> addInstr(result, IRInstruction(Opcode.LOADHAY, IRDataType.WORD, reg1=regNum), null)
|
||||
RegisterOrPair.XY -> addInstr(result, IRInstruction(Opcode.LOADHXY, IRDataType.WORD, reg1=regNum), null)
|
||||
in Cx16VirtualRegisters -> addInstr(result, IRInstruction(Opcode.LOADM, irType(returns.type), reg1=regNum, labelSymbol = "cx16.${returns.register.registerOrPair.toString().lowercase()}"), null)
|
||||
RegisterOrPair.FAC1 -> addInstr(result, IRInstruction(Opcode.LOADHFACZERO, IRDataType.FLOAT, fpReg1 = regNum), null)
|
||||
RegisterOrPair.FAC2 -> addInstr(result, IRInstruction(Opcode.LOADHFACONE, IRDataType.FLOAT, fpReg1 = regNum), null)
|
||||
null -> if(returns.register.statusflag!=null)
|
||||
result += assignCpuStatusFlagReturnvalue(returns.register.statusflag!!, regNum)
|
||||
else
|
||||
throw AssemblyError("weird CPU register")
|
||||
else -> throw AssemblyError("weird CPU register")
|
||||
}
|
||||
addInstr(result, loadCpuRegInstr, null)
|
||||
|
||||
// build an assignment to store the value in the actual target.
|
||||
val assign = PtAssignment(target.position)
|
||||
@ -93,6 +93,30 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return result
|
||||
}
|
||||
|
||||
private fun assignCpuStatusFlagReturnvalue(statusflag: Statusflag, regNum: Int): IRCodeChunks {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
when(statusflag) {
|
||||
Statusflag.Pc -> {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=regNum, immediate = 0)
|
||||
it += IRInstruction(Opcode.ROXL, IRDataType.BYTE, reg1=regNum)
|
||||
}
|
||||
}
|
||||
Statusflag.Pz -> TODO("find a way to assign cpu Z status bit to reg $regNum but it can already be clobbered by other return values")
|
||||
Statusflag.Pn -> TODO("find a way to assign cpu Z status bit to reg $regNum but it can already be clobbered by other return values")
|
||||
Statusflag.Pv -> {
|
||||
val skipLabel = codeGen.createLabelName()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=regNum, immediate = 0)
|
||||
it += IRInstruction(Opcode.BSTVC, labelSymbol = skipLabel)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=regNum, immediate = 1)
|
||||
}
|
||||
result += IRCodeChunk(skipLabel, null)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
internal fun translate(augAssign: PtAugmentedAssign): IRCodeChunks {
|
||||
// augmented assignment always has just a single target
|
||||
if (augAssign.target.children.single() is PtIrRegister)
|
||||
@ -110,7 +134,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val chunks = when (augAssign.operator) {
|
||||
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"-=" -> operatorMinusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"*=" -> operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"*=" -> operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"/=" -> operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"|=" -> operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"or=" -> operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
@ -207,7 +231,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val tr = expressionEval.translateExpression(array.index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
if(!array.splitWords && eltSize>1)
|
||||
result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
|
||||
result += codeGen.multiplyByConst(DataType.UBYTE, tr.resultReg, eltSize)
|
||||
return tr.resultReg
|
||||
}
|
||||
|
||||
@ -769,7 +793,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return result
|
||||
}
|
||||
|
||||
private fun operatorMultiplyInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
|
||||
private fun operatorMultiplyInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
||||
if(array!=null) {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
@ -783,7 +807,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val valueReg=codeGen.registers.next(eltDt)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, eltDt, reg1=valueReg, immediate = constValue)
|
||||
it += IRInstruction(Opcode.MULM, eltDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
val opcode = if(signed) Opcode.MULSM else Opcode.MULM
|
||||
it += IRInstruction(opcode, eltDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@ -803,22 +828,23 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress)
|
||||
IRInstruction(Opcode.MULSM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.MULSM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
} else {
|
||||
if(constFactorRight!=null && !constFactorRight.type.isFloat) {
|
||||
val factor = constFactorRight.number.toInt()
|
||||
result += codeGen.multiplyByConstInplace(vmDt, constAddress, symbol, factor)
|
||||
result += codeGen.multiplyByConstInplace(vmDt, signed, constAddress, symbol, factor)
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val opcode = if(signed) Opcode.MULSM else Opcode.MULM
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
IRInstruction(opcode, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
IRInstruction(opcode, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
, null)
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package prog8.codegen.intermediate
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.DataType
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
@ -662,7 +663,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(eltSize>1)
|
||||
it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
|
||||
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
|
||||
if(msb)
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = target.variable.name)
|
||||
@ -683,7 +684,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(eltSize>1)
|
||||
it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
|
||||
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
|
||||
if(msb)
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = target.variable.name)
|
||||
|
@ -2,10 +2,7 @@ package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.*
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.Cx16VirtualRegisters
|
||||
import prog8.code.core.Statusflag
|
||||
import prog8.code.core.*
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
@ -392,7 +389,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(arrayIx.index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
if(eltSize>1)
|
||||
result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
|
||||
result += codeGen.multiplyByConst(DataType.UBYTE, tr.resultReg, eltSize)
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=tr.resultReg, labelSymbol = arrayVarSymbol), null)
|
||||
@ -565,8 +562,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return when(binExpr.operator) {
|
||||
"+" -> operatorPlus(binExpr, vmDt)
|
||||
"-" -> operatorMinus(binExpr, vmDt)
|
||||
"*" -> operatorMultiply(binExpr, vmDt)
|
||||
"/" -> operatorDivide(binExpr, vmDt, signed)
|
||||
"*" -> operatorMultiply(binExpr, binExpr.left.type)
|
||||
"/" -> operatorDivide(binExpr, binExpr.left.type)
|
||||
"%" -> operatorModulo(binExpr, vmDt)
|
||||
"|" -> operatorOr(binExpr, vmDt, true)
|
||||
"&" -> operatorAnd(binExpr, vmDt, true)
|
||||
@ -1049,7 +1046,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
private fun loadStatusAsBooleanResult(branchForTrue: Opcode, result: MutableList<IRCodeChunkBase>): Int {
|
||||
// TODO this used to be a single instruction like SCC, SCS, SZ etc but those were problematic
|
||||
// TODO this used to be a single instruction like SCC, SCS, SZ etc
|
||||
val other = codeGen.createLabelName()
|
||||
val after = codeGen.createLabelName()
|
||||
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
@ -1064,7 +1061,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
private fun compareRegisterAsBooleanResult(branchForTrue: Opcode, dt: IRDataType, reg1: Int, reg2: Int, result: MutableList<IRCodeChunkBase>): Int {
|
||||
// TODO this used to be a single instruction like SCC, SCS, SZ etc but those were problematic
|
||||
// TODO this used to be a single instruction like SCC, SCS, SZ etc
|
||||
val other = codeGen.createLabelName()
|
||||
val after = codeGen.createLabelName()
|
||||
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
@ -1224,7 +1221,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun operatorDivide(binExpr: PtBinaryExpression, vmDt: IRDataType, signed: Boolean): ExpressionCodeResult {
|
||||
private fun operatorDivide(binExpr: PtBinaryExpression, dt: DataType): ExpressionCodeResult {
|
||||
val vmDt = irType(dt)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constFactorRight = binExpr.right as? PtNumber
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
@ -1239,7 +1237,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||
addInstr(result, if(signed)
|
||||
addInstr(result, if(dt.isSigned)
|
||||
IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||
else
|
||||
IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
|
||||
@ -1251,13 +1249,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val factor = constFactorRight.number.toInt()
|
||||
result += codeGen.divideByConst(vmDt, tr.resultReg, factor, signed)
|
||||
result += codeGen.divideByConst(vmDt, tr.resultReg, factor, dt.isSigned)
|
||||
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||
} else {
|
||||
if(binExpr.right is PtNumber) {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
addInstr(result, if (signed)
|
||||
addInstr(result, if (dt.isSigned)
|
||||
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||
@ -1268,7 +1266,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
addInstr(result, if (signed)
|
||||
addInstr(result, if (dt.isSigned)
|
||||
IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||
else
|
||||
IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
|
||||
@ -1279,7 +1277,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun operatorMultiply(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
|
||||
private fun operatorMultiply(binExpr: PtBinaryExpression, dt: DataType): ExpressionCodeResult {
|
||||
val vmDt = irType(dt)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constFactorLeft = binExpr.left as? PtNumber
|
||||
val constFactorRight = binExpr.right as? PtNumber
|
||||
@ -1301,7 +1300,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||
addInstr(result, IRInstruction(Opcode.MULR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.MULSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
||||
ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg)
|
||||
}
|
||||
} else {
|
||||
@ -1309,20 +1308,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(binExpr.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val factor = constFactorLeft.number.toInt()
|
||||
result += codeGen.multiplyByConst(vmDt, tr.resultReg, factor)
|
||||
result += codeGen.multiplyByConst(dt, tr.resultReg, factor)
|
||||
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||
} else if(constFactorRight!=null && !constFactorRight.type.isFloat) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val factor = constFactorRight.number.toInt()
|
||||
result += codeGen.multiplyByConst(vmDt, tr.resultReg, factor)
|
||||
result += codeGen.multiplyByConst(dt, tr.resultReg, factor)
|
||||
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||
} else {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.MULR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
|
||||
val opcode = if(dt.isSigned) Opcode.MULSR else Opcode.MULR
|
||||
addInstr(result, IRInstruction(opcode, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
|
||||
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
|
@ -256,8 +256,8 @@ class IRCodeGen(
|
||||
is PtBool,
|
||||
is PtArray,
|
||||
is PtBlock,
|
||||
is PtDefer -> throw AssemblyError("should have been transformed")
|
||||
is PtString -> throw AssemblyError("should not occur as separate statement node ${node.position}")
|
||||
is PtDefer -> throw AssemblyError("defer should have been transformed")
|
||||
is PtString -> throw AssemblyError("string should not occur as separate statement node ${node.position}")
|
||||
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
|
||||
else -> TODO("missing codegen for $node")
|
||||
}
|
||||
@ -420,8 +420,9 @@ class IRCodeGen(
|
||||
whenStmt.choices.children.forEach {
|
||||
val choice = it as PtWhenChoice
|
||||
if(choice.isElse) {
|
||||
require(choice.parent.children.last() === choice)
|
||||
result += translateNode(choice.statements)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
// is always the last node so can fall through
|
||||
} else {
|
||||
if(choice.statements.children.isEmpty()) {
|
||||
// no statements for this choice value, jump to the end immediately
|
||||
@ -433,22 +434,30 @@ class IRCodeGen(
|
||||
}
|
||||
} else {
|
||||
val choiceLabel = createLabelName()
|
||||
choices.add(choiceLabel to choice)
|
||||
val onlyJumpLabel = ((choice.statements.children.singleOrNull() as? PtJump)?.target as? PtIdentifier)?.name
|
||||
val branchLabel: String
|
||||
if(onlyJumpLabel==null) {
|
||||
choices.add(choiceLabel to choice)
|
||||
branchLabel = choiceLabel
|
||||
} else {
|
||||
branchLabel = onlyJumpLabel
|
||||
}
|
||||
choice.values.children.map { v -> v as PtNumber }.sortedBy { v -> v.number }.forEach { value ->
|
||||
result += IRCodeChunk(null, null).also { chunk ->
|
||||
chunk += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = choiceLabel)
|
||||
chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = branchLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
|
||||
if(choices.isNotEmpty())
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
|
||||
choices.forEach { (label, choice) ->
|
||||
result += labelFirstChunk(translateNode(choice.statements), label)
|
||||
val lastStatement = choice.statements.children.last()
|
||||
if(lastStatement !is PtReturn && lastStatement !is PtJump)
|
||||
if(!choice.isOnlyGotoOrReturn())
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
}
|
||||
|
||||
@ -469,10 +478,11 @@ class IRCodeGen(
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
val elementDt = irType(iterable.type.elementType())
|
||||
val iterableLength = symbolTable.getLength(iterable.name)
|
||||
val loopvarSymbol = forLoop.variable.name
|
||||
val indexReg = registers.next(IRDataType.BYTE)
|
||||
val tmpReg = registers.next(IRDataType.BYTE)
|
||||
val tmpReg = registers.next(elementDt)
|
||||
val loopLabel = createLabelName()
|
||||
val endLabel = createLabelName()
|
||||
when {
|
||||
@ -480,9 +490,9 @@ class IRCodeGen(
|
||||
// iterate over a zero-terminated string
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
|
||||
result += IRCodeChunk(loopLabel, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name)
|
||||
it += IRInstruction(Opcode.LOADX, elementDt, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name)
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tmpReg, labelSymbol = loopvarSymbol)
|
||||
it += IRInstruction(Opcode.STOREM, elementDt, reg1 = tmpReg, labelSymbol = loopvarSymbol)
|
||||
}
|
||||
result += translateNode(forLoop.statements)
|
||||
val jumpChunk = IRCodeChunk(null, null)
|
||||
@ -493,8 +503,7 @@ class IRCodeGen(
|
||||
}
|
||||
iterable.type.isSplitWordArray -> {
|
||||
// iterate over lsb/msb split word array
|
||||
val elementDt = iterable.type.elementType()
|
||||
if(!elementDt.isWord)
|
||||
if(elementDt!=IRDataType.WORD)
|
||||
throw AssemblyError("weird dt")
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
|
||||
result += IRCodeChunk(loopLabel, null).also {
|
||||
@ -504,7 +513,7 @@ class IRCodeGen(
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, labelSymbol=iterable.name+"_msb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, labelSymbol=iterable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=concatReg, reg2=tmpRegMsb, reg3=tmpRegLsb)
|
||||
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=concatReg, labelSymbol = loopvarSymbol)
|
||||
it += IRInstruction(Opcode.STOREM, elementDt, reg1=concatReg, labelSymbol = loopvarSymbol)
|
||||
}
|
||||
result += translateNode(forLoop.statements)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
@ -771,7 +780,7 @@ class IRCodeGen(
|
||||
code += if(factor==0.0) {
|
||||
IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = 0.0)
|
||||
} else {
|
||||
IRInstruction(Opcode.MUL, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
|
||||
IRInstruction(Opcode.MULS, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@ -789,38 +798,40 @@ class IRCodeGen(
|
||||
val factorReg = registers.next(IRDataType.FLOAT)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, immediateFp = factor)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
||||
IRInstruction(Opcode.MULSM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.MULSM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
internal fun multiplyByConst(dt: IRDataType, reg: Int, factor: Int): IRCodeChunk {
|
||||
internal fun multiplyByConst(dt: DataType, reg: Int, factor: Int): IRCodeChunk {
|
||||
val irdt = irType(dt)
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(factor==1)
|
||||
return code
|
||||
val pow2 = powersOfTwoInt.indexOf(factor)
|
||||
if(pow2==1) {
|
||||
// just shift 1 bit
|
||||
code += IRInstruction(Opcode.LSL, dt, reg1=reg)
|
||||
code += IRInstruction(Opcode.LSL, irdt, reg1=reg)
|
||||
}
|
||||
else if(pow2>=1) {
|
||||
// just shift multiple bits
|
||||
val pow2reg = registers.next(IRDataType.BYTE)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=pow2reg, immediate = pow2)
|
||||
code += IRInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg)
|
||||
code += IRInstruction(Opcode.LSLN, irdt, reg1=reg, reg2=pow2reg)
|
||||
} else {
|
||||
code += if (factor == 0) {
|
||||
IRInstruction(Opcode.LOAD, dt, reg1=reg, immediate = 0)
|
||||
IRInstruction(Opcode.LOAD, irdt, reg1=reg, immediate = 0)
|
||||
} else {
|
||||
IRInstruction(Opcode.MUL, dt, reg1=reg, immediate = factor)
|
||||
val opcode = if(dt.isSigned) Opcode.MULS else Opcode.MUL
|
||||
IRInstruction(opcode, irdt, reg1=reg, immediate = factor)
|
||||
}
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
internal fun multiplyByConstInplace(dt: IRDataType, knownAddress: Int?, symbol: String?, factor: Int): IRCodeChunk {
|
||||
internal fun multiplyByConstInplace(dt: IRDataType, signed: Boolean, knownAddress: Int?, symbol: String?, factor: Int): IRCodeChunk {
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(factor==1)
|
||||
return code
|
||||
@ -850,10 +861,11 @@ class IRCodeGen(
|
||||
else {
|
||||
val factorReg = registers.next(dt)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, immediate = factor)
|
||||
val opcode = if(signed) Opcode.MULSM else Opcode.MULM
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, address = knownAddress)
|
||||
IRInstruction(opcode, dt, reg1=factorReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, labelSymbol = symbol)
|
||||
IRInstruction(opcode, dt, reg1=factorReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@ -1954,7 +1966,7 @@ class IRCodeGen(
|
||||
null -> when(registerOrFlag.statusflag) {
|
||||
// TODO: do the statusflag argument as last
|
||||
Statusflag.Pc -> chunk += IRInstruction(Opcode.LSR, paramDt, reg1=resultReg)
|
||||
else -> throw AssemblyError("weird statusflag as param")
|
||||
else -> throw AssemblyError("unsupported statusflag as param")
|
||||
}
|
||||
else -> throw AssemblyError("unsupported register arg")
|
||||
}
|
||||
|
@ -357,7 +357,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
var changed = false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
when (ins.opcode) {
|
||||
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> {
|
||||
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MULS, Opcode.MOD -> {
|
||||
if (ins.immediate == 1) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
|
@ -48,6 +48,36 @@ class IRUnusedCodeRemover(
|
||||
}
|
||||
|
||||
irprog.st.removeTree(blockLabel)
|
||||
removeBlockInits(irprog, blockLabel)
|
||||
}
|
||||
|
||||
private fun removeBlockInits(code: IRProgram, blockLabel: String) {
|
||||
val instructions = code.globalInits.instructions
|
||||
instructions.toTypedArray().forEach {ins ->
|
||||
if(ins.labelSymbol?.startsWith(blockLabel)==true) {
|
||||
instructions.remove(ins)
|
||||
}
|
||||
}
|
||||
|
||||
// remove stray loads
|
||||
instructions.toTypedArray().forEach { ins ->
|
||||
if(ins.opcode in arrayOf(Opcode.LOAD, Opcode.LOADR, Opcode.LOADM)) {
|
||||
if(ins.reg1!=0) {
|
||||
if(instructions.count { it.reg1==ins.reg1 || it.reg2==ins.reg1 } <2) {
|
||||
if(ins.labelSymbol!=null)
|
||||
code.st.removeIfExists(ins.labelSymbol!!)
|
||||
instructions.remove(ins)
|
||||
}
|
||||
}
|
||||
else if(ins.fpReg1!=0) {
|
||||
if (instructions.count { it.fpReg1 == ins.fpReg1 || it.fpReg2 == ins.fpReg1 } < 2) {
|
||||
if(ins.labelSymbol!=null)
|
||||
code.st.removeIfExists(ins.labelSymbol!!)
|
||||
instructions.remove(ins)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeUnusedSubroutines(): Int {
|
||||
|
@ -49,7 +49,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
if(truepart.statements.singleOrNull() is Jump) {
|
||||
return listOf(
|
||||
IAstModification.InsertAfter(ifElse, elsepart, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse)
|
||||
IAstModification.ReplaceNode(elsepart, AnonymousScope.empty(), ifElse)
|
||||
)
|
||||
}
|
||||
if(elsepart.statements.singleOrNull() is Jump) {
|
||||
@ -57,7 +57,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
|
||||
IAstModification.InsertAfter(ifElse, truepart, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse),
|
||||
IAstModification.ReplaceNode(elsepart, AnonymousScope.empty(), ifElse),
|
||||
IAstModification.ReplaceNode(truepart, elsepart, ifElse)
|
||||
)
|
||||
}
|
||||
|
@ -82,12 +82,11 @@ class StatementOptimizer(private val program: Program,
|
||||
// empty true part? switch with the else part
|
||||
if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) {
|
||||
val invertedCondition = invertCondition(ifElse.condition, program)
|
||||
val emptyscope = AnonymousScope(mutableListOf(), ifElse.elsepart.position)
|
||||
val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.truepart, truepart, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, emptyscope, ifElse)
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope.empty(), ifElse)
|
||||
)
|
||||
}
|
||||
|
||||
@ -106,7 +105,7 @@ class StatementOptimizer(private val program: Program,
|
||||
if(ifElse.truepart.statements.singleOrNull() is Return) {
|
||||
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope.empty(), ifElse),
|
||||
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
@ -146,7 +145,7 @@ class StatementOptimizer(private val program: Program,
|
||||
if (range.size() == 1) {
|
||||
// for loop over a (constant) range of just a single value-- optimize the loop away
|
||||
// loopvar/reg = range value , follow by block
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
val scope = AnonymousScope.empty(forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
@ -161,7 +160,7 @@ class StatementOptimizer(private val program: Program,
|
||||
// loop over string of length 1 -> just assign the single character
|
||||
val character = options.compTarget.encodeString(sv.value, sv.encoding)[0]
|
||||
val byte = NumericLiteral(BaseDataType.UBYTE, character.toDouble(), iterable.position)
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
val scope = AnonymousScope.empty(forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
@ -173,7 +172,7 @@ class StatementOptimizer(private val program: Program,
|
||||
// loop over array of length 1 -> just assign the single value
|
||||
val av = (iterable.value as ArrayLiteral).value[0].constValue(program)?.number
|
||||
if(av!=null) {
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
val scope = AnonymousScope.empty(forLoop.position)
|
||||
scope.statements.add(Assignment(
|
||||
AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
|
||||
AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||
@ -466,7 +465,7 @@ class StatementOptimizer(private val program: Program,
|
||||
return IfElse(
|
||||
compare,
|
||||
AnonymousScope(mutableListOf(assign), position),
|
||||
AnonymousScope(mutableListOf(), position),
|
||||
AnonymousScope.empty(),
|
||||
position
|
||||
)
|
||||
}
|
||||
@ -501,6 +500,27 @@ class StatementOptimizer(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(whenStmt.betterAsOnGoto(program, options)) {
|
||||
// rewrite when into a on..goto , which is faster and also smaller for ~5+ cases
|
||||
var elseJump: Jump? = null
|
||||
val jumps = mutableListOf<Pair<Int, Jump>>()
|
||||
whenStmt.choices.forEach { choice ->
|
||||
if(choice.values==null) {
|
||||
elseJump = choice.statements.statements.single() as Jump
|
||||
} else {
|
||||
choice.values!!.forEach { value ->
|
||||
jumps.add(value.constValue(program)!!.number.toInt() to choice.statements.statements.single() as Jump)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val jumpLabels = jumps.sortedBy { it.first }.map { it.second.target as IdentifierReference }
|
||||
|
||||
val elsePart = if(elseJump==null) null else AnonymousScope(mutableListOf(elseJump), elseJump.position)
|
||||
val onGoto = OnGoto(false, whenStmt.condition, jumpLabels, elsePart, whenStmt.position)
|
||||
return listOf(IAstModification.ReplaceNode(whenStmt, onGoto, parent))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
@ -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 SIZEOF_BOOL = 1
|
||||
const ubyte SIZEOF_BYTE = 1
|
||||
const ubyte SIZEOF_UBYTE = 1
|
||||
const ubyte SIZEOF_WORD = 2
|
||||
const ubyte SIZEOF_UWORD = 2
|
||||
const ubyte SIZEOF_FLOAT = 5
|
||||
const ubyte SIZEOF_BOOL = sizeof(bool)
|
||||
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@ -946,6 +947,18 @@ _no_msb_size
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
|
||||
%asm {{
|
||||
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
@ -984,6 +997,7 @@ cx16 {
|
||||
&uword r14 = $1bfc
|
||||
&uword r15 = $1bfe
|
||||
|
||||
; signed word versions
|
||||
&word r0s = $1be0
|
||||
&word r1s = $1be2
|
||||
&word r2s = $1be4
|
||||
@ -1001,6 +1015,7 @@ cx16 {
|
||||
&word r14s = $1bfc
|
||||
&word r15s = $1bfe
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $1be0
|
||||
&ubyte r1L = $1be2
|
||||
&ubyte r2L = $1be4
|
||||
@ -1035,6 +1050,7 @@ cx16 {
|
||||
&ubyte r14H = $1bfd
|
||||
&ubyte r15H = $1bff
|
||||
|
||||
; signed byte versions (low and high bytes)
|
||||
&byte r0sL = $1be0
|
||||
&byte r1sL = $1be2
|
||||
&byte r2sL = $1be4
|
||||
@ -1069,6 +1085,42 @@ cx16 {
|
||||
&byte r14sH = $1bfd
|
||||
&byte r15sH = $1bff
|
||||
|
||||
; boolean versions (low and high bytes)
|
||||
&bool r0bL = $1be0
|
||||
&bool r1bL = $1be2
|
||||
&bool r2bL = $1be4
|
||||
&bool r3bL = $1be6
|
||||
&bool r4bL = $1be8
|
||||
&bool r5bL = $1bea
|
||||
&bool r6bL = $1bec
|
||||
&bool r7bL = $1bee
|
||||
&bool r8bL = $1bf0
|
||||
&bool r9bL = $1bf2
|
||||
&bool r10bL = $1bf4
|
||||
&bool r11bL = $1bf6
|
||||
&bool r12bL = $1bf8
|
||||
&bool r13bL = $1bfa
|
||||
&bool r14bL = $1bfc
|
||||
&bool r15bL = $1bfe
|
||||
|
||||
&bool r0bH = $1be1
|
||||
&bool r1bH = $1be3
|
||||
&bool r2bH = $1be5
|
||||
&bool r3bH = $1be7
|
||||
&bool r4bH = $1be9
|
||||
&bool r5bH = $1beb
|
||||
&bool r6bH = $1bed
|
||||
&bool r7bH = $1bef
|
||||
&bool r8bH = $1bf1
|
||||
&bool r9bH = $1bf3
|
||||
&bool r10bH = $1bf5
|
||||
&bool r11bH = $1bf7
|
||||
&bool r12bH = $1bf9
|
||||
&bool r13bH = $1bfb
|
||||
&bool r14bH = $1bfd
|
||||
&bool r15bH = $1bff
|
||||
|
||||
|
||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
%asm {{
|
||||
ldy #31
|
||||
|
@ -439,12 +439,13 @@ sys {
|
||||
|
||||
const ubyte target = 64 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
|
||||
|
||||
const ubyte SIZEOF_BOOL = 1
|
||||
const ubyte SIZEOF_BYTE = 1
|
||||
const ubyte SIZEOF_UBYTE = 1
|
||||
const ubyte SIZEOF_WORD = 2
|
||||
const ubyte SIZEOF_UWORD = 2
|
||||
const ubyte SIZEOF_FLOAT = 5
|
||||
const ubyte SIZEOF_BOOL = sizeof(bool)
|
||||
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@ -968,6 +969,18 @@ _no_msb_size
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
|
||||
%asm {{
|
||||
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
@ -1006,6 +1019,7 @@ cx16 {
|
||||
&uword r14 = $cffc
|
||||
&uword r15 = $cffe
|
||||
|
||||
; signed word versions
|
||||
&word r0s = $cfe0
|
||||
&word r1s = $cfe2
|
||||
&word r2s = $cfe4
|
||||
@ -1023,6 +1037,7 @@ cx16 {
|
||||
&word r14s = $cffc
|
||||
&word r15s = $cffe
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $cfe0
|
||||
&ubyte r1L = $cfe2
|
||||
&ubyte r2L = $cfe4
|
||||
@ -1057,6 +1072,7 @@ cx16 {
|
||||
&ubyte r14H = $cffd
|
||||
&ubyte r15H = $cfff
|
||||
|
||||
; signed byte versions (low and high bytes)
|
||||
&byte r0sL = $cfe0
|
||||
&byte r1sL = $cfe2
|
||||
&byte r2sL = $cfe4
|
||||
@ -1091,6 +1107,42 @@ cx16 {
|
||||
&byte r14sH = $cffd
|
||||
&byte r15sH = $cfff
|
||||
|
||||
; boolean versions
|
||||
&bool r0bL = $cfe0
|
||||
&bool r1bL = $cfe2
|
||||
&bool r2bL = $cfe4
|
||||
&bool r3bL = $cfe6
|
||||
&bool r4bL = $cfe8
|
||||
&bool r5bL = $cfea
|
||||
&bool r6bL = $cfec
|
||||
&bool r7bL = $cfee
|
||||
&bool r8bL = $cff0
|
||||
&bool r9bL = $cff2
|
||||
&bool r10bL = $cff4
|
||||
&bool r11bL = $cff6
|
||||
&bool r12bL = $cff8
|
||||
&bool r13bL = $cffa
|
||||
&bool r14bL = $cffc
|
||||
&bool r15bL = $cffe
|
||||
|
||||
&bool r0bH = $cfe1
|
||||
&bool r1bH = $cfe3
|
||||
&bool r2bH = $cfe5
|
||||
&bool r3bH = $cfe7
|
||||
&bool r4bH = $cfe9
|
||||
&bool r5bH = $cfeb
|
||||
&bool r6bH = $cfed
|
||||
&bool r7bH = $cfef
|
||||
&bool r8bH = $cff1
|
||||
&bool r9bH = $cff3
|
||||
&bool r10bH = $cff5
|
||||
&bool r11bH = $cff7
|
||||
&bool r12bH = $cff9
|
||||
&bool r13bH = $cffb
|
||||
&bool r14bH = $cffd
|
||||
&bool r15bH = $cfff
|
||||
|
||||
|
||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
%asm {{
|
||||
ldy #31
|
||||
|
@ -28,12 +28,20 @@
|
||||
; that routine can for instance call current() (or just look at the active_task variable) to get the id of the next task to execute.
|
||||
; It has then to return a boolean: true=next task is to be executed, false=skip the task this time.
|
||||
; - in tasks: call yield() to pass control to the next task. Use the returned userdata value to do different things.
|
||||
; For now, you MUST call yield() only from the actual subroutine that has been registered as a task!
|
||||
; (this is because otherwise the cpu call stack gets messed up and an RTS in task1 could suddenly pop a return address belonging to another tasks' call frame)
|
||||
; - call current() to get the current task id.
|
||||
; - call kill(taskid) to kill a task by id.
|
||||
; - call killall() to kill all tasks.
|
||||
; - IMPORTANT: if you add the same subroutine multiple times, IT CANNOT DEPEND ON ANY LOCAL VARIABLES OR R0-R15 TO KEEP STATE. NOT EVEN REPEAT LOOP COUNTERS.
|
||||
; Those are all shared in the different tasks! You HAVE to use a mechanism around the userdata value (pointer?) to keep separate state elsewhere!
|
||||
; - IMPORTANT: ``defer`` cannot be used inside a coroutine that is reused for multiple tasks!!!
|
||||
;
|
||||
; TIP: HOW TO WAIT without BLOCKING other coroutines?
|
||||
; Make sure you call yield() in the waiting loop, for example:
|
||||
; uword timer = cbm.RDTIM16() + 60
|
||||
; while cbm.RDTIM16() != timer
|
||||
; void coroutines.yield()
|
||||
|
||||
coroutines {
|
||||
%option ignore_unused
|
||||
@ -41,19 +49,17 @@ coroutines {
|
||||
const ubyte MAX_TASKS = 64
|
||||
uword[MAX_TASKS] tasklist
|
||||
uword[MAX_TASKS] userdatas
|
||||
uword[MAX_TASKS] returnaddresses
|
||||
ubyte active_task
|
||||
uword supervisor
|
||||
|
||||
sub add(uword taskaddress, uword userdata) -> ubyte {
|
||||
sub add(uword @nozp taskaddress, uword @nozp userdata) -> ubyte {
|
||||
; find the next empty slot in the tasklist and stick it there
|
||||
; returns the task id of the new task, or 255 if there was no space for more tasks. 0 is a valid task id!
|
||||
; also returns the success in the Carry flag (carry set=success, carry clear = task was not added)
|
||||
for cx16.r0L in 0 to len(tasklist)-1 {
|
||||
if tasklist[cx16.r0L] == 0 {
|
||||
tasklist[cx16.r0L] = taskaddress
|
||||
tasklist[cx16.r0L] = sys.get_as_returnaddress(taskaddress)
|
||||
userdatas[cx16.r0L] = userdata
|
||||
returnaddresses[cx16.r0L] = 0
|
||||
sys.set_carry()
|
||||
return cx16.r0L
|
||||
}
|
||||
@ -70,57 +76,48 @@ coroutines {
|
||||
}
|
||||
}
|
||||
|
||||
sub run(uword supervisor_routine) {
|
||||
sub run(uword @nozp supervisor_routine) {
|
||||
supervisor = supervisor_routine
|
||||
for active_task in 0 to len(tasklist)-1 {
|
||||
if tasklist[active_task]!=0 {
|
||||
; activate the termination handler and start the first task
|
||||
; note: cannot use pushw() because JSR doesn't push the return address in the same way
|
||||
sys.push_returnaddress(&termination)
|
||||
goto tasklist[active_task]
|
||||
sys.pushw(tasklist[active_task])
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub yield() -> uword {
|
||||
; Store the return address of the yielding task,
|
||||
; and continue with the next one instead (round-robin)
|
||||
; Returns the associated userdata value
|
||||
uword task_start, task_continue
|
||||
returnaddresses[active_task] = sys.popw()
|
||||
; Store the return address of the yielding task, and continue with the next one instead (round-robin)
|
||||
; Returns the associated userdata value.
|
||||
; NOTE: CAN ONLY BE CALLED FROM THE SCOPE OF THE SUBROUTINE THAT HAS BEEN REGISTERED AS THE TASK!
|
||||
uword task_return_address
|
||||
tasklist[active_task] = sys.popw()
|
||||
|
||||
resume_with_next_task:
|
||||
skip_task:
|
||||
if not next_task() {
|
||||
void sys.popw() ; remove return to the termination handler
|
||||
return 0 ; exiting here will now actually return from the start() call back to the calling program :)
|
||||
return 0 ; exiting here will now actually return back to the calling program that called run()
|
||||
}
|
||||
|
||||
if supervisor!=0 {
|
||||
if supervisor!=0
|
||||
if lsb(call(supervisor))==0
|
||||
goto resume_with_next_task
|
||||
}
|
||||
goto skip_task
|
||||
|
||||
if task_continue==0 {
|
||||
; fetch start address of next task.
|
||||
; address on the stack must be pushed in reverse byte order
|
||||
; also, subtract 1 from the start address because JSR pushes returnaddress minus 1
|
||||
; note: cannot use pushw() because JSR doesn't push the return address in the same way
|
||||
sys.push_returnaddress(task_start)
|
||||
} else
|
||||
sys.pushw(task_continue)
|
||||
|
||||
; returning from yield then continues with the next coroutine
|
||||
; returning from yield then continues with the next coroutine:
|
||||
sys.pushw(task_return_address)
|
||||
return userdatas[active_task]
|
||||
|
||||
sub next_task() -> bool {
|
||||
; search through the task list for the next active task
|
||||
repeat len(tasklist) {
|
||||
active_task++
|
||||
if active_task==len(returnaddresses)
|
||||
if active_task==len(tasklist)
|
||||
active_task=0
|
||||
task_start = tasklist[active_task]
|
||||
if task_start!=0 {
|
||||
task_continue = returnaddresses[active_task]
|
||||
task_return_address = tasklist[active_task]
|
||||
if task_return_address!=0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -128,9 +125,8 @@ resume_with_next_task:
|
||||
}
|
||||
}
|
||||
|
||||
sub kill(ubyte taskid) {
|
||||
sub kill(ubyte @nozp taskid) {
|
||||
tasklist[taskid] = 0
|
||||
returnaddresses[taskid] = 0
|
||||
}
|
||||
|
||||
sub current() -> ubyte {
|
||||
@ -138,12 +134,10 @@ resume_with_next_task:
|
||||
}
|
||||
|
||||
sub termination() {
|
||||
; a task has terminated. wipe it from the list.
|
||||
; this is an internal routine
|
||||
; internal routine: a task has terminated. wipe it from the list.
|
||||
kill(active_task)
|
||||
; reactivate this termination handler
|
||||
; note: cannot use pushw() because JSR doesn't push the return address in the same way
|
||||
; reactivate this termination handler and go to the next task
|
||||
sys.push_returnaddress(&termination)
|
||||
goto coroutines.yield.resume_with_next_task
|
||||
goto coroutines.yield.skip_task
|
||||
}
|
||||
}
|
||||
|
@ -22,18 +22,23 @@ monogfx {
|
||||
const ubyte MODE_STIPPLE = %00000001
|
||||
const ubyte MODE_INVERT = %00000010
|
||||
|
||||
uword buffer_visible, buffer_back
|
||||
|
||||
|
||||
sub lores() {
|
||||
; enable 320*240 bitmap mode
|
||||
buffer_visible = buffer_back = $0000
|
||||
cx16.VERA_CTRL=0
|
||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||
cx16.VERA_DC_HSCALE = 64
|
||||
cx16.VERA_DC_VSCALE = 64
|
||||
cx16.VERA_L1_CONFIG = %00000100
|
||||
cx16.VERA_L1_MAPBASE = 0
|
||||
cx16.VERA_L1_TILEBASE = 0
|
||||
cx16.VERA_L1_TILEBASE = 0 ; lores
|
||||
width = 320
|
||||
height = 240
|
||||
lores_mode = true
|
||||
buffer_visible = buffer_back = $0000
|
||||
mode = MODE_NORMAL
|
||||
clear_screen(false)
|
||||
}
|
||||
@ -46,14 +51,40 @@ monogfx {
|
||||
cx16.VERA_DC_VSCALE = 128
|
||||
cx16.VERA_L1_CONFIG = %00000100
|
||||
cx16.VERA_L1_MAPBASE = 0
|
||||
cx16.VERA_L1_TILEBASE = %00000001
|
||||
cx16.VERA_L1_TILEBASE = %00000001 ; hires
|
||||
width = 640
|
||||
height = 480
|
||||
lores_mode = false
|
||||
buffer_visible = buffer_back = $0000
|
||||
mode = MODE_NORMAL
|
||||
clear_screen(false)
|
||||
}
|
||||
|
||||
sub enable_doublebuffer() {
|
||||
; enable double buffering mode
|
||||
if lores_mode {
|
||||
buffer_visible = $0000
|
||||
buffer_back = $2800
|
||||
} else {
|
||||
buffer_visible = $0000
|
||||
buffer_back = $9800
|
||||
}
|
||||
}
|
||||
|
||||
sub swap_buffers(bool wait_for_vsync) {
|
||||
; flip the buffers: make the back buffer visible and the other one now the backbuffer.
|
||||
; to avoid any screen tearing it is advised to call this during the vertical blank (pass true)
|
||||
if wait_for_vsync
|
||||
sys.waitvsync()
|
||||
cx16.r0 = buffer_back
|
||||
buffer_back = buffer_visible
|
||||
buffer_visible = cx16.r0
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.r0 &= %1111110000000000
|
||||
cx16.VERA_L1_TILEBASE = cx16.VERA_L1_TILEBASE & 1 | (cx16.r0H >>1 )
|
||||
}
|
||||
|
||||
|
||||
sub textmode() {
|
||||
; back to normal text mode
|
||||
cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode
|
||||
@ -559,6 +590,7 @@ drawmode: ora cx16.r15L
|
||||
sub disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) {
|
||||
; Warning: NO BOUNDS CHECKS. Make sure circle fits in the screen.
|
||||
; Midpoint algorithm, filled
|
||||
; Note: has problems with INVERT draw mode because of horizontal span overdrawing. Horizontal lines may occur.
|
||||
if radius==0
|
||||
return
|
||||
ubyte @zp yy = 0
|
||||
@ -597,6 +629,7 @@ drawmode: ora cx16.r15L
|
||||
sub safe_disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) {
|
||||
; Does bounds checking and clipping.
|
||||
; Midpoint algorithm, filled
|
||||
; Note: has problems with INVERT draw mode because of horizontal span overdrawing. Horizontal lines may occur.
|
||||
if radius==0
|
||||
return
|
||||
ubyte @zp yy = 0
|
||||
@ -672,8 +705,7 @@ nostipple:
|
||||
invert:
|
||||
prepare()
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
eor p8v_maskbits,y
|
||||
eor cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
}}
|
||||
return
|
||||
@ -696,7 +728,7 @@ invert:
|
||||
adc p8v_times40_lsb,y
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda p8v_times40_msb,y
|
||||
adc #0
|
||||
adc p8v_buffer_back+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
|
||||
lda p8v_xx
|
||||
@ -708,18 +740,29 @@ invert:
|
||||
; width=640 (hires)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda p8v_xx
|
||||
and #7
|
||||
pha ; xbits
|
||||
|
||||
; xx /= 8
|
||||
lsr p8v_xx+1
|
||||
ror p8v_xx
|
||||
lsr p8v_xx+1
|
||||
ror p8v_xx
|
||||
lsr p8v_xx
|
||||
}}
|
||||
xx /= 8
|
||||
;xx /= 8
|
||||
xx += yy*(640/8)
|
||||
%asm {{
|
||||
lda p8v_xx+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda p8v_xx
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda p8v_xx+1
|
||||
clc
|
||||
adc p8v_buffer_back+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda #0
|
||||
rol a ; hi bit carry also needed when double-buffering
|
||||
sta cx16.VERA_ADDR_H
|
||||
plx ; xbits
|
||||
lda p8v_maskbits,x
|
||||
}}
|
||||
@ -761,11 +804,15 @@ invert:
|
||||
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda p8v_xx+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda p8v_xx
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda p8v_xx+1
|
||||
clc
|
||||
adc p8v_buffer_back+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda #0
|
||||
rol a ; hi bit carry also needed when double-buffering
|
||||
sta cx16.VERA_ADDR_H
|
||||
ply ; xbits
|
||||
lda p8s_plot.p8v_maskbits,y
|
||||
and cx16.VERA_DATA0
|
||||
@ -848,8 +895,8 @@ skip:
|
||||
}
|
||||
|
||||
sub fill_scanline_right() {
|
||||
; TODO maybe this could use vera auto increment, but that requires some clever masking calculations
|
||||
cx16.r9s = xx
|
||||
; TODO maybe this could use vera auto increment, but that requires some clever masking calculations
|
||||
cx16.r9s = xx
|
||||
while xx <= width-1 {
|
||||
if pgetset()
|
||||
break
|
||||
@ -884,11 +931,15 @@ skip:
|
||||
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda p8v_xpos+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda p8v_xpos
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda p8v_xpos+1
|
||||
clc
|
||||
adc p8v_buffer_back+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda #0
|
||||
rol a ; hi bit carry also needed when double-buffering
|
||||
sta cx16.VERA_ADDR_H
|
||||
ply ; xbits
|
||||
lda p8s_plot.p8v_maskbits,y
|
||||
and cx16.VERA_DATA0
|
||||
@ -935,12 +986,12 @@ _doplot beq +
|
||||
ror a
|
||||
lsr a
|
||||
lsr a
|
||||
clc
|
||||
ldy p8v_yy
|
||||
clc
|
||||
adc p8v_times40_lsb,y
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda p8v_times40_msb,y
|
||||
adc #0
|
||||
adc p8v_buffer_back+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda #%00010000 ; autoincr
|
||||
sta cx16.VERA_ADDR_H
|
||||
@ -948,7 +999,33 @@ _doplot beq +
|
||||
}
|
||||
else {
|
||||
cx16.r0 = yy*(640/8)
|
||||
cx16.vaddr(0, cx16.r0+(xx/8), 0, 1)
|
||||
;cx16.r0 += xx/8
|
||||
%asm {{
|
||||
ldy p8v_xx+1
|
||||
lda p8v_xx
|
||||
sty P8ZP_SCRATCH_B1
|
||||
lsr P8ZP_SCRATCH_B1
|
||||
ror a
|
||||
lsr P8ZP_SCRATCH_B1
|
||||
ror a
|
||||
lsr a
|
||||
clc
|
||||
adc cx16.r0
|
||||
sta cx16.r0
|
||||
bcc +
|
||||
inc cx16.r0+1
|
||||
+
|
||||
stz cx16.VERA_CTRL
|
||||
lda cx16.r0L
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.r0H
|
||||
clc
|
||||
adc p8v_buffer_back+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda #%00001000 ; autoincr (1 bit shifted)
|
||||
rol a ; hi bit carry also needed when double-buffering
|
||||
sta cx16.VERA_ADDR_H
|
||||
}}
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -1116,15 +1193,11 @@ cdraw_mod2 ora cx16.VERA_DATA1
|
||||
cmp #0
|
||||
beq +
|
||||
lda #255
|
||||
+ ldy #80
|
||||
- sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
+ ldy #40
|
||||
-
|
||||
.rept 16
|
||||
sta cx16.VERA_DATA0
|
||||
.endrept
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
|
@ -177,6 +177,7 @@ cx16 {
|
||||
&uword r14 = $001e
|
||||
&uword r15 = $0020
|
||||
|
||||
; signed word versions
|
||||
&word r0s = $0002
|
||||
&word r1s = $0004
|
||||
&word r2s = $0006
|
||||
@ -194,6 +195,7 @@ cx16 {
|
||||
&word r14s = $001e
|
||||
&word r15s = $0020
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $0002
|
||||
&ubyte r1L = $0004
|
||||
&ubyte r2L = $0006
|
||||
@ -228,6 +230,7 @@ cx16 {
|
||||
&ubyte r14H = $001f
|
||||
&ubyte r15H = $0021
|
||||
|
||||
; signed byte versions (low and high bytes)
|
||||
&byte r0sL = $0002
|
||||
&byte r1sL = $0004
|
||||
&byte r2sL = $0006
|
||||
@ -262,6 +265,42 @@ cx16 {
|
||||
&byte r14sH = $001f
|
||||
&byte r15sH = $0021
|
||||
|
||||
; boolean versions
|
||||
&bool r0bL = $0002
|
||||
&bool r1bL = $0004
|
||||
&bool r2bL = $0006
|
||||
&bool r3bL = $0008
|
||||
&bool r4bL = $000a
|
||||
&bool r5bL = $000c
|
||||
&bool r6bL = $000e
|
||||
&bool r7bL = $0010
|
||||
&bool r8bL = $0012
|
||||
&bool r9bL = $0014
|
||||
&bool r10bL = $0016
|
||||
&bool r11bL = $0018
|
||||
&bool r12bL = $001a
|
||||
&bool r13bL = $001c
|
||||
&bool r14bL = $001e
|
||||
&bool r15bL = $0020
|
||||
|
||||
&bool r0bH = $0003
|
||||
&bool r1bH = $0005
|
||||
&bool r2bH = $0007
|
||||
&bool r3bH = $0009
|
||||
&bool r4bH = $000b
|
||||
&bool r5bH = $000d
|
||||
&bool r6bH = $000f
|
||||
&bool r7bH = $0011
|
||||
&bool r8bH = $0013
|
||||
&bool r9bH = $0015
|
||||
&bool r10bH = $0017
|
||||
&bool r11bH = $0019
|
||||
&bool r12bH = $001b
|
||||
&bool r13bH = $001d
|
||||
&bool r14bH = $001f
|
||||
&bool r15bH = $0021
|
||||
|
||||
|
||||
; VERA registers
|
||||
|
||||
const uword VERA_BASE = $9F20
|
||||
@ -474,7 +513,7 @@ extsub $ff68 = mouse_config(byte shape @A, ubyte resX @X, ubyte resY @Y) clobbe
|
||||
extsub $ff6b = mouse_get(ubyte zdataptr @X) -> ubyte @A, byte @X ; use mouse_pos() instead
|
||||
extsub $ff71 = mouse_scan() clobbers(A, X, Y)
|
||||
extsub $ff53 = joystick_scan() clobbers(A, X, Y)
|
||||
extsub $ff56 = joystick_get(ubyte joynr @A) -> uword @AX, bool @Y ; note: everything is inverted
|
||||
extsub $ff56 = joystick_get(ubyte joynr @A) -> uword @AX, bool @Y ; note: everything is inverted even the boolean present flag. Also see detect_joysticks() and get_all_joysticks()
|
||||
|
||||
; X16Edit (rom bank 13/14 but you ideally should use the routine search_x16edit() to search for the correct bank)
|
||||
extsub $C000 = x16edit_default() clobbers(A,X,Y)
|
||||
@ -1168,8 +1207,39 @@ asmsub restore_vera_context() clobbers(A) {
|
||||
}}
|
||||
}
|
||||
|
||||
sub joysticks_detect() -> ubyte {
|
||||
; returns bits 0-4, set to 1 if that joystick is present.
|
||||
; bit 0 = keyboard joystick, bit 1 - 4 = joypads 1 to 4
|
||||
cx16.r0H = 255
|
||||
for cx16.r0L in 4 downto 0 {
|
||||
void cx16.joystick_get(cx16.r0L)
|
||||
%asm {{
|
||||
cpy #1 ; present?
|
||||
}}
|
||||
rol(cx16.r0H)
|
||||
}
|
||||
return ~cx16.r0H
|
||||
}
|
||||
|
||||
; Commander X16 IRQ dispatcher routines
|
||||
sub joysticks_getall(bool also_keyboard_js) -> uword {
|
||||
; returns combined pressed buttons from all connected joysticks
|
||||
; note: returns the 'normal' not inverted status bits for the buttons (1 = button pressed.)
|
||||
cx16.r0H = 1
|
||||
if also_keyboard_js
|
||||
cx16.r0H = 0
|
||||
cx16.r1 = $ffff
|
||||
for cx16.r0L in cx16.r0H to 4 {
|
||||
bool notpresent
|
||||
cx16.r2, notpresent = cx16.joystick_get(cx16.r0L)
|
||||
if not notpresent {
|
||||
cx16.r1 &= cx16.r2
|
||||
}
|
||||
}
|
||||
return ~cx16.r1
|
||||
}
|
||||
|
||||
|
||||
; Commander X16 IRQ dispatcher routines
|
||||
|
||||
inline asmsub disable_irqs() clobbers(A) {
|
||||
; Disable all Vera IRQ sources. Note that it does NOT set the CPU IRQ disabled status bit!
|
||||
@ -1216,6 +1286,7 @@ _vsync_vec .word ?
|
||||
_line_vec .word ?
|
||||
_aflow_vec .word ?
|
||||
_sprcol_vec .word ?
|
||||
_continue_with_system_handler .byte ?
|
||||
.send BSS
|
||||
|
||||
_irq_dispatcher
|
||||
@ -1224,44 +1295,42 @@ _irq_dispatcher
|
||||
cld
|
||||
lda cx16.VERA_ISR
|
||||
and cx16.VERA_IEN ; only consider the bits for sources that can actually raise the IRQ
|
||||
sta cx16.VERA_ISR ; note: AFLOW can only be cleared by filling the audio FIFO for at least 1/4. Not via the ISR bit.
|
||||
|
||||
bit #2
|
||||
stz _continue_with_system_handler
|
||||
|
||||
bit #2 ; make sure to test for LINE IRQ first to handle that as soon as we can
|
||||
beq +
|
||||
pha
|
||||
jsr _line_handler
|
||||
ldy #2
|
||||
sty cx16.VERA_ISR
|
||||
bra _dispatch_end
|
||||
+
|
||||
bit #4
|
||||
beq +
|
||||
jsr _sprcol_handler
|
||||
ldy #4
|
||||
sty cx16.VERA_ISR
|
||||
bra _dispatch_end
|
||||
+
|
||||
bit #8
|
||||
beq +
|
||||
jsr _aflow_handler
|
||||
; note: AFLOW can only be cleared by filling the audio FIFO for at least 1/4. Not via the ISR bit.
|
||||
bra _dispatch_end
|
||||
+
|
||||
bit #1
|
||||
beq +
|
||||
tsb _continue_with_system_handler
|
||||
pla
|
||||
|
||||
+ lsr a
|
||||
bcc +
|
||||
pha
|
||||
jsr _vsync_handler
|
||||
cmp #0
|
||||
bne _dispatch_end
|
||||
ldy #1
|
||||
sty cx16.VERA_ISR
|
||||
bra _return_irq
|
||||
+
|
||||
lda #0
|
||||
_dispatch_end
|
||||
cmp #0
|
||||
beq _return_irq
|
||||
jsr sys.restore_prog8_internals
|
||||
tsb _continue_with_system_handler
|
||||
pla
|
||||
|
||||
+ lsr a
|
||||
lsr a
|
||||
bcc +
|
||||
pha
|
||||
jsr _sprcol_handler
|
||||
tsb _continue_with_system_handler
|
||||
pla
|
||||
|
||||
+ lsr a
|
||||
bcc +
|
||||
jsr _aflow_handler
|
||||
tsb _continue_with_system_handler
|
||||
|
||||
+ jsr sys.restore_prog8_internals
|
||||
lda _continue_with_system_handler
|
||||
beq _no_sys_handler
|
||||
jmp (sys.restore_irq._orig_irqvec) ; continue with normal kernal irq routine
|
||||
_return_irq
|
||||
jsr sys.restore_prog8_internals
|
||||
_no_sys_handler
|
||||
ply
|
||||
plx
|
||||
pla
|
||||
@ -1485,12 +1554,13 @@ sys {
|
||||
|
||||
const ubyte target = 16 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
|
||||
|
||||
const ubyte SIZEOF_BOOL = 1
|
||||
const ubyte SIZEOF_BYTE = 1
|
||||
const ubyte SIZEOF_UBYTE = 1
|
||||
const ubyte SIZEOF_WORD = 2
|
||||
const ubyte SIZEOF_UWORD = 2
|
||||
const ubyte SIZEOF_FLOAT = 5
|
||||
const ubyte SIZEOF_BOOL = sizeof(bool)
|
||||
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@ -1997,6 +2067,18 @@ save_SCRATCH_ZPWORD2 .word ?
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
|
||||
%asm {{
|
||||
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
@ -241,6 +241,28 @@ divmod_ub_asm .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
remainder_ub_asm .proc
|
||||
; -- divide A by Y, returns remainder in A (unsigned)
|
||||
; division by zero will result in just the original number.
|
||||
; This routine specialcases 0,1,2 and otherwise is just a repeated subtraction.
|
||||
cpy #0
|
||||
beq _zero
|
||||
cpy #1
|
||||
bne +
|
||||
lda #0
|
||||
rts
|
||||
+ cpy #2
|
||||
bne +
|
||||
and #1
|
||||
rts
|
||||
+ sty P8ZP_SCRATCH_REG
|
||||
sec
|
||||
- sbc P8ZP_SCRATCH_REG
|
||||
bcs -
|
||||
adc P8ZP_SCRATCH_REG
|
||||
_zero rts
|
||||
.pend
|
||||
|
||||
divmod_w_asm .proc
|
||||
; signed word division: make everything positive and fix sign afterwards
|
||||
sta P8ZP_SCRATCH_W2
|
||||
|
@ -697,25 +697,36 @@ log2_tab
|
||||
; Linear interpolation (LERP)
|
||||
; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 255]
|
||||
; guarantees v = v1 when t = 255
|
||||
return v0 + msb(t as uword * (v1 - v0) + 255)
|
||||
if v1<v0
|
||||
return v0 - msb(t as uword * (v0 - v1) + 255)
|
||||
else
|
||||
return v0 + msb(t as uword * (v1 - v0) + 255)
|
||||
}
|
||||
|
||||
sub lerpw(uword v0, uword v1, uword t) -> uword {
|
||||
; Linear interpolation (LERP) on word values
|
||||
; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 65535]
|
||||
; guarantees v = v1 when t = 65535
|
||||
; guarantees v = v1 when t = 65535. Clobbers R15.
|
||||
if v1<v0 {
|
||||
t *= v0-v1
|
||||
cx16.r15 = math.mul16_last_upper()
|
||||
if t!=0
|
||||
cx16.r15++
|
||||
return v0 - cx16.r15
|
||||
}
|
||||
t *= v1-v0
|
||||
cx16.r0 = math.mul16_last_upper()
|
||||
cx16.r15 = math.mul16_last_upper()
|
||||
if t!=0
|
||||
cx16.r0++
|
||||
return v0 + cx16.r0
|
||||
cx16.r15++
|
||||
return v0 + cx16.r15
|
||||
}
|
||||
|
||||
sub interpolate(ubyte v, ubyte inputMin, ubyte inputMax, ubyte outputMin, ubyte outputMax) -> ubyte {
|
||||
; Interpolate a value v in interval [inputMin, inputMax] to output interval [outputMin, outputMax]
|
||||
; There is no version for words because of lack of precision in the fixed point calculation there.
|
||||
cx16.r0 = ((v-inputMin)*256+inputMax) / (inputMax-inputMin)
|
||||
cx16.r0 *= (outputMax-outputMin)
|
||||
return cx16.r0H + outputMin
|
||||
; Clobbers R15.
|
||||
; (There is no version for words because of lack of precision in the fixed point calculation there)
|
||||
cx16.r15 = ((v-inputMin)*256+inputMax) / (inputMax-inputMin)
|
||||
cx16.r15 *= (outputMax-outputMin)
|
||||
return cx16.r15H + outputMin
|
||||
}
|
||||
}
|
||||
|
@ -98,12 +98,13 @@ sys {
|
||||
|
||||
const ubyte target = 32 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
|
||||
|
||||
const ubyte SIZEOF_BOOL = 1
|
||||
const ubyte SIZEOF_BYTE = 1
|
||||
const ubyte SIZEOF_UBYTE = 1
|
||||
const ubyte SIZEOF_WORD = 2
|
||||
const ubyte SIZEOF_UWORD = 2
|
||||
const ubyte SIZEOF_FLOAT = 0 ; undefined, no floats supported
|
||||
const ubyte SIZEOF_BOOL = sizeof(bool)
|
||||
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@ -481,6 +482,18 @@ save_SCRATCH_ZPWORD2 .word ?
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
|
||||
%asm {{
|
||||
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
@ -519,6 +532,7 @@ cx16 {
|
||||
&uword r14 = $7ffc
|
||||
&uword r15 = $7ffe
|
||||
|
||||
; signed word versions
|
||||
&word r0s = $7fe0
|
||||
&word r1s = $7fe2
|
||||
&word r2s = $7fe4
|
||||
@ -536,6 +550,7 @@ cx16 {
|
||||
&word r14s = $7ffc
|
||||
&word r15s = $7ffe
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $7fe0
|
||||
&ubyte r1L = $7fe2
|
||||
&ubyte r2L = $7fe4
|
||||
@ -570,6 +585,7 @@ cx16 {
|
||||
&ubyte r14H = $7ffd
|
||||
&ubyte r15H = $7fff
|
||||
|
||||
; signed byte versions (low and high bytes)
|
||||
&byte r0sL = $7fe0
|
||||
&byte r1sL = $7fe2
|
||||
&byte r2sL = $7fe4
|
||||
@ -604,6 +620,42 @@ cx16 {
|
||||
&byte r14sH = $7ffd
|
||||
&byte r15sH = $7fff
|
||||
|
||||
; boolean versions
|
||||
&bool r0bL = $7fe0
|
||||
&bool r1bL = $7fe2
|
||||
&bool r2bL = $7fe4
|
||||
&bool r3bL = $7fe6
|
||||
&bool r4bL = $7fe8
|
||||
&bool r5bL = $7fea
|
||||
&bool r6bL = $7fec
|
||||
&bool r7bL = $7fee
|
||||
&bool r8bL = $7ff0
|
||||
&bool r9bL = $7ff2
|
||||
&bool r10bL = $7ff4
|
||||
&bool r11bL = $7ff6
|
||||
&bool r12bL = $7ff8
|
||||
&bool r13bL = $7ffa
|
||||
&bool r14bL = $7ffc
|
||||
&bool r15bL = $7ffe
|
||||
|
||||
&bool r0bH = $7fe1
|
||||
&bool r1bH = $7fe3
|
||||
&bool r2bH = $7fe5
|
||||
&bool r3bH = $7fe7
|
||||
&bool r4bH = $7fe9
|
||||
&bool r5bH = $7feb
|
||||
&bool r6bH = $7fed
|
||||
&bool r7bH = $7fef
|
||||
&bool r8bH = $7ff1
|
||||
&bool r9bH = $7ff3
|
||||
&bool r10bH = $7ff5
|
||||
&bool r11bH = $7ff7
|
||||
&bool r12bH = $7ff9
|
||||
&bool r13bH = $7ffb
|
||||
&bool r14bH = $7ffd
|
||||
&bool r15bH = $7fff
|
||||
|
||||
|
||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
%asm {{
|
||||
ldy #31
|
||||
|
@ -43,46 +43,72 @@ _done
|
||||
}}
|
||||
}
|
||||
|
||||
/*
|
||||
prog8 source code for the above routine:
|
||||
|
||||
sub gnomesort_ub(uword @requirezp values, ubyte num_elements) {
|
||||
sub gnomesort_by_ub(uword @requirezp uw_keys, uword values, ubyte num_elements) {
|
||||
; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse).
|
||||
ubyte @zp pos=1
|
||||
while pos != num_elements {
|
||||
if values[pos]>=values[pos-1]
|
||||
if uw_keys[pos]>=uw_keys[pos-1]
|
||||
pos++
|
||||
else {
|
||||
; swap elements
|
||||
cx16.r0L = values[pos-1]
|
||||
values[pos-1] = values[pos]
|
||||
values[pos] = cx16.r0L
|
||||
cx16.r0L = uw_keys[pos-1]
|
||||
uw_keys[pos-1] = uw_keys[pos]
|
||||
uw_keys[pos] = cx16.r0L
|
||||
uword @requirezp vptr = values + pos*$0002 -2
|
||||
cx16.r0 = peekw(vptr)
|
||||
pokew(vptr, peekw(vptr+2))
|
||||
pokew(vptr+2, cx16.r0)
|
||||
|
||||
pos--
|
||||
if_z
|
||||
pos++
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
sub gnomesort_uw(uword values, ubyte num_elements) {
|
||||
; TODO optimize this more, rewrite in asm?
|
||||
ubyte @zp pos = 1
|
||||
uword @requirezp ptr = values+2
|
||||
sub gnomesort_uw(uword @requirezp values, ubyte num_elements) {
|
||||
; Sorts the values array (no-split unsigned words).
|
||||
; Max number of elements is 128. Clobbers R0 and R1.
|
||||
ubyte @zp pos=2
|
||||
num_elements *= 2
|
||||
while pos != num_elements {
|
||||
cx16.r0 = peekw(ptr-2)
|
||||
cx16.r1 = peekw(ptr)
|
||||
if cx16.r0<=cx16.r1 {
|
||||
pos++
|
||||
ptr+=2
|
||||
}
|
||||
cx16.r1L = pos-2
|
||||
if peekw(values+pos) >= peekw(values + cx16.r1L)
|
||||
pos += 2
|
||||
else {
|
||||
; swap elements
|
||||
pokew(ptr-2, cx16.r1)
|
||||
pokew(ptr, cx16.r0)
|
||||
if pos>1 {
|
||||
pos--
|
||||
ptr-=2
|
||||
}
|
||||
cx16.r0 = peekw(values + cx16.r1L)
|
||||
pokew(values + cx16.r1L, peekw(values + pos))
|
||||
pokew(values + pos, cx16.r0)
|
||||
pos-=2
|
||||
if_z
|
||||
pos+=2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub gnomesort_by_uw(uword @requirezp uw_keys, uword wordvalues, ubyte num_elements) {
|
||||
; Sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse).
|
||||
; both arrays should be no-split array of words. uw_keys are unsigned.
|
||||
; Max number of elements is 128. Clobbers R0 and R1.
|
||||
ubyte @zp pos=2
|
||||
num_elements *= 2
|
||||
while pos != num_elements {
|
||||
cx16.r1L = pos-2
|
||||
if peekw(uw_keys+pos) >= peekw(uw_keys + cx16.r1L)
|
||||
pos += 2
|
||||
else {
|
||||
; swap elements
|
||||
cx16.r0 = peekw(uw_keys + cx16.r1L)
|
||||
pokew(uw_keys + cx16.r1L, peekw(uw_keys+ pos))
|
||||
pokew(uw_keys + pos, cx16.r0)
|
||||
cx16.r0 = peekw(wordvalues + cx16.r1L)
|
||||
pokew(wordvalues + cx16.r1L, peekw(wordvalues + pos))
|
||||
pokew(wordvalues + pos, cx16.r0)
|
||||
|
||||
pos-=2
|
||||
if_z
|
||||
pos+=2
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -90,6 +116,7 @@ _done
|
||||
; gnomesort_pointers is not worth it over shellshort_pointers.
|
||||
|
||||
sub shellsort_ub(uword @requirezp values, ubyte num_elements) {
|
||||
; sorts the values array (unsigned bytes).
|
||||
num_elements--
|
||||
ubyte @zp gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
@ -112,6 +139,7 @@ _done
|
||||
}
|
||||
|
||||
sub shellsort_uw(uword @requirezp values, ubyte num_elements) {
|
||||
; sorts the values array (no-split unsigned words).
|
||||
num_elements--
|
||||
ubyte gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
@ -121,13 +149,65 @@ _done
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
while j>=gap {
|
||||
uword @zp v = peekw(values+k*2)
|
||||
uword @zp v = peekw(values+k*$0002)
|
||||
if v <= temp break
|
||||
pokew(values+j*2, v)
|
||||
pokew(values+j*$0002, v)
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
pokew(values+j*2, temp)
|
||||
pokew(values+j*$0002, temp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub shellsort_by_ub(uword @requirezp ub_keys, uword @requirezp wordvalues, ubyte num_elements) {
|
||||
; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse).
|
||||
num_elements--
|
||||
ubyte @zp gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
ubyte i
|
||||
for i in gap to num_elements {
|
||||
ubyte @zp temp = ub_keys[i]
|
||||
uword temp_wv = peekw(wordvalues + i*$0002)
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
repeat {
|
||||
ubyte @zp v = ub_keys[k]
|
||||
if v <= temp break
|
||||
if j < gap break
|
||||
ub_keys[j] = v
|
||||
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
ub_keys[j] = temp
|
||||
pokew(wordvalues + j*$0002, temp_wv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub shellsort_by_uw(uword @requirezp uw_keys, uword @requirezp wordvalues, ubyte num_elements) {
|
||||
; sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse).
|
||||
; both arrays should be no-split array of words. uw_keys are unsigned.
|
||||
num_elements--
|
||||
ubyte gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
ubyte i
|
||||
for i in gap to num_elements {
|
||||
uword @zp temp = peekw(uw_keys+i*$0002)
|
||||
uword temp_wv = peekw(wordvalues + i*$0002)
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
while j>=gap {
|
||||
uword @zp v = peekw(uw_keys+k*2)
|
||||
if v <= temp break
|
||||
pokew(uw_keys+j*2, v)
|
||||
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
pokew(uw_keys+j*2, temp)
|
||||
pokew(wordvalues + j*$0002, temp_wv)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -144,14 +224,14 @@ _done
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
while j>=gap {
|
||||
cx16.r0 = peekw(pointers+k*2)
|
||||
cx16.r0 = peekw(pointers+k*$0002)
|
||||
void call(comparefunc)
|
||||
if_cs break
|
||||
pokew(pointers+j*2, cx16.r0)
|
||||
pokew(pointers+j*$0002, cx16.r0)
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
pokew(pointers+j*2, cx16.r1)
|
||||
pokew(pointers+j*$0002, cx16.r1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -151,6 +151,33 @@ _found tya
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub find_eol(uword string @AY) -> ubyte @A, bool @Pc {
|
||||
; Locates the position of the first End Of Line character in the string.
|
||||
; This is a convenience function that looks for both a CR or LF (byte 13 or byte 10) as being a possible Line Ending.
|
||||
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
|
||||
%asm {{
|
||||
; need to copy the the cx16 virtual registers to zeropage to make this run on C64...
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
beq _notfound
|
||||
cmp #13
|
||||
beq _found
|
||||
cmp #10
|
||||
beq _found
|
||||
iny
|
||||
bne -
|
||||
_notfound lda #255
|
||||
clc
|
||||
rts
|
||||
_found tya
|
||||
sec
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub rfind(uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc {
|
||||
; Locates the first position of the given character in the string, starting from the right.
|
||||
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
|
||||
|
@ -404,25 +404,36 @@ math {
|
||||
; Linear interpolation (LERP)
|
||||
; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 255]
|
||||
; guarantees v = v1 when t = 255
|
||||
return v0 + msb(t as uword * (v1 - v0) + 255)
|
||||
if v1<v0
|
||||
return v0 - msb(t as uword * (v0 - v1) + 255)
|
||||
else
|
||||
return v0 + msb(t as uword * (v1 - v0) + 255)
|
||||
}
|
||||
|
||||
sub lerpw(uword v0, uword v1, uword t) -> uword {
|
||||
; Linear interpolation (LERP) on word values
|
||||
; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 65535]
|
||||
; guarantees v = v1 when t = 65535
|
||||
; guarantees v = v1 when t = 65535. Clobbers R15.
|
||||
if v1<v0 {
|
||||
t *= v0-v1
|
||||
cx16.r15 = math.mul16_last_upper()
|
||||
if t!=0
|
||||
cx16.r15++
|
||||
return v0 - cx16.r15
|
||||
}
|
||||
t *= v1-v0
|
||||
cx16.r0 = math.mul16_last_upper()
|
||||
cx16.r15 = math.mul16_last_upper()
|
||||
if t!=0
|
||||
cx16.r0++
|
||||
return v0 + cx16.r0
|
||||
cx16.r15++
|
||||
return v0 + cx16.r15
|
||||
}
|
||||
|
||||
sub interpolate(ubyte v, ubyte inputMin, ubyte inputMax, ubyte outputMin, ubyte outputMax) -> ubyte {
|
||||
; Interpolate a value v in interval [inputMin, inputMax] to output interval [outputMin, outputMax]
|
||||
; There is no version for words because of lack of precision in the fixed point calculation there.
|
||||
cx16.r0 = ((v-inputMin)*256+inputMax) / (inputMax-inputMin)
|
||||
cx16.r0 *= (outputMax-outputMin)
|
||||
return cx16.r0H + outputMin
|
||||
; Clobbers R15.
|
||||
; (There is no version for words because of lack of precision in the fixed point calculation there)
|
||||
cx16.r15 = ((v-inputMin)*256+inputMax) / (inputMax-inputMin)
|
||||
cx16.r15 *= (outputMax-outputMin)
|
||||
return cx16.r15H + outputMin
|
||||
}
|
||||
}
|
||||
|
103
compiler/res/prog8lib/virtual/sorting.p8
Normal file
103
compiler/res/prog8lib/virtual/sorting.p8
Normal file
@ -0,0 +1,103 @@
|
||||
; **experimental** data sorting routines, API subject to change!!
|
||||
|
||||
; NOTE: gnomesort is not implemented here, just use shellshort.
|
||||
|
||||
sorting {
|
||||
%option ignore_unused
|
||||
|
||||
sub shellsort_ub(uword @requirezp values, ubyte num_elements) {
|
||||
num_elements--
|
||||
ubyte @zp gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
ubyte i
|
||||
for i in gap to num_elements {
|
||||
ubyte @zp temp = values[i]
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
repeat {
|
||||
ubyte @zp v = values[k]
|
||||
if v <= temp break
|
||||
if j < gap break
|
||||
values[j] = v
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
values[j] = temp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub shellsort_uw(uword @requirezp values, ubyte num_elements) {
|
||||
num_elements--
|
||||
ubyte gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
ubyte i
|
||||
for i in gap to num_elements {
|
||||
uword @zp temp = peekw(values+i*$0002)
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
while j>=gap {
|
||||
uword @zp v = peekw(values+k*2)
|
||||
if v <= temp break
|
||||
pokew(values+j*2, v)
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
pokew(values+j*2, temp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub shellsort_by_ub(uword @requirezp ub_keys, uword @requirezp wordvalues, ubyte num_elements) {
|
||||
; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse).
|
||||
num_elements--
|
||||
ubyte @zp gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
ubyte i
|
||||
for i in gap to num_elements {
|
||||
ubyte @zp temp = ub_keys[i]
|
||||
uword temp_wv = peekw(wordvalues + i*$0002)
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
repeat {
|
||||
ubyte @zp v = ub_keys[k]
|
||||
if v <= temp break
|
||||
if j < gap break
|
||||
ub_keys[j] = v
|
||||
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
ub_keys[j] = temp
|
||||
pokew(wordvalues + j*$0002, temp_wv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub shellsort_by_uw(uword @requirezp uw_keys, uword @requirezp wordvalues, ubyte num_elements) {
|
||||
; sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse).
|
||||
; both arrays should be no-split array of words. uw_keys are unsigned.
|
||||
num_elements--
|
||||
ubyte gap
|
||||
for gap in [132, 57, 23, 10, 4, 1] {
|
||||
ubyte i
|
||||
for i in gap to num_elements {
|
||||
uword @zp temp = peekw(uw_keys+i*$0002)
|
||||
uword temp_wv = peekw(wordvalues + i*$0002)
|
||||
ubyte @zp j = i
|
||||
ubyte @zp k = j-gap
|
||||
while j>=gap {
|
||||
uword @zp v = peekw(uw_keys+k*2)
|
||||
if v <= temp break
|
||||
pokew(uw_keys+j*2, v)
|
||||
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
|
||||
j = k
|
||||
k -= gap
|
||||
}
|
||||
pokew(uw_keys+j*2, temp)
|
||||
pokew(wordvalues + j*$0002, temp_wv)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -53,34 +53,47 @@ strings {
|
||||
target[ix]=0
|
||||
}
|
||||
|
||||
sub find(str st, ubyte character) -> ubyte {
|
||||
sub find(str st, ubyte character) -> ubyte, bool {
|
||||
; Locates the first position of the given character in the string,
|
||||
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
|
||||
; NOTE: because this isn't an asmsub, there's only a SINGLE return value here. On the c64/cx16 targets etc there are 2 return values.
|
||||
; returns index in A, and boolean if found or not. (when false A will also be 255, an invalid index).
|
||||
ubyte ix
|
||||
for ix in 0 to length(st)-1 {
|
||||
if st[ix]==character {
|
||||
sys.set_carry()
|
||||
return ix
|
||||
return ix, true
|
||||
}
|
||||
}
|
||||
sys.clear_carry()
|
||||
return 255
|
||||
return 255, false
|
||||
}
|
||||
|
||||
sub rfind(uword stringptr, ubyte character) -> ubyte {
|
||||
sub find_eol(str st) -> ubyte, bool {
|
||||
; Locates the position of the first End Of Line character in the string.
|
||||
; This is a convenience function that looks for both a CR or LF (byte 13 or byte 10) as being a possible Line Ending.
|
||||
; returns index in A, and boolean if found or not. (when false A will also be 255, an invalid index).
|
||||
ubyte ix
|
||||
for ix in 0 to length(st)-1 {
|
||||
if st[ix] in "\x0a\x0d" {
|
||||
sys.set_carry()
|
||||
return ix, true
|
||||
}
|
||||
}
|
||||
sys.clear_carry()
|
||||
return 255, false
|
||||
}
|
||||
|
||||
sub rfind(uword stringptr, ubyte character) -> ubyte, bool {
|
||||
; Locates the first position of the given character in the string, starting from the right.
|
||||
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
|
||||
; NOTE: because this isn't an asmsub, there's only a SINGLE return value here. On the c64/cx16 targets etc there are 2 return values.
|
||||
; returns index in A, and boolean if found or not. (when false A will also be 255, an invalid index).
|
||||
ubyte ix
|
||||
for ix in length(stringptr)-1 downto 0 {
|
||||
if stringptr[ix]==character {
|
||||
sys.set_carry()
|
||||
return ix
|
||||
return ix, true
|
||||
}
|
||||
}
|
||||
sys.clear_carry()
|
||||
return 255
|
||||
return 255, false
|
||||
}
|
||||
|
||||
sub contains(str st, ubyte character) -> bool {
|
||||
|
@ -7,12 +7,13 @@ sys {
|
||||
|
||||
const ubyte target = 255 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
|
||||
|
||||
const ubyte SIZEOF_BOOL = 1
|
||||
const ubyte SIZEOF_BYTE = 1
|
||||
const ubyte SIZEOF_UBYTE = 1
|
||||
const ubyte SIZEOF_WORD = 2
|
||||
const ubyte SIZEOF_UWORD = 2
|
||||
const ubyte SIZEOF_FLOAT = 8
|
||||
const ubyte SIZEOF_BOOL = sizeof(bool)
|
||||
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_FLOAT = sizeof(float)
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@ -198,6 +199,12 @@ sys {
|
||||
}}
|
||||
}
|
||||
|
||||
sub get_as_returnaddress(uword address) -> uword {
|
||||
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
|
||||
address--
|
||||
return mkword(lsb(address), msb(address))
|
||||
}
|
||||
|
||||
sub pop() -> ubyte {
|
||||
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
|
||||
%ir {{
|
||||
@ -231,9 +238,8 @@ sys {
|
||||
|
||||
if_cs
|
||||
cx16.r0L |= 1
|
||||
; TODO: overflow flag not yet supported
|
||||
; if_vs
|
||||
; cx16.r0L |= %01000000
|
||||
if_vs
|
||||
cx16.r0L |= %01000000
|
||||
|
||||
return cx16.r0L
|
||||
}
|
||||
@ -260,6 +266,7 @@ cx16 {
|
||||
&uword r14 = $ff1e
|
||||
&uword r15 = $ff20
|
||||
|
||||
; signed word versions
|
||||
&word r0s = $ff02
|
||||
&word r1s = $ff04
|
||||
&word r2s = $ff06
|
||||
@ -277,6 +284,7 @@ cx16 {
|
||||
&word r14s = $ff1e
|
||||
&word r15s = $ff20
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $ff02
|
||||
&ubyte r1L = $ff04
|
||||
&ubyte r2L = $ff06
|
||||
@ -311,6 +319,7 @@ cx16 {
|
||||
&ubyte r14H = $ff1f
|
||||
&ubyte r15H = $ff21
|
||||
|
||||
; signed byte versions (low and high bytes)
|
||||
&byte r0sL = $ff02
|
||||
&byte r1sL = $ff04
|
||||
&byte r2sL = $ff06
|
||||
@ -345,6 +354,42 @@ cx16 {
|
||||
&byte r14sH = $ff1f
|
||||
&byte r15sH = $ff21
|
||||
|
||||
; boolean versions
|
||||
&bool r0bL = $ff02
|
||||
&bool r1bL = $ff04
|
||||
&bool r2bL = $ff06
|
||||
&bool r3bL = $ff08
|
||||
&bool r4bL = $ff0a
|
||||
&bool r5bL = $ff0c
|
||||
&bool r6bL = $ff0e
|
||||
&bool r7bL = $ff10
|
||||
&bool r8bL = $ff12
|
||||
&bool r9bL = $ff14
|
||||
&bool r10bL = $ff16
|
||||
&bool r11bL = $ff18
|
||||
&bool r12bL = $ff1a
|
||||
&bool r13bL = $ff1c
|
||||
&bool r14bL = $ff1e
|
||||
&bool r15bL = $ff20
|
||||
|
||||
&bool r0bH = $ff03
|
||||
&bool r1bH = $ff05
|
||||
&bool r2bH = $ff07
|
||||
&bool r3bH = $ff09
|
||||
&bool r4bH = $ff0b
|
||||
&bool r5bH = $ff0d
|
||||
&bool r6bH = $ff0f
|
||||
&bool r7bH = $ff11
|
||||
&bool r8bH = $ff13
|
||||
&bool r9bH = $ff15
|
||||
&bool r10bH = $ff17
|
||||
&bool r11bH = $ff19
|
||||
&bool r12bH = $ff1b
|
||||
&bool r13bH = $ff1d
|
||||
&bool r14bH = $ff1f
|
||||
&bool r15bH = $ff21
|
||||
|
||||
|
||||
sub save_virtual_registers() {
|
||||
uword[32] storage
|
||||
storage[0] = r0
|
||||
|
@ -65,6 +65,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val dontSplitWordArrays by cli.option(ArgType.Boolean, fullName = "dontsplitarrays", description = "don't store any word array as split lsb/msb in memory, as if all of those have @nosplit")
|
||||
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of ${CompilationTargets.joinToString(",")} or a custom target properties file) (required)")
|
||||
val showTimings by cli.option(ArgType.Boolean, fullName = "timings", description = "show internal compiler timings (for performance analysis)")
|
||||
val varsGolden by cli.option(ArgType.Boolean, fullName = "varsgolden", description = "put uninitialized variables in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
|
||||
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
|
||||
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "run a .p8ir IR source file in the embedded VM")
|
||||
@ -181,6 +182,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
warnSymbolShadowing == true,
|
||||
quietAll == true,
|
||||
quietAll == true || quietAssembler == true,
|
||||
showTimings == true,
|
||||
asmListfile == true,
|
||||
dontIncludeSourcelines != true,
|
||||
experimentalCodegen == true,
|
||||
@ -265,6 +267,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
warnSymbolShadowing == true,
|
||||
quietAll == true,
|
||||
quietAll == true || quietAssembler == true,
|
||||
showTimings == true,
|
||||
asmListfile == true,
|
||||
dontIncludeSourcelines != true,
|
||||
experimentalCodegen == true,
|
||||
|
@ -115,6 +115,19 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
||||
else -> NumericLiteral(BaseDataType.UBYTE, program.memsizer.memorySize(dt.getOrUndef(), null).toDouble(), position)
|
||||
}
|
||||
} else {
|
||||
val identifier = args[0] as? IdentifierReference
|
||||
if(identifier?.nameInSource?.size==1) {
|
||||
when(identifier.nameInSource[0]) {
|
||||
"ubyte" -> return NumericLiteral.optimalInteger(program.memsizer.memorySize(BaseDataType.UBYTE), position)
|
||||
"byte" -> return NumericLiteral.optimalInteger(program.memsizer.memorySize(BaseDataType.BYTE), position)
|
||||
"uword" -> return NumericLiteral.optimalInteger(program.memsizer.memorySize(BaseDataType.UWORD), position)
|
||||
"word" -> return NumericLiteral.optimalInteger(program.memsizer.memorySize(BaseDataType.WORD), position)
|
||||
"long" -> return NumericLiteral.optimalInteger(program.memsizer.memorySize(BaseDataType.LONG), position)
|
||||
"float" -> return NumericLiteral.optimalInteger(program.memsizer.memorySize(BaseDataType.FLOAT), position)
|
||||
"bool" -> return NumericLiteral.optimalInteger(program.memsizer.memorySize(BaseDataType.BOOL), position)
|
||||
}
|
||||
}
|
||||
|
||||
throw SyntaxError("sizeof invalid argument type", position)
|
||||
}
|
||||
}
|
||||
|
@ -24,9 +24,11 @@ import java.nio.file.Path
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.isRegularFile
|
||||
import kotlin.io.path.nameWithoutExtension
|
||||
import kotlin.math.round
|
||||
import kotlin.system.exitProcess
|
||||
import kotlin.system.measureTimeMillis
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.DurationUnit
|
||||
import kotlin.time.measureTime
|
||||
import kotlin.time.measureTimedValue
|
||||
|
||||
|
||||
class CompilationResult(val compilerAst: Program, // deprecated, use codegenAst instead
|
||||
@ -40,6 +42,7 @@ class CompilerArguments(val filepath: Path,
|
||||
val warnSymbolShadowing: Boolean,
|
||||
val quietAll: Boolean,
|
||||
val quietAssembler: Boolean,
|
||||
val showTimings: Boolean,
|
||||
val asmListfile: Boolean,
|
||||
val includeSourcelines: Boolean,
|
||||
val experimentalCodegen: Boolean,
|
||||
@ -83,9 +86,20 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
}
|
||||
|
||||
try {
|
||||
val totalTime = measureTimeMillis {
|
||||
val totalTime = measureTime {
|
||||
val libraryDirs = if(compTarget.libraryPath!=null) listOf(compTarget.libraryPath.toString()) else emptyList()
|
||||
val (program, options, imported) = parseMainModule(args.filepath, args.errors, compTarget, args.sourceDirs, libraryDirs, args.quietAll)
|
||||
val (parseresult, parseDuration) = measureTimedValue {
|
||||
parseMainModule(
|
||||
args.filepath,
|
||||
args.errors,
|
||||
compTarget,
|
||||
args.sourceDirs,
|
||||
libraryDirs,
|
||||
args.quietAll
|
||||
)
|
||||
}
|
||||
|
||||
val (program, options, imported) = parseresult
|
||||
compilationOptions = options
|
||||
|
||||
with(compilationOptions) {
|
||||
@ -120,83 +134,115 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
}
|
||||
|
||||
|
||||
processAst(program, args.errors, compilationOptions)
|
||||
val processDuration = measureTime {
|
||||
processAst(program, args.errors, compilationOptions)
|
||||
}
|
||||
|
||||
// println("*********** COMPILER AST RIGHT BEFORE OPTIMIZING *************")
|
||||
// printProgram(program)
|
||||
|
||||
if (compilationOptions.optimize) {
|
||||
optimizeAst(
|
||||
program,
|
||||
compilationOptions,
|
||||
args.errors,
|
||||
BuiltinFunctionsFacade(BuiltinFunctions),
|
||||
)
|
||||
val optimizeDuration = measureTime {
|
||||
if (compilationOptions.optimize) {
|
||||
optimizeAst(
|
||||
program,
|
||||
compilationOptions,
|
||||
args.errors,
|
||||
BuiltinFunctionsFacade(BuiltinFunctions),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
determineProgramLoadAddress(program, compilationOptions, args.errors)
|
||||
args.errors.report()
|
||||
postprocessAst(program, args.errors, compilationOptions)
|
||||
args.errors.report()
|
||||
val postprocessDuration = measureTime {
|
||||
determineProgramLoadAddress(program, compilationOptions, args.errors)
|
||||
args.errors.report()
|
||||
postprocessAst(program, args.errors, compilationOptions)
|
||||
args.errors.report()
|
||||
}
|
||||
|
||||
// println("*********** COMPILER AST BEFORE ASSEMBLYGEN *************")
|
||||
// printProgram(program)
|
||||
|
||||
var createAssemblyDuration = Duration.ZERO
|
||||
var simplifiedAstDuration = Duration.ZERO
|
||||
|
||||
if (args.writeAssembly) {
|
||||
|
||||
// re-initialize memory areas with final compilationOptions
|
||||
compilationOptions.compTarget.initializeMemoryAreas(compilationOptions)
|
||||
program.processAstBeforeAsmGeneration(compilationOptions, args.errors)
|
||||
args.errors.report()
|
||||
|
||||
if(args.printAst1) {
|
||||
if (args.printAst1) {
|
||||
println("\n*********** COMPILER AST *************")
|
||||
printProgram(program)
|
||||
println("*********** COMPILER AST END *************\n")
|
||||
}
|
||||
|
||||
val intermediateAst = SimplifiedAstMaker(program, args.errors).transform()
|
||||
val stMaker = SymbolTableMaker(intermediateAst, compilationOptions)
|
||||
val symbolTable = stMaker.make()
|
||||
val (intermediateAst, simplifiedAstDuration2) = measureTimedValue {
|
||||
val intermediateAst = SimplifiedAstMaker(program, args.errors).transform()
|
||||
val stMaker = SymbolTableMaker(intermediateAst, compilationOptions)
|
||||
val symbolTable = stMaker.make()
|
||||
|
||||
postprocessSimplifiedAst(intermediateAst, symbolTable, args.errors)
|
||||
args.errors.report()
|
||||
|
||||
if(compilationOptions.optimize) {
|
||||
optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable, args.errors)
|
||||
postprocessSimplifiedAst(intermediateAst, symbolTable, args.errors)
|
||||
args.errors.report()
|
||||
|
||||
if (compilationOptions.optimize) {
|
||||
optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable, args.errors)
|
||||
args.errors.report()
|
||||
}
|
||||
|
||||
if (args.printAst2) {
|
||||
println("\n*********** SIMPLIFIED AST *************")
|
||||
printAst(intermediateAst, true, ::println)
|
||||
println("*********** SIMPLIFIED AST END *************\n")
|
||||
}
|
||||
|
||||
verifyFinalAstBeforeAsmGen(intermediateAst, compilationOptions, symbolTable, args.errors)
|
||||
args.errors.report()
|
||||
intermediateAst
|
||||
}
|
||||
simplifiedAstDuration =simplifiedAstDuration2
|
||||
|
||||
if(args.printAst2) {
|
||||
println("\n*********** SIMPLIFIED AST *************")
|
||||
printAst(intermediateAst, true, ::println)
|
||||
println("*********** SIMPLIFIED AST END *************\n")
|
||||
}
|
||||
|
||||
verifyFinalAstBeforeAsmGen(intermediateAst, compilationOptions, symbolTable, args.errors)
|
||||
args.errors.report()
|
||||
|
||||
if(!createAssemblyAndAssemble(intermediateAst, args.errors, compilationOptions, program.generatedLabelSequenceNumber)) {
|
||||
System.err.println("Error in codegeneration or assembler")
|
||||
return null
|
||||
createAssemblyDuration = measureTime {
|
||||
if (!createAssemblyAndAssemble(
|
||||
intermediateAst,
|
||||
args.errors,
|
||||
compilationOptions,
|
||||
program.generatedLabelSequenceNumber
|
||||
)
|
||||
) {
|
||||
System.err.println("Error in codegeneration or assembler")
|
||||
return null
|
||||
}
|
||||
}
|
||||
ast = intermediateAst
|
||||
} else {
|
||||
if(args.printAst1) {
|
||||
if (args.printAst1) {
|
||||
println("\n*********** COMPILER AST *************")
|
||||
printProgram(program)
|
||||
println("*********** COMPILER AST END *************\n")
|
||||
}
|
||||
if(args.printAst2) {
|
||||
if (args.printAst2) {
|
||||
System.err.println("There is no simplified Ast available if assembly generation is disabled.")
|
||||
}
|
||||
}
|
||||
|
||||
System.out.flush()
|
||||
System.err.flush()
|
||||
|
||||
if(!args.quietAll && args.showTimings) {
|
||||
println("\n**** TIMINGS ****")
|
||||
println("source parsing : ${parseDuration.toString(DurationUnit.SECONDS, 3)}")
|
||||
println("ast processing : ${processDuration.toString(DurationUnit.SECONDS, 3)}")
|
||||
println("ast optimizing : ${optimizeDuration.toString(DurationUnit.SECONDS, 3)}")
|
||||
println("ast postprocess : ${postprocessDuration.toString(DurationUnit.SECONDS, 3)}")
|
||||
println("code prepare : ${simplifiedAstDuration.toString(DurationUnit.SECONDS, 3)}")
|
||||
println("code generation : ${createAssemblyDuration.toString(DurationUnit.SECONDS, 3)}")
|
||||
val totalDuration = parseDuration + processDuration + optimizeDuration + postprocessDuration + simplifiedAstDuration + createAssemblyDuration
|
||||
println(" total : ${totalDuration.toString(DurationUnit.SECONDS, 3)}")
|
||||
}
|
||||
}
|
||||
|
||||
System.out.flush()
|
||||
System.err.flush()
|
||||
if(!args.quietAll) {
|
||||
val seconds = totalTime / 1000.0
|
||||
println("\nTotal compilation+assemble time: ${round(seconds * 100.0) / 100.0} sec.")
|
||||
println("\nTotal compilation+assemble time: ${totalTime.toString(DurationUnit.SECONDS, 3)}.")
|
||||
}
|
||||
return CompilationResult(resultingProgram!!, ast, compilationOptions, importedFiles)
|
||||
} catch (px: ParseError) {
|
||||
@ -424,7 +470,7 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions
|
||||
errors.report()
|
||||
program.reorderStatements(errors)
|
||||
errors.report()
|
||||
program.desugaring(errors)
|
||||
program.desugaring(errors, compilerOptions)
|
||||
errors.report()
|
||||
program.changeNotExpressionAndIfComparisonExpr(errors, compilerOptions.compTarget)
|
||||
errors.report()
|
||||
@ -486,7 +532,7 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
|
||||
}
|
||||
|
||||
private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||
program.desugaring(errors)
|
||||
program.desugaring(errors, compilerOptions)
|
||||
program.addTypecasts(errors, compilerOptions)
|
||||
errors.report()
|
||||
program.variousCleanups(errors, compilerOptions)
|
||||
@ -495,8 +541,21 @@ private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOpt
|
||||
program.verifyFunctionArgTypes(errors, compilerOptions)
|
||||
errors.report()
|
||||
program.moveMainBlockAsFirst(compilerOptions.compTarget)
|
||||
|
||||
val fixer = BeforeAsmAstChanger(program, compilerOptions, errors)
|
||||
fixer.visit(program)
|
||||
while (errors.noErrors() && fixer.applyModifications() > 0) {
|
||||
fixer.visit(program)
|
||||
}
|
||||
|
||||
program.checkValid(errors, compilerOptions) // check if final tree is still valid
|
||||
errors.report()
|
||||
|
||||
val cleaner = BeforeAsmTypecastCleaner(program, errors)
|
||||
cleaner.visit(program)
|
||||
while (errors.noErrors() && cleaner.applyModifications() > 0) {
|
||||
cleaner.visit(program)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAssemblyAndAssemble(program: PtProgram,
|
||||
|
@ -5,7 +5,9 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C128Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.PETTarget
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.compiler.builtinFunctionReturnType
|
||||
import java.io.CharConversionException
|
||||
@ -182,8 +184,6 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
val iterableDt = forLoop.iterable.inferType(program).getOrUndef()
|
||||
|
||||
if(iterableDt.isNumeric) TODO("iterable type should not be simple numeric!? "+forLoop.position)
|
||||
|
||||
if(forLoop.iterable is IFunctionCall) {
|
||||
errors.err("can not loop over function call return value", forLoop.position)
|
||||
} else if(!(iterableDt.isIterable) && forLoop.iterable !is RangeExpression) {
|
||||
@ -196,7 +196,7 @@ internal class AstChecker(private val program: Program,
|
||||
require(loopvar.datatype.isNumericOrBool)
|
||||
when (loopvar.datatype.base) {
|
||||
BaseDataType.UBYTE -> {
|
||||
if(!iterableDt.isUnsignedByte && !iterableDt.isUnsignedByteArray && !iterableDt.isString) // TODO remove ubyte check?
|
||||
if(!iterableDt.isUnsignedByte && !iterableDt.isUnsignedByteArray && !iterableDt.isString)
|
||||
errors.err("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position)
|
||||
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression)
|
||||
}
|
||||
@ -205,7 +205,7 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("bool loop variable can only loop over boolean array", forLoop.position)
|
||||
}
|
||||
BaseDataType.UWORD -> {
|
||||
if(!iterableDt.isUnsignedByte && !iterableDt.isUnsignedWord && !iterableDt.isString && // TODO remove byte and word check?
|
||||
if(!iterableDt.isUnsignedByte && !iterableDt.isUnsignedWord && !iterableDt.isString &&
|
||||
!iterableDt.isUnsignedByteArray && !iterableDt.isUnsignedWordArray &&
|
||||
!iterableDt.isSplitWordArray)
|
||||
errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position)
|
||||
@ -213,7 +213,7 @@ internal class AstChecker(private val program: Program,
|
||||
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression)
|
||||
}
|
||||
BaseDataType.BYTE -> {
|
||||
if(!iterableDt.isSignedByte && !iterableDt.isSignedByteArray) // TODO remove byte check?
|
||||
if(!iterableDt.isSignedByte && !iterableDt.isSignedByteArray)
|
||||
errors.err("byte loop variable can only loop over bytes", forLoop.position)
|
||||
}
|
||||
BaseDataType.WORD -> {
|
||||
@ -406,11 +406,42 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
// subroutine must contain at least one 'return' or 'goto'
|
||||
// (or if it has an asm block, that must contain a 'rts' or 'jmp' or 'bra')
|
||||
var haveReturnError = false
|
||||
if(!hasReturnOrExternalJumpOrRts(subroutine)) {
|
||||
if (subroutine.returntypes.isNotEmpty()) {
|
||||
// for asm subroutines with an address, no statement check is possible.
|
||||
if (subroutine.asmAddress == null && !subroutine.inline)
|
||||
if (subroutine.asmAddress == null && !subroutine.inline) {
|
||||
err("non-inline subroutine has result value(s) and thus must have at least one 'return' or external 'goto' in it (or the assembler equivalent in case of %asm)")
|
||||
haveReturnError = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val lastStatement = subroutine.statements.reversed().dropWhile { it is Subroutine || it is VarDecl || it is Directive || (it is Assignment && it.origin== AssignmentOrigin.VARINIT) }.firstOrNull()
|
||||
if(!haveReturnError && !subroutine.isAsmSubroutine && !subroutine.inline && subroutine.returntypes.isNotEmpty() && lastStatement !is Return) {
|
||||
if(lastStatement==null)
|
||||
err("subroutine '${subroutine.name}' has result value(s) but doesn't end with a return statement")
|
||||
else {
|
||||
val returnError = when (lastStatement) {
|
||||
is Jump, is OnGoto -> false
|
||||
is IStatementContainer -> !hasReturnOrExternalJumpOrRts(lastStatement as IStatementContainer)
|
||||
is InlineAssembly -> !lastStatement.hasReturnOrRts()
|
||||
is ForLoop -> hasReturnOrExternalJumpOrRts(lastStatement.body)
|
||||
is IfElse -> !hasReturnOrExternalJumpOrRts(lastStatement.truepart) && !hasReturnOrExternalJumpOrRts(lastStatement.elsepart)
|
||||
is ConditionalBranch -> !hasReturnOrExternalJumpOrRts(lastStatement.truepart) && !hasReturnOrExternalJumpOrRts(lastStatement.elsepart)
|
||||
is RepeatLoop -> {
|
||||
lastStatement.iterations!=null || !hasReturnOrExternalJumpOrRts(lastStatement.body)
|
||||
}
|
||||
is UntilLoop -> !hasReturnOrExternalJumpOrRts(lastStatement.body)
|
||||
is WhileLoop -> !hasReturnOrExternalJumpOrRts(lastStatement.body)
|
||||
is When -> lastStatement.choices.all { !hasReturnOrExternalJumpOrRts(it.statements) }
|
||||
else -> true
|
||||
}
|
||||
|
||||
if(returnError) {
|
||||
val pos = if(lastStatement is Subroutine) subroutine.position else lastStatement.position
|
||||
errors.err("subroutine '${subroutine.name}' has result value(s) but doesn't end with a return statement", pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -763,6 +794,9 @@ internal class AstChecker(private val program: Program,
|
||||
// FLOATS enabled?
|
||||
if(!compilerOptions.floats && (decl.datatype.isFloat || decl.datatype.isFloatArray) && decl.type != VarDeclType.MEMORY)
|
||||
err("floating point used, but that is not enabled via options")
|
||||
else if(compilerOptions.compTarget.name in arrayOf(PETTarget.NAME, C128Target.NAME) && decl.type != VarDeclType.CONST && (decl.datatype.isFloat || decl.datatype.isFloatArray)) {
|
||||
err("pet32 and c128 target do not support floating point numbers yet")
|
||||
}
|
||||
|
||||
// ARRAY without size specifier MUST have an iterable initializer value
|
||||
if(decl.isArray && decl.arraysize==null) {
|
||||
@ -1669,6 +1703,9 @@ internal class AstChecker(private val program: Program,
|
||||
if(whenStmt.condition.constValue(program)!=null)
|
||||
errors.warn("when-value is a constant and will always result in the same choice", whenStmt.condition.position)
|
||||
|
||||
if(whenStmt.betterAsOnGoto(program, compilerOptions))
|
||||
errors.info("when statement can be replaced with on..goto", whenStmt.position)
|
||||
|
||||
super.visit(whenStmt)
|
||||
}
|
||||
|
||||
@ -2063,6 +2100,12 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun visit(onGoto: OnGoto) {
|
||||
if(!onGoto.index.inferType(program).getOrUndef().isUnsignedByte) {
|
||||
errors.err("on..goto index must be an unsigned byte", onGoto.index.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, errors: IErrorReporter) {
|
||||
|
@ -20,19 +20,6 @@ internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: Compila
|
||||
checker.visit(this)
|
||||
}
|
||||
|
||||
internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationOptions, errors: IErrorReporter) {
|
||||
val fixer = BeforeAsmAstChanger(this, compilerOptions, errors)
|
||||
fixer.visit(this)
|
||||
while (errors.noErrors() && fixer.applyModifications() > 0) {
|
||||
fixer.visit(this)
|
||||
}
|
||||
val cleaner = BeforeAsmTypecastCleaner(this, errors)
|
||||
cleaner.visit(this)
|
||||
while (errors.noErrors() && cleaner.applyModifications() > 0) {
|
||||
cleaner.visit(this)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Program.reorderStatements(errors: IErrorReporter) {
|
||||
val reorder = StatementReorderer(this, errors)
|
||||
reorder.visit(this)
|
||||
@ -103,8 +90,8 @@ internal fun Program.addTypecasts(errors: IErrorReporter, options: CompilationOp
|
||||
caster.applyModifications()
|
||||
}
|
||||
|
||||
fun Program.desugaring(errors: IErrorReporter) {
|
||||
val desugar = CodeDesugarer(this, errors)
|
||||
fun Program.desugaring(errors: IErrorReporter, options: CompilationOptions) {
|
||||
val desugar = CodeDesugarer(this, options.compTarget, errors)
|
||||
desugar.visit(this)
|
||||
while(errors.noErrors() && desugar.applyModifications()>0)
|
||||
desugar.visit(this)
|
||||
|
@ -139,7 +139,7 @@ class AstPreprocessor(val program: Program,
|
||||
}
|
||||
} else {
|
||||
// handle declaration of a single variable
|
||||
if(decl.value!=null && decl.datatype.isNumericOrBool) {
|
||||
if(decl.value!=null && !decl.datatype.isIterable) {
|
||||
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, null, false, decl.position)
|
||||
val assign = Assignment(target, decl.value!!, AssignmentOrigin.VARINIT, decl.position)
|
||||
replacements.add(IAstModification.ReplaceNode(decl, assign, scope))
|
||||
|
@ -3,6 +3,7 @@ package prog8.compiler.astprocessing
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.defaultZero
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.statements.*
|
||||
@ -48,18 +49,27 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
|
||||
val mods = mutableListOf<IAstModification>()
|
||||
|
||||
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine.
|
||||
// and if an assembly block doesn't contain a rts/rti.
|
||||
// and if an assembly block doesn't contain a rts/rti. AND if there's no return value(s) because we can't make one up!
|
||||
if (!subroutine.isAsmSubroutine) {
|
||||
if(subroutine.isEmpty()) {
|
||||
val returnStmt = Return(arrayOf(), subroutine.position)
|
||||
mods += IAstModification.InsertLast(returnStmt, subroutine)
|
||||
if(subroutine.returntypes.isNotEmpty())
|
||||
errors.err("subroutine is missing a return statement with value(s)", subroutine.position)
|
||||
else {
|
||||
val returnStmt = Return(arrayOf(), subroutine.position)
|
||||
mods += IAstModification.InsertLast(returnStmt, subroutine)
|
||||
}
|
||||
} else {
|
||||
val last = subroutine.statements.last()
|
||||
if((last !is InlineAssembly || !last.hasReturnOrRts()) && last !is Return) {
|
||||
val lastStatement = subroutine.statements.reversed().firstOrNull { it !is Subroutine }
|
||||
if(lastStatement !is Return) {
|
||||
val returnStmt = Return(arrayOf(), subroutine.position)
|
||||
mods += IAstModification.InsertLast(returnStmt, subroutine)
|
||||
if(subroutine.returntypes.isNotEmpty()) {
|
||||
// .... we cannot return this as an error, because that also breaks legitimate cases where the return is done from within a nested scope somewhere
|
||||
// errors.err("subroutine is missing a return statement with value(s)", subroutine.position)
|
||||
} else {
|
||||
val returnStmt = Return(arrayOf(), subroutine.position)
|
||||
mods += IAstModification.InsertLast(returnStmt, subroutine)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -76,8 +86,20 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
|
||||
&& prevStmt !is Subroutine
|
||||
&& prevStmt !is Return
|
||||
) {
|
||||
val returnStmt = Return(arrayOf(), subroutine.position)
|
||||
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope)
|
||||
if(!subroutine.inline) {
|
||||
if(outerScope is Subroutine && outerScope.returntypes.isNotEmpty()) {
|
||||
if(outerScope.returntypes.size>1 || !outerScope.returntypes[0].isNumericOrBool) {
|
||||
errors.err("subroutine is missing a return statement to avoid falling through into nested subroutine", outerStatements[subroutineStmtIdx-1].position)
|
||||
} else {
|
||||
val zero = defaultZero(outerScope.returntypes[0].base, Position.DUMMY)
|
||||
val returnStmt = Return(arrayOf(zero), outerStatements[subroutineStmtIdx - 1].position)
|
||||
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope)
|
||||
}
|
||||
} else {
|
||||
val returnStmt = Return(arrayOf(), outerStatements[subroutineStmtIdx - 1].position)
|
||||
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,13 +5,10 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.ComparisonOperators
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal class CodeDesugarer(val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
internal class CodeDesugarer(val program: Program, private val target: ICompilationTarget, private val errors: IErrorReporter) : AstWalker() {
|
||||
|
||||
// Some more code shuffling to simplify the Ast that the codegenerator has to process.
|
||||
// Several changes have already been done by the StatementReorderer !
|
||||
@ -27,6 +24,7 @@ internal class CodeDesugarer(val program: Program, private val errors: IErrorRep
|
||||
// - @(&var) and @(&var+1) replaced by lsb(var) and msb(var) if var is a word
|
||||
// - flatten chained assignments
|
||||
// - remove alias nodes
|
||||
// - convert on..goto/call to jumpaddr array and separate goto/call
|
||||
|
||||
override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
|
||||
return listOf(IAstModification.Remove(alias, parent as IStatementContainer))
|
||||
@ -106,7 +104,7 @@ if not CONDITION
|
||||
untilLoop.body,
|
||||
IfElse(invertCondition(untilLoop.condition, program),
|
||||
AnonymousScope(mutableListOf(program.jumpLabel(loopLabel)), pos),
|
||||
AnonymousScope(mutableListOf(), pos),
|
||||
AnonymousScope.empty(),
|
||||
pos)
|
||||
), pos)
|
||||
return listOf(IAstModification.ReplaceNode(untilLoop, replacement, parent))
|
||||
@ -150,7 +148,7 @@ _after:
|
||||
loopLabel,
|
||||
IfElse(invertCondition(whileLoop.condition, program),
|
||||
AnonymousScope(mutableListOf(program.jumpLabel(afterLabel)), pos),
|
||||
AnonymousScope(mutableListOf(), pos),
|
||||
AnonymousScope.empty(),
|
||||
pos),
|
||||
whileLoop.body,
|
||||
program.jumpLabel(loopLabel),
|
||||
@ -350,4 +348,60 @@ _after:
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(ongoto: OnGoto, parent: Node): Iterable<IAstModification> {
|
||||
val indexDt = ongoto.index.inferType(program).getOrUndef()
|
||||
if(!indexDt.isUnsignedByte)
|
||||
return noModifications
|
||||
|
||||
val numlabels = ongoto.labels.size
|
||||
val split = if(ongoto.isCall)
|
||||
true // for calls (indirect JSR), split array is always the optimal choice
|
||||
else
|
||||
target.cpu==CpuType.CPU6502 // for goto (indirect JMP), split array is optimal for 6502, but NOT for the 65C02 (it has a different JMP addressing mode available)
|
||||
val arrayDt = DataType.arrayFor(BaseDataType.UWORD, split)
|
||||
val labelArray = ArrayLiteral(InferredTypes.knownFor(arrayDt), ongoto.labels.toTypedArray(), ongoto.position)
|
||||
val jumplistArray = VarDecl.createAutoOptionalSplit(labelArray)
|
||||
|
||||
val indexValue: Expression
|
||||
val conditionVar: VarDecl?
|
||||
val assignIndex: Assignment?
|
||||
|
||||
// put condition in temp var, if it is not simple; to avoid evaluating expression multiple times
|
||||
if (ongoto.index.isSimple) {
|
||||
indexValue = ongoto.index
|
||||
assignIndex = null
|
||||
conditionVar = null
|
||||
} else {
|
||||
conditionVar = VarDecl.createAuto(indexDt)
|
||||
indexValue = IdentifierReference(listOf(conditionVar.name), conditionVar.position)
|
||||
val varTarget = AssignTarget(indexValue, null, null, null, false, conditionVar.position)
|
||||
assignIndex = Assignment(varTarget, ongoto.index, AssignmentOrigin.USERCODE, ongoto.position)
|
||||
}
|
||||
|
||||
val callTarget = ArrayIndexedExpression(IdentifierReference(listOf(jumplistArray.name), jumplistArray.position), ArrayIndex(indexValue.copy(), indexValue.position), ongoto.position)
|
||||
val callIndexed = AnonymousScope.empty(ongoto.position)
|
||||
if(ongoto.isCall) {
|
||||
callIndexed.statements.add(FunctionCallStatement(IdentifierReference(listOf("call"), ongoto.position), mutableListOf(callTarget), true, ongoto.position))
|
||||
} else {
|
||||
callIndexed.statements.add(Jump(callTarget, ongoto.position))
|
||||
}
|
||||
|
||||
val ifSt = if(ongoto.elsepart==null || ongoto.elsepart!!.isEmpty()) {
|
||||
// if index<numlabels call(labels[index])
|
||||
val compare = BinaryExpression(indexValue.copy(), "<", NumericLiteral.optimalInteger(numlabels, ongoto.position), ongoto.position)
|
||||
IfElse(compare, callIndexed, AnonymousScope.empty(), ongoto.position)
|
||||
} else {
|
||||
// if index>=numlabels elselabel() else call(labels[index])
|
||||
val compare = BinaryExpression(indexValue.copy(), ">=", NumericLiteral.optimalInteger(numlabels, ongoto.position), ongoto.position)
|
||||
IfElse(compare, ongoto.elsepart!!, callIndexed, ongoto.position)
|
||||
}
|
||||
|
||||
val replacementScope = AnonymousScope(if(conditionVar==null)
|
||||
mutableListOf(ifSt, jumplistArray)
|
||||
else
|
||||
mutableListOf(conditionVar, assignIndex!!, ifSt, jumplistArray)
|
||||
, ongoto.position)
|
||||
return listOf(IAstModification.ReplaceNode(ongoto, replacementScope, parent))
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
is VarDecl -> transform(statement)
|
||||
is When -> transform(statement)
|
||||
is WhileLoop -> throw FatalAstException("while loops must have been converted to jumps")
|
||||
is OnGoto -> throw FatalAstException("ongoto must have been converted to array and separate call/goto")
|
||||
}
|
||||
}
|
||||
|
||||
@ -772,7 +773,6 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
return cast
|
||||
}
|
||||
|
||||
|
||||
private fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
|
||||
return if (SourceCode.isLibraryResource(filename)) {
|
||||
return com.github.michaelbull.result.runCatching {
|
||||
|
@ -77,7 +77,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), false, src, outputDir, writeAssembly = false)
|
||||
val statements = result!!.compilerAst.entrypoint.statements
|
||||
statements.size shouldBe 7
|
||||
statements.size shouldBe 8
|
||||
val a1 = statements[2] as Assignment
|
||||
val a2 = statements[3] as Assignment
|
||||
val a3 = statements[4] as Assignment
|
||||
|
@ -36,6 +36,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
|
||||
warnSymbolShadowing = false,
|
||||
quietAll = true,
|
||||
quietAssembler = true,
|
||||
showTimings = false,
|
||||
asmListfile = false,
|
||||
includeSourcelines = false,
|
||||
experimentalCodegen = false,
|
||||
@ -156,6 +157,7 @@ class TestCompilerOnExamplesCx16: FunSpec({
|
||||
"interpolation",
|
||||
"kefrenbars",
|
||||
"keyboardhandler",
|
||||
"landscape",
|
||||
"life",
|
||||
"mandelbrot",
|
||||
"multi-irq-old",
|
||||
|
@ -28,6 +28,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
|
||||
warnSymbolShadowing = false,
|
||||
quietAll = true,
|
||||
quietAssembler = true,
|
||||
showTimings = false,
|
||||
asmListfile = false,
|
||||
includeSourcelines = false,
|
||||
experimentalCodegen = false,
|
||||
|
@ -262,12 +262,13 @@ class TestMemory: FunSpec({
|
||||
shouldThrow<IllegalArgumentException> {
|
||||
target.memorySize(BaseDataType.UNDEFINED)
|
||||
}
|
||||
shouldThrow<IllegalArgumentException> {
|
||||
target.memorySize(BaseDataType.LONG)
|
||||
shouldThrow<NoSuchElementException> {
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.LONG), 10)
|
||||
}
|
||||
target.memorySize(BaseDataType.BOOL) shouldBe 1
|
||||
target.memorySize(BaseDataType.BYTE) shouldBe 1
|
||||
target.memorySize(BaseDataType.WORD) shouldBe 2
|
||||
target.memorySize(BaseDataType.LONG) shouldBe 4
|
||||
target.memorySize(BaseDataType.FLOAT) shouldBe target.FLOAT_MEM_SIZE
|
||||
|
||||
target.memorySize(DataType.BOOL, null) shouldBe 1
|
||||
@ -283,13 +284,14 @@ class TestMemory: FunSpec({
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.BOOL), 10) shouldBe 10
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.BYTE), 10) shouldBe 10
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.WORD), 10) shouldBe 20
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.WORD), 10) shouldBe 20
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.UWORD), 10) shouldBe 20
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.FLOAT), 10) shouldBe 10*target.FLOAT_MEM_SIZE
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.WORD, true), 10) shouldBe 20
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.UWORD, true), 10) shouldBe 20
|
||||
|
||||
target.memorySize(DataType.BOOL, 10) shouldBe 10
|
||||
target.memorySize(DataType.UWORD, 10) shouldBe 20
|
||||
target.memorySize(DataType.LONG, 10) shouldBe 40
|
||||
target.memorySize(DataType.FLOAT, 10) shouldBe 10*target.FLOAT_MEM_SIZE
|
||||
}
|
||||
}
|
||||
|
@ -37,16 +37,16 @@ class TestNumericLiteral: FunSpec({
|
||||
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos), NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos)) shouldBe true
|
||||
}
|
||||
|
||||
test("test truncating") {
|
||||
test("test truncating avoidance") {
|
||||
shouldThrow<ExpressionError> {
|
||||
NumericLiteral(BaseDataType.BYTE, -2.345, dummyPos)
|
||||
}.message shouldContain "refused truncating"
|
||||
}.message shouldContain "float value given for integer"
|
||||
shouldThrow<ExpressionError> {
|
||||
NumericLiteral(BaseDataType.BYTE, -2.6, dummyPos)
|
||||
}.message shouldContain "refused truncating"
|
||||
}.message shouldContain "float value given for integer"
|
||||
shouldThrow<ExpressionError> {
|
||||
NumericLiteral(BaseDataType.UWORD, 2222.345, dummyPos)
|
||||
}.message shouldContain "refused truncating"
|
||||
}.message shouldContain "float value given for integer"
|
||||
NumericLiteral(BaseDataType.UBYTE, 2.0, dummyPos).number shouldBe 2.0
|
||||
NumericLiteral(BaseDataType.BYTE, -2.0, dummyPos).number shouldBe -2.0
|
||||
NumericLiteral(BaseDataType.UWORD, 2222.0, dummyPos).number shouldBe 2222.0
|
||||
@ -213,7 +213,7 @@ class TestNumericLiteral: FunSpec({
|
||||
test("cast can change value") {
|
||||
fun num(dt: BaseDataType, num: Double): NumericLiteral {
|
||||
val n = NumericLiteral(dt, num, Position.DUMMY)
|
||||
n.linkParents(AnonymousScope(mutableListOf(), Position.DUMMY))
|
||||
n.linkParents(AnonymousScope.empty())
|
||||
return n
|
||||
}
|
||||
val cast1 = num(BaseDataType.UBYTE, 200.0).cast(BaseDataType.BYTE, false)
|
||||
@ -233,7 +233,7 @@ class TestNumericLiteral: FunSpec({
|
||||
test("convert cannot change value") {
|
||||
fun num(dt: BaseDataType, num: Double): NumericLiteral {
|
||||
val n = NumericLiteral(dt, num, Position.DUMMY)
|
||||
n.linkParents(AnonymousScope(mutableListOf(), Position.DUMMY))
|
||||
n.linkParents(AnonymousScope.empty())
|
||||
return n
|
||||
}
|
||||
num(BaseDataType.UBYTE, 200.0).convertTypeKeepValue(BaseDataType.BYTE).isValid shouldBe false
|
||||
|
@ -22,7 +22,9 @@ import prog8.code.core.Position
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.vm.VmRunner
|
||||
import prog8tests.helpers.*
|
||||
import kotlin.io.path.readText
|
||||
|
||||
|
||||
class TestOptimization: FunSpec({
|
||||
@ -225,7 +227,7 @@ other {
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target(), optimize=false, src, outputDir, writeAssembly = false)!!
|
||||
val assignFF = result.compilerAst.entrypoint.statements.last() as Assignment
|
||||
val assignFF = result.compilerAst.entrypoint.statements.dropLast(1).last() as Assignment
|
||||
assignFF.isAugmentable shouldBe true
|
||||
assignFF.target.identifier!!.nameInSource shouldBe listOf("ff")
|
||||
val value = assignFF.value as BinaryExpression
|
||||
@ -252,7 +254,7 @@ other {
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
result.compilerAst.entrypoint.statements.size shouldBe 7
|
||||
result.compilerAst.entrypoint.statements.size shouldBe 8
|
||||
val alldecls = result.compilerAst.entrypoint.allDefinedSymbols.toList()
|
||||
alldecls.map { it.first } shouldBe listOf("unused_but_shared", "usedvar_only_written", "usedvar")
|
||||
}
|
||||
@ -276,12 +278,12 @@ other {
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
result.compilerAst.entrypoint.statements.size shouldBe 3
|
||||
result.compilerAst.entrypoint.statements.size shouldBe 4
|
||||
val ifstmt = result.compilerAst.entrypoint.statements[0] as IfElse
|
||||
ifstmt.truepart.statements.size shouldBe 1
|
||||
(ifstmt.truepart.statements[0] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "r0")
|
||||
val func2 = result.compilerAst.entrypoint.statements[2] as Subroutine
|
||||
func2.statements.size shouldBe 2
|
||||
val func2 = result.compilerAst.entrypoint.statements.last() as Subroutine
|
||||
func2.statements.size shouldBe 3
|
||||
(func2.statements[0] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "r0")
|
||||
}
|
||||
|
||||
@ -312,7 +314,7 @@ main {
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
result.compilerAst.entrypoint.statements.size shouldBe 0
|
||||
result.compilerAst.entrypoint.statements.size shouldBe 1
|
||||
result.compilerAst.entrypoint.definingScope.statements.size shouldBe 1
|
||||
}
|
||||
|
||||
@ -350,7 +352,7 @@ main {
|
||||
z6 = z1 - 5
|
||||
*/
|
||||
val statements = result.compilerAst.entrypoint.statements
|
||||
statements.size shouldBe 12
|
||||
statements.size shouldBe 13
|
||||
val z1decl = statements[0] as VarDecl
|
||||
val z1init = statements[1] as Assignment
|
||||
val z2decl = statements[2] as VarDecl
|
||||
@ -395,8 +397,8 @@ main {
|
||||
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 5
|
||||
val assign=stmts.last() as Assignment
|
||||
stmts.size shouldBe 6
|
||||
val assign=stmts[4] as Assignment
|
||||
(assign.target.memoryAddress?.addressExpression as IdentifierReference).nameInSource shouldBe listOf("aa")
|
||||
}
|
||||
|
||||
@ -412,8 +414,8 @@ main {
|
||||
"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 5
|
||||
val assign=stmts.last() as Assignment
|
||||
stmts.size shouldBe 6
|
||||
val assign=stmts[4] as Assignment
|
||||
(assign.target.memoryAddress?.addressExpression as IdentifierReference).nameInSource shouldBe listOf("aa")
|
||||
}
|
||||
|
||||
@ -431,7 +433,7 @@ main {
|
||||
"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 10
|
||||
stmts.size shouldBe 11
|
||||
stmts.filterIsInstance<VarDecl>().size shouldBe 5
|
||||
stmts.filterIsInstance<Assignment>().size shouldBe 5
|
||||
}
|
||||
@ -508,7 +510,7 @@ main {
|
||||
xx += 6
|
||||
*/
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 6
|
||||
stmts.size shouldBe 7
|
||||
stmts.filterIsInstance<VarDecl>().size shouldBe 3
|
||||
stmts.filterIsInstance<Assignment>().size shouldBe 3
|
||||
}
|
||||
@ -537,13 +539,13 @@ main {
|
||||
xx += 10
|
||||
*/
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 7
|
||||
stmts.size shouldBe 8
|
||||
stmts.filterIsInstance<VarDecl>().size shouldBe 2
|
||||
stmts.filterIsInstance<Assignment>().size shouldBe 5
|
||||
val assignXX1 = stmts[1] as Assignment
|
||||
assignXX1.target.identifier!!.nameInSource shouldBe listOf("xx")
|
||||
assignXX1.value shouldBe NumericLiteral(BaseDataType.UWORD, 20.0, Position.DUMMY)
|
||||
val assignXX2 = stmts.last() as Assignment
|
||||
val assignXX2 = stmts[6] as Assignment
|
||||
assignXX2.target.identifier!!.nameInSource shouldBe listOf("xx")
|
||||
val xxValue = assignXX2.value as BinaryExpression
|
||||
xxValue.operator shouldBe "+"
|
||||
@ -577,7 +579,7 @@ main {
|
||||
thingy++
|
||||
*/
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 6
|
||||
stmts.size shouldBe 7
|
||||
val ifStmt = stmts[5] as IfElse
|
||||
val containment = ifStmt.condition as ContainmentCheck
|
||||
(containment.element as IdentifierReference).nameInSource shouldBe listOf("source")
|
||||
@ -612,7 +614,7 @@ main {
|
||||
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 5
|
||||
stmts.size shouldBe 6
|
||||
val ifStmt = stmts[4] as IfElse
|
||||
val containment = ifStmt.condition as ContainmentCheck
|
||||
(containment.element as IdentifierReference).nameInSource shouldBe listOf("source")
|
||||
@ -634,7 +636,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 5
|
||||
stmts.size shouldBe 6
|
||||
val ifStmt = stmts[4] as IfElse
|
||||
ifStmt.condition shouldBe instanceOf<BinaryExpression>()
|
||||
}
|
||||
@ -652,7 +654,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 5
|
||||
stmts.size shouldBe 6
|
||||
val ifStmt = stmts[4] as IfElse
|
||||
ifStmt.condition shouldBe instanceOf<BinaryExpression>()
|
||||
}
|
||||
@ -670,7 +672,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 5
|
||||
stmts.size shouldBe 6
|
||||
val ifStmt = stmts[4] as IfElse
|
||||
ifStmt.condition shouldBe instanceOf<BinaryExpression>()
|
||||
}
|
||||
@ -687,7 +689,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 3
|
||||
stmts.size shouldBe 4
|
||||
}
|
||||
|
||||
test("repeated assignments to IO register should remain") {
|
||||
@ -828,7 +830,7 @@ main {
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = false, errors = errors)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 4
|
||||
st.size shouldBe 5
|
||||
val xxConst = st[0] as VarDecl
|
||||
xxConst.type shouldBe VarDeclType.CONST
|
||||
xxConst.name shouldBe "xx"
|
||||
@ -872,7 +874,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 8
|
||||
st.size shouldBe 9
|
||||
val if1c = (st[4] as IfElse).condition as PrefixExpression
|
||||
val if2c = (st[5] as IfElse).condition as PrefixExpression
|
||||
val if3c = (st[6] as IfElse).condition as PrefixExpression
|
||||
@ -915,7 +917,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 12
|
||||
st.size shouldBe 13
|
||||
val if1 = st[4] as IfElse
|
||||
val if2 = st[5] as IfElse
|
||||
val if3 = st[6] as IfElse
|
||||
@ -970,7 +972,7 @@ main {
|
||||
|
||||
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 17
|
||||
st.size shouldBe 18
|
||||
|
||||
val answerValue = (st[3] as Assignment).value
|
||||
answerValue shouldBe NumericLiteral(BaseDataType.UWORD, 0.0, Position.DUMMY)
|
||||
@ -1019,7 +1021,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 3
|
||||
st.size shouldBe 4
|
||||
val ifCond1 = (st[0] as IfElse).condition as BinaryExpression
|
||||
val ifCond2 = (st[1] as IfElse).condition as BinaryExpression
|
||||
val ifCond3 = (st[2] as IfElse).condition as BinaryExpression
|
||||
@ -1178,4 +1180,54 @@ main {
|
||||
}"""
|
||||
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("correct unused block removal for virtual target") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0++
|
||||
}
|
||||
}
|
||||
|
||||
some_block {
|
||||
uword buffer = memory("arena", 2000, 0)
|
||||
}
|
||||
|
||||
|
||||
other_block {
|
||||
sub redherring (uword buffer) {
|
||||
%ir {{
|
||||
loadm.w r99000,other_block.redherring.buffer
|
||||
}}
|
||||
}
|
||||
}
|
||||
"""
|
||||
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = true)!!
|
||||
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
|
||||
VmRunner().runProgram(virtfile.readText(), false)
|
||||
}
|
||||
|
||||
test("correct unused block removal for c64 target") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0++
|
||||
}
|
||||
}
|
||||
|
||||
some_block {
|
||||
uword buffer = memory("arena", 2000, 0)
|
||||
}
|
||||
|
||||
|
||||
other_block {
|
||||
sub redherring (uword buffer) {
|
||||
%asm {{
|
||||
lda #<p8b_other_block.p8s_redherring.p8v_buffer
|
||||
ldy #>p8b_other_block.p8s_redherring.p8v_buffer
|
||||
}}
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), true, src, outputDir) shouldNotBe null
|
||||
}
|
||||
})
|
||||
|
@ -19,11 +19,7 @@ import prog8.code.core.Position
|
||||
import prog8.code.core.unescape
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.encodings.Encoder
|
||||
import prog8.code.target.encodings.AtasciiEncoding
|
||||
import prog8.code.target.encodings.C64osEncoding
|
||||
import prog8.code.target.encodings.IsoEncoding
|
||||
import prog8.code.target.encodings.PetsciiEncoding
|
||||
import prog8.code.target.encodings.*
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.compileText
|
||||
import java.io.CharConversionException
|
||||
@ -227,7 +223,7 @@ class TestStringEncodings: FunSpec({
|
||||
|
||||
context("iso") {
|
||||
test("iso accepts iso-characters") {
|
||||
val result = IsoEncoding.encode("a_~ëç")
|
||||
val result = IsoEncoding.encode("a_~ëç", false)
|
||||
result.getOrElse { throw it }.map {it.toInt()} shouldBe listOf(97, 95, 126, 235, 231)
|
||||
}
|
||||
|
||||
@ -276,13 +272,13 @@ class TestStringEncodings: FunSpec({
|
||||
passthrough[1] shouldBe '\u801b'
|
||||
passthrough[2] shouldBe '\u8099'
|
||||
passthrough[3] shouldBe '\u80ff'
|
||||
var encoded = Encoder.encodeString(passthrough, Encoding.PETSCII)
|
||||
var encoded = Encoder(false).encodeString(passthrough, Encoding.PETSCII)
|
||||
encoded shouldBe listOf<UByte>(0u, 0x1bu, 0x99u, 0xffu)
|
||||
encoded = Encoder.encodeString(passthrough, Encoding.ATASCII)
|
||||
encoded = Encoder(false).encodeString(passthrough, Encoding.ATASCII)
|
||||
encoded shouldBe listOf<UByte>(0u, 0x1bu, 0x99u, 0xffu)
|
||||
encoded = Encoder.encodeString(passthrough, Encoding.SCREENCODES)
|
||||
encoded = Encoder(false).encodeString(passthrough, Encoding.SCREENCODES)
|
||||
encoded shouldBe listOf<UByte>(0u, 0x1bu, 0x99u, 0xffu)
|
||||
encoded = Encoder.encodeString(passthrough, Encoding.ISO)
|
||||
encoded = Encoder(false).encodeString(passthrough, Encoding.ISO)
|
||||
encoded shouldBe listOf<UByte>(0u, 0x1bu, 0x99u, 0xffu)
|
||||
}
|
||||
|
||||
@ -413,5 +409,39 @@ class TestStringEncodings: FunSpec({
|
||||
val char2 = (main.statements[5] as Assignment).value as NumericLiteral
|
||||
char2.number shouldBe 80.0
|
||||
}
|
||||
|
||||
test("with newline conversion") {
|
||||
val encoder = Encoder(true)
|
||||
encoder.encodeString("\n\r", Encoding.PETSCII) shouldBe listOf<UByte>(13u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.SCREENCODES) shouldBe listOf<UByte>(141u, 141u)
|
||||
encoder.encodeString("\n\r", Encoding.ATASCII) shouldBe listOf<UByte>(155u, 155u)
|
||||
encoder.encodeString("\n\r", Encoding.ISO) shouldBe listOf<UByte>(13u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.ISO5) shouldBe listOf<UByte>(13u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.ISO16) shouldBe listOf<UByte>(13u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.CP437) shouldBe listOf<UByte>(10u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.KATAKANA) shouldBe listOf<UByte>(13u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.C64OS) shouldBe listOf<UByte>(13u, 13u)
|
||||
|
||||
encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.PETSCII).map { it.code } shouldBe listOf(10, 10)
|
||||
encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.ISO).map { it.code } shouldBe listOf(10, 10)
|
||||
encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.CP437).map { it.code } shouldBe listOf(10, 13)
|
||||
}
|
||||
|
||||
test("without newline conversion") {
|
||||
val encoder = Encoder(false)
|
||||
encoder.encodeString("\n\r", Encoding.PETSCII) shouldBe listOf<UByte>(13u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.SCREENCODES) shouldBe listOf<UByte>(141u, 141u)
|
||||
encoder.encodeString("\n\r", Encoding.ATASCII) shouldBe listOf<UByte>(155u, 155u)
|
||||
encoder.encodeString("\n\r", Encoding.ISO) shouldBe listOf<UByte>(10u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.ISO5) shouldBe listOf<UByte>(10u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.ISO16) shouldBe listOf<UByte>(10u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.CP437) shouldBe listOf<UByte>(10u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.KATAKANA) shouldBe listOf<UByte>(10u, 13u)
|
||||
encoder.encodeString("\n\r", Encoding.C64OS) shouldBe listOf<UByte>(13u, 13u)
|
||||
|
||||
encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.PETSCII).map { it.code } shouldBe listOf(10, 10)
|
||||
encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.ISO).map { it.code } shouldBe listOf(10, 13)
|
||||
encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.CP437).map { it.code } shouldBe listOf(10, 13)
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -54,6 +54,9 @@ class TestSubroutines: FunSpec({
|
||||
}
|
||||
|
||||
asmsub asmfunc(str thing @AY) {
|
||||
%asm {{
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub func(str thing) {
|
||||
@ -67,12 +70,12 @@ class TestSubroutines: FunSpec({
|
||||
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
||||
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
||||
asmfunc.isAsmSubroutine shouldBe true
|
||||
asmfunc.statements.isEmpty() shouldBe true
|
||||
asmfunc.statements.size shouldBe 1
|
||||
func.isAsmSubroutine shouldBe false
|
||||
withClue("str param for subroutines should be changed into UWORD") {
|
||||
asmfunc.parameters.single().type shouldBe DataType.UWORD
|
||||
func.parameters.single().type shouldBe DataType.UWORD
|
||||
func.statements.size shouldBe 4
|
||||
func.statements.size shouldBe 5
|
||||
val paramvar = func.statements[0] as VarDecl
|
||||
paramvar.name shouldBe "thing"
|
||||
paramvar.datatype shouldBe DataType.UWORD
|
||||
@ -174,6 +177,9 @@ class TestSubroutines: FunSpec({
|
||||
}
|
||||
|
||||
asmsub asmfunc(ubyte[] thing @AY) {
|
||||
%asm {{
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub func(ubyte[] thing) {
|
||||
|
@ -54,7 +54,7 @@ class TestTypecasts: FunSpec({
|
||||
}"""
|
||||
val result = compileText(C64Target(), false, text, outputDir, writeAssembly = false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 6
|
||||
stmts.size shouldBe 7
|
||||
val expr = (stmts[5] as Assignment).value as BinaryExpression
|
||||
expr.operator shouldBe "and"
|
||||
(expr.left as IdentifierReference).nameInSource shouldBe listOf("bb2") // no cast
|
||||
@ -157,7 +157,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(C64Target(), false, text, outputDir, writeAssembly = false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 7
|
||||
stmts.size shouldBe 8
|
||||
val fcall1 = ((stmts[4] as Assignment).value as IFunctionCall)
|
||||
fcall1.args[0] shouldBe NumericLiteral(BaseDataType.BOOL, 1.0, Position.DUMMY)
|
||||
fcall1.args[1] shouldBe NumericLiteral(BaseDataType.BOOL, 0.0, Position.DUMMY)
|
||||
@ -209,7 +209,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(C64Target(), false, text, outputDir, writeAssembly = false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 3
|
||||
stmts.size shouldBe 4
|
||||
}
|
||||
|
||||
test("ubyte to word casts") {
|
||||
@ -224,7 +224,7 @@ main {
|
||||
|
||||
val result = compileText(C64Target(), true, src, outputDir, writeAssembly = false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 4
|
||||
stmts.size shouldBe 5
|
||||
val assign1tc = (stmts[2] as Assignment).value as TypecastExpression
|
||||
val assign2tc = (stmts[3] as Assignment).value as TypecastExpression
|
||||
assign1tc.type shouldBe BaseDataType.WORD
|
||||
@ -255,7 +255,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(C64Target(), false, text, outputDir, writeAssembly = false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 8
|
||||
stmts.size shouldBe 9
|
||||
val arg1 = (stmts[2] as IFunctionCall).args.single()
|
||||
val arg2 = (stmts[3] as IFunctionCall).args.single()
|
||||
val arg3 = (stmts[4] as IFunctionCall).args.single()
|
||||
@ -318,23 +318,21 @@ main {
|
||||
errors.errors[1] shouldContain "no cast"
|
||||
}
|
||||
|
||||
test("refuse to truncate float literal 1") {
|
||||
test("allow explicit float literal cast to integer") {
|
||||
val text = """
|
||||
%option enable_floats
|
||||
main {
|
||||
sub start() {
|
||||
float @shared fl = 3.456 as uword
|
||||
fl = 1.234 as uword
|
||||
cx16.r0 = 1234.5678 as uword
|
||||
cx16.r1L = 99.333 as ubyte
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, text, outputDir, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "refused"
|
||||
errors.errors[1] shouldContain "refused"
|
||||
compileText(C64Target(), false, text, outputDir, errors=errors) shouldNotBe null
|
||||
errors.errors.size shouldBe 0
|
||||
}
|
||||
|
||||
test("refuse to truncate float literal 2") {
|
||||
test("refuse to truncate float inplace") {
|
||||
val text = """
|
||||
%option enable_floats
|
||||
main {
|
||||
@ -350,23 +348,6 @@ main {
|
||||
errors.errors[0] shouldContain "in-place makes no sense"
|
||||
}
|
||||
|
||||
test("refuse to truncate float literal 3") {
|
||||
val text = """
|
||||
%option enable_floats
|
||||
main {
|
||||
sub start() {
|
||||
uword @shared ww = 3.456 as uword
|
||||
ww++
|
||||
ww = 3.456 as uword
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, text, outputDir, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "refused"
|
||||
errors.errors[1] shouldContain "refused"
|
||||
}
|
||||
|
||||
test("correct implicit casts of signed number comparison and logical expressions") {
|
||||
val text = """
|
||||
%import floats
|
||||
@ -903,7 +884,7 @@ main {
|
||||
val result = compileText(C64Target(), false, src, outputDir, writeAssembly = false)!!
|
||||
val program = result.compilerAst
|
||||
val st = program.entrypoint.statements
|
||||
st.size shouldBe 1
|
||||
st.size shouldBe 2
|
||||
val assign = st[0] as Assignment
|
||||
assign.target.inferType(program).getOrUndef().base shouldBe BaseDataType.BYTE
|
||||
val ifexpr = assign.value as IfExpression
|
||||
@ -928,7 +909,7 @@ main {
|
||||
val result = compileText(C64Target(), false, src, outputDir, writeAssembly = false)!!
|
||||
val program = result.compilerAst
|
||||
val st = program.entrypoint.statements
|
||||
st.size shouldBe 6
|
||||
st.size shouldBe 7
|
||||
val v1 = (st[2] as Assignment).value as BinaryExpression
|
||||
v1.operator shouldBe "+"
|
||||
(v1.left as IdentifierReference).nameInSource shouldBe listOf("cx16","r0")
|
||||
|
@ -52,7 +52,7 @@ class TestConst: FunSpec({
|
||||
// cx16.r5s = llw - 1899
|
||||
// cx16.r7s = llw + 99
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 9
|
||||
stmts.size shouldBe 10
|
||||
|
||||
val addR0value = (stmts[4] as Assignment).value
|
||||
val binexpr0 = addR0value as BinaryExpression
|
||||
@ -109,7 +109,7 @@ class TestConst: FunSpec({
|
||||
// result++
|
||||
// result = llw * 18.0
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 12
|
||||
stmts.size shouldBe 13
|
||||
|
||||
val mulR0Value = (stmts[3] as Assignment).value
|
||||
val binexpr0 = mulR0Value as BinaryExpression
|
||||
@ -157,7 +157,7 @@ class TestConst: FunSpec({
|
||||
// cx16.r3s = llw /2 *10
|
||||
// cx16.r4s = llw *90 /5
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 7
|
||||
stmts.size shouldBe 8
|
||||
|
||||
val mulR0Value = (stmts[2] as Assignment).value
|
||||
val binexpr0 = mulR0Value as BinaryExpression
|
||||
@ -251,7 +251,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 5
|
||||
st.size shouldBe 6
|
||||
(st[0] as VarDecl).type shouldBe VarDeclType.CONST
|
||||
val assignv1 = (st[2] as Assignment).value
|
||||
val assignv2 = (st[4] as Assignment).value
|
||||
@ -274,7 +274,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), false, src, outputDir, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 6
|
||||
st.size shouldBe 7
|
||||
((st[0] as VarDecl).value as NumericLiteral).number shouldBe 0x2000
|
||||
((st[1] as VarDecl).value as NumericLiteral).number shouldBe 0x9e00
|
||||
((st[2] as VarDecl).value as NumericLiteral).number shouldBe 0x9e00+2*30
|
||||
|
@ -288,13 +288,13 @@ class TestProg8Parser: FunSpec( {
|
||||
}
|
||||
"""
|
||||
val module = parseModule(SourceCode.Text(srcText))
|
||||
assertPositionOf(module, Regex("^string:[0-9a-f\\-]+$"), 1, 0)
|
||||
assertPositionOf(module, Regex("^string:[0-9a-f\\-]+$"), 1, 1)
|
||||
}
|
||||
|
||||
test("of Module parsed from a file") {
|
||||
val path = assumeReadableFile(fixturesDir, "ast_simple_main.p8")
|
||||
val module = parseModule(ImportFileSystem.getFile(path))
|
||||
assertPositionOf(module, SourceCode.relative(path).toString(), 1, 0)
|
||||
assertPositionOf(module, SourceCode.relative(path).toString(), 1, 1)
|
||||
}
|
||||
|
||||
test("of non-root Nodes parsed from file") {
|
||||
@ -302,7 +302,7 @@ class TestProg8Parser: FunSpec( {
|
||||
|
||||
val module = parseModule(ImportFileSystem.getFile(path))
|
||||
val mpf = module.position.file
|
||||
assertPositionOf(module, SourceCode.relative(path).toString(), 1, 0)
|
||||
assertPositionOf(module, SourceCode.relative(path).toString(), 1, 1)
|
||||
val mainBlock = module.statements.filterIsInstance<Block>()[0]
|
||||
assertPositionOf(mainBlock, mpf, 2, 1, 4)
|
||||
val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0]
|
||||
@ -907,7 +907,7 @@ class TestProg8Parser: FunSpec( {
|
||||
"""
|
||||
val result = compileText(C64Target(), false, text, outputDir, writeAssembly = false)!!
|
||||
val start = result.compilerAst.entrypoint
|
||||
val containmentChecks = start.statements.takeLast(4)
|
||||
val containmentChecks = start.statements.takeLast(5)
|
||||
(containmentChecks[0] as IfElse).condition shouldBe instanceOf<ContainmentCheck>()
|
||||
(containmentChecks[1] as IfElse).condition shouldBe instanceOf<ContainmentCheck>()
|
||||
(containmentChecks[2] as Assignment).value shouldBe instanceOf<ContainmentCheck>()
|
||||
@ -948,7 +948,7 @@ class TestProg8Parser: FunSpec( {
|
||||
"""
|
||||
val result = compileText(C64Target(), false, text, outputDir, writeAssembly = false)!!
|
||||
val stmt = result.compilerAst.entrypoint.statements
|
||||
stmt.size shouldBe 12
|
||||
stmt.size shouldBe 13
|
||||
val var1 = stmt[0] as VarDecl
|
||||
var1.sharedWithAsm shouldBe true
|
||||
var1.zeropage shouldBe ZeropageWish.REQUIRE_ZEROPAGE
|
||||
@ -998,7 +998,7 @@ main {
|
||||
; curly braces without newline
|
||||
sub start () { foo() derp() other() }
|
||||
sub foo() { cx16.r0++ }
|
||||
asmsub derp() { %asm {{ nop }} %ir {{ load.b r0,1 }} }
|
||||
asmsub derp() { %asm {{ nop rts }} %ir {{ load.b r0,1 return }} }
|
||||
|
||||
; curly braces on next line
|
||||
sub other()
|
||||
@ -1014,6 +1014,7 @@ main {
|
||||
{{
|
||||
txa
|
||||
tay
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
@ -1022,6 +1023,7 @@ main {
|
||||
%ir
|
||||
{{
|
||||
load.b r0,1
|
||||
return
|
||||
}}
|
||||
}
|
||||
}"""
|
||||
@ -1042,7 +1044,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 8
|
||||
st.size shouldBe 9
|
||||
val assigns = st.filterIsInstance<Assignment>()
|
||||
(assigns[0].value as NumericLiteral).number shouldBe 12345
|
||||
(assigns[1].value as NumericLiteral).number shouldBe 0xffee
|
||||
@ -1053,11 +1055,54 @@ main {
|
||||
test("oneliner") {
|
||||
val src="""
|
||||
main { sub start() { cx16.r0++ cx16.r1++ } }
|
||||
other { asmsub thing() { %asm {{ inx }} } }
|
||||
other { asmsub thing() { %asm {{ inx rts }} } }
|
||||
"""
|
||||
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 2
|
||||
st.size shouldBe 3
|
||||
}
|
||||
|
||||
test("allow type name as argument for sizeof()") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
bool @shared b
|
||||
float @shared f
|
||||
word @shared w
|
||||
const long ll = 9999999
|
||||
ubyte @shared ub1, ub2
|
||||
|
||||
ub1 = sys.SIZEOF_BOOL
|
||||
ub2 = sys.SIZEOF_WORD
|
||||
ub1 = sys.SIZEOF_LONG
|
||||
ub2 = sys.SIZEOF_FLOAT
|
||||
|
||||
ub1 = sizeof(true)
|
||||
ub2 = sizeof(1234)
|
||||
ub1 = sizeof(12345678)
|
||||
ub2 = sizeof(9.999)
|
||||
|
||||
ub1 = sizeof(b)
|
||||
ub2 = sizeof(w)
|
||||
ub1 = sizeof(ll)
|
||||
ub2 = sizeof(f)
|
||||
|
||||
ub1 = sizeof(bool)
|
||||
ub2 = sizeof(word)
|
||||
ub1 = sizeof(long)
|
||||
ub2 = sizeof(float)
|
||||
}
|
||||
}"""
|
||||
|
||||
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 27
|
||||
val assignments = st.dropLast(1).takeLast(16)
|
||||
assignments.forEach { a ->
|
||||
(a as Assignment).value shouldBe instanceOf<NumericLiteral>()
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
@ -243,4 +243,40 @@ main {
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain ":8:5: address must be a constant"
|
||||
}
|
||||
|
||||
test("detect missing return") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
void read_loadlist()
|
||||
void test2()
|
||||
}
|
||||
|
||||
|
||||
sub read_loadlist() -> bool {
|
||||
cx16.r0++
|
||||
if cx16.r0==0
|
||||
return false
|
||||
cx16.r1++
|
||||
; TODO missing return! ERROR!
|
||||
}
|
||||
|
||||
sub test2() -> bool {
|
||||
cx16.r0++
|
||||
return false
|
||||
|
||||
sub sub1() {
|
||||
cx16.r0++
|
||||
}
|
||||
|
||||
sub sub2() {
|
||||
cx16.r0++
|
||||
}
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(Cx16Target(), false, src, outputDir, errors, false) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "doesn't end with a return"
|
||||
}
|
||||
})
|
||||
|
@ -17,9 +17,7 @@ import prog8.code.ast.*
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.code.target.*
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.compileText
|
||||
|
||||
@ -271,28 +269,6 @@ main {
|
||||
errors.errors[0] shouldContain "has result value"
|
||||
errors.errors[1] shouldContain "has result value"
|
||||
}
|
||||
|
||||
test("missing return value is not a syntax error if there's an external goto") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0 = runit1()
|
||||
runit2()
|
||||
}
|
||||
|
||||
sub runit1() -> uword {
|
||||
repeat {
|
||||
cx16.r0++
|
||||
goto runit2
|
||||
}
|
||||
}
|
||||
|
||||
sub runit2() {
|
||||
cx16.r0++
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
|
||||
}
|
||||
}
|
||||
|
||||
context("variable declarations") {
|
||||
@ -355,7 +331,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 12
|
||||
st.size shouldBe 13
|
||||
st[0] shouldBe instanceOf<VarDecl>() // x
|
||||
st[2] shouldBe instanceOf<VarDecl>() // y
|
||||
st[4] shouldBe instanceOf<VarDecl>() // z
|
||||
@ -429,7 +405,7 @@ main {
|
||||
errors.warnings.all { "dirty variable" in it } shouldBe true
|
||||
val start = result.compilerAst.entrypoint
|
||||
val st = start.statements
|
||||
st.size shouldBe 9
|
||||
st.size shouldBe 10
|
||||
val assignments = st.filterIsInstance<Assignment>()
|
||||
assignments.size shouldBe 2
|
||||
assignments[0].target.identifier?.nameInSource shouldBe listOf("locwi")
|
||||
@ -523,7 +499,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 7
|
||||
stmts.size shouldBe 8
|
||||
val assign1expr = (stmts[3] as Assignment).value as BinaryExpression
|
||||
val assign2expr = (stmts[5] as Assignment).value as BinaryExpression
|
||||
assign1expr.operator shouldBe "<<"
|
||||
@ -554,7 +530,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 9
|
||||
stmts.size shouldBe 10
|
||||
}
|
||||
|
||||
test("alternative notation for negative containment check") {
|
||||
@ -570,7 +546,7 @@ main {
|
||||
"""
|
||||
val result = compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 4
|
||||
stmts.size shouldBe 5
|
||||
val value1 = (stmts[2] as Assignment).value as PrefixExpression
|
||||
val value2 = (stmts[3] as Assignment).value as PrefixExpression
|
||||
value1.operator shouldBe "not"
|
||||
@ -764,7 +740,7 @@ main {
|
||||
}"""
|
||||
val result=compileText(VMTarget(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 11
|
||||
st.size shouldBe 12
|
||||
|
||||
val ifCond = (st[8] as IfElse).condition as BinaryExpression
|
||||
ifCond.operator shouldBe ">="
|
||||
@ -792,7 +768,7 @@ main {
|
||||
|
||||
val result=compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly=false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 6
|
||||
st.size shouldBe 7
|
||||
val value = (st[5] as Assignment).value as BinaryExpression
|
||||
value.operator shouldBe "%"
|
||||
}
|
||||
@ -838,10 +814,9 @@ main {
|
||||
}"""
|
||||
val result = compileText(VMTarget(), optimize=true, src, outputDir, writeAssembly=false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 8
|
||||
val assignUbbVal = ((st[5] as Assignment).value as TypecastExpression)
|
||||
assignUbbVal.type shouldBe BaseDataType.UBYTE
|
||||
assignUbbVal.expression shouldBe instanceOf<IdentifierReference>()
|
||||
st.size shouldBe 9
|
||||
val assignUbbVal = (st[5] as Assignment).value as IdentifierReference
|
||||
assignUbbVal.inferType(result.compilerAst) shouldBe InferredTypes.knownFor(BaseDataType.BYTE)
|
||||
val assignVaddr = (st[7] as Assignment).value as FunctionCallExpression
|
||||
assignVaddr.target.nameInSource shouldBe listOf("mkword")
|
||||
val tc = assignVaddr.args[0] as TypecastExpression
|
||||
@ -970,6 +945,7 @@ main {
|
||||
if cx16.r0==0
|
||||
return cx16.r0+cx16.r1
|
||||
defer cx16.r2++
|
||||
return 999
|
||||
}
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), optimize=true, src, outputDir, writeAssembly=true)!!
|
||||
@ -1078,6 +1054,80 @@ main {
|
||||
compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly=true) shouldNotBe null
|
||||
compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("using the cx16 virtual registers as various datatypes") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
uword uw = 9999
|
||||
word sw = -2222
|
||||
ubyte ub = 42
|
||||
byte sb = -99
|
||||
bool bb = true
|
||||
|
||||
cx16.r0 = uw
|
||||
cx16.r0s = sw
|
||||
cx16.r0L = ub
|
||||
cx16.r0H = ub
|
||||
cx16.r0sL = sb
|
||||
cx16.r0sH = sb
|
||||
cx16.r0bL = bb
|
||||
cx16.r0bH = bb
|
||||
|
||||
uw = cx16.r0
|
||||
sw = cx16.r0s
|
||||
ub = cx16.r0L
|
||||
ub = cx16.r0H
|
||||
sb = cx16.r0sL
|
||||
sb = cx16.r0sH
|
||||
bb = cx16.r0bL
|
||||
bb = cx16.r0bH
|
||||
}
|
||||
}"""
|
||||
|
||||
compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
|
||||
compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
|
||||
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
|
||||
compileText(PETTarget(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
|
||||
compileText(C128Target(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("on..goto") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r13L = 1
|
||||
cx16.r12L = 0
|
||||
|
||||
on cx16.r12L+1 call (
|
||||
thing.func1,
|
||||
thing.func2,
|
||||
thing.func3)
|
||||
else {
|
||||
; not jumped
|
||||
cx16.r0++
|
||||
}
|
||||
|
||||
on cx16.r13L+1 goto (thing.func1, thing.func2, thing.func3)
|
||||
}
|
||||
}
|
||||
|
||||
thing {
|
||||
sub func1() {
|
||||
cx16.r10 += 1
|
||||
}
|
||||
sub func2() {
|
||||
cx16.r10 += 2
|
||||
}
|
||||
sub func3() {
|
||||
cx16.r10 += 3
|
||||
}
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=true) shouldNotBe null
|
||||
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=true) shouldNotBe null
|
||||
compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly=true) shouldNotBe null
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
@ -206,7 +206,7 @@ main {
|
||||
}"""
|
||||
val result = compileText(C64Target(), false, src, outputDir, writeAssembly = false)!!.compilerAst
|
||||
val st = result.entrypoint.statements
|
||||
st.size shouldBe 8
|
||||
st.size shouldBe 9
|
||||
st[0] shouldBe instanceOf<VarDecl>()
|
||||
st[1] shouldBe instanceOf<VarDecl>()
|
||||
st[2] shouldBe instanceOf<VarDecl>()
|
||||
|
@ -27,6 +27,7 @@ internal fun compileFile(
|
||||
warnSymbolShadowing = false,
|
||||
quietAll = true,
|
||||
quietAssembler = true,
|
||||
showTimings = false,
|
||||
asmListfile = false,
|
||||
includeSourcelines = false,
|
||||
experimentalCodegen = false,
|
||||
|
@ -38,8 +38,7 @@ main {
|
||||
zz = words[3]
|
||||
}
|
||||
}"""
|
||||
val target = VMTarget()
|
||||
val result = compileText(target, false, src, outputDir, writeAssembly = true)!!
|
||||
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = true)!!
|
||||
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
|
||||
VmRunner().runProgram(virtfile.readText(), false)
|
||||
}
|
||||
@ -58,8 +57,7 @@ main {
|
||||
zz = words[3]
|
||||
}
|
||||
}"""
|
||||
val target = VMTarget()
|
||||
val result = compileText(target, false, src, outputDir, writeAssembly = true)!!
|
||||
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = true)!!
|
||||
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
|
||||
VmRunner().runProgram(virtfile.readText(), false)
|
||||
}
|
||||
@ -107,7 +105,7 @@ test {
|
||||
}
|
||||
}"""
|
||||
val target = VMTarget()
|
||||
var result = compileText(target, false, src, outputDir, writeAssembly = true)!!
|
||||
var result = compileText(VMTarget(), false, src, outputDir, writeAssembly = true)!!
|
||||
var virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
|
||||
VmRunner().runProgram(virtfile.readText(), false)
|
||||
|
||||
|
@ -520,4 +520,20 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
|
||||
override fun visit(alias: Alias) {
|
||||
output("alias ${alias.alias} = ${alias.target.nameInSource.joinToString(".")}")
|
||||
}
|
||||
|
||||
override fun visit(onGoto: OnGoto) {
|
||||
output(if(onGoto.isCall) "selectcall " else "selectgoto ")
|
||||
onGoto.index.accept(this)
|
||||
output(" from (")
|
||||
onGoto.labels.forEachIndexed { idx, label ->
|
||||
label.accept(this)
|
||||
if(idx!=onGoto.labels.lastIndex)
|
||||
output(", ")
|
||||
}
|
||||
outputln(")")
|
||||
if(onGoto.elsepart!=null && onGoto.elsepart.isNotEmpty()) {
|
||||
output(" else ")
|
||||
onGoto.elsepart.accept(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,9 @@ import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.source.SourceCode
|
||||
|
||||
|
||||
@ -135,6 +137,26 @@ interface IStatementContainer {
|
||||
return null
|
||||
}
|
||||
|
||||
fun hasReturnStatement(): Boolean {
|
||||
fun hasReturnStatement(stmt: Statement): Boolean {
|
||||
when(stmt) {
|
||||
is AnonymousScope -> return stmt.statements.any { hasReturnStatement(it) }
|
||||
is ForLoop -> return stmt.body.hasReturnStatement()
|
||||
is IfElse -> return stmt.truepart.hasReturnStatement() || stmt.elsepart.hasReturnStatement()
|
||||
is WhileLoop -> return stmt.body.hasReturnStatement()
|
||||
is RepeatLoop -> return stmt.body.hasReturnStatement()
|
||||
is UntilLoop -> return stmt.body.hasReturnStatement()
|
||||
is When -> return stmt.choices.any { it.statements.hasReturnStatement() }
|
||||
is ConditionalBranch -> return stmt.truepart.hasReturnStatement() || stmt.elsepart.hasReturnStatement()
|
||||
is UnrollLoop -> return stmt.body.hasReturnStatement()
|
||||
is Return -> return true
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
return statements.any { hasReturnStatement(it) }
|
||||
}
|
||||
|
||||
val allDefinedSymbols: Sequence<Pair<String, Statement>>
|
||||
get() {
|
||||
return statements.asSequence().filterIsInstance<INamedStatement>().map { Pair(it.name, it as Statement) }
|
||||
|
@ -1,813 +0,0 @@
|
||||
package prog8.ast.antlr
|
||||
|
||||
import org.antlr.v4.runtime.ParserRuleContext
|
||||
import org.antlr.v4.runtime.Token
|
||||
import org.antlr.v4.runtime.tree.TerminalNode
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.SyntaxError
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.source.SourceCode
|
||||
import prog8.parser.Prog8ANTLRParser.*
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.isRegularFile
|
||||
|
||||
|
||||
/***************** Antlr Extension methods to create AST ****************/
|
||||
|
||||
private data class NumericLiteralNode(val number: Double, val datatype: BaseDataType)
|
||||
|
||||
|
||||
private fun ParserRuleContext.toPosition() : Position {
|
||||
val pathString = start.inputStream.sourceName
|
||||
val filename = if(SourceCode.isRegularFilesystemPath(pathString)) {
|
||||
val path = Path(pathString)
|
||||
if(path.isRegularFile()) {
|
||||
SourceCode.relative(path).toString()
|
||||
} else {
|
||||
path.toString()
|
||||
}
|
||||
} else {
|
||||
pathString
|
||||
}
|
||||
// note: beware of TAB characters in the source text, they count as 1 column...
|
||||
return Position(filename, start.line, start.charPositionInLine+1, start.charPositionInLine + 1 + start.stopIndex - start.startIndex)
|
||||
}
|
||||
|
||||
internal fun BlockContext.toAst(isInLibrary: Boolean) : Block {
|
||||
val blockstatements = block_statement().map {
|
||||
when {
|
||||
it.variabledeclaration()!=null -> it.variabledeclaration().toAst()
|
||||
it.subroutinedeclaration()!=null -> it.subroutinedeclaration().toAst()
|
||||
it.directive()!=null -> it.directive().toAst()
|
||||
it.inlineasm()!=null -> it.inlineasm().toAst()
|
||||
it.inlineir()!=null -> it.inlineir().toAst()
|
||||
it.labeldef()!=null -> it.labeldef().toAst()
|
||||
it.alias()!=null -> it.alias().toAst()
|
||||
else -> throw FatalAstException("weird block node $it")
|
||||
}
|
||||
}
|
||||
return Block(identifier().text, integerliteral()?.toAst()?.number?.toUInt(), blockstatements.toMutableList(), isInLibrary, toPosition())
|
||||
}
|
||||
|
||||
private fun Statement_blockContext.toAst(): MutableList<Statement> =
|
||||
statement().asSequence().map { it.toAst() }.toMutableList()
|
||||
|
||||
private fun VariabledeclarationContext.toAst() : Statement {
|
||||
vardecl()?.let {
|
||||
return it.toAst(VarDeclType.VAR, null)
|
||||
}
|
||||
|
||||
varinitializer()?.let {
|
||||
return it.vardecl().toAst(VarDeclType.VAR, it.expression().toAst())
|
||||
}
|
||||
|
||||
constdecl()?.let {
|
||||
val cvarinit = it.varinitializer()
|
||||
return cvarinit.vardecl().toAst(VarDeclType.CONST, cvarinit.expression().toAst())
|
||||
}
|
||||
|
||||
memoryvardecl()?.let {
|
||||
val mvarinit = it.varinitializer()
|
||||
return mvarinit.vardecl().toAst(VarDeclType.MEMORY, mvarinit.expression().toAst())
|
||||
}
|
||||
|
||||
throw FatalAstException("weird variable decl $this")
|
||||
}
|
||||
|
||||
private fun SubroutinedeclarationContext.toAst() : Subroutine {
|
||||
return when {
|
||||
subroutine()!=null -> subroutine().toAst()
|
||||
asmsubroutine()!=null -> asmsubroutine().toAst()
|
||||
extsubroutine()!=null -> extsubroutine().toAst()
|
||||
else -> throw FatalAstException("weird subroutine decl $this")
|
||||
}
|
||||
}
|
||||
|
||||
private fun StatementContext.toAst() : Statement {
|
||||
val vardecl = variabledeclaration()?.toAst()
|
||||
if(vardecl!=null) return vardecl
|
||||
|
||||
val assignment = assignment()?.toAst()
|
||||
if(assignment!=null) return assignment
|
||||
|
||||
val augassign = augassignment()?.toAst()
|
||||
if(augassign!=null) return augassign
|
||||
|
||||
postincrdecr()?.let {
|
||||
val tgt = it.assign_target().toAst()
|
||||
val operator = it.operator.text
|
||||
val pos = it.toPosition()
|
||||
// print("\u001b[92mINFO\u001B[0m ") // bright green
|
||||
// println("${pos}: ++ and -- will be removed in a future version, please use +=1 or -=1 instead.") // .... if we decode to remove them one day
|
||||
val addSubOne = BinaryExpression(tgt.toExpression(), if(operator=="++") "+" else "-", NumericLiteral.optimalInteger(1, pos), pos)
|
||||
return Assignment(tgt, addSubOne, AssignmentOrigin.USERCODE, pos)
|
||||
}
|
||||
|
||||
val directive = directive()?.toAst()
|
||||
if(directive!=null) return directive
|
||||
|
||||
val label = labeldef()?.toAst()
|
||||
if(label!=null) return label
|
||||
|
||||
val jump = unconditionaljump()?.toAst()
|
||||
if(jump!=null) return jump
|
||||
|
||||
val fcall = functioncall_stmt()?.toAst()
|
||||
if(fcall!=null) return fcall
|
||||
|
||||
val ifstmt = if_stmt()?.toAst()
|
||||
if(ifstmt!=null) return ifstmt
|
||||
|
||||
val returnstmt = returnstmt()?.toAst()
|
||||
if(returnstmt!=null) return returnstmt
|
||||
|
||||
val subroutine = subroutinedeclaration()?.toAst()
|
||||
if(subroutine!=null) return subroutine
|
||||
|
||||
val asm = inlineasm()?.toAst()
|
||||
if(asm!=null) return asm
|
||||
|
||||
val ir = inlineir()?.toAst()
|
||||
if(ir!=null) return ir
|
||||
|
||||
val branchstmt = branch_stmt()?.toAst()
|
||||
if(branchstmt!=null) return branchstmt
|
||||
|
||||
val forloop = forloop()?.toAst()
|
||||
if(forloop!=null) return forloop
|
||||
|
||||
val untilloop = untilloop()?.toAst()
|
||||
if(untilloop!=null) return untilloop
|
||||
|
||||
val whileloop = whileloop()?.toAst()
|
||||
if(whileloop!=null) return whileloop
|
||||
|
||||
val repeatloop = repeatloop()?.toAst()
|
||||
if(repeatloop!=null) return repeatloop
|
||||
|
||||
val whenstmt = whenstmt()?.toAst()
|
||||
if(whenstmt!=null) return whenstmt
|
||||
|
||||
val breakstmt = breakstmt()?.toAst()
|
||||
if(breakstmt!=null) return breakstmt
|
||||
|
||||
val continuestmt = continuestmt()?.toAst()
|
||||
if(continuestmt!=null) return continuestmt
|
||||
|
||||
val unrollstmt = unrollloop()?.toAst()
|
||||
if(unrollstmt!=null) return unrollstmt
|
||||
|
||||
val deferstmt = defer()?.toAst()
|
||||
if(deferstmt!=null) return deferstmt
|
||||
|
||||
val aliasstmt = alias()?.toAst()
|
||||
if(aliasstmt!=null) return aliasstmt
|
||||
|
||||
throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text")
|
||||
}
|
||||
|
||||
private fun AsmsubroutineContext.toAst(): Subroutine {
|
||||
val inline = this.inline()!=null
|
||||
val subdecl = asmsub_decl().toAst()
|
||||
val statements = statement_block()?.toAst() ?: mutableListOf()
|
||||
return Subroutine(subdecl.name, subdecl.parameters.toMutableList(), subdecl.returntypes.toMutableList(),
|
||||
subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
|
||||
subdecl.asmClobbers, null, true, inline, statements = statements, position = toPosition()
|
||||
)
|
||||
}
|
||||
|
||||
private fun ExtsubroutineContext.toAst(): Subroutine {
|
||||
val subdecl = asmsub_decl().toAst()
|
||||
val constbank = constbank?.toAst()?.number?.toUInt()?.toUByte()
|
||||
val varbank = varbank?.toAst()
|
||||
val addr = address.toAst()
|
||||
val address = Subroutine.Address(constbank, varbank, addr)
|
||||
return Subroutine(subdecl.name, subdecl.parameters.toMutableList(), subdecl.returntypes.toMutableList(),
|
||||
subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
|
||||
subdecl.asmClobbers, address, true, inline = false, statements = mutableListOf(), position = toPosition()
|
||||
)
|
||||
}
|
||||
|
||||
private class AsmsubDecl(val name: String,
|
||||
val parameters: List<SubroutineParameter>,
|
||||
val returntypes: List<DataType>,
|
||||
val asmParameterRegisters: List<RegisterOrStatusflag>,
|
||||
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
||||
val asmClobbers: Set<CpuRegister>)
|
||||
|
||||
private fun Asmsub_declContext.toAst(): AsmsubDecl {
|
||||
val name = identifier().text
|
||||
val params = asmsub_params()?.toAst() ?: emptyList()
|
||||
val returns = asmsub_returns()?.toAst() ?: emptyList()
|
||||
val clobbers = asmsub_clobbers()?.clobber()?.toAst() ?: emptySet()
|
||||
val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.zp, it.registerOrPair, it.position) }
|
||||
val normalReturntypes = returns.map { it.type }
|
||||
val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag) }
|
||||
val returnRegisters = returns.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag) }
|
||||
return AsmsubDecl(name, normalParameters, normalReturntypes, paramRegisters, returnRegisters, clobbers)
|
||||
}
|
||||
|
||||
private class AsmSubroutineParameter(name: String,
|
||||
type: DataType,
|
||||
registerOrPair: RegisterOrPair?,
|
||||
val statusflag: Statusflag?,
|
||||
position: Position) : SubroutineParameter(name, type, ZeropageWish.DONTCARE, registerOrPair, position)
|
||||
|
||||
private class AsmSubroutineReturn(val type: DataType,
|
||||
val registerOrPair: RegisterOrPair?,
|
||||
val statusflag: Statusflag?)
|
||||
|
||||
private fun Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn>
|
||||
= asmsub_return().map {
|
||||
val register = it.register.text
|
||||
var registerorpair: RegisterOrPair? = null
|
||||
var statusregister: Statusflag? = null
|
||||
if(register!=null) {
|
||||
when (register) {
|
||||
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(register)
|
||||
in Statusflag.names -> statusregister = Statusflag.valueOf(register)
|
||||
else -> throw SyntaxError("invalid register or status flag", toPosition())
|
||||
}
|
||||
}
|
||||
// asmsubs currently only return a base datatype
|
||||
val returnBaseDt = it.datatype().toAst()
|
||||
AsmSubroutineReturn(
|
||||
DataType.forDt(returnBaseDt),
|
||||
registerorpair,
|
||||
statusregister)
|
||||
}
|
||||
|
||||
private fun Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter> = asmsub_param().map {
|
||||
val vardecl = it.vardecl()
|
||||
val baseDt = vardecl.datatype()?.toAst() ?: BaseDataType.UNDEFINED
|
||||
var datatype = DataType.forDt(baseDt)
|
||||
if(vardecl.ARRAYSIG()!=null || vardecl.arrayindex()!=null)
|
||||
datatype = datatype.elementToArray()
|
||||
val (registerorpair, statusregister) = parseParamRegister(it.register, it.toPosition())
|
||||
val identifiers = vardecl.identifier()
|
||||
if(identifiers.size>1)
|
||||
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
|
||||
val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME()
|
||||
AsmSubroutineParameter(identifiername.text, datatype, registerorpair, statusregister, toPosition())
|
||||
}
|
||||
|
||||
private fun parseParamRegister(registerTok: Token?, pos: Position): Pair<RegisterOrPair?, Statusflag?> {
|
||||
if(registerTok==null)
|
||||
return Pair(null, null)
|
||||
val register = registerTok.text
|
||||
var registerorpair: RegisterOrPair? = null
|
||||
var statusregister: Statusflag? = null
|
||||
if(register!=null) {
|
||||
when (register) {
|
||||
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(register)
|
||||
in Statusflag.names -> statusregister = Statusflag.valueOf(register)
|
||||
else -> {
|
||||
throw SyntaxError("invalid register or status flag", Position(pos.file, registerTok.line, registerTok.charPositionInLine, registerTok.charPositionInLine+1))
|
||||
}
|
||||
}
|
||||
}
|
||||
return Pair(registerorpair, statusregister)
|
||||
}
|
||||
|
||||
private fun Functioncall_stmtContext.toAst(): Statement {
|
||||
val void = this.VOID() != null
|
||||
val location = scoped_identifier().toAst()
|
||||
return if(expression_list() == null)
|
||||
FunctionCallStatement(location, mutableListOf(), void, toPosition())
|
||||
else
|
||||
FunctionCallStatement(location, expression_list().toAst().toMutableList(), void, toPosition())
|
||||
}
|
||||
|
||||
private fun FunctioncallContext.toAst(): FunctionCallExpression {
|
||||
val location = scoped_identifier().toAst()
|
||||
return if(expression_list() == null)
|
||||
FunctionCallExpression(location, mutableListOf(), toPosition())
|
||||
else
|
||||
FunctionCallExpression(location, expression_list().toAst().toMutableList(), toPosition())
|
||||
}
|
||||
|
||||
private fun InlineasmContext.toAst(): InlineAssembly {
|
||||
val text = INLINEASMBLOCK().text
|
||||
return InlineAssembly(text.substring(2, text.length-2), false, toPosition())
|
||||
}
|
||||
|
||||
private fun InlineirContext.toAst(): InlineAssembly {
|
||||
val text = INLINEASMBLOCK().text
|
||||
return InlineAssembly(text.substring(2, text.length-2), true, toPosition())
|
||||
}
|
||||
|
||||
private fun ReturnstmtContext.toAst() : Return {
|
||||
val values = if(returnvalues()==null || returnvalues().expression().isEmpty()) arrayOf() else returnvalues().expression().map { it.toAst() }.toTypedArray()
|
||||
return Return(values, toPosition())
|
||||
}
|
||||
|
||||
private fun UnconditionaljumpContext.toAst(): Jump {
|
||||
return Jump(expression().toAst(), toPosition())
|
||||
}
|
||||
|
||||
private fun LabeldefContext.toAst(): Statement =
|
||||
Label(children[0].text, toPosition())
|
||||
|
||||
private fun AliasContext.toAst(): Statement =
|
||||
Alias(identifier().text, scoped_identifier().toAst(), toPosition())
|
||||
|
||||
private fun SubroutineContext.toAst() : Subroutine {
|
||||
// non-asm subroutine
|
||||
val returntypes = sub_return_part()?.datatype()?.map { it.toAst() } ?: emptyList()
|
||||
return Subroutine(
|
||||
identifier().text,
|
||||
sub_params()?.toAst()?.toMutableList() ?: mutableListOf(),
|
||||
returntypes.map { DataType.forDt(it) }.toMutableList(),
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
emptySet(),
|
||||
asmAddress = null,
|
||||
isAsmSubroutine = false,
|
||||
inline = false,
|
||||
statements = statement_block()?.toAst() ?: mutableListOf(),
|
||||
position = toPosition()
|
||||
)
|
||||
}
|
||||
|
||||
private fun Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
||||
sub_param().map {
|
||||
val decl = it.vardecl()
|
||||
val tags = decl.TAG().map { t -> t.text }
|
||||
val validTags = arrayOf("@zp", "@requirezp", "@nozp", "@split", "@nosplit", "@shared")
|
||||
for(tag in tags) {
|
||||
if(tag !in validTags)
|
||||
throw SyntaxError("invalid parameter tag '$tag'", toPosition())
|
||||
}
|
||||
val zp = getZpOption(tags)
|
||||
val baseDt = decl.datatype()?.toAst() ?: BaseDataType.UNDEFINED
|
||||
var datatype = DataType.forDt(baseDt)
|
||||
if(decl.ARRAYSIG()!=null || decl.arrayindex()!=null)
|
||||
datatype = datatype.elementToArray()
|
||||
|
||||
val identifiers = decl.identifier()
|
||||
if(identifiers.size>1)
|
||||
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
|
||||
val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME()
|
||||
|
||||
val (registerorpair, statusregister) = parseParamRegister(it.register, it.toPosition())
|
||||
if(statusregister!=null) {
|
||||
throw SyntaxError("can't use status register as param for normal subroutines", Position(toPosition().file, it.register.line, it.register.charPositionInLine, it.register.charPositionInLine+1))
|
||||
}
|
||||
SubroutineParameter(identifiername.text, datatype, zp, registerorpair, it.toPosition())
|
||||
}
|
||||
|
||||
private fun getZpOption(tags: List<String>): ZeropageWish = when {
|
||||
"@requirezp" in tags -> ZeropageWish.REQUIRE_ZEROPAGE
|
||||
"@zp" in tags -> ZeropageWish.PREFER_ZEROPAGE
|
||||
"@nozp" in tags -> ZeropageWish.NOT_IN_ZEROPAGE
|
||||
else -> ZeropageWish.DONTCARE
|
||||
}
|
||||
|
||||
private fun getSplitOption(tags: List<String>): SplitWish {
|
||||
return when {
|
||||
"@nosplit" in tags -> SplitWish.NOSPLIT
|
||||
"@split" in tags -> SplitWish.SPLIT
|
||||
else -> SplitWish.DONTCARE
|
||||
}
|
||||
}
|
||||
|
||||
private fun Assign_targetContext.toAst() : AssignTarget {
|
||||
return when(this) {
|
||||
is IdentifierTargetContext -> {
|
||||
val identifier = scoped_identifier().toAst()
|
||||
AssignTarget(identifier, null, null, null, false, scoped_identifier().toPosition())
|
||||
}
|
||||
is MemoryTargetContext ->
|
||||
AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(), directmemory().toPosition()), null, false, toPosition())
|
||||
is ArrayindexedTargetContext -> {
|
||||
val ax = arrayindexed()
|
||||
val arrayvar = ax.scoped_identifier().toAst()
|
||||
val index = ax.arrayindex().toAst()
|
||||
val arrayindexed = ArrayIndexedExpression(arrayvar, index, ax.toPosition())
|
||||
AssignTarget(null, arrayindexed, null, null, false, toPosition())
|
||||
}
|
||||
is VoidTargetContext -> {
|
||||
AssignTarget(null, null, null, null, true, void_().toPosition())
|
||||
}
|
||||
else -> throw FatalAstException("weird assign target node $this")
|
||||
}
|
||||
}
|
||||
|
||||
private fun Multi_assign_targetContext.toAst() : AssignTarget {
|
||||
val targets = this.assign_target().map { it.toAst() }
|
||||
return AssignTarget(null, null, null, targets, false, toPosition())
|
||||
}
|
||||
|
||||
private fun ClobberContext.toAst() : Set<CpuRegister> {
|
||||
val names = this.NAME().map { it.text }
|
||||
try {
|
||||
return names.map { CpuRegister.valueOf(it) }.toSet()
|
||||
} catch(_: IllegalArgumentException) {
|
||||
throw SyntaxError("invalid cpu register", toPosition())
|
||||
}
|
||||
}
|
||||
|
||||
private fun AssignmentContext.toAst(): Statement {
|
||||
val multiAssign = multi_assign_target()
|
||||
if(multiAssign!=null) {
|
||||
return Assignment(multiAssign.toAst(), expression().toAst(), AssignmentOrigin.USERCODE, toPosition())
|
||||
}
|
||||
|
||||
val nestedAssign = assignment()
|
||||
return if(nestedAssign==null)
|
||||
Assignment(assign_target().toAst(), expression().toAst(), AssignmentOrigin.USERCODE, toPosition())
|
||||
else
|
||||
ChainedAssignment(assign_target().toAst(), nestedAssign.toAst(), toPosition())
|
||||
}
|
||||
|
||||
private fun AugassignmentContext.toAst(): Assignment {
|
||||
// replace A += X with A = A + X
|
||||
val target = assign_target().toAst()
|
||||
val oper = operator.text.substringBefore('=')
|
||||
val expression = BinaryExpression(target.toExpression(), oper, expression().toAst(), expression().toPosition())
|
||||
return Assignment(assign_target().toAst(), expression, AssignmentOrigin.USERCODE, toPosition())
|
||||
}
|
||||
|
||||
private fun DatatypeContext.toAst(): BaseDataType {
|
||||
return try {
|
||||
BaseDataType.valueOf(text.uppercase())
|
||||
} catch (_: IllegalArgumentException) {
|
||||
BaseDataType.UNDEFINED
|
||||
}
|
||||
}
|
||||
|
||||
private fun ArrayindexContext.toAst() : ArrayIndex =
|
||||
ArrayIndex(expression().toAst(), toPosition())
|
||||
|
||||
internal fun DirectiveContext.toAst() : Directive {
|
||||
if(directivenamelist() != null) {
|
||||
val identifiers = directivenamelist().scoped_identifier().map { DirectiveArg(it.text, null, it.toPosition()) }
|
||||
return Directive(directivename.text, identifiers, toPosition())
|
||||
}
|
||||
else
|
||||
return Directive(directivename.text, directivearg().map { it.toAst() }, toPosition())
|
||||
}
|
||||
|
||||
private fun DirectiveargContext.toAst() : DirectiveArg {
|
||||
val str = stringliteral()
|
||||
if(str!=null) {
|
||||
if (str.encoding?.text != null)
|
||||
throw SyntaxError("don't use a string encoding for directive arguments", toPosition())
|
||||
return DirectiveArg(str.text.substring(1, text.length-1), integerliteral()?.toAst()?.number?.toUInt(), toPosition())
|
||||
}
|
||||
|
||||
return DirectiveArg(identifier()?.text, integerliteral()?.toAst()?.number?.toUInt(), toPosition())
|
||||
}
|
||||
|
||||
private fun IntegerliteralContext.toAst(): NumericLiteralNode {
|
||||
fun makeLiteral(literalTextWithGrouping: String, radix: Int): NumericLiteralNode {
|
||||
val literalText = literalTextWithGrouping.replace("_", "")
|
||||
val integer: Int
|
||||
var datatype = BaseDataType.UBYTE
|
||||
when (radix) {
|
||||
10 -> {
|
||||
integer = try {
|
||||
literalText.toInt()
|
||||
} catch(x: NumberFormatException) {
|
||||
throw SyntaxError("invalid decimal literal ${x.message}", toPosition())
|
||||
}
|
||||
datatype = when(integer) {
|
||||
in 0..255 -> BaseDataType.UBYTE
|
||||
in -128..127 -> BaseDataType.BYTE
|
||||
in 0..65535 -> BaseDataType.UWORD
|
||||
in -32768..32767 -> BaseDataType.WORD
|
||||
in -2147483647..2147483647 -> BaseDataType.LONG
|
||||
else -> BaseDataType.FLOAT
|
||||
}
|
||||
}
|
||||
2 -> {
|
||||
if(literalText.length>16)
|
||||
datatype = BaseDataType.LONG
|
||||
else if(literalText.length>8)
|
||||
datatype = BaseDataType.UWORD
|
||||
try {
|
||||
integer = literalText.toInt(2)
|
||||
} catch(x: NumberFormatException) {
|
||||
throw SyntaxError("invalid binary literal ${x.message}", toPosition())
|
||||
}
|
||||
}
|
||||
16 -> {
|
||||
if(literalText.length>4)
|
||||
datatype = BaseDataType.LONG
|
||||
else if(literalText.length>2)
|
||||
datatype = BaseDataType.UWORD
|
||||
try {
|
||||
integer = literalText.toInt(16)
|
||||
} catch(x: NumberFormatException) {
|
||||
throw SyntaxError("invalid hexadecimal literal ${x.message}", toPosition())
|
||||
}
|
||||
}
|
||||
else -> throw FatalAstException("invalid radix")
|
||||
}
|
||||
return NumericLiteralNode(integer.toDouble(), datatype)
|
||||
}
|
||||
val terminal: TerminalNode = children[0] as TerminalNode
|
||||
val integerPart = this.intpart.text
|
||||
return when (terminal.symbol.type) {
|
||||
DEC_INTEGER -> makeLiteral(integerPart, 10)
|
||||
HEX_INTEGER -> makeLiteral(integerPart.substring(1), 16)
|
||||
BIN_INTEGER -> makeLiteral(integerPart.substring(1), 2)
|
||||
else -> throw FatalAstException(terminal.text)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ExpressionContext.toAst(insideParentheses: Boolean=false) : Expression {
|
||||
|
||||
val litval = literalvalue()
|
||||
if(litval!=null) {
|
||||
val booleanlit = litval.booleanliteral()?.toAst()
|
||||
return if(booleanlit!=null) {
|
||||
NumericLiteral.fromBoolean(booleanlit, litval.toPosition())
|
||||
}
|
||||
else {
|
||||
val intLit = litval.integerliteral()?.toAst()
|
||||
when {
|
||||
intLit!=null -> when(intLit.datatype) {
|
||||
BaseDataType.UBYTE -> NumericLiteral(BaseDataType.UBYTE, intLit.number, litval.toPosition())
|
||||
BaseDataType.BYTE -> NumericLiteral(BaseDataType.BYTE, intLit.number, litval.toPosition())
|
||||
BaseDataType.UWORD -> NumericLiteral(BaseDataType.UWORD, intLit.number, litval.toPosition())
|
||||
BaseDataType.WORD -> NumericLiteral(BaseDataType.WORD, intLit.number, litval.toPosition())
|
||||
BaseDataType.LONG -> NumericLiteral(BaseDataType.LONG, intLit.number, litval.toPosition())
|
||||
BaseDataType.FLOAT -> NumericLiteral(BaseDataType.FLOAT, intLit.number, litval.toPosition())
|
||||
else -> throw FatalAstException("invalid datatype for numeric literal")
|
||||
}
|
||||
litval.floatliteral()!=null -> NumericLiteral(BaseDataType.FLOAT, litval.floatliteral().toAst(), litval.toPosition())
|
||||
litval.stringliteral()!=null -> litval.stringliteral().toAst()
|
||||
litval.charliteral()!=null -> litval.charliteral().toAst()
|
||||
litval.arrayliteral()!=null -> {
|
||||
val array = litval.arrayliteral().toAst()
|
||||
// the actual type of the arraysize can not yet be determined here
|
||||
// the ConstantFold takes care of that and converts the type if needed.
|
||||
ArrayLiteral(InferredTypes.InferredType.unknown(), array, position = litval.toPosition())
|
||||
}
|
||||
else -> throw FatalAstException("invalid parsed literal")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(arrayindexed()!=null) {
|
||||
val ax = arrayindexed()
|
||||
val identifier = ax.scoped_identifier().toAst()
|
||||
val index = ax.arrayindex().toAst()
|
||||
return ArrayIndexedExpression(identifier, index, ax.toPosition())
|
||||
}
|
||||
|
||||
if(scoped_identifier()!=null)
|
||||
return scoped_identifier().toAst()
|
||||
|
||||
if(bop!=null) {
|
||||
val operator = bop.text.trim().replace("\\s+".toRegex(), " ")
|
||||
return BinaryExpression(
|
||||
left.toAst(),
|
||||
operator,
|
||||
right.toAst(),
|
||||
toPosition(),
|
||||
insideParentheses = insideParentheses
|
||||
)
|
||||
}
|
||||
|
||||
if(prefix!=null)
|
||||
return PrefixExpression(prefix.text, expression(0).toAst(), toPosition())
|
||||
|
||||
val funcall = functioncall()?.toAst()
|
||||
if(funcall!=null) return funcall
|
||||
|
||||
if (rangefrom!=null && rangeto!=null) {
|
||||
val defaultstep = if(rto.text == "to") 1 else -1
|
||||
val step = rangestep?.toAst() ?: NumericLiteral.optimalInteger(defaultstep, toPosition())
|
||||
return RangeExpression(rangefrom.toAst(), rangeto.toAst(), step, toPosition())
|
||||
}
|
||||
|
||||
if(childCount==3 && children[0].text=="(" && children[2].text==")")
|
||||
return expression(0).toAst(insideParentheses=true) // expression within ( )
|
||||
|
||||
if(typecast()!=null) {
|
||||
// typecast is always to a base datatype
|
||||
val baseDt = typecast().datatype().toAst()
|
||||
return TypecastExpression(expression(0).toAst(), baseDt, false, toPosition())
|
||||
}
|
||||
|
||||
if(directmemory()!=null)
|
||||
return DirectMemoryRead(directmemory().expression().toAst(), toPosition())
|
||||
|
||||
if(addressof()!=null) {
|
||||
val addressOf = addressof()
|
||||
val identifier = addressOf.scoped_identifier()
|
||||
val msb = addressOf.ADDRESS_OF_MSB()!=null
|
||||
// note: &< (ADDRESS_OF_LSB) is equivalent to a regular &.
|
||||
return if (identifier != null)
|
||||
AddressOf(addressof().scoped_identifier().toAst(), null, msb, toPosition())
|
||||
else {
|
||||
val array = addressOf.arrayindexed()
|
||||
AddressOf(array.scoped_identifier().toAst(), array.arrayindex().toAst(), msb, toPosition())
|
||||
}
|
||||
}
|
||||
|
||||
if(if_expression()!=null) {
|
||||
val ifex = if_expression()
|
||||
val (condition, truevalue, falsevalue) = ifex.expression()
|
||||
return IfExpression(condition.toAst(), truevalue.toAst(), falsevalue.toAst(), toPosition())
|
||||
}
|
||||
|
||||
throw FatalAstException(text)
|
||||
}
|
||||
|
||||
private fun CharliteralContext.toAst(): CharLiteral {
|
||||
val text = this.SINGLECHAR().text
|
||||
val enc = this.encoding?.text
|
||||
val encoding =
|
||||
if(enc!=null)
|
||||
Encoding.entries.singleOrNull { it.prefix == enc }
|
||||
?: throw SyntaxError("invalid encoding", toPosition())
|
||||
else
|
||||
Encoding.DEFAULT
|
||||
val raw = text.substring(1, text.length - 1)
|
||||
try {
|
||||
return CharLiteral.fromEscaped(raw, encoding, toPosition())
|
||||
} catch(ex: IllegalArgumentException) {
|
||||
throw SyntaxError(ex.message!!, toPosition())
|
||||
}
|
||||
}
|
||||
|
||||
private fun StringliteralContext.toAst(): StringLiteral {
|
||||
val text=this.STRING().text
|
||||
val enc = encoding?.text
|
||||
val encoding =
|
||||
if(enc!=null)
|
||||
Encoding.entries.singleOrNull { it.prefix == enc }
|
||||
?: throw SyntaxError("invalid encoding", toPosition())
|
||||
else
|
||||
Encoding.DEFAULT
|
||||
val raw = text.substring(1, text.length-1)
|
||||
try {
|
||||
return StringLiteral.fromEscaped(raw, encoding, toPosition())
|
||||
} catch(ex: IllegalArgumentException) {
|
||||
throw SyntaxError(ex.message!!, toPosition())
|
||||
}
|
||||
}
|
||||
|
||||
private fun Expression_listContext.toAst() = expression().map{ it.toAst() }
|
||||
|
||||
private fun Scoped_identifierContext.toAst() : IdentifierReference {
|
||||
return IdentifierReference(identifier().map { it.text }, toPosition())
|
||||
}
|
||||
|
||||
private fun FloatliteralContext.toAst() = text.replace("_","").toDouble()
|
||||
|
||||
private fun BooleanliteralContext.toAst() = when(text) {
|
||||
"true" -> true
|
||||
"false" -> false
|
||||
else -> throw FatalAstException(text)
|
||||
}
|
||||
|
||||
private fun ArrayliteralContext.toAst() : Array<Expression> =
|
||||
expression().map { it.toAst() }.toTypedArray()
|
||||
|
||||
private fun If_stmtContext.toAst(): IfElse {
|
||||
val condition = expression().toAst()
|
||||
val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
val elseStatements = else_part()?.toAst() ?: mutableListOf()
|
||||
val trueScope = AnonymousScope(trueStatements, statement_block()?.toPosition()
|
||||
?: statement().toPosition())
|
||||
val elseScope = AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition())
|
||||
return IfElse(condition, trueScope, elseScope, toPosition())
|
||||
}
|
||||
|
||||
private fun Else_partContext.toAst(): MutableList<Statement> {
|
||||
return statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
}
|
||||
|
||||
private fun Branch_stmtContext.toAst(): ConditionalBranch {
|
||||
val branchcondition = branchcondition().toAst()
|
||||
val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
val elseStatements = else_part()?.toAst() ?: mutableListOf()
|
||||
val trueScope = AnonymousScope(trueStatements, statement_block()?.toPosition()
|
||||
?: statement().toPosition())
|
||||
val elseScope = AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition())
|
||||
return ConditionalBranch(branchcondition, trueScope, elseScope, toPosition())
|
||||
}
|
||||
|
||||
private fun BranchconditionContext.toAst() = BranchCondition.valueOf(
|
||||
text.substringAfter('_').uppercase()
|
||||
)
|
||||
|
||||
private fun ForloopContext.toAst(): ForLoop {
|
||||
val loopvar = scoped_identifier().toAst()
|
||||
val iterable = expression()!!.toAst()
|
||||
val scope =
|
||||
if(statement()!=null)
|
||||
AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition())
|
||||
else
|
||||
AnonymousScope(statement_block().toAst(), statement_block().toPosition())
|
||||
return ForLoop(loopvar, iterable, scope, toPosition())
|
||||
}
|
||||
|
||||
private fun BreakstmtContext.toAst() = Break(toPosition())
|
||||
|
||||
private fun ContinuestmtContext.toAst() = Continue(toPosition())
|
||||
|
||||
private fun DeferContext.toAst(): Defer {
|
||||
val block = statement_block()?.toAst()
|
||||
if(block!=null) {
|
||||
val scope = AnonymousScope(block, statement_block()?.toPosition() ?: toPosition())
|
||||
return Defer(scope, toPosition())
|
||||
}
|
||||
val singleStmt = statement()!!.toAst()
|
||||
val scope = AnonymousScope(mutableListOf(singleStmt), statement().toPosition())
|
||||
return Defer(scope, toPosition())
|
||||
}
|
||||
|
||||
private fun WhileloopContext.toAst(): WhileLoop {
|
||||
val condition = expression().toAst()
|
||||
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
val scope = AnonymousScope(statements, statement_block()?.toPosition()
|
||||
?: statement().toPosition())
|
||||
return WhileLoop(condition, scope, toPosition())
|
||||
}
|
||||
|
||||
private fun RepeatloopContext.toAst(): RepeatLoop {
|
||||
val iterations = expression()?.toAst()
|
||||
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
val scope = AnonymousScope(statements, statement_block()?.toPosition()
|
||||
?: statement().toPosition())
|
||||
return RepeatLoop(iterations, scope, toPosition())
|
||||
}
|
||||
|
||||
private fun UnrollloopContext.toAst(): UnrollLoop {
|
||||
val iterations = expression().toAst()
|
||||
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
val scope = AnonymousScope(statements, statement_block()?.toPosition()
|
||||
?: statement().toPosition())
|
||||
return UnrollLoop(iterations, scope, toPosition())
|
||||
}
|
||||
|
||||
private fun UntilloopContext.toAst(): UntilLoop {
|
||||
val untilCondition = expression().toAst()
|
||||
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
val scope = AnonymousScope(statements, statement_block()?.toPosition()
|
||||
?: statement().toPosition())
|
||||
return UntilLoop(scope, untilCondition, toPosition())
|
||||
}
|
||||
|
||||
private fun WhenstmtContext.toAst(): When {
|
||||
val condition = expression().toAst()
|
||||
val choices = this.when_choice()?.map { it.toAst() }?.toMutableList() ?: mutableListOf()
|
||||
return When(condition, choices, toPosition())
|
||||
}
|
||||
|
||||
private fun When_choiceContext.toAst(): WhenChoice {
|
||||
val values = expression_list()?.toAst()
|
||||
val stmt = statement()?.toAst()
|
||||
val stmtBlock = statement_block()?.toAst()?.toMutableList() ?: mutableListOf()
|
||||
if(stmt!=null)
|
||||
stmtBlock.add(stmt)
|
||||
val scope = AnonymousScope(stmtBlock, toPosition())
|
||||
return WhenChoice(values?.toMutableList(), scope, toPosition())
|
||||
}
|
||||
|
||||
private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl {
|
||||
val tags = TAG().map { it.text }
|
||||
val validTags = arrayOf("@zp", "@requirezp", "@nozp", "@split", "@nosplit", "@shared", "@alignword", "@alignpage", "@align64", "@dirty")
|
||||
for(tag in tags) {
|
||||
if(tag !in validTags)
|
||||
throw SyntaxError("invalid variable tag '$tag'", toPosition())
|
||||
}
|
||||
val zp = getZpOption(tags)
|
||||
val split = getSplitOption(tags)
|
||||
val identifiers = identifier()
|
||||
val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME()
|
||||
val name = if(identifiers.size==1) identifiername.text else "<multiple>"
|
||||
val isArray = ARRAYSIG() != null || arrayindex() != null
|
||||
val alignword = "@alignword" in tags
|
||||
val align64 = "@align64" in tags
|
||||
val alignpage = "@alignpage" in tags
|
||||
if(alignpage && alignword)
|
||||
throw SyntaxError("choose a single alignment option", toPosition())
|
||||
val baseDt = datatype()?.toAst() ?: BaseDataType.UNDEFINED
|
||||
val dt = if(isArray) DataType.arrayFor(baseDt, split!=SplitWish.NOSPLIT) else DataType.forDt(baseDt)
|
||||
|
||||
return VarDecl(
|
||||
type, VarDeclOrigin.USERCODE,
|
||||
dt,
|
||||
zp,
|
||||
split,
|
||||
arrayindex()?.toAst(),
|
||||
name,
|
||||
if(identifiers.size==1) emptyList() else identifiers.map {
|
||||
val idname = it.NAME() ?: it.UNDERSCORENAME()
|
||||
idname.text
|
||||
},
|
||||
value,
|
||||
"@shared" in tags,
|
||||
if(alignword) 2u else if(align64) 64u else if(alignpage) 256u else 0u,
|
||||
"@dirty" in tags,
|
||||
toPosition()
|
||||
)
|
||||
}
|
750
compilerAst/src/prog8/ast/antlr/Antlr2KotlinVisitor.kt
Normal file
750
compilerAst/src/prog8/ast/antlr/Antlr2KotlinVisitor.kt
Normal file
@ -0,0 +1,750 @@
|
||||
package prog8.ast.antlr
|
||||
|
||||
import org.antlr.v4.runtime.ParserRuleContext
|
||||
import org.antlr.v4.runtime.Token
|
||||
import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor
|
||||
import org.antlr.v4.runtime.tree.TerminalNode
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.SyntaxError
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.source.SourceCode
|
||||
import prog8.parser.Prog8ANTLRParser.*
|
||||
import prog8.parser.Prog8ANTLRVisitor
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.isRegularFile
|
||||
|
||||
|
||||
class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node>(), Prog8ANTLRVisitor<Node> {
|
||||
|
||||
override fun visitModule(ctx: ModuleContext): Module {
|
||||
val statements = ctx.module_element().map { it.accept(this) as Statement }
|
||||
return Module(statements.toMutableList(), ctx.toPosition(), source)
|
||||
}
|
||||
|
||||
override fun visitBlock(ctx: BlockContext): Block {
|
||||
val name = getname(ctx.identifier())
|
||||
val address = (ctx.integerliteral()?.accept(this) as NumericLiteral?)?.number?.toUInt()
|
||||
val statements = ctx.block_statement().map { it.accept(this) as Statement }
|
||||
return Block(name, address, statements.toMutableList(), source.isFromLibrary, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitExpression(ctx: ExpressionContext): Expression {
|
||||
if(ctx.sizeof_expression!=null) {
|
||||
val sdt = ctx.sizeof_argument().datatype()
|
||||
val datatype = if(sdt!=null) baseDatatypeFor(sdt) else null
|
||||
val expression = ctx.sizeof_argument().expression()?.accept(this) as Expression?
|
||||
val sizeof = IdentifierReference(listOf("sizeof"), ctx.toPosition())
|
||||
val arg = if (expression != null) expression else {
|
||||
require(datatype != null)
|
||||
IdentifierReference(listOf(datatype.name.lowercase()), ctx.toPosition())
|
||||
}
|
||||
return FunctionCallExpression(sizeof, mutableListOf(arg), ctx.toPosition())
|
||||
}
|
||||
|
||||
if(ctx.bop!=null) {
|
||||
val operator = ctx.bop.text.trim().replace("\\s+".toRegex(), " ")
|
||||
return BinaryExpression(
|
||||
ctx.left.accept(this) as Expression,
|
||||
operator,
|
||||
ctx.right.accept(this) as Expression,
|
||||
ctx.toPosition()
|
||||
)
|
||||
}
|
||||
|
||||
if(ctx.prefix!=null) {
|
||||
return PrefixExpression(ctx.prefix.text, ctx.expression(0).accept(this) as Expression, ctx.toPosition())
|
||||
}
|
||||
|
||||
if(ctx.rangefrom!=null && ctx.rangeto!=null) {
|
||||
val defaultstep = if(ctx.rto.text == "to") 1 else -1
|
||||
return RangeExpression(
|
||||
ctx.rangefrom.accept(this) as Expression,
|
||||
ctx.rangeto.accept(this) as Expression,
|
||||
ctx.rangestep?.accept(this) as Expression? ?: NumericLiteral.optimalInteger(defaultstep, ctx.toPosition()),
|
||||
ctx.toPosition())
|
||||
}
|
||||
|
||||
if(ctx.typecast()!=null) {
|
||||
// typecast is always to a base datatype
|
||||
val baseDt = baseDatatypeFor(ctx.typecast().datatype())
|
||||
return TypecastExpression(ctx.expression(0).accept(this) as Expression, baseDt, false, ctx.toPosition())
|
||||
}
|
||||
|
||||
if(ctx.childCount==3 && ctx.children[0].text=="(" && ctx.children[2].text==")")
|
||||
return ctx.expression(0).accept(this) as Expression // expression within ( )
|
||||
|
||||
return visitChildren(ctx) as Expression
|
||||
}
|
||||
|
||||
override fun visitSubroutinedeclaration(ctx: SubroutinedeclarationContext): Subroutine {
|
||||
if(ctx.subroutine()!=null)
|
||||
return ctx.subroutine().accept(this) as Subroutine
|
||||
if(ctx.asmsubroutine()!=null)
|
||||
return ctx.asmsubroutine().accept(this) as Subroutine
|
||||
if(ctx.extsubroutine()!=null)
|
||||
return ctx.extsubroutine().accept(this) as Subroutine
|
||||
throw FatalAstException("weird subroutine")
|
||||
}
|
||||
|
||||
override fun visitAlias(ctx: AliasContext): Alias {
|
||||
val identifier = getname(ctx.identifier())
|
||||
val target = ctx.scoped_identifier().accept(this) as IdentifierReference
|
||||
return Alias(identifier, target, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitDefer(ctx: DeferContext): Defer {
|
||||
val statements = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
|
||||
return Defer(statements, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitLabeldef(ctx: LabeldefContext): Label {
|
||||
return Label(getname(ctx.identifier()), ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitUnconditionaljump(ctx: UnconditionaljumpContext): Jump {
|
||||
return Jump(ctx.expression().accept(this) as Expression, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitDirective(ctx: DirectiveContext): Directive {
|
||||
if(ctx.directivenamelist() != null) {
|
||||
val namelist = ctx.directivenamelist().scoped_identifier().map { it.accept(this) as IdentifierReference }
|
||||
val identifiers = namelist.map { DirectiveArg(it.nameInSource.joinToString("."), null, ctx.toPosition()) }
|
||||
return Directive(ctx.directivename.text, identifiers, ctx.toPosition())
|
||||
}
|
||||
else
|
||||
return Directive(ctx.directivename.text, ctx.directivearg().map { it.accept(this) as DirectiveArg }, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitDirectivearg(ctx: DirectiveargContext): DirectiveArg {
|
||||
val integer = (ctx.integerliteral()?.accept(this) as NumericLiteral?)?.number?.toUInt()
|
||||
val str = ctx.stringliteral()
|
||||
if(str!=null) {
|
||||
if (str.encoding?.text != null)
|
||||
throw SyntaxError("don't use a string encoding for directive arguments", ctx.toPosition())
|
||||
return DirectiveArg(str.text.substring(1, str.text.length-1), integer, ctx.toPosition())
|
||||
}
|
||||
val identifier = ctx.identifier()?.accept(this) as IdentifierReference?
|
||||
return DirectiveArg(identifier?.nameInSource?.single(), integer, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitVardecl(ctx: VardeclContext): VarDecl {
|
||||
val tags = ctx.TAG().map { it.text }
|
||||
val validTags = arrayOf("@zp", "@requirezp", "@nozp", "@split", "@nosplit", "@shared", "@alignword", "@alignpage", "@align64", "@dirty")
|
||||
for(tag in tags) {
|
||||
if(tag !in validTags)
|
||||
throw SyntaxError("invalid variable tag '$tag'", ctx.toPosition())
|
||||
}
|
||||
val zp = getZpOption(tags)
|
||||
val split = getSplitOption(tags)
|
||||
val alignword = "@alignword" in tags
|
||||
val align64 = "@align64" in tags
|
||||
val alignpage = "@alignpage" in tags
|
||||
if(alignpage && alignword)
|
||||
throw SyntaxError("choose a single alignment option", ctx.toPosition())
|
||||
|
||||
val identifiers = ctx.identifier().map { getname(it) }
|
||||
val identifiername = identifiers[0]
|
||||
val name = if(identifiers.size==1) identifiername else "<multiple>"
|
||||
|
||||
val arrayIndex = ctx.arrayindex()?.accept(this) as ArrayIndex?
|
||||
val isArray = ctx.ARRAYSIG() != null || arrayIndex != null
|
||||
val baseDt = baseDatatypeFor(ctx.datatype())
|
||||
val dt = if(isArray) DataType.arrayFor(baseDt, split!=SplitWish.NOSPLIT) else DataType.forDt(baseDt)
|
||||
|
||||
return VarDecl(
|
||||
VarDeclType.VAR, // can be changed to MEMORY or CONST as required
|
||||
VarDeclOrigin.USERCODE,
|
||||
dt,
|
||||
zp,
|
||||
split,
|
||||
arrayIndex,
|
||||
name,
|
||||
if(identifiers.size==1) emptyList() else identifiers,
|
||||
null,
|
||||
"@shared" in tags,
|
||||
if(alignword) 2u else if(align64) 64u else if(alignpage) 256u else 0u,
|
||||
"@dirty" in tags,
|
||||
ctx.toPosition()
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitVarinitializer(ctx: VarinitializerContext): VarDecl {
|
||||
val vardecl = ctx.vardecl().accept(this) as VarDecl
|
||||
vardecl.value = ctx.expression().accept(this) as Expression
|
||||
return vardecl
|
||||
}
|
||||
|
||||
override fun visitConstdecl(ctx: ConstdeclContext): VarDecl {
|
||||
val vardecl = ctx.varinitializer().accept(this) as VarDecl
|
||||
vardecl.type = VarDeclType.CONST
|
||||
return vardecl
|
||||
}
|
||||
|
||||
override fun visitMemoryvardecl(ctx: MemoryvardeclContext): VarDecl {
|
||||
val vardecl = ctx.varinitializer().accept(this) as VarDecl
|
||||
vardecl.type = VarDeclType.MEMORY
|
||||
return vardecl
|
||||
}
|
||||
|
||||
override fun visitArrayindex(ctx: ArrayindexContext): ArrayIndex {
|
||||
return ArrayIndex(ctx.expression().accept(this) as Expression, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitAssignment(ctx: AssignmentContext): Statement {
|
||||
val multiAssign = ctx.multi_assign_target()
|
||||
if(multiAssign!=null) {
|
||||
return Assignment(multiAssign.accept(this) as AssignTarget, ctx.expression().accept(this) as Expression, AssignmentOrigin.USERCODE, ctx.toPosition())
|
||||
}
|
||||
|
||||
val nestedAssign = ctx.assignment()
|
||||
return if(nestedAssign==null)
|
||||
Assignment(ctx.assign_target().accept(this) as AssignTarget, ctx.expression().accept(this) as Expression, AssignmentOrigin.USERCODE, ctx.toPosition())
|
||||
else
|
||||
ChainedAssignment(ctx.assign_target().accept(this) as AssignTarget, nestedAssign.accept(this) as Statement, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitAugassignment(ctx: AugassignmentContext): Assignment {
|
||||
// replace A += X with A = A + X
|
||||
val target = ctx.assign_target().accept(this) as AssignTarget
|
||||
val oper = ctx.operator.text.substringBefore('=')
|
||||
val expression = BinaryExpression(target.toExpression(), oper, ctx.expression().accept(this) as Expression, ctx.toPosition())
|
||||
return Assignment(target, expression, AssignmentOrigin.USERCODE, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitIdentifierTarget(ctx: IdentifierTargetContext): AssignTarget {
|
||||
val identifier = ctx.scoped_identifier().accept(this) as IdentifierReference
|
||||
return AssignTarget(identifier, null, null, null, false, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitArrayindexedTarget(ctx: ArrayindexedTargetContext): AssignTarget {
|
||||
val ax = ctx.arrayindexed()
|
||||
val arrayvar = ax.scoped_identifier().accept(this) as IdentifierReference
|
||||
val index = ax.arrayindex().accept(this) as ArrayIndex
|
||||
val arrayindexed = ArrayIndexedExpression(arrayvar, index, ax.toPosition())
|
||||
return AssignTarget(null, arrayindexed, null, null, false, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitMemoryTarget(ctx: MemoryTargetContext): AssignTarget {
|
||||
return AssignTarget(null, null,
|
||||
DirectMemoryWrite(ctx.directmemory().expression().accept(this) as Expression, ctx.toPosition()),
|
||||
null, false, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitVoidTarget(ctx: VoidTargetContext): AssignTarget {
|
||||
return AssignTarget(null, null, null, null, true, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitMulti_assign_target(ctx: Multi_assign_targetContext): AssignTarget {
|
||||
val targets = ctx.assign_target().map { it.accept(this) as AssignTarget }
|
||||
return AssignTarget(null, null, null, targets, false, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitPostincrdecr(ctx: PostincrdecrContext): Assignment {
|
||||
val tgt = ctx.assign_target().accept(this) as AssignTarget
|
||||
val operator = ctx.operator.text
|
||||
val pos = ctx.toPosition()
|
||||
val addSubOne = BinaryExpression(tgt.toExpression(), if(operator=="++") "+" else "-", NumericLiteral.optimalInteger(1, pos), pos)
|
||||
return Assignment(tgt, addSubOne, AssignmentOrigin.USERCODE, pos)
|
||||
}
|
||||
|
||||
override fun visitArrayindexed(ctx: ArrayindexedContext): ArrayIndexedExpression {
|
||||
val identifier = ctx.scoped_identifier().accept(this) as IdentifierReference
|
||||
val index = ctx.arrayindex().accept(this) as ArrayIndex
|
||||
return ArrayIndexedExpression(identifier, index, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitDirectmemory(ctx: DirectmemoryContext): DirectMemoryRead {
|
||||
return DirectMemoryRead(ctx.expression().accept(this) as Expression, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitAddressof(ctx: AddressofContext): AddressOf {
|
||||
val identifier = ctx.scoped_identifier()?.accept(this) as IdentifierReference?
|
||||
val msb = ctx.ADDRESS_OF_MSB()!=null
|
||||
// note: &< (ADDRESS_OF_LSB) is equivalent to a regular &.
|
||||
return if (identifier != null)
|
||||
AddressOf(identifier, null, msb, ctx.toPosition())
|
||||
else {
|
||||
val array = ctx.arrayindexed()
|
||||
AddressOf(array.scoped_identifier().accept(this) as IdentifierReference,
|
||||
array.arrayindex().accept(this) as ArrayIndex,
|
||||
msb, ctx.toPosition())
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitFunctioncall(ctx: FunctioncallContext): FunctionCallExpression {
|
||||
val name = ctx.scoped_identifier().accept(this) as IdentifierReference
|
||||
val args = ctx.expression_list()?.expression()?.map { it.accept(this) as Expression } ?: emptyList()
|
||||
return FunctionCallExpression(name, args.toMutableList(), ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitFunctioncall_stmt(ctx: Functioncall_stmtContext): FunctionCallStatement {
|
||||
val void = ctx.VOID() != null
|
||||
val name = ctx.scoped_identifier().accept(this) as IdentifierReference
|
||||
val args = ctx.expression_list()?.expression()?.map { it.accept(this) as Expression } ?: emptyList()
|
||||
return FunctionCallStatement(name, args.toMutableList(), void, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitReturnstmt(ctx: ReturnstmtContext): Return {
|
||||
val cvalues = ctx.returnvalues()
|
||||
val values = if(cvalues==null || cvalues.expression().isEmpty()) arrayOf() else cvalues.expression().map { it.accept(this) as Expression }.toTypedArray()
|
||||
return Return(values, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitBreakstmt(ctx: BreakstmtContext): Break {
|
||||
return Break(ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitContinuestmt(ctx: ContinuestmtContext): Continue {
|
||||
return Continue(ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitIdentifier(ctx: IdentifierContext): IdentifierReference {
|
||||
return IdentifierReference(listOf(getname(ctx)), ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitScoped_identifier(ctx: Scoped_identifierContext): IdentifierReference {
|
||||
val children = ctx.identifier().map { it.text }
|
||||
return IdentifierReference(children, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitIntegerliteral(ctx: IntegerliteralContext): NumericLiteral {
|
||||
|
||||
fun makeLiteral(literalTextWithGrouping: String, radix: Int): Pair<Double, BaseDataType> {
|
||||
val literalText = literalTextWithGrouping.replace("_", "")
|
||||
val integer: Int
|
||||
var datatype = BaseDataType.UBYTE
|
||||
when (radix) {
|
||||
10 -> {
|
||||
integer = try {
|
||||
literalText.toInt()
|
||||
} catch(x: NumberFormatException) {
|
||||
throw SyntaxError("invalid decimal literal ${x.message}", ctx.toPosition())
|
||||
}
|
||||
datatype = when(integer) {
|
||||
in 0..255 -> BaseDataType.UBYTE
|
||||
in -128..127 -> BaseDataType.BYTE
|
||||
in 0..65535 -> BaseDataType.UWORD
|
||||
in -32768..32767 -> BaseDataType.WORD
|
||||
in -2147483647..2147483647 -> BaseDataType.LONG
|
||||
else -> BaseDataType.FLOAT
|
||||
}
|
||||
}
|
||||
2 -> {
|
||||
if(literalText.length>16)
|
||||
datatype = BaseDataType.LONG
|
||||
else if(literalText.length>8)
|
||||
datatype = BaseDataType.UWORD
|
||||
try {
|
||||
integer = literalText.toInt(2)
|
||||
} catch(x: NumberFormatException) {
|
||||
throw SyntaxError("invalid binary literal ${x.message}", ctx.toPosition())
|
||||
}
|
||||
}
|
||||
16 -> {
|
||||
if(literalText.length>4)
|
||||
datatype = BaseDataType.LONG
|
||||
else if(literalText.length>2)
|
||||
datatype = BaseDataType.UWORD
|
||||
try {
|
||||
integer = literalText.toInt(16)
|
||||
} catch(x: NumberFormatException) {
|
||||
throw SyntaxError("invalid hexadecimal literal ${x.message}", ctx.toPosition())
|
||||
}
|
||||
}
|
||||
else -> throw FatalAstException("invalid radix")
|
||||
}
|
||||
return integer.toDouble() to datatype
|
||||
}
|
||||
|
||||
val terminal: TerminalNode = ctx.children[0] as TerminalNode
|
||||
val integerPart = ctx.intpart.text
|
||||
val integer = when (terminal.symbol.type) {
|
||||
DEC_INTEGER -> makeLiteral(integerPart, 10)
|
||||
HEX_INTEGER -> makeLiteral(integerPart.substring(1), 16)
|
||||
BIN_INTEGER -> makeLiteral(integerPart.substring(1), 2)
|
||||
else -> throw FatalAstException(terminal.text)
|
||||
}
|
||||
return NumericLiteral(integer.second, integer.first, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitBooleanliteral(ctx: BooleanliteralContext): NumericLiteral {
|
||||
val boolean = when(ctx.text) {
|
||||
"true" -> true
|
||||
"false" -> false
|
||||
else -> throw FatalAstException(ctx.text)
|
||||
}
|
||||
return NumericLiteral.fromBoolean(boolean, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitArrayliteral(ctx: ArrayliteralContext): ArrayLiteral {
|
||||
val array = ctx.expression().map { it.accept(this) as Expression }.toTypedArray()
|
||||
// the actual type of the arraysize can not yet be determined here
|
||||
// the ConstantFold takes care of that and converts the type if needed.
|
||||
return ArrayLiteral(InferredTypes.InferredType.unknown(), array, position = ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitStringliteral(ctx: StringliteralContext): StringLiteral {
|
||||
val text = ctx.STRING().text
|
||||
val enc = ctx.encoding?.text
|
||||
val encoding =
|
||||
if(enc!=null)
|
||||
Encoding.entries.singleOrNull { it.prefix == enc }
|
||||
?: throw SyntaxError("invalid encoding", ctx.toPosition())
|
||||
else
|
||||
Encoding.DEFAULT
|
||||
val raw = text.substring(1, text.length-1)
|
||||
try {
|
||||
return StringLiteral.fromEscaped(raw, encoding, ctx.toPosition())
|
||||
} catch(ex: IllegalArgumentException) {
|
||||
throw SyntaxError(ex.message!!, ctx.toPosition())
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitCharliteral(ctx: CharliteralContext): CharLiteral {
|
||||
val text = ctx.SINGLECHAR().text
|
||||
val enc = ctx.encoding?.text
|
||||
val encoding =
|
||||
if(enc!=null)
|
||||
Encoding.entries.singleOrNull { it.prefix == enc }
|
||||
?: throw SyntaxError("invalid encoding", ctx.toPosition())
|
||||
else
|
||||
Encoding.DEFAULT
|
||||
val raw = text.substring(1, text.length - 1)
|
||||
try {
|
||||
return CharLiteral.fromEscaped(raw, encoding, ctx.toPosition())
|
||||
} catch(ex: IllegalArgumentException) {
|
||||
throw SyntaxError(ex.message!!, ctx.toPosition())
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitFloatliteral(ctx: FloatliteralContext): NumericLiteral {
|
||||
val floatvalue = ctx.text.replace("_","").toDouble()
|
||||
return NumericLiteral(BaseDataType.FLOAT, floatvalue, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitInlineasm(ctx: InlineasmContext): InlineAssembly {
|
||||
val text = ctx.INLINEASMBLOCK().text
|
||||
return InlineAssembly(text.substring(2, text.length-2), false, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitInlineir(ctx: InlineirContext): InlineAssembly {
|
||||
val text = ctx.INLINEASMBLOCK().text
|
||||
return InlineAssembly(text.substring(2, text.length-2), true, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitSubroutine(ctx: SubroutineContext): Subroutine {
|
||||
val name = getname(ctx.identifier())
|
||||
val parameters = ctx.sub_params()?.sub_param()?.map { it.accept(this) as SubroutineParameter } ?: emptyList()
|
||||
val returntypes = ctx.sub_return_part()?.datatype()?. map { dataTypeFor(it) } ?: emptyList()
|
||||
val statements = ctx.statement_block().accept(this) as AnonymousScope
|
||||
return Subroutine(
|
||||
name,
|
||||
parameters.toMutableList(),
|
||||
returntypes.toMutableList(),
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
emptySet(),
|
||||
asmAddress = null,
|
||||
isAsmSubroutine = false,
|
||||
inline = false,
|
||||
statements = statements.statements,
|
||||
position = ctx.toPosition()
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitStatement_block(ctx: Statement_blockContext): AnonymousScope {
|
||||
val statements = ctx.statement().map { it.accept(this) as Statement }
|
||||
return AnonymousScope(statements.toMutableList(), ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitSub_param(pctx: Sub_paramContext): SubroutineParameter {
|
||||
val decl = pctx.vardecl()
|
||||
val tags = decl.TAG().map { t -> t.text }
|
||||
val validTags = arrayOf("@zp", "@requirezp", "@nozp", "@split", "@nosplit", "@shared")
|
||||
for(tag in tags) {
|
||||
if(tag !in validTags)
|
||||
throw SyntaxError("invalid parameter tag '$tag'", pctx.toPosition())
|
||||
}
|
||||
val zp = getZpOption(tags)
|
||||
val decldt = decl.datatype()
|
||||
var datatype = if(decldt!=null) dataTypeFor(decldt) else DataType.UNDEFINED
|
||||
if(decl.ARRAYSIG()!=null || decl.arrayindex()!=null)
|
||||
datatype = datatype.elementToArray()
|
||||
|
||||
val identifiers = decl.identifier()
|
||||
if(identifiers.size>1)
|
||||
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
|
||||
val identifiername = getname(identifiers[0])
|
||||
|
||||
val (registerorpair, statusregister) = parseParamRegister(pctx.register, pctx.toPosition())
|
||||
if(statusregister!=null) {
|
||||
throw SyntaxError("can't use status register as param for normal subroutines", Position(pctx.toPosition().file, pctx.register.line, pctx.register.charPositionInLine, pctx.register.charPositionInLine+1))
|
||||
}
|
||||
return SubroutineParameter(identifiername, datatype, zp, registerorpair, pctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitAsmsubroutine(ctx: AsmsubroutineContext): Subroutine {
|
||||
val inline = ctx.INLINE()!=null
|
||||
val ad = asmSubDecl(ctx.asmsub_decl())
|
||||
val statements = ctx.statement_block().accept(this) as AnonymousScope
|
||||
|
||||
return Subroutine(ad.name,
|
||||
ad.parameters.toMutableList(),
|
||||
ad.returntypes.toMutableList(),
|
||||
ad.asmParameterRegisters,
|
||||
ad.asmReturnvaluesRegisters,
|
||||
ad.asmClobbers, null, true, inline,
|
||||
statements = statements.statements, position = ctx.toPosition()
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitExtsubroutine(ctx: ExtsubroutineContext): Subroutine {
|
||||
val subdecl = asmSubDecl(ctx.asmsub_decl())
|
||||
val constbank = (ctx.constbank?.accept(this) as NumericLiteral?)?.number?.toUInt()?.toUByte()
|
||||
val varbank = ctx.varbank?.accept(this) as IdentifierReference?
|
||||
val addr = ctx.address.accept(this) as Expression
|
||||
val address = Subroutine.Address(constbank, varbank, addr)
|
||||
return Subroutine(subdecl.name, subdecl.parameters.toMutableList(), subdecl.returntypes.toMutableList(),
|
||||
subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
|
||||
subdecl.asmClobbers, address, true, inline = false, statements = mutableListOf(), position = ctx.toPosition()
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitIf_stmt(ctx: If_stmtContext): IfElse {
|
||||
val condition = ctx.expression().accept(this) as Expression
|
||||
val truepart = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
|
||||
val elsepart = ctx.else_part()?.accept(this) as AnonymousScope? ?: AnonymousScope.empty()
|
||||
return IfElse(condition, truepart, elsepart, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitElse_part(ctx: Else_partContext): AnonymousScope {
|
||||
return stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
|
||||
}
|
||||
|
||||
override fun visitIf_expression(ctx: If_expressionContext): IfExpression {
|
||||
val (condition, truevalue, falsevalue) = ctx.expression().map { it.accept(this) as Expression }
|
||||
return IfExpression(condition, truevalue, falsevalue, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitBranch_stmt(ctx: Branch_stmtContext): ConditionalBranch {
|
||||
val branchcondition = branchCondition(ctx.branchcondition())
|
||||
val truepart = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
|
||||
val elsepart = ctx.else_part()?.accept(this) as AnonymousScope? ?: AnonymousScope.empty()
|
||||
return ConditionalBranch(branchcondition, truepart, elsepart, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitForloop(ctx: ForloopContext): ForLoop {
|
||||
val loopvar = ctx.scoped_identifier().accept(this) as IdentifierReference
|
||||
val iterable = ctx.expression().accept(this) as Expression
|
||||
val scope = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
|
||||
return ForLoop(loopvar, iterable, scope, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitWhileloop(ctx: WhileloopContext): WhileLoop {
|
||||
val condition = ctx.expression().accept(this) as Expression
|
||||
val statements = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
|
||||
return WhileLoop(condition, statements, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitUntilloop(ctx: UntilloopContext): UntilLoop {
|
||||
val condition = ctx.expression().accept(this) as Expression
|
||||
val statements = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
|
||||
return UntilLoop(statements, condition, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitRepeatloop(ctx: RepeatloopContext): RepeatLoop {
|
||||
val iterations = ctx.expression()?.accept(this) as Expression?
|
||||
val statements = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
|
||||
return RepeatLoop(iterations, statements, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitUnrollloop(ctx: UnrollloopContext): UnrollLoop {
|
||||
val iterations = ctx.expression().accept(this) as Expression
|
||||
val statements = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
|
||||
return UnrollLoop(iterations, statements, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitWhenstmt(ctx: WhenstmtContext): When {
|
||||
val condition = ctx.expression().accept(this) as Expression
|
||||
val choices = ctx.when_choice()?.map { it.accept(this) as WhenChoice }?.toMutableList() ?: mutableListOf()
|
||||
return When(condition, choices, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitWhen_choice(ctx: When_choiceContext): WhenChoice {
|
||||
val values = ctx.expression_list()?.expression()?.map { it.accept(this) as Expression }
|
||||
val statements = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
|
||||
return WhenChoice(values?.toMutableList(), statements, ctx.toPosition())
|
||||
}
|
||||
|
||||
override fun visitOngoto(ctx: OngotoContext): OnGoto {
|
||||
val elsepart = ctx.else_part()?.accept(this) as AnonymousScope? ?: AnonymousScope.empty()
|
||||
val isCall = ctx.kind.text == "call"
|
||||
val index = ctx.expression().accept(this) as Expression
|
||||
val labels = ctx.directivenamelist().scoped_identifier().map { it.accept(this) as IdentifierReference }
|
||||
return OnGoto(isCall, index, labels, elsepart, ctx.toPosition())
|
||||
}
|
||||
|
||||
|
||||
override fun visitModule_element(ctx: Module_elementContext): Node = visitChildren(ctx)
|
||||
override fun visitBlock_statement(ctx: Block_statementContext): Statement = visitChildren(ctx) as Statement
|
||||
override fun visitStatement(ctx: StatementContext): Statement = visitChildren(ctx) as Statement
|
||||
override fun visitVariabledeclaration(ctx: VariabledeclarationContext): VarDecl = visitChildren(ctx) as VarDecl
|
||||
override fun visitLiteralvalue(ctx: LiteralvalueContext): Expression = visitChildren(ctx) as Expression
|
||||
|
||||
|
||||
override fun visitDirectivenamelist(ctx: DirectivenamelistContext) = throw FatalAstException("should not be called")
|
||||
override fun visitAsmsub_decl(ctx: Asmsub_declContext?) = throw FatalAstException("should not be called")
|
||||
override fun visitAsmsub_params(ctx: Asmsub_paramsContext) = throw FatalAstException("should not be called")
|
||||
override fun visitExpression_list(ctx: Expression_listContext) = throw FatalAstException("should not be called")
|
||||
override fun visitBranchcondition(ctx: BranchconditionContext) = throw FatalAstException("should not be called")
|
||||
override fun visitDatatype(ctx: DatatypeContext) = throw FatalAstException("should not be called")
|
||||
override fun visitSizeof_argument(ctx: Sizeof_argumentContext) = throw FatalAstException("should not be called")
|
||||
override fun visitReturnvalues(ctx: ReturnvaluesContext) = throw FatalAstException("should not be called")
|
||||
override fun visitTypecast(ctx: TypecastContext) = throw FatalAstException("should not be called")
|
||||
override fun visitSub_params(ctx: Sub_paramsContext) = throw FatalAstException("should not be called")
|
||||
override fun visitAsmsub_param(ctx: Asmsub_paramContext) = throw FatalAstException("should not be called")
|
||||
override fun visitAsmsub_clobbers(ctx: Asmsub_clobbersContext) = throw FatalAstException("should not be called")
|
||||
override fun visitClobber(ctx: ClobberContext) = throw FatalAstException("should not be called")
|
||||
override fun visitAsmsub_returns(ctx: Asmsub_returnsContext) = throw FatalAstException("should not be called")
|
||||
override fun visitAsmsub_return(ctx: Asmsub_returnContext) = throw FatalAstException("should not be called")
|
||||
override fun visitSub_return_part(ctx: Sub_return_partContext) = throw FatalAstException("should not be called")
|
||||
|
||||
|
||||
private fun getname(identifier: IdentifierContext): String = identifier.children[0].text
|
||||
|
||||
private fun ParserRuleContext.toPosition() : Position {
|
||||
val pathString = start.inputStream.sourceName
|
||||
val filename = if(SourceCode.isRegularFilesystemPath(pathString)) {
|
||||
val path = Path(pathString)
|
||||
if(path.isRegularFile()) {
|
||||
SourceCode.relative(path).toString()
|
||||
} else {
|
||||
path.toString()
|
||||
}
|
||||
} else {
|
||||
pathString
|
||||
}
|
||||
// note: beware of TAB characters in the source text, they count as 1 column...
|
||||
return Position(filename, start.line, start.charPositionInLine+1, start.charPositionInLine + 1 + start.stopIndex - start.startIndex)
|
||||
}
|
||||
|
||||
private fun getZpOption(tags: List<String>): ZeropageWish = when {
|
||||
"@requirezp" in tags -> ZeropageWish.REQUIRE_ZEROPAGE
|
||||
"@zp" in tags -> ZeropageWish.PREFER_ZEROPAGE
|
||||
"@nozp" in tags -> ZeropageWish.NOT_IN_ZEROPAGE
|
||||
else -> ZeropageWish.DONTCARE
|
||||
}
|
||||
|
||||
private fun getSplitOption(tags: List<String>): SplitWish {
|
||||
return when {
|
||||
"@nosplit" in tags -> SplitWish.NOSPLIT
|
||||
"@split" in tags -> SplitWish.SPLIT
|
||||
else -> SplitWish.DONTCARE
|
||||
}
|
||||
}
|
||||
|
||||
private fun asmSubroutineParam(pctx: Asmsub_paramContext): AsmSubroutineParameter {
|
||||
val vardecl = pctx.vardecl()
|
||||
val decldt = vardecl.datatype()
|
||||
var datatype = if(decldt!=null) dataTypeFor(decldt) else DataType.UNDEFINED
|
||||
if(vardecl.ARRAYSIG()!=null || vardecl.arrayindex()!=null)
|
||||
datatype = datatype.elementToArray()
|
||||
val (registerorpair, statusregister) = parseParamRegister(pctx.register, pctx.toPosition())
|
||||
val identifiers = vardecl.identifier()
|
||||
if(identifiers.size>1)
|
||||
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
|
||||
val identifiername = getname(identifiers[0])
|
||||
return AsmSubroutineParameter(identifiername, datatype, registerorpair, statusregister, pctx.toPosition())
|
||||
}
|
||||
|
||||
private fun parseParamRegister(registerTok: Token?, pos: Position): Pair<RegisterOrPair?, Statusflag?> {
|
||||
if(registerTok==null)
|
||||
return Pair(null, null)
|
||||
val register = registerTok.text
|
||||
var registerorpair: RegisterOrPair? = null
|
||||
var statusregister: Statusflag? = null
|
||||
if(register!=null) {
|
||||
when (register) {
|
||||
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(register)
|
||||
in Statusflag.names -> statusregister = Statusflag.valueOf(register)
|
||||
else -> {
|
||||
throw SyntaxError("invalid register or status flag", Position(pos.file, registerTok.line, registerTok.charPositionInLine, registerTok.charPositionInLine+1))
|
||||
}
|
||||
}
|
||||
}
|
||||
return Pair(registerorpair, statusregister)
|
||||
}
|
||||
|
||||
private fun asmReturn(rctx: Asmsub_returnContext): AsmSubroutineReturn {
|
||||
val register = rctx.register.text
|
||||
var registerorpair: RegisterOrPair? = null
|
||||
var statusregister: Statusflag? = null
|
||||
if(register!=null) {
|
||||
when (register) {
|
||||
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(register)
|
||||
in Statusflag.names -> statusregister = Statusflag.valueOf(register)
|
||||
else -> throw SyntaxError("invalid register or status flag", rctx.toPosition())
|
||||
}
|
||||
}
|
||||
return AsmSubroutineReturn(
|
||||
dataTypeFor(rctx.datatype()),
|
||||
registerorpair,
|
||||
statusregister)
|
||||
}
|
||||
|
||||
private fun cpuRegister(text: String, pos: Position): CpuRegister {
|
||||
try {
|
||||
return CpuRegister.valueOf(text)
|
||||
} catch(_: IllegalArgumentException) {
|
||||
throw SyntaxError("invalid cpu register", pos)
|
||||
}
|
||||
}
|
||||
|
||||
private fun dataTypeFor(it: DatatypeContext) = DataType.forDt(baseDatatypeFor(it))
|
||||
|
||||
private fun baseDatatypeFor(it: DatatypeContext) = BaseDataType.valueOf(it.text.uppercase())
|
||||
|
||||
private fun stmtBlockOrSingle(statementBlock: Statement_blockContext?, statement: StatementContext?): AnonymousScope {
|
||||
return if(statementBlock!=null)
|
||||
statementBlock.accept(this) as AnonymousScope
|
||||
else if(statement!=null)
|
||||
AnonymousScope(mutableListOf(statement.accept(this) as Statement), statement.toPosition())
|
||||
else
|
||||
AnonymousScope.empty()
|
||||
}
|
||||
|
||||
private fun branchCondition(ctx: BranchconditionContext) = BranchCondition.valueOf(ctx.text.substringAfter('_').uppercase())
|
||||
|
||||
private fun asmSubDecl(ad: Asmsub_declContext): AsmsubDecl {
|
||||
val name = getname(ad.identifier())
|
||||
val params = ad.asmsub_params()?.asmsub_param()?.map { asmSubroutineParam(it) } ?: emptyList()
|
||||
val returns = ad.asmsub_returns()?.asmsub_return()?.map { asmReturn(it) } ?: emptyList()
|
||||
val clobbers = ad.asmsub_clobbers()?.clobber()?.NAME()?.map { cpuRegister(it.text, ad.toPosition()) } ?: emptyList()
|
||||
val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.zp, it.registerOrPair, it.position) }
|
||||
val normalReturntypes = returns.map { it.type }
|
||||
val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag) }
|
||||
val returnRegisters = returns.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag) }
|
||||
return AsmsubDecl(name, normalParameters, normalReturntypes, paramRegisters, returnRegisters, clobbers.toSet())
|
||||
}
|
||||
|
||||
private class AsmsubDecl(val name: String,
|
||||
val parameters: List<SubroutineParameter>,
|
||||
val returntypes: List<DataType>,
|
||||
val asmParameterRegisters: List<RegisterOrStatusflag>,
|
||||
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
||||
val asmClobbers: Set<CpuRegister>)
|
||||
|
||||
private class AsmSubroutineParameter(name: String,
|
||||
type: DataType,
|
||||
registerOrPair: RegisterOrPair?,
|
||||
val statusflag: Statusflag?,
|
||||
position: Position) : SubroutineParameter(name, type, ZeropageWish.DONTCARE, registerOrPair, position)
|
||||
|
||||
private class AsmSubroutineReturn(val type: DataType,
|
||||
val registerOrPair: RegisterOrPair?,
|
||||
val statusflag: Statusflag?)
|
||||
}
|
@ -151,8 +151,7 @@ class BinaryExpression(
|
||||
var left: Expression,
|
||||
var operator: String,
|
||||
var right: Expression,
|
||||
override val position: Position,
|
||||
private val insideParentheses: Boolean = false
|
||||
override val position: Position
|
||||
) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
@ -172,7 +171,7 @@ class BinaryExpression(
|
||||
replacement.parent = this
|
||||
}
|
||||
|
||||
override fun copy() = BinaryExpression(left.copy(), operator, right.copy(), position, insideParentheses)
|
||||
override fun copy() = BinaryExpression(left.copy(), operator, right.copy(), position)
|
||||
override fun toString() = "[$left $operator $right]"
|
||||
|
||||
override val isSimple = false
|
||||
@ -509,30 +508,24 @@ class DirectMemoryRead(var addressExpression: Expression, override val position:
|
||||
}
|
||||
|
||||
class NumericLiteral(val type: BaseDataType, // only numerical types allowed + bool (there is no separate BooleanLiteral node)
|
||||
numbervalue: Double, // can be byte, word or float depending on the type
|
||||
val number: Double, // can be byte, word or float depending on the type
|
||||
override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
val number: Double by lazy {
|
||||
if(type==BaseDataType.FLOAT)
|
||||
numbervalue
|
||||
else {
|
||||
val trunc = truncate(numbervalue)
|
||||
if(trunc != numbervalue)
|
||||
throw ExpressionError("refused truncating of float to avoid loss of precision", position)
|
||||
trunc
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
when(type) {
|
||||
BaseDataType.UBYTE -> require(numbervalue in 0.0..255.0)
|
||||
BaseDataType.BYTE -> require(numbervalue in -128.0..127.0)
|
||||
BaseDataType.UWORD -> require(numbervalue in 0.0..65535.0)
|
||||
BaseDataType.WORD -> require(numbervalue in -32768.0..32767.0)
|
||||
BaseDataType.LONG -> require(numbervalue in -2147483647.0..2147483647.0)
|
||||
BaseDataType.BOOL -> require(numbervalue==0.0 || numbervalue==1.0)
|
||||
BaseDataType.UBYTE -> require(number in 0.0..255.0)
|
||||
BaseDataType.BYTE -> require(number in -128.0..127.0)
|
||||
BaseDataType.UWORD -> require(number in 0.0..65535.0)
|
||||
BaseDataType.WORD -> require(number in -32768.0..32767.0)
|
||||
BaseDataType.LONG -> require(number in -2147483647.0..2147483647.0)
|
||||
BaseDataType.BOOL -> require(number==0.0 || number==1.0)
|
||||
else -> require(type.isNumericOrBool) { "numeric literal type should be numeric or bool: $type" }
|
||||
}
|
||||
if(type!=BaseDataType.FLOAT) {
|
||||
if(truncate(number) != number)
|
||||
throw ExpressionError("float value given for integer datatype", position)
|
||||
}
|
||||
}
|
||||
|
||||
override val isSimple = true
|
||||
@ -749,16 +742,44 @@ class NumericLiteral(val type: BaseDataType, // only numerical types allowed
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
try {
|
||||
if (targettype == BaseDataType.BYTE && number >= -128 && number <= 127)
|
||||
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
|
||||
if (targettype == BaseDataType.UBYTE && number >= 0 && number <= 255)
|
||||
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
|
||||
if (targettype == BaseDataType.WORD && number >= -32768 && number <= 32767)
|
||||
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
|
||||
if (targettype == BaseDataType.UWORD && number >= 0 && number <= 65535)
|
||||
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
|
||||
if(targettype==BaseDataType.LONG && number >=0 && number <= 2147483647)
|
||||
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
|
||||
when (targettype) {
|
||||
BaseDataType.BYTE if number >= -128 && number <= 127 -> {
|
||||
val converted = number.toInt().toByte().toDouble()
|
||||
if(implicit && converted!=number)
|
||||
return ValueAfterCast(false, "refused truncating of float to avoid loss of precision", this)
|
||||
else
|
||||
return ValueAfterCast(true, null, NumericLiteral(targettype, converted, position))
|
||||
}
|
||||
BaseDataType.UBYTE if number >= 0 && number <= 255 -> {
|
||||
val converted = number.toInt().toUByte().toDouble()
|
||||
if(implicit && converted!=number)
|
||||
return ValueAfterCast(false, "refused truncating of float to avoid loss of precision", this)
|
||||
else
|
||||
return ValueAfterCast(true, null, NumericLiteral(targettype, converted, position))
|
||||
}
|
||||
BaseDataType.WORD if number >= -32768 && number <= 32767 -> {
|
||||
val converted = number.toInt().toShort().toDouble()
|
||||
if(implicit && converted!=number)
|
||||
return ValueAfterCast(false, "refused truncating of float to avoid loss of precision", this)
|
||||
else
|
||||
return ValueAfterCast(true, null, NumericLiteral(targettype, converted, position))
|
||||
}
|
||||
BaseDataType.UWORD if number >= 0 && number <= 65535 -> {
|
||||
val converted = number.toInt().toUShort().toDouble()
|
||||
if(implicit && converted!=number)
|
||||
return ValueAfterCast(false, "refused truncating of float to avoid loss of precision", this)
|
||||
else
|
||||
return ValueAfterCast(true, null, NumericLiteral(targettype, converted, position))
|
||||
}
|
||||
BaseDataType.LONG if number >=0 && number <= 2147483647 -> {
|
||||
val converted = number.toInt().toDouble()
|
||||
if(implicit && converted!=number)
|
||||
return ValueAfterCast(false, "refused truncating of float to avoid loss of precision", this)
|
||||
else
|
||||
return ValueAfterCast(true, null, NumericLiteral(targettype, converted, position))
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
} catch (x: ExpressionError) {
|
||||
return ValueAfterCast(false, x.message,null)
|
||||
}
|
||||
|
@ -239,19 +239,20 @@ enum class VarDeclType {
|
||||
MEMORY
|
||||
}
|
||||
|
||||
class VarDecl(val type: VarDeclType,
|
||||
val origin: VarDeclOrigin,
|
||||
val datatype: DataType,
|
||||
val zeropage: ZeropageWish,
|
||||
val splitwordarray: SplitWish,
|
||||
var arraysize: ArrayIndex?,
|
||||
override val name: String,
|
||||
val names: List<String>,
|
||||
var value: Expression?,
|
||||
val sharedWithAsm: Boolean,
|
||||
val alignment: UInt,
|
||||
val dirty: Boolean,
|
||||
override val position: Position) : Statement(), INamedStatement {
|
||||
class VarDecl(
|
||||
var type: VarDeclType,
|
||||
val origin: VarDeclOrigin,
|
||||
val datatype: DataType,
|
||||
val zeropage: ZeropageWish,
|
||||
val splitwordarray: SplitWish,
|
||||
var arraysize: ArrayIndex?,
|
||||
override val name: String,
|
||||
val names: List<String>,
|
||||
var value: Expression?,
|
||||
val sharedWithAsm: Boolean,
|
||||
val alignment: UInt,
|
||||
val dirty: Boolean,
|
||||
override val position: Position) : Statement(), INamedStatement {
|
||||
override lateinit var parent: Node
|
||||
var allowInitializeWithZero = true
|
||||
|
||||
@ -284,7 +285,7 @@ class VarDecl(val type: VarDeclType,
|
||||
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
|
||||
var arrayDt = array.type.getOrElse { throw FatalAstException("unknown dt") }
|
||||
if(arrayDt.isSplitWordArray) {
|
||||
// autovars for array literals are NOT stored as a split word array!
|
||||
// autovars for array literals are NEVER stored as a split word array!
|
||||
when(arrayDt.sub) {
|
||||
BaseDataType.WORD -> arrayDt = DataType.arrayFor(BaseDataType.WORD, false)
|
||||
BaseDataType.UWORD -> arrayDt = DataType.arrayFor(BaseDataType.UWORD, false)
|
||||
@ -296,6 +297,24 @@ class VarDecl(val type: VarDeclType,
|
||||
SplitWish.NOSPLIT, arraysize, autoVarName, emptyList(), array,
|
||||
sharedWithAsm = false, alignment = 0u, dirty = false, position = array.position)
|
||||
}
|
||||
|
||||
fun createAutoOptionalSplit(array: ArrayLiteral): VarDecl {
|
||||
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
|
||||
val arrayDt = array.type.getOrElse { throw FatalAstException("unknown dt") }
|
||||
val split = if(arrayDt.isSplitWordArray) SplitWish.SPLIT else if(arrayDt.isWordArray) SplitWish.NOSPLIT else SplitWish.DONTCARE
|
||||
val arraysize = ArrayIndex.forArray(array)
|
||||
return VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, arrayDt, ZeropageWish.NOT_IN_ZEROPAGE,
|
||||
split, arraysize, autoVarName, emptyList(), array,
|
||||
sharedWithAsm = false, alignment = 0u, dirty = false, position = array.position)
|
||||
}
|
||||
|
||||
fun createAuto(dt: DataType): VarDecl {
|
||||
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
|
||||
val vardecl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, dt, ZeropageWish.NOT_IN_ZEROPAGE,
|
||||
SplitWish.DONTCARE, null, autoVarName, emptyList(), null,
|
||||
sharedWithAsm = false, alignment = 0u, dirty = false, position = Position.DUMMY)
|
||||
return vardecl
|
||||
}
|
||||
}
|
||||
|
||||
val isArray: Boolean
|
||||
@ -812,6 +831,10 @@ class AnonymousScope(override val statements: MutableList<Statement>,
|
||||
statements.forEach { it.linkParents(this) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun empty(pos: Position?=null): AnonymousScope = AnonymousScope(mutableListOf(), pos ?: Position.DUMMY)
|
||||
}
|
||||
|
||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||
require(replacement is Statement)
|
||||
val idx = statements.indexOfFirst { it===node }
|
||||
@ -1177,6 +1200,30 @@ class When(var condition: Expression,
|
||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||
override fun referencesIdentifier(nameInSource: List<String>): Boolean =
|
||||
condition.referencesIdentifier(nameInSource) || choices.any { it.referencesIdentifier(nameInSource) }
|
||||
|
||||
fun betterAsOnGoto(program: Program, compilerOptions: CompilationOptions): Boolean {
|
||||
// a when that has only goto's and the values 0,1,2,3,4... is better written as a on..goto
|
||||
val sizeLimit = if(compilerOptions.compTarget.cpu == CpuType.CPU65C02) 4 else 6
|
||||
if(choices.size >= sizeLimit) {
|
||||
if (condition.inferType(program).isBytes) {
|
||||
if (choices.all { (it.statements.statements.singleOrNull() as? Jump)?.target is IdentifierReference }) {
|
||||
val values = choices.flatMap {
|
||||
it.values ?: mutableListOf()
|
||||
}.map {
|
||||
it.constValue(program)?.number?.toInt()
|
||||
}
|
||||
if(null !in values) {
|
||||
val sortedValues = values.filterNotNull().sorted()
|
||||
val range = IntRange(sortedValues.first(), sortedValues.last())
|
||||
if(range.toList() == sortedValues) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
class WhenChoice(var values: MutableList<Expression>?, // if null, this is the 'else' part
|
||||
@ -1233,3 +1280,29 @@ class DirectMemoryWrite(var addressExpression: Expression, override val position
|
||||
override fun copy() = DirectMemoryWrite(addressExpression.copy(), position)
|
||||
override fun referencesIdentifier(nameInSource: List<String>): Boolean = addressExpression.referencesIdentifier(nameInSource)
|
||||
}
|
||||
|
||||
class OnGoto(
|
||||
val isCall: Boolean,
|
||||
val index: Expression,
|
||||
val labels: List<IdentifierReference>,
|
||||
val elsepart: AnonymousScope?,
|
||||
override val position: Position
|
||||
) : Statement() {
|
||||
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
index.linkParents(this)
|
||||
labels.forEach { it.linkParents(this) }
|
||||
elsepart?.linkParents(this)
|
||||
}
|
||||
|
||||
override fun copy(): OnGoto = OnGoto(isCall, index.copy(), labels.map { it.copy() }, elsepart?.copy(), position)
|
||||
override fun referencesIdentifier(nameInSource: List<String>) = index.referencesIdentifier(nameInSource) || labels.any { it.referencesIdentifier(nameInSource) } || elsepart?.referencesIdentifier(nameInSource) == true
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||
throw FatalAstException("can't replace")
|
||||
}
|
||||
}
|
||||
|
@ -137,6 +137,7 @@ abstract class AstWalker {
|
||||
open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(whenStmt: When, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(ongoto: OnGoto, parent: Node): Iterable<IAstModification> = noModifications
|
||||
|
||||
open fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(array: ArrayLiteral, parent: Node): Iterable<IAstModification> = noModifications
|
||||
@ -182,6 +183,7 @@ abstract class AstWalker {
|
||||
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(whenStmt: When, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(ongoto: OnGoto, parent: Node): Iterable<IAstModification> = noModifications
|
||||
|
||||
protected val modifications = mutableListOf<Triple<IAstModification, Node, Node>>()
|
||||
|
||||
@ -502,5 +504,13 @@ abstract class AstWalker {
|
||||
chainedAssignment.nested.accept(this, chainedAssignment)
|
||||
track(after(chainedAssignment, parent), chainedAssignment, parent)
|
||||
}
|
||||
|
||||
fun visit(ongoto: OnGoto, parent: Node) {
|
||||
track(before(ongoto, parent), ongoto, parent)
|
||||
ongoto.index.accept(this, ongoto)
|
||||
ongoto.labels.forEach { it.accept(this, ongoto) }
|
||||
ongoto.elsepart?.accept(this, ongoto)
|
||||
track(after(ongoto, parent), ongoto, parent)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,4 +199,10 @@ interface IAstVisitor {
|
||||
chainedAssignment.target.accept(this)
|
||||
chainedAssignment.nested.accept(this)
|
||||
}
|
||||
|
||||
fun visit(onGoto: OnGoto) {
|
||||
onGoto.index.accept(this)
|
||||
onGoto.labels.forEach { it.accept(this) }
|
||||
onGoto.elsepart?.accept(this)
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,7 @@ package prog8.parser
|
||||
|
||||
import org.antlr.v4.runtime.*
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.antlr.toAst
|
||||
import prog8.ast.statements.Block
|
||||
import prog8.ast.statements.Directive
|
||||
import prog8.ast.antlr.Antlr2KotlinVisitor
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.source.SourceCode
|
||||
|
||||
@ -25,41 +23,12 @@ object Prog8Parser {
|
||||
parser.addErrorListener(antlrErrorListener)
|
||||
|
||||
val parseTree = parser.module()
|
||||
val module = ParsedModule(src)
|
||||
|
||||
parseTree.module_element().forEach {
|
||||
val block = it.block()?.toAst(module.isLibrary)
|
||||
val directive = it.directive()?.toAst()
|
||||
if(directive != null) module.add(directive)
|
||||
if(block != null) module.add(block)
|
||||
}
|
||||
|
||||
return module
|
||||
val visitor = Antlr2KotlinVisitor(src)
|
||||
val visitorResult = visitor.visit(parseTree)
|
||||
return visitorResult as Module
|
||||
}
|
||||
|
||||
private class ParsedModule(source: SourceCode) :
|
||||
Module(mutableListOf(), Position(source.origin, 1, 0, 0), source)
|
||||
{
|
||||
|
||||
/**
|
||||
* Adds a [Directive] to [statements] and
|
||||
* sets this Module as its [parent].
|
||||
* Note: you can only add [Directive]s or [Block]s to a Module.
|
||||
*/
|
||||
fun add(child: Directive) {
|
||||
child.linkParents(this)
|
||||
statements.add(child)
|
||||
}
|
||||
/**
|
||||
* Adds a [Block] to [statements] and
|
||||
* sets this Module as its [parent].
|
||||
* Note: you can only add [Directive]s or [Block]s to a Module.
|
||||
*/
|
||||
fun add(child: Block) {
|
||||
child.linkParents(this)
|
||||
statements.add(child)
|
||||
}
|
||||
}
|
||||
|
||||
private object Prog8ErrorStrategy: BailErrorStrategy() {
|
||||
private fun fillIn(e: RecognitionException?, ctx: ParserRuleContext?) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.3.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.4 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-c128.p8
|
||||
@ -108,7 +108,6 @@ LIBRARY MODULE NAME: coroutines
|
||||
coroutines {
|
||||
const ubyte MAX_TASKS
|
||||
ubyte active_task
|
||||
uword[] returnaddresses
|
||||
uword supervisor
|
||||
uword[] tasklist
|
||||
uword[] userdatas
|
||||
@ -237,6 +236,7 @@ strings {
|
||||
copy (uword source @R0, uword target @AY) -> clobbers (A) -> ubyte @Y
|
||||
endswith (str st, str suffix) -> bool
|
||||
find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc
|
||||
find_eol (uword string @AY) -> ubyte @A, bool @Pc
|
||||
findstr (str haystack, str needle) -> ubyte
|
||||
hash (str string @AY) -> ubyte @A
|
||||
isdigit (ubyte petsciichar @A) -> bool @Pc
|
||||
@ -514,6 +514,7 @@ sys {
|
||||
const ubyte SIZEOF_BOOL
|
||||
const ubyte SIZEOF_BYTE
|
||||
const ubyte SIZEOF_FLOAT
|
||||
const ubyte SIZEOF_LONG
|
||||
const ubyte SIZEOF_UBYTE
|
||||
const ubyte SIZEOF_UWORD
|
||||
const ubyte SIZEOF_WORD
|
||||
@ -527,6 +528,7 @@ sys {
|
||||
exit (ubyte returnvalue @A)
|
||||
exit2 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y)
|
||||
exit3 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc)
|
||||
get_as_returnaddress (uword address @XY) -> uword @AX
|
||||
internal_stringcopy (uword source @R0, uword target @AY) -> clobbers (A,Y)
|
||||
irqsafe_clear_irqd ()
|
||||
irqsafe_set_irqd ()
|
||||
@ -559,6 +561,8 @@ cx16 {
|
||||
&uword r0
|
||||
&ubyte r0H
|
||||
&ubyte r0L
|
||||
&bool r0bH
|
||||
&bool r0bL
|
||||
&word r0s
|
||||
&byte r0sH
|
||||
&byte r0sL
|
||||
@ -566,89 +570,119 @@ cx16 {
|
||||
&uword r10
|
||||
&ubyte r10H
|
||||
&ubyte r10L
|
||||
&bool r10bH
|
||||
&bool r10bL
|
||||
&word r10s
|
||||
&byte r10sH
|
||||
&byte r10sL
|
||||
&uword r11
|
||||
&ubyte r11H
|
||||
&ubyte r11L
|
||||
&bool r11bH
|
||||
&bool r11bL
|
||||
&word r11s
|
||||
&byte r11sH
|
||||
&byte r11sL
|
||||
&uword r12
|
||||
&ubyte r12H
|
||||
&ubyte r12L
|
||||
&bool r12bH
|
||||
&bool r12bL
|
||||
&word r12s
|
||||
&byte r12sH
|
||||
&byte r12sL
|
||||
&uword r13
|
||||
&ubyte r13H
|
||||
&ubyte r13L
|
||||
&bool r13bH
|
||||
&bool r13bL
|
||||
&word r13s
|
||||
&byte r13sH
|
||||
&byte r13sL
|
||||
&uword r14
|
||||
&ubyte r14H
|
||||
&ubyte r14L
|
||||
&bool r14bH
|
||||
&bool r14bL
|
||||
&word r14s
|
||||
&byte r14sH
|
||||
&byte r14sL
|
||||
&uword r15
|
||||
&ubyte r15H
|
||||
&ubyte r15L
|
||||
&bool r15bH
|
||||
&bool r15bL
|
||||
&word r15s
|
||||
&byte r15sH
|
||||
&byte r15sL
|
||||
&ubyte r1H
|
||||
&ubyte r1L
|
||||
&bool r1bH
|
||||
&bool r1bL
|
||||
&word r1s
|
||||
&byte r1sH
|
||||
&byte r1sL
|
||||
&uword r2
|
||||
&ubyte r2H
|
||||
&ubyte r2L
|
||||
&bool r2bH
|
||||
&bool r2bL
|
||||
&word r2s
|
||||
&byte r2sH
|
||||
&byte r2sL
|
||||
&uword r3
|
||||
&ubyte r3H
|
||||
&ubyte r3L
|
||||
&bool r3bH
|
||||
&bool r3bL
|
||||
&word r3s
|
||||
&byte r3sH
|
||||
&byte r3sL
|
||||
&uword r4
|
||||
&ubyte r4H
|
||||
&ubyte r4L
|
||||
&bool r4bH
|
||||
&bool r4bL
|
||||
&word r4s
|
||||
&byte r4sH
|
||||
&byte r4sL
|
||||
&uword r5
|
||||
&ubyte r5H
|
||||
&ubyte r5L
|
||||
&bool r5bH
|
||||
&bool r5bL
|
||||
&word r5s
|
||||
&byte r5sH
|
||||
&byte r5sL
|
||||
&uword r6
|
||||
&ubyte r6H
|
||||
&ubyte r6L
|
||||
&bool r6bH
|
||||
&bool r6bL
|
||||
&word r6s
|
||||
&byte r6sH
|
||||
&byte r6sL
|
||||
&uword r7
|
||||
&ubyte r7H
|
||||
&ubyte r7L
|
||||
&bool r7bH
|
||||
&bool r7bL
|
||||
&word r7s
|
||||
&byte r7sH
|
||||
&byte r7sL
|
||||
&uword r8
|
||||
&ubyte r8H
|
||||
&ubyte r8L
|
||||
&bool r8bH
|
||||
&bool r8bL
|
||||
&word r8s
|
||||
&byte r8sH
|
||||
&byte r8sL
|
||||
&uword r9
|
||||
&ubyte r9H
|
||||
&ubyte r9L
|
||||
&bool r9bH
|
||||
&bool r9bL
|
||||
&word r9s
|
||||
&byte r9sH
|
||||
&byte r9sL
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.3.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.4 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-c64.p8
|
||||
@ -108,7 +108,6 @@ LIBRARY MODULE NAME: coroutines
|
||||
coroutines {
|
||||
const ubyte MAX_TASKS
|
||||
ubyte active_task
|
||||
uword[] returnaddresses
|
||||
uword supervisor
|
||||
uword[] tasklist
|
||||
uword[] userdatas
|
||||
@ -364,6 +363,7 @@ strings {
|
||||
copy (uword source @R0, uword target @AY) -> clobbers (A) -> ubyte @Y
|
||||
endswith (str st, str suffix) -> bool
|
||||
find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc
|
||||
find_eol (uword string @AY) -> ubyte @A, bool @Pc
|
||||
findstr (str haystack, str needle) -> ubyte
|
||||
hash (str string @AY) -> ubyte @A
|
||||
isdigit (ubyte petsciichar @A) -> bool @Pc
|
||||
@ -643,6 +643,7 @@ sys {
|
||||
const ubyte SIZEOF_BOOL
|
||||
const ubyte SIZEOF_BYTE
|
||||
const ubyte SIZEOF_FLOAT
|
||||
const ubyte SIZEOF_LONG
|
||||
const ubyte SIZEOF_UBYTE
|
||||
const ubyte SIZEOF_UWORD
|
||||
const ubyte SIZEOF_WORD
|
||||
@ -656,6 +657,7 @@ sys {
|
||||
exit (ubyte returnvalue @A)
|
||||
exit2 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y)
|
||||
exit3 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc)
|
||||
get_as_returnaddress (uword address @XY) -> uword @AX
|
||||
internal_stringcopy (uword source @R0, uword target @AY) -> clobbers (A,Y)
|
||||
irqsafe_clear_irqd ()
|
||||
irqsafe_set_irqd ()
|
||||
@ -688,6 +690,8 @@ cx16 {
|
||||
&uword r0
|
||||
&ubyte r0H
|
||||
&ubyte r0L
|
||||
&bool r0bH
|
||||
&bool r0bL
|
||||
&word r0s
|
||||
&byte r0sH
|
||||
&byte r0sL
|
||||
@ -695,89 +699,119 @@ cx16 {
|
||||
&uword r10
|
||||
&ubyte r10H
|
||||
&ubyte r10L
|
||||
&bool r10bH
|
||||
&bool r10bL
|
||||
&word r10s
|
||||
&byte r10sH
|
||||
&byte r10sL
|
||||
&uword r11
|
||||
&ubyte r11H
|
||||
&ubyte r11L
|
||||
&bool r11bH
|
||||
&bool r11bL
|
||||
&word r11s
|
||||
&byte r11sH
|
||||
&byte r11sL
|
||||
&uword r12
|
||||
&ubyte r12H
|
||||
&ubyte r12L
|
||||
&bool r12bH
|
||||
&bool r12bL
|
||||
&word r12s
|
||||
&byte r12sH
|
||||
&byte r12sL
|
||||
&uword r13
|
||||
&ubyte r13H
|
||||
&ubyte r13L
|
||||
&bool r13bH
|
||||
&bool r13bL
|
||||
&word r13s
|
||||
&byte r13sH
|
||||
&byte r13sL
|
||||
&uword r14
|
||||
&ubyte r14H
|
||||
&ubyte r14L
|
||||
&bool r14bH
|
||||
&bool r14bL
|
||||
&word r14s
|
||||
&byte r14sH
|
||||
&byte r14sL
|
||||
&uword r15
|
||||
&ubyte r15H
|
||||
&ubyte r15L
|
||||
&bool r15bH
|
||||
&bool r15bL
|
||||
&word r15s
|
||||
&byte r15sH
|
||||
&byte r15sL
|
||||
&ubyte r1H
|
||||
&ubyte r1L
|
||||
&bool r1bH
|
||||
&bool r1bL
|
||||
&word r1s
|
||||
&byte r1sH
|
||||
&byte r1sL
|
||||
&uword r2
|
||||
&ubyte r2H
|
||||
&ubyte r2L
|
||||
&bool r2bH
|
||||
&bool r2bL
|
||||
&word r2s
|
||||
&byte r2sH
|
||||
&byte r2sL
|
||||
&uword r3
|
||||
&ubyte r3H
|
||||
&ubyte r3L
|
||||
&bool r3bH
|
||||
&bool r3bL
|
||||
&word r3s
|
||||
&byte r3sH
|
||||
&byte r3sL
|
||||
&uword r4
|
||||
&ubyte r4H
|
||||
&ubyte r4L
|
||||
&bool r4bH
|
||||
&bool r4bL
|
||||
&word r4s
|
||||
&byte r4sH
|
||||
&byte r4sL
|
||||
&uword r5
|
||||
&ubyte r5H
|
||||
&ubyte r5L
|
||||
&bool r5bH
|
||||
&bool r5bL
|
||||
&word r5s
|
||||
&byte r5sH
|
||||
&byte r5sL
|
||||
&uword r6
|
||||
&ubyte r6H
|
||||
&ubyte r6L
|
||||
&bool r6bH
|
||||
&bool r6bL
|
||||
&word r6s
|
||||
&byte r6sH
|
||||
&byte r6sL
|
||||
&uword r7
|
||||
&ubyte r7H
|
||||
&ubyte r7L
|
||||
&bool r7bH
|
||||
&bool r7bL
|
||||
&word r7s
|
||||
&byte r7sH
|
||||
&byte r7sL
|
||||
&uword r8
|
||||
&ubyte r8H
|
||||
&ubyte r8L
|
||||
&bool r8bH
|
||||
&bool r8bL
|
||||
&word r8s
|
||||
&byte r8sH
|
||||
&byte r8sL
|
||||
&uword r9
|
||||
&ubyte r9H
|
||||
&ubyte r9L
|
||||
&bool r9bH
|
||||
&bool r9bL
|
||||
&word r9s
|
||||
&byte r9sH
|
||||
&byte r9sL
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.3.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.4 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-cx16.p8
|
||||
@ -146,7 +146,6 @@ LIBRARY MODULE NAME: coroutines
|
||||
coroutines {
|
||||
const ubyte MAX_TASKS
|
||||
ubyte active_task
|
||||
uword[] returnaddresses
|
||||
uword supervisor
|
||||
uword[] tasklist
|
||||
uword[] userdatas
|
||||
@ -530,6 +529,7 @@ LIBRARY MODULE NAME: monogfx
|
||||
----------------------------
|
||||
|
||||
monogfx {
|
||||
uword buffer_visible, buffer_back
|
||||
const ubyte MODE_INVERT
|
||||
const ubyte MODE_NORMAL
|
||||
const ubyte MODE_STIPPLE
|
||||
@ -545,6 +545,7 @@ monogfx {
|
||||
cs_innerloop640 (bool draw @A) -> clobbers (Y)
|
||||
disc (uword xcenter, uword ycenter, ubyte radius, bool draw)
|
||||
drawmode (ubyte dm)
|
||||
enable_doublebuffer ()
|
||||
fill (uword x, uword y, bool draw, ubyte stack_rambank)
|
||||
fillrect (uword xx, uword yy, uword rwidth, uword rheight, bool draw)
|
||||
hires ()
|
||||
@ -560,6 +561,7 @@ monogfx {
|
||||
safe_disc (uword xcenter, uword ycenter, ubyte radius, bool draw)
|
||||
safe_horizontal_line (uword xx, uword yy, uword length, bool draw)
|
||||
safe_plot (uword xx, uword yy, bool draw)
|
||||
swap_buffers (bool wait_for_vsync)
|
||||
text (uword xx, uword yy, bool draw, str sctextptr)
|
||||
text_charset (ubyte charset)
|
||||
textmode ()
|
||||
@ -672,6 +674,7 @@ strings {
|
||||
copy (uword source @R0, uword target @AY) -> clobbers (A) -> ubyte @Y
|
||||
endswith (str st, str suffix) -> bool
|
||||
find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc
|
||||
find_eol (uword string @AY) -> ubyte @A, bool @Pc
|
||||
findstr (str haystack, str needle) -> ubyte
|
||||
hash (str string @AY) -> ubyte @A
|
||||
isdigit (ubyte petsciichar @A) -> bool @Pc
|
||||
@ -890,6 +893,8 @@ cx16 {
|
||||
&uword r0
|
||||
&ubyte r0H
|
||||
&ubyte r0L
|
||||
&bool r0bH
|
||||
&bool r0bL
|
||||
&word r0s
|
||||
&byte r0sH
|
||||
&byte r0sL
|
||||
@ -897,89 +902,119 @@ cx16 {
|
||||
&uword r10
|
||||
&ubyte r10H
|
||||
&ubyte r10L
|
||||
&bool r10bH
|
||||
&bool r10bL
|
||||
&word r10s
|
||||
&byte r10sH
|
||||
&byte r10sL
|
||||
&uword r11
|
||||
&ubyte r11H
|
||||
&ubyte r11L
|
||||
&bool r11bH
|
||||
&bool r11bL
|
||||
&word r11s
|
||||
&byte r11sH
|
||||
&byte r11sL
|
||||
&uword r12
|
||||
&ubyte r12H
|
||||
&ubyte r12L
|
||||
&bool r12bH
|
||||
&bool r12bL
|
||||
&word r12s
|
||||
&byte r12sH
|
||||
&byte r12sL
|
||||
&uword r13
|
||||
&ubyte r13H
|
||||
&ubyte r13L
|
||||
&bool r13bH
|
||||
&bool r13bL
|
||||
&word r13s
|
||||
&byte r13sH
|
||||
&byte r13sL
|
||||
&uword r14
|
||||
&ubyte r14H
|
||||
&ubyte r14L
|
||||
&bool r14bH
|
||||
&bool r14bL
|
||||
&word r14s
|
||||
&byte r14sH
|
||||
&byte r14sL
|
||||
&uword r15
|
||||
&ubyte r15H
|
||||
&ubyte r15L
|
||||
&bool r15bH
|
||||
&bool r15bL
|
||||
&word r15s
|
||||
&byte r15sH
|
||||
&byte r15sL
|
||||
&ubyte r1H
|
||||
&ubyte r1L
|
||||
&bool r1bH
|
||||
&bool r1bL
|
||||
&word r1s
|
||||
&byte r1sH
|
||||
&byte r1sL
|
||||
&uword r2
|
||||
&ubyte r2H
|
||||
&ubyte r2L
|
||||
&bool r2bH
|
||||
&bool r2bL
|
||||
&word r2s
|
||||
&byte r2sH
|
||||
&byte r2sL
|
||||
&uword r3
|
||||
&ubyte r3H
|
||||
&ubyte r3L
|
||||
&bool r3bH
|
||||
&bool r3bL
|
||||
&word r3s
|
||||
&byte r3sH
|
||||
&byte r3sL
|
||||
&uword r4
|
||||
&ubyte r4H
|
||||
&ubyte r4L
|
||||
&bool r4bH
|
||||
&bool r4bL
|
||||
&word r4s
|
||||
&byte r4sH
|
||||
&byte r4sL
|
||||
&uword r5
|
||||
&ubyte r5H
|
||||
&ubyte r5L
|
||||
&bool r5bH
|
||||
&bool r5bL
|
||||
&word r5s
|
||||
&byte r5sH
|
||||
&byte r5sL
|
||||
&uword r6
|
||||
&ubyte r6H
|
||||
&ubyte r6L
|
||||
&bool r6bH
|
||||
&bool r6bL
|
||||
&word r6s
|
||||
&byte r6sH
|
||||
&byte r6sL
|
||||
&uword r7
|
||||
&ubyte r7H
|
||||
&ubyte r7L
|
||||
&bool r7bH
|
||||
&bool r7bL
|
||||
&word r7s
|
||||
&byte r7sH
|
||||
&byte r7sL
|
||||
&uword r8
|
||||
&ubyte r8H
|
||||
&ubyte r8L
|
||||
&bool r8bH
|
||||
&bool r8bL
|
||||
&word r8s
|
||||
&byte r8sH
|
||||
&byte r8sL
|
||||
&uword r9
|
||||
&ubyte r9H
|
||||
&ubyte r9L
|
||||
&bool r9bH
|
||||
&bool r9bL
|
||||
&word r9s
|
||||
&byte r9sH
|
||||
&byte r9sL
|
||||
@ -1093,6 +1128,8 @@ cx16 {
|
||||
iso_cursor_char (ubyte character @X) -> clobbers (A,X,Y)
|
||||
joystick_get (ubyte joynr @A) -> uword @AX, bool @Y = $ff56
|
||||
joystick_scan () -> clobbers (A,X,Y) = $ff53
|
||||
joysticks_detect () -> ubyte
|
||||
joysticks_getall (bool also_keyboard_js) -> uword
|
||||
kbdbuf_get_modifiers () -> ubyte @A = $fec0
|
||||
kbdbuf_peek () -> ubyte @A, ubyte @X = $febd
|
||||
kbdbuf_put (ubyte key @A) -> clobbers (X) = $fec3
|
||||
@ -1215,6 +1252,7 @@ sys {
|
||||
const ubyte SIZEOF_BOOL
|
||||
const ubyte SIZEOF_BYTE
|
||||
const ubyte SIZEOF_FLOAT
|
||||
const ubyte SIZEOF_LONG
|
||||
const ubyte SIZEOF_UBYTE
|
||||
const ubyte SIZEOF_UWORD
|
||||
const ubyte SIZEOF_WORD
|
||||
@ -1226,6 +1264,7 @@ sys {
|
||||
exit (ubyte returnvalue @A)
|
||||
exit2 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y)
|
||||
exit3 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc)
|
||||
get_as_returnaddress (uword address @XY) -> uword @AX
|
||||
internal_stringcopy (uword source @R0, uword target @AY) -> clobbers (A,Y)
|
||||
irqsafe_clear_irqd ()
|
||||
irqsafe_set_irqd ()
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.3.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.4 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-pet32.p8
|
||||
@ -108,7 +108,6 @@ LIBRARY MODULE NAME: coroutines
|
||||
coroutines {
|
||||
const ubyte MAX_TASKS
|
||||
ubyte active_task
|
||||
uword[] returnaddresses
|
||||
uword supervisor
|
||||
uword[] tasklist
|
||||
uword[] userdatas
|
||||
@ -188,6 +187,7 @@ strings {
|
||||
copy (uword source @R0, uword target @AY) -> clobbers (A) -> ubyte @Y
|
||||
endswith (str st, str suffix) -> bool
|
||||
find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc
|
||||
find_eol (uword string @AY) -> ubyte @A, bool @Pc
|
||||
findstr (str haystack, str needle) -> ubyte
|
||||
hash (str string @AY) -> ubyte @A
|
||||
isdigit (ubyte petsciichar @A) -> bool @Pc
|
||||
@ -263,6 +263,7 @@ sys {
|
||||
const ubyte SIZEOF_BOOL
|
||||
const ubyte SIZEOF_BYTE
|
||||
const ubyte SIZEOF_FLOAT
|
||||
const ubyte SIZEOF_LONG
|
||||
const ubyte SIZEOF_UBYTE
|
||||
const ubyte SIZEOF_UWORD
|
||||
const ubyte SIZEOF_WORD
|
||||
@ -274,6 +275,7 @@ sys {
|
||||
exit (ubyte returnvalue @A)
|
||||
exit2 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y)
|
||||
exit3 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc)
|
||||
get_as_returnaddress (uword address @XY) -> uword @AX
|
||||
internal_stringcopy (uword source @R0, uword target @AY) -> clobbers (A,Y)
|
||||
irqsafe_clear_irqd ()
|
||||
irqsafe_set_irqd ()
|
||||
@ -302,6 +304,8 @@ cx16 {
|
||||
&uword r0
|
||||
&ubyte r0H
|
||||
&ubyte r0L
|
||||
&bool r0bH
|
||||
&bool r0bL
|
||||
&word r0s
|
||||
&byte r0sH
|
||||
&byte r0sL
|
||||
@ -309,89 +313,119 @@ cx16 {
|
||||
&uword r10
|
||||
&ubyte r10H
|
||||
&ubyte r10L
|
||||
&bool r10bH
|
||||
&bool r10bL
|
||||
&word r10s
|
||||
&byte r10sH
|
||||
&byte r10sL
|
||||
&uword r11
|
||||
&ubyte r11H
|
||||
&ubyte r11L
|
||||
&bool r11bH
|
||||
&bool r11bL
|
||||
&word r11s
|
||||
&byte r11sH
|
||||
&byte r11sL
|
||||
&uword r12
|
||||
&ubyte r12H
|
||||
&ubyte r12L
|
||||
&bool r12bH
|
||||
&bool r12bL
|
||||
&word r12s
|
||||
&byte r12sH
|
||||
&byte r12sL
|
||||
&uword r13
|
||||
&ubyte r13H
|
||||
&ubyte r13L
|
||||
&bool r13bH
|
||||
&bool r13bL
|
||||
&word r13s
|
||||
&byte r13sH
|
||||
&byte r13sL
|
||||
&uword r14
|
||||
&ubyte r14H
|
||||
&ubyte r14L
|
||||
&bool r14bH
|
||||
&bool r14bL
|
||||
&word r14s
|
||||
&byte r14sH
|
||||
&byte r14sL
|
||||
&uword r15
|
||||
&ubyte r15H
|
||||
&ubyte r15L
|
||||
&bool r15bH
|
||||
&bool r15bL
|
||||
&word r15s
|
||||
&byte r15sH
|
||||
&byte r15sL
|
||||
&ubyte r1H
|
||||
&ubyte r1L
|
||||
&bool r1bH
|
||||
&bool r1bL
|
||||
&word r1s
|
||||
&byte r1sH
|
||||
&byte r1sL
|
||||
&uword r2
|
||||
&ubyte r2H
|
||||
&ubyte r2L
|
||||
&bool r2bH
|
||||
&bool r2bL
|
||||
&word r2s
|
||||
&byte r2sH
|
||||
&byte r2sL
|
||||
&uword r3
|
||||
&ubyte r3H
|
||||
&ubyte r3L
|
||||
&bool r3bH
|
||||
&bool r3bL
|
||||
&word r3s
|
||||
&byte r3sH
|
||||
&byte r3sL
|
||||
&uword r4
|
||||
&ubyte r4H
|
||||
&ubyte r4L
|
||||
&bool r4bH
|
||||
&bool r4bL
|
||||
&word r4s
|
||||
&byte r4sH
|
||||
&byte r4sL
|
||||
&uword r5
|
||||
&ubyte r5H
|
||||
&ubyte r5L
|
||||
&bool r5bH
|
||||
&bool r5bL
|
||||
&word r5s
|
||||
&byte r5sH
|
||||
&byte r5sL
|
||||
&uword r6
|
||||
&ubyte r6H
|
||||
&ubyte r6L
|
||||
&bool r6bH
|
||||
&bool r6bL
|
||||
&word r6s
|
||||
&byte r6sH
|
||||
&byte r6sL
|
||||
&uword r7
|
||||
&ubyte r7H
|
||||
&ubyte r7L
|
||||
&bool r7bH
|
||||
&bool r7bL
|
||||
&word r7s
|
||||
&byte r7sH
|
||||
&byte r7sL
|
||||
&uword r8
|
||||
&ubyte r8H
|
||||
&ubyte r8L
|
||||
&bool r8bH
|
||||
&bool r8bL
|
||||
&word r8s
|
||||
&byte r8sH
|
||||
&byte r8sL
|
||||
&uword r9
|
||||
&ubyte r9H
|
||||
&ubyte r9L
|
||||
&bool r9bH
|
||||
&bool r9bL
|
||||
&word r9s
|
||||
&byte r9sH
|
||||
&byte r9sL
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.3.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.4 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-virtual.p8
|
||||
@ -279,7 +279,8 @@ strings {
|
||||
contains (str st, ubyte character) -> bool
|
||||
copy (str source, str target) -> ubyte
|
||||
endswith (str st, str suffix) -> bool
|
||||
find (str st, ubyte character) -> ubyte
|
||||
find (str st, ubyte character) -> ubyte, bool
|
||||
find_eol (str st) -> ubyte, bool
|
||||
findstr (str haystack, str needle) -> ubyte
|
||||
hash (str st) -> ubyte
|
||||
isdigit (ubyte character) -> bool
|
||||
@ -297,7 +298,7 @@ strings {
|
||||
ltrim (str s)
|
||||
ltrimmed (str s) -> str
|
||||
ncompare (str st1, str st2, ubyte length) -> byte
|
||||
rfind (uword stringptr, ubyte character) -> ubyte
|
||||
rfind (uword stringptr, ubyte character) -> ubyte, bool
|
||||
right (str source, ubyte slen, str target)
|
||||
rstrip (str s)
|
||||
rtrim (str s)
|
||||
@ -327,6 +328,7 @@ sys {
|
||||
const ubyte SIZEOF_BOOL
|
||||
const ubyte SIZEOF_BYTE
|
||||
const ubyte SIZEOF_FLOAT
|
||||
const ubyte SIZEOF_LONG
|
||||
const ubyte SIZEOF_UBYTE
|
||||
const ubyte SIZEOF_UWORD
|
||||
const ubyte SIZEOF_WORD
|
||||
@ -336,6 +338,7 @@ sys {
|
||||
disable_caseswitch ()
|
||||
enable_caseswitch ()
|
||||
exit (ubyte returnvalue)
|
||||
get_as_returnaddress (uword address) -> uword
|
||||
gfx_clear (ubyte color)
|
||||
gfx_enable (ubyte mode)
|
||||
gfx_getpixel (uword xx, uword yy) -> ubyte
|
||||
@ -364,6 +367,8 @@ cx16 {
|
||||
&uword r0
|
||||
&ubyte r0H
|
||||
&ubyte r0L
|
||||
&bool r0bH
|
||||
&bool r0bL
|
||||
&word r0s
|
||||
&byte r0sH
|
||||
&byte r0sL
|
||||
@ -371,89 +376,119 @@ cx16 {
|
||||
&uword r10
|
||||
&ubyte r10H
|
||||
&ubyte r10L
|
||||
&bool r10bH
|
||||
&bool r10bL
|
||||
&word r10s
|
||||
&byte r10sH
|
||||
&byte r10sL
|
||||
&uword r11
|
||||
&ubyte r11H
|
||||
&ubyte r11L
|
||||
&bool r11bH
|
||||
&bool r11bL
|
||||
&word r11s
|
||||
&byte r11sH
|
||||
&byte r11sL
|
||||
&uword r12
|
||||
&ubyte r12H
|
||||
&ubyte r12L
|
||||
&bool r12bH
|
||||
&bool r12bL
|
||||
&word r12s
|
||||
&byte r12sH
|
||||
&byte r12sL
|
||||
&uword r13
|
||||
&ubyte r13H
|
||||
&ubyte r13L
|
||||
&bool r13bH
|
||||
&bool r13bL
|
||||
&word r13s
|
||||
&byte r13sH
|
||||
&byte r13sL
|
||||
&uword r14
|
||||
&ubyte r14H
|
||||
&ubyte r14L
|
||||
&bool r14bH
|
||||
&bool r14bL
|
||||
&word r14s
|
||||
&byte r14sH
|
||||
&byte r14sL
|
||||
&uword r15
|
||||
&ubyte r15H
|
||||
&ubyte r15L
|
||||
&bool r15bH
|
||||
&bool r15bL
|
||||
&word r15s
|
||||
&byte r15sH
|
||||
&byte r15sL
|
||||
&ubyte r1H
|
||||
&ubyte r1L
|
||||
&bool r1bH
|
||||
&bool r1bL
|
||||
&word r1s
|
||||
&byte r1sH
|
||||
&byte r1sL
|
||||
&uword r2
|
||||
&ubyte r2H
|
||||
&ubyte r2L
|
||||
&bool r2bH
|
||||
&bool r2bL
|
||||
&word r2s
|
||||
&byte r2sH
|
||||
&byte r2sL
|
||||
&uword r3
|
||||
&ubyte r3H
|
||||
&ubyte r3L
|
||||
&bool r3bH
|
||||
&bool r3bL
|
||||
&word r3s
|
||||
&byte r3sH
|
||||
&byte r3sL
|
||||
&uword r4
|
||||
&ubyte r4H
|
||||
&ubyte r4L
|
||||
&bool r4bH
|
||||
&bool r4bL
|
||||
&word r4s
|
||||
&byte r4sH
|
||||
&byte r4sL
|
||||
&uword r5
|
||||
&ubyte r5H
|
||||
&ubyte r5L
|
||||
&bool r5bH
|
||||
&bool r5bL
|
||||
&word r5s
|
||||
&byte r5sH
|
||||
&byte r5sL
|
||||
&uword r6
|
||||
&ubyte r6H
|
||||
&ubyte r6L
|
||||
&bool r6bH
|
||||
&bool r6bL
|
||||
&word r6s
|
||||
&byte r6sH
|
||||
&byte r6sL
|
||||
&uword r7
|
||||
&ubyte r7H
|
||||
&ubyte r7L
|
||||
&bool r7bH
|
||||
&bool r7bL
|
||||
&word r7s
|
||||
&byte r7sH
|
||||
&byte r7sL
|
||||
&uword r8
|
||||
&ubyte r8H
|
||||
&ubyte r8L
|
||||
&bool r8bH
|
||||
&bool r8bL
|
||||
&word r8s
|
||||
&byte r8sH
|
||||
&byte r8sL
|
||||
&uword r9
|
||||
&ubyte r9H
|
||||
&ubyte r9L
|
||||
&bool r9bH
|
||||
&bool r9bL
|
||||
&word r9s
|
||||
&byte r9sH
|
||||
&byte r9sL
|
||||
|
@ -18,14 +18,20 @@ Then you can choose a few ways to get a compiler:
|
||||
|
||||
**Or, install via a Package Manager:**
|
||||
|
||||
Currently, it's only available on `AUR <https://wiki.archlinux.org/title/Arch_User_Repository>`_ for Arch Linux and compatible systems.
|
||||
The package is called `"prog8" <https://aur.archlinux.org/packages/prog8>`_.
|
||||
Arch Linux:
|
||||
Currently, it's available on `AUR <https://wiki.archlinux.org/title/Arch_User_Repository>`_ for Arch Linux and compatible systems.
|
||||
The package is called `"prog8" <https://aur.archlinux.org/packages/prog8>`_.
|
||||
|
||||
This package, alongside the compiler itself, also globally installs syntax highlighting for ``vim`` and ``nano``.
|
||||
In order to run compiler, you can type ``prog8c``. The usage of those commands is exactly the same as with the ``java -jar`` method.
|
||||
This package, alongside the compiler itself, also globally installs syntax highlighting for ``vim`` and ``nano``.
|
||||
In order to run compiler, you can type ``prog8c``. The usage of those commands is exactly the same as with the ``java -jar`` method.
|
||||
|
||||
In case you prefer to install AUR packages in a traditional manner, make sure to install `"tass64" package <https://aur.archlinux.org/packages/tass64>`_
|
||||
before installing prog8, as `makepkg <https://wiki.archlinux.org/title/Makepkg>`_ itself doesn't fetch AUR dependencies.
|
||||
In case you prefer to install AUR packages in a traditional manner, make sure to install `"tass64" package <https://aur.archlinux.org/packages/tass64>`_
|
||||
before installing prog8, as `makepkg <https://wiki.archlinux.org/title/Makepkg>`_ itself doesn't fetch AUR dependencies.
|
||||
|
||||
Mac OS (and Linux, and WSL2 on Windows):
|
||||
Prog8 can be installed via `Homebrew <https://formulae.brew.sh/formula/prog8>`_ using the command ``brew install prog8``.
|
||||
It will make the ``prog8c`` command available and also installs the other required software tools for you.
|
||||
While Homebrew works on Linux, it's probably best to first check your distribution's native package manager.
|
||||
|
||||
**Or, download a bleeding edge development version from Github:**
|
||||
|
||||
@ -236,6 +242,9 @@ One or more .p8 module files
|
||||
machine's configuration and properties from that configuration file instead of using one of the built-in targets.
|
||||
See :ref:`customizable_target` for details about this.
|
||||
|
||||
``-timings``
|
||||
Show a more detailed breakdown of the time taken in various compiler phases, for performance analysis of the compiler itself.
|
||||
|
||||
``-varsgolden``
|
||||
Like ``-varshigh``, but places the variables in the $0400-$07FF "golden ram" area instead.
|
||||
Because this is in normal system memory, there are no bank switching issues.
|
||||
|
@ -101,6 +101,7 @@ Features
|
||||
- Programs can be configured to execute in ROM
|
||||
- Conditional branches for status flags that map 1:1 to processor branch instructions for optimal efficiency
|
||||
- ``when`` statement to avoid if-else chains
|
||||
- ``on .. goto`` statement for fast jump tables
|
||||
- ``in`` expression for concise and efficient multi-value/containment test
|
||||
- ``defer`` statement to help write concise and robust subroutine cleanup logic
|
||||
- Several specialized built-in functions, such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
|
||||
|
@ -178,10 +178,9 @@ setlsb (x, value)
|
||||
setmsb (x, value)
|
||||
Sets the most significant byte of word variable x to a new value. Leaves the LSB untouched.
|
||||
|
||||
sizeof (name) ; sizeof (number)
|
||||
Number of bytes that the object 'name', or the number 'number' occupies in memory.
|
||||
This is a constant determined by the data type of
|
||||
the object. For instance, for a variable of type uword, the sizeof is 2.
|
||||
sizeof (name) ; sizeof (number) ; sizeof(datatype)
|
||||
The constant number of bytes that the object 'name', the number 'number' or the type 'datatype' occupies in memory.
|
||||
For instance, for a variable of type uword, the sizeof is 2.
|
||||
For an 10 element array of floats, it is 50 (on the C64, where a float is 5 bytes).
|
||||
Note: usually you will be interested in the number of elements in an array, use len() for that.
|
||||
|
||||
@ -363,15 +362,13 @@ Read the `conv source code <https://github.com/irmen/prog8/tree/master/compiler/
|
||||
to see what's in there.
|
||||
|
||||
|
||||
coroutines (experimental)
|
||||
-------------------------
|
||||
coroutines
|
||||
----------
|
||||
Provides a system to make cooperative multitasking programs via coroutines.
|
||||
A 'coroutine' is a subroutine whose execution you can pause and resume.
|
||||
This library handles the voodoo for you to switch between such coroutines transparently,
|
||||
so it can seem that your program is executing many subroutines at the same time.
|
||||
|
||||
API is experimental and may change or disappear in a future version.
|
||||
|
||||
Read the `coroutines source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/coroutines.p8>`_
|
||||
to see what's in there. And look at the ``multitasking`` example to see how it can be used.
|
||||
Here is a minimal example (if the library gets more stable, better docs will be written here)::
|
||||
@ -796,16 +793,17 @@ but perhaps the provided ones can be of service too.
|
||||
``lerp(v0, v1, t)``
|
||||
Linear interpolation routine for unsigned byte values.
|
||||
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. Also works if v0 > v1.
|
||||
|
||||
``lerpw(v0, v1, t)``
|
||||
Linear interpolation routine for unsigned word values.
|
||||
Returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 65535]
|
||||
Guarantees v = v1 when t = 65535.
|
||||
Guarantees v = v1 when t = 65535. Also works if v0 > v1.
|
||||
Clobbers R15.
|
||||
|
||||
``interpolate(v, inputMin, inputMax, outputMin, outputMax)``
|
||||
Interpolate a value v in interval [inputMin, inputMax] to output interval [outputMin, outputMax]
|
||||
All values are unsigned bytes.
|
||||
All values are unsigned bytes. Clobbers R15
|
||||
(there is no version for word values because of lack of precision in the fixed point calculation there).
|
||||
|
||||
|
||||
@ -816,6 +814,7 @@ Full-screen lores or hires monochrome bitmap graphics routines, available on the
|
||||
- two resolutions: lores 320*240 or hires 640*480 bitmap mode
|
||||
- optimized routines for monochrome (2-color) graphics
|
||||
- clearing screen, switching screen mode, also back to text mode
|
||||
- doublebuffering option to avoid flicker
|
||||
- drawing and reading individual pixels
|
||||
- drawing lines, rectangles, filled rectangles, circles, discs
|
||||
- flood fill
|
||||
@ -823,7 +822,7 @@ Full-screen lores or hires monochrome bitmap graphics routines, available on the
|
||||
- can draw using a stipple pattern (alternate black/white pixels) and in invert mode (toggle pixels)
|
||||
|
||||
Read the `monogfx source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/cx16/monogfx.p8>`_
|
||||
to see what's in there.
|
||||
and the `testmonogfx` example program, to see what's in there.
|
||||
|
||||
|
||||
palette (cx16 only)
|
||||
@ -1177,9 +1176,7 @@ to see what's in there. (Note: slight variations for different compiler targets)
|
||||
|
||||
verafx (cx16 only)
|
||||
-------------------
|
||||
Available for the Cx16 target.
|
||||
Experimental routines that use the new Vera FX logic (hopefully coming in the Vera in new X16 boards,
|
||||
the emulators already support it).
|
||||
Available for the Cx16 target. Routines that use the Vera FX logic to accelerate certain operations.
|
||||
|
||||
``available``
|
||||
Returns true if Vera FX is available, false if not (that would be an older Vera chip)
|
||||
|
@ -24,7 +24,6 @@ Comments
|
||||
Everything on the line after a semicolon ``;`` is a comment and is ignored by the compiler.
|
||||
If the whole line is just a comment, this line will be copied into the resulting assembly source code for reference.
|
||||
There's also a block-comment: everything surrounded with ``/*`` and ``*/`` is ignored and this can span multiple lines.
|
||||
This block comment is experimental for now: it may change or even be removed again in a future compiler version.
|
||||
The recommended way to comment out a bunch of lines remains to just bulk comment them individually with ``;``.
|
||||
|
||||
Directive
|
||||
@ -744,9 +743,30 @@ An example, to select the number of cards to use depending on what game is playe
|
||||
numcards = 52
|
||||
|
||||
|
||||
on .. goto / on .. call statement (jump table)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
when statement ('jump table')
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
The ``on goto / call`` statement is suitable to create a fast call of a subroutine from a list based on an index value.
|
||||
it selects a function to jump to in O(1) whereas a similar when-statement, runs in O(n) because that one checks each index value.
|
||||
The ``on goto / call`` instead simply gets the correct entry from an array of function pointers and jumps to it directly.
|
||||
The index value that is used is 0-based; 0 will jump to the first entry in the list, 1 to the second, and so on.
|
||||
If the value is too large (i.e. outside the list of functions), no call is performed, and execution continues.
|
||||
In this case you can optionally add an ``else`` block that is then executed instead. Here's an example::
|
||||
|
||||
on math.randrange(5) call (
|
||||
routines.func1,
|
||||
routines.func2,
|
||||
routines.func3 )
|
||||
else {
|
||||
txt.print("no call was done")
|
||||
}
|
||||
|
||||
on math.randrange(5) goto (routines.func1, routines.func2, routines.func3)
|
||||
txt.print("no jump was taken")
|
||||
|
||||
|
||||
when statement
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Instead of writing a bunch of sequential if-elseif statements, it is more readable to
|
||||
use a ``when`` statement. (It will also result in greatly improved assembly code generation)
|
||||
@ -767,7 +787,8 @@ datatype as the when-value, otherwise no efficient comparison can be done.
|
||||
You can explicitly put a list of numbers that all should result in the same case,
|
||||
or even use any *range expression* as long as it denotes a constant list of numbers.
|
||||
Be aware that every number is compared individually so using long lists of numbers and/or
|
||||
many choice cases will result in poor performance.
|
||||
many choice cases will result in poor performance. If you need to call a certain function
|
||||
based on some sequential index number, look at the ``on .. goto / call`` statement instead.
|
||||
|
||||
Choices can result in a single statement or a block of multiple statements in which
|
||||
case you have to use { } to enclose them.
|
||||
@ -1092,14 +1113,19 @@ so pay attention to any jumps and rts instructions in the inlined code!
|
||||
don't want a ``rts`` or ``jmp`` or ``bra`` in it!
|
||||
|
||||
.. note::
|
||||
The 'virtual' 16-bit registers from the Commander X16 can also be specified as ``R0`` .. ``R15`` .
|
||||
The **sixteen 'virtual' 16-bit registers** from the Commander X16 can also be specified as ``R0`` .. ``R15`` .
|
||||
This means you don't have to set them up manually before calling a subroutine that takes
|
||||
one or more parameters in those 'registers'. You can just list the arguments directly.
|
||||
*This also works on the Commodore 64!* (however they are not as efficient there because they're not in zeropage)
|
||||
In prog8 and assembly code these 'registers' are directly accessible too via
|
||||
``cx16.r0`` .. ``cx16.r15`` (these are memory-mapped uword values),
|
||||
``cx16.r0s`` .. ``cx16.r15s`` (these are memory-mapped word values),
|
||||
and ``L`` / ``H`` variants are also available to directly access the low and high bytes of these.
|
||||
*This also works on the other compilation targets!* (however they might not be as efficient there as on the X16,
|
||||
because on most other targets such as the C64, these registers are not placed in zeropage due to lack of space)
|
||||
In both regular **prog8** *and* **assembly** code these 'registers' are directly accessible too via:
|
||||
|
||||
- ``cx16.r0`` - ``cx16.r15`` (memory-mapped **uword** values, most often these are used)
|
||||
- ``cx16.r0s`` - ``cx16.r15s`` (memory-mapped **word** values, used when you need a signed word)
|
||||
- ``cx16.r0H``, ``cx16.r0L`` (for each r0..r15; memory-mapped **ubyte** values, both bytes of the register)
|
||||
- ``cx16.r0sH``, ``cx16.r0sL`` (for each r0..r15; memory-mapped **byte** values, both bytes of the register)
|
||||
- ``cx16.r0bH``, ``cx16.r0bL`` (for each r0..r15; memory-mapped **bool** values, both bytes of the register)
|
||||
|
||||
You can use them directly but their name isn't very descriptive, so it may be useful to define
|
||||
an alias for them when using them regularly.
|
||||
|
||||
|
@ -1,18 +1,17 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
STRUCTS: are being developed in their own separate branch for now, called "structs".
|
||||
Idea is to make it feature complete in the IR/Virtual target, then merge it to master?, and then start building the 6502 code generation for it.
|
||||
|
||||
...
|
||||
|
||||
|
||||
Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- for version 12.0 (branch pre12) : translate '\n' to char code 13 in ISO encoding as well (and all others that don't do it yet, EXCEPT cp437, because it has no control chars) so you don't have to use \r anymore as newline
|
||||
- for version 12.0 (branch pre12) : on <expr> goto label1, label2, label3 and 'call' instead of 'goto' to make it a JSR. Faster jump tables than a when, and much easier to write than a call + an array. Number starts at 0. If number too large, no jump is taken. (or add 'else failLabel' ?) else @dirty to skip all checks?
|
||||
- STRUCTS: are now being developed in their own separate branch "structs". This will be for the next major version of the compiler (v12)
|
||||
- is "checkAssignmentCompatible" redundant (gets called just 1 time!) when we also have "checkValueTypeAndRange" ?
|
||||
- enums?
|
||||
- romable: should we have a way to explicitly set the memory address for the BSS area (instead of only the highram bank number on X16, allow a memory address too for the -varshigh option?)
|
||||
- romable: should we have a way to explicitly set the memory address for the BSS area (add a -varsaddress and -slabsaddress options?)
|
||||
- romable: fix remaining codegens (some for loops, see ForLoopsAsmGen)
|
||||
- Kotlin: can we use inline value classes in certain spots?
|
||||
- add float support to the configurable compiler targets
|
||||
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
|
||||
@ -24,7 +23,7 @@ Future Things and Ideas
|
||||
Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEvaluating() and argumentsViaRegisters().
|
||||
- Does it make codegen easier if everything is an expression? Start with the PtProgram ast classes, change statements to expressions that have (new) VOID data type
|
||||
- Can we support signed % (remainder) somehow?
|
||||
- Multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays. Probaby only useful if we have typed pointers. (addressed in 'struct' branch)
|
||||
- Multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays. Probaby only useful once we have typed pointers. (addressed in 'struct' branch)
|
||||
- make a form of "manual generics" possible like: varsub routine(T arg)->T where T is expanded to a specific type
|
||||
(this is already done hardcoded for several of the builtin functions)
|
||||
- [much work:] more support for (64tass) SEGMENTS in the prog8 syntax itself?
|
||||
@ -60,7 +59,6 @@ IR/VM
|
||||
Libraries
|
||||
---------
|
||||
- Add split-word array sorting routines to sorting module?
|
||||
- cx16: _irq_dispatcher now only dispatches a single irq source, better to ROL/BCC to handle *all* possible (multiple) sources.
|
||||
- See if the raster interrupt handler on the C64 can be tweaked to be a more stable raster irq
|
||||
- pet32 target: make syslib more complete (missing kernal routines)?
|
||||
- need help with: PET disk routines (OPEN, SETLFS etc are not exposed as kernal calls)
|
||||
@ -70,12 +68,10 @@ Libraries
|
||||
Optimizations
|
||||
-------------
|
||||
|
||||
- when choices that end with a goto create a redundant bra, get rid of it. Also some redundant RTS somewhere?
|
||||
- Sorting module gnomesort_uw could be optimized more by fully rewriting it in asm? Shellshort seems consistently faster even if most of the words are already sorted.
|
||||
- can the for loop temp var be replaced by the same logic that createRepeatCounterVar() uses for repeat loops? Take care of nested for/repeat loops to not use the same var
|
||||
- in Identifier: use typedarray of strings instead of listOf? Other places?
|
||||
- Compilation speed: try to join multiple modifications in 1 result in the AST processors instead of returning it straight away every time
|
||||
- Compare output of some Oscar64 samples to what prog8 does for the equivalent code (see https://github.com/drmortalwombat/OscarTutorials/tree/main and https://github.com/drmortalwombat/oscar64/tree/main/samples)
|
||||
- Optimize the IfExpression code generation to be more like regular if-else code. (both 6502 and IR) search for "TODO don't store condition as expression"
|
||||
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
|
||||
for instance, vars used inside loops first, then loopvars, then uwords used as pointers (or these first??), then the rest
|
||||
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once 6502-codegen is done from IR code,
|
||||
those checks should probably be removed, or be made permanent
|
||||
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once 6502-codegen is done from IR code, those checks should probably be removed, or be made permanent
|
||||
|
@ -23,12 +23,13 @@ sys {
|
||||
|
||||
const ubyte target = 8 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
|
||||
|
||||
const ubyte SIZEOF_BOOL = 1
|
||||
const ubyte SIZEOF_BYTE = 1
|
||||
const ubyte SIZEOF_UBYTE = 1
|
||||
const ubyte SIZEOF_WORD = 2
|
||||
const ubyte SIZEOF_UWORD = 2
|
||||
const ubyte SIZEOF_FLOAT = 0 ; undefined, no float support
|
||||
const ubyte SIZEOF_BOOL = sizeof(bool)
|
||||
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_FLOAT = 0 ; undefined, no floats supported
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
const ubyte MIN_UBYTE = 0
|
||||
@ -383,6 +384,18 @@ save_SCRATCH_ZPWORD2 .word ?
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
|
||||
%asm {{
|
||||
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
@ -402,7 +415,6 @@ save_SCRATCH_ZPWORD2 .word ?
|
||||
cx16 {
|
||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||
; they are simulated on the Atari as well but their location in memory is different
|
||||
; TODO
|
||||
&uword r0 = $1b00
|
||||
&uword r1 = $1b02
|
||||
&uword r2 = $1b04
|
||||
@ -420,6 +432,7 @@ cx16 {
|
||||
&uword r14 = $1b1c
|
||||
&uword r15 = $1b1e
|
||||
|
||||
; signed word versions
|
||||
&word r0s = $1b00
|
||||
&word r1s = $1b02
|
||||
&word r2s = $1b04
|
||||
@ -437,6 +450,7 @@ cx16 {
|
||||
&word r14s = $1b1c
|
||||
&word r15s = $1b1e
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $1b00
|
||||
&ubyte r1L = $1b02
|
||||
&ubyte r2L = $1b04
|
||||
@ -471,6 +485,7 @@ cx16 {
|
||||
&ubyte r14H = $1b1d
|
||||
&ubyte r15H = $1b1f
|
||||
|
||||
; signed byte versions (low and high bytes)
|
||||
&byte r0sL = $1b00
|
||||
&byte r1sL = $1b02
|
||||
&byte r2sL = $1b04
|
||||
@ -505,6 +520,41 @@ cx16 {
|
||||
&byte r14sH = $1b1d
|
||||
&byte r15sH = $1b1f
|
||||
|
||||
; boolean versions
|
||||
&bool r0bL = $1b00
|
||||
&bool r1bL = $1b02
|
||||
&bool r2bL = $1b04
|
||||
&bool r3bL = $1b06
|
||||
&bool r4bL = $1b08
|
||||
&bool r5bL = $1b0a
|
||||
&bool r6bL = $1b0c
|
||||
&bool r7bL = $1b0e
|
||||
&bool r8bL = $1b10
|
||||
&bool r9bL = $1b12
|
||||
&bool r10bL = $1b14
|
||||
&bool r11bL = $1b16
|
||||
&bool r12bL = $1b18
|
||||
&bool r13bL = $1b1a
|
||||
&bool r14bL = $1b1c
|
||||
&bool r15bL = $1b1e
|
||||
|
||||
&bool r0bH = $1b01
|
||||
&bool r1bH = $1b03
|
||||
&bool r2bH = $1b05
|
||||
&bool r3bH = $1b07
|
||||
&bool r4bH = $1b09
|
||||
&bool r5bH = $1b0b
|
||||
&bool r6bH = $1b0d
|
||||
&bool r7bH = $1b0f
|
||||
&bool r8bH = $1b11
|
||||
&bool r9bH = $1b13
|
||||
&bool r10bH = $1b15
|
||||
&bool r11bH = $1b17
|
||||
&bool r12bH = $1b19
|
||||
&bool r13bH = $1b1b
|
||||
&bool r14bH = $1b1d
|
||||
&bool r15bH = $1b1f
|
||||
|
||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
%asm {{
|
||||
ldy #31
|
||||
|
@ -15,11 +15,12 @@ sys {
|
||||
|
||||
const ubyte target = 7 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
|
||||
|
||||
const ubyte SIZEOF_BOOL = 1
|
||||
const ubyte SIZEOF_BYTE = 1
|
||||
const ubyte SIZEOF_UBYTE = 1
|
||||
const ubyte SIZEOF_WORD = 2
|
||||
const ubyte SIZEOF_UWORD = 2
|
||||
const ubyte SIZEOF_BOOL = sizeof(bool)
|
||||
const ubyte SIZEOF_BYTE = sizeof(byte)
|
||||
const ubyte SIZEOF_UBYTE = sizeof(ubyte)
|
||||
const ubyte SIZEOF_WORD = sizeof(word)
|
||||
const ubyte SIZEOF_UWORD = sizeof(uword)
|
||||
const ubyte SIZEOF_LONG = sizeof(long)
|
||||
const ubyte SIZEOF_FLOAT = 0 ; undefined, no floats supported
|
||||
const byte MIN_BYTE = -128
|
||||
const byte MAX_BYTE = 127
|
||||
@ -316,6 +317,18 @@ save_SCRATCH_ZPWORD2 .word ?
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
|
||||
%asm {{
|
||||
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
@ -351,6 +364,7 @@ cx16 {
|
||||
&uword r14 = $001e
|
||||
&uword r15 = $0020
|
||||
|
||||
; signed word versions
|
||||
&word r0s = $0002
|
||||
&word r1s = $0004
|
||||
&word r2s = $0006
|
||||
@ -368,6 +382,7 @@ cx16 {
|
||||
&word r14s = $001e
|
||||
&word r15s = $0020
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $0002
|
||||
&ubyte r1L = $0004
|
||||
&ubyte r2L = $0006
|
||||
@ -402,6 +417,7 @@ cx16 {
|
||||
&ubyte r14H = $001f
|
||||
&ubyte r15H = $0021
|
||||
|
||||
; signed byte versions (low and high bytes)
|
||||
&byte r0sL = $0002
|
||||
&byte r1sL = $0004
|
||||
&byte r2sL = $0006
|
||||
@ -436,6 +452,42 @@ cx16 {
|
||||
&byte r14sH = $001f
|
||||
&byte r15sH = $0021
|
||||
|
||||
; boolean versions
|
||||
&bool r0bL = $0002
|
||||
&bool r1bL = $0004
|
||||
&bool r2bL = $0006
|
||||
&bool r3bL = $0008
|
||||
&bool r4bL = $000a
|
||||
&bool r5bL = $000c
|
||||
&bool r6bL = $000e
|
||||
&bool r7bL = $0010
|
||||
&bool r8bL = $0012
|
||||
&bool r9bL = $0014
|
||||
&bool r10bL = $0016
|
||||
&bool r11bL = $0018
|
||||
&bool r12bL = $001a
|
||||
&bool r13bL = $001c
|
||||
&bool r14bL = $001e
|
||||
&bool r15bL = $0020
|
||||
|
||||
&bool r0bH = $0003
|
||||
&bool r1bH = $0005
|
||||
&bool r2bH = $0007
|
||||
&bool r3bH = $0009
|
||||
&bool r4bH = $000b
|
||||
&bool r5bH = $000d
|
||||
&bool r6bH = $000f
|
||||
&bool r7bH = $0011
|
||||
&bool r8bH = $0013
|
||||
&bool r9bH = $0015
|
||||
&bool r10bH = $0017
|
||||
&bool r11bH = $0019
|
||||
&bool r12bH = $001b
|
||||
&bool r13bH = $001d
|
||||
&bool r14bH = $001f
|
||||
&bool r15bH = $0021
|
||||
|
||||
|
||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
%asm {{
|
||||
ldy #31
|
||||
|
@ -159,6 +159,7 @@ cx16 {
|
||||
&uword r14 = $cffc
|
||||
&uword r15 = $cffe
|
||||
|
||||
; signed word versions
|
||||
&word r0s = $cfe0
|
||||
&word r1s = $cfe2
|
||||
&word r2s = $cfe4
|
||||
@ -176,6 +177,7 @@ cx16 {
|
||||
&word r14s = $cffc
|
||||
&word r15s = $cffe
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $cfe0
|
||||
&ubyte r1L = $cfe2
|
||||
&ubyte r2L = $cfe4
|
||||
@ -210,6 +212,7 @@ cx16 {
|
||||
&ubyte r14H = $cffd
|
||||
&ubyte r15H = $cfff
|
||||
|
||||
; signed byte versions (low and high bytes)
|
||||
&byte r0sL = $cfe0
|
||||
&byte r1sL = $cfe2
|
||||
&byte r2sL = $cfe4
|
||||
@ -243,4 +246,39 @@ cx16 {
|
||||
&byte r13sH = $cffb
|
||||
&byte r14sH = $cffd
|
||||
&byte r15sH = $cfff
|
||||
|
||||
; boolean versions
|
||||
&bool r0bL = $cfe0
|
||||
&bool r1bL = $cfe2
|
||||
&bool r2bL = $cfe4
|
||||
&bool r3bL = $cfe6
|
||||
&bool r4bL = $cfe8
|
||||
&bool r5bL = $cfea
|
||||
&bool r6bL = $cfec
|
||||
&bool r7bL = $cfee
|
||||
&bool r8bL = $cff0
|
||||
&bool r9bL = $cff2
|
||||
&bool r10bL = $cff4
|
||||
&bool r11bL = $cff6
|
||||
&bool r12bL = $cff8
|
||||
&bool r13bL = $cffa
|
||||
&bool r14bL = $cffc
|
||||
&bool r15bL = $cffe
|
||||
|
||||
&bool r0bH = $cfe1
|
||||
&bool r1bH = $cfe3
|
||||
&bool r2bH = $cfe5
|
||||
&bool r3bH = $cfe7
|
||||
&bool r4bH = $cfe9
|
||||
&bool r5bH = $cfeb
|
||||
&bool r6bH = $cfed
|
||||
&bool r7bH = $cfef
|
||||
&bool r8bH = $cff1
|
||||
&bool r9bH = $cff3
|
||||
&bool r10bH = $cff5
|
||||
&bool r11bH = $cff7
|
||||
&bool r12bH = $cff9
|
||||
&bool r13bH = $cffb
|
||||
&bool r14bH = $cffd
|
||||
&bool r15bH = $cfff
|
||||
}
|
||||
|
@ -159,6 +159,7 @@ cx16 {
|
||||
&uword r14 = $001e
|
||||
&uword r15 = $0020
|
||||
|
||||
; signed word versions
|
||||
&word r0s = $0002
|
||||
&word r1s = $0004
|
||||
&word r2s = $0006
|
||||
@ -176,6 +177,7 @@ cx16 {
|
||||
&word r14s = $001e
|
||||
&word r15s = $0020
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $0002
|
||||
&ubyte r1L = $0004
|
||||
&ubyte r2L = $0006
|
||||
@ -210,6 +212,7 @@ cx16 {
|
||||
&ubyte r14H = $001f
|
||||
&ubyte r15H = $0021
|
||||
|
||||
; signed byte versions (low and high bytes)
|
||||
&byte r0sL = $0002
|
||||
&byte r1sL = $0004
|
||||
&byte r2sL = $0006
|
||||
@ -243,4 +246,39 @@ cx16 {
|
||||
&byte r13sH = $001d
|
||||
&byte r14sH = $001f
|
||||
&byte r15sH = $0021
|
||||
|
||||
; boolean versions
|
||||
&bool r0bL = $0002
|
||||
&bool r1bL = $0004
|
||||
&bool r2bL = $0006
|
||||
&bool r3bL = $0008
|
||||
&bool r4bL = $000a
|
||||
&bool r5bL = $000c
|
||||
&bool r6bL = $000e
|
||||
&bool r7bL = $0010
|
||||
&bool r8bL = $0012
|
||||
&bool r9bL = $0014
|
||||
&bool r10bL = $0016
|
||||
&bool r11bL = $0018
|
||||
&bool r12bL = $001a
|
||||
&bool r13bL = $001c
|
||||
&bool r14bL = $001e
|
||||
&bool r15bL = $0020
|
||||
|
||||
&bool r0bH = $0003
|
||||
&bool r1bH = $0005
|
||||
&bool r2bH = $0007
|
||||
&bool r3bH = $0009
|
||||
&bool r4bH = $000b
|
||||
&bool r5bH = $000d
|
||||
&bool r6bH = $000f
|
||||
&bool r7bH = $0011
|
||||
&bool r8bH = $0013
|
||||
&bool r9bH = $0015
|
||||
&bool r10bH = $0017
|
||||
&bool r11bH = $0019
|
||||
&bool r12bH = $001b
|
||||
&bool r13bH = $001d
|
||||
&bool r14bH = $001f
|
||||
&bool r15bH = $0021
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user